Warning, file /epic/src/DRICH_geo.cpp was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007 #include "DD4hep/DetFactoryHelper.h"
0008 #include "DD4hep/OpticalSurfaces.h"
0009 #include "DD4hep/Printout.h"
0010 #include "DDRec/DetectorData.h"
0011 #include "DDRec/Surface.h"
0012 #include <numeric>
0013 #include <vector>
0014
0015 #include <XML/Helper.h>
0016
0017 using namespace dd4hep;
0018 using namespace dd4hep::rec;
0019
0020 #ifdef WITH_IRT2_SUPPORT
0021 #include <TFile.h>
0022
0023 #include "IRT2/CherenkovDetectorCollection.h"
0024 #include "IRT2/SphericalSurface.h"
0025
0026 using namespace IRT2;
0027 #endif
0028
0029
0030 static Ref_t createDetector(Detector& desc, xml::Handle_t handle, SensitiveDetector sens) {
0031
0032 xml::DetElement detElem = handle;
0033 std::string detName = detElem.nameStr();
0034 int detID = detElem.id();
0035 xml::Component dims = detElem.dimensions();
0036 OpticalSurfaceManager surfMgr = desc.surfaceManager();
0037 DetElement det(detName, detID);
0038 sens.setType("tracker");
0039
0040
0041 #ifdef WITH_IRT2_SUPPORT
0042 auto geometry = CherenkovDetectorCollection::Instance();
0043 auto cdet = geometry->AddNewDetector(detName.c_str());
0044 #endif
0045
0046
0047
0048 auto vesselZmin = dims.attr<double>(_Unicode(zmin));
0049 auto vesselLength = dims.attr<double>(_Unicode(length));
0050 auto vesselRmin0 = dims.attr<double>(_Unicode(rmin0));
0051 auto vesselRmin1 = dims.attr<double>(_Unicode(rmin1));
0052 auto vesselRmax0 = dims.attr<double>(_Unicode(rmax0));
0053 auto vesselRmax1 = dims.attr<double>(_Unicode(rmax1));
0054 auto vesselRmax2 = dims.attr<double>(_Unicode(rmax2));
0055 auto snoutLength = dims.attr<double>(_Unicode(snout_length));
0056 auto nSectors = dims.attr<int>(_Unicode(nsectors));
0057 #ifdef WITH_IRT2_SUPPORT
0058 cdet->SetSectorCount(nSectors);
0059
0060 cdet->SetSectorPhase(0.0);
0061 #endif
0062 auto wallThickness = dims.attr<double>(_Unicode(wall_thickness));
0063 auto windowThickness = dims.attr<double>(_Unicode(window_thickness));
0064 auto vesselMat = desc.material(detElem.attr<std::string>(_Unicode(material)));
0065
0066 auto gasvolMatName = detElem.attr<std::string>(_Unicode(gas));
0067 auto gasvolMat = desc.material(gasvolMatName);
0068
0069 auto vesselVis = desc.visAttributes(detElem.attr<std::string>(_Unicode(vis_vessel)));
0070 auto gasvolVis = desc.visAttributes(detElem.attr<std::string>(_Unicode(vis_gas)));
0071
0072 auto radiatorElem = detElem.child(_Unicode(radiator));
0073 auto radiatorRmin = radiatorElem.attr<double>(_Unicode(rmin));
0074 auto radiatorRmax = radiatorElem.attr<double>(_Unicode(rmax));
0075 auto radiatorPitch = radiatorElem.attr<double>(_Unicode(pitch));
0076 auto radiatorFrontplane = radiatorElem.attr<double>(_Unicode(frontplane));
0077
0078 auto aerogelElem = radiatorElem.child(_Unicode(aerogel));
0079 auto aerogelMatName = aerogelElem.attr<std::string>(_Unicode(material));
0080 auto aerogelMat = desc.material(aerogelMatName);
0081 auto aerogelVis = desc.visAttributes(aerogelElem.attr<std::string>(_Unicode(vis)));
0082 auto aerogelThickness = aerogelElem.attr<double>(_Unicode(thickness));
0083
0084 auto coronasElem = radiatorElem.child(_Unicode(coronas));
0085 auto coronasMat = desc.material(coronasElem.attr<std::string>(_Unicode(material)));
0086 auto coronasVis = desc.visAttributes(coronasElem.attr<std::string>(_Unicode(vis)));
0087 auto coronasThickness = coronasElem.attr<double>(_Unicode(thickness));
0088 auto segmentationType = coronasElem.attr<std::string>("segmentation");
0089
0090 std::vector<double> radii;
0091 std::vector<int> numSegments;
0092 for (xml::Collection_t crownIt(coronasElem, _Unicode(crown)); crownIt; ++crownIt) {
0093 xml::Component crownElem = crownIt;
0094 radii.push_back(crownElem.attr<double>(_Unicode(radius)));
0095 if (crownElem.hasAttr(_Unicode(num_segments)))
0096 numSegments.push_back(crownElem.attr<int>(_Unicode(num_segments)));
0097 }
0098 int numCrowns = radii.size();
0099
0100 auto filterElem = radiatorElem.child(_Unicode(filter));
0101 auto filterMatName = filterElem.attr<std::string>(_Unicode(material));
0102 auto filterMat = desc.material(filterMatName);
0103 auto filterVis = desc.visAttributes(filterElem.attr<std::string>(_Unicode(vis)));
0104 auto filterThickness = filterElem.attr<double>(_Unicode(thickness));
0105
0106 auto airgapElem = radiatorElem.child(_Unicode(airgap));
0107 auto airgapMat = desc.material(airgapElem.attr<std::string>(_Unicode(material)));
0108 auto airgapVis = desc.visAttributes(airgapElem.attr<std::string>(_Unicode(vis)));
0109 auto airgapThickness = airgapElem.attr<double>(_Unicode(thickness));
0110
0111 auto mirrorElem = detElem.child(_Unicode(mirror));
0112 auto mirrorMat = desc.material(mirrorElem.attr<std::string>(_Unicode(material)));
0113 auto mirrorVis = desc.visAttributes(mirrorElem.attr<std::string>(_Unicode(vis)));
0114 auto mirrorSurf = surfMgr.opticalSurface(mirrorElem.attr<std::string>(_Unicode(surface)));
0115 auto mirrorBackplane = mirrorElem.attr<double>(_Unicode(backplane));
0116 auto mirrorThickness = mirrorElem.attr<double>(_Unicode(thickness));
0117 auto mirrorRmin = mirrorElem.attr<double>(_Unicode(rmin));
0118 auto mirrorRmax = mirrorElem.attr<double>(_Unicode(rmax));
0119 auto mirrorPhiw = mirrorElem.attr<double>(_Unicode(phiw));
0120 auto focusTuneZ = mirrorElem.attr<double>(_Unicode(focus_tune_z));
0121 auto focusTuneX = mirrorElem.attr<double>(_Unicode(focus_tune_x));
0122
0123 auto sensorboxLength = desc.constant<double>("DRICH_sensorbox_length");
0124 auto sensorboxRmin = desc.constant<double>("DRICH_sensorbox_rmin");
0125 auto sensorboxRmax = desc.constant<double>("DRICH_sensorbox_rmax");
0126 auto sensorboxDphi = desc.constant<double>("DRICH_sensorbox_dphi");
0127
0128 auto pssElem = detElem.child(_Unicode(sensors)).child(_Unicode(pss));
0129 auto pssMat = desc.material(pssElem.attr<std::string>(_Unicode(material)));
0130 auto pssVis = desc.visAttributes(pssElem.attr<std::string>(_Unicode(vis)));
0131 auto pssSurf = surfMgr.opticalSurface(pssElem.attr<std::string>(_Unicode(surface)));
0132 auto pssSide = pssElem.attr<double>(_Unicode(side));
0133 auto pssThickness = pssElem.attr<double>(_Unicode(thickness));
0134
0135 auto resinElem = detElem.child(_Unicode(sensors)).child(_Unicode(resin));
0136 auto resinMat = desc.material(resinElem.attr<std::string>(_Unicode(material)));
0137 auto resinVis = desc.visAttributes(resinElem.attr<std::string>(_Unicode(vis)));
0138 auto resinSide = resinElem.attr<double>(_Unicode(side));
0139 auto resinThickness = resinElem.attr<double>(_Unicode(thickness));
0140
0141 auto pduElem = detElem.child(_Unicode(sensors)).child(_Unicode(pdu));
0142 auto pduNumSensors = desc.constant<int>("DRICH_pdu_num_sensors");
0143 auto pduSensorGap = desc.constant<double>("DRICH_pdu_sensor_gap");
0144 auto pduGap = desc.constant<double>("DRICH_pdu_gap");
0145
0146 auto sensorSphElem = detElem.child(_Unicode(sensors)).child(_Unicode(sphere));
0147 auto sensorSphRadius = sensorSphElem.attr<double>(_Unicode(radius));
0148 auto sensorSphCenterX = sensorSphElem.attr<double>(_Unicode(centerx));
0149 auto sensorSphCenterZ = sensorSphElem.attr<double>(_Unicode(centerz));
0150
0151 auto sensorSphPatchElem = detElem.child(_Unicode(sensors)).child(_Unicode(sphericalpatch));
0152 auto sensorSphPatchPhiw = sensorSphPatchElem.attr<double>(_Unicode(phiw));
0153 auto sensorSphPatchRmin = sensorSphPatchElem.attr<double>(_Unicode(rmin));
0154 auto sensorSphPatchRmax = sensorSphPatchElem.attr<double>(_Unicode(rmax));
0155 auto sensorSphPatchZmin = sensorSphPatchElem.attr<double>(_Unicode(zmin));
0156
0157 auto readoutName = detElem.attr<std::string>(_Unicode(readout));
0158
0159 auto debugOpticsMode = desc.constant<int>("DRICH_debug_optics");
0160 bool debugSector = desc.constant<int>("DRICH_debug_sector") == 1;
0161 bool debugMirror = desc.constant<int>("DRICH_debug_mirror") == 1;
0162 bool debugSensors = desc.constant<int>("DRICH_debug_sensors") == 1;
0163
0164
0165 bool debugOptics = debugOpticsMode > 0;
0166 if (debugOptics) {
0167 printout(WARNING, "DRICH_geo", "DEBUGGING DRICH OPTICS");
0168 switch (debugOpticsMode) {
0169 case 1:
0170 vesselMat = aerogelMat = filterMat = pssMat = gasvolMat = desc.material("VacuumOptical");
0171 break;
0172 case 2:
0173 vesselMat = aerogelMat = filterMat = pssMat = desc.material("VacuumOptical");
0174 break;
0175 case 3:
0176 vesselMat = aerogelMat = filterMat = gasvolMat = desc.material("VacuumOptical");
0177 break;
0178 default:
0179 printout(FATAL, "DRICH_geo", "UNKNOWN debugOpticsMode");
0180 return det;
0181 }
0182 }
0183
0184
0185 if (debugOptics || debugMirror || debugSensors)
0186 debugSector = true;
0187 if (debugSector)
0188 gasvolVis = vesselVis = desc.invisible();
0189
0190 #ifdef WITH_IRT2_SUPPORT
0191 SphericalSurface* msurface = 0;
0192 IRT2::OpticalBoundary* mboundary = 0;
0193 #endif
0194
0195
0196
0197
0198
0199 std::vector<std::string> sensorIDfields = {"pdu", "sipm", "sector"};
0200 const auto& readoutCoder = *desc.readout(readoutName).idSpec().decoder();
0201
0202 uint64_t cellMask = 0;
0203 for (const auto& idField : sensorIDfields)
0204 cellMask |= readoutCoder[idField].mask();
0205 desc.add(Constant("DRICH_cell_mask", std::to_string(cellMask)));
0206
0207 auto encodeSensorID = [&readoutCoder](auto ids) {
0208 uint64_t enc = 0;
0209 for (const auto& [idField, idValue] : ids)
0210 enc |= uint64_t(idValue) << readoutCoder[idField].offset();
0211 return enc;
0212 };
0213 #ifdef WITH_IRT2_SUPPORT
0214 uint64_t sector_mask = ~(0x0ul) ^ readoutCoder["sector"].mask();
0215
0216 cdet->SetReadoutCellMask(cellMask & sector_mask);
0217 #endif
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229 double tankLength = vesselLength - snoutLength;
0230 double vesselZmax = vesselZmin + vesselLength;
0231
0232
0233 double boreDelta = vesselRmin1 - vesselRmin0;
0234 double snoutDelta = vesselRmax1 - vesselRmax0;
0235 Cone vesselSnout(snoutLength / 2.0, vesselRmin0, vesselRmax0,
0236 vesselRmin0 + boreDelta * snoutLength / vesselLength, vesselRmax1);
0237 Cone gasvolSnout(
0238
0239
0240
0241
0242
0243 snoutLength / 2.0, vesselRmin0 + wallThickness, vesselRmax0 - wallThickness,
0244 vesselRmin0 + boreDelta * (snoutLength - windowThickness) / vesselLength + wallThickness,
0245 vesselRmax1 - wallThickness + windowThickness * (vesselRmax1 - vesselRmax0) / snoutLength);
0246
0247
0248
0249
0250 Polycone vesselTank(
0251 0, 2 * M_PI,
0252
0253 {vesselSnout.rMin2(),
0254 std::lerp(vesselSnout.rMin2(), vesselRmin1, (sensorboxLength - snoutLength) / tankLength),
0255 vesselRmin1},
0256 {vesselSnout.rMax2(), vesselRmax2, vesselRmax2},
0257
0258 {-tankLength / 2.0, -tankLength / 2.0 + sensorboxLength - snoutLength, tankLength / 2.0});
0259 Polycone gasvolTank(
0260 0, 2 * M_PI,
0261
0262 {gasvolSnout.rMin2(),
0263 std::lerp(gasvolSnout.rMin2(), vesselRmin1 + wallThickness,
0264 (sensorboxLength - snoutLength) / tankLength),
0265 vesselRmin1 + wallThickness},
0266 {gasvolSnout.rMax2(), vesselRmax2 - wallThickness, vesselRmax2 - wallThickness},
0267
0268 {-tankLength / 2.0 + windowThickness,
0269 -tankLength / 2.0 + windowThickness + sensorboxLength - snoutLength,
0270 tankLength / 2.0 - windowThickness});
0271
0272
0273 double dphi = atan2(wallThickness, sensorboxRmax);
0274 Tube vesselSensorboxTube(sensorboxRmin, sensorboxRmax, sensorboxLength / 2., -sensorboxDphi / 2.,
0275 sensorboxDphi / 2.);
0276 Tube gasvolSensorboxTube(sensorboxRmin + wallThickness, sensorboxRmax - wallThickness,
0277 sensorboxLength / 2., -sensorboxDphi / 2. + dphi,
0278 sensorboxDphi / 2. - dphi);
0279
0280
0281 UnionSolid vesselUnion(vesselTank, vesselSnout, Position(0., 0., -vesselLength / 2.));
0282 UnionSolid gasvolUnion(gasvolTank, gasvolSnout,
0283 Position(0., 0., -vesselLength / 2. + windowThickness));
0284
0285
0286 for (int isec = 0; isec < nSectors; isec++) {
0287 RotationZ sectorRotation((isec + 0.5) * 2 * M_PI / nSectors);
0288 vesselUnion = UnionSolid(
0289 vesselUnion, vesselSensorboxTube,
0290 Transform3D(sectorRotation, Position(0., 0., -(snoutLength + sensorboxLength - 0.6) / 2.)));
0291 gasvolUnion = UnionSolid(
0292 gasvolUnion, gasvolSensorboxTube,
0293 Transform3D(sectorRotation,
0294 Position(0., 0., -(snoutLength + sensorboxLength) / 2. + windowThickness)));
0295 }
0296
0297
0298 Box vesselBox(1001, 1001, 1001);
0299 Box gasvolBox(1000, 1000, 1000);
0300
0301
0302 Solid vesselSolid, gasvolSolid;
0303 switch (debugOpticsMode) {
0304 case 0:
0305 vesselSolid = vesselUnion;
0306 gasvolSolid = gasvolUnion;
0307 break;
0308 case 1:
0309 case 3:
0310 vesselSolid = vesselBox;
0311 gasvolSolid = gasvolBox;
0312 break;
0313 case 2:
0314 vesselSolid = vesselBox;
0315 gasvolSolid = gasvolUnion;
0316 break;
0317 }
0318
0319
0320 Volume vesselVol(detName, vesselSolid, vesselMat);
0321 Volume gasvolVol(detName + "_gas", gasvolSolid, gasvolMat);
0322 vesselVol.setVisAttributes(vesselVis);
0323 vesselVol.setRegion(desc, detElem.regionStr());
0324 gasvolVol.setVisAttributes(gasvolVis);
0325
0326
0327
0328
0329
0330
0331
0332
0333 auto originFront = Position(0., 0., -tankLength / 2.0 - snoutLength);
0334
0335 auto vesselPos = Position(0, 0, vesselZmin) - originFront;
0336
0337
0338 PlacedVolume gasvolPV = vesselVol.placeVolume(gasvolVol, Position(0, 0, 0));
0339 DetElement gasvolDE(det, "gasvol_de", 0);
0340 gasvolDE.setPlacement(gasvolPV);
0341
0342
0343 Volume motherVol = desc.pickMotherVolume(det);
0344 PlacedVolume vesselPV = motherVol.placeVolume(vesselVol, vesselPos);
0345 vesselPV.addPhysVolID("system", detID);
0346 det.setPlacement(vesselPV);
0347
0348
0349
0350
0351
0352
0353 Volume aerogelVol;
0354 PlacedVolume aerogelPV;
0355 DetElement aerogelDE;
0356 double structureThickness = aerogelThickness;
0357 auto radiatorPos = Position(0., 0., radiatorFrontplane + 0.5 * structureThickness) + originFront;
0358
0359 Cone aerogelSolid(aerogelThickness / 2.0, radiatorRmin, radiatorRmax,
0360 radiatorRmin + boreDelta * aerogelThickness / vesselLength, radiatorRmax);
0361 auto aerogelPlacement = Translation3D(radiatorPos) *
0362 RotationY(radiatorPitch);
0363 aerogelVol = Volume(detName + "_aerogel", aerogelSolid, aerogelMat);
0364 aerogelVol.setVisAttributes(aerogelVis);
0365 aerogelPV = gasvolVol.placeVolume(aerogelVol, aerogelPlacement);
0366 aerogelDE = DetElement(det, "aerogel_de", 0);
0367 aerogelDE.setPlacement(aerogelPV);
0368
0369 if (segmentationType == "trapezoidal") {
0370 double crownHeight = structureThickness;
0371 std::vector<double> innerRadiusBottoms_half;
0372 std::vector<double> innerRadiusTops_half;
0373 std::vector<double> outerRadiusBottoms_half;
0374 std::vector<double> outerRadiusTops_half;
0375
0376
0377 for (int i = 0; i < numCrowns; i++) {
0378 double centralRadius = radii[i];
0379 double rMinBottom, rMinTop, rMaxBottom, rMaxTop;
0380
0381 if (i == 0) {
0382
0383 rMinBottom = centralRadius - boreDelta * (coronasThickness / 2.0) / vesselLength;
0384 rMinTop = rMinBottom + snoutDelta * aerogelThickness / snoutLength;
0385 rMaxBottom = rMinBottom + boreDelta * coronasThickness / vesselLength;
0386 rMaxTop = rMinTop + boreDelta * coronasThickness / vesselLength;
0387
0388 } else {
0389 double innerRadius = centralRadius - coronasThickness / 2.0;
0390 double outerRadius = centralRadius + coronasThickness / 2.0;
0391
0392
0393
0394 if (i == numCrowns - 1) {
0395 double safetyMargin = 0.05 * dd4hep::cm;
0396 outerRadius -= safetyMargin;
0397 }
0398
0399 rMinBottom = innerRadius;
0400 rMinTop = innerRadius;
0401 rMaxBottom = outerRadius;
0402 rMaxTop = outerRadius;
0403 }
0404 innerRadiusBottoms_half.push_back(rMinBottom);
0405 innerRadiusTops_half.push_back(rMinTop);
0406 outerRadiusBottoms_half.push_back(rMaxBottom);
0407 outerRadiusTops_half.push_back(rMaxTop);
0408
0409 Cone crownSolid(crownHeight / 2.0, rMinBottom, rMaxBottom, rMinTop, rMaxTop);
0410 std::string crownName = "CarbonCrown_" + std::to_string(i);
0411 Volume crownVol(crownName, crownSolid, coronasMat);
0412 crownVol.setVisAttributes(coronasVis);
0413
0414
0415 aerogelVol.placeVolume(crownVol, Position(0., 0., 0.));
0416 }
0417
0418
0419 for (int i = 0; i < numCrowns - 1; i++) {
0420 int N = numSegments[i];
0421
0422 double rMin_Zminus = outerRadiusBottoms_half[i];
0423 double rMax_Zminus = innerRadiusBottoms_half[i + 1];
0424 double rMin_Zplus = outerRadiusTops_half[i];
0425 double rMax_Zplus = innerRadiusTops_half[i + 1];
0426
0427 double segmentSpacing = 2 * M_PI / N;
0428 double segmentAngularWidth = coronasThickness / rMin_Zminus;
0429
0430 for (int p = 0; p < N; p++) {
0431 double phiStart = p * segmentSpacing;
0432 double phiEnd = phiStart + segmentAngularWidth;
0433
0434 ConeSegment segmentSolid(crownHeight / 2.0, rMin_Zminus, rMax_Zminus, rMin_Zplus,
0435 rMax_Zplus, phiStart, phiEnd);
0436 std::string segName = "CarbonSegment_" + std::to_string(i) + "_" + std::to_string(p);
0437 Volume segVol(segName, segmentSolid, coronasMat);
0438 segVol.setVisAttributes(coronasVis);
0439
0440
0441 aerogelVol.placeVolume(segVol, Position(0., 0., 0.));
0442 }
0443 }
0444 }
0445
0446 else if (segmentationType == "square") {
0447 printout(WARNING, "DRICH_geo", "Square segmentation requested but not implemented yet.");
0448 }
0449
0450 Cone airgapSolid(airgapThickness / 2.0,
0451 radiatorRmin + boreDelta * structureThickness / vesselLength, radiatorRmax,
0452 radiatorRmin + boreDelta * (structureThickness + airgapThickness) / vesselLength,
0453 radiatorRmax);
0454 Cone filterSolid(filterThickness / 2.0,
0455 radiatorRmin + boreDelta * (structureThickness + airgapThickness) / vesselLength,
0456 radiatorRmax,
0457 radiatorRmin + boreDelta *
0458 (structureThickness + airgapThickness + filterThickness) /
0459 vesselLength,
0460 radiatorRmax);
0461
0462 Volume airgapVol(detName + "_airgap", airgapSolid, airgapMat);
0463 Volume filterVol(detName + "_filter", filterSolid, filterMat);
0464 airgapVol.setVisAttributes(airgapVis);
0465 filterVol.setVisAttributes(filterVis);
0466
0467
0468 if (!debugOptics) {
0469
0470 auto airgapPlacement =
0471 Translation3D(radiatorPos) *
0472 RotationY(radiatorPitch) *
0473 Translation3D(0., 0.,
0474 (structureThickness + airgapThickness) / 2.);
0475 auto airgapPV = gasvolVol.placeVolume(airgapVol, airgapPlacement);
0476 DetElement airgapDE(det, "airgap_de", 0);
0477 airgapDE.setPlacement(airgapPV);
0478
0479 auto filterPlacement =
0480 Translation3D(0., 0., airgapThickness) *
0481 Translation3D(radiatorPos) *
0482 RotationY(radiatorPitch) *
0483 Translation3D(0., 0.,
0484 (structureThickness + filterThickness) / 2.);
0485 auto filterPV = gasvolVol.placeVolume(filterVol, filterPlacement);
0486 DetElement filterDE(det, "filter_de", 0);
0487 filterDE.setPlacement(filterPV);
0488
0489 #if defined(WITH_IRT2_SUPPORT) || defined(WITH_IRT1_SUPPORT)
0490
0491 double aerogelZpos = vesselPos.z() + aerogelPV.position().z();
0492 double filterZpos = vesselPos.z() + filterPV.position().z();
0493 #endif
0494
0495 #ifdef WITH_IRT1_SUPPORT
0496 double airgapZpos = vesselPos.z() + airgapPV.position().z();
0497 desc.add(Constant("DRICH_aerogel_zpos", std::to_string(aerogelZpos)));
0498 desc.add(Constant("DRICH_airgap_zpos", std::to_string(airgapZpos)));
0499 desc.add(Constant("DRICH_filter_zpos", std::to_string(filterZpos)));
0500 #endif
0501
0502 #ifdef WITH_IRT2_SUPPORT
0503 {
0504 TVector3 nx(1, 0, 0), ny(0, -1, 0);
0505
0506 for (int isec = 0; isec < nSectors; isec++) {
0507 {
0508
0509 auto boundary = new FlatSurface(TVector3(0, 0, 0), nx, ny);
0510
0511 auto radiator = geometry->SetContainerVolume(cdet, "GasVolume", isec,
0512 (G4LogicalVolume*)(0x0), 0, boundary);
0513 radiator->SetAlternativeMaterialName(gasvolMatName.c_str());
0514 }
0515
0516 {
0517 auto surface = new FlatSurface((1 / mm) * TVector3(0, 0, aerogelZpos), nx, ny);
0518
0519 auto radiator =
0520 geometry->AddFlatRadiator(cdet, "Aerogel", CherenkovDetector::Upstream, isec,
0521 (G4LogicalVolume*)(0x1), 0, surface, aerogelThickness / mm);
0522 radiator->SetAlternativeMaterialName(aerogelMatName.c_str());
0523 }
0524
0525 {
0526 auto surface = new FlatSurface((1 / mm) * TVector3(0, 0, filterZpos), nx, ny);
0527
0528 auto radiator =
0529 geometry->AddFlatRadiator(cdet, "Acrylic", CherenkovDetector::Upstream, isec,
0530 (G4LogicalVolume*)(0x2), 0, surface, filterThickness / mm);
0531 radiator->SetAlternativeMaterialName(filterMatName.c_str());
0532 }
0533 }
0534 }
0535 #endif
0536 }
0537
0538 #ifdef WITH_IRT1_SUPPORT
0539
0540 desc.add(Constant("DRICH_aerogel_material", aerogelMat.ptr()->GetName(), "string"));
0541 desc.add(Constant("DRICH_airgap_material", airgapMat.ptr()->GetName(), "string"));
0542 desc.add(Constant("DRICH_filter_material", filterMat.ptr()->GetName(), "string"));
0543 desc.add(Constant("DRICH_gasvol_material", gasvolMat.ptr()->GetName(), "string"));
0544 #endif
0545 #ifdef WITH_IRT2_SUPPORT
0546
0547 auto pd = new IRT2::CherenkovPhotonDetector(0, 0);
0548
0549
0550 geometry->AddPhotonDetector(cdet, 0, pd);
0551
0552
0553 pd->SetActiveAreaSize(pssSide / mm);
0554
0555
0556 pd->SetGeometricEfficiency(0.74);
0557 #endif
0558
0559
0560 for (int isec = 0; isec < nSectors; isec++) {
0561
0562 if (debugSector && isec != 0)
0563 continue;
0564
0565
0566 RotationZ sectorRotation((isec + 0.5) * 2 * M_PI / nSectors);
0567 std::string secName = "sec" + std::to_string(isec);
0568
0569
0570
0571
0572
0573 double zS = sensorSphCenterZ + vesselZmin;
0574 double xS = sensorSphCenterX;
0575
0576 double b = vesselZmax - mirrorBackplane;
0577
0578 double zF = zS + focusTuneZ;
0579 double xF = xS + focusTuneX;
0580
0581
0582
0583
0584
0585
0586
0587
0588 double mirrorCenterZ = b * zF / (2 * b - zF);
0589 double mirrorCenterX = b * xF / (2 * b - zF);
0590 double mirrorRadius = b - mirrorCenterZ;
0591
0592
0593 mirrorCenterZ -= vesselZmin;
0594
0595
0596 double mirrorThetaRot = std::asin(mirrorCenterX / mirrorRadius);
0597 double mirrorTheta1 = mirrorThetaRot - std::asin((mirrorCenterX - mirrorRmin) / mirrorRadius);
0598 double mirrorTheta2 = mirrorThetaRot + std::asin((mirrorRmax - mirrorCenterX) / mirrorRadius);
0599
0600
0601 if (debugMirror) {
0602 mirrorTheta1 = 0;
0603 mirrorTheta2 = M_PI;
0604 }
0605
0606
0607
0608 Sphere mirrorSolid1(mirrorRadius, mirrorRadius + mirrorThickness, mirrorTheta1, mirrorTheta2,
0609 -40 * degree, 40 * degree);
0610
0611
0612 auto mirrorPos = Position(mirrorCenterX, 0., mirrorCenterZ) + originFront;
0613 auto mirrorPlacement(
0614 Translation3D(mirrorPos) *
0615 RotationY(-mirrorThetaRot)
0616 );
0617
0618
0619
0620 Tube pieSlice(0.01 * cm, vesselRmax2, tankLength / 2.0, -mirrorPhiw / 2.0, mirrorPhiw / 2.0);
0621 IntersectionSolid mirrorSolid2(pieSlice, mirrorSolid1, mirrorPlacement);
0622
0623
0624 Volume mirrorVol(detName + "_mirror_" + secName, mirrorSolid2, mirrorMat);
0625 mirrorVol.setVisAttributes(mirrorVis);
0626 auto mirrorSectorPlacement = Transform3D(sectorRotation);
0627 auto mirrorPV = gasvolVol.placeVolume(mirrorVol, mirrorSectorPlacement);
0628
0629
0630 DetElement mirrorDE(det, "mirror_de_" + secName, isec);
0631 mirrorDE.setPlacement(mirrorPV);
0632 SkinSurface mirrorSkin(desc, mirrorDE, "mirror_optical_surface_" + secName, mirrorSurf,
0633 mirrorVol);
0634 mirrorSkin.isValid();
0635
0636 #if defined(WITH_IRT2_SUPPORT) || defined(WITH_IRT1_SUPPORT)
0637
0638
0639 auto mirrorFinalPlacement = mirrorSectorPlacement * mirrorPlacement;
0640 auto mirrorFinalCenter = vesselPos + mirrorFinalPlacement.Translation().Vect();
0641 #endif
0642
0643 #ifdef WITH_IRT2_SUPPORT
0644 {
0645
0646 msurface = new SphericalSurface(
0647 (1 / mm) * TVector3(mirrorFinalCenter.x(), mirrorFinalCenter.y(), mirrorFinalCenter.z()),
0648 (1 / mm) * mirrorRadius);
0649 mboundary = new IRT2::OpticalBoundary(cdet->GetRadiator("GasVolume"), msurface, false);
0650
0651 cdet->StoreOpticalBoundary(mboundary);
0652
0653
0654 cdet->GetContainerVolume()->m_Borders[isec].second = msurface;
0655 }
0656 #endif
0657 #ifdef WITH_IRT1_SUPPORT
0658 desc.add(Constant("DRICH_mirror_center_x_" + secName, std::to_string(mirrorFinalCenter.x())));
0659 desc.add(Constant("DRICH_mirror_center_y_" + secName, std::to_string(mirrorFinalCenter.y())));
0660 desc.add(Constant("DRICH_mirror_center_z_" + secName, std::to_string(mirrorFinalCenter.z())));
0661 if (isec == 0)
0662 desc.add(Constant("DRICH_mirror_radius", std::to_string(mirrorRadius)));
0663 #endif
0664
0665
0666
0667
0668 if (debugSensors) {
0669 pssSide = 2 * M_PI * sensorSphRadius / 64;
0670 }
0671
0672
0673 auto sensorSphPos = Position(sensorSphCenterX, 0., sensorSphCenterZ) + originFront;
0674 auto sensorSphFinalCenter = sectorRotation * Position(xS, 0.0, zS);
0675 #ifdef WITH_IRT1_SUPPORT
0676 desc.add(
0677 Constant("DRICH_sensor_sph_center_x_" + secName, std::to_string(sensorSphFinalCenter.x())));
0678 desc.add(
0679 Constant("DRICH_sensor_sph_center_y_" + secName, std::to_string(sensorSphFinalCenter.y())));
0680 desc.add(
0681 Constant("DRICH_sensor_sph_center_z_" + secName, std::to_string(sensorSphFinalCenter.z())));
0682 if (isec == 0)
0683 desc.add(Constant("DRICH_sensor_sph_radius", std::to_string(sensorSphRadius)));
0684 #endif
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695
0696
0697
0698
0699
0700
0701
0702
0703
0704
0705
0706 int ipdu = 0;
0707
0708
0709 double pduPitch = pduNumSensors * resinSide + (pduNumSensors + 1) * pduSensorGap + pduGap;
0710
0711
0712
0713
0714
0715
0716
0717
0718
0719
0720
0721
0722
0723
0724
0725
0726
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736
0737
0738
0739
0740
0741 Box pssSolid(pssSide / 2., pssSide / 2., pssThickness / 2.);
0742 Box resinSolid(resinSide / 2., resinSide / 2., resinThickness / 2.);
0743
0744
0745 SubtractionSolid resinSolidEmbedded(
0746 resinSolid, pssSolid,
0747 Transform3D(Translation3D(0., 0., (resinThickness - pssThickness) / 2.)));
0748
0749
0750
0751
0752
0753
0754
0755
0756
0757
0758
0759
0760
0761
0762 Volume pssVol(detName + "_pss_" + secName, pssSolid, pssMat);
0763 Volume resinVol(detName + "_resin_" + secName, resinSolidEmbedded, resinMat);
0764 pssVol.setVisAttributes(pssVis);
0765 resinVol.setVisAttributes(resinVis);
0766
0767
0768 if (!debugOptics || debugOpticsMode == 3)
0769 pssVol.setSensitiveDetector(sens);
0770
0771
0772 if (!debugOptics || debugOpticsMode == 3) {
0773 SkinSurface pssSkin(desc, det, "sensor_optical_surface_" + secName, pssSurf, pssVol);
0774 pssSkin.isValid();
0775 }
0776
0777
0778 double pduSensorPitch = resinSide + pduSensorGap;
0779 double pduSensorOffsetMax = pduSensorPitch * (pduNumSensors - 1) / 2.0;
0780
0781
0782
0783 auto pssPlacement = Transform3D(Translation3D(0., 0., -pssThickness / 2.0));
0784 auto resinPlacement = Transform3D(Translation3D(0., 0., -resinThickness / 2.0));
0785
0786
0787 Assembly pduAssembly(detName + "_pdu_" + secName);
0788
0789
0790
0791 std::vector<PlacedVolume> sensorPVs;
0792 for (int sensorIx = 0; sensorIx < pduNumSensors; sensorIx++) {
0793 for (int sensorIy = 0; sensorIy < pduNumSensors; sensorIy++) {
0794 int isipm = sensorIx * pduNumSensors + sensorIy;
0795 double pduSensorOffsetX = sensorIx * pduSensorPitch - pduSensorOffsetMax;
0796 double pduSensorOffsetY = sensorIy * pduSensorPitch - pduSensorOffsetMax;
0797 Assembly sensorAssembly(detName + "_sensor_" + secName + "_" + std::to_string(isipm));
0798 auto pssPV = sensorAssembly.placeVolume(pssVol, pssPlacement);
0799 sensorAssembly.placeVolume(resinVol, resinPlacement);
0800 pssPV.addPhysVolID("sipm", isipm);
0801 pduAssembly.placeVolume(
0802 sensorAssembly, Transform3D(Translation3D(pduSensorOffsetX, pduSensorOffsetY, 0.0)));
0803 sensorPVs.push_back(pssPV);
0804 }
0805 }
0806
0807
0808 Transform3D frontServiceTransformation = Transform3D(Translation3D(0., 0., -resinThickness));
0809 for (xml::Collection_t serviceElem(pduElem.child(_Unicode(frontservices)), _Unicode(service));
0810 serviceElem; ++serviceElem) {
0811 auto serviceName = serviceElem.attr<std::string>(_Unicode(name));
0812 auto serviceSide = serviceElem.attr<double>(_Unicode(side));
0813 auto serviceThickness = serviceElem.attr<double>(_Unicode(thickness));
0814 auto serviceMat = desc.material(serviceElem.attr<std::string>(_Unicode(material)));
0815 auto serviceVis = desc.visAttributes(serviceElem.attr<std::string>(_Unicode(vis)));
0816 Box serviceSolid(serviceSide / 2.0, serviceSide / 2.0, serviceThickness / 2.0);
0817 Volume serviceVol(detName + "_" + serviceName + "_" + secName, serviceSolid, serviceMat);
0818 serviceVol.setVisAttributes(serviceVis);
0819 frontServiceTransformation =
0820 Transform3D(Translation3D(0., 0., -serviceThickness / 2.0)) * frontServiceTransformation;
0821 pduAssembly.placeVolume(serviceVol, frontServiceTransformation);
0822 frontServiceTransformation =
0823 Transform3D(Translation3D(0., 0., -serviceThickness / 2.0)) * frontServiceTransformation;
0824 }
0825
0826
0827 auto boardsElem = pduElem.child(_Unicode(boards));
0828 auto boardsMat = desc.material(boardsElem.attr<std::string>(_Unicode(material)));
0829 auto boardsVis = desc.visAttributes(boardsElem.attr<std::string>(_Unicode(vis)));
0830 Transform3D backServiceTransformation;
0831 for (xml::Collection_t boardElem(boardsElem, _Unicode(board)); boardElem; ++boardElem) {
0832 auto boardName = boardElem.attr<std::string>(_Unicode(name));
0833 auto boardWidth = boardElem.attr<double>(_Unicode(width));
0834 auto boardLength = boardElem.attr<double>(_Unicode(length));
0835 auto boardThickness = boardElem.attr<double>(_Unicode(thickness));
0836 auto boardOffset = boardElem.attr<double>(_Unicode(offset));
0837 Box boardSolid(boardWidth / 2.0, boardThickness / 2.0, boardLength / 2.0);
0838 Volume boardVol(detName + "_" + boardName + "+" + secName, boardSolid, boardsMat);
0839 boardVol.setVisAttributes(boardsVis);
0840 auto boardTransformation =
0841 Translation3D(0., boardOffset, -boardLength / 2.0) * frontServiceTransformation;
0842 pduAssembly.placeVolume(boardVol, boardTransformation);
0843 if (boardName == "RDO")
0844 backServiceTransformation =
0845 Translation3D(0., 0., -boardLength) * frontServiceTransformation;
0846 }
0847
0848
0849 for (xml::Collection_t serviceElem(pduElem.child(_Unicode(backservices)), _Unicode(service));
0850 serviceElem; ++serviceElem) {
0851 auto serviceName = serviceElem.attr<std::string>(_Unicode(name));
0852 auto serviceSide = serviceElem.attr<double>(_Unicode(side));
0853 auto serviceThickness = serviceElem.attr<double>(_Unicode(thickness));
0854 auto serviceMat = desc.material(serviceElem.attr<std::string>(_Unicode(material)));
0855 auto serviceVis = desc.visAttributes(serviceElem.attr<std::string>(_Unicode(vis)));
0856 Box serviceSolid(serviceSide / 2.0, serviceSide / 2.0, serviceThickness / 2.0);
0857 Volume serviceVol(detName + "_" + serviceName + "_" + secName, serviceSolid, serviceMat);
0858 serviceVol.setVisAttributes(serviceVis);
0859 backServiceTransformation =
0860 Transform3D(Translation3D(0., 0., -serviceThickness / 2.0)) * backServiceTransformation;
0861 pduAssembly.placeVolume(serviceVol, backServiceTransformation);
0862 backServiceTransformation =
0863 Transform3D(Translation3D(0., 0., -serviceThickness / 2.0)) * backServiceTransformation;
0864 }
0865
0866
0867
0868
0869 double nTheta = M_PI * sensorSphRadius / pduPitch;
0870 for (int t = 0; t < (int)(nTheta + 0.5); t++) {
0871 double thetaGen = t / ((double)nTheta) * M_PI;
0872
0873
0874 double nPhi = 2 * M_PI * sensorSphRadius * std::sin(thetaGen) / pduPitch;
0875 for (int p = 0; p < (int)(nPhi + 0.5); p++) {
0876 double phiGen = p / ((double)nPhi) * 2 * M_PI - M_PI;
0877
0878
0879
0880 double xGen = sensorSphRadius * std::sin(thetaGen) * std::cos(phiGen);
0881 double yGen = sensorSphRadius * std::sin(thetaGen) * std::sin(phiGen);
0882 double zGen = sensorSphRadius * std::cos(thetaGen);
0883
0884 double x = zGen;
0885 double y = xGen;
0886 double z = yGen;
0887
0888
0889 double zCheck = z + sensorSphCenterZ;
0890 double xCheck = x + sensorSphCenterX;
0891 double yCheck = y;
0892 double rCheck = std::hypot(xCheck, yCheck);
0893 double phiCheck = std::atan2(yCheck, xCheck);
0894
0895
0896 bool patchCut = std::fabs(phiCheck) < sensorSphPatchPhiw && zCheck > sensorSphPatchZmin &&
0897 rCheck > sensorSphPatchRmin && rCheck < sensorSphPatchRmax;
0898 if (debugSensors)
0899 patchCut = std::fabs(phiCheck) < sensorSphPatchPhiw;
0900 if (patchCut) {
0901
0902
0903
0904
0905
0906
0907
0908
0909
0910 auto pduAssemblyPlacement =
0911 sectorRotation *
0912 Translation3D(sensorSphPos) *
0913 RotationX(phiGen) *
0914 RotationZ(thetaGen) *
0915 Translation3D(sensorSphRadius, 0., 0.) *
0916 RotationY(M_PI / 2) *
0917 RotationZ(-M_PI / 2);
0918
0919
0920
0921 auto pduPV = gasvolVol.placeVolume(pduAssembly, pduAssemblyPlacement);
0922 pduPV.addPhysVolID("sector", isec).addPhysVolID("pdu", ipdu);
0923
0924
0925
0926 int isipm = 0;
0927 for (int sensorIx = 0; sensorIx < pduNumSensors; sensorIx++) {
0928 for (int sensorIy = 0; sensorIy < pduNumSensors; sensorIy++) {
0929
0930 auto pduSensorOffsetX = sensorIx * pduSensorPitch - pduSensorOffsetMax;
0931 auto pduSensorOffsetY = sensorIy * pduSensorPitch - pduSensorOffsetMax;
0932 auto sensorAssemblyPlacement =
0933 Transform3D(Translation3D(pduSensorOffsetX, pduSensorOffsetY, 0.0));
0934
0935
0936
0937 auto sensorID = encodeSensorID(std::vector<std::pair<std::string, int>>{
0938 {"sector", isec}, {"pdu", ipdu}, {"sipm", isipm}});
0939
0940 std::string sensorIDname =
0941 secName + "_pdu" + std::to_string(ipdu) + "_sipm" + std::to_string(isipm);
0942 DetElement pssDE(det, "sensor_de_" + sensorIDname, sensorID);
0943
0944
0945
0946
0947 pssDE.setPlacement(pduPV);
0948
0949
0950
0951 auto pduOrigin = ROOT::Math::XYZPoint(0, 0, 0);
0952 auto sensorPos = Translation3D(vesselPos) *
0953 pduAssemblyPlacement *
0954 sensorAssemblyPlacement *
0955 pduOrigin;
0956 auto pduPos = Translation3D(vesselPos) *
0957 pduAssemblyPlacement *
0958 pduOrigin;
0959
0960
0961
0962 auto normVector = [pduAssemblyPlacement](Direction n) {
0963 return pduAssemblyPlacement * n;
0964 };
0965 auto sensorNormX = normVector(Direction{
0966 -1.,
0967 0.,
0968 0.,
0969 });
0970 auto sensorNormY = normVector(Direction{
0971 0.,
0972 -1.,
0973 0.,
0974 });
0975
0976
0977
0978
0979
0980
0981
0982
0983 auto distActual = std::sqrt((sensorPos - sensorSphFinalCenter).Mag2());
0984 auto distExpected = std::hypot(pduSensorOffsetX, pduSensorOffsetY, sensorSphRadius);
0985 auto testOnSphere = distActual - distExpected;
0986 if (std::abs(testOnSphere) > 1e-6) {
0987 printout(ERROR, "DRICH_geo", "sensor %s failed on-sphere test; testOnSphere=%f",
0988 sensorIDname.c_str(), testOnSphere);
0989 throw std::runtime_error("dRICH sensor position test failed");
0990 }
0991
0992 Direction radialDir =
0993 Direction(pduPos) - sensorSphFinalCenter;
0994 auto sensorNormZ = sensorNormX.Cross(sensorNormY);
0995 auto testOrtho =
0996 sensorNormX.Dot(sensorNormY);
0997 auto testRadial =
0998 radialDir.Cross(sensorNormZ)
0999 .Mag2();
1000 auto testDirection = radialDir.Dot(
1001 sensorNormZ);
1002 if (std::abs(testOrtho) > 1e-6 || std::abs(testRadial) > 1e-6 || testDirection <= 0) {
1003 printout(ERROR, "DRICH_geo", "sensor %s failed orientation test",
1004 sensorIDname.c_str());
1005 printout(ERROR, "DRICH_geo", " testOrtho = %f; should be zero", testOrtho);
1006 printout(ERROR, "DRICH_geo", " testRadial = %f; should be zero", testRadial);
1007 printout(ERROR, "DRICH_geo", " testDirection = %f; should be positive",
1008 testDirection);
1009 throw std::runtime_error("dRICH sensor orientation test failed");
1010 }
1011
1012 #ifdef WITH_IRT2_SUPPORT
1013 {
1014
1015 auto surface = new FlatSurface(
1016 (1 / mm) * TVector3(sensorPos.x(), sensorPos.y(), sensorPos.z()),
1017 TVector3(sensorNormX.x(), sensorNormX.y(), sensorNormX.z()),
1018 TVector3(sensorNormY.x(), sensorNormY.y(), sensorNormY.z()));
1019
1020
1021 auto irt = pd->AllocateIRT(isec, sensorID & sector_mask);
1022
1023
1024 if (cdet->m_OpticalBoundaries[CherenkovDetector::Upstream].find(isec) !=
1025 cdet->m_OpticalBoundaries[CherenkovDetector::Upstream].end())
1026 for (auto boundary : cdet->m_OpticalBoundaries[CherenkovDetector::Upstream][isec])
1027 irt->AddOpticalBoundary(boundary);
1028
1029
1030 irt->AddOpticalBoundary(mboundary);
1031
1032
1033
1034
1035
1036 pd->AddItselfToOpticalBoundaries(irt, surface);
1037 }
1038 #endif
1039 #ifdef WITH_IRT1_SUPPORT
1040
1041 auto pssVarMap = pssDE.extension<VariantParameters>(false);
1042 if (pssVarMap == nullptr) {
1043 pssVarMap = new VariantParameters();
1044 pssDE.addExtension<VariantParameters>(pssVarMap);
1045 }
1046
1047 auto addVecToMap = [pssVarMap](std::string key, auto vec) {
1048 pssVarMap->set<double>(key + "_x", vec.x());
1049 pssVarMap->set<double>(key + "_y", vec.y());
1050 pssVarMap->set<double>(key + "_z", vec.z());
1051 };
1052 addVecToMap("pos", sensorPos);
1053 addVecToMap("normX", sensorNormX);
1054 addVecToMap("normY", sensorNormY);
1055 printout(DEBUG, "DRICH_geo", "sensor %s:", sensorIDname.c_str());
1056 for (auto kv : pssVarMap->variantParameters)
1057 printout(DEBUG, "DRICH_geo", " %s: %f", kv.first.c_str(),
1058 pssVarMap->get<double>(kv.first));
1059 #endif
1060 printout(DEBUG, "DRICH_geo", "sensor %s:", sensorIDname.c_str());
1061
1062
1063 isipm++;
1064 }
1065 }
1066
1067
1068 ipdu++;
1069
1070 }
1071 }
1072 }
1073
1074
1075
1076 #ifdef WITH_IRT1_SUPPORT
1077
1078 if (isec == 0)
1079 desc.add(Constant("DRICH_num_pdus", std::to_string(ipdu)));
1080 else if (ipdu != desc.constant<int>("DRICH_num_pdus"))
1081 printout(WARNING, "DRICH_geo", "number of PDUs is not the same for each sector");
1082 #endif
1083
1084 }
1085
1086 return det;
1087 }
1088
1089
1090 DECLARE_DETELEMENT(epic_DRICH, createDetector)