From 2f77be65f0e223ba9e11a78217e41ac6497004e7 Mon Sep 17 00:00:00 2001 From: Whitney Armstrong <warmstrong@anl.gov> Date: Mon, 23 Mar 2020 16:58:05 -0500 Subject: [PATCH] new file: part1/overview.md --- src/docs/part1/overview.md | 380 +++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 src/docs/part1/overview.md diff --git a/src/docs/part1/overview.md b/src/docs/part1/overview.md new file mode 100644 index 0000000..a45006d --- /dev/null +++ b/src/docs/part1/overview.md @@ -0,0 +1,380 @@ +Tutorial Part 1 +=============== + + * [Introduction](#introduction) + * [How to build a detector from scratch](#how-to-build-a-detector-from-scratch) + + [Compiling a new detector](#compiling-a-new-detector) + + [Building the geometry](#building-the-geometry) + - [Compact detector description entry element](#compact-detector-description-entry-element) + - [Geometry Construction](#geometry-construction) + * [XML Parsing Tip : Provide good default values](#xml-parsing-tip--provide-good-default-values) + - [Critical parts of build_detector](#critical-parts-of-build_detector) + * [The Readout and Bit Fields](#the-readout-and-bit-fields) + * [Simple Reconstruction Overview of scripts](#simple-reconstruction-overview-of-scripts) + + [Dependencies](#dependencies) + + [Running the scripts](#running-the-scripts) + + [To Do](#to-do) + +Introduction +------------ + +To a newcomer it might not be immediately clear why a *generic detector +library* based on `dd4hep` is a good solution to the 'simulation and +reconstruction geometry problem'. However, with the following examples we +intend to show how generic detector libraries can be used to access the +geometry information in simulation and reconstruction. + +We try to emphasize in the following examples that dd4hep (plus a data model) +allows the software components to be only loosely decoupled. This allows for a +'no framework' framework approach where the emphasis is on the underlying +algorithms used (or being designed). Furthermore, using ROOT's TDataframe, the +details of each task are made manifest. + +Furthermore, the 'no framework' framework allows for the each step to be +executed using any tool available, however, the IO and data model should be +fixed. + + +How to build a detector from scratch +------------------------------------ + +Building a new (generic) detector using dd4hep is rather straight forward if we +make the following assumptions. + +1. We will use the built-in sensitive detectors +2. We will use the built-in data model (hits) associated with these detectors + +These items can be customized by using the DD4hep plugin mechanism. This will +be covered in another tutorial. + +### Compiling a new detector + +For this tutorial we will build a simplified Roman Pot detector. +We will discuss the detector built in the source file +`src/GenericDetectors/src/SimpleRomanPot_geo.cpp`. +To compile this detector into the GenericDetectors library the detector needs +to be added to the list of sources in the cmake file +`src/GenericDetectors/CMakeLists.txt`. + +``` +dd4hep_add_plugin(${a_lib_name} SOURCES + src/BeamPipe_geo.cpp + ... + src/SimpleRomanPot_geo.cpp # add this line + ) +``` + +### Building the geometry + +The work of defining the detector is done in a function (here called +`build_detector`) that is registered using the DD4hep plugin macro +`DECLARE_DETELEMENT`. + +``` +static Ref_t build_detector(Detector& dtor, xml_h e, SensitiveDetector sens) +{ + xml_det_t x_det = e; + Material air = dtor.air(); +... +} +DECLARE_DETELEMENT(SimpleRomanPot, build_detector) +``` + +The argument signature of the `build_detector` is: +- `Detector& dtor`: This handle provides the main hook to the detector tree (`dd4hep::Detector`). +- `xml_h e`: Handle to the XML `<detector>` tag associated with the detector in + the "compact" detector description file (more on this later). This provides + the mechanism for feeding in the run-time construction parameters. +- `SensitiveDetector sens`: The sensitive detector to be assigned to the + sensitive volumes/elements of the detector. + +The DD4hep plugin macro `DECLARE_DETELEMENT(SimpleRomanPot, build_detector)` +stamps out the necessary boiler plate code to register a new detector called +`SimpleRomanPot` which is build by calling `build_detector`. + +#### Compact detector description entry element + +The `<detector>` tag defines a new instance of a detector and requires the +attributes "id", "name", and "type". For example: + +``` +<detector id="1" name="MyRomanPot" type="SimpleRomanPot" + vis="RedVis" readout="RomanPotHits" zoffset="1.0*m"> +</detector> +```` + +This defines an instance of the detector named "MyRomanPot" of type +"SimpleRomanPot" (i.e. the type-name given in the first argument of the DD4hep +`DECLARE_DETELEMENT` macro) and with id=1. The additional attributes (vis, +readout, zoffset) will are optional. + +The detector tag is provided as the second argument in the `build_detector` +function. It can be parsed *how ever you want* in order to extract detector +information. The allowed attributes are listed in +[`UnicodeValues.h`](http://test-dd4hep.web.cern.ch/test-dd4hep/doxygen/html/_unicode_values_8h_source.html) +where it is clear how to add new attributes. + + +#### Geometry Construction + +If you are familiar with Geant4 or TGeo geometry construction then this will be +easy. DD4hep has TGeo under hood but there are a few naming tweaks. The +following table will help orient the user. + +| *DD4hep* | *Geant4* | *TGeo | +| ------ | -------- | ---- | +| Solid | G4VSolid | TGeoShape | +| Volume | G4LogicalVolume | TGeoVolume | +| PlacedVolume | G4PVPlacement | TGeoNode | +| Element | G4Element | TGeoElement | +| Material | G4Material | TGeoMaterial/TGeoMedium| + +##### XML Parsing Tip : Provide good default values + +If you have a detector parameter which we later will tweak (while optimizing +the design) try to get the value from the xml element but provide a good +default value. For example: + +``` +double radius = ( x_det.hasAttr(_Unicode(radius)) ) ? x_det.attr<double>(_Unicode(radius)) : 5.0*dd4hep::cm; +``` + +This provides a default radius of 5 cm when `x_det` does not have a "radius" +attribute defined. We will return to this later. + +#### Critical parts of build_detector + + +We will now look at parts of the source file `src/GenericDetectors/src/SimpleRomanPot_geo.cpp`. + +``` +static Ref_t build_detector(Detector& dtor, xml_h e, SensitiveDetector sens) +{ + xml_det_t x_det = e; + Material air = dtor.air(); + Material carbon = dtor.material("CarbonFiber"); + Material silicon = dtor.material("SiliconOxide"); + Material aluminum = dtor.material("Aluminum"); + Material vacuum = dtor.material("Vacuum"); + Material supp_mat = carbon; + Material sens_mat = silicon; + int det_id = x_det.id(); // id=1 + string det_name = x_det.nameStr(); // "MyRomanPot" +``` + +Here we are grabbing the materials that are assumed to be already defined. Also +we are getting the detector id and name defined in the `detector` tag. + +Next we define an [Assembly +volume](http://test-dd4hep.web.cern.ch/test-dd4hep/doxygen/html/classdd4hep_1_1_assembly.html). +Here we also stumble upon the important class +[`dd4hep::DetElement`](http://test-dd4hep.web.cern.ch/test-dd4hep/doxygen/html/classdd4hep_1_1_det_element.html#details). +It is a means of providing the detector hierarchy/tree, but doesn't necessarily +have to map exactly to detector geometry. However, it typically will typically +parallel the geometry (and probably should). + +``` + string module_name = "RomanPot"; + Assembly assembly(det_name + "_assembly"); + DetElement sdet( det_name, det_id); + sens.setType("tracker"); +``` +The last line sets the `SensitiveDetector sens` argument to be the tracker type +(which is a DD4hep built-in). We will soon assign this to sensitive volumes. +`sdet` is associated with the mother detector element by the constructor which +looks up the detector name (here "MyRomanPot"). + +``` + double z_offset = (x_det.hasAttr(_Unicode(zoffset))) ? x_det.attr<double>(_Unicode(zoffset)) : 0.0; + double thickness = (x_det.hasAttr(_Unicode(thickness))) ? x_det.attr<double>(_Unicode(thickness)) : 0.01*dd4hep::cm; +``` +Here we grab attributes and provide default values. We continue with default +values that could also be define through attributes, however, we will want to +add child elements of the detector tag (so the attributes does not grow too +long). + +``` + double rp_chamber_thickness = 5.0*dd4hep::mm; + double rp_chamber_radius = 5.0*dd4hep::cm; + double rp_chamber_length = 50.0*dd4hep::cm; + Tube rp_beam_pipe_tube(rp_chamber_radius, rp_chamber_radius+rp_chamber_thickness, rp_chamber_length/2.0); + Tube rp_beam_vacuum_tube(0.0, rp_chamber_radius+rp_chamber_thickness, rp_chamber_length/2.0); + Tube rp_beam_vacuum_tube2(0.0, rp_chamber_radius, rp_chamber_length/2.0); + + double rp_detector_tube_radius = 2.5*dd4hep::cm; + double rp_detector_tube_length = 20.0*dd4hep::cm; + Tube rp_detector_tube(rp_detector_tube_radius, rp_detector_tube_radius+rp_chamber_thickness, rp_detector_tube_length/2.0); + Tube rp_detector_vacuum_tube(0.0, rp_detector_tube_radius+rp_chamber_thickness, rp_detector_tube_length/2.0); + Tube rp_detector_vacuum_tube2(0.0, rp_detector_tube_radius, rp_detector_tube_length/2.0); + + ROOT::Math::Rotation3D rot_X( ROOT::Math::RotationX(M_PI/2.0) ); + ROOT::Math::Rotation3D rot_Y( ROOT::Math::RotationY(M_PI/2.0) ); + + UnionSolid rp_chamber_tee1(rp_beam_vacuum_tube, rp_detector_vacuum_tube, rot_X); + UnionSolid rp_chamber_tee12(rp_chamber_tee1, rp_detector_vacuum_tube, rot_Y); + + UnionSolid rp_chamber_tee2(rp_beam_vacuum_tube2, rp_detector_vacuum_tube2, rot_X); + UnionSolid rp_chamber_tee22(rp_chamber_tee2, rp_detector_vacuum_tube2, rot_Y); + + SubtractionSolid sub1(rp_chamber_tee12,rp_chamber_tee22); + Volume rp_chamber_vol("rp_chamber_walls_vol", sub1, aluminum); + Volume rp_vacuum_vol("rp_chamber_vacuum_vol", rp_chamber_tee22, vacuum); +``` +The above code builds the up the two solids associated with 3 tubes +intersecting. One volume is the aluminum vacuum chamber walls and the other is +the vacuum contained within this envelope. + +Next we must place these two volumes in the assembly volume (which is just an +empty container-like volume. The PlacedVolume is then associated with a +BitFieldValue in the readout's BitField64 readout string. In this case the +"layer" BitFieldValue. The BitField64 is used to construct unique VolumeIDs and +CellIDs for PlacedVolumes and Segmentations respectively. + +``` + PlacedVolume pv; + pv = assembly.placeVolume( rp_chamber_vol ); + pv = assembly.placeVolume( rp_vacuum_vol ); + pv.addPhysVolID( "layer", 2 ); +``` + +Set the PlacedVolume BitFieldValue ID. "2" in this case. + +``` + double supp_x_half = 1.0*dd4hep::cm; + double supp_y_half = 1.0*dd4hep::cm; + double supp_thickness = 1.0*dd4hep::mm; + double supp_gap_half_width = 1.0*dd4hep::mm; + double sens_thickness = 0.1*dd4hep::mm; + + Box supp_box( supp_x_half, supp_y_half, supp_thickness/2.0 ); + Box sens_box( supp_x_half-supp_gap_half_width, supp_y_half-supp_gap_half_width, sens_thickness/2.0 ); + +``` + +Next we define vectors which are used to define a "surface" (which will later +generate simulation tracker hits). + +``` + // create a measurement plane for the tracking surface attched to the sensitive volume + Vector3D u( 1. , 0. , 0. ) ; + Vector3D v( 0. , 1. , 0. ) ; + Vector3D n( 0. , 0. , 1. ) ; + //Vector3D o( 0. , 0. , 0. ) ; + + // compute the inner and outer thicknesses that need to be assigned to the tracking surface + // depending on wether the support is above or below the sensor + // The tracking surface is used in reconstruction. It provides material thickness + // and radation lengths needed for various algorithms, routines, etc. + double inner_thickness = supp_thickness/2.0; + double outer_thickness = supp_thickness/2.0; + double z_shift = 5.0*dd4hep::mm; + double xy_shift = 15.0*dd4hep::mm; + + SurfaceType type( SurfaceType::Sensitive ) ; +``` + +We now define a simple rectangular pixel sensor. This will be the first of +four: two will come in along the x axis and two along the y axis. + +``` + // ------------- x1 + Volume support1_vol( "xsenseor_supp", supp_box, supp_mat ); + Volume sensor1_vol( "xsenseor_sens", sens_box, sens_mat ); + VolPlane surf1( sensor1_vol, type, inner_thickness , outer_thickness, u,v,n); + sensor1_vol.setSensitiveDetector(sens); +``` +The code above builds two volumes one which will contain the sensitive volume. +The sensitive volume is assigned to be a sensitive detector. + +``` + DetElement layer1_DE( sdet, "layer1_DE", 1 ); + pv = rp_vacuum_vol.placeVolume( support1_vol, Position(xy_shift,0, -z_shift) ); + pv.addPhysVolID("layer", 1 ); + layer1_DE.setPlacement( pv ) ; +``` +The above creates a new DetElement, places the support volume in the vacuum, +sets this PlacedVolume to associate with the layer bitfield, and adds the PV to +the DetElement. Note the DetElement is constructed with the parent element +(sdet) being the first argument. In this way it is clear we are building, +(semi-)parallel to the geometry, a detector element hierarchy. + +``` + DetElement mod1( layer1_DE , "module_1", 1 ); + pv = support1_vol.placeVolume(sensor1_vol, Position(0,0,0)); + pv.addPhysVolID("module", 1 ); + mod1.setPlacement( pv ); +``` +This creates the module DetElement and places the sensitive volume in the +support volume (already placed). It also assocates the pv with the module +bitfield and then adds the PV to the DetElement. + +The above is repeated for the three remaining sensitive elements. + +Finally we get the top level volume to place the assemble volume. Note we are +using the zoffset. This PV is then associated with the top level "system" +bitfieldvalue. + +``` + pv = dtor.pickMotherVolume(sdet).placeVolume(assembly, Position(0,0,z_offset)); + pv.addPhysVolID("system", det_id); // Set the subdetector system ID. + sdet.setPlacement(pv); + + assembly->GetShape()->ComputeBBox() ; + return sdet; +} + +``` + +The Readout and Bit Fields +-------------------------- + + +Simple Reconstruction Overview of scripts +--------------------------------------- + +In the examples here we use ROOT and the LCIO data model. +The following scripts should be executed in order: + +1. `run_example` : bash script that runs `ddsim` with the Geant4 macro file + `gps.mac` and the input geometry defined in `gem_tracker_disc.xml`. +2. `scripts/example_hit_position.cxx` : ROOT script showing how to use both the + "segmentation" and "surface" geometry to get the hit position information. +3. `scripts/example_cell_size.cxx` : ROOT script showing how information about + the geometry beyond the position. +4. `scripts/example_digi.cxx` : ROOT script performing a crude digitization of + gem tracker hits. +5. `scripts/example_hit_recon.cxx` : ROOT script that shows how to to use + dd4hep to lookup a 'segment' (cell) position based on the unique cellID + attached to the hit object. +6. `scripts/example_tracking.cxx` : ROOT script showing how to use the + `GenFind` finding library along with `GenFit` to produce tracks. + +### Dependencies + +There are some library dependencies: + +- DD4hep +- lcgeo +- GenFind +- GenFit +- NPdet + + +### Running the scripts + + +``` +./run_example +root scripts/example_digi.cxx++ +root scripts/example_hit_position.cxx++ # no output +root scripts/example_cell_size.cxx++ # no output +root scripts/example_hit_recon.cxx++ +root scripts/example_tracking.cxx++ +``` + +### To Do + +- Show how to build a detector +- Finish track fitting script. +- Create example for basic PFA usage + -- GitLab