gfields
Loading...
Searching...
No Matches
gfield_multipoles.cc
Go to the documentation of this file.
1// CLHEP units
2#include "CLHEP/Units/PhysicalConstants.h"
3
4// geant4 headers
5#include "G4ThreeVector.hh"
6
7// gfields
8#include "gfield_multipoles.h"
9#include "gfieldConventions.h"
10
11// gemv
12// #include "gutilities.h"
13
14
15// tells the DLL how to create a GFieldFactory in each plugin .so/.dylib
16extern "C" GField* GFieldFactory(const std::shared_ptr<GOptions>& g) { return static_cast<GField*>(new GField_MultipolesFactory(g)); }
17
18
19// for now this implementation follows gemc
20// references of this implementation:
21// - https://cds.cern.ch/record/1333874/files/1.pdf
22// - https://uspas.fnal.gov/materials/12MSU/magnet_elements.pdf
23// - https://cas.web.cern.ch/sites/default/files/lectures/bruges-2009/wolski-1.pdf
24// notice strength is defined at a reference radius of 1m
25void GField_MultipolesFactory::GetFieldValue(const double pos[3], G4double* bfield) const {
26 // ======= Configuration / conventions =======
27 // strength: Tesla at reference radius r0 for all multipole orders
28 const double r0 = CLHEP::m; // reference radius; make this a member if you want
29
30 // ======= Basic checks =======
31 if (pole_number < 2 || (pole_number % 2) != 0) {
32 log->error(ERR_WRONG_POLE_NUMBER,
33 "Pole number must be an even integer >= 2 (2=dipole,4=quadrupole,...)");
34 }
35
36 // ======= Positions and local frame =======
37 const G4ThreeVector x0(pos[0], pos[1], pos[2]); // query point (lab)
38 const G4ThreeVector x1(origin[0], origin[1], origin[2]); // magnet origin (lab)
39
40 // shift to magnet-centered coordinates and "unroll" the magnet by -rotation_angle
41 G4ThreeVector p = x0 - x1;
42 if (rotaxis == 0) p.rotateX(-rotation_angle);
43 else if (rotaxis == 1) p.rotateY(-rotation_angle);
44 else if (rotaxis == 2) p.rotateZ(-rotation_angle);
45
46 // Identify transverse plane (u,v) ⟂ to the axis, and the axial coordinate w (unused here)
47 double u = 0.0, v = 0.0;
48 switch (rotaxis) {
49 case 0: u = p.y();
50 v = p.z();
51 break; // axis = X ⇒ transverse plane = (Y,Z)
52 case 1: u = p.z();
53 v = p.x();
54 break; // axis = Y ⇒ transverse plane = (Z,X)
55 case 2: u = p.x();
56 v = p.y();
57 break; // axis = Z ⇒ transverse plane = (X,Y)
58 default: break;
59 }
60
61 const double r = std::hypot(u, v); // transverse radius
62 const double phi = std::atan2(v, u); // azimuth in transverse plane
63
64 // ======= Axial (solenoid-like) mode if explicitly requested =======
65 if (longitudinal) {
66 // Uniform axial field aligned with rotaxis; not a multipole.
67 G4ThreeVector B_local(0, 0, 0);
68 switch (rotaxis) {
69 case 0: B_local.setX(strength);
70 break;
71 case 1: B_local.setY(strength);
72 break;
73 case 2: B_local.setZ(strength);
74 break;
75 default: break;
76 }
77 // Roll back to lab
78 G4ThreeVector B_lab = B_local;
79 if (rotaxis == 0) B_lab.rotateX(+rotation_angle);
80 else if (rotaxis == 1) B_lab.rotateY(+rotation_angle);
81 else if (rotaxis == 2) B_lab.rotateZ(+rotation_angle);
82
83 bfield[0] = B_lab.x();
84 bfield[1] = B_lab.y();
85 bfield[2] = B_lab.z();
86
87 log->info(2, "Axial field mode (solenoid-like). Strength: ", strength,
88 " T, Field: (", bfield[0], ", ", bfield[1], ", ", bfield[2], ")");
89 return;
90 }
91
92 // ======= Transverse multipole (standard accelerator definition) =======
93 const int n = pole_number / 2; // 1=dipole, 2=quadrupole, 3=sextupole, ...
94 const int a = n - 1; // power of r
95
96 // r^a scaling with reference radius r0; for dipole (a=0) this is 1
97 double ra = 1.0;
98 if (a > 0) {
99 // If r=0 and a>0, |B|=0 (for ideal multipoles).
100 if (r == 0.0) {
101 bfield[0] = bfield[1] = bfield[2] = 0.0;
102 return;
103 }
104 ra = std::pow(r / r0, static_cast<double>(a));
105 }
106
107 // "Normal" multipole angular dependence:
108 // Bu = strength * r^(n-1) * cos((n-1)*phi)
109 // Bv = strength * r^(n-1) * sin((n-1)*phi)
110 // This yields a constant transverse dipole for n=1.
111 const double Bu = strength * ra * std::cos(a * phi);
112 const double Bv = strength * ra * std::sin(a * phi);
113
114 // Place (Bu,Bv) into the correct transverse components of B_local; axial component = 0
115 G4ThreeVector B_local(0, 0, 0);
116 switch (rotaxis) {
117 case 0: B_local.setY(Bu);
118 B_local.setZ(Bv);
119 break; // axis X → (Y,Z)
120 case 1: B_local.setZ(Bu);
121 B_local.setX(Bv);
122 break; // axis Y → (Z,X)
123 case 2: B_local.setX(Bu);
124 B_local.setY(Bv);
125 break; // axis Z → (X,Y)
126 default: break;
127 }
128
129 // Rotate (roll) back to lab
130 G4ThreeVector B_lab = B_local;
131 if (rotaxis == 0) B_lab.rotateX(+rotation_angle);
132 else if (rotaxis == 1) B_lab.rotateY(+rotation_angle);
133 else if (rotaxis == 2) B_lab.rotateZ(+rotation_angle);
134
135 // Output
136 bfield[0] = B_lab.x();
137 bfield[1] = B_lab.y();
138 bfield[2] = B_lab.z();
139
140 log->info(2, "Pole Number: ", pole_number,
141 ", n: ", n,
142 ", Strength: ", strength,
143 ", Requested at: (", pos[0], ", ", pos[1], ", ", pos[2], ")",
144 ", Rotation angle: ", rotation_angle,
145 ", Rotation axis: ", rotaxis,
146 ", longitudinal: ", longitudinal,
147 ", Field: (", bfield[0], ", ", bfield[1], ", ", bfield[2], ")");
148}
149
150
152 gfield_definitions = gfd;
153
154 pole_number = get_field_parameter_int("pole_number");
155 origin[0] = get_field_parameter_double("vx");
156 origin[1] = get_field_parameter_double("vy");
157 origin[2] = get_field_parameter_double("vz");
158 rotation_angle = get_field_parameter_double("rotation_angle");
159 auto rot_axis_option = gfield_definitions.field_parameters["rotaxis"];
160 longitudinal = false;
161 if (gfield_definitions.field_parameters["longitudinal"] == "true") {
162 longitudinal = true;
163 log->info(1, "Longitudinal field");
164 }
165 else { log->info(1, "Transverse field"); }
166
167 if (rot_axis_option == "X" || rot_axis_option == "x") { rotaxis = 0; }
168 else if (rot_axis_option == "Y" || rot_axis_option == "y") { rotaxis = 1; }
169 else if (rot_axis_option == "Z" || rot_axis_option == "z") { rotaxis = 2; }
170 else {
171 log->error(ERR_WRONG_FIELD_ROTATION, "GField_MultipolesFactory::load_field_definitions: Rotation axis " + gfield_definitions.field_parameters["rotaxis"] +
172 " not supported. Exiting.");
173 }
174 log->info(1, "Rotation axis: ", rotaxis);
175 strength = get_field_parameter_double("strength");
176}
Factory class for creating and managing multipole magnetic fields.
void GetFieldValue(const double pos[3], G4double *bfield) const override
Calculates the magnetic field at a given position.
void load_field_definitions(GFieldDefinition gfd) override
Sets the field definition for the field.
Abstract base class representing a magnetic field.
Definition gfield.h:62
GFieldDefinition gfield_definitions
Definition gfield.h:113
double get_field_parameter_double(const std::string &key)
Definition gfield.h:91
int get_field_parameter_int(const std::string &key)
Definition gfield.h:90
#define ERR_WRONG_FIELD_ROTATION
#define ERR_WRONG_POLE_NUMBER
GField * GFieldFactory(const std::shared_ptr< GOptions > &g)
Utility struct to load GFields from options.
Definition gfield.h:18
std::map< std::string, std::string > field_parameters
Field parameters as key-value pairs.
Definition gfield.h:29