1 //                                               -*- C++ -*-
2 /**
3  * @brief PythonPointToFieldFunction implementation
4  *
5  *  Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6  *
7  *  This library is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 #include <Python.h>
22 #include "openturns/swig_runtime.hxx"
23 
24 #include "openturns/PythonPointToFieldFunction.hxx"
25 #include "openturns/OSS.hxx"
26 #include "openturns/Description.hxx"
27 #include "openturns/PythonWrappingFunctions.hxx"
28 #include "openturns/PersistentObjectFactory.hxx"
29 #include "openturns/Exception.hxx"
30 
31 BEGIN_NAMESPACE_OPENTURNS
32 
33 CLASSNAMEINIT(PythonPointToFieldFunction)
34 
35 static const Factory<PythonPointToFieldFunction> Factory_PythonPointToFieldFunction;
36 
37 /* Default constructor */
PythonPointToFieldFunction()38 PythonPointToFieldFunction::PythonPointToFieldFunction()
39   : PointToFieldFunctionImplementation()
40   , pyObj_(0)
41 {
42   // Nothing to do
43 }
44 
45 
46 /* Constructor from Python object*/
PythonPointToFieldFunction(PyObject * pyCallable)47 PythonPointToFieldFunction::PythonPointToFieldFunction(PyObject * pyCallable)
48   : PointToFieldFunctionImplementation()
49   , pyObj_(pyCallable)
50 {
51   Py_XINCREF(pyCallable);
52 
53   // Set the name of the object as its Python classname
54   ScopedPyObjectPointer cls(PyObject_GetAttrString ( pyObj_,
55                             const_cast<char *>("__class__" )));
56   ScopedPyObjectPointer name(PyObject_GetAttrString( cls.get(),
57                              const_cast<char *>("__name__" )));
58   setName(convert< _PyString_, String >(name.get()));
59 
60 
61 
62   const UnsignedInteger inputDimension  = getInputDimension();
63   const UnsignedInteger outputDimension = getOutputDimension();
64   Description description(inputDimension + outputDimension);
65 
66   ScopedPyObjectPointer descIn(PyObject_CallMethod( pyObj_,
67                                const_cast<char *>("getInputDescription"),
68                                const_cast<char *>("()")));
69   if ( descIn.get()
70        && PySequence_Check(descIn.get())
71        && (PySequence_Size(descIn.get()) == (SignedInteger)inputDimension))
72   {
73     setInputDescription(convert< _PySequence_, Description >(descIn.get()));
74   }
75   else setInputDescription(Description::BuildDefault(inputDimension, "x"));
76 
77 
78   ScopedPyObjectPointer descOut(PyObject_CallMethod( pyObj_,
79                                 const_cast<char *>("getOutputDescription" ),
80                                 const_cast<char *>("()")));
81   if ( descOut.get()
82        && PySequence_Check(descOut.get())
83        && (PySequence_Size(descOut.get()) == (SignedInteger)outputDimension))
84   {
85     setOutputDescription(convert< _PySequence_, Description >(descOut.get()));
86   }
87   else setOutputDescription(Description::BuildDefault(outputDimension, "y"));
88 
89   ScopedPyObjectPointer outputMesh(PyObject_CallMethod ( pyObj_,
90                                    const_cast<char *>("getOutputMesh"),
91                                    const_cast<char *>("()")));
92   void * ptr = 0;
93   if (SWIG_IsOK(SWIG_ConvertPtr(outputMesh.get(), &ptr, SWIG_TypeQuery("OT::Mesh *"), 0)))
94   {
95     outputMesh_ = *reinterpret_cast< OT::Mesh * >(ptr);
96   }
97   else
98   {
99     throw InvalidArgumentException(HERE) << "getOutputMesh() does not return a Mesh";
100   }
101 }
102 
103 /* Virtual constructor */
clone() const104 PythonPointToFieldFunction * PythonPointToFieldFunction::clone() const
105 {
106   return new PythonPointToFieldFunction(*this);
107 }
108 
109 /* Copy constructor */
PythonPointToFieldFunction(const PythonPointToFieldFunction & other)110 PythonPointToFieldFunction::PythonPointToFieldFunction(const PythonPointToFieldFunction & other)
111   : PointToFieldFunctionImplementation(other)
112   , pyObj_()
113 {
114   ScopedPyObjectPointer pyObjClone(deepCopy(other.pyObj_));
115   pyObj_ = pyObjClone.get();
116   Py_XINCREF(pyObj_);
117 }
118 
119 /* Copy assignment operator */
operator =(const PythonPointToFieldFunction & rhs)120 PythonPointToFieldFunction & PythonPointToFieldFunction::operator=(const PythonPointToFieldFunction & rhs)
121 {
122   if (this != & rhs)
123   {
124     PointToFieldFunctionImplementation::operator=(rhs);
125     ScopedPyObjectPointer pyObjClone(deepCopy(rhs.pyObj_));
126     pyObj_ = pyObjClone.get();
127     Py_XINCREF(pyObj_);
128   }
129   return *this;
130 }
131 
132 /* Destructor */
~PythonPointToFieldFunction()133 PythonPointToFieldFunction::~PythonPointToFieldFunction()
134 {
135   Py_XDECREF(pyObj_);
136 }
137 
138 /* Comparison operator */
operator ==(const PythonPointToFieldFunction &) const139 Bool PythonPointToFieldFunction::operator ==(const PythonPointToFieldFunction & ) const
140 {
141   return true;
142 }
143 
144 /* String converter */
__repr__() const145 String PythonPointToFieldFunction::__repr__() const
146 {
147   OSS oss;
148   oss << "class=" << PythonPointToFieldFunction::GetClassName()
149       << " name=" << getName()
150       << " input description=" << getInputDescription()
151       << " output description=" << getOutputDescription();
152   return oss;
153 }
154 
155 /* String converter */
__str__(const String &) const156 String PythonPointToFieldFunction::__str__(const String & ) const
157 {
158   OSS oss;
159   oss << "class=" << PythonPointToFieldFunction::GetClassName()
160       << " name=" << getName();
161   return oss;
162 }
163 
164 
165 
166 /* Here is the interface that all derived class must implement */
167 
168 /* Operator () */
operator ()(const Point & inP) const169 Sample PythonPointToFieldFunction::operator() (const Point & inP) const
170 {
171   const UnsignedInteger inputDimension = getInputDimension();
172   if (inputDimension != inP.getDimension())
173     throw InvalidDimensionException(HERE) << "Input point has incorrect dimension. Got " << inP.getDimension() << ". Expected " << getInputDimension();
174 
175   callsNumber_.increment();
176 
177   ScopedPyObjectPointer pyInP(SWIG_NewPointerObj(new OT::Point(inP), SWIG_TypeQuery("OT::Point *"), SWIG_POINTER_OWN | 0));
178   ScopedPyObjectPointer result(PyObject_CallFunctionObjArgs(pyObj_, pyInP.get(), NULL));
179   if (result.isNull())
180   {
181     handleException();
182   }
183 
184   Sample outF;
185   try
186   {
187     outF = convert< _PySequence_, Sample >(result.get());
188   }
189   catch (InvalidArgumentException &)
190   {
191     throw InvalidArgumentException(HERE) << "Output value for " << getName() << "._exec_sample() method is not a 2d-sequence object";
192   }
193 
194   const UnsignedInteger outputSize = getOutputMesh().getVerticesNumber();
195 
196   if (outF.getSize() != outputSize)
197     throw InvalidArgumentException(HERE) << "Python Field function returned a sequence object with incorrect size (got "
198                                          << outF.getSize() << ", expected " << outputSize << ")";
199   if (outF.getDimension() != getOutputDimension())
200     throw InvalidArgumentException(HERE) << "Python Field function returned a sequence object with incorrect dimension (got "
201                                          << outF.getDimension() << ", expected " << getOutputDimension() << ")";
202   outF.setDescription(getOutputDescription());
203   return outF;
204 }
205 
206 /* Accessor for input point dimension */
getInputDimension() const207 UnsignedInteger PythonPointToFieldFunction::getInputDimension() const
208 {
209   ScopedPyObjectPointer result(PyObject_CallMethod ( pyObj_,
210                                const_cast<char *>("getInputDimension"),
211                                const_cast<char *>("()")));
212   UnsignedInteger dim = convert< _PyInt_, UnsignedInteger >(result.get());
213   return dim;
214 }
215 
216 
217 /* Accessor for output point dimension */
getOutputDimension() const218 UnsignedInteger PythonPointToFieldFunction::getOutputDimension() const
219 {
220   ScopedPyObjectPointer result(PyObject_CallMethod (pyObj_,
221                                const_cast<char *>("getOutputDimension"),
222                                const_cast<char *>("()")));
223   UnsignedInteger dim = convert< _PyInt_, UnsignedInteger >(result.get());
224   return dim;
225 }
226 
227 
228 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const229 void PythonPointToFieldFunction::save(Advocate & adv) const
230 {
231   PointToFieldFunctionImplementation::save( adv );
232 
233   pickleSave(adv, pyObj_);
234 }
235 
236 
237 /* Method save() reloads the object from the StorageManager */
load(Advocate & adv)238 void PythonPointToFieldFunction::load(Advocate & adv)
239 {
240   PointToFieldFunctionImplementation::load( adv );
241 
242   pickleLoad(adv, pyObj_);
243 }
244 
245 
246 END_NAMESPACE_OPENTURNS
247