Skip to content

GeoModel Kernel Class Reference

This section describes in more detail the classes in the geometry kernel. In most cases we provide the class interface. In cases where part of the interface is used only internally by the geometry kernel itself and not by other users. In such cases we present a simplified picture of the interfaces.

Detailed descriptions of the geometry kernel classes follow.

Reference counting

Warning

The custom reference counting used in GeoModel was developed when no smart pointers were available for C++. That part will be the object of a major refactoring in the near future.

Many objects need to be allocated in the description of a complicated geometry. For efficient use of memory, these should be shared as often as possible. The extensive sharing of objects in a geometry system calls for a way of destroying the objects as soon as they are not used—by any other object that references them. The scheme for doing this is called reference counting. In the GeoModelKernel classes, it is achieved by mixing in a abstract base class, RCBase:

//RCBase
  Constructor:
    RCBase()
  Public Methods:
    void ref ()
    void unref ()
    unsigned int refCount ()

RCBase is inherited by many of the classes in the geometry system. Reference-counted objects can only be created using operator new, and cannot be created on the stack. The methods ref() and unref() can be called to increase or decrease the reference count of an object. When the reference count decreases to zero, the object deletes itself. The accessor refCount() returns the current reference count.

Materials and Elements

Introduction

Two classes are used for describing materials: GeoElement and GeoMaterial.

Elements are declared by specifying a name, chemical symbol, and atomic number and mass; the latter being specified in atomic mass units.

Materials are constructed with a name and density and are not ready-for-use until one or more elements have been added, e.g.:

water->add(hydrogen,0.11);
water->add(oxygen,0.89);
water->lock();

The add() method takes an element and its mass fraction, and is overloaded to accept also a material and its mass fraction. The lock() method protects the material against further addition of elements, and re-normalizes the mass fractions so that they sum to unity

The material responds to various queries about its composition, and in addition can return a series of derived quantities of physical interest. The interaction length and radiation length of a material are familiar and are described in the Particle Data Book1.

Ionization energy loss in materials follows the Bethe-Bloch formula and is governed by two constants, an overall normalization term and the ionization potential, which can be computed from the atomic number and density of the material; the calculation is also described in the Particle Data Book cited above1. The calculation does not include small corrections to the energy loss due to chemical binding of elements. These constants are available through the methods getDeDxConstant() and getDeDxI0(). The method getDeDxMin() returns the minimum ionization energy loss and is derived from the other methods.

Both materials and elements are reference-counted; the reference count of an element is incremented when it added to a material and decremented when a referencing material is destroyed; materials are reference counted when they are used in logical volumes and decremented when the referencing logical volume is destroyed.

GeoElement

// GeoElement
  //Constructor:
    GeoElement (const std::string & Name, const std::string & Symbol, double Z, double A)
  //Public Methods:
    std::string getName() const std::string getSymbol() const double getZ() const
    double getA() const
    double getN() const

GeoElement has a constructor which takes a name, a chemical symbol, an atomic number, and an atomic weight2. The public methods provide access to this information. The getN() method returns the effective number of nucleons in the material, \(Z+A\).

GeoMaterial

// GeoMaterial
  //Constructor:
    GeoMaterial (const std::string & Name, double Density) const
  //Public Methods:
    void add (GeoElement * element, double fraction = 1.0) void add (GeoMaterial * material, double fraction) void lock ()
    std::string getName () const
    double getDensity () const
    unsigned int getID() const
    unsigned int getNumElements () const
    const GeoElement * getElement (unsigned int i) const double getFraction (int i) const
    double getRadLength () const
    double getIntLength () const
    double getDeDxConstant () const
    double getDeDxI0 () const
    double getDeDxMin () const

GeoMaterial is a class that describes a material, which is a list of elements. It is created “empty”; subsequently, elements are added one-by-one until the material is “locked”. When the material is locked, no further editing is possible, and a series of derived quantities (radiation length, interaction length, etc.) is computed for the material.

The construvctor, GeoMaterial(const std::string & Name, double Density) Constructs the material with a name and a density3.

The method add(GeoElement * element, double fraction = 1.0) adds an element to the material, with a specified mass fraction.

The method add(GeoMaterial * material, double fraction) adds a material to the material, with a specified mass fraction. This is useful for combining precombined materials, such as argon + ethane.

The method lock() Locks the material against further editing, and computes all derived quanties such as radiation length and interaction length.

The method getName() returns the name of the material as a string.

The method getDensity() returns the density of the material3.

The method getID() returns the id of the material as an integere. The id is generated automatically by counting instances of materials.

The method getNumElements() returns the number of elements in a material.

The method getElement(i) returns a pointer to the \(i^{th}\) element.

The method getFraction(i) returns the fraction of the \(i^{th}\) element.

The method getRadLength() returns the radiation length of the material, computed from the density, the list of constituents, and their properties.

The method getIntLength() returns the interaction length of the material, computed from the density the list of constituents, and their properties.

The following methods refer to ionization energy loss, specifically, the following formulation:

\[ \frac{dE}{dx} = \frac{K}{\beta^2} (ln (\frac{2m_e c^2 \beta^2 \gamma^2}{I_0}) - \beta^2 ) \]

The method getDeDxConstant() returns the constant, \(K\), which depends upon the material properties (mostly the density).

While the method getDeDxI0() returns the effective ionization potential \(I_0\), which is a property of the material.

The method getDeDxMin() returns an approximation for the ionization of a minimum ionizing particle (\(\beta\gamma=3.4\)), given by: \(K \times 11.528\).

Shapes

Introduction

The shape classes in the geometry kernel are data structures designed to describe several geometrical primitives. Table 1 describes the different shapes presently provided within the geometry kernel. This set is extensible; one only needs to derive a new shape from the base class and insure that it fits the pattern described below. Shapes are reference-counted objects, as described in Reference Counting.

In the table here below, a list of the existing geometrical shapes in the GeoModelKernel package is given:

Class Shape
GeoBox Box
GeoCons Cone Section
GeoEllipticalTube
GeoGenericTrap
GeoPara Parallelepiped
GeoPcon Polycone
GeoPgon Polygon
GeoSimplePolygonBrep
GeoTessellatedSolid
GeoTorus
GeoTrap Trapezoid (complex)
GeoTrd Trapezoid (simple)
GeoTube Tube
GeoTubs Tube Section
GeoTwistedTrap Twisted Trapezoid
GeoUnidentifiedShape

In addition to the geometrical shapes listed above, a set of boolean operator shapes is also added:

Class Operation
GeoShapeIntersection Intersection
GeoShapeShift Shift
GeoShapeSubtraction Subtraction
GeoShapeUnion Intersection

All shapes provide access to their geometry attributes (height, width, & cetera), and in addition perform several other services:

  • They calculate their volume
  • They can combine themselves using Boolean operations
  • They allow themselves to be identified through a built-in type identification scheme.

The volume calculation is an analytic calculation provided by each subclass.

One or more Boolean operation upon shapes creates a binary expression tree. This tree can be navigated later and the Boolean volumes declared to clients who can cope with them: GEANT4, notably, and certain visualization systems. Several Boolean operations may be combined in a single line of code:

GeoShape *A, *B, *C;
const GeoShape & D = (*A).add((*B) .subtract (*C));

A shape’s reference count is incremented either when the shape is used by a GeoLogVol, or in a Boolean expression. In the above example, D has been new’d, so has an unnamed temporary. The reference count of the temporary is incremented when it is combined with A to make D. When D’s reference count falls to zero, D is deleted, and the temporary is deleted.

Shapes can also be shifted about before they are used in a Boolean operation. The operation looks like this:

GeoShape *A, *B;
HepTransform3D offset;
const GeoShape & D = A->subtract (B<<offset);

The type identification scheme works by comparing the result of a static method with the result of a pure virtual method:

GeoShape *A;
if (A->typeId() == GeoBox::GetClassTypeId() ) {
    //
    // It’s a box!
    //
}

Both methods return a coded integer. When the class returns the same integer as the object, a match has occurred. Alternately one can use the methods getClassType() and type(), which return meaningful strings like “Box”, “Cons”… These are human-readable but slower to compare than unsigned integers.

All GeoShapes have the following interface:

// GeoShape
// Public Methods:
   virtual double volume () const
   const GeoShapeUnion & add ()  const
   const GeoShapeSubtraction & subtract(const GeoShape & shape) const
   const GeoShapeIntersection &   intersection(const GeoShape & shape) const
   const GeoShapeShift & operator << (const HepTransform3D &) const
   virtual const std::string & type () const
   virtual ShapeType typeID ()  const

//Static Public Methods
   const std::string & getClassType ()
   const ShapeType getClassTypeID ()

The classes GeoShapeShift, GeoShapeUnion, GeoShapeSubtraction, and GeoShapeIntersection are internal and should be considered for experts. We will not discuss them further.

We now present the interfaces to specific shapes. In general these shapes are by default constructed as symmetrically around the origin as possible.

GeoBox

// === GeoBox ===

   //Constructor:
   GeoBox (double XHalfLength, double YHalfLength, double ZHalfLength)

   //Public Methods:
   double getXHalfLength() const
   double getYHalfLength() const
   double getZHalfLength() const

The constructor fills the box with the \(x\), \(y\), and \(z\) half-lengths, and the accessors return those quantities.

The GeoBox shape
Figure: A GeoBox object, representing a rectangular prism or “box”.

GeoCons

// === GeoCons ===

   // Constructor:
   GeoCons (double RMin1, double RMin2,   
            double RMax1, double RMax2, 
            double DZ,  double SPhi,  double DPhi)

   // Public Methods:
   double getRMin1() const
   double getRMin2() const
   double getRMax1() const
   double getRMax2() const
   double getDZ() const
   double getSPhi() const
   double getDPhi() const

A GeoCons represents a cone section positioned with its axis along the \(z\) direction, and is specified by a starting value of \(\phi\), a total subtended angle in \(\phi\), a half-width in \(z\), and minimum and maximum values of radius at both extremities in \(z\). The constructor specifies the values of these constants, and the accessors return them.

The GeoCons shape
Figure: A GeoCons Object, representing a cone section.

GeoPara

// === GeoPara ===

  // Constructor:
  GeoPara (double XHalfLength, double YHalfLength, double ZHalfLength,
           double Alpha, double Theta, double Phi)

  // Public Methods:
  double getXHalfLength() const
  double getYHalfLength() const
  double getZHalfLength() const
  double getTheta() const
  double getAlpha() const
  double getPhi() const

The GeoPara class represents a parallelepiped. Faces at \(\pm z\) are parallel to the \(x-y\) plane. One edge of each of these faces is parallel to the \(x\)-axis, while the other edge makes an angle \(\alpha\) with respect to the \(y\)-axis. The remaining edge of the parallelepiped is oriented along a vector whose polar angle is \(\theta\) and whose azimuthal angle is \(\phi\). Half-lengths in \(x\), \(y\), and \(z\) describe the projections of the sides of the parallelepiped project onto the coordinate axes. The constructor fills these data, while the accessors return them.

Warning

Visualization of GeoPara is on the to-do list.

The GeoPara shape
Figure: GeoPara object, representing a parallelepiped.

GeoPcon

//=== GeoPcon ===

  // Constructor:
  GeoPcon (double SPhi, double DPhi)

  // Public Methods:
  void addPlane (double ZPlane, double RMinPlane, double RMaxPlane)
  double getSPhi() const
  double getDPhi() const
  unsigned int getNPlanes ()
  bool isValid () const
  const double & getZPlane (unsigned int i) const
  const double & getRMinPlane (unsigned int i) const
  const double & getRMaxPlane (unsigned int i) const

GeoPcon represents a polycone, which is a union of simple cone sections. The polycone subtends a fixed angle in \(\phi\) (DPhi) beginning at \(\phi_0\) (SPhi), and is specified at \(n\) locations in \(z\). At each \(z\) location, the inner and outer radius is given.

When the polycone is constructed, only \(\phi_0\) and \(\phi\) are given; then, the information at each \(z\) location is given, one plane at a time, by using the addPlane() method. At least two planes must be given, otherwise the polycone is not valid and methods such as volume() will fail and throw an exception. The isValid() method can be used to determine whether the polycone has at least two planes.

Note

A polycone (GeoPcon) with exactly two planes is equivalent geometrically to a cone section (GeoCons).

The GeoPcon shape
Figure: A GeoPcon object, representing a "polycone", which is a union of cone sections.

GeoPgon

//=== GeoPgon ===

  // Constructor:
  GeoPgon (double SPhi, double DPhi, unsigned int NSides)

  // Public Methods:
  double getSPhi() const
  double getDPhi() const
  unsigned int NSides() const
  unsigned int getNPlanes () const
  const double & getZPlane (unsigned int i) const
  const double & getRMinPlane (unsigned int i) const
  const double & getRMaxPlane (unsigned int i) const
  bool isValid () const
  void addPlane (double ZPlane, double RMinPlane, double RMaxPlane)

GeoPgon is similar to a GeoPcon (polycone). Like a GeoPcon it subtends a fixed angle in \(\phi\) (dPhi) beginning at \(\phi_0\) (sPhi), and is further specified by giving inner and outer radii at \(n\) locations in \(z\). However the GeoPgon object has a polygonal cross section, and the solid angle \(\phi\) is segmented into a fixed number of sides.

The constructor takes \(\phi\) , \(\phi_0\), and the number of sides in the cross-section as arguments; then, the information at each \(z\) location is given, one plane at a time, by using the addPlane() method. At least two planes must be given, otherwise the polygon is not valid and methods such as volume() will fail and throw an exception. The isValid() method can be used to determine whether the polygon has at least two planes.

Warning

Visualization of GeoPgon is on the to-do list.

The GeoPgon shape
Figure: GeoPgon object, representing a polycone with a polygonal cross section.

GeoTrap

//=== GeoTrap ===

  // Constructor:
  GeoTrap (double ZHalfLength, double Theta, double Phi, 
           double Dydzn, double Dxdyndzn, double Dxdypdzn,
           double Angleydzn, double Dydzp, double Dxdyndzp, 
           double Dxdypdzp, double Angleydzp)

  // Public Methods:
  double getZHalfLength() const
  double getTheta() const
  double getPhi() const
  double getDydzn() const
  double getDxdyndzn() const
  double getDxdypdzn() const
  double getAngleydzn() const
  double getDydzp() const
  double getDxdyndzp() const
  double getDxdypdzp() const
  double getAngleydzp() const

The GeoTrap class represents a very general kind of trapezoid. Two faces at \(\pm \Delta z\) (ZHalfLength) are parallel to each other and to the \(x-y\) plane. The centers of the faces are offset by a vector whose polar and azimuthal angles respectively are \(\theta\) and \(\phi\). At \(-\Delta z\), two edges parallel to the \(x\)-axis are offset by \(\pm \Delta y_n\) (Dydzn) from the face’s center, and these two faces have half-lengths of \(\Delta x_{\Delta y_n \Delta z_n}\) (Dxdyndzn) and \(\Delta x_{\Delta y_p \Delta z_n}\) (Dxdypdzn). The face at \(+\Delta z\) are similar: two edges parallel to the \(x\)-axis are offset by \(\pm \Delta y_p\) (Dydzp) from the face’s center, and these two faces have half-lengths of \(\Delta x_{\Delta y_n \Delta z_p}\) (Dxdyndzp) and \(\Delta x_{\Delta y_p \Delta z_p}\) (Dxdypdzp).

The two edges not parallel to the \(x\)-axis make an angle of \(\alpha_n\) (Angleydzn) and \(\alpha_p\) (Angleydzp) with respect to the \(y\)-axis on the bottom face (at \(-\Delta z\)) and the top face (at \(+\Delta z\)), respectively.

The constructor fills the GeoTrap with these values and the accessors return them.

The GeoTrap shape
Figure: A GeoTrap object, representing a very general kind of trapezoid.

GeoTrd

//=== GeoTrd ===

  // Constructor:
  GeoTrd ( double XHalfLength1, double XHalfLength2,
           double YHalfLength1, double YHalfLength2,
           double ZHalfLength)

  // Public Properties:
  double getXHalfLength1() const
  double getXHalfLength2() const
  double getYHalfLength1() const
  double getYHalfLength2() const
  double getYHalfLength() const

A GeoTrd is a simple trapezoid. Two faces at \(\pm \Delta z\) are parallel to each other and to the \(x-y\) plane, and each centered on the \(z\)-axis. At \(-\Delta z\) (\(+\Delta z\)), the half-length of the edges parallel to the \(x\)-axis is XHalfLength1 (XHalfLength2) and the half-length of the edges parallel to the \(y\)-axis is YHalfLength1 (YHalfLength2).

The constructor fills the object with these values and the accessors return them.

The GeoTrd shape
Figure: A GeoTrd object, representing a simple trapezoid.

GeoTube

//=== GeoTube ===

  // Constructor:
  GeoTube (double RMin, double RMax, double ZHalfLength)

  // Public Methods:
  double getRMin() const
  double getRMax() const
  double getZHalfLength() const

The GeoTube class represents a tube, specified by an inner radius (RMin), an outer radius (RMax) and a half-length in \(z\) (ZHalfLength).

The constructor fills these quantities and the accessors return them.

The GeoTube shape
Figure N: A GeoTube object, representing a tube or "pipe".

GeoTubs

//=== GeoTubs ===

  // Constructor:
  GeoTubs (double RMin, double RMax, double ZHalfLength, double SPhi, double DPhi)

  // Public Methods:
  double getRMin() const
  double getRMax() const
  double getZHalfLength() const
  double getSPhi() const
  double getDPhi() const

A GeoTubs is a tube section; a tube that subtends some plane angle (less than \(360^\circ\)) in \(\phi\). The GeoTubs is constructed by providing the inner radius (RMin), outer radius (RMax), half-length in \(z\) (ZHalfLength), as well as the starting \(\phi\) and \(\Delta \phi\).

Member functions provide access to these quantities.

The GeoTubs shape
Figure: A GeoTubs object, representing a tube section.

GeoTwistedTrap

//=== GeoTwistedTrap ===

  // Constructor:
  GeoTwistedTrap(double  PhiTwist,   // twist angle
                 double  Dz,     // half z length
                 double  Theta,  // direction between end planes
                 double  Phi,    // defined by polar and azim. angles
                 double  Dy1,    // half y length at -pDz
                 double  Dx1,    // half x length at -pDz,-pDy
                 double  Dx2,    // half x length at -pDz,+pDy
                 double  Dy2,    // half y length at +pDz
                 double  Dx3,    // half x length at +pDz,-pDy
                 double  Dx4,    // half x length at +pDz,+pDy
                 double  Alph    // tilt angle
                 )

  // Public Methods:
 double getY1HalfLength() const 
 double getX1HalfLength() const 
 double getX2HalfLength() const 
 double getY2HalfLength() const 
 double getX3HalfLength() const 
 double getX4HalfLength() const 
 double getZHalfLength()  const 
 double getPhiTwist()     const 
 double getTheta()        const 
 double getPhi()          const 
 double getTiltAngleAlpha() const 

The GeoTwistedTrap class represents a twisted trapezoid. Two faces at \(\pm \Delta z\) (Dz) are parallel to each other and to the \(x-y\) plane. The centers of the faces are offset by a vector whose polar and azimuthal angles respectively are \(\theta\) and \(\phi\). At \(-\Delta z\), two edges parallel to the \(x\)-axis are offset by \(\pm \Delta y_1\) (Dy1) from the face’s center, and these two faces have half-lengths of \(\Delta x_{1}\) (Dx1) and \(\Delta x_{2}\) (Dx2). The face at \(+\Delta z\) are similar: two edges parallel to the \(x\)-axis are offset by \(\pm \Delta y_2\) (Dy2) from the face’s center, and these two faces have half-lengths of \(\Delta x_{3}\) (Dx3) and \(\Delta x_{4}\) (Dx4).

The face at \(+ \Delta z\) is twisted with respect to the one at \(- \Delta z\) of an angle defined by \(\phi Twist\). The tilt angle \(\alpha\) defines the angle with respect to the y axis from the centre of the top and bottom trapezoids. It transforms the two trapezoids from isosceles to scalene.

The constructor fills the GeoTwistedTrap with these values and the accessors return them.

The GeoTwistedTrap shape
Figure: GeoTwistedTrap object, representing a twisted trapezoid.

Logical Volumes

GeoLogVol

//=== GeoLogVol ===

  // Constructor:
  GeoLogVol ( const std::string & Name, 
              const GeoShape * Shape, 
              const GeoMaterial * Material)

  // Public Methods:
  const std::string & getName () const
  const GeoShape * getShape () const
  const GeoMaterial * getMaterial () const

A GeoLogVol is an agglomeration of a name, a shape, and a material. These constituents are provided as arguments to the constructor, which increments the reference count of both the material and the shape. These reference counts are decremented when the GeoLogVol is destroyed.

The GeoLogVol provides const access to its constituents through the three access methods list above.

Physical Volumes

Physical volumes are objects that have a single logical volume and list of daughters. These daughters can have several types:

  • Physical volume.
  • Physical volume property nodes, such as name tags, or transformations.
  • Parametrizations of physical volumes.

These types of daughters are referred to collectively as GeoGraphNodes. Physical volumes and graph nodes are the building blocks of the geometry graph. The geometry graph is a specification of a physical volume tree, which, when traversed at a later time, appears to consist of physical volumes within other physical volumes.

Unlike other geometry modelers, in GeoModel physical volumes live within other physical volumes and not within logical volumes. This simplifies tree traversal.

classDiagram RCBase <|-- GeoGraphNode GeoGraphNode <|-- GeoNameTag GeoGraphNode <|-- GeoSerialDenominator GeoGraphNode <|-- GeoTransform GeoGraphNode <|-- GeoVPhysVol GeoGraphNode <|-- GeoSerialTransformer GeoTransform <|-- GeoAlignableTransform GeoVPhysVol <|-- GeoPhysVol GeoVPhysVol <|-- GeoVFullPhysVol GeoVFullPhysVol <|-- GeoFullPhysVol end

In GeoModel there are two main types of physical volumes, GeoPhysVol and GeoFullPhysVol. These in turn each have an interface class, GeoVPhysVol and GeoVFullPhysVol5. The user needs to be concerned only with the classes GeoPhysVol and GeoFullPhysVol. The former is generally used for nondescript pieces of detector geometry whose absolute position in space is not accessed often; while the latter is used typically for active detector components whose absolution position in space is used frequently within reconstruction, or digitization or hit creation. GeoFullPhysVol has a method for caching important information like absolute transformation of the piece with respect to the global coordinates, default transformation, and the name.

GeoPhysVol

// GeoPhysVol
  //Constructor:
    GeoPhysVol (const GeoLogVol * LogVol)
  //Public Methods:
    void add (GeoGraphNode * graphNode)
  //Public Methods from GeoVPhysVol
    bool isShared() const
    Query<unsigned int> indexOf (PVConstLink daughter) const
    PVConstLink getParent () const
    const GeoLogVol * getLogVol () const
    unsigned int getNChildVols () const
    PVConstLink  getChildVol (unsigned int index) const
    GeoTrf::Transform3D getXToChildVol (unsigned int index) const
    GeoTrf::Transform3D getDefXToChildVol (unsigned int index) const
    std::string getNameOfChildVol (unsigned int i) const
    Query<unsigned int> getIdOfChildVol() const
    void apply (GeoVolumeAction * action) const
    unsigned int getNChildVolAndST() const
    GeoTrf::Transform3D getX    (const GeoVAlignmentStore* store=nullptr) const
    GeoTrf::Transform3D getDefX (const GeoVAlignmentStore* store=nullptr) const
    unsigned int getNChildNodes() const
    const GeoGraphNode * const * getChildNode (unsigned int i) const
    const GeoGraphNode * const * findChildNode (const GeoGraphNode *n) const
  //Public Methods from GeoGraphNode
    void exec (GeoNodeAction * action)
    void dockTo (GeoVPhysVol* pv)

GeoPhysVol (const GeoLogVol * LogVol) Constructs the physical volume from the logical volume. The reference count of the logical volume is incremented, and will be decremented again when the physical volume is destroyed.

void add (GeoGraphNode * graphNode) Adds a graph node to the physical volume. The reference count of the graph node is incremented.

bool isShared() const Accessor to determine whether the physical volume is shared; i.e, used more than once in the geometry description.

Query<unsigned int> indexOf (PVConstLink daughter) const Accessor to determine the index of a daughter physical volume, in other words, in position within the list of daughters. The result only counts physical volumes as daughters, not their properties.

PVConstLink getParent () const Returns the parent physical volume. In case the parent is not unique (i.e., if the physical volume is shared), return NULL.

const GeoLogVol * getLogVol () const Returns the logical volume.

unsigned int getNChildVols () const Returns the number of child volumes. This includes only physical volumes and does not count property nodes. It does, however, include virtual physical volumes from a parametrization.

PVConstLink getChildVol (unsigned int index) const Returns the specified child volume.

GeoTrf::Transform3D getXToChildVol (unsigned int index) const Returns the transformation to the specified child volume. The transformation is relative to this object; it is not an absolute transformation to a global coordinate system.

GeoTrf::Transform3D getDefXToChildVol (unsigned int index) const Returns the default transformation of the specified child volume, relative to this object.

std::string getNameOfChildVol (unsigned int i) const Returns the name of the child volume, relative to this object.

Query<unsigned int> getIdOfChildVol() const Returns the identifier of the child volume.

void apply (GeoVolumeAction * action) const Applies a volume action to the volume. This action normally recursively visits each daughter volume.

unsigned int getNChildVolAndST() const Returns the number of child physical volumes and serial transformers.

GeoTrf::Transform3D getX (const GeoVAlignmentStore* store=nullptr) const Returns the transformation of the given physical volume to its parent physical volume. If the given physical volume is shared, then an exception is thrown.

GeoTrf::Transform3D getDefX (const GeoVAlignmentStore* store=nullptr) const Returns the default transformation of the given physical volume to its parent physical volume. If the given physical volume is shared, then an exception is thrown.

unsigned int getNChildNodes() const Returns the number of child graph nodes.

const GeoGraphNode * const * getChildNode (unsigned int i) const Returns the child graph node, relative to this object.

const GeoGraphNode * const * findChildNode (const GeoGraphNode *n) const Checks if the object contains child node n. If it does, then the function returns the pointer to n, otherwise it returns nullptr.

void exec (GeoNodeAction * action) Applies a node action to the volume. This action normally recursively visits each graph node in the geometry graph and includes handler functions even for property nodes.

void dockTo (GeoVPhysVol* pv) Caches pv as a pointer to the parent physical volume.

GeoFullPhysVol

// GeoFullPhysVol
  //Constructor:
    GeoFullPhysVol (const GeoLogVol * LogVol)
  //Public Methods:
    void add (GeoGraphNode * graphNode)
    GeoFullPhysVol* clone(bool attached=true)
    const GeoFullPhysVol* cloneOrigin() const
    void clear()
  //Public Methods from GeoVFullPhysVol
    const GeoTrf::Transform3D & getAbsoluteTransform () const
    const GeoTrf::Transform3D & getDefAbsoluteTransform () const
    void clearPositionInfo () 
    const std::string &  getAbsoluteName () const
    Query<unsigned int> getId () const
  //Public Methods from GeoVPhysVol
    bool isShared() const
    Query<unsigned int> indexOf (PVConstLink daughter) const
    PVConstLink getParent () const
    const GeoLogVol * getLogVol () const
    unsigned int getNChildVols () const
    PVConstLink  getChildVol (unsigned int index) const
    GeoTrf::Transform3D getXToChildVol (unsigned int index) const
    GeoTrf::Transform3D getDefXToChildVol (unsigned int index) const
    std::string getNameOfChildVol (unsigned int i) const
    Query<unsigned int> getIdOfChildVol() const
    void apply (GeoVolumeAction * action) const
    unsigned int getNChildVolAndST() const
    GeoTrf::Transform3D getX    (const GeoVAlignmentStore* store=nullptr) const
    GeoTrf::Transform3D getDefX (const GeoVAlignmentStore* store=nullptr) const
    unsigned int getNChildNodes() const
    const GeoGraphNode * const * getChildNode (unsigned int i) const
    const GeoGraphNode * const * findChildNode (const GeoGraphNode *n) const
  //Public Methods from GeoGraphNode
    void exec (GeoNodeAction * action)
    void dockTo (GeoVPhysVol* pv)

GeoFullPhysVol (const GeoLogVol * LogVol) Constructs the full physical volume from the logical volume. The reference count of the logical volume is incremented, and will be decremented again when the physical volume is destroyed.

void add (GeoGraphNode * graphNode) Adds a graph node to the full physical volume. The reference count of the graph node is incremented.

GeoFullPhysVol* clone(bool attached=true) Clones full physical volume, and returns a pointer to the cloned instance.

  • If the attached argument is true, then all cloned volumes are meant to stay identical to their clone origin for the entire lifetime. No further modifications are permitted either in origin, or its clones. I
  • If the attached argument is false, further modifications in clone origin and its clones are permitted.

const GeoFullPhysVol* cloneOrigin() const Returns a pointer to the full physical volume this object has been cloned from, otherwise returns nullptr.

void clear() Drop the tree under this node. This method breaks consistency of cloned volumes!

const GeoTrf::Transform3D & getAbsoluteTransform () const Returns the absolute transform with respect to global coordinate system.

const GeoTrf::Transform3D & getDefAbsoluteTransform () const Retuns the default absolute transform with respect to the global coordinate system.

void clearPositionInfo () Clears the position information cache. Users do not normally ever need to do this. The cache is automatically cleared by the geometry system whenever a change in alignment is detected.

const std::string & getAbsoluteName () const Returns the absolute name of the volume. This is a “/” separated string of names whose substrings represent physical volumes along the path from the world physical volume down to this volume.

Query<unsigned int> getId () const Returns an integer identifier for labeling purposes.

bool isShared() const Accessor to determine whether the full physical volume is shared; i.e, used more than once in the geometry description. Given full physical volumes cannot be shared, this method is expected to only return false.

Query<unsigned int> indexOf (PVConstLink daughter) const Accessor to determine the index of a daughter physical volume, in other words, in position within the list of daughters. The result only counts physical volumes as daughters, not their properties.

PVConstLink getParent () const Returns the parent physical volume. In case the parent is not unique (i.e., if the physical volume is shared), return NULL.

const GeoLogVol * getLogVol () const Returns the logical volume.

unsigned int getNChildVols () const Returns the number of child volumes. This includes only physical volumes and does not count property nodes. It does, however, include virtual physical volumes from a parametrization.

PVConstLink getChildVol (unsigned int index) const Returns the specified child volume.

GeoTrf::Transform3D getXToChildVol (unsigned int index) const Returns the transformation to the specified child volume. The transformation is relative to this object; it is not an absolute transformation to a global coordinate system.

GeoTrf::Transform3D getDefXToChildVol (unsigned int index) const Returns the default transformation of the specified child volume, relative to this object.

std::string getNameOfChildVol (unsigned int i) const Returns the name of the child volume, relative to this object.

Query<unsigned int> getIdOfChildVol() const Returns the identifier of the child volume.

void apply (GeoVolumeAction * action) const Applies a volume action to the volume. This action normally recursively visits each daughter volume.

unsigned int getNChildVolAndST() const Returns the number of child physical volumes and serial transformers.

GeoTrf::Transform3D getX (const GeoVAlignmentStore* store=nullptr) const Returns the transformation of the given physical volume to its parent physical volume. If the given physical volume is shared, then an exception is thrown.

GeoTrf::Transform3D getDefX (const GeoVAlignmentStore* store=nullptr) const Returns the default transformation of the given physical volume to its parent physical volume. If the given physical volume is shared, then an exception is thrown.

unsigned int getNChildNodes() const Returns the number of child graph nodes.

const GeoGraphNode * const * getChildNode (unsigned int i) const Returns the child graph node, relative to this object.

const GeoGraphNode * const * findChildNode (const GeoGraphNode *n) const Checks if the object contains child node n. If it does, then the function returns the pointer to n, otherwise it returns nullptr.

void exec (GeoNodeAction * action) Applies a node action to the volume. This action normally recursively visits each graph node in the geometry graph and includes handler functions even for property nodes.

void dockTo (GeoVPhysVol* pv) Caches pv as a pointer to the parent physical volume.

Transformations

Transformations (class GeoTransform and GeoAlignableTransform) are graph nodes that can be inserted into the geometry graph prior to the insertion of a physical volume. Transformations in the geometry graph are interpreted as affecting the position of the physical volume that follows. They do not affect the position of subsequent physical volumes.

No transformations are inserted before a physical volume, the physical volume remains at the center of its mother volume.

If one transformation is inserted, the physical volume is moved relative to the mother volume according to the transformation (which includes both a rotation and a translation).

If more than one transformation is inserted, then the composition of all transformations is applied to the physical volume. The last transformation to be added (the one closest to the physical volume) is applied first.

Instances of GeoTransform and GeoAlignableTransform may be shared within a geometry graph.

The two classes both provide access to a default transformation and to an actual transformation. The major difference between them is that these transformations may differ for the GeoAlignableTransform but not for the GeoTransform. In addition, the misalignments may be set or cleared for GeoAlignableTransform.

The full interface for these classes is shown below.

GeoTransform

//=== GeoTransform ===

  // Constructor:
  GeoTransform (const HepTransform3D & Transform)

  // Public Methods:
  HepTransform3D getTransform () const
  HepTransform3D getDefTransform () const
  const HepTransform3D * getDelta () const

  // Public Methods from GeoGraphNode
  void exec (GeoNodeAction * action)

GeoAlignableTransform

//=== GeoAlignableTransform ===

  // Constructor:
  GeoAlignableTransform (const HepTransform3D & Transform)

  // Public Methods:
  void setDelta (const HepTransform3D & Delta)
  void clearDelta ()

  // Public Methods from GeoTransform:
  HepTransform3D getTransform () const
  HepTransform3D getDefTransform () const
  const HepTransform3D * getDelta () const

  // Public Methods from GeoGraphNode:
  void exec (GeoNodeAction * action) 

Name Tags, Identifier Tags and Serial Denominators.

To minimize the use of memory, name information is not stored within physical volumes. In the case of a very large and complicated geometry, the need to denominate many millions of volumes is deemed largely unnecessary. However, certain pieces of the geometry are important and do deserve to be named. For these pieces, we have created two types of objects: name tags, and serial denominators.

Each physical volume is associated with a name, and with an absolution name. The name of the object is a simple string; while the absolute name is a “/” separated string that looks like a Unix directory: /WORLD/INNERDET/SCT/BARRELSUPPORT, for example. When a physical volume is placed into a tree, by default its name is ANONYMOUS.

The default name can be modified by placing a name tag into the geometry graph immediately prior to a physical volume. A name tag is applied only to the physical volume that is inserted after the name tag into the geometry graph; it does not affect subsequent physical volumes. The class representing a name tag is GeoNameTag:

GeoNameTag

//=== GeoNameTag ===

  // Constructors:
  GeoNameTag (const std::string & BaseName)

  // Public Methods:
  const std::string & getName() const

  // Public Methods from GeoGraphNode
  void exec (GeoNodeAction * action)

The interface is simple, consisting only of a constructor taking a name-string, and an accessor which retrieves it.

Another way of modifying the default name is through an object that automatically generates name strings for all subsequent volumes added to a specific physical volume.
This object is called a GeoSerialDenominator:

GeoSerialDenominator

//=== GeoSerialDenominator ===

  // Constructors:
  GeoSerialDenominator (const std::string & BaseName)

  // Public Methods:
  const std::string & getBaseName() const

  // Public Methods from GeoGraphNode
  void exec (GeoNodeAction * action)

Its interface is also simple: one constructor, and one accessor which retrieves the base name. If the base name is "BASE, then the serial denominator generates names like BASE0, BASE1, BASE2, etc.
This continues until the last child, or until a GeoNameTag is encountered, or until a new GeoSerialDenominator is encountered.

The generation of names using GeoNameTag and GeoSerialDenominator applies also to the virtual volumes when parameterization is used. If a name tag is used with parameterization, then all of the virtual physical volumes of the parameterization are given the name tag. In case a serial denominator is used, each parameterized volume is given a name consisting of the base name plus an additional serial number.

In many cases it is useful to assign an identifier, or “serial number”, to physical volumes. One use case is to enable a simulation engine such as GEANT46 to make a correspondence between a piece of geometry designated as sensitive, and the readout element corresponding to that piece. For such cases, we provide a GeoIdentifierTag, similar to a GeoNameTag. It provides a way of labeling physical volumes with an unsigned int. GeoIdentifierTag tags, however, apply only to the physical volume that immediately follows the tag in the geometry graph. Its interface follows:

GeoIdentifierTag

//=== GeoIdentifierTag ===

  // Constructors:
  GeoIdentifierTag (unsigned int id)

  // Public Methods:
  const std::string & getIdentifier() const

  // Public Methods from GeoGraphNode
  void exec (GeoNodeAction * action)

Parametrization

Introduction

A principle goal in the design of the geometry kernel has been to limit memory usage. A powerful way of doing this is to parameterize the volumes rather than to create them, say, inside of a single, double, or other multiple loop.

Parameterizations are mathematical recipes for creating volumes. There are three main ingredients to these recipes:

  • GENFUNCTIONS, which are mathematical function-objects; they allow one to perform function arithmetic in the same way that one performs floating point arithmetic.

  • TRANSFUNCTIONS, which, together with GENFUNCTIONS and HepTransform3D, allow one to expand and parametrize elements of the Euclidean group (i.e., rigid body transformations).

  • GeoSerialTransformer, a kind of GeoGraphNode that allows a particular physical volume to be placed according to a TRANSFUNCTION expansion of a rigid body transformation.

An example will demonstrate how this is useful. The example is taken from the actual ATLAS detector description, from the GeoModel description of the liquid argon barrel calorimeter:

#include "GeoGenericFunctions/AbsFunction.hh"
#include "GeoGenericFunctions/Variable.hh"
#include "GeoModelKernel/GeoXF.h"  
#include "GeoModelKernel/GeoSerialTransformer.h"

using namespace Genfun;
using namespace GeoXF;

// Define some constants:

GeoPhysVol  *pV, *world;
int          N;
double       c1, c2, r, z;

// Construct a simple linear function of a variable i:

Variable      i;
GENFUNCTION   g = c1+c2*i;

// Parameterize a transformation using this function:

TRANSFUNCTION xf = Pow(HepRotateZ3D(1),g)*
                   HepTranslateX3D(r)*
                   HepTranslateZ3D(z);

// Use the parametrized transformation to place a volume N times:

GeoSerialTransformer *st=new GeoSerialTransformer(pV, &xf, N);
world->add(st);

The file AbsFunction.hh, from GeoModel/GeoModelCore/GeoGenericFunctions—which has been adapted from the CLHEP package—, defines the interface to GENFUNCTIONS and must be included. In addition, if specific functions such as trig functions, higher transcendental functions or physics-specific functions are required, header files for these function-objects should be included from the same area.

The headers GeoXF.h and GeoSerialTransformer.h are needed for the classes TRANSFUNCTION and GeoSerialTransformer, respectively.

GENFUNCTIONS and TRANSFUNCTIONS both live within namespaces, which we access with the using statements near the top of the example. After defining the variables used in this example, we construct a simple linear function of an input variable i:

Variable    i;
GENFUNCTION g =  c1+c2*i;

This example is very simple, but shows already how we can use these classes to build symbolic expressions. A variety of functions lives already within GeoModel/GeoModelCore/GeoGenericFunctions. The set is user-extensible, and the extension procedure is amply described within the CLHEP GeoGenericFunctions package documentation. Addition, subtraction, multiplication, division, composition, and direct product operations are all valid.

The next step, in which TRANSFUNCTIONS are constructed, parametrizes the rigid body transformation. The TRANSFUNCTION, xf, has a function call operator that can be used to evaluate a particular rigid body transformation as a function of an input argument, like this:

HepTransform3D tx = xf(j);

The expansion of the TRANSFUNCTION is as follows. Let \(X_i (i = 1, 2, … N)\) represent any transformation, either a rotation, a translation, or even some combination of these. The rotations may be about a coordinate axis, or even some other axis. Furthermore, let us denote by \(f_i(x) ( \textrm{where}\ i =1, 2… N)\) a function of a single variable. Then, the expansion of an arbitrary function is:

\[ T(x) = X_1^{f_1(x)} * X_2^{f_2(x)} * X_3^{f_3(x)} \cdots X_n^{f_n(x)} \]

In this expression, \(T(x)\) is the resulting transformation, which is now a function of the single input parameter, \(x\). The expansion is both simple, and completely general. A single term in this expansion (for example \(X_2^{f_2(x)}\)), will be referred to as an exponentiated transformation. It is implemented in terms of the class Pow, which has the following constructor:

Pow(const HepTransform3D &, GENFUNCTION f);

Exponentiated transformations are simple transfunctions, and can be composed to make other TRANSFUNCTIONS. The TRANSFUNCTION interface also allows one to compose fixed transformations with exponentiated transformations.

The interface to GENFUNCTION and TRANSFUNCTION provide all the necessary operations on these types of object. The interfaces to these types of objects are not simple to read, so we will not attempt to explain them in this document. Instead, one should assume that all well-defined mathematical properties that apply to functions are properties of GENFUNCTIONS, and all mathematical properties that apply to parameterizations of elements of the Euclidean group are properties of TRANSFUNCTIONS.

Once one has a a TRANSFUNCTION in hand, it can be used together with a GeoSerialTransformer object to repeatedly place a physical volume. To do this, use the following constructor:

GeoSerialTransformer(const GeoPhysVol *pVol,
                     TRANSFUNCTION xf,
                     unsigned int N);

In this constructor, pVol is the volume to be repeatedly placed, xf is the TRANSFUNCTION that specifies how to place it, and N is the desired number of copies.

The GeoSerialTransformer can then be added to the geometry graph. During any subsequent volume traversal, the geometry graph will appear to contain multiple physical volumes at different locations. However, only the memory of a single physical volume and a TRANSFUNCTION is actually allocated.

During node traversal (using GeoNodeActions) one can recover, from the geometry graph, the actual recipe for generating volumes. This is sometimes useful; for example, in case one wishes to create a GEANT4 parameterization from a GeoModel parameterization.

Note

One further word about parameterizations is in order: parameterizations, as they are usually understood, allows for the shape or composition of an object to vary as a function of copy number. This is presently not a part of the GeoModelKernel.

However, we intend to include this in subsequent releases. The basic design strategy is to start with a concrete shape class, such as GeoBox, and to use this a basis of a new class for parameterizing boxes. In the new class—call it GeoBoxParameterization—we replace all of the floating point member data with GENFUNCTION member data, and all of the floating point constructor arguments with GENFUNCTION constructor arguments. In this way we create a very flexible recipe for generating a box.

The same technique can be used to vary an objects composition as a function of copy number.

GeoSerialTransformer

//=== GeoSerialTransformer ===

  // Constructors:
  GeoSerialTransformer (const GeoVPhysVol * volume, const GeoXF::Function * func, unsigned int copies)

  // Public Methods:
  HepTransform3D getTransform (int  i) const
  unsigned int getNCopies() const
  PVConstLink getVolume() const;

  // Public Methods from GeoGraphNode
  void exec (GeoNodeAction * action)

Actions

The principle way of accessing the physical volume tree is through actions which facilitate a recursive tree traversal and accumulate certain volatile information, such as a volume’s absolute position, during the traversal. An action is applied to a node in the tree, which applies it in turn to its children.

One type of action, GeoNodeAction, is passed to all children. Another type, GeoVolumeAction, is only passed only to daughter physical volumes (this includes “virtual” physical volumes that come from parameterizations). Casual users should consider subclassing GeoVolumeAction in order to recurse through all of the physical volumes in the geometry graph. “Power users” will occasionally need to access the geometry graph directly, in order to visit nodes such as GeoSerialTransformer nodes. Such users should consider subclassing GeoNodeAction. This section describes the two types of existing actions in more detail.

Volume Actions and associated classes

GeoVolumeAction

GeoVolumeAction is a base class specifically designed for user subclassing, and all users are encouraged to use it for data access.

Its interface is here:

//=== GeoVolumeAction ===

  // Enumerated Types:
  enum GeoVolumeAction::Type {TOP_DOWN, BOTTOM_UP}

  // Constructor:
  GeoVolumeAction (Type type  = TOP_DOWN)

  // Public Methods:
  void handleVPhysVol (const GeoVPhysVol * )
  void terminate ()
  const GeoTraversalState * getState () const

In order to subclass this, follow this checklist:

  1. Write a class inheriting from GeoVolumeAction. You should decide whether your class should walk through the volumes from the top down, which is the default, or from the bottom up. If you want a bottom up tree traversal you need to initialize the base class, accordingly, in the constructor for your new class.

  2. Override the handleVPhysVol() method in order to do something with each volume you encounter during geometry graph traversal. You may, of course, add member data or additional methods in order to carry out the action, or to access results.

  3. If you wish for the action to hit every node in the tree, you don’t need to do anything special. If you wish it to terminate early, call the terminate() method from with your handleVPhysVol(). Geometry graph traversal will immediately terminate.

  4. The action keeps track of its traversal state, accumulating such information as absolute transformation to the current node, and the path to the current node. The state can be retrieved from the getState() method. This may be called from with your handleVPhysVol() method in order to, say, find out where you are in global coordinates.

A GeoVolumeAction upon a tree of physical volumes is initiated when the following line of code is invoked:

GeoPhysVol        *vol;
MyGeoVolumeAction action;
vol->apply(&action);

Your handleVPhysVol() routine can obtain information about the current node from two sources: first, from the physical volume itself, which is passed as an argument to the routine, and secondly from the state (class GeoTraversalState) which is available from the getState() method. We examine that next.

GeoTraversalState

//=== GeoTraversalState ===

  // Const Public Methods:
  const HepTransform3D & getTransform () const
  const std::string & getName () const
  Query<unsigned int> getId() const
  const HepTransform3D & getDefTransform () const
  const std::string & getAbsoluteName () const
  const HepTransform3D & getDefAbsoluteTransform () const
  const HepTransform3D & getAbsoluteTransform () const
  const GeoNodePath * getPath () const

The interface is simple and self-explanatory; only the getPath() method and the information it returns needs further explanation. The path (class GeoNodePath) is an ordered stack of nodes that shows how the current node was reached.

Its interface is shown here:

GeoNodePath

//== GeoNodePath ==

  // Public Methods:
  unsigned int getLength ()
  const GeoVPhysVol * getItem(unsigned int i)
  const GeoVPhysVol * getHead ()
  const GeoVPhysVol * getTail ()

The head node, or node from which the action was initiated, and tail node, or last node in the path, are available. The total length of the path can be retrieved, as well as arbitrary item along the path.

TemplateVolAction

TemplateVolAction is a class which has been added only as an illustration for how to write volume actions. The header file is shown here below:

#ifndef TemplateVolAction_h
#define TemplateVolAction_h 1

class TemplateVolAction:public GeoVolumeAction  
{

public:

  // Constructor
  TemplateVolAction ();

  // Destructor
  ~TemplateVolAction ();

  // Action routine
  virtual void handleVPhysVol (const GeoVPhysVol *);

};
#endif

while the source file is shown in the following:

#include "GeoModelKernel/TemplateVolAction.h"

TemplateVolAction::TemplateVolAction ()
:GeoVolumeAction (GeoVolumeAction::TOP_DOWN)
{
}

TemplateVolAction::~TemplateVolAction ()
{
}

void TemplateVolAction::handleVPhysVol (const GeoVPhysVol *)
{
  /* 
   * Your procedure here.  This one does nothing...
   */
}

Node Actions

GeoNodeAction classes do more than traverse the tree of physical volumes. These actions stop and execute on every graph node in the geometry graph, not the physical volumes represented within or generated by the geometry graph. Three kinds of node actions are used internally by the kernel:

  • GeoCountVolAction,
  • GeoAccessVolumeAction,
  • GeoClearAbsPosAction.

GeoCountVolAction counts the number of physical volumes below some node, down do a depth. GeoAccessVolumeAction is for retrieving a particular volume. GeoClearAbsPosAction can be used to invalidate a cache of absolute position information below some node. The first two have potential usefulness outside of the geometry kernel itself, and will be discussed below.

The following methods are available on all GeoNodeAction classes, and control the depth limit:

  • void setDepthLimit(int limit);
  • Query<unsigned int> getDepthLimit() const;
  • void clearDepthLimit(),

Specific GeoNodeAction classes may set specific defaults for their depth limits. See the documentation on these actions.

GeoCountVolAction

//== GeoCountVolAction ==

  // Constructor:
  GeoCountVolAction ();

  // Public Methods:
  const  unsigned int getCount() const;

When this action is executed upon a node it counts daughter volumes. The count includes only physical volumes. It contains virtual, or parameterized volumes as well as actual volumes. The depth limit for this action by default is 1: only the volumes directly beneath the top volume are counted. The final count does not include the top volume, only the children. The depth limit can be reset or cleared using the methods clearDepthLimit() or setDepthLimit() from the base class.

Here is a typical use case:

GeoPhysVol *world;
GeoCountVolAction cv;
cv->setDepthLimit(2);
world->exec(&cv);
int count = cv.getCount ();

GeoAccessVolumeAction

//== GeoAccessVolumeAction ==

  // Constructor:
  GeoAccessVolumeAction (unsigned int Index)

  // Public Methods:
  PVConstLink getVolume () const
  const HepTransform3D & getTransform () const
  const HepTransform3D & getDefTransform () const
  const std::string & getName () const
  Query<unsigned int> getId() const

GeoAccessVolumeAction is used to retrieve physical volume and some of its properties from within the geometry tree.

The constructor needs to provide the index of the child volume which is sought. The public methods can be used once the action has executed and return a link to the daughter volume, the transform to the daughter, the default transform to the daughter, and the name of the daughter.

A typical use case is shown here:

int index
GeoFullPhysVol *vol;
GeoAccessVolumeAction av (index);
vol->exec (&av);
std::string name = av->getName();

Besides the name, the transformations (default and misaligned), can be retrieved along with the volume. The action executes to a depth of 1; i.e. it is used only for accessing direct descendents.

A simpler alternative to accessing daughter volumes in this way is to use the methods in GeoVPhysVol and subclasses to retrieve information about their daughters. This is simpler, and uses the action internally. Using the action directly, however, can be faster if accessing different kinds of information at the same time since the volume location can be performed just once.

For Power Users: How to Make Your Own GeoNodeAction

The basic structure of the GeoModel is: a geometry graph which emulates a physical volume tree. The graph is constructed by adding a combination of physical volumes, transformations, name tags, serial denominators and serial transformers into physical volumes. The information is usually retrieved by scanning the physical volume tree as opposed to the geometry graph. The class GeoVolumeAction exists in order to be subclassed by users for this purpose.

However, in certain more rare cases users will need to navigate the geometry graph directly. For example, when declaring the geometry to the simulation, one may wish to translate GeoModel parametrizations from GeoSerialTransformer nodes into G4Parametrizations. In that case one is interested in accessing the parametrization directly and not the virtual volumes that they generate. These ‘’power users’‘ can access all nodes in the geometry tree by subclassing GeoNodeAction. In this subsection, we examine how to do this.

GeoNodeAction

GeoNodeAction has the following interface:

//== GeoNodeAction ==

  // Constructor:
  GeoNodeAction()

  // Public Methods:
  GeoNodePath * getPath () const
  Query<unsigned int>  getDepthLimit () const
  void terminate ()
  bool shouldTerminate () const
  void setDepthLimit (unsigned int limit)
  void clearDepthLimit ()

  // Virtual Public Methods to be overridden in subclasses:
  void handleNode (const GeoGraphNode * )
  void handleTransform (const GeoTransform * )
  void handlePhysVol (const GeoPhysVol * )
  void handleFullPhysVol (const GeoFullPhysVol * )
  void handleNameTag (const GeoNameTag * )
  void handleIdentifierTag(const GeoIdentifierTag *)
  void handleSerialDenominator (const GeoSerialDenominator * )
  void handleSerialTransformer (const GeoSerialTransformer  * )

When subclassing the GeoNodeAction, the principle task is to write a handle method for each type of GeoGraphNode object that you wish to visit. These methods are shown in the above table under the rubric “Virtual Public Methods to be overridden in subclasses”. They are called in sequence each time a specific kind of graph node is encountered, from top to bottom.

In addition, the action can specify its own depth by calling the methods setDepthLimit() and clearDepthLimit(). In addition the action can be terminated by any time by calling the terminate() method; usually this should be done within one of the handler methods. The path (class GeoNodePath, discussed in section 2.8.1) can also be accessed from anywhere within the class, most notably within the handler methods.

Base classes for subsystem description

The geometry kernel contains three base classes for subsystem description. These provide require a minimum amount of functionality from subclasses—just enough to function within a reasonable framework. The classes are: GeoVDetectorElement, an abstract base class for a separately-alignable piece of a detector subsystem, GeoVDetectorFactory, an abstract base class for an algorithm which builds the geometry, including the detector elements and other pieces of nondescript support material, and a GeoVDetectorManager, which is stored in the transient detector store and provides access to the geometry, both material geometry and readout geometry. Both classes should be extended by subsystems people to provide a bona fide readout geometry interface.

The interfaces to these classes are shown here:

GeoVDetectorElement

//== GeoVDetectorElement ==

  // Constructor:
  GeoVDetectorElement (const GeoVFullPhysVol * fullPhysVol)

  // Public Methods:
  const GeoVFullPhysVol * getMaterialGeom () const

  // Virtual Public Methods to be overridden by subclasses:
  Identifier identify() const;

The constructor for a GeoVDetectorElement requires a full physical volume, which should be unique and which should live under a unique branch. This is how all detector elements know where they are.

The getMaterialGeom() method provides access to the full physical volume. This is mostly for subclasses to use, when they compute derived position information.

The identify() method provides an identifier for the volume.

Note

Note that the coupling to the actual identification scheme is loose because the identify() method is pure virtual and the Identifier class is forward-declared.

GeoVDetectorFactory

//== GeoVDetectorFactory ==

  // Constructor:
  GeoVDetectorFactor()

  // Virtual Public Methods to be overridden by subclasses:
  void create (GeoPhysVol * world)
  const GeoVDetectorManager *getDetectorManager() const;

GeoVDetectorFactories create geometry and map it into readout geometry. For the simulation, access to the raw geometry is required. The interface to GeoVDetectorFactory contains two pure virtual functions which must be implemented in the subclass.

The method create (GeoPhysVol * world) creates the material and readout geometry, and attaches the raw geometry to the physical volume tree, under the world volume, which is passed in from the calling routine.

The method getDetectorManager() returns a detector manager, which contains all of the constructed geometry. It is permissible to return a pointer to a subclass of GeoVDetectorManager (the so-called covariant return type mechanism). This is proper C++ and the correct way of avoiding dynamic casting.

GeoVDetectorManager

//== GeoVDetectorManager ==

  // Constructor:
  GeoVDetectorManager()

  // Virtual Public Methods to be overridden by subclasses:
  unsigned int getNumTreeTops () const
  PVConstLink getTreeTop (unsigned int i) const

GeoVDetectorManagers hold the results of geometry construction and are stored in the transient detector store. For the simulation, access to the raw geometry is required. Experience has shown that subsystems cannot be cleanly implemented in terms of a single, top-level volume; instead several volumes must exist at the top level, for topological reasons, hence the iterative access to top level volumes, or tree-tops. The interface to GeoVDetectorManager contains several pure virtual functions which must be implemented in the subclass.

The method getNumTreeTops() returns the number of top-level physical volumes in the raw geometry for the subsystem.

The method getTreeTop(unsigned int i) returns the \(i^{th}\) top-level physical volume for the subsystem.

Appendix: The Query template class.

The template class Query<T> is designed as the return type of a query that can fail. One place we use it within this library is to return the index of a daughter volume, in other words, its position within a child list. If the volume is not found within the daughter list, the query fails. The failure could be handled in several ways, one of which would be to return a value of -999. If the user were to blithely use this value without checking it first, the program could likely crash immediately or misbehave later.

The class Query<unsigned int> fixes ths problem. Because it has a constructor taking unsigned int as a single argument and a cast operator, it can freely convert itself to and from an unsigned int. So you can write:

GeoPhysVol *parent, *child;
unsigned int c = parent->getIndex(child);

If the function succeeds, it returns Query<unsigned int> and a conversion takes place. But, if it fails, the conversion itself throws an exception, in this case std::range_error.

One does not need to handle the exception in order to proceed past the failed query, a better way is to check the return value, and this can be done by recoding the example as follows:

GeoPhysVol *parent, *child;
Query<unsigned int> c = parent->getIndex(child);
if (c.isValid()) {
    // Now use c like an ordinary unsigned integer:
    unsigned int d = c+1;
}

Now, this kind of checking is safer, but can always be skipped if the operation is guaranteed to succeed, for example, if the programmer knows the child is in the daughter list (because she put it there). The class Query<T> is based on the class Fallible<T> from reference7.


  1. http://pdg.lbl.gov/ 

  2. The atomic weight should be specified using units such as g/cm3 (also known as CLHEP4 units). 

  3. The density is normally specified using GeoModel units, ported from CLHEP4. The native unit of mass is the MeV, the native unit for length is the mm. A material with a density of 1 g/cm3 has a density of 1.7 x 10-22, in these units. 

  4. CLHEP - A Class Library for High Energy Physics, https://proj-clhep.web.cern.ch/proj-clhep/ 

  5. The reason for the interface classes is to provide a hook for virtual physical volumes which use recipes to generate children that do not actually exist permanently in memory. So far this has not been necessary to achieve parameterization, but we do not for now rule out the need for an interface class. 

  6. https://geant4.web.cern.ch/ 

  7. JJ.Barton and LR.Nackmann, Scientific and Engineering C++, 1994, Addison Wesley