BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSBendBuilder.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 "globals.hh" // geant4 globals / types
20
21#include "BDSAcceleratorComponent.hh"
22#include "BDSBeamPipeInfo.hh"
23#include "BDSBendBuilder.hh"
24#include "BDSComponentFactory.hh"
25#include "BDSDebug.hh"
26#include "BDSException.hh"
27#include "BDSFieldType.hh"
28#include "BDSGlobalConstants.hh"
29#include "BDSIntegratorSet.hh"
30#include "BDSIntegratorType.hh"
31#include "BDSIntegratorSetType.hh"
32#include "BDSMagnetStrength.hh"
33#include "BDSLine.hh"
34#include "BDSMagnet.hh"
35#include "BDSMagnetOuterInfo.hh"
36#include "BDSModulatorInfo.hh"
37#include "BDSUtilities.hh"
38
39#include "parser/element.h"
40#include "parser/elementtype.h"
41
42#include "G4Transform3D.hh"
43
44using namespace GMAD;
45
47{
48 G4bool finiteAngle = BDS::IsFinite((*st)["angle"]);
49 G4bool finiteField = BDS::IsFinite((*st)["field"]);
50 return !(finiteAngle || finiteField);
51}
52
53BDSAcceleratorComponent* BDS::BuildSBendLine(const G4String& elementName,
54 const Element* element,
56 G4double brho,
57 const BDSIntegratorSet* integratorSet,
58 G4double incomingFaceAngle,
59 G4double outgoingFaceAngle,
60 G4bool buildFringeFields,
61 const GMAD::Element* prevElement,
62 const GMAD::Element* nextElement,
63 BDSModulatorInfo* fieldModulator)
64{
65 const G4String baseName = elementName;
66 const G4double thinElementArcLength = BDSGlobalConstants::Instance()->ThinElementLength();
67 const G4bool yokeOnLeft = BDSComponentFactory::YokeOnLeft(element,st);
68 const G4double arcLength = element->l * CLHEP::m;
69 const G4double angle = (*st)["angle"];
70 G4double fint = element->fint;
71 G4double fintx = element->fintx;
72 G4double hgap = element->hgap * CLHEP::m;
73 G4double synchronousT0AtStart = (*st)["synchronousT0"];
74 G4double synchronousT0AtEnd = synchronousT0AtStart + (arcLength/CLHEP::c_light);
75 G4double synchronousT0AtMiddle = synchronousT0AtStart + (0.5*arcLength/CLHEP::c_light);
76 (*st)["synchronousT0"] = synchronousT0AtMiddle; // update the value on the strength
77
78 // Note for tilted dipoles, the geometry is tilted but the curvilinear world isn't,
79 // therefore we tilt the field to match the geometry.
80 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
81
82 G4bool buildFringeIncoming = buildFringeFields;
83 G4bool buildFringeOutgoing = buildFringeFields;
84
85 G4bool finiteK1 = BDS::IsFinite((*st)["k1"]);
86 BDSFieldType dipoleFieldType = finiteK1 ? BDSFieldType::dipolequadrupole : BDSFieldType::dipole;
87
88 if (buildFringeFields)
89 {
90 // perform series of checks on fringe field parameters
91 // nominally, don't build the fringe element if there's no face angle
92 if (!BDS::IsFinite(element->e1))
93 {buildFringeIncoming = false;} // by default false
94 if (!BDS::IsFinite(element->e2))
95 {buildFringeOutgoing = false;}
96
97 // fintx = -1 is how madx writes out that fintx should default to fint
98 // it's also our default. If by default fint=0 and fintx=0, no fringes
99 // would be built. If finite fint and -1 for fintx both will be built.
100 // if fint=0 and fintx != -1, only the outgoin will be built
101 if (fintx == -1)
102 {fintx = fint;}
103
104 // however if finite hgap and fint or fintx specified, there is an effect
105 if (BDS::IsFinite(fint) && BDS::IsFinite(hgap))
106 {buildFringeIncoming = true;}
107 if (BDS::IsFinite(fintx) && BDS::IsFinite(hgap))
108 {buildFringeOutgoing = true;}
109
110 // don't build fringe elements if a non-matrix integrator set is used and
111 // only a pole face effect is specified. The angled pole face geometry is
112 // constructed so a thin integrated pole face kick shouldn't be applied as well
113 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry())
114 {
115 // no hgap check - finite hgap but 0 fint/x will not produce a fringe kick so fringes won't be built anyway
116 if (BDS::IsFinite(element->e1) && !BDS::IsFinite(fint))
117 {buildFringeIncoming = false;}
118 if (BDS::IsFinite(element->e2) && !BDS::IsFinite(fintx))
119 {buildFringeOutgoing = false;}
120 }
121
122 // overriding checks - don't build fringe field if we're butt against another
123 // sbend.
124 if (prevElement)
125 {
126 if (prevElement->type == ElementType::_SBEND)
127 {
128 buildFringeIncoming = false;
129 if (BDS::IsFinite(prevElement->e2 - element->e1))
130 {throw BDSException( __METHOD_NAME__, prevElement->name + " e2 clashes with " + elementName + " e1");}
131 }
132 }
133 if (nextElement)
134 {
135 if (nextElement->type == ElementType::_SBEND)
136 {buildFringeOutgoing = false;}
137 // check above on clashing sbends with e1 e2 will be used for next bend.
138 }
139
140 if (!BDS::IsFinite(angle))
141 {// no fringe fields if magnet of zero angle, ie strength
142 buildFringeIncoming = false;
143 buildFringeOutgoing = false;
144 }
145 }// end of checks
146
147 // Calculate number of sbends to split parent into
148 G4int nSBends = BDS::CalculateNSBendSegments(arcLength, angle, incomingFaceAngle,
149 outgoingFaceAngle);
150
151 G4bool zeroStrength = BDS::ZeroStrengthDipole(st);
152 if (zeroStrength)
153 {
154 buildFringeIncoming = false;
155 buildFringeOutgoing = false;
156 }
157
158 // If no angle, means no fringe strength, so even if there are pole face rotations, we don't
159 // need the fringe elements. If there's only 1 segment we may still have a finite
160 // angle and therefore finite strength and the pole face rotations will require fringe
161 // elements
162 if (!BDS::IsFinite(angle))
163 {
164 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
165 BDSFieldInfo* vacuumField = nullptr;
166 BDSFieldInfo* outerField = nullptr;
167 // prepare one sbend segment
168 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, -incomingFaceAngle,
169 -outgoingFaceAngle);
170 auto mgInfo = BDSComponentFactory::PrepareMagnetOuterInfo(baseName, element,
171 -incomingFaceAngle,
172 -outgoingFaceAngle,
173 bpInfo,
174 yokeOnLeft);
175 if (!zeroStrength)
176 {
177 vacuumField = new BDSFieldInfo(dipoleFieldType,
178 brho,
179 intType,
180 st,
181 true,
182 fieldTiltOffset);
183 vacuumField->SetModulatorInfo(fieldModulator);
185 BDSFieldType::dipole,
186 bpInfo,
187 mgInfo,
188 fieldTiltOffset,
189 integratorSet,
190 brho,
192 fieldModulator);
193 }
194 BDSMagnet* oneBend = new BDSMagnet(BDSMagnetType::sectorbend,
195 baseName,
196 arcLength,
197 bpInfo,
198 mgInfo,
199 vacuumField,
200 -angle,
201 outerField); // angle 0, but should be minus for 3d cart.
202 return oneBend;
203 }
204
205 // We're definitely building a line now.
206 BDSLine* sbendline = new BDSLine(baseName); // line for resultant sbend
207
208 // length of bends minus the fringe segments
209 G4double centralArcLength = arcLength;
210 G4double centralAngle = angle;
211 G4double oneFringeAngle = 0;
212 if (BDS::IsFinite(angle))
213 {oneFringeAngle = (thinElementArcLength / arcLength) * angle;}
214
215 if (buildFringeIncoming)
216 {
217 centralArcLength -= thinElementArcLength;
218 centralAngle -= oneFringeAngle;
219 }
220 if (buildFringeOutgoing)
221 {
222 centralArcLength -= thinElementArcLength;
223 centralAngle -= oneFringeAngle;
224 }
225
226 // calculate their angles and length
227 G4double semiAngle = centralAngle / (G4double) nSBends;
228 G4double semiArcLength = centralArcLength / (G4double) nSBends;
229
230 BDSMagnetStrength* semiStrength = new BDSMagnetStrength(*st); // the copy is crucial to copy the field strength
231 (*semiStrength)["angle"] = semiAngle;
232 (*semiStrength)["length"] = semiArcLength;
233
234 G4double zExtentIn = 0;
235 G4double zExtentOut = 0;
236 G4bool fadeIn = true;
237 G4bool fadeOut = true;
238
239 // unlike an rbend, the sbend will mostly likely be split up into segments.
240 // we must check that the faces of each segment (varying from e1 to e2) will
241 // not overlap given the outer diameter.
242 // calculate extent along z due poleface rotation at half the horizontal width.
243 // note this is only w.r.t. pole face rotation and not the general bending angle
244 G4double horizontalWidth = BDSComponentFactory::PrepareHorizontalWidth(element);
245 if (incomingFaceAngle > 0)
246 {zExtentIn = 0.5*horizontalWidth*std::tan(incomingFaceAngle - 0.5*std::abs(semiAngle));}
247 else if (incomingFaceAngle < 0)
248 {zExtentIn = 0.5*horizontalWidth*std::tan(0.5*std::abs(semiAngle) + incomingFaceAngle);}
249 if (outgoingFaceAngle > 0)
250 {zExtentOut = 0.5*horizontalWidth*std::tan(outgoingFaceAngle - 0.5*std::abs(semiAngle));}
251 else if (outgoingFaceAngle < 0)
252 {zExtentOut = 0.5*horizontalWidth*std::tan(0.5*std::abs(semiAngle) + outgoingFaceAngle);}
253
254 // decide if segment angles fade or not depending on the extents
255 if (std::abs(zExtentIn) < semiArcLength/4.0)
256 {fadeIn = false;}
257 if (std::abs(zExtentOut) < semiArcLength/4.0)
258 {fadeOut = false;}
259
260 // field recipe for one segment of the sbend
261 G4String centralName = baseName + "_even_ang";
262 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
263 BDSFieldInfo* semiVacuumField = nullptr;
264 BDSFieldInfo* semiOuterField = nullptr;
265 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, 0.5*semiAngle, 0.5*semiAngle);
266 auto mgInfo = BDSComponentFactory::PrepareMagnetOuterInfo(centralName, element,
267 0.5*semiAngle, 0.5*semiAngle, bpInfo,
268 yokeOnLeft);
269 if (!zeroStrength)
270 {
271 semiVacuumField = new BDSFieldInfo(dipoleFieldType,
272 brho,
273 intType,
274 semiStrength,
275 true,
276 fieldTiltOffset);
277 semiVacuumField->SetModulatorInfo(fieldModulator);
278 semiOuterField = BDSComponentFactory::PrepareMagnetOuterFieldInfo(semiStrength,
279 BDSFieldType::dipole,
280 bpInfo,
281 mgInfo,
282 fieldTiltOffset,
283 integratorSet,
284 brho,
286 fieldModulator);
287 }
288 mgInfo->name = centralName;
289 BDSMagnet* centralWedge = new BDSMagnet(BDSMagnetType::sectorbend,
290 centralName,
291 semiArcLength,
292 bpInfo,
293 mgInfo,
294 semiVacuumField,
295 -semiAngle,
296 semiOuterField); // minus for 3d cartesian conversion
297
298 // check magnet outer info
299 auto magnetOuterInfoCheck = BDSComponentFactory::PrepareMagnetOuterInfo("checking", element,
300 -incomingFaceAngle,
301 -outgoingFaceAngle,
302 bpInfo, yokeOnLeft);
303 // minus for conversion to 3d cartesian
304 G4double minimalRadius = 2*magnetOuterInfoCheck->MinimumIntersectionRadiusRequired();
305 // extra pedantic check for dipoles with certain geometry types
306 if (!magnetOuterInfoCheck->hStyle)
307 {// in the case of C-shaped poled dipoles, the full 'horizontalWidth' is shifted to the yoke side
308 switch (magnetOuterInfoCheck->geometryType.underlying())
309 {
310 case BDSMagnetGeometryType::polescircular:
311 case BDSMagnetGeometryType::polesfacet:
312 case BDSMagnetGeometryType::polesfacetcrop:
313 case BDSMagnetGeometryType::polessquare:
314 {
315 minimalRadius *= element->yokeOnInside ? 2.0 : 0.5;
316 break;
317 }
318 default:
319 {break;}
320 }
321 }
322 BDSComponentFactory::CheckBendLengthAngleWidthCombo(semiArcLength, -semiAngle, minimalRadius, element->name);
323 delete magnetOuterInfoCheck; // clean up
324
325 // build incoming fringe field if required
326 if (buildFringeIncoming)
327 {
328 G4double e1 = element->e1;
329 // if using a non-matrix integrator set, this code should only be reached if a finite fint is specified.
330 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so set
331 // e1 to 0 in the magnet strength object so only fringe effects are applied
332 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
333 && BDS::IsFinite(fint))
334 {e1 = 0;}
335
336 BDSMagnetStrength* fringeStIn = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
337 e1, element->e2, fintx, true);
338 (*fringeStIn)["synchronousT0"] = synchronousT0AtStart;
339 G4String segmentName = baseName + "_e1_fringe";
340 G4double fringeAngleIn = 0.5*oneFringeAngle - incomingFaceAngle;
341 G4double fringeAngleOut = 0.5*oneFringeAngle + incomingFaceAngle;
342 BDSMagnet* startfringe = BDS::BuildDipoleFringe(element, fringeAngleIn, fringeAngleOut,
343 segmentName, fringeStIn, brho,
344 integratorSet, dipoleFieldType, fieldModulator);
345 sbendline->AddComponent(startfringe);
346 }
347
348 // logic for elements in the sbend line:
349 // reuse central segment for all segment of in/out half if no poleface angle(s)
350 // if small poleface, new first/last segment, reuse central wedge for remainder of in/out half
351 // otherwise fade in/out faces for all segments in app. halves.
352 // 'central' one is definitely used for the central part, but also it's just a segment
353 // with even incoming and outgoing face angles w.r.t. the chord.
354 G4double segmentAngleIn = 0;
355 G4double segmentAngleOut = 0;
356 G4int numberOfUniqueComponents = 1; // used for naming purposes
357 BDSMagnet* oneBend = nullptr;
358 G4bool centralWedgeUsed = false; // keep track to avoid memory leak
359 const G4int numSegmentsEitherSide = (nSBends - 1) / 2;
360 for (G4int i = 0; i < nSBends; ++i)
361 {
362 G4String name = baseName;
363 if (i < numSegmentsEitherSide)
364 {// first half of magnet
365 if (!BDS::IsFinite(incomingFaceAngle)) // no pole face rotation so just repeat central segment
366 {oneBend = centralWedge;}
367 else if (fadeIn) // build incremented angled segment
368 {
369 name += "_"+std::to_string(numberOfUniqueComponents);
370 numberOfUniqueComponents++;
371 BDS::UpdateSegmentAngles(i,nSBends,semiAngle,incomingFaceAngle,outgoingFaceAngle,segmentAngleIn,segmentAngleOut);
372 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
373 segmentAngleIn, segmentAngleOut, semiStrength,
374 brho, integratorSet, yokeOnLeft, semiOuterField);
375 }
376 else
377 {// finite pole face, but not strong so build one angled, then repeat the rest to save memory
378 if (i == 0) // the first one is unique
379 {
380 name += "_"+std::to_string(numberOfUniqueComponents);
381 numberOfUniqueComponents++;
382 segmentAngleIn = 0.5*semiAngle - incomingFaceAngle; // whole pole face angle
383 segmentAngleOut = 0.5*semiAngle; // even matching angle
384 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
385 segmentAngleIn, segmentAngleOut, semiStrength,
386 brho, integratorSet, yokeOnLeft, semiOuterField);
387 }
388 else // others afterwards are a repeat of the even angled one
389 {oneBend = centralWedge;}
390 }
391 }
392 else if (i > numSegmentsEitherSide) // i is zero counting
393 {// second half of magnet
394 if (!BDS::IsFinite(outgoingFaceAngle)) // no pole face rotation so just repeat central segment
395 {oneBend = centralWedge;}
396 else if (fadeOut) // build incremented angled segment
397 {
398 name += "_"+std::to_string(numberOfUniqueComponents);
399 numberOfUniqueComponents++;
400 BDS::UpdateSegmentAngles(i,nSBends,semiAngle,incomingFaceAngle,outgoingFaceAngle,segmentAngleIn,segmentAngleOut);
401 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
402 segmentAngleIn, segmentAngleOut, semiStrength,
403 brho, integratorSet, yokeOnLeft, semiOuterField);
404 }
405 else
406 {// finite pole face, but not strong so build only one unique angled on output face
407 if (i == (nSBends-1)) // last one
408 {
409 name += "_"+std::to_string(numberOfUniqueComponents);
410 numberOfUniqueComponents++;
411 segmentAngleIn = 0.5*semiAngle;
412 segmentAngleOut = 0.5*semiAngle - outgoingFaceAngle;
413 oneBend = BDS::BuildSingleSBend(element, name, semiArcLength, semiAngle,
414 segmentAngleIn, segmentAngleOut, semiStrength,
415 brho, integratorSet, yokeOnLeft, semiOuterField);
416 }
417 else // after central, but before unique end piece - even angled.
418 {oneBend = centralWedge;}
419 }
420 }
421 else // the middle piece
422 {oneBend = centralWedge;}
423
424 // append to the line
425 sbendline->AddComponent(oneBend);
426
427 centralWedgeUsed = centralWedgeUsed || (oneBend == centralWedge);
428 }
429
430 if (!centralWedgeUsed)
431 {delete centralWedge;}
432
433 //Last element should be fringe if poleface specified
434 if (buildFringeOutgoing)
435 {
436 G4double e2 = element->e2;
437 // if using a non-matrix integrator set, this code should only be reached if a finite fintx is specified.
438 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so set
439 // e2 to 0 in the magnet strength object so only fringe effects are applied
440 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
441 && BDS::IsFinite(fintx))
442 {e2 = 0;}
443
444 BDSMagnetStrength* fringeStOut = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
445 element->e1, e2, fintx, false);
446 (*fringeStOut)["synchronousT0"] = synchronousT0AtEnd;
447 G4double fringeAngleIn = 0.5*oneFringeAngle + outgoingFaceAngle;
448 G4double fringeAngleOut = 0.5*oneFringeAngle - outgoingFaceAngle;
449 G4String segmentName = baseName + "_e2_fringe";
450
451 BDSMagnet* endfringe = BDS::BuildDipoleFringe(element, fringeAngleIn, fringeAngleOut,
452 segmentName, fringeStOut, brho,
453 integratorSet, dipoleFieldType);
454 sbendline->AddComponent(endfringe);
455 }
456 return sbendline;
457}
458
459void BDS::UpdateSegmentAngles(G4int index,
460 G4int nSBends,
461 G4double semiAngle,
462 G4double incomingFaceAngle,
463 G4double outgoingFaceAngle,
464 G4double& segmentAngleIn,
465 G4double& segmentAngleOut)
466{
467 // nSBends is always odd.
468 // index assumed 0 counting.
469 G4bool central = index == (nSBends - 1) / 2;
470 if (central)
471 {// shouldn't happen, but just in case
472 segmentAngleIn = 0.5*semiAngle;
473 segmentAngleOut = 0.5*semiAngle;
474 return;
475 }
476
477 G4bool firstHalf = index + 1 < (nSBends + 1) / 2;
478 G4int numberToFadeOver = (nSBends - 1) / 2;
479 if (firstHalf)
480 {// fade from incomingFaceAngle to 0
481 G4double delta = incomingFaceAngle / (G4double) numberToFadeOver;
482 G4double inputFaceAngle = incomingFaceAngle - ((G4double)index * delta);
483 G4double outputFaceAngle = incomingFaceAngle - ((G4double)(index+1) * delta);
484 // if incomingFaceAngle was 0, each would have 1/2 of semiAngle
485 segmentAngleIn = 0.5*semiAngle - inputFaceAngle;
486 segmentAngleOut = 0.5*semiAngle + outputFaceAngle;
487 }
488 else
489 {// second half - fade from 0 to outgoingFaceAngle
490 G4double delta = outgoingFaceAngle / (G4double) numberToFadeOver;
491 G4int secondHalfIndex = index - ((nSBends + 1) / 2);
492 // here we fade from 0 contribution to maximum (opposite of above if statement)
493 G4double inputFaceAngle = outgoingFaceAngle - ( (G4double)(numberToFadeOver - secondHalfIndex) * delta);
494 G4double outputFaceAngle = outgoingFaceAngle - ( (G4double)(numberToFadeOver - (secondHalfIndex + 1)) * delta);
495 // if incomingFaceAngle was 0, each would have 1/2 of semiAngle
496 segmentAngleIn = 0.5*semiAngle + inputFaceAngle;
497 segmentAngleOut = 0.5*semiAngle - outputFaceAngle;
498 }
499}
500
502 const G4String& name,
503 G4double arcLength,
504 G4double angle,
505 G4double angleIn,
506 G4double angleOut,
507 const BDSMagnetStrength* strength,
508 G4double brho,
509 const BDSIntegratorSet* integratorSet,
510 G4bool yokeOnLeft,
511 const BDSFieldInfo* outerFieldIn,
512 BDSModulatorInfo* fieldModulator)
513{
514 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, angleIn, angleOut);
515
516 BDSMagnetStrength* strengthCopy = new BDSMagnetStrength(*strength); // the copy is crucial to copy the field strength
517 auto magnetOuterInfo = BDSComponentFactory::PrepareMagnetOuterInfo(name, element, angleIn, angleOut, bpInfo, yokeOnLeft);
518 // set the name to the desired one rather than the one from the element
519 magnetOuterInfo->name = name;
520
521 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
522
523 G4bool finiteK1 = BDS::IsFinite((*strength)["k1"]);
524 BDSFieldType dipoleFieldType = finiteK1 ? BDSFieldType::dipolequadrupole : BDSFieldType::dipole;
525 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
526 BDSFieldInfo* vacuumField = nullptr;
527 BDSFieldInfo* outerField = nullptr;
528 G4bool zeroStrength = BDS::ZeroStrengthDipole(strengthCopy);
529 if (!zeroStrength)
530 {
531 vacuumField = new BDSFieldInfo(dipoleFieldType,
532 brho,
533 intType,
534 strengthCopy,
535 true,
536 fieldTiltOffset);
537 vacuumField->SetModulatorInfo(fieldModulator);
538 outerField = new BDSFieldInfo(*outerFieldIn);
539 }
540
541 BDSMagnet* magnet = new BDSMagnet(BDSMagnetType::sectorbend,
542 name,
543 arcLength,
544 bpInfo,
545 magnetOuterInfo,
546 vacuumField,
547 -angle,
548 outerField);
549
550 return magnet;
551}
552
553BDSLine* BDS::BuildRBendLine(const G4String& elementName,
554 const Element* element,
555 const Element* prevElement,
556 const Element* nextElement,
557 G4double brho,
559 const BDSIntegratorSet* integratorSet,
560 G4double incomingFaceAngle,
561 G4double outgoingFaceAngle,
562 G4bool buildFringeFields,
563 BDSModulatorInfo* fieldModulator)
564{
565 const G4String name = elementName;
566 BDSLine* rbendline = new BDSLine(name); // line for resultant rbend
567
568 const G4double thinElementArcLength = BDSGlobalConstants::Instance()->ThinElementLength();
569 const G4bool yokeOnLeft = BDSComponentFactory::YokeOnLeft(element, st);
570 G4bool buildFringeIncoming = buildFringeFields;
571 G4bool buildFringeOutgoing = buildFringeFields;
572 G4double fint = element->fint;
573 G4double fintx = element->fintx;
574 G4double hgap = element->hgap * CLHEP::m;
575
576 // Angle here is in the 'strength' convention of +ve angle -> -ve x deflection
577 const G4double angle = (*st)["angle"];
578 const G4double arcLength = (*st)["length"];
579
580 G4double synchronousT0AtStart = (*st)["synchronousT0"];
581 G4double synchronousT0AtEnd = synchronousT0AtStart + (arcLength/CLHEP::c_light);
582 G4double synchronousT0AtMiddle = synchronousT0AtStart + (0.5*arcLength/CLHEP::c_light);
583 (*st)["synchronousT0"] = synchronousT0AtMiddle; // update the value on the strength
584
585 G4bool zeroStrength = BDS::ZeroStrengthDipole(st);
586
587 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
588
589 G4bool finiteK1 = BDS::IsFinite((*st)["k1"]);
590 BDSFieldType dipoleFieldType = finiteK1 ? BDSFieldType::dipolequadrupole : BDSFieldType::dipole;
591
592 // Here, 'no face angle' really means that the rbend becomes an sbend.
593 // Calculate how far away we are from an sbend.
594 G4double incomingFaceAngleWRTSBend = incomingFaceAngle + angle*0.5;
595 G4double outgoingFaceangleWRTSBend = outgoingFaceAngle + angle*0.5;
596 if (!BDS::IsFinite(incomingFaceAngleWRTSBend) && (!integratorSet->IsMatrixIntegratorSet()))
597 {buildFringeIncoming = false;}
598 if (!BDS::IsFinite(outgoingFaceangleWRTSBend) && (!integratorSet->IsMatrixIntegratorSet()))
599 {buildFringeOutgoing = false;}
600
601 // fintx = -1 is how madx writes out that fintx should default to fint
602 // it's also our default. If by default fint=0 and fintx=0, no fringes
603 // would be built. If finite fint and -1 for fintx both will be built.
604 // if fint=0 and fintx != -1, only the outgoin will be built
605 if (fintx == -1)
606 {fintx = fint;}
607
608 // however if finite hgap and fint or fintx specified, there is an effect
609 if (BDS::IsFinite(fint) && BDS::IsFinite(hgap))
610 {buildFringeIncoming = true;}
611 if (BDS::IsFinite(fintx) && BDS::IsFinite(hgap))
612 {buildFringeOutgoing = true;}
613
614 // the poleface angles to be used in tracking only.
615 G4double trackingPolefaceAngleIn = element->e1;
616 G4double trackingPolefaceAngleOut = element->e2;
617 if (integratorSet->IsMatrixIntegratorSet())
618 {// for this integrator set we track the rbend as an sbend with extra pole face rotation
619 trackingPolefaceAngleIn += angle*0.5;
620 trackingPolefaceAngleOut += angle*0.5;
621 }
622
623 G4double e1 = -incomingFaceAngle;
624 G4double e2 = -outgoingFaceAngle;
625
626 if (prevElement)
627 {
628 if (BDS::IsFinite(prevElement->e2) && BDS::IsFinite(element->e1))
629 {
630 throw BDSException(__METHOD_NAME__, prevElement->name + " has finite e2!\n Clashes with " + elementName + " with finite e1");
631 }
632 if (prevElement->type == ElementType::_RBEND)
633 {
634 buildFringeIncoming = false;
635 e1 = angle*0.5;
636 }
637 }
638 if (nextElement)
639 {
640 if (BDS::IsFinite(nextElement->e1) && BDS::IsFinite(element->e2))
641 {
642 throw BDSException(__METHOD_NAME__ + nextElement->name + " has finite e1!\n Clashes with " + elementName + " with finite e2");
643 }
644 if (nextElement->type == ElementType::_RBEND)
645 {
646 buildFringeOutgoing = false;
647 e2 = angle*0.5;
648 }
649 }
650
651 if (zeroStrength)
652 {
653 buildFringeIncoming = false;
654 buildFringeOutgoing = false;
655 }
656
657 // used for debugging purposes to forefully try out one and not the other fringe
658 //buildFringeIncoming = false;
659 //buildFringeOutgoing = false;
660
661 // if we're building the fringe elements, we reduce the length of the central section
662 // and proportion the bending by their length w.r.t. the full length of the total rbend.
663 // angles of the faces are in order:
664 // angleIn / fringeInOutputAngle / centralInputFaceAngle / centralOutputFaceAngle
665 // fringeOutInputAngle / angleOut
666 // default face angles for an rbend are 0 - ie parallel faces, plus any pole face rotation
667 // angle in and out of total rbend are nominally the face angles.
668 G4double angleIn = incomingFaceAngle;
669 G4double angleOut = outgoingFaceAngle;
670 G4double fringeInOutputAngle = 0;
671 G4double centralInputFaceAngle = angleIn;
672 G4double centralOutputFaceAngle = angleOut;
673 G4double fringeOutInputAngle = 0;
674 G4double centralArcLength = arcLength; // full length to start with
675 G4double centralAngle = angle;
676 G4double oneFringeAngle = 0;
677 if (BDS::IsFinite(angle))
678 {oneFringeAngle = (thinElementArcLength / arcLength) * angle;}
679
680 if (buildFringeIncoming && buildFringeOutgoing)
681 {// both
682 centralArcLength -= 2*thinElementArcLength;
683 centralAngle -= 2*oneFringeAngle;
684 angleIn = e1 + (0.5*oneFringeAngle - 0.5*angle);
685 fringeInOutputAngle = -angleIn;
686 centralInputFaceAngle = e1;
687 centralOutputFaceAngle = e2;
688 fringeOutInputAngle = - (e2 + (0.5*oneFringeAngle - 0.5*angle));
689 angleOut = -fringeOutInputAngle;
690 }
691 else if (buildFringeIncoming)
692 {// incoming only
693 centralArcLength -= thinElementArcLength;
694 centralAngle -= oneFringeAngle;
695 angleIn = e1 + (0.5*oneFringeAngle - 0.5*angle);
696 fringeInOutputAngle = -angleIn; // fringe built the same
697 centralInputFaceAngle = e1 + 0.5*oneFringeAngle;
698 centralOutputFaceAngle = e2 - 0.5*oneFringeAngle;
699 }
700 else if (buildFringeOutgoing)
701 {// outgoing only
702 centralArcLength -= thinElementArcLength;
703 centralAngle -= oneFringeAngle;
704 centralInputFaceAngle = e1 - 0.5*oneFringeAngle;
705 centralOutputFaceAngle = e2 + 0.5*oneFringeAngle;
706 fringeOutInputAngle = - (e2 + (0.5*oneFringeAngle - 0.5*angle));;
707 angleOut = e2 + (0.5*oneFringeAngle - 0.5*angle);;
708 }
709 else
710 {// neither
711 // centralArcLength the same
712 // centralAngle the same
713 centralInputFaceAngle = e1;
714 centralOutputFaceAngle = e2;
715 }
716
717 if (buildFringeIncoming)
718 {
719 G4double trackingPolefaceAngle = trackingPolefaceAngleIn;
720 // if using a non-matrix integrator set, this code should only be reached if a finite fint is specified.
721 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so subtract
722 // e1 for in the magnet strength object so only the fringe & the natural rbend angled face effects are applied
723 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
724 && BDS::IsFinite(fint))
725 {trackingPolefaceAngle -= element->e1;}
726
727 BDSMagnetStrength* fringeStIn = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
728 trackingPolefaceAngle, trackingPolefaceAngleOut,
729 fintx, true);
730 (*fringeStIn)["synchronousT0"] = synchronousT0AtStart;
731 G4String fringeName = name + "_e1_fringe";
732
733 // element used for beam pipe materials etc - not strength, angle or length.
734 BDSMagnet* startfringe = BDS::BuildDipoleFringe(element, angleIn, fringeInOutputAngle,
735 fringeName,
736 fringeStIn, brho,
737 integratorSet, dipoleFieldType, fieldModulator);
738 rbendline->AddComponent(startfringe);
739 }
740
741 // override copied length and angle
742 (*st)["length"] = centralArcLength;
743 (*st)["angle"] = centralAngle;
744
745 BDSIntegratorType intType = BDS::GetDipoleIntegratorType(integratorSet, element);
746 BDSFieldInfo* vacuumField = nullptr;
747 BDSFieldInfo* outerField = nullptr;
748 auto bpInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, centralInputFaceAngle, centralOutputFaceAngle);
749 auto mgInfo = BDSComponentFactory::PrepareMagnetOuterInfo(elementName, element, centralInputFaceAngle, centralOutputFaceAngle, bpInfo, yokeOnLeft);
750 mgInfo->name = elementName;
751 if (!zeroStrength)
752 {
753 vacuumField = new BDSFieldInfo(dipoleFieldType,
754 brho,
755 intType,
756 st,
757 true,
758 fieldTiltOffset);
759 vacuumField->SetModulatorInfo(fieldModulator);
761 BDSFieldType::dipole,
762 bpInfo,
763 mgInfo,
764 fieldTiltOffset,
765 integratorSet,
766 brho,
768 fieldModulator);
769 }
770
771 // Here we change from the strength angle convention of +ve angle corresponds to
772 // deflection in negative x, to correct 3d +ve angle corresponds to deflection in
773 // positive x. Hence, angle sign flip for construction.
774 BDSMagnet* oneBend = new BDSMagnet(BDSMagnetType::rectangularbend,
775 elementName+"_centre",
776 centralArcLength,
777 bpInfo,
778 mgInfo,
779 vacuumField,
780 -centralAngle,
781 outerField);
782
783 rbendline->AddComponent(oneBend);
784
785 //Last element should be fringe if poleface specified
786 if (buildFringeOutgoing)
787 {
788 G4double trackingPolefaceAngle = trackingPolefaceAngleOut;
789 // if using a non-matrix integrator set, this code should only be reached if a finite fintx is specified.
790 // As the angled face geometry is constructed, a thin pole face kick shouldn't also be applied, so subtract
791 // e2 for in the magnet strength object so only the fringe & the natural rbend angled face effects are applied
792 if (!integratorSet->IsMatrixIntegratorSet() && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry()
793 && BDS::IsFinite(fintx))
794 {trackingPolefaceAngle -= element->e2;}
795 BDSMagnetStrength* fringeStOut = BDS::GetFringeMagnetStrength(element, st, oneFringeAngle,
796 trackingPolefaceAngleIn, trackingPolefaceAngle,
797 fintx, false);
798 (*fringeStOut)["synchronousT0"] = synchronousT0AtEnd;
799 G4String fringeName = name + "_e2_fringe";
800
801 BDSMagnet* endfringe = BDS::BuildDipoleFringe(element, fringeOutInputAngle, angleOut,
802 fringeName,
803 fringeStOut, brho,
804 integratorSet, dipoleFieldType);
805 rbendline->AddComponent(endfringe);
806 }
807
808 return rbendline;
809}
810
812 G4double angleIn,
813 G4double angleOut,
814 const G4String& name,
816 G4double brho,
817 const BDSIntegratorSet* integratorSet,
818 BDSFieldType dipoleFieldType,
819 BDSModulatorInfo* fieldModulator)
820{
821 BDSBeamPipeInfo* beamPipeInfo = BDSComponentFactory::PrepareBeamPipeInfo(element, angleIn, angleOut);
822 beamPipeInfo->beamPipeType = BDSBeamPipeType::circularvacuum;
823 auto magnetOuterInfo = BDSComponentFactory::PrepareMagnetOuterInfo(name, element,
824 angleIn, angleOut, beamPipeInfo);
825 magnetOuterInfo->geometryType = BDSMagnetGeometryType::none;
826 magnetOuterInfo->name = name;
827 magnetOuterInfo->buildEndPieces = false;
828
829 G4Transform3D fieldTiltOffset = BDSComponentFactory::CreateFieldTransform(element);
830
831 BDSIntegratorType intType = integratorSet->dipoleFringe;
832 BDSFieldInfo* vacuumField = new BDSFieldInfo(dipoleFieldType,
833 brho,
834 intType,
835 st,
836 true,
837 fieldTiltOffset);
838 vacuumField->SetModulatorInfo(fieldModulator);
839
840 return new BDSMagnet(BDSMagnetType::dipolefringe,
841 name,
842 (*st)["length"],
843 beamPipeInfo,
844 magnetOuterInfo,
845 vacuumField,
846 -(*st)["angle"],
847 nullptr,
848 true);
849}
850
851G4int BDS::CalculateNSBendSegments(G4double length,
852 G4double angle,
853 G4double incomingFaceAngle,
854 G4double outgoingFaceAngle,
855 G4double aperturePrecision)
856{
857 // Split a bend into equal segments such that the maximum distance between the
858 // chord and arc is 1mm.
859
860 // from formula: L/2 / N tan (angle/N) < precision. (L=physical length)
861 // add poleface rotations onto angle as absolute number (just to be safe)
862 G4double totalAngle = std::abs(angle) + std::abs(incomingFaceAngle) + std::abs(outgoingFaceAngle);
863 G4int nSBends = (G4int) ceil(std::sqrt(length*totalAngle/2/aperturePrecision));
864 if (nSBends==0)
865 {nSBends = 1;} // can happen in case angle = 0
866 if (BDSGlobalConstants::Instance()->DontSplitSBends())
867 {nSBends = 1;} // use for debugging
868 if (nSBends % 2 == 0)
869 {nSBends += 1;} // always have odd number of poles for poleface rotations
870#ifdef BDSDEBUG
871 G4cout << __METHOD_NAME__ << " splitting sbend into " << nSBends << " sbends" << G4endl;
872#endif
873 return nSBends;
874}
875
877 const Element* element)
878{
879 // default is dipole integrator
880 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::dipole);
881
882 // check for finite k1 and change integrator type if needed
883 if (BDS::IsFinite(element->k1))
884 {intType = integratorSet->Integrator(BDSFieldType::dipolequadrupole);}
885
886 //if (BDS::IsFinite(element->tilt))
887 // {intType = BDSIntegratorType::dipolerodrigues2;}
888
889 return intType;
890}
891
892BDSMagnetStrength* BDS::GetFringeMagnetStrength(const Element* element,
893 const BDSMagnetStrength* st,
894 G4double fringeAngle,
895 G4double e1,
896 G4double e2,
897 G4double fintx,
898 G4bool isEntrance)
899{
900 BDSMagnetStrength* fringeSt = new BDSMagnetStrength(*st);
901 (*fringeSt)["length"] = BDSGlobalConstants::Instance()->ThinElementLength();
902 (*fringeSt)["angle"] = fringeAngle;
903 (*fringeSt)["e1"] = e1; // supply separately as it may be modified for rbends
904 (*fringeSt)["e2"] = e2; // supply separately as it may be modified for rbends
905 (*fringeSt)["fint"] = element->fint;
906 (*fringeSt)["fintx"] = fintx; // supply separately as it may be modified to match madx behaviour
907 (*fringeSt)["fintk2"] = element->fintK2;
908 (*fringeSt)["fintk2"] = element->fintxK2;
909 (*fringeSt)["hgap"] = element->hgap * CLHEP::m;
910 (*fringeSt)["isentrance"] = isEntrance;
911 (*fringeSt)["h1"] = element->h1 / CLHEP::m;
912 (*fringeSt)["h2"] = element->h2 / CLHEP::m;
913 return fringeSt;
914}
Abstract class that represents a component of an accelerator.
Holder class for all information required to describe a beam pipe model.
BDSBeamPipeType beamPipeType
Public member for direct access.
static G4bool YokeOnLeft(const GMAD::Element *el, const BDSMagnetStrength *st)
static G4double PrepareHorizontalWidth(GMAD::Element const *el, G4double defaultHorizontalWidth=-1)
Prepare the element horizontal width in Geant4 units - if not set, use the global default.
static G4Transform3D CreateFieldTransform(const BDSTiltOffset *tiltOffset)
Create a transform from a tilt offset. If nullptr, returns identity transform.
static BDSFieldInfo * PrepareMagnetOuterFieldInfo(const BDSMagnetStrength *vacuumSt, const BDSFieldType &fieldType, const BDSBeamPipeInfo *bpInfo, const BDSMagnetOuterInfo *outerInfo, const G4Transform3D &fieldTransform, const BDSIntegratorSet *integratorSetIn, G4double brhoIn, G4double outerFieldScaling=1.0, BDSModulatorInfo *modulatorInfo=nullptr)
Prepare the field definition for the yoke of a magnet.
static BDSMagnetOuterInfo * PrepareMagnetOuterInfo(const G4String &elementNameIn, const GMAD::Element *el, const BDSMagnetStrength *st, const BDSBeamPipeInfo *beamPipe, G4double defaultHorizontalWidth=-1, G4double defaultVHRatio=1.0, G4double defaultCoilWidthFraction=-1, G4double defaultCoilHeightFraction=-1)
static void CheckBendLengthAngleWidthCombo(G4double arcLength, G4double angle, G4double horizontalWidth, const G4String &name="not given")
static BDSBeamPipeInfo * PrepareBeamPipeInfo(GMAD::Element const *el, const G4ThreeVector &inputFaceNormal=G4ThreeVector(0, 0,-1), const G4ThreeVector &outputFaceNormal=G4ThreeVector(0, 0, 1))
static G4double ScalingFieldOuter(const GMAD::Element *ele)
Get the scaling factor for a particular outer field depending on the global and individual setting.
General exception with possible name of object and message.
Definition: BDSException.hh:35
All info required to build complete field of any type.
Definition: BDSFieldInfo.hh:66
static BDSGlobalConstants * Instance()
Access method.
Which integrator to use for each type of magnet / field object.
G4bool IsMatrixIntegratorSet() const
Accessor for bool of is the integrator set matrix style.
BDSIntegratorType Integrator(const BDSFieldType field) const
Get appropriate integrator based on the field type.
A class that hold multiple accelerator components.
Definition: BDSLine.hh:38
void AddComponent(BDSAcceleratorComponent *component)
Add a component to the line.
Definition: BDSLine.cc:28
Efficient storage of magnet strengths.
Abstract base class that implements features common to all magnets.
Definition: BDSMagnet.hh:45
Holder class for all information required to describe a modulator.
G4bool ZeroStrengthDipole(const BDSMagnetStrength *st)
Return whether finite angle or field for a dipole.
G4int CalculateNSBendSegments(G4double length, G4double angle, G4double incomingFaceAngle=0, G4double outgoingFaceAngle=0, G4double aperturePrecision=1.0)
BDSMagnet * BuildSingleSBend(const GMAD::Element *element, const G4String &name, G4double arcLength, G4double angle, G4double angleIn, G4double angleOut, const BDSMagnetStrength *strength, G4double brho, const BDSIntegratorSet *integratorSet, G4bool yokeOnLeft, const BDSFieldInfo *outerFieldIn, BDSModulatorInfo *fieldModulator=nullptr)
Function to return a single sector bend section.
BDSMagnet * BuildDipoleFringe(const GMAD::Element *element, G4double angleIn, G4double angleOut, const G4String &name, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, BDSFieldType dipoleFieldType, BDSModulatorInfo *fieldModulator=nullptr)
BDSLine * BuildRBendLine(const G4String &elementName, const GMAD::Element *element, const GMAD::Element *prevElement, const GMAD::Element *nextElement, G4double brho, BDSMagnetStrength *st, const BDSIntegratorSet *integratorSet, G4double incomingFaceAngle, G4double outgoingFaceAngle, G4bool buildFringeFields, BDSModulatorInfo *fieldModulator=nullptr)
BDSIntegratorType GetDipoleIntegratorType(const BDSIntegratorSet *integratorSet, const GMAD::Element *element)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
BDSAcceleratorComponent * BuildSBendLine(const G4String &elementName, const GMAD::Element *element, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, G4double incomingFaceAngle, G4double outgoingFaceAngle, G4bool buildFringeFields, const GMAD::Element *prevElement, const GMAD::Element *nextElement, BDSModulatorInfo *fieldModulator=nullptr)
Parser namespace for GMAD language. Combination of Geant4 and MAD.
Element class.
Definition: element.h:43
double hgap
half distance of pole separation for purposes of fringe fields - 'half gap'
Definition: element.h:66
double fintx
fringe field integral at the dipole exit
Definition: element.h:63
double h2
output pole face curvature for bends
Definition: element.h:68
double h1
input pole face curvature for bends
Definition: element.h:67
double fintxK2
second fringe field integral at the dipole exit - for TRANSPORT matching
Definition: element.h:65
double fint
fringe field integral at the dipole entrance
Definition: element.h:62
double e2
output pole face rotation for bends
Definition: element.h:61
double l
length in metres
Definition: element.h:49
double e1
input pole face rotation for bends
Definition: element.h:60
double k1
quadrupole
Definition: element.h:54
double fintK2
second fringe field integral at the dipole entrance - for TRANSPORT matching
Definition: element.h:64
ElementType type
element enum
Definition: element.h:44