|
| 1 | +#ifndef SIMCORE_BERTINI_EVENTTOPOLOGY_PROCESS_H |
| 2 | +#define SIMCORE_BERTINI_EVENTTOPOLOGY_PROCESS_H |
| 3 | + |
| 4 | +#include <G4CascadeInterface.hh> |
| 5 | +#include <G4EventManager.hh> |
| 6 | +#include <G4HadFinalState.hh> |
| 7 | +#include <G4HadProjectile.hh> |
| 8 | +#include <G4HadronicInteraction.hh> |
| 9 | +#include <G4Nucleus.hh> |
| 10 | +#include <iostream> |
| 11 | + |
| 12 | +#include "SimCore/PhotoNuclearModel.h" |
| 13 | +#include "SimCore/UserEventInformation.h" |
| 14 | +namespace simcore { |
| 15 | + |
| 16 | +/* |
| 17 | +** A wrapper interface around the Bertini cascade process (G4CascadeInterface) |
| 18 | +** which reruns the event generator until a particular condition is met for the |
| 19 | +** products. The decision of whether or not to rerun the event generator is |
| 20 | +** handled by the acceptEvent virtual function. |
| 21 | +** |
| 22 | +** For example of a derived class, see |
| 23 | +** SimCore/include/SimCore/PhotoNuclearModels/BertiniNothingHardModel.h |
| 24 | +** |
| 25 | +** Note: You almost certainly want to use these types of models together with a |
| 26 | +** photonuclear topology filter. |
| 27 | +** |
| 28 | +** Note: When performing N attempts, this will increment the event weight in the |
| 29 | +** UserEventInformation by 1/N. To change this behaviour, override the |
| 30 | +** incrementEventWeight function. |
| 31 | +*/ |
| 32 | + |
| 33 | +class BertiniEventTopologyProcess : public G4CascadeInterface { |
| 34 | + public: |
| 35 | + BertiniEventTopologyProcess(bool count_light_ions = true) |
| 36 | + : G4CascadeInterface{}, count_light_ions_{count_light_ions} {} |
| 37 | + |
| 38 | + /* |
| 39 | + * The primary function for derived classes to customize. After each call to |
| 40 | + * the Bertini cascade, this function will be called to see whether or not to |
| 41 | + * keep the event. The products can be accessed from the `theParticleChange` |
| 42 | + * member inherited from G4CascadeInterface (i.e. the Bertini cascade). |
| 43 | + */ |
| 44 | + virtual bool acceptEvent() const = 0; |
| 45 | + |
| 46 | + /* |
| 47 | + * Is the projectile of interest? |
| 48 | + * |
| 49 | + * If false, the cascade will only run once. Example use includes only |
| 50 | + * applying repeated simulations for particular energy ranges. Is called |
| 51 | + * automatically during `ApplyYourself`. |
| 52 | + * |
| 53 | + **/ |
| 54 | + virtual bool acceptProjectile(const G4HadProjectile& projectile) const = 0; |
| 55 | + |
| 56 | + /* |
| 57 | + * Is the target nucleus of interest? |
| 58 | + * |
| 59 | + * If false, the cascade will only run once. Example use includes only |
| 60 | + * applying repeated simulations for high Z materials. Is called |
| 61 | + * automatically during `ApplyYourself`. |
| 62 | + * |
| 63 | + **/ |
| 64 | + virtual bool acceptTarget(const G4Nucleus& targetNucleus) const = 0; |
| 65 | + |
| 66 | + /* |
| 67 | + * Run the Bertini cascade until the condition given by `acceptEvent` is |
| 68 | + * matched by the reaction products. Increments the event weight by 1/n where |
| 69 | + * n is the number of attempts needed to produce a match. |
| 70 | + * |
| 71 | + **/ |
| 72 | + G4HadFinalState* ApplyYourself(const G4HadProjectile& projectile, |
| 73 | + G4Nucleus& targetNucleus) override; |
| 74 | + |
| 75 | + /* |
| 76 | + * Geant4 assumes that secondaries produced from the bertini cascade are owned |
| 77 | + * by some other part of the code. Since we are re-running the cascade until |
| 78 | + * we get something matching our condition in `acceptEvent`, we have to make |
| 79 | + * sure to clean up any secondaries from failed attempts. |
| 80 | + * |
| 81 | + */ |
| 82 | + void cleanupSecondaries(); |
| 83 | + |
| 84 | + /** |
| 85 | + * Check if the PDG code corresponds to a light ion nucleus. |
| 86 | + * |
| 87 | + * Nuclear PDG codes are given by ±10LZZZAAAI So to find the atomic |
| 88 | + * number, we first divide by 10 (to lose the I-component) and then |
| 89 | + * take the modulo with 1000. |
| 90 | + * |
| 91 | + */ |
| 92 | + constexpr bool isLightIon(const int pdgCode) const { |
| 93 | + // |
| 94 | + // TODO: Is the < check necessary? |
| 95 | + if (pdgCode > 1000000000 && pdgCode < 10000000000) { |
| 96 | + // Check if the atomic number is less than or equal to 4 |
| 97 | + return ((pdgCode / 10) % 1000) <= 4; |
| 98 | + } |
| 99 | + return false; |
| 100 | + } |
| 101 | + |
| 102 | + /** |
| 103 | + * Whether or not to include a particular particle type in any counting. |
| 104 | + * Unless \ref count_light_ions_ is set, we don't count anything with a |
| 105 | + * nuclear PDG code. This is consistent with the counting behaviour used in |
| 106 | + * the PhotoNuclearDQM. |
| 107 | + * |
| 108 | + * If \ref count_light_ions_ is set, we also match PDG codes for nuclei with |
| 109 | + * atomic number < 4. |
| 110 | + * |
| 111 | + * @see isLightIon |
| 112 | + * |
| 113 | + */ |
| 114 | + constexpr bool skipCountingParticle(const int pdgcode) const { |
| 115 | + return !(pdgcode < 10000 || count_light_ions_ && isLightIon(pdgcode)); |
| 116 | + } |
| 117 | + |
| 118 | + /* |
| 119 | + * Update the event weight depending on the number of attempts made. |
| 120 | + * |
| 121 | + * @param N The number of attempts used for a successful topology |
| 122 | + * production. |
| 123 | + * |
| 124 | + **/ |
| 125 | + |
| 126 | + virtual void incrementEventWeight(int N) { |
| 127 | + auto event_info{static_cast<UserEventInformation*>( |
| 128 | + G4EventManager::GetEventManager()->GetUserInformation())}; |
| 129 | + event_info->incWeight(1. / N); |
| 130 | + } |
| 131 | + |
| 132 | + private: |
| 133 | + bool count_light_ions_; |
| 134 | +}; |
| 135 | +} // namespace simcore |
| 136 | + |
| 137 | +#endif /* SIMCORE_BERTINI_EVENTTOPOLOGY_PROCESS_H */ |
0 commit comments