OGS
ogs_python_module.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include <pybind11/pybind11.h>
5#include <pybind11/stl.h>
6#include <spdlog/spdlog.h>
7#include <tclap/CmdLine.h>
8
9#include <range/v3/range/conversion.hpp>
10#include <range/v3/view/transform.hpp>
11
15#include "BaseLib/ConfigTree.h"
16#include "BaseLib/DateTools.h"
17#include "BaseLib/Error.h"
18#include "BaseLib/FileTools.h"
19#include "BaseLib/Logging.h"
20#include "BaseLib/MPI.h"
21#include "BaseLib/RunTime.h"
24#include "InfoLib/GitInfo.h"
25
26static constexpr int EXIT_ARGPARSE_FAILURE = 3; // "mangled" TCLAP status
27static constexpr int EXIT_ARGPARSE_EXIT_OK = 2; // "mangled" TCLAP status
28static_assert(EXIT_FAILURE == 1);
29static_assert(EXIT_SUCCESS == 0);
30
31#define OGS_ALWAYS_ASSERT(cond) \
32 if (!(cond)) \
33 { \
34 OGS_FATAL("OGS assertion failed {}", #cond); \
35 }
36
37std::pair<int, std::vector<char*>> toArgcArgv(
38 std::vector<std::string>& argv_str)
39{
40 int argc = argv_str.size();
41 auto argv_vec = argv_str |
42 ranges::views::transform([](auto& s) { return s.data(); }) |
43 ranges::to<std::vector<char*>>();
44 argv_vec.push_back(nullptr); // last entry must be a nullptr!
45
46 return {argc, std::move(argv_vec)};
47}
48
49// Needs to be exported, see
50// https://pybind11.readthedocs.io/en/stable/advanced/misc.html#partitioning-code-over-multiple-extension-modules
51class PYBIND11_EXPORT OGSSimulation
52{
53public:
54 explicit OGSSimulation(std::vector<std::string>& argv_str)
55 {
56 auto [argc, argv_vec] = toArgcArgv(argv_str);
57 char** argv = argv_vec.data();
58
59 mpi_setup.emplace(argc, argv);
60
61 CommandLineArguments cli_args;
62 try
63 {
64 cli_args = parseCommandLineArguments(argc, argv, false);
65 }
66 catch (TCLAP::ArgException const& e)
67 {
68 // TODO fragile interplay between (incomplete) simulation
69 // initialization and OGS logger initialization
71
72 std::cerr << "Parsing the OGS commandline failed: " << e.what()
73 << '\n';
74
75 // "mangle" TCLAP's status
76 throw(e);
77 }
78 catch (TCLAP::ExitException const& e)
79 {
80 // TODO fragile interplay between (incomplete) simulation
81 // initialization and OGS logger initialization
83
84 if (e.getExitStatus() == 0)
85 {
86 // --version/--help
87 return;
88 }
89
90 throw(e);
91 }
92
94
95 DBUG("OGSSimulation::OGSSimulation(std::vector<std::string>&)");
96
97 INFO(
98 "This is OpenGeoSys-6 version {:s}. Log version: {:d}, Log level: "
99 "{:s}.",
101
103
104 {
105 auto const start_time = std::chrono::system_clock::now();
106 auto const time_str = BaseLib::formatDate(start_time);
107 // todo ask Tobias: started vs starts
108#ifdef USE_PETSC
109 int size;
110 MPI_Comm_size(BaseLib::MPI::OGS_COMM_WORLD, &size);
111 int rank;
112 MPI_Comm_rank(BaseLib::MPI::OGS_COMM_WORLD, &rank);
113 INFO(
114 "OGS starts on {:s} in MPI mode [{}] of {} / Python embedded "
115 "mode.",
116 time_str, rank, size);
117#else
118 INFO("OGS starts on {:s} in serial mode / Python embedded mode.",
119 time_str);
120#endif
121 }
122 try
123 {
124 simulation = std::make_unique<Simulation>(argc, argv);
125 simulation->initializeDataStructures(
126 std::move(cli_args.project),
127 std::move(cli_args.xml_patch_file_names),
128 cli_args.reference_path_is_set,
129 std::move(cli_args.reference_path), cli_args.nonfatal,
130 std::move(cli_args.outdir), std::move(cli_args.mesh_dir),
131 std::move(cli_args.script_dir), cli_args.write_prj);
132 }
133 catch (std::exception& e)
134 {
135 ERR("{}", e.what());
136 ogs_status = EXIT_FAILURE;
137 simulation.reset();
138 throw(e);
139 }
140 INFO("OpenGeoSys is now initialized.");
141 }
142
144 {
146
147 BaseLib::RunTime run_time;
148
149 {
150 auto const start_time = std::chrono::system_clock::now();
151 auto const time_str = BaseLib::formatDate(start_time);
152 INFO("OGS started on {:s} in serial mode.", time_str);
153 }
154
155 try
156 {
157 run_time.start();
158 bool solver_succeeded = simulation->executeSimulation();
159 simulation->outputLastTimeStep();
160 test_definition = simulation->getTestDefinition();
162
163 if (solver_succeeded)
164 {
165 INFO("[time] Simulation completed. It took {:g} s.",
166 run_time.elapsed());
167 }
168 else
169 {
170 INFO("[time] Simulation failed. It took {:g} s.",
171 run_time.elapsed());
172 }
173 ogs_status = solver_succeeded ? EXIT_SUCCESS : EXIT_FAILURE;
174 }
175 catch (std::exception& e)
176 {
177 ERR("{}", e.what());
178 ogs_status = EXIT_FAILURE;
179 }
180
181 if (ogs_status == EXIT_FAILURE)
182 {
183 auto const end_time = std::chrono::system_clock::now();
184 auto const time_str = BaseLib::formatDate(end_time);
185 ERR("OGS terminated with error on {:s}.", time_str);
186 return EXIT_FAILURE;
187 }
188
189 return ogs_status;
190 }
191
193 {
195
196 try
197 {
198 bool solver_succeeded = simulation->executeTimeStep();
199 ogs_status = solver_succeeded ? EXIT_SUCCESS : EXIT_FAILURE;
200 }
201 catch (std::exception& e)
202 {
203 ERR("{}", e.what());
204 ogs_status = EXIT_FAILURE;
205 }
206 return ogs_status;
207 }
208
209 double currentTime() const
210 {
212 return simulation->currentTime();
213 }
214
215 double endTime() const
216 {
218 return simulation->endTime();
219 }
220
221 OGSMesh& getMesh(std::string const& name)
222 {
224
225 auto const mesh_it = mesh_mapping.find(name);
226 if (mesh_it != mesh_mapping.end())
227 {
228 INFO("found OGSMesh '{}' with address: {}", name,
229 fmt::ptr(&(mesh_it->second)));
230 return mesh_it->second;
231 }
232
233 auto const& [it, success] =
234 mesh_mapping.insert({name, OGSMesh(simulation->getMesh(name))});
235 if (!success)
236 {
237 OGS_FATAL("Could not access mesh '{}'.", name);
238 }
239 INFO("insert OGSMesh '{}' with address: {}", name,
240 fmt::ptr(&(it->second)));
241 return it->second;
242 }
243
244 std::vector<std::string> getMeshNames() const
245 {
247
248 return simulation->getMeshNames();
249 }
250
252 {
253 if (simulation)
254 {
255 simulation->outputLastTimeStep();
256 simulation.reset(nullptr);
257 }
258
259 // Check for swallowed ConfigTree errors after Simulation destructor
260 // runs. This catches configuration errors in objects destroyed at end
261 // of scope.
262 try
263 {
265 }
266 catch (std::exception& e)
267 {
268 ERR("{}", e.what());
269 ogs_status = EXIT_FAILURE;
270 }
271
272 if (ogs_status == EXIT_SUCCESS && test_definition_pending)
273 {
275 }
277
278 mpi_setup.reset();
279
280 return ogs_status;
281 }
282
283 int status() const { return ogs_status; }
284
285 bool initialized() const { return simulation != nullptr; }
286
287private:
288 int ogs_status = EXIT_SUCCESS;
289
290 std::unique_ptr<Simulation> simulation;
291 std::optional<ApplicationsLib::TestDefinition> test_definition{
292 std::nullopt};
294 std::map<std::string, OGSMesh> mesh_mapping;
295 std::optional<BaseLib::MPI::Setup> mpi_setup;
296};
297
304PYBIND11_MODULE(OGSSimulator, m)
305{
306 m.attr("__name__") = "ogs.OGSSimulator";
307 m.doc() = "pybind11 ogs plugin";
308
309 pybind11::class_<OGSSimulation>(m, "OGSSimulation")
310 .def(pybind11::init<std::vector<std::string>&>())
311 .def("current_time", &OGSSimulation::currentTime,
312 "get current OGS time")
313 .def("end_time", &OGSSimulation::endTime, "get end OGS time")
314 .def("execute_simulation", &OGSSimulation::executeSimulation,
315 "execute OGS simulation")
316 .def("execute_time_step", &OGSSimulation::executeTimeStep,
317 "execute OGS time step")
318 .def("mesh", &OGSSimulation::getMesh,
319 pybind11::return_value_policy::automatic_reference,
320 pybind11::arg("name"), "get unstructured grid from ogs")
321 .def("mesh_names", &OGSSimulation::getMeshNames,
322 "get names of all meshes from ogs")
323 .def("close", &OGSSimulation::finalize, "finalize OGS simulation")
324 .def_property_readonly("status", &OGSSimulation::status)
325 .def_property_readonly(
326 "initialized", &OGSSimulation::initialized,
327 "Tells if the simulation object has been completely initialized.");
328}
CommandLineArguments parseCommandLineArguments(int argc, char *argv[], bool const exit_on_exception)
#define OGS_FATAL(...)
Definition Error.h:10
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
static void assertNoSwallowedErrors()
Asserts that there have not been any errors reported in the destructor.
Count the running time.
Definition RunTime.h:18
double elapsed() const
Get the elapsed time in seconds.
Definition RunTime.h:31
void start()
Start the timer.
Definition RunTime.h:21
std::map< std::string, OGSMesh > mesh_mapping
std::optional< ApplicationsLib::TestDefinition > test_definition
bool initialized() const
std::optional< BaseLib::MPI::Setup > mpi_setup
OGSMesh & getMesh(std::string const &name)
std::unique_ptr< Simulation > simulation
double endTime() const
OGSSimulation(std::vector< std::string > &argv_str)
double currentTime() const
std::vector< std::string > getMeshNames() const
static OGS_EXPORT_SYMBOL int runTestDefinitions(std::optional< ApplicationsLib::TestDefinition > &test_definition)
MPI_Comm OGS_COMM_WORLD
Definition MPI.cpp:9
void initOGSLogger(std::string const &log_level)
Definition Logging.cpp:104
std::string formatDate(std::chrono::time_point< std::chrono::system_clock > const &time)
std::string defaultLogLevel()
bool createOutputDirectory(std::string const &dir)
GITINFOLIB_EXPORT const std::string ogs_version
static constexpr int EXIT_ARGPARSE_EXIT_OK
PYBIND11_MODULE(OGSSimulator, m)
static constexpr int EXIT_ARGPARSE_FAILURE
#define OGS_ALWAYS_ASSERT(cond)
std::pair< int, std::vector< char * > > toArgcArgv(std::vector< std::string > &argv_str)
std::vector< std::string > xml_patch_file_names