BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSFieldMagSolenoidSheet.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 "BDSDebug.hh"
20#include "BDSFieldMagSolenoidSheet.hh"
21#include "BDSMagnetStrength.hh"
22#include "BDSUtilities.hh"
23
24#include "G4ThreeVector.hh"
25#include "G4Types.hh"
26
27#include "CLHEP/Units/PhysicalConstants.h"
28#include "CLHEP/Units/SystemOfUnits.h"
29
30#include <algorithm>
31#include <cmath>
32
33BDSFieldMagSolenoidSheet::BDSFieldMagSolenoidSheet(BDSMagnetStrength const* strength,
34 G4double radiusIn):
35 BDSFieldMagSolenoidSheet((*strength)["length"], radiusIn, (*strength)["field"])
36{;}
37
38BDSFieldMagSolenoidSheet::BDSFieldMagSolenoidSheet(G4double fullLength,
39 G4double sheetRadius,
40 G4double B0In):
41 a(sheetRadius),
42 halfLength(0.5*fullLength),
43 B0(B0In),
44 spatialLimit(std::min(1e-5*sheetRadius, 1e-5*fullLength)),
45 normalisation(1.0)
46{
48
49 // The field inside the current cylinder is actually slightly parabolic in rho.
50 // The equation for the field takes B0 as the peak magnetic field at the current
51 // cylinder sheet. So we evaluate it here then normalise. ~<1% adjustment in magnitude.
52 G4double testBz = OnAxisBz(halfLength, -halfLength);
53 normalisation = B0 / testBz;
54}
55
56G4ThreeVector BDSFieldMagSolenoidSheet::GetField(const G4ThreeVector& position,
57 const G4double /*t*/) const
58{
59 G4double z = position.z();
60 G4double rho = position.perp();
61 G4double phi = position.phi(); // angle about z axis
62
63 // check if close to current source - function not well-behaved at exactly the rho of
64 // the current source or at the boundary of +- halfLength
65 if (std::abs(std::abs(z) - halfLength) < spatialLimit && (rho < a+2*spatialLimit))
66 {return G4ThreeVector();} // close to +-z edge of cylinder and inside the radius
67 else if (std::abs(rho - a) < spatialLimit && (std::abs(z) < halfLength+2*spatialLimit))
68 {return G4ThreeVector();} // close to radius and inside +- z
69
70 G4double zp = z + halfLength;
71 G4double zm = z - halfLength;
72
73 G4double Brho = 0;
74 G4double Bz = 0;
75
76 // approximation for on-axis
77 if (std::abs(rho) < spatialLimit)
78 {Bz = OnAxisBz(zp, zm);}
79 else
80 {
81 G4double zpSq = zp*zp;
82 G4double zmSq = zm*zm;
83
84 G4double rhoPlusA = rho + a;
85 G4double rhoPlusASq = rhoPlusA * rhoPlusA;
86 G4double aMinusRho = a - rho;
87 G4double aMinusRhoSq = aMinusRho*aMinusRho;
88
89 G4double denominatorP = std::sqrt(zpSq + rhoPlusASq);
90 G4double denominatorM = std::sqrt(zmSq + rhoPlusASq);
91
92 G4double alphap = a / denominatorP;
93 G4double alpham = a / denominatorM;
94
95 G4double betap = zp / denominatorP;
96 G4double betam = zm / denominatorM;
97
98 G4double gamma = (a - rho) / (rhoPlusA);
99 G4double gammaSq = gamma * gamma;
100
101 G4double kp = std::sqrt(zpSq + aMinusRhoSq) / denominatorP;
102 G4double km = std::sqrt(zmSq + aMinusRhoSq) / denominatorM;
103
104 Brho = B0 * (alphap * CEL(kp, 1, 1, -1) - alpham * CEL(km, 1, 1, -1)) / CLHEP::pi;
105 Bz = ((B0 * a) / (rhoPlusA)) * (betap * CEL(kp, gammaSq, 1, gamma) - betam * CEL(km, gammaSq, 1, gamma)) / CLHEP::pi;
106 // technically possible for integral to return nan, so protect against it and default to B0 along z
107 if (std::isnan(Brho))
108 {Brho = 0;}
109 if (std::isnan(Bz))
110 {Bz = B0;}
111 }
112 // we have to be consistent with the phi we calculated at the beginning,
113 // so unit rho is in the x direction.
114 G4ThreeVector result = G4ThreeVector(Brho,0,Bz) * normalisation;
115 result = result.rotateZ(phi);
116 return result;
117}
118
119G4double BDSFieldMagSolenoidSheet::CEL(G4double kc,
120 G4double p,
121 G4double c,
122 G4double s,
123 G4int nIterationLimit)
124{
125 if (!BDS::IsFinite(kc))
126 {return NAN;}
127
128 G4double errtol = 0.000001;
129 G4double k = std::abs(kc);
130 G4double pp = p;
131 G4double cc = c;
132 G4double ss = s;
133 G4double em = 1.0;
134 if (p > 0)
135 {
136 pp = std::sqrt(p);
137 ss = s/pp;
138 }
139 else
140 {
141 G4double f = kc * kc;
142 G4double q = 1.0 - f;
143 G4double g = 1.0 - pp;
144 f = f - pp;
145 q = q * (ss - c * pp);
146 pp = std::sqrt(f / g);
147 cc = (c - ss) / g;
148 ss = -q / (g * g * pp) + cc * pp;
149 }
150
151 G4double f = cc;
152 cc = cc + ss/pp;
153 G4double g = k/pp;
154 ss = 2*(ss + f*g);
155 pp = g + pp;
156 g = em;
157 em = k + em;
158 G4double kk = k;
159 G4int nLoop = 0;
160 while ( (std::abs(g-k) > g*errtol) && nLoop < nIterationLimit)
161 {
162 k = 2*std::sqrt(kk);
163 kk = k*em;
164 f = cc;
165 cc = cc + ss/pp;
166 g = kk / pp;
167 ss = 2*(ss + f*g);
168 pp = g + pp;
169 g = em;
170 em = k + em;
171 nLoop++;
172 }
173 G4double result = CLHEP::halfpi*(ss + cc*em)/( em*(em + pp) );
174 return result;
175}
176
178 G4double zm) const
179{
180 G4double f1 = zp / std::sqrt( zp*zp + a*a );
181 G4double f2 = zm / std::sqrt( zm*zm + a*a );
182 G4double Bz = 0.5*B0 * (f1 - f2);
183 return Bz;
184}
Class that provides the magnetic field due to a cylinder of current.
G4double OnAxisBz(G4double zp, G4double zm) const
static G4double CEL(G4double kc, G4double p, G4double c, G4double s, G4int nIterationLimit=1000)
Generalised Complete Elliptical Integral.
virtual G4ThreeVector GetField(const G4ThreeVector &position, const G4double t=0) const
Calculate the field value.
G4bool finiteStrength
Flag to cache whether finite nor not.
Definition: BDSFieldMag.hh:83
Efficient storage of magnet strengths.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())