From d912b95c27c8362f1df4af6e13c1a266039dfa75 Mon Sep 17 00:00:00 2001
From: Walter Van Herck <w.van.herck@fz-juelich.de>
Date: Tue, 19 Jun 2018 12:04:39 +0200
Subject: [PATCH] Move creation of averaged multilayer out of DWBAComputation

---
 Core/Computation/DWBAComputation.cpp       | 47 ++----------
 Core/Computation/DWBAComputation.h         |  6 --
 Core/Computation/DWBASingleComputation.cpp | 12 +--
 Core/Computation/DWBASingleComputation.h   |  4 +-
 Core/Computation/IComputationUtils.cpp     | 89 +++++++++++++++++++++-
 Core/Computation/IComputationUtils.h       | 10 +++
 Core/Material/MaterialFactoryFuncs.cpp     |  2 +-
 Core/Material/MaterialFactoryFuncs.h       |  2 +-
 Tests/UnitTests/Core/Other/MaterialTest.h  |  8 +-
 9 files changed, 114 insertions(+), 66 deletions(-)

diff --git a/Core/Computation/DWBAComputation.cpp b/Core/Computation/DWBAComputation.cpp
index d49425f498b..a8a1ecbbe26 100644
--- a/Core/Computation/DWBAComputation.cpp
+++ b/Core/Computation/DWBAComputation.cpp
@@ -75,46 +75,13 @@ void DWBAComputation::runProtected()
     }
 }
 
-std::unique_ptr<MultiLayer> DWBAComputation::getAveragedMultilayer() const
-{
-    std::map<size_t, std::vector<HomogeneousRegion>> region_map;
-    m_single_computation.mergeRegionMap(region_map);
-    std::unique_ptr<MultiLayer> P_result(mP_multi_layer->clone());
-    auto last_layer_index = P_result->numberOfLayers()-1;
-    for (auto& entry : region_map)
-    {
-        auto i_layer = entry.first;
-        if (i_layer==0 || i_layer==last_layer_index)
-            continue;  // skip semi-infinite layers
-        auto layer_mat = P_result->layerMaterial(i_layer);
-        if (!checkRegions(entry.second))
-            throw std::runtime_error("DWBAComputation::getAveragedMultilayer: "
-                                     "total volumetric fraction of particles exceeds 1!");
-        auto new_mat = createAveragedMaterial(layer_mat, entry.second);
-        P_result->setLayerMaterial(i_layer, new_mat);
-    }
-    return P_result;
-}
-
-std::unique_ptr<MultiLayer> DWBAComputation::getMultilayerForFresnel() const
-{
-    std::unique_ptr<MultiLayer> P_result = m_sim_options.useAvgMaterials()
-                                           ? getAveragedMultilayer()
-                                           : std::unique_ptr<MultiLayer>(mP_multi_layer->clone());
-    P_result->initBFields();
-    return P_result;
-}
-
 void DWBAComputation::initFresnelMap()
 {
-    auto multilayer = getMultilayerForFresnel();
-    mP_fresnel_map->setMultilayer(*multilayer);
-}
-
-bool DWBAComputation::checkRegions(const std::vector<HomogeneousRegion>& regions) const
-{
-    double total_fraction = 0;
-    for (auto& region : regions)
-        total_fraction += region.m_volume;
-    return (total_fraction >= 0 && total_fraction <= 1);
+    auto region_map = m_single_computation.regionMap();
+    std::unique_ptr<MultiLayer> P_multilayer =
+            m_sim_options.useAvgMaterials()
+                 ? IComputationUtils::CreateAveragedMultilayer(*mP_multi_layer, region_map)
+                 : std::unique_ptr<MultiLayer>(mP_multi_layer->clone());
+    P_multilayer->initBFields();
+    mP_fresnel_map->setMultilayer(*P_multilayer);
 }
diff --git a/Core/Computation/DWBAComputation.h b/Core/Computation/DWBAComputation.h
index 86b9d92e5d0..34d76386d54 100644
--- a/Core/Computation/DWBAComputation.h
+++ b/Core/Computation/DWBAComputation.h
@@ -41,14 +41,8 @@ public:
 
 private:
     void runProtected() override;
-    // creates a multilayer that contains averaged materials, for use in Fresnel calculations
-    std::unique_ptr<MultiLayer> getAveragedMultilayer() const;
-    // creates a multilayer for use in Fresnel calculations; if needed, it calculates average
-    // materials and precalculates the magnetic B fields
-    std::unique_ptr<MultiLayer> getMultilayerForFresnel() const;
     // sets the correct layer materials for the Fresnel map to use
     void initFresnelMap();
-    bool checkRegions(const std::vector<HomogeneousRegion>& regions) const;
 
     std::vector<SimulationElement>::iterator m_begin_it, m_end_it; //!< these iterators define the span of detector bins this simulation will work on
     std::unique_ptr<IFresnelMap> mP_fresnel_map; //!< Contains the information, necessary to calculate the Fresnel coefficients.
diff --git a/Core/Computation/DWBASingleComputation.cpp b/Core/Computation/DWBASingleComputation.cpp
index 4d36aeee67a..45769b32ccf 100644
--- a/Core/Computation/DWBASingleComputation.cpp
+++ b/Core/Computation/DWBASingleComputation.cpp
@@ -33,6 +33,7 @@ void DWBASingleComputation::setProgressHandler(ProgressHandler* p_progress)
 void DWBASingleComputation::addLayoutComputation(ParticleLayoutComputation* p_layout_comp)
 {
     m_layout_comps.emplace_back(p_layout_comp);
+    p_layout_comp->mergeRegionMap(m_region_map);
 }
 
 void DWBASingleComputation::setRoughnessComputation(RoughMultiLayerComputation* p_roughness_comp)
@@ -61,14 +62,7 @@ void DWBASingleComputation::compute(SimulationElement& elem) const
     }
 }
 
-void DWBASingleComputation::mergeRegionMap(
-        std::map<size_t, std::vector<HomogeneousRegion> >& region_map) const
+const std::map<size_t, std::vector<HomogeneousRegion>>& DWBASingleComputation::regionMap() const
 {
-    for (auto& layout_comp : m_layout_comps) {
-        layout_comp->mergeRegionMap(region_map);
-    }
+    return m_region_map;
 }
-
-static_assert(std::is_move_constructible<DWBASingleComputation>::value,
-              "DWBASingleComputation should be move constructable");
-
diff --git a/Core/Computation/DWBASingleComputation.h b/Core/Computation/DWBASingleComputation.h
index fbec6519378..4e5fc1de414 100644
--- a/Core/Computation/DWBASingleComputation.h
+++ b/Core/Computation/DWBASingleComputation.h
@@ -48,8 +48,8 @@ public:
     void setSpecularBinComputation(GISASSpecularComputation* p_spec_comp);
     void compute(SimulationElement& elem) const;
 
-    //! Merges its region map into the given one (notice non-const reference parameter)
-    void mergeRegionMap(std::map<size_t, std::vector<HomogeneousRegion>>& region_map) const;
+    //! Retrieves a map of regions for the calculation of averaged layers
+    const std::map<size_t, std::vector<HomogeneousRegion>>& regionMap() const;
 
 private:
     std::vector<std::unique_ptr<ParticleLayoutComputation>> m_layout_comps;
diff --git a/Core/Computation/IComputationUtils.cpp b/Core/Computation/IComputationUtils.cpp
index 9f877e78dc5..138a573e47c 100644
--- a/Core/Computation/IComputationUtils.cpp
+++ b/Core/Computation/IComputationUtils.cpp
@@ -13,19 +13,27 @@
 // ************************************************************************** //
 
 #include "IComputationUtils.h"
+#include "ILayout.h"
+#include "IParticle.h"
+#include "Layer.h"
+#include "MaterialFactoryFuncs.h"
 #include "MatrixFresnelMap.h"
 #include "MultiLayer.h"
 #include "ScalarFresnelMap.h"
 #include "SimulationOptions.h"
+#include "SlicedFormFactorList.h"
 
-namespace IComputationUtils {
+namespace {
+void ScaleRegionMap(std::map<size_t, std::vector<HomogeneousRegion>>& region_map, double factor);
+bool CheckRegions(const std::vector<HomogeneousRegion>& regions);
+}
 
+namespace IComputationUtils {
 std::unique_ptr<IFresnelMap> CreateFresnelMap(const MultiLayer& multilayer,
                                               const SimulationOptions& sim_options,
                                               bool allow_average_layers)
 {
     std::unique_ptr<IFresnelMap> P_result;
-
     if (!multilayer.requiresMatrixRTCoefficients())
         P_result.reset(new ScalarFresnelMap());
     else
@@ -37,8 +45,83 @@ std::unique_ptr<IFresnelMap> CreateFresnelMap(const MultiLayer& multilayer,
     if (!allow_average_layers && sim_options.useAvgMaterials())
         throw std::runtime_error("Error in IComputationUtils::CreateFresnelMap: using averaged "
                                  "materials is not allowed for this computation");
-
     P_result->setMultilayer(multilayer);
     return P_result;
 }
+
+std::unique_ptr<MultiLayer> CreateAveragedMultilayer(
+        const MultiLayer& multilayer,
+        const std::map<size_t, std::vector<HomogeneousRegion>>& region_map)
+{
+    auto local_region_map = region_map.size() == 0
+                            ? GetRegionMap(multilayer)
+                            : region_map;
+    std::unique_ptr<MultiLayer> P_result(multilayer.clone());
+    auto last_layer_index = P_result->numberOfLayers()-1;
+    for (auto& entry : local_region_map)
+    {
+        auto i_layer = entry.first;
+        if (i_layer==0 || i_layer==last_layer_index)
+            continue;  // skip semi-infinite layers
+        auto layer_mat = P_result->layerMaterial(i_layer);
+        if (!CheckRegions(entry.second))
+            throw std::runtime_error("IComputationUtils::CreateAveragedMultilayer: "
+                                     "total volumetric fraction of particles exceeds 1!");
+        auto new_mat = CreateAveragedMaterial(layer_mat, entry.second);
+        P_result->setLayerMaterial(i_layer, new_mat);
+    }
+    return P_result;
+}
+
+std::map<size_t, std::vector<HomogeneousRegion>> GetRegionMap(const MultiLayer& multilayer)
+{
+    std::map<size_t, std::vector<HomogeneousRegion>> result_map;
+    size_t nLayers = multilayer.numberOfLayers();
+    for (size_t i=0; i<nLayers; ++i) {
+        const Layer* layer = multilayer.layer(i);
+        for (auto p_layout : layer->layouts()) {
+            double layout_abundance = p_layout->getTotalAbundance();
+            double scale_factor = p_layout->totalParticleSurfaceDensity()/layout_abundance;
+            for (const IParticle* particle: p_layout->particles()) {
+                auto sliced_ffs = SlicedFormFactorList::CreateSlicedFormFactors(
+                                      *particle, multilayer, i);
+                auto particle_region_map = sliced_ffs.regionMap();
+                double particle_scaling = particle->abundance()*scale_factor;
+                ScaleRegionMap(particle_region_map, particle_scaling);
+                MergeRegionMap(result_map, particle_region_map);
+            }
+        }
+    }
+    return result_map;
 }
+
+void MergeRegionMap(std::map<size_t, std::vector<HomogeneousRegion>>& dest,
+                    const std::map<size_t, std::vector<HomogeneousRegion>>& source)
+{
+    for (auto& entry : source) {
+        size_t layer_index = entry.first;
+        auto regions = entry.second;
+        dest[layer_index].insert(dest[layer_index].begin(), regions.begin(), regions.end());
+    }
+}
+
+}  // namespace IComputationUtils
+
+namespace {
+void ScaleRegionMap(std::map<size_t, std::vector<HomogeneousRegion>>& region_map, double factor)
+{
+    for (auto& entry : region_map) {
+        for (auto& region : entry.second) {
+            region.m_volume *= factor;
+        }
+    }
+}
+bool CheckRegions(const std::vector<HomogeneousRegion>& regions)
+{
+    double total_fraction = 0.0;
+    for (auto& region : regions)
+        total_fraction += region.m_volume;
+    return (total_fraction >= 0 && total_fraction <= 1);
+}
+}  // unnamed namespace
+
diff --git a/Core/Computation/IComputationUtils.h b/Core/Computation/IComputationUtils.h
index a577286e3f0..2910068fcc6 100644
--- a/Core/Computation/IComputationUtils.h
+++ b/Core/Computation/IComputationUtils.h
@@ -15,8 +15,11 @@
 #ifndef ICOMPUTATIONUTILS_H
 #define ICOMPUTATIONUTILS_H
 
+#include <map>
 #include <memory>
+#include <vector>
 
+class HomogeneousRegion;
 class IFresnelMap;
 class MultiLayer;
 class SimulationOptions;
@@ -25,6 +28,13 @@ namespace IComputationUtils {
 std::unique_ptr<IFresnelMap> CreateFresnelMap(const MultiLayer& multilayer,
                                               const SimulationOptions& sim_options,
                                               bool allow_average_layers=true);
+// creates a multilayer that contains averaged materials, for use in Fresnel calculations
+std::unique_ptr<MultiLayer> CreateAveragedMultilayer(
+        const MultiLayer& multilayer,
+        const std::map<size_t, std::vector<HomogeneousRegion>>& region_map = {});
+std::map<size_t, std::vector<HomogeneousRegion>> GetRegionMap(const MultiLayer& multilayer);
+void MergeRegionMap(std::map<size_t, std::vector<HomogeneousRegion>>& dest,
+                    const std::map<size_t, std::vector<HomogeneousRegion>>& source);
 }
 
 #endif  // ICOMPUTATIONUTILS_H
diff --git a/Core/Material/MaterialFactoryFuncs.cpp b/Core/Material/MaterialFactoryFuncs.cpp
index 065aad2f102..3379283153b 100644
--- a/Core/Material/MaterialFactoryFuncs.cpp
+++ b/Core/Material/MaterialFactoryFuncs.cpp
@@ -62,7 +62,7 @@ Material MaterialBySLD(const std::string& name, double sld_real, double sld_imag
     return Material(std::move(mat_impl));
 }
 
-Material createAveragedMaterial(const Material& layer_mat,
+Material CreateAveragedMaterial(const Material& layer_mat,
                                 const std::vector<HomogeneousRegion>& regions)
 {
     // determine the type of returned material
diff --git a/Core/Material/MaterialFactoryFuncs.h b/Core/Material/MaterialFactoryFuncs.h
index c5f4b16f094..905984871e0 100644
--- a/Core/Material/MaterialFactoryFuncs.h
+++ b/Core/Material/MaterialFactoryFuncs.h
@@ -68,7 +68,7 @@ BA_CORE_API_ Material MaterialBySLD(const std::string& name, double sld_real, do
 
 //! Creates averaged material. Square refractive index of returned material is arithmetic mean over
 //! _regions_ and _layer_mat_. Magnetization (if present) is averaged linearly.
-BA_CORE_API_ Material createAveragedMaterial(const Material& layer_mat,
+BA_CORE_API_ Material CreateAveragedMaterial(const Material& layer_mat,
                                              const std::vector<HomogeneousRegion>& regions);
 
 #endif //SWIG
diff --git a/Tests/UnitTests/Core/Other/MaterialTest.h b/Tests/UnitTests/Core/Other/MaterialTest.h
index 5c2a787c15f..4987c7e3392 100644
--- a/Tests/UnitTests/Core/Other/MaterialTest.h
+++ b/Tests/UnitTests/Core/Other/MaterialTest.h
@@ -163,7 +163,7 @@ TEST_F(MaterialTest, AveragedMaterialTest)
     const std::vector<HomogeneousRegion> regions
         = {HomogeneousRegion{0.25, material}, HomogeneousRegion{0.25, material}};
 
-    const Material material_avr = createAveragedMaterial(material, regions);
+    const Material material_avr = CreateAveragedMaterial(material, regions);
     EXPECT_EQ(material_avr.getName(), material.getName() + "_avg");
     EXPECT_EQ(material_avr.magnetization(), magnetization);
     EXPECT_DOUBLE_EQ(material_avr.materialData().real(), 0.5);
@@ -171,7 +171,7 @@ TEST_F(MaterialTest, AveragedMaterialTest)
     EXPECT_TRUE(material_avr.typeID() == MATERIAL_TYPES::RefractiveMaterial);
 
     const Material material2 = MaterialBySLD();
-    const Material material_avr2 = createAveragedMaterial(material2, regions);
+    const Material material_avr2 = CreateAveragedMaterial(material2, regions);
     const complex_t expected_res = std::conj(1.0 - std::sqrt(complex_t(0.5, 0.25)));
     EXPECT_DOUBLE_EQ(material_avr2.materialData().real(), expected_res.real());
     EXPECT_DOUBLE_EQ(material_avr2.materialData().imag(), expected_res.imag());
@@ -179,12 +179,12 @@ TEST_F(MaterialTest, AveragedMaterialTest)
     EXPECT_TRUE(material_avr2.typeID() == MATERIAL_TYPES::RefractiveMaterial);
 
     const Material material3 = MaterialBySLD("Material3", 0.5, 0.5, magnetization);
-    EXPECT_THROW(createAveragedMaterial(material3, regions), std::runtime_error);
+    EXPECT_THROW(CreateAveragedMaterial(material3, regions), std::runtime_error);
 
     const Material material4 = HomogeneousMaterial();
     const std::vector<HomogeneousRegion> regions2
         = {HomogeneousRegion{0.25, material3}, HomogeneousRegion{0.25, material3}};
-    const Material material_avr3 = createAveragedMaterial(material4, regions2);
+    const Material material_avr3 = CreateAveragedMaterial(material4, regions2);
     EXPECT_DOUBLE_EQ(material_avr3.materialData().real(), 0.25);
     EXPECT_DOUBLE_EQ(material_avr3.materialData().imag(), 0.25);
     EXPECT_EQ(material_avr3.magnetization(), kvector_t(0.5, 0.0, 0.0));
-- 
GitLab