Nektar++
Loading...
Searching...
No Matches
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
195 const NekDouble &time)
196{
197 try
198 {
199 if (m_pyFilter)
200 {
201 m_pyFilter->Initialise(pFields, time);
202 }
203 else
204 {
205 m_global["filter_initialise"](ArrayOneDToPyList(pFields), time);
206 }
207 }
208 catch (py::error_already_set const &)
209 {
210 std::cout << "Error in Python: " << parse_python_exception()
211 << std::endl;
212 }
213}
214
217 const NekDouble &time)
218{
219 try
220 {
221 if (m_pyFilter)
222 {
223 m_pyFilter->Update(pFields, time);
224 }
225 else
226 {
227 m_global["filter_update"](ArrayOneDToPyList(pFields), time);
228 }
229 }
230 catch (py::error_already_set const &)
231 {
232 std::cout << "Error in Python: " << parse_python_exception()
233 << std::endl;
234 }
235}
236
239 const NekDouble &time)
240{
241 try
242 {
243 if (m_pyFilter)
244 {
245 m_pyFilter->Finalise(pFields, time);
246 }
247 else
248 {
249 m_global["filter_finalise"](ArrayOneDToPyList(pFields), time);
250 }
251 }
252 catch (py::error_already_set const &)
253 {
254 std::cout << "Error in Python: " << parse_python_exception()
255 << std::endl;
256 }
257}
258
260{
261 if (m_pyFilter)
262 {
263 return m_pyFilter->IsTimeDependent();
264 }
265
266 return true;
267}
268
269} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
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.
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)
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.
static std::string className
Name of the class.
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()