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
31#include <jse/jse.h>
32#include <polyfem/embedded_spec/polyfem_opt.hpp>
33#include <polyfem/embedded_spec/polyfem_objective.hpp>
34
35#include <Eigen/Core>
36
37#include <memory>
38#include <algorithm>
39#include <fstream>
40#include <vector>
41#include <string>
42#include <set>
43#include <stdexcept>
44
45namespace spdlog::level
46{
48 spdlog::level::level_enum,
49 {{spdlog::level::level_enum::trace, "trace"},
50 {spdlog::level::level_enum::debug, "debug"},
51 {spdlog::level::level_enum::info, "info"},
52 {spdlog::level::level_enum::warn, "warning"},
53 {spdlog::level::level_enum::err, "error"},
54 {spdlog::level::level_enum::critical, "critical"},
55 {spdlog::level::level_enum::off, "off"},
56 {spdlog::level::level_enum::trace, 0},
57 {spdlog::level::level_enum::debug, 1},
58 {spdlog::level::level_enum::info, 2},
59 {spdlog::level::level_enum::warn, 3},
60 {spdlog::level::level_enum::err, 3},
61 {spdlog::level::level_enum::critical, 4},
62 {spdlog::level::level_enum::off, 5}})
63}
64
65namespace polyfem::solver
66{
67
68 std::shared_ptr<polysolve::nonlinear::Solver> AdjointOptUtils::make_nl_solver(const json &solver_params, const json &linear_solver_params, const double characteristic_length)
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(solver_params, linear_solver_params, characteristic_length, adjoint_logger());
73
74 names = polysolve::nonlinear::BoxConstraintSolver::available_solvers();
75 if (std::find(names.begin(), names.end(), solver_params["solver"]) != names.end())
76 return polysolve::nonlinear::BoxConstraintSolver::create(solver_params, linear_solver_params, characteristic_length, adjoint_logger());
77
78 log_and_throw_adjoint_error("Invalid nonlinear solver name!");
79 }
80
81 Eigen::VectorXd AdjointOptUtils::inverse_evaluation(const json &args, const int ndof, const std::vector<int> &variable_sizes, VariableToSimulationGroup &var2sim)
82 {
83 // Auto mode, pure inverse eval.
84 if (args.is_string() && args.get<std::string>() == "auto")
85 {
86 Eigen::VectorXd x = var2sim.data[0]->inverse_eval();
87 if (x.size() != ndof)
88 {
89 log_and_throw_adjoint_error("inverse_eval() returned {} DOF, expected {}.", x.size(), ndof);
90 }
91 return x;
92 }
93
94 // Manual mode.
95 // 1. Try get initial specified by user first.
96 // 2. Fallback to inverse_eval.
97 Eigen::VectorXd x;
98 x.setZero(ndof);
99 int accumulative = 0;
100 int var = 0;
101 for (const auto &arg : args)
102 {
103 const auto &arg_initial = arg["initial"];
104 Eigen::VectorXd tmp(variable_sizes[var]);
105 if (arg_initial.is_array() && arg_initial.size() > 0)
106 {
107 tmp = arg_initial;
108 x.segment(accumulative, tmp.size()) = tmp;
109 }
110 else if (arg_initial.is_number())
111 {
112 tmp.setConstant(arg_initial.get<double>());
113 x.segment(accumulative, tmp.size()) = tmp;
114 }
115 else
116 {
117 // We seem to assume var2sim maps to parameter block in perfect order.
118 // But since either json spec or other part of the code enforce this, it's
119 // dangerous to rely on it.
120 if (var2sim.data.size() != 1)
121 {
122 logger().warn("Computing initial guess via inverse eval with multiple"
123 " variable to simulation. You should make sure var2sim maps one"
124 " to one to optimization parameter blocks in perfect order.");
125 }
126 x += var2sim.data[var]->inverse_eval();
127 }
128
129 accumulative += tmp.size();
130 var++;
131 }
132
133 return x;
134 }
135
137 {
138 state.assemble_rhs();
139 state.assemble_mass_mat();
140 Eigen::MatrixXd sol, pressure;
141 state.solve_problem(sol, pressure);
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<legacy::State>> &states)
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 state_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(*states[state_id], surface, ids);
229 for (const auto &i : ids)
230 node_ids.insert(i);
231 }
232 return node_ids.size() * states[state_id]->mesh->dimension();
233 }
234 else if (selection.contains("volume_selection"))
235 {
236 auto volume_selection = selection["volume_selection"].get<std::vector<int>>();
237 auto state_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(*states[state_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(*states[state_id], ids);
251 for (const auto &i : ids)
252 node_ids.erase(i);
253 }
254
255 return node_ids.size() * states[state_id]->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
main class that contains the polyfem solver and all its state
Definition State.hpp:114
void assemble_mass_mat()
assemble mass, step 4 of solve build mass matrix based on defined basis modifies mass (and maybe more...
Definition State.cpp:1462
void assemble_rhs()
compute rhs, step 3 of solve build rhs vector based on defined basis and given rhs of the problem mod...
Definition State.cpp:1593
void solve_problem(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves the problems
Definition State.cpp:1665
std::vector< std::shared_ptr< VariableToSimulation > > data
NLOHMANN_JSON_SERIALIZE_ENUM(CollisionProxyTessellation, {{CollisionProxyTessellation::REGULAR, "regular"}, {CollisionProxyTessellation::IRREGULAR, "irregular"}})
void apply_objective_json_spec(json &args, const json &rules)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
void compute_surface_node_ids(const legacy::State &state, const int surface_selection, std::vector< int > &node_ids)
void compute_volume_node_ids(const legacy::State &state, const int volume_selection, std::vector< int > &node_ids)
spdlog::logger & adjoint_logger()
Retrieves the current logger for adjoint.
Definition Logger.cpp:30
void compute_total_surface_node_ids(const legacy::State &state, 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 void solve_pde(legacy::State &state)
static int compute_variable_size(const json &args, const std::vector< std::shared_ptr< legacy::State > > &states)
static std::shared_ptr< polysolve::nonlinear::Solver > make_nl_solver(const json &solver_params, const json &linear_solver_params, const double characteristic_length)
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)