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