Geometry

In principle, a dgcode geometry module can use completely normal Geant4 code to construct the geometry, inside the Construct() method of the associated geometry class. The only constraint is that it must return the world volume (the physical volume into which all other volumes are placed directly or indirectly).

//Include whatever Geant4 header files here you want:
#include "G4Box.hh"

G4VPhysicalVolume* GeoTricorder::Construct()
{
  // ... construct your Geant4 geometry in whatever manner you wish here...
  //
  // And return the world volume inside which all other volumes are placed:
  //
  return myWorld;
}

It is beyond the scope of the present page to cover all aspects and possibilities for how one can construct a geometry in Geant4, and it is recommended to also spend some time reading about geometry defintions in the Geant4 documentation (note that for historical reasons geometry definition is often referred to as “Detector definition” in Geant4). At the very least, it is important to have a clear understanding of the difference between the Geant4 concepts of a solid (a geometrical shape like a box or cylinder), a logical volume (a solid which also has a material defined), and a physical volume (a logical volume which has been placed into a mother logical volume in a particular position). It is also important to understand how a geometry is built up in a tree structure, with daughter volumes inside their mother volumes, and the world volume being at the root of this tree.

While developing a geometry, you most certainly will want to visualise the effect of the code you are developing. Visualisation of geometry can for instance be performed by supplying a --viewer flag to any sim-script or simply via the with the sb_g4utils_geodisplay command. Refer to the dedicated page for details about how to use the visualisation tools.

As concerns creation of G4Material objects, dgcode provides a handy way to set these up from simple strings (cf. an intro here and the list of examples in the cookbook). Thus, assuming you have found the relevant string to use for your material in the cookbook, you can easily create a material:

auto mat_Al = getMaterial("stdlib::Al_sg225.ncmat;temp=250K");
auto mat_Gas = getMaterial("gasmix::0.7xAr+0.3xCO2/1.5atm/250K");
//mat_Al and mat_Gas are now pointers to G4Material objects.

Unless you plan to define a completely hardcoded and static geometry, you most likely wish to expose one or more parameters of your simulation. This could be anything from the thickness of a particular component, the number of gadgets, or even the material that goes inside some particular component. The way to do this is to define the parameters and their default values in the constructor of your geometry class, and then query them inside the ::Construct() method as needed. We can see an example of how this is done in the TriCorder example:

#include "G4Interfaces/GeoConstructPyExport.hh"

namespace {
  class GeoTriCorder final : public G4Interfaces::GeoConstructBase {
  public:
    GeoTriCorder();
    G4VPhysicalVolume* Construct() override;
  };
}

PYTHON_MODULE( mod )
{
  GeoConstructPyExport::exportGeo<GeoTriCorder>(mod, "GeoTriCorder");
}

#include "G4Box.hh"
#include "G4Orb.hh"

GeoTriCorder::GeoTriCorder()
  : GeoConstructBase("G4GeoTriCorder/GeoTriCorder")
{

  addParameterDouble("sample_posz_mm",5.0);
  addParameterDouble("sample_radius_mm",5.0);
  addParameterDouble("detector_size_cm",50.0);
  addParameterDouble("detector_sample_dist_cm",10.0);
  addParameterString("material_sample","stdlib::Al_sg225.ncmat");
  addParameterString("material_lab",
                     "gasmix::0.7xAr+0.3xCO2/2.0atm/293.15K");

}

G4VPhysicalVolume* GeoTriCorder::Construct()
{
  const double sample_posz = getParameterDouble("sample_posz_mm")*Units::mm;
  const double sample_radius = getParameterDouble("sample_radius_mm")*Units::mm;
  const double det_size = getParameterDouble("detector_size_cm")*Units::cm;
  const double det_depth = 1.0*Units::cm;//ok to hardcode non-interesting parameters
  const double det_sample_dist = getParameterDouble("detector_sample_dist_cm")*Units::cm;

  auto mat_sample = getParameterMaterial("material_sample");
  auto mat_lab = getParameterMaterial("material_lab");
  auto mat_det = getMaterial("Vacuum");

  const double dz_world = 1.001 * ( std::abs<double>(sample_posz)
                                    + sample_radius+det_depth+det_sample_dist );
  const double dxy_world = 1.001 * std::max<double>(0.5*det_size,sample_radius);
  auto worldvols = place( new G4Box("World", dxy_world, dxy_world, dz_world),
                          mat_lab );
  auto lvWorld = worldvols.logvol;

  place( new G4Orb("Sample",sample_radius),
         mat_sample,0,0,sample_posz,lvWorld,YELLOW );

  place(new G4Box("Detector",0.5*det_size,0.5*det_size,0.5*det_depth),mat_det,
        0,0,sample_posz+det_sample_dist+0.5*det_depth,lvWorld,RED);

  return worldvols.physvol;
}

In addition to being able to define Double and String type parameters like this, one can also add parameters of Int and Boolean types (a material is basically just a string parameter). A few other more advanced options exists. Interested users are referred to the GeoConstructBase, GeoBase and ParametersBase classes for the details. Also note how the example above use the good practice of making the units of a particular parameter be explicit by its name, and later on converting the value by multiplication with the appropriate constant (e.g. when extracting sample_posz_mm we immediately multiply its value with Units::mm to make sure the actual C++ variable is now in a unit compatible with Geant4 units.

One final thing to note in the above, is that in dgcode we have a convenience method called place(..), which is used to directly combine a shape with a material and a position. As can be seen in the implementation here, this is really just wrapping completely standard Geant4 code. Feel free to use this function or not, as you wish.

For completeness, we show how some or all of the parameters might be modified in a Python sim-script:

import G4GeoTriCorder.GeoTriCorder as geomodule
import G4StdGenerators.SimpleGen as genmodule
import G4Launcher

geo = geomodule.create()
geo.sample_posz_mm = 5.0

gen = genmodule.create()
gen.particleName = 'neutron'
gen.neutron_wavelength_aangstrom = 2.2
gen.fixed_z_meters = -0.1

launcher = G4Launcher(geo,gen)
launcher.setOutput('tricorder','REDUCED')#Griff output
launcher.go()

Or they might be modified directly from the command line:

$> sb_tricorder_sim material_sample='stdlib::Cu_sg225.ncmat' sample_radius_mm=7.0