ANASEN_analysis/Armory/anasenMS Decay Version.cpp
2026-05-28 11:40:52 -04:00

632 lines
23 KiB
C++

#include "TRandom.h" // ROOT random number generators, gRandom
#include "TFile.h" // ROOT file I/O
#include "TTree.h" // ROOT tree storage
#include "TH1.h" // 1D histograms
#include "TH2.h" // 2D histograms
#include "TStyle.h" // ROOT plotting style controls
#include "TCanvas.h" // ROOT canvas drawing
#include "TBenchmark.h" // timing measurement
#include "TGraph.h" // for energy loss interpolation
#include <cstring>
#include "TApplication.h" // ROOT app loop
#include "ClassTransfer.h" // Reaction kinematics and MC event generation
#include "ClassAnasen.h" // ANASEN detector model classes (SX3, PW, etc.)
#include <stdio.h>
#include <stdlib.h>
#include <set>
#include "TLegend.h"
#include "TH1D.h"
#include "TObjArray.h"
#include "TBranch.h"
#include <iostream>
#include <fstream>
//======== Generate light particle based on reaction
// calculate real and reconstructed tracks and Q-value uncertainty
// Function to load energy loss table from file
TGraph* LoadELoss(const char* filename) {
TGraph* g = new TGraph(filename, "%lg %lg");
return g;
}
bool IsDeadAnode(int id){
static std::set<int> dead = {}; // add dead anode IDs here, 0-23
return dead.count(id);
}
bool IsDeadCathode(int id){
static std::set<int> dead = {}; // add dead cathode IDs here, 0-23
return dead.count(id);
}
bool IsDeadSX3(int id){
static std::set<int> dead = {}; // add dead SX3 IDs here, 0-23 1,7,9,3
return dead.count(id);
}
// Simulate sequential two-body decay of an unstable parent in its rest frame.
// The parent is boosted from the lab frame, the daughter (A1,Z1) is returned in lab frame,
// and the emitted ejectile (A2,Z2) is written to ejectileOut.
TLorentzVector SimulateSequentialDecay(const TLorentzVector &parent,
int daughterA, int daughterZ,
int ejectA, int ejectZ,
TLorentzVector &ejectileOut){
Isotope daughter(daughterA, daughterZ);
Isotope ejectile(ejectA, ejectZ);
double M = parent.M();
double mD = daughter.Mass;
double mE = ejectile.Mass;
double sqM = M * M;
double sum = mD + mE;
double diff = mD - mE;
double p2 = (sqM - sum*sum) * (sqM - diff*diff) / (4.0 * sqM);
if( p2 < 0 ) p2 = 0;
double p = TMath::Sqrt(p2);
double cosTheta = 2.0 * gRandom->Rndm() - 1.0;
double theta = TMath::ACos(cosTheta);
double phi = gRandom->Rndm() * TMath::TwoPi();
TVector3 v;
v.SetMagThetaPhi(p, theta, phi);
TLorentzVector daughterLab;
daughterLab.SetVectM(v, mD);
TLorentzVector ejectileLab;
ejectileLab.SetVectM(-v, mE);
TVector3 boost = parent.BoostVector();
daughterLab.Boost(boost);
ejectileLab.Boost(boost);
ejectileOut = ejectileLab;
return daughterLab;
}
int main(int argc, char **argv){
printf("=========================================\n");
printf("=== ANASEN Monte Carlo ===\n");
printf("=========================================\n");
// number of events can be overridden from command line
int numEvent = 1000000;
if( argc >= 2 ) numEvent = atoi(argv[1]);
// Reaction setup for 18Ne + 4He -> p + 21Na*.
// The heavy product 21Na* is then decayed to 20Ne + p in the simulation.
TransferReaction transfer;
transfer.SetA(18, 10, 0); // 18Ne
transfer.SetIncidentEnergyAngle(4.4, 0, 0); // KEA in MeV/u, theta and phi in degree
transfer.Seta(4, 2); // 4He target
transfer.Setb(1, 1); // outgoing proton from the primary transfer
transfer.SetB(21, 11); // 21Na* heavy product
bool enableSequentialDecay = true;
const int decayDaughterA = 20;
const int decayDaughterZ = 10;
const int decayEjectA = 1;
const int decayEjectZ = 1;
// Excited state lists (projectile and heavy-product excitation states)
std::vector<float> ExAList = {0}; // 18Ne projectile excitations in MeV
std::vector<float> ExList = {2.5}; // 21Na* excitation in MeV (above proton separation threshold)
// define vertex position uniform distribution ranges (mm)
double vertexXRange[2] = { -5, 5}; // mm
double vertexYRange[2] = { -5, 5};
double vertexZRange[2] = { -100, 100};
// detector resolution / uncertainty parameters
double sigmaSX3_W = -1; // mm, if < 0 use mid-point (no spread in SX3 horizontal dimension)
double sigmaSX3_L = 3; // mm, vertical spread for SX3
double sigmaPW_A = 0; // normalized anode uncertainty term (0-1)
double sigmaPW_C = 0; // normalized cathode uncertainty term (0-1)
// status printout
printf("------------ Vertex :\n");
printf("X : %7.2f - %7.2f mm\n", vertexXRange[0], vertexXRange[1]);
printf("Y : %7.2f - %7.2f mm\n", vertexYRange[0], vertexYRange[1]);
printf("Z : %7.2f - %7.2f mm\n", vertexZRange[0], vertexZRange[1]);
printf("------------ Uncertainty :\n");
printf(" SX3 horizontal : %.1f\n", sigmaSX3_W);
printf(" SX3 vertical : %.1f\n", sigmaSX3_L);
printf(" Anode : %.1f mm\n", sigmaPW_A);
printf(" Cathode : %.1f mm\n", sigmaPW_C);
printf(" num_eve : %d \n",numEvent);
// calculates energy/momentum/kinematics constants for transfer reaction
transfer.CalReactionConstant();
printf("Primary reaction: %s at %.2f MeV/u\n", transfer.GetReactionName().Data(), 4.4);
printf("Sequential decay enabled: %s\n", enableSequentialDecay ? "yes" : "no");
int nExA = ExAList.size();
int nEx = ExList.size();
// optional visualization control: pass "vis" as 3rd arg
bool enableVis = (argc >= 3 && strcmp(argv[2], "vis") == 0);
TApplication *app = nullptr;
if(enableVis){
app = new TApplication("anasenVis", &argc, argv);
}
// storage for tracks during simulation (for visualization)
std::vector<TVector3> visTrackVertex, visTrackDir, visTrackHitPos;
std::vector<std::pair<int,int>> visTrackWires; // {anodeID, cathodeID}
// create detector representation in memory
ANASEN * anasen = new ANASEN(); // top-level detector object
SX3 * sx3 = anasen->GetSX3(); // silicon array part
PW * pw = anasen->GetPW(); // proportional wire chamber part
// output file + trees
TString saveFileName = "SimAnasen1.root";
printf("\e[32m#################################### building Tree in %s\e[0m\n", saveFileName.Data());
TFile * saveFile = new TFile(saveFileName, "recreate");
TTree * tree1 = new TTree("tree1", "tree1");
TTree * tree2 = new TTree("tree2", "tree2");
// beam and CM variables saved in tree
double KEA;
double KEA2;
tree1->Branch("beamKEA", &KEA, "beamKEA/D");
tree2->Branch("beamKEA", &KEA2, "beamKEA/D");
double thetaCM, phiCM;
double thetaCM2, phiCM2;
tree1->Branch("thetaCM", &thetaCM, "thetaCM/D");
tree1->Branch("phiCM", &phiCM, "phiCM/D");
tree2->Branch("thetaCM", &thetaCM2, "thetaCM/D");
tree2->Branch("phiCM", &phiCM2, "phiCM/D");
// outgoing particles in lab frame (light/heavy)
double thetab, phib;
double Tb;
double thetaB, phiB, TB;
std::array<double, 2> T;
tree1->Branch("thetab", &thetab, "thetab/D"); // polar angle of light particle in lab frame
tree1->Branch("phib", &phib, "phib/D"); // azimuthal angle of light particle in lab frame
tree1->Branch("Tb", &Tb, "Tb/D"); // kinetic energy of light particle at vertex (before energy loss)
tree1->Branch("thetaB", &thetaB, "thetaB/D");
tree1->Branch("phiB", &phiB, "phiB/D");
tree1->Branch("TB", &TB, "TB/D"); // kinetic energy of heavy particle at vertex
tree1->Branch("T", &T, "T/D"); // placeholder for true Q-value, currently set to 0 for simplicity
double thetab2, phib2;
double Tb2;
double thetaB2, phiB2, TB2;
std::array<double, 2> T2;
tree2->Branch("thetab", &thetab2, "thetab/D");
tree2->Branch("phib", &phib2, "phib/D");
tree2->Branch("Tb", &Tb2, "Tb/D");
tree2->Branch("thetaB", &thetaB2, "thetaB/D");
tree2->Branch("phiB", &phiB2, "phiB/D");
tree2->Branch("TB", &TB2, "TB/D");
tree2->Branch("T", &T2, "T/D");
// excitation state identifiers
int ExAID;
double ExA;
tree1->Branch("ExAID", &ExAID, "ExAID/I"); // projectile excitation state ID
tree1->Branch("ExA", &ExA, "ExA/D"); // projectile excitation energy in MeV
int ExAID2;
double ExA2;
tree2->Branch("ExAID", &ExAID2, "ExAID/I");
tree2->Branch("ExA", &ExA2, "ExA/D");
int ExID;
double Ex;
tree1->Branch("ExID", &ExID, "ExID/I"); // target excitation state ID
tree1->Branch("Ex", &Ex, "Ex/D"); // target excitation energy in MeV
int ExID2;
double Ex2;
tree2->Branch("ExID", &ExID2, "ExID/I");
tree2->Branch("Ex", &Ex2, "Ex/D");
// true vertex position in target volume
double vertexX, vertexY, vertexZ;
tree1->Branch("vX", &vertexX, "VertexX/D"); // true vertex X position in mm
tree1->Branch("vY", &vertexY, "VertexY/D"); // true vertex Y position in mm
tree1->Branch("vZ", &vertexZ, "VertexZ/D"); // true vertex Z position in mm
double vertexX2, vertexY2, vertexZ2;
tree2->Branch("vX", &vertexX2, "VertexX/D");
tree2->Branch("vY", &vertexY2, "VertexY/D");
tree2->Branch("vZ", &vertexZ2, "VertexZ/D");
// reconstructed SX3 hit position
double sx3X, sx3Y, sx3Z;
tree1->Branch("sx3X", &sx3X, "sx3X/D"); // reconstructed X position from SX3 (with optional smearing)
tree1->Branch("sx3Y", &sx3Y, "sx3Y/D"); // reconstructed Y position from SX3 (with optional smearing)
tree1->Branch("sx3Z", &sx3Z, "sx3Z/D"); // reconstructed Z position from SX3 (with optional smearing)
double sx3X2, sx3Y2, sx3Z2;
tree2->Branch("sx3X", &sx3X2, "sx3X/D");
tree2->Branch("sx3Y", &sx3Y2, "sx3Y/D");
tree2->Branch("sx3Z", &sx3Z2, "sx3Z/D");
// PW nearest and next nearest wires
int anodeID[2], cathodeID[2];
int anodeID2[2], cathodeID2[2];
tree1->Branch("aID", anodeID, "anodeID/I"); // anodeID[0] is nearest anode wire, anodeID[1] is next nearest anode wire
tree1->Branch("cID", cathodeID, "cathodeID/I"); // cathodeID[0] is nearest cathode wire, cathodeID[1] is next nearest cathode wire
tree2->Branch("aID", anodeID2, "anodeID/I");
tree2->Branch("cID", cathodeID2, "cathodeID/I");
// distances to nearest wires
double anodeDist[2], cathodeDist[2];
double anodeDist2[2], cathodeDist2[2];
tree1->Branch("aDist", anodeDist, "anodeDist/D");
tree1->Branch("cDist", cathodeDist, "cathodeDist/D");
tree2->Branch("aDist", anodeDist2, "anodeDist/D");
tree2->Branch("cDist", cathodeDist2, "cathodeDist/D");
// SX3 channel assignment and Z fraction (depth) information
int sx3ID, sx3Up, sx3Dn, sx3Bk;
double sx3ZFrac;
int sx3ID2, sx3Up2, sx3Dn2, sx3Bk2;
double sx3ZFrac2;
tree1->Branch("sx3ID", &sx3ID, "sx3ID/I");
tree1->Branch("sx3Up", &sx3Up, "sx3Up/I");
tree1->Branch("sx3Dn", &sx3Dn, "sx3Dn/I");
tree1->Branch("sx3Bk", &sx3Bk, "sx3Bk/I");
tree1->Branch("sx3ZFrac", &sx3ZFrac, "sx3ZFrac/D");
tree2->Branch("sx3ID", &sx3ID2, "sx3ID/I");
tree2->Branch("sx3Up", &sx3Up2, "sx3Up/I");
tree2->Branch("sx3Dn", &sx3Dn2, "sx3Dn/I");
tree2->Branch("sx3Bk", &sx3Bk2, "sx3Bk/I");
tree2->Branch("sx3ZFrac", &sx3ZFrac2, "sx3ZFrac/D");
// reconstructed angles from PW track fit, method 1 and 2
double reTheta, rePhi;
double reTheta2, rePhi2;
tree1->Branch("reTheta", &reTheta, "reconstucted_theta/D");
tree1->Branch("rePhi", &rePhi, "reconstucted_phi/D");
tree2->Branch("reTheta", &reTheta2, "reconstucted_theta/D");
tree2->Branch("rePhi", &rePhi2, "reconstucted_phi/D");
double reTheta1, rePhi1;
double reTheta12, rePhi12;
tree1->Branch("reTheta1", &reTheta1, "reconstucted_theta1/D");
tree1->Branch("rePhi1", &rePhi1, "reconstucted_phi1/D");
tree2->Branch("reTheta1", &reTheta12, "reconstucted_theta1/D");
tree2->Branch("rePhi1", &rePhi12, "reconstucted_phi1/D");
// reconstructed vertex Z from PW fit
double z0;
double z02;
tree1->Branch("z0", &z0, "reconstucted_Z/D");
tree2->Branch("z0", &z02, "reconstucted_Z/D");
//========timer
TBenchmark clock;
bool shown ;
clock.Reset();
clock.Start("timer");
shown = false;
//================================= Calculate event loop
for( int i = 0; i < numEvent ; i++){
// randomly sample target/projectile excitations
ExAID = gRandom->Integer(nExA);
ExA = ExAList[ExAID];
transfer.SetExA(ExA);
ExID = gRandom->Integer(nEx);
Ex = ExList[ExID];
transfer.SetExB(Ex);
// recalc kinematic constants for chosen states
transfer.CalReactionConstant();
// isotropic CM direction
thetaCM = TMath::ACos(2 * gRandom->Rndm() - 1) ;
phiCM = (gRandom->Rndm() - 0.5) * TMath::TwoPi();
//==== Calculate reaction kinematics in lab frame for the primary transfer
TLorentzVector * output = transfer.Event(thetaCM, phiCM); // returns array of outputs
TLorentzVector Pb = output[2]; // primary proton from transfer
TLorentzVector PB = output[3]; // excited 21Na* heavy product
thetab = Pb.Theta() * TMath::RadToDeg();
Tb = (Pb.E() - Pb.M()); // kinetic energy of the light proton from the primary transfer
thetaB = PB.Theta() * TMath::RadToDeg();
TB = (PB.E() - PB.M());
phib = Pb.Phi() * TMath::RadToDeg();
phiB = PB.Phi() * TMath::RadToDeg();
T[0] = Tb;
T[1] = TB;
// prepare secondary proton from 21Na* sequential decay
TLorentzVector decayProton;
TLorentzVector heavy20;
if(enableSequentialDecay){
heavy20 = SimulateSequentialDecay(PB, decayDaughterA, decayDaughterZ,
decayEjectA, decayEjectZ, decayProton);
thetab2 = decayProton.Theta() * TMath::RadToDeg();
phib2 = decayProton.Phi() * TMath::RadToDeg();
Tb2 = decayProton.E() - decayProton.M();
thetaB2 = heavy20.Theta() * TMath::RadToDeg();
phiB2 = heavy20.Phi() * TMath::RadToDeg();
TB2 = heavy20.E() - heavy20.M();
T2[0] = Tb2;
T2[1] = TB2;
} else {
thetab2 = TMath::QuietNaN();
phib2 = TMath::QuietNaN();
Tb2 = TMath::QuietNaN();
thetaB2 = TMath::QuietNaN();
phiB2 = TMath::QuietNaN();
TB2 = TMath::QuietNaN();
T2[0] = TMath::QuietNaN();
T2[1] = TMath::QuietNaN();
}
delete [] output;
// vertex position in target volume
vertexX = (vertexXRange[1]- vertexXRange[0])*gRandom->Rndm() + vertexXRange[0];
vertexY = (vertexYRange[1]- vertexYRange[0])*gRandom->Rndm() + vertexYRange[0];
vertexZ = (vertexZRange[1]- vertexZRange[0])*gRandom->Rndm() + vertexZRange[0];
TVector3 vertex(vertexX, vertexY, vertexZ);
// set direction vector from lab angle
TVector3 dir(1, 0, 0);
dir.SetTheta(thetab * TMath::DegToRad());
dir.SetPhi(phib * TMath::DegToRad());
// run detector response models for PW and SX3
pw->FindWireID(vertex, dir, false);
sx3->FindSX3Pos(vertex, dir, false);
PWHitInfo hitInfo = pw->GetHitInfo();
anodeID[0] = hitInfo.nearestWire.first; // nearest anode wire ID
cathodeID[0] = hitInfo.nearestWire.second; // nearest cathode wire ID
anodeID[1] = hitInfo.nextNearestWire.first; // next nearest anode wire ID
cathodeID[1] = hitInfo.nextNearestWire.second; // next nearest cathode wire ID
anodeDist[1] = hitInfo.nextNearestDist.first; // distance to next nearest anode wire
cathodeDist[1] = hitInfo.nextNearestDist.second; // distance to next nearest cathode wire
if(IsDeadAnode(anodeID[0])) continue;
if(IsDeadCathode(cathodeID[0])) continue;
// SX3 hit channel info and depth fraction
sx3ID = sx3->GetID();
if(IsDeadSX3(sx3ID)) continue;
anodeDist[0] = hitInfo.nearestDist.first; // distance to nearest anode wire
cathodeDist[0] = hitInfo.nearestDist.second; // distance to nearest cathode wire
if( sx3ID >= 0 ){
sx3Up = sx3->GetChUp();
sx3Dn = sx3->GetChDn();
sx3Bk = sx3->GetChBk();
sx3ZFrac = sx3->GetZFrac();
// apply intrinsic detector resolution to true SX3 hit position
// for no smearing comment out and use GetHitPos();
TVector3 hitPos = sx3->GetHitPosWithSigma(sigmaSX3_W, sigmaSX3_L);
sx3X = hitPos.X();
sx3Y = hitPos.Y();
sx3Z = hitPos.Z();
// store track data for visualization if enabled
if(enableVis){
visTrackVertex.push_back(vertex);
visTrackDir.push_back(dir);
visTrackHitPos.push_back(hitPos);
visTrackWires.push_back({anodeID[0], cathodeID[0]});
}
// reconstruct track from PW readings + SX3 hit
pw->CalTrack(hitPos, anodeID[0], cathodeID[0], false);
reTheta = pw->GetTrackTheta() * TMath::RadToDeg();
rePhi = pw->GetTrackPhi() * TMath::RadToDeg();
// alternative track algorithm with uncertainty parameters
pw->CalTrack2(hitPos, hitInfo, sigmaPW_A, sigmaPW_C, false);
reTheta1 = pw->GetTrackTheta() * TMath::RadToDeg();
rePhi1 = pw->GetTrackPhi() * TMath::RadToDeg();
z0 = pw->GetZ0();
tree1->Fill();
// fill tree2 using the secondary proton (proton 2) track
TVector3 dir2(1, 0, 0);
dir2.SetTheta(thetab2 * TMath::DegToRad());
dir2.SetPhi(phib2 * TMath::DegToRad());
pw->FindWireID(vertex, dir2, false);
sx3->FindSX3Pos(vertex, dir2, false);
PWHitInfo hitInfo2 = pw->GetHitInfo();
anodeID2[0] = hitInfo2.nearestWire.first;
cathodeID2[0] = hitInfo2.nearestWire.second;
anodeID2[1] = hitInfo2.nextNearestWire.first;
cathodeID2[1] = hitInfo2.nextNearestWire.second;
anodeDist2[1] = hitInfo2.nextNearestDist.first;
cathodeDist2[1] = hitInfo2.nextNearestDist.second;
if(IsDeadAnode(anodeID2[0]) || IsDeadCathode(cathodeID2[0])){
sx3ID2 = -1;
} else {
sx3ID2 = sx3->GetID();
}
if(sx3ID2 < 0 || IsDeadSX3(sx3ID2)){
sx3ID2 = -1;
sx3Up2 = -1;
sx3Dn2 = -1;
sx3Bk2 = -1;
sx3ZFrac2 = TMath::QuietNaN();
sx3X2 = TMath::QuietNaN();
sx3Y2 = TMath::QuietNaN();
sx3Z2 = TMath::QuietNaN();
anodeDist2[0] = TMath::QuietNaN();
cathodeDist2[0] = TMath::QuietNaN();
reTheta2 = TMath::QuietNaN();
rePhi2 = TMath::QuietNaN();
reTheta12 = TMath::QuietNaN();
rePhi12 = TMath::QuietNaN();
z02 = TMath::QuietNaN();
} else {
anodeDist2[0] = hitInfo2.nearestDist.first;
cathodeDist2[0] = hitInfo2.nearestDist.second;
sx3Up2 = sx3->GetChUp();
sx3Dn2 = sx3->GetChDn();
sx3Bk2 = sx3->GetChBk();
sx3ZFrac2 = sx3->GetZFrac();
TVector3 hitPos2 = sx3->GetHitPosWithSigma(sigmaSX3_W, sigmaSX3_L);
sx3X2 = hitPos2.X();
sx3Y2 = hitPos2.Y();
sx3Z2 = hitPos2.Z();
pw->CalTrack(hitPos2, anodeID2[0], cathodeID2[0], false);
reTheta2 = pw->GetTrackTheta() * TMath::RadToDeg();
rePhi2 = pw->GetTrackPhi() * TMath::RadToDeg();
pw->CalTrack2(hitPos2, hitInfo2, sigmaPW_A, sigmaPW_C, false);
reTheta12 = pw->GetTrackTheta() * TMath::RadToDeg();
rePhi12 = pw->GetTrackPhi() * TMath::RadToDeg();
z02 = pw->GetZ0();
}
// copy common event info to tree2
KEA2 = KEA;
thetaCM2 = thetaCM;
phiCM2 = phiCM;
ExAID2 = ExAID;
ExA2 = ExA;
ExID2 = ExID;
Ex2 = Ex;
vertexX2 = vertexX;
vertexY2 = vertexY;
vertexZ2 = vertexZ;
tree2->Fill();
}else{
// no valid SX3 hit: mark clearly invalid
sx3Up = -1;
sx3Dn = -1;
sx3Bk = -1;
sx3ZFrac = TMath::QuietNaN();
sx3X = TMath::QuietNaN();
sx3Y = TMath::QuietNaN();
sx3Z = TMath::QuietNaN();
reTheta = TMath::QuietNaN();
rePhi = TMath::QuietNaN();
reTheta1 = TMath::QuietNaN();
rePhi1 = TMath::QuietNaN();
z0 = TMath::QuietNaN();
//Tb = -12354567; // mark kinetic energy as invalid for no hit case
// fill tree with original data (no energy loss for these events)
//comment out tree fill for no hit case
//tree->Fill();
}
//#################################################################### Timer
// measure elapsed real time and print progress roughly every 10 sec
clock.Stop("timer");
Double_t time = clock.GetRealTime("timer");
clock.Start("timer");
if ( !shown ) {
if (fmod(time, 10) < 1 ){
printf( "%10d[%2d%%]| %8.2f sec | expect: %5.1f min \n", i, TMath::Nint((i+1)*100./numEvent), time , numEvent*time/(i+1)/60);
shown = 1;
}
} else {
if (fmod(time, 10) > 9 ){
shown = 0;
}
}
}
// write results to ROOT file and close
tree1->Write("", TObject::kOverwrite);
tree2->Write("", TObject::kOverwrite);
int count1 = tree1->GetEntries();
int count2 = tree2->GetEntries();
saveFile->Close();
printf("=============== done. saved as %s. tree1 entries: %d, tree2 entries: %d\n", saveFileName.Data(), count1, count2);
if(enableVis){ // to enable visualization, run with 3rd argument "vis", e.g. "./anasenMC 1000 vis"
printf("Displaying geometry with %zu tracks from simulation\n", visTrackVertex.size());
// Build full geometry with all wires
anasen->DrawAnasen(0, 23, 0, 23, -1, true);
// Add all stored tracks to the geometry
TGeoManager *geom = anasen->GetGeoManager();
TGeoVolume *worldBox = anasen->GetWorldBox();
if(geom && worldBox && visTrackVertex.size() > 0){
int trackNodeID = 500; // start node IDs for tracks
for(size_t iTrack = 0; iTrack < visTrackVertex.size(); ++iTrack){
TVector3 vertex = visTrackVertex[iTrack];
TVector3 dir = visTrackDir[iTrack];
TVector3 hitPos = visTrackHitPos[iTrack];
double theta = dir.Theta() * TMath::RadToDeg();
double phi = dir.Phi() * TMath::RadToDeg();
// Add a line marker at the vertex
TGeoVolume *startMarker = geom->MakeSphere("startMarker", 0, 0, 2.0);
startMarker->SetLineColor(kBlack);
worldBox->AddNode(startMarker, trackNodeID,
new TGeoCombiTrans(vertex.X(), vertex.Y(), vertex.Z(),
new TGeoRotation("rot", 0, 0, 0)));
trackNodeID++;
// Add track line from vertex toward hit position
TGeoVolume *trackLine = geom->MakeTube("trackLine", 0, 0, 0.08, 150.0);
trackLine->SetLineColor(kBlue);
worldBox->AddNode(trackLine, trackNodeID,
new TGeoCombiTrans(vertex.X(), vertex.Y(), vertex.Z(),
new TGeoRotation("rotTrack", phi + 90, theta, 0)));
trackNodeID++;
// Add hit position marker
TGeoVolume *hitMarker = geom->MakeSphere("hitMarker", 0, 0, 2.0);
hitMarker->SetLineColor(kRed);
worldBox->AddNode(hitMarker, trackNodeID,
new TGeoCombiTrans(hitPos.X(), hitPos.Y(), hitPos.Z(),
new TGeoRotation("rotHit", 0, 0, 0)));
trackNodeID++;
}
// Redraw geometry with all tracks
geom->CloseGeometry();
geom->SetVisLevel(4);
worldBox->Draw("ogle");
}
if(app){
printf("Entering ROOT event loop\n");
app->Run();
}
}
delete anasen;
return 0;
}