Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:22:12

0001 #include "PHG4TpcEndCapDetector.h"
0002 
0003 #include "PHG4TpcEndCapDisplayAction.h"
0004 
0005 #include <phparameter/PHParameters.h>
0006 
0007 #include <g4main/PHG4Detector.h>
0008 #include <g4main/PHG4DisplayAction.h>  // for PHG4DisplayAction
0009 #include <g4main/PHG4Subsystem.h>
0010 
0011 #include <TSystem.h>
0012 
0013 #include <Geant4/G4AssemblyVolume.hh>
0014 #include <Geant4/G4Box.hh>
0015 #include <Geant4/G4ExtrudedSolid.hh>
0016 #include <Geant4/G4LogicalVolume.hh>
0017 #include <Geant4/G4Material.hh>
0018 #include <Geant4/G4RotationMatrix.hh>
0019 #include <Geant4/G4String.hh>
0020 #include <Geant4/G4SystemOfUnits.hh>
0021 #include <Geant4/G4ThreeVector.hh>
0022 #include <Geant4/G4Transform3D.hh>
0023 #include <Geant4/G4Tubs.hh>
0024 #include <Geant4/G4TwoVector.hh>
0025 #include <Geant4/G4Types.hh>  // for G4double
0026 #include <Geant4/G4VPhysicalVolume.hh>
0027 
0028 #include <CLHEP/Vector/RotationZ.h>
0029 
0030 #include <algorithm>  // for max, copy
0031 #include <cassert>
0032 #include <cmath>
0033 #include <cstdlib>  // for exit
0034 #include <format>
0035 #include <iostream>
0036 
0037 class G4VSolid;
0038 class PHCompositeNode;
0039 
0040 //____________________________________________________________________________..
0041 PHG4TpcEndCapDetector::PHG4TpcEndCapDetector(PHG4Subsystem *subsys,
0042                                              PHCompositeNode *Node,
0043                                              PHParameters *parameters,
0044                                              const std::string &dnam)
0045   : PHG4Detector(subsys, Node, dnam)
0046   , m_Params(parameters)
0047   , m_DisplayAction(dynamic_cast<PHG4TpcEndCapDisplayAction *>(subsys->GetDisplayAction()))
0048 
0049 {
0050   assert(subsys->GetDisplayAction());
0051   assert(m_DisplayAction);
0052   Verbosity(m_Params->get_int_param("construction_verbosity"));
0053 }
0054 
0055 PHG4TpcEndCapDetector::~PHG4TpcEndCapDetector()
0056 {
0057   if (m_EndCapAssembly)
0058   {
0059     if (Verbosity())
0060     {
0061       std::cout << __PRETTY_FUNCTION__ << " delete m_EndCapAssembly" << std::endl;
0062     }
0063 
0064     delete m_EndCapAssembly;
0065   }
0066 }
0067 
0068 //_______________________________________________________________
0069 int PHG4TpcEndCapDetector::IsInDetector(G4VPhysicalVolume *volume) const
0070 {
0071   G4LogicalVolume *logvol = volume->GetLogicalVolume();
0072   std::set<G4LogicalVolume *>::const_iterator iter = m_LogicalVolumesSet.find(logvol);
0073   if (iter != m_LogicalVolumesSet.end())
0074   {
0075     return 1;
0076   }
0077   return 0;
0078 }
0079 
0080 //_______________________________________________________________
0081 void PHG4TpcEndCapDetector::ConstructMe(G4LogicalVolume *logicWorld)
0082 {
0083   assert(m_DisplayAction);
0084 
0085   assert(m_EndCapAssembly == nullptr);
0086   m_EndCapAssembly = ConstructEndCapAssembly();
0087   assert(m_EndCapAssembly);
0088 
0089   G4TranslateZ3D g4vec_front_z(m_Params->get_double_param("envelop_front_surface_z") * cm);
0090 
0091   G4RotateY3D rotm_otherside(180 * deg);
0092 
0093   G4ThreeVector g4vec_center(m_Params->get_double_param("place_x") * cm,
0094                              m_Params->get_double_param("place_y") * cm,
0095                              m_Params->get_double_param("place_z") * cm);
0096   G4RotationMatrix rotm_center;
0097   rotm_center.rotateX(m_Params->get_double_param("rot_x") * deg);
0098   rotm_center.rotateY(m_Params->get_double_param("rot_y") * deg);
0099   rotm_center.rotateZ(m_Params->get_double_param("rot_z") * deg);
0100   G4Transform3D transform_center(rotm_center, g4vec_center);
0101 
0102   int i = 0;
0103   //  G4Transform3D transform_side1 = g4vec_front_z * transform_center;
0104   G4Transform3D transform_side1 = transform_center * g4vec_front_z;
0105   m_EndCapAssembly->MakeImprint(logicWorld, transform_side1, i++, OverlapCheck());
0106   // the other side
0107   G4Transform3D transform_side2 = transform_center * rotm_otherside * g4vec_front_z;
0108   m_EndCapAssembly->MakeImprint(logicWorld, transform_side2, i++, OverlapCheck());
0109 
0110   return;
0111 }
0112 
0113 G4AssemblyVolume *PHG4TpcEndCapDetector::ConstructEndCapAssembly()
0114 {
0115   G4AssemblyVolume *assemblyvol = new G4AssemblyVolume();
0116   G4double starting_z(0);
0117 
0118   // Internal HBD structure
0119   // From doi:10.1016/j.nima.2011.04.015
0120   // Component Material X0 (cm) Thickness (cm) Area (%) Rad. Length (%)
0121   //  Mesh SS 1.67 0.003 11.5 0.021  <- not used for GEMs trackers
0122   //  AddLayer("Mesh", "Steel",
0123   //          0.003 * cm, false, 11.5);
0124 
0125   //  //  GEM frames FR4 17.1 0.15x4 6.5 0.228 <- not used for GEMs trackers
0126   //  AddLayer("Frame0", "G10",
0127   //          0.15 * cm, false, 6.5);
0128 
0129   std::vector<double> thickness;
0130   std::vector<std::string> material;
0131   material.emplace_back("G4_Cu");
0132   thickness.push_back(0.0005 * 2. * cm);
0133   material.emplace_back("G4_KAPTON");
0134   thickness.push_back(0.005 * cm);
0135   material.emplace_back("sPHENIX_TPC_Gas");  // proper gas name, but should be pulled from params to match TpcSubsystem?
0136   thickness.push_back(0.2 * cm);
0137   G4Material *temp = GetDetectorMaterial("GEMeffective", false);
0138   if (temp == nullptr)
0139   {
0140     CreateCompositeMaterial("GEMeffective", material, thickness);  // see new function below
0141   }
0142   double totalThickness = 0;
0143   for (double thicknes : thickness)
0144   {
0145     totalThickness += thicknes;
0146   }
0147 
0148   const int n_GEM_layers = m_Params->get_int_param("n_GEM_layers");
0149 
0150   // instead of building this layer-by-layer, we build a single block corresponding to all the gems that were previously handled in this fashion:
0151   totalThickness *= n_GEM_layers;
0152   AddLayer(assemblyvol, starting_z, G4String("GEMAllParts"), "GEMeffective", totalThickness, 64);  // note this slightly undercounts the gas because the gas fill should be 100%, and slightly mispositions the inner edge of the material because the way it is made <100% in AddLayer is by making it thinner than nominally requested but centering it in the region it would have occupied.
0153 
0154   // 16 layer readout plane by TTM
0155   // https://indico.bnl.gov/event/8307/contributions/36744/attachments/27646/42337/R3-Review.pptx
0156   const int n_PCB_layers(16);
0157   // 35 um / layer Cu
0158   AddLayer(assemblyvol, starting_z, G4String("PCBCu"), "G4_Cu", 0.0035 * cm * n_PCB_layers, 80);
0159   // 7 mil / layer board
0160   AddLayer(assemblyvol, starting_z, "PCBBase", "FR4", 0.00254 * cm * 7 * n_PCB_layers, 100);
0161 
0162   ConstructWagonWheel(assemblyvol, starting_z);
0163   ConstructElectronics(assemblyvol, starting_z);
0164 
0165   return assemblyvol;
0166 }
0167 
0168 void PHG4TpcEndCapDetector::CreateCompositeMaterial(
0169     const std::string &compositeName,
0170     std::vector<std::string> materialName,
0171     const std::vector<double> &thickness)
0172 {
0173   // takes in a list of material names known to Geant already, and thicknesses, and creates a new material called compositeName.
0174 
0175   // check that desired material name doesn't already exist
0176   G4Material *tempmat = GetDetectorMaterial(compositeName, false);
0177 
0178   if (tempmat != nullptr)
0179   {
0180     std::cout << __PRETTY_FUNCTION__ << " Fatal Error: composite material " << compositeName << " already exists" << std::endl;
0181     assert(!tempmat);
0182   }
0183 
0184   // check that both arrays have the same depth
0185   assert(materialName.size() == thickness.size());
0186 
0187   // sum up the areal density and total thickness so we can divvy it out
0188   double totalArealDensity = 0;
0189   double totalThickness = 0;
0190   for (std::vector<double>::size_type i = 0; i < thickness.size(); i++)
0191   {
0192     tempmat = GetDetectorMaterial(materialName[i]);
0193     if (tempmat == nullptr)
0194     {
0195       std::cout << __PRETTY_FUNCTION__ << " Fatal Error: component material " << materialName[i] << " does not exist." << std::endl;
0196       gSystem->Exit(1);
0197       exit(1);
0198     }
0199     totalArealDensity += tempmat->GetDensity() * thickness[i];
0200     totalThickness += thickness[i];
0201   }
0202 
0203   // register a new material with the average density of the whole:
0204   double compositeDensity = totalArealDensity / totalThickness;
0205   G4Material *composite = new G4Material(compositeName, compositeDensity, thickness.size());
0206 
0207   // now calculate the fraction due to each material, and register those
0208   for (std::vector<double>::size_type i = 0; i < thickness.size(); i++)
0209   {
0210     tempmat = GetDetectorMaterial(materialName[i]);  // don't need to check this, since we did in the previous loop.
0211     composite->AddMaterial(tempmat, thickness[i] * tempmat->GetDensity() / totalArealDensity);
0212   }
0213 
0214   // how to register our finished material?
0215   return;
0216 }
0217 
0218 void PHG4TpcEndCapDetector ::AddLayer(  //
0219     G4AssemblyVolume *assemblyvol,
0220     G4double &z_start,
0221     const std::string &_name,      //! name base for this layer
0222     const std::string &_material,  //! material name in G4
0223     G4double _depth,               //! depth in G4 units
0224     double _percentage_filled      //! percentage filled//
0225 )
0226 {
0227   z_start += _depth / 2.;
0228   G4ThreeVector g4vec(0, 0, z_start);
0229   z_start += _depth / 2.;
0230 
0231   std::string name_base = std::format("{}_Layer_{}", GetName(), _name);
0232 
0233   G4VSolid *solid_layer = new G4Tubs(
0234       name_base,
0235       m_Params->get_double_param("envelop_r_min") * cm,
0236       m_Params->get_double_param("envelop_r_max") * cm,
0237       _depth * _percentage_filled / 100. / 2.,
0238       0, CLHEP::twopi);
0239 
0240   auto *material = GetDetectorMaterial(_material);
0241   if (material == nullptr)
0242   {
0243     std::cout << __PRETTY_FUNCTION__ << " Fatal Error: missing material " << _material << std::endl;
0244     assert(material);
0245   }
0246 
0247   G4LogicalVolume *logical_layer = new G4LogicalVolume(solid_layer, material, name_base);
0248   m_LogicalVolumesSet.insert(logical_layer);
0249 
0250   assemblyvol->AddPlacedVolume(logical_layer, g4vec, nullptr);
0251 
0252   assert(m_DisplayAction);
0253   m_DisplayAction->AddVolume(logical_layer, _material);
0254 
0255   return;
0256 }
0257 
0258 void PHG4TpcEndCapDetector::ConstructWagonWheel(G4AssemblyVolume *assmeblyvol,
0259                                                 G4double &z_start)  // careful z_start is modified and being used later
0260 {
0261   const int n_sectors = m_Params->get_int_param("n_sectors");
0262   assert(n_sectors >= 1);
0263   const int n_radial_modules = m_Params->get_int_param("n_radial_modules");
0264   assert(n_radial_modules >= 1);
0265 
0266   const std::string material_name(m_Params->get_string_param("wagon_wheel_material"));
0267   auto *material = GetDetectorMaterial(material_name);
0268   if (material == nullptr)
0269   {
0270     std::cout << __PRETTY_FUNCTION__ << " Fatal Error: missing material " << m_Params->get_string_param("wagon_wheel_material") << std::endl;
0271     assert(material);
0272   }
0273   const G4double wagon_wheel_sector_phi_offset = m_Params->get_double_param("wagon_wheel_sector_phi_offset_degree") * degree;
0274 
0275   ///////////////////////////////////////////////
0276   // wagon_wheel_front_frame ring
0277   ///////////////////////////////////////////////
0278   if (Verbosity())
0279   {
0280     std::cout << __PRETTY_FUNCTION__ << " - wagon_wheel_front_frame z_start = " << z_start << std::endl;
0281   }
0282 
0283   const G4double wagon_wheel_front_frame_thickness = m_Params->get_double_param("wagon_wheel_front_frame_thickness") * cm;
0284   const G4double wagon_wheel_front_frame_spoke_width = m_Params->get_double_param("wagon_wheel_front_frame_spoke_width") * cm;
0285 
0286   z_start += wagon_wheel_front_frame_thickness / 2.;
0287   G4ThreeVector g4vec_wagon_wheel_front_frame(0, 0, z_start);
0288   z_start += wagon_wheel_front_frame_thickness / 2.;
0289 
0290   const G4double wagon_wheel_front_frame_R_inner = m_Params->get_double_param("wagon_wheel_front_frame_R_inner") * cm;
0291   const G4double wagon_wheel_front_frame_R_outer = m_Params->get_double_param("wagon_wheel_front_frame_R_outer") * cm;
0292 
0293   for (int ring_id = 0; ring_id <= n_radial_modules; ++ring_id)
0294   {
0295     G4double Rin = wagon_wheel_front_frame_R_inner;
0296     G4double Rout = wagon_wheel_front_frame_R_outer;
0297 
0298     if (ring_id > 0)
0299     {
0300       Rin = m_Params->get_double_param(std::format("wagon_wheel_front_frame_R_R{}_outer", ring_id)) *
0301             cm;
0302     }
0303     if (ring_id < n_radial_modules)
0304     {
0305       Rout = m_Params->get_double_param(std::format("wagon_wheel_front_frame_R_R{}_inner", ring_id + 1)) *
0306              cm;
0307     }
0308 
0309     std::string name_base = std::format("{}_{}_Ring{}", GetName(), "wagon_wheel_front_frame", ring_id);
0310 
0311 
0312     G4VSolid *solid_wagon_wheel_front_frame = new G4Tubs(
0313         name_base,
0314         Rin,
0315         Rout,
0316         wagon_wheel_front_frame_thickness / 2.,
0317         0, CLHEP::twopi);
0318 
0319     G4LogicalVolume *log_solid_wagon_wheel_front_frame = new G4LogicalVolume(solid_wagon_wheel_front_frame, material, name_base);
0320     m_LogicalVolumesSet.insert(log_solid_wagon_wheel_front_frame);
0321 
0322     assmeblyvol->AddPlacedVolume(log_solid_wagon_wheel_front_frame,
0323                                  g4vec_wagon_wheel_front_frame,
0324                                  nullptr);
0325     assert(m_DisplayAction);
0326     m_DisplayAction->AddVolume(log_solid_wagon_wheel_front_frame, "wagon_wheel");
0327 
0328   }  // for (int ring_id = 0; ring_id <= n_radial_modules; ++ring_id)
0329 
0330   ///////////////////////////////////////////////
0331   // wagon_wheel_front_frame spoke
0332   ///////////////////////////////////////////////
0333   for (int ring_id = 1; ring_id <= n_radial_modules; ++ring_id)
0334   {
0335     G4double Rout =
0336       m_Params->get_double_param(std::format("wagon_wheel_front_frame_R_R{}_outer", ring_id)) * cm;
0337     G4double Rin =
0338       m_Params->get_double_param(std::format("wagon_wheel_front_frame_R_R{}_inner", ring_id)) * cm;
0339 
0340     const G4double reduced_height = std::sqrt(Rout * Rout - wagon_wheel_front_frame_spoke_width / 2 * wagon_wheel_front_frame_spoke_width / 2);
0341 
0342     std::vector<G4TwoVector> vertexes;
0343     vertexes.emplace_back(-wagon_wheel_front_frame_spoke_width / 2, Rin);
0344     vertexes.emplace_back(+wagon_wheel_front_frame_spoke_width / 2, Rin);
0345     vertexes.emplace_back(+wagon_wheel_front_frame_spoke_width / 2, reduced_height);
0346     vertexes.emplace_back(-wagon_wheel_front_frame_spoke_width / 2, reduced_height);
0347 
0348     G4TwoVector zero(0, 0);
0349 
0350     std::string name_base_spoke = std::format("{}_{}_Ring{}_spoke", GetName(), "wagon_wheel_front_frame", ring_id);
0351 
0352 
0353     G4VSolid *solid_wagon_wheel_front_frame_spoke = new G4ExtrudedSolid(name_base_spoke,
0354                                                                         vertexes,
0355                                                                         wagon_wheel_front_frame_thickness / 2.,
0356                                                                         zero, 1.0,
0357                                                                         zero, 1.0);
0358     G4LogicalVolume *log_solid_wagon_wheel_front_frame_spoke = new G4LogicalVolume(solid_wagon_wheel_front_frame_spoke, material, name_base_spoke);
0359     m_LogicalVolumesSet.insert(log_solid_wagon_wheel_front_frame_spoke);
0360 
0361     const G4double sector_dphi = CLHEP::twopi / n_sectors;
0362     for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0363     {
0364       G4Transform3D trans_spoke(CLHEP::HepRotationZ(wagon_wheel_sector_phi_offset + sector_dphi * sector_id), g4vec_wagon_wheel_front_frame);
0365 
0366       assmeblyvol->AddPlacedVolume(log_solid_wagon_wheel_front_frame_spoke,
0367                                    trans_spoke);
0368       assert(m_DisplayAction);
0369       m_DisplayAction->AddVolume(log_solid_wagon_wheel_front_frame_spoke, "wagon_wheel");
0370 
0371     }  //     for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0372 
0373   }  //  for (int ring_id = 0; ring_id < n_radial_modules; ++ring_id)
0374 
0375   ///////////////////////////////////////////////
0376   // wagon_wheel_rim_outer
0377   ///////////////////////////////////////////////
0378   if (Verbosity())
0379   {
0380     std::cout << __PRETTY_FUNCTION__ << " - wagon_wheel_rim_outer z_start = " << z_start << std::endl;
0381   }
0382 
0383   {
0384     const G4double wagon_wheel_rim_outer_Rin = m_Params->get_double_param("wagon_wheel_rim_outer_Rin") * cm;
0385     const G4double wagon_wheel_rim_outer_Rout = m_Params->get_double_param("wagon_wheel_rim_outer_Rout") * cm;
0386     const G4double wagon_wheel_rim_outer_thickness = m_Params->get_double_param("wagon_wheel_rim_outer_thickness") * cm;
0387 
0388     G4ThreeVector g4vec_wagon_wheel_rim_outer(0, 0, z_start + wagon_wheel_rim_outer_thickness / 2.);
0389 
0390     std::string name_base = std::format("{}_wagon_wheel_rim_outer", GetName());
0391 
0392 
0393     G4VSolid *solid_wagon_wheel = new G4Tubs(
0394         name_base,
0395         wagon_wheel_rim_outer_Rin,
0396         wagon_wheel_rim_outer_Rout,
0397         wagon_wheel_rim_outer_thickness / 2.,
0398         0, CLHEP::twopi);
0399 
0400     G4LogicalVolume *log_solid_wagon_wheel = new G4LogicalVolume(solid_wagon_wheel, material, name_base);
0401     m_LogicalVolumesSet.insert(log_solid_wagon_wheel);
0402 
0403     assmeblyvol->AddPlacedVolume(log_solid_wagon_wheel,
0404                                  g4vec_wagon_wheel_rim_outer,
0405                                  nullptr);
0406     assert(m_DisplayAction);
0407     m_DisplayAction->AddVolume(log_solid_wagon_wheel, "wagon_wheel");
0408 
0409   }  // wagon_wheel_rim_outer
0410 
0411   ///////////////////////////////////////////////
0412   // wagon_wheel_spoke
0413   ///////////////////////////////////////////////
0414   {
0415     const G4double wagon_wheel_spoke_width = m_Params->get_double_param("wagon_wheel_spoke_width") * cm;
0416     const G4double wagon_wheel_spoke_height_inner = m_Params->get_double_param("wagon_wheel_spoke_height_inner") * cm;
0417     const G4double wagon_wheel_spoke_height_outer = m_Params->get_double_param("wagon_wheel_spoke_height_outer") * cm;
0418     const G4double wagon_wheel_spoke_R_inner = m_Params->get_double_param("wagon_wheel_spoke_R_inner") * cm;
0419     const G4double wagon_wheel_spoke_R_outer = m_Params->get_double_param("wagon_wheel_spoke_R_outer") * cm;
0420 
0421     std::string name_base = std::format("{}_wagon_wheel_spoke", GetName());
0422 
0423     std::vector<G4TwoVector> vertexes;
0424     vertexes.emplace_back(0, wagon_wheel_spoke_R_inner);
0425     vertexes.emplace_back(0, wagon_wheel_spoke_R_outer);
0426     vertexes.emplace_back(wagon_wheel_spoke_height_outer, wagon_wheel_spoke_R_outer);
0427     vertexes.emplace_back(wagon_wheel_spoke_height_inner, wagon_wheel_spoke_R_inner);
0428     G4TwoVector zero(0, 0);
0429 
0430     G4VSolid *solid_wagon_wheel_spoke = new G4ExtrudedSolid(name_base,
0431                                                             vertexes,
0432                                                             wagon_wheel_spoke_width / 2.,
0433                                                             zero, 1.0,
0434                                                             zero, 1.0);
0435     G4LogicalVolume *log_solid_wagon_wheel_spoke = new G4LogicalVolume(solid_wagon_wheel_spoke, material, name_base);
0436     m_LogicalVolumesSet.insert(log_solid_wagon_wheel_spoke);
0437 
0438     G4ThreeVector g4vec_wagon_wheel_spoke(0, 0, z_start + wagon_wheel_spoke_width / 2.);
0439 
0440     const G4double sector_dphi = CLHEP::twopi / n_sectors;
0441     for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0442     {
0443       G4RotateY3D rotm_spoke(-90 * deg);
0444       G4Transform3D trans_spoke(CLHEP::HepRotationZ(wagon_wheel_sector_phi_offset + sector_dphi * sector_id),
0445                                 g4vec_wagon_wheel_spoke);
0446       G4Transform3D trans_spoke_final = trans_spoke * rotm_spoke;
0447 
0448       assmeblyvol->AddPlacedVolume(log_solid_wagon_wheel_spoke,
0449                                    trans_spoke_final);
0450       assert(m_DisplayAction);
0451       m_DisplayAction->AddVolume(log_solid_wagon_wheel_spoke, "wagon_wheel");
0452 
0453     }  //     for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0454 
0455   }  // wagon_wheel_rim_outer
0456 }
0457 
0458 void PHG4TpcEndCapDetector::ConstructElectronics(G4AssemblyVolume *assmeblyvol,
0459                                                  G4double z_start)
0460 {
0461   const int n_sectors = m_Params->get_int_param("n_sectors");
0462   assert(n_sectors >= 1);
0463 
0464   const G4double sector_dphi = CLHEP::twopi / n_sectors;
0465 
0466   const int n_radial_modules = m_Params->get_int_param("n_radial_modules");
0467   assert(n_radial_modules >= 1);
0468   const G4double wagon_wheel_sector_phi_offset = m_Params->get_double_param("wagon_wheel_sector_phi_offset_degree") * degree;
0469   const G4double wagon_wheel_spoke_width = m_Params->get_double_param("wagon_wheel_spoke_width") * cm;
0470 
0471   ///////////////////////////////////////////////
0472   // electronics_cooling_block_material ring
0473   ///////////////////////////////////////////////
0474   const G4double electronics_cooling_block_thickness = m_Params->get_double_param("electronics_cooling_block_thickness") * cm;
0475   if (electronics_cooling_block_thickness > 0)
0476   {
0477     if (Verbosity())
0478     {
0479       std::cout << __PRETTY_FUNCTION__ << " - electronics_cooling_block_material z_start = " << z_start << std::endl;
0480     }
0481 
0482     const std::string electronics_cooling_block_material_name(m_Params->get_string_param("electronics_cooling_block_material"));
0483     auto *material = GetDetectorMaterial(electronics_cooling_block_material_name);
0484     if (material == nullptr)
0485     {
0486       std::cout << __PRETTY_FUNCTION__ << " Fatal Error: missing material " << m_Params->get_string_param("electronics_cooling_block_material_name") << std::endl;
0487       gSystem->Exit(1);
0488       exit(1);
0489     }
0490 
0491     G4ThreeVector g4vec_electronics_cooling_block(0, 0, z_start + electronics_cooling_block_thickness / 2.);
0492 
0493     const G4double electronics_cooling_block_R_inner = m_Params->get_double_param("electronics_cooling_block_R_inner") * cm;
0494     const G4double electronics_cooling_block_R_outer = m_Params->get_double_param("electronics_cooling_block_R_outer") * cm;
0495 
0496     for (int ring_id = 0; ring_id <= n_radial_modules; ++ring_id)
0497     {
0498       G4double Rin = electronics_cooling_block_R_inner;
0499       G4double Rout = electronics_cooling_block_R_outer;
0500 
0501       if (ring_id > 0)
0502       {
0503         Rin = m_Params->get_double_param(std::format("electronics_cooling_block_R_R{}_outer", ring_id)) * cm;
0504       }
0505       if (ring_id < n_radial_modules)
0506       {
0507         Rout = m_Params->get_double_param(std::format("electronics_cooling_block_R_R{}_inner", ring_id + 1)) * cm;
0508       }
0509 
0510       std::string name_base = std::format("{}_{}_Ring{}", GetName(), "electronics_cooling_block", ring_id);
0511 
0512       const G4double spoke_phi = atan2(wagon_wheel_spoke_width, Rin);
0513 
0514       //      const G4double sector_dphi = CLHEP::twopi / n_sectors;
0515 
0516       G4VSolid *solid = new G4Tubs(
0517           name_base,
0518           Rin,
0519           Rout,
0520           electronics_cooling_block_thickness / 2.,
0521           spoke_phi, sector_dphi - 2 * spoke_phi);
0522 
0523       if (Verbosity())
0524       {
0525         std::cout << __PRETTY_FUNCTION__ << " - electronics_cooling_block " << name_base
0526                   << " Rin = " << Rin << " Rout = " << Rout
0527                   << " phi = " << spoke_phi << " to " << (sector_dphi - spoke_phi) << std::endl;
0528       }
0529 
0530       //
0531       G4LogicalVolume *log_vol = new G4LogicalVolume(solid, material, name_base);
0532       m_LogicalVolumesSet.insert(log_vol);
0533 
0534       for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0535       {
0536         G4Transform3D trans(
0537             CLHEP::HepRotationZ(wagon_wheel_sector_phi_offset + sector_dphi * sector_id),
0538             g4vec_electronics_cooling_block);
0539         //
0540         assmeblyvol->AddPlacedVolume(log_vol, trans);
0541         assert(m_DisplayAction);
0542         m_DisplayAction->AddVolume(log_vol, "cooling_block");
0543 
0544       }  //     for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0545     }  // for (int ring_id = 0; ring_id <= n_radial_modules; ++ring_id)
0546   }  // electronics_cooling_block_material  if (electronics_cooling_block_thickness>0)
0547 
0548   ///////////////////////////////////////////////
0549   // electronics
0550   ///////////////////////////////////////////////
0551   const G4double electronics_FEE_depth = m_Params->get_double_param("electronics_FEE_depth") * cm;
0552   const G4double electronics_FEE_Cu_thickness = m_Params->get_double_param("electronics_FEE_Cu_thickness") * cm;
0553   const G4double electronics_FEE_PCB_thickness = m_Params->get_double_param("electronics_FEE_PCB_thickness") * cm;
0554   const G4double electronics_FEE_Al_thickness = m_Params->get_double_param("electronics_FEE_Al_thickness") * cm;
0555   const G4double electronics_assemly_thickness = electronics_FEE_Cu_thickness + electronics_FEE_PCB_thickness + electronics_FEE_Al_thickness;
0556 
0557   if (m_Params->get_int_param("electronics_enable") != 0)
0558   {
0559     for (int ring_id = 1; ring_id <= n_radial_modules; ++ring_id)
0560     {
0561       const G4double Rout = m_Params->get_double_param(std::format("electronics_cooling_block_R_R{}_outer", ring_id)) * cm - electronics_assemly_thickness;
0562       const G4double Rin = m_Params->get_double_param(std::format("electronics_cooling_block_R_R{}_inner", ring_id)) * cm + electronics_assemly_thickness;
0563       const int nFEE = m_Params->get_int_param(std::format("electronics_nFEE_R{}", ring_id));
0564 
0565 
0566       if (nFEE <= 0)
0567       {
0568         std::cout << __PRETTY_FUNCTION__ << " warning : ignore FEE construction for module " << ring_id << " as "
0569                   << std::format("electronics_nFEE_R2{}", ring_id) << " = " << nFEE << std::endl;
0570 
0571         continue;
0572       }
0573 
0574       G4AssemblyVolume *assmeblyvol_electronics = new G4AssemblyVolume();
0575       G4double starting_electronics(0);
0576       std::string name_base = std::format("{}_{}_Ring{}", GetName(), "electronics", ring_id);
0577 
0578       if (Verbosity())
0579       {
0580         std::cout << __PRETTY_FUNCTION__ << " - electronics G4_PCB z_start = " << z_start
0581                   << " starting_electronics = " << starting_electronics << std::endl;
0582       }
0583       starting_electronics -= electronics_FEE_PCB_thickness / 2.;
0584       G4ThreeVector g4vec_electronics;
0585       g4vec_electronics.set(starting_electronics, (Rout + Rin) * .5, z_start + electronics_FEE_depth / 2.);
0586       starting_electronics -= electronics_FEE_PCB_thickness / 2.;
0587       G4VSolid *solid_electronics = nullptr;
0588       solid_electronics = new G4Box(name_base + "_PCB",
0589                                     electronics_FEE_PCB_thickness / 2.,
0590                                     (Rout - Rin) / 2.,
0591                                     electronics_FEE_depth / 2.);
0592 
0593       G4LogicalVolume *log_electronics = nullptr;
0594       log_electronics = new G4LogicalVolume(solid_electronics, GetDetectorMaterial("FR4"), name_base + "_PCB");
0595       m_LogicalVolumesSet.insert(log_electronics);
0596 
0597       assmeblyvol_electronics->AddPlacedVolume(log_electronics,
0598                                                g4vec_electronics, nullptr);
0599       m_DisplayAction->AddVolume(log_electronics, "FR4");
0600       if (Verbosity())
0601       {
0602         std::cout << __PRETTY_FUNCTION__ << " - electronics G4_Cu z_start = " << z_start
0603                   << " starting_electronics = " << starting_electronics << std::endl;
0604       }
0605       starting_electronics -= electronics_FEE_Cu_thickness / 2.;
0606       g4vec_electronics.set(starting_electronics, (Rout + Rin) * .5, z_start + electronics_FEE_depth / 2.);
0607       starting_electronics -= electronics_FEE_Cu_thickness / 2.;
0608 
0609       solid_electronics = new G4Box(name_base + "_Cu",
0610                                     electronics_FEE_Cu_thickness / 2.,
0611                                     (Rout - Rin) / 2.,
0612                                     electronics_FEE_depth / 2.);
0613 
0614       log_electronics = new G4LogicalVolume(solid_electronics, GetDetectorMaterial("G4_Cu"), name_base + "_Cu");
0615       m_LogicalVolumesSet.insert(log_electronics);
0616 
0617       assmeblyvol_electronics->AddPlacedVolume(log_electronics,
0618                                                g4vec_electronics, nullptr);
0619       m_DisplayAction->AddVolume(log_electronics, "Cu");
0620       if (Verbosity())
0621       {
0622         std::cout << __PRETTY_FUNCTION__ << " - electronics Al z_start = " << z_start
0623                   << " starting_electronics = " << starting_electronics << std::endl;
0624       }
0625       starting_electronics -= electronics_FEE_Al_thickness / 2.;
0626       g4vec_electronics.set(starting_electronics, (Rout + Rin) * .5, z_start + electronics_FEE_depth / 2.);
0627       starting_electronics -= electronics_FEE_Al_thickness / 2.;
0628 
0629       solid_electronics = new G4Box(name_base + "_Al",
0630                                     electronics_FEE_Al_thickness / 2.,
0631                                     (Rout - Rin) / 2.,
0632                                     electronics_FEE_depth / 2.);
0633 
0634       log_electronics = new G4LogicalVolume(solid_electronics,
0635                                             GetDetectorMaterial("G4_Al"),
0636                                             name_base + "_Al");
0637       m_LogicalVolumesSet.insert(log_electronics);
0638 
0639       assmeblyvol_electronics->AddPlacedVolume(log_electronics,
0640                                                g4vec_electronics, nullptr);
0641       m_DisplayAction->AddVolume(log_electronics, "cooling_block");
0642 
0643       for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0644       {
0645         const G4double sector_phi_shift = wagon_wheel_sector_phi_offset + sector_dphi * sector_id;
0646         const G4double spoke_phi = atan2(wagon_wheel_spoke_width, Rin);
0647         const G4double board_dphi = (sector_dphi - 2 * spoke_phi) / (nFEE + 1);
0648         const G4double board_phi_start = sector_phi_shift + spoke_phi + board_dphi;
0649 
0650         for (int board_id = 0; board_id < nFEE; ++board_id)
0651         {
0652           G4Transform3D trans_electronic = G4RotateZ3D(board_phi_start + board_dphi * board_id);
0653 
0654           assmeblyvol->AddPlacedAssembly(assmeblyvol_electronics, trans_electronic);
0655         }
0656       }  //     for (int sector_id = 0; sector_id < n_sectors; ++sector_id)
0657 
0658     }  //  for (int ring_id = 0; ring_id < n_radial_modules; ++ring_id)
0659   }
0660 }
0661 
0662 //_______________________________________________________________
0663 void PHG4TpcEndCapDetector::Print(const std::string &what) const
0664 {
0665   std::cout << "PHG4TpcEndCap Detector:" << std::endl;
0666   if (what == "ALL" || what == "VOLUME")
0667   {
0668     std::cout << "Version 0.1" << std::endl;
0669     std::cout << "Parameters:" << std::endl;
0670     m_Params->Print();
0671   }
0672   return;
0673 }