File indexing completed on 2025-12-17 09:22:11
0001 #include "PHG4TpcDirectLaser.h"
0002
0003 #include <phparameter/PHParameterInterface.h> // for PHParameterInterface
0004
0005 #include <g4main/PHG4HitContainer.h>
0006 #include <g4main/PHG4HitDefs.h> // for get_volume_id
0007 #include <g4main/PHG4Hitv1.h>
0008 #include <g4main/PHG4Particlev3.h>
0009 #include <g4main/PHG4TruthInfoContainer.h>
0010 #include <g4main/PHG4VtxPointv1.h>
0011
0012 #include <fun4all/Fun4AllReturnCodes.h>
0013 #include <fun4all/SubsysReco.h> // for SubsysReco
0014
0015 #include <phool/PHCompositeNode.h>
0016 #include <phool/PHIODataNode.h>
0017 #include <phool/PHNode.h>
0018 #include <phool/PHNodeIterator.h>
0019 #include <phool/PHObject.h> // for PHObject
0020 #include <phool/getClass.h>
0021 #include <phool/phool.h> // for PHWHERE
0022
0023 #include <g4detectors/PHG4TpcGeom.h>
0024 #include <g4detectors/PHG4TpcGeomContainer.h>
0025
0026 #include <trackbase_historic/SvtxTrackMap.h>
0027 #include <trackbase_historic/SvtxTrackMap_v2.h>
0028 #include <trackbase_historic/SvtxTrack_v2.h>
0029
0030 #include <TFile.h>
0031 #include <TNtuple.h>
0032 #include <TVector3.h> // for TVector3, operator*
0033
0034 #include <gsl/gsl_const_mksa.h> // for the speed of light
0035
0036 #include <cassert>
0037 #include <iostream> // for operator<<, basic_os...
0038 #include <optional>
0039
0040 namespace
0041 {
0042 using PHG4Particle_t = PHG4Particlev3;
0043 using PHG4VtxPoint_t = PHG4VtxPointv1;
0044 using PHG4Hit_t = PHG4Hitv1;
0045
0046
0047 template <class T>
0048 constexpr T square(const T& x)
0049 {
0050 return x * x;
0051 }
0052
0053
0054 const int detId = PHG4HitDefs::get_volume_id("PHG4TpcDirectLaser");
0055
0056
0057
0058 constexpr double cm = 1.0;
0059
0060
0061
0062 constexpr double speed_of_light = GSL_CONST_MKSA_SPEED_OF_LIGHT * 1e-7;
0063
0064
0065 constexpr double maxHitLength = 1. * cm;
0066
0067
0068 constexpr double begin_CM = 20. * cm;
0069 constexpr double end_CM = 78. * cm;
0070
0071
0072 std::optional<TVector3> central_membrane_intersection(const TVector3& start, const TVector3& direction, double halfwidth_CM)
0073 {
0074 const double end = start.z() > 0 ? halfwidth_CM : -halfwidth_CM;
0075 const double dist = end - start.z();
0076
0077
0078 if (direction.z() == 0)
0079 {
0080 return std::nullopt;
0081 }
0082
0083
0084 if (dist * direction.z() < 0)
0085 {
0086 return std::nullopt;
0087 }
0088
0089 const double direction_scale = dist / direction.z();
0090 return start + direction * direction_scale;
0091 }
0092
0093
0094 std::optional<TVector3> endcap_intersection(const TVector3& start, const TVector3& direction, double halflength_tpc)
0095 {
0096 const double end = start.z() > 0 ? halflength_tpc : -halflength_tpc;
0097 const double dist = end - start.z();
0098
0099
0100 if (direction.z() == 0)
0101 {
0102 return std::nullopt;
0103 }
0104
0105
0106 if (dist * direction.z() < 0)
0107 {
0108 return std::nullopt;
0109 }
0110
0111 const double direction_scale = dist / direction.z();
0112 return start + direction * direction_scale;
0113 }
0114
0115
0116 std::optional<TVector3> cylinder_line_intersection(const TVector3& s, const TVector3& v, double radius)
0117 {
0118 const double R2 = square(radius);
0119
0120
0121
0122 const double a = square(v.x()) + square(v.y());
0123 const double b = 2 * (v.x() * s.x() + v.y() * s.y());
0124 const double c = square(s.x()) + square(s.y()) - R2;
0125
0126 const double rootterm = square(b) - 4 * a * c;
0127
0128
0129
0130
0131
0132
0133 if (rootterm < 0 || a == 0)
0134 {
0135 return std::nullopt;
0136 }
0137
0138
0139 const double sqrtterm = std::sqrt(rootterm);
0140 const double t1 = (-b + sqrtterm) / (2 * a);
0141 const double t2 = (-b - sqrtterm) / (2 * a);
0142
0143
0144
0145
0146
0147 const double& min_t = (t2 < t1 && t2 > 0) ? t2 : t1;
0148 return s + v * min_t;
0149 }
0150
0151
0152 std::optional<TVector3> field_cage_intersection(const TVector3& start, const TVector3& direction)
0153 {
0154 auto ofc_strike = cylinder_line_intersection(start, direction, end_CM);
0155 auto ifc_strike = cylinder_line_intersection(start, direction, begin_CM);
0156
0157
0158 if (!ifc_strike)
0159 {
0160 return ofc_strike;
0161 }
0162 if (!ofc_strike)
0163 {
0164 return ifc_strike;
0165 }
0166
0167
0168 const auto ifc_dist = (ifc_strike->Z() - start.Z()) / direction.Z();
0169 const auto ofc_dist = (ofc_strike->Z() - start.Z()) / direction.Z();
0170
0171 if (ifc_dist < 0)
0172 {
0173 return (ofc_dist > 0) ? ofc_strike : std::nullopt;
0174 }
0175 if (ofc_dist < 0)
0176 {
0177 return ifc_strike;
0178 }
0179
0180 return (ifc_dist < ofc_dist) ? ifc_strike : ofc_strike;
0181 }
0182
0183
0184 inline std::ostream& operator<<(std::ostream& out, const TVector3& vector)
0185 {
0186 out << "( " << vector.x() << ", " << vector.y() << ", " << vector.z() << ")";
0187 return out;
0188 }
0189
0190 }
0191
0192
0193 PHG4TpcDirectLaser::PHG4TpcDirectLaser(const std::string& name)
0194 : SubsysReco(name)
0195 , PHParameterInterface(name)
0196 {
0197 InitializeParameters();
0198 }
0199
0200
0201 int PHG4TpcDirectLaser::InitRun(PHCompositeNode* topNode)
0202 {
0203
0204 m_g4truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
0205 if (!m_g4truthinfo)
0206 {
0207 std::cout << "Fun4AllDstPileupMerger::load_nodes - creating node G4TruthInfo" << std::endl;
0208
0209 PHNodeIterator iter(topNode);
0210 auto* dstNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
0211 if (!dstNode)
0212 {
0213 std::cout << PHWHERE << "DST Node missing, aborting." << std::endl;
0214 return Fun4AllReturnCodes::ABORTRUN;
0215 }
0216
0217 m_g4truthinfo = new PHG4TruthInfoContainer();
0218 dstNode->addNode(new PHIODataNode<PHObject>(m_g4truthinfo, "G4TruthInfo", "PHObject"));
0219 }
0220
0221
0222 hitnodename = "G4HIT_" + detector;
0223 auto* g4hit = findNode::getClass<PHG4HitContainer>(topNode, hitnodename);
0224 if (!g4hit)
0225 {
0226 std::cout << Name() << " Could not locate G4HIT node " << hitnodename << std::endl;
0227 return Fun4AllReturnCodes::ABORTRUN;
0228 }
0229
0230
0231
0232 m_track_map = findNode::getClass<SvtxTrackMap>(topNode, m_track_map_name);
0233 if (!m_track_map)
0234 {
0235
0236 PHNodeIterator iter(topNode);
0237 auto* dstNode = static_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
0238 if (!dstNode)
0239 {
0240 std::cout << PHWHERE << "DST Node missing, aborting." << std::endl;
0241 return Fun4AllReturnCodes::ABORTRUN;
0242 }
0243
0244
0245 iter = PHNodeIterator(dstNode);
0246 auto* node = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "SVTX"));
0247 if (!node)
0248 {
0249 dstNode->addNode(node = new PHCompositeNode("SVTX"));
0250 }
0251
0252
0253 m_track_map = new SvtxTrackMap_v2;
0254 node->addNode(new PHIODataNode<PHObject>(m_track_map, m_track_map_name, "PHObject"));
0255 }
0256
0257
0258 UpdateParametersWithMacro();
0259 electrons_per_cm = get_int_param("electrons_per_cm");
0260 electrons_per_gev = get_double_param("electrons_per_gev");
0261
0262 PHG4TpcGeomContainer *GeomContainer = findNode::getClass<PHG4TpcGeomContainer>(topNode, "TPCGEOMCONTAINER");
0263
0264 PHG4TpcGeom *layergeom = GeomContainer->GetLayerCellGeom(20);
0265 double maxdriftlength = layergeom->get_max_driftlength();
0266 halfwidth_CM = layergeom->get_CM_halfwidth();
0267 halflength_tpc = maxdriftlength + halfwidth_CM;
0268
0269
0270 SetupLasers();
0271
0272
0273 if (m_steppingpattern == true)
0274 {
0275 std::cout << "PHG4TpcDirectLaser::InitRun - m_steppingpattern: " << m_steppingpattern << std::endl;
0276 std::cout << "PHG4TpcDirectLaser::InitRun - nTotalSteps: " << nTotalSteps << std::endl;
0277 }
0278 else
0279 {
0280 std::cout << "PHG4TpcDirectLaser::InitRun - m_autoAdvanceDirectLaser: " << m_autoAdvanceDirectLaser << std::endl;
0281 std::cout << "PHG4TpcDirectLaser::InitRun - phi steps: " << nPhiSteps << " min: " << minPhi << " max: " << maxPhi << std::endl;
0282 std::cout << "PHG4TpcDirectLaser::InitRun - theta steps: " << nThetaSteps << " min: " << minTheta << " max: " << maxTheta << std::endl;
0283 std::cout << "PHG4TpcDirectLaser::InitRun - nTotalSteps: " << nTotalSteps << std::endl;
0284 }
0285 std::cout << "PHG4TpcDirectLaser::InitRun - electrons_per_cm: " << electrons_per_cm << std::endl;
0286 std::cout << "PHG4TpcDirectLaser::InitRun - electrons_per_gev " << electrons_per_gev << std::endl;
0287
0288
0289
0290 std::string LASER_ANGLES_ROOTFILE = std::string(getenv("CALIBRATIONROOT")) + "/TPC/DirectLaser/theta_phi_laser.root";
0291 TFile* infile1 = TFile::Open(LASER_ANGLES_ROOTFILE.c_str());
0292
0293 pattern = (TNtuple*) infile1->Get("angles");
0294 pattern->SetBranchAddress("#theta", &theta_p);
0295 pattern->SetBranchAddress("#phi", &phi_p);
0296
0297 return Fun4AllReturnCodes::EVENT_OK;
0298 }
0299
0300
0301 int PHG4TpcDirectLaser::process_event(PHCompositeNode* topNode)
0302 {
0303
0304 m_g4truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
0305 assert(m_g4truthinfo);
0306
0307
0308 m_g4hitcontainer = findNode::getClass<PHG4HitContainer>(topNode, hitnodename);
0309 assert(m_g4hitcontainer);
0310
0311
0312 m_track_map = findNode::getClass<SvtxTrackMap>(topNode, m_track_map_name);
0313 assert(m_track_map);
0314
0315 if (m_autoAdvanceDirectLaser || m_steppingpattern)
0316 {
0317 AimToNextPatternStep();
0318 }
0319
0320 else
0321 {
0322
0323 AimToThetaPhi(arbitrary_theta, arbitrary_phi);
0324 }
0325
0326 return Fun4AllReturnCodes::EVENT_OK;
0327 }
0328
0329
0330 void PHG4TpcDirectLaser::SetDefaultParameters()
0331 {
0332
0333
0334
0335
0336
0337
0338 static constexpr double Ne_dEdx = 1.56;
0339 static constexpr double CF4_dEdx = 7.00;
0340 static constexpr double Ne_NTotal = 43;
0341 static constexpr double CF4_NTotal = 100;
0342 static constexpr double Tpc_NTot = 0.5 * Ne_NTotal + 0.5 * CF4_NTotal;
0343 static constexpr double Tpc_dEdx = 0.5 * Ne_dEdx + 0.5 * CF4_dEdx;
0344 static constexpr double Tpc_ElectronsPerKeV = Tpc_NTot / Tpc_dEdx;
0345
0346
0347 set_default_double_param("electrons_per_gev", Tpc_ElectronsPerKeV * 1e6);
0348
0349
0350
0351
0352
0353 set_default_int_param("electrons_per_cm", 72);
0354 }
0355
0356
0357 void PHG4TpcDirectLaser::SetPhiStepping(int n, double min, double max)
0358 {
0359 if (n < 0 || max < min)
0360 {
0361 std::cout << PHWHERE << " - invalid" << std::endl;
0362 return;
0363 }
0364 nPhiSteps = n;
0365 minPhi = min;
0366 maxPhi = max;
0367 nTotalSteps = nThetaSteps * nPhiSteps;
0368 return;
0369 }
0370
0371 void PHG4TpcDirectLaser::SetThetaStepping(int n, double min, double max)
0372 {
0373 if (n < 0 || max < min)
0374 {
0375 std::cout << PHWHERE << " - invalid" << std::endl;
0376 return;
0377 }
0378 nThetaSteps = n;
0379 minTheta = min;
0380 maxTheta = max;
0381 nTotalSteps = nThetaSteps * nPhiSteps;
0382
0383 return;
0384 }
0385
0386
0387 void PHG4TpcDirectLaser::SetFileStepping(int n)
0388 {
0389 if (n < 0 || n > 13802)
0390 {
0391 std::cout << PHWHERE << " - invalid" << std::endl;
0392 return;
0393 }
0394 nTotalSteps = n;
0395
0396 return;
0397 }
0398
0399
0400
0401 void PHG4TpcDirectLaser::SetupLasers()
0402 {
0403
0404 m_lasers.clear();
0405
0406
0407 const TVector3 position_base(60 * cm, 0., halflength_tpc);
0408
0409
0410 for (int i = 0; i < 8; ++i)
0411 {
0412 Laser laser;
0413
0414
0415
0416
0417
0418
0419 laser.m_position = position_base;
0420 if (i < 4)
0421 {
0422 laser.m_position.SetZ(position_base.z());
0423 laser.m_direction = -1;
0424 laser.m_phi = M_PI / 2 * i - (15 * M_PI / 180);
0425 }
0426 else
0427 {
0428 laser.m_position.SetZ(-position_base.z());
0429 laser.m_direction = 1;
0430 laser.m_phi = M_PI / 2 * i + (15 * M_PI / 180);
0431 }
0432
0433
0434 laser.m_position.RotateZ(laser.m_phi);
0435
0436
0437 m_lasers.push_back(laser);
0438
0439
0440
0441 }
0442 }
0443
0444
0445 void PHG4TpcDirectLaser::AimToNextPatternStep()
0446 {
0447 if (nTotalSteps >= 1)
0448 {
0449 if (m_steppingpattern)
0450 {
0451 AimToPatternStep_File(currentPatternStep);
0452 ++currentPatternStep;
0453 }
0454 else
0455 {
0456 AimToPatternStep(currentPatternStep);
0457 ++currentPatternStep;
0458 }
0459 }
0460 }
0461
0462
0463 void PHG4TpcDirectLaser::AimToThetaPhi(double theta, double phi)
0464 {
0465 if (Verbosity())
0466 {
0467 std::cout << "PHG4TpcDirectLaser::AimToThetaPhi - theta: " << theta << " phi: " << phi << std::endl;
0468 }
0469
0470
0471 for (const auto& laser : m_lasers)
0472 {
0473 AppendLaserTrack(theta, phi, laser);
0474 }
0475 }
0476
0477
0478 void PHG4TpcDirectLaser::AimToPatternStep(int n)
0479 {
0480
0481 n = n % nTotalSteps;
0482
0483 if (Verbosity())
0484 {
0485 std::cout << "PHG4TpcDirectLaser::AimToPatternStep - step: " << n << "/" << nTotalSteps << std::endl;
0486 }
0487
0488
0489 currentPatternStep = n;
0490
0491
0492 const int thetaStep = n / nPhiSteps;
0493 const double theta = minTheta + thetaStep * (maxTheta - minTheta) / nThetaSteps;
0494
0495
0496 const int phiStep = n % nPhiSteps;
0497 const double phi = minPhi + phiStep * (maxPhi - minPhi) / nPhiSteps;
0498
0499
0500 AimToThetaPhi(theta, phi);
0501
0502 return;
0503 }
0504
0505
0506
0507 void PHG4TpcDirectLaser::AimToPatternStep_File(int n)
0508 {
0509
0510 n = n % nTotalSteps;
0511
0512 if (Verbosity())
0513 {
0514 std::cout << "PHG4TpcDirectLaser::AimToPatternStep_File - step: " << n << "/" << nTotalSteps << std::endl;
0515 }
0516
0517
0518 currentPatternStep = n;
0519
0520 pattern->GetEntry(n);
0521
0522
0523 std::cout << "From file, current entry = " << n << " Theta: " << theta_p << " Phi: " << phi_p << std::endl;
0524
0525 const double theta = theta_p * M_PI / 180.;
0526
0527
0528 const double phi = phi_p * M_PI / 180.;
0529
0530
0531 AimToThetaPhi(theta, phi);
0532
0533 return;
0534 }
0535
0536
0537
0538 void PHG4TpcDirectLaser::AppendLaserTrack(double theta, double phi, const PHG4TpcDirectLaser::Laser& laser)
0539 {
0540 if (!m_g4hitcontainer)
0541 {
0542 std::cout << PHWHERE << "invalid g4hit container. aborting" << std::endl;
0543 return;
0544 }
0545
0546
0547 const auto& pos = laser.m_position;
0548
0549
0550 const auto& direction = laser.m_direction;
0551 TVector3 dir(0, 0, direction);
0552
0553
0554 dir.RotateY(theta * direction);
0555
0556 if (laser.m_direction == -1)
0557 {
0558 dir.RotateZ(phi);
0559 }
0560 else
0561 {
0562 dir.RotateZ(-phi);
0563 }
0564
0565
0566 dir.RotateZ(laser.m_phi);
0567
0568
0569 if (Verbosity())
0570 {
0571 std::cout << "PHG4TpcDirectLaser::AppendLaserTrack - position: " << pos << " direction: " << dir << std::endl;
0572 }
0573
0574
0575 static constexpr double total_momentum = 1;
0576
0577
0578 int trackid = -1;
0579
0580
0581 if (m_g4truthinfo)
0582 {
0583
0584 const auto vtxid = m_g4truthinfo->maxvtxindex() + 1;
0585 auto* const vertex = new PHG4VtxPoint_t(pos.x(), pos.y(), pos.z(), 0, vtxid);
0586 m_g4truthinfo->AddVertex(vtxid, vertex);
0587
0588
0589 trackid = m_g4truthinfo->maxtrkindex() + 1;
0590
0591
0592 auto* particle = new PHG4Particle_t();
0593 particle->set_track_id(trackid);
0594 particle->set_vtx_id(vtxid);
0595 particle->set_parent_id(0);
0596 particle->set_primary_id(trackid);
0597 particle->set_px(total_momentum * dir.x());
0598 particle->set_py(total_momentum * dir.y());
0599 particle->set_pz(total_momentum * dir.z());
0600
0601 m_g4truthinfo->AddParticle(trackid, particle);
0602 }
0603
0604
0605 if (m_track_map)
0606 {
0607 SvtxTrack_v2 track;
0608 track.set_x(pos.x());
0609 track.set_y(pos.y());
0610 track.set_z(pos.z());
0611
0612
0613 track.set_px(total_momentum * dir.x());
0614 track.set_py(total_momentum * dir.y());
0615 track.set_pz(total_momentum * dir.z());
0616
0617
0618 m_track_map->insert(&track);
0619
0620 if (Verbosity())
0621 {
0622 std::cout << "PHG4TpcDirectLaser::AppendLaserTrack - position: " << pos << " direction: " << dir << std::endl;
0623 }
0624 }
0625
0626
0627
0628
0629
0630
0631
0632 const auto plane_strike = (pos.z() * dir.z() > 0) ? endcap_intersection(pos, dir,halflength_tpc) : central_membrane_intersection(pos, dir, halfwidth_CM);
0633
0634
0635 const auto fc_strike = field_cage_intersection(pos, dir);
0636
0637
0638 if (!(plane_strike || fc_strike))
0639 {
0640 return;
0641 }
0642
0643
0644
0645 const TVector3& strike = (fc_strike && (!plane_strike || fc_strike->z() / dir.z() < plane_strike->z() / dir.z())) ? *fc_strike : *plane_strike;
0646
0647
0648 TVector3 delta = (strike - pos);
0649 double fullLength = delta.Mag();
0650 int nHitSteps = fullLength / maxHitLength + 1;
0651
0652 TVector3 start = pos;
0653 TVector3 end = start;
0654 TVector3 step = dir * (maxHitLength / (dir.Mag()));
0655 double stepLength = 0;
0656
0657 if (Verbosity())
0658 {
0659 std::cout << "PHG4TpcDirectLaser::AppendLaserTrack -"
0660 << " fullLength: " << fullLength
0661 << " nHitSteps: " << nHitSteps
0662 << std::endl;
0663 }
0664
0665 for (int i = 0; i < nHitSteps; i++)
0666 {
0667 start = end;
0668 if (i + 1 == nHitSteps)
0669 {
0670
0671 end = strike;
0672 delta = start - end;
0673 stepLength = delta.Mag();
0674 }
0675 else
0676 {
0677
0678 end = start + step;
0679 stepLength = step.Mag();
0680 }
0681
0682
0683 auto* hit = new PHG4Hit_t;
0684 hit->set_trkid(trackid);
0685 hit->set_layer(99);
0686
0687
0688 hit->set_x(0, start.X() / cm);
0689 hit->set_y(0, start.Y() / cm);
0690 hit->set_z(0, start.Z() / cm);
0691 hit->set_t(0, (start - pos).Mag() / speed_of_light);
0692
0693 hit->set_x(1, end.X() / cm);
0694 hit->set_y(1, end.Y() / cm);
0695 hit->set_z(1, end.Z() / cm);
0696 hit->set_t(1, (end - pos).Mag() / speed_of_light);
0697
0698
0699 hit->set_px(0, dir.X());
0700 hit->set_py(0, dir.Y());
0701 hit->set_pz(0, dir.Z());
0702
0703 hit->set_px(1, dir.X());
0704 hit->set_py(1, dir.Y());
0705 hit->set_pz(1, dir.Z());
0706
0707 const double totalE = electrons_per_cm * stepLength / electrons_per_gev;
0708
0709 hit->set_eion(totalE);
0710 hit->set_edep(totalE);
0711 m_g4hitcontainer->AddHit(detId, hit);
0712 }
0713
0714 return;
0715 }