Flow123d  jenkins-Flow123d-linux-release-multijob-282
field_python.impl.hh
Go to the documentation of this file.
1 /*
2  * function_python_impl.hh
3  *
4  * Created on: Oct 10, 2012
5  * Author: jb
6  */
7 
8 #ifndef FIELD_PYTHON_IMPL_HH_
9 #define FIELD_PYTHON_IMPL_HH_
10 
11 
12 #include <boost/type_traits.hpp>
13 #include "fields/field_python.hh"
14 
15 /// Implementation.
16 
17 namespace it = Input::Type;
18 
19 template <int spacedim, class Value>
21 
22 
23 
24 template <int spacedim, class Value>
26  Input::Type::AbstractRecord &a_type, const typename Value::ElementInputType *eit
27  )
28 {
29  it::Record type
30  = it::Record("FieldPython", FieldAlgorithmBase<spacedim,Value>::template_name()+" Field given by a Python script.")
31  .derive_from(a_type)
32  .declare_key("script_string", it::String(), it::Default::read_time("Obligatory if 'script_file' is not given."),
33  "Python script given as in place string")
34  .declare_key("script_file", it::FileName::input(), it::Default::read_time("Obligatory if 'script_striong' is not given."),
35  "Python script given as external file")
37  "Function in the given script that returns tuple containing components of the return type.\n"
38  "For NxM tensor values: tensor(row,col) = tuple( M*row + col ).");
39 
40  return type;
41 }
42 
43 
44 
45 template <int spacedim, class Value>
47 : FieldAlgorithmBase<spacedim, Value>( n_comp)
48 {
49 #ifdef HAVE_PYTHON
50  p_func_=NULL;
51  p_module_=NULL;
52  p_args_=NULL;
53  p_value_=NULL;
54 #else
55  xprintf(UsrErr, "Flow123d compiled without support for Python, FieldPython can not be used.\n");
56 #endif // HAVE_PYTHON
57 }
58 
59 
60 
61 template <int spacedim, class Value>
62 void FieldPython<spacedim, Value>::set_python_field_from_string(const string &python_source, const string &func_name)
63 {
64 #ifdef HAVE_PYTHON
65  p_module_ = PythonLoader::load_module_from_string("python_field_"+func_name, python_source);
66  set_func(func_name);
67 #endif // HAVE_PYTHON
68 }
69 
70 
71 
72 
73 
74 template <int spacedim, class Value>
76  Input::Iterator<string> it = rec.find<string>("script_string");
77  if (it) {
78  set_python_field_from_string( *it, rec.val<string>("function") );
79  } else {
80  Input::Iterator<FilePath> it = rec.find<FilePath>("script_file");
81  if (! it) xprintf(UsrErr, "Either 'script_string' or 'script_file' has to be specified in PythonField initialization.");
82  set_python_field_from_file( *it, rec.val<string>("function") );
83  }
84 }
85 
86 
87 
88 template <int spacedim, class Value>
89 void FieldPython<spacedim, Value>::set_python_field_from_file(const FilePath &file_name, const string &func_name)
90 {
91 #ifdef HAVE_PYTHON
92  p_module_ = PythonLoader::load_module_from_file( string(file_name) );
93  set_func(func_name);
94 #endif // HAVE_PYTHON
95 }
96 
97 
98 
99 
100 template <int spacedim, class Value>
101 void FieldPython<spacedim, Value>::set_func(const string &func_name)
102 {
103 #ifdef HAVE_PYTHON
104  char func_char[func_name.size()+2];
105  std::strcpy(func_char, func_name.c_str());
106  p_func_ = PyObject_GetAttrString(p_module_, func_char );
107  if (! p_func_) {
108  if (PyErr_Occurred()) PyErr_Print();
109  xprintf(UsrErr, "Field '%s' not found in the python module: %s\n", func_name.c_str(), PyModule_GetName(p_module_) );
110  Py_XDECREF(p_func_);
111  Py_XDECREF(p_module_);
112 
113  }
114  if (! PyCallable_Check(p_func_)) {
115  xprintf(UsrErr, "Field '%s' from the python module: %s is not callable.\n", func_name.c_str(), PyModule_GetName(p_module_) );
116  Py_XDECREF(p_func_);
117  Py_XDECREF(p_module_);
118 
119  }
120 
121  p_args_ = PyTuple_New( spacedim );
122 
123  // try field call
124  for(unsigned int i = 0; i < spacedim; i++) {
125  p_value_ = PyFloat_FromDouble( double(i) );
126  PyTuple_SetItem(p_args_, i, p_value_);
127  }
128  p_value_ = PyObject_CallObject(p_func_, p_args_);
129 
130  if (p_value_ == NULL) {
131  PyErr_Print();
132  // TODO: use cout to output also field arguments
133  xprintf(Err,"Failed to call field '%s' from the python module: %s\n", func_name.c_str(), PyModule_GetName(p_module_) );
134  }
135 
136  if ( ! PyTuple_Check( p_value_)) {
137  xprintf(UsrErr, "Field '%s' from the python module: %s doesn't return Tuple.\n", func_name.c_str(), PyModule_GetName(p_module_) );
138  }
139 
140  unsigned int size = PyTuple_Size( p_value_);
141 
142  unsigned int value_size=this->value_.n_rows() * this->value_.n_cols();
143  if ( size != value_size) {
144  xprintf(UsrErr, "Field '%s' from the python module: %s returns %d components but should return %d components.\n"
145  ,func_name.c_str(), PyModule_GetName(p_module_), size, value_size);
146  }
147 
148 #endif // HAVE_PYTHON
149 
150 }
151 
152 /**
153  * Returns one value in one given point. ResultType can be used to avoid some costly calculation if the result is trivial.
154  */
155 template <int spacedim, class Value>
156 typename Value::return_type const & FieldPython<spacedim, Value>::value(const Point &p, const ElementAccessor<spacedim> &elm)
157 {
158  set_value(p,elm, this->value_);
159  return this->r_value_;
160 }
161 
162 
163 /**
164  * Returns std::vector of scalar values in several points at once.
165  */
166 template <int spacedim, class Value>
169 {
170  ASSERT_EQUAL( point_list.size(), value_list.size() );
171  for(unsigned int i=0; i< point_list.size(); i++) {
172  Value envelope(value_list[i]);
173  ASSERT( envelope.n_rows()==this->value_.n_rows(),
174  "value_list[%d] has wrong number of rows: %d; should match number of components: %d\n",
175  i, envelope.n_rows(),this->value_.n_rows());
176  set_value(point_list[i], elm, envelope );
177  }
178 }
179 
180 /**
181 * Returns one vector value in one given point.
182 */
183 template <int spacedim, class Value>
185 {
186 #ifdef HAVE_PYTHON
187  for(unsigned int i = 0; i < spacedim; i++) {
188  p_value_ = PyFloat_FromDouble( p[i] );
189  PyTuple_SetItem(p_args_, i, p_value_);
190  }
191  p_value_ = PyObject_CallObject(p_func_, p_args_);
192 
193  if (p_value_ == NULL) {
194  PyErr_Print();
195  xprintf(Err,"FieldPython call failed\n");
196  }
197 
198  unsigned int pos =0;
199  for(unsigned int row=0; row < value.n_rows(); row++)
200  for(unsigned int col=0; col < value.n_cols(); col++, pos++)
201  if ( boost::is_integral< typename Value::element_type >::value ) value(row,col) = PyLong_AsLong( PyTuple_GetItem( p_value_, pos ) );
202  else value(row,col) = PyFloat_AsDouble( PyTuple_GetItem( p_value_, pos ) );
203 
204 #endif // HAVE_PYTHON
205 }
206 
207 
208 
209 
210 template <int spacedim, class Value>
212 #ifdef HAVE_PYTHON
213  Py_CLEAR(p_module_);
214  Py_CLEAR(p_func_);
215  Py_CLEAR(p_value_);
216  Py_CLEAR(p_args_);
217 #endif // HAVE_PYTHON
218 }
219 
220 
221 #endif /* FIELD_PYTHON_IMPL_HH_ */
void set_python_field_from_file(const FilePath &file_name, const string &func_name)
void set_func(const string &func_name)
void set_python_field_from_string(const string &python_source, const string &func_name)
virtual void value_list(const std::vector< Point > &point_list, const ElementAccessor< spacedim > &elm, std::vector< typename Value::return_type > &value_list)
static Default obligatory()
Definition: type_record.hh:89
Iterator< Ret > find(const string &key) const
Record & derive_from(AbstractRecord &parent)
Definition: type_record.cc:197
void set_value(const Point &p, const ElementAccessor< spacedim > &elm, Value &value)
#define ASSERT(...)
Definition: global_defs.h:121
static FileName input()
Definition: type_base.hh:464
#define ASSERT_EQUAL(a, b)
Definition: global_defs.h:136
FieldPython(unsigned int n_comp=0)
Accessor to the data with type Type::Record.
Definition: accessors.hh:327
const Ret val(const string &key) const
static Input::Type::Record get_input_type(Input::Type::AbstractRecord &a_type, const typename Value::ElementInputType *eit)
#define xprintf(...)
Definition: system.hh:100
Class for declaration of polymorphic Record.
Definition: type_record.hh:487
Space< spacedim >::Point Point
Dedicated class for storing path to input and output files.
Definition: file_path.hh:32
Definition: system.hh:72
static Default read_time(const std::string &description)
Definition: type_record.hh:77
virtual ~FieldPython()
Definition: system.hh:72
Record type proxy class.
Definition: type_record.hh:169
virtual Value::return_type const & value(const Point &p, const ElementAccessor< spacedim > &elm)
virtual void init_from_input(const Input::Record &rec)
Record & declare_key(const string &key, const KeyType &type, const Default &default_value, const string &description)
Definition: type_record.cc:430