PolyFEM
Loading...
Searching...
No Matches
Optimizations.cpp
Go to the documentation of this file.
2
4#include <polyfem/Common.hpp>
5
20
23
24#include <polysolve/nonlinear/BoxConstraintSolver.hpp>
25#include <polysolve/linear/Solver.hpp>
26
27#include <jse/jse.h>
28#include <polyfem/embedded_spec/polyfem_opt.hpp>
29#include <polyfem/embedded_spec/polyfem_objective.hpp>
30
31#include <Eigen/Core>
32
33#include <memory>
34#include <algorithm>
35#include <fstream>
36#include <vector>
37#include <string>
38#include <set>
39#include <stdexcept>
40
41namespace spdlog::level
42{
44 spdlog::level::level_enum,
45 {{spdlog::level::level_enum::trace, "trace"},
46 {spdlog::level::level_enum::debug, "debug"},
47 {spdlog::level::level_enum::info, "info"},
48 {spdlog::level::level_enum::warn, "warning"},
49 {spdlog::level::level_enum::err, "error"},
50 {spdlog::level::level_enum::critical, "critical"},
51 {spdlog::level::level_enum::off, "off"},
52 {spdlog::level::level_enum::trace, 0},
53 {spdlog::level::level_enum::debug, 1},
54 {spdlog::level::level_enum::info, 2},
55 {spdlog::level::level_enum::warn, 3},
56 {spdlog::level::level_enum::err, 3},
57 {spdlog::level::level_enum::critical, 4},
58 {spdlog::level::level_enum::off, 5}})
59}
60
61namespace polyfem::solver
62{
63
64 std::shared_ptr<polysolve::nonlinear::Solver> AdjointOptUtils::make_nl_solver(
65 const json &solver_params,
66 const json &linear_solver_params,
67 const double characteristic_length,
68 const bool strict_validation)
69 {
70 auto names = polysolve::nonlinear::Solver::available_solvers();
71 if (std::find(names.begin(), names.end(), solver_params["solver"]) != names.end())
72 return polysolve::nonlinear::Solver::create(
73 solver_params, linear_solver_params, characteristic_length, adjoint_logger(), strict_validation);
74
75 names = polysolve::nonlinear::BoxConstraintSolver::available_solvers();
76 if (std::find(names.begin(), names.end(), solver_params["solver"]) != names.end())
77 return polysolve::nonlinear::BoxConstraintSolver::create(
78 solver_params, linear_solver_params, characteristic_length, adjoint_logger(), strict_validation);
79
80 log_and_throw_adjoint_error("Invalid nonlinear solver name!");
81 }
82
83 Eigen::VectorXd AdjointOptUtils::inverse_evaluation(const json &args, const int ndof, const std::vector<int> &variable_sizes, VariableToSimulationGroup &var2sim)
84 {
85 // Auto mode, pure inverse eval.
86 if (args.is_string() && args.get<std::string>() == "auto")
87 {
88 Eigen::VectorXd x = var2sim.data[0]->inverse_eval();
89 if (x.size() != ndof)
90 {
91 log_and_throw_adjoint_error("inverse_eval() returned {} DOF, expected {}.", x.size(), ndof);
92 }
93 return x;
94 }
95
96 // Manual mode.
97 // 1. Try get initial specified by user first.
98 // 2. Fallback to inverse_eval.
99 Eigen::VectorXd x;
100 x.setZero(ndof);
101 int accumulative = 0;
102 int var = 0;
103 for (const auto &arg : args)
104 {
105 const auto &arg_initial = arg["initial"];
106 Eigen::VectorXd tmp(variable_sizes[var]);
107 if (arg_initial.is_array() && arg_initial.size() > 0)
108 {
109 tmp = arg_initial;
110 x.segment(accumulative, tmp.size()) = tmp;
111 }
112 else if (arg_initial.is_number())
113 {
114 tmp.setConstant(arg_initial.get<double>());
115 x.segment(accumulative, tmp.size()) = tmp;
116 }
117 else
118 {
119 // We seem to assume var2sim maps to parameter block in perfect order.
120 // But since either json spec or other part of the code enforce this, it's
121 // dangerous to rely on it.
122 if (var2sim.data.size() != 1)
123 {
124 logger().warn("Computing initial guess via inverse eval with multiple"
125 " variable to simulation. You should make sure var2sim maps one"
126 " to one to optimization parameter blocks in perfect order.");
127 }
128 x += var2sim.data[var]->inverse_eval();
129 }
130
131 accumulative += tmp.size();
132 var++;
133 }
134
135 return x;
136 }
137
139 {
140 Eigen::MatrixXd solution;
141 varform.solve(solution, nullptr, {}, false);
142 }
143
144 void apply_objective_json_spec(json &args, const json &rules)
145 {
146 if (args.is_array())
147 {
148 for (auto &arg : args)
149 apply_objective_json_spec(arg, rules);
150 }
151 else if (args.is_object())
152 {
153 jse::JSE jse;
154 const bool valid_input = jse.verify_json(args, rules);
155
156 if (!valid_input)
157 {
158 logger().error("invalid objective json:\n{}", jse.log2str());
159 throw std::runtime_error("Invalid objective json file");
160 }
161
162 args = jse.inject_defaults(args, rules);
163
164 for (auto &it : args.items())
165 {
166 if (it.key().find("objective") != std::string::npos)
167 apply_objective_json_spec(it.value(), rules);
168 }
169 }
170 }
171
172 json AdjointOptUtils::apply_opt_json_spec(const json &input_args, bool strict_validation)
173 {
174 json args_in = input_args;
175
176 // CHECK validity json
177 json rules;
178 jse::JSE jse;
179 {
180 jse.strict = strict_validation;
181 rules = jse::embed::polyfem_opt_spec::polyfem_opt::spec();
182
183 polysolve::linear::Solver::apply_default_solver(rules, "/solver/linear");
184 }
185
186 if (args_in.contains("/solver/linear"_json_pointer))
187 polysolve::linear::Solver::select_valid_solver(args_in["solver"]["linear"], logger());
188
189 const bool valid_input = jse.verify_json(args_in, rules);
190
191 if (!valid_input)
192 {
193 logger().error("invalid input json:\n{}", jse.log2str());
194 throw std::runtime_error("Invalid input json file");
195 }
196
197 json args = jse.inject_defaults(args_in, rules);
198
199 const json obj_rules = jse::embed::polyfem_objective_spec::polyfem_objective::spec();
200 apply_objective_json_spec(args["functionals"], obj_rules);
201
202 apply_objective_json_spec(args["stopping_conditions"], obj_rules);
203
204 return args;
205 }
206
207 int AdjointOptUtils::compute_variable_size(const json &args, const std::vector<std::shared_ptr<varform::DifferentiableVarForm>> &varforms)
208 {
209 if (args["number"].is_number())
210 {
211 return args["number"].get<int>();
212 }
213 else if (args["number"].is_null() && args["initial"].size() > 0)
214 {
215 return args["initial"].size();
216 }
217 else if (args["number"].is_object())
218 {
219 auto selection = args["number"];
220 if (selection.contains("surface_selection"))
221 {
222 auto surface_selection = selection["surface_selection"].get<std::vector<int>>();
223 auto varform_id = selection["state"];
224 std::set<int> node_ids = {};
225 for (const auto &surface : surface_selection)
226 {
227 std::vector<int> ids;
228 compute_surface_node_ids(*varforms[varform_id], surface, ids);
229 for (const auto &i : ids)
230 node_ids.insert(i);
231 }
232 return node_ids.size() * varforms[varform_id]->get_mesh().dimension();
233 }
234 else if (selection.contains("volume_selection"))
235 {
236 auto volume_selection = selection["volume_selection"].get<std::vector<int>>();
237 auto varform_id = selection["state"];
238 std::set<int> node_ids = {};
239 for (const auto &volume : volume_selection)
240 {
241 std::vector<int> ids;
242 compute_volume_node_ids(*varforms[varform_id], volume, ids);
243 for (const auto &i : ids)
244 node_ids.insert(i);
245 }
246
247 if (selection["exclude_boundary_nodes"])
248 {
249 std::vector<int> ids;
250 compute_total_surface_node_ids(*varforms[varform_id], ids);
251 for (const auto &i : ids)
252 node_ids.erase(i);
253 }
254
255 return node_ids.size() * varforms[varform_id]->get_mesh().dimension();
256 }
257 }
258
259 log_and_throw_adjoint_error("Incorrect specification for parameters.");
260 return -1;
261 }
262} // namespace polyfem::solver
int x
std::vector< std::shared_ptr< VariableToSimulation > > data
Optimization-facing interface implemented by differentiated VarForm adapters.
virtual void solve(Eigen::MatrixXd &solution, const InitialConditionOverride *initial_condition_override, const ForwardStepCallback &post_step, bool differentiable)=0
NLOHMANN_JSON_SERIALIZE_ENUM(CollisionProxyTessellation, {{CollisionProxyTessellation::REGULAR, "regular"}, {CollisionProxyTessellation::IRREGULAR, "irregular"}})
void apply_objective_json_spec(json &args, const json &rules)
void compute_surface_node_ids(const varform::DifferentiableVarForm &varform, const int surface_selection, std::vector< int > &node_ids)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
spdlog::logger & adjoint_logger()
Retrieves the current logger for adjoint.
Definition Logger.cpp:30
void compute_total_surface_node_ids(const varform::DifferentiableVarForm &varform, std::vector< int > &node_ids)
void compute_volume_node_ids(const varform::DifferentiableVarForm &varform, const int volume_selection, std::vector< int > &node_ids)
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:79
static int compute_variable_size(const json &args, const std::vector< std::shared_ptr< varform::DifferentiableVarForm > > &varforms)
static void solve_pde(varform::DifferentiableVarForm &varform)
static json apply_opt_json_spec(const json &input_args, bool strict_validation)
static Eigen::VectorXd inverse_evaluation(const json &args, const int ndof, const std::vector< int > &variable_sizes, VariableToSimulationGroup &var2sim)
static std::shared_ptr< polysolve::nonlinear::Solver > make_nl_solver(const json &solver_params, const json &linear_solver_params, const double characteristic_length, const bool strict_validation=true)