PolyFEM
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1#include <filesystem>
2
3#include <CLI/CLI.hpp>
4
5#include <polyfem/State.hpp>
7#ifdef POLYFEM_WITH_OPTIMIZATION
9#endif
10
18
19using namespace polyfem;
20using namespace solver;
21
22bool has_arg(const CLI::App &command_line, const std::string &value)
23{
24 const auto *opt = command_line.get_option_no_throw(value.size() == 1 ? ("-" + value) : ("--" + value));
25 if (!opt)
26 return false;
27
28 return opt->count() > 0;
29}
30
31int forward_simulation(const CLI::App &command_line,
32 const io::ResourceIO &resources,
33 const std::string &output_dir,
34 const unsigned max_threads,
35 const bool is_strict,
36 const bool fallback_solver,
37 const spdlog::level::level_enum &log_level,
38 json &in_args);
39
40int forward_simulation(const CLI::App &command_line,
41 const io::CheckpointReader &checkpoint,
42 const std::string &output_dir,
43 unsigned max_threads,
44 bool is_strict,
45 bool fallback_solver,
46 const spdlog::level::level_enum &log_level,
47 json &in_args);
48
49#ifdef POLYFEM_WITH_OPTIMIZATION
50int optimization_simulation(const CLI::App &command_line,
51 const io::ResourceIO &resources,
52 const unsigned max_threads,
53 const bool is_strict,
54 const spdlog::level::level_enum &log_level,
55 json &opt_args);
56#endif
57
59
61 json &in_args,
62 const bool is_strict)
63{
64 State state;
65 state.init(in_args, resources, is_strict);
66 return run_forward_simulation(state);
67}
68
70 const json &in_args,
71 const bool is_strict)
72{
73 State state;
74 state.init(in_args, checkpoint, is_strict);
75 return run_forward_simulation(state);
76}
77
79{
80 state.load_mesh();
81
82 Eigen::MatrixXd sol;
83 state.solve(sol);
84
85 state.variational_formulation->compute_errors(sol);
86
87 io::OutRuntimeData timings = state.variational_formulation->output_timings();
88 logger().info("total time: {}s", timings.total_time());
89
90 state.variational_formulation->save_json(sol);
91 state.variational_formulation->export_data(sol);
92
93 return EXIT_SUCCESS;
94}
95
96int forward_simulation_with_legacy_state(const std::vector<std::string> &names,
97 const std::vector<Eigen::MatrixXi> &cells,
98 const std::vector<Eigen::MatrixXd> &vertices,
99 json &in_args,
100 const bool is_strict)
101{
102 legacy::State state;
103 state.init(in_args, is_strict);
104
105 logger().warn("Running forward simulation with legacy state.");
106 state.load_mesh(/*non_conforming=*/false, names, cells, vertices);
107
108 // Mesh was not loaded successfully; load_mesh() logged the error.
109 if (state.mesh == nullptr)
110 {
111 // Cannot proceed without a mesh.
112 return EXIT_FAILURE;
113 }
114
115 state.stats.compute_mesh_stats(*state.mesh);
116
117 state.build_basis();
118
119 state.assemble_rhs();
120 state.assemble_mass_mat();
121
122 Eigen::MatrixXd sol;
123 Eigen::MatrixXd pressure;
124
125 state.solve_problem(sol, pressure);
126
127 state.compute_errors(sol);
128
129 logger().info("total time: {}s", state.timings.total_time());
130
131 state.save_json(sol);
132 state.export_data(sol, pressure);
133
134 return EXIT_SUCCESS;
135}
136
137int main(int argc, char **argv)
138{
139 using namespace polyfem;
140
141 CLI::App command_line{"polyfem"};
142
143 command_line.ignore_case();
144 command_line.ignore_underscore();
145
146 // Eigen::setNbThreads(1);
147 unsigned max_threads = std::numeric_limits<unsigned>::max();
148 command_line.add_option("--max_threads", max_threads, "Maximum number of threads");
149
150 auto input = command_line.add_option_group("input");
151
152 std::string json_file = "";
153 input->add_option("-j,--json", json_file, "Simulation JSON file")->check(CLI::ExistingFile);
154
155 std::string yaml_file = "";
156 input->add_option("-y,--yaml", yaml_file, "Simulation YAML file")->check(CLI::ExistingFile);
157
158 std::string hdf5_file = "";
159 input->add_option("--hdf5", hdf5_file, "Simulation HDF5 file")->check(CLI::ExistingFile);
160
161 std::string checkpoint_file = "";
162 auto *checkpoint_option = input->add_option("--checkpoint", checkpoint_file, "Versioned PolyFEM checkpoint")->check(CLI::ExistingFile);
163 bool checkpoint_reorder = false;
164 command_line.add_flag(
165 "--checkpoint-reorder", checkpoint_reorder,
166 "Reorder checkpoint state from its saved input-node ordering")
167 ->needs(checkpoint_option);
168
169 input->require_option(1);
170
171 std::string output_dir = "";
172 command_line.add_option("-o,--output_dir", output_dir, "Directory for output files")->check(CLI::ExistingDirectory | CLI::NonexistentPath);
173
174 bool is_strict = true;
175 command_line.add_flag("-s,--strict_validation,!--ns,!--no_strict_validation", is_strict, "Disables strict validation of input JSON");
176
177 bool fallback_solver = false;
178 command_line.add_flag("--enable_overwrite_solver", fallback_solver, "If solver in input is not present, falls back to default.");
179
180 const std::vector<std::pair<std::string, spdlog::level::level_enum>>
181 SPDLOG_LEVEL_NAMES_TO_LEVELS = {
182 {"trace", spdlog::level::trace},
183 {"debug", spdlog::level::debug},
184 {"info", spdlog::level::info},
185 {"warning", spdlog::level::warn},
186 {"error", spdlog::level::err},
187 {"critical", spdlog::level::critical},
188 {"off", spdlog::level::off}};
189 spdlog::level::level_enum log_level = spdlog::level::debug;
190 command_line.add_option("--log_level", log_level, "Log level")
191 ->transform(CLI::CheckedTransformer(SPDLOG_LEVEL_NAMES_TO_LEVELS, CLI::ignore_case));
192
193 CLI11_PARSE(command_line, argc, argv);
194
195 json in_args = json({});
196 if (!json_file.empty() || !yaml_file.empty())
197 {
198 const io::LoadedInput loaded = !json_file.empty()
199 ? io::load_json_input(json_file)
200 : io::load_yaml_input(yaml_file);
201 in_args = loaded.config;
202
203 if (in_args.contains("states"))
204 {
205#ifndef POLYFEM_WITH_OPTIMIZATION
206 log_and_throw_error("PolyFEM was built without optimization support.");
207#else
208 return optimization_simulation(command_line, *loaded.resources, max_threads, is_strict, log_level, in_args);
209#endif
210 }
211 else
212 return forward_simulation(command_line, *loaded.resources, output_dir, max_threads,
213 is_strict, fallback_solver, log_level, in_args);
214 }
215 else if (!hdf5_file.empty())
216 {
217 const io::LoadedInput loaded = io::load_hdf5_input(hdf5_file);
218 in_args = loaded.config;
219 return forward_simulation(command_line, *loaded.resources, output_dir, max_threads,
220 is_strict, fallback_solver, log_level, in_args);
221 }
222 else
223 {
224 io::CheckpointReader checkpoint(checkpoint_file);
225 in_args = checkpoint.config();
226 return forward_simulation(command_line, checkpoint, output_dir, max_threads,
227 is_strict, fallback_solver, log_level, in_args);
228 }
229}
230
231int forward_simulation(const CLI::App &command_line,
232 const io::ResourceIO &resources,
233 const std::string &output_dir,
234 const unsigned max_threads,
235 const bool is_strict,
236 const bool fallback_solver,
237 const spdlog::level::level_enum &log_level,
238 json &in_args)
239{
240 if (in_args.empty())
241 {
242 logger().error("No input file specified!");
243 return command_line.exit(CLI::RequiredError("--json or --hdf5"));
244 }
245
246 json tmp = json::object();
247 if (has_arg(command_line, "log_level"))
248 tmp["/output/log/level"_json_pointer] = int(log_level);
249 if (has_arg(command_line, "max_threads"))
250 tmp["/solver/max_threads"_json_pointer] = max_threads;
251 if (has_arg(command_line, "output_dir"))
252 tmp["/output/directory"_json_pointer] = std::filesystem::absolute(output_dir);
253 if (has_arg(command_line, "enable_overwrite_solver"))
254 tmp["/solver/linear/enable_overwrite_solver"_json_pointer] = fallback_solver;
255 assert(tmp.is_object());
256 in_args.merge_patch(tmp);
257
258 const std::string checkpoint_path =
259 in_args.contains("/input/checkpoint/path"_json_pointer)
260 ? in_args["/input/checkpoint/path"_json_pointer].get<std::string>()
261 : std::string();
262 if (!checkpoint_path.empty())
263 {
264 const std::filesystem::path path = std::filesystem::path(checkpoint_path).is_absolute()
265 ? std::filesystem::path(checkpoint_path)
266 : resources.host_directory() / checkpoint_path;
267 io::CheckpointReader checkpoint(path.lexically_normal());
268 json continuation = checkpoint.config();
269 continuation["input"]["checkpoint"]["path"] = "";
270 continuation["input"]["checkpoint"]["reorder"] =
271 in_args.contains("/input/checkpoint/reorder"_json_pointer)
272 ? in_args["/input/checkpoint/reorder"_json_pointer].get<bool>()
273 : false;
274 return forward_simulation(
275 command_line, checkpoint, output_dir, max_threads,
276 is_strict, fallback_solver, log_level, continuation);
277 }
278
279 if (varform::uses_varform_state(in_args, resources))
280 return forward_simulation_with_varform_state(resources, in_args, is_strict);
281
282 if (dynamic_cast<const io::HDF5IO *>(&resources) != nullptr)
283 log_and_throw_error("HDF5 resources are supported only by the non-legacy State.");
284 in_args["root_path"] = resources.describe("");
285
286 return forward_simulation_with_legacy_state({}, {}, {}, in_args, is_strict);
287}
288
289int forward_simulation(const CLI::App &command_line,
290 const io::CheckpointReader &checkpoint,
291 const std::string &output_dir,
292 const unsigned max_threads,
293 const bool is_strict,
294 const bool fallback_solver,
295 const spdlog::level::level_enum &log_level,
296 json &in_args)
297{
298 if (!varform::uses_varform_state(in_args, checkpoint.resources()))
299 log_and_throw_error("Checkpoints are supported only by the non-legacy State.");
300
301 json tmp = json::object();
302 if (has_arg(command_line, "log_level"))
303 tmp["/output/log/level"_json_pointer] = int(log_level);
304 if (has_arg(command_line, "max_threads"))
305 tmp["/solver/max_threads"_json_pointer] = max_threads;
306 if (has_arg(command_line, "output_dir"))
307 tmp["/output/directory"_json_pointer] = std::filesystem::absolute(output_dir);
308 if (has_arg(command_line, "enable_overwrite_solver"))
309 tmp["/solver/linear/enable_overwrite_solver"_json_pointer] = fallback_solver;
310 if (has_arg(command_line, "checkpoint-reorder"))
311 tmp["/input/checkpoint/reorder"_json_pointer] = true;
312 in_args.merge_patch(tmp);
313
314 return forward_simulation_with_varform_state(checkpoint, in_args, is_strict);
315}
316
317#ifdef POLYFEM_WITH_OPTIMIZATION
318int optimization_simulation(const CLI::App &command_line,
319 const io::ResourceIO &resources,
320 const unsigned max_threads,
321 const bool is_strict,
322 const spdlog::level::level_enum &log_level,
323 json &opt_args)
324{
325 json tmp = json::object();
326 if (has_arg(command_line, "log_level"))
327 tmp["/output/log/level"_json_pointer] = int(log_level);
328 if (has_arg(command_line, "max_threads"))
329 tmp["/solver/max_threads"_json_pointer] = max_threads;
330 opt_args.merge_patch(tmp);
331
332 OptState opt_state;
333 return opt_state.run(opt_args, resources, is_strict);
334}
335#endif
Main class containing the PolyFEM adjoint solver and its optimization data.
Definition OptState.hpp:30
int run(json args, const bool strict_validation)
Run optimization, including remeshing restarts when the selected trigger requests them.
Definition OptState.cpp:277
VarForm-only simulation state.
Definition State.hpp:37
std::shared_ptr< varform::VarForm > variational_formulation
active variational formulation
Definition State.hpp:71
void init(const json &args, bool strict_validation, bool is_adjoint_optimization=false)
initialize the polyfem solver with a json settings
Definition State.cpp:239
void solve(Eigen::MatrixXd &sol)
solves the problem, call other methods
Definition State.cpp:502
void load_mesh(bool non_conforming=false)
loads the mesh from the json arguments
Definition State.cpp:446
const ResourceIO & resources() const
const json & config() const
HDF5 logical paths always use POSIX separators and roots.
std::unique_ptr< const ResourceIO > resources
timers from polyfem.
double total_time() const
computes total time
void compute_mesh_stats(const polyfem::mesh::Mesh &mesh)
compute stats (counts els type, mesh lenght, etc), step 1 of solve
Definition OutData.cpp:3021
Read-only interface to a hierarchical collection of simulation resources.
virtual const std::filesystem::path & host_directory() const =0
Host directory used only as the base for ordinary filesystem outputs.
virtual std::string describe(const std::string &path) const =0
main class that contains the polyfem solver and all its state
Definition State.hpp:114
void init(const json &args, const bool strict_validation)
initialize the polyfem solver with a json settings
void compute_errors(const Eigen::MatrixXd &sol)
computes all errors
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:578
io::OutStatsData stats
Other statistics.
Definition State.hpp:728
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
Definition StateLoad.cpp:94
void assemble_mass_mat()
assemble mass, step 4 of solve build mass matrix based on defined basis modifies mass (and maybe more...
Definition State.cpp:1462
io::OutRuntimeData timings
runtime statistics
Definition State.hpp:726
void export_data(const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure)
saves all data on the disk according to the input params
void build_basis()
builds the bases step 2 of solve modifies bases, pressure_bases, geom_bases_, boundary_nodes,...
Definition State.cpp:509
void save_json(const Eigen::MatrixXd &sol, std::ostream &out)
saves the output statistic to a stream
void assemble_rhs()
compute rhs, step 3 of solve build rhs vector based on defined basis and given rhs of the problem mod...
Definition State.cpp:1593
void solve_problem(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves the problems
Definition State.cpp:1666
int forward_simulation_with_legacy_state(const std::vector< std::string > &names, const std::vector< Eigen::MatrixXi > &cells, const std::vector< Eigen::MatrixXd > &vertices, json &in_args, const bool is_strict)
Definition main.cpp:96
int run_forward_simulation(State &state)
Definition main.cpp:78
int main(int argc, char **argv)
Definition main.cpp:137
int forward_simulation_with_varform_state(const io::ResourceIO &resources, json &in_args, const bool is_strict)
Definition main.cpp:60
int forward_simulation(const CLI::App &command_line, const io::ResourceIO &resources, const std::string &output_dir, const unsigned max_threads, const bool is_strict, const bool fallback_solver, const spdlog::level::level_enum &log_level, json &in_args)
Definition main.cpp:231
bool has_arg(const CLI::App &command_line, const std::string &value)
Definition main.cpp:22
list tmp
Definition p_bases.py:366
LoadedInput load_json_input(const std::filesystem::path &path)
LoadedInput load_hdf5_input(const std::filesystem::path &path)
LoadedInput load_yaml_input(const std::filesystem::path &path)
bool uses_varform_state(json args, const io::ResourceIO &resources)
Checks if the given JSON arguments use a VarForm state.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73