File indexing completed on 2025-08-06 08:19:02
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "PHG4SectorConstructor.h"
0010 #include "PHG4SectorDisplayAction.h"
0011
0012 #include <g4main/PHG4Detector.h>
0013 #include <g4main/PHG4DisplayAction.h> // for PHG4DisplayAction
0014 #include <g4main/PHG4Subsystem.h> // for PHG4Subsystem
0015
0016 #include <Geant4/G4Box.hh>
0017 #include <Geant4/G4DisplacedSolid.hh> // for G4DisplacedSolid
0018 #include <Geant4/G4Exception.hh> // for G4Exception
0019 #include <Geant4/G4ExceptionSeverity.hh> // for FatalException, JustWarning
0020 #include <Geant4/G4IntersectionSolid.hh>
0021 #include <Geant4/G4LogicalVolume.hh>
0022 #include <Geant4/G4PVPlacement.hh>
0023 #include <Geant4/G4PhysicalConstants.hh> // for pi
0024 #include <Geant4/G4Sphere.hh>
0025 #include <Geant4/G4String.hh>
0026 #include <Geant4/G4SystemOfUnits.hh> // for cm, um, perCent
0027 #include <Geant4/G4ThreeVector.hh> // for G4ThreeVector
0028 #include <Geant4/G4Transform3D.hh> // for G4Transform3D, G4RotateX3D
0029 #include <Geant4/G4Tubs.hh>
0030 #include <Geant4/G4Types.hh> // for G4int
0031
0032 #include <algorithm> // for max
0033 #include <cassert>
0034 #include <climits>
0035 #include <cmath>
0036 #include <iostream>
0037 #include <sstream>
0038
0039 class G4Material;
0040
0041 PHG4Sector::PHG4SectorConstructor::PHG4SectorConstructor(const std::string &name, PHG4Subsystem *subsys)
0042 : overlapcheck_sector(false)
0043 , name_base(name)
0044 , m_DisplayAction(dynamic_cast<PHG4SectorDisplayAction *>(subsys->GetDisplayAction()))
0045 , m_Verbosity(0)
0046 {
0047 }
0048
0049 void PHG4Sector::PHG4SectorConstructor::Construct_Sectors(G4LogicalVolume *WorldLog)
0050 {
0051
0052 if (geom.get_total_thickness() == 0)
0053 {
0054 G4Exception(
0055 (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
0056 __FILE__, FatalException,
0057 " detector configured with zero thickness!");
0058 }
0059
0060 if (geom.get_min_polar_angle() == geom.get_max_polar_angle())
0061 {
0062 G4Exception(
0063 (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
0064 __FILE__, FatalException, "min_polar_angle = max_polar_angle!");
0065 }
0066 if (geom.get_min_polar_angle() > geom.get_max_polar_angle())
0067 {
0068 G4Exception(
0069 (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
0070 __FILE__, JustWarning,
0071 "min and max polar angle got reversed. Correcting them.");
0072
0073 const double t = geom.get_max_polar_angle();
0074 geom.set_max_polar_angle(geom.get_min_polar_angle());
0075 geom.set_min_polar_angle(t);
0076 }
0077 if ((geom.get_min_polar_edge() == Sector_Geometry::kFlatEdge or geom.get_max_polar_edge() == Sector_Geometry::kFlatEdge) and geom.get_N_Sector() <= 2)
0078 {
0079 G4Exception(
0080 (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
0081 __FILE__, FatalException,
0082 "can NOT use flat edge for single or double sector detector!");
0083 }
0084
0085 const G4Transform3D transform_Det_to_Hall =
0086 G4RotateX3D(-geom.get_normal_polar_angle()) * G4TranslateZ3D(
0087 geom.get_normal_start() + geom.get_total_thickness() / 2);
0088
0089 const G4Transform3D transform_Hall_to_Det(transform_Det_to_Hall.inverse());
0090
0091
0092
0093 static const double epsilon = std::numeric_limits<float>::epsilon();
0094 const double sph_min_polar_angle =
0095 (geom.get_min_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_min_polar_angle() : (0 + epsilon);
0096 const double sph_max_polar_angle =
0097 (geom.get_max_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_max_polar_angle() : (pi - epsilon);
0098
0099 G4VSolid *SecConeBoundary_Hall = new G4Sphere("SecConeBoundary_Hall",
0100 geom.get_normal_start(), geom.get_max_R(),
0101 pi / 2 - pi / geom.get_N_Sector(), 2 * pi / geom.get_N_Sector(),
0102 sph_min_polar_angle, sph_max_polar_angle - sph_min_polar_angle
0103 );
0104
0105 G4VSolid *SecConeBoundary_Det = new G4DisplacedSolid("SecConeBoundary_Det",
0106 SecConeBoundary_Hall, transform_Hall_to_Det);
0107
0108 G4VSolid *Boundary_Det = SecConeBoundary_Det;
0109
0110 if (geom.get_min_polar_edge() != Sector_Geometry::kConeEdge or geom.get_max_polar_edge() != Sector_Geometry::kConeEdge)
0111 {
0112
0113
0114 const double sph_min_polar_size =
0115 (geom.get_min_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_max_R() : geom.get_normal_start() * tan(geom.get_normal_polar_angle() - geom.get_min_polar_angle());
0116 const double sph_max_polar_size =
0117 (geom.get_max_polar_edge() == Sector_Geometry::kConeEdge) ? geom.get_max_R() : geom.get_normal_start() * tan(geom.get_max_polar_angle() - geom.get_normal_polar_angle());
0118
0119 G4VSolid *BoxBoundary_Det = new G4Box("BoxBoundary_Det",
0120 geom.get_max_R(), (sph_min_polar_size + sph_max_polar_size) / 2,
0121 geom.get_total_thickness());
0122 G4VSolid *BoxBoundary_Det_Place = new G4DisplacedSolid(
0123 "BoxBoundary_Det_Place", BoxBoundary_Det, nullptr,
0124 G4ThreeVector(0, (sph_max_polar_size - sph_min_polar_size) / 2, 0));
0125
0126 Boundary_Det = new G4IntersectionSolid("Boundary_Det",
0127 BoxBoundary_Det_Place, SecConeBoundary_Det);
0128 }
0129
0130 G4VSolid *DetectorBox_Det = Construct_Sectors_Plane("DetectorBox_Det",
0131 -geom.get_total_thickness() / 2, geom.get_total_thickness(),
0132 Boundary_Det);
0133
0134 G4Material *p_mat = PHG4Detector::GetDetectorMaterial(geom.get_material());
0135
0136 G4LogicalVolume *DetectorLog_Det = new G4LogicalVolume(DetectorBox_Det,
0137 p_mat, name_base + "_Log");
0138 RegisterLogicalVolume(DetectorLog_Det);
0139
0140 for (G4int sec = 0; sec < geom.get_N_Sector(); sec++)
0141 {
0142 RegisterPhysicalVolume(
0143 new G4PVPlacement(
0144 G4RotateZ3D(2 * pi / geom.get_N_Sector() * sec) * transform_Det_to_Hall, DetectorLog_Det,
0145 name_base + "_Physical", WorldLog, false, sec, overlapcheck_sector));
0146 }
0147
0148
0149 double z_start = -geom.get_total_thickness() / 2;
0150
0151 for (const auto &l : geom.layer_list)
0152 {
0153 if (l.percentage_filled > 100. || l.percentage_filled < 0)
0154 {
0155 std::ostringstream strstr;
0156 strstr << name_base << " have invalid layer ";
0157 strstr << l.name << " with percentage_filled =" << l.percentage_filled;
0158
0159 G4Exception(
0160 (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(), __FILE__, FatalException,
0161 strstr.str().c_str());
0162 }
0163
0164 const std::string layer_name = name_base + "_" + l.name;
0165
0166 G4VSolid *LayerSol_Det = Construct_Sectors_Plane(layer_name + "_Sol",
0167 z_start, l.depth * l.percentage_filled * perCent, Boundary_Det);
0168
0169 G4LogicalVolume *LayerLog_Det = new G4LogicalVolume(LayerSol_Det,
0170 PHG4Detector::GetDetectorMaterial(l.material), layer_name + "_Log");
0171 RegisterLogicalVolume(LayerLog_Det);
0172
0173 RegisterPhysicalVolume(
0174 new G4PVPlacement(nullptr, G4ThreeVector(), LayerLog_Det,
0175 layer_name + "_Physical", DetectorLog_Det, false, 0, overlapcheck_sector),
0176 l.active);
0177
0178 z_start += l.depth;
0179 }
0180
0181 if (std::abs(z_start - geom.get_total_thickness() / 2) > 1 * um)
0182 {
0183 std::ostringstream strstr;
0184 strstr << name_base << " - accumulated thickness = "
0185 << (z_start + geom.get_total_thickness() / 2) / um
0186 << " um expected thickness = " << geom.get_total_thickness() / um
0187 << " um";
0188 G4Exception(
0189 (std::string("PHG4SectorConstructor::Construct_Sectors::") + (name_base)).c_str(),
0190 __FILE__, FatalException, strstr.str().c_str());
0191 }
0192
0193 m_DisplayAction->AddVolume(DetectorLog_Det, "DetectorBox");
0194 if (Verbosity() > 1)
0195 {
0196 std::cout << "PHG4SectorConstructor::Construct_Sectors::" << name_base
0197 << " - total thickness = " << geom.get_total_thickness() / cm << " cm"
0198 << std::endl;
0199 std::cout << "PHG4SectorConstructor::Construct_Sectors::" << name_base << " - "
0200 << map_log_vol.size() << " logical volume constructed" << std::endl;
0201 std::cout << "PHG4SectorConstructor::Construct_Sectors::" << name_base << " - "
0202 << map_phy_vol.size() << " physical volume constructed; "
0203 << map_active_phy_vol.size() << " is active." << std::endl;
0204 }
0205 }
0206
0207 G4VSolid *
0208 PHG4Sector::PHG4SectorConstructor::Construct_Sectors_Plane(
0209 const std::string &name,
0210 const double start_z,
0211 const double thickness,
0212 G4VSolid *SecConeBoundary_Det
0213 )
0214 {
0215 assert(SecConeBoundary_Det);
0216
0217 G4VSolid *Sol_Raw = new G4Tubs(name + "_Raw",
0218 0,
0219 geom.get_max_R(),
0220 thickness / 2,
0221 0,
0222 2 * pi
0223 );
0224
0225 G4VSolid *Sol_Place = new G4DisplacedSolid(name + "_Place", Sol_Raw, nullptr,
0226 G4ThreeVector(0, 0, start_z + thickness / 2));
0227
0228 G4VSolid *Sol = new G4IntersectionSolid(name, Sol_Place,
0229 SecConeBoundary_Det);
0230
0231 return Sol;
0232
0233 }
0234
0235 void PHG4Sector::Sector_Geometry::SetDefault()
0236 {
0237 N_Sector = 8;
0238 material = "G4_AIR";
0239
0240
0241 normal_polar_angle = 0;
0242
0243
0244 min_polar_angle = .1;
0245
0246
0247 max_polar_angle = .3;
0248
0249
0250 normal_start = 305 * cm;
0251
0252 min_polar_edge = kConeEdge;
0253
0254 max_polar_edge = kConeEdge;
0255 }
0256
0257 G4LogicalVolume *
0258 PHG4Sector::PHG4SectorConstructor::RegisterLogicalVolume(G4LogicalVolume *v)
0259 {
0260 if (!v)
0261 {
0262 std::cout
0263 << "PHG4SectorConstructor::RegisterVolume - Error - invalid volume!"
0264 << std::endl;
0265 return v;
0266 }
0267 if (map_log_vol.find(v->GetName()) != map_log_vol.end())
0268 {
0269 std::cout << "PHG4SectorConstructor::RegisterVolume - Warning - replacing "
0270 << v->GetName() << std::endl;
0271 }
0272
0273 map_log_vol[v->GetName()] = v;
0274
0275 return v;
0276 }
0277
0278 G4PVPlacement *
0279 PHG4Sector::PHG4SectorConstructor::RegisterPhysicalVolume(G4PVPlacement *v,
0280 const bool active)
0281 {
0282 if (!v)
0283 {
0284 std::cout
0285 << "PHG4SectorConstructor::RegisterPhysicalVolume - Error - invalid volume!"
0286 << std::endl;
0287 return v;
0288 }
0289
0290 phy_vol_idx_t id(v->GetName(), v->GetCopyNo());
0291
0292 if (map_phy_vol.find(id) != map_phy_vol.end())
0293 {
0294 std::cout
0295 << "PHG4SectorConstructor::RegisterPhysicalVolume - Warning - replacing "
0296 << v->GetName() << "[" << v->GetCopyNo() << "]" << std::endl;
0297 }
0298
0299 map_phy_vol[id] = v;
0300
0301 if (active)
0302 {
0303 map_active_phy_vol[id] = v;
0304 }
0305
0306 return v;
0307 }
0308
0309 double
0310 PHG4Sector::Sector_Geometry::get_total_thickness() const
0311 {
0312 double sum = 0;
0313 for (const auto &it : layer_list)
0314 {
0315 sum += it.depth;
0316 }
0317 return sum;
0318 }
0319
0320 double
0321 PHG4Sector::Sector_Geometry::get_max_R() const
0322 {
0323
0324 assert(std::abs(min_polar_angle - normal_polar_angle) < pi / 2);
0325 assert(std::abs(max_polar_angle - normal_polar_angle) < pi / 2);
0326
0327 if (N_Sector <= 2)
0328 {
0329
0330 if (cos(min_polar_angle + normal_polar_angle) <= 0)
0331 {
0332 std::stringstream strstr;
0333 strstr << "Geometry check failed. " << std::endl;
0334 strstr << "normal_polar_angle = " << normal_polar_angle << std::endl;
0335 strstr << "min_polar_angle = " << min_polar_angle << std::endl;
0336 strstr << "max_polar_angle = " << max_polar_angle << std::endl;
0337 strstr << "cos(min_polar_angle + normal_polar_angle) = "
0338 << cos(min_polar_angle + normal_polar_angle) << std::endl;
0339
0340 G4Exception("Sector_Geometry::get_max_R", __FILE__, FatalException,
0341 strstr.str().c_str());
0342 }
0343 if (cos(max_polar_angle + normal_polar_angle) <= 0)
0344 {
0345 std::stringstream strstr;
0346 strstr << "Geometry check failed. " << std::endl;
0347 strstr << "normal_polar_angle = " << normal_polar_angle << std::endl;
0348 strstr << "min_polar_angle = " << min_polar_angle << std::endl;
0349 strstr << "max_polar_angle = " << max_polar_angle << std::endl;
0350 strstr << "cos(max_polar_angle + normal_polar_angle) = "
0351 << cos(max_polar_angle + normal_polar_angle) << std::endl;
0352
0353 G4Exception("Sector_Geometry::get_max_R", __FILE__, FatalException,
0354 strstr.str().c_str());
0355 }
0356
0357 const double max_tan_angle = std::max(
0358 std::abs(tan(min_polar_angle + normal_polar_angle)),
0359 std::abs(tan(max_polar_angle + normal_polar_angle)));
0360
0361 return (get_normal_start() + get_total_thickness()) * sqrt(1 + max_tan_angle * max_tan_angle);
0362 }
0363 else
0364 {
0365 const double max_angle = std::max(
0366 std::abs(min_polar_angle - normal_polar_angle),
0367 std::abs(max_polar_angle - normal_polar_angle));
0368
0369 return (get_normal_start() + get_total_thickness()) * sqrt(1 + pow(tan(max_angle), 2) + pow(tan(2 * pi / N_Sector), 2)) * 2;
0370 }
0371 }
0372
0373
0374
0375
0376 void PHG4Sector::Sector_Geometry::AddLayers_DriftVol_COMPASS(const double drift_vol_thickness)
0377 {
0378
0379
0380
0381 AddLayer("EntranceWindow", "G4_MYLAR", 25 * um, false, 100);
0382 AddLayer("Cathode", "G4_GRAPHITE", 10 * um, false, 100);
0383 AddLayer("DrftVol", material, drift_vol_thickness, true, 100);
0384 }
0385
0386
0387
0388
0389
0390 void PHG4Sector::Sector_Geometry::AddLayers_HBD_GEM(const int n_GEM_layers)
0391 {
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403 for (int gem = 1; gem <= n_GEM_layers; gem++)
0404 {
0405 std::stringstream sid;
0406 sid << gem;
0407
0408
0409 AddLayer(G4String("GEMFrontCu") + G4String(sid.str()), "G4_Cu",
0410 0.0005 * cm, false, 64);
0411
0412
0413 AddLayer(G4String("GEMKapton") + G4String(sid.str()), "G4_KAPTON",
0414 0.005 * cm, false, 64);
0415
0416
0417 AddLayer(G4String("GEMBackCu") + G4String(sid.str()), "G4_Cu",
0418 0.0005 * cm, false, 64);
0419
0420
0421 AddLayer(G4String("Frame") + G4String(sid.str()), "G10", 0.15 * cm, false,
0422 6.5);
0423 }
0424
0425
0426 AddLayer(G4String("PCBKapton"), "G4_KAPTON", 0.005 * cm, false, 100);
0427
0428
0429 AddLayer(G4String("PCBCu"), "G4_Cu", 0.0005 * cm, false, 80);
0430
0431
0432 AddLayer("Facesheet", "G10", 0.025 * 2 * cm, false, 100);
0433
0434
0435
0436
0437
0438 }
0439
0440
0441
0442
0443
0444 void PHG4Sector::Sector_Geometry::AddLayers_HBD_Readout()
0445 {
0446
0447
0448 AddLayer(G4String("ReadoutFR4"), "G10", 0.05 * cm, false, 100);
0449 AddLayer(G4String("ReadoutCu"), "G4_Cu", 0.001 * cm, false, 100);
0450
0451
0452
0453 AddLayer(G4String("SocketsCu"), "G4_Cu", 0.0005 * cm, false, 100);
0454 }
0455
0456
0457
0458
0459
0460 void PHG4Sector::Sector_Geometry::AddLayers_AeroGel_ePHENIX(const double radiator_length,
0461 const double expansion_length, std::string radiator)
0462 {
0463 if (radiator == "Default")
0464 {
0465 radiator = "ePHENIX_AeroGel";
0466 }
0467
0468
0469 AddLayer("EntranceWindow", "G10", 0.05 * cm, false, 100);
0470 AddLayer("AeroGel", radiator, radiator_length, false, 100);
0471 AddLayer("ExpansionVol", "G4_AIR", expansion_length, false, 100);
0472
0473
0474 AddLayer(G4String("ReadoutFR4"), "G10", 0.05 * cm, false, 100);
0475 AddLayer(G4String("ReadoutCu"), "G4_Cu", 0.001 * cm, false, 100);
0476 AddLayer(G4String("SocketsCu"), "G4_Cu", 0.0005 * cm, false, 100);
0477 }