Back to home page

EIC code displayed by LXR

 
 

    


Warning, /tutorial-geometry-development-using-dd4hep/episodes/03-modifying-geometry.md is written in an unsupported language. File is not indexed.

0001 ---
0002 title: "Modifying geometry"
0003 teaching: 20
0004 exercises: 40
0005 ---
0006 
0007 ::::::::::::::::::::::::::::::::::::::::::::: questions
0008 
0009 - How do we modify or add geometry defined in DD4hep?
0010 
0011 :::::::::::::::::::::::::::::::::::::::::::::
0012 
0013 ::::::::::::::::::::::::::::::::::::::::::::: objectives
0014 
0015 - Understand the structure of a geometry plugin source file.
0016 
0017 :::::::::::::::::::::::::::::::::::::::::::::
0018 
0019 Until now we have interacted with the read-only geometry that is stored inside the container. We will now move on to modifying this geometry. That requires that we work in a local copy of the geometry.
0020 
0021 ## Checking out your local copy of the geometry
0022 
0023 We will start with a local copy of the `epic` repository:
0024 
0025 ```bash
0026 $ cd ~/eic/
0027 $ git clone https://github.com/eic/epic
0028 $ cd epic
0029 $ ls
0030 bin    calibrations    compact         macro      reports           scripts  templates
0031 build  CMakeLists.txt  configurations  README.md  requirements.txt  src      views
0032 ```
0033 
0034 As you can tell, the content of the repository itself is quite different from the installed version (as is often the case for other software as well). You will recognize, however, the `compact` directory with the subsystem xml files.
0035 
0036 ### Important: to follow this tutorial we will need to checkout an older version of the geometry
0037 
0038 ```bash
0039 $ git checkout 25.08.0
0040 ```
0041 
0042 In order to compile and install the local geometry repository into a local directory, we can use the following commands:
0043 
0044 ```bash
0045 $ cd ~/eic/epic
0046 $ cmake -B build -S . -DCMAKE_INSTALL_PREFIX=install
0047 $ cmake --build build -- install
0048 ```
0049 
0050 ::::::::::::::::::::::::::::::::::::::::::::: callout
0051 
0052 Note: To speed up compilation, you may add the option `-j4` to the last command, where `4` corresponds to the number of cores you can use.
0053 
0054 :::::::::::::::::::::::::::::::::::::::::::::
0055 
0056 This will install the geometry into the directory `~/eic/epic/install/` (and subdirectories). You will notice that `~/eic/epic/install/share/epic` contains the same files that we explored earlier inside the `/opt/detector` directory.
0057 
0058 As before, we now need to load the environment for this geometry. We can again use the `bin/thisepic.sh` script for this, though now we must use the one installed in our local installation directory:
0059 
0060 ```bash
0061 $ source install/bin/thisepic.sh
0062 $ env | grep DETECTOR
0063 ```
0064 
0065 You should now see that the `$DETECTOR_PATH` variable points to your local install path.
0066 
0067 When we run `dd_web_display --export $DETECTOR_PATH/$DETECTOR_CONFIG.xml` now, we will use the local geometry parametrization and the local geometry plugins. (Note: As before, downloads of fieldmaps and calibration files will be necessary.)
0068 
0069 ::::::::::::::::::::::::::::::::::::::::::::: challenge
0070 
0071 ## Quick Exercise: build a local geometry
0072 
0073 - Ensure that you have a local copy of the geometry repository which you can compile and install in a local directory.
0074 - Verify that, after sourcing the `bin/thisepic.sh` script, the `DETECTOR_PATH` points to the correct local install directory.
0075 - Verify that `dd_web_display` can indeed export the geometry for the detector subsystem configuration you used before.
0076 
0077 ::::::::::::::: solution
0078 
0079 After `cmake -B build -S . -DCMAKE_INSTALL_PREFIX=install` and `cmake --build build -- install`,
0080 sourcing `install/bin/thisepic.sh` sets `DETECTOR_PATH` to `~/eic/epic/install/share/epic`.
0081 Running `dd_web_display --export $DETECTOR_PATH/epic_vertex_only.xml` then exports the geometry from
0082 your local build.
0083 
0084 :::::::::::::::
0085 
0086 :::::::::::::::::::::::::::::::::::::::::::::
0087 
0088 ## Anatomy of a detector plugin
0089 
0090 ### Introduction
0091 
0092 It may be clear at this point how to make modifications to the parametrization, commit them to a branch in the local repository, and submit a pull request on GitHub to include them in the main branch.
0093 
0094 For the remainder of this lesson we will focus on the vertex barrel detector using the much reduced geometry configuration `epic_vertex_only.xml` so that any change made are more evident.
0095 
0096 What we have not covered yet is the discussion of what goes into a detector plugin. Let's look at the `epic_VertexBarrel` plugin we encountered earlier. The names of the plugins may not agree with the source files in the `src/` directory. This allows us to support multiple detector types with the same source files. In this case, `epic_VertexBarrel` is defined in the file `src/BarrelTrackerWithFrame_geo.cpp`.
0097 
0098 If you are not sure what file in the `src` directory builds the plugin you are looking for, find the `type` in the xml detector definition and use the `grep` shell command.
0099 
0100 ```bash
0101 $ grep -r epic_VertexBarrel src/
0102 src/BarrelTrackerWithFrame_geo.cpp:DECLARE_DETELEMENT(epic_VertexBarrel,    create_BarrelTrackerWithFrame)
0103 ```
0104 
0105 The DECLARE_DETELEMENT line, at the bottom of the `src/BarrelTrackerWithFrame_geo.cpp` file provides dd4hep with the link which tells it to call the `create_BarrelTrackerWithFrame` function when an xml detector definition is given the `epic_VertexBarrel` type.
0106 
0107 We now know that changing the content of the `create_BarrelTrackerWithFrame` function should be called when the `epic_vertex_only.xml` is used when dd4hep loads a geometry.
0108 
0109 ### Passing parameters from xml
0110 
0111 Next we will take a deeper dive into the `create_BarrelTrackerWithFrame` function to pick out the key components and how it is configured by the xml file `compact/tracking/vertex_barrel.xml`
0112 
0113 ```c++
0114 static Ref_t create_BarrelTrackerWithFrame(Detector& description, xml_h e, SensitiveDetector sens)
0115 ```
0116 
0117 here `description` contains access to the full tree defined from the main detector xml file. `e` contains the specific information contained within the xml tree within the `<detector>` blocks.
0118 
0119 There are a few ways to access these xml configuration parameters. Some xml elements have methods provided by dd4hep which allow direct access to the values, such as:
0120 
0121 ```c++
0122 Material                     air      = description.air();
0123 int                          det_id   = x_det.id();
0124 string                       det_name = x_det.nameStr();
0125 ```
0126 
0127 A list of tags for which dd4hep provides a convenient conversion method can be found in the [dd4hep UnicodeValues header](https://dd4hep.web.cern.ch/dd4hep/reference/UnicodeValues_8h_source.html).
0128 
0129 More often you may be wanting to define a parameter by a tag of your choice or if you're wanting to be certain how it's being handled. The following (abridged) code is an example of how to access parameters of any name.
0130 
0131 ```c++
0132   for (xml_coll_t su(x_det, _U(support)); su; ++su) {
0133     xml_comp_t  x_support         = su;
0134     double      support_thickness = getAttrOrDefault(x_support, _U(thickness), 2.0 * mm);
0135     double      support_length    = getAttrOrDefault(x_support, _U(length), 2.0 * mm);
0136     double      support_rmin      = getAttrOrDefault(x_support, _U(rmin), 2.0 * mm);
0137     double      support_zstart    = getAttrOrDefault(x_support, _U(zstart), 2.0 * mm);
0138     std::string support_name      = getAttrOrDefault<std::string>(x_support, _Unicode(name), "support_tube");
0139     std::string support_vis       = getAttrOrDefault<std::string>(x_support, _Unicode(vis), "AnlRed");
0140   }
0141 ```
0142 
0143 The code loops over all `<support>` elements inside the `<detector>` block, located using `_U(support)` which interprets content as unicode. Inside each support node the `getAttrOrDefault` method sets the variables to the values given by the unicode `thickness` etc, or if they are not present in the support node sets a default value. If you want to require the parameter be defined in the xml file you could simply use `x_support.child(thickness)`.
0144 
0145 ::::::::::::::::::::::::::::::::::::::::::::: callout
0146 
0147 Note:
0148 
0149 - No support blocks actually currently appear in the `compact/tracking/vertex_barrel.xml` file so this block of code won't be run.
0150 - In the xml file, there must be no white space between the parameter, = sign and the value.
0151 
0152 :::::::::::::::::::::::::::::::::::::::::::::
0153 
0154 A fuller description of how to access and use the xml parameters is given in [section 2.4 of the dd4hep manual](https://dd4hep.web.cern.ch/dd4hep/usermanuals/DD4hepManual/DD4hepManualch2.html#x3-210002.4).
0155 
0156 ::::::::::::::::::::::::::::::::::::::::::::: challenge
0157 
0158 ## Exercise: add a configuration parameter
0159 
0160 - Create and checkout a new branch forked from the 25.08.0 branch.
0161 - Add a new configuration parameter into `compact/tracking/vertex_barrel.xml`.
0162 - Add code to `src/BarrelTrackerWithFrame_geo.cpp` which will read the new parameter and a print statement to display its value to the terminal.
0163 - Recompile and rerun the `dd_web_display` step using `epic_vertex_only.xml` to verify that the printout statement has been added.
0164 - Change the values in the xml and rerun to verify the value is being read properly.
0165 
0166 ::::::::::::::: solution
0167 
0168 Add a `<constant>` (or an attribute on the `<detector>` element) in `vertex_barrel.xml`, read it in
0169 `create_BarrelTrackerWithFrame` with `getAttrOrDefault(x_det, _Unicode(yourparam), default)`, and
0170 print it with e.g. `std::cout`. After `cmake --build build -- install` and re-running
0171 `dd_web_display`, your printout appears in the terminal and changes when you edit the xml value.
0172 
0173 :::::::::::::::
0174 
0175 :::::::::::::::::::::::::::::::::::::::::::::
0176 
0177 ::::::::::::::::::::::::::::::::::::::::::::: callout
0178 
0179 Note: Changes to the xml files in `install/share/epic/`... can be made without recompiling the code, however they will be overwritten when the code is recompiled. In order to test temporary changes a top level configuration file can be copied to a path outside of `install`. This then needs to be edited to internally point to the compact file you are editing rather than the path given by the install, `${DETECTOR_PATH}`.
0180 
0181 :::::::::::::::::::::::::::::::::::::::::::::
0182 
0183 ### Building new components
0184 
0185 DD4hep geometries are built in a similar hierarchical way to Geant4 geometries.
0186 
0187 - Shape - 3D shape of the component. Can be a simple shape, made from boolean combinations of simple shapes or imported from CAD.
0188 - Volume - Adds physical properties to the shape such as its material and if its sensitive.
0189 - Placement - Position(s) that the volume is located within your detector geometry, this can be a position nested within another volume, containing unique identifier.
0190 
0191 We will start by looking at the shapes and volumes. The most common shapes you are likely to find yourself using are `Box` and `Tube`. In `src/BarrelTrackerWithFrame_geo.cpp` both are used, `module_component` is defined as `Box`, the volume of which takes its material from the xml description:
0192 
0193 ```c++
0194       Box          c_box(x_comp.width() / 2, x_comp.length() / 2, x_comp.thickness() / 2);
0195       Volume       c_vol(c_nam, c_box, description.material(x_comp.materialStr()));
0196 ```
0197 
0198 `layer` volumes into which `module_component`s are later placed are described as a `Tube` but this time the volume is directly given the air material:
0199 
0200 ```c++
0201     Tube       lay_tub(x_barrel.inner_r(), x_barrel.outer_r(), x_barrel.z_length() / 2.0);
0202     Volume     lay_vol(lay_nam, lay_tub, air); // Create the layer envelope volume.
0203 ```
0204 
0205 In DD4hep there is a type of volume called an Assembly which contains volumes placed within itself but doesn't have a shape of its own. This is very useful for arranging volumes without needing a container volume defined which might have overlaps of their own where none are really present.
0206 
0207 ::::::::::::::::::::::::::::::::::::::::::::: callout
0208 
0209 Notes:
0210 
0211 - The [DD4hep shapes](https://dd4hep.web.cern.ch/dd4hep/usermanuals/DD4hepManual/DD4hepManualch2.html#x3-290002.9) are based directly off the [ROOT geometry shapes](https://root.cern.ch/root/htmldoc/guides/users-guide/Geometry.html#shapes). A useful, probably out of date table comparing the ROOT, DD4hep and Geant4 shape names can be found in [DD4hep issue #588](https://github.com/AIDASoft/DD4hep/issues/588).
0212 - DD4hep provides some shape plugins directly, so very basic geometries can be described directly in the xml with no additional code.
0213 
0214 :::::::::::::::::::::::::::::::::::::::::::::
0215 
0216 ::::::::::::::::::::::::::::::::::::::::::::: challenge
0217 
0218 ## Exercise: build a new volume
0219 
0220 - Create a new simple volume within the hierachy in `src/BarrelTrackerWithFrame_geo.cpp`.
0221 - Recompile and rerun the `dd_web_display` step using `epic_vertex_only.xml` locating the new shape(s) you have added in the ROOTJS viewer.
0222 - Build a new tube volume which contains tracking layers.
0223 
0224 ::::::::::::::: solution
0225 
0226 Construct a shape (e.g. `Tube` or `Box`), wrap it in a `Volume` with a material, and place it with
0227 `placeVolume` inside an existing volume in `create_BarrelTrackerWithFrame`. After
0228 `cmake --build build -- install` and re-exporting with `dd_web_display`, the new shape appears in the
0229 JSROOT viewer.
0230 
0231 :::::::::::::::
0232 
0233 :::::::::::::::::::::::::::::::::::::::::::::
0234 
0235 ### Testing overlaps
0236 
0237 It is important for running the Geant4 simulation that geometries do not overlap. When stepping through the geometry a particle cannot know which volume it is in. An overlap check is run by GitHub when you request that your changes are merged into the main branch of the epic code.
0238 
0239 ```bash
0240 python scripts/checkOverlaps.py -c ${DETECTOR_PATH}/epic_vertex_only.xml
0241 ```
0242 
0243 ::::::::::::::::::::::::::::::::::::::::::::: challenge
0244 
0245 ## Exercise: run the overlap check
0246 
0247 - Run the overlap check on your geometry with the added component.
0248 - Change some parameters to add/remove the overlap and compare the output.
0249 
0250 ::::::::::::::: solution
0251 
0252 `python scripts/checkOverlaps.py -c ${DETECTOR_PATH}/epic_vertex_only.xml` reports any overlapping
0253 volumes with their names and overlap distance. After moving your new volume so it intersects a
0254 neighbour, the check lists the new overlap; moving it clear again removes it from the report.
0255 
0256 :::::::::::::::
0257 
0258 :::::::::::::::::::::::::::::::::::::::::::::
0259 
0260 ### Readout
0261 
0262 Placed volumes can be made sensitive by setting e.g.
0263 
0264 ```c++
0265   c_vol.setSensitiveDetector(sens);
0266 ```
0267 
0268 The type of information that will be saved to the output is defined usually as either:
0269 
0270 ```c++
0271   sens.setType("tracker");
0272   sens.setType("calorimeter");
0273 ```
0274 
0275 In the xml file the readout for the detector is passed in the `readout` field of the `detector` definition
0276 
0277 ```xml
0278     <detector
0279       id="VertexBarrel_0_ID"
0280       name="VertexBarrel"
0281       type="epic_VertexBarrel"
0282       readout="VertexBarrelHits"
0283       insideTrackingVolume="true">
0284 ```
0285 
0286 Where the readout name references a `readout` block also defined in the xml description
0287 
0288 ```xml
0289   <readouts>
0290     <readout name="VertexBarrelHits">
0291       <segmentation type="CartesianGridXY" grid_size_x="0.020*mm" grid_size_y="0.020*mm" />
0292       <id>system:8,layer:4,module:12,sensor:2,x:32:-16,y:-16</id>
0293     </readout>
0294   </readouts>
0295 ```
0296 
0297 Here the name given will appear as the name of the branch containing the hits in the output edm4hep file. dd4hep provides very convenient segmentation to the readout which allows hits in a readout volume to be divided up to locations beyond its natural boundaries, this is configured by the `x` and `y` parameters as well as the `grid_size`.
0298 
0299 The readout branch contains the information on the hit energy deposited, time of arival etc. which is usually found in a simulation output but in addition it contains `CellID` which is a 64 bit field which uniquely identifies the detector segmentation.
0300 
0301 In the case of `VertexBarrelHits`, 8 bits always required by the system, 4 bits locate a specific layer, 12 a module, 2 a sensor and 32 the remaining x-y segmentation. In the code, dd4hep requires a separate hierachy of the geometry detector elements which are given tags and numbers so they can be uniquely identified. This hieracy doesn't have to strictly follow the way the volumes are themselves constructed.
0302 
0303 ```c++
0304   DetElement mod_elt(lay_elt, module_name, module);
0305   pv = lay_vol.placeVolume(module_env, tr);
0306   pv.addPhysVolID("module", module);
0307   mod_elt.setPlacement(pv);
0308 ```
0309 
0310 Here `mod_elt` is given the parent element `layer_elt`, the name and module number. Then the element is attached to a placed volume which has been given the physical volume id `module`.
0311 
0312 To run the simulation and produce an output file containing the detector hits you can use `npsim`. I would suggest only using a small sample of events given by the `--numberOfEvents` flag.
0313 
0314 ```bash
0315 $ npsim --runType run --compactFile $DETECTOR_PATH/epic_vertex_only.xml --inputFiles root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/SIDIS/pythia6-eic/1.0.0/18x275/q2_0to1/pythia_ep_noradcor_18x275_q2_0.000000001_1.0_run9.ab.hepmc3.tree.root --numberOfEvents 100 --outputFile test.edm4hep.root
0316 ```
0317 
0318 Inside the output file `test.edm4hep.root` there should be 5 trees:
0319 
0320 ```
0321 events
0322 runs
0323 meta
0324 metadata
0325 podio_metadata
0326 ```
0327 
0328 The events tree contains the readout of your detectors. In this example it contains the following branches:
0329 
0330 ```
0331 EventHeader
0332 _EventHeader_weights
0333 MCParticles
0334 _MCParticles_parents
0335 _MCParticles_daughters
0336 VertexBarrelHits
0337 _VertexBarrelHits_particle
0338 GPIntKeys
0339 GPIntValues
0340 GPFloatKeys
0341 GPFloatValues
0342 GPDoubleKeys
0343 GPDoubleValues
0344 GPStringKeys
0345 GPStringValues
0346 ```
0347 
0348 The `EventHeader` branch holds per-event bookkeeping and the `GP*` branches hold general parameters (key/value metadata) attached to the frame. The `MCParticles` branch contains information on all of the particles described by your generator and any secondaries produced in the simulation. `VertexBarrelHits` contains the hit information of the vertex barrel and has the association branch `_VertexBarrelHits_particle` which references the particle in the `MCParticles` branch which caused the hit.
0349 
0350 ::::::::::::::::::::::::::::::::::::::::::::: challenge
0351 
0352 ## Exercise: run a simulation and inspect the output
0353 
0354 - Run the simulation with a small dataset.
0355 - Look at the output datafiles using your favorite ROOT browser.
0356 - Change the sensitive type of the `BarrelTrackerWithFrame_geo.cpp` and compare the output to what you first saw.
0357 - Try to make your new tube volume sensitive by setting it as sensitive and adding a `DetElement` and giving it the necessary `addPhysVolID` values not currently used by the tracker.
0358 - Open ended - Move your new Tube detector into a separate src file (or create a new simple detector) and include it separately in the xml definition and readout.
0359 
0360 ::::::::::::::: solution
0361 
0362 Running `npsim ... --numberOfEvents 100 --outputFile test.edm4hep.root` produces a file whose
0363 `events` tree holds the `VertexBarrelHits` collection. Switching `sens.setType("tracker")` to
0364 `"calorimeter"` changes the stored hit type. Making the new tube sensitive requires
0365 `setSensitiveDetector(sens)`, a matching `DetElement`, and an `addPhysVolID` value that does not
0366 collide with the existing `layer`/`module`/`sensor` fields in the readout `<id>`.
0367 
0368 :::::::::::::::
0369 
0370 :::::::::::::::::::::::::::::::::::::::::::::
0371 
0372 ### ACTS?
0373 
0374 ToDo
0375 
0376 ::::::::::::::::::::::::::::::::::::::::::::: keypoints
0377 
0378 - To add or modify geometry, we add geometry plugins written in C++.
0379 
0380 :::::::::::::::::::::::::::::::::::::::::::::