PolyFEM
Loading...
Searching...
No Matches
BuildFromJson.cpp
Go to the documentation of this file.
2
3#include <polyfem/State.hpp>
4#include <polyfem/Common.hpp>
5
7
10
26
27#include <Eigen/Core>
28#include <spdlog/fmt/fmt.h>
29
30#include <string>
31#include <memory>
32#include <cassert>
33#include <cstddef>
34#include <fstream>
35#include <set>
36#include <utility>
37#include <vector>
38
40{
41 namespace
42 {
43 bool load_json(const std::string &json_file, json &out)
44 {
45 std::ifstream file(json_file);
46 if (!file.is_open())
47 {
48 return false;
49 }
50
51 file >> out;
52
53 out["root_path"] = json_file;
54
55 return true;
56 }
57 } // namespace
58
59 std::shared_ptr<State> build_state(
60 const json &args,
61 const size_t max_threads)
62 {
63 std::shared_ptr<State> state = std::make_shared<State>();
64 state->set_max_threads(max_threads);
65
66 json in_args = args;
67 in_args["solver"]["max_threads"] = max_threads;
68 if (!args.contains("output") || !args["output"].contains("log") || !args["output"]["log"].contains("level"))
69 {
70 const json tmp = R"({
71 "output": {
72 "log": {
73 "level": "error"
74 }
75 }
76 })"_json;
77
78 in_args.merge_patch(tmp);
79 }
80
81 state->optimization_enabled = true;
82 state->init(in_args, true);
83 state->load_mesh();
84 state->build_basis();
85 state->assemble_rhs();
86 state->assemble_mass_mat();
87
88 return state;
89 }
90
91 std::vector<std::shared_ptr<State>> build_states(
92 const std::string &root_path,
93 const json &args,
94 const size_t max_threads)
95 {
96 std::vector<std::shared_ptr<State>> states(args.size());
97 for (int i = 0; i < args.size(); ++i)
98 {
99 json cur_args;
100 std::string abs_path = utils::resolve_path(args[i]["path"], root_path, false);
101 if (!load_json(abs_path, cur_args))
102 {
103 log_and_throw_adjoint_error("Can't find json for State {}", i);
104 }
105
106 states[i] = build_state(cur_args, max_threads);
107 }
108 return states;
109 }
110
111 std::shared_ptr<solver::Parametrization> build_parametrization(
112 const json &args,
113 const std::vector<std::shared_ptr<State>> &states,
114 const std::vector<int> &variable_sizes)
115 {
116 using namespace polyfem::solver;
117
118 std::shared_ptr<Parametrization> map;
119 const std::string type = args["type"];
120 if (type == "per-body-to-per-elem")
121 {
122 map = std::make_shared<PerBody2PerElem>(*(states[args["state"]]->mesh));
123 }
124 else if (type == "per-body-to-per-node")
125 {
126 map = std::make_shared<PerBody2PerNode>(*(states[args["state"]]->mesh),
127 states[args["state"]]->bases,
128 states[args["state"]]->n_bases);
129 }
130 else if (type == "E-nu-to-lambda-mu")
131 {
132 map = std::make_shared<ENu2LambdaMu>(args["is_volume"]);
133 }
134 else if (type == "slice")
135 {
136 if (args["from"] != -1 || args["to"] != -1)
137 {
138 map = std::make_shared<SliceMap>(args["from"], args["to"], args["last"]);
139 }
140 else if (args["parameter_index"] != -1)
141 {
142 int idx = args["parameter_index"].get<int>();
143 int from, to, last;
144 int cumulative = 0;
145 for (int i = 0; i < variable_sizes.size(); ++i)
146 {
147 if (i == idx)
148 {
149 from = cumulative;
150 to = from + variable_sizes[i];
151 }
152 cumulative += variable_sizes[i];
153 }
154 last = cumulative;
155 map = std::make_shared<SliceMap>(from, to, last);
156 }
157 else
158 {
159 log_and_throw_adjoint_error("Incorrect spec for SliceMap!");
160 }
161 }
162 else if (type == "exp")
163 {
164 map = std::make_shared<ExponentialMap>(args["from"], args["to"]);
165 }
166 else if (type == "scale")
167 {
168 map = std::make_shared<Scaling>(args["value"]);
169 }
170 else if (type == "power")
171 {
172 map = std::make_shared<PowerMap>(args["power"]);
173 }
174 else if (type == "append-values")
175 {
176 Eigen::VectorXd vals = args["values"];
177 map = std::make_shared<InsertConstantMap>(vals, args["start"]);
178 }
179 else if (type == "append-const")
180 {
181 map = std::make_shared<InsertConstantMap>(args["size"], args["value"], args["start"]);
182 }
183 else if (type == "linear-filter")
184 {
185 map = std::make_shared<LinearFilter>(*(states[args["state"]]->mesh), args["radius"]);
186 }
187 else if (type == "bounded-biharmonic-weights")
188 {
189 map = std::make_shared<BoundedBiharmonicWeights2Dto3D>(
190 args["num_control_vertices"], args["num_vertices"],
191 *states[args["state"]], args["allow_rotations"]);
192 }
193 else if (type == "scalar-velocity-parametrization")
194 {
195 map = std::make_shared<ScalarVelocityParametrization>(args["start_val"], args["dt"]);
196 }
197 else
198 {
199 log_and_throw_adjoint_error("Unkown parametrization!");
200 }
201
202 return map;
203 }
204
205 std::unique_ptr<solver::VariableToSimulation> build_variable_to_simulation(
206 const json &args,
207 const std::vector<std::shared_ptr<State>> &states,
208 const std::vector<std::shared_ptr<DiffCache>> &diff_caches,
209 const std::vector<int> &variable_sizes)
210 {
211 using namespace polyfem::solver;
212
213 // Collect relevant states from state index json.
214 std::vector<std::shared_ptr<State>> rel_states;
215 std::vector<std::shared_ptr<DiffCache>> rel_diff_caches;
216 if (args["state"].is_array())
217 {
218 for (int i : args["state"])
219 {
220 rel_states.push_back(states[i]);
221 rel_diff_caches.push_back(diff_caches[i]);
222 }
223 }
224 else
225 {
226 const int state_id = args["state"];
227 rel_states.push_back(states[state_id]);
228 rel_diff_caches.push_back(diff_caches[state_id]);
229 }
230
231 // Build all parametrizations.
232 std::vector<std::shared_ptr<Parametrization>> map_list;
233 for (const auto &arg : args["composition"])
234 {
235 map_list.push_back(build_parametrization(arg, states, variable_sizes));
236 }
237 CompositeParametrization compo{std::move(map_list)};
238
239 // Build VariableToSimulation based on type string.
240 std::string type = args["type"];
241 std::unique_ptr<VariableToSimulation> var2sim;
242 if (type == "shape")
243 {
244 var2sim = std::make_unique<ShapeVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
245 }
246 else if (type == "elastic")
247 {
248 var2sim = std::make_unique<ElasticVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
249 }
250 else if (type == "friction")
251 {
252 var2sim = std::make_unique<FrictionCoeffientVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
253 }
254 else if (type == "damping")
255 {
256 var2sim = std::make_unique<DampingCoeffientVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
257 }
258 else if (type == "initial")
259 {
260 var2sim = std::make_unique<InitialConditionVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
261 }
262 else if (type == "dirichlet")
263 {
264 var2sim = std::make_unique<DirichletVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
265 }
266 else if (type == "dirichlet-nodes")
267 {
268 var2sim = std::make_unique<DirichletNodesVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
269 }
270 else if (type == "pressure")
271 {
272 var2sim = std::make_unique<PressureVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
273 }
274 else if (type == "periodic-shape")
275 {
276 var2sim = std::make_unique<PeriodicShapeVariableToSimulation>(std::move(rel_states), std::move(rel_diff_caches), std::move(compo));
277 }
278 else
279 {
280 log_and_throw_adjoint_error("Invalid type of VariableToSimulation!");
281 }
282
283 var2sim->set_output_indexing(args);
284
285 return var2sim;
286 }
287
289 const json &args,
290 const std::vector<std::shared_ptr<State>> &states,
291 const std::vector<std::shared_ptr<DiffCache>> &diff_caches,
292 const std::vector<int> &variable_sizes)
293 {
295 for (const auto &arg : args)
296 {
297 v2s_group.data.push_back(
298 build_variable_to_simulation(arg, states, diff_caches, variable_sizes));
299 }
300 return v2s_group;
301 }
302
303 std::shared_ptr<solver::AdjointForm> build_form(
304 const json &args,
306 const std::vector<std::shared_ptr<State>> &states,
307 const std::vector<std::shared_ptr<DiffCache>> &diff_caches)
308 {
309 using namespace polyfem::solver;
310
311 std::shared_ptr<AdjointForm> obj;
312 if (args.is_array())
313 {
314 std::vector<std::shared_ptr<AdjointForm>> forms;
315 for (const auto &arg : args)
316 {
317 forms.push_back(build_form(arg, var2sim, states, diff_caches));
318 }
319
320 obj = std::make_shared<SumCompositeForm>(var2sim, forms);
321 }
322 else
323 {
324 const std::string type = args["type"];
325 if (type == "transient_integral")
326 {
327 std::shared_ptr<StaticForm> static_obj =
328 std::dynamic_pointer_cast<StaticForm>(build_form(args["static_objective"], var2sim, states, diff_caches));
329 if (!static_obj)
330 {
331 log_and_throw_adjoint_error("Transient integral objective must have a static objective!");
332 }
333 const auto &state = states[args["state"]];
334 obj = std::make_shared<TransientForm>(
335 var2sim, state->args["time"]["time_steps"], state->args["time"]["dt"],
336 args["integral_type"], args["steps"].get<std::vector<int>>(),
337 static_obj);
338 }
339 else if (type == "proxy_transient_integral")
340 {
341 std::shared_ptr<StaticForm> static_obj =
342 std::dynamic_pointer_cast<StaticForm>(build_form(args["static_objective"], var2sim, states, diff_caches));
343 if (!static_obj)
344 {
345 log_and_throw_adjoint_error("Transient integral objective must have a static objective!");
346 }
347 if (args["steps"].size() == 0)
348 {
349 log_and_throw_adjoint_error("ProxyTransientForm requires non-empty \"steps\"!");
350 }
351 const auto &state = states[args["state"]];
352 obj = std::make_shared<ProxyTransientForm>(
353 var2sim, state->args["time"]["time_steps"], state->args["time"]["dt"],
354 args["integral_type"], args["steps"].get<std::vector<int>>(),
355 static_obj);
356 }
357 else if (type == "power")
358 {
359 std::shared_ptr<AdjointForm> obj_aux =
360 build_form(args["objective"], var2sim, states, diff_caches);
361 obj = std::make_shared<PowerForm>(obj_aux, args["power"]);
362 }
363 else if (type == "divide")
364 {
365 std::shared_ptr<AdjointForm> obj1 =
366 build_form(args["objective"][0], var2sim, states, diff_caches);
367 std::shared_ptr<AdjointForm> obj2 =
368 build_form(args["objective"][1], var2sim, states, diff_caches);
369 std::vector<std::shared_ptr<AdjointForm>> objs({obj1, obj2});
370 obj = std::make_shared<DivideForm>(objs);
371 }
372 else if (type == "plus-const")
373 {
374 obj = std::make_shared<PlusConstCompositeForm>(
375 build_form(args["objective"], var2sim, states, diff_caches), args["value"]);
376 }
377 else if (type == "log")
378 {
379 obj = std::make_shared<LogCompositeForm>(
380 build_form(args["objective"], var2sim, states, diff_caches));
381 }
382 else if (type == "compliance")
383 {
384 obj = std::make_shared<ComplianceForm>(var2sim, states[args["state"]], diff_caches[args["state"]],
385 args);
386 }
387 else if (type == "acceleration")
388 {
389 obj = std::make_shared<AccelerationForm>(var2sim,
390 states[args["state"]], diff_caches[args["state"]], args);
391 }
392 else if (type == "kinetic")
393 {
394 obj = std::make_shared<AccelerationForm>(var2sim,
395 states[args["state"]], diff_caches[args["state"]], args);
396 }
397 else if (type == "target")
398 {
399 std::shared_ptr<TargetForm> tmp =
400 std::make_shared<TargetForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
401 auto reference_cached =
402 args["reference_cached_body_ids"].get<std::vector<int>>();
403 tmp->set_reference(
404 states[args["target_state"]],
405 diff_caches[args["target_state"]],
406 std::set(reference_cached.begin(), reference_cached.end()));
407 obj = tmp;
408 }
409 else if (type == "displacement-target")
410 {
411 std::shared_ptr<TargetForm> tmp =
412 std::make_shared<TargetForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
413
414 Eigen::VectorXd target_displacement;
415 target_displacement.setZero(states[args["state"]]->mesh->dimension());
416 if (target_displacement.size() != args["target_displacement"].size())
417 {
418 log_and_throw_error("Target displacement shape must match the dimension of the simulation");
419 }
420 for (int i = 0; i < target_displacement.size(); ++i)
421 {
422 target_displacement(i) = args["target_displacement"][i].get<double>();
423 }
424 if (args["active_dimension"].size() > 0)
425 {
426 if (target_displacement.size() != args["active_dimension"].size())
427 {
428 log_and_throw_error("Active dimension shape must match the dimension of the simulation");
429 }
430 std::vector<bool> active_dimension_mask(args["active_dimension"].size());
431 for (int i = 0; i < args["active_dimension"].size(); ++i)
432 {
433 active_dimension_mask[i] = args["active_dimension"][i].get<bool>();
434 }
435 tmp->set_active_dimension(active_dimension_mask);
436 }
437 tmp->set_reference(target_displacement);
438 obj = tmp;
439 }
440 else if (type == "center-target")
441 {
442 obj = std::make_shared<BarycenterTargetForm>(
443 var2sim, args, states[args["state"]], diff_caches[args["state"]], states[args["target_state"]], diff_caches[args["target_state"]]);
444 }
445 else if (type == "sdf-target")
446 {
447 std::shared_ptr<SDFTargetForm> tmp = std::make_shared<SDFTargetForm>(
448 var2sim, states[args["state"]], diff_caches[args["state"]], args);
449 double delta = args["delta"].get<double>();
450 if (!states[args["state"]]->mesh->is_volume())
451 {
452 int dim = 2;
453 Eigen::MatrixXd control_points(args["control_points"].size(), dim);
454 for (int i = 0; i < control_points.rows(); ++i)
455 {
456 for (int j = 0; j < control_points.cols(); ++j)
457 {
458 control_points(i, j) = args["control_points"][i][j].get<double>();
459 }
460 }
461 Eigen::VectorXd knots(args["knots"].size());
462 for (int i = 0; i < knots.size(); ++i)
463 {
464 knots(i) = args["knots"][i].get<double>();
465 }
466 tmp->set_bspline_target(control_points, knots, delta);
467 }
468 else
469 {
470 int dim = 3;
471 Eigen::MatrixXd control_points_grid(args["control_points_grid"].size(), dim);
472 for (int i = 0; i < control_points_grid.rows(); ++i)
473 {
474 for (int j = 0; j < control_points_grid.cols(); ++j)
475 {
476 control_points_grid(i, j) = args["control_points_grid"][i][j].get<double>();
477 }
478 }
479 Eigen::VectorXd knots_u(args["knots_u"].size());
480 for (int i = 0; i < knots_u.size(); ++i)
481 {
482 knots_u(i) = args["knots_u"][i].get<double>();
483 }
484 Eigen::VectorXd knots_v(args["knots_v"].size());
485 for (int i = 0; i < knots_v.size(); ++i)
486 {
487 knots_v(i) = args["knots_v"][i].get<double>();
488 }
489 tmp->set_bspline_target(control_points_grid, knots_u, knots_v, delta);
490 }
491
492 obj = tmp;
493 }
494 else if (type == "mesh-target")
495 {
496 std::shared_ptr<MeshTargetForm> tmp = std::make_shared<MeshTargetForm>(
497 var2sim, states[args["state"]], diff_caches[args["state"]], args);
498 double delta = args["delta"].get<double>();
499
500 std::string mesh_path =
501 states[args["state"]]->resolve_input_path(args["mesh_path"].get<std::string>());
502 Eigen::MatrixXd V;
503 Eigen::MatrixXi E, F;
504 bool read = polyfem::io::OBJReader::read(mesh_path, V, E, F);
505 if (!read)
506 {
507 log_and_throw_error(fmt::format("Could not read mesh! {}", mesh_path));
508 }
509 tmp->set_surface_mesh_target(V, F, delta);
510 obj = tmp;
511 }
512 else if (type == "function-target")
513 {
514 std::shared_ptr<TargetForm> tmp =
515 std::make_shared<TargetForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
516 tmp->set_reference(args["target_function"], args["target_function_gradient"]);
517 obj = tmp;
518 }
519 else if (type == "node-target")
520 {
521 obj = std::make_shared<NodeTargetForm>(states[args["state"]], diff_caches[args["state"]], var2sim, args);
522 }
523 else if (type == "min-dist-target")
524 {
525 obj = std::make_shared<MinTargetDistForm>(
526 var2sim, args["steps"], args["target"], args, states[args["state"]], diff_caches[args["state"]]);
527 }
528 else if (type == "position")
529 {
530 obj = std::make_shared<PositionForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
531 }
532 else if (type == "stress")
533 {
534 obj = std::make_shared<StressForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
535 }
536 else if (type == "stress_norm")
537 {
538 obj = std::make_shared<StressNormForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
539 }
540 else if (type == "dirichlet_energy")
541 {
542 obj = std::make_shared<DirichletEnergyForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
543 }
544 else if (type == "elastic_energy")
545 {
546 obj = std::make_shared<ElasticEnergyForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
547 }
548 else if (type == "quadratic_contact_force_norm")
549 {
550 obj = std::make_shared<ProxyContactForceForm>(
551 var2sim, states[args["state"]], diff_caches[args["state"]], args["dhat"], true, args);
552 }
553 else if (type == "log_contact_force_norm")
554 {
555 obj = std::make_shared<ProxyContactForceForm>(
556 var2sim, states[args["state"]], diff_caches[args["state"]], args["dhat"], false, args);
557 }
558 else if (type == "max_stress")
559 {
560 obj = std::make_shared<MaxStressForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
561 }
562 else if (type == "smooth_contact_force_norm")
563 {
564 // assert(states[args["state"]]->args["contact"]["use_gcp_formulation"]);
565 obj = std::make_shared<SmoothContactForceForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
566 }
567 else if (type == "volume")
568 {
569 obj = std::make_shared<VolumeForm>(var2sim, states[args["state"]], diff_caches[args["state"]], args);
570 }
571 else if (type == "soft_constraint")
572 {
573 std::vector<std::shared_ptr<AdjointForm>> forms({build_form(args["objective"], var2sim, states, diff_caches)});
574 Eigen::VectorXd bounds = args["soft_bound"];
575 obj = std::make_shared<InequalityConstraintForm>(forms, bounds, args["power"]);
576 }
577 else if (type == "min_jacobian")
578 {
579 obj = std::make_shared<MinJacobianForm>(var2sim, states[args["state"]]);
580 }
581 else if (type == "AMIPS")
582 {
583 obj = std::make_shared<AMIPSForm>(var2sim, states[args["state"]]);
584 }
585 else if (type == "boundary_smoothing")
586 {
587 if (args["surface_selection"].is_array())
588 {
589 obj = std::make_shared<BoundarySmoothingForm>(
590 var2sim, states[args["state"]], args["scale_invariant"],
591 args["power"], args["surface_selection"].get<std::vector<int>>(),
592 args["dimensions"].get<std::vector<int>>());
593 }
594 else
595 {
596 obj = std::make_shared<BoundarySmoothingForm>(
597 var2sim, states[args["state"]], args["scale_invariant"],
598 args["power"],
599 std::vector<int>{args["surface_selection"].get<int>()},
600 args["dimensions"].get<std::vector<int>>());
601 }
602 }
603 else if (type == "collision_barrier")
604 {
605 obj = std::make_shared<CollisionBarrierForm>(
606 var2sim, states[args["state"]], args["dhat"]);
607 }
608 else if (type == "layer_thickness")
609 {
610 obj = std::make_shared<LayerThicknessForm>(
611 var2sim, states[args["state"]],
612 args["boundary_ids"].get<std::vector<int>>(), args["dhat"]);
613 }
614 else if (type == "layer_thickness_log")
615 {
616 obj = std::make_shared<LayerThicknessForm>(
617 var2sim, states[args["state"]],
618 args["boundary_ids"].get<std::vector<int>>(), args["dhat"], true,
619 args["dmin"]);
620 }
621 else if (type == "deformed_collision_barrier")
622 {
623 obj = std::make_shared<DeformedCollisionBarrierForm>(
624 var2sim, states[args["state"]], diff_caches[args["state"]], args["dhat"]);
625 }
626 else if (type == "parametrized_product")
627 {
628 std::vector<std::shared_ptr<Parametrization>> map_list;
629 for (const auto &arg : args["parametrization"])
630 {
631 map_list.push_back(build_parametrization(arg, states, {}));
632 }
633 obj = std::make_shared<ParametrizedProductForm>(
634 CompositeParametrization(std::move(map_list)));
635 }
636 else
637 {
638 log_and_throw_adjoint_error("Objective not implemented!");
639 }
640
641 obj->set_weight(args["weight"]);
642 if (args["print_energy"].get<std::string>() != "")
643 {
644 obj->enable_energy_print(args["print_energy"]);
645 }
646 }
647
648 return obj;
649 }
650
651} // namespace polyfem::from_json
int V
ElementAssemblyValues vals
Definition Assembler.cpp:22
static bool read(const std::string obj_file_name, std::vector< std::vector< double > > &V, std::vector< std::vector< double > > &TC, std::vector< std::vector< double > > &N, std::vector< std::vector< int > > &F, std::vector< std::vector< int > > &FTC, std::vector< std::vector< int > > &FN, std::vector< std::vector< int > > &L)
Read a mesh from an ascii obj file.
Definition OBJReader.cpp:32
A collection of VariableToSimulation.
std::vector< std::shared_ptr< VariableToSimulation > > data
bool load_json(const std::string &json_file, json &out)
Definition main.cpp:28
std::vector< std::shared_ptr< State > > build_states(const std::string &root_path, const json &args, const size_t max_threads)
solver::VariableToSimulationGroup build_variable_to_simulation_group(const json &args, const std::vector< std::shared_ptr< State > > &states, const std::vector< std::shared_ptr< DiffCache > > &diff_caches, const std::vector< int > &variable_sizes)
std::shared_ptr< State > build_state(const json &args, const size_t max_threads)
std::unique_ptr< solver::VariableToSimulation > build_variable_to_simulation(const json &args, const std::vector< std::shared_ptr< State > > &states, const std::vector< std::shared_ptr< DiffCache > > &diff_caches, const std::vector< int > &variable_sizes)
std::shared_ptr< solver::Parametrization > build_parametrization(const json &args, const std::vector< std::shared_ptr< State > > &states, const std::vector< int > &variable_sizes)
std::shared_ptr< solver::AdjointForm > build_form(const json &args, const solver::VariableToSimulationGroup &var2sim, const std::vector< std::shared_ptr< State > > &states, const std::vector< std::shared_ptr< DiffCache > > &diff_caches)
std::string resolve_path(const std::string &path, const std::string &input_file_path, const bool only_if_exists=false)
nlohmann::json json
Definition Common.hpp:9
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