diff --git a/Framework/StackInterface/CombinedStack.h b/Framework/StackInterface/CombinedStack.h index 44a29a6280bbf7aa61e05301d3bd9fd9adc8dd5b..7633d383bef2eb1a6131d10e3caf282d606e1adf 100644 --- a/Framework/StackInterface/CombinedStack.h +++ b/Framework/StackInterface/CombinedStack.h @@ -19,51 +19,88 @@ namespace corsika::stack { /** + * @class CombinedParticleInterface * + * You may combine two StackData object, see class CombinedStackImpl + * below, into one Stack, using a combined StackIterator (aka + * CombinedParticleInterface) interface class. + * + * This allows to add specific information to a given Stack, could + * be special information on a subset of entries + * (e.g. NuclearStackExtension) or also (multi) thinning weights for + * all particles. + * + * Many Stacks can be combined into more complex object. + * + * The two sub-stacks must both provide their independent + * ParticleInterface classes. * */ - template <template <typename> typename ParticleInterface, - template <typename> typename ParticleInterfaceAdd, typename StackIterator> + template <template <typename> typename ParticleInterfaceA, + template <typename> typename ParticleInterfaceB, typename StackIterator> class CombinedParticleInterface - : public ParticleInterfaceAdd<ParticleInterface<StackIterator>> { + : public ParticleInterfaceB<ParticleInterfaceA<StackIterator>> { - using C = - CombinedParticleInterface<ParticleInterface, ParticleInterfaceAdd, StackIterator>; - using T = ParticleInterfaceAdd<ParticleInterface<StackIterator>>; - using I = ParticleInterface<StackIterator>; + using PI_C = + CombinedParticleInterface<ParticleInterfaceA, ParticleInterfaceB, StackIterator>; + using PI_A = ParticleInterfaceA<StackIterator>; + using PI_B = ParticleInterfaceB<ParticleInterfaceA<StackIterator>>; protected: - using T::GetIndex; - using T::GetStackData; + using PI_B::GetIndex; // choose B, A would also work + using PI_B::GetStackData; // choose B, A would also work public: + /** + * @name wrapper for user functions + * @{ + * + * In this set of functions we call the user-provide + * ParticleInterface SetParticleData(...) methods, either with + * parent particle reference, or w/o. + * + * There is one implicit assumption here: if only one data tuple + * is provided for SetParticleData, the data is passed on to + * ParticleInterfaceA and the ParticleInterfaceB is + * default-initialized. There are many occasions where this is the + * desired behaviour, e.g. for thinning etc. + * + */ + template <typename... Args1> void SetParticleData(const std::tuple<Args1...> vA) { - I::SetParticleData(vA); - T::SetParticleData(); + PI_A::SetParticleData(vA); + PI_B::SetParticleData(); } template <typename... Args1, typename... Args2> void SetParticleData(const std::tuple<Args1...> vA, const std::tuple<Args2...> vB) { - I::SetParticleData(vA); - T::SetParticleData(vB); + PI_A::SetParticleData(vA); + PI_B::SetParticleData(vB); } template <typename... Args1> - void SetParticleData(C& p, const std::tuple<Args1...> vA) { + void SetParticleData(PI_C& p, const std::tuple<Args1...> vA) { // static_assert(MT<I>::has_not, "error"); - I::SetParticleData(static_cast<I&>(p), vA); // original stack - T::SetParticleData(static_cast<T&>(p)); // addon stack + PI_A::SetParticleData(static_cast<PI_A&>(p), vA); // original stack + PI_B::SetParticleData(static_cast<PI_B&>(p)); // addon stack } template <typename... Args1, typename... Args2> - void SetParticleData(C& p, const std::tuple<Args1...> vA, + void SetParticleData(PI_C& p, const std::tuple<Args1...> vA, const std::tuple<Args2...> vB) { - I::SetParticleData(static_cast<I&>(p), vA); - T::SetParticleData(static_cast<T&>(p), vB); + PI_A::SetParticleData(static_cast<PI_A&>(p), vA); + PI_B::SetParticleData(static_cast<PI_B&>(p), vB); } + ///@} }; /** - * Memory implementation of the most simple (stupid) particle stack object. + * @class CombinedStackImpl + * + * Memory implementation of a combined data stack. + * + * The two stack data user objects Stack1Impl and Stack2Impl are + * merged into one consistent Stack container object providing + * access to the combined number of data entries. */ template <typename Stack1Impl, typename Stack2Impl> class CombinedStackImpl : public Stack1Impl, public Stack2Impl { @@ -118,11 +155,17 @@ namespace corsika::stack { Stack2Impl::DecrementSize(); } - private: - /// the actual memory to store particle data - }; // end class CombinedStackImpl + /** + * Helper template alias `CombinedStack` to construct new combined + * stack from two stack data objects and a particle readout interface. + * + * Note that the Stack2Impl provides only /additional/ data to + * Stack1Impl. This is important (see above) since tuple data for + * initialization are forwarded to Stack1Impl (first). + */ + template <typename Stack1Impl, typename Stack2Impl, template <typename> typename _PI> using CombinedStack = Stack<CombinedStackImpl<Stack1Impl, Stack2Impl>, _PI>; diff --git a/Framework/StackInterface/ParticleBase.h b/Framework/StackInterface/ParticleBase.h index b5704e762a667b8ff7d4c7442a068f561d2dee8a..e3504fbeff43ca80f584b179db7eb8bd907609c8 100644 --- a/Framework/StackInterface/ParticleBase.h +++ b/Framework/StackInterface/ParticleBase.h @@ -73,8 +73,8 @@ namespace corsika::stack { * args is a variadic list of input data that has to match the * function description in the user defined ParticleInterface::AddSecondary(...) */ - template <typename... Args> - StackIterator AddSecondary(const Args... args) { + template <typename... TArgs> + StackIterator AddSecondary(const TArgs... args) { return GetStack().AddSecondary(GetIterator(), args...); } diff --git a/Framework/StackInterface/SecondaryView.h b/Framework/StackInterface/SecondaryView.h index ac2e3ed19e462ecaca7e7def0bfd2abcb58e31ac..3e781f8e6c70edbca333c5ffd53d6f630961678d 100644 --- a/Framework/StackInterface/SecondaryView.h +++ b/Framework/StackInterface/SecondaryView.h @@ -68,10 +68,10 @@ namespace corsika::stack { * the constructor of the SecondaryView class * @{ */ - using InnerStackTypeV = Stack<StackDataType, ParticleInterface>; - typedef StackIteratorInterface<typename std::remove_reference<StackDataType>::type, - ParticleInterface, InnerStackTypeV> - StackIteratorV; + using InnerStackTypeValue = Stack<StackDataType, ParticleInterface>; + using StackIteratorValue = + StackIteratorInterface<typename std::remove_reference<StackDataType>::type, + ParticleInterface, InnerStackTypeValue>; /// @} public: @@ -85,7 +85,8 @@ namespace corsika::stack { /** * this is the full type of the declared ParticleInterface: typedef typename */ - using ParticleType = typename StackIterator::ParticleInterfaceType; + using ParticleType = StackIterator; + using ParticleInterfaceType = typename StackIterator::ParticleInterfaceType; friend class StackIteratorInterface< typename std::remove_reference<StackDataType>::type, ParticleInterface, ViewType>; @@ -102,22 +103,27 @@ namespace corsika::stack { SecondaryView(Args... args); public: - SecondaryView(StackIteratorV& vP) - : Stack<StackDataType&, ParticleInterface>(vP.GetStackData()) - , fProjectileIndex(vP.GetIndex()) {} + SecondaryView(StackIteratorValue& vI) + : Stack<StackDataType&, ParticleInterface>(vI.GetStackData()) + , fProjectileIndex(vI.GetIndex()) {} - auto GetProjectile() { + StackIterator GetProjectile() { // NOTE: 0 is special marker here for PROJECTILE, see GetIndexFromIterator return StackIterator(*this, 0); } template <typename... Args> auto AddSecondary(const Args... v) { + StackIterator proj = GetProjectile(); + return AddSecondary(proj, v...); + } + + template <typename... Args> + auto AddSecondary(StackIterator& proj, const Args... v) { InnerStackType::GetStackData().IncrementSize(); const unsigned int idSec = GetSize(); const unsigned int index = InnerStackType::GetStackData().GetSize() - 1; fIndices.push_back(index); - StackIterator proj = GetProjectile(); // NOTE: "+1" is since "0" is special marker here for PROJECTILE, see // GetIndexFromIterator return StackIterator(*this, idSec + 1, proj, v...); @@ -164,7 +170,7 @@ namespace corsika::stack { /** * need overwrite Stack::Delete, since we want to call SecondaryView::DeleteLast */ - void Delete(ParticleType p) { Delete(p.GetIterator()); } + void Delete(ParticleInterfaceType p) { Delete(p.GetIterator()); } /** * delete last particle on stack by decrementing stack size diff --git a/Framework/StackInterface/StackIteratorInterface.h b/Framework/StackInterface/StackIteratorInterface.h index cf1b6d7b53efb14f3fbdd4283831e1f34fe751de..4e5cc6d059967d8d64ffdb51a2474052e7bd0d2b 100644 --- a/Framework/StackInterface/StackIteratorInterface.h +++ b/Framework/StackInterface/StackIteratorInterface.h @@ -58,20 +58,20 @@ namespace corsika::stack { ParticleInterface, in this example StackDataType::GetData(const unsigned int vIndex). - For an example see stack_example.cc, or the + For two examples see stack_example.cc, or the corsika::processes::sibyll::SibStack class */ template <typename StackDataType, template <typename> typename ParticleInterface, - typename StackType = - Stack<StackDataType, ParticleInterface>> //, bool IsBase=true > + typename StackType = Stack<StackDataType, ParticleInterface>> class StackIteratorInterface - : public ParticleInterface<StackIteratorInterface<StackDataType, ParticleInterface, - StackType>> { //,IsBase> { + : public ParticleInterface< + StackIteratorInterface<StackDataType, ParticleInterface, StackType>> { public: - using ParticleInterfaceType = ParticleInterface< - StackIteratorInterface<StackDataType, ParticleInterface, StackType>>; //,IsBase>; + using ParticleInterfaceType = + ParticleInterface<corsika::stack::StackIteratorInterface< + StackDataType, ParticleInterface, StackType>>; // friends are needed for access to protected methods friend class Stack<StackDataType, @@ -110,8 +110,7 @@ namespace corsika::stack { StackIteratorInterface(StackType& data, const unsigned int index, const Args... args) : fIndex(index) , fData(&data) { - ParticleInterfaceType& p = **this; - p.SetParticleData(args...); + (**this).SetParticleData(args...); } /** constructor that also sets new values on particle data object, including reference @@ -129,9 +128,7 @@ namespace corsika::stack { StackIteratorInterface& parent, const Args... args) : fIndex(index) , fData(&data) { - ParticleInterfaceType& p = **this; - ParticleInterfaceType& pa = *parent; - p.SetParticleData(pa, args...); + (**this).SetParticleData(*parent, args...); } public: @@ -200,12 +197,12 @@ namespace corsika::stack { template <typename StackDataType, template <typename> typename ParticleInterface, typename StackType = Stack<StackDataType, ParticleInterface>> class ConstStackIteratorInterface - : public ParticleInterface<ConstStackIteratorInterface< - StackDataType, ParticleInterface, StackType>> { //,IsBase> { + : public ParticleInterface< + ConstStackIteratorInterface<StackDataType, ParticleInterface, StackType>> { public: - typedef ParticleInterface<ConstStackIteratorInterface< - StackDataType, ParticleInterface, StackType>> //,IsBase> + typedef ParticleInterface< + ConstStackIteratorInterface<StackDataType, ParticleInterface, StackType>> ParticleInterfaceType; friend class Stack<StackDataType, ParticleInterface>; // for access to GetIndex diff --git a/Framework/StackInterface/testCombinedStack.cc b/Framework/StackInterface/testCombinedStack.cc index 11a3314a2e60a517b9c1b197f913ffec1e7cc421..9ae2f8ad60c8cee4028466e977edb37b6e7f508f 100644 --- a/Framework/StackInterface/testCombinedStack.cc +++ b/Framework/StackInterface/testCombinedStack.cc @@ -10,6 +10,7 @@ */ #include <corsika/stack/CombinedStack.h> +#include <corsika/stack/SecondaryView.h> #include <corsika/stack/Stack.h> #include <testTestStack.h> // for testing: simple stack. This is a @@ -31,6 +32,10 @@ using namespace corsika; using namespace corsika::stack; using namespace std; +//////////////////////////////////////////////////////////// +// first level test: combine two stacks: +// StackTest = (TestStackData + TestStackData2) + // definition of stack-data object class TestStackData2 { @@ -82,7 +87,7 @@ public: double GetData2() const { return GetStackData().GetData2(GetIndex()); } }; -// combined stack +// combined stack: StackTest = (TestStackData + TestStackData2) template <typename StackIter> using CombinedTestInterfaceType = corsika::stack::CombinedParticleInterface<TestParticleInterface, @@ -196,6 +201,8 @@ TEST_CASE("Combined Stack", "[stack]") { } //////////////////////////////////////////////////////////// +// next level: combine three stacks: +// combined stack: StackTest2 = ((TestStackData + TestStackData2) + TestStackData3) // definition of stack-data object class TestStackData3 { @@ -228,6 +235,7 @@ private: std::vector<double> fData3; }; +// --------------------------------------- // defintion of a stack-readout object, the iteractor dereference // operator will deliver access to these function template <typename T> @@ -248,7 +256,7 @@ public: double GetData3() const { return GetStackData().GetData3(GetIndex()); } }; -// double combined stack +// double combined stack: // combined stack template <typename StackIter> using CombinedTestInterfaceType2 = @@ -264,12 +272,14 @@ TEST_CASE("Combined Stack - multi", "[stack]") { StackTest2 s; REQUIRE(s.GetSize() == 0); + // add new particle, only provide tuple data for StackTest auto p1 = s.AddParticle(std::tuple{9.9}); + // add new particle, provide tuple data for both StackTest and TestStackData3 auto p2 = s.AddParticle(std::tuple{8.8}, std::tuple{0.1}); + // examples to explicitly change data on stack p2.SetData2(0.1); // not clear why this is needed, need to check // SetParticleData workflow for more complicated // settings - // auto p3 = s.AddParticle( std::tuple {8.8}, std::tuple{1.}, std::tuple{0.1} ); p1.SetData3(20.2); p2.SetData3(10.3); @@ -301,3 +311,49 @@ TEST_CASE("Combined Stack - multi", "[stack]") { REQUIRE(s.GetSize() == 0); } } + +//////////////////////////////////////////////////////////// + +// final level test, create SecondaryView on StackTest2 + +/* + See Issue 161 + + unfortunately clang does not support this in the same way (yet) as + gcc, so we have to distinguish here. If clang cataches up, we could + remove the clang branch here and also in corsika::Cascade. The gcc + code is much more generic and universal. + */ +template <typename StackIter> +using CombinedTestInterfaceType2 = + corsika::stack::CombinedParticleInterface<StackTest::PIType, TestParticleInterface3, + StackIter>; + +using StackTest2 = CombinedStack<typename StackTest::StackImpl, TestStackData3, + CombinedTestInterfaceType2>; + +#if defined(__clang__) +using StackTestView = SecondaryView<TestStackData, TestParticleInterface>; +#elif defined(__GNUC__) || defined(__GNUG__) +template <typename S, template <typename> typename _PIType = S::template PIType> +struct MakeView { + using type = corsika::stack::SecondaryView<typename S::StackImpl, _PIType>; +}; +using StackTestView = MakeView<StackTest2>::type; +#endif + +TEST_CASE("Combined Stack - secondary view") { + + SECTION("create secondaries via secondaryview") { + + StackTest2 stack; + auto particle = stack.AddParticle(std::tuple{9.9}); + + StackTestView view(particle); + + auto projectile = view.GetProjectile(); + projectile.AddSecondary(std::tuple{8.8}); + + REQUIRE(stack.GetSize() == 2); + } +} diff --git a/Framework/StackInterface/testSecondaryView.cc b/Framework/StackInterface/testSecondaryView.cc index 08d27c169bfa6144abd203603eae74dc1e134c98..6f13ceecd1d5f88171a1b6e0574b5e1aaaab2c93 100644 --- a/Framework/StackInterface/testSecondaryView.cc +++ b/Framework/StackInterface/testSecondaryView.cc @@ -33,6 +33,24 @@ using namespace std; typedef Stack<TestStackData, TestParticleInterface> StackTest; +/* + See Issue 161 + + unfortunately clang does not support this in the same way (yet) as + gcc, so we have to distinguish here. If clang cataches up, we could + remove the clang branch here and also in corsika::Cascade. The gcc + code is much more generic and universal. + */ +#if defined(__clang__) +using StackTestView = SecondaryView<TestStackData, TestParticleInterface>; +#elif defined(__GNUC__) || defined(__GNUG__) +template <typename S, template <typename> typename _PIType = S::template PIType> +struct MakeView { + using type = corsika::stack::SecondaryView<typename S::StackImpl, _PIType>; +}; +using StackTestView = MakeView<StackTest>::type; +#endif + TEST_CASE("SecondaryStack", "[stack]") { // helper function for sum over stack data @@ -51,55 +69,71 @@ TEST_CASE("SecondaryStack", "[stack]") { auto particle = s.GetNextParticle(); - typedef SecondaryView<TestStackData, TestParticleInterface> StackTestView; - StackTestView v(particle); - REQUIRE(v.GetSize() == 0); + StackTestView view(particle); + REQUIRE(view.GetSize() == 0); { - auto proj = v.GetProjectile(); + auto proj = view.GetProjectile(); REQUIRE(proj.GetData() == particle.GetData()); + proj.AddSecondary(std::tuple{4.4}); } - v.AddSecondary(std::tuple{4.4}); - v.AddSecondary(std::tuple{4.5}); - v.AddSecondary(std::tuple{4.6}); + view.AddSecondary(std::tuple{4.5}); + view.AddSecondary(std::tuple{4.6}); - REQUIRE(v.GetSize() == 3); + REQUIRE(view.GetSize() == 3); REQUIRE(s.GetSize() == 5); - REQUIRE(!v.IsEmpty()); + REQUIRE(!view.IsEmpty()); auto sumView = [](const StackTestView& stack) { - double v = 0; - for (const auto& p : stack) { v += p.GetData(); } - return v; + double value = 0; + for (const auto& p : stack) { value += p.GetData(); } + return value; }; REQUIRE(sum(s) == sumS + 4.4 + 4.5 + 4.6); - REQUIRE(sumView(v) == 4.4 + 4.5 + 4.6); + REQUIRE(sumView(view) == 4.4 + 4.5 + 4.6); - v.DeleteLast(); - REQUIRE(v.GetSize() == 2); + view.DeleteLast(); + REQUIRE(view.GetSize() == 2); REQUIRE(s.GetSize() == 4); REQUIRE(sum(s) == sumS + 4.4 + 4.5); - REQUIRE(sumView(v) == 4.4 + 4.5); + REQUIRE(sumView(view) == 4.4 + 4.5); - auto pDel = v.GetNextParticle(); - v.Delete(pDel); - REQUIRE(v.GetSize() == 1); + auto pDel = view.GetNextParticle(); + view.Delete(pDel); + REQUIRE(view.GetSize() == 1); REQUIRE(s.GetSize() == 3); REQUIRE(sum(s) == sumS + 4.4 + 4.5 - pDel.GetData()); - REQUIRE(sumView(v) == 4.4 + 4.5 - pDel.GetData()); + REQUIRE(sumView(view) == 4.4 + 4.5 - pDel.GetData()); - v.Delete(v.GetNextParticle()); + view.Delete(view.GetNextParticle()); REQUIRE(sum(s) == sumS); - REQUIRE(sumView(v) == 0); - REQUIRE(v.IsEmpty()); + REQUIRE(sumView(view) == 0); + REQUIRE(view.IsEmpty()); { - auto proj = v.GetProjectile(); + auto proj = view.GetProjectile(); REQUIRE(proj.GetData() == particle.GetData()); } } + + SECTION("secondary view, construct from ParticleType") { + StackTest s; + REQUIRE(s.GetSize() == 0); + s.AddParticle(std::tuple{9.9}); + s.AddParticle(std::tuple{8.8}); + + auto iterator = s.GetNextParticle(); + typename StackTest::ParticleType& particle = iterator; // as in corsika::Cascade + + StackTestView view(particle); + REQUIRE(view.GetSize() == 0); + + view.AddSecondary(std::tuple{4.4}); + + REQUIRE(view.GetSize() == 1); + } }