Skip to content

Latest commit

 

History

History
472 lines (354 loc) · 26 KB

DD4hepInFCCSW.md

File metadata and controls

472 lines (354 loc) · 26 KB

DD4hep Detector Description

Table of contents:

Ingredients Describing a Detector

To first order two files are involved to describe a detector in DD4hep.

  • The xml-based detector description that defines the dimensions and materials of the detector
  • The macro that implements the factory method to construct the detector from the xml-description

Constructing Detector Geometry

Similarly to Geant4 itself, DD4hep tries to re-use detector elements without having to keep every instance in memory. I.e. many dd4hep::PlacedVolume are used that refer to one dd4hep::Volume. To exploit this feature as much as possible:

  • Come up with the largest component that is repeatable (e.g. a detector wedge)
    • Usually this component is similar or equal to the components that are built when physically building the detector
  • Construct this component once and then place it several times to construct the whole detector
  • This should minimise the number of fully unique volumes you create

A Minimal Working Example

Imagine we want to create a simple cone with a radius of 1 m and a length of 1 m. Also see Coordinate Conventions for some explanations on lengths and coordinates in DD4hep and Geant4. Note that in the following all numbers are accompanied with units to explicitly state what you mean.

  • Detector description: First we describe the world (or a box around the sub-detector) and sub-detector in an XML:
<?xml version="1.0" encoding="UTF-8"?>
<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">

  <!-- use DD4hep elements and material definitions: -->
  <includes>
    <gdmlFile  ref="elements.xml"/>
    <gdmlFile  ref="materials.xml"/>
  </includes>

  <!-- some information about your detector: -->
  <info name="ExampleDetector"
    title="This is just an example"
    author="T. Test"
    url="no"
    status="development"
    version="1.0">
    <comment>This is really just a test!</comment>
  </info>

    <!-- this defines the dimensions of the world volume (i.e. top level volume), generated by DD4hep automatically: -->
    <define>
      <constant name="world_size" value="80*m"/> <!-- make sure to always use units -->
      <constant name="world_x" value="world_size"/>
      <constant name="world_y" value="world_size"/>
      <constant name="world_z" value="world_size"/>
    </define>

  <!-- this is where we actually define our detector: -->
  <detectors>
    <detector id="0" name="Name" type="Example"> <!-- note that the type is important, see below -->
      <dimensions rmin1="0" rmax1="1*m" rmin2="0" rmax2="2*m" dz="1*m" material="Aluminum" />
    </detector>
  </detectors>
</lcdd>
  • Factory method: Now we define a method that actually creates the geometry of the sub-detector, the type given in the xml tells DD4hep which factory method to call for a detector (see the very end of this snippet):
namespace detector {
static dd4hep::Ref_t createElement(
  dd4hep::Detector& lcdd,                 // dd4hep's main detector description interface
  xml_h xmlElement,                             // the xml-tree that describes this detector
  dd4hep::SensitiveDetector sensDet   // dd4hep's interface to the sensitive detector
  ) {
    // Get the detector description from the xml-tree
    xml_det_t    x_det = xml_h;
    std::string  name  = x_det.nameStr();
    // Create the detector element
    dd4hep::DetElement   coneDet( name, x_det.id() );
    // Get the world volume
    dd4hep::Volume experimentalHall =  lcdd.pickMotherVolume( coneDet );  // gets the containing volume (i.e. the world volume in this case)
    // Get the dimensions defined in the xml-tree
    xml_comp_t  coneDim ( x_det.child( _U(dimensions) ) );
    // Create the cone
    dd4hep::Cone cone( coneDim.dz(), coneDim.rmin1(), coneDim.rmax1(), coneDim.rmin2(), coneDim.rmax2());
    // Create the volume corresponding to the cone
    dd4hep::Volume coneVol( x_det.nameStr()+ "_SimpleCone", cone, lcdd.material(coneDim.materialStr()) );
    // Create the placed volume
    dd4hep::PlacedVolume conePhys;
    // And place it in the world
    conePhys = experimentalHall.placeVolume( coneVol, // the volume to be placed
                                              dd4hep::Transform3D(dd4hep::RotationZ(0.), // any rotation to be done
                                              dd4hep::Position trans(0., 0., 0. ) )); // the position where to place it
    // connect placed volume and physical volume
    coneDet.setPlacement( conePhys );
}
} // namespace detector
DECLARE_DETELEMENT(Example, detector::createElement) // factory method

Again, the type Example in the xml-description corresponds to the name we put in the DECLARE_DETELEMENT macro, this creates the link between the factory method and the xml-description.

Sensitive Detectors

The above example works, but it does not yet create any sensitive detectors that are needed by Geant4 to calculate and create energy deposits. While in GDML you have to define your sensitive detectors yourself, DD4hep has a mechanism of doing some of this for you.

Attaching the sensitive volume to the detector consists of:

  • Writing a sensitive detector that processes the hits and telling DD4hep to use it for a given detector
  • Creating the readout structure (to uniquely identify the parts of the detector where the deposits are made).

Both of them involve changes in the XML description and in the factory method.

Using an Existing Sensitive Detector Definition

The type of the sensitive detector corresponds to the name of a sensitive detector. There are few implementations of the sensitive detectors in FCCSW:

  • SimpleTrackerSD
  • SimpleCalorimeterSD
  • AggregateCalorimeterSD

SimpleTrackerSD and SimpleCalorimeterSD create hits for each energy deposit. AggregateCalorimeterSD accumulates the energy deposits in cells and loops over the whole collection of hits, increasing the simulation time.

If you need to create your own custom SDs, see User-defined Sensitive Detectors.

  • In the XML description, the sensitive (active) module should be indicated for the corresponding detector:
<detector id="0" ... sensitive="true">
  <sensitive type="SimpleTrackerSD"/>
</detector>

or for a sub-component, e.g. here for a module_component:

<module_component ... sensitive="true"/>
  • The factory method should then contain (corresponding to the first case):
dd4hep::SensitiveDetector sd = aSensDet;
dd4hep::xml::Dimension sdTyp = x_det.child(_U(sensitive)); // retrieve the type
detVol.setSensitiveDetector(aSensDet);                     // set the type
if ( x_det.isSensitive() ) {
  sd.setType(sdTyp.typeStr());                             // set for the whole detector
}

or (corresponding to the second case)

dd4hep::SensitiveDetector aSensDet
aSensDet.setType("SimpleTrackerSD");       // this could also be retrieved from xml like above
if (xComp.isSensitive()) {
  modCompVol.setSensitiveDetector(aSensDet); // only set for certain components
}

Readout Definition

In order to recognise where the energy was deposited, the sensitive volume has readout attached which allows to uniquely identify the volume element.

  • Extending the xml-description:

    We need to add the readout tag to the detector description, which defines the way a unique ID is calculated for each sensitive detector. This ID is a 64 bit integer, where we can define which bits are used to identify what. E.g., if we had 4 sub-detectors, we would need 2 bits to uniquely identify each of them (2^2 = 4). Then if the sub-detector for which we want to add the sensitive detector had, e.g., 80 modules, we need 7 bits to uniquely identify those (6 bits correspond to 64 unique IDs, 7 to 128). Then if your modules had, yet again, sub components, you'd have to think about how many bits you need for those, etc. (Note that also if all 4 sub-detectors have 80 modules, we still would only need to have 7 bits for the modules, since we have the first 2 bits for sub-detector identification.)

    In addition to this unique identifier, the same mechanism of DD4hep allows to define a segmentation of your smallest component that allows you to track where a particle passed through your sensitive detector. For example we can define a grid with step sizes of 0.5 mm. DD4hep then needs also to be able to uniquely identify those within the component and the corresponding bits need to be reserved. In our example let's keep half of the 64 bit integer for this grid, so 32 bits and half of each for x coordinate and y coordinate. Segmentation is described in more detail further.

    So in our example of 4 sub-detectors and our new detector having 80 modules, we would write this (leaving out all the boiler plate code shown above):

<readouts>
  <readout name="ExampleReadout">
    <!-- add the grid and define the cell sizes -->
    <segmentation type="CartesianGridXY" grid_size_x="0.5*mm" grid_size_y="0.5*mm"/>
    <!-- define how the ID is calculated: 2 bits for the system ID (sub-detectors) -->
    <!-- 7 bits for the modules and starting from bit 32, 16 bit for a signed (the "-") integer -->
    <!-- for x and 16 bit signed integer for y. -->
    <id>system:2,module:7,x:32:-16,y:-16</id>
  </readout>
</readouts>

<detectors>
  <detector id="0" name="Name" type="Example">
    <dimensions rmin="0" rmax="1*m" dz="1*m" />
  </detector>
</detectors>
  • Extending the factory method:

    In the factory method all we need to do is tell DD4hep what part a sensitive detector is and which part of the readout ID is foreseen for that part of the detector. Please note that segmentation is done automatically, hence it does not need any implementation in the factory method.

// imagine the rest as above
conePhys = experimentalHall.placeVolume( coneVol, // the volume to be placed
                                          dd4hep::Transform3D(dd4hep::RotationZ(0.), // any rotation to be done
                                          dd4hep::Position trans(0., 0., 0. ) )); // the position where to place it

// Add the ID for our sub detector, using the ID number given in the xml
conePhys.addPhysVolID( "system", x_det.id() );
// connect placed volume and physical volume
coneDet.setPlacement( conePhys );
// Here you define your volume (skipping how to get dimensions, see above):
dd4hep::Box module( ... );
dd4hep::Volume moduleVol( ... );
// Now we place the module:
for (int iMod = 0; iMod < 80; ++iMod) {
  // skipping rotation and position here, but placing it somewhere:
  auto placedVol = conePhys.placeVolume(moduleVol, ...);
  // Tell DD4hep this is a sensitive detector
  placedVol.setSensitiveDetector(sensDet);
  // Add the ID for the module and use the loop index as unique identification
  placedVol.addPhysVolID("module", iMod);
}
// rest as above

User-defined Sensitive Detectors

As mentioned above, you may want to define your own sensitive detector. For specifying the behaviour of the active elements (how and what should be saved in the hits collections), you need:

  1. Implementation of G4VSensitiveDetector (in example below: Detector/DetSensitive/src/SimpleCalorimeterSD.(h/cpp))
  2. Factory method (of SD) for DD4hep

In order to use the common methods to store your information in the EDM output (at the end of simulation), we advise to use the hit classes in DetCommon. If those classes are not sufficient, you'll need to create your own hit class implementation and the corresponding tools to translate your hit collections to FCC-EDM.

Example of a custom SD

  • Implementation of G4VSensitiveDetector class, e.g. det::SimpleCalorimeterSD:

    • ::Initialize(..) - create the hit collection
    • ::ProcessHits(..) - add entries to the hit collection with position, cellId, energy deposit, time, ... Hit base class used in the collection is already implemented in fcc::Geant4CaloHit.

    There is a method to retrieve the cell identification based on the DD4hep segmentation (det::segmentation::cellID(dd4hep::Segmentation, const G4Step&)).

namespace det {
class SimpleCalorimeterSD : public G4VSensitiveDetector {
public:
  SimpleCalorimeterSD(std::string aDetectorName, std::string aReadoutName, dd4hep::Segmentation aSeg);
  /// Destructor
  ~SimpleCalorimeterSD();
  virtual void Initialize(G4HCofThisEvent* aHitsCollections) final;
  virtual bool ProcessHits(G4Step* aStep, G4TouchableHistory*) final;
private:
  /// Collection of calorimeter hits
  G4THitsCollection<fcc::Geant4CaloHit>* calorimeterCollection;
};
}
  • Factory method for DD4hep is created in Detector/DetSensitive/src/SDWrapper.cpp. Any other sensitive detector may be added in the same way.
namespace dd4hep {
namespace Simulation {
// Factory method to create an instance of SimpleCalorimeterSD
static G4VSensitiveDetector* create_simple_calorimeter_sd(const std::string& aDetectorName,
                                                          dd4hep::Detector& aLcdd) {
  std::string readoutName = aLcdd.sensitiveDetector(aDetectorName).readout().name();
  return new det::SimpleCalorimeterSD(aDetectorName,
                                      readoutName,
                                      aLcdd.sensitiveDetector(aDetectorName).readout().segmentation());
}
}
}
DECLARE_EXTERNAL_GEANT4SENSITIVEDETECTOR(SimpleCalorimeterSD,dd4hep::sim::create_simple_calorimeter_sd)

Note that the name we put in the DECLARE_EXTERNAL_GEANT4SENSITIVEDETECTOR macro corresponds to the type SimpleCalorimeterSD in the xml-description, this creates the link between the factory method and the xml-description.

Segmentation

Possible grids (Cartesian 2D, Cartesian 3D, polar) can be found in DDSegmentation.

Cartesian segmentation

The centre of the Cartesian grid is in the centre of the detector.

The middle cell has the centre at x = 0 and the boundaries are at (+/-)0.5*cellSize. Note that for segmentation, the integers used for cell IDs need to be signed (indicated by '-' in the readout <id> tag in XML)

Figure below presents the cells and their identification for cellSize = 1

   +-----------------------------+
   | -2  | -1  |  0  |  1  |  2  |
|-----|-----|-----|-----|-----|-----|---> x
-3   -2    -1     0     1     2     3

Caveat: due to the middle cell centred at x = 0, for some detectors the outermost cells may be of different size than the rest.

Let's consider a detector of total width detSize = 4 and the the cell width cellSize = 1. The segmentation that is usually introduced in Geant is to divide the detector into detSize / cellSize = 4 cells which start from the detector boundary. It is depicted below:

 +-----------------------+
 |  0  |  1  |  2  |  3  |
-|-----|-----|-----|-----|---> x
-2    -1     0     1     2

However, DD4hep segmentation of the volume is done automatically (for any detector shape), with a simple formula used to calculate the cell ID, without any knowledge of the detector size, boundaries etc.

int( floor( (position + 0.5 * cellSize ) / cellSize) )

The centre position of the volume becomes the centre of the middle cell. As a consequence, in the example below, the two outermost cells have the width of 0.5 * cellSize.

 +-----------------------+
 |-2| -1  |  0  |  1  | 2|
-|-----|-----|-----|-----|---> x
-2    -1     0     1     2

Moreover, the total number of cells is 5 (even though ideally just 4 cells could be fitted inside detector volume). So, instead of using log_2(detSize / cellSize) = log_2(4) = 2 bits to decode the cellID, actually an additional bit needs to be used. Take this into consideration while defining the segmentation part of the readout structure.

Coordinate Conventions

  • Position vector points to centre of volume
  • Local coordinates start at the position vector
  • Sizes are defined as the offset position vector
    • e.g. the size along x you give to the constructor of a cube is the half-width

Visualisation

For the event visualisation in the detector please see visualisation package documentation

DD4hep comes with geoDisplay that can be used to display the detector geometry you implement. To use this display in the FCCSW context prepend the command with the run script in the FCCSW directory (after compilation).

You can also combine several description files (e.g. to check if components overlap, of if you want to use the detector master files):

./run geoDisplay -compact Detector/DetFCChhBaseline1/compact/FCChh_DectEmptyMaster.xml \
                          Detector/DetFCChhHCalTile/compact/FCChh_HCalBarrel_TileCal.xml

N.b.: When running geoDisplay through a X-Session, by default OpenGL may not be used. If you see the warning:

libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast

You can try to set the following environment variable to try and enable OpenGL:

export LIBGL_ALWAYS_INDIRECT=1

FCCSW folder structure

The folder structure should look something like this (only including one baseline and one sub-detector), details may be found below:

+ Detector              # the base directory
|- DetInterface         # contains virtual interface classes
|- DetComponents        # implementations of services / tools
|- DetExtensions        # for reconstruction extensions
|- DetSensitive         # custom sensitive detectors
|+ DetCommon            # basic shapes / elements / materials
 |- compact             # contains XMLs defining elements, materials, air volumes
 |- src                 # factories for basic shapes
|+ DetFCChhBaseline1    # the first FCC-hh baseline description
 |- compact             # master XMLs defining dimensions and world volumes
|+ DetFCChhHCalTile     # a sub-detector directory
 |- compact             # contains XMLs defining the detector, optionally an XML for standalone tests
 |- src                 # factories specific for this sub-detector

The detector descriptions and geometry services / tools can be found in the Detector package. The FCCSW components that provide the geometry to the remaining packages can be found in:

  • DetInterface (virtual interface classes)
  • DetComponents (actual implementations of the services / tools)

Extensions of the DD4hep description are found in:

  • DetExtensions (for reconstruction)
  • DetSensitive (custom sensitive volumes)

The master XMLs

The descriptions are placed in sub-folders of the Detector package. We plan to have the possibility of having several baseline detectors for the different accelerators. For each such baseline we will have a sub-directory "DetFCC" + Flavour + "Baseline" + X where X will be an increasing index and Flavour = ee, eh, hh, selected to describe the accelerator the baseline is meant for (for example DetFCChhBaseline1). In each baseline description we expect to have a compact directory with:

  • A dimensions XML that defines the sub-detector envelopes, e.g. here
  • A master XML that includes the baseline sub-detectors, e.g. here
  • A master XML that does only include the dimensions (meant to be used with individual sub-detector XMLs) , e.g. here

More specifically, the latter allows to have the possibility to use the GeoSvc with a sub-set of sub-detectors using the official dimensions (e.g. here).

Sub-detector descriptions

Sub-detector directories follow the naming convention "DetFCC" + Flavour + SubDetectorName + DetectorCharacteristic, where Flavour = ee, eh, hh, SubDetectorName = ECal, HCal, Tracker, Muon and finally the DetectorCharacteristic specifies the concrete description (e.g. Parametric (for a description to be used with FastSim) or Tile (for an ATLAS-like TileCal)). Examples: DetFCChhHCalTile or DetFCChhECalSimple.

Sub-detector directories should in most cases have both compact and src directories. The description in compact should not have a world volume defined in order to be able to include it in a master xml. If a description for standalone tests (with dimensions deviating from the baseline) is needed, it may be placed alongside the detector description with a postfix Standalone (and this one should define the world volume). However, please note that for standalone tests that use the baseline dimensions something like this should rather be put in the corresponding options file:

geoservice = GeoSvc("GeoSvc", detectors=['file:Detector/DetFCChhBaseline1/compact/FCChh_DectEmptyMaster.xml',
                                         'file:Detector/DetFCChhHCalTile/compact/FCChh_HCalBarrel_TileCal.xml' ]

To facilitate this mix-and-match functionality, the sub-detectors should use the variables that describe the envelope volume which are defined in the master dimensions. For all sub-detectors variables rmin, rmax and dz are defined with the naming convention DetectorPart + SubDetector + '_' + variable where DetectorPart can be Bar, EndCap or Fwd, SubDetector can be Tracker, ECal, HCal or Muon and variable can be rmin, rmax, dz or zOffset (only for forward and end-cap detectors). Examples: BarHCal_rmin or FwdHCal_zOffset.

Common descriptions

The final sub-directory DetCommon includes descriptions and macros that should be independently usable by all baselines / sub-detectors. This includes primitive shapes (cones, cylinders, boxes, etc.), material & element descriptions. Additionally place-holders can be found that place air-filled cylinders using the dimensions defined in a master-dimension file. These are meant for debugging the dimensions file.

Material budget estimation

We provide an example configuration and plotting script for estimating the material budget as a function of eta.

./run fccrun.py Examples/options/material_scan.py
./run python Examples/scripts/material_plots.py

The configuration file material_scan allows to customise the binning of the material scan with etaMax and etaBinning. The detector which is scanned is obtained from the GeoSvc and the corresponding detector description can be changed there as usual. The scan is performed from -etaMax to +etaMax with a step-size of etaBinning. It is also averaged over azimuthal angle, taking nPhiTrials random directions, set by default to 100. The start point of the direction is (0,0,0), whereas the end point is by default the end of the world volume. It may be changed to other volume border by specifying the volume name in property envelopeName. If point (0,0,0) is not contained inside the envelope, the distance to the closest volume border is taken into account.

The output of the first command is the ROOT file DD4hep_material_scan.root which contains a TTree that has 6 branches:

  • eta: The pseudorapidity along which the ray was cast from (0, 0, 0)
  • nMaterials: The number of materials found by the ray-cast along the eta direction
  • nX0: The number of radiation lenghts X0 for each material in this eta bin
  • nLambda: The number of nuclear interaction lengths lambda for each material in this eta bin
  • matDepth: The depth in mm for each material in this eta bin
  • material: The names of all materials in this eta bin (as defined in DD4hep)

The plotting script outputs three plots as pdf, which contain the number of interaction lenghts, radiation lengths and the material thickness, stacked according to the material name. Additionally, the plots are created for |eta| and eta. The script sums up all materials that have the same name in the eta bin and discards any air that is encountered.

Troubleshooting

  • Overlapping volumes may lead to some hard-to-detect errors. If a daughter volume is completely outside the parent volume, an error is reported during the conversion to Geant4 geometry description (PANIC! - Overlapping daughter with mother volume.). But sometimes overlaps errors are silent, yet result in sensitive detectors not registering any hits. The test suite therefore includes a check on overlaps in FCChh_DectMaster.xml, which should be fixed. It is located under Test/TestGeometry/tests/options/geant_overlap_test_fcchh.py, if it is needed to be run manually. A second test is provided by the ROOT geometry package. It can be run via the checkOverlaps.py script from DD4hep. With the command line option -o s it uses sampling to check for overlaps, but ignores extrusions of daughter volumes. Without this option, it is vice-versa, so it is recommended to run it in both modes. Note that the sampling does not work correctly if several xml files are used for the detector description. It is best to add your detector to the master xml file.
  • It is an error to declare a volume as well as one of its daughters sensitive. This is reported somewhat confusingly as populate: Severe error: Duplicated Volume entry: 1A [THIS SHOULD NEVER HAPPEN] but is in fact an issue with the sensitive detector.