Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:13:52

0001 #pragma once
0002 
0003 #include <unordered_map>
0004 #include <vector>
0005 #include "VecGeom/base/Vector3D.h"
0006 #include "VecGeom/management/ABBoxManager.h"
0007 #include "VecGeom/base/SOA3D.h"
0008 #include "VecGeom/base/robin_hood.h" // for fast hash map
0009 #include <type_traits>
0010 
0011 namespace vecgeom {
0012 inline namespace VECGEOM_IMPL_NAMESPACE {
0013 
0014 // A class representing a sparse voxel structure
0015 // via a hash map; A voxel can hold an abitrary number of properties of type P
0016 // If we have scalar properties.. we will save one indirection and attach the property
0017 // directly to the map
0018 template <typename P, bool ScalarProperties = false>
0019 class FlatVoxelHashMap {
0020 public:
0021   FlatVoxelHashMap(Vector3D<float> const &lower, Vector3D<float> const &Length, int Nx, int Ny, int Nz)
0022       : fNx{Nx}, fNy{Ny}, fNz{Nz}
0023   {
0024     fLowerX    = lower.x();
0025     fLowerY    = lower.y();
0026     fLowerZ    = lower.z();
0027     fDeltaX    = Length.x() / Nx;
0028     fDeltaY    = Length.y() / Ny;
0029     fDeltaZ    = Length.z() / Nz;
0030     fInvDeltaX = 1. / fDeltaX;
0031     fInvDeltaY = 1. / fDeltaY;
0032     fInvDeltaZ = 1. / fDeltaZ;
0033   }
0034 
0035   // main method to add something; must be called in sorted key sequence
0036   // to achieve contiguous property storage per key
0037   void addProperty(Vector3D<float> const &point, P const &property)
0038   {
0039     const auto key = getVoxelKey(point);
0040     addPropertyForKey(key, property);
0041   }
0042 
0043   // length of diagonal of voxels
0044   float getVoxelDiagonal() const { return std::sqrt(fDeltaX * fDeltaX + fDeltaY * fDeltaY + fDeltaZ * fDeltaZ); }
0045 
0046   float getVoxelVolume() const { return fDeltaX * fDeltaY * fDeltaZ; }
0047 
0048   void addPropertyForKey(long key, P const &property)
0049   {
0050     auto iter = fVoxelMap.find(key);
0051     if (key == fLastKey) {
0052       // we append the property to the property vector
0053       // if a new property and increment the length count
0054       if (iter == fVoxelMap.end()) {
0055         std::cerr << "THERE SHOULD BE AN ENTRY ALREADY\n";
0056       }
0057       append<P>(iter, property);
0058     } else {
0059       // a new insertion; there should be no previous
0060       // entry at this key otherwise the property storage might
0061       // be corrupted
0062       if (iter != fVoxelMap.end()) {
0063         std::cerr << "FATAL ERROR IN INSERTION\n";
0064       }
0065       // inserting new property
0066       assign<P>(key, property);
0067       fLastKey = key;
0068     }
0069   }
0070 
0071   // a special method allowing to mark regions from a sequence of bounding boxes
0072   // TODO: somewhere else might be a better place for this
0073   void markFromBoundingBoxes(LogicalVolume const &lvol)
0074   {
0075     // IDEA:
0076     // a) iterate over the inside of the boxes in strides of
0077     //    out voxel grid and add the key and the property to some tmp storage
0078 
0079     // b) sort the keys so that we can insert them in order into the voxel container
0080 
0081     // get the BOUNDING BOXES
0082     std::vector<std::pair<long, P>> keyToProp;
0083 
0084     int nDaughters{0};
0085     auto abboxes = ABBoxManager::Instance().GetABBoxes(&lvol, nDaughters);
0086     for (int d = 0; d < nDaughters; ++d) {
0087       const auto &lower = abboxes[2 * d];
0088       const auto &upper = abboxes[2 * d + 1];
0089 
0090       int kxlow{-1}, kylow{-1}, kzlow{-1};
0091       int kxup{-1}, kyup{-1}, kzup{-1};
0092       getVoxelCoordinates(lower.x(), lower.y(), lower.z(), kxlow, kylow, kzlow);
0093       getVoxelCoordinates(upper.x(), upper.y(), upper.z(), kxup, kyup, kzup);
0094       // correct for out if bounds effects
0095       kxlow = std::max(kxlow, 0);
0096       kylow = std::max(kylow, 0);
0097       kzlow = std::max(kzlow, 0);
0098       // correct for out if bounds effects
0099       kxup = std::min(kxup, fNx - 1);
0100       kyup = std::min(kyup, fNy - 1);
0101       kzup = std::min(kzup, fNz - 1);
0102 
0103       // iterate over the box from lower to upper and fill keys
0104       for (int kx = kxlow; kx <= kxup; ++kx) {
0105         for (int ky = kylow; ky <= kyup; ++ky) {
0106           for (int kz = kzlow; kz <= kzup; ++kz) {
0107             keyToProp.push_back(std::pair<long, P>(getKeyFromCells(kx, ky, kz), d));
0108           }
0109         }
0110       }
0111     }
0112 
0113     // sort the container according to key
0114     std::sort(keyToProp.begin(), keyToProp.end(),
0115               [](std::pair<long, P> const &a, std::pair<long, P> const &b) { return a.first < b.first; });
0116 
0117     // iterate over final container and insert properties into the hash map
0118     for (auto &e : keyToProp) {
0119       addPropertyForKey(e.first, e.second);
0120     }
0121   }
0122 
0123   // main method to query something
0124   bool isOccupied(Vector3D<float> const &point) const { return fVoxelMap.find(getVoxelKey(point)) != fVoxelMap.end(); }
0125 
0126   bool isOccupied(long key) const { return fVoxelMap.find(key) != fVoxelMap.end(); }
0127 
0128   // get all voxel keys for a complete container of points
0129   // TODO: this can be nicely vectorized
0130   std::vector<long> getKeys(SOA3D<float> const &points)
0131   {
0132     std::vector<long> keys;
0133     for (size_t i = 0; i < points.size(); ++i) {
0134       keys.push_back(getKey(points.x(i), points.y(i), points.z(i)));
0135     }
0136     return keys;
0137   }
0138 
0139   // get all voxel keys for a complete container of points
0140   // TODO: this can be nicely vectorized
0141   template <typename T>
0142   void getKeys(SOA3D<T> const &points, std::vector<long> &keys)
0143   {
0144     for (size_t i = 0; i < points.size(); ++i) {
0145       keys.push_back(getKey(points.x(i), points.y(i), points.z(i)));
0146     }
0147   }
0148 
0149   // calculates the integer voxel coordinates
0150   void getVoxelCoordinates(float x, float y, float z, int &kx, int &ky, int &kz) const
0151   {
0152     kx = (int)((x - fLowerX) * fInvDeltaX);
0153     ky = (int)((y - fLowerY) * fInvDeltaY);
0154     kz = (int)((z - fLowerZ) * fInvDeltaZ);
0155   }
0156 
0157   long getKeyFromCells(int kx, int ky, int kz) const { return kx + fNx * (ky + kz * fNy); }
0158 
0159   long getKey(float x, float y, float z) const
0160   {
0161     const auto kx = (int)((x - fLowerX) * fInvDeltaX);
0162     const auto ky = (int)((y - fLowerY) * fInvDeltaY);
0163     const auto kz = (int)((z - fLowerZ) * fInvDeltaZ);
0164     // if (kx < 0 || kx >= fNx) {
0165     //  std::cerr << "key problem in x " << x << " (lowerX ) " << fLowerX << " key " << kx << "\n";
0166     //}
0167     // if (ky < 0 || ky >= fNy) {
0168     //  std::cerr << "key problem in y " << y << " (lowerY ) " << fLowerY << " key " << ky << "\n";
0169     //}
0170     // if (kz < 0 || kz >= fNz) {
0171     //  std::cerr << "key problem in z " << y << " (lowerZ ) " << fLowerZ << " key " << kz << "\n";
0172     //}
0173     return getKeyFromCells(kx, ky, kz);
0174   }
0175 
0176   long getVoxelKey(Vector3D<float> const &point) const { return getKey(point.x(), point.y(), point.z()); }
0177 
0178   const P *getProperties(Vector3D<float> const &point, int &length) const
0179   {
0180     return getPropertiesGivenKey(getVoxelKey(point), length);
0181   }
0182 
0183   // returns the pointer to the first property and how many properties
0184   // there are following
0185   // TODO: Instead of a pointer we should return an iterable
0186   // std::span as soon as this is available
0187   const P *getPropertiesGivenKey(long key, int &length) const { return getPropertiesImpl<P>(key, length); }
0188 
0189   Vector3D<int> keyToCell(long key) const
0190   {
0191     const auto kz = key / (fNx * fNy);
0192     const auto ky = (key - (kz * fNx * fNy)) / fNx;
0193     const auto kx = key - (kz * fNx * fNy) - ky * fNx;
0194     return Vector3D<int>(kx, ky, kz);
0195   }
0196 
0197   // returns **mid** point of voxel in cartesian coordinates
0198   Vector3D<float> keyToPos(long key) const
0199   {
0200     const auto kz = key / (fNx * fNy);
0201     const auto ky = (key - (kz * fNx * fNy)) / fNx;
0202     const auto kx = key - (kz * fNx * fNy) - ky * fNx;
0203     return Vector3D<float>(fLowerX + kx * fDeltaX * 1.5, fLowerY + ky * fDeltaY * 1.5, fLowerZ + kz * fDeltaZ * 1.5);
0204   }
0205 
0206   // return the lower and upper coordinates describing the extend of a voxel of some key
0207   void Extent(long key, Vector3D<float> &lower, Vector3D<float> &upper) const
0208   {
0209     const auto kz = key / (fNx * fNy);
0210     const auto ky = (key - (kz * fNx * fNy)) / fNx;
0211     const auto kx = key - (kz * fNx * fNy) - ky * fNx;
0212     lower         = Vector3D<float>(fLowerX + kx * fDeltaX, fLowerY + ky * fDeltaY, fLowerZ + kz * fDeltaZ);
0213     upper         = lower + Vector3D<float>(fDeltaX, fDeltaY, fDeltaZ);
0214   }
0215 
0216   // mainly for debugging
0217   void print() const
0218   {
0219     int count{0};
0220     int pcount{0};
0221     for (auto &k : fVoxelMap) {
0222       auto key = k.first;
0223       int number{0};
0224       auto props = getPropertiesGivenKey(key, number);
0225       pcount += number;
0226       count++;
0227       std::cout << " voxel at key " << key << " : " << keyToCell(key) << " pos " << keyToPos(key) << " filled with "
0228                 << number << " properties \n";
0229       std::cout << "{ ";
0230       for (int i = 0; i < number; ++i) {
0231         std::cout << props[i] << " , ";
0232       }
0233       std::cout << " }\n";
0234     }
0235     std::cout << "NUM VOXELS OCCUPIED " << count << " SUM PROPERTIES " << pcount << "\n";
0236   }
0237 
0238   /// clear the container
0239   void clear()
0240   {
0241     fVoxelMap.clear();
0242     fProperties.clear();
0243   }
0244 
0245   /// get number of filled voxels
0246   size_t size() const { return fVoxelMap.size(); }
0247 
0248 private:
0249   // the dimensions
0250   int fNx = 0;
0251   int fNy = 0;
0252   int fNz = 0;
0253 
0254   // offset in 3D space
0255   float fLowerX    = 0.;
0256   float fLowerY    = 0.;
0257   float fLowerZ    = 0.;
0258   float fDeltaX    = 1.;
0259   float fDeltaY    = 1.;
0260   float fDeltaZ    = 1.;
0261   float fInvDeltaX = 1.;
0262   float fInvDeltaY = 1.;
0263   float fInvDeltaZ = 1.;
0264 
0265   long fLastKey = -1;
0266 
0267   // we map a long key to the start index of the property map together
0268   // with the number of properties for this voxel
0269   //  std::unordered_map<long, std::pair<int, int>> fVoxelMap;
0270 
0271   // this function gets evaluated only if ScalarProperties = true
0272   template <typename T>
0273   void assign(long key, typename std::enable_if<ScalarProperties, T>::type prop)
0274   {
0275     fVoxelMap[key] = prop;
0276   }
0277 
0278   // this function gets called/compiled only if ScalarProperties = false
0279   template <typename T>
0280   void assign(long key, typename std::enable_if<!ScalarProperties, T>::type prop)
0281   {
0282     fVoxelMap[key] = std::pair<int, int>(fProperties.size(), 1);
0283     fProperties.push_back(prop);
0284   }
0285 
0286   // this function gets evaluated only if ScalarProperties = true
0287   template <typename T, typename Iter>
0288   void append(Iter iter, typename std::enable_if<ScalarProperties, T>::type prop)
0289   {
0290     // nothing to be done - simply not possible
0291     // TODO: this has to throw an exception or assert
0292     std::cerr << "INVALID APPEND TO SCALAR PORPERTIES\n";
0293   }
0294 
0295   // this function gets called/compiled only if ScalarProperties = false
0296   template <typename T, typename Iter>
0297   void append(Iter iter, typename std::enable_if<!ScalarProperties, T>::type prop)
0298   {
0299     fProperties.push_back(prop);
0300     iter->second.second++; // we increment the number of properties for this voxel
0301   }
0302 
0303   // implementation for ScalarProperties = false
0304   template <typename T>
0305   T const *getPropertiesImpl(long key, int &length,
0306                              typename std::enable_if<!ScalarProperties, T>::type * = nullptr) const
0307   {
0308     length    = 0;
0309     auto iter = fVoxelMap.find(key);
0310     if (iter != fVoxelMap.end()) {
0311       length = iter->second.second;
0312       return &fProperties[iter->second.first];
0313     }
0314     return nullptr;
0315   }
0316 
0317   // implementation for ScalarProperties = true
0318   template <typename T>
0319   T const *getPropertiesImpl(long key, int &length,
0320                              typename std::enable_if<ScalarProperties, T>::type * = nullptr) const
0321   {
0322     length    = 0;
0323     auto iter = fVoxelMap.find(key);
0324     if (iter != fVoxelMap.end()) {
0325       length = 1;
0326       return &(iter->second);
0327     }
0328     return nullptr;
0329   }
0330 
0331   using MapValueType = typename std::conditional<ScalarProperties, P, std::pair<int, int>>::type;
0332   robin_hood::unordered_map<long, MapValueType> fVoxelMap;
0333 
0334   std::vector<P> fProperties;
0335 };
0336 
0337 } // namespace VECGEOM_IMPL_NAMESPACE
0338 } // namespace vecgeom