19#include <polyfem/embedded_spec/polyfem.hpp>
20#include <polyfem/embedded_spec/polyfem_dirichlet.hpp>
22#include <polysolve/linear/Solver.hpp>
24#include <spdlog/sinks/basic_file_sink.h>
25#include <spdlog/sinks/ostream_sink.h>
26#include <spdlog/sinks/stdout_color_sinks.h>
28#include <ipc/utils/logger.hpp>
29#ifdef POLYFEM_WITH_ITR
30#include <wmtk/utils/Logger.hpp>
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}})
64 using namespace utils;
68 std::string root_path(
const json &args)
71 return args[
"root_path"].get<std::string>();
75 std::string resolve_input_path(
const json &args,
const std::string &path,
const bool only_if_exists =
false)
80 std::string resolve_output_path(
const std::string &output_dir,
const std::string &path)
82 if (output_dir.empty() ||
path.empty() || std::filesystem::path(path).is_absolute())
84 return std::filesystem::weakly_canonical(std::filesystem::path(output_dir) / path).string();
87 bool contact_enabled(
const json &args)
89 return args[
"contact"][
"enabled"];
92 void init_time(
json &args, Units &units)
97 const double t0 =
Units::convert(args[
"time"][
"t0"], units.time());
109 else if (num_valid == 2)
119 time_steps = int(std::ceil((tend - t0) / dt));
120 assert(time_steps > 0);
124 time_steps =
args[
"time"][
"time_steps"];
125 assert(time_steps > 0);
126 dt = (tend - t0) / time_steps;
131 throw std::runtime_error(
"This code should be unreachable!");
141 time_steps =
args[
"time"][
"time_steps"];
142 assert(time_steps > 0);
144 tend = t0 + time_steps * dt;
148 throw std::runtime_error(
"This code should be unreachable!");
151 else if (num_valid == 3)
155 time_steps =
args[
"time"][
"time_steps"];
157 if (std::abs(t0 + dt * time_steps - tend) > 1e-12)
161 args[
"time"][
"tend"] = tend;
162 args[
"time"][
"dt"] = dt;
163 args[
"time"][
"time_steps"] = time_steps;
165 units.characteristic_length() *= dt;
167 logger().info(
"t0={}, dt={}, tend={}", t0, dt, tend);
177 const std::string &log_file,
178 const spdlog::level::level_enum log_level,
179 const spdlog::level::level_enum file_log_level,
182 std::vector<spdlog::sink_ptr> sinks;
186 console_sink_ = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
190 if (!log_file.empty())
192 file_sink_ = std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file,
true);
198 spdlog::flush_every(std::chrono::seconds(3));
203 std::vector<spdlog::sink_ptr> sinks;
204 sinks.emplace_back(std::make_shared<spdlog::sinks::ostream_sink_mt>(os,
false));
209 const std::vector<spdlog::sink_ptr> &sinks,
210 const spdlog::level::level_enum log_level)
212 set_logger(std::make_shared<spdlog::logger>(
"polyfem", sinks.begin(), sinks.end()));
215 ipc::set_logger(std::make_shared<spdlog::logger>(
"ipctk", sinks.begin(), sinks.end()));
217#ifdef POLYFEM_WITH_ITR
218 wmtk::set_logger(std::make_shared<spdlog::logger>(
"wmtk", sinks.begin(), sinks.end()));
221 logger().set_level(spdlog::level::trace);
222 ipc::logger().set_level(spdlog::level::trace);
223#ifdef POLYFEM_WITH_ITR
224 wmtk::logger().set_level(spdlog::level::trace);
232 spdlog::set_level(log_level);
239 logger().set_level(log_level);
240 ipc::logger().set_level(log_level);
245 const json &p_args_in,
246 const bool strict_validation,
247 const bool is_adjoint_optimization)
249 json args_in = p_args_in;
250 const bool contact_dhat_was_explicit = args_in.contains(
"/contact/dhat"_json_pointer);
257 jse.strict = strict_validation;
258 rules = jse::embed::polyfem_spec::polyfem::spec();
260 polysolve::linear::Solver::apply_default_solver(rules,
"/solver/linear");
261 polysolve::linear::Solver::apply_default_solver(rules,
"/solver/adjoint_linear");
264 polysolve::linear::Solver::select_valid_solver(args_in[
"solver"][
"linear"],
logger());
265 if (args_in[
"solver"][
"adjoint_linear"].is_null())
266 args_in[
"solver"][
"adjoint_linear"] = args_in[
"solver"][
"linear"];
268 polysolve::linear::Solver::select_valid_solver(args_in[
"solver"][
"adjoint_linear"],
logger());
270 if (args_in.contains(
"/solver/nonlinear"_json_pointer))
272 if (args_in.contains(
"/solver/augmented_lagrangian/nonlinear"_json_pointer))
274 assert(args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"].is_object());
275 json nonlinear = args_in[
"solver"][
"nonlinear"];
276 nonlinear.merge_patch(args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"]);
277 args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"] = nonlinear;
281 args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"] = args_in[
"solver"][
"nonlinear"];
285 const bool valid_input = jse.verify_json(args_in, rules);
288 logger().error(
"invalid input json:\n{}", jse.log2str());
289 throw std::runtime_error(
"Invalid input json file");
292 args = jse.inject_defaults(args_in, rules);
295 args, jse::embed::polyfem_dirichlet_spec::polyfem_dirichlet::spec());
300 if (!args_in.contains(
"/space/advanced/bc_method"_json_pointer) &&
args[
"space"][
"basis_type"] !=
"Lagrange")
302 logger().warn(
"Setting bc method to lsq for non-Lagrange basis");
303 args[
"space"][
"advanced"][
"bc_method"] =
"lsq";
306 const std::string output_dir = resolve_input_path(
args,
args[
"output"][
"directory"].get<std::string>());
307 if (!output_dir.empty())
308 std::filesystem::create_directories(output_dir);
310 std::string out_path_log =
args[
"output"][
"log"][
"path"];
311 if (!out_path_log.empty())
312 out_path_log = resolve_output_path(output_dir, out_path_log);
314 for (
auto &path :
args[
"constraints"][
"hard"])
315 path = resolve_input_path(
args, path.get<std::string>());
317 for (
auto &path :
args[
"constraints"][
"soft"])
318 path[
"data"] = resolve_input_path(
args, path[
"data"].get<std::string>());
322 args[
"output"][
"log"][
"level"],
323 args[
"output"][
"log"][
"file_level"],
324 args[
"output"][
"log"][
"quiet"]);
326 logger().info(
"Saving output to {}", output_dir);
330 init_time(
args, units);
332 if (contact_enabled(
args))
334 if (
args[
"solver"][
"contact"][
"friction_iterations"] == 0)
336 logger().info(
"specified friction_iterations is 0; disabling friction");
337 args[
"contact"][
"friction_coefficient"] = 0.0;
339 else if (
args[
"solver"][
"contact"][
"friction_iterations"] < 0)
341 args[
"solver"][
"contact"][
"friction_iterations"] = std::numeric_limits<int>::max();
343 if (
args[
"contact"][
"friction_coefficient"] == 0.0)
345 args[
"solver"][
"contact"][
"friction_iterations"] = 0;
350 args[
"solver"][
"contact"][
"friction_iterations"] = 0;
351 args[
"contact"][
"friction_coefficient"] = 0;
352 args[
"contact"][
"periodic"] =
false;
356 if (formulation.empty())
358 logger().error(
"specify some 'materials'");
359 throw std::runtime_error(
"invalid input");
364 throw std::runtime_error(
"polyfem::State is varform-only; use polyfem::legacy::State for " + formulation +
".");
367 args[
"contact"][
"_dhat_was_explicit"] = contact_dhat_was_explicit;
369 args[
"contact"].erase(
"_dhat_was_explicit");
379 const std::function<
int(
const size_t,
const std::vector<int> &,
const RowVectorNd &,
bool)> &boundary_marker,
381 bool skip_boundary_sideset)
385 logger().info(
"Loading mesh...");
387 std::unique_ptr<Mesh> mesh =
Mesh::create(meshin, non_conforming);
390 logger().error(
"Unable to load the mesh");
395 mesh->bounding_box(min, max);
397 logger().info(
"mesh bb min [{}], max [{}]", min, max);
399 if (!skip_boundary_sideset)
400 mesh->compute_boundary_ids(boundary_marker);
403 logger().info(
" took {}s", timer.getElapsedTime());
411 const std::vector<std::string> &names,
412 const std::vector<Eigen::MatrixXi> &cells,
413 const std::vector<Eigen::MatrixXd> &vertices)
415 assert(names.size() == cells.size());
416 assert(vertices.size() == cells.size());
421 logger().info(
"Loading mesh ...");
427 args[
"geometry"],
args[
"root_path"],
428 names, vertices, cells, non_conforming);
434 mesh->bounding_box(min, max);
436 logger().info(
"mesh bb min [{}], max [{}]", min, max);
439 logger().info(
" took {}s", timer.getElapsedTime());
441#ifdef POLYFEM_WITH_MISO
442 if (!mesh->is_simplicial())
447 args[
"space"][
"advanced"][
"count_flipped_els_continuous"] =
false;
448 args[
"output"][
"paraview"][
"options"][
"jacobian_validity"] =
false;
449 args[
"solver"][
"advanced"][
"check_inversion"] =
"Discrete";
451 else if (
args[
"solver"][
"advanced"][
"check_inversion"] !=
"Discrete")
453 args[
"space"][
"advanced"][
"use_corner_quadrature"] =
true;
std::shared_ptr< varform::VarForm > variational_formulation
active variational formulation
void set_max_threads(const int max_threads=std::numeric_limits< int >::max())
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::function< void(int, int, double, double)> time_callback
Optional UI progress callback.
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)
void init(const json &args, const bool strict_validation)
initialize the polyfem solver with a json settings
void load_mesh(bool non_conforming=false, const std::vector< std::string > &names=std::vector< std::string >(), const std::vector< Eigen::MatrixXi > &cells=std::vector< Eigen::MatrixXi >(), const std::vector< Eigen::MatrixXd > &vertices=std::vector< Eigen::MatrixXd >())
loads the mesh from the json arguments
void set_max_threads(const int max_threads=std::numeric_limits< int >::max())
void set_log_level(const spdlog::level::level_enum log_level)
change log level
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 solve(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves the problem, call other methods
static std::unique_ptr< Mesh > create(const std::string &path, const bool non_conforming=false)
factory to build the proper 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< Mesh > read_fem_geometry(const Units &units, const json &geometry, const std::string &root_path, const std::vector< std::string > &_names, const std::vector< Eigen::MatrixXd > &_vertices, const std::vector< Eigen::MatrixXi > &_cells, const bool non_conforming)
read FEM meshes from a geometry JSON array (or single)
std::string resolve_path(const std::string &path, const std::string &input_file_path, const bool only_if_exists=false)
void expand_bc_sidecars(json &args, const json &rules)
Expand string entries in dirichlet_boundary that point to .json files.
bool is_param_valid(const json ¶ms, const std::string &key)
Determine if a key exists and is non-null in a json object.
void apply_common_params(json &args)
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)