From 4e3c8e155a7a3dc08815e68bfd6223362f1a8b59 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:56:27 +0100
Subject: [PATCH 01/85] PyCore: The header needed for using Python and Numpy
 C-API

---
 PyTools/Embed/PyCore.h | 57 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 PyTools/Embed/PyCore.h

diff --git a/PyTools/Embed/PyCore.h b/PyTools/Embed/PyCore.h
new file mode 100644
index 00000000000..933c85ea937
--- /dev/null
+++ b/PyTools/Embed/PyCore.h
@@ -0,0 +1,57 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyTools/Embed/PyCore.h
+//! @brief     Includes python header and takes care of warnings.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifdef SWIG
+#error no need to expose this header to Swig
+#endif
+
+#ifndef USER_API
+#ifndef BORNAGAIN_PYTOOLS_PYCORE_H
+#define BORNAGAIN_PYTOOLS_PYCORE_H
+
+#ifdef BORNAGAIN_PYTHON
+#undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
+#ifdef INCLUDE_NUMPY
+
+#ifdef BORNAGAIN_PYTHON_API_SWIG
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` and `NO_IMPORT_ARRAY` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
+#define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
+#define NO_IMPORT_ARRAY
+#endif // BORNAGAIN_PYTHON_API_SWIG
+
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION // to prevent spurious warning
+#include <numpy/arrayobject.h>
+
+#endif // INCLUDE_NUMPY
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#endif  // BORNAGAIN_PYTHON
+#endif  // BORNAGAIN_PYTOOLS_PYCORE_H
+#endif  // USER_API
-- 
GitLab


From 1cf631a52e811890408545a08111596d666aede6 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:56:39 +0100
Subject: [PATCH 02/85] PyObjectDecl: Forward declaration of PyObject

This header is used when a module needs `PyObject*`, or `np_size_t` (Numpy).
---
 PyTools/Embed/PyObjectDecl.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
 create mode 100644 PyTools/Embed/PyObjectDecl.h

diff --git a/PyTools/Embed/PyObjectDecl.h b/PyTools/Embed/PyObjectDecl.h
new file mode 100644
index 00000000000..8fdd48f8d38
--- /dev/null
+++ b/PyTools/Embed/PyObjectDecl.h
@@ -0,0 +1,16 @@
+// forward declarations needed for using `PyObject`
+
+#ifndef USER_API
+
+#ifndef PYOBJECT_H
+#define PYOBJECT_H
+
+#ifndef PyObject_HEAD
+struct _object;
+typedef _object PyObject;
+#endif
+
+typedef long int np_size_t;  // size type used in Numpy
+
+#endif // PYOBJECT_H
+#endif // USER_API
-- 
GitLab


From bf1c5285f3529bb8338079e039c07cf27f064974 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:56:59 +0100
Subject: [PATCH 03/85] Result: A container for returned results which may fail

The class `Result` contains a value (or none) and a `Status` which denotes the status of the value (OK/Warning/Error).
This is used to avoid throwing exceptions in a module, leaving the decision to the callers whether an exception is needed or not.
---
 PyTools/Embed/Result.cpp |  36 +++++++++++
 PyTools/Embed/Result.h   | 131 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 167 insertions(+)
 create mode 100644 PyTools/Embed/Result.cpp
 create mode 100644 PyTools/Embed/Result.h

diff --git a/PyTools/Embed/Result.cpp b/PyTools/Embed/Result.cpp
new file mode 100644
index 00000000000..2b32315670f
--- /dev/null
+++ b/PyTools/Embed/Result.cpp
@@ -0,0 +1,36 @@
+#include "Result.h"
+
+Status::Status(const Status::Type type, const std::string& message):
+    type{type},
+    message{message} {}
+
+bool Status::isSet() const
+{
+    return (type != Status::Type::None);
+}
+
+
+bool Status::isOK() const
+{
+    return (type == Status::Type::Info);
+}
+
+
+bool Status::isWarning() const
+{
+    return (type == Status::Type::Warning);
+}
+
+
+bool Status::isError() const
+{
+    return (type == Status::Type::Error);
+}
+
+
+Status::operator bool() const
+{
+    return isOK() || isWarning();
+}
+
+//========================================
diff --git a/PyTools/Embed/Result.h b/PyTools/Embed/Result.h
new file mode 100644
index 00000000000..73f752fbfe0
--- /dev/null
+++ b/PyTools/Embed/Result.h
@@ -0,0 +1,131 @@
+#ifndef RESULT_H
+#define RESULT_H
+
+#include <utility>  // move
+#include <string>
+
+// ========================================
+
+// Indicator for the status of a result
+struct Status
+{
+    enum class Type {None=-1, Info, Warning, Error};
+    Type type = Type::None;
+
+    // status message
+    std::string message;
+
+    Status(const Status::Type type = Type::None,
+           const std::string& message = "");
+
+    bool isSet() const;
+
+    bool isOK() const;
+
+    bool isWarning() const;
+
+    bool isError() const;
+
+    // cast to boolean (true if result is valid)
+    operator bool() const;
+};
+
+//========================================
+
+// Container for Results which may fail
+/* A `Result<T>` instance contains a value of type `T` (or none) and
+   a `Status` which denotes the status of the value (OK/Warning/Error).
+   This is used to avoid throwing exceptions in a _core_ module,
+   leaving the decision to the callers (or the higher interface) whether
+   an exception should be raised or not.
+*/
+template <typename ResType>
+class Result
+{
+
+public:
+
+    using type = ResType;
+
+    // stored result
+    ResType val;
+
+    // status of the PyObject
+    Status status;
+
+    Result(ResType&& result, Status&& status);
+
+    Result(ResType&& result);
+
+    Result(Status&& status);
+
+    ~Result();
+
+    // disallow copy constructor
+    Result(const Result&) = delete;
+    // disallow copy assignment
+    Result& operator=(const Result&) = delete;
+    // allow move constructor
+    Result(Result&& other);
+    // reset the container to the initial status
+    void reset();
+    // check validity of the Result
+    bool valid() const;
+    // check if the Result is set
+    bool isSet() const;
+};
+
+
+//-- Implementation of Result
+
+template <typename ResType>
+Result<ResType>::Result(ResType&& result,
+                        Status&& status):
+    val{std::move(result)},
+    status{std::move(status)} {}
+
+
+template <typename ResType>
+Result<ResType>::Result(ResType&& result):
+    Result(std::move(result), Status(Status::Type::Info)) {}
+
+
+template <typename ResType>
+Result<ResType>::Result(Status&& status):
+    status{std::move(status)} {}
+
+
+template <typename ResType>
+Result<ResType>::~Result() {}
+
+
+template <typename ResType>
+Result<ResType>::Result(Result<ResType>&& other):
+    Result(std::move(other.val), std::move(other.status))
+{
+    // reset the moved object
+    other.reset();
+}
+
+
+template <typename ResType>
+void Result<ResType>::reset()
+{
+    status = Status();
+}
+
+
+template <typename ResType>
+bool Result<ResType>::valid() const
+{
+    return (status.isOK() || status.isWarning());
+}
+
+
+template <typename ResType>
+bool Result<ResType>::isSet() const
+{
+    return (status.isSet());
+}
+
+#endif  // RESULT_H
-- 
GitLab


From ee8b54438171e5706169a086545e2aa699683752 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:56:50 +0100
Subject: [PATCH 04/85] PyObjectPtr: Container for the results from the
 embedded Python interpreter

The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and a `Status` which denotes the status of the value (OK/Warning/Error).
Decrementing Python reference-count is performed automatically when a
`PyObjectPtr` expires.
---
 PyTools/Embed/PyObjectPtr.cpp | 83 +++++++++++++++++++++++++++++++++++
 PyTools/Embed/PyObjectPtr.h   | 72 ++++++++++++++++++++++++++++++
 2 files changed, 155 insertions(+)
 create mode 100644 PyTools/Embed/PyObjectPtr.cpp
 create mode 100644 PyTools/Embed/PyObjectPtr.h

diff --git a/PyTools/Embed/PyObjectPtr.cpp b/PyTools/Embed/PyObjectPtr.cpp
new file mode 100644
index 00000000000..55390c99ab5
--- /dev/null
+++ b/PyTools/Embed/PyObjectPtr.cpp
@@ -0,0 +1,83 @@
+#include "PyObjectPtr.h"
+#include "PythonInterpreter.h"
+
+#include <string>
+#include <sstream>  // stringstream
+#include <exception>
+
+PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr,
+                         const Status& status):
+    ptr{pyobject_ptr},
+    status{status}
+{
+    // if PyObject pointer is null but result type is not `None` or `Error`,
+    // then that will be an `Error` case.
+    if(!ptr && status.type != Status::Type::None
+       && status.type != Status::Type::Error)
+    {
+        this->status.type = Status::Type::Error;
+    }
+}
+
+
+PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr):
+    PyObjectPtr(pyobject_ptr, Status(Status::Type::Info)) {}
+
+
+PyObjectPtr::PyObjectPtr(const Status& status):
+    PyObjectPtr(nullptr, status) {}
+
+
+PyObjectPtr::~PyObjectPtr()
+{
+    discard();
+}
+
+
+PyObjectPtr::PyObjectPtr(PyObjectPtr&& other):
+    PyObjectPtr(other.ptr, other.status)
+{
+    // reset the moved object
+    other.reset();
+}
+
+
+PyObject* PyObjectPtr::release()
+{
+    PyObject* pyobject_ptr {ptr};
+    reset();
+    return pyobject_ptr;
+}
+
+
+void PyObjectPtr::reset()
+{
+    ptr = nullptr;
+    status = Status();
+}
+
+
+void PyObjectPtr::discard()
+{
+    if (!PythonInterpreter::isInitialized()) {
+        throw(std::runtime_error(
+                  "Decrementing Python reference-count without "
+                  "Python initialized leads to memory access violation "
+                  "(segmentation fault)"));
+    }
+
+    PythonInterpreter::DecRef(ptr);
+    reset();
+}
+
+
+bool PyObjectPtr::valid() const
+{
+    return (ptr && (status.isOK() || status.isWarning()));
+}
+
+
+bool PyObjectPtr::isSet() const
+{
+    return (ptr && status.isSet());
+}
diff --git a/PyTools/Embed/PyObjectPtr.h b/PyTools/Embed/PyObjectPtr.h
new file mode 100644
index 00000000000..2165b64770a
--- /dev/null
+++ b/PyTools/Embed/PyObjectPtr.h
@@ -0,0 +1,72 @@
+#ifndef PYOBJECTPTR_H
+#define PYOBJECTPTR_H
+
+#include "PyObjectDecl.h"
+#include "Result.h"  // Status
+#include <vector>
+
+//========================================
+
+// safe container for PyObjects
+/*
+The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and a `Status` which denotes the status of the value (OK/Warning/Error).
+Decrementing Python reference-count is performed automatically when a
+`PyObjectPtr` expires.
+*/
+
+class PyObjectPtr
+{
+
+public:
+    // raw pointer to the PyObject
+    PyObject* ptr = nullptr;
+
+    // status of the PyObject
+    Status status;
+
+    PyObjectPtr(PyObject* pyobject_ptr, const Status& status);
+
+    PyObjectPtr(PyObject* pyobject_ptr);
+
+    PyObjectPtr(const Status& status);
+
+    ~PyObjectPtr();
+
+    // disallow copy constructor
+    PyObjectPtr(const PyObjectPtr&) = delete;
+    // disallow copy assignment
+    PyObjectPtr& operator=(const PyObjectPtr&) = delete;
+    // allow move constructor
+    PyObjectPtr(PyObjectPtr&& other);
+    // reset the container to the initial status (does _not_ release the Python resource)
+    void reset();
+    // reset the container to the initial status and return the PyObject pointer (does _not_ release the Python resource)
+    PyObject* release();
+    // discards the Python resource (decrements the Python reference)
+    void discard();
+    // check validity of the PyObjectPtr
+    bool valid() const;
+    // check if the PyObjectPtr is set
+    bool isSet() const;
+};
+
+
+// Numpy array descriptor
+struct NpArrayDescr
+{
+    // Numpy array descriptors
+    bool C_contiguous = false;
+    bool F_contiguous = false;
+    // whether the data area of arr is aligned and in machine byte-order
+    bool wellbehaved = false;
+    // number of dimensions
+    int n_dims = 0;
+    // sizes of dimensions
+    std::vector<np_size_t> dims;
+    // character code indicating the data type
+    char dtype = '\0';
+    // whether Numpy array owns its data
+    bool owns_data = false;
+};
+
+#endif  // PYOBJECTPTR_H
-- 
GitLab


From 1e6024d1656382b381a7a176d8fc59098ad45e02 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:57:10 +0100
Subject: [PATCH 05/85] PythonInterpreter: Embedded Python functionality

The embedded Python and Numpy functionality is implemented in a self-contained module.

The stable part of the Python C-API is marked. The compatibility across
Python 3.7-3.9 is ensured.
---
 PyTools/Embed/PythonInterpreter.cpp | 953 ++++++++++++++++++++++++++++
 PyTools/Embed/PythonInterpreter.h   | 103 +++
 2 files changed, 1056 insertions(+)
 create mode 100644 PyTools/Embed/PythonInterpreter.cpp
 create mode 100644 PyTools/Embed/PythonInterpreter.h

diff --git a/PyTools/Embed/PythonInterpreter.cpp b/PyTools/Embed/PythonInterpreter.cpp
new file mode 100644
index 00000000000..bae6070c731
--- /dev/null
+++ b/PyTools/Embed/PythonInterpreter.cpp
@@ -0,0 +1,953 @@
+/* Embedded Python Interpreter
+
+Note that Python objects are structures allocated on the heap,
+accessed through pointers of type `PyObject*`.
+
+References:
+- Python C-API <https://docs.python.org/3/c-api>
+- Python ABI stability <https://docs.python.org/3/c-api/stable.html>
+- Numpy C-API <https://numpy.org/doc/stable/reference/c-api>;
+  <https://numpy.org/doc/stable/reference/c-api/array.html>.
+- Python Extension Patterns <https://pythonextensionpatterns.readthedocs.io>
+- "Python behind the scenes" series <https://tenthousandmeters.com/tag/python-behind-the-scenes>
+- Python's garbage collector <https://rushter.com/blog/python-garbage-collector>
+*/
+
+#define INCLUDE_NUMPY  // also include Numpy via PyCore
+#include "PyCore.h"
+#undef INCLUDE_NUMPY
+
+#include "PythonInterpreter.h"
+
+#include <cstddef>  // NULL
+#include <cstring>  // memcpy
+#include <string>
+#include <sstream>
+#include <vector>
+#include <memory>  // unique_ptr
+
+#include <iostream>  // DEBUG
+//========================================
+
+
+namespace {
+//-- Auxiliary functions
+
+// thin wrapper to initialize Numpy
+int _init_numpy()
+{
+    if(!PyArray_API) {
+        /* To use Numpy Array C-API from an extension module,
+           `import_array` function must be called.
+           If the extension module is self-contained in a single .c file,
+           then that is all that needs to be done.
+           This function imports the module where the function-pointer table
+           is stored and points the correct variable to it.
+        */
+        // NOTE: import_array returns `NULL` on failure.
+        import_array();
+        return 1; // Numpy Array API loaded successfully
+    }
+
+    return 2;  // Numpy Array API is already loaded
+}
+
+
+//! Converts char to string. In the case of nullptr will return an empty string
+std::string toString(const char* const c)
+{
+    if (c)
+        return c;
+    return "";
+}
+
+
+std::string toString(const wchar_t* const c)
+{
+    if (!c)
+        return "";
+    std::wstring wstr(c);
+    return std::string(wstr.begin(), wstr.end());
+}
+
+//! Converts PyObject into vector of strings, if possible, or throws exception
+Result<std::vector<std::string>> toVectorString(PyObject* py_object)
+{
+    std::vector<std::string> result;
+
+    if (PyTuple_Check(py_object)) {
+        for (Py_ssize_t i = 0; i < PyTuple_Size(py_object); i++) {
+            PyObject* value = PyTuple_GetItem(py_object, i);
+            result.push_back(PythonInterpreter::pyStrtoString(value));
+        }
+    } else if (PyList_Check(py_object)) {
+        for (Py_ssize_t i = 0; i < PyList_Size(py_object); i++) {
+            PyObject* value = PyList_GetItem(py_object, i);
+            result.push_back(PythonInterpreter::pyStrtoString(value));
+        }
+    } else
+        return {Status(Status::Type::Error,
+                       "Cannnot convert the given Python object "
+                       "to vector<string>.")};
+    return {std::move(result)};
+}
+
+
+// auxiliary function to convert a Numpy array data of real type `real_t`
+// to vector<double>.
+template<typename real_t>
+inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr,
+                                 npy_intp rowIdxMax, npy_intp colIdxMax,
+                                 std::vector<double>& vector_out)
+{
+    std::size_t idx = 0;
+    for(npy_intp i_row = 0; i_row < rowIdxMax; ++i_row) {
+        for(npy_intp j_col = 0; j_col < colIdxMax; ++j_col) {
+            const double val = static_cast<double>(
+                *reinterpret_cast<real_t*>(PyArray_GETPTR2(npArray_ptr, i_row, j_col)));
+            vector_out[idx] = val;
+            ++idx;
+        }
+    }
+}
+
+
+double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
+{
+    if(!npy_array.valid() || !PyArray_Check(npy_array.ptr)) {
+        throw std::runtime_error(
+            "PythonInterpreter: Cannot convert an invalid Numpy array to a C-Array");
+    }
+
+    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(npy_array.ptr)};
+    PyArray_Descr* npArray_descr = PyArray_DESCR(npArray_ptr);
+    const int npArray_type = PyArray_TYPE(npArray_ptr);
+    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
+    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
+    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
+
+    // DEBUG
+    // type checking
+    if(PyDataType_ISUNSIGNED(npArray_descr))
+        printf("AN>> Array datatype = uint\n");
+    else if(PyDataType_ISSIGNED(npArray_descr))
+        printf("AN>> Array datatype = int\n");
+    else if(PyDataType_ISFLOAT(npArray_descr))
+        printf("AN>> Array datatype = float\n");
+    else
+        printf("AN>> Array datatype = ?\n");
+
+    if(npArray_type == NPY_FLOAT)
+        printf("AN>> Array type (%d) = f32\n", npArray_type);
+
+    if(PyArray_IS_C_CONTIGUOUS(npArray_ptr))
+        printf("AN>> Array is C-contiguous (row-major)\n");
+
+    if(PyArray_IS_F_CONTIGUOUS(npArray_ptr))
+        printf("AN>> Array is Fortran-contiguous (column-major)\n");
+
+    if(PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
+        printf("AN>> Array is well-behaved\n");
+
+    if(npArray_flags & NPY_ARRAY_OWNDATA)
+        printf("AN>> Array owns its data\n");
+    else
+        printf("AN>> Array does not own its data\n");
+
+    for(int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
+        printf("AN>> Array-dim{%d} = %ld\n", i_dim, npArray_dims[i_dim]);
+
+    // END DEBUG
+
+    // TODO: Verify array is 2d
+
+    // void *PyArray_GETPTR2(PyArrayObject *obj, npy_intp i, npy_intp j)
+    // Quick, inline access to the element at the given coordinates in the ndarray, obj, which must have 2 dimensions (this is not checked). The corresponding i, j,  coordinates can be any integer but will be interpreted as npy_intp. You may want to typecast the returned pointer to the data type of the ndarray.
+
+    // element-by-element copy of the Numpy array data (always work)
+    const std::size_t vsize = static_cast<std::size_t>(PyArray_SIZE(npArray_ptr));
+    std::vector<double> out1(vsize);
+    for (std::size_t i = 0; i < vsize; i++) {
+        out1[i] = *reinterpret_cast<double*>(PyArray_GETPTR1(npArray_ptr, i));
+    }
+
+    // using bulk memcpy-like operation, but keep in mind that NumPy ndarrays may be mis-aligned for the data type, have non-native byte order, or other subtle attributes that make such copies less than desirable.
+
+    std::vector<double> out2(vsize);
+    std::memcpy(out2.data(), PyArray_DATA(npArray_ptr),
+                sizeof(double) * vsize);
+
+    // using either approach, out now contains a copy of the ndarray's data
+
+    double* npArray_data_ptr {reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
+    if(!npArray_data_ptr) {
+        // checkError();
+        throw std::runtime_error("PythonInterpreter: Cannot convert an invalid Numpy array to a C-Array (invalid data pointer)");
+    }
+
+    // TODO: Problem with the reference to numpy array!!!
+    // returns a _new_ reference; ie. caller is responsible for the Python ref-count
+    return npArray_data_ptr;
+}
+
+
+Result<double*> NpyArray1D_to_C(PyObject* pyobject_ptr)
+{
+    if(!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: Cannot convert an invalid "
+                       "Numpy array to a C-Array")};
+    }
+
+    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
+    const int npArray_type = PyArray_TYPE(npArray_ptr);
+    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
+    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
+    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
+    // character code indicating the data type
+    const char npArray_dtype = npArray_descr->type;
+
+    //TODO: Enable type-casting for the Numpy array
+    if(npArray_type != NPY_DOUBLE) {
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Expected a Numpy array of type _double_ "
+               << "(given type '" << npArray_dtype << "')";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // DEBUG
+    // type checking
+    if(PyDataType_ISUNSIGNED(npArray_descr))
+        printf("AN>> Array datatype = uint\n");
+    else if(PyDataType_ISSIGNED(npArray_descr))
+        printf("AN>> Array datatype = int\n");
+    else if(PyDataType_ISFLOAT(npArray_descr))
+        printf("AN>> Array datatype = float\n");
+    else
+        printf("AN>> Array datatype = ?\n");
+
+    if(npArray_type == NPY_FLOAT)
+        printf("AN>> Array type (%d) = f32\n", npArray_type);
+
+    if(PyArray_IS_C_CONTIGUOUS(npArray_ptr))
+        printf("AN>> Array is C-contiguous (row-major)\n");
+
+    if(PyArray_IS_F_CONTIGUOUS(npArray_ptr))
+        printf("AN>> Array is Fortran-contiguous (column-major)\n");
+
+    if(PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
+        printf("AN>> Array is well-behaved\n");
+
+    if(npArray_flags & NPY_ARRAY_OWNDATA)
+        printf("AN>> Array owns its data\n");
+    else
+        printf("AN>> Array does not own its data\n");
+
+    for(int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
+        printf("AN>> Array-dim{%d} = %ld\n", i_dim, npArray_dims[i_dim]);
+
+    // END DEBUG
+
+    // TODO: check if array is contiguous
+    double* npArray_data_ptr {reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
+    if(!npArray_data_ptr) {
+        PythonInterpreter::checkError();
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot convert an invalid Numpy array to a C-Array"
+                       "(data pointer is invalid)")};
+    }
+
+    return {std::move(npArray_data_ptr)};
+}
+
+
+PyObjectPtr call_pyfunction_npy_array(
+    PyObjectPtr& py_module, const std::string& fn_name, PyObjectPtr& npy_array)
+{
+    // Call a Python function (from a given module) which takes a
+    // single Numpy array as input and returns a Numpy array.
+
+    if (!py_module.valid()) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter: Cannot call a function "
+                       "from an invalid Python module")};
+    }
+
+    PyObject* pFunc = PyObject_GetAttrString(py_module.ptr, fn_name.c_str());
+    if (!pFunc || !PyCallable_Check(pFunc)) {
+        PythonInterpreter::checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter: The function '" << fn_name << "' "
+               << "is not callable";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    PyObject* pReturn = PyObject_CallFunctionObjArgs(pFunc, npy_array.ptr,
+                                                     NULL);
+    if (!PyArray_Check(pReturn)) {
+        PythonInterpreter::checkError();
+        Py_DecRef(pFunc);
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter: Invalid return value from calling "
+            "the function '" << fn_name << "' (expected a Numpy array)";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return {pReturn};
+}
+
+}  // namespace
+//----------------------------------------
+
+// Python stable ABI
+Status PythonInterpreter::initialize()
+{
+    std::stringstream msg_ss;
+
+    if (!Py_IsInitialized()) {
+        Py_Initialize();
+        msg_ss << "PythonInterpreter: Initialized Python - "
+               << "version: " << Py_GetVersion();
+        return {Status::Type::Info, msg_ss.str()};
+
+    }
+
+    // if Python already initialized
+    msg_ss << "PythonInterpreter: Python already initialized - "
+           << "version: " << Py_GetVersion();
+    return {Status::Type::Warning, msg_ss.str()};
+}
+
+// Python stable ABI
+bool PythonInterpreter::isInitialized()
+{
+    return static_cast<bool>(Py_IsInitialized());
+}
+
+
+// Python stable ABI
+Status PythonInterpreter::finalize()
+{
+    // undo all initializations made by Py_Initialize() and subsequent use
+    // of Python/C API functions, and destroy all sub-interpreters.
+    // This is a no-op when called for a second time.
+    Py_Finalize();
+
+    std::stringstream msg_ss;
+    msg_ss << "PythonInterpreter: Finalized Python - "
+           << "version: " << Py_GetVersion();
+    return {Status::Type::Info, msg_ss.str()};
+}
+
+
+// Python stable ABI
+Status PythonInterpreter::checkError()
+{
+    if (PyErr_Occurred()) {
+        // print a standard traceback to sys.stderr and clear the error indicator
+        std::cerr << "---PythonInterpreter: Error in Python interpreter:\n";
+        PyErr_Print();
+        std::cerr << "\n---\n";
+        return {Status::Type::Error};
+    }
+
+    return {Status::Type::Info, "No Python errors occured"};
+}
+
+
+// Python stable ABI
+void PythonInterpreter::addPythonPath(const std::string& path)
+{
+    if (!path.empty()) {
+        // add path to `PYTHONPATH`
+        PyObject* sysPath = PySys_GetObject((char*)"path");
+        PyList_Append(sysPath, PyUnicode_FromString(path.c_str()));
+    }
+}
+
+// Python stable ABI
+int PythonInterpreter::setPythonPath(const std::string& path)
+{
+    // returns 0 on success, -1 on error
+    const int result = PySys_SetObject(
+        (char*)"path", PyUnicode_FromString(path.c_str()));
+    return result;
+}
+
+// Python stable ABI
+PyObjectPtr PythonInterpreter::import(
+    const std::string& pymodule_name, const std::string& path)
+{
+    addPythonPath(path);
+
+    // import the module
+    PyObject* pymodule = PyImport_ImportModule(pymodule_name.c_str());
+    if (!pymodule || !PyModule_Check(pymodule)) {
+        checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter: Cannot load Python module "
+               << "'" << pymodule_name << "' "
+               << "(given path = '" << path << "')";
+        return PyObjectPtr(Status(Status::Type::Error, msg_ss.str()));
+    }
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return {pymodule};
+}
+
+
+// Python stable ABI
+void PythonInterpreter::DecRef(PyObject* py_object)
+{
+    Py_XDECREF(py_object);
+}
+
+
+std::string PythonInterpreter::pyStrtoString(PyObject* py_object)
+{
+    std::string result;
+    PyObject* pyStr = PyUnicode_AsEncodedString(py_object, "utf-8", "replace");
+    if (pyStr) {
+        result = std::string(PyBytes_AsString(pyStr));
+        Py_DecRef(pyStr);
+    }
+
+    return result;
+}
+
+
+std::string PythonInterpreter::runtimeInfo()
+{
+    std::stringstream result;
+
+    // Runtime environment
+    result << std::string(60, '=') << "\n";
+    /* DEBUG
+
+    result << "PATH: " << BaseUtils::System::getenv("PATH") << "\n";
+    result << "PYTHONPATH: " << BaseUtils::System::getenv("PYTHONPATH") << "\n";
+    result << "PYTHONHOME: " << BaseUtils::System::getenv("PYTHONHOME") << "\n";
+
+    */
+
+    // Embedded Python details
+    result << "Py_GetProgramName(): " << toString(Py_GetProgramName()) << "\n";
+    result << "Py_GetProgramFullPath(): " << toString(Py_GetProgramFullPath())
+           << "\n";
+    result << "Py_GetPath(): " << toString(Py_GetPath()) << "\n";
+    result << "Py_GetPythonHome(): " << toString(Py_GetPythonHome()) << "\n";
+
+    // Runtime Python's sys.path
+    PyObject* sysPath = PySys_GetObject((char*)"path");
+    Result<std::vector<std::string>> content {toVectorString(sysPath)};
+    if(content.valid()) {
+        result << "sys.path: ";
+        for (auto s : content.val)
+            result << s << ",";
+        result << "\n";
+    }
+
+    return result.str();
+}
+
+// Attempt to retrieve Python stack trace
+// Ref: <https://stackoverflow.com/q/1796510>
+std::string PythonInterpreter::stackTrace()
+{
+    std::stringstream result;
+
+    if (PyErr_Occurred()) {
+        PyObject *ptype, *pvalue, *ptraceback, *pystr;
+
+        PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+        pystr = PyObject_Str(pvalue);
+        if (char* str = PyBytes_AsString(pystr))
+            result << std::string(str) << "\n";
+        Py_DecRef(pystr);
+
+        PyObject* module_name = PyUnicode_FromString("traceback");
+        PyObject* py_traceback_module = PyImport_Import(module_name);
+        Py_DecRef(module_name);
+
+        if (py_traceback_module) {
+            result << "\n";
+            PyObject* pyth_func = PyObject_GetAttrString(py_traceback_module, "format_exception");
+            if (pyth_func && PyCallable_Check(pyth_func)) {
+                PyObject* pyth_val =
+                    PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
+                Py_DecRef(pyth_func);
+                if (pyth_val) {
+                    pystr = PyObject_Str(pyth_val);
+                    if (char* str = PyBytes_AsString(pystr))
+                        result << std::string(str);
+                    Py_DecRef(pystr);
+                    Py_DecRef(pyth_val);
+                }
+            }
+            result << "\n";
+        }
+
+        Py_DecRef(py_traceback_module);
+    }
+
+    result << "\n";
+    result << PythonInterpreter::runtimeInfo();
+    result << "\n";
+
+    return result.str();
+}
+
+
+std::string PythonInterpreter::errorDescription(const std::string& title)
+{
+    std::stringstream msg;
+    msg << title << "\n";
+    msg << PythonInterpreter::stackTrace() << "\n";
+    return msg.str();
+}
+
+//----------------------------------------
+
+Status PythonInterpreter::Numpy::initialize() {
+
+    // initialize Python C API, if needed
+    Status py_status{PythonInterpreter::initialize()};
+    if(py_status.isError())
+        return py_status;
+
+    const int res {_init_numpy()};
+
+    switch(res)
+    {
+    case 1:
+        return {Status::Type::Info, "PythonInterpreter: Initialized Numpy"};
+    case 2:
+        return {Status::Type::Warning, "PythonInterpreter: Numpy already initialized"};
+    default:
+        return {Status::Type::Error, "PythonInterpreter: Cannot initialize Numpy"};
+    }
+}
+
+
+bool PythonInterpreter::Numpy::isInitialized()
+{
+    return static_cast<bool>(PyArray_API);
+}
+
+//----------------------------------------
+
+Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(
+    PyObject* pyobject_ptr)
+{
+    if(!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Invalid Numpy array")};
+    }
+
+    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
+    const int npArray_type = PyArray_TYPE(npArray_ptr);
+    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
+    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
+    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
+    // character code indicating the data type
+    const char npArray_dtype = npArray_descr->type;
+
+
+    NpArrayDescr np_descr_out;
+    np_descr_out.n_dims = npArray_ndim;
+    if (np_descr_out.n_dims > 0) {
+        np_descr_out.dims.resize(npArray_ndim);
+        for(npy_intp i_d = 0; i_d < npArray_ndim; ++i_d)
+            np_descr_out.dims[i_d] = npArray_dims[i_d];
+    }
+
+    np_descr_out.dtype = npArray_dtype;
+
+    if(PyArray_IS_C_CONTIGUOUS(npArray_ptr))
+        np_descr_out.C_contiguous = true;
+
+    if(PyArray_IS_F_CONTIGUOUS(npArray_ptr))
+        np_descr_out.F_contiguous = true;
+
+    if(PyArray_ISBEHAVED_RO(npArray_ptr))
+        np_descr_out.wellbehaved = true;
+
+    if(npArray_flags & NPY_ARRAY_OWNDATA)
+        np_descr_out.owns_data = true;
+
+    return {std::move(np_descr_out)};
+}
+
+
+Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
+{
+    if(!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: Cannot convert an invalid "
+                       "Numpy array to a C-Array")};
+    }
+
+    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
+    const int npArray_type = PyArray_TYPE(npArray_ptr);
+    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
+    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
+    const std::size_t npArray_size = static_cast<std::size_t>
+        (PyArray_SIZE(npArray_ptr));
+    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
+    // character code indicating the data type
+    const char npArray_dtype = npArray_descr->type;
+
+    // Numpy array type must be 2d
+    if(npArray_ndim != 2) {
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Expected a Numpy 2d-array "
+               << "(given number of dimensions is " << npArray_ndim << ")";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // Numpy array type must be numeric and real (eligible for casting to double)
+    if(!PyDataType_ISNUMBER(npArray_descr) || PyDataType_ISCOMPLEX(npArray_descr)) {
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Expected a Numpy array of numeric type and real "
+               << "(given type '" << npArray_dtype << "')";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    std::vector<double> data(npArray_size);
+
+    const npy_intp rowIdxMax = npArray_dims[0], colIdxMax = npArray_dims[1];
+
+    // branch on type
+    switch(npArray_type)
+    {
+    // float
+    case NPY_DOUBLE:
+        _realArray2DToDouble<npy_double>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_FLOAT:
+        _realArray2DToDouble<npy_float>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    // signed int
+    case NPY_INT8:
+        _realArray2DToDouble<npy_int8>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_INT16:
+        _realArray2DToDouble<npy_int16>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_INT32:
+        _realArray2DToDouble<npy_int32>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_LONG:
+        _realArray2DToDouble<npy_long>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    // unsigned int
+    case NPY_UINT8:
+        _realArray2DToDouble<npy_uint8>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_UINT16:
+        _realArray2DToDouble<npy_uint16>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_UINT32:
+        _realArray2DToDouble<npy_uint32>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    case NPY_ULONG:
+        _realArray2DToDouble<npy_ulong>(npArray_ptr, rowIdxMax, colIdxMax, data);
+        break;
+    default:
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Conversion of Numpy array of dtype '" << npArray_dtype << "' "
+               << "to vector<double> is not implemented";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    std::stringstream msg_ss;
+    msg_ss << "PythonInterpreter::Numpy: "
+           << "Numpy 2d-array of dtype '" << npArray_dtype << "' "
+           << "and shape (" << npArray_dims[0] << "," << npArray_dims[1] << ") "
+           << "converted to vector<double> with size " << data.size();
+
+    return {std::move(data), Status(Status::Type::Info, msg_ss.str())};
+}
+
+
+PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(
+    double* const c_array, const np_size_t dims[2])
+{
+    if (!c_array) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot create a Numpy 1D-array from a null data pointer")};
+    }
+
+    const np_size_t size = dims[0] * dims[1];
+    if (size < 1) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot create a Numpy 1D-array from a data with size = 0")};
+    }
+
+    npy_intp npDims[2] = {dims[0], dims[1]};
+
+    // create stand-alone Numpy array (float64)
+    PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 2,
+                                              npDims, NPY_DOUBLE);
+    if (!npArray_ptr) {
+        checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Cannot create a Numpy 1D-array from the "
+               << "given data (size = " << size << ")";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // obtain pointer to data buffer of Numpy array
+    double* array_buffer = static_cast<double*>(
+        PyArray_DATA((PyArrayObject*)(npArray_ptr)));
+
+    for (np_size_t index = 0; index < size; ++index)
+    {
+        *array_buffer = c_array[index];
+        ++array_buffer;
+    }
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return {npArray_ptr};
+}
+
+
+PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(
+    double* const c_array, const np_size_t size)
+{
+    if (!c_array) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot create a Numpy 1D-array from a null data pointer")};
+    }
+
+    if (size < 1) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot create a Numpy 1D-array from a data with size = 0")};
+    }
+
+    npy_intp npDims[1] = {size};
+
+    // create stand-alone Numpy array (float64)
+    PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 1,
+                                              npDims, NPY_DOUBLE);
+    if (!npArray_ptr) {
+        checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Cannot create a Numpy 1D-array from the "
+               << "given data (size = " << size << ")";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // obtain pointer to data buffer of Numpy array
+    double* array_buffer = static_cast<double*>(
+        PyArray_DATA((PyArrayObject*)(npArray_ptr)));
+
+    for (np_size_t index = 0; index < size; ++index)
+    {
+        *array_buffer = c_array[index];
+        ++array_buffer;
+    }
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return {npArray_ptr};
+}
+
+
+PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(
+    double* const c_array, const np_size_t dims[2])
+{
+    if (!c_array) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot create a Numpy 2D-array from a null data pointer")};
+    }
+
+    const np_size_t size = dims[0] * dims[1];
+    if (size < 1) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: "
+                       "Cannot create a Numpy 2D-array from a data with size = 0")};
+    }
+
+    npy_intp npDims[2] = {dims[0], dims[1]};
+
+    // convert the contiguous C-array to Numpy array
+    PyObject* npArray_ptr = PyArray_SimpleNewFromData(
+        /* n_dims */ 2, npDims, NPY_DOUBLE, reinterpret_cast<void*>(c_array));
+
+    if(!npArray_ptr || !PyArray_Check(npArray_ptr)) {
+        PythonInterpreter::checkError();
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter::Numpy: Cannot convert the given "
+                       "C-Array to a Numpy 2D-array")};
+    }
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return {npArray_ptr};
+}
+
+
+PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensions)
+{
+    // descriptors for the array dimensions
+    const std::size_t n_dims = dimensions.size();
+    if (n_dims < 1) {
+        return {Status(Status::Type::Error,
+                       "Cannot make a Numpy with the given number of dimensions; "
+                       "number of dimensions must be >= 1")};
+    }
+
+    for (std::size_t d = 0; d < n_dims; ++d) {
+        if (dimensions[d] < 2) {
+            return {Status(Status::Type::Error,
+                           "Cannot make a Numpy with the given dimensions; "
+                           "dimensions must be >= 2")};
+        }
+    }
+
+    npy_int ndim_numpy = static_cast<npy_int>(n_dims);
+    npy_intp* ndimsizes_numpy = new npy_intp[n_dims];
+    for (std::size_t d = 0; d < n_dims; ++d)
+        ndimsizes_numpy[d] = dimensions[d];
+
+    // creating standalone numpy array
+    PyObject* npyArray_ptr = PyArray_SimpleNew(
+        ndim_numpy, ndimsizes_numpy, NPY_DOUBLE);
+    delete[] ndimsizes_numpy;
+
+    if (!npyArray_ptr) {
+        checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Cannot create a Numpy " << n_dims << "D-array "
+               << "from the given data";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    return {npyArray_ptr};
+}
+
+
+Result<double*> PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
+{
+
+    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+
+    // get the pointer to data buffer of the Numpy array
+    double* data_ptr = static_cast<double*>(
+        PyArray_DATA((PyArrayObject*)npArray_ptr));
+
+    if (!data_ptr) {
+        checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::Numpy: "
+               << "Numpy array has invalid data pointer";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    return {std::move(data_ptr)};
+}
+
+//----------------------------------------
+
+PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
+{
+    PythonInterpreter::addPythonPath(path);
+
+#ifndef _WIN32
+    // Stores signal handler before numpy's mess it up.
+    // This is to make ctrl-c working from terminal.
+
+    PyOS_sighandler_t sighandler;
+    sighandler = PyOS_getsig(SIGINT);
+#endif
+
+    PyObject* ba_pymodule = PyImport_ImportModule("bornagain");
+
+    if (!ba_pymodule || !PyModule_Check(ba_pymodule)) {
+        checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter: Cannot load 'bornagain' Python module "
+               << "(given path = '" << path << "')";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // restores single handler to make ctr-c alive.
+#ifndef _WIN32
+    PyOS_setsig(SIGINT, sighandler);
+#endif
+
+    return {ba_pymodule};
+}
+
+
+PyObjectPtr PythonInterpreter::BornAgain::importScript(
+    const std::string& script, const std::string& path)
+{
+    PyObjectPtr ba_pymodule {PythonInterpreter::BornAgain::import(path)};
+    // TODO: Check ba module
+
+    PyObject* pCompiledFn = Py_CompileString(script.c_str(), "", Py_file_input);
+    if (!pCompiledFn) {
+        ba_pymodule.discard();
+        return {Status(Status::Type::Error,
+                       PythonInterpreter::errorDescription(
+                           "Cannot compile Python snippet"))};
+    }
+
+    // create a module
+    PyObject* tmpModule = PyImport_ExecCodeModule((char*)"tmpModule", pCompiledFn);
+    if (!tmpModule) {
+        Py_DecRef(pCompiledFn);
+        ba_pymodule.discard();
+        return {Status(Status::Type::Error,
+                       PythonInterpreter::errorDescription(
+                           "Cannot execute Python snippet"))};
+    }
+
+    return {tmpModule};
+}
+
+
+Result<std::vector<std::string>> PythonInterpreter::BornAgain::listOfFunctions(
+    const std::string& script, const std::string& path)
+{
+    PyObjectPtr tmpModule {PythonInterpreter::BornAgain::importScript(script, path)};
+
+    PyObject* pDict = PyModule_GetDict(tmpModule.ptr);
+    if (!pDict) {
+        return {Status(Status::Type::Error, "PythonInterpreter::BornAgain: "
+                       "Cannot obtain the dictionary from the script module")};
+    }
+
+    PyObject *key, *value;
+    Py_ssize_t pos = 0;
+    std::vector<std::string> fn_names;
+    while (PyDict_Next(pDict, &pos, &key, &value)) {
+        if (PyCallable_Check(value)) {
+            std::string func_name {PythonInterpreter::pyStrtoString(key)};
+            if (func_name.find("__") == std::string::npos)
+                fn_names.push_back(func_name);
+        }
+    }
+
+    Py_DecRef(pDict);
+
+    return {std::move(fn_names)};
+}
diff --git a/PyTools/Embed/PythonInterpreter.h b/PyTools/Embed/PythonInterpreter.h
new file mode 100644
index 00000000000..db62584b2b4
--- /dev/null
+++ b/PyTools/Embed/PythonInterpreter.h
@@ -0,0 +1,103 @@
+#ifndef PYTHONINTRP_H
+#define PYTHONINTRP_H
+
+#include <vector>
+#include <string>
+#include "PyObjectPtr.h"
+
+class MultiLayer;
+
+// Embedded Python interpreter
+namespace PythonInterpreter {
+
+// Python stable ABI
+Status initialize();
+
+// Python stable ABI
+bool isInitialized();
+
+// Python stable ABI
+Status finalize();
+
+// Python stable ABI
+Status checkError();
+
+// Python stable ABI
+void addPythonPath(const std::string& path);
+
+// Python stable ABI
+int setPythonPath(const std::string& path);
+
+// Python stable ABI
+PyObjectPtr import(const std::string& pymodule_name,
+                   const std::string& path = "");
+// Python stable ABI
+void DecRef(PyObject* py_object);
+
+//! Converts String PyObject into string, if possible, or throws exception
+std::string pyStrtoString(PyObject* obj);
+
+//! Returns multi-line string representing PATH, PYTHONPATH, sys.path and other info.
+std::string runtimeInfo();
+
+//! Returns string representing python stack trace.
+std::string stackTrace();
+
+//! Returns the full error description via Python stack trace
+std::string errorDescription(const std::string& title = "");
+
+//-- Numpy-related functionality
+namespace Numpy {
+
+// initialize Numpy
+Status initialize();
+
+// Python stable ABI
+bool isInitialized();
+
+//! Obtains the descriptor of a Numpy array
+Result<NpArrayDescr> arrayDescriptor(PyObject* pyobject_ptr);
+
+//! Creates an empty Numpy array of type `double` with the given dimensions
+PyObjectPtr arrayND(std::vector<std::size_t>& dimensions);
+
+//! Returns the pointer to the data buffer of a Numpy array of type `double`
+Result<double*> getDataPtr(PyObject* pyobject_ptr);
+
+//! Creates a Numpy 1D-array from a contiguous C-array of type `double`.
+// Data will be copied.
+PyObjectPtr createArray1DfromC(double* const c_array, const np_size_t size);
+
+// !Creates a Numpy 2D-array from a contiguous C-array of type `double`.
+// Data will be copied.
+PyObjectPtr createArray2DfromC(double* const c_array, const np_size_t dims[2]);
+
+// !Creates a `vector<double>` from a Numpy 2D-array (data will be copied).
+Result<std::vector<double>> createVectorFromArray2D(PyObject* pyobject_ptr);
+
+// !Creates a 2D Numpy array from a contiguous C-array of type `double`.
+// The data is _not_ copied, and the Numpy array will _not_ own its data.
+PyObjectPtr CArrayAsNpy2D(double* const c_array, const np_size_t dims[2]);
+
+}  // namespace Numpy
+
+//-- BornAgain-related functionality
+namespace BornAgain {
+//! Imports BornAgain from given location. If path is empty, tries to rely on PYTHONPATH.
+PyObjectPtr import(const std::string& path = "");
+
+// import a 'BornAgain' Python script
+PyObjectPtr importScript(const std::string& script,
+                         const std::string& path);
+
+//! Returns list of functions defined in the script.
+//! @param script: Python script
+//! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
+Result<std::vector<std::string>> listOfFunctions(
+    const std::string& script, const std::string& path);
+
+}  // namespace BornAgain
+
+}  // namespace PythonInterpreter
+
+#endif  // PYTHONINTRP_H
-- 
GitLab


From 0e5ddf8bdad1b25ad98842daa7929d388f0b9cd3 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 7 Feb 2022 13:34:45 +0100
Subject: [PATCH 06/85] PythonInterpreter: Add Fabio functionality

---
 PyTools/Embed/PythonInterpreter.cpp | 64 +++++++++++++++++++++++++++++
 PyTools/Embed/PythonInterpreter.h   | 10 +++++
 2 files changed, 74 insertions(+)

diff --git a/PyTools/Embed/PythonInterpreter.cpp b/PyTools/Embed/PythonInterpreter.cpp
index bae6070c731..3855791516d 100644
--- a/PyTools/Embed/PythonInterpreter.cpp
+++ b/PyTools/Embed/PythonInterpreter.cpp
@@ -951,3 +951,67 @@ Result<std::vector<std::string>> PythonInterpreter::BornAgain::listOfFunctions(
 
     return {std::move(fn_names)};
 }
+
+//----------------------------------------
+
+PyObjectPtr PythonInterpreter::Fabio::import()
+{
+    return {PythonInterpreter::import("fabio")};
+}
+
+
+PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename,
+                                           PyObjectPtr& fabio_module)
+{
+    // load an image via calling `fabio.load` function which takes a
+    // filename (Python string) and returns a Numpy array.
+
+    if (!fabio_module.valid() || !PyModule_Check(fabio_module.ptr)) {
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter.fabio: Invalid Python module "
+                       "(expected 'fabio' module)")};
+    }
+
+    PyObject* pFunc = PyObject_GetAttrString(fabio_module.ptr, (char*)"open");
+    if (!pFunc || !PyCallable_Check(pFunc)) {
+        PythonInterpreter::checkError();
+        return {Status(Status::Type::Error,
+                       "PythonInterpreter.fabio: The function 'fabio.open' is not callable")};
+    }
+
+    // convert the filename to a Python unicode string
+    PyObject* pFilename = PyUnicode_FromString(filename.c_str());
+    if (!pFilename) {
+        PythonInterpreter::checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter.fabio: Filename '" << filename << "' "
+               << "cannot be converted to Python unicode string";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // get the result of the call `fabio.open(<filename>)`
+    PyObject* pResult_open = PyObject_CallFunctionObjArgs(pFunc, pFilename, NULL);
+    Py_DecRef(pFunc);
+    if (!pResult_open) {
+        PythonInterpreter::checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter.fabio: Invalid return value from "
+               << "calling the function 'fabio.open(\"" << filename << "\")'";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // get `result.data` (must be a Numpy array)
+    PyObject* npyArray_ptr = PyObject_GetAttrString(pResult_open, (char*)"data");
+    Py_DecRef(pResult_open);
+    if (!npyArray_ptr || !PyArray_Check(npyArray_ptr)) {
+        PythonInterpreter::checkError();
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter.fabio: Invalid return value from "
+               << "calling the function 'fabio.open(\"" << filename << "\")' "
+               << "(expected a Numpy array)";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return {npyArray_ptr};
+}
diff --git a/PyTools/Embed/PythonInterpreter.h b/PyTools/Embed/PythonInterpreter.h
index db62584b2b4..26219081b53 100644
--- a/PyTools/Embed/PythonInterpreter.h
+++ b/PyTools/Embed/PythonInterpreter.h
@@ -98,6 +98,16 @@ Result<std::vector<std::string>> listOfFunctions(
 
 }  // namespace BornAgain
 
+//-- Fabio-related functionality
+namespace Fabio {
+// import 'fabio' Python module
+PyObjectPtr import();
+// call 'fabio.open' on a given file (needs Numpy)
+PyObjectPtr open(const std::string& filename,
+                 PyObjectPtr& fabio_module);
+
+} // namespace Fabio
+
 }  // namespace PythonInterpreter
 
 #endif  // PYTHONINTRP_H
-- 
GitLab


From 1cf3ecfcd600d07c5f684ef175762223a0dc9bf5 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Thu, 10 Feb 2022 13:11:14 +0100
Subject: [PATCH 07/85] PythonInterpreter: Remove unused functions or variables

---
 PyTools/Embed/PythonInterpreter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/PyTools/Embed/PythonInterpreter.cpp b/PyTools/Embed/PythonInterpreter.cpp
index 3855791516d..cc3807fb94f 100644
--- a/PyTools/Embed/PythonInterpreter.cpp
+++ b/PyTools/Embed/PythonInterpreter.cpp
@@ -551,7 +551,7 @@ Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(
 
     PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
     PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
-    const int npArray_type = PyArray_TYPE(npArray_ptr);
+    // const int npArray_type = PyArray_TYPE(npArray_ptr);
     const int npArray_ndim = PyArray_NDIM(npArray_ptr);
     npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
     const int npArray_flags = PyArray_FLAGS(npArray_ptr);
-- 
GitLab


From e74df853fc676279b40bb6a3e8c76df0545cd8a9 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Sat, 5 Feb 2022 17:29:48 +0100
Subject: [PATCH 08/85] Amend PythonInterpreter for Windows compiler MSVC 19

The change prevents the following errors from Windows compiler MSVC 19 on x64
platform:
```
error C2196: case value 'NPY_LONG' already used
error C2196: case value 'NPY_ULONG' already used
```
---
 PyTools/Embed/PythonInterpreter.cpp | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/PyTools/Embed/PythonInterpreter.cpp b/PyTools/Embed/PythonInterpreter.cpp
index cc3807fb94f..50df04d84fc 100644
--- a/PyTools/Embed/PythonInterpreter.cpp
+++ b/PyTools/Embed/PythonInterpreter.cpp
@@ -646,9 +646,16 @@ Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(Py
     case NPY_INT32:
         _realArray2DToDouble<npy_int32>(npArray_ptr, rowIdxMax, colIdxMax, data);
         break;
+
+// prevent the error from Windows compiler MSVC 19:
+// `error C2196: case value 'NPY_LONG' already used`
+#ifndef _WIN32
+
     case NPY_LONG:
         _realArray2DToDouble<npy_long>(npArray_ptr, rowIdxMax, colIdxMax, data);
         break;
+#endif // _WIN32
+
     // unsigned int
     case NPY_UINT8:
         _realArray2DToDouble<npy_uint8>(npArray_ptr, rowIdxMax, colIdxMax, data);
@@ -659,9 +666,15 @@ Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(Py
     case NPY_UINT32:
         _realArray2DToDouble<npy_uint32>(npArray_ptr, rowIdxMax, colIdxMax, data);
         break;
+
+// prevent the error from Windows compiler MSVC 19:
+// `error C2196: case value 'NPY_ULONG' already used`
+#ifndef _WIN32
     case NPY_ULONG:
         _realArray2DToDouble<npy_ulong>(npArray_ptr, rowIdxMax, colIdxMax, data);
         break;
+#endif // _WIN32
+
     default:
         std::stringstream msg_ss;
         msg_ss << "PythonInterpreter::Numpy: "
-- 
GitLab


From 786f8c2f852ce44422ae477f673710d3f0b4838b Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Wed, 2 Feb 2022 11:05:04 +0100
Subject: [PATCH 09/85] Add PyTools/CMakeLists.txt

---
 PyTools/CMakeLists.txt | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 PyTools/CMakeLists.txt

diff --git a/PyTools/CMakeLists.txt b/PyTools/CMakeLists.txt
new file mode 100644
index 00000000000..18f6637af92
--- /dev/null
+++ b/PyTools/CMakeLists.txt
@@ -0,0 +1,27 @@
+############################################################################
+# CMakeLists.txt file for building libBornAgainPyCore
+############################################################################
+
+# NOTE: The module is directly dependent on Python and Numpy C-API
+if(NOT BORNAGAIN_PYTHON)
+    return()
+endif()
+
+set(component PyCore)
+set(name PyCore)
+set(lib BornAgain${name})
+
+# --- source and include files ---
+# Python-dependent source files
+file(GLOB source_files */*.cpp)
+file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
+
+target_include_directories(${lib}
+    PRIVATE
+    ${Python3_INCLUDES}
+)
+
+target_link_libraries(${lib}
+    PUBLIC
+    ${Python3_LIBRARIES}
+)
-- 
GitLab


From 8e73fd755d9321303205a21471aca01d018a2ad0 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Thu, 3 Feb 2022 12:02:22 +0100
Subject: [PATCH 10/85] Add PyTools/Sample/CMakeLists.txt

---
 PyTools/Sample/CMakeLists.txt | 64 +++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 PyTools/Sample/CMakeLists.txt

diff --git a/PyTools/Sample/CMakeLists.txt b/PyTools/Sample/CMakeLists.txt
new file mode 100644
index 00000000000..b8adcb949f5
--- /dev/null
+++ b/PyTools/Sample/CMakeLists.txt
@@ -0,0 +1,64 @@
+############################################################################
+# CMakeLists.txt file for building libBornAgainPyTools
+############################################################################
+
+# NOTE: The module is directly dependent on Python and Numpy C-API
+if(NOT BORNAGAIN_PYTHON)
+    return()
+endif()
+
+set(name PyImportMultiLayer)
+set(basename BornAgain${name})
+
+# --- source and include files ---
+# Python-dependent source files
+file(GLOB ${basename}_SRC_PY */*.cpp)
+file(GLOB ${basename}_INCLUDES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
+
+# --- compile properties ---
+set(${basename}_INCLUDE_DIRS_PUBLIC
+    ${CMAKE_SOURCE_DIR}
+    ${LibHeinz_INCLUDE_DIR}
+    )
+
+#-- build libraries for multiple Python versions
+message(STATUS "Define ${basename} for multiple Python versions "
+    "(${BORNAGAIN_PYTHON_PACKAGE_VERSIONS})...")
+
+foreach(pyminversion ${BORNAGAIN_PYTHON_PACKAGE_MINOR_VERSIONS})
+    if(NOT ${pyminversion} STREQUAL "<MAIN>")
+        set(pytag py3${pyminversion}) # eg. 'py38'
+        set(pysuffix _${pytag}) # eg. '_py38'
+        set(is_main NO)
+    else()
+        # the main version of the library has no Python tag
+        set(pytag "")
+        set(pysuffix "")
+        set(is_main YES)
+    endif()
+
+    # Python-dependent objects
+    set(objlib_py ${basename}_objects_py${pysuffix})
+    add_library(${objlib_py} OBJECT)
+    set_target_properties(${objlib_py} PROPERTIES
+        POSITION_INDEPENDENT_CODE ON
+        SOURCES "${${basename}_SRC_PY}"
+        )
+
+    target_compile_definitions(${objlib_py} PUBLIC BORNAGAIN_PYTHON)
+
+    target_include_directories(${objlib_py}
+        PUBLIC
+        ${CMAKE_SOURCE_DIR}
+        ${LibHeinz_INCLUDE_DIR}
+        PRIVATE
+        "${Python3_INCLUDE_DIRS${pysuffix}}"
+        "${Python3_NumPy_INCLUDE_DIRS${pysuffix}}"
+        )
+
+    target_link_libraries(${objlib_py}
+        PUBLIC
+        "${Python3_LIBRARY_RELEASE${pysuffix}}"
+        "BornAgainPyTools${pysuffix}")
+
+endforeach()
-- 
GitLab


From d812604db4b04f172250eb756e9d68b177267ce3 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:58:01 +0100
Subject: [PATCH 11/85] Add PyTools/Embed/Makefile [DEBUG]

This is used only for development.
---
 PyTools/Embed/Makefile | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 PyTools/Embed/Makefile

diff --git a/PyTools/Embed/Makefile b/PyTools/Embed/Makefile
new file mode 100644
index 00000000000..57a593ed7eb
--- /dev/null
+++ b/PyTools/Embed/Makefile
@@ -0,0 +1,28 @@
+.PHONY: all clean py
+
+all: py
+
+
+CC=gcc
+LINK=gcc
+CXX=g++
+
+CXX_INCLUDES=-I/usr/include/python3.9 -I$${HOME}/Projects/bornagain -I$${HOME}/.local/opt/libheinz/include
+CXX_LINKS=-lpython3.9
+
+BA_FLAGS=-DBORNAGAIN_PYTHON
+CXX_CFLAGS=-Wall -Wextra -Wpedantic -fsanitize=undefined ${BA_FLAGS} ${CXX_INCLUDES}
+CXX_LFLAGS= ${CXX_LINKS}  # -Wl,-verbose
+CPP_CFLAGS=-std=c++17
+
+py: PythonInterpreter.cpp PythonInterpreter.h PyObjectPtr.cpp PyObjectDecl.h
+	@echo "Making an embedded Python interpreter..."
+	${CXX} -c -fPIC Result.cpp ${CPP_CFLAGS} ${CXX_CFLAGS}
+	${CXX} -c -fPIC PythonInterpreter.cpp PyObjectPtr.cpp ${CPP_CFLAGS} ${CXX_CFLAGS}
+	${CXX} -shared -o libpythonembed.so PythonInterpreter.o PyObjectPtr.o Result.o ${CXX_CFLAGS}
+	${CXX} -o testEmbeddedPython.exe test_embedded_python.cpp ${CXX_CFLAGS} ${CPP_CFLAGS} -L. -lpythonembed ${CXX_LFLAGS} '-Wl,--disable-new-dtags,-rpath,$$ORIGIN'
+
+
+clean:
+	@echo "Cleaning all products..."
+	rm *.o *.out *.exe
-- 
GitLab


From 7a2d4ebd95bd29789a5ee05c1e7b95a1497ef6eb Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 1 Feb 2022 14:57:27 +0100
Subject: [PATCH 12/85] Sample/Multilayer/PyImport: Import a MultiLayer
 instance from a Python code

---
 .../Sample/Multilayer/ImportMultiLayer.cpp    | 73 +++++++++++++++++++
 PyTools/Sample/Multilayer/ImportMultiLayer.h  | 23 ++++++
 2 files changed, 96 insertions(+)
 create mode 100644 PyTools/Sample/Multilayer/ImportMultiLayer.cpp
 create mode 100644 PyTools/Sample/Multilayer/ImportMultiLayer.h

diff --git a/PyTools/Sample/Multilayer/ImportMultiLayer.cpp b/PyTools/Sample/Multilayer/ImportMultiLayer.cpp
new file mode 100644
index 00000000000..53c7f15d5d2
--- /dev/null
+++ b/PyTools/Sample/Multilayer/ImportMultiLayer.cpp
@@ -0,0 +1,73 @@
+#ifdef BORNAGAIN_PYTHON
+
+#include "PyTools/Embed/PyCore.h"
+#include "ImportMultiLayer.h"
+#include "Sample/Multilayer/MultiLayer.h"
+#include "PyTools/Embed/PythonInterpreter.h"  // BornAgain::importScript
+// SWIG runtime access for creating a MultiLayer instance from Python
+#include "auto/Wrap/swig_runtime.h"
+
+
+namespace PythonInterpreter
+{
+
+Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
+    const std::string& script, const std::string& functionName,
+    const std::string& path)
+{
+    PyObjectPtr tmpModule {PythonInterpreter::BornAgain::importScript(script, path)};
+
+    // locate the `get_simulation` function (it is an attribute of the module)
+    PyObject* pAddFn = PyObject_GetAttrString(tmpModule.ptr, functionName.c_str());
+    if (!pAddFn) {
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::BornAgain: "
+               << "Cannot locate the compiled function '" << functionName << "'";
+        return {Status(Status::Type::Error, msg_ss.str())};
+    }
+
+    // create a `MultiLayer` Python object via calling the function
+    PyObject* instance = PyObject_CallFunctionObjArgs(pAddFn, NULL);
+
+    // clean up
+    Py_DecRef(pAddFn);
+
+    if (!instance) {
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::BornAgain: "
+               << "Cannot call the function '" << functionName << "'";
+        return {Status(Status::Type::Error,
+                       PythonInterpreter::errorDescription(msg_ss.str()))};
+    }
+
+    // Construct a C++ object from the Python object
+    /* NOTE:
+       This conversion requires a SWIG-produced Python API.
+     */
+
+    void* argp1 = 0;
+    swig_type_info* pTypeInfo = SWIG_TypeQuery("MultiLayer *");
+
+    const int res = SWIG_ConvertPtr(instance, &argp1, pTypeInfo, 0);
+    if (!SWIG_IsOK(res)) {
+        Py_DecRef(instance);
+        std::stringstream msg_ss;
+        msg_ss << "PythonInterpreter::BornAgain: "
+               << "SWIG failed to extract a 'MultiLayer' instance "
+               << "via calling the function '" << functionName << "'";
+        return {Status(Status::Type::Error,
+                       PythonInterpreter::errorDescription(msg_ss.str()))};
+    }
+
+    MultiLayer* multilayer = reinterpret_cast<MultiLayer*>(argp1);
+    std::unique_ptr<MultiLayer> result_ptr(multilayer->clone());
+
+    Py_DecRef(instance);
+
+    // TODO: check if it is possible to move a unique_ptr
+    return {std::move(result_ptr)};
+}
+
+}  // namespace PythonInterpreter
+
+#endif // BORNAGAIN_PYTHON
diff --git a/PyTools/Sample/Multilayer/ImportMultiLayer.h b/PyTools/Sample/Multilayer/ImportMultiLayer.h
new file mode 100644
index 00000000000..f8731529504
--- /dev/null
+++ b/PyTools/Sample/Multilayer/ImportMultiLayer.h
@@ -0,0 +1,23 @@
+#ifndef SWIG
+
+#include <string>
+#include <memory>  // unique_ptr
+#include "PyTools/Embed/PyObjectDecl.h"
+#include "PyTools/Embed/Result.h"
+
+class MultiLayer;
+
+namespace PythonInterpreter
+{
+
+//! Creates a multi layer by running python code in embedded interpreter.
+//! @param script: Python script
+//! @param functionName: A function name in this script which produces a MultiLayer
+//! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
+Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
+    const std::string& script, const std::string& functionName,
+    const std::string& path = "");
+
+}  // namespace PythonInterpreter
+
+#endif // SWIG
-- 
GitLab


From c4515e4f478d9c3b1c3c554ae6547c578ad2d7d0 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Thu, 3 Feb 2022 11:24:10 +0100
Subject: [PATCH 13/85] GUI/View/Project/PyImportAssistant: Use
 PythonInterpreter functionality

Use PythonInterpreter functionality instead of using Python C-API directly.
---
 GUI/View/Project/PyImportAssistant.cpp | 42 ++++++++++++++------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index 58459747b16..97f20520155 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -30,7 +30,8 @@
 #include "GUI/View/Project/ProjectManager.h"
 #include "GUI/View/Tool/Globals.h"
 #include "Sample/Multilayer/MultiLayer.h"
-#include "Sample/Multilayer/PyImport.h"
+#include "PyTools/Embed/PythonInterpreter.h"  // listOfFunctions
+#include "PyTools/Sample/Multilayer/ImportMultiLayer.h"  // createMultiLayerFromPython
 #include <QApplication>
 #include <QFileDialog>
 #include <QTextStream>
@@ -101,21 +102,23 @@ QString readFile(const QString& fileName)
 
 std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QString& funcName)
 {
-    std::unique_ptr<MultiLayer> result;
-
     QApplication::setOverrideCursor(Qt::WaitCursor);
-    try {
-        result = Py::Import::createFromPython(snippet.toStdString(), funcName.toStdString(),
-                                              bornagainDir());
-    } catch (const std::exception& ex) {
+
+    Result<std::unique_ptr<MultiLayer>> result{
+        PythonInterpreter::createMultiLayerFromPython(
+            snippet.toStdString(), funcName.toStdString(),
+            bornagainDir())};
+
+    if (!result.valid()) {
         QApplication::restoreOverrideCursor();
         QString message("Exception thrown while executing Python code to create sample.\n\n");
-        QString details = QString::fromStdString(std::string(ex.what()));
-        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
+        QString details = QString::fromStdString(result.status.message);
+    DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
     }
+
     QApplication::restoreOverrideCursor();
 
-    return result;
+    return std::move(result.val);
 }
 
 //! Lets user select a function name which generates a MultiLayer.
@@ -148,22 +151,23 @@ QString selectPySampleFunction(const QStringList& funcNames)
 
 QString getPySampleFunctionName(const QString& snippet)
 {
-    QStringList funcList;
-
     QApplication::setOverrideCursor(Qt::WaitCursor);
-    try {
-        auto funcs = Py::Import::listOfFunctions(snippet.toStdString(), bornagainDir());
-        funcList = GUI::Util::String::fromStdStrings(funcs);
-    } catch (const std::exception& ex) {
+
+    Result<std::vector<std::string>> funcs_res {
+        PythonInterpreter::BornAgain::listOfFunctions(
+            snippet.toStdString(), bornagainDir())};
+
+    if(!funcs_res.valid())
+    {
         QApplication::restoreOverrideCursor();
         QString message("Exception thrown while acquiring functions from Python code.\n\n");
-        QString details = QString::fromStdString(std::string(ex.what()));
+        QString details = QString::fromStdString(funcs_res.status.message);
         DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
-
         return "";
     }
-    QApplication::restoreOverrideCursor();
 
+    QApplication::restoreOverrideCursor();
+    QStringList funcList {GUI::Util::String::fromStdStrings(funcs_res.val)};
     return selectPySampleFunction(funcList);
 }
 
-- 
GitLab


From b9d0f3884174a1042ff45ebd889252d7b3f042b3 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 21 Apr 2023 16:50:11 +0200
Subject: [PATCH 14/85] Adapt GUI/CMakeLists.txt

Also minor fix to compile flags.
---
 GUI/CMakeLists.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt
index 8f611c4327e..5c5c40fc54a 100644
--- a/GUI/CMakeLists.txt
+++ b/GUI/CMakeLists.txt
@@ -43,10 +43,15 @@ add_library(${lib} SHARED ${source_files} ${RC_SRCS} ${include_files} ${form_fil
 # switch OFF Qt debug output in any configuration except DEBUG
 target_compile_definitions(${lib} PUBLIC $<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG_OUTPUT>)
 
+if(BORNAGAIN_PYTHON)
+    target_compile_definitions(${lib} PRIVATE -DBORNAGAIN_PYTHON)
+endif()
+
 # --- external dependences ---------
 
 target_link_libraries(${lib}
     PUBLIC
+    BornAgainPyCore
     BornAgainSim
     BornAgainImg3D
     Qt6::Widgets
-- 
GitLab


From 601f9b7d7db533b0d1c19bede710165ade384e7b Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 17 Apr 2023 16:11:24 +0200
Subject: [PATCH 15/85] Adapt Device/Data/Datafield module

---
 Device/Data/Datafield.cpp | 53 ++++++++++++++++++++++++---------------
 Device/Data/Datafield.h   |  1 -
 2 files changed, 33 insertions(+), 21 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index aa03fee453c..af0c47bde4a 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -206,8 +206,17 @@ Datafield* Datafield::crop(double xmin, double xmax) const
 
 #ifdef BORNAGAIN_PYTHON
 
-PyObject* Datafield::npArray() const
+#include "PyTools/Embed/PyObjectDecl.h"
+#include "PyTools/Embed/PythonInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
+
+template <>
+PyObject* OutputData<double>::getArray() const
 {
+    // TODO: Thoroughly check this function regarding index manipulations
+
+    Status init{PythonInterpreter::Numpy::initialize()};
+    if (init.isError())
+        return nullptr;
     std::vector<size_t> dimensions;
     for (size_t i = 0; i < rank(); i++)
         dimensions.push_back(axis(i).size());
@@ -217,38 +226,42 @@ PyObject* Datafield::npArray() const
         std::swap(dimensions[0], dimensions[1]);
 
     // creating ndarray objects describing size of dimensions
-    npy_int ndim_numpy = (int)dimensions.size();
-    auto* ndimsizes_numpy = new npy_intp[dimensions.size()];
-    for (size_t i = 0; i < dimensions.size(); i++)
-        ndimsizes_numpy[i] = dimensions[i];
+    PyObjectPtr pyarray{PythonInterpreter::Numpy::arrayND(dimensions)};
+    if (!pyarray.valid())
+        return nullptr;
 
-    // creating standalone numpy array
-    PyObject* pyarray = PyArray_SimpleNew(ndim_numpy, ndimsizes_numpy, NPY_DOUBLE);
-    delete[] ndimsizes_numpy;
-    ASSERT(pyarray);
+    // get the pointer to the data buffer of the array (assumed to be C-contiguous)
+    Result<double*> data{PythonInterpreter::Numpy::getDataPtr(pyarray.ptr)};
 
-    // getting pointer to data buffer of numpy array
-    double* array_buffer = (double*)PyArray_DATA((PyArrayObject*)pyarray);
+    if (!data.valid())
+        return nullptr;
 
+    double* array_buffer = data.val;
+    const std::size_t idxMax = getAllocatedSize();
     // 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];
+        for (size_t idx = 0; idx < idxMax; ++idx) {
+            const std::vector<int> axes_indices = getAxesBinIndices(idx);
+            const std::size_t ax0_size = m_axes[0]->size(), ax1_size = m_axes[1]->size(),
+                              offset =
+                                  axes_indices[0] + ax0_size * (ax1_size - 1 - axes_indices[1]);
+            array_buffer[offset] = (*this)[idx];
+        }
+    } else if (rank() == 1) {
+        for (size_t idx = 0; idx < idxMax; ++idx) {
+            *array_buffer = (*this)[idx];
+            ++array_buffer;
         }
     } else {
-        for (size_t i = 0; i < size(); ++i)
-            *array_buffer++ = (*this)[i];
+        return nullptr;
     }
 
-    return pyarray;
+    // returns a _new_ reference; ie. caller is responsible for the ref-count
+    return pyarray.release();
 }
 
 #endif // BORNAGAIN_PYTHON
 
-
 Datafield* Datafield::xProjection() const
 {
     return create_xProjection(0, static_cast<int>(xAxis().size()) - 1);
diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index a468739a3c1..877fa8df262 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -15,7 +15,6 @@
 #ifndef BORNAGAIN_DEVICE_DATA_DATAFIELD_H
 #define BORNAGAIN_DEVICE_DATA_DATAFIELD_H
 
-#include "Base/Py/PyObject.h"
 #include <memory>
 #include <vector>
 
-- 
GitLab


From 4fa7b122fde9d675782879fc49190ceabab7eca4 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 17 Apr 2023 17:05:48 +0200
Subject: [PATCH 16/85] Rename 'PyTools' to 'PyCore'

---
 Device/Data/Datafield.cpp                                  | 4 ++--
 GUI/View/Project/PyImportAssistant.cpp                     | 4 ++--
 {PyTools => PyCore}/CMakeLists.txt                         | 0
 {PyTools => PyCore}/Embed/Makefile                         | 0
 {PyTools => PyCore}/Embed/PyCore.h                         | 2 +-
 {PyTools => PyCore}/Embed/PyObjectDecl.h                   | 0
 {PyTools => PyCore}/Embed/PyObjectPtr.cpp                  | 0
 {PyTools => PyCore}/Embed/PyObjectPtr.h                    | 0
 {PyTools => PyCore}/Embed/PythonInterpreter.cpp            | 0
 {PyTools => PyCore}/Embed/PythonInterpreter.h              | 0
 {PyTools => PyCore}/Embed/Result.cpp                       | 0
 {PyTools => PyCore}/Embed/Result.h                         | 0
 {PyTools => PyCore}/Sample/CMakeLists.txt                  | 0
 {PyTools => PyCore}/Sample/Multilayer/ImportMultiLayer.cpp | 4 ++--
 {PyTools => PyCore}/Sample/Multilayer/ImportMultiLayer.h   | 4 ++--
 15 files changed, 9 insertions(+), 9 deletions(-)
 rename {PyTools => PyCore}/CMakeLists.txt (100%)
 rename {PyTools => PyCore}/Embed/Makefile (100%)
 rename {PyTools => PyCore}/Embed/PyCore.h (97%)
 rename {PyTools => PyCore}/Embed/PyObjectDecl.h (100%)
 rename {PyTools => PyCore}/Embed/PyObjectPtr.cpp (100%)
 rename {PyTools => PyCore}/Embed/PyObjectPtr.h (100%)
 rename {PyTools => PyCore}/Embed/PythonInterpreter.cpp (100%)
 rename {PyTools => PyCore}/Embed/PythonInterpreter.h (100%)
 rename {PyTools => PyCore}/Embed/Result.cpp (100%)
 rename {PyTools => PyCore}/Embed/Result.h (100%)
 rename {PyTools => PyCore}/Sample/CMakeLists.txt (100%)
 rename {PyTools => PyCore}/Sample/Multilayer/ImportMultiLayer.cpp (95%)
 rename {PyTools => PyCore}/Sample/Multilayer/ImportMultiLayer.h (89%)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index af0c47bde4a..4309b20f374 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -206,8 +206,8 @@ Datafield* Datafield::crop(double xmin, double xmax) const
 
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyTools/Embed/PyObjectDecl.h"
-#include "PyTools/Embed/PythonInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
+#include "PyCore/Embed/PyObjectDecl.h"
+#include "PyCore/Embed/PythonInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
 
 template <>
 PyObject* OutputData<double>::getArray() const
diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index 97f20520155..d7bf56269ce 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -30,8 +30,8 @@
 #include "GUI/View/Project/ProjectManager.h"
 #include "GUI/View/Tool/Globals.h"
 #include "Sample/Multilayer/MultiLayer.h"
-#include "PyTools/Embed/PythonInterpreter.h"  // listOfFunctions
-#include "PyTools/Sample/Multilayer/ImportMultiLayer.h"  // createMultiLayerFromPython
+#include "PyCore/Embed/PythonInterpreter.h"  // listOfFunctions
+#include "PyCore/Sample/Multilayer/ImportMultiLayer.h"  // createMultiLayerFromPython
 #include <QApplication>
 #include <QFileDialog>
 #include <QTextStream>
diff --git a/PyTools/CMakeLists.txt b/PyCore/CMakeLists.txt
similarity index 100%
rename from PyTools/CMakeLists.txt
rename to PyCore/CMakeLists.txt
diff --git a/PyTools/Embed/Makefile b/PyCore/Embed/Makefile
similarity index 100%
rename from PyTools/Embed/Makefile
rename to PyCore/Embed/Makefile
diff --git a/PyTools/Embed/PyCore.h b/PyCore/Embed/PyCore.h
similarity index 97%
rename from PyTools/Embed/PyCore.h
rename to PyCore/Embed/PyCore.h
index 933c85ea937..10533b160a0 100644
--- a/PyTools/Embed/PyCore.h
+++ b/PyCore/Embed/PyCore.h
@@ -2,7 +2,7 @@
 //
 //  BornAgain: simulate and fit reflection and scattering
 //
-//! @file      PyTools/Embed/PyCore.h
+//! @file      PyCore/Embed/PyCore.h
 //! @brief     Includes python header and takes care of warnings.
 //!
 //! @homepage  http://www.bornagainproject.org
diff --git a/PyTools/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
similarity index 100%
rename from PyTools/Embed/PyObjectDecl.h
rename to PyCore/Embed/PyObjectDecl.h
diff --git a/PyTools/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
similarity index 100%
rename from PyTools/Embed/PyObjectPtr.cpp
rename to PyCore/Embed/PyObjectPtr.cpp
diff --git a/PyTools/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
similarity index 100%
rename from PyTools/Embed/PyObjectPtr.h
rename to PyCore/Embed/PyObjectPtr.h
diff --git a/PyTools/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
similarity index 100%
rename from PyTools/Embed/PythonInterpreter.cpp
rename to PyCore/Embed/PythonInterpreter.cpp
diff --git a/PyTools/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
similarity index 100%
rename from PyTools/Embed/PythonInterpreter.h
rename to PyCore/Embed/PythonInterpreter.h
diff --git a/PyTools/Embed/Result.cpp b/PyCore/Embed/Result.cpp
similarity index 100%
rename from PyTools/Embed/Result.cpp
rename to PyCore/Embed/Result.cpp
diff --git a/PyTools/Embed/Result.h b/PyCore/Embed/Result.h
similarity index 100%
rename from PyTools/Embed/Result.h
rename to PyCore/Embed/Result.h
diff --git a/PyTools/Sample/CMakeLists.txt b/PyCore/Sample/CMakeLists.txt
similarity index 100%
rename from PyTools/Sample/CMakeLists.txt
rename to PyCore/Sample/CMakeLists.txt
diff --git a/PyTools/Sample/Multilayer/ImportMultiLayer.cpp b/PyCore/Sample/Multilayer/ImportMultiLayer.cpp
similarity index 95%
rename from PyTools/Sample/Multilayer/ImportMultiLayer.cpp
rename to PyCore/Sample/Multilayer/ImportMultiLayer.cpp
index 53c7f15d5d2..4da4157ac24 100644
--- a/PyTools/Sample/Multilayer/ImportMultiLayer.cpp
+++ b/PyCore/Sample/Multilayer/ImportMultiLayer.cpp
@@ -1,9 +1,9 @@
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyTools/Embed/PyCore.h"
+#include "PyCore/Embed/PyCore.h"
 #include "ImportMultiLayer.h"
 #include "Sample/Multilayer/MultiLayer.h"
-#include "PyTools/Embed/PythonInterpreter.h"  // BornAgain::importScript
+#include "PyCore/Embed/PythonInterpreter.h"  // BornAgain::importScript
 // SWIG runtime access for creating a MultiLayer instance from Python
 #include "auto/Wrap/swig_runtime.h"
 
diff --git a/PyTools/Sample/Multilayer/ImportMultiLayer.h b/PyCore/Sample/Multilayer/ImportMultiLayer.h
similarity index 89%
rename from PyTools/Sample/Multilayer/ImportMultiLayer.h
rename to PyCore/Sample/Multilayer/ImportMultiLayer.h
index f8731529504..ac1714992ab 100644
--- a/PyTools/Sample/Multilayer/ImportMultiLayer.h
+++ b/PyCore/Sample/Multilayer/ImportMultiLayer.h
@@ -2,8 +2,8 @@
 
 #include <string>
 #include <memory>  // unique_ptr
-#include "PyTools/Embed/PyObjectDecl.h"
-#include "PyTools/Embed/Result.h"
+#include "PyCore/Embed/PyObjectDecl.h"
+#include "PyCore/Embed/Result.h"
 
 class MultiLayer;
 
-- 
GitLab


From 00e18178358aa5ba722f4e2ecc7b6b2474994b11 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 21 Apr 2023 14:19:22 +0200
Subject: [PATCH 17/85] rm unneeded PyCore/Sample/CMakeLists.txt

---
 PyCore/Sample/CMakeLists.txt | 64 ------------------------------------
 1 file changed, 64 deletions(-)
 delete mode 100644 PyCore/Sample/CMakeLists.txt

diff --git a/PyCore/Sample/CMakeLists.txt b/PyCore/Sample/CMakeLists.txt
deleted file mode 100644
index b8adcb949f5..00000000000
--- a/PyCore/Sample/CMakeLists.txt
+++ /dev/null
@@ -1,64 +0,0 @@
-############################################################################
-# CMakeLists.txt file for building libBornAgainPyTools
-############################################################################
-
-# NOTE: The module is directly dependent on Python and Numpy C-API
-if(NOT BORNAGAIN_PYTHON)
-    return()
-endif()
-
-set(name PyImportMultiLayer)
-set(basename BornAgain${name})
-
-# --- source and include files ---
-# Python-dependent source files
-file(GLOB ${basename}_SRC_PY */*.cpp)
-file(GLOB ${basename}_INCLUDES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
-
-# --- compile properties ---
-set(${basename}_INCLUDE_DIRS_PUBLIC
-    ${CMAKE_SOURCE_DIR}
-    ${LibHeinz_INCLUDE_DIR}
-    )
-
-#-- build libraries for multiple Python versions
-message(STATUS "Define ${basename} for multiple Python versions "
-    "(${BORNAGAIN_PYTHON_PACKAGE_VERSIONS})...")
-
-foreach(pyminversion ${BORNAGAIN_PYTHON_PACKAGE_MINOR_VERSIONS})
-    if(NOT ${pyminversion} STREQUAL "<MAIN>")
-        set(pytag py3${pyminversion}) # eg. 'py38'
-        set(pysuffix _${pytag}) # eg. '_py38'
-        set(is_main NO)
-    else()
-        # the main version of the library has no Python tag
-        set(pytag "")
-        set(pysuffix "")
-        set(is_main YES)
-    endif()
-
-    # Python-dependent objects
-    set(objlib_py ${basename}_objects_py${pysuffix})
-    add_library(${objlib_py} OBJECT)
-    set_target_properties(${objlib_py} PROPERTIES
-        POSITION_INDEPENDENT_CODE ON
-        SOURCES "${${basename}_SRC_PY}"
-        )
-
-    target_compile_definitions(${objlib_py} PUBLIC BORNAGAIN_PYTHON)
-
-    target_include_directories(${objlib_py}
-        PUBLIC
-        ${CMAKE_SOURCE_DIR}
-        ${LibHeinz_INCLUDE_DIR}
-        PRIVATE
-        "${Python3_INCLUDE_DIRS${pysuffix}}"
-        "${Python3_NumPy_INCLUDE_DIRS${pysuffix}}"
-        )
-
-    target_link_libraries(${objlib_py}
-        PUBLIC
-        "${Python3_LIBRARY_RELEASE${pysuffix}}"
-        "BornAgainPyTools${pysuffix}")
-
-endforeach()
-- 
GitLab


From c091c9d7712923924f6880d350be326c9cc4fd5a Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 24 Apr 2023 11:42:47 +0200
Subject: [PATCH 18/85] CMakeLists.txt<root>: add PyCore module

also minor improvements.
---
 CMakeLists.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e59c964668..b59ddabb0f2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,7 +32,7 @@ set(CMAKE_CXX_STANDARD 20)
 ### Options
 
 # options that are on by default (switch off for accelerated builds of limited scope)
-option(BORNAGAIN_PYTHON "Build with python support" ON)
+option(BORNAGAIN_PYTHON "Build with Python support" ON)
 option(BA_GUI "Build a graphical user interface" ON)
 option(BA_TIFF_SUPPORT "Tiff files read/write support" ON)
 option(BA_TESTS "Build tests" ON)
@@ -211,7 +211,7 @@ add_subdirectory(Fit/3rdparty)
 add_subdirectory(Fit)
 
 # other components
-set(CoreComponents "Base;Param;Sample;Resample;Device;Sim")
+set(CoreComponents "PyCore;Base;Param;Sample;Resample;Device;Sim")
 set(AllComponents "${CoreComponents};Img3D;GUI")
 
 # code analysis
-- 
GitLab


From 6e4b6d53e00624e2a25dbd278e2eb2cac1a28867 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 17 Apr 2023 17:41:14 +0200
Subject: [PATCH 19/85] rm old Python-based modules

---
 Base/Py/PyCore.h               |  46 --------
 Base/Py/PyObject.h             |  28 -----
 Base/Py/PyUtil.cpp             | 199 ---------------------------------
 Base/Py/PyUtil.h               |  57 ----------
 Sample/Multilayer/PyImport.cpp | 124 --------------------
 Sample/Multilayer/PyImport.h   |  51 ---------
 6 files changed, 505 deletions(-)
 delete mode 100644 Base/Py/PyCore.h
 delete mode 100644 Base/Py/PyObject.h
 delete mode 100644 Base/Py/PyUtil.cpp
 delete mode 100644 Base/Py/PyUtil.h
 delete mode 100644 Sample/Multilayer/PyImport.cpp
 delete mode 100644 Sample/Multilayer/PyImport.h

diff --git a/Base/Py/PyCore.h b/Base/Py/PyCore.h
deleted file mode 100644
index 9aa95b1f6f5..00000000000
--- a/Base/Py/PyCore.h
+++ /dev/null
@@ -1,46 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Base/Py/PyCore.h
-//! @brief     Includes python header and takes care of warnings.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef SWIG
-#error no need to expose this header to Swig
-#endif // SWIG
-#ifndef BORNAGAIN_BASE_PY_PYCORE_H
-#define BORNAGAIN_BASE_PY_PYCORE_H
-
-#ifdef BORNAGAIN_PYTHON
-
-#undef _POSIX_C_SOURCE
-#undef _XOPEN_SOURCE
-
-#ifdef __GNUC__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-
-#include <Python.h>
-#define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
-#define NO_IMPORT_ARRAY
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION // to prevent spurious warning
-#include <numpy/arrayobject.h>
-
-#include "auto/Wrap/swig_runtime.h"
-
-#ifdef __GNUC__
-#pragma GCC diagnostic pop
-#endif
-
-#endif // BORNAGAIN_PYTHON
-
-#endif // BORNAGAIN_BASE_PY_PYCORE_H
diff --git a/Base/Py/PyObject.h b/Base/Py/PyObject.h
deleted file mode 100644
index ed0aa1a34fb..00000000000
--- a/Base/Py/PyObject.h
+++ /dev/null
@@ -1,28 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Base/Py/PyObject.h
-//! @brief     PyObject forward declaration.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef SWIG
-#error no need to expose this header to Swig
-#endif // SWIG
-#ifndef BORNAGAIN_BASE_PY_PYOBJECT_H
-#define BORNAGAIN_BASE_PY_PYOBJECT_H
-
-#ifdef BORNAGAIN_PYTHON
-#ifndef PyObject_HEAD
-struct _object;
-typedef _object PyObject;
-#endif
-#endif
-
-#endif // BORNAGAIN_BASE_PY_PYOBJECT_H
diff --git a/Base/Py/PyUtil.cpp b/Base/Py/PyUtil.cpp
deleted file mode 100644
index 107ee4630cf..00000000000
--- a/Base/Py/PyUtil.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Base/Py/PyUtil.cpp
-//! @brief     IOmplements various functions from PyUtil namespace
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef BORNAGAIN_PYTHON
-
-#include "Base/Py/PyUtil.h"
-#include "Base/Py/PyCore.h"
-#include "Base/Util/Assert.h"
-#include "Base/Util/SysUtil.h"
-#include <iostream>
-#include <sstream>
-#include <stdexcept>
-
-std::string BaseUtil::Python::toString(PyObject* obj)
-{
-    std::string result;
-    PyObject* pyStr = PyUnicode_AsEncodedString(obj, "utf-8", "replace");
-    if (pyStr != nullptr) {
-        result = std::string(PyBytes_AsString(pyStr));
-        Py_DecRef(pyStr);
-    }
-    return result;
-}
-
-std::vector<std::string> BaseUtil::Python::toVectorString(PyObject* obj)
-{
-    std::vector<std::string> result;
-
-    if (PyTuple_Check(obj)) {
-        for (Py_ssize_t i = 0; i < PyTuple_Size(obj); i++) {
-            PyObject* value = PyTuple_GetItem(obj, i);
-            result.push_back(toString(value));
-        }
-    } else if (PyList_Check(obj)) {
-        for (Py_ssize_t i = 0; i < PyList_Size(obj); i++) {
-            PyObject* value = PyList_GetItem(obj, i);
-            result.push_back(toString(value));
-        }
-    } else
-        ASSERT(false); // unexpected object
-
-    return result;
-}
-
-std::string BaseUtil::Python::toString(char* c)
-{
-    if (c)
-        return c;
-    return "";
-}
-
-std::string BaseUtil::Python::toString(wchar_t* c)
-{
-    if (!c)
-        return "";
-    std::wstring wstr(c);
-    return std::string(wstr.begin(), wstr.end());
-}
-
-PyObject* BaseUtil::Python::import_bornagain(const std::vector<std::string>& paths)
-{
-    if (Py_IsInitialized())
-        return nullptr;
-
-    Py_InitializeEx(0); // like Py_Initialize, but skip registration of signal handlers
-
-    if (!paths.empty()) {
-        PyObject* sysPath = PySys_GetObject((char*)"path");
-        for (const std::string& path : paths)
-            PyList_Append(sysPath, PyString_FromString(path.c_str()));
-    }
-
-    // Stores signal handler before numpy's mess it up.
-    // This is to make ctrl-c working from terminal.
-#ifndef _WIN32
-    PyOS_sighandler_t sighandler;
-    sighandler = PyOS_getsig(SIGINT);
-#endif
-
-    PyObject* pmod = PyImport_ImportModule("bornagain");
-    if (!pmod) {
-        PyErr_Print();
-        throw std::runtime_error("Cannot load bornagain");
-    }
-
-    // restores single handler to make ctr-c alive.
-#ifndef _WIN32
-    PyOS_setsig(SIGINT, sighandler);
-#endif
-
-    return pmod;
-}
-
-std::string BaseUtil::Python::pythonRuntimeInfo()
-{
-    Py_InitializeEx(0);
-
-    std::stringstream result;
-
-    // Runtime environment
-    result << std::string(60, '=') << "\n";
-    result << "PATH: " << BaseUtil::System::getenv("PATH") << "\n";
-    result << "PYTHONPATH: " << BaseUtil::System::getenv("PYTHONPATH") << "\n";
-    result << "PYTHONHOME: " << BaseUtil::System::getenv("PYTHONHOME") << "\n";
-
-    // Embedded Python details
-    result << "Py_GetProgramName(): " << BaseUtil::Python::toString(Py_GetProgramName()) << "\n";
-    result << "Py_GetProgramFullPath(): " << BaseUtil::Python::toString(Py_GetProgramFullPath())
-           << "\n";
-    result << "Py_GetPath(): " << BaseUtil::Python::toString(Py_GetPath()) << "\n";
-    result << "Py_GetPythonHome(): " << BaseUtil::Python::toString(Py_GetPythonHome()) << "\n";
-
-    // Runtime Python's sys.path
-    PyObject* sysPath = PySys_GetObject((char*)"path");
-    auto content = BaseUtil::Python::toVectorString(sysPath);
-    result << "sys.path: ";
-    for (auto s : content)
-        result << s << ",";
-    result << "\n";
-
-    return result.str();
-}
-
-// Attempt to retrieve Python stack trace
-// https://stackoverflow.com/questions/1796510/accessing-a-python-traceback-from-the-c-api
-
-std::string BaseUtil::Python::pythonStackTrace()
-{
-    std::stringstream result;
-
-    if (PyErr_Occurred()) {
-        PyObject *ptype, *pvalue, *ptraceback, *pystr;
-
-        PyErr_Fetch(&ptype, &pvalue, &ptraceback);
-        pystr = PyObject_Str(pvalue);
-        if (char* str = PyString_AsString(pystr))
-            result << std::string(str) << "\n";
-
-        PyObject* module_name = PyString_FromString("traceback");
-        PyObject* pyth_module = PyImport_Import(module_name);
-        Py_DecRef(module_name);
-
-        if (pyth_module) {
-            result << "\n";
-            PyObject* pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
-            if (pyth_func && PyCallable_Check(pyth_func)) {
-                PyObject* pyth_val =
-                    PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
-                if (pyth_val) {
-                    pystr = PyObject_Str(pyth_val);
-                    if (char* str = PyString_AsString(pystr))
-                        result << std::string(str);
-                    Py_DecRef(pyth_val);
-                }
-            }
-            result << "\n";
-        }
-    }
-
-    result << "\n";
-    result << pythonRuntimeInfo();
-    result << "\n";
-
-    return result.str();
-}
-
-PyObject* BaseUtil::Python::createNumpyArray(const std::vector<double>& data)
-{
-    const size_t ndim(1);
-    npy_int ndim_numpy = ndim;
-    auto* ndimsizes_numpy = new npy_intp[ndim];
-    ndimsizes_numpy[0] = data.size();
-
-    // creating standalone numpy array
-    PyObject* pyarray = PyArray_SimpleNew(ndim_numpy, ndimsizes_numpy, NPY_DOUBLE);
-    delete[] ndimsizes_numpy;
-    ASSERT(pyarray);
-
-    // getting pointer to data buffer of numpy array
-    double* array_buffer = (double*)PyArray_DATA((PyArrayObject*)pyarray);
-
-    for (size_t index = 0; index < data.size(); ++index)
-        *array_buffer++ = data[index];
-
-    return pyarray;
-}
-
-#endif // BORNAGAIN_PYTHON
diff --git a/Base/Py/PyUtil.h b/Base/Py/PyUtil.h
deleted file mode 100644
index 54846d5ee2c..00000000000
--- a/Base/Py/PyUtil.h
+++ /dev/null
@@ -1,57 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Base/Py/PyUtil.h
-//! @brief     Defines PyUtil namespace
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef SWIG
-#error no need to expose this header to Swig
-#endif // SWIG
-#ifndef BORNAGAIN_BASE_PY_PYUTIL_H
-#define BORNAGAIN_BASE_PY_PYUTIL_H
-
-#ifdef BORNAGAIN_PYTHON
-
-#include "Base/Py/PyObject.h"
-#include <memory>
-#include <string>
-#include <vector>
-
-class MultiLayer;
-
-namespace BaseUtil::Python {
-
-//! Converts PyObject into string, if possible, or throws exception.
-std::string toString(PyObject* obj);
-
-//! Converts PyObject into vector of strings, if possible, or throws exception.
-std::vector<std::string> toVectorString(PyObject* obj);
-
-//! Converts char to string. In the case of nullptr will return an empty string.
-std::string toString(char* c);
-std::string toString(wchar_t* c);
-
-//! Imports BornAgain from given location. If path is empty, tries to rely on PYTHONPATH.
-PyObject* import_bornagain(const std::vector<std::string>& paths = {});
-
-//! Returns multi-line string representing PATH, PYTHONPATH, sys.path and other info.
-std::string pythonRuntimeInfo();
-
-//! Returns string representing python stack trace.
-std::string pythonStackTrace();
-
-PyObject* createNumpyArray(const std::vector<double>& data);
-
-} // namespace BaseUtil::Python
-
-#endif // BORNAGAIN_PYTHON
-
-#endif // BORNAGAIN_BASE_PY_PYUTIL_H
diff --git a/Sample/Multilayer/PyImport.cpp b/Sample/Multilayer/PyImport.cpp
deleted file mode 100644
index 962ea3407ab..00000000000
--- a/Sample/Multilayer/PyImport.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Sample/Multilayer/PyImport.cpp
-//! @brief     Implements PyImport namespace
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef BORNAGAIN_PYTHON
-
-#include "Sample/Multilayer/PyImport.h"
-#include "Base/Py/PyCore.h"
-#include "Base/Py/PyUtil.h"
-#include "Sample/Multilayer/MultiLayer.h"
-
-namespace {
-
-std::string error_description(const std::string& title)
-{
-    std::stringstream buf;
-    buf << title << "\n";
-    buf << BaseUtil::Python::pythonStackTrace() << "\n";
-    return buf.str();
-}
-
-} // namespace
-
-std::unique_ptr<MultiLayer> Py::Import::createFromPython(const std::string& script,
-                                                         const std::string& functionName,
-                                                         const std::string& path)
-{
-    BaseUtil::Python::import_bornagain({path});
-
-    PyObject* pCompiledFn = Py_CompileString(script.c_str(), "", Py_file_input);
-    if (!pCompiledFn)
-        throw std::runtime_error(error_description("Cannot compile snippet"));
-
-    // create a module
-    PyObject* pModule = PyImport_ExecCodeModule((char*)"test", pCompiledFn);
-    if (!pModule) {
-        Py_DecRef(pCompiledFn);
-        throw std::runtime_error(error_description("Cannot exec module"));
-    }
-
-    // locate the "get_simulation" function (it's an attribute of the module)
-    PyObject* pAddFn = PyObject_GetAttrString(pModule, functionName.c_str());
-    if (!pAddFn)
-        throw std::runtime_error("Cannot find function " + functionName);
-
-    PyObject* instance = PyObject_CallFunctionObjArgs(pAddFn, NULL);
-    if (!instance) {
-        Py_DecRef(pAddFn);
-        Py_DecRef(pModule);
-        Py_DecRef(pCompiledFn);
-        throw std::runtime_error(error_description("Failed running function " + functionName));
-    }
-
-    // clean up
-    Py_DecRef(pAddFn);
-    Py_DecRef(pModule);
-    Py_DecRef(pCompiledFn);
-
-    void* argp1 = 0;
-    swig_type_info* pTypeInfo = SWIG_TypeQuery("MultiLayer *");
-
-    const int res = SWIG_ConvertPtr(instance, &argp1, pTypeInfo, 0);
-    if (!SWIG_IsOK(res)) {
-        Py_DecRef(instance);
-        throw std::runtime_error("SWIG failed to extract a MultiLayer.");
-    }
-
-    MultiLayer* sample = reinterpret_cast<MultiLayer*>(argp1);
-    std::unique_ptr<MultiLayer> result(sample->clone());
-
-    Py_DecRef(instance);
-
-    return result;
-}
-
-std::vector<std::string> Py::Import::listOfFunctions(const std::string& script,
-                                                     const std::string& path)
-{
-    BaseUtil::Python::import_bornagain({path});
-
-    PyObject* pCompiledFn = Py_CompileString(script.c_str(), "", Py_file_input);
-    if (!pCompiledFn)
-        throw std::runtime_error(error_description("Cannot compile snippet"));
-
-    // create a module
-    PyObject* pModule = PyImport_ExecCodeModule((char*)"test", pCompiledFn);
-    if (!pModule) {
-        Py_DecRef(pCompiledFn);
-        throw std::runtime_error(error_description("Cannot exec module"));
-    }
-
-    PyObject* dict = PyModule_GetDict(pModule);
-    if (!dict)
-        throw std::runtime_error("Cannot get dictionary from module");
-
-    std::vector<std::string> result;
-    PyObject *key, *value;
-    Py_ssize_t pos = 0;
-    while (PyDict_Next(dict, &pos, &key, &value)) {
-        if (PyCallable_Check(value)) {
-            std::string func_name = BaseUtil::Python::toString(key);
-            if (func_name.find("__") == std::string::npos)
-                result.push_back(func_name);
-        }
-    }
-
-    Py_DecRef(dict);
-    Py_DecRef(pModule);
-    Py_DecRef(pCompiledFn);
-
-    return result;
-}
-
-#endif // BORNAGAIN_PYTHON
diff --git a/Sample/Multilayer/PyImport.h b/Sample/Multilayer/PyImport.h
deleted file mode 100644
index 134e97ed82f..00000000000
--- a/Sample/Multilayer/PyImport.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      Sample/Multilayer/PyImport.h
-//! @brief     Defines PyImport namespace
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2018
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifdef SWIG
-#error no need to expose this header to Swig
-#endif // SWIG
-#ifndef BORNAGAIN_SAMPLE_MULTILAYER_PYIMPORT_H
-#define BORNAGAIN_SAMPLE_MULTILAYER_PYIMPORT_H
-
-#ifdef BORNAGAIN_PYTHON
-
-#include <memory>
-#include <string>
-#include <vector>
-
-class MultiLayer;
-
-namespace Py::Import {
-
-////! Returns directory name
-// std::string bornagainPythonDir();
-
-//! Creates a multi layer by running python code in embedded interpreter.
-//! @param script: Python script
-//! @param functionName: A function name in this script which produces a MultiLayer
-//! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-std::unique_ptr<MultiLayer> createFromPython(const std::string& script,
-                                             const std::string& functionName,
-                                             const std::string& path = "");
-
-//! Returns list of functions defined in the script.
-//! @param script: Python script
-//! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-std::vector<std::string> listOfFunctions(const std::string& script, const std::string& path = "");
-
-} // namespace Py::Import
-
-#endif // BORNAGAIN_PYTHON
-
-#endif // BORNAGAIN_SAMPLE_MULTILAYER_PYIMPORT_H
-- 
GitLab


From 78612f25cf3c895b158b3d9b0900125ba92f5774 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 17 Apr 2023 17:26:17 +0200
Subject: [PATCH 20/85] fix PyCore/CMakeLists.txt

---
 PyCore/CMakeLists.txt | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/PyCore/CMakeLists.txt b/PyCore/CMakeLists.txt
index 18f6637af92..59bb4a00a2c 100644
--- a/PyCore/CMakeLists.txt
+++ b/PyCore/CMakeLists.txt
@@ -8,17 +8,22 @@ if(NOT BORNAGAIN_PYTHON)
 endif()
 
 set(component PyCore)
-set(name PyCore)
-set(lib BornAgain${name})
+set(lib BornAgain${component})
 
 # --- source and include files ---
 # Python-dependent source files
-file(GLOB source_files */*.cpp)
-file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
+file(GLOB_RECURSE source_files */*.cpp)
+file(GLOB_RECURSE include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
+
+# --- make the library ---
+MakeLib(${component} ${lib} "" ${BORNAGAIN_PYTHON})
 
 target_include_directories(${lib}
     PRIVATE
-    ${Python3_INCLUDES}
+    ${CMAKE_SOURCE_DIR}
+    ${LibHeinz_INCLUDE_DIR}
+    ${Python3_INCLUDE_DIRS}
+    ${Python3_NumPy_INCLUDE_DIRS}
 )
 
 target_link_libraries(${lib}
-- 
GitLab


From 78a170c698a8877353ff06f6481ffcc1a80c87f7 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 24 Apr 2023 13:04:41 +0200
Subject: [PATCH 21/85] PyCore/Embed/PythonInterpreter.cpp: fix the return type
 of `_init_numpy` function

---
 PyCore/Embed/PythonInterpreter.cpp | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 50df04d84fc..94fe051e2db 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -34,7 +34,7 @@ namespace {
 //-- Auxiliary functions
 
 // thin wrapper to initialize Numpy
-int _init_numpy()
+std::nullptr_t _init_numpy(int& status)
 {
     if(!PyArray_API) {
         /* To use Numpy Array C-API from an extension module,
@@ -44,12 +44,16 @@ int _init_numpy()
            This function imports the module where the function-pointer table
            is stored and points the correct variable to it.
         */
-        // NOTE: import_array returns `NULL` on failure.
+        // NOTE: Numpy's `import_array` returns `NULL` on failure;
+        // hence return type must be `std::nullptr_t`.
+        status = 0;
         import_array();
-        return 1; // Numpy Array API loaded successfully
+        status = 1; // Numpy Array API loaded successfully
+        return NULL;
     }
 
-    return 2;  // Numpy Array API is already loaded
+    status = 2; // Numpy Array API is already loaded
+    return NULL;
 }
 
 
@@ -519,7 +523,8 @@ Status PythonInterpreter::Numpy::initialize() {
     if(py_status.isError())
         return py_status;
 
-    const int res {_init_numpy()};
+    int res;
+    _init_numpy(res);
 
     switch(res)
     {
-- 
GitLab


From b32426b9b81163256feedb4c863da113b08437d1 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 17 Apr 2023 18:02:33 +0200
Subject: [PATCH 22/85] Adapt MakeLib.cmake

---
 cmake/BornAgain/MakeLib.cmake | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/cmake/BornAgain/MakeLib.cmake b/cmake/BornAgain/MakeLib.cmake
index db4348fc3e7..322fdfc01c4 100644
--- a/cmake/BornAgain/MakeLib.cmake
+++ b/cmake/BornAgain/MakeLib.cmake
@@ -1,8 +1,13 @@
 # Configure one component library.
 
-function(MakeLib name lib tmpdir withPython)
+function(MakeLib name lib swigtmpdir withPython)
+
+    # NOTE: `source_files` and `include_files` are defined in the parent scope
+
+    string(STRIP "${swigtmpdir}" swigtmpdir)
+
+    if(withPython AND swigtmpdir)
 
-    if(withPython)
         if((CLANG) OR (GCC))
             # suppress warnings from auto-generated code (last updated for Swig 4.0.1)
             set_source_files_properties(${AUTO_DIR}/lib${lib}_wrap.cpp
@@ -52,14 +57,22 @@ function(MakeLib name lib tmpdir withPython)
         set_target_properties(${lib} PROPERTIES LINK_FLAGS ${link_flags})
     endif()
 
-    if(withPython)
-        SwigLib(${name} ${lib} ${tmpdir})
-        if(BA_PY_PACKAGE)
-            # add the BornAgain library to the Python wheel
-            add_library_to_wheel(${lib})
-        endif()
+    if(BORNAGAIN_PYTHON)
+        target_compile_definitions(${lib} PRIVATE -DBORNAGAIN_PYTHON)
+    endif()
+
+    # SWIG-produced interface
+    if(swigtmpdir)
+        SwigLib(${name} ${lib} ${swigtmpdir})
+    endif()
+
+    # Python package
+    if(BA_PY_PACKAGE)
+        # add the BornAgain library to the Python wheel
+        add_library_to_wheel(${lib})
     endif()
 
+    # installation
     install(TARGETS ${lib}
         LIBRARY DESTINATION ${destination_lib} COMPONENT Libraries
         RUNTIME DESTINATION ${destination_lib} COMPONENT Libraries)
-- 
GitLab


From 140937ee04ae932f096e7811087d5b0b71ed931c Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 18 Apr 2023 16:48:47 +0200
Subject: [PATCH 23/85] Adapt MakePythonWheel

---
 cmake/multipython/MakePythonWheel.cmake | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/cmake/multipython/MakePythonWheel.cmake b/cmake/multipython/MakePythonWheel.cmake
index 89d94cf0198..8cd35a71e6c 100644
--- a/cmake/multipython/MakePythonWheel.cmake
+++ b/cmake/multipython/MakePythonWheel.cmake
@@ -141,11 +141,21 @@ function(add_library_to_wheel lib)
 
     # copy the shared library and its Python interface to the Python wheel folder
     set(_dst ${BA_PY_LIBRARY_OUTPUT_DIR}/)
+
     add_custom_command(TARGET ${lib}
         POST_BUILD
         COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${lib}>" ${_dst}/${libfilename}
-        COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_PROPERTY:${lib},_LIBRARY_PY>" ${_dst}
         COMMENT "Add library ${lib} to the Python wheel..."
+    )
+
+    get_target_property(lib_LIBRARY_PY ${lib} _LIBRARY_PY)
+    string(STRIP ${lib_LIBRARY_PY} lib_LIBRARY_PY)
+    if(lib_LIBRARY_PY)
+        add_custom_command(TARGET ${lib}
+            POST_BUILD
+            COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_PROPERTY:${lib},_LIBRARY_PY>" ${_dst}
+            COMMENT "Add Python wrapper-script of library ${lib} to the Python wheel..."
         )
+    endif()
 
 endfunction()
-- 
GitLab


From 589ca747df1d23bbe9a393271db29b2dc02e93cf Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 18 Apr 2023 17:10:59 +0200
Subject: [PATCH 24/85] Adapt Device/CMakeLists.txt

---
 Device/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Device/CMakeLists.txt b/Device/CMakeLists.txt
index 07952cf0864..929a4a9bd76 100644
--- a/Device/CMakeLists.txt
+++ b/Device/CMakeLists.txt
@@ -21,6 +21,7 @@ set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
 target_link_libraries(${lib}
     PUBLIC
     BornAgainBase
+    BornAgainPyCore
     BornAgainFit
     BornAgainParam
     BornAgainSample
-- 
GitLab


From cecd57eb19c9034e0e207372a91b01bbe27b1d1c Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 17 Apr 2023 18:02:25 +0200
Subject: [PATCH 25/85] Adapt Device/Data/Datafield module

---
 Device/Data/Datafield.cpp | 24 +++++++++---------------
 Device/Data/Datafield.h   |  2 ++
 2 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index 4309b20f374..336b82c9d57 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -15,7 +15,6 @@
 #include "Device/Data/Datafield.h"
 #include "Base/Axis/Frame.h"
 #include "Base/Axis/Scale.h"
-#include "Base/Py/PyCore.h"
 #include "Base/Util/Assert.h"
 #include <algorithm>
 
@@ -206,11 +205,9 @@ Datafield* Datafield::crop(double xmin, double xmax) const
 
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyCore/Embed/PyObjectDecl.h"
 #include "PyCore/Embed/PythonInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
 
-template <>
-PyObject* OutputData<double>::getArray() const
+PyObject* Datafield::npArray() const
 {
     // TODO: Thoroughly check this function regarding index manipulations
 
@@ -237,21 +234,18 @@ PyObject* OutputData<double>::getArray() const
         return nullptr;
 
     double* array_buffer = data.val;
-    const std::size_t idxMax = getAllocatedSize();
+
     // filling numpy array with output_data
     if (rank() == 2) {
-        for (size_t idx = 0; idx < idxMax; ++idx) {
-            const std::vector<int> axes_indices = getAxesBinIndices(idx);
-            const std::size_t ax0_size = m_axes[0]->size(), ax1_size = m_axes[1]->size(),
-                              offset =
-                                  axes_indices[0] + ax0_size * (ax1_size - 1 - axes_indices[1]);
-            array_buffer[offset] = (*this)[idx];
+        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 idx = 0; idx < idxMax; ++idx) {
-            *array_buffer = (*this)[idx];
-            ++array_buffer;
-        }
+        for (size_t i = 0; i < size(); ++i)
+            *array_buffer++ = (*this)[i];
     } else {
         return nullptr;
     }
diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index 877fa8df262..9acb6ba3f72 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -18,6 +18,8 @@
 #include <memory>
 #include <vector>
 
+#include "PyCore/Embed/PyObjectDecl.h"
+
 using std::size_t;
 
 class Scale;
-- 
GitLab


From 5a76608764a1e961124062a51f34ef88143b2496 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 21 Apr 2023 16:51:08 +0200
Subject: [PATCH 26/85] MakeLib: simplify interface and avoid global variables

---
 Base/CMakeLists.txt           | 14 ++++++++++----
 Device/CMakeLists.txt         | 14 ++++++++++----
 Fit/CMakeLists.txt            | 14 ++++++++++----
 Param/CMakeLists.txt          | 14 ++++++++++----
 PyCore/CMakeLists.txt         | 11 ++++++++++-
 Resample/CMakeLists.txt       | 14 ++++++++++----
 Sample/CMakeLists.txt         | 14 ++++++++++----
 Sim/CMakeLists.txt            | 14 ++++++++++----
 cmake/BornAgain/MakeLib.cmake | 24 +++++++++++++++++++++---
 9 files changed, 101 insertions(+), 32 deletions(-)

diff --git a/Base/CMakeLists.txt b/Base/CMakeLists.txt
index 9c145e15c68..172f74d8d90 100644
--- a/Base/CMakeLists.txt
+++ b/Base/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+#--- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependences ---
 
diff --git a/Device/CMakeLists.txt b/Device/CMakeLists.txt
index 929a4a9bd76..d0159f8470e 100644
--- a/Device/CMakeLists.txt
+++ b/Device/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependencies ---
 
diff --git a/Fit/CMakeLists.txt b/Fit/CMakeLists.txt
index 6cbc90450d0..276a7b279bd 100644
--- a/Fit/CMakeLists.txt
+++ b/Fit/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependences ---
 
diff --git a/Param/CMakeLists.txt b/Param/CMakeLists.txt
index 5faa98a59cd..be8c78debc6 100644
--- a/Param/CMakeLists.txt
+++ b/Param/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependences ---
 
diff --git a/PyCore/CMakeLists.txt b/PyCore/CMakeLists.txt
index 59bb4a00a2c..2ad73e555ce 100644
--- a/PyCore/CMakeLists.txt
+++ b/PyCore/CMakeLists.txt
@@ -15,8 +15,17 @@ set(lib BornAgain${component})
 file(GLOB_RECURSE source_files */*.cpp)
 file(GLOB_RECURSE include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR ""
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
+
 # --- make the library ---
-MakeLib(${component} ${lib} "" ${BORNAGAIN_PYTHON})
+MakeLib(${lib})
 
 target_include_directories(${lib}
     PRIVATE
diff --git a/Resample/CMakeLists.txt b/Resample/CMakeLists.txt
index b93d3c75fdf..a94ffb57b5b 100644
--- a/Resample/CMakeLists.txt
+++ b/Resample/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependences ---
 
diff --git a/Sample/CMakeLists.txt b/Sample/CMakeLists.txt
index a9c71c09920..b21d7ebaac4 100644
--- a/Sample/CMakeLists.txt
+++ b/Sample/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependences ---
 
diff --git a/Sim/CMakeLists.txt b/Sim/CMakeLists.txt
index 4812c7c587a..27d5790dde6 100644
--- a/Sim/CMakeLists.txt
+++ b/Sim/CMakeLists.txt
@@ -10,11 +10,17 @@ set(lib BornAgain${component})
 file(GLOB source_files */*.cpp)
 file(GLOB include_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} */*.h)
 
-# --- make the library ---
-
-MakeLib(${component} ${lib} ${CMAKE_CURRENT_BINARY_DIR}/Wrap ${BORNAGAIN_PYTHON})
+# --- define target properties ---
+add_library(${lib} SHARED)
+set_target_properties(${lib} PROPERTIES
+    COMPONENT_NAME ${component}
+    SWIG_TMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/Wrap"
+    SOURCE_FILES "${source_files}"
+    INCLUDE_FILES "${include_files}"
+    EMBED_PYTHON ${BORNAGAIN_PYTHON})
 
-set(${lib}_LIBRARY ${lib} PARENT_SCOPE)
+# --- make the library ---
+MakeLib(${lib})
 
 # --- external dependences ---
 
diff --git a/cmake/BornAgain/MakeLib.cmake b/cmake/BornAgain/MakeLib.cmake
index 322fdfc01c4..ecf4520b05d 100644
--- a/cmake/BornAgain/MakeLib.cmake
+++ b/cmake/BornAgain/MakeLib.cmake
@@ -1,8 +1,13 @@
 # Configure one component library.
 
-function(MakeLib name lib swigtmpdir withPython)
+function(MakeLib lib)
 
-    # NOTE: `source_files` and `include_files` are defined in the parent scope
+    # get target properties
+    get_target_property(name ${lib} COMPONENT_NAME)
+    get_target_property(swigtmpdir ${lib} SWIG_TMP_DIR)
+    get_target_property(withPython ${lib} EMBED_PYTHON)
+    get_target_property(source_files ${lib} SOURCE_FILES)
+    get_target_property(include_files ${lib} INCLUDE_FILES)
 
     string(STRIP "${swigtmpdir}" swigtmpdir)
 
@@ -22,8 +27,8 @@ function(MakeLib name lib swigtmpdir withPython)
         list(APPEND source_files ${AUTO_DIR}/lib${lib}_wrap.cpp)
     endif()
 
-    add_library(${lib} SHARED ${source_files})
     set_target_properties(${lib} PROPERTIES
+        SOURCES "${source_files}"
         PREFIX ${libprefix}
         SUFFIX ${libsuffix}
         OUTPUT_NAME ${lib}
@@ -72,6 +77,19 @@ function(MakeLib name lib swigtmpdir withPython)
         add_library_to_wheel(${lib})
     endif()
 
+    if(WIN32 AND BORNAGAIN_PYTHON)
+        # python in windows required .pyd extension for the library name
+        install(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}.pyd
+            DESTINATION ${destination_python} COMPONENT Libraries)
+        add_custom_command(
+            TARGET ${lib}
+            POST_BUILD
+            COMMAND ${CMAKE_COMMAND} -E copy
+            ${CMAKE_BINARY_DIR}/bin/${libprefix}${lib}${libsuffix}
+            ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}${libsuffix}
+        )
+    endif()
+
     # installation
     install(TARGETS ${lib}
         LIBRARY DESTINATION ${destination_lib} COMPONENT Libraries
-- 
GitLab


From 67369d953b20ba8e9ea03e0134ac482a1ea4440f Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 21 Apr 2023 14:31:07 +0200
Subject: [PATCH 27/85] SwigLib: move compile-flag setting and Windows
 post-build and installation commands to MakeLib

---
 cmake/BornAgain/SwigLib.cmake | 30 +++++++-----------------------
 1 file changed, 7 insertions(+), 23 deletions(-)

diff --git a/cmake/BornAgain/SwigLib.cmake b/cmake/BornAgain/SwigLib.cmake
index c9491a3a894..e53614d9a02 100644
--- a/cmake/BornAgain/SwigLib.cmake
+++ b/cmake/BornAgain/SwigLib.cmake
@@ -1,17 +1,17 @@
 # Configure Python bindings for one component library.
 # Called from function MakeLib.
 
-function(SwigLib name lib tmpdir)
+function(SwigLib name lib swigtmpdir)
 
     if(NOT BORNAGAIN_PYTHON)
         message(FATAL_ERROR "Function SwigLib called though BORNAGAIN_PYTHON=false")
     endif()
 
-    message(STATUS "SwigLib ${name}: ${lib} ${tmpdir}")
+    message(STATUS "SwigLib ${name}: ${lib} ${swigtmpdir}")
 
     if(CONFIGURE_BINDINGS)
 
-        file(MAKE_DIRECTORY ${tmpdir})
+        file(MAKE_DIRECTORY ${swigtmpdir})
 
         # static (manually written) input files
         set(swig_dependencies
@@ -30,19 +30,19 @@ function(SwigLib name lib tmpdir)
         #     Please keep -Werror, in order not to overlook critical warnings.
         #     Dispensable warnings are disabled in Wrap/Swig/warnings.i.
         #     Joachim, oct20.
-        set(SWIG_FLAGS "-c++;-python;-Werror;-o;${AUTO_DIR}/lib${lib}_wrap.cpp;-outdir;${tmpdir}"
+        set(SWIG_FLAGS "-c++;-python;-Werror;-o;${AUTO_DIR}/lib${lib}_wrap.cpp;-outdir;${swigtmpdir}"
             ";-I${LibHeinz_INCLUDE_DIR}"
             ";-I${CMAKE_SOURCE_DIR};-I${CMAKE_BINARY_DIR}/inc")
 
         add_custom_command(
             OUTPUT ${AUTO_DIR}/lib${lib}.py
             COMMAND ${Python3_EXECUTABLE} ${SWIG_DIR}/tweaks.py
-                   ${tmpdir}/lib${lib}.py
+                   ${swigtmpdir}/lib${lib}.py
                    ${AUTO_DIR}/lib${lib}.py
-            DEPENDS ${tmpdir}/lib${lib}.py
+            DEPENDS ${swigtmpdir}/lib${lib}.py
             )
         add_custom_command(
-            OUTPUT ${tmpdir}/lib${lib}.py
+            OUTPUT ${swigtmpdir}/lib${lib}.py
                    ${AUTO_DIR}/lib${lib}_wrap.h
                    ${AUTO_DIR}/lib${lib}_wrap.cpp
             COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} ${SWIG_DIR}/lib${lib}.i
@@ -68,26 +68,10 @@ function(SwigLib name lib tmpdir)
 
     add_dependencies(${lib} ${lib}_python)
 
-    target_compile_definitions(${lib} PUBLIC -DBORNAGAIN_PYTHON)
     target_include_directories(${lib} PRIVATE ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS})
     target_link_libraries(${lib} PRIVATE ${Python3_LIBRARIES})
 
     install(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${lib}.py
         DESTINATION ${destination_python} COMPONENT Libraries) # required by swig
 
-    if(WIN32)
-        # python in windows required .pyd extension for the library name
-        if(BORNAGAIN_PYTHON)
-            install(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}.pyd
-                DESTINATION ${destination_python} COMPONENT Libraries)
-            add_custom_command(
-                TARGET ${lib}
-                POST_BUILD
-                COMMAND ${CMAKE_COMMAND} -E copy
-                ${CMAKE_BINARY_DIR}/bin/${libprefix}${lib}${libsuffix}
-                ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}${libsuffix}
-                )
-        endif()
-    endif()
-
 endfunction()
-- 
GitLab


From c1f27b82d6afd7d30c624da4b695d9c015c31b53 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 21 Apr 2023 17:08:33 +0200
Subject: [PATCH 28/85] SwigLib: minor improvements, add comments and TODO

---
 cmake/BornAgain/SwigLib.cmake | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/cmake/BornAgain/SwigLib.cmake b/cmake/BornAgain/SwigLib.cmake
index e53614d9a02..2c76ed79543 100644
--- a/cmake/BornAgain/SwigLib.cmake
+++ b/cmake/BornAgain/SwigLib.cmake
@@ -3,6 +3,7 @@
 
 function(SwigLib name lib swigtmpdir)
 
+    # TODO: Why BORNAGAIN_PYTHON is needed for Swig API?
     if(NOT BORNAGAIN_PYTHON)
         message(FATAL_ERROR "Function SwigLib called though BORNAGAIN_PYTHON=false")
     endif()
@@ -55,6 +56,11 @@ function(SwigLib name lib swigtmpdir)
 
     endif()
 
+    # include Python and Numpy headers and link to Python shared library
+    target_include_directories(${lib} PRIVATE ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS})
+    target_link_libraries(${lib} PRIVATE ${Python3_LIBRARIES})
+
+    # copy SWIG-produced Python wrapper script to the library output directory
     add_custom_target(
         ${lib}_python
         COMMAND ${CMAKE_COMMAND}
@@ -68,10 +74,7 @@ function(SwigLib name lib swigtmpdir)
 
     add_dependencies(${lib} ${lib}_python)
 
-    target_include_directories(${lib} PRIVATE ${Python3_INCLUDE_DIRS} ${Python3_NumPy_INCLUDE_DIRS})
-    target_link_libraries(${lib} PRIVATE ${Python3_LIBRARIES})
-
     install(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${lib}.py
-        DESTINATION ${destination_python} COMPONENT Libraries) # required by swig
+        DESTINATION ${destination_python} COMPONENT Libraries)
 
 endfunction()
-- 
GitLab


From b40cf63dc92830ad921b5ee70349d48c8d80db5d Mon Sep 17 00:00:00 2001
From: "a.nejati.debug@debian-ci" <a.nejati.debug@debian-ci>
Date: Mon, 24 Apr 2023 15:24:58 +0200
Subject: [PATCH 29/85] Apply clang-format

---
 GUI/View/Project/PyImportAssistant.cpp        |  22 +-
 PyCore/Embed/PyCore.h                         |   6 +-
 PyCore/Embed/PyObjectDecl.h                   |   2 +-
 PyCore/Embed/PyObjectPtr.cpp                  |  40 +--
 PyCore/Embed/PyObjectPtr.h                    |  19 +-
 PyCore/Embed/PythonInterpreter.cpp            | 275 ++++++++----------
 PyCore/Embed/PythonInterpreter.h              |  25 +-
 PyCore/Embed/Result.cpp                       |   8 +-
 PyCore/Embed/Result.h                         |  45 +--
 PyCore/Sample/Multilayer/ImportMultiLayer.cpp |  23 +-
 PyCore/Sample/Multilayer/ImportMultiLayer.h   |  15 +-
 11 files changed, 225 insertions(+), 255 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index d7bf56269ce..a6a0028e8de 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -29,9 +29,9 @@
 #include "GUI/View/Info/MessageBox.h"
 #include "GUI/View/Project/ProjectManager.h"
 #include "GUI/View/Tool/Globals.h"
+#include "PyCore/Embed/PythonInterpreter.h"            // listOfFunctions
+#include "PyCore/Sample/Multilayer/ImportMultiLayer.h" // createMultiLayerFromPython
 #include "Sample/Multilayer/MultiLayer.h"
-#include "PyCore/Embed/PythonInterpreter.h"  // listOfFunctions
-#include "PyCore/Sample/Multilayer/ImportMultiLayer.h"  // createMultiLayerFromPython
 #include <QApplication>
 #include <QFileDialog>
 #include <QTextStream>
@@ -104,16 +104,14 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
 
-    Result<std::unique_ptr<MultiLayer>> result{
-        PythonInterpreter::createMultiLayerFromPython(
-            snippet.toStdString(), funcName.toStdString(),
-            bornagainDir())};
+    Result<std::unique_ptr<MultiLayer>> result{PythonInterpreter::createMultiLayerFromPython(
+        snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
     if (!result.valid()) {
         QApplication::restoreOverrideCursor();
         QString message("Exception thrown while executing Python code to create sample.\n\n");
         QString details = QString::fromStdString(result.status.message);
-    DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
+        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
     }
 
     QApplication::restoreOverrideCursor();
@@ -153,12 +151,10 @@ QString getPySampleFunctionName(const QString& snippet)
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
 
-    Result<std::vector<std::string>> funcs_res {
-        PythonInterpreter::BornAgain::listOfFunctions(
-            snippet.toStdString(), bornagainDir())};
+    Result<std::vector<std::string>> funcs_res{
+        PythonInterpreter::BornAgain::listOfFunctions(snippet.toStdString(), bornagainDir())};
 
-    if(!funcs_res.valid())
-    {
+    if (!funcs_res.valid()) {
         QApplication::restoreOverrideCursor();
         QString message("Exception thrown while acquiring functions from Python code.\n\n");
         QString details = QString::fromStdString(funcs_res.status.message);
@@ -167,7 +163,7 @@ QString getPySampleFunctionName(const QString& snippet)
     }
 
     QApplication::restoreOverrideCursor();
-    QStringList funcList {GUI::Util::String::fromStdStrings(funcs_res.val)};
+    QStringList funcList{GUI::Util::String::fromStdStrings(funcs_res.val)};
     return selectPySampleFunction(funcList);
 }
 
diff --git a/PyCore/Embed/PyCore.h b/PyCore/Embed/PyCore.h
index 10533b160a0..42027dff657 100644
--- a/PyCore/Embed/PyCore.h
+++ b/PyCore/Embed/PyCore.h
@@ -52,6 +52,6 @@
 #pragma GCC diagnostic pop
 #endif
 
-#endif  // BORNAGAIN_PYTHON
-#endif  // BORNAGAIN_PYTOOLS_PYCORE_H
-#endif  // USER_API
+#endif // BORNAGAIN_PYTHON
+#endif // BORNAGAIN_PYTOOLS_PYCORE_H
+#endif // USER_API
diff --git a/PyCore/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
index 8fdd48f8d38..dfdd0e71c31 100644
--- a/PyCore/Embed/PyObjectDecl.h
+++ b/PyCore/Embed/PyObjectDecl.h
@@ -10,7 +10,7 @@ struct _object;
 typedef _object PyObject;
 #endif
 
-typedef long int np_size_t;  // size type used in Numpy
+typedef long int np_size_t; // size type used in Numpy
 
 #endif // PYOBJECT_H
 #endif // USER_API
diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index 55390c99ab5..e7e908a1cc3 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -1,31 +1,32 @@
 #include "PyObjectPtr.h"
 #include "PythonInterpreter.h"
 
-#include <string>
-#include <sstream>  // stringstream
 #include <exception>
+#include <sstream> // stringstream
+#include <string>
 
-PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr,
-                         const Status& status):
-    ptr{pyobject_ptr},
-    status{status}
+PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr, const Status& status)
+    : ptr{pyobject_ptr}
+    , status{status}
 {
     // if PyObject pointer is null but result type is not `None` or `Error`,
     // then that will be an `Error` case.
-    if(!ptr && status.type != Status::Type::None
-       && status.type != Status::Type::Error)
-    {
+    if (!ptr && status.type != Status::Type::None && status.type != Status::Type::Error) {
         this->status.type = Status::Type::Error;
     }
 }
 
 
-PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr):
-    PyObjectPtr(pyobject_ptr, Status(Status::Type::Info)) {}
+PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr)
+    : PyObjectPtr(pyobject_ptr, Status(Status::Type::Info))
+{
+}
 
 
-PyObjectPtr::PyObjectPtr(const Status& status):
-    PyObjectPtr(nullptr, status) {}
+PyObjectPtr::PyObjectPtr(const Status& status)
+    : PyObjectPtr(nullptr, status)
+{
+}
 
 
 PyObjectPtr::~PyObjectPtr()
@@ -34,8 +35,8 @@ PyObjectPtr::~PyObjectPtr()
 }
 
 
-PyObjectPtr::PyObjectPtr(PyObjectPtr&& other):
-    PyObjectPtr(other.ptr, other.status)
+PyObjectPtr::PyObjectPtr(PyObjectPtr&& other)
+    : PyObjectPtr(other.ptr, other.status)
 {
     // reset the moved object
     other.reset();
@@ -44,7 +45,7 @@ PyObjectPtr::PyObjectPtr(PyObjectPtr&& other):
 
 PyObject* PyObjectPtr::release()
 {
-    PyObject* pyobject_ptr {ptr};
+    PyObject* pyobject_ptr{ptr};
     reset();
     return pyobject_ptr;
 }
@@ -60,10 +61,9 @@ void PyObjectPtr::reset()
 void PyObjectPtr::discard()
 {
     if (!PythonInterpreter::isInitialized()) {
-        throw(std::runtime_error(
-                  "Decrementing Python reference-count without "
-                  "Python initialized leads to memory access violation "
-                  "(segmentation fault)"));
+        throw(std::runtime_error("Decrementing Python reference-count without "
+                                 "Python initialized leads to memory access violation "
+                                 "(segmentation fault)"));
     }
 
     PythonInterpreter::DecRef(ptr);
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 2165b64770a..41ffdb04eb3 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -2,20 +2,19 @@
 #define PYOBJECTPTR_H
 
 #include "PyObjectDecl.h"
-#include "Result.h"  // Status
+#include "Result.h" // Status
 #include <vector>
 
 //========================================
 
 // safe container for PyObjects
 /*
-The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and a `Status` which denotes the status of the value (OK/Warning/Error).
-Decrementing Python reference-count is performed automatically when a
-`PyObjectPtr` expires.
+The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and a `Status` which denotes the status
+of the value (OK/Warning/Error). Decrementing Python reference-count is performed automatically when
+a `PyObjectPtr` expires.
 */
 
-class PyObjectPtr
-{
+class PyObjectPtr {
 
 public:
     // raw pointer to the PyObject
@@ -40,7 +39,8 @@ public:
     PyObjectPtr(PyObjectPtr&& other);
     // reset the container to the initial status (does _not_ release the Python resource)
     void reset();
-    // reset the container to the initial status and return the PyObject pointer (does _not_ release the Python resource)
+    // reset the container to the initial status and return the PyObject pointer (does _not_ release
+    // the Python resource)
     PyObject* release();
     // discards the Python resource (decrements the Python reference)
     void discard();
@@ -52,8 +52,7 @@ public:
 
 
 // Numpy array descriptor
-struct NpArrayDescr
-{
+struct NpArrayDescr {
     // Numpy array descriptors
     bool C_contiguous = false;
     bool F_contiguous = false;
@@ -69,4 +68,4 @@ struct NpArrayDescr
     bool owns_data = false;
 };
 
-#endif  // PYOBJECTPTR_H
+#endif // PYOBJECTPTR_H
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 94fe051e2db..74d6558405a 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -13,20 +13,20 @@ References:
 - Python's garbage collector <https://rushter.com/blog/python-garbage-collector>
 */
 
-#define INCLUDE_NUMPY  // also include Numpy via PyCore
+#define INCLUDE_NUMPY // also include Numpy via PyCore
 #include "PyCore.h"
 #undef INCLUDE_NUMPY
 
 #include "PythonInterpreter.h"
 
-#include <cstddef>  // NULL
-#include <cstring>  // memcpy
-#include <string>
+#include <cstddef> // NULL
+#include <cstring> // memcpy
+#include <memory>  // unique_ptr
 #include <sstream>
+#include <string>
 #include <vector>
-#include <memory>  // unique_ptr
 
-#include <iostream>  // DEBUG
+#include <iostream> // DEBUG
 //========================================
 
 
@@ -36,7 +36,7 @@ namespace {
 // thin wrapper to initialize Numpy
 std::nullptr_t _init_numpy(int& status)
 {
-    if(!PyArray_API) {
+    if (!PyArray_API) {
         /* To use Numpy Array C-API from an extension module,
            `import_array` function must be called.
            If the extension module is self-contained in a single .c file,
@@ -90,23 +90,21 @@ Result<std::vector<std::string>> toVectorString(PyObject* py_object)
             result.push_back(PythonInterpreter::pyStrtoString(value));
         }
     } else
-        return {Status(Status::Type::Error,
-                       "Cannnot convert the given Python object "
-                       "to vector<string>.")};
+        return {Status(Status::Type::Error, "Cannnot convert the given Python object "
+                                            "to vector<string>.")};
     return {std::move(result)};
 }
 
 
 // auxiliary function to convert a Numpy array data of real type `real_t`
 // to vector<double>.
-template<typename real_t>
-inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr,
-                                 npy_intp rowIdxMax, npy_intp colIdxMax,
-                                 std::vector<double>& vector_out)
+template <typename real_t>
+inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowIdxMax,
+                                 npy_intp colIdxMax, std::vector<double>& vector_out)
 {
     std::size_t idx = 0;
-    for(npy_intp i_row = 0; i_row < rowIdxMax; ++i_row) {
-        for(npy_intp j_col = 0; j_col < colIdxMax; ++j_col) {
+    for (npy_intp i_row = 0; i_row < rowIdxMax; ++i_row) {
+        for (npy_intp j_col = 0; j_col < colIdxMax; ++j_col) {
             const double val = static_cast<double>(
                 *reinterpret_cast<real_t*>(PyArray_GETPTR2(npArray_ptr, i_row, j_col)));
             vector_out[idx] = val;
@@ -118,12 +116,12 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr,
 
 double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
 {
-    if(!npy_array.valid() || !PyArray_Check(npy_array.ptr)) {
+    if (!npy_array.valid() || !PyArray_Check(npy_array.ptr)) {
         throw std::runtime_error(
             "PythonInterpreter: Cannot convert an invalid Numpy array to a C-Array");
     }
 
-    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(npy_array.ptr)};
+    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(npy_array.ptr)};
     PyArray_Descr* npArray_descr = PyArray_DESCR(npArray_ptr);
     const int npArray_type = PyArray_TYPE(npArray_ptr);
     const int npArray_ndim = PyArray_NDIM(npArray_ptr);
@@ -132,33 +130,33 @@ double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
 
     // DEBUG
     // type checking
-    if(PyDataType_ISUNSIGNED(npArray_descr))
+    if (PyDataType_ISUNSIGNED(npArray_descr))
         printf("AN>> Array datatype = uint\n");
-    else if(PyDataType_ISSIGNED(npArray_descr))
+    else if (PyDataType_ISSIGNED(npArray_descr))
         printf("AN>> Array datatype = int\n");
-    else if(PyDataType_ISFLOAT(npArray_descr))
+    else if (PyDataType_ISFLOAT(npArray_descr))
         printf("AN>> Array datatype = float\n");
     else
         printf("AN>> Array datatype = ?\n");
 
-    if(npArray_type == NPY_FLOAT)
+    if (npArray_type == NPY_FLOAT)
         printf("AN>> Array type (%d) = f32\n", npArray_type);
 
-    if(PyArray_IS_C_CONTIGUOUS(npArray_ptr))
+    if (PyArray_IS_C_CONTIGUOUS(npArray_ptr))
         printf("AN>> Array is C-contiguous (row-major)\n");
 
-    if(PyArray_IS_F_CONTIGUOUS(npArray_ptr))
+    if (PyArray_IS_F_CONTIGUOUS(npArray_ptr))
         printf("AN>> Array is Fortran-contiguous (column-major)\n");
 
-    if(PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
+    if (PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
         printf("AN>> Array is well-behaved\n");
 
-    if(npArray_flags & NPY_ARRAY_OWNDATA)
+    if (npArray_flags & NPY_ARRAY_OWNDATA)
         printf("AN>> Array owns its data\n");
     else
         printf("AN>> Array does not own its data\n");
 
-    for(int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
+    for (int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
         printf("AN>> Array-dim{%d} = %ld\n", i_dim, npArray_dims[i_dim]);
 
     // END DEBUG
@@ -166,7 +164,10 @@ double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
     // TODO: Verify array is 2d
 
     // void *PyArray_GETPTR2(PyArrayObject *obj, npy_intp i, npy_intp j)
-    // Quick, inline access to the element at the given coordinates in the ndarray, obj, which must have 2 dimensions (this is not checked). The corresponding i, j,  coordinates can be any integer but will be interpreted as npy_intp. You may want to typecast the returned pointer to the data type of the ndarray.
+    // Quick, inline access to the element at the given coordinates in the ndarray, obj, which must
+    // have 2 dimensions (this is not checked). The corresponding i, j,  coordinates can be any
+    // integer but will be interpreted as npy_intp. You may want to typecast the returned pointer to
+    // the data type of the ndarray.
 
     // element-by-element copy of the Numpy array data (always work)
     const std::size_t vsize = static_cast<std::size_t>(PyArray_SIZE(npArray_ptr));
@@ -175,18 +176,20 @@ double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
         out1[i] = *reinterpret_cast<double*>(PyArray_GETPTR1(npArray_ptr, i));
     }
 
-    // using bulk memcpy-like operation, but keep in mind that NumPy ndarrays may be mis-aligned for the data type, have non-native byte order, or other subtle attributes that make such copies less than desirable.
+    // using bulk memcpy-like operation, but keep in mind that NumPy ndarrays may be mis-aligned for
+    // the data type, have non-native byte order, or other subtle attributes that make such copies
+    // less than desirable.
 
     std::vector<double> out2(vsize);
-    std::memcpy(out2.data(), PyArray_DATA(npArray_ptr),
-                sizeof(double) * vsize);
+    std::memcpy(out2.data(), PyArray_DATA(npArray_ptr), sizeof(double) * vsize);
 
     // using either approach, out now contains a copy of the ndarray's data
 
-    double* npArray_data_ptr {reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
-    if(!npArray_data_ptr) {
+    double* npArray_data_ptr{reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
+    if (!npArray_data_ptr) {
         // checkError();
-        throw std::runtime_error("PythonInterpreter: Cannot convert an invalid Numpy array to a C-Array (invalid data pointer)");
+        throw std::runtime_error("PythonInterpreter: Cannot convert an invalid Numpy array to a "
+                                 "C-Array (invalid data pointer)");
     }
 
     // TODO: Problem with the reference to numpy array!!!
@@ -197,13 +200,12 @@ double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
 
 Result<double*> NpyArray1D_to_C(PyObject* pyobject_ptr)
 {
-    if(!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: Cannot convert an invalid "
-                       "Numpy array to a C-Array")};
+    if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
+        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: Cannot convert an invalid "
+                                            "Numpy array to a C-Array")};
     }
 
-    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
     PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
     const int npArray_type = PyArray_TYPE(npArray_ptr);
     const int npArray_ndim = PyArray_NDIM(npArray_ptr);
@@ -212,8 +214,8 @@ Result<double*> NpyArray1D_to_C(PyObject* pyobject_ptr)
     // character code indicating the data type
     const char npArray_dtype = npArray_descr->type;
 
-    //TODO: Enable type-casting for the Numpy array
-    if(npArray_type != NPY_DOUBLE) {
+    // TODO: Enable type-casting for the Numpy array
+    if (npArray_type != NPY_DOUBLE) {
         std::stringstream msg_ss;
         msg_ss << "PythonInterpreter::Numpy: "
                << "Expected a Numpy array of type _double_ "
@@ -223,61 +225,59 @@ Result<double*> NpyArray1D_to_C(PyObject* pyobject_ptr)
 
     // DEBUG
     // type checking
-    if(PyDataType_ISUNSIGNED(npArray_descr))
+    if (PyDataType_ISUNSIGNED(npArray_descr))
         printf("AN>> Array datatype = uint\n");
-    else if(PyDataType_ISSIGNED(npArray_descr))
+    else if (PyDataType_ISSIGNED(npArray_descr))
         printf("AN>> Array datatype = int\n");
-    else if(PyDataType_ISFLOAT(npArray_descr))
+    else if (PyDataType_ISFLOAT(npArray_descr))
         printf("AN>> Array datatype = float\n");
     else
         printf("AN>> Array datatype = ?\n");
 
-    if(npArray_type == NPY_FLOAT)
+    if (npArray_type == NPY_FLOAT)
         printf("AN>> Array type (%d) = f32\n", npArray_type);
 
-    if(PyArray_IS_C_CONTIGUOUS(npArray_ptr))
+    if (PyArray_IS_C_CONTIGUOUS(npArray_ptr))
         printf("AN>> Array is C-contiguous (row-major)\n");
 
-    if(PyArray_IS_F_CONTIGUOUS(npArray_ptr))
+    if (PyArray_IS_F_CONTIGUOUS(npArray_ptr))
         printf("AN>> Array is Fortran-contiguous (column-major)\n");
 
-    if(PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
+    if (PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
         printf("AN>> Array is well-behaved\n");
 
-    if(npArray_flags & NPY_ARRAY_OWNDATA)
+    if (npArray_flags & NPY_ARRAY_OWNDATA)
         printf("AN>> Array owns its data\n");
     else
         printf("AN>> Array does not own its data\n");
 
-    for(int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
+    for (int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
         printf("AN>> Array-dim{%d} = %ld\n", i_dim, npArray_dims[i_dim]);
 
     // END DEBUG
 
     // TODO: check if array is contiguous
-    double* npArray_data_ptr {reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
-    if(!npArray_data_ptr) {
+    double* npArray_data_ptr{reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
+    if (!npArray_data_ptr) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot convert an invalid Numpy array to a C-Array"
-                       "(data pointer is invalid)")};
+        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: "
+                                            "Cannot convert an invalid Numpy array to a C-Array"
+                                            "(data pointer is invalid)")};
     }
 
     return {std::move(npArray_data_ptr)};
 }
 
 
-PyObjectPtr call_pyfunction_npy_array(
-    PyObjectPtr& py_module, const std::string& fn_name, PyObjectPtr& npy_array)
+PyObjectPtr call_pyfunction_npy_array(PyObjectPtr& py_module, const std::string& fn_name,
+                                      PyObjectPtr& npy_array)
 {
     // Call a Python function (from a given module) which takes a
     // single Numpy array as input and returns a Numpy array.
 
     if (!py_module.valid()) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter: Cannot call a function "
-                       "from an invalid Python module")};
+        return {Status(Status::Type::Error, "PythonInterpreter: Cannot call a function "
+                                            "from an invalid Python module")};
     }
 
     PyObject* pFunc = PyObject_GetAttrString(py_module.ptr, fn_name.c_str());
@@ -289,14 +289,14 @@ PyObjectPtr call_pyfunction_npy_array(
         return {Status(Status::Type::Error, msg_ss.str())};
     }
 
-    PyObject* pReturn = PyObject_CallFunctionObjArgs(pFunc, npy_array.ptr,
-                                                     NULL);
+    PyObject* pReturn = PyObject_CallFunctionObjArgs(pFunc, npy_array.ptr, NULL);
     if (!PyArray_Check(pReturn)) {
         PythonInterpreter::checkError();
         Py_DecRef(pFunc);
         std::stringstream msg_ss;
         msg_ss << "PythonInterpreter: Invalid return value from calling "
-            "the function '" << fn_name << "' (expected a Numpy array)";
+                  "the function '"
+               << fn_name << "' (expected a Numpy array)";
         return {Status(Status::Type::Error, msg_ss.str())};
     }
 
@@ -304,7 +304,7 @@ PyObjectPtr call_pyfunction_npy_array(
     return {pReturn};
 }
 
-}  // namespace
+} // namespace
 //----------------------------------------
 
 // Python stable ABI
@@ -317,7 +317,6 @@ Status PythonInterpreter::initialize()
         msg_ss << "PythonInterpreter: Initialized Python - "
                << "version: " << Py_GetVersion();
         return {Status::Type::Info, msg_ss.str()};
-
     }
 
     // if Python already initialized
@@ -377,14 +376,12 @@ void PythonInterpreter::addPythonPath(const std::string& path)
 int PythonInterpreter::setPythonPath(const std::string& path)
 {
     // returns 0 on success, -1 on error
-    const int result = PySys_SetObject(
-        (char*)"path", PyUnicode_FromString(path.c_str()));
+    const int result = PySys_SetObject((char*)"path", PyUnicode_FromString(path.c_str()));
     return result;
 }
 
 // Python stable ABI
-PyObjectPtr PythonInterpreter::import(
-    const std::string& pymodule_name, const std::string& path)
+PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const std::string& path)
 {
     addPythonPath(path);
 
@@ -440,15 +437,14 @@ std::string PythonInterpreter::runtimeInfo()
 
     // Embedded Python details
     result << "Py_GetProgramName(): " << toString(Py_GetProgramName()) << "\n";
-    result << "Py_GetProgramFullPath(): " << toString(Py_GetProgramFullPath())
-           << "\n";
+    result << "Py_GetProgramFullPath(): " << toString(Py_GetProgramFullPath()) << "\n";
     result << "Py_GetPath(): " << toString(Py_GetPath()) << "\n";
     result << "Py_GetPythonHome(): " << toString(Py_GetPythonHome()) << "\n";
 
     // Runtime Python's sys.path
     PyObject* sysPath = PySys_GetObject((char*)"path");
-    Result<std::vector<std::string>> content {toVectorString(sysPath)};
-    if(content.valid()) {
+    Result<std::vector<std::string>> content{toVectorString(sysPath)};
+    if (content.valid()) {
         result << "sys.path: ";
         for (auto s : content.val)
             result << s << ",";
@@ -516,18 +512,18 @@ std::string PythonInterpreter::errorDescription(const std::string& title)
 
 //----------------------------------------
 
-Status PythonInterpreter::Numpy::initialize() {
+Status PythonInterpreter::Numpy::initialize()
+{
 
     // initialize Python C API, if needed
     Status py_status{PythonInterpreter::initialize()};
-    if(py_status.isError())
+    if (py_status.isError())
         return py_status;
 
     int res;
     _init_numpy(res);
 
-    switch(res)
-    {
+    switch (res) {
     case 1:
         return {Status::Type::Info, "PythonInterpreter: Initialized Numpy"};
     case 2:
@@ -545,16 +541,14 @@ bool PythonInterpreter::Numpy::isInitialized()
 
 //----------------------------------------
 
-Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(
-    PyObject* pyobject_ptr)
+Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
 {
-    if(!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Invalid Numpy array")};
+    if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
+        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: "
+                                            "Invalid Numpy array")};
     }
 
-    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
     PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
     // const int npArray_type = PyArray_TYPE(npArray_ptr);
     const int npArray_ndim = PyArray_NDIM(npArray_ptr);
@@ -568,49 +562,48 @@ Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(
     np_descr_out.n_dims = npArray_ndim;
     if (np_descr_out.n_dims > 0) {
         np_descr_out.dims.resize(npArray_ndim);
-        for(npy_intp i_d = 0; i_d < npArray_ndim; ++i_d)
+        for (npy_intp i_d = 0; i_d < npArray_ndim; ++i_d)
             np_descr_out.dims[i_d] = npArray_dims[i_d];
     }
 
     np_descr_out.dtype = npArray_dtype;
 
-    if(PyArray_IS_C_CONTIGUOUS(npArray_ptr))
+    if (PyArray_IS_C_CONTIGUOUS(npArray_ptr))
         np_descr_out.C_contiguous = true;
 
-    if(PyArray_IS_F_CONTIGUOUS(npArray_ptr))
+    if (PyArray_IS_F_CONTIGUOUS(npArray_ptr))
         np_descr_out.F_contiguous = true;
 
-    if(PyArray_ISBEHAVED_RO(npArray_ptr))
+    if (PyArray_ISBEHAVED_RO(npArray_ptr))
         np_descr_out.wellbehaved = true;
 
-    if(npArray_flags & NPY_ARRAY_OWNDATA)
+    if (npArray_flags & NPY_ARRAY_OWNDATA)
         np_descr_out.owns_data = true;
 
     return {std::move(np_descr_out)};
 }
 
 
-Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
+Result<std::vector<double>>
+PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
-    if(!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: Cannot convert an invalid "
-                       "Numpy array to a C-Array")};
+    if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
+        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: Cannot convert an invalid "
+                                            "Numpy array to a C-Array")};
     }
 
-    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
     PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
     const int npArray_type = PyArray_TYPE(npArray_ptr);
     const int npArray_ndim = PyArray_NDIM(npArray_ptr);
     npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
-    const std::size_t npArray_size = static_cast<std::size_t>
-        (PyArray_SIZE(npArray_ptr));
+    const std::size_t npArray_size = static_cast<std::size_t>(PyArray_SIZE(npArray_ptr));
     const int npArray_flags = PyArray_FLAGS(npArray_ptr);
     // character code indicating the data type
     const char npArray_dtype = npArray_descr->type;
 
     // Numpy array type must be 2d
-    if(npArray_ndim != 2) {
+    if (npArray_ndim != 2) {
         std::stringstream msg_ss;
         msg_ss << "PythonInterpreter::Numpy: "
                << "Expected a Numpy 2d-array "
@@ -619,7 +612,7 @@ Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(Py
     }
 
     // Numpy array type must be numeric and real (eligible for casting to double)
-    if(!PyDataType_ISNUMBER(npArray_descr) || PyDataType_ISCOMPLEX(npArray_descr)) {
+    if (!PyDataType_ISNUMBER(npArray_descr) || PyDataType_ISCOMPLEX(npArray_descr)) {
         std::stringstream msg_ss;
         msg_ss << "PythonInterpreter::Numpy: "
                << "Expected a Numpy array of numeric type and real "
@@ -632,8 +625,7 @@ Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(Py
     const npy_intp rowIdxMax = npArray_dims[0], colIdxMax = npArray_dims[1];
 
     // branch on type
-    switch(npArray_type)
-    {
+    switch (npArray_type) {
     // float
     case NPY_DOUBLE:
         _realArray2DToDouble<npy_double>(npArray_ptr, rowIdxMax, colIdxMax, data);
@@ -698,8 +690,8 @@ Result<std::vector<double>> PythonInterpreter::Numpy::createVectorFromArray2D(Py
 }
 
 
-PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(
-    double* const c_array, const np_size_t dims[2])
+PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
+                                                         const np_size_t dims[2])
 {
     if (!c_array) {
         return {Status(Status::Type::Error,
@@ -717,8 +709,7 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(
     npy_intp npDims[2] = {dims[0], dims[1]};
 
     // create stand-alone Numpy array (float64)
-    PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 2,
-                                              npDims, NPY_DOUBLE);
+    PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 2, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
         std::stringstream msg_ss;
@@ -729,11 +720,9 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(
     }
 
     // obtain pointer to data buffer of Numpy array
-    double* array_buffer = static_cast<double*>(
-        PyArray_DATA((PyArrayObject*)(npArray_ptr)));
+    double* array_buffer = static_cast<double*>(PyArray_DATA((PyArrayObject*)(npArray_ptr)));
 
-    for (np_size_t index = 0; index < size; ++index)
-    {
+    for (np_size_t index = 0; index < size; ++index) {
         *array_buffer = c_array[index];
         ++array_buffer;
     }
@@ -743,8 +732,8 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(
 }
 
 
-PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(
-    double* const c_array, const np_size_t size)
+PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
+                                                         const np_size_t size)
 {
     if (!c_array) {
         return {Status(Status::Type::Error,
@@ -761,8 +750,7 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(
     npy_intp npDims[1] = {size};
 
     // create stand-alone Numpy array (float64)
-    PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 1,
-                                              npDims, NPY_DOUBLE);
+    PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 1, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
         std::stringstream msg_ss;
@@ -773,11 +761,9 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(
     }
 
     // obtain pointer to data buffer of Numpy array
-    double* array_buffer = static_cast<double*>(
-        PyArray_DATA((PyArrayObject*)(npArray_ptr)));
+    double* array_buffer = static_cast<double*>(PyArray_DATA((PyArrayObject*)(npArray_ptr)));
 
-    for (np_size_t index = 0; index < size; ++index)
-    {
+    for (np_size_t index = 0; index < size; ++index) {
         *array_buffer = c_array[index];
         ++array_buffer;
     }
@@ -787,8 +773,7 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(
 }
 
 
-PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(
-    double* const c_array, const np_size_t dims[2])
+PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const np_size_t dims[2])
 {
     if (!c_array) {
         return {Status(Status::Type::Error,
@@ -809,11 +794,10 @@ PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(
     PyObject* npArray_ptr = PyArray_SimpleNewFromData(
         /* n_dims */ 2, npDims, NPY_DOUBLE, reinterpret_cast<void*>(c_array));
 
-    if(!npArray_ptr || !PyArray_Check(npArray_ptr)) {
+    if (!npArray_ptr || !PyArray_Check(npArray_ptr)) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: Cannot convert the given "
-                       "C-Array to a Numpy 2D-array")};
+        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: Cannot convert the given "
+                                            "C-Array to a Numpy 2D-array")};
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
@@ -833,9 +817,8 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
 
     for (std::size_t d = 0; d < n_dims; ++d) {
         if (dimensions[d] < 2) {
-            return {Status(Status::Type::Error,
-                           "Cannot make a Numpy with the given dimensions; "
-                           "dimensions must be >= 2")};
+            return {Status(Status::Type::Error, "Cannot make a Numpy with the given dimensions; "
+                                                "dimensions must be >= 2")};
         }
     }
 
@@ -845,8 +828,7 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
         ndimsizes_numpy[d] = dimensions[d];
 
     // creating standalone numpy array
-    PyObject* npyArray_ptr = PyArray_SimpleNew(
-        ndim_numpy, ndimsizes_numpy, NPY_DOUBLE);
+    PyObject* npyArray_ptr = PyArray_SimpleNew(ndim_numpy, ndimsizes_numpy, NPY_DOUBLE);
     delete[] ndimsizes_numpy;
 
     if (!npyArray_ptr) {
@@ -865,11 +847,10 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
 Result<double*> PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 {
 
-    PyArrayObject* npArray_ptr {reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
+    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
 
     // get the pointer to data buffer of the Numpy array
-    double* data_ptr = static_cast<double*>(
-        PyArray_DATA((PyArrayObject*)npArray_ptr));
+    double* data_ptr = static_cast<double*>(PyArray_DATA((PyArrayObject*)npArray_ptr));
 
     if (!data_ptr) {
         checkError();
@@ -915,18 +896,17 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
 }
 
 
-PyObjectPtr PythonInterpreter::BornAgain::importScript(
-    const std::string& script, const std::string& path)
+PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script,
+                                                       const std::string& path)
 {
-    PyObjectPtr ba_pymodule {PythonInterpreter::BornAgain::import(path)};
+    PyObjectPtr ba_pymodule{PythonInterpreter::BornAgain::import(path)};
     // TODO: Check ba module
 
     PyObject* pCompiledFn = Py_CompileString(script.c_str(), "", Py_file_input);
     if (!pCompiledFn) {
         ba_pymodule.discard();
         return {Status(Status::Type::Error,
-                       PythonInterpreter::errorDescription(
-                           "Cannot compile Python snippet"))};
+                       PythonInterpreter::errorDescription("Cannot compile Python snippet"))};
     }
 
     // create a module
@@ -935,23 +915,22 @@ PyObjectPtr PythonInterpreter::BornAgain::importScript(
         Py_DecRef(pCompiledFn);
         ba_pymodule.discard();
         return {Status(Status::Type::Error,
-                       PythonInterpreter::errorDescription(
-                           "Cannot execute Python snippet"))};
+                       PythonInterpreter::errorDescription("Cannot execute Python snippet"))};
     }
 
     return {tmpModule};
 }
 
 
-Result<std::vector<std::string>> PythonInterpreter::BornAgain::listOfFunctions(
-    const std::string& script, const std::string& path)
+Result<std::vector<std::string>>
+PythonInterpreter::BornAgain::listOfFunctions(const std::string& script, const std::string& path)
 {
-    PyObjectPtr tmpModule {PythonInterpreter::BornAgain::importScript(script, path)};
+    PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
     PyObject* pDict = PyModule_GetDict(tmpModule.ptr);
     if (!pDict) {
         return {Status(Status::Type::Error, "PythonInterpreter::BornAgain: "
-                       "Cannot obtain the dictionary from the script module")};
+                                            "Cannot obtain the dictionary from the script module")};
     }
 
     PyObject *key, *value;
@@ -959,7 +938,7 @@ Result<std::vector<std::string>> PythonInterpreter::BornAgain::listOfFunctions(
     std::vector<std::string> fn_names;
     while (PyDict_Next(pDict, &pos, &key, &value)) {
         if (PyCallable_Check(value)) {
-            std::string func_name {PythonInterpreter::pyStrtoString(key)};
+            std::string func_name{PythonInterpreter::pyStrtoString(key)};
             if (func_name.find("__") == std::string::npos)
                 fn_names.push_back(func_name);
         }
@@ -978,16 +957,14 @@ PyObjectPtr PythonInterpreter::Fabio::import()
 }
 
 
-PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename,
-                                           PyObjectPtr& fabio_module)
+PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObjectPtr& fabio_module)
 {
     // load an image via calling `fabio.load` function which takes a
     // filename (Python string) and returns a Numpy array.
 
     if (!fabio_module.valid() || !PyModule_Check(fabio_module.ptr)) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter.fabio: Invalid Python module "
-                       "(expected 'fabio' module)")};
+        return {Status(Status::Type::Error, "PythonInterpreter.fabio: Invalid Python module "
+                                            "(expected 'fabio' module)")};
     }
 
     PyObject* pFunc = PyObject_GetAttrString(fabio_module.ptr, (char*)"open");
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 26219081b53..f3423477f7c 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -1,9 +1,9 @@
 #ifndef PYTHONINTRP_H
 #define PYTHONINTRP_H
 
-#include <vector>
-#include <string>
 #include "PyObjectPtr.h"
+#include <string>
+#include <vector>
 
 class MultiLayer;
 
@@ -29,8 +29,7 @@ void addPythonPath(const std::string& path);
 int setPythonPath(const std::string& path);
 
 // Python stable ABI
-PyObjectPtr import(const std::string& pymodule_name,
-                   const std::string& path = "");
+PyObjectPtr import(const std::string& pymodule_name, const std::string& path = "");
 // Python stable ABI
 void DecRef(PyObject* py_object);
 
@@ -79,7 +78,7 @@ Result<std::vector<double>> createVectorFromArray2D(PyObject* pyobject_ptr);
 // The data is _not_ copied, and the Numpy array will _not_ own its data.
 PyObjectPtr CArrayAsNpy2D(double* const c_array, const np_size_t dims[2]);
 
-}  // namespace Numpy
+} // namespace Numpy
 
 //-- BornAgain-related functionality
 namespace BornAgain {
@@ -87,27 +86,25 @@ namespace BornAgain {
 PyObjectPtr import(const std::string& path = "");
 
 // import a 'BornAgain' Python script
-PyObjectPtr importScript(const std::string& script,
-                         const std::string& path);
+PyObjectPtr importScript(const std::string& script, const std::string& path);
 
 //! Returns list of functions defined in the script.
 //! @param script: Python script
 //! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-Result<std::vector<std::string>> listOfFunctions(
-    const std::string& script, const std::string& path);
+Result<std::vector<std::string>> listOfFunctions(const std::string& script,
+                                                 const std::string& path);
 
-}  // namespace BornAgain
+} // namespace BornAgain
 
 //-- Fabio-related functionality
 namespace Fabio {
 // import 'fabio' Python module
 PyObjectPtr import();
 // call 'fabio.open' on a given file (needs Numpy)
-PyObjectPtr open(const std::string& filename,
-                 PyObjectPtr& fabio_module);
+PyObjectPtr open(const std::string& filename, PyObjectPtr& fabio_module);
 
 } // namespace Fabio
 
-}  // namespace PythonInterpreter
+} // namespace PythonInterpreter
 
-#endif  // PYTHONINTRP_H
+#endif // PYTHONINTRP_H
diff --git a/PyCore/Embed/Result.cpp b/PyCore/Embed/Result.cpp
index 2b32315670f..c6561cd2a1d 100644
--- a/PyCore/Embed/Result.cpp
+++ b/PyCore/Embed/Result.cpp
@@ -1,8 +1,10 @@
 #include "Result.h"
 
-Status::Status(const Status::Type type, const std::string& message):
-    type{type},
-    message{message} {}
+Status::Status(const Status::Type type, const std::string& message)
+    : type{type}
+    , message{message}
+{
+}
 
 bool Status::isSet() const
 {
diff --git a/PyCore/Embed/Result.h b/PyCore/Embed/Result.h
index 73f752fbfe0..a2940c211d8 100644
--- a/PyCore/Embed/Result.h
+++ b/PyCore/Embed/Result.h
@@ -1,22 +1,20 @@
 #ifndef RESULT_H
 #define RESULT_H
 
-#include <utility>  // move
 #include <string>
+#include <utility> // move
 
 // ========================================
 
 // Indicator for the status of a result
-struct Status
-{
-    enum class Type {None=-1, Info, Warning, Error};
+struct Status {
+    enum class Type { None = -1, Info, Warning, Error };
     Type type = Type::None;
 
     // status message
     std::string message;
 
-    Status(const Status::Type type = Type::None,
-           const std::string& message = "");
+    Status(const Status::Type type = Type::None, const std::string& message = "");
 
     bool isSet() const;
 
@@ -40,11 +38,9 @@ struct Status
    an exception should be raised or not.
 */
 template <typename ResType>
-class Result
-{
+class Result {
 
 public:
-
     using type = ResType;
 
     // stored result
@@ -79,29 +75,36 @@ public:
 //-- Implementation of Result
 
 template <typename ResType>
-Result<ResType>::Result(ResType&& result,
-                        Status&& status):
-    val{std::move(result)},
-    status{std::move(status)} {}
+Result<ResType>::Result(ResType&& result, Status&& status)
+    : val{std::move(result)}
+    , status{std::move(status)}
+{
+}
 
 
 template <typename ResType>
-Result<ResType>::Result(ResType&& result):
-    Result(std::move(result), Status(Status::Type::Info)) {}
+Result<ResType>::Result(ResType&& result)
+    : Result(std::move(result), Status(Status::Type::Info))
+{
+}
 
 
 template <typename ResType>
-Result<ResType>::Result(Status&& status):
-    status{std::move(status)} {}
+Result<ResType>::Result(Status&& status)
+    : status{std::move(status)}
+{
+}
 
 
 template <typename ResType>
-Result<ResType>::~Result() {}
+Result<ResType>::~Result()
+{
+}
 
 
 template <typename ResType>
-Result<ResType>::Result(Result<ResType>&& other):
-    Result(std::move(other.val), std::move(other.status))
+Result<ResType>::Result(Result<ResType>&& other)
+    : Result(std::move(other.val), std::move(other.status))
 {
     // reset the moved object
     other.reset();
@@ -128,4 +131,4 @@ bool Result<ResType>::isSet() const
     return (status.isSet());
 }
 
-#endif  // RESULT_H
+#endif // RESULT_H
diff --git a/PyCore/Sample/Multilayer/ImportMultiLayer.cpp b/PyCore/Sample/Multilayer/ImportMultiLayer.cpp
index 4da4157ac24..71e3661d8bb 100644
--- a/PyCore/Sample/Multilayer/ImportMultiLayer.cpp
+++ b/PyCore/Sample/Multilayer/ImportMultiLayer.cpp
@@ -1,21 +1,20 @@
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyCore/Embed/PyCore.h"
 #include "ImportMultiLayer.h"
+#include "PyCore/Embed/PyCore.h"
+#include "PyCore/Embed/PythonInterpreter.h" // BornAgain::importScript
 #include "Sample/Multilayer/MultiLayer.h"
-#include "PyCore/Embed/PythonInterpreter.h"  // BornAgain::importScript
 // SWIG runtime access for creating a MultiLayer instance from Python
 #include "auto/Wrap/swig_runtime.h"
 
 
-namespace PythonInterpreter
-{
+namespace PythonInterpreter {
 
-Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
-    const std::string& script, const std::string& functionName,
-    const std::string& path)
+Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(const std::string& script,
+                                                               const std::string& functionName,
+                                                               const std::string& path)
 {
-    PyObjectPtr tmpModule {PythonInterpreter::BornAgain::importScript(script, path)};
+    PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
     // locate the `get_simulation` function (it is an attribute of the module)
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.ptr, functionName.c_str());
@@ -36,8 +35,7 @@ Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
         std::stringstream msg_ss;
         msg_ss << "PythonInterpreter::BornAgain: "
                << "Cannot call the function '" << functionName << "'";
-        return {Status(Status::Type::Error,
-                       PythonInterpreter::errorDescription(msg_ss.str()))};
+        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg_ss.str()))};
     }
 
     // Construct a C++ object from the Python object
@@ -55,8 +53,7 @@ Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
         msg_ss << "PythonInterpreter::BornAgain: "
                << "SWIG failed to extract a 'MultiLayer' instance "
                << "via calling the function '" << functionName << "'";
-        return {Status(Status::Type::Error,
-                       PythonInterpreter::errorDescription(msg_ss.str()))};
+        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg_ss.str()))};
     }
 
     MultiLayer* multilayer = reinterpret_cast<MultiLayer*>(argp1);
@@ -68,6 +65,6 @@ Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
     return {std::move(result_ptr)};
 }
 
-}  // namespace PythonInterpreter
+} // namespace PythonInterpreter
 
 #endif // BORNAGAIN_PYTHON
diff --git a/PyCore/Sample/Multilayer/ImportMultiLayer.h b/PyCore/Sample/Multilayer/ImportMultiLayer.h
index ac1714992ab..4bf855213c4 100644
--- a/PyCore/Sample/Multilayer/ImportMultiLayer.h
+++ b/PyCore/Sample/Multilayer/ImportMultiLayer.h
@@ -1,23 +1,22 @@
 #ifndef SWIG
 
-#include <string>
-#include <memory>  // unique_ptr
 #include "PyCore/Embed/PyObjectDecl.h"
 #include "PyCore/Embed/Result.h"
+#include <memory> // unique_ptr
+#include <string>
 
 class MultiLayer;
 
-namespace PythonInterpreter
-{
+namespace PythonInterpreter {
 
 //! Creates a multi layer by running python code in embedded interpreter.
 //! @param script: Python script
 //! @param functionName: A function name in this script which produces a MultiLayer
 //! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(
-    const std::string& script, const std::string& functionName,
-    const std::string& path = "");
+Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(const std::string& script,
+                                                               const std::string& functionName,
+                                                               const std::string& path = "");
 
-}  // namespace PythonInterpreter
+} // namespace PythonInterpreter
 
 #endif // SWIG
-- 
GitLab


From df7ee30cf4a39164cbf02de16583884eb51a87aa Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 24 Apr 2023 15:35:01 +0200
Subject: [PATCH 30/85] PyCore/Embed/PythonInterpreter: rm unneeded functions
 and variables

---
 PyCore/Embed/PythonInterpreter.cpp | 203 +----------------------------
 1 file changed, 1 insertion(+), 202 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 74d6558405a..bcd277e974b 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -56,16 +56,6 @@ std::nullptr_t _init_numpy(int& status)
     return NULL;
 }
 
-
-//! Converts char to string. In the case of nullptr will return an empty string
-std::string toString(const char* const c)
-{
-    if (c)
-        return c;
-    return "";
-}
-
-
 std::string toString(const wchar_t* const c)
 {
     if (!c)
@@ -113,197 +103,6 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
     }
 }
 
-
-double* npyArray_to_CArray_WIP(PyObjectPtr& npy_array)
-{
-    if (!npy_array.valid() || !PyArray_Check(npy_array.ptr)) {
-        throw std::runtime_error(
-            "PythonInterpreter: Cannot convert an invalid Numpy array to a C-Array");
-    }
-
-    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(npy_array.ptr)};
-    PyArray_Descr* npArray_descr = PyArray_DESCR(npArray_ptr);
-    const int npArray_type = PyArray_TYPE(npArray_ptr);
-    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
-    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
-    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
-
-    // DEBUG
-    // type checking
-    if (PyDataType_ISUNSIGNED(npArray_descr))
-        printf("AN>> Array datatype = uint\n");
-    else if (PyDataType_ISSIGNED(npArray_descr))
-        printf("AN>> Array datatype = int\n");
-    else if (PyDataType_ISFLOAT(npArray_descr))
-        printf("AN>> Array datatype = float\n");
-    else
-        printf("AN>> Array datatype = ?\n");
-
-    if (npArray_type == NPY_FLOAT)
-        printf("AN>> Array type (%d) = f32\n", npArray_type);
-
-    if (PyArray_IS_C_CONTIGUOUS(npArray_ptr))
-        printf("AN>> Array is C-contiguous (row-major)\n");
-
-    if (PyArray_IS_F_CONTIGUOUS(npArray_ptr))
-        printf("AN>> Array is Fortran-contiguous (column-major)\n");
-
-    if (PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
-        printf("AN>> Array is well-behaved\n");
-
-    if (npArray_flags & NPY_ARRAY_OWNDATA)
-        printf("AN>> Array owns its data\n");
-    else
-        printf("AN>> Array does not own its data\n");
-
-    for (int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
-        printf("AN>> Array-dim{%d} = %ld\n", i_dim, npArray_dims[i_dim]);
-
-    // END DEBUG
-
-    // TODO: Verify array is 2d
-
-    // void *PyArray_GETPTR2(PyArrayObject *obj, npy_intp i, npy_intp j)
-    // Quick, inline access to the element at the given coordinates in the ndarray, obj, which must
-    // have 2 dimensions (this is not checked). The corresponding i, j,  coordinates can be any
-    // integer but will be interpreted as npy_intp. You may want to typecast the returned pointer to
-    // the data type of the ndarray.
-
-    // element-by-element copy of the Numpy array data (always work)
-    const std::size_t vsize = static_cast<std::size_t>(PyArray_SIZE(npArray_ptr));
-    std::vector<double> out1(vsize);
-    for (std::size_t i = 0; i < vsize; i++) {
-        out1[i] = *reinterpret_cast<double*>(PyArray_GETPTR1(npArray_ptr, i));
-    }
-
-    // using bulk memcpy-like operation, but keep in mind that NumPy ndarrays may be mis-aligned for
-    // the data type, have non-native byte order, or other subtle attributes that make such copies
-    // less than desirable.
-
-    std::vector<double> out2(vsize);
-    std::memcpy(out2.data(), PyArray_DATA(npArray_ptr), sizeof(double) * vsize);
-
-    // using either approach, out now contains a copy of the ndarray's data
-
-    double* npArray_data_ptr{reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
-    if (!npArray_data_ptr) {
-        // checkError();
-        throw std::runtime_error("PythonInterpreter: Cannot convert an invalid Numpy array to a "
-                                 "C-Array (invalid data pointer)");
-    }
-
-    // TODO: Problem with the reference to numpy array!!!
-    // returns a _new_ reference; ie. caller is responsible for the Python ref-count
-    return npArray_data_ptr;
-}
-
-
-Result<double*> NpyArray1D_to_C(PyObject* pyobject_ptr)
-{
-    if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: Cannot convert an invalid "
-                                            "Numpy array to a C-Array")};
-    }
-
-    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
-    PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
-    const int npArray_type = PyArray_TYPE(npArray_ptr);
-    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
-    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
-    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
-    // character code indicating the data type
-    const char npArray_dtype = npArray_descr->type;
-
-    // TODO: Enable type-casting for the Numpy array
-    if (npArray_type != NPY_DOUBLE) {
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Expected a Numpy array of type _double_ "
-               << "(given type '" << npArray_dtype << "')";
-        return {Status(Status::Type::Error, msg_ss.str())};
-    }
-
-    // DEBUG
-    // type checking
-    if (PyDataType_ISUNSIGNED(npArray_descr))
-        printf("AN>> Array datatype = uint\n");
-    else if (PyDataType_ISSIGNED(npArray_descr))
-        printf("AN>> Array datatype = int\n");
-    else if (PyDataType_ISFLOAT(npArray_descr))
-        printf("AN>> Array datatype = float\n");
-    else
-        printf("AN>> Array datatype = ?\n");
-
-    if (npArray_type == NPY_FLOAT)
-        printf("AN>> Array type (%d) = f32\n", npArray_type);
-
-    if (PyArray_IS_C_CONTIGUOUS(npArray_ptr))
-        printf("AN>> Array is C-contiguous (row-major)\n");
-
-    if (PyArray_IS_F_CONTIGUOUS(npArray_ptr))
-        printf("AN>> Array is Fortran-contiguous (column-major)\n");
-
-    if (PyArray_ISBEHAVED_RO(npArray_ptr) || PyArray_ISBEHAVED(npArray_ptr))
-        printf("AN>> Array is well-behaved\n");
-
-    if (npArray_flags & NPY_ARRAY_OWNDATA)
-        printf("AN>> Array owns its data\n");
-    else
-        printf("AN>> Array does not own its data\n");
-
-    for (int i_dim = 0; i_dim < npArray_ndim; ++i_dim)
-        printf("AN>> Array-dim{%d} = %ld\n", i_dim, npArray_dims[i_dim]);
-
-    // END DEBUG
-
-    // TODO: check if array is contiguous
-    double* npArray_data_ptr{reinterpret_cast<double*>(PyArray_DATA(npArray_ptr))};
-    if (!npArray_data_ptr) {
-        PythonInterpreter::checkError();
-        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: "
-                                            "Cannot convert an invalid Numpy array to a C-Array"
-                                            "(data pointer is invalid)")};
-    }
-
-    return {std::move(npArray_data_ptr)};
-}
-
-
-PyObjectPtr call_pyfunction_npy_array(PyObjectPtr& py_module, const std::string& fn_name,
-                                      PyObjectPtr& npy_array)
-{
-    // Call a Python function (from a given module) which takes a
-    // single Numpy array as input and returns a Numpy array.
-
-    if (!py_module.valid()) {
-        return {Status(Status::Type::Error, "PythonInterpreter: Cannot call a function "
-                                            "from an invalid Python module")};
-    }
-
-    PyObject* pFunc = PyObject_GetAttrString(py_module.ptr, fn_name.c_str());
-    if (!pFunc || !PyCallable_Check(pFunc)) {
-        PythonInterpreter::checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter: The function '" << fn_name << "' "
-               << "is not callable";
-        return {Status(Status::Type::Error, msg_ss.str())};
-    }
-
-    PyObject* pReturn = PyObject_CallFunctionObjArgs(pFunc, npy_array.ptr, NULL);
-    if (!PyArray_Check(pReturn)) {
-        PythonInterpreter::checkError();
-        Py_DecRef(pFunc);
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter: Invalid return value from calling "
-                  "the function '"
-               << fn_name << "' (expected a Numpy array)";
-        return {Status(Status::Type::Error, msg_ss.str())};
-    }
-
-    // returns a _new_ reference; ie. caller is responsible for the ref-count
-    return {pReturn};
-}
-
 } // namespace
 //----------------------------------------
 
@@ -598,7 +397,7 @@ PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
     const int npArray_ndim = PyArray_NDIM(npArray_ptr);
     npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
     const std::size_t npArray_size = static_cast<std::size_t>(PyArray_SIZE(npArray_ptr));
-    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
+    // const int npArray_flags = PyArray_FLAGS(npArray_ptr);
     // character code indicating the data type
     const char npArray_dtype = npArray_descr->type;
 
-- 
GitLab


From e0991fe73450ca56b85b7233de590d7f12ba5a88 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 21 Apr 2023 12:28:28 +0200
Subject: [PATCH 31/85] Turn off PyEmbedded tests [DEBUG]

---
 Tests/Unit/PyBinding/CMakeLists.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Tests/Unit/PyBinding/CMakeLists.txt b/Tests/Unit/PyBinding/CMakeLists.txt
index a44ba90285a..23b3c6c0165 100644
--- a/Tests/Unit/PyBinding/CMakeLists.txt
+++ b/Tests/Unit/PyBinding/CMakeLists.txt
@@ -2,6 +2,8 @@
  # BornAgain/Tests/Unit/PyBinding/CMakeLists.txt
 ############################################################################
 
+return()
+
 include(GoogleTest) # provides gtest_discover_tests
 
 set(test TestPyBinding)
-- 
GitLab


From 584f515173fc6d812b67f49405cfb1ec3c47602a Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 13:57:09 +0200
Subject: [PATCH 32/85] Move 'PyCore/Sample/Multilayer/' to 'PyCore/Sample/'

---
 PyCore/Sample/{Multilayer => }/ImportMultiLayer.cpp | 0
 PyCore/Sample/{Multilayer => }/ImportMultiLayer.h   | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename PyCore/Sample/{Multilayer => }/ImportMultiLayer.cpp (100%)
 rename PyCore/Sample/{Multilayer => }/ImportMultiLayer.h (100%)

diff --git a/PyCore/Sample/Multilayer/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
similarity index 100%
rename from PyCore/Sample/Multilayer/ImportMultiLayer.cpp
rename to PyCore/Sample/ImportMultiLayer.cpp
diff --git a/PyCore/Sample/Multilayer/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
similarity index 100%
rename from PyCore/Sample/Multilayer/ImportMultiLayer.h
rename to PyCore/Sample/ImportMultiLayer.h
-- 
GitLab


From edda1da85e0a1db140cf8801667d2331518cfba6 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 14:07:42 +0200
Subject: [PATCH 33/85] Move Status implementation to PyObjectPtr

---
 PyCore/Embed/PyObjectPtr.cpp | 40 +++++++++++++++++++++++++++++++++++-
 PyCore/Embed/PyObjectPtr.h   | 25 ++++++++++++++++++++--
 2 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index e7e908a1cc3..0a24fbbbbbc 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -1,10 +1,48 @@
 #include "PyObjectPtr.h"
 #include "PythonInterpreter.h"
 
-#include <exception>
+#include <exception> // runtime_error
 #include <sstream> // stringstream
 #include <string>
 
+
+Status::Status(const Status::Type type, const std::string& message)
+    : type{type}
+    , message{message}
+{
+}
+
+bool Status::isSet() const
+{
+    return (type != Status::Type::None);
+}
+
+
+bool Status::isOK() const
+{
+    return (type == Status::Type::Info);
+}
+
+
+bool Status::isWarning() const
+{
+    return (type == Status::Type::Warning);
+}
+
+
+bool Status::isError() const
+{
+    return (type == Status::Type::Error);
+}
+
+
+Status::operator bool() const
+{
+    return isOK() || isWarning();
+}
+
+//----------------------------------------
+
 PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr, const Status& status)
     : ptr{pyobject_ptr}
     , status{status}
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 41ffdb04eb3..e9de0100706 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -2,10 +2,31 @@
 #define PYOBJECTPTR_H
 
 #include "PyObjectDecl.h"
-#include "Result.h" // Status
 #include <vector>
+#include <string>
+
+// Indicator for the status of a result
+struct Status {
+    enum class Type { None = -1, Info, Warning, Error };
+    Type type = Type::None;
+
+    // status message
+    std::string message;
+
+    Status(const Status::Type type = Type::None, const std::string& message = "");
+
+    bool isSet() const;
+
+    bool isOK() const;
+
+    bool isWarning() const;
+
+    bool isError() const;
+
+    // cast to boolean (true if result is valid)
+    operator bool() const;
+};
 
-//========================================
 
 // safe container for PyObjects
 /*
-- 
GitLab


From fb783379c1478f803b1217c1c737ef81a20f747e Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 14:12:04 +0200
Subject: [PATCH 34/85] rm Result module

---
 PyCore/Embed/Result.cpp |  38 ------------
 PyCore/Embed/Result.h   | 134 ----------------------------------------
 2 files changed, 172 deletions(-)
 delete mode 100644 PyCore/Embed/Result.cpp
 delete mode 100644 PyCore/Embed/Result.h

diff --git a/PyCore/Embed/Result.cpp b/PyCore/Embed/Result.cpp
deleted file mode 100644
index c6561cd2a1d..00000000000
--- a/PyCore/Embed/Result.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "Result.h"
-
-Status::Status(const Status::Type type, const std::string& message)
-    : type{type}
-    , message{message}
-{
-}
-
-bool Status::isSet() const
-{
-    return (type != Status::Type::None);
-}
-
-
-bool Status::isOK() const
-{
-    return (type == Status::Type::Info);
-}
-
-
-bool Status::isWarning() const
-{
-    return (type == Status::Type::Warning);
-}
-
-
-bool Status::isError() const
-{
-    return (type == Status::Type::Error);
-}
-
-
-Status::operator bool() const
-{
-    return isOK() || isWarning();
-}
-
-//========================================
diff --git a/PyCore/Embed/Result.h b/PyCore/Embed/Result.h
deleted file mode 100644
index a2940c211d8..00000000000
--- a/PyCore/Embed/Result.h
+++ /dev/null
@@ -1,134 +0,0 @@
-#ifndef RESULT_H
-#define RESULT_H
-
-#include <string>
-#include <utility> // move
-
-// ========================================
-
-// Indicator for the status of a result
-struct Status {
-    enum class Type { None = -1, Info, Warning, Error };
-    Type type = Type::None;
-
-    // status message
-    std::string message;
-
-    Status(const Status::Type type = Type::None, const std::string& message = "");
-
-    bool isSet() const;
-
-    bool isOK() const;
-
-    bool isWarning() const;
-
-    bool isError() const;
-
-    // cast to boolean (true if result is valid)
-    operator bool() const;
-};
-
-//========================================
-
-// Container for Results which may fail
-/* A `Result<T>` instance contains a value of type `T` (or none) and
-   a `Status` which denotes the status of the value (OK/Warning/Error).
-   This is used to avoid throwing exceptions in a _core_ module,
-   leaving the decision to the callers (or the higher interface) whether
-   an exception should be raised or not.
-*/
-template <typename ResType>
-class Result {
-
-public:
-    using type = ResType;
-
-    // stored result
-    ResType val;
-
-    // status of the PyObject
-    Status status;
-
-    Result(ResType&& result, Status&& status);
-
-    Result(ResType&& result);
-
-    Result(Status&& status);
-
-    ~Result();
-
-    // disallow copy constructor
-    Result(const Result&) = delete;
-    // disallow copy assignment
-    Result& operator=(const Result&) = delete;
-    // allow move constructor
-    Result(Result&& other);
-    // reset the container to the initial status
-    void reset();
-    // check validity of the Result
-    bool valid() const;
-    // check if the Result is set
-    bool isSet() const;
-};
-
-
-//-- Implementation of Result
-
-template <typename ResType>
-Result<ResType>::Result(ResType&& result, Status&& status)
-    : val{std::move(result)}
-    , status{std::move(status)}
-{
-}
-
-
-template <typename ResType>
-Result<ResType>::Result(ResType&& result)
-    : Result(std::move(result), Status(Status::Type::Info))
-{
-}
-
-
-template <typename ResType>
-Result<ResType>::Result(Status&& status)
-    : status{std::move(status)}
-{
-}
-
-
-template <typename ResType>
-Result<ResType>::~Result()
-{
-}
-
-
-template <typename ResType>
-Result<ResType>::Result(Result<ResType>&& other)
-    : Result(std::move(other.val), std::move(other.status))
-{
-    // reset the moved object
-    other.reset();
-}
-
-
-template <typename ResType>
-void Result<ResType>::reset()
-{
-    status = Status();
-}
-
-
-template <typename ResType>
-bool Result<ResType>::valid() const
-{
-    return (status.isOK() || status.isWarning());
-}
-
-
-template <typename ResType>
-bool Result<ResType>::isSet() const
-{
-    return (status.isSet());
-}
-
-#endif // RESULT_H
-- 
GitLab


From 219ee2cf06d40aba6a0fbdd3f19cab92bfff528c Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 14:30:09 +0200
Subject: [PATCH 35/85] PyCore/Sample/ImportMultiLayer: avoid cyclic dependence
 on MultiLayer module; rm usage of Result class

---
 GUI/View/Project/PyImportAssistant.cpp |  9 ++++--
 PyCore/Sample/ImportMultiLayer.cpp     | 42 +++++++++++---------------
 PyCore/Sample/ImportMultiLayer.h       | 17 +++++++----
 3 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index a6a0028e8de..fe8d60d4f34 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -103,9 +103,12 @@ QString readFile(const QString& fileName)
 std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QString& funcName)
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
+    void* result_ptr = nullptr;
+    PyObjectPtr result{PythonInterpreter::createMultiLayerFromPython(
+            snippet.toStdString(), funcName.toStdString(), bornagainDir(), result_ptr)};
 
-    Result<std::unique_ptr<MultiLayer>> result{PythonInterpreter::createMultiLayerFromPython(
-        snippet.toStdString(), funcName.toStdString(), bornagainDir())};
+    std::unique_ptr<MultiLayer> multilayer_ptr(
+        reinterpret_cast<MultiLayer*>(result_ptr)->clone());
 
     if (!result.valid()) {
         QApplication::restoreOverrideCursor();
@@ -116,7 +119,7 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
 
     QApplication::restoreOverrideCursor();
 
-    return std::move(result.val);
+    return std::move(multilayer_ptr);
 }
 
 //! Lets user select a function name which generates a MultiLayer.
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 71e3661d8bb..3572d83f0da 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -1,28 +1,28 @@
 #ifdef BORNAGAIN_PYTHON
 
-#include "ImportMultiLayer.h"
 #include "PyCore/Embed/PyCore.h"
+#include "ImportMultiLayer.h"
 #include "PyCore/Embed/PythonInterpreter.h" // BornAgain::importScript
-#include "Sample/Multilayer/MultiLayer.h"
+
 // SWIG runtime access for creating a MultiLayer instance from Python
 #include "auto/Wrap/swig_runtime.h"
 
 
 namespace PythonInterpreter {
 
-Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(const std::string& script,
-                                                               const std::string& functionName,
-                                                               const std::string& path)
+PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
+                                       const std::string& script,
+                                       const std::string& functionName,
+                                       const std::string& path)
 {
     PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
     // locate the `get_simulation` function (it is an attribute of the module)
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.ptr, functionName.c_str());
     if (!pAddFn) {
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::BornAgain: "
-               << "Cannot locate the compiled function '" << functionName << "'";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter::BornAgain: "
+            "Cannot locate the compiled function '" + functionName + "'";
+        return {Status(Status::Type::Error, msg)};
     }
 
     // create a `MultiLayer` Python object via calling the function
@@ -32,10 +32,9 @@ Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(const std::string
     Py_DecRef(pAddFn);
 
     if (!instance) {
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::BornAgain: "
-               << "Cannot call the function '" << functionName << "'";
-        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg_ss.str()))};
+        std::string msg = "PythonInterpreter::BornAgain: "
+            "Cannot call the function '" + functionName + "'";
+        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
     }
 
     // Construct a C++ object from the Python object
@@ -49,20 +48,13 @@ Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(const std::string
     const int res = SWIG_ConvertPtr(instance, &argp1, pTypeInfo, 0);
     if (!SWIG_IsOK(res)) {
         Py_DecRef(instance);
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::BornAgain: "
-               << "SWIG failed to extract a 'MultiLayer' instance "
-               << "via calling the function '" << functionName << "'";
-        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg_ss.str()))};
+        std::string msg = "PythonInterpreter::BornAgain: SWIG failed to extract a "
+            "'MultiLayer' instance via calling the function '" + functionName + "'";
+        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
     }
 
-    MultiLayer* multilayer = reinterpret_cast<MultiLayer*>(argp1);
-    std::unique_ptr<MultiLayer> result_ptr(multilayer->clone());
-
-    Py_DecRef(instance);
-
-    // TODO: check if it is possible to move a unique_ptr
-    return {std::move(result_ptr)};
+    multilayer_ptr = reinterpret_cast<void*>(argp1);
+    return {instance};
 }
 
 } // namespace PythonInterpreter
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 4bf855213c4..b7b62250920 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -1,8 +1,9 @@
+#ifndef BORNAGAIN_PYCORE_MULTILAYER_H
+#define BORNAGAIN_PYCORE_MULTILAYER_H
+
 #ifndef SWIG
 
-#include "PyCore/Embed/PyObjectDecl.h"
-#include "PyCore/Embed/Result.h"
-#include <memory> // unique_ptr
+#include "PyCore/Embed/PyObjectPtr.h"
 #include <string>
 
 class MultiLayer;
@@ -13,10 +14,14 @@ namespace PythonInterpreter {
 //! @param script: Python script
 //! @param functionName: A function name in this script which produces a MultiLayer
 //! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-Result<std::unique_ptr<MultiLayer>> createMultiLayerFromPython(const std::string& script,
-                                                               const std::string& functionName,
-                                                               const std::string& path = "");
+//! @param multilayer_ptr: a void pointer to the resulting MultiLayer instance in C++
+PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
+                                       const std::string& script,
+                                       const std::string& functionName,
+                                       const std::string& path = "");
 
 } // namespace PythonInterpreter
 
 #endif // SWIG
+
+#endif // BORNAGAIN_PYCORE_MULTILAYER_H
-- 
GitLab


From 06bf782e472ebcd067eeb7b472f20f3199098512 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 15:14:02 +0200
Subject: [PATCH 36/85] PythonInterpreter: rm usages of stringstream and Result
 class

---
 PyCore/Embed/PythonInterpreter.cpp | 270 ++++++++++++++---------------
 PyCore/Embed/PythonInterpreter.h   |  10 +-
 2 files changed, 137 insertions(+), 143 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index bcd277e974b..6cfd5ddc63e 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -22,13 +22,10 @@ References:
 #include <cstddef> // NULL
 #include <cstring> // memcpy
 #include <memory>  // unique_ptr
-#include <sstream>
 #include <string>
 #include <vector>
-
-#include <iostream> // DEBUG
-//========================================
-
+#include <stdexcept> // runtime_error
+#include <iostream> // cerr
 
 namespace {
 //-- Auxiliary functions
@@ -64,8 +61,15 @@ std::string toString(const wchar_t* const c)
     return std::string(wstr.begin(), wstr.end());
 }
 
+std::string toString(const char* const c)
+{
+    if (!c)
+        return "";
+    return std::string(c);
+}
+
 //! Converts PyObject into vector of strings, if possible, or throws exception
-Result<std::vector<std::string>> toVectorString(PyObject* py_object)
+std::vector<std::string> toVectorString(PyObject* py_object)
 {
     std::vector<std::string> result;
 
@@ -79,10 +83,14 @@ Result<std::vector<std::string>> toVectorString(PyObject* py_object)
             PyObject* value = PyList_GetItem(py_object, i);
             result.push_back(PythonInterpreter::pyStrtoString(value));
         }
-    } else
-        return {Status(Status::Type::Error, "Cannnot convert the given Python object "
-                                            "to vector<string>.")};
-    return {std::move(result)};
+    } else {
+        throw(std::runtime_error(
+                  PythonInterpreter::errorDescription(
+                      "PythonInterpreter: Cannnot convert the given Python object "
+                      "to vector<string>.")));
+    }
+
+    return result;
 }
 
 
@@ -109,19 +117,19 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
 // Python stable ABI
 Status PythonInterpreter::initialize()
 {
-    std::stringstream msg_ss;
+    std::string msg;
 
     if (!Py_IsInitialized()) {
         Py_Initialize();
-        msg_ss << "PythonInterpreter: Initialized Python - "
-               << "version: " << Py_GetVersion();
-        return {Status::Type::Info, msg_ss.str()};
+        return {Status::Type::Info,
+            "PythonInterpreter: Initialized Python - version: "
+            + toString(Py_GetVersion())};
     }
 
     // if Python already initialized
-    msg_ss << "PythonInterpreter: Python already initialized - "
-           << "version: " << Py_GetVersion();
-    return {Status::Type::Warning, msg_ss.str()};
+    return {Status::Type::Warning,
+        "PythonInterpreter: Python already initialized - version: "
+        + toString(Py_GetVersion())};
 }
 
 // Python stable ABI
@@ -139,10 +147,8 @@ Status PythonInterpreter::finalize()
     // This is a no-op when called for a second time.
     Py_Finalize();
 
-    std::stringstream msg_ss;
-    msg_ss << "PythonInterpreter: Finalized Python - "
-           << "version: " << Py_GetVersion();
-    return {Status::Type::Info, msg_ss.str()};
+    return {Status::Type::Info,
+        "PythonInterpreter: Finalized Python - version: " + toString(Py_GetVersion())};
 }
 
 
@@ -188,11 +194,9 @@ PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const st
     PyObject* pymodule = PyImport_ImportModule(pymodule_name.c_str());
     if (!pymodule || !PyModule_Check(pymodule)) {
         checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter: Cannot load Python module "
-               << "'" << pymodule_name << "' "
-               << "(given path = '" << path << "')";
-        return PyObjectPtr(Status(Status::Type::Error, msg_ss.str()));
+        return {Status(Status::Type::Error,
+                       errorDescription("PythonInterpreter: Cannot load Python module '"
+                                        + pymodule_name + "' (given path = '" + path + "')"))};
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
@@ -222,10 +226,10 @@ std::string PythonInterpreter::pyStrtoString(PyObject* py_object)
 
 std::string PythonInterpreter::runtimeInfo()
 {
-    std::stringstream result;
+    std::string result;
 
     // Runtime environment
-    result << std::string(60, '=') << "\n";
+    result = std::string(60, '=') + "\n";
     /* DEBUG
 
     result << "PATH: " << BaseUtils::System::getenv("PATH") << "\n";
@@ -235,29 +239,28 @@ std::string PythonInterpreter::runtimeInfo()
     */
 
     // Embedded Python details
-    result << "Py_GetProgramName(): " << toString(Py_GetProgramName()) << "\n";
-    result << "Py_GetProgramFullPath(): " << toString(Py_GetProgramFullPath()) << "\n";
-    result << "Py_GetPath(): " << toString(Py_GetPath()) << "\n";
-    result << "Py_GetPythonHome(): " << toString(Py_GetPythonHome()) << "\n";
+    result =+ "Py_GetProgramName(): " + toString(Py_GetProgramName()) + "\n";
+    result =+ "Py_GetProgramFullPath(): " + toString(Py_GetProgramFullPath()) + "\n";
+    result =+ "Py_GetPath(): " + toString(Py_GetPath()) + "\n";
+    result =+ "Py_GetPythonHome(): " + toString(Py_GetPythonHome()) + "\n";
 
     // Runtime Python's sys.path
     PyObject* sysPath = PySys_GetObject((char*)"path");
-    Result<std::vector<std::string>> content{toVectorString(sysPath)};
-    if (content.valid()) {
-        result << "sys.path: ";
-        for (auto s : content.val)
-            result << s << ",";
-        result << "\n";
+    std::vector<std::string> content{toVectorString(sysPath)};
+    result += "sys.path: ";
+    for (const std::string& s : content) {
+        result += s + ",";
     }
+    result += "\n";
 
-    return result.str();
+    return result;
 }
 
 // Attempt to retrieve Python stack trace
 // Ref: <https://stackoverflow.com/q/1796510>
 std::string PythonInterpreter::stackTrace()
 {
-    std::stringstream result;
+    std::string result;
 
     if (PyErr_Occurred()) {
         PyObject *ptype, *pvalue, *ptraceback, *pystr;
@@ -265,7 +268,7 @@ std::string PythonInterpreter::stackTrace()
         PyErr_Fetch(&ptype, &pvalue, &ptraceback);
         pystr = PyObject_Str(pvalue);
         if (char* str = PyBytes_AsString(pystr))
-            result << std::string(str) << "\n";
+            result += std::string(str) + "\n";
         Py_DecRef(pystr);
 
         PyObject* module_name = PyUnicode_FromString("traceback");
@@ -273,7 +276,7 @@ std::string PythonInterpreter::stackTrace()
         Py_DecRef(module_name);
 
         if (py_traceback_module) {
-            result << "\n";
+            result += "\n";
             PyObject* pyth_func = PyObject_GetAttrString(py_traceback_module, "format_exception");
             if (pyth_func && PyCallable_Check(pyth_func)) {
                 PyObject* pyth_val =
@@ -282,31 +285,29 @@ std::string PythonInterpreter::stackTrace()
                 if (pyth_val) {
                     pystr = PyObject_Str(pyth_val);
                     if (char* str = PyBytes_AsString(pystr))
-                        result << std::string(str);
+                        result += std::string(str);
                     Py_DecRef(pystr);
                     Py_DecRef(pyth_val);
                 }
             }
-            result << "\n";
+            result += "\n";
         }
 
         Py_DecRef(py_traceback_module);
     }
 
-    result << "\n";
-    result << PythonInterpreter::runtimeInfo();
-    result << "\n";
+    result += "\n";
+    result += PythonInterpreter::runtimeInfo();
+    result += "\n";
 
-    return result.str();
+    return result;
 }
 
 
 std::string PythonInterpreter::errorDescription(const std::string& title)
 {
-    std::stringstream msg;
-    msg << title << "\n";
-    msg << PythonInterpreter::stackTrace() << "\n";
-    return msg.str();
+    std::string msg = title + "\n" + PythonInterpreter::stackTrace() + "\n";
+    return msg;
 }
 
 //----------------------------------------
@@ -340,11 +341,11 @@ bool PythonInterpreter::Numpy::isInitialized()
 
 //----------------------------------------
 
-Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
+NpArrayDescr PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: "
-                                            "Invalid Numpy array")};
+        throw(std::runtime_error(
+                  errorDescription("PythonInterpreter::Numpy: Invalid Numpy array")));
     }
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -379,16 +380,17 @@ Result<NpArrayDescr> PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobjec
     if (npArray_flags & NPY_ARRAY_OWNDATA)
         np_descr_out.owns_data = true;
 
-    return {std::move(np_descr_out)};
+    return np_descr_out;
 }
 
 
-Result<std::vector<double>>
+std::vector<double>
 PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: Cannot convert an invalid "
-                                            "Numpy array to a C-Array")};
+        throw(std::runtime_error(
+                  errorDescription("PythonInterpreter::Numpy: Cannot convert an invalid "
+                                   "Numpy array to a C-Array")));
     }
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -403,20 +405,20 @@ PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 
     // Numpy array type must be 2d
     if (npArray_ndim != 2) {
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Expected a Numpy 2d-array "
-               << "(given number of dimensions is " << npArray_ndim << ")";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter::Numpy: Expected a Numpy 2d-array "
+            "(given number of dimensions is " + std::to_string(npArray_ndim) + ")";
+        throw(std::runtime_error(
+                  errorDescription("PythonInterpreter::Numpy: Expected a Numpy 2d-array "
+                                   "(given number of dimensions is "
+                                   + std::to_string(npArray_ndim) + ")")));
     }
 
     // Numpy array type must be numeric and real (eligible for casting to double)
     if (!PyDataType_ISNUMBER(npArray_descr) || PyDataType_ISCOMPLEX(npArray_descr)) {
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Expected a Numpy array of numeric type and real "
-               << "(given type '" << npArray_dtype << "')";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter::Numpy: "
+            "Expected a Numpy array of numeric type and real "
+            "(given type '" + std::to_string(npArray_dtype) + "')";
+        throw(std::runtime_error(errorDescription(msg)));
     }
 
     std::vector<double> data(npArray_size);
@@ -472,20 +474,14 @@ PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 #endif // _WIN32
 
     default:
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Conversion of Numpy array of dtype '" << npArray_dtype << "' "
-               << "to vector<double> is not implemented";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        throw(std::runtime_error(
+                  errorDescription("PythonInterpreter::Numpy: "
+                                   "Conversion of Numpy array of dtype '"
+                                   + std::to_string(npArray_dtype) + "' "
+                                   "to vector<double> is not implemented")));
     }
 
-    std::stringstream msg_ss;
-    msg_ss << "PythonInterpreter::Numpy: "
-           << "Numpy 2d-array of dtype '" << npArray_dtype << "' "
-           << "and shape (" << npArray_dims[0] << "," << npArray_dims[1] << ") "
-           << "converted to vector<double> with size " << data.size();
-
-    return {std::move(data), Status(Status::Type::Info, msg_ss.str())};
+    return data;
 }
 
 
@@ -511,11 +507,10 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
     PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 2, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Cannot create a Numpy 1D-array from the "
-               << "given data (size = " << size << ")";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter::Numpy: "
+            "Cannot create a Numpy 1D-array from the "
+            "given data (size = " + std::to_string(size) + ")";
+        return {Status(Status::Type::Error, errorDescription(msg))};
     }
 
     // obtain pointer to data buffer of Numpy array
@@ -542,8 +537,8 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
 
     if (size < 1) {
         return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot create a Numpy 1D-array from a data with size = 0")};
+                       errorDescription("PythonInterpreter::Numpy: "
+                                        "Cannot create a Numpy 1D-array from a data with size = 0"))};
     }
 
     npy_intp npDims[1] = {size};
@@ -552,11 +547,10 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
     PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 1, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Cannot create a Numpy 1D-array from the "
-               << "given data (size = " << size << ")";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter::Numpy: "
+            "Cannot create a Numpy 1D-array from the "
+            "given data (size = " + std::to_string(size) + ")";
+        return {Status(Status::Type::Error, errorDescription(msg))};
     }
 
     // obtain pointer to data buffer of Numpy array
@@ -576,15 +570,15 @@ PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const
 {
     if (!c_array) {
         return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot create a Numpy 2D-array from a null data pointer")};
+                       errorDescription("PythonInterpreter::Numpy: "
+                                        "Cannot create a Numpy 2D-array from a null data pointer"))};
     }
 
     const np_size_t size = dims[0] * dims[1];
     if (size < 1) {
         return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot create a Numpy 2D-array from a data with size = 0")};
+                       errorDescription("PythonInterpreter::Numpy: "
+                                        "Cannot create a Numpy 2D-array from a data with size = 0"))};
     }
 
     npy_intp npDims[2] = {dims[0], dims[1]};
@@ -595,8 +589,9 @@ PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const
 
     if (!npArray_ptr || !PyArray_Check(npArray_ptr)) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error, "PythonInterpreter::Numpy: Cannot convert the given "
-                                            "C-Array to a Numpy 2D-array")};
+        return {Status(Status::Type::Error,
+                       errorDescription("PythonInterpreter::Numpy: Cannot convert the given "
+                                        "C-Array to a Numpy 2D-array"))};
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
@@ -610,14 +605,15 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
     const std::size_t n_dims = dimensions.size();
     if (n_dims < 1) {
         return {Status(Status::Type::Error,
-                       "Cannot make a Numpy with the given number of dimensions; "
-                       "number of dimensions must be >= 1")};
+                       errorDescription("Cannot make a Numpy with the given number "
+                                        "of dimensions; number of dimensions must be >= 1"))};
     }
 
     for (std::size_t d = 0; d < n_dims; ++d) {
         if (dimensions[d] < 2) {
-            return {Status(Status::Type::Error, "Cannot make a Numpy with the given dimensions; "
-                                                "dimensions must be >= 2")};
+            return {Status(Status::Type::Error,
+                           errorDescription("Cannot make a Numpy with the given dimensions; "
+                                            "dimensions must be >= 2"))};
         }
     }
 
@@ -632,18 +628,16 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
 
     if (!npyArray_ptr) {
         checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Cannot create a Numpy " << n_dims << "D-array "
-               << "from the given data";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter::Numpy: Cannot create a Numpy "
+            + std::to_string(n_dims) + "D-array from the given data";
+        return {Status(Status::Type::Error, errorDescription(msg))};
     }
 
     return {npyArray_ptr};
 }
 
 
-Result<double*> PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
+double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 {
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -653,13 +647,12 @@ Result<double*> PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
     if (!data_ptr) {
         checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter::Numpy: "
-               << "Numpy array has invalid data pointer";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        throw(std::runtime_error(
+                  errorDescription("PythonInterpreter::Numpy: "
+                                   "Numpy array has invalid data pointer")));
     }
 
-    return {std::move(data_ptr)};
+    return data_ptr;
 }
 
 //----------------------------------------
@@ -680,10 +673,9 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
 
     if (!ba_pymodule || !PyModule_Check(ba_pymodule)) {
         checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter: Cannot load 'bornagain' Python module "
-               << "(given path = '" << path << "')";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        std::string msg = "PythonInterpreter: Cannot load 'bornagain' Python module "
+            "(given path = '" + path + "')";
+        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
     }
 
     // restores single handler to make ctr-c alive.
@@ -721,15 +713,16 @@ PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script
 }
 
 
-Result<std::vector<std::string>>
+std::vector<std::string>
 PythonInterpreter::BornAgain::listOfFunctions(const std::string& script, const std::string& path)
 {
     PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
     PyObject* pDict = PyModule_GetDict(tmpModule.ptr);
     if (!pDict) {
-        return {Status(Status::Type::Error, "PythonInterpreter::BornAgain: "
-                                            "Cannot obtain the dictionary from the script module")};
+        checkError();
+        throw(std::runtime_error("PythonInterpreter::BornAgain: "
+                                 "Cannot obtain the dictionary from the script module"));
     }
 
     PyObject *key, *value;
@@ -745,7 +738,7 @@ PythonInterpreter::BornAgain::listOfFunctions(const std::string& script, const s
 
     Py_DecRef(pDict);
 
-    return {std::move(fn_names)};
+    return fn_names;
 }
 
 //----------------------------------------
@@ -762,25 +755,27 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     // filename (Python string) and returns a Numpy array.
 
     if (!fabio_module.valid() || !PyModule_Check(fabio_module.ptr)) {
-        return {Status(Status::Type::Error, "PythonInterpreter.fabio: Invalid Python module "
-                                            "(expected 'fabio' module)")};
+        return {Status(Status::Type::Error,
+                       errorDescription("PythonInterpreter.fabio: Invalid Python module "
+                                        "(expected 'fabio' module)"))};
     }
 
     PyObject* pFunc = PyObject_GetAttrString(fabio_module.ptr, (char*)"open");
     if (!pFunc || !PyCallable_Check(pFunc)) {
         PythonInterpreter::checkError();
         return {Status(Status::Type::Error,
-                       "PythonInterpreter.fabio: The function 'fabio.open' is not callable")};
+                       errorDescription("PythonInterpreter.fabio: "
+                                        "The function 'fabio.open' is not callable"))};
     }
 
     // convert the filename to a Python unicode string
     PyObject* pFilename = PyUnicode_FromString(filename.c_str());
     if (!pFilename) {
         PythonInterpreter::checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter.fabio: Filename '" << filename << "' "
-               << "cannot be converted to Python unicode string";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        return {Status(Status::Type::Error,
+                       errorDescription("PythonInterpreter.fabio: Filename '"
+                                        + filename + "' "
+                                        "cannot be converted to Python unicode string"))};
     }
 
     // get the result of the call `fabio.open(<filename>)`
@@ -788,10 +783,10 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     Py_DecRef(pFunc);
     if (!pResult_open) {
         PythonInterpreter::checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter.fabio: Invalid return value from "
-               << "calling the function 'fabio.open(\"" << filename << "\")'";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        return {Status(Status::Type::Error,
+                       errorDescription("PythonInterpreter.fabio: "
+                                        "Invalid return value from calling the function "
+                                        "'fabio.open(\"" + filename + "\")'"))};
     }
 
     // get `result.data` (must be a Numpy array)
@@ -799,11 +794,10 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     Py_DecRef(pResult_open);
     if (!npyArray_ptr || !PyArray_Check(npyArray_ptr)) {
         PythonInterpreter::checkError();
-        std::stringstream msg_ss;
-        msg_ss << "PythonInterpreter.fabio: Invalid return value from "
-               << "calling the function 'fabio.open(\"" << filename << "\")' "
-               << "(expected a Numpy array)";
-        return {Status(Status::Type::Error, msg_ss.str())};
+        return {Status(Status::Type::Error,
+                       errorDescription("PythonInterpreter.fabio: Invalid return value from "
+                                        "calling the function 'fabio.open(\""
+                                        + filename + "\")' (expected a Numpy array)"))};
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index f3423477f7c..9ebf623b908 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -55,13 +55,13 @@ Status initialize();
 bool isInitialized();
 
 //! Obtains the descriptor of a Numpy array
-Result<NpArrayDescr> arrayDescriptor(PyObject* pyobject_ptr);
+NpArrayDescr arrayDescriptor(PyObject* pyobject_ptr);
 
 //! Creates an empty Numpy array of type `double` with the given dimensions
 PyObjectPtr arrayND(std::vector<std::size_t>& dimensions);
 
 //! Returns the pointer to the data buffer of a Numpy array of type `double`
-Result<double*> getDataPtr(PyObject* pyobject_ptr);
+double* getDataPtr(PyObject* pyobject_ptr);
 
 //! Creates a Numpy 1D-array from a contiguous C-array of type `double`.
 // Data will be copied.
@@ -72,7 +72,7 @@ PyObjectPtr createArray1DfromC(double* const c_array, const np_size_t size);
 PyObjectPtr createArray2DfromC(double* const c_array, const np_size_t dims[2]);
 
 // !Creates a `vector<double>` from a Numpy 2D-array (data will be copied).
-Result<std::vector<double>> createVectorFromArray2D(PyObject* pyobject_ptr);
+std::vector<double> createVectorFromArray2D(PyObject* pyobject_ptr);
 
 // !Creates a 2D Numpy array from a contiguous C-array of type `double`.
 // The data is _not_ copied, and the Numpy array will _not_ own its data.
@@ -91,8 +91,8 @@ PyObjectPtr importScript(const std::string& script, const std::string& path);
 //! Returns list of functions defined in the script.
 //! @param script: Python script
 //! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-Result<std::vector<std::string>> listOfFunctions(const std::string& script,
-                                                 const std::string& path);
+std::vector<std::string> listOfFunctions(const std::string& script,
+                                         const std::string& path);
 
 } // namespace BornAgain
 
-- 
GitLab


From 3976648f86290c0fa3362a94ea0e141b3e7530df Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 8 May 2023 14:14:38 +0200
Subject: [PATCH 37/85] PythonInterpreter: add an explanation for "Python
 stable ABI" + cleanup

---
 PyCore/Embed/PythonInterpreter.cpp | 28 +++-------------------------
 1 file changed, 3 insertions(+), 25 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 6cfd5ddc63e..98c35801436 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -28,7 +28,6 @@ References:
 #include <iostream> // cerr
 
 namespace {
-//-- Auxiliary functions
 
 // thin wrapper to initialize Numpy
 std::nullptr_t _init_numpy(int& status)
@@ -114,6 +113,9 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
 } // namespace
 //----------------------------------------
 
+// NOTE: "Python stable ABI" denotes the functions which use _only_ the Python's stable ABI;
+// see <https://docs.python.org/3/c-api/stable.html>
+
 // Python stable ABI
 Status PythonInterpreter::initialize()
 {
@@ -138,7 +140,6 @@ bool PythonInterpreter::isInitialized()
     return static_cast<bool>(Py_IsInitialized());
 }
 
-
 // Python stable ABI
 Status PythonInterpreter::finalize()
 {
@@ -151,7 +152,6 @@ Status PythonInterpreter::finalize()
         "PythonInterpreter: Finalized Python - version: " + toString(Py_GetVersion())};
 }
 
-
 // Python stable ABI
 Status PythonInterpreter::checkError()
 {
@@ -166,7 +166,6 @@ Status PythonInterpreter::checkError()
     return {Status::Type::Info, "No Python errors occured"};
 }
 
-
 // Python stable ABI
 void PythonInterpreter::addPythonPath(const std::string& path)
 {
@@ -203,14 +202,12 @@ PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const st
     return {pymodule};
 }
 
-
 // Python stable ABI
 void PythonInterpreter::DecRef(PyObject* py_object)
 {
     Py_XDECREF(py_object);
 }
 
-
 std::string PythonInterpreter::pyStrtoString(PyObject* py_object)
 {
     std::string result;
@@ -223,21 +220,12 @@ std::string PythonInterpreter::pyStrtoString(PyObject* py_object)
     return result;
 }
 
-
 std::string PythonInterpreter::runtimeInfo()
 {
     std::string result;
 
     // Runtime environment
     result = std::string(60, '=') + "\n";
-    /* DEBUG
-
-    result << "PATH: " << BaseUtils::System::getenv("PATH") << "\n";
-    result << "PYTHONPATH: " << BaseUtils::System::getenv("PYTHONPATH") << "\n";
-    result << "PYTHONHOME: " << BaseUtils::System::getenv("PYTHONHOME") << "\n";
-
-    */
-
     // Embedded Python details
     result =+ "Py_GetProgramName(): " + toString(Py_GetProgramName()) + "\n";
     result =+ "Py_GetProgramFullPath(): " + toString(Py_GetProgramFullPath()) + "\n";
@@ -303,7 +291,6 @@ std::string PythonInterpreter::stackTrace()
     return result;
 }
 
-
 std::string PythonInterpreter::errorDescription(const std::string& title)
 {
     std::string msg = title + "\n" + PythonInterpreter::stackTrace() + "\n";
@@ -333,7 +320,6 @@ Status PythonInterpreter::Numpy::initialize()
     }
 }
 
-
 bool PythonInterpreter::Numpy::isInitialized()
 {
     return static_cast<bool>(PyArray_API);
@@ -383,7 +369,6 @@ NpArrayDescr PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
     return np_descr_out;
 }
 
-
 std::vector<double>
 PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
@@ -484,7 +469,6 @@ PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
     return data;
 }
 
-
 PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
                                                          const np_size_t dims[2])
 {
@@ -525,7 +509,6 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
     return {npArray_ptr};
 }
 
-
 PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
                                                          const np_size_t size)
 {
@@ -598,7 +581,6 @@ PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const
     return {npArray_ptr};
 }
 
-
 PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensions)
 {
     // descriptors for the array dimensions
@@ -636,7 +618,6 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
     return {npyArray_ptr};
 }
 
-
 double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 {
 
@@ -654,7 +635,6 @@ double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
     return data_ptr;
 }
-
 //----------------------------------------
 
 PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
@@ -686,7 +666,6 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
     return {ba_pymodule};
 }
 
-
 PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script,
                                                        const std::string& path)
 {
@@ -740,7 +719,6 @@ PythonInterpreter::BornAgain::listOfFunctions(const std::string& script, const s
 
     return fn_names;
 }
-
 //----------------------------------------
 
 PyObjectPtr PythonInterpreter::Fabio::import()
-- 
GitLab


From 921f17f1227e6c9d51aaf3ede98bdb2b715c2bd2 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 16:15:04 +0200
Subject: [PATCH 38/85] Datafield::npArray: rm usage of Result class

---
 Device/Data/Datafield.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index 336b82c9d57..58e88ebea20 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -228,12 +228,12 @@ PyObject* Datafield::npArray() const
         return nullptr;
 
     // get the pointer to the data buffer of the array (assumed to be C-contiguous)
-    Result<double*> data{PythonInterpreter::Numpy::getDataPtr(pyarray.ptr)};
+    double* data{PythonInterpreter::Numpy::getDataPtr(pyarray.ptr)};
 
-    if (!data.valid())
+    if (!data)
         return nullptr;
 
-    double* array_buffer = data.val;
+    double* array_buffer = data;
 
     // filling numpy array with output_data
     if (rank() == 2) {
-- 
GitLab


From 21f555fadda7e7ad3fb9e03c8a2f19c224d6b0dd Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 17:19:34 +0200
Subject: [PATCH 39/85] Datafield::npArray: add assertion to disallow rank() >
 2

---
 Device/Data/Datafield.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index 58e88ebea20..f9f4aa00b3b 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -214,6 +214,9 @@ PyObject* Datafield::npArray() const
     Status init{PythonInterpreter::Numpy::initialize()};
     if (init.isError())
         return nullptr;
+
+    ASSERT(rank() <= 2);
+
     std::vector<size_t> dimensions;
     for (size_t i = 0; i < rank(); i++)
         dimensions.push_back(axis(i).size());
@@ -246,8 +249,6 @@ PyObject* Datafield::npArray() const
     } else if (rank() == 1) {
         for (size_t i = 0; i < size(); ++i)
             *array_buffer++ = (*this)[i];
-    } else {
-        return nullptr;
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
-- 
GitLab


From ca8eb9e4e2f8bde2650d29981ca60cfd38e61f78 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 16:15:12 +0200
Subject: [PATCH 40/85] PyImportAssistant: rm usage of Result class

fx PyImportAssistant
---
 GUI/View/Project/PyImportAssistant.cpp | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index fe8d60d4f34..a1308c8c465 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -30,7 +30,7 @@
 #include "GUI/View/Project/ProjectManager.h"
 #include "GUI/View/Tool/Globals.h"
 #include "PyCore/Embed/PythonInterpreter.h"            // listOfFunctions
-#include "PyCore/Sample/Multilayer/ImportMultiLayer.h" // createMultiLayerFromPython
+#include "PyCore/Sample/ImportMultiLayer.h" // createMultiLayerFromPython
 #include "Sample/Multilayer/MultiLayer.h"
 #include <QApplication>
 #include <QFileDialog>
@@ -104,8 +104,8 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
     void* result_ptr = nullptr;
-    PyObjectPtr result{PythonInterpreter::createMultiLayerFromPython(
-            snippet.toStdString(), funcName.toStdString(), bornagainDir(), result_ptr)};
+    PyObjectPtr result{PythonInterpreter::createMultiLayerFromPython(result_ptr,
+            snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
     std::unique_ptr<MultiLayer> multilayer_ptr(
         reinterpret_cast<MultiLayer*>(result_ptr)->clone());
@@ -119,7 +119,7 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
 
     QApplication::restoreOverrideCursor();
 
-    return std::move(multilayer_ptr);
+    return multilayer_ptr;
 }
 
 //! Lets user select a function name which generates a MultiLayer.
@@ -154,19 +154,18 @@ QString getPySampleFunctionName(const QString& snippet)
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
 
-    Result<std::vector<std::string>> funcs_res{
+    std::vector<std::string> funcs_res{
         PythonInterpreter::BornAgain::listOfFunctions(snippet.toStdString(), bornagainDir())};
 
-    if (!funcs_res.valid()) {
+    if (funcs_res.empty()) {
         QApplication::restoreOverrideCursor();
         QString message("Exception thrown while acquiring functions from Python code.\n\n");
-        QString details = QString::fromStdString(funcs_res.status.message);
-        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
+        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, "").exec();
         return "";
     }
 
     QApplication::restoreOverrideCursor();
-    QStringList funcList{GUI::Util::String::fromStdStrings(funcs_res.val)};
+    QStringList funcList{GUI::Util::String::fromStdStrings(funcs_res)};
     return selectPySampleFunction(funcList);
 }
 
-- 
GitLab


From 791437d9ec91894c6d60b2b2c763a56c44e43a45 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 15:12:55 +0200
Subject: [PATCH 41/85] PyObjectPtr: Minor improvements

---
 PyCore/Embed/PyObjectPtr.cpp |  3 +--
 PyCore/Embed/PyObjectPtr.h   | 10 ++--------
 2 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index 0a24fbbbbbc..d951b44ecf7 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -1,8 +1,7 @@
 #include "PyObjectPtr.h"
 #include "PythonInterpreter.h"
 
-#include <exception> // runtime_error
-#include <sstream> // stringstream
+#include <stdexcept> // runtime_error
 #include <string>
 
 
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index e9de0100706..8313279a4b9 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -45,19 +45,13 @@ public:
     Status status;
 
     PyObjectPtr(PyObject* pyobject_ptr, const Status& status);
-
     PyObjectPtr(PyObject* pyobject_ptr);
-
     PyObjectPtr(const Status& status);
-
     ~PyObjectPtr();
-
-    // disallow copy constructor
     PyObjectPtr(const PyObjectPtr&) = delete;
-    // disallow copy assignment
-    PyObjectPtr& operator=(const PyObjectPtr&) = delete;
-    // allow move constructor
     PyObjectPtr(PyObjectPtr&& other);
+    PyObjectPtr& operator=(const PyObjectPtr&) = delete;
+
     // reset the container to the initial status (does _not_ release the Python resource)
     void reset();
     // reset the container to the initial status and return the PyObject pointer (does _not_ release
-- 
GitLab


From e713f4b718fc00af6f3f7a34c6f2eeb59e4a580a Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 17:28:45 +0200
Subject: [PATCH 42/85] PyObjectPtr: define Status as a class, instead of a
 struct

---
 PyCore/Embed/PyObjectPtr.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 8313279a4b9..49fb7af3e8d 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -6,7 +6,8 @@
 #include <string>
 
 // Indicator for the status of a result
-struct Status {
+class Status {
+public:
     enum class Type { None = -1, Info, Warning, Error };
     Type type = Type::None;
 
-- 
GitLab


From 3d62c93209e05ae41db6102e9562a56ba528bc85 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 17:25:48 +0200
Subject: [PATCH 43/85] PyObjectPtr: rename NpArrayDescr to
 NumpyArrayDescriptor

---
 PyCore/Embed/PyObjectPtr.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 49fb7af3e8d..a17ccce0aa6 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -68,10 +68,10 @@ public:
 
 
 // Numpy array descriptor
-struct NpArrayDescr {
+struct NumpyArrayDescriptor {
     // Numpy array descriptors
-    bool C_contiguous = false;
-    bool F_contiguous = false;
+    bool C_contiguous = false;  // C-contiguous array flag
+    bool F_contiguous = false;  // FORTRAN-contiguous array flag
     // whether the data area of arr is aligned and in machine byte-order
     bool wellbehaved = false;
     // number of dimensions
-- 
GitLab


From 19b614ad302054f35b9cdf0722e06410f9b469e5 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 17:26:11 +0200
Subject: [PATCH 44/85] PythonInterpreter: rename NpArrayDescr to
 NumpyArrayDescriptor

---
 PyCore/Embed/PythonInterpreter.cpp | 4 ++--
 PyCore/Embed/PythonInterpreter.h   | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 98c35801436..69228a73236 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -327,7 +327,7 @@ bool PythonInterpreter::Numpy::isInitialized()
 
 //----------------------------------------
 
-NpArrayDescr PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
+NumpyArrayDescriptor PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
         throw(std::runtime_error(
@@ -344,7 +344,7 @@ NpArrayDescr PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
     const char npArray_dtype = npArray_descr->type;
 
 
-    NpArrayDescr np_descr_out;
+    NumpyArrayDescriptor np_descr_out;
     np_descr_out.n_dims = npArray_ndim;
     if (np_descr_out.n_dims > 0) {
         np_descr_out.dims.resize(npArray_ndim);
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 9ebf623b908..5d1c241f70c 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -55,7 +55,7 @@ Status initialize();
 bool isInitialized();
 
 //! Obtains the descriptor of a Numpy array
-NpArrayDescr arrayDescriptor(PyObject* pyobject_ptr);
+NumpyArrayDescriptor arrayDescriptor(PyObject* pyobject_ptr);
 
 //! Creates an empty Numpy array of type `double` with the given dimensions
 PyObjectPtr arrayND(std::vector<std::size_t>& dimensions);
-- 
GitLab


From 04a8c62f35557f92e50911d2e080579e3d70cb29 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Fri, 28 Apr 2023 17:30:55 +0200
Subject: [PATCH 45/85] PyObjectDecl: rm USER_API

---
 PyCore/Embed/PyObjectDecl.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/PyCore/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
index dfdd0e71c31..12b12be4428 100644
--- a/PyCore/Embed/PyObjectDecl.h
+++ b/PyCore/Embed/PyObjectDecl.h
@@ -1,7 +1,5 @@
 // forward declarations needed for using `PyObject`
 
-#ifndef USER_API
-
 #ifndef PYOBJECT_H
 #define PYOBJECT_H
 
@@ -13,4 +11,3 @@ typedef _object PyObject;
 typedef long int np_size_t; // size type used in Numpy
 
 #endif // PYOBJECT_H
-#endif // USER_API
-- 
GitLab


From 996f35490de137a63dab32da6c188d2000f94611 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 8 May 2023 11:50:20 +0200
Subject: [PATCH 46/85] PyCore: add the standard initial comment blocks to all
 header files

---
 PyCore/Embed/PyObjectDecl.h      | 20 ++++++++++++++++----
 PyCore/Embed/PyObjectPtr.h       | 20 +++++++++++++++++---
 PyCore/Embed/PythonInterpreter.h | 20 +++++++++++++++++---
 PyCore/Sample/ImportMultiLayer.h | 20 +++++++++++++++++---
 4 files changed, 67 insertions(+), 13 deletions(-)

diff --git a/PyCore/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
index 12b12be4428..715cc1aa599 100644
--- a/PyCore/Embed/PyObjectDecl.h
+++ b/PyCore/Embed/PyObjectDecl.h
@@ -1,7 +1,19 @@
-// forward declarations needed for using `PyObject`
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Embed/PyObjectDecl.h
+//! @brief     Forward declarations needed for using `PyObject`.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
 
-#ifndef PYOBJECT_H
-#define PYOBJECT_H
+#ifndef BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
+#define BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
 
 #ifndef PyObject_HEAD
 struct _object;
@@ -10,4 +22,4 @@ typedef _object PyObject;
 
 typedef long int np_size_t; // size type used in Numpy
 
-#endif // PYOBJECT_H
+#endif // BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index a17ccce0aa6..8e7d0076c82 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -1,5 +1,19 @@
-#ifndef PYOBJECTPTR_H
-#define PYOBJECTPTR_H
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Embed/PyObjectPtr.h
+//! @brief     Defines classes PyObjectPtr and Status.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
+#define BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 
 #include "PyObjectDecl.h"
 #include <vector>
@@ -84,4 +98,4 @@ struct NumpyArrayDescriptor {
     bool owns_data = false;
 };
 
-#endif // PYOBJECTPTR_H
+#endif // BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 5d1c241f70c..0a4a2fc5249 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -1,5 +1,19 @@
-#ifndef PYTHONINTRP_H
-#define PYTHONINTRP_H
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Embed/PythonInterpreter.h
+//! @brief     Definition of functions and classes to expose Python-interpreter functionality to C++.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
+#define BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
 
 #include "PyObjectPtr.h"
 #include <string>
@@ -107,4 +121,4 @@ PyObjectPtr open(const std::string& filename, PyObjectPtr& fabio_module);
 
 } // namespace PythonInterpreter
 
-#endif // PYTHONINTRP_H
+#endif // BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index b7b62250920..57d3e4a9120 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -1,5 +1,19 @@
-#ifndef BORNAGAIN_PYCORE_MULTILAYER_H
-#define BORNAGAIN_PYCORE_MULTILAYER_H
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Sample/ImportMultiLayer.h
+//! @brief     Forward declaration for createMultiLayerFromPython.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
+#ifndef BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
+#define BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
 
 #ifndef SWIG
 
@@ -24,4 +38,4 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
 
 #endif // SWIG
 
-#endif // BORNAGAIN_PYCORE_MULTILAYER_H
+#endif // BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
-- 
GitLab


From 8651f1947819dff716799fee2c0a3753add35486 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 8 May 2023 12:19:18 +0200
Subject: [PATCH 47/85] PyCore: use Doxygen style for comments

---
 PyCore/Embed/PyCore.h            |  7 ++--
 PyCore/Embed/PyObjectDecl.h      |  4 ++-
 PyCore/Embed/PyObjectPtr.h       | 56 ++++++++++++++++----------------
 PyCore/Embed/PythonInterpreter.h | 30 ++++++++---------
 PyCore/Sample/ImportMultiLayer.h | 12 ++++---
 5 files changed, 56 insertions(+), 53 deletions(-)

diff --git a/PyCore/Embed/PyCore.h b/PyCore/Embed/PyCore.h
index 42027dff657..211857da2bf 100644
--- a/PyCore/Embed/PyCore.h
+++ b/PyCore/Embed/PyCore.h
@@ -36,11 +36,12 @@
 #ifdef INCLUDE_NUMPY
 
 #ifdef BORNAGAIN_PYTHON_API_SWIG
-/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` and `NO_IMPORT_ARRAY` one can use
-   the Numpy Array API in multiple files for a single extension module.
-*/
+
+// NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` and `NO_IMPORT_ARRAY` one can use
+// the Numpy Array API in multiple files for a single extension module.
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 #define NO_IMPORT_ARRAY
+
 #endif // BORNAGAIN_PYTHON_API_SWIG
 
 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION // to prevent spurious warning
diff --git a/PyCore/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
index 715cc1aa599..c1af8b2a02d 100644
--- a/PyCore/Embed/PyObjectDecl.h
+++ b/PyCore/Embed/PyObjectDecl.h
@@ -16,9 +16,11 @@
 #define BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
 
 #ifndef PyObject_HEAD
+
 struct _object;
 typedef _object PyObject;
-#endif
+
+#endif // PyObject_HEAD
 
 typedef long int np_size_t; // size type used in Numpy
 
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 8e7d0076c82..a8c5a3f1ecc 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -19,13 +19,13 @@
 #include <vector>
 #include <string>
 
-// Indicator for the status of a result
+//! Indicator for the status of a result
 class Status {
 public:
     enum class Type { None = -1, Info, Warning, Error };
     Type type = Type::None;
 
-    // status message
+    //! Status message
     std::string message;
 
     Status(const Status::Type type = Type::None, const std::string& message = "");
@@ -38,25 +38,23 @@ public:
 
     bool isError() const;
 
-    // cast to boolean (true if result is valid)
+    //! Cast the validity status to boolean (true if result is valid)
     operator bool() const;
 };
 
 
-// safe container for PyObjects
-/*
-The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and a `Status` which denotes the status
-of the value (OK/Warning/Error). Decrementing Python reference-count is performed automatically when
-a `PyObjectPtr` expires.
-*/
-
+//! Safe container for PyObjects
+//! The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and
+//! a `Status` which denotes the status of the value (OK/Warning/Error).
+//! Decrementing Python reference-count is performed automatically when
+//! a `PyObjectPtr` expires.
 class PyObjectPtr {
 
 public:
-    // raw pointer to the PyObject
+    //! Raw pointer to the PyObject
     PyObject* ptr = nullptr;
 
-    // status of the PyObject
+    //! Status of the PyObject
     Status status;
 
     PyObjectPtr(PyObject* pyobject_ptr, const Status& status);
@@ -67,34 +65,36 @@ public:
     PyObjectPtr(PyObjectPtr&& other);
     PyObjectPtr& operator=(const PyObjectPtr&) = delete;
 
-    // reset the container to the initial status (does _not_ release the Python resource)
+    //! Resets the container to the initial status (does _not_ release the Python resource)
     void reset();
-    // reset the container to the initial status and return the PyObject pointer (does _not_ release
-    // the Python resource)
+    //! Resets the container to the initial status and return the PyObject pointer
+    //! (does _not_ release the Python resource)
     PyObject* release();
-    // discards the Python resource (decrements the Python reference)
+    //! Discards the Python resource (decrements the Python reference)
     void discard();
-    // check validity of the PyObjectPtr
+    //! Check the validity of the PyObjectPtr
     bool valid() const;
-    // check if the PyObjectPtr is set
+    //! Checks whether the PyObjectPtr is set
     bool isSet() const;
 };
 
 
-// Numpy array descriptor
-struct NumpyArrayDescriptor {
-    // Numpy array descriptors
-    bool C_contiguous = false;  // C-contiguous array flag
-    bool F_contiguous = false;  // FORTRAN-contiguous array flag
-    // whether the data area of arr is aligned and in machine byte-order
+//! Numpy array descriptors
+struct NumpyArrayDescriptor
+{
+    //! C-contiguous array flag
+    bool C_contiguous = false;
+    //! FORTRAN-contiguous array flag
+    bool F_contiguous = false;
+    //! Flag to indicate if the data area of the Numpy array is aligned and in machine byte-order
     bool wellbehaved = false;
-    // number of dimensions
+    //! Number of dimensions
     int n_dims = 0;
-    // sizes of dimensions
+    //! Sizes of dimensions
     std::vector<np_size_t> dims;
-    // character code indicating the data type
+    //! Character code indicating the data type
     char dtype = '\0';
-    // whether Numpy array owns its data
+    //! Flag to indicate whether the Numpy array owns its data
     bool owns_data = false;
 };
 
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 0a4a2fc5249..e98a1ae63cc 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -21,7 +21,6 @@
 
 class MultiLayer;
 
-// Embedded Python interpreter
 namespace PythonInterpreter {
 
 // Python stable ABI
@@ -62,10 +61,10 @@ std::string errorDescription(const std::string& title = "");
 //-- Numpy-related functionality
 namespace Numpy {
 
-// initialize Numpy
+//! Initializes Numpy
 Status initialize();
 
-// Python stable ABI
+//! Checks if Numpy is initialized
 bool isInitialized();
 
 //! Obtains the descriptor of a Numpy array
@@ -81,40 +80,39 @@ double* getDataPtr(PyObject* pyobject_ptr);
 // Data will be copied.
 PyObjectPtr createArray1DfromC(double* const c_array, const np_size_t size);
 
-// !Creates a Numpy 2D-array from a contiguous C-array of type `double`.
-// Data will be copied.
+//! Creates a Numpy 2D-array from a contiguous C-array of type `double`.
+//! Data will be copied.
 PyObjectPtr createArray2DfromC(double* const c_array, const np_size_t dims[2]);
 
-// !Creates a `vector<double>` from a Numpy 2D-array (data will be copied).
+//! Creates a `vector<double>` from a Numpy 2D-array (data will be copied).
 std::vector<double> createVectorFromArray2D(PyObject* pyobject_ptr);
 
-// !Creates a 2D Numpy array from a contiguous C-array of type `double`.
-// The data is _not_ copied, and the Numpy array will _not_ own its data.
+//! Creates a 2D Numpy array from a contiguous C-array of type `double`.
+//! The data is _not_ copied, and the Numpy array will _not_ own its data.
 PyObjectPtr CArrayAsNpy2D(double* const c_array, const np_size_t dims[2]);
 
 } // namespace Numpy
 
-//-- BornAgain-related functionality
+// BornAgain-related functionality
 namespace BornAgain {
 //! Imports BornAgain from given location. If path is empty, tries to rely on PYTHONPATH.
 PyObjectPtr import(const std::string& path = "");
 
-// import a 'BornAgain' Python script
+//! Imports a 'BornAgain' Python script
 PyObjectPtr importScript(const std::string& script, const std::string& path);
 
-//! Returns list of functions defined in the script.
-//! @param script: Python script
-//! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
+//! Returns list of functions defined in the script, given a script filename and
+//! a path to import BornAgain library (if empty, relies on `PYTHONPATH`).
 std::vector<std::string> listOfFunctions(const std::string& script,
                                          const std::string& path);
 
 } // namespace BornAgain
 
-//-- Fabio-related functionality
+// Fabio-related functionality
 namespace Fabio {
-// import 'fabio' Python module
+//! Imports 'fabio' Python module
 PyObjectPtr import();
-// call 'fabio.open' on a given file (needs Numpy)
+//! Calls 'fabio.open' on a given file (needs Numpy)
 PyObjectPtr open(const std::string& filename, PyObjectPtr& fabio_module);
 
 } // namespace Fabio
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 57d3e4a9120..48d56c60683 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -24,11 +24,13 @@ class MultiLayer;
 
 namespace PythonInterpreter {
 
-//! Creates a multi layer by running python code in embedded interpreter.
-//! @param script: Python script
-//! @param functionName: A function name in this script which produces a MultiLayer
-//! @param path: A path to import BornAgain library. If empty, relies on PYTHONPATH
-//! @param multilayer_ptr: a void pointer to the resulting MultiLayer instance in C++
+//! Creates a multi layer by running python code in embedded interpreter, given
+//! The Python-script filename and the name of the function which produces a `MultiLayer`.
+//! A path can be given to import `BornAgain` library (if empty, relies on `PYTHONPATH`).
+//! Finally, if successful, `multilayer_ptr` will be a void pointer to the resulting
+// `MultiLayer` instance in C++ (memory should be managed by the caller).
+//! The returned value is the PyObject corresponding the intermediate _Python_ instance of
+//! of the `MultiLayer` class.
 PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
                                        const std::string& script,
                                        const std::string& functionName,
-- 
GitLab


From 27ca76f88a60bf99cfe3a1f0f35e6f56db2a65c3 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Mon, 8 May 2023 12:24:21 +0200
Subject: [PATCH 48/85] PyObjectPtr: make the raw pointer to the Python object
 a private member

---
 Device/Data/Datafield.cpp          |  2 +-
 PyCore/Embed/PyObjectPtr.cpp       | 22 +++++++++++++---------
 PyCore/Embed/PyObjectPtr.h         |  9 ++++++---
 PyCore/Embed/PythonInterpreter.cpp |  6 +++---
 PyCore/Sample/ImportMultiLayer.cpp |  2 +-
 5 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index f9f4aa00b3b..e9e6d96fed1 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -231,7 +231,7 @@ PyObject* Datafield::npArray() const
         return nullptr;
 
     // get the pointer to the data buffer of the array (assumed to be C-contiguous)
-    double* data{PythonInterpreter::Numpy::getDataPtr(pyarray.ptr)};
+    double* data{PythonInterpreter::Numpy::getDataPtr(pyarray.get())};
 
     if (!data)
         return nullptr;
diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index d951b44ecf7..d4ff222b511 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -43,12 +43,12 @@ Status::operator bool() const
 //----------------------------------------
 
 PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr, const Status& status)
-    : ptr{pyobject_ptr}
-    , status{status}
+    : status{status}
+    , m_ptr{pyobject_ptr}
 {
     // if PyObject pointer is null but result type is not `None` or `Error`,
     // then that will be an `Error` case.
-    if (!ptr && status.type != Status::Type::None && status.type != Status::Type::Error) {
+    if (!m_ptr && status.type != Status::Type::None && status.type != Status::Type::Error) {
         this->status.type = Status::Type::Error;
     }
 }
@@ -73,16 +73,20 @@ PyObjectPtr::~PyObjectPtr()
 
 
 PyObjectPtr::PyObjectPtr(PyObjectPtr&& other)
-    : PyObjectPtr(other.ptr, other.status)
+    : PyObjectPtr(other.get(), other.status)
 {
     // reset the moved object
     other.reset();
 }
 
+PyObject* PyObjectPtr::get()
+{
+    return m_ptr;
+}
 
 PyObject* PyObjectPtr::release()
 {
-    PyObject* pyobject_ptr{ptr};
+    PyObject* pyobject_ptr{m_ptr};
     reset();
     return pyobject_ptr;
 }
@@ -90,7 +94,7 @@ PyObject* PyObjectPtr::release()
 
 void PyObjectPtr::reset()
 {
-    ptr = nullptr;
+    m_ptr = nullptr;
     status = Status();
 }
 
@@ -103,18 +107,18 @@ void PyObjectPtr::discard()
                                  "(segmentation fault)"));
     }
 
-    PythonInterpreter::DecRef(ptr);
+    PythonInterpreter::DecRef(m_ptr);
     reset();
 }
 
 
 bool PyObjectPtr::valid() const
 {
-    return (ptr && (status.isOK() || status.isWarning()));
+    return (m_ptr && (status.isOK() || status.isWarning()));
 }
 
 
 bool PyObjectPtr::isSet() const
 {
-    return (ptr && status.isSet());
+    return (m_ptr && status.isSet());
 }
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index a8c5a3f1ecc..ae4061f6015 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -51,9 +51,6 @@ public:
 class PyObjectPtr {
 
 public:
-    //! Raw pointer to the PyObject
-    PyObject* ptr = nullptr;
-
     //! Status of the PyObject
     Status status;
 
@@ -65,6 +62,8 @@ public:
     PyObjectPtr(PyObjectPtr&& other);
     PyObjectPtr& operator=(const PyObjectPtr&) = delete;
 
+    //! Returns the raw pointer to the PyObject
+    PyObject* get();
     //! Resets the container to the initial status (does _not_ release the Python resource)
     void reset();
     //! Resets the container to the initial status and return the PyObject pointer
@@ -76,6 +75,10 @@ public:
     bool valid() const;
     //! Checks whether the PyObjectPtr is set
     bool isSet() const;
+
+private:
+    //! Raw pointer to the PyObject
+    PyObject* m_ptr = nullptr;
 };
 
 
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 69228a73236..acba3c00d24 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -697,7 +697,7 @@ PythonInterpreter::BornAgain::listOfFunctions(const std::string& script, const s
 {
     PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
-    PyObject* pDict = PyModule_GetDict(tmpModule.ptr);
+    PyObject* pDict = PyModule_GetDict(tmpModule.get());
     if (!pDict) {
         checkError();
         throw(std::runtime_error("PythonInterpreter::BornAgain: "
@@ -732,13 +732,13 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     // load an image via calling `fabio.load` function which takes a
     // filename (Python string) and returns a Numpy array.
 
-    if (!fabio_module.valid() || !PyModule_Check(fabio_module.ptr)) {
+    if (!fabio_module.valid() || !PyModule_Check(fabio_module.get())) {
         return {Status(Status::Type::Error,
                        errorDescription("PythonInterpreter.fabio: Invalid Python module "
                                         "(expected 'fabio' module)"))};
     }
 
-    PyObject* pFunc = PyObject_GetAttrString(fabio_module.ptr, (char*)"open");
+    PyObject* pFunc = PyObject_GetAttrString(fabio_module.get(), (char*)"open");
     if (!pFunc || !PyCallable_Check(pFunc)) {
         PythonInterpreter::checkError();
         return {Status(Status::Type::Error,
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 3572d83f0da..66d674de1a9 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -18,7 +18,7 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
     PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
     // locate the `get_simulation` function (it is an attribute of the module)
-    PyObject* pAddFn = PyObject_GetAttrString(tmpModule.ptr, functionName.c_str());
+    PyObject* pAddFn = PyObject_GetAttrString(tmpModule.get(), functionName.c_str());
     if (!pAddFn) {
         std::string msg = "PythonInterpreter::BornAgain: "
             "Cannot locate the compiled function '" + functionName + "'";
-- 
GitLab


From 8b9e516557359914263d23390becd748e11c6d34 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 9 May 2023 10:24:02 +0200
Subject: [PATCH 49/85] PyCore/Sample/ImportMultiLayer: fix the check for SWIG
 macro definition

---
 PyCore/Sample/ImportMultiLayer.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 48d56c60683..998e952cad3 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -15,7 +15,9 @@
 #ifndef BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
 #define BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
 
-#ifndef SWIG
+#ifdef SWIG
+#error no need to expose this header to Swig
+#endif
 
 #include "PyCore/Embed/PyObjectPtr.h"
 #include <string>
@@ -38,6 +40,4 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
 
 } // namespace PythonInterpreter
 
-#endif // SWIG
-
 #endif // BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
-- 
GitLab


From cd33d691e15bc7ed00f1f2c5f783441e2eb272f2 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 9 May 2023 10:26:44 +0200
Subject: [PATCH 50/85] PyCore: fix include statements

---
 PyCore/Embed/PyObjectPtr.h         | 4 ++--
 PyCore/Embed/PythonInterpreter.cpp | 2 +-
 PyCore/Embed/PythonInterpreter.h   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index ae4061f6015..858c80da365 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -15,9 +15,9 @@
 #ifndef BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 #define BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 
-#include "PyObjectDecl.h"
-#include <vector>
+#include "PyCore/Embed/PyObjectDecl.h"
 #include <string>
+#include <vector>
 
 //! Indicator for the status of a result
 class Status {
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index acba3c00d24..5002c10fedd 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -14,7 +14,7 @@ References:
 */
 
 #define INCLUDE_NUMPY // also include Numpy via PyCore
-#include "PyCore.h"
+#include "PyCore/Embed/PyCore.h"
 #undef INCLUDE_NUMPY
 
 #include "PythonInterpreter.h"
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index e98a1ae63cc..412a4454afb 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -15,7 +15,7 @@
 #ifndef BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
 #define BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
 
-#include "PyObjectPtr.h"
+#include "PyCore/Embed/PyObjectPtr.h"
 #include <string>
 #include <vector>
 
-- 
GitLab


From 3ff62454b497707d48fa45a52e02de4a8aca14b6 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Tue, 9 May 2023 10:27:23 +0200
Subject: [PATCH 51/85] PyCore: rm Makefile (used only for debugging)

---
 PyCore/Embed/Makefile | 28 ----------------------------
 1 file changed, 28 deletions(-)
 delete mode 100644 PyCore/Embed/Makefile

diff --git a/PyCore/Embed/Makefile b/PyCore/Embed/Makefile
deleted file mode 100644
index 57a593ed7eb..00000000000
--- a/PyCore/Embed/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-.PHONY: all clean py
-
-all: py
-
-
-CC=gcc
-LINK=gcc
-CXX=g++
-
-CXX_INCLUDES=-I/usr/include/python3.9 -I$${HOME}/Projects/bornagain -I$${HOME}/.local/opt/libheinz/include
-CXX_LINKS=-lpython3.9
-
-BA_FLAGS=-DBORNAGAIN_PYTHON
-CXX_CFLAGS=-Wall -Wextra -Wpedantic -fsanitize=undefined ${BA_FLAGS} ${CXX_INCLUDES}
-CXX_LFLAGS= ${CXX_LINKS}  # -Wl,-verbose
-CPP_CFLAGS=-std=c++17
-
-py: PythonInterpreter.cpp PythonInterpreter.h PyObjectPtr.cpp PyObjectDecl.h
-	@echo "Making an embedded Python interpreter..."
-	${CXX} -c -fPIC Result.cpp ${CPP_CFLAGS} ${CXX_CFLAGS}
-	${CXX} -c -fPIC PythonInterpreter.cpp PyObjectPtr.cpp ${CPP_CFLAGS} ${CXX_CFLAGS}
-	${CXX} -shared -o libpythonembed.so PythonInterpreter.o PyObjectPtr.o Result.o ${CXX_CFLAGS}
-	${CXX} -o testEmbeddedPython.exe test_embedded_python.cpp ${CXX_CFLAGS} ${CPP_CFLAGS} -L. -lpythonembed ${CXX_LFLAGS} '-Wl,--disable-new-dtags,-rpath,$$ORIGIN'
-
-
-clean:
-	@echo "Cleaning all products..."
-	rm *.o *.out *.exe
-- 
GitLab


From 988998e13d353138d5a03013d11debf022baf4ad Mon Sep 17 00:00:00 2001
From: "a.nejati.debug@debian-ci" <a.nejati.debug@debian-ci>
Date: Mon, 8 May 2023 15:07:09 +0200
Subject: [PATCH 52/85] Apply clang-format

---
 GUI/View/Project/PyImportAssistant.cpp |   9 +-
 PyCore/Embed/PyObjectPtr.h             |   3 +-
 PyCore/Embed/PythonInterpreter.cpp     | 124 +++++++++++++------------
 PyCore/Embed/PythonInterpreter.h       |   6 +-
 PyCore/Sample/ImportMultiLayer.cpp     |  17 ++--
 PyCore/Sample/ImportMultiLayer.h       |   3 +-
 6 files changed, 82 insertions(+), 80 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index a1308c8c465..2ff81c4b76a 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -29,7 +29,7 @@
 #include "GUI/View/Info/MessageBox.h"
 #include "GUI/View/Project/ProjectManager.h"
 #include "GUI/View/Tool/Globals.h"
-#include "PyCore/Embed/PythonInterpreter.h"            // listOfFunctions
+#include "PyCore/Embed/PythonInterpreter.h" // listOfFunctions
 #include "PyCore/Sample/ImportMultiLayer.h" // createMultiLayerFromPython
 #include "Sample/Multilayer/MultiLayer.h"
 #include <QApplication>
@@ -104,11 +104,10 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
     void* result_ptr = nullptr;
-    PyObjectPtr result{PythonInterpreter::createMultiLayerFromPython(result_ptr,
-            snippet.toStdString(), funcName.toStdString(), bornagainDir())};
+    PyObjectPtr result{PythonInterpreter::createMultiLayerFromPython(
+        result_ptr, snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
-    std::unique_ptr<MultiLayer> multilayer_ptr(
-        reinterpret_cast<MultiLayer*>(result_ptr)->clone());
+    std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr)->clone());
 
     if (!result.valid()) {
         QApplication::restoreOverrideCursor();
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 858c80da365..dca6fb3cf49 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -83,8 +83,7 @@ private:
 
 
 //! Numpy array descriptors
-struct NumpyArrayDescriptor
-{
+struct NumpyArrayDescriptor {
     //! C-contiguous array flag
     bool C_contiguous = false;
     //! FORTRAN-contiguous array flag
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 5002c10fedd..f03d99cf2bd 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -19,13 +19,13 @@ References:
 
 #include "PythonInterpreter.h"
 
-#include <cstddef> // NULL
-#include <cstring> // memcpy
-#include <memory>  // unique_ptr
+#include <cstddef>   // NULL
+#include <cstring>   // memcpy
+#include <iostream>  // cerr
+#include <memory>    // unique_ptr
+#include <stdexcept> // runtime_error
 #include <string>
 #include <vector>
-#include <stdexcept> // runtime_error
-#include <iostream> // cerr
 
 namespace {
 
@@ -83,10 +83,9 @@ std::vector<std::string> toVectorString(PyObject* py_object)
             result.push_back(PythonInterpreter::pyStrtoString(value));
         }
     } else {
-        throw(std::runtime_error(
-                  PythonInterpreter::errorDescription(
-                      "PythonInterpreter: Cannnot convert the given Python object "
-                      "to vector<string>.")));
+        throw(std::runtime_error(PythonInterpreter::errorDescription(
+            "PythonInterpreter: Cannnot convert the given Python object "
+            "to vector<string>.")));
     }
 
     return result;
@@ -124,14 +123,12 @@ Status PythonInterpreter::initialize()
     if (!Py_IsInitialized()) {
         Py_Initialize();
         return {Status::Type::Info,
-            "PythonInterpreter: Initialized Python - version: "
-            + toString(Py_GetVersion())};
+                "PythonInterpreter: Initialized Python - version: " + toString(Py_GetVersion())};
     }
 
     // if Python already initialized
-    return {Status::Type::Warning,
-        "PythonInterpreter: Python already initialized - version: "
-        + toString(Py_GetVersion())};
+    return {Status::Type::Warning, "PythonInterpreter: Python already initialized - version: "
+                                       + toString(Py_GetVersion())};
 }
 
 // Python stable ABI
@@ -149,7 +146,7 @@ Status PythonInterpreter::finalize()
     Py_Finalize();
 
     return {Status::Type::Info,
-        "PythonInterpreter: Finalized Python - version: " + toString(Py_GetVersion())};
+            "PythonInterpreter: Finalized Python - version: " + toString(Py_GetVersion())};
 }
 
 // Python stable ABI
@@ -227,10 +224,10 @@ std::string PythonInterpreter::runtimeInfo()
     // Runtime environment
     result = std::string(60, '=') + "\n";
     // Embedded Python details
-    result =+ "Py_GetProgramName(): " + toString(Py_GetProgramName()) + "\n";
-    result =+ "Py_GetProgramFullPath(): " + toString(Py_GetProgramFullPath()) + "\n";
-    result =+ "Py_GetPath(): " + toString(Py_GetPath()) + "\n";
-    result =+ "Py_GetPythonHome(): " + toString(Py_GetPythonHome()) + "\n";
+    result = +"Py_GetProgramName(): " + toString(Py_GetProgramName()) + "\n";
+    result = +"Py_GetProgramFullPath(): " + toString(Py_GetProgramFullPath()) + "\n";
+    result = +"Py_GetPath(): " + toString(Py_GetPath()) + "\n";
+    result = +"Py_GetPythonHome(): " + toString(Py_GetPythonHome()) + "\n";
 
     // Runtime Python's sys.path
     PyObject* sysPath = PySys_GetObject((char*)"path");
@@ -330,8 +327,8 @@ bool PythonInterpreter::Numpy::isInitialized()
 NumpyArrayDescriptor PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        throw(std::runtime_error(
-                  errorDescription("PythonInterpreter::Numpy: Invalid Numpy array")));
+        throw(
+            std::runtime_error(errorDescription("PythonInterpreter::Numpy: Invalid Numpy array")));
     }
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -369,13 +366,12 @@ NumpyArrayDescriptor PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobjec
     return np_descr_out;
 }
 
-std::vector<double>
-PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
+std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
         throw(std::runtime_error(
-                  errorDescription("PythonInterpreter::Numpy: Cannot convert an invalid "
-                                   "Numpy array to a C-Array")));
+            errorDescription("PythonInterpreter::Numpy: Cannot convert an invalid "
+                             "Numpy array to a C-Array")));
     }
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -391,18 +387,20 @@ PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
     // Numpy array type must be 2d
     if (npArray_ndim != 2) {
         std::string msg = "PythonInterpreter::Numpy: Expected a Numpy 2d-array "
-            "(given number of dimensions is " + std::to_string(npArray_ndim) + ")";
+                          "(given number of dimensions is "
+                          + std::to_string(npArray_ndim) + ")";
         throw(std::runtime_error(
-                  errorDescription("PythonInterpreter::Numpy: Expected a Numpy 2d-array "
-                                   "(given number of dimensions is "
-                                   + std::to_string(npArray_ndim) + ")")));
+            errorDescription("PythonInterpreter::Numpy: Expected a Numpy 2d-array "
+                             "(given number of dimensions is "
+                             + std::to_string(npArray_ndim) + ")")));
     }
 
     // Numpy array type must be numeric and real (eligible for casting to double)
     if (!PyDataType_ISNUMBER(npArray_descr) || PyDataType_ISCOMPLEX(npArray_descr)) {
         std::string msg = "PythonInterpreter::Numpy: "
-            "Expected a Numpy array of numeric type and real "
-            "(given type '" + std::to_string(npArray_dtype) + "')";
+                          "Expected a Numpy array of numeric type and real "
+                          "(given type '"
+                          + std::to_string(npArray_dtype) + "')";
         throw(std::runtime_error(errorDescription(msg)));
     }
 
@@ -459,11 +457,11 @@ PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 #endif // _WIN32
 
     default:
-        throw(std::runtime_error(
-                  errorDescription("PythonInterpreter::Numpy: "
-                                   "Conversion of Numpy array of dtype '"
-                                   + std::to_string(npArray_dtype) + "' "
-                                   "to vector<double> is not implemented")));
+        throw(std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+                                                  "Conversion of Numpy array of dtype '"
+                                                  + std::to_string(npArray_dtype)
+                                                  + "' "
+                                                    "to vector<double> is not implemented")));
     }
 
     return data;
@@ -492,8 +490,9 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
     if (!npArray_ptr) {
         checkError();
         std::string msg = "PythonInterpreter::Numpy: "
-            "Cannot create a Numpy 1D-array from the "
-            "given data (size = " + std::to_string(size) + ")";
+                          "Cannot create a Numpy 1D-array from the "
+                          "given data (size = "
+                          + std::to_string(size) + ")";
         return {Status(Status::Type::Error, errorDescription(msg))};
     }
 
@@ -519,9 +518,10 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
     }
 
     if (size < 1) {
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter::Numpy: "
-                                        "Cannot create a Numpy 1D-array from a data with size = 0"))};
+        return {
+            Status(Status::Type::Error,
+                   errorDescription("PythonInterpreter::Numpy: "
+                                    "Cannot create a Numpy 1D-array from a data with size = 0"))};
     }
 
     npy_intp npDims[1] = {size};
@@ -531,8 +531,9 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
     if (!npArray_ptr) {
         checkError();
         std::string msg = "PythonInterpreter::Numpy: "
-            "Cannot create a Numpy 1D-array from the "
-            "given data (size = " + std::to_string(size) + ")";
+                          "Cannot create a Numpy 1D-array from the "
+                          "given data (size = "
+                          + std::to_string(size) + ")";
         return {Status(Status::Type::Error, errorDescription(msg))};
     }
 
@@ -552,16 +553,18 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
 PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const np_size_t dims[2])
 {
     if (!c_array) {
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter::Numpy: "
-                                        "Cannot create a Numpy 2D-array from a null data pointer"))};
+        return {
+            Status(Status::Type::Error,
+                   errorDescription("PythonInterpreter::Numpy: "
+                                    "Cannot create a Numpy 2D-array from a null data pointer"))};
     }
 
     const np_size_t size = dims[0] * dims[1];
     if (size < 1) {
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter::Numpy: "
-                                        "Cannot create a Numpy 2D-array from a data with size = 0"))};
+        return {
+            Status(Status::Type::Error,
+                   errorDescription("PythonInterpreter::Numpy: "
+                                    "Cannot create a Numpy 2D-array from a data with size = 0"))};
     }
 
     npy_intp npDims[2] = {dims[0], dims[1]};
@@ -611,7 +614,7 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
     if (!npyArray_ptr) {
         checkError();
         std::string msg = "PythonInterpreter::Numpy: Cannot create a Numpy "
-            + std::to_string(n_dims) + "D-array from the given data";
+                          + std::to_string(n_dims) + "D-array from the given data";
         return {Status(Status::Type::Error, errorDescription(msg))};
     }
 
@@ -628,9 +631,8 @@ double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
     if (!data_ptr) {
         checkError();
-        throw(std::runtime_error(
-                  errorDescription("PythonInterpreter::Numpy: "
-                                   "Numpy array has invalid data pointer")));
+        throw(std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+                                                  "Numpy array has invalid data pointer")));
     }
 
     return data_ptr;
@@ -654,7 +656,8 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
     if (!ba_pymodule || !PyModule_Check(ba_pymodule)) {
         checkError();
         std::string msg = "PythonInterpreter: Cannot load 'bornagain' Python module "
-            "(given path = '" + path + "')";
+                          "(given path = '"
+                          + path + "')";
         return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
     }
 
@@ -692,8 +695,8 @@ PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script
 }
 
 
-std::vector<std::string>
-PythonInterpreter::BornAgain::listOfFunctions(const std::string& script, const std::string& path)
+std::vector<std::string> PythonInterpreter::BornAgain::listOfFunctions(const std::string& script,
+                                                                       const std::string& path)
 {
     PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
@@ -751,9 +754,9 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     if (!pFilename) {
         PythonInterpreter::checkError();
         return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter.fabio: Filename '"
-                                        + filename + "' "
-                                        "cannot be converted to Python unicode string"))};
+                       errorDescription("PythonInterpreter.fabio: Filename '" + filename
+                                        + "' "
+                                          "cannot be converted to Python unicode string"))};
     }
 
     // get the result of the call `fabio.open(<filename>)`
@@ -764,7 +767,8 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
         return {Status(Status::Type::Error,
                        errorDescription("PythonInterpreter.fabio: "
                                         "Invalid return value from calling the function "
-                                        "'fabio.open(\"" + filename + "\")'"))};
+                                        "'fabio.open(\""
+                                        + filename + "\")'"))};
     }
 
     // get `result.data` (must be a Numpy array)
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 412a4454afb..2702649333a 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -3,7 +3,8 @@
 //  BornAgain: simulate and fit reflection and scattering
 //
 //! @file      PyCore/Embed/PythonInterpreter.h
-//! @brief     Definition of functions and classes to expose Python-interpreter functionality to C++.
+//! @brief     Definition of functions and classes to expose Python-interpreter functionality to
+//! C++.
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
@@ -103,8 +104,7 @@ PyObjectPtr importScript(const std::string& script, const std::string& path);
 
 //! Returns list of functions defined in the script, given a script filename and
 //! a path to import BornAgain library (if empty, relies on `PYTHONPATH`).
-std::vector<std::string> listOfFunctions(const std::string& script,
-                                         const std::string& path);
+std::vector<std::string> listOfFunctions(const std::string& script, const std::string& path);
 
 } // namespace BornAgain
 
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 66d674de1a9..45550aa80ef 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -1,7 +1,7 @@
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyCore/Embed/PyCore.h"
 #include "ImportMultiLayer.h"
+#include "PyCore/Embed/PyCore.h"
 #include "PyCore/Embed/PythonInterpreter.h" // BornAgain::importScript
 
 // SWIG runtime access for creating a MultiLayer instance from Python
@@ -10,10 +10,8 @@
 
 namespace PythonInterpreter {
 
-PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
-                                       const std::string& script,
-                                       const std::string& functionName,
-                                       const std::string& path)
+PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string& script,
+                                       const std::string& functionName, const std::string& path)
 {
     PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
 
@@ -21,7 +19,8 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.get(), functionName.c_str());
     if (!pAddFn) {
         std::string msg = "PythonInterpreter::BornAgain: "
-            "Cannot locate the compiled function '" + functionName + "'";
+                          "Cannot locate the compiled function '"
+                          + functionName + "'";
         return {Status(Status::Type::Error, msg)};
     }
 
@@ -33,7 +32,8 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
 
     if (!instance) {
         std::string msg = "PythonInterpreter::BornAgain: "
-            "Cannot call the function '" + functionName + "'";
+                          "Cannot call the function '"
+                          + functionName + "'";
         return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
     }
 
@@ -49,7 +49,8 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
     if (!SWIG_IsOK(res)) {
         Py_DecRef(instance);
         std::string msg = "PythonInterpreter::BornAgain: SWIG failed to extract a "
-            "'MultiLayer' instance via calling the function '" + functionName + "'";
+                          "'MultiLayer' instance via calling the function '"
+                          + functionName + "'";
         return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
     }
 
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 998e952cad3..97aa9127fd7 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -33,8 +33,7 @@ namespace PythonInterpreter {
 // `MultiLayer` instance in C++ (memory should be managed by the caller).
 //! The returned value is the PyObject corresponding the intermediate _Python_ instance of
 //! of the `MultiLayer` class.
-PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr,
-                                       const std::string& script,
+PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string& script,
                                        const std::string& functionName,
                                        const std::string& path = "");
 
-- 
GitLab


From 6bea4228514db4e3a4d95bba36e6afa12e4703dd Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Wed, 10 May 2023 11:34:27 +0200
Subject: [PATCH 53/85] PyCore: rm Status class

---
 Device/Data/Datafield.cpp              |   4 +-
 GUI/View/Project/PyImportAssistant.cpp |   3 +-
 PyCore/Embed/PyObjectPtr.cpp           |  75 +---------
 PyCore/Embed/PyObjectPtr.h             |  39 +----
 PyCore/Embed/PythonInterpreter.cpp     | 198 ++++++++++++-------------
 PyCore/Embed/PythonInterpreter.h       |  12 +-
 PyCore/Sample/ImportMultiLayer.cpp     |  25 ++--
 7 files changed, 119 insertions(+), 237 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index e9e6d96fed1..d79a326dbf1 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -211,9 +211,7 @@ PyObject* Datafield::npArray() const
 {
     // TODO: Thoroughly check this function regarding index manipulations
 
-    Status init{PythonInterpreter::Numpy::initialize()};
-    if (init.isError())
-        return nullptr;
+    PythonInterpreter::Numpy::initialize();
 
     ASSERT(rank() <= 2);
 
diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index 2ff81c4b76a..169d4cdde7f 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -112,8 +112,7 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
     if (!result.valid()) {
         QApplication::restoreOverrideCursor();
         QString message("Exception thrown while executing Python code to create sample.\n\n");
-        QString details = QString::fromStdString(result.status.message);
-        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, details).exec();
+        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, "").exec();
     }
 
     QApplication::restoreOverrideCursor();
diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index d4ff222b511..6f91d8b395d 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -2,78 +2,19 @@
 #include "PythonInterpreter.h"
 
 #include <stdexcept> // runtime_error
-#include <string>
-
-
-Status::Status(const Status::Type type, const std::string& message)
-    : type{type}
-    , message{message}
-{
-}
-
-bool Status::isSet() const
-{
-    return (type != Status::Type::None);
-}
-
-
-bool Status::isOK() const
-{
-    return (type == Status::Type::Info);
-}
-
-
-bool Status::isWarning() const
-{
-    return (type == Status::Type::Warning);
-}
-
-
-bool Status::isError() const
-{
-    return (type == Status::Type::Error);
-}
-
-
-Status::operator bool() const
-{
-    return isOK() || isWarning();
-}
-
-//----------------------------------------
-
-PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr, const Status& status)
-    : status{status}
-    , m_ptr{pyobject_ptr}
-{
-    // if PyObject pointer is null but result type is not `None` or `Error`,
-    // then that will be an `Error` case.
-    if (!m_ptr && status.type != Status::Type::None && status.type != Status::Type::Error) {
-        this->status.type = Status::Type::Error;
-    }
-}
-
 
 PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr)
-    : PyObjectPtr(pyobject_ptr, Status(Status::Type::Info))
+    : m_ptr{pyobject_ptr}
 {
 }
 
-
-PyObjectPtr::PyObjectPtr(const Status& status)
-    : PyObjectPtr(nullptr, status)
-{
-}
-
-
 PyObjectPtr::~PyObjectPtr()
 {
     discard();
 }
 
-
 PyObjectPtr::PyObjectPtr(PyObjectPtr&& other)
-    : PyObjectPtr(other.get(), other.status)
+    : PyObjectPtr(other.get())
 {
     // reset the moved object
     other.reset();
@@ -91,14 +32,11 @@ PyObject* PyObjectPtr::release()
     return pyobject_ptr;
 }
 
-
 void PyObjectPtr::reset()
 {
     m_ptr = nullptr;
-    status = Status();
 }
 
-
 void PyObjectPtr::discard()
 {
     if (!PythonInterpreter::isInitialized()) {
@@ -111,14 +49,7 @@ void PyObjectPtr::discard()
     reset();
 }
 
-
 bool PyObjectPtr::valid() const
 {
-    return (m_ptr && (status.isOK() || status.isWarning()));
-}
-
-
-bool PyObjectPtr::isSet() const
-{
-    return (m_ptr && status.isSet());
+    return static_cast<bool>(m_ptr);
 }
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index dca6fb3cf49..b397a8f8066 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -16,47 +16,16 @@
 #define BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 
 #include "PyCore/Embed/PyObjectDecl.h"
-#include <string>
 #include <vector>
 
-//! Indicator for the status of a result
-class Status {
-public:
-    enum class Type { None = -1, Info, Warning, Error };
-    Type type = Type::None;
-
-    //! Status message
-    std::string message;
-
-    Status(const Status::Type type = Type::None, const std::string& message = "");
-
-    bool isSet() const;
-
-    bool isOK() const;
-
-    bool isWarning() const;
-
-    bool isError() const;
-
-    //! Cast the validity status to boolean (true if result is valid)
-    operator bool() const;
-};
-
-
-//! Safe container for PyObjects
-//! The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and
-//! a `Status` which denotes the status of the value (OK/Warning/Error).
+//! Safe container for PyObjects:
+//! The class `PyObjectPtr` contains a `PyObject*` (or `NULL`).
 //! Decrementing Python reference-count is performed automatically when
-//! a `PyObjectPtr` expires.
+//! the life-time of a `PyObjectPtr` expires.
 class PyObjectPtr {
 
 public:
-    //! Status of the PyObject
-    Status status;
-
-    PyObjectPtr(PyObject* pyobject_ptr, const Status& status);
     PyObjectPtr(PyObject* pyobject_ptr);
-    PyObjectPtr(const Status& status);
     ~PyObjectPtr();
     PyObjectPtr(const PyObjectPtr&) = delete;
     PyObjectPtr(PyObjectPtr&& other);
@@ -73,8 +42,6 @@ public:
     void discard();
     //! Check the validity of the PyObjectPtr
     bool valid() const;
-    //! Checks whether the PyObjectPtr is set
-    bool isSet() const;
 
 private:
     //! Raw pointer to the PyObject
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index f03d99cf2bd..95d1c0a6259 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -27,6 +27,7 @@ References:
 #include <string>
 #include <vector>
 
+
 namespace {
 
 // thin wrapper to initialize Numpy
@@ -42,9 +43,9 @@ std::nullptr_t _init_numpy(int& status)
         */
         // NOTE: Numpy's `import_array` returns `NULL` on failure;
         // hence return type must be `std::nullptr_t`.
-        status = 0;
+        status = 1;
         import_array();
-        status = 1; // Numpy Array API loaded successfully
+        status = 0; // Numpy Array API loaded successfully
         return NULL;
     }
 
@@ -83,9 +84,9 @@ std::vector<std::string> toVectorString(PyObject* py_object)
             result.push_back(PythonInterpreter::pyStrtoString(value));
         }
     } else {
-        throw(std::runtime_error(PythonInterpreter::errorDescription(
+        throw std::runtime_error(PythonInterpreter::errorDescription(
             "PythonInterpreter: Cannnot convert the given Python object "
-            "to vector<string>.")));
+            "to vector<string>."));
     }
 
     return result;
@@ -116,19 +117,13 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
 // see <https://docs.python.org/3/c-api/stable.html>
 
 // Python stable ABI
-Status PythonInterpreter::initialize()
+void PythonInterpreter::initialize()
 {
-    std::string msg;
-
     if (!Py_IsInitialized()) {
         Py_Initialize();
-        return {Status::Type::Info,
-                "PythonInterpreter: Initialized Python - version: " + toString(Py_GetVersion())};
     }
 
-    // if Python already initialized
-    return {Status::Type::Warning, "PythonInterpreter: Python already initialized - version: "
-                                       + toString(Py_GetVersion())};
+    // if Python interpreter is already initialized, do nothing
 }
 
 // Python stable ABI
@@ -138,29 +133,26 @@ bool PythonInterpreter::isInitialized()
 }
 
 // Python stable ABI
-Status PythonInterpreter::finalize()
+void PythonInterpreter::finalize()
 {
     // undo all initializations made by Py_Initialize() and subsequent use
     // of Python/C API functions, and destroy all sub-interpreters.
     // This is a no-op when called for a second time.
     Py_Finalize();
-
-    return {Status::Type::Info,
-            "PythonInterpreter: Finalized Python - version: " + toString(Py_GetVersion())};
 }
 
 // Python stable ABI
-Status PythonInterpreter::checkError()
+bool PythonInterpreter::checkError()
 {
     if (PyErr_Occurred()) {
         // print a standard traceback to sys.stderr and clear the error indicator
         std::cerr << "---PythonInterpreter: Error in Python interpreter:\n";
         PyErr_Print();
         std::cerr << "\n---\n";
-        return {Status::Type::Error};
+        return true;
     }
 
-    return {Status::Type::Info, "No Python errors occured"};
+    return false;
 }
 
 // Python stable ABI
@@ -171,14 +163,20 @@ void PythonInterpreter::addPythonPath(const std::string& path)
         PyObject* sysPath = PySys_GetObject((char*)"path");
         PyList_Append(sysPath, PyUnicode_FromString(path.c_str()));
     }
+
+    throw std::runtime_error("PythonInterpreter.addPythonPath: Cannot add to an empty Python path.");
 }
 
 // Python stable ABI
-int PythonInterpreter::setPythonPath(const std::string& path)
+void PythonInterpreter::setPythonPath(const std::string& path)
 {
     // returns 0 on success, -1 on error
     const int result = PySys_SetObject((char*)"path", PyUnicode_FromString(path.c_str()));
-    return result;
+    if (result != 0) {
+        PythonInterpreter::checkError();
+        throw std::runtime_error("PythonInterpreter.setPythonPath: Cannot set the Python path.");
+    }
+
 }
 
 // Python stable ABI
@@ -190,9 +188,9 @@ PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const st
     PyObject* pymodule = PyImport_ImportModule(pymodule_name.c_str());
     if (!pymodule || !PyModule_Check(pymodule)) {
         checkError();
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter: Cannot load Python module '"
-                                        + pymodule_name + "' (given path = '" + path + "')"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter: Cannot load Python module '"
+                             + pymodule_name + "' (given path = '" + path + "')"));
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
@@ -296,25 +294,25 @@ std::string PythonInterpreter::errorDescription(const std::string& title)
 
 //----------------------------------------
 
-Status PythonInterpreter::Numpy::initialize()
+int PythonInterpreter::Numpy::initialize()
 {
 
     // initialize Python C API, if needed
-    Status py_status{PythonInterpreter::initialize()};
-    if (py_status.isError())
-        return py_status;
+    PythonInterpreter::initialize();
 
     int res;
     _init_numpy(res);
 
     switch (res) {
+    case 0:
+        return res;
     case 1:
-        return {Status::Type::Info, "PythonInterpreter: Initialized Numpy"};
+        throw std::runtime_error(errorDescription("PythonInterpreter: Cannot initialize Numpy"));
     case 2:
-        return {Status::Type::Warning, "PythonInterpreter: Numpy already initialized"};
-    default:
-        return {Status::Type::Error, "PythonInterpreter: Cannot initialize Numpy"};
+        return res;
     }
+
+    return res;
 }
 
 bool PythonInterpreter::Numpy::isInitialized()
@@ -389,10 +387,10 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
         std::string msg = "PythonInterpreter::Numpy: Expected a Numpy 2d-array "
                           "(given number of dimensions is "
                           + std::to_string(npArray_ndim) + ")";
-        throw(std::runtime_error(
+        throw std::runtime_error(
             errorDescription("PythonInterpreter::Numpy: Expected a Numpy 2d-array "
                              "(given number of dimensions is "
-                             + std::to_string(npArray_ndim) + ")")));
+                             + std::to_string(npArray_ndim) + ")"));
     }
 
     // Numpy array type must be numeric and real (eligible for casting to double)
@@ -401,7 +399,7 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
                           "Expected a Numpy array of numeric type and real "
                           "(given type '"
                           + std::to_string(npArray_dtype) + "')";
-        throw(std::runtime_error(errorDescription(msg)));
+        throw std::runtime_error(errorDescription(msg));
     }
 
     std::vector<double> data(npArray_size);
@@ -457,11 +455,11 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
 #endif // _WIN32
 
     default:
-        throw(std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+        throw std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
                                                   "Conversion of Numpy array of dtype '"
                                                   + std::to_string(npArray_dtype)
                                                   + "' "
-                                                    "to vector<double> is not implemented")));
+                                                  "to vector<double> is not implemented"));
     }
 
     return data;
@@ -471,16 +469,14 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
                                                          const np_size_t dims[2])
 {
     if (!c_array) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot create a Numpy 1D-array from a null data pointer")};
+        throw std::runtime_error("PythonInterpreter::Numpy: "
+                                 "Cannot create a Numpy 1D-array from a null data pointer");
     }
 
     const np_size_t size = dims[0] * dims[1];
     if (size < 1) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot create a Numpy 1D-array from a data with size = 0")};
+        throw std::runtime_error("PythonInterpreter::Numpy: "
+                                 "Cannot create a Numpy 1D-array from a data with size = 0");
     }
 
     npy_intp npDims[2] = {dims[0], dims[1]};
@@ -489,11 +485,10 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
     PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 2, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
-        std::string msg = "PythonInterpreter::Numpy: "
-                          "Cannot create a Numpy 1D-array from the "
-                          "given data (size = "
-                          + std::to_string(size) + ")";
-        return {Status(Status::Type::Error, errorDescription(msg))};
+        throw std::runtime_error("PythonInterpreter::Numpy: "
+                                 "Cannot create a Numpy 1D-array from the "
+                                 "given data (size = "
+                                 + std::to_string(size) + ")");
     }
 
     // obtain pointer to data buffer of Numpy array
@@ -512,16 +507,14 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
                                                          const np_size_t size)
 {
     if (!c_array) {
-        return {Status(Status::Type::Error,
-                       "PythonInterpreter::Numpy: "
-                       "Cannot create a Numpy 1D-array from a null data pointer")};
+        throw std::runtime_error("PythonInterpreter::Numpy: "
+                                 "Cannot create a Numpy 1D-array from a null data pointer");
     }
 
     if (size < 1) {
-        return {
-            Status(Status::Type::Error,
-                   errorDescription("PythonInterpreter::Numpy: "
-                                    "Cannot create a Numpy 1D-array from a data with size = 0"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::Numpy: "
+                             "Cannot create a Numpy 1D-array from a data with size = 0"));
     }
 
     npy_intp npDims[1] = {size};
@@ -534,7 +527,10 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
                           "Cannot create a Numpy 1D-array from the "
                           "given data (size = "
                           + std::to_string(size) + ")";
-        return {Status(Status::Type::Error, errorDescription(msg))};
+        throw std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+                                                  "Cannot create a Numpy 1D-array from the "
+                                                  "given data (size = "
+                                                  + std::to_string(size) + ")"));
     }
 
     // obtain pointer to data buffer of Numpy array
@@ -553,18 +549,16 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
 PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const np_size_t dims[2])
 {
     if (!c_array) {
-        return {
-            Status(Status::Type::Error,
-                   errorDescription("PythonInterpreter::Numpy: "
-                                    "Cannot create a Numpy 2D-array from a null data pointer"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::Numpy: "
+                             "Cannot create a Numpy 2D-array from a null data pointer"));
     }
 
     const np_size_t size = dims[0] * dims[1];
     if (size < 1) {
-        return {
-            Status(Status::Type::Error,
-                   errorDescription("PythonInterpreter::Numpy: "
-                                    "Cannot create a Numpy 2D-array from a data with size = 0"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::Numpy: "
+                             "Cannot create a Numpy 2D-array from a data with size = 0"));
     }
 
     npy_intp npDims[2] = {dims[0], dims[1]};
@@ -575,9 +569,9 @@ PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const
 
     if (!npArray_ptr || !PyArray_Check(npArray_ptr)) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter::Numpy: Cannot convert the given "
-                                        "C-Array to a Numpy 2D-array"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::Numpy: Cannot convert the given "
+                             "C-Array to a Numpy 2D-array"));
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
@@ -589,16 +583,16 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
     // descriptors for the array dimensions
     const std::size_t n_dims = dimensions.size();
     if (n_dims < 1) {
-        return {Status(Status::Type::Error,
-                       errorDescription("Cannot make a Numpy with the given number "
-                                        "of dimensions; number of dimensions must be >= 1"))};
+        throw std::runtime_error(
+            errorDescription("Cannot make a Numpy with the given number "
+                             "of dimensions; number of dimensions must be >= 1"));
     }
 
     for (std::size_t d = 0; d < n_dims; ++d) {
         if (dimensions[d] < 2) {
-            return {Status(Status::Type::Error,
-                           errorDescription("Cannot make a Numpy with the given dimensions; "
-                                            "dimensions must be >= 2"))};
+            throw std::runtime_error(
+                errorDescription("Cannot make a Numpy with the given dimensions; "
+                                 "dimensions must be >= 2"));
         }
     }
 
@@ -613,9 +607,9 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
 
     if (!npyArray_ptr) {
         checkError();
-        std::string msg = "PythonInterpreter::Numpy: Cannot create a Numpy "
-                          + std::to_string(n_dims) + "D-array from the given data";
-        return {Status(Status::Type::Error, errorDescription(msg))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::Numpy: Cannot create a Numpy "
+                             + std::to_string(n_dims) + "D-array from the given data"));
     }
 
     return {npyArray_ptr};
@@ -655,10 +649,9 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
 
     if (!ba_pymodule || !PyModule_Check(ba_pymodule)) {
         checkError();
-        std::string msg = "PythonInterpreter: Cannot load 'bornagain' Python module "
-                          "(given path = '"
-                          + path + "')";
-        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter: Cannot load 'bornagain' Python module "
+                             "(given path = '" + path + "')"));
     }
 
     // restores single handler to make ctr-c alive.
@@ -678,8 +671,7 @@ PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script
     PyObject* pCompiledFn = Py_CompileString(script.c_str(), "", Py_file_input);
     if (!pCompiledFn) {
         ba_pymodule.discard();
-        return {Status(Status::Type::Error,
-                       PythonInterpreter::errorDescription("Cannot compile Python snippet"))};
+        throw std::runtime_error(errorDescription("Cannot compile Python snippet"));
     }
 
     // create a module
@@ -687,8 +679,7 @@ PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script
     if (!tmpModule) {
         Py_DecRef(pCompiledFn);
         ba_pymodule.discard();
-        return {Status(Status::Type::Error,
-                       PythonInterpreter::errorDescription("Cannot execute Python snippet"))};
+        throw std::runtime_error(errorDescription("Cannot execute Python snippet"));
     }
 
     return {tmpModule};
@@ -703,8 +694,8 @@ std::vector<std::string> PythonInterpreter::BornAgain::listOfFunctions(const std
     PyObject* pDict = PyModule_GetDict(tmpModule.get());
     if (!pDict) {
         checkError();
-        throw(std::runtime_error("PythonInterpreter::BornAgain: "
-                                 "Cannot obtain the dictionary from the script module"));
+        throw std::runtime_error("PythonInterpreter::BornAgain: "
+                                 "Cannot obtain the dictionary from the script module");
     }
 
     PyObject *key, *value;
@@ -736,27 +727,25 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     // filename (Python string) and returns a Numpy array.
 
     if (!fabio_module.valid() || !PyModule_Check(fabio_module.get())) {
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter.fabio: Invalid Python module "
-                                        "(expected 'fabio' module)"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter.fabio: Invalid Python module "
+                             "(expected 'fabio' module)"));
     }
 
     PyObject* pFunc = PyObject_GetAttrString(fabio_module.get(), (char*)"open");
     if (!pFunc || !PyCallable_Check(pFunc)) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter.fabio: "
-                                        "The function 'fabio.open' is not callable"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter.fabio: The function 'fabio.open' is not callable"));
     }
 
     // convert the filename to a Python unicode string
     PyObject* pFilename = PyUnicode_FromString(filename.c_str());
     if (!pFilename) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter.fabio: Filename '" + filename
-                                        + "' "
-                                          "cannot be converted to Python unicode string"))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter.fabio: Filename '" + filename
+                             + "' cannot be converted to Python unicode string"));
     }
 
     // get the result of the call `fabio.open(<filename>)`
@@ -764,11 +753,10 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     Py_DecRef(pFunc);
     if (!pResult_open) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter.fabio: "
-                                        "Invalid return value from calling the function "
-                                        "'fabio.open(\""
-                                        + filename + "\")'"))};
+        std::runtime_error(
+            errorDescription("PythonInterpreter.fabio: "
+                             "Invalid return value from calling the function "
+                             "'fabio.open(\"" + filename + "\")'"));
     }
 
     // get `result.data` (must be a Numpy array)
@@ -776,10 +764,10 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     Py_DecRef(pResult_open);
     if (!npyArray_ptr || !PyArray_Check(npyArray_ptr)) {
         PythonInterpreter::checkError();
-        return {Status(Status::Type::Error,
-                       errorDescription("PythonInterpreter.fabio: Invalid return value from "
-                                        "calling the function 'fabio.open(\""
-                                        + filename + "\")' (expected a Numpy array)"))};
+        std::runtime_error(
+            errorDescription("PythonInterpreter.fabio: Invalid return value from "
+                             "calling the function 'fabio.open(\""
+                             + filename + "\")' (expected a Numpy array)"));
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 2702649333a..38df1344f06 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -20,27 +20,25 @@
 #include <string>
 #include <vector>
 
-class MultiLayer;
-
 namespace PythonInterpreter {
 
 // Python stable ABI
-Status initialize();
+void initialize();
 
 // Python stable ABI
 bool isInitialized();
 
 // Python stable ABI
-Status finalize();
+void finalize();
 
 // Python stable ABI
-Status checkError();
+bool checkError();
 
 // Python stable ABI
 void addPythonPath(const std::string& path);
 
 // Python stable ABI
-int setPythonPath(const std::string& path);
+void setPythonPath(const std::string& path);
 
 // Python stable ABI
 PyObjectPtr import(const std::string& pymodule_name, const std::string& path = "");
@@ -63,7 +61,7 @@ std::string errorDescription(const std::string& title = "");
 namespace Numpy {
 
 //! Initializes Numpy
-Status initialize();
+int initialize();
 
 //! Checks if Numpy is initialized
 bool isInitialized();
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 45550aa80ef..7e015ff08af 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -7,6 +7,8 @@
 // SWIG runtime access for creating a MultiLayer instance from Python
 #include "auto/Wrap/swig_runtime.h"
 
+#include <stdexcept> // runtime_error
+
 
 namespace PythonInterpreter {
 
@@ -18,10 +20,10 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     // locate the `get_simulation` function (it is an attribute of the module)
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.get(), functionName.c_str());
     if (!pAddFn) {
-        std::string msg = "PythonInterpreter::BornAgain: "
-                          "Cannot locate the compiled function '"
-                          + functionName + "'";
-        return {Status(Status::Type::Error, msg)};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::BornAgain: "
+                             "Cannot locate the compiled function '"
+                             + functionName + "'"));
     }
 
     // create a `MultiLayer` Python object via calling the function
@@ -31,10 +33,9 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     Py_DecRef(pAddFn);
 
     if (!instance) {
-        std::string msg = "PythonInterpreter::BornAgain: "
-                          "Cannot call the function '"
-                          + functionName + "'";
-        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::BornAgain: Cannot call the function '"
+                             + functionName + "'"));
     }
 
     // Construct a C++ object from the Python object
@@ -48,10 +49,10 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     const int res = SWIG_ConvertPtr(instance, &argp1, pTypeInfo, 0);
     if (!SWIG_IsOK(res)) {
         Py_DecRef(instance);
-        std::string msg = "PythonInterpreter::BornAgain: SWIG failed to extract a "
-                          "'MultiLayer' instance via calling the function '"
-                          + functionName + "'";
-        return {Status(Status::Type::Error, PythonInterpreter::errorDescription(msg))};
+        throw std::runtime_error(
+            errorDescription("PythonInterpreter::BornAgain: SWIG failed to extract a "
+                             "'MultiLayer' instance via calling the function '"
+                             + functionName + "'"));
     }
 
     multilayer_ptr = reinterpret_cast<void*>(argp1);
-- 
GitLab


From 4f463aab8aab6366694b7ad9a019e2cc7b2182e9 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Wed, 10 May 2023 16:04:22 +0200
Subject: [PATCH 54/85] PythonInterpreter: rm unneeded auxiliary function

---
 PyCore/Embed/PythonInterpreter.cpp | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 95d1c0a6259..85458f7aee2 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -61,13 +61,6 @@ std::string toString(const wchar_t* const c)
     return std::string(wstr.begin(), wstr.end());
 }
 
-std::string toString(const char* const c)
-{
-    if (!c)
-        return "";
-    return std::string(c);
-}
-
 //! Converts PyObject into vector of strings, if possible, or throws exception
 std::vector<std::string> toVectorString(PyObject* py_object)
 {
-- 
GitLab


From 1c44f1d34082c6ad0a7ce30b89e29664aa43ed28 Mon Sep 17 00:00:00 2001
From: AlQuemist <alquemist@Lyriks>
Date: Thu, 3 Feb 2022 12:06:57 +0100
Subject: [PATCH 55/85] Wrap/Swig/commons.i: Minor improvement to Numpy usage

---
 Wrap/Swig/commons.i | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Wrap/Swig/commons.i b/Wrap/Swig/commons.i
index a9a170d2d41..2863fd9634e 100644
--- a/Wrap/Swig/commons.i
+++ b/Wrap/Swig/commons.i
@@ -14,12 +14,17 @@
 
 %{
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 %}
 
 %include "numpy.i"
 %init %{
-    import_array();
+    if(!PyArray_API) {
+        import_array();
+    }
 %}
 
 #define GCC_DIAG_OFF(x)
-- 
GitLab


From 9e97b46b51dd85c54c17459a6ee6ac1a5d73f060 Mon Sep 17 00:00:00 2001
From: "a.nejati.debug@debian-ci" <a.nejati.debug@debian-ci>
Date: Tue, 9 May 2023 15:14:07 +0200
Subject: [PATCH 56/85] Update SWIG-produced wrappers for Python API

---
 auto/Wrap/libBornAgainBase_wrap.cpp     | 7 ++++++-
 auto/Wrap/libBornAgainDevice_wrap.cpp   | 7 ++++++-
 auto/Wrap/libBornAgainFit_wrap.cpp      | 7 ++++++-
 auto/Wrap/libBornAgainParam_wrap.cpp    | 7 ++++++-
 auto/Wrap/libBornAgainResample_wrap.cpp | 7 ++++++-
 auto/Wrap/libBornAgainSample_wrap.cpp   | 7 ++++++-
 auto/Wrap/libBornAgainSim_wrap.cpp      | 7 ++++++-
 7 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/auto/Wrap/libBornAgainBase_wrap.cpp b/auto/Wrap/libBornAgainBase_wrap.cpp
index 272622c7c94..e9cdd8146ce 100644
--- a/auto/Wrap/libBornAgainBase_wrap.cpp
+++ b/auto/Wrap/libBornAgainBase_wrap.cpp
@@ -4121,6 +4121,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -29293,7 +29296,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
diff --git a/auto/Wrap/libBornAgainDevice_wrap.cpp b/auto/Wrap/libBornAgainDevice_wrap.cpp
index 7f79e992872..735efdd2fba 100644
--- a/auto/Wrap/libBornAgainDevice_wrap.cpp
+++ b/auto/Wrap/libBornAgainDevice_wrap.cpp
@@ -4156,6 +4156,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -39245,7 +39248,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
diff --git a/auto/Wrap/libBornAgainFit_wrap.cpp b/auto/Wrap/libBornAgainFit_wrap.cpp
index 910c7a0f390..564bd7a6a68 100644
--- a/auto/Wrap/libBornAgainFit_wrap.cpp
+++ b/auto/Wrap/libBornAgainFit_wrap.cpp
@@ -4125,6 +4125,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -29489,7 +29492,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
diff --git a/auto/Wrap/libBornAgainParam_wrap.cpp b/auto/Wrap/libBornAgainParam_wrap.cpp
index a092f8b9d0b..e0553d7ef58 100644
--- a/auto/Wrap/libBornAgainParam_wrap.cpp
+++ b/auto/Wrap/libBornAgainParam_wrap.cpp
@@ -4124,6 +4124,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -32480,7 +32483,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
diff --git a/auto/Wrap/libBornAgainResample_wrap.cpp b/auto/Wrap/libBornAgainResample_wrap.cpp
index 1e369af586b..e9b9628aee8 100644
--- a/auto/Wrap/libBornAgainResample_wrap.cpp
+++ b/auto/Wrap/libBornAgainResample_wrap.cpp
@@ -4115,6 +4115,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -29100,7 +29103,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
diff --git a/auto/Wrap/libBornAgainSample_wrap.cpp b/auto/Wrap/libBornAgainSample_wrap.cpp
index b4e396d3f54..ad3af34a6ed 100644
--- a/auto/Wrap/libBornAgainSample_wrap.cpp
+++ b/auto/Wrap/libBornAgainSample_wrap.cpp
@@ -4224,6 +4224,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -61197,7 +61200,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
diff --git a/auto/Wrap/libBornAgainSim_wrap.cpp b/auto/Wrap/libBornAgainSim_wrap.cpp
index d9924e7b0be..08c00bb2fc4 100644
--- a/auto/Wrap/libBornAgainSim_wrap.cpp
+++ b/auto/Wrap/libBornAgainSim_wrap.cpp
@@ -4154,6 +4154,9 @@ SWIGINTERNINLINE PyObject*
 
 
 #define SWIG_FILE_WITH_INIT
+/* NOTE: Using `PY_ARRAY_UNIQUE_SYMBOL` one can use
+   the Numpy Array API in multiple files for a single extension module.
+*/
 #define PY_ARRAY_UNIQUE_SYMBOL BORNAGAIN_PYTHONAPI_ARRAY
 
 
@@ -37192,7 +37195,9 @@ SWIG_init(void) {
   
   SWIG_Python_SetConstant(d, "SHARED_PTR_DISOWN",SWIG_From_int(static_cast< int >(0)));
   
-  import_array();
+  if(!PyArray_API) {
+    import_array();
+  }
   
   
   // thread safe initialization
-- 
GitLab


From 73e7c501c8278d84419ad8235f1626eea7bb2569 Mon Sep 17 00:00:00 2001
From: "a.nejati.debug@debian-ci" <a.nejati.debug@debian-ci>
Date: Wed, 10 May 2023 16:25:59 +0200
Subject: [PATCH 57/85] Apply clang-format

---
 PyCore/Embed/PythonInterpreter.cpp | 41 +++++++++++++++---------------
 PyCore/Sample/ImportMultiLayer.cpp | 12 ++++-----
 2 files changed, 25 insertions(+), 28 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index 85458f7aee2..fcaa4b2b7d9 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -157,7 +157,8 @@ void PythonInterpreter::addPythonPath(const std::string& path)
         PyList_Append(sysPath, PyUnicode_FromString(path.c_str()));
     }
 
-    throw std::runtime_error("PythonInterpreter.addPythonPath: Cannot add to an empty Python path.");
+    throw std::runtime_error(
+        "PythonInterpreter.addPythonPath: Cannot add to an empty Python path.");
 }
 
 // Python stable ABI
@@ -169,7 +170,6 @@ void PythonInterpreter::setPythonPath(const std::string& path)
         PythonInterpreter::checkError();
         throw std::runtime_error("PythonInterpreter.setPythonPath: Cannot set the Python path.");
     }
-
 }
 
 // Python stable ABI
@@ -181,9 +181,9 @@ PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const st
     PyObject* pymodule = PyImport_ImportModule(pymodule_name.c_str());
     if (!pymodule || !PyModule_Check(pymodule)) {
         checkError();
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter: Cannot load Python module '"
-                             + pymodule_name + "' (given path = '" + path + "')"));
+        throw std::runtime_error(errorDescription("PythonInterpreter: Cannot load Python module '"
+                                                  + pymodule_name + "' (given path = '" + path
+                                                  + "')"));
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
@@ -452,7 +452,7 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
                                                   "Conversion of Numpy array of dtype '"
                                                   + std::to_string(npArray_dtype)
                                                   + "' "
-                                                  "to vector<double> is not implemented"));
+                                                    "to vector<double> is not implemented"));
     }
 
     return data;
@@ -600,9 +600,9 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
 
     if (!npyArray_ptr) {
         checkError();
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: Cannot create a Numpy "
-                             + std::to_string(n_dims) + "D-array from the given data"));
+        throw std::runtime_error(errorDescription("PythonInterpreter::Numpy: Cannot create a Numpy "
+                                                  + std::to_string(n_dims)
+                                                  + "D-array from the given data"));
     }
 
     return {npyArray_ptr};
@@ -644,7 +644,8 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
         checkError();
         throw std::runtime_error(
             errorDescription("PythonInterpreter: Cannot load 'bornagain' Python module "
-                             "(given path = '" + path + "')"));
+                             "(given path = '"
+                             + path + "')"));
     }
 
     // restores single handler to make ctr-c alive.
@@ -720,9 +721,8 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     // filename (Python string) and returns a Numpy array.
 
     if (!fabio_module.valid() || !PyModule_Check(fabio_module.get())) {
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter.fabio: Invalid Python module "
-                             "(expected 'fabio' module)"));
+        throw std::runtime_error(errorDescription("PythonInterpreter.fabio: Invalid Python module "
+                                                  "(expected 'fabio' module)"));
     }
 
     PyObject* pFunc = PyObject_GetAttrString(fabio_module.get(), (char*)"open");
@@ -746,10 +746,10 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     Py_DecRef(pFunc);
     if (!pResult_open) {
         PythonInterpreter::checkError();
-        std::runtime_error(
-            errorDescription("PythonInterpreter.fabio: "
-                             "Invalid return value from calling the function "
-                             "'fabio.open(\"" + filename + "\")'"));
+        std::runtime_error(errorDescription("PythonInterpreter.fabio: "
+                                            "Invalid return value from calling the function "
+                                            "'fabio.open(\""
+                                            + filename + "\")'"));
     }
 
     // get `result.data` (must be a Numpy array)
@@ -757,10 +757,9 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     Py_DecRef(pResult_open);
     if (!npyArray_ptr || !PyArray_Check(npyArray_ptr)) {
         PythonInterpreter::checkError();
-        std::runtime_error(
-            errorDescription("PythonInterpreter.fabio: Invalid return value from "
-                             "calling the function 'fabio.open(\""
-                             + filename + "\")' (expected a Numpy array)"));
+        std::runtime_error(errorDescription("PythonInterpreter.fabio: Invalid return value from "
+                                            "calling the function 'fabio.open(\""
+                                            + filename + "\")' (expected a Numpy array)"));
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 7e015ff08af..57e15a633bd 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -20,10 +20,9 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     // locate the `get_simulation` function (it is an attribute of the module)
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.get(), functionName.c_str());
     if (!pAddFn) {
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter::BornAgain: "
-                             "Cannot locate the compiled function '"
-                             + functionName + "'"));
+        throw std::runtime_error(errorDescription("PythonInterpreter::BornAgain: "
+                                                  "Cannot locate the compiled function '"
+                                                  + functionName + "'"));
     }
 
     // create a `MultiLayer` Python object via calling the function
@@ -33,9 +32,8 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     Py_DecRef(pAddFn);
 
     if (!instance) {
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter::BornAgain: Cannot call the function '"
-                             + functionName + "'"));
+        throw std::runtime_error(errorDescription(
+            "PythonInterpreter::BornAgain: Cannot call the function '" + functionName + "'"));
     }
 
     // Construct a C++ object from the Python object
-- 
GitLab


From 3b958a26e986e601bd303583626aaf51ed2d3f30 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:10:13 +0200
Subject: [PATCH 58/85] Doxy + PyCore

---
 Doc/Doxygen/core/Doxyfile.in | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Doc/Doxygen/core/Doxyfile.in b/Doc/Doxygen/core/Doxyfile.in
index 070fb3f9209..5bb7ae11cdf 100644
--- a/Doc/Doxygen/core/Doxyfile.in
+++ b/Doc/Doxygen/core/Doxyfile.in
@@ -899,6 +899,7 @@ WARN_LOGFILE           =
 
 INPUT                  = @CMAKE_SOURCE_DIR@/Doc/Doxygen/core \
                          @CMAKE_SOURCE_DIR@/Doc/Doxygen/common \
+                         @CMAKE_SOURCE_DIR@/PyCore \
                          @CMAKE_SOURCE_DIR@/Base \
                          @CMAKE_SOURCE_DIR@/Fit \
                          @CMAKE_SOURCE_DIR@/Param \
-- 
GitLab


From 17169e4713d43d1fa5bad51ac267d1d6dafc9c21 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:32:04 +0200
Subject: [PATCH 59/85] rm cpp conditional that depends on USER_API which is
 defined nowhere

---
 PyCore/Embed/PyCore.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/PyCore/Embed/PyCore.h b/PyCore/Embed/PyCore.h
index 211857da2bf..a936b50e17c 100644
--- a/PyCore/Embed/PyCore.h
+++ b/PyCore/Embed/PyCore.h
@@ -16,7 +16,6 @@
 #error no need to expose this header to Swig
 #endif
 
-#ifndef USER_API
 #ifndef BORNAGAIN_PYTOOLS_PYCORE_H
 #define BORNAGAIN_PYTOOLS_PYCORE_H
 
@@ -55,4 +54,3 @@
 
 #endif // BORNAGAIN_PYTHON
 #endif // BORNAGAIN_PYTOOLS_PYCORE_H
-#endif // USER_API
-- 
GitLab


From 4e43ec0aa7ae5a906b8c7df9087a91e6ef809f8b Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:33:56 +0200
Subject: [PATCH 60/85] PyObjectPtr: add/correct file header comment

---
 PyCore/Embed/PyObjectPtr.cpp | 14 ++++++++++++++
 PyCore/Embed/PyObjectPtr.h   |  2 +-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index 6f91d8b395d..0999b33bcd6 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -1,3 +1,17 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Embed/PyObjectPtr.h
+//! @brief     Implements class PyObjectPtr
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
 #include "PyObjectPtr.h"
 #include "PythonInterpreter.h"
 
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index b397a8f8066..2d9a446fc6b 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -3,7 +3,7 @@
 //  BornAgain: simulate and fit reflection and scattering
 //
 //! @file      PyCore/Embed/PyObjectPtr.h
-//! @brief     Defines classes PyObjectPtr and Status.
+//! @brief     Defines class PyObjectPtr and struct NumpyArrayDescriptor
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
-- 
GitLab


From 983e7c8b7bc642613a94dad54a9d4cabdf316cec Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:38:14 +0200
Subject: [PATCH 61/85] rm unused NumpyArrayDescriptor

---
 PyCore/Embed/PyObjectPtr.h         | 19 --------------
 PyCore/Embed/PythonInterpreter.cpp | 42 ------------------------------
 PyCore/Embed/PythonInterpreter.h   |  3 ---
 3 files changed, 64 deletions(-)

diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 2d9a446fc6b..5971fd262ac 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -48,23 +48,4 @@ private:
     PyObject* m_ptr = nullptr;
 };
 
-
-//! Numpy array descriptors
-struct NumpyArrayDescriptor {
-    //! C-contiguous array flag
-    bool C_contiguous = false;
-    //! FORTRAN-contiguous array flag
-    bool F_contiguous = false;
-    //! Flag to indicate if the data area of the Numpy array is aligned and in machine byte-order
-    bool wellbehaved = false;
-    //! Number of dimensions
-    int n_dims = 0;
-    //! Sizes of dimensions
-    std::vector<np_size_t> dims;
-    //! Character code indicating the data type
-    char dtype = '\0';
-    //! Flag to indicate whether the Numpy array owns its data
-    bool owns_data = false;
-};
-
 #endif // BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index fcaa4b2b7d9..a862f905ec0 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -315,48 +315,6 @@ bool PythonInterpreter::Numpy::isInitialized()
 
 //----------------------------------------
 
-NumpyArrayDescriptor PythonInterpreter::Numpy::arrayDescriptor(PyObject* pyobject_ptr)
-{
-    if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        throw(
-            std::runtime_error(errorDescription("PythonInterpreter::Numpy: Invalid Numpy array")));
-    }
-
-    PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
-    PyArray_Descr* npArray_descr = PyArray_DTYPE(npArray_ptr);
-    // const int npArray_type = PyArray_TYPE(npArray_ptr);
-    const int npArray_ndim = PyArray_NDIM(npArray_ptr);
-    npy_intp* npArray_dims = PyArray_DIMS(npArray_ptr);
-    const int npArray_flags = PyArray_FLAGS(npArray_ptr);
-    // character code indicating the data type
-    const char npArray_dtype = npArray_descr->type;
-
-
-    NumpyArrayDescriptor np_descr_out;
-    np_descr_out.n_dims = npArray_ndim;
-    if (np_descr_out.n_dims > 0) {
-        np_descr_out.dims.resize(npArray_ndim);
-        for (npy_intp i_d = 0; i_d < npArray_ndim; ++i_d)
-            np_descr_out.dims[i_d] = npArray_dims[i_d];
-    }
-
-    np_descr_out.dtype = npArray_dtype;
-
-    if (PyArray_IS_C_CONTIGUOUS(npArray_ptr))
-        np_descr_out.C_contiguous = true;
-
-    if (PyArray_IS_F_CONTIGUOUS(npArray_ptr))
-        np_descr_out.F_contiguous = true;
-
-    if (PyArray_ISBEHAVED_RO(npArray_ptr))
-        np_descr_out.wellbehaved = true;
-
-    if (npArray_flags & NPY_ARRAY_OWNDATA)
-        np_descr_out.owns_data = true;
-
-    return np_descr_out;
-}
-
 std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index 38df1344f06..a8d2d51fa60 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -66,9 +66,6 @@ int initialize();
 //! Checks if Numpy is initialized
 bool isInitialized();
 
-//! Obtains the descriptor of a Numpy array
-NumpyArrayDescriptor arrayDescriptor(PyObject* pyobject_ptr);
-
 //! Creates an empty Numpy array of type `double` with the given dimensions
 PyObjectPtr arrayND(std::vector<std::size_t>& dimensions);
 
-- 
GitLab


From 346365f5cdea281cb297c1da6c1dc5f94de08c8d Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:44:23 +0200
Subject: [PATCH 62/85] PythonInterpreter: standardize comments

---
 PyCore/Embed/PythonInterpreter.cpp | 31 +++++++++++++++++-------------
 PyCore/Embed/PythonInterpreter.h   |  3 +--
 2 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index a862f905ec0..a0180e651b9 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -1,3 +1,17 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Embed/PythonInterpreter.h
+//! @brief     Implements functions to expose Python-interpreter functionality to C++.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
 /* Embedded Python Interpreter
 
 Note that Python objects are structures allocated on the heap,
@@ -27,7 +41,6 @@ References:
 #include <string>
 #include <vector>
 
-
 namespace {
 
 // thin wrapper to initialize Numpy
@@ -104,7 +117,7 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
 }
 
 } // namespace
-//----------------------------------------
+
 
 // NOTE: "Python stable ABI" denotes the functions which use _only_ the Python's stable ABI;
 // see <https://docs.python.org/3/c-api/stable.html>
@@ -112,11 +125,8 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
 // Python stable ABI
 void PythonInterpreter::initialize()
 {
-    if (!Py_IsInitialized()) {
+    if (!Py_IsInitialized())
         Py_Initialize();
-    }
-
-    // if Python interpreter is already initialized, do nothing
 }
 
 // Python stable ABI
@@ -285,11 +295,9 @@ std::string PythonInterpreter::errorDescription(const std::string& title)
     return msg;
 }
 
-//----------------------------------------
 
 int PythonInterpreter::Numpy::initialize()
 {
-
     // initialize Python C API, if needed
     PythonInterpreter::initialize();
 
@@ -313,8 +321,6 @@ bool PythonInterpreter::Numpy::isInitialized()
     return static_cast<bool>(PyArray_API);
 }
 
-//----------------------------------------
-
 std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
@@ -582,7 +588,7 @@ double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
     return data_ptr;
 }
-//----------------------------------------
+
 
 PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
 {
@@ -665,14 +671,13 @@ std::vector<std::string> PythonInterpreter::BornAgain::listOfFunctions(const std
 
     return fn_names;
 }
-//----------------------------------------
+
 
 PyObjectPtr PythonInterpreter::Fabio::import()
 {
     return {PythonInterpreter::import("fabio")};
 }
 
-
 PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObjectPtr& fabio_module)
 {
     // load an image via calling `fabio.load` function which takes a
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PythonInterpreter.h
index a8d2d51fa60..627d9204050 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PythonInterpreter.h
@@ -3,8 +3,7 @@
 //  BornAgain: simulate and fit reflection and scattering
 //
 //! @file      PyCore/Embed/PythonInterpreter.h
-//! @brief     Definition of functions and classes to expose Python-interpreter functionality to
-//! C++.
+//! @brief     Declares functions to expose Python-interpreter functionality to C++.
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
-- 
GitLab


From e79465a623cda2c538ddceda1dbfcf714f99b61a Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:44:56 +0200
Subject: [PATCH 63/85] rm duplicate includes

---
 PyCore/Embed/PythonInterpreter.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PythonInterpreter.cpp
index a0180e651b9..923471d29f0 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PythonInterpreter.cpp
@@ -32,14 +32,11 @@ References:
 #undef INCLUDE_NUMPY
 
 #include "PythonInterpreter.h"
-
 #include <cstddef>   // NULL
 #include <cstring>   // memcpy
 #include <iostream>  // cerr
 #include <memory>    // unique_ptr
 #include <stdexcept> // runtime_error
-#include <string>
-#include <vector>
 
 namespace {
 
-- 
GitLab


From a289dce03e8d8d2b9d552b49d784a390deb56053 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:48:25 +0200
Subject: [PATCH 64/85] shorten name -> PyInterpreter

---
 Device/Data/Datafield.cpp                     |   8 +-
 GUI/View/Project/PyImportAssistant.cpp        |   6 +-
 ...ythonInterpreter.cpp => PyInterpreter.cpp} | 166 +++++++++---------
 .../{PythonInterpreter.h => PyInterpreter.h}  |  12 +-
 PyCore/Embed/PyObjectPtr.cpp                  |   6 +-
 PyCore/Sample/ImportMultiLayer.cpp            |  14 +-
 PyCore/Sample/ImportMultiLayer.h              |   4 +-
 7 files changed, 105 insertions(+), 111 deletions(-)
 rename PyCore/Embed/{PythonInterpreter.cpp => PyInterpreter.cpp} (77%)
 rename PyCore/Embed/{PythonInterpreter.h => PyInterpreter.h} (93%)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index d79a326dbf1..cd22ebc69e9 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -205,13 +205,13 @@ Datafield* Datafield::crop(double xmin, double xmax) const
 
 #ifdef BORNAGAIN_PYTHON
 
-#include "PyCore/Embed/PythonInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
+#include "PyCore/Embed/PyInterpreter.h" // Numpy::arrayND, Numpy::getDataPtr
 
 PyObject* Datafield::npArray() const
 {
     // TODO: Thoroughly check this function regarding index manipulations
 
-    PythonInterpreter::Numpy::initialize();
+    PyInterpreter::Numpy::initialize();
 
     ASSERT(rank() <= 2);
 
@@ -224,12 +224,12 @@ PyObject* Datafield::npArray() const
         std::swap(dimensions[0], dimensions[1]);
 
     // creating ndarray objects describing size of dimensions
-    PyObjectPtr pyarray{PythonInterpreter::Numpy::arrayND(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{PythonInterpreter::Numpy::getDataPtr(pyarray.get())};
+    double* data{PyInterpreter::Numpy::getDataPtr(pyarray.get())};
 
     if (!data)
         return nullptr;
diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index 169d4cdde7f..f360317bb4f 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -29,7 +29,7 @@
 #include "GUI/View/Info/MessageBox.h"
 #include "GUI/View/Project/ProjectManager.h"
 #include "GUI/View/Tool/Globals.h"
-#include "PyCore/Embed/PythonInterpreter.h" // listOfFunctions
+#include "PyCore/Embed/PyInterpreter.h"     // listOfFunctions
 #include "PyCore/Sample/ImportMultiLayer.h" // createMultiLayerFromPython
 #include "Sample/Multilayer/MultiLayer.h"
 #include <QApplication>
@@ -104,7 +104,7 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
     void* result_ptr = nullptr;
-    PyObjectPtr result{PythonInterpreter::createMultiLayerFromPython(
+    PyObjectPtr result{PyInterpreter::createMultiLayerFromPython(
         result_ptr, snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
     std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr)->clone());
@@ -153,7 +153,7 @@ QString getPySampleFunctionName(const QString& snippet)
     QApplication::setOverrideCursor(Qt::WaitCursor);
 
     std::vector<std::string> funcs_res{
-        PythonInterpreter::BornAgain::listOfFunctions(snippet.toStdString(), bornagainDir())};
+        PyInterpreter::BornAgain::listOfFunctions(snippet.toStdString(), bornagainDir())};
 
     if (funcs_res.empty()) {
         QApplication::restoreOverrideCursor();
diff --git a/PyCore/Embed/PythonInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
similarity index 77%
rename from PyCore/Embed/PythonInterpreter.cpp
rename to PyCore/Embed/PyInterpreter.cpp
index 923471d29f0..10bea670785 100644
--- a/PyCore/Embed/PythonInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -2,7 +2,7 @@
 //
 //  BornAgain: simulate and fit reflection and scattering
 //
-//! @file      PyCore/Embed/PythonInterpreter.h
+//! @file      PyCore/Embed/PyInterpreter.cpp
 //! @brief     Implements functions to expose Python-interpreter functionality to C++.
 //!
 //! @homepage  http://www.bornagainproject.org
@@ -31,7 +31,7 @@ References:
 #include "PyCore/Embed/PyCore.h"
 #undef INCLUDE_NUMPY
 
-#include "PythonInterpreter.h"
+#include "PyInterpreter.h"
 #include <cstddef>   // NULL
 #include <cstring>   // memcpy
 #include <iostream>  // cerr
@@ -79,16 +79,16 @@ std::vector<std::string> toVectorString(PyObject* py_object)
     if (PyTuple_Check(py_object)) {
         for (Py_ssize_t i = 0; i < PyTuple_Size(py_object); i++) {
             PyObject* value = PyTuple_GetItem(py_object, i);
-            result.push_back(PythonInterpreter::pyStrtoString(value));
+            result.push_back(PyInterpreter::pyStrtoString(value));
         }
     } else if (PyList_Check(py_object)) {
         for (Py_ssize_t i = 0; i < PyList_Size(py_object); i++) {
             PyObject* value = PyList_GetItem(py_object, i);
-            result.push_back(PythonInterpreter::pyStrtoString(value));
+            result.push_back(PyInterpreter::pyStrtoString(value));
         }
     } else {
-        throw std::runtime_error(PythonInterpreter::errorDescription(
-            "PythonInterpreter: Cannnot convert the given Python object "
+        throw std::runtime_error(PyInterpreter::errorDescription(
+            "PyInterpreter: Cannnot convert the given Python object "
             "to vector<string>."));
     }
 
@@ -120,20 +120,20 @@ inline void _realArray2DToDouble(PyArrayObject* const npArray_ptr, npy_intp rowI
 // see <https://docs.python.org/3/c-api/stable.html>
 
 // Python stable ABI
-void PythonInterpreter::initialize()
+void PyInterpreter::initialize()
 {
     if (!Py_IsInitialized())
         Py_Initialize();
 }
 
 // Python stable ABI
-bool PythonInterpreter::isInitialized()
+bool PyInterpreter::isInitialized()
 {
     return static_cast<bool>(Py_IsInitialized());
 }
 
 // Python stable ABI
-void PythonInterpreter::finalize()
+void PyInterpreter::finalize()
 {
     // undo all initializations made by Py_Initialize() and subsequent use
     // of Python/C API functions, and destroy all sub-interpreters.
@@ -142,11 +142,11 @@ void PythonInterpreter::finalize()
 }
 
 // Python stable ABI
-bool PythonInterpreter::checkError()
+bool PyInterpreter::checkError()
 {
     if (PyErr_Occurred()) {
         // print a standard traceback to sys.stderr and clear the error indicator
-        std::cerr << "---PythonInterpreter: Error in Python interpreter:\n";
+        std::cerr << "---PyInterpreter: Error in Python interpreter:\n";
         PyErr_Print();
         std::cerr << "\n---\n";
         return true;
@@ -156,7 +156,7 @@ bool PythonInterpreter::checkError()
 }
 
 // Python stable ABI
-void PythonInterpreter::addPythonPath(const std::string& path)
+void PyInterpreter::addPythonPath(const std::string& path)
 {
     if (!path.empty()) {
         // add path to `PYTHONPATH`
@@ -164,23 +164,22 @@ void PythonInterpreter::addPythonPath(const std::string& path)
         PyList_Append(sysPath, PyUnicode_FromString(path.c_str()));
     }
 
-    throw std::runtime_error(
-        "PythonInterpreter.addPythonPath: Cannot add to an empty Python path.");
+    throw std::runtime_error("PyInterpreter.addPythonPath: Cannot add to an empty Python path.");
 }
 
 // Python stable ABI
-void PythonInterpreter::setPythonPath(const std::string& path)
+void PyInterpreter::setPythonPath(const std::string& path)
 {
     // returns 0 on success, -1 on error
     const int result = PySys_SetObject((char*)"path", PyUnicode_FromString(path.c_str()));
     if (result != 0) {
-        PythonInterpreter::checkError();
-        throw std::runtime_error("PythonInterpreter.setPythonPath: Cannot set the Python path.");
+        PyInterpreter::checkError();
+        throw std::runtime_error("PyInterpreter.setPythonPath: Cannot set the Python path.");
     }
 }
 
 // Python stable ABI
-PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const std::string& path)
+PyObjectPtr PyInterpreter::import(const std::string& pymodule_name, const std::string& path)
 {
     addPythonPath(path);
 
@@ -188,7 +187,7 @@ PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const st
     PyObject* pymodule = PyImport_ImportModule(pymodule_name.c_str());
     if (!pymodule || !PyModule_Check(pymodule)) {
         checkError();
-        throw std::runtime_error(errorDescription("PythonInterpreter: Cannot load Python module '"
+        throw std::runtime_error(errorDescription("PyInterpreter: Cannot load Python module '"
                                                   + pymodule_name + "' (given path = '" + path
                                                   + "')"));
     }
@@ -198,12 +197,12 @@ PyObjectPtr PythonInterpreter::import(const std::string& pymodule_name, const st
 }
 
 // Python stable ABI
-void PythonInterpreter::DecRef(PyObject* py_object)
+void PyInterpreter::DecRef(PyObject* py_object)
 {
     Py_XDECREF(py_object);
 }
 
-std::string PythonInterpreter::pyStrtoString(PyObject* py_object)
+std::string PyInterpreter::pyStrtoString(PyObject* py_object)
 {
     std::string result;
     PyObject* pyStr = PyUnicode_AsEncodedString(py_object, "utf-8", "replace");
@@ -215,7 +214,7 @@ std::string PythonInterpreter::pyStrtoString(PyObject* py_object)
     return result;
 }
 
-std::string PythonInterpreter::runtimeInfo()
+std::string PyInterpreter::runtimeInfo()
 {
     std::string result;
 
@@ -241,7 +240,7 @@ std::string PythonInterpreter::runtimeInfo()
 
 // Attempt to retrieve Python stack trace
 // Ref: <https://stackoverflow.com/q/1796510>
-std::string PythonInterpreter::stackTrace()
+std::string PyInterpreter::stackTrace()
 {
     std::string result;
 
@@ -280,23 +279,23 @@ std::string PythonInterpreter::stackTrace()
     }
 
     result += "\n";
-    result += PythonInterpreter::runtimeInfo();
+    result += PyInterpreter::runtimeInfo();
     result += "\n";
 
     return result;
 }
 
-std::string PythonInterpreter::errorDescription(const std::string& title)
+std::string PyInterpreter::errorDescription(const std::string& title)
 {
-    std::string msg = title + "\n" + PythonInterpreter::stackTrace() + "\n";
+    std::string msg = title + "\n" + PyInterpreter::stackTrace() + "\n";
     return msg;
 }
 
 
-int PythonInterpreter::Numpy::initialize()
+int PyInterpreter::Numpy::initialize()
 {
     // initialize Python C API, if needed
-    PythonInterpreter::initialize();
+    PyInterpreter::initialize();
 
     int res;
     _init_numpy(res);
@@ -305,7 +304,7 @@ int PythonInterpreter::Numpy::initialize()
     case 0:
         return res;
     case 1:
-        throw std::runtime_error(errorDescription("PythonInterpreter: Cannot initialize Numpy"));
+        throw std::runtime_error(errorDescription("PyInterpreter: Cannot initialize Numpy"));
     case 2:
         return res;
     }
@@ -313,17 +312,16 @@ int PythonInterpreter::Numpy::initialize()
     return res;
 }
 
-bool PythonInterpreter::Numpy::isInitialized()
+bool PyInterpreter::Numpy::isInitialized()
 {
     return static_cast<bool>(PyArray_API);
 }
 
-std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
+std::vector<double> PyInterpreter::Numpy::createVectorFromArray2D(PyObject* pyobject_ptr)
 {
     if (!pyobject_ptr || !PyArray_Check(pyobject_ptr)) {
-        throw(std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: Cannot convert an invalid "
-                             "Numpy array to a C-Array")));
+        throw(std::runtime_error(errorDescription("PyInterpreter::Numpy: Cannot convert an invalid "
+                                                  "Numpy array to a C-Array")));
     }
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -338,18 +336,17 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
 
     // Numpy array type must be 2d
     if (npArray_ndim != 2) {
-        std::string msg = "PythonInterpreter::Numpy: Expected a Numpy 2d-array "
+        std::string msg = "PyInterpreter::Numpy: Expected a Numpy 2d-array "
                           "(given number of dimensions is "
                           + std::to_string(npArray_ndim) + ")";
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: Expected a Numpy 2d-array "
-                             "(given number of dimensions is "
-                             + std::to_string(npArray_ndim) + ")"));
+        throw std::runtime_error(errorDescription("PyInterpreter::Numpy: Expected a Numpy 2d-array "
+                                                  "(given number of dimensions is "
+                                                  + std::to_string(npArray_ndim) + ")"));
     }
 
     // Numpy array type must be numeric and real (eligible for casting to double)
     if (!PyDataType_ISNUMBER(npArray_descr) || PyDataType_ISCOMPLEX(npArray_descr)) {
-        std::string msg = "PythonInterpreter::Numpy: "
+        std::string msg = "PyInterpreter::Numpy: "
                           "Expected a Numpy array of numeric type and real "
                           "(given type '"
                           + std::to_string(npArray_dtype) + "')";
@@ -409,7 +406,7 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
 #endif // _WIN32
 
     default:
-        throw std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+        throw std::runtime_error(errorDescription("PyInterpreter::Numpy: "
                                                   "Conversion of Numpy array of dtype '"
                                                   + std::to_string(npArray_dtype)
                                                   + "' "
@@ -419,17 +416,16 @@ std::vector<double> PythonInterpreter::Numpy::createVectorFromArray2D(PyObject*
     return data;
 }
 
-PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
-                                                         const np_size_t dims[2])
+PyObjectPtr PyInterpreter::Numpy::createArray2DfromC(double* const c_array, const np_size_t dims[2])
 {
     if (!c_array) {
-        throw std::runtime_error("PythonInterpreter::Numpy: "
+        throw std::runtime_error("PyInterpreter::Numpy: "
                                  "Cannot create a Numpy 1D-array from a null data pointer");
     }
 
     const np_size_t size = dims[0] * dims[1];
     if (size < 1) {
-        throw std::runtime_error("PythonInterpreter::Numpy: "
+        throw std::runtime_error("PyInterpreter::Numpy: "
                                  "Cannot create a Numpy 1D-array from a data with size = 0");
     }
 
@@ -439,7 +435,7 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
     PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 2, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
-        throw std::runtime_error("PythonInterpreter::Numpy: "
+        throw std::runtime_error("PyInterpreter::Numpy: "
                                  "Cannot create a Numpy 1D-array from the "
                                  "given data (size = "
                                  + std::to_string(size) + ")");
@@ -457,17 +453,16 @@ PyObjectPtr PythonInterpreter::Numpy::createArray2DfromC(double* const c_array,
     return {npArray_ptr};
 }
 
-PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
-                                                         const np_size_t size)
+PyObjectPtr PyInterpreter::Numpy::createArray1DfromC(double* const c_array, const np_size_t size)
 {
     if (!c_array) {
-        throw std::runtime_error("PythonInterpreter::Numpy: "
+        throw std::runtime_error("PyInterpreter::Numpy: "
                                  "Cannot create a Numpy 1D-array from a null data pointer");
     }
 
     if (size < 1) {
         throw std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: "
+            errorDescription("PyInterpreter::Numpy: "
                              "Cannot create a Numpy 1D-array from a data with size = 0"));
     }
 
@@ -477,11 +472,11 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
     PyObject* npArray_ptr = PyArray_SimpleNew(/* n_dims */ 1, npDims, NPY_DOUBLE);
     if (!npArray_ptr) {
         checkError();
-        std::string msg = "PythonInterpreter::Numpy: "
+        std::string msg = "PyInterpreter::Numpy: "
                           "Cannot create a Numpy 1D-array from the "
                           "given data (size = "
                           + std::to_string(size) + ")";
-        throw std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+        throw std::runtime_error(errorDescription("PyInterpreter::Numpy: "
                                                   "Cannot create a Numpy 1D-array from the "
                                                   "given data (size = "
                                                   + std::to_string(size) + ")"));
@@ -500,18 +495,18 @@ PyObjectPtr PythonInterpreter::Numpy::createArray1DfromC(double* const c_array,
 }
 
 
-PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const np_size_t dims[2])
+PyObjectPtr PyInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const np_size_t dims[2])
 {
     if (!c_array) {
         throw std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: "
+            errorDescription("PyInterpreter::Numpy: "
                              "Cannot create a Numpy 2D-array from a null data pointer"));
     }
 
     const np_size_t size = dims[0] * dims[1];
     if (size < 1) {
         throw std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: "
+            errorDescription("PyInterpreter::Numpy: "
                              "Cannot create a Numpy 2D-array from a data with size = 0"));
     }
 
@@ -522,17 +517,16 @@ PyObjectPtr PythonInterpreter::Numpy::CArrayAsNpy2D(double* const c_array, const
         /* n_dims */ 2, npDims, NPY_DOUBLE, reinterpret_cast<void*>(c_array));
 
     if (!npArray_ptr || !PyArray_Check(npArray_ptr)) {
-        PythonInterpreter::checkError();
-        throw std::runtime_error(
-            errorDescription("PythonInterpreter::Numpy: Cannot convert the given "
-                             "C-Array to a Numpy 2D-array"));
+        PyInterpreter::checkError();
+        throw std::runtime_error(errorDescription("PyInterpreter::Numpy: Cannot convert the given "
+                                                  "C-Array to a Numpy 2D-array"));
     }
 
     // returns a _new_ reference; ie. caller is responsible for the ref-count
     return {npArray_ptr};
 }
 
-PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensions)
+PyObjectPtr PyInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensions)
 {
     // descriptors for the array dimensions
     const std::size_t n_dims = dimensions.size();
@@ -561,7 +555,7 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
 
     if (!npyArray_ptr) {
         checkError();
-        throw std::runtime_error(errorDescription("PythonInterpreter::Numpy: Cannot create a Numpy "
+        throw std::runtime_error(errorDescription("PyInterpreter::Numpy: Cannot create a Numpy "
                                                   + std::to_string(n_dims)
                                                   + "D-array from the given data"));
     }
@@ -569,7 +563,7 @@ PyObjectPtr PythonInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensio
     return {npyArray_ptr};
 }
 
-double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
+double* PyInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 {
 
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
@@ -579,7 +573,7 @@ double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
     if (!data_ptr) {
         checkError();
-        throw(std::runtime_error(errorDescription("PythonInterpreter::Numpy: "
+        throw(std::runtime_error(errorDescription("PyInterpreter::Numpy: "
                                                   "Numpy array has invalid data pointer")));
     }
 
@@ -587,9 +581,9 @@ double* PythonInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 }
 
 
-PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
+PyObjectPtr PyInterpreter::BornAgain::import(const std::string& path)
 {
-    PythonInterpreter::addPythonPath(path);
+    PyInterpreter::addPythonPath(path);
 
 #ifndef _WIN32
     // Stores signal handler before numpy's mess it up.
@@ -604,7 +598,7 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
     if (!ba_pymodule || !PyModule_Check(ba_pymodule)) {
         checkError();
         throw std::runtime_error(
-            errorDescription("PythonInterpreter: Cannot load 'bornagain' Python module "
+            errorDescription("PyInterpreter: Cannot load 'bornagain' Python module "
                              "(given path = '"
                              + path + "')"));
     }
@@ -617,10 +611,10 @@ PyObjectPtr PythonInterpreter::BornAgain::import(const std::string& path)
     return {ba_pymodule};
 }
 
-PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script,
-                                                       const std::string& path)
+PyObjectPtr PyInterpreter::BornAgain::importScript(const std::string& script,
+                                                   const std::string& path)
 {
-    PyObjectPtr ba_pymodule{PythonInterpreter::BornAgain::import(path)};
+    PyObjectPtr ba_pymodule{PyInterpreter::BornAgain::import(path)};
     // TODO: Check ba module
 
     PyObject* pCompiledFn = Py_CompileString(script.c_str(), "", Py_file_input);
@@ -641,15 +635,15 @@ PyObjectPtr PythonInterpreter::BornAgain::importScript(const std::string& script
 }
 
 
-std::vector<std::string> PythonInterpreter::BornAgain::listOfFunctions(const std::string& script,
-                                                                       const std::string& path)
+std::vector<std::string> PyInterpreter::BornAgain::listOfFunctions(const std::string& script,
+                                                                   const std::string& path)
 {
-    PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
+    PyObjectPtr tmpModule{PyInterpreter::BornAgain::importScript(script, path)};
 
     PyObject* pDict = PyModule_GetDict(tmpModule.get());
     if (!pDict) {
         checkError();
-        throw std::runtime_error("PythonInterpreter::BornAgain: "
+        throw std::runtime_error("PyInterpreter::BornAgain: "
                                  "Cannot obtain the dictionary from the script module");
     }
 
@@ -658,7 +652,7 @@ std::vector<std::string> PythonInterpreter::BornAgain::listOfFunctions(const std
     std::vector<std::string> fn_names;
     while (PyDict_Next(pDict, &pos, &key, &value)) {
         if (PyCallable_Check(value)) {
-            std::string func_name{PythonInterpreter::pyStrtoString(key)};
+            std::string func_name{PyInterpreter::pyStrtoString(key)};
             if (func_name.find("__") == std::string::npos)
                 fn_names.push_back(func_name);
         }
@@ -670,34 +664,34 @@ std::vector<std::string> PythonInterpreter::BornAgain::listOfFunctions(const std
 }
 
 
-PyObjectPtr PythonInterpreter::Fabio::import()
+PyObjectPtr PyInterpreter::Fabio::import()
 {
-    return {PythonInterpreter::import("fabio")};
+    return {PyInterpreter::import("fabio")};
 }
 
-PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObjectPtr& fabio_module)
+PyObjectPtr PyInterpreter::Fabio::open(const std::string& filename, PyObjectPtr& fabio_module)
 {
     // load an image via calling `fabio.load` function which takes a
     // filename (Python string) and returns a Numpy array.
 
     if (!fabio_module.valid() || !PyModule_Check(fabio_module.get())) {
-        throw std::runtime_error(errorDescription("PythonInterpreter.fabio: Invalid Python module "
+        throw std::runtime_error(errorDescription("PyInterpreter.fabio: Invalid Python module "
                                                   "(expected 'fabio' module)"));
     }
 
     PyObject* pFunc = PyObject_GetAttrString(fabio_module.get(), (char*)"open");
     if (!pFunc || !PyCallable_Check(pFunc)) {
-        PythonInterpreter::checkError();
+        PyInterpreter::checkError();
         throw std::runtime_error(
-            errorDescription("PythonInterpreter.fabio: The function 'fabio.open' is not callable"));
+            errorDescription("PyInterpreter.fabio: The function 'fabio.open' is not callable"));
     }
 
     // convert the filename to a Python unicode string
     PyObject* pFilename = PyUnicode_FromString(filename.c_str());
     if (!pFilename) {
-        PythonInterpreter::checkError();
+        PyInterpreter::checkError();
         throw std::runtime_error(
-            errorDescription("PythonInterpreter.fabio: Filename '" + filename
+            errorDescription("PyInterpreter.fabio: Filename '" + filename
                              + "' cannot be converted to Python unicode string"));
     }
 
@@ -705,8 +699,8 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     PyObject* pResult_open = PyObject_CallFunctionObjArgs(pFunc, pFilename, NULL);
     Py_DecRef(pFunc);
     if (!pResult_open) {
-        PythonInterpreter::checkError();
-        std::runtime_error(errorDescription("PythonInterpreter.fabio: "
+        PyInterpreter::checkError();
+        std::runtime_error(errorDescription("PyInterpreter.fabio: "
                                             "Invalid return value from calling the function "
                                             "'fabio.open(\""
                                             + filename + "\")'"));
@@ -716,8 +710,8 @@ PyObjectPtr PythonInterpreter::Fabio::open(const std::string& filename, PyObject
     PyObject* npyArray_ptr = PyObject_GetAttrString(pResult_open, (char*)"data");
     Py_DecRef(pResult_open);
     if (!npyArray_ptr || !PyArray_Check(npyArray_ptr)) {
-        PythonInterpreter::checkError();
-        std::runtime_error(errorDescription("PythonInterpreter.fabio: Invalid return value from "
+        PyInterpreter::checkError();
+        std::runtime_error(errorDescription("PyInterpreter.fabio: Invalid return value from "
                                             "calling the function 'fabio.open(\""
                                             + filename + "\")' (expected a Numpy array)"));
     }
diff --git a/PyCore/Embed/PythonInterpreter.h b/PyCore/Embed/PyInterpreter.h
similarity index 93%
rename from PyCore/Embed/PythonInterpreter.h
rename to PyCore/Embed/PyInterpreter.h
index 627d9204050..c3c3725f1b2 100644
--- a/PyCore/Embed/PythonInterpreter.h
+++ b/PyCore/Embed/PyInterpreter.h
@@ -2,7 +2,7 @@
 //
 //  BornAgain: simulate and fit reflection and scattering
 //
-//! @file      PyCore/Embed/PythonInterpreter.h
+//! @file      PyCore/Embed/PyInterpreter.h
 //! @brief     Declares functions to expose Python-interpreter functionality to C++.
 //!
 //! @homepage  http://www.bornagainproject.org
@@ -12,14 +12,14 @@
 //
 //  ************************************************************************************************
 
-#ifndef BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
-#define BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
+#ifndef BORNAGAIN_PYCORE_EMBED_PYINTERPRETER_H
+#define BORNAGAIN_PYCORE_EMBED_PYINTERPRETER_H
 
 #include "PyCore/Embed/PyObjectPtr.h"
 #include <string>
 #include <vector>
 
-namespace PythonInterpreter {
+namespace PyInterpreter {
 
 // Python stable ABI
 void initialize();
@@ -111,6 +111,6 @@ PyObjectPtr open(const std::string& filename, PyObjectPtr& fabio_module);
 
 } // namespace Fabio
 
-} // namespace PythonInterpreter
+} // namespace PyInterpreter
 
-#endif // BORNAGAIN_PYCORE_EMBED_PYTHONINTERPRETER_H
+#endif // BORNAGAIN_PYCORE_EMBED_PYINTERPRETER_H
diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index 0999b33bcd6..93e9530bd2b 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -13,7 +13,7 @@
 //  ************************************************************************************************
 
 #include "PyObjectPtr.h"
-#include "PythonInterpreter.h"
+#include "PyInterpreter.h"
 
 #include <stdexcept> // runtime_error
 
@@ -53,13 +53,13 @@ void PyObjectPtr::reset()
 
 void PyObjectPtr::discard()
 {
-    if (!PythonInterpreter::isInitialized()) {
+    if (!PyInterpreter::isInitialized()) {
         throw(std::runtime_error("Decrementing Python reference-count without "
                                  "Python initialized leads to memory access violation "
                                  "(segmentation fault)"));
     }
 
-    PythonInterpreter::DecRef(m_ptr);
+    PyInterpreter::DecRef(m_ptr);
     reset();
 }
 
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 57e15a633bd..c538008ee88 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -2,7 +2,7 @@
 
 #include "ImportMultiLayer.h"
 #include "PyCore/Embed/PyCore.h"
-#include "PyCore/Embed/PythonInterpreter.h" // BornAgain::importScript
+#include "PyCore/Embed/PyInterpreter.h" // BornAgain::importScript
 
 // SWIG runtime access for creating a MultiLayer instance from Python
 #include "auto/Wrap/swig_runtime.h"
@@ -10,17 +10,17 @@
 #include <stdexcept> // runtime_error
 
 
-namespace PythonInterpreter {
+namespace PyInterpreter {
 
 PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string& script,
                                        const std::string& functionName, const std::string& path)
 {
-    PyObjectPtr tmpModule{PythonInterpreter::BornAgain::importScript(script, path)};
+    PyObjectPtr tmpModule{PyInterpreter::BornAgain::importScript(script, path)};
 
     // locate the `get_simulation` function (it is an attribute of the module)
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.get(), functionName.c_str());
     if (!pAddFn) {
-        throw std::runtime_error(errorDescription("PythonInterpreter::BornAgain: "
+        throw std::runtime_error(errorDescription("PyInterpreter::BornAgain: "
                                                   "Cannot locate the compiled function '"
                                                   + functionName + "'"));
     }
@@ -33,7 +33,7 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
 
     if (!instance) {
         throw std::runtime_error(errorDescription(
-            "PythonInterpreter::BornAgain: Cannot call the function '" + functionName + "'"));
+            "PyInterpreter::BornAgain: Cannot call the function '" + functionName + "'"));
     }
 
     // Construct a C++ object from the Python object
@@ -48,7 +48,7 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     if (!SWIG_IsOK(res)) {
         Py_DecRef(instance);
         throw std::runtime_error(
-            errorDescription("PythonInterpreter::BornAgain: SWIG failed to extract a "
+            errorDescription("PyInterpreter::BornAgain: SWIG failed to extract a "
                              "'MultiLayer' instance via calling the function '"
                              + functionName + "'"));
     }
@@ -57,6 +57,6 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     return {instance};
 }
 
-} // namespace PythonInterpreter
+} // namespace PyInterpreter
 
 #endif // BORNAGAIN_PYTHON
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 97aa9127fd7..bac5057da48 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -24,7 +24,7 @@
 
 class MultiLayer;
 
-namespace PythonInterpreter {
+namespace PyInterpreter {
 
 //! Creates a multi layer by running python code in embedded interpreter, given
 //! The Python-script filename and the name of the function which produces a `MultiLayer`.
@@ -37,6 +37,6 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
                                        const std::string& functionName,
                                        const std::string& path = "");
 
-} // namespace PythonInterpreter
+} // namespace PyInterpreter
 
 #endif // BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
-- 
GitLab


From ce04f7836965e3f3491a0019590bea0c61c4608c Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:54:01 +0200
Subject: [PATCH 65/85] rm unused include

---
 PyCore/Embed/PyObjectPtr.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 5971fd262ac..6ff4232b60d 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -16,7 +16,6 @@
 #define BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 
 #include "PyCore/Embed/PyObjectDecl.h"
-#include <vector>
 
 //! Safe container for PyObjects:
 //! The class `PyObjectPtr` contains a `PyObject*` (or `NULL`).
-- 
GitLab


From 906c33411317a51ca18be2ed7728adb257832774 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 09:57:58 +0200
Subject: [PATCH 66/85] get && reset => release

---
 PyCore/Embed/PyObjectPtr.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index 93e9530bd2b..6c0cfb60716 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -28,10 +28,8 @@ PyObjectPtr::~PyObjectPtr()
 }
 
 PyObjectPtr::PyObjectPtr(PyObjectPtr&& other)
-    : PyObjectPtr(other.get())
+    : PyObjectPtr(other.release())
 {
-    // reset the moved object
-    other.reset();
 }
 
 PyObject* PyObjectPtr::get()
-- 
GitLab


From 94247bc1afdf1e3a7910549d4f6d8360f99fe3c7 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 10:03:45 +0200
Subject: [PATCH 67/85] code formatting

---
 PyCore/Embed/PyInterpreter.cpp     | 4 +---
 PyCore/Embed/PyInterpreter.h       | 2 ++
 PyCore/Embed/PyObjectPtr.cpp       | 3 +--
 PyCore/Embed/PyObjectPtr.h         | 1 -
 PyCore/Sample/ImportMultiLayer.cpp | 1 -
 5 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/PyCore/Embed/PyInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
index 10bea670785..d62cb345cb1 100644
--- a/PyCore/Embed/PyInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -230,9 +230,8 @@ std::string PyInterpreter::runtimeInfo()
     PyObject* sysPath = PySys_GetObject((char*)"path");
     std::vector<std::string> content{toVectorString(sysPath)};
     result += "sys.path: ";
-    for (const std::string& s : content) {
+    for (const std::string& s : content)
         result += s + ",";
-    }
     result += "\n";
 
     return result;
@@ -565,7 +564,6 @@ PyObjectPtr PyInterpreter::Numpy::arrayND(std::vector<std::size_t>& dimensions)
 
 double* PyInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 {
-
     PyArrayObject* npArray_ptr{reinterpret_cast<PyArrayObject*>(pyobject_ptr)};
 
     // get the pointer to data buffer of the Numpy array
diff --git a/PyCore/Embed/PyInterpreter.h b/PyCore/Embed/PyInterpreter.h
index c3c3725f1b2..55fc1e1af42 100644
--- a/PyCore/Embed/PyInterpreter.h
+++ b/PyCore/Embed/PyInterpreter.h
@@ -90,6 +90,7 @@ PyObjectPtr CArrayAsNpy2D(double* const c_array, const np_size_t dims[2]);
 
 // BornAgain-related functionality
 namespace BornAgain {
+
 //! Imports BornAgain from given location. If path is empty, tries to rely on PYTHONPATH.
 PyObjectPtr import(const std::string& path = "");
 
@@ -104,6 +105,7 @@ std::vector<std::string> listOfFunctions(const std::string& script, const std::s
 
 // Fabio-related functionality
 namespace Fabio {
+
 //! Imports 'fabio' Python module
 PyObjectPtr import();
 //! Calls 'fabio.open' on a given file (needs Numpy)
diff --git a/PyCore/Embed/PyObjectPtr.cpp b/PyCore/Embed/PyObjectPtr.cpp
index 6c0cfb60716..34363bcf4ce 100644
--- a/PyCore/Embed/PyObjectPtr.cpp
+++ b/PyCore/Embed/PyObjectPtr.cpp
@@ -51,11 +51,10 @@ void PyObjectPtr::reset()
 
 void PyObjectPtr::discard()
 {
-    if (!PyInterpreter::isInitialized()) {
+    if (!PyInterpreter::isInitialized())
         throw(std::runtime_error("Decrementing Python reference-count without "
                                  "Python initialized leads to memory access violation "
                                  "(segmentation fault)"));
-    }
 
     PyInterpreter::DecRef(m_ptr);
     reset();
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 6ff4232b60d..5e3ff54c48e 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -22,7 +22,6 @@
 //! Decrementing Python reference-count is performed automatically when
 //! the life-time of a `PyObjectPtr` expires.
 class PyObjectPtr {
-
 public:
     PyObjectPtr(PyObject* pyobject_ptr);
     ~PyObjectPtr();
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index c538008ee88..8eedcb35e8d 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -9,7 +9,6 @@
 
 #include <stdexcept> // runtime_error
 
-
 namespace PyInterpreter {
 
 PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string& script,
-- 
GitLab


From 9ee786373c291d875014f7deefcb834cbbcee11c Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 10:14:05 +0200
Subject: [PATCH 68/85] define np_size_t where it is used

---
 PyCore/Embed/PyInterpreter.h | 2 ++
 PyCore/Embed/PyObjectDecl.h  | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/PyCore/Embed/PyInterpreter.h b/PyCore/Embed/PyInterpreter.h
index 55fc1e1af42..d4c8267838c 100644
--- a/PyCore/Embed/PyInterpreter.h
+++ b/PyCore/Embed/PyInterpreter.h
@@ -59,6 +59,8 @@ std::string errorDescription(const std::string& title = "");
 //-- Numpy-related functionality
 namespace Numpy {
 
+typedef long int np_size_t; // size type used in Numpy
+
 //! Initializes Numpy
 int initialize();
 
diff --git a/PyCore/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
index c1af8b2a02d..a2b52e87e0b 100644
--- a/PyCore/Embed/PyObjectDecl.h
+++ b/PyCore/Embed/PyObjectDecl.h
@@ -22,6 +22,4 @@ typedef _object PyObject;
 
 #endif // PyObject_HEAD
 
-typedef long int np_size_t; // size type used in Numpy
-
 #endif // BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
-- 
GitLab


From 7c62ff14d770d76cdb14195383c43fcd9defd701 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 10:19:59 +0200
Subject: [PATCH 69/85] ImportMultilayer: add/simplify file header

---
 PyCore/Sample/ImportMultiLayer.cpp | 20 ++++++++++++++++----
 PyCore/Sample/ImportMultiLayer.h   |  7 +++----
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 8eedcb35e8d..271cc81d46d 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -1,3 +1,17 @@
+//  ************************************************************************************************
+//
+//  BornAgain: simulate and fit reflection and scattering
+//
+//! @file      PyCore/Sample/ImportMultiLayer.h
+//! @brief     Implements createMultiLayerFromPython.
+//!
+//! @homepage  http://www.bornagainproject.org
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2018
+//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
+//
+//  ************************************************************************************************
+
 #ifdef BORNAGAIN_PYTHON
 
 #include "ImportMultiLayer.h"
@@ -18,11 +32,10 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
 
     // locate the `get_simulation` function (it is an attribute of the module)
     PyObject* pAddFn = PyObject_GetAttrString(tmpModule.get(), functionName.c_str());
-    if (!pAddFn) {
+    if (!pAddFn)
         throw std::runtime_error(errorDescription("PyInterpreter::BornAgain: "
                                                   "Cannot locate the compiled function '"
                                                   + functionName + "'"));
-    }
 
     // create a `MultiLayer` Python object via calling the function
     PyObject* instance = PyObject_CallFunctionObjArgs(pAddFn, NULL);
@@ -30,10 +43,9 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     // clean up
     Py_DecRef(pAddFn);
 
-    if (!instance) {
+    if (!instance)
         throw std::runtime_error(errorDescription(
             "PyInterpreter::BornAgain: Cannot call the function '" + functionName + "'"));
-    }
 
     // Construct a C++ object from the Python object
     /* NOTE:
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index bac5057da48..76c06749c31 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -3,7 +3,7 @@
 //  BornAgain: simulate and fit reflection and scattering
 //
 //! @file      PyCore/Sample/ImportMultiLayer.h
-//! @brief     Forward declaration for createMultiLayerFromPython.
+//! @brief     Declares createMultiLayerFromPython.
 //!
 //! @homepage  http://www.bornagainproject.org
 //! @license   GNU General Public License v3 or higher (see COPYING)
@@ -12,12 +12,11 @@
 //
 //  ************************************************************************************************
 
-#ifndef BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
-#define BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
-
 #ifdef SWIG
 #error no need to expose this header to Swig
 #endif
+#ifndef BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
+#define BORNAGAIN_PYCORE_SAMPLE_IMPORTMULTILAYER_H
 
 #include "PyCore/Embed/PyObjectPtr.h"
 #include <string>
-- 
GitLab


From e4fc54291a067fd16182ce183163fd23477c9ee9 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 10:21:22 +0200
Subject: [PATCH 70/85] correct include

---
 PyCore/Sample/ImportMultiLayer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 271cc81d46d..dd02da7f350 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -14,7 +14,7 @@
 
 #ifdef BORNAGAIN_PYTHON
 
-#include "ImportMultiLayer.h"
+#include "PyCore/Sample/ImportMultiLayer.h"
 #include "PyCore/Embed/PyCore.h"
 #include "PyCore/Embed/PyInterpreter.h" // BornAgain::importScript
 
-- 
GitLab


From 10110d86ec63caf0a754d0de9afcaac9aec3712e Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 12:58:10 +0200
Subject: [PATCH 71/85] rm blank line

---
 PyCore/Sample/ImportMultiLayer.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index dd02da7f350..61333b45312 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -20,7 +20,6 @@
 
 // SWIG runtime access for creating a MultiLayer instance from Python
 #include "auto/Wrap/swig_runtime.h"
-
 #include <stdexcept> // runtime_error
 
 namespace PyInterpreter {
-- 
GitLab


From 9b4f35d2a132cb5903b5637a64af801e8e17d902 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 13:25:55 +0200
Subject: [PATCH 72/85] correct include; assert that added path is not empty

---
 PyCore/Embed/PyInterpreter.cpp | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/PyCore/Embed/PyInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
index d62cb345cb1..0e4c4024bb4 100644
--- a/PyCore/Embed/PyInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -31,7 +31,8 @@ References:
 #include "PyCore/Embed/PyCore.h"
 #undef INCLUDE_NUMPY
 
-#include "PyInterpreter.h"
+#include "PyCore/Embed/PyInterpreter.h"
+#include "Base/Util/Assert.h"
 #include <cstddef>   // NULL
 #include <cstring>   // memcpy
 #include <iostream>  // cerr
@@ -151,20 +152,15 @@ bool PyInterpreter::checkError()
         std::cerr << "\n---\n";
         return true;
     }
-
     return false;
 }
 
 // Python stable ABI
 void PyInterpreter::addPythonPath(const std::string& path)
 {
-    if (!path.empty()) {
-        // add path to `PYTHONPATH`
-        PyObject* sysPath = PySys_GetObject((char*)"path");
-        PyList_Append(sysPath, PyUnicode_FromString(path.c_str()));
-    }
-
-    throw std::runtime_error("PyInterpreter.addPythonPath: Cannot add to an empty Python path.");
+    ASSERT(!path.empty());
+    PyObject* sysPath = PySys_GetObject((char*)"path");
+    PyList_Append(sysPath, PyUnicode_FromString(path.c_str())); // add to `PYTHONPATH`
 }
 
 // Python stable ABI
-- 
GitLab


From ae2004d59798705226c8a15a06fbbcfa6122493b Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 13:33:11 +0200
Subject: [PATCH 73/85] correct logic for addPythonPath

---
 PyCore/Embed/PyInterpreter.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/PyCore/Embed/PyInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
index 0e4c4024bb4..07b970bc83f 100644
--- a/PyCore/Embed/PyInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -177,7 +177,9 @@ void PyInterpreter::setPythonPath(const std::string& path)
 // Python stable ABI
 PyObjectPtr PyInterpreter::import(const std::string& pymodule_name, const std::string& path)
 {
-    addPythonPath(path);
+    ASSERT(!pymodule_name.empty());
+    if(!path.empty())
+        addPythonPath(path);
 
     // import the module
     PyObject* pymodule = PyImport_ImportModule(pymodule_name.c_str());
@@ -577,7 +579,8 @@ double* PyInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
 PyObjectPtr PyInterpreter::BornAgain::import(const std::string& path)
 {
-    PyInterpreter::addPythonPath(path);
+    if(!path.empty())
+        PyInterpreter::addPythonPath(path);
 
 #ifndef _WIN32
     // Stores signal handler before numpy's mess it up.
-- 
GitLab


From 9e5da4cccc5d7165f18a74aea7577ae4f98f93f5 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 14:37:32 +0200
Subject: [PATCH 74/85] rm unnessary cast

---
 PyCore/Embed/PyInterpreter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/PyCore/Embed/PyInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
index 07b970bc83f..8a73b0e2d35 100644
--- a/PyCore/Embed/PyInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -159,7 +159,7 @@ bool PyInterpreter::checkError()
 void PyInterpreter::addPythonPath(const std::string& path)
 {
     ASSERT(!path.empty());
-    PyObject* sysPath = PySys_GetObject((char*)"path");
+    PyObject* sysPath = PySys_GetObject("path");
     PyList_Append(sysPath, PyUnicode_FromString(path.c_str())); // add to `PYTHONPATH`
 }
 
-- 
GitLab


From 7a25045fb92aefdd137cce7e7d3e5df20638b2c7 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 17:14:51 +0200
Subject: [PATCH 75/85] add call to initialize() => resolved segfault

---
 PyCore/Embed/PyInterpreter.cpp | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/PyCore/Embed/PyInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
index 8a73b0e2d35..3873f5d8af2 100644
--- a/PyCore/Embed/PyInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -178,6 +178,9 @@ void PyInterpreter::setPythonPath(const std::string& path)
 PyObjectPtr PyInterpreter::import(const std::string& pymodule_name, const std::string& path)
 {
     ASSERT(!pymodule_name.empty());
+
+    PyInterpreter::Numpy::initialize();
+
     if(!path.empty())
         addPythonPath(path);
 
@@ -583,15 +586,16 @@ PyObjectPtr PyInterpreter::BornAgain::import(const std::string& path)
         PyInterpreter::addPythonPath(path);
 
 #ifndef _WIN32
-    // Stores signal handler before numpy's mess it up.
-    // This is to make ctrl-c working from terminal.
-
-    PyOS_sighandler_t sighandler;
-    sighandler = PyOS_getsig(SIGINT);
+    // store ctrl-C handler before Numpy messes it up
+    PyOS_sighandler_t sighandler = PyOS_getsig(SIGINT);
 #endif
 
     PyObject* ba_pymodule = PyImport_ImportModule("bornagain");
 
+#ifndef _WIN32
+    PyOS_setsig(SIGINT, sighandler); // restore previous ctrl-C handler
+#endif
+
     if (!ba_pymodule || !PyModule_Check(ba_pymodule)) {
         checkError();
         throw std::runtime_error(
@@ -600,11 +604,6 @@ PyObjectPtr PyInterpreter::BornAgain::import(const std::string& path)
                              + path + "')"));
     }
 
-    // restores single handler to make ctr-c alive.
-#ifndef _WIN32
-    PyOS_setsig(SIGINT, sighandler);
-#endif
-
     return {ba_pymodule};
 }
 
@@ -635,6 +634,8 @@ PyObjectPtr PyInterpreter::BornAgain::importScript(const std::string& script,
 std::vector<std::string> PyInterpreter::BornAgain::listOfFunctions(const std::string& script,
                                                                    const std::string& path)
 {
+    PyInterpreter::Numpy::initialize();
+
     PyObjectPtr tmpModule{PyInterpreter::BornAgain::importScript(script, path)};
 
     PyObject* pDict = PyModule_GetDict(tmpModule.get());
-- 
GitLab


From 345780833984fe388b6438f5d961718700509cf0 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 11:19:44 +0200
Subject: [PATCH 76/85] no need to clone

---
 GUI/View/Project/PyImportAssistant.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index f360317bb4f..a27b7dc9443 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -107,7 +107,7 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
     PyObjectPtr result{PyInterpreter::createMultiLayerFromPython(
         result_ptr, snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
-    std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr)->clone());
+    std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr));
 
     if (!result.valid()) {
         QApplication::restoreOverrideCursor();
-- 
GitLab


From f37079618d0ecd68ec72efd262cd7460a94efeae Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 17:39:49 +0200
Subject: [PATCH 77/85] expand auto

---
 GUI/View/Project/PyImportAssistant.cpp      | 6 +++---
 GUI/View/SampleDesigner/SampleListModel.cpp | 9 ++++-----
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index a27b7dc9443..db33105cc4a 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -107,7 +107,7 @@ std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QStri
     PyObjectPtr result{PyInterpreter::createMultiLayerFromPython(
         result_ptr, snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
-    std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr));
+    std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr)->clone());
 
     if (!result.valid()) {
         QApplication::restoreOverrideCursor();
@@ -184,9 +184,9 @@ std::unique_ptr<MultiLayer> PyImportAssistant::importMultiLayer()
     if (funcName.isEmpty())
         return {};
 
-    auto sample = createMultiLayer(snippet, funcName);
+    std::unique_ptr<MultiLayer> sample = createMultiLayer(snippet, funcName);
     if (!sample)
-        return {};
+        throw std::runtime_error("Import did not yield MultiLayer object");
 
     if (sample->sampleName() == "Unnamed")
         sample->setSampleName(GUI::Util::Path::baseName(fileName).toStdString());
diff --git a/GUI/View/SampleDesigner/SampleListModel.cpp b/GUI/View/SampleDesigner/SampleListModel.cpp
index 304e0d788ee..fc297ae116c 100644
--- a/GUI/View/SampleDesigner/SampleListModel.cpp
+++ b/GUI/View/SampleDesigner/SampleListModel.cpp
@@ -162,14 +162,13 @@ QModelIndex SampleListModel::createSampleFromExamples(const QString& className,
 #ifdef BORNAGAIN_PYTHON
 QModelIndex SampleListModel::createSampleFromPython()
 {
-    auto sample = PyImportAssistant::importMultiLayer();
+    std::unique_ptr<MultiLayer> sample = PyImportAssistant::importMultiLayer();
     if (!sample)
-        return {}; // any messages already shown to user; no dlg necessary anymore
+        return {}; // any messages already shown to user
 
-
-    auto* sampleItem = PyImportAssistant::itemizeSample(*sample);
+    SampleItem* sampleItem = PyImportAssistant::itemizeSample(*sample);
     if (!sampleItem)
-        return {}; // any messages already shown to user; no dlg necessary anymore
+        return {}; // any messages already shown to user
 
     sampleItem->setDescription("Imported from python code");
 
-- 
GitLab


From d6cb4dc133c4d4a25bd0325891e7399e7e79598e Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 17:53:35 +0200
Subject: [PATCH 78/85] avoid duplicate statement

---
 GUI/View/Project/PyImportAssistant.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index db33105cc4a..44b38b4e114 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -155,14 +155,14 @@ QString getPySampleFunctionName(const QString& snippet)
     std::vector<std::string> funcs_res{
         PyInterpreter::BornAgain::listOfFunctions(snippet.toStdString(), bornagainDir())};
 
+    QApplication::restoreOverrideCursor();
+
     if (funcs_res.empty()) {
-        QApplication::restoreOverrideCursor();
         QString message("Exception thrown while acquiring functions from Python code.\n\n");
         DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, "").exec();
         return "";
     }
 
-    QApplication::restoreOverrideCursor();
     QStringList funcList{GUI::Util::String::fromStdStrings(funcs_res)};
     return selectPySampleFunction(funcList);
 }
-- 
GitLab


From 7efd8db501e4d69a37512b1c06827c7b95cf59aa Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 17:55:10 +0200
Subject: [PATCH 79/85] disambiguate local fct name

---
 GUI/View/Project/PyImportAssistant.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index 44b38b4e114..ffc44cf87aa 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -100,7 +100,7 @@ QString readFile(const QString& fileName)
 //! Creates a multi-layer by executing a funcName in embedded Python.
 //! Function is supposed to be in code provided by 'snippet'.
 
-std::unique_ptr<MultiLayer> createMultiLayer(const QString& snippet, const QString& funcName)
+std::unique_ptr<MultiLayer> code2sample(const QString& snippet, const QString& funcName)
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
     void* result_ptr = nullptr;
@@ -184,7 +184,7 @@ std::unique_ptr<MultiLayer> PyImportAssistant::importMultiLayer()
     if (funcName.isEmpty())
         return {};
 
-    std::unique_ptr<MultiLayer> sample = createMultiLayer(snippet, funcName);
+    std::unique_ptr<MultiLayer> sample = code2sample(snippet, funcName);
     if (!sample)
         throw std::runtime_error("Import did not yield MultiLayer object");
 
-- 
GitLab


From 5349be9379663fa31b39e0a3c76e0a8276991877 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 17:58:57 +0200
Subject: [PATCH 80/85] err msg

---
 PyCore/Sample/ImportMultiLayer.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index 61333b45312..d0f00e36546 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -58,9 +58,7 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
     if (!SWIG_IsOK(res)) {
         Py_DecRef(instance);
         throw std::runtime_error(
-            errorDescription("PyInterpreter::BornAgain: SWIG failed to extract a "
-                             "'MultiLayer' instance via calling the function '"
-                             + functionName + "'"));
+            errorDescription("Call of '" + functionName + "' did not yield MultiLayer instance"));
     }
 
     multilayer_ptr = reinterpret_cast<void*>(argp1);
-- 
GitLab


From 2036ae0b77c76a027c1c91d6b6e95a3563b53a4e Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 18:20:49 +0200
Subject: [PATCH 81/85] simplify result checking and error handling

---
 GUI/View/Project/PyImportAssistant.cpp | 14 +++----------
 PyCore/Embed/PyInterpreter.cpp         |  6 +++---
 PyCore/Sample/ImportMultiLayer.cpp     | 27 ++++++++++++--------------
 PyCore/Sample/ImportMultiLayer.h       |  5 ++---
 4 files changed, 20 insertions(+), 32 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index ffc44cf87aa..67b1703db1a 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -103,21 +103,13 @@ QString readFile(const QString& fileName)
 std::unique_ptr<MultiLayer> code2sample(const QString& snippet, const QString& funcName)
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
-    void* result_ptr = nullptr;
-    PyObjectPtr result{PyInterpreter::createMultiLayerFromPython(
-        result_ptr, snippet.toStdString(), funcName.toStdString(), bornagainDir())};
 
-    std::unique_ptr<MultiLayer> multilayer_ptr(reinterpret_cast<MultiLayer*>(result_ptr)->clone());
-
-    if (!result.valid()) {
-        QApplication::restoreOverrideCursor();
-        QString message("Exception thrown while executing Python code to create sample.\n\n");
-        DetailedMessageBox(GUI::Global::mainWindow, "Python failure", message, "").exec();
-    }
+    void* result_ptr = PyInterpreter::createMultiLayerFromPython(
+        snippet.toStdString(), funcName.toStdString(), bornagainDir());
 
     QApplication::restoreOverrideCursor();
 
-    return multilayer_ptr;
+    return std::unique_ptr<MultiLayer>{reinterpret_cast<MultiLayer*>(result_ptr)->clone()};
 }
 
 //! Lets user select a function name which generates a MultiLayer.
diff --git a/PyCore/Embed/PyInterpreter.cpp b/PyCore/Embed/PyInterpreter.cpp
index 3873f5d8af2..a8a977fc454 100644
--- a/PyCore/Embed/PyInterpreter.cpp
+++ b/PyCore/Embed/PyInterpreter.cpp
@@ -31,8 +31,8 @@ References:
 #include "PyCore/Embed/PyCore.h"
 #undef INCLUDE_NUMPY
 
-#include "PyCore/Embed/PyInterpreter.h"
 #include "Base/Util/Assert.h"
+#include "PyCore/Embed/PyInterpreter.h"
 #include <cstddef>   // NULL
 #include <cstring>   // memcpy
 #include <iostream>  // cerr
@@ -181,7 +181,7 @@ PyObjectPtr PyInterpreter::import(const std::string& pymodule_name, const std::s
 
     PyInterpreter::Numpy::initialize();
 
-    if(!path.empty())
+    if (!path.empty())
         addPythonPath(path);
 
     // import the module
@@ -582,7 +582,7 @@ double* PyInterpreter::Numpy::getDataPtr(PyObject* pyobject_ptr)
 
 PyObjectPtr PyInterpreter::BornAgain::import(const std::string& path)
 {
-    if(!path.empty())
+    if (!path.empty())
         PyInterpreter::addPythonPath(path);
 
 #ifndef _WIN32
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index d0f00e36546..d6f9674de09 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -24,8 +24,8 @@
 
 namespace PyInterpreter {
 
-PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string& script,
-                                       const std::string& functionName, const std::string& path)
+void* createMultiLayerFromPython(const std::string& script, const std::string& functionName,
+                                 const std::string& path)
 {
     PyObjectPtr tmpModule{PyInterpreter::BornAgain::importScript(script, path)};
 
@@ -37,32 +37,29 @@ PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string&
                                                   + functionName + "'"));
 
     // create a `MultiLayer` Python object via calling the function
-    PyObject* instance = PyObject_CallFunctionObjArgs(pAddFn, NULL);
+    PyObject* ret1 = PyObject_CallFunctionObjArgs(pAddFn, NULL);
 
     // clean up
     Py_DecRef(pAddFn);
 
-    if (!instance)
-        throw std::runtime_error(errorDescription(
-            "PyInterpreter::BornAgain: Cannot call the function '" + functionName + "'"));
+    if (!ret1)
+        throw std::runtime_error(
+            errorDescription("Failed execting Python function '" + functionName + "'"));
 
-    // Construct a C++ object from the Python object
-    /* NOTE:
-       This conversion requires a SWIG-produced Python API.
-     */
+    // Construct a C++ object from the Python object.
+    // This conversion requires a SWIG-produced Python API.
 
     void* argp1 = 0;
     swig_type_info* pTypeInfo = SWIG_TypeQuery("MultiLayer *");
 
-    const int res = SWIG_ConvertPtr(instance, &argp1, pTypeInfo, 0);
+    const int res = SWIG_ConvertPtr(ret1, &argp1, pTypeInfo, 0);
     if (!SWIG_IsOK(res)) {
-        Py_DecRef(instance);
+        Py_DecRef(ret1);
         throw std::runtime_error(
-            errorDescription("Call of '" + functionName + "' did not yield MultiLayer instance"));
+            errorDescription("Function " + functionName + " did not yield MultiLayer instance"));
     }
 
-    multilayer_ptr = reinterpret_cast<void*>(argp1);
-    return {instance};
+    return reinterpret_cast<void*>(argp1);
 }
 
 } // namespace PyInterpreter
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 76c06749c31..c8065a3900a 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -32,9 +32,8 @@ namespace PyInterpreter {
 // `MultiLayer` instance in C++ (memory should be managed by the caller).
 //! The returned value is the PyObject corresponding the intermediate _Python_ instance of
 //! of the `MultiLayer` class.
-PyObjectPtr createMultiLayerFromPython(void*& multilayer_ptr, const std::string& script,
-                                       const std::string& functionName,
-                                       const std::string& path = "");
+void* createMultiLayerFromPython(const std::string& script, const std::string& functionName,
+                                 const std::string& path = "");
 
 } // namespace PyInterpreter
 
-- 
GitLab


From 92064858a0ae00f2e11044c1123bb163efc5c3d3 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 18:23:31 +0200
Subject: [PATCH 82/85] rm fwd decl

---
 PyCore/Sample/ImportMultiLayer.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index c8065a3900a..58b30b7ef99 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -21,8 +21,6 @@
 #include "PyCore/Embed/PyObjectPtr.h"
 #include <string>
 
-class MultiLayer;
-
 namespace PyInterpreter {
 
 //! Creates a multi layer by running python code in embedded interpreter, given
-- 
GitLab


From 206e0830514fa460dc603e8fdaf0731586519456 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 18:31:08 +0200
Subject: [PATCH 83/85] PyInterpreter::pyscript2object no longer knows about
 MultiLayer

---
 GUI/View/Project/PyImportAssistant.cpp |  6 +++---
 PyCore/Sample/ImportMultiLayer.cpp     | 16 +++++-----------
 PyCore/Sample/ImportMultiLayer.h       | 12 +++---------
 3 files changed, 11 insertions(+), 23 deletions(-)

diff --git a/GUI/View/Project/PyImportAssistant.cpp b/GUI/View/Project/PyImportAssistant.cpp
index 67b1703db1a..7f03fd1375f 100644
--- a/GUI/View/Project/PyImportAssistant.cpp
+++ b/GUI/View/Project/PyImportAssistant.cpp
@@ -104,12 +104,12 @@ std::unique_ptr<MultiLayer> code2sample(const QString& snippet, const QString& f
 {
     QApplication::setOverrideCursor(Qt::WaitCursor);
 
-    void* result_ptr = PyInterpreter::createMultiLayerFromPython(
-        snippet.toStdString(), funcName.toStdString(), bornagainDir());
+    void* pObj = PyInterpreter::pyscript2object(snippet.toStdString(), funcName.toStdString(),
+                                                "MultiLayer", bornagainDir());
 
     QApplication::restoreOverrideCursor();
 
-    return std::unique_ptr<MultiLayer>{reinterpret_cast<MultiLayer*>(result_ptr)->clone()};
+    return std::unique_ptr<MultiLayer>{reinterpret_cast<MultiLayer*>(pObj)->clone()};
 }
 
 //! Lets user select a function name which generates a MultiLayer.
diff --git a/PyCore/Sample/ImportMultiLayer.cpp b/PyCore/Sample/ImportMultiLayer.cpp
index d6f9674de09..6a35d88beae 100644
--- a/PyCore/Sample/ImportMultiLayer.cpp
+++ b/PyCore/Sample/ImportMultiLayer.cpp
@@ -17,15 +17,11 @@
 #include "PyCore/Sample/ImportMultiLayer.h"
 #include "PyCore/Embed/PyCore.h"
 #include "PyCore/Embed/PyInterpreter.h" // BornAgain::importScript
+#include "auto/Wrap/swig_runtime.h"     // for creating a core object from Python
+#include <stdexcept>
 
-// SWIG runtime access for creating a MultiLayer instance from Python
-#include "auto/Wrap/swig_runtime.h"
-#include <stdexcept> // runtime_error
-
-namespace PyInterpreter {
-
-void* createMultiLayerFromPython(const std::string& script, const std::string& functionName,
-                                 const std::string& path)
+void* PyInterpreter::pyscript2object(const std::string& script, const std::string& functionName,
+                                     const std::string& typeName, const std::string& path)
 {
     PyObjectPtr tmpModule{PyInterpreter::BornAgain::importScript(script, path)};
 
@@ -50,7 +46,7 @@ void* createMultiLayerFromPython(const std::string& script, const std::string& f
     // This conversion requires a SWIG-produced Python API.
 
     void* argp1 = 0;
-    swig_type_info* pTypeInfo = SWIG_TypeQuery("MultiLayer *");
+    swig_type_info* pTypeInfo = SWIG_TypeQuery((typeName + "*").c_str());
 
     const int res = SWIG_ConvertPtr(ret1, &argp1, pTypeInfo, 0);
     if (!SWIG_IsOK(res)) {
@@ -62,6 +58,4 @@ void* createMultiLayerFromPython(const std::string& script, const std::string& f
     return reinterpret_cast<void*>(argp1);
 }
 
-} // namespace PyInterpreter
-
 #endif // BORNAGAIN_PYTHON
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 58b30b7ef99..8e58e4f40d4 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -23,15 +23,9 @@
 
 namespace PyInterpreter {
 
-//! Creates a multi layer by running python code in embedded interpreter, given
-//! The Python-script filename and the name of the function which produces a `MultiLayer`.
-//! A path can be given to import `BornAgain` library (if empty, relies on `PYTHONPATH`).
-//! Finally, if successful, `multilayer_ptr` will be a void pointer to the resulting
-// `MultiLayer` instance in C++ (memory should be managed by the caller).
-//! The returned value is the PyObject corresponding the intermediate _Python_ instance of
-//! of the `MultiLayer` class.
-void* createMultiLayerFromPython(const std::string& script, const std::string& functionName,
-                                 const std::string& path = "");
+//! Creates a core object by running python code in embedded interpreter.
+void* pyscript2object(const std::string& script, const std::string& functionName,
+                      const std::string& typeName, const std::string& path = "");
 
 } // namespace PyInterpreter
 
-- 
GitLab


From 85b372a0cc958511fa5a6f79bb808c70d20b2e22 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 22:15:53 +0200
Subject: [PATCH 84/85] don't include PyCore unless BORNAGAIN_PYTHON is defined

---
 PyCore/Embed/PyCore.h                       | 6 +++---
 PyCore/Embed/PyInterpreter.h                | 3 +++
 PyCore/Embed/PyObjectDecl.h                 | 3 +++
 PyCore/Embed/PyObjectPtr.h                  | 3 +++
 PyCore/Sample/ImportMultiLayer.h            | 3 +++
 Tests/Functional/Consistence/CMakeLists.txt | 1 +
 Tests/Functional/CoreSpecial/CMakeLists.txt | 1 +
 Tests/Functional/Fitting/CMakeLists.txt     | 1 +
 Tests/Suite/GUI/CMakeLists.txt              | 1 +
 Tests/Suite/Persist/CMakeLists.txt          | 1 +
 Tests/Suite/Py/CMakeLists.txt               | 1 +
 Tests/Unit/Device/CMakeLists.txt            | 1 +
 Tests/Unit/GUI/CMakeLists.txt               | 1 +
 Tests/Unit/Numeric/CMakeLists.txt           | 1 +
 Tests/Unit/Sim/CMakeLists.txt               | 1 +
 15 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/PyCore/Embed/PyCore.h b/PyCore/Embed/PyCore.h
index a936b50e17c..a6461ed2494 100644
--- a/PyCore/Embed/PyCore.h
+++ b/PyCore/Embed/PyCore.h
@@ -12,14 +12,15 @@
 //
 //  ************************************************************************************************
 
+#ifndef BORNAGAIN_PYTHON
+#error this header requires Python support
+#endif
 #ifdef SWIG
 #error no need to expose this header to Swig
 #endif
-
 #ifndef BORNAGAIN_PYTOOLS_PYCORE_H
 #define BORNAGAIN_PYTOOLS_PYCORE_H
 
-#ifdef BORNAGAIN_PYTHON
 #undef _POSIX_C_SOURCE
 #undef _XOPEN_SOURCE
 
@@ -52,5 +53,4 @@
 #pragma GCC diagnostic pop
 #endif
 
-#endif // BORNAGAIN_PYTHON
 #endif // BORNAGAIN_PYTOOLS_PYCORE_H
diff --git a/PyCore/Embed/PyInterpreter.h b/PyCore/Embed/PyInterpreter.h
index d4c8267838c..318918a9af7 100644
--- a/PyCore/Embed/PyInterpreter.h
+++ b/PyCore/Embed/PyInterpreter.h
@@ -12,6 +12,9 @@
 //
 //  ************************************************************************************************
 
+#ifndef BORNAGAIN_PYTHON
+#error this header requires Python support
+#endif
 #ifndef BORNAGAIN_PYCORE_EMBED_PYINTERPRETER_H
 #define BORNAGAIN_PYCORE_EMBED_PYINTERPRETER_H
 
diff --git a/PyCore/Embed/PyObjectDecl.h b/PyCore/Embed/PyObjectDecl.h
index a2b52e87e0b..7c778a01786 100644
--- a/PyCore/Embed/PyObjectDecl.h
+++ b/PyCore/Embed/PyObjectDecl.h
@@ -12,6 +12,9 @@
 //
 //  ************************************************************************************************
 
+#ifndef BORNAGAIN_PYTHON
+#error this header requires Python support
+#endif
 #ifndef BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
 #define BORNAGAIN_PYCORE_EMBED_PYOBJECTDECL_H
 
diff --git a/PyCore/Embed/PyObjectPtr.h b/PyCore/Embed/PyObjectPtr.h
index 5e3ff54c48e..10ee85b1a8c 100644
--- a/PyCore/Embed/PyObjectPtr.h
+++ b/PyCore/Embed/PyObjectPtr.h
@@ -12,6 +12,9 @@
 //
 //  ************************************************************************************************
 
+#ifndef BORNAGAIN_PYTHON
+#error this header requires Python support
+#endif
 #ifndef BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 #define BORNAGAIN_PYCORE_EMBED_PYOBJECTPTR_H
 
diff --git a/PyCore/Sample/ImportMultiLayer.h b/PyCore/Sample/ImportMultiLayer.h
index 8e58e4f40d4..d7d3be30da1 100644
--- a/PyCore/Sample/ImportMultiLayer.h
+++ b/PyCore/Sample/ImportMultiLayer.h
@@ -12,6 +12,9 @@
 //
 //  ************************************************************************************************
 
+#ifndef BORNAGAIN_PYTHON
+#error this header requires Python support
+#endif
 #ifdef SWIG
 #error no need to expose this header to Swig
 #endif
diff --git a/Tests/Functional/Consistence/CMakeLists.txt b/Tests/Functional/Consistence/CMakeLists.txt
index e1a35c3b984..46021b1f4ef 100644
--- a/Tests/Functional/Consistence/CMakeLists.txt
+++ b/Tests/Functional/Consistence/CMakeLists.txt
@@ -5,6 +5,7 @@ set(test TestCoreConsistence)
 file(GLOB source_files *.cpp)
 
 add_executable(${test} ${source_files} ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 
 target_include_directories(${test} PUBLIC
     ${BornAgainSim_INCLUDE_DIRS}
diff --git a/Tests/Functional/CoreSpecial/CMakeLists.txt b/Tests/Functional/CoreSpecial/CMakeLists.txt
index 930eec5cb1a..07339ff72f3 100644
--- a/Tests/Functional/CoreSpecial/CMakeLists.txt
+++ b/Tests/Functional/CoreSpecial/CMakeLists.txt
@@ -8,6 +8,7 @@ set(source_files
     )
 
 add_executable(${test} ${source_files} ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_include_directories(${test}
     PUBLIC
     ${CMAKE_SOURCE_DIR}/Tests/Unit/utilities
diff --git a/Tests/Functional/Fitting/CMakeLists.txt b/Tests/Functional/Fitting/CMakeLists.txt
index 4c0b06fa5b5..c014c872a3d 100644
--- a/Tests/Functional/Fitting/CMakeLists.txt
+++ b/Tests/Functional/Fitting/CMakeLists.txt
@@ -24,6 +24,7 @@ set(test TestCoreFitting)
 file(GLOB source_files *.cpp)
 
 add_executable(${test} ${source_files} ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_include_directories(${test}
     PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/common/gtest/gtest-1.8.0/include)
 target_link_libraries(${test} BornAgainTestSimFactory gtest)
diff --git a/Tests/Suite/GUI/CMakeLists.txt b/Tests/Suite/GUI/CMakeLists.txt
index 8492fc6481b..b3a6dd58683 100644
--- a/Tests/Suite/GUI/CMakeLists.txt
+++ b/Tests/Suite/GUI/CMakeLists.txt
@@ -28,6 +28,7 @@ set(test TestGuiSuite)
 set(source_files Check.cpp ../Common/RunTest.cpp ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_compile_options(${test} PUBLIC -DGUI_STD_TEST -DTESTNAME=GUI)
 target_compile_definitions(${test} PUBLIC DISABLE_DISTRIBUTION_TESTS)
 
diff --git a/Tests/Suite/Persist/CMakeLists.txt b/Tests/Suite/Persist/CMakeLists.txt
index 2990110f7dc..f7ebcf6d248 100644
--- a/Tests/Suite/Persist/CMakeLists.txt
+++ b/Tests/Suite/Persist/CMakeLists.txt
@@ -28,6 +28,7 @@ set(test TestSuitePersist)
 set(source_files Check.cpp ../Common/RunTest.cpp ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_compile_definitions(${test} PUBLIC DISABLE_DISTRIBUTION_TESTS -DTESTNAME=Persist)
 target_include_directories(${test} PUBLIC
     ${BornAgainSim_INCLUDE_DIRS}
diff --git a/Tests/Suite/Py/CMakeLists.txt b/Tests/Suite/Py/CMakeLists.txt
index 65fcb717d4c..0fce5374e12 100644
--- a/Tests/Suite/Py/CMakeLists.txt
+++ b/Tests/Suite/Py/CMakeLists.txt
@@ -31,6 +31,7 @@ set(source_files Check.cpp
     ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_compile_options(${test} PUBLIC -DPYTHON_STD_TEST -DTESTNAME=Py)
 target_compile_definitions(${test} PUBLIC DISABLE_DISTRIBUTION_TESTS)
 
diff --git a/Tests/Unit/Device/CMakeLists.txt b/Tests/Unit/Device/CMakeLists.txt
index c9711e1bd45..4af951753db 100644
--- a/Tests/Unit/Device/CMakeLists.txt
+++ b/Tests/Unit/Device/CMakeLists.txt
@@ -5,6 +5,7 @@ set(test UnitTestDevice)
 file(GLOB source_files "*.cpp" ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_link_libraries(${test} BornAgainDevice gtest)
 
 gtest_discover_tests(${test} DISCOVERY_TIMEOUT 300 TEST_PREFIX Unit.Device.)
diff --git a/Tests/Unit/GUI/CMakeLists.txt b/Tests/Unit/GUI/CMakeLists.txt
index 7017c7e7675..9b5ddab89e1 100644
--- a/Tests/Unit/GUI/CMakeLists.txt
+++ b/Tests/Unit/GUI/CMakeLists.txt
@@ -11,6 +11,7 @@ include_directories(${CMAKE_SOURCE_DIR}/Tests/Unit/utilities)
 
 set(CMAKE_AUTOMOC ON)
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_link_libraries(${test} BornAgainGUI gtest Qt6::Core Qt6::Test)
 
 set(test_data_dir ${TEST_OUTPUT_DIR}/Unit/GUI)
diff --git a/Tests/Unit/Numeric/CMakeLists.txt b/Tests/Unit/Numeric/CMakeLists.txt
index c84a1136d29..7dbe1e4e392 100644
--- a/Tests/Unit/Numeric/CMakeLists.txt
+++ b/Tests/Unit/Numeric/CMakeLists.txt
@@ -9,6 +9,7 @@ endif()
 file(GLOB source_files "*.cpp" ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_include_directories(${test} PUBLIC
     ${CMAKE_SOURCE_DIR}/3rdparty/common/gtest/gtest-1.8.0/include
     ${CMAKE_SOURCE_DIR}/Tests/Unit/utilities)
diff --git a/Tests/Unit/Sim/CMakeLists.txt b/Tests/Unit/Sim/CMakeLists.txt
index 4e85ac25487..984b2549076 100644
--- a/Tests/Unit/Sim/CMakeLists.txt
+++ b/Tests/Unit/Sim/CMakeLists.txt
@@ -5,6 +5,7 @@ set(test UnitTestSim)
 file(GLOB source_files "*.cpp" ${CMAKE_SOURCE_DIR}/Tests/GTestWrapper/TestAll.cpp)
 
 add_executable(${test} ${source_files})
+target_compile_definitions(${test} PRIVATE -DBORNAGAIN_PYTHON)
 target_link_libraries(${test} BornAgainSim gtest)
 
 gtest_discover_tests(${test} DISCOVERY_TIMEOUT 300 TEST_PREFIX Unit.Sim.)
-- 
GitLab


From 762362539aa298c74b4034f2db57251819a7494d Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 15 May 2023 22:29:17 +0200
Subject: [PATCH 85/85] restore build with -DBORNAGAIN_PYTHON=OFF -DBA_GUI=OFF

---
 CMakeLists.txt                |  4 +++-
 Device/CMakeLists.txt         |  7 ++++--
 Device/Data/Datafield.h       |  2 ++
 GUI/CMakeLists.txt            |  4 +++-
 cmake/BornAgain/MakeLib.cmake | 45 ++++++++++++++++++-----------------
 5 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b59ddabb0f2..0f30fcec2b9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -262,7 +262,9 @@ if(BA_TESTS)
 endif()
 
 # documentation
-add_subdirectory(hugo)
+if(BORNAGAIN_PYTHON) # required for examples
+    add_subdirectory(hugo)
+endif()
 add_subdirectory(Doc/man)
 if(CONFIGURE_DOXY)
     add_subdirectory(Doc/Doxygen)
diff --git a/Device/CMakeLists.txt b/Device/CMakeLists.txt
index d0159f8470e..61245dd0311 100644
--- a/Device/CMakeLists.txt
+++ b/Device/CMakeLists.txt
@@ -27,14 +27,17 @@ MakeLib(${lib})
 target_link_libraries(${lib}
     PUBLIC
     BornAgainBase
-    BornAgainPyCore
     BornAgainFit
     BornAgainParam
     BornAgainSample
     BornAgainResample
     ${CMAKE_THREAD_LIBS_INIT}
     ${tspectrum_LIBRARY}
-    )
+)
+if(BORNAGAIN_PYTHON)
+    target_link_libraries(${lib} PUBLIC BornAgainPyCore)
+endif()
+
 target_include_directories(${lib}
     PUBLIC
     ${CMAKE_SOURCE_DIR}
diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index 9acb6ba3f72..53e53560465 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -18,7 +18,9 @@
 #include <memory>
 #include <vector>
 
+#ifdef BORNAGAIN_PYTHON
 #include "PyCore/Embed/PyObjectDecl.h"
+#endif
 
 using std::size_t;
 
diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt
index 5c5c40fc54a..f69e8b2b6fc 100644
--- a/GUI/CMakeLists.txt
+++ b/GUI/CMakeLists.txt
@@ -51,7 +51,6 @@ endif()
 
 target_link_libraries(${lib}
     PUBLIC
-    BornAgainPyCore
     BornAgainSim
     BornAgainImg3D
     Qt6::Widgets
@@ -62,6 +61,9 @@ target_link_libraries(${lib}
     Qt6::PrintSupport
     qcustomplot
 )
+if(BORNAGAIN_PYTHON)
+    target_link_libraries(${lib} PUBLIC BornAgainPyCore)
+endif()
 target_include_directories(${lib} PUBLIC
     ${CMAKE_SOURCE_DIR}
     ${CMAKE_SOURCE_DIR}/3rdparty/GUI/qcustomplot
diff --git a/cmake/BornAgain/MakeLib.cmake b/cmake/BornAgain/MakeLib.cmake
index ecf4520b05d..86ad2b519c2 100644
--- a/cmake/BornAgain/MakeLib.cmake
+++ b/cmake/BornAgain/MakeLib.cmake
@@ -64,31 +64,32 @@ function(MakeLib lib)
 
     if(BORNAGAIN_PYTHON)
         target_compile_definitions(${lib} PRIVATE -DBORNAGAIN_PYTHON)
-    endif()
 
-    # SWIG-produced interface
-    if(swigtmpdir)
-        SwigLib(${name} ${lib} ${swigtmpdir})
-    endif()
+        # SWIG-produced interface
+        if(swigtmpdir)
+            SwigLib(${name} ${lib} ${swigtmpdir})
+        endif()
 
-    # Python package
-    if(BA_PY_PACKAGE)
-        # add the BornAgain library to the Python wheel
-        add_library_to_wheel(${lib})
-    endif()
+        # Python package
+        if(BA_PY_PACKAGE)
+            # add the BornAgain library to the Python wheel
+            add_library_to_wheel(${lib})
+        endif()
 
-    if(WIN32 AND BORNAGAIN_PYTHON)
-        # python in windows required .pyd extension for the library name
-        install(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}.pyd
-            DESTINATION ${destination_python} COMPONENT Libraries)
-        add_custom_command(
-            TARGET ${lib}
-            POST_BUILD
-            COMMAND ${CMAKE_COMMAND} -E copy
-            ${CMAKE_BINARY_DIR}/bin/${libprefix}${lib}${libsuffix}
-            ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}${libsuffix}
-        )
-    endif()
+        if(WIN32)
+            # python in windows required .pyd extension for the library name
+            install(FILES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}.pyd
+                DESTINATION ${destination_python} COMPONENT Libraries)
+            add_custom_command(
+                TARGET ${lib}
+                POST_BUILD
+                COMMAND ${CMAKE_COMMAND} -E copy
+                ${CMAKE_BINARY_DIR}/bin/${libprefix}${lib}${libsuffix}
+                ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${libprefix}${lib}${libsuffix}
+            )
+        endif()
+
+    endif() # BORNAGAIN_PYTHON
 
     # installation
     install(TARGETS ${lib}
-- 
GitLab