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