From ceec0556da7e74512fd66a0d3399fba51b6b43a0 Mon Sep 17 00:00:00 2001 From: Alvaro Tolosa Delgado <114166109+atolosadelgado@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:49:26 +0200 Subject: [PATCH] Add the ARC detector (#271) --- CMakeLists.txt | 1 + FCCee/CLD/compact/CLD_o3_v01/ARC_o1_v01.xml | 151 +++ FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml | 50 + FCCee/CLD/compact/CLD_o3_v01/README.md | 38 + .../RadiatorCell_FinalOptimisation_o1_v01.xml | 228 +++++ FCCee/CLD/compact/CLD_o3_v01/elements.xml | 884 ++++++++++++++++ FCCee/CLD/compact/CLD_o3_v01/materials.xml | 19 + .../CLD_o3_v01/materials_arc_o1_v01.xml | 401 ++++++++ FCCee/CLD/compact/README.md | 4 + detector/PID/ARC_geo_o1_v01.cpp | 964 ++++++++++++++++++ detector/PID/README.md | 33 + example/arcfullsim.py | 134 +++ lcgeoTests/CMakeLists.txt | 15 + scripts/run_geo_visualization_qt.sh | 6 + utils/overlap.mac | 4 + 15 files changed, 2932 insertions(+) create mode 100644 FCCee/CLD/compact/CLD_o3_v01/ARC_o1_v01.xml create mode 100644 FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml create mode 100644 FCCee/CLD/compact/CLD_o3_v01/README.md create mode 100644 FCCee/CLD/compact/CLD_o3_v01/RadiatorCell_FinalOptimisation_o1_v01.xml create mode 100644 FCCee/CLD/compact/CLD_o3_v01/elements.xml create mode 100644 FCCee/CLD/compact/CLD_o3_v01/materials.xml create mode 100644 FCCee/CLD/compact/CLD_o3_v01/materials_arc_o1_v01.xml create mode 100644 FCCee/CLD/compact/README.md create mode 100644 detector/PID/ARC_geo_o1_v01.cpp create mode 100644 detector/PID/README.md create mode 100755 example/arcfullsim.py create mode 100755 scripts/run_geo_visualization_qt.sh create mode 100644 utils/overlap.mac diff --git a/CMakeLists.txt b/CMakeLists.txt index a8510a0b4..0f81c84b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ file(GLOB sources ./detector/CaloTB/*.cpp ./FCalTB/setup/*.cpp ./plugins/LinearSortingPolicy.cpp + ./detector/PID/ARC_geo_o1_v01.cpp ) file(GLOB G4sources diff --git a/FCCee/CLD/compact/CLD_o3_v01/ARC_o1_v01.xml b/FCCee/CLD/compact/CLD_o3_v01/ARC_o1_v01.xml new file mode 100644 index 000000000..f5390b496 --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/ARC_o1_v01.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + system:5,barrel:3,cellnumber:24,x:32:-16,y:-16 + + + + diff --git a/FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml b/FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml new file mode 100644 index 000000000..1e3004e3d --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml @@ -0,0 +1,50 @@ + + + + The compact format of the CLD+ARC detector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FCCee/CLD/compact/CLD_o3_v01/README.md b/FCCee/CLD/compact/CLD_o3_v01/README.md new file mode 100644 index 000000000..08aa96673 --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/README.md @@ -0,0 +1,38 @@ +CLD_o3_v01 Detector +====================== + +This option (o3) of CLD includes the Array of RICH Cells (ARC) subdetector. The ARC is placed after the tracker and before the calorimeters. + +# PID assisted by ARC + +Array of RICH Cells (ARC) is a novel RICH detector concept. Detailed description of it can be found in [this dedicated README file](../../../detector/PID/README.md). + +The identification of the particle crossing the ARC detector is based on the Cherenkov light produced by two components: a radiator gas, and a layer of aerogel. + +Similar systems have been used in the past, e.g. at the LHCb experiment. Details of the setup and analysis of the data can be found [here](https://s3.cern.ch/inspire-prod-files-9/92927eb16166b155de56b61339f05521). + + + +## ARC detector compact files + +There are 4 compact `.xml` files that are needed for building the detector. + +1. `RadiatorCell_FinalOptimisation_o1_v01.xml`. Contains the parameters that determine the position of mirrors and sensors inside the cells. See the *REMINDER* below. + +2. `materials_arc_o1_v01.xml`. Contains the description of the different materials needed in the ARC, including their optical properties if needed and the optical surfaces. + +3. `elements.xml`. Contains the whole periodic table of the elements. This file is a copy of the one provided by DD4hep. + +4. `ARC_o1_v01.xml`. Contains the detector section which builds the detector elements `ARCENDCAP_o1_v01_T` and `ARCBARREL_o1_v01_T`, visualization attributes, the segmentation and readout of the sensors, and specifications to build the detector: + +* Material of each component: vessel, radiator gas, light sensor, aerogel layer, cooling plate + +* Total size of the light sensor, now implemented as rectangular (X length can be different from Y) + +* Thickness of mirror and sensor + +* Z position of endcap: The variable `ARC_ENDCAP_ZPOS` defines the middle point of the endcap along the Z axis. Therefore, the endcap spans over `ARC_ENDCAP_ZPOS +- ARC_ENDCAP_LENGTH/2` along the Z axis. + +* Readout and segmentation of the light sensors. The readout includes the definition of the Volume ID bit-field. Barrel field corresponds to barrel (0) or endcaps (+/-1), cellnumber fields correspond to a number which is given consecutively as the cells are placed. + +*REMINDER*: the cell parameters defined in the file `RadiatorCell_FinalOptimization.xml` were optimized by a dedicated ray-tracing dedicated software for the initial geometry of the ARC detector (radial depth of 20 cm, outer radius of 2.1 m and a length of 4.4 m). If geometry of the ARC changes, these cell parameters should be optimized again. diff --git a/FCCee/CLD/compact/CLD_o3_v01/RadiatorCell_FinalOptimisation_o1_v01.xml b/FCCee/CLD/compact/CLD_o3_v01/RadiatorCell_FinalOptimisation_o1_v01.xml new file mode 100644 index 000000000..2ca0047f0 --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/RadiatorCell_FinalOptimisation_o1_v01.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FCCee/CLD/compact/CLD_o3_v01/elements.xml b/FCCee/CLD/compact/CLD_o3_v01/elements.xml new file mode 100644 index 000000000..f35eb3454 --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/elements.xml @@ -0,0 +1,884 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FCCee/CLD/compact/CLD_o3_v01/materials.xml b/FCCee/CLD/compact/CLD_o3_v01/materials.xml new file mode 100644 index 000000000..3d3274d67 --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/materials.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/FCCee/CLD/compact/CLD_o3_v01/materials_arc_o1_v01.xml b/FCCee/CLD/compact/CLD_o3_v01/materials_arc_o1_v01.xml new file mode 100644 index 000000000..f38b74d57 --- /dev/null +++ b/FCCee/CLD/compact/CLD_o3_v01/materials_arc_o1_v01.xml @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + n_air = [dens(Si02)-dens(aerogel)] / [dens(Si02)-dens(Air) ] + + + + + + + + + TO BE IMPROVED + + + + + + + + CMS ECAL https://gitlab.cern.ch/geant4/geant4/-/blob/master/examples/advanced/composite_calorimeter/dataglobal/material.cms#L183 + + + + + + + Filling of the ARC walls, made of aerogel as template material + n_air = [dens(Si02)-dens(aerogel)] / [dens(Si02)-dens(Air) ] + + + + + + + + + + + + + + + + + diff --git a/FCCee/CLD/compact/README.md b/FCCee/CLD/compact/README.md new file mode 100644 index 000000000..609fb2994 --- /dev/null +++ b/FCCee/CLD/compact/README.md @@ -0,0 +1,4 @@ +CLD +==== + +CLD_o3_v01 : CLD option 3 includes ARC detector for PID diff --git a/detector/PID/ARC_geo_o1_v01.cpp b/detector/PID/ARC_geo_o1_v01.cpp new file mode 100644 index 000000000..fd31d8ced --- /dev/null +++ b/detector/PID/ARC_geo_o1_v01.cpp @@ -0,0 +1,964 @@ +//---------------------------------- +// ARC detector v01 +//---------------------------------- + +/*! + * \brief Detector constructor of barrel of ARC detector + * \details This code creates full geometry of ARC detector + * Evolved from the pfRICH example in DD4hep. + * \author Alvaro Tolosa-Delgado alvaro.tolosa.delgado@cern.ch + * \author Martin Tat martin.tat@cern.ch + * \version 1 + * \date 2023 + * \pre DD4hep compiled with Geant4+Qt + * \bug Walls do not reflect optical photons. Hard-coded values in many places. + */ + +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/OpticalSurfaces.h" +#include "DD4hep/Printout.h" + +using namespace dd4hep; + +// #define DUMP_SENSOR_POSITIONS + +/// Function to build ARC endcaps +static Ref_t create_arc_endcap_cell(Detector &desc, xml::Handle_t handle, SensitiveDetector sens) +{ + xml::DetElement detElem = handle; + std::string detName = detElem.nameStr(); + int detID = detElem.id(); + OpticalSurfaceManager surfMgr = desc.surfaceManager(); + DetElement det(detName, detID); + sens.setType("tracker"); + + double zpos_endcap = detElem.attr(_Unicode(zpos)); + + auto gasElem = detElem.child(_Unicode(radiatorgas)); + auto gasvolMat = desc.material(gasElem.attr(_Unicode(material))); + auto gasvolVis = desc.visAttributes(gasElem.attr(_Unicode(vis))); + + auto vesselElem = detElem.child(_Unicode(vessel)); + auto vesselSkinMat = desc.material(vesselElem.attr(_Unicode(skinMaterial))); + auto vesselSkinVis = desc.visAttributes(vesselElem.attr(_Unicode(skin_vis))); + + auto vesselBulkMat = desc.material(vesselElem.attr(_Unicode(bulk_material))); + auto vesselBulkVis = desc.visAttributes(vesselElem.attr(_Unicode(bulk_vis))); + + double bulk_skin_ratio = vesselElem.attr(_Unicode(bulk_skin_ratio)); + + if( 0 > bulk_skin_ratio || 1 < bulk_skin_ratio ) + throw std::runtime_error("ARC: bulk_skin_ratio must be a number between 0 and 1"); + + + // read Martin file and store parameters by name in the map +// fill_cell_parameters_m(); + + // mother volume corresponds to the world + Volume motherVol = desc.pickMotherVolume(det); + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // VESSEL PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double vessel_outer_r = desc.constantAsDouble("ARC_ENDCAP_R_OUTER"); + double vessel_inner_r = desc.constantAsDouble("ARC_ENDCAP_R_INNER"); + double vessel_length = desc.constantAsDouble("ARC_ENDCAP_LENGTH"); + double vessel_wall_thickness = desc.constantAsDouble("ARC_VESSEL_WALL_THICKNESS"); + if (vessel_outer_r <= vessel_inner_r) + throw std::runtime_error("Ilegal parameters: vessel_outer_r <= vessel_inner_r"); + // // //-------------------------------------------------------------// // // + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // AEROGEL PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double aerogel_thickness = desc.constantAsDouble("ARC_AEROGEL_THICKNESS"); + auto aerogelMat = desc.material("Aerogel_PFRICH"); + // // //-------------------------------------------------------------// // // + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // COOLING PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double cooling_thickness = desc.constantAsDouble("ARC_COOLING_THICKNESS"); + // // //-------------------------------------------------------------// // // + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // CELL PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + /// Cell is an hexagonal prysm + double hexagon_side_length = 14.815 * cm; + double hexagon_apothem = hexagon_side_length * cos(30 * deg); + /// each cell corresponds to one object of the following class + /// which gathers all the important parameters + /// this info can be moved later to compact file + struct mycell_t + { + /// Martin number for row + int row = {0}; + /// Martin number for column + int col = {0}; + /// Roger ID number + int RID = {-1}; + /// x position of the cell + double x = {0.}; + /// y position of the cell + double y = {0.}; + /// if reflected + bool isReflected = {false}; + }; + +// // // // // // // // // // // // // // // // // // // // // // // +// SCHEME OF UNIQUE CELLS INSIDE A SECTOR +// CELL NUMBERING CORRESPONDS TO ROGERS +// MARTIN NUMBERING SPECIFIED BY ROW/COLUMN +// THIS SECTOR MUST BE MIRRORED, AND THEN +// BOTH (ORIGINAL AND MIRRORED) REPEATED 6 TIMES +// +// _____ _____ +// / \ / \ . +// 7 _____/ 21 \_____/ 18 \ . +// / \ / \ / . +// 7 / 20 \_____/ 17 \_____/ . +// \ / \ / \ . +// 6 \_____/ 16 \_____/ 14 \ . +// / \ / \ / . +// 6 / 15 \_____/ 13 \_____/ . +// \ / \ / \ . +// 5 \_____/ 12 \_____/ 10 \ . +// / \ / \ / . +// 5 / 11 \_____/ 9 \_____/ . +// \ / \ / \ . +// 4 \_____/ 8 \_____/ 7 \ . +// \ / \ / . +// 4 \_____/ 6 \_____/ . +// / \ / \ . +// 3 / 5 \_____/ 4 \ . +// \ / \ / . +// 3 \_____/ 3 \_____/ . +// \ / \ . +// 2 \_____/ 2 \ . +// / \ / . +// 2 / 1 \_____/ . +// \ / . +// COLUMN ^ \_____/ . +// ROW-> 4 3 2 1 +// +// Y axis = column +// X axis = row +// // // // // // // // // // // // // // // // // // // // // // // + + /// vector with cell geometric parameters + std::vector mycell_v(21); + { + double hx_u = hexagon_apothem; + double hx_x = hexagon_side_length; + mycell_v[0] = {1, 2, 2, 0, 4 * hx_u}; + mycell_v[1] = {1, 3, 4, 0, 6 * hx_u}; + mycell_v[2] = {1, 4, 7, 0, 8 * hx_u}; + mycell_v[3] = {1, 5, 10, 0, 10 * hx_u}; + mycell_v[4] = {1, 6, 14, 0, 12 * hx_u}; + mycell_v[5] = {1, 7, 18, 0, 14 * hx_u}; + mycell_v[6] = {2, 2, 1, -1.5 * hx_x, 3 * hx_u}; + mycell_v[7] = {2, 3, 3, -1.5 * hx_x, 5 * hx_u, true}; + mycell_v[8] = {2, 4, 6, -1.5 * hx_x, 7 * hx_u, true}; + mycell_v[9] = {2, 5, 9, -1.5 * hx_x, 9 * hx_u, true}; + mycell_v[10] = {2, 6, 13, -1.5 * hx_x, 11 * hx_u, true}; + mycell_v[11] = {2, 7, 17, -1.5 * hx_x, 13 * hx_u, true}; + mycell_v[12] = {3, 3, 5, -3.0 * hx_x, 6 * hx_u}; + mycell_v[13] = {3, 4, 8, -3.0 * hx_x, 8 * hx_u, true}; + mycell_v[14] = {3, 5, 12, -3.0 * hx_x, 10 * hx_u, true}; + mycell_v[15] = {3, 6, 16, -3.0 * hx_x, 12 * hx_u, true}; + mycell_v[16] = {3, 7, 21, -3.0 * hx_x, 14 * hx_u, true}; + mycell_v[17] = {4, 5, 11, -4.5 * hx_x, 9 * hx_u}; + mycell_v[18] = {4, 6, 15, -4.5 * hx_x, 11 * hx_u, true}; + mycell_v[19] = {4, 7, 20, -4.5 * hx_x, 13 * hx_u, true}; + mycell_v[20] = {5, 6, 19, -6.0 * hx_x, 12 * hx_u}; + } + + /// Distance in phi angle between complete sectors + double phistep = 60 * deg; + /// number of repetition of sectors + int phinmax = 6; // 6; + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // MIRROR PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double mirror_z_origin_Martin = vessel_length / 2. - vessel_wall_thickness - 37 * cm; + auto mirrorElem = detElem.child(_Unicode(mirror)); + double mirrorThickness = mirrorElem.attr(_Unicode(thickness)); + auto mirrorSurf = surfMgr.opticalSurface(mirrorElem.attr(_Unicode(surface))); + auto mirrorMat = desc.material(mirrorElem.attr(_Unicode(material))); + // // //-------------------------------------------------------------// // // + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // LIGHT SENSOR PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + //default values + double sensor_sidex = 8 * cm; + double sensor_sidey = 8 * cm; + double sensor_thickness = 0.2 * cm; + // empirical distance to keep the sensor inside the cell volume + double sensor_z_origin_Martin = -vessel_length / 2. + vessel_wall_thickness + 0.5 * cooling_thickness; + auto sensorMat = desc.material("SiliconOptical"); + auto sensorVis = desc.visAttributes("no_vis"); + + // Read from xml the parameters for the sensor module + { + auto sensorElem = detElem.child(_Unicode(sensors)); + sensor_sidex = sensorElem.attr(_Unicode(sensor_side_X)); + sensor_sidey = sensorElem.attr(_Unicode(sensor_side_Y)); + sensor_thickness = sensorElem.attr(_Unicode(thickness)); + sensorMat = desc.material(sensorElem.attr(_Unicode(material))); + sensorVis = desc.visAttributes(sensorElem.attr(_Unicode(vis))); + } + // // //-------------------------------------------------------------// // // + + + // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // + // // //+++++++++++ BUILD VESSEL, CELL AND SENSOR VOLUMES ++++++++++// // // + // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // + + // Build cylinder for gas, and the vessel for the gas + Tube gasenvelopeS( vessel_inner_r + vessel_wall_thickness, + vessel_outer_r - vessel_wall_thickness, + vessel_length/2.); + Volume endcap_cells_gas_envelope (detName+"_gasEnvelope", gasenvelopeS, gasvolMat ); + endcap_cells_gas_envelope.setVisAttributes( desc.visAttributes("arc_envelope_vis") ); + + Tube vesselEnvelopeSolid( vessel_inner_r, + vessel_outer_r, + vessel_length/2. + vessel_wall_thickness); + Volume endcap_cells_vessel_envelope (detName+"_vesselEnvelope", vesselEnvelopeSolid, vesselSkinMat ); + endcap_cells_vessel_envelope.setVisAttributes( vesselSkinVis ); + + // if 0==bulk_skin_ratio do not create bulk at all + if(0 alpha) + alpha += 180 * deg; + double dx = center_of_sphere_x * cos(alpha); + double dy = center_of_sphere_x * sin(alpha); + + /// 3D transformation of mirrorVolFull in order to place it inside the gas volume + Transform3D mirrorTr(RotationZYX(0, 0, 0), Translation3D(dx, dy, center_of_sphere_z)); + + /// Define the actual mirror as intersection of the hex cell volume and the hollow sphere just defined + Solid mirrorSol = IntersectionSolid(cellS, mirrorShapeFull, mirrorTr); + std::string mirrorVolName = create_part_name_ff("mirror"); + Volume mirrorVol(mirrorVolName, mirrorSol, mirrorMat); + mirrorVol.setVisAttributes(desc.visAttributes(Form("arc_mirror_vis%d", ncell.RID))); + PlacedVolume mirrorPV = cellV.placeVolume(mirrorVol); + + DetElement mirrorDE(cellDE, mirrorVolName + "DE", 6 * cellCounter+1 ); + mirrorDE.setPlacement(mirrorPV); + SkinSurface mirrorSkin(desc, mirrorDE, Form("mirror_optical_surface%d", cellCounter), mirrorSurf, mirrorVol); // FIXME: 3rd arg needs `imod`? + mirrorSkin.isValid(); + + + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ COOLING PLATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + auto coolingTrCell = RotationZYX(0, 0, angle_of_sensor ) * + Translation3D(0, center_of_sensor_x, sensor_z_pos-cooling_z_offset); + + Solid coolingSol = IntersectionSolid(cellS, coolingSol_tube, coolingTrCell); + std::string coolingName = create_part_name_ff("cooling"); + /// TODO: change material + Volume coolingVol( coolingName , coolingSol, mirrorMat ); + coolingVol.setVisAttributes( desc.visAttributes("arc_cooling_vis") ); + cellV.placeVolume(coolingVol); + + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ AEROGEL PLATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + auto aerogelTrCell = RotationZYX(0, 0, angle_of_sensor ) * + Translation3D(0, center_of_sensor_x, sensor_z_pos+aerogel_z_offset); + + Solid aerogelSol = IntersectionSolid(cellS, aerogelSol_tube, aerogelTrCell); + std::string aerogelName = create_part_name_ff("aerogel"); + Volume aerogelVol( aerogelName , aerogelSol, aerogelMat ); + aerogelVol.setVisAttributes( desc.visAttributes("arc_aerogel_vis") ); + cellV.placeVolume(aerogelVol); + + auto sensorTr = RotationZYX(alpha - 90 * deg, 0 , angle_of_sensor )* + Translation3D(0, center_of_sensor_x, sensor_z_pos ); + + + PlacedVolume sensorPV = cellV.placeVolume(sensorVol, sensorTr); +// sensorPV.addPhysVolID("cellnumber", 6 * cellCounter+2); +#ifdef DUMP_SENSOR_POSITIONS + ofile_sensor_pos << 6 * cellCounter+2 << '\t' + << ncell.RID << '\t' + << ncell.isReflected << '\t' + << ncell.x << '\t' + << ncell.y << '\t' + << phin << '\n'; +#endif + DetElement sensorDE(cellDE, create_part_name_ff("sensor") + "DE", 6 * cellCounter+2 ); + sensorDE.setType("tracker"); + sensorDE.setPlacement(sensorPV); + + PlacedVolume cellPV = endcap_cells_gas_envelope.placeVolume(cellV, RotationZ(phistep * phin) * Translation3D(ncell.x, ncell.y, 0)); + cellPV.addPhysVolID("cellnumber", createPhysVolID() );//6*cellCounter + 0); + cellDE.setPlacement( cellPV ); + + if ( ncell.isReflected) + { + std::string cellRefName = create_part_name_ff("cell_ref"); + Volume cellV_reflected(cellRefName, cellS, gasvolMat); + cellV_reflected.setVisAttributes( gasvolVis ); + DetElement cell_reflected_DE(det, cellRefName+"DE", 6 * cellCounter + 3); + Transform3D mirrorTr_reflected(RotationZYX(0, 0, 0), Translation3D(-dx, dy, center_of_sphere_z)); + + /// Define the actual mirror as intersection of the mother volume and the hollow sphere just defined + Solid mirrorSol_reflected = IntersectionSolid(cellS, mirrorShapeFull, mirrorTr_reflected); + Volume mirrorVol_reflected(mirrorVolName + "_ref1", mirrorSol_reflected, mirrorMat); + mirrorVol_reflected.setVisAttributes(desc.visAttributes(Form("arc_mirror_vis%d", ncell.RID))); + PlacedVolume mirror_ref_PV = cellV_reflected.placeVolume(mirrorVol_reflected); + + DetElement mirror_ref_DE(cell_reflected_DE, mirrorVolName + "_ref1" + "DE", 6 * cellCounter+4 ); + mirror_ref_DE.setPlacement(mirror_ref_PV); + SkinSurface mirror_ref_Skin(desc, mirror_ref_DE, Form("mirror_ref_optical_surface%d", cellCounter), mirrorSurf, mirrorVol_reflected); // FIXME: 3rd arg needs `imod`? + mirror_ref_Skin.isValid(); + + auto sensorTr_reflected = RotationZYX(-alpha + 90 * deg, 0 /*90*deg-angle_of_sensor*/, angle_of_sensor)* + Translation3D(0, center_of_sensor_x, sensor_z_origin_Martin); + PlacedVolume sensor_ref_PV = cellV_reflected.placeVolume(sensorVol, sensorTr_reflected); +// sensor_ref_PV.addPhysVolID("cellnumber", 6 * cellCounter+5); + DetElement sensor_ref_DE(cell_reflected_DE, create_part_name_ff("sensor") + "_ref_DE", 6 * cellCounter+5 ); + sensor_ref_DE.setPlacement(sensor_ref_PV); + +#ifdef DUMP_SENSOR_POSITIONS + ofile_sensor_pos << 6 * cellCounter+5 << '\t' + << ncell.RID << '\t' + << ncell.isReflected << '\t' + << ncell.x << '\t' + << ncell.y << '\t' + << phin << '\n'; +#endif + + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ COOLING PLATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ AEROGEL PLATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + cellV_reflected.placeVolume(coolingVol); + cellV_reflected.placeVolume(aerogelVol); + + + PlacedVolume cell_ref_PV = endcap_cells_gas_envelope.placeVolume(cellV_reflected, RotationZ(phistep * phin) * Translation3D(-ncell.x, ncell.y, 0)); + cell_ref_PV.addPhysVolID("cellnumber", createPhysVolID() );//6*cellCounter + 3); + cell_reflected_DE.setPlacement( cell_ref_PV ); + } + } //-- end loop for sector + } //-- end loop for endcap + + endcap_cells_vessel_envelope.placeVolume(endcap_cells_gas_envelope); + + + + Assembly endcaps_assemblyV("endcaps_assemblyV"); + + Transform3D endcapZPos_Tr(RotationZYX(0,0,0), Translation3D(0, 0, zpos_endcap)); + PlacedVolume endcapZPos_PV = endcaps_assemblyV.placeVolume(endcap_cells_vessel_envelope, endcapZPos_Tr); + endcapZPos_PV.addPhysVolID("barrel", 1); + + DetElement endcapZPos_DE(det, "endcapZPos_DE", 0 ); + endcapZPos_DE.setPlacement(endcapZPos_PV); + + + Transform3D envelope_zreflected_Tr(RotationZYX( 0 ,0,180*deg), Translation3D(0, 0, -zpos_endcap)); + PlacedVolume endcapZNeg_PV = endcaps_assemblyV.placeVolume(endcap_cells_vessel_envelope, envelope_zreflected_Tr); + endcapZNeg_PV.addPhysVolID("barrel", 2); + + DetElement endcapZNeg_DE(det, "endcapZNeg_DE", 2 ); + endcapZNeg_DE.setPlacement(endcapZNeg_PV); + + + PlacedVolume endcaps_PV = motherVol.placeVolume(endcaps_assemblyV); + endcaps_PV.addPhysVolID("system", detID); + det.setPlacement(endcaps_PV); + + return det; +} +DECLARE_DETELEMENT(ARCENDCAP_o1_v01_T, create_arc_endcap_cell) + + +/// Function to build ARC barrel +/** + * The geometry tree is the following: + * vessel (CarbFib)-> vessel bulk (foam) + * -> gas envelope (gas)-> gas cell 1 (gas) -> elements (mirror, sensor, aeogel, cooling) + * -> gas envelope (gas)-> gas cell 2 (gas) -> elements (mirror, sensor, aeogel, cooling) + * -> gas envelope (gas)-> gas cell ... + */ +static Ref_t create_arc_barrel_cell(Detector &desc, xml::Handle_t handle, SensitiveDetector sens) +{ + xml::DetElement detElem = handle; + std::string detName = detElem.nameStr(); + int detID = detElem.id(); + OpticalSurfaceManager surfMgr = desc.surfaceManager(); + DetElement det(detName, detID); + sens.setType("tracker"); + + auto gasElem = detElem.child(_Unicode(radiatorgas)); + auto gasvolMat = desc.material(gasElem.attr(_Unicode(material))); + auto gasvolVis = desc.visAttributes(gasElem.attr(_Unicode(vis))); + + auto vesselElem = detElem.child(_Unicode(vessel)); + auto vesselSkinMat = desc.material(vesselElem.attr(_Unicode(skinMaterial))); + auto vesselSkinVis = desc.visAttributes(vesselElem.attr(_Unicode(skin_vis))); + + auto vesselBulkMat = desc.material(vesselElem.attr(_Unicode(bulk_material))); + auto vesselBulkVis = desc.visAttributes(vesselElem.attr(_Unicode(bulk_vis))); + + double bulk_skin_ratio = vesselElem.attr(_Unicode(bulk_skin_ratio)); + + if( 0 > bulk_skin_ratio || 1 < bulk_skin_ratio ) + throw std::runtime_error("ARC: bulk_skin_ratio must be a number between 0 and 1"); + + // mother volume corresponds to the world + Volume motherVol = desc.pickMotherVolume(det); + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // VESSEL PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double vessel_outer_r = desc.constantAsDouble("ARC_BARREL_R_OUTER"); + double vessel_inner_r = desc.constantAsDouble("ARC_BARREL_R_INNER"); + double vessel_length = desc.constantAsDouble("ARC_BARREL_LENGTH"); + double vessel_wall_thickness = desc.constantAsDouble("ARC_VESSEL_WALL_THICKNESS"); + if (vessel_outer_r <= vessel_inner_r) + throw std::runtime_error("Ilegal parameters: vessel_outer_r <= vessel_inner_r"); + // // //-------------------------------------------------------------// // // + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // AEROGEL PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double aerogel_radial_thickness = desc.constantAsDouble("ARC_AEROGEL_THICKNESS"); + auto aerogelMat = desc.material("Aerogel_PFRICH"); + + // // //-------------------------------------------------------------// // // + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // COOLING PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + double cooling_radial_thickness = desc.constantAsDouble("ARC_COOLING_THICKNESS"); + // // //-------------------------------------------------------------// // // + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // CELL PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + /// Distance in x-direction + double hexagon_side_length = 14.815 * cm; + double hex_apothem_length = hexagon_side_length*cos( M_PI / 6. ); // + double zstep = 2 * hex_apothem_length; + /// Distance in phi angle between cells + /// since cells are regular hexagons, this distance matches + /// the angle that one cell covers + double phistep = 13.333 * deg; + /// number of repetition of unique cells around the barrel + int phinmax = 27; // 27; + // // //-------------------------------------------------------------// // // + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // // // MIRROR PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + auto mirrorElem = detElem.child(_Unicode(mirror)); + auto mirrorSurf = surfMgr.opticalSurface(mirrorElem.attr(_Unicode(surface))); + auto mirrorMat = desc.material(mirrorElem.attr(_Unicode(material))); + double mirrorThickness = mirrorElem.attr(_Unicode(thickness)); + // if this z shrink is not applied, the upper tip of the mirrors are cut + // TODO: crosscheck with Martin distances between mirrors and sensors + double mirror_z_safe_shrink = 6*mm; + double mirror_z_origin_Martin = vessel_outer_r - vessel_wall_thickness - 37 * cm - mirrorThickness - mirror_z_safe_shrink; + // // //-------------------------------------------------------------// // // + + + + // // // // // // // // // // // // // // // // // // // // // // // // // // + // // // // // // LIGHT SENSOR PARAMETERS // // // // // // + // // // // // // // // // // // // // // // // // // // // // // // // // // + //default values + double sensor_sidex = 8 * cm; + double sensor_sidey = 8 * cm; + double sensor_thickness = 0.2 * cm; + double sensor_z_origin_Martin = vessel_inner_r + vessel_wall_thickness + 5*mm; + auto sensorMat = desc.material("SiliconOptical"); + auto sensorVis = desc.visAttributes("arc_no_vis"); + // auto sensorSurf = surfMgr.opticalSurface(sensorElem.attr(_Unicode(surface))); + + // Read from xml the parameters for the sensor module + { + auto sensorElem = detElem.child(_Unicode(sensors)); + sensor_sidex = sensorElem.attr(_Unicode(sensor_side_Phi)); + sensor_sidey = sensorElem.attr(_Unicode(sensor_side_Z)); + sensor_thickness = sensorElem.attr(_Unicode(thickness)); + sensorMat = desc.material(sensorElem.attr(_Unicode(material))); + sensorVis = desc.visAttributes(sensorElem.attr(_Unicode(vis))); + } + // // //-------------------------------------------------------------// // // + + + + // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // + // // //+++++++++++ BUILD VESSEL, CELL AND SENSOR VOLUMES ++++++++++// // // + // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // + + // Build cylinder for gas, and the vessel for the gas + Tube gasenvelopeS( vessel_inner_r + vessel_wall_thickness, + vessel_outer_r - vessel_wall_thickness, + vessel_length/2.); + Volume barrel_cells_gas_envelope (detName+"_gasEnvelope", gasenvelopeS, gasvolMat ); + barrel_cells_gas_envelope.setVisAttributes( desc.visAttributes("arc_envelope_vis") ); + + + Tube vesselEnvelopeSolid( vessel_inner_r, + vessel_outer_r, + vessel_length/2. + vessel_wall_thickness); + Volume barrel_cells_vessel_envelope (detName+"_vesselSkin", vesselEnvelopeSolid, vesselSkinMat ); + barrel_cells_vessel_envelope.setVisAttributes( vesselSkinVis ); + + // if 0==bulk_skin_ratio do not create bulk at all + if(0 zplanes = { vessel_inner_r + vessel_wall_thickness, + (vessel_outer_r - vessel_wall_thickness)*cos(angle_hex) + }; + std::vector rs = { hex_apothem_length -1*mm, hex_apothem_length-1*mm }; + /// Hexagonal truncated pyramid + Polyhedra cell_shape("cellShape", 6, 30 * deg, 360 * deg, zplanes, rs); + /// rotation of 90deg around Y axis, to align Z axis of pyramid with X axis of cylinder + Transform3D pyramidTr(RotationZYX(0, -90. * deg, 0. * deg), Translation3D(0, 0, 0)); + + // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // + + + // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // + + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // Build the mirror for ncell=1..18 + // negative values correspond the cells that placed for z<0 + std::vector ncell_vector = {-2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, /*, -18,*/ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 , 17 /*, 18 */ + }; + + // dummy counter to identify the cell number + // TODO: improve ID bitfield + int cellCounter(0); + + // WARNING for developping purposes +// ncell_vector = {16,17}; +// phinmax = 1; + + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // // // loop to build each cell, repeated 27 times around phi // // // + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + for (auto ncell_original : ncell_vector) + { + // Activate flag to reflect parameters later + int ncell = ncell_original; + bool reflect_parameters = false; + if (0 > ncell) + { + ncell *= -1; + reflect_parameters = true; + } + for (int phin = 0; phin < phinmax; ++phin) + { + + // WARNING for developping purposes + // The following line skips even number cells + // if (!(ncell % 2)) + // continue; + + // The following line skips odd number cells + // if ((ncell % 2)) + // continue; + // there is no cell number 0, and cell number 1 do not need to be reflected + if (0 == ncell || -1 == ncell) + continue; + + + auto create_part_name_ff = [ncell,detName,phin,reflect_parameters](std::string partName) { + std::string fullName = detName + "_" + partName; + fullName += std::to_string(ncell); + fullName += "_ref" + std::to_string(reflect_parameters); + fullName += "_phi" + std::to_string(phin); + dd4hep::printout(dd4hep::DEBUG,"ARCBARREL_T", "+++ New name:%s",fullName.c_str()); + return fullName; + }; + + + + + + + std::string cellName = create_part_name_ff( "cell"); + + /// Volume that contains gas and other stuff + Volume cellVol(cellName, cell_shape, gasvolMat); + cellVol.setVisAttributes( gasvolVis ); + /// Detector element that will contain cellVol later + /// there are 3 elements with ID: + /// the cell, ID= 3 * cellCounter + /// its mirror. ID = 3 * cellCounter +1 + /// and its sensor, ID = 3 * cellCounter +2 + DetElement cellDE(det, cellName+"DE", 3 * cellCounter); + + + + // initialize parameters for creating the mirror + double center_of_sphere_x(-999.); + double center_of_sphere_z(-999.); + double radius_of_sphere(-999.); + + double center_of_sensor_x(-999.); + double center_of_sensor_z_offset(0); + double angle_of_sensor(-999.); + + // convert Roger nomenclature (one cell number) to Martin nomenclature (row and col numbers) + int name_col = ncell / 2; + int name_row = ncell % 2 ? 1 : 2; + // retrieve cell parameters + // if parameter not present, exception is thrown and not catched + { + std::string name_col_s = std::to_string(name_col); + std::string name_row_s = std::to_string(name_row); + std::string MartinCellName = "Radiator_c" + name_col_s + "_r" + name_row_s; + + radius_of_sphere = desc.constantAsDouble(MartinCellName + "_Curvature"); + + center_of_sphere_x = desc.constantAsDouble(MartinCellName + "_XPosition"); + + double zposition = desc.constantAsDouble(MartinCellName + "_ZPosition"); + + center_of_sensor_x = desc.constantAsDouble(MartinCellName + "_DetPosition"); + + if( desc.constants().count(MartinCellName + "_DetPositionZ") ) + center_of_sensor_z_offset = desc.constantAsDouble(MartinCellName + "_DetPositionZ"); + + angle_of_sensor = desc.constantAsDouble(MartinCellName + "_DetTilt"); + + center_of_sphere_z = mirror_z_origin_Martin + zposition; + + // check if parameters are ok + if (radius_of_sphere <= mirrorThickness) + throw std::runtime_error("Ilegal parameters: radius_of_sphere <= mirrorThickness"); + } + + // reflect parameters for cells with z<0, + // ie, ncell < 0 + if (reflect_parameters) + { + center_of_sphere_x *= -1.0; + center_of_sensor_x *= -1.0; + angle_of_sensor *= -1.0; + } + + // create the semi-sphere that will result in the mirror + Sphere mirrorShapeFull(radius_of_sphere - mirrorThickness, + radius_of_sphere, + 0., + 3.14 / 2); + /// 3D transformation of mirrorVolFull in order to place it inside the gas volume + Transform3D mirrorTr(RotationZYX(0, 0, 0), Translation3D(center_of_sphere_x, 0, center_of_sphere_z - mirror_z_safe_shrink)); + + // TODO: cell 18 corresponds to half a pyramid, currently is full pyramid + Solid mirrorSol = IntersectionSolid(cell_shape, mirrorShapeFull, mirrorTr); + + std::string mirrorName = create_part_name_ff("mirror"); // detName + "_mirror" + std::to_string(ncell) + "z" + std::to_string(reflect_parameters) + + Volume mirrorVol( mirrorName, mirrorSol, mirrorMat ); + mirrorVol.setVisAttributes(desc.visAttributes(Form("arc_mirror_vis%d", ncell))); + PlacedVolume mirrorPV = cellVol.placeVolume(mirrorVol); + + DetElement mirrorDE(cellDE, mirrorName + "DE", 3 * cellCounter+1 ); + mirrorDE.setPlacement(mirrorPV); + SkinSurface mirrorSkin(desc, mirrorDE, Form("mirror_optical_surface%d", cellCounter), mirrorSurf, mirrorVol); // FIXME: 3rd arg needs `imod`? + mirrorSkin.isValid(); + + // Place detector in cell + // Build sensor shape + Box sensorSol(sensor_sidex / 2, sensor_sidey / 2, sensor_thickness / 2); + std::string sensorName = create_part_name_ff("sensor"); + Volume sensorVol( sensorName, sensorSol, sensorMat ); + sensorVol.setVisAttributes( sensorVis ); + sensorVol.setSensitiveDetector(sens); + + double center_of_sensor_z = center_of_sensor_z_offset + sensor_z_origin_Martin; + + Transform3D sensorTr(RotationZYX(0, 90 * deg - angle_of_sensor, 0), Translation3D(-center_of_sensor_z, 0, center_of_sensor_x)); + PlacedVolume sensorPV = cellVol.placeVolume(sensorVol, RotationZYX(0, 90. * deg, 0. * deg)*sensorTr); + sensorPV.addPhysVolID("cellnumber", 3 * cellCounter+2); + DetElement sensorDE(cellDE, sensorName + "DE", 3 * cellCounter+2 ); + sensorDE.setType("tracker"); + sensorDE.setPlacement(sensorPV); + + // this is an empirical parameter in order to pass the overlaps + double safe_distance_from_sensor = 262*um; + + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ COOLING PLATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + { + double cooling_z_offset = sensor_thickness + cooling_radial_thickness/2 + safe_distance_from_sensor; + Tube coolingSol_tube(0, 1.5*hexagon_side_length, cooling_radial_thickness); + Transform3D coolingTr(RotationZYX(0, 90 * deg - angle_of_sensor, 0), Translation3D(-center_of_sensor_z+cooling_z_offset, 0, center_of_sensor_x)); + auto coolingTrCell = RotationZYX(0, 90. * deg, 0. * deg)*coolingTr; + Solid coolingSol = IntersectionSolid(cell_shape, coolingSol_tube, coolingTrCell); + std::string coolingName = create_part_name_ff("cooling"); + /// TODO: change material + Volume coolingVol( coolingName, coolingSol, mirrorMat ); + coolingVol.setVisAttributes( desc.visAttributes("arc_cooling_vis") ); + cellVol.placeVolume(coolingVol ); + } + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ AEROGEL PLATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + double aerogel_z_offset = sensor_thickness - aerogel_radial_thickness/2 - safe_distance_from_sensor; + Tube aerogelSol_tube(0, 1.5*hexagon_side_length, cooling_radial_thickness); + Transform3D aerogelTr(RotationZYX(0, 90 * deg - angle_of_sensor, 0), Translation3D(-center_of_sensor_z + aerogel_z_offset, 0, center_of_sensor_x)); + auto aerogelTrCell = RotationZYX(0, 90. * deg, 0. * deg)*aerogelTr; + Solid aerogelSol = IntersectionSolid(cell_shape, aerogelSol_tube, aerogelTrCell); + std::string aerogelName = create_part_name_ff("aerogel"); + /// TODO: change material + Volume aerogelVol( aerogelName, aerogelSol, aerogelMat ); + aerogelVol.setVisAttributes( desc.visAttributes("arc_aerogel_vis") ); + cellVol.placeVolume(aerogelVol ); + // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ // + + + // position of mirror in cylinder coordinate system + double mirror_abs_pos_z = name_col * zstep - 0.5 * zstep * (2 == name_row); + if (reflect_parameters) + mirror_abs_pos_z *= -1.0; + + // row 2 is shifted half step size + double phi_offset = 0 + 0.5 * phistep * (2 == name_row); + + + auto cellTr = RotationZ(phistep * phin + phi_offset) * Translation3D(0, 0, mirror_abs_pos_z)*pyramidTr ; + PlacedVolume cellPV = barrel_cells_gas_envelope.placeVolume(cellVol, cellTr); + cellDE.setPlacement(cellPV); + + + // increase counter + cellCounter++; + + } + } + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + // // // ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> ~> // // // + barrel_cells_vessel_envelope.placeVolume(barrel_cells_gas_envelope); + PlacedVolume assemblyPV = motherVol.placeVolume(barrel_cells_vessel_envelope); + assemblyPV.addPhysVolID("system", detID); + det.setPlacement(assemblyPV); + return det; +} + + +DECLARE_DETELEMENT(ARCBARREL_o1_v01_T, create_arc_barrel_cell) diff --git a/detector/PID/README.md b/detector/PID/README.md new file mode 100644 index 000000000..d3e6e1662 --- /dev/null +++ b/detector/PID/README.md @@ -0,0 +1,33 @@ +ARC detector o1 v01 +======================= + +The Array of RICH Cells (ARC) is a novel RICH detector concept. The ARC detector is made up of three `Detector Elements`, a barrel and two endcaps. Both C++ constructors are declared in the same file `ARC_geo_o1_v01.cpp`. + +The barrel is constructed through the repetition of 17 unique cells, 27 times around phi, and mirrored over the XY plane. Each endcap consist of six repetition of a sector. One sector is made out of 21 unique cells, some of them are reflected within the sector. Further details and figures can be found in [Martin Tat's talk](https://indico.cern.ch/event/1231098/contributions/5179993/attachments/2568014/4427756/ARC_Presentation_DD4HEPIntro_15th_December_2022.pdf). The original design included an additional half cell at the edge of the barrel (cell number 18), and some more cells in the endcap. These cells are not included in the current design because they require some extra work to be properly implemented without causing overlaps. + + +The volume of each cell + +* is filled by a radiator gas (e.g. C4F10), as specified in the compact file; + +* has a spherical mirror in the outer radial part of the cell, clipped to fit inside the cell volume; + +* has a sandwich of three layers in the inner radial part, from inner to outer, a cooling plate, a light sensor, and an aerogel radiator; + +* has vessel walls are 1 cm thick. + +The current detector has a radial depth of 20 cm, an outer radius of 2.1 m and a length of 4.4 m. These dimensions are hard-coded now. In a later version, global geometry parameters and some other internal parameters will be set up freely in the compact file. + +The uniqueness of the cells happens because the position of the sensor and mirror is optimized for each cell, and defined by 5 parameters. They correspond to +* the position of the mirror (2 parameters), +* the position and angle of the sensor (3 parameters) +* an optional parameter (`xxx_DetPositionZ`) can be given to increase the distance of the sensor from the vessel, so that the volumes do not overlap. + +The parameters are defined as `DD4hep` constants in the `RadiatorCell_FinalOptimization.xml` file. The value of these parameters is linked to the geometry description. If the geometry of the ARC detector (namely its radius and thickness) is changed, these parameters should be reoptimized by a dedicated ray-tracing software. + +The material description is taken from the [Proximity Focusing RICH (pfRICH) detector example in DD4hep](https://github.com/AIDASoft/DD4hep/tree/master/examples/OpticalTracker). The optical surface for the mirror, as well as the materials, are defined in the compact file `materials_arc_o1_v01.xml`. In a later version, the optical surface for the sensor will be used to take into account the possible light detection efficiency. In addition, some optical surfaces for the vessel are needed to properly recreate a possible light background (not included at the moment). The Aerogel material without optical properties is used as template for the bulk part of the vessel walls. + +Some presentation about the ARC design can be found here: +* Dec-22 ARC meeting indico: https://indico.cern.ch/event/1231098/ +* Mar-28 Discussion about ARC implementation in DD4hep: https://indico.cern.ch/event/1266428/ +* Analysis of the data can be adapted from [previous RICH detectors](https://s3.cern.ch/inspire-prod-files-9/92927eb16166b155de56b61339f05521) diff --git a/example/arcfullsim.py b/example/arcfullsim.py new file mode 100755 index 000000000..513200041 --- /dev/null +++ b/example/arcfullsim.py @@ -0,0 +1,134 @@ +""" +Test of ARC detector +The lines 27-95 setup the simulation using DD4hep +The lines 99-127 run the simulation and produce some plots +An exception is thrown in case the simulation failed or the output file is too small. + Last modification 2023-Jun-23 + Author: Alvaro Tolosa-Delgado +""" +from __future__ import absolute_import, unicode_literals +import logging +import sys +import os + +from DDSim.DD4hepSimulation import DD4hepSimulation + +import ROOT + +if __name__ == "__main__": + logging.basicConfig( + format="%(name)-16s %(levelname)s %(message)s", + level=logging.INFO, + stream=sys.stdout, + ) + logger = logging.getLogger("DDSim") + + SIM = DD4hepSimulation() + + # Default is enable visualization of tracks + SIM.runType = "qt" + SIM.macroFile ='vis.mac' + + # Ensure that Cerenkov and optical physics are always loaded + def setupCerenkov(kernel): + from DDG4 import PhysicsList + + seq = kernel.physicsList() + cerenkov = PhysicsList(kernel, "Geant4CerenkovPhysics/CerenkovPhys") + cerenkov.MaxNumPhotonsPerStep = 10 + cerenkov.MaxBetaChangePerStep = 10.0 + cerenkov.TrackSecondariesFirst = False + cerenkov.VerboseLevel = 0 + cerenkov.enableUI() + seq.adopt(cerenkov) + ph = PhysicsList(kernel, "Geant4OpticalPhotonPhysics/OpticalGammaPhys") + ph.addParticleConstructor("G4OpticalPhoton") + ph.VerboseLevel = 0 + ph.BoundaryInvokeSD = True + ph.enableUI() + seq.adopt(ph) + return None + + SIM.physics.setupUserPhysics(setupCerenkov) + + # Allow energy depositions to 0 energy in trackers (which include optical detectors) + SIM.filter.tracker = "edep0" + + # Some detectors are only sensitive to optical photons + SIM.filter.filters["opticalphotons"] = dict( + name="ParticleSelectFilter/OpticalPhotonSelector", + parameter={"particle": "opticalphoton"}, + ) + SIM.filter.mapDetFilter["ARCBARREL"] = "opticalphotons" + SIM.filter.mapDetFilter["ARCENDCAP"] = "opticalphotons" + + # Use the optical tracker for the PFRICH + SIM.action.mapActions["ARCBARREL"] = "Geant4OpticalTrackerAction" + SIM.action.mapActions["ARCENDCAP"] = "Geant4OpticalTrackerAction" + + # Disable user tracker particle handler, so hits can be associated to photons + SIM.part.userParticleHandler = "" + + # Particle gun settings: pions with fixed energy and theta, varying phi + SIM.numberOfEvents = 1000 + SIM.enableGun = True + SIM.gun.energy = "50*GeV" + SIM.gun.particle = "pi+" + #SIM.gun.thetaMin = "30*deg" + #SIM.gun.thetaMax = "150*deg" + #SIM.gun.phiMin = "0*deg" + #SIM.gun.phiMax = "240.1*deg" + SIM.gun.distribution = "uniform" + SIM.gun.multiplicity = 1 + SIM.gun.position = "0 0 0*cm" + + + # Default compact file + SIM.compactFile = "./compact/arc_full_v0.xml" + + # Output file (assuming CWD) + SIM.outputFile = "arcsim.root" + #SIM.outputFile = "arcsim_edm4hep.root" + + # Override with user options + SIM.parseOptions() + + # Run the simulation + try: + SIM.run() + if os.path.getsize( SIM.outputFile ) < 1000000 : + raise RuntimeError("Output file not found or size less than 1MB") + # Create some images + outImagePrefix = "arcsim_" + ROOT.gROOT.SetBatch(1) + rootfile = ROOT.TFile(SIM.outputFile) + if not "edm4hep" in SIM.outputFile : + EVENT = rootfile.Get("EVENT") + EVENT.Draw("ARC_HITS.position.Z():ARC_HITS.position.phi()","((ARC_HITS.cellID>>5)&0x7)==0") + ROOT.gPad.SaveAs( outImagePrefix + "barrel_" + SIM.gun.particle + ".png") + EVENT.Draw("ARC_HITS.position.Y():ARC_HITS.position.X()","((ARC_HITS.cellID>>5)&0x7)==1") + ROOT.gPad.SaveAs( outImagePrefix + "endcapZpos_" + SIM.gun.particle + ".png") + EVENT.Draw("ARC_HITS.position.Y():ARC_HITS.position.X()","((ARC_HITS.cellID>>5)&0x7)==2") + ROOT.gPad.SaveAs( outImagePrefix + "endcapZneg_" + SIM.gun.particle + ".png") + EVENT.Draw("ARC_HITS.position.Y():ARC_HITS.position.X()","((ARC_HITS.cellID>>5)&0x7)==2&&ARC_HITS.position.Y()>0&&ARC_HITS.position.X()>0") + ROOT.gPad.SaveAs( outImagePrefix + "endcapZneg_" + SIM.gun.particle + "zoom.png") + else : + EVENT = rootfile.Get("events") + EVENT.Draw("ARC_HITS.position.z:atan(ARC_HITS.position.y/ARC_HITS.position.x)","((ARC_HITS.cellID>>5)&0x7)==0&& ARC_HITS.position.x>0") + ROOT.gPad.SaveAs( outImagePrefix + "barrel_" + SIM.gun.particle + ".png") + EVENT.Draw("ARC_HITS.position.y:ARC_HITS.position.x","((ARC_HITS.cellID>>5)&0x7)==1") + ROOT.gPad.SaveAs( outImagePrefix + "endcapZpos_" + SIM.gun.particle + ".png") + EVENT.Draw("ARC_HITS.position.y:ARC_HITS.position.x","((ARC_HITS.cellID>>5)&0x7)==2") + ROOT.gPad.SaveAs( outImagePrefix + "endcapZneg_" + SIM.gun.particle + ".png") + EVENT.Draw("ARC_HITS.position.y:ARC_HITS.position.x","((ARC_HITS.cellID>>5)&0x7)==2&& ARC_HITS.position.x>0&& ARC_HITS.position.y>0") + ROOT.gPad.SaveAs( outImagePrefix + "endcapZneg_" + SIM.gun.particle + "zoom.png") + + rootfile.Close() + + logger.info("TEST: passed") + + except NameError as e: + logger.fatal("TEST: failed") + if "global name" in str(e): + globalToSet = str(e).split("'")[1] + logger.fatal("Unknown global variable, please add\nglobal %s\nto your steeringFile" % globalToSet) diff --git a/lcgeoTests/CMakeLists.txt b/lcgeoTests/CMakeLists.txt index dbfe05349..9d908bd83 100644 --- a/lcgeoTests/CMakeLists.txt +++ b/lcgeoTests/CMakeLists.txt @@ -74,6 +74,21 @@ ADD_TEST( t_${test_name} "${CMAKE_INSTALL_PREFIX}/bin/run_test_${PackageName}.sh ddsim --steeringFile=${CMAKE_CURRENT_SOURCE_DIR}/../example/steeringFile.py --compactFile=${CMAKE_CURRENT_SOURCE_DIR}/../CLIC/compact/CLIC_o2_v04/CLIC_o2_v04.xml --runType=batch -G -N=1 --outputFile=testCLIC_o2_v04.slcio ) SET_TESTS_PROPERTIES( t_${test_name} PROPERTIES FAIL_REGULAR_EXPRESSION "Exception;EXCEPTION;ERROR;Error" ) + +#-------------------------------------------------- +# test for ARC o1 v01 +SET( test_name "test_ARC_o1_v01_run" ) +ADD_TEST( t_${test_name} "${CMAKE_INSTALL_PREFIX}/bin/run_test_${PackageName}.sh" + python3 ${CMAKE_CURRENT_SOURCE_DIR}/../example/arcfullsim.py --compactFile=${CMAKE_CURRENT_SOURCE_DIR}/../FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml --runType batch ) +SET_TESTS_PROPERTIES( t_${test_name} PROPERTIES FAIL_REGULAR_EXPRESSION "Exception;EXCEPTION;ERROR;Error" ) +set_tests_properties( t_${test_name} PROPERTIES TIMEOUT 120) + +SET( test_name "test_ARC_o1_v01_overlap" ) +ADD_TEST( t_${test_name} "${CMAKE_INSTALL_PREFIX}/bin/run_test_${PackageName}.sh" + ddsim --compactFile=${CMAKE_CURRENT_SOURCE_DIR}/../FCCee/CLD/compact/CLD_o3_v01/CLD_o3_v01.xml --runType run --part.userParticleHandler= --macroFile=${CMAKE_CURRENT_SOURCE_DIR}/../utils/overlap.mac ) +SET_TESTS_PROPERTIES( t_${test_name} PROPERTIES FAIL_REGULAR_EXPRESSION "Exception;EXCEPTION;ERROR;Error" ) +set_tests_properties( t_${test_name} PROPERTIES TIMEOUT 240) + #-------------------------------------------------- ADD_EXECUTABLE( TestSensThickness src/TestSensThickness.cpp ) diff --git a/scripts/run_geo_visualization_qt.sh b/scripts/run_geo_visualization_qt.sh new file mode 100755 index 000000000..6cc04a4ad --- /dev/null +++ b/scripts/run_geo_visualization_qt.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# +# Display detector using the Qt interface of Geant4, via ddsim +# Arguments: +# 1: compact file, with absolute path +ddsim --compactFile $1 --runType qt --macroFile ../example/vis.mac --part.userParticleHandler='' diff --git a/utils/overlap.mac b/utils/overlap.mac new file mode 100644 index 000000000..16fd2c663 --- /dev/null +++ b/utils/overlap.mac @@ -0,0 +1,4 @@ +/geometry/test/run +exit + +