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 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

Subclassed by corsika::BoundaryCrossingProcess< TDerived >, corsika::CascadeEquationsProcess< TDerived >, corsika::ContinuousProcess< TDerived >, corsika::DecayProcess< 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 size_t 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 CascadeEquationsProcess : public corsika::BaseProcess<TDerived>
#include <CascadeEquationsProcess.hpp>

Processes executing cascade-equations calculations.

Create a new CascadeEquationsProcess, e.g. for XYModel, via:

class XYModel : public CascadeEquationsProcess<XYModel> {};

and provide the necessary interface method:

template <typename TStack>
void doCascadeEquations(TStack& stack);

Cascade equation processes may generate new particles on the stack. They also typically generate output.

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 &#8212; 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. In user code those two methods do not need to be templated, they could use the types e.g. corsika::setup::Stack::particle_type &#8212; 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 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, Code const, Code const, FourMomentum const&, FourMomentum const&)

Wrapper around internal process doInteraction.

CrossSectionType getCrossSection(Code const, Code const, FourMomentum const&, FourMomentum const&) const

Wrapper around internal process getCrossSection.

InteractionHistogram const &getHistogram() const

returns the filles histograms.

Returns:

InteractionHistogram, which contains the histogram data

class 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 const projectile_id, HEPEnergyType const lab_energy, HEPEnergyType const mass_target)

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 Nucleus : Mass of nucleus

  • Z – if projectile_id is 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<class TUnderlyingProcess>
class InteractionLengthModifier : public corsika::InteractionProcess<InteractionLengthModifier<TUnderlyingProcess>>
#include <InteractionLengthModifier.hpp>

Wrapper around an InteractionProcess that allows modifying the interaction length returned by the underlying process in a user-defined way. *

Public Types

using functor_signature = GrammageType(GrammageType, corsika::Code, HEPEnergyType)

The signature of the modifying functor. Arguments are original int. length, PID, energy.

Public Functions

InteractionLengthModifier(TUnderlyingProcess &&process, std::function<functor_signature> modifier)

Create wrapper around InteractionProcess.

Note that the passed process object itself may no longer be used, only through this class.

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

wrapper around internal process doInteraction

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

! returns underlying process getInteractionLength modified

TUnderlyingProcess const &getProcess() const

! obtain reference to wrapped process

template<typename TModel>
class InteractionProcess : public corsika::BaseProcess<TModel>
#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. In user code, those two methods do not need to be templated, they could use the types e.g. corsika::setup::Stack::particle_type &#8212; 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 NullModel
#include <NullModel.hpp>

Process that does nothing.

It is not even derived from BaseProcess. But it can be added to a ProcessSequence.

Public Static Functions

static inline size_t constexpr getNumberOfProcesses()

Default number of processes is zero, 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 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 &#8212; but by the cost of loosing all flexibility otherwise provided.

A StackProcess has only one constructor StackProcess::StackProcess(unsigned int const nStep) where nStep ( \(n_{step}\)) 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 a StackProcess is going to be executed is determined from iStep_ and nStep_ using the modulo: \( !(iStep\_ \; \% \; nStep\_) \). And iStep_ is increased for each evaluation (step).

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 TCondition, typename TSequence, typename USequence, int IndexFirstProcess = 0, int IndexOfProcess1 = count_processes<TSequence, IndexFirstProcess>::count, int IndexOfProcess2 = count_processes<USequence, IndexOfProcess1>::count>
class SwitchProcessSequence : public corsika::BaseProcess<SwitchProcessSequence<TCondition, TSequence, USequence>>
#include <SwitchProcessSequence.hpp>

Class to switch between two process branches.

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

TSequence and USequence 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. Please use the corsika::make_select(condition, sequence, alt_sequence) factory function for best results.

TCondition has to implement a bool operator()(Particle const&). Note: TCondition 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:
  • TCondition – selector functor/function

  • TSequence – is of type BaseProcess, either a dedicatd process, or a ProcessSequence

  • USequence – is of type BaseProcess, either a dedicatd process, or a ProcessSequence

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

  • IndexOfProcess1 – index of TSequence (counting of Process)

  • IndexOfProcess2 – index of USequence (counting of Process)

Public Functions

inline SwitchProcessSequence(TCondition sel, TSequence in_A, USequence in_B)

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:
  • sel – functor to switch between branch A and B

  • in_A – process branch A

  • in_B – process branch B

Public Static Functions

static inline size_t constexpr getNumberOfProcesses()

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

template<typename TProcess1, typename TProcess2 = NullModel, int ProcessIndexOffset = 0, int IndexOfProcess1 = corsika::count_processes<TProcess1, corsika::count_processes<TProcess2, ProcessIndexOffset>::count>::count, int IndexOfProcess2 = corsika::count_processes<TProcess2, ProcessIndexOffset>::count>
class 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

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:
template<typename TParticle>
ProcessReturn doBoundaryCrossing(TParticle &particle, typename TParticle::node_type const &from, typename TParticle::node_type const &to)

List of all BoundaryProcess.

Template Parameters:

TParticle

Parameters:
  • particle – The particle.

  • from – Volume the particle is exiting.

  • to – Volume the particle is entering.

Returns:

ProcessReturn

template<typename TSecondaries>
void doSecondaries(TSecondaries &vS)

Process all secondaries in TSecondaries.

The seondaries produced by other processes and accessible via TSecondaries are processed by all SecondariesProcesse via a call here.

Template Parameters:

TSecondaries

Parameters:

vS

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 TStack>
void doCascadeEquations(TStack &stack)

Execute the CascadeEquationsProcess-es in the ProcessSequence.

void initCascadeEquations()

Init the CascadeEquationsProcess-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.

Template Parameters:
  • TParticle – particle type.

  • TTrack – the trajectory type.

Parameters:
  • particle – The particle data object.

  • track – The track data object.

Returns:

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

template<typename TParticle>
CrossSectionType getCrossSection(TParticle const &projectile, Code const targetId, FourMomentum const &targetP4) const

Calculates the cross section of a projectile with a target.

Template Parameters:

TParticle

Parameters:
  • projectile

  • targetId

  • targetP4

Returns:

CrossSectionType

template<typename TSecondaryView, typename TRNG>
inline ProcessReturn selectInteraction(TSecondaryView &&view, FourMomentum const &projectileP4, NuclearComposition const &composition, TRNG &&rng, CrossSectionType const cx_select, CrossSectionType cx_sum = CrossSectionType::zero())

Selects one concrete InteractionProcess and samples a target nucleus from the material.

The selectInteraction method statically loops over all active InteractionProcess and calculates the material-weighted cross section for all of them. In an iterative way those cross sections are summed up. The random number cx_select, uniformely drawn from the cross section before energy losses, is used to discriminate the selected sub-process here. If the cross section after the step smaller than it was before, there is a non-zero probability that the particle survives and no interaction takes place. This method becomes imprecise when cross section rise with falling energies.

If a sub-process was selected, the target nucleus is selected from the material (weighted with cross section). The interaction is then executed.

Template Parameters:
  • TSecondaryView – Object type as storage for new secondary particles.

  • TRNG – Object type to produce random numbers.

Parameters:
  • view – Object to store new secondary particles.

  • projectileP4 – The four momentum of the projectile.

  • composition – The environment/material composition.

  • rng – Random number object.

  • cx_select – Drawn random numer, uniform between [0, cx_initial]

  • cx_sum – For interal use, to sum up cross section contributions.

Returns:

ProcessReturn

Public Static Functions

static inline size_t constexpr getNumberOfProcesses()

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