# Changing geometry :::{admonition} Page contributors :class: callout dropdown Alvaro Tolosa Delgado, Andre Sailer ::: The description of the componentes of a particle detector can be extremely complex. `DD4hep` exploits the properties of `xml` file format to store hierarchically the geometry and materials of each component of a given detector. Thus the detector description is given in one or more `xml` files. In this section, the following topics are covered: * how tow to simulate the existing detector model of the `CLD` detector * modify its XML files * check for overlaps with Geant4 * get a better understanding of what DD4hep does under the hood * Write your own C++ detector constructor and run with it This section is adapted from the [talk given by Andre Sailer](https://indico.cern.ch/event/1182767/contributions/5093486/attachments/2532164/4356996/221020_soft_tutorial_dd4hep.pdf) in the *FCC Software Tutorial 2022*. ## Setup The first step is to set up a CentOS 7 machine with CVMFS. It can be a local machine, a remote machine (e.g. [lxplus.cern.ch](https://lxplusdoc.web.cern.ch/)), a virtual machine (e.g. [CernVM](https://cernvm.cern.ch/appliance/)) or a containerized system (e.g. [Docker](https://linux.web.cern.ch/dockerimages/), or Apptainer). Then, the stack with the FCC software can be sourced as ```shell source /cvmfs/fcc.cern.ch/sw/latest/setup.sh ``` The following step is to create a working directory, ```shell mkdir mydd4heptutorial cd mydd4heptutorial ``` Then, copy the CLD detector description into the working directory, ```shell cp -r $LCGEO/FCCee/compact/FCCee_o1_v05 . ``` In order to plot the histograms in the terminal, the histoprint python package is used and it can be installed with the following command ```shell pip install --user histoprint ``` If everything goes as expected, the contents of your working directory shall look this: ```shell ls -ltra total 8 drwxr-xr-x. 28 sailer zf 4096 Oct 14 11:29 .. drwxr-xr-x. 3 sailer zf 26 Oct 14 11:30 . drwxr-xr-x. 2 sailer zf 4096 Oct 14 11:30 FCCee_o1_v05 ``` Another ingredient is a script to plot some information after the simulations to show that the geometry really did change. The `showPlots.py` script plots the per hit energy deposition and position along Z axis in the ECal endcap. The histograms are printed in the terminal to avoid the use of a graphical interface. If a graphical system is available, one can also visualize the geometry with `geoDisplay` or the Geant4 visualizations.
Click to show the code of the python script showPlots.py ```python #!/bin/env python import sys, ROOT from histoprint import print_hist ROOT.gROOT.SetBatch(True) if len(sys.argv) < 2: print("Please specify input file"); sys.exit(1) inputFile = sys.argv[1]; print("Reading:", inputFile) tfile = ROOT.TFile.Open(inputFile) myTree = tfile.Get("events") myTree.Draw("ECalEndcapCollection.position.z>>zHist(100, 2300, 2510)", "ECalEndcapCollection.position.z > 0") myTree.Draw("ECalEndcapCollection.energy>>eHist(30, 0, 0.002)") print_hist(tfile.Get("zHist"), title="Hits per Layer") print_hist(tfile.Get("eHist"), title="Energy per Hit") ```

In this section, the detector geometry will be changed in every step. The geometry can be visualized using different tools, as it is explained in the next section. ## Modify an existing XML file The detector geometry is complex, and takes a while to be built. To speed up the following steps, the Silicon Tracker can be removed. To do so, 1. Open `FCCee_o1_v05/FCCee_o1_v05.xml` 2. Find the following lines in the file, and delete/comment them ```xml ``` Remember that a comment in `xml` looks like ``. 3. Comment out the plugins section ` ... ` in the same file. To simulate the interaction of 100 muons with the CLD detector, the following command is used inside the directory `mydd4heptutorial`. It takes some minutes to build the geometry and run the actual simulation. ```shell ddsim --compactFile FCCee_o1_v05/FCCee_o1_v05.xml \ --enableGun \ --gun.distribution uniform \ --gun.energy "10*GeV" \ --gun.particle mu- \ --numberOfEvents 100 \ --outputFile Step1_edm4hep.root ``` And now plot the distribution with `showPlots.py` ```shell python showPlots.py Step1_edm4hep.root ``` The simulation runs 20 times faster than the full geometry simulation and the distributions do not change. ## Overlap checking Whenever you change the geometry in a non-trivial way there are the possibilities of overlaps and the following things should be kept in mind 1. There are no trivial changes 2. See point 1 3. Run the overlap check Geant4 can perform the overlap check of a given geometry. This action is available for `ddsim`. A macro file with the Geant4 instructions has to be provided to `ddsim`. To do so, create a file named as *overlap.mac* and write these commands inside ``` /geometry/test/run exit ``` And then we run `ddsim` with this macro file, and dump the output to a text file for easy browsing: ```shell ddsim --compactFile FCCee_o1_v05/FCCee_o1_v05.xml \ --runType run \ --macroFile overlap.mac > overlapDump & ``` With the full detector model including the tracker this would take about 30 minutes. The output shows some exceptions: * The exceptions about the VertexEndcapModule are due to a too small envelope, and could be easily fixed * The exceptions about the Boolean Volume are more vexing, let’s ignore those for now. The HOMAbsorber include can be dropped as well to silent these errors. ## Modifying the ECAL In this section, it is shown how to change the number of layers and silicon thicknesses of the ECal Endcap. To do so, 1. Open the file `FCCee_o1_v05/ECalEndcap_o2_v01_03.xml` 2. Find this block: ```xml ``` 3. Change the number of layers from **40** to **20**, and the silicon thickness from `0.50*mm` to `1.00*mm`.
Click to show that piece of code from FCCee_o1_v05/ECalEndcap_o2_v01_03.xml after the change ```xml ```

Simulate again with the new ECal, ```shell ddsim --compactFile FCCee_o1_v05/FCCee_o1_v05.xml \ --enableGun \ --gun.distribution uniform \ --gun.energy "10*GeV" \ --gun.particle mu- \ --numberOfEvents 100 \ --outputFile Step2_edm4hep.root ``` Compare the histograms produced from `Step1_edm4hep.root` and `Step2_edm4hep.root`. ## Geometry driver modifications The type attribute of the detector tag tells DD4hep which detector constructor to load ```xml ``` DD4hep’s plugin service will look in the `*.components` files it finds via the `LD_LIBRARY_PATH` (or `DD4HEP_LIBRARY_PATH` on macOS because of SIP) environment variables, and load the library on-demand, and then instantiate the function. For example, to know which library contains the GenericCalEndcap plugin ```shell for DIR in $(echo $LD_LIBRARY_PATH | tr ":" " ") ; do grep GenericCalEndcap $DIR/*.components 2> /dev/null; done ``` Usually we would add our detector to an existing project, but because overwriting existing libraries in the environment requires some work, we start with a new project. This requires two main steps, first creating a new detector constructor, and second creating the detector description in a **xml file**. Start by executing the following code inside the directory `mydd4heptutorial`, in order to create the detector constructor project ```shell mkdir MyFirstDetector cd MyFirstDetector touch CMakeLists.txt mkdir src touch src/MyFirstDetector.cpp ``` The `CMakeLists.txt` file has to contain the following code ```cmake cmake_minimum_required(VERSION 3.12 FATAL_ERROR) set(PackageName MyFirstDetector) project(${PackageName}) find_package(DD4hep REQUIRED COMPONENTS DDG4) # our sources set(sources ./src/MyFirstDetector.cpp) # create our library and make the components file add_dd4hep_plugin(${PackageName} SHARED ${sources}) # link it with DDCore, or whatever you need target_link_libraries(${PackageName} DD4hep::DDCore) # Create this_package.sh file, and install dd4hep_instantiate_package(${PackageName}) ``` And the `MyFirstDetector.cpp` file has to contain the following code ```cpp #include "DD4hep/DetFactoryHelper.h" #include "XML/Layering.h" #include "XML/Utilities.h" #include "DDRec/DetectorData.h" static dd4hep::Ref_t create_detector( dd4hep::Detector &theDetector, xml_h entities, dd4hep::SensitiveDetector sens) { // XML Detector Element (confusingly also XML::DetElement) xml_det_t x_det = entities; // DetElement of our detector instance, attach additional information, sub-elements... // uses name of detector and ID number as defined in the XML detector tag std::string detName = x_det.nameStr(); sens.setType("calorimeter"); dd4hep::DetElement sdet(detName, x_det.id()); // get the dimensions tag xml_dim_t dim = x_det.dimensions(); // read its attributes double rmin = dim.rmin(); double rmax = dim.rmax(); double zmax = dim.zmax(); // Make a Cylinder dd4hep::Tube envelope(rmin, rmax, zmax); dd4hep::Material air = theDetector.air(); dd4hep::Volume envelopeVol(detName + "_envelope", envelope, air); dd4hep::PlacedVolume physvol = theDetector.pickMotherVolume(sdet).placeVolume(envelopeVol); // add system ID and identify as barrel (as opposed to endcap +/-1) physvol.addPhysVolID("system", sdet.id()).addPhysVolID(_U(side), 0); sdet.setPlacement(physvol); return sdet; } DECLARE_DETELEMENT(MyFirstDetector, create_detector) ``` Still inside the `MyFirstDetector` directory, execute the following shell commands, ```shell mkdir build install cd build cmake -D CMAKE_INSTALL_PREFIX=$PWD/../install .. make install source ../install/bin/thisMyFirstDetector.sh ``` Look at `$PWD/../install` and note the content of the `bin` and `lib` directories, have a look at the `libMyFirstDetector.components` file. After creating the detector constructor project, a detector description has to be given as a `xml` file. In the `FCCee_o1_v05` directory, create a `mydetector.xml` file with the following content
Click to show the code of mydetector.xml ```xml system:5,side:2,module:8,stave:4,layer:9,submodule:4,r:32:10,eta:-11,phi:-11 ```

Now, modify the `FCCee_o1_v05/FCCee_o1_v05.xml` and replace the include for the `ECalBarrel` with `mydetector.xml`. After every change in the geometry, the overlap check must be run inside `FCCee_o1_v05` directory ```shell ddsim --compactFile FCCee_o1_v05/FCCee_o1_v05.xml \ --runType run \ --macroFile overlap.mac > overlapDump2 & ``` If there are no exceptions, we can continue. Now, we add some layers to the detector. First, we add a `layer` component inside the ` ... ` section of `FCCee_o1_v05/mydetector.xml` detector description file.
Click to show the code of mydetector.xml ```xml system:5,side:2,module:8,stave:4,layer:9,submodule:4,r:32:10,eta:-11,phi:-11 ```

In order to actually build the layers that were included in the detector description, we have to add the corresponding interpretation in the detector constructor defined in `MyFirstDetector.cpp`, which should look like the following:
Click to show the code of MyFirstDetector.cpp ```cpp #include "DD4hep/DetFactoryHelper.h" #include "DDRec/DetectorData.h" #include "XML/Layering.h" #include "XML/Utilities.h" static dd4hep::Ref_t create_detector(dd4hep::Detector &theDetector, xml_h entities, dd4hep::SensitiveDetector sens) { // XML Detector Element (confusingly also XML::DetElement) xml_det_t x_det = entities; // DetElement of our detector instance, attach additional information, // sub-elements... uses name of detector and ID number as defined in the XML // detector tag std::string detName = x_det.nameStr(); sens.setType("calorimeter"); dd4hep::DetElement sdet(detName, x_det.id()); // get the dimensions tag xml_dim_t dim = x_det.dimensions(); // read its attributes double rmin = dim.rmin(); double rmax = dim.rmax(); double zmax = dim.zmax(); // Make a Cylinder dd4hep::Tube envelope(rmin, rmax, zmax); dd4hep::Material air = theDetector.air(); dd4hep::Volume envelopeVol(detName + "_envelope", envelope, air); dd4hep::PlacedVolume physvol = theDetector.pickMotherVolume(sdet).placeVolume(envelopeVol); // add system ID and identify as barrel (as opposed to endcap +/-1) physvol.addPhysVolID("system", sdet.id()).addPhysVolID(_U(side), 0); sdet.setPlacement(physvol); // Interpretation of layers: double currentInnerRadius = rmin; // running inner radius dd4hep::Layering layering(x_det); // convenience class int layerNum = 0; for (xml_coll_t c(x_det, _U(layer)); c; ++c, ++layerNum) { xml_comp_t x_layer = c; const dd4hep::Layer *lay = layering.layer(layerNum); // Get the layer from the layering engine. const double layerThickness = lay->thickness(); // loop over the number of repetitions for (int i = 0, repeat = x_layer.repeat(); i < repeat; ++i, ++layerNum) { std::string layerName = detName + dd4hep::_toString(layerNum, "_layer%d"); // make a volume for the layer dd4hep::Tube layerTube(currentInnerRadius, currentInnerRadius + layerThickness, zmax); dd4hep::Volume layerVol(layerName, layerTube, air); dd4hep::DetElement layerElement(sdet, layerName, layerNum); dd4hep::PlacedVolume layerVolPlaced = envelopeVol.placeVolume(layerVol); layerVolPlaced.addPhysVolID("layer", layerNum); int sliceNum = 0; for (xml_coll_t slice(x_layer, _U(slice)); slice; ++slice, ++sliceNum) { xml_comp_t x_slice = slice; double sliceThickness = x_slice.thickness(); dd4hep::Material sliceMat = theDetector.material(x_slice.materialStr()); std::string sliceName = layerName + dd4hep::_toString(sliceNum, "slice%d"); dd4hep::Tube sliceTube(currentInnerRadius, currentInnerRadius + sliceThickness, zmax); dd4hep::Volume sliceVol(sliceName, sliceTube, sliceMat); if (x_slice.isSensitive()) { sliceVol.setSensitiveDetector(sens); } // place the slice in the layer layerVol.placeVolume(sliceVol); currentInnerRadius += sliceThickness; } // slices } // repetitions } // layers return sdet; } DECLARE_DETELEMENT(MyFirstDetector, create_detector) ```

To take effect, the code has to be recompiled, ```shell cd MyFirstDetector/build make install ``` After every change in the geometry, the overlap check must be run. To do so, go to the top working directory and run it as before ```shell ddsim --compactFile FCCee_o1_v05/FCCee_o1_v05.xml \ --runType run \ --macroFile overlap.mac > overlapDump3 & ``` The number of checks now increased, and if everything is done properly, no overlaps are found. Now the geometry is ready to go for a simulation. As it was done previously, we use `ddsim` to run a simulation of 100 muons in the geometry that has just been defined, as in the previous steps ```shell ddsim --compactFile FCCee_o1_v05/FCCee_o1_v05.xml \ --enableGun \ --gun.distribution uniform \ --gun.energy "10*GeV" \ --gun.particle mu- \ --numberOfEvents 100 \ --outputFile Step3_edm4hep.root ``` Modify `showPlots.py` to display properties from this collection (MyReadout).
Click to show the code of the python script showPlots.py ```python #!/bin/env python import sys, ROOT from histoprint import print_hist ROOT.gROOT.SetBatch(True) if len(sys.argv) < 2: print("Please specify input file"); sys.exit(1) inputFile = sys.argv[1]; print("Reading:", inputFile) tfile = ROOT.TFile.Open(inputFile) myTree = tfile.Get("events") myTree.Draw("sqrt(MyReadout.position.x*MyReadout.position.x+MyReadout.position.y*MyReadout.position.y)>>rHist(100, 2150, 2352)") myTree.Draw("MyReadout.energy>>eHist(30, 0, 0.002)") print_hist(tfile.Get("rHist"), title="Hits per Layer") print_hist(tfile.Get("eHist"), title="Energy per Hit") ```

## Final note Changing geometries with DD4hep can range from trivial to sophisticated. The overlap checker shall be run after every change. Check the following links for further information and support: 1. Browse the [DD4hep documentation](https://dd4hep.web.cern.ch/dd4hep/) and [beginners guide](https://dd4hep.web.cern.ch/dd4hep/page/beginners-guide/) 2. Ask at [https://github.com/aidasoft/dd4hep](https://github.com/aidasoft/dd4hep) 3. Post your questions or suggestions at FCC software [forum](https://fccsw-forum.web.cern.ch/) or [Mattermost channel](https://mattermost.web.cern.ch/fccsw/)