From 3bc58cc3e7b760b06720a103569b42415716ccdb Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 19 Apr 2021 22:53:10 +0200
Subject: [PATCH 1/4] minor cleanup, comment

---
 Device/Unit/IUnitConverter.cpp        |  8 +++-----
 Device/Unit/IUnitConverter.h          |  7 ++++++-
 auto/Wrap/doxygenDevice.i             |  2 +-
 auto/Wrap/libBornAgainDevice.py       |  4 ++--
 auto/Wrap/libBornAgainDevice_wrap.cpp | 23 +++++++++++++----------
 5 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/Device/Unit/IUnitConverter.cpp b/Device/Unit/IUnitConverter.cpp
index 9fc5d9691b6..43fde2f70ff 100644
--- a/Device/Unit/IUnitConverter.cpp
+++ b/Device/Unit/IUnitConverter.cpp
@@ -17,7 +17,7 @@
 
 IUnitConverter::~IUnitConverter() = default;
 
-std::string IUnitConverter::axisName(size_t i_axis, Axes::Units units_type) const
+std::string IUnitConverter::axisName(size_t i_axis, const Axes::Units& units_type) const
 {
     const auto& name_maps = createNameMaps();
     if (name_maps.size() <= i_axis)
@@ -25,8 +25,7 @@ std::string IUnitConverter::axisName(size_t i_axis, Axes::Units units_type) cons
                                  "is smaller or equal to the axis index"
                                  + std::to_string(static_cast<int>(i_axis)));
     const auto& name_map = name_maps[i_axis];
-    units_type = substituteDefaultUnits(units_type);
-    const auto& it = name_map.find(units_type);
+    const auto& it = name_map.find(substituteDefaultUnits(units_type));
     if (it == name_map.cend())
         throwUnitsError("IUnitConverter::axisName", availableUnits());
     return it->second;
@@ -35,9 +34,8 @@ std::string IUnitConverter::axisName(size_t i_axis, Axes::Units units_type) cons
 std::unique_ptr<OutputData<double>>
 IUnitConverter::createConvertedData(const OutputData<double>& data, Axes::Units units) const
 {
-    const size_t dim = data.rank();
     std::unique_ptr<OutputData<double>> result(new OutputData<double>);
-    for (size_t i = 0; i < dim; ++i)
+    for (size_t i = 0; i < data.rank(); ++i)
         result->addAxis(*createConvertedAxis(i, units));
     result->setRawDataVector(data.getRawDataVector());
     return result;
diff --git a/Device/Unit/IUnitConverter.h b/Device/Unit/IUnitConverter.h
index 1bace479b34..86ef8cfbf5e 100644
--- a/Device/Unit/IUnitConverter.h
+++ b/Device/Unit/IUnitConverter.h
@@ -41,6 +41,11 @@ const std::map<Axes::Units, const char*> axisUnitLabel = {
 #ifndef USER_API
 
 //! Interface to provide axis translations to different units for simulation output
+
+//! Child classes are currently declared in
+//! - Device/Detector/SimpleUnitConverters.h,
+//! - Core/Scan/UnitConverter1D.h.
+
 //! @ingroup simulation_internal
 
 class IUnitConverter : public ICloneable {
@@ -55,7 +60,7 @@ public:
     virtual double calculateMax(size_t i_axis, Axes::Units units_type) const = 0;
     virtual size_t axisSize(size_t i_axis) const = 0;
 
-    std::string axisName(size_t i_axis, Axes::Units units_type = Axes::Units::DEFAULT) const;
+    std::string axisName(size_t i_axis, const Axes::Units& units_type = Axes::Units::DEFAULT) const;
 
     virtual std::vector<Axes::Units> availableUnits() const = 0;
     virtual Axes::Units defaultUnits() const = 0;
diff --git a/auto/Wrap/doxygenDevice.i b/auto/Wrap/doxygenDevice.i
index d6b0bd66cbe..17b09979e3f 100644
--- a/auto/Wrap/doxygenDevice.i
+++ b/auto/Wrap/doxygenDevice.i
@@ -1476,7 +1476,7 @@ C++ includes: IUnitConverter.h
 %feature("docstring")  IUnitConverter::axisSize "virtual size_t IUnitConverter::axisSize(size_t i_axis) const =0
 ";
 
-%feature("docstring")  IUnitConverter::axisName "std::string IUnitConverter::axisName(size_t i_axis, Axes::Units units_type=Axes::Units::DEFAULT) const
+%feature("docstring")  IUnitConverter::axisName "std::string IUnitConverter::axisName(size_t i_axis, const Axes::Units &units_type=Axes::Units::DEFAULT) const
 ";
 
 %feature("docstring")  IUnitConverter::availableUnits "virtual std::vector<Axes::Units> IUnitConverter::availableUnits() const =0
diff --git a/auto/Wrap/libBornAgainDevice.py b/auto/Wrap/libBornAgainDevice.py
index 500538dc7eb..c1b8e5f6d1f 100644
--- a/auto/Wrap/libBornAgainDevice.py
+++ b/auto/Wrap/libBornAgainDevice.py
@@ -3779,8 +3779,8 @@ class IUnitConverter(libBornAgainBase.ICloneable):
 
     def axisName(self, *args):
         r"""
-        axisName(IUnitConverter self, size_t i_axis, Axes::Units units_type=Axes::Units::DEFAULT) -> std::string
-        std::string IUnitConverter::axisName(size_t i_axis, Axes::Units units_type=Axes::Units::DEFAULT) const
+        axisName(IUnitConverter self, size_t i_axis, Axes::Units const & units_type=Axes::Units::DEFAULT) -> std::string
+        std::string IUnitConverter::axisName(size_t i_axis, const Axes::Units &units_type=Axes::Units::DEFAULT) const
 
         """
         return _libBornAgainDevice.IUnitConverter_axisName(self, *args)
diff --git a/auto/Wrap/libBornAgainDevice_wrap.cpp b/auto/Wrap/libBornAgainDevice_wrap.cpp
index 664400ded04..62d2997dc0a 100644
--- a/auto/Wrap/libBornAgainDevice_wrap.cpp
+++ b/auto/Wrap/libBornAgainDevice_wrap.cpp
@@ -35090,13 +35090,14 @@ SWIGINTERN PyObject *_wrap_IUnitConverter_axisName__SWIG_0(PyObject *SWIGUNUSEDP
   PyObject *resultobj = 0;
   IUnitConverter *arg1 = (IUnitConverter *) 0 ;
   size_t arg2 ;
-  Axes::Units arg3 ;
+  Axes::Units *arg3 = 0 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
   size_t val2 ;
   int ecode2 = 0 ;
   int val3 ;
-  int ecode3 = 0 ;
+  int ecode3 ;
+  Axes::Units temp3 ;
   std::string result;
   
   if ((nobjs < 3) || (nobjs > 3)) SWIG_fail;
@@ -35110,12 +35111,14 @@ SWIGINTERN PyObject *_wrap_IUnitConverter_axisName__SWIG_0(PyObject *SWIGUNUSEDP
     SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "IUnitConverter_axisName" "', argument " "2"" of type '" "size_t""'");
   } 
   arg2 = static_cast< size_t >(val2);
-  ecode3 = SWIG_AsVal_int(swig_obj[2], &val3);
+  ecode3 = SWIG_AsVal_int (swig_obj[2], &val3);
   if (!SWIG_IsOK(ecode3)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "IUnitConverter_axisName" "', argument " "3"" of type '" "Axes::Units""'");
-  } 
-  arg3 = static_cast< Axes::Units >(val3);
-  result = ((IUnitConverter const *)arg1)->axisName(arg2,arg3);
+    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "IUnitConverter_axisName" "', argument " "3"" of type '" "Axes::Units const &""'");
+  } else {
+    temp3 = static_cast< Axes::Units >(val3);
+    arg3 = &temp3;
+  }
+  result = ((IUnitConverter const *)arg1)->axisName(arg2,(Axes::Units const &)*arg3);
   resultobj = SWIG_From_std_string(static_cast< std::string >(result));
   return resultobj;
 fail:
@@ -35200,7 +35203,7 @@ SWIGINTERN PyObject *_wrap_IUnitConverter_axisName(PyObject *self, PyObject *arg
 fail:
   SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'IUnitConverter_axisName'.\n"
     "  Possible C/C++ prototypes are:\n"
-    "    IUnitConverter::axisName(size_t,Axes::Units) const\n"
+    "    IUnitConverter::axisName(size_t,Axes::Units const &) const\n"
     "    IUnitConverter::axisName(size_t) const\n");
   return 0;
 }
@@ -45457,8 +45460,8 @@ static PyMethodDef SwigMethods[] = {
 		"\n"
 		""},
 	 { "IUnitConverter_axisName", _wrap_IUnitConverter_axisName, METH_VARARGS, "\n"
-		"IUnitConverter_axisName(IUnitConverter self, size_t i_axis, Axes::Units units_type=Axes::Units::DEFAULT) -> std::string\n"
-		"std::string IUnitConverter::axisName(size_t i_axis, Axes::Units units_type=Axes::Units::DEFAULT) const\n"
+		"IUnitConverter_axisName(IUnitConverter self, size_t i_axis, Axes::Units const & units_type=Axes::Units::DEFAULT) -> std::string\n"
+		"std::string IUnitConverter::axisName(size_t i_axis, const Axes::Units &units_type=Axes::Units::DEFAULT) const\n"
 		"\n"
 		""},
 	 { "IUnitConverter_availableUnits", _wrap_IUnitConverter_availableUnits, METH_O, "\n"
-- 
GitLab


From 276d3b03cf2bbf23d8b5d50f10fae10fb8be7be9 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 19 Apr 2021 22:58:34 +0200
Subject: [PATCH 2/4] mv anonymous namespace to top; clang-format

---
 Core/Fitting/FitObjective.cpp    |  3 +--
 Core/Fitting/FitObjective.h      |  3 +--
 Core/Fitting/ObjectiveMetric.cpp |  3 +--
 Core/Fitting/SimDataPair.cpp     |  1 -
 Core/Scan/UnitConverter1D.cpp    | 43 +++++++++++++-------------------
 Core/Scan/UnitConverter1D.h      |  1 +
 6 files changed, 22 insertions(+), 32 deletions(-)

diff --git a/Core/Fitting/FitObjective.cpp b/Core/Fitting/FitObjective.cpp
index 92b4286d0e8..791386cf27a 100644
--- a/Core/Fitting/FitObjective.cpp
+++ b/Core/Fitting/FitObjective.cpp
@@ -72,8 +72,7 @@ FitObjective::~FitObjective() = default;
 //! @param weight: weight of dataset in metric calculations
 void FitObjective::addSimulationAndData(simulation_builder_t builder,
                                         const OutputData<double>& data,
-                                        std::unique_ptr<OutputData<double>>&& stdv,
-                                        double weight)
+                                        std::unique_ptr<OutputData<double>>&& stdv, double weight)
 {
     m_fit_objects.emplace_back(builder, data, std::move(stdv), weight);
 }
diff --git a/Core/Fitting/FitObjective.h b/Core/Fitting/FitObjective.h
index c0be95b08d2..71c834d4f67 100644
--- a/Core/Fitting/FitObjective.h
+++ b/Core/Fitting/FitObjective.h
@@ -42,8 +42,7 @@ public:
 
 #ifndef SWIG
     void addSimulationAndData(simulation_builder_t builder, const OutputData<double>& data,
-                              std::unique_ptr<OutputData<double>>&& stdv,
-                              double weight = 1.0);
+                              std::unique_ptr<OutputData<double>>&& stdv, double weight = 1.0);
 #endif
     //! Constructs simulation/data pair for later fit.
     //! @param callback: simulation builder capable of producing simulations
diff --git a/Core/Fitting/ObjectiveMetric.cpp b/Core/Fitting/ObjectiveMetric.cpp
index 45714c4a235..583ecc45d07 100644
--- a/Core/Fitting/ObjectiveMetric.cpp
+++ b/Core/Fitting/ObjectiveMetric.cpp
@@ -45,8 +45,7 @@ void checkIntegrity(const std::vector<double>& sim_data, const std::vector<doubl
 }
 
 void checkIntegrity(const std::vector<double>& sim_data, const std::vector<double>& exp_data,
-                    const std::vector<double>& exp_stdv,
-                    const std::vector<double>& weight_factors)
+                    const std::vector<double>& exp_stdv, const std::vector<double>& weight_factors)
 {
     if (sim_data.size() != exp_stdv.size())
         throw std::runtime_error("Error in ObjectiveMetric: input arrays have different sizes");
diff --git a/Core/Fitting/SimDataPair.cpp b/Core/Fitting/SimDataPair.cpp
index b9063810423..a154cc20766 100644
--- a/Core/Fitting/SimDataPair.cpp
+++ b/Core/Fitting/SimDataPair.cpp
@@ -35,7 +35,6 @@ std::unique_ptr<OutputData<double>> initUserWeights(const OutputData<double>& sh
 }
 } // namespace
 
-
 SimDataPair::SimDataPair(simulation_builder_t builder, const OutputData<double>& raw_data,
                          std::unique_ptr<OutputData<double>>&& raw_stdv, double user_weight)
     : m_simulation_builder(builder)
diff --git a/Core/Scan/UnitConverter1D.cpp b/Core/Scan/UnitConverter1D.cpp
index 87835deaae5..849ab84ead2 100644
--- a/Core/Scan/UnitConverter1D.cpp
+++ b/Core/Scan/UnitConverter1D.cpp
@@ -23,12 +23,27 @@
 #include "Device/Unit/AxisNames.h"
 
 namespace {
-double getQ(double wavelength, double angle);
 
-double getInvQ(double wavelength, double q);
+double getQ(double wavelength, double angle)
+{
+    return 4.0 * M_PI * std::sin(angle) / wavelength;
+}
+
+double getInvQ(double wavelength, double q)
+{
+    double sin_angle = q * wavelength / (4.0 * M_PI);
+    return std::asin(sin_angle);
+}
 
 std::unique_ptr<PointwiseAxis>
-createTranslatedAxis(const IAxis& axis, std::function<double(double)> translator, std::string name);
+createTranslatedAxis(const IAxis& axis, std::function<double(double)> translator, std::string name)
+{
+    auto coordinates = axis.binCenters();
+    for (size_t i = 0, size = coordinates.size(); i < size; ++i)
+        coordinates[i] = translator(coordinates[i]);
+    return std::make_unique<PointwiseAxis>(name, coordinates);
+}
+
 } // namespace
 
 std::unique_ptr<UnitConverter1D> UnitConverter1D::createUnitConverter(const ISpecularScan& scan)
@@ -236,25 +251,3 @@ std::function<double(double)> UnitConverterQSpec::getTraslatorTo(Axes::Units uni
         throwUnitsError("UnitConverterQSpec::getTraslatorTo", availableUnits());
     }
 }
-
-namespace {
-double getQ(double wavelength, double angle)
-{
-    return 4.0 * M_PI * std::sin(angle) / wavelength;
-}
-
-double getInvQ(double wavelength, double q)
-{
-    double sin_angle = q * wavelength / (4.0 * M_PI);
-    return std::asin(sin_angle);
-}
-
-std::unique_ptr<PointwiseAxis>
-createTranslatedAxis(const IAxis& axis, std::function<double(double)> translator, std::string name)
-{
-    auto coordinates = axis.binCenters();
-    for (size_t i = 0, size = coordinates.size(); i < size; ++i)
-        coordinates[i] = translator(coordinates[i]);
-    return std::make_unique<PointwiseAxis>(name, coordinates);
-}
-} // namespace
diff --git a/Core/Scan/UnitConverter1D.h b/Core/Scan/UnitConverter1D.h
index 124757ec3a9..1c86f80cc6d 100644
--- a/Core/Scan/UnitConverter1D.h
+++ b/Core/Scan/UnitConverter1D.h
@@ -131,5 +131,6 @@ protected:
 
     std::unique_ptr<IAxis> m_axis; //!< qz values (in inv. nm).
 };
+
 #endif // BORNAGAIN_CORE_SCAN_UNITCONVERTER1D_H
 #endif // USER_API
-- 
GitLab


From e6a6be3cbeb827fcfab3e4c6d8971854cbfb4359 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 19 Apr 2021 22:58:58 +0200
Subject: [PATCH 3/4] clang-format: allow 2 empty lines

---
 .clang-format | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.clang-format b/.clang-format
index 1ea3be2d5d0..731105a5097 100644
--- a/.clang-format
+++ b/.clang-format
@@ -10,5 +10,6 @@ ColumnLimit: 100
 ConstructorInitializerAllOnOneLineOrOnePerLine: true
 IndentCaseLabels: false
 IndentWidth: 4
+MaxEmptyLinesToKeep: 2
 PointerAlignment: Left
 UseTab: Never
-- 
GitLab


From cd702f2b98fad5d518a0f738f2998528b78404e9 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Mon, 19 Apr 2021 23:55:45 +0200
Subject: [PATCH 4/4] mv convertData(..) from ISimulation to SimDataPair

---
 Core/Fitting/SimDataPair.cpp           |  59 +++++-
 Core/Scan/UnitConverter1D.cpp          |   1 +
 Core/Simulation/ISimulation.cpp        |  51 -----
 Core/Simulation/ISimulation.h          |   3 -
 Core/Simulation/UnitConverterUtils.cpp |  17 +-
 auto/Wrap/doxygenCore.i                |   5 -
 auto/Wrap/doxygenDevice.i              |   7 +-
 auto/Wrap/libBornAgainCore.py          |  10 -
 auto/Wrap/libBornAgainCore_wrap.cpp    | 274 +++++++------------------
 auto/Wrap/libBornAgainDevice.py        |   7 +-
 10 files changed, 147 insertions(+), 287 deletions(-)

diff --git a/Core/Fitting/SimDataPair.cpp b/Core/Fitting/SimDataPair.cpp
index a154cc20766..5aa1c0d82cc 100644
--- a/Core/Fitting/SimDataPair.cpp
+++ b/Core/Fitting/SimDataPair.cpp
@@ -19,6 +19,7 @@
 #include "Device/Data/DataUtils.h"
 
 namespace {
+
 [[noreturn]] void throwInitializationException(std::string method)
 {
     std::stringstream ss;
@@ -33,8 +34,60 @@ std::unique_ptr<OutputData<double>> initUserWeights(const OutputData<double>& sh
     result->setAllTo(value);
     return result;
 }
+
+bool detHasSameDimensions(const IDetector& detector, const OutputData<double>& data)
+{
+    if (data.rank() != detector.dimension())
+        return false;
+
+    for (size_t i = 0; i < detector.dimension(); ++i)
+        if (data.axis(i).size() != detector.axis(i).size())
+            return false;
+
+    return true;
+}
+
+//! Convert user data to SimulationResult object for later drawing in various axes units.
+//! User data will be cropped to the ROI defined in the simulation, amplitudes in areas
+//! corresponding to the masked areas of the detector will be set to zero.
+
+SimulationResult convertData(const ISimulation& simulation, const OutputData<double>& data,
+                             bool put_masked_areas_to_zero)
+{
+    auto converter = UnitConverterUtils::createConverter(simulation);
+    auto roi_data = UnitConverterUtils::createOutputData(*converter, converter->defaultUnits());
+
+    if (roi_data->hasSameDimensions(data)) {
+        // data is already cropped to ROI
+        if (put_masked_areas_to_zero) {
+            simulation.detector().iterate(
+                [&](IDetector::const_iterator it) {
+                    (*roi_data)[it.roiIndex()] = data[it.roiIndex()];
+                },
+                /*visit_masked*/ false);
+        } else {
+            roi_data->setRawDataVector(data.getRawDataVector());
+        }
+
+    } else if (detHasSameDimensions(simulation.detector(), data)) {
+        // exp data has same shape as the detector, we have to put orig data to smaller roi map
+        simulation.detector().iterate(
+            [&](IDetector::const_iterator it) {
+                (*roi_data)[it.roiIndex()] = data[it.detectorIndex()];
+            },
+            /*visit_masked*/ !put_masked_areas_to_zero);
+
+    } else {
+        throw std::runtime_error("FitObject::init_dataset() -> Error. Detector and exp data have "
+                                 "different shape.");
+    }
+
+    return SimulationResult(*roi_data, *converter);
+}
+
 } // namespace
 
+
 SimDataPair::SimDataPair(simulation_builder_t builder, const OutputData<double>& raw_data,
                          std::unique_ptr<OutputData<double>>&& raw_stdv, double user_weight)
     : m_simulation_builder(builder)
@@ -86,10 +139,10 @@ void SimDataPair::execSimulation(const mumufit::Parameters& params)
     if (!m_simulation || m_sim_data.size() == 0)
         throwInitializationException("initResultArrays");
 
-    m_exp_data = m_simulation->convertData(*m_raw_data, true);
+    m_exp_data = convertData(*m_simulation, *m_raw_data, true);
 
     if (containsUncertainties()) {
-        m_uncertainties = m_simulation->convertData(*m_raw_uncertainties, true);
+        m_uncertainties = convertData(*m_simulation, *m_raw_uncertainties, true);
     } else {
         const IUnitConverter& converter = m_sim_data.converter();
         std::unique_ptr<OutputData<double>> dummy_array =
@@ -97,7 +150,7 @@ void SimDataPair::execSimulation(const mumufit::Parameters& params)
         m_uncertainties = SimulationResult(*dummy_array, converter);
     }
 
-    m_user_weights = m_simulation->convertData(*m_raw_user_weights, true);
+    m_user_weights = convertData(*m_simulation, *m_raw_user_weights, true);
 }
 
 bool SimDataPair::containsUncertainties() const
diff --git a/Core/Scan/UnitConverter1D.cpp b/Core/Scan/UnitConverter1D.cpp
index 849ab84ead2..15109ffadac 100644
--- a/Core/Scan/UnitConverter1D.cpp
+++ b/Core/Scan/UnitConverter1D.cpp
@@ -46,6 +46,7 @@ createTranslatedAxis(const IAxis& axis, std::function<double(double)> translator
 
 } // namespace
 
+
 std::unique_ptr<UnitConverter1D> UnitConverter1D::createUnitConverter(const ISpecularScan& scan)
 {
     if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(&scan))
diff --git a/Core/Simulation/ISimulation.cpp b/Core/Simulation/ISimulation.cpp
index 170b885354a..3c9ed33a3a3 100644
--- a/Core/Simulation/ISimulation.cpp
+++ b/Core/Simulation/ISimulation.cpp
@@ -17,7 +17,6 @@
 #include "Core/Computation/IBackground.h"
 #include "Core/Computation/IComputation.h"
 #include "Core/Simulation/MPISimulation.h"
-#include "Core/Simulation/UnitConverterUtils.h"
 #include "Param/Base/ParameterPool.h"
 #include "Sample/Multilayer/MultiLayerUtils.h"
 #include "Sample/SampleBuilderEngine/ISampleBuilder.h"
@@ -26,18 +25,6 @@
 
 namespace {
 
-bool detHasSameDimensions(const IDetector& detector, const OutputData<double>& data)
-{
-    if (data.rank() != detector.dimension())
-        return false;
-
-    for (size_t i = 0; i < detector.dimension(); ++i)
-        if (data.axis(i).size() != detector.axis(i).size())
-            return false;
-
-    return true;
-}
-
 size_t getIndexStep(size_t total_size, size_t n_handlers)
 {
     ASSERT(total_size > 0);
@@ -295,41 +282,3 @@ void ISimulation::runSingleSimulation(size_t batch_start, size_t batch_size, dou
     addBackgroundIntensity(batch_start, batch_size);
     addDataToCache(weight);
 }
-
-//! Convert user data to SimulationResult object for later drawing in various axes units.
-//! User data will be cropped to the ROI defined in the simulation, amplitudes in areas
-//! corresponding to the masked areas of the detector will be set to zero.
-
-SimulationResult ISimulation::convertData(const OutputData<double>& data,
-                                          bool put_masked_areas_to_zero)
-{
-    auto converter = UnitConverterUtils::createConverter(*this);
-    auto roi_data = UnitConverterUtils::createOutputData(*converter, converter->defaultUnits());
-
-    if (roi_data->hasSameDimensions(data)) {
-        // data is already cropped to ROI
-        if (put_masked_areas_to_zero) {
-            detector().iterate(
-                [&](IDetector::const_iterator it) {
-                    (*roi_data)[it.roiIndex()] = data[it.roiIndex()];
-                },
-                /*visit_masked*/ false);
-        } else {
-            roi_data->setRawDataVector(data.getRawDataVector());
-        }
-
-    } else if (detHasSameDimensions(detector(), data)) {
-        // exp data has same shape as the detector, we have to put orig data to smaller roi map
-        detector().iterate(
-            [&](IDetector::const_iterator it) {
-                (*roi_data)[it.roiIndex()] = data[it.detectorIndex()];
-            },
-            /*visit_masked*/ !put_masked_areas_to_zero);
-
-    } else {
-        throw std::runtime_error("FitObject::init_dataset() -> Error. Detector and exp data have "
-                                 "different shape.");
-    }
-
-    return SimulationResult(*roi_data, *converter);
-}
diff --git a/Core/Simulation/ISimulation.h b/Core/Simulation/ISimulation.h
index 66e3d3d64ae..f4ee6aaebcb 100644
--- a/Core/Simulation/ISimulation.h
+++ b/Core/Simulation/ISimulation.h
@@ -92,9 +92,6 @@ public:
 
     std::vector<const INode*> getChildren() const;
 
-    SimulationResult convertData(const OutputData<double>& data,
-                                 bool put_masked_areas_to_zero = true);
-
     friend class MPISimulation;
 
 #ifndef SWIG
diff --git a/Core/Simulation/UnitConverterUtils.cpp b/Core/Simulation/UnitConverterUtils.cpp
index ae606bd44dd..015bbbe2909 100644
--- a/Core/Simulation/UnitConverterUtils.cpp
+++ b/Core/Simulation/UnitConverterUtils.cpp
@@ -39,7 +39,8 @@ UnitConverterUtils::createConverterForGISAS(const Instrument& instrument)
 
     if (const auto* const det = dynamic_cast<const SphericalDetector*>(detector))
         return std::make_unique<SphericalConverter>(*det, instrument.beam());
-    else if (const auto* const det = dynamic_cast<const RectangularDetector*>(detector))
+
+    if (const auto* const det = dynamic_cast<const RectangularDetector*>(detector))
         return std::make_unique<RectangularConverter>(*det, instrument.beam());
 
     throw std::runtime_error("Error in createConverterForGISAS: wrong or absent detector type");
@@ -47,20 +48,18 @@ UnitConverterUtils::createConverterForGISAS(const Instrument& instrument)
 
 std::unique_ptr<IUnitConverter> UnitConverterUtils::createConverter(const ISimulation& simulation)
 {
-    if (auto gisas = dynamic_cast<const GISASSimulation*>(&simulation)) {
+    if (auto gisas = dynamic_cast<const GISASSimulation*>(&simulation))
         return createConverterForGISAS(gisas->instrument());
 
-    } else if (auto spec = dynamic_cast<const SpecularSimulation*>(&simulation)) {
+    if (auto spec = dynamic_cast<const SpecularSimulation*>(&simulation))
         return UnitConverter1D::createUnitConverter(*spec->dataHandler());
 
-    } else if (auto probe = dynamic_cast<const DepthProbeSimulation*>(&simulation)) {
+    if (auto probe = dynamic_cast<const DepthProbeSimulation*>(&simulation))
         return probe->createUnitConverter();
 
-    } else if (auto off_spec = dynamic_cast<const OffSpecularSimulation*>(&simulation)) {
+    if (auto off_spec = dynamic_cast<const OffSpecularSimulation*>(&simulation))
         return off_spec->createUnitConverter();
 
-    } else {
-        throw std::runtime_error("UnitConverterUtils::createConverter -> "
-                                 "Not implemented simulation.");
-    }
+    throw std::runtime_error("UnitConverterUtils::createConverter -> "
+                             "Not implemented simulation.");
 }
diff --git a/auto/Wrap/doxygenCore.i b/auto/Wrap/doxygenCore.i
index 50ccdc2bc1a..c6dfb7fc2e4 100644
--- a/auto/Wrap/doxygenCore.i
+++ b/auto/Wrap/doxygenCore.i
@@ -1081,11 +1081,6 @@ Initializes a progress monitor that prints to stdout.
 %feature("docstring")  ISimulation::getChildren "std::vector< const INode * > ISimulation::getChildren() const
 ";
 
-%feature("docstring")  ISimulation::convertData "SimulationResult ISimulation::convertData(const OutputData< double > &data, bool put_masked_areas_to_zero=true)
-
-Convert user data to SimulationResult object for later drawing in various axes units. User data will be cropped to the ROI defined in the simulation, amplitudes in areas corresponding to the masked areas of the detector will be set to zero. 
-";
-
 %feature("docstring")  ISimulation::ISimulation "ISimulation::ISimulation(const Beam &beam, const IDetector &detector)
 ";
 
diff --git a/auto/Wrap/doxygenDevice.i b/auto/Wrap/doxygenDevice.i
index 17b09979e3f..0001c5949d3 100644
--- a/auto/Wrap/doxygenDevice.i
+++ b/auto/Wrap/doxygenDevice.i
@@ -1453,7 +1453,12 @@ Returns true if area defined by two bins is inside or on border of polygon (more
 // File: classIUnitConverter.xml
 %feature("docstring") IUnitConverter "
 
-Interface to provide axis translations to different units for simulation output
+Interface to provide axis translations to different units for simulation output.
+
+Child classes are currently declared in
+ Device/Detector/SimpleUnitConverters.h,
+
+Core/Scan/UnitConverter1D.h.
 
 C++ includes: IUnitConverter.h
 ";
diff --git a/auto/Wrap/libBornAgainCore.py b/auto/Wrap/libBornAgainCore.py
index 1df514b3094..97b1cb3d9a3 100644
--- a/auto/Wrap/libBornAgainCore.py
+++ b/auto/Wrap/libBornAgainCore.py
@@ -3665,16 +3665,6 @@ class ISimulation(libBornAgainBase.ICloneable, libBornAgainParam.INode):
         """
         return _libBornAgainCore.ISimulation_getChildren(self)
 
-    def convertData(self, data, put_masked_areas_to_zero=True):
-        r"""
-        convertData(ISimulation self, OutputData< double > const & data, bool put_masked_areas_to_zero=True) -> SimulationResult
-        SimulationResult ISimulation::convertData(const OutputData< double > &data, bool put_masked_areas_to_zero=true)
-
-        Convert user data to SimulationResult object for later drawing in various axes units. User data will be cropped to the ROI defined in the simulation, amplitudes in areas corresponding to the masked areas of the detector will be set to zero. 
-
-        """
-        return _libBornAgainCore.ISimulation_convertData(self, data, put_masked_areas_to_zero)
-
     def setSampleBuilder(self, ptr):
         self.samplebuilder = ptr
         self.setSampleBuilderCpp(ptr)
diff --git a/auto/Wrap/libBornAgainCore_wrap.cpp b/auto/Wrap/libBornAgainCore_wrap.cpp
index 0a41cb1676e..a90e0175fe7 100644
--- a/auto/Wrap/libBornAgainCore_wrap.cpp
+++ b/auto/Wrap/libBornAgainCore_wrap.cpp
@@ -3137,77 +3137,76 @@ namespace Swig {
 #define SWIGTYPE_p_IterationInfo swig_types[37]
 #define SWIGTYPE_p_MultiLayer swig_types[38]
 #define SWIGTYPE_p_OffSpecularSimulation swig_types[39]
-#define SWIGTYPE_p_OutputDataT_double_t swig_types[40]
-#define SWIGTYPE_p_ParameterDistribution swig_types[41]
-#define SWIGTYPE_p_ParameterPool swig_types[42]
-#define SWIGTYPE_p_PoissonNoiseBackground swig_types[43]
-#define SWIGTYPE_p_ProgressHandler__Callback_t swig_types[44]
-#define SWIGTYPE_p_PyBuilderCallback swig_types[45]
-#define SWIGTYPE_p_PyObserverCallback swig_types[46]
-#define SWIGTYPE_p_QSpecScan swig_types[47]
-#define SWIGTYPE_p_RealLimits swig_types[48]
-#define SWIGTYPE_p_ScanResolution swig_types[49]
-#define SWIGTYPE_p_SimulationOptions swig_types[50]
-#define SWIGTYPE_p_SimulationResult swig_types[51]
-#define SWIGTYPE_p_SpecularSimulation swig_types[52]
-#define SWIGTYPE_p_VarianceConstantFunction swig_types[53]
-#define SWIGTYPE_p_VarianceSimFunction swig_types[54]
-#define SWIGTYPE_p_allocator_type swig_types[55]
-#define SWIGTYPE_p_char swig_types[56]
-#define SWIGTYPE_p_difference_type swig_types[57]
-#define SWIGTYPE_p_first_type swig_types[58]
-#define SWIGTYPE_p_int swig_types[59]
-#define SWIGTYPE_p_key_type swig_types[60]
-#define SWIGTYPE_p_long_long swig_types[61]
-#define SWIGTYPE_p_mapped_type swig_types[62]
-#define SWIGTYPE_p_mumufit__MinimizerResult swig_types[63]
-#define SWIGTYPE_p_mumufit__Parameters swig_types[64]
-#define SWIGTYPE_p_p_PyObject swig_types[65]
-#define SWIGTYPE_p_second_type swig_types[66]
-#define SWIGTYPE_p_short swig_types[67]
-#define SWIGTYPE_p_signed_char swig_types[68]
-#define SWIGTYPE_p_size_type swig_types[69]
-#define SWIGTYPE_p_std__allocatorT_AxisInfo_t swig_types[70]
-#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_double_t_t swig_types[71]
-#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t swig_types[72]
-#define SWIGTYPE_p_std__allocatorT_INode_const_p_t swig_types[73]
-#define SWIGTYPE_p_std__allocatorT_INode_p_t swig_types[74]
-#define SWIGTYPE_p_std__allocatorT_double_t swig_types[75]
-#define SWIGTYPE_p_std__allocatorT_int_t swig_types[76]
-#define SWIGTYPE_p_std__allocatorT_std__complexT_double_t_t swig_types[77]
-#define SWIGTYPE_p_std__allocatorT_std__pairT_double_double_t_t swig_types[78]
-#define SWIGTYPE_p_std__allocatorT_std__pairT_std__string_const_double_t_t swig_types[79]
-#define SWIGTYPE_p_std__allocatorT_std__string_t swig_types[80]
-#define SWIGTYPE_p_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t swig_types[81]
-#define SWIGTYPE_p_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t swig_types[82]
-#define SWIGTYPE_p_std__allocatorT_unsigned_long_t swig_types[83]
-#define SWIGTYPE_p_std__complexT_double_t swig_types[84]
-#define SWIGTYPE_p_std__invalid_argument swig_types[85]
-#define SWIGTYPE_p_std__lessT_std__string_t swig_types[86]
-#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[87]
-#define SWIGTYPE_p_std__pairT_double_double_t swig_types[88]
-#define SWIGTYPE_p_std__shared_ptrT_ISampleBuilder_t swig_types[89]
-#define SWIGTYPE_p_std__vectorT_AxisInfo_std__allocatorT_AxisInfo_t_t swig_types[90]
-#define SWIGTYPE_p_std__vectorT_BasicVector3DT_double_t_std__allocatorT_BasicVector3DT_double_t_t_t swig_types[91]
-#define SWIGTYPE_p_std__vectorT_BasicVector3DT_std__complexT_double_t_t_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t_t swig_types[92]
-#define SWIGTYPE_p_std__vectorT_INode_const_p_std__allocatorT_INode_const_p_t_t swig_types[93]
-#define SWIGTYPE_p_std__vectorT_INode_p_std__allocatorT_INode_p_t_t swig_types[94]
-#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[95]
-#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[96]
-#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[97]
-#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[98]
-#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[99]
-#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[100]
-#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[101]
-#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[102]
-#define SWIGTYPE_p_swig__SwigPyIterator swig_types[103]
-#define SWIGTYPE_p_unsigned_char swig_types[104]
-#define SWIGTYPE_p_unsigned_int swig_types[105]
-#define SWIGTYPE_p_unsigned_long_long swig_types[106]
-#define SWIGTYPE_p_unsigned_short swig_types[107]
-#define SWIGTYPE_p_value_type swig_types[108]
-static swig_type_info *swig_types[110];
-static swig_module_info swig_module = {swig_types, 109, 0, 0, 0, 0};
+#define SWIGTYPE_p_ParameterDistribution swig_types[40]
+#define SWIGTYPE_p_ParameterPool swig_types[41]
+#define SWIGTYPE_p_PoissonNoiseBackground swig_types[42]
+#define SWIGTYPE_p_ProgressHandler__Callback_t swig_types[43]
+#define SWIGTYPE_p_PyBuilderCallback swig_types[44]
+#define SWIGTYPE_p_PyObserverCallback swig_types[45]
+#define SWIGTYPE_p_QSpecScan swig_types[46]
+#define SWIGTYPE_p_RealLimits swig_types[47]
+#define SWIGTYPE_p_ScanResolution swig_types[48]
+#define SWIGTYPE_p_SimulationOptions swig_types[49]
+#define SWIGTYPE_p_SimulationResult swig_types[50]
+#define SWIGTYPE_p_SpecularSimulation swig_types[51]
+#define SWIGTYPE_p_VarianceConstantFunction swig_types[52]
+#define SWIGTYPE_p_VarianceSimFunction swig_types[53]
+#define SWIGTYPE_p_allocator_type swig_types[54]
+#define SWIGTYPE_p_char swig_types[55]
+#define SWIGTYPE_p_difference_type swig_types[56]
+#define SWIGTYPE_p_first_type swig_types[57]
+#define SWIGTYPE_p_int swig_types[58]
+#define SWIGTYPE_p_key_type swig_types[59]
+#define SWIGTYPE_p_long_long swig_types[60]
+#define SWIGTYPE_p_mapped_type swig_types[61]
+#define SWIGTYPE_p_mumufit__MinimizerResult swig_types[62]
+#define SWIGTYPE_p_mumufit__Parameters swig_types[63]
+#define SWIGTYPE_p_p_PyObject swig_types[64]
+#define SWIGTYPE_p_second_type swig_types[65]
+#define SWIGTYPE_p_short swig_types[66]
+#define SWIGTYPE_p_signed_char swig_types[67]
+#define SWIGTYPE_p_size_type swig_types[68]
+#define SWIGTYPE_p_std__allocatorT_AxisInfo_t swig_types[69]
+#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_double_t_t swig_types[70]
+#define SWIGTYPE_p_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t swig_types[71]
+#define SWIGTYPE_p_std__allocatorT_INode_const_p_t swig_types[72]
+#define SWIGTYPE_p_std__allocatorT_INode_p_t swig_types[73]
+#define SWIGTYPE_p_std__allocatorT_double_t swig_types[74]
+#define SWIGTYPE_p_std__allocatorT_int_t swig_types[75]
+#define SWIGTYPE_p_std__allocatorT_std__complexT_double_t_t swig_types[76]
+#define SWIGTYPE_p_std__allocatorT_std__pairT_double_double_t_t swig_types[77]
+#define SWIGTYPE_p_std__allocatorT_std__pairT_std__string_const_double_t_t swig_types[78]
+#define SWIGTYPE_p_std__allocatorT_std__string_t swig_types[79]
+#define SWIGTYPE_p_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t swig_types[80]
+#define SWIGTYPE_p_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t swig_types[81]
+#define SWIGTYPE_p_std__allocatorT_unsigned_long_t swig_types[82]
+#define SWIGTYPE_p_std__complexT_double_t swig_types[83]
+#define SWIGTYPE_p_std__invalid_argument swig_types[84]
+#define SWIGTYPE_p_std__lessT_std__string_t swig_types[85]
+#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[86]
+#define SWIGTYPE_p_std__pairT_double_double_t swig_types[87]
+#define SWIGTYPE_p_std__shared_ptrT_ISampleBuilder_t swig_types[88]
+#define SWIGTYPE_p_std__vectorT_AxisInfo_std__allocatorT_AxisInfo_t_t swig_types[89]
+#define SWIGTYPE_p_std__vectorT_BasicVector3DT_double_t_std__allocatorT_BasicVector3DT_double_t_t_t swig_types[90]
+#define SWIGTYPE_p_std__vectorT_BasicVector3DT_std__complexT_double_t_t_std__allocatorT_BasicVector3DT_std__complexT_double_t_t_t_t swig_types[91]
+#define SWIGTYPE_p_std__vectorT_INode_const_p_std__allocatorT_INode_const_p_t_t swig_types[92]
+#define SWIGTYPE_p_std__vectorT_INode_p_std__allocatorT_INode_p_t_t swig_types[93]
+#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[94]
+#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[95]
+#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[96]
+#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[97]
+#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[98]
+#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[99]
+#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[100]
+#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[101]
+#define SWIGTYPE_p_swig__SwigPyIterator swig_types[102]
+#define SWIGTYPE_p_unsigned_char swig_types[103]
+#define SWIGTYPE_p_unsigned_int swig_types[104]
+#define SWIGTYPE_p_unsigned_long_long swig_types[105]
+#define SWIGTYPE_p_unsigned_short swig_types[106]
+#define SWIGTYPE_p_value_type swig_types[107]
+static swig_type_info *swig_types[109];
+static swig_module_info swig_module = {swig_types, 108, 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)
 
@@ -39838,128 +39837,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_ISimulation_convertData__SWIG_0(PyObject *SWIGUNUSEDPARM(self), Py_ssize_t nobjs, PyObject **swig_obj) {
-  PyObject *resultobj = 0;
-  ISimulation *arg1 = (ISimulation *) 0 ;
-  OutputData< double > *arg2 = 0 ;
-  bool arg3 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  void *argp2 = 0 ;
-  int res2 = 0 ;
-  bool val3 ;
-  int ecode3 = 0 ;
-  SimulationResult result;
-  
-  if ((nobjs < 3) || (nobjs > 3)) SWIG_fail;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ISimulation, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ISimulation_convertData" "', argument " "1"" of type '" "ISimulation *""'"); 
-  }
-  arg1 = reinterpret_cast< ISimulation * >(argp1);
-  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_OutputDataT_double_t,  0  | 0);
-  if (!SWIG_IsOK(res2)) {
-    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ISimulation_convertData" "', argument " "2"" of type '" "OutputData< double > const &""'"); 
-  }
-  if (!argp2) {
-    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "ISimulation_convertData" "', argument " "2"" of type '" "OutputData< double > const &""'"); 
-  }
-  arg2 = reinterpret_cast< OutputData< double > * >(argp2);
-  ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3);
-  if (!SWIG_IsOK(ecode3)) {
-    SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "ISimulation_convertData" "', argument " "3"" of type '" "bool""'");
-  } 
-  arg3 = static_cast< bool >(val3);
-  result = (arg1)->convertData((OutputData< double > const &)*arg2,arg3);
-  resultobj = SWIG_NewPointerObj((new SimulationResult(static_cast< const SimulationResult& >(result))), SWIGTYPE_p_SimulationResult, SWIG_POINTER_OWN |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
-SWIGINTERN PyObject *_wrap_ISimulation_convertData__SWIG_1(PyObject *SWIGUNUSEDPARM(self), Py_ssize_t nobjs, PyObject **swig_obj) {
-  PyObject *resultobj = 0;
-  ISimulation *arg1 = (ISimulation *) 0 ;
-  OutputData< double > *arg2 = 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  void *argp2 = 0 ;
-  int res2 = 0 ;
-  SimulationResult result;
-  
-  if ((nobjs < 2) || (nobjs > 2)) SWIG_fail;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ISimulation, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ISimulation_convertData" "', argument " "1"" of type '" "ISimulation *""'"); 
-  }
-  arg1 = reinterpret_cast< ISimulation * >(argp1);
-  res2 = SWIG_ConvertPtr(swig_obj[1], &argp2, SWIGTYPE_p_OutputDataT_double_t,  0  | 0);
-  if (!SWIG_IsOK(res2)) {
-    SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ISimulation_convertData" "', argument " "2"" of type '" "OutputData< double > const &""'"); 
-  }
-  if (!argp2) {
-    SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "ISimulation_convertData" "', argument " "2"" of type '" "OutputData< double > const &""'"); 
-  }
-  arg2 = reinterpret_cast< OutputData< double > * >(argp2);
-  result = (arg1)->convertData((OutputData< double > const &)*arg2);
-  resultobj = SWIG_NewPointerObj((new SimulationResult(static_cast< const SimulationResult& >(result))), SWIGTYPE_p_SimulationResult, SWIG_POINTER_OWN |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
-SWIGINTERN PyObject *_wrap_ISimulation_convertData(PyObject *self, PyObject *args) {
-  Py_ssize_t argc;
-  PyObject *argv[4] = {
-    0
-  };
-  
-  if (!(argc = SWIG_Python_UnpackTuple(args, "ISimulation_convertData", 0, 3, argv))) SWIG_fail;
-  --argc;
-  if (argc == 2) {
-    int _v;
-    void *vptr = 0;
-    int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_ISimulation, 0);
-    _v = SWIG_CheckState(res);
-    if (_v) {
-      int res = SWIG_ConvertPtr(argv[1], 0, SWIGTYPE_p_OutputDataT_double_t, SWIG_POINTER_NO_NULL | 0);
-      _v = SWIG_CheckState(res);
-      if (_v) {
-        return _wrap_ISimulation_convertData__SWIG_1(self, argc, argv);
-      }
-    }
-  }
-  if (argc == 3) {
-    int _v;
-    void *vptr = 0;
-    int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_ISimulation, 0);
-    _v = SWIG_CheckState(res);
-    if (_v) {
-      int res = SWIG_ConvertPtr(argv[1], 0, SWIGTYPE_p_OutputDataT_double_t, SWIG_POINTER_NO_NULL | 0);
-      _v = SWIG_CheckState(res);
-      if (_v) {
-        {
-          int res = SWIG_AsVal_bool(argv[2], NULL);
-          _v = SWIG_CheckState(res);
-        }
-        if (_v) {
-          return _wrap_ISimulation_convertData__SWIG_0(self, argc, argv);
-        }
-      }
-    }
-  }
-  
-fail:
-  SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'ISimulation_convertData'.\n"
-    "  Possible C/C++ prototypes are:\n"
-    "    ISimulation::convertData(OutputData< double > const &,bool)\n"
-    "    ISimulation::convertData(OutputData< double > const &)\n");
-  return 0;
-}
-
-
 SWIGINTERN PyObject *ISimulation_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *obj;
   if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
@@ -44705,13 +44582,6 @@ static PyMethodDef SwigMethods[] = {
 		"std::vector< const INode * > ISimulation::getChildren() const\n"
 		"\n"
 		""},
-	 { "ISimulation_convertData", _wrap_ISimulation_convertData, METH_VARARGS, "\n"
-		"ISimulation_convertData(ISimulation self, OutputData< double > const & data, bool put_masked_areas_to_zero=True) -> SimulationResult\n"
-		"SimulationResult ISimulation::convertData(const OutputData< double > &data, bool put_masked_areas_to_zero=true)\n"
-		"\n"
-		"Convert user data to SimulationResult object for later drawing in various axes units. User data will be cropped to the ROI defined in the simulation, amplitudes in areas corresponding to the masked areas of the detector will be set to zero. \n"
-		"\n"
-		""},
 	 { "ISimulation_swigregister", ISimulation_swigregister, METH_O, NULL},
 	 { "delete_ISimulation2D", _wrap_delete_ISimulation2D, METH_O, "\n"
 		"delete_ISimulation2D(ISimulation2D self)\n"
@@ -45585,7 +45455,6 @@ static swig_type_info _swigt__p_IntensityFunctionSqrt = {"_p_IntensityFunctionSq
 static swig_type_info _swigt__p_IterationInfo = {"_p_IterationInfo", "IterationInfo *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_MultiLayer = {"_p_MultiLayer", "MultiLayer *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_OffSpecularSimulation = {"_p_OffSpecularSimulation", "OffSpecularSimulation *", 0, 0, (void*)0, 0};
-static swig_type_info _swigt__p_OutputDataT_double_t = {"_p_OutputDataT_double_t", "OutputData< double > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_ParameterDistribution = {"_p_ParameterDistribution", "ParameterDistribution *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_PoissonNoiseBackground = {"_p_PoissonNoiseBackground", "PoissonNoiseBackground *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_ProgressHandler__Callback_t = {"_p_ProgressHandler__Callback_t", "ProgressHandler::Callback_t *", 0, 0, (void*)0, 0};
@@ -45695,7 +45564,6 @@ static swig_type_info *swig_type_initial[] = {
   &_swigt__p_IterationInfo,
   &_swigt__p_MultiLayer,
   &_swigt__p_OffSpecularSimulation,
-  &_swigt__p_OutputDataT_double_t,
   &_swigt__p_ParameterDistribution,
   &_swigt__p_ParameterPool,
   &_swigt__p_PoissonNoiseBackground,
@@ -45807,7 +45675,6 @@ static swig_cast_info _swigc__p_IntensityFunctionSqrt[] = {  {&_swigt__p_Intensi
 static swig_cast_info _swigc__p_IterationInfo[] = {  {&_swigt__p_IterationInfo, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_MultiLayer[] = {  {&_swigt__p_MultiLayer, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_OffSpecularSimulation[] = {  {&_swigt__p_OffSpecularSimulation, 0, 0, 0},{0, 0, 0, 0}};
-static swig_cast_info _swigc__p_OutputDataT_double_t[] = {  {&_swigt__p_OutputDataT_double_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_ParameterDistribution[] = {  {&_swigt__p_ParameterDistribution, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_PoissonNoiseBackground[] = {  {&_swigt__p_PoissonNoiseBackground, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_ProgressHandler__Callback_t[] = {  {&_swigt__p_ProgressHandler__Callback_t, 0, 0, 0},{0, 0, 0, 0}};
@@ -45917,7 +45784,6 @@ static swig_cast_info *swig_cast_initial[] = {
   _swigc__p_IterationInfo,
   _swigc__p_MultiLayer,
   _swigc__p_OffSpecularSimulation,
-  _swigc__p_OutputDataT_double_t,
   _swigc__p_ParameterDistribution,
   _swigc__p_ParameterPool,
   _swigc__p_PoissonNoiseBackground,
diff --git a/auto/Wrap/libBornAgainDevice.py b/auto/Wrap/libBornAgainDevice.py
index c1b8e5f6d1f..3637bb1b6e5 100644
--- a/auto/Wrap/libBornAgainDevice.py
+++ b/auto/Wrap/libBornAgainDevice.py
@@ -3724,7 +3724,12 @@ class IUnitConverter(libBornAgainBase.ICloneable):
     r"""
 
 
-    Interface to provide axis translations to different units for simulation output
+    Interface to provide axis translations to different units for simulation output.
+
+    Child classes are currently declared in
+     Device/Detector/SimpleUnitConverters.h,
+
+    Core/Scan/UnitConverter1D.h.
 
     C++ includes: IUnitConverter.h
 
-- 
GitLab