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