Project
Loading...
Searching...
No Matches
MatLayerCylSet.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
13
16
17#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version
18#include "GPUCommonLogger.h"
19#include <TFile.h>
20#include <TGeoManager.h>
22#include <tbb/blocked_range.h>
23#include <tbb/enumerable_thread_specific.h>
24#include <tbb/global_control.h>
25#include <tbb/parallel_for.h>
26#include <algorithm>
27#include <chrono>
28#include <cstdlib>
29#include <vector>
30//#define _DBG_LOC_ // for local debugging only
31
32#endif // !GPUCA_ALIGPUCODE
33#undef NDEBUG
34using namespace o2::base;
35
37
38#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version
39
40//________________________________________________________________________________
41void MatLayerCylSet::addLayer(float rmin, float rmax, float zmax, float dz, float drphi)
42{
43 // add new layer checking for overlaps
45 assert(rmin < rmax && zmax > 0 && dz > 0 && drphi > 0);
47 int nlr = getNLayers();
48 if (!nlr) {
49 // book local storage
50 auto sz = sizeof(MatLayerCylSetLayout);
53 mFlatBufferSize = sz;
54 //--------------????
55 get()->mRMin = 1.e99;
56 get()->mRMax = 0.;
57 }
58
59 for (int il = 0; il < nlr; il++) {
60 const auto& lr = getLayer(il);
61 if (lr.getRMax() > rmin && rmax > lr.getRMin()) {
62 LOG(fatal) << "new layer overlaps with layer " << il;
63 }
64 }
65 auto* oldLayers = o2::gpu::FlatObject::resizeArray(get()->mLayers, nlr, nlr + 1);
66 // dynamyc buffers of old layers were used in new ones, detach them
67 for (int i = nlr; i--;) {
68 oldLayers[i].clearInternalBufferPtr();
69 }
70 delete[] oldLayers;
71 get()->mLayers[nlr].initSegmentation(rmin, rmax, zmax, dz, drphi);
72 get()->mNLayers++;
73 get()->mRMin = get()->mRMin > rmin ? rmin : get()->mRMin;
74 get()->mRMax = get()->mRMax < rmax ? rmax : get()->mRMax;
75 get()->mZMax = get()->mZMax < zmax ? zmax : get()->mZMax;
76 get()->mRMin2 = get()->mRMin * get()->mRMin;
77 get()->mRMax2 = get()->mRMax * get()->mRMax;
78}
79
80//________________________________________________________________________________
82{
84 const char* env = std::getenv("NTHREADS_MATBUD");
85 if (!env) {
86 return 1;
87 }
88 int n = std::atoi(env);
89 if (n < 1) {
90 LOG(warning) << "Ignoring invalid NTHREADS_MATBUD=" << env;
91 return 1;
92 }
93 return n;
94}
95
96//________________________________________________________________________________
97void MatLayerCylSet::populateFromTGeo(int ntrPerCell, int nThreads, MatbudGeomBackend backend)
98{
101 assert(mConstructionMask == InProgress);
102
104 LOG(fatal) << "MatbudGeomBackend::VECGEOM requested but O2 was built without VecGeom support (TGeo2VecGeom not found at configure time)";
105 }
106
107 int nlr = getNLayers();
108 if (!nlr) {
109 LOG(error) << "The LUT is not yet initialized";
110 return;
111 }
112 if (get()->mR2Intervals) {
113 LOG(error) << "The LUT is already populated";
114 return;
115 }
116
117 if (nThreads < 0) {
118 nThreads = getNThreadsFromEnv();
119 }
120
121 using Clock = std::chrono::steady_clock;
122 auto seconds = [](Clock::time_point a, Clock::time_point b) {
123 return std::chrono::duration<double>(b - a).count();
124 };
125
126 if (nThreads <= 1) {
127 for (int i = 0; i < nlr; i++) {
128 LOG(info) << "Populating with " << ntrPerCell << " trials Lr " << i;
129 get()->mLayers[i].print();
130 }
131 const auto tSetupStart = Clock::now();
132#ifdef O2_WITH_VECGEOM
133 if (backend == MatbudGeomBackend::VECGEOM) {
134 // Trigger the lazy VecGeom world build/BVH init here so it counts as "setup" below,
135 // not as fill time for whichever cell happens first.
136 GeometryManager::vecGeomMaterialBudget(0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
137 }
138#endif
139 const auto tFillStart = Clock::now();
140 for (int i = 0; i < nlr; i++) {
141 get()->mLayers[i].populateFromTGeo(ntrPerCell, backend);
142 }
143 const auto tFillEnd = Clock::now();
145 LOG(info) << "LUT fill: 1 thread, setup " << seconds(tSetupStart, tFillStart)
146 << " s, cells " << seconds(tFillStart, tFillEnd) << " s";
147 return;
148 }
149
150 // Cells of all layers form one flat index range so that the load is balanced across
151 // threads even though layers differ a lot in cell count. layerOffsets[i] is the first
152 // flat index of layer i; a binary search maps a flat index back to (layer, iz, iphi).
153 std::vector<size_t> layerOffsets(nlr + 1, 0);
154 for (int i = 0; i < nlr; i++) {
155 LOG(info) << "Queuing " << ntrPerCell << " trials Lr " << i;
156 get()->mLayers[i].print();
157 const auto& lr = get()->mLayers[i];
158 layerOffsets[i + 1] = layerOffsets[i] + size_t(lr.getNZBins()) * lr.getNPhiBins();
159 }
160 const size_t totalCells = layerOffsets[nlr];
161
162 const auto tSetupStart = Clock::now();
163
164 auto fillRange = [this, ntrPerCell, backend, &layerOffsets](const tbb::blocked_range<size_t>& range, TGeoNavigator* nav) {
165 for (size_t idx = range.begin(); idx != range.end(); ++idx) {
166 auto it = std::upper_bound(layerOffsets.begin(), layerOffsets.end(), idx);
167 const int layerIdx = int(std::distance(layerOffsets.begin(), it)) - 1;
168 const size_t cellInLayer = idx - layerOffsets[layerIdx];
169 auto& layer = this->get()->mLayers[layerIdx];
170 const int nphi = layer.getNPhiBins();
171 layer.populateFromTGeo(int(cellInLayer % nphi), int(cellInLayer / nphi), ntrPerCell, nav, backend);
172 }
173 };
174
175 Clock::time_point tFillStart, tFillEnd;
176 if (backend == MatbudGeomBackend::ROOT) {
177 // TGeo has to be told that several threads will navigate it, and each thread needs its own
178 // navigator. SetMaxThreads() is one-way -- TGeoManager has no API to return to
179 // single-threaded mode -- so we do not pretend to restore it; that is harmless because
180 // meanMaterialBudget() decides whether to lock from its own argument, not from this global.
181 // The navigators we book are ours, though, so those we do give back.
182 gGeoManager->SetMaxThreads(nThreads);
183
184 tbb::enumerable_thread_specific<TGeoNavigator*> threadNavigators(
185 []() { return gGeoManager->AddNavigator(); });
186
187 tFillStart = Clock::now();
188 {
189 tbb::global_control threadControl(tbb::global_control::max_allowed_parallelism, nThreads);
190 tbb::parallel_for(tbb::blocked_range<size_t>(0, totalCells),
191 [&fillRange, &threadNavigators](const tbb::blocked_range<size_t>& range) {
192 fillRange(range, threadNavigators.local());
193 });
194 }
195 tFillEnd = Clock::now();
196
197 for (TGeoNavigator* nav : threadNavigators) {
198 gGeoManager->RemoveNavigator(nav);
199 }
200 } else {
201 // VecGeom navigation needs no per-thread navigator bookkeeping. Trigger the lazy world
202 // build/BVH init before tFillStart so it counts as "setup", not fill time.
203#ifdef O2_WITH_VECGEOM
204 GeometryManager::vecGeomMaterialBudget(0.f, 0.f, 0.f, 0.f, 0.f, 1.f);
205#endif
206 tFillStart = Clock::now();
207 {
208 tbb::global_control threadControl(tbb::global_control::max_allowed_parallelism, nThreads);
209 tbb::parallel_for(tbb::blocked_range<size_t>(0, totalCells),
210 [&fillRange](const tbb::blocked_range<size_t>& range) {
211 fillRange(range, nullptr);
212 });
213 }
214 tFillEnd = Clock::now();
215 }
216
218 const auto tEnd = Clock::now();
219
220 // Reported separately because only the middle term scales: the setup walks every volume
221 // in the geometry (TGeoManager::SetMaxThreads) and the teardown is serial by nature.
222 LOG(info) << "LUT fill: " << nThreads << " threads, setup " << seconds(tSetupStart, tFillStart)
223 << " s, cells " << seconds(tFillStart, tFillEnd)
224 << " s, finalize " << seconds(tFillEnd, tEnd) << " s";
225}
226
227//________________________________________________________________________________
229{
230 // build layer search structures
231 assert(mConstructionMask == InProgress);
232 int nlr = getNLayers();
233 int nR2Int = 2 * (nlr + 1);
234 o2::gpu::FlatObject::resizeArray(get()->mR2Intervals, 0, nR2Int);
235 o2::gpu::FlatObject::resizeArray(get()->mInterval2LrID, 0, nR2Int);
236 get()->mR2Intervals[0] = get()->mRMin2;
237 get()->mR2Intervals[1] = getLayer(0).getRMax2();
238 get()->mInterval2LrID[0] = 0;
239 auto& nRIntervals = get()->mNRIntervals;
240 nRIntervals = 1;
241
242 for (int i = 1; i < nlr; i++) {
243 const auto& lr = getLayer(i);
244 if (o2::math_utils::sqrt(lr.getRMin2()) > o2::math_utils::sqrt(get()->mR2Intervals[nRIntervals] + Ray::Tiny)) {
245 // register gap
246 get()->mInterval2LrID[nRIntervals] = -1;
247 get()->mR2Intervals[++nRIntervals] = lr.getRMin2();
248 }
249 get()->mInterval2LrID[nRIntervals] = i;
250 get()->mR2Intervals[++nRIntervals] = lr.getRMax2();
251 }
252 delete[] o2::gpu::FlatObject::resizeArray(get()->mInterval2LrID, nR2Int, nRIntervals); // rebook with precise size
253 delete[] o2::gpu::FlatObject::resizeArray(get()->mR2Intervals, nR2Int, ++nRIntervals); // rebook with precise size
254 //
255}
256
257//________________________________________________________________________________
258void MatLayerCylSet::dumpToTree(const std::string& outName) const
259{
261
262 o2::utils::TreeStreamRedirector dump(outName.data(), "recreate");
263 for (int i = 0; i < getNLayers(); i++) {
264 const auto& lr = getLayer(i);
265 float r = 0.5 * (lr.getRMin() + lr.getRMax());
266 // per cell dump
267 int nphib = lr.getNPhiBins();
268 for (int ip = 0; ip < nphib; ip++) {
269 float phi = 0.5 * (lr.getPhiBinMin(ip) + lr.getPhiBinMax(ip));
270 float sn, cs;
271 int ips = lr.phiBin2Slice(ip);
272 char merge = 0; // not mergeable
273 if (ip + 1 < nphib) {
274 int ips1 = lr.phiBin2Slice(ip + 1);
275 merge = ips == ips1 ? -1 : lr.canMergePhiSlices(ips, ips1); // -1 for already merged
276 } else {
277 merge = -2; // last one
278 }
279 o2::math_utils::sincos(phi, sn, cs);
280 float x = r * cs, y = r * sn;
281 for (int iz = 0; iz < lr.getNZBins(); iz++) {
282 float z = 0.5 * (lr.getZBinMin(iz) + lr.getZBinMax(iz));
283 auto cell = lr.getCellPhiBin(ip, iz);
284 dump << "cell"
285 << "ilr=" << i << "r=" << r << "phi=" << phi << "x=" << x << "y=" << y << "z=" << z << "ip=" << ip << "ips=" << ips << "iz=" << iz
286 << "mrgnxt=" << merge << "val=" << cell << "\n";
287 }
288 }
289 //
290 // statistics per layer
291 MatCell mean, rms;
292 lr.getMeanRMS(mean, rms);
293 dump << "lay"
294 << "ilr=" << i << "r=" << r << "mean=" << mean << "rms=" << rms << "\n";
295 }
296}
297
298//________________________________________________________________________________
299void MatLayerCylSet::writeToFile(const std::string& outFName)
300{
302
303 TFile outf(outFName.data(), "recreate");
304 if (outf.IsZombie()) {
305 return;
306 }
307 outf.WriteObjectAny(this, Class(), "ccdb_object");
308 outf.Close();
309}
310
312{
314 LOG(info) << "Layer voxel already initialized; Aborting";
315 return;
316 }
317 LOG(info) << "Initializing voxel layer lookup";
318 // do some check if voxels are dimensioned correctly
319 if (LayerRMax < get()->mRMax) {
320 LOG(fatal) << "Cannot initialized layer voxel lookup due to dimension problem (fix constants in MatLayerCylSet.h)";
321 }
322 // the top bit of an entry carries the ambiguity flag, so the interval index has one bit less
323 if (get()->mNRIntervals > VoxelSegmentMask) {
324 LOG(fatal) << "Too many R intervals (" << get()->mNRIntervals << ") to pack into a layer voxel lookup entry";
325 }
326 for (int voxel = 0; voxel < NumVoxels; ++voxel) {
327 // check the 2 extremes of this voxel "covering"
328 const auto lowerR = voxelRMin(voxel);
329 const auto upperR = voxelRMax(voxel);
330 const auto lowerSegment = searchSegment(lowerR * lowerR);
331 const auto upperSegment = searchSegment(upperR * upperR);
332 mLayerVoxelLU[voxel] = uint16_t(lowerSegment) | (lowerSegment != upperSegment ? VoxelAmbiguousBit : uint16_t{0});
333 }
335}
336
337//________________________________________________________________________________
338MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName)
339{
340 TFile inpf(inpFName.data());
341 if (inpf.IsZombie()) {
342 LOG(error) << "Failed to open input file " << inpFName;
343 return nullptr;
344 }
345 MatLayerCylSet* mb = reinterpret_cast<MatLayerCylSet*>(inpf.GetObjectChecked("ccdb_object", Class()));
346 if (!mb && !(mb = reinterpret_cast<MatLayerCylSet*>(inpf.GetObjectChecked("MatBud", Class())))) { // for old objects
347 LOG(error) << "Failed to load mat.LUT from " << inpFName;
348 return nullptr;
349 }
350 auto rptr = rectifyPtrFromFile(mb);
351 return rptr;
352}
353
354//________________________________________________________________________________
356{
357 // rectify object loaded from file
358 if (ptr && !ptr->get()) {
359 ptr->fixPointers();
360 }
361 ptr->initLayerVoxelLU();
362 return ptr;
363}
364
365//________________________________________________________________________________
367{
368 // merge similar (whose relative budget does not differ within maxRelDiff) phi slices
369 assert(mConstructionMask == InProgress);
370 for (int i = getNLayers(); i--;) {
371 get()->mLayers[i].optimizePhiSlices(maxRelDiff);
372 }
373 // flatten(); // RS: TODO
374}
375
376//________________________________________________________________________________
378{
380 if (!get()) {
381 printf("Not initialized yet\n");
382 return;
383 }
385 LOG(warning) << "Object is not yet flattened";
386 }
387 for (int i = 0; i < getNLayers(); i++) {
388 printf("#%3d | ", i);
390 }
391 printf("%.2f < R < %.2f %d layers with total size %.2f MB\n", getRMin(), getRMax(), getNLayers(),
392 float(getFlatBufferSize()) / 1024 / 1024);
393}
394
395//________________________________________________________________________________
396void MatLayerCylSet::scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0, bool _rho)
397{
398 lrFrom = std::max(0, std::min(lrFrom, get()->mNLayers - 1));
399 lrTo = std::max(0, std::min(lrTo, get()->mNLayers - 1));
400 int dir = lrFrom >= lrTo ? -1 : 1;
401 lrTo += dir;
402 for (int i = lrFrom; i != lrTo; i += dir) {
403 get()->mLayers[i].scale(factor, _x2x0, _rho);
404 }
405}
406
407//________________________________________________________________________________
408void MatLayerCylSet::scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0, bool _rho)
409{
410 if (rFrom > rTo) {
411 std::swap(rFrom, rTo);
412 }
413 Ray ray(std::max(getRMin(), rFrom), 0., 0., std::min(getRMax(), rTo), 0., 0.);
414 short lmin, lmax;
415 if (!getLayersRange(ray, lmin, lmax)) {
416 LOGP(warn, "No layers found for {} < r < {}", rFrom, rTo);
417 return;
418 }
419 scaleLayersByID(lmin, lmax, factor, _x2x0, _rho);
420}
421
422#endif
423
424#ifndef GPUCA_GPUCODE
425//________________________________________________________________________________
427{
428 std::size_t sz = alignSize(sizeof(MatLayerCylSetLayout), getBufferAlignmentBytes()); // hold data members
429
430 sz = alignSize(sz + get()->mNLayers * sizeof(MatLayerCyl), MatLayerCyl::getClassAlignmentBytes());
431 sz = alignSize(sz + (get()->mNRIntervals + 1) * sizeof(float), getBufferAlignmentBytes());
432 sz = alignSize(sz + get()->mNRIntervals * sizeof(int), getBufferAlignmentBytes());
433
434 for (int i = 0; i < getNLayers(); i++) {
436 }
437 return sz;
438}
439#endif // ! GPUCA_GPUCODE
440
441//_________________________________________________________________________________________________
442GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, float x1, float y1, float z1) const
443{
444 // get material budget traversed on the line between point0 and point1
445 MatBudget rval;
446 Ray ray(x0, y0, z0, x1, y1, z1);
447 short lmin, lmax; // get innermost and outermost relevant layer
448 if (ray.isTooShort() || !getLayersRange(ray, lmin, lmax)) {
449 rval.length = ray.getDist();
450 return rval;
451 }
452 short lrID = lmax;
453 while (lrID >= lmin) { // go from outside to inside
454 const auto& lr = getLayer(lrID);
455 int nphiSlices = lr.getNPhiSlices();
456 int nc = ray.crossLayer(lr); // determines how many crossings this ray has with this tubular layer
457 for (int ic = nc; ic--;) {
458 float cross1, cross2;
459 ray.getCrossParams(ic, cross1, cross2); // tmax,tmin of crossing the layer
460
461 auto phi0 = ray.getPhi(cross1), phi1 = ray.getPhi(cross2), dPhi = phi0 - phi1;
462 auto phiID = lr.getPhiSliceID(phi0), phiIDLast = lr.getPhiSliceID(phi1);
463 // account for eventual wrapping around 0
464 if (dPhi > 0.f) {
465 if (dPhi > o2::constants::math::PI) { // wraps around phi=0
466 phiIDLast += nphiSlices;
467 }
468 } else {
469 if (dPhi < -o2::constants::math::PI) { // wraps around phi=0
470 phiID += nphiSlices;
471 }
472 }
473
474 int stepPhiID = phiID > phiIDLast ? -1 : 1;
475 bool checkMorePhi = true;
476 auto tStartPhi = cross1, tEndPhi = 0.f;
477 do {
478 // get the path in the current phi slice
479 if (phiID == phiIDLast) {
480 tEndPhi = cross2;
481 checkMorePhi = false;
482 } else { // last phi slice still not reached
483 const int boundaryPhiID = stepPhiID > 0 ? phiID + 1 : phiID;
484 // phiID may be offset by one revolution to handle wrapping, but never by more.
485 const int wrappedBoundaryPhiID = boundaryPhiID < nphiSlices ? boundaryPhiID : boundaryPhiID - nphiSlices;
486 tEndPhi = ray.crossRadial(lr, wrappedBoundaryPhiID);
487 if (tEndPhi == Ray::InvalidT) {
488 break; // ray parallel to radial line, abandon check for phi bin change
489 }
490 const auto tMarginPhi = 1.e-6f + 1.e-5f * (cross1 - cross2);
491 // if (!(tEndPhi >= cross2 - tMarginPhi) | !(tEndPhi <= cross1 + tMarginPhi)) { // use non-short-circuit | to reject eventual NANs
492 if (tEndPhi < cross2 - tMarginPhi || tEndPhi > cross1 + tMarginPhi) {
493 tEndPhi = cross2;
494 checkMorePhi = false;
495 }
496 }
497 auto zID = lr.getZBinID(ray.getZ(tStartPhi));
498 auto zIDLast = lr.getZBinID(ray.getZ(tEndPhi));
499 const int wrappedPhiID = phiID < nphiSlices ? phiID : phiID - nphiSlices;
500 const auto* cellRow = lr.getCellRow(wrappedPhiID);
501 // check if Zbins are crossed
502
503#ifdef _DBG_LOC_
504 printf("-- Zdiff (%3d : %3d) mode: t: %+e %+e\n", zID, zIDLast, tStartPhi, tEndPhi);
505#endif
506
507 if (zID != zIDLast) {
508 auto stepZID = zID < zIDLast ? 1 : -1;
509 bool checkMoreZ = true;
510 auto tStartZ = tStartPhi, tEndZ = 0.f;
511 do {
512 if (zID == zIDLast) {
513 tEndZ = tEndPhi;
514 checkMoreZ = false;
515 } else {
516 tEndZ = ray.crossZ(lr.getZBinMin(stepZID > 0 ? zID + 1 : zID));
517 if (tEndZ == Ray::InvalidT) { // track normal to Z axis, abandon Zbin change test
518 break;
519 }
520 }
521 // account materials of this step
522 float step = tEndZ > tStartZ ? tEndZ - tStartZ : tStartZ - tEndZ; // the real step is ray.getDist(tEnd-tStart), will rescale all later
523 const auto& cell = cellRow[zID];
524 rval.meanRho += cell.meanRho * step;
525 rval.meanX2X0 += cell.meanX2X0 * step;
526 rval.length += step;
527
528#ifdef _DBG_LOC_
529 float pos0[3] = {ray.getPos(tStartZ, 0), ray.getPos(tStartZ, 1), ray.getPos(tStartZ, 2)};
530 float pos1[3] = {ray.getPos(tEndZ, 0), ray.getPos(tEndZ, 1), ray.getPos(tEndZ, 2)};
531 printf(
532 "Lr#%3d / cross#%d : account %f<t<%f at phiSlice %d | Zbin: %3d (%3d) |[%+e %+e +%e]:[%+e %+e %+e] "
533 "Step: %.3e StrpCor: %.3e\n",
534 lrID, ic, tEndZ, tStartZ, wrappedPhiID, zID, zIDLast,
535 pos0[0], pos0[1], pos0[2], pos1[0], pos1[1], pos1[2], step, ray.getDist(step));
536#endif
537
538 tStartZ = tEndZ;
539 zID += stepZID;
540 } while (checkMoreZ);
541 } else {
542 float step = tEndPhi > tStartPhi ? tEndPhi - tStartPhi : tStartPhi - tEndPhi; // the real step is |ray.getDist(tEnd-tStart)|, will rescale all later
543 const auto& cell = cellRow[zID];
544 rval.meanRho += cell.meanRho * step;
545 rval.meanX2X0 += cell.meanX2X0 * step;
546 rval.length += step;
547
548#ifdef _DBG_LOC_
549 float pos0[3] = {ray.getPos(tStartPhi, 0), ray.getPos(tStartPhi, 1), ray.getPos(tStartPhi, 2)};
550 float pos1[3] = {ray.getPos(tEndPhi, 0), ray.getPos(tEndPhi, 1), ray.getPos(tEndPhi, 2)};
551 printf(
552 "Lr#%3d / cross#%d : account %f<t<%f at phiSlice %d | Zbin: %3d ----- |[%+e %+e +%e]:[%+e %+e %+e]"
553 "Step: %.3e StrpCor: %.3e\n",
554 lrID, ic, tEndPhi, tStartPhi, wrappedPhiID, zID,
555 pos0[0], pos0[1], pos0[2], pos1[0], pos1[1], pos1[2], step, ray.getDist(step));
556#endif
557 }
558 //
559 tStartPhi = tEndPhi;
560 phiID += stepPhiID;
561
562 } while (checkMorePhi);
563 }
564 lrID--;
565 } // loop over layers
566
567 if (rval.length != 0.f) {
568 rval.meanRho /= rval.length; // average
569 rval.meanX2X0 *= ray.getDist(); // normalize
570 }
571 rval.length = ray.getDist();
572
573#ifdef _DBG_LOC_
574 printf("<rho> = %e, x2X0 = %e | step = %e\n", rval.meanRho, rval.meanX2X0, rval.length);
575#endif
576 return rval;
577}
578
579//_________________________________________________________________________________________________
580GPUd() bool MatLayerCylSet::getLayersRange(const Ray& ray, short& lmin, short& lmax) const
581{
582 // get range of layers corresponding to rmin/rmax
583 //
584 lmin = lmax = -1;
585 float rmin2, rmax2;
586 ray.getMinMaxR2(rmin2, rmax2);
587
588 if (rmin2 >= getRMax2() || rmax2 <= getRMin2()) {
589 return false;
590 }
591 int lmxInt, lmnInt;
592 if (!mInitializedLayerVoxelLU) {
593 lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2;
594 lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0;
595 } else {
596 // The two lookups are independent so overlapping the pair is worth the clumsier shape.
597 const bool useMax = rmax2 < getRMax2();
598 const bool useMin = rmin2 >= getRMin2();
599 const int ixMax = useMax ? voxelIndex(rmax2) : NumVoxels - 1;
600 const int ixMin = useMin ? voxelIndex(rmin2) : 0;
601 const uint16_t eMax = mLayerVoxelLU[ixMax];
602 const uint16_t eMin = mLayerVoxelLU[ixMin];
603 lmxInt = useMax ? resolveLayerRange(rmax2, ixMax, eMax) : get()->mNRIntervals - 2;
604 lmnInt = useMin ? resolveLayerRange(rmin2, ixMin, eMin) : 0;
605 }
606
607 const auto* interval2LrID = get()->mInterval2LrID;
608 lmax = interval2LrID[lmxInt];
609 lmin = interval2LrID[lmnInt];
610 // make sure lmnInt and/or lmxInt are not in the gap
611 if (lmax < 0) {
612 lmax = interval2LrID[lmxInt - 1]; // rmax2 is in the gap, take highest layer below rmax2
613 }
614 if (lmin < 0) {
615 lmin = interval2LrID[lmnInt + 1]; // rmin2 is in the gap, take lowest layer above rmin2
616 }
617 return lmin <= lmax; // valid if both are not in the same gap
618}
619
620GPUd() int MatLayerCylSet::searchLayerFast(float r2, int low, int high) const
621{
622 // we can avoid the sqrt .. at the cost of more memory in the lookup
623 const auto index = voxelIndex(r2);
624 return resolveLayerRange(r2, index, mLayerVoxelLU[index]);
625}
626
627GPUd() int MatLayerCylSet::resolveLayerRange(float r2, int voxel, uint16_t entry) const
628{
629 const int layersfirst = entry & VoxelSegmentMask;
630 if (entry & VoxelAmbiguousBit) {
631 // Recreate the upper candidate only for the small fraction of undecided voxels
632 const auto upperR = voxelRMax(voxel);
633 const auto layerslast = searchSegment(upperR * upperR);
634 return searchSegment(r2, layersfirst, layerslast + 1);
635 }
636 return layersfirst;
637}
638
639GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const
640{
642 if (low < 0) {
643 low = 0;
644 }
645 if (high < 0) {
646 high = get()->mNRIntervals;
647 }
648 int mid = (low + high) >> 1;
649 const auto* r2Intervals = get()->mR2Intervals;
650 while (mid != low) {
651 if (val < r2Intervals[mid]) {
652 high = mid;
653 } else {
654 low = mid;
655 }
656 mid = (low + high) >> 1;
657 }
658
659 return mid;
660}
661
662#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version
663
665{
666 // make object flat: move all content to single internally allocated buffer
667 assert(mConstructionMask == InProgress);
668
669 int sz = estimateFlatBufferSize();
670 // create new internal buffer with total size and copy data
673 mFlatBufferSize = sz;
674 int nLr = getNLayers();
675
676 auto offs = alignSize(sizeof(MatLayerCylSetLayout), getBufferAlignmentBytes()); // account for the alignment
677 // move array of layer pointers to the flat array
678 auto* oldLayers = o2::gpu::FlatObject::resizeArray(get()->mLayers, nLr, nLr, (MatLayerCyl*)(mFlatBufferPtr + offs));
679 // dynamyc buffers of old layers were used in new ones, detach them
680 for (int i = nLr; i--;) {
681 oldLayers[i].clearInternalBufferPtr();
682 }
683 delete[] oldLayers;
684 offs = alignSize(offs + nLr * sizeof(MatLayerCyl), MatLayerCyl::getClassAlignmentBytes()); // account for the alignment
685
686 // move array of R2 boundaries to the flat array
687 const int nRBound = get()->mNRIntervals;
688 delete[] o2::gpu::FlatObject::resizeArray(get()->mR2Intervals, nRBound, nRBound, (float*)(mFlatBufferPtr + offs));
689 offs = alignSize(offs + nRBound * sizeof(float), getBufferAlignmentBytes()); // account for the alignment
690
691 // move array of interval -> layer ID to the flat array
692 delete[] o2::gpu::FlatObject::resizeArray(get()->mInterval2LrID, nRBound - 1, nRBound - 1, (int*)(mFlatBufferPtr + offs));
693 offs = alignSize(offs + (nRBound - 1) * sizeof(int), getBufferAlignmentBytes()); // account for the alignment
694
695 for (int il = 0; il < nLr; il++) {
696 MatLayerCyl& lr = get()->mLayers[il];
697 lr.flatten(mFlatBufferPtr + offs);
698 offs = alignSize(offs + lr.getFlatBufferSize(), getBufferAlignmentBytes()); // account for the alignment
699 }
701}
702
703//______________________________________________
704void MatLayerCylSet::moveBufferTo(char* newFlatBufferPtr)
705{
707 flatObject::moveBufferTo(newFlatBufferPtr);
709}
710#endif // !GPUCA_ALIGPUCODE
711
712#ifndef GPUCA_GPUCODE
713//______________________________________________
714void MatLayerCylSet::setFutureBufferAddress(char* futureFlatBufferPtr)
715{
718 fixPointers(mFlatBufferPtr, futureFlatBufferPtr, false); // flag that futureFlatBufferPtr is not valid yet
719 flatObject::setFutureBufferAddress(futureFlatBufferPtr);
720}
721
722//______________________________________________
723void MatLayerCylSet::setActualBufferAddress(char* actualFlatBufferPtr)
724{
727 fixPointers(actualFlatBufferPtr);
728}
729//______________________________________________
730void MatLayerCylSet::cloneFromObject(const MatLayerCylSet& obj, char* newFlatBufferPtr)
731{
733 flatObject::cloneFromObject(obj, newFlatBufferPtr);
735 // the voxel lookup lives outside the flat buffer
736 if (obj.mInitializedLayerVoxelLU) {
737 std::copy(obj.mLayerVoxelLU, obj.mLayerVoxelLU + NumVoxels, mLayerVoxelLU);
739 }
740}
741
742//______________________________________________
743void MatLayerCylSet::fixPointers(char* newBasePtr)
744{
745 // fix pointers on the internal structure of the flat buffer after retrieving it from the file
746 if (newBasePtr) {
747 mFlatBufferPtr = newBasePtr; // used to impose external pointer
748 } else {
749 mFlatBufferPtr = mFlatBufferContainer; // impose pointer after reading from file
750 }
751 auto offs = alignSize(sizeof(MatLayerCylSetLayout), getBufferAlignmentBytes()); // account for the alignment
752 char* newPtr = mFlatBufferPtr + offs; // correct pointer on MatLayerCyl*
753 char* oldPtr = reinterpret_cast<char*>(get()->mLayers); // old pointer read from the file
754 fixPointers(oldPtr, newPtr);
755}
756
757//______________________________________________
758void MatLayerCylSet::fixPointers(char* oldPtr, char* newPtr, bool newPtrValid)
759{
760 // fix pointers on the internal structure of the flat buffer after retrieving it from the file
761 auto* layPtr = get()->mLayers;
762 get()->mLayers = flatObject::relocatePointer(oldPtr, newPtr, get()->mLayers);
763 get()->mR2Intervals = flatObject::relocatePointer(oldPtr, newPtr, get()->mR2Intervals);
764 get()->mInterval2LrID = flatObject::relocatePointer(oldPtr, newPtr, get()->mInterval2LrID);
765 if (newPtrValid) {
766 layPtr = get()->mLayers;
767 }
768 for (int i = 0; i < getNLayers(); i++) {
769 layPtr[i].setFlatPointer(flatObject::relocatePointer(oldPtr, newPtr, layPtr[i].getFlatBufferPtr()));
770 layPtr[i].fixPointers(oldPtr, newPtr);
771 }
772}
773#endif // !GPUCA_GPUCODE
774
775#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version
776
777MatLayerCylSet* MatLayerCylSet::extractCopy(float rmin, float rmax, float tolerance, const MatLayerCylSet* addTo) const
778{
779 // extract layers in the covering rmin-rmax range. If addTo is provided, simply substitute its layers by those from this
780 if (addTo && addTo->getNLayers() != getNLayers()) {
781 LOGP(fatal, "addTo has {} layers, this has {}", addTo->getNLayers(), getNLayers());
782 }
783 Ray ray(std::max(getRMin(), rmin), 0., 0., std::min(getRMax(), rmax), 0., 0.);
784 short lmin, lmax;
785 if (!getLayersRange(ray, lmin, lmax)) {
786 LOGP(warn, "No layers found for {} < r < {}", rmin, rmax);
787 return nullptr;
788 }
789 LOGP(info, "Will extract layers {}:{} (out of {} layers) for {} < r < {}", lmin, lmax, getNLayers(), rmin, rmax);
790 MatLayerCylSet* copy = new MatLayerCylSet();
791 int lrCount = 0, lrCounOld = 0, lrCountTot = 0;
792 auto addLr = [copy, &lrCountTot](const MatLayerCyl& lr) {
793 float drphi = lr.getDPhi() * (lr.getRMin() + lr.getRMax()) / 2. * 0.999;
794 copy->addLayer(lr.getRMin(), lr.getRMax(), lr.getZMax(), lr.getDZ(), drphi);
795 auto& lrNew = copy->getLayer(lrCountTot++);
796 for (int iz = 0; iz < lrNew.getNZBins(); iz++) {
797 for (int ip = 0; ip < lrNew.getNPhiBins(); ip++) {
798 lrNew.getCellPhiBin(ip, iz).set(lr.getCellPhiBin(ip, iz));
799 }
800 }
801 };
802 if (addTo) {
803 for (int il = 0; il < lmin; il++) {
804 addLr(addTo->getLayer(il));
805 lrCounOld++;
806 }
807 }
808 for (int il = lmin; il <= lmax; il++) {
809 addLr(getLayer(il));
810 lrCount++;
811 }
812 if (addTo) {
813 for (int il = lmax + 1; il < getNLayers(); il++) {
814 addLr(addTo->getLayer(il));
815 lrCounOld++;
816 }
817 }
818 copy->finalizeStructures();
819 copy->optimizePhiSlices(tolerance);
820 copy->flatten();
821 LOGP(info, "Added layers {}:{} for {}<r<{} {}", lmin, lmax, rmin, rmax, fmt::format(", {} layers were transferred from additional set", lrCounOld));
822 return copy;
823}
824
825#endif
int32_t i
#define GPUd()
Declarations for the wrapper for the set of cylindrical material layers.
useful math constants
TBranch * ptr
void merge(Options const &options)
static constexpr bool isVecGeomAvailable()
void setActualBufferAddress(char *actualFlatBufferPtr)
uint16_t mLayerVoxelLU[NumVoxels]
static constexpr uint16_t VoxelAmbiguousBit
static constexpr size_t getBufferAlignmentBytes()
Gives minimal alignment in bytes required for the flat buffer.
void addLayer(float rmin, float rmax, float zmax, float dz, float drphi)
void optimizePhiSlices(float maxRelDiff=0.05)
void cloneFromObject(const MatLayerCylSet &obj, char *newFlatBufferPtr)
MatLayerCyl & getLayer(int i)
static MatLayerCylSet * loadFromFile(const std::string &inpFName="matbud.root")
MatLayerCylSet * extractCopy(float rmin, float rmax, float tol=1e-3, const MatLayerCylSet *toAdd=nullptr) const
static constexpr int NumVoxels
void print(bool data=false) const
void populateFromTGeo(int ntrPerCel=10, int nThreads=-1, MatbudGeomBackend backend=MatbudGeomBackend::ROOT)
bool mInitializedLayerVoxelLU
first interval based on known radius, plus the ambiguity flag (static dimension for easy copy to GPU)
void scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0=true, bool _rho=true)
void moveBufferTo(char *newFlatBufferPtr)
std::size_t estimateFlatBufferSize() const
GPUCA_ALIGPUCODE.
void dumpToTree(const std::string &outName="matbudTree.root") const
static constexpr uint16_t VoxelSegmentMask
static MatLayerCylSet * rectifyPtrFromFile(MatLayerCylSet *ptr)
void setFutureBufferAddress(char *futureFlatBufferPtr)
static constexpr float LayerRMax
void scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0=true, bool _rho=true)
void fixPointers(char *newPtr=nullptr)
void writeToFile(const std::string &outFName="matbud.root")
static constexpr size_t getClassAlignmentBytes()
Gives minimal alignment in bytes required for the class object.
void print(bool data=false) const
void flatten(char *newPtr)
MatCell & getCellPhiBin(int iphi, int iz)
static constexpr float InvalidT
Definition Ray.h:46
static constexpr float Tiny
Definition Ray.h:47
void setFutureBufferAddress(char *futureFlatBufferPtr)
Definition FlatObject.h:569
uint32_t mConstructionMask
mask for constructed object members, first two bytes are used by this class
Definition FlatObject.h:321
int32_t mFlatBufferSize
size of the flat buffer
Definition FlatObject.h:320
char * mFlatBufferContainer
Definition FlatObject.h:322
static T * relocatePointer(const char *oldBase, char *newBase, const T *ptr)
Relocates a pointer inside a buffer to the new buffer address.
Definition FlatObject.h:283
void moveBufferTo(char *newBufferPtr)
Definition FlatObject.h:408
static constexpr size_t alignSize(size_t sizeBytes, size_t alignmentBytes)
_______________ Generic utilities _______________________________________________
Definition FlatObject.h:275
T * resizeArray(T *&ptr, int32_t oldSize, int32_t newSize, T *newPtr=nullptr)
Definition FlatObject.h:135
void cloneFromObject(const FlatObject &obj, char *newFlatBufferPtr)
Definition FlatObject.h:385
@ InProgress
construction started: temporary memory is reserved
Definition FlatObject.h:317
@ Constructed
the object is constructed, temporary memory is released
Definition FlatObject.h:316
void dump(const std::string what, DPMAP m, int verbose)
Definition dcs-ccdb.cxx:79
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint entry
Definition glcorearb.h:5735
GLuint GLfloat GLfloat GLfloat GLfloat y1
Definition glcorearb.h:5034
GLuint GLfloat GLfloat GLfloat x1
Definition glcorearb.h:5034
GLuint index
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLint * range
Definition glcorearb.h:1899
GLboolean * data
Definition glcorearb.h:298
GLuint GLfloat x0
Definition glcorearb.h:5034
GLuint GLfloat * val
Definition glcorearb.h:1582
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLfloat GLfloat y0
Definition glcorearb.h:5034
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr float PI
auto get(const std::byte *buffer, size_t=0)
Definition DataHeader.h:454
float length
length in material
Definition MatCell.h:55
float meanRho
mean density, g/cm^3
Definition MatCell.h:30
float meanX2X0
fraction of radiaton lenght
Definition MatCell.h:31
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"