CbmRoot
Loading...
Searching...
No Matches
KfMaterialMapFactory.cxx
Go to the documentation of this file.
1/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Sergei Zharko [committer] */
4
10
12
13#include "KfMaterialMap.h"
14#include "TGeoManager.h"
15#include "TGeoMedium.h"
16#include "TGeoNavigator.h"
17#include "TGeoNode.h"
18#include "TGeoPhysicalNode.h"
19#include "TGeoVoxelFinder.h"
20
21#include <Logger.h>
22
23#include <sstream>
24#include <thread>
25
27using kf::tools::MaterialMapFactory;
28
29// ---------------------------------------------------------------------------------------------------------------------
30//
32{
33 // constructor
34
35 // save the current number of threads to restore it when the work is done
36 fNthreadsOld = gGeoManager->GetMaxThreads();
37
38 // set defaut n threads to 1
39
40 fNthreads = 1;
41
42 std::stringstream msg;
43 msg << "\n------------------------------------------------------------------------------------";
44 msg << "\n Material budget map creator";
45 msg << '\n';
46 msg << "\n !! To run it with the full speed, set: \"export OMP_NUM_THREADS=<n CPUs>\" before running !!";
47 msg << '\n';
48 // if possible, read the allowed n threads from the environment variable
49 {
50 const char* env = std::getenv("OMP_NUM_THREADS");
51 if (env) {
52 fNthreads = TString(env).Atoi();
53 msg << "\n found environment variable OMP_NUM_THREADS = \"" << env << "\", read as integer: " << fNthreads;
54 }
55 }
56
57 // ensure that at least one thread is set
58
59 if (fNthreads < 1) {
60 fNthreads = 1;
61 }
62
63 msg << "\n Maps will be created using " << fNthreads << " CPU threads";
64 msg << '\n';
65 msg << "\n It might crash because of a ROOT bug. In this case do one of the following: ";
66 msg << '\n';
67 msg << "\n - fix illegal overlaps in the geometry via gGeoManager->CheckOverlaps()";
68 msg << "\n - run with CbmL1::SetSafeMaterialInitialization() option. ";
69 msg << "\n It fixes the crash, but slows down the initialization significantly.";
70 msg << "\n - manually disable voxel finders for problematic volumes in CaToolsMaterialMapFactory.cxx";
71 msg << '\n';
72 msg << "\n------------------------------------------------------------------------------------";
73 LOG_IF(info, fVerbose > 0) << msg.str();
74
76
77 //
78 // It looks like TGeoVoxelFinder contains a bug and therefore sometimes produces a crash.
79 //
80 // Currently, the crash only happens on some of TGeoVolumeAssembly volumes
81 // and only when an (inproper?) alignment matrices are applied.
82 //
83 // Here we try to catch this case and disable those finders.
84 //
85 // Disabling all the voxel finders leads to a very long initialization, try to avoid it!
86 //
87 // TODO: make a bug report to the ROOT team; remove the following code once the bug is fixed.
88 //
89
90 if (fDoSafeInitialization) { // disable all voxel finders
91 TObjArray* volumes = gGeoManager->GetListOfVolumes();
92 if (volumes) {
93 for (int iv = 0; iv < volumes->GetEntriesFast(); iv++) {
94 TGeoVolume* vol = dynamic_cast<TGeoVolume*>(volumes->At(iv));
95 if (!vol) {
96 continue;
97 }
98 TGeoVoxelFinder* finder = vol->GetVoxels();
99 if (finder) {
100 finder->SetInvalid();
101 }
102 }
103 }
104 }
105 else { // disable some voxel finders in some cases
106
107 // check if any alignment was applied
108
109 bool isAlignmentApplied = false;
110 {
111 TObjArray* nodes = gGeoManager->GetListOfPhysicalNodes();
112 if (nodes) {
113 for (int in = 0; in < nodes->GetEntriesFast(); in++) {
114 TGeoPhysicalNode* node = dynamic_cast<TGeoPhysicalNode*>(nodes->At(in));
115 if (!node) continue;
116 if (node->IsAligned()) {
117 isAlignmentApplied = true;
118 break;
119 }
120 }
121 }
122 }
123
124 // disable potentially problematic voxel finders
125
126 if (isAlignmentApplied) {
127 TObjArray* volumes = gGeoManager->GetListOfVolumes();
128 if (volumes) {
129 for (int iv = 0; iv < volumes->GetEntriesFast(); iv++) {
130 TGeoVolumeAssembly* vol = dynamic_cast<TGeoVolumeAssembly*>(volumes->At(iv));
131 if (!vol) {
132 continue;
133 }
134 TGeoVoxelFinder* finder = vol->GetVoxels();
135 if (finder) {
136 finder->SetInvalid();
137 }
138 }
139 }
140 }
141 }
142}
143
144// ---------------------------------------------------------------------------------------------------------------------
145//
147{
148 // destructor
149
151
152 // delete the navigators, created in this class
153
154 for (auto i = fNavigators.begin(); i != fNavigators.end(); i++) {
155 gGeoManager->RemoveNavigator(*i);
156 }
157
158 // once TGeoManager is swithched in multithreaded mode, there is no way to make it non-mltithreaded again
159 // therefore we should set SetMaxThreads( >=0 )
160
161 if (fNthreadsOld > 0) {
162 gGeoManager->SetMaxThreads(fNthreadsOld);
163 }
164 else {
165 gGeoManager->SetMaxThreads(1);
166 }
167
168 // ensure that the default navigator exists
169
171}
172
173// ---------------------------------------------------------------------------------------------------------------------
174//
176{
177 // Get navigator for current thread, create it if it doesnt exist.
178 // Produce an error when anything goes wrong
179
180 TGeoNavigator* navi = gGeoManager->GetCurrentNavigator();
181 if (!navi) {
182
183 navi = gGeoManager->AddNavigator();
184
185 if (iThread >= 0) {
186 fNavigators.push_back(navi);
187 }
188
189 if (navi != gGeoManager->GetCurrentNavigator()) {
190 LOG(fatal) << "ca::tools::MaterialMapFactory: unexpected behavior of TGeoManager::AddNavigator() !!";
191 }
192 }
193
194 if (!navi) {
195 LOG(fatal) << "ca::tools::MaterialMapFactory: can not find / create TGeoNavigator for thread " << iThread;
196 }
197
198 return navi;
199}
200
201// ---------------------------------------------------------------------------------------------------------------------
202//
204{
205 // clean up thread data in TGeoManager
206
207 gGeoManager->ClearThreadsMap();
208
209 // create a default navigator for multithreaded mode
210 // otherwise gGeoManager->GetCurrentNavigator() returns nullptr that might produces crashes in other code
211
213}
214
216{
217 // (re)initialise the number of threads in TGeoManager
218
219 // reserve enough threads in TGeoManager
220 if (fNthreads > gGeoManager->GetMaxThreads()) {
221 gGeoManager->SetMaxThreads(fNthreads);
222 // in case the number of threads is truncated by TGeoManager (must not happen)
223 fNthreads = gGeoManager->GetMaxThreads();
224 if (fNthreads < 1) {
225 fNthreads = 1;
226 }
227 }
229}
230
231// ---------------------------------------------------------------------------------------------------------------------
232//
233MaterialMap MaterialMapFactory::GenerateMaterialMap(double zRef, double zMin, double zMax, double xyMax, int nBinsDim)
234{
236 if (fTargetZ + 0.05 >= zRef) {
237 LOG(warn)
238 << "kf::tools::MaterialMapFactory: the material reference z-coordinate must be at least 0.1 cm downstream "
239 "the target. Shifting the reference z-coordinate to the lower limit: target Z = "
240 << fTargetZ << ", material reference z = " << zRef;
241 }
242 zRef = std::max(zRef, fTargetZ + 0.05);
243 zMin = std::max(fTargetZ, zMin);
244 zMax = std::max(zMax, zRef + 0.05);
245 }
246
247 if (!(zMin <= zRef && zRef <= zMax)) {
248 LOG(fatal) << "kf::tools::MaterialMapFactory: the material parameters are inconsistent. It must be: zMin " << zMin
249 << " <= zRef " << zRef << " <= zMax " << zMax;
250 }
251
252 MaterialMap matBudget(nBinsDim, xyMax, zRef, zMin, zMax);
253 double binSizeX = 2. * xyMax / nBinsDim;
254 double binSizeY = 2. * xyMax / nBinsDim;
255
256 // call it every time. for the case the number of threads was reset in some other code
257 InitThreads();
258
259 long nThreadCrosses[fNthreads];
260 long nThreadRays[fNthreads];
261 long nThreadRaysExpected = nBinsDim * nBinsDim * fNraysBinPerDim * fNraysBinPerDim / fNthreads;
262 for (int i = 0; i < fNthreads; i++) {
263 nThreadCrosses[i] = 0;
264 nThreadRays[i] = 0;
265 }
266
267 LOG_IF(info, fVerbose > -1) << "kf::tools::MaterialMapFactory: Generate material map using " << fNthreads
268 << " threads..";
269
270 auto naviThread = [&](int iThread) {
271 // create a navigator for this thread
272 TGeoNavigator* navi = GetCurrentNavigator(iThread);
273
274 for (int iBinX = iThread; iBinX < nBinsDim; iBinX += fNthreads) {
275 for (int iBinY = 0; iBinY < nBinsDim; ++iBinY) {
276 double radThick = 0.;
277 int nRays = 0;
278 double d = 1. / (fNraysBinPerDim);
279 for (int iStepX = 0; iStepX < fNraysBinPerDim; iStepX++) {
280 for (int iStepY = 0; iStepY < fNraysBinPerDim; iStepY++) {
281
282 double rayRadThick = 0.;
283 // ray position at zRef
284 double rayX = -xyMax + (iBinX + d * (iStepX + 0.5)) * binSizeX;
285 double rayY = -xyMax + (iBinY + d * (iStepY + 0.5)) * binSizeY;
286
287 // ray slopes
288 double tx = 0.;
289 double ty = 0.;
290 double t = 1.;
292 tx = rayX / (zRef - fTargetZ);
293 ty = rayY / (zRef - fTargetZ);
294 t = sqrt(1. + tx * tx + ty * ty);
295 }
296
297 // ray position at zMin
298 double z = zMin;
299 double x = rayX + tx * (z - zRef);
300 double y = rayY + ty * (z - zRef);
301 LOG_IF(info, fVerbose > 2) << "ray at " << x << " " << y << " " << z << " zMin " << zMin << " zMax "
302 << zMax;
303
304 TGeoNode* node = navi->InitTrack(x, y, z, tx / t, ty / t, 1. / t);
305
306 bool doContinue = 1;
307 for (int iStep = 0; doContinue; iStep++) {
308
309 if (!node) {
310 // may happen when tracing outside of the CBM volume -> produce a warning
311 LOG(warning) << "kf::tools::MaterialMapFactory: TGeoNavigator can not find the geo node";
312 break;
313 }
314
315 TGeoMedium* medium = node->GetMedium();
316 if (!medium) {
317 LOG(fatal) << "kf::tools::MaterialMapFactory: TGeoNavigator can not find the geo medium";
318 }
319
320 TGeoMaterial* material = medium->GetMaterial();
321 if (!material) {
322 LOG(fatal) << "kf::tools::MaterialMapFactory: TGeoNavigator can not find the geo material";
323 }
324
325 double radLen = material->GetRadLen();
326
327 std::string nodeName(node->GetName());
328 // exclude the beampipe !!!
329 if ((nodeName.find("pipe") != std::string::npos) && (nodeName.find("pipe1") == std::string::npos)
330 && (nodeName.find("pipe2") == std::string::npos)) {
331 radLen = 1.e30;
332 }
333
334 if (radLen < kMinRadLength) { // 0.5612 is rad. length of Lead at normal density
335 LOG(fatal) << "kf::tools::MaterialMapFactory: failed assertion! "
336 << " TGeoNavigator returns a suspicious material with an unrealistic "
337 << "radiation length of " << radLen << " cm. "
338 << " The allowed minimum is 0.56 cm (Lead has 0.5612 cm). Check your material! "
339 "Modify this assertion if necessary."
340 << " TGeoNode \"" << node->GetName() << "\", TGeoMedium \"" << medium->GetName()
341 << "\", TGeoMaterial \"" << material->GetName() << "\"";
342 }
343
344 // move navi to the next material border
345 node = navi->FindNextBoundaryAndStep(); //5., kTRUE);
346 nThreadCrosses[iThread]++;
347
348 LOG_IF(info, fVerbose > 2) << " RL " << radLen << " next pt " << navi->GetCurrentPoint()[0] << " "
349 << navi->GetCurrentPoint()[1] << " " << navi->GetCurrentPoint()[2];
350
351 double zNew = navi->GetCurrentPoint()[2];
352 if (zNew >= zMax) {
353 zNew = zMax;
354 doContinue = 0;
355 }
356
357 //if (zNew < z) {
358 //LOG(info) << " z curr " << z << " z new " << zNew << " dz " << zNew - z ; }
359 double dz = zNew - z;
360 if (dz < 0.) {
361 if (iStep == 0 && dz > -1.e-8) {
362 // when the ray starts exactly at the volume border, first dz might become negative,
363 // probably due to some roundoff errors. So we let it be a bit negative for the first intersection.
364 dz = 0.;
365 }
366 else {
367 LOG(fatal)
368 << "kf::tools::MaterialMapFactory: TGeoNavigator propagates the ray upstream. Something is wrong."
369 << " z old " << z << " z new " << zNew << ", dz " << dz;
370 }
371 }
372 double thick = dz / radLen;
373 rayRadThick += thick;
374 z = zNew;
375 } // end of the tracing of one ray
376
377 if (rayRadThick > 0.01) {
378 rayRadThick = 0.005;
379 }
380 radThick += rayRadThick;
381
382 nRays++;
383 nThreadRays[iThread]++;
384 if (fVerbose > 2 && nThreadRays[iThread] % 1000000 == 0) {
385 LOG(info) << "kf::tools::MaterialMapFactory: report from thread " << iThread << ": material map is "
386 << 100. * nThreadRays[iThread] / nThreadRaysExpected << " \% done";
387 }
388 }
389 }
390 radThick /= nRays;
391 LOG_IF(info, fVerbose > 2) << " radThick " << radThick;
392 //doPrint = (radThick > 0.01);
393 matBudget.SetRadThickBin(iBinX, iBinY, radThick);
394 } // iBinX
395 } // iBinY
396 };
397
398 std::vector<std::thread> threads(fNthreads);
399
400 // run n threads
401 for (int i = 0; i < fNthreads; i++) {
402 threads[i] = std::thread(naviThread, i);
403 }
404
405 // wait for the threads to finish
406 for (auto& th : threads) {
407 th.join();
408 }
409
411 long nRays = 0;
412 long nCrosses = 0;
413 for (int i = 0; i < fNthreads; i++) {
414 nRays += nThreadRays[i];
415 nCrosses += nThreadCrosses[i];
416 }
417
418 LOG_IF(info, fVerbose > 0) << "kf::tools::MaterialMapFactory: shooted " << nRays << " rays. Each ray crossed "
419 << nCrosses * 1. / nRays << " material boundaries in average";
420
421 return matBudget;
422}
Utility to generate material budget map from the TGeoNavigator representation of the Setup (implement...
friend fvec sqrt(const fvec &a)
A map of station thickness in units of radiation length (X0) to the specific point in XY plane.
void SetRadThickBin(int iBinX, int iBinY, float thickness)
Sets value of material thickness in units of X0 for a given cell of the material table.
MaterialMapFactory(int verbose=0)
Constructor from parameters.
int fNthreadsOld
number of threads in TGeoManager before the helper was created
std::vector< TGeoNavigator * > fNavigators
list of created navigators
bool fDoRadialProjection
if project rays horizontally along the Z axis (special mode)
cbm::algo::kf::MaterialMap GenerateMaterialMap(double zRef, double zMin, double zMax, double xyMax, int nBinsDim) override
Generates a material budget map.
TGeoNavigator * GetCurrentNavigator(int iThread)
Gets navigator for current thread, creates it if it does not exist.
void CleanUpThreads()
Cleans up the TGeoManager: threadIds, create a default navigator.
double fTargetZ
z of the target for the radial projection
static constexpr double kMinRadLength
Minimal radiational length allowed [cm].
void InitThreads()
Initializes the necessary amount of threads in TGeoManager.
int fNraysBinPerDim
shoot fNraysBinPerDim * fNraysBinPerDim rays in each map bin