BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
BDSComponentFactory.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2022.
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 "BDSComponentFactory.hh"
20
21// elements
22#ifdef USE_AWAKE
23#include "BDSAwakeScintillatorScreen.hh"
24#include "BDSAwakeSpectrometer.hh"
25#endif
26#include "BDSCavityElement.hh"
27#include "BDSCollimatorCrystal.hh"
28#include "BDSCollimatorElliptical.hh"
29#include "BDSCollimatorJaw.hh"
30#include "BDSCollimatorRectangular.hh"
31#include "BDSColours.hh"
32#include "BDSComponentFactoryUser.hh"
33#ifdef USE_DICOM
34#include "BDSCT.hh"
35#include "BDSDicomIntersectVolume.hh"
36#endif
37#include "BDSDegrader.hh"
38#include "BDSDrift.hh"
39#include "BDSDump.hh"
40#include "BDSElement.hh"
41#include "BDSLaserWire.hh"
42#include "BDSLine.hh"
43#include "BDSMagnet.hh"
44#include "BDSSamplerPlane.hh"
45#include "BDSScreen.hh"
46#include "BDSShield.hh"
47#include "BDSTarget.hh"
48#include "BDSTeleporter.hh"
49#include "BDSTerminator.hh"
50#include "BDSTiltOffset.hh"
51#include "BDSTransform3D.hh"
52#include "BDSWireScanner.hh"
53#include "BDSUndulator.hh"
54#include "BDSWarning.hh"
55
56// general
57#include "BDSAcceleratorComponentRegistry.hh"
58#include "BDSBeamPipeFactory.hh"
59#include "BDSBeamPipeInfo.hh"
60#include "BDSBeamPipeType.hh"
61#include "BDSBendBuilder.hh"
62#include "BDSLine.hh"
63#include "BDSCavityInfo.hh"
64#include "BDSCavityType.hh"
65#include "BDSCrystalInfo.hh"
66#include "BDSCrystalType.hh"
67#include "BDSDebug.hh"
68#include "BDSException.hh"
69#include "BDSExecOptions.hh"
70#include "BDSFieldInfo.hh"
71#include "BDSFieldFactory.hh"
72#include "BDSFieldType.hh"
73#include "BDSGlobalConstants.hh"
74#include "BDSGap.hh"
75#include "BDSIntegratorSet.hh"
76#include "BDSIntegratorSetType.hh"
77#include "BDSIntegratorType.hh"
78#include "BDSIntegratorDipoleFringe.hh"
79#include "BDSMagnetOuterFactory.hh"
80#include "BDSMagnetOuterInfo.hh"
81#include "BDSMagnetGeometryType.hh"
82#include "BDSMagnetStrength.hh"
83#include "BDSMagnetType.hh"
84#include "BDSMaterials.hh"
85#include "BDSParser.hh"
86#include "BDSParticleDefinition.hh"
87#include "BDSUtilities.hh"
88
89#include "globals.hh" // geant4 types / globals
90#include "G4String.hh"
91#include "G4Transform3D.hh"
92
93#include "CLHEP/Units/SystemOfUnits.h"
94#include "CLHEP/Units/PhysicalConstants.h"
95
96#include "parser/element.h"
97#include "parser/elementtype.h"
98#include "parser/cavitymodel.h"
99#include "parser/newcolour.h"
100#include "parser/crystal.h"
101
102#include <cmath>
103#include <limits>
104#include <string>
105#include <utility>
106
107using namespace GMAD;
108
110
112 BDSComponentFactoryUser* userComponentFactoryIn,
113 G4bool usualPrintOut):
114 designParticle(designParticleIn),
115 userComponentFactory(userComponentFactoryIn),
116 lengthSafety(BDSGlobalConstants::Instance()->LengthSafety()),
117 thinElementLength(BDSGlobalConstants::Instance()->ThinElementLength()),
118 includeFringeFields(BDSGlobalConstants::Instance()->IncludeFringeFields()),
119 yokeFields(BDSGlobalConstants::Instance()->YokeFields()),
120 integratorSetType(BDSGlobalConstants::Instance()->IntegratorSet())
121{
122 if (!designParticle)
123 {throw BDSException(__METHOD_NAME__, "no valid design particle - required.");}
124 brho = designParticle->BRho();
125 beta0 = designParticle->Beta();
126
127 integratorSet = BDS::IntegratorSet(integratorSetType);
128 if (usualPrintOut)
129 {G4cout << __METHOD_NAME__ << "Using \"" << integratorSetType << "\" set of integrators" << G4endl;}
130
131 PrepareColours(); // prepare colour definitions from parser
132 PrepareCavityModels(); // prepare rf cavity model info from parser
133 PrepareCrystals(); // prepare crystal model info from parser
134}
135
136BDSComponentFactory::~BDSComponentFactory()
137{
138 for (const auto& info : cavityInfos)
139 {delete info.second;}
140 for (const auto& info : crystalInfos)
141 {delete info.second;}
142
143 // Deleted here although not used directly here as new geometry can only be
144 // created through this class.
145 try
146 {//no exceptions in destructor
149 }
150 catch (...)
151 {;}
152}
153
155 Element const* prevElementIn,
156 Element const* nextElementIn,
157 G4double currentArcLength)
158{
159 element = elementIn;
160 prevElement = prevElementIn;
161 nextElement = nextElementIn;
162 G4double angleIn = 0.0;
163 G4double angleOut = 0.0;
164 G4bool registered = false;
165 // Used for multiple instances of the same element but different poleface rotations.
166 // Ie only a drift is modified to match the pole face rotation of a magnet.
167 G4bool differentFromDefinition = false;
168
169#ifdef BDSDEBUG
170 G4cout << __METHOD_NAME__ << "named: \"" << element->name << "\"" << G4endl;
171#endif
172
173 if (BDSAcceleratorComponentRegistry::Instance()->IsRegistered(element->name))
174 {registered = true;}
175
176 if (element->type == ElementType::_DRIFT)
177 {
178 if (prevElement)
179 {angleIn = OutgoingFaceAngle(prevElement);}
180 if (nextElement)
181 {angleOut = IncomingFaceAngle(nextElement);}
182
183 //if drift has been modified at all
184 if (BDS::IsFinite(angleIn) || BDS::IsFinite(angleOut))
185 {differentFromDefinition = true;}
186 }
187 else if (element->type == ElementType::_RBEND)
188 {// bend builder will construct it to match - but here we just know it's different
189 // match a previous rbend with half the angle
190 if (prevElement)
191 {
192 if (prevElement->type == ElementType::_RBEND) // also if includeFringeFields
193 {differentFromDefinition = true;}
194 }
195 // match the upcoming rbend with half the angle
196 if (nextElement)
197 {
198 if (nextElement->type == ElementType::_RBEND) // also if includeFringeFields
199 {differentFromDefinition = true;}
200 }
201 }
202 else if (element->type == ElementType::_SBEND)
203 {
204 if (prevElement)
205 {
206 if (prevElement->type == ElementType::_SBEND && includeFringeFields)
207 {differentFromDefinition = true;}
208 }
209 if (nextElement)
210 {
211 if (nextElement->type == ElementType::_SBEND && includeFringeFields)
212 {differentFromDefinition = true;}
213 }
214 }
215 else if (element->type == ElementType::_THINMULT || (element->type == ElementType::_MULT && !HasSufficientMinimumLength(element, false)) || (element->type == ElementType::_THINRMATRIX))
216 {
217 // thinmultipole only uses one angle - so `angleIn`
219 {// both exist
220 ElementType prevType = prevElement->type;
221 ElementType nextType = nextElement->type;
222 if (prevType == ElementType::_DRIFT && nextType != ElementType::_DRIFT)
223 {angleIn = -IncomingFaceAngle(nextElement);} // previous is drift which will match next
224 else if (prevType != ElementType::_DRIFT && nextType == ElementType::_DRIFT)
225 {angleIn = OutgoingFaceAngle(prevElement);} // next is drift which will match prev
226 }
227 else if (prevElement)
228 {angleIn = OutgoingFaceAngle(prevElement);} // only previous element - match it
229 else
230 {angleIn = IncomingFaceAngle(nextElement);} // only next element - match it
231
232 // flag as unique only if the angleIn is changed and the geometry is built at an angle
233 if (BDS::IsFinite(angleIn))
234 {differentFromDefinition = true;}
235 }
236 else if (element->type == ElementType::_SOLENOID)
237 {// we build incoming / outgoing fringe fields for solenoids
238 if (prevElement)
239 {
240 if (prevElement->type == ElementType::_SOLENOID)
241 {differentFromDefinition = true;}
242 }
243 if (nextElement)
244 {
245 if (nextElement->type == ElementType::_SOLENOID)
246 {differentFromDefinition = true;}
247 }
248 }
249
250 // Check if the component already exists and return that.
251 // Don't use the registry for output elements since reliant on unique name.
252 if (registered && !differentFromDefinition)
253 {
254#ifdef BDSDEBUG
255 G4cout << __METHOD_NAME__ << "using already manufactured component" << G4endl;
256#endif
258 }
259
260 // Update name for this component
261 elementName = element->name;
262
263 if (differentFromDefinition)
264 {
265 G4int val;
267 {
269 val = 0;
270 }
271 else
272 {
273 val = modifiedElements[elementName] + 1;
275 }
276 elementName += "_mod_" + std::to_string(val);
277 }
278
279 BDSAcceleratorComponent* component = nullptr;
280#ifdef BDSDEBUG
281 G4cout << __METHOD_NAME__ << " - creating \"" << elementName << "\"" << G4endl;
282 element->print();
283#endif
284 switch(element->type)
285 {
286 case ElementType::_DRIFT:
287 {component = CreateDrift(angleIn, angleOut); break;}
288 case ElementType::_RF:
289 {
290 component = CreateRF(currentArcLength);
291 differentFromDefinition = true; // unique phase for every placement in beam line
292 break;
293 }
294 case ElementType::_SBEND:
295 {component = CreateSBend(); break;}
296 case ElementType::_RBEND:
297 {component = CreateRBend(); break;}
298 case ElementType::_HKICKER:
299 {component = CreateKicker(KickerType::horizontal); break;}
300 case ElementType::_VKICKER:
301 {component = CreateKicker(KickerType::vertical); break;}
302 case ElementType::_KICKER:
303 case ElementType::_TKICKER:
304 {component = CreateKicker(KickerType::general); break;}
305 case ElementType::_QUAD:
306 {component = CreateQuad(); break;}
307 case ElementType::_SEXTUPOLE:
308 {component = CreateSextupole(); break;}
309 case ElementType::_OCTUPOLE:
310 {component = CreateOctupole(); break;}
311 case ElementType::_DECAPOLE:
312 {component = CreateDecapole(); break;}
313 case ElementType::_MULT:
314 {
315 if(!BDS::IsFinite(element->l))
316 {
317 component = CreateThinMultipole(angleIn);
318 break;
319 }
320 component = CreateMultipole();
321 break;}
322 case ElementType::_THINMULT:
323 {component = CreateThinMultipole(angleIn); break;}
324 case ElementType::_ELEMENT:
325 {component = CreateElement(); break;}
326 case ElementType::_SOLENOID:
327 {component = CreateSolenoid(); break;}
328 case ElementType::_ECOL:
329 {component = CreateEllipticalCollimator(); break;}
330 case ElementType::_RCOL:
331 {component = CreateRectangularCollimator(); break;}
332 case ElementType::_TARGET:
333 {component = CreateTarget(); break;}
334 case ElementType::_JCOL:
335 {component = CreateJawCollimator(); break;}
336 case ElementType::_MUONSPOILER:
337 {component = CreateMuonSpoiler(); break;}
338 case ElementType::_SHIELD:
339 {component = CreateShield(); break;}
340 case ElementType::_DEGRADER:
341 {component = CreateDegrader(); break;}
342 case ElementType::_WIRESCANNER:
343 {component = CreateWireScanner(); break;}
344 case ElementType::_GAP:
345 {component = CreateGap(); break;}
346 case ElementType::_CRYSTALCOL:
347 {component = CreateCrystalCollimator(); break;}
348 case ElementType::_LASER:
349 {component = CreateLaser(); break;}
350 case ElementType::_SCREEN:
351 {component = CreateScreen(); break;}
352 case ElementType::_TRANSFORM3D:
353 {component = CreateTransform3D(); break;}
354 case ElementType::_THINRMATRIX:
355 {component = CreateThinRMatrix(angleIn, elementName); break;}
356 case ElementType::_PARALLELTRANSPORTER:
357 {component = CreateParallelTransporter(); break;}
358 case ElementType::_RMATRIX:
359 {component = CreateRMatrix(); break;}
360 case ElementType::_UNDULATOR:
361 {component = CreateUndulator(); break;}
362 case ElementType::_USERCOMPONENT:
363 {
365 {throw BDSException(__METHOD_NAME__, "no user component factory registered");}
366 G4String typeName = G4String(element->userTypeName);
368 {throw BDSException(__METHOD_NAME__, "no such component \"" + element->userTypeName + "\" registered.");}
369 else
370 {
371 component = userComponentFactory->ConstructComponent(typeName,
372 element,
375 currentArcLength);
376 }
377 break;
378 }
379 case ElementType::_DUMP:
380 {component = CreateDump(); break;}
381 case ElementType::_CT:
382#ifdef USE_DICOM
383 {component = CreateCT(); break;}
384#else
385 {throw BDSException(__METHOD_NAME__, "ct element can't be used - not compiled with dicom module!");}
386#endif
387 case ElementType::_AWAKESCREEN:
388#ifdef USE_AWAKE
389 {component = CreateAwakeScreen(); break;}
390#else
391 throw BDSException(__METHOD_NAME__, "Awake Screen can't be used - not compiled with AWAKE module!");
392#endif
393 case ElementType::_AWAKESPECTROMETER:
394#ifdef USE_AWAKE
395 {component = CreateAwakeSpectrometer(); break;}
396#else
397 {throw BDSException(__METHOD_NAME__, "Awake Spectrometer can't be used - not compiled with AWAKE module!");}
398#endif
399 // common types, but nothing to do here
400 case ElementType::_MARKER:
401 case ElementType::_LINE:
402 case ElementType::_REV_LINE:
403 {component = nullptr; break;}
404 default:
405 {
406 G4cerr << __METHOD_NAME__ << "unknown type " << element->type << G4endl;
407 throw BDSException(__METHOD_NAME__, "");
408 break;
409 }
410 }
411
412 // note this test will only be reached (and therefore the component registered)
413 // if both the component didn't exist and it has been constructed
414 if (component)
415 {
418 component->SetRegion(element->region);
419 // the minimum kinetic energy is only implemented in certain components
421
422 // infinite absorbers for collimators - must be done after SetMinimumKineticEnergy and
423 // specific to these elements. must be done before initialise too.
424 switch (element->type)
425 {
426 case ElementType::_ECOL:
427 case ElementType::_RCOL:
428 case ElementType::_JCOL:
429 {
430 if (BDSGlobalConstants::Instance()->CollimatorsAreInfiniteAbsorbers())
431 {component->SetMinimumKineticEnergy(std::numeric_limits<double>::max());}
432 break;
433 }
434 default:
435 {break;}
436 }
437
438 SetFieldDefinitions(element, component);
439 component->Initialise();
440 // register component and memory
441 BDSAcceleratorComponentRegistry::Instance()->RegisterComponent(component,differentFromDefinition);
442 }
443
444 return component;
445}
446
448 const G4double teleporterHorizontalWidth,
449 const G4Transform3D& transformIn)
450{
452 (*st)["length"] = teleporterLength; // convey length scale to integrator
453 BDSFieldInfo* vacuumFieldInfo = new BDSFieldInfo(BDSFieldType::teleporter,
454 brho,
455 BDSIntegratorType::teleporter,
456 st,
457 true,
458 transformIn);
459
460 G4cout << "---->creating Teleporter, "
461 << "l = " << teleporterLength/CLHEP::m << "m"
462 << G4endl;
463
464 return( new BDSTeleporter(teleporterLength, teleporterHorizontalWidth, vacuumFieldInfo));
465}
466
467BDSAcceleratorComponent* BDSComponentFactory::CreateDrift(G4double angleIn, G4double angleOut)
468{
470 {return nullptr;}
471
472 // Create normal vectors
473 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn, angleOut);
474
475 // current element tilt
476 G4double currentTilt = element->tilt * CLHEP::rad;
477 G4double prevTilt = 0;
478 G4double nextTilt = 0;
479 if (prevElement)
480 {prevTilt = prevElement->tilt * CLHEP::rad;}
481 if (nextElement)
482 {nextTilt = nextElement->tilt * CLHEP::rad;}
483 G4ThreeVector inputFaceNormal = faces.first.rotateZ(prevTilt - currentTilt);
484 G4ThreeVector outputFaceNormal = faces.second.rotateZ(nextTilt - currentTilt);
485
486 const G4double length = element->l*CLHEP::m;
487
488 // Beampipeinfo needed here to get aper1 for check.
489 BDSBeamPipeInfo* beamPipeInfo = PrepareBeamPipeInfo(element, inputFaceNormal,
490 outputFaceNormal);
491
492 const BDSExtent extent = beamPipeInfo->Extent();
493 G4bool facesWillIntersect = BDS::WillIntersect(inputFaceNormal, outputFaceNormal,
494 length, extent, extent);
495
496 if (facesWillIntersect)
497 {
498 G4cerr << __METHOD_NAME__ << "Drift \"" << elementName
499 << "\" between \"";
500 if (prevElement)
501 {G4cerr << prevElement->name;}
502 else
503 {G4cerr << "none";}
504 G4cerr << "\" and \"";
505 if (nextElement)
506 {G4cerr << nextElement->name;}
507 else
508 {G4cerr << "none";}
509 G4cerr << "\" is too short given its width and the angle of its faces." << G4endl;
510 throw BDSException(__METHOD_NAME__, "");
511 }
512
513 return (new BDSDrift(elementName,
514 length,
515 beamPipeInfo));
516}
517
518BDSAcceleratorComponent* BDSComponentFactory::CreateRF(G4double currentArcLength)
519{
521 {return nullptr;}
522
523 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::rfcavity);
524
525 BDSFieldType fieldType = BDSFieldType::rf; // simple sinusoidal E field only
526 if (!(element->fieldVacuum.empty()))
527 {
529 fieldType = field->FieldType();
530 }
531 // note cavity length is not the same as currentArcLength
532 G4double cavityLength = element->l * CLHEP::m;
533
534 // use cavity fringe option, includeFringeFields does not affect cavity fringes
535 G4bool buildCavityFringes = BDSGlobalConstants::Instance()->IncludeFringeFieldsCavities();
536
537 G4bool buildIncomingFringe = buildCavityFringes;
538 // only check if trying to build fringes to begin with as this check should only ever turn off fringe building
539 if (prevElement && buildIncomingFringe) // could be nullptr
540 {// only build fringe if previous element isn't another cavity
541 buildIncomingFringe = prevElement->type != ElementType::_RF;
542 }
543
544 G4bool buildOutgoingFringe = buildCavityFringes;
545 // only check if trying to build fringes to begin with as this check should only ever turn off fringe building
546 if (nextElement && buildOutgoingFringe) // could be nullptr
547 {// only build fringe if next element isn't another cavity
548 buildOutgoingFringe = nextElement->type != ElementType::_RF;
549 }
550
551 if (buildIncomingFringe)
552 {cavityLength -= thinElementLength;}
553 if (buildOutgoingFringe)
554 {cavityLength -= thinElementLength;}
555
556 // supply currentArcLength (not element length) to strength as its needed
557 // for time offset from s=0 position
558 BDSMagnetStrength* stIn = nullptr;
559 BDSMagnetStrength* stOut = nullptr;
560 BDSMagnetStrength* st = PrepareCavityStrength(element, cavityLength, currentArcLength, stIn, stOut);
561 G4Transform3D fieldTrans = CreateFieldTransform(element);
562 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
563 brho,
564 intType,
565 st,
566 true,
567 fieldTrans);
568
569 // limit step length in field - crucial to this component
570 // to get the motion correct this has to be less than one oscillation.
571 // Don't set if frequency is zero as the field will have no oscillation, so we can integrate
572 // safely over longer steps without the field changing.
574 {
575 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
576 G4double stepFraction = 0.025;
577 G4double period = 1. / (element->frequency*CLHEP::hertz);
578 // choose the smallest length scale based on the length of the component of the distance
579 // travelled in one period - so improved for high frequency fields
580 G4double limit = std::min((*st)["length"], CLHEP::c_light*period) * stepFraction;
581 auto ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
582 if (ul != defaultUL)
583 {vacuumField->SetUserLimits(ul);}
584 }
585
586 BDSCavityInfo* cavityInfo = PrepareCavityModelInfo(element, (*st)["frequency"]);
587
588 // update 0 point of field with geometry
589 (*st)["equatorradius"] = cavityInfo->equatorRadius;
590 G4Material* vacuumMaterial = PrepareVacuumMaterial(element);
591
592 // aperture radius. Default is beam pipe radius / aper1 if a cavity model isn't specified.
593 G4double cavityApertureRadius = cavityInfo->irisRadius;
594
595 if (!BDS::IsFinite((*st)["efield"]) || !buildCavityFringes)
596 {// ie no rf field - don't bother with fringe effects
597 delete stIn;
598 delete stOut;
599 return new BDSCavityElement(elementName,
600 cavityLength,
601 vacuumMaterial,
602 vacuumField,
603 cavityInfo);
604 }
605
606 BDSLine* cavityLine = new BDSLine(elementName);
607
608 if (buildIncomingFringe)
609 {
610 //BDSMagnetStrength* stIn = PrepareCavityFringeStrength(element, cavityLength, currentArcLength, true);
611 // update with info for fringe matrix elements
612 (*stIn)["rmat11"] = 1;
613 (*stIn)["rmat21"] = 0;
614 (*stIn)["rmat22"] = 1;
615 (*stIn)["rmat33"] = 1;
616 (*stIn)["rmat43"] = 0;
617 (*stIn)["rmat44"] = 1;
618 (*stIn)["length"] = BDSGlobalConstants::Instance()->ThinElementLength();
619 (*stIn)["isentrance"] = true;
620 auto cavityFringeIn = CreateCavityFringe(0, stIn, elementName + "_fringe_in", cavityApertureRadius);
621 cavityLine->AddComponent(cavityFringeIn);
622 }
623 else
624 {delete stIn;}
625
626 auto cavity = new BDSCavityElement(elementName,
627 cavityLength,
628 vacuumMaterial,
629 vacuumField,
630 cavityInfo);
631
632 cavityLine->AddComponent(cavity);
633
634 if (buildOutgoingFringe)
635 {
636 //BDSMagnetStrength* stOut = PrepareCavityFringeStrength(element, cavityLength, currentArcLength, false);
637 // update with info for fringe matrix elements
638 (*stOut)["rmat11"] = 1;
639 (*stOut)["rmat21"] = 0;
640 (*stOut)["rmat22"] = 1;
641 (*stOut)["rmat33"] = 1;
642 (*stOut)["rmat43"] = 0;
643 (*stOut)["rmat44"] = 1;
644 (*stOut)["length"] = BDSGlobalConstants::Instance()->ThinElementLength();
645 (*stOut)["isentrance"] = false;
646 auto cavityFringeIn = CreateCavityFringe(0, stOut, elementName + "_fringe_out", cavityApertureRadius);
647 cavityLine->AddComponent(cavityFringeIn);
648 }
649 else
650 {delete stOut;}
651
652 return cavityLine;
653}
654
655BDSAcceleratorComponent* BDSComponentFactory::CreateSBend()
656{
658 {return nullptr;}
659
661
662 // don't check here on whether the possibly next / previous sbend will clash with
663 // pole face angles - let that be checked after element construction in the beamline
665 SetBeta0(st);
666 G4double angle = 0;
667 G4double field = 0;
669 (*st)["angle"] = angle;
670 (*st)["field"] = field*element->scaling;
671 (*st)["by"] = 1;// bx,by,bz is unit field direction, so (0,1,0) here
672 (*st)["length"] = element->l * CLHEP::m; // arc length
673 (*st)["scaling"]= element->scaling;
674
675 // quadrupole component
677 {(*st)["k1"] = element->scaling*element->k1;}
678
679#ifdef BDSDEBUG
680 G4cout << "Angle (rad) " << (*st)["angle"] / CLHEP::rad << G4endl;
681 G4cout << "Field (T) " << (*st)["field"] / CLHEP::tesla << G4endl;
682#endif
683 // geometric face angles (can be different from specification depending on integrator set used)
684 G4double incomingFaceAngle = IncomingFaceAngle(element);
685 G4double outgoingFaceAngle = OutgoingFaceAngle(element);
686
688 incomingFaceAngle, outgoingFaceAngle,
690
691 return sBendLine;
692}
693
694BDSAcceleratorComponent* BDSComponentFactory::CreateRBend()
695{
697 {return nullptr;}
698
700
701 // don't check here on whether the possibly next / previous sbend will clash with
702 // pole face angles - let that be checked after element construction in the beamline
703
705 SetBeta0(st);
706 G4double arcLength = 0, chordLength = 0, field = 0, angle = 0;
707 CalculateAngleAndFieldRBend(element, arcLength, chordLength, field, angle);
708
709 (*st)["angle"] = angle;
710 (*st)["field"] = field * element->scaling;
711 (*st)["by"] = 1;// bx,by,bz is unit field direction, so (0,1,0) here
712 (*st)["length"] = arcLength;
713 (*st)["scaling"]= element->scaling;
714
715 // Quadrupole component
717 {(*st)["k1"] = element->scaling * element->k1;}
718
719 // geometric face angles (can be different from specification depending on integrator set used)
720 G4double incomingFaceAngle = IncomingFaceAngle(element);
721 G4double outgoingFaceAngle = OutgoingFaceAngle(element);
722
723 // Check the faces won't overlap due to too strong an angle with too short a magnet
724 auto bp = PrepareBeamPipeInfo(element);
726 -incomingFaceAngle, -outgoingFaceAngle,
727 bp, element->yokeOnInside);
728 CheckBendLengthAngleWidthCombo(arcLength, (*st)["angle"], oiCheck->MinimumIntersectionRadiusRequired(), elementName);
729 delete oiCheck;
730 delete bp;
731
732 // the above in / out face angles are not w.r.t. the local coords - subtract angle/2 to convert
733 // this may seem like undoing the += in the functions, but they're used for the beam pipes
734 // and matching.
735 incomingFaceAngle -= 0.5*angle;
736 outgoingFaceAngle -= 0.5*angle;
737
739 brho, st, integratorSet,
740 incomingFaceAngle, outgoingFaceAngle,
742 return rbendline;
743}
745 G4double& vkick,
746 const KickerType type) const
747{
748 G4bool kickFinite = BDS::IsFinite(element->kick);
749 switch (type)
750 {
751 case KickerType::horizontal:
752 {
753 hkick = kickFinite ? element->kick : element->hkick;
754 // backwards compatibility - if both are zero but angle if finite
755 // for this element - use that.
756 if (!BDS::IsFinite(hkick) && BDS::IsFinite(element->angle))
757 {hkick = element->angle;} //+ve to match hkick definition
758 vkick = 0;
759 break;
760 }
761 case KickerType::vertical:
762 {
763 vkick = kickFinite ? element->kick : element->vkick;
764 // backwards compatibility - if both are zero but angle if finite
765 // for this element - use that.
766 if (!BDS::IsFinite(vkick) && BDS::IsFinite(element->angle))
767 {vkick = element->angle;} //+ve to match vkick definition
768 hkick = 0;
769 break;
770 }
771 case KickerType::general:
772 {
773 hkick = element->hkick;
774 vkick = element->vkick;
775 // element->kick will be ignored
777 {BDS::Warning(__METHOD_NAME__, "'kick' parameter defined in element \"" + elementName + "\" but will be ignored as general kicker.");}
778 }
779 default:
780 {break;}
781 }
782}
783
784BDSAcceleratorComponent* BDSComponentFactory::CreateKicker(KickerType type)
785{
786 const G4String baseName = elementName;
788 SetBeta0(st);
789 BDSFieldType fieldType = BDSFieldType::dipole;
790 BDSIntegratorType intType = BDSIntegratorType::g4classicalrk4; // default
791 G4double chordLength;
792 G4double scaling = element->scaling;
793 G4double hkick = 0;
794 G4double vkick = 0;
795 GetKickValue(hkick, vkick, type);
796 (*st)["scaling"] = scaling; // needed in kicker fringes otherwise default is zero
797 (*st)["hkick"] = scaling * hkick;
798 (*st)["vkick"] = scaling * vkick;
799
800 // create fringe magnet strengths. Copies supplied strength object so it should contain all
801 // the kicker strength information as well as the added fringe information
802 BDSMagnetStrength* fringeStIn = BDS::GetFringeMagnetStrength(element,
803 st,
804 0,
805 element->e1,
806 element->e2,
807 element->fintx,
808 true);
809 BDSMagnetStrength* fringeStOut = new BDSMagnetStrength(*fringeStIn);
810 (*fringeStOut)["isentrance"] = false;
811
812 // check if the fringe effect is finite
813 G4bool finiteEntrFringe = false;
814 G4bool finiteExitFringe = false;
815 if (BDS::IsFinite(BDS::FringeFieldCorrection(fringeStIn, true)) ||
817 {finiteEntrFringe = true;}
818 if (BDS::IsFinite(BDS::FringeFieldCorrection(fringeStOut, true)) ||
820 {finiteExitFringe = true;}
821
822 // only build the fringe elements if the poleface rotation or fringe field correction terms are finite
823 G4bool buildEntranceFringe = false;
824 G4bool buildExitFringe = false;
825 if (BDS::IsFinite(element->e1) || finiteEntrFringe)
826 {buildEntranceFringe = true;}
827 if (BDS::IsFinite(element->e2) || finiteExitFringe)
828 {buildExitFringe = true;}
830 {
831 buildEntranceFringe = false;
832 buildExitFringe = false;
833 }
834
835 if (!HasSufficientMinimumLength(element, false)) // false for don't print warning
836 {// thin kicker
837 fieldType = BDSFieldType::bfieldzero;
838 intType = BDSIntegratorType::kickerthin;
839 chordLength = thinElementLength;
840
841 // Fringe and poleface effects for a thin kicker require an effective bending radius, rho.
842 // Lack of length and angle knowledge means the field is the only way rho can be calculated.
843 // A zero field would lead to div by zero errors in the integrator constructor, therefore
844 // do not replace the kicker magnetstrength with the fringe magnetstrength which should prevent
845 // any fringe/poleface effects from ever being applied.
846 if (!BDS::IsFinite(element->B))
847 {
848 // only print warning if a poleface or fringe field effect was specified
849 if (buildEntranceFringe || buildExitFringe)
850 {
851 G4cout << __METHOD_NAME__ << "WARNING - finite B field required for kicker pole face and fringe fields,"
852 " effects are unavailable for element ""\"" << elementName << "\"." << G4endl;
853 }
854 buildEntranceFringe = false;
855 buildExitFringe = false;
856 }
857 // A thin kicker or tkicker element has possible hkick and vkick combination, meaning the
858 // field direction cannot be assumed. Therefore we are unsure of poleface angle and fringe
859 // effects so don't replace the kicker magnetstrength with the fringe magnetstrength
860 else if (type == KickerType::general)
861 {
862 // only print warning if a poleface or fringe field effect was specified
863 if (buildEntranceFringe || buildExitFringe)
864 {
865 G4cerr << __METHOD_NAME__ << " Poleface and fringe field effects are unavailable "
866 << "for the thin (t)kicker element ""\"" << elementName << "\"." << G4endl;
867 }
868 buildEntranceFringe = false;
869 buildExitFringe = false;
870 }
871 // Good to apply fringe effects.
872 else
873 {
874 // overwrite magnet strength with copy of fringe strength. Should be safe as it has the
875 // kicker information from copying previously.
876 delete st;
877 st = new BDSMagnetStrength(*fringeStIn);
878 // supply the scaled field for a thin kicker field as it is required to calculate
879 // the effective bending radius needed for fringe field and poleface effects
880 (*st)["field"] = element->B * CLHEP::tesla * scaling;
881 (*fringeStIn) ["field"] = (*st)["field"];
882 (*fringeStOut) ["field"] = (*st)["field"];
883
884 // set field for vertical kickers - element needs rotating as poleface rotation is assumed
885 // to be about the vertical axis unless explicitly tilted.
886 if (type == KickerType::vertical)
887 {(*st)["bx"] = 1;}
888 else
889 {(*st)["by"] = 1;}
890 }
891 }
892 else
893 {// thick kicker
894 chordLength = element->l*CLHEP::m;
895 // sin(angle) = dP -> angle = sin^-1(dP)
896 G4double angleX = std::asin(hkick * scaling);
897 G4double angleY = std::asin(vkick * scaling);
898
899 if (std::isnan(angleX))
900 {throw BDSException(__METHOD_NAME__, "hkick too strong for element \"" + element->name + "\" ");}
901 if (std::isnan(angleY))
902 {throw BDSException(__METHOD_NAME__, "vkick too strong for element \"" + element->name + "\" ");}
903
904 // Setup result variables - 'x' and 'y' refer to the components along the direction
905 // the particle will change. These will therefore not be Bx and By.
906 G4double fieldX = 0;
907 G4double fieldY = 0;
908
909 // if B is specified and hkick and vkick (including backwards compatible check on
910 // 'angle') are not, then use the field for the appropriate component
911 // can only be 1d in this case -> doesn't work for tkicker
912 if (BDS::IsFinite(element->B) && (!BDS::IsFinite(hkick) && !BDS::IsFinite(vkick)))
913 {
914 switch (type)
915 {// 'X' and 'Y' are the angle of bending here, not the B field direction.
916 case KickerType::horizontal:
917 case KickerType::general:
918 {fieldX = element->B * CLHEP::tesla * scaling; break;}
919 case KickerType::vertical:
920 {fieldY = element->B * CLHEP::tesla * scaling; break;}
921 default:
922 {break;} // do nothing - no field - just for compiler warnings
923 }
924 }
925 else
926 {
927 if (BDS::IsFinite(angleX))
928 {// with comments
929 // calculate the chord length of the arc through the field from the straight
930 // ahead length for this element which here is 'chordLength'.
931 G4double fieldChordLengthX = chordLength / std::cos(0.5*angleX);
932
933 // now calculate the bending radius
934 G4double bendingRadiusX = fieldChordLengthX * 0.5 / sin(std::abs(angleX) * 0.5);
935
936 // no calculate the arc length of the trajectory based on each bending radius
937 G4double arcLengthX = std::abs(bendingRadiusX * angleX);
938
939 // -ve here in horizontal only for convention matching
940 fieldX = FieldFromAngle(-angleX, arcLengthX);
941 } // else fieldX default is 0
942
943 if (BDS::IsFinite(angleY))
944 {// same as x, no need for comments
945 G4double fieldChordLengthY = chordLength / std::cos(0.5*angleY);
946 G4double bendingRadiusY = fieldChordLengthY * 0.5 / sin(std::abs(angleY) * 0.5);
947 G4double arcLengthY = std::abs(bendingRadiusY * angleY);
948 fieldY = FieldFromAngle(angleY, arcLengthY);
949 }
950 }
951
952 // note field for kick in x is unit Y, hence B = (y,x,0) here
953 // field calculated from scaled angle so no need to scale field here
954 G4ThreeVector field = G4ThreeVector(fieldY, fieldX, 0);
955 G4double fieldMag = field.mag();
956 G4ThreeVector unitField = field.unit();
957
958 (*st)["field"] = fieldMag;
959 (*st)["bx"] = unitField.x();
960 (*st)["by"] = unitField.y();
961
962 // preserve sign of field strength for fringe integrators
963 // needed to calculate correct value of rho
964 (*fringeStIn)["field"] = (*st)["field"];
965 (*fringeStOut)["field"] = (*st)["field"];
966 if (fieldX < 0 || fieldY < 0)
967 {
968 (*fringeStIn)["field"] *= -1;
969 (*fringeStOut)["field"] *= -1;
970 }
971 }
972
973 (*fringeStIn) ["bx"] = (*st)["bx"];
974 (*fringeStIn) ["by"] = (*st)["by"];
975 (*fringeStOut)["bx"] = (*st)["bx"];
976 (*fringeStOut)["by"] = (*st)["by"];
977
979 G4double defaultVHRatio = 1.5;
980 switch (type)
981 {
982 case KickerType::horizontal:
983 case KickerType::general:
984 {t = BDSMagnetType::hkicker; break;}
985 case KickerType::vertical:
986 {
987 t = BDSMagnetType::vkicker;
988 defaultVHRatio = 1./defaultVHRatio; // inverted for vertical magnet
989 break;
990 }
991 default:
992 {t = BDSMagnetType::hkicker; break;}
993 }
994
995 G4Transform3D fieldTrans = CreateFieldTransform(element);
996 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
997 brho,
998 intType,
999 st,
1000 true,
1001 fieldTrans);
1002
1003 G4bool yokeOnLeft = YokeOnLeft(element, st);
1004 auto bpInf = PrepareBeamPipeInfo(element);
1005
1006 // Decide on a default horizontalWidth for the kicker - try 0.3x ie smaller kicker
1007 // than typical magnet, but if that would not fit around the beam pipe - go back to
1008 // the default horizontalWidth. Code further along will warn if it still doesn't fit.
1009 const G4double globalDefaultHW = BDSGlobalConstants::Instance()->HorizontalWidth();
1010 G4double defaultHorizontalWidth = 0.3 * globalDefaultHW;
1011 BDSExtent bpExt = bpInf->Extent();
1012 G4double bpDX = bpExt.DX();
1013 G4double bpDY = bpExt.DY();
1014 if (bpDX > defaultHorizontalWidth && bpDX < globalDefaultHW)
1015 {defaultHorizontalWidth = globalDefaultHW;}
1016 else if (bpDY > defaultHorizontalWidth && bpDY > globalDefaultHW)
1017 {defaultHorizontalWidth = globalDefaultHW;}
1018
1019 auto magOutInf = PrepareMagnetOuterInfo(elementName, element, 0, 0, bpInf, yokeOnLeft,
1020 defaultHorizontalWidth, defaultVHRatio, 0.9);
1021
1022 BDSFieldInfo* outerField = nullptr;
1023 G4bool externalOuterField = !(element->fieldOuter.empty());
1024 if (yokeFields && !externalOuterField)
1025 {
1026 outerField = PrepareMagnetOuterFieldInfo(st,
1027 fieldType,
1028 bpInf,
1029 magOutInf,
1030 fieldTrans,
1032 brho,
1033 ScalingFieldOuter(element));
1034 }
1035
1037 {
1038 delete fringeStIn;
1039 delete fringeStOut;
1040 // fringe effect applied in integrator so nothing more to do.
1041 // no outer field as thin component here
1042 return new BDSMagnet(t,
1043 baseName,
1044 chordLength,
1045 bpInf,
1046 magOutInf,
1047 vacuumField,
1048 0, nullptr, // default values for optional args (angle, outerFieldInfo)
1049 true); // isThin
1050 }
1051 else
1052 {
1053 BDSLine* kickerLine = new BDSLine(baseName);
1054 // subtract fringe length from kicker to preserve element length
1055 G4double kickerChordLength = chordLength;
1056 if (buildEntranceFringe)
1057 {kickerChordLength -= thinElementLength;}
1058 if (buildExitFringe)
1059 {kickerChordLength -= thinElementLength;}
1060
1061 if (buildEntranceFringe)
1062 {
1063 G4String entrFringeName = baseName + "_e1_fringe";
1064 BDSMagnet* startfringe = BDS::BuildDipoleFringe(element, 0, 0,
1065 entrFringeName,
1066 fringeStIn,
1067 brho,
1069 fieldType);
1070 kickerLine->AddComponent(startfringe);
1071 }
1072
1073 G4String kickerName = baseName;
1074 BDSMagnet* kicker = new BDSMagnet(t,
1075 kickerName,
1076 kickerChordLength,
1077 bpInf,
1078 magOutInf,
1079 vacuumField,
1080 0,
1081 outerField);
1082 kickerLine->AddComponent(kicker);
1083
1084 if (buildEntranceFringe)
1085 {
1086 G4String exitFringeName = baseName + "_e2_fringe";
1087 BDSMagnet* endfringe = BDS::BuildDipoleFringe(element, 0, 0,
1088 exitFringeName,
1089 fringeStOut, brho,
1090 integratorSet, fieldType);
1091 kickerLine->AddComponent(endfringe);
1092 }
1093 return kickerLine;
1094 }
1095}
1096
1097BDSAcceleratorComponent* BDSComponentFactory::CreateQuad()
1098{
1100 {return nullptr;}
1101
1103 SetBeta0(st);
1104 (*st)["k1"] = element->k1 * element->scaling;
1105
1106 return CreateMagnet(element, st, BDSFieldType::quadrupole, BDSMagnetType::quadrupole);
1107}
1108
1109BDSAcceleratorComponent* BDSComponentFactory::CreateSextupole()
1110{
1112 {return nullptr;}
1113
1115 SetBeta0(st);
1116 (*st)["k2"] = element->k2 * element->scaling;
1117
1118 return CreateMagnet(element, st, BDSFieldType::sextupole, BDSMagnetType::sextupole);
1119}
1120
1121BDSAcceleratorComponent* BDSComponentFactory::CreateOctupole()
1122{
1124 {return nullptr;}
1125
1127 SetBeta0(st);
1128 (*st)["k3"] = element->k3 * element->scaling;
1129
1130 return CreateMagnet(element, st, BDSFieldType::octupole, BDSMagnetType::octupole);
1131}
1132
1133BDSAcceleratorComponent* BDSComponentFactory::CreateDecapole()
1134{
1136 {return nullptr;}
1137
1139 SetBeta0(st);
1140 (*st)["k4"] = element->k4 * element->scaling;
1141
1142 return CreateMagnet(element, st, BDSFieldType::decapole, BDSMagnetType::decapole);
1143}
1144
1145BDSAcceleratorComponent* BDSComponentFactory::CreateMultipole()
1146{
1148 {return nullptr;}
1149
1151
1152 return CreateMagnet(element, st, BDSFieldType::multipole, BDSMagnetType::multipole,
1153 (*st)["angle"]); // multipole could bend beamline
1154}
1155
1156BDSAcceleratorComponent* BDSComponentFactory::CreateThinMultipole(G4double angleIn)
1157{
1159 BDSBeamPipeInfo* beamPipeInfo = PrepareBeamPipeInfo(element, angleIn, -angleIn);
1160 beamPipeInfo->beamPipeType = BDSBeamPipeType::circularvacuum;
1162 -angleIn, angleIn, beamPipeInfo);
1163 magnetOuterInfo->geometryType = BDSMagnetGeometryType::none;
1164
1165 BDSIntegratorType intType = integratorSet->multipoleThin;
1166 G4Transform3D fieldTrans = CreateFieldTransform(element);
1167 BDSFieldInfo* vacuumField = new BDSFieldInfo(BDSFieldType::multipole,
1168 brho,
1169 intType,
1170 st,
1171 true,
1172 fieldTrans);
1173
1174 BDSMagnet* thinMultipole = new BDSMagnet(BDSMagnetType::thinmultipole,
1177 beamPipeInfo,
1178 magnetOuterInfo,
1179 vacuumField,
1180 0, nullptr, // default values for optional args (angle, outerFieldInfo)
1181 true); // isThin
1182
1183 thinMultipole->SetExtent(BDSExtent(beamPipeInfo->aper1,
1184 beamPipeInfo->aper1,
1185 thinElementLength*0.5));
1186
1187 return thinMultipole;
1188}
1189
1190BDSAcceleratorComponent* BDSComponentFactory::CreateElement()
1191{
1193 {throw BDSException(__METHOD_NAME__, "insufficient length for element \"" + element->name + "\" - must specify a suitable length");}
1194
1195 // we don't specify the field explicitly here - this is done generically
1196 // in the main CreateComponent method with SetFieldDefinitions.
1197 std::vector<G4String> vacuumBiasVolumeNames = BDS::SplitOnWhiteSpace(G4String(element->namedVacuumVolumes));
1198 return (new BDSElement(elementName,
1199 element->l * CLHEP::m,
1202 element->angle * CLHEP::rad,
1203 &vacuumBiasVolumeNames,
1205 element->markAsCollimator,
1207}
1208
1209BDSAcceleratorComponent* BDSComponentFactory::CreateSolenoid()
1210{
1212 {return nullptr;}
1213
1215 SetBeta0(st);
1216 (*st)["bz"] = 1;
1217 (*st)["length"] = element->l * CLHEP::m * 0.8; // arbitrary fraction of 0.7 for current length of full length
1218 const G4double scaling = element->scaling;
1219 if (BDS::IsFinite(element->B))
1220 {
1221 (*st)["field"] = scaling * element->B * CLHEP::tesla;
1222 (*st)["ks"] = (*st)["field"] / brho;
1223 }
1224 else
1225 {
1226 (*st)["field"] = (scaling * element->ks / CLHEP::m) * brho;
1227 (*st)["ks"] = element->ks;
1228 }
1229
1230 if (!BDS::IsFinite((*st)["field"]) || !includeFringeFields)
1231 {// ie no strength solenoid - don't bother with fringe effects
1232 return CreateMagnet(element, st, BDSFieldType::solenoid, BDSMagnetType::solenoid);
1233 }
1234
1235 // lambda to help - sign convention - this is the 'entry' version
1236 auto strength = [](G4double phi){
1238 (*s)["rmat11"] = 1;
1239 (*s)["rmat22"] = 1;
1240 (*s)["rmat33"] = 1;
1241 (*s)["rmat44"] = 1;
1242 (*s)["rmat41"] = -phi;
1243 (*s)["rmat23"] = phi;
1244 return s;
1245 };
1246
1247 G4bool buildIncomingFringe = true;
1248 if (prevElement) // could be nullptr
1249 {// only build fringe if previous element isn't another solenoid
1250 buildIncomingFringe = prevElement->type != ElementType::_SOLENOID;
1251 }
1252 G4bool buildOutgoingFringe = true;
1253 if (nextElement) // could be nullptr
1254 {// only build fringe if next element isn't another solenoid
1255 buildOutgoingFringe = nextElement->type != ElementType::_SOLENOID;
1256 }
1257
1258 G4double solenoidBodyLength = element->l * CLHEP::m;
1259
1260 if (buildIncomingFringe)
1261 {solenoidBodyLength -= thinElementLength;}
1262 if (buildOutgoingFringe)
1263 {solenoidBodyLength -= thinElementLength;}
1264
1265 // scale factor to account for reduced body length due to fringe placement.
1266 G4double lengthScaling = solenoidBodyLength / (element->l * CLHEP::m);
1267 G4double s = 0.5*(*st)["ks"] * lengthScaling; // already includes scaling
1268 BDSLine* bLine = new BDSLine(elementName);
1269
1270 if (buildIncomingFringe)
1271 {
1272 auto stIn = strength(s);
1273 auto solenoidIn = CreateThinRMatrix(0, stIn, elementName + "_fringe_in");
1274 bLine->AddComponent(solenoidIn);
1275 }
1276
1277 // Do not use CreateMagnet method as solenoid body length needs to be reduced to conserve total
1278 // element length. The solenoid strength is scaled accordingly.
1279
1281 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::solenoid);
1282 G4Transform3D fieldTrans = CreateFieldTransform(element);
1283 BDSFieldInfo* vacuumField = new BDSFieldInfo(BDSFieldType::solenoid,
1284 brho,
1285 intType,
1286 st,
1287 true,
1288 fieldTrans);
1289
1290 BDSMagnetOuterInfo* outerInfo = PrepareMagnetOuterInfo(elementName + "_centre", element, st, bpInfo);
1291 vacuumField->SetScalingRadius(outerInfo->innerRadius); // purely for completeness of information - not required
1292 BDSFieldInfo* outerField = nullptr;
1293
1294 // only make a default multipolar field if the yokeFields flag is on and
1295 // there isn't an 'outerField' specified for the element
1296 G4bool externalOuterField = !(element->fieldOuter.empty());
1297 if (yokeFields && !externalOuterField)
1298 {
1299 outerField = PrepareMagnetOuterFieldInfo(st,
1300 BDSFieldType::solenoid,
1301 bpInfo,
1302 outerInfo,
1303 fieldTrans,
1305 brho,
1306 ScalingFieldOuter(element));
1307
1308 // determine a suitable radius for the current carrying coil of the solenoid
1309 // this defines the field geometry
1310 // there is no coil in our geometry so it is a bit fictional
1311 G4double beamPipeRadius = bpInfo->IndicativeRadius();
1312 G4double outerRadius = outerInfo->horizontalWidth * 0.5;
1313 G4double coilRadius = beamPipeRadius + 0.25*(outerRadius - beamPipeRadius);
1314 outerField->SetScalingRadius(coilRadius);
1315 }
1316
1317 auto solenoid = new BDSMagnet(BDSMagnetType::solenoid,
1319 solenoidBodyLength,
1320 bpInfo,
1321 outerInfo,
1322 vacuumField,
1323 0,
1324 outerField);
1325
1326 bLine->AddComponent(solenoid);
1327
1328 if (buildOutgoingFringe)
1329 {
1330 auto stOut = strength(-s);
1331 auto solenoidOut = CreateThinRMatrix(0, stOut, elementName + "_fringe_out");
1332 bLine->AddComponent(solenoidOut);
1333 }
1334
1335 return bLine;
1336}
1337
1338BDSAcceleratorComponent* BDSComponentFactory::CreateParallelTransporter()
1339{
1341 return CreateMagnet(element, st, BDSFieldType::paralleltransporter, BDSMagnetType::paralleltransporter);
1342}
1343
1344BDSAcceleratorComponent* BDSComponentFactory::CreateRectangularCollimator()
1345{
1347 {return nullptr;}
1348 G4bool circularOuter = false;
1349 G4String apertureType = G4String(element->apertureType);
1350 if (apertureType == "circular")
1351 {circularOuter = true;}
1353 element->l*CLHEP::m,
1357 element->xsize*CLHEP::m,
1358 element->ysize*CLHEP::m,
1359 element->xsizeOut*CLHEP::m,
1360 element->ysizeOut*CLHEP::m,
1362 circularOuter);
1363}
1364
1365BDSAcceleratorComponent* BDSComponentFactory::CreateTarget()
1366{
1368 {return nullptr;}
1369 G4bool circularOuter = false;
1370 G4String apertureType = G4String(element->apertureType);
1371 if (apertureType == "circular")
1372 {circularOuter = true;}
1373 return new BDSTarget(elementName,
1374 element->l*CLHEP::m,
1378 circularOuter);
1379}
1380
1381BDSAcceleratorComponent* BDSComponentFactory::CreateEllipticalCollimator()
1382{
1384 {return nullptr;}
1385
1386 G4bool circularOuter = false;
1387 G4String apertureType = G4String(element->apertureType);
1388 if (apertureType == "circular")
1389 {circularOuter = true;}
1391 element->l*CLHEP::m,
1395 element->xsize*CLHEP::m,
1396 element->ysize*CLHEP::m,
1397 element->xsizeOut*CLHEP::m,
1398 element->ysizeOut*CLHEP::m,
1400 circularOuter);
1401}
1402
1403BDSAcceleratorComponent* BDSComponentFactory::CreateJawCollimator()
1404{
1406 {return nullptr;}
1407
1408 return new BDSCollimatorJaw(elementName,
1409 element->l*CLHEP::m,
1411 element->xsize*CLHEP::m,
1412 element->ysize*CLHEP::m,
1413 element->xsizeLeft*CLHEP::m,
1414 element->xsizeRight*CLHEP::m,
1415 true,
1416 true,
1420}
1421
1422BDSAcceleratorComponent* BDSComponentFactory::CreateMuonSpoiler()
1423{
1425 {return nullptr;}
1426
1427 G4double elLength = element->l*CLHEP::m;
1428 BDSFieldInfo* outerField = nullptr;
1429 if (BDS::IsFinite(element->B))
1430 {
1432 (*st)["field"] = element->scaling * element->B * CLHEP::tesla;
1433 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::muonspoiler);
1434 G4Transform3D fieldTrans = CreateFieldTransform(element);
1435 outerField = new BDSFieldInfo(BDSFieldType::muonspoiler,
1436 brho,
1437 intType,
1438 st,
1439 true,
1440 fieldTrans);
1441
1442 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
1443 G4double limit = elLength / 20.0;
1444 auto ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
1445 if (ul != defaultUL)
1446 {outerField->SetUserLimits(ul);}
1447 }
1448 auto bpInfo = PrepareBeamPipeInfo(element);
1449
1450 return new BDSMagnet(BDSMagnetType::muonspoiler,
1452 elLength,
1453 bpInfo,
1454 PrepareMagnetOuterInfo(elementName, element, 0, 0, bpInfo), // 0 angled face in and out
1455 nullptr,
1456 0,
1457 outerField);
1458}
1459
1460BDSAcceleratorComponent* BDSComponentFactory::CreateShield()
1461{
1463 {return nullptr;}
1464
1466
1467 G4Colour* colour = PrepareColour(element);
1468 G4Material* material = PrepareMaterial(element, "concrete");
1469
1470 BDSShield* shield = new BDSShield(elementName,
1471 element->l*CLHEP::m,
1473 element->xsize*CLHEP::m,
1474 element->ysize*CLHEP::m,
1475 material,
1476 colour,
1477 bpInfo);
1478 return shield;
1479}
1480
1481BDSAcceleratorComponent* BDSComponentFactory::CreateDegrader()
1482{
1484 {return nullptr;}
1485
1486 G4double degraderOffset;
1487 if ((element->materialThickness < 0) && (element->degraderOffset < 0))
1488 {throw BDSException(__METHOD_NAME__, "both \"materialThickness\" and \"degraderOffset\" are either undefined or < 0");}
1489 if (element->degraderOffset < 0)
1490 {throw BDSException(__METHOD_NAME__, "\"degraderOffset\" cannot be < 0");}
1492 {throw BDSException(__METHOD_NAME__, "\"materialThickness\" cannot be greater than the element length");}
1493
1494 if ((element->materialThickness <= 0) && (element->degraderOffset >= 0))
1495 {degraderOffset = element->degraderOffset*CLHEP::m;}
1496 else
1497 {
1498 //Width of wedge base
1499 G4double wedgeBasewidth = (element->l*CLHEP::m /element->numberWedges) - lengthSafety;
1500
1501 //Angle between hypotenuse and height (in the triangular wedge face)
1502 G4double theta = std::atan(wedgeBasewidth / (2.0*element->wedgeLength*CLHEP::m));
1503
1504 //Overlap distance of wedges
1505 G4double thicknessPerWedge = (wedgeBasewidth - element->materialThickness*CLHEP::m/element->numberWedges);
1506 degraderOffset = (0.5*thicknessPerWedge) * (std::sin(CLHEP::halfpi - theta) / std::sin(theta));
1507 }
1508
1509 // include base thickness in each wedge so it covers the whole beam aperture when set to the thickest
1510 // possible amount of material, otherwise a fraction of the beam wouldn't pass through the wedges.
1511 auto bpi = PrepareBeamPipeInfo(element);
1512 G4double baseWidth = bpi->aper1;
1513 delete bpi;
1514
1515 return (new BDSDegrader(elementName,
1516 element->l*CLHEP::m,
1519 element->wedgeLength*CLHEP::m,
1520 element->degraderHeight*CLHEP::m,
1521 degraderOffset,
1522 baseWidth,
1526}
1527
1528BDSAcceleratorComponent* BDSComponentFactory::CreateWireScanner()
1529{
1531 {return nullptr;}
1532
1534 {throw BDSException(__METHOD_NAME__, "\"angle\" parameter set for wirescanner \"" + elementName + "\" but this should not be set. Please unset and use \"wireAngle\".");}
1535
1536 G4ThreeVector wireOffset = G4ThreeVector(element->wireOffsetX * CLHEP::m,
1537 element->wireOffsetY * CLHEP::m,
1538 element->wireOffsetZ * CLHEP::m);
1539
1540 return (new BDSWireScanner(elementName,
1541 element->l*CLHEP::m,
1544 element->wireDiameter*CLHEP::m,
1545 element->wireLength*CLHEP::m,
1546 element->wireAngle*CLHEP::rad,
1547 wireOffset));
1548}
1549
1550BDSAcceleratorComponent* BDSComponentFactory::CreateUndulator()
1551{
1553 {return nullptr;}
1554
1555 const BDSFieldType undField = BDSFieldType::undulator;
1556
1558 BDSIntegratorType intType = integratorSet->Integrator(undField);
1559 G4Transform3D fieldTrans = CreateFieldTransform(element);
1561 SetBeta0(st);
1562 (*st)["length"] = element->undulatorPeriod * CLHEP::m;
1563 (*st)["field"] = element->scaling * element->B * CLHEP::tesla;
1564
1565 BDSFieldInfo* vacuumFieldInfo = new BDSFieldInfo(undField,
1566 brho,
1567 intType,
1568 st,
1569 true,
1570 fieldTrans);
1571 //BDSFieldInfo* outerFieldInfo = PrepareMagnetOuterFieldInfo(st, undField, bpInfo, 0, fieldTrans);
1572 BDSFieldInfo* outerFieldInfo = nullptr;
1573 // limit step length in field - crucial to this component
1574 // to get the motion correct this has to be less than one oscillation
1575 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
1576 G4double limit = (*st)["length"] * 0.075;
1577 auto ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
1578 if (ul != defaultUL)
1579 {vacuumFieldInfo->SetUserLimits(ul);}
1580
1581 return (new BDSUndulator(elementName,
1582 element->l * CLHEP::m,
1583 element->undulatorPeriod * CLHEP::m,
1584 element->undulatorMagnetHeight * CLHEP::m,
1586 element->undulatorGap * CLHEP::m,
1587 bpInfo,
1588 vacuumFieldInfo,
1589 outerFieldInfo,
1590 element->material));
1591}
1592
1593BDSAcceleratorComponent* BDSComponentFactory::CreateDump()
1594{
1595 G4double chordLength = element->l*CLHEP::m;
1597 {
1598 G4cout << __METHOD_NAME__ << "Using default length of 1 mm for dump" << G4endl;
1599 chordLength = 1*CLHEP::mm;
1600 }
1601
1602 G4bool circular = false;
1603 G4String apertureType = G4String(element->apertureType);
1604 if (apertureType == "circular")
1605 {circular = true;}
1606 else if (apertureType != "rectangular" && !apertureType.empty())
1607 {throw BDSException(__METHOD_NAME__, "unknown shape for dump: \"" + apertureType + "\"");}
1608
1609 BDSDump* result = new BDSDump(elementName,
1610 chordLength,
1612 circular);
1613 return result;
1614}
1615
1616#ifdef USE_DICOM
1617BDSAcceleratorComponent* BDSComponentFactory::CreateCT()
1618{
1620 {return nullptr;}
1621
1622 BDSCT* result = new BDSCT(elementName,
1625 new BDSDicomIntersectVolume(); // TBC
1626
1627 return result;
1628}
1629#endif
1630
1631BDSAcceleratorComponent* BDSComponentFactory::CreateGap()
1632{
1634 {return nullptr;}
1635
1636 return (new BDSGap(elementName,
1637 element->l*CLHEP::m,
1638 element->angle*CLHEP::rad));
1639}
1640
1641BDSAcceleratorComponent* BDSComponentFactory::CreateCrystalCollimator()
1642{
1644 {return nullptr;}
1645
1646 BDSCrystalInfo* left = nullptr;
1647 BDSCrystalInfo* right = nullptr;
1648 if (!element->crystalBoth.empty())
1649 {
1650 left = PrepareCrystalInfo(G4String(element->crystalBoth));
1651 right = PrepareCrystalInfo(G4String(element->crystalBoth));
1652 }
1653 else if (element->crystalBoth.empty() && !element->crystalRight.empty() && !element->crystalLeft.empty())
1654 {
1655 left = PrepareCrystalInfo(G4String(element->crystalLeft));
1656 right = PrepareCrystalInfo(G4String(element->crystalRight));
1657 }
1658 else if (element->crystalRight.empty())
1659 {left = PrepareCrystalInfo(G4String(element->crystalLeft));}
1660 else
1661 {right = PrepareCrystalInfo(G4String(element->crystalRight));}
1662
1663 if (left && !right && BDS::IsFinite(element->crystalAngleYAxisRight))
1664 {
1665 G4cout << G4endl << G4endl << __METHOD_NAME__
1666 << "Left crystal being used but right angle set - perhaps check input for element "
1667 << elementName << G4endl << G4endl;
1668 }
1669 if (!left && right && BDS::IsFinite(element->crystalAngleYAxisLeft))
1670 {
1671 G4cout << G4endl << G4endl << __METHOD_NAME__
1672 << "Right crystal being used but left angle set - perhaps check input for element "
1673 << elementName << G4endl << G4endl;
1674 }
1675
1677 element->l*CLHEP::m,
1679 left,
1680 right,
1681 element->xsize*CLHEP::m, // symmetric for now
1682 element->xsize*CLHEP::m,
1683 element->crystalAngleYAxisLeft*CLHEP::rad,
1684 element->crystalAngleYAxisRight*CLHEP::rad));
1685}
1686
1687BDSAcceleratorComponent* BDSComponentFactory::CreateLaser()
1688{
1690 {return nullptr;}
1691
1692 G4double length = element->l*CLHEP::m;
1693 G4double lambda = element->waveLength*CLHEP::m;
1694
1695 G4ThreeVector direction = G4ThreeVector(element->xdir,element->ydir,element->zdir);
1696 G4ThreeVector position = G4ThreeVector(0,0,0);
1697
1698 return (new BDSLaserWire(elementName, length, lambda, direction) );
1699}
1700
1701BDSAcceleratorComponent* BDSComponentFactory::CreateScreen()
1702{
1704 {return nullptr;}
1705
1706 G4TwoVector size;
1707 size.setX(element->screenXSize*CLHEP::m);
1708 size.setY(element->screenYSize*CLHEP::m);
1709 G4cout << __METHOD_NAME__ << " - size = " << size << G4endl;
1710
1711 BDSScreen* theScreen = new BDSScreen( elementName,
1712 element->l*CLHEP::m,
1714 size,
1715 element->angle);
1716 if (element->layerThicknesses.size() != element->layerMaterials.size())
1717 {
1718 throw BDSException(__METHOD_NAME__, "Element \"" + elementName + "\" must have the " +
1719 "same number of materials as layers - check 'layerMaterials'");
1720 }
1721
1722 if (element->layerThicknesses.empty())
1723 {throw BDSException(__METHOD_NAME__, "Element: \"" + elementName + "\" has 0 screen layers");}
1724
1725 std::list<std::string>::const_iterator itm;
1726 std::list<double>::const_iterator itt;
1727 std::list<int>::const_iterator itIsSampler;
1728 for (itt = element->layerThicknesses.begin(),
1729 itm = element->layerMaterials.begin(),
1730 itIsSampler = element->layerIsSampler.begin();
1731 itt != element->layerThicknesses.end();
1732 ++itt, ++itm)
1733 {
1734#ifdef BDSDEBUG
1735 G4cout << __METHOD_NAME__ << " - screen layer: thickness: "
1736 << *(itt) << ", material " << (*itm)
1737 << ", isSampler: " << (*itIsSampler) << G4endl;
1738#endif
1739 if (!(element->layerIsSampler.empty()))
1740 {
1741 theScreen->AddScreenLayer((*itt)*CLHEP::m, *itm, *itIsSampler);
1742 ++itIsSampler;
1743 }
1744 else
1745 {theScreen->AddScreenLayer((*itt)*CLHEP::m, *itm);}
1746 }
1747 return theScreen;
1748}
1749
1750#ifdef USE_AWAKE
1751BDSAcceleratorComponent* BDSComponentFactory::CreateAwakeScreen()
1752{
1753 return (new BDSAwakeScintillatorScreen(elementName,
1755 element->tscint*CLHEP::m,
1756 element->windowScreenGap*CLHEP::m,
1757 element->angle,
1758 element->twindow*CLHEP::m,
1760}
1761
1762BDSAcceleratorComponent* BDSComponentFactory::CreateAwakeSpectrometer()
1763{
1764 BDSFieldInfo* awakeField = nullptr;
1765 if (element->fieldAll.empty())
1766 {
1767 BDSMagnetStrength* awakeStrength = new BDSMagnetStrength();
1768 SetBeta0(awakeStrength);
1769 (*awakeStrength)["field"] = element->scaling * element->B * CLHEP::tesla;
1770 (*awakeStrength)["by"] = 1; // bx,by,bz is unit field direction, so (0,1,0) here
1771
1772 G4Transform3D fieldTrans = CreateFieldTransform(element);
1773 awakeField = new BDSFieldInfo(BDSFieldType::dipole,
1774 brho,
1775 BDSIntegratorType::g4classicalrk4,
1776 awakeStrength,
1777 true,
1778 fieldTrans);
1779 }
1780 else
1782 return (new BDSAwakeSpectrometer(elementName,
1783 element->awakeMagnetOffsetX*CLHEP::m,
1784 element->l*CLHEP::m,
1785 awakeField,
1786 element->poleStartZ*CLHEP::m,
1788 element->tscint*CLHEP::m,
1789 element->screenPSize*CLHEP::m,
1790 element->windowScreenGap*CLHEP::m,
1791 element->angle,
1792 element->twindow*CLHEP::m,
1794 element->tmount*CLHEP::m,
1796 element->screenEndZ*CLHEP::m,
1797 element->spec,
1798 element->screenWidth*CLHEP::m));
1799}
1800#endif // end of USE_AWAKE
1801
1802BDSAcceleratorComponent* BDSComponentFactory::CreateTransform3D()
1803{
1804 if (element->axisAngle)
1805 {
1806 return new BDSTransform3D(elementName,
1807 element->xdir * CLHEP::m,
1808 element->ydir * CLHEP::m,
1809 element->zdir * CLHEP::m,
1810 element->axisX,
1811 element->axisY,
1812 element->axisZ,
1813 element->angle * CLHEP::rad);
1814 }
1815 else
1816 {
1817 return new BDSTransform3D(elementName,
1818 element->xdir * CLHEP::m,
1819 element->ydir * CLHEP::m,
1820 element->zdir * CLHEP::m,
1821 element->phi * CLHEP::rad,
1822 element->theta * CLHEP::rad,
1823 element->psi * CLHEP::rad);
1824 }
1825}
1826
1828{
1829#ifdef BDSDEBUG
1830 G4cout << "---->creating Terminator" << G4endl;
1831#endif
1832 return new BDSTerminator(width);
1833}
1834
1835BDSAcceleratorComponent* BDSComponentFactory::CreateRMatrix()
1836{
1838
1839 GMAD::Element* elementNew = new GMAD::Element(*element);
1840 elementNew->l = (element->l-thinElementLength)/2.0;
1841
1842 BDSAcceleratorComponent* parallelTransport1 = CreateMagnet(elementNew,
1843 st,
1844 BDSFieldType::paralleltransporter,
1845 BDSMagnetType::paralleltransporter);
1846 BDSAcceleratorComponent* rmatrix = CreateThinRMatrix(0,
1847 elementName + "_centre");
1848 BDSAcceleratorComponent* parallelTransport2 = CreateMagnet(elementNew,
1849 st,
1850 BDSFieldType::paralleltransporter,
1851 BDSMagnetType::paralleltransporter);
1852
1853 const G4String baseName = elementName;
1854 BDSLine* bLine = new BDSLine(baseName);
1855 bLine->AddComponent(parallelTransport1);
1856 bLine->AddComponent(rmatrix);
1857 bLine->AddComponent(parallelTransport2);
1858
1859 delete elementNew;
1860
1861 return bLine;
1862}
1863
1864BDSAcceleratorComponent* BDSComponentFactory::CreateThinRMatrix(G4double angleIn,
1865 const G4String& name)
1866{
1868 return CreateThinRMatrix(angleIn, st, name);
1869}
1870
1871BDSAcceleratorComponent* BDSComponentFactory::CreateThinRMatrix(G4double angleIn,
1873 const G4String& name,
1874 BDSIntegratorType intType,
1875 BDSFieldType fieldType,
1876 G4double beamPipeRadius)
1877{
1878 BDSBeamPipeInfo* beamPipeInfo = PrepareBeamPipeInfo(element, angleIn, -angleIn);
1879 beamPipeInfo->beamPipeType = BDSBeamPipeType::circularvacuum;
1880
1881 // override beampipe radius if supplied - must be set to be iris size for cavity model fringes.
1882 if (BDS::IsFinite(beamPipeRadius))
1883 {beamPipeInfo->aper1 = beamPipeRadius;}
1884
1885 BDSMagnetOuterInfo* magnetOuterInfo = PrepareMagnetOuterInfo(name, element,
1886 -angleIn, angleIn, beamPipeInfo);
1887 magnetOuterInfo->geometryType = BDSMagnetGeometryType::none;
1888
1889 G4Transform3D fieldTrans = CreateFieldTransform(element);
1890 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
1891 brho,
1892 intType,
1893 st,
1894 true,
1895 fieldTrans);
1896 vacuumField->SetBeamPipeRadius(beamPipeInfo->aper1);
1897
1898 BDSMagnet* thinRMatrix = new BDSMagnet(BDSMagnetType::rmatrix,
1899 name,
1901 beamPipeInfo,
1902 magnetOuterInfo,
1903 vacuumField,
1904 0, nullptr, // default values for optional args (angle, outerFieldInfo)
1905 true); // isThin
1906
1907 thinRMatrix->SetExtent(BDSExtent(beamPipeInfo->aper1,
1908 beamPipeInfo->aper1,
1909 thinElementLength*0.5));
1910
1911 return thinRMatrix;
1912}
1913
1914BDSAcceleratorComponent* BDSComponentFactory::CreateCavityFringe(G4double angleIn,
1916 const G4String& name,
1917 G4double irisRadius)
1918{
1919 BDSIntegratorType intType = integratorSet->cavityFringe;
1920 BDSFieldType fieldType = BDSFieldType::cavityfringe;
1921 BDSAcceleratorComponent* cavityFringe = CreateThinRMatrix(angleIn, st, name, intType,fieldType, irisRadius);
1922 return cavityFringe;
1923}
1924
1927 BDSFieldType fieldType,
1928 BDSMagnetType magnetType,
1929 G4double angle,
1930 const G4String& nameSuffix) const
1931{
1933 BDSIntegratorType intType = integratorSet->Integrator(fieldType);
1934 G4Transform3D fieldTrans = CreateFieldTransform(element);
1935 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
1936 brho,
1937 intType,
1938 st,
1939 true,
1940 fieldTrans);
1941
1942 BDSMagnetOuterInfo* outerInfo = PrepareMagnetOuterInfo(elementName + nameSuffix, element, st, bpInfo);
1943 vacuumField->SetScalingRadius(outerInfo->innerRadius); // purely for completeness of information - not required
1944 BDSFieldInfo* outerField = nullptr;
1945
1946 // only make a default multipolar field if the yokeFields flag is on and
1947 // there isn't an 'outerField' specified for the element
1948 G4bool externalOuterField = !(el->fieldOuter.empty());
1949 if (yokeFields && !externalOuterField)
1950 {
1951 outerField = PrepareMagnetOuterFieldInfo(st,
1952 fieldType,
1953 bpInfo,
1954 outerInfo,
1955 fieldTrans,
1957 brho,
1958 ScalingFieldOuter(element));
1959 }
1960
1961 return new BDSMagnet(magnetType,
1962 elementName + nameSuffix,
1963 el->l * CLHEP::m,
1964 bpInfo,
1965 outerInfo,
1966 vacuumField,
1967 angle,
1968 outerField);
1969}
1970
1972 G4bool printWarning)
1973{
1974 if (el->l < 1e-7) // 'l' already in metres from parser
1975 {
1976 if (printWarning)
1977 {BDS::Warning("---> NOT creating element \"" + el->name + "\" -> l < 1e-7 m: l = " + std::to_string(el->l) + " m");}
1978 return false;
1979 }
1980 else
1981 {return true;}
1982}
1983
1985 G4double maxAngle)
1986{
1987 if (std::abs(element->e1) > maxAngle)
1988 {throw BDSException(__METHOD_NAME__, "Pole face angle e1: " + std::to_string(element->e1) +
1989 " is greater than " + std::to_string(maxAngle));}
1990 if (std::abs(element->e2) > maxAngle)
1991 {throw BDSException(__METHOD_NAME__, "Pole face angle e2: " + std::to_string(element->e2) +
1992 " is greater than " + std::to_string(maxAngle));}
1993}
1994
1996 const BDSMagnetStrength* st)
1997{
1998 // for lhcleft and lhcright - purposively override this behaviour
1999 auto mgt = MagnetGeometryType(element);
2000 if (mgt == BDSMagnetGeometryType::lhcleft)
2001 {return true;} // active beam pipe is right and yoke is on the left
2002 if (mgt == BDSMagnetGeometryType::lhcright)
2003 {return false;}
2004
2005 G4double angle = (*st)["angle"];
2006 G4double hkickAng = -(*st)["hkick"]; // not really angle but proportional in the right direction
2007 G4double vkickAng = -(*st)["vkick"];
2008 if (!BDS::IsFinite(angle) && BDS::IsFinite(hkickAng))
2009 {angle = hkickAng;}
2010 if (!BDS::IsFinite(angle) && BDS::IsFinite(vkickAng))
2011 {angle = vkickAng;}
2012 G4bool yokeOnLeft;
2013 if ((angle < 0) && (element->yokeOnInside))
2014 {yokeOnLeft = true;}
2015 else if ((angle > 0) && (!(element->yokeOnInside)))
2016 {yokeOnLeft = true;}
2017 else
2018 {yokeOnLeft = false;}
2019 return yokeOnLeft;
2020}
2021
2022G4double BDSComponentFactory::ScalingFieldOuter(const GMAD::Element* ele)
2023{
2024 return ele->scalingFieldOuterSet ? ele->scalingFieldOuter : BDSGlobalConstants::Instance()->ScalingFieldOuter();
2025}
2026
2028 const BDSFieldType& fieldType,
2029 const BDSBeamPipeInfo* bpInfo,
2030 const BDSMagnetOuterInfo* outerInfo,
2031 const G4Transform3D& fieldTransform,
2032 const BDSIntegratorSet* integratorSetIn,
2033 G4double brhoIn,
2034 G4double outerFieldScaling)
2035{
2036 BDSFieldType outerType;
2037 switch (fieldType.underlying())
2038 {
2039 case BDSFieldType::dipole:
2040 {outerType = BDSFieldType::multipoleouterdipole; break;}
2041 case BDSFieldType::quadrupole:
2042 {outerType = BDSFieldType::multipoleouterquadrupole; break;}
2043 case BDSFieldType::sextupole:
2044 {outerType = BDSFieldType::multipoleoutersextupole; break;}
2045 case BDSFieldType::octupole:
2046 {outerType = BDSFieldType::multipoleouteroctupole; break;}
2047 case BDSFieldType::decapole:
2048 {outerType = BDSFieldType::multipoleouterdecapole; break;}
2049 case BDSFieldType::skewquadrupole:
2050 {outerType = BDSFieldType::skewmultipoleouterquadrupole; break;}
2051 case BDSFieldType::skewsextupole:
2052 {outerType = BDSFieldType::skewmultipoleoutersextupole; break;}
2053 case BDSFieldType::skewoctupole:
2054 {outerType = BDSFieldType::skewmultipoleouteroctupole; break;}
2055 case BDSFieldType::skewdecapole:
2056 {outerType = BDSFieldType::skewmultipoleouterdecapole; break;}
2057 case BDSFieldType::dipole3d:
2058 case BDSFieldType::solenoid:
2059 {outerType = BDSFieldType::solenoidsheet; break;}
2060 default:
2061 {return nullptr; break;} // no possible outer field for any other magnet types
2062 }
2063
2064 BDSMagnetStrength* stCopy = new BDSMagnetStrength(*vacuumSt);
2065 (*stCopy)["scalingOuter"] = outerFieldScaling;
2066 BDSIntegratorType intType = integratorSetIn->Integrator(outerType);
2067 BDSFieldInfo* outerField = new BDSFieldInfo(outerType,
2068 brhoIn,
2069 intType,
2070 stCopy,
2071 true,
2072 fieldTransform);
2073
2074 outerField->SetChordStepMinimum(BDSGlobalConstants::Instance()->ChordStepMinimumYoke());
2075 if (outerInfo)
2076 {
2077 outerField->SetScalingRadius(outerInfo->innerRadius);
2078 outerField->SetSecondFieldOnLeft(outerInfo->yokeOnLeft);
2079 auto gt = outerInfo->geometryType;
2080 G4bool yfmLHC = BDSGlobalConstants::Instance()->YokeFieldsMatchLHCGeometry();
2081 if ((gt == BDSMagnetGeometryType::lhcleft || gt == BDSMagnetGeometryType::lhcright) && yfmLHC)
2082 {
2083 if (fieldType == BDSFieldType::dipole)
2084 {outerField->SetFieldType(BDSFieldType::multipoleouterdipolelhc);}
2085 else if (fieldType == BDSFieldType::quadrupole)
2086 {outerField->SetFieldType(BDSFieldType::multipoleouterquadrupolelhc);}
2087 else if (fieldType == BDSFieldType::sextupole)
2088 {outerField->SetFieldType(BDSFieldType::multipoleoutersextupolelhc);}
2089 }
2090 }
2091
2092 if (bpInfo)
2093 {outerField->SetBeamPipeRadius(bpInfo->IndicativeRadius());}
2094 return outerField;
2095}
2096
2098 const Element* el,
2099 const BDSMagnetStrength* st,
2100 const BDSBeamPipeInfo* beamPipe,
2101 G4double defaultHorizontalWidth,
2102 G4double defaultVHRatio,
2103 G4double defaultCoilWidthFraction,
2104 G4double defaultCoilHeightFraction)
2105{
2106 G4bool yokeOnLeft = YokeOnLeft(el,st);
2107 G4double angle = (*st)["angle"];
2108
2109 return PrepareMagnetOuterInfo(elementNameIn, el, 0.5*angle, 0.5*angle, beamPipe, yokeOnLeft,
2110 defaultHorizontalWidth, defaultVHRatio, defaultCoilWidthFraction,
2111 defaultCoilHeightFraction);
2112}
2113
2115{
2116 BDSMagnetGeometryType result;
2118 // magnet geometry type
2119 if (el->magnetGeometryType.empty() || globals->IgnoreLocalMagnetGeometry())
2120 {result = globals->MagnetGeometryType();}
2121 else
2122 {result = BDS::DetermineMagnetGeometryType(el->magnetGeometryType);}
2123 return result;
2124}
2125
2127 const Element* el,
2128 const G4double angleIn,
2129 const G4double angleOut,
2130 const BDSBeamPipeInfo* beamPipe,
2131 const G4bool yokeOnLeft,
2132 G4double defaultHorizontalWidth,
2133 G4double defaultVHRatio,
2134 G4double defaultCoilWidthFraction,
2135 G4double defaultCoilHeightFraction)
2136{
2138
2140 info->name = elementNameIn;
2141
2142 // magnet geometry type
2143 info->geometryType = MagnetGeometryType(el);
2144 if (! (el->magnetGeometryType.empty() || globals->IgnoreLocalMagnetGeometry()) )
2145 {
2146 info->geometryTypeAndPath = el->magnetGeometryType;
2147 if (el->stripOuterVolume)
2148 {BDS::Warning(__METHOD_NAME__, "stripOuterVolume for element \"" + el->name + "\" will have no effect");}
2149 }
2150
2151 // set face angles w.r.t. chord
2152 info->angleIn = angleIn;
2153 info->angleOut = angleOut;
2154
2155 // horizontal width
2156 info->horizontalWidth = PrepareHorizontalWidth(el, defaultHorizontalWidth);
2157
2158 // inner radius of magnet geometry - TODO when poles can be built away from beam pipe
2159 info->innerRadius = beamPipe->IndicativeRadius();
2160
2161 // outer material
2162 G4Material* outerMaterial;
2163 if (el->material.empty())
2164 {
2165 G4String defaultMaterialName = globals->OuterMaterialName();
2166 outerMaterial = BDSMaterials::Instance()->GetMaterial(defaultMaterialName);
2167 }
2168 else
2169 {outerMaterial = BDSMaterials::Instance()->GetMaterial(el->material);}
2170 info->outerMaterial = outerMaterial;
2171
2172 // yoke direction
2173 info->yokeOnLeft = yokeOnLeft;
2174
2175 if (el->hStyle < 0) // it's unset
2176 {info->hStyle = globals->HStyle();}
2177 else
2178 {info->hStyle = G4bool(el->hStyle);} // convert from int to bool
2179
2180 if (el->vhRatio > 0)
2181 {info->vhRatio = G4double(el->vhRatio);}
2182 else if (globals->VHRatio() > 0)
2183 {info->vhRatio = globals->VHRatio();}
2184 else if (defaultVHRatio > 0) // allow calling function to supply optional default
2185 {info->vhRatio = defaultVHRatio;}
2186 else
2187 {info->vhRatio = info->hStyle ? 0.8 : 1.0;} // h default : c default
2188
2189 if (el->coilWidthFraction > 0)
2190 {info->coilWidthFraction = G4double(el->coilWidthFraction);}
2191 else if (globals->CoilWidthFraction() > 0)
2192 {info->coilWidthFraction = globals->CoilWidthFraction();}
2193 else if (defaultCoilHeightFraction > 0) // allow calling function to supply optional default
2194 {info->coilWidthFraction = defaultCoilWidthFraction;}
2195 else
2196 {info->coilWidthFraction = info->hStyle ? 0.8 : 0.65;} // h default : c default
2197
2198 if (el->coilHeightFraction > 0)
2199 {info->coilHeightFraction = G4double(el->coilHeightFraction);}
2200 else if (globals->CoilHeightFraction() > 0)
2201 {info->coilHeightFraction = globals->CoilHeightFraction();}
2202 else if (defaultCoilHeightFraction > 0) // allow calling function to supply optional default
2203 {info->coilHeightFraction = defaultCoilHeightFraction;}
2204 else
2205 {info->coilHeightFraction = 0.8;} // default for both h and c type
2206
2207 info->colour = PrepareColour(el);
2208 info->autoColour = el->autoColour;
2209
2210 return info;
2211}
2212
2214 G4double defaultHorizontalWidth)
2215{
2216 G4double horizontalWidth = el->horizontalWidth*CLHEP::m;
2217 if (horizontalWidth < 1e-6)
2218 {//horizontalWidth not set - use either global or specified default
2219 if (defaultHorizontalWidth > 0)
2220 {horizontalWidth = defaultHorizontalWidth;}
2221 else
2222 {horizontalWidth = BDSGlobalConstants::Instance()->HorizontalWidth();}
2223 }
2224 return horizontalWidth;
2225}
2226
2228{
2229 G4Material* result = nullptr;
2230 if (el->vacuumMaterial.empty())
2231 {result = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->VacuumMaterial());}
2232 else
2234 return result;
2235}
2236
2238 const G4ThreeVector& inputFaceNormalIn,
2239 const G4ThreeVector& outputFaceNormalIn)
2240{
2241 BDSBeamPipeInfo* defaultModel = BDSGlobalConstants::Instance()->DefaultBeamPipeModel();
2242 BDSBeamPipeInfo* result;
2243 if (!BDSGlobalConstants::Instance()->IgnoreLocalAperture())
2244 {
2245 try
2246 {
2247 result = new BDSBeamPipeInfo(defaultModel,
2248 el->apertureType,
2249 el->aper1 * CLHEP::m,
2250 el->aper2 * CLHEP::m,
2251 el->aper3 * CLHEP::m,
2252 el->aper4 * CLHEP::m,
2253 el->vacuumMaterial,
2254 el->beampipeThickness * CLHEP::m,
2255 el->beampipeMaterial,
2256 inputFaceNormalIn,
2257 outputFaceNormalIn);
2258 }
2259 catch (BDSException& e)
2260 {
2261 G4String msg = "\nProblem in element: \"" + el->name + "\"";
2262 e.AppendToMessage(msg);
2263 throw e;
2264 }
2265 }
2266 else
2267 {// ignore the aperture model from the element and use the global one
2268 result = new BDSBeamPipeInfo(*defaultModel); // ok as only pointers to materials
2269 result->inputFaceNormal = inputFaceNormalIn;
2270 result->outputFaceNormal = outputFaceNormalIn;
2271 }
2272 return result;
2273}
2274
2276 const G4double angleIn,
2277 const G4double angleOut)
2278{
2279 auto faces = BDS::CalculateFaces(angleIn, angleOut);
2280 BDSBeamPipeInfo* info = PrepareBeamPipeInfo(el, faces.first, faces.second);
2281 return info;
2282}
2283
2285{
2286#ifdef BDSDEBUG
2287 G4cout << __METHOD_NAME__ << "offsetX,Y: " << el->offsetX << " " << el->offsetY << " tilt: " << el->tilt << G4endl;
2288#endif
2289 G4double xOffset = el->offsetX * CLHEP::m;
2290 G4double yOffset = el->offsetY * CLHEP::m;
2291 G4double tilt = el->tilt * CLHEP::rad;
2292
2293 BDSTiltOffset* result = nullptr;
2294 if (BDS::IsFinite(xOffset) || BDS::IsFinite(yOffset) || BDS::IsFinite(tilt))
2295 {result = new BDSTiltOffset(xOffset, yOffset, tilt);}
2296 return result;
2297}
2298
2300{
2301 return tiltOffset ? tiltOffset->Transform3D() : G4Transform3D();
2302}
2303
2305{
2307 G4Transform3D trans = CreateFieldTransform(to);
2308 delete to;
2309 return trans;
2310}
2311
2313 G4double angle,
2314 G4double horizontalWidth,
2315 const G4String& name)
2316{
2317 G4double radiusFromAngleLength = std::abs(arcLength / angle); // s = r*theta -> r = s/theta
2318 if (horizontalWidth > 2*radiusFromAngleLength)
2319 {
2320 G4cerr << "Error: the combination of length, angle and horizontalWidth in element named \"" << name
2321 << "\" will result in overlapping faces!" << G4endl << "Please reduce the horizontalWidth" << G4endl;
2322 throw BDSException(__METHOD_NAME__, "");
2323 }
2324}
2325
2327{
2328 for (const auto& model : BDSParser::Instance()->GetCavityModels())
2329 {
2330 // material can either be specified in
2331 G4Material* material = nullptr;
2332 if (!model.material.empty())
2333 {material = BDSMaterials::Instance()->GetMaterial(model.material);}
2334
2335 auto info = new BDSCavityInfo(BDS::DetermineCavityType(model.type),
2336 material,
2337 model.irisRadius*CLHEP::m,
2338 model.thickness*CLHEP::m,
2339 model.equatorRadius*CLHEP::m,
2340 model.halfCellLength*CLHEP::m,
2341 model.numberOfPoints,
2342 model.numberOfCells,
2343 model.equatorHorizontalAxis*CLHEP::m,
2344 model.equatorVerticalAxis*CLHEP::m,
2345 model.irisHorizontalAxis*CLHEP::m,
2346 model.irisVerticalAxis*CLHEP::m,
2347 model.tangentLineAngle);
2348
2349 cavityInfos[model.name] = info;
2350 }
2351}
2352
2354{
2355 if (!coloursInitialised)
2356 {
2357 BDSColours* allColours = BDSColours::Instance();
2358 for (const auto& colour : BDSParser::Instance()->GetColours())
2359 {
2360 allColours->DefineColour(G4String(colour.name),
2361 (G4double) colour.red,
2362 (G4double) colour.green,
2363 (G4double) colour.blue,
2364 (G4double) colour.alpha);
2365 }
2366 coloursInitialised = true;
2367 }
2368}
2369
2371{
2372 for (auto& model : BDSParser::Instance()->GetCrystals())
2373 {
2374 G4Material* material = BDSMaterials::Instance()->GetMaterial(model.material);
2375
2376 auto info = new BDSCrystalInfo(material,
2377 G4String(model.data),
2378 BDS::DetermineCrystalType(model.shape),
2379 G4double(model.lengthX)*CLHEP::m,
2380 G4double(model.lengthY)*CLHEP::m,
2381 G4double(model.lengthZ)*CLHEP::m,
2382 G4double(model.sizeA)*CLHEP::m,
2383 G4double(model.sizeB)*CLHEP::m,
2384 G4double(model.sizeC)*CLHEP::m,
2385 G4double(model.alpha)*CLHEP::halfpi,
2386 G4double(model.beta)*CLHEP::halfpi,
2387 G4double(model.gamma)*CLHEP::halfpi,
2388 G4int (model.spaceGroup),
2389 G4double(model.bendingAngleYAxis)*CLHEP::rad,
2390 G4double(model.bendingAngleZAxis)*CLHEP::rad,
2391 G4double(model.miscutAngleY)*CLHEP::rad);
2392 crystalInfos[model.name] = info;
2393 }
2394
2395}
2396
2398{
2399 auto result = crystalInfos.find(crystalName);
2400 if (result == crystalInfos.end())
2401 {
2402 G4cout << "Defined crystals are:" << G4endl;
2403 for (const auto& kv : crystalInfos)
2404 {G4cout << kv.first << G4endl;}
2405 throw BDSException(__METHOD_NAME__, "unknown crystal \"" + crystalName + "\" - please define it");
2406 }
2407
2408 // prepare a copy so the component can own that recipe
2409 BDSCrystalInfo* info = new BDSCrystalInfo(*(result->second));
2410 return info;
2411}
2412
2414 G4double frequency) const
2415{
2416 // If the cavity model name (identifier) has been defined, return a *copy* of
2417 // that model - so that the component will own that info object.
2418
2419 G4String modelName = G4String(el->cavityModel);
2420
2421 // no specific model - prepare a default based on element parameters
2422 if (modelName == "")
2423 {return PrepareCavityModelInfoForElement(el, frequency);}
2424
2425 // cavity model name specified - match up with parser object already translated here
2426 auto result = cavityInfos.find(modelName);
2427 if (result == cavityInfos.end())
2428 {throw BDSException(__METHOD_NAME__, "unknown cavity model identifier \"" + el->cavityModel + "\" - please define it");}
2429
2430 // we make a per element copy of the definition
2431 BDSCavityInfo* info = new BDSCavityInfo(*(result->second));
2432
2433 // check cavity radius against horizontal width. Cavity radius calculation same as that in CavityFactory classes.
2434 G4double cavityRadius = info->equatorRadius + info->thickness + lengthSafety;
2435 G4double horizontalWidth = PrepareHorizontalWidth(el);
2436 if (cavityRadius > horizontalWidth)
2437 {
2438 throw BDSException(__METHOD_NAME__, "Cavity horizontalWidth for element \"" + elementName + "\" is smaller " +
2439 "than the cavity model radius.");
2440 }
2441
2442 // If no material specified, we take the material from the element. If no material at
2443 // all, we exit with warning.
2444 if (!info->material)
2445 {
2446 if (el->material.empty())
2447 {
2448 G4cout << "ERROR: Cavity material is not defined for cavity \"" << elementName << "\""
2449 << "or for cavity model \"" << el->cavityModel << "\" - please define it" << G4endl;
2450 throw BDSException(__METHOD_NAME__, "");
2451 }
2452 else
2453 {info->material = BDSMaterials::Instance()->GetMaterial(el->material);}
2454 }
2455
2456 return info;
2457}
2458
2460 G4double frequency) const
2461{
2463 BDSBeamPipeInfo* aperture = PrepareBeamPipeInfo(el);
2464
2465 G4double aper1 = aperture->aper1;
2466 G4double horizontalWidth = PrepareHorizontalWidth(el);
2467 G4double defaultHorizontalWidth = 20*CLHEP::cm;
2468 if (aper1 < defaultHorizontalWidth) // only do if the aperture will fit
2469 {horizontalWidth = std::min(defaultHorizontalWidth, horizontalWidth);} // better default
2470 G4double thickness = aperture->beamPipeThickness;
2471 G4double equatorRadius = horizontalWidth - thickness;
2472 if (equatorRadius <= 0)
2473 {
2474 throw BDSException(__METHOD_NAME__, "combination of horizontalWidth and beampipeThickness for element \"" +
2475 el->name + "\" produce 0 size cavity");
2476 }
2477
2478 // assume single cell cavity
2479 G4double length = el->l * CLHEP::m;
2480 G4double cellLength = length;
2481 G4int nCells = 1;
2482
2483 // calculate number of cells if frequency is finite -
2484 // frequency can be zero in which case only build 1 cell
2485 if (BDS::IsFinite(frequency))
2486 {
2487 cellLength = 2*CLHEP::c_light / frequency; // half wavelength
2488 G4double nCavities = length / cellLength;
2489 nCells = G4int(std::floor(nCavities));
2490 }
2491
2492 if (nCells == 0) // protect against long wavelengths or cavities
2493 {
2494 nCells = 1;
2495 cellLength = length;
2496 }
2497
2498 BDSCavityInfo* defaultCI = new BDSCavityInfo(BDSCavityType::pillbox,
2499 BDSMaterials::Instance()->GetMaterial("Copper"),
2500 aperture->aper1,
2501 thickness,
2502 equatorRadius,
2503 cellLength*0.5);
2504
2505 delete aperture;
2506 return defaultCI;
2507}
2508
2510 G4double cavityLength,
2511 G4double currentArcLength,
2512 BDSMagnetStrength*& fringeIn,
2513 BDSMagnetStrength*& fringeOut) const
2514{
2516 SetBeta0(st);
2517 G4double chordLength = cavityLength; // length may be reduced for fringe placement.
2518 G4double scaling = el->scaling;
2519 (*st)["equatorradius"] = 1*CLHEP::m; // to prevent 0 division - updated later on in createRF
2520 (*st)["length"] = chordLength;
2521
2522 // scale factor to account for reduced body length due to fringe placement.
2523 G4double lengthScaling = cavityLength / (element->l * CLHEP::m);
2524
2525 if (BDS::IsFinite(el->gradient))
2526 {(*st)["efield"] = scaling * el->gradient * CLHEP::volt / CLHEP::m;}
2527 else
2528 {(*st)["efield"] = scaling * el->E * CLHEP::volt / chordLength;}
2529 (*st)["efield"] /= lengthScaling;
2530
2531 G4double frequency = std::abs(el->frequency * CLHEP::hertz);
2532 (*st)["frequency"] = frequency;
2533
2534 // set the phase from the element even if zero frequency, field should be cos(phi) = constant.
2535 G4double phase = el->phase * CLHEP::rad;
2536 (*st)["phase"] = phase;
2537
2538 // fringe strengths
2539 fringeIn = new BDSMagnetStrength(*st);
2540 fringeOut = new BDSMagnetStrength(*st);
2541
2542 // if frequency is 0, don't update phase with offset. Fringes should have the same phase.
2543 if (!BDS::IsFinite(frequency))
2544 {return st;}
2545
2546 // for finite frequency, construct it so that phase is w.r.t. the centre of the cavity
2547 // and that it's 0 by default
2548 G4double period = 1. / frequency;
2549 G4double tOffset = 0;
2550 if (BDS::IsFinite(el->tOffset)) // use the one specified
2551 {tOffset = el->tOffset * CLHEP::s;}
2552 else // this gives 0 phase at the middle of cavity assuming relativistic particle with v = c
2553 {tOffset = (currentArcLength + 0.5 * chordLength) / CLHEP::c_light;}
2554
2555 // use a cheeky lambda to avoid repeating the calculation code
2556 auto getPhaseFromT = [](G4double tOffsetIn, G4double periodIn)
2557 {
2558 G4double nPeriods = tOffsetIn / periodIn;
2559 // phase is the remainder from total phase / N*2pi, where n is unknown.
2560 G4double integerPart = 0;
2561 G4double fractionalPart = std::modf(nPeriods, &integerPart);
2562 G4double phaseOffset = fractionalPart * CLHEP::twopi;
2563 return phaseOffset;
2564 };
2565
2566 G4double phaseOffset = getPhaseFromT(tOffset, period);
2567 (*st)["phase"] -= phaseOffset;
2568
2569 // sort phase / timing for each fringe
2570 G4double tOffsetIn = tOffset; // copy central T0
2571 G4double tOffsetOut = tOffset;
2572 G4double tHalfCavity = (0.5 * chordLength) / CLHEP::c_light;
2573 // this gives correct phase at the beginning of cavity
2574 tOffsetIn -= tHalfCavity;
2575 // this gives correct phase at the end of cavity
2576 tOffsetOut += tHalfCavity;
2577
2578 G4double phaseOffsetIn = getPhaseFromT(tOffsetIn, period);
2579 G4double phaseOffsetOut = getPhaseFromT(tOffsetOut, period);
2580 (*fringeIn)["phase"] = phaseOffsetIn;
2581 (*fringeOut)["phase"] = phaseOffsetOut;
2582
2583 return st;
2584}
2585
2587{
2588 G4String colour = el->colour;
2589 if (colour.empty())
2591 else
2592 {return BDSColours::Instance()->GetColour(colour);}
2593}
2594
2596 const G4String& defaultMaterialName)
2597{
2598 G4String materialName = el->material;
2599 if (materialName.empty())
2600 {return BDSMaterials::Instance()->GetMaterial(defaultMaterialName);}
2601 else
2602 {return BDSMaterials::Instance()->GetMaterial(materialName);}
2603}
2604
2606{
2607 G4String materialName = el->material;
2608 if (materialName.empty())
2609 {throw BDSException(__METHOD_NAME__, "element \"" + el->name + "\" has no material specified.");}
2610 else
2611 {return BDSMaterials::Instance()->GetMaterial(materialName);}
2612}
2613
2615 BDSAcceleratorComponent* component) const
2616{
2617 // Test for a line. And if so apply to each sub-component.
2618 G4Transform3D fieldTrans = CreateFieldTransform(element);
2619 if (BDSLine* line = dynamic_cast<BDSLine*>(component))
2620 {
2621 for (auto comp : *line)
2622 {SetFieldDefinitions(el, comp);}
2623 }
2624 if (BDSMagnet* mag = dynamic_cast<BDSMagnet*>(component))
2625 {
2626 if (!(el->fieldAll.empty()))
2627 {
2628 G4cerr << "Error: Magnet named \"" << elementName
2629 << "\" is a magnet, but has fieldAll defined." << G4endl
2630 << "Can only have fieldOuter and or fieldVacuum specified." << G4endl;
2631 throw BDSException(__METHOD_NAME__, "");
2632 }
2633 if (!(el->fieldOuter.empty())) // ie variable isn't ""
2634 {
2635 BDSFieldInfo* info = new BDSFieldInfo(*(BDSFieldFactory::Instance()->GetDefinition(el->fieldOuter)));
2636 if (info->ProvideGlobal())
2637 {info->SetTransformBeamline(fieldTrans);}
2638 info->CompoundBScaling(ScalingFieldOuter(el));
2639 mag->SetOuterField(info);
2640 }
2641 if (!(el->fieldVacuum.empty()))
2642 {
2643 BDSFieldInfo* info = new BDSFieldInfo(*(BDSFieldFactory::Instance()->GetDefinition(el->fieldVacuum)));
2644 if (info->ProvideGlobal())
2645 {info->SetTransformBeamline(fieldTrans);}
2646 mag->SetVacuumField(info);
2647 }
2648 }
2649 else
2650 {
2651 if (!(el->fieldAll.empty()))
2652 {
2653 BDSFieldInfo* info = new BDSFieldInfo(*(BDSFieldFactory::Instance()->GetDefinition(el->fieldAll)));
2654 if (info->ProvideGlobal())
2655 {info->SetTransformBeamline(fieldTrans);}
2656 if (el->scalingFieldOuter != 1)
2657 {BDS::Warning("component \"" + el->name + "\" has \"scalingFieldOuter\" != 1.0 -> this will have no effect for \"fieldAll\"");}
2658 component->SetField(info);
2659 }
2660 }
2661}
2662
2664{
2666 SetBeta0(st);
2667 G4double scaling = el->scaling;
2668 (*st)["length"] = el->l * CLHEP::m; // length needed for thin multipoles
2669 // component strength is only normalised by length for thick multipoles
2670 if (el->type == ElementType::_THINMULT || (el->type == ElementType::_MULT && !BDS::IsFinite(el->l)))
2671 {(*st)["length"] = 1*CLHEP::m;}
2672 auto kn = el->knl.begin();
2673 auto ks = el->ksl.begin();
2674 std::vector<G4String> normKeys = st->NormalComponentKeys();
2675 std::vector<G4String> skewKeys = st->SkewComponentKeys();
2676 auto nkey = normKeys.begin();
2677 auto skey = skewKeys.begin();
2678 // Separate loops for kn and ks. The length of knl and ksl are determined by the input in the gmad file.
2679 for (; kn != el->knl.end(); kn++, nkey++)
2680 {(*st)[*nkey] = scaling * (*kn);}
2681 for (; ks != el->ksl.end(); ks++, skey++)
2682 {(*st)[*skey] = scaling * (*ks);}
2683
2684 return st;
2685}
2686
2688{
2690 G4double scaling = el->scaling;
2691 // G4double length = el->l;
2692
2693 (*st)["kick1"] = scaling * el->kick1;
2694 (*st)["kick2"] = scaling * el->kick2;
2695 (*st)["kick3"] = scaling * el->kick3;
2696 (*st)["kick4"] = scaling * el->kick4;
2697
2698 (*st)["rmat11"] = scaling * el->rmat11;
2699 (*st)["rmat12"] = scaling * el->rmat12;
2700 (*st)["rmat13"] = scaling * el->rmat13;
2701 (*st)["rmat14"] = scaling * el->rmat14;
2702
2703 (*st)["rmat21"] = scaling * el->rmat21;
2704 (*st)["rmat22"] = scaling * el->rmat22;
2705 (*st)["rmat23"] = scaling * el->rmat23;
2706 (*st)["rmat24"] = scaling * el->rmat24;
2707
2708 (*st)["rmat31"] = scaling * el->rmat31;
2709 (*st)["rmat32"] = scaling * el->rmat32;
2710 (*st)["rmat33"] = scaling * el->rmat33;
2711 (*st)["rmat34"] = scaling * el->rmat34;
2712
2713 (*st)["rmat41"] = scaling * el->rmat41;
2714 (*st)["rmat42"] = scaling * el->rmat42;
2715 (*st)["rmat43"] = scaling * el->rmat43;
2716 (*st)["rmat44"] = scaling * el->rmat44;
2717
2718 return st;
2719}
2720
2721G4double BDSComponentFactory::FieldFromAngle(const G4double angle,
2722 const G4double arcLength) const
2723{
2724 if (!BDS::IsFinite(angle))
2725 {return 0;}
2726 else
2727 {return brho * angle / arcLength;}
2728}
2729
2730G4double BDSComponentFactory::AngleFromField(const G4double field,
2731 const G4double arcLength) const
2732{
2733 if (!BDS::IsFinite(field))
2734 {return 0;}
2735 else
2736 {return field * arcLength / brho;}
2737}
2738
2740 G4double& angle,
2741 G4double& field) const
2742{
2743 G4double arcLength = el->l * CLHEP::m;
2744 if (BDS::IsFinite(el->B) && el->angleSet)
2745 {// both are specified and should be used - under or overpowered dipole by design
2746 field = el->B * CLHEP::tesla;
2747 angle = el->angle * CLHEP::rad;
2748 }
2749 else if (BDS::IsFinite(el->B))
2750 {// only B field - calculate angle
2751 field = el->B * CLHEP::tesla;
2752 angle = AngleFromField(field, arcLength); // length is arc length for an sbend
2753 }
2754 else
2755 {// only angle - calculate B field
2756 angle = el->angle * CLHEP::rad;
2757 field = FieldFromAngle(angle, arcLength);
2758 }
2759 // un-split sbends are effectively rbends - don't construct a >pi/2 degree rbend
2760 // also cannot construct sbends with angles > pi/2
2761 if (BDSGlobalConstants::Instance()->DontSplitSBends() && (std::abs(angle) > CLHEP::pi/2.0))
2762 {throw BDSException("Error: the unsplit sbend "+ el->name + " cannot be constucted as its bending angle is defined to be greater than pi/2.");}
2763
2764 else if (std::abs(angle) > CLHEP::pi*2.0)
2765 {throw BDSException("Error: the sbend "+ el->name +" cannot be constucted as its bending angle is defined to be greater than 2 pi.");}
2766}
2767
2769 G4double& arcLength,
2770 G4double& chordLength,
2771 G4double& field,
2772 G4double& angle) const
2773{
2774 // 'l' in the element represents the chord length for an rbend - must calculate arc length
2775 // for the field calculation and the accelerator component.
2776 chordLength = el->l * CLHEP::m;
2777 G4double arcLengthLocal = chordLength; // default for no angle
2778
2779 if (BDS::IsFinite(el->B) && el->angleSet)
2780 {// both are specified and should be used - under or overpowered dipole by design
2781 field = el->B * CLHEP::tesla;
2782 // note, angle must be finite for this part to be used so we're protected against
2783 // infinite bending radius and therefore nan arcLength.
2784 angle = el->angle * CLHEP::rad;
2785 G4double bendingRadius = brho / field;
2786
2787 // protect against bad calculation from 0 angle and finite field
2788 if (BDS::IsFinite(angle))
2789 {arcLengthLocal = bendingRadius * angle;}
2790 else
2791 {arcLengthLocal = chordLength;}
2792 }
2793 else if (BDS::IsFinite(el->B))
2794 {// only B field - calculate angle
2795 field = el->B * CLHEP::tesla;
2796 G4double bendingRadius = brho / field; // in mm as brho already in g4 units
2797 angle = 2.0*std::asin(chordLength*0.5 / bendingRadius);
2798 if (std::isnan(angle))
2799 {throw BDSException("Field too strong for element " + el->name + ", magnet bending angle will be greater than pi.");}
2800
2801 arcLengthLocal = bendingRadius * angle;
2802 }
2803 else
2804 {// (assume) only angle - calculate B field
2805 angle = el->angle * CLHEP::rad;
2806 if (BDS::IsFinite(angle))
2807 {
2808 // sign for bending radius doesn't matter (from angle) as it's only used for arc length.
2809 // this is the inverse equation of that in BDSAcceleratorComponent to calculate
2810 // the chord length from the arclength and angle.
2811 G4double bendingRadius = chordLength * 0.5 / std::sin(std::abs(angle) * 0.5);
2812 arcLengthLocal = bendingRadius * angle;
2813 field = brho * angle / std::abs(arcLengthLocal);
2814 }
2815 else
2816 {field = 0;} // 0 angle -> chord length and arc length the same; field 0
2817 }
2818
2819 // Ensure positive length despite sign of angle.
2820 arcLength = std::abs(arcLengthLocal);
2821
2822 if (std::abs(angle) > CLHEP::pi/2.0)
2823 {throw BDSException("Error: the rbend " + el->name + " cannot be constucted as its bending angle is defined to be greater than pi/2.");}
2824}
2825
2827{
2828 G4double bendAngle = 0;
2829 if (el->type == ElementType::_RBEND)
2830 {
2831 G4double arcLength = 0, chordLength = 0, field = 0;
2832 CalculateAngleAndFieldRBend(el, arcLength, chordLength, field, bendAngle);
2833 }
2834 else if (el->type == ElementType::_SBEND)
2835 {
2836 G4double field = 0; // required by next function.
2837 CalculateAngleAndFieldSBend(el, bendAngle, field);
2838 }
2839 // else the default is 0
2840 return bendAngle;
2841}
2842
2844{
2845 if (!el)
2846 {return 0;}
2847 // note thin multipoles will match the faces of any magnets, but not contain
2848 // the face angles themselves in the GMAD::Element. this is ok though as
2849 // detector construction will not give a thin multipole as a previous element
2850 // - it'll be skipped while looking backwards.
2851 G4double outgoingFaceAngle = 0;
2852 G4double bendAngle = BendAngle(el);
2853
2854 if (el->type == ElementType::_RBEND)
2855 {
2857 {return outgoingFaceAngle;}
2858 // angle is w.r.t. outgoing reference trajectory so rbend face is angled
2859 // by half the bend angle
2860 outgoingFaceAngle += 0.5 * bendAngle;
2861 }
2862
2863 // if we're using the matrix integrator set, we build the bends flat irrespective of parameters
2864 // if we have a finite k1 for a bend, we're forced to use the bdsimmatrix integrator set
2866 {return outgoingFaceAngle;}
2867
2868 // for an sbend, the output face is nominally normal to the outgoing
2869 // reference trajectory - so zero here - only changes with e1/e2.
2870 // we need angle though to decide which way it goes
2871
2872 // +ve e1/e2 shorten the outside of the bend - so flips with angle
2873 G4double e2 = el->e2*CLHEP::rad;
2874 if (BDS::IsFinite(e2) && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry())
2875 {// so if the angle is 0, +1 will be returned
2876 G4double factor = bendAngle < 0 ? -1 : 1;
2877 outgoingFaceAngle += factor * e2;
2878 }
2879
2880 return outgoingFaceAngle;
2881}
2882
2884{
2885 if (!el)
2886 {return 0;}
2887 // note thin multipoles will match the faces of any magnets, but not contain
2888 // the face angles themselves in the GMAD::Element. this is ok though as
2889 // detector construction will not give a thin multipole as a next element
2890 // - it'll be skipped while looking forwards.
2891 G4double incomingFaceAngle = 0;
2892 G4double bendAngle = BendAngle(el);
2893
2894 if (el->type == ElementType::_RBEND)
2895 {
2897 {return incomingFaceAngle;}
2898 // angle is w.r.t. outgoing reference trajectory so rbend face is angled
2899 // by half the bend angle
2900 incomingFaceAngle += 0.5 * bendAngle;
2901 }
2902
2903 // if we're using the matrix integrator set, we build the bends flat irrespective of parameters
2904 // if we have a finite k1 for a bend, we're forced to use the bdsimmatrix integrator set
2906 {return incomingFaceAngle;}
2907
2908 // for an sbend, the output face or nominally normal to the outgoing
2909 // reference trajectory - so zero here - only changes with e1/e2.
2910 // we need angle though to decide which way it goes
2911
2912 // +ve e1/e2 shorten the outside of the bend - so flips with angle
2913 G4double e1 = el->e1*CLHEP::rad;
2914 if (BDS::IsFinite(e1) && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry())
2915 {// so if the angle is 0, +1 will be returned
2916 G4double factor = bendAngle < 0 ? -1 : 1;
2917 incomingFaceAngle += factor * e1;
2918 }
2919
2920 return incomingFaceAngle;
2921}
static BDSAcceleratorComponentRegistry * Instance()
Singleton accessor.
void RegisterComponent(BDSAcceleratorComponent *component, bool isModified=false)
BDSAcceleratorComponent * GetComponent(const G4String &name)
Abstract class that represents a component of an accelerator.
virtual void SetMinimumKineticEnergy(G4double)
virtual void SetBiasVacuumList(const std::list< std::string > &biasVacuumListIn)
Copy the bias list to this element.
void SetField(BDSFieldInfo *fieldInfoIn)
Set the field definition for the whole component.
virtual void SetBiasMaterialList(const std::list< std::string > &biasMaterialListIn)
Copy the bias list to this element.
virtual void SetRegion(const G4String &regionIn)
Set the region name for this component.
static BDSBeamPipeFactory * Instance()
Singleton accessor.
Holder class for all information required to describe a beam pipe model.
BDSExtent Extent() const
G4double aper1
Public member for direct access.
G4ThreeVector outputFaceNormal
Public member for direct access.
G4double beamPipeThickness
Public member for direct access.
G4double IndicativeRadius() const
Return an indicative extent of the beam pipe - typically the maximum of x or y extent.
BDSBeamPipeType beamPipeType
Public member for direct access.
G4ThreeVector inputFaceNormal
Public member for direct access.
RF Cavity. Uses factories to construct appropriate geometry.
Holder for all Geometrical information required to create an RF cavity.
G4double equatorRadius
Equator radius - half width of widest part.
G4double irisRadius
Iris radius - half width of narrowest part.
G4Material * material
Material.
G4double thickness
Thickness of wall material.
A piece of vacuum beam pipe with a crystal for channelling.
A class for an elliptical collimator.
Collimator with only two jaw and no top bit.
A class for a rectangular collimator.
Colour class that holds all colours used in BDSIM.
Definition: BDSColours.hh:33
static BDSColours * Instance()
singleton pattern
Definition: BDSColours.cc:33
G4Colour * GetColour(const G4String &type, G4bool normaliseTo255=true)
Get colour from name.
Definition: BDSColours.cc:202
void DefineColour(const G4String &name, G4double red, G4double green, G4double blue, G4double alpha=1, G4bool normaliseTo255=true)
Define a new colour.
Definition: BDSColours.cc:152
Factory for user specified accelerator components.
G4bool CanConstructComponentByName(const G4String &componentTypeName) const
Check whether a component can be constructed - ie if the name exists.
BDSAcceleratorComponent * ConstructComponent(const G4String &componentTypeName, GMAD::Element const *elementIn, GMAD::Element const *prevElementIn, GMAD::Element const *nextElementIn, G4double currentArcLength=0)
static G4bool YokeOnLeft(const GMAD::Element *el, const BDSMagnetStrength *st)
std::map< G4String, BDSCrystalInfo * > crystalInfos
Maps of crystal info instances by name.
G4double OutgoingFaceAngle(const GMAD::Element *el) const
G4double AngleFromField(const G4double field, const G4double arcLength) const
BDSComponentFactoryUser * userComponentFactory
User component factory if any.
KickerType
Private enum for kicker types.
G4double FieldFromAngle(const G4double angle, const G4double arcLength) const
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.
BDSComponentFactory()=delete
No default constructor.
BDSAcceleratorComponent * CreateTeleporter(const G4double teleporterLength, const G4double teleporterWidth, const G4Transform3D &transformIn)
G4Material * PrepareVacuumMaterial(GMAD::Element const *el) const
Prepare the vacuum material from the element or resort to default in options.
void CalculateAngleAndFieldRBend(const GMAD::Element *el, G4double &arcLength, G4double &chordLength, G4double &field, G4double &angle) const
static G4Transform3D CreateFieldTransform(const BDSTiltOffset *tiltOffset)
Create a transform from a tilt offset. If nullptr, returns identity transform.
BDSMagnet * CreateMagnet(const GMAD::Element *el, BDSMagnetStrength *st, BDSFieldType fieldType, BDSMagnetType magnetType, G4double angle=0.0, const G4String &nameSuffix="") const
Helper method for common magnet construction.
BDSMagnetStrength * PrepareMagnetStrengthForMultipoles(GMAD::Element const *el) const
Prepare magnet strength for multipoles.
GMAD::Element const * element
element for storing instead of passing around
static void PoleFaceRotationsNotTooLarge(const GMAD::Element *el, G4double maxAngle=0.5 *CLHEP::halfpi)
Check whether the pole face rotation angles are too big for practical construction.
G4double thinElementLength
Length of a thin element.
BDSCavityInfo * PrepareCavityModelInfoForElement(GMAD::Element const *el, G4double frequency) const
GMAD::Element const * nextElement
element access to previous element (can be nullptr)
BDSCavityInfo * PrepareCavityModelInfo(GMAD::Element const *el, G4double frequency) const
G4bool yokeFields
Cache of whether to include yoke magnetic fields.
static G4bool coloursInitialised
void PrepareColours()
Prepare all colours defined in the parser.
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)
BDSAcceleratorComponent * CreateTerminator(G4double witdth)
static G4Colour * PrepareColour(GMAD::Element const *element)
Checks if colour is specified for element, else uses the default for that element type.
BDSMagnetStrength * PrepareCavityStrength(GMAD::Element const *el, G4double cavityLength, G4double currentArcLength, BDSMagnetStrength *&fringeIn, BDSMagnetStrength *&fringeOut) const
BDSMagnetStrength * PrepareMagnetStrengthForRMatrix(GMAD::Element const *el) const
Prepare magnet strength for rmatrix.
G4bool includeFringeFields
Cache of whether to include fringe fields.
void SetFieldDefinitions(GMAD::Element const *el, BDSAcceleratorComponent *component) const
BDSAcceleratorComponent * CreateComponent(GMAD::Element const *elementIn, GMAD::Element const *prevElementIn, GMAD::Element const *nextElementIn, G4double currentArcLength=0)
static void CheckBendLengthAngleWidthCombo(G4double arcLength, G4double angle, G4double horizontalWidth, const G4String &name="not given")
void PrepareCrystals()
Prepare all crystals in defined the parser.
G4double lengthSafety
Length safety from global constants.
G4String elementName
Variable used to pass around the possibly modified name of an element.
std::map< G4String, G4int > modifiedElements
void GetKickValue(G4double &hkick, G4double &vkick, const KickerType type) const
G4double BendAngle(const GMAD::Element *el) const
Calculate the angle of a bend whether it's an rbend or an sbend.
G4double IncomingFaceAngle(const GMAD::Element *el) const
void CalculateAngleAndFieldSBend(GMAD::Element const *el, G4double &angle, G4double &field) const
static G4Material * PrepareMaterial(GMAD::Element const *element, const G4String &defaultMaterialName)
Checks if a material is named in Element::material, else uses the supplied default.
static BDSTiltOffset * CreateTiltOffset(GMAD::Element const *el)
Create the tilt and offset information object by inspecting the parser element.
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)
Prepare the field definition for the yoke of a magnet.
const BDSIntegratorSet * integratorSet
Local copy of reference to integrator set to use.
G4double brho
Rigidity in T*m (G4units) for beam particles.
void SetBeta0(BDSMagnetStrength *stIn) const
Simple setter used to add Beta0 to a strength instance.
BDSCrystalInfo * PrepareCrystalInfo(const G4String &crystalName) const
static BDSBeamPipeInfo * PrepareBeamPipeInfo(GMAD::Element const *el, const G4ThreeVector &inputFaceNormal=G4ThreeVector(0, 0,-1), const G4ThreeVector &outputFaceNormal=G4ThreeVector(0, 0, 1))
static BDSMagnetGeometryType MagnetGeometryType(const GMAD::Element *el)
std::map< G4String, BDSCavityInfo * > cavityInfos
Map of cavity model info instances by name.
G4bool HasSufficientMinimumLength(GMAD::Element const *el, G4bool printWarning=true)
Test the component length is sufficient for practical construction.
GMAD::Element const * prevElement
element access to previous element (can be nullptr)
Holder for all information required to create a crystal.
Degrader based on wedge design used in the PSI medical accelerator.
Definition: BDSDegrader.hh:35
A piece of vacuum beam pipe.
Definition: BDSDrift.hh:39
An infinite absorber.
Definition: BDSDump.hh:33
A class for a generic piece of external geometry.
Definition: BDSElement.hh:39
General exception with possible name of object and message.
Definition: BDSException.hh:35
Holder for +- extents in 3 dimensions.
Definition: BDSExtent.hh:39
G4double DX() const
The difference in a dimension.
Definition: BDSExtent.hh:83
G4double DY() const
The difference in a dimension.
Definition: BDSExtent.hh:84
static BDSFieldFactory * Instance()
Public accessor method for singleton pattern.
BDSFieldInfo * GetDefinition(const G4String &name) const
All info required to build complete field of any type.
Definition: BDSFieldInfo.hh:65
void SetTransformBeamline(const G4Transform3D &transformIn)
Set the beam line transform.
void CompoundBScaling(G4double extraBScalingIn)
*= for BScaling.
G4bool ProvideGlobal() const
Accessor.
void SetFieldType(BDSFieldType fieldTypeIn)
Set Transform - could be done afterwards once instance of this class is passed around.
BDSFieldType FieldType() const
Accessor.
void SetUserLimits(G4UserLimits *userLimitsIn)
Delete and replace the user limits which this class owns (only if not default ul).
Gap in accelerator beamline.
Definition: BDSGap.hh:32
void SetExtent(const BDSExtent &extIn)
Set extent.
A class that holds global options and constants.
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 laser wire scanner.
Definition: BDSLaserWire.hh:35
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
static BDSMagnetOuterFactory * Instance()
Singleton accessor.
Holder struct of all information required to create the outer geometry of a magnet.
G4bool hStyle
H Style for dipoles. If not, it's assumed C style.
Efficient storage of magnet strengths.
static std::vector< G4String > SkewComponentKeys()
Accessor for skew component keys - k1 - k12.
static std::vector< G4String > NormalComponentKeys()
Accessor for normal component keys - k1 - k12.
Abstract base class that implements features common to all magnets.
Definition: BDSMagnet.hh:45
static BDSMaterials * Instance()
Singleton pattern access.
Definition: BDSMaterials.cc:38
G4Material * GetMaterial(G4String material) const
Get material by name.
static BDSParser * Instance()
Access method.
Definition: BDSParser.cc:28
Wrapper for particle definition.
A multilayer screen in a beam pipe.
Definition: BDSScreen.hh:39
void AddScreenLayer(G4double thickness, G4String material, G4int isSampler=0)
Add a screen layer.
Definition: BDSScreen.cc:68
A square mask of solid material with a square aperture.
Definition: BDSShield.hh:40
A class for a box or cylinder piece of 1 material.
Definition: BDSTarget.hh:36
An element that unnaturally shifts the beam across its length - a fudge volume.
Class for small control volume for circular macines.
A holder for any placement offsets and rotations for a BDSAcceleratorComponent.
G4Transform3D Transform3D() const
Get a transform to represent this tilt offset.
Transform in beam line that takes up no space.
type underlying() const
return underlying value (can be used in switch statement)
Undulator.
Definition: BDSUndulator.hh:39
Single cylindrical wire inside beam pipe.
const BDSIntegratorSet * IntegratorSet(G4String set)
Return the appropriate set of integrators to use for each magnet type.
BDSMagnetGeometryType DetermineMagnetGeometryType(G4String geometryType)
function to determine the enum type of the magnet geometry (case-insensitive)
G4bool WillIntersect(const G4ThreeVector &incomingNormal, const G4ThreeVector &outgoingNormal, const G4double &zSeparation, const BDSExtent &incomingExtent, const BDSExtent &outgoingExtent)
BDSCrystalType DetermineCrystalType(G4String crystalType)
function to determine the enum type of the cavity geometry (case-insensitive)
G4double SecondFringeFieldCorrection(BDSMagnetStrength const *strength, G4bool entranceOrExit)
Function to calculate the value of the second fringe field correction term.
BDSMagnet * BuildDipoleFringe(const GMAD::Element *element, G4double angleIn, G4double angleOut, const G4String &name, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, BDSFieldType dipoleFieldType)
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)
G4UserLimits * CreateUserLimits(G4UserLimits *defaultUL, G4double length, G4double fraction=1.6)
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)
std::vector< G4String > SplitOnWhiteSpace(const G4String &input)
Split a string on whitespace and return a vector of these 'words'.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
BDSCavityType DetermineCavityType(G4String cavityType)
function to determine the enum type of the cavity geometry (case-insensitive)
std::pair< G4ThreeVector, G4ThreeVector > CalculateFaces(G4double angleInIn, G4double angleOutIn)
Calculate input and output normal vector.
G4double FringeFieldCorrection(BDSMagnetStrength const *strength, G4bool entranceOrExit)
Function to calculate the value of the fringe field correction term.
Parser namespace for GMAD language. Combination of Geant4 and MAD.
ElementType
types of elements
Definition: elementtype.h:28
std::string typestr(ElementType type)
conversion from enum to string
Definition: elementtype.cc:29
Element class.
Definition: element.h:43
double hkick
fractional delta px for hkicker
Definition: element.h:70
std::string dicomDataFile
for CT, file for DICOM construction data
Definition: element.h:222
std::string spec
arbitrary specification to pass to beamline builder
Definition: element.h:218
void print(int ident=0) const
print method
Definition: element.cc:276
double twindow
thickness of window
Definition: element.h:132
double B
magnetic field
Definition: element.h:59
double aper4
beampipe information, new aperture model
Definition: element.h:109
std::string beampipeMaterial
beampipe information, new aperture model
Definition: element.h:111
double rmat33
rmatrix elements, only 4x4
Definition: element.h:95
double rmat11
rmatrix elements, only 4x4
Definition: element.h:85
double rmat43
rmatrix elements, only 4x4
Definition: element.h:99
std::list< std::string > layerMaterials
for screen
Definition: element.h:138
double scaling
Overall scaling of field strength.
Definition: element.h:50
double windowScreenGap
air gap between window and screen
Definition: element.h:134
double gradient
for rf cavities in V / m
Definition: element.h:74
double degraderOffset
for degrader
Definition: element.h:168
double wireAngle
for wirescanner
Definition: element.h:177
double aper2
beampipe information, new aperture model
Definition: element.h:107
double fintx
fringe field integral at the dipole exit
Definition: element.h:63
double kick4
rmatrix elements, only 4x4
Definition: element.h:84
double k2
sextupole
Definition: element.h:55
double rmat42
rmatrix elements, only 4x4
Definition: element.h:98
double aper1
beampipe information, new aperture model
Definition: element.h:106
double rmat41
rmatrix elements, only 4x4
Definition: element.h:97
double awakeMagnetOffsetX
for AWAKE spectrometer
Definition: element.h:147
double zdir
for 3d transform and laser
Definition: element.h:156
std::string namedVacuumVolumes
For imported geometry - identify vacuum volumes.
Definition: element.h:216
bool stripOuterVolume
For Element. Make it an assembly.
Definition: element.h:213
double screenPSize
for AWAKE spectrometer
Definition: element.h:143
double xdir
for 3d transform and laser
Definition: element.h:154
double rmat34
rmatrix elements, only 4x4
Definition: element.h:96
double psi
for 3d transforms
Definition: element.h:159
double wireOffsetX
for wirescanner
Definition: element.h:174
double rmat31
rmatrix elements, only 4x4
Definition: element.h:93
double wedgeLength
for degrader
Definition: element.h:165
double vhRatio
ratio of vertial to horizontal for some magnets
Definition: element.h:120
double waveLength
for laser wire and 3d transforms
Definition: element.h:158
double rmat22
rmatrix elements, only 4x4
Definition: element.h:90
std::string userTypeName
User component element type name.
Definition: element.h:46
double wireOffsetY
for wirescanner
Definition: element.h:175
double kick3
rmatrix elements, only 4x4
Definition: element.h:83
double rmat24
rmatrix elements, only 4x4
Definition: element.h:92
double tOffset
time offset used for phase calculation (ns)
Definition: element.h:78
std::list< std::string > biasMaterialList
Definition: element.h:192
double xsizeRight
individual collimator jaw half widths
Definition: element.h:126
double coilWidthFraction
Fraction of available h space the coil will take up.
Definition: element.h:121
double rmat44
rmatrix elements, only 4x4
Definition: element.h:100
double ks
solenoid
Definition: element.h:52
std::string fieldAll
Field for everything.
Definition: element.h:210
std::string region
region with range cuts
Definition: element.h:207
double wireDiameter
for wirescanner
Definition: element.h:172
double tmount
thickness of the screen mount
Definition: element.h:133
double wireOffsetZ
for wirescanner
Definition: element.h:176
std::list< double > ksl
skew multipole expansion
Definition: element.h:73
int numberWedges
for degrader
Definition: element.h:164
double kick1
rmatrix elements, only 4x4
Definition: element.h:81
std::list< double > knl
multipole expansion coefficients
Definition: element.h:72
std::string windowmaterial
for AWAKE spectrometer
Definition: element.h:148
double e2
output pole face rotation for bends
Definition: element.h:61
double phase
phase of rf cavity (rad)
Definition: element.h:77
double ysizeOut
collimator aperture or laser spotsize for laser
Definition: element.h:125
std::string fieldVacuum
Vacuum field.
Definition: element.h:209
bool autoColour
Automagically colour the external geometry.
Definition: element.h:214
double angle
bending angle
Definition: element.h:58
int hStyle
-1 = unset; 0 = false (ie c style); 1 = true, use hstyle
Definition: element.h:119
std::string geometryFile
For Element. File for external geometry.
Definition: element.h:212
std::string vacuumMaterial
beampipe information, new aperture model
Definition: element.h:112
double vkick
fractional delta py for vkicker
Definition: element.h:71
double E
electric field amplitude for rf cavities in V
Definition: element.h:75
double coilHeightFraction
Fraction of availalbe v space the coil will take up.
Definition: element.h:122
std::list< int > layerIsSampler
for screen
Definition: element.h:139
double screenYSize
Definition: element.h:135
double offsetX
offset X
Definition: element.h:127
double screenEndZ
for AWAKE spectrometer
Definition: element.h:144
double ysize
collimator aperture or laser spotsize for laser
Definition: element.h:124
double rmat23
rmatrix elements, only 4x4
Definition: element.h:91
std::string colour
Override colour for certain items.
Definition: element.h:225
double rmat13
rmatrix elements, only 4x4
Definition: element.h:87
double tilt
tilt
Definition: element.h:123
double materialThickness
for degrader
Definition: element.h:167
double l
length in metres
Definition: element.h:49
double kick
fractional delta p for either h or v kicker
Definition: element.h:69
double rmat14
rmatrix elements, only 4x4
Definition: element.h:88
double beampipeThickness
beampipe information, new aperture model
Definition: element.h:105
std::string dicomDataPath
for CT, file for DICOM construction data
Definition: element.h:221
double minimumKineticEnergy
minimum kinetic energy for user limits - respected on element by element basis
Definition: element.h:197
std::string scintmaterial
for AWAKE spectrometer
Definition: element.h:149
double ydir
for 3d transform and laser
Definition: element.h:155
double undulatorMagnetHeight
for undulator
Definition: element.h:183
std::string cavityModel
model for rf cavities
Definition: element.h:219
double e1
input pole face rotation for bends
Definition: element.h:60
double k1
quadrupole
Definition: element.h:54
std::string fieldOuter
Outer field.
Definition: element.h:208
double undulatorPeriod
for undulator
Definition: element.h:181
double wireLength
for wirescanner
Definition: element.h:173
double undulatorGap
for undulator
Definition: element.h:182
double poleStartZ
for AWAKE spectrometer
Definition: element.h:145
bool angleSet
Definition: element.h:236
std::string mountmaterial
for AWAKE spectrometer
Definition: element.h:150
double k4
decapole
Definition: element.h:57
double aper3
beampipe information, new aperture model
Definition: element.h:108
ElementType type
element enum
Definition: element.h:44
std::list< std::string > biasVacuumList
physics biasing list for the vacuum
Definition: element.h:194
double offsetY
offset Y
Definition: element.h:128
std::list< double > layerThicknesses
for screen
Definition: element.h:137
double scalingFieldOuter
Extra arbitrary scaling for outer field - compounded with 'scaling'.
Definition: element.h:51
double degraderHeight
for degrader
Definition: element.h:166
double tscint
thickness of scintillating part of screen
Definition: element.h:131
double k3
octupole
Definition: element.h:56
double screenWidth
for AWAKE spectrometer
Definition: element.h:146
double frequency
frequency for rf cavity in Hz
Definition: element.h:76
double kick2
rmatrix elements, only 4x4
Definition: element.h:82
double rmat12
rmatrix elements, only 4x4
Definition: element.h:86
std::string apertureType
beampipe information, new aperture model
Definition: element.h:110
double rmat21
rmatrix elements, only 4x4
Definition: element.h:89
double rmat32
rmatrix elements, only 4x4
Definition: element.h:94