ATLAS Offline Software
G4mplEquationSetup.cxx
Go to the documentation of this file.
1 //
2 // ********************************************************************
3 // * License and Disclaimer *
4 // * *
5 // * The Geant4 software is copyright of the Copyright Holders of *
6 // * the Geant4 Collaboration. It is provided under the terms and *
7 // * conditions of the Geant4 Software License, included in the file *
8 // * LICENSE and available at http://cern.ch/geant4/license . These *
9 // * include a list of copyright holders. *
10 // * *
11 // * Neither the authors of this software system, nor their employing *
12 // * institutes,nor the agencies providing financial support for this *
13 // * work make any representation or warranty, express or implied, *
14 // * regarding this software system or assume any liability for its *
15 // * use. Please see the license in the file LICENSE and URL above *
16 // * for the full disclaimer and the limitation of liability. *
17 // * *
18 // * This code implementation is the result of the scientific and *
19 // * technical work of the GEANT4 collaboration. *
20 // * By using, copying, modifying or distributing the software (or *
21 // * any work based on the software) you agree to acknowledge its *
22 // * use in resulting scientific publications, and indicate your *
23 // * acceptance of all terms of the Geant4 Software license. *
24 // ********************************************************************
25 //
28 //
29 //
30 //
31 // G4mplEquationSetup is responsible for switching between two different
32 // equation of motions, one for monopoles and another for the rest of the
33 // particles.
34 //
35 // This updated version is designed to cope with:
36 // - multiple G4FieldManager objects being defined in the setup
37 // - many G4MagneticField objects (i.e a global and one or more local fiedls)
38 // - a G4FieldManager which switches the G4Field object to which it points
39 // during the simulation (e.g. to null)
40 
41 // =======================================================================
42 // Modified: 19 May 2019, M. Bandieramonte: introduced MT mode. The class
43 // was a Singleton and it was not thread-safe.
44 // Added the #ifdef G4MULTITHREADED directive to handle
45 // the multithreaded case. One instance of the class will be created
46 // per each thread and stored in a tbb::concurrent_unordered_map that
47 // is hashed with the threadID number.
48 // Modified: 28 August 2013, W. Taylor: adapted for ATLAS
49 // Created: 23 May 2013, J. Apostolakis
50 // Adapted from G4MonopoleFieldSetup by B. Bozsogi
51 // =======================================================================
52 
53 // class header
54 #include "G4mplEquationSetup.hh"
55 // package headers
56 #include "G4mplEqMagElectricField.hh"
57 // Geant4 headers
58 #include "G4MagneticField.hh"
59 #include "G4UniformMagField.hh"
60 #include "G4FieldManager.hh"
61 #include "G4TransportationManager.hh"
62 #include "G4Mag_UsualEqRhs.hh"
63 #include "G4MagIntegratorStepper.hh"
64 #include "G4ChordFinder.hh"
65 #include "G4MagIntegratorDriver.hh"
66 #include "G4AtlasRK4.hh" // Used for non-magnetic particles
67 #include "G4ClassicalRK4.hh" // Used for monopoles
68 // NOTE:
69 // Only Steppers which can integrate over any number of variables can be used
70 // to integrate the equation of motion of a monopole.
71 // The following cannot be used:
72 // Helix... - these integrate over 6 variables only; also assume helix as 'base'
73 // AtlasRK4, NystromRK4 - these have the equation of motion embedded inside
74 #include "G4SystemOfUnits.hh"
75 #include "G4UniformMagField.hh"
76 
77 #ifdef G4MULTITHREADED
78 G4mplEquationSetup::ESThreadMap_t G4mplEquationSetup::m_ESThreadMap;
79 #endif
80 
81 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
82 
83 G4mplEquationSetup::G4mplEquationSetup():
84  fMinStep(0.01*CLHEP::mm), // minimal step of 1 mm is default
85  fVerbose(false)
86 {
87  if( fVerbose )
88  G4cout << "!!! G4mplEquationSetup constructor" << G4endl;
89 }
90 
91 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
92 
93 G4mplEquationSetup* G4mplEquationSetup::GetInstance()
94 {
95 #ifdef G4MULTITHREADED
96  auto es = getES();
97  if (!es) //nullpointer if it is not found
98  return setES();
99  else return es;
100 #else
101  //Standard implementation of a Singleton Pattern
102  static G4mplEquationSetup instance;
103  return &instance;
104 #endif
105 
106 }
107 
108 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
109 
110 G4mplEquationSetup::~G4mplEquationSetup()
111 {
112  if( fVerbose )
113  G4cout << "!!! G4mplEquationSetup destructor" << G4endl;
114 
115  delete fMonopoleEquation;
116  delete fMonopoleStepper;
117 
118  delete fMonopoleChordFinder;
119  // JA: Is protection below still needed ?
120  // WJT: Avoid segmentation violation in G4FieldManager destructor
121 }
122 
123 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
124 
125 #ifdef G4MULTITHREADED
126  G4mplEquationSetup* G4mplEquationSetup::getES()
127  {
128  // Get current thread-ID
129  const auto tid = std::this_thread::get_id();
130  auto esPair = m_ESThreadMap.find(tid);
131  if(esPair == m_ESThreadMap.end())
132  return nullptr; //if not found return null pointer
133  else return esPair->second;
134  }
135 
136 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
137 
138  G4mplEquationSetup* G4mplEquationSetup::setES()
139  {
140  G4mplEquationSetup* instance = new G4mplEquationSetup;
141  const auto tid = std::this_thread::get_id();
142  auto inserted = m_ESThreadMap.insert( std::make_pair(tid, instance)).first;
143  return (G4mplEquationSetup*) inserted->second;
144  }
145 #endif
146 
147 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
148 
149 // The ownership model of the current implementation is the following:
150 // An instance of G4mplEquationSetup owns
151 // - the equation fMonopoleEquation
152 // - the stepper fMonopoleStepper
153 // - the chord-finder fChordFinder
154 // They are transiently associated with the current FieldManager during the
155 // 'stepping' loop of the one monopole track.
156 
157 // InitialiseForField must be called either
158 // - at the start of the simulation (when the global field manager has been assigned, or else
159 // - at the start of tracking of the first monopole track (and never later than this!)
160 
161 void
162 G4mplEquationSetup::InitialiseForField(G4FieldManager* fieldManager )
163 {
164  if (fieldManager == nullptr) {
165  return;
166  }
167 
168  // Store the 'original ChordFinder of the volume - to enable us to restore it,
169  // when either we
170  // i. enter a volume with a different field manager or
171  // ii. have finished tracking the current track. Fix: J.A. 27/10/2022
172  fOriginalChordFinder = fieldManager->GetChordFinder();
173  fCurrentFieldManager = fieldManager;
174  // How to ensure that two/multiple calls do not overwrite it?
175  // Consider whether it can be null
176 
177  const G4MagneticField* magField = dynamic_cast<const G4MagneticField*>(fieldManager->GetDetectorField());
178  G4MagneticField* magFieldNC ATLAS_THREAD_SAFE = const_cast<G4MagneticField*>(magField);
179 
180  CreateStepperToChordFinder(magFieldNC);
181 }
182 
183 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
184 
185 void
186 G4mplEquationSetup::CreateStepperToChordFinder(G4MagneticField* magFieldNC)
187 {
188  if(magFieldNC == nullptr)
189  {
190  static G4ThreadLocal G4UniformMagField nullField( G4ThreeVector(0.0, 0.0, 0.0));
191  magFieldNC= &nullField;
192  }
193 
194  delete fMonopoleEquation;
195  fMonopoleEquation = new G4mplEqMagElectricField(magFieldNC);
196 
197  delete fMonopoleStepper;
198  fMonopoleStepper = new G4ClassicalRK4( fMonopoleEquation, 8 ); // for time information..
199 
200  if ( !fMonopoleStepper )
201  {
202  G4ExceptionDescription ermsg;
203  ermsg << "The creation of a stepper for Monopoles failed - trying to use G4ClassicalRK4";
204  G4Exception("G4mplEquationSetup::InitialiseForField",
205  "FailureToCreateObject", FatalException, ermsg);
206  }
207 
208  delete fMonopoleChordFinder;
209 
210  auto integrDriver = new G4MagInt_Driver( fMinStep, fMonopoleStepper,
211  // cppcheck-suppress nullPointerRedundantCheck; false positive
212  fMonopoleStepper->GetNumberOfVariables() );
213  fMonopoleChordFinder = new G4ChordFinder( integrDriver );
214 }
215 
216 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
217 
218 // This method must be called before
219 
220 void G4mplEquationSetup::SwitchStepperAndChordFinder(G4bool useMonopoleEq,
221  G4FieldManager* fieldManager)
222 {
223  // For the existing version to work, InitialiseForField must be called
224  // - for *this* field manager (not another) before this call
225  // - potentially after a change of 'field' object in this field manager (tbc)
226  const G4MagneticField* magField = dynamic_cast<const G4MagneticField*>(fieldManager->GetDetectorField());
227  G4MagneticField* magFieldNC ATLAS_THREAD_SAFE = const_cast<G4MagneticField*>(magField); // fieldManager only allow const access despite being non-const
228 
229  // New code is needed to cope with the following cases:
230  // - a different field manager is encountered (i.e. a local one or second local one)
231  // - the 'field' object to which the G4FieldManager is pointing was changed
232 
233  if ( magFieldNC )
234  {
235  // First check whether the field in equation of motion has changed!
236  CheckAndUpdateField( magFieldNC );
237 
238  if (useMonopoleEq) {
239  fieldManager->SetChordFinder( fMonopoleChordFinder );
240  } else {
241  fieldManager->SetChordFinder( fOriginalChordFinder );
242  }
243  }
244 }
245 
246 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
247 
248 void G4mplEquationSetup::ResetIntegration(G4FieldManager* fieldManager)
249 // Restore the original ChordFinder to either
250 // - allow non-monopole tracks to work seamlessly
251 // - prepare for move of monopole to volume/region with different fieldManager
252 {
253  if( fieldManager != fCurrentFieldManager){
254  G4ExceptionDescription ermsg;
255  ermsg << "This method expects to be called with the chordfinder it knows"
256  << " yet its is called with " << fieldManager
257  << " and expected " << fCurrentFieldManager << G4endl;
258  G4Exception("G4mplEquationSetup::ResetIntegration",
259  "InvalidState", FatalException, ermsg);
260  }
261 
262  if( fCurrentFieldManager ) {
263  fCurrentFieldManager->SetChordFinder(fOriginalChordFinder);
264 
265  if ( fVerbose )
266  G4cout << " G4mplEquationSetup: Reset Chord-Finder to original one (for pure electric charge): "
267  << fOriginalChordFinder << G4endl;
268 
269  fOriginalChordFinder = nullptr; // Now Forget it!
270  fCurrentFieldManager = nullptr;
271  }
272  // Since these objects referred to the outgoing field manager, clean them up!
273  delete fMonopoleEquation; fMonopoleEquation= nullptr;
274  delete fMonopoleStepper; fMonopoleStepper= nullptr;
275  delete fMonopoleChordFinder; fMonopoleChordFinder= nullptr;
276 }
277 
278 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
279 
280 void G4mplEquationSetup::CheckAndUpdateField( G4MagneticField* magFieldNC )
281 // Ensure that the field of this fieldManager is the one in the equation - else update it to be so.
282 {
283  if( magFieldNC != fMonopoleEquation->GetFieldObj() )
284  {
285  // fMonopoleEquation->SetFieldObj( magFieldNC ); // Dangerous method -- but types are both G4MagneticField
286  // A cleaner version will call again Initialise ...
287  CreateStepperToChordFinder(magFieldNC);
288  }
289 }
290 
instance
std::map< std::string, double > instance
Definition: Run_To_Get_Tags.h:8
CLHEP
STD'S.
Definition: IAtRndmGenSvc.h:19
python.SystemOfUnits.mm
int mm
Definition: SystemOfUnits.py:83
ATLAS_THREAD_SAFE
#define ATLAS_THREAD_SAFE
Definition: checker_macros.h:211