BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSIntegratorQuadrupole.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 "BDSDebug.hh"
19#include "BDSIntegratorQuadrupole.hh"
20#include "BDSMagnetStrength.hh"
21#include "BDSStep.hh"
22#include "BDSUtilities.hh"
23
24#include "G4Mag_EqRhs.hh"
25#include "G4MagIntegratorStepper.hh"
26#include "G4ThreeVector.hh"
27
28#include "CLHEP/Units/SystemOfUnits.h"
29
30#include <cmath>
31#include <limits>
32
34 G4double brho,
35 G4Mag_EqRhs* eqOfMIn,
36 G4double minimumRadiusOfCurvatureIn):
37 BDSIntegratorMag(eqOfMIn, 6),
38 minimumRadiusOfCurvature(minimumRadiusOfCurvatureIn)
39{
40 // B' = dBy/dx = Brho * (1/Brho dBy/dx) = Brho * k1
41 // we take |Brho| as it depends on charge and so does the eqOfM->FCof()
42 // so they'd both cancel out.
43 bPrime = std::abs(brho) * (*strength)["k1"] / CLHEP::m2;
44
45 zeroStrength = !BDS::IsFiniteStrength(bPrime);
46}
47
48void BDSIntegratorQuadrupole::Stepper(const G4double yIn[],
49 const G4double dydx[],
50 const G4double h,
51 G4double yOut[],
52 G4double yErr[])
53{
54 // in case of zero field or neutral particles do a linear step
55 const G4double fcof = eqOfM->FCof();
57 {
58 AdvanceDriftMag(yIn,h,yOut,yErr);
59 SetDistChord(0);
60 return;
61 }
62
63 G4ThreeVector mom = G4ThreeVector(yIn[3], yIn[4], yIn[5]);
64 G4double momMag = mom.mag();
65
66 // quad strength k normalised to charge and momentum of this particle
67 // note bPrime was calculated w.r.t. the nominal rigidity.
68 // eqOfM->FCof() gives us conversion to MeV,mm and rigidity in Tm correctly
69 // as well as charge of the given particle
70 G4double kappa = fcof*bPrime/momMag;
71
72 // neutral particle or no strength - advance as a drift.
73 if(std::abs(kappa) < 1e-20)
74 {
75 AdvanceDriftMag(yIn, h, yOut, yErr);
76 SetDistChord(0);
77 return;
78 }
79
80 G4ThreeVector pos = G4ThreeVector(yIn[0], yIn[1], yIn[2]);
81 G4ThreeVector momUnit = mom.unit();
82 BDSStep localPosMom = GlobalToCurvilinear(pos, momUnit, h, true);
83 G4ThreeVector localPos = localPosMom.PreStepPoint();
84 G4ThreeVector localMomUnit = localPosMom.PostStepPoint();
85
86 G4double xp = localMomUnit.x();
87 G4double yp = localMomUnit.y();
88 G4double zp = localMomUnit.z();
89
90 // only proceed with thick matrix if particle is paraxial
91 // judged by forward momentum > 1-limit and |transverse| < limit (default limit=0.1)
92 if (zp < (1.0-backupStepperMomLimit) || std::abs(xp) > backupStepperMomLimit || std::abs(yp) > backupStepperMomLimit)
93 {
94 backupStepper->Stepper(yIn, dydx, h, yOut, yErr);
95 SetDistChord(backupStepper->DistChord());
96 return;
97 }
98
99 G4double x0 = localPos.x();
100 G4double y0 = localPos.y();
101 G4double z0 = localPos.z();
102
103 // local r'' (for curvature)
104 G4ThreeVector localA;
105 localA.setX(-zp*x0); // can this be replaced by a cross product?
106 localA.setY( zp*y0); // G4ThreeVector has a cross method
107 localA.setZ( x0*xp - y0*yp);
108 localA *= kappa;
109 // determine effective curvature
110 G4double localAMag = localA.mag();
111 G4double radiusOfCurvature = std::numeric_limits<double>::max();
112 if (BDS::IsFiniteStrength(localAMag))
113 {radiusOfCurvature = 1./localAMag;} // avoid division by 0
114
115 // if we have a low energy particle that makes it into the paraxial cuts
116 // it could cause problems later in paraxial algorithm so use backup integrator
117 if (std::abs(radiusOfCurvature) < minimumRadiusOfCurvature)
118 {
119 backupStepper->Stepper(yIn, dydx, h, yOut, yErr);
120 SetDistChord(backupStepper->DistChord());
121 return;
122 }
123
124 G4double h2 = h*h;
125 // initialise output variables with input position as default
126 G4double x1 = x0;
127 G4double y1 = y0;
128 // z1 calculated below
129 G4double xp1 = xp;
130 G4double yp1 = yp;
131 G4double zp1 = zp;
132
133 // chord distance (simple quadratic approx)
134 G4double dc = h2/(8*radiusOfCurvature);
135 if (std::isnan(dc))
136 {SetDistChord(0);}
137 else
138 {SetDistChord(dc);}
139
140 G4double rootK = std::sqrt(std::abs(kappa*zp)); // direction independent
141 if (std::isnan(rootK))
142 {rootK = 0;}
143 G4double rootKh = rootK*h*zp;
144 G4double X11=0,X12=0,X21=0,X22=0;
145 G4double Y11=0,Y12=0,Y21=0,Y22=0;
146
147 if (kappa >= 0)
148 {//focussing
149 X11 = std::cos(rootKh);
150 X12 = std::sin(rootKh)/rootK;
151 X21 =-std::abs(kappa)*X12;
152 X22 = X11;
153
154 Y11 = std::cosh(rootKh);
155 Y12 = std::sinh(rootKh)/rootK;
156 Y21 = std::abs(kappa)*Y12;
157 Y22 = Y11;
158 }
159 else
160 {// defocussing
161 X11 = cosh(rootKh);
162 X12 = sinh(rootKh)/rootK;
163 X21 = std::abs(kappa)*X12;
164 X22 = X11;
165
166 Y11 = std::cos(rootKh);
167 Y12 = std::sin(rootKh)/rootK;
168 Y21 = -std::abs(kappa)*Y12;
169 Y22 = Y11;
170 }
171
172 x1 = X11*x0 + X12*xp;
173 xp1 = X21*x0 + X22*xp;
174
175 y1 = Y11*y0 + Y12*yp;
176 yp1 = Y21*y0 + Y22*yp;
177
178 // relies on normalised momenta otherwise this will be nan.
179 zp1 = std::sqrt(1 - xp1*xp1 - yp1*yp1);
180 if (std::isnan(zp1))
181 {zp1 = zp;} // ensure not nan
182
183 // new z position will be projection of h onto the z axis
184 G4double z1 = z0 + std::sqrt(std::abs(h2 - std::pow(x1-x0,2) - std::pow(y1-y0,2)));
185
186 localPos.setX(x1);
187 localPos.setY(y1);
188 localPos.setZ(z1);
189 localMomUnit.setX(xp1);
190 localMomUnit.setY(yp1);
191 localMomUnit.setZ(zp1);
192
193 // normalise from unit momentum to absolute momentum
194 G4ThreeVector localMomOut = localMomUnit * momMag;
195
196 // convert to global coordinates
197 BDSStep globalPosMom = CurvilinearToGlobal(localPos, localMomOut, true);
198 G4ThreeVector globalPosOut = globalPosMom.PreStepPoint();
199 G4ThreeVector globalMomOut = globalPosMom.PostStepPoint();
200
201 // error along direction of travel really
202 G4ThreeVector globalMomOutU = globalMomOut.unit();
203 globalMomOutU *= 1e-8;
204
205 // write out coordinates and errors to arrays
206 for (G4int i = 0; i < 3; i++)
207 {
208 yOut[i] = globalPosOut[i];
209 yOut[i+3] = globalMomOut[i];
210 yErr[i] = globalMomOutU[i]*1e-10; // empirically this has to be very small
211 yErr[i+3] = 1e-40;
212 }
213}
214
215
BDSStep GlobalToCurvilinear(const G4double fieldArcLength, const G4ThreeVector &unitField, const G4double angle, const G4ThreeVector &position, const G4ThreeVector &unitMomentum, const G4double h, const G4bool useCurvilinearWorld, const G4double FCof, const G4double tilt=0)
void AdvanceDriftMag(const G4double yIn[], const G4double h, G4double yOut[], G4double yErr[]) const
Error array [6] all 0.
Common functionality to BDSIM integrators.
G4Mag_EqRhs * eqOfM
void SetDistChord(G4double distChordIn)
Setter for distChord to private member.
G4MagIntegratorStepper * backupStepper
virtual void Stepper(const G4double y[], const G4double dydx[], const G4double h, G4double yOut[], G4double yErr[])
BDSIntegratorQuadrupole()
Private default constructor to enforce use of supplied constructor.
G4double bPrime
B Field Gradient.
Efficient storage of magnet strengths.
A simple class to represent the positions of a step.
Definition BDSStep.hh:32
G4ThreeVector PostStepPoint() const
Accessor.
Definition BDSStep.hh:42
G4ThreeVector PreStepPoint() const
Accessor.
Definition BDSStep.hh:41
G4bool IsFiniteStrength(G4double variable)