BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSIntegratorDipoleRodrigues2.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) BDSIM Collaboration, 2001 - 2026.
3
4This file is part of BDSIM.
5
6BDSIM is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published
8by the Free Software Foundation version 3 of the License.
9
10BDSIM is distributed in the hope that it will be useful, but
11WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
17*/
18#include "BDSIntegratorDipoleRodrigues2.hh"
19#include "BDSMagnetStrength.hh"
20#include "BDSUtilities.hh"
21
22#include "globals.hh"
23#include "G4ClassicalRK4.hh"
24#include "G4Mag_EqRhs.hh"
25#include "G4ThreeVector.hh"
26
27#include "CLHEP/Units/SystemOfUnits.h"
28
29#include <cmath>
30
32 G4double minimumRadiusOfCurvatureIn):
33 G4MagHelicalStepper(eqOfMIn),
34 eqOfM(eqOfMIn),
35 minimumRadiusOfCurvature(minimumRadiusOfCurvatureIn)
36{;}
37
39 G4ThreeVector field,
40 G4double stepLength,
41 G4double yOut[6])
42{
43 AdvanceHelix(yIn, field, stepLength, yOut);
44}
45
47 const G4double& h,
48 G4double yOut[6])
49{
50 G4double bO[6]; // original location field value - must be 6 long
51 eqOfM->GetFieldValue(yIn, bO);
52 G4ThreeVector bOriginal = G4ThreeVector(bO[0],bO[1],bO[2]);
53
54 // Do a full step using G4MagHelicalStepper
55 AdvanceHelix(yIn, bOriginal, h, yOut);
56}
57
58void BDSIntegratorDipoleRodrigues2::Stepper(const G4double yIn[6],
59 const G4double[] /*dydx*/,
60 G4double h,
61 G4double yOut[6],
62 G4double yErr[6])
63{
64 // overwrite any previous rad and helix to avoid contaminating this step
66
67 // Protect against very small steps or neutral particles drift through.
68 if (h < 1e-12 || !BDS::IsFiniteStrength(eqOfM->FCof()))
69 {
70 AdvanceDriftMag(yIn,h,yOut,yErr);
71 FudgeDistChordToZero(); // see doxygen in header for explanation
72 return;
73 }
74
75 // Arrays for field querying (g4 interface)
76 G4double bO[6]; // original location field value - must be 6 long
77 eqOfM->GetFieldValue(yIn, bO);
78 G4ThreeVector bOriginal = G4ThreeVector(bO[0],bO[1],bO[2]);
79
80 // protect against zero field as G4's Advance helix gives wrong ang and rad
81 if (!BDS::IsFiniteStrength(bOriginal.mag()))
82 {
83 AdvanceDriftMag(yIn, h, yOut, yErr);
85 return;
86 }
87
88 // Do a full step - the result we use
89 AdvanceHelix(yIn, bOriginal, h, yOut);
90 // cache some calculated parameters as there's no way to cache the distchord
91 // directly as the G4MagHelicalStepper class doesn't make DistChord() virtual.
92 G4double ang = GetAngCurve();
93 G4double rad = GetRadHelix();
94 // the G4MagHelicalStepper can sometimes produce nans for rad helix! These
95 // go into distchord and then ruin the tracking across events.
96 if (std::isnan(rad))
97 {
98 rad = h / ang;
99 SetRadHelix(rad);
100 }
101
102 // Now we have the radius of curvature to check on.
103 // If it's smaller than limit, we artificially advance the particle
104 // along its helix axis (parallel to the field) so it'll hit something
105 // and finish in a timely manner.
106 const G4double radiusOfCurvature = rad;
107 if (std::abs(radiusOfCurvature) < minimumRadiusOfCurvature)
108 {
109 AdvanceHelixForSpiralling(yIn, bOriginal, h, yOut, yErr);
110 // Update parameters that distchord will be calculated from full step info.
111 SetAngCurve(ang);
112 SetRadHelix(rad);
113 return;
114 }
115
116 // error estimation
117 // if it's a very small step - no error - safer as differences between large positions
118 // for very small steps can have numerical precision issues.
119 if (h < 1e-9) // 1e-9 is 1pm (in g4 units) - the minimum length scale of bdsim
120 {
121 // set error to be low - required
122 for (G4int i = 0; i < 3; i++)
123 {
124 yErr[i] = 1e-20;
125 yErr[i+3] = 1e-40;
126 }
127 SetAngCurve(ang);
128 SetRadHelix(rad); // must update these for distchord
129 return;
130 }
131
132 // normal error estimation - do two half steps and compare difference to
133 // the result from one full step
134 G4double yTemp[7];
135 AdvanceHelix(yIn, bOriginal, h*0.5, yTemp); // first step
136
137 // resample field at midway point (although if pure dipole, this is
138 // unnecessary) - could go outside the range of the field though
139 G4double bM[6]; // mid point location field value - must be 6 long
140 GetEquationOfMotion()->GetFieldValue(yTemp, bM);
141 G4ThreeVector bMid = G4ThreeVector(bM[0],bM[1],bM[2]);
142
143 G4double yTemp2[7];
144 AdvanceHelix(yTemp, bMid, h*0.5, yTemp2); // second step
145
146 // Error estimation
147 for (G4int i = 0; i < 6; i++)
148 {yErr[i] = std::abs(yOut[i] - yTemp2[i]);}
149
150 // Update parameters that distchord will be calcualted from from full step info.
151 SetAngCurve(ang);
152 SetRadHelix(rad);
153}
154
156 const G4ThreeVector& field,
157 const G4double& h,
158 G4double yOut[6],
159 G4double yErr[6])
160{
161 G4ThreeVector fieldUnit = field.unit();
162
163 // protect against zero field as this algorithm would be wrong
164 if (!BDS::IsFinite(fieldUnit.mag()))
165 {
166 AdvanceDriftMag(yIn, h, yOut, yErr);
168 return;
169 }
170
171 G4double momMag = G4ThreeVector(yIn[3], yIn[4], yIn[5]).mag();
172
173 // Artificially change momentum to be along field direction.
174 G4ThreeVector momNew = fieldUnit*momMag;
175
176 // Create updated array for tracking.
177 G4double yModified[7];
178 for (G4int i = 0; i < 3; i++)
179 {
180 yModified[i] = yIn[i]; // position stays the same
181 yModified[i + 3] = momNew[i]; // update momentum
182 }
183
184 // Simply advance as a drift along this vector.
185 AdvanceDriftMag(yModified, h, yOut, yErr);
186
187 // set error to be low - required
188 for (G4int i = 0; i < 3; i++)
189 {
190 yErr[i] = 1e-20;
191 yErr[i+3] = 1e-40;
192 }
193}
194
196{
197 // Large angle condition in G4MagHelicalStepper DistChord() method is faster, so set angle to 10.
198 SetAngCurve(10);
199 SetRadHelix(0);
200}
void AdvanceHelixForSpiralling(const G4double yIn[6], const G4ThreeVector &field, const G4double &h, G4double yOut[6], G4double yErr[6])
G4Mag_EqRhs * eqOfM
Cache of equation of motion. This class does not own it.
void SingleStep(const G4double yIn[6], const G4double &h, G4double yOut[6])
virtual void DumbStepper(const G4double yIn[6], G4ThreeVector field, G4double stepLength, G4double yOut[6])
virtual void Stepper(const G4double yIn[6], const G4double dydx[], G4double h, G4double yOut[6], G4double yErr[6])
Output error array.
BDSIntegratorDipoleRodrigues2()=delete
Private default constructor to force use of provided one.
void AdvanceDriftMag(const G4double yIn[], const G4double h, G4double yOut[], G4double yErr[]) const
Error array [6] all 0.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
G4bool IsFiniteStrength(G4double variable)