1 // -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_PYTHON_COMMON_FVECTOR_HH
4 #define DUNE_PYTHON_COMMON_FVECTOR_HH
5 
6 #include <cstddef>
7 
8 #include <algorithm>
9 #include <stdexcept>
10 #include <string>
11 #include <tuple>
12 #include <utility>
13 
14 #include <dune/common/fvector.hh>
15 
16 #include <dune/python/common/typeregistry.hh>
17 #include <dune/python/common/densevector.hh>
18 #include <dune/python/common/string.hh>
19 #include <dune/python/pybind11/pybind11.h>
20 #include <dune/python/pybind11/operators.h>
21 
22 namespace Dune
23 {
24 
25   namespace Python
26   {
27 
28     // to_string
29     // ---------
30 
31     template< class K, int size >
to_string(const FieldVector<K,size> & x)32     inline static std::string to_string ( const FieldVector< K, size > &x )
33     {
34       return "(" + join( ", ", [] ( auto &&x ) { return to_string( x ); }, x.begin(), x.end() ) + ")";
35     }
36 
37 
38 
39     // registerFieldVector
40     // -------------------
41     template< class K, int size, class ...options >
registerFieldVector(pybind11::handle scope,pybind11::class_<Dune::FieldVector<K,size>,options...> cls)42     void registerFieldVector ( pybind11::handle scope, pybind11::class_<Dune::FieldVector<K,size>, options...> cls )
43     {
44       using pybind11::operator""_a;
45 
46       typedef Dune::FieldVector<K, size> FV;
47       cls.def( pybind11::init( [] () { return new FV(); } ) );
48 
49       if( size == 1 )
50       {
51         cls.def( pybind11::init( [] ( int a ) { return new FV( K( a ) ); } ) );
52         cls.def( pybind11::init( [] ( K a ) { return new FV( a ); } ) );
53         cls.def( "__float__", [] ( const FV &self ) { return self[ 0 ]; } );
54         pybind11::implicitly_convertible< int, FV >();
55         pybind11::implicitly_convertible< K, FV >();
56       }
57 
58       cls.def( pybind11::init( [] ( pybind11::buffer x ) {
59           pybind11::buffer_info info = x.request();
60           if( info.format != pybind11::format_descriptor< K >::format() )
61             throw pybind11::value_error( "Incompatible buffer format." );
62           if( info.ndim != 1 )
63             throw pybind11::value_error( "Only one-dimensional buffers can be converted into FieldVector." );
64           const ssize_t stride = info.strides[ 0 ] / sizeof( K );
65           const ssize_t sz = std::min( static_cast< ssize_t >( size ), info.shape[ 0 ] );
66 
67           FV *self = new FV( K( 0 ) );
68           for( ssize_t i = 0; i < sz; ++i )
69             (*self)[ i ] = static_cast< K * >( info.ptr )[ i*stride ];
70           return self;
71         } ), "x"_a );
72 
73       cls.def( pybind11::init( [] ( pybind11::tuple x ) {
74           FV *self = new FV( K( 0 ) );
75           const std::size_t sz = std::min( static_cast< std::size_t >( size ), x.size() );
76           // should this fail in case the sizes do not match?
77           for( std::size_t i = 0; i < sz; ++i )
78             (*self)[ i ] = x[ i ].template cast< K >();
79           return self;
80         } ), "x"_a );
81 
82       cls.def( pybind11::init( [] ( pybind11::list x ) {
83             FV *self = new FV( K( 0 ) );
84             const std::size_t sz = std::min( static_cast< std::size_t >( size ), x.size() );
85             // should this fail in case the sizes do not match?
86             for( std::size_t i = 0; i < sz; ++i )
87               (*self)[ i ] = x[ i ].template cast< K >();
88             return self;
89           } ), "x"_a );
90 
91       cls.def( pybind11::init( [] ( pybind11::args args ) {
92             FV *self = new FV( K( 0 ) );
93             const std::size_t sz = std::min( static_cast< std::size_t >( size ), args.size() );
94             // should this fail in case the sizes do not match?
95             for( std::size_t i = 0; i < sz; ++i )
96               (*self)[ i ] = args[ i ].template cast< K >();
97             return self;
98           } ) );
99 
100       pybind11::implicitly_convertible< pybind11::args, FV >();
101       pybind11::implicitly_convertible< pybind11::buffer, FV >();
102 
103       cls.def("copy", [](FV& , pybind11::args l) {
104             FV v(K(0));
105             const std::size_t sz = std::min(v.size(), l.size());
106             // should this fail in case the sizes do not match?
107             for (std::size_t i = 0; i < sz; ++i)
108               v[i] = l[i].template cast<K>();
109             return v;
110           });
111 
112       cls.def( "__str__", [] ( const FV &self ) { return to_string( self ); } );
113       cls.def( "__repr__", [] ( const FV &self ) {
114           return "Dune::FieldVector<"+to_string(size)+">"+to_string(self);
115           } );
116 
117       cls.def_buffer( [] ( FV &self ) -> pybind11::buffer_info {
118           return pybind11::buffer_info(
119               &self[ 0 ],                                         /* Pointer to buffer */
120               sizeof( K ),                                        /* Size of one scalar */
121               pybind11::format_descriptor< K >::format(),         /* Python struct-style format descriptor */
122               1,                                                  /* Number of dimensions */
123               { size },                                           /* Buffer dimensions */
124               /* Strides (in bytes) for each index */
125               {
126                 static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 1 ] ) - reinterpret_cast< char * >( &self[ 0 ] ) ) } );
127               }
128         );
129 
130       registerDenseVector< FV >( cls );
131     }
132 
133     template< class K, int size >
registerFieldVector(pybind11::handle scope,std::integral_constant<int,size>={} )134     void registerFieldVector ( pybind11::handle scope, std::integral_constant< int, size > = {} )
135     {
136       typedef Dune::FieldVector<K, size> FV;
137 
138       auto entry = insertClass<FV>(scope, "FieldVector_"+std::to_string(size), pybind11::buffer_protocol(),
139         GenerateTypeName("Dune::FieldVector",MetaType<K>(),size),IncludeFiles{"dune/common/fvector.hh"}
140         );
141       if (!entry.second)
142         return;
143       registerFieldVector(scope, entry.first);
144     }
145 
146     template<class K, int... size>
registerFieldVector(pybind11::handle scope,std::integer_sequence<int,size...>)147     void registerFieldVector(pybind11::handle scope, std::integer_sequence<int, size...>)
148     {
149       std::ignore = std::make_tuple((registerFieldVector<K>(scope, std::integral_constant<int, size>()), size)...);
150     }
151 
152   } // namespace Python
153 
154 } // namespace Dune
155 
156 #endif // #ifndef DUNE_PYTHON_COMMON_FVECTOR_HH
157