BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSComponentFactory.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) BDSIM Collaboration, 2001 - 2026.
3
4This file is part of BDSIM.
5
6BDSIM is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published
8by the Free Software Foundation version 3 of the License.
9
10BDSIM is distributed in the hope that it will be useful, but
11WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
17*/
18#include "BDSComponentFactory.hh"
19
20// elements
21#ifdef USE_AWAKE
22#include "BDSAwakeScintillatorScreen.hh"
23#include "BDSAwakeSpectrometer.hh"
24#endif
25#include "BDSCavityElement.hh"
26#include "BDSCollimatorCrystal.hh"
27#include "BDSCollimatorElliptical.hh"
28#include "BDSCollimatorJaw.hh"
29#include "BDSCollimatorJawTip.hh"
30#include "BDSCollimatorRectangular.hh"
31#include "BDSCollimatorBeamMask.hh"
32#include "BDSColours.hh"
33#include "BDSColourFromMaterial.hh"
34#include "BDSComponentFactoryUser.hh"
35#ifdef USE_DICOM
36#include "BDSCT.hh"
37#include "BDSDicomIntersectVolume.hh"
38#endif
39#include "BDSDegrader.hh"
40#include "BDSDrift.hh"
41#include "BDSDump.hh"
42#include "BDSElement.hh"
43#include "BDSGaborLens.hh"
44#include "BDSLaserWire.hh"
45#include "BDSLaserWireNew.hh"
46#include "BDSLaserwireBuilder.hh"
47#include "BDSLine.hh"
48#include "BDSMagnet.hh"
49#include "BDSMuonCooler.hh"
50#include "BDSSamplerPlane.hh"
51#include "BDSScreen.hh"
52#include "BDSShield.hh"
53#include "BDSTarget.hh"
54#include "BDSTeleporter.hh"
55#include "BDSTerminator.hh"
56#include "BDSTiltOffset.hh"
57#include "BDSTransform3D.hh"
58#include "BDSWireScanner.hh"
59#include "BDSUndulator.hh"
60#include "BDSWarning.hh"
61
62// general
63#include "BDSAcceleratorComponentRegistry.hh"
64#include "BDSBeamlineIntegral.hh"
65#include "BDSBeamPipeFactory.hh"
66#include "BDSBeamPipeInfo.hh"
67#include "BDSBeamPipeType.hh"
68#include "BDSBendBuilder.hh"
69#include "BDSLine.hh"
70#include "BDSCavityInfo.hh"
71#include "BDSCavityFieldType.hh"
72#include "BDSCavityType.hh"
73#include "BDSCrystalInfo.hh"
74#include "BDSCrystalType.hh"
75#include "BDSDebug.hh"
76#include "BDSException.hh"
77#include "BDSExecOptions.hh"
78#include "BDSFieldEMRFCavity.hh"
79#include "BDSFieldInfo.hh"
80#include "BDSFieldFactory.hh"
81#include "BDSFieldType.hh"
82#include "BDSGlobalConstants.hh"
83#include "BDSGap.hh"
84#include "BDSGasCapillary.hh"
85#include "BDSGasJet.hh"
86#include "BDSIntegratorSet.hh"
87#include "BDSIntegratorSetType.hh"
88#include "BDSIntegratorType.hh"
89#include "BDSIntegratorDipoleFringe.hh"
90#include "BDSLaser.hh"
91#include "BDSMagnetOuterFactory.hh"
92#include "BDSMagnetOuterInfo.hh"
93#include "BDSMagnetGeometryType.hh"
94#include "BDSMagnetStrength.hh"
95#include "BDSMagnetType.hh"
96#include "BDSMaterials.hh"
97#include "BDSMuonCoolerBuilder.hh"
98#include "BDSParser.hh"
99#include "BDSParticleDefinition.hh"
100#include "BDSUtilities.hh"
101
102#include "globals.hh" // geant4 types / globals
103#include "G4String.hh"
104#include "G4Transform3D.hh"
105
106#include "CLHEP/Units/SystemOfUnits.h"
107#include "CLHEP/Units/PhysicalConstants.h"
108
109#include "parser/element.h"
110#include "parser/elementtype.h"
111#include "parser/cavitymodel.h"
112#include "parser/newcolour.h"
113#include "parser/crystal.h"
114#include "parser/laser.h"
115
116#include <cmath>
117#include <limits>
118#include <string>
119#include <utility>
120
121using namespace GMAD;
122
124
126 G4bool usualPrintOut):
127 userComponentFactory(userComponentFactoryIn),
128 lengthSafety(BDSGlobalConstants::Instance()->LengthSafety()),
129 thinElementLength(BDSGlobalConstants::Instance()->ThinElementLength()),
130 includeFringeFields(BDSGlobalConstants::Instance()->IncludeFringeFields()),
131 yokeFields(BDSGlobalConstants::Instance()->YokeFields()),
132 defaultModulator(nullptr),
133 integralUpToThisComponent(nullptr),
134 synchronousTAtMiddleOfThisComponent(0),
135 integratorSetType(BDSGlobalConstants::Instance()->IntegratorSet())
136{
137 integratorSet = BDS::IntegratorSet(integratorSetType);
138 if (usualPrintOut)
139 {G4cout << __METHOD_NAME__ << "using \"" << integratorSetType << "\" set of integrators" << G4endl;}
140
141 PrepareColours(); // prepare colour definitions from parser
142 PrepareCavityModels(); // prepare rf cavity model info from parser
143 PrepareCrystals(); // prepare crystal model info from parser
144 PrepareLasers();
145
146 // TBC - leave as nullptr
147 //defaultModulator = BDSFieldFactory::Instance()->GetModulatorDefinition(BDSGlobalConstants::Instance()->FieldModulator());
148}
149
150BDSComponentFactory::~BDSComponentFactory()
151{
152 for (const auto& info : cavityInfos)
153 {delete info.second;}
154 for (const auto& info : crystalInfos)
155 {delete info.second;}
156 for (auto laser : lasers)
157 {delete laser.second;}
158
159 // Deleted here although not used directly here as new geometry can only be
160 // created through this class.
161 try
162 {//no exceptions in destructor
165 }
166 catch (...)
167 {;}
168}
169
171 Element const* prevElementIn,
172 Element const* nextElementIn,
173 BDSBeamlineIntegral& integral)
174{
175 switch (elementIn->type)
176 {
177 case ElementType::_MARKER:
178 case ElementType::_LINE:
179 case ElementType::_REV_LINE:
180 {return nullptr;}
181 default:
182 {break;}
183 }
184#ifdef BDSDEBUG
185 G4cout << elementIn->name << "\t " << integral.arcLength/CLHEP::m << "\t " << integral.synchronousTAtEnd << G4endl;
186#endif
187
188 element = elementIn;
189 prevElement = prevElementIn;
190 nextElement = nextElementIn;
191 integralUpToThisComponent = &integral; // <- this is used for brho and beta throughout this factory for the current call of it
192 synchronousTAtMiddleOfThisComponent = integralUpToThisComponent->ProvideSynchronousTAtCentreOfNextElement(elementIn);
193 G4double angleIn = 0.0;
194 G4double angleOut = 0.0;
195 G4bool registered = false;
196 // Used for multiple instances of the same element but different poleface rotations.
197 // Ie only a drift is modified to match the pole face rotation of a magnet.
198 G4bool differentFromDefinition = false;
199
200#ifdef BDSDEBUG
201 G4cout << __METHOD_NAME__ << "named: \"" << element->name << "\"" << G4endl;
202#endif
203 G4String searchName = element->name;
204 if (integral.rigidityCount > 0)
205 {searchName += "_" + std::to_string(integral.rigidityCount);}
206 if (BDSAcceleratorComponentRegistry::Instance()->IsRegistered(searchName, integral.designParticle.BRho()))
207 {registered = true;}
208
209 if (element->type == ElementType::_DRIFT)
210 {
211 if (prevElement)
212 {angleIn = OutgoingFaceAngle(prevElement);}
213 if (nextElement)
214 {angleOut = IncomingFaceAngle(nextElement);}
215
216 //if drift has been modified at all
217 if (BDS::IsFinite(angleIn) || BDS::IsFinite(angleOut))
218 {differentFromDefinition = true;}
219 }
220 else if (element->type == ElementType::_RBEND)
221 {// bend builder will construct it to match - but here we just know it's different
222 // match a previous rbend with half the angle
223 if (prevElement)
224 {
225 if (prevElement->type == ElementType::_RBEND) // also if includeFringeFields
226 {differentFromDefinition = true;}
227 }
228 // match the upcoming rbend with half the angle
229 if (nextElement)
230 {
231 if (nextElement->type == ElementType::_RBEND) // also if includeFringeFields
232 {differentFromDefinition = true;}
233 }
234 }
235 else if (element->type == ElementType::_SBEND)
236 {
237 if (prevElement)
238 {
239 if (prevElement->type == ElementType::_SBEND && includeFringeFields)
240 {differentFromDefinition = true;}
241 }
242 if (nextElement)
243 {
244 if (nextElement->type == ElementType::_SBEND && includeFringeFields)
245 {differentFromDefinition = true;}
246 }
247 }
248 else if (element->type == ElementType::_THINMULT || (element->type == ElementType::_MULT && !HasSufficientMinimumLength(element, false)) || (element->type == ElementType::_THINRMATRIX))
249 {
250 // thinmultipole only uses one angle - so `angleIn`
252 {// both exist
253 ElementType prevType = prevElement->type;
254 ElementType nextType = nextElement->type;
255 if (prevType == ElementType::_DRIFT && nextType != ElementType::_DRIFT)
256 {angleIn = -IncomingFaceAngle(nextElement);} // previous is drift which will match next
257 else if (prevType != ElementType::_DRIFT && nextType == ElementType::_DRIFT)
258 {angleIn = OutgoingFaceAngle(prevElement);} // next is drift which will match prev
259 }
260 else if (prevElement)
261 {angleIn = OutgoingFaceAngle(prevElement);} // only previous element - match it
262 else
263 {angleIn = IncomingFaceAngle(nextElement);} // only next element - match it
264
265 // flag as unique only if the angleIn is changed and the geometry is built at an angle
266 if (BDS::IsFinite(angleIn))
267 {differentFromDefinition = true;}
268 }
269 else if (element->type == ElementType::_SOLENOID)
270 {// we build incoming / outgoing fringe fields for solenoids
271 if (prevElement)
272 {
273 if (prevElement->type == ElementType::_SOLENOID)
274 {differentFromDefinition = true;}
275 }
276 if (nextElement)
277 {
278 if (nextElement->type == ElementType::_SOLENOID)
279 {differentFromDefinition = true;}
280 }
281 }
282
283 // Check if the component already exists and return that.
284 // Don't use the registry for output elements since reliant on unique name.
285 if (registered && !differentFromDefinition)
286 {
287#ifdef BDSDEBUG
288 G4cout << __METHOD_NAME__ << "using already manufactured component" << G4endl;
289#endif
290 integral.Integrate(*elementIn); // update beamline integral for this component
291 return BDSAcceleratorComponentRegistry::Instance()->GetComponent(searchName, integral.designParticle.BRho());
292 }
293
294 // Update name for this component
295 elementName = searchName;
296
297 if (differentFromDefinition)
298 {
299 G4int val;
301 {
303 val = 0;
304 }
305 else
306 {
307 val = modifiedElements[elementName] + 1;
309 }
310 elementName += "_mod_" + std::to_string(val);
311 }
312
313 BDSAcceleratorComponent* component = nullptr;
314 try
315 {
316 switch(element->type)
317 {
318 case ElementType::_DRIFT:
319 {component = CreateDrift(angleIn, angleOut); break;}
320 case ElementType::_RF:
321 {
322 component = CreateRF(RFFieldDirection::z);
323 differentFromDefinition = true; // unique phase for every placement in beam line
324 break;
325 }
326 case ElementType::_RFX:
327 {
328 component = CreateRF(RFFieldDirection::x);
329 differentFromDefinition = true; // unique phase for every placement in beam line
330 break;
331 }
332 case ElementType::_RFY:
333 {
334 component = CreateRF(RFFieldDirection::y);
335 differentFromDefinition = true; // unique phase for every placement in beam line
336 break;
337 }
338 case ElementType::_SBEND:
339 {component = CreateSBend(); break;}
340 case ElementType::_RBEND:
341 {component = CreateRBend(); break;}
342 case ElementType::_HKICKER:
343 {component = CreateKicker(KickerType::horizontal); break;}
344 case ElementType::_VKICKER:
345 {component = CreateKicker(KickerType::vertical); break;}
346 case ElementType::_KICKER:
347 case ElementType::_TKICKER:
348 {component = CreateKicker(KickerType::general); break;}
349 case ElementType::_QUAD:
350 {component = CreateQuad(); break;}
351 case ElementType::_SEXTUPOLE:
352 {component = CreateSextupole(); break;}
353 case ElementType::_OCTUPOLE:
354 {component = CreateOctupole(); break;}
355 case ElementType::_DECAPOLE:
356 {component = CreateDecapole(); break;}
357 case ElementType::_MULT:
358 {
359 if(!BDS::IsFinite(element->l))
360 {
361 component = CreateThinMultipole(angleIn);
362 break;
363 }
364 component = CreateMultipole();
365 break;
366 }
367 case ElementType::_THINMULT:
368 {component = CreateThinMultipole(angleIn); break;}
369 case ElementType::_ELEMENT:
370 {component = CreateElement(); break;}
371 case ElementType::_SOLENOID:
372 {component = CreateSolenoid(); break;}
373 case ElementType::_ECOL:
374 {component = CreateEllipticalCollimator(); break;}
375 case ElementType::_RCOL:
376 {component = CreateRectangularCollimator(); break;}
377 case ElementType::_BMCOL:
378 {component = CreateBeamMaskCollimator(); break;}
379 case ElementType::_GASCAP:
380 {component = CreateGasCapillary(); break;}
381 case ElementType::_GASJET:
382 {component = CreateGasJet(); break;}
383 case ElementType::_TARGET:
384 {component = CreateTarget(); break;}
385 case ElementType::_JCOL:
386 {component = CreateJawCollimator(); break;}
387 case ElementType::_JCOLTIP:
388 {component = CreateTipJawCollimator(); break;}
389 case ElementType::_MUONCOOLER:
390 {component = CreateMuonCooler(); break;}
391 case ElementType::_MUONSPOILER:
392 {component = CreateMuonSpoiler(); break;}
393 case ElementType::_SHIELD:
394 {component = CreateShield(); break;}
395 case ElementType::_DEGRADER:
396 {component = CreateDegrader(); break;}
397 case ElementType::_WIRESCANNER:
398 {component = CreateWireScanner(); break;}
399 case ElementType::_GAP:
400 {component = CreateGap(); break;}
401 case ElementType::_CRYSTALCOL:
402 {component = CreateCrystalCollimator(); break;}
403 case ElementType::_LASERWIREOLD:
404 {component = CreateLaser(); break;}
405 case ElementType::_SCREEN:
406 {component = CreateScreen(); break;}
407 case ElementType::_TRANSFORM3D:
408 {component = CreateTransform3D(); break;}
409 case ElementType::_THINRMATRIX:
410 {component = CreateThinRMatrix(angleIn, elementName); break;}
411 case ElementType::_PARALLELTRANSPORTER:
412 {component = CreateParallelTransporter(); break;}
413 case ElementType::_RMATRIX:
414 {component = CreateRMatrix(); break;}
415 case ElementType::_UNDULATOR:
416 {component = CreateUndulator(); break;}
417 case ElementType::_LASERWIRE:
418 {component = CreateLaserwire(synchronousTAtMiddleOfThisComponent); break;}
419 case ElementType::_USERCOMPONENT:
420 {
422 {throw BDSException(__METHOD_NAME__, "no user component factory registered");}
423 G4String typeName = G4String(element->userTypeName);
425 {throw BDSException(__METHOD_NAME__, "no such component \"" + element->userTypeName + "\" registered.");}
426 else
427 {
428 component = userComponentFactory->ConstructComponent(typeName,
429 element,
432 integral);
433 }
434 break;
435 }
436 case ElementType::_DUMP:
437 {component = CreateDump(); break;}
438 case ElementType::_GABORLENS:
439 {component = CreateGaborLens(); break;}
440 case ElementType::_CT:
441#ifdef USE_DICOM
442 {component = CreateCT(); break;}
443#else
444 {throw BDSException(__METHOD_NAME__, "ct element can't be used - not compiled with dicom module!");}
445#endif
446 case ElementType::_AWAKESCREEN:
447#ifdef USE_AWAKE
448 {component = CreateAwakeScreen(); break;}
449#else
450 throw BDSException(__METHOD_NAME__, "Awake Screen can't be used - not compiled with AWAKE module!");
451#endif
452 case ElementType::_AWAKESPECTROMETER:
453#ifdef USE_AWAKE
454 {component = CreateAwakeSpectrometer(); break;}
455#else
456 {throw BDSException(__METHOD_NAME__, "Awake Spectrometer can't be used - not compiled with AWAKE module!");}
457#endif
458 // common types, but nothing to do here
459 case ElementType::_MARKER:
460 case ElementType::_LINE:
461 case ElementType::_REV_LINE:
462 {component = nullptr; break;}
463 default:
464 {
465 G4cerr << __METHOD_NAME__ << "unknown type " << element->type << G4endl;
466 throw BDSException(__METHOD_NAME__, "");
467 break;
468 }
469 }
470 }
471 catch (BDSException& e)
472 {
473 e.AppendToMessage("\nBDSComponentFactory> Problem creating element \"" + element->name + "\"");
474 throw e;
475 }
476
477 // note this test will only be reached (and therefore the component registered)
478 // if both the component didn't exist and it has been constructed
479 if (component)
480 {
484 component->SetRegion(element->region);
485 // the minimum kinetic energy is only implemented in certain components
487
488 // infinite absorbers for collimators - must be done after SetMinimumKineticEnergy and
489 // specific to these elements. must be done before initialise too.
490 switch (element->type)
491 {
492 case ElementType::_ECOL:
493 case ElementType::_RCOL:
494 case ElementType::_JCOL:
495 case ElementType::_JCOLTIP:
496 case ElementType::_BMCOL:
497 {
498 if (BDSGlobalConstants::Instance()->CollimatorsAreInfiniteAbsorbers())
499 {component->SetMinimumKineticEnergy(std::numeric_limits<double>::max());}
500 break;
501 }
502 default:
503 {break;}
504 }
505
506 SetFieldDefinitions(element, component);
507 component->Initialise();
508 // register component and memory
509 BDSAcceleratorComponentRegistry::Instance()->RegisterComponent(component, integral.designParticle.BRho(), differentFromDefinition);
510
511 integral.Integrate(*elementIn); // update beamline integral for this component
512 }
513
514 return component;
515}
516
518 const G4double teleporterHorizontalWidth,
519 const G4Transform3D& transformIn)
520{
522 (*st)["length"] = teleporterLength; // convey length scale to integrator
523 BDSFieldInfo* vacuumFieldInfo = new BDSFieldInfo(BDSFieldType::teleporter,
524 BRho(),
525 BDSIntegratorType::teleporter,
526 st,
527 true,
528 transformIn);
529
530 G4cout << "---->creating Teleporter, " << "l = " << teleporterLength/CLHEP::m << "m" << G4endl;
531
532 return( new BDSTeleporter(teleporterLength, teleporterHorizontalWidth, vacuumFieldInfo));
533}
534
535BDSAcceleratorComponent* BDSComponentFactory::CreateDrift(G4double angleIn, G4double angleOut)
536{
538 {return nullptr;}
539
540 // Create normal vectors
541 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn, angleOut);
542
543 // current element tilt
544 G4double currentTilt = element->tilt * CLHEP::rad;
545 G4double prevTilt = 0;
546 G4double nextTilt = 0;
547 if (prevElement)
548 {prevTilt = prevElement->tilt * CLHEP::rad;}
549 if (nextElement)
550 {nextTilt = nextElement->tilt * CLHEP::rad;}
551 G4ThreeVector inputFaceNormal = faces.first.rotateZ(prevTilt - currentTilt);
552 G4ThreeVector outputFaceNormal = faces.second.rotateZ(nextTilt - currentTilt);
553
554 const G4double length = element->l*CLHEP::m;
555
556 BDSBeamPipeInfo* beamPipeInfo = PrepareBeamPipeInfo(element, inputFaceNormal, outputFaceNormal);
557
558 const BDSExtent extent = beamPipeInfo->Extent();
559 G4bool facesWillIntersect = BDS::WillIntersect(inputFaceNormal, outputFaceNormal,
560 length, extent, extent);
561
562 if (facesWillIntersect)
563 {
564 G4cerr << __METHOD_NAME__ << "Drift \"" << elementName
565 << "\" between \"";
566 if (prevElement)
567 {G4cerr << prevElement->name;}
568 else
569 {G4cerr << "none";}
570 G4cerr << "\" and \"";
571 if (nextElement)
572 {G4cerr << nextElement->name;}
573 else
574 {G4cerr << "none";}
575 G4cerr << "\" is too short given its width and the angle of its faces." << G4endl;
576 throw BDSException(__METHOD_NAME__, "");
577 }
578
579 return (new BDSDrift(elementName,
580 length,
581 beamPipeInfo));
582}
583
584BDSAcceleratorComponent* BDSComponentFactory::CreateRF(RFFieldDirection direction)
585{
587 {return nullptr;}
588
589 BDSFieldType fieldType;
590 switch (direction)
591 {// simple sinusoidal E field only
592 case RFFieldDirection::x:
593 {fieldType = BDSFieldType::rfconstantinx; break;}
594 case RFFieldDirection::y:
595 {fieldType = BDSFieldType::rfconstantiny; break;}
596 case RFFieldDirection::z:
597 {
598 G4String cftName = element->cavityFieldType.empty() ? BDSGlobalConstants::Instance()->CavityFieldType() : element->cavityFieldType;
600 fieldType = BDS::FieldTypeFromCavityFieldType(cft);
601
602 // optional more complex cavity field along z - done here only for the body and purposively
603 // excluded from the general setting of fieldVacuum later on which would overwrite it
604 if (!(element->fieldVacuum.empty()))
605 {
607 fieldType = field->FieldType();
608 }
609 break;
610 }
611 }
612
613 BDSIntegratorType intType = integratorSet->Integrator(fieldType);
614
615 // note cavity length is not the same as currentArcLength
616 G4double cavityLength = element->l * CLHEP::m;
617
618 // use cavity fringe option, includeFringeFields does not affect cavity fringes
619 G4bool buildCavityFringes = BDSGlobalConstants::Instance()->IncludeFringeFieldsCavities();
620 // don't build fringes for transverse rf cavities - none provided
621 if (fieldType == BDSFieldType::rfconstantinx || fieldType == BDSFieldType::rfconstantiny)
622 {buildCavityFringes = false;}
623
624 G4bool buildIncomingFringe = buildCavityFringes;
625 // only check if trying to build fringes to begin with as this check should only ever turn off fringe building
626 if (prevElement && buildIncomingFringe) // could be nullptr
627 {// only build fringe if previous element isn't another cavity
628 buildIncomingFringe = prevElement->type != ElementType::_RF;
629 }
630
631 G4bool buildOutgoingFringe = buildCavityFringes;
632 // only check if trying to build fringes to begin with as this check should only ever turn off fringe building
633 if (nextElement && buildOutgoingFringe) // could be nullptr
634 {// only build fringe if next element isn't another cavity
635 buildOutgoingFringe = nextElement->type != ElementType::_RF;
636 }
637
638 if (buildIncomingFringe)
639 {cavityLength -= thinElementLength;}
640 if (buildOutgoingFringe)
641 {cavityLength -= thinElementLength;}
642
643 // supply currentArcLength (not element length) to strength as it's needed
644 // for time offset from s=0 position
645 BDSMagnetStrength* stIn = nullptr; // deleted later if not needed
646 BDSMagnetStrength* stOut = nullptr;
647 BDSMagnetStrength* st = PrepareCavityStrength(element, fieldType, cavityLength, stIn, stOut);
648 // st already has the synchronous time information in it
649 G4Transform3D fieldTrans = CreateFieldTransform(element);
650 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
651 BRho(),
652 intType,
653 st,
654 true,
655 fieldTrans);
656 auto modulator = ModulatorDefinition(element);
657 vacuumField->SetModulatorInfo(modulator); // works even if none
658
659 // limit step length in field - crucial to this component
660 // to get the motion correct this has to be less than one oscillation.
661 // Don't set if frequency is zero as the field will have no oscillation, so we can integrate
662 // safely over longer steps without the field changing.
664 {
665 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
666 G4double stepFraction = 0.025;
667 G4double period = 1. / (element->frequency*CLHEP::hertz);
668 // choose the smallest length scale based on the length of the component of the distance
669 // travelled in one period - so improved for high frequency fields
670 G4double limit = std::min((*st)["length"], integralUpToThisComponent->designParticle.Velocity()*period) * stepFraction;
671 auto ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
672 if (ul != defaultUL)
673 {vacuumField->SetUserLimits(ul);}
674 }
675
676 BDSCavityInfo* cavityInfo = PrepareCavityModelInfo(element, (*st)["frequency"]);
677
678 // update 0 point of field with geometry
679 // this relies on BDSFieldInfo not copying the strength so when we update the contents of *st
680 // it is applied inside vaccumFieldInfo
681 (*st)["equatorradius"] = cavityInfo->equatorRadius;
682 G4Material* vacuumMaterial = PrepareVacuumMaterial(element);
683
684 // aperture radius. Default is beam pipe radius / aper1 if a cavity model isn't specified.
685 G4double cavityApertureRadius = cavityInfo->irisRadius;
686
687 if (!BDS::IsFinite((*st)["efield"]) || !buildCavityFringes)
688 {// ie no rf field - don't bother with fringe effects
689 delete stIn;
690 delete stOut;
691 return new BDSCavityElement(elementName,
692 cavityLength,
693 vacuumMaterial,
694 vacuumField,
695 cavityInfo);
696 }
697
698 BDSLine* cavityLine = new BDSLine(elementName);
699
700 if (buildIncomingFringe)
701 {
702 //BDSMagnetStrength* stIn = PrepareCavityFringeStrength(element, cavityLength, currentArcLength, true);
703 // update with info for fringe matrix elements
704 (*stIn)["rmat11"] = 1;
705 (*stIn)["rmat21"] = 0;
706 (*stIn)["rmat22"] = 1;
707 (*stIn)["rmat33"] = 1;
708 (*stIn)["rmat43"] = 0;
709 (*stIn)["rmat44"] = 1;
710 (*stIn)["length"] = BDSGlobalConstants::Instance()->ThinElementLength();
711 (*stIn)["isentrance"] = true;
712 auto cavityFringeIn = CreateCavityFringe(0, stIn, elementName + "_fringe_in", cavityApertureRadius, modulator);
713 cavityLine->AddComponent(cavityFringeIn);
714 }
715 else
716 {delete stIn;}
717
718 auto cavity = new BDSCavityElement(elementName,
719 cavityLength,
720 vacuumMaterial,
721 vacuumField,
722 cavityInfo);
723
724 cavityLine->AddComponent(cavity);
725
726 if (buildOutgoingFringe)
727 {
728 //BDSMagnetStrength* stOut = PrepareCavityFringeStrength(element, cavityLength, currentArcLength, false);
729 // update with info for fringe matrix elements
730 (*stOut)["rmat11"] = 1;
731 (*stOut)["rmat21"] = 0;
732 (*stOut)["rmat22"] = 1;
733 (*stOut)["rmat33"] = 1;
734 (*stOut)["rmat43"] = 0;
735 (*stOut)["rmat44"] = 1;
736 (*stOut)["length"] = BDSGlobalConstants::Instance()->ThinElementLength();
737 (*stOut)["isentrance"] = false;
738 auto cavityFringeIn = CreateCavityFringe(0, stOut, elementName + "_fringe_out", cavityApertureRadius, modulator);
739 cavityLine->AddComponent(cavityFringeIn);
740 }
741 else
742 {delete stOut;}
743
744 return cavityLine;
745}
746
747BDSAcceleratorComponent* BDSComponentFactory::CreateSBend()
748{
750 {return nullptr;}
751
753
754 // don't check here on whether the possibly next / previous sbend will clash with
755 // pole face angles - let that be checked after element construction in the beamline
757 SetBeta0(st);
758 G4double angle = 0;
759 G4double field = 0;
761 (*st)["angle"] = angle;
762 (*st)["field"] = field*element->scaling;
763 (*st)["by"] = 1;// bx,by,bz is unit field direction, so (0,1,0) here
764 (*st)["length"] = element->l * CLHEP::m; // arc length
765 (*st)["scaling"]= element->scaling;
766 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
767 auto modulator = ModulatorDefinition(element, true);
768
769 // quadrupole component
771 {(*st)["k1"] = element->scaling*element->k1;}
772
773#ifdef BDSDEBUG
774 G4cout << "Angle (rad) " << (*st)["angle"] / CLHEP::rad << G4endl;
775 G4cout << "Field (T) " << (*st)["field"] / CLHEP::tesla << G4endl;
776#endif
777 // geometric face angles (can be different from specification depending on integrator set used)
778 G4double incomingFaceAngle = IncomingFaceAngle(element);
779 G4double outgoingFaceAngle = OutgoingFaceAngle(element);
780
781 auto sBendLine = BDS::BuildSBendLine(elementName, element, st, BRho(), integratorSet,
782 incomingFaceAngle, outgoingFaceAngle,
784
785 return sBendLine;
786}
787
788BDSAcceleratorComponent* BDSComponentFactory::CreateRBend()
789{
791 {return nullptr;}
792
794
795 // don't check here on whether the possibly next / previous sbend will clash with
796 // pole face angles - let that be checked after element construction in the beamline
797
799 SetBeta0(st);
800 G4double arcLength = 0, chordLength = 0, field = 0, angle = 0;
801 CalculateAngleAndFieldRBend(element, BRho(), arcLength, chordLength, field, angle);
802
803 (*st)["angle"] = angle;
804 (*st)["field"] = field * element->scaling;
805 (*st)["by"] = 1;// bx,by,bz is unit field direction, so (0,1,0) here
806 (*st)["length"] = arcLength;
807 (*st)["scaling"]= element->scaling;
808 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
809
810 // Quadrupole component
812 {(*st)["k1"] = element->scaling * element->k1;}
813
814 // geometric face angles (can be different from specification depending on integrator set used)
815 G4double incomingFaceAngle = IncomingFaceAngle(element);
816 G4double outgoingFaceAngle = OutgoingFaceAngle(element);
817
818 // Check the faces won't overlap due to too strong an angle with too short a magnet
819 auto bp = PrepareBeamPipeInfo(element);
821 -incomingFaceAngle, -outgoingFaceAngle,
822 bp, element->yokeOnInside);
823 CheckBendLengthAngleWidthCombo(arcLength, (*st)["angle"], oiCheck->MinimumIntersectionRadiusRequired(), elementName);
824 delete oiCheck;
825 delete bp;
826
827 // the above in / out face angles are not w.r.t. the local coords - subtract angle/2 to convert
828 // this may seem like undoing the += in the functions, but they're used for the beam pipes
829 // and matching.
830 incomingFaceAngle -= 0.5*angle;
831 outgoingFaceAngle -= 0.5*angle;
832
834 BRho(), st, integratorSet,
835 incomingFaceAngle, outgoingFaceAngle,
838 return rbendline;
839}
840
842 G4double& vkick,
843 const KickerType type) const
844{
845 G4bool kickFinite = BDS::IsFinite(element->kick);
846 switch (type)
847 {
848 case KickerType::horizontal:
849 {
850 hkick = kickFinite ? element->kick : element->hkick;
851 // backwards compatibility - if both are zero but angle if finite
852 // for this element - use that.
853 if (!BDS::IsFinite(hkick) && BDS::IsFinite(element->angle))
854 {hkick = element->angle;} //+ve to match hkick definition
855 vkick = 0;
856 break;
857 }
858 case KickerType::vertical:
859 {
860 vkick = kickFinite ? element->kick : element->vkick;
861 // backwards compatibility - if both are zero but angle if finite
862 // for this element - use that.
863 if (!BDS::IsFinite(vkick) && BDS::IsFinite(element->angle))
864 {vkick = element->angle;} //+ve to match vkick definition
865 hkick = 0;
866 break;
867 }
868 case KickerType::general:
869 {
870 hkick = element->hkick;
871 vkick = element->vkick;
872 // element->kick will be ignored
874 {BDS::Warning(__METHOD_NAME__, "'kick' parameter defined in element \"" + elementName + "\" but will be ignored as general kicker.");}
875 }
876 default:
877 {break;}
878 }
879}
880
881BDSAcceleratorComponent* BDSComponentFactory::CreateKicker(KickerType type)
882{
883 const G4String baseName = elementName;
885 SetBeta0(st);
886 BDSFieldType fieldType = BDSFieldType::dipole;
887 BDSIntegratorType intType = BDSIntegratorType::g4classicalrk4; // default
888 G4double chordLength;
889 G4double scaling = element->scaling;
890 G4double hkick = 0;
891 G4double vkick = 0;
892 GetKickValue(hkick, vkick, type);
893 (*st)["scaling"] = scaling; // needed in kicker fringes otherwise default is zero
894 (*st)["hkick"] = scaling * hkick;
895 (*st)["vkick"] = scaling * vkick;
896 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
897
898 // create fringe magnet strengths. Copies supplied strength object so it should contain all
899 // the kicker strength information as well as the added fringe information
900 BDSMagnetStrength* fringeStIn = BDS::GetFringeMagnetStrength(element,
901 st,
902 0,
903 element->e1,
904 element->e2,
905 element->fintx,
906 true);
907 BDSMagnetStrength* fringeStOut = new BDSMagnetStrength(*fringeStIn);
908 (*fringeStOut)["isentrance"] = false;
909
910 // check if the fringe effect is finite
911 G4bool finiteEntrFringe = false;
912 G4bool finiteExitFringe = false;
913 if (BDS::IsFinite(BDS::FringeFieldCorrection(fringeStIn, true)) ||
915 {finiteEntrFringe = true;}
916 if (BDS::IsFinite(BDS::FringeFieldCorrection(fringeStOut, true)) ||
918 {finiteExitFringe = true;}
919
920 // only build the fringe elements if the poleface rotation or fringe field correction terms are finite
921 G4bool buildEntranceFringe = false;
922 G4bool buildExitFringe = false;
923 if (BDS::IsFinite(element->e1) || finiteEntrFringe)
924 {buildEntranceFringe = true;}
925 if (BDS::IsFinite(element->e2) || finiteExitFringe)
926 {buildExitFringe = true;}
928 {
929 buildEntranceFringe = false;
930 buildExitFringe = false;
931 }
932
933 G4bool isThin = false; // required for setting field as 'thin' later (which controls geant tracking error parameters)
934 if (!HasSufficientMinimumLength(element, false)) // false for don't print warning
935 {// thin kicker
936 fieldType = BDSFieldType::bfieldzero;
937 intType = BDSIntegratorType::kickerthin;
938 chordLength = thinElementLength;
939 isThin = true;
940
941 // Fringe and poleface effects for a thin kicker require an effective bending radius, rho.
942 // Lack of length and angle knowledge means the field is the only way rho can be calculated.
943 // A zero field would lead to div by zero errors in the integrator constructor, therefore
944 // do not replace the kicker magnet strength with the fringe magnet strength which should prevent
945 // any fringe/poleface effects from ever being applied.
946 if (!BDS::IsFinite(element->B))
947 {
948 // only print warning if a poleface or fringe field effect was specified
949 if (buildEntranceFringe || buildExitFringe)
950 {
951 G4cout << __METHOD_NAME__ << "WARNING - finite B field required for kicker pole face and fringe fields,"
952 " effects are unavailable for element ""\"" << elementName << "\"." << G4endl;
953 }
954 buildEntranceFringe = false;
955 buildExitFringe = false;
956 }
957 else if (type == KickerType::general)
958 {
959 // A thin kicker or tkicker element has possible hkick and vkick combination, meaning the
960 // field direction cannot be assumed. Therefore, we are unsure of poleface angle and fringe
961 // effects so don't replace the kicker magnet strength with the fringe magnet strength
962
963 // only print warning if a poleface or fringe field effect was specified
964 if (buildEntranceFringe || buildExitFringe)
965 {
966 G4cerr << __METHOD_NAME__ << " Poleface and fringe field effects are unavailable "
967 << "for the thin (t)kicker element ""\"" << elementName << "\"." << G4endl;
968 }
969 buildEntranceFringe = false;
970 buildExitFringe = false;
971 }
972
973 else
974 {// Good to apply fringe effects.
975 // overwrite magnet strength with copy of fringe strength. Should be safe as it has the
976 // kicker information from copying previously.
977 delete st;
978 st = new BDSMagnetStrength(*fringeStIn);
979 // supply the scaled field for a thin kicker field as it is required to calculate
980 // the effective bending radius needed for fringe field and poleface effects
981 (*st)["field"] = element->B * CLHEP::tesla * scaling;
982 (*fringeStIn) ["field"] = (*st)["field"];
983 (*fringeStOut) ["field"] = (*st)["field"];
984
985 // set field for vertical kickers - element needs rotating as poleface rotation is assumed
986 // to be about the vertical axis unless explicitly tilted.
987 if (type == KickerType::vertical)
988 {(*st)["bx"] = 1;}
989 else
990 {(*st)["by"] = 1;}
991 }
992 }
993 else
994 {// thick kicker
995 chordLength = element->l*CLHEP::m;
996 // sin(angle) = dP -> angle = sin^-1(dP)
997 G4double angleX = std::asin(hkick * scaling);
998 G4double angleY = std::asin(vkick * scaling);
999
1000 if (std::isnan(angleX))
1001 {throw BDSException(__METHOD_NAME__, "hkick too strong for element \"" + element->name + "\" ");}
1002 if (std::isnan(angleY))
1003 {throw BDSException(__METHOD_NAME__, "vkick too strong for element \"" + element->name + "\" ");}
1004
1005 // Setup result variables - 'x' and 'y' refer to the components along the direction
1006 // the particle will change. These will therefore not be Bx and By.
1007 G4double fieldX = 0;
1008 G4double fieldY = 0;
1009
1010 // if B is specified and hkick and vkick (including backwards compatible check on
1011 // 'angle') are not, then use the field for the appropriate component
1012 // can only be 1d in this case -> doesn't work for tkicker
1013 if (BDS::IsFinite(element->B) && (!BDS::IsFinite(hkick) && !BDS::IsFinite(vkick)))
1014 {
1015 switch (type)
1016 {// 'X' and 'Y' are the angle of bending here, not the B field direction.
1017 case KickerType::horizontal:
1018 case KickerType::general:
1019 {fieldX = element->B * CLHEP::tesla * scaling; break;}
1020 case KickerType::vertical:
1021 {fieldY = element->B * CLHEP::tesla * scaling; break;}
1022 default:
1023 {break;} // do nothing - no field - just for compiler warnings
1024 }
1025 }
1026 else
1027 {
1028 if (BDS::IsFinite(angleX))
1029 {// with comments
1030 // calculate the chord length of the arc through the field from the straight
1031 // ahead length for this element which here is 'chordLength'.
1032 G4double fieldChordLengthX = chordLength / std::cos(0.5*angleX);
1033
1034 // now calculate the bending radius
1035 G4double bendingRadiusX = fieldChordLengthX * 0.5 / sin(std::abs(angleX) * 0.5);
1036
1037 // no calculate the arc length of the trajectory based on each bending radius
1038 G4double arcLengthX = std::abs(bendingRadiusX * angleX);
1039
1040 // -ve here in horizontal only for convention matching
1041 fieldX = FieldFromAngle(-angleX, arcLengthX);
1042 } // else fieldX default is 0
1043
1044 if (BDS::IsFinite(angleY))
1045 {// same as x, no need for comments
1046 G4double fieldChordLengthY = chordLength / std::cos(0.5*angleY);
1047 G4double bendingRadiusY = fieldChordLengthY * 0.5 / sin(std::abs(angleY) * 0.5);
1048 G4double arcLengthY = std::abs(bendingRadiusY * angleY);
1049 fieldY = FieldFromAngle(angleY, arcLengthY);
1050 }
1051 }
1052
1053 // note field for kick in x is unit Y, hence B = (y,x,0) here
1054 // field calculated from scaled angle so no need to scale field here
1055 G4ThreeVector field = G4ThreeVector(fieldY, fieldX, 0);
1056 G4double fieldMag = field.mag();
1057 G4ThreeVector unitField = field.unit();
1058
1059 (*st)["field"] = fieldMag;
1060 (*st)["bx"] = unitField.x();
1061 (*st)["by"] = unitField.y();
1062
1063 // preserve sign of field strength for fringe integrators
1064 // needed to calculate correct value of rho
1065 (*fringeStIn)["field"] = (*st)["field"];
1066 (*fringeStOut)["field"] = (*st)["field"];
1067 if (fieldX < 0 || fieldY < 0)
1068 {
1069 (*fringeStIn)["field"] *= -1;
1070 (*fringeStOut)["field"] *= -1;
1071 }
1072 }
1073
1074 (*fringeStIn) ["bx"] = (*st)["bx"];
1075 (*fringeStIn) ["by"] = (*st)["by"];
1076 (*fringeStOut)["bx"] = (*st)["bx"];
1077 (*fringeStOut)["by"] = (*st)["by"];
1078
1079 BDSMagnetType t;
1080 G4double defaultVHRatio = 1.5;
1081 switch (type)
1082 {
1083 case KickerType::horizontal:
1084 case KickerType::general:
1085 {t = BDSMagnetType::hkicker; break;}
1086 case KickerType::vertical:
1087 {
1088 t = BDSMagnetType::vkicker;
1089 defaultVHRatio = 1./defaultVHRatio; // inverted for vertical magnet
1090 break;
1091 }
1092 default:
1093 {t = BDSMagnetType::hkicker; break;}
1094 }
1095
1096 G4Transform3D fieldTrans = CreateFieldTransform(element);
1097 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
1098 BRho(),
1099 intType,
1100 st,
1101 true,
1102 fieldTrans);
1103 vacuumField->SetModulatorInfo(ModulatorDefinition(element, true)); // works even if none
1104 if (isThin)
1105 {vacuumField->SetFieldAsThin();}
1106
1107 G4bool yokeOnLeft = YokeOnLeft(element, st);
1108 auto bpInf = PrepareBeamPipeInfo(element);
1109
1110 // Decide on a default horizontalWidth for the kicker - try 0.3x ie smaller kicker
1111 // than typical magnet, but if that would not fit around the beam pipe - go back to
1112 // the default horizontalWidth. Code further along will warn if it still doesn't fit.
1113 const G4double globalDefaultHW = BDSGlobalConstants::Instance()->HorizontalWidth();
1114 G4double defaultHorizontalWidth = 0.3 * globalDefaultHW;
1115 BDSExtent bpExt = bpInf->Extent();
1116 G4double bpDX = bpExt.DX();
1117 G4double bpDY = bpExt.DY();
1118 if (bpDX > defaultHorizontalWidth && bpDX < globalDefaultHW)
1119 {defaultHorizontalWidth = globalDefaultHW;}
1120 else if (bpDY > defaultHorizontalWidth && bpDY > globalDefaultHW)
1121 {defaultHorizontalWidth = globalDefaultHW;}
1122
1123 auto magOutInf = PrepareMagnetOuterInfo(elementName, element, 0, 0, bpInf, yokeOnLeft,
1124 defaultHorizontalWidth, defaultVHRatio, 0.9);
1125
1126 BDSFieldInfo* outerField = nullptr;
1127 G4bool externalOuterField = !(element->fieldOuter.empty());
1128 if (yokeFields && !externalOuterField)
1129 {
1130 outerField = PrepareMagnetOuterFieldInfo(st,
1131 fieldType,
1132 bpInf,
1133 magOutInf,
1134 fieldTrans,
1136 BRho(),
1139 }
1140
1142 {
1143 delete fringeStIn;
1144 delete fringeStOut;
1145 // fringe effect applied in integrator so nothing more to do.
1146 // no outer field as thin component here
1147 return new BDSMagnet(t,
1148 baseName,
1149 chordLength,
1150 bpInf,
1151 magOutInf,
1152 vacuumField,
1153 0, nullptr, // default values for optional args (angle, outerFieldInfo)
1154 true); // isThin
1155 }
1156 else
1157 {
1158 BDSLine* kickerLine = new BDSLine(baseName);
1159 // subtract fringe length from kicker to preserve element length
1160 G4double kickerChordLength = chordLength;
1161 if (buildEntranceFringe)
1162 {kickerChordLength -= thinElementLength;}
1163 if (buildExitFringe)
1164 {kickerChordLength -= thinElementLength;}
1165
1166 if (buildEntranceFringe)
1167 {
1168 G4String entrFringeName = baseName + "_e1_fringe";
1169 BDSMagnet* startfringe = BDS::BuildDipoleFringe(element, 0, 0,
1170 entrFringeName,
1171 fringeStIn,
1172 BRho(),
1174 fieldType);
1175 kickerLine->AddComponent(startfringe);
1176 }
1177
1178 G4String kickerName = baseName;
1179 BDSMagnet* kicker = new BDSMagnet(t,
1180 kickerName,
1181 kickerChordLength,
1182 bpInf,
1183 magOutInf,
1184 vacuumField,
1185 0,
1186 outerField);
1187 kickerLine->AddComponent(kicker);
1188
1189 if (buildEntranceFringe)
1190 {
1191 G4String exitFringeName = baseName + "_e2_fringe";
1192 BDSMagnet* endfringe = BDS::BuildDipoleFringe(element, 0, 0,
1193 exitFringeName,
1194 fringeStOut, BRho(),
1195 integratorSet, fieldType);
1196 kickerLine->AddComponent(endfringe);
1197 }
1198 return kickerLine;
1199 }
1200}
1201
1202BDSAcceleratorComponent* BDSComponentFactory::CreateQuad()
1203{
1205 {return nullptr;}
1206
1208 (*st)["k1"] = element->k1 * element->scaling;
1209
1210 return CreateMagnet(element, st, BDSFieldType::quadrupole, BDSMagnetType::quadrupole);
1211}
1212
1213BDSAcceleratorComponent* BDSComponentFactory::CreateSextupole()
1214{
1216 {return nullptr;}
1217
1219 (*st)["k2"] = element->k2 * element->scaling;
1220
1221 return CreateMagnet(element, st, BDSFieldType::sextupole, BDSMagnetType::sextupole);
1222}
1223
1224BDSAcceleratorComponent* BDSComponentFactory::CreateOctupole()
1225{
1227 {return nullptr;}
1228
1230 (*st)["k3"] = element->k3 * element->scaling;
1231
1232 return CreateMagnet(element, st, BDSFieldType::octupole, BDSMagnetType::octupole);
1233}
1234
1235BDSAcceleratorComponent* BDSComponentFactory::CreateDecapole()
1236{
1238 {return nullptr;}
1239
1241 (*st)["k4"] = element->k4 * element->scaling;
1242
1243 return CreateMagnet(element, st, BDSFieldType::decapole, BDSMagnetType::decapole);
1244}
1245
1246BDSAcceleratorComponent* BDSComponentFactory::CreateMultipole()
1247{
1249 {return nullptr;}
1250
1252
1253 return CreateMagnet(element, st, BDSFieldType::multipole, BDSMagnetType::multipole,
1254 (*st)["angle"]); // multipole could bend beamline
1255}
1256
1257BDSAcceleratorComponent* BDSComponentFactory::CreateThinMultipole(G4double angleIn)
1258{
1260 BDSBeamPipeInfo* beamPipeInfo = PrepareBeamPipeInfo(element, angleIn, -angleIn);
1261 beamPipeInfo->beamPipeType = BDSBeamPipeType::circularvacuum;
1263 -angleIn, angleIn, beamPipeInfo);
1264 magnetOuterInfo->geometryType = BDSMagnetGeometryType::none;
1265
1266 BDSIntegratorType intType = integratorSet->multipoleThin;
1267 G4Transform3D fieldTrans = CreateFieldTransform(element);
1268 BDSFieldInfo* vacuumField = new BDSFieldInfo(BDSFieldType::multipole,
1269 BRho(),
1270 intType,
1271 st,
1272 true,
1273 fieldTrans);
1274 vacuumField->SetModulatorInfo(ModulatorDefinition(element, true));
1275 vacuumField->SetFieldAsThin();
1276
1277 BDSMagnet* thinMultipole = new BDSMagnet(BDSMagnetType::thinmultipole,
1280 beamPipeInfo,
1281 magnetOuterInfo,
1282 vacuumField,
1283 0, nullptr, // default values for optional args (angle, outerFieldInfo)
1284 true); // isThin
1285
1286 thinMultipole->SetExtent(BDSExtent(beamPipeInfo->aper1,
1287 beamPipeInfo->aper1,
1288 thinElementLength*0.5));
1289
1290 return thinMultipole;
1291}
1292
1293BDSAcceleratorComponent* BDSComponentFactory::CreateElement()
1294{
1296 {throw BDSException(__METHOD_NAME__, "insufficient length for element \"" + element->name + "\" - must specify a suitable length");}
1297
1298 // we don't specify the field explicitly here - this is done generically
1299 // in the main CreateComponent method with SetFieldDefinitions.
1300 std::vector<G4String> vacuumBiasVolumeNames = BDS::SplitOnWhiteSpace(G4String(element->namedVacuumVolumes));
1301
1302 G4double angle = -element->angle * CLHEP::rad; // this is to match the convention of MADX for bends
1303 G4double l = element->l * CLHEP::m;
1304 // calculate the arc length as that's what we need for BDSAcceleratorComponent,
1305 // which in turn calculates the chord length internally for placement.
1306 G4double arcLength = BDS::ArcLengthFromChordLength(l, angle);
1308 {arcLength = l;} // reset back to just l
1309 return (new BDSElement(elementName,
1310 arcLength,
1313 angle,
1314 &vacuumBiasVolumeNames,
1316 element->markAsCollimator,
1318}
1319
1320BDSAcceleratorComponent* BDSComponentFactory::CreateSolenoid()
1321{
1323 {return nullptr;}
1324
1326 SetBeta0(st);
1327 (*st)["bz"] = 1;
1328 G4double chordLength = element->l * CLHEP::m;
1329 // arbitrary fraction of 0.8 for current length of full length - used for the yoke field that varies in z
1330 (*st)["length"] = chordLength * 0.8;
1331 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
1332 const G4double scaling = element->scaling;
1333 if (BDS::IsFinite(element->B))
1334 {
1335 (*st)["field"] = scaling * element->B * CLHEP::tesla;
1336 (*st)["ks"] = (*st)["field"] / BRho();
1337 }
1338 else
1339 {
1340 (*st)["field"] = (scaling * element->ks / CLHEP::m) * BRho();
1341 (*st)["ks"] = element->ks;
1342 }
1343
1344 if (!BDS::IsFinite((*st)["field"]) || !includeFringeFields)
1345 {// ie no strength solenoid - don't bother with fringe effects
1346 return CreateMagnet(element, st, BDSFieldType::solenoid, BDSMagnetType::solenoid);
1347 }
1348
1349 // lambda to help - sign convention - this is the 'entry' version
1350 auto strength = [](G4double phi){
1352 (*s)["rmat11"] = 1;
1353 (*s)["rmat22"] = 1;
1354 (*s)["rmat33"] = 1;
1355 (*s)["rmat44"] = 1;
1356 (*s)["rmat41"] = -phi;
1357 (*s)["rmat23"] = phi;
1358 return s;
1359 };
1360
1361 G4bool buildIncomingFringe = true;
1362 if (prevElement) // could be nullptr
1363 {// only build fringe if previous element isn't another solenoid
1364 buildIncomingFringe = prevElement->type != ElementType::_SOLENOID;
1365 }
1366 G4bool buildOutgoingFringe = true;
1367 if (nextElement) // could be nullptr
1368 {// only build fringe if next element isn't another solenoid
1369 buildOutgoingFringe = nextElement->type != ElementType::_SOLENOID;
1370 }
1371
1372 G4double solenoidBodyLength = element->l * CLHEP::m;
1373
1374 if (buildIncomingFringe)
1375 {solenoidBodyLength -= thinElementLength;}
1376 if (buildOutgoingFringe)
1377 {solenoidBodyLength -= thinElementLength;}
1378
1379 // scale factor to account for reduced body length due to fringe placement.
1380 G4double lengthScaling = solenoidBodyLength / (element->l * CLHEP::m);
1381 G4double s = 0.5*(*st)["ks"] * lengthScaling; // already includes scaling
1382 BDSLine* bLine = new BDSLine(elementName);
1383
1384 auto modulator = ModulatorDefinition(element, true);
1385
1386 if (buildIncomingFringe)
1387 {
1388 auto stIn = strength(s);
1389 auto solenoidIn = CreateThinRMatrix(0, stIn, elementName + "_fringe_in",
1390 BDSIntegratorType::rmatrixthin, BDSFieldType::rmatrix, 0, modulator);
1391 bLine->AddComponent(solenoidIn);
1392 }
1393
1394 // Do not use CreateMagnet method as solenoid body length needs to be reduced to conserve total
1395 // element length. The solenoid strength is scaled accordingly.
1396
1398 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::solenoid);
1399 G4Transform3D fieldTrans = CreateFieldTransform(element);
1400 BDSFieldInfo* vacuumField = new BDSFieldInfo(BDSFieldType::solenoid,
1401 BRho(),
1402 intType,
1403 st,
1404 true,
1405 fieldTrans);
1406 vacuumField->SetModulatorInfo(modulator);
1407
1408 BDSMagnetOuterInfo* outerInfo = PrepareMagnetOuterInfo(elementName + "_centre", element, st, bpInfo);
1409 vacuumField->SetScalingRadius(outerInfo->innerRadius); // purely for completeness of information - not required
1410 BDSFieldInfo* outerField = nullptr;
1411
1412 // only make a default multipolar field if the yokeFields flag is on and
1413 // there isn't an 'outerField' specified for the element
1414 G4bool externalOuterField = !(element->fieldOuter.empty());
1415 if (yokeFields && !externalOuterField)
1416 {
1417 outerField = PrepareMagnetOuterFieldInfo(st,
1418 BDSFieldType::solenoid,
1419 bpInfo,
1420 outerInfo,
1421 fieldTrans,
1423 BRho(),
1425 modulator);
1426
1427 // determine a suitable radius for the current carrying coil of the solenoid
1428 // this defines the field geometry
1429 // there is no coil in our geometry so it is a bit fictional
1430 G4double beamPipeRadius = bpInfo->IndicativeRadius();
1431 G4double outerRadius = outerInfo->horizontalWidth * 0.5;
1432 G4double coilRadius = beamPipeRadius + 0.25*(outerRadius - beamPipeRadius);
1433 outerField->SetScalingRadius(coilRadius);
1434 }
1435
1436 auto solenoid = new BDSMagnet(BDSMagnetType::solenoid,
1438 solenoidBodyLength,
1439 bpInfo,
1440 outerInfo,
1441 vacuumField,
1442 0,
1443 outerField);
1444
1445 bLine->AddComponent(solenoid);
1446
1447 if (buildOutgoingFringe)
1448 {
1449 auto stOut = strength(-s);
1450 auto solenoidOut = CreateThinRMatrix(0, stOut, elementName + "_fringe_out",
1451 BDSIntegratorType::rmatrixthin, BDSFieldType::rmatrix, 0, modulator);
1452 bLine->AddComponent(solenoidOut);
1453 }
1454
1455 return bLine;
1456}
1457
1458BDSAcceleratorComponent* BDSComponentFactory::CreateParallelTransporter()
1459{
1461 return CreateMagnet(element, st, BDSFieldType::paralleltransporter, BDSMagnetType::paralleltransporter);
1462}
1463
1464BDSAcceleratorComponent* BDSComponentFactory::CreateRectangularCollimator()
1465{
1467 {return nullptr;}
1468 G4bool circularOuter = false;
1469 G4String apertureType = G4String(element->apertureType);
1470 if (apertureType == "circular")
1471 {circularOuter = true;}
1472 auto material = PrepareMaterial(element);
1474 element->l*CLHEP::m,
1476 material,
1478 element->xsize*CLHEP::m,
1479 element->ysize*CLHEP::m,
1480 element->xsizeOut*CLHEP::m,
1481 element->ysizeOut*CLHEP::m,
1482 PrepareColour(element, material),
1483 circularOuter);
1484}
1485
1486BDSAcceleratorComponent* BDSComponentFactory::CreateBeamMaskCollimator()
1487{
1489 {return nullptr;}
1490 G4bool circularOuter = false;
1491 G4String outerShape = G4String(element->outerShape);
1492 if (outerShape == "circular")
1493 {circularOuter = true;}
1495 element->l*CLHEP::m,
1497 PrepareHorizontalWidth(element, 0.15*CLHEP::m),
1500 element->xsize*CLHEP::m,
1501 element->ysize*CLHEP::m,
1502 element->xsize2*CLHEP::m,
1503 element->ysize2*CLHEP::m,
1504 element->offsetX*CLHEP::m,
1505 element->offsetY*CLHEP::m,
1506 element->offsetX2*CLHEP::m,
1507 element->offsetY2*CLHEP::m,
1508 element->tilt2*CLHEP::rad,
1510 circularOuter);
1511}
1512
1513BDSAcceleratorComponent* BDSComponentFactory::CreateGasCapillary()
1514{
1516 {return nullptr;}
1517
1518 G4bool circularOuter = false;
1519 G4String outerShape = G4String(element->outerShape);
1520 if (outerShape == "circular")
1521 {circularOuter = true;}
1522
1523 std::vector<std::string> materials{ element->layerMaterials.begin(), element->layerMaterials.end() };
1524
1525 return new BDSGasCapillary(elementName,
1526 element->l*CLHEP::m,
1528 PrepareHorizontalWidth(element, 0.15*CLHEP::m),
1529 BDSMaterials::Instance()->GetMaterial(materials[0]),
1530 BDSMaterials::Instance()->GetMaterial(materials[1]),
1531 BDSMaterials::Instance()->GetMaterial(materials[2]),
1532 element->xsize*CLHEP::m,
1533 element->materialThickness*CLHEP::m,
1534 circularOuter);
1535}
1536
1537BDSAcceleratorComponent* BDSComponentFactory::CreateGasJet()
1538{
1540 {return nullptr;}
1541
1542 return new BDSGasJet(elementName,
1543 element->l*CLHEP::m,
1546 element->xdir*CLHEP::m,
1547 element->ydir*CLHEP::m,
1548 element->zdir*CLHEP::m,
1549 element->phi*CLHEP::m,
1550 element->theta*CLHEP::m,
1551 element->psi*CLHEP::m,
1552 element->offsetX*CLHEP::m,
1553 element->offsetY*CLHEP::m);
1554}
1555
1556BDSAcceleratorComponent* BDSComponentFactory::CreateTarget()
1557{
1559 {return nullptr;}
1560 G4bool circularOuter = false;
1561 G4String apertureType = G4String(element->apertureType);
1562 if (apertureType == "circular")
1563 {circularOuter = true;}
1564 auto material = PrepareMaterial(element);
1565 return new BDSTarget(elementName,
1566 element->l*CLHEP::m,
1568 material,
1569 PrepareColour(element, material),
1570 circularOuter);
1571}
1572
1573BDSAcceleratorComponent* BDSComponentFactory::CreateEllipticalCollimator()
1574{
1576 {return nullptr;}
1577
1578 G4bool circularOuter = false;
1579 G4String apertureType = G4String(element->apertureType);
1580 if (apertureType == "circular")
1581 {circularOuter = true;}
1582 auto material = PrepareMaterial(element);
1584 element->l*CLHEP::m,
1586 material,
1588 element->xsize*CLHEP::m,
1589 element->ysize*CLHEP::m,
1590 element->xsizeOut*CLHEP::m,
1591 element->ysizeOut*CLHEP::m,
1592 PrepareColour(element, material),
1593 circularOuter);
1594}
1595
1596BDSAcceleratorComponent* BDSComponentFactory::CreateJawCollimator()
1597{
1599 {return nullptr;}
1600 auto material = PrepareMaterial(element);
1601 return new BDSCollimatorJaw(elementName,
1602 element->l*CLHEP::m,
1604 element->xsize*CLHEP::m,
1605 element->ysize*CLHEP::m,
1606 element->xsizeLeft*CLHEP::m,
1607 element->xsizeRight*CLHEP::m,
1608 element->jawTiltLeft*CLHEP::rad,
1609 element->jawTiltRight*CLHEP::rad,
1610 true,
1611 true,
1612 material,
1614 PrepareColour(element, material));
1615}
1616
1617BDSAcceleratorComponent* BDSComponentFactory::CreateTipJawCollimator()
1618{
1620 {return nullptr;}
1621 auto collimatorMaterial = PrepareMaterial(element);
1622 auto collimatorTipMaterial = PrepareTipMaterial(element);
1624 element->l*CLHEP::m,
1626 element->xsize*CLHEP::m,
1627 element->ysize*CLHEP::m,
1628 element->xsizeLeft*CLHEP::m,
1629 element->xsizeRight*CLHEP::m,
1630 element->jawTiltLeft*CLHEP::rad,
1631 element->jawTiltRight*CLHEP::rad,
1632 element->tipThickness*CLHEP::m,
1633 true,
1634 true,
1635 collimatorMaterial,
1636 collimatorTipMaterial,
1638 PrepareColour(element, collimatorMaterial),
1639 PrepareColour(element, collimatorTipMaterial));
1640}
1641
1642BDSAcceleratorComponent* BDSComponentFactory::CreateMuonSpoiler()
1643{
1645 {return nullptr;}
1646
1647 G4double elLength = element->l*CLHEP::m;
1648 BDSFieldInfo* outerField = nullptr;
1649 if (BDS::IsFinite(element->B))
1650 {
1652 (*st)["field"] = element->scaling * element->B * CLHEP::tesla;
1653 BDSIntegratorType intType = integratorSet->Integrator(BDSFieldType::muonspoiler);
1654 G4Transform3D fieldTrans = CreateFieldTransform(element);
1655 outerField = new BDSFieldInfo(BDSFieldType::muonspoiler,
1656 BRho(),
1657 intType,
1658 st,
1659 true,
1660 fieldTrans);
1661 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
1662 G4double limit = elLength / 20.0;
1663 auto ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
1664 if (ul != defaultUL)
1665 {outerField->SetUserLimits(ul);}
1666 }
1667 auto bpInfo = PrepareBeamPipeInfo(element);
1668
1669 return new BDSMagnet(BDSMagnetType::muonspoiler,
1671 elLength,
1672 bpInfo,
1673 PrepareMagnetOuterInfo(elementName, element, 0, 0, bpInfo), // 0 angled face in and out
1674 nullptr,
1675 0,
1676 outerField);
1677}
1678
1679BDSAcceleratorComponent* BDSComponentFactory::CreateMuonCooler()
1680{
1682 {return nullptr;}
1683
1685 auto beamPipeInfo = PrepareBeamPipeInfo(element);
1686 auto result = BDS::BuildMuonCooler(elementName,
1687 element->l * CLHEP::m,
1688 element->horizontalWidth * CLHEP::m,
1689 def,
1690 beamPipeInfo,
1691 BRho());
1692 return result;
1693}
1694
1695BDSAcceleratorComponent* BDSComponentFactory::CreateShield()
1696{
1698 {return nullptr;}
1699
1701
1702 G4Colour* colour = PrepareColour(element);
1703 G4Material* material = PrepareMaterial(element, "concrete");
1704
1705 BDSShield* shield = new BDSShield(elementName,
1706 element->l*CLHEP::m,
1708 element->xsize*CLHEP::m,
1709 element->ysize*CLHEP::m,
1710 material,
1711 colour,
1712 bpInfo);
1713 return shield;
1714}
1715
1716BDSAcceleratorComponent* BDSComponentFactory::CreateDegrader()
1717{
1719 {return nullptr;}
1720
1721 G4double degraderOffset;
1722 if ((element->materialThickness < 0) && (element->degraderOffset < 0))
1723 {throw BDSException(__METHOD_NAME__, "both \"materialThickness\" and \"degraderOffset\" are either undefined or < 0");}
1724 if (element->degraderOffset < 0)
1725 {throw BDSException(__METHOD_NAME__, "\"degraderOffset\" cannot be < 0");}
1727 {throw BDSException(__METHOD_NAME__, "\"materialThickness\" cannot be greater than the element length");}
1728
1729 if ((element->materialThickness <= 0) && (element->degraderOffset >= 0))
1730 {degraderOffset = element->degraderOffset*CLHEP::m;}
1731 else
1732 {
1733 //Width of wedge base
1734 G4double wedgeBasewidth = (element->l*CLHEP::m /element->numberWedges) - lengthSafety;
1735
1736 //Angle between hypotenuse and height (in the triangular wedge face)
1737 G4double theta = std::atan(wedgeBasewidth / (2.0*element->wedgeLength*CLHEP::m));
1738
1739 //Overlap distance of wedges
1740 G4double thicknessPerWedge = (wedgeBasewidth - element->materialThickness*CLHEP::m/element->numberWedges);
1741 degraderOffset = (0.5*thicknessPerWedge) * (std::sin(CLHEP::halfpi - theta) / std::sin(theta));
1742 }
1743
1744 // include base thickness in each wedge so it covers the whole beam aperture when set to the thickest
1745 // possible amount of material, otherwise a fraction of the beam wouldn't pass through the wedges.
1746 auto bpi = PrepareBeamPipeInfo(element);
1747 G4double baseWidth = bpi->aper1;
1748 delete bpi;
1749 auto material = PrepareMaterial(element);
1750 return (new BDSDegrader(elementName,
1751 element->l*CLHEP::m,
1754 element->wedgeLength*CLHEP::m,
1755 element->degraderHeight*CLHEP::m,
1756 degraderOffset,
1757 baseWidth,
1758 material,
1760 PrepareColour(element, material)));
1761}
1762
1763BDSAcceleratorComponent* BDSComponentFactory::CreateWireScanner()
1764{
1766 {return nullptr;}
1767
1769 {throw BDSException(__METHOD_NAME__, "\"angle\" parameter set for wirescanner \"" + elementName + "\" but this should not be set. Please unset and use \"wireAngle\".");}
1770
1771 G4ThreeVector wireOffset = G4ThreeVector(element->wireOffsetX * CLHEP::m,
1772 element->wireOffsetY * CLHEP::m,
1773 element->wireOffsetZ * CLHEP::m);
1774
1775 return (new BDSWireScanner(elementName,
1776 element->l*CLHEP::m,
1779 element->wireDiameter*CLHEP::m,
1780 element->wireLength*CLHEP::m,
1781 element->wireAngle*CLHEP::rad,
1782 wireOffset));
1783}
1784
1785BDSAcceleratorComponent* BDSComponentFactory::CreateUndulator()
1786{
1788 {return nullptr;}
1789
1790 const BDSFieldType undField = BDSFieldType::undulator;
1791
1793 BDSIntegratorType intType = integratorSet->Integrator(undField);
1794 G4Transform3D fieldTrans = CreateFieldTransform(element);
1796 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
1797 SetBeta0(st);
1798 (*st)["length"] = element->undulatorPeriod * CLHEP::m;
1799 (*st)["field"] = element->scaling * element->B * CLHEP::tesla;
1800
1801 BDSFieldInfo* vacuumFieldInfo = new BDSFieldInfo(undField,
1802 BRho(),
1803 intType,
1804 st,
1805 true,
1806 fieldTrans);
1807 vacuumFieldInfo->SetModulatorInfo(ModulatorDefinition(element, true));
1808 //BDSFieldInfo* outerFieldInfo = PrepareMagnetOuterFieldInfo(st, undField, bpInfo, 0, fieldTrans);
1809 BDSFieldInfo* outerFieldInfo = nullptr;
1810 // limit step length in field - crucial to this component
1811 // to get the motion correct this has to be less than one oscillation
1812 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
1813 G4double limit = (*st)["length"] * 0.075;
1814 auto ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
1815 if (ul != defaultUL)
1816 {vacuumFieldInfo->SetUserLimits(ul);}
1817
1818 return (new BDSUndulator(elementName,
1819 element->l * CLHEP::m,
1820 element->undulatorPeriod * CLHEP::m,
1821 element->undulatorMagnetHeight * CLHEP::m,
1823 element->undulatorGap * CLHEP::m,
1824 bpInfo,
1825 vacuumFieldInfo,
1826 outerFieldInfo,
1827 PrepareMaterial(element, "iron")));
1828}
1829
1830BDSAcceleratorComponent* BDSComponentFactory::CreateDump()
1831{
1832 G4double chordLength = element->l*CLHEP::m;
1834 {
1835 G4cout << __METHOD_NAME__ << "using default length of 1 mm for dump" << G4endl;
1836 chordLength = 1*CLHEP::mm;
1837 }
1838
1839 G4bool circular = false;
1840 G4String apertureType = G4String(element->apertureType);
1841 if (apertureType == "circular")
1842 {circular = true;}
1843 else if (apertureType != "rectangular" && !apertureType.empty())
1844 {throw BDSException(__METHOD_NAME__, "unknown shape for dump: \"" + apertureType + "\"");}
1845
1846 BDSDump* result = new BDSDump(elementName,
1847 chordLength,
1849 circular);
1850 return result;
1851}
1852
1853#ifdef USE_DICOM
1854BDSAcceleratorComponent* BDSComponentFactory::CreateCT()
1855{
1857 {return nullptr;}
1858
1859 BDSCT* result = new BDSCT(elementName,
1862 new BDSDicomIntersectVolume(); // TBC
1863
1864 return result;
1865}
1866#endif
1867
1868BDSAcceleratorComponent* BDSComponentFactory::CreateGap()
1869{
1871 {return nullptr;}
1872
1873 return (new BDSGap(elementName,
1874 element->l*CLHEP::m,
1875 element->angle*CLHEP::rad));
1876}
1877
1878BDSAcceleratorComponent* BDSComponentFactory::CreateCrystalCollimator()
1879{
1881 {return nullptr;}
1882
1883 BDSCrystalInfo* left = nullptr;
1884 BDSCrystalInfo* right = nullptr;
1885 if (!element->crystalBoth.empty())
1886 {
1887 left = PrepareCrystalInfo(G4String(element->crystalBoth));
1888 right = PrepareCrystalInfo(G4String(element->crystalBoth));
1889 }
1890 else if (element->crystalBoth.empty() && !element->crystalRight.empty() && !element->crystalLeft.empty())
1891 {
1892 left = PrepareCrystalInfo(G4String(element->crystalLeft));
1893 right = PrepareCrystalInfo(G4String(element->crystalRight));
1894 }
1895 else if (element->crystalRight.empty())
1896 {left = PrepareCrystalInfo(G4String(element->crystalLeft));}
1897 else
1898 {right = PrepareCrystalInfo(G4String(element->crystalRight));}
1899
1900 if (left && !right && BDS::IsFinite(element->crystalAngleYAxisRight))
1901 {
1902 G4cout << G4endl << G4endl << __METHOD_NAME__
1903 << "Left crystal being used but right angle set - perhaps check input for element "
1904 << elementName << G4endl << G4endl;
1905 }
1906 if (!left && right && BDS::IsFinite(element->crystalAngleYAxisLeft))
1907 {
1908 G4cout << G4endl << G4endl << __METHOD_NAME__
1909 << "Right crystal being used but left angle set - perhaps check input for element "
1910 << elementName << G4endl << G4endl;
1911 }
1912
1914 element->l*CLHEP::m,
1916 left,
1917 right,
1918 element->xsize*CLHEP::m, // symmetric for now
1919 element->xsize*CLHEP::m,
1920 element->crystalAngleYAxisLeft*CLHEP::rad,
1921 element->crystalAngleYAxisRight*CLHEP::rad));
1922}
1923
1924BDSAcceleratorComponent* BDSComponentFactory::CreateLaser()
1925{
1927 {return nullptr;}
1928
1929 BDSLaser* laser = new BDSLaser(element->wavelength);
1930 G4double length = element->l*CLHEP::m;
1931 G4double lambda = laser->Wavelength()*CLHEP::m;
1932
1933 G4ThreeVector direction = G4ThreeVector(element->xdir,element->ydir,element->zdir);
1934 G4ThreeVector position = G4ThreeVector(0,0,0);
1935
1936 return (new BDSLaserWire(elementName, length, lambda, direction) );
1937}
1938
1939BDSAcceleratorComponent* BDSComponentFactory::CreateScreen()
1940{
1942 {return nullptr;}
1943
1944 G4TwoVector size;
1945 size.setX(element->screenXSize*CLHEP::m);
1946 size.setY(element->screenYSize*CLHEP::m);
1947 G4cout << __METHOD_NAME__ << " - size = " << size << G4endl;
1948
1949 BDSScreen* theScreen = new BDSScreen( elementName,
1950 element->l*CLHEP::m,
1952 size,
1953 element->angle);
1954 if (element->layerThicknesses.size() != element->layerMaterials.size())
1955 {
1956 throw BDSException(__METHOD_NAME__, "Element \"" + elementName + "\" must have the " +
1957 "same number of materials as layers - check 'layerMaterials'");
1958 }
1959
1960 if (element->layerThicknesses.empty())
1961 {throw BDSException(__METHOD_NAME__, "Element: \"" + elementName + "\" has 0 screen layers");}
1962
1963 std::list<std::string>::const_iterator itm;
1964 std::list<double>::const_iterator itt;
1965 std::list<int>::const_iterator itIsSampler;
1966 for (itt = element->layerThicknesses.begin(),
1967 itm = element->layerMaterials.begin(),
1968 itIsSampler = element->layerIsSampler.begin();
1969 itt != element->layerThicknesses.end();
1970 ++itt, ++itm)
1971 {
1972#ifdef BDSDEBUG
1973 G4cout << __METHOD_NAME__ << " - screen layer: thickness: "
1974 << *(itt) << ", material " << (*itm)
1975 << ", isSampler: " << (*itIsSampler) << G4endl;
1976#endif
1977 if (!(element->layerIsSampler.empty()))
1978 {
1979 theScreen->AddScreenLayer((*itt)*CLHEP::m, *itm, *itIsSampler);
1980 ++itIsSampler;
1981 }
1982 else
1983 {theScreen->AddScreenLayer((*itt)*CLHEP::m, *itm);}
1984 }
1985 return theScreen;
1986}
1987
1988#ifdef USE_AWAKE
1989BDSAcceleratorComponent* BDSComponentFactory::CreateAwakeScreen()
1990{
1991 return (new BDSAwakeScintillatorScreen(elementName,
1993 element->tscint*CLHEP::m,
1994 element->windowScreenGap*CLHEP::m,
1995 element->angle,
1996 element->twindow*CLHEP::m,
1998}
1999
2000BDSAcceleratorComponent* BDSComponentFactory::CreateAwakeSpectrometer()
2001{
2002 BDSFieldInfo* awakeField = nullptr;
2003 if (element->fieldAll.empty())
2004 {
2005 BDSMagnetStrength* awakeStrength = new BDSMagnetStrength();
2006 SetBeta0(awakeStrength);
2007 (*awakeStrength)["field"] = element->scaling * element->B * CLHEP::tesla;
2008 (*awakeStrength)["by"] = 1; // bx,by,bz is unit field direction, so (0,1,0) here
2009
2010 G4Transform3D fieldTrans = CreateFieldTransform(element);
2011 awakeField = new BDSFieldInfo(BDSFieldType::dipole,
2012 BRho(),
2013 BDSIntegratorType::g4classicalrk4,
2014 awakeStrength,
2015 true,
2016 fieldTrans);
2017 awakeField->SetModulatorInfo(ModulatorDefinition(element, true));
2018 }
2019 else
2021 return (new BDSAwakeSpectrometer(elementName,
2022 element->awakeMagnetOffsetX*CLHEP::m,
2023 element->l*CLHEP::m,
2024 awakeField,
2025 element->poleStartZ*CLHEP::m,
2027 element->tscint*CLHEP::m,
2028 element->screenPSize*CLHEP::m,
2029 element->windowScreenGap*CLHEP::m,
2030 element->angle,
2031 element->twindow*CLHEP::m,
2033 element->tmount*CLHEP::m,
2035 element->screenEndZ*CLHEP::m,
2036 element->spec,
2037 element->screenWidth*CLHEP::m));
2038}
2039#endif // end of USE_AWAKE
2040
2041BDSAcceleratorComponent* BDSComponentFactory::CreateTransform3D()
2042{
2043 if (element->axisAngle)
2044 {
2046 {BDS::Warning(__METHOD_NAME__, "component \""+element->name+"\" has axisAngle=1 but one or more of (phi,theta,psi) are non-zero - check");}
2047 return new BDSTransform3D(elementName,
2048 element->xdir * CLHEP::m,
2049 element->ydir * CLHEP::m,
2050 element->zdir * CLHEP::m,
2051 element->axisX,
2052 element->axisY,
2053 element->axisZ,
2054 element->angle * CLHEP::rad);
2055 }
2056 else
2057 {
2059 {BDS::Warning(__METHOD_NAME__, "component \""+element->name+"\" does not have axisAngle=1 but one or more axisAngle variables are non-zero - check");}
2060 return new BDSTransform3D(elementName,
2061 element->xdir * CLHEP::m,
2062 element->ydir * CLHEP::m,
2063 element->zdir * CLHEP::m,
2064 element->phi * CLHEP::rad,
2065 element->theta * CLHEP::rad,
2066 element->psi * CLHEP::rad);
2067 }
2068}
2069
2071{
2072#ifdef BDSDEBUG
2073 G4cout << "---->creating Terminator" << G4endl;
2074#endif
2075 return new BDSTerminator(width);
2076}
2077
2078BDSAcceleratorComponent* BDSComponentFactory::CreateRMatrix()
2079{
2081
2082 GMAD::Element* elementNew = new GMAD::Element(*element);
2083 elementNew->l = (element->l-thinElementLength)/2.0;
2084
2085 BDSAcceleratorComponent* parallelTransport1 = CreateMagnet(elementNew,
2086 st,
2087 BDSFieldType::paralleltransporter,
2088 BDSMagnetType::paralleltransporter);
2089 BDSAcceleratorComponent* rmatrix = CreateThinRMatrix(0,
2090 elementName + "_centre");
2091 BDSAcceleratorComponent* parallelTransport2 = CreateMagnet(elementNew,
2092 st,
2093 BDSFieldType::paralleltransporter,
2094 BDSMagnetType::paralleltransporter);
2095
2096 const G4String baseName = elementName;
2097 BDSLine* bLine = new BDSLine(baseName);
2098 bLine->AddComponent(parallelTransport1);
2099 bLine->AddComponent(rmatrix);
2100 bLine->AddComponent(parallelTransport2);
2101
2102 delete elementNew;
2103
2104 return bLine;
2105}
2106
2107BDSAcceleratorComponent* BDSComponentFactory::CreateThinRMatrix(G4double angleIn,
2108 const G4String& name)
2109{
2111 auto modulator = ModulatorDefinition(element, true);
2112 return CreateThinRMatrix(angleIn, st, name, BDSIntegratorType::rmatrixthin, BDSFieldType::rmatrix, 0, modulator);
2113}
2114
2115BDSAcceleratorComponent* BDSComponentFactory::CreateThinRMatrix(G4double angleIn,
2117 const G4String& name,
2118 BDSIntegratorType intType,
2119 BDSFieldType fieldType,
2120 G4double beamPipeRadius,
2121 BDSModulatorInfo* fieldModulator)
2122{
2123 BDSBeamPipeInfo* beamPipeInfo = PrepareBeamPipeInfo(element, angleIn, -angleIn);
2124 beamPipeInfo->beamPipeType = BDSBeamPipeType::circularvacuum;
2125
2126 // override beampipe radius if supplied - must be set to be iris size for cavity model fringes.
2127 if (BDS::IsFinite(beamPipeRadius))
2128 {beamPipeInfo->aper1 = beamPipeRadius;}
2129
2130 BDSMagnetOuterInfo* magnetOuterInfo = PrepareMagnetOuterInfo(name, element, -angleIn, angleIn, beamPipeInfo);
2131 magnetOuterInfo->geometryType = BDSMagnetGeometryType::none;
2132
2133 G4Transform3D fieldTrans = CreateFieldTransform(element);
2134 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
2135 BRho(),
2136 intType,
2137 st,
2138 true,
2139 fieldTrans);
2140 vacuumField->SetBeamPipeRadius(beamPipeInfo->aper1);
2141 vacuumField->SetModulatorInfo(fieldModulator);
2142 vacuumField->SetFieldAsThin();
2143
2144 BDSMagnet* thinRMatrix = new BDSMagnet(BDSMagnetType::rmatrix,
2145 name,
2147 beamPipeInfo,
2148 magnetOuterInfo,
2149 vacuumField,
2150 0, nullptr, // default values for optional args (angle, outerFieldInfo)
2151 true); // isThin
2152
2153 thinRMatrix->SetExtent(BDSExtent(beamPipeInfo->aper1,
2154 beamPipeInfo->aper1,
2155 thinElementLength*0.5));
2156
2157 return thinRMatrix;
2158}
2159
2160BDSAcceleratorComponent* BDSComponentFactory::CreateCavityFringe(G4double angleIn,
2162 const G4String& name,
2163 G4double irisRadius,
2164 BDSModulatorInfo* fieldModulator)
2165{
2166 BDSIntegratorType intType = integratorSet->cavityFringe;
2167 BDSFieldType fieldType = BDSFieldType::cavityfringe;
2168 BDSAcceleratorComponent* cavityFringe = CreateThinRMatrix(angleIn, st, name, intType, fieldType, irisRadius, fieldModulator);
2169 return cavityFringe;
2170}
2171
2172BDSAcceleratorComponent* BDSComponentFactory::CreateGaborLens()
2173{
2175 {return nullptr;}
2176 // force circular vacuum volume
2177 BDSBeamPipeInfo* defaultModel = BDSGlobalConstants::Instance()->DefaultBeamPipeModel();
2178 BDSBeamPipeInfo* bpInfo = new BDSBeamPipeInfo(defaultModel,
2179 "circular",
2180 element->aper1 * CLHEP::m,
2181 0,0,0,
2183 element->beampipeThickness * CLHEP::m,
2185 G4ThreeVector(0,0,-1),
2186 G4ThreeVector(0,0,1));
2187
2188 const BDSFieldType gaborLensField = BDSFieldType::gaborlens;
2189 BDSIntegratorType intType = integratorSet->Integrator(gaborLensField);
2190 G4Transform3D fieldTrans = CreateFieldTransform(element);
2192 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
2193 SetBeta0(st);
2194 (*st)["length"] = element->l * CLHEP::m;
2196
2197 BDSFieldInfo* vacuumFieldInfo = new BDSFieldInfo(gaborLensField,
2198 BRho(),
2199 intType,
2200 st,
2201 true,
2202 fieldTrans);
2203
2204 vacuumFieldInfo->SetModulatorInfo(ModulatorDefinition(element, true));
2205
2206 G4Material* outerMaterial;
2207 if (element->material.empty())
2208 {
2209 G4String defaultMaterialName = BDSGlobalConstants::Instance()->OuterMaterialName();
2210 outerMaterial = BDSMaterials::Instance()->GetMaterial(defaultMaterialName);
2211 }
2212 else
2213 {outerMaterial = BDSMaterials::Instance()->GetMaterial(element->material);}
2214
2215 // hard coded for anode and electrode
2216 G4Material* copper = BDSMaterials::Instance()->GetMaterial("copper");
2217
2218 auto gaborlens = new BDSGaborLens(elementName,
2219 element->l*CLHEP::m,
2221 element->anodeLength*CLHEP::m,
2222 copper,
2223 element->anodeRadius*CLHEP::m,
2224 element->anodeThickness*CLHEP::m,
2225 element->electrodeLength*CLHEP::m,
2226 copper,
2227 element->electrodeRadius*CLHEP::m,
2228 element->electrodeThickness*CLHEP::m,
2229 outerMaterial,
2231 bpInfo,
2232 vacuumFieldInfo);
2233 return gaborlens;
2234}
2235
2236BDSAcceleratorComponent* BDSComponentFactory::CreateLaserwire(G4double syncrhonousTime)
2237{
2239 {return nullptr;}
2240
2241 BDSLaser* laser = PrepareLaser(element);
2242 laser->SetT0(syncrhonousTime);
2243
2244 G4ThreeVector laserOffset = G4ThreeVector(element->laserOffsetX * CLHEP::m,
2245 element->laserOffsetY * CLHEP::m,
2246 element->laserOffsetZ * CLHEP::m);
2247 G4String colour = laser->GetLaserColour();
2248
2249 return (new BDSLaserWireNew(elementName,
2250 element->l*CLHEP::m,
2252 laser,
2253 30.0*laser->Sigma0(),
2254 element->wireLength*CLHEP::m,
2255 element->laserOffsetTheta*CLHEP::rad,
2256 element->laserOffsetPhi*CLHEP::rad,
2257 laserOffset,
2258 BDSColours::Instance()->GetColour(colour)));
2259}
2260
2263 BDSFieldType fieldType,
2264 BDSMagnetType magnetType,
2265 G4double angle,
2266 const G4String& nameSuffix) const
2267{
2269 BDSIntegratorType intType = integratorSet->Integrator(fieldType);
2270 G4Transform3D fieldTrans = CreateFieldTransform(element);
2271 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
2272 SetBeta0(st);
2273 BDSFieldInfo* vacuumField = new BDSFieldInfo(fieldType,
2274 BRho(),
2275 intType,
2276 st,
2277 true,
2278 fieldTrans);
2279 vacuumField->SetModulatorInfo(ModulatorDefinition(el, true));
2280
2281 BDSMagnetOuterInfo* outerInfo = PrepareMagnetOuterInfo(elementName + nameSuffix, element, st, bpInfo);
2282 vacuumField->SetScalingRadius(outerInfo->innerRadius); // purely for completeness of information - not required
2283 BDSFieldInfo* outerField = nullptr;
2284
2285 // only make a default multipolar field if the yokeFields flag is on and
2286 // there isn't an 'outerField' specified for the element
2287 G4bool externalOuterField = !(el->fieldOuter.empty());
2288 if (yokeFields && !externalOuterField)
2289 {
2290 outerField = PrepareMagnetOuterFieldInfo(st,
2291 fieldType,
2292 bpInfo,
2293 outerInfo,
2294 fieldTrans,
2296 BRho(),
2299 }
2300
2301 return new BDSMagnet(magnetType,
2302 elementName + nameSuffix,
2303 el->l * CLHEP::m,
2304 bpInfo,
2305 outerInfo,
2306 vacuumField,
2307 angle,
2308 outerField);
2309}
2310
2312 G4bool printWarning)
2313{
2314 if (el->l < 1e-7) // 'l' already in metres from parser
2315 {
2316 if (printWarning)
2317 {BDS::Warning("---> NOT creating element \"" + el->name + "\" -> l < 1e-7 m: l = " + std::to_string(el->l) + " m");}
2318 return false;
2319 }
2320 else
2321 {return true;}
2322}
2323
2325 G4double maxAngle)
2326{
2327 if (std::abs(element->e1) > maxAngle)
2328 {throw BDSException(__METHOD_NAME__, "Pole face angle e1: " + std::to_string(element->e1) +
2329 " is greater than " + std::to_string(maxAngle));}
2330 if (std::abs(element->e2) > maxAngle)
2331 {throw BDSException(__METHOD_NAME__, "Pole face angle e2: " + std::to_string(element->e2) +
2332 " is greater than " + std::to_string(maxAngle));}
2333}
2334
2336 const BDSMagnetStrength* st)
2337{
2338 // for lhcleft and lhcright - purposively override this behaviour
2339 auto mgt = MagnetGeometryType(element);
2340 if (mgt == BDSMagnetGeometryType::lhcleft)
2341 {return true;} // active beam pipe is right and yoke is on the left
2342 if (mgt == BDSMagnetGeometryType::lhcright)
2343 {return false;}
2344
2345 G4double angle = (*st)["angle"];
2346 G4double hkickAng = -(*st)["hkick"]; // not really angle but proportional in the right direction
2347 G4double vkickAng = -(*st)["vkick"];
2348 if (!BDS::IsFinite(angle) && BDS::IsFinite(hkickAng))
2349 {angle = hkickAng;}
2350 if (!BDS::IsFinite(angle) && BDS::IsFinite(vkickAng))
2351 {angle = vkickAng;}
2352 G4bool yokeOnLeft;
2353 if ((angle < 0) && (element->yokeOnInside))
2354 {yokeOnLeft = true;}
2355 else if ((angle > 0) && (!(element->yokeOnInside)))
2356 {yokeOnLeft = true;}
2357 else
2358 {yokeOnLeft = false;}
2359 return yokeOnLeft;
2360}
2361
2363{
2364 return ele->scalingFieldOuterSet ? ele->scalingFieldOuter : BDSGlobalConstants::Instance()->ScalingFieldOuter();
2365}
2366
2368 const BDSFieldType& fieldType,
2369 const BDSBeamPipeInfo* bpInfo,
2370 const BDSMagnetOuterInfo* outerInfo,
2371 const G4Transform3D& fieldTransform,
2372 const BDSIntegratorSet* integratorSetIn,
2373 G4double brhoIn,
2374 G4double outerFieldScaling,
2375 BDSModulatorInfo* modulatorInfo)
2376{
2377 BDSFieldType outerType;
2378 switch (fieldType.underlying())
2379 {
2380 case BDSFieldType::dipole:
2381 {outerType = BDSFieldType::multipoleouterdipole; break;}
2382 case BDSFieldType::quadrupole:
2383 {outerType = BDSFieldType::multipoleouterquadrupole; break;}
2384 case BDSFieldType::sextupole:
2385 {outerType = BDSFieldType::multipoleoutersextupole; break;}
2386 case BDSFieldType::octupole:
2387 {outerType = BDSFieldType::multipoleouteroctupole; break;}
2388 case BDSFieldType::decapole:
2389 {outerType = BDSFieldType::multipoleouterdecapole; break;}
2390 case BDSFieldType::skewquadrupole:
2391 {outerType = BDSFieldType::skewmultipoleouterquadrupole; break;}
2392 case BDSFieldType::skewsextupole:
2393 {outerType = BDSFieldType::skewmultipoleoutersextupole; break;}
2394 case BDSFieldType::skewoctupole:
2395 {outerType = BDSFieldType::skewmultipoleouteroctupole; break;}
2396 case BDSFieldType::skewdecapole:
2397 {outerType = BDSFieldType::skewmultipoleouterdecapole; break;}
2398 case BDSFieldType::dipole3d:
2399 case BDSFieldType::solenoid:
2400 {outerType = BDSFieldType::solenoidsheet; break;}
2401 default:
2402 {return nullptr; break;} // no possible outer field for any other magnet types
2403 }
2404
2405 BDSMagnetStrength* stCopy = new BDSMagnetStrength(*vacuumSt);
2406 (*stCopy)["scalingOuter"] = outerFieldScaling;
2407 BDSIntegratorType intType = integratorSetIn->Integrator(outerType);
2408 BDSFieldInfo* outerField = new BDSFieldInfo(outerType,
2409 brhoIn,
2410 intType,
2411 stCopy,
2412 true,
2413 fieldTransform);
2414 outerField->SetModulatorInfo(modulatorInfo);
2415 outerField->SetChordStepMinimum(BDSGlobalConstants::Instance()->ChordStepMinimumYoke());
2416 if (outerInfo)
2417 {
2418 outerField->SetScalingRadius(outerInfo->innerRadius);
2419 outerField->SetSecondFieldOnLeft(outerInfo->yokeOnLeft);
2420 auto gt = outerInfo->geometryType;
2421 G4bool yfmLHC = BDSGlobalConstants::Instance()->YokeFieldsMatchLHCGeometry();
2422 if ((gt == BDSMagnetGeometryType::lhcleft || gt == BDSMagnetGeometryType::lhcright) && yfmLHC)
2423 {
2424 if (fieldType == BDSFieldType::dipole)
2425 {outerField->SetFieldType(BDSFieldType::multipoleouterdipolelhc);}
2426 else if (fieldType == BDSFieldType::quadrupole)
2427 {outerField->SetFieldType(BDSFieldType::multipoleouterquadrupolelhc);}
2428 else if (fieldType == BDSFieldType::sextupole)
2429 {outerField->SetFieldType(BDSFieldType::multipoleoutersextupolelhc);}
2430 }
2431 }
2432
2433 if (bpInfo)
2434 {outerField->SetBeamPipeRadius(bpInfo->IndicativeRadius());}
2435 return outerField;
2436}
2437
2439 const Element* el,
2440 const BDSMagnetStrength* st,
2441 const BDSBeamPipeInfo* beamPipe,
2442 G4double defaultHorizontalWidth,
2443 G4double defaultVHRatio,
2444 G4double defaultCoilWidthFraction,
2445 G4double defaultCoilHeightFraction)
2446{
2447 G4bool yokeOnLeft = YokeOnLeft(el,st);
2448 G4double angle = (*st)["angle"];
2449
2450 return PrepareMagnetOuterInfo(elementNameIn, el, 0.5*angle, 0.5*angle, beamPipe, yokeOnLeft,
2451 defaultHorizontalWidth, defaultVHRatio, defaultCoilWidthFraction,
2452 defaultCoilHeightFraction);
2453}
2454
2456{
2457 BDSMagnetGeometryType result;
2459 // magnet geometry type
2460 if (el->magnetGeometryType.empty() || globals->IgnoreLocalMagnetGeometry())
2461 {result = globals->MagnetGeometryType();}
2462 else
2463 {result = BDS::DetermineMagnetGeometryType(el->magnetGeometryType);}
2464 return result;
2465}
2466
2468 const Element* el,
2469 const G4double angleIn,
2470 const G4double angleOut,
2471 const BDSBeamPipeInfo* beamPipe,
2472 const G4bool yokeOnLeft,
2473 G4double defaultHorizontalWidth,
2474 G4double defaultVHRatio,
2475 G4double defaultCoilWidthFraction,
2476 G4double defaultCoilHeightFraction)
2477{
2479
2481 info->name = elementNameIn;
2482
2483 // magnet geometry type
2484 info->geometryType = MagnetGeometryType(el);
2485 if (! (el->magnetGeometryType.empty() || globals->IgnoreLocalMagnetGeometry()) )
2486 {
2487 info->geometryTypeAndPath = el->magnetGeometryType;
2488 if (el->stripOuterVolume)
2489 {BDS::Warning(__METHOD_NAME__, "stripOuterVolume for element \"" + el->name + "\" will have no effect");}
2490 }
2491
2492 // set face angles w.r.t. chord
2493 info->angleIn = angleIn;
2494 info->angleOut = angleOut;
2495
2496 // horizontal width
2497 info->horizontalWidth = PrepareHorizontalWidth(el, defaultHorizontalWidth);
2498
2499 // inner radius of magnet geometry - TODO when poles can be built away from beam pipe
2500 info->innerRadius = beamPipe->IndicativeRadius();
2501
2502 // outer material
2503 G4Material* outerMaterial;
2504 if (el->material.empty())
2505 {
2506 G4String defaultMaterialName = globals->OuterMaterialName();
2507 outerMaterial = BDSMaterials::Instance()->GetMaterial(defaultMaterialName);
2508 }
2509 else
2510 {outerMaterial = BDSMaterials::Instance()->GetMaterial(el->material);}
2511 info->outerMaterial = outerMaterial;
2512
2513 // yoke direction
2514 info->yokeOnLeft = yokeOnLeft;
2515
2516 if (el->hStyle < 0) // it's unset
2517 {info->hStyle = globals->HStyle();}
2518 else
2519 {info->hStyle = G4bool(el->hStyle);} // convert from int to bool
2520
2521 if (el->vhRatio > 0)
2522 {info->vhRatio = G4double(el->vhRatio);}
2523 else if (globals->VHRatio() > 0)
2524 {info->vhRatio = globals->VHRatio();}
2525 else if (defaultVHRatio > 0) // allow calling function to supply optional default
2526 {info->vhRatio = defaultVHRatio;}
2527 else
2528 {info->vhRatio = info->hStyle ? 0.8 : 1.0;} // h default : c default
2529
2530 if (el->coilWidthFraction > 0)
2531 {info->coilWidthFraction = G4double(el->coilWidthFraction);}
2532 else if (globals->CoilWidthFraction() > 0)
2533 {info->coilWidthFraction = globals->CoilWidthFraction();}
2534 else if (defaultCoilHeightFraction > 0) // allow calling function to supply optional default
2535 {info->coilWidthFraction = defaultCoilWidthFraction;}
2536 else
2537 {info->coilWidthFraction = info->hStyle ? 0.8 : 0.65;} // h default : c default
2538
2539 if (el->coilHeightFraction > 0)
2540 {info->coilHeightFraction = G4double(el->coilHeightFraction);}
2541 else if (globals->CoilHeightFraction() > 0)
2542 {info->coilHeightFraction = globals->CoilHeightFraction();}
2543 else if (defaultCoilHeightFraction > 0) // allow calling function to supply optional default
2544 {info->coilHeightFraction = defaultCoilHeightFraction;}
2545 else
2546 {info->coilHeightFraction = 0.8;} // default for both h and c type
2547
2548 info->colour = PrepareColour(el);
2549 info->autoColour = el->autoColour;
2550
2551 return info;
2552}
2553
2555 G4double defaultHorizontalWidth)
2556{
2557 G4double horizontalWidth = el->horizontalWidth*CLHEP::m;
2558 if (horizontalWidth < 1e-6)
2559 {//horizontalWidth not set - use either global or specified default
2560 if (defaultHorizontalWidth > 0)
2561 {horizontalWidth = defaultHorizontalWidth;}
2562 else
2563 {horizontalWidth = BDSGlobalConstants::Instance()->HorizontalWidth();}
2564 }
2565 return horizontalWidth;
2566}
2567
2569{
2570 G4Material* result = nullptr;
2571 if (el->vacuumMaterial.empty())
2572 {result = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->VacuumMaterial());}
2573 else
2575 return result;
2576}
2577
2579 const G4ThreeVector& inputFaceNormalIn,
2580 const G4ThreeVector& outputFaceNormalIn)
2581{
2582 BDSBeamPipeInfo* defaultModel = BDSGlobalConstants::Instance()->DefaultBeamPipeModel();
2583 BDSBeamPipeInfo* result;
2584 if (!BDSGlobalConstants::Instance()->IgnoreLocalAperture())
2585 {
2586 try
2587 {
2588 result = new BDSBeamPipeInfo(defaultModel,
2589 el->apertureType,
2590 el->aper1 * CLHEP::m,
2591 el->aper2 * CLHEP::m,
2592 el->aper3 * CLHEP::m,
2593 el->aper4 * CLHEP::m,
2594 el->vacuumMaterial,
2595 el->beampipeThickness * CLHEP::m,
2596 el->beampipeMaterial,
2597 inputFaceNormalIn,
2598 outputFaceNormalIn);
2599 }
2600 catch (BDSException& e)
2601 {
2602 G4String msg = "\nProblem in element: \"" + el->name + "\"";
2603 e.AppendToMessage(msg);
2604 throw e;
2605 }
2606 }
2607 else
2608 {// ignore the aperture model from the element and use the global one
2609 result = new BDSBeamPipeInfo(*defaultModel); // ok as only pointers to materials
2610 result->inputFaceNormal = inputFaceNormalIn;
2611 result->outputFaceNormal = outputFaceNormalIn;
2612 }
2613 return result;
2614}
2615
2617 const G4double angleIn,
2618 const G4double angleOut)
2619{
2620 auto faces = BDS::CalculateFaces(angleIn, angleOut);
2621 BDSBeamPipeInfo* info = PrepareBeamPipeInfo(el, faces.first, faces.second);
2622 return info;
2623}
2624
2626{
2627#ifdef BDSDEBUG
2628 G4cout << __METHOD_NAME__ << "offsetX,Y: " << el->offsetX << " " << el->offsetY << " tilt: " << el->tilt << G4endl;
2629#endif
2630 G4double xOffset = el->offsetX * CLHEP::m;
2631 G4double yOffset = el->offsetY * CLHEP::m;
2632 G4double tilt = el->tilt * CLHEP::rad;
2633
2634 BDSTiltOffset* result = nullptr;
2635 if (BDS::IsFinite(xOffset) || BDS::IsFinite(yOffset) || BDS::IsFinite(tilt))
2636 {result = new BDSTiltOffset(xOffset, yOffset, tilt);}
2637 return result;
2638}
2639
2641{
2642 return tiltOffset ? tiltOffset->Transform3D() : G4Transform3D();
2643}
2644
2646{
2648 G4Transform3D trans = CreateFieldTransform(to);
2649 delete to;
2650 return trans;
2651}
2652
2654 G4double angle,
2655 G4double horizontalWidth,
2656 const G4String& name)
2657{
2658 G4double radiusFromAngleLength = std::abs(arcLength / angle); // s = r*theta -> r = s/theta
2659 if (horizontalWidth > 2*radiusFromAngleLength)
2660 {
2661 G4cerr << "Error: the combination of length, angle and horizontalWidth in element named \"" << name
2662 << "\" will result in overlapping faces!" << G4endl << "Please reduce the horizontalWidth" << G4endl;
2663 throw BDSException(__METHOD_NAME__, "");
2664 }
2665}
2666
2668{
2669 for (const auto& model : BDSParser::Instance()->GetCavityModels())
2670 {
2671 // material can either be specified in
2672 G4Material* material = nullptr;
2673 if (!model.material.empty())
2674 {material = BDSMaterials::Instance()->GetMaterial(model.material);}
2675
2676 auto info = new BDSCavityInfo(BDS::DetermineCavityType(model.type),
2677 material,
2678 model.irisRadius*CLHEP::m,
2679 model.thickness*CLHEP::m,
2680 model.equatorRadius*CLHEP::m,
2681 model.halfCellLength*CLHEP::m,
2682 model.numberOfPoints,
2683 model.numberOfCells,
2684 model.equatorHorizontalAxis*CLHEP::m,
2685 model.equatorVerticalAxis*CLHEP::m,
2686 model.irisHorizontalAxis*CLHEP::m,
2687 model.irisVerticalAxis*CLHEP::m,
2688 model.tangentLineAngle);
2689
2690 cavityInfos[model.name] = info;
2691 }
2692}
2693
2695{
2696 for (const auto& laser : BDSParser::Instance()->GetLasers())
2697 {
2698 G4double sigma0 = 0;
2699 if (BDS::IsFinite(laser.w0))
2700 {sigma0 = 0.5 * laser.w0;}
2701 else if (BDS::IsFinite(laser.sigma0))
2702 {sigma0 = laser.sigma0;}
2703 else
2704 {throw BDSException(__METHOD_NAME__, "Neither \"w0\" or \"sigma0\" are defined \"" + laser.name + "\"");}
2705 sigma0 *= CLHEP::m;
2706 G4ThreeVector polarization(laser.laserPolarization1,laser.laserPolarization2,laser.laserPolarization3);
2707 BDSLaser* las = new BDSLaser(laser.wavelength*CLHEP::m,
2708 laser.m2,
2709 laser.pulseDuration*CLHEP::s,
2710 laser.pulseEnergy*CLHEP::joule,
2711 sigma0,
2712 laser.laserArrivalTime*CLHEP::s,
2713 0,
2714 polarization,
2715 laser.ignoreRayleighRange);
2716 lasers[laser.name] = las;
2717 }
2718}
2719
2721{
2722
2723 G4String laserBeam = G4String(el->laserBeam);
2724 auto result = lasers.find(laserBeam);
2725 if (result == lasers.end())
2726 {
2727 G4cout << "Unknown laser \"" << laserBeam << "\" - please define it" << G4endl;
2728 exit(1);
2729 }
2730
2731 // prepare a copy so the component can own that recipe
2732 BDSLaser* laser = new BDSLaser(*(result->second));
2733 return laser;
2734}
2735
2737{
2738 if (!coloursInitialised)
2739 {
2740 BDSColours* allColours = BDSColours::Instance();
2741 for (const auto& colour : BDSParser::Instance()->GetColours())
2742 {
2743 allColours->DefineColour(G4String(colour.name),
2744 (G4double) colour.red,
2745 (G4double) colour.green,
2746 (G4double) colour.blue,
2747 (G4double) colour.alpha);
2748 }
2749 coloursInitialised = true;
2750 }
2751}
2752
2754{
2755 for (auto& model : BDSParser::Instance()->GetCrystals())
2756 {
2757 G4Material* material = BDSMaterials::Instance()->GetMaterial(model.material);
2758
2759 auto info = new BDSCrystalInfo(material,
2760 G4String(model.data),
2761 BDS::DetermineCrystalType(model.shape),
2762 G4double(model.lengthX)*CLHEP::m,
2763 G4double(model.lengthY)*CLHEP::m,
2764 G4double(model.lengthZ)*CLHEP::m,
2765 G4double(model.sizeA)*CLHEP::m,
2766 G4double(model.sizeB)*CLHEP::m,
2767 G4double(model.sizeC)*CLHEP::m,
2768 G4double(model.alpha)*CLHEP::halfpi,
2769 G4double(model.beta)*CLHEP::halfpi,
2770 G4double(model.gamma)*CLHEP::halfpi,
2771 G4int (model.spaceGroup),
2772 G4double(model.bendingAngleYAxis)*CLHEP::rad,
2773 G4double(model.bendingAngleZAxis)*CLHEP::rad,
2774 G4double(model.miscutAngleY)*CLHEP::rad);
2775 crystalInfos[model.name] = info;
2776 }
2777
2778}
2779
2781{
2782 auto result = crystalInfos.find(crystalName);
2783 if (result == crystalInfos.end())
2784 {
2785 G4cout << "Defined crystals are:" << G4endl;
2786 for (const auto& kv : crystalInfos)
2787 {G4cout << kv.first << G4endl;}
2788 throw BDSException(__METHOD_NAME__, "unknown crystal \"" + crystalName + "\" - please define it");
2789 }
2790
2791 // prepare a copy so the component can own that recipe
2792 BDSCrystalInfo* info = new BDSCrystalInfo(*(result->second));
2793 return info;
2794}
2795
2797 G4double frequency) const
2798{
2799 // If the cavity model name (identifier) has been defined, return a *copy* of
2800 // that model - so that the component will own that info object.
2801 G4String modelName = G4String(el->cavityModel);
2802
2803 // no specific model - prepare a default based on element parameters
2804 if (modelName.empty())
2805 {return PrepareCavityModelInfoForElement(el, frequency);}
2806
2807 // cavity model name specified - match up with parser object already translated here
2808 auto result = cavityInfos.find(modelName);
2809 if (result == cavityInfos.end())
2810 {throw BDSException(__METHOD_NAME__, "unknown cavity model identifier \"" + el->cavityModel + "\" - please define it");}
2811
2812 // we make a per element copy of the definition
2813 BDSCavityInfo* info = new BDSCavityInfo(*(result->second));
2814
2815 // check cavity radius against horizontal width. Cavity radius calculation same as that in CavityFactory classes.
2816 G4double cavityRadius = info->equatorRadius + info->thickness + lengthSafety;
2817 G4double horizontalWidth = PrepareHorizontalWidth(el);
2818 if (cavityRadius > horizontalWidth)
2819 {
2820 G4String msg = "Cavity horizontalWidth for element \"" + elementName + "\" is smaller " + "than the cavity model radius.";
2821 throw BDSException(__METHOD_NAME__, msg);
2822 }
2823
2824 // If no material specified, we take the material from the element. If no material at
2825 // all, we exit with warning.
2826 if (!info->material)
2827 {
2828 if (el->material.empty())
2829 {
2830 G4String msg = "cavity material is not defined for cavity \"" + elementName + "\"";
2831 msg += " or for cavity model \"" + el->cavityModel + "\" - please define it";
2832 throw BDSException(__METHOD_NAME__, msg);
2833 }
2834 else
2835 {info->material = BDSMaterials::Instance()->GetMaterial(el->material);}
2836 }
2837
2838 return info;
2839}
2840
2842 G4double frequency) const
2843{
2845 BDSBeamPipeInfo* aperture = PrepareBeamPipeInfo(el);
2846
2847 G4double aper1 = aperture->aper1;
2848 G4double horizontalWidth = PrepareHorizontalWidth(el);
2849
2850 G4double defaultHorizontalWidth = 20*CLHEP::cm;
2851 if (aper1 < defaultHorizontalWidth) // only do if the aperture will fit
2852 {horizontalWidth = std::min(defaultHorizontalWidth, horizontalWidth);} // better default
2853 G4double thickness = aperture->beamPipeThickness;
2854 G4double equatorRadius = horizontalWidth - thickness;
2855 if (equatorRadius <= 0)
2856 {
2857 G4String msg = "horizontalWidth - beampipeThickness <= 0 for element \"" + el->name + "\" -> this quantity must be positive";
2858 throw BDSException(__METHOD_NAME__, msg);
2859 }
2860
2861 // assume single cell cavity
2862 G4double length = el->l * CLHEP::m;
2863 G4double cellLength = length;
2864 G4int nCells = 1;
2865
2866 // calculate number of cells if frequency is finite -
2867 // frequency can be zero in which case only build 1 cell
2868 if (BDS::IsFinite(frequency))
2869 {
2870 cellLength = 2*CLHEP::c_light / frequency; // half wavelength
2871 G4double nCavities = length / cellLength;
2872 nCells = G4int(std::floor(nCavities));
2873 }
2874
2875 if (nCells == 0) // protect against long wavelengths or cavities
2876 {
2877 nCells = 1;
2878 cellLength = length;
2879 }
2880
2881 BDSCavityInfo* defaultCI = new BDSCavityInfo(BDSCavityType::pillbox,
2882 BDSMaterials::Instance()->GetMaterial("Copper"),
2883 aperture->aper1,
2884 thickness,
2885 equatorRadius,
2886 cellLength*0.5);
2887
2888 delete aperture;
2889 return defaultCI;
2890}
2891
2893 BDSFieldType fieldType,
2894 G4double cavityLength,
2895 const BDSParticleDefinition& incomingParticle)
2896{
2897 G4double eField = 0; // - result variable
2898 G4double scaling = el->scaling;
2899 G4double frequency = el->frequency * CLHEP::rad;
2900 G4double phase = el->phase * CLHEP::rad;
2901
2902 // the sign of the field to be accelerating is handled here - each field class just uses the value
2903 G4int acceleratingFieldDirectionFactor = BDS::Sign(incomingParticle.BRho());
2904
2905 switch (fieldType.underlying())
2906 {
2907 case BDSFieldType::rfconstantinx:
2908 case BDSFieldType::rfconstantiny:
2909 {// voltage is not used for these
2910 eField = scaling * el->gradient * CLHEP::volt / CLHEP::m;
2911 break;
2912 }
2913 case BDSFieldType::rfconstantinz:
2914 {
2915 if (BDS::IsFinite(el->gradient))
2916 {eField = scaling * el->gradient * CLHEP::volt / CLHEP::m;}
2917 else
2918 {eField = scaling * el->E * CLHEP::volt / cavityLength;}
2919 break;
2920 }
2921 case BDSFieldType::rfpillbox:
2922 {
2923 if (BDS::IsFinite(el->gradient))
2924 {eField = scaling * el->gradient * CLHEP::volt / CLHEP::m;}
2925 else
2926 {
2927 G4double transitTimeFactor = BDSFieldEMRFCavity::TransitTimeFactor(frequency, phase, cavityLength, incomingParticle.Beta());
2928 eField = scaling * el->E * CLHEP::volt / cavityLength;
2929 // divide by the transit time factor so the total energy gain comes out as expected
2930 eField /= transitTimeFactor;
2931 }
2932 break;
2933 }
2934 default:
2935 {break;} // shouldn't happen
2936 }
2937 eField *= acceleratingFieldDirectionFactor;
2938 return eField;
2939}
2940
2942 G4double brhoIn,
2943 G4double& arcLength,
2944 G4double& chordLength,
2945 G4double& field,
2946 G4double& angle)
2947{
2948 // 'l' in the element represents the chord length for an rbend - must calculate arc length
2949 // for the field calculation and the accelerator component.
2950 chordLength = el->l * CLHEP::m;
2951 G4double arcLengthLocal = chordLength; // default for no angle
2952
2953 if (BDS::IsFinite(el->B) && el->angleSet)
2954 {// both are specified and should be used - under or overpowered dipole by design
2955 // the angle and the length are set, so we ignore the beam definition and its bending radius
2956 field = el->B * CLHEP::tesla;
2957 angle = el->angle * CLHEP::rad;
2958 // protect against bad calculation from 0 angle and finite field
2959 if (BDS::IsFinite(angle))
2960 {
2961 G4double radiusOfCurvatureOfDipole = 0.5 * chordLength / std::sin(0.5 * angle);
2962 arcLengthLocal = radiusOfCurvatureOfDipole * angle;
2963 }
2964 else
2965 {arcLengthLocal = chordLength;}
2966 }
2967 else if (BDS::IsFinite(el->B))
2968 {// only B field - calculate angle
2969 field = el->B * CLHEP::tesla;
2970 G4double bendingRadius = brhoIn / field; // in mm as brho already in g4 units
2971 angle = 2.0*std::asin(chordLength*0.5 / bendingRadius);
2972 if (std::isnan(angle))
2973 {throw BDSException("Field too strong for element " + el->name + ", magnet bending angle will be greater than pi.");}
2974
2975 arcLengthLocal = bendingRadius * angle;
2976 }
2977 else
2978 {// (assume) only angle - calculate B field
2979 angle = el->angle * CLHEP::rad;
2980 if (BDS::IsFinite(angle))
2981 {
2982 // sign for bending radius doesn't matter (from angle) as it's only used for arc length.
2983 // this is the inverse equation of that in BDSAcceleratorComponent to calculate
2984 // the chord length from the arclength and angle.
2985 G4double bendingRadius = chordLength * 0.5 / std::sin(std::abs(angle) * 0.5);
2986 arcLengthLocal = bendingRadius * angle;
2987 field = brhoIn * angle / std::abs(arcLengthLocal);
2988 }
2989 else
2990 {field = 0;} // 0 angle -> chord length and arc length the same; field 0
2991 }
2992
2993 // Ensure positive length despite sign of angle.
2994 arcLength = std::abs(arcLengthLocal);
2995
2996 if (std::abs(angle) > CLHEP::pi/2.0)
2997 {throw BDSException("Error: the rbend " + el->name + " cannot be constructed as its bending angle is defined to be greater than pi/2.");}
2998}
2999
3001 BDSFieldType fieldType,
3002 G4double cavityLength,
3003 BDSMagnetStrength*& fringeIn,
3004 BDSMagnetStrength*& fringeOut) const
3005{
3007 SetBeta0(st);
3008 G4double chordLength = cavityLength; // length may be reduced for fringe placement.
3009 (*st)["equatorradius"] = 1*CLHEP::m; // to prevent 0 division - updated later in createRF
3010 (*st)["length"] = chordLength;
3011 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
3012
3013 switch (fieldType.underlying())
3014 {
3015 case BDSFieldType::rfpillbox:
3016 case BDSFieldType::rfconstantinz:
3017 {(*st)["ez"] = 1.0; break;}
3018 case BDSFieldType::rfconstantinx:
3019 {(*st)["ex"] = 1.0; break;}
3020 case BDSFieldType::rfconstantiny:
3021 {(*st)["ey"] = 1.0; break;}
3022 default:
3023 {(*st)["ez"] = 1.0; break;}
3024 }
3025
3026 // scale factor to account for reduced body length due to fringe placement.
3027 G4double lengthScaling = cavityLength / (element->l * CLHEP::m);
3028
3029 if ((fieldType == BDSFieldType::rfconstantinx || fieldType == BDSFieldType::rfconstantiny) && BDS::IsFinite(el->E) )
3030 {throw BDSException(__METHOD_NAME__, "only \"gradient\" is accepted for rfconstantinx or rfconstantiny components and not \"E\"");}
3031
3032 G4double eField = EFieldFromElement(el, fieldType, chordLength, integralUpToThisComponent->designParticle); // includes scaling
3033 (*st)["efield"] = eField / lengthScaling;
3034
3035 G4double frequency = std::abs(el->frequency * CLHEP::hertz);
3036 (*st)["frequency"] = frequency;
3037
3038 // set the phase from the element even if zero frequency, field should be cos(0 + phi) = constant.
3039 G4double phase = el->phase * CLHEP::rad;
3040 if (BDS::IsFinite(el->tOffset))
3041 {
3042 if (BDS::IsFinite(el->phase))
3043 {throw BDSException(__METHOD_NAME__, "component: " + el->name + " has both tOffset and phase specified - only one can be used.");}
3044 phase = el->tOffset*CLHEP::s * frequency * CLHEP::twopi;
3045 }
3046 (*st)["phase"] = phase;
3047
3048 // fringe strengths
3049 fringeIn = new BDSMagnetStrength(*st);
3050 fringeOut = new BDSMagnetStrength(*st);
3051 // fringe synchronousT0 - should be the same as the centre of the component they're linked to
3052 // fringe phase - here the values are copied into the fringe strengths - so only the raw input
3053 // phase that are provided from the input that generally modulate the fringe
3054
3055 // if frequency is 0, don't update phase with offset. Fringes should have the same phase.
3056 if (!BDS::IsFinite(frequency))
3057 {return st;}
3058
3059 return st;
3060}
3061
3062G4Colour* BDSComponentFactory::PrepareColour(Element const* el, const G4Material* material)
3063{
3064 G4String colour = el->colour;
3065 if (material && el->autoColour)
3066 {return BDSColourFromMaterial::Instance()->GetColour(material);}
3067 else if (colour.empty())
3069 else
3070 {return BDSColours::Instance()->GetColour(colour);}
3071}
3072
3074 const G4String& defaultMaterialName)
3075{
3076 G4String materialName = el->material;
3077 if (materialName.empty())
3078 {return BDSMaterials::Instance()->GetMaterial(defaultMaterialName);}
3079 else
3080 {return BDSMaterials::Instance()->GetMaterial(materialName);}
3081}
3082
3084{
3085 G4String materialName = el->material;
3086 if (materialName.empty())
3087 {throw BDSException(__METHOD_NAME__, "element \"" + el->name + "\" has no material specified.");}
3088 else
3089 {return BDSMaterials::Instance()->GetMaterial(materialName);}
3090}
3091
3093 const G4String& defaultMaterialName)
3094{
3095 G4String materialName = el->tipMaterial;
3096 if (materialName.empty())
3097 {return BDSMaterials::Instance()->GetMaterial(defaultMaterialName);}
3098 else
3099 {return BDSMaterials::Instance()->GetMaterial(materialName);}
3100}
3101
3103{
3104 G4String materialName = el->tipMaterial;
3105 if (materialName.empty())
3106 {throw BDSException(__METHOD_NAME__, "element \"" + el->name + "\" has no tip material specified.");}
3107 else
3108 {return BDSMaterials::Instance()->GetMaterial(materialName);}
3109}
3110
3112 BDSAcceleratorComponent* component) const
3113{
3114 // Test for a line. And if so apply to each sub-component.
3115 G4Transform3D fieldTrans = CreateFieldTransform(element);
3116 if (BDSLine* line = dynamic_cast<BDSLine*>(component))
3117 {
3118 for (auto comp : *line)
3119 {SetFieldDefinitions(el, comp);}
3120 }
3121 // RF can ues fieldVacuum for the cavity field model but this would overwrite it which is wrong
3122 BDSMagnet* mag = dynamic_cast<BDSMagnet*>(component);
3123 if (mag && el->type != ElementType::_RF)
3124 {
3125 if (!(el->fieldAll.empty()))
3126 {
3127 G4cerr << "Error: Magnet named \"" << elementName
3128 << "\" is a magnet, but has fieldAll defined." << G4endl
3129 << "Can only have fieldOuter and or fieldVacuum specified." << G4endl;
3130 throw BDSException(__METHOD_NAME__, "");
3131 }
3132 if (!(el->fieldOuter.empty()))
3133 {
3134 BDSFieldInfo* info = new BDSFieldInfo(*(BDSFieldFactory::Instance()->GetDefinition(el->fieldOuter)));
3135 if (info->ProvideGlobal())
3136 {info->SetTransformBeamline(fieldTrans);}
3138 SetModulatorDefinition(el, info);
3139 mag->SetOuterField(info);
3140 }
3141 if (!(el->fieldVacuum.empty()))
3142 {
3143 BDSFieldInfo* info = new BDSFieldInfo(*(BDSFieldFactory::Instance()->GetDefinition(el->fieldVacuum)));
3144 if (info->ProvideGlobal())
3145 {info->SetTransformBeamline(fieldTrans);}
3146 SetModulatorDefinition(el, info);
3147 mag->SetVacuumField(info);
3148 }
3149 }
3150 else
3151 {
3152 if (!(el->fieldAll.empty()))
3153 {
3154 BDSFieldInfo* info = new BDSFieldInfo(*(BDSFieldFactory::Instance()->GetDefinition(el->fieldAll)));
3155 if (info->ProvideGlobal())
3156 {info->SetTransformBeamline(fieldTrans);}
3158 if (el->scalingFieldOuter != 1)
3159 {BDS::Warning("component \"" + el->name + "\" has \"scalingFieldOuter\" != 1.0 -> this will have no effect for \"fieldAll\"");}
3160 component->SetField(info);
3161 }
3162 }
3163}
3164
3166 BDSFieldInfo* info) const
3167{
3168 if (!el->fieldModulator.empty())
3169 {
3170 if (info->ModulatorInfo()) // already exists
3171 {throw BDSException(__METHOD_NAME__, "\""+elementName+"\" uses a field map with a modulator but also a modulator\ndouble modulation is not allowed");}
3172 else
3173 {
3174 auto modDef = ModulatorDefinition(el);
3175 info->SetModulatorInfo(modDef); // works even if none
3176 }
3177 }
3178}
3179
3181{
3183 SetBeta0(st);
3184 G4double scaling = el->scaling;
3185 G4double arcLength = el->l * CLHEP::m;
3186 (*st)["length"] = arcLength; // length needed for thin multipoles
3187 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
3188 // component strength is only normalised by length for thick multipoles
3189 if (el->type == ElementType::_THINMULT || (el->type == ElementType::_MULT && !BDS::IsFinite(el->l)))
3190 {(*st)["length"] = 1*CLHEP::m;}
3191 auto kn = el->knl.begin();
3192 auto ks = el->ksl.begin();
3193 std::vector<G4String> normKeys = BDSMagnetStrength::NormalComponentKeys();
3194 std::vector<G4String> skewKeys = BDSMagnetStrength::SkewComponentKeys();
3195 auto nkey = normKeys.begin();
3196 auto skey = skewKeys.begin();
3197 // Separate loops for kn and ks. The length of knl and ksl are determined by the input in the gmad file.
3198 for (; kn != el->knl.end(); kn++, nkey++)
3199 {(*st)[*nkey] = scaling * (*kn);}
3200 for (; ks != el->ksl.end(); ks++, skey++)
3201 {(*st)[*skey] = scaling * (*ks);}
3202
3203 return st;
3204}
3205
3207{
3209 (*st)["synchronousT0"] = synchronousTAtMiddleOfThisComponent;
3210 SetBeta0(st);
3211 G4double scaling = el->scaling;
3212 // G4double length = el->l;
3213
3214 (*st)["kick1"] = scaling * el->kick1;
3215 (*st)["kick2"] = scaling * el->kick2;
3216 (*st)["kick3"] = scaling * el->kick3;
3217 (*st)["kick4"] = scaling * el->kick4;
3218
3219 (*st)["rmat11"] = scaling * el->rmat11;
3220 (*st)["rmat12"] = scaling * el->rmat12;
3221 (*st)["rmat13"] = scaling * el->rmat13;
3222 (*st)["rmat14"] = scaling * el->rmat14;
3223
3224 (*st)["rmat21"] = scaling * el->rmat21;
3225 (*st)["rmat22"] = scaling * el->rmat22;
3226 (*st)["rmat23"] = scaling * el->rmat23;
3227 (*st)["rmat24"] = scaling * el->rmat24;
3228
3229 (*st)["rmat31"] = scaling * el->rmat31;
3230 (*st)["rmat32"] = scaling * el->rmat32;
3231 (*st)["rmat33"] = scaling * el->rmat33;
3232 (*st)["rmat34"] = scaling * el->rmat34;
3233
3234 (*st)["rmat41"] = scaling * el->rmat41;
3235 (*st)["rmat42"] = scaling * el->rmat42;
3236 (*st)["rmat43"] = scaling * el->rmat43;
3237 (*st)["rmat44"] = scaling * el->rmat44;
3238
3239 return st;
3240}
3241
3242G4double BDSComponentFactory::FieldFromAngle(const G4double angle,
3243 const G4double arcLength) const
3244{
3245 if (!BDS::IsFinite(angle))
3246 {return 0;}
3247 else
3248 {return BRho() * angle / arcLength;}
3249}
3250
3251G4double BDSComponentFactory::AngleFromField(const G4double field,
3252 const G4double arcLength) const
3253{
3254 if (!BDS::IsFinite(field))
3255 {return 0;}
3256 else
3257 {return field * arcLength / BRho();}
3258}
3259
3261 G4double& angle,
3262 G4double& field) const
3263{
3264 G4double arcLength = el->l * CLHEP::m;
3265 if (BDS::IsFinite(el->B) && el->angleSet)
3266 {// both are specified and should be used - under or overpowered dipole by design
3267 field = el->B * CLHEP::tesla;
3268 angle = el->angle * CLHEP::rad;
3269 }
3270 else if (BDS::IsFinite(el->B))
3271 {// only B field - calculate angle
3272 field = el->B * CLHEP::tesla;
3273 angle = AngleFromField(field, arcLength); // length is arc length for an sbend
3274 }
3275 else
3276 {// only angle - calculate B field
3277 angle = el->angle * CLHEP::rad;
3278 field = FieldFromAngle(angle, arcLength);
3279 }
3280 // un-split sbends are effectively rbends - don't construct a >pi/2 degree rbend
3281 // also cannot construct sbends with angles > pi/2
3282 if (BDSGlobalConstants::Instance()->DontSplitSBends() && (std::abs(angle) > CLHEP::pi/2.0))
3283 {throw BDSException("Error: the unsplit sbend "+ el->name + " cannot be constructed as its bending angle is defined to be greater than pi/2.");}
3284
3285 else if (std::abs(angle) > CLHEP::pi*2.0)
3286 {throw BDSException("Error: the sbend "+ el->name +" cannot be constructed as its bending angle is defined to be greater than 2 pi.");}
3287}
3288
3290{
3291 G4double bendAngle = 0;
3292 if (el->type == ElementType::_RBEND)
3293 {
3294 G4double arcLength = 0, chordLength = 0, field = 0;
3295 CalculateAngleAndFieldRBend(el, BRho(), arcLength, chordLength, field, bendAngle);
3296 }
3297 else if (el->type == ElementType::_SBEND)
3298 {
3299 G4double field = 0; // required by next function.
3300 CalculateAngleAndFieldSBend(el, bendAngle, field);
3301 }
3302 else if (el->type == ElementType::_ELEMENT)
3303 {bendAngle = -1*el->angle*CLHEP::rad;}
3304 // else the default is 0
3305 return bendAngle;
3306}
3307
3309{
3310 if (!el)
3311 {return 0;}
3312 // note thin multipoles will match the faces of any magnets, but not contain
3313 // the face angles themselves in the GMAD::Element. this is ok though as
3314 // detector construction will not give a thin multipole as a previous element
3315 // - it'll be skipped while looking backwards.
3316 G4double outgoingFaceAngle = 0;
3317 G4double bendAngle = BendAngle(el);
3318
3319 // +ve e1/e2 shorten the outside of the bend - so flips with angle
3320 G4double e2 = el->e2*CLHEP::rad;
3321 if (el->type == ElementType::_ELEMENT)
3322 {// so if the angle is 0, +1 will be returned
3323 G4double factor = bendAngle < 0 ? -1 : 1;
3324 outgoingFaceAngle += factor * e2;
3325 return outgoingFaceAngle;
3326 }
3327 else if (el->type == ElementType::_RBEND)
3328 {
3330 {return outgoingFaceAngle;}
3331 // angle is w.r.t. outgoing reference trajectory so rbend face is angled
3332 // by half the bend angle
3333 outgoingFaceAngle += 0.5 * bendAngle;
3334 }
3335
3336 // if we're using the matrix integrator set, we build the bends flat irrespective of parameters
3337 // if we have a finite k1 for a bend, we're forced to use the bdsimmatrix integrator set
3339 {return outgoingFaceAngle;}
3340
3341 // for an sbend, the output face is nominally normal to the outgoing
3342 // reference trajectory - so zero here - only changes with e1/e2.
3343 // we need angle though to decide which way it goes
3344
3345 // +ve e1/e2 shorten the outside of the bend - so flips with angle
3346 if (BDS::IsFinite(e2) && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry())
3347 {// so if the angle is 0, +1 will be returned
3348 G4double factor = bendAngle < 0 ? -1 : 1;
3349 outgoingFaceAngle += factor * e2;
3350 }
3351
3352 return outgoingFaceAngle;
3353}
3354
3356{
3357 if (!el)
3358 {return 0;}
3359 // note thin multipoles will match the faces of any magnets, but not contain
3360 // the face angles themselves in the GMAD::Element. this is ok though as
3361 // detector construction will not give a thin multipole as a next element
3362 // - it'll be skipped while looking forwards.
3363 G4double incomingFaceAngle = 0;
3364 G4double bendAngle = BendAngle(el);
3365
3366 // +ve e1/e2 shorten the outside of the bend - so flips with angle
3367 G4double e1 = el->e1*CLHEP::rad;
3368 if (el->type == ElementType::_ELEMENT)
3369 {// so if the angle is 0, +1 will be returned
3370 G4double factor = bendAngle < 0 ? -1 : 1;
3371 incomingFaceAngle += factor * e1;
3372 return incomingFaceAngle;
3373 }
3374 else if (el->type == ElementType::_RBEND)
3375 {
3377 {return incomingFaceAngle;}
3378 // angle is w.r.t. outgoing reference trajectory so rbend face is angled
3379 // by half the bend angle
3380 incomingFaceAngle += 0.5 * bendAngle;
3381 }
3382
3383 // if we're using the matrix integrator set, we build the bends flat irrespective of parameters
3384 // if we have a finite k1 for a bend, we're forced to use the bdsimmatrix integrator set
3386 {return incomingFaceAngle;}
3387
3388 // for an sbend, the output face or nominally normal to the outgoing
3389 // reference trajectory - so zero here - only changes with e1/e2.
3390 // we need angle though to decide which way it goes
3391
3392 // +ve e1/e2 shorten the outside of the bend - so flips with angle
3393 if (BDS::IsFinite(e1) && BDSGlobalConstants::Instance()->BuildPoleFaceGeometry())
3394 {// so if the angle is 0, +1 will be returned
3395 G4double factor = bendAngle < 0 ? -1 : 1;
3396 incomingFaceAngle += factor * e1;
3397 }
3398
3399 return incomingFaceAngle;
3400}
3401
3403 G4bool inDevelopment) const
3404{
3405 if (inDevelopment)
3407 return el->fieldModulator.empty() ? defaultModulator : BDSFieldFactory::Instance()->GetModulatorDefinition(el->fieldModulator);
3408}
3409
3411{
3412 if (!element->fieldModulator.empty())
3413 {throw BDSException(__METHOD_NAME__, "fieldModulator is currently in development for element \"" + elementName + "\"");}
3414}
3415
3417{
3418 (*st)["kg"] = element->scaling * element->kg / CLHEP::m; // kg units per m
3419 (*st)["field"] = element->scaling * element->B * CLHEP::tesla;
3420 (*st)["equatorradius"] = element->anodeRadius*CLHEP::m;
3421
3422 if ((*st)["kg"] < 0)
3423 {throw BDSException(__METHOD_NAME__, "kg strength cannot be negative for element \"" + elementName + "\"");}
3424 if (!BDS::IsFinite((*st)["kg"]) && ((*st)["field"] < 0))
3425 {throw BDSException(__METHOD_NAME__, "B field cannot be negative for element \"" + elementName + "\"");}
3426
3427 const G4double c = CLHEP::c_light;
3428 const BDSParticleDefinition& designParticle = integralUpToThisComponent->designParticle;
3429 const G4double gamma = designParticle.Gamma();
3430 const G4double momentum = designParticle.Momentum(); // in MeV
3431 const G4double mass = designParticle.Mass(); // in MeV
3432
3433 G4double convFactor = gamma * std::pow(c,2) / (4*std::pow(momentum,2));
3434
3435 G4double b2 = 0;
3436 // set field & kg ahead of later changes
3437 if (BDS::IsFinite((*st)["kg"]))
3438 {
3439 b2 = (*st)["kg"] / convFactor * CLHEP::tesla;
3440 (*st)["field"] = std::sqrt(b2) ;
3441 }
3442 else
3443 {
3444 b2 = std::pow((*st)["field"],2);
3445 (*st)["kg"] = b2 * convFactor / CLHEP::m;
3446 }
3447
3448 // set plasma field as its own magnetStrength key - efield key will be used later for confinement field strength
3449 (*st)["plasmaEfield"] = -1.0 * b2 * std::pow(c,2) / (4*mass);
3450
3451 // TODO: set "efield" and "field" to be electric and magnetic confinement field strengths
3452}
static BDSAcceleratorComponentRegistry * Instance()
Singleton accessor.
BDSAcceleratorComponent * GetComponent(const G4String &name, G4double rigidtyAtConstructionTime)
void RegisterComponent(BDSAcceleratorComponent *component, G4double rigidtyAtConstructionTime, bool isModified=false)
Abstract class that represents a component of an accelerator.
virtual void SetBiasMaterialLVList(const std::list< std::string > &biasMaterialLVListIn)
Copy the bias list to this element.
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.
A class that holds the current integrated quantities along a beam line.
void Integrate(const GMAD::Element &componentAsDefined)
G4int rigidityCount
Which change in rigidity we're at. Starts at 0, +1 after each change. Used for naming.
G4double ProvideSynchronousTAtCentreOfNextElement(const GMAD::Element *el) const
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 class for a beam mask collimator.
A piece of vacuum beam pipe with a crystal for channelling.
A class for an elliptical collimator.
Collimator with jaws having a bulk material and a tip of a different material.
Collimator with only two jaw and no top bit.
A class for a rectangular collimator.
static BDSColourFromMaterial * Instance()
Singleton pattern.
G4Colour * GetColour(const G4Material *material, const G4String &prefixToStripFromName="")
Get colour from name.
Colour class that holds all colours used in BDSIM.
Definition BDSColours.hh:33
G4Colour * GetColour(G4double red, G4double green, G4double blue, G4double alpha=1)
Get a cached anonymous colour by values.
static BDSColours * Instance()
singleton pattern
Definition BDSColours.cc:32
void DefineColour(const G4String &name, G4double red, G4double green, G4double blue, G4double alpha=1, G4bool normaliseTo255=true)
Define a new colour.
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, const BDSBeamlineIntegral &integral)
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
void INDEVELOPMENTERROR() const
TBC - remove when modulators are implemented fully.
BDSComponentFactoryUser * userComponentFactory
User component factory if any.
KickerType
Private enum for kicker types.
BDSAcceleratorComponent * CreateComponent(GMAD::Element const *elementIn, GMAD::Element const *prevElementIn, GMAD::Element const *nextElementIn, BDSBeamlineIntegral &integral)
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.
void CalculateGaborLensStrength(BDSMagnetStrength *st) const
Calculate the electric field strength of the confined plasma in a Gabor lens.
BDSComponentFactory()=delete
No default constructor.
BDSAcceleratorComponent * CreateTeleporter(const G4double teleporterLength, const G4double teleporterWidth, const G4Transform3D &transformIn)
BDSModulatorInfo * defaultModulator
Default modulator for all components.
G4Material * PrepareVacuumMaterial(GMAD::Element const *el) const
Prepare the vacuum material from the element or resort to default in options.
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.
static void CalculateAngleAndFieldRBend(const GMAD::Element *el, G4double brhoIn, G4double &arcLength, G4double &chordLength, G4double &field, G4double &angle)
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
BDSLaser * PrepareLaser(GMAD::Element const *el) const
G4bool yokeFields
Cache of whether to include yoke magnetic fields.
static G4double EFieldFromElement(GMAD::Element const *el, BDSFieldType fieldType, G4double cavityLength, const BDSParticleDefinition &incomingParticle)
static BDSFieldInfo * PrepareMagnetOuterFieldInfo(const BDSMagnetStrength *vacuumSt, const BDSFieldType &fieldType, const BDSBeamPipeInfo *bpInfo, const BDSMagnetOuterInfo *outerInfo, const G4Transform3D &fieldTransform, const BDSIntegratorSet *integratorSetIn, G4double brhoIn, G4double outerFieldScaling=1.0, BDSModulatorInfo *modulatorInfo=nullptr)
Prepare the field definition for the yoke of a magnet.
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)
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
static void CheckBendLengthAngleWidthCombo(G4double arcLength, G4double angle, G4double horizontalWidth, const G4String &name="not given")
void PrepareCrystals()
Prepare all crystals in defined the parser.
static G4Colour * PrepareColour(GMAD::Element const *element, const G4Material *material=nullptr)
G4double lengthSafety
Length safety from global constants.
BDSMagnetStrength * PrepareCavityStrength(GMAD::Element const *el, BDSFieldType fieldType, G4double cavityLength, BDSMagnetStrength *&fringeIn, BDSMagnetStrength *&fringeOut) const
G4String elementName
Variable used to pass around the possibly modified name of an element.
std::map< G4String, BDSLaser * > lasers
Map of laser instances by name. Owned by this class.
static G4Material * PrepareTipMaterial(GMAD::Element const *element, const G4String &defaultMaterialName)
Checks if a material is named in Element::tipMaterial, else uses the supplied default.
std::map< G4String, G4int > modifiedElements
void GetKickValue(G4double &hkick, G4double &vkick, const KickerType type) const
BDSBeamlineIntegral * integralUpToThisComponent
To save passing it through many functions arguments.
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.
void SetModulatorDefinition(GMAD::Element const *el, BDSFieldInfo *info) const
void PrepareLasers()
Prepare all lasers defined in the parser.
const BDSIntegratorSet * integratorSet
Local copy of reference to integrator set to use.
BDSModulatorInfo * ModulatorDefinition(const GMAD::Element *el, G4bool inDevelopment=false) const
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)
G4double BRho() const
Simple accessor to simplify repetitive code.
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.
static G4double ScalingFieldOuter(const GMAD::Element *ele)
Get the scaling factor for a particular outer field depending on the global and individual setting.
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.
A piece of vacuum beam pipe.
Definition BDSDrift.hh:38
An infinite absorber.
Definition BDSDump.hh:32
A class for a generic piece of external geometry.
Definition BDSElement.hh:38
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:38
G4double DX() const
The difference in a dimension.
Definition BDSExtent.hh:82
G4double DY() const
The difference in a dimension.
Definition BDSExtent.hh:83
static G4double TransitTimeFactor(G4double frequency, G4double phase, G4double zLength, G4double beta)
General function put here as it represents the equations in this class.
static BDSFieldFactory * Instance()
Public accessor method for singleton pattern.
BDSFieldInfo * GetDefinition(const G4String &name) const
BDSModulatorInfo * GetModulatorDefinition(const G4String &modulatorName) const
All info required to build complete field of any type.
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.
void SetFieldAsThin()
Set thin to allow geant tracking error controls to be set seperately for thin elements.
BDSModulatorInfo * ModulatorInfo() const
Accessor.
BDSFieldType FieldType() const
Accessor.
void SetUserLimits(G4UserLimits *userLimitsIn)
Delete and replace the user limits which this class owns (only if not default ul).
Gabor lens based on prototype design at Imperial College (https://doi.org/10.3390/app11104357)
Gap in accelerator beamline.
Definition BDSGap.hh:31
Base class for gas capillary.
A class for a gas jet.
Definition BDSGasJet.hh:36
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.
Laserwire in beam pipe.
A laser wire scanner.
Class to provide laser intensity at any point.
Definition BDSLaser.hh:35
G4double Wavelength() const
Accessor.
Definition BDSLaser.hh:63
G4double Sigma0() const
Accessor.
Definition BDSLaser.hh:67
void SetT0(G4double T0In)
Accessor.
Definition BDSLaser.hh:72
A class that hold multiple accelerator components.
Definition BDSLine.hh:37
void AddComponent(BDSAcceleratorComponent *component)
Add a component to the line.
Definition BDSLine.cc:27
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:44
static BDSMaterials * Instance()
Singleton pattern access.
G4Material * GetMaterial(G4String material) const
Get material by name.
Holder class for all information required to describe a modulator.
static BDSParser * Instance()
Access method.
Definition BDSParser.cc:29
std::vector< GMAD::CavityModel > GetCavityModels() const
Return the parser list of that object.
Definition BDSParser.hh:98
GMAD::CoolingChannel GetCoolingChannel(const std::string &objectName)
Return a cooling channel object by name. Throws an exception if it doesn't exist.
Definition BDSParser.cc:117
std::vector< GMAD::Crystal > GetCrystals() const
Return the parser list of that object.
Definition BDSParser.hh:100
std::vector< GMAD::NewColour > GetColours() const
Return the parser list of that object.
Definition BDSParser.hh:99
std::vector< GMAD::Laser > GetLasers() const
Return the parser list of that object.
Definition BDSParser.hh:104
Wrapper for particle definition.
G4double Mass() const
Accessor.
G4double Momentum() const
Accessor.
G4double BRho() const
Accessor.
G4double Beta() const
Accessor.
G4double Velocity() const
Accessor.
G4double Gamma() const
Accessor.
A multilayer screen in a beam pipe.
Definition BDSScreen.hh:38
void AddScreenLayer(G4double thickness, G4String material, G4int isSampler=0)
Add a screen layer.
Definition BDSScreen.cc:67
A square mask of solid material with a square aperture.
Definition BDSShield.hh:39
A class for a box or cylinder piece of 1 material.
Definition BDSTarget.hh:35
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.
Single cylindrical wire inside beam pipe.
Cooling channel parameters.
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.
BDSCavityFieldType DetermineCavityFieldType(G4String cavityFieldType)
function to determine the enum type of the cavity field type (case-insensitive)
BDSMuonCooler * BuildMuonCooler(const G4String &elementName, G4double chordLength, G4double horizontalWidth, const GMAD::CoolingChannel &definition, BDSBeamPipeInfo *beamPipeInfo, G4double designRigidity)
G4UserLimits * CreateUserLimits(G4UserLimits *defaultUL, G4double length, G4double fraction=1.6)
BDSMagnet * BuildDipoleFringe(const GMAD::Element *element, G4double angleIn, G4double angleOut, const G4String &name, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, BDSFieldType dipoleFieldType, BDSModulatorInfo *fieldModulator=nullptr)
BDSLine * BuildRBendLine(const G4String &elementName, const GMAD::Element *element, const GMAD::Element *prevElement, const GMAD::Element *nextElement, G4double brho, BDSMagnetStrength *st, const BDSIntegratorSet *integratorSet, G4double incomingFaceAngle, G4double outgoingFaceAngle, G4bool buildFringeFields, BDSModulatorInfo *fieldModulator=nullptr)
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())
G4double ArcLengthFromChordLength(G4double chordLength, G4double angle)
Calculate the arc length from the chord length for a given angle.
BDSCavityType DetermineCavityType(G4String cavityType)
function to determine the enum type of the cavity geometry (case-insensitive)
BDSAcceleratorComponent * BuildSBendLine(const G4String &elementName, const GMAD::Element *element, BDSMagnetStrength *st, G4double brho, const BDSIntegratorSet *integratorSet, G4double incomingFaceAngle, G4double outgoingFaceAngle, G4bool buildFringeFields, const GMAD::Element *prevElement, const GMAD::Element *nextElement, BDSModulatorInfo *fieldModulator=nullptr)
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:27
std::string typestr(ElementType type)
conversion from enum to string
Element class.
Definition element.h:47
double hkick
fractional delta px for hkicker
Definition element.h:74
std::string dicomDataFile
for CT, file for DICOM construction data
Definition element.h:266
std::string spec
Arbitrary specification to pass to beamline builder.
Definition element.h:261
double twindow
thickness of window
Definition element.h:145
double B
magnetic field
Definition element.h:63
double aper4
beampipe information, new aperture model
Definition element.h:115
std::string beampipeMaterial
beampipe information, new aperture model
Definition element.h:117
double ysize2
size of the beam mask second aperture
Definition element.h:138
double laserOffsetTheta
for laserwire
Definition element.h:186
double rmat33
rmatrix elements, only 4x4
Definition element.h:101
double rmat11
rmatrix elements, only 4x4
Definition element.h:91
double rmat43
rmatrix elements, only 4x4
Definition element.h:105
double laserOffsetPhi
for laserwire
Definition element.h:187
std::list< std::string > layerMaterials
for screen
Definition element.h:151
double scaling
Overall scaling of field strength.
Definition element.h:54
std::string cavityFieldType
Name for type of field to use in a cavity.
Definition element.h:263
double windowScreenGap
air gap between window and screen
Definition element.h:147
std::string tipMaterial
tip material
Definition element.h:220
double gradient
for rf cavities in V / m
Definition element.h:78
double degraderOffset
for degrader
Definition element.h:181
double aper2
beampipe information, new aperture model
Definition element.h:113
double anodeLength
for gabor lens
Definition element.h:209
double fintx
fringe field integral at the dipole exit
Definition element.h:67
double kick4
rmatrix elements, only 4x4
Definition element.h:90
double k2
sextupole
Definition element.h:59
double rmat42
rmatrix elements, only 4x4
Definition element.h:104
double aper1
beampipe information, new aperture model
Definition element.h:112
double rmat41
rmatrix elements, only 4x4
Definition element.h:103
std::string outerShape
'rectangular' or 'circular'. used in gascap and bmcol
Definition element.h:141
double laserOffsetY
for laserwire
Definition element.h:189
double awakeMagnetOffsetX
for AWAKE spectrometer
Definition element.h:160
double zdir
for 3d transform and laser
Definition element.h:169
std::string namedVacuumVolumes
For imported geometry - identify vacuum volumes.
Definition element.h:259
double anodeThickness
for gabor lens
Definition element.h:211
bool stripOuterVolume
For Element. Make it an assembly.
Definition element.h:253
double screenPSize
for AWAKE spectrometer
Definition element.h:156
double xdir
for 3d transform and laser
Definition element.h:167
double rmat34
rmatrix elements, only 4x4
Definition element.h:102
double psi
for 3d transforms
Definition element.h:172
double rmat31
rmatrix elements, only 4x4
Definition element.h:99
double wedgeLength
for degrader
Definition element.h:178
double vhRatio
ratio of vertial to horizontal for some magnets
Definition element.h:126
double rmat22
rmatrix elements, only 4x4
Definition element.h:96
std::string userTypeName
User component element type name.
Definition element.h:50
double offsetY2
offset of the beam mask second aperture
Definition element.h:139
double kick3
rmatrix elements, only 4x4
Definition element.h:89
double rmat24
rmatrix elements, only 4x4
Definition element.h:98
double tOffset
time offset used for phase calculation (ns)
Definition element.h:82
std::list< std::string > biasMaterialList
Definition element.h:230
double xsizeRight
individual collimator jaw half widths
Definition element.h:132
double coilWidthFraction
Fraction of available h space the coil will take up.
Definition element.h:127
double rmat44
rmatrix elements, only 4x4
Definition element.h:106
double ks
solenoid
Definition element.h:56
std::string fieldAll
Field for everything.
Definition element.h:250
double electrodeRadius
for gabor lens
Definition element.h:213
double jawTiltRight
jaw collimator jaw tilts (angle in x-z plane)
Definition element.h:133
std::string region
region with range cuts
Definition element.h:247
double wireDiameter
@[ for wirescanner
Definition element.h:194
double tmount
thickness of the screen mount
Definition element.h:146
std::list< double > ksl
skew multipole expansion
Definition element.h:77
int numberWedges
for degrader
Definition element.h:177
double kick1
rmatrix elements, only 4x4
Definition element.h:87
std::list< std::string > biasMaterialLVList
physics biasing list for specific LVs
Definition element.h:234
std::list< double > knl
multipole expansion coefficients
Definition element.h:76
std::string windowmaterial
for AWAKE spectrometer
Definition element.h:161
double e2
output pole face rotation for bends
Definition element.h:65
double phase
phase of rf cavity (rad)
Definition element.h:81
double ysizeOut
collimator aperture or laser spotsize for laser
Definition element.h:131
std::string fieldVacuum
Vacuum field.
Definition element.h:249
bool autoColour
Automagically colour the external geometry.
Definition element.h:254
double angle
bending angle
Definition element.h:62
double electrodeThickness
for gabor lens
Definition element.h:214
int hStyle
-1 = unset; 0 = false (ie c style); 1 = true, use hstyle
Definition element.h:125
std::string geometryFile
For Element. File for external geometry.
Definition element.h:252
std::string vacuumMaterial
beampipe information, new aperture model
Definition element.h:118
double vkick
fractional delta py for vkicker
Definition element.h:75
double laserOffsetZ
for laserwire
Definition element.h:190
double E
voltage for rf cavities in V that will be assumed over length l
Definition element.h:79
double coilHeightFraction
Fraction of availalbe v space the coil will take up.
Definition element.h:128
std::list< int > layerIsSampler
for screen
Definition element.h:152
double laserOffsetX
for laserwire
Definition element.h:188
bool elementLengthIsArcLength
For Element. Treat the length as arc length, if not chord.
Definition element.h:256
double screenYSize
Definition element.h:148
double offsetX
offset X
Definition element.h:134
double screenEndZ
for AWAKE spectrometer
Definition element.h:157
double ysize
collimator aperture or laser spotsize for laser
Definition element.h:130
std::string coolingDefinition
For muon cooler.
Definition element.h:278
double rmat23
rmatrix elements, only 4x4
Definition element.h:97
std::string colour
Override colour for certain items.
Definition element.h:269
double rmat13
rmatrix elements, only 4x4
Definition element.h:93
double anodeRadius
for gabor lens
Definition element.h:210
double tilt
tilt
Definition element.h:129
double materialThickness
for degrader
Definition element.h:180
double l
length in metres
Definition element.h:53
double kick
fractional delta p for either h or v kicker
Definition element.h:73
double rmat14
rmatrix elements, only 4x4
Definition element.h:94
double beampipeThickness
beampipe information, new aperture model
Definition element.h:111
std::string dicomDataPath
for CT, file for DICOM construction data
Definition element.h:265
double wavelength
for laser wire and 3d transforms
Definition element.h:171
double minimumKineticEnergy
minimum kinetic energy for user limits - respected on element by element basis
Definition element.h:237
std::string scintmaterial
for AWAKE spectrometer
Definition element.h:162
double ydir
for 3d transform and laser
Definition element.h:168
double undulatorMagnetHeight
for undulator
Definition element.h:205
double tipThickness
for jaw collimator with tip
Definition element.h:219
std::string cavityModel
Name of geometry model object for rfconstantinz cavities.
Definition element.h:262
double e1
input pole face rotation for bends
Definition element.h:64
double k1
quadrupole
Definition element.h:58
std::string fieldOuter
Outer field.
Definition element.h:248
std::string laserBeam
for laserwire
Definition element.h:185
double undulatorPeriod
for undulator
Definition element.h:203
double electrodeLength
for gabor lens
Definition element.h:212
double tilt2
tilt of the beam mask second aperture
Definition element.h:140
double undulatorGap
for undulator
Definition element.h:204
double poleStartZ
for AWAKE spectrometer
Definition element.h:158
bool angleSet
Definition element.h:283
std::string mountmaterial
for AWAKE spectrometer
Definition element.h:163
double k4
decapole
Definition element.h:61
double aper3
beampipe information, new aperture model
Definition element.h:114
ElementType type
element enum
Definition element.h:48
std::list< std::string > biasVacuumList
physics biasing list for the vacuum
Definition element.h:232
double offsetY
offset Y
Definition element.h:135
std::list< double > layerThicknesses
for screen
Definition element.h:150
double scalingFieldOuter
Extra arbitrary scaling for outer field - compounded with 'scaling'.
Definition element.h:55
double kg
gabor lens
Definition element.h:84
double degraderHeight
for degrader
Definition element.h:179
double tscint
thickness of scintillating part of screen
Definition element.h:144
double k3
octupole
Definition element.h:60
double screenWidth
for AWAKE spectrometer
Definition element.h:159
double frequency
frequency for rf cavity in Hz
Definition element.h:80
double kick2
rmatrix elements, only 4x4
Definition element.h:88
double rmat12
rmatrix elements, only 4x4
Definition element.h:92
std::string apertureType
beampipe information, new aperture model
Definition element.h:116
double rmat21
rmatrix elements, only 4x4
Definition element.h:95
double rmat32
rmatrix elements, only 4x4
Definition element.h:100