From fdf7af4b3067762d476d9fd6814218b3174961c4 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 19 Dec 2022 12:33:53 +0100
Subject: [PATCH] provide way to scale reflectometry simulation ex post; use it
 in RealLifeReflectometryFitting.py

---
 Device/Data/Datafield.cpp                     | 11 +--
 Device/Data/Datafield.h                       |  5 +-
 Device/Histo/SimulationResult.cpp             |  6 --
 Device/Histo/SimulationResult.h               |  4 +-
 .../specular/RealLifeReflectometryFitting.py  |  6 +-
 auto/Wrap/libBornAgainDevice.py               | 16 ++--
 auto/Wrap/libBornAgainDevice_wrap.cpp         | 92 ++++++++++---------
 7 files changed, 70 insertions(+), 70 deletions(-)

diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index fd2be8c9c2f..ab4bfbe494b 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -156,15 +156,14 @@ std::vector<double> Datafield::flatVector() const
     return result;
 }
 
-Datafield* Datafield::normalizedToMaximum() const
+void Datafield::scale(double factor)
 {
-    double maxval = maxVal();
-    ASSERT(maxval > 0);
-    std::vector<double> out(frame().size());
     for (size_t i = 0; i < frame().size(); ++i)
-        out[i] = m_values[i] / maxval;
+        m_values[i] *= factor;
 
-    return new Datafield(m_frame->cloned_axes(), out);
+    if (!m_errSigmas.empty())
+        for (size_t i = 0; i < frame().size(); ++i)
+            m_errSigmas[i] *= factor;
 }
 
 double Datafield::maxVal() const
diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index f5428941dcc..7737e149e2a 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -81,6 +81,9 @@ public:
     //! ied accessor (const)
     const double& operator[](size_t i) const { return m_values[i]; }
 
+    //! Multiplies contents by constant factor
+    void scale(double factor);
+
     // helpers
 
     //! Returns true if object have same dimensions and number of axes bins
@@ -97,8 +100,6 @@ public:
     PyObject* npArray() const;
 #endif
 
-    Datafield* normalizedToMaximum() const;
-
     //! Project a 2D histogram into 1D histogram along X. The projection is made
     //! from all bins along y-axis.
     Datafield* xProjection() const;
diff --git a/Device/Histo/SimulationResult.cpp b/Device/Histo/SimulationResult.cpp
index 653582ee20a..9e0368af47d 100644
--- a/Device/Histo/SimulationResult.cpp
+++ b/Device/Histo/SimulationResult.cpp
@@ -103,12 +103,6 @@ const double& SimulationResult::operator[](size_t i) const
     return (*m_data)[i];
 }
 
-SimulationResult SimulationResult::relativeToMaximum() const
-{
-    std::unique_ptr<Datafield> data2(m_data->normalizedToMaximum());
-    return {*data2, m_coordsys->clone()};
-}
-
 #ifdef BORNAGAIN_PYTHON
 PyObject* SimulationResult::array(Coords units) const
 {
diff --git a/Device/Histo/SimulationResult.h b/Device/Histo/SimulationResult.h
index 079ef64c778..5f856f28fc4 100644
--- a/Device/Histo/SimulationResult.h
+++ b/Device/Histo/SimulationResult.h
@@ -51,15 +51,13 @@ public:
     const ICoordSystem& converter() const;
 
     //! Data element access
+    Datafield& data_field() { return *m_data; } // TODO disambiguate name from datafield()
     size_t rank() const;
     double& operator[](size_t i);
     const double& operator[](size_t i) const;
     size_t size() const;
     bool empty() const { return size() == 0; }
 
-    //! Returns modified SimulationResult: all intensities dvided by maximum intensity
-    SimulationResult relativeToMaximum() const;
-
     //! Returns intensity data as Python numpy array
 #ifdef BORNAGAIN_PYTHON
     PyObject* array(Coords units = Coords::UNDEFINED) const;
diff --git a/Examples/fit/specular/RealLifeReflectometryFitting.py b/Examples/fit/specular/RealLifeReflectometryFitting.py
index c9229d7bc81..c2f77907fbd 100755
--- a/Examples/fit/specular/RealLifeReflectometryFitting.py
+++ b/Examples/fit/specular/RealLifeReflectometryFitting.py
@@ -101,7 +101,6 @@ def create_simulation(sample, arg_dict, bin_start, bin_end):
     scan.setFootprintFactor(footprint)
 
     simulation = ba.SpecularSimulation(scan, sample)
-    simulation.beam().setIntensity(arg_dict["intensity"])
     return simulation
 
 
@@ -141,7 +140,10 @@ def run_simulation(arg_dict, bin_start=0, bin_end=-1):
     sample = buildSample(arg_dict)
     simulation = create_simulation(sample, arg_dict, bin_start, bin_end)
 
-    return simulation.simulate()
+    result = simulation.simulate()
+    result.data_field().scale(arg_dict["intensity"])
+
+    return result
 
 
 def chi_2(real_data, sim_data, weights):
diff --git a/auto/Wrap/libBornAgainDevice.py b/auto/Wrap/libBornAgainDevice.py
index f5f2313d370..97ea1d4b488 100644
--- a/auto/Wrap/libBornAgainDevice.py
+++ b/auto/Wrap/libBornAgainDevice.py
@@ -2108,6 +2108,10 @@ class Datafield(object):
         r"""setVector(Datafield self, vdouble1d_t data_vector)"""
         return _libBornAgainDevice.Datafield_setVector(self, data_vector)
 
+    def scale(self, factor):
+        r"""scale(Datafield self, double factor)"""
+        return _libBornAgainDevice.Datafield_scale(self, factor)
+
     def hasSameSizes(self, other):
         r"""hasSameSizes(Datafield self, Datafield other) -> bool"""
         return _libBornAgainDevice.Datafield_hasSameSizes(self, other)
@@ -2127,10 +2131,6 @@ class Datafield(object):
         r"""npArray(Datafield self) -> PyObject *"""
         return _libBornAgainDevice.Datafield_npArray(self)
 
-    def normalizedToMaximum(self):
-        r"""normalizedToMaximum(Datafield self) -> Datafield"""
-        return _libBornAgainDevice.Datafield_normalizedToMaximum(self)
-
     def xProjection(self, *args):
         r"""
         xProjection(Datafield self) -> Datafield
@@ -3127,6 +3127,10 @@ class SimulationResult(object):
         r"""converter(SimulationResult self) -> ICoordSystem const &"""
         return _libBornAgainDevice.SimulationResult_converter(self)
 
+    def data_field(self):
+        r"""data_field(SimulationResult self) -> Datafield"""
+        return _libBornAgainDevice.SimulationResult_data_field(self)
+
     def rank(self):
         r"""rank(SimulationResult self) -> size_t"""
         return _libBornAgainDevice.SimulationResult_rank(self)
@@ -3139,10 +3143,6 @@ class SimulationResult(object):
         r"""empty(SimulationResult self) -> bool"""
         return _libBornAgainDevice.SimulationResult_empty(self)
 
-    def relativeToMaximum(self):
-        r"""relativeToMaximum(SimulationResult self) -> SimulationResult"""
-        return _libBornAgainDevice.SimulationResult_relativeToMaximum(self)
-
     def array(self, *args):
         r"""array(SimulationResult self, Coords units=UNDEFINED) -> PyObject"""
         return _libBornAgainDevice.SimulationResult_array(self, *args)
diff --git a/auto/Wrap/libBornAgainDevice_wrap.cpp b/auto/Wrap/libBornAgainDevice_wrap.cpp
index 1d004526aa7..68e56e1740d 100644
--- a/auto/Wrap/libBornAgainDevice_wrap.cpp
+++ b/auto/Wrap/libBornAgainDevice_wrap.cpp
@@ -28018,6 +28018,35 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Datafield_scale(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Datafield *arg1 = (Datafield *) 0 ;
+  double arg2 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  double val2 ;
+  int ecode2 = 0 ;
+  PyObject *swig_obj[2] ;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Datafield_scale", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Datafield, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Datafield_scale" "', argument " "1"" of type '" "Datafield *""'"); 
+  }
+  arg1 = reinterpret_cast< Datafield * >(argp1);
+  ecode2 = SWIG_AsVal_double(swig_obj[1], &val2);
+  if (!SWIG_IsOK(ecode2)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "Datafield_scale" "', argument " "2"" of type '" "double""'");
+  } 
+  arg2 = static_cast< double >(val2);
+  (arg1)->scale(arg2);
+  resultobj = SWIG_Py_Void();
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Datafield_hasSameSizes(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   Datafield *arg1 = (Datafield *) 0 ;
@@ -28269,29 +28298,6 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_Datafield_normalizedToMaximum(PyObject *self, PyObject *args) {
-  PyObject *resultobj = 0;
-  Datafield *arg1 = (Datafield *) 0 ;
-  void *argp1 = 0 ;
-  int res1 = 0 ;
-  PyObject *swig_obj[1] ;
-  Datafield *result = 0 ;
-  
-  if (!args) SWIG_fail;
-  swig_obj[0] = args;
-  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Datafield, 0 |  0 );
-  if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Datafield_normalizedToMaximum" "', argument " "1"" of type '" "Datafield const *""'"); 
-  }
-  arg1 = reinterpret_cast< Datafield * >(argp1);
-  result = (Datafield *)((Datafield const *)arg1)->normalizedToMaximum();
-  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Datafield, 0 |  0 );
-  return resultobj;
-fail:
-  return NULL;
-}
-
-
 SWIGINTERN PyObject *_wrap_Datafield_xProjection__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {
   PyObject *resultobj = 0;
   Datafield *arg1 = (Datafield *) 0 ;
@@ -37131,30 +37137,30 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_SimulationResult_rank(PyObject *self, PyObject *args) {
+SWIGINTERN PyObject *_wrap_SimulationResult_data_field(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   SimulationResult *arg1 = (SimulationResult *) 0 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
   PyObject *swig_obj[1] ;
-  size_t result;
+  Datafield *result = 0 ;
   
   if (!args) SWIG_fail;
   swig_obj[0] = args;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_SimulationResult, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_rank" "', argument " "1"" of type '" "SimulationResult const *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_data_field" "', argument " "1"" of type '" "SimulationResult *""'"); 
   }
   arg1 = reinterpret_cast< SimulationResult * >(argp1);
-  result = ((SimulationResult const *)arg1)->rank();
-  resultobj = SWIG_From_size_t(static_cast< size_t >(result));
+  result = (Datafield *) &(arg1)->data_field();
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Datafield, 0 |  0 );
   return resultobj;
 fail:
   return NULL;
 }
 
 
-SWIGINTERN PyObject *_wrap_SimulationResult_size(PyObject *self, PyObject *args) {
+SWIGINTERN PyObject *_wrap_SimulationResult_rank(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   SimulationResult *arg1 = (SimulationResult *) 0 ;
   void *argp1 = 0 ;
@@ -37166,10 +37172,10 @@ SWIGINTERN PyObject *_wrap_SimulationResult_size(PyObject *self, PyObject *args)
   swig_obj[0] = args;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_SimulationResult, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_size" "', argument " "1"" of type '" "SimulationResult const *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_rank" "', argument " "1"" of type '" "SimulationResult const *""'"); 
   }
   arg1 = reinterpret_cast< SimulationResult * >(argp1);
-  result = ((SimulationResult const *)arg1)->size();
+  result = ((SimulationResult const *)arg1)->rank();
   resultobj = SWIG_From_size_t(static_cast< size_t >(result));
   return resultobj;
 fail:
@@ -37177,46 +37183,46 @@ fail:
 }
 
 
-SWIGINTERN PyObject *_wrap_SimulationResult_empty(PyObject *self, PyObject *args) {
+SWIGINTERN PyObject *_wrap_SimulationResult_size(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   SimulationResult *arg1 = (SimulationResult *) 0 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
   PyObject *swig_obj[1] ;
-  bool result;
+  size_t result;
   
   if (!args) SWIG_fail;
   swig_obj[0] = args;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_SimulationResult, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_empty" "', argument " "1"" of type '" "SimulationResult const *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_size" "', argument " "1"" of type '" "SimulationResult const *""'"); 
   }
   arg1 = reinterpret_cast< SimulationResult * >(argp1);
-  result = (bool)((SimulationResult const *)arg1)->empty();
-  resultobj = SWIG_From_bool(static_cast< bool >(result));
+  result = ((SimulationResult const *)arg1)->size();
+  resultobj = SWIG_From_size_t(static_cast< size_t >(result));
   return resultobj;
 fail:
   return NULL;
 }
 
 
-SWIGINTERN PyObject *_wrap_SimulationResult_relativeToMaximum(PyObject *self, PyObject *args) {
+SWIGINTERN PyObject *_wrap_SimulationResult_empty(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   SimulationResult *arg1 = (SimulationResult *) 0 ;
   void *argp1 = 0 ;
   int res1 = 0 ;
   PyObject *swig_obj[1] ;
-  SimulationResult result;
+  bool result;
   
   if (!args) SWIG_fail;
   swig_obj[0] = args;
   res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_SimulationResult, 0 |  0 );
   if (!SWIG_IsOK(res1)) {
-    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_relativeToMaximum" "', argument " "1"" of type '" "SimulationResult const *""'"); 
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SimulationResult_empty" "', argument " "1"" of type '" "SimulationResult const *""'"); 
   }
   arg1 = reinterpret_cast< SimulationResult * >(argp1);
-  result = ((SimulationResult const *)arg1)->relativeToMaximum();
-  resultobj = SWIG_NewPointerObj((new SimulationResult(result)), SWIGTYPE_p_SimulationResult, SWIG_POINTER_OWN |  0 );
+  result = (bool)((SimulationResult const *)arg1)->empty();
+  resultobj = SWIG_From_bool(static_cast< bool >(result));
   return resultobj;
 fail:
   return NULL;
@@ -38297,6 +38303,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "Datafield_minVal", _wrap_Datafield_minVal, METH_O, "Datafield_minVal(Datafield self) -> double"},
 	 { "Datafield_setAllTo", _wrap_Datafield_setAllTo, METH_VARARGS, "Datafield_setAllTo(Datafield self, double const & value)"},
 	 { "Datafield_setVector", _wrap_Datafield_setVector, METH_VARARGS, "Datafield_setVector(Datafield self, vdouble1d_t data_vector)"},
+	 { "Datafield_scale", _wrap_Datafield_scale, METH_VARARGS, "Datafield_scale(Datafield self, double factor)"},
 	 { "Datafield_hasSameSizes", _wrap_Datafield_hasSameSizes, METH_VARARGS, "Datafield_hasSameSizes(Datafield self, Datafield other) -> bool"},
 	 { "Datafield_hasSameShape", _wrap_Datafield_hasSameShape, METH_VARARGS, "Datafield_hasSameShape(Datafield self, Datafield other) -> bool"},
 	 { "Datafield_crop", _wrap_Datafield_crop, METH_VARARGS, "\n"
@@ -38304,7 +38311,6 @@ static PyMethodDef SwigMethods[] = {
 		"Datafield_crop(Datafield self, double xmin, double xmax) -> Datafield\n"
 		""},
 	 { "Datafield_npArray", _wrap_Datafield_npArray, METH_O, "Datafield_npArray(Datafield self) -> PyObject *"},
-	 { "Datafield_normalizedToMaximum", _wrap_Datafield_normalizedToMaximum, METH_O, "Datafield_normalizedToMaximum(Datafield self) -> Datafield"},
 	 { "Datafield_xProjection", _wrap_Datafield_xProjection, METH_VARARGS, "\n"
 		"Datafield_xProjection(Datafield self) -> Datafield\n"
 		"Datafield_xProjection(Datafield self, double yvalue) -> Datafield\n"
@@ -38599,10 +38605,10 @@ static PyMethodDef SwigMethods[] = {
 	 { "SimulationResult_axisMinMax", _wrap_SimulationResult_axisMinMax, METH_VARARGS, "SimulationResult_axisMinMax(SimulationResult self, size_t i, Coords units=UNDEFINED) -> pvacuum_double_t"},
 	 { "SimulationResult_axisName", _wrap_SimulationResult_axisName, METH_VARARGS, "SimulationResult_axisName(SimulationResult self, size_t i, Coords units=UNDEFINED) -> std::string"},
 	 { "SimulationResult_converter", _wrap_SimulationResult_converter, METH_O, "SimulationResult_converter(SimulationResult self) -> ICoordSystem const &"},
+	 { "SimulationResult_data_field", _wrap_SimulationResult_data_field, METH_O, "SimulationResult_data_field(SimulationResult self) -> Datafield"},
 	 { "SimulationResult_rank", _wrap_SimulationResult_rank, METH_O, "SimulationResult_rank(SimulationResult self) -> size_t"},
 	 { "SimulationResult_size", _wrap_SimulationResult_size, METH_O, "SimulationResult_size(SimulationResult self) -> size_t"},
 	 { "SimulationResult_empty", _wrap_SimulationResult_empty, METH_O, "SimulationResult_empty(SimulationResult self) -> bool"},
-	 { "SimulationResult_relativeToMaximum", _wrap_SimulationResult_relativeToMaximum, METH_O, "SimulationResult_relativeToMaximum(SimulationResult self) -> SimulationResult"},
 	 { "SimulationResult_array", _wrap_SimulationResult_array, METH_VARARGS, "SimulationResult_array(SimulationResult self, Coords units=UNDEFINED) -> PyObject"},
 	 { "SimulationResult_convertedBinCenters", _wrap_SimulationResult_convertedBinCenters, METH_VARARGS, "\n"
 		"SimulationResult_convertedBinCenters(SimulationResult self, Coords units=UNDEFINED) -> vdouble1d_t\n"
-- 
GitLab