Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-17 08:35:36

0001 
0002 #ifndef VECGEOM_SURFACE_LOGICEVALUATOR_H
0003 #define VECGEOM_SURFACE_LOGICEVALUATOR_H
0004 
0005 #include <VecGeom/surfaces/Model.h>
0006 
0007 namespace vgbrep {
0008 
0009 /// @brief Evaluate the inside result of the logic expression.
0010 /// @param plocalVol Point in local volume coordinates
0011 /// @param volId Volume id
0012 /// @param logic Logic expression
0013 /// @param surfdata Surface data storage
0014 /// @param logic_id Logical id entering/exiting surface for which the logic is known
0015 /// @param is_inside whether the known entering/exiting surface is inside or not
0016 /// @return Inside property
0017 template <typename Real_t>
0018 VECCORE_ATT_HOST_DEVICE bool EvaluateInside(vecgeom::Vector3D<Real_t> const &plocalVol, int volId,
0019                                             LogicExpression const &logic, SurfData<Real_t> const &surfdata,
0020                                             const int logic_id = vecgeom::kMaximumInt, const bool is_inside = 0)
0021 {
0022   auto test_bit  = [](unsigned bset, int bit) { return (bset & (unsigned(1) << bit)) > 0; };
0023   auto set_bit   = [](unsigned &bset, int bit) { bset |= unsigned(1) << bit; };
0024   auto reset_bit = [](unsigned &bset, int bit) { bset &= ~(unsigned(1) << bit); };
0025   auto swap_bit  = [](unsigned &bset, int bit) { bset ^= unsigned(1) << bit; };
0026   ///< Lambda to get the inside for individual unplaced surfaces of the same logical volume
0027   auto insideSurf = [&](int isurf, bool flip) {
0028     // Convert point from volume to local surface coordinates
0029     auto itrans                 = surfdata.fLocalSurf[isurf].fTrans;
0030     Vector3D<Real_t> plocalSurf = itrans.Transform(plocalVol);
0031     auto const &unplaced        = surfdata.fLocalSurf[isurf].fSurface;
0032     return unplaced.Inside(plocalSurf, surfdata, flip);
0033   };
0034   unsigned stack  = 0;
0035   unsigned negate = 0;
0036   int depth       = 0;
0037   bool result     = false;
0038   unsigned i;
0039   for (i = 0; i < logic.size(); ++i) {
0040     switch (logic[i]) {
0041     case lplus:
0042       depth++;
0043       break;
0044     case lminus:
0045       result = test_bit(stack, depth);
0046       reset_bit(negate, depth--);
0047       result ^= test_bit(negate, depth);
0048       if (result)
0049         set_bit(stack, depth);
0050       else
0051         reset_bit(stack, depth);
0052       break;
0053     case lnot:
0054       swap_bit(negate, depth);
0055       break;
0056     case lor:
0057       if (test_bit(stack, depth))
0058         i = logic[i + 1] - 1;
0059       else
0060         i++;
0061       break;
0062     case land:
0063       if (!test_bit(stack, depth))
0064         i = logic[i + 1] - 1;
0065       else
0066         i++;
0067       break;
0068     default:
0069       // This is an operand
0070       result = logic[i] == std::abs(logic_id) ? is_inside ^ bool(negate) : insideSurf(int(logic[i]), bool(negate));
0071       result ^= test_bit(negate, depth);
0072       reset_bit(negate, depth);
0073       // We can ignore the previous value because of short-circuiting
0074       if (result)
0075         set_bit(stack, depth);
0076       else
0077         reset_bit(stack, depth);
0078     }
0079   }
0080   VECGEOM_ASSERT(depth == 0);
0081   return (stack & 1) > 0;
0082 }
0083 
0084 /// @brief Evaluate the isotropic safety for the logic expression.
0085 /// @param plocalVol Point in local volume coordinates
0086 /// @param volId Volume id
0087 /// @param logic Logic expression
0088 /// @param surfdata Surface data storage
0089 /// @return Safety value. Infine length if safety larger than safe_max
0090 template <typename Real_t>
0091 VECCORE_ATT_HOST_DEVICE Real_t EvaluateSafety(vecgeom::Vector3D<Real_t> const &plocalVol, int volId, bool exiting,
0092                                               LogicExpression const &logic, SurfData<Real_t> const &surfdata,
0093                                               Real_t safe_max = vecgeom::InfinityLength<Real_t>())
0094 {
0095   struct Safetyval {
0096     vecgeom::Vector3D<Real_t> onsurf; ///< point on surface
0097     Real_t safety{0.};                ///< computed safety value
0098     short int isurf{-1};              ///< surface to which it applies (-1 = un-initialized)
0099     char op{0};                       ///< operator applying to it: 0 = uninitialized, 1 = '&', -1 = '|'
0100 
0101     VECCORE_ATT_HOST_DEVICE
0102     VECGEOM_FORCE_INLINE
0103     void Reset()
0104     {
0105       safety = 0.;
0106       isurf  = -1;
0107       op     = 0;
0108     }
0109 
0110     ///< @brief Perform the reduction with another safety value
0111     VECCORE_ATT_HOST_DEVICE
0112     VECGEOM_FORCE_INLINE
0113     void SwapReduction(const Safetyval &other)
0114     {
0115       // make sure the operator is reset at exit
0116       auto crt_op = op;
0117       op          = 0;
0118       // In case one of the surfaces is not defined, act as identity
0119       if (other.isurf < 0) return;
0120       if (isurf < 0) {
0121         operator=(other);
0122         return;
0123       }
0124       VECGEOM_ASSERT(crt_op != 0);
0125 
0126       if ((crt_op > 0) ^ (safety > other.safety)) {
0127         // swap current safety with other one
0128         safety = other.safety;
0129         onsurf = other.onsurf;
0130         isurf  = other.isurf;
0131       }
0132     }
0133   };
0134 
0135   ///< Lambda to get the safety for individual framed surfaces of the same logical volume
0136   auto safetySurf = [&](Safetyval &safety_surf) {
0137     // Convert point from volume to local surface coordinates
0138     auto trans             = surfdata.fLocalSurf[safety_surf.isurf].fTrans;
0139     Vector3D<Real_t> local = trans.Transform(plocalVol);
0140     auto const &framedsurf = surfdata.fLocalSurf[safety_surf.isurf];
0141     bool flipped           = framedsurf.fLogicId < 0;
0142     auto const &unplaced   = framedsurf.fSurface;
0143     unplaced.Safety(local, /*exiting ^*/ flipped, surfdata, safety_surf.safety, safety_surf.onsurf);
0144   };
0145 
0146   auto safetyFrame = [&](Safetyval &safety_surf) {
0147     // Compute safety from point projected on surface to the surface frame
0148     auto const &framedsurf = surfdata.fLocalSurf[safety_surf.isurf];
0149     bool valid             = false;
0150     auto safety            = framedsurf.fFrame.Safety(safety_surf.onsurf, safety_surf.safety, surfdata, valid);
0151     if (valid) safety_surf.safety = safety;
0152   };
0153 
0154   // Implementation of the following infix Boolean expression evaluation:
0155   // * The logic expression contains operands (surface ids), operators `&` or `|` and depths changed by `(` or `)`
0156   // * Operands may be negated `!` meaning that the corresponding half-space is flipped compared to the standard normal
0157   // convention. Negation is ignored because it is taken into account in the surface flipping, which negates the safety.
0158   //   * The logic expression is evaluated left to right taking the following actions depending on the current item:
0159   //     * Operands trigger signed safety evaluation for the half-space surface, using the convention: outside =
0160   //     positive.
0161   //     * Operators are cached for the current depth. Upon reading an operand and having a cached operator, min/max is
0162   //       called according to the operation, the result replacing the currently cached value. The safety reduction
0163   //       between two values is dependent on the operator:
0164   //       * safety(a & b) = max(safety_a, safety_b)
0165   //       * safety(a | b) = min(safety_a, safety_b)
0166   //       * if current operand does not have an operator (e.g. first in an expression in a new level), the reduction
0167   //       is done with a unit operand not changing the current one
0168   //     * Depth increase `(` pushes to the stack the current computed safety AND operator as sign of the safety: `+`
0169   //     for &
0170   //       and `-` for |
0171   //     * Depth decrease `)` pops the cached safety and operation and performs the safety reduction with the current
0172   //       cached value.
0173   // * At the end of the expression evaluation, the sign of the safety is negated if exiting is true, and the safety to
0174   // the actual closest frame is computed
0175 
0176   // TO DO: assert that the maximum depth is not hit after logic expression simplification
0177   constexpr int kStackSize = 8; // maximum depth (nested Boolean operations) for the input logic expression.
0178   Safetyval cached_safety[kStackSize];
0179   Safetyval crt_safety;
0180 
0181   int depth = 0;
0182   unsigned i;
0183   for (i = 0; i < logic.size(); ++i) {
0184     auto item = logic[i];
0185     if (item == lplus) {
0186       // increase depth: cache current safety and reset it
0187       cached_safety[depth++] = crt_safety;
0188       crt_safety.Reset();
0189     } else if (item == lminus) {
0190       // decrease depth: check if cached safety is valid
0191       depth--;
0192       VECGEOM_ASSERT(depth >= 0);
0193       crt_safety.op = cached_safety[depth].op;
0194       crt_safety.SwapReduction(cached_safety[depth]);
0195     } else if (item == lnot) {
0196       // negate = true;
0197     } else if (LogicExpression::is_operator_token(item)) {
0198       crt_safety.op = (item == land) ? 1 : -1;
0199       i++;
0200     } else {
0201       // This is a surface index
0202       Safetyval new_safety;
0203       new_safety.isurf = short(item);
0204       safetySurf(new_safety);
0205       crt_safety.SwapReduction(new_safety);
0206     }
0207   }
0208   VECGEOM_ASSERT(depth == 0);
0209   if (exiting) crt_safety.safety = -crt_safety.safety;
0210   // If needed, compute safety to the frame
0211   if (crt_safety.safety > 0. && crt_safety.safety <= safe_max) safetyFrame(crt_safety);
0212   return crt_safety.safety;
0213 }
0214 
0215 } // namespace vgbrep
0216 #endif