Newer
Older
* (c) Copyright 2020 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/process/ProcessSequence.hpp>
#include <corsika/framework/process/SwitchProcessSequence.hpp>
#include <corsika/framework/core/PhysicalUnits.hpp>
#include <corsika/framework/process/ContinuousProcessStepLength.hpp>
#include <catch2/catch.hpp>
#include <array>
#include <iomanip>
#include <iostream>
ralfulrich
committed
#include <typeinfo>
/*
Unit test for testing all Process types and their arrangement in
containers ProcessSequence and SwitchProcessSequence
*/
using namespace corsika;
// DummyNode is only needed for BoundaryCrossingProcess
struct DummyNode {
// The stack is non-existent for this example
struct DummyStack {};
// our data object (particle) is a simple arrary of doubles
struct DummyData {
double data_[nData] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
typedef DummyNode node_type; // for BoundaryCrossingProcess
// there is no real trajectory/track
struct DummyTrajectory {};
// since there is no stack, there is also no view. This is a simplistic dummy object
// sufficient here.
struct DummyView {
DummyView(DummyData& p)
: p_(p) {}
DummyData& p_;
DummyData& parent() { return p_; }
};
ralfulrich
committed
int globalCount = 0; // simple counter
ralfulrich
committed
int checkInteract = 0; // use this as a bit field
int checkSec = 0; // use this as a bit field
int checkCont = 0; // use this as a bit field
class ContinuousProcess1 : public ContinuousProcess<ContinuousProcess1> {
ContinuousProcess1(int const v, LengthType const step)
: v_(v)
, step_(step) {
CORSIKA_LOG_DEBUG(
"globalCount: {} "
", v_: {} ",
globalCount, v_);
void setStep(LengthType const v) { step_ = v; }
template <typename D, typename T>
ProcessReturn doContinuous(D& d, T&, bool flag) const {
ralfulrich
committed
checkCont |= 1;
for (int i = 0; i < nData; ++i) d.data_[i] += 0.933;
template <typename TParticle, typename TTrack>
LengthType getMaxStepLength(TParticle&, TTrack&) {
return step_;
}
bool getFlag() const { return flag_; }
void resetFlag() { flag_ = false; }
LengthType step_ = 0_m;
mutable bool flag_ = false;
};
class ContinuousProcess2 : public ContinuousProcess<ContinuousProcess2> {
ContinuousProcess2(int const v, LengthType const step)
: v_(v)
, step_(step) {
CORSIKA_LOG_DEBUG(
"globalCount: {}"
", v_: {}",
globalCount, v_);
void setStep(LengthType const v) { step_ = v; }
template <typename D, typename T>
ProcessReturn doContinuous(D& d, T&, bool const flag) const {
CORSIKA_LOG_DEBUG("ContinuousProcess2::DoContinuous");
ralfulrich
committed
checkCont |= 2;
ralfulrich
committed
}
template <typename TParticle, typename TTrack>
LengthType getMaxStepLength(TParticle&, TTrack&) {
return step_;
}
bool getFlag() const { return flag_; }
void resetFlag() { flag_ = false; }
LengthType step_ = 0_m;
mutable bool flag_ = false;
ralfulrich
committed
};
class ContinuousProcess3 : public ContinuousProcess<ContinuousProcess3> {
public:
ContinuousProcess3(int const v, LengthType const step)
: v_(v)
, step_(step) {
CORSIKA_LOG_DEBUG(
"globalCount: {}"
", v_: {} ",
globalCount, v_);
ralfulrich
committed
globalCount++;
}
void setStep(LengthType const v) { step_ = v; }
ralfulrich
committed
template <typename D, typename T>
ProcessReturn doContinuous(D& d, T&, bool const flag) const {
CORSIKA_LOG_DEBUG("ContinuousProcess3::DoContinuous");
ralfulrich
committed
checkCont |= 4;
template <typename TParticle, typename TTrack>
LengthType getMaxStepLength(TParticle&, TTrack&) {
return step_;
}
bool getFlag() const { return flag_; }
void resetFlag() { flag_ = false; }
LengthType step_ = 0_m;
mutable bool flag_ = false;
class Process1 : public InteractionProcess<Process1> {
CORSIKA_LOG_DEBUG(
"globalCount: {}"
", v_: {}",
globalCount, v_);
;
ralfulrich
committed
template <typename TView>
void doInteraction(TView& v) const {
ralfulrich
committed
checkInteract |= 1;
for (int i = 0; i < nData; ++i) v.parent().data_[i] += 1 + i;
template <typename TParticle>
GrammageType getInteractionLength(TParticle&) const {
return 10_g / square(1_cm);
}
class Process2 : public InteractionProcess<Process2> {
CORSIKA_LOG_DEBUG(
"globalCount: {}"
", v_: {}",
globalCount, v_);
ralfulrich
committed
template <typename TView>
void doInteraction(TView& v) const {
ralfulrich
committed
checkInteract |= 2;
for (int i = 0; i < nData; ++i) v.parent().data_[i] /= 1.1;
template <typename Particle>
GrammageType getInteractionLength(Particle&) const {
ralfulrich
committed
return 20_g / (1_cm * 1_cm);
class Process3 : public InteractionProcess<Process3> {
CORSIKA_LOG_DEBUG(
"globalCount: {}"
", v_: {}",
globalCount, v_);
ralfulrich
committed
template <typename TView>
void doInteraction(TView& v) const {
ralfulrich
committed
checkInteract |= 4;
for (int i = 0; i < nData; ++i) v.parent().data_[i] *= 1.01;
template <typename Particle>
GrammageType getInteractionLength(Particle&) const {
ralfulrich
committed
return 30_g / (1_cm * 1_cm);
};
class Process4 : public BaseProcess<Process4> {
public:
CORSIKA_LOG_DEBUG(
"globalCount: {}"
", v_: {}",
globalCount, v_);
template <typename D, typename T>
ProcessReturn doContinuous(D& d, T&, bool const) const {
ralfulrich
committed
checkCont |= 8;
for (int i = 0; i < nData; ++i) { d.data_[i] /= 1.2; }
ralfulrich
committed
template <typename TView>
ralfulrich
committed
checkInteract |= 8;
};
class Decay1 : public DecayProcess<Decay1> {
ralfulrich
committed
template <typename TView>
ralfulrich
committed
checkDecay |= 1;
}
};
class Decay2 : public DecayProcess<Decay2> {
public:
ralfulrich
committed
globalCount++;
}
template <typename Particle>
ralfulrich
committed
return 2_s;
}
class Stack1 : public StackProcess<Stack1> {
public:
: StackProcess(n) {}
template <typename TStack>
void doStack(TStack const&) {
class Boundary1 : public BoundaryCrossingProcess<Boundary1> {
public:
template <typename Particle>
ProcessReturn doBoundaryCrossing(Particle& p, typename Particle::node_type const& from,
typename Particle::node_type const& to) {
for (int i = 0; i < nData; ++i) { p.data_[i] += v_ * (from.data_ - to.data_); }
TEST_CASE("ProcessSequence General", "ProcessSequence") {
corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
SECTION("BaseProcess") {
Process1 m1(0);
const Process4 m4(3);
CHECK(is_process_v<Process1>);
CHECK_FALSE(is_process_v<DummyData>);
CHECK(is_process_v<decltype(m4)>);
CHECK(is_process_v<decltype(Decay1(1))>);
CHECK(is_process_v<decltype(ContinuousProcess3{3, 3_m})>);
SECTION("Check construction") {
CHECK(is_process_v<decltype(sequence1)>);
CHECK(is_process_v<decltype(m2)>);
CHECK(is_process_sequence_v<decltype(sequence1)>);
CHECK_FALSE(is_process_sequence_v<decltype(m2)>);
CHECK_FALSE(is_switch_process_sequence_v<decltype(sequence1)>);
CHECK_FALSE(is_switch_process_sequence_v<decltype(m2)>);
CHECK_FALSE(is_process_sequence_v<decltype(Decay1(7))>);
CHECK_FALSE(is_switch_process_sequence_v<decltype(Decay1(7))>);
ralfulrich
committed
ralfulrich
committed
CHECK(is_process_sequence_v<decltype(sequence2)> == true);
ralfulrich
committed
CHECK(is_process_sequence_v<decltype(sequence3)> == true);
auto sequence2 = make_sequence(cp1, m2, m3);
GrammageType const tot = sequence2.getInteractionLength(particle);
InverseGrammageType const tot_inv = sequence2.getInverseInteractionLength(particle);
CORSIKA_LOG_DEBUG(
"lambda_tot={}"
"; lambda_tot_inv={}",
tot, tot_inv);
CHECK(tot / 1_g * square(1_cm) == 12);
CHECK(tot_inv * 1_g / square(1_cm) == 1. / 12);
auto sequence2 = make_sequence(cp1, m2, m3, d3);
TimeType const tot = sequence2.getLifetime(particle);
InverseTimeType const tot_inv = sequence2.getInverseLifetime(particle);
CORSIKA_LOG_DEBUG(
"lambda_tot={}"
"; lambda_tot_inv={}",
tot, tot_inv);
CHECK(tot / 1_s == 1);
CHECK(tot_inv * 1_s == 1.);
ContinuousProcess1 cp1(0, 1_m); // += 0.933
ContinuousProcess2 cp2(1, 1.1_m); // += 0.111
Process2 m2(2); // /= 1.1
Process3 m3(3); // *= 1.01
auto sequence2 = make_sequence(cp1, m2, m3, cp2);
std::cout << boost::typeindex::type_id<decltype(sequence2)>().pretty_name()
<< std::endl;
DummyData particle;
DummyTrajectory track;
cp1.resetFlag();
cp2.resetFlag();
ContinuousProcessStepLength const step1 = sequence2.getMaxStepLength(particle, track);
CHECK(LengthType(step1) == 1_m);
sequence2.doContinuous(particle, track, step1);
CHECK(cp1.getFlag());
CHECK_FALSE(cp2.getFlag());
CORSIKA_LOG_INFO("step1, l={}, i={}", LengthType(step1),
ContinuousProcessIndex(step1).getIndex());
cp1.resetFlag();
cp2.resetFlag();
cp1.setStep(10_m);
ContinuousProcessStepLength const step2 = sequence2.getMaxStepLength(particle, track);
CHECK(LengthType(step2) == 1.1_m);
CHECK(ContinuousProcessIndex(step1) != ContinuousProcessIndex(step2));
sequence2.doContinuous(particle, track, step2);
CHECK_FALSE(cp1.getFlag());
CHECK(cp2.getFlag());
CORSIKA_LOG_INFO("step2, l={}, i={}", LengthType(step2),
ContinuousProcessIndex(step2).getIndex());
// reset
particle = DummyData();
track = DummyTrajectory();
int const nLoop = 5;
for (int i = 0; i < nData; ++i) { test_data[i] += 0.933 + 0.111; }
sequence2.doContinuous(particle, track, ContinuousProcessIndex(1));
for (int i = 0; i < nData; i++) {
CORSIKA_LOG_DEBUG("data_[{}]={}", i, particle.data_[i]);
CHECK(particle.data_[i] == Approx(test_data[i]).margin(1e-9));
SECTION("StackProcess") {
Stack1 s1(1);
Stack1 s2(2);
DummyStack stack;
for (int i = 0; i < nLoop; ++i) { sequence1.doStack(stack); }
CHECK(s1.getCount() == 20);
CHECK(s2.getCount() == 10);
ContinuousProcess2 cp2(1, 2_m); // += 0.111
Process2 m2(2); // /= 1.1
auto sequence2 = make_sequence(cp2, m2);
auto sequence3 = make_sequence(cp2, m2, s1);
CHECK(is_process_sequence_v<decltype(sequence2)> == true);
CHECK(is_process_sequence_v<decltype(sequence3)> == true);
CHECK(contains_stack_process_v<decltype(sequence2)> == false);
CHECK(contains_stack_process_v<decltype(sequence3)> == true);
SECTION("BoundaryCrossingProcess") {
globalCount = 0;
Boundary1 b1;
auto sequence1 = make_sequence(b1);
DummyData particle;
DummyNode node_from(5);
DummyNode node_to(4);
for (int i = 0; i < nLoop; ++i) {
sequence1.doBoundaryCrossing(particle, node_from, node_to);
}
for (int i = 0; i < nData; i++) {
CORSIKA_LOG_DEBUG("data_[{}]={}", i, particle.data_[i]);
CHECK(particle.data_[i] == Approx(nLoop).margin(1e-9));
}
CHECK(is_process_sequence_v<decltype(sequence1)> == true);
CHECK(contains_stack_process_v<decltype(sequence1)> == false);
CHECK(count_processes<decltype(sequence1)>::count == 1);
}
TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
/**
* In this example switching is done only by "data_[0]>0", where
* data in an arrray of doubles, DummyData.
*/
struct SwitchSelect {
SwitchResult operator()(DummyData const& p) const {
if (p.data_[0] > 0) return SwitchResult::First;
return SwitchResult::Second;
}
};
SwitchSelect select1;
auto cp1 = ContinuousProcess1(0, 1_m);
auto cp2 = ContinuousProcess2(0, 2_m);
auto cp3 = ContinuousProcess3(0, 3_m);
auto sequence1 = make_sequence(Process1(0), cp2, Decay1(0), Boundary1(1.0));
auto sequence2 = make_sequence(cp3, Process2(0), Boundary1(-1.0), Decay2(0));
auto sequence3 = make_sequence(cp1, Process3(0),
SwitchProcessSequence(sequence1, sequence2, select1));
auto sequence4 =
make_sequence(cp1, Boundary1(2.0), Process3(0),
SwitchProcessSequence(sequence1, Boundary1(-1.0), select1));
auto sequence_alt = make_sequence(
cp1, Process3(0),
make_select(make_sequence(Process1(0), cp2, Decay1(0), Boundary1(1.0)),
make_sequence(cp3, Process2(0), Boundary1(-1.0), Decay2(0)),
select1));
auto switch_seq = SwitchProcessSequence(sequence1, sequence2, select1);
CHECK(is_process_sequence_v<decltype(switch_seq)>);
CHECK(is_switch_process_sequence_v<decltype(switch_seq)>);
// CHECK(is_switch_process_sequence_v<decltype(&switch_seq)>);
CHECK(is_switch_process_sequence_v<decltype(
SwitchProcessSequence(sequence1, sequence2, select1))>);
CHECK(is_process_sequence_v<decltype(sequence3)>);
CHECK_FALSE(is_switch_process_sequence_v<decltype(sequence3)>);
CHECK(is_process_sequence_v<decltype(
SwitchProcessSequence(sequence1, sequence2, select1))>);
CHECK(is_switch_process_sequence_v<decltype(
SwitchProcessSequence(sequence1, sequence2, select1))>);
// check that same process sequence can be build in different ways
CHECK(typeid(sequence3) == typeid(sequence_alt));
CHECK(is_process_sequence_v<decltype(sequence3)>);
SwitchProcessSequence(sequence1, sequence2, select1))>);
}
SECTION("Check interfaces") {
DummyData particle;
DummyTrajectory track;
DummyView view(particle);
checkDecay = 0;
checkInteract = 0;
checkSec = 0;
checkCont = 0;
particle.data_[0] = 100; // data positive
sequence3.doContinuous(particle, track, ContinuousProcessIndex(1));
CHECK(checkInteract == 0);
CHECK(checkDecay == 0);
CHECK(checkCont == 0b011);
CHECK(checkSec == 0);
checkDecay = 0;
checkInteract = 0;
checkSec = 0;
checkCont = 0;
particle.data_[0] = -100; // data negative
sequence3.doContinuous(particle, track, ContinuousProcessIndex(1));
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
CHECK(checkInteract == 0);
CHECK(checkDecay == 0);
CHECK(checkCont == 0b101);
CHECK(checkSec == 0);
// 1/(30g/cm2) is Process3
InverseGrammageType lambda_select = .9 / 30. * square(1_cm) / 1_g;
InverseTimeType time_select = 0.1 / second;
checkDecay = 0;
checkInteract = 0;
checkSec = 0;
checkCont = 0;
particle.data_[0] = 100; // data positive
sequence3.selectInteraction(view, lambda_select);
sequence3.selectDecay(view, time_select);
CHECK(checkInteract == 0b100); // this is Process3
CHECK(checkDecay == 0b001); // this is Decay1
CHECK(checkCont == 0);
CHECK(checkSec == 0);
lambda_select = 1.01 / 30. * square(1_cm) / 1_g;
checkInteract = 0;
sequence3.selectInteraction(view, lambda_select);
CHECK(checkInteract == 0b001); // this is Process1
checkDecay = 0;
checkInteract = 0;
checkSec = 0;
checkCont = 0;
particle.data_[0] = -100; // data negative
sequence3.selectInteraction(view, lambda_select);
sequence3.selectDecay(view, time_select);
CHECK(checkInteract == 0b010); // this is Process2
CHECK(checkDecay == 0b010); // this is Decay2
CHECK(checkCont == 0);
CHECK(checkSec == 0);
checkDecay = 0;
checkInteract = 0;
checkSec = 0;
checkCont = 0;
particle.data_[0] = -100; // data negative
sequence3.doSecondaries(view);
Stack1 stack(0);
sequence3.doStack(stack);
CHECK(checkInteract == 0);
CHECK(checkDecay == 0);
CHECK(checkCont == 0);
CHECK(checkSec == 0);
// check the SwitchProcessSequence where no process is selected in
// selected branch (fallthrough)
checkDecay = 0;
checkInteract = 0;
checkSec = 0;
checkCont = 0;
sequence4.selectInteraction(view, lambda_select);
sequence4.doSecondaries(view);
sequence4.selectDecay(view, time_select);
sequence4.doSecondaries(view);
CHECK(checkInteract == 0);
CHECK(checkCont == 0);
CHECK(checkSec == 0);
// check that large "select" value will correctly ignore the call
lambda_select = 1e5 * square(1_cm) / 1_g;
time_select = 1e5 / second;
checkDecay = 0;
checkInteract = 0;
sequence3.selectInteraction(view, lambda_select);
sequence3.selectDecay(view, time_select);
CHECK(checkInteract == 0);
CHECK(checkDecay == 0);
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
SECTION("Check ContinuousProcesses in SwitchProcessSequence") {
DummyData particle;
DummyTrajectory track;
particle.data_[0] =
100; // data positive, selects particular branch on SwitchProcessSequence
cp1.setStep(10_m);
cp2.setStep(15_m);
cp3.setStep(100_m);
cp1.resetFlag();
cp2.resetFlag();
cp3.resetFlag();
ContinuousProcessStepLength const step1 = sequence3.getMaxStepLength(particle, track);
CHECK(LengthType(step1) == 10_m);
sequence3.doContinuous(particle, track, step1);
CHECK(cp1.getFlag());
CHECK_FALSE(cp2.getFlag());
CHECK_FALSE(cp3.getFlag());
CORSIKA_LOG_INFO("step1, l={}, i={}", LengthType(step1),
ContinuousProcessIndex(step1).getIndex());
particle.data_[0] =
100; // data positive, selects particular branch on SwitchProcessSequence
cp1.setStep(50_m);
cp2.setStep(15_m);
cp3.setStep(100_m);
cp1.resetFlag();
cp2.resetFlag();
cp3.resetFlag();
ContinuousProcessStepLength const step2 = sequence3.getMaxStepLength(particle, track);
CHECK(LengthType(step2) == 15_m);
sequence3.doContinuous(particle, track, step2);
CHECK_FALSE(cp1.getFlag());
CHECK(cp2.getFlag());
CHECK_FALSE(cp3.getFlag());
CORSIKA_LOG_INFO("step2, len_cont={}, indexLimit={} type={}", LengthType(step2),
ContinuousProcessIndex(step2).getIndex(),
boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
particle.data_[0] =
-100; // data positive, selects particular branch on SwitchProcessSequence
cp1.setStep(11_m);
cp2.setStep(15_m);
cp3.setStep(100_m);
cp1.resetFlag();
cp2.resetFlag();
cp3.resetFlag();
ContinuousProcessStepLength const step3 = sequence3.getMaxStepLength(particle, track);
CHECK(LengthType(step3) == 11_m);
sequence3.doContinuous(particle, track, step3);
CHECK(cp1.getFlag());
CHECK_FALSE(cp2.getFlag());
CHECK_FALSE(cp3.getFlag());
CORSIKA_LOG_INFO("step3, len_cont={}, indexLimit={} type={}", LengthType(step3),
ContinuousProcessIndex(step3).getIndex(),
boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
particle.data_[0] =
-100; // data positive, selects particular branch on SwitchProcessSequence
cp1.setStep(11_m);
cp2.setStep(15_m);
cp3.setStep(2_m);
cp1.resetFlag();
cp2.resetFlag();
cp3.resetFlag();
ContinuousProcessStepLength const step4 = sequence3.getMaxStepLength(particle, track);
CHECK(LengthType(step4) == 2_m);
sequence3.doContinuous(particle, track, step4);
CHECK_FALSE(cp1.getFlag());
CHECK_FALSE(cp2.getFlag());
CHECK(cp3.getFlag());
CORSIKA_LOG_INFO("step4, len_cont={}, indexLimit={} type={}", LengthType(step4),
ContinuousProcessIndex(step4).getIndex(),
boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
}
SECTION("Check BoundaryCrossingProcess in SwitchProcessSequence") {
DummyData particle;
DummyNode node_from(1);
DummyNode node_to(2);
particle.data_[0] =
100; // data positive, selects particular branch on SwitchProcessSequence
sequence4.doBoundaryCrossing(particle, node_from, node_to);
CHECK(particle.data_[0] == 97); // 100 - 2*1 - 1*1
particle.data_[0] =
-100; // data positive, selects particular branch on SwitchProcessSequence
sequence4.doBoundaryCrossing(particle, node_from, node_to);
CHECK(particle.data_[0] == -101); // -100 - 2*1 + 1*1
}
TEST_CASE("ProcessSequence Indexing", "ProcessSequence") {
logging::set_level(logging::level::info);
corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
int const n0 = count_processes<Decay2>::count;
int const n1 = count_processes<ContinuousProcess3>::count;
int const n2 = count_processes<ContinuousProcess2,
count_processes<Process2, count_processes<ContinuousProcess3>::count>::count;
count_processes<ContinuousProcess3, count_processes<Process2>::count>::count;
count_processes<ContinuousProcess2,
count_processes<Process1, count_processes<ContinuousProcess3, 10>::count>::count;
int const n11_c =
count_processes<ContinuousProcess3, count_processes<Process1, 10>::count>::count;
CHECK(n1_b == 2);
CHECK(n1_c == 2);
CHECK(n11_b == 12);
CHECK(n11_c == 12);
std::cout << count_processes<ContinuousProcess3>::count << std::endl;
std::cout << count_processes<Process3>::count << std::endl;
struct SwitchSelect {
SwitchResult operator()(DummyData const& p) const {
std::cout << "SwitchSelect data=" << p.data_[0] << std::endl;
if (p.data_[0] > 0) return SwitchResult::First;
return SwitchResult::Second;
}
};
auto sequence1 = make_sequence(Process1(0), ContinuousProcess2(0, 2_m), Decay1(0));
auto sequence2 = make_sequence(ContinuousProcess3(0, 3_m), Process2(0), Decay2(0),
ContinuousProcess1(0, 1_m));
auto switch_seq = SwitchProcessSequence(sequence1, sequence2, select1);
auto sequence3 = make_sequence(ContinuousProcess1(0, 1_m), Process3(0), switch_seq);
auto sequence4 = make_sequence(ContinuousProcess1(0, 1_m), Process3(0),
SwitchProcessSequence(sequence1, sequence2, select1));
int const switch_seq_n = count_processes<decltype(switch_seq)>::count;
int const sequence3_n = count_processes<decltype(sequence3)>::count;
CHECK(decltype(sequence1)::getNumberOfProcesses() == 3);
CHECK(count_processes<decltype(sequence1)>::count == 3);
CHECK(count_processes<decltype(sequence2)>::count == 4);
CHECK(switch_seq_n == 7);
CHECK(sequence3_n == 9);
CHECK(count_processes<decltype(sequence4)>::count == 9);
std::cout << "switch_seq "
<< boost::typeindex::type_id<decltype(switch_seq)>().pretty_name()
<< std::endl;
std::cout << "sequence3 "
<< boost::typeindex::type_id<decltype(sequence3)>().pretty_name()
<< std::endl;