Physics modules and processes

group Processes

Physics processes in CORSIKA 8 are clustered in ProcessSequence and SwitchProcessSequence containers.

The former is a mere (ordered) collection, while the latter has the option to switch between two alternative ProcessSequences.

Depending on the type of data to act on and on the allowed actions of processes there are several interface options:

And all processes (including ProcessSequence and SwitchProcessSequence) are derived from BaseProcess.

Processes of any type (e.g. p1, p2, p3,…) can be assembled into a ProcessSequence using the make_sequence factory function.

auto sequence1 = make_sequence(p1, p2, p3);
auto sequence2 = make_sequence(p4, p5, p6, p7);
auto sequence3 = make_sequence(sequence1, sequemce2, p8, p9);

Note, if the order of processes matters, the order of occurence in the ProcessSequence determines the executiion order.

SecondariesProcess alyways act on new secondaries produced (i.e. in InteractionProcess and DecayProcess) in the scope of their ProcessSequence. For example if i1 and i2 are InteractionProcesses and s1 is a SecondariesProcess, then

auto sequence = make_sequence(i1, make_sequence(i2, s1))

will result in s1 acting only on the particles produced by i2 and not by i1. This can be very useful, e.g. to fine tune thinning.

A special type of ProcessSequence is SwitchProcessSequence, which has two branches and a functor that can select between these two branches.

auto sequence = make_switch(sequence1, sequence2, selector);

where the only requirement to selector is that it provides a SwitchResult operator()(Particle const& particle) const method. Thus, based on the dynamic properties of particle the functor can make its decision. This is clearly important for switching between low-energy and high-energy models, but not limited to this. The selection can even be done with a lambda function.

template<typename TDerived>
struct corsika::BaseProcess : public corsika::_BaseProcess
#include <BaseProcess.hpp>

Each process in C8 must derive from BaseProcess.

The structural base type of a process object in a ProcessSequence. Both, the ProcessSequence and all its elements are of type BaseProcess

Todo:

rename BaseProcess into just Process

rename _BaseProcess, or find better alternative in FIXME ./Processes/AnalyticProcessors/ExecTime.h, see e.g. how this is done in ProcessSequence.hpp/make_sequence

Subclassed by corsika::BoundaryCrossingProcess< TDerived >, corsika::ContinuousProcess< TDerived >, corsika::DecayProcess< TDerived >, corsika::InteractionProcess< TDerived >, corsika::SecondariesProcess< TDerived >, corsika::StackProcess< TDerived >

Public Types

using process_type = TDerived

Base processor type for use in other template classes.

Public Static Functions

static inline unsigned int constexpr getNumberOfProcesses()

Default number of processes is just one, obviously.

template<typename TDerived>
class BoundaryCrossingProcess : public corsika::BaseProcess<TDerived>
#include <BoundaryCrossingProcess.hpp>

Processes acting on the particles traversion from one volume into another volume.

Create a new BoundaryCrossingProcess, e.g. for XYModel, via

class XYModel : public BoundaryCrossingProcess<XYModel> {};

and provide the necessary interface method:

template <typename TParticle>
ProcessReturn XYModel::doBoundaryCrossing(TParticle& Particle,
                                typename TParticle::node_type const& from,
                                typename TParticle::node_type const& to);

where Particle is the object to read particle data from a Stack. The volume the particle is originating from is from, the volume where it goes to is to.

template<typename TDerived>
class ContinuousProcess : public corsika::BaseProcess<TDerived>
#include <ContinuousProcess.hpp>

Processes with continuous effects along a particle Trajectory.

Create a new ContinuousProcess, e.g. for XYModel, via

class XYModel : public ContinuousProcess<XYModel> {};

and provide two necessary interface methods:

template <typename TParticle, typename TTrack>
LengthType getMaxStepLength(TParticle const& p, TTrack const& track) const;

which allows any ContinuousProcess to tell to CORSIKA a maximum allowed step length. Such step-length limitation, if it turns out to be smaller/sooner than any other limit (decay length, interaction length, other continuous processes, geometry, etc.) will lead to a limited step length.

template <typename TParticle, typename TTrack>
ProcessReturn doContinuous(TParticle& p, TTrack const& t, bool const stepLimit)
const;

which applied any continuous effects on Particle p along Trajectory t. The particle in all typical scenarios will be altered by a doContinuous. The flag stepLimit will be true if the preious evaluation of getMaxStepLength resulted in this particular ContinuousProcess to be responsible for the step length limit on the current track t. This information can be expoited and avoid e.g. any uncessary calculations.

Particle and Track are the valid classes to access particles and track (Trajectory) data on the Stack. Those two methods do not need to be templated, they could use the types e.g. corsika::setup::Stack::particle_type but by the cost of loosing all flexibility otherwise provided.

class ContinuousProcessIndex
#include <ContinuousProcessIndex.hpp>

To index individual processes (continuous processes) inside a ProcessSequence.

class ContinuousProcessStepLength
#include <ContinuousProcessStepLength.hpp>

To store step length in LengthType and unique index in ProcessSequence of shortest step ContinuousProcess.

template<typename TDerived>
struct DecayProcess : public corsika::BaseProcess<TDerived>
#include <DecayProcess.hpp>

Process decribing the decay of particles.

Create a new DecayProcess, e.g. for XYModel, via

class XYModel : public DecayProcess<XYModel> {};

and provide the two necessary interface methods

template <typename TSecondaryView>
void XYModel::doDecay(TSecondaryView&);

template <typename TParticle>
TimeType getLifetime(TParticle const&)

Where, of course, SecondaryView and Particle are the valid classes to access particles on the Stack. Those two methods do not need to be templated, they could use the types e.g. corsika::setup::Stack::particle_type but by the cost of loosing all flexibility otherwise provided.

SecondaryView allows to retrieve the properties of the projectile particles, AND to store new particles (secondaries) which then subsequently can be processes by SecondariesProcess. This is how the output of decays can be studied right away.

template<class TCountedProcess>
class corsika::InteractionCounter : public corsika::InteractionProcess<InteractionCounter<TCountedProcess>>
#include <InteractionCounter.hpp>

Wrapper around an InteractionProcess that fills histograms of the number of calls to doInteraction() binned in projectile energy (both in lab and center-of-mass frame) and species

Use by wrapping a normal InteractionProcess

InteractionProcess collision1;
InteractionClounter<collision1> counted_collision1;

Public Functions

template<typename TSecondaryView>
void doInteraction(TSecondaryView &view)

wrapper around internall process doInteraction

template<typename TParticle>
GrammageType getInteractionLength(TParticle const &particle) const

! returns internal process getInteractionLength

InteractionHistogram const &getHistogram() const

returns the filles histograms

Return

InteractionHistogram, which contains the histogram data

class corsika::InteractionHistogram
#include <InteractionHistogram.hpp>

Class that creates and stores histograms of collisions \(dN/dE_{lab}\), \(dN/d\sqrt{s}\) which is used by class InteractionCounter.

Histograms are of type boost::histogram

Public Functions

void fill(Code projectile_id, HEPEnergyType lab_energy, HEPEnergyType mass_target, int A = 0, int Z = 0)

fill both CMS and lab histograms at the same time

Parameters
  • projectile_id: corsika::Code of particle

  • lab_energy: Energy in lab. frame

  • mass_target: Mass of target particle

  • A: if projectile_id is corsika::Nucleus : Mass of nucleus

  • Z: if projectile_id is corsika::Nucleus : Charge of nucleus

inline hist_type const &CMSHist() const

return histogram in c.m.s. frame

inline hist_type const &labHist() const

return histogram in laboratory frame

template<typename TDerived>
class InteractionProcess : public corsika::BaseProcess<TDerived>
#include <InteractionProcess.hpp>

Process describing the interaction of particles.

Create a new InteractionProcess, e.g. for XYModel, via

class XYModel : public InteractionProcess<XYModel> {};

and provide the two necessary interface methods

template <typename TSecondaryView>
void XYModel::doInteraction(TSecondaryView&);

template <typename TParticle>
GrammageType XYModel::getInteractionLength(TParticle const&)

Where, of course, SecondaryView and Particle are the valid classes to access particles on the Stack. Those two methods do not need to be templated, they could use the types e.g. corsika::setup::Stack::particle_type but by the cost of loosing all flexibility otherwise provided.

SecondaryView allows to retrieve the properties of the projectile particles, AND to store new particles (secondaries) which then subsequently can be processes by SecondariesProcess. This is how the output of interactions can be studied right away.

class corsika::NullModel
#include <NullModel.hpp>

Process that does nothing.

It is not even derived from BaseProcess

Public Static Functions

static inline unsigned int constexpr getNumberOfProcesses()

Default number of processes is just one, obviously.

template<typename TDerived>
class SecondariesProcess : public corsika::BaseProcess<TDerived>
#include <SecondariesProcess.hpp>

Processes acting on the secondaries produced by other processes.

Create a new SecondariesProcess, e.g. for XYModel, via

class XYModel : public SecondariesProcess<XYModel> {};

and provide the necessary interface method:

template <typename TStackView>
void doSecondaries(TStackView& StackView);

where StackView is an object that can store secondaries on a stack and also iterate over these secondaries.

template<typename TDerived>
class corsika::StackProcess : public corsika::BaseProcess<TDerived>
#include <StackProcess.hpp>

Process to act on the entire particle stack.

Create a new StackProcess, e.g. for XYModel, via

class XYModel : public StackProcess<XYModel> {};

and provide the necessary interface method

template <typename TStack>
void XYModel::doStack(TStack&);

Where, of course, Stack is the valid class to access particles on the Stack. This methods does not need to be templated, they could use the types e.g. corsika::setup::Stack directly but by the cost of loosing all flexibility otherwise provided.

A StackProcess has only one constructor StackProcess::StackProcess(unsigned int const nStep) where nStep is the number of steps of the cascade stepping after which the stack process should be run. Good values are on the order of 1000, which will not compromise run time in the end, but provide all the benefits of the StackProcess.

The number of “steps” during the cascade processing after

which this StackProcess is going to be executed.

The logic is “iStep_ modulo nStep_”

Public Functions

inline int getStep() const

return the current Cascade step counter

inline bool checkStep()

check if current step is where StackProcess should be executed, this also increases the internal step counter implicitly

template<typename TProcess1, typename TProcess2, typename TSelect, int IndexFirstProcess = 0, int IndexOfProcess1 = count_processes<TProcess1, IndexFirstProcess>::count, int IndexOfProcess2 = count_processes<TProcess2, IndexOfProcess1>::count>
class corsika::SwitchProcessSequence : public corsika::BaseProcess<SwitchProcessSequence<TProcess1, TProcess2, TSelect>>
#include <SwitchProcessSequence.hpp>

Class to switch between two process branches.

A compile-time static list of processes that uses an internal TSelect class to switch between different versions of processes (or process sequence).

TProcess1 and TProcess2 must be derived from BaseProcess and are both references if possible (lvalue), otherwise (rvalue) they are just classes. This allows us to handle both, rvalue as well as lvalue Processes in the SwitchProcessSequence.

TSelect has to implement a operator()(const Particle&) and has to return either SwitchResult::First or SwitchResult::Second. Note: TSelect may absolutely also use random numbers to sample between its results. This can be used to achieve arbitrarily smooth transition or mixtures of processes.

Warning: do not put StackProcess into a SwitchProcessSequence since this makes no sense. The StackProcess acts on an entire particle stack and not on indiviidual particles.

Template parameters:

See also class

ProcessSequence.
Template Parameters
  • TProcess1: is of type BaseProcess, either a dedicatd process, or a ProcessSequence

  • TProcess2: is of type BaseProcess, either a dedicatd process, or a ProcessSequence

  • TSelect: selector functor/function

  • IndexFirstProcess: to count and index each Process in the entire process-chain

  • IndexOfProcess1: index of TProcess1 (counting of Process)

  • IndexOfProcess2: index of TProcess2 (counting of Process)

Public Functions

inline SwitchProcessSequence(TProcess1 in_A, TProcess2 in_B, TSelect sel)

Only valid user constructor will create fully initialized object.

SwitchProcessSequence supports and encourages move semantics. You can use object, l-value references or r-value references to construct sequences.

Parameters
  • in_A: process branch A

  • in_A: process branch B

  • sel: functor to switch between branch A and B

Public Static Functions

static inline unsigned int constexpr getNumberOfProcesses()

static counter to uniquely index (count) all ContinuousProcess in switch sequence.

template<typename TProcess1, typename TProcess2 = NullModel, int ProcessIndexOffset = 0, int IndexOfProcess1 = count_processes<TProcess1, count_processes<TProcess2, ProcessIndexOffset>::count>::count, int IndexOfProcess2 = count_processes<TProcess2, ProcessIndexOffset>::count>
class corsika::ProcessSequence : public corsika::BaseProcess<ProcessSequence<TProcess1, TProcess2>>
#include <ProcessSequence.hpp>

 Definition of a static process list/sequence

A compile time static list of processes. The compiler will generate a new type based on template logic containing all the elements provided by the user.

TProcess1 and TProcess2 must both be derived from BaseProcess, and are both references if possible (lvalue), otherwise (rvalue) they are just classes. This allows us to handle both, rvalue as well as lvalue Processes in the ProcessSequence.

(For your potential interest, the static version of the ProcessSequence and all Process types are based on the CRTP C++ design pattern)

Template parameters:

Template Parameters
  • TProcess1: is of type BaseProcess, either a dedicatd process, or a ProcessSequence

  • TProcess2: is of type BaseProcess, either a dedicatd process, or a ProcessSequence

  • IndexFirstProcess: to count and index each Process in the entire process-chain. The offset is the starting value for this ProcessSequence

  • IndexOfProcess1: index of TProcess1 (counting of Process)

  • IndexOfProcess2: index of TProcess2 (counting of Process)

Public Functions

inline ProcessSequence(TProcess1 in_A, TProcess2 in_B)

Only valid user constructor will create fully initialized object.

ProcessSequence supports and encourages move semantics. You can use object, l-value references or r-value references to construct sequences.

Parameters
  • in_A: process/list A

  • in_A: process/list B

bool checkStep()

The processes of type StackProcess do have an internal counter, so they can be exectuted only each N steps.

Often these are “maintenacne processes” that do not need to run after each single step of the simulations. In the CheckStep function it is tested if either A_ or B_ are StackProcess and if they are due for execution.

template<typename TStack>
void doStack(TStack &stack)

Execute the StackProcess-es in the ProcessSequence.

template<typename TParticle, typename TTrack>
ContinuousProcessStepLength getMaxStepLength(TParticle &particle, TTrack &vTrack)

Calculate the maximum allowed length of the next tracking step, based on all ContinuousProcess-es.

The maximum allowed step length is the minimum of the allowed track lenght over all ContinuousProcess-es in the ProcessSequence.

Return

ContinuousProcessStepLength which contains the step length itself in LengthType, and a unique identifier of the related ContinuousProcess.

Public Static Functions

static inline unsigned int constexpr getNumberOfProcesses()

static counter to uniquely index (count) all ContinuousProcess in switch sequence.