From 862581e94f9278427c201b28a85463726263067b Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Fri, 25 Aug 2023 14:12:54 +0200
Subject: [PATCH] Frame + m_trafos

---
 Base/Axis/Frame.cpp                 |  49 ++-
 Base/Axis/Frame.h                   |  14 +-
 Wrap/Swig/libBornAgainBase.i        |   1 +
 auto/Wrap/libBornAgainBase.py       |  22 +-
 auto/Wrap/libBornAgainBase_wrap.cpp | 445 +++++++++++++++++++++++-----
 5 files changed, 458 insertions(+), 73 deletions(-)

diff --git a/Base/Axis/Frame.cpp b/Base/Axis/Frame.cpp
index dcd2ade4f7b..5a8107f91b3 100644
--- a/Base/Axis/Frame.cpp
+++ b/Base/Axis/Frame.cpp
@@ -13,16 +13,24 @@
 //  ************************************************************************************************
 
 #include "Base/Axis/Frame.h"
+#include "Base/Axis/FrameTrafo.h"
 #include "Base/Axis/FrameUtil.h"
 #include "Base/Axis/Scale.h"
 #include "Base/Util/Assert.h"
+#include "Base/Util/StringUtil.h"
 
-Frame::Frame(CloneableVector<const Scale> axes)
+Frame::Frame(CloneableVector<const Scale> axes, CloneableVector<const FrameTrafo> trafos)
     : m_axes(std::move(axes))
+    , m_trafos(std::move(trafos))
     , m_size(FrameUtil::product_size(m_axes.reference()))
 {
 }
 
+Frame::Frame(CloneableVector<const Scale> axes)
+    : Frame(axes, {})
+{
+}
+
 Frame::Frame(const Scale* ax0)
     : Frame(std::vector<const Scale*>{ax0})
 {
@@ -42,6 +50,26 @@ Frame* Frame::clone() const
     return new Frame(*this);
 }
 
+void Frame::setAxes(CloneableVector<const Scale> axes)
+{
+    m_axes.clear();
+    m_axes = std::move(axes);
+}
+
+void Frame::addTrafo(FrameTrafo* trafo)
+{
+    m_trafos.emplace_back(trafo);
+}
+
+const FrameTrafo& Frame::findTrafo(const std::string& name) const
+{
+    for (const FrameTrafo* trafo : m_trafos)
+        if (trafo->name == name)
+            return *trafo;
+    throw std::runtime_error("Requested unavailable trafo '" + name
+                             + "' ; available: " + Base::String::join(availableTrafos(), ", "));
+}
+
 size_t Frame::rank() const
 {
     return m_axes.size();
@@ -140,6 +168,25 @@ bool Frame::hasSameSizes(const Frame& o) const
     return true;
 }
 
+std::vector<std::string> Frame::availableTrafos() const
+{
+    std::vector<std::string> result;
+    for (const FrameTrafo* trafo : m_trafos)
+        result.push_back(trafo->name);
+    return result;
+}
+
+Frame* Frame::plottableFrame(const FrameTrafo& trafo) const
+{
+    ASSERT(trafo.coords.size() == rank());
+    std::vector<const Scale*> outaxes;
+    for (size_t k = 0; k < rank(); ++k) {
+        Scale* s = new Scale(axis(k).plottableScale(trafo.coords[k], trafo.axTrafos[k]));
+        outaxes.emplace_back(s);
+    }
+    return new Frame(std::move(outaxes));
+}
+
 Frame* Frame::plottableFrame(std::vector<std::string> labels) const
 {
     ASSERT(labels.size() == rank() || labels.size() == 0);
diff --git a/Base/Axis/Frame.h b/Base/Axis/Frame.h
index 61293c5fcae..5993826738e 100644
--- a/Base/Axis/Frame.h
+++ b/Base/Axis/Frame.h
@@ -20,6 +20,7 @@
 
 using std::size_t;
 
+class FrameTrafo;
 class Scale;
 
 //! Holds one or two axes.
@@ -27,13 +28,18 @@ class Scale;
 class Frame {
 public:
     //! Constructor that takes ownership of supplied axes.
+    Frame(CloneableVector<const Scale> axes, CloneableVector<const FrameTrafo> trafos);
     Frame(CloneableVector<const Scale> axes);
     Frame(const Scale* ax0);
     Frame(const Scale* ax0, const Scale* ax1);
     Frame(const Frame&);
-
     ~Frame();
 
+    void setAxes(CloneableVector<const Scale> axes);
+
+    void addTrafo(FrameTrafo* trafo);
+    const FrameTrafo& findTrafo(const std::string& name) const;
+
     Frame* clone() const;
 
     //! Returns number of dimensions.
@@ -83,12 +89,14 @@ public:
 #endif // SWIG
 
     Frame* plottableFrame(std::vector<std::string> labels = {}) const;
+    Frame* plottableFrame(const FrameTrafo& trafo) const;
     Frame* flat() const;
 
-protected:
-    CloneableVector<const Scale> m_axes;
+    std::vector<std::string> availableTrafos() const;
 
 private:
+    CloneableVector<const Scale> m_axes;
+    CloneableVector<const FrameTrafo> m_trafos;
     size_t m_size; // cached product of axis sizes
 };
 
diff --git a/Wrap/Swig/libBornAgainBase.i b/Wrap/Swig/libBornAgainBase.i
index d0f3842e9fc..74c0d61f292 100644
--- a/Wrap/Swig/libBornAgainBase.i
+++ b/Wrap/Swig/libBornAgainBase.i
@@ -20,6 +20,7 @@
 #include <heinz/Complex.h>
 #include "Base/Axis/Bin.h"
 #include "Base/Axis/Frame.h"
+#include "Base/Axis/FrameTrafo.h"
 #include "Base/Axis/Scale.h"
 #include "Base/Axis/MakeScale.h"
 #include "Base/Const/Units.h"
diff --git a/auto/Wrap/libBornAgainBase.py b/auto/Wrap/libBornAgainBase.py
index 08579f96532..0087fa2b2c7 100644
--- a/auto/Wrap/libBornAgainBase.py
+++ b/auto/Wrap/libBornAgainBase.py
@@ -1986,6 +1986,7 @@ class Frame(object):
 
     def __init__(self, *args):
         r"""
+        __init__(Frame self, CloneableVector< Scale const > axes, CloneableVector< FrameTrafo const > trafos) -> Frame
         __init__(Frame self, CloneableVector< Scale const > axes) -> Frame
         __init__(Frame self, Scale ax0) -> Frame
         __init__(Frame self, Scale ax0, Scale ax1) -> Frame
@@ -1994,6 +1995,18 @@ class Frame(object):
         _libBornAgainBase.Frame_swiginit(self, _libBornAgainBase.new_Frame(*args))
     __swig_destroy__ = _libBornAgainBase.delete_Frame
 
+    def setAxes(self, axes):
+        r"""setAxes(Frame self, CloneableVector< Scale const > axes)"""
+        return _libBornAgainBase.Frame_setAxes(self, axes)
+
+    def addTrafo(self, trafo):
+        r"""addTrafo(Frame self, FrameTrafo * trafo)"""
+        return _libBornAgainBase.Frame_addTrafo(self, trafo)
+
+    def findTrafo(self, name):
+        r"""findTrafo(Frame self, std::string const & name) -> FrameTrafo const &"""
+        return _libBornAgainBase.Frame_findTrafo(self, name)
+
     def clone(self):
         r"""clone(Frame self) -> Frame"""
         return _libBornAgainBase.Frame_clone(self)
@@ -2047,13 +2060,20 @@ class Frame(object):
         return _libBornAgainBase.Frame___eq__(self, arg2)
 
     def plottableFrame(self, *args):
-        r"""plottableFrame(Frame self, vector_string_t labels={}) -> Frame"""
+        r"""
+        plottableFrame(Frame self, vector_string_t labels={}) -> Frame
+        plottableFrame(Frame self, FrameTrafo const & trafo) -> Frame
+        """
         return _libBornAgainBase.Frame_plottableFrame(self, *args)
 
     def flat(self):
         r"""flat(Frame self) -> Frame"""
         return _libBornAgainBase.Frame_flat(self)
 
+    def availableTrafos(self):
+        r"""availableTrafos(Frame self) -> vector_string_t"""
+        return _libBornAgainBase.Frame_availableTrafos(self)
+
 # Register Frame in _libBornAgainBase:
 _libBornAgainBase.Frame_swigregister(Frame)
 class R3(object):
diff --git a/auto/Wrap/libBornAgainBase_wrap.cpp b/auto/Wrap/libBornAgainBase_wrap.cpp
index 7b565d0d236..899eacc5e51 100644
--- a/auto/Wrap/libBornAgainBase_wrap.cpp
+++ b/auto/Wrap/libBornAgainBase_wrap.cpp
@@ -3387,65 +3387,67 @@ namespace Swig {
 /* -------- TYPES TABLE (BEGIN) -------- */
 
 #define SWIGTYPE_p_Bin1D swig_types[0]
-#define SWIGTYPE_p_CloneableVectorT_Scale_const_t swig_types[1]
-#define SWIGTYPE_p_Coordinate swig_types[2]
-#define SWIGTYPE_p_Frame swig_types[3]
-#define SWIGTYPE_p_ICloneable swig_types[4]
-#define SWIGTYPE_p_Rotation3DT_double_t swig_types[5]
-#define SWIGTYPE_p_Scale swig_types[6]
-#define SWIGTYPE_p_Span swig_types[7]
-#define SWIGTYPE_p_ThreadInfo swig_types[8]
-#define SWIGTYPE_p_Vec3T_double_t swig_types[9]
-#define SWIGTYPE_p_Vec3T_int_t swig_types[10]
-#define SWIGTYPE_p_Vec3T_std__complexT_double_t_t swig_types[11]
-#define SWIGTYPE_p_allocator_type swig_types[12]
-#define SWIGTYPE_p_char swig_types[13]
-#define SWIGTYPE_p_difference_type swig_types[14]
-#define SWIGTYPE_p_first_type swig_types[15]
-#define SWIGTYPE_p_int swig_types[16]
-#define SWIGTYPE_p_key_type swig_types[17]
-#define SWIGTYPE_p_long_long swig_types[18]
-#define SWIGTYPE_p_mapped_type swig_types[19]
-#define SWIGTYPE_p_p_PyObject swig_types[20]
-#define SWIGTYPE_p_second_type swig_types[21]
-#define SWIGTYPE_p_short swig_types[22]
-#define SWIGTYPE_p_signed_char swig_types[23]
-#define SWIGTYPE_p_size_type swig_types[24]
-#define SWIGTYPE_p_std__allocatorT_double_t swig_types[25]
-#define SWIGTYPE_p_std__allocatorT_int_t swig_types[26]
-#define SWIGTYPE_p_std__allocatorT_std__complexT_double_t_t swig_types[27]
-#define SWIGTYPE_p_std__allocatorT_std__pairT_double_double_t_t swig_types[28]
-#define SWIGTYPE_p_std__allocatorT_std__pairT_std__string_const_double_t_t swig_types[29]
-#define SWIGTYPE_p_std__allocatorT_std__string_t swig_types[30]
-#define SWIGTYPE_p_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t swig_types[31]
-#define SWIGTYPE_p_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t swig_types[32]
-#define SWIGTYPE_p_std__allocatorT_unsigned_long_t swig_types[33]
-#define SWIGTYPE_p_std__complexT_double_t swig_types[34]
-#define SWIGTYPE_p_std__functionT_double_fdoubleF_t swig_types[35]
-#define SWIGTYPE_p_std__invalid_argument swig_types[36]
-#define SWIGTYPE_p_std__lessT_std__string_t swig_types[37]
-#define SWIGTYPE_p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t swig_types[38]
-#define SWIGTYPE_p_std__optionalT_Bin1D_t swig_types[39]
-#define SWIGTYPE_p_std__pairT_double_double_t swig_types[40]
-#define SWIGTYPE_p_std__vectorT_Bin1D_std__allocatorT_Bin1D_t_t swig_types[41]
-#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[42]
-#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[43]
-#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[44]
-#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[45]
-#define SWIGTYPE_p_std__vectorT_std__pairT_std__string_std__functionT_double_fdoubleF_t_t_std__allocatorT_std__pairT_std__string_std__functionT_double_fdoubleF_t_t_t_t swig_types[46]
-#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[47]
-#define SWIGTYPE_p_std__vectorT_std__vectorT_double_std__allocatorT_double_t_t_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t_t swig_types[48]
-#define SWIGTYPE_p_std__vectorT_std__vectorT_int_std__allocatorT_int_t_t_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t_t swig_types[49]
-#define SWIGTYPE_p_std__vectorT_unsigned_int_std__allocatorT_unsigned_int_t_t swig_types[50]
-#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[51]
-#define SWIGTYPE_p_swig__SwigPyIterator swig_types[52]
-#define SWIGTYPE_p_unsigned_char swig_types[53]
-#define SWIGTYPE_p_unsigned_int swig_types[54]
-#define SWIGTYPE_p_unsigned_long_long swig_types[55]
-#define SWIGTYPE_p_unsigned_short swig_types[56]
-#define SWIGTYPE_p_value_type swig_types[57]
-static swig_type_info *swig_types[59];
-static swig_module_info swig_module = {swig_types, 58, 0, 0, 0, 0};
+#define SWIGTYPE_p_CloneableVectorT_FrameTrafo_const_t swig_types[1]
+#define SWIGTYPE_p_CloneableVectorT_Scale_const_t swig_types[2]
+#define SWIGTYPE_p_Coordinate swig_types[3]
+#define SWIGTYPE_p_Frame swig_types[4]
+#define SWIGTYPE_p_FrameTrafo swig_types[5]
+#define SWIGTYPE_p_ICloneable swig_types[6]
+#define SWIGTYPE_p_Rotation3DT_double_t swig_types[7]
+#define SWIGTYPE_p_Scale swig_types[8]
+#define SWIGTYPE_p_Span swig_types[9]
+#define SWIGTYPE_p_ThreadInfo swig_types[10]
+#define SWIGTYPE_p_Vec3T_double_t swig_types[11]
+#define SWIGTYPE_p_Vec3T_int_t swig_types[12]
+#define SWIGTYPE_p_Vec3T_std__complexT_double_t_t swig_types[13]
+#define SWIGTYPE_p_allocator_type swig_types[14]
+#define SWIGTYPE_p_char swig_types[15]
+#define SWIGTYPE_p_difference_type swig_types[16]
+#define SWIGTYPE_p_first_type swig_types[17]
+#define SWIGTYPE_p_int swig_types[18]
+#define SWIGTYPE_p_key_type swig_types[19]
+#define SWIGTYPE_p_long_long swig_types[20]
+#define SWIGTYPE_p_mapped_type swig_types[21]
+#define SWIGTYPE_p_p_PyObject swig_types[22]
+#define SWIGTYPE_p_second_type swig_types[23]
+#define SWIGTYPE_p_short swig_types[24]
+#define SWIGTYPE_p_signed_char swig_types[25]
+#define SWIGTYPE_p_size_type swig_types[26]
+#define SWIGTYPE_p_std__allocatorT_double_t swig_types[27]
+#define SWIGTYPE_p_std__allocatorT_int_t swig_types[28]
+#define SWIGTYPE_p_std__allocatorT_std__complexT_double_t_t swig_types[29]
+#define SWIGTYPE_p_std__allocatorT_std__pairT_double_double_t_t swig_types[30]
+#define SWIGTYPE_p_std__allocatorT_std__pairT_std__string_const_double_t_t swig_types[31]
+#define SWIGTYPE_p_std__allocatorT_std__string_t swig_types[32]
+#define SWIGTYPE_p_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t swig_types[33]
+#define SWIGTYPE_p_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t swig_types[34]
+#define SWIGTYPE_p_std__allocatorT_unsigned_long_t swig_types[35]
+#define SWIGTYPE_p_std__complexT_double_t swig_types[36]
+#define SWIGTYPE_p_std__functionT_double_fdoubleF_t swig_types[37]
+#define SWIGTYPE_p_std__invalid_argument swig_types[38]
+#define SWIGTYPE_p_std__lessT_std__string_t swig_types[39]
+#define SWIGTYPE_p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t swig_types[40]
+#define SWIGTYPE_p_std__optionalT_Bin1D_t swig_types[41]
+#define SWIGTYPE_p_std__pairT_double_double_t swig_types[42]
+#define SWIGTYPE_p_std__vectorT_Bin1D_std__allocatorT_Bin1D_t_t swig_types[43]
+#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[44]
+#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[45]
+#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[46]
+#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[47]
+#define SWIGTYPE_p_std__vectorT_std__pairT_std__string_std__functionT_double_fdoubleF_t_t_std__allocatorT_std__pairT_std__string_std__functionT_double_fdoubleF_t_t_t_t swig_types[48]
+#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[49]
+#define SWIGTYPE_p_std__vectorT_std__vectorT_double_std__allocatorT_double_t_t_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t_t swig_types[50]
+#define SWIGTYPE_p_std__vectorT_std__vectorT_int_std__allocatorT_int_t_t_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t_t swig_types[51]
+#define SWIGTYPE_p_std__vectorT_unsigned_int_std__allocatorT_unsigned_int_t_t swig_types[52]
+#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[53]
+#define SWIGTYPE_p_swig__SwigPyIterator swig_types[54]
+#define SWIGTYPE_p_unsigned_char swig_types[55]
+#define SWIGTYPE_p_unsigned_int swig_types[56]
+#define SWIGTYPE_p_unsigned_long_long swig_types[57]
+#define SWIGTYPE_p_unsigned_short swig_types[58]
+#define SWIGTYPE_p_value_type swig_types[59]
+static swig_type_info *swig_types[61];
+static swig_module_info swig_module = {swig_types, 60, 0, 0, 0, 0};
 #define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
 #define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
 
@@ -6988,6 +6990,7 @@ SWIGINTERN void std_vector_Sl_std_pair_Sl_double_Sc_double_Sg__Sg__insert__SWIG_
 #include <heinz/Complex.h>
 #include "Base/Axis/Bin.h"
 #include "Base/Axis/Frame.h"
+#include "Base/Axis/FrameTrafo.h"
 #include "Base/Axis/Scale.h"
 #include "Base/Axis/MakeScale.h"
 #include "Base/Const/Units.h"
@@ -27245,6 +27248,61 @@ fail:
 
 
 SWIGINTERN PyObject *_wrap_new_Frame__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+  PyObject *resultobj = 0;
+  SwigValueWrapper< CloneableVector< Scale const > > arg1 ;
+  SwigValueWrapper< CloneableVector< FrameTrafo const > > arg2 ;
+  void *argp1 ;
+  int res1 = 0 ;
+  void *argp2 ;
+  int res2 = 0 ;
+  Frame *result = 0 ;
+  
+  if ((nobjs < 2) || (nobjs > 2)) SWIG_fail;
+  {
+    res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_CloneableVectorT_Scale_const_t,  0  | 0);
+    if (!SWIG_IsOK(res1)) {
+      SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "new_Frame" "', argument " "1"" of type '" "CloneableVector< Scale const >""'"); 
+    }  
+    if (!argp1) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "new_Frame" "', argument " "1"" of type '" "CloneableVector< Scale const >""'");
+    } else {
+      CloneableVector< Scale const > * temp = reinterpret_cast< CloneableVector< Scale const > * >(argp1);
+      arg1 = *temp;
+      if (SWIG_IsNewObj(res1)) delete temp;
+    }
+  }
+  {
+    res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_CloneableVectorT_FrameTrafo_const_t,  0  | 0);
+    if (!SWIG_IsOK(res2)) {
+      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "new_Frame" "', argument " "2"" of type '" "CloneableVector< FrameTrafo const >""'"); 
+    }  
+    if (!argp2) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "new_Frame" "', argument " "2"" of type '" "CloneableVector< FrameTrafo const >""'");
+    } else {
+      CloneableVector< FrameTrafo const > * temp = reinterpret_cast< CloneableVector< FrameTrafo const > * >(argp2);
+      arg2 = *temp;
+      if (SWIG_IsNewObj(res2)) delete temp;
+    }
+  }
+  {
+    try {
+      result = (Frame *)new Frame(arg1,arg2);
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Frame, SWIG_POINTER_NEW |  0 );
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_Frame__SWIG_1(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   SwigValueWrapper< CloneableVector< Scale const > > arg1 ;
   void *argp1 ;
@@ -27283,7 +27341,7 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_new_Frame__SWIG_1(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+SWIGINTERN PyObject *_wrap_new_Frame__SWIG_2(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   Scale *arg1 = (Scale *) 0 ;
   void *argp1 = 0 ;
@@ -27314,7 +27372,7 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_new_Frame__SWIG_2(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+SWIGINTERN PyObject *_wrap_new_Frame__SWIG_3(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   Scale *arg1 = (Scale *) 0 ;
   Scale *arg2 = (Scale *) 0 ;
@@ -27353,7 +27411,7 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_new_Frame__SWIG_3(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+SWIGINTERN PyObject *_wrap_new_Frame__SWIG_4(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   Frame *arg1 = 0 ;
   void *argp1 = 0 ;
@@ -27400,7 +27458,7 @@ SWIGINTERN PyObject *_wrap_new_Frame(PyObject *self, PyObject *args) {
     int res = SWIG_ConvertPtr(argv[0], 0, SWIGTYPE_p_CloneableVectorT_Scale_const_t, SWIG_POINTER_NO_NULL | 0);
     _v = SWIG_CheckState(res);
     if (_v) {
-      return _wrap_new_Frame__SWIG_0(self, argc, argv);
+      return _wrap_new_Frame__SWIG_1(self, argc, argv);
     }
   }
   if (argc == 1) {
@@ -27409,7 +27467,7 @@ SWIGINTERN PyObject *_wrap_new_Frame(PyObject *self, PyObject *args) {
     int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_Scale, 0);
     _v = SWIG_CheckState(res);
     if (_v) {
-      return _wrap_new_Frame__SWIG_1(self, argc, argv);
+      return _wrap_new_Frame__SWIG_2(self, argc, argv);
     }
   }
   if (argc == 1) {
@@ -27417,7 +27475,7 @@ SWIGINTERN PyObject *_wrap_new_Frame(PyObject *self, PyObject *args) {
     int res = SWIG_ConvertPtr(argv[0], 0, SWIGTYPE_p_Frame, SWIG_POINTER_NO_NULL | 0);
     _v = SWIG_CheckState(res);
     if (_v) {
-      return _wrap_new_Frame__SWIG_3(self, argc, argv);
+      return _wrap_new_Frame__SWIG_4(self, argc, argv);
     }
   }
   if (argc == 2) {
@@ -27430,7 +27488,19 @@ SWIGINTERN PyObject *_wrap_new_Frame(PyObject *self, PyObject *args) {
       int res = SWIG_ConvertPtr(argv[1], &vptr, SWIGTYPE_p_Scale, 0);
       _v = SWIG_CheckState(res);
       if (_v) {
-        return _wrap_new_Frame__SWIG_2(self, argc, argv);
+        return _wrap_new_Frame__SWIG_3(self, argc, argv);
+      }
+    }
+  }
+  if (argc == 2) {
+    int _v = 0;
+    int res = SWIG_ConvertPtr(argv[0], 0, SWIGTYPE_p_CloneableVectorT_Scale_const_t, SWIG_POINTER_NO_NULL | 0);
+    _v = SWIG_CheckState(res);
+    if (_v) {
+      int res = SWIG_ConvertPtr(argv[1], 0, SWIGTYPE_p_CloneableVectorT_FrameTrafo_const_t, SWIG_POINTER_NO_NULL | 0);
+      _v = SWIG_CheckState(res);
+      if (_v) {
+        return _wrap_new_Frame__SWIG_0(self, argc, argv);
       }
     }
   }
@@ -27438,6 +27508,7 @@ SWIGINTERN PyObject *_wrap_new_Frame(PyObject *self, PyObject *args) {
 fail:
   SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'new_Frame'.\n"
     "  Possible C/C++ prototypes are:\n"
+    "    Frame::Frame(CloneableVector< Scale const >,CloneableVector< FrameTrafo const >)\n"
     "    Frame::Frame(CloneableVector< Scale const >)\n"
     "    Frame::Frame(Scale const *)\n"
     "    Frame::Frame(Scale const *,Scale const *)\n"
@@ -27478,6 +27549,139 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Frame_setAxes(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Frame *arg1 = (Frame *) 0 ;
+  SwigValueWrapper< CloneableVector< Scale const > > arg2 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Frame_setAxes", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Frame, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Frame_setAxes" "', argument " "1"" of type '" "Frame *""'"); 
+  }
+  arg1 = reinterpret_cast< Frame * >(argp1);
+  {
+    res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_CloneableVectorT_Scale_const_t,  0  | 0);
+    if (!SWIG_IsOK(res2)) {
+      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Frame_setAxes" "', argument " "2"" of type '" "CloneableVector< Scale const >""'"); 
+    }  
+    if (!argp2) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "Frame_setAxes" "', argument " "2"" of type '" "CloneableVector< Scale const >""'");
+    } else {
+      CloneableVector< Scale const > * temp = reinterpret_cast< CloneableVector< Scale const > * >(argp2);
+      arg2 = *temp;
+      if (SWIG_IsNewObj(res2)) delete temp;
+    }
+  }
+  {
+    try {
+      (arg1)->setAxes(arg2);
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = SWIG_Py_Void();
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_Frame_addTrafo(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Frame *arg1 = (Frame *) 0 ;
+  FrameTrafo *arg2 = (FrameTrafo *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  PyObject *swig_obj[2] ;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Frame_addTrafo", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Frame, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Frame_addTrafo" "', argument " "1"" of type '" "Frame *""'"); 
+  }
+  arg1 = reinterpret_cast< Frame * >(argp1);
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2,SWIGTYPE_p_FrameTrafo, 0 |  0 );
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Frame_addTrafo" "', argument " "2"" of type '" "FrameTrafo *""'"); 
+  }
+  arg2 = reinterpret_cast< FrameTrafo * >(argp2);
+  {
+    try {
+      (arg1)->addTrafo(arg2);
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = SWIG_Py_Void();
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_Frame_findTrafo(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Frame *arg1 = (Frame *) 0 ;
+  std::string *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  int res2 = SWIG_OLDOBJ ;
+  PyObject *swig_obj[2] ;
+  FrameTrafo *result = 0 ;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Frame_findTrafo", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Frame, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Frame_findTrafo" "', argument " "1"" of type '" "Frame const *""'"); 
+  }
+  arg1 = reinterpret_cast< Frame * >(argp1);
+  {
+    std::string *ptr = (std::string *)0;
+    res2 = SWIG_AsPtr_std_string(swig_obj[1], &ptr);
+    if (!SWIG_IsOK(res2)) {
+      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Frame_findTrafo" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    if (!ptr) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "Frame_findTrafo" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    arg2 = ptr;
+  }
+  {
+    try {
+      result = (FrameTrafo *) &((Frame const *)arg1)->findTrafo((std::string const &)*arg2);
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_FrameTrafo, 0 |  0 );
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return resultobj;
+fail:
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Frame_clone(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   Frame *arg1 = (Frame *) 0 ;
@@ -28062,6 +28266,48 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Frame_plottableFrame__SWIG_2(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
+  PyObject *resultobj = 0;
+  Frame *arg1 = (Frame *) 0 ;
+  FrameTrafo *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  void *argp2 = 0 ;
+  int res2 = 0 ;
+  Frame *result = 0 ;
+  
+  if ((nobjs < 2) || (nobjs > 2)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Frame, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Frame_plottableFrame" "', argument " "1"" of type '" "Frame const *""'"); 
+  }
+  arg1 = reinterpret_cast< Frame * >(argp1);
+  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_FrameTrafo,  0  | 0);
+  if (!SWIG_IsOK(res2)) {
+    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Frame_plottableFrame" "', argument " "2"" of type '" "FrameTrafo const &""'"); 
+  }
+  if (!argp2) {
+    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "Frame_plottableFrame" "', argument " "2"" of type '" "FrameTrafo const &""'"); 
+  }
+  arg2 = reinterpret_cast< FrameTrafo * >(argp2);
+  {
+    try {
+      result = (Frame *)((Frame const *)arg1)->plottableFrame((FrameTrafo const &)*arg2);
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Frame, 0 |  0 );
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Frame_plottableFrame(PyObject *self, PyObject *args) {
   Py_ssize_t argc;
   PyObject *argv[3] = {
@@ -28079,6 +28325,19 @@ SWIGINTERN PyObject *_wrap_Frame_plottableFrame(PyObject *self, PyObject *args)
       return _wrap_Frame_plottableFrame__SWIG_1(self, argc, argv);
     }
   }
+  if (argc == 2) {
+    int _v = 0;
+    void *vptr = 0;
+    int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_Frame, 0);
+    _v = SWIG_CheckState(res);
+    if (_v) {
+      int res = SWIG_ConvertPtr(argv[1], 0, SWIGTYPE_p_FrameTrafo, SWIG_POINTER_NO_NULL | 0);
+      _v = SWIG_CheckState(res);
+      if (_v) {
+        return _wrap_Frame_plottableFrame__SWIG_2(self, argc, argv);
+      }
+    }
+  }
   if (argc == 2) {
     int _v = 0;
     void *vptr = 0;
@@ -28097,7 +28356,8 @@ fail:
   SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'Frame_plottableFrame'.\n"
     "  Possible C/C++ prototypes are:\n"
     "    Frame::plottableFrame(std::vector< std::string,std::allocator< std::string > >) const\n"
-    "    Frame::plottableFrame() const\n");
+    "    Frame::plottableFrame() const\n"
+    "    Frame::plottableFrame(FrameTrafo const &) const\n");
   return 0;
 }
 
@@ -28135,6 +28395,39 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Frame_availableTrafos(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Frame *arg1 = (Frame *) 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  PyObject *swig_obj[1] ;
+  std::vector< std::string,std::allocator< std::string > > result;
+  
+  if (!args) SWIG_fail;
+  swig_obj[0] = args;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Frame, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Frame_availableTrafos" "', argument " "1"" of type '" "Frame const *""'"); 
+  }
+  arg1 = reinterpret_cast< Frame * >(argp1);
+  {
+    try {
+      result = ((Frame const *)arg1)->availableTrafos();
+    } catch (const std::exception& ex) {
+      // message shown in the Python interpreter
+      const std::string msg {
+        "BornAgain C++ Exception: " + std::string(ex.what())
+      };
+      SWIG_exception(SWIG_RuntimeError, msg.c_str());
+    }
+  }
+  resultobj = swig::from(static_cast< std::vector< std::string,std::allocator< std::string > > >(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *Frame_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *obj;
   if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
@@ -30674,12 +30967,16 @@ static PyMethodDef SwigMethods[] = {
 	 { "EquiDivision", _wrap_EquiDivision, METH_VARARGS, "EquiDivision(std::string name, size_t N, double start, double end) -> Scale"},
 	 { "EquiScan", _wrap_EquiScan, METH_VARARGS, "EquiScan(std::string name, size_t N, double start, double end) -> Scale"},
 	 { "new_Frame", _wrap_new_Frame, METH_VARARGS, "\n"
+		"Frame(CloneableVector< Scale const > axes, CloneableVector< FrameTrafo const > trafos)\n"
 		"Frame(CloneableVector< Scale const > axes)\n"
 		"Frame(Scale ax0)\n"
 		"Frame(Scale ax0, Scale ax1)\n"
 		"new_Frame(Frame arg1) -> Frame\n"
 		""},
 	 { "delete_Frame", _wrap_delete_Frame, METH_O, "delete_Frame(Frame self)"},
+	 { "Frame_setAxes", _wrap_Frame_setAxes, METH_VARARGS, "Frame_setAxes(Frame self, CloneableVector< Scale const > axes)"},
+	 { "Frame_addTrafo", _wrap_Frame_addTrafo, METH_VARARGS, "Frame_addTrafo(Frame self, FrameTrafo * trafo)"},
+	 { "Frame_findTrafo", _wrap_Frame_findTrafo, METH_VARARGS, "Frame_findTrafo(Frame self, std::string const & name) -> FrameTrafo const &"},
 	 { "Frame_clone", _wrap_Frame_clone, METH_O, "Frame_clone(Frame self) -> Frame"},
 	 { "Frame_rank", _wrap_Frame_rank, METH_O, "Frame_rank(Frame self) -> size_t"},
 	 { "Frame_size", _wrap_Frame_size, METH_O, "Frame_size(Frame self) -> size_t"},
@@ -30693,8 +30990,12 @@ static PyMethodDef SwigMethods[] = {
 	 { "Frame_toGlobalIndex", _wrap_Frame_toGlobalIndex, METH_VARARGS, "Frame_toGlobalIndex(Frame self, std::vector< unsigned int,std::allocator< unsigned int > > const & axes_indices) -> size_t"},
 	 { "Frame_hasSameSizes", _wrap_Frame_hasSameSizes, METH_VARARGS, "Frame_hasSameSizes(Frame self, Frame arg2) -> bool"},
 	 { "Frame___eq__", _wrap_Frame___eq__, METH_VARARGS, "Frame___eq__(Frame self, Frame arg2) -> bool"},
-	 { "Frame_plottableFrame", _wrap_Frame_plottableFrame, METH_VARARGS, "Frame_plottableFrame(Frame self, vector_string_t labels={}) -> Frame"},
+	 { "Frame_plottableFrame", _wrap_Frame_plottableFrame, METH_VARARGS, "\n"
+		"Frame_plottableFrame(Frame self, vector_string_t labels={}) -> Frame\n"
+		"Frame_plottableFrame(Frame self, FrameTrafo const & trafo) -> Frame\n"
+		""},
 	 { "Frame_flat", _wrap_Frame_flat, METH_O, "Frame_flat(Frame self) -> Frame"},
+	 { "Frame_availableTrafos", _wrap_Frame_availableTrafos, METH_O, "Frame_availableTrafos(Frame self) -> vector_string_t"},
 	 { "Frame_swigregister", Frame_swigregister, METH_O, NULL},
 	 { "Frame_swiginit", Frame_swiginit, METH_VARARGS, NULL},
 	 { "new_R3", _wrap_new_R3, METH_VARARGS, "\n"
@@ -30760,9 +31061,11 @@ static PyMethodDef SwigMethods[] = {
 /* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */
 
 static swig_type_info _swigt__p_Bin1D = {"_p_Bin1D", "Bin1D *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_CloneableVectorT_FrameTrafo_const_t = {"_p_CloneableVectorT_FrameTrafo_const_t", "CloneableVector< FrameTrafo const > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_CloneableVectorT_Scale_const_t = {"_p_CloneableVectorT_Scale_const_t", "CloneableVector< Scale const > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_Coordinate = {"_p_Coordinate", "Coordinate *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_Frame = {"_p_Frame", "Frame *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_FrameTrafo = {"_p_FrameTrafo", "FrameTrafo *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_ICloneable = {"_p_ICloneable", "ICloneable *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_Rotation3DT_double_t = {"_p_Rotation3DT_double_t", "RotMatrix *|Rotation3D< double > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_Scale = {"_p_Scale", "Scale *", 0, 0, (void*)0, 0};
@@ -30820,9 +31123,11 @@ static swig_type_info _swigt__p_value_type = {"_p_value_type", "value_type *", 0
 
 static swig_type_info *swig_type_initial[] = {
   &_swigt__p_Bin1D,
+  &_swigt__p_CloneableVectorT_FrameTrafo_const_t,
   &_swigt__p_CloneableVectorT_Scale_const_t,
   &_swigt__p_Coordinate,
   &_swigt__p_Frame,
+  &_swigt__p_FrameTrafo,
   &_swigt__p_ICloneable,
   &_swigt__p_Rotation3DT_double_t,
   &_swigt__p_Scale,
@@ -30880,9 +31185,11 @@ static swig_type_info *swig_type_initial[] = {
 };
 
 static swig_cast_info _swigc__p_Bin1D[] = {  {&_swigt__p_Bin1D, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_CloneableVectorT_FrameTrafo_const_t[] = {  {&_swigt__p_CloneableVectorT_FrameTrafo_const_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_CloneableVectorT_Scale_const_t[] = {  {&_swigt__p_CloneableVectorT_Scale_const_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_Coordinate[] = {  {&_swigt__p_Coordinate, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_Frame[] = {  {&_swigt__p_Frame, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_FrameTrafo[] = {  {&_swigt__p_FrameTrafo, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_ICloneable[] = {  {&_swigt__p_ICloneable, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_Rotation3DT_double_t[] = {  {&_swigt__p_Rotation3DT_double_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_Scale[] = {  {&_swigt__p_Scale, 0, 0, 0},{0, 0, 0, 0}};
@@ -30940,9 +31247,11 @@ static swig_cast_info _swigc__p_value_type[] = {  {&_swigt__p_value_type, 0, 0,
 
 static swig_cast_info *swig_cast_initial[] = {
   _swigc__p_Bin1D,
+  _swigc__p_CloneableVectorT_FrameTrafo_const_t,
   _swigc__p_CloneableVectorT_Scale_const_t,
   _swigc__p_Coordinate,
   _swigc__p_Frame,
+  _swigc__p_FrameTrafo,
   _swigc__p_ICloneable,
   _swigc__p_Rotation3DT_double_t,
   _swigc__p_Scale,
-- 
GitLab