From a3a5ec52a9add7fcebe6a0f35fff30f647071943 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Thu, 21 Sep 2023 17:07:40 +0200
Subject: [PATCH] + Datafield::npErrors

---
 Device/Data/Datafield.cpp             | 105 +++++++++++++++-----------
 Device/Data/Datafield.h               |   1 +
 auto/Wrap/libBornAgainDevice.py       |   4 +
 auto/Wrap/libBornAgainDevice_wrap.cpp |  34 +++++++++
 4 files changed, 101 insertions(+), 43 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index da0c4953557..13842cb118b 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -19,6 +19,63 @@
 #include "Base/Util/Assert.h"
 #include <algorithm>
 
+#ifdef BORNAGAIN_PYTHON
+
+#include "PyCore/Embed/PyInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
+
+namespace {
+
+PyObject* npExport(const Frame& frame, const std::vector<double>& flatData)
+{
+    if (flatData.empty())
+        return nullptr;
+
+    // TODO: Thoroughly check this function regarding index manipulations
+
+    PyInterpreter::Numpy::initialize();
+
+    ASSERT(frame.rank() <= 2);
+
+    std::vector<size_t> dimensions;
+    for (size_t i = 0; i < frame.rank(); i++)
+        dimensions.push_back(frame.axis(i).size());
+    // for rot90 of 2-dim arrays to conform with numpy
+    if (dimensions.size() == 2)
+        std::swap(dimensions[0], dimensions[1]);
+
+    // creating ndarray objects describing size of dimensions
+    PyObjectPtr pyarray{PyInterpreter::Numpy::arrayND(dimensions)};
+    ASSERT(pyarray.valid());
+
+    // get the pointer to the data buffer of the array (assumed to be C-contiguous)
+    double* data{PyInterpreter::Numpy::getDataPtr(pyarray.get())};
+    ASSERT(data);
+
+    double* array_buffer = data;
+
+    // filling numpy array with output_data
+    if (frame.rank() == 2) {
+        for (size_t i = 0; i < frame.size(); ++i) {
+            std::vector<int> axes_indices = frame.allIndices(i);
+            size_t offset = axes_indices[0]
+                            + frame.axis(0).size() * (frame.axis(1).size() - 1 - axes_indices[1]);
+            array_buffer[offset] = flatData[i];
+        }
+    } else if (frame.rank() == 1) {
+        for (size_t i = 0; i < frame.size(); ++i)
+            *array_buffer++ = flatData[i];
+    } else
+        ASSERT_NEVER;
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return pyarray.release();
+}
+
+} // namespace
+
+#endif // BORNAGAIN_PYTHON
+
+
 Datafield::Datafield(std::string title, const Frame* frame, std::vector<double> values,
                      std::vector<double> errSigmas)
     : m_title(std::move(title))
@@ -224,52 +281,14 @@ Datafield* Datafield::crop(double xmin, double xmax) const
 
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyCore/Embed/PyInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
-
 PyObject* Datafield::npArray() const
 {
-    // TODO: Thoroughly check this function regarding index manipulations
-
-    PyInterpreter::Numpy::initialize();
-
-    ASSERT(rank() <= 2);
-
-    std::vector<size_t> dimensions;
-    for (size_t i = 0; i < rank(); i++)
-        dimensions.push_back(axis(i).size());
-
-    // for rot90 of 2-dim arrays to conform with numpy
-    if (dimensions.size() == 2)
-        std::swap(dimensions[0], dimensions[1]);
-
-    // creating ndarray objects describing size of dimensions
-    PyObjectPtr pyarray{PyInterpreter::Numpy::arrayND(dimensions)};
-    if (!pyarray.valid())
-        return nullptr;
-
-    // get the pointer to the data buffer of the array (assumed to be C-contiguous)
-    double* data{PyInterpreter::Numpy::getDataPtr(pyarray.get())};
-
-    if (!data)
-        return nullptr;
-
-    double* array_buffer = data;
-
-    // filling numpy array with output_data
-    if (rank() == 2) {
-        for (size_t i = 0; i < size(); ++i) {
-            std::vector<int> axes_indices = frame().allIndices(i);
-            size_t offset =
-                axes_indices[0] + axis(0).size() * (axis(1).size() - 1 - axes_indices[1]);
-            array_buffer[offset] = (*this)[i];
-        }
-    } else if (rank() == 1) {
-        for (size_t i = 0; i < size(); ++i)
-            *array_buffer++ = (*this)[i];
-    }
+    return ::npExport(frame(), flatVector());
+}
 
-    // returns a _new_ reference; ie. caller is responsible for the ref-count
-    return pyarray.release();
+PyObject* Datafield::npErrors() const
+{
+    return ::npExport(frame(), errorSigmas());
 }
 
 #endif // BORNAGAIN_PYTHON
diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index b835382576d..739d2a2b892 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -65,6 +65,7 @@ public:
 #ifdef BORNAGAIN_PYTHON
     //! Returns data as Python numpy array.
     PyObject* npArray() const;
+    PyObject* npErrors() const;
 #endif
 
     //... retrieve basic info
diff --git a/auto/Wrap/libBornAgainDevice.py b/auto/Wrap/libBornAgainDevice.py
index 81641c33be0..fcd2b4c12bd 100644
--- a/auto/Wrap/libBornAgainDevice.py
+++ b/auto/Wrap/libBornAgainDevice.py
@@ -2093,6 +2093,10 @@ class Datafield(object):
         r"""npArray(Datafield self) -> PyObject *"""
         return _libBornAgainDevice.Datafield_npArray(self)
 
+    def npErrors(self):
+        r"""npErrors(Datafield self) -> PyObject *"""
+        return _libBornAgainDevice.Datafield_npErrors(self)
+
     def frame(self):
         r"""frame(Datafield self) -> Frame"""
         return _libBornAgainDevice.Datafield_frame(self)
diff --git a/auto/Wrap/libBornAgainDevice_wrap.cpp b/auto/Wrap/libBornAgainDevice_wrap.cpp
index a2b67fac7c3..e8cd0ec2047 100644
--- a/auto/Wrap/libBornAgainDevice_wrap.cpp
+++ b/auto/Wrap/libBornAgainDevice_wrap.cpp
@@ -29712,6 +29712,39 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Datafield_npErrors(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Datafield *arg1 = (Datafield *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  PyObject *result = 0 ;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Datafield, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Datafield_npErrors" "', argument " "1"" of type '" "Datafield const *""'"); 
+  }
+  arg1 = reinterpret_cast< Datafield * >(argp1);
+  {
+    try {
+      result = (PyObject *)((Datafield const *)arg1)->npErrors();
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = result;
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Datafield_frame(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   Datafield *arg1 = (Datafield *) 0 ;
@@ -40895,6 +40928,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "Datafield_setAt", _wrap_Datafield_setAt, METH_VARARGS, "Datafield_setAt(Datafield self, size_t i, double val)"},
 	 { "Datafield_valAt", _wrap_Datafield_valAt, METH_VARARGS, "Datafield_valAt(Datafield self, size_t i) -> double"},
 	 { "Datafield_npArray", _wrap_Datafield_npArray, METH_O, "Datafield_npArray(Datafield self) -> PyObject *"},
+	 { "Datafield_npErrors", _wrap_Datafield_npErrors, METH_O, "Datafield_npErrors(Datafield self) -> PyObject *"},
 	 { "Datafield_frame", _wrap_Datafield_frame, METH_O, "Datafield_frame(Datafield self) -> Frame"},
 	 { "Datafield_rank", _wrap_Datafield_rank, METH_O, "Datafield_rank(Datafield self) -> size_t"},
 	 { "Datafield_axis", _wrap_Datafield_axis, METH_VARARGS, "Datafield_axis(Datafield self, size_t k) -> Scale"},
-- 
GitLab