19#include <spdlog/sinks/stdout_color_sinks.h>
20#include <spdlog/sinks/basic_file_sink.h>
21#include <spdlog/sinks/ostream_sink.h>
23#include <ipc/utils/logger.hpp>
24#include <wmtk/utils/Logger.hpp>
34 spdlog::level::level_enum,
35 {{spdlog::level::level_enum::trace,
"trace"},
36 {spdlog::level::level_enum::debug,
"debug"},
37 {spdlog::level::level_enum::info,
"info"},
38 {spdlog::level::level_enum::warn,
"warning"},
39 {spdlog::level::level_enum::err,
"error"},
40 {spdlog::level::level_enum::critical,
"critical"},
41 {spdlog::level::level_enum::off,
"off"},
42 {spdlog::level::level_enum::trace, 0},
43 {spdlog::level::level_enum::debug, 1},
44 {spdlog::level::level_enum::info, 2},
45 {spdlog::level::level_enum::warn, 3},
46 {spdlog::level::level_enum::err, 3},
47 {spdlog::level::level_enum::critical, 4},
48 {spdlog::level::level_enum::off, 5}})
53 using namespace problem;
54 using namespace utils;
66 const std::string &log_file,
67 const spdlog::level::level_enum log_level,
68 const spdlog::level::level_enum file_log_level,
71 std::vector<spdlog::sink_ptr> sinks;
75 console_sink_ = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
79 if (!log_file.empty())
81 file_sink_ = std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file,
true);
88 spdlog::flush_every(std::chrono::seconds(3));
93 std::vector<spdlog::sink_ptr> sinks;
94 sinks.emplace_back(std::make_shared<spdlog::sinks::ostream_sink_mt>(os,
false));
99 const std::vector<spdlog::sink_ptr> &sinks,
100 const spdlog::level::level_enum log_level)
102 set_logger(std::make_shared<spdlog::logger>(
"polyfem", sinks.begin(), sinks.end()));
105 ipc::set_logger(std::make_shared<spdlog::logger>(
"ipctk", sinks.begin(), sinks.end()));
107 wmtk::set_logger(std::make_shared<spdlog::logger>(
"wmtk", sinks.begin(), sinks.end()));
110 logger().set_level(spdlog::level::trace);
111 ipc::logger().set_level(spdlog::level::trace);
112 wmtk::logger().set_level(spdlog::level::trace);
119 spdlog::set_level(log_level);
128 logger().set_level(log_level);
129 ipc::logger().set_level(log_level);
135 json args_in = p_args_in;
143 jse.strict = strict_validation;
144 const std::string polyfem_input_spec = POLYFEM_INPUT_SPEC;
145 std::ifstream file(polyfem_input_spec);
151 logger().error(
"unable to open {} rules", polyfem_input_spec);
152 throw std::runtime_error(
"Invald spec file");
155 jse.include_directories.push_back(POLYFEM_JSON_SPEC_DIR);
156 jse.include_directories.push_back(POLYSOLVE_JSON_SPEC_DIR);
157 rules = jse.inject_include(rules);
159 polysolve::linear::Solver::apply_default_solver(rules,
"/solver/linear");
160 polysolve::linear::Solver::apply_default_solver(rules,
"/solver/adjoint_linear");
163 polysolve::linear::Solver::select_valid_solver(args_in[
"solver"][
"linear"],
logger());
164 if (args_in[
"solver"][
"adjoint_linear"].is_null())
165 args_in[
"solver"][
"adjoint_linear"] = args_in[
"solver"][
"linear"];
167 polysolve::linear::Solver::select_valid_solver(args_in[
"solver"][
"adjoint_linear"],
logger());
170 if (args_in.contains(
"/solver/nonlinear"_json_pointer))
172 if (args_in.contains(
"/solver/augmented_lagrangian/nonlinear"_json_pointer))
174 assert(args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"].is_object());
177 json nonlinear = args_in[
"solver"][
"nonlinear"];
178 nonlinear.merge_patch(args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"]);
179 args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"] = nonlinear;
184 args_in[
"solver"][
"augmented_lagrangian"][
"nonlinear"] = args_in[
"solver"][
"nonlinear"];
187 const bool valid_input = jse.verify_json(args_in, rules);
191 logger().error(
"invalid input json:\n{}", jse.log2str());
192 throw std::runtime_error(
"Invald input json file");
196 this->
args = jse.inject_defaults(args_in, rules);
199 if (!args_in.contains(
"/space/advanced/bc_method"_json_pointer) && this->args[
"space"][
"basis_type"] !=
"Lagrange")
201 logger().warn(
"Setting bc method to lsq for non-Lagrange basis");
202 this->
args[
"space"][
"advanced"][
"bc_method"] =
"lsq";
207 if (!output_dir.empty())
209 std::filesystem::create_directories(
output_dir);
213 std::string out_path_log = this->
args[
"output"][
"log"][
"path"];
214 if (!out_path_log.empty())
221 this->
args[
"output"][
"log"][
"level"],
222 this->
args[
"output"][
"log"][
"file_level"],
223 this->
args[
"output"][
"log"][
"quiet"]);
225 logger().info(
"Saving output to {}", output_dir);
227 const unsigned int thread_in = this->
args[
"solver"][
"max_threads"];
230 has_dhat = args_in[
"contact"].contains(
"dhat");
236 if (
args[
"solver"][
"contact"][
"friction_iterations"] == 0)
238 logger().info(
"specified friction_iterations is 0; disabling friction");
239 args[
"contact"][
"friction_coefficient"] = 0.0;
241 else if (
args[
"solver"][
"contact"][
"friction_iterations"] < 0)
243 args[
"solver"][
"contact"][
"friction_iterations"] = std::numeric_limits<int>::max();
245 if (
args[
"contact"][
"friction_coefficient"] == 0.0)
247 args[
"solver"][
"contact"][
"friction_iterations"] = 0;
252 args[
"solver"][
"contact"][
"friction_iterations"] = 0;
253 args[
"contact"][
"friction_coefficient"] = 0;
254 args[
"contact"][
"periodic"] =
false;
263 if (!other_name.empty())
269 if (
args[
"solver"][
"advanced"][
"check_inversion"] ==
"Conservative")
271 if (
auto elastic_assembler = std::dynamic_pointer_cast<assembler::ElasticityAssembler>(
assembler))
272 elastic_assembler->set_use_robust_jacobian();
275 if (!
args.contains(
"preset_problem"))
278 problem = std::make_shared<assembler::GenericScalarProblem>(
"GenericScalar");
280 problem = std::make_shared<assembler::GenericTensorProblem>(
"GenericTensor");
283 if (!
args[
"time"].is_null())
285 const auto tmp = R
"({"is_time_dependent": true})"_json;
290 auto bc =
args[
"boundary_conditions"];
293 problem->set_parameters(
args[
"initial_conditions"]);
299 if (
args[
"preset_problem"][
"type"] ==
"Kernel")
320 if (!
args[
"contact"][
"use_convergent_formulation"])
322 args[
"contact"][
"use_convergent_formulation"] =
true;
323 logger().info(
"Use convergent formulation for differentiable contact...");
325 if (
args[
"/solver/contact/barrier_stiffness"_json_pointer].is_string())
327 logger().error(
"Only constant barrier stiffness is supported in differentiable contact!");
331 if (
args.contains(
"boundary_conditions") &&
args[
"boundary_conditions"].contains(
"rhs"))
334 if ((
rhs.is_array() &&
rhs.size() > 0 &&
rhs[0].is_string()) ||
rhs.is_string())
335 logger().error(
"Only constant rhs over space is supported in differentiable code!");
362 else if (num_valid == 2)
372 time_steps = int(ceil((tend - t0) / dt));
373 assert(time_steps > 0);
377 time_steps =
args[
"time"][
"time_steps"];
378 assert(time_steps > 0);
379 dt = (tend - t0) / time_steps;
384 throw std::runtime_error(
"This code should be unreachable!");
395 time_steps =
args[
"time"][
"time_steps"];
396 assert(time_steps > 0);
398 tend = t0 + time_steps * dt;
403 throw std::runtime_error(
"This code should be unreachable!");
406 else if (num_valid == 3)
410 time_steps =
args[
"time"][
"time_steps"];
413 if (abs(t0 + dt * time_steps - tend) > 1e-12)
420 args[
"time"][
"tend"] = tend;
421 args[
"time"][
"dt"] = dt;
422 args[
"time"][
"time_steps"] = time_steps;
426 logger().info(
"t0={}, dt={}, tend={}", t0, dt, tend);
432 for (
auto &a : assemblers)
438 if (!
args[
"materials"].is_array() &&
args[
"materials"][
"type"] ==
"AMIPSAutodiff")
440 json transform_params = {};
441 transform_params[
"canonical_transformation"] = json::array();
442 if (!
mesh->is_volume())
444 Eigen::MatrixXd regular_tri(3, 3);
445 regular_tri << 0, 0, 1,
447 1. / 2., std::sqrt(3) / 2., 1;
448 regular_tri.transposeInPlace();
449 Eigen::MatrixXd regular_tri_inv = regular_tri.inverse();
452 for (
int e = 0; e <
mesh->n_elements(); e++)
454 Eigen::MatrixXd transform;
456 transform_params[
"canonical_transformation"].push_back(
json({
470 Eigen::MatrixXd regular_tet(4, 4);
471 regular_tet << 0, 0, 0, 1,
473 1. / 2., std::sqrt(3) / 2., 0, 1,
474 1. / 2., 1. / 2. / std::sqrt(3), std::sqrt(3) / 2., 1;
475 regular_tet.transposeInPlace();
476 Eigen::MatrixXd regular_tet_inv = regular_tet.inverse();
479 for (
int e = 0; e <
mesh->n_elements(); e++)
481 Eigen::MatrixXd transform;
483 transform_params[
"canonical_transformation"].push_back(
json({
502 transform_params[
"solve_displacement"] =
true;
508 std::vector<int> body_ids(
mesh->n_elements());
509 for (
int i = 0; i <
mesh->n_elements(); ++i)
510 body_ids[i] =
mesh->get_body_id(i);
512 for (
auto &a : assemblers)
513 a->set_materials(body_ids,
args[
"materials"],
units);
518 const int size = (this->assembler->
is_tensor() || this->assembler->
is_fluid()) ? this->
mesh->dimension() : 1;
524 std::vector<int> body_ids(
mesh->n_elements());
525 for (
int i = 0; i <
mesh->n_elements(); ++i)
526 body_ids[i] =
mesh->get_body_id(i);
std::string formulation() const
return the formulation (checks if the problem is scalar or not and deals with multiphysics)
void init(const json &args, const bool strict_validation)
initialize the polyfem solver with a json settings
std::string root_path() const
Get the root path for the state (e.g., args["root_path"] or ".")
std::shared_ptr< assembler::Assembler > assembler
assemblers
std::string resolve_output_path(const std::string &path) const
Resolve output path relative to output_dir if the path is not absolute.
std::string output_dir
Directory for output files.
solver::CacheLevel optimization_enabled
void set_materials(std::vector< std::shared_ptr< assembler::Assembler > > &assemblers) const
set the material and the problem dimension
void set_max_threads(const int max_threads=std::numeric_limits< int >::max())
std::shared_ptr< assembler::Assembler > pressure_assembler
std::shared_ptr< assembler::Mass > mass_matrix_assembler
bool has_dhat
stores if input json contains dhat
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
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
void init_time()
initialize time settings if args contains "time"
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Resolve input path relative to root_path() if the path is not absolute.
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
bool is_contact_enabled() const
does the simulation has contact
std::shared_ptr< assembler::MixedAssembler > mixed_assembler
Eigen::MatrixXd rhs
System right-hand side.
void init(const json &json)
double characteristic_length() const
static double convert(const json &val, const std::string &unit_type)
const std::string & time() const
virtual bool is_tensor() const
virtual void set_size(const int size)
virtual bool is_fluid() const
static std::shared_ptr< MixedAssembler > make_mixed_assembler(const std::string &formulation)
static std::string other_assembler_name(const std::string &formulation)
static std::shared_ptr< Assembler > make_assembler(const std::string &formulation)
void compute_face_jacobian(const int el_id, const Eigen::MatrixXd &reference_map, Eigen::MatrixXd &jacobian) const
void compute_cell_jacobian(const int el_id, const Eigen::MatrixXd &reference_map, Eigen::MatrixXd &jacobian) const
static const ProblemFactory & factory()
std::shared_ptr< assembler::Problem > get_problem(const std::string &problem) const
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"}})
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.
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)