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