Nektar++
Python/Module.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: Module.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Python wrapper for Module.
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#include <FieldUtils/Module.h>
39
40#include <boost/program_options.hpp>
41
42#ifdef NEKTAR_USING_VTK
45#endif
46
47using namespace Nektar;
48using namespace Nektar::FieldUtils;
49
50/**
51 * @brief Module wrapper to handle virtual function calls in @c Module and its
52 * subclasses as defined by the template parameter @tparam MODTYPE.
53 */
54#pragma GCC visibility push(hidden)
55template <typename MODTYPE>
56struct ModuleWrap : public MODTYPE, public py::trampoline_self_life_support
57{
58 /**
59 * @brief Constructor, which is identical to FieldUtils::Module.
60 *
61 * @param field Input field.
62 */
64 {
65 }
66
67 /**
68 * @brief Concrete implementation of the Module::Process function.
69 */
70 void v_Process([[maybe_unused]] po::variables_map &vm) override
71 {
72 py::gil_scoped_acquire gil;
73 py::function override = py::get_override(this, "Process");
74
75 if (this->m_f->m_nParts > 1)
76 {
77 if (this->GetModulePriority() == eOutput)
78 {
79 this->m_f->m_comm = this->m_f->m_partComm;
80 if (this->GetModuleName() != "OutputInfo")
81 {
82 this->RegisterConfig("writemultiplefiles");
83 }
84 }
85 else if (this->GetModulePriority() == eCreateGraph)
86 {
87 this->m_f->m_comm = this->m_f->m_partComm;
88 }
89 else
90 {
91 this->m_f->m_comm = this->m_f->m_defComm;
92 }
93 }
94
95 if (override)
96 {
97 override();
98 return;
99 }
100
101 throw ErrorUtil::NekError("Process() is a pure virtual function");
102 }
103
104 std::string v_GetModuleName() override
105 {
106 py::gil_scoped_acquire gil;
107 py::function override = py::get_override(this, "GetModuleName");
108
109 if (override)
110 {
111 return py::cast<std::string>(override());
112 }
113
114 throw ErrorUtil::NekError("GetModuleName() is a pure virtual function");
115 }
116
118 {
119 py::gil_scoped_acquire gil;
120 py::function override = py::get_override(this, "GetModulePriority");
121
122 if (override)
123 {
124 return py::cast<ModulePriority>(override());
125 }
126
128 "GetModulePriority() is a pure virtual function");
129 }
130
131 /**
132 * @brief Defines a configuration option for this module.
133 *
134 * @param key The name of the configuration option.
135 * @param def The option's default value.
136 * @param desc A text description of the option.
137 * @param isBool If true, this option is a boolean-type (true/false).
138 */
139 void AddConfigOption(std::string key, std::string def, std::string desc,
140 bool isBool)
141 {
142 ConfigOption conf(isBool, def, desc);
143 this->m_config[key] = conf;
144 }
145
146 // We expose Module::m_f as a public member variable so that we can
147 // adjust this using Python attributes.
148 using Module::m_f;
149};
150#pragma GCC visibility pop
151
152template <typename MODTYPE> struct ModulePublic : public MODTYPE
153{
154 using MODTYPE::v_GetModuleName;
155 using MODTYPE::v_GetModulePriority;
156 using MODTYPE::v_Process;
157};
158
159// Wrapper around Module::Process(&vm).
160// Performs switching of m_comm if nparts > 1.
162{
163 if (m->m_f->m_nParts > 1)
164 {
165 if (m->GetModulePriority() == eOutput)
166 {
167 m->m_f->m_comm = m->m_f->m_partComm;
168 if (m->GetModuleName() != "OutputInfo")
169 {
170 m->RegisterConfig("writemultiplefiles");
171 }
172 }
173 else if (m->GetModulePriority() == eCreateGraph)
174 {
175 m->m_f->m_comm = m->m_f->m_partComm;
176 }
177 else
178 {
179 m->m_f->m_comm = m->m_f->m_defComm;
180 }
181 }
182 m->SetDefaults();
183 m->Process(m->m_f->m_vm);
184}
185
186template <typename T>
187T Module_GetConfig(std::shared_ptr<Module> mod, const std::string &key)
188{
189 return mod->GetConfigOption(key).as<T>();
190}
191
192template <typename MODTYPE> struct ModuleTypeProxy
193{
194};
195
196template <> struct ModuleTypeProxy<InputModule>
197{
198 static const ModuleType value = eInputModule;
199};
200
202{
203 static const ModuleType value = eProcessModule;
204};
205
206template <> struct ModuleTypeProxy<OutputModule>
207{
208 static const ModuleType value = eOutputModule;
209};
210
214
215#ifdef NEKTAR_USING_VTK
216vtkUnstructuredGrid *Module_GetVtkGrid(std::shared_ptr<Module> mod)
217{
218 std::shared_ptr<OutputVtk> vtkModule =
219 std::dynamic_pointer_cast<OutputVtk>(mod);
220
222
223 if (!vtkModule)
224 {
225 throw NekError("This module is not the OutputVtk module, cannot get"
226 "VTK data.");
227 }
228
229 return vtkModule->GetVtkGrid();
230}
231#else
232void Module_GetVtkGrid(std::shared_ptr<Module> mod)
233{
235 throw NekError("Nektar++ has not been compiled with VTK support.");
236}
237#endif
238
239/**
240 * @brief Lightweight wrapper for Module factory creation function.
241 *
242 * @param modType Module type (input/process/output).
243 * @param modName Module name (typically filename extension).
244 * @param field Field that will be passed between modules.
245 * @tparam MODTYPE Subclass of Module (e.g #InputModule, #OutputModule)
246 */
247template <typename MODTYPE>
248ModuleSharedPtr Module_Create(py::args args, const py::kwargs &kwargs)
249{
251
253
254 if (modType == eProcessModule && py::len(args) != 2)
255 {
256 throw NekError("ProcessModule.Create() requires two arguments: "
257 "module name and a Field object.");
258 }
259 else if (modType != eProcessModule && py::len(args) < 2)
260 {
261 throw NekError(ModuleTypeMap[modType] +
262 "Module.Create() requires "
263 "two arguments: module name and a Field object; "
264 "optionally a filename.");
265 }
266
267 std::string modName = py::str(args[0]);
268 ModuleKey modKey = std::make_pair(modType, modName);
269
271
272 try
273 {
274 field = py::cast<FieldSharedPtr>(args[1]);
275 }
276 catch (...)
277 {
278 throw NekError("Second argument to Create() should be a Field object.");
279 }
280
281 if (!GetModuleFactory().ModuleExists(modKey))
282 {
283 throw ErrorUtil::NekError("Module '" + modName + "' does not exist.");
284 }
285
287
288 if (modType == eInputModule)
289 {
290 // For input modules we can try to interpret the remaining arguments as
291 // input files. Assume that the file's type is identical to the module
292 // name.
293 for (int i = 2; i < py::len(args); ++i)
294 {
295 std::string in_fname = py::str(args[i]);
296 mod->RegisterConfig("infile", in_fname);
297 mod->AddFile(modName, in_fname);
298 }
299 }
300 else if (modType == eOutputModule && py::len(args) >= 3)
301 {
302 // For output modules we can try to interpret the remaining argument as
303 // an output file.
304 mod->RegisterConfig("outfile", py::str(args[2]));
305 }
306
307 // Process keyword arguments.
308 for (auto &kv : kwargs)
309 {
310 std::string arg = py::str(kv.first);
311
312 if (arg == "infile" && modKey.first == eInputModule)
313 {
314 if (!py::isinstance<py::dict>(kv.second))
315 {
316 throw NekError("infile should be a dictionary.");
317 }
318
319 py::dict ftype_fname_dict = py::cast<py::dict>(kv.second);
320
321 for (auto &kv2 : ftype_fname_dict)
322 {
323 std::string f_type = py::str(kv2.first);
324 std::string f_name = py::str(kv2.second);
325 mod->RegisterConfig(arg, f_name);
326 mod->AddFile(f_type, f_name);
327 }
328 }
329 else
330 {
331 std::string val = py::str(kv.second);
332 mod->RegisterConfig(arg, val);
333 }
334 }
335
336 mod->SetDefaults();
337
338 return mod;
339}
340
341/**
342 * @brief Lightweight wrapper for FieldUtils::Module::RegisterConfig.
343 *
344 * @param mod Module to call
345 * @param key Configuration key.
346 * @param value Optional value (some configuration options are boolean).
347 */
348void Module_RegisterConfig(std::shared_ptr<Module> mod, std::string const &key,
349 std::string const &value)
350{
351 mod->RegisterConfig(key, value);
352}
353
354template <typename MODTYPE>
356 std::string const &key,
357 std::string const &defValue,
358 std::string const &desc, bool isBool)
359{
360 mod->AddConfigOption(key, defValue, desc, isBool);
361}
362
363template <typename MODTYPE> struct PythonModuleClass
364{
365 PythonModuleClass(py::module &m, std::string modName)
366 {
367 py::classh<MODTYPE, Module, ModuleWrap<MODTYPE>>(m, modName.c_str())
368 .def(py::init<FieldSharedPtr>())
369
370 .def("AddConfigOption", &ModuleWrap_AddConfigOption<MODTYPE>,
371 py::arg("key"), py::arg("defValue"), py::arg("desc"),
372 py::arg("isBool") = false)
373
374 // Allow direct access to field object through a property.
375 .def_readwrite("field", &ModuleWrap<MODTYPE>::m_f)
376
377 // Process function for this module.
378 .def("Process", &ModulePublic<MODTYPE>::v_Process)
380 .def_static("Create", &Module_Create<MODTYPE>);
381 }
382};
383
384void export_Module(py::module &m)
385{
387
388 // Export ModuleType enum.
390
391 // Wrapper for the Module class. Note that since Module contains a pure
392 // virtual function, we need the ModuleWrap helper class to handle this for
393 // us. In the lightweight wrappers above, we therefore need to ensure we're
394 // passing std::shared_ptr<Module> as the first argument, otherwise they
395 // won't accept objects constructed from Python.
396 py::classh<Module, ModuleWrap<Module>>(m, "Module")
397 .def(py::init<FieldSharedPtr>())
398
399 // Process function for this module.
400 .def("Process", &Module_Process)
401 .def("Run", &Module_Process)
402
403 // Configuration options.
404 .def("RegisterConfig", Module_RegisterConfig, py::arg("key"),
405 py::arg("value") = "")
406 .def("PrintConfig", &Module::PrintConfig)
407 .def("SetDefaults", &Module::SetDefaults)
408 .def("GetStringConfig", Module_GetConfig<std::string>)
409 .def("GetFloatConfig", Module_GetConfig<double>)
410 .def("GetIntConfig", Module_GetConfig<int>)
411 .def("GetBoolConfig", Module_GetConfig<bool>)
412 .def("AddConfigOption", &ModuleWrap_AddConfigOption<Module>,
413 py::arg("key"), py::arg("defValue"), py::arg("desc"),
414 py::arg("isBool") = false)
415 .def("GetVtkGrid", &Module_GetVtkGrid)
416
417 // Allow direct access to field object through a property.
418 .def_readwrite("field", &ModuleWrap<Module>::m_f)
419
420 // Factory functions.
421 .def_static("Register", [](ModuleType const &modType,
422 std::string const &modName,
423 py::object &obj) {
424 ModuleKey key(modType, modName);
425 fac(key, obj, std::string(ModuleTypeMap[modType]) + "_" + modName);
426 });
427
428 PythonModuleClass<InputModule>(m, "InputModule");
429 PythonModuleClass<ProcessModule>(m, "ProcessModule");
430 PythonModuleClass<OutputModule>(m, "OutputModule");
431}
Nektar::ErrorUtil::NekError NekError
#define NEKPY_WRAP_ENUM_STRING(MOD, ENUMNAME, MAPNAME)
Definition: NekPyConfig.hpp:62
T Module_GetConfig(std::shared_ptr< Module > mod, const std::string &key)
void Module_RegisterConfig(std::shared_ptr< Module > mod, std::string const &key, std::string const &value)
Lightweight wrapper for FieldUtils::Module::RegisterConfig.
void Module_Process(ModuleSharedPtr m)
ModuleSharedPtr Module_Create(py::args args, const py::kwargs &kwargs)
Lightweight wrapper for Module factory creation function.
void ModuleWrap_AddConfigOption(std::shared_ptr< ModuleWrap< MODTYPE > > mod, std::string const &key, std::string const &defValue, std::string const &desc, bool isBool)
void export_Module(py::module &m)
void Module_GetVtkGrid(std::shared_ptr< Module > mod)
Abstract base class for input modules.
Definition: Module.h:287
Abstract base class for output modules.
Definition: Module.h:316
Abstract base class for processing modules.
Definition: Module.h:301
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:1026
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:180
const std::string ModuleTypeMap[]
Definition: Module.h:72
std::shared_ptr< Module > ModuleSharedPtr
Definition: Module.h:329
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:47
Module wrapper to handle virtual function calls in Module and its subclasses as defined by the templa...
void AddConfigOption(std::string key, std::string def, std::string desc, bool isBool)
Defines a configuration option for this module.
std::string v_GetModuleName() override
ModulePriority v_GetModulePriority() override
void v_Process(po::variables_map &vm) override
Concrete implementation of the Module::Process function.
ModuleWrap(FieldSharedPtr field)
Constructor, which is identical to FieldUtils::Module.
Represents a command-line configuration option.
Definition: Module.h:129
PythonModuleClass(py::module &m, std::string modName)