Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 09:07:48

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file orange/orangeinp/detail/CsgLogicUtils.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <algorithm>
0010 #include <type_traits>
0011 #include <vector>
0012 
0013 #include "corecel/Assert.hh"
0014 #include "corecel/cont/VariantUtils.hh"
0015 #include "corecel/math/Algorithms.hh"
0016 #include "orange/OrangeTypes.hh"
0017 
0018 #include "../CsgTree.hh"
0019 #include "../CsgTypes.hh"
0020 
0021 namespace celeritas
0022 {
0023 namespace orangeinp
0024 {
0025 namespace detail
0026 {
0027 //---------------------------------------------------------------------------//
0028 /*!
0029  * Result of building a logic representation of a node.
0030  */
0031 struct BuildLogicResult
0032 {
0033     using VecLogic = std::vector<logic_int>;
0034     using VecSurface = std::vector<LocalSurfaceId>;
0035 
0036     VecSurface faces;
0037     VecLogic logic;
0038 };
0039 
0040 //---------------------------------------------------------------------------//
0041 /*!
0042  * Sort the faces of a volume and remap the logic expression.
0043  */
0044 inline BuildLogicResult::VecSurface remap_faces(BuildLogicResult::VecLogic& lgc)
0045 {
0046     // Construct sorted vector of faces
0047     BuildLogicResult::VecSurface faces;
0048     for (auto const& v : lgc)
0049     {
0050         if (!logic::is_operator_token(v))
0051         {
0052             faces.push_back(LocalSurfaceId{v});
0053         }
0054     }
0055 
0056     // Sort and uniquify the vector
0057     std::sort(faces.begin(), faces.end());
0058     faces.erase(std::unique(faces.begin(), faces.end()), faces.end());
0059 
0060     // Remap logic
0061     for (auto& v : lgc)
0062     {
0063         if (!logic::is_operator_token(v))
0064         {
0065             auto iter
0066                 = find_sorted(faces.begin(), faces.end(), LocalSurfaceId{v});
0067             CELER_ASSUME(iter != faces.end());
0068             v = iter - faces.begin();
0069         }
0070     }
0071     return faces;
0072 }
0073 
0074 //---------------------------------------------------------------------------//
0075 /*!
0076  * Construct a logic representation of a node.
0077  *
0078  * The result is a pair of vectors: the sorted surface IDs comprising the faces
0079  * of this volume, and the logical representation using \em face IDs, i.e. with
0080  * the surfaces remapped to the index of the surface in the face vector.
0081  *
0082  * The function is templated on a policy class that determines the logic
0083  * representation. The policy class must have an operator() that takes a
0084  * NodeId.
0085  *
0086  * The per-node local surfaces (faces) are sorted in ascending order of ID, not
0087  * of access, since they're always evaluated sequentially rather than as part
0088  * of the logic evaluation itself.
0089  */
0090 template<class BuildLogicPolicy>
0091 inline BuildLogicResult build_logic(BuildLogicPolicy&& policy, NodeId n)
0092 {
0093     static_assert(std::is_invocable_v<BuildLogicPolicy, NodeId>);
0094     static_assert(std::is_rvalue_reference_v<BuildLogicPolicy&&>,
0095                   "Will move from policy: rvalue ref expected");
0096     CELER_EXPECT(policy.empty());
0097 
0098     // Construct logic vector as local surface IDs
0099     policy(n);
0100     auto lgc = std::forward<BuildLogicPolicy>(policy).logic();
0101     return {remap_faces(lgc), std::move(lgc)};
0102 }
0103 
0104 //---------------------------------------------------------------------------//
0105 /*!
0106  * Base class for logic builder policies following CRTP pattern.
0107  *
0108  * The call operator for Negated and Joined are not implemented in the base
0109  * policy and must be provided by the derived class.
0110  */
0111 template<class BuilderPolicy>
0112 class BaseBuildLogicPolicy
0113 {
0114   public:
0115     //!@{
0116     //! \name Type aliases
0117     using VecLogic = std::vector<logic_int>;
0118     using VecSurface = std::vector<LocalSurfaceId>;
0119     //!@}
0120 
0121     static_assert(std::is_same_v<LocalSurfaceId::size_type, logic_int>,
0122                   "unsupported: add enum logic conversion for different-sized "
0123                   "face and surface ints");
0124 
0125   public:
0126     // Construct without mapping
0127     explicit inline BaseBuildLogicPolicy(CsgTree const& tree);
0128     // Construct with optional mapping
0129     inline BaseBuildLogicPolicy(CsgTree const& tree, VecSurface const& vs);
0130 
0131     //! Build from a node ID
0132     inline void operator()(NodeId const& n);
0133 
0134     //!@{
0135     //! \name Visit a node directly
0136     // Append 'true'
0137     inline void operator()(True const&);
0138     // False is never explicitly part of the node tree
0139     inline void operator()(False const&);
0140     // Append a surface ID
0141     inline void operator()(Surface const&);
0142     // Aliased nodes should never be reachable explicitly
0143     inline void operator()(Aliased const&);
0144     //!@}
0145 
0146     //! Whether no logic has been filled
0147     bool empty() const { return logic_.empty(); }
0148 
0149     //! Access the logic expression
0150     VecLogic&& logic() && { return std::move(logic_); }
0151 
0152   protected:
0153     //! Access the logic expression directly
0154     VecLogic& logic() & { return logic_; }
0155 
0156   private:
0157     ContainerVisitor<CsgTree const&, NodeId> visit_node_;
0158     VecSurface const* mapping_{nullptr};
0159     VecLogic logic_;
0160 };
0161 
0162 //---------------------------------------------------------------------------//
0163 /*!
0164  * Construct without mapping.
0165  */
0166 template<class BuilderPolicy>
0167 BaseBuildLogicPolicy<BuilderPolicy>::BaseBuildLogicPolicy(CsgTree const& tree)
0168     : visit_node_{tree}
0169 {
0170     static_assert(std::is_base_of_v<BaseBuildLogicPolicy, BuilderPolicy>,
0171                   "CRTP: template parameter must be derived class");
0172 }
0173 
0174 //---------------------------------------------------------------------------//
0175 /*!
0176  * Construct with optional mapping.
0177  *
0178  * The optional surface mapping is an ordered vector of *existing* surface IDs.
0179  * Those surface IDs will be replaced by the index in the array. All existing
0180  * surface IDs must be present!
0181  */
0182 template<class BuilderPolicy>
0183 BaseBuildLogicPolicy<BuilderPolicy>::BaseBuildLogicPolicy(CsgTree const& tree,
0184                                                           VecSurface const& vs)
0185     : visit_node_{tree}, mapping_{&vs}
0186 {
0187     static_assert(std::is_base_of_v<BaseBuildLogicPolicy, BuilderPolicy>,
0188                   "CRTP: template parameter must be derived class");
0189 }
0190 
0191 //---------------------------------------------------------------------------//
0192 /*!
0193  * Build from a node ID.
0194  */
0195 template<class BuilderPolicy>
0196 void BaseBuildLogicPolicy<BuilderPolicy>::operator()(NodeId const& n)
0197 {
0198     visit_node_(static_cast<BuilderPolicy&>(*this), n);
0199 }
0200 
0201 //---------------------------------------------------------------------------//
0202 /*!
0203  * Append the "true" token.
0204  */
0205 template<class BuilderPolicy>
0206 void BaseBuildLogicPolicy<BuilderPolicy>::operator()(True const&)
0207 {
0208     logic_.push_back(logic::ltrue);
0209 }
0210 
0211 //---------------------------------------------------------------------------//
0212 /*!
0213  * Explicit "False" should never be possible for a CSG cell.
0214  *
0215  * The 'false' standin is always aliased to "not true" in the CSG tree.
0216  */
0217 template<class BuilderPolicy>
0218 void BaseBuildLogicPolicy<BuilderPolicy>::operator()(False const&)
0219 {
0220     CELER_ASSERT_UNREACHABLE();
0221 }
0222 
0223 //---------------------------------------------------------------------------//
0224 /*!
0225  * Push a surface ID.
0226  */
0227 template<class BuilderPolicy>
0228 void BaseBuildLogicPolicy<BuilderPolicy>::operator()(Surface const& s)
0229 {
0230     CELER_EXPECT(s.id < logic::lbegin);
0231     // Get index of original surface or remapped
0232     logic_int sidx = [this, sid = s.id] {
0233         if (!mapping_)
0234         {
0235             return sid.unchecked_get();
0236         }
0237         else
0238         {
0239             // Remap by finding position of surface in our mapping
0240             auto iter = find_sorted(mapping_->begin(), mapping_->end(), sid);
0241             CELER_ASSERT(iter != mapping_->end());
0242             return logic_int(iter - mapping_->begin());
0243         }
0244     }();
0245 
0246     logic_.push_back(sidx);
0247 }
0248 
0249 //---------------------------------------------------------------------------//
0250 /*!
0251  * Push an aliased node.
0252  *
0253  * Aliased node shouldn't be reachable if the tree is fully simplified, but
0254  * could be reachable for testing purposes.
0255  */
0256 template<class BuilderPolicy>
0257 void BaseBuildLogicPolicy<BuilderPolicy>::operator()(Aliased const& n)
0258 {
0259     (*this)(n.node);
0260 }
0261 
0262 //---------------------------------------------------------------------------//
0263 /*!
0264  * Recursively construct a logic vector from a node with postfix operation.
0265  *
0266  * This is a policy used as template parameter of the \c build_logic function.
0267  * The user invokes this class with a node ID (usually representing a cell),
0268  * and then this class recurses into the daughters using a tree visitor.
0269  *
0270  * Example: \verbatim
0271     all(1, 3, 5) -> {{1, 3, 5}, "0 1 & 2 & &"}
0272     all(1, 3, !all(2, 4)) -> {{1, 2, 3, 4}, "0 2 & 1 3 & ~ &"}
0273  * \endverbatim
0274  */
0275 class PostfixBuildLogicPolicy
0276     : public BaseBuildLogicPolicy<PostfixBuildLogicPolicy>
0277 {
0278   public:
0279     //!@{
0280     //! \name Type aliases
0281     using BaseBuildLogicPolicy::VecLogic;
0282     using BaseBuildLogicPolicy::VecSurface;
0283     //!@}
0284 
0285   public:
0286     using BaseBuildLogicPolicy::BaseBuildLogicPolicy;
0287 
0288     //!@{
0289     //! \name Visit a node directly
0290     using BaseBuildLogicPolicy::operator();
0291 
0292     //! Visit a negated node and append 'not'.
0293     void operator()(Negated const& n)
0294     {
0295         (*this)(n.node);
0296         logic().push_back(logic::lnot);
0297     }
0298 
0299     //! Visit daughter nodes and append the conjunction.
0300     void operator()(Joined const& n)
0301     {
0302         CELER_EXPECT(n.nodes.size() > 1);
0303 
0304         // Visit first node, then add conjunction for subsequent nodes
0305         auto iter = n.nodes.begin();
0306         (*this)(*iter++);
0307 
0308         while (iter != n.nodes.end())
0309         {
0310             (*this)(*iter++);
0311             logic().push_back(n.op);
0312         }
0313     }
0314     //!@}
0315 };
0316 
0317 //---------------------------------------------------------------------------//
0318 /*!
0319  * Recursively construct a logic vector from a node with infix operation.
0320  *
0321  * This is a policy used as template parameter of \c build_logic.
0322  * The user invokes this class with a node ID (usually representing a cell),
0323  * and then this class recurses into the daughters using a tree visitor.
0324  *
0325  * Example: \verbatim
0326     all(1, 3, 5) -> {{1, 3, 5}, "(0 & 1 & 2)"}
0327     all(1, 3, any(~(2), ~(4))) -> {{1, 2, 3, 4}, "(0 & 2 & (~1 | ~3))"}
0328  * \endverbatim
0329  */
0330 class InfixBuildLogicPolicy : public BaseBuildLogicPolicy<InfixBuildLogicPolicy>
0331 {
0332   public:
0333     //!@{
0334     //! \name Type aliases
0335     using BaseBuildLogicPolicy::VecLogic;
0336     using BaseBuildLogicPolicy::VecSurface;
0337     //!@}
0338 
0339   public:
0340     using BaseBuildLogicPolicy::BaseBuildLogicPolicy;
0341 
0342     //!@{
0343     //! \name Visit a node directly
0344     using BaseBuildLogicPolicy::operator();
0345 
0346     //! Append 'not' and visit a negated node.
0347     void operator()(Negated const& n)
0348     {
0349         this->logic().push_back(logic::lnot);
0350         (*this)(n.node);
0351     }
0352 
0353     //! Visit daughter nodes and append the conjunction.
0354     void operator()(Joined const& n)
0355     {
0356         CELER_EXPECT(n.nodes.size() > 1);
0357         auto& logic = this->logic();
0358         logic.push_back(logic::lopen);
0359         // Visit first node, then add conjunction for subsequent nodes
0360         auto iter = n.nodes.begin();
0361         (*this)(*iter++);
0362 
0363         while (iter != n.nodes.end())
0364         {
0365             logic.push_back(n.op);
0366             (*this)(*iter++);
0367         }
0368         logic.push_back(logic::lclose);
0369     }
0370     //!@}
0371 };
0372 
0373 //---------------------------------------------------------------------------//
0374 }  // namespace detail
0375 }  // namespace orangeinp
0376 }  // namespace celeritas