BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSDetectorConstruction.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 "BDSAcceleratorComponent.hh"
20#include "BDSAcceleratorComponentRegistry.hh"
21#include "BDSAcceleratorModel.hh"
22#include "BDSApertureInfo.hh"
23#include "BDSAuxiliaryNavigator.hh"
24#include "BDSBeamline.hh"
25#include "BDSBeamlineBLMBuilder.hh"
26#include "BDSBeamlineEndPieceBuilder.hh"
27#include "BDSBeamlineElement.hh"
28#include "BDSBeamlineIntegral.hh"
29#include "BDSBeamlinePlacementBuilder.hh"
30#include "BDSBeamlineSet.hh"
31#include "BDSBeamPipeInfo.hh"
32#include "BDSBLM.hh"
33#include "BDSBLMRegistry.hh"
34#include "BDSBOptrMultiParticleChangeCrossSection.hh"
35#include "BDSComponentFactory.hh"
36#include "BDSComponentFactoryUser.hh"
37#include "BDSCurvilinearBuilder.hh"
38#include "BDSDebug.hh"
39#include "BDSDetectorConstruction.hh"
40#include "BDSException.hh"
41#include "BDSExtent.hh"
42#include "BDSFieldBuilder.hh"
43#include "BDSFieldObjects.hh"
44#include "BDSFieldQuery.hh"
45#include "BDSFieldQueryInfo.hh"
46#include "BDSFieldLoaderQueryPoints.hh"
47#include "BDSGap.hh"
48#include "BDSGeometryComponent.hh"
49#include "BDSGeometryExternal.hh"
50#include "BDSGeometryFactory.hh"
51#include "BDSGlobalConstants.hh"
52#include "BDSHistBinMapper.hh"
53#include "BDSIntegratorSet.hh"
54#include "BDSLine.hh"
55#include "BDSMaterials.hh"
56#include "BDSParser.hh"
57#include "BDSPhysicalVolumeInfo.hh"
58#include "BDSPhysicalVolumeInfoRegistry.hh"
59#include "BDSRegion.hh"
60#include "BDSSamplerInfo.hh"
61#include "BDSSamplerType.hh"
62#include "BDSScorerFactory.hh"
63#include "BDSScorerInfo.hh"
64#include "BDSScorerMeshInfo.hh"
65#include "BDSScoringMeshBox.hh"
66#include "BDSScoringMeshCylinder.hh"
67#include "BDSSDEnergyDeposition.hh"
68#include "BDSSDManager.hh"
69#include "BDSSDType.hh"
70#include "BDSSurvey.hh"
71#include "BDSTeleporter.hh"
72#include "BDSTrajectoryPoint.hh"
73#include "BDSTunnelBuilder.hh"
74#include "BDSUtilities.hh"
75#include "BDSWarning.hh"
76
77#include "parser/blmplacement.h"
78#include "parser/element.h"
79#include "parser/fastlist.h"
80#include "parser/options.h"
81#include "parser/physicsbiasing.h"
82#include "parser/placement.h"
83#include "parser/samplerplacement.h"
84#include "parser/scorermesh.h"
85
86#include "globals.hh"
87#include "G4AffineTransform.hh"
88#include "G4Box.hh"
89#include "G4LogicalVolume.hh"
90#include "G4Material.hh"
91#include "G4ProductionCuts.hh"
92#include "G4PVPlacement.hh"
93#include "G4VPrimitiveScorer.hh"
94#include "G4Region.hh"
95#include "G4ScoringManager.hh"
96#include "G4String.hh"
97#include "G4Transform3D.hh"
98#include "G4Version.hh"
99#include "G4VisAttributes.hh"
100#include "G4VPhysicalVolume.hh"
101#if G4VERSION_NUMBER > 1039
102#include "G4ChannelingOptrMultiParticleChangeCrossSection.hh"
103#endif
104#if G4VERSION_NUMBER > 1109
105#include "G4HadronicParameters.hh"
106#endif
107
108#ifdef BDSCHECKUSERLIMITS
109#include "G4UserLimits.hh"
110#endif
111
112#include "CLHEP/Units/SystemOfUnits.h"
113#include "CLHEP/Vector/EulerAngles.h"
114
115#include <iterator>
116#include <limits>
117#include <list>
118#include <map>
119#include <set>
120#include <sstream>
121#include <string>
122#include <utility>
123#include <vector>
124
125
126BDSDetectorConstruction::BDSDetectorConstruction(BDSComponentFactoryUser* userComponentFactoryIn):
127 placementBL(nullptr),
128 designParticle(nullptr),
129 userComponentFactory(userComponentFactoryIn),
130 nSamplers(0),
131 buildPlacementFieldsWorld(false),
132 worldLogicalVolume(nullptr)
133{
135 verbose = globals->Verbose();
136 checkOverlaps = globals->CheckOverlaps();
137 circular = globals->Circular();
138
139 if (globals->RestoreFTPFDiffractionForAGreater10())
140#if G4VERSION_NUMBER > 1109
141 {
142 G4cout << __METHOD_NAME__ << "restoring diffraction for target / projectiles with A > 10 in the FTFP hadronic model (even if not used)" << G4endl;
143 G4HadronicParameters::Instance()->SetEnableDiffDissociationForBGreater10(true);
144 }
145#else
146 {
147 if (globals->RestoreFTPFDiffractionForAGreater10Set()) // shouldn't warn about default being on
148 {BDS::Warning(__METHOD_NAME__, "\"restoreFTPFDiffractionForAGreater10\" is only available for Geant4 v11.1 and later");}
149 }
150#endif
151
152 BDSTrajectoryPoint::dEThresholdForScattering = globals->DEThresholdForScattering();
153
154 // instantiate the accelerator model holding class
155 acceleratorModel = BDSAcceleratorModel::Instance();
156 canSampleAngledFaces = true;
157 BDSIntegratorSetType integratorSetType = globals->IntegratorSet();
158 if ( (integratorSetType == BDSIntegratorSetType::bdsimtwo)
159 || (integratorSetType == BDSIntegratorSetType::geant4)
160#if G4VERSION_NUMBER > 1039
161 || (integratorSetType == BDSIntegratorSetType::geant4dp)
162#endif
163 )
164 { // set to be value of option, default is false.
165 canSampleAngledFaces = globals->SampleElementsWithPoleface();
166 }
167
168 UpdateSamplerDiameterAndCountSamplers();
169 PrepareExtraSamplerSDs();
170 CountPlacementFields();
171}
172
174{
175 nSamplers = 0;
176 auto beamline = BDSParser::Instance()->GetBeamline(); // main beam line
177 G4double maxBendingRatio = 1e-9;
178 for (const auto& blElement : beamline)
179 {
180 // count number of samplers
181 auto st = BDS::DetermineSamplerType(blElement.samplerType);
182 if (st != BDSSamplerType::none)
183 {nSamplers++;}
184
185 G4double length = blElement.l;
186 G4double angle = blElement.angle;
187 if (!BDS::IsFinite(length))
188 {continue;} // avoid divide by 0
189 G4double ratio = angle / length;
190 maxBendingRatio = std::max(maxBendingRatio, ratio);
191 }
192
194 G4double curvilinearRadius = 0.5*globals->CurvilinearDiameter();
195 G4double tolerance = 0.9; // 10% tolerance -> factor of 0.9
196 if (maxBendingRatio > 0.4*tolerance) // max ratio for a 2.5m sampler diameter
197 {
198 G4double curvilinearRadiusBends = (tolerance / maxBendingRatio)*CLHEP::m;
199 if (curvilinearRadiusBends < curvilinearRadius)
200 {
201 G4cout << __METHOD_NAME__ << "Reducing curvilinear diameter from " << 2*curvilinearRadius / CLHEP::m
202 << "m to " << 2*curvilinearRadiusBends / CLHEP::m << "m" << G4endl;
203 globals->SetCurvilinearDiameter(2*curvilinearRadiusBends);
205 }
206 G4double sd = globals->SamplerDiameter();
207 if (curvilinearRadius*2 < sd)
208 {
209 G4cout << __METHOD_NAME__ << "Reducing sampler diameter from " << sd / CLHEP::m << "m to the same" << G4endl;
210 globals->SetSamplerDiameter(2*curvilinearRadius);
211 }
212 }
213
214 // add number of sampler placements to count of samplers
216}
217
219{
220 const auto& samplerFilterIDtoPDGSet = BDSParser::Instance()->GetSamplerFilterIDToSet();
221 BDSSDManager::Instance()->ConstructSamplerSDsForParticleSets(samplerFilterIDtoPDGSet);
222}
223
225{
226 G4int nFields = 0;
227 const auto& placements = BDSParser::Instance()->GetPlacements();
228 for (const auto& placement : placements)
229 {// here we assume if a bdsim element is used at all that it's active even though it may not be
230 if (!placement.fieldAll.empty() || !placement.bdsimElement.empty())
231 {nFields++;}
232 }
233 buildPlacementFieldsWorld = nFields > 0;
234}
235
237{
238 if (verbose || debug)
239 {G4cout << __METHOD_NAME__ << "starting accelerator geometry construction\n" << G4endl;}
240
241 // construct all parser defined regions
243
244 // construct all parser defined aperture objects
246
247 // construct the main beam line and any other secondary beam lines
249
250 // construct placement geometry from parser
251 BDSBeamline* mainBeamLine = BDSAcceleratorModel::Instance()->BeamlineSetMain().massWorld;
252 auto componentFactory = new BDSComponentFactory(userComponentFactory, false); // false for printing out integrator set again
254 mainBeamLine,
255 componentFactory,
257 BDSAcceleratorModel::Instance()->RegisterPlacementBeamline(placementBL); // Acc model owns it
258 delete componentFactory;
259
260 BDSBeamline* blms = BDS::BuildBLMs(BDSParser::Instance()->GetBLMs(), mainBeamLine);
261 if (blms)
262 {BDSAcceleratorModel::Instance()->RegisterBLMs(blms);} // Acc model owns it
263
264 // build the tunnel and supports
266 {BuildTunnel();}
267
268 // build world and calculate coordinates
269 auto worldPV = BuildWorld();
270
271 // placement procedure - put everything in the world
272 ComponentPlacement(worldPV);
273
274 if (verbose || debug)
275 {G4cout << __METHOD_NAME__ << "detector Construction done" << G4endl;}
276
277 fieldQueries = BDSDetectorConstruction::PrepareFieldQueries(mainBeamLine);
280
281#ifdef BDSDEBUG
282 G4cout << G4endl << __METHOD_NAME__ << "printing material table" << G4endl;
283 G4cout << *(G4Material::GetMaterialTable()) << G4endl << G4endl;
284 if (verbose || debug) {G4cout << "Finished listing materials, returning physiWorld" << G4endl;}
285#endif
286#ifdef BDSCHECKUSERLIMITS
287 PrintUserLimitsSummary(worldPV);
288#endif
289 return worldPV;
290}
291
292BDSDetectorConstruction::~BDSDetectorConstruction()
293{
294#if G4VERSION_NUMBER > 1009
295 // delete bias objects
296 for (auto i : biasObjects)
297 {delete i;}
298#endif
299 for (auto q : fieldQueries)
300 {delete q;}
301}
302
304{
305 BDSRegion* defaultRegion = new BDSRegion("default");
306 for (const GMAD::Region& r : BDSParser::Instance()->GetRegions())
307 {
308 BDSRegion* reg = new BDSRegion(r, defaultRegion);
309 G4cout << "New region defined: " << G4endl << *reg << G4endl;
311 }
312 delete defaultRegion;
313}
314
316{
317 std::map<G4String, BDSApertureInfo*> apertures;
319 {
320 BDSApertureInfo* ap = new BDSApertureInfo(a.apertureType,
321 a.aper1 * CLHEP::m,
322 a.aper2 * CLHEP::m,
323 a.aper3 * CLHEP::m,
324 a.aper4 * CLHEP::m,
325 a.name);
326 apertures[a.name] = ap;
327 }
329}
330
332{
333 // build main beam line
334 if (verbose || debug)
335 {G4cout << "parsing the beamline element list..."<< G4endl;}
337 G4Transform3D initialTransform = g->BeamlineTransform();
338
339 BDSBeamlineIntegral startingPoint(*designParticle, 0, g->BeamlineS(), g->IntegrateKineticEnergyAlongBeamline());
340 BDSBeamlineIntegral* finishingPoint = new BDSBeamlineIntegral(startingPoint);
341
342 BDSBeamlineSet mainBeamline = BuildBeamline(BDSParser::Instance()->GetBeamline(),
343 "main beam line",
344 startingPoint,
345 finishingPoint,
346 initialTransform,
347 circular);
348
349 if (finishingPoint->changeOfEnergyEncountered)
350 {G4cout << "Design particle properties at end of beam line: " << G4endl << finishingPoint->designParticle;}
351
352 // TODO - don't need this finish integral for now - for multiple beamlines may need ot pass it off
353 delete finishingPoint;
354#ifdef BDSDEBUG
355 G4cout << "Registry size "
357 G4cout << "Parser beam line size "
358 << BDSParser::Instance()->GetBeamline().size() << G4endl;
360#endif
361
362 // print warning if beam line is approximately circular but flag isn't specified
363 if (!circular && mainBeamline.massWorld)
364 {
365 if (mainBeamline.massWorld->ElementAnglesSumToCircle())
366 {BDS::Warning("Total sum of all element angles is approximately 2*pi but the circular option was not specified, this simulation may run indefinitely");}
367 }
368
369 // register the beam line in the holder class for the full model
371
372 // build secondary beam lines
373 // loop over placements and check if any are beam lines (have sequences specified)
374 auto placements = BDSParser::Instance()->GetPlacements();
375 for (const auto& placement : placements)
376 {
377 if (placement.sequence.empty())
378 {continue;} // no sequence specified -> just a placement
379 auto parserLine = BDSParser::Instance()->GetSequence(placement.sequence);
380
381 // determine offset in world for extra beam line
382 const BDSBeamline* mbl = mainBeamline.massWorld;
383 // TODO - so by default if placement.s is finite, it'll be made w.r.t. the main beam line
384 // but this could be any beam line in future if we find the right beam line to pass in.
385 G4Transform3D startTransform = CreatePlacementTransform(placement, mbl);
386 G4double startS = mbl ? mbl->back()->GetSPositionEnd() : 0;
387
390 BDSBeamlineIntegral thisBeamlineStartingPoint(startingPoint.designParticle, 0, startS, g->IntegrateKineticEnergyAlongBeamline());
391 BDSBeamlineIntegral* thisBeamlineFinishingPoint = new BDSBeamlineIntegral(thisBeamlineStartingPoint);
392
393 // aux beam line must be non-circular by definition to branch off of beam line (for now)
394 // TODO - the naming convention here is repeated in BDSParallelWorldInfo which is registered
395 // beforehand separately - fix by making the information originate in one place despite
396 // the parallel world instantiated first before Construct() in this class is called.
397 G4String beamlineName = placement.name + "_" + placement.sequence;
398 BDSBeamlineSet extraBeamline = BuildBeamline(parserLine,
399 beamlineName,
400 thisBeamlineStartingPoint,
401 thisBeamlineFinishingPoint,
402 startTransform,
403 false, // circular
404 true); // is placement
405 // TODO - make use of this finishing transform
406 delete thisBeamlineFinishingPoint;
407 acceleratorModel->RegisterBeamlineSetExtra(beamlineName, extraBeamline);
408 }
409}
410
412{
413 BDSSamplerType sType = BDS::DetermineSamplerType(element->samplerType);
414 if (sType == BDSSamplerType::none)
415 {return nullptr;}
416 BDSSamplerInfo* result = new BDSSamplerInfo(element->samplerName,
417 sType,
418 (G4int)element->samplerParticleSetID);
419 return result;
420}
421
423 const G4String& name,
424 const BDSBeamlineIntegral& startingIntegral,
425 BDSBeamlineIntegral*& integral,
426 const G4Transform3D& initialTransform,
427 G4bool beamlineIsCircular,
428 G4bool isPlacementBeamline)
429{
430 if (beamLine.empty()) // note a line always has a 'line' element first so an empty line will not be 'empty'
431 {return BDSBeamlineSet();}
432
433 BDSComponentFactory* theComponentFactory = new BDSComponentFactory(userComponentFactory);
434 BDSBeamline* massWorld = new BDSBeamline(initialTransform, startingIntegral.arcLength);
435
436 if (beamlineIsCircular)
437 {
438 G4bool unsuitable = UnsuitableFirstElement(beamLine.begin());
439 if (unsuitable)
440 {
441 G4cerr << "The first element in the beam line is unsuitable for a circular "
442 << "model as the first element will " << G4endl << "overlap with the "
443 << "teleporter and terminator - the necessary mechanics for a circular "
444 << "model in Geant4" << G4endl;
445 throw BDSException(__METHOD_NAME__, "check construction for circular machine");
446 }
447 }
448
449 if (beamLine.size() <= 1) // if an empty LINE it still has 1 item in it
450 {throw BDSException(__METHOD_NAME__, "BDSIM requires the sequence defined with the use command to have at least one element.");}
451
452 for (auto elementIt = beamLine.begin(); elementIt != beamLine.end(); ++elementIt)
453 {
454 // find next and previous element, but ignore special elements or thin elements.
455 const GMAD::Element* prevElement = nullptr;
456 auto prevIt = elementIt;
457 while (prevIt != beamLine.begin())
458 {
459 --prevIt;
460 if (prevIt->isSpecial() == false && prevIt->l > BDSGlobalConstants::Instance()->ThinElementLength())
461 {
462 prevElement = &(*prevIt);
463 break;
464 }
465 }
466
467 const GMAD::Element* nextElement = nullptr;
468 auto nextIt = elementIt;
469 ++nextIt;
470 G4double nextElementInputFace = 0; // get poleface angle for next element whilst testing if next element exists
471 while (nextIt != beamLine.end())
472 {
473 if (nextIt->isSpecial() == false && nextIt->l > BDSGlobalConstants::Instance()->ThinElementLength())
474 {
475 nextElement = &(*nextIt);
476 //rotated entrance face of the next element may modify the exit face of the current element.
477 nextElementInputFace = nextElement->e1;
478 break;
479 }
480 ++nextIt;
481 }
482 BDSAcceleratorComponent* temp = theComponentFactory->CreateComponent(&(*elementIt),
483 prevElement,
484 nextElement,
485 *integral);
486 if (temp)
487 {
488 G4bool forceNoSamplerOnThisElement = false;
489 if ((!canSampleAngledFaces) && (BDS::IsFinite((*elementIt).e2)))
490 {forceNoSamplerOnThisElement = true;}
491 if ((!canSampleAngledFaces) && (BDS::IsFinite(nextElementInputFace)))
492 {forceNoSamplerOnThisElement = true;}
493 if (temp->GetType() == "dump") // don't sample after a dump as there'll be nothing
494 {forceNoSamplerOnThisElement = true;}
495 BDSSamplerInfo* samplerInfo = forceNoSamplerOnThisElement ? nullptr : BuildSamplerInfo(&(*elementIt));
496 BDSTiltOffset* tiltOffset = BDSComponentFactory::CreateTiltOffset(&(*elementIt));
497 massWorld->AddComponent(temp, tiltOffset, samplerInfo, integral);
498 }
499 }
500
501 // Special circular machine bits
502 // Add terminator to do ring turn counting logic and kill particles
503 // Add teleporter to account for slight ring offset
504 if (beamlineIsCircular && !massWorld->empty())
505 {
506 if (integral->changeOfEnergyEncountered && integral->integrateKineticEnergy)
507 {
508 G4String msg = "a change in energy was encountered in a circular machine and both\n";
509 msg += "integrateKineticEnergyAlongBeamline=1 (default is 1) and circular options were used.\n";
510 msg += "This will be wrong for more than one turn...";
511 BDS::Warning(__METHOD_NAME__, msg);
512 }
513#ifdef BDSDEBUG
514 G4cout << __METHOD_NAME__ << "Circular machine - creating terminator & teleporter" << G4endl;
515#endif
516 G4double teleporterLength = 0;
517 G4Transform3D teleporterTransform = BDS::CalculateTeleporterDelta(massWorld, teleporterLength);
518
519 auto hasBeamPipeInfo = [](BDSBeamlineElement* ble) {return ble->GetBeamPipeInfo() != nullptr;};
520 auto firstElementWithBPInfo = std::find_if(massWorld->begin(), massWorld->end(), hasBeamPipeInfo);
521 auto lastElementWithBPInfo = std::find_if(massWorld->rbegin(), massWorld->rend(), hasBeamPipeInfo);
522
523 G4double firstbeamPipeMaxExtent = (*firstElementWithBPInfo)->GetBeamPipeInfo()->Extent().MaximumAbsTransverse();
524 G4double lastbeamPipeMaxExtent = (*lastElementWithBPInfo)->GetBeamPipeInfo()->Extent().MaximumAbsTransverse();
525
526 // the extent is a half width, so we double it - also the terminator width.
527 G4double teleporterHorizontalWidth = 2 * std::max(firstbeamPipeMaxExtent, lastbeamPipeMaxExtent);
528
529 BDSAcceleratorComponent* terminator = theComponentFactory->CreateTerminator(teleporterHorizontalWidth);
530 if (terminator)
531 {
532 terminator->Initialise();
533 massWorld->AddComponent(terminator, nullptr, nullptr, integral);
534 }
535
536 BDSAcceleratorComponent* teleporter = theComponentFactory->CreateTeleporter(teleporterLength,
537 teleporterHorizontalWidth,
538 teleporterTransform);
539 if (teleporter)
540 {
541 teleporter->Initialise();
542 massWorld->AddComponent(teleporter, nullptr, nullptr, integral);
543 }
544 }
545
546 if (BDSGlobalConstants::Instance()->Survey())
547 {
548 G4String surveyFileName = BDSGlobalConstants::Instance()->SurveyFileName() + ".dat";
549 if (isPlacementBeamline)
550 {surveyFileName = BDSGlobalConstants::Instance()->SurveyFileName() + "_" + name + ".dat";}
551 BDSSurvey* survey = new BDSSurvey(surveyFileName);
552 survey->Write(massWorld);
553 delete survey;
554 }
555 delete theComponentFactory;
556
557 // print summary
558 G4cout << __METHOD_NAME__ << "\"" << name << "\" " << G4endl << *massWorld;
559
560 // Build curvilinear geometry w.r.t. beam line.
562 BDSBeamline* clBeamline = clBuilder->BuildCurvilinearBeamLine1To1(massWorld, beamlineIsCircular);
563 BDSBeamline* clBridgeBeamline = clBuilder->BuildCurvilinearBridgeBeamLine(clBeamline);
564 delete clBuilder;
565
566 // construct beamline of end pieces
567 BDSBeamline* endPieces = BDS::BuildEndPieceBeamline(massWorld, circular);
568 BDSBeamlineSet beamlineSet;
569 beamlineSet.massWorld = massWorld;
570 beamlineSet.curvilinearWorld = clBeamline;
571 beamlineSet.curvilinearBridgeWorld = clBridgeBeamline;
572 beamlineSet.endPieces = endPieces;
573 return beamlineSet;
574}
575
577{
578 const BDSBeamline* mainBeamLine = acceleratorModel->BeamlineMain();
579 BDSBeamline* tunnelBeamline;
580 BDSTunnelBuilder* tb = new BDSTunnelBuilder(BDSGlobalConstants::Instance()->TunnelMaxSegmentLength());
581 tunnelBeamline = tb->BuildTunnelSections(mainBeamLine);
582 delete tb;
583
585}
586
588{
589 // shortcut
591
592 // calculate extents of everything we need to place in the world first
593 std::vector<BDSExtentGlobal> extents;
594
595 // these beam lines should always exist so are safe to access.
596 const auto& blMain = acceleratorModel->BeamlineSetMain();
597 blMain.GetExtentGlobals(extents);
598
599 // check optional placement beam line (like vector of placements)
601 if (plBeamline) // optional - may be nullptr
602 {extents.push_back(plBeamline->GetExtentGlobal());}
603
604 // check tunnel beam line
605 BDSBeamline* tunnelBeamline = acceleratorModel->TunnelBeamline();
606 if (tunnelBeamline)
607 {extents.push_back(tunnelBeamline->GetExtentGlobal());}
608
609 // check extra beam lines
610 const auto& extras = BDSAcceleratorModel::Instance()->ExtraBeamlines();
611 // extras is a map, so iterator has first and second for key and value
612 for (const auto& bl : extras)
613 {bl.second.GetExtentGlobals(extents);}
614
615 // inspect any sampler placements and calculate their extent without constructing them
616 extents.push_back(CalculateExtentOfSamplerPlacements(blMain.massWorld));
617
618 // inspect any scoring meshes and calculate their extent without constructing them
619 extents.push_back(CalculateExtentOfScorerMeshes(blMain.massWorld));
620
621 // Expand to maximum extents of each beam line.
622 G4ThreeVector worldR;
623 // loop over all extents from all beam lines
624 for (const auto& ext : extents)
625 {
626 for (G4int i = 0; i < 3; i++)
627 {worldR[i] = std::max(worldR[i], ext.GetMaximumExtentAbsolute()[i]);} // expand with the maximum
628 }
629
630 G4String worldName = "World";
631 G4VSolid* worldSolid = nullptr;
632 G4LogicalVolume* worldLV = nullptr;
633
634 G4String worldGeometryFile = globals->WorldGeometryFile();
635 if (!worldGeometryFile.empty())
636 {
637 if (globals->WorldMaterialSet())
638 {BDS::Warning(__METHOD_NAME__, "conflicting options - world material option specified but material will be taken from world GDML file");}
639 G4bool ac = globals->AutoColourWorldGeometryFile();
640
641 std::vector<G4String> namedWorldVacuumVolumes = BDS::SplitOnWhiteSpace(globals->WorldVacuumVolumeNames());
642
644 worldGeometryFile,
645 nullptr,
646 ac,
647 0, 0,
648 &namedWorldVacuumVolumes,
649 true,
650 BDSSDType::energydepworldcontents,
651 BDSSDType::energydepvacuum);
652
653 // get list of 'material' and 'vacuum' volumes for possible biasing of this geometry
654 worldVacuumLogicalVolumes = geom->VacuumVolumes();
655 auto allWorldVolumes = geom->GetAllLogicalVolumes();
656 allWorldVolumes.erase(geom->GetContainerLogicalVolume());
657 for (auto volume : worldVacuumLogicalVolumes)
658 {allWorldVolumes.erase(volume);}
659 worldContentsLogicalVolumes = allWorldVolumes; // cache volumes for biasing
660
661 worldExtent = geom->GetExtent();
662 BDSExtentGlobal worldExtentGlobal = BDSExtentGlobal(worldExtent, G4Transform3D());
663 G4bool worldContainsAllBeamlines = worldExtentGlobal.Encompasses(extents);
664
665 G4cout << "External world geometry: \"" << worldGeometryFile << "\"" << G4endl;
666 G4cout << "Loaded world extent: \n" << worldExtent << G4endl;
667
668 // cannot construct world if any beamline extent is greater than the world extents
669 if (!worldContainsAllBeamlines)
670 {
671 G4String message = "Beamlines cannot be constructed, beamline extents are larger than \n";
672 message += "the extents of the external world";
673 throw BDSException(__METHOD_NAME__, message);
674 }
675
676 worldSolid = geom->GetContainerSolid();
677 worldLV = geom->GetContainerLogicalVolume();
678
679 // make the world sensitive to energy deposition with its own unique hits collection
680 // this will be a nullptr depending on the options.
681 // make world sensitive if importance sampling is needed
682 if (globals->StoreELossWorld()
683 || globals->StoreELossWorldIntegral()
684 || globals->UseImportanceSampling()
685 || globals->StoreELossWorldContents()
686 || globals->StoreELossWorldContentsIntegral())
687 {
689 // override the logical volume itself with a specific SD
690 worldLV->SetSensitiveDetector(BDSSDManager::Instance()->WorldComplete());
691 }
692 }
693 else
694 {
695 // add on margin for constructed world volume
696 G4double margin = globals->WorldVolumeMargin();
697 margin = std::max(margin, 2*CLHEP::m); // minimum margin of 2m.
698 worldR += G4ThreeVector(margin,margin,margin); //add 5m extra in every dimension
699
700 G4cout << __METHOD_NAME__ << "World dimensions: " << worldR / CLHEP::m << " m" << G4endl;
701
702 G4String worldMaterialName = globals->WorldMaterial();
703 G4Material* worldMaterial = BDSMaterials::Instance()->GetMaterial(worldMaterialName);
704 worldExtent = BDSExtent(worldR);
705 worldSolid = new G4Box(worldName + "_solid", worldR.x(), worldR.y(), worldR.z());
706
707
708 worldLV = new G4LogicalVolume(worldSolid, // solid
709 worldMaterial, // material
710 worldName + "_lv"); // name
711
712 // make the world sensitive to energy deposition with its own unique hits collection
713 // note with our world, there are no 'contents' to also consider
714 if (globals->StoreELossWorld() || globals->StoreELossWorldIntegral())
715 {worldLV->SetSensitiveDetector(BDSSDManager::Instance()->WorldComplete());}
716 }
717
718 // visual attributes
719 // copy the debug vis attributes but change to force wireframe
720 G4VisAttributes* debugWorldVis = new G4VisAttributes(*(globals->ContainerVisAttr()));
721 debugWorldVis->SetForceWireframe(true);//just wireframe so we can see inside it
722 worldLV->SetVisAttributes(debugWorldVis);
723
724 // set limits
725 worldLV->SetUserLimits(globals->DefaultUserLimits());
726
727 // place the world
728 G4VPhysicalVolume* worldPV = new G4PVPlacement(nullptr, // no rotation
729 G4ThreeVector(), // at (0,0,0)
730 worldLV, // its logical volume
731 worldName, // its name
732 nullptr, // its mother volume
733 false, // no boolean operation
734 0, // copy number
735 checkOverlaps); // overlap checking
736
737 // Register the lv & pvs to our holder class for the model
741
742 // Register world PV with our auxiliary navigator so steppers and magnetic
743 // fields know which geometry to navigate to get local / global transforms.
744 // This is the regular world used as a backup to the curvilinear world.
747
750
751 worldLogicalVolume = worldLV; // set for possible biasing
752 return worldPV;
753}
754
755void BDSDetectorConstruction::ComponentPlacement(G4VPhysicalVolume* worldPV)
756{
757 // We musn't place parallel world geometry here - their world is produced by
758 // Geant4 at the right time, so we have a separate placement call for them
759 BDSBeamlineSet mainBL = BDSAcceleratorModel::Instance()->BeamlineSetMain();
760 PlaceBeamlineInWorld(mainBL.massWorld,
761 worldPV, checkOverlaps, true, false, false, false, true); // record pv set to element for output
762 PlaceBeamlineInWorld(mainBL.endPieces,
763 worldPV, checkOverlaps);
765 {
767 worldPV, checkOverlaps);
768 }
769 // No energy counter SD added here as individual placements have that attached
770 // during construction time
771 PlaceBeamlineInWorld(placementBL, worldPV, checkOverlaps, false, false, false, false, true);
772
773 // Place BLMs. Similarly, no sensitivity set here - done at construction time.
774 PlaceBeamlineInWorld(BDSAcceleratorModel::Instance()->BLMsBeamline(),
775 worldPV,
777 false,
778 false,
779 false,
780 true); // use incremental copy numbers
781
782 const auto& extras = BDSAcceleratorModel::Instance()->ExtraBeamlines();
783 for (auto const& bl : extras)
784 {// extras is a map so iterator has first and second for key and value
785 // note these are currently not sensitive as there's no CL frame for them
786 PlaceBeamlineInWorld(bl.second.massWorld, worldPV, checkOverlaps, false, false, false, false, true);
787 PlaceBeamlineInWorld(bl.second.endPieces, worldPV, checkOverlaps);
788 }
789}
790
792 G4VPhysicalVolume* containerPV,
793 G4bool checkOverlaps,
794 G4bool setRegions,
795 G4bool registerInfo,
796 G4bool useCLPlacementTransform,
797 G4bool useIncrementalCopyNumbers,
798 G4bool registerPlacementNamesForOutput)
799{
800 if (!beamline)
801 {return;}
802
803 G4int i = 0;
804 for (auto element : *beamline)
805 {
806 // Do nothing for gap element
807 if (dynamic_cast<BDSGap*>(element->GetAcceleratorComponent()))
808 {continue;}
809
810 if (setRegions)
811 {
812 auto accComp = element->GetAcceleratorComponent();
813 const G4String regionName = accComp->GetRegion();
814 if (!regionName.empty()) // ie string is defined so we should attach region
815 {
816 G4Region* region = BDSAcceleratorModel::Instance()->Region(regionName);
817 auto contLV = accComp->GetContainerLogicalVolume();
818 contLV->SetRegion(region);
819 region->AddRootLogicalVolume(contLV);
820 }
821 }
822
823 // setup the sensitivity
824 element->GetAcceleratorComponent()->AttachSensitiveDetectors();
825
826 // make the placement
827 G4int copyNumber = useIncrementalCopyNumbers ? i : element->GetCopyNo();
828 G4String placementName = element->GetPlacementName() + "_pv";
829 std::set<G4VPhysicalVolume*> pvs = element->PlaceElement(placementName, containerPV, useCLPlacementTransform,
830 copyNumber, checkOverlaps);
831
832 if (registerInfo)
833 {
834 BDSPhysicalVolumeInfo* theinfo = new BDSPhysicalVolumeInfo(element->GetName(),
835 placementName,
836 element->GetSPositionMiddle(),
837 element->GetIndex(),
838 beamline);
839
841 }
842 if (registerPlacementNamesForOutput)
844 i++; // for incremental copy numbers
845 }
846}
847
849 const BDSBeamline* beamLine,
850 G4double* S,
851 BDSExtent* placementExtent,
852 const G4String& objectTypeForErrorMsg)
853{
854 G4Transform3D result;
855
856 // 3 scenarios
857 // 1) global placement X,Y,Z + rotation
858 // 2) w.r.t. beam line placement x,y,S + rotation
859 // 3) w.r.t. element in beam line placement elementName + x,y,s + rotation
860
861 // in all cases, need the rotation
862 G4RotationMatrix rm = G4RotationMatrix();
863 if (placement.axisAngle)
864 {
865 G4ThreeVector axis = G4ThreeVector(placement.axisX,
866 placement.axisY,
867 placement.axisZ);
868 rm = G4RotationMatrix(axis, placement.angle*CLHEP::rad);
869 }
870 else
871 {
872 if (BDS::IsFinite(placement.phi) ||
873 BDS::IsFinite(placement.theta) ||
874 BDS::IsFinite(placement.psi))
875 {// only build if finite
876 CLHEP::HepEulerAngles ang = CLHEP::HepEulerAngles(placement.phi*CLHEP::rad,
877 placement.theta*CLHEP::rad,
878 placement.psi*CLHEP::rad);
879 rm = G4RotationMatrix(ang);
880 }
881 }
882
883 // create a transform w.r.t. the beam line if s is finite and it's not w.r.t a
884 // particular element. If it's w.r.t. a particular element, treat s as local curvilinear
885 // s and use as local 'z' in the transform.
886 if (!placement.referenceElement.empty())
887 {// scenario 3
888 if (!beamLine)
889 {throw BDSException(__METHOD_NAME__, "no valid beam line yet for " + objectTypeForErrorMsg + " w.r.t. a beam line.");}
890 const BDSBeamlineElement* element = beamLine->GetElement(placement.referenceElement,
891 placement.referenceElementNumber);
892 if (!element)
893 {
894 G4cerr << __METHOD_NAME__ << "No element named \"" << placement.referenceElement << "\" (instance #"
895 << placement.referenceElementNumber << ") in " << objectTypeForErrorMsg << " \""
896 << placement.name << "\"" << G4endl;
897 G4cout << "Note, this may be because the element is a bend and split into " << G4endl;
898 G4cout << "multiple sections with unique names. Run the visualiser to get " << G4endl;
899 G4cout << "the name of the segment, or place w.r.t. the element before / after." << G4endl;
900 throw BDSException("Error in "+objectTypeForErrorMsg+".");
901 }
902 // in this case we should use s for longitudinal offset - warn user if mistakenly using z
903 if (BDS::IsFinite(placement.z))
904 {
905 G4String message = objectTypeForErrorMsg + " \"" + placement.name + "\" is placed using";
906 message += " a referenceElement but the z offset is\n non zero. Note, s should be used to";
907 message += " offset the placement in this case and z will\n have no effect.";
908 BDS::Warning(message);
909 }
910 G4double sCoordinate = element->GetSPositionMiddle(); // start from middle of element
911 sCoordinate += placement.s * CLHEP::m; // add on (what's considered) 'local' s from the placement
912 if (S)
913 {*S = sCoordinate;}
914
915 G4ThreeVector offset = G4ThreeVector();
916 if (placementExtent)
917 {offset = SideToLocalOffset(placement, beamLine, *placementExtent);}
918
919 G4Transform3D beamlinePart = beamLine->GetGlobalEuclideanTransform(sCoordinate,
920 placement.x * CLHEP::m + offset.x(),
921 placement.y * CLHEP::m + offset.y());
922 G4Transform3D localRotation(rm, G4ThreeVector());
923 result = beamlinePart * localRotation;
924 }
925 else if (BDS::IsFinite(placement.s))
926 {// scenario 2
927 if (!beamLine)
928 {throw BDSException(__METHOD_NAME__, "no valid beam line yet placement w.r.t. a beam line.");}
929 G4ThreeVector offset = G4ThreeVector();
930 if (placementExtent)
931 {offset = SideToLocalOffset(placement, beamLine, *placementExtent);}
932
933 G4Transform3D beamlinePart = beamLine->GetGlobalEuclideanTransform(placement.s * CLHEP::m,
934 placement.x * CLHEP::m + offset.x(),
935 placement.y * CLHEP::m + offset.y());
936
937 G4Transform3D localRotation(rm, G4ThreeVector());
938 result = beamlinePart * localRotation;
939 if (S)
940 {*S = placement.s*CLHEP::m;}
941 }
942 else
943 {// scenario 1
944 G4ThreeVector translation = G4ThreeVector(placement.x*CLHEP::m,
945 placement.y*CLHEP::m,
946 placement.z*CLHEP::m);
947
948
949 result = G4Transform3D(rm, translation);
950 if (S)
951 {*S = -1000;} // default
952 }
953
954 return result;
955}
956
958 const BDSBeamline* beamLine,
959 G4double* S)
960{
961 // convert a scorermesh to a general placement for generation of the transform only.
962 GMAD::Placement convertedPlacement(scorerMesh);
963 return CreatePlacementTransform(convertedPlacement, beamLine, S, nullptr, "scorermesh");
964}
965
967 const BDSBeamline* beamLine,
968 G4double* S)
969{
970 // convert a sampler placement to a general placement for generation of the transform only.
971 GMAD::Placement convertedPlacement(samplerPlacement);
972 return CreatePlacementTransform(convertedPlacement, beamLine, S, nullptr, "samplerplacement");
973}
974
976 const BDSBeamline* beamLine,
977 G4double* S,
978 BDSExtent* blmExtent)
979{
980 // convert a sampler placement to a general placement for generation of the transform.
981 GMAD::Placement convertedPlacement(blmPlacement);
982 return CreatePlacementTransform(convertedPlacement, beamLine, S, blmExtent, "blm");
983}
984
986 const BDSBeamline* beamLine,
987 G4double* S)
988{
989 GMAD::Placement convertedPlacement(queryPlacement);
990 return CreatePlacementTransform(convertedPlacement, beamLine, S, nullptr, "query");
991}
992
994 const BDSBeamline* beamLine,
995 const BDSExtent& placementExtent)
996{
997 G4ThreeVector result = G4ThreeVector();
998 G4String side = G4String(placement.side);
999 side = BDS::LowerCase(side);
1000
1001 // Get the iterators pointing to the first and last elements
1002 // that the placement lines up with.
1003 G4double pathLength = placement.s*CLHEP::m;
1004 std::pair<G4double, G4double> extentZ = placementExtent.ExtentZ();
1005 G4double sLow = pathLength + extentZ.first;
1006 G4double sHigh = pathLength + extentZ.second;
1007 // iterator pointing to lower bound
1008 auto start = beamLine->FindFromS(sLow);
1009 auto end = beamLine->FindFromS(sHigh);
1010 if (end != beamLine->end())
1011 {end++;}
1012
1013 // Fold across the extents returning the greatest extent. The transverse extents
1014 // will give be the transverse extents of the beamline section.
1015 BDSExtent sectionMaxExtent = BDSExtent();
1016 for (auto iter = start; iter != end; ++iter)
1017 {sectionMaxExtent = BDS::MaximumCombinedExtent((*iter)->GetExtent(), sectionMaxExtent);}
1018
1019 // Multiplied by 5 because it works...
1020 G4double ls = 5 * BDSGlobalConstants::Instance()->LengthSafetyLarge();
1021
1022 if (BDS::IsFinite(placement.sideOffset))
1023 {ls = placement.sideOffset * CLHEP::m;}
1024
1025 if (side == "top")
1026 {
1027 result.setY(sectionMaxExtent.YPos() + placementExtent.YPos() + ls);
1028 G4double xOffset = sectionMaxExtent.XPos() - 0.5*sectionMaxExtent.DX();
1029 result.setX(xOffset);
1030 }
1031 else if (side == "bottom")
1032 {
1033 result.setY(sectionMaxExtent.YNeg() + placementExtent.YNeg() - ls);
1034 G4double xOffset = sectionMaxExtent.XPos() - 0.5*sectionMaxExtent.DX();
1035 result.setX(xOffset);
1036 }
1037 else if (side == "left")
1038 {result.setX(sectionMaxExtent.XPos() + placementExtent.XPos() + ls);}
1039 else if (side == "right")
1040 {result.setX(sectionMaxExtent.XNeg() + placementExtent.XNeg() - ls);}
1041 else if (side != "")
1042 {throw BDSException(std::string("Unknown side in placement: " + side));}
1043 return result;
1044}
1045
1047{
1048 BDSExtent apertureExtent;
1049 if (sp.apertureModel.empty())
1050 {
1051 if (sp.samplerType == "plane")
1052 {
1053 BDSApertureInfo aperture = BDSApertureInfo(sp.shape,
1054 sp.aper1 * CLHEP::m,
1055 sp.aper2 * CLHEP::m,
1056 sp.aper3 * CLHEP::m,
1057 sp.aper4 * CLHEP::m,
1058 sp.name);
1059 apertureExtent = aperture.Extent();
1060 }
1061 else if (sp.samplerType == "cylinder" || sp.samplerType == "cylinderforward") // we ignore the possibility of only a part-cylinder
1062 {apertureExtent = BDSExtent(sp.aper1*CLHEP::m, sp.aper1*CLHEP::m, sp.aper2*CLHEP::m);}
1063 else if (sp.samplerType == "sphere" || sp.samplerType == "sphereforward") // we ignore the possibility of only a part-sphere
1064 {apertureExtent = BDSExtent(sp.aper1*CLHEP::m, sp.aper1*CLHEP::m, sp.aper1*CLHEP::m);}
1065 else
1066 {throw BDSException(__METHOD_NAME__, "unknown samplerType \"" + sp.samplerType + "\" in samplerplacement \"" + sp.name + "\"");}
1067 }
1068 else
1069 {
1070 BDSApertureInfo* aperture = BDSAcceleratorModel::Instance()->Aperture(sp.apertureModel);
1071 apertureExtent = aperture->Extent();
1072 }
1073
1074 // aperture is only transverse - fiddle z
1075 BDSExtent result = BDSExtent(apertureExtent.XNeg(), apertureExtent.XPos(),
1076 apertureExtent.YNeg(), apertureExtent.YPos(),
1077 1*CLHEP::um, 1*CLHEP::um);
1078 return result;
1079}
1080
1082{
1083 BDSExtentGlobal result;
1084 std::vector<GMAD::SamplerPlacement> samplerPlacements = BDSParser::Instance()->GetSamplerPlacements();
1085 for (const auto& samplerPlacement : samplerPlacements)
1086 {
1087 BDSExtent samplerExtent = CalculateExtentOfSamplerPlacement(samplerPlacement);
1088 G4Transform3D placementTransform = CreatePlacementTransform(samplerPlacement, beamLine);
1089 BDSExtentGlobal samplerExtentG = BDSExtentGlobal(samplerExtent, placementTransform);
1090 result = result.ExpandToEncompass(samplerExtentG);
1091 }
1092 return result;
1093}
1094
1096{
1097 BDSScorerMeshInfo recipe(sm);
1098 return recipe.Extent();
1099}
1100
1102{
1103 BDSExtentGlobal result;
1104 std::vector<GMAD::ScorerMesh> scorerMeshes = BDSParser::Instance()->GetScorerMesh();
1105 for (const auto& mesh : scorerMeshes)
1106 {
1107 BDSExtent meshExtent = CalculateExtentOfScorerMesh(mesh);
1108 G4Transform3D placementTransform = CreatePlacementTransform(mesh, beamLine);
1109 BDSExtentGlobal meshExtentG = BDSExtentGlobal(meshExtent, placementTransform);
1110 result = result.ExpandToEncompass(meshExtentG);
1111 }
1112 return result;
1113}
1114
1115#if G4VERSION_NUMBER > 1009
1117BDSDetectorConstruction::BuildCrossSectionBias(const std::list<std::string>& biasList,
1118 const std::list<std::string>& defaultBias,
1119 const G4String& elementName)
1120{
1121 // no accelerator components to bias
1122 if (biasList.empty() && defaultBias.empty())
1123 {return nullptr;}
1124
1125 std::list<std::string> biasesAll = biasList.empty() ? defaultBias : biasList;
1126
1127 // build a unique 'key' as the sorted set of bias names
1128 std::set<std::string> biasNamesSorted = {biasesAll.begin(), biasesAll.end()};
1129 G4String biasSetKey;
1130 G4String biasSetPrintOut;
1131 for (const auto& n : biasNamesSorted)
1132 {
1133 biasSetKey += n + "_";
1134 biasSetPrintOut += n + " ";
1135 }
1136 biasSetKey = BDS::StrStrip(biasSetKey, '_', BDS::StringStripType::trailing);
1137
1138 auto exists = biasSetObjects.find(biasSetKey);
1139 if (exists != biasSetObjects.end())
1140 {return exists->second;}
1141
1142 // loop over all physics biasing
1143 G4cout << "Bias> Creating unique set of bias objects ( " << biasSetPrintOut << ")" << G4endl;
1145
1146 const auto& biasObjectList = BDSParser::Instance()->GetBiasing();
1147 for (std::string const & bs : biasesAll)
1148 {
1149 auto result = biasObjectList.find(bs);
1150 if (result == biasObjectList.end())
1151 {throw BDSException("Error: bias named \"" + bs + "\" not found for element named \"" + elementName + "\"");}
1152 const GMAD::PhysicsBiasing& pb = *result;
1153
1154 if(debug)
1155 {G4cout << __METHOD_NAME__ << "bias loop : " << bs << " " << pb.particle << " " << pb.process << G4endl;}
1156
1157 eg->AddParticle(pb.particle);
1158
1159 // loop through all processes
1160 if (pb.flag.size() != pb.processList.size())
1161 {throw BDSException(__METHOD_NAME__, "number of flag entries in \"" + pb.name + "\" doesn't match number of processes");}
1162 if (pb.factor.size() != pb.processList.size())
1163 {throw BDSException(__METHOD_NAME__, "number of factor entries in \"" + pb.name + "\" doesn't match number of processes");}
1164 for (unsigned int p = 0; p < pb.processList.size(); ++p)
1165 {eg->SetBias(bs, pb.particle,pb.processList[p],pb.factor[p],(G4int)pb.flag[p]);}
1166 }
1167
1168 biasObjects.push_back(eg);
1169 biasSetObjects[biasSetKey] = eg; // cache it
1170 return eg;
1171}
1172#endif
1173
1175{
1176#if G4VERSION_NUMBER > 1009
1178 if (debug)
1179 {G4cout << __METHOD_NAME__ << "registry=" << registry << G4endl;}
1180
1181#if G4VERSION_NUMBER > 1039
1182 // only available in 4.10.4 onwards
1183 // crystal biasing necessary for implementation of variable density
1184 std::set<G4LogicalVolume*>* crystals = BDSAcceleratorModel::Instance()->VolumeSet("crystals");
1185 if (!crystals->empty())
1186 {
1187 G4cout << __METHOD_NAME__ << "Using crystal biasing: true" << G4endl; // to match print out style further down
1188 auto crystalBiasing = new G4ChannelingOptrMultiParticleChangeCrossSection();
1189 for (auto crystal : *crystals)
1190 {crystalBiasing->AttachTo(crystal);}
1191 }
1192#endif
1193
1194 auto blmSet = BDSBLMRegistry::Instance()->BLMs();
1195 for (auto blm : blmSet)
1196 {
1197 G4String biasNamesS = blm->Bias();
1198 if (biasNamesS.empty())
1199 {continue;}
1200 auto biasNamesV = BDS::SplitOnWhiteSpace(biasNamesS);
1201 std::list<std::string> biasNames = {biasNamesV.begin(), biasNamesV.end()};
1202 std::list<std::string> emptyDefaultBias;
1203 auto biasForBLM = BuildCrossSectionBias(biasNames, emptyDefaultBias, blm->GetName());
1204 for (auto lv : blm->GetAllLogicalVolumes())
1205 {biasForBLM->AttachTo(lv);}
1206 biasForBLM->AttachTo(blm->GetContainerLogicalVolume()); // in some cases it's just a single volume
1207 }
1208
1210 G4String defaultBiasVacuum = BDSParser::Instance()->GetOptions().defaultBiasVacuum;
1211 auto defaultBiasVacuumVector = BDS::SplitOnWhiteSpace(defaultBiasVacuum);
1212 auto defaultBiasVacuumList = std::list<std::string>(defaultBiasVacuumVector.begin(), defaultBiasVacuumVector.end());
1213 G4String defaultBiasMaterial = BDSParser::Instance()->GetOptions().defaultBiasMaterial;
1214 auto defaultBiasMaterialVector = BDS::SplitOnWhiteSpace(defaultBiasMaterial);
1215 auto defaultBiasMaterialList = std::list<std::string>(defaultBiasMaterialVector.begin(), defaultBiasMaterialVector.end());
1216 G4String biasForWorldVolume = g->BiasForWorldVolume();
1217 auto biasForWorldVolumeVector = BDS::SplitOnWhiteSpace(biasForWorldVolume);
1218 auto biasForWorldVolumeList = std::list<std::string>(biasForWorldVolumeVector.begin(), biasForWorldVolumeVector.end());
1219 G4String biasForWorldContents = g->BiasForWorldContents();
1220 auto biasForWorldContentsVector = BDS::SplitOnWhiteSpace(biasForWorldContents);
1221 auto biasForWorldContentsList = std::list<std::string>(biasForWorldContentsVector.begin(), biasForWorldContentsVector.end());
1222 G4String biasForWorldVacuum = g->BiasForWorldVacuum();
1223 auto biasForWorldVacuumVector = BDS::SplitOnWhiteSpace(biasForWorldVacuum);
1224 auto biasForWorldVacuumList = std::list<std::string>(biasForWorldVacuumVector.begin(), biasForWorldVacuumVector.end());
1225
1226 G4bool useDefaultBiasVacuum = !defaultBiasVacuum.empty();
1227 G4bool useDefaultBiasMaterial = !defaultBiasMaterial.empty();
1228 const auto& biasObjectList = BDSParser::Instance()->GetBiasing();
1229 G4bool useBiasForWorldVolume = !biasForWorldVolume.empty();
1230 G4bool useBiasForWorldContents = !biasForWorldContents.empty();
1231 G4bool useBiasForWorldVacuum = !biasForWorldVacuum.empty();
1232 G4bool biasesDefined = !biasObjectList.empty();
1233
1234 G4bool overallUseBiasing = useDefaultBiasVacuum || useDefaultBiasMaterial || biasesDefined || useBiasForWorldVolume || useBiasForWorldContents;
1235 G4cout << __METHOD_NAME__ << "Using generic biasing: " << BDS::BoolToString(overallUseBiasing) << G4endl;
1236 if (!overallUseBiasing)
1237 {return;} // no biasing used -> dont attach as just overhead for no reason
1238
1239 // apply per element biases
1240 std::unordered_map<ACRegistryKey, BDSAcceleratorComponent*> allAcceleratorComponents = registry->AllComponentsIncludingUnique();
1241 for (auto const & item : allAcceleratorComponents)
1242 {
1243 if (debug)
1244 {G4cout << __METHOD_NAME__ << "checking component named: " << item.first.componentName << G4endl;}
1245 BDSAcceleratorComponent* accCom = item.second;
1246 BDSLine* l = dynamic_cast<BDSLine*>(accCom);
1247 if (l)
1248 {continue;} // do nothing for lines because each sub-component already has all definitions
1249 G4String accName = accCom->GetName();
1250 std::set<G4LogicalVolume*> vacuumLVs = accCom->GetAcceleratorVacuumLogicalVolumes();
1251
1252 // Build vacuum bias object based on vacuum bias list in the component
1253 auto vacuumBiasList = accCom->GetBiasVacuumList();
1254 if (!vacuumBiasList.empty() && vacuumLVs.empty())
1255 {BDS::Warning("biasVacuum set for component \"" + accName + "\" but there's no 'vacuum' volume for it and it can't be biased.\nRemove biasVacuum or name it with the namedVacuumVolumes parameter.");}
1256 if ((!vacuumBiasList.empty() || useDefaultBiasVacuum) && !vacuumLVs.empty())
1257 {
1258 auto egVacuum = BuildCrossSectionBias(vacuumBiasList, defaultBiasVacuumList, accName);
1259 for (auto lv : vacuumLVs)
1260 {
1261 if (debug)
1262 {G4cout << __METHOD_NAME__ << "vacuum volume name: " << lv << " " << lv->GetName() << G4endl;}
1263 egVacuum->AttachTo(lv);
1264 }
1265 }
1266
1267 // Build material bias object based on material bias list in the component
1268 auto materialBiasList = accCom->GetBiasMaterialList();
1269 if (!materialBiasList.empty() || useDefaultBiasMaterial)
1270 {
1271 auto egMaterial = BuildCrossSectionBias(materialBiasList, defaultBiasMaterialList, accName);
1272 auto allLVs = accCom->GetAcceleratorMaterialLogicalVolumes();
1273 if (debug)
1274 {G4cout << __METHOD_NAME__ << "# of logical volumes for biasing under 'material': " << allLVs.size() << G4endl;}
1275 for (auto lv : allLVs)
1276 {// BDSAcceleratorComponent automatically removes 'vacuum' volumes from all so we don't need to check
1277 if (debug)
1278 {G4cout << __METHOD_NAME__ << "Biasing 'material' logical volume: " << lv << " " << lv->GetName() << G4endl;}
1279 egMaterial->AttachTo(lv);
1280 }
1281 }
1282
1283 // Build material bias object for a specific LV based on material bias list in the component
1284 auto nameAndMaterialLVBiasList = accCom->GetBiasMaterialLVList();
1285 if (!nameAndMaterialLVBiasList.empty() || useDefaultBiasMaterial)
1286 {
1287 std::map<std::string, std::string> namesAndBiasesMap;
1288 for (G4String lvbias : nameAndMaterialLVBiasList)
1289 {
1290 auto splitpos = lvbias.find(':');
1291 auto lvname = lvbias.substr(0,splitpos);
1292 auto biasname = lvbias.substr(splitpos+1);
1293 namesAndBiasesMap[lvname] = biasname;
1294 }
1295 auto allLVs = accCom->GetAcceleratorMaterialLogicalVolumes();
1296 if (debug)
1297 {G4cout << __METHOD_NAME__ << "# of logical volumes for biasing under 'materialLV': " << allLVs.size() << G4endl;}
1298 for (auto lv : allLVs)
1299 {// BDSAcceleratorComponent automatically removes 'vacuum' volumes from all so we don't need to check
1300 if (debug)
1301 {G4cout << __METHOD_NAME__ << "Biasing 'materialLV' logical volume: " << lv << " " << lv->GetName() << G4endl;}
1302 for (const auto& nameAndBias : namesAndBiasesMap)
1303 {
1304 if (lv->GetName().find(nameAndBias.first) != std::string::npos)
1305 {
1306 auto egMaterialLV = BuildCrossSectionBias({nameAndBias.second}, defaultBiasMaterialList, accName);
1307 egMaterialLV->AttachTo(lv);
1308 }
1309 }
1310 }
1311 }
1312 }
1313
1314 if (useBiasForWorldContents)
1315 {
1316 std::list<std::string> emptyList;
1317 auto egWC = BuildCrossSectionBias(emptyList, biasForWorldContentsList, "world_contents_bias");
1318 for (auto lv : worldContentsLogicalVolumes)
1319 {egWC->AttachTo(lv);}
1320 }
1321 if (useBiasForWorldVolume)
1322 {
1323 std::list<std::string> emptyList;
1324 auto egWV = BuildCrossSectionBias(emptyList, biasForWorldVolumeList, "world_volume_bias");
1325 egWV->AttachTo(worldLogicalVolume);
1326 }
1327 if (useBiasForWorldVacuum)
1328 {
1329 std::list<std::string> emptyList;
1330 auto egWVac = BuildCrossSectionBias(emptyList, biasForWorldVacuumList, "world_vacuum_bias");
1331 for (auto lv : worldVacuumLogicalVolumes)
1332 {egWVac->AttachTo(lv);}
1333 }
1334#endif
1335}
1336
1338{
1339 auto flds = BDSFieldBuilder::Instance()->CreateAndAttachAll(); // avoid shadowing 'fields'
1341
1343}
1344
1345G4bool BDSDetectorConstruction::UnsuitableFirstElement(GMAD::FastList<GMAD::Element>::FastListConstIterator element)
1346{
1347 // skip past any line elements in parser to find first non-line element
1348 while ((*element).type == GMAD::ElementType::_LINE)
1349 {element++;}
1350
1351 if ((*element).type == GMAD::ElementType::_RBEND)
1352 {return true;} // unsuitable
1353 else if (BDS::IsFinite((*element).e1))
1354 {return true;} // unsuitable
1355 else
1356 {return false;} // suitable
1357}
1358
1360{
1361 // needed for filtering
1362 G4LogicalVolume* worldLV = acceleratorModel->WorldLV();
1363
1364 std::vector<GMAD::ScorerMesh> scoringMeshes = BDSParser::Instance()->GetScorerMesh();
1365 std::vector<GMAD::Scorer> scorers = BDSParser::Instance()->GetScorers();
1366
1367 if (scoringMeshes.empty())
1368 {return;}
1369
1370 G4ScoringManager* scManager = G4ScoringManager::GetScoringManager();
1371 scManager->SetVerboseLevel(1);
1372
1373 // convert all the parser scorer definitions into recipes (including parameter checking)
1374 std::map<G4String, BDSScorerInfo> scorerRecipes;
1375 for (const auto& scorer : scorers)
1376 {
1377 BDSScorerInfo si = BDSScorerInfo(scorer, true); // true = upgrade to 3d as required for 3d mesh here
1378 scorerRecipes.insert(std::make_pair(si.name, si));
1379 }
1380
1381 // construct meshes
1382 BDSScorerFactory scorerFactory;
1383 for (const auto& mesh : scoringMeshes)
1384 {
1385 // convert to recipe class as this checks parameters
1386 BDSScorerMeshInfo meshRecipe = BDSScorerMeshInfo(mesh);
1387
1388 // name we'll use for the mesh
1389 G4String meshName = meshRecipe.name;
1390
1391 // TBC - could be any beam line in future - just w.r.t. main beam line just now
1392 const BDSBeamline* mbl = BDSAcceleratorModel::Instance()->BeamlineMain();
1393 G4Transform3D placement = CreatePlacementTransform(mesh, mbl);
1394
1395 BDSScoringMeshBox* scorerBox = nullptr;
1396 BDSScoringMeshCylinder* scorerCylindrical = nullptr;
1397 const BDSHistBinMapper* mapper = nullptr;
1398
1399 G4String geometryType = BDS::LowerCase(G4String(mesh.geometryType));
1400
1401 if (geometryType == "box")
1402 {// create a scoring box
1403 scorerBox = new BDSScoringMeshBox(meshName, meshRecipe, placement);
1404 mapper = scorerBox->Mapper();
1405 }
1406 else if (geometryType == "cylindrical")
1407 {// create a scoring cylinder
1408 scorerCylindrical = new BDSScoringMeshCylinder(meshName, meshRecipe, placement);
1409 mapper = scorerCylindrical->Mapper();
1410 }
1411 else
1412 {
1413 G4String msg = "mesh geometry type \"" + geometryType + "\" is not correct. The possible options are \"box\" and \"cylindrical\"";
1414 throw BDSException(__METHOD_NAME__, msg);
1415 }
1416
1417 // add the scorer(s) to the scoring mesh
1418 std::vector<G4String> meshPrimitiveScorerNames; // final vector of unique mesh + ps names
1419 std::vector<G4double> meshPrimitiveScorerUnits;
1420 std::vector<G4String> scorerNames;
1421
1422 std::vector<G4String> words = BDS::SplitOnWhiteSpace(mesh.scoreQuantity);
1423 for (const auto& word : words)
1424 {
1425 auto search = scorerRecipes.find(word);
1426 if (search == scorerRecipes.end())
1427 {throw BDSException(__METHOD_NAME__, "scorerQuantity \"" + word + "\" for mesh \"" + meshName + "\" not found.");}
1428
1429 G4double psUnit = 1.0;
1430 G4VPrimitiveScorer* ps = scorerFactory.CreateScorer(&(search->second), mapper, &psUnit, worldLV);
1431 // The mesh internally creates a multifunctional detector which is an SD and has
1432 // the name of the mesh. Any primitive scorer attached is added to the mfd. To get
1433 // the hits map we need the full name of the unique primitive scorer so we build that
1434 // name here and store it.
1435 G4String uniqueName = meshName + "/" + ps->GetName();
1436 meshPrimitiveScorerNames.push_back(uniqueName);
1437 meshPrimitiveScorerUnits.push_back(psUnit);
1438
1439 // sets the current ps but appends to list of multiple
1440 if (geometryType == "box")
1441 {scorerBox->SetPrimitiveScorer(ps);}
1442 else if (geometryType == "cylindrical")
1443 {scorerCylindrical->SetPrimitiveScorer(ps);}
1444
1445 BDSScorerHistogramDef outputHistogram(meshRecipe, uniqueName, ps->GetName(), psUnit, *mapper);
1446 BDSAcceleratorModel::Instance()->RegisterScorerHistogramDefinition(outputHistogram);
1447 BDSAcceleratorModel::Instance()->RegisterScorerPlacement(meshName, placement);
1448 }
1449
1450 if (geometryType == "box")
1451 {scManager->RegisterScoringMesh(scorerBox);} // sets the current ps but appends to list of multiple
1452 else if (geometryType == "cylindrical")
1453 {scManager->RegisterScoringMesh(scorerCylindrical);}// sets the current ps but appends to list of multiple
1454
1455 // register it with the sd manager as this is where we get all collection IDs from
1456 // in the end of event action. This must come from the mesh as it creates the
1457 // multifunctionaldetector and therefore has the complete name of the scorer collection
1458 BDSSDManager::Instance()->RegisterPrimitiveScorerNames(meshPrimitiveScorerNames, &meshPrimitiveScorerUnits);
1459 }
1460}
1461
1462std::vector<BDSFieldQueryInfo*> BDSDetectorConstruction::PrepareFieldQueries(const BDSBeamline* mainBeamline)
1463{
1464 std::vector<BDSFieldQueryInfo*> result;
1465 const std::vector<GMAD::Query>& parserQueries = BDSParser::Instance()->GetQuery();
1466 for (const auto& def : parserQueries)
1467 {
1468 G4bool assumeQueryMagnetic = false;
1469 if (!def.queryMagneticField && !def.queryElectricField)
1470 {
1471 assumeQueryMagnetic = true;
1472 G4cout << __METHOD_NAME__ << "neither \"queryMagneticField\" nor \"queryElectricField\" are turned on for definition \""
1473 << def.name << "\"" << G4endl;
1474 G4cout << "-> querying magnetic field by default" << G4endl;
1475 }
1476
1477 if (!def.pointsFile.empty())
1478 {
1479 std::vector<G4String> columnNames;
1480 auto points = BDS::LoadFieldQueryPoints(G4String(def.pointsFile), &columnNames);
1481 result.emplace_back(new BDSFieldQueryInfo(G4String(def.name),
1482 G4String(def.outfileMagnetic),
1483 G4String(def.outfileElectric),
1484 G4bool(def.queryMagneticField) || assumeQueryMagnetic,
1485 G4bool(def.queryElectricField),
1486 points,
1487 columnNames,
1488 G4bool(def.overwriteExistingFiles),
1489 G4String(def.fieldObject),
1490 def.checkParameters,
1491 def.drawArrows,
1492 def.drawZeroValuePoints,
1493 def.drawBoxes,
1494 def.boxAlpha));
1495 }
1496 else
1497 {
1498 G4Transform3D globalTransform3D = CreatePlacementTransform(def, mainBeamline);
1499 auto rot = globalTransform3D.getRotation();
1500 rot = rot.inverse();
1501 G4AffineTransform globalTransform(rot, globalTransform3D.getTranslation());
1502
1503 result.emplace_back(new BDSFieldQueryInfo(G4String(def.name),
1504 G4String(def.outfileMagnetic),
1505 G4String(def.outfileElectric),
1506 G4bool(def.queryMagneticField) || assumeQueryMagnetic,
1507 G4bool(def.queryElectricField),
1508 {def.nx, def.xmin*CLHEP::m, def.xmax*CLHEP::m},
1509 {def.ny, def.ymin*CLHEP::m, def.ymax*CLHEP::m},
1510 {def.nz, def.zmin*CLHEP::m, def.zmax*CLHEP::m},
1511 {def.nt, def.tmin*CLHEP::ns, def.tmax*CLHEP::ns},
1512 globalTransform,
1513 G4bool(def.overwriteExistingFiles),
1514 G4String(def.fieldObject),
1515 G4bool(def.printTransform),
1516 def.checkParameters,
1517 def.drawArrows,
1518 def.drawZeroValuePoints,
1519 def.drawBoxes,
1520 def.boxAlpha));
1521 }
1522 }
1523 return result;
1524}
1525
1526#ifdef BDSCHECKUSERLIMITS
1527void BDSDetectorConstruction::PrintUserLimitsSummary(const G4VPhysicalVolume* world) const
1528{
1529 G4cout << "USERLIMITS START" << G4endl;
1530 G4double globalMinEK = BDSGlobalConstants::Instance()->MinimumKineticEnergy();
1531 PrintUserLimitsPV(world, globalMinEK);
1532 G4cout << "USERLIMITS END" << G4endl;
1533}
1534
1535void BDSDetectorConstruction::PrintUserLimitsPV(const G4VPhysicalVolume* aPV, G4double globalMinEK) const
1536{
1537 const auto lv = aPV->GetLogicalVolume();
1538 const auto ul = lv->GetUserLimits();
1539 if (ul)
1540 {
1541 G4Track dummyTrack;
1542 G4double ekUL = ul->GetUserMinEkine(dummyTrack);
1543 //G4cout << lv->GetName() << " Ek Min: " << ekUL << G4endl;
1544 if (ekUL < globalMinEK)
1545 {G4cout << lv->GetName() << " Ek Min: " << ekUL << " MeV < global: " << globalMinEK << " MeV" << G4endl;}
1546 }
1547 else
1548 {G4cout << lv->GetName() << " no G4UserLimits" << G4endl;}
1549 for (G4int i = 0; i < (G4int)lv->GetNoDaughters(); i++)
1550 {PrintUserLimitsPV(lv->GetDaughter(i), globalMinEK);}
1551}
1552#endif
1553
1555{
1556 if (!worldLogicalVolume)
1557 {return;}
1558 G4cout << "\nSensitivity Summary:\n" << G4endl;
1559 PrintSensitiveDetectorsOfLV(worldLogicalVolume, 0);
1560 G4cout << "\n\n" << G4endl;
1561}
1562
1563void BDSDetectorConstruction::PrintSensitiveDetectorsOfLV(const G4LogicalVolume* lv, G4int currentDepth) const
1564{
1565 for (G4int i = 0; i < currentDepth; i++)
1566 {G4cout << "-> ";}
1567 G4cout << lv->GetName() << " ";
1568 auto sensitiveDetector = lv->GetSensitiveDetector();
1569 if (sensitiveDetector)
1570 {G4cout << sensitiveDetector->GetName();}
1571 else
1572 {G4cout << "none";}
1573 G4cout << G4endl;
1574
1575 for (std::size_t i = 0; i < lv->GetNoDaughters(); i++)
1576 {PrintSensitiveDetectorsOfLV(lv->GetDaughter(i)->GetLogicalVolume(), currentDepth+1);}
1577}
A registry of constructed BDSAcceleratorComponent instances that can be searched.
static BDSAcceleratorComponentRegistry * Instance()
Singleton accessor.
std::unordered_map< ACRegistryKey, BDSAcceleratorComponent * > AllComponentsIncludingUnique() const
void PrintNumberOfEachType() const
Print out the number of each type of component registered.
Abstract class that represents a component of an accelerator.
std::list< std::string > GetBiasMaterialLVList() const
Access the bias list copied from parser.
virtual G4String GetName() const
The name of the component without modification.
virtual std::set< G4LogicalVolume * > GetAcceleratorVacuumLogicalVolumes() const
Access the 'vacuum' volume(s) in this component if any. Default is empty set.
virtual std::set< G4LogicalVolume * > GetAcceleratorMaterialLogicalVolumes() const
Return a set of logical volumes excluding the ones in the 'vacuum' set.
std::list< std::string > GetBiasVacuumList() const
Access the bias list copied from parser.
G4String GetType() const
Get a string describing the type of the component.
std::list< std::string > GetBiasMaterialList() const
Access the bias list copied from parser.
void RegisterFields(std::vector< BDSFieldObjects * > &fieldsIn)
Register all field objects.
void RegisterBeamlineSetExtra(const G4String &name, const BDSBeamlineSet &setIn)
Register a set of beam lines to be managed and cleared up at the end of the simulation.
void RegisterWorldLV(G4LogicalVolume *worldIn)
Register constituent of world.
std::set< G4LogicalVolume * > * VolumeSet(const G4String &name)
Returns pointer to a set of logical volumes. If no set by that name exits, create it.
BDSApertureInfo * Aperture(G4String name) const
const std::map< G4String, BDSBeamlineSet > & ExtraBeamlines() const
Accessor.
BDSBeamline * TunnelBeamline() const
Access the beam line containing all the tunnel segments.
void RegisterApertures(const std::map< G4String, BDSApertureInfo * > &aperturesIn)
Register a map of apertures with associated names.
void RegisterScorerPlacement(const G4String &meshName, const G4Transform3D &placement)
Register a copy of the scorer placement so it can be used in the output.
const BDSBeamlineSet & BeamlineSetMain() const
Accessor.
const BDSBeamline * BeamlineMain() const
Accessor.
void RegisterTunnelBeamline(BDSBeamline *beamlineIn)
Register the beam line containing all the tunnel segments.
BDSBeamline * PlacementBeamline() const
Access the beam line of arbitrary placements.
void RegisterWorldPV(G4VPhysicalVolume *worldIn)
Register constituent of world.
void RegisterPlacementBeamline(BDSBeamline *placementBLIn)
Register the beam line of arbitrary placements.
void RegisterBeamlineSetMain(const BDSBeamlineSet &setIn)
Register the main beam line set.
void RegisterScorerHistogramDefinition(const BDSScorerHistogramDef &def)
G4LogicalVolume * WorldLV() const
Access the logical volume of the world.
void RegisterBLMs(BDSBeamline *blmsIn)
Register a beam line of blm placements.
G4Region * Region(const G4String &name) const
Access region information. Will exit if not found.
void RegisterWorldSolid(G4VSolid *worldIn)
Register constituent of world.
void RegisterRegion(BDSRegion *region)
Register a region.
Holder class for all information required to describe an aperture.
BDSExtent Extent() const
static void AttachWorldVolumeToNavigator(G4VPhysicalVolume *worldPVIn)
Setup the navigator w.r.t. to a world volume - typically real world.
static BDSBLMRegistry * Instance()
Accessor for registry.
A class that holds a fully constructed BDSAcceleratorComponent as well as any information relevant to...
G4double GetSPositionEnd() const
Accessor.
G4double GetSPositionMiddle() const
Accessor.
A class that holds the current integrated quantities along a beam line.
G4bool integrateKineticEnergy
On by default, but allow control to not integrate it for old behaviour.
Simple struct to return a beamline plus associated beam lines.
void GetExtentGlobals(std::vector< BDSExtentGlobal > &extents) const
Append global extents of all beamlines to supplied vector.
A vector of BDSBeamlineElement instances - a beamline.
const BDSBeamlineElement * GetElement(G4String acceleratorComponentName, G4int i=0) const
Get the ith placement of an element in the beam line. Returns null pointer if not found.
BDSBeamlineElement * back() const
Return a reference to the last element.
G4bool ElementAnglesSumToCircle() const
Check if the sum of the angle of all elements is two pi.
reverse_iterator rend()
Iterator mechanics.
void AddComponent(BDSAcceleratorComponent *component, BDSTiltOffset *tiltOffset=nullptr, BDSSamplerInfo *samplerInfo=nullptr, const BDSBeamlineIntegral *beamlineIntegral=nullptr)
const_iterator FindFromS(G4double S) const
Returns an iterator to the beamline element at s.
G4bool empty() const
Iterator mechanics.
G4Transform3D GetGlobalEuclideanTransform(G4double s, G4double x=0, G4double y=0, G4int *indexOfFoundElement=nullptr) const
BDSExtentGlobal GetExtentGlobal() const
Get the global extents for this beamline.
reverse_iterator rbegin()
Iterator mechanics.
iterator begin()
Iterator mechanics.
iterator end()
Iterator mechanics.
Factory for user specified accelerator components.
Factory to produce all types of BDSAcceleratorComponents.
BDSAcceleratorComponent * CreateComponent(GMAD::Element const *elementIn, GMAD::Element const *prevElementIn, GMAD::Element const *nextElementIn, BDSBeamlineIntegral &integral)
BDSAcceleratorComponent * CreateTeleporter(const G4double teleporterLength, const G4double teleporterWidth, const G4Transform3D &transformIn)
BDSAcceleratorComponent * CreateTerminator(G4double witdth)
static BDSTiltOffset * CreateTiltOffset(GMAD::Element const *el)
Create the tilt and offset information object by inspecting the parser element.
Factory for simple parallel geometry for curvilinear coordinates.
BDSBeamline * BuildCurvilinearBeamLine1To1(BDSBeamline const *const beamline, const G4bool circular)
Build a beam line of curvilinear geometry based on another beam line.
BDSBeamline * BuildCurvilinearBridgeBeamLine(BDSBeamline const *const beamline)
Build bridging volumes to join the curvilinear ones.
G4bool circular
Whether or not we're building a circular machine.
BDSBOptrMultiParticleChangeCrossSection * BuildCrossSectionBias(const std::list< std::string > &biasList, const std::list< std::string > &defaultBias, const G4String &elementName)
Function that creates physics biasing cross section.
void InitialiseRegions()
Create and set parameters for various G4Regions.
std::vector< BDSBOptrMultiParticleChangeCrossSection * > biasObjects
List of bias objects - for memory management.
BDSExtent CalculateExtentOfScorerMesh(const GMAD::ScorerMesh &sm) const
Calculate local extent of scorer mesh in 3D.
BDSAcceleratorModel * acceleratorModel
Accelerator model pointer.
static G4ThreeVector SideToLocalOffset(const GMAD::Placement &placement, const BDSBeamline *beamLine, const BDSExtent &placementExtent)
Attach component with extent2 to component with extent1 with placement.
G4bool verbose
Variable copied from global constants.
static std::vector< BDSFieldQueryInfo * > PrepareFieldQueries(const BDSBeamline *mainBeamline)
Prepare field queries from parser information.
void VerboseSensitivity() const
Print out the sensitivity of every single volume so far constructed in the world.
void InitialiseApertures()
Create all aperture definitions from parser and store in BDSAcceleratorModel.
void CountPlacementFields()
Count number of fields required for placements.
G4bool canSampleAngledFaces
Whether the integrator set permits sampling elements with angled faces.
BDSExtent worldExtent
Record of the world extent.
std::set< G4LogicalVolume * > worldContentsLogicalVolumes
Cache of possibly loaded logical volumes from a world geometry file - used for biasing.
static BDSSamplerInfo * BuildSamplerInfo(const GMAD::Element *element)
BDSExtent CalculateExtentOfSamplerPlacement(const GMAD::SamplerPlacement &sp) const
Calculate local extent of custom user sampler.
void ConstructScoringMeshes()
Construct scoring meshes.
BDSBeamlineSet BuildBeamline(const GMAD::FastList< GMAD::Element > &beamLine, const G4String &name, const BDSBeamlineIntegral &startingIntegral, BDSBeamlineIntegral *&integral, const G4Transform3D &initialTransform=G4Transform3D(), G4bool beamlineIsCircular=false, G4bool isPlacementBeamline=false)
BDSExtentGlobal CalculateExtentOfScorerMeshes(const BDSBeamline *bl) const
G4bool checkOverlaps
Variable copied from global constants.
G4int nSamplers
Count of number of samplers to be built.
void ComponentPlacement(G4VPhysicalVolume *worldPV)
Place beam line, tunnel beam line, end pieces and placements in world.
void BuildBeamlines()
Build the main beam line and then any other required beam lines.
const BDSParticleDefinition * designParticle
Particle definition all components are built w.r.t. Includes rigidity etc.
void BuildTunnel()
Build the tunnel around the already constructed flat beam line.
virtual void ConstructSDandField()
Construct sensitive detectors and fields.
void PrintSensitiveDetectorsOfLV(const G4LogicalVolume *lv, G4int currentDepth) const
Recursive function to print out each sensitive detector name.
static void PlaceBeamlineInWorld(BDSBeamline *beamline, G4VPhysicalVolume *containerPV, G4bool checkOverlaps=false, G4bool setRegions=false, G4bool registerInfo=false, G4bool useCLPlacementTransform=false, G4bool useIncrementalCopyNumbers=false, G4bool registerPlacementNamesForOutput=false)
virtual G4VPhysicalVolume * Construct()
static G4Transform3D CreatePlacementTransform(const GMAD::Placement &placement, const BDSBeamline *beamLine, G4double *S=nullptr, BDSExtent *placementExtent=nullptr, const G4String &objectTypeForErrorMsg="placement")
G4bool UnsuitableFirstElement(std::list< GMAD::Element >::const_iterator element)
BDSExtentGlobal CalculateExtentOfSamplerPlacements(const BDSBeamline *beamLine) const
General exception with possible name of object and message.
Holder for +- extents in 3 dimensions with a rotation and translation.
BDSExtentGlobal ExpandToEncompass(const BDSExtentGlobal &other) const
Return a copy of this extent but expanded to encompass another global extent.
G4bool Encompasses(const BDSExtentGlobal &otherExtent)
Return whether the extent encompasses another extent.
Holder for +- extents in 3 dimensions.
Definition BDSExtent.hh:39
G4double XPos() const
Accessor.
Definition BDSExtent.hh:66
G4double XNeg() const
Accessor.
Definition BDSExtent.hh:65
G4double YNeg() const
Accessor.
Definition BDSExtent.hh:67
std::pair< G4double, G4double > ExtentZ() const
Accessor.
Definition BDSExtent.hh:63
G4double DX() const
The difference in a dimension.
Definition BDSExtent.hh:83
G4double YPos() const
Accessor.
Definition BDSExtent.hh:68
static BDSFieldBuilder * Instance()
Singleton pattern accessor.
Holder class for all information required for a field query.
static void AttachWorldVolumeToNavigator(G4VPhysicalVolume *worldPVIn)
Setup the navigator w.r.t. to a world volume.
Gap in accelerator beamline.
Definition BDSGap.hh:32
G4LogicalVolume * GetContainerLogicalVolume() const
Accessor - see member for more info.
BDSExtent GetExtent() const
Accessor - see member for more info.
virtual void AttachSensitiveDetectors()
Attach a sensitive detector class to all registered sensitive volumes in this component.
virtual std::set< G4LogicalVolume * > GetAllLogicalVolumes() const
Access all logical volumes belonging to this component.
G4VSolid * GetContainerSolid() const
Accessor - see member for more info.
A loaded piece of externally provided geometry.
const std::set< G4LogicalVolume * > & VacuumVolumes() const
Access the vacuum volumes.
BDSGeometryExternal * BuildGeometry(G4String componentName, const G4String &formatAndFilePath, std::map< G4String, G4Colour * > *colourMapping=nullptr, G4bool autoColour=true, G4double suggestedLength=0, G4double suggestedHorizontalWidth=0, std::vector< G4String > *namedVacuumVolumes=nullptr, G4bool makeSensitive=true, BDSSDType sensitivityType=BDSSDType::energydep, BDSSDType vacuumSensitivityType=BDSSDType::energydepvacuum, G4bool stripOuterVolumeAndMakeAssembly=false, G4UserLimits *userLimitsToAttachToAllLVs=nullptr, G4bool dontReloadGeometry=false)
static BDSGeometryFactory * Instance()
Singleton accessor.
A class that holds global options and constants.
void SetCurvilinearDiameterShrunkForBends()
Setter.
static BDSGlobalConstants * Instance()
Access method.
void SetCurvilinearDiameter(G4double curvilinearDiameterIn)
Setter.
void SetSamplerDiameter(G4double samplerDiameterIn)
Setter.
G4bool UseImportanceSampling() const
Is importance sampling being used.
Mapping from axis indices to 1D index.
A class that hold multiple accelerator components.
Definition BDSLine.hh:38
static BDSMaterials * Instance()
Singleton pattern access.
G4Material * GetMaterial(G4String material) const
Get material by name.
const GMAD::FastList< GMAD::Element > & GetSequence(const std::string &name)
Return sequence.
Definition BDSParser.hh:82
std::vector< GMAD::Scorer > GetScorers() const
Return the parser list of that object.
Definition BDSParser.hh:111
static BDSParser * Instance()
Access method.
Definition BDSParser.cc:30
std::vector< GMAD::Aperture > GetApertures() const
Return the parser list of that object.
Definition BDSParser.hh:115
const GMAD::Options & GetOptions() const
Return options.
Definition BDSParser.hh:51
std::vector< GMAD::Query > GetQuery() const
Return the parser list of that object.
Definition BDSParser.hh:108
std::vector< GMAD::SamplerPlacement > GetSamplerPlacements() const
Return the parser list of that object.
Definition BDSParser.hh:110
std::vector< GMAD::Region > GetRegions() const
Return the parser list of that object.
Definition BDSParser.hh:109
const GMAD::FastList< GMAD::PhysicsBiasing > & GetBiasing() const
Return biasing list.
Definition BDSParser.hh:93
std::vector< GMAD::ScorerMesh > GetScorerMesh() const
Return the parser list of that object.
Definition BDSParser.hh:112
std::vector< GMAD::Placement > GetPlacements() const
Return the parser list of that object.
Definition BDSParser.hh:107
void RegisterPVsForOutput(const BDSBeamlineElement *element, const std::set< G4VPhysicalVolume * > &physicalVolumes)
void RegisterExcludedPV(G4VPhysicalVolume *physicalVolume)
void RegisterInfo(G4VPhysicalVolume *physicalVolume, BDSPhysicalVolumeInfo *info, G4bool isReadOutVolume=false, G4bool isTunnel=false)
static BDSPhysicalVolumeInfoRegistry * Instance()
Singleton accessor.
A class holding any information pertaining to a particular physical volume in a BDSIM geant4 model.
Range cuts for a region. Help with defaults.
Definition BDSRegion.hh:41
void ConstructSamplerSDsForParticleSets(const std::map< int, std::set< int > > &samplerFilterIDtoPDGSetIn)
void RegisterPrimitiveScorerNames(const std::vector< G4String > &namesIn, const std::vector< G4double > *units=nullptr)
Loop over a vector and apply above single name function.
All info required to build a sampler but not place it.
Create primitive scorers on demand.
G4VPrimitiveScorer * CreateScorer(const BDSScorerInfo *info, const BDSHistBinMapper *mapper, G4double *unit=nullptr, G4LogicalVolume *worldLV=nullptr)
Main function to create a scorer.
Definition for a scorer histogram.
Recipe class for scorer. Checks values.
G4String name
Scorer name.
Recipe class for a scoring mesh.
Wrapper for G4ScoringBox to allow full access to placement.
Wrapper for G4ScoringCylinder to allow full access to placement.
A class of functions to output Geant4/Mokka/BDSIM parameters for the beamline.
Definition BDSSurvey.hh:39
void Write(BDSBeamlineElement *beamlineElement)
write line
Definition BDSSurvey.cc:109
A holder for any placement offsets and rotations for a BDSAcceleratorComponent.
static G4double dEThresholdForScattering
A class that constructs tunnel segments around a beamline.
BDSBeamline * BuildTunnelSections(const BDSBeamline *flatBeamLine) const
Aperture class.
Definition aperture.h:38
blm for parser.
List with Efficient Lookup.
Definition fastlist.h:42
FastListIterator begin()
Definition fastlist.h:218
FastListIterator end()
Definition fastlist.h:223
bool empty() const
Whether the list is empty.
Definition fastlist.h:68
FastListConstIterator find(std::string name, unsigned int count=1) const
Definition fastlist.h:257
int size() const
size of list
Definition fastlist.h:167
const FastList< Element > & GetBeamline() const
Definition parser.cc:945
Physics biasing class for parser.
std::vector< double > factor
factors corresponding to process
std::vector< PhysicsBiasingType > flag
flag which particles are biased
std::string particle
particle name
std::string process
geant4 process: single string, but can have multiple processes separated with a space
Placement class for parser.
Definition placement.h:41
bool axisAngle
Flag to use the axis angle construction of rotation.
Definition placement.h:65
std::string name
Name of this placement.
Definition placement.h:43
std::string side
which side to attach to: top, bottom, left, right.
Definition placement.h:66
double theta
Euler angle for rotation.
Definition placement.h:55
double sideOffset
Gap between side and component.
Definition placement.h:67
double psi
Euler angle for rotation.
Definition placement.h:56
double y
Offset in y.
Definition placement.h:51
double s
Curvilinear s position to place w.r.t..
Definition placement.h:49
double x
Offset in x.
Definition placement.h:50
int referenceElementNumber
Index of repetition of element if there are multiple uses.
Definition placement.h:48
double phi
Euler angle for rotation.
Definition placement.h:54
std::string referenceElement
Name of reference element w.r.t. to place to.
Definition placement.h:47
Query structure class for parser.
Definition query.h:37
Region class for parser.
Definition region.h:36
Sampler placement class for parser.
std::string name
Name of this samplerplacement.
std::string samplerType
Plane, Cylinder, Sphere.
ScorerMesh class for parser.
Definition scorermesh.h:40
BDSBeamline * BuildEndPieceBeamline(const BDSBeamline *beamline, const G4bool circularMachine)
G4Transform3D CalculateTeleporterDelta(const BDSBeamline *beamline, G4double &teleporterLength)
std::vector< BDSFourVector< G4double > > LoadFieldQueryPoints(const G4String &fileName, std::vector< G4String > *columnNamesIn)
BDSExtent MaximumCombinedExtent(const BDSExtent &first, const BDSExtent &second)
Returns the extent which is the greatest extent in all six directions.
Definition BDSExtent.cc:248
G4String LowerCase(const G4String &str)
Utility function to simplify lots of syntax changes for pedantic g4 changes.
BDSBeamline * BuildPlacementGeometry(const std::vector< GMAD::Placement > &placements, const BDSBeamline *parentBeamLine, BDSComponentFactory *componentFactory, const BDSParticleDefinition *designParticle)
std::vector< G4String > SplitOnWhiteSpace(const G4String &input)
Split a string on whitespace and return a vector of these 'words'.
BDSBeamline * BuildBLMs(const std::vector< GMAD::BLMPlacement > &blmPlacements, const BDSBeamline *parentBeamLine)
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
G4String StrStrip(const G4String &str, char ch, StringStripType stripType=StringStripType::both)
Utility function to simplify lots of syntax changes for pedantic g4 changes.
Element class.
Definition element.h:45
std::string samplerType
element has a sampler of this type (default "none")
Definition element.h:238
int samplerParticleSetID
Definition element.h:243
double e1
input pole face rotation for bends
Definition element.h:62
std::string samplerName
name of sampler (default empty)
Definition element.h:237