24#include <polyfem/embedded_spec/polyfem.hpp>
25#include <polyfem/embedded_spec/polyfem_dirichlet.hpp>
27#include <polysolve/linear/Solver.hpp>
29#include <spdlog/sinks/basic_file_sink.h>
30#include <spdlog/sinks/ostream_sink.h>
31#include <spdlog/sinks/stdout_color_sinks.h>
33#include <ipc/utils/logger.hpp>
46 spdlog::level::level_enum,
47 {{spdlog::level::level_enum::trace,
"trace"},
48 {spdlog::level::level_enum::debug,
"debug"},
49 {spdlog::level::level_enum::info,
"info"},
50 {spdlog::level::level_enum::warn,
"warning"},
51 {spdlog::level::level_enum::err,
"error"},
52 {spdlog::level::level_enum::critical,
"critical"},
53 {spdlog::level::level_enum::off,
"off"},
54 {spdlog::level::level_enum::trace, 0},
55 {spdlog::level::level_enum::debug, 1},
56 {spdlog::level::level_enum::info, 2},
57 {spdlog::level::level_enum::warn, 3},
58 {spdlog::level::level_enum::err, 3},
59 {spdlog::level::level_enum::critical, 4},
60 {spdlog::level::level_enum::off, 5}})
66 using namespace utils;
70 std::string formulation_from_args(
const json &args)
72 if (!
args.contains(
"materials") || args[
"materials"].is_null())
75 if (args[
"materials"].is_array())
78 for (
const auto &m :
args[
"materials"])
80 const std::string
tmp = m[
"type"];
83 else if (current != tmp)
88 current =
"MultiModels";
100 return args[
"materials"].value(
"type",
"");
103 std::string resolve_output_path(
const std::string &output_dir,
const std::string &path)
105 if (output_dir.empty() ||
path.empty() || std::filesystem::path(path).is_absolute())
107 return std::filesystem::weakly_canonical(std::filesystem::path(output_dir) / path).string();
110 bool contact_enabled(
const json &args)
112 return args[
"contact"][
"enabled"];
115 void init_time(
json &args, Units &units)
120 const double t0 =
Units::convert(args[
"time"][
"t0"], units.time());
132 else if (num_valid == 2)
142 time_steps = int(std::ceil((tend - t0) / dt));
143 assert(time_steps > 0);
147 time_steps =
args[
"time"][
"time_steps"];
150 dt = (tend - t0) / time_steps;
155 throw std::runtime_error(
"This code should be unreachable!");
165 time_steps =
args[
"time"][
"time_steps"];
170 tend = t0 + time_steps * dt;
174 throw std::runtime_error(
"This code should be unreachable!");
177 else if (num_valid == 3)
181 time_steps =
args[
"time"][
"time_steps"];
183 if (std::abs(t0 + dt * time_steps - tend) > 1e-12)
187 args[
"time"][
"tend"] = tend;
188 args[
"time"][
"dt"] = dt;
189 args[
"time"][
"time_steps"] = time_steps;
191 units.characteristic_length() *= dt;
193 logger().info(
"t0={}, dt={}, tend={}", t0, dt, tend);
203 const std::string &log_file,
204 const spdlog::level::level_enum log_level,
205 const spdlog::level::level_enum file_log_level,
208 std::vector<spdlog::sink_ptr> sinks;
212 console_sink_ = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
216 if (!log_file.empty())
218 file_sink_ = std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file,
true);
224 spdlog::flush_every(std::chrono::seconds(3));
229 std::vector<spdlog::sink_ptr> sinks;
230 sinks.emplace_back(std::make_shared<spdlog::sinks::ostream_sink_mt>(os,
false));
235 const std::vector<spdlog::sink_ptr> &sinks,
236 const spdlog::level::level_enum log_level)
238 set_logger(std::make_shared<spdlog::logger>(
"polyfem", sinks.begin(), sinks.end()));
241 ipc::set_logger(std::make_shared<spdlog::logger>(
"ipctk", sinks.begin(), sinks.end()));
243 logger().set_level(spdlog::level::trace);
244 ipc::logger().set_level(spdlog::level::trace);
251 spdlog::set_level(log_level);
258 logger().set_level(log_level);
259 ipc::logger().set_level(log_level);
264 const json &p_args_in,
265 const bool strict_validation,
266 const bool is_adjoint_optimization)
269 init(p_args_in, resources, strict_validation, is_adjoint_optimization);
273 const json &p_args_in,
275 const bool strict_validation,
276 const bool is_adjoint_optimization)
279 json args_in = p_args_in;
281 ? args_in[
"root_path"].get<std::string>()
284 args_in.erase(
"root_path");
285 const bool contact_dhat_was_explicit = args_in.contains(
"/contact/dhat"_json_pointer);
293 jse.strict = strict_validation;
294 rules = jse::embed::polyfem_spec::polyfem::spec();
296 polysolve::linear::Solver::apply_default_solver(rules,
"/solver/linear");
297 polysolve::linear::Solver::apply_default_solver(rules,
"/solver/adjoint_linear");
300 polysolve::linear::Solver::select_valid_solver(args_in[
"solver"][
"linear"],
logger());
301 if (args_in[
"solver"][
"adjoint_linear"].is_null())
302 args_in[
"solver"][
"adjoint_linear"] = args_in[
"solver"][
"linear"];
304 polysolve::linear::Solver::select_valid_solver(args_in[
"solver"][
"adjoint_linear"],
logger());
306 if (args_in.contains(
"/solver/nonlinear"_json_pointer))
308 if (args_in.contains(
"/solver/augmented_lagrangian/nonlinear"_json_pointer))
310 assert(args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"].is_object());
311 json nonlinear = args_in[
"solver"][
"nonlinear"];
312 nonlinear.merge_patch(args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"]);
313 args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"] = nonlinear;
317 args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"] = args_in[
"solver"][
"nonlinear"];
321 const bool valid_input = jse.verify_json(args_in, rules);
324 logger().error(
"invalid input json:\n{}", jse.log2str());
325 throw std::runtime_error(
"Invalid input json file");
328 args = jse.inject_defaults(args_in, rules);
331 args, jse::embed::polyfem_dirichlet_spec::polyfem_dirichlet::spec(), *
resources_);
336 if (!args_in.contains(
"/space/advanced/bc_method"_json_pointer) &&
args[
"space"][
"basis_type"] !=
"Lagrange")
338 logger().warn(
"Setting bc method to lsq for non-Lagrange basis");
339 args[
"space"][
"advanced"][
"bc_method"] =
"lsq";
342 const std::string configured_output_dir =
args[
"output"][
"directory"].get<std::string>();
343 const std::string output_dir = configured_output_dir.empty()
345 : (std::filesystem::path(configured_output_dir).is_absolute()
346 ? std::filesystem::path(configured_output_dir)
347 :
resources_->host_directory() / configured_output_dir)
350 if (!output_dir.empty())
351 std::filesystem::create_directories(output_dir);
353 std::string out_path_log =
args[
"output"][
"log"][
"path"];
354 if (!out_path_log.empty())
355 out_path_log = resolve_output_path(output_dir, out_path_log);
359 args[
"output"][
"log"][
"level"],
360 args[
"output"][
"log"][
"file_level"],
361 args[
"output"][
"log"][
"quiet"]);
363 logger().info(
"Saving output to {}", output_dir);
367 init_time(
args, units);
369 if (contact_enabled(
args))
371 if (
args[
"solver"][
"contact"][
"friction_iterations"] == 0)
373 logger().info(
"specified friction_iterations is 0; disabling friction");
374 args[
"contact"][
"friction_coefficient"] = 0.0;
376 else if (
args[
"solver"][
"contact"][
"friction_iterations"] < 0)
378 args[
"solver"][
"contact"][
"friction_iterations"] = std::numeric_limits<int>::max();
380 if (
args[
"contact"][
"friction_coefficient"] == 0.0)
382 args[
"solver"][
"contact"][
"friction_iterations"] = 0;
387 args[
"solver"][
"contact"][
"friction_iterations"] = 0;
388 args[
"contact"][
"friction_coefficient"] = 0;
389 args[
"contact"][
"periodic"] =
false;
392 const std::string formulation = formulation_from_args(
args);
393 if (formulation.empty())
395 logger().error(
"specify some 'materials'");
396 throw std::runtime_error(
"invalid input");
401 throw std::runtime_error(
"Unsupported configuration for formulation " + formulation +
".");
404 args[
"contact"][
"_dhat_was_explicit"] = contact_dhat_was_explicit;
406 args[
"contact"].erase(
"_dhat_was_explicit");
411 init(checkpoint.
config(), checkpoint, strict_validation);
415 const json &continuation,
417 const bool strict_validation)
419 init(continuation, checkpoint.
resources(), strict_validation,
false);
424 "Checkpoint formulation '{}' is incompatible with configured formulation '{}'.",
426 if (std::abs(
args[
"time"][
"dt"].get<double>() -
checkpoint_->get().metadata().dt) > 1e-12)
428 if (std::abs(
args[
"time"][
"t0"].get<double>() -
checkpoint_->get().metadata().time) > 1e-12)
429 log_and_throw_error(
"Checkpoint time is incompatible with its continuation configuration.");
439 const std::function<
int(
const size_t,
const std::vector<int> &,
const RowVectorNd &,
bool)> &boundary_marker,
441 bool skip_boundary_sideset)
445 logger().info(
"Loading mesh...");
451 logger().error(
"Unable to load the mesh");
456 mesh->bounding_box(min, max);
458 logger().info(
"mesh bb min [{}], max [{}]", min, max);
460 if (!skip_boundary_sideset)
461 mesh->compute_boundary_ids(boundary_marker);
464 logger().info(
" took {}s", timer.getElapsedTime());
475 logger().info(
"Loading mesh ...");
485 loaded_geometry.
fem =
checkpoint_->get().read_mesh(
"/checkpoint/meshes/active", non_conforming);
487 args[
"geometry"], obstacle_displacements, dirichlet_conditions,
488 loaded_geometry.
fem->dimension(), non_conforming);
491 loaded_geometry = geometry_loader.
load(
492 args[
"geometry"], obstacle_displacements, dirichlet_conditions, non_conforming);
493 auto &mesh = loaded_geometry.
fem;
499 mesh->bounding_box(min, max);
501 logger().info(
"mesh bb min [{}], max [{}]", min, max);
504 logger().info(
" took {}s", timer.getElapsedTime());
506#ifdef POLYFEM_WITH_MISO
507 if (!mesh->is_simplicial())
512 args[
"space"][
"advanced"][
"count_flipped_els_continuous"] =
false;
513 args[
"output"][
"paraview"][
"options"][
"jacobian_validity"] =
false;
514 args[
"solver"][
"advanced"][
"check_inversion"] =
"Discrete";
516 else if (
args[
"solver"][
"advanced"][
"check_inversion"] !=
"Discrete")
518 args[
"space"][
"advanced"][
"use_corner_quadrature"] =
true;
std::shared_ptr< varform::VarForm > variational_formulation
active variational formulation
void init(const json &args, bool strict_validation, bool is_adjoint_optimization=false)
initialize the polyfem solver with a json settings
void solve(Eigen::MatrixXd &sol)
solves the problem, call other methods
void set_mesh(GEO::Mesh &meshin, const std::function< int(const size_t, const std::vector< int > &, const RowVectorNd &, bool)> &boundary_marker, bool non_conforming=false, bool skip_boundary_sideset=false)
loads the mesh from a geogram mesh
void set_max_threads(const int max_threads=std::numeric_limits< int >::max())
std::optional< std::reference_wrapper< const io::CheckpointReader > > checkpoint_
spdlog::sink_ptr file_sink_
spdlog::sink_ptr console_sink_
logger sink to stdout
json args
main input arguments containing all defaults
void set_log_level(const spdlog::level::level_enum log_level)
change log level
std::unique_ptr< const io::ResourceIO > resources_
std::function< void(int, int, double, double)> time_callback
Optional UI progress callback.
void load_mesh(bool non_conforming=false)
loads the mesh from the json arguments
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
void init(const json &json)
static double convert(const json &val, const std::string &unit_type)
static bool is_elastic_material(const std::string &material)
utility to check if material is one of the elastic materials
const ResourceIO & resources() const
const json & config() const
Read-only interface to a hierarchical collection of simulation resources.
virtual std::unique_ptr< const ResourceIO > with_root(const std::string &root) const =0
Applies geometry JSON semantics while delegating all decoding to MeshLoader.
Obstacle load_obstacles(const json &geometry, const std::vector< json > &obstacle_displacements, const std::vector< json > &dirichlet_conditions, int dimension, bool non_conforming=false) const
LoadedGeometry load(const json &geometry, const std::vector< json > &obstacle_displacements, const std::vector< json > &dirichlet_conditions, bool non_conforming=false) const
std::unique_ptr< Mesh > fem
Format-independent input used to construct a runtime Mesh.
static std::unique_ptr< Mesh > create(MeshData data, bool non_conforming=false)
Construct a runtime mesh from format-independent decoded data.
static MeshData from_geogram(GEO::Mesh &mesh)
void set_logger(spdlog::logger &logger)
static GeogramUtils & instance()
void set_num_threads(const int max_threads)
NLOHMANN_JSON_SERIALIZE_ENUM(CollisionProxyTessellation, {{CollisionProxyTessellation::REGULAR, "regular"}, {CollisionProxyTessellation::IRREGULAR, "irregular"}})
std::unique_ptr< const io::ResourceIO > apply_common_params(json &args, const io::ResourceIO &resources)
void expand_bc_sidecars(json &args, const json &rules)
Expand string entries in dirichlet_boundary that point to .json files.
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
bool is_param_valid(const json ¶ms, const std::string &key)
Determine if a key exists and is non-null in a json object.
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void set_logger(std::shared_ptr< spdlog::logger > p_logger)
Setup a logger object to be used by Polyfem.
void log_and_throw_error(const std::string &msg)