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 INFO("OGS starts on {:s} in serial mode / Python embedded mode.",
109 time_str);
110 }
111 try
112 {
113 simulation = std::make_unique<Simulation>(argc, argv);
114 simulation->initializeDataStructures(
115 std::move(cli_args.project),
116 std::move(cli_args.xml_patch_file_names),
117 cli_args.reference_path_is_set,
118 std::move(cli_args.reference_path), cli_args.nonfatal,
119 std::move(cli_args.outdir), std::move(cli_args.mesh_dir),
120 std::move(cli_args.script_dir), cli_args.write_prj);
121 }
122 catch (std::exception& e)
123 {
124 ERR("{}", e.what());
125 ogs_status = EXIT_FAILURE;
126 simulation.reset();
127 throw(e);
128 }
129 INFO("OpenGeoSys is now initialized.");
130 }
131
133 {
135
136 BaseLib::RunTime run_time;
137
138 {
139 auto const start_time = std::chrono::system_clock::now();
140 auto const time_str = BaseLib::formatDate(start_time);
141 INFO("OGS started on {:s} in serial mode.", time_str);
142 }
143
144 std::optional<ApplicationsLib::TestDefinition> test_definition{
145 std::nullopt};
146
147 try
148 {
149 run_time.start();
150 bool solver_succeeded = simulation->executeSimulation();
151 simulation->outputLastTimeStep();
152 test_definition = simulation->getTestDefinition();
153
154 if (solver_succeeded)
155 {
156 INFO("[time] Simulation completed. It took {:g} s.",
157 run_time.elapsed());
158 }
159 else
160 {
161 INFO("[time] Simulation failed. It took {:g} s.",
162 run_time.elapsed());
163 }
164 ogs_status = solver_succeeded ? EXIT_SUCCESS : EXIT_FAILURE;
165 }
166 catch (std::exception& e)
167 {
168 ERR("{}", e.what());
169 ogs_status = EXIT_FAILURE;
170 }
171
172 if (ogs_status == EXIT_FAILURE)
173 {
174 auto const end_time = std::chrono::system_clock::now();
175 auto const time_str = BaseLib::formatDate(end_time);
176 ERR("OGS terminated with error on {:s}.", time_str);
177 return EXIT_FAILURE;
178 }
179
180 return Simulation::runTestDefinitions(test_definition);
181 }
182
184 {
186
187 try
188 {
189 bool solver_succeeded = simulation->executeTimeStep();
190 ogs_status = solver_succeeded ? EXIT_SUCCESS : EXIT_FAILURE;
191 }
192 catch (std::exception& e)
193 {
194 ERR("{}", e.what());
195 ogs_status = EXIT_FAILURE;
196 }
197 return ogs_status;
198 }
199
200 double currentTime() const
201 {
203 return simulation->currentTime();
204 }
205
206 double endTime() const
207 {
209 return simulation->endTime();
210 }
211
212 OGSMesh& getMesh(std::string const& name)
213 {
215
216 auto const mesh_it = mesh_mapping.find(name);
217 if (mesh_it != mesh_mapping.end())
218 {
219 INFO("found OGSMesh '{}' with address: {}", name,
220 fmt::ptr(&(mesh_it->second)));
221 return mesh_it->second;
222 }
223
224 auto const& [it, success] =
225 mesh_mapping.insert({name, OGSMesh(simulation->getMesh(name))});
226 if (!success)
227 {
228 OGS_FATAL("Could not access mesh '{}'.", name);
229 }
230 INFO("insert OGSMesh '{}' with address: {}", name,
231 fmt::ptr(&(it->second)));
232 return it->second;
233 }
234
235 std::vector<std::string> getMeshNames() const
236 {
238
239 return simulation->getMeshNames();
240 }
241
242 void finalize()
243 {
244 if (simulation)
245 {
246 simulation->outputLastTimeStep();
247 simulation.reset(nullptr);
248 }
249 mpi_setup.reset();
250
251 // Check for swallowed ConfigTree errors after Simulation destructor
252 // runs. This catches configuration errors in objects destroyed at end
253 // of scope.
254 try
255 {
257 }
258 catch (std::exception& e)
259 {
260 ERR("{}", e.what());
261 throw;
262 }
263 }
264
265 int status() const { return ogs_status; }
266
267 bool initialized() const { return simulation != nullptr; }
268
269private:
270 int ogs_status = EXIT_SUCCESS;
271
272 std::unique_ptr<Simulation> simulation;
273 std::map<std::string, OGSMesh> mesh_mapping;
274 std::optional<BaseLib::MPI::Setup> mpi_setup;
275};
276
283PYBIND11_MODULE(OGSSimulator, m)
284{
285 m.attr("__name__") = "ogs.OGSSimulator";
286 m.doc() = "pybind11 ogs plugin";
287
288 pybind11::class_<OGSSimulation>(m, "OGSSimulation")
289 .def(pybind11::init<std::vector<std::string>&>())
290 .def("current_time", &OGSSimulation::currentTime,
291 "get current OGS time")
292 .def("end_time", &OGSSimulation::endTime, "get end OGS time")
293 .def("execute_simulation", &OGSSimulation::executeSimulation,
294 "execute OGS simulation")
295 .def("execute_time_step", &OGSSimulation::executeTimeStep,
296 "execute OGS time step")
297 .def("mesh", &OGSSimulation::getMesh,
298 pybind11::return_value_policy::automatic_reference,
299 pybind11::arg("name"), "get unstructured grid from ogs")
300 .def("mesh_names", &OGSSimulation::getMeshNames,
301 "get names of all meshes from ogs")
302 .def("close", &OGSSimulation::finalize, "finalize OGS simulation")
303 .def_property_readonly("status", &OGSSimulation::status)
304 .def_property_readonly(
305 "initialized", &OGSSimulation::initialized,
306 "Tells if the simulation object has been completely initialized.");
307}
CommandLineArguments parseCommandLineArguments(int argc, char *argv[], bool const exit_on_exception)
#define OGS_FATAL(...)
Definition Error.h:19
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
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)
void initOGSLogger(std::string const &log_level)
Definition Logging.cpp:56
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