BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
Loading...
Searching...
No Matches
BDSPSPopulationScaled.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2023.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "BDSDebug.hh"
20#include "BDSException.hh"
21#include "BDSHistBinMapper.hh"
22#include "BDSScorerConversionLoader.hh"
23#include "BDSPSPopulationScaled.hh"
24#include "BDSUtilities.hh"
25
26#include "G4IonTable.hh"
27#include "G4PhysicsVector.hh"
28#include "G4String.hh"
29#include "G4Types.hh"
30#include "G4VSolid.hh"
31#include "G4VPVParameterisation.hh"
32
33#include <cmath>
34#include <fstream>
35#include <iterator>
36#include <string>
37#include <dirent.h>
38
39#ifdef USE_GZSTREAM
40#include "src-external/gzstream/gzstream.h"
41#endif
42
44 G4int depth):
45 G4VPrimitiveScorer(scorerName, depth),
46 HCID(-1),
47 EvtMap(nullptr)
48{;}
49
51 const G4String& pathname,
52 G4int depth):
53 BDSPSPopulationScaled(scorerName, depth)
54{
55 G4String directory = BDS::GetFullPath(pathname);
56 if (directory.back() != '/')
57 {directory += '/';}
58
59 std::vector<G4String> dirsAngle;
60 std::vector<G4String> filesParticle;
61
62 dirsAngle = LoadDirectoryContents(directory);
63
64 G4cout << "Scorer \"" << GetName() << "\" - adding conversionFiles from: " + directory << G4endl;
65
66 for (const auto& dirnameAng : dirsAngle)
67 {
68 G4String dirAng = directory + dirnameAng;
69 filesParticle = LoadDirectoryContents(dirAng);
70
72
73 G4double ang = (G4double)std::stod(dirnameAng);
74 angles.push_back(ang); // Store the angle for interpolation
75 G4int angleIndex = (G4int)angles.size() - 1;
76
77 std::vector<G4int> ionPIDs; // Store the ion particle ids vs. angle for interpolation
78
79 for (const auto& filePDG : filesParticle)
80 {
81 G4String filepathPDG = dirAng + '/' + filePDG;
82 G4int pid = (G4int)std::stoi(filePDG);
83
84 if (filePDG.substr((filePDG.find_last_of(".") + 1)) == "gz" && BDS::FileExists(filepathPDG))
85 {
86#ifdef USE_GZSTREAM
88 conversionFactors[angleIndex][pid] = loaderC.Load(filepathPDG, true);
89#else
90 throw BDSException(__METHOD_NAME__, "Compressed file loading - but BDSIM not compiled with ZLIB.");
91#endif
92 }
93 else if (BDS::FileExists(filepathPDG))
94 {conversionFactors[angleIndex][pid] = loader.Load(filepathPDG, true);}
95
96 if (pid > 1e7)
97 {ionPIDs.push_back(pid);}
98 }
99 ionParticleIDs[angleIndex] = ionPIDs;
100 }
101
102 G4cout << "Scorer \"" << GetName() << " Loaded data for " << angles.size() << " angles: [";
103 for (auto a : angles)
104 {
105 G4cout << a;
106 if (a == angles.back())
107 {break;}
108 G4cout << ", ";
109 }
110 G4cout << "]*rad" << G4endl;
111}
112
113BDSPSPopulationScaled::~BDSPSPopulationScaled()
114{
115 for (const auto& af : conversionFactors)
116 {
117 for (auto cf : af.second)
118 {delete cf.second;}
119 }
120}
121
122void BDSPSPopulationScaled::Initialize(G4HCofThisEvent* HCE)
123{
124 EvtMap = new G4THitsMap<G4double>(detector->GetName(), GetName());
125 if (HCID < 0)
126 {HCID = GetCollectionID(0);}
127
128 HCE->AddHitsCollection(HCID, EvtMap);
129}
130
131void BDSPSPopulationScaled::EndOfEvent(G4HCofThisEvent* /*HEC*/)
132{
133 fCellTrackLogger.clear();
134}
135
136void BDSPSPopulationScaled::clear()
137{
138 EvtMap->clear();
139 fCellTrackLogger.clear();
140}
141
142G4bool BDSPSPopulationScaled::ProcessHits(G4Step* aStep, G4TouchableHistory*)
143{
144 G4double stepLength = aStep->GetStepLength();
145
146 if (!BDS::IsFinite(stepLength))
147 {return false;}
148
149 G4double radiationQuantity = 0;
150 G4int index = GetIndex(aStep);
151 G4TrackLogger& tlog = fCellTrackLogger[index];
152
153 if (tlog.FirstEnterance(aStep->GetTrack()->GetTrackID()))
154 {
155 const G4VTouchable* touchable = aStep->GetPreStepPoint()->GetTouchable();
156 auto rot = touchable->GetRotation();
157 G4ThreeVector unitZ = G4ThreeVector(0, 0, 1);
158 G4ThreeVector blmUnitZ = unitZ.transform(*rot);
159
160 auto momDirection = aStep->GetPreStepPoint()->GetMomentumDirection();
161
162 // The angle used for the lookup is sign-independent and spans the range 0 to pi/2
163 G4double angle = std::fmod(std::abs(momDirection.angle(blmUnitZ)), CLHEP::pi);
164 if (angle > CLHEP::pi/2.)
165 {angle = CLHEP::pi - angle;}
166
167 G4double kineticEnergy = aStep->GetPreStepPoint()->GetKineticEnergy();
168 G4double weight = aStep->GetPreStepPoint()->GetWeight();
169 G4double factor = GetConversionFactor(aStep->GetTrack()->GetDefinition()->GetPDGEncoding(),
170 kineticEnergy,
171 angle);
172 radiationQuantity = weight * factor;
173
174 EvtMap->add(index, radiationQuantity);
175 }
176
177 return true;
178}
179
180G4double BDSPSPopulationScaled::GetConversionFactor(G4int particleID,
181 G4double kineticEnergy,
182 G4double angle) const
183{
184 G4double angleNearestIndex = NearestNeighbourAngleIndex(angles, angle);
185
186 if (angleNearestIndex < 0)
187 {return 0;}
188
189 std::map<G4int, G4PhysicsVector*> conversionFactorsPart;
190 auto searchAngle = conversionFactors.find(angleNearestIndex);
191 if (searchAngle != conversionFactors.end())
192 {conversionFactorsPart = searchAngle->second;}
193 else
194 {return 0;}
195
196 if (particleID < 1e7)
197 {
198 auto search = conversionFactorsPart.find(particleID);
199 if (search != conversionFactorsPart.end())
200 {return search->second->Value(kineticEnergy);}
201 else
202 {return 0;}
203 }
204 else
205 {
206 // Get the nearest neighbour ion particle ID based on the ion Z
207 G4int particleIDNearest = NearestNeighbourIonPID(ionParticleIDs.find(angleNearestIndex)->second, particleID);
208 if (particleIDNearest < 0)
209 {return 0;}
210
211 // Get the ion Z in order to normalise the kinetic energy for table look-up
212 G4double nearestIonZ = (G4double)GetZFromParticleID(particleID);
213
214 auto searchIon = conversionFactorsPart.find(particleIDNearest);
215 if (searchIon != conversionFactorsPart.end())
216 {return searchIon->second->Value(kineticEnergy / nearestIonZ);}
217 else
218 {return 0;}
219 }
220}
221
222std::vector<G4String> BDSPSPopulationScaled::LoadDirectoryContents(const G4String& dirname)
223{
224 std::vector<G4String> contents;
225
226 struct dirent* entry = nullptr;
227 DIR* dp = nullptr;
228
229 dp = opendir(dirname.c_str());
230 if (dp == nullptr)
231 {throw BDSException(__METHOD_NAME__, "Cannot open directory " + dirname);}
232 else
233 {
234 while ((entry = readdir(dp)) != nullptr)
235 {
236 std::string name_string(entry->d_name);
237 if (name_string.front() != '.')
238 {contents.emplace_back(name_string);}
239 }
240 }
241 closedir(dp);
242 return contents;
243}
244
245G4int BDSPSPopulationScaled::NearestNeighbourAngleIndex(const std::vector<G4double>& vec, G4double value) const
246{
247 if (vec.empty())
248 {return -1;}
249
250 G4double nearestNeighbourAngle;
251
252 auto const it = std::upper_bound(vec.begin(), vec.end(), value) - 1;
253 if (it == vec.end())
254 {nearestNeighbourAngle = vec.back();}
255 else
256 {nearestNeighbourAngle = *it;}
257
258 auto const itr = std::find(vec.begin(), vec.end(), nearestNeighbourAngle);
259 auto index = (G4int) std::distance(vec.begin(), itr);
260
261 return index;
262}
263
264G4int BDSPSPopulationScaled::NearestNeighbourIonPID(const std::vector<G4int>& vec, G4int value) const
265{
266 // Make a vector of the ion Z for all ion particles
267 std::vector<G4int> vecZ;
268 for (auto pid: vec)
269 {vecZ.push_back(GetZFromParticleID(pid));}
270
271 if (vecZ.empty())
272 {return -1;}
273
274 G4int nearestNeighbourZ;
275 // Perform nearest neighbour interpolation on the ion Z
276 auto const it = std::upper_bound(vecZ.begin(), vecZ.end(), GetZFromParticleID(value)) - 1;
277 if (it == vecZ.end())
278 {nearestNeighbourZ = vecZ.back();}
279 else
280 {nearestNeighbourZ = *it;}
281
282 // Find the index of the nearest neighbour Z - used to look up the full particle ID
283 auto const itr = std::find(vecZ.begin(), vecZ.end(), nearestNeighbourZ);
284 int index = (int)std::distance(vecZ.begin(), itr);
285
286 return vec.at(index);
287}
288
289G4int BDSPSPopulationScaled::GetZFromParticleID(G4int particleID) const
290{
291 G4int Z = 0;
292 G4int A = 0;
293 G4double E = 0.0;
294 G4int lvl = 0;
295 G4IonTable::GetNucleusByEncoding(particleID, Z, A, E, lvl);
296 return Z;
297}
298
299void BDSPSPopulationScaled::PrintAll()
300{
301 G4cout << " MultiFunctionalDet " << detector->GetName() << G4endl;
302 G4cout << " PrimitiveScorer " << GetName() << G4endl;
303 G4cout << " Number of entries " << EvtMap->entries() << G4endl;
304 auto itr = EvtMap->GetMap()->begin();
305 for (; itr != EvtMap->GetMap()->end(); itr++)
306 {
307 G4cout << " copy no.: " << itr->first
308 << " population: " << *(itr->second) / GetUnitValue()
309 << " [quantity]"
310 << G4endl;
311 }
312}
General exception with possible name of object and message.
Definition: BDSException.hh:35
Primitive scorer for population in a volume with a conversion factor based on angle and kinetic energ...
BDSPSPopulationScaled(const G4String &name, G4int depth=0)
Loader for scoring conversion tables as function of energy.
G4PhysicsVector * Load(const G4String &fileName, G4bool silent=false)
Load the file.
G4String GetFullPath(G4String filename, bool excludeNameFromPath=false, bool useCWDForPrefix=false)
G4bool FileExists(const G4String &filename)
Checks if filename exists.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
const char * GetName(int)
Name of element.
Definition: python.cc:38