From 5b432632b08b5160efa0e5e99c715d6a692e1c81 Mon Sep 17 00:00:00 2001 From: Barak Schmookler Date: Sat, 9 Nov 2024 20:51:24 -0600 Subject: [PATCH] Added benchmark for single neutron in the ZDC (#99) --- .gitlab-ci.yml | 2 + Snakefile | 1 + benchmarks/zdc_neutron/README.md | 10 + benchmarks/zdc_neutron/Snakefile | 65 ++++ .../zdc_neutron/analysis/fwd_neutrons_geant.C | 331 ++++++++++++++++++ .../zdc_neutron/analysis/fwd_neutrons_recon.C | 306 ++++++++++++++++ benchmarks/zdc_neutron/config.yml | 38 ++ .../zdc_neutron/gen_forward_neutrons.cxx | 102 ++++++ 8 files changed, 855 insertions(+) create mode 100644 benchmarks/zdc_neutron/README.md create mode 100644 benchmarks/zdc_neutron/Snakefile create mode 100644 benchmarks/zdc_neutron/analysis/fwd_neutrons_geant.C create mode 100644 benchmarks/zdc_neutron/analysis/fwd_neutrons_recon.C create mode 100644 benchmarks/zdc_neutron/config.yml create mode 100644 benchmarks/zdc_neutron/gen_forward_neutrons.cxx diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4d77de9b..e313531b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -136,6 +136,7 @@ include: - local: 'benchmarks/lfhcal/config.yml' - local: 'benchmarks/zdc/config.yml' - local: 'benchmarks/zdc_lyso/config.yml' + - local: 'benchmarks/zdc_neutron/config.yml' - local: 'benchmarks/zdc_photon/config.yml' - local: 'benchmarks/zdc_pi0/config.yml' - local: 'benchmarks/material_scan/config.yml' @@ -170,6 +171,7 @@ deploy_results: - "collect_results:tracking_performances_dis" - "collect_results:zdc" - "collect_results:zdc_lyso" + - "collect_results:zdc_neutron" - "collect_results:insert_muon" - "collect_results:insert_tau" - "collect_results:zdc_photon" diff --git a/Snakefile b/Snakefile index b913cc80..23ae5169 100644 --- a/Snakefile +++ b/Snakefile @@ -9,6 +9,7 @@ include: "benchmarks/tracking_performances/Snakefile" include: "benchmarks/tracking_performances_dis/Snakefile" include: "benchmarks/lfhcal/Snakefile" include: "benchmarks/zdc_lyso/Snakefile" +include: "benchmarks/zdc_neutron/Snakefile" include: "benchmarks/insert_muon/Snakefile" include: "benchmarks/zdc_lambda/Snakefile" include: "benchmarks/zdc_photon/Snakefile" diff --git a/benchmarks/zdc_neutron/README.md b/benchmarks/zdc_neutron/README.md new file mode 100644 index 00000000..623d1465 --- /dev/null +++ b/benchmarks/zdc_neutron/README.md @@ -0,0 +1,10 @@ +Detector Benchmark for single neutron in the ZDC +================================================= + +## Overview +This benchmark generates single-neutron events. The neutrons are generated with 100 GeV total momentum; polar angles of 0-6 mRad with respect to the proton/ion beam direction, uniform over cosine of the polar angle; and uniform azimuthal angles with respect to the proton/ion beam direction. The benchmark creates acceptance and reconstruction performance plots. + +## Contacts +[Barak Schmookler](baraks@ucr.edu) + + diff --git a/benchmarks/zdc_neutron/Snakefile b/benchmarks/zdc_neutron/Snakefile new file mode 100644 index 00000000..4b4c0d36 --- /dev/null +++ b/benchmarks/zdc_neutron/Snakefile @@ -0,0 +1,65 @@ +# Generate the single neutrons and put them into a HepMC file +rule zdc_neutron_hepmc: + input: + script = "benchmarks/zdc_neutron/gen_forward_neutrons.cxx", + output: + hepmcfile="sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.hepmc", + params: + num_events=1000, + shell: + """ +root -l -b -q '{input.script}({params.num_events}, 0, "{output.hepmcfile}")' +""" + +# Run the generated events through the Geant simulation +rule zdc_neutron_sim: + input: + hepmcfile="sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.hepmc", + warmup="warmup/{DETECTOR_CONFIG}.edm4hep.root", + output: + "sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.edm4hep.root", + params: + num_events=100, + shell: + """ +set -m # monitor mode to prevent lingering processes +exec npsim \ + --runType batch \ + -v WARNING \ + --compactFile $DETECTOR_PATH/{wildcards.DETECTOR_CONFIG}.xml \ + --numberOfEvents {params.num_events} \ + --inputFiles {input.hepmcfile} \ + --outputFile {output} +""" + + +# Process the file produced in the previous step through EICRecon +rule zdc_neutron_reco: + input: + "sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.edm4hep.root", + output: + "sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.edm4eic.root", + shell: + """ +set -m # monitor mode to prevent lingering processes +exec env DETECTOR_CONFIG={wildcards.DETECTOR_CONFIG} \ + eicrecon {input} -Ppodio:output_file={output} \ + -Ppodio:output_collections=MCParticles,EcalFarForwardZDCRawHits,EcalFarForwardZDCRecHits,EcalFarForwardZDCClusters,HcalFarForwardZDCRawHits,HcalFarForwardZDCRecHits,HcalFarForwardZDCClusters,ReconstructedFarForwardZDCNeutrons +""" + + +# Run the analysis scripts +rule zdc_neutron_analyses: + input: + geant_script = "benchmarks/zdc_neutron/analysis/fwd_neutrons_geant.C", + data_geant = "sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.edm4hep.root", + recon_script = "benchmarks/zdc_neutron/analysis/fwd_neutrons_recon.C", + data_recon = "sim_output/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons.edm4eic.root", + output: + geant_analysis_out = "results/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons_geant.pdf", + recon_analysis_out = "results/zdc_neutron/{DETECTOR_CONFIG}/fwd_neutrons_recon.pdf", + shell: + """ +root -l -b -q '{input.geant_script}("{input.data_geant}","{output.geant_analysis_out}")' +root -l -b -q '{input.recon_script}("{input.data_recon}","{output.recon_analysis_out}")' +""" diff --git a/benchmarks/zdc_neutron/analysis/fwd_neutrons_geant.C b/benchmarks/zdc_neutron/analysis/fwd_neutrons_geant.C new file mode 100644 index 00000000..0e4c7e92 --- /dev/null +++ b/benchmarks/zdc_neutron/analysis/fwd_neutrons_geant.C @@ -0,0 +1,331 @@ +//------------------ +int get_layer_number(TVector3 pos){ + + // Get Layer position wrt proton beam direction + pos.RotateY(0.025); + + auto local_z = pos.Z() - 35.8*1000.; //in mm + + // Convert to layer number + // local_z = 22.25 + (layer_number - 1)*24.9 + int layer_number = (int)std::round( (local_z - 22.25)/24.9 ) + 1; + + return layer_number; +} + +//------------------ +void fwd_neutrons_geant(std::string inputfile, std::string outputfile){ + + //Define Style + gStyle->SetOptStat(0); + gStyle->SetPadBorderMode(0); + gStyle->SetFrameBorderMode(0); + gStyle->SetFrameLineWidth(2); + gStyle->SetLabelSize(0.035,"X"); + gStyle->SetLabelSize(0.035,"Y"); + //gStyle->SetLabelOffset(0.01,"X"); + //gStyle->SetLabelOffset(0.01,"Y"); + gStyle->SetTitleXSize(0.04); + gStyle->SetTitleXOffset(0.9); + gStyle->SetTitleYSize(0.04); + gStyle->SetTitleYOffset(0.9); + + //Define histograms + TH2 *h1_neut = new TH2D("h1_neut","Neutron true energy vs. polar angle",100,0.6,2.2,100,0,200); + h1_neut->GetXaxis()->SetTitle("#theta [deg]"); h1_neut->GetXaxis()->CenterTitle(); + h1_neut->GetYaxis()->SetTitle("E [GeV]"); h1_neut->GetYaxis()->CenterTitle(); + + TH2 *h2_neut = new TH2D("h2_neut","Neutron true azimuthal angle vs. polar angle around p axis",100,-0.1,12,100,-200,200); + h2_neut->GetXaxis()->SetTitle("#theta^{*} [mRad]"); h2_neut->GetXaxis()->CenterTitle(); + h2_neut->GetYaxis()->SetTitle("#phi^{*} [deg]"); h2_neut->GetYaxis()->CenterTitle(); + + TH2 *h2a_neut = new TH2D("h2a_neut","Neutron true azimuthal angle vs. cosine of polar angle around p axis",100,0.99996,1,100,-200,200); + h2a_neut->GetXaxis()->SetTitle("cos(#theta^{*})"); h2a_neut->GetXaxis()->CenterTitle(); + h2a_neut->GetYaxis()->SetTitle("#phi^{*} [deg]"); h2a_neut->GetYaxis()->CenterTitle(); + + //Require HCal hit energy sum > 1 GeV + TH2 *h3_neut = new TH2D("h3_neut","Neutron true azimuthal angle vs. polar angle around p axis",100,-0.1,12,100,-200,200); + h3_neut->GetXaxis()->SetTitle("#theta^{*} [mRad]"); h3_neut->GetXaxis()->CenterTitle(); + h3_neut->GetYaxis()->SetTitle("#phi^{*} [deg]"); h3_neut->GetYaxis()->CenterTitle(); + + TH2 *h4_neut = new TH2D("h4_neut","Neutron local hit position at ZDC HCal front face",100,-300,300,100,-300,300); + h4_neut->GetXaxis()->SetTitle("x [mm]"); h4_neut->GetXaxis()->CenterTitle(); + h4_neut->GetYaxis()->SetTitle("y [mm]"); h4_neut->GetYaxis()->CenterTitle(); + + TH1 *h1_ecal = new TH1D("h1_ecal","Total true hit energy sum",100,-0.1,2); + h1_ecal->GetXaxis()->SetTitle("E [GeV]"); h1_ecal->GetXaxis()->CenterTitle(); + h1_ecal->SetLineColor(kRed);h1_ecal->SetLineWidth(2); + + TH1 *h1_hcal = new TH1D("h1_hcal","Total true hit energy sum",100,-0.1,2); + h1_hcal->GetXaxis()->SetTitle("E [GeV]"); h1_hcal->GetXaxis()->CenterTitle(); + h1_hcal->SetLineColor(kBlue);h1_hcal->SetLineWidth(2); + + //Cut on theta* + TH1 *h2_ecal = new TH1D("h2_ecal","Total true hit energy sum",100,-0.1,2); + h2_ecal->GetXaxis()->SetTitle("E [GeV]"); h2_ecal->GetXaxis()->CenterTitle(); + h2_ecal->SetLineColor(kRed);h2_ecal->SetLineWidth(2); + + //Cut on theta* + TH1 *h2_hcal = new TH1D("h2_hcal","Total true hit energy sum",100,-0.1,2); + h2_hcal->GetXaxis()->SetTitle("E [GeV]"); h2_hcal->GetXaxis()->CenterTitle(); + h2_hcal->SetLineColor(kBlue);h2_hcal->SetLineWidth(2); + + //Cut on theta* + TH1 *h2a_ecal = new TH1D("h2a_ecal","Total true hit energy sum",200,-0.1,30); + h2a_ecal->GetXaxis()->SetTitle("E [GeV]"); h2a_ecal->GetXaxis()->CenterTitle(); + h2a_ecal->SetLineColor(kRed);h2a_ecal->SetLineWidth(2); + + //Cut on theta* + TH1 *h2a_hcal = new TH1D("h2a_hcal","Total true hit energy sum",200,-0.1,30); + h2a_hcal->GetXaxis()->SetTitle("E [GeV]"); h2a_hcal->GetXaxis()->CenterTitle(); + h2a_hcal->SetLineColor(kBlue);h2a_hcal->SetLineWidth(2); + + //Cut on theta* + TH2 *h2_cal_both = new TH2D("h2_cal_both","Total true hit energy sum: ECal vs. HCal",100,-0.1,2.5,100,-0.1,30); + h2_cal_both->GetXaxis()->SetTitle("E_{HCal.} [GeV]"); h2_cal_both->GetXaxis()->CenterTitle(); + h2_cal_both->GetYaxis()->SetTitle("E_{ECal.} [GeV]"); h2_cal_both->GetYaxis()->CenterTitle(); + + TH1 *h3a_hcal = new TH1D("h3a_hcal","Cells w/ non-zero energy deposit ",100,0,64); + h3a_hcal->GetXaxis()->SetTitle("Layer Number in ZDC HCal"); h3a_hcal->GetXaxis()->CenterTitle(); + h3a_hcal->GetYaxis()->SetTitle("Number of cells hit per event");h3a_hcal->GetYaxis()->CenterTitle(); + h3a_hcal->SetLineColor(kBlue);h3a_hcal->SetLineWidth(3); + + //Cut on theta* < 3.5 mRad + TH1 *h3b_hcal = new TH1D("h3b_hcal","Cells w/ non-zero energy deposit ",100,0,64); + h3b_hcal->GetXaxis()->SetTitle("Layer Number in ZDC HCal"); h3b_hcal->GetXaxis()->CenterTitle(); + h3b_hcal->GetYaxis()->SetTitle("Number of cells hit per event");h3b_hcal->GetYaxis()->CenterTitle(); + h3b_hcal->SetLineColor(kBlue);h3b_hcal->SetLineWidth(3); + + //Cut on 3.5 mRad < theta* < 6 mRad + TH1 *h3c_hcal = new TH1D("h3c_hcal","Cells w/ non-zero energy deposit ",100,0,64); + h3c_hcal->GetXaxis()->SetTitle("Layer Number in ZDC HCal"); h3c_hcal->GetXaxis()->CenterTitle(); + h3c_hcal->GetYaxis()->SetTitle("Number of cells hit per event");h3c_hcal->GetYaxis()->CenterTitle(); + h3c_hcal->SetLineColor(kBlue);h3c_hcal->SetLineWidth(3); + + //Read ROOT file + TFile* file = new TFile(inputfile.c_str()); + TTree *tree = (TTree*) file->Get("events"); + + //Set cut + float edep_cut = 1.; //In GeV + + cout<<"Total number of events to analyze is "<GetEntries()< gen_status(tr, "MCParticles.generatorStatus"); + TTreeReaderArray gen_pid(tr, "MCParticles.PDG"); + TTreeReaderArray gen_px(tr, "MCParticles.momentum.x"); + TTreeReaderArray gen_py(tr, "MCParticles.momentum.y"); + TTreeReaderArray gen_pz(tr, "MCParticles.momentum.z"); + TTreeReaderArray gen_mass(tr, "MCParticles.mass"); + TTreeReaderArray gen_charge(tr, "MCParticles.charge"); + TTreeReaderArray gen_vx(tr, "MCParticles.vertex.x"); + TTreeReaderArray gen_vy(tr, "MCParticles.vertex.y"); + TTreeReaderArray gen_vz(tr, "MCParticles.vertex.z"); + + //ZDC LYSO ECal + TTreeReaderArray ecal_hit_e(tr,"EcalFarForwardZDCHits.energy"); + + //ZDC SiPM-on-tile HCal + TTreeReaderArray hcal_hit_e(tr, "HcalFarForwardZDCHits.energy"); + TTreeReaderArray hcal_hit_x(tr, "HcalFarForwardZDCHits.position.x"); + TTreeReaderArray hcal_hit_y(tr, "HcalFarForwardZDCHits.position.y"); + TTreeReaderArray hcal_hit_z(tr, "HcalFarForwardZDCHits.position.z"); + + //Other variables + int counter(0),counter_3p5(0),counter_3p5to6(0); + + TLorentzVector neut_true; //True neutron in lab coordinates + TLorentzVector neut_true_rot; //True neutron wrt proton beam direction + + float ecal_e_tot(0); + float hcal_e_tot(0); + + //Loop over events + while (tr.Next()) { + + if(counter%1000==0) cout<<"Analyzing event "<Fill(neut_true.Theta()*TMath::RadToDeg(),neut_true.E()); + + //Wrt proton beam direction + neut_true_rot = neut_true; + neut_true_rot.RotateY(0.025); + h2_neut->Fill(neut_true_rot.Theta()*1000.,neut_true_rot.Phi()*TMath::RadToDeg()); //Theta* in mRad + h2a_neut->Fill(std::cos(neut_true_rot.Theta()),neut_true_rot.Phi()*TMath::RadToDeg()); + + } + } //End loop over generated particles + + //Loop over ECal hits + for(int ihit=0;ihitFill(layer_number); + if( neut_true_rot.Theta()*1000. < 3.5 ) h3b_hcal->Fill(layer_number); + if( neut_true_rot.Theta()*1000. > 3.5 && neut_true_rot.Theta()*1000 < 6.) h3c_hcal->Fill(layer_number); + + } //End loop over HCal hits + + //Fill histograms + h1_ecal->Fill(ecal_e_tot); + h1_hcal->Fill(hcal_e_tot); + + if(hcal_e_tot > edep_cut){ + h3_neut->Fill(neut_true_rot.Theta()*1000.,neut_true_rot.Phi()*TMath::RadToDeg()); //Theta* in mRad + + //Projection to front face of HCal + auto proj_x = 35.8 * 1000. * tan(neut_true_rot.Theta()) * cos(neut_true_rot.Phi()) ; + auto proj_y = 35.8 * 1000. * tan(neut_true_rot.Theta()) * sin(neut_true_rot.Phi()) ; + + h4_neut->Fill(proj_x,proj_y); + } + + if( neut_true_rot.Theta()*1000. < 3.5 ){ + h2_ecal->Fill(ecal_e_tot); + h2_hcal->Fill(hcal_e_tot); + h2a_ecal->Fill(ecal_e_tot); + h2a_hcal->Fill(hcal_e_tot); + + h2_cal_both->Fill(hcal_e_tot,ecal_e_tot); + + counter_3p5++; + } + + if( neut_true_rot.Theta()*1000. > 3.5 && neut_true_rot.Theta()*1000 < 6.){ + counter_3p5to6++; + } + + } //End loop over events + + //Scale histograms + h3a_hcal->Scale(1./counter); + h3b_hcal->Scale(1./counter_3p5); + h3c_hcal->Scale(1./counter_3p5to6); + + //Make plots + TCanvas *c1 = new TCanvas("c1"); + h1_neut->Draw("colz"); + + TCanvas *c2 = new TCanvas("c2"); + h2_neut->Draw("colz"); + + TCanvas *c2a = new TCanvas("c2a"); + h2a_neut->Draw("colz"); + + TCanvas *c3 = new TCanvas("c3"); + h1_ecal->Draw(""); + h1_hcal->Draw("same"); + + TLegend *leg3 = new TLegend(0.6,0.6,0.85,0.8); + leg3->SetBorderSize(0);leg3->SetFillStyle(0); + leg3->AddEntry(h1_ecal,"Sum of ZDC Ecal hit energies","l"); + leg3->AddEntry(h1_hcal,"Sum of ZDC Hcal hit energies","l"); + leg3->Draw(); + + TCanvas *c4 = new TCanvas("c4"); + h3_neut->Draw("colz"); + + TLatex *tex4 = new TLatex(7,150,Form("Hcal hit energy sum > %.1f GeV",edep_cut)); + tex4->SetTextSize(0.035); + tex4->Draw(); + + TCanvas *c4a = new TCanvas("c4a"); + h4_neut->Draw("colz"); + + TLatex *tex4a = new TLatex(50,250,Form("Hcal hit energy sum > %.1f GeV",edep_cut)); + tex4a->SetTextSize(0.035); + tex4a->Draw(); + + TCanvas *c5 = new TCanvas("c5"); + h2_ecal->Draw(""); + h2_hcal->Draw("same"); + + TLegend *leg5 = new TLegend(0.5,0.6,0.9,0.8); + leg5->SetBorderSize(0);leg5->SetFillStyle(0); + leg5->SetHeader("Require neutron #theta^{*} (wrt proton beam) < 3.5 mRad"); + leg5->AddEntry(h1_ecal,"Sum of ZDC Ecal hit energies","l"); + leg5->AddEntry(h1_hcal,"Sum of ZDC Hcal hit energies","l"); + leg5->Draw(); + + TCanvas *c5a = new TCanvas("c5a"); + c5a->SetLogy(); + h2a_ecal->Draw(""); + h2a_hcal->Draw("same"); + + leg5->Draw(); + + TCanvas *c5b = new TCanvas("c5b"); + c5b->SetLogz(); + h2_cal_both->Draw("colz"); + + TLatex *tex5b = new TLatex(0,28,"Require neutron #theta^{*} (wrt proton beam) < 3.5 mRad"); + tex5b->SetTextSize(0.03); + tex5b->Draw(); + + TCanvas *c6a = new TCanvas("c6a"); + h3a_hcal->Draw(); + + TLegend *leg6a = new TLegend(0.5,0.6,0.9,0.8); + leg6a->SetBorderSize(0);leg6a->SetFillStyle(0); + leg6a->SetHeader("Average over all events"); + leg6a->Draw(); + + TCanvas *c6b = new TCanvas("c6b"); + h3b_hcal->Draw(); + + TLegend *leg6b = new TLegend(0.5,0.6,0.9,0.8); + leg6b->SetBorderSize(0);leg6b->SetFillStyle(0); + leg6b->SetHeader("Average over events w/ neutron #theta^{*} < 3.5 mRad"); + leg6b->Draw(); + + TCanvas *c6c = new TCanvas("c6c"); + h3c_hcal->Draw(); + + TLegend *leg6c = new TLegend(0.5,0.6,0.9,0.8); + leg6c->SetBorderSize(0);leg6c->SetFillStyle(0); + leg6c->SetHeader("Average over events w/ neutron 3.5 < #theta^{*} < 6 mRad"); + leg6c->Draw(); + + //Print plots to file + c1->Print(Form("%s[",outputfile.c_str())); + c1->Print(Form("%s",outputfile.c_str())); + c2->Print(Form("%s",outputfile.c_str())); + c2a->Print(Form("%s",outputfile.c_str())); + c3->Print(Form("%s",outputfile.c_str())); + c4->Print(Form("%s",outputfile.c_str())); + c4a->Print(Form("%s",outputfile.c_str())); + c5->Print(Form("%s",outputfile.c_str())); + c5a->Print(Form("%s",outputfile.c_str())); + c5b->Print(Form("%s",outputfile.c_str())); + c6a->Print(Form("%s",outputfile.c_str())); + c6b->Print(Form("%s",outputfile.c_str())); + c6c->Print(Form("%s",outputfile.c_str())); + c6c->Print(Form("%s]",outputfile.c_str())); + +} diff --git a/benchmarks/zdc_neutron/analysis/fwd_neutrons_recon.C b/benchmarks/zdc_neutron/analysis/fwd_neutrons_recon.C new file mode 100644 index 00000000..8a8a7b90 --- /dev/null +++ b/benchmarks/zdc_neutron/analysis/fwd_neutrons_recon.C @@ -0,0 +1,306 @@ +//------------------ +void fwd_neutrons_recon(std::string inputfile, std::string outputfile){ + + //Define Style + gStyle->SetOptStat(0); + gStyle->SetPadBorderMode(0); + gStyle->SetFrameBorderMode(0); + gStyle->SetFrameLineWidth(2); + gStyle->SetLabelSize(0.035,"X"); + gStyle->SetLabelSize(0.035,"Y"); + //gStyle->SetLabelOffset(0.01,"X"); + //gStyle->SetLabelOffset(0.01,"Y"); + gStyle->SetTitleXSize(0.04); + gStyle->SetTitleXOffset(0.9); + gStyle->SetTitleYSize(0.04); + gStyle->SetTitleYOffset(0.9); + + //Define histograms + TH2 *h1_neut = new TH2D("h1_neut","Neutron true energy vs. polar angle",100,0.6,2.2,100,0,200); + h1_neut->GetXaxis()->SetTitle("#theta [deg]"); h1_neut->GetXaxis()->CenterTitle(); + h1_neut->GetYaxis()->SetTitle("E [GeV]"); h1_neut->GetYaxis()->CenterTitle(); + + TH2 *h2_neut = new TH2D("h2_neut","Neutron true azimuthal angle vs. polar angle around p axis",100,-0.1,12,100,-200,200); + h2_neut->GetXaxis()->SetTitle("#theta^{*} [mRad]"); h2_neut->GetXaxis()->CenterTitle(); + h2_neut->GetYaxis()->SetTitle("#phi^{*} [deg]"); h2_neut->GetYaxis()->CenterTitle(); + + TH2 *h2a_neut = new TH2D("h2a_neut","Neutron true azimuthal angle vs. cosine of polar angle around p axis",100,0.99996,1,100,-200,200); + h2a_neut->GetXaxis()->SetTitle("cos(#theta^{*})"); h2a_neut->GetXaxis()->CenterTitle(); + h2a_neut->GetYaxis()->SetTitle("#phi^{*} [deg]"); h2a_neut->GetYaxis()->CenterTitle(); + + TH1 *h1_ecal_adc = new TH1D("h1_ecal_adc","ECal ADC amplitude spectrum",1000,-0.1,35000); + h1_ecal_adc->GetXaxis()->SetTitle("ADC Channel"); h1_ecal_adc->GetXaxis()->CenterTitle(); + h1_ecal_adc->SetLineColor(kRed);h1_ecal_adc->SetLineWidth(2); + + TH1 *h1_hcal_adc = new TH1D("h1_hcal_adc","HCal ADC amplitude spectrum",1000,-0.1,35000); + h1_hcal_adc->GetXaxis()->SetTitle("ADC Channel"); h1_hcal_adc->GetXaxis()->CenterTitle(); + h1_hcal_adc ->SetLineColor(kBlue);h1_hcal_adc->SetLineWidth(2); + + TH1 *h1_ecal = new TH1D("h1_ecal","Total reconstructed hit energy sum",100,-0.1,2); + h1_ecal->GetXaxis()->SetTitle("E [GeV]"); h1_ecal->GetXaxis()->CenterTitle(); + h1_ecal->SetLineColor(kRed);h1_ecal->SetLineWidth(2); + + TH1 *h1_hcal = new TH1D("h1_hcal","Total reconstructed hit energy sum",100,-0.1,2); + h1_hcal->GetXaxis()->SetTitle("E [GeV]"); h1_hcal->GetXaxis()->CenterTitle(); + h1_hcal->SetLineColor(kBlue);h1_hcal->SetLineWidth(2); + + //Cut on theta* + TH1 *h2_ecal = new TH1D("h2_ecal","Total reconstructed hit energy sum",100,-0.1,2); + h2_ecal->GetXaxis()->SetTitle("E [GeV]"); h2_ecal->GetXaxis()->CenterTitle(); + h2_ecal->SetLineColor(kRed);h2_ecal->SetLineWidth(2); + + //Cut on theta* + TH1 *h2_hcal = new TH1D("h2_hcal","Total reconstructed hit energy sum",100,-0.1,2); + h2_hcal->GetXaxis()->SetTitle("E [GeV]"); h2_hcal->GetXaxis()->CenterTitle(); + h2_hcal->SetLineColor(kBlue);h2_hcal->SetLineWidth(2); + + //Cut on theta* + TH1 *h3_ecal = new TH1D("h3_ecal","Number of reconstructed clusters in ECal",5,0,5); + h3_ecal->GetXaxis()->SetTitle("Number of clusters"); h3_ecal->GetXaxis()->CenterTitle(); + h3_ecal->GetXaxis()->SetNdivisions(405);h3_ecal->GetXaxis()->CenterLabels(); + h3_ecal->SetLineColor(kRed);h3_ecal->SetLineWidth(2); + + //Cut on theta* + TH1 *h3_hcal = new TH1D("h3_hcal","Number of reconstructed clusters in HCal",5,0,5); + h3_hcal->GetXaxis()->SetTitle("Number of clusters"); h3_hcal->GetXaxis()->CenterTitle(); + h3_hcal->GetXaxis()->SetNdivisions(405);h3_hcal->GetXaxis()->CenterLabels(); + h3_hcal->SetLineColor(kBlue);h3_hcal->SetLineWidth(2); + + //Cut on theta* + TH1 *h4_hcal = new TH1D("h4_hcal","HCal cluster energy: Events w/ 1 HCal Cluster",100,-0.1,120); + h4_hcal->GetXaxis()->SetTitle("E [GeV]"); h4_hcal->GetXaxis()->CenterTitle(); + h4_hcal->SetLineColor(kBlue);h4_hcal->SetLineWidth(2); + + //Cut on generated neutron theta* + 1 HCal Cluster + TH1 *h1_neut_rec = new TH1D("h1_neut_rec","Reconstructed Neutron Energy",100,-0.1,120); + h1_neut_rec->GetXaxis()->SetTitle("E [GeV]"); h1_neut_rec->GetXaxis()->CenterTitle(); + h1_neut_rec->SetLineColor(kBlue);h1_neut_rec->SetLineWidth(2); + + //Cut on generated neutron theta* + 1 HCal Cluster + TH1 *h2_neut_rec = new TH2D("h2_neut_rec","Reconstructed Neutron local hit position at ZDC HCal front face",100,-200,200,100,-200,200); + h2_neut_rec->GetXaxis()->SetTitle("x [mm]"); h2_neut_rec->GetXaxis()->CenterTitle(); + h2_neut_rec->GetYaxis()->SetTitle("y [mm]"); h2_neut_rec->GetYaxis()->CenterTitle(); + + //Read ROOT file + TFile* file = new TFile(inputfile.c_str()); + TTree *tree = (TTree*) file->Get("events"); + + cout<<"Total number of events to analyze is "<GetEntries()< gen_status(tr, "MCParticles.generatorStatus"); + TTreeReaderArray gen_pid(tr, "MCParticles.PDG"); + TTreeReaderArray gen_px(tr, "MCParticles.momentum.x"); + TTreeReaderArray gen_py(tr, "MCParticles.momentum.y"); + TTreeReaderArray gen_pz(tr, "MCParticles.momentum.z"); + TTreeReaderArray gen_mass(tr, "MCParticles.mass"); + TTreeReaderArray gen_charge(tr, "MCParticles.charge"); + TTreeReaderArray gen_vx(tr, "MCParticles.vertex.x"); + TTreeReaderArray gen_vy(tr, "MCParticles.vertex.y"); + TTreeReaderArray gen_vz(tr, "MCParticles.vertex.z"); + + //ZDC LYSO ECal + TTreeReaderArray ecal_adc(tr,"EcalFarForwardZDCRawHits.amplitude"); + TTreeReaderArray ecal_hit_e(tr,"EcalFarForwardZDCRecHits.energy"); + TTreeReaderArray ecal_cluster_energy(tr, "EcalFarForwardZDCClusters.energy"); + + //ZDC SiPM-on-tile HCal + TTreeReaderArray hcal_adc(tr,"HcalFarForwardZDCRawHits.amplitude"); + TTreeReaderArray hcal_hit_e(tr, "HcalFarForwardZDCRecHits.energy"); + TTreeReaderArray hcal_cluster_energy(tr, "HcalFarForwardZDCClusters.energy"); + + //Reconstructed neutron quantity + TTreeReaderArray rec_neutron_energy(tr,"ReconstructedFarForwardZDCNeutrons.energy"); + TTreeReaderArray rec_neutron_px(tr,"ReconstructedFarForwardZDCNeutrons.momentum.x"); + TTreeReaderArray rec_neutron_py(tr,"ReconstructedFarForwardZDCNeutrons.momentum.y"); + TTreeReaderArray rec_neutron_pz(tr,"ReconstructedFarForwardZDCNeutrons.momentum.z"); + + //Other variables + int counter(0); + + TLorentzVector neut_true; //True neutron in lab coordinates + TLorentzVector neut_true_rot; //True neutron wrt proton beam direction + + float ecal_e_tot(0); + float hcal_e_tot(0); + + //Loop over events + while (tr.Next()) { + + if(counter%100==0) cout<<"Analyzing event "<Fill(neut_true.Theta()*TMath::RadToDeg(),neut_true.E()); + + //Wrt proton beam direction + neut_true_rot = neut_true; + neut_true_rot.RotateY(0.025); + h2_neut->Fill(neut_true_rot.Theta()*1000.,neut_true_rot.Phi()*TMath::RadToDeg()); //Theta* in mRad + h2a_neut->Fill(std::cos(neut_true_rot.Theta()),neut_true_rot.Phi()*TMath::RadToDeg()); + + } + } //End loop over generated particles + + //Loop over ECal ADC hits (Raw hits may have different length than Rec hits due to zero supression) + for(int iadc=0;iadcFill(ecal_adc[iadc]); + } + + //Loop over ECal hits + for(int ihit=0;ihitFill(hcal_adc[iadc]); + } + + //Loop over HCal hits + for(int ihit=0;ihitFill(ecal_e_tot); + h1_hcal->Fill(hcal_e_tot); + + if( neut_true_rot.Theta()*1000. < 3.5 ){ + h2_ecal->Fill(ecal_e_tot); + h2_hcal->Fill(hcal_e_tot); + + h3_ecal->Fill(ecal_clus_size); + h3_hcal->Fill(hcal_clus_size); + + //HCal cluster energy -- 1 cluster events + if(hcal_clus_size==1) h4_hcal->Fill(hcal_cluster_energy[0]); + } + + //Reconstructed neutron(s) + for(int irec=0;irecFill(rec_neutron_energy[irec]); + + TVector3 neut_rec(rec_neutron_px[irec],rec_neutron_py[irec],rec_neutron_pz[irec]); + + //Wrt proton beam direction + TVector3 neut_rec_rot = neut_rec; + neut_rec_rot.RotateY(0.025); + + //Projection of reconstructed neutron to front face of HCal + auto proj_x = 35.8 * 1000. * tan(neut_rec_rot.Theta()) * cos(neut_rec_rot.Phi()) ; + auto proj_y = 35.8 * 1000. * tan(neut_rec_rot.Theta()) * sin(neut_rec_rot.Phi()) ; + h2_neut_rec->Fill(proj_x,proj_y); + } + } //End loop over reconstructed neutrons + + } //End loop over events + + //Make plots + TCanvas *c1 = new TCanvas("c1"); + h1_neut->Draw("colz"); + + TCanvas *c2 = new TCanvas("c2"); + h2_neut->Draw("colz"); + + TCanvas *c2a = new TCanvas("c2a"); + h2a_neut->Draw("colz"); + + TCanvas *c3a = new TCanvas("c3a"); + c3a->SetLogy(); + h1_ecal_adc->Draw(); + + TCanvas *c3b = new TCanvas("c3b"); + c3b->SetLogy(); + h1_hcal_adc->Draw(); + + TCanvas *c4 = new TCanvas("c4"); + h1_ecal->Draw(""); + h1_hcal->Draw("same"); + + TLegend *leg4 = new TLegend(0.6,0.6,0.85,0.8); + leg4->SetBorderSize(0);leg4->SetFillStyle(0); + leg4->AddEntry(h1_ecal,"Sum of digitized ZDC Ecal hit energies","l"); + leg4->AddEntry(h1_hcal,"Sum of digitized ZDC Hcal hit energies","l"); + leg4->Draw(); + + TCanvas *c5 = new TCanvas("c5"); + h2_ecal->Draw(""); + h2_hcal->Draw("same"); + + TLegend *leg5 = new TLegend(0.5,0.6,0.9,0.8); + leg5->SetBorderSize(0);leg5->SetFillStyle(0); + leg5->SetHeader("Require neutron #theta^{*} (wrt proton beam) < 3.5 mRad"); + leg5->AddEntry(h1_ecal,"Sum of digitized ZDC Ecal hit energies","l"); + leg5->AddEntry(h1_hcal,"Sum of digitized ZDC Hcal hit energies","l"); + leg5->Draw(); + + TCanvas *c6a = new TCanvas("c6a"); + h3_ecal->Draw(); + + TLegend *leg6 = new TLegend(0.5,0.7,0.9,0.9); + leg6->SetBorderSize(0);leg6->SetFillStyle(0); + leg6->SetHeader("Require neutron #theta^{*} (wrt proton beam) < 3.5 mRad"); + leg6->Draw(); + + TCanvas *c6b = new TCanvas("c6b"); + h3_hcal->Draw(); + leg6->Draw(); + + TCanvas *c7 = new TCanvas("c7"); + h4_hcal->Draw(); + + TLegend *leg7 = new TLegend(0.15,0.7,0.5,0.9); + leg7->SetBorderSize(0);leg7->SetFillStyle(0); + leg7->SetHeader("Require neutron #theta^{*} (wrt proton beam) < 3.5 mRad"); + leg7->Draw(); + + TCanvas *c8 = new TCanvas("c8"); + h1_neut_rec->Draw(); + + TLegend *leg8 = new TLegend(0.15,0.7,0.7,0.9); + leg8->SetBorderSize(0);leg8->SetFillStyle(0); + leg8->SetHeader("Generated neutron #theta^{*} < 3.5 mRad + 1 HCal cluster"); + leg8->Draw(); + + TCanvas *c9 = new TCanvas("c9"); + h2_neut_rec->Draw("colz"); + + TLatex *tex9 = new TLatex(-150,150,"Generated neutron #theta^{*} < 3.5 mRad + 1 HCal cluster"); + tex9->SetTextSize(0.03); + tex9->Draw(); + + //Print plots to file + c1->Print(Form("%s[",outputfile.c_str())); + c1->Print(Form("%s",outputfile.c_str())); + c2->Print(Form("%s",outputfile.c_str())); + c2a->Print(Form("%s",outputfile.c_str())); + c3a->Print(Form("%s",outputfile.c_str())); + c3b->Print(Form("%s",outputfile.c_str())); + c4->Print(Form("%s",outputfile.c_str())); + c5->Print(Form("%s",outputfile.c_str())); + c6a->Print(Form("%s",outputfile.c_str())); + c6b->Print(Form("%s",outputfile.c_str())); + c7->Print(Form("%s",outputfile.c_str())); + c8->Print(Form("%s",outputfile.c_str())); + c9->Print(Form("%s",outputfile.c_str())); + c9->Print(Form("%s]",outputfile.c_str())); + +} diff --git a/benchmarks/zdc_neutron/config.yml b/benchmarks/zdc_neutron/config.yml new file mode 100644 index 00000000..715a9b20 --- /dev/null +++ b/benchmarks/zdc_neutron/config.yml @@ -0,0 +1,38 @@ +sim:zdc_neutron: + extends: .det_benchmark + stage: simulate + script: + - snakemake --cache --cores 1 sim_output/zdc_neutron/epic_craterlake/fwd_neutrons.edm4eic.root + retry: + max: 2 + when: + - runner_system_failure + +bench:zdc_neutron: + extends: .det_benchmark + stage: benchmarks + needs: + - ["sim:zdc_neutron"] + script: + - snakemake --cores 1 results/zdc_neutron/epic_craterlake/fwd_neutrons_geant.pdf + +collect_results:zdc_neutron: + extends: .det_benchmark + stage: collect + needs: + - "bench:zdc_neutron" + script: + - ls -lrht + - mv results{,_save}/ # move results directory out of the way to preserve it + - snakemake --cores 1 --delete-all-output results/zdc_neutron/epic_craterlake/fwd_neutrons_geant.pdf + - mv results{_save,}/ + # convert to png + - | + gs -sDEVICE=pngalpha -dUseCropBox -r144 \ + -o 'results/zdc_neutron/epic_craterlake/geant_plots_%03d.png' \ + results/zdc_neutron/epic_craterlake/fwd_neutrons_geant.pdf + - | + gs -sDEVICE=pngalpha -dUseCropBox -r144 \ + -o 'results/zdc_neutron/epic_craterlake/recon_plots_%03d.png' \ + results/zdc_neutron/epic_craterlake/fwd_neutrons_recon.pdf + diff --git a/benchmarks/zdc_neutron/gen_forward_neutrons.cxx b/benchmarks/zdc_neutron/gen_forward_neutrons.cxx new file mode 100644 index 00000000..90a3eafe --- /dev/null +++ b/benchmarks/zdc_neutron/gen_forward_neutrons.cxx @@ -0,0 +1,102 @@ +#include "HepMC3/GenEvent.h" +#include "HepMC3/ReaderAscii.h" +#include "HepMC3/WriterAscii.h" +#include "HepMC3/Print.h" + +#include "TRandom3.h" +#include "TVector3.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace HepMC3; + +//Generate single neutrons in the forward detector region. +void gen_forward_neutrons(int n_events = 10000, UInt_t seed = 0, const char* out_fname = "fwd_neutrons.hepmc") +{ + + double theta_min = 0.0; //in mRad + double theta_max = 6.0; //in mRad + double cost_min = std::cos(theta_max/1000.) ; //Minimum value of cos(theta) + double cost_max = std::cos(theta_min/1000.) ; //Maximum value of cos(theta) + + double p_min = 100.; //in GeV/c + double p_max = 100.; //in GeV/c + + WriterAscii hepmc_output(out_fname); + int events_parsed = 0; + GenEvent evt(Units::GEV, Units::MM); + + // Random number generator + TRandom3 *r1 = new TRandom3(seed); //Default = 0, which uses clock to set seed + cout<<"Random number seed is "<GetSeed()<<"!"<GetParticle("neutron"); + const double mass = particle->Mass(); + const int pdgID = particle->PdgCode(); + + for (events_parsed = 0; events_parsed < n_events; events_parsed++) { + + //Set the event number + evt.set_event_number(events_parsed); + + // FourVector(px,py,pz,e,pdgid,status) + // type 4 is beam + // pdgid 11 - electron + // pdgid 2212 - proton + GenParticlePtr p1 = + std::make_shared(FourVector(0.0, 0.0, 10.0, 10.0), 11, 4); + GenParticlePtr p2 = std::make_shared( + FourVector(0.0, 0.0, 0.0, 0.938), 2212, 4); + + // Define momentum with respect to EIC proton beam direction + Double_t p = r1->Uniform(p_min,p_max); //Uniform momentum generation + Double_t phi = r1->Uniform(0.0, 2.0 * M_PI); //Uniform phi generation + + Double_t cost = r1->Uniform(cost_min,cost_max); //Uniform cos(theta) generation + Double_t th = std::acos(cost); + + Double_t px = p * std::cos(phi) * std::sin(th); + Double_t py = p * std::sin(phi) * std::sin(th); + Double_t pz = p * std::cos(th); + Double_t E = sqrt(p*p + mass*mass); + + //Rotate to lab coordinate system + TVector3 pvec(px,py,pz); + double cross_angle = -25./1000.; //in Rad + TVector3 pbeam_dir(sin(cross_angle),0,cos(cross_angle)); //proton beam direction + pvec.RotateY(-pbeam_dir.Theta()); // Theta is returned positive, beam in negative X + + // type 1 is final state + GenParticlePtr p3 = std::make_shared( + FourVector(pvec.X(), pvec.Y(), pvec.Z(), E), pdgID, 1 ); + + GenVertexPtr v1 = std::make_shared(); + v1->add_particle_in(p1); + v1->add_particle_in(p2); + + v1->add_particle_out(p3); + evt.add_vertex(v1); + + if (events_parsed == 0) { + std::cout << "First event: " << std::endl; + Print::listing(evt); + } + + hepmc_output.write_event(evt); + if (events_parsed % 10000 == 0) { + std::cout << "Event: " << events_parsed << std::endl; + } + evt.clear(); + } + hepmc_output.close(); + std::cout << "Events parsed and written: " << events_parsed << std::endl; +}