pb11_wrapper.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. #include <string>
  2. #include "nmie.hpp"
  3. #include "nmie-basic.hpp"
  4. #include "nmie-nearfield.hpp"
  5. #include <pybind11/pybind11.h>
  6. #include <pybind11/numpy.h>
  7. #include <pybind11/complex.h>
  8. namespace py = pybind11;
  9. template <typename T>
  10. std::vector<T> Py2Vector(const py::array_t<T> &py_x) {
  11. std::vector<T> c_x(py_x.size());
  12. std::memcpy(c_x.data(), py_x.data(), py_x.size()*sizeof(T));
  13. return c_x;
  14. }
  15. // https://github.com/pybind/pybind11/issues/1042#issuecomment-508582847
  16. //template <typename Sequence>
  17. //inline py::array_t<typename Sequence::value_type> Vector2Py(Sequence&& seq) {
  18. // // Move entire object to heap (Ensure is moveable!). Memory handled via Python capsule
  19. // Sequence* seq_ptr = new Sequence(std::move(seq));
  20. // auto capsule = py::capsule(seq_ptr, [](void* p) { delete reinterpret_cast<Sequence*>(p); });
  21. // return py::array(seq_ptr->size(), // shape of array
  22. // seq_ptr->data(), // c-style contiguous strides for Sequence
  23. // capsule // numpy array references this parent
  24. // );
  25. //}
  26. template <typename outputType>
  27. inline py::array_t<outputType> Vector2Py(const std::vector<outputType>& seq) {
  28. return py::array(seq.size(), seq.data());
  29. }
  30. template <typename inputType=double, typename outputType=double>
  31. py::array_t< std::complex<outputType>> VectorComplex2Py(const std::vector<std::complex<inputType> > &cf_x) {
  32. auto c_x = nmie::ConvertComplexVector<outputType, inputType>(cf_x);
  33. auto py_x = py::array_t< std::complex<outputType>>(c_x.size());
  34. auto py_x_buffer = py_x.request();
  35. auto *py_x_ptr = (std::complex<outputType> *) py_x_buffer.ptr;
  36. std::memcpy(py_x_ptr, c_x.data(), c_x.size()*sizeof(std::complex<outputType>));
  37. return py_x;
  38. }
  39. // https://stackoverflow.com/questions/17294629/merging-flattening-sub-vectors-into-a-single-vector-c-converting-2d-to-1d
  40. template <typename T>
  41. std::vector<T> flatten(const std::vector<std::vector<T>> &v) {
  42. std::size_t total_size = 0;
  43. for (const auto &sub : v)
  44. total_size += sub.size(); // I wish there was a transform_accumulate
  45. std::vector<T> result;
  46. result.reserve(total_size);
  47. for (const auto &sub : v)
  48. result.insert(result.end(), sub.begin(), sub.end());
  49. return result;
  50. }
  51. template <typename T>
  52. py::array Vector2DComplex2Py(const std::vector<std::vector<T > > &x) {
  53. size_t dim1 = x.size();
  54. size_t dim2 = x[0].size();
  55. auto result = flatten(x);
  56. // https://github.com/tdegeus/pybind11_examples/blob/master/04_numpy-2D_cpp-vector/example.cpp
  57. size_t ndim = 2;
  58. std::vector<size_t> shape = {dim1, dim2};
  59. std::vector<size_t> strides = {sizeof(T)*dim2, sizeof(T)};
  60. // return 2-D NumPy array
  61. return py::array(py::buffer_info(
  62. result.data(), /* data as contiguous array */
  63. sizeof(T), /* size of one scalar */
  64. py::format_descriptor<T>::format(), /* data type */
  65. ndim, /* number of dimensions */
  66. shape, /* shape of the matrix */
  67. strides /* strides for each axis */
  68. ));
  69. }
  70. namespace nmie {
  71. template<typename FloatType>
  72. class PyMultiLayerMie : public MultiLayerMie<FloatType> {
  73. public:
  74. template<typename outputType> py::array_t<std::complex<outputType>> GetS1();
  75. template<typename outputType> py::array_t<std::complex<outputType>> GetS2();
  76. template<typename outputType> py::array_t<std::complex<outputType>> GetAn();
  77. template<typename outputType> py::array_t<std::complex<outputType>> GetBn();
  78. template<typename outputType> py::array GetLayerAn();
  79. template<typename outputType> py::array GetLayerBn();
  80. template<typename outputType> py::array GetLayerCn();
  81. template<typename outputType> py::array GetLayerDn();
  82. template<typename outputType> py::array GetFieldE();
  83. template<typename outputType> py::array GetFieldH();
  84. template<typename outputType> py::array_t<outputType> GetFieldEabs();
  85. template<typename outputType> py::array_t<outputType> GetFieldHabs();
  86. template<typename inputType>
  87. void SetLayersSize(const py::array_t<inputType, py::array::c_style | py::array::forcecast> &py_layer_size);
  88. template<typename inputType>
  89. void SetLayersIndex(const py::array_t<std::complex<inputType>, py::array::c_style | py::array::forcecast> &py_index);
  90. template<typename inputType>
  91. void SetAngles(const py::array_t<inputType, py::array::c_style | py::array::forcecast> &py_angles);
  92. void SetFieldCoords(const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Xp,
  93. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Yp,
  94. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Zp);
  95. };
  96. // Python interface
  97. template<typename FloatType>
  98. template<typename inputType>
  99. void PyMultiLayerMie<FloatType>::SetLayersSize(
  100. const py::array_t<inputType, py::array::c_style | py::array::forcecast> &py_layer_size) {
  101. auto layer_size_dp = Py2Vector<inputType>(py_layer_size);
  102. this->MultiLayerMie<FloatType>::SetLayersSize(ConvertVector<FloatType>(layer_size_dp));
  103. }
  104. template<typename FloatType>
  105. template<typename inputType>
  106. void PyMultiLayerMie<FloatType>::SetLayersIndex(
  107. const py::array_t<std::complex<inputType>, py::array::c_style | py::array::forcecast> &py_index) {
  108. auto index_dp = Py2Vector<std::complex<inputType> >(py_index);
  109. this->MultiLayerMie<FloatType>::SetLayersIndex(ConvertComplexVector<FloatType>(index_dp));
  110. }
  111. template<typename FloatType>
  112. template<typename inputType>
  113. void PyMultiLayerMie<FloatType>::SetAngles(const py::array_t<inputType,
  114. py::array::c_style | py::array::forcecast> &py_angles) {
  115. auto angles_dp = Py2Vector<inputType>(py_angles);
  116. this->MultiLayerMie<FloatType>::SetAngles(ConvertVector<FloatType>(angles_dp));
  117. }
  118. template<typename FloatType>
  119. template<typename outputType>
  120. py::array_t<std::complex<outputType>> PyMultiLayerMie<FloatType>::GetS1() {
  121. return VectorComplex2Py<FloatType, outputType>(
  122. this->MultiLayerMie<FloatType>::GetS1()
  123. );
  124. }
  125. template<typename FloatType>
  126. template<typename outputType>
  127. py::array_t<std::complex<outputType>> PyMultiLayerMie<FloatType>::GetS2() {
  128. return VectorComplex2Py<FloatType, outputType>(
  129. this->MultiLayerMie<FloatType>::GetS2()
  130. );
  131. }
  132. template <typename FloatType> template <typename outputType>
  133. py::array_t<outputType> PyMultiLayerMie<FloatType>::GetFieldEabs() {
  134. return Vector2Py(ConvertVector<double>(
  135. this->MultiLayerMie<FloatType>::GetFieldEabs()
  136. ));
  137. }
  138. template <typename FloatType> template <typename outputType>
  139. py::array_t<outputType> PyMultiLayerMie<FloatType>::GetFieldHabs() {
  140. return Vector2Py(ConvertVector<double>(
  141. this->MultiLayerMie<FloatType>::GetFieldHabs()
  142. ));
  143. }
  144. template<typename FloatType>
  145. template<typename outputType>
  146. py::array_t<std::complex<outputType>> PyMultiLayerMie<FloatType>::GetAn() {
  147. return VectorComplex2Py<FloatType, outputType>(
  148. this->MultiLayerMie<FloatType>::GetAn()
  149. );
  150. }
  151. template<typename FloatType>
  152. template<typename outputType>
  153. py::array_t<std::complex<outputType>> PyMultiLayerMie<FloatType>::GetBn() {
  154. return VectorComplex2Py<FloatType, outputType>(
  155. this->MultiLayerMie<FloatType>::GetBn()
  156. );
  157. }
  158. template<typename FloatType>
  159. template<typename outputType>
  160. py::array PyMultiLayerMie<FloatType>::GetFieldE() {
  161. return Vector2DComplex2Py<std::complex<outputType> >(
  162. ConvertComplexVectorVector<outputType>(
  163. this->MultiLayerMie<FloatType>::GetFieldE()
  164. )
  165. );
  166. }
  167. template<typename FloatType>
  168. template<typename outputType>
  169. py::array PyMultiLayerMie<FloatType>::GetFieldH() {
  170. return Vector2DComplex2Py<std::complex<outputType> >(
  171. ConvertComplexVectorVector<outputType>(
  172. this->MultiLayerMie<FloatType>::GetFieldH()
  173. )
  174. );
  175. }
  176. template<typename FloatType>
  177. template<typename outputType>
  178. py::array PyMultiLayerMie<FloatType>::GetLayerAn() {
  179. return Vector2DComplex2Py<std::complex<outputType> >(
  180. ConvertComplexVectorVector<outputType>(
  181. this->MultiLayerMie<FloatType>::GetLayerAn()
  182. )
  183. );
  184. }
  185. template<typename FloatType>
  186. template<typename outputType>
  187. py::array PyMultiLayerMie<FloatType>::GetLayerBn() {
  188. return Vector2DComplex2Py<std::complex<outputType> >(
  189. ConvertComplexVectorVector<outputType>(
  190. this->MultiLayerMie<FloatType>::GetLayerBn()
  191. )
  192. );
  193. }
  194. template<typename FloatType>
  195. template<typename outputType>
  196. py::array PyMultiLayerMie<FloatType>::GetLayerCn() {
  197. return Vector2DComplex2Py<std::complex<outputType> >(
  198. ConvertComplexVectorVector<outputType>(
  199. this->MultiLayerMie<FloatType>::GetLayerCn()
  200. )
  201. );
  202. }
  203. template<typename FloatType>
  204. template<typename outputType>
  205. py::array PyMultiLayerMie<FloatType>::GetLayerDn() {
  206. return Vector2DComplex2Py<std::complex<outputType> >(
  207. ConvertComplexVectorVector<outputType>(
  208. this->MultiLayerMie<FloatType>::GetLayerDn()
  209. )
  210. );
  211. }
  212. template<typename FloatType>
  213. void PyMultiLayerMie<FloatType>::SetFieldCoords(
  214. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Xp,
  215. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Yp,
  216. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Zp) {
  217. auto c_Xp = Py2Vector<double>(py_Xp);
  218. auto c_Yp = Py2Vector<double>(py_Yp);
  219. auto c_Zp = Py2Vector<double>(py_Zp);
  220. this->MultiLayerMie<FloatType>::SetFieldCoords({ConvertVector<FloatType>(c_Xp),
  221. ConvertVector<FloatType>(c_Yp),
  222. ConvertVector<FloatType>(c_Zp)});
  223. }
  224. }
  225. template<typename T>
  226. void declare_nmie(py::module &m, const std::string &typestr) {
  227. using mie_typed = nmie::PyMultiLayerMie<nmie::FloatType>;
  228. std::string pyclass_name = std::string("mie") + typestr;
  229. py::class_<mie_typed>(m, pyclass_name.c_str(), py::buffer_protocol(), py::dynamic_attr())
  230. .def(py::init<>())
  231. .def("GetPECLayer", &mie_typed::GetPECLayer)
  232. .def("SetPECLayer", &mie_typed::SetPECLayer)
  233. .def("SetMaxTerms", &mie_typed::SetMaxTerms)
  234. .def("GetMaxTerms", &mie_typed::GetMaxTerms)
  235. .def("SetModeNmaxAndType", &mie_typed::SetModeNmaxAndType)
  236. .def("RunMieCalculation", &mie_typed::RunMieCalculation)
  237. .def("calcScattCoeffs", &mie_typed::calcScattCoeffs)
  238. .def("calcExpanCoeffs", &mie_typed::calcExpanCoeffs)
  239. .def("RunFieldCalculation", &mie_typed::RunFieldCalculation)
  240. .def("RunFieldCalculationPolar", &mie_typed::RunFieldCalculationPolar)
  241. .def("GetPECLayer", &mie_typed::GetPECLayer)
  242. .def("SetLayersSize", static_cast<
  243. void (mie_typed::*)
  244. (const py::array_t<double, py::array::c_style | py::array::forcecast>&)>
  245. (&mie_typed::SetLayersSize)
  246. )
  247. .def("SetLayersIndex", static_cast<
  248. void (mie_typed::*)
  249. (const py::array_t<std::complex<double>, py::array::c_style | py::array::forcecast> &)>
  250. (&mie_typed::SetLayersIndex)
  251. )
  252. .def("SetAngles", static_cast<
  253. void (mie_typed::*)
  254. (const py::array_t<double, py::array::c_style | py::array::forcecast>&)>
  255. (&mie_typed::SetAngles)
  256. )
  257. .def("SetFieldCoords", static_cast<
  258. void (mie_typed::*)
  259. (
  260. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Xp,
  261. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Yp,
  262. const py::array_t<double, py::array::c_style | py::array::forcecast> &py_Zp
  263. )>
  264. (&mie_typed::SetFieldCoords)
  265. )
  266. .def("GetQext", &mie_typed::GetQext<double>)
  267. .def("GetQsca", &mie_typed::GetQsca<double>)
  268. .def("GetQabs", &mie_typed::GetQabs<double>)
  269. .def("GetQpr", &mie_typed::GetQpr<double>)
  270. .def("GetQbk", &mie_typed::GetQbk<double>)
  271. .def("GetAsymmetryFactor", &mie_typed::GetAsymmetryFactor<double>)
  272. .def("GetAlbedo", &mie_typed::GetAlbedo<double>)
  273. .def("GetS1", &mie_typed::GetS1<double>)
  274. .def("GetS2", &mie_typed::GetS2<double>)
  275. .def("GetAn", &mie_typed::GetAn<double>)
  276. .def("GetBn", &mie_typed::GetBn<double>)
  277. .def("GetFieldE", &mie_typed::GetFieldE<double>)
  278. .def("GetFieldH", &mie_typed::GetFieldH<double>)
  279. .def("GetFieldEabs", &mie_typed::GetFieldEabs<double>)
  280. .def("GetFieldHabs", &mie_typed::GetFieldHabs<double>)
  281. .def("GetFieldConvergence", &mie_typed::GetFieldConvergence)
  282. .def("GetLayerAn", &mie_typed::GetLayerAn<double>)
  283. .def("GetLayerBn", &mie_typed::GetLayerBn<double>)
  284. .def("GetLayerCn", &mie_typed::GetLayerCn<double>)
  285. .def("GetLayerDn", &mie_typed::GetLayerDn<double>)
  286. ;
  287. }
  288. // wrap as Python module
  289. #ifdef MULTI_PRECISION
  290. std::string precision_name = "_mp";
  291. PYBIND11_MODULE(scattnlay_mp, m)
  292. #else
  293. std::string precision_name = "_dp";
  294. PYBIND11_MODULE(scattnlay_dp, m)
  295. #endif // MULTI_PRECISION
  296. {
  297. m.doc() = "The Python version of scattnlay";
  298. declare_nmie<nmie::FloatType>(m, precision_name);
  299. }