Nektar++
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
FilterPython.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: FilterPython.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: Run a Python script during runtime through Filters.
32//
33///////////////////////////////////////////////////////////////////////////////
34
36
37#include <fstream>
38#include <streambuf>
39#include <string>
40
41namespace Nektar::SolverUtils
42{
43
44/// Temporarily stolen from boost examples
46{
47 PyObject *type_ptr = nullptr, *value_ptr = nullptr,
48 *traceback_ptr = nullptr;
49
50 // Fetch the exception info from the Python C API
51 PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr);
52
53 // Fallback error
54 std::string ret("Unfetchable Python error");
55
56 // If the fetch got a type pointer, parse the type into the exception string
57 if (type_ptr != nullptr)
58 {
59 py::handle h_type(type_ptr);
60 ret = py::str(h_type);
61 }
62
63 // Do the same for the exception value (the stringification of the
64 // exception)
65 if (value_ptr != nullptr)
66 {
67 py::handle h_val(value_ptr);
68 ret += ": " + std::string(py::str(h_val));
69 }
70
71 // Parse lines from the traceback using the Python traceback module
72 if (traceback_ptr != nullptr)
73 {
74 py::handle h_tb(traceback_ptr);
75
76 // Load the traceback module and the format_tb function
77 py::object tb(py::module_::import("traceback"));
78 py::object fmt_tb(tb.attr("format_tb"));
79
80 // Call format_tb to get a list of traceback strings
81 py::object tb_list(fmt_tb(h_tb));
82
83 // Join the traceback strings into a single string
84 py::object tb_str(py::str("\n").attr("join")(tb_list));
85
86 // Extract the string, check the extraction, and fallback in necessary
87 ret += ": " + std::string(py::str(tb_str));
88 }
89 return ret;
90}
91
92// Converts a OneD array of ExpLists to a Python list.
93inline py::list ArrayOneDToPyList(
95{
96 py::list expLists;
97
98 for (int i = 0; i < pFields.size(); ++i)
99 {
100 expLists.append(py::cast(pFields[i]));
101 }
102
103 return expLists;
104}
105
106std::string FilterPython::className =
108
110 const std::shared_ptr<EquationSystem> &pEquation,
111 const ParamMap &pParams)
112 : Filter(pSession, pEquation)
113{
114 if (!Py_IsInitialized())
115 {
116 py::initialize_interpreter();
117 }
118
119 auto it = pParams.find("PythonFile");
120 ASSERTL0(it != pParams.end(), "Empty parameter 'PythonFile'.");
121 std::string pythonFile = it->second, pythonCode;
122
123 // Set the global namespace.
124 m_global = py::module_::import("__main__").attr("__dict__");
125
126 try
127 {
128 std::ifstream t(pythonFile);
129 std::string str((std::istreambuf_iterator<char>(t)),
130 std::istreambuf_iterator<char>());
131 pythonCode = str;
132 }
133 catch (...)
134 {
135 ASSERTL0(false, "Error reading Python file: " + pythonFile);
136 }
137
138 // Print out the loaded session file if we're running in verbose mode.
139 if (pSession->DefinesCmdLineArgument("verbose"))
140 {
141 std::cout << "-----------------" << std::endl;
142 std::cout << "BEGIN PYTHON CODE" << std::endl;
143 std::cout << "-----------------" << std::endl;
144 std::cout << pythonCode << std::endl;
145 std::cout << "-----------------" << std::endl;
146 std::cout << "END PYTHON CODE" << std::endl;
147 std::cout << "-----------------" << std::endl;
148 }
149
150 try
151 {
152 // Import NekPy libraries. This ensures we have all of our boost python
153 // wrappers. I guess this could also be done by the calling script...
154 auto nekpy = py::module_::import("NekPy");
155 auto multireg = py::module_::import("NekPy.MultiRegions");
156
157 // Eval the python code. We can then grab functions etc from the global
158 // namespace.
159 py::exec(pythonCode.c_str(), m_global, m_global);
160 }
161 catch (py::error_already_set const &)
162 {
163 // Parse and output the exception
164 ASSERTL0(false,
165 "Failed to load Python code: " + parse_python_exception());
166 }
167
168 // Give the option as well to just create a filter that's defined via
169 // Python.
170 it = pParams.find("FilterName");
171 if (it != pParams.end())
172 {
173 std::string filterName = it->second;
174
175 // Check filter factory to make sure we have this class.
176 ASSERTL0(GetFilterFactory().ModuleExists(filterName),
177 "Unable to locate filter named '" + filterName + "'");
178
179 try
180 {
181 m_pyFilter = GetFilterFactory().CreateInstance(filterName, pSession,
182 pEquation, pParams);
183 }
184 catch (py::error_already_set const &)
185 {
186 // Parse and output the exception
187 ASSERTL0(false,
188 "Failed to load Python code: " + parse_python_exception());
189 }
190 }
191}
192
194{
195}
196
199 const NekDouble &time)
200{
201 try
202 {
203 if (m_pyFilter)
204 {
205 m_pyFilter->Initialise(pFields, time);
206 }
207 else
208 {
209 m_global["filter_initialise"](ArrayOneDToPyList(pFields), time);
210 }
211 }
212 catch (py::error_already_set const &)
213 {
214 std::cout << "Error in Python: " << parse_python_exception()
215 << std::endl;
216 }
217}
218
221 const NekDouble &time)
222{
223 try
224 {
225 if (m_pyFilter)
226 {
227 m_pyFilter->Update(pFields, time);
228 }
229 else
230 {
231 m_global["filter_update"](ArrayOneDToPyList(pFields), time);
232 }
233 }
234 catch (py::error_already_set const &)
235 {
236 std::cout << "Error in Python: " << parse_python_exception()
237 << std::endl;
238 }
239}
240
243 const NekDouble &time)
244{
245 try
246 {
247 if (m_pyFilter)
248 {
249 m_pyFilter->Finalise(pFields, time);
250 }
251 else
252 {
253 m_global["filter_finalise"](ArrayOneDToPyList(pFields), time);
254 }
255 }
256 catch (py::error_already_set const &)
257 {
258 std::cout << "Error in Python: " << parse_python_exception()
259 << std::endl;
260 }
261}
262
264{
265 if (m_pyFilter)
266 {
267 return m_pyFilter->IsTimeDependent();
268 }
269
270 return true;
271}
272
273} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
std::map< std::string, std::string > ParamMap
Definition: Filter.h:66
py::object m_global
Container for Python global variables.
Definition: FilterPython.h:86
void v_Finalise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
std::shared_ptr< Filter > m_pyFilter
Python filter class to run (optionally)
Definition: FilterPython.h:88
SOLVER_UTILS_EXPORT ~FilterPython() override
void v_Update(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
SOLVER_UTILS_EXPORT FilterPython(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< EquationSystem > &pEquation, const ParamMap &pParams)
static FilterSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< EquationSystem > &pEquation, const std::map< std::string, std::string > &pParams)
Creates an instance of this class.
Definition: FilterPython.h:53
static std::string className
Name of the class.
Definition: FilterPython.h:64
void v_Initialise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::string parse_python_exception()
Temporarily stolen from boost examples.
py::list ArrayOneDToPyList(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields)
FilterFactory & GetFilterFactory()
double NekDouble