Dune-Fufem 2.11-git
Loading...
Searching...
No Matches
conversion.hh
Go to the documentation of this file.
1// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set ts=8 sw=4 et sts=4:
3
4// SPDX-FileCopyrightText: Copyright © DUNE-FUFEM Project contributors, see file AUTHORS.md
5// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later
6
7#ifndef DUNE_FUFEM_PYTHON_CONVERSION_HH
8#define DUNE_FUFEM_PYTHON_CONVERSION_HH
9
10// Only introduce the dune-python interface if python
11// was found and enabled.
12#if HAVE_PYTHON || DOXYGEN
13
14#include <Python.h>
15
16#include <memory>
17#include <string>
18#include <sstream>
19#include <vector>
20#include <array>
21#include <map>
22
31
33
34#include <dune/istl/bvector.hh>
35
36#include <dune/grid/common/gridfactory.hh>
37
39
43
44namespace Impl
45{
46
47template<int dim, int dimworld = dim, class ctype = double>
48class BoundarySegmentFromCallable
49 : public Dune::BoundarySegment<dim, dimworld, ctype>
50{
51 using Domain = Dune::FieldVector<ctype, dim-1>;
53
54public:
55 BoundarySegmentFromCallable(std::function<Range(Domain)> f)
56 : f_(f)
57 {}
58
59 Range operator()(const Domain& x) const
60 {
61 return f_(x);
62 }
63
64private:
65 std::function<Range(Domain)> f_;
66};
67
68}
69
70namespace Python
71{
72
73// *****************************************************************************
74// specializations of Conversion that do the PyObject* <-> C++-type conversion
75// *****************************************************************************
76
77// conversion of double
78template<>
79struct Conversion<double>
80{
81 enum {useDefaultConstructorConversion=true};
82 static void toC(PyObject* x, double& y) { y = PyFloat_AsDouble(x); }
83 static PyObject* toPy(const double& x) { return PyFloat_FromDouble(x); }
84};
85
86// conversion of int
87template<>
88struct Conversion<int>
89{
90 enum {useDefaultConstructorConversion=true};
91
92#if PY_MAJOR_VERSION >= 3
93 static void toC(PyObject* x, int& y) { y = PyLong_AsLong(x); }
94 static PyObject* toPy(const int& x) { return PyLong_FromLong(x); }
95#else
96 static void toC(PyObject* x, int& y) { y = PyInt_AsLong(x); }
97 static PyObject* toPy(const int& x) { return PyInt_FromLong(x); }
98#endif
99};
100
101// conversion of long
102template<>
103struct Conversion<long>
104{
105 enum {useDefaultConstructorConversion=true};
106
107 static void toC(PyObject* x, long& y) { y = PyLong_AsLong(x); }
108 static PyObject* toPy(const long& x) { return PyLong_FromLong(x); }
109};
110
111// conversion of bool
112template<>
113struct Conversion<bool>
114{
115 enum {useDefaultConstructorConversion=true};
116 static void toC(PyObject* x, bool& y) {
117 int i = PyObject_IsTrue(x);
118 if (i==-1)
119 handlePythonError("Conversion<bool>::toC()", "cannot interpret python object as bool");
120 y = i;
121 }
122 static PyObject* toPy(const bool& x) { return PyBool_FromLong(x); }
123};
124
125// conversion of unsigned int
126template<>
127struct Conversion<unsigned int>
128{
129 enum {useDefaultConstructorConversion=true};
130#if PY_MAJOR_VERSION >= 3
131 static void toC(PyObject* x, unsigned int& y) { y = PyLong_AsUnsignedLongMask(x); }
132 static PyObject* toPy(const unsigned int& x) { return PyLong_FromLong(x); }
133#else
134 static void toC(PyObject* x, unsigned int& y) { y = PyInt_AsUnsignedLongMask(x); }
135 static PyObject* toPy(const unsigned int& x) { return PyInt_FromLong(x); }
136#endif
137};
138
139// conversion of unsigned int
140template<>
141struct Conversion<unsigned long>
142{
143 enum {useDefaultConstructorConversion=true};
144#if PY_MAJOR_VERSION >= 3
145 static void toC(PyObject* x, unsigned long& y) { y = PyLong_AsUnsignedLongMask(x); }
146 static PyObject* toPy(const unsigned long& x) { return PyLong_FromLong(x); }
147#else
148 static void toC(PyObject* x, unsigned long& y) { y = PyInt_AsUnsignedLongMask(x); }
149 static PyObject* toPy(const unsigned long& x) { return PyInt_FromLong(x); }
150#endif
151};
152
153// conversion of string
154template<>
155struct Conversion<std::string>
156{
157 enum {useDefaultConstructorConversion=true};
158
159#if PY_MAJOR_VERSION >= 3
160 static void toC(PyObject* x, std::string& y) { y = PyUnicode_AsUTF8(x); }
161 static PyObject* toPy(const std::string& x) { return Imp::inc(PyUnicode_FromString(x.c_str())); }
162#else
163 static void toC(PyObject* x, std::string& y) { y = PyString_AsString(x); }
164 static PyObject* toPy(const std::string& x) { return PyString_FromString(x.c_str()); }
165#endif
166};
167
168// conversion of raw char[]
169template<int i>
170struct Conversion<char[i]>
171{
172 static void toC(PyObject* x, char y[i])
173 {
174 DUNE_THROW(Dune::Exception, "Conversion to C type char[] not implemented");
175 }
176
177#if PY_MAJOR_VERSION >= 3
178 static PyObject* toPy(const char x[i]) { return Imp::inc(PyUnicode_FromString(x)); }
179#else
180 static PyObject* toPy(const char x[i]) { return PyString_FromString(x); }
181#endif
182};
183
184// conversion to shared_ptr
185template<class P>
186struct Conversion<std::shared_ptr<P> >
187{
188 enum {useDefaultConstructorConversion=true};
189 static void toC(PyObject* o, std::shared_ptr<P>& p)
190 {
191 P* rawP;
192 Conversion<P*>::toC(o, rawP);
193 p = std::shared_ptr<P>(rawP);
194 }
195 static PyObject* toPy(const std::shared_ptr<P>& x)
196 {
197 DUNE_THROW(Dune::Exception, "Conversion from C type std::shared_ptr<" << typeid(P).name() << "> not implemented");
198 return 0;
199 }
200};
201
202// conversion of vector
203template<class T>
204struct Conversion<std::vector<T> >
205{
206 enum {useDefaultConstructorConversion=true};
207 static void toC(PyObject* list, std::vector<T>& v)
208 {
209 if (not PySequence_Check(list))
210 DUNE_THROW(Dune::Exception, "cannot convert a non-sequence to a std::vector");
211 int size = PySequence_Size(list);
212 v.resize(size);
213 for(int i=0; i<size; ++i)
214 Reference(PySequence_GetItem(list, i)).toC(v[i]);
215 // The above is OK since PySequence_GetItem returns a new reference.
216 // With PyList_GetItem we would need one of the following since it
217 // returns a borrowed reference:
218 // Conversion<T>::toC(PyList_GetItem(list, i), v[i]);
219 // Reference(Imp::inc(PyList_GetItem(list, i))).toC(v[i]);
220 }
221 static PyObject* toPy(const std::vector<T>& v)
222 {
223 PyObject* list = PyList_New(v.size());
224 for(size_t i=0; i<v.size(); ++i)
225 PyList_SetItem(list, i, Imp::inc(makeObject(v[i])));
226 // We have to use inc() since PyList_SetItem STEALS a reference!
227 return list;
228 }
229};
230
231// conversion to dict to map
232template<class K, class V, class C, class A>
233struct Conversion<std::map<K, V, C, A> >
234{
235 enum {useDefaultConstructorConversion=true};
236 static void toC(PyObject* dict, std::map<K, V, C, A>& map)
237 {
238 if (not PyDict_Check(dict))
239 DUNE_THROW(Dune::Exception, "cannot convert a non-dictionary to a std::map");
240 PyObject* key;
241 PyObject* value;
242 Py_ssize_t pos = 0;
243 while (PyDict_Next(dict, &pos, &key, &value))
244 {
245 K cKey;
246 V cValue;
247 Reference(Imp::inc(key)).toC(cKey);
248 Reference(Imp::inc(value)).toC(cValue);
249 map[cKey] = cValue;
250 }
251 }
252
253 static PyObject* toPy(const std::map<K, V, C, A>& map)
254 {
255 DUNE_THROW(Dune::Exception, "Conversion from C type std::map<K,V,C,A> not implemented");
256 return 0;
257 }
258};
259
260// conversion of std::array
261template<class T, std::size_t n>
262struct Conversion<std::array<T,n>>
263{
264 enum {useDefaultConstructorConversion=true};
265 static void toC(PyObject* list, std::array<T,n>& v)
266 {
267 if (not PySequence_Check(list))
268 {
269 if (n!=1)
270 DUNE_THROW(Dune::Exception, "cannot convert a non-sequence to a std::array<T,n> with n>1");
271
272 Reference(Imp::inc(list)).toC(v[0]);
273 }
274 else
275 {
276 if (PySequence_Size(list)!=n)
277 DUNE_THROW(Dune::Exception, "cannot convert a sequence of size " << PySequence_Size(list) << " to a std::array of size " << n);
278
279 for(size_t i=0; i<n; ++i)
280 Reference(PySequence_GetItem(list, i)).toC(v[i]);
281 // The above is OK since PySequence_GetItem returns a new reference.
282 // With PyTuple_GetItem we would need one of the following since it
283 // returns a borrowed reference:
284 // Conversion<T>::toC(PyTuple_GetItem(list, i), v[i]);
285 // Reference(Imp::inc(PyTuple_GetItem(list, i))).toC(v[i]);
286 }
287 }
288
289 static PyObject* toPy(const std::array<T,n>& v)
290 {
291 PyObject* tuple = PyTuple_New(n);
292 for(int i=0; i<n; ++i)
293 PyTuple_SetItem(tuple, i, Imp::inc(makeObject(v[i])));
294 // We have to use inc() since PyList_SetItem STEALS a reference!
295 return tuple;
296 }
297};
298
299// conversion of FieldVector
300template<class T, int n>
301struct Conversion<Dune::FieldVector<T,n> >
302{
303 enum {useDefaultConstructorConversion=true};
304 static void toC(PyObject* list, Dune::FieldVector<T,n>& v)
305 {
306 if (not PySequence_Check(list))
307 {
308 if (n!=1)
309 DUNE_THROW(Dune::Exception, "cannot convert a non-sequence to a Dune::FieldVector<T,n> with n>1");
310
311 Reference(Imp::inc(list)).toC(v[0]);
312 }
313 else
314 {
315 if (PySequence_Size(list)!=n)
316 DUNE_THROW(Dune::Exception, "cannot convert a sequence of size " << PySequence_Size(list) << " to a Dune::FieldVector of size " << n);
317
318 for(size_t i=0; i<n; ++i)
319 Reference(PySequence_GetItem(list, i)).toC(v[i]);
320 // The above is OK since PySequence_GetItem returns a new reference.
321 // With PyTuple_GetItem we would need one of the following since it
322 // returns a borrowed reference:
323 // Conversion<T>::toC(PyTuple_GetItem(list, i), v[i]);
324 // Reference(Imp::inc(PyTuple_GetItem(list, i))).toC(v[i]);
325 }
326 }
327
328 static PyObject* toPy(const Dune::FieldVector<T,n>& v)
329 {
330 PyObject* tuple = PyTuple_New(n);
331 for(int i=0; i<n; ++i)
332 PyTuple_SetItem(tuple, i, Imp::inc(makeObject(v[i])));
333 // We have to use inc() since PyList_SetItem STEALS a reference!
334 return tuple;
335 }
336};
337
338// conversion of FieldMatrix
339template<class T, int n, int m>
340struct Conversion<Dune::FieldMatrix<T,n,m> >
341{
342 enum {useDefaultConstructorConversion=true};
343 static void toC(PyObject* list, Dune::FieldMatrix<T,n,m>& v)
344 {
345 if (not PySequence_Check(list))
346 {
347 if (n!=1)
348 DUNE_THROW(Dune::Exception, "cannot convert a non-sequence to a Dune::FieldMatrix<T,n,m> with n>1");
349
350 Reference(Imp::inc(list)).toC(v[0]);
351 }
352 else
353 {
354 if (PySequence_Size(list)!=n)
355 DUNE_THROW(Dune::Exception, "cannot convert a sequence to a Dune::FieldMatrix<T,n,m> with a different size");
356
357 for(size_t i=0; i<n; ++i)
358 Reference(PySequence_GetItem(list, i)).toC(v[i]);
359 // The above is OK since PySequence_GetItem returns a new reference.
360 // With PyTuple_GetItem we would need one of the following since it
361 // returns a borrowed reference:
362 // Conversion<T>::toC(PyTuple_GetItem(list, i), v[i]);
363 // Reference(Imp::inc(PyTuple_GetItem(list, i))).toC(v[i]);
364 }
365 }
366
367 static PyObject* toPy(const Dune::FieldMatrix<T,n,m>& v)
368 {
369 PyObject* tuple = PyTuple_New(n);
370 for(int i=0; i<n; ++i)
371 PyTuple_SetItem(tuple, i, Imp::inc(makeObject(v[i])));
372 // We have to use inc() since PyList_SetItem STEALS a reference!
373 return tuple;
374 }
375};
376
377// conversion of BlockVector
378template<class T>
379struct Conversion<Dune::BlockVector<T> >
380{
381 enum {useDefaultConstructorConversion=true};
382 static void toC(PyObject* list, Dune::BlockVector<T>& v)
383 {
384 if (not PySequence_Check(list))
385 DUNE_THROW(Dune::Exception, "cannot convert a non-sequence to a Dune::BlockVector");
386 int size = PySequence_Size(list);
387 v.resize(size);
388 for(int i=0; i<size; ++i)
389 Reference(PySequence_GetItem(list, i)).toC(v[i]);
390 // The above is OK since PySequence_GetItem returns a new reference.
391 // With PyList_GetItem we would need one of the following since it
392 // returns a borrowed reference:
393 // Conversion<T>::toC(PyList_GetItem(list, i), v[i]);
394 // Reference(Imp::inc(PyList_GetItem(list, i))).toC(v[i]);
395 }
396 static PyObject* toPy(const Dune::BlockVector<T>& v)
397 {
398 PyObject* list = PyList_New(v.size());
399 for(size_t i=0; i<v.size(); ++i)
400 PyList_SetItem(list, i, Imp::inc(makeObject(v[i])));
401 // We have to use inc() since PyList_SetItem STEALS a reference!
402 return list;
403 }
404};
405
406
407// conversion of FieldVector
408template<class... T>
409struct Conversion<Dune::TupleVector<T...> >
410{
411 enum {useDefaultConstructorConversion=true};
412 static void toC(PyObject* list, Dune::TupleVector<T...>& v)
413 {
414 static constexpr auto n = Dune::index_constant<sizeof...(T)>{};
415 if (not PySequence_Check(list))
416 DUNE_THROW(Dune::Exception, "cannot convert a non-sequence to a Dune::TupleVector<T...>");
417 else
418 {
419 if ((std::size_t)PySequence_Size(list)!=v.size())
420 DUNE_THROW(Dune::Exception, "cannot convert a sequence of size " << PySequence_Size(list) << " to a Dune::TupleVector of size " << v.size());
421
422 Dune::Hybrid::forEach(Dune::range(n), [&](auto i) {
423 Reference(PySequence_GetItem(list, i)).toC(v[i]);
424 });
425 // The above is OK since PySequence_GetItem returns a new reference.
426 // With PyTuple_GetItem we would need one of the following since it
427 // returns a borrowed reference:
428 // Conversion<T>::toC(PySequence_GetItem(list, i), v[i]);
429 // Reference(Imp::inc(PySequence_GetItem(list, i))).toC(v[i]);
430 }
431 }
432
433 static PyObject* toPy(const Dune::TupleVector<T...>& v)
434 {
435 static constexpr auto n = Dune::index_constant<sizeof...(T)>{};
436 PyObject* tuple = PyTuple_New(n);
437 Dune::Hybrid::forEach(Dune::range(n), [&](auto i) {
438 PyTuple_SetItem(tuple, i, Imp::inc(makeObject(v[i])));
439 });
440 // We have to use inc() since PyTuple_SetItem STEALS a reference!
441 return tuple;
442 }
443};
444
445
446// conversion to dict to ParameterTree
447template<>
448struct Conversion<Dune::ParameterTree>
449{
450 enum {useDefaultConstructorConversion=true};
451 static void toC(PyObject* dict, Dune::ParameterTree& param)
452 {
453 if (not PyDict_Check(dict))
454 DUNE_THROW(Dune::Exception, "cannot convert a non-dictionary to a Dune::ParameterTree");
455 PyObject* key;
456 PyObject* value;
457 Py_ssize_t pos = 0;
458 while (PyDict_Next(dict, &pos, &key, &value))
459 {
460 std::string cKey = Reference(Imp::inc(key)).str();
461
462 if (PyDict_Check(value))
463 Reference(Imp::inc(value)).toC(param.sub(cKey));
464 else
465 param[cKey] = Reference(Imp::inc(value)).str();
466 }
467 }
468
469 static PyObject* toPy(const Dune::ParameterTree& map)
470 {
471 DUNE_THROW(Dune::Exception, "Conversion from C type Dune::ParameterTree not implemented");
472 return 0;
473 }
474};
475
476template<class Domain, class Range>
477struct Conversion<std::function<Range(Domain)>>
478{
479 enum {useDefaultConstructorConversion=true};
480 static void toC(PyObject* pyF, std::function<Range(Domain)>& f)
481 {
482 f = Python::makeFunction<Range(Domain)>(Imp::inc(pyF));
483 }
484};
485
486template<int dim, int dimworld, class ctype>
487struct Conversion<Dune::BoundarySegment<dim, dimworld, ctype>*>
488{
489 enum {useDefaultConstructorConversion=true};
490 static void toC(PyObject* pyF, Dune::BoundarySegment<dim, dimworld, ctype>*& segmentPointer)
491 {
493
494 using Segment = Impl::BoundarySegmentFromCallable<dim, dimworld, ctype>;
495
496 segmentPointer = new Segment(make_function<Range>(Imp::inc(pyF)));
497 }
498};
499
500
517template<class GridType>
518struct Conversion<Dune::GridFactory<GridType> >
519{
520 enum {useDefaultConstructorConversion=true};
521
522 static void toC(PyObject* pyGridRaw, Dune::GridFactory<GridType>& gridFactory)
523 {
524 const int dim = GridType::dimension;
525 const int dimworld = GridType::dimensionworld;
526
528 typedef typename std::vector<unsigned int> Element;
529 typedef typename std::vector<unsigned int> Face;
531 typedef std::shared_ptr<Segment> SegmentPointer;
532
533 Reference grid(Imp::inc(pyGridRaw));
534
535 Reference vertexIt = iter(grid.get("vertices"));
536 for(Reference vertex = next(vertexIt); vertex; vertex = next(vertexIt))
537 gridFactory.insertVertex(vertex.toC<Vertex>());
538
539 Reference elementIt = iter(grid.get("elements"));
540 for(Reference element = next(elementIt); element; element = next(elementIt))
541 {
542 auto type = Dune::geometryTypeFromVertexCount(dim, size(element));
543 gridFactory.insertElement(type, element.toC<Element>());
544 }
545
546 if (grid.hasAttr("segments"))
547 {
548 Reference segmentIt = iter(grid.get("segments"));
549 for(Reference segment = next(segmentIt); segment; segment = next(segmentIt))
550 {
551 if (Python::isCallable(segment))
552 gridFactory.insertBoundarySegment(segment.toC<Face>(), segment.toC<SegmentPointer>());
553 else
554 gridFactory.insertBoundarySegment(segment.toC<Face>());
555 }
556 }
557 }
558};
559
560
561} // end of namespace Python
562
563
564
565#else
566 #warning dunepython.hh was included but python was not found or enabled!
567#endif // DUNE_FUFEM_PYTHON_CONVERSION_HH
568
569
570#endif
571
const char * name()
int size() const
static constexpr IntegralRange< std::decay_t< T > > range(T &&from, U &&to) noexcept
constexpr void forEach(Range &&range, F &&f)
size_type dim() const
virtual void operator()()=0
#define DUNE_THROW(E,...)
STL namespace.
Definition callable.hh:34
Reference iter(const Reference &seq)
Get iterator of iterable object.
Definition common.hh:406
bool isCallable(const Reference &ref)
Check if python object is callable.
Definition common.hh:532
void handlePythonError(const std::string &origin, const std::string &message)
If a python error occurred throw an exception and clear python error indicator.
Definition common.hh:589
Reference tuple(const Ts &... ts)
Definition common.hh:370
Reference makeObject(const T &t)
Create python object from C++ object.
Definition common.hh:351
Reference next(const Reference &iter)
Get next item from iterator.
Definition common.hh:421
Reference dict()
Create an empty Python tuple.
Definition common.hh:380
const Grid & grid() const
ALBERTA EL Element
void resize(size_type size)
ParameterTree & sub(const std::string &sub)
static constexpr std::size_t size()
virtual void insertElement(const GeometryType &type, const std::vector< unsigned int > &vertices)
virtual void insertVertex(const FieldVector< ctype, dimworld > &pos)
virtual void insertBoundarySegment(const std::vector< unsigned int > &vertices)
Class representing a face.
Definition facehierarchy.hh:120
static PyObject * toPy(const T &x)
Definition common.hh:72
static void toC(PyObject *x, T &y)
Definition common.hh:67
T c_str(T... args)
T forward(T... args)
T resize(T... args)
T size(T... args)