PolyFEM
Loading...
Searching...
No Matches
OptState.cpp
Go to the documentation of this file.
1#include "OptState.hpp"
2
3#include <polyfem/Common.hpp>
4
9
17
20
21#include <polysolve/nonlinear/Solver.hpp>
22
23#include <spdlog/sinks/stdout_color_sinks.h>
24#include <spdlog/sinks/basic_file_sink.h>
25#include <spdlog/sinks/ostream_sink.h>
26
27#include <Eigen/Core>
28
29#include <algorithm>
30#include <filesystem>
31#include <fstream>
32#include <functional>
33#include <limits>
34#include <memory>
35#include <optional>
36#include <string>
37#include <vector>
38
39namespace spdlog::level
40{
42 spdlog::level::level_enum,
43 {{spdlog::level::level_enum::trace, "trace"},
44 {spdlog::level::level_enum::debug, "debug"},
45 {spdlog::level::level_enum::info, "info"},
46 {spdlog::level::level_enum::warn, "warning"},
47 {spdlog::level::level_enum::err, "error"},
48 {spdlog::level::level_enum::critical, "critical"},
49 {spdlog::level::level_enum::off, "off"},
50 {spdlog::level::level_enum::trace, 0},
51 {spdlog::level::level_enum::debug, 1},
52 {spdlog::level::level_enum::info, 2},
53 {spdlog::level::level_enum::warn, 3},
54 {spdlog::level::level_enum::err, 3},
55 {spdlog::level::level_enum::critical, 4},
56 {spdlog::level::level_enum::off, 5}})
57}
58
59namespace polyfem
60{
61 namespace
62 {
63 bool is_failed_status(const polysolve::nonlinear::Status status)
64 {
65 using Status = polysolve::nonlinear::Status;
66 return status == Status::NanEncountered
67 || status == Status::NotDescentDirection
68 || status == Status::LineSearchFailed
69 || status == Status::UpdateDirectionFailed
70 || status == Status::NotStarted
71 || status == Status::Continue;
72 }
73
74 std::string parse_remeshing_trigger(const json &args)
75 {
76 const json &remeshing = args["remeshing"];
77 if (remeshing.is_null())
78 return "none";
79
80 const json &trigger = remeshing["trigger"];
81 if (trigger.is_null())
82 return "none";
83
84 const bool periodic = trigger.contains("periodic") && trigger["periodic"].is_object();
85 const bool scaled_jacobian = trigger.contains("scaled_jacobian") && trigger["scaled_jacobian"].is_object();
86 if (periodic && scaled_jacobian)
87 return "multiple";
88 if (periodic)
89 return "periodic";
90 if (scaled_jacobian)
91 return "scaled_jacobian";
92 return "none";
93 }
94
95 std::vector<json> load_state_jsons(
96 const io::ResourceIO &resources,
97 const json &args,
98 std::vector<std::unique_ptr<const io::ResourceIO>> &state_resources)
99 {
100 std::vector<json> result;
101 state_resources.clear();
102 for (int i = 0; i < args.size(); ++i)
103 {
104 const std::string path = args[i]["path"].get<std::string>();
105 if (!resources.exists(path))
106 log_and_throw_adjoint_error("Can't find json for varform::DifferentiableVarForm {}", i);
107 result.push_back(json::parse(resources.read_string(path)));
108 const std::string parent = std::filesystem::path(path).parent_path().generic_string();
109 state_resources.push_back(resources.with_root(parent));
110 }
111 return result;
112 }
113
114 void validate_remeshing_json(const json &args)
115 {
116 const std::string trigger = parse_remeshing_trigger(args);
117 if (trigger == "multiple")
118 log_and_throw_error("Optimization remeshing requires exactly one trigger: periodic or scaled_jacobian.");
119 const json &remeshing = args["remeshing"];
120 const int state = remeshing["state"];
121 if (state >= args["states"].size())
122 log_and_throw_error("Optimization remeshing state index {} is out of range.", state);
123
124 for (const auto &variable : args["variable_to_simulation"])
125 {
126 if (variable["type"] != "shape")
127 continue;
128
129 const json &variable_state = variable["state"];
130 const bool targets_state = variable_state.is_array()
131 ? std::find(variable_state.begin(), variable_state.end(), state) != variable_state.end()
132 : variable_state.get<int>() == state;
133 if (!targets_state)
134 continue;
135
136 const json &selection = variable["active_geometry_nodes"];
137 const bool selects_all_nodes = selection.is_array() && selection.empty();
138 if (selects_all_nodes || selection.is_object())
139 continue;
140
142 "Optimization remeshing changes mesh vertex numbering. "
143 "Shape variable {} targeting state {} must use a geometry-based "
144 "active_geometry_nodes selector (interior, boundary, or "
145 "boundary_excluding_surface), or an empty selector for all nodes.",
146 variable.value("name", "shape"), state);
147 }
148 }
149
150 void validate_remeshing_geometry(const json &state_args, const int geometry)
151 {
152 if (geometry < 0 || geometry >= state_args["geometry"].size())
153 log_and_throw_error("Optimization remeshing geometry index {} is out of range.", geometry);
154 if (state_args["geometry"][geometry].value("is_obstacle", false))
155 log_and_throw_error("Optimization remeshing geometry {} is an obstacle.", geometry);
156 }
157
159 double minimum_scaled_jacobian(
160 const Eigen::MatrixXd &vertices,
161 const Eigen::MatrixXi &elements)
162 {
163 Eigen::VectorXd quality;
164 solver::AdjointTools::scaled_jacobian(vertices, elements, quality);
165 return quality.minCoeff();
166 }
167
169 double minimum_scaled_jacobian(const varform::DifferentiableVarForm &varform)
170 {
171 Eigen::MatrixXd vertices;
172 Eigen::MatrixXi elements;
173 varform.get_vertices(vertices);
174 varform.get_elements(elements);
175 return minimum_scaled_jacobian(vertices, elements);
176 }
177
184 bool remesh_and_write(
185 const OptState &opt_state,
186 const int state,
187 const int body_id,
188 const std::filesystem::path &path,
189 const std::optional<double> quality_threshold)
190 {
191 mesh::MmgOptions options;
192 options.optim = true;
193
194 Eigen::MatrixXd vertices;
195 Eigen::MatrixXi elements;
196 opt_state.varforms[state]->get_vertices(vertices);
197 opt_state.varforms[state]->get_elements(elements);
198
199 Eigen::MatrixXd remeshed_vertices;
200 Eigen::MatrixXi remeshed_boundary;
201 Eigen::MatrixXi remeshed_elements;
202 bool has_shape_variable = false;
203 for (const auto &var2sim : opt_state.variable_to_simulations.data)
204 {
205 const auto shape_v2s = std::dynamic_pointer_cast<solver::ShapeVariableToSimulation>(var2sim);
206 if (!shape_v2s || !shape_v2s->affects_varform(*opt_state.varforms[state]))
207 continue;
208 has_shape_variable = true;
209
210 for (int other_state = 0; other_state < opt_state.varforms.size(); ++other_state)
211 {
212 if (other_state != state
213 && shape_v2s->affects_varform(*opt_state.varforms[other_state]))
214 {
216 "Optimization remeshing cannot remesh state {} while its shape variable also affects state {}. This is not supported currently.",
217 state, other_state);
218 }
219 }
220 }
221 if (!has_shape_variable)
222 {
223 logger().error("Can not remesh state {} because it's not affected by shape variables. No reason to remesh if you are not doing shape optimization.", state);
224 return false;
225 }
226
227 const mesh::Mesh &mesh = opt_state.varforms[state]->get_mesh();
228 bool success = false;
229 if (mesh.dimension() == 2)
230 {
231 success = mesh::remesh_2d(
232 vertices, elements, remeshed_vertices, remeshed_elements,
233 options);
234 }
235 else
236 {
237 success = mesh::remesh_3d(
238 vertices, elements, remeshed_vertices, remeshed_boundary,
239 remeshed_elements, options);
240 }
241 if (!success)
242 {
243 return false;
244 }
245
246 const double input_quality = minimum_scaled_jacobian(vertices, elements);
247 const double output_quality = minimum_scaled_jacobian(remeshed_vertices, remeshed_elements);
248 logger().info(
249 "Optimization remeshing minimum scaled Jacobian: {} -> {}.",
250 input_quality, output_quality);
251 // If trigger mode is scaled jacobian, check quality immediately after remeshing.
252 if (quality_threshold.has_value() && output_quality <= *quality_threshold)
253 {
255 "MMG remeshing failed to satisfy minimum scaled Jacobian threshold: "
256 "input={}, output={}, required > {}. This is because MMG use a different convergence criteria, please switch remeshing trigger to periodic.",
257 input_quality, output_quality, *quality_threshold);
258 }
259
261 path.string(), remeshed_vertices, remeshed_elements,
262 std::vector<int>(remeshed_elements.rows(), body_id),
263 opt_state.varforms[state]->get_mesh().is_volume(), false);
264 return true;
265 }
266 } // namespace
267
269 {
270 }
271
276
277 int OptState::run(json input_args, const bool strict_validation)
278 {
279 std::filesystem::path root = std::filesystem::current_path();
280 if (utils::is_param_valid(input_args, "root_path"))
281 root = input_args["root_path"].get<std::string>();
282 resources_ = std::make_unique<io::FileSystemIO>(root);
283 return run(std::move(input_args), *resources_, strict_validation);
284 }
285
287 json input_args,
288 const io::ResourceIO &resources,
289 const bool strict_validation)
290 {
291 resources_ = resources.with_root("");
292 input_args = solver::AdjointOptUtils::apply_opt_json_spec(input_args, strict_validation);
293
294 std::string mode = parse_remeshing_trigger(input_args);
295 bool remeshing_enabled = mode != "none";
296 if (remeshing_enabled)
297 validate_remeshing_json(input_args);
298
299 std::filesystem::path output_root = input_args["output"]["directory"].get<std::string>();
300 if (!output_root.is_absolute())
301 output_root = resources_->host_directory() / output_root;
302 int max_restarts = remeshing_enabled
303 ? input_args["remeshing"]["max_restarts"].get<int>()
304 : 0;
305 state_args = load_state_jsons(*resources_, input_args["states"], state_resources);
306
307 int state = 0;
308 int geometry = 0;
309 int body_id = 0;
310 if (remeshing_enabled)
311 {
312 state = input_args["remeshing"]["state"].get<int>();
313 geometry = input_args["remeshing"]["geometry"].get<int>();
314 validate_remeshing_geometry(state_args[state], geometry);
315 body_id = state_args[state]["geometry"][geometry].value("volume_selection", 0);
316 }
317
318 int remesh_count = 0;
319 while (true)
320 {
321 remeshing_requested_ = false;
322
323 // Each remeshing round use a unique output directory.
324 json round_args = input_args;
325 if (remeshing_enabled)
326 {
327 const std::filesystem::path round_dir = output_root / fmt::format("remesh_round_{:d}", remesh_count);
328 round_args["output"]["directory"] = round_dir.string();
329 state_args[state]["output"]["directory"] = round_dir.string();
330 }
331
332 init(round_args, *resources_, strict_validation);
333 create_varforms(args["solver"]["max_threads"].get<int>());
336
337 Eigen::VectorXd x;
339 // Dry run mode. Compute objective and exit immediately.
340 if (args["compute_objective"].get<bool>())
341 {
342 logger().info("Objective is {}", eval(x));
343 return EXIT_SUCCESS;
344 }
345
346 const polysolve::nonlinear::Status status = solve(x);
347 if (is_failed_status(status))
348 {
349 logger().error("Optimization failed: {}.", polysolve::nonlinear::status_message(status));
350 return EXIT_FAILURE;
351 }
353 {
354 return EXIT_SUCCESS;
355 }
356 if (remesh_count >= max_restarts)
357 {
358 logger().info("Reached the optimization remeshing limit of {} restart(s).", max_restarts);
359 return EXIT_SUCCESS;
360 }
361
362 const std::filesystem::path round_dir = output_root / fmt::format("remesh_round_{:d}", remesh_count);
363 const std::filesystem::path remeshed_path = std::filesystem::absolute(round_dir / "remeshed.msh");
364 const std::optional<double> quality_threshold = mode == "scaled_jacobian"
365 ? std::optional<double>(args["remeshing"]["trigger"]["scaled_jacobian"]["quality_threshold"].get<double>())
366 : std::nullopt;
367 if (!remesh_and_write(
368 *this, state, body_id,
369 remeshed_path, quality_threshold))
370 {
371 logger().error("MMG failed to produce a valid optimization restart mesh.");
372 return EXIT_FAILURE;
373 }
374
375 state_args[state]["geometry"][geometry]["mesh"] = remeshed_path.string();
376 logger().info("Restarting optimization from remeshed mesh {}.", remeshed_path.string());
377 ++remesh_count;
378 }
379 }
380
382 const std::string &log_file,
383 const spdlog::level::level_enum log_level,
384 const spdlog::level::level_enum file_log_level,
385 const bool is_quiet)
386 {
387 std::vector<spdlog::sink_ptr> sinks;
388
389 if (!is_quiet)
390 {
391 console_sink_ = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
392 sinks.emplace_back(console_sink_);
393 }
394
395 if (!log_file.empty())
396 {
397 file_sink_ = std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file, /*truncate=*/true);
398 // Set the file sink separately from the console so it can save all messages
399 file_sink_->set_level(file_log_level);
400 sinks.push_back(file_sink_);
401 }
402
403 init_logger(sinks, log_level);
404 spdlog::flush_every(std::chrono::seconds(3));
405 }
406
407 void OptState::init_logger(std::ostream &os, const spdlog::level::level_enum log_level)
408 {
409 std::vector<spdlog::sink_ptr> sinks;
410 sinks.emplace_back(std::make_shared<spdlog::sinks::ostream_sink_mt>(os, false));
411 init_logger(sinks, log_level);
412 }
413
415 const std::vector<spdlog::sink_ptr> &sinks,
416 const spdlog::level::level_enum log_level)
417 {
418 set_adjoint_logger(std::make_shared<spdlog::logger>("adjoint-polyfem", sinks.begin(), sinks.end()));
419
420 // Set the logger at the lowest level, so all messages are passed to the sinks
421 adjoint_logger().set_level(spdlog::level::trace);
422 set_log_level(log_level);
423 }
424
425 void OptState::set_log_level(const spdlog::level::level_enum log_level)
426 {
427 adjoint_logger().set_level(log_level);
428 if (console_sink_)
429 console_sink_->set_level(log_level); // Shared by all loggers
430 }
431
432 void OptState::init(const json &p_args_in, const bool strict_validation)
433 {
434 std::filesystem::path root = std::filesystem::current_path();
435 if (utils::is_param_valid(p_args_in, "root_path"))
436 root = p_args_in["root_path"].get<std::string>();
437 resources_ = std::make_unique<io::FileSystemIO>(root);
438 init(p_args_in, *resources_, strict_validation);
439 }
440
442 const json &p_args_in,
443 const io::ResourceIO &resources,
444 const bool strict_validation)
445 {
446 resources_ = resources.with_root("");
447 strict_validation_ = strict_validation;
448 json args_in = p_args_in; // mutable copy
449 args = solver::AdjointOptUtils::apply_opt_json_spec(args_in, strict_validation);
450
451 // Save output directory and resolve output paths dynamically
452 std::filesystem::path output_path = args["output"]["directory"].get<std::string>();
453 if (!output_path.is_absolute())
454 output_path = resources_->host_directory() / output_path;
455 const std::string output_dir = output_path.lexically_normal().string();
456 if (!output_dir.empty())
457 {
458 std::filesystem::create_directories(output_dir);
459 }
460 this->output_dir = output_dir;
461
462 std::string out_path_log = args["output"]["log"]["path"];
463 if (!out_path_log.empty())
464 {
465 out_path_log = (std::filesystem::path(out_path_log).is_absolute()
466 ? std::filesystem::path(out_path_log)
467 : std::filesystem::path(output_dir) / out_path_log)
468 .lexically_normal()
469 .string();
470 }
471
473 out_path_log,
474 args["output"]["log"]["level"],
475 args["output"]["log"]["file_level"],
476 args["output"]["log"]["quiet"]);
477
478 adjoint_logger().info("Saving adjoint output to {}", output_dir);
479
480 const int thread_in = args["solver"]["max_threads"];
482 }
483
484 void OptState::create_varforms(const int max_threads)
485 {
486 if (state_args.empty())
487 state_args = load_state_jsons(*resources_, args["states"], state_resources);
488
489 size_t threads = max_threads <= 0
490 ? std::numeric_limits<unsigned int>::max()
491 : max_threads;
492 varforms.clear();
493 for (int i = 0; i < state_args.size(); ++i)
494 {
495 json cur_args = state_args[i];
496 if (!args["output"]["log"].empty())
497 cur_args["output"]["log"].merge_patch(args["output"]["log"]);
498 varforms.push_back(from_json::build_differentiable_varform(cur_args, *state_resources.at(i), threads));
499 }
500
501 diff_caches.resize(varforms.size());
502 for (auto &diff_cache : diff_caches)
503 {
504 diff_cache = std::make_shared<DiffCache>();
505 }
506
508
510 }
511
513 {
514 for (int i = 0; i < varforms.size(); ++i)
515 {
516 const varform::DifferentiableVarForm &varform = *varforms[i];
517 if (!varform.solve_data())
518 {
520 "varform::DifferentiableVarForm {} ({}) does not expose solve data required by optimization.",
521 i, varform.name());
522 }
523
524 // No transient linear support.
525 if (varform.get_problem().is_time_dependent() && varform.is_problem_linear())
526 {
528 "varform::DifferentiableVarForm {}: transient linear problem is not supported in optimization.", i);
529 }
530
531 if (varform.is_contact_enabled())
532 {
533 // No non-convergent contact formulation support.
534 if (!varform.get_args()["contact"]["use_gcp_formulation"].get<bool>()
535 && !varform.get_args()["contact"]["use_convergent_formulation"].get<bool>())
536 {
538 "varform::DifferentiableVarForm {}: non-convergent contact formulation is not supported in optimization.", i);
539 }
540
541 // No non-const barrier stiffness support.
542 if (varform.get_args()["/solver/contact/barrier_stiffness"_json_pointer].is_string())
543 {
545 "varform::DifferentiableVarForm {}: only constant barrier stiffness is supported in optimization.", i);
546 }
547 }
548
549 // No non-const boundary support.
550 if (varform.get_args().contains("boundary_conditions") && varform.get_args()["boundary_conditions"].contains("rhs"))
551 {
552 const json &rhs = varform.get_args()["boundary_conditions"]["rhs"];
553 if (rhs.is_string() || (rhs.is_array() && rhs.size() > 0 && rhs[0].is_string()))
554 {
556 "varform::DifferentiableVarForm {}: only constant rhs over space is supported in optimization.", i);
557 }
558 }
559
560 // No high order geometric basis support.
561 for (const auto &element_bases : varform.primary_space().geometry_basis_list())
562 {
563 for (const auto &basis : element_bases.bases)
564 {
565 if (basis.order() > 1)
566 {
568 "varform::DifferentiableVarForm {}: high-order geometry basis is not supported in optimization.", i);
569 }
570 }
571 }
572 }
573 }
574
576 {
577 std::vector<std::reference_wrapper<const io::ResourceIO>> resource_refs;
578 resource_refs.reserve(state_resources.size());
579 for (const auto &resources : state_resources)
580 resource_refs.emplace_back(*resources);
581
582 const json &parameters = args["parameters"];
583 bool is_auto = parameters.is_string() && parameters.get<std::string>() == "auto";
584
585 // Auto mode.
586 // In auto mode optimization parameters dof is inferred. No need to parse json.
587 if (is_auto)
588 {
589 if (args["variable_to_simulation"].size() != 1)
590 {
592 "Auto parameters are only supported with a single variable to simulation.");
593 }
594
595 for (auto &composition : utils::json_as_array(args["variable_to_simulation"][0]["composition"]))
596 {
597 if (composition["type"].get<std::string>() == "slice")
598 {
599 log_and_throw_adjoint_error("Auto parameters do not support slice maps in composition.");
600 }
601 }
602
604
605 ndof = variable_to_simulations.data[0]->inverse_dof();
607
608 return;
609 }
610
611 // Manual mode.
612 // We need to parse optimization parameter blocks to load dof first.
613 variable_sizes.clear();
614 ndof = 0;
615 for (const auto &arg : args["parameters"])
616 {
618 ndof += size;
619 variable_sizes.push_back(size);
620 }
621
622 /* variable to simulations */
624 args["variable_to_simulation"], varforms, diff_caches, variable_sizes, resource_refs);
625
626 // Verify varaible dof.
627 for (int i = 0; i < variable_to_simulations.data.size(); ++i)
628 {
629 auto &var2sim = variable_to_simulations.data[i];
630 int inv_dof = var2sim->inverse_dof();
631 if (inv_dof != ndof)
632 {
634 "VariableToSimulation {} (type {}) expects {} DOF, but parameters define {} DOF.",
635 i, var2sim->name(), inv_dof, ndof);
636 }
637 }
638 }
639
641 {
642 std::vector<std::reference_wrapper<const io::ResourceIO>> resource_refs;
643 resource_refs.reserve(state_resources.size());
644 for (const auto &resources : state_resources)
645 resource_refs.emplace_back(*resources);
646
647 /* forms */
648 std::shared_ptr<solver::AdjointForm> obj = from_json::build_form(
649 args["functionals"], variable_to_simulations, varforms, diff_caches, resource_refs);
650
651 /* stopping conditions */
652 std::vector<std::shared_ptr<solver::AdjointForm>> stopping_conditions;
653 for (const auto &arg : args["stopping_conditions"])
654 stopping_conditions.push_back(
656
657 std::function<bool()> remeshing_trigger;
658 const std::string mode = parse_remeshing_trigger(args);
659 if (mode == "periodic")
660 {
661 int period = args["remeshing"]["trigger"]["periodic"]["period"];
662 // this capture is for remeshing_requested_ specifically.
663 remeshing_trigger = [this, period, iter = 0]() mutable {
664 remeshing_requested_ = (iter % period) == 0;
665 ++iter;
667 adjoint_logger().debug(
668 "Periodic optimization remeshing triggered after {} accepted iteration(s).", iter);
670 };
671 }
672 else if (mode == "scaled_jacobian")
673 {
674 int state = args["remeshing"]["state"];
675 double threshold = args["remeshing"]["trigger"]["scaled_jacobian"]["quality_threshold"];
676 auto varform = varforms[state];
677 // this capture is for remeshing_requested_ specifically.
678 remeshing_trigger = [this, varform, threshold]() {
679 double quality = minimum_scaled_jacobian(*varform);
680 adjoint_logger().debug(
681 "Minimum scaled Jacobian: {} (remesh threshold: {}).",
682 quality, threshold);
683 remeshing_requested_ = quality <= threshold;
685 };
686 }
687
688 nl_problem = std::make_unique<solver::AdjointNLProblem>(
689 obj, stopping_conditions, variable_to_simulations, varforms, diff_caches,
690 args, std::move(remeshing_trigger));
691 }
692
699
700 double OptState::eval(Eigen::VectorXd &x) const
701 {
702 nl_problem->solution_changed(x);
703 return nl_problem->value(x);
704 }
705
706 polysolve::nonlinear::Status OptState::solve(Eigen::VectorXd &x)
707 {
709 args["solver"]["nonlinear"],
710 args["solver"]["linear"],
711 args["solver"]["advanced"]["characteristic_length"],
713 nl_problem->normalize_forms();
714 nl_solver->minimize(*nl_problem, x);
715 return nl_solver->status();
716 }
717} // namespace polyfem
int x
void initial_guess(Eigen::VectorXd &x)
Definition OptState.cpp:693
void set_log_level(const spdlog::level::level_enum log_level)
change log level
Definition OptState.cpp:425
json args
main input arguments containing all defaults
Definition OptState.hpp:51
std::vector< std::shared_ptr< varform::DifferentiableVarForm > > varforms
Variational formulations used by the optimization.
Definition OptState.hpp:92
void init(const json &args, const bool strict_validation)
initialize the polyfem solver with a json settings
Definition OptState.cpp:432
int run(json args, const bool strict_validation)
Run optimization, including remeshing restarts when the selected trigger requests them.
Definition OptState.cpp:277
std::vector< std::shared_ptr< DiffCache > > diff_caches
Definition OptState.hpp:95
spdlog::sink_ptr file_sink_
Definition OptState.hpp:114
void check_unsupported() const
Check and throw if any forward simulation varform::DifferentiableVarForm is not supported.
Definition OptState.cpp:512
double eval(Eigen::VectorXd &x) const
Definition OptState.cpp:700
std::vector< std::unique_ptr< const io::ResourceIO > > state_resources
Definition OptState.hpp:94
std::vector< int > variable_sizes
variables
Definition OptState.hpp:98
std::unique_ptr< solver::AdjointNLProblem > nl_problem
Definition OptState.hpp:103
std::string output_dir
Directory for output files.
Definition OptState.hpp:126
std::vector< json > state_args
Definition OptState.hpp:93
spdlog::sink_ptr console_sink_
logger sink to stdout
Definition OptState.hpp:113
void init_logger(const std::string &log_file, const spdlog::level::level_enum log_level, const spdlog::level::level_enum file_log_level, const bool is_quiet)
initializing the logger
Definition OptState.cpp:381
std::unique_ptr< const io::ResourceIO > resources_
Definition OptState.hpp:118
solver::VariableToSimulationGroup variable_to_simulations
Definition OptState.hpp:101
void create_varforms(const int max_threads=-1)
Create the optimization variational formulations.
Definition OptState.cpp:484
polysolve::nonlinear::Status solve(Eigen::VectorXd &x)
Definition OptState.cpp:706
void init_variables()
init variables
Definition OptState.cpp:575
OptState()
Constructor.
Definition OptState.cpp:272
virtual bool is_time_dependent() const
Definition Problem.hpp:62
static void write(const std::string &path, const mesh::Mesh &mesh, const bool binary)
saves the mesh
Definition MshWriter.cpp:7
Read-only interface to a hierarchical collection of simulation resources.
virtual std::unique_ptr< const ResourceIO > with_root(const std::string &root) const =0
std::vector< std::shared_ptr< VariableToSimulation > > data
void set_logger(spdlog::logger &logger)
static GeogramUtils & instance()
static NThread & get()
Definition par_for.hpp:19
void set_num_threads(const int max_threads)
Definition par_for.hpp:27
Optimization-facing interface implemented by differentiated VarForm adapters.
virtual std::string name() const =0
virtual solver::SolveData * solve_data()=0
virtual assembler::Problem & get_problem()=0
virtual bool is_contact_enabled() const =0
virtual const FESpace & primary_space() const =0
const std::vector< basis::ElementBases > & geometry_basis_list() const
Definition FESpace.hpp:115
list vertices
Definition p_bases.py:238
std::shared_ptr< varform::DifferentiableVarForm > build_differentiable_varform(const json &args, const io::ResourceIO &resources, const size_t max_threads)
solver::VariableToSimulationGroup build_variable_to_simulation_group(const json &args, const std::vector< std::shared_ptr< varform::DifferentiableVarForm > > &varforms, const std::vector< std::shared_ptr< DiffCache > > &diff_caches, const std::vector< int > &variable_sizes, const std::vector< std::reference_wrapper< const io::ResourceIO > > &resources)
std::shared_ptr< solver::AdjointForm > build_form(const json &args, const solver::VariableToSimulationGroup &var2sim, const std::vector< std::shared_ptr< varform::DifferentiableVarForm > > &varforms, const std::vector< std::shared_ptr< DiffCache > > &diff_caches, const std::vector< std::reference_wrapper< const io::ResourceIO > > &resources)
bool scaled_jacobian(Mesh3DStorage &hmi, Mesh_Quality &mq)
NLOHMANN_JSON_SERIALIZE_ENUM(CollisionProxyTessellation, {{CollisionProxyTessellation::REGULAR, "regular"}, {CollisionProxyTessellation::IRREGULAR, "irregular"}})
void scaled_jacobian(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, Eigen::VectorXd &quality)
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
Definition JSONUtils.hpp:49
bool is_param_valid(const json &params, const std::string &key)
Determine if a key exists and is non-null in a json object.
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
nlohmann::json json
Definition Common.hpp:9
void set_adjoint_logger(std::shared_ptr< spdlog::logger > p_logger)
Setup a logger object to be used by adjoint Polyfem.
Definition Logger.cpp:68
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:79
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
static int compute_variable_size(const json &args, const std::vector< std::shared_ptr< varform::DifferentiableVarForm > > &varforms)
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)