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 - 2024.
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 "BDSSpecialFunctions.hh"
23#include "BDSUtilities.hh"
24
25#include "G4ThreeVector.hh"
26#include "G4Types.hh"
27
28#include "CLHEP/Units/PhysicalConstants.h"
29#include "CLHEP/Units/SystemOfUnits.h"
30
31#include <algorithm>
32#include <cmath>
33
34BDSFieldMagSolenoidSheet::BDSFieldMagSolenoidSheet(BDSMagnetStrength const* strength,
35 G4double radiusIn, G4double toleranceIn):
36 BDSFieldMagSolenoidSheet((*strength)["field"], false, radiusIn, (*strength)["length"], toleranceIn)
37{;}
38
39BDSFieldMagSolenoidSheet::BDSFieldMagSolenoidSheet(G4double strength,
40 G4bool strengthIsCurrent,
41 G4double sheetRadius,
42 G4double fullLength,
43 G4double toleranceIn):
44 a(sheetRadius),
45 halfLength(0.5*fullLength),
46 B0(0.0),
47 I(0.0),
48 spatialLimit(std::min(1e-5*sheetRadius, 1e-5*fullLength)),
49 normalisation(1.0) ,
50 coilTolerance(toleranceIn)
51{
52 finiteStrength = BDS::IsFinite(std::abs(strength));
53 // apply relationship B0 = mu_0 I / 2 a for on-axis rho=0,z=0
54 if (strengthIsCurrent)
55 {
56 I = strength;
57 B0 = CLHEP::mu0 * strength / (CLHEP::pi*2* halfLength);
58 }
59 else
60 {// strength is B0 -> calculate current
61 B0 = strength;
62 I = B0 *(CLHEP::pi*2* halfLength) / CLHEP::mu0;
63 }
64
65 // The field inside the current cylinder is actually slightly parabolic in rho.
66 // The equation for the field takes B0 as the peak magnetic field at the current
67 // cylinder sheet. So we evaluate it here then normalise. ~<1% adjustment in magnitude.
68 //G4double testBz = OnAxisBz(halfLength, -halfLength);
69 //normalisation = B0 / testBz;
70}
71
72G4ThreeVector BDSFieldMagSolenoidSheet::GetField(const G4ThreeVector& position,
73 const G4double /*t*/) const
74{
75 G4double z = position.z();
76 G4double rho = position.perp();
77 G4double phi = position.phi(); // angle about z axis
78
79 // check if close to current source - function not well-behaved at exactly the rho of
80 // the current source or at the boundary of +- halfLength
81 if (std::abs(rho - a) < spatialLimit && (std::abs(z) < halfLength+2*spatialLimit))
82 {return G4ThreeVector();} // close to radius and inside +- z
83 G4double zp = z + halfLength;
84 G4double zm = z - halfLength;
85
86 G4double Brho = 0;
87 G4double Bz = 0;
88
89 // approximation for on-axis
90 if (std::abs(OnAxisBz(zp, zm))< coilTolerance)
91 {
92 Brho = 0.0;
93 Bz = 0.0;
94 }
95 else if (std::abs(rho) < spatialLimit)
96 {Bz = OnAxisBz(zp, zm);}
97 else
98 {
99 G4double zpSq = zp*zp;
100 G4double zmSq = zm*zm;
101
102 G4double rhoPlusA = rho + a;
103 G4double rhoPlusASq = rhoPlusA * rhoPlusA;
104 G4double aMinusRho = a - rho;
105 G4double aMinusRhoSq = aMinusRho*aMinusRho;
106
107 G4double denominatorP = std::sqrt(zpSq + rhoPlusASq);
108 G4double denominatorM = std::sqrt(zmSq + rhoPlusASq);
109
110 G4double alphap = a / denominatorP;
111 G4double alpham = a / denominatorM;
112
113 G4double betap = zp / denominatorP;
114 G4double betam = zm / denominatorM;
115
116 G4double gamma = (a - rho) / (rhoPlusA);
117 G4double gammaSq = gamma * gamma;
118
119 G4double kp = std::sqrt(zpSq + aMinusRhoSq) / denominatorP;
120 G4double km = std::sqrt(zmSq + aMinusRhoSq) / denominatorM;
121
122 Brho = B0 * (alphap * BDS::CEL(kp, 1, 1, -1) - alpham * BDS::CEL(km, 1, 1, -1));
123 Bz = ((B0 * a) / (rhoPlusA)) * (betap * BDS::CEL(kp, gammaSq, 1, gamma) - betam * BDS::CEL(km, gammaSq, 1, gamma));
124 // technically possible for integral to return nan, so protect against it and default to B0 along z
125 if (std::isnan(Brho))
126 {Brho = 0;}
127 if (std::isnan(Bz))
128 {Bz = B0;}
129 }
130 // we have to be consistent with the phi we calculated at the beginning,
131 // so unit rho is in the x direction.
132 G4ThreeVector result = G4ThreeVector(Brho,0,Bz)* normalisation;
133 result = result.rotateZ(phi);
134 return result;
135}
136
138 G4double zm) const
139{
140 G4double f1 = zp / std::sqrt( zp*zp + a*a );
141 G4double f2 = zm / std::sqrt( zm*zm + a*a );
142 G4double Bz = 0.5*B0 *CLHEP::pi* (f1 - f2);
143 return Bz;
144}
Class that provides the magnetic field due to a cylinder of current.
G4double OnAxisBz(G4double zp, G4double zm) const
virtual G4ThreeVector GetField(const G4ThreeVector &position, const G4double t=0) const
Calculate the field value.
G4bool finiteStrength
Flag to cache whether finite nor not.
Efficient storage of magnet strengths.
G4double CEL(G4double kc, G4double p, G4double c, G4double s, G4int nIterationLimit=1000)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
STL namespace.