From a3bb7076ae4eae361ce335fdb2845ac3e9f48ee5 Mon Sep 17 00:00:00 2001
From: Sylvester Joosten <sjoosten@anl.gov>
Date: Mon, 15 Nov 2021 04:50:58 +0000
Subject: [PATCH] Tweaked nECAL fill algorithm for better fill, and added
 asymetric extra fill around beampipe

---
 compact/definitions.xml          |  14 ++-
 compact/ecal_backward_hybrid.xml |   2 +
 src/HybridCalorimeter_geo.cpp    | 172 +++++++++++--------------------
 3 files changed, 74 insertions(+), 114 deletions(-)

diff --git a/compact/definitions.xml b/compact/definitions.xml
index 30f1dc92..7ce5757e 100644
--- a/compact/definitions.xml
+++ b/compact/definitions.xml
@@ -311,6 +311,13 @@ Examples:
     <constant name="Eta3_9_tan"       value="tan(2*atan(exp(-3.9)))" />
     <constant name="Eta4_0_tan"       value="tan(2*atan(exp(-4.0)))" />
     <constant name="Eta4_1_tan"       value="tan(2*atan(exp(-4.1)))" />
+    <constant name="Eta4_2_tan"       value="tan(2*atan(exp(-4.2)))" />
+    <constant name="Eta4_3_tan"       value="tan(2*atan(exp(-4.3)))" />
+    <constant name="Eta4_4_tan"       value="tan(2*atan(exp(-4.4)))" />
+    <constant name="Eta4_5_tan"       value="tan(2*atan(exp(-4.5)))" />
+    <constant name="Eta4_6_tan"       value="tan(2*atan(exp(-4.6)))" />
+    <constant name="Eta4_7_tan"       value="tan(2*atan(exp(-4.7)))" />
+    <constant name="Eta4_8_tan"       value="tan(2*atan(exp(-4.8)))" />
 
     <comment>Solenoid option</comment>
 
@@ -429,7 +436,12 @@ Service gaps in FW direction (before endcapP ECAL) and BW direction (before endc
 
     <constant name="EcalEndcapN_zmin"               value="BackwardPIDRegion_zmin + BackwardInnerEndcapRegion_length"/>
     <constant name="EcalEndcapN_length"             value="60*cm" />
-    <constant name="EcalEndcapN_rmin"               value="max((EcalEndcapN_zmin + EcalEndcapN_length) * tan(abs(CrossingAngle)) + 15.5 * mm, 5*cm)" />
+    <comment>
+      rmin1: rmin round electron pipe (ignoring the hadron pipe)
+      rmin2: rmin around both beam pipes
+    </comment>
+    <constant name="EcalEndcapN_rmin1"               value="Eta4_6_tan * EcalEndcapN_zmin" />
+    <constant name="EcalEndcapN_rmin2"               value="Eta4_1_tan * EcalEndcapN_zmin" />
     <constant name="EcalEndcapN_rmax"               value="CentralTrackingRegion_rmax" />
 
     <constant name="EcalBarrelRegion_thickness"     value="45.0*cm"/>
diff --git a/compact/ecal_backward_hybrid.xml b/compact/ecal_backward_hybrid.xml
index 9115722f..e7b8380d 100644
--- a/compact/ecal_backward_hybrid.xml
+++ b/compact/ecal_backward_hybrid.xml
@@ -34,6 +34,8 @@
     <constant name="GlassModule_wrap" value="2*CrystalModule_wrap"/>
     <constant name="GlassModule_z0" value="0.0*cm"/>
 
+    <constant name="EcalEndcapNIonCutout_dphi" value="30*degree"/>
+
     <constant name="EcalEndcapN_thickness" value="GlassModule_length"/>
     <constant name="EcalEndcapN_z0" value="-EcalEndcapN_zmin - EcalEndcapN_thickness/2"/>
     <constant name="EcalEndcapNCrystal_rmax" value="40*cm"/>
diff --git a/src/HybridCalorimeter_geo.cpp b/src/HybridCalorimeter_geo.cpp
index c7d61330..bcc04451 100644
--- a/src/HybridCalorimeter_geo.cpp
+++ b/src/HybridCalorimeter_geo.cpp
@@ -40,7 +40,9 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
     auto air_material = desc.material("Air");
 
     double ROut = desc.constantAsDouble("EcalEndcapN_rmax");
-    double RIn = desc.constantAsDouble("EcalEndcapN_rmin");
+    double RIn_el = desc.constantAsDouble("EcalEndcapN_rmin1");
+    double ionCutout_dphi = desc.constantAsDouble("EcalEndcapNIonCutout_dphi");
+    double RIn = desc.constantAsDouble("EcalEndcapN_rmin2");
     double SizeZ = desc.constantAsDouble("EcalEndcapN_thickness");
     double thickness = desc.constantAsDouble("EcalEndcapN_thickness");
     double trans_radius = desc.constantAsDouble("EcalEndcapNCrystal_rmax");
@@ -57,23 +59,35 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
     double Crystal_z0 = desc.constantAsDouble("CrystalModule_z0");
 
     // RIn and ROut will define outer tube embedding the calorimeter
-    // centers_rin/out define the maximum radius of module centers
+    // centers_rmin/out define the maximum radius of module centers
     // so that modules are not overlapping with mother tube volume
-    double hypotenuse = sqrt(0.5 * glass_distance * glass_distance);
-    double centers_rin = RIn + hypotenuse + 1*mm;
-    double centers_rout = ROut - hypotenuse - 1*mm;
-
-    const double Crystal_offset = -0.5*(Crystal_thickness - thickness);
+    const double glassHypotenuse = std::hypot(glass_distance, glass_distance)/2;
+    const double crystalHypotenuse = glassHypotenuse/2;
+    // Offset these values a bit so we don't miss edge-blocks
+    const double glassCenters_rmax  = ROut - glassHypotenuse + 1 * mm;
+    const double crystalCenters_rmin = RIn + crystalHypotenuse - 1 * mm;
+    // Also limits of the inner crystal blocks fill
+    const double cutout_tan = tan(ionCutout_dphi/2);
+    const double cutout_rmin = RIn_el + crystalHypotenuse - 1 * mm;
+
+    // Offset to align the modules at the zmin of the endcap,
+    const double Crystal_offset = -0.5 * (Crystal_thickness - thickness);
     const double Glass_offset = -0.5*(Glass_thickness - thickness);
 
     // envelope
-
-    // Assembly assembly(detName);
-
-    Tube outer_tube(RIn, ROut, SizeZ / 2.0, 0., 360.0 * deg);
-    Volume ecal_vol("negative_ecal", outer_tube, air_material);
+    // consists of an glass tube of the full thickness, and a crystal inner tube
+    // for the crystal that allows us to get closet to the beampipe without
+    // overlaps, and a partial electron tube that allows us to get closer to the
+    // electron beampipe in the region where there is no ion beampipe
+    Tube glass_tube(min(RIn + glassHypotenuse*2, trans_radius), ROut, SizeZ / 2.0, 0., 360.0 * deg);
+    Tube crystal_tube(RIn, min(RIn + glassHypotenuse*2, trans_radius), Crystal_thickness/ 2.0, 0., 360.0 * deg);
+    Tube electron_tube(RIn_el, RIn, Crystal_thickness / 2., ionCutout_dphi / 2., 360.0 * deg - ionCutout_dphi / 2);
+    UnionSolid outer_envelope(glass_tube, crystal_tube, Position(0,0,Crystal_offset));
+    UnionSolid envelope(outer_envelope, electron_tube, Position(0,0,Crystal_offset));
+    Volume ecal_vol("negative_ecal", envelope, air_material);
     ecal_vol.setVisAttributes(desc.visAttributes("HybridEcalOuterVis"));
 
+    // TODO why 1cm and not something else?
     double Glass_OuterR = ROut - 1 * cm ;
     double Glass_InnerR = trans_radius;
 
@@ -91,9 +105,11 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
     // GLASS
     double diameter = 2 * Glass_OuterR;
 
-    // How many towers do we have per row/columnt?
-    // Add a gap + diameter as if we have N towers, we have N-1 gaps;
-    int towersInRow = std::ceil((diameter + Glass_Gap) /  (Glass_Width + Glass_Gap));
+    // Can we fit an even or odd amount of glass blocks within our rmax?
+    // This determines the transition points between crystal and glass as we need the
+    // outer crystal arangement to be in multiples of 2 (aligned with glass)
+    const int towersInRow = std::ceil((diameter + Glass_Gap) /  (Glass_Width + Glass_Gap));
+    const bool align_even = (towersInRow % 2);
 
     // Is it odd or even number of towersInRow
     double leftTowerPos, topTowerPos;
@@ -114,109 +130,39 @@ static Ref_t create_detector(Detector& desc, xml::Handle_t handle, SensitiveDete
 
     int moduleIndex = 0;
 
-//    fmt::print("\nCE EMCAL GLASS SQUARE START\n");
-//    fmt::print("Glass_thickness = {} cm;\n", Glass_thickness / cm);
-//    fmt::print("Glass_Width     = {} cm;\n", Glass_Width / cm);
-//    fmt::print("Glass_Gap       = {} cm;\n", Glass_Gap / cm);
-//    fmt::print("Glass_InnerR    = {} cm;\n", Glass_InnerR / cm);
-//    fmt::print("Glass_OuterR    = {} cm;\n", Glass_OuterR / cm);
-//    fmt::print("Glass_PosZ      = {} cm;\n", glass_shift_z / cm);
-//    fmt::print("Towers in Row/Col   = {} cm;\n", glass_shift_z / cm);
-//    fmt::print("Top left tower pos  = {:<10} {:<10} cm;\n", -leftTowerPos / cm, topTowerPos / cm);
-// fmt::print("#Towers info:\n");
-// fmt::print("#{:<5} {:<6} {:<3} {:<3} {:>10} {:>10}   {}\n", "idx",  "code", "col", "row", "x", "y", "name");
-
-
-    // We first do a "dry run", not really placing modules,
-    // but figuring out the ID scheme, number of modules, etc.
-    int glassModuleCount = 0;
-    int crystalModuleCount = 0;
-    int firstCrystRow = 1000000;   // The first row, where crystals are started
-    int firstCrystCol = 1000000;   // The fist column, where crystals are started
-    for(int rowIndex=0; rowIndex < towersInRow; rowIndex++) {
-      for(int colIndex=0; colIndex < towersInRow; colIndex++) {
-        double glass_x = leftTowerPos + colIndex * glass_distance;
-        double glass_y = topTowerPos + rowIndex * glass_distance;
-        double r = sqrt(glass_x * glass_x + glass_y * glass_y);
-
-        if (r < centers_rout && r > centers_rin) {
-          // we are placing something
-          if(r<trans_radius) {
-            // 4 crystal modules will be placed
-            crystalModuleCount+=4;
-
-            // Finding the first col and row where crystals start
-            // is the same algorithm as finding a minimum in array
-            if(colIndex<firstCrystCol) {
-              firstCrystCol = colIndex;
-            }
-            if(rowIndex<firstCrystRow) {
-              firstCrystRow = rowIndex;
-            }
-          }
-          else
-          {
-            // glass module will be places
-            glassModuleCount++;
-          }
-        }
-      }
-    }
-    // fmt::print("#Towers info:\n");
-    // fmt::print("#{:<5} {:<6} {:<3} {:<3} {:>10} {:>10}   {}\n", "idx",  "code", "col", "row", "x", "y", "name");
     int glass_module_index = 0;
     int cryst_module_index = 0;
     for(int rowIndex=0; rowIndex < towersInRow; rowIndex++) {
       for(int colIndex=0; colIndex < towersInRow; colIndex++) {
-        double glass_x = leftTowerPos + colIndex * (Glass_Width + Glass_Gap);
-        double glass_y = topTowerPos + rowIndex * (Glass_Width + Glass_Gap);
-        double r = sqrt(glass_x * glass_x + glass_y * glass_y);
-
-        if (r < centers_rout && r > centers_rin) {
-          int code = 1000 * rowIndex + colIndex;
-          std::string name = fmt::format("ce_EMCAL_glass_phys_{}", code);
-
-          if(r<trans_radius) {
-
-            // first crystal module
-            double crystal_x = glass_x - crystal_distance / 2;
-            double crystal_y = glass_y - crystal_distance / 2;
-            auto   placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_z0 + Crystal_offset));
-            placement.addPhysVolID("sector", 1);
-            placement.addPhysVolID("module", cryst_module_index++);
-
-            // second crystal module
-            crystal_x = glass_x + crystal_distance / 2;
-            crystal_y = glass_y - crystal_distance / 2;
-            placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_z0 + Crystal_offset));
-            placement.addPhysVolID("sector", 1);
-            placement.addPhysVolID("module", cryst_module_index++);
-
-            // third crystal module
-            crystal_x = glass_x - crystal_distance / 2;
-            crystal_y = glass_y + crystal_distance / 2;
-            placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_z0 + Crystal_offset));
-            placement.addPhysVolID("sector", 1);
-            placement.addPhysVolID("module", cryst_module_index++);
-
-            // forth crystal module
-            crystal_x = glass_x + crystal_distance / 2;
-            crystal_y = glass_y + crystal_distance / 2;
-            placement = ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_z0 + Crystal_offset));
-            placement.addPhysVolID("sector", 1);
-            placement.addPhysVolID("module", cryst_module_index++);
-          }
-          else
-          {
-            // glass module
-            auto placement = ecal_vol.placeVolume(glass_module, Position(glass_x, glass_y, Glass_z0 + Glass_offset));
-            placement.addPhysVolID("sector", 2);
-            placement.addPhysVolID("module", glass_module_index++);
+        const double glass_x = leftTowerPos + colIndex * (Glass_Width + Glass_Gap);
+        const double glass_y = topTowerPos + rowIndex * (Glass_Width + Glass_Gap);
+        const double r = std::hypot(glass_x, glass_y);
+        // crystal if within the transition radius (as defined by the equivalent glass
+        // block)
+        if (r < trans_radius) {
+          for (const auto dx : {-1, 1}) {
+            for (const auto& dy : {-1, 1}) {
+              const double crystal_x = glass_x + dx * crystal_distance / 2;
+              const double crystal_y = glass_y + dy * crystal_distance / 2;
+              const double crystal_r = std::hypot(crystal_x, crystal_y);
+              // check if crystal in the main crystal ring?
+              const bool mainRing = (crystal_r > crystalCenters_rmin);
+              const bool innerRing = !mainRing && crystal_r > cutout_rmin;
+              const bool ionCutout = crystal_x > 0 && fabs(crystal_y / crystal_x) < cutout_tan;
+              if (mainRing || (innerRing && !ionCutout)) {
+                auto placement =
+                    ecal_vol.placeVolume(crystal_module, Position(crystal_x, crystal_y, Crystal_z0 + Crystal_offset));
+                placement.addPhysVolID("sector", 1);
+                placement.addPhysVolID("module", cryst_module_index++);
+              }
+            }
           }
-
-
-          // fmt::print(" {:<5} {:<6} {:<3} {:<3} {:>10.4f} {:>10.4f}   {}\n", towerIndex, code, colIndex, rowIndex, x / cm, y / cm, name);
-          //glass_module_index++;
+        // Glass block if within the rmax
+        } else if (r < glassCenters_rmax) {
+          // glass module
+          auto placement = ecal_vol.placeVolume(glass_module, Position(glass_x, glass_y, Glass_z0 + Glass_offset));
+          placement.addPhysVolID("sector", 2);
+          placement.addPhysVolID("module", glass_module_index++);
         }
       }
     }
-- 
GitLab