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