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