4545#include < G4VUserPhysicsList.hh>
4646#include < G4ParticleTable.hh>
4747#include < G4ParticleDefinition.hh>
48+ #include < G4PhysicsListHelper.hh>
4849#include < G4ProcessManager.hh>
4950#include < G4ProcessVector.hh>
5051#include < G4VEnergyLossProcess.hh>
@@ -92,7 +93,8 @@ inline bool isMonopolePDG(int pdg) { return o2::sim::isMonopole(pdg); }
9293// / so that G4Transportation queries the field for it
9394constexpr G4double gMonopoleFieldGateMoment = 1.0e-20 ;
9495
95- // Extent of the TPC drift region
96+ // Extent of the TPC field cage, i.e. the region with the drift field (smaller than
97+ // the TPC_Drift volume, which extends beyond the endcaps and the field-cage rods)
9698constexpr G4double gTPCFieldCageRMin = 83.5 * CLHEP ::cm;
9799constexpr G4double gTPCFieldCageRMax = 254.5 * CLHEP ::cm;
98100constexpr G4double gTPCFieldCageZMax = 249.525 * CLHEP ::cm;
@@ -107,6 +109,11 @@ inline double tpcDriftFieldMagnitude()
107109 }
108110 try {
109111 const double valueKVPerCm = o2::conf::ConfigurableParam::getValueAs<float >(" TPCGEMParam.ElectricField[0]" );
112+ if (valueKVPerCm <= 0 .) {
113+ LOG (warn) << " O2MonopolePhysics: TPCGEMParam.ElectricField[0] = " << valueKVPerCm
114+ << " kV/cm; the monopole is not coupled to a drift field" ;
115+ return 0 .;
116+ }
110117 return valueKVPerCm * CLHEP ::kilovolt / CLHEP ::cm;
111118 } catch (...) {
112119 LOG (warn) << " O2MonopolePhysics: the TPC is in the geometry but TPCGEMParam is not "
@@ -159,6 +166,9 @@ class O2MonopoleEquation : public G4EquationOfMotion
159166 // Non-zero only while an O2 monopole is being transported. The sign follows
160167 // the PDG sign, so monopole and anti-monopole are pushed in opposite
161168 // directions along B.
169+ // TODO: both species are electrically neutral today, so their magnetic sign is
170+ // the same function of the PDG sign; once they carry electric charge the
171+ // asymmetric species (4120000) needs the opposite relative sign.
162172 double signedMagneticCharge = 0 .;
163173 int pdg = 0 ;
164174 if (const G4Track* track = currentTrack ()) {
@@ -462,9 +472,8 @@ class O2MonopolePhysics : public G4VUserPhysicsList
462472 pmanager->RemoveProcess (proc);
463473 }
464474 }
465- // Ordering (AtRest, AlongStep, PostStep) = (-1, 1, 1) as in the Geant4
466- // monopole example: continuous-and-discrete energy loss, not active at rest.
467- pmanager->AddProcess (new G4mplIonisation (mMagneticCharge ), -1 , 1 , 1 );
475+ // Ordered by G4PhysicsListHelper from the process subtype, as in the Geant4 monopole example
476+ G4PhysicsListHelper::GetPhysicsListHelper ()->RegisterProcess (new G4mplIonisation (mMagneticCharge ), particle);
468477
469478 // G4Transportation only looks up the field when the particle has a
470479 // non-zero electric charge or a non-zero magnetic moment (μ) and a monopole has
@@ -542,6 +551,11 @@ TG4RunConfiguration* createMonopoleRunConfiguration(const TString& userGeometry,
542551 // One Dirac charge g_D = eplus / (2*alpha) ~= 68.5 eplus (Dirac quantisation).
543552 // magneticChargeDirac counts Dirac charges (1.0 == classic single monopole),
544553 // matching the "g_1" convention used by the O2 monopole generator input.
554+ // G4mplIonisation silently replaces a zero charge with one Dirac charge, while the
555+ // equation of motion would use the zero, so non-positive values are rejected
556+ if (!(magneticChargeDirac > 0 .)) {
557+ LOG (fatal) << " O2MonopolePhysics: G4.monopoleMagneticCharge must be positive, got " << magneticChargeDirac;
558+ }
545559 const double gDirac = CLHEP ::eplus / (2.0 * CLHEP ::fine_structure_const);
546560 const double magneticChargeEplus = magneticChargeDirac * gDirac ;
547561 LOG (info) << " Monopole ionisation enabled: magnetic charge = " << magneticChargeDirac
0 commit comments