Chapter 3.  Extending Toolkit Functionality

3.1.  Geometry

3.1.1.  What can be extended ?

Geant4 already allows a user to describe any desired solid, and to use it in a detector description, in some cases, however, the user may want or need to extend Geant4's geometry. One reason can be that some methods and types in the geometry are general and the user can utilise specialised knowledge about his or her geometry to gain a speedup. The most evident case where this can happen is when a particular type of solid is a key element for a specific detector geometry and an investment in improving its runtime performance may be worthwhile.

To extend the functionality of the Geometry in this way, a toolkit developer must write a small number of methods for the new solid. We will document below these methods and their specifications. Note that the implementation details for some methods are not a trivial matter: these methods must provide the functionality of finding whether a point is inside a solid, finding the intersection of a line with it, and finding the distance to the solid along any direction. However once the solid class has been created with all its specifications fulfilled, it can be used like any Geant4 solid, as it implements the abstract interface of G4VSolid.

Other additions can also potentially be achieved. For example, an advanced user could add a new way of creating physical volumes. However, because each type of volume has a corresponding navigator helper, this would require to create a new Navigator as well. To do this the user would have to inherit from G4Navigator and modify the new Navigator to handle this type of volumes. This can certainly be done, but will probably be made easier to achieve in the future versions of the Geant4 toolkit.

3.1.2.  Adding a new type of Solid

We list below the required methods for integrating a new type of solid in Geant4. Note that Geant4's specifications for a solid pay significant attention to what happens at points that are within a small distance (tolerance, kCarTolerance in the code) of the surface. So special care must be taken to handle these cases in considering all different possible scenarios, in order to respect the specifications and allow the solid to be used correctly by the other components of the geometry module.

Creating a derived class of G4VSolid

The solid must inherit from G4VSolid or one of its derived classes and implement its virtual functions.

Mandatory member functions you must define are the following pure virtual of G4VSolid:

 EInside Inside(const G4ThreeVector& p)
 G4double DistanceToIn(const G4ThreeVector& p)
 G4double DistanceToIn(const G4ThreeVector& p, const G4ThreeVector& v)
 G4ThreeVector SurfaceNormal(const G4ThreeVector& p)
 G4double DistanceToOut(const G4ThreeVector& p)
 G4double DistanceToOut(const G4ThreeVector& p, const G4ThreeVector& v,
                        const G4bool calcNorm=false,
                              G4bool *validNorm=0, G4ThreeVector *n)
 G4bool CalculateExtent(const EAxis pAxis,
                        const G4VoxelLimits& pVoxelLimit,
                        const G4AffineTransform& pTransform,
                              G4double& pMin,
                              G4double& pMax) const
 G4GeometryType GetEntityType() const
 std::ostream& StreamInfo(std::ostream& os) const

They must perform the following functions

  EInside Inside(const G4ThreeVector& p)

This method must return:

  • kOutside if the point at offset p is outside the shape boundaries plus Tolerance/2,
  • kSurface if the point is <= Tolerance/2 from a surface, or
  • kInside otherwise.

G4ThreeVector SurfaceNormal(const G4ThreeVector& p)

Return the outwards pointing unit normal of the shape for the surface closest to the point at offset p.

G4double DistanceToIn(const G4ThreeVector& p)

Calculate distance to nearest surface of shape from an outside point p. The distance can be an underestimate.

G4double DistanceToIn(const G4ThreeVector& p, const G4ThreeVector& v)

Return the distance along the normalised vector v to the shape, from the point at offset p. If there is no intersection, return kInfinity. The first intersection resulting from `leaving' a surface/volume is discarded. Hence, this is tolerant of points on surface of shape.

G4double DistanceToOut(const G4ThreeVector& p)

Calculate distance to nearest surface of shape from an inside point. The distance can be an underestimate.

G4double DistanceToOut(const G4ThreeVector& p, const G4ThreeVector& v,
                        const G4bool calcNorm=false,
                        G4bool *validNorm=0, G4ThreeVector *n=0);

Return distance along the normalised vector v to the shape, from a point at an offset p inside or on the surface of the shape. Intersections with surfaces, when the point is not greater than kCarTolerance/2 from a surface, must be ignored.

If calcNorm is true, then it must also set validNorm to either

  • true, if the solid lies entirely behind or on the exiting surface. Then it must set n to the outwards normal vector (the Magnitude of the vector is not defined).
  • false, if the solid does not lie entirely behind or on the exiting surface.

If calcNorm is false, then validNorm and n are unused.

G4bool CalculateExtent(const EAxis pAxis,
                        const G4VoxelLimits& pVoxelLimit,
                        const G4AffineTransform& pTransform,
                              G4double& pMin,
                              G4double& pMax) const

Calculate the minimum and maximum extent of the solid, when under the specified transform, and within the specified limits. If the solid is not intersected by the region, return false, else return true.

G4GeometryType GetEntityType() const;

Provide identification of the class of an object (required for persistency and STEP interface).

std::ostream& StreamInfo(std::ostream& os) const

Should dump the contents of the solid to an output stream.

The method:

G4VSolid* Clone() const

should be implemented for every solid to provide a way to clone themselves in a new object with same specifications.

The method:

G4ThreeVector GetPointOnSurface() const

returns a random point located on the surface of the solid. Points returned should not necessarily be uniformly distributed.

The method:

G4double GetCubicVolume()

should be implemented for every solid in order to cache the computed value (and therefore reuse it for future calls to the method) and to eventually implement a precise computation of the solid's volume. If the method will not be overloaded, the default implementation from the base class will be used (estimation through a Monte Carlo algorithm) and the computed value will not be stored.

The method:

G4double GetSurfaceArea()

should be implemented for every solid in order to cache the computed value (and therefore reuse it for future calls to the method) and to eventually implement a precise computation of the solid's surface area. If the method will not be overloaded, the default implementation from the base class will be used (estimation through a Monte Carlo algorithm) and the computed value will not be stored.

There are a few member functions to be defined for the purpose of visualisation. The first method is mandatory, and the next four are not.

  // Mandatory
  virtual void DescribeYourselfTo (G4VGraphicsScene& scene) const = 0;

  // Not mandatory
  virtual G4VisExtent GetExtent() const;
  virtual G4Polyhedron* CreatePolyhedron () const;
  virtual G4NURBS*      CreateNURBS      () const;
  virtual G4Polyhedron* GetPolyhedron    () const;

What these methods should do and how they should be implemented is described here.

 void DescribeYourselfTo (G4VGraphicsScene& scene) const;

This method is required in order to identify the solid to the graphics scene. It is used for the purposes of ``double dispatch''. All implementations should be similar to the one for G4Box:

void G4Box::DescribeYourselfTo (G4VGraphicsScene& scene) const
{
  scene.AddSolid (*this);
}

The method:

 G4VisExtent GetExtent() const;

provides extent (bounding box) as a possible hint to the graphics view. You must create it by finding a box that encloses your solid, and returning a VisExtent that is created from this. The G4VisExtent must presumably be given the minus x, plus x, minus y, plus y, minus z and plus z extents of this ``box''. For example a cylinder can say

G4VisExtent G4Tubs::GetExtent() const
{
  // Define the sides of the box into which the G4Tubs instance would fit.
  return G4VisExtent (-fRMax, fRMax, -fRMax, fRMax, -fDz, fDz);
}

The method:

 G4Polyhedron* CreatePolyhedron () const;

is required by the visualisation system, in order to create a realistic rendering of your solid. To create a G4Polyhedron for your solid, consult G4Polyhedron.

While the method:

 G4Polyhedron* GetPolyhedron () const;

is a ``smart'' access function that creates on requests a polyhedron and stores it for future access and should be customised for every solid.

3.1.3.  Modifying the Navigator

For the vast majority of use-cases, it is not indeed necessary (and definitely not advised) to extend or modify the existing classes for navigation in the geometry. A possible use-case for which this may apply, is for the description of a new kind of physical volume to be integrated. We believe that our set of choices for creating physical volumes is varied enough for nearly all needs. Future extensions of the Geant4 toolkit will probably make easier exchanging or extending the G4Navigator, by introducing an abstraction level simplifying the customisation. At this time, a simple abstraction level of the navigator is provided by allowing overloading of the relevant functionalities.

Extending the Navigator

The main responsibilities of the Navigator are:

  • locate a point in the tree of the geometrical volumes;
  • compute the length a particle can travel from a point in a certain direction before encountering a volume boundary.

The Navigator utilises one helper class for each type of physical volume that exists. You will have to reuse the helper classes provided in the base Navigator or create new ones for the new type of physical volume.

To extend G4Navigator you will have then to inherit from it and modify these functions in your ModifiedNavigator to request the answers for your new physical volume type from the new helper class. The ModifiedNavigator should delegate other cases to the Geant4's standard Navigator.

Replacing the Navigator

Replacing the Navigator is another possible operation. It is similar to extending the Navigator, in that any types of physical volume that will be allowed must be handled by it. The same functionality is required as described in the previous section.

However the amount of work is probably potentially larger, if support for all the current types of physical volumes is required.

The Navigator utilises one helper class for each type of physical volume that exists. These could also potentially be replaced, allowing a simpler way to create a new navigation system.