File indexing completed on 2025-08-05 08:18:16
0001 #include "PHG4TpcDetector.h"
0002 #include "PHG4TpcDefs.h"
0003 #include "PHG4TpcDisplayAction.h"
0004
0005 #include <g4detectors/PHG4CellDefs.h>
0006 #include <g4detectors/PHG4TpcCylinderGeom.h>
0007 #include <g4detectors/PHG4TpcCylinderGeomContainer.h>
0008
0009 #include <phparameter/PHParameters.h>
0010
0011 #include <g4main/PHG4Detector.h> // for PHG4Detector
0012 #include <g4main/PHG4DisplayAction.h> // for PHG4DisplayAction
0013 #include <g4main/PHG4Subsystem.h>
0014
0015 #include <cdbobjects/CDBTTree.h>
0016
0017 #include <ffamodules/CDBInterface.h>
0018
0019 #include <phparameter/PHParameters.h>
0020
0021 #include <phool/PHCompositeNode.h>
0022 #include <phool/PHIODataNode.h>
0023 #include <phool/PHNodeIterator.h>
0024 #include <phool/getClass.h>
0025 #include <phool/recoConsts.h>
0026 #include <phool/sphenix_constants.h>
0027
0028 #include <TSystem.h>
0029
0030 #include <Geant4/G4LogicalVolume.hh>
0031 #include <Geant4/G4Material.hh>
0032 #include <Geant4/G4NistManager.hh>
0033 #include <Geant4/G4PVPlacement.hh>
0034 #include <Geant4/G4String.hh> // for G4String
0035 #include <Geant4/G4SystemOfUnits.hh>
0036 #include <Geant4/G4ThreeVector.hh> // for G4ThreeVector
0037 #include <Geant4/G4Tubs.hh>
0038 #include <Geant4/G4UserLimits.hh>
0039 #include <Geant4/G4VPhysicalVolume.hh> // for G4VPhysicalVolume
0040
0041 #include <algorithm> // for max, copy
0042 #include <cassert>
0043 #include <cmath>
0044 #include <cstdlib> // for exit
0045 #include <iostream> // for basic_ostream::operator<<
0046 #include <map> // for map
0047 #include <sstream>
0048
0049 class G4VSolid;
0050 class PHCompositeNode;
0051
0052
0053 PHG4TpcDetector::PHG4TpcDetector(PHG4Subsystem *subsys, PHCompositeNode *Node, PHParameters *parameters, const std::string &dnam)
0054 : PHG4Detector(subsys, Node, dnam)
0055 , m_DisplayAction(dynamic_cast<PHG4TpcDisplayAction *>(subsys->GetDisplayAction()))
0056 , m_Params(parameters)
0057 , m_ActiveFlag(m_Params->get_int_param("active"))
0058 , m_AbsorberActiveFlag(m_Params->get_int_param("absorberactive"))
0059 , m_InnerCageRadius(m_Params->get_double_param("gas_inner_radius") * cm - m_Params->get_double_param("cage_layer_9_thickness") * cm - m_Params->get_double_param("cage_layer_8_thickness") * cm - m_Params->get_double_param("cage_layer_7_thickness") * cm - m_Params->get_double_param("cage_layer_6_thickness") * cm - m_Params->get_double_param("cage_layer_5_thickness") * cm - m_Params->get_double_param("cage_layer_4_thickness") * cm - m_Params->get_double_param("cage_layer_3_thickness") * cm - m_Params->get_double_param("cage_layer_2_thickness") * cm - m_Params->get_double_param("cage_layer_1_thickness") * cm)
0060 , m_OuterCageRadius(m_Params->get_double_param("gas_outer_radius") * cm + m_Params->get_double_param("cage_layer_9_thickness") * cm + m_Params->get_double_param("cage_layer_8_thickness") * cm + m_Params->get_double_param("cage_layer_7_thickness") * cm + m_Params->get_double_param("cage_layer_6_thickness") * cm + m_Params->get_double_param("cage_layer_5_thickness") * cm + m_Params->get_double_param("cage_layer_4_thickness") * cm + m_Params->get_double_param("cage_layer_3_thickness") * cm + m_Params->get_double_param("cage_layer_2_thickness") * cm + m_Params->get_double_param("cage_layer_1_thickness") * cm)
0061 {
0062 }
0063
0064 PHG4TpcDetector::~PHG4TpcDetector()
0065 {
0066 delete m_cdbttree;
0067 delete m_G4UserLimits;
0068 }
0069
0070 int PHG4TpcDetector::IsInTpc(G4VPhysicalVolume *volume) const
0071 {
0072 if (m_ActiveFlag)
0073 {
0074 if (m_ActiveVolumeSet.find(volume) != m_ActiveVolumeSet.end())
0075 {
0076 return 1;
0077 }
0078 }
0079 if (m_AbsorberActiveFlag)
0080 {
0081 if (m_AbsorberVolumeSet.find(volume) != m_AbsorberVolumeSet.end())
0082 {
0083 return -1;
0084 }
0085 }
0086 return 0;
0087 }
0088
0089
0090 void PHG4TpcDetector::ConstructMe(G4LogicalVolume *logicWorld)
0091 {
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104 double steplimits = m_Params->get_double_param("steplimits") * cm;
0105 if (std::isfinite(steplimits) && steplimits > 0)
0106 {
0107 m_G4UserLimits = new G4UserLimits(steplimits);
0108 }
0109
0110 G4VSolid *tpc_envelope = new G4Tubs("tpc_envelope", m_InnerCageRadius, m_OuterCageRadius, m_Params->get_double_param("tpc_length") * cm / 2., 0., 2 * M_PI);
0111
0112 recoConsts *rc = recoConsts::instance();
0113 G4LogicalVolume *tpc_envelope_logic = new G4LogicalVolume(tpc_envelope,
0114 GetDetectorMaterial(rc->get_StringFlag("WorldMaterial")),
0115 "tpc_envelope");
0116 m_DisplayAction->AddVolume(tpc_envelope_logic, "TpcEnvelope");
0117
0118 ConstructTpcExternalSupports(logicWorld);
0119
0120 ConstructTpcCageVolume(tpc_envelope_logic);
0121 ConstructTpcGasVolume(tpc_envelope_logic);
0122
0123 new G4PVPlacement(nullptr, G4ThreeVector(m_Params->get_double_param("place_x") * cm, m_Params->get_double_param("place_y") * cm, m_Params->get_double_param("place_z") * cm),
0124 tpc_envelope_logic, "tpc_envelope",
0125 logicWorld, false, false, OverlapCheck());
0126
0127
0128 add_geometry_node();
0129 }
0130
0131 void PHG4TpcDetector::CreateTpcGasMixture()
0132 {
0133 G4double density;
0134 G4int ncomponents, natoms;
0135
0136 G4double tpcGasTemperature = (273.15 + m_Params->get_double_param("TPC_gas_temperature")) * kelvin;
0137 G4double tpcGasPressure = m_Params->get_double_param("TPC_gas_pressure") * atmosphere;
0138
0139 G4Material *CF4 = new G4Material("CF4", density = sphenix_constants::CF4_density * mg / cm3, ncomponents = 2, kStateGas, tpcGasTemperature, tpcGasPressure);
0140 CF4->AddElement(G4NistManager::Instance()->FindOrBuildElement("C"), natoms = 1);
0141 CF4->AddElement(G4NistManager::Instance()->FindOrBuildElement("F"), natoms = 4);
0142
0143 G4Material *N2 = new G4Material("N2", density = 1.165 * mg / cm3, ncomponents = 1, kStateGas, tpcGasTemperature, tpcGasPressure);
0144 N2->AddElement(G4NistManager::Instance()->FindOrBuildElement("N"), natoms = 2);
0145
0146
0147
0148 G4Material *isobutane = new G4Material("isobutane", density = 2.59 * mg / cm3, ncomponents = 2, kStateGas, tpcGasTemperature, tpcGasPressure);
0149 isobutane->AddElement(G4NistManager::Instance()->FindOrBuildElement("C"), natoms = 4);
0150 isobutane->AddElement(G4NistManager::Instance()->FindOrBuildElement("H"), natoms = 10);
0151
0152 double Ne_frac = m_Params->get_double_param("Ne_frac");
0153 double Ar_frac = m_Params->get_double_param("Ar_frac");
0154 double CF4_frac = m_Params->get_double_param("CF4_frac");
0155 double N2_frac = m_Params->get_double_param("N2_frac");
0156 double isobutane_frac = m_Params->get_double_param("isobutane_frac");
0157
0158 const double Ne_den = G4NistManager::Instance()->FindOrBuildMaterial("G4_Ne")->GetDensity();
0159 const double Ar_den = G4NistManager::Instance()->FindOrBuildMaterial("G4_Ar")->GetDensity();
0160 const double CF4_den = CF4->GetDensity();
0161 const double N2_den = N2->GetDensity();
0162 const double isobutane_den = isobutane->GetDensity();
0163
0164 const double sphenix_tpc_gas_den = (Ne_den * Ne_frac)
0165 + (Ar_den * Ar_frac)
0166 + (CF4_den * CF4_frac)
0167 + (N2_den * N2_frac)
0168 + (isobutane_den * isobutane_frac);
0169
0170 G4Material *sPHENIX_tpc_gas = new G4Material("sPHENIX_TPC_Gas", sphenix_tpc_gas_den, ncomponents = 5, kStateGas);
0171 sPHENIX_tpc_gas->AddMaterial(G4NistManager::Instance()->FindOrBuildMaterial("G4_Ne"), (Ne_den * Ne_frac) / sphenix_tpc_gas_den);
0172 sPHENIX_tpc_gas->AddMaterial(G4NistManager::Instance()->FindOrBuildMaterial("G4_Ar"), (Ar_den * Ar_frac) / sphenix_tpc_gas_den);
0173 sPHENIX_tpc_gas->AddMaterial(CF4, (CF4_den * CF4_frac) / sphenix_tpc_gas_den);
0174 sPHENIX_tpc_gas->AddMaterial(N2, (N2_den * N2_frac) / sphenix_tpc_gas_den);
0175 sPHENIX_tpc_gas->AddMaterial(isobutane, (isobutane_den * isobutane_frac) / sphenix_tpc_gas_den);
0176 }
0177
0178 int PHG4TpcDetector::ConstructTpcGasVolume(G4LogicalVolume *tpc_envelope)
0179 {
0180 static std::map<int, std::string> tpcgasvolname =
0181 {{PHG4TpcDefs::North, "tpc_gas_north"},
0182 {PHG4TpcDefs::South, "tpc_gas_south"}};
0183
0184 CreateTpcGasMixture();
0185
0186
0187 double tpc_window_thickness = m_Params->get_double_param("window_thickness") * cm;
0188 double tpc_half_length = (m_Params->get_double_param("tpc_length") * cm - tpc_window_thickness) / 2.;
0189
0190
0191
0192 std::vector<double> thickness;
0193 std::vector<std::string> material;
0194 material.emplace_back("G4_Ni");
0195 thickness.push_back(.240 * cm);
0196 material.emplace_back("G4_Au");
0197 thickness.push_back(.008 * cm);
0198 G4Material *temp = nullptr;
0199 temp = GetDetectorMaterial("ENIG", false);
0200 if (temp == nullptr)
0201 {
0202 CreateCompositeMaterial("ENIG", material, thickness);
0203 }
0204
0205 G4VSolid *tpc_window = new G4Tubs("tpc_window", m_Params->get_double_param("gas_inner_radius") * cm, m_Params->get_double_param("gas_outer_radius") * cm, tpc_window_thickness / 2., 0., 2 * M_PI);
0206
0207 G4LogicalVolume *tpc_window_logic = new G4LogicalVolume(tpc_window,
0208 GetDetectorMaterial("ENIG"),
0209 "tpc_window");
0210
0211
0212
0213
0214
0215
0216
0217
0218 m_DisplayAction->AddVolume(tpc_window_logic, "TpcWindow");
0219 G4VPhysicalVolume *tpc_window_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0),
0220 tpc_window_logic, "tpc_window",
0221 tpc_envelope, false, PHG4TpcDefs::Window, OverlapCheck());
0222
0223 m_AbsorberVolumeSet.insert(tpc_window_phys);
0224
0225
0226
0227 double tpc_window_surface1_thickness = m_Params->get_double_param("window_surface1_thickness") * cm;
0228 double tpc_window_surface2_thickness = m_Params->get_double_param("window_surface2_thickness") * cm;
0229 double tpc_window_surface2_core_thickness = tpc_window_thickness - 2 * tpc_window_surface1_thickness;
0230 double tpc_window_core_thickness = tpc_window_surface2_core_thickness - 2 * (tpc_window_surface2_thickness);
0231
0232 G4VSolid *tpc_window_surface2_core =
0233 new G4Tubs("tpc_window_surface2_core", m_Params->get_double_param("gas_inner_radius") * cm, m_Params->get_double_param("gas_outer_radius") * cm,
0234 tpc_window_surface2_core_thickness / 2., 0., 2 * M_PI);
0235 G4LogicalVolume *tpc_window_surface2_core_logic = new G4LogicalVolume(tpc_window_surface2_core,
0236 GetDetectorMaterial(m_Params->get_string_param("window_surface2_material")),
0237 "tpc_window_surface2_core");
0238
0239 m_DisplayAction->AddVolume(tpc_window_surface2_core_logic, "TpcWindow");
0240
0241
0242
0243
0244
0245 G4VPhysicalVolume *tpc_window_surface2_core_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0),
0246 tpc_window_surface2_core_logic, "tpc_window_surface2_core",
0247 tpc_window_logic, false, PHG4TpcDefs::WindowCore, OverlapCheck());
0248
0249 m_AbsorberVolumeSet.insert(tpc_window_surface2_core_phys);
0250
0251
0252 G4VSolid *tpc_window_core =
0253 new G4Tubs("tpc_window", m_Params->get_double_param("gas_inner_radius") * cm, m_Params->get_double_param("gas_outer_radius") * cm,
0254 tpc_window_core_thickness / 2., 0., 2 * M_PI);
0255 G4LogicalVolume *tpc_window_core_logic = new G4LogicalVolume(tpc_window_core,
0256 GetDetectorMaterial(m_Params->get_string_param("window_core_material")),
0257 "tpc_window_core");
0258
0259 m_DisplayAction->AddVolume(tpc_window_core_logic, "TpcHoneyComb");
0260 G4VPhysicalVolume *tpc_window_core_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0),
0261 tpc_window_core_logic, "tpc_window_core",
0262 tpc_window_surface2_core_logic, false, PHG4TpcDefs::WindowCore, OverlapCheck());
0263
0264 m_AbsorberVolumeSet.insert(tpc_window_core_phys);
0265
0266
0267 G4VSolid *tpc_gas = new G4Tubs("tpc_gas", m_Params->get_double_param("gas_inner_radius") * cm, m_Params->get_double_param("gas_outer_radius") * cm, tpc_half_length / 2., 0., 2 * M_PI);
0268
0269 G4LogicalVolume *tpc_gas_logic = new G4LogicalVolume(tpc_gas,
0270 GetDetectorMaterial("sPHENIX_TPC_Gas"),
0271 "tpc_gas");
0272
0273 tpc_gas_logic->SetUserLimits(m_G4UserLimits);
0274 m_DisplayAction->AddVolume(tpc_gas_logic, "TpcGas");
0275 G4VPhysicalVolume *tpc_gas_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, (tpc_half_length + tpc_window_thickness) / 2.),
0276 tpc_gas_logic, tpcgasvolname[PHG4TpcDefs::North],
0277 tpc_envelope, false, PHG4TpcDefs::North, OverlapCheck());
0278
0279
0280 m_ActiveVolumeSet.insert(tpc_gas_phys);
0281 tpc_gas_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, -(tpc_half_length + tpc_window_thickness) / 2.),
0282 tpc_gas_logic, tpcgasvolname[PHG4TpcDefs::South],
0283 tpc_envelope, false, PHG4TpcDefs::South, OverlapCheck());
0284
0285
0286 m_ActiveVolumeSet.insert(tpc_gas_phys);
0287
0288 #if G4VERSION_NUMBER >= 1033
0289 const G4RegionStore *theRegionStore = G4RegionStore::GetInstance();
0290 G4Region *tpcregion = theRegionStore->GetRegion("REGION_TPCGAS");
0291 tpc_gas_logic->SetRegion(tpcregion);
0292 tpcregion->AddRootLogicalVolume(tpc_gas_logic);
0293 #endif
0294 return 0;
0295 }
0296
0297 int PHG4TpcDetector::ConstructTpcExternalSupports(G4LogicalVolume *logicWorld)
0298 {
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311 G4Material *stainlessSteel = GetDetectorMaterial("G4_STAINLESS-STEEL");
0312 double inch = 2.54 * cm;
0313
0314
0315 double hangerX = 21.193 * inch;
0316 double hangerY = 24.246 * inch;
0317 double hangerDiameter = 2. * inch;
0318 double extra_length = 0.941 * inch;
0319
0320 G4VSolid *hangerSupport = new G4Tubs("tpc_hanger_support", 0, hangerDiameter / 2., (6 * inch) + extra_length, 0., 2 * M_PI);
0321 G4LogicalVolume *hangerSupportLogic = new G4LogicalVolume(hangerSupport,
0322 stainlessSteel,
0323 "tpc_hanger_support");
0324
0325 double tpc_steel_location = (90 * inch) / 2 - 3 * inch - extra_length / 2.0;
0326
0327 m_DisplayAction->AddVolume(hangerSupportLogic, "TpcHangerSupport");
0328 G4VPhysicalVolume *tpc_hanger_support_phys[4] = {nullptr, nullptr, nullptr, nullptr};
0329 tpc_hanger_support_phys[0] = new G4PVPlacement(nullptr, G4ThreeVector(hangerX, hangerY, tpc_steel_location),
0330 hangerSupportLogic, "tpc_hanger_support_northleft",
0331 logicWorld, false, 0, OverlapCheck());
0332 tpc_hanger_support_phys[1] = new G4PVPlacement(nullptr, G4ThreeVector(-hangerX, hangerY, tpc_steel_location),
0333 hangerSupportLogic, "tpc_hanger_support_northright",
0334 logicWorld, false, 1, OverlapCheck());
0335 tpc_hanger_support_phys[2] = new G4PVPlacement(nullptr, G4ThreeVector(hangerX, hangerY, -tpc_steel_location),
0336 hangerSupportLogic, "tpc_hanger_support_southleft",
0337 logicWorld, false, 2, OverlapCheck());
0338 tpc_hanger_support_phys[3] = new G4PVPlacement(nullptr, G4ThreeVector(-hangerX, hangerY, -tpc_steel_location),
0339 hangerSupportLogic, "tpc_hanger_support_southright",
0340 logicWorld, false, 3, OverlapCheck());
0341
0342 m_AbsorberVolumeSet.insert(tpc_hanger_support_phys[0]);
0343 m_AbsorberVolumeSet.insert(tpc_hanger_support_phys[1]);
0344 m_AbsorberVolumeSet.insert(tpc_hanger_support_phys[2]);
0345 m_AbsorberVolumeSet.insert(tpc_hanger_support_phys[3]);
0346
0347 G4Material *carbonFiber = GetDetectorMaterial("CFRP_INTT");
0348 double hangerBeamInnerDiameter = 2.0 * inch;
0349 double hangerBeamOuterDiameter = 2.521 * inch;
0350
0351 G4VSolid *hangerBeam = new G4Tubs("tpc_hanger_beam", hangerBeamInnerDiameter / 2., hangerBeamOuterDiameter / 2., (90 * inch) / 2., 0., 2 * M_PI);
0352 G4LogicalVolume *hangerBeamLogic = new G4LogicalVolume(hangerBeam,
0353 carbonFiber,
0354 "tpc_hanger_beam");
0355
0356 m_DisplayAction->AddVolume(hangerBeamLogic, "TpcHangerBeam");
0357 G4VPhysicalVolume *tpc_hanger_beam_phys[2] = {nullptr, nullptr};
0358 tpc_hanger_beam_phys[0] = new G4PVPlacement(nullptr, G4ThreeVector(hangerX, hangerY, 0),
0359 hangerBeamLogic, "tpc_hanger_beam_left",
0360 logicWorld, false, 0, OverlapCheck());
0361 tpc_hanger_beam_phys[1] = new G4PVPlacement(nullptr, G4ThreeVector(-hangerX, hangerY, 0),
0362 hangerBeamLogic, "tpc_hanger_beam_right",
0363 logicWorld, false, 1, OverlapCheck());
0364
0365 m_AbsorberVolumeSet.insert(tpc_hanger_beam_phys[0]);
0366 m_AbsorberVolumeSet.insert(tpc_hanger_beam_phys[1]);
0367
0368
0369
0370
0371 double rodAngleStart = M_PI / 12.;
0372 double rodAngularSpacing = 2 * M_PI / 12.;
0373 double rodRadius = 31.5 * inch;
0374 double rodWallThickness = 1. / 8. * inch;
0375 double rodDiameter = 3. / 4. * inch;
0376 G4VSolid *tieRod = new G4Tubs("tpc_tie_rod", rodDiameter / 2. - rodWallThickness, rodDiameter / 2., (m_Params->get_double_param("tpc_length") * cm) / 2., 0., 2 * M_PI);
0377 G4LogicalVolume *tieRodLogic = new G4LogicalVolume(tieRod,
0378 carbonFiber,
0379 "tpc_tie_rod");
0380
0381 G4VPhysicalVolume *tpc_tie_rod_phys[12] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
0382 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
0383
0384 std::ostringstream name;
0385 for (int i = 0; i < 12; i++)
0386 {
0387 double ang = rodAngleStart + rodAngularSpacing * i;
0388 name.str("");
0389 name << "tpc_tie_rod_" << i;
0390 tpc_tie_rod_phys[i] = new G4PVPlacement(nullptr, G4ThreeVector(rodRadius * cos(ang), rodRadius * sin(ang), 0),
0391 tieRodLogic, name.str(),
0392 logicWorld, false, i, OverlapCheck());
0393 m_AbsorberVolumeSet.insert(tpc_tie_rod_phys[i]);
0394 }
0395
0396
0397
0398
0399
0400
0401
0402 return 0;
0403 }
0404
0405 int PHG4TpcDetector::ConstructTpcCageVolume(G4LogicalVolume *tpc_envelope)
0406 {
0407
0408
0409
0410
0411
0412
0413 static const int nlayers = 9;
0414 static const double thickness[nlayers] = {m_Params->get_double_param("cage_layer_1_thickness") * cm,
0415 m_Params->get_double_param("cage_layer_2_thickness") * cm,
0416 m_Params->get_double_param("cage_layer_3_thickness") * cm,
0417 m_Params->get_double_param("cage_layer_4_thickness") * cm,
0418 m_Params->get_double_param("cage_layer_5_thickness") * cm,
0419 m_Params->get_double_param("cage_layer_6_thickness") * cm,
0420 m_Params->get_double_param("cage_layer_7_thickness") * cm,
0421 m_Params->get_double_param("cage_layer_8_thickness") * cm,
0422 m_Params->get_double_param("cage_layer_9_thickness") * cm};
0423
0424 static const std::string material[nlayers] = {m_Params->get_string_param("cage_layer_1_material"),
0425 m_Params->get_string_param("cage_layer_2_material"),
0426 m_Params->get_string_param("cage_layer_3_material"),
0427 m_Params->get_string_param("cage_layer_4_material"),
0428 m_Params->get_string_param("cage_layer_5_material"),
0429 m_Params->get_string_param("cage_layer_6_material"),
0430 m_Params->get_string_param("cage_layer_7_material"),
0431 m_Params->get_string_param("cage_layer_8_material"),
0432 m_Params->get_string_param("cage_layer_9_material")};
0433
0434 double tpc_cage_radius = m_InnerCageRadius;
0435 std::ostringstream name;
0436 for (int i = 0; i < nlayers; i++)
0437 {
0438 name.str("");
0439 int layerno = i + 1;
0440 name << "tpc_cage_layer_" << layerno;
0441 G4VSolid *tpc_cage_layer = new G4Tubs(name.str(), tpc_cage_radius, tpc_cage_radius + thickness[i], m_Params->get_double_param("tpc_length") * cm / 2., 0., 2 * M_PI);
0442 G4LogicalVolume *tpc_cage_layer_logic = new G4LogicalVolume(tpc_cage_layer,
0443 GetDetectorMaterial(material[i]),
0444 name.str());
0445 m_DisplayAction->AddTpcInnerLayer(tpc_cage_layer_logic);
0446 G4VPhysicalVolume *tpc_cage_layer_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0),
0447 tpc_cage_layer_logic, name.str(),
0448 tpc_envelope, false, layerno, OverlapCheck());
0449 m_AbsorberVolumeSet.insert(tpc_cage_layer_phys);
0450 tpc_cage_radius += thickness[i];
0451 }
0452
0453
0454 tpc_cage_radius = m_OuterCageRadius;
0455 for (int i = 0; i < nlayers; i++)
0456 {
0457 tpc_cage_radius -= thickness[i];
0458 name.str("");
0459 int layerno = 10 + 1 + i;
0460 name << "tpc_cage_layer_" << layerno;
0461 G4VSolid *tpc_cage_layer = new G4Tubs(name.str(), tpc_cage_radius, tpc_cage_radius + thickness[i], m_Params->get_double_param("tpc_length") * cm / 2., 0., 2 * M_PI);
0462 G4LogicalVolume *tpc_cage_layer_logic = new G4LogicalVolume(tpc_cage_layer,
0463 GetDetectorMaterial(material[i]),
0464 name.str());
0465 m_DisplayAction->AddTpcOuterLayer(tpc_cage_layer_logic);
0466 G4VPhysicalVolume *tpc_cage_layer_phys = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0),
0467 tpc_cage_layer_logic, name.str(),
0468 tpc_envelope, false, layerno, OverlapCheck());
0469 m_AbsorberVolumeSet.insert(tpc_cage_layer_phys);
0470 }
0471
0472 return 0;
0473 }
0474
0475 void PHG4TpcDetector::CreateCompositeMaterial(
0476 const std::string &compositeName,
0477 std::vector<std::string> materialName,
0478 const std::vector<double> &thickness)
0479 {
0480
0481
0482
0483 G4Material *tempmat = GetDetectorMaterial(compositeName, false);
0484
0485 if (tempmat != nullptr)
0486 {
0487 std::cout << __PRETTY_FUNCTION__ << " Fatal Error: composite material " << compositeName << " already exists" << std::endl;
0488 assert(!tempmat);
0489 }
0490
0491
0492 assert(materialName.size() == thickness.size());
0493
0494
0495 double totalArealDensity = 0, totalThickness = 0;
0496 for (std::vector<double>::size_type i = 0; i < thickness.size(); i++)
0497 {
0498 tempmat = GetDetectorMaterial(materialName[i]);
0499 if (tempmat == nullptr)
0500 {
0501 std::cout << __PRETTY_FUNCTION__ << " Fatal Error: component material " << materialName[i] << " does not exist." << std::endl;
0502 gSystem->Exit(1);
0503 exit(1);
0504 }
0505 totalArealDensity += tempmat->GetDensity() * thickness[i];
0506 totalThickness += thickness[i];
0507 }
0508
0509
0510 double compositeDensity = totalArealDensity / totalThickness;
0511 G4Material *composite = new G4Material(compositeName, compositeDensity, thickness.size());
0512
0513
0514 for (std::vector<double>::size_type i = 0; i < thickness.size(); i++)
0515 {
0516 tempmat = GetDetectorMaterial(materialName[i]);
0517 composite->AddMaterial(tempmat, thickness[i] * tempmat->GetDensity() / totalArealDensity);
0518 }
0519
0520
0521 return;
0522 }
0523
0524
0525 void PHG4TpcDetector::add_geometry_node()
0526 {
0527
0528 const std::string geonode_name = "CYLINDERCELLGEOM_SVTX";
0529 auto geonode = findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode(), geonode_name);
0530 if (!geonode)
0531 {
0532 geonode = new PHG4TpcCylinderGeomContainer;
0533 PHNodeIterator iter(topNode());
0534 auto runNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "RUN"));
0535 auto newNode = new PHIODataNode<PHObject>(geonode, geonode_name, "PHObject");
0536 runNode->addNode(newNode);
0537 }
0538
0539 m_cdb = CDBInterface::instance();
0540 std::string calibdir = m_cdb->getUrl("TPC_FEE_CHANNEL_MAP");
0541
0542 if (! calibdir.empty())
0543 {
0544 m_cdbttree = new CDBTTree(calibdir);
0545 m_cdbttree->LoadCalibrations();
0546 }
0547 else
0548 {
0549 std::cout << "PHG4TpcPadPlaneReadout::InitRun no TPC_FEE_CHANNEL_MAP found" << std::endl;
0550 exit(1);
0551 }
0552
0553 const std::array<int, 3> NTpcLayers =
0554 {{m_Params->get_int_param("ntpc_layers_inner"),
0555 m_Params->get_int_param("ntpc_layers_mid"),
0556 m_Params->get_int_param("ntpc_layers_outer")}};
0557
0558 std::array<double, 3> MinLayer{};
0559 MinLayer[0] = m_Params->get_int_param("tpc_minlayer_inner");
0560 MinLayer[1] = MinLayer[0] + NTpcLayers[0];
0561 MinLayer[2] = MinLayer[1] + NTpcLayers[1];
0562
0563
0564 const std::array<double, 5> Thickness =
0565 {{
0566 0.56598621677629212,
0567 1.0206889851687158,
0568 1.0970475085472556,
0569 0.5630547309825637,
0570 0.56891770257002054,
0571 }};
0572
0573 const double drift_velocity = m_Params->get_double_param("drift_velocity");
0574 const double tpc_adc_clock = m_Params->get_double_param("tpc_adc_clock");
0575 const double MaxZ = m_Params->get_double_param("maxdriftlength");
0576 const double TBinWidth = tpc_adc_clock;
0577 const double extended_readout_time = m_Params->get_double_param("extended_readout_time");
0578 const double MaxT = extended_readout_time + 2. * MaxZ / drift_velocity;
0579 const double MinT = 0;
0580 const int NTBins = (int) ((MaxT - MinT) / TBinWidth) + 1;
0581
0582 std::cout << PHWHERE << "MaxT " << MaxT << " TBinWidth " << TBinWidth << " extended readout time "
0583 << extended_readout_time << " NTBins = " << NTBins << " drift velocity " << drift_velocity << std::endl;
0584
0585
0586 const std::array<int, 3> NPhiBins =
0587 {{m_Params->get_int_param("ntpc_phibins_inner"),
0588 m_Params->get_int_param("ntpc_phibins_mid"),
0589 m_Params->get_int_param("ntpc_phibins_outer")}};
0590
0591
0592
0593 static constexpr int NSides = 2;
0594 static constexpr int NSectors = 12;
0595 static constexpr int NLayers = 16*3;
0596
0597 std::array<std::vector<double>, NSides> sector_R_bias;
0598 std::array<std::vector<double>, NSides> sector_Phi_bias;
0599 std::array<std::vector<double>, NSides> sector_min_Phi;
0600 std::array<std::vector<double>, NSides> sector_max_Phi;
0601 std::array<std::vector<double >, NLayers > pad_phi;
0602 std::array<std::vector<double >, NLayers > pad_R;
0603 std::array<double, NLayers > layer_radius;
0604 std::array<double, NLayers > phi_bin_width_cdb;
0605 std::array<std::array<std::array<double, 3 >, NSectors >, NSides > sec_max_phi;
0606 std::array<std::array<std::array<double, 3 >, NSectors >, NSides > sec_min_phi;
0607 int Nfee = 26;
0608 int Nch = 256;
0609
0610 for (int f = 0; f < Nfee; f++)
0611 {
0612 for (int ch = 0; ch < Nch; ch++)
0613 {
0614 unsigned int key = 256 * (f) + ch;
0615 std::string varname = "layer";
0616 int l = m_cdbttree->GetIntValue(key, varname);
0617 if (l > 6)
0618 {
0619 int v_layer = l - 7;
0620 std::string phiname = "phi";
0621 pad_phi[v_layer].push_back( m_cdbttree->GetDoubleValue(key, phiname) );
0622 std::string rname = "R";
0623 pad_R[v_layer].push_back( m_cdbttree->GetDoubleValue(key, rname) );
0624 }
0625 }
0626 }
0627
0628 for(size_t layer=0;layer<NLayers;layer++)
0629 {
0630 layer_radius[layer]=0;
0631 for (int pad=0; pad<(int)pad_R[(int)layer].size(); pad++)
0632 {
0633 layer_radius[(int)layer] += pad_R[(int)layer][pad];
0634 }
0635
0636 layer_radius[(int)layer] = layer_radius[(int)layer]/pad_R[(int)layer].size();
0637 layer_radius[(int)layer] = layer_radius[(int)layer]/10.;
0638
0639 auto min_phi_iter = std::min_element(pad_phi[layer].begin(),pad_phi[layer].end());
0640 auto max_phi_iter = std::max_element(pad_phi[layer].begin(),pad_phi[layer].end());
0641 double min_phi = static_cast<double>(*min_phi_iter);
0642 double max_phi = static_cast<double>(*max_phi_iter);
0643 min_phi = min_phi - M_PI/2.;
0644 max_phi = max_phi - M_PI/2.;
0645 phi_bin_width_cdb[layer] = std::abs(max_phi - min_phi)/(NPhiBins[(int)(layer / 16)]/12 - 1);
0646 double SectorPhi = std::abs(max_phi - min_phi) + phi_bin_width_cdb[layer];
0647 for (int zside = 0; zside < 2; zside++)
0648 {
0649 for (int isector = 0; isector < NSectors; isector++)
0650 {
0651 if (zside ==0){
0652 sec_min_phi[zside][isector][(int)layer / 16] = M_PI - 2 * M_PI / 12 * (isector + 1) +(-(max_phi) - phi_bin_width_cdb[layer]/2. ) ;
0653 sec_max_phi[zside][isector][(int)layer / 16] = sec_min_phi[zside][isector][(int)layer / 16] + SectorPhi;
0654 }
0655 if (zside ==1){
0656 sec_max_phi[zside][isector][(int)layer / 16] = M_PI - 2 * M_PI / 12 * (isector + 1)+ (max_phi + phi_bin_width_cdb[layer]/2. ) ;
0657 sec_min_phi[zside][isector][(int)layer / 16] = sec_max_phi[zside][isector][(int)layer / 16] - SectorPhi;
0658 }
0659 }
0660 }
0661 }
0662
0663 for (int iregion = 0; iregion < 3; ++iregion)
0664 {
0665
0666 for (int zside = 0; zside < 2; zside++)
0667 {
0668 sector_R_bias[zside].clear();
0669 sector_Phi_bias[zside].clear();
0670 sector_min_Phi[zside].clear();
0671 sector_max_Phi[zside].clear();
0672
0673 for (int isector = 0; isector < NSectors; ++isector)
0674 {
0675
0676
0677 sector_R_bias[zside].push_back(0);
0678 sector_Phi_bias[zside].push_back(0);
0679
0680 sector_min_Phi[zside].push_back(sec_min_phi[zside][isector][iregion]);
0681 sector_max_Phi[zside].push_back(sec_max_phi[zside][isector][iregion]);
0682 }
0683 }
0684
0685
0686 for (int layer = MinLayer[iregion]; layer < MinLayer[iregion] + NTpcLayers[iregion]; ++layer)
0687 {
0688 if (Verbosity())
0689 {
0690 std::cout << " layer " << layer << " MinLayer " << MinLayer[iregion] << " region " << iregion
0691 << " radius " << layer_radius[(int) layer - 7]
0692 << " thickness " << Thickness[iregion]
0693 << " NTBins " << NTBins << " tmin " << MinT << " tstep " << TBinWidth
0694 << " phibins " << NPhiBins[iregion] << " phistep " << phi_bin_width_cdb[layer] << std::endl;
0695 }
0696
0697 auto layerseggeo = new PHG4TpcCylinderGeom;
0698 layerseggeo->set_layer(layer);
0699
0700 double r_length = Thickness[iregion];
0701 if (iregion == 0)
0702 {
0703 if (layer % 2 == 0)
0704 {
0705 r_length = Thickness[4];
0706 }
0707 else
0708 {
0709 r_length = Thickness[3];
0710 }
0711 }
0712 int v_layer = layer-7;
0713 if (v_layer>=0){
0714 layerseggeo->set_thickness(r_length);
0715 layerseggeo->set_radius(layer_radius[v_layer]);
0716 layerseggeo->set_binning(PHG4CellDefs::sizebinning);
0717 layerseggeo->set_zbins(NTBins);
0718 layerseggeo->set_zmin(MinT);
0719 layerseggeo->set_zstep(TBinWidth);
0720 layerseggeo->set_phibins(NPhiBins[iregion]);
0721 layerseggeo->set_phistep(phi_bin_width_cdb[v_layer]);
0722 layerseggeo->set_r_bias(sector_R_bias);
0723 layerseggeo->set_phi_bias(sector_Phi_bias);
0724 layerseggeo->set_sector_min_phi(sector_min_Phi);
0725 layerseggeo->set_sector_max_phi(sector_max_Phi);
0726 }
0727
0728
0729
0730 if (NPhiBins[iregion] * NTBins > 5100000)
0731 {
0732 std::cout << "increase Tpc cellsize, number of cells "
0733 << NPhiBins[iregion] * NTBins << " for layer " << layer
0734 << " exceed 5.1M limit" << std::endl;
0735 gSystem->Exit(1);
0736 }
0737 geonode->AddLayerCellGeom(layerseggeo);
0738
0739 }
0740 }
0741 }