From df704e193fcf6cbab0d512ecded411ae19d05efa Mon Sep 17 00:00:00 2001
From: ralfulrich <ralf.ulrich@kit.edu>
Date: Wed, 17 Feb 2021 10:23:27 +0100
Subject: [PATCH] added weights to stack

---
 corsika/detail/setup/SetupStack.hpp           |   3 +-
 corsika/stack/GeometryNodeStackExtension.hpp  |   2 +-
 corsika/stack/WeightStackExtension.hpp        | 119 ++++++++++++++++++
 tests/stack/CMakeLists.txt                    |   1 +
 .../stack/testGeometryNodeStackExtension.cpp  |   2 +-
 tests/stack/testWeightStackExtension.cpp      |  85 +++++++++++++
 6 files changed, 209 insertions(+), 3 deletions(-)
 create mode 100644 corsika/stack/WeightStackExtension.hpp
 create mode 100644 tests/stack/testWeightStackExtension.cpp

diff --git a/corsika/detail/setup/SetupStack.hpp b/corsika/detail/setup/SetupStack.hpp
index d31e940f6..6577b023f 100644
--- a/corsika/detail/setup/SetupStack.hpp
+++ b/corsika/detail/setup/SetupStack.hpp
@@ -11,6 +11,7 @@
 #include <corsika/framework/stack/CombinedStack.hpp>
 #include <corsika/stack/GeometryNodeStackExtension.hpp>
 #include <corsika/stack/NuclearStackExtension.hpp>
+#include <corsika/stack/WeightStackExtension.hpp>
 #include <corsika/stack/history/HistorySecondaryProducer.hpp>
 #include <corsika/stack/history/HistoryStackExtension.hpp>
 
@@ -27,7 +28,7 @@ namespace corsika {
     // environment:
     template <typename TStackIter>
     using SetupGeometryDataInterface =
-        typename node::MakeGeometryDataInterface<TStackIter, setup::Environment>::type;
+        typename node::make_GeometryDataInterface<TStackIter, setup::Environment>::type;
 
     // combine particle data stack with geometry information for tracking
     template <typename TStackIter>
diff --git a/corsika/stack/GeometryNodeStackExtension.hpp b/corsika/stack/GeometryNodeStackExtension.hpp
index e7298bea4..9cd81cc77 100644
--- a/corsika/stack/GeometryNodeStackExtension.hpp
+++ b/corsika/stack/GeometryNodeStackExtension.hpp
@@ -117,7 +117,7 @@ namespace corsika::node {
   };
 
   template <typename T, typename TEnv>
-  struct MakeGeometryDataInterface {
+  struct make_GeometryDataInterface {
     typedef GeometryDataInterface<T, TEnv> type;
   };
 
diff --git a/corsika/stack/WeightStackExtension.hpp b/corsika/stack/WeightStackExtension.hpp
new file mode 100644
index 000000000..2cbcce482
--- /dev/null
+++ b/corsika/stack/WeightStackExtension.hpp
@@ -0,0 +1,119 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace corsika::weights {
+
+  /**
+   * Describe "particle weights" on a Stack.
+   *
+   * Corresponding defintion of a stack-readout object, the iteractor
+   * dereference operator will deliver access to these function
+   * defintion of a stack-readout object, the iteractor dereference
+   * operator will deliver access to these function
+   */
+
+  /**
+   * \tparam TParentStack The stack to be exteneded here with weight data
+   */
+  template <typename TParentStack>
+  struct WeightDataInterface : public TParentStack {
+
+    typedef TParentStack super_type;
+
+  public:
+    // default version for particle-creation from input data
+    void setParticleData(std::tuple<double> const v) { setWeight(std::get<0>(v)); }
+    void setParticleData(WeightDataInterface& parent, std::tuple<double> const) {
+      setWeight(parent.getWeight()); // copy Weight from parent particle!
+    }
+    void setParticleData() { setWeight(1); } // default weight
+    void setParticleData(WeightDataInterface& parent) {
+      setWeight(parent.getWeight()); // copy Weight from parent particle!
+    }
+
+    std::string asString() const {
+      return fmt::format("weight={}", fmt::ptr(getWeight()));
+    }
+
+    void setWeight(double const v) {
+
+      super_type::getStackData().setWeight(super_type::getIndex(), v);
+    }
+
+    double getWeight() const {
+      return super_type::getStackData().getWeight(super_type::getIndex());
+    }
+  };
+
+  // definition of stack-data object to store geometry information
+
+  /**
+   * @class WeightData
+   *
+   * definition of stack-data object to store geometry information
+   */
+  class WeightData {
+
+  public:
+    typedef std::vector<double> weight_vector_type;
+
+    WeightData() = default;
+
+    WeightData(WeightData const&) = default;
+
+    WeightData(WeightData&&) = default;
+
+    WeightData& operator=(WeightData const&) = default;
+
+    WeightData& operator=(WeightData&&) = default;
+
+    // these functions are needed for the Stack interface
+    void clear() { weight_vector_.clear(); }
+
+    unsigned int getSize() const { return weight_vector_.size(); }
+
+    unsigned int getCapacity() const { return weight_vector_.size(); }
+
+    void copy(int const i1, int const i2) { weight_vector_[i2] = weight_vector_[i1]; }
+
+    void swap(int const i1, int const i2) {
+      std::swap(weight_vector_[i1], weight_vector_[i2]);
+    }
+
+    // custom data access function
+    void setWeight(int const i, double const v) { weight_vector_[i] = v; }
+
+    double getWeight(int const i) const { return weight_vector_[i]; }
+
+    // these functions are also needed by the Stack interface
+    void incrementSize() { weight_vector_.push_back(1); } // default weight
+
+    void decrementSize() {
+      if (weight_vector_.size() > 0) { weight_vector_.pop_back(); }
+    }
+
+    // custom private data section
+  private:
+    weight_vector_type weight_vector_;
+  };
+
+  template <typename TParentStack>
+  struct make_WeightDataInterface {
+    typedef WeightDataInterface<TParentStack> type;
+  };
+
+} // namespace corsika::weights
diff --git a/tests/stack/CMakeLists.txt b/tests/stack/CMakeLists.txt
index 8fe1070f1..17305b6a4 100644
--- a/tests/stack/CMakeLists.txt
+++ b/tests/stack/CMakeLists.txt
@@ -3,6 +3,7 @@ set (test_stack_sources
   testHistoryStack.cpp
   testHistoryView.cpp
   testGeometryNodeStackExtension.cpp
+  testWeightStackExtension.cpp
   testDummyStack.cpp
   testVectorStack.cpp
   testNuclearStackExtension.cpp
diff --git a/tests/stack/testGeometryNodeStackExtension.cpp b/tests/stack/testGeometryNodeStackExtension.cpp
index 8256927d9..c623709e6 100644
--- a/tests/stack/testGeometryNodeStackExtension.cpp
+++ b/tests/stack/testGeometryNodeStackExtension.cpp
@@ -26,7 +26,7 @@ public:
 // the GeometryNode stack needs to know the type of geometry-nodes from the DummyEnv:
 template <typename TStackIter>
 using DummyGeometryDataInterface =
-    typename node::MakeGeometryDataInterface<TStackIter, DummyEnv>::type;
+    typename node::make_GeometryDataInterface<TStackIter, DummyEnv>::type;
 
 // combine dummy stack with geometry information for tracking
 template <typename TStackIter>
diff --git a/tests/stack/testWeightStackExtension.cpp b/tests/stack/testWeightStackExtension.cpp
new file mode 100644
index 000000000..715051b05
--- /dev/null
+++ b/tests/stack/testWeightStackExtension.cpp
@@ -0,0 +1,85 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#include <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/stack/DummyStack.hpp>
+#include <corsika/stack/WeightStackExtension.hpp>
+
+using namespace corsika;
+
+#include <catch2/catch.hpp>
+
+#include <iostream>
+using namespace std;
+
+// the Weight stack:
+template <typename TStackIter>
+using DummyWeightDataInterface =
+    typename weights::make_WeightDataInterface<TStackIter>::type;
+
+// combine dummy stack with geometry information for tracking
+template <typename TStackIter>
+using StackWithGeometryInterface =
+    CombinedParticleInterface<dummy_stack::DummyStack::pi_type, DummyWeightDataInterface,
+                              TStackIter>;
+
+using TestStack =
+    CombinedStack<typename dummy_stack::DummyStack::stack_implementation_type,
+                  weights::WeightData, StackWithGeometryInterface>;
+
+TEST_CASE("WeightStackExtension", "[stack]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  dummy_stack::NoData noData;
+
+  SECTION("write weights") {
+
+    double const weight = 5.1;
+
+    TestStack s;
+    s.addParticle(std::make_tuple(noData), std::tuple<double>{weight});
+
+    CHECK(s.getEntries() == 1);
+  }
+
+  SECTION("write/read weights") {
+    double const weight = 15;
+
+    TestStack s;
+    auto p = s.addParticle(std::make_tuple(noData));
+    p.setWeight(weight);
+    CHECK(s.getEntries() == 1);
+
+    const auto pout = s.getNextParticle();
+    CHECK(pout.getWeight() == 15);
+  }
+
+  SECTION("stack fill and cleanup") {
+
+    double const weight = 16;
+
+    TestStack s;
+    // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
+    for (int i = 0; i < 99; ++i) {
+      auto p = s.addParticle(std::tuple<dummy_stack::NoData>{noData});
+      p.setWeight(weight);
+    }
+
+    CHECK(s.getEntries() == 99);
+    double v = 0;
+    for (int i = 0; i < 99; ++i) {
+      auto p = s.getNextParticle();
+      v += p.getWeight();
+      p.erase();
+    }
+    CHECK(v == 99 * weight);
+    CHECK(s.getEntries() == 0);
+  }
+}
-- 
GitLab