Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:39:32

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 // Copyright (C) 2026 G4OCCT Contributors
0003 
0004 /// @file G4OCCTAssemblyVolume.hh
0005 /// @brief Declaration of G4OCCTAssemblyVolume.
0006 
0007 #ifndef G4OCCT_G4OCCTAssemblyVolume_hh
0008 #define G4OCCT_G4OCCTAssemblyVolume_hh
0009 
0010 #include "G4OCCT/G4OCCTLogicalVolume.hh"
0011 #include "G4OCCT/G4OCCTMaterialMap.hh"
0012 #include "G4OCCT/G4OCCTSensitiveDetectorMap.hh"
0013 
0014 #include <G4AssemblyVolume.hh>
0015 #include <G4String.hh>
0016 
0017 // OCCT (forward-declared where possible; full includes in .cc)
0018 #include <TDF_Label.hxx>
0019 #include <gp_Trsf.hxx>
0020 
0021 #include <map>
0022 #include <string>
0023 
0024 /**
0025  * @brief Extends Geant4's G4AssemblyVolume with an OCCT XDE label reference.
0026  *
0027  * Imports a STEP assembly file using the OCCT Extended Data Exchange (XDE)
0028  * layer and constructs the corresponding Geant4 volume hierarchy.
0029  *
0030  * ## OCCT XDE to Geant4 mapping
0031  *
0032  * | XDE entity                          | Geant4 result                       |
0033  * |-------------------------------------|-------------------------------------|
0034  * | Top-level assembly label            | `G4OCCTAssemblyVolume` (this class) |
0035  * | Simple-shape label (first use)      | `G4OCCTSolid` + `G4OCCTLogicalVolume` |
0036  * | Simple-shape label (repeated use)   | Shared `G4OCCTLogicalVolume`        |
0037  * | Reference label + TopLoc_Location   | `AddPlacedVolume()` on parent assembly |
0038  * | XDE name attribute                  | Volume name string                  |
0039  * | XDE material attribute (if present) | `G4Material*` via `G4OCCTMaterialMap` |
0040  * | Part name (fallback when no mat attr) | `G4Material*` via `G4OCCTMaterialMap` |
0041  *
0042  * ## Instance sharing
0043  *
0044  * When the same XDE shape label is referenced from multiple locations the
0045  * corresponding `G4OCCTLogicalVolume` is created only once and reused for
0046  * each placement (incrementing the copy number each time).  This mirrors
0047  * Geant4's own convention.
0048  *
0049  * ## Thread safety
0050  *
0051  * This class constructs the Geant4 geometry during `FromSTEP()` and is
0052  * subsequently read-only.  It inherits `G4AssemblyVolume`'s thread-safety
0053  * guarantees after construction.
0054  *
0055  * ## Usage
0056  * ```cpp
0057  * G4OCCTMaterialMap matMap;
0058  * matMap.Add("Al 6061-T6", G4NistManager::Instance()->FindOrBuildMaterial("G4_Al"));
0059  *
0060  * auto* assembly = G4OCCTAssemblyVolume::FromSTEP("detector.step", matMap);
0061  *
0062  * // In ConstructSDandField():
0063  * G4OCCTSensitiveDetectorMap sdMap;
0064  * sdMap.Add("Absorber", myAbsoSD);
0065  * assembly->ApplySDMap(sdMap);
0066  *
0067  * // Imprint into the world logical volume
0068  * G4ThreeVector pos;
0069  * G4RotationMatrix rot;
0070  * assembly->MakeImprint(worldLV, pos, &rot);
0071  * ```
0072  *
0073  * See docs/step_assembly_import.md for the full design and background.
0074  */
0075 class G4OCCTAssemblyVolume : public G4AssemblyVolume {
0076 public:
0077   G4OCCTAssemblyVolume()  = default;
0078   ~G4OCCTAssemblyVolume() = default;
0079 
0080   /**
0081    * Import a STEP assembly file and construct the Geant4 volume hierarchy.
0082    *
0083    * Reads the STEP file at @p path using `STEPCAFControl_Reader` (the OCCT
0084    * XDE reader that preserves assembly structure, names, and material
0085    * attributes).  The label tree is traversed depth-first; each simple-shape
0086    * leaf creates a `G4OCCTSolid` + `G4OCCTLogicalVolume`, and each placement
0087    * is recorded via `AddPlacedVolume()` on the top-level assembly.
0088    *
0089    * Every material name encountered in the STEP file must be present in
0090    * @p materialMap; an unresolved name triggers a fatal `G4Exception`.
0091    * When a shape carries no XDE material attribute, its part (label) name
0092    * is used as the lookup key instead, accommodating STEP writers that do
0093    * not write material attributes.
0094    *
0095    * The leaf shapes are recentered (bounding-box centroid moved to the OCCT
0096    * origin) before being wrapped in `G4OCCTSolid`; the recentering offset is
0097    * absorbed into the placement transformation so that parts appear at their
0098    * correct world positions.
0099    *
0100    * @param path        Filesystem path to the STEP file.
0101    * @param materialMap Map from STEP material name or part name to `G4Material*`.
0102    * @return Pointer to a newly heap-allocated `G4OCCTAssemblyVolume` owned by
0103    *         the caller.  The returned object already contains all child
0104    *         volumes; call `MakeImprint()` to place it in the world.
0105    * @throws std::runtime_error if the file cannot be read or yields no shapes.
0106    */
0107   static G4OCCTAssemblyVolume* FromSTEP(const std::string& path,
0108                                         const G4OCCTMaterialMap& materialMap);
0109 
0110   /**
0111    * Return all logical volumes created during import, keyed by part name.
0112    *
0113    * The map is populated during `FromSTEP()`.  When two STEP parts share
0114    * the same name the later name is suffixed with `_<n>` so that every entry
0115    * is unique.
0116    */
0117   const std::map<G4String, G4OCCTLogicalVolume*>& GetLogicalVolumes() const {
0118     return fLogicalVolumes;
0119   }
0120 
0121   /**
0122    * Assign sensitive detectors to all logical volumes whose names match
0123    * entries in @p sdMap.
0124    *
0125    * Iterates all logical volumes created during `FromSTEP()` and calls
0126    * `sdMap.Resolve(name)` for each.  When a non-null sensitive detector is
0127    * returned, `G4LogicalVolume::SetSensitiveDetector()` is called on the
0128    * corresponding logical volume.
0129    *
0130    * @param sdMap  Map from volume-name patterns to sensitive detector pointers.
0131    * @return Number of logical volumes for which a sensitive detector was assigned.
0132    *
0133    * @note Call this from `G4VUserDetectorConstruction::ConstructSDandField()`
0134    *       **after** both `FromSTEP()` and the SD objects have been created and
0135    *       registered with `G4SDManager`.
0136    */
0137   std::size_t ApplySDMap(const G4OCCTSensitiveDetectorMap& sdMap);
0138 
0139 private:
0140   /// All G4OCCTLogicalVolume objects created during import, keyed by unique name.
0141   std::map<G4String, G4OCCTLogicalVolume*> fLogicalVolumes;
0142 
0143   /// Build context passed through the recursive traversal.
0144   struct BuildContext;
0145 
0146   /**
0147    * Recursively import an XDE label into the Geant4 hierarchy.
0148    *
0149    * @param label         XDE label to import (assembly, simple shape, or reference).
0150    * @param parentAssembly Non-null assembly to add child volumes to; may be
0151    *                       @c this (the top-level) or an intermediate
0152    *                       `G4AssemblyVolume`.
0153    * @param composedTrsf  Accumulated rigid-body transformation from the root
0154    *                       to this label's coordinate frame.
0155    * @param ctx           Mutable build context (prototype map, copy counter, …).
0156    */
0157   static void ImportLabel(const TDF_Label& label, G4AssemblyVolume* parentAssembly,
0158                           const gp_Trsf& composedTrsf, BuildContext& ctx);
0159 };
0160 
0161 #endif // G4OCCT_G4OCCTAssemblyVolume_hh