BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSFieldFactory.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2024.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "BDSArrayReflectionType.hh"
20#include "BDSBeamPipeInfo.hh"
21#include "BDSDebug.hh"
22#include "BDSException.hh"
23#include "BDSPTCOneTurnMap.hh"
24#include "BDSPrimaryGeneratorAction.hh"
25#include "BDSFieldClassType.hh"
26#include "BDSFieldE.hh"
27#include "BDSFieldEGlobal.hh"
28#include "BDSFieldEGlobalPlacement.hh"
29#include "BDSFieldEInterpolated.hh"
30#include "BDSFieldEInterpolated2Layer.hh"
31#include "BDSFieldESinusoid.hh"
32#include "BDSFieldEZero.hh"
33#include "BDSFieldEM.hh"
34#include "BDSFieldEMGlobal.hh"
35#include "BDSFieldEMGlobalPlacement.hh"
36#include "BDSFieldEMInterpolated.hh"
37#include "BDSFieldEMMuonCooler.hh"
38#include "BDSFieldEMRFCavity.hh"
39#include "BDSFieldEMZero.hh"
40#include "BDSFieldFactory.hh"
41#include "BDSFieldGaborLens.hh"
42#include "BDSFieldInfo.hh"
43#include "BDSFieldInfoExtra.hh"
44#include "BDSFieldLoader.hh"
45#include "BDSFieldMag.hh"
46#include "BDSFieldMagDecapole.hh"
47#include "BDSFieldMagDipole.hh"
48#include "BDSFieldMagDipoleOuter.hh"
49#include "BDSFieldMagDipoleOuterOld.hh"
50#include "BDSFieldMagDipoleQuadrupole.hh"
51#include "BDSFieldMagGlobal.hh"
52#include "BDSFieldMagGlobalPlacement.hh"
53#include "BDSFieldMagInterpolated.hh"
54#include "BDSFieldMagInterpolated2Layer.hh"
55#include "BDSFieldMagMultipole.hh"
56#include "BDSFieldMagMultipoleOuter.hh"
57#include "BDSFieldMagMultipoleOuterDual.hh"
58#include "BDSFieldMagMultipoleOuterDualOld.hh"
59#include "BDSFieldMagMultipoleOuterOld.hh"
60#include "BDSFieldMagMuonSpoiler.hh"
61#include "BDSFieldMagOctupole.hh"
62#include "BDSFieldMagQuadrupole.hh"
63#include "BDSFieldMagSextupole.hh"
64#include "BDSFieldMagSolenoidBlock.hh"
65#include "BDSFieldMagSolenoidSheet.hh"
66#include "BDSFieldMagSolenoidLoop.hh"
67#include "BDSFieldMagSkewOwn.hh"
68#include "BDSFieldMagUndulator.hh"
69#include "BDSFieldMagZero.hh"
70#include "BDSFieldObjects.hh"
71#include "BDSFieldType.hh"
72#include "BDSGlobalConstants.hh"
73#include "BDSIntegratorCavityFringe.hh"
74#include "BDSIntegratorDecapole.hh"
75#include "BDSIntegratorDipoleRodrigues.hh"
76#include "BDSIntegratorDipoleRodrigues2.hh"
77#include "BDSIntegratorDipoleFringe.hh"
78#include "BDSIntegratorDipoleFringeScaling.hh"
79#include "BDSIntegratorDipoleQuadrupole.hh"
80#include "BDSIntegratorEuler.hh"
81#include "BDSIntegratorG4RK4MinStep.hh"
82#include "BDSIntegratorKickerThin.hh"
83#include "BDSIntegratorOctupole.hh"
84#include "BDSIntegratorQuadrupole.hh"
85#include "BDSIntegratorMultipoleThin.hh"
86#include "BDSIntegratorParallelTransport.hh"
87#include "BDSIntegratorSextupole.hh"
88#include "BDSIntegratorSolenoid.hh"
89#include "BDSIntegratorTeleporter.hh"
90#include "BDSIntegratorRMatrixThin.hh"
91#include "BDSIntegratorType.hh"
92#include "BDSMagnetOuterFactoryLHC.hh"
93#include "BDSMagnetStrength.hh"
94#include "BDSMagnetType.hh"
95#include "BDSModulator.hh"
96#include "BDSModulatorInfo.hh"
97#include "BDSModulatorLinearT.hh"
98#include "BDSModulatorSinT.hh"
99#include "BDSModulatorTopHatT.hh"
100#include "BDSModulatorType.hh"
101#include "BDSParser.hh"
102#include "BDSParticleDefinition.hh"
103#include "BDSUtilities.hh"
104#include "BDSWarning.hh"
105
106#include "parser/field.h"
107#include "parser/modulator.h"
108
109#include "globals.hh" // geant4 types / globals
110#include "G4EquationOfMotion.hh"
111#include "G4EqMagElectricField.hh"
112#include "G4MagIntegratorStepper.hh"
113#include "G4Mag_UsualEqRhs.hh"
114#include "G4RotationMatrix.hh"
115#include "G4String.hh"
116#include "G4ThreeVector.hh"
117#include "G4Transform3D.hh"
118#include "G4Version.hh"
119
120// geant4 integrators
121#include "G4CashKarpRKF45.hh"
122#include "G4ClassicalRK4.hh"
123#include "G4ConstRK4.hh"
124#include "G4ExactHelixStepper.hh"
125#include "G4ExplicitEuler.hh"
126#include "G4HelixExplicitEuler.hh"
127#include "G4HelixHeum.hh"
128#include "G4HelixImplicitEuler.hh"
129#include "G4HelixMixedStepper.hh"
130#include "G4HelixSimpleRunge.hh"
131#include "G4ImplicitEuler.hh"
132#include "G4NystromRK4.hh"
133#include "G4RKG3_Stepper.hh"
134#include "G4SimpleHeum.hh"
135#include "G4SimpleRunge.hh"
136#if G4VERSION_NUMBER > 1029
137#include "G4BogackiShampine23.hh"
138#include "G4BogackiShampine45.hh"
139#include "G4DoLoMcPriRK34.hh"
140#include "G4DormandPrince745.hh"
141#include "G4DormandPrinceRK56.hh"
142#include "G4TsitourasRK45.hh"
143#endif
144#if G4VERSION_NUMBER > 1039
145#include "G4DormandPrinceRK78.hh"
146#include "G4RK547FEq1.hh"
147#include "G4RK547FEq2.hh"
148#include "G4RK547FEq3.hh"
149#endif
150
151#include "CLHEP/Units/SystemOfUnits.h"
152#include "CLHEP/Vector/EulerAngles.h"
153
154#include <limits>
155#include <map>
156#include <utility>
157#include <vector>
158
161
163
165{
166 if (!instance)
167 {instance = new BDSFieldFactory();}
168 return instance;
169}
170
172 useOldMultipoleOuterFields(false)
173{
174 G4double defaultRigidity = std::numeric_limits<double>::max();
175 if (designParticle)
176 {defaultRigidity = designParticle->BRho();}
177 // we do this so we don't have to have used the parser to use this class
179 {
181 PrepareFieldDefinitions(BDSParser::Instance()->GetFields(), defaultRigidity);
182 }
183 useOldMultipoleOuterFields = BDSGlobalConstants::Instance()->UseOldMultipoleOuterFields();
184}
185
186BDSFieldFactory::~BDSFieldFactory()
187{
188 for (auto& info : parserDefinitions)
189 {delete info.second;}
190 for (auto& info : parserModulatorDefinitions)
191 {delete info.second;}
192}
193
194void BDSFieldFactory::PrepareFieldDefinitions(const std::vector<GMAD::Field>& definitions,
195 G4double defaultBRho)
196{
197 for (const auto& definition : definitions)
198 {
199 if (definition.type.empty())
200 {
201 G4String msg = "\"type\" not specified in field definition \"";
202 msg += definition.name + "\", but required.";
203 throw BDSException(__METHOD_NAME__, msg);
204 }
205 BDSFieldType fieldType = BDS::DetermineFieldType(definition.type);
206 BDSIntegratorType intType = BDS::DetermineIntegratorType(definition.integrator);
207
208 G4ThreeVector offset = G4ThreeVector(definition.x*CLHEP::m,
209 definition.y*CLHEP::m,
210 definition.z*CLHEP::m);
211
212 G4RotationMatrix rm;
213 if (definition.axisAngle)
214 {
215 G4ThreeVector axis = G4ThreeVector(definition.axisX,
216 definition.axisY,
217 definition.axisZ);
218 rm = G4RotationMatrix(axis, definition.angle*CLHEP::rad);
219 }
220 else
221 {
222 if (BDS::IsFinite(definition.phi) ||
223 BDS::IsFinite(definition.theta) ||
224 BDS::IsFinite(definition.psi))
225 {// only build if finite
226 CLHEP::HepEulerAngles ang = CLHEP::HepEulerAngles(definition.phi*CLHEP::rad,
227 definition.theta*CLHEP::rad,
228 definition.psi*CLHEP::rad);
229 rm = G4RotationMatrix(ang);
230 }
231 // else rm is default rotation matrix
232 }
233
234 G4Transform3D transform = G4Transform3D(rm, offset);
235
236 BDSFieldFormat magFormat = BDSFieldFormat::none;
237 G4String magFile = "";
238 G4bool magFileSpecified = !definition.magneticFile.empty();
239 if (magFileSpecified)
240 {
241 std::pair<G4String, G4String> bf = BDS::SplitOnColon(G4String(definition.magneticFile));
242 magFormat = BDS::DetermineFieldFormat(bf.first);
243 magFile = BDS::GetFullPath(bf.second);
244 }
245
246 BDSFieldFormat eleFormat = BDSFieldFormat::none;
247 G4String eleFile = "";
248 G4bool eleFileSpecified = !definition.electricFile.empty();
249 if (eleFileSpecified)
250 {
251 std::pair<G4String, G4String> ef = BDS::SplitOnColon(G4String(definition.electricFile));
252 eleFormat = BDS::DetermineFieldFormat(ef.first);
253 eleFile = BDS::GetFullPath(ef.second);
254 }
255
256 BDSInterpolatorType magIntType = BDSInterpolatorType::cubic3d;
257 if (magFileSpecified)
258 {// determine and check type of integrator
259 G4int nDimFF = BDS::NDimensionsOfFieldFormat(magFormat);
260 if (!definition.magneticInterpolator.empty())
261 {
262 magIntType = BDS::DetermineInterpolatorType(G4String(definition.magneticInterpolator));
263 // detect if an auto type and match up accordingly, else check it's the right one
264 if (BDS::InterpolatorTypeIsAuto(magIntType))
265 {magIntType = BDS::InterpolatorTypeSpecificFromAuto(nDimFF, magIntType);}
266 else
267 {
268 G4int nDimInt = BDS::NDimensionsOfInterpolatorType(magIntType);
269 if (nDimFF != nDimInt)
270 {
271 G4String message = "mismatch in number of dimensions between magnetic interpolator ";
272 message += "and field map format for field definition \"" + definition.name + "\"";
273 throw BDSException(__METHOD_NAME__, message);
274 }
275 }
276 }
277 else
278 {magIntType = DefaultInterpolatorType(nDimFF);}
279 }
280
281 BDSInterpolatorType eleIntType = BDSInterpolatorType::cubic3d;
282 if (eleFileSpecified)
283 {// determine and check type of integrator
284 G4int nDimFF = BDS::NDimensionsOfFieldFormat(eleFormat);
285 if (!definition.electricInterpolator.empty())
286 {
287 eleIntType = BDS::DetermineInterpolatorType(G4String(definition.electricInterpolator));
288 // detect if an auto type and match up accordingly, else check it's the right one
289 if (BDS::InterpolatorTypeIsAuto(eleIntType))
290 {eleIntType = BDS::InterpolatorTypeSpecificFromAuto(nDimFF, eleIntType);}
291 else
292 {
293 G4int nDimInt = BDS::NDimensionsOfInterpolatorType(eleIntType);
294 if (nDimFF != nDimInt)
295 {
296 G4String message = "mismatch in number of dimensions between electric interpolator ";
297 message += "and field map format for field definition \"" + definition.name + "\"";
298 throw BDSException(__METHOD_NAME__, message);
299 }
300 }
301 }
302 else
303 {eleIntType = DefaultInterpolatorType(nDimFF);}
304 }
305
306 G4UserLimits* fieldLimit = nullptr;
307 G4bool ignoreUpdateOfMaximumStepSize = definition.maximumStepLengthOverride > 0;
308 if (definition.maximumStepLength > 0 || ignoreUpdateOfMaximumStepSize)
309 {// only assign if specified
310 auto defaultUL = BDSGlobalConstants::Instance()->DefaultUserLimits();
311 // copy the default and update with the length of the object rather than the default 1m
312 G4double limit = G4double(definition.maximumStepLength) * CLHEP::m;
313 if (ignoreUpdateOfMaximumStepSize)
314 {
315 limit = definition.maximumStepLengthOverride * CLHEP::m;
316 G4cout << __METHOD_NAME__ << "maximumStepLengthOverride set to " << limit << " mm for field definition \""
317 << definition.name << "\" -> careful!" << G4endl;
318 }
319 G4UserLimits* ul = BDS::CreateUserLimits(defaultUL, limit, 1.0);
320 // only specify a user limit object if the step length was specified
321 if (ul != defaultUL)
322 {fieldLimit = ul;}
323 }
324
326 G4double poleTipRadius = BDSGlobalConstants::Instance()->DefaultBeamPipeModel()->aper1;
327 if (!definition.fieldParameters.empty())
328 {PrepareFieldStrengthFromParameters(st, definition.fieldParameters, poleTipRadius);}
329
330 BDSFieldInfo* info = new BDSFieldInfo(fieldType,
331 defaultBRho,
332 intType,
333 st,
334 G4bool(definition.globalTransform),
335 transform,
336 magFile,
337 magFormat,
338 magIntType,
339 eleFile,
340 eleFormat,
341 eleIntType,
342 false, /*don't cache transforms*/
343 G4double(definition.eScaling),
344 G4double(definition.bScaling),
345 G4double(definition.t*CLHEP::s),
346 G4bool(definition.autoScale),
347 fieldLimit);
348 info->SetScalingRadius(poleTipRadius);
349 info->SetModulatorInfo(GetModulatorDefinition(definition.fieldModulator));
350 if (ignoreUpdateOfMaximumStepSize)
351 {info->SetIgnoreUpdateOfMaximumStepSize(true);}
352
353 if (!definition.magneticSubField.empty())
354 {
355 if (definition.magneticSubField == definition.name)
356 {throw BDSException(__METHOD_NAME__, "error in \"" + definition.name + "\": magneticSubField cannot be the field itself");}
357 info->SetMagneticSubField(G4String(definition.magneticSubField));
358 }
359 if (!definition.electricSubField.empty())
360 {
361 if (definition.electricSubField == definition.name)
362 {throw BDSException(__METHOD_NAME__, "error in \"" + definition.name + "\": electricSubField cannot be the field itself");}
363 info->SetElectricSubField(G4String(definition.electricSubField));
364 }
365 if (!definition.magneticReflection.empty())
366 {
367 G4String magneticReflection = G4String(definition.magneticReflection);
368 BDSArrayReflectionTypeSet mar = BDS::DetermineArrayReflectionTypeSet(magneticReflection);
369 info->SetMagneticArrayReflectionType(mar);
370 }
371 if (!definition.electricReflection.empty())
372 {
373 G4String electricReflection = G4String(definition.electricReflection);
374 BDSArrayReflectionTypeSet ear = BDS::DetermineArrayReflectionTypeSet(electricReflection);
375 info->SetElectricArrayReflectionType(ear);
376 }
377
378 info->SetNameOfParserDefinition(G4String(definition.name));
379 if (BDSGlobalConstants::Instance()->Verbose())
380 {
381 G4cout << "Definition: \"" << definition.name << "\"" << G4endl;
382 G4cout << *info << G4endl;
383 }
384 parserDefinitions[G4String(definition.name)] = info;
385 }
386}
387
388void BDSFieldFactory::PrepareModulatorDefinitions(const std::vector<GMAD::Modulator>& definitions)
389{
390 for (const auto& definition : definitions)
391 {
392 if (definition.type.empty())
393 {
394 G4String msg = "\"type\" not specified in modulator definition \"";
395 msg += definition.name + "\", but required.";
396 throw BDSException(__METHOD_NAME__, msg);
397 }
398 BDSModulatorType modulatorType = BDS::DetermineModulatorType(definition.type);
399
400 G4double frequency = definition.frequency * CLHEP::hertz;
401 G4double phase = definition.phase * CLHEP::rad;
402 if (BDS::IsFinite(definition.tOffset))
403 {
404 if (BDS::IsFinite(definition.tOffset))
405 {throw BDSException(__METHOD_NAME__, "definition: " + definition.name + " has both tOffset and phase specified - only one can be used.");}
406 phase = definition.tOffset*CLHEP::s * frequency * CLHEP::twopi;
407 }
408
409 // We can't calculate any global phase here because this one modulator info may
410 // be used by multiple beam line elements at different locations
411 BDSModulatorInfo* info = new BDSModulatorInfo(modulatorType,
412 frequency,
413 phase,
414 definition.tOffset * CLHEP::s,
415 definition.amplitudeScale,
416 definition.amplitudeOffset,
417 definition.T0 * CLHEP::s,
418 definition.T1 * CLHEP::s);
419 info->nameOfParserDefinition = definition.name;
420 parserModulatorDefinitions[G4String(definition.name)] = info;
421 }
422}
423
425 const G4String& parameterNameForError) const
426{
427 G4double result = 0;
428 try
429 {result = std::stod(value);}
430 catch (std::exception& e)
431 {
432 G4String msg(e.what());
433 G4String baseMsg = "Unable to interpret value (\"" + value + "\" of parameter \"";
434 baseMsg += parameterNameForError + "\" as a number: ";
435 throw BDSException(__METHOD_NAME__, baseMsg + msg);
436 }
437 return result;
438}
439
441 const G4String& fieldParameters,
442 G4double& poleTipRadius) const
443{
444 // use function from BDSUtilities to process user params string into
445 // map of strings and values.
446 std::map<G4String, G4String> map = BDS::GetUserParametersMap(fieldParameters, '=');
447
448 for (const auto& keyValue : map)
449 {
450 if (BDSMagnetStrength::ValidKey(keyValue.first))
451 {
452 G4double value = ConvertToDoubleWithException(keyValue.second, keyValue.first);
453 (*st)[keyValue.first] = value * BDSMagnetStrength::Unit(keyValue.first);
454 }
455 else if (keyValue.first == "poletipradius")
456 {poleTipRadius = ConvertToDoubleWithException(keyValue.second, keyValue.first) * CLHEP::m;}
457 else
458 {
459 G4String msg = "Invalid key \"" + keyValue.first + "\" for field parameters. ";
460 msg += "Acceptable parameters are: \n";
461 const std::vector<G4String>& allKeys = BDSMagnetStrength::AllKeys();
462 for (G4int i = 0; i < (G4int)allKeys.size(); i++)
463 {
464 msg += allKeys[i] + ", ";
465 if ((i % 10 == 0) && (i > 0))
466 {msg += "\n";}
467 }
468 throw BDSException(__METHOD_NAME__, msg);
469 }
470 }
471}
472
474{
475 // Here we test if the string is empty and return nullptr. We do this so
476 // this method can be used without exiting when no key is specified at all.
477 // If a key is given and not found, then that requires the users attention.
478 if (name.empty())
479 {return nullptr;}
480 auto result = parserDefinitions.find(name);
481 if (result == parserDefinitions.end())
482 {// not a valid key
483 G4cerr << __METHOD_NAME__ << "\"" << name << "\" is not a valid field specifier" << G4endl;
484 G4cout << "Defined field specifiers are:" << G4endl;
485 for (const auto& it : parserDefinitions)
486 {G4cout << "\"" << it.first << "\"" << G4endl;}
487 throw BDSException(__METHOD_NAME__, "invalid field name");
488 }
489 return result->second;
490}
491
493{
494 if (modulatorName.empty())
495 {return nullptr;}
496
497 auto search = parserModulatorDefinitions.find(modulatorName);
498 if (search == parserModulatorDefinitions.end())
499 {
500 G4cerr << __METHOD_NAME__ << "\"" << modulatorName << "\" is not a valid modulator definition name" << G4endl;
501 G4cout << "Defined modulator definitions are:" << G4endl;
502 for (const auto& it : parserModulatorDefinitions)
503 {G4cout << "\"" << it.first << "\"" << G4endl;}
504 throw BDSException(__METHOD_NAME__, "invalid modulator name");
505 }
506 else
507 {return search->second;}
508}
509
511 const BDSMagnetStrength* scalingStrength,
512 const G4String& scalingKey)
513{
514 // Forward on to delegate functions for the main types of field
515 // such as E, EM and Magnetic
516 BDSFieldObjects* field = nullptr;
517
518 if (info.FieldType() == BDSFieldType::none)
519 {return field;} // as nullptr
520
522 try
523 {
524 switch (clas.underlying())
525 {
526 case BDSFieldClassType::magnetic:
527 {field = CreateFieldMag(info, scalingStrength, scalingKey); break;}
528 case BDSFieldClassType::electromagnetic:
529 {field = CreateFieldEM(info); break;}
530 case BDSFieldClassType::electric:
531 {field = CreateFieldE(info); break;}
532 case BDSFieldClassType::irregular:
533 {field = CreateFieldIrregular(info); break;}
534 default:
535 {break;} // this will return nullptr
536 }
537 }
538 catch (BDSException& e)
539 {
540 e.AppendToMessage("\nProblem with field possibly named \"" + info.NameOfParserDefinition() + "\"");
541 throw e;
542 }
543 return field;
544}
545
547{
548 BDSInterpolatorType result;
549 switch (numberOfDimensions)
550 {
551 case 1:
552 {result = BDSInterpolatorType::cubic1d; break;}
553 case 2:
554 {result = BDSInterpolatorType::cubic2d; break;}
555 case 3:
556 {result = BDSInterpolatorType::cubic3d; break;}
557 case 4:
558 {result = BDSInterpolatorType::cubic4d; break;}
559 default:
560 {throw BDSException(__METHOD_NAME__, "unsupported number of dimensions " + std::to_string(numberOfDimensions));}
561 }
562 return result;
563}
564
566 const BDSMagnetStrength* scalingStrength,
567 const G4String& scalingKey)
568{
569 const BDSMagnetStrength* strength = info.MagnetStrength();
570 BDSFieldMag* field = CreateFieldMagRaw(info, scalingStrength, scalingKey);
571 if (!field)
572 {return nullptr;} // return nullptr of right type
573
574 BDSFieldMag* resultantField = field;
575 // Optionally provide local to global transform using curvilinear coordinate system.
577 {resultantField = new BDSFieldMagGlobalPlacement(field);}
578 else if (info.ProvideGlobal())
579 {resultantField = new BDSFieldMagGlobal(field);}
580
581 // Always this equation of motion for magnetic (only) fields
582 BDSMagUsualEqRhs* eqOfM = new BDSMagUsualEqRhs(resultantField);
583
584 // Create appropriate integrator
585 G4MagIntegratorStepper* integrator = CreateIntegratorMag(info, eqOfM, strength);
586
587 BDSFieldObjects* completeField = new BDSFieldObjects(&info, resultantField, eqOfM, integrator);
588 return completeField;
589}
590
592 const BDSMagnetStrength* scalingStrength,
593 const G4String& scalingKey)
594{
595 BDSFieldMag* field = nullptr;
596 const BDSMagnetStrength* strength = info.MagnetStrength();
597 G4double brho = info.BRho();
598 G4double poleTipRadius = info.PoleTipRadius();
599 G4double beamPipeRadius = info.BeamPipeRadius();
600 switch (info.FieldType().underlying())
601 {
602 case BDSFieldType::bmap1d:
603 case BDSFieldType::bmap2d:
604 case BDSFieldType::bmap3d:
605 case BDSFieldType::bmap4d:
606 case BDSFieldType::mokka:
607 {
609 scalingStrength,
610 scalingKey);
611 if (ff)
612 {info.UpdateUserLimitsLengthMaximumStepSize(ff->SmallestSpatialStep(), true);}
613 field = ff;
614 break;
615 }
616 case BDSFieldType::bfieldzero:
617 {field = new BDSFieldMagZero(); break;}
618 case BDSFieldType::solenoid:
619 case BDSFieldType::dipole:
620 case BDSFieldType::dipole3d:
621 {field = new BDSFieldMagDipole(strength); break;}
622 case BDSFieldType::solenoidblock:
623 {field = new BDSFieldMagSolenoidBlock(strength, poleTipRadius); break;}
624 case BDSFieldType::solenoidloop:
625 {field = new BDSFieldMagSolenoidLoop(strength, poleTipRadius); break;}
626 case BDSFieldType::solenoidsheet:
627 {field = new BDSFieldMagSolenoidSheet(strength, poleTipRadius); break;}
628 case BDSFieldType::quadrupole:
629 {field = new BDSFieldMagQuadrupole(strength, brho); break;}
630 case BDSFieldType::undulator:
631 {field = new BDSFieldMagUndulator(strength, beamPipeRadius); break;}
632 case BDSFieldType::dipolequadrupole:
633 {field = new BDSFieldMagDipoleQuadrupole(strength, brho); break;}
634 case BDSFieldType::sextupole:
635 {field = new BDSFieldMagSextupole(strength, brho); break;}
636 case BDSFieldType::octupole:
637 {field = new BDSFieldMagOctupole(strength, brho); break;}
638 case BDSFieldType::decapole:
639 {field = new BDSFieldMagDecapole(strength, brho); break;}
640 case BDSFieldType::multipole:
641 {field = new BDSFieldMagMultipole(strength, brho); break;}
642 case BDSFieldType::muonspoiler:
643 {field = new BDSFieldMagMuonSpoiler(strength, brho); break;}
644 case BDSFieldType::skewquadrupole:
645 {field = new BDSFieldMagSkewOwn(new BDSFieldMagQuadrupole(strength, brho), CLHEP::pi/4.); break;}
646 case BDSFieldType::skewsextupole:
647 {field = new BDSFieldMagSkewOwn(new BDSFieldMagSextupole(strength, brho), CLHEP::pi/6.); break;}
648 case BDSFieldType::skewoctupole:
649 {field = new BDSFieldMagSkewOwn(new BDSFieldMagOctupole(strength, brho), CLHEP::pi/8.); break;}
650 case BDSFieldType::skewdecapole:
651 {field = new BDSFieldMagSkewOwn(new BDSFieldMagDecapole(strength, brho), CLHEP::pi/10.); break;}
652 case BDSFieldType::multipoleouterdipole:
653 {// suitable only for querying transversely in x,y - no 3d nature
654 BDSFieldMag* innerField = new BDSFieldMagDipole(strength);
655 G4bool positiveField = (*strength)["field"] < 0; // convention for dipoles - "positive"
656 if (useOldMultipoleOuterFields)
657 {field = new BDSFieldMagMultipoleOuterOld(1, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
658 else // positive field for a dipole has no meaning for this class - fix at false
659 {field = new BDSFieldMagMultipoleOuter(1, poleTipRadius, innerField, false, brho, GetOuterScaling(strength));}
660 delete innerField; // no longer required
661 break;
662 }
663 case BDSFieldType::multipoleouterquadrupole:
664 {
665 BDSFieldMag* innerField = new BDSFieldMagQuadrupole(strength, brho);
666 G4bool positiveField = (*strength)["k1"] > 0;
667 if (useOldMultipoleOuterFields)
668 {field = new BDSFieldMagMultipoleOuterOld(2, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
669 else
670 {field = new BDSFieldMagMultipoleOuter(2, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
671 delete innerField; // no longer required
672 break;
673 }
674 case BDSFieldType::multipoleoutersextupole:
675 {
676 BDSFieldMag* innerField = new BDSFieldMagSextupole(strength, brho);
677 G4bool positiveField = (*strength)["k2"] > 0;
678 if (useOldMultipoleOuterFields)
679 {field = new BDSFieldMagMultipoleOuterOld(3, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
680 else
681 {field = new BDSFieldMagMultipoleOuter(3, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
682 delete innerField; // no longer required
683 break;
684 }
685 case BDSFieldType::multipoleouteroctupole:
686 {
687 BDSFieldMag* innerField = new BDSFieldMagOctupole(strength, brho);
688 G4bool positiveField = (*strength)["k3"] > 0;
689 if (useOldMultipoleOuterFields)
690 {field = new BDSFieldMagMultipoleOuterOld(4, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
691 else
692 {field = new BDSFieldMagMultipoleOuter(4, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
693 delete innerField; // no longer required
694 break;
695 }
696 case BDSFieldType::multipoleouterdecapole:
697 {
698 BDSFieldMag* innerField = new BDSFieldMagDecapole(strength, brho);
699 G4bool positiveField = (*strength)["k4"] > 0;
700 if (useOldMultipoleOuterFields)
701 {field = new BDSFieldMagMultipoleOuterOld(5, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
702 else
703 {field = new BDSFieldMagMultipoleOuter(5, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
704 delete innerField; // no longer required
705 break;
706 }
707 case BDSFieldType::skewmultipoleouterquadrupole:
708 {
709 BDSFieldMag* innerField = new BDSFieldMagQuadrupole(strength, brho);
710 G4bool positiveField = (*strength)["k1"] > 0;
711 BDSFieldMag* normalField;
712 if (useOldMultipoleOuterFields)
713 {normalField = new BDSFieldMagMultipoleOuterOld(2, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
714 else
715 {normalField = new BDSFieldMagMultipoleOuter(2, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
716 field = new BDSFieldMagSkewOwn(normalField, CLHEP::pi/4.);
717 delete innerField; // no longer required
718 break;
719 }
720 case BDSFieldType::skewmultipoleoutersextupole:
721 {
722 BDSFieldMag* innerField = new BDSFieldMagSextupole(strength, brho);
723 G4bool positiveField = (*strength)["k2"] > 0;
724 BDSFieldMag* normalField;
725 if (useOldMultipoleOuterFields)
726 {normalField = new BDSFieldMagMultipoleOuterOld(3, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
727 else
728 {normalField = new BDSFieldMagMultipoleOuter(3, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
729 field = new BDSFieldMagSkewOwn(normalField, CLHEP::pi/6.);
730 delete innerField; // no longer required
731 break;
732 }
733 case BDSFieldType::skewmultipoleouteroctupole:
734 {
735 BDSFieldMag* innerField = new BDSFieldMagOctupole(strength, brho);
736 G4bool positiveField = (*strength)["k3"] > 0;
737 BDSFieldMag* normalField;
738 if (useOldMultipoleOuterFields)
739 {normalField = new BDSFieldMagMultipoleOuterOld(4, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
740 else
741 {normalField = new BDSFieldMagMultipoleOuter(4, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
742 field = new BDSFieldMagSkewOwn(normalField, CLHEP::pi/8.);
743 delete innerField; // no longer required
744 break;
745 }
746 case BDSFieldType::skewmultipoleouterdecapole:
747 {
748 BDSFieldMag* innerField = new BDSFieldMagDecapole(strength, brho);
749 G4bool positiveField = (*strength)["k4"] > 0;
750 BDSFieldMag* normalField;
751 if (useOldMultipoleOuterFields)
752 {normalField = new BDSFieldMagMultipoleOuterOld(5, poleTipRadius, innerField, positiveField, GetOuterScaling(strength));}
753 else
754 {normalField = new BDSFieldMagMultipoleOuter(5, poleTipRadius, innerField, positiveField, brho, GetOuterScaling(strength));}
755 field = new BDSFieldMagSkewOwn(normalField, CLHEP::pi/10.);
756 delete innerField; // no longer required
757 break;
758 }
759 case BDSFieldType::multipoleouterdipole3d:
760 {
761 if (useOldMultipoleOuterFields)
762 {field = new BDSFieldMagDipoleOuterOld(strength, poleTipRadius, GetOuterScaling(strength));}
763 else
764 {field = new BDSFieldMagDipoleOuter(strength, poleTipRadius, GetOuterScaling(strength));}
765 break;
766 }
767 case BDSFieldType::multipoleouterdipolelhc:
768 {
769 BDSFieldMag* innerField = new BDSFieldMagDipole(strength);
770 G4bool positiveField = (*strength)["field"] < 0; // convention for dipoles - "positive"
771 G4bool positiveField2 = (*strength)["angle"] > 0;
773 if (useOldMultipoleOuterFields)
774 {
775 field = new BDSFieldMagMultipoleOuterDualOld(1, poleTipRadius, innerField, positiveField, dx,
776 info.SecondFieldOnLeft(), GetOuterScaling(strength));
777 }
778 else
779 {
780 field = new BDSFieldMagMultipoleOuterDual(1, poleTipRadius, innerField, positiveField2, brho, dx,
781 info.SecondFieldOnLeft(), true, GetOuterScaling(strength));
782 }
783 delete innerField; // no longer required
784 break;
785 }
786 case BDSFieldType::multipoleouterquadrupolelhc:
787 {
788 BDSFieldMag* innerField = new BDSFieldMagQuadrupole(strength, brho);
789 G4bool positiveField = (*strength)["k1"] > 0;
791 if (useOldMultipoleOuterFields)
792 {
793 field = new BDSFieldMagMultipoleOuterDualOld(2, poleTipRadius, innerField, positiveField, dx,
794 info.SecondFieldOnLeft(), GetOuterScaling(strength));
795 }
796 else
797 {
798 field = new BDSFieldMagMultipoleOuterDual(2, poleTipRadius, innerField, positiveField, brho, dx,
799 info.SecondFieldOnLeft(), false, GetOuterScaling(strength));
800 }
801 delete innerField; // no longer required
802 break;
803 }
804 case BDSFieldType::multipoleoutersextupolelhc:
805 {
806 BDSFieldMag* innerField = new BDSFieldMagSextupole(strength, brho);
807 G4bool positiveField = (*strength)["k2"] > 0;
809 if (useOldMultipoleOuterFields)
810 {
811 field = new BDSFieldMagMultipoleOuterDualOld(3, poleTipRadius, innerField, positiveField, dx,
812 info.SecondFieldOnLeft(), GetOuterScaling(strength));
813 }
814 else
815 {
816 field = new BDSFieldMagMultipoleOuterDual(3, poleTipRadius, innerField, positiveField, dx, brho,
817 info.SecondFieldOnLeft(), true, GetOuterScaling(strength));
818 }
819 delete innerField; // no longer required
820 break;
821 }
822 case BDSFieldType::paralleltransporter:
823 default:
824 {// there is no need for case BDSFieldType::none as this won't be used in this function.
825 break;
826 }
827 }
828
829 // Set transform for local geometry offset
830 // Do this before wrapping in global converter BDSFieldMagGlobal so that the sub-field
831 // has it and not the global wrapper.
832 if (field)
833 {
834 field->SetTransform(info.TransformComplete());
835
836 if (info.ModulatorInfo())
837 {
838 BDSModulator* modulator = CreateModulator(info.ModulatorInfo(), info);
839 if (modulator->VariesWithTime() && field->TimeVarying())
840 {BDS::Warning(__METHOD_NAME__, "using a time varying modulation on a time varying field for field \"" + info.NameOfParserDefinition() + "\"");}
841 field->SetModulator(modulator);
843 {
844 G4cout << "maximumStepLengthOverride used for field definition \"" << info.NameOfParserDefinition()
845 << "\" so will not reduce maximum step size for modulator\n";
846 G4cout << "Would be " << modulator->RecommendedMaxStepLength() << " mm" << G4endl;
847 }
849 }
850 }
851
852 if (!info.MagneticSubFieldName().empty() && field)
853 {
854 // set the transform of the 'main' field to only the transform defined in that field definition
855 field->SetTransform(info.Transform());
856
857 auto mainField = dynamic_cast<BDSFieldMagInterpolated*>(field);
858 if (!mainField)
859 {throw BDSException(__METHOD_NAME__, "subfield specified for non-field map type field - not supported");}
860
861 BDSFieldInfo* subFieldRecipe = new BDSFieldInfo(*(GetDefinition(info.MagneticSubFieldName())));
862 BDSFieldMag* subFieldRaw = CreateFieldMagRaw(*subFieldRecipe, scalingStrength, scalingKey);
863 auto subField = dynamic_cast<BDSFieldMagInterpolated*>(subFieldRaw);
864 if (!subField)
865 {throw BDSException(__METHOD_NAME__, "subfield type is not a field map type field - not supported");}
866 field = new BDSFieldMagInterpolated2Layer(mainField, subField);
867 // the transform goes beamline transform to the 2Layer class, then inside that the individual field transforms
868 field->SetTransform(info.TransformBeamline());
869 delete subFieldRecipe;
870 }
871
872 return field;
873}
874
876{
877 G4double brho = info.BRho();
878 BDSFieldEM* field = nullptr;
879 switch (info.FieldType().underlying())
880 {
881 case BDSFieldType::rfpillbox:
882 {field = new BDSFieldEMRFCavity(info.MagnetStrength()); break;}
883 case BDSFieldType::ebmap1d:
884 case BDSFieldType::ebmap2d:
885 case BDSFieldType::ebmap3d:
886 case BDSFieldType::ebmap4d:
887 {
889 if (ff)
890 {info.UpdateUserLimitsLengthMaximumStepSize(ff->SmallestSpatialStep(), true);}
891 field = ff;
892 break;
893 }
894 case BDSFieldType::ebfieldzero:
895 {field = new BDSFieldEMZero(); break;}
896 case BDSFieldType::gaborlens:
897 {field = new BDSFieldGaborLens(info.MagnetStrength()); break;}
898 case BDSFieldType::muoncooler:
899 {field = CreateMuonCoolerField(info, brho); break;}
900 default:
901 return nullptr;
902 break;
903 }
904
905 // Set transform for local geometry offset
906 if (field)
907 {
908 field->SetTransform(info.TransformComplete());
909
910 if (info.ModulatorInfo())
911 {
912 BDSModulator* modulator = CreateModulator(info.ModulatorInfo(), info);
913 if (modulator->VariesWithTime() && field->TimeVarying())
914 {BDS::Warning(__METHOD_NAME__, "using a time varying modulation on a time varying field for field \"" + info.NameOfParserDefinition() + "\"");}
915 field->SetModulator(modulator);
917 }
918 }
919
920 if (!field)
921 {return nullptr;}
922
923 // Optionally provide local to global transform using curvilinear coordinate system.
924 BDSFieldEM* resultantField = field;
926 {resultantField = new BDSFieldEMGlobalPlacement(field);}
927 else if (info.ProvideGlobal())
928 {resultantField = new BDSFieldEMGlobal(field);}
929
930 // Equation of motion for em fields
931 G4EqMagElectricField* eqOfM = new G4EqMagElectricField(resultantField);
932
933 // Create appropriate integrator
934 G4MagIntegratorStepper* integrator = CreateIntegratorEM(info, eqOfM);
935
936 BDSFieldObjects* completeField = new BDSFieldObjects(&info, resultantField, eqOfM, integrator);
937 return completeField;
938}
939
941{
942 BDSFieldE* field = CreateFieldERaw(info);
943 if (!field)
944 {return nullptr;}
945
946 // Optionally provide local to global transform using curvilinear coordinate system.
947 BDSFieldE* resultantField = field;
949 {resultantField = new BDSFieldEGlobalPlacement(field);}
950 else if (info.ProvideGlobal())
951 {resultantField = new BDSFieldEGlobal(field);}
952
953 // Equation of motion for em fields
954 G4EqMagElectricField* eqOfM = new G4EqMagElectricField(resultantField);
955
956 // Create appropriate integrator
957 G4MagIntegratorStepper* integrator = CreateIntegratorE(info, eqOfM);
958
959 BDSFieldObjects* completeField = new BDSFieldObjects(&info, resultantField, eqOfM, integrator);
960 return completeField;
961}
962
964{
965 BDSFieldE* field = nullptr;
966 switch (info.FieldType().underlying())
967 {
968 case BDSFieldType::rfconstantinx:
969 case BDSFieldType::rfconstantiny:
970 case BDSFieldType::rfconstantinz:
971 {field = new BDSFieldESinusoid(info.MagnetStrength()); break;}
972 case BDSFieldType::emap1d:
973 case BDSFieldType::emap2d:
974 case BDSFieldType::emap3d:
975 case BDSFieldType::emap4d:
976 {
978 if (ff)
979 {info.UpdateUserLimitsLengthMaximumStepSize(ff->SmallestSpatialStep(), true);}
980 field = ff;
981 break;
982 }
983 case BDSFieldType::efieldzero:
984 {field = new BDSFieldEZero(); break;}
985 default:
986 return nullptr;
987 break;
988 }
989
990 // Set transform for local geometry offset
991 if (field)
992 {
993 field->SetTransform(info.TransformComplete());
994
995 if (info.ModulatorInfo())
996 {
997 BDSModulator* modulator = CreateModulator(info.ModulatorInfo(), info);
998 if (modulator->VariesWithTime() && field->TimeVarying())
999 {BDS::Warning(__METHOD_NAME__, "using a time varying modulation on a time varying field for field \"" + info.NameOfParserDefinition() + "\"");}
1000 field->SetModulator(modulator);
1002 }
1003 }
1004
1005 if (!info.ElectricSubFieldName().empty() && field)
1006 {
1007 // set the transform of the 'main' field to only the transform defined in that field definition
1008 field->SetTransform(info.Transform());
1009
1010 auto mainField = dynamic_cast<BDSFieldEInterpolated*>(field);
1011 if (!mainField)
1012 {throw BDSException(__METHOD_NAME__, "subfield specified for non-field map type field - not supported");}
1013
1014 BDSFieldInfo* subFieldRecipe = new BDSFieldInfo(*(GetDefinition(info.ElectricSubFieldName())));
1015 BDSFieldE* subFieldRaw = CreateFieldERaw(*subFieldRecipe);
1016 auto subField = dynamic_cast<BDSFieldEInterpolated*>(subFieldRaw);
1017 if (!subField)
1018 {throw BDSException(__METHOD_NAME__, "subfield type is not a field map type field - not supported");}
1019 field = new BDSFieldEInterpolated2Layer(mainField, subField);
1020 // the transform goes beamline transform to the 2Layer class, then inside that the individual field transforms
1021 field->SetTransform(info.TransformBeamline());
1022 delete subFieldRecipe;
1023 }
1024
1025 return field;
1026}
1027
1029{
1030 // special routine for each special / irregular field
1031 BDSFieldObjects* result = nullptr;
1032 switch (info.FieldType().underlying())
1033 {
1034 case BDSFieldType::teleporter:
1035 {result = CreateTeleporter(info); break;}
1036 case BDSFieldType::rmatrix:
1037 {result = CreateRMatrix(info); break;}
1038 case BDSFieldType::cavityfringe:
1039 {result = CreateCavityFringe(info); break;}
1040 case BDSFieldType::paralleltransporter:
1041 {result = CreateParallelTransport(info); break;}
1042 default:
1043 {break;}
1044 }
1045 return result;
1046}
1047
1048G4MagIntegratorStepper* BDSFieldFactory::CreateIntegratorMag(const BDSFieldInfo& info,
1049 G4Mag_EqRhs* eqOfM,
1050 const BDSMagnetStrength* strength)
1051{
1052 const G4double minimumRadiusOfCurvature = 10*CLHEP::cm;
1053 G4double brho = info.BRho();
1054 G4MagIntegratorStepper* integrator = nullptr;
1055 // these ones can only be used for magnetic field
1056 switch (info.IntegratorType().underlying())
1057 {
1058 case BDSIntegratorType::solenoid:
1059 integrator = new BDSIntegratorSolenoid(strength, brho, eqOfM); break;
1060 case BDSIntegratorType::dipolerodrigues:
1061 integrator = new BDSIntegratorDipoleRodrigues(strength, brho, eqOfM); break;
1062 case BDSIntegratorType::dipolerodrigues2:
1063 integrator = new BDSIntegratorDipoleRodrigues2(eqOfM, minimumRadiusOfCurvature); break;
1064 case BDSIntegratorType::dipolematrix:
1065 integrator = new BDSIntegratorDipoleQuadrupole(strength, brho, eqOfM, minimumRadiusOfCurvature, designParticle, info.Tilt()); break;
1066 case BDSIntegratorType::quadrupole:
1067 integrator = new BDSIntegratorQuadrupole(strength, brho, eqOfM, minimumRadiusOfCurvature); break;
1068 case BDSIntegratorType::sextupole:
1069 integrator = new BDSIntegratorSextupole(strength, brho, eqOfM); break;
1070 case BDSIntegratorType::octupole:
1071 integrator = new BDSIntegratorOctupole(strength, brho, eqOfM); break;
1072 case BDSIntegratorType::decapole:
1073 integrator = new BDSIntegratorDecapole(strength, brho, eqOfM); break;
1074 case BDSIntegratorType::multipolethin:
1075 integrator = new BDSIntegratorMultipoleThin(strength, brho, eqOfM); break;
1076 case BDSIntegratorType::dipolefringe:
1077 integrator = new BDSIntegratorDipoleFringe(strength, brho, eqOfM, minimumRadiusOfCurvature, info.Tilt()); break;
1078 case BDSIntegratorType::dipolefringescaling:
1079 integrator = new BDSIntegratorDipoleFringeScaling(strength, brho, eqOfM, minimumRadiusOfCurvature, info.Tilt()); break;
1080 case BDSIntegratorType::euler:
1081 integrator = new BDSIntegratorEuler(eqOfM); break;
1082 case BDSIntegratorType::kickerthin:
1083 integrator = new BDSIntegratorKickerThin(strength, brho, eqOfM, minimumRadiusOfCurvature); break;
1084 case BDSIntegratorType::g4rk4minimumstep:
1085 integrator = new BDSIntegratorG4RK4MinStep(eqOfM, BDSGlobalConstants::Instance()->ChordStepMinimumYoke()); break;
1086 case BDSIntegratorType::rmatrixthin:
1087 integrator = new BDSIntegratorRMatrixThin(strength, eqOfM, info.BeamPipeRadius()); break;
1088 case BDSIntegratorType::cavityfringe:
1089 integrator = new BDSIntegratorCavityFringe(strength, eqOfM, brho, info.BeamPipeRadius()); break;
1090 case BDSIntegratorType::g4constrk4:
1091 integrator = new G4ConstRK4(eqOfM); break;
1092 case BDSIntegratorType::g4exacthelixstepper:
1093 integrator = new G4ExactHelixStepper(eqOfM); break;
1094 case BDSIntegratorType::g4helixexpliciteuler:
1095 integrator = new G4HelixExplicitEuler(eqOfM); break;
1096 case BDSIntegratorType::g4helixheum:
1097 integrator = new G4HelixHeum(eqOfM); break;
1098 case BDSIntegratorType::g4heliximpliciteuler:
1099 integrator = new G4HelixImplicitEuler(eqOfM); break;
1100 case BDSIntegratorType::g4helixmixedstepper:
1101 integrator = new G4HelixMixedStepper(eqOfM); break;
1102 case BDSIntegratorType::g4helixsimplerunge:
1103 integrator = new G4HelixSimpleRunge(eqOfM); break;
1104 case BDSIntegratorType::g4nystromrk4:
1105 integrator = new G4NystromRK4(eqOfM); break;
1106 case BDSIntegratorType::g4rkg3stepper:
1107 integrator = new G4RKG3_Stepper(eqOfM); break;
1108 case BDSIntegratorType::g4cashkarprkf45:
1109 case BDSIntegratorType::g4classicalrk4:
1110 case BDSIntegratorType::g4expliciteuler:
1111 case BDSIntegratorType::g4impliciteuler:
1112 case BDSIntegratorType::g4simpleheum:
1113 case BDSIntegratorType::g4simplerunge:
1114#if G4VERSION_NUMBER > 1029
1115 case BDSIntegratorType::g4bogackishampine23:
1116 case BDSIntegratorType::g4bogackishampine45:
1117 case BDSIntegratorType::g4dolomcprirk34:
1118 case BDSIntegratorType::g4dormandprince745:
1119 case BDSIntegratorType::g4dormandprincerk56:
1120 case BDSIntegratorType::g4tsitourasrk45:
1121#endif
1122#if G4VERSION_NUMBER > 1039
1123 case BDSIntegratorType::g4dormandprincerk78:
1124 case BDSIntegratorType::g4rk547feq1:
1125 case BDSIntegratorType::g4rk547feq2:
1126 case BDSIntegratorType::g4rk547feq3:
1127#endif
1128 integrator = CreateIntegratorEM(info, (G4EquationOfMotion*)eqOfM); break;
1129 default:
1130 break; // returns nullptr;
1131 }
1132
1133 return integrator;
1134}
1135
1136G4MagIntegratorStepper* BDSFieldFactory::CreateIntegratorEM(const BDSFieldInfo& info,
1137 G4EquationOfMotion* eqOfM)
1138{
1139 G4MagIntegratorStepper* integrator = nullptr;
1140 switch (info.IntegratorType().underlying())
1141 {
1142 // do the EM ones first, then complain
1143 case BDSIntegratorType::g4cashkarprkf45:
1144 integrator = new G4CashKarpRKF45(eqOfM, 8); break;
1145 case BDSIntegratorType::g4classicalrk4:
1146 integrator = new G4ClassicalRK4(eqOfM, 8); break;
1147 case BDSIntegratorType::g4expliciteuler:
1148 integrator = new G4ExplicitEuler(eqOfM, 8); break;
1149 case BDSIntegratorType::g4impliciteuler:
1150 integrator = new G4ImplicitEuler(eqOfM, 8); break;
1151 case BDSIntegratorType::g4simpleheum:
1152 integrator = new G4SimpleHeum(eqOfM, 8); break;
1153 case BDSIntegratorType::g4simplerunge:
1154 integrator = new G4SimpleRunge(eqOfM, 8); break;
1155#if G4VERSION_NUMBER > 1029
1156 case BDSIntegratorType::g4bogackishampine23:
1157 {integrator = new G4BogackiShampine45(eqOfM, 8); break;}
1158 case BDSIntegratorType::g4bogackishampine45:
1159 {integrator = new G4BogackiShampine45(eqOfM, 8); break;}
1160 case BDSIntegratorType::g4dolomcprirk34:
1161 {integrator = new G4DoLoMcPriRK34(eqOfM, 8); break;}
1162 case BDSIntegratorType::g4dormandprince745:
1163 {integrator = new G4DormandPrince745(eqOfM, 8); break;}
1164 case BDSIntegratorType::g4dormandprincerk56:
1165 {integrator = new G4DormandPrinceRK56(eqOfM, 8); break;}
1166 case BDSIntegratorType::g4tsitourasrk45:
1167 {integrator = new G4TsitourasRK45(eqOfM, 8); break;}
1168#endif
1169#if G4VERSION_NUMBER > 1039
1170 case BDSIntegratorType::g4dormandprincerk78:
1171 {integrator = new G4DormandPrinceRK78(eqOfM, 8); break;}
1172 case BDSIntegratorType::g4rk547feq1:
1173 {integrator = new G4RK547FEq1(eqOfM, 8); break;}
1174 case BDSIntegratorType::g4rk547feq2:
1175 {integrator = new G4RK547FEq2(eqOfM, 8); break;}
1176 case BDSIntegratorType::g4rk547feq3:
1177 {integrator = new G4RK547FEq3(eqOfM, 8); break;}
1178#endif
1179 case BDSIntegratorType::solenoid:
1180 case BDSIntegratorType::dipolerodrigues:
1181 case BDSIntegratorType::quadrupole:
1182 case BDSIntegratorType::sextupole:
1183 case BDSIntegratorType::octupole:
1184 case BDSIntegratorType::decapole:
1185 case BDSIntegratorType::dipolefringe:
1186 case BDSIntegratorType::g4constrk4:
1187 case BDSIntegratorType::g4exacthelixstepper:
1188 case BDSIntegratorType::g4helixexpliciteuler:
1189 case BDSIntegratorType::g4helixheum:
1190 case BDSIntegratorType::g4heliximpliciteuler:
1191 case BDSIntegratorType::g4helixmixedstepper:
1192 case BDSIntegratorType::g4helixsimplerunge:
1193 case BDSIntegratorType::g4nystromrk4:
1194 case BDSIntegratorType::g4rkg3stepper:
1195 {
1196 G4cerr << "Error: integrator \"" << info.IntegratorType() << "\" is not suitable for an EM field." << G4endl;
1197 G4cout << "Suitable integrators are:" << G4endl;
1198 std::vector<BDSIntegratorType> types = {
1199 BDSIntegratorType::g4cashkarprkf45,
1200 BDSIntegratorType::g4classicalrk4,
1201 BDSIntegratorType::g4expliciteuler,
1202 BDSIntegratorType::g4impliciteuler,
1203 BDSIntegratorType::g4simpleheum,
1204 BDSIntegratorType::g4simplerunge
1205#if G4VERSION_NUMBER > 1029
1206 ,
1207 BDSIntegratorType::g4bogackishampine23,
1208 BDSIntegratorType::g4bogackishampine45,
1209 BDSIntegratorType::g4dolomcprirk34,
1210 BDSIntegratorType::g4dormandprince745,
1211 BDSIntegratorType::g4dormandprincerk56,
1212 BDSIntegratorType::g4tsitourasrk45
1213#endif
1214#if G4VERSION_NUMBER > 1039
1215 ,
1216 BDSIntegratorType::g4dormandprincerk78,
1217 BDSIntegratorType::g4rk547feq1,
1218 BDSIntegratorType::g4rk547feq2,
1219 BDSIntegratorType::g4rk547feq3
1220#endif
1221 };
1222 for (auto type : types)
1223 {G4cout << type << G4endl;}
1224 throw BDSException(__METHOD_NAME__, "invalid integrator type");
1225 }
1226 default:
1227 break; // returns nullptr;
1228 }
1229 return integrator;
1230}
1231
1232G4MagIntegratorStepper* BDSFieldFactory::CreateIntegratorE(const BDSFieldInfo& info,
1233 G4EquationOfMotion* eqOfM)
1234{
1235 return CreateIntegratorEM(info,eqOfM);
1236}
1237
1239{
1240 G4MagneticField* bGlobalField = new BDSFieldMagZero();
1241 G4Mag_EqRhs* bEqOfMotion = new G4Mag_UsualEqRhs(bGlobalField);
1242
1243 G4MagIntegratorStepper* integrator;
1244 auto mapfile = BDSGlobalConstants::Instance()->PTCOneTurnMapFileName(); // TBC - this shouldn't come from global constants
1245 BDSPTCOneTurnMap* otm = nullptr;
1246
1247 if (!mapfile.empty())
1248 {
1249 otm = new BDSPTCOneTurnMap(mapfile, designParticle);
1251 }
1252
1253 integrator = new BDSIntegratorTeleporter(bEqOfMotion, info.TransformComplete(),
1254 (*info.MagnetStrength())["length"],
1255 otm);
1256
1257 BDSFieldObjects* completeField = new BDSFieldObjects(&info, bGlobalField,
1258 bEqOfMotion, integrator);
1259 return completeField;
1260}
1261
1263{
1264 G4MagneticField* bGlobalField = new BDSFieldMagZero();
1265 G4Mag_EqRhs* bEqOfMotion = new G4Mag_UsualEqRhs(bGlobalField);
1266 G4MagIntegratorStepper* integrator = new BDSIntegratorRMatrixThin(info.MagnetStrength(),bEqOfMotion,0.95*info.BeamPipeRadius());
1267 BDSFieldObjects* completeField = new BDSFieldObjects(&info, bGlobalField,
1268 bEqOfMotion, integrator);
1269 return completeField;
1270}
1271
1273{
1274 BDSFieldMag* bGlobalField = new BDSFieldMagZero();
1275 BDSMagUsualEqRhs* bEqOfMotion = new BDSMagUsualEqRhs(bGlobalField);
1276 G4MagIntegratorStepper* integrator = new BDSIntegratorCavityFringe(info.MagnetStrength(),bEqOfMotion,info.BRho(),0.95*info.BeamPipeRadius());
1277 BDSFieldObjects* completeField = new BDSFieldObjects(&info, bGlobalField,
1278 bEqOfMotion, integrator);
1279 return completeField;
1280}
1281
1283{
1284 G4MagneticField* bGlobalField = new BDSFieldMagZero();
1285 G4Mag_EqRhs* bEqOfMotion = new G4Mag_UsualEqRhs(bGlobalField);
1286 G4MagIntegratorStepper* integrator = new BDSIntegratorParallelTransport(bEqOfMotion);
1287 BDSFieldObjects* completeField = new BDSFieldObjects(&info, bGlobalField,
1288 bEqOfMotion, integrator);
1289 return completeField;
1290}
1291
1293{
1294 G4double result = 1.0;
1295 if (st->KeyHasBeenSet("scalingOuter"))
1296 {result = (*st)["scalingOuter"];}
1297 return result;
1298}
1299
1301 G4double brho) const
1302{
1303 BDSFieldInfoExtra* extraInfo = info.ExtraInfo();
1304 BDSFieldInfoExtraMuonCooler* mcExtraInfo = dynamic_cast<BDSFieldInfoExtraMuonCooler*>(extraInfo);
1305 if (!mcExtraInfo) // shouldn't happen, but just for safety
1306 {throw BDSException(__METHOD_NAME__, "no muon cooler extra definitions for field definition: " + info.NameOfParserDefinition());}
1307
1308 BDSFieldEM* result = new BDSFieldEMMuonCooler(mcExtraInfo, brho);
1309 return result;
1310}
1312 const BDSFieldInfo& info) const
1313{
1314 if (!modulatorRecipe)
1315 {return nullptr;}
1316 BDSModulator* result = nullptr;
1317 try
1318 {
1319 switch (modulatorRecipe->modulatorType.underlying())
1320 {
1321 case BDSModulatorType::lineart:
1322 {
1323 result = new BDSModulatorLinearT(modulatorRecipe->T0,
1324 modulatorRecipe->T1,
1325 modulatorRecipe->scale,
1326 modulatorRecipe->amplitudeOffset);
1327 break;
1328 }
1329 case BDSModulatorType::sint:
1330 {
1331 result = new BDSModulatorSinT(modulatorRecipe->frequency,
1332 modulatorRecipe->phase,
1333 info.SynchronousT(),
1334 modulatorRecipe->amplitudeOffset,
1335 modulatorRecipe->scale);
1336 break;
1337 }
1338 case BDSModulatorType::singlobalt:
1339 {
1340 result = new BDSModulatorSinT(modulatorRecipe->frequency,
1341 modulatorRecipe->phase,
1342 0.0,
1343 modulatorRecipe->amplitudeOffset,
1344 modulatorRecipe->scale);
1345 break;
1346 }
1347 case BDSModulatorType::tophatt:
1348 {
1349 result = new BDSModulatorTopHatT(modulatorRecipe->T0,
1350 modulatorRecipe->T1,
1351 modulatorRecipe->scale);
1352 break;
1353 }
1354 case BDSModulatorType::none:
1355 default:
1356 {break;}
1357 }
1358 }
1359 catch (BDSException& e)
1360 {
1361 G4String extraMsg = "\nProblem in field definition for component \"" + info.NameOfParserDefinition() + "\"";
1362 e.AppendToMessage(extraMsg);
1363 throw e;
1364 }
1365 return result;
1366}
G4double aper1
Public member for direct access.
General exception with possible name of object and message.
Wrapper class to convert to global coordinates using a navigator for placements.
A base class for electric fields in local to be used in global coordinates.
Two interpolated fields in one. One takes precedence in a subregion.
Class to provide scaling and a base class pointer for interpolator fields.
A base class for electro-magnetic fields in local to be used in global coordinates.
A base class for electro-magnetic fields in local to be used in global coordinates.
Class to provide scaling and a base class pointer for interpolator fields.
A composite RF and B field for a muon cooler.
Pill box cavity electromagnetic field.
Null EM field - for special cases where we need a valid object.
Interface for BDSIM electro-magnetic fields that may or may not be local.
Definition BDSFieldEM.hh:41
void SetModulator(BDSModulator *modulatorIn)
Set the optional modulator.
Definition BDSFieldEM.hh:82
virtual G4bool TimeVarying() const
Definition BDSFieldEM.hh:58
virtual void SetTransform(const G4Transform3D &transformIn)
Definition BDSFieldEM.cc:88
A sinusoidal electric (only) field that doesn't vary with position. Uses cosine.
Null E field - for special cases where we need a valid object.
Interface for BDSIM electric fields that may or may not be local.
Definition BDSFieldE.hh:39
virtual G4bool TimeVarying() const
Definition BDSFieldE.hh:56
void SetModulator(BDSModulator *modulatorIn)
Set the optional modulator.
Definition BDSFieldE.hh:77
virtual void SetTransform(const G4Transform3D &transformIn)
Definition BDSFieldE.cc:84
Factory that produces fields and their associated objects.
G4double GetOuterScaling(const BDSMagnetStrength *st) const
Return the parameter "outerScaling" from strength st, but default to 1.
void PrepareFieldDefinitions(const std::vector< GMAD::Field > &definitions, G4double defaultBRho)
Prepare all required definitions that can be used dynamically.
void PrepareModulatorDefinitions(const std::vector< GMAD::Modulator > &definitions)
Prepare all required modulator definitions that can be used dynamically.
BDSFieldObjects * CreateFieldE(const BDSFieldInfo &info)
Create an electric field.
BDSFieldObjects * CreateFieldIrregular(const BDSFieldInfo &info)
Create an irregular (special) field.
BDSFieldObjects * CreateRMatrix(const BDSFieldInfo &info)
Create special rmatrix 'field' that applies an rmatrix.
void PrepareFieldStrengthFromParameters(BDSMagnetStrength *st, const G4String &fieldParameters, G4double &poleTipRadius) const
static BDSPrimaryGeneratorAction * primaryGeneratorAction
Cache of primary generator action.
BDSModulator * CreateModulator(const BDSModulatorInfo *modulatorRecipe, const BDSFieldInfo &info) const
Create the necessary modulator.
BDSFieldE * CreateFieldERaw(const BDSFieldInfo &info)
Creat just the electric field object.
G4double ConvertToDoubleWithException(const G4String &value, const G4String &parameterNameForError) const
Convert the string 'value' to a double. Throw an exception including the parameterNameForError if it ...
static BDSFieldFactory * Instance()
Public accessor method for singleton pattern.
BDSFieldObjects * CreateField(const BDSFieldInfo &info, const BDSMagnetStrength *scalingStrength=nullptr, const G4String &scalingKey="none")
Main interface to field factory.
BDSFieldObjects * CreateParallelTransport(const BDSFieldInfo &info)
static BDSFieldFactory * instance
Instance - singleton pattern.
BDSFieldInfo * GetDefinition(const G4String &name) const
BDSFieldObjects * CreateCavityFringe(const BDSFieldInfo &info)
Create special rf cavity fringe 'field' that applies an rmatrix.
std::map< G4String, BDSFieldInfo * > parserDefinitions
BDSFieldInfo definitions prepare from parser vector of definitions.
G4MagIntegratorStepper * CreateIntegratorMag(const BDSFieldInfo &info, G4Mag_EqRhs *eqOfM, const BDSMagnetStrength *strength)
BDSFieldEM * CreateMuonCoolerField(const BDSFieldInfo &info, G4double brho) const
Create a composite muon cooler field EM field.
G4MagIntegratorStepper * CreateIntegratorE(const BDSFieldInfo &info, G4EquationOfMotion *eqOfM)
BDSFieldObjects * CreateFieldMag(const BDSFieldInfo &info, const BDSMagnetStrength *scalingStrength=nullptr, const G4String &scalingKey="none")
Create a purely magnetic field.
static BDSInterpolatorType DefaultInterpolatorType(G4int numberOfDimensions)
Suggest a default interpolator.
BDSFieldObjects * CreateFieldEM(const BDSFieldInfo &info)
Create a general EM field.
BDSFieldMag * CreateFieldMagRaw(const BDSFieldInfo &info, const BDSMagnetStrength *scalingStrength=nullptr, const G4String &scalingKey="none")
Creat just the magnetic field object.
BDSFieldFactory()
Private default constructor as singleton class.
BDSModulatorInfo * GetModulatorDefinition(const G4String &modulatorName) const
BDSFieldObjects * CreateTeleporter(const BDSFieldInfo &info)
G4MagIntegratorStepper * CreateIntegratorEM(const BDSFieldInfo &info, G4EquationOfMotion *eqOfM)
static const BDSParticleDefinition * designParticle
Cache of design particle for fields.
Gabor lens electromagnetic field.
necessary extra information for a muon cooler field.
Base class for any extra (complex) information for a particular field.
All info required to build complete field of any type.
G4String MagneticSubFieldName() const
Accessor.
void UpdateUserLimitsLengthMaximumStepSize(G4double maximumStepSize, G4bool warn=false) const
G4Transform3D TransformComplete() const
Compound transform of field + beam line transform.
G4String NameOfParserDefinition() const
Accessor.
G4bool SecondFieldOnLeft() const
Accessor.
G4double PoleTipRadius() const
Accessor.
G4bool ProvideGlobal() const
Accessor.
BDSIntegratorType IntegratorType() const
Accessor.
G4double BeamPipeRadius() const
Accessor.
G4String ElectricSubFieldName() const
Accessor.
G4Transform3D Transform() const
Transform for the field definition only.
G4bool UsePlacementWorldTransform() const
Accessor.
G4bool IgnoreUpdateOfMaximumStepSize() const
Accessor.
BDSModulatorInfo * ModulatorInfo() const
Accessor.
G4double Tilt() const
Accessor.
BDSFieldType FieldType() const
Accessor.
BDSMagnetStrength * MagnetStrength() const
Accessor.
G4double BRho() const
Accessor.
G4Transform3D TransformBeamline() const
Transform from the curvilinear coordinates to the beam line component.
BDSFieldInfoExtra * ExtraInfo() const
Accessor.
BDSFieldEMInterpolated * LoadEMField(const BDSFieldInfo &info)
Main interface to load an electro-magnetic field.
BDSFieldMagInterpolated * LoadMagField(const BDSFieldInfo &info, const BDSMagnetStrength *scalingStrength=nullptr, const G4String &scalingKey="none")
Main interface to load a magnetic field.
static BDSFieldLoader * Instance()
Singleton accessor.
BDSFieldEInterpolated * LoadEField(const BDSFieldInfo &info)
Main interface to load an electric field.
Class that provides the magnetic strength in a decapole.
A perfect magetic dipole in 3D, normal field inside 1/2 poleTipRadius.
A perfect magetic dipole in 3D, normal field inside 1/2 poleTipRadius.
Class that provides the magnetic strength in a mixed dipole / quadrupole.
A uniform dipole field.
A base class for magnetic fields in local to be used in global coordinates.
A base class for magnetic fields in local to be used in global coordinates.
Two interpolated fields in one. One takes precedence in a subregion.
Class to provide scaling and a base class pointer for interpolator fields.
Sum of two multipole fields spaced by a distance in x.
Sum of two multipole fields spaced by a distance in x.
A simple paramaterisation of N-Pole outer yoke magnetic field.
A simple parameterisation of N-Pole outer yoke magnetic field.
Class that provides the magnetic strength in a quadrupole.
Field of a Muon Spoiler.
Class that provides the magnetic strength in an octupole.
Class that provides the magnetic strength in a quadrupole.
Class that provides the magnetic strength in a sextupole.
A wrapper class for BDSFieldMagSkew where it owns the field.
Class that provides the magnetic field due to a square annulus of current.
Class that provides the magnetic field due to a cylinder of current.
Class that provides the magnetic field due to a cylinder of current.
Class that provides the magnetic strength in a quadrupole.
Null B field - for special cases where we need a valid object.
Interface for static magnetic fields that may or may not be local.
virtual void SetTransform(const G4Transform3D &transformIn)
virtual G4bool TimeVarying() const
void SetModulator(BDSModulator *modulatorIn)
Set the optional modulator.
A holder for all the Geant4 field related objects.
static BDSGlobalConstants * Instance()
Access method.
Integrator for RF cavity fringes. Only the transverse momentum kicks are applied, this integrator wil...
Integrator for Decapolar field.
Derived fringe field integrator that does normalise to momentum.
Integrator that ignores the field and uses the analytical solution for a dipole kick.
Integrator for combined dipole and quadrupolar field.
Exact helix through pure dipole field.
Stepper that calculates trajectory through uniform magnetic field.
BDSIM 2nd order Euler integration.
Integrator that wraps a G4ClassicalRK4 and below a minimum step size uses a drift.
Integrator for thin h or v kick.
Integrator that ignores the field and uses the analytical solution to a multipole.
Integrator for octupole field.
Integrator that just moves the particle parallel to the s axis.
Integrator that ignores the field and uses the analytical solution to a quadrupole.
Integrator that just moves the particle parallel to the s axis.
Integrator for sextupole field.
Integrator that ignores the field and uses the analytical solution to a solenoid.
Custom unphysical integrator to advance particle in teleporter.
Override G4Mag_UsualEqRhs, provides BDSIM integrators access to particle attributes.
static const G4double beamSeparation
Used in many places - make it a constant in the code and put here as most relevant.
Efficient storage of magnet strengths.
G4bool KeyHasBeenSet(const G4String &key) const
Whether a key has been set.
static G4bool ValidKey(const G4String &key)
Whether or not the supplied key is a valid magnet strength parameter.
static const std::vector< G4String > & AllKeys()
Accessor to all keys.
static G4double Unit(const G4String &key)
Access a unit factor for a given key.
Holder class for all information required to describe a modulator.
G4double T1
Public member for direct access.
G4double frequency
Public member for direct access.
BDSModulatorType modulatorType
Public member for direct access.
G4double T0
Public member for direct access.
G4double phase
Public member for direct access.
G4double scale
Public member for direct access.
G4double amplitudeOffset
Public member for direct access.
Top-hat modulator as a function of T.
Sinusoidal modulator as a function of global time.
Top-hat modulator as a function of T.
Base class for a modulator.
virtual G4double RecommendedMaxStepLength() const =0
Must return the smallest spatial.
virtual G4bool VariesWithTime() const =0
Each derived class should override this.
Class to load and use PTC 1 turn map.
static BDSParser * Instance()
Access method.
Definition BDSParser.cc:30
static bool IsInitialised()
Returns if parser is initialised.
Definition BDSParser.cc:51
Wrapper for particle definition.
G4double BRho() const
Accessor.
Generates primary particle vertices using BDSBunch.
void RegisterPTCOneTurnMap(BDSPTCOneTurnMap *otmIn)
type underlying() const
return underlying value (can be used in switch statement)
BDSArrayReflectionTypeSet DetermineArrayReflectionTypeSet(const G4String &arrayReflectionType)
Return a std::set of reflection types. Split string on white space.
BDSInterpolatorType InterpolatorTypeSpecificFromAuto(G4int nDimension, BDSInterpolatorType autoType)
BDSModulatorType DetermineModulatorType(G4String mType)
Function that gives corresponding enum value for string (case-insensitive)
G4bool InterpolatorTypeIsAuto(BDSInterpolatorType typeIn)
Return true if the type is one containing 'auto'.
BDSFieldFormat DetermineFieldFormat(G4String fieldformat)
Function that gives corresponding enum value for string (case-insensitive)
std::pair< G4String, G4String > SplitOnColon(const G4String &formatAndPath)
BDSFieldType DetermineFieldType(G4String fieldType)
Function that gives corresponding enum value for string (case-insensitive)
BDSInterpolatorType DetermineInterpolatorType(G4String interpolatorType)
Function that determines enum from string (case-insensitive).
BDSFieldClassType DetermineFieldClassType(BDSFieldType fieldType)
Function that gives the corresponding enum value for a field type enum.
G4String GetFullPath(G4String filename, bool excludeNameFromPath=false, bool useCWDForPrefix=false)
G4UserLimits * CreateUserLimits(G4UserLimits *defaultUL, G4double length, G4double fraction=1.6)
BDSIntegratorType DetermineIntegratorType(G4String integratorType)
Function that determines enum from string (case-insensitive).
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
G4int NDimensionsOfInterpolatorType(const BDSInterpolatorType &it)
Report the number of dimensions for that interpolator type.
G4int NDimensionsOfFieldFormat(const BDSFieldFormat &ff)
Report the number of dimensions for that format.
std::map< G4String, G4String > GetUserParametersMap(const G4String &userParameters, char delimiter=':')
Take one long string and split on space and then on colon. "key1:value1 key2:value2" etc.