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