Belle II Software development
G4LongLivedNeutralTransportation.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9// modified from GEANT4 G4Transportation class
10
11#include <framework/logging/Logger.h>
12
13#include "G4TransportationProcessType.hh"
14
15#include "G4PhysicalConstants.hh"
16#include "G4SystemOfUnits.hh"
17#include "G4ProductionCutsTable.hh"
18#include "G4PrimaryParticle.hh"
19
20#include "G4FieldManagerStore.hh"
21#include <simulation/longlivedneutral/G4LongLivedNeutralTransportation.h>
22
23class G4VSensitiveDetector;
24
25using namespace Belle2;
26
28
30//
31// Constructor
32
34 : G4VProcess(G4String("LongLivedNeutralTransportation"), fTransportation),
35 fFieldExertedForce(false),
36 fPreviousSftOrigin(0., 0., 0.),
37 fPreviousSafety(0.0),
38 fEndPointDistance(-1.0),
39 fShortStepOptimisation(false) // Old default: true (=fast short steps)
40{
41 SetProcessSubType(static_cast<G4int>(TRANSPORTATION));
42 pParticleChange = &fParticleChange; // Required to conform to G4VProcess
43 SetVerboseLevel(verbosity);
44
45 G4TransportationManager* transportMgr ;
46
47 transportMgr = G4TransportationManager::GetTransportationManager() ;
48
49 fLinearNavigator = transportMgr->GetNavigatorForTracking() ;
50
51 fFieldPropagator = transportMgr->GetPropagatorInField() ;
52
53
54
55
57 // Use the old defaults: Warning = 100 MeV, Important = 250 MeV, No Trials = 10;
58
59 // Cannot determine whether a field exists here, as it would
60 // depend on the relative order of creating the detector's
61 // field and this process. That order is not guaranteed.
63 // This value must be updated using DoesAnyFieldExist() at least at the
64 // start of each Run -- for now this is at the Start of every Track. TODO
65
66 static G4ThreadLocal G4TouchableHandle* pNullTouchableHandle = 0;
67 pNullTouchableHandle = new G4TouchableHandle;
68 fCurrentTouchableHandle = *pNullTouchableHandle;
69 // Points to (G4VTouchable*) 0
70
71
72#ifdef G4VERBOSE
73 if (verboseLevel > 0) {
74 G4cout << " G4LongLivedNeutralTransportation constructor> set fShortStepOptimisation to ";
75 if (fShortStepOptimisation) { G4cout << "true" << G4endl; }
76 else { G4cout << "false" << G4endl; }
77 }
78#endif
79}
80
82
84{
85 if (fSumEnergyKilled > 0.0) {
86 PrintStatistics(G4cout);
87 }
88
89}
90
92
93void
95{
96 outStr << " G4LongLivedNeutralTransportation: Statistics for looping particles " << G4endl;
97 outStr << " No looping tracks found or killed. " << G4endl;
98
99}
100
102//
103// Responsibilities:
104// Find whether the geometry limits the Step, and to what length
105// Calculate the new value of the safety and return it.
106// Store the final time, position and momentum.
107
109AlongStepGetPhysicalInteractionLength(const G4Track& track,
110 G4double, // previousStepSize
111 G4double currentMinimumStep,
112 G4double& currentSafety,
113 G4GPILSelection* selection)
114{
115 G4double geometryStepLength = -1.0, newSafety = -1.0;
116
117 // Initial actions moved to StartTrack()
118 // --------------------------------------
119 // Note: in case another process changes touchable handle
120 // it will be necessary to add here (for all steps)
121 // fCurrentTouchableHandle = aTrack->GetTouchableHandle();
122
123 // GPILSelection is set to default value of CandidateForSelection
124 // It is a return value
125 //
126 *selection = CandidateForSelection ;
127
129 fLastStepInVolume = false;
130 fNewTrack = false;
131
132 fParticleChange.ProposeFirstStepInVolume(fFirstStepInVolume);
133
134 // Get initial Energy/Momentum of the track
135 //
136 const G4DynamicParticle* pParticle = track.GetDynamicParticle() ;
137 const G4PrimaryParticle* primaryParticle = pParticle->GetPrimaryParticle();
138 G4ThreeVector startMomentumDir = primaryParticle->GetMomentumDirection() ;
139 G4ThreeVector startPosition = track.GetPosition() ;
140
141 // The Step Point safety can be limited by other geometries and/or the
142 // assumptions of any process - it's not always the geometrical safety.
143 // We calculate the starting point's isotropic safety here.
144 //
145 G4ThreeVector OriginShift = startPosition - fPreviousSftOrigin ;
146 G4double MagSqShift = OriginShift.mag2() ;
147 if (MagSqShift >= sqr(fPreviousSafety)) {
148 currentSafety = 0.0 ;
149 } else {
150 currentSafety = fPreviousSafety - std::sqrt(MagSqShift) ;
151 }
152
153 // Is the particle charged or has it a magnetic moment?
154 //
155 G4double particleCharge = 0 ;
156
157 fGeometryLimitedStep = false ;
158
159 // There is no need to locate the current volume. It is Done elsewhere:
160 // On track construction
161 // By the tracking, after all AlongStepDoIts, in "Relocation"
162
163 G4double linearStepLength ;
164 if (fShortStepOptimisation && (currentMinimumStep <= currentSafety)) {
165 // The Step is guaranteed to be taken
166 //
167 geometryStepLength = currentMinimumStep ;
168 fGeometryLimitedStep = false ;
169 } else {
170 // Find whether the straight path intersects a volume
171 //
172 linearStepLength = fLinearNavigator->ComputeStep(startPosition,
173 startMomentumDir,
174 currentMinimumStep,
175 newSafety) ;
176 // Remember last safety origin & value.
177 //
178 fPreviousSftOrigin = startPosition ;
179 fPreviousSafety = newSafety ;
180
181
182 currentSafety = newSafety ;
183
184 fGeometryLimitedStep = (linearStepLength <= currentMinimumStep);
186 // The geometry limits the Step size (an intersection was found.)
187 geometryStepLength = linearStepLength ;
188 } else {
189 // The full Step is taken.
190 geometryStepLength = currentMinimumStep ;
191 }
192
193 fEndPointDistance = geometryStepLength ;
194
195 // Calculate final position
196 //
197 fTransportEndPosition = startPosition + geometryStepLength * startMomentumDir;
198
199 // Momentum direction, energy and polarisation are unchanged by transport
200 //
201 fTransportEndMomentumDir = startMomentumDir ;
202 fTransportEndKineticEnergy = primaryParticle->GetKineticEnergy() ;
203 fTransportEndSpin = track.GetPolarization();
204 fMomentumChanged = false ;
205 fEndGlobalTimeComputed = false ;
206 }
207
208
209 // If we are asked to go a step length of 0, and we are on a boundary
210 // then a boundary will also limit the step -> we must flag this.
211 //
212 if (currentMinimumStep == 0.0) {
213 if (currentSafety == 0.0) { fGeometryLimitedStep = true; }
214 }
215
216 // Update the safety starting from the end-point,
217 // if it will become negative at the end-point.
218 //
219 if (currentSafety < fEndPointDistance) {
220 if (particleCharge != 0.0) {
221 G4double endSafety =
223 currentSafety = endSafety ;
225 fPreviousSafety = currentSafety ;
226
227
228 // Because the Stepping Manager assumes it is from the start point,
229 // add the StepLength
230 //
231 currentSafety += fEndPointDistance ;
232
233#ifdef G4DEBUG_TRANSPORT
234 G4cout.precision(12) ;
235 G4cout << "***G4LongLivedNeutralTransportation::AlongStepGPIL ** " << G4endl ;
236 G4cout << " Called Navigator->ComputeSafety at " << fTransportEndPosition
237 << " and it returned safety= " << endSafety << G4endl ;
238 G4cout << " Adding endpoint distance " << fEndPointDistance
239 << " to obtain pseudo-safety= " << currentSafety << G4endl ;
240 } else {
241 G4cout << "***G4LongLivedNeutralTransportation::AlongStepGPIL ** " << G4endl ;
242 G4cout << " Avoiding call to ComputeSafety : " << G4endl;
243 G4cout << " charge = " << particleCharge << G4endl;
244 G4cout << " mag moment = " << magneticMoment << G4endl;
245#endif
246 }
247 }
248
249 fParticleChange.ProposeTrueStepLength(geometryStepLength) ;
250
251 return geometryStepLength ;
252}
253
255//
256// Initialize ParticleChange (by setting all its members equal
257// to corresponding members in G4Track)
258
259G4VParticleChange* G4LongLivedNeutralTransportation::AlongStepDoIt(const G4Track& track,
260 const G4Step& stepData)
261{
262 // static G4ThreadLocal G4long noCallsASDI = 0;
263 // noCallsASDI++;
264
265 fParticleChange.Initialize(track) ;
266
267 // Code for specific process
268 //
269 fParticleChange.ProposePosition(fTransportEndPosition) ;
270 fParticleChange.ProposeMomentumDirection(fTransportEndMomentumDir) ;
272 fParticleChange.SetMomentumChanged(fMomentumChanged) ;
273
274 fParticleChange.ProposePolarization(fTransportEndSpin);
275
276 G4double deltaTime = 0.0 ;
277
278 // Calculate Lab Time of Flight (ONLY if field Equations used it!)
279 // G4double endTime = fCandidateEndGlobalTime;
280 // G4double delta_time = endTime - startTime;
281
282 G4double startTime = track.GetGlobalTime() ;
283
284 const G4DynamicParticle* pParticle = track.GetDynamicParticle() ;
285 const G4PrimaryParticle* primaryParticle = pParticle->GetPrimaryParticle();
286
287
289 // The time was not integrated .. make the best estimate possible
290 //
291
292 // use c*beta from primary particle, llp will not be slowed down anyways
293 // default was: stepData.GetPreStepPoint()->GetVelocity();, but assumes wrong values from particle definition
294 B2DEBUG(0, "Velocity calculated from Step data: " << stepData.GetPreStepPoint()->GetVelocity());
295
296 G4double initialVelocity = c_light * primaryParticle->GetTotalMomentum() / primaryParticle->GetTotalEnergy();
297 G4double stepLength = track.GetStepLength();
298
299 deltaTime = 0.0; // in case initialVelocity = 0
300 if (initialVelocity > 0.0) { deltaTime = stepLength / initialVelocity; }
301
302 fCandidateEndGlobalTime = startTime + deltaTime ;
303 fParticleChange.ProposeLocalTime(track.GetLocalTime() + deltaTime) ;
304 } else {
305 deltaTime = fCandidateEndGlobalTime - startTime ;
306 fParticleChange.ProposeGlobalTime(fCandidateEndGlobalTime) ;
307 }
308
309 // Now Correct by Lorentz factor to get delta "proper" Time
310 G4double restMass = primaryParticle->GetMass() ;
311 G4double deltaProperTime = deltaTime * (restMass / primaryParticle->GetTotalEnergy()) ;
312
313 fParticleChange.ProposeProperTime(track.GetProperTime() + deltaProperTime) ;
314 //fParticleChange.ProposeTrueStepLength( track.GetStepLength() ) ;
315
316 // Another (sometimes better way) is to use a user-limit maximum Step size
317 // to alleviate this problem ..
318
319 // Introduce smooth curved trajectories to particle-change
320 //
321 fParticleChange.SetPointerToVectorOfAuxiliaryPoints
322 (fFieldPropagator->GimmeTrajectoryVectorAndForgetIt());
323
324 return &fParticleChange ;
325}
326
328//
329// This ensures that the PostStep action is always called,
330// so that it can do the relocation if it is needed.
331//
332
335 G4double, // previousStepSize
336 G4ForceCondition* pForceCond)
337{
338 fFieldExertedForce = false; // Not known
339 *pForceCond = Forced ;
340 return DBL_MAX ; // was kInfinity ; but convention now is DBL_MAX
341}
342
344//
345
346G4VParticleChange* G4LongLivedNeutralTransportation::PostStepDoIt(const G4Track& track,
347 const G4Step&)
348{
349 G4TouchableHandle retCurrentTouchable ; // The one to return
350 G4bool isLastStep = false;
351
352 // Initialize ParticleChange (by setting all its members equal
353 // to corresponding members in G4Track)
354 // fParticleChange.Initialize(track) ; // To initialise TouchableChange
355
356 fParticleChange.ProposeTrackStatus(track.GetTrackStatus()) ;
357
358 // If the Step was determined by the volume boundary,
359 // logically relocate the particle
360
362 // fCurrentTouchable will now become the previous touchable,
363 // and what was the previous will be freed.
364 // (Needed because the preStepPoint can point to the previous touchable)
365
366 fLinearNavigator->SetGeometricallyLimitedStep() ;
368 LocateGlobalPointAndUpdateTouchableHandle(track.GetPosition(),
369 track.GetMomentumDirection(),
371 true) ;
372 // Check whether the particle is out of the world volume
373 // If so it has exited and must be killed.
374 //
375 if (fCurrentTouchableHandle->GetVolume() == 0) {
376 fParticleChange.ProposeTrackStatus(fStopAndKill) ;
377 }
378 retCurrentTouchable = fCurrentTouchableHandle ;
379 fParticleChange.SetTouchableHandle(fCurrentTouchableHandle) ;
380
381 // Update the Step flag which identifies the Last Step in a volume
383 isLastStep = fLinearNavigator->ExitedMotherVolume()
384 or fLinearNavigator->EnteredDaughterVolume() ;
385 else
386 isLastStep = fFieldPropagator->IsLastStepInVolume();
387 } else { // fGeometryLimitedStep is false
388 // This serves only to move the Navigator's location
389 //
390 fLinearNavigator->LocateGlobalPointWithinVolume(track.GetPosition()) ;
391
392 // The value of the track's current Touchable is retained.
393 // (and it must be correct because we must use it below to
394 // overwrite the (unset) one in particle change)
395 // It must be fCurrentTouchable too ??
396 //
397 fParticleChange.SetTouchableHandle(track.GetTouchableHandle()) ;
398 retCurrentTouchable = track.GetTouchableHandle() ;
399
400 isLastStep = false;
401 } // endif ( fGeometryLimitedStep )
402 fLastStepInVolume = isLastStep;
403
404 fParticleChange.ProposeFirstStepInVolume(fFirstStepInVolume);
405 fParticleChange.ProposeLastStepInVolume(isLastStep);
406
407 const G4VPhysicalVolume* pNewVol = retCurrentTouchable->GetVolume() ;
408 G4Material* pNewMaterial = nullptr ;
409 G4VSensitiveDetector* pNewSensitiveDetector = nullptr ;
410
411 if (pNewVol != 0) {
412 pNewMaterial = pNewVol->GetLogicalVolume()->GetMaterial();
413 pNewSensitiveDetector = pNewVol->GetLogicalVolume()->GetSensitiveDetector();
414 }
415
416 fParticleChange.SetMaterialInTouchable(pNewMaterial) ;
417 fParticleChange.SetSensitiveDetectorInTouchable(pNewSensitiveDetector) ;
418
419 const G4MaterialCutsCouple* pNewMaterialCutsCouple = 0;
420 if (pNewVol != 0) {
421 pNewMaterialCutsCouple = pNewVol->GetLogicalVolume()->GetMaterialCutsCouple();
422 }
423
424 if (pNewVol != 0 && pNewMaterialCutsCouple != 0
425 && pNewMaterialCutsCouple->GetMaterial() != pNewMaterial) {
426 // for parametrized volume
427 //
428 pNewMaterialCutsCouple =
429 G4ProductionCutsTable::GetProductionCutsTable()
430 ->GetMaterialCutsCouple(pNewMaterial,
431 pNewMaterialCutsCouple->GetProductionCuts());
432 }
433 fParticleChange.SetMaterialCutsCoupleInTouchable(pNewMaterialCutsCouple);
434
435 // temporarily until Get/Set Material of ParticleChange,
436 // and StepPoint can be made const.
437 // Set the touchable in ParticleChange
438 // this must always be done because the particle change always
439 // uses this value to overwrite the current touchable pointer.
440 //
441 fParticleChange.SetTouchableHandle(retCurrentTouchable) ;
442
443 return &fParticleChange ;
444}
445
447// New method takes over the responsibility to reset the state of
448// G4LongLivedNeutralTransportation object at the start of a new track or the resumption
449// of a suspended track.
450//
451
452void
454{
455 G4VProcess::StartTracking(aTrack);
456 fNewTrack = true;
457 fFirstStepInVolume = true;
458 fLastStepInVolume = false;
459
460 // The actions here are those that were taken in AlongStepGPIL
461 // when track.GetCurrentStepNumber()==1
462
463 // Whether field exists should be determined at run level -- TODO
465
466 // reset safety value and center
467 //
468 fPreviousSafety = 0.0 ;
469 fPreviousSftOrigin = G4ThreeVector(0., 0., 0.) ;
470
471 // reset looping counter -- for motion in field
472 fNoLooperTrials = 0;
473 // Must clear this state .. else it depends on last track's value
474 // --> a better solution would set this from state of suspended track TODO ?
475 // Was if( aTrack->GetCurrentStepNumber()==1 ) { .. }
476
477 // ChordFinder reset internal state
478 //
480 fFieldPropagator->ClearPropagatorState();
481 // Resets all state of field propagator class (ONLY) including safety
482 // values (in case of overlaps and to wipe for first track).
483 }
484
485 // Make sure to clear the chord finders of all fields (i.e. managers)
486 //
487 G4FieldManagerStore* fieldMgrStore = G4FieldManagerStore::GetInstance();
488 fieldMgrStore->ClearAllChordFindersState();
489
490 // Update the current touchable handle (from the track's)
491 //
492 fCurrentTouchableHandle = aTrack->GetTouchableHandle();
493
494 // Inform field propagator of new track
495 //
496 if (fFieldPropagator) fFieldPropagator->PrepareNewTrack();
497}
498
500//
501
502
504//
505// Suppress (or not) warnings about 'looping' particles
506
508{
509 fSilenceLooperWarnings = val; // Flag to *Suppress* all 'looper' warnings
510 // G4CoupledTransportation::fSilenceLooperWarnings= val;
511}
512
514//
516{
518}
519
520
522//
524{
525 // Restores the old high values -- potentially appropriate for energy-frontier
526 // HEP experiments.
527 // Caution: All tracks with E < 100 MeV that are found to loop are
528 SetThresholdWarningEnergy(100.0 * CLHEP::MeV); // Warn above this energy
529 SetThresholdImportantEnergy(250.0 * CLHEP::MeV); // Extra trial above this En
530
531 G4int maxTrials = 10;
532 SetThresholdTrials(maxTrials);
533
534
535
536}
537
539void G4LongLivedNeutralTransportation::SetLowLooperThresholds() // Values for low-E applications
540{
541 // These values were the default in Geant4 10.5 - beta
542 SetThresholdWarningEnergy(1.0 * CLHEP::keV); // Warn above this En
543 SetThresholdImportantEnergy(1.0 * CLHEP::MeV); // Extra trials above it
544
545 G4int maxTrials = 30; // A new value - was 10
546 SetThresholdTrials(maxTrials);
547
548}
549
551//
552
554//
555
557//
559
560// StreamInfo(std::ostream& out, const G4ParticleDefinition& part, G4bool rst) const
561
562{
563 G4String indent = " "; // : "");
564 G4int oldPrec = outStream.precision(6);
565 // outStream << std::setprecision(6);
566 outStream << G4endl << indent << GetProcessName() << ": ";
567
568 outStream << " Parameters for looping particles: " << G4endl
569 << " warning-E = " << fThreshold_Warning_Energy / CLHEP::MeV << " MeV " << G4endl
570 << " important E = " << fThreshold_Important_Energy / CLHEP::MeV << " MeV " << G4endl
571 << " thresholdTrials " << fThresholdTrials << G4endl;
572 outStream.precision(oldPrec);
573}
G4TouchableHandle fCurrentTouchableHandle
Current touchable handle.
void PrintStatistics(std::ostream &outStr) const
returns current logging info of the algorithm
G4VParticleChange * PostStepDoIt(const G4Track &track, const G4Step &stepData)
G4VProcess::PostStepDoIt() implementation,.
G4double fCandidateEndGlobalTime
The particle's state after this Step, Store for DoIt.
G4double AlongStepGetPhysicalInteractionLength(const G4Track &track, G4double previousStepSize, G4double currentMinimumStep, G4double &currentSafety, G4GPILSelection *selection)
G4VProcess::AlongStepGetPhysicalInteractionLength() implementation,.
void SetThresholdWarningEnergy(G4double newEnWarn)
Set fThreshold_Warning_Energy.
void SetThresholdImportantEnergy(G4double newEnImp)
Set fThreshold_Important_Energy.
virtual void ProcessDescription(std::ostream &outStream) const
G4LongLivedNeutralTransportation::ProcessDescription()
G4bool fGeometryLimitedStep
Flag to determine whether a boundary was reached.
G4ThreeVector fTransportEndMomentumDir
The particle's state after this Step, Store for DoIt.
void SetThresholdTrials(G4int newMaxTrials)
Set fThresholdTrials.
G4VParticleChange * AlongStepDoIt(const G4Track &track, const G4Step &stepData)
G4VProcess::AlongStepDoIt() implementation,.
void SetHighLooperThresholds()
Get/Set parameters for killing loopers: Above 'important' energy a 'looping' particle in field will N...
G4double fSumEnergyKilled
Sum of abandoned looping tracks energies.
G4ThreeVector fPreviousSftOrigin
Remember last safety origin.
static void SetSilenceLooperWarnings(G4bool val)
Do not warn about 'looping' particles.
G4double PostStepGetPhysicalInteractionLength(const G4Track &track, G4double previousStepSize, G4ForceCondition *pForceCond)
G4VProcess::PostStepGetPhysicalInteractionLength() implementation.
G4ThreeVector fTransportEndSpin
The particle's state after this Step, Store for DoIt.
G4Navigator * fLinearNavigator
Propagator used to transport the particle.
static G4bool fSilenceLooperWarnings
Flag to Supress all 'looper' warnings.
G4ThreeVector fTransportEndPosition
The particle's state after this Step, Store for DoIt.
G4bool fEndGlobalTimeComputed
The particle's state after this Step, Store for DoIt.
G4bool fShortStepOptimisation
Whether to avoid calling G4Navigator for short step ( < safety) If using it, the safety estimate for ...
G4int fNoLooperTrials
Counter for steps in which particle reports 'looping', if it is above 'Important' Energy.
G4bool fMomentumChanged
The particle's state after this Step, Store for DoIt.
G4LongLivedNeutralTransportation(G4int verbosityLevel=1)
Constructor.
void StartTracking(G4Track *aTrack)
Reset state for new (potentially resumed) track.
G4ParticleChangeForTransport fParticleChange
New ParticleChange.
void SetLowLooperThresholds()
Shortcut method - old values (meant for HEP)
G4bool DoesGlobalFieldExist()
Checks whether a field exists for the "global" field manager.
static G4bool GetSilenceLooperWarnings()
Do not throw exception about 'looping' particles.
G4PropagatorInField * fFieldPropagator
Propagator used to transport the particle.
G4double fTransportEndKineticEnergy
The particle's state after this Step, Store for DoIt.
Abstract base class for different kinds of events.