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 <h5pp/h5pp.h>
6
7#include <polyfem/State.hpp>
9#ifdef POLYFEM_WITH_OPTIMIZATION
11#endif
12
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
31bool load_json(const std::string &json_file, json &out)
32{
33 std::ifstream file(json_file);
34
35 if (!file.is_open())
36 return false;
37
38 file >> out;
39
40 if (!out.contains("root_path"))
41 out["root_path"] = json_file;
42
43 return true;
44}
45
46bool load_yaml(const std::string &yaml_file, json &out)
47{
48 try
49 {
50 out = io::yaml_file_to_json(yaml_file);
51 if (!out.contains("root_path"))
52 out["root_path"] = yaml_file;
53 }
54 catch (...)
55 {
56 return false;
57 }
58 return true;
59}
60
61int forward_simulation(const CLI::App &command_line,
62 const std::string &hdf5_file,
63 const std::string output_dir,
64 const unsigned max_threads,
65 const bool is_strict,
66 const bool fallback_solver,
67 const spdlog::level::level_enum &log_level,
68 json &in_args);
69
70#ifdef POLYFEM_WITH_OPTIMIZATION
71int optimization_simulation(const CLI::App &command_line,
72 const unsigned max_threads,
73 const bool is_strict,
74 const spdlog::level::level_enum &log_level,
75 json &opt_args);
76#endif
77
78int forward_simulation_with_varform_state(const std::vector<std::string> &names,
79 const std::vector<Eigen::MatrixXi> &cells,
80 const std::vector<Eigen::MatrixXd> &vertices,
81 json &in_args,
82 const bool is_strict)
83{
84 State state;
85 state.init(in_args, is_strict);
86 state.load_mesh(/*non_conforming=*/false, names, cells, vertices);
87
88 Eigen::MatrixXd sol;
89 state.solve(sol);
90
91 state.variational_formulation->compute_errors(sol);
92
93 io::OutRuntimeData timings = state.variational_formulation->output_timings();
94 logger().info("total time: {}s", timings.total_time());
95
96 state.variational_formulation->save_json(sol);
97 state.variational_formulation->export_data(sol);
98
99 return EXIT_SUCCESS;
100}
101
102int forward_simulation_with_legacy_state(const std::vector<std::string> &names,
103 const std::vector<Eigen::MatrixXi> &cells,
104 const std::vector<Eigen::MatrixXd> &vertices,
105 json &in_args,
106 const bool is_strict)
107{
108 legacy::State state;
109 state.init(in_args, is_strict);
110
111 logger().warn("Running forward simulation with legacy state.");
112 state.load_mesh(/*non_conforming=*/false, names, cells, vertices);
113
114 // Mesh was not loaded successfully; load_mesh() logged the error.
115 if (state.mesh == nullptr)
116 {
117 // Cannot proceed without a mesh.
118 return EXIT_FAILURE;
119 }
120
121 state.stats.compute_mesh_stats(*state.mesh);
122
123 state.build_basis();
124
125 state.assemble_rhs();
126 state.assemble_mass_mat();
127
128 Eigen::MatrixXd sol;
129 Eigen::MatrixXd pressure;
130
131 state.solve_problem(sol, pressure);
132
133 state.compute_errors(sol);
134
135 logger().info("total time: {}s", state.timings.total_time());
136
137 state.save_json(sol);
138 state.export_data(sol, pressure);
139
140 return EXIT_SUCCESS;
141}
142
143int main(int argc, char **argv)
144{
145 using namespace polyfem;
146
147 CLI::App command_line{"polyfem"};
148
149 command_line.ignore_case();
150 command_line.ignore_underscore();
151
152 // Eigen::setNbThreads(1);
153 unsigned max_threads = std::numeric_limits<unsigned>::max();
154 command_line.add_option("--max_threads", max_threads, "Maximum number of threads");
155
156 auto input = command_line.add_option_group("input");
157
158 std::string json_file = "";
159 input->add_option("-j,--json", json_file, "Simulation JSON file")->check(CLI::ExistingFile);
160
161 std::string yaml_file = "";
162 input->add_option("-y,--yaml", yaml_file, "Simulation YAML file")->check(CLI::ExistingFile);
163
164 std::string hdf5_file = "";
165 input->add_option("--hdf5", hdf5_file, "Simulation HDF5 file")->check(CLI::ExistingFile);
166
167 input->require_option(1);
168
169 std::string output_dir = "";
170 command_line.add_option("-o,--output_dir", output_dir, "Directory for output files")->check(CLI::ExistingDirectory | CLI::NonexistentPath);
171
172 bool is_strict = true;
173 command_line.add_flag("-s,--strict_validation,!--ns,!--no_strict_validation", is_strict, "Disables strict validation of input JSON");
174
175 bool fallback_solver = false;
176 command_line.add_flag("--enable_overwrite_solver", fallback_solver, "If solver in input is not present, falls back to default.");
177
178 const std::vector<std::pair<std::string, spdlog::level::level_enum>>
179 SPDLOG_LEVEL_NAMES_TO_LEVELS = {
180 {"trace", spdlog::level::trace},
181 {"debug", spdlog::level::debug},
182 {"info", spdlog::level::info},
183 {"warning", spdlog::level::warn},
184 {"error", spdlog::level::err},
185 {"critical", spdlog::level::critical},
186 {"off", spdlog::level::off}};
187 spdlog::level::level_enum log_level = spdlog::level::debug;
188 command_line.add_option("--log_level", log_level, "Log level")
189 ->transform(CLI::CheckedTransformer(SPDLOG_LEVEL_NAMES_TO_LEVELS, CLI::ignore_case));
190
191 CLI11_PARSE(command_line, argc, argv);
192
193 json in_args = json({});
194
195 if (!json_file.empty() || !yaml_file.empty())
196 {
197 const bool ok = !json_file.empty() ? load_json(json_file, in_args) : load_yaml(yaml_file, in_args);
198
199 if (!ok)
200 log_and_throw_error(fmt::format("unable to open {} file", json_file));
201
202 if (in_args.contains("states"))
203 {
204#ifndef POLYFEM_WITH_OPTIMIZATION
205 log_and_throw_error("PolyFEM was built without optimization support.");
206#else
207 return optimization_simulation(command_line, max_threads, is_strict, log_level, in_args);
208#endif
209 }
210 else
211 return forward_simulation(command_line, "", output_dir, max_threads,
212 is_strict, fallback_solver, log_level, in_args);
213 }
214 else
215 return forward_simulation(command_line, hdf5_file, output_dir, max_threads,
216 is_strict, fallback_solver, log_level, in_args);
217}
218
219int forward_simulation(const CLI::App &command_line,
220 const std::string &hdf5_file,
221 const std::string output_dir,
222 const unsigned max_threads,
223 const bool is_strict,
224 const bool fallback_solver,
225 const spdlog::level::level_enum &log_level,
226 json &in_args)
227{
228 std::vector<std::string> names;
229 std::vector<Eigen::MatrixXi> cells;
230 std::vector<Eigen::MatrixXd> vertices;
231
232 if (in_args.empty() && hdf5_file.empty())
233 {
234 logger().error("No input file specified!");
235 return command_line.exit(CLI::RequiredError("--json or --hdf5"));
236 }
237
238 if (in_args.empty() && !hdf5_file.empty())
239 {
240 using MatrixXl = Eigen::Matrix<int64_t, Eigen::Dynamic, Eigen::Dynamic>;
241
242 h5pp::File file(hdf5_file, h5pp::FileAccess::READONLY);
243 std::string json_string = file.readDataset<std::string>("json");
244
245 in_args = json::parse(json_string);
246 in_args["root_path"] = hdf5_file;
247
248 names = file.findGroups("", "/meshes");
249 cells.resize(names.size());
250 vertices.resize(names.size());
251
252 for (int i = 0; i < names.size(); ++i)
253 {
254 const std::string &name = names[i];
255 cells[i] = file.readDataset<MatrixXl>("/meshes/" + name + "/c").cast<int>();
256 vertices[i] = file.readDataset<Eigen::MatrixXd>("/meshes/" + name + "/v");
257 }
258 }
259
260 json tmp = json::object();
261 if (has_arg(command_line, "log_level"))
262 tmp["/output/log/level"_json_pointer] = int(log_level);
263 if (has_arg(command_line, "max_threads"))
264 tmp["/solver/max_threads"_json_pointer] = max_threads;
265 if (has_arg(command_line, "output_dir"))
266 tmp["/output/directory"_json_pointer] = std::filesystem::absolute(output_dir);
267 if (has_arg(command_line, "enable_overwrite_solver"))
268 tmp["/solver/linear/enable_overwrite_solver"_json_pointer] = fallback_solver;
269 assert(tmp.is_object());
270 in_args.merge_patch(tmp);
271
272 if (varform::uses_varform_state(in_args))
273 return forward_simulation_with_varform_state(names, cells, vertices, in_args, is_strict);
274
275 return forward_simulation_with_legacy_state(names, cells, vertices, in_args, is_strict);
276}
277
278#ifdef POLYFEM_WITH_OPTIMIZATION
279int optimization_simulation(const CLI::App &command_line,
280 const unsigned max_threads,
281 const bool is_strict,
282 const spdlog::level::level_enum &log_level,
283 json &opt_args)
284{
285 json tmp = json::object();
286 if (has_arg(command_line, "log_level"))
287 tmp["/output/log/level"_json_pointer] = int(log_level);
288 if (has_arg(command_line, "max_threads"))
289 tmp["/solver/max_threads"_json_pointer] = max_threads;
290 opt_args.merge_patch(tmp);
291
292 OptState opt_state;
293 opt_state.init(opt_args, is_strict);
294
295 opt_state.create_states(opt_state.args["solver"]["max_threads"].get<int>());
296 opt_state.init_variables();
297 opt_state.create_problem();
298
299 Eigen::VectorXd x;
300 opt_state.initial_guess(x);
301
302 if (opt_state.args["compute_objective"].get<bool>())
303 {
304 logger().info("Objective is {}", opt_state.eval(x));
305 return EXIT_SUCCESS;
306 }
307
308 opt_state.solve(x);
309 return EXIT_SUCCESS;
310}
311#endif
int x
main class that contains the polyfem adjoint solver and all its state
Definition OptState.hpp:31
void solve(Eigen::VectorXd &x)
Definition OptState.cpp:306
void initial_guess(Eigen::VectorXd &x)
Definition OptState.cpp:293
void create_states(const int max_threads=-1)
create the opt states
Definition OptState.cpp:141
json args
main input arguments containing all defaults
Definition OptState.hpp:47
void init(const json &args, const bool strict_validation)
initialize the polyfem solver with a json settings
Definition OptState.cpp:110
double eval(Eigen::VectorXd &x) const
Definition OptState.cpp:300
void init_variables()
init variables
Definition OptState.cpp:217
VarForm-only simulation state.
Definition State.hpp:30
void init(const json &args, const bool strict_validation)
initialize the polyfem solver with a json settings
Definition State.cpp:243
std::shared_ptr< varform::VarForm > variational_formulation
active variational formulation
Definition State.hpp:47
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 State.cpp:402
void solve(Eigen::MatrixXd &sol)
solves the problem, call other methods
Definition State.cpp:454
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:2017
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:594
io::OutStatsData stats
Other statistics.
Definition State.hpp:744
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:91
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:742
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:507
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:1665
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:102
bool load_json(const std::string &json_file, json &out)
Definition main.cpp:31
int main(int argc, char **argv)
Definition main.cpp:143
int forward_simulation_with_varform_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:78
bool load_yaml(const std::string &yaml_file, json &out)
Definition main.cpp:46
bool has_arg(const CLI::App &command_line, const std::string &value)
Definition main.cpp:22
int forward_simulation(const CLI::App &command_line, const std::string &hdf5_file, 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:219
list tmp
Definition p_bases.py:366
json yaml_file_to_json(const std::string &yaml_filepath)
Load a YAML file to JSON.
bool uses_varform_state(json args)
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