From 08586ff4ef31c02ce2ebcc6b6428df4eb794952f Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 16 Sep 2024 15:40:20 +0100 Subject: [PATCH 01/65] Added files --- Snakefile | 1 + benchmarks/beamline/Snakefile | 25 +++++++++++++++++ benchmarks/beamline/analysis.C | 50 ++++++++++++++++++++++++++++++++++ benchmarks/beamline/functors.h | 36 ++++++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 benchmarks/beamline/Snakefile create mode 100644 benchmarks/beamline/analysis.C create mode 100644 benchmarks/beamline/functors.h diff --git a/Snakefile b/Snakefile index d645066a..c001f8b0 100644 --- a/Snakefile +++ b/Snakefile @@ -51,6 +51,7 @@ include: "benchmarks/insert_tau/Snakefile" include: "benchmarks/femc_electron/Snakefile" include: "benchmarks/femc_photon/Snakefile" include: "benchmarks/femc_pi0/Snakefile" +include: "benchmarks/beamline/Snakefile" use_s3 = config["remote_provider"].lower() == "s3" use_xrootd = config["remote_provider"].lower() == "xrootd" diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile new file mode 100644 index 00000000..70eaf753 --- /dev/null +++ b/benchmarks/beamline/Snakefile @@ -0,0 +1,25 @@ +rule beamline_sim: + input: + inputfile="/scratch/EIC/Events/el_beam_18.hepmc", + output: + "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + log: + "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root.log", + params: + N_EVENTS=10000 + shell: + """ + exec npsim \ + --runType batch \ + --inputFiles {input.inputfile} \ + --random.seed 1234 \ + --numberOfEvents {params.N_EVENTS} \ + --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --outputFile {output} + """ +rule beamline_analysis + input: + "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + output: + "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis.root" + log: \ No newline at end of file diff --git a/benchmarks/beamline/analysis.C b/benchmarks/beamline/analysis.C new file mode 100644 index 00000000..a0551139 --- /dev/null +++ b/benchmarks/beamline/analysis.C @@ -0,0 +1,50 @@ +// Script to plot the x and y positions and momentum of beamline particles as they pass through the magnets + +#include +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHit.h" +#include "edm4hep/SimCalorimeterHit.h" +#include "ROOT/RDataFrame.hxx" +#include "ROOT/RDF/RInterface.hxx" +#include "ROOT/RVec.hxx" +#include "functors.h" + +using RVecS = ROOT::VecOps::RVec; +using RNode = ROOT::RDF::RNode; + +void analysis( std::string inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ + + //Set implicit multi-threading + ROOT::EnableImplicitMT(); + + //Load the detector config + dd4hep::Detector& detector = dd4hep::Detector::getInstance(); + detector.fromCompact(compactName); + + ROOT::RDataFrame d0("events",inFile, {"BackwardsBeamlineHits"}); + RNode d1 = d0; + RVecS colNames = d1.GetColumnNames(); + + //Get the collection + std::string readoutName = "BackwardsBeamlineHits"; + + if(Any(colNames==readoutName)){ + + auto ids = detector.readout(readoutName).idSpec().fields(); + + for(auto &[key,id]: ids){ + TString colName = key+"ID"; + d1 = d1.Define(colName,getSubID(key,detector),{readoutName}); + } + } + else{ + std::cout << "Collection " << readoutName << " not found in file" << std::endl; + return; + } + + d1.Snapshot("events","output.root"); + +} diff --git a/benchmarks/beamline/functors.h b/benchmarks/beamline/functors.h new file mode 100644 index 00000000..d81b4567 --- /dev/null +++ b/benchmarks/beamline/functors.h @@ -0,0 +1,36 @@ +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHit.h" +#include "edm4hep/SimCalorimeterHit.h" + +using RVecHits = ROOT::VecOps::RVec; + +//----------------------------------------------------------------------------------------- +// Grab Component functor +//----------------------------------------------------------------------------------------- +struct getSubID{ + getSubID(std::string cname, dd4hep::Detector& det, std::string rname = "BackwardsBeamlineHits") : componentName(cname), detector(det), readoutName(rname){} + + ROOT::VecOps::RVec operator()(const RVecHits& evt) { + auto decoder = detector.readout(readoutName).idSpec().decoder(); + auto indexID = decoder->index(componentName); + ROOT::VecOps::RVec result; + for(const auto& h: evt) { + result.push_back(decoder->get(h.cellID,indexID)); + } + return result; + }; + + void SetComponent(std::string cname){ + componentName = cname; + } + void SetReadout(std::string rname){ + readoutName = rname; + } + + private: + std::string componentName; + dd4hep::Detector& detector; + std::string readoutName; +}; \ No newline at end of file From df1b38ee23b0c43909c72e3fc5976ce40f68740a Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 30 Oct 2024 10:18:21 +0000 Subject: [PATCH 02/65] Update --- benchmarks/beamline/Snakefile | 25 ++++--- benchmarks/beamline/analysis.C | 116 +++++++++++++++++++++++++++++---- benchmarks/beamline/functors.h | 71 +++++++++++++++++++- 3 files changed, 189 insertions(+), 23 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 70eaf753..94b5c1e7 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -1,25 +1,30 @@ rule beamline_sim: input: - inputfile="/scratch/EIC/Events/el_beam_18.hepmc", + macro="beamGPS.mac", output: "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", log: "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root.log", - params: - N_EVENTS=10000 shell: """ exec npsim \ - --runType batch \ - --inputFiles {input.inputfile} \ - --random.seed 1234 \ - --numberOfEvents {params.N_EVENTS} \ + --runType run \ + --enableG4GPS \ + --macroFile {input.macro} \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ - --outputFile {output} + --outputFile {output} \ """ -rule beamline_analysis + +rule beamline_analysis: input: "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", output: "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis.root" - log: \ No newline at end of file + log: + "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis.root.log" + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + shell: + """ + root -l -b -q 'analysis.C("{input}", "{output}", "{params.xml}")' + """ diff --git a/benchmarks/beamline/analysis.C b/benchmarks/beamline/analysis.C index a0551139..0c4c9465 100644 --- a/benchmarks/beamline/analysis.C +++ b/benchmarks/beamline/analysis.C @@ -4,22 +4,35 @@ #include "DD4hep/Detector.h" #include "DDRec/CellIDPositionConverter.h" #include "edm4hep/MCParticleCollection.h" -#include "edm4hep/SimTrackerHit.h" -#include "edm4hep/SimCalorimeterHit.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/Vector3f.h" +#include "edm4hep/Vector3d.h" #include "ROOT/RDataFrame.hxx" #include "ROOT/RDF/RInterface.hxx" #include "ROOT/RVec.hxx" #include "functors.h" +#include "TCanvas.h" +#include "TStyle.h" using RVecS = ROOT::VecOps::RVec; using RNode = ROOT::RDF::RNode; -void analysis( std::string inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", +void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + TString outFile = "output.root", std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ + //Set ROOT style + gStyle->SetPadLeftMargin(0.25); // Set left margin + gStyle->SetPadRightMargin(0.15); // Set right margin + gStyle->SetPadTopMargin(0.05); // Set top margin + gStyle->SetPadBottomMargin(0.15);// Set bottom margin + gStyle->SetTitleYOffset(2); // Adjust y-axis title offset + gStyle->SetOptStat(1110); + //Set implicit multi-threading ROOT::EnableImplicitMT(); - + //Load the detector config dd4hep::Detector& detector = dd4hep::Detector::getInstance(); detector.fromCompact(compactName); @@ -27,24 +40,105 @@ void analysis( std::string inFile = "/scratch/EIC/G4out/beamline/beamlineT ROOT::RDataFrame d0("events",inFile, {"BackwardsBeamlineHits"}); RNode d1 = d0; RVecS colNames = d1.GetColumnNames(); + + //Set number of entries to process + // d1 = d1.Range(0,1000); //Get the collection std::string readoutName = "BackwardsBeamlineHits"; + std::cout << "Running lazy RDataframe execution" << std::endl; + if(Any(colNames==readoutName)){ - auto ids = detector.readout(readoutName).idSpec().fields(); + d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName}) + .Define("endID",getSubID("end",detector),{readoutName}); + + //global x,y,z position and momentum + d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x") + .Define("ypos_global","BackwardsBeamlineHits.position.y") + .Define("zpos_global","BackwardsBeamlineHits.position.z") + .Define("px_global","BackwardsBeamlineHits.momentum.x") + .Define("py_global","BackwardsBeamlineHits.momentum.y") + .Define("pz_global","BackwardsBeamlineHits.momentum.z"); + + d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName}) + .Define("xpos","hitPosMom[0]") + .Define("ypos","hitPosMom[1]") + .Define("zpos","hitPosMom[2]") + .Define("xmom","hitPosMom[3]") + .Define("ymom","hitPosMom[4]") + .Define("zmom","hitPosMom[5]"); - for(auto &[key,id]: ids){ - TString colName = key+"ID"; - d1 = d1.Define(colName,getSubID(key,detector),{readoutName}); - } } else{ std::cout << "Collection " << readoutName << " not found in file" << std::endl; return; + } + + std::cout << "Executing Analysis and creating histograms" << std::endl; + + //Create array of histogram results + std::map> hHistsxpx; + std::map> hHistsypy; + + + //Create histograms + for(int i=0; i<=7; i++){ + + std::string name = "pipeID"; + name += std::to_string(i); + auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") + .Define("yposf","ypos["+std::to_string(i)+"]") + .Define("xmomf","xmom["+std::to_string(i)+"]") + .Define("ymomf","ymom["+std::to_string(i)+"]"); + + //Calculate Min and Max values + auto xmin = filterDF.Min("xposf").GetValue(); + auto xmax = filterDF.Max("xposf").GetValue(); + auto ymin = filterDF.Min("yposf").GetValue(); + auto ymax = filterDF.Max("yposf").GetValue(); + auto pxmin = filterDF.Min("xmomf").GetValue(); + auto pxmax = filterDF.Max("xmomf").GetValue(); + auto pymin = filterDF.Min("ymomf").GetValue(); + auto pymax = filterDF.Max("ymomf").GetValue(); + + TString xname = name+";x offset [mm]; x trajectory component"; + TString yname = name+";y offset [mm]; y trajectory component"; + hHistsxpx[name] = filterDF.Histo2D({xname,xname,100,xmin,xmax,100,pxmin,pxmax},"xposf","xmomf"); + hHistsypy[name] = filterDF.Histo2D({yname,yname,100,ymin,ymax,100,pymin,pymax},"yposf","ymomf"); + } - - d1.Snapshot("events","output.root"); + TCanvas *cX = new TCanvas("c1","c1",3000,1600); + cX->Divide(4,2); + + int i=1; + //Write histograms to file + for(auto& h : hHistsxpx){ + cX->cd(i++); + h.second->Draw("colz"); + } + + + TCanvas *cY = new TCanvas("c2","c2",3000,1600); + cY->Divide(4,2); + + i=1; + for(auto& h : hHistsypy){ + cY->cd(i++); + h.second->Draw("colz"); + } + + TFile *f = new TFile(outFile,"RECREATE"); + cX->Write(); + cY->Write(); + + f->Close(); + + std::cout << "Saving events to file" << std::endl; + + // ROOT::RDF::RSnapshotOptions opts; + // opts.fMode = "UPDATE"; + // d1.Snapshot("events",outFile,{"pipeID","endID","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); } diff --git a/benchmarks/beamline/functors.h b/benchmarks/beamline/functors.h index d81b4567..ee2414d0 100644 --- a/benchmarks/beamline/functors.h +++ b/benchmarks/beamline/functors.h @@ -1,8 +1,10 @@ #include "DD4hep/Detector.h" #include "DDRec/CellIDPositionConverter.h" #include "edm4hep/MCParticleCollection.h" -#include "edm4hep/SimTrackerHit.h" -#include "edm4hep/SimCalorimeterHit.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "DD4hep/VolumeManager.h" +#include "TFile.h" using RVecHits = ROOT::VecOps::RVec; @@ -33,4 +35,69 @@ struct getSubID{ std::string componentName; dd4hep::Detector& detector; std::string readoutName; +}; + +//----------------------------------------------------------------------------------------- +// Transform global x,y,z position and momentum into local coordinates +//----------------------------------------------------------------------------------------- +struct globalToLocal{ + globalToLocal(dd4hep::Detector& det) : detector(det){ + volumeManager = dd4hep::VolumeManager::getVolumeManager(det); + } + + ROOT::VecOps::RVec> operator()(const RVecHits& evt) { + + ROOT::VecOps::RVec> result; + ROOT::VecOps::RVec xvec; + ROOT::VecOps::RVec yvec; + ROOT::VecOps::RVec zvec; + ROOT::VecOps::RVec pxvec; + ROOT::VecOps::RVec pyvec; + ROOT::VecOps::RVec pzvec; + + for(const auto& h: evt) { + auto cellID = h.cellID; + // dd4hep::DetElement detelement = volumeManager.lookupDetElement(cellID); + // auto detelement = volumeManager.lookupVolumePlacement(cellID); + auto detelement = volumeManager.lookupDetElement(cellID); + const TGeoMatrix& transform = detelement.nominal().worldTransformation(); + // transform.Print(); + //auto transform = volumeManager.worldTransformation(cellID); + //Convert position to local coordinates + auto pos = h.position; + Double_t globalPos[3] = {pos.x/10, pos.y/10, pos.z/10}; + Double_t localPos[3]; + transform.MasterToLocal(globalPos, localPos); + // std::cout << "Global: " << globalPos[0] << " " << globalPos[1] << " " << globalPos[2] << std::endl; + // std::cout << "Local: " << localPos[0] << " " << localPos[1] << " " << localPos[2] << std::endl; + //Transform global momentum to local coordinates + auto mom = h.momentum; + Double_t globalMom[3] = {mom.x, mom.y, mom.z}; + Double_t localMom[3]; + transform.MasterToLocalVect(globalMom, localMom); + // std::cout << "Global: " << globalMom[0] << " " << globalMom[1] << " " << globalMom[2] << std::endl; + // std::cout << "Local: " << localMom[0] << " " << localMom[1] << " " << localMom[2] << std::endl; + + xvec.push_back(localPos[0]); + yvec.push_back(localPos[1]); + zvec.push_back(localPos[2]); + pxvec.push_back(localMom[0]); + pyvec.push_back(localMom[1]); + pzvec.push_back(localMom[2]); + + } + + result.push_back(xvec); + result.push_back(yvec); + result.push_back(zvec); + result.push_back(pxvec); + result.push_back(pyvec); + result.push_back(pzvec); + + return result; + }; + + private: + dd4hep::Detector& detector; + dd4hep::VolumeManager volumeManager; }; \ No newline at end of file From 810cc3b84ee3ff440de661fe9fd87e5672766112 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 11 Mar 2025 17:42:35 +0000 Subject: [PATCH 03/65] Make canvas name more useful --- benchmarks/beamline/Snakefile | 10 +++++----- benchmarks/beamline/analysis.C | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 94b5c1e7..13cd66a7 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -2,9 +2,9 @@ rule beamline_sim: input: macro="beamGPS.mac", output: - "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + "/scratch/EIC/G4out/beamline/beamlineTest{tag}.edm4hep.root", log: - "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root.log", + "/scratch/EIC/G4out/beamline/beamlineTest{tag}.edm4hep.root.log", shell: """ exec npsim \ @@ -17,11 +17,11 @@ rule beamline_sim: rule beamline_analysis: input: - "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + "/scratch/EIC/G4out/beamline/beamlineTest{tag}.edm4hep.root", output: - "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis.root" + "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis{tag}.root" log: - "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis.root.log" + "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis{tag}.root.log" params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", shell: diff --git a/benchmarks/beamline/analysis.C b/benchmarks/beamline/analysis.C index 0c4c9465..77672054 100644 --- a/benchmarks/beamline/analysis.C +++ b/benchmarks/beamline/analysis.C @@ -110,7 +110,7 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. } - TCanvas *cX = new TCanvas("c1","c1",3000,1600); + TCanvas *cX = new TCanvas("x_px_canvas","x_px_canvas",3000,1600); cX->Divide(4,2); int i=1; @@ -121,7 +121,7 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. } - TCanvas *cY = new TCanvas("c2","c2",3000,1600); + TCanvas *cY = new TCanvas("y_py_canvas","y_py_canvas",3000,1600); cY->Divide(4,2); i=1; From 0973e5d250c43e82b6bf2ddb508688485af3e1c5 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 11 Mar 2025 17:54:21 +0000 Subject: [PATCH 04/65] Add beam generator macro --- benchmarks/beamline/beamGPS.mac | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 benchmarks/beamline/beamGPS.mac diff --git a/benchmarks/beamline/beamGPS.mac b/benchmarks/beamline/beamGPS.mac new file mode 100644 index 00000000..28d771ba --- /dev/null +++ b/benchmarks/beamline/beamGPS.mac @@ -0,0 +1,10 @@ +/gps/ang/type beam2d +/gps/ang/sigma_x 0.0002017 rad # 201.7 urad +/gps/ang/sigma_y 0.0001873 rad # 187.3 urad +/gps/pos/type Beam +/gps/pos/sigma_x 0.119 mm +/gps/pos/sigma_y 0.0107 mm +/gps/energy 18 GeV +/gps/particle e- + +/run/beamOn 100000 \ No newline at end of file From 1323fe045148f9142e3e90d448794b6da7e9a5f3 Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 30 Apr 2025 18:26:15 +0100 Subject: [PATCH 05/65] Update analysis --- benchmarks/beamline/analysis.C | 373 +++++++++++++++++++++++++++++--- benchmarks/beamline/beamGPS.mac | 3 +- benchmarks/beamline/functors.h | 77 ++++++- 3 files changed, 419 insertions(+), 34 deletions(-) diff --git a/benchmarks/beamline/analysis.C b/benchmarks/beamline/analysis.C index 77672054..f37c2ce1 100644 --- a/benchmarks/beamline/analysis.C +++ b/benchmarks/beamline/analysis.C @@ -15,6 +15,7 @@ #include "TCanvas.h" #include "TStyle.h" + using RVecS = ROOT::VecOps::RVec; using RNode = ROOT::RDF::RNode; @@ -23,12 +24,17 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ //Set ROOT style - gStyle->SetPadLeftMargin(0.25); // Set left margin - gStyle->SetPadRightMargin(0.15); // Set right margin - gStyle->SetPadTopMargin(0.05); // Set top margin - gStyle->SetPadBottomMargin(0.15);// Set bottom margin - gStyle->SetTitleYOffset(2); // Adjust y-axis title offset - gStyle->SetOptStat(1110); + gStyle->SetPadLeftMargin(0.1); // Set left margin + gStyle->SetPadRightMargin(0.0); // Set right margin + gStyle->SetPadTopMargin(0.0); // Set top margin + gStyle->SetPadBottomMargin(0.1);// Set bottom margin + gStyle->SetTitleAlign(13); + gStyle->SetTitleX(0.12); // Place the title on the top right + gStyle->SetTitleY(0.985); // Place the title on the top right + gStyle->SetTitleSize(0.08, "t"); + gStyle->SetTitleXOffset(1.0); // Adjust y-axis title offset + gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset + gStyle->SetOptStat(0); //Set implicit multi-threading ROOT::EnableImplicitMT(); @@ -45,14 +51,44 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. // d1 = d1.Range(0,1000); //Get the collection - std::string readoutName = "BackwardsBeamlineHits"; + std::string readoutName = "BackwardsBeamlineHits"; std::cout << "Running lazy RDataframe execution" << std::endl; if(Any(colNames==readoutName)){ d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName}) - .Define("endID",getSubID("end",detector),{readoutName}); + .Define("endID",getSubID("end",detector),{readoutName}) + .Define("pipeParameters",getVolumeParametersFromCellID(detector),{readoutName}) + .Define("pipeRadius",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec radii; + for (const auto& param : params) { + radii.push_back(param.radius); + } + return radii; + }, {"pipeParameters"}) + .Define("xdet",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec xPos; + for (const auto& param : params) { + xPos.push_back(param.xPos); + } + return xPos; + }, {"pipeParameters"}) + .Define("zdet",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec zPos; + for (const auto& param : params) { + zPos.push_back(param.zPos); + } + return zPos; + }, {"pipeParameters"}) + .Define("rotation",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec rotation; + for (const auto& param : params) { + rotation.push_back(param.rotation); + } + return rotation; + }, {"pipeParameters"}); + //global x,y,z position and momentum d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x") @@ -66,9 +102,13 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. .Define("xpos","hitPosMom[0]") .Define("ypos","hitPosMom[1]") .Define("zpos","hitPosMom[2]") - .Define("xmom","hitPosMom[3]") - .Define("ymom","hitPosMom[4]") - .Define("zmom","hitPosMom[5]"); + .Define("xmomMag","hitPosMom[3]") + .Define("ymomMag","hitPosMom[4]") + .Define("zmomMag","hitPosMom[5]") + .Define("momMag","sqrt(xmomMag*xmomMag+ymomMag*ymomMag+zmomMag*zmomMag)") + .Define("xmom","xmomMag/momMag") + .Define("ymom","ymomMag/momMag") + .Define("zmom","zmomMag/momMag"); } else{ @@ -76,13 +116,58 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. return; } + // Calculate the maximum pipe radius for plotting + auto maxPipeRadius = 1.2*d1.Max("pipeRadius").GetValue(); + std::cout << "Executing Analysis and creating histograms" << std::endl; //Create array of histogram results + std::map> hHistsxy; + std::map> hHistsxyZoom; std::map> hHistsxpx; std::map> hHistsypy; + std::map xMeans; + std::map yMeans; + std::map xStdDevs; + std::map yStdDevs; + std::map pxMeans; + std::map pyMeans; + std::map pxStdDevs; + std::map pyStdDevs; + + //Fit paremeter and error maps + std::map xMeanFit; + std::map yMeanFit; + std::map xMeanFitErr; + std::map yMeanFitErr; + std::map xStdDevFit; + std::map yStdDevFit; + std::map xStdDevFitErr; + std::map yStdDevFitErr; + std::map pxMeanFit; + std::map pyMeanFit; + std::map pxMeanFitErr; + std::map pyMeanFitErr; + std::map pxStdDevFit; + std::map pyStdDevFit; + std::map pxStdDevFitErr; + std::map pyStdDevFitErr; + + std::map pipeRadii; + std::map pipeXPos; + std::map pipeZPos; + std::map pipeRotation; + auto xmin = d1.Min("xpos").GetValue(); + auto xmax = d1.Max("xpos").GetValue(); + auto ymin = d1.Min("ypos").GetValue(); + auto ymax = d1.Max("ypos").GetValue(); + auto pxmin = d1.Min("xmom").GetValue(); + auto pxmax = d1.Max("xmom").GetValue(); + auto pymin = d1.Min("ymom").GetValue(); + auto pymax = d1.Max("ymom").GetValue(); + //Create histograms for(int i=0; i<=7; i++){ @@ -91,54 +176,278 @@ void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest. auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") .Define("yposf","ypos["+std::to_string(i)+"]") .Define("xmomf","xmom["+std::to_string(i)+"]") - .Define("ymomf","ymom["+std::to_string(i)+"]"); + .Define("ymomf","ymom["+std::to_string(i)+"]") + .Define("pipeRadiusf","pipeRadius["+std::to_string(i)+"]") + .Define("xdetf","xdet["+std::to_string(i)+"]") + .Define("zdetf","zdet["+std::to_string(i)+"]") + .Define("rotationf","rotation["+std::to_string(i)+"]"); + //Calculate Min and Max values - auto xmin = filterDF.Min("xposf").GetValue(); - auto xmax = filterDF.Max("xposf").GetValue(); - auto ymin = filterDF.Min("yposf").GetValue(); - auto ymax = filterDF.Max("yposf").GetValue(); - auto pxmin = filterDF.Min("xmomf").GetValue(); - auto pxmax = filterDF.Max("xmomf").GetValue(); - auto pymin = filterDF.Min("ymomf").GetValue(); - auto pymax = filterDF.Max("ymomf").GetValue(); - - TString xname = name+";x offset [mm]; x trajectory component"; - TString yname = name+";y offset [mm]; y trajectory component"; - hHistsxpx[name] = filterDF.Histo2D({xname,xname,100,xmin,xmax,100,pxmin,pxmax},"xposf","xmomf"); - hHistsypy[name] = filterDF.Histo2D({yname,yname,100,ymin,ymax,100,pymin,pymax},"yposf","ymomf"); + auto xminf = filterDF.Min("xposf").GetValue(); + auto xmaxf = filterDF.Max("xposf").GetValue(); + auto yminf = filterDF.Min("yposf").GetValue(); + auto ymaxf = filterDF.Max("yposf").GetValue(); + auto pxminf = filterDF.Min("xmomf").GetValue(); + auto pxmaxf = filterDF.Max("xmomf").GetValue(); + auto pyminf = filterDF.Min("ymomf").GetValue(); + auto pymaxf = filterDF.Max("ymomf").GetValue(); + // Calculate means and standard deviations + xMeans[name] = filterDF.Mean("xposf").GetValue(); + yMeans[name] = filterDF.Mean("yposf").GetValue(); + xStdDevs[name] = filterDF.StdDev("xposf").GetValue(); + yStdDevs[name] = filterDF.StdDev("yposf").GetValue(); + pxMeans[name] = filterDF.Mean("xmomf").GetValue(); + pyMeans[name] = filterDF.Mean("ymomf").GetValue(); + pxStdDevs[name] = filterDF.StdDev("xmomf").GetValue(); + pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue(); + + // Calculate axes for zoomed beamspot histogram so that it is quare around the mean x and y + double halfrange = std::max({xMeans[name]-xminf, xmaxf-xMeans[name], yMeans[name]-yminf, ymaxf-yMeans[name]}); + double xMinZoom = xMeans[name] - halfrange; + double xMaxZoom = xMeans[name] + halfrange; + double yMinZoom = yMeans[name] - halfrange; + double yMaxZoom = yMeans[name] + halfrange; + + TString beamspotName = "Beamspot ID"+std::to_string(i)+";x offset [cm]; y offset [cm]"; + TString xyname = name+";x Offset [cm]; y Offset [cm]"; + TString xname = name+";x Offset [cm]; x trajectory component"; + TString yname = name+";y Offset [cm]; y trajectory component"; + hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf"); + hHistsxyZoom[name] = filterDF.Histo2D({xyname,xyname,100,xMinZoom,xMaxZoom,100,yMinZoom,yMaxZoom},"xposf","yposf"); + hHistsxpx[name] = filterDF.Histo2D({xname,xname,400,xmin,xmax,400,pxmin,pxmax},"xposf","xmomf"); + hHistsypy[name] = filterDF.Histo2D({yname,yname,400,ymin,ymax,400,pymin,pymax},"yposf","ymomf"); + + //Parameters of the pipe + pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); + pipeXPos[name] = filterDF.Max("xdetf").GetValue(); + pipeZPos[name] = filterDF.Max("zdetf").GetValue(); + pipeRotation[name] = filterDF.Max("rotationf").GetValue(); + //Fit gaussian to the x, y, px and py distributions + auto xhist = hHistsxy[name]->ProjectionX(); + auto yhist = hHistsxy[name]->ProjectionY(); + auto pxhist = hHistsxpx[name]->ProjectionY(); + auto pyhist = hHistsypy[name]->ProjectionY(); + xhist->Fit("gaus","Q"); + yhist->Fit("gaus","Q"); + pxhist->Fit("gaus","Q"); + pyhist->Fit("gaus","Q"); + //Get the fit parameters and errors + xMeanFit[name] = xhist->GetFunction("gaus")->GetParameter(1); + yMeanFit[name] = yhist->GetFunction("gaus")->GetParameter(1); + xMeanFitErr[name] = xhist->GetFunction("gaus")->GetParError(1); + yMeanFitErr[name] = yhist->GetFunction("gaus")->GetParError(1); + xStdDevFit[name] = xhist->GetFunction("gaus")->GetParameter(2); + yStdDevFit[name] = yhist->GetFunction("gaus")->GetParameter(2); + xStdDevFitErr[name] = xhist->GetFunction("gaus")->GetParError(2); + yStdDevFitErr[name] = yhist->GetFunction("gaus")->GetParError(2); + pxMeanFit[name] = pxhist->GetFunction("gaus")->GetParameter(1); + pyMeanFit[name] = pyhist->GetFunction("gaus")->GetParameter(1); + pxMeanFitErr[name] = pxhist->GetFunction("gaus")->GetParError(1); + pyMeanFitErr[name] = pyhist->GetFunction("gaus")->GetParError(1); + pxStdDevFit[name] = pxhist->GetFunction("gaus")->GetParameter(2); + pyStdDevFit[name] = pyhist->GetFunction("gaus")->GetParameter(2); + pxStdDevFitErr[name] = pxhist->GetFunction("gaus")->GetParError(2); + pyStdDevFitErr[name] = pyhist->GetFunction("gaus")->GetParError(2); + } + // Create histograms of the beamspot + TCanvas *cXY = new TCanvas("beamspot_canvas","beamspot_canvas",3000,1600); + cXY->Divide(4,2); + int i=1; + for(auto [name,h] : hHistsxy){ + // Get the pipe radius for this histogram + auto pipeRadius = pipeRadii[name]; + cXY->cd(i++); + + h->Draw("col"); + //Draw cicle + TEllipse *circle = new TEllipse(0,0,pipeRadius); + circle->SetLineColor(kRed); + circle->SetLineWidth(2); + circle->SetFillStyle(0); + circle->Draw("same"); + + // Add zoomed version in the top-right corner + TPad *pad = new TPad("zoomPad", "Zoomed View", 0.65, 0.65, 1.0, 1.0); + pad->SetFillStyle(0); // Transparent background + pad->Draw(); + pad->cd(); + // Draw the zoomed histogram without its title or axis titles + hHistsxyZoom[name]->SetTitle(""); + hHistsxyZoom[name]->GetXaxis()->SetTitle(""); + hHistsxyZoom[name]->GetYaxis()->SetTitle(""); + hHistsxyZoom[name]->Draw("col"); + cXY->cd(); // Return to the main canvas + } + + // x-px canvas TCanvas *cX = new TCanvas("x_px_canvas","x_px_canvas",3000,1600); cX->Divide(4,2); - int i=1; + i=1; //Write histograms to file for(auto& h : hHistsxpx){ cX->cd(i++); - h.second->Draw("colz"); + h.second->Draw("col"); } - + // y-py canvas TCanvas *cY = new TCanvas("y_py_canvas","y_py_canvas",3000,1600); cY->Divide(4,2); i=1; for(auto& h : hHistsypy){ cY->cd(i++); - h.second->Draw("colz"); + h.second->Draw("col"); } + // Save 2D canvases as pngs + cXY->SaveAs("beamspot.png"); + cX->SaveAs("x_px.png"); + cY->SaveAs("y_py.png"); + + // --------------------------------------------------------------------------- + // Create histograms showing the fitted means and standard deviations of the positions and momenta + // --------------------------------------------------------------------------- + + // Create histograms for fitted X means and standard deviations + TH1F* hFittedXMeans = CreateFittedHistogram("hFittedXMeans", + "Mean X Offset [cm]", + xMeanFit, + xMeanFitErr, + "Pipe ID"); + + TH1F* hFittedXStdDevs = CreateFittedHistogram("hFittedXStdDevs", + "Std Deviation X Offset [cm]", + xStdDevFit, + xStdDevFitErr, + "Pipe ID"); + + // Create histograms for fitted Y means and standard deviations + TH1F* hFittedYMeans = CreateFittedHistogram("hFittedYMeans", + "Mean Y Offset [cm]", + yMeanFit, + yMeanFitErr, + "Pipe ID"); + + TH1F* hFittedYStdDevs = CreateFittedHistogram("hFittedYStdDevs", + "Std Deviation Y Offset [cm]", + yStdDevFit, + yStdDevFitErr, + "Pipe ID"); + + TH1F* hFittedPxMeans = CreateFittedHistogram("hFittedPxMeans", + "Mean Px", + pxMeanFit, + pxMeanFitErr, + "Pipe ID"); + TH1F* hFittedPyMeans = CreateFittedHistogram("hFittedPyMeans", + "Mean Py", + pyMeanFit, + pyMeanFitErr, + "Pipe ID"); + TH1F* hFittedPxStdDevs = CreateFittedHistogram("hFittedPxStdDevs", + "Std Deviation Px", + pxStdDevFit, + pxStdDevFitErr, + "Pipe ID"); + TH1F* hFittedPyStdDevs = CreateFittedHistogram("hFittedPyStdDevs", + "Std Deviation Py", + pyStdDevFit, + pyStdDevFitErr, + "Pipe ID"); + + + // Create a canvas for the fitted beamspot means and standard deviations + TCanvas *cFittedMeans = new TCanvas("cFittedMeans", "Fitted Beamspot Means and Std Deviation", 1200, 800); + cFittedMeans->Divide(2, 2); + cFittedMeans->cd(1); + hFittedXMeans->Draw("E1"); // "E1" draws error bars + cFittedMeans->cd(2); + hFittedXStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMeans->cd(3); + hFittedYMeans->Draw("E1"); // "E1" draws error bars + cFittedMeans->cd(4); + hFittedYStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMeans->SetGrid(); + cFittedMeans->Update(); + // Save the canvas as a PNG file + cFittedMeans->SaveAs("fitted_means_stddevs.png"); + + // Create a canvas for the fitted momentum means and standard deviations + TCanvas *cFittedMomentumMeans = new TCanvas("cFittedMomentumMeans", "Fitted Momentum Means and Std Deviation", 1200, 800); + cFittedMomentumMeans->Divide(2, 2); + cFittedMomentumMeans->cd(1); + hFittedPxMeans->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->cd(2); + hFittedPxStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->cd(3); + hFittedPyMeans->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->cd(4); + hFittedPyStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->SetGrid(); + cFittedMomentumMeans->Update(); + // Save the canvas as a PNG file + cFittedMomentumMeans->SaveAs("fitted_momentum_means_stddevs.png"); + + + // ----------------------------------------------------------------------------- + // Create histograms of the beampipe parameters + // ----------------------------------------------------------------------------- + TH1F* hPipeRadii = CreateFittedHistogram("hPipeRadii", + "Radius [cm]", + pipeRadii, + {}, + "Pipe ID"); + TH1F* hPipeXPos = CreateFittedHistogram("hPipeXPos", + "X Position [cm]", + pipeXPos, + {}, + "Pipe ID"); + TH1F* hPipeZPos = CreateFittedHistogram("hPipeZPos", + "Z Position [cm]", + pipeZPos, + {}, + "Pipe ID"); + TH1F* hPipeRotations = CreateFittedHistogram("hPipeRotations", + "Rotation [rad]", + pipeRotation, + {}, + "Pipe ID"); + + // Create a canvas for the pipe parameters + TCanvas *cPipeParams = new TCanvas("cPipeParams", "Pipe Parameters", 1200, 400); + cPipeParams->Divide(4, 1); + cPipeParams->cd(1); + hPipeRadii->Draw(""); + cPipeParams->cd(2); + hPipeXPos->Draw(""); + cPipeParams->cd(3); + hPipeZPos->Draw(""); + cPipeParams->cd(4); + hPipeRotations->Draw(""); + cPipeParams->SetGrid(); + cPipeParams->Update(); + // Save the canvas as a PNG file + cPipeParams->SaveAs("pipe_parameters.png"); + + TFile *f = new TFile(outFile,"RECREATE"); + cXY->Write(); cX->Write(); cY->Write(); + cFittedMeans->Write(); + cFittedMomentumMeans->Write(); + cPipeParams->Write(); f->Close(); std::cout << "Saving events to file" << std::endl; - // ROOT::RDF::RSnapshotOptions opts; - // opts.fMode = "UPDATE"; - // d1.Snapshot("events",outFile,{"pipeID","endID","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); + ROOT::RDF::RSnapshotOptions opts; + // opts.fMode = "UPDATE"; + // d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); } diff --git a/benchmarks/beamline/beamGPS.mac b/benchmarks/beamline/beamGPS.mac index 28d771ba..8ecd6bbb 100644 --- a/benchmarks/beamline/beamGPS.mac +++ b/benchmarks/beamline/beamGPS.mac @@ -4,7 +4,8 @@ /gps/pos/type Beam /gps/pos/sigma_x 0.119 mm /gps/pos/sigma_y 0.0107 mm -/gps/energy 18 GeV +/gps/energy 17.846 GeV +#/gps/energy 18 GeV /gps/particle e- /run/beamOn 100000 \ No newline at end of file diff --git a/benchmarks/beamline/functors.h b/benchmarks/beamline/functors.h index ee2414d0..fede4b84 100644 --- a/benchmarks/beamline/functors.h +++ b/benchmarks/beamline/functors.h @@ -100,4 +100,79 @@ struct globalToLocal{ private: dd4hep::Detector& detector; dd4hep::VolumeManager volumeManager; -}; \ No newline at end of file +}; + +struct volParams{ + double radius; + double xPos; + double yPos; + double zPos; + double rotation; +}; + +// Functor to get the volume element parameters from the CellID +struct getVolumeParametersFromCellID { + getVolumeParametersFromCellID(dd4hep::Detector& det) : detector(det) { + volumeManager = dd4hep::VolumeManager::getVolumeManager(det); + } + + ROOT::VecOps::RVec operator()(const RVecHits& evt) { + ROOT::VecOps::RVec result; + // Look up the detector element using the CellID + for(const auto& h : evt) { + auto cellID = h.cellID; + auto detelement = volumeManager.lookupDetElement(cellID); + const TGeoMatrix& transform = detelement.nominal().worldTransformation(); + const Double_t* translation = transform.GetTranslation(); + const Double_t* rotationMatrix = transform.GetRotationMatrix(); // Compute rotation angle around the Y-axis + double rotationAngleY = std::atan2(rotationMatrix[2], rotationMatrix[8]); // atan2(r13, r33) + auto volume = detelement.volume(); + dd4hep::ConeSegment cone = volume.solid(); + volParams params{ + cone.rMax1(), + translation[0], + translation[1], + translation[2], + rotationAngleY + }; + result.push_back(params); + } + return result; + } + +private: + dd4hep::Detector& detector; + dd4hep::VolumeManager volumeManager; +}; + +TH1F* CreateFittedHistogram(const std::string& histName, + const std::string& histTitle, + const std::map& valueMap, + const std::map& errorMap, + const std::string& xAxisLabel) { + // Number of bins corresponds to the number of entries in the value map + int nPoints = valueMap.size(); + TH1F* hist = new TH1F(histName.c_str(), (";" + xAxisLabel + ";" + histTitle).c_str(), nPoints, 0.5, nPoints + 0.5); + + // Fill the histogram with values and errors, and set custom bin labels + int binIndex = 1; // Start from bin 1 + for (const auto& [name, value] : valueMap) { + hist->SetBinContent(binIndex, value); // Set the bin content + if(errorMap.size()==valueMap.size()){ + hist->SetBinError(binIndex, errorMap.at(name)); // Set the bin error + } + else{ + hist->SetBinError(binIndex, 0); // Set the bin error to 0 if not provided + } + hist->GetXaxis()->SetBinLabel(binIndex, name); // Set the bin label + binIndex++; + } + + // Customize the histogram + hist->SetMarkerStyle(20); + hist->SetMarkerSize(1.0); + hist->SetLineColor(kBlue); + hist->SetMarkerColor(kRed); + + return hist; +} \ No newline at end of file From 6329372609f67c3a5035d03686b71ed62a768c22 Mon Sep 17 00:00:00 2001 From: simonge Date: Fri, 2 May 2025 10:23:14 +0100 Subject: [PATCH 06/65] Add phasespace scripts --- benchmarks/beamline/Snakefile | 31 +- benchmarks/beamline/beamlineAnalysis.C | 453 ++++++++++++++++++ .../beamline/{beamGPS.mac => beamlineGPS.mac} | 0 .../{analysis.C => phasespaceAnalysis.C} | 2 +- benchmarks/beamline/phasespaceGPS.mac | 11 + 5 files changed, 489 insertions(+), 8 deletions(-) create mode 100644 benchmarks/beamline/beamlineAnalysis.C rename benchmarks/beamline/{beamGPS.mac => beamlineGPS.mac} (100%) rename benchmarks/beamline/{analysis.C => phasespaceAnalysis.C} (99%) create mode 100644 benchmarks/beamline/phasespaceGPS.mac diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 13cd66a7..dc091e03 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -1,10 +1,10 @@ rule beamline_sim: input: - macro="beamGPS.mac", + macro="{test}GPS.mac", output: - "/scratch/EIC/G4out/beamline/beamlineTest{tag}.edm4hep.root", + "/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root", log: - "/scratch/EIC/G4out/beamline/beamlineTest{tag}.edm4hep.root.log", + "/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root.log", shell: """ exec npsim \ @@ -15,16 +15,33 @@ rule beamline_sim: --outputFile {output} \ """ +rule beamline_geometry_image: + output: + "geometry_image{tag}.png" + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + shell: + """ + exec npsim \ + --compactFile {params.xml} \ + --runType vis \ + --enableG4GPS \ + --macroFile visbeam.mac + mv geometry_image.png {output} + """ + rule beamline_analysis: input: - "/scratch/EIC/G4out/beamline/beamlineTest{tag}.edm4hep.root", + data="/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root", + script="{test}Analysis.C", output: - "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis{tag}.root" + "/scratch/EIC/ReconOut/beamline/{test}TestAnalysis{tag}.root" log: - "/scratch/EIC/ReconOut/beamline/beamlineTestAnalysis{tag}.root.log" + "/scratch/EIC/ReconOut/beamline/{test}TestAnalysis{tag}.root.log" params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", shell: """ - root -l -b -q 'analysis.C("{input}", "{output}", "{params.xml}")' + root -l -b -q 'analysis.C("{input.data}", "{output}", "{params.xml}")' """ + diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C new file mode 100644 index 00000000..abd677a8 --- /dev/null +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -0,0 +1,453 @@ +// Script to plot the x and y positions and momentum of beamline particles as they pass through the magnets + +#include +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/Vector3f.h" +#include "edm4hep/Vector3d.h" +#include "ROOT/RDataFrame.hxx" +#include "ROOT/RDF/RInterface.hxx" +#include "ROOT/RVec.hxx" +#include "functors.h" +#include "TCanvas.h" +#include "TStyle.h" + + +using RVecS = ROOT::VecOps::RVec; +using RNode = ROOT::RDF::RNode; + +void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + TString outFile = "output.root", + std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ + + //Set ROOT style + gStyle->SetPadLeftMargin(0.1); // Set left margin + gStyle->SetPadRightMargin(0.0); // Set right margin + gStyle->SetPadTopMargin(0.0); // Set top margin + gStyle->SetPadBottomMargin(0.1);// Set bottom margin + gStyle->SetTitleAlign(13); + gStyle->SetTitleX(0.12); // Place the title on the top right + gStyle->SetTitleY(0.985); // Place the title on the top right + gStyle->SetTitleSize(0.08, "t"); + gStyle->SetTitleXOffset(1.0); // Adjust y-axis title offset + gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset + gStyle->SetOptStat(0); + + //Set implicit multi-threading + ROOT::EnableImplicitMT(); + + //Load the detector config + dd4hep::Detector& detector = dd4hep::Detector::getInstance(); + detector.fromCompact(compactName); + + ROOT::RDataFrame d0("events",inFile, {"BackwardsBeamlineHits"}); + RNode d1 = d0; + RVecS colNames = d1.GetColumnNames(); + + //Set number of entries to process + // d1 = d1.Range(0,1000); + + //Get the collection + std::string readoutName = "BackwardsBeamlineHits"; + + std::cout << "Running lazy RDataframe execution" << std::endl; + + if(Any(colNames==readoutName)){ + + d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName}) + .Define("endID",getSubID("end",detector),{readoutName}) + .Define("pipeParameters",getVolumeParametersFromCellID(detector),{readoutName}) + .Define("pipeRadius",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec radii; + for (const auto& param : params) { + radii.push_back(param.radius); + } + return radii; + }, {"pipeParameters"}) + .Define("xdet",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec xPos; + for (const auto& param : params) { + xPos.push_back(param.xPos); + } + return xPos; + }, {"pipeParameters"}) + .Define("zdet",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec zPos; + for (const auto& param : params) { + zPos.push_back(param.zPos); + } + return zPos; + }, {"pipeParameters"}) + .Define("rotation",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec rotation; + for (const auto& param : params) { + rotation.push_back(param.rotation); + } + return rotation; + }, {"pipeParameters"}); + + + //global x,y,z position and momentum + d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x") + .Define("ypos_global","BackwardsBeamlineHits.position.y") + .Define("zpos_global","BackwardsBeamlineHits.position.z") + .Define("px_global","BackwardsBeamlineHits.momentum.x") + .Define("py_global","BackwardsBeamlineHits.momentum.y") + .Define("pz_global","BackwardsBeamlineHits.momentum.z"); + + d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName}) + .Define("xpos","hitPosMom[0]") + .Define("ypos","hitPosMom[1]") + .Define("zpos","hitPosMom[2]") + .Define("xmomMag","hitPosMom[3]") + .Define("ymomMag","hitPosMom[4]") + .Define("zmomMag","hitPosMom[5]") + .Define("momMag","sqrt(xmomMag*xmomMag+ymomMag*ymomMag+zmomMag*zmomMag)") + .Define("xmom","xmomMag/momMag") + .Define("ymom","ymomMag/momMag") + .Define("zmom","zmomMag/momMag"); + + } + else{ + std::cout << "Collection " << readoutName << " not found in file" << std::endl; + return; + } + + // Calculate the maximum pipe radius for plotting + auto maxPipeRadius = 1.2*d1.Max("pipeRadius").GetValue(); + + std::cout << "Executing Analysis and creating histograms" << std::endl; + + //Create array of histogram results + std::map> hHistsxy; + std::map> hHistsxyZoom; + std::map> hHistsxpx; + std::map> hHistsypy; + + std::map xMeans; + std::map yMeans; + std::map xStdDevs; + std::map yStdDevs; + std::map pxMeans; + std::map pyMeans; + std::map pxStdDevs; + std::map pyStdDevs; + + //Fit paremeter and error maps + std::map xMeanFit; + std::map yMeanFit; + std::map xMeanFitErr; + std::map yMeanFitErr; + std::map xStdDevFit; + std::map yStdDevFit; + std::map xStdDevFitErr; + std::map yStdDevFitErr; + std::map pxMeanFit; + std::map pyMeanFit; + std::map pxMeanFitErr; + std::map pyMeanFitErr; + std::map pxStdDevFit; + std::map pyStdDevFit; + std::map pxStdDevFitErr; + std::map pyStdDevFitErr; + + std::map pipeRadii; + std::map pipeXPos; + std::map pipeZPos; + std::map pipeRotation; + + auto xmin = d1.Min("xpos").GetValue(); + auto xmax = d1.Max("xpos").GetValue(); + auto ymin = d1.Min("ypos").GetValue(); + auto ymax = d1.Max("ypos").GetValue(); + auto pxmin = d1.Min("xmom").GetValue(); + auto pxmax = d1.Max("xmom").GetValue(); + auto pymin = d1.Min("ymom").GetValue(); + auto pymax = d1.Max("ymom").GetValue(); + + //Create histograms + for(int i=0; i<=7; i++){ + + std::string name = "pipeID"; + name += std::to_string(i); + auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") + .Define("yposf","ypos["+std::to_string(i)+"]") + .Define("xmomf","xmom["+std::to_string(i)+"]") + .Define("ymomf","ymom["+std::to_string(i)+"]") + .Define("pipeRadiusf","pipeRadius["+std::to_string(i)+"]") + .Define("xdetf","xdet["+std::to_string(i)+"]") + .Define("zdetf","zdet["+std::to_string(i)+"]") + .Define("rotationf","rotation["+std::to_string(i)+"]"); + + + //Calculate Min and Max values + auto xminf = filterDF.Min("xposf").GetValue(); + auto xmaxf = filterDF.Max("xposf").GetValue(); + auto yminf = filterDF.Min("yposf").GetValue(); + auto ymaxf = filterDF.Max("yposf").GetValue(); + auto pxminf = filterDF.Min("xmomf").GetValue(); + auto pxmaxf = filterDF.Max("xmomf").GetValue(); + auto pyminf = filterDF.Min("ymomf").GetValue(); + auto pymaxf = filterDF.Max("ymomf").GetValue(); + // Calculate means and standard deviations + xMeans[name] = filterDF.Mean("xposf").GetValue(); + yMeans[name] = filterDF.Mean("yposf").GetValue(); + xStdDevs[name] = filterDF.StdDev("xposf").GetValue(); + yStdDevs[name] = filterDF.StdDev("yposf").GetValue(); + pxMeans[name] = filterDF.Mean("xmomf").GetValue(); + pyMeans[name] = filterDF.Mean("ymomf").GetValue(); + pxStdDevs[name] = filterDF.StdDev("xmomf").GetValue(); + pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue(); + + // Calculate axes for zoomed beamspot histogram so that it is quare around the mean x and y + double halfrange = std::max({xMeans[name]-xminf, xmaxf-xMeans[name], yMeans[name]-yminf, ymaxf-yMeans[name]}); + double xMinZoom = xMeans[name] - halfrange; + double xMaxZoom = xMeans[name] + halfrange; + double yMinZoom = yMeans[name] - halfrange; + double yMaxZoom = yMeans[name] + halfrange; + + TString beamspotName = "Beamspot ID"+std::to_string(i)+";x offset [cm]; y offset [cm]"; + TString xyname = name+";x Offset [cm]; y Offset [cm]"; + TString xname = name+";x Offset [cm]; x trajectory component"; + TString yname = name+";y Offset [cm]; y trajectory component"; + hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf"); + hHistsxyZoom[name] = filterDF.Histo2D({xyname,xyname,100,xMinZoom,xMaxZoom,100,yMinZoom,yMaxZoom},"xposf","yposf"); + hHistsxpx[name] = filterDF.Histo2D({xname,xname,400,xmin,xmax,400,pxmin,pxmax},"xposf","xmomf"); + hHistsypy[name] = filterDF.Histo2D({yname,yname,400,ymin,ymax,400,pymin,pymax},"yposf","ymomf"); + + //Parameters of the pipe + pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); + pipeXPos[name] = filterDF.Max("xdetf").GetValue(); + pipeZPos[name] = filterDF.Max("zdetf").GetValue(); + pipeRotation[name] = filterDF.Max("rotationf").GetValue(); + + //Fit gaussian to the x, y, px and py distributions + auto xhist = hHistsxy[name]->ProjectionX(); + auto yhist = hHistsxy[name]->ProjectionY(); + auto pxhist = hHistsxpx[name]->ProjectionY(); + auto pyhist = hHistsypy[name]->ProjectionY(); + xhist->Fit("gaus","Q"); + yhist->Fit("gaus","Q"); + pxhist->Fit("gaus","Q"); + pyhist->Fit("gaus","Q"); + //Get the fit parameters and errors + xMeanFit[name] = xhist->GetFunction("gaus")->GetParameter(1); + yMeanFit[name] = yhist->GetFunction("gaus")->GetParameter(1); + xMeanFitErr[name] = xhist->GetFunction("gaus")->GetParError(1); + yMeanFitErr[name] = yhist->GetFunction("gaus")->GetParError(1); + xStdDevFit[name] = xhist->GetFunction("gaus")->GetParameter(2); + yStdDevFit[name] = yhist->GetFunction("gaus")->GetParameter(2); + xStdDevFitErr[name] = xhist->GetFunction("gaus")->GetParError(2); + yStdDevFitErr[name] = yhist->GetFunction("gaus")->GetParError(2); + pxMeanFit[name] = pxhist->GetFunction("gaus")->GetParameter(1); + pyMeanFit[name] = pyhist->GetFunction("gaus")->GetParameter(1); + pxMeanFitErr[name] = pxhist->GetFunction("gaus")->GetParError(1); + pyMeanFitErr[name] = pyhist->GetFunction("gaus")->GetParError(1); + pxStdDevFit[name] = pxhist->GetFunction("gaus")->GetParameter(2); + pyStdDevFit[name] = pyhist->GetFunction("gaus")->GetParameter(2); + pxStdDevFitErr[name] = pxhist->GetFunction("gaus")->GetParError(2); + pyStdDevFitErr[name] = pyhist->GetFunction("gaus")->GetParError(2); + + } + + // Create histograms of the beamspot + TCanvas *cXY = new TCanvas("beamspot_canvas","beamspot_canvas",3000,1600); + cXY->Divide(4,2); + int i=1; + for(auto [name,h] : hHistsxy){ + // Get the pipe radius for this histogram + auto pipeRadius = pipeRadii[name]; + cXY->cd(i++); + + h->Draw("col"); + //Draw cicle + TEllipse *circle = new TEllipse(0,0,pipeRadius); + circle->SetLineColor(kRed); + circle->SetLineWidth(2); + circle->SetFillStyle(0); + circle->Draw("same"); + + // Add zoomed version in the top-right corner + TPad *pad = new TPad("zoomPad", "Zoomed View", 0.65, 0.65, 1.0, 1.0); + pad->SetFillStyle(0); // Transparent background + pad->Draw(); + pad->cd(); + // Draw the zoomed histogram without its title or axis titles + hHistsxyZoom[name]->SetTitle(""); + hHistsxyZoom[name]->GetXaxis()->SetTitle(""); + hHistsxyZoom[name]->GetYaxis()->SetTitle(""); + hHistsxyZoom[name]->Draw("col"); + cXY->cd(); // Return to the main canvas + } + + // x-px canvas + TCanvas *cX = new TCanvas("x_px_canvas","x_px_canvas",3000,1600); + cX->Divide(4,2); + + i=1; + //Write histograms to file + for(auto& h : hHistsxpx){ + cX->cd(i++); + h.second->Draw("col"); + } + + // y-py canvas + TCanvas *cY = new TCanvas("y_py_canvas","y_py_canvas",3000,1600); + cY->Divide(4,2); + + i=1; + for(auto& h : hHistsypy){ + cY->cd(i++); + h.second->Draw("col"); + } + + // Save 2D canvases as pngs + cXY->SaveAs("beamspot.png"); + cX->SaveAs("x_px.png"); + cY->SaveAs("y_py.png"); + + // --------------------------------------------------------------------------- + // Create histograms showing the fitted means and standard deviations of the positions and momenta + // --------------------------------------------------------------------------- + + // Create histograms for fitted X means and standard deviations + TH1F* hFittedXMeans = CreateFittedHistogram("hFittedXMeans", + "Mean X Offset [cm]", + xMeanFit, + xMeanFitErr, + "Pipe ID"); + + TH1F* hFittedXStdDevs = CreateFittedHistogram("hFittedXStdDevs", + "Std Deviation X Offset [cm]", + xStdDevFit, + xStdDevFitErr, + "Pipe ID"); + + // Create histograms for fitted Y means and standard deviations + TH1F* hFittedYMeans = CreateFittedHistogram("hFittedYMeans", + "Mean Y Offset [cm]", + yMeanFit, + yMeanFitErr, + "Pipe ID"); + + TH1F* hFittedYStdDevs = CreateFittedHistogram("hFittedYStdDevs", + "Std Deviation Y Offset [cm]", + yStdDevFit, + yStdDevFitErr, + "Pipe ID"); + + TH1F* hFittedPxMeans = CreateFittedHistogram("hFittedPxMeans", + "Mean Px", + pxMeanFit, + pxMeanFitErr, + "Pipe ID"); + TH1F* hFittedPyMeans = CreateFittedHistogram("hFittedPyMeans", + "Mean Py", + pyMeanFit, + pyMeanFitErr, + "Pipe ID"); + TH1F* hFittedPxStdDevs = CreateFittedHistogram("hFittedPxStdDevs", + "Std Deviation Px", + pxStdDevFit, + pxStdDevFitErr, + "Pipe ID"); + TH1F* hFittedPyStdDevs = CreateFittedHistogram("hFittedPyStdDevs", + "Std Deviation Py", + pyStdDevFit, + pyStdDevFitErr, + "Pipe ID"); + + + // Create a canvas for the fitted beamspot means and standard deviations + TCanvas *cFittedMeans = new TCanvas("cFittedMeans", "Fitted Beamspot Means and Std Deviation", 1200, 800); + cFittedMeans->Divide(2, 2); + cFittedMeans->cd(1); + hFittedXMeans->Draw("E1"); // "E1" draws error bars + cFittedMeans->cd(2); + hFittedXStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMeans->cd(3); + hFittedYMeans->Draw("E1"); // "E1" draws error bars + cFittedMeans->cd(4); + hFittedYStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMeans->SetGrid(); + cFittedMeans->Update(); + // Save the canvas as a PNG file + cFittedMeans->SaveAs("fitted_means_stddevs.png"); + + // Create a canvas for the fitted momentum means and standard deviations + TCanvas *cFittedMomentumMeans = new TCanvas("cFittedMomentumMeans", "Fitted Momentum Means and Std Deviation", 1200, 800); + cFittedMomentumMeans->Divide(2, 2); + cFittedMomentumMeans->cd(1); + hFittedPxMeans->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->cd(2); + hFittedPxStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->cd(3); + hFittedPyMeans->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->cd(4); + hFittedPyStdDevs->Draw("E1"); // "E1" draws error bars + cFittedMomentumMeans->SetGrid(); + cFittedMomentumMeans->Update(); + // Save the canvas as a PNG file + cFittedMomentumMeans->SaveAs("fitted_momentum_means_stddevs.png"); + + + // ----------------------------------------------------------------------------- + // Create histograms of the beampipe parameters + // ----------------------------------------------------------------------------- + TH1F* hPipeRadii = CreateFittedHistogram("hPipeRadii", + "Radius [cm]", + pipeRadii, + {}, + "Pipe ID"); + TH1F* hPipeXPos = CreateFittedHistogram("hPipeXPos", + "X Position [cm]", + pipeXPos, + {}, + "Pipe ID"); + TH1F* hPipeZPos = CreateFittedHistogram("hPipeZPos", + "Z Position [cm]", + pipeZPos, + {}, + "Pipe ID"); + TH1F* hPipeRotations = CreateFittedHistogram("hPipeRotations", + "Rotation [rad]", + pipeRotation, + {}, + "Pipe ID"); + + // Create a canvas for the pipe parameters + TCanvas *cPipeParams = new TCanvas("cPipeParams", "Pipe Parameters", 1200, 400); + cPipeParams->Divide(4, 1); + cPipeParams->cd(1); + hPipeRadii->Draw(""); + cPipeParams->cd(2); + hPipeXPos->Draw(""); + cPipeParams->cd(3); + hPipeZPos->Draw(""); + cPipeParams->cd(4); + hPipeRotations->Draw(""); + cPipeParams->SetGrid(); + cPipeParams->Update(); + // Save the canvas as a PNG file + cPipeParams->SaveAs("pipe_parameters.png"); + + + TFile *f = new TFile(outFile,"RECREATE"); + cXY->Write(); + cX->Write(); + cY->Write(); + cFittedMeans->Write(); + cFittedMomentumMeans->Write(); + cPipeParams->Write(); + + f->Close(); + + std::cout << "Saving events to file" << std::endl; + + ROOT::RDF::RSnapshotOptions opts; + // opts.fMode = "UPDATE"; + // d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); +} diff --git a/benchmarks/beamline/beamGPS.mac b/benchmarks/beamline/beamlineGPS.mac similarity index 100% rename from benchmarks/beamline/beamGPS.mac rename to benchmarks/beamline/beamlineGPS.mac diff --git a/benchmarks/beamline/analysis.C b/benchmarks/beamline/phasespaceAnalysis.C similarity index 99% rename from benchmarks/beamline/analysis.C rename to benchmarks/beamline/phasespaceAnalysis.C index f37c2ce1..770e52cd 100644 --- a/benchmarks/beamline/analysis.C +++ b/benchmarks/beamline/phasespaceAnalysis.C @@ -19,7 +19,7 @@ using RVecS = ROOT::VecOps::RVec; using RNode = ROOT::RDF::RNode; -void analysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", +void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", TString outFile = "output.root", std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ diff --git a/benchmarks/beamline/phasespaceGPS.mac b/benchmarks/beamline/phasespaceGPS.mac new file mode 100644 index 00000000..9ef8ed23 --- /dev/null +++ b/benchmarks/beamline/phasespaceGPS.mac @@ -0,0 +1,11 @@ +/gps/ang/type beam2d +/gps/ang/sigma_x 0.011 rad # 11 mrad +/gps/ang/sigma_y 0.011 rad # 11 mrad +/gps/pos/type Beam +/gps/pos/sigma_x 0.119 mm +/gps/pos/sigma_y 0.0107 mm +/gps/energy 17.846 GeV +#/gps/energy 18 GeV +/gps/particle e- + +/run/beamOn 100000 \ No newline at end of file From 67d765c8a33a9c808e75e41f9411bed67bae35d9 Mon Sep 17 00:00:00 2001 From: simonge Date: Fri, 2 May 2025 14:16:13 +0100 Subject: [PATCH 07/65] Change name of functors --- benchmarks/beamline/beamlineAnalysis.C | 2 +- benchmarks/beamline/phasespaceAnalysis.C | 14 +++++++------- benchmarks/beamline/phasespaceGPS.mac | 2 +- .../beamline/{functors.h => shared_functions.h} | 0 4 files changed, 9 insertions(+), 9 deletions(-) rename benchmarks/beamline/{functors.h => shared_functions.h} (100%) diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C index abd677a8..32cd51cd 100644 --- a/benchmarks/beamline/beamlineAnalysis.C +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -11,7 +11,7 @@ #include "ROOT/RDataFrame.hxx" #include "ROOT/RDF/RInterface.hxx" #include "ROOT/RVec.hxx" -#include "functors.h" +#include "shared_functions.h" #include "TCanvas.h" #include "TStyle.h" diff --git a/benchmarks/beamline/phasespaceAnalysis.C b/benchmarks/beamline/phasespaceAnalysis.C index 770e52cd..6ee5b05c 100644 --- a/benchmarks/beamline/phasespaceAnalysis.C +++ b/benchmarks/beamline/phasespaceAnalysis.C @@ -11,7 +11,7 @@ #include "ROOT/RDataFrame.hxx" #include "ROOT/RDF/RInterface.hxx" #include "ROOT/RVec.hxx" -#include "functors.h" +#include "shared_functions.h" #include "TCanvas.h" #include "TStyle.h" @@ -305,9 +305,9 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea } // Save 2D canvases as pngs - cXY->SaveAs("beamspot.png"); - cX->SaveAs("x_px.png"); - cY->SaveAs("y_py.png"); + // cXY->SaveAs("beamspot.png"); + // cX->SaveAs("x_px.png"); + // cY->SaveAs("y_py.png"); // --------------------------------------------------------------------------- // Create histograms showing the fitted means and standard deviations of the positions and momenta @@ -375,7 +375,7 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea cFittedMeans->SetGrid(); cFittedMeans->Update(); // Save the canvas as a PNG file - cFittedMeans->SaveAs("fitted_means_stddevs.png"); + // cFittedMeans->SaveAs("fitted_means_stddevs.png"); // Create a canvas for the fitted momentum means and standard deviations TCanvas *cFittedMomentumMeans = new TCanvas("cFittedMomentumMeans", "Fitted Momentum Means and Std Deviation", 1200, 800); @@ -391,7 +391,7 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea cFittedMomentumMeans->SetGrid(); cFittedMomentumMeans->Update(); // Save the canvas as a PNG file - cFittedMomentumMeans->SaveAs("fitted_momentum_means_stddevs.png"); + // cFittedMomentumMeans->SaveAs("fitted_momentum_means_stddevs.png"); // ----------------------------------------------------------------------------- @@ -432,7 +432,7 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea cPipeParams->SetGrid(); cPipeParams->Update(); // Save the canvas as a PNG file - cPipeParams->SaveAs("pipe_parameters.png"); + // cPipeParams->SaveAs("pipe_parameters.png"); TFile *f = new TFile(outFile,"RECREATE"); diff --git a/benchmarks/beamline/phasespaceGPS.mac b/benchmarks/beamline/phasespaceGPS.mac index 9ef8ed23..5d33ceff 100644 --- a/benchmarks/beamline/phasespaceGPS.mac +++ b/benchmarks/beamline/phasespaceGPS.mac @@ -8,4 +8,4 @@ #/gps/energy 18 GeV /gps/particle e- -/run/beamOn 100000 \ No newline at end of file +/run/beamOn 1000000 \ No newline at end of file diff --git a/benchmarks/beamline/functors.h b/benchmarks/beamline/shared_functions.h similarity index 100% rename from benchmarks/beamline/functors.h rename to benchmarks/beamline/shared_functions.h From 3afabcb8d8f3e470c958d052a716537b483d7049 Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 4 Jun 2025 14:53:53 +0100 Subject: [PATCH 08/65] Remove image and acceptance benchmarks for now --- benchmarks/beamline/Snakefile | 15 - benchmarks/beamline/phasespaceAnalysis.C | 453 ----------------------- benchmarks/beamline/phasespaceGPS.mac | 11 - 3 files changed, 479 deletions(-) delete mode 100644 benchmarks/beamline/phasespaceAnalysis.C delete mode 100644 benchmarks/beamline/phasespaceGPS.mac diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index dc091e03..88de43c1 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -15,21 +15,6 @@ rule beamline_sim: --outputFile {output} \ """ -rule beamline_geometry_image: - output: - "geometry_image{tag}.png" - params: - xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - shell: - """ - exec npsim \ - --compactFile {params.xml} \ - --runType vis \ - --enableG4GPS \ - --macroFile visbeam.mac - mv geometry_image.png {output} - """ - rule beamline_analysis: input: data="/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root", diff --git a/benchmarks/beamline/phasespaceAnalysis.C b/benchmarks/beamline/phasespaceAnalysis.C deleted file mode 100644 index 6ee5b05c..00000000 --- a/benchmarks/beamline/phasespaceAnalysis.C +++ /dev/null @@ -1,453 +0,0 @@ -// Script to plot the x and y positions and momentum of beamline particles as they pass through the magnets - -#include -#include "DD4hep/Detector.h" -#include "DDRec/CellIDPositionConverter.h" -#include "edm4hep/MCParticleCollection.h" -#include "edm4hep/SimTrackerHitCollection.h" -#include "edm4hep/SimCalorimeterHitCollection.h" -#include "edm4hep/Vector3f.h" -#include "edm4hep/Vector3d.h" -#include "ROOT/RDataFrame.hxx" -#include "ROOT/RDF/RInterface.hxx" -#include "ROOT/RVec.hxx" -#include "shared_functions.h" -#include "TCanvas.h" -#include "TStyle.h" - - -using RVecS = ROOT::VecOps::RVec; -using RNode = ROOT::RDF::RNode; - -void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", - TString outFile = "output.root", - std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ - - //Set ROOT style - gStyle->SetPadLeftMargin(0.1); // Set left margin - gStyle->SetPadRightMargin(0.0); // Set right margin - gStyle->SetPadTopMargin(0.0); // Set top margin - gStyle->SetPadBottomMargin(0.1);// Set bottom margin - gStyle->SetTitleAlign(13); - gStyle->SetTitleX(0.12); // Place the title on the top right - gStyle->SetTitleY(0.985); // Place the title on the top right - gStyle->SetTitleSize(0.08, "t"); - gStyle->SetTitleXOffset(1.0); // Adjust y-axis title offset - gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset - gStyle->SetOptStat(0); - - //Set implicit multi-threading - ROOT::EnableImplicitMT(); - - //Load the detector config - dd4hep::Detector& detector = dd4hep::Detector::getInstance(); - detector.fromCompact(compactName); - - ROOT::RDataFrame d0("events",inFile, {"BackwardsBeamlineHits"}); - RNode d1 = d0; - RVecS colNames = d1.GetColumnNames(); - - //Set number of entries to process - // d1 = d1.Range(0,1000); - - //Get the collection - std::string readoutName = "BackwardsBeamlineHits"; - - std::cout << "Running lazy RDataframe execution" << std::endl; - - if(Any(colNames==readoutName)){ - - d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName}) - .Define("endID",getSubID("end",detector),{readoutName}) - .Define("pipeParameters",getVolumeParametersFromCellID(detector),{readoutName}) - .Define("pipeRadius",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec radii; - for (const auto& param : params) { - radii.push_back(param.radius); - } - return radii; - }, {"pipeParameters"}) - .Define("xdet",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec xPos; - for (const auto& param : params) { - xPos.push_back(param.xPos); - } - return xPos; - }, {"pipeParameters"}) - .Define("zdet",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec zPos; - for (const auto& param : params) { - zPos.push_back(param.zPos); - } - return zPos; - }, {"pipeParameters"}) - .Define("rotation",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec rotation; - for (const auto& param : params) { - rotation.push_back(param.rotation); - } - return rotation; - }, {"pipeParameters"}); - - - //global x,y,z position and momentum - d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x") - .Define("ypos_global","BackwardsBeamlineHits.position.y") - .Define("zpos_global","BackwardsBeamlineHits.position.z") - .Define("px_global","BackwardsBeamlineHits.momentum.x") - .Define("py_global","BackwardsBeamlineHits.momentum.y") - .Define("pz_global","BackwardsBeamlineHits.momentum.z"); - - d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName}) - .Define("xpos","hitPosMom[0]") - .Define("ypos","hitPosMom[1]") - .Define("zpos","hitPosMom[2]") - .Define("xmomMag","hitPosMom[3]") - .Define("ymomMag","hitPosMom[4]") - .Define("zmomMag","hitPosMom[5]") - .Define("momMag","sqrt(xmomMag*xmomMag+ymomMag*ymomMag+zmomMag*zmomMag)") - .Define("xmom","xmomMag/momMag") - .Define("ymom","ymomMag/momMag") - .Define("zmom","zmomMag/momMag"); - - } - else{ - std::cout << "Collection " << readoutName << " not found in file" << std::endl; - return; - } - - // Calculate the maximum pipe radius for plotting - auto maxPipeRadius = 1.2*d1.Max("pipeRadius").GetValue(); - - std::cout << "Executing Analysis and creating histograms" << std::endl; - - //Create array of histogram results - std::map> hHistsxy; - std::map> hHistsxyZoom; - std::map> hHistsxpx; - std::map> hHistsypy; - - std::map xMeans; - std::map yMeans; - std::map xStdDevs; - std::map yStdDevs; - std::map pxMeans; - std::map pyMeans; - std::map pxStdDevs; - std::map pyStdDevs; - - //Fit paremeter and error maps - std::map xMeanFit; - std::map yMeanFit; - std::map xMeanFitErr; - std::map yMeanFitErr; - std::map xStdDevFit; - std::map yStdDevFit; - std::map xStdDevFitErr; - std::map yStdDevFitErr; - std::map pxMeanFit; - std::map pyMeanFit; - std::map pxMeanFitErr; - std::map pyMeanFitErr; - std::map pxStdDevFit; - std::map pyStdDevFit; - std::map pxStdDevFitErr; - std::map pyStdDevFitErr; - - std::map pipeRadii; - std::map pipeXPos; - std::map pipeZPos; - std::map pipeRotation; - - auto xmin = d1.Min("xpos").GetValue(); - auto xmax = d1.Max("xpos").GetValue(); - auto ymin = d1.Min("ypos").GetValue(); - auto ymax = d1.Max("ypos").GetValue(); - auto pxmin = d1.Min("xmom").GetValue(); - auto pxmax = d1.Max("xmom").GetValue(); - auto pymin = d1.Min("ymom").GetValue(); - auto pymax = d1.Max("ymom").GetValue(); - - //Create histograms - for(int i=0; i<=7; i++){ - - std::string name = "pipeID"; - name += std::to_string(i); - auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") - .Define("yposf","ypos["+std::to_string(i)+"]") - .Define("xmomf","xmom["+std::to_string(i)+"]") - .Define("ymomf","ymom["+std::to_string(i)+"]") - .Define("pipeRadiusf","pipeRadius["+std::to_string(i)+"]") - .Define("xdetf","xdet["+std::to_string(i)+"]") - .Define("zdetf","zdet["+std::to_string(i)+"]") - .Define("rotationf","rotation["+std::to_string(i)+"]"); - - - //Calculate Min and Max values - auto xminf = filterDF.Min("xposf").GetValue(); - auto xmaxf = filterDF.Max("xposf").GetValue(); - auto yminf = filterDF.Min("yposf").GetValue(); - auto ymaxf = filterDF.Max("yposf").GetValue(); - auto pxminf = filterDF.Min("xmomf").GetValue(); - auto pxmaxf = filterDF.Max("xmomf").GetValue(); - auto pyminf = filterDF.Min("ymomf").GetValue(); - auto pymaxf = filterDF.Max("ymomf").GetValue(); - // Calculate means and standard deviations - xMeans[name] = filterDF.Mean("xposf").GetValue(); - yMeans[name] = filterDF.Mean("yposf").GetValue(); - xStdDevs[name] = filterDF.StdDev("xposf").GetValue(); - yStdDevs[name] = filterDF.StdDev("yposf").GetValue(); - pxMeans[name] = filterDF.Mean("xmomf").GetValue(); - pyMeans[name] = filterDF.Mean("ymomf").GetValue(); - pxStdDevs[name] = filterDF.StdDev("xmomf").GetValue(); - pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue(); - - // Calculate axes for zoomed beamspot histogram so that it is quare around the mean x and y - double halfrange = std::max({xMeans[name]-xminf, xmaxf-xMeans[name], yMeans[name]-yminf, ymaxf-yMeans[name]}); - double xMinZoom = xMeans[name] - halfrange; - double xMaxZoom = xMeans[name] + halfrange; - double yMinZoom = yMeans[name] - halfrange; - double yMaxZoom = yMeans[name] + halfrange; - - TString beamspotName = "Beamspot ID"+std::to_string(i)+";x offset [cm]; y offset [cm]"; - TString xyname = name+";x Offset [cm]; y Offset [cm]"; - TString xname = name+";x Offset [cm]; x trajectory component"; - TString yname = name+";y Offset [cm]; y trajectory component"; - hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf"); - hHistsxyZoom[name] = filterDF.Histo2D({xyname,xyname,100,xMinZoom,xMaxZoom,100,yMinZoom,yMaxZoom},"xposf","yposf"); - hHistsxpx[name] = filterDF.Histo2D({xname,xname,400,xmin,xmax,400,pxmin,pxmax},"xposf","xmomf"); - hHistsypy[name] = filterDF.Histo2D({yname,yname,400,ymin,ymax,400,pymin,pymax},"yposf","ymomf"); - - //Parameters of the pipe - pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); - pipeXPos[name] = filterDF.Max("xdetf").GetValue(); - pipeZPos[name] = filterDF.Max("zdetf").GetValue(); - pipeRotation[name] = filterDF.Max("rotationf").GetValue(); - - //Fit gaussian to the x, y, px and py distributions - auto xhist = hHistsxy[name]->ProjectionX(); - auto yhist = hHistsxy[name]->ProjectionY(); - auto pxhist = hHistsxpx[name]->ProjectionY(); - auto pyhist = hHistsypy[name]->ProjectionY(); - xhist->Fit("gaus","Q"); - yhist->Fit("gaus","Q"); - pxhist->Fit("gaus","Q"); - pyhist->Fit("gaus","Q"); - //Get the fit parameters and errors - xMeanFit[name] = xhist->GetFunction("gaus")->GetParameter(1); - yMeanFit[name] = yhist->GetFunction("gaus")->GetParameter(1); - xMeanFitErr[name] = xhist->GetFunction("gaus")->GetParError(1); - yMeanFitErr[name] = yhist->GetFunction("gaus")->GetParError(1); - xStdDevFit[name] = xhist->GetFunction("gaus")->GetParameter(2); - yStdDevFit[name] = yhist->GetFunction("gaus")->GetParameter(2); - xStdDevFitErr[name] = xhist->GetFunction("gaus")->GetParError(2); - yStdDevFitErr[name] = yhist->GetFunction("gaus")->GetParError(2); - pxMeanFit[name] = pxhist->GetFunction("gaus")->GetParameter(1); - pyMeanFit[name] = pyhist->GetFunction("gaus")->GetParameter(1); - pxMeanFitErr[name] = pxhist->GetFunction("gaus")->GetParError(1); - pyMeanFitErr[name] = pyhist->GetFunction("gaus")->GetParError(1); - pxStdDevFit[name] = pxhist->GetFunction("gaus")->GetParameter(2); - pyStdDevFit[name] = pyhist->GetFunction("gaus")->GetParameter(2); - pxStdDevFitErr[name] = pxhist->GetFunction("gaus")->GetParError(2); - pyStdDevFitErr[name] = pyhist->GetFunction("gaus")->GetParError(2); - - } - - // Create histograms of the beamspot - TCanvas *cXY = new TCanvas("beamspot_canvas","beamspot_canvas",3000,1600); - cXY->Divide(4,2); - int i=1; - for(auto [name,h] : hHistsxy){ - // Get the pipe radius for this histogram - auto pipeRadius = pipeRadii[name]; - cXY->cd(i++); - - h->Draw("col"); - //Draw cicle - TEllipse *circle = new TEllipse(0,0,pipeRadius); - circle->SetLineColor(kRed); - circle->SetLineWidth(2); - circle->SetFillStyle(0); - circle->Draw("same"); - - // Add zoomed version in the top-right corner - TPad *pad = new TPad("zoomPad", "Zoomed View", 0.65, 0.65, 1.0, 1.0); - pad->SetFillStyle(0); // Transparent background - pad->Draw(); - pad->cd(); - // Draw the zoomed histogram without its title or axis titles - hHistsxyZoom[name]->SetTitle(""); - hHistsxyZoom[name]->GetXaxis()->SetTitle(""); - hHistsxyZoom[name]->GetYaxis()->SetTitle(""); - hHistsxyZoom[name]->Draw("col"); - cXY->cd(); // Return to the main canvas - } - - // x-px canvas - TCanvas *cX = new TCanvas("x_px_canvas","x_px_canvas",3000,1600); - cX->Divide(4,2); - - i=1; - //Write histograms to file - for(auto& h : hHistsxpx){ - cX->cd(i++); - h.second->Draw("col"); - } - - // y-py canvas - TCanvas *cY = new TCanvas("y_py_canvas","y_py_canvas",3000,1600); - cY->Divide(4,2); - - i=1; - for(auto& h : hHistsypy){ - cY->cd(i++); - h.second->Draw("col"); - } - - // Save 2D canvases as pngs - // cXY->SaveAs("beamspot.png"); - // cX->SaveAs("x_px.png"); - // cY->SaveAs("y_py.png"); - - // --------------------------------------------------------------------------- - // Create histograms showing the fitted means and standard deviations of the positions and momenta - // --------------------------------------------------------------------------- - - // Create histograms for fitted X means and standard deviations - TH1F* hFittedXMeans = CreateFittedHistogram("hFittedXMeans", - "Mean X Offset [cm]", - xMeanFit, - xMeanFitErr, - "Pipe ID"); - - TH1F* hFittedXStdDevs = CreateFittedHistogram("hFittedXStdDevs", - "Std Deviation X Offset [cm]", - xStdDevFit, - xStdDevFitErr, - "Pipe ID"); - - // Create histograms for fitted Y means and standard deviations - TH1F* hFittedYMeans = CreateFittedHistogram("hFittedYMeans", - "Mean Y Offset [cm]", - yMeanFit, - yMeanFitErr, - "Pipe ID"); - - TH1F* hFittedYStdDevs = CreateFittedHistogram("hFittedYStdDevs", - "Std Deviation Y Offset [cm]", - yStdDevFit, - yStdDevFitErr, - "Pipe ID"); - - TH1F* hFittedPxMeans = CreateFittedHistogram("hFittedPxMeans", - "Mean Px", - pxMeanFit, - pxMeanFitErr, - "Pipe ID"); - TH1F* hFittedPyMeans = CreateFittedHistogram("hFittedPyMeans", - "Mean Py", - pyMeanFit, - pyMeanFitErr, - "Pipe ID"); - TH1F* hFittedPxStdDevs = CreateFittedHistogram("hFittedPxStdDevs", - "Std Deviation Px", - pxStdDevFit, - pxStdDevFitErr, - "Pipe ID"); - TH1F* hFittedPyStdDevs = CreateFittedHistogram("hFittedPyStdDevs", - "Std Deviation Py", - pyStdDevFit, - pyStdDevFitErr, - "Pipe ID"); - - - // Create a canvas for the fitted beamspot means and standard deviations - TCanvas *cFittedMeans = new TCanvas("cFittedMeans", "Fitted Beamspot Means and Std Deviation", 1200, 800); - cFittedMeans->Divide(2, 2); - cFittedMeans->cd(1); - hFittedXMeans->Draw("E1"); // "E1" draws error bars - cFittedMeans->cd(2); - hFittedXStdDevs->Draw("E1"); // "E1" draws error bars - cFittedMeans->cd(3); - hFittedYMeans->Draw("E1"); // "E1" draws error bars - cFittedMeans->cd(4); - hFittedYStdDevs->Draw("E1"); // "E1" draws error bars - cFittedMeans->SetGrid(); - cFittedMeans->Update(); - // Save the canvas as a PNG file - // cFittedMeans->SaveAs("fitted_means_stddevs.png"); - - // Create a canvas for the fitted momentum means and standard deviations - TCanvas *cFittedMomentumMeans = new TCanvas("cFittedMomentumMeans", "Fitted Momentum Means and Std Deviation", 1200, 800); - cFittedMomentumMeans->Divide(2, 2); - cFittedMomentumMeans->cd(1); - hFittedPxMeans->Draw("E1"); // "E1" draws error bars - cFittedMomentumMeans->cd(2); - hFittedPxStdDevs->Draw("E1"); // "E1" draws error bars - cFittedMomentumMeans->cd(3); - hFittedPyMeans->Draw("E1"); // "E1" draws error bars - cFittedMomentumMeans->cd(4); - hFittedPyStdDevs->Draw("E1"); // "E1" draws error bars - cFittedMomentumMeans->SetGrid(); - cFittedMomentumMeans->Update(); - // Save the canvas as a PNG file - // cFittedMomentumMeans->SaveAs("fitted_momentum_means_stddevs.png"); - - - // ----------------------------------------------------------------------------- - // Create histograms of the beampipe parameters - // ----------------------------------------------------------------------------- - TH1F* hPipeRadii = CreateFittedHistogram("hPipeRadii", - "Radius [cm]", - pipeRadii, - {}, - "Pipe ID"); - TH1F* hPipeXPos = CreateFittedHistogram("hPipeXPos", - "X Position [cm]", - pipeXPos, - {}, - "Pipe ID"); - TH1F* hPipeZPos = CreateFittedHistogram("hPipeZPos", - "Z Position [cm]", - pipeZPos, - {}, - "Pipe ID"); - TH1F* hPipeRotations = CreateFittedHistogram("hPipeRotations", - "Rotation [rad]", - pipeRotation, - {}, - "Pipe ID"); - - // Create a canvas for the pipe parameters - TCanvas *cPipeParams = new TCanvas("cPipeParams", "Pipe Parameters", 1200, 400); - cPipeParams->Divide(4, 1); - cPipeParams->cd(1); - hPipeRadii->Draw(""); - cPipeParams->cd(2); - hPipeXPos->Draw(""); - cPipeParams->cd(3); - hPipeZPos->Draw(""); - cPipeParams->cd(4); - hPipeRotations->Draw(""); - cPipeParams->SetGrid(); - cPipeParams->Update(); - // Save the canvas as a PNG file - // cPipeParams->SaveAs("pipe_parameters.png"); - - - TFile *f = new TFile(outFile,"RECREATE"); - cXY->Write(); - cX->Write(); - cY->Write(); - cFittedMeans->Write(); - cFittedMomentumMeans->Write(); - cPipeParams->Write(); - - f->Close(); - - std::cout << "Saving events to file" << std::endl; - - ROOT::RDF::RSnapshotOptions opts; - // opts.fMode = "UPDATE"; - // d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); -} diff --git a/benchmarks/beamline/phasespaceGPS.mac b/benchmarks/beamline/phasespaceGPS.mac deleted file mode 100644 index 5d33ceff..00000000 --- a/benchmarks/beamline/phasespaceGPS.mac +++ /dev/null @@ -1,11 +0,0 @@ -/gps/ang/type beam2d -/gps/ang/sigma_x 0.011 rad # 11 mrad -/gps/ang/sigma_y 0.011 rad # 11 mrad -/gps/pos/type Beam -/gps/pos/sigma_x 0.119 mm -/gps/pos/sigma_y 0.0107 mm -/gps/energy 17.846 GeV -#/gps/energy 18 GeV -/gps/particle e- - -/run/beamOn 1000000 \ No newline at end of file From 99633c27222be46530e0f79e4a32c25a18034561 Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 4 Jun 2025 18:38:36 +0100 Subject: [PATCH 09/65] Prepare for CI testing --- benchmarks/beamline/Snakefile | 51 ++++++++++++++++++++------ benchmarks/beamline/beamlineAnalysis.C | 21 +++++++---- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 88de43c1..6c5639f3 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -1,10 +1,8 @@ rule beamline_sim: input: - macro="{test}GPS.mac", + macro="benchmarks/beamline/beamlineGPS.mac", output: - "/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root", - log: - "/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root.log", + "sim_output/beamline/beamlineTest{CAMPAIGN}.edm4hep.root", shell: """ exec npsim \ @@ -16,17 +14,46 @@ rule beamline_sim: """ rule beamline_analysis: - input: - data="/scratch/EIC/G4out/beamline/{test}Test{tag}.edm4hep.root", - script="{test}Analysis.C", - output: - "/scratch/EIC/ReconOut/beamline/{test}TestAnalysis{tag}.root" - log: - "/scratch/EIC/ReconOut/beamline/{test}TestAnalysis{tag}.root.log" params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + input: + script="benchmarks/beamline/beamlineAnalysis.C", + data="sim_output/beamline/beamlineTest{CAMPAIGN}.edm4hep.root", + output: + rootfile="sim_output/beamline/analysis/beamlineTestAnalysis{CAMPAIGN}.root", + beamspot_canvas="sim_output/beamline/analysis/beamspot_{CAMPAIGN}.png", + x_px_canvas="sim_output/beamline/analysis/x_px_{CAMPAIGN}.png", + y_py_canvas="sim_output/beamline/analysis/y_py_{CAMPAIGN}.png", + fitted_position_means_stdevs_canvas="sim_output/beamline/analysis/fitted_position_means_stdevs_{CAMPAIGN}.png", + fitted_momentum_means_stdevs_canvas="sim_output/beamline/analysis/fitted_momentum_means_stdevs_{CAMPAIGN}.png", + pipe_parameter_canvas="sim_output/beamline/analysis/pipe_parameter_{CAMPAIGN}.png", shell: """ - root -l -b -q 'analysis.C("{input.data}", "{output}", "{params.xml}")' + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", + "{output.beamspot_canvas}", "{output.x_px_canvas}", "{output.y_py_canvas}", + "{output.fitted_position_means_stdevs_canvas}", "{output.fitted_momentum_means_stdevs_canvas}", + "{output.pipe_parameter_canvas}")' """ +rule beamline: + input: + [ + "sim_output/beamline/analysis/beamlineTestAnalysis{CAMPAIGN}.root", + "sim_output/beamline/analysis/beamspot_{CAMPAIGN}.png", + "sim_output/beamline/analysis/x_px_{CAMPAIGN}.png", + "sim_output/beamline/analysis/y_py_{CAMPAIGN}.png", + "sim_output/beamline/analysis/fitted_position_means_stdevs_{CAMPAIGN}.png", + "sim_output/beamline/analysis/fitted_momentum_means_stdevs_{CAMPAIGN}.png", + "sim_output/beamline/analysis/pipe_parameter_{CAMPAIGN}.png", + ], + output: + directory("results/beamline/{CAMPAIGN}/") + shell: + """ + mkdir {output} + cp {input} {output} + """ + +rule beamline_local: + input: + "results/beamline/local/" \ No newline at end of file diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C index 32cd51cd..64b251b2 100644 --- a/benchmarks/beamline/beamlineAnalysis.C +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -21,7 +21,14 @@ using RNode = ROOT::RDF::RNode; void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", TString outFile = "output.root", - std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ + std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml", + TString beamspotCanvasName = "beamspot_canvas.png", + TString x_pxCanvasName = "x_px_canvas.png", + TString y_pyCanvasName = "y_py_canvas.png", + TString fittedPositionMeansCanvasName = "fitted_means_stddevs.png", + TString fittedMomentumMeansCanvasName = "fitted_momentum_means_stddevs.png", + TString pipeParamsCanvasName = "pipe_parameters.png" + ){ //Set ROOT style gStyle->SetPadLeftMargin(0.1); // Set left margin @@ -305,9 +312,9 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b } // Save 2D canvases as pngs - cXY->SaveAs("beamspot.png"); - cX->SaveAs("x_px.png"); - cY->SaveAs("y_py.png"); + cXY->SaveAs(beamspotCanvasName); + cX->SaveAs(x_pxCanvasName); + cY->SaveAs(y_pyCanvasName); // --------------------------------------------------------------------------- // Create histograms showing the fitted means and standard deviations of the positions and momenta @@ -375,7 +382,7 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b cFittedMeans->SetGrid(); cFittedMeans->Update(); // Save the canvas as a PNG file - cFittedMeans->SaveAs("fitted_means_stddevs.png"); + cFittedMeans->SaveAs(fittedPositionMeansCanvasName); // Create a canvas for the fitted momentum means and standard deviations TCanvas *cFittedMomentumMeans = new TCanvas("cFittedMomentumMeans", "Fitted Momentum Means and Std Deviation", 1200, 800); @@ -391,7 +398,7 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b cFittedMomentumMeans->SetGrid(); cFittedMomentumMeans->Update(); // Save the canvas as a PNG file - cFittedMomentumMeans->SaveAs("fitted_momentum_means_stddevs.png"); + cFittedMomentumMeans->SaveAs(fittedMomentumMeansCanvasName); // ----------------------------------------------------------------------------- @@ -432,7 +439,7 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b cPipeParams->SetGrid(); cPipeParams->Update(); // Save the canvas as a PNG file - cPipeParams->SaveAs("pipe_parameters.png"); + cPipeParams->SaveAs(pipeParamsCanvasName); TFile *f = new TFile(outFile,"RECREATE"); From 0deb7016325b30c36c7e7f4953d172943525f5e1 Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 4 Jun 2025 18:38:50 +0100 Subject: [PATCH 10/65] Remove other benchmarks --- .gitlab-ci.yml | 106 +++++++++++++++++++++++++------------------------ Snakefile | 84 +++++++++++++++++++-------------------- 2 files changed, 96 insertions(+), 94 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c0b49fe0..2b6f3656 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -124,62 +124,64 @@ get_data: - runner_system_failure include: - - local: 'benchmarks/backgrounds/config.yml' - - local: 'benchmarks/backwards_ecal/config.yml' - - local: 'benchmarks/calo_pid/config.yml' - - local: 'benchmarks/ecal_gaps/config.yml' - - local: 'benchmarks/tracking_detectors/config.yml' - - local: 'benchmarks/tracking_performances/config.yml' - - local: 'benchmarks/tracking_performances_dis/config.yml' - - local: 'benchmarks/barrel_ecal/config.yml' - - local: 'benchmarks/barrel_hcal/config.yml' - - 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' - - local: 'benchmarks/pid/config.yml' - - local: 'benchmarks/timing/config.yml' - - local: 'benchmarks/b0_tracker/config.yml' - - local: 'benchmarks/insert_muon/config.yml' - - local: 'benchmarks/insert_tau/config.yml' - - local: 'benchmarks/zdc_sigma/config.yml' - - local: 'benchmarks/zdc_lambda/config.yml' - - local: 'benchmarks/insert_neutron/config.yml' - - local: 'benchmarks/femc_electron/config.yml' - - local: 'benchmarks/femc_photon/config.yml' - - local: 'benchmarks/femc_pi0/config.yml' + # - local: 'benchmarks/backgrounds/config.yml' + # - local: 'benchmarks/backwards_ecal/config.yml' + # - local: 'benchmarks/calo_pid/config.yml' + # - local: 'benchmarks/ecal_gaps/config.yml' + # - local: 'benchmarks/tracking_detectors/config.yml' + # - local: 'benchmarks/tracking_performances/config.yml' + # - local: 'benchmarks/tracking_performances_dis/config.yml' + # - local: 'benchmarks/barrel_ecal/config.yml' + # - local: 'benchmarks/barrel_hcal/config.yml' + # - 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' + # - local: 'benchmarks/pid/config.yml' + # - local: 'benchmarks/timing/config.yml' + # - local: 'benchmarks/b0_tracker/config.yml' + # - local: 'benchmarks/insert_muon/config.yml' + # - local: 'benchmarks/insert_tau/config.yml' + # - local: 'benchmarks/zdc_sigma/config.yml' + # - local: 'benchmarks/zdc_lambda/config.yml' + # - local: 'benchmarks/insert_neutron/config.yml' + # - local: 'benchmarks/femc_electron/config.yml' + # - local: 'benchmarks/femc_photon/config.yml' + # - local: 'benchmarks/femc_pi0/config.yml' + - local: 'benchmarks/beamline/config.yml' deploy_results: allow_failure: true stage: deploy needs: - - "collect_results:backgrounds" - - "collect_results:backwards_ecal" - - "collect_results:barrel_ecal" - - "collect_results:barrel_hcal" - - "collect_results:calo_pid" - - "collect_results:ecal_gaps" - - "collect_results:lfhcal" - - "collect_results:material_scan" - - "collect_results:pid" - - "collect_results:tracking_performance" - - "collect_results:tracking_performance_campaigns" - - "collect_results:zdc_sigma" - - "collect_results:zdc_lambda" - - "collect_results:insert_neutron" - - "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" - - "collect_results:zdc_pi0" - - "collect_results:femc_electron" - - "collect_results:femc_photon" - - "collect_results:femc_pi0" + # - "collect_results:backgrounds" + # - "collect_results:backwards_ecal" + # - "collect_results:barrel_ecal" + # - "collect_results:barrel_hcal" + # - "collect_results:calo_pid" + # - "collect_results:ecal_gaps" + # - "collect_results:lfhcal" + # - "collect_results:material_scan" + # - "collect_results:pid" + # - "collect_results:tracking_performance" + # - "collect_results:tracking_performance_campaigns" + # - "collect_results:zdc_sigma" + # - "collect_results:zdc_lambda" + # - "collect_results:insert_neutron" + # - "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" + # - "collect_results:zdc_pi0" + # - "collect_results:femc_electron" + # - "collect_results:femc_photon" + # - "collect_results:femc_pi0" + - "collect_results/beamline" script: - snakemake $SNAKEMAKE_FLAGS --cores 1 results/metadata.json - find results -print | sort | tee summary.txt diff --git a/Snakefile b/Snakefile index c001f8b0..3350fb35 100644 --- a/Snakefile +++ b/Snakefile @@ -30,27 +30,27 @@ def find_epic_libraries(): return libs -include: "benchmarks/backgrounds/Snakefile" -include: "benchmarks/backwards_ecal/Snakefile" -include: "benchmarks/barrel_ecal/Snakefile" -include: "benchmarks/calo_pid/Snakefile" -include: "benchmarks/ecal_gaps/Snakefile" -include: "benchmarks/material_scan/Snakefile" -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" -include: "benchmarks/zdc_pi0/Snakefile" -include: "benchmarks/zdc_sigma/Snakefile" -include: "benchmarks/insert_neutron/Snakefile" -include: "benchmarks/insert_tau/Snakefile" -include: "benchmarks/femc_electron/Snakefile" -include: "benchmarks/femc_photon/Snakefile" -include: "benchmarks/femc_pi0/Snakefile" +#include: "benchmarks/backgrounds/Snakefile" +#include: "benchmarks/backwards_ecal/Snakefile" +#include: "benchmarks/barrel_ecal/Snakefile" +#include: "benchmarks/calo_pid/Snakefile" +#include: "benchmarks/ecal_gaps/Snakefile" +#include: "benchmarks/material_scan/Snakefile" +#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" +#include: "benchmarks/zdc_pi0/Snakefile" +#include: "benchmarks/zdc_sigma/Snakefile" +#include: "benchmarks/insert_neutron/Snakefile" +#include: "benchmarks/insert_tau/Snakefile" +#include: "benchmarks/femc_electron/Snakefile" +#include: "benchmarks/femc_photon/Snakefile" +#include: "benchmarks/femc_pi0/Snakefile" include: "benchmarks/beamline/Snakefile" use_s3 = config["remote_provider"].lower() == "s3" @@ -121,24 +121,24 @@ awk -f {input.converter} {input.notebook} > {output} """ -rule metadata: - output: - "results/metadata.json" - shell: - """ -cat > {output} < {output} < Date: Wed, 4 Jun 2025 19:33:55 +0100 Subject: [PATCH 11/65] Add missing config.yml --- benchmarks/beamline/config.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 benchmarks/beamline/config.yml diff --git a/benchmarks/beamline/config.yml b/benchmarks/beamline/config.yml new file mode 100644 index 00000000..996a5fb4 --- /dev/null +++ b/benchmarks/beamline/config.yml @@ -0,0 +1,26 @@ +sim:beamline: + extends: .det_benchmark + stage: simulate + script: + - | + snakemake --cache --cores 5 \ + sim_output/beamline/beamlineTestlocal.edm4hep.root + +bench:beamline: + extends: .det_benchmark + stage: benchmarks + needs: + - ["sim:beamline"] + script: + - snakemake $SNAKEMAKE_FLAGS --cores 3 beamline_local + +collect_results:beamline: + extends: .det_benchmark + stage: collect + needs: + - "bench:beamline" + script: + - ls -lrht + - mv results{,_save}/ # move results directory out of the way to preserve it + - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output beamline_local + - mv results{_save,}/ From 7a583553acbc7aa5f819d26b7b8aa1cac9edd1ed Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 4 Jun 2025 19:39:19 +0100 Subject: [PATCH 12/65] Correct typo --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2b6f3656..bd6d0383 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -181,7 +181,7 @@ deploy_results: # - "collect_results:femc_electron" # - "collect_results:femc_photon" # - "collect_results:femc_pi0" - - "collect_results/beamline" + - "collect_results:beamline" script: - snakemake $SNAKEMAKE_FLAGS --cores 1 results/metadata.json - find results -print | sort | tee summary.txt From 5cd62be1c807b637816ce19a98a939dcaa3f0a0c Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 17 Jun 2025 17:16:26 +0100 Subject: [PATCH 13/65] Re-enable other benchmarks and update cores --- Snakefile | 42 +++++++++++++++++----------------- benchmarks/beamline/config.yml | 4 ++-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Snakefile b/Snakefile index 3350fb35..5e374afd 100644 --- a/Snakefile +++ b/Snakefile @@ -30,27 +30,27 @@ def find_epic_libraries(): return libs -#include: "benchmarks/backgrounds/Snakefile" -#include: "benchmarks/backwards_ecal/Snakefile" -#include: "benchmarks/barrel_ecal/Snakefile" -#include: "benchmarks/calo_pid/Snakefile" -#include: "benchmarks/ecal_gaps/Snakefile" -#include: "benchmarks/material_scan/Snakefile" -#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" -#include: "benchmarks/zdc_pi0/Snakefile" -#include: "benchmarks/zdc_sigma/Snakefile" -#include: "benchmarks/insert_neutron/Snakefile" -#include: "benchmarks/insert_tau/Snakefile" -#include: "benchmarks/femc_electron/Snakefile" -#include: "benchmarks/femc_photon/Snakefile" -#include: "benchmarks/femc_pi0/Snakefile" +include: "benchmarks/backgrounds/Snakefile" +include: "benchmarks/backwards_ecal/Snakefile" +include: "benchmarks/barrel_ecal/Snakefile" +include: "benchmarks/calo_pid/Snakefile" +include: "benchmarks/ecal_gaps/Snakefile" +include: "benchmarks/material_scan/Snakefile" +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" +include: "benchmarks/zdc_pi0/Snakefile" +include: "benchmarks/zdc_sigma/Snakefile" +include: "benchmarks/insert_neutron/Snakefile" +include: "benchmarks/insert_tau/Snakefile" +include: "benchmarks/femc_electron/Snakefile" +include: "benchmarks/femc_photon/Snakefile" +include: "benchmarks/femc_pi0/Snakefile" include: "benchmarks/beamline/Snakefile" use_s3 = config["remote_provider"].lower() == "s3" diff --git a/benchmarks/beamline/config.yml b/benchmarks/beamline/config.yml index 996a5fb4..ce147524 100644 --- a/benchmarks/beamline/config.yml +++ b/benchmarks/beamline/config.yml @@ -3,7 +3,7 @@ sim:beamline: stage: simulate script: - | - snakemake --cache --cores 5 \ + snakemake --cache --cores 2 \ sim_output/beamline/beamlineTestlocal.edm4hep.root bench:beamline: @@ -12,7 +12,7 @@ bench:beamline: needs: - ["sim:beamline"] script: - - snakemake $SNAKEMAKE_FLAGS --cores 3 beamline_local + - snakemake $SNAKEMAKE_FLAGS --cores 2 beamline_local collect_results:beamline: extends: .det_benchmark From 312ca425c4b70607cb4091137f4d7bb40956a55c Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 18 Jun 2025 18:29:42 +0100 Subject: [PATCH 14/65] Add some pass fail checks --- benchmarks/beamline/beamlineAnalysis.C | 51 ++++++++++++++++++++++++-- benchmarks/beamline/beamlineGPS.mac | 6 +-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C index 64b251b2..851c4fd0 100644 --- a/benchmarks/beamline/beamlineAnalysis.C +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -19,7 +19,7 @@ using RVecS = ROOT::VecOps::RVec; using RNode = ROOT::RDF::RNode; -void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", +int beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", TString outFile = "output.root", std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml", TString beamspotCanvasName = "beamspot_canvas.png", @@ -46,6 +46,8 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b //Set implicit multi-threading ROOT::EnableImplicitMT(); + int pass = 0; + //Load the detector config dd4hep::Detector& detector = dd4hep::Detector::getInstance(); detector.fromCompact(compactName); @@ -56,6 +58,9 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b //Set number of entries to process // d1 = d1.Range(0,1000); + int nEntries = d1.Count().GetValue(); + float acceptableLoss = 0.99; // Set the acceptable loss percentage to 1% + float acceptableEntries = nEntries * acceptableLoss; //Get the collection std::string readoutName = "BackwardsBeamlineHits"; @@ -120,7 +125,7 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b } else{ std::cout << "Collection " << readoutName << " not found in file" << std::endl; - return; + return 1; } // Calculate the maximum pipe radius for plotting @@ -199,6 +204,32 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b auto pxmaxf = filterDF.Max("xmomf").GetValue(); auto pyminf = filterDF.Min("ymomf").GetValue(); auto pymaxf = filterDF.Max("ymomf").GetValue(); + // If the min and max values are equal, set them to a small value + if(xminf == xmaxf) { + xminf -= 0.01; + xmaxf += 0.01; + pass = 1; // Set pass to 1 if xminf == xmaxf + std::cout << "Warning: xminf == xmaxf for pipe ID " << i << ", likely no hits." << std::endl; + } + if(yminf == ymaxf) { + yminf -= 0.01; + ymaxf += 0.01; + pass = 1; // Set pass to 1 if yminf == ymaxf + std::cout << "Warning: yminf == ymaxf for pipe ID " << i << ", likely no hits." << std::endl; + } + if(pxminf == pxmaxf) { + pxminf -= 0.01; + pxmaxf += 0.01; + pass = 1; // Set pass to 1 if pxminf == pxmaxf + std::cout << "Warning: pxminf == pxmaxf for pipe ID " << i << ", likely no hits." << std::endl; + } + if(pyminf == pymaxf) { + pyminf -= 0.01; + pymaxf += 0.01; + pass = 1; // Set pass to 1 if pyminf == pymaxf + std::cout << "Warning: pyminf == pymaxf for pipe ID " << i << ", likely no hits." << std::endl; + } + // Calculate means and standard deviations xMeans[name] = filterDF.Mean("xposf").GetValue(); yMeans[name] = filterDF.Mean("yposf").GetValue(); @@ -227,6 +258,7 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b //Parameters of the pipe pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); + std::cout << "Pipe ID: " << name << " Radius: " << pipeRadii[name] << std::endl; pipeXPos[name] = filterDF.Max("xdetf").GetValue(); pipeZPos[name] = filterDF.Max("zdetf").GetValue(); pipeRotation[name] = filterDF.Max("rotationf").GetValue(); @@ -265,6 +297,14 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b cXY->Divide(4,2); int i=1; for(auto [name,h] : hHistsxy){ + + // Check histogram entries + if(h->GetEntries() < acceptableEntries){ + std::cout << "Warning: Only " << h->GetEntries()/nEntries << " of particles contributing to histogram " << name + << " , which is below the accepted threshold of " << acceptableEntries << std::endl; + pass = 1; + } + // Get the pipe radius for this histogram auto pipeRadius = pipeRadii[name]; cXY->cd(i++); @@ -452,9 +492,12 @@ void beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b f->Close(); - std::cout << "Saving events to file" << std::endl; + std::cout << "Analysis complete. Results saved to " << outFile << std::endl; + return pass; + + // std::cout << "Saving events to file" << std::endl; - ROOT::RDF::RSnapshotOptions opts; + // ROOT::RDF::RSnapshotOptions opts; // opts.fMode = "UPDATE"; // d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); } diff --git a/benchmarks/beamline/beamlineGPS.mac b/benchmarks/beamline/beamlineGPS.mac index 8ecd6bbb..d3cdc21b 100644 --- a/benchmarks/beamline/beamlineGPS.mac +++ b/benchmarks/beamline/beamlineGPS.mac @@ -4,8 +4,8 @@ /gps/pos/type Beam /gps/pos/sigma_x 0.119 mm /gps/pos/sigma_y 0.0107 mm -/gps/energy 17.846 GeV -#/gps/energy 18 GeV +#/gps/energy 17.846 GeV +/gps/energy 18 GeV /gps/particle e- -/run/beamOn 100000 \ No newline at end of file +/run/beamOn 10000 \ No newline at end of file From 02e0c0e03af4972c8c0327de57c0ebf9268ff6c2 Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 18 Jun 2025 21:14:07 +0100 Subject: [PATCH 15/65] Set sim and analysis dir by variable --- benchmarks/beamline/Snakefile | 39 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 6c5639f3..5722a943 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -1,8 +1,11 @@ -rule beamline_sim: +SIMOUTDIR="sim_output/beamline/" +ANALYSISDIR=SIMOUTDIR+"analysis/" + +rule beamline_steering_sim: input: macro="benchmarks/beamline/beamlineGPS.mac", output: - "sim_output/beamline/beamlineTest{CAMPAIGN}.edm4hep.root", + SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", shell: """ exec npsim \ @@ -13,20 +16,20 @@ rule beamline_sim: --outputFile {output} \ """ -rule beamline_analysis: +rule beamline_steering_analysis: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", input: script="benchmarks/beamline/beamlineAnalysis.C", - data="sim_output/beamline/beamlineTest{CAMPAIGN}.edm4hep.root", + data=SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", output: - rootfile="sim_output/beamline/analysis/beamlineTestAnalysis{CAMPAIGN}.root", - beamspot_canvas="sim_output/beamline/analysis/beamspot_{CAMPAIGN}.png", - x_px_canvas="sim_output/beamline/analysis/x_px_{CAMPAIGN}.png", - y_py_canvas="sim_output/beamline/analysis/y_py_{CAMPAIGN}.png", - fitted_position_means_stdevs_canvas="sim_output/beamline/analysis/fitted_position_means_stdevs_{CAMPAIGN}.png", - fitted_momentum_means_stdevs_canvas="sim_output/beamline/analysis/fitted_momentum_means_stdevs_{CAMPAIGN}.png", - pipe_parameter_canvas="sim_output/beamline/analysis/pipe_parameter_{CAMPAIGN}.png", + rootfile=ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", + beamspot_canvas=ANALYSISDIR+"beamspot_{CAMPAIGN}.png", + x_px_canvas=ANALYSISDIR+"x_px_{CAMPAIGN}.png", + y_py_canvas=ANALYSISDIR+"y_py_{CAMPAIGN}.png", + fitted_position_means_stdevs_canvas=ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", + fitted_momentum_means_stdevs_canvas=ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", + pipe_parameter_canvas=ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", shell: """ root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", @@ -38,13 +41,13 @@ rule beamline_analysis: rule beamline: input: [ - "sim_output/beamline/analysis/beamlineTestAnalysis{CAMPAIGN}.root", - "sim_output/beamline/analysis/beamspot_{CAMPAIGN}.png", - "sim_output/beamline/analysis/x_px_{CAMPAIGN}.png", - "sim_output/beamline/analysis/y_py_{CAMPAIGN}.png", - "sim_output/beamline/analysis/fitted_position_means_stdevs_{CAMPAIGN}.png", - "sim_output/beamline/analysis/fitted_momentum_means_stdevs_{CAMPAIGN}.png", - "sim_output/beamline/analysis/pipe_parameter_{CAMPAIGN}.png", + ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", + ANALYSISDIR+"beamspot_{CAMPAIGN}.png", + ANALYSISDIR+"x_px_{CAMPAIGN}.png", + ANALYSISDIR+"y_py_{CAMPAIGN}.png", + ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", + ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", + ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", ], output: directory("results/beamline/{CAMPAIGN}/") From e39d0f9d4ef4f1271a9b904bce5c75428614b33c Mon Sep 17 00:00:00 2001 From: simonge Date: Thu, 19 Jun 2025 10:38:28 +0100 Subject: [PATCH 16/65] Revert exclusion of other benchmarks --- .gitlab-ci.yml | 104 ++++++++++++++++++++++++------------------------- Snakefile | 42 ++++++++++---------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bd6d0383..31456e7e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -124,64 +124,64 @@ get_data: - runner_system_failure include: - # - local: 'benchmarks/backgrounds/config.yml' - # - local: 'benchmarks/backwards_ecal/config.yml' - # - local: 'benchmarks/calo_pid/config.yml' - # - local: 'benchmarks/ecal_gaps/config.yml' - # - local: 'benchmarks/tracking_detectors/config.yml' - # - local: 'benchmarks/tracking_performances/config.yml' - # - local: 'benchmarks/tracking_performances_dis/config.yml' - # - local: 'benchmarks/barrel_ecal/config.yml' - # - local: 'benchmarks/barrel_hcal/config.yml' - # - 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' - # - local: 'benchmarks/pid/config.yml' - # - local: 'benchmarks/timing/config.yml' - # - local: 'benchmarks/b0_tracker/config.yml' - # - local: 'benchmarks/insert_muon/config.yml' - # - local: 'benchmarks/insert_tau/config.yml' - # - local: 'benchmarks/zdc_sigma/config.yml' - # - local: 'benchmarks/zdc_lambda/config.yml' - # - local: 'benchmarks/insert_neutron/config.yml' - # - local: 'benchmarks/femc_electron/config.yml' - # - local: 'benchmarks/femc_photon/config.yml' - # - local: 'benchmarks/femc_pi0/config.yml' + - local: 'benchmarks/backgrounds/config.yml' + - local: 'benchmarks/backwards_ecal/config.yml' - local: 'benchmarks/beamline/config.yml' + - local: 'benchmarks/calo_pid/config.yml' + - local: 'benchmarks/ecal_gaps/config.yml' + - local: 'benchmarks/tracking_detectors/config.yml' + - local: 'benchmarks/tracking_performances/config.yml' + - local: 'benchmarks/tracking_performances_dis/config.yml' + - local: 'benchmarks/barrel_ecal/config.yml' + - local: 'benchmarks/barrel_hcal/config.yml' + - 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' + - local: 'benchmarks/pid/config.yml' + - local: 'benchmarks/timing/config.yml' + - local: 'benchmarks/b0_tracker/config.yml' + - local: 'benchmarks/insert_muon/config.yml' + - local: 'benchmarks/insert_tau/config.yml' + - local: 'benchmarks/zdc_sigma/config.yml' + - local: 'benchmarks/zdc_lambda/config.yml' + - local: 'benchmarks/insert_neutron/config.yml' + - local: 'benchmarks/femc_electron/config.yml' + - local: 'benchmarks/femc_photon/config.yml' + - local: 'benchmarks/femc_pi0/config.yml' deploy_results: allow_failure: true stage: deploy needs: - # - "collect_results:backgrounds" - # - "collect_results:backwards_ecal" - # - "collect_results:barrel_ecal" - # - "collect_results:barrel_hcal" - # - "collect_results:calo_pid" - # - "collect_results:ecal_gaps" - # - "collect_results:lfhcal" - # - "collect_results:material_scan" - # - "collect_results:pid" - # - "collect_results:tracking_performance" - # - "collect_results:tracking_performance_campaigns" - # - "collect_results:zdc_sigma" - # - "collect_results:zdc_lambda" - # - "collect_results:insert_neutron" - # - "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" - # - "collect_results:zdc_pi0" - # - "collect_results:femc_electron" - # - "collect_results:femc_photon" - # - "collect_results:femc_pi0" + - "collect_results:backgrounds" + - "collect_results:backwards_ecal" + - "collect_results:barrel_ecal" + - "collect_results:barrel_hcal" - "collect_results:beamline" + - "collect_results:calo_pid" + - "collect_results:ecal_gaps" + - "collect_results:lfhcal" + - "collect_results:material_scan" + - "collect_results:pid" + - "collect_results:tracking_performance" + - "collect_results:tracking_performance_campaigns" + - "collect_results:zdc_sigma" + - "collect_results:zdc_lambda" + - "collect_results:insert_neutron" + - "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" + - "collect_results:zdc_pi0" + - "collect_results:femc_electron" + - "collect_results:femc_photon" + - "collect_results:femc_pi0" script: - snakemake $SNAKEMAKE_FLAGS --cores 1 results/metadata.json - find results -print | sort | tee summary.txt diff --git a/Snakefile b/Snakefile index 5e374afd..d70cd9e4 100644 --- a/Snakefile +++ b/Snakefile @@ -121,24 +121,24 @@ awk -f {input.converter} {input.notebook} > {output} """ -#rule metadata: -# output: -# "results/metadata.json" -# shell: -# """ -#cat > {output} < {output} < Date: Thu, 19 Jun 2025 11:33:55 +0100 Subject: [PATCH 17/65] Review suggestions --- Snakefile | 2 +- benchmarks/beamline/Snakefile | 16 +++++++--------- benchmarks/beamline/config.yml | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Snakefile b/Snakefile index d70cd9e4..4e856e1d 100644 --- a/Snakefile +++ b/Snakefile @@ -33,6 +33,7 @@ def find_epic_libraries(): include: "benchmarks/backgrounds/Snakefile" include: "benchmarks/backwards_ecal/Snakefile" include: "benchmarks/barrel_ecal/Snakefile" +include: "benchmarks/beamline/Snakefile" include: "benchmarks/calo_pid/Snakefile" include: "benchmarks/ecal_gaps/Snakefile" include: "benchmarks/material_scan/Snakefile" @@ -51,7 +52,6 @@ include: "benchmarks/insert_tau/Snakefile" include: "benchmarks/femc_electron/Snakefile" include: "benchmarks/femc_photon/Snakefile" include: "benchmarks/femc_pi0/Snakefile" -include: "benchmarks/beamline/Snakefile" use_s3 = config["remote_provider"].lower() == "s3" use_xrootd = config["remote_provider"].lower() == "xrootd" diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 5722a943..bfba8748 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -40,15 +40,13 @@ rule beamline_steering_analysis: rule beamline: input: - [ - ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", - ANALYSISDIR+"beamspot_{CAMPAIGN}.png", - ANALYSISDIR+"x_px_{CAMPAIGN}.png", - ANALYSISDIR+"y_py_{CAMPAIGN}.png", - ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", - ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", - ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", - ], + ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", + ANALYSISDIR+"beamspot_{CAMPAIGN}.png", + ANALYSISDIR+"x_px_{CAMPAIGN}.png", + ANALYSISDIR+"y_py_{CAMPAIGN}.png", + ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", + ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", + ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", output: directory("results/beamline/{CAMPAIGN}/") shell: diff --git a/benchmarks/beamline/config.yml b/benchmarks/beamline/config.yml index ce147524..ef6b84e4 100644 --- a/benchmarks/beamline/config.yml +++ b/benchmarks/beamline/config.yml @@ -3,7 +3,7 @@ sim:beamline: stage: simulate script: - | - snakemake --cache --cores 2 \ + snakemake $SNAKEMAKE_FLAGS --cores 2 \ sim_output/beamline/beamlineTestlocal.edm4hep.root bench:beamline: From 81fad99121202b5349dd84191e5ba06d3e2454cc Mon Sep 17 00:00:00 2001 From: Dmitry Kalinkin Date: Thu, 19 Jun 2025 18:45:56 -0400 Subject: [PATCH 18/65] Snakefile: fix for out-of-tree running --- benchmarks/beamline/Snakefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index bfba8748..a43b5054 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -3,7 +3,7 @@ ANALYSISDIR=SIMOUTDIR+"analysis/" rule beamline_steering_sim: input: - macro="benchmarks/beamline/beamlineGPS.mac", + macro=workflow.source_path("beamlineGPS.mac"), output: SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", shell: @@ -20,7 +20,7 @@ rule beamline_steering_analysis: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", input: - script="benchmarks/beamline/beamlineAnalysis.C", + script=workflow.source_path("beamlineAnalysis.C"), data=SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", output: rootfile=ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", @@ -57,4 +57,4 @@ rule beamline: rule beamline_local: input: - "results/beamline/local/" \ No newline at end of file + "results/beamline/local/" From 6b21ff11333ae22cf9c4dc380b5213d34653abe0 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinkin Date: Thu, 19 Jun 2025 18:46:43 -0400 Subject: [PATCH 19/65] Snakefile restore indentation --- Snakefile | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Snakefile b/Snakefile index 4e856e1d..2277a081 100644 --- a/Snakefile +++ b/Snakefile @@ -122,21 +122,21 @@ awk -f {input.converter} {input.notebook} > {output} rule metadata: - output: - "results/metadata.json" - shell: - """ + output: + "results/metadata.json" + shell: + """ cat > {output} < Date: Mon, 23 Jun 2025 17:19:59 +0100 Subject: [PATCH 20/65] Add header to inputs too --- benchmarks/beamline/Snakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index a43b5054..b9478bc6 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -21,6 +21,7 @@ rule beamline_steering_analysis: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", input: script=workflow.source_path("beamlineAnalysis.C"), + header=workflow.source_path("shared_functions.h"), data=SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", output: rootfile=ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", From c7e268f10cf7c59fe51cb3a5aaa3634a4474606d Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 20 May 2025 13:51:19 +0100 Subject: [PATCH 21/65] Add low-q2 phase space electron tests --- benchmarks/beamline/phasespaceAnalysis.C | 253 +++++++++++++++++++++++ benchmarks/beamline/phasespaceGPS.mac | 13 ++ 2 files changed, 266 insertions(+) create mode 100644 benchmarks/beamline/phasespaceAnalysis.C create mode 100644 benchmarks/beamline/phasespaceGPS.mac diff --git a/benchmarks/beamline/phasespaceAnalysis.C b/benchmarks/beamline/phasespaceAnalysis.C new file mode 100644 index 00000000..4792e653 --- /dev/null +++ b/benchmarks/beamline/phasespaceAnalysis.C @@ -0,0 +1,253 @@ +// Script to plot the x and y positions and momentum of beamline particles as they pass through the magnets + +#include +#include "DD4hep/Detector.h" +#include "DDRec/CellIDPositionConverter.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/Vector3f.h" +#include "edm4hep/Vector3d.h" +#include "ROOT/RDataFrame.hxx" +#include "ROOT/RDF/RInterface.hxx" +#include "ROOT/RVec.hxx" +#include "shared_functions.h" +#include "TCanvas.h" +#include "TStyle.h" + + +using RVecS = ROOT::VecOps::RVec; +using RNode = ROOT::RDF::RNode; + +void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + TString outFile = "output.root", + std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ + + //Set ROOT style + gStyle->SetPadLeftMargin(0.1); // Set left margin + gStyle->SetPadRightMargin(0.0); // Set right margin + gStyle->SetPadTopMargin(0.0); // Set top margin + gStyle->SetPadBottomMargin(0.1);// Set bottom margin + gStyle->SetTitleAlign(13); + gStyle->SetTitleX(0.12); // Place the title on the top right + gStyle->SetTitleY(0.985); // Place the title on the top right + gStyle->SetTitleSize(0.08, "t"); + gStyle->SetTitleXOffset(1.0); // Adjust y-axis title offset + gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset + gStyle->SetOptStat(0); + + //Set implicit multi-threading + ROOT::EnableImplicitMT(); + + //Load the detector config + dd4hep::Detector& detector = dd4hep::Detector::getInstance(); + detector.fromCompact(compactName); + + ROOT::RDataFrame d0("events",inFile, {"BackwardsBeamlineHits"}); + RNode d1 = d0; + RVecS colNames = d1.GetColumnNames(); + + //Set number of entries to process + // d1 = d1.Range(0,1000); + + //Get the collection + std::string readoutName = "BackwardsBeamlineHits"; + + std::cout << "Running lazy RDataframe execution" << std::endl; + + if(Any(colNames==readoutName)){ + + d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName}) + .Define("endID",getSubID("end",detector),{readoutName}) + .Define("pipeParameters",getVolumeParametersFromCellID(detector),{readoutName}) + .Define("pipeRadius",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec radii; + for (const auto& param : params) { + radii.push_back(param.radius); + } + return radii; + }, {"pipeParameters"}) + .Define("xdet",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec xPos; + for (const auto& param : params) { + xPos.push_back(param.xPos); + } + return xPos; + }, {"pipeParameters"}) + .Define("zdet",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec zPos; + for (const auto& param : params) { + zPos.push_back(param.zPos); + } + return zPos; + }, {"pipeParameters"}) + .Define("rotation",[](const ROOT::VecOps::RVec& params) { + ROOT::VecOps::RVec rotation; + for (const auto& param : params) { + rotation.push_back(param.rotation); + } + return rotation; + }, {"pipeParameters"}); + + + //global x,y,z position and momentum + d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x") + .Define("ypos_global","BackwardsBeamlineHits.position.y") + .Define("zpos_global","BackwardsBeamlineHits.position.z") + .Define("px_global","BackwardsBeamlineHits.momentum.x") + .Define("py_global","BackwardsBeamlineHits.momentum.y") + .Define("pz_global","BackwardsBeamlineHits.momentum.z"); + + d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName}) + .Define("xpos","hitPosMom[0]") + .Define("ypos","hitPosMom[1]") + .Define("zpos","hitPosMom[2]") + .Define("xmomMag","hitPosMom[3]") + .Define("ymomMag","hitPosMom[4]") + .Define("zmomMag","hitPosMom[5]") + .Define("momMag","sqrt(xmomMag*xmomMag+ymomMag*ymomMag+zmomMag*zmomMag)") + .Define("xmom","xmomMag/momMag") + .Define("ymom","ymomMag/momMag") + .Define("zmom","zmomMag/momMag"); + + } + else{ + std::cout << "Collection " << readoutName << " not found in file" << std::endl; + return; + } + + // Calculate the maximum pipe radius for plotting + auto maxPipeRadius = 1.2*d1.Max("pipeRadius").GetValue(); + + std::cout << "Executing Analysis and creating histograms" << std::endl; + + //Create array of histogram results + std::map> hHistsxy; + std::map> hHistsxyZoom; + std::map> hHistsxpx; + std::map> hHistsypy; + + std::map xMeans; + std::map yMeans; + std::map xStdDevs; + std::map yStdDevs; + std::map pxMeans; + std::map pyMeans; + std::map pxStdDevs; + std::map pyStdDevs; + + //Fit paremeter and error maps + std::map xMeanFit; + std::map yMeanFit; + std::map xMeanFitErr; + std::map yMeanFitErr; + std::map xStdDevFit; + std::map yStdDevFit; + std::map xStdDevFitErr; + std::map yStdDevFitErr; + std::map pxMeanFit; + std::map pyMeanFit; + std::map pxMeanFitErr; + std::map pyMeanFitErr; + std::map pxStdDevFit; + std::map pyStdDevFit; + std::map pxStdDevFitErr; + std::map pyStdDevFitErr; + + std::map pipeRadii; + std::map pipeXPos; + std::map pipeZPos; + std::map pipeRotation; + + auto xmin = d1.Min("xpos").GetValue(); + auto xmax = d1.Max("xpos").GetValue(); + auto ymin = d1.Min("ypos").GetValue(); + auto ymax = d1.Max("ypos").GetValue(); + auto pxmin = d1.Min("xmom").GetValue(); + auto pxmax = d1.Max("xmom").GetValue(); + auto pymin = d1.Min("ymom").GetValue(); + auto pymax = d1.Max("ymom").GetValue(); + + //Create histograms + for(int i=0; i<=7; i++){ + + std::string name = "pipeID"; + name += std::to_string(i); + auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") + .Define("yposf","ypos["+std::to_string(i)+"]") + .Define("xmomf","xmom["+std::to_string(i)+"]") + .Define("ymomf","ymom["+std::to_string(i)+"]") + .Define("pipeRadiusf","pipeRadius["+std::to_string(i)+"]") + .Define("xdetf","xdet["+std::to_string(i)+"]") + .Define("zdetf","zdet["+std::to_string(i)+"]") + .Define("rotationf","rotation["+std::to_string(i)+"]"); + + + //Calculate Min and Max values + auto xminf = filterDF.Min("xposf").GetValue(); + auto xmaxf = filterDF.Max("xposf").GetValue(); + auto yminf = filterDF.Min("yposf").GetValue(); + auto ymaxf = filterDF.Max("yposf").GetValue(); + auto pxminf = filterDF.Min("xmomf").GetValue(); + auto pxmaxf = filterDF.Max("xmomf").GetValue(); + auto pyminf = filterDF.Min("ymomf").GetValue(); + auto pymaxf = filterDF.Max("ymomf").GetValue(); + // Calculate means and standard deviations + xMeans[name] = filterDF.Mean("xposf").GetValue(); + yMeans[name] = filterDF.Mean("yposf").GetValue(); + xStdDevs[name] = filterDF.StdDev("xposf").GetValue(); + yStdDevs[name] = filterDF.StdDev("yposf").GetValue(); + pxMeans[name] = filterDF.Mean("xmomf").GetValue(); + pyMeans[name] = filterDF.Mean("ymomf").GetValue(); + pxStdDevs[name] = filterDF.StdDev("xmomf").GetValue(); + pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue(); + + + TString beamspotName = "Beamspot ID"+std::to_string(i)+";x offset [cm]; y offset [cm]"; + TString xyname = name+";x Offset [cm]; y Offset [cm]"; + TString xname = name+";x Offset [cm]; x trajectory component"; + TString yname = name+";y Offset [cm]; y trajectory component"; + hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf"); + + //Parameters of the pipe + pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); + pipeXPos[name] = filterDF.Max("xdetf").GetValue(); + pipeZPos[name] = filterDF.Max("zdetf").GetValue(); + pipeRotation[name] = filterDF.Max("rotationf").GetValue(); + + } + + // Create histograms of the beamspot + TCanvas *cXY = new TCanvas("beamspot_canvas","beamspot_canvas",3000,1600); + cXY->Divide(4,2); + int i=1; + for(auto [name,h] : hHistsxy){ + // Get the pipe radius for this histogram + auto pipeRadius = pipeRadii[name]; + cXY->cd(i++); + + h->Draw("col"); + //Draw cicle + TEllipse *circle = new TEllipse(0,0,pipeRadius); + circle->SetLineColor(kRed); + circle->SetLineWidth(2); + circle->SetFillStyle(0); + circle->Draw("same"); + + } + + // Save 2D canvases as pngs + cXY->SaveAs("phasespace_in_beampipe.png"); + + + TFile *f = new TFile(outFile,"RECREATE"); + cXY->Write(); + + f->Close(); + + std::cout << "Saving events to file" << std::endl; + + ROOT::RDF::RSnapshotOptions opts; + opts.fMode = "UPDATE"; + d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); +} diff --git a/benchmarks/beamline/phasespaceGPS.mac b/benchmarks/beamline/phasespaceGPS.mac new file mode 100644 index 00000000..81b1a814 --- /dev/null +++ b/benchmarks/beamline/phasespaceGPS.mac @@ -0,0 +1,13 @@ +/gps/ang/type beam2d +/gps/ang/sigma_x 0.0011 rad # 11 mrad +/gps/ang/sigma_y 0.0011 rad # 11 mrad +/gps/pos/type Beam +/gps/pos/sigma_x 0.119 mm +/gps/pos/sigma_y 0.0107 mm +#/gps/energy 17.846 GeV +/gps/ene/type Lin +/gps/ene/min 6 GeV +/gps/ene/max 17 GeV +/gps/particle e- + +/run/beamOn 100000 \ No newline at end of file From c964d40ea6c2225c695222ff1b9db4fe5e0d0f90 Mon Sep 17 00:00:00 2001 From: simonge Date: Thu, 19 Jun 2025 10:36:37 +0100 Subject: [PATCH 22/65] Add acceptance sim --- benchmarks/beamline/Snakefile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index b9478bc6..750a8884 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -16,6 +16,21 @@ rule beamline_steering_sim: --outputFile {output} \ """ +rule beamline_acceptance_sim: + input: + macro="benchmarks/beamline/phasespaceGPS.mac", + output: + SIMOUTDIR+"phasespaceTest{CAMPAIGN}.edm4hep.root", + shell: + """ + exec npsim \ + --runType run \ + --enableG4GPS \ + --macroFile {input.macro} \ + --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --outputFile {output} \ + """ + rule beamline_steering_analysis: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", From 8b98ed6f700ae37fce2f5d0cb7ce19be4656c139 Mon Sep 17 00:00:00 2001 From: simonge Date: Fri, 20 Jun 2025 14:07:51 +0100 Subject: [PATCH 23/65] Make simulation run with correct range and 1000x faster --- benchmarks/beamline/Snakefile | 24 +++++++++++++++++++++++- benchmarks/beamline/phasespaceGPS.mac | 13 +++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 750a8884..ba1b8507 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -14,6 +14,7 @@ rule beamline_steering_sim: --macroFile {input.macro} \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ --outputFile {output} \ + --physics.rangecut 1000000 \ """ rule beamline_acceptance_sim: @@ -29,6 +30,7 @@ rule beamline_acceptance_sim: --macroFile {input.macro} \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ --outputFile {output} \ + --physics.rangecut 1000000 \ """ rule beamline_steering_analysis: @@ -54,6 +56,22 @@ rule beamline_steering_analysis: "{output.pipe_parameter_canvas}")' """ +rule beamline_acceptance_analysis: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + input: + script="benchmarks/beamline/phasespaceAnalysis.C", + data=SIMOUTDIR+"phasespaceTest{CAMPAIGN}.edm4hep.root", + output: + rootfile=ANALYSISDIR+"phasespaceTestAnalysis{CAMPAIGN}.root", + beampipe_canvas=ANALYSISDIR+"phasespace_in_beampipe_{CAMPAIGN}.png", + etheta_canvas=ANALYSISDIR+"phasespace_energy_theta_{CAMPAIGN}.png", + etheta_acceptance_canvas=ANALYSISDIR+"phasespace_energy_theta_acceptance_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", "{output.beampipe_canvas}","{output.etheta_canvas}","{output.etheta_acceptance_canvas}")' + """ + rule beamline: input: ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", @@ -62,7 +80,11 @@ rule beamline: ANALYSISDIR+"y_py_{CAMPAIGN}.png", ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", - ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", + ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", + ANALYSISDIR+"phasespaceTestAnalysis{CAMPAIGN}.root", + ANALYSISDIR+"phasespace_in_beampipe_{CAMPAIGN}.png", + ANALYSISDIR+"phasespace_energy_theta_{CAMPAIGN}.png", + ANALYSISDIR+"phasespace_energy_theta_acceptance_{CAMPAIGN}.png", output: directory("results/beamline/{CAMPAIGN}/") shell: diff --git a/benchmarks/beamline/phasespaceGPS.mac b/benchmarks/beamline/phasespaceGPS.mac index 81b1a814..59b48263 100644 --- a/benchmarks/beamline/phasespaceGPS.mac +++ b/benchmarks/beamline/phasespaceGPS.mac @@ -1,13 +1,14 @@ + /gps/ang/type beam2d -/gps/ang/sigma_x 0.0011 rad # 11 mrad -/gps/ang/sigma_y 0.0011 rad # 11 mrad +/gps/ang/sigma_x 0.011 rad # 11 mrad +/gps/ang/sigma_y 0.011 rad # 11 mrad /gps/pos/type Beam /gps/pos/sigma_x 0.119 mm /gps/pos/sigma_y 0.0107 mm -#/gps/energy 17.846 GeV +/gps/particle e- /gps/ene/type Lin +/gps/ene/gradient 0.00001 /gps/ene/min 6 GeV -/gps/ene/max 17 GeV -/gps/particle e- +/gps/ene/max 18 GeV -/run/beamOn 100000 \ No newline at end of file +/run/beamOn 1000000 \ No newline at end of file From 31fe8ef6efc649492cb55963c2ac18d9dbcc0c47 Mon Sep 17 00:00:00 2001 From: simonge Date: Fri, 20 Jun 2025 14:09:14 +0100 Subject: [PATCH 24/65] Add outputs from script --- benchmarks/beamline/phasespaceAnalysis.C | 139 +++++++++++++++++------ 1 file changed, 106 insertions(+), 33 deletions(-) diff --git a/benchmarks/beamline/phasespaceAnalysis.C b/benchmarks/beamline/phasespaceAnalysis.C index 4792e653..20e74b80 100644 --- a/benchmarks/beamline/phasespaceAnalysis.C +++ b/benchmarks/beamline/phasespaceAnalysis.C @@ -19,9 +19,12 @@ using RVecS = ROOT::VecOps::RVec; using RNode = ROOT::RDF::RNode; -void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", - TString outFile = "output.root", - std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml"){ +int phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", + TString outFile = "output.root", + std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml", + TString beampipeCanvasName = "phasespace_in_beampipe.png", + TString EThetaCanvasName = "phasespace_energy_theta.png", + TString EThetaAccCanvasName= "phasespace_energy_theta_acceptance.png") { //Set ROOT style gStyle->SetPadLeftMargin(0.1); // Set left margin @@ -36,8 +39,10 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset gStyle->SetOptStat(0); + int pass = 0; + //Set implicit multi-threading - ROOT::EnableImplicitMT(); + // ROOT::EnableImplicitMT(); //Load the detector config dd4hep::Detector& detector = dd4hep::Detector::getInstance(); @@ -51,10 +56,44 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea // d1 = d1.Range(0,1000); //Get the collection + std::string mcParticlesName = "MCParticles"; std::string readoutName = "BackwardsBeamlineHits"; std::cout << "Running lazy RDataframe execution" << std::endl; + if(Any(colNames==mcParticlesName)){ + + // Get theta and energy of the particles + d1 = d1.Define("theta",[](const vector& mcParticles) { + // Calculate theta from momentum components as angle from negative z axis + double px = mcParticles[0].momentum.x; + double py = mcParticles[0].momentum.y; + double pz = mcParticles[0].momentum.z; + double p = std::sqrt(px*px + py*py + pz*pz); + double theta = M_PI-std::acos(pz / p); // Angle from the z-axis + + return theta; + }, {mcParticlesName}) + .Define("energy",[](const vector& mcParticles) { + + //Calculate energy from mass and momentum + double mass = mcParticles[0].mass; + double px = mcParticles[0].momentum.x; + double py = mcParticles[0].momentum.y; + double pz = mcParticles[0].momentum.z; + double energy = std::sqrt(px*px + py*py + pz*pz + mass*mass); + + return energy; + }, {mcParticlesName}); + + } + else{ + std::cout << "Collection " << mcParticlesName << " not found in file" << std::endl; + return 1; + } + + auto totalETheta = d1.Histo2D({"Energy vs Theta","Energy vs Theta",100,4,18,100,0,0.011},"energy","theta"); + if(Any(colNames==readoutName)){ d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName}) @@ -91,13 +130,14 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea //global x,y,z position and momentum - d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x") - .Define("ypos_global","BackwardsBeamlineHits.position.y") - .Define("zpos_global","BackwardsBeamlineHits.position.z") - .Define("px_global","BackwardsBeamlineHits.momentum.x") - .Define("py_global","BackwardsBeamlineHits.momentum.y") - .Define("pz_global","BackwardsBeamlineHits.momentum.z"); - + d1 = d1 .Define("NHits","BackwardsBeamlineHits.size()") + .Alias("xpos_global","BackwardsBeamlineHits.position.x") + .Alias("ypos_global","BackwardsBeamlineHits.position.y") + .Alias("zpos_global","BackwardsBeamlineHits.position.z") + .Alias("px_global","BackwardsBeamlineHits.momentum.x") + .Alias("py_global","BackwardsBeamlineHits.momentum.y") + .Alias("pz_global","BackwardsBeamlineHits.momentum.z"); + d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName}) .Define("xpos","hitPosMom[0]") .Define("ypos","hitPosMom[1]") @@ -113,7 +153,7 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea } else{ std::cout << "Collection " << readoutName << " not found in file" << std::endl; - return; + return 1; } // Calculate the maximum pipe radius for plotting @@ -123,9 +163,8 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea //Create array of histogram results std::map> hHistsxy; - std::map> hHistsxyZoom; - std::map> hHistsxpx; - std::map> hHistsypy; + std::map> hHistsETheta; + std::map xMeans; std::map yMeans; @@ -172,17 +211,20 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea for(int i=0; i<=7; i++){ std::string name = "pipeID"; - name += std::to_string(i); - auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") - .Define("yposf","ypos["+std::to_string(i)+"]") - .Define("xmomf","xmom["+std::to_string(i)+"]") - .Define("ymomf","ymom["+std::to_string(i)+"]") - .Define("pipeRadiusf","pipeRadius["+std::to_string(i)+"]") - .Define("xdetf","xdet["+std::to_string(i)+"]") - .Define("zdetf","zdet["+std::to_string(i)+"]") - .Define("rotationf","rotation["+std::to_string(i)+"]"); + std::string str_i = std::to_string(i); + name += str_i; + auto filterDF = d1.Filter(std::to_string(i+1)+"<=NHits" ) + .Define("xposf","xpos["+str_i+"]") + .Define("yposf","ypos["+str_i+"]") + .Define("xmomf","xmom["+str_i+"]") + .Define("ymomf","ymom["+str_i+"]") + .Define("pipeRadiusf","pipeRadius["+str_i+"]") + .Define("xdetf","xdet["+str_i+"]") + .Define("zdetf","zdet["+str_i+"]") + .Define("rotationf","rotation["+str_i+"]"); - + // auto report = filterDF.Display({"NHits","theta","energy"}); + // report->Print(); //Calculate Min and Max values auto xminf = filterDF.Min("xposf").GetValue(); auto xmaxf = filterDF.Max("xposf").GetValue(); @@ -203,14 +245,20 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue(); - TString beamspotName = "Beamspot ID"+std::to_string(i)+";x offset [cm]; y offset [cm]"; + TString beamspotName = "Beamspot ID"+str_i+";x offset [cm]; y offset [cm]"; TString xyname = name+";x Offset [cm]; y Offset [cm]"; TString xname = name+";x Offset [cm]; x trajectory component"; TString yname = name+";y Offset [cm]; y trajectory component"; hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf"); + auto extraFilterDF = filterDF.Filter(std::to_string(i+1)+"==NHits" ); + TString EThetaName = "Energy vs Theta ID"+str_i+";Energy [GeV]; Theta [rad]"; + TString nameETheta = name+";Energy [GeV]; Theta [rad]"; + hHistsETheta[name] = extraFilterDF.Histo2D({EThetaName,EThetaName,100,4,18,100,0,0.011},"energy","theta"); + //Parameters of the pipe - pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); + pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); + std::cout << "Pipe ID: " << name << " Radius: " << pipeRadii[name] << " " << filterDF.Min("pipeRadiusf").GetValue() << std::endl; pipeXPos[name] = filterDF.Max("xdetf").GetValue(); pipeZPos[name] = filterDF.Max("zdetf").GetValue(); pipeRotation[name] = filterDF.Max("rotationf").GetValue(); @@ -236,18 +284,43 @@ void phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/bea } - // Save 2D canvases as pngs - cXY->SaveAs("phasespace_in_beampipe.png"); + // Cnavas for energy vs theta + TCanvas *cETheta = new TCanvas("energy_theta_canvas","energy_theta_canvas",3000,1600); + cETheta->Divide(4,2); + i=1; + for(auto [name,h] : hHistsETheta){ + cETheta->cd(i++); + h->Draw("colz"); + } + + // Canvas for energy vs theta acceptance + TCanvas *cEThetaAcc = new TCanvas("energy_theta_acceptance_canvas","energy_theta_acceptance_canvas",3000,1600); + cEThetaAcc->Divide(4,2); + i=1; + for(auto [name,h] : hHistsETheta){ + cEThetaAcc->cd(i++); + h->Divide(totalETheta.GetPtr()); + h->Draw("colz"); + } + // Save 2D canvases as pngs + cXY->SaveAs(beampipeCanvasName); + cETheta->SaveAs(EThetaCanvasName); + cEThetaAcc->SaveAs(EThetaAccCanvasName); TFile *f = new TFile(outFile,"RECREATE"); cXY->Write(); + cETheta->Write(); + cEThetaAcc->Write(); f->Close(); - std::cout << "Saving events to file" << std::endl; + std::cout << "Analysis complete. Results saved to " << outFile << std::endl; + return pass; + + // std::cout << "Saving events to file" << std::endl; - ROOT::RDF::RSnapshotOptions opts; - opts.fMode = "UPDATE"; - d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); + // ROOT::RDF::RSnapshotOptions opts; + // opts.fMode = "UPDATE"; + // d1.Snapshot("events",outFile,{"pipeID","endID","pipeRadius","xpos","ypos","zpos","xmom","ymom","zmom","xpos_global","ypos_global","zpos_global","px_global","py_global","pz_global"},opts); } From ccddf53e19a1b374b9d0a7e3672e7a35ec87313b Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 23 Jun 2025 22:57:14 +0100 Subject: [PATCH 25/65] Change code inputs to workflow source path --- benchmarks/beamline/Snakefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index ba1b8507..90943244 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -19,7 +19,7 @@ rule beamline_steering_sim: rule beamline_acceptance_sim: input: - macro="benchmarks/beamline/phasespaceGPS.mac", + macro=workflow.source_path("phasespaceGPS.mac"), output: SIMOUTDIR+"phasespaceTest{CAMPAIGN}.edm4hep.root", shell: @@ -60,7 +60,8 @@ rule beamline_acceptance_analysis: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", input: - script="benchmarks/beamline/phasespaceAnalysis.C", + script=workflow.source_path("phasespaceAnalysis.C"), + header=workflow.source_path("shared_functions.h"), data=SIMOUTDIR+"phasespaceTest{CAMPAIGN}.edm4hep.root", output: rootfile=ANALYSISDIR+"phasespaceTestAnalysis{CAMPAIGN}.root", From f44d6900721abab8064b5aab5fbd54ac78d410b3 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 23 Jun 2025 23:02:59 +0100 Subject: [PATCH 26/65] rename phasespace to acceptance --- benchmarks/beamline/Snakefile | 24 +++++++++---------- ...sespaceAnalysis.C => acceptanceAnalysis.C} | 2 +- .../{phasespaceGPS.mac => acceptanceGPS.mac} | 0 3 files changed, 13 insertions(+), 13 deletions(-) rename benchmarks/beamline/{phasespaceAnalysis.C => acceptanceAnalysis.C} (99%) rename benchmarks/beamline/{phasespaceGPS.mac => acceptanceGPS.mac} (100%) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 90943244..a1cc0911 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -19,9 +19,9 @@ rule beamline_steering_sim: rule beamline_acceptance_sim: input: - macro=workflow.source_path("phasespaceGPS.mac"), + macro=workflow.source_path("acceptanceGPS.mac"), output: - SIMOUTDIR+"phasespaceTest{CAMPAIGN}.edm4hep.root", + SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", shell: """ exec npsim \ @@ -60,14 +60,14 @@ rule beamline_acceptance_analysis: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", input: - script=workflow.source_path("phasespaceAnalysis.C"), + script=workflow.source_path("acceptanceAnalysis.C"), header=workflow.source_path("shared_functions.h"), - data=SIMOUTDIR+"phasespaceTest{CAMPAIGN}.edm4hep.root", + data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", output: - rootfile=ANALYSISDIR+"phasespaceTestAnalysis{CAMPAIGN}.root", - beampipe_canvas=ANALYSISDIR+"phasespace_in_beampipe_{CAMPAIGN}.png", - etheta_canvas=ANALYSISDIR+"phasespace_energy_theta_{CAMPAIGN}.png", - etheta_acceptance_canvas=ANALYSISDIR+"phasespace_energy_theta_acceptance_{CAMPAIGN}.png", + rootfile=ANALYSISDIR+"acceptanceTestAnalysis{CAMPAIGN}.root", + beampipe_canvas=ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", + etheta_canvas=ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", + etheta_acceptance_canvas=ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", shell: """ root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", "{output.beampipe_canvas}","{output.etheta_canvas}","{output.etheta_acceptance_canvas}")' @@ -82,10 +82,10 @@ rule beamline: ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", - ANALYSISDIR+"phasespaceTestAnalysis{CAMPAIGN}.root", - ANALYSISDIR+"phasespace_in_beampipe_{CAMPAIGN}.png", - ANALYSISDIR+"phasespace_energy_theta_{CAMPAIGN}.png", - ANALYSISDIR+"phasespace_energy_theta_acceptance_{CAMPAIGN}.png", + ANALYSISDIR+"acceptanceTestAnalysis{CAMPAIGN}.root", + ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", + ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", + ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", output: directory("results/beamline/{CAMPAIGN}/") shell: diff --git a/benchmarks/beamline/phasespaceAnalysis.C b/benchmarks/beamline/acceptanceAnalysis.C similarity index 99% rename from benchmarks/beamline/phasespaceAnalysis.C rename to benchmarks/beamline/acceptanceAnalysis.C index 20e74b80..7645caec 100644 --- a/benchmarks/beamline/phasespaceAnalysis.C +++ b/benchmarks/beamline/acceptanceAnalysis.C @@ -19,7 +19,7 @@ using RVecS = ROOT::VecOps::RVec; using RNode = ROOT::RDF::RNode; -int phasespaceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/beamlineTest.edm4hep.root", +int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/acceptanceTest.edm4hep.root", TString outFile = "output.root", std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml", TString beampipeCanvasName = "phasespace_in_beampipe.png", diff --git a/benchmarks/beamline/phasespaceGPS.mac b/benchmarks/beamline/acceptanceGPS.mac similarity index 100% rename from benchmarks/beamline/phasespaceGPS.mac rename to benchmarks/beamline/acceptanceGPS.mac From 4df2119ba4a9046dd325ea4d48a9e6a414390b77 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 23 Jun 2025 23:07:26 +0100 Subject: [PATCH 27/65] Remove unused code --- benchmarks/beamline/acceptanceAnalysis.C | 110 +---------------------- 1 file changed, 4 insertions(+), 106 deletions(-) diff --git a/benchmarks/beamline/acceptanceAnalysis.C b/benchmarks/beamline/acceptanceAnalysis.C index 7645caec..19cb21ff 100644 --- a/benchmarks/beamline/acceptanceAnalysis.C +++ b/benchmarks/beamline/acceptanceAnalysis.C @@ -105,50 +105,16 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin radii.push_back(param.radius); } return radii; - }, {"pipeParameters"}) - .Define("xdet",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec xPos; - for (const auto& param : params) { - xPos.push_back(param.xPos); - } - return xPos; - }, {"pipeParameters"}) - .Define("zdet",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec zPos; - for (const auto& param : params) { - zPos.push_back(param.zPos); - } - return zPos; - }, {"pipeParameters"}) - .Define("rotation",[](const ROOT::VecOps::RVec& params) { - ROOT::VecOps::RVec rotation; - for (const auto& param : params) { - rotation.push_back(param.rotation); - } - return rotation; }, {"pipeParameters"}); //global x,y,z position and momentum - d1 = d1 .Define("NHits","BackwardsBeamlineHits.size()") - .Alias("xpos_global","BackwardsBeamlineHits.position.x") - .Alias("ypos_global","BackwardsBeamlineHits.position.y") - .Alias("zpos_global","BackwardsBeamlineHits.position.z") - .Alias("px_global","BackwardsBeamlineHits.momentum.x") - .Alias("py_global","BackwardsBeamlineHits.momentum.y") - .Alias("pz_global","BackwardsBeamlineHits.momentum.z"); + d1 = d1 .Define("NHits","BackwardsBeamlineHits.size()"); d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName}) .Define("xpos","hitPosMom[0]") .Define("ypos","hitPosMom[1]") - .Define("zpos","hitPosMom[2]") - .Define("xmomMag","hitPosMom[3]") - .Define("ymomMag","hitPosMom[4]") - .Define("zmomMag","hitPosMom[5]") - .Define("momMag","sqrt(xmomMag*xmomMag+ymomMag*ymomMag+zmomMag*zmomMag)") - .Define("xmom","xmomMag/momMag") - .Define("ymom","ymomMag/momMag") - .Define("zmom","zmomMag/momMag"); + .Define("zpos","hitPosMom[2]"); } else{ @@ -166,46 +132,7 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin std::map> hHistsETheta; - std::map xMeans; - std::map yMeans; - std::map xStdDevs; - std::map yStdDevs; - std::map pxMeans; - std::map pyMeans; - std::map pxStdDevs; - std::map pyStdDevs; - - //Fit paremeter and error maps - std::map xMeanFit; - std::map yMeanFit; - std::map xMeanFitErr; - std::map yMeanFitErr; - std::map xStdDevFit; - std::map yStdDevFit; - std::map xStdDevFitErr; - std::map yStdDevFitErr; - std::map pxMeanFit; - std::map pyMeanFit; - std::map pxMeanFitErr; - std::map pyMeanFitErr; - std::map pxStdDevFit; - std::map pyStdDevFit; - std::map pxStdDevFitErr; - std::map pyStdDevFitErr; - std::map pipeRadii; - std::map pipeXPos; - std::map pipeZPos; - std::map pipeRotation; - - auto xmin = d1.Min("xpos").GetValue(); - auto xmax = d1.Max("xpos").GetValue(); - auto ymin = d1.Min("ypos").GetValue(); - auto ymax = d1.Max("ypos").GetValue(); - auto pxmin = d1.Min("xmom").GetValue(); - auto pxmax = d1.Max("xmom").GetValue(); - auto pymin = d1.Min("ymom").GetValue(); - auto pymax = d1.Max("ymom").GetValue(); //Create histograms for(int i=0; i<=7; i++){ @@ -216,34 +143,8 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin auto filterDF = d1.Filter(std::to_string(i+1)+"<=NHits" ) .Define("xposf","xpos["+str_i+"]") .Define("yposf","ypos["+str_i+"]") - .Define("xmomf","xmom["+str_i+"]") - .Define("ymomf","ymom["+str_i+"]") - .Define("pipeRadiusf","pipeRadius["+str_i+"]") - .Define("xdetf","xdet["+str_i+"]") - .Define("zdetf","zdet["+str_i+"]") - .Define("rotationf","rotation["+str_i+"]"); - - // auto report = filterDF.Display({"NHits","theta","energy"}); - // report->Print(); - //Calculate Min and Max values - auto xminf = filterDF.Min("xposf").GetValue(); - auto xmaxf = filterDF.Max("xposf").GetValue(); - auto yminf = filterDF.Min("yposf").GetValue(); - auto ymaxf = filterDF.Max("yposf").GetValue(); - auto pxminf = filterDF.Min("xmomf").GetValue(); - auto pxmaxf = filterDF.Max("xmomf").GetValue(); - auto pyminf = filterDF.Min("ymomf").GetValue(); - auto pymaxf = filterDF.Max("ymomf").GetValue(); - // Calculate means and standard deviations - xMeans[name] = filterDF.Mean("xposf").GetValue(); - yMeans[name] = filterDF.Mean("yposf").GetValue(); - xStdDevs[name] = filterDF.StdDev("xposf").GetValue(); - yStdDevs[name] = filterDF.StdDev("yposf").GetValue(); - pxMeans[name] = filterDF.Mean("xmomf").GetValue(); - pyMeans[name] = filterDF.Mean("ymomf").GetValue(); - pxStdDevs[name] = filterDF.StdDev("xmomf").GetValue(); - pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue(); - + .Define("pipeRadiusf","pipeRadius["+str_i+"]"); + TString beamspotName = "Beamspot ID"+str_i+";x offset [cm]; y offset [cm]"; TString xyname = name+";x Offset [cm]; y Offset [cm]"; @@ -259,9 +160,6 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin //Parameters of the pipe pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); std::cout << "Pipe ID: " << name << " Radius: " << pipeRadii[name] << " " << filterDF.Min("pipeRadiusf").GetValue() << std::endl; - pipeXPos[name] = filterDF.Max("xdetf").GetValue(); - pipeZPos[name] = filterDF.Max("zdetf").GetValue(); - pipeRotation[name] = filterDF.Max("rotationf").GetValue(); } From 96e8b507aadda7f1f474cb50be5a9ce30525a097 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 23 Jun 2025 23:12:04 +0100 Subject: [PATCH 28/65] Define both simulations in the yml --- benchmarks/beamline/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/benchmarks/beamline/config.yml b/benchmarks/beamline/config.yml index ef6b84e4..f92d9e7c 100644 --- a/benchmarks/beamline/config.yml +++ b/benchmarks/beamline/config.yml @@ -1,4 +1,4 @@ -sim:beamline: +sim:beamline:beamspot: extends: .det_benchmark stage: simulate script: @@ -6,6 +6,14 @@ sim:beamline: snakemake $SNAKEMAKE_FLAGS --cores 2 \ sim_output/beamline/beamlineTestlocal.edm4hep.root +sim:beamline:acceptance: + extends: .det_benchmark + stage: simulate + script: + - | + snakemake $SNAKEMAKE_FLAGS --cores 2 \ + sim_output/beamline/acceptanceTestlocal.edm4hep.root + bench:beamline: extends: .det_benchmark stage: benchmarks From 6878af8cba5902da8c27171e6d8aaaf2fdcb491e Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 24 Jun 2025 12:47:06 +0100 Subject: [PATCH 29/65] Add entry fraction plot --- benchmarks/beamline/Snakefile | 7 ++-- benchmarks/beamline/acceptanceAnalysis.C | 44 ++++++++++++++++++++---- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index a1cc0911..0d40f90a 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -68,9 +68,11 @@ rule beamline_acceptance_analysis: beampipe_canvas=ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", etheta_canvas=ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", etheta_acceptance_canvas=ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", + entrys_canvas=ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png", shell: """ - root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", "{output.beampipe_canvas}","{output.etheta_canvas}","{output.etheta_acceptance_canvas}")' + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", "{output.beampipe_canvas}","{output.etheta_canvas}","{output.etheta_acceptance_canvas}", + "{output.entrys_canvas}")' """ rule beamline: @@ -85,7 +87,8 @@ rule beamline: ANALYSISDIR+"acceptanceTestAnalysis{CAMPAIGN}.root", ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", - ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", + ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", + ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png", output: directory("results/beamline/{CAMPAIGN}/") shell: diff --git a/benchmarks/beamline/acceptanceAnalysis.C b/benchmarks/beamline/acceptanceAnalysis.C index 19cb21ff..0cf6c72e 100644 --- a/benchmarks/beamline/acceptanceAnalysis.C +++ b/benchmarks/beamline/acceptanceAnalysis.C @@ -22,9 +22,10 @@ using RNode = ROOT::RDF::RNode; int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/acceptanceTest.edm4hep.root", TString outFile = "output.root", std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml", - TString beampipeCanvasName = "phasespace_in_beampipe.png", - TString EThetaCanvasName = "phasespace_energy_theta.png", - TString EThetaAccCanvasName= "phasespace_energy_theta_acceptance.png") { + TString beampipeCanvasName = "acceptance_in_beampipe.png", + TString EThetaCanvasName = "acceptance_energy_theta.png", + TString EThetaAccCanvasName= "acceptance_energy_theta_acceptance.png", + TString entryFractionCanvasName = "acceptance_entries.png") { //Set ROOT style gStyle->SetPadLeftMargin(0.1); // Set left margin @@ -52,6 +53,8 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin RNode d1 = d0; RVecS colNames = d1.GetColumnNames(); + //Get total number of entries + int nEntries = d1.Count().GetValue(); //Set number of entries to process // d1 = d1.Range(0,1000); @@ -92,7 +95,10 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin return 1; } - auto totalETheta = d1.Histo2D({"Energy vs Theta","Energy vs Theta",100,4,18,100,0,0.011},"energy","theta"); + int eBins = 100; + int thetaBins = 100; + + auto totalETheta = d1.Histo2D({"Energy vs Theta","Energy vs Theta",eBins,4,18,thetaBins,0,0.011},"energy","theta"); if(Any(colNames==readoutName)){ @@ -133,6 +139,8 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin std::map pipeRadii; + std::map filterEntries; + std::map filterAcceptanceIntegral; //Create histograms for(int i=0; i<=7; i++){ @@ -155,7 +163,7 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin auto extraFilterDF = filterDF.Filter(std::to_string(i+1)+"==NHits" ); TString EThetaName = "Energy vs Theta ID"+str_i+";Energy [GeV]; Theta [rad]"; TString nameETheta = name+";Energy [GeV]; Theta [rad]"; - hHistsETheta[name] = extraFilterDF.Histo2D({EThetaName,EThetaName,100,4,18,100,0,0.011},"energy","theta"); + hHistsETheta[name] = extraFilterDF.Histo2D({EThetaName,EThetaName,eBins,4,18,thetaBins,0,0.011},"energy","theta"); //Parameters of the pipe pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); @@ -186,9 +194,10 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin TCanvas *cETheta = new TCanvas("energy_theta_canvas","energy_theta_canvas",3000,1600); cETheta->Divide(4,2); i=1; - for(auto [name,h] : hHistsETheta){ + for(auto [name,h] : hHistsETheta){ cETheta->cd(i++); h->Draw("colz"); + filterEntries[name] = h->GetEntries()/ nEntries; } // Canvas for energy vs theta acceptance @@ -199,17 +208,40 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin cEThetaAcc->cd(i++); h->Divide(totalETheta.GetPtr()); h->Draw("colz"); + filterAcceptanceIntegral[name] = h->Integral()/ (eBins*thetaBins); // Normalize by the number of bins } + TH1F* hPipeEntries = CreateFittedHistogram("hNumberOfHits", + "Number of Hits per Pipe ID", + filterEntries, + {}, + "Pipe ID"); + TH1F* hPipeAcceptance = CreateFittedHistogram("hPipeAcceptance", + "Acceptance per Pipe ID", + filterAcceptanceIntegral, + {}, + "Pipe ID"); + + TCanvas *cPipeAcceptance = new TCanvas("cPipeAcceptance", "Pipe Acceptance", 1200, 400); + cPipeAcceptance->Divide(2, 1); + cPipeAcceptance->cd(1); + hPipeEntries->Draw(""); + cPipeAcceptance->cd(2); + hPipeAcceptance->Draw(""); + cPipeAcceptance->SetGrid(); + cPipeAcceptance->Update(); + // Save 2D canvases as pngs cXY->SaveAs(beampipeCanvasName); cETheta->SaveAs(EThetaCanvasName); cEThetaAcc->SaveAs(EThetaAccCanvasName); + cPipeAcceptance->SaveAs(entryFractionCanvasName); TFile *f = new TFile(outFile,"RECREATE"); cXY->Write(); cETheta->Write(); cEThetaAcc->Write(); + cPipeAcceptance->Write(); f->Close(); From 09f8681f8b255ede321b18769d74b69e95bd5c6f Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 24 Jun 2025 19:46:39 +0100 Subject: [PATCH 30/65] Make filtering more robust --- benchmarks/beamline/acceptanceAnalysis.C | 19 +++++----- benchmarks/beamline/beamlineAnalysis.C | 47 +++++++++++++++--------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/benchmarks/beamline/acceptanceAnalysis.C b/benchmarks/beamline/acceptanceAnalysis.C index 0cf6c72e..5f530408 100644 --- a/benchmarks/beamline/acceptanceAnalysis.C +++ b/benchmarks/beamline/acceptanceAnalysis.C @@ -138,7 +138,7 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin std::map> hHistsETheta; - std::map pipeRadii; + std::map> pipeRadii; std::map filterEntries; std::map filterAcceptanceIntegral; @@ -148,10 +148,9 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin std::string name = "pipeID"; std::string str_i = std::to_string(i); name += str_i; - auto filterDF = d1.Filter(std::to_string(i+1)+"<=NHits" ) - .Define("xposf","xpos["+str_i+"]") - .Define("yposf","ypos["+str_i+"]") - .Define("pipeRadiusf","pipeRadius["+str_i+"]"); + auto filterDF = d1.Define("xposf","xpos[pipeID=="+str_i+"]") + .Define("yposf","ypos[pipeID=="+str_i+"]") + .Define("pipeRadiusf","pipeRadius[pipeID=="+str_i+"]"); TString beamspotName = "Beamspot ID"+str_i+";x offset [cm]; y offset [cm]"; @@ -160,15 +159,15 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin TString yname = name+";y Offset [cm]; y trajectory component"; hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf"); - auto extraFilterDF = filterDF.Filter(std::to_string(i+1)+"==NHits" ); + auto extraFilterDF = filterDF.Filter("All(pipeID<"+std::to_string(i+1)+")&&Any(pipeID=="+std::to_string(i)+")" ); TString EThetaName = "Energy vs Theta ID"+str_i+";Energy [GeV]; Theta [rad]"; TString nameETheta = name+";Energy [GeV]; Theta [rad]"; hHistsETheta[name] = extraFilterDF.Histo2D({EThetaName,EThetaName,eBins,4,18,thetaBins,0,0.011},"energy","theta"); //Parameters of the pipe - pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue(); - std::cout << "Pipe ID: " << name << " Radius: " << pipeRadii[name] << " " << filterDF.Min("pipeRadiusf").GetValue() << std::endl; - + pipeRadii[name] = filterDF.Max("pipeRadiusf"); + // std::cout << "Pipe ID: " << name << " Radius: " << pipeRadii[name] << " " << filterDF.Min("pipeRadiusf").GetValue() << std::endl; + } // Create histograms of the beamspot @@ -177,7 +176,7 @@ int acceptanceAnalysis( TString inFile = "/scratch/EIC/G4out/beamlin int i=1; for(auto [name,h] : hHistsxy){ // Get the pipe radius for this histogram - auto pipeRadius = pipeRadii[name]; + auto pipeRadius = pipeRadii[name].GetValue(); cXY->cd(i++); h->Draw("col"); diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C index 851c4fd0..f83196cc 100644 --- a/benchmarks/beamline/beamlineAnalysis.C +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -59,7 +59,7 @@ int beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b //Set number of entries to process // d1 = d1.Range(0,1000); int nEntries = d1.Count().GetValue(); - float acceptableLoss = 0.99; // Set the acceptable loss percentage to 1% + float acceptableLoss = 0.999; // Set the acceptable loss percentage to 0.1% float acceptableEntries = nEntries * acceptableLoss; //Get the collection @@ -171,28 +171,39 @@ int beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b std::map pipeZPos; std::map pipeRotation; - auto xmin = d1.Min("xpos").GetValue(); - auto xmax = d1.Max("xpos").GetValue(); - auto ymin = d1.Min("ypos").GetValue(); - auto ymax = d1.Max("ypos").GetValue(); - auto pxmin = d1.Min("xmom").GetValue(); - auto pxmax = d1.Max("xmom").GetValue(); - auto pymin = d1.Min("ymom").GetValue(); - auto pymax = d1.Max("ymom").GetValue(); + // Queue up all actions + auto xmin_ptr = d1.Min("xpos"); + auto xmax_ptr = d1.Max("xpos"); + auto ymin_ptr = d1.Min("ypos"); + auto ymax_ptr = d1.Max("ypos"); + auto pxmin_ptr = d1.Min("xmom"); + auto pxmax_ptr = d1.Max("xmom"); + auto pymin_ptr = d1.Min("ymom"); + auto pymax_ptr = d1.Max("ymom"); + + // Now trigger the event loop (only once) + auto xmin = xmin_ptr.GetValue(); + auto xmax = xmax_ptr.GetValue(); + auto ymin = ymin_ptr.GetValue(); + auto ymax = ymax_ptr.GetValue(); + auto pxmin = pxmin_ptr.GetValue(); + auto pxmax = pxmax_ptr.GetValue(); + auto pymin = pymin_ptr.GetValue(); + auto pymax = pymax_ptr.GetValue(); //Create histograms for(int i=0; i<=7; i++){ std::string name = "pipeID"; name += std::to_string(i); - auto filterDF = d1.Define("xposf","xpos["+std::to_string(i)+"]") - .Define("yposf","ypos["+std::to_string(i)+"]") - .Define("xmomf","xmom["+std::to_string(i)+"]") - .Define("ymomf","ymom["+std::to_string(i)+"]") - .Define("pipeRadiusf","pipeRadius["+std::to_string(i)+"]") - .Define("xdetf","xdet["+std::to_string(i)+"]") - .Define("zdetf","zdet["+std::to_string(i)+"]") - .Define("rotationf","rotation["+std::to_string(i)+"]"); + auto filterDF = d1.Define("xposf","xpos[pipeID=="+std::to_string(i)+"]") + .Define("yposf","ypos[pipeID=="+std::to_string(i)+"]") + .Define("xmomf","xmom[pipeID=="+std::to_string(i)+"]") + .Define("ymomf","ymom[pipeID=="+std::to_string(i)+"]") + .Define("pipeRadiusf","pipeRadius[pipeID=="+std::to_string(i)+"]") + .Define("xdetf","xdet[pipeID=="+std::to_string(i)+"]") + .Define("zdetf","zdet[pipeID=="+std::to_string(i)+"]") + .Define("rotationf","rotation[pipeID=="+std::to_string(i)+"]"); //Calculate Min and Max values @@ -292,6 +303,8 @@ int beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b } + + // Create histograms of the beamspot TCanvas *cXY = new TCanvas("beamspot_canvas","beamspot_canvas",3000,1600); cXY->Divide(4,2); From 40e1fa9a881ceb3513f9d376113aab052c4d2210 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 24 Jun 2025 20:49:20 +0100 Subject: [PATCH 31/65] Change entry limit warning --- benchmarks/beamline/beamlineAnalysis.C | 2 +- benchmarks/beamline/beamlineGPS.mac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C index f83196cc..42525598 100644 --- a/benchmarks/beamline/beamlineAnalysis.C +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -314,7 +314,7 @@ int beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b // Check histogram entries if(h->GetEntries() < acceptableEntries){ std::cout << "Warning: Only " << h->GetEntries()/nEntries << " of particles contributing to histogram " << name - << " , which is below the accepted threshold of " << acceptableEntries << std::endl; + << " , which is below the accepted threshold of " << acceptableEntries/nEntries << std::endl; pass = 1; } diff --git a/benchmarks/beamline/beamlineGPS.mac b/benchmarks/beamline/beamlineGPS.mac index d3cdc21b..54267451 100644 --- a/benchmarks/beamline/beamlineGPS.mac +++ b/benchmarks/beamline/beamlineGPS.mac @@ -8,4 +8,4 @@ /gps/energy 18 GeV /gps/particle e- -/run/beamOn 10000 \ No newline at end of file +/run/beamOn 100000 \ No newline at end of file From 158c8efb220eacefb6c407d478ed117c57db6d65 Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 25 Jun 2025 13:21:39 +0100 Subject: [PATCH 32/65] Add reconstruction training based on beampipe exit --- benchmarks/beamline/ProcessData.py | 15 ++ benchmarks/beamline/RegressionModel.py | 128 +++++++++++++ benchmarks/beamline/Snakefile | 12 ++ benchmarks/beamline/SteeringRegression.py | 55 ++++++ benchmarks/beamline/TestModel.py | 211 ++++++++++++++++++++++ benchmarks/beamline/processData.C | 37 ++++ 6 files changed, 458 insertions(+) create mode 100644 benchmarks/beamline/ProcessData.py create mode 100644 benchmarks/beamline/RegressionModel.py create mode 100644 benchmarks/beamline/SteeringRegression.py create mode 100644 benchmarks/beamline/TestModel.py create mode 100644 benchmarks/beamline/processData.C diff --git a/benchmarks/beamline/ProcessData.py b/benchmarks/beamline/ProcessData.py new file mode 100644 index 00000000..7eb6ad1a --- /dev/null +++ b/benchmarks/beamline/ProcessData.py @@ -0,0 +1,15 @@ +import uproot +import awkward as ak + +def create_arrays(dataFiles,beamEnergy=18): + + # List of branches to load + branches = ["features","targets"] + + # Load data from concatenated list of files + data = uproot.concatenate([f"{file}:events" for file in dataFiles], branches, library="ak") + + input_data = data["features"] + target_data = data["targets"]/beamEnergy + + return input_data, target_data \ No newline at end of file diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py new file mode 100644 index 00000000..ecef920a --- /dev/null +++ b/benchmarks/beamline/RegressionModel.py @@ -0,0 +1,128 @@ +import torch +import torch.nn as nn +import torch.optim as optim +import numpy as np + +class ProjectToX0Plane(nn.Module): + def forward(self, x): + # x shape: (batch, 6) -> [x, y, z, px, py, pz] + x0 = x[:, 0] + y0 = x[:, 1] + z0 = x[:, 2] + px = x[:, 3] + py = x[:, 4] + pz = x[:, 5] + + # Avoid division by zero for px + eps = 1e-8 + px_safe = torch.where(px.abs() < eps, eps * torch.sign(px) + eps, px) + t = -x0 / px_safe + + y_proj = y0 + py * t + z_proj = z0 + pz * t + + # Output: [y_proj, z_proj, px, pz] + return torch.stack([y_proj, z_proj, px, pz], dim=1) + +class RegressionModel(nn.Module): + def __init__(self): + super(RegressionModel, self).__init__() + self.fc1 = nn.Linear(4, 512) + self.fc2 = nn.Linear(512, 64) + self.fc4 = nn.Linear(64, 3) + self.input_mean = torch.tensor([0.0, 0.0, 0.0, 0.0]) + self.input_std = torch.tensor([1.0, 1.0, 1.0, 1.0]) + # self.input_covariance = torch.tensor([[1.0, 0.0, 0.0, 0.0], + # [0.0, 1.0, 0.0, 0.0], + # [0.0, 0.0, 1.0, 0.0], + # [0.0, 0.0, 0.0, 1.0]]) + self.output_mean = torch.tensor([0.0, 0.0, 0.0]) + self.output_std = torch.tensor([1.0, 1.0, 1.0]) + # self.output_correlation = torch.tensor([[1.0, 0.0, 0.0], + # [0.0, 1.0, 0.0], + # [0.0, 0.0, 1.0]]) + + def forward(self, x): + x = ProjectToX0Plane()(x) + x = (x-self.input_mean)/self.input_std + x = torch.tanh(self.fc1(x)) + x = torch.tanh(self.fc2(x)) + x = self.fc4(x) + x = x*self.output_std + self.output_mean + return x + + def adapt(self, input_data, output_data): + in_mean = input_data.mean(axis=0) + in_std = input_data.std (axis=0) + self.input_mean = torch.tensor(in_mean) + self.input_std = torch.tensor(in_std) + + # Calculate the correlation matrix of the input data + # input_normalized = (input_data-in_mean)/in_std + # input_correlation = np.corrcoef(input_normalized, rowvar=False) + # Invert the correlation matrix and convert into float tensor + # self.input_covariance = torch.tensor(np.linalg.inv(input_correlation).astype(np.float32)) + + self.output_mean = torch.tensor(output_data.mean(axis=0)) + self.output_std = torch.tensor(output_data.std (axis=0)) + +def makeModel(): + # Create the model + model = RegressionModel() + # Define the optimizer + optimizer = optim.Adam(model.parameters(), lr=0.0001) + # Define the loss function + criterion = nn.MSELoss() + + return model, optimizer, criterion + +def trainModel(epochs, train_loader, val_loader): + + # device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + # print(f"Using device: {device}") + + model, optimizer, criterion = makeModel() + # model.to(device) + + # Verify that the model parameters are on the GPU + # for name, param in model.named_parameters(): + # print(f"{name} is on {param.device}") + + # Adapt the model using the training data from the training loader + # Project inputs to X0 plane before adaptation + inputs = train_loader.dataset.tensors[0] + projected_inputs = ProjectToX0Plane()(inputs) + model.adapt(projected_inputs.detach().numpy(), train_loader.dataset.tensors[1].detach().numpy()) + + for epoch in range(epochs): + model.train() + running_loss = 0.0 + for inputs, targets in train_loader: + # inputs, targets = inputs.to(device), targets.to(device) + optimizer.zero_grad() + outputs = model(inputs) + loss = criterion(outputs, targets) + loss.backward() + optimizer.step() + running_loss += loss.item() * inputs.size(0) + + epoch_loss = running_loss / len(train_loader.dataset) + # print(f"Epoch [{epoch+1}/{epochs}], Loss: {epoch_loss:.4f}") + + + # Validation step + model.eval() + val_loss = 0.0 + with torch.no_grad(): + for val_inputs, val_targets in val_loader: + # val_inputs, val_targets = val_inputs.to(device), val_targets.to(device) + val_outputs = model(val_inputs) + val_loss += criterion(val_outputs, val_targets).item() * val_inputs.size(0) + # val_outputs = model(val_input) + # val_loss = criterion(val_outputs, val_target) + + val_loss /= len(val_loader.dataset) + + print(f"Epoch [{epoch+1}/{epochs}], Loss: {loss}, Val Loss: {val_loss}") + + return model \ No newline at end of file diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 0d40f90a..4a0ccfb2 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -75,6 +75,18 @@ rule beamline_acceptance_analysis: "{output.entrys_canvas}")' """ +# Trains a regression model to predict the TaggerTrackerTargetTensor from the TaggerTrackerFeatureTensor. +rule beamline_steering_reconstruction_training: + input: + script="SteeringRegression.py", + data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", + output: + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstruction.onnx", + shell: + """ + python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} + """ + rule beamline: input: ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", diff --git a/benchmarks/beamline/SteeringRegression.py b/benchmarks/beamline/SteeringRegression.py new file mode 100644 index 00000000..50d30004 --- /dev/null +++ b/benchmarks/beamline/SteeringRegression.py @@ -0,0 +1,55 @@ +import torch +import argparse +from ProcessData import create_arrays +from torch.utils.data import DataLoader, TensorDataset +from RegressionModel import makeModel, trainModel + +# Parse arguments +parser = argparse.ArgumentParser(description='Train a regression model for the Tagger.') +parser.add_argument('--dataFiles', type=str, nargs='+', help='Path to the data files') +parser.add_argument('--outModelFile', type=str, default="regression_model.onnx", help='Output file for the trained model') +parser.add_argument('--batchSize', type=int, default=4096, help='Batch size for training') +parser.add_argument('--epochs', type=int, default=100, help='Number of epochs for training') +args = parser.parse_args() + +input_data, target_data = create_arrays(args.dataFiles) + +# print(f"Input data shape: {input_data.shape}") +# print(f"Target data shape: {target_data.shape}") + +torch_input_data = torch.tensor(input_data) +torch_target_data = torch.tensor(target_data) + +print(f"Input data shape: {torch_input_data.shape}") +print(f"Target data shape: {torch_target_data.shape}") + +# Split data into training and validation sets +validation_fraction = 0.25 +split_index = int(len(torch_input_data) * (1 - validation_fraction)) + +val_input_data = torch_input_data[split_index:] +val_target_data = torch_target_data[split_index:] +train_input_data = torch_input_data[:split_index] +train_target_data = torch_target_data[:split_index] + +# Create TensorDatasets +train_dataset = TensorDataset(train_input_data, train_target_data) +val_dataset = TensorDataset(val_input_data, val_target_data) + +# Create DataLoaders +train_loader = DataLoader(train_dataset, batch_size=args.batchSize, shuffle=True ) +val_loader = DataLoader(val_dataset, batch_size=args.batchSize, shuffle=False) + +print(f"Training data: {len(train_input_data)} samples") + +model = trainModel(args.epochs, train_loader, val_loader) + +# Save the trained model to ONNX format + +dummy_input = torch_input_data[0].unsqueeze(0) # Create a dummy input for the model + +torch.onnx.export(model, dummy_input, args.outModelFile, + input_names=['input'], output_names=['output'], + dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}) + +print(f"Model has been saved to {args.outModelFile}") \ No newline at end of file diff --git a/benchmarks/beamline/TestModel.py b/benchmarks/beamline/TestModel.py new file mode 100644 index 00000000..dfd95931 --- /dev/null +++ b/benchmarks/beamline/TestModel.py @@ -0,0 +1,211 @@ +import onnxruntime as ort +import argparse +import numpy as np +from ProcessData import create_arrays +import matplotlib.pyplot as plt +from matplotlib.colors import LogNorm +from scipy.stats import norm +from scipy.optimize import curve_fit + +# Parse arguments +parser = argparse.ArgumentParser(description='Train a regression model for the Tagger.') +parser.add_argument('--modelFile', type=str, default="regression_model.onnx", help='Path to the ONNX model file') +parser.add_argument('--dataFiles', type=str, nargs='+', help='Path to the data files') +parser.add_argument('--outDir', type=str, default=".", help='Output directory') +args = parser.parse_args() +modelFile = args.modelFile +dataFiles = args.dataFiles +outDir = args.outDir +outGraphFile = outDir + "/output_vs_target.png" +outGraphFile2 = outDir + "/output_vs_target2.png" +outGraphFile3 = outDir + "/output_vs_target3.png" +outGraphFile4 = outDir + "/output_vs_target4.png" + +input_data, target_data = create_arrays(dataFiles) + +target_data = np.array(target_data) + +# Load the ONNX model +session = ort.InferenceSession(modelFile) + +# Run the model on the input data +input_name = session.get_inputs()[0].name +output_name = session.get_outputs()[0].name +input_data = np.array(input_data,dtype=np.float32) +output = session.run([output_name], {input_name: input_data}) +output = np.array(output[0]) + +out_theta = np.arctan2(np.sqrt(output[:,0]**2 + output[:,1]**2),output[:,2]) +out_phi = np.arctan2(output[:,1],output[:,0]) +out_mag = np.sqrt(output[:,0]**2 + output[:,1]**2 + output[:,2]**2) +in_theta = np.arctan2(np.sqrt(target_data[:,0]**2 + target_data[:,1]**2),target_data[:,2]) +in_phi = np.arctan2(target_data[:,1],target_data[:,0]) +in_mag = np.sqrt(target_data[:,0]**2 + target_data[:,1]**2 + target_data[:,2]**2) + + +thetadiff = out_theta - in_theta +phidiff = out_phi - in_phi +# Move phidiff to within -pi/2 and pi/2 +phidiff = (phidiff + np.pi) % (2 * np.pi) - np.pi +magdiff = out_mag - in_mag + +diff = (target_data - output)/target_data +diffrange = [[-5,5],[-5,5],[-0.5,0.5]] +datarange = [[-0.02,0.02],[-0.02,0.02],[-1,0]] + +# Use the 'seismic' colormap +cmap = plt.get_cmap('seismic') + +# Creates histograms to compare the target and output data +fig, axs = plt.subplots(3, 3, figsize=(12, 12)) +for i in range(3): + # 2D histograms showing trends in the data + axs[0,i].hist2d(target_data[:,i], output[:,i], bins=100, range=[datarange[i],datarange[i]], cmap="seismic", norm=LogNorm(), label="Output vs Target") + axs[0,i].set_xlabel(f"Variable {i} Target") + axs[0,i].set_ylabel(f"Variable {i} Output") + + axs[1,i].hist(diff[:,i], bins=100, alpha=0.5, range=diffrange[i], label="Difference") + axs[1,i].set_xlabel(f"Variable {i} Difference") + axs[1,i].set_ylabel("Counts") + + axs[2,i].hist2d(target_data[:,i], diff[:,i], bins=100, range=[datarange[i],diffrange[i]], cmap="seismic", norm=LogNorm(), label="Difference vs Target") + axs[2,i].set_xlabel(f"Variable {i} Target") + axs[2,i].set_ylabel(f"Variable {i} Difference") + +plt.show() +plt.savefig(outGraphFile) + +# Creates histograms to compare theta, phi and mag target and output data +fig2, axs2 = plt.subplots(3, 3, figsize=(12, 12)) + +thetarange = [np.pi-0.01,np.pi] +phirange = [-np.pi,np.pi] +magrange = [0,1] + +thetadiffrange = [-0.01,0.01] +phidiffrange = [-np.pi,np.pi] +magdiffrange = [-0.1,0.1] + +# 2D histograms showing trends in the data +axs2[0,0].hist2d(out_theta, in_theta, bins=100, range=[thetarange,thetarange], cmap="seismic", norm=LogNorm(), label="Output vs Target") +axs2[0,0].set_xlabel("Theta Target") +axs2[0,0].set_ylabel("Theta Output") + +axs2[0,1].hist2d(out_phi, in_phi, bins=100, range=[phirange,phirange], cmap="seismic", label="Output vs Target") +axs2[0,1].set_xlabel("Phi Target") +axs2[0,1].set_ylabel("Phi Output") + +axs2[0,2].hist2d(out_mag, in_mag, bins=100, range=[magrange,magrange], cmap="seismic", norm=LogNorm(), label="Output vs Target") +axs2[0,2].set_xlabel("Mag Target") +axs2[0,2].set_ylabel("Mag Output") + +axs2[1,0].hist(thetadiff, bins=100, alpha=0.5, range=thetadiffrange, label="Difference") +axs2[1,0].set_xlabel("Theta Difference") +axs2[1,0].set_ylabel("Counts") + +axs2[1,1].hist(phidiff, bins=100, alpha=0.5, range=phidiffrange, label="Difference") +axs2[1,1].set_xlabel("Phi Difference") +axs2[1,1].set_ylabel("Counts") + +axs2[1,2].hist(magdiff, bins=100, alpha=0.5, range=magdiffrange, label="Difference") +axs2[1,2].set_xlabel("Mag Difference") +axs2[1,2].set_ylabel("Counts") + +axs2[2,0].hist2d(in_theta, thetadiff, bins=100, range=[thetarange,thetadiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") +axs2[2,0].set_xlabel("Theta Target") +axs2[2,0].set_ylabel("Theta Difference") + +axs2[2,1].hist2d(in_phi, phidiff, bins=100, range=[phirange,phidiffrange], cmap="seismic", label="Difference vs Target") +axs2[2,1].set_xlabel("Phi Target") +axs2[2,1].set_ylabel("Phi Difference") + +axs2[2,2].hist2d(in_mag, magdiff, bins=100, range=[magrange,magdiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") +axs2[2,2].set_xlabel("Mag Target") +axs2[2,2].set_ylabel("Mag Difference") + +plt.show() +plt.savefig(outGraphFile2) + +# Create histograms where the theta value has been cut at less than 3.14 +fig3, axs3 = plt.subplots(3, 3, figsize=(12, 12)) + +out_theta_cut = out_theta[out_theta < 3.14] +in_theta_cut = in_theta[out_theta < 3.14] +thetadiff_cut = thetadiff[out_theta < 3.14] + +out_phi_cut = out_phi[out_theta < 3.14] +in_phi_cut = in_phi[out_theta < 3.14] +phidiff_cut = phidiff[out_theta < 3.14] + +out_mag_cut = out_mag[out_theta < 3.14] +in_mag_cut = in_mag[out_theta < 3.14] +magdiff_cut = magdiff[out_theta < 3.14] + +axs3[0,0].hist2d(out_theta_cut, in_theta_cut, bins=100, range=[thetarange,thetarange], cmap="seismic", norm=LogNorm(), label="Output vs Target") +axs3[0,0].set_xlabel("Theta Target") +axs3[0,0].set_ylabel("Theta Output") + +axs3[0,1].hist2d(out_phi_cut, in_phi_cut, bins=100, range=[phirange,phirange], cmap="seismic", label="Output vs Target") +axs3[0,1].set_xlabel("Phi Target") +axs3[0,1].set_ylabel("Phi Output") + +axs3[0,2].hist2d(out_mag_cut, in_mag_cut, bins=100, range=[magrange,magrange], cmap="seismic", norm=LogNorm(), label="Output vs Target") +axs3[0,2].set_xlabel("Mag Target") +axs3[0,2].set_ylabel("Mag Output") + +axs3[1,0].hist(thetadiff_cut, bins=100, alpha=0.5, range=thetadiffrange, label="Difference") +axs3[1,0].set_xlabel("Theta Difference") +axs3[1,0].set_ylabel("Counts") + +axs3[1,1].hist(phidiff_cut, bins=100, alpha=0.5, range=phidiffrange, label="Difference") +axs3[1,1].set_xlabel("Phi Difference") +axs3[1,1].set_ylabel("Counts") + +axs3[1,2].hist(magdiff_cut, bins=100, alpha=0.5, range=magdiffrange, label="Difference") +axs3[1,2].set_xlabel("Mag Difference") +axs3[1,2].set_ylabel("Counts") + +axs3[2,0].hist2d(in_theta_cut, thetadiff_cut, bins=100, range=[thetarange,thetadiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") +axs3[2,0].set_xlabel("Theta Target") +axs3[2,0].set_ylabel("Theta Difference") + +axs3[2,1].hist2d(in_phi_cut, phidiff_cut, bins=100, range=[phirange,phidiffrange], cmap="seismic", label="Difference vs Target") +axs3[2,1].set_xlabel("Phi Target") +axs3[2,1].set_ylabel("Phi Difference") + +axs3[2,2].hist2d(in_mag_cut, magdiff_cut, bins=100, range=[magrange,magdiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") +axs3[2,2].set_xlabel("Mag Target") +axs3[2,2].set_ylabel("Mag Difference") + +plt.show() +plt.savefig(outGraphFile3) + +# Create plots where a Gaussian has been fitted to the data +# Function to fit a Gaussian and plot the results +def plot_gaussian_fit(ax, data, range, xlabel, ylabel): + def gaussian(x, mu, sigma, amplitude): + return amplitude * np.exp(-0.5 * ((x - mu) / sigma) ** 2) + + hist, bin_edges = np.histogram(data, bins=100, range=range, density=True) + bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 + + popt, _ = curve_fit(gaussian, bin_centers, hist, p0=[0, 0.01, 1]) + mu, sigma, amplitude = popt + print(f"mu={mu}, sigma={sigma}, amplitude={amplitude}") + + x = np.linspace(range[0], range[1], 100) + ax.plot(x, gaussian(x, *popt), 'k', linewidth=2) + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel) + ax.hist(data, bins=100, alpha=0.5, range=range, edgecolor='black', density=True) + ax.legend([f'Fit: $\mu$={mu:.5f}, $\sigma$={sigma:.5f}']) + +# Create histograms with Gaussian fits +fig4, axs4 = plt.subplots(3, 1, figsize=(8, 12)) + +plot_gaussian_fit(axs4[0], thetadiff, thetadiffrange, "Theta Difference", "Density") +plot_gaussian_fit(axs4[1], phidiff_cut, phidiffrange, "Phi Difference", "Density") +plot_gaussian_fit(axs4[2], magdiff, magdiffrange, "Mag Difference", "Density") + +plt.show() +plt.savefig(outGraphFile4) \ No newline at end of file diff --git a/benchmarks/beamline/processData.C b/benchmarks/beamline/processData.C new file mode 100644 index 00000000..e2262b31 --- /dev/null +++ b/benchmarks/beamline/processData.C @@ -0,0 +1,37 @@ +#include "ROOT/RDataFrame.hxx" +#include "TFile.h" +#include "TTree.h" +#include "edm4hep/MCParticle.h" +#include "edm4hep/SimTrackerHit.h" +#include + +void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestcurrent.edm4hep.root", const TString outputFile="test.root", const int desired_cellID = 66757) { + // Define the branches to read + std::vector branches = { + "BackwardsBeamlineHits.cellID", + "BackwardsBeamlineHits.position.x", + "BackwardsBeamlineHits.position.y", + "BackwardsBeamlineHits.position.z", + "BackwardsBeamlineHits.momentum.x", + "BackwardsBeamlineHits.momentum.y", + "BackwardsBeamlineHits.momentum.z", + "MCParticles.momentum.x", + "MCParticles.momentum.y", + "MCParticles.momentum.z" + }; + + // Create a ROOT DataFrame to read the input files + ROOT::RDataFrame df("events", inputFile); + + //Filter on events with only a single MCParticle + auto filterDF = df.Filter("MCParticles.size()==1") + .Define("desiredHits", "BackwardsBeamlineHits[BackwardsBeamlineHits.cellID=="+std::to_string(desired_cellID)+"]") + .Filter("desiredHits.size()==1") + .Define("features", "std::array{static_cast(desiredHits[0].position.x), static_cast(desiredHits[0].position.y), static_cast(desiredHits[0].position.z), static_cast(desiredHits[0].momentum.x), static_cast(desiredHits[0].momentum.y), static_cast(desiredHits[0].momentum.z)}") + .Define("targets", "std::array{static_cast(MCParticles[0].momentum.x), static_cast(MCParticles[0].momentum.y), static_cast(MCParticles[0].momentum.z)}"); + + // Save the filtered data to a new ROOT file + filterDF.Snapshot("events", outputFile, {"features","targets"}); + + std::cout << "Filtered data saved to " << outputFile << std::endl; +} \ No newline at end of file From d937fdb0790640401847ca0203dc7c088e680efe Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 25 Jun 2025 15:14:37 +0100 Subject: [PATCH 33/65] Update model and atempt to setup snakemake --- benchmarks/beamline/RegressionModel.py | 85 +++++++++++++++----------- benchmarks/beamline/Snakefile | 35 +++++++++-- 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index ecef920a..02ce775f 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -27,44 +27,59 @@ def forward(self, x): class RegressionModel(nn.Module): def __init__(self): super(RegressionModel, self).__init__() + self.project_to_x0 = ProjectToX0Plane() self.fc1 = nn.Linear(4, 512) self.fc2 = nn.Linear(512, 64) self.fc4 = nn.Linear(64, 3) - self.input_mean = torch.tensor([0.0, 0.0, 0.0, 0.0]) - self.input_std = torch.tensor([1.0, 1.0, 1.0, 1.0]) - # self.input_covariance = torch.tensor([[1.0, 0.0, 0.0, 0.0], - # [0.0, 1.0, 0.0, 0.0], - # [0.0, 0.0, 1.0, 0.0], - # [0.0, 0.0, 0.0, 1.0]]) - self.output_mean = torch.tensor([0.0, 0.0, 0.0]) - self.output_std = torch.tensor([1.0, 1.0, 1.0]) - # self.output_correlation = torch.tensor([[1.0, 0.0, 0.0], - # [0.0, 1.0, 0.0], - # [0.0, 0.0, 1.0]]) + + # Normalization parameters + self.input_mean = nn.Parameter(torch.zeros(4), requires_grad=False) + self.input_std = nn.Parameter(torch.ones(4), requires_grad=False) + self.output_mean = nn.Parameter(torch.zeros(3), requires_grad=False) + self.output_std = nn.Parameter(torch.ones(3), requires_grad=False) def forward(self, x): - x = ProjectToX0Plane()(x) - x = (x-self.input_mean)/self.input_std + # Apply projection and normalization + x = self.project_to_x0(x) + x = (x - self.input_mean) / self.input_std + + # Pass through the fully connected layers + x = self._core_forward(x) + + # Denormalize outputs + x = x * self.output_std + self.output_mean + return x + + def _core_forward(self, x): + # Core fully connected layers x = torch.tanh(self.fc1(x)) x = torch.tanh(self.fc2(x)) x = self.fc4(x) - x = x*self.output_std + self.output_mean return x def adapt(self, input_data, output_data): - in_mean = input_data.mean(axis=0) - in_std = input_data.std (axis=0) - self.input_mean = torch.tensor(in_mean) - self.input_std = torch.tensor(in_std) + # Compute normalization parameters from training data + self.input_mean.data = torch.tensor(input_data.mean(axis=0), dtype=torch.float32) + self.input_std.data = torch.tensor(input_data.std(axis=0), dtype=torch.float32) + self.output_mean.data = torch.tensor(output_data.mean(axis=0), dtype=torch.float32) + self.output_std.data = torch.tensor(output_data.std(axis=0), dtype=torch.float32) + +def preprocess_data(model, data_loader): + inputs = data_loader.dataset.tensors[0] + targets = data_loader.dataset.tensors[1] - # Calculate the correlation matrix of the input data - # input_normalized = (input_data-in_mean)/in_std - # input_correlation = np.corrcoef(input_normalized, rowvar=False) - # Invert the correlation matrix and convert into float tensor - # self.input_covariance = torch.tensor(np.linalg.inv(input_correlation).astype(np.float32)) + # Apply projection + projected_inputs = ProjectToX0Plane()(inputs) + + # Compute normalization parameters + model.adapt(projected_inputs.detach().numpy(), targets.detach().numpy()) + + # Normalize inputs and targets + normalized_inputs = (projected_inputs - model.input_mean) / model.input_std + normalized_targets = (targets - model.output_mean) / model.output_std - self.output_mean = torch.tensor(output_data.mean(axis=0)) - self.output_std = torch.tensor(output_data.std (axis=0)) + # Replace the dataset with preprocessed data + data_loader.dataset.tensors = (normalized_inputs, normalized_targets) def makeModel(): # Create the model @@ -78,21 +93,19 @@ def makeModel(): def trainModel(epochs, train_loader, val_loader): - # device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - # print(f"Using device: {device}") + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + print(f"Using device: {device}") model, optimizer, criterion = makeModel() - # model.to(device) + model.to(device) # Verify that the model parameters are on the GPU # for name, param in model.named_parameters(): # print(f"{name} is on {param.device}") - - # Adapt the model using the training data from the training loader - # Project inputs to X0 plane before adaptation - inputs = train_loader.dataset.tensors[0] - projected_inputs = ProjectToX0Plane()(inputs) - model.adapt(projected_inputs.detach().numpy(), train_loader.dataset.tensors[1].detach().numpy()) + + # Preprocess training and validation data + preprocess_data(model, train_loader) + preprocess_data(model, val_loader) for epoch in range(epochs): model.train() @@ -100,7 +113,7 @@ def trainModel(epochs, train_loader, val_loader): for inputs, targets in train_loader: # inputs, targets = inputs.to(device), targets.to(device) optimizer.zero_grad() - outputs = model(inputs) + outputs = model._core_forward(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() @@ -116,7 +129,7 @@ def trainModel(epochs, train_loader, val_loader): with torch.no_grad(): for val_inputs, val_targets in val_loader: # val_inputs, val_targets = val_inputs.to(device), val_targets.to(device) - val_outputs = model(val_inputs) + val_outputs = model._core_forward(val_inputs) val_loss += criterion(val_outputs, val_targets).item() * val_inputs.size(0) # val_outputs = model(val_input) # val_loss = criterion(val_outputs, val_target) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 4a0ccfb2..240a181d 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -75,18 +75,45 @@ rule beamline_acceptance_analysis: "{output.entrys_canvas}")' """ -# Trains a regression model to predict the TaggerTrackerTargetTensor from the TaggerTrackerFeatureTensor. -rule beamline_steering_reconstruction_training: +# Processes the simulation output data for training +rule beamline_steering_reconstruction_preperation: input: - script="SteeringRegression.py", + script=workflow.source_path("processData.C"), data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", output: - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstruction.onnx", + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + shell: + """ + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}")' + """ + +# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +rule beamline_steering_reconstruction_training: + input: + script=workflow.source_path("SteeringRegression.py"), + scriptdata=workflow.source_path("ProcessData.py"), + scriptmodel=workflow.source_path("RegressionModel.py"), + data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + output: + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstruction{CAMPAIGN}.onnx", shell: """ python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} """ +# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +rule beamline_steering_reconstruction_training: + input: + script=workflow.source_path("TestModel.py"), + data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + model=ANALYSISDIR+"BeamlineSteeringReconstruction{CAMPAIGN}.onnx", + output: + directory(ANALYSISDIR+"/NN_Test_{CAMPAIGN}/") + shell: + """ + python {input.script} --dataFiles {input.data} --modelFile {output.onnxfile} --outDir {output} + """ + rule beamline: input: ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", From e5a4ab8625e266e444a6887a9a7b1bcd62e74a1f Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 25 Jun 2025 17:31:30 +0100 Subject: [PATCH 34/65] Fix snakemane rule and silence npsim info --- benchmarks/beamline/Snakefile | 8 +++++--- benchmarks/beamline/acceptanceGPS.mac | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 240a181d..32338c9a 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -13,6 +13,7 @@ rule beamline_steering_sim: --enableG4GPS \ --macroFile {input.macro} \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --printLevel WARNING \ --outputFile {output} \ --physics.rangecut 1000000 \ """ @@ -29,6 +30,7 @@ rule beamline_acceptance_sim: --enableG4GPS \ --macroFile {input.macro} \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --printLevel WARNING \ --outputFile {output} \ --physics.rangecut 1000000 \ """ @@ -81,7 +83,7 @@ rule beamline_steering_reconstruction_preperation: script=workflow.source_path("processData.C"), data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", output: - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + rootfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", shell: """ root -l -b -q '{input.script}("{input.data}", "{output.rootfile}")' @@ -102,13 +104,13 @@ rule beamline_steering_reconstruction_training: """ # Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. -rule beamline_steering_reconstruction_training: +rule beamline_steering_reconstruction_test: input: script=workflow.source_path("TestModel.py"), data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", model=ANALYSISDIR+"BeamlineSteeringReconstruction{CAMPAIGN}.onnx", output: - directory(ANALYSISDIR+"/NN_Test_{CAMPAIGN}/") + directory("sim_output/beamline/analysis/NN_Test_{CAMPAIGN}/") shell: """ python {input.script} --dataFiles {input.data} --modelFile {output.onnxfile} --outDir {output} diff --git a/benchmarks/beamline/acceptanceGPS.mac b/benchmarks/beamline/acceptanceGPS.mac index 59b48263..5f87735e 100644 --- a/benchmarks/beamline/acceptanceGPS.mac +++ b/benchmarks/beamline/acceptanceGPS.mac @@ -11,4 +11,4 @@ /gps/ene/min 6 GeV /gps/ene/max 18 GeV -/run/beamOn 1000000 \ No newline at end of file +/run/beamOn 4000000 \ No newline at end of file From 13030cca3e118ddd5510a72fddec1eea1c59d64d Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 25 Jun 2025 18:54:47 +0100 Subject: [PATCH 35/65] Fix snakemake attribute --- benchmarks/beamline/Snakefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 32338c9a..58294a94 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -113,7 +113,8 @@ rule beamline_steering_reconstruction_test: directory("sim_output/beamline/analysis/NN_Test_{CAMPAIGN}/") shell: """ - python {input.script} --dataFiles {input.data} --modelFile {output.onnxfile} --outDir {output} + mkdir {output} + python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output} """ rule beamline: From 02665c35a5f224b644d0b6229512767f9750d3ad Mon Sep 17 00:00:00 2001 From: simonge Date: Thu, 26 Jun 2025 22:12:28 +0100 Subject: [PATCH 36/65] Scale momentum to unit vector --- benchmarks/beamline/RegressionModel.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index 02ce775f..cc540a29 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -13,6 +13,12 @@ def forward(self, x): py = x[:, 4] pz = x[:, 5] + #normalize the momentum components + momentum = torch.sqrt(px**2 + py**2 + pz**2) + px = px / momentum + py = py / momentum + pz = pz / momentum + # Avoid division by zero for px eps = 1e-8 px_safe = torch.where(px.abs() < eps, eps * torch.sign(px) + eps, px) @@ -28,8 +34,9 @@ class RegressionModel(nn.Module): def __init__(self): super(RegressionModel, self).__init__() self.project_to_x0 = ProjectToX0Plane() - self.fc1 = nn.Linear(4, 512) - self.fc2 = nn.Linear(512, 64) + self.fc1 = nn.Linear(4, 64) + self.fc2 = nn.Linear(64, 64) + # self.fc3 = nn.Linear(64, 64) self.fc4 = nn.Linear(64, 3) # Normalization parameters @@ -52,8 +59,9 @@ def forward(self, x): def _core_forward(self, x): # Core fully connected layers - x = torch.tanh(self.fc1(x)) - x = torch.tanh(self.fc2(x)) + x = torch.relu(self.fc1(x)) + x = torch.relu(self.fc2(x)) + # x = torch.tanh(self.fc3(x)) x = self.fc4(x) return x @@ -71,6 +79,10 @@ def preprocess_data(model, data_loader): # Apply projection projected_inputs = ProjectToX0Plane()(inputs) + # Print a few examples of inputs and projected inputs + print("Inputs (first 5):", inputs[:5]) + print("Projected Inputs (first 5):", projected_inputs[:5]) + # Compute normalization parameters model.adapt(projected_inputs.detach().numpy(), targets.detach().numpy()) From 16adc7f9f0cd866c55d73482c8186f8721cdee42 Mon Sep 17 00:00:00 2001 From: simonge Date: Fri, 27 Jun 2025 09:16:07 +0100 Subject: [PATCH 37/65] Add tensors to device too --- benchmarks/beamline/RegressionModel.py | 8 ++++++-- benchmarks/beamline/acceptanceGPS.mac | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index cc540a29..78ecfdb8 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -112,8 +112,8 @@ def trainModel(epochs, train_loader, val_loader): model.to(device) # Verify that the model parameters are on the GPU - # for name, param in model.named_parameters(): - # print(f"{name} is on {param.device}") + for name, param in model.named_parameters(): + print(f"{name} is on {param.device}") # Preprocess training and validation data preprocess_data(model, train_loader) @@ -123,6 +123,8 @@ def trainModel(epochs, train_loader, val_loader): model.train() running_loss = 0.0 for inputs, targets in train_loader: + inputs = inputs.to(device) + targets = targets.to(device) # inputs, targets = inputs.to(device), targets.to(device) optimizer.zero_grad() outputs = model._core_forward(inputs) @@ -140,6 +142,8 @@ def trainModel(epochs, train_loader, val_loader): val_loss = 0.0 with torch.no_grad(): for val_inputs, val_targets in val_loader: + val_inputs = val_inputs.to(device) + val_targets = val_targets.to(device) # val_inputs, val_targets = val_inputs.to(device), val_targets.to(device) val_outputs = model._core_forward(val_inputs) val_loss += criterion(val_outputs, val_targets).item() * val_inputs.size(0) diff --git a/benchmarks/beamline/acceptanceGPS.mac b/benchmarks/beamline/acceptanceGPS.mac index 5f87735e..59b48263 100644 --- a/benchmarks/beamline/acceptanceGPS.mac +++ b/benchmarks/beamline/acceptanceGPS.mac @@ -11,4 +11,4 @@ /gps/ene/min 6 GeV /gps/ene/max 18 GeV -/run/beamOn 4000000 \ No newline at end of file +/run/beamOn 1000000 \ No newline at end of file From a208cd7d28312a21642574258c93aebaa0370c67 Mon Sep 17 00:00:00 2001 From: Simon Gardner Date: Mon, 7 Jul 2025 21:48:56 +0100 Subject: [PATCH 38/65] Update benchmarks/beamline/Snakefile Co-authored-by: Dmitry Kalinkin --- benchmarks/beamline/Snakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 58294a94..75c524e7 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -20,6 +20,7 @@ rule beamline_steering_sim: rule beamline_acceptance_sim: input: + warmup="warmup/epic_ip6_extended.edm4hep.root", macro=workflow.source_path("acceptanceGPS.mac"), output: SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", From d2ebe9137d803488b86ad583cf01de5c88ac1153 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 14 Jul 2025 16:42:07 +0100 Subject: [PATCH 39/65] Various improvements --- benchmarks/beamline/ProcessData.py | 2 +- benchmarks/beamline/RegressionModel.py | 99 ++++++++++++----------- benchmarks/beamline/Snakefile | 19 ++++- benchmarks/beamline/SteeringRegression.py | 19 +++-- benchmarks/beamline/TestModel.py | 93 ++++++++++++++++++--- benchmarks/beamline/beamlineAnalysis.C | 2 + benchmarks/beamline/processData.C | 14 +++- 7 files changed, 180 insertions(+), 68 deletions(-) diff --git a/benchmarks/beamline/ProcessData.py b/benchmarks/beamline/ProcessData.py index 7eb6ad1a..0bfdaf3c 100644 --- a/benchmarks/beamline/ProcessData.py +++ b/benchmarks/beamline/ProcessData.py @@ -7,7 +7,7 @@ def create_arrays(dataFiles,beamEnergy=18): branches = ["features","targets"] # Load data from concatenated list of files - data = uproot.concatenate([f"{file}:events" for file in dataFiles], branches, library="ak") + data = uproot.concatenate([f"{file}:events" for file in dataFiles], branches, library="np") input_data = data["features"] target_data = data["targets"]/beamEnergy diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index 78ecfdb8..55a06999 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -6,37 +6,43 @@ class ProjectToX0Plane(nn.Module): def forward(self, x): # x shape: (batch, 6) -> [x, y, z, px, py, pz] - x0 = x[:, 0] - y0 = x[:, 1] - z0 = x[:, 2] - px = x[:, 3] - py = x[:, 4] - pz = x[:, 5] - - #normalize the momentum components + x0, y0, z0, px, py, pz = x.unbind(dim=1) + + # Normalize momentum components momentum = torch.sqrt(px**2 + py**2 + pz**2) - px = px / momentum - py = py / momentum - pz = pz / momentum + px_norm = px / momentum + py_norm = py / momentum + pz_norm = pz / momentum # Avoid division by zero for px - eps = 1e-8 - px_safe = torch.where(px.abs() < eps, eps * torch.sign(px) + eps, px) - t = -x0 / px_safe + # eps = 1e-8 + # px_safe = torch.where(px_norm.abs() < eps, eps * torch.sign(px_norm) + eps, px_norm) + t = -x0 / px_norm - y_proj = y0 + py * t - z_proj = z0 + pz * t + y_proj = y0 + py_norm * t + z_proj = z0 + pz_norm * t - # Output: [y_proj, z_proj, px, pz] - return torch.stack([y_proj, z_proj, px, pz], dim=1) + # Output: [y_proj, z_proj, px_norm, py_norm] + return torch.stack([y_proj, z_proj, px_norm, py_norm], dim=1) + + def project_numpy(self, arr): + """ + Projects a numpy array of shape (N, 6) using the forward method, + returns a numpy array of shape (N, 4). + """ + device = next(self.parameters()).device if any(p.device.type != 'cpu' for p in self.parameters()) else 'cpu' + x = torch.from_numpy(arr).float().to(device) + with torch.no_grad(): + projected = self.forward(x) + return projected.cpu().numpy() class RegressionModel(nn.Module): def __init__(self): super(RegressionModel, self).__init__() self.project_to_x0 = ProjectToX0Plane() - self.fc1 = nn.Linear(4, 64) - self.fc2 = nn.Linear(64, 64) - # self.fc3 = nn.Linear(64, 64) + self.fc1 = nn.Linear(4, 512) + self.fc2 = nn.Linear(512, 64) + # self.fc3 = nn.Linear(16, 8) self.fc4 = nn.Linear(64, 3) # Normalization parameters @@ -61,18 +67,18 @@ def _core_forward(self, x): # Core fully connected layers x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) - # x = torch.tanh(self.fc3(x)) + # x = torch.relu(self.fc3(x)) x = self.fc4(x) return x def adapt(self, input_data, output_data): - # Compute normalization parameters from training data - self.input_mean.data = torch.tensor(input_data.mean(axis=0), dtype=torch.float32) - self.input_std.data = torch.tensor(input_data.std(axis=0), dtype=torch.float32) - self.output_mean.data = torch.tensor(output_data.mean(axis=0), dtype=torch.float32) - self.output_std.data = torch.tensor(output_data.std(axis=0), dtype=torch.float32) + # Compute normalization parameters using PyTorch + self.input_mean.data = input_data.mean(dim=0) + self.input_std.data = input_data.std(dim=0) + self.output_mean.data = output_data.mean(dim=0) + self.output_std.data = output_data.std(dim=0) -def preprocess_data(model, data_loader): +def preprocess_data(model, data_loader, adapt=True): inputs = data_loader.dataset.tensors[0] targets = data_loader.dataset.tensors[1] @@ -84,7 +90,8 @@ def preprocess_data(model, data_loader): print("Projected Inputs (first 5):", projected_inputs[:5]) # Compute normalization parameters - model.adapt(projected_inputs.detach().numpy(), targets.detach().numpy()) + if adapt: + model.adapt(projected_inputs, targets) # Normalize inputs and targets normalized_inputs = (projected_inputs - model.input_mean) / model.input_std @@ -97,37 +104,36 @@ def makeModel(): # Create the model model = RegressionModel() # Define the optimizer - optimizer = optim.Adam(model.parameters(), lr=0.0001) + optimizer = optim.Adam(model.parameters(), lr=0.001) # Define the loss function criterion = nn.MSELoss() return model, optimizer, criterion -def trainModel(epochs, train_loader, val_loader): - - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - print(f"Using device: {device}") +def trainModel(epochs, train_loader, val_loader, device): model, optimizer, criterion = makeModel() - model.to(device) # Verify that the model parameters are on the GPU for name, param in model.named_parameters(): print(f"{name} is on {param.device}") # Preprocess training and validation data - preprocess_data(model, train_loader) - preprocess_data(model, val_loader) + # preprocess_data(model, train_loader) + # preprocess_data(model, val_loader, adapt=False) + projected_inputs = model.project_to_x0(train_loader.dataset.tensors[0]) + model.adapt(projected_inputs, train_loader.dataset.tensors[1]) + + model.to(device) for epoch in range(epochs): model.train() running_loss = 0.0 for inputs, targets in train_loader: - inputs = inputs.to(device) - targets = targets.to(device) - # inputs, targets = inputs.to(device), targets.to(device) + inputs, targets = inputs.to(device), targets.to(device) optimizer.zero_grad() - outputs = model._core_forward(inputs) + outputs = model(inputs) + # outputs = model._core_forward(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() @@ -142,16 +148,13 @@ def trainModel(epochs, train_loader, val_loader): val_loss = 0.0 with torch.no_grad(): for val_inputs, val_targets in val_loader: - val_inputs = val_inputs.to(device) - val_targets = val_targets.to(device) - # val_inputs, val_targets = val_inputs.to(device), val_targets.to(device) - val_outputs = model._core_forward(val_inputs) + val_inputs, val_targets = val_inputs.to(device), val_targets.to(device) + val_outputs = model(val_inputs) + # val_outputs = model._core_forward(val_inputs) val_loss += criterion(val_outputs, val_targets).item() * val_inputs.size(0) - # val_outputs = model(val_input) - # val_loss = criterion(val_outputs, val_target) val_loss /= len(val_loader.dataset) - print(f"Epoch [{epoch+1}/{epochs}], Loss: {loss}, Val Loss: {val_loss}") + print(f"Epoch [{epoch+1}/{epochs}], Loss: {epoch_loss}, Val Loss: {val_loss}") return model \ No newline at end of file diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 58294a94..d4463738 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -108,15 +108,30 @@ rule beamline_steering_reconstruction_test: input: script=workflow.source_path("TestModel.py"), data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", - model=ANALYSISDIR+"BeamlineSteeringReconstruction{CAMPAIGN}.onnx", + model=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", output: - directory("sim_output/beamline/analysis/NN_Test_{CAMPAIGN}/") + directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") + shell: + """ + mkdir {output} + python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output} --epochs 20 + """ + +# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +rule beamline_steering_reconstruction_full: + input: + script=workflow.source_path("TestModel.py"), + data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + model=ANALYSISDIR+"BeamlineSteeringReconstructionFull{CAMPAIGN}.onnx", + output: + directory(ANALYSISDIR+"NN_Full_{CAMPAIGN}/") shell: """ mkdir {output} python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output} """ + rule beamline: input: ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", diff --git a/benchmarks/beamline/SteeringRegression.py b/benchmarks/beamline/SteeringRegression.py index 50d30004..f064c5f9 100644 --- a/benchmarks/beamline/SteeringRegression.py +++ b/benchmarks/beamline/SteeringRegression.py @@ -8,8 +8,8 @@ parser = argparse.ArgumentParser(description='Train a regression model for the Tagger.') parser.add_argument('--dataFiles', type=str, nargs='+', help='Path to the data files') parser.add_argument('--outModelFile', type=str, default="regression_model.onnx", help='Output file for the trained model') -parser.add_argument('--batchSize', type=int, default=4096, help='Batch size for training') -parser.add_argument('--epochs', type=int, default=100, help='Number of epochs for training') +parser.add_argument('--batchSize', type=int, default=256, help='Batch size for training') +parser.add_argument('--epochs', type=int, default=200, help='Number of epochs for training') args = parser.parse_args() input_data, target_data = create_arrays(args.dataFiles) @@ -17,8 +17,15 @@ # print(f"Input data shape: {input_data.shape}") # print(f"Target data shape: {target_data.shape}") -torch_input_data = torch.tensor(input_data) -torch_target_data = torch.tensor(target_data) +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +print(f"Using device: {device}") +print("Device:", torch.cuda.get_device_name(0)) + +total_samples = 20000000 +# total_samples = len(input_data) + +torch_input_data = torch.tensor(input_data[:total_samples,:], dtype=torch.float32) +torch_target_data = torch.tensor(target_data[:total_samples,:], dtype=torch.float32) print(f"Input data shape: {torch_input_data.shape}") print(f"Target data shape: {torch_target_data.shape}") @@ -42,11 +49,11 @@ print(f"Training data: {len(train_input_data)} samples") -model = trainModel(args.epochs, train_loader, val_loader) +model = trainModel(args.epochs, train_loader, val_loader, device) # Save the trained model to ONNX format -dummy_input = torch_input_data[0].unsqueeze(0) # Create a dummy input for the model +dummy_input = torch_input_data[0].unsqueeze(0).to(device) # Create a dummy input for the model torch.onnx.export(model, dummy_input, args.outModelFile, input_names=['input'], output_names=['output'], diff --git a/benchmarks/beamline/TestModel.py b/benchmarks/beamline/TestModel.py index dfd95931..17514615 100644 --- a/benchmarks/beamline/TestModel.py +++ b/benchmarks/beamline/TestModel.py @@ -2,6 +2,7 @@ import argparse import numpy as np from ProcessData import create_arrays +from RegressionModel import ProjectToX0Plane import matplotlib.pyplot as plt from matplotlib.colors import LogNorm from scipy.stats import norm @@ -17,9 +18,11 @@ dataFiles = args.dataFiles outDir = args.outDir outGraphFile = outDir + "/output_vs_target.png" -outGraphFile2 = outDir + "/output_vs_target2.png" -outGraphFile3 = outDir + "/output_vs_target3.png" -outGraphFile4 = outDir + "/output_vs_target4.png" +outGraphFile2 = outDir + "/transformed_output_vs_target.png" +outGraphFile3 = outDir + "/transformed_cut_output_vs_target.png" +outGraphFile4 = outDir + "/projected_output_vs_target.png" +correlationFile = outDir + "/correlations.png" +projectedCorrelationFile = outDir + "/projected_correlations.png" input_data, target_data = create_arrays(dataFiles) @@ -91,7 +94,7 @@ axs2[0,0].set_xlabel("Theta Target") axs2[0,0].set_ylabel("Theta Output") -axs2[0,1].hist2d(out_phi, in_phi, bins=100, range=[phirange,phirange], cmap="seismic", label="Output vs Target") +axs2[0,1].hist2d(out_phi, in_phi, bins=100, range=[phirange,phirange], cmap="seismic", norm=LogNorm(), label="Output vs Target") axs2[0,1].set_xlabel("Phi Target") axs2[0,1].set_ylabel("Phi Output") @@ -115,7 +118,7 @@ axs2[2,0].set_xlabel("Theta Target") axs2[2,0].set_ylabel("Theta Difference") -axs2[2,1].hist2d(in_phi, phidiff, bins=100, range=[phirange,phidiffrange], cmap="seismic", label="Difference vs Target") +axs2[2,1].hist2d(in_phi, phidiff, bins=100, range=[phirange,phidiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") axs2[2,1].set_xlabel("Phi Target") axs2[2,1].set_ylabel("Phi Difference") @@ -145,7 +148,7 @@ axs3[0,0].set_xlabel("Theta Target") axs3[0,0].set_ylabel("Theta Output") -axs3[0,1].hist2d(out_phi_cut, in_phi_cut, bins=100, range=[phirange,phirange], cmap="seismic", label="Output vs Target") +axs3[0,1].hist2d(out_phi_cut, in_phi_cut, bins=100, range=[phirange,phirange], cmap="seismic", norm=LogNorm(), label="Output vs Target") axs3[0,1].set_xlabel("Phi Target") axs3[0,1].set_ylabel("Phi Output") @@ -169,7 +172,7 @@ axs3[2,0].set_xlabel("Theta Target") axs3[2,0].set_ylabel("Theta Difference") -axs3[2,1].hist2d(in_phi_cut, phidiff_cut, bins=100, range=[phirange,phidiffrange], cmap="seismic", label="Difference vs Target") +axs3[2,1].hist2d(in_phi_cut, phidiff_cut, bins=100, range=[phirange,phidiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") axs3[2,1].set_xlabel("Phi Target") axs3[2,1].set_ylabel("Phi Difference") @@ -204,8 +207,78 @@ def gaussian(x, mu, sigma, amplitude): fig4, axs4 = plt.subplots(3, 1, figsize=(8, 12)) plot_gaussian_fit(axs4[0], thetadiff, thetadiffrange, "Theta Difference", "Density") -plot_gaussian_fit(axs4[1], phidiff_cut, phidiffrange, "Phi Difference", "Density") -plot_gaussian_fit(axs4[2], magdiff, magdiffrange, "Mag Difference", "Density") +plot_gaussian_fit(axs4[1], phidiff_cut, phidiffrange, "Phi Difference", "Density") +plot_gaussian_fit(axs4[2], magdiff, magdiffrange, "Mag Difference", "Density") plt.show() -plt.savefig(outGraphFile4) \ No newline at end of file +plt.savefig(outGraphFile4) + + +# Look at the correlations between all of the variables in target_data and projected_inputs +# Create a comparison variable array +comparisson_variables = np.concatenate((input_data, target_data), axis=1) + +# Project inputs onto the X0 plane +projected_inputs = ProjectToX0Plane().project_numpy(input_data) + +# Concatenate the projected inputs and target data +projected_comparisson_variables = np.concatenate((projected_inputs, target_data), axis=1) +projected_comparisson_variables = projected_comparisson_variables[(abs(projected_comparisson_variables[:, 3]) < 0.02)] +projected_comparisson_variables = projected_comparisson_variables[(abs(projected_comparisson_variables[:, 2]+0.025) < 0.028)] # Filter for px < 0.1 + +# Calculate limits for each variable based on the data +limits = { + "ox": [np.min(comparisson_variables[:, 0]), np.max(comparisson_variables[:, 0])], + "oy": [np.min(comparisson_variables[:, 1]), np.max(comparisson_variables[:, 1])], + "y": [-200, 200], + "z": [-17000,-9000], + "px": [np.min(projected_comparisson_variables[:, 2]), np.max(projected_comparisson_variables[:, 2])], + "py": [np.min(projected_comparisson_variables[:, 3]), np.max(projected_comparisson_variables[:, 3])], + "opx": [np.min(comparisson_variables[:, 3]), np.max(comparisson_variables[:, 3])], + "opy": [np.min(comparisson_variables[:, 4]), np.max(comparisson_variables[:, 4])], + "opz": [np.min(comparisson_variables[:, 5]), np.max(comparisson_variables[:, 5])], + "Px": [np.min(projected_comparisson_variables[:, 4]), np.max(projected_comparisson_variables[:, 4])], + "Py": [np.min(projected_comparisson_variables[:, 5]), np.max(projected_comparisson_variables[:, 5])], + "Pz": [np.min(projected_comparisson_variables[:, 6]), np.max(projected_comparisson_variables[:, 6])], +} + + +labels = ["ox","oy", "z", "opx", "opy", "opz", "Px", "Py", "Pz"] +fig5, axs5 = plt.subplots(9, 9, figsize=(30, 30)) +for i in range(9): + for j in range(9): + if i == j: + axs5[j, i].hist(comparisson_variables[:, i], range=limits[labels[i]], bins=100, alpha=0.5, label=labels[i]) + axs5[j, i].set_xlabel(labels[i]) + axs5[j, i].set_ylabel("Counts") + #set log scale for y-axis if the data is skewed + axs5[j, i].set_yscale('log') + else: + axs5[j, i].hist2d(comparisson_variables[:, i], comparisson_variables[:, j], range=[limits[labels[i]],limits[labels[j]]], bins=100, cmap="seismic", norm=LogNorm()) + axs5[j, i].set_xlabel(labels[i]) + axs5[j, i].set_ylabel(labels[j]) +plt.tight_layout() +plt.savefig(correlationFile) +plt.show() + + + +# Plot the correlations between all of the variables in target_data and projected_inputs +projected_labels = ["y", "z", "px", "py", "Px", "Py", "Pz"] +fig6, axs6 = plt.subplots(7, 7, figsize=(30, 30)) +for i in range(7): + for j in range(7): + if i == j: + axs6[j, i].hist(projected_comparisson_variables[:, i], range=limits[projected_labels[i]], bins=100, alpha=0.5, label=projected_labels[i]) + axs6[j, i].set_xlabel(projected_labels[i]) + axs6[j, i].set_ylabel("Counts") + #set log scale for y-axis if the data is skewed + axs6[j, i].set_yscale('log') + else: + axs6[j, i].hist2d(projected_comparisson_variables[:, i], projected_comparisson_variables[:, j], range=[limits[projected_labels[i]],limits[projected_labels[j]]], bins=100, cmap="seismic", norm=LogNorm()) + axs6[j, i].set_xlabel(projected_labels[i]) + axs6[j, i].set_ylabel(projected_labels[j]) + +plt.tight_layout() +plt.savefig(projectedCorrelationFile) +plt.show() \ No newline at end of file diff --git a/benchmarks/beamline/beamlineAnalysis.C b/benchmarks/beamline/beamlineAnalysis.C index 42525598..08fd7618 100644 --- a/benchmarks/beamline/beamlineAnalysis.C +++ b/benchmarks/beamline/beamlineAnalysis.C @@ -316,6 +316,8 @@ int beamlineAnalysis( TString inFile = "/scratch/EIC/G4out/beamline/b std::cout << "Warning: Only " << h->GetEntries()/nEntries << " of particles contributing to histogram " << name << " , which is below the accepted threshold of " << acceptableEntries/nEntries << std::endl; pass = 1; + } else{ + std::cout << "Histogram " << name << " has " << h->GetEntries() << " entries." << std::endl; } // Get the pipe radius for this histogram diff --git a/benchmarks/beamline/processData.C b/benchmarks/beamline/processData.C index e2262b31..185d3054 100644 --- a/benchmarks/beamline/processData.C +++ b/benchmarks/beamline/processData.C @@ -20,6 +20,8 @@ void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_a "MCParticles.momentum.z" }; + float momentum_tolerance = 0.0001; // Define the momentum tolerance for filtering + // Create a ROOT DataFrame to read the input files ROOT::RDataFrame df("events", inputFile); @@ -28,7 +30,17 @@ void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_a .Define("desiredHits", "BackwardsBeamlineHits[BackwardsBeamlineHits.cellID=="+std::to_string(desired_cellID)+"]") .Filter("desiredHits.size()==1") .Define("features", "std::array{static_cast(desiredHits[0].position.x), static_cast(desiredHits[0].position.y), static_cast(desiredHits[0].position.z), static_cast(desiredHits[0].momentum.x), static_cast(desiredHits[0].momentum.y), static_cast(desiredHits[0].momentum.z)}") - .Define("targets", "std::array{static_cast(MCParticles[0].momentum.x), static_cast(MCParticles[0].momentum.y), static_cast(MCParticles[0].momentum.z)}"); + .Define("targets", "std::array{static_cast(MCParticles[0].momentum.x), static_cast(MCParticles[0].momentum.y), static_cast(MCParticles[0].momentum.z)}") + .Define("features_momentum", [](const std::array& features) { + return std::sqrt(features[3] * features[3] + features[4] * features[4] + features[5] * features[5]); + }, {"features"}) + .Define("targets_momentum", [](const std::array& targets) { + return std::sqrt(targets[0] * targets[0] + targets[1] * targets[1] + targets[2] * targets[2]); + }, {"targets"}) + .Filter([momentum_tolerance](float features_momentum, float targets_momentum) { + float relative_difference = std::abs(features_momentum - targets_momentum) / targets_momentum; + return relative_difference <= momentum_tolerance; + }, {"features_momentum", "targets_momentum"});; // Save the filtered data to a new ROOT file filterDF.Snapshot("events", outputFile, {"features","targets"}); From ae27686fa05564b0f1ed6491a158f3625c58b067 Mon Sep 17 00:00:00 2001 From: simonge Date: Fri, 25 Jul 2025 15:17:49 +0100 Subject: [PATCH 40/65] Lots of updates, filtering of lowq2 hepmc events --- .gitignore | 7 ++ Snakefile | 46 ++++++------- benchmarks/beamline/ProcessData.py | 12 ++-- benchmarks/beamline/RegressionModel.py | 59 ++++++++-------- benchmarks/beamline/Snakefile | 84 ++++++++++++++++------- benchmarks/beamline/SteeringRegression.py | 27 ++++---- benchmarks/beamline/TestModel.py | 38 +++++++--- benchmarks/beamline/filterHEPMC3.py | 40 +++++++++++ benchmarks/beamline/processData.C | 80 ++++++++++++++------- 9 files changed, 257 insertions(+), 136 deletions(-) create mode 100644 benchmarks/beamline/filterHEPMC3.py diff --git a/.gitignore b/.gitignore index bf34adef..66337cd9 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,13 @@ __pycache__/ *$py.class .ipynb_checkpoints +# results and simulation output +results/ +sim_output/ +.snakemake/ +calibrations/ +fieldmaps/ + # test for calorimeter calorimeters/test/ *.d diff --git a/Snakefile b/Snakefile index 4526fcc9..9279f4c9 100644 --- a/Snakefile +++ b/Snakefile @@ -30,30 +30,30 @@ def find_epic_libraries(): return libs -include: "benchmarks/backgrounds/Snakefile" -include: "benchmarks/backwards_ecal/Snakefile" -include: "benchmarks/barrel_ecal/Snakefile" +# include: "benchmarks/backgrounds/Snakefile" +# include: "benchmarks/backwards_ecal/Snakefile" +# include: "benchmarks/barrel_ecal/Snakefile" include: "benchmarks/beamline/Snakefile" -include: "benchmarks/calo_pid/Snakefile" -include: "benchmarks/ecal_gaps/Snakefile" -include: "benchmarks/material_scan/Snakefile" -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" -include: "benchmarks/zdc_pi0/Snakefile" -include: "benchmarks/zdc_sigma/Snakefile" -include: "benchmarks/insert_neutron/Snakefile" -include: "benchmarks/insert_tau/Snakefile" -include: "benchmarks/femc_electron/Snakefile" -include: "benchmarks/femc_photon/Snakefile" -include: "benchmarks/femc_pi0/Snakefile" -include: "benchmarks/nhcal_acceptance/Snakefile" -include: "benchmarks/nhcal_basic_distribution/Snakefile" +# include: "benchmarks/calo_pid/Snakefile" +# include: "benchmarks/ecal_gaps/Snakefile" +# include: "benchmarks/material_scan/Snakefile" +# 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" +# include: "benchmarks/zdc_pi0/Snakefile" +# include: "benchmarks/zdc_sigma/Snakefile" +# include: "benchmarks/insert_neutron/Snakefile" +# include: "benchmarks/insert_tau/Snakefile" +# include: "benchmarks/femc_electron/Snakefile" +# include: "benchmarks/femc_photon/Snakefile" +# include: "benchmarks/femc_pi0/Snakefile" +# include: "benchmarks/nhcal_acceptance/Snakefile" +# include: "benchmarks/nhcal_basic_distribution/Snakefile" use_s3 = config["remote_provider"].lower() == "s3" use_xrootd = config["remote_provider"].lower() == "xrootd" diff --git a/benchmarks/beamline/ProcessData.py b/benchmarks/beamline/ProcessData.py index 0bfdaf3c..7d76123a 100644 --- a/benchmarks/beamline/ProcessData.py +++ b/benchmarks/beamline/ProcessData.py @@ -1,15 +1,15 @@ import uproot import awkward as ak -def create_arrays(dataFiles,beamEnergy=18): +def create_arrays(dataFiles,entries=None): # List of branches to load - branches = ["features","targets"] + branches = ["_TaggerTrackerFeatureTensor_floatData","_TaggerTrackerTargetTensor_floatData"] # Load data from concatenated list of files - data = uproot.concatenate([f"{file}:events" for file in dataFiles], branches, library="np") - - input_data = data["features"] - target_data = data["targets"]/beamEnergy + data = uproot.concatenate([f"{file}:events" for file in dataFiles], branches, entry_stop=entries, library="ak") + + input_data = data["_TaggerTrackerFeatureTensor_floatData"] + target_data = data["_TaggerTrackerTargetTensor_floatData"] return input_data, target_data \ No newline at end of file diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index 55a06999..dfede5fc 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -42,18 +42,19 @@ def __init__(self): self.project_to_x0 = ProjectToX0Plane() self.fc1 = nn.Linear(4, 512) self.fc2 = nn.Linear(512, 64) - # self.fc3 = nn.Linear(16, 8) - self.fc4 = nn.Linear(64, 3) + self.fc3 = nn.Linear(64, 3) # Output layer for # Normalization parameters self.input_mean = nn.Parameter(torch.zeros(4), requires_grad=False) self.input_std = nn.Parameter(torch.ones(4), requires_grad=False) self.output_mean = nn.Parameter(torch.zeros(3), requires_grad=False) self.output_std = nn.Parameter(torch.ones(3), requires_grad=False) + def forward(self, x): - # Apply projection and normalization - x = self.project_to_x0(x) + # Apply projection + x = self.project_to_x0(x) + # Normalize inputs x = (x - self.input_mean) / self.input_std # Pass through the fully connected layers @@ -65,37 +66,32 @@ def forward(self, x): def _core_forward(self, x): # Core fully connected layers - x = torch.relu(self.fc1(x)) - x = torch.relu(self.fc2(x)) - # x = torch.relu(self.fc3(x)) - x = self.fc4(x) + x = torch.tanh(self.fc1(x)) + x = torch.tanh(self.fc2(x)) + x = self.fc3(x) return x def adapt(self, input_data, output_data): - # Compute normalization parameters using PyTorch + # Normalization self.input_mean.data = input_data.mean(dim=0) self.input_std.data = input_data.std(dim=0) self.output_mean.data = output_data.mean(dim=0) self.output_std.data = output_data.std(dim=0) def preprocess_data(model, data_loader, adapt=True): - inputs = data_loader.dataset.tensors[0] + inputs = data_loader.dataset.tensors[0] targets = data_loader.dataset.tensors[1] # Apply projection projected_inputs = ProjectToX0Plane()(inputs) - # Print a few examples of inputs and projected inputs - print("Inputs (first 5):", inputs[:5]) - print("Projected Inputs (first 5):", projected_inputs[:5]) - # Compute normalization parameters if adapt: model.adapt(projected_inputs, targets) # Normalize inputs and targets - normalized_inputs = (projected_inputs - model.input_mean) / model.input_std - normalized_targets = (targets - model.output_mean) / model.output_std + normalized_inputs = (projected_inputs - model.input_mean ) / model.input_std + normalized_targets = (targets - model.output_mean) / model.output_std # Replace the dataset with preprocessed data data_loader.dataset.tensors = (normalized_inputs, normalized_targets) @@ -104,7 +100,7 @@ def makeModel(): # Create the model model = RegressionModel() # Define the optimizer - optimizer = optim.Adam(model.parameters(), lr=0.001) + optimizer = optim.Adam(model.parameters(), lr=0.0001) # Define the loss function criterion = nn.MSELoss() @@ -114,33 +110,34 @@ def trainModel(epochs, train_loader, val_loader, device): model, optimizer, criterion = makeModel() + model.to(device) + + # Preprocess training and validation data + preprocess_data(model, train_loader, adapt=True) + + # Preprocess validation data without adapting + preprocess_data(model, val_loader, adapt=False) + + # Move data to the GPU + train_loader.dataset.tensors = (train_loader.dataset.tensors[0].to(device), train_loader.dataset.tensors[1].to(device)) + val_loader.dataset.tensors = (val_loader.dataset.tensors[0].to(device), val_loader.dataset.tensors[1].to(device)) + # Verify that the model parameters are on the GPU for name, param in model.named_parameters(): print(f"{name} is on {param.device}") - - # Preprocess training and validation data - # preprocess_data(model, train_loader) - # preprocess_data(model, val_loader, adapt=False) - projected_inputs = model.project_to_x0(train_loader.dataset.tensors[0]) - model.adapt(projected_inputs, train_loader.dataset.tensors[1]) - - model.to(device) for epoch in range(epochs): model.train() running_loss = 0.0 for inputs, targets in train_loader: - inputs, targets = inputs.to(device), targets.to(device) optimizer.zero_grad() - outputs = model(inputs) - # outputs = model._core_forward(inputs) + outputs = model._core_forward(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) epoch_loss = running_loss / len(train_loader.dataset) - # print(f"Epoch [{epoch+1}/{epochs}], Loss: {epoch_loss:.4f}") # Validation step @@ -148,9 +145,7 @@ def trainModel(epochs, train_loader, val_loader, device): val_loss = 0.0 with torch.no_grad(): for val_inputs, val_targets in val_loader: - val_inputs, val_targets = val_inputs.to(device), val_targets.to(device) - val_outputs = model(val_inputs) - # val_outputs = model._core_forward(val_inputs) + val_outputs = model._core_forward(val_inputs) val_loss += criterion(val_outputs, val_targets).item() * val_inputs.size(0) val_loss /= len(val_loader.dataset) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 3183185f..c95f9a6e 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -34,8 +34,39 @@ rule beamline_acceptance_sim: --enableG4GPS \ --macroFile {input.macro} \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ - --printLevel WARNING \ --outputFile {output} \ + --physics.rangecut 100*m + """ + +rule filter_hepmc_for_training: + input: + warmup="warmup/epic_ip6_extended.edm4hep.root", + script=workflow.source_path("filterHEPMC3.py"), + output: + SIMOUTDIR+"trainingTest{CAMPAIGN}.hepmc3.tree.root", + params: + events="root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/SIDIS/pythia6-eic/1.0.0/18x275/q2_0to1/pythia_ep_noradcor_18x275_q2_0.000000001_1.0_run1.ab.hepmc3.tree.root", + shell: + """ + python {input.script} --outFile {output} --inFile {params.events} + """ + +rule beamline_training_sim: + input: + warmup="warmup/epic_ip6_extended.edm4hep.root", + events=SIMOUTDIR+"trainingTest{CAMPAIGN}.hepmc3.tree.root", + output: + SIMOUTDIR+"trainingTest{CAMPAIGN}.edm4hep.root", + shell: + """ + exec npsim \ + --runType run \ + --random.seed 1 \ + --inputFiles {input.events} \ + --numberOfEvents 2000000 \ + --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --outputFile {output} \ + --physics.rangecut 1000*m """ rule beamline_steering_analysis: @@ -80,18 +111,35 @@ rule beamline_acceptance_analysis: "{output.entrys_canvas}")' """ + +# Run EICRecon and create the necessary input tensors. +rule beamline_steering_reconstruction_eicrecon: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,BackwardsBeamlineHits", + input: + data=SIMOUTDIR+"trainingTest{CAMPAIGN}.edm4hep.root", + executable="/home/simong/EIC/EICrecon/bin/eicrecon", + output: + eicreconfile=ANALYSISDIR+"trainingTest{CAMPAIGN}.eicrecon.edm4hep.root", + shell: + """ + {input.executable} -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore=janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile {input.data} + """ + # Processes the simulation output data for training -rule beamline_steering_reconstruction_preperation: +rule beamline_steering_reconstruction_preparation: input: script=workflow.source_path("processData.C"), - data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", + data=ANALYSISDIR+"trainingTest{CAMPAIGN}.eicrecon.edm4hep.root", output: rootfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", shell: """ - root -l -b -q '{input.script}("{input.data}", "{output.rootfile}")' + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}",18.0)' """ + # Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. rule beamline_steering_reconstruction_training: input: @@ -100,10 +148,10 @@ rule beamline_steering_reconstruction_training: scriptmodel=workflow.source_path("RegressionModel.py"), data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", output: - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstruction{CAMPAIGN}.onnx", + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", shell: """ - python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} + python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 400 """ # Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. @@ -113,28 +161,13 @@ rule beamline_steering_reconstruction_test: data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", model=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", output: - directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") - shell: - """ - mkdir {output} - python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output} --epochs 20 - """ - -# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. -rule beamline_steering_reconstruction_full: - input: - script=workflow.source_path("TestModel.py"), - data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", - model=ANALYSISDIR+"BeamlineSteeringReconstructionFull{CAMPAIGN}.onnx", - output: - directory(ANALYSISDIR+"NN_Full_{CAMPAIGN}/") + directory=directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") shell: """ - mkdir {output} - python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output} + mkdir {output.directory} + python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output.directory} """ - rule beamline: input: ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", @@ -149,12 +182,13 @@ rule beamline: ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png", + directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") output: directory("results/beamline/{CAMPAIGN}/") shell: """ mkdir {output} - cp {input} {output} + cp -r {input} {output} """ rule beamline_local: diff --git a/benchmarks/beamline/SteeringRegression.py b/benchmarks/beamline/SteeringRegression.py index f064c5f9..01ed0103 100644 --- a/benchmarks/beamline/SteeringRegression.py +++ b/benchmarks/beamline/SteeringRegression.py @@ -2,30 +2,27 @@ import argparse from ProcessData import create_arrays from torch.utils.data import DataLoader, TensorDataset -from RegressionModel import makeModel, trainModel +from RegressionModel import trainModel # Parse arguments parser = argparse.ArgumentParser(description='Train a regression model for the Tagger.') parser.add_argument('--dataFiles', type=str, nargs='+', help='Path to the data files') parser.add_argument('--outModelFile', type=str, default="regression_model.onnx", help='Output file for the trained model') -parser.add_argument('--batchSize', type=int, default=256, help='Batch size for training') -parser.add_argument('--epochs', type=int, default=200, help='Number of epochs for training') -args = parser.parse_args() +parser.add_argument('--batchSize', type=int, default=1024, help='Batch size for training') +parser.add_argument('--epochs', type=int, default=1000, help='Number of epochs for training') +parser.add_argument('--entries', type=int, default=None, help='Number of entries to process from the data files') -input_data, target_data = create_arrays(args.dataFiles) +args = parser.parse_args() -# print(f"Input data shape: {input_data.shape}") -# print(f"Target data shape: {target_data.shape}") +input_data, target_data = create_arrays(args.dataFiles, entries=args.entries) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Using device: {device}") -print("Device:", torch.cuda.get_device_name(0)) +if device.type == 'cuda': + print("Device:", torch.cuda.get_device_name(0)) -total_samples = 20000000 -# total_samples = len(input_data) - -torch_input_data = torch.tensor(input_data[:total_samples,:], dtype=torch.float32) -torch_target_data = torch.tensor(target_data[:total_samples,:], dtype=torch.float32) +torch_input_data = torch.tensor(input_data, dtype=torch.float32) +torch_target_data = torch.tensor(target_data, dtype=torch.float32) print(f"Input data shape: {torch_input_data.shape}") print(f"Target data shape: {torch_target_data.shape}") @@ -47,13 +44,13 @@ train_loader = DataLoader(train_dataset, batch_size=args.batchSize, shuffle=True ) val_loader = DataLoader(val_dataset, batch_size=args.batchSize, shuffle=False) +# Train the model print(f"Training data: {len(train_input_data)} samples") - model = trainModel(args.epochs, train_loader, val_loader, device) # Save the trained model to ONNX format - dummy_input = torch_input_data[0].unsqueeze(0).to(device) # Create a dummy input for the model +model.to(device) # Ensure the model is on the correct device torch.onnx.export(model, dummy_input, args.outModelFile, input_names=['input'], output_names=['output'], diff --git a/benchmarks/beamline/TestModel.py b/benchmarks/beamline/TestModel.py index 17514615..ed9ae87a 100644 --- a/benchmarks/beamline/TestModel.py +++ b/benchmarks/beamline/TestModel.py @@ -23,6 +23,7 @@ outGraphFile4 = outDir + "/projected_output_vs_target.png" correlationFile = outDir + "/correlations.png" projectedCorrelationFile = outDir + "/projected_correlations.png" +differenceCorrelationFile = outDir + "/difference_correlations.png" input_data, target_data = create_arrays(dataFiles) @@ -223,8 +224,10 @@ def gaussian(x, mu, sigma, amplitude): # Concatenate the projected inputs and target data projected_comparisson_variables = np.concatenate((projected_inputs, target_data), axis=1) -projected_comparisson_variables = projected_comparisson_variables[(abs(projected_comparisson_variables[:, 3]) < 0.02)] -projected_comparisson_variables = projected_comparisson_variables[(abs(projected_comparisson_variables[:, 2]+0.025) < 0.028)] # Filter for px < 0.1 +diff_cut = diff[(abs(projected_comparisson_variables[:, 3]) < 0.02)] +projected_comparisson_variables_cut = projected_comparisson_variables[(abs(projected_comparisson_variables[:, 3]) < 0.02)] +diff_cut = diff_cut[(abs(projected_comparisson_variables_cut[:, 2]+0.025) < 0.028)] +projected_comparisson_variables_cut = projected_comparisson_variables_cut[(abs(projected_comparisson_variables_cut[:, 2]+0.025) < 0.028)] # Filter for px < 0.1 # Calculate limits for each variable based on the data limits = { @@ -232,14 +235,14 @@ def gaussian(x, mu, sigma, amplitude): "oy": [np.min(comparisson_variables[:, 1]), np.max(comparisson_variables[:, 1])], "y": [-200, 200], "z": [-17000,-9000], - "px": [np.min(projected_comparisson_variables[:, 2]), np.max(projected_comparisson_variables[:, 2])], - "py": [np.min(projected_comparisson_variables[:, 3]), np.max(projected_comparisson_variables[:, 3])], + "px": [np.min(projected_comparisson_variables_cut[:, 2]), np.max(projected_comparisson_variables_cut[:, 2])], + "py": [np.min(projected_comparisson_variables_cut[:, 3]), np.max(projected_comparisson_variables_cut[:, 3])], "opx": [np.min(comparisson_variables[:, 3]), np.max(comparisson_variables[:, 3])], "opy": [np.min(comparisson_variables[:, 4]), np.max(comparisson_variables[:, 4])], "opz": [np.min(comparisson_variables[:, 5]), np.max(comparisson_variables[:, 5])], - "Px": [np.min(projected_comparisson_variables[:, 4]), np.max(projected_comparisson_variables[:, 4])], - "Py": [np.min(projected_comparisson_variables[:, 5]), np.max(projected_comparisson_variables[:, 5])], - "Pz": [np.min(projected_comparisson_variables[:, 6]), np.max(projected_comparisson_variables[:, 6])], + "Px": [np.min(projected_comparisson_variables_cut[:, 4]), np.max(projected_comparisson_variables_cut[:, 4])], + "Py": [np.min(projected_comparisson_variables_cut[:, 5]), np.max(projected_comparisson_variables_cut[:, 5])], + "Pz": [np.min(projected_comparisson_variables_cut[:, 6]), np.max(projected_comparisson_variables_cut[:, 6])], } @@ -269,16 +272,31 @@ def gaussian(x, mu, sigma, amplitude): for i in range(7): for j in range(7): if i == j: - axs6[j, i].hist(projected_comparisson_variables[:, i], range=limits[projected_labels[i]], bins=100, alpha=0.5, label=projected_labels[i]) + axs6[j, i].hist(projected_comparisson_variables_cut[:, i], range=limits[projected_labels[i]], bins=100, alpha=0.5, label=projected_labels[i]) axs6[j, i].set_xlabel(projected_labels[i]) axs6[j, i].set_ylabel("Counts") #set log scale for y-axis if the data is skewed axs6[j, i].set_yscale('log') else: - axs6[j, i].hist2d(projected_comparisson_variables[:, i], projected_comparisson_variables[:, j], range=[limits[projected_labels[i]],limits[projected_labels[j]]], bins=100, cmap="seismic", norm=LogNorm()) + axs6[j, i].hist2d(projected_comparisson_variables_cut[:, i], projected_comparisson_variables_cut[:, j], range=[limits[projected_labels[i]],limits[projected_labels[j]]], bins=100, cmap="seismic", norm=LogNorm()) axs6[j, i].set_xlabel(projected_labels[i]) axs6[j, i].set_ylabel(projected_labels[j]) + plt.tight_layout() plt.savefig(projectedCorrelationFile) -plt.show() \ No newline at end of file +plt.show() + +# Plot the correlations between the output diferences and projected inputs +output_labels = ["pred_PX", "pred_PY", "pred_PZ"] +fig7, axs7 = plt.subplots(3, 7, figsize=(15, 8)) +for i in range(3): + for j in range(7): + axs7[i, j].hist2d(projected_comparisson_variables_cut[:, j], diff_cut[:, i], range=[limits[projected_labels[j]],diffrange[i]], bins=100, cmap="seismic", norm=LogNorm()) + axs7[i, j].set_xlabel(projected_labels[j]) + axs7[i, j].set_ylabel(output_labels[i]) + + +plt.tight_layout() +plt.savefig(differenceCorrelationFile) +plt.show() diff --git a/benchmarks/beamline/filterHEPMC3.py b/benchmarks/beamline/filterHEPMC3.py new file mode 100644 index 00000000..cd7647ea --- /dev/null +++ b/benchmarks/beamline/filterHEPMC3.py @@ -0,0 +1,40 @@ + +from pyHepMC3 import HepMC3 as hm +from pyHepMC3.rootIO import HepMC3 as hmrootIO +import argparse +import sys + +# Parse arguments +parser = argparse.ArgumentParser(description='Train a regression model for the Tagger.') +parser.add_argument('--outFile', type=str, default="temp.hepmc3.tree.root", help='Path to the output file') +parser.add_argument('--inFile', type=str, nargs='+', help='Path to the input files') + +args = parser.parse_args() + +input_file = args.inFile[0] # Change to your input file +output_file = args.outFile + +# Initialize reader and writer +reader = hm.deduce_reader(input_file) +if not reader: + print(f"Error: Could not open input file {input_file}", file=sys.stderr) + sys.exit(1) +writer = hmrootIO.WriterRootTree(output_file) +if not writer: + print(f"Error: Could not create output file {output_file}", file=sys.stderr) + sys.exit(1) + +event = hm.GenEvent() +while not reader.failed(): + reader.read_event(event) + for p in list(event.particles()): + if p.pid() != 11: + event.remove_particle(p) + # Only write events that still have electrons + if any(p.pid() == 11 for p in event.particles()): + writer.write_event(event) + event.clear() + +reader.close() +writer.close() +print(f"Filtered file written to {output_file}") \ No newline at end of file diff --git a/benchmarks/beamline/processData.C b/benchmarks/beamline/processData.C index 185d3054..e366993a 100644 --- a/benchmarks/beamline/processData.C +++ b/benchmarks/beamline/processData.C @@ -5,32 +5,35 @@ #include "edm4hep/SimTrackerHit.h" #include -void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestcurrent.edm4hep.root", const TString outputFile="test.root", const int desired_cellID = 66757) { - // Define the branches to read - std::vector branches = { - "BackwardsBeamlineHits.cellID", - "BackwardsBeamlineHits.position.x", - "BackwardsBeamlineHits.position.y", - "BackwardsBeamlineHits.position.z", - "BackwardsBeamlineHits.momentum.x", - "BackwardsBeamlineHits.momentum.y", - "BackwardsBeamlineHits.momentum.z", - "MCParticles.momentum.x", - "MCParticles.momentum.y", - "MCParticles.momentum.z" - }; - - float momentum_tolerance = 0.0001; // Define the momentum tolerance for filtering +void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestcurrent.edm4hep.root", const TString outputFile="test.root", const double BeamEnergy=18.0, const int desired_cellID = 66757, const bool appendTruth = true) { + + float momentum_tolerance = 0.01; // Define the momentum tolerance for filtering // Create a ROOT DataFrame to read the input files ROOT::RDataFrame df("events", inputFile); - //Filter on events with only a single MCParticle - auto filterDF = df.Filter("MCParticles.size()==1") - .Define("desiredHits", "BackwardsBeamlineHits[BackwardsBeamlineHits.cellID=="+std::to_string(desired_cellID)+"]") - .Filter("desiredHits.size()==1") - .Define("features", "std::array{static_cast(desiredHits[0].position.x), static_cast(desiredHits[0].position.y), static_cast(desiredHits[0].position.z), static_cast(desiredHits[0].momentum.x), static_cast(desiredHits[0].momentum.y), static_cast(desiredHits[0].momentum.z)}") - .Define("targets", "std::array{static_cast(MCParticles[0].momentum.x), static_cast(MCParticles[0].momentum.y), static_cast(MCParticles[0].momentum.z)}") + //Filter on events with only a single MCParticle which hasn't scattered as it enters the drift volume + auto filterDF = df.Define("SimParticles", "MCParticles[MCParticles.generatorStatus==1]") + .Filter("SimParticles.size()==1") + .Define("beamlineHit", "BackwardsBeamlineHits[BackwardsBeamlineHits.cellID=="+std::to_string(desired_cellID)+"]") + .Filter("beamlineHit.size()==1") + .Define("features", [BeamEnergy](const ROOT::VecOps::RVec& hit) { + return std::array{ + static_cast(hit[0].position.x), + static_cast(hit[0].position.y), + static_cast(hit[0].position.z), + static_cast(hit[0].momentum.x / BeamEnergy), + static_cast(hit[0].momentum.y / BeamEnergy), + static_cast(hit[0].momentum.z / BeamEnergy) + }; + }, {"beamlineHit"}) + .Define("targets", [BeamEnergy](const ROOT::VecOps::RVec& mcps) { + return std::array{ + static_cast(mcps[0].momentum.x / BeamEnergy), + static_cast(mcps[0].momentum.y / BeamEnergy), + static_cast(mcps[0].momentum.z / BeamEnergy) + }; + }, {"SimParticles"}) .Define("features_momentum", [](const std::array& features) { return std::sqrt(features[3] * features[3] + features[4] * features[4] + features[5] * features[5]); }, {"features"}) @@ -40,10 +43,37 @@ void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_a .Filter([momentum_tolerance](float features_momentum, float targets_momentum) { float relative_difference = std::abs(features_momentum - targets_momentum) / targets_momentum; return relative_difference <= momentum_tolerance; - }, {"features_momentum", "targets_momentum"});; + }, {"features_momentum", "targets_momentum"}); + + auto taggerDF = filterDF.Filter("_TaggerTrackerFeatureTensor_shape[0]==1"); // Save the filtered data to a new ROOT file - filterDF.Snapshot("events", outputFile, {"features","targets"}); + taggerDF.Snapshot("events", outputFile, {"_TaggerTrackerFeatureTensor_floatData","_TaggerTrackerFeatureTensor_shape","_TaggerTrackerTargetTensor_floatData","features_momentum","targets_momentum"}); + + // Print the size of the original DataFrame + // std::cout << "Original DataFrame size: " << df.Count().GetValue() << std::endl; + // // Print the size of the filtered DataFrame + // std::cout << "Filtered DataFrame size: " << filterDF.Count().GetValue() << std::endl; + + // std::cout << "Tagger filtered DataFrame size" << taggerDF.Count().GetValue() << std::endl; + + // std::cout << "Filtered data saved to " << outputFile << std::endl; + + // If appendTruth is true, add the truth information + if (appendTruth) { + // Open the output file in update mode + ROOT::RDF::RSnapshotOptions opts; + opts.fMode = "update"; + auto aliasDF = filterDF.Redefine("_TaggerTrackerFeatureTensor_floatData", "features") + .Redefine("_TaggerTrackerTargetTensor_floatData", "targets"); + // filterDF = Concatenate({aliasDF, filterDF}); + aliasDF.Snapshot("truthevents", outputFile, {"_TaggerTrackerFeatureTensor_floatData", "_TaggerTrackerFeatureTensor_shape", "_TaggerTrackerTargetTensor_floatData"}, opts); + std::cout << "Truth information appended to " << outputFile << std::endl; + + // std::cout << "Total events after appending truth: " << aliasDF.Count().GetValue() << std::endl; + } + + + - std::cout << "Filtered data saved to " << outputFile << std::endl; } \ No newline at end of file From 3075944ddec66c9bd80e2c556153635add6995c8 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 28 Jul 2025 15:23:32 +0100 Subject: [PATCH 41/65] Add some versitility --- benchmarks/beamline/ProcessData.py | 10 +- benchmarks/beamline/RegressionModel.py | 32 +++-- benchmarks/beamline/Snakefile | 150 +++++++++++++++------- benchmarks/beamline/SteeringRegression.py | 5 +- 4 files changed, 134 insertions(+), 63 deletions(-) diff --git a/benchmarks/beamline/ProcessData.py b/benchmarks/beamline/ProcessData.py index 7d76123a..86123d43 100644 --- a/benchmarks/beamline/ProcessData.py +++ b/benchmarks/beamline/ProcessData.py @@ -1,15 +1,15 @@ import uproot import awkward as ak -def create_arrays(dataFiles,entries=None): +def create_arrays(dataFiles,featureName="_TaggerTrackerFeatureTensor_floatData",targetName="_TaggerTrackerTargetTensor_floatData", entries=None, treeName="events"): # List of branches to load - branches = ["_TaggerTrackerFeatureTensor_floatData","_TaggerTrackerTargetTensor_floatData"] + branches = [featureName,targetName] # Load data from concatenated list of files - data = uproot.concatenate([f"{file}:events" for file in dataFiles], branches, entry_stop=entries, library="ak") + data = uproot.concatenate([f"{file}:{treeName}" for file in dataFiles], branches, entry_stop=entries, library="ak") - input_data = data["_TaggerTrackerFeatureTensor_floatData"] - target_data = data["_TaggerTrackerTargetTensor_floatData"] + input_data = data[featureName] + target_data = data[targetName] return input_data, target_data \ No newline at end of file diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index dfede5fc..fd388241 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -37,9 +37,9 @@ def project_numpy(self, arr): return projected.cpu().numpy() class RegressionModel(nn.Module): - def __init__(self): + def __init__(self, project=True): super(RegressionModel, self).__init__() - self.project_to_x0 = ProjectToX0Plane() + self.project_to_x0 = ProjectToX0Plane() if project else None self.fc1 = nn.Linear(4, 512) self.fc2 = nn.Linear(512, 64) self.fc3 = nn.Linear(64, 3) # Output layer for @@ -52,8 +52,9 @@ def __init__(self): def forward(self, x): - # Apply projection - x = self.project_to_x0(x) + # Conditionally apply projection + if self.project and self.project_to_x0 is not None: + x = self.project_to_x0(x) # Normalize inputs x = (x - self.input_mean) / self.input_std @@ -82,8 +83,12 @@ def preprocess_data(model, data_loader, adapt=True): inputs = data_loader.dataset.tensors[0] targets = data_loader.dataset.tensors[1] - # Apply projection - projected_inputs = ProjectToX0Plane()(inputs) + + # Apply projection if project_to_x0 is not None + if model.project_to_x0 is not None: + projected_inputs = model.project_to_x0(inputs) + else: + projected_inputs = inputs # Skip projection if not needed # Compute normalization parameters if adapt: @@ -96,9 +101,9 @@ def preprocess_data(model, data_loader, adapt=True): # Replace the dataset with preprocessed data data_loader.dataset.tensors = (normalized_inputs, normalized_targets) -def makeModel(): +def makeModel(project=True): # Create the model - model = RegressionModel() + model = RegressionModel(project=project) # Define the optimizer optimizer = optim.Adam(model.parameters(), lr=0.0001) # Define the loss function @@ -108,7 +113,16 @@ def makeModel(): def trainModel(epochs, train_loader, val_loader, device): - model, optimizer, criterion = makeModel() + project = True + # Check shape of input data to see if projection needs to be done + if train_loader.dataset.tensors[0].shape[1] == 6: + project = True + if train_loader.dataset.tensors[0].shape[1] == 4: + project = False + else: + raise ValueError("Input data must have shape (N, 6) or (N, 4)") + + model, optimizer, criterion = makeModel(project=project) model.to(device) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index c95f9a6e..db099a8a 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -1,6 +1,10 @@ SIMOUTDIR="sim_output/beamline/" ANALYSISDIR=SIMOUTDIR+"analysis/" +########################################################################################## +### Rules for checking the steering of the electron beam through the magnets +########################################################################################## + rule beamline_steering_sim: input: warmup="warmup/epic_ip6_extended.edm4hep.root", @@ -20,6 +24,33 @@ rule beamline_steering_sim: --physics.rangecut 100*m """ +rule beamline_steering_analysis: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + input: + script=workflow.source_path("beamlineAnalysis.C"), + header=workflow.source_path("shared_functions.h"), + data=SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", + output: + rootfile=ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", + beamspot_canvas=ANALYSISDIR+"beamspot_{CAMPAIGN}.png", + x_px_canvas=ANALYSISDIR+"x_px_{CAMPAIGN}.png", + y_py_canvas=ANALYSISDIR+"y_py_{CAMPAIGN}.png", + fitted_position_means_stdevs_canvas=ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", + fitted_momentum_means_stdevs_canvas=ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", + pipe_parameter_canvas=ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", + "{output.beamspot_canvas}", "{output.x_px_canvas}", "{output.y_py_canvas}", + "{output.fitted_position_means_stdevs_canvas}", "{output.fitted_momentum_means_stdevs_canvas}", + "{output.pipe_parameter_canvas}")' + """ + +########################################################################################## +### Rules for checking the acceptance of electrons at each stage of the beamline +########################################################################################## + rule beamline_acceptance_sim: input: warmup="warmup/epic_ip6_extended.edm4hep.root", @@ -38,12 +69,37 @@ rule beamline_acceptance_sim: --physics.rangecut 100*m """ +rule beamline_acceptance_analysis: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + input: + script=workflow.source_path("acceptanceAnalysis.C"), + header=workflow.source_path("shared_functions.h"), + data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", + output: + rootfile=ANALYSISDIR+"acceptanceTestAnalysis{CAMPAIGN}.root", + beampipe_canvas=ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", + etheta_canvas=ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", + etheta_acceptance_canvas=ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", + entrys_canvas=ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", "{output.beampipe_canvas}","{output.etheta_canvas}","{output.etheta_acceptance_canvas}", + "{output.entrys_canvas}")' + """ + +########################################################################################## +### Rules for checking the reconstruction of the electron momentum +########################################################################################## + +# Filter LowQ2 events from xrootd server - Using the acceptance events didn't work +# ToDo: Do a proper investigation into why the feature to target mapping isn't unique rule filter_hepmc_for_training: input: warmup="warmup/epic_ip6_extended.edm4hep.root", script=workflow.source_path("filterHEPMC3.py"), output: - SIMOUTDIR+"trainingTest{CAMPAIGN}.hepmc3.tree.root", + SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", params: events="root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/SIDIS/pythia6-eic/1.0.0/18x275/q2_0to1/pythia_ep_noradcor_18x275_q2_0.000000001_1.0_run1.ab.hepmc3.tree.root", shell: @@ -51,87 +107,69 @@ rule filter_hepmc_for_training: python {input.script} --outFile {output} --inFile {params.events} """ +# Run pythia6 Low-Q2 events through the simulation rule beamline_training_sim: input: warmup="warmup/epic_ip6_extended.edm4hep.root", - events=SIMOUTDIR+"trainingTest{CAMPAIGN}.hepmc3.tree.root", + events=SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", output: - SIMOUTDIR+"trainingTest{CAMPAIGN}.edm4hep.root", + SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", shell: """ exec npsim \ --runType run \ --random.seed 1 \ --inputFiles {input.events} \ - --numberOfEvents 2000000 \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ --outputFile {output} \ --physics.rangecut 1000*m """ -rule beamline_steering_analysis: +# Run EICrecon and create input tensors for reconstruction. +rule beamline_reconstruction_tensors_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", + ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: - script=workflow.source_path("beamlineAnalysis.C"), - header=workflow.source_path("shared_functions.h"), - data=SIMOUTDIR+"beamlineTest{CAMPAIGN}.edm4hep.root", + data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", output: - rootfile=ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", - beamspot_canvas=ANALYSISDIR+"beamspot_{CAMPAIGN}.png", - x_px_canvas=ANALYSISDIR+"x_px_{CAMPAIGN}.png", - y_py_canvas=ANALYSISDIR+"y_py_{CAMPAIGN}.png", - fitted_position_means_stdevs_canvas=ANALYSISDIR+"fitted_position_means_stdevs_{CAMPAIGN}.png", - fitted_momentum_means_stdevs_canvas=ANALYSISDIR+"fitted_momentum_means_stdevs_{CAMPAIGN}.png", - pipe_parameter_canvas=ANALYSISDIR+"pipe_parameter_{CAMPAIGN}.png", + eicreconfile=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ - root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", - "{output.beamspot_canvas}", "{output.x_px_canvas}", "{output.y_py_canvas}", - "{output.fitted_position_means_stdevs_canvas}", "{output.fitted_momentum_means_stdevs_canvas}", - "{output.pipe_parameter_canvas}")' + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ -rule beamline_acceptance_analysis: +# Run EICrecon to create TaggerTrackerReconstructedParticles +rule beamline_reconstruction_particles_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", + ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: - script=workflow.source_path("acceptanceAnalysis.C"), - header=workflow.source_path("shared_functions.h"), - data=SIMOUTDIR+"acceptanceTest{CAMPAIGN}.edm4hep.root", + data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", output: - rootfile=ANALYSISDIR+"acceptanceTestAnalysis{CAMPAIGN}.root", - beampipe_canvas=ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", - etheta_canvas=ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", - etheta_acceptance_canvas=ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", - entrys_canvas=ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png", + eicreconfile=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ - root -l -b -q '{input.script}("{input.data}", "{output.rootfile}", "{params.xml}", "{output.beampipe_canvas}","{output.etheta_canvas}","{output.etheta_acceptance_canvas}", - "{output.entrys_canvas}")' + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ +# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles +#rule beamline_reconstruction_particles_test: -# Run EICRecon and create the necessary input tensors. -rule beamline_steering_reconstruction_eicrecon: - params: - xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,BackwardsBeamlineHits", - input: - data=SIMOUTDIR+"trainingTest{CAMPAIGN}.edm4hep.root", - executable="/home/simong/EIC/EICrecon/bin/eicrecon", - output: - eicreconfile=ANALYSISDIR+"trainingTest{CAMPAIGN}.eicrecon.edm4hep.root", - shell: - """ - {input.executable} -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore=janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile {input.data} - """ + +########################################################################################## +### Rules for training new onnx reconstruction neural network +########################################################################################## # Processes the simulation output data for training rule beamline_steering_reconstruction_preparation: input: script=workflow.source_path("processData.C"), - data=ANALYSISDIR+"trainingTest{CAMPAIGN}.eicrecon.edm4hep.root", + data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", output: rootfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", shell: @@ -154,7 +192,7 @@ rule beamline_steering_reconstruction_training: python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 400 """ -# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +# Tests new regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. rule beamline_steering_reconstruction_test: input: script=workflow.source_path("TestModel.py"), @@ -168,6 +206,9 @@ rule beamline_steering_reconstruction_test: python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output.directory} """ +########################################################################################## +# Combine results +########################################################################################## rule beamline: input: ANALYSISDIR+"beamlineTestAnalysis{CAMPAIGN}.root", @@ -181,16 +222,29 @@ rule beamline: ANALYSISDIR+"acceptance_in_beampipe_{CAMPAIGN}.png", ANALYSISDIR+"acceptance_energy_theta_{CAMPAIGN}.png", ANALYSISDIR+"acceptance_energy_theta_acceptance_{CAMPAIGN}.png", - ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png", + ANALYSISDIR+"acceptance_entrys_{CAMPAIGN}.png" + output: + directory("results/beamline/steering_{CAMPAIGN}/") + shell: + """ + mkdir {output} + cp -r {input} {output} + """ + +rule reconstruction: + input: directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") output: - directory("results/beamline/{CAMPAIGN}/") + directory("results/beamline/reconstruction_{CAMPAIGN}/") shell: """ mkdir {output} cp -r {input} {output} """ +########################################################################################## +# Defualt running +########################################################################################## rule beamline_local: input: "results/beamline/local/" diff --git a/benchmarks/beamline/SteeringRegression.py b/benchmarks/beamline/SteeringRegression.py index 01ed0103..2b059310 100644 --- a/benchmarks/beamline/SteeringRegression.py +++ b/benchmarks/beamline/SteeringRegression.py @@ -11,10 +11,13 @@ parser.add_argument('--batchSize', type=int, default=1024, help='Batch size for training') parser.add_argument('--epochs', type=int, default=1000, help='Number of epochs for training') parser.add_argument('--entries', type=int, default=None, help='Number of entries to process from the data files') +parser.add_argument('--treeName', type=str, default="events", help='Name of the tree in the ROOT files') +parser.add_argument('--featureName', type=str, default="_TaggerTrackerFeatureTensor_floatData", help='Name of the feature tensor') +parser.add_argument('--targetName', type=str, default="_TaggerTrackerTargetTensor_floatData", help='Name of the target tensor') args = parser.parse_args() -input_data, target_data = create_arrays(args.dataFiles, entries=args.entries) +input_data, target_data = create_arrays(args.dataFiles, entries=args.entries, treeName=args.treeName, featureName=args.featureName, targetName=args.targetName) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Using device: {device}") From ecd582954c89faaf0bf34b74dbf067ba3cc0ecc0 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 28 Jul 2025 15:23:54 +0100 Subject: [PATCH 42/65] Add resolution test --- benchmarks/beamline/reconstructionAnalysis.C | 252 +++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 benchmarks/beamline/reconstructionAnalysis.C diff --git a/benchmarks/beamline/reconstructionAnalysis.C b/benchmarks/beamline/reconstructionAnalysis.C new file mode 100644 index 00000000..b96daca9 --- /dev/null +++ b/benchmarks/beamline/reconstructionAnalysis.C @@ -0,0 +1,252 @@ +// Plots the resolution of the reconstructed particles + +#include +#include "ROOT/RDataFrame.hxx" +#include "ROOT/RDF/RInterface.hxx" +#include "ROOT/RVec.hxx" +#include "edm4hep/MCParticleCollection.h" +#include "edm4eic/ReconstructedParticleCollection.h" +#include "TCanvas.h" +#include "TStyle.h" + +void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", + TString momentumCanvasName = "momentum_resolution.png", + TString energyThetaPhiCanvasName = "energy_theta_phi_resolution.png", + TString relationCanvasName = "relation_resolution.png", + TString resolutionGraphsCanvasName = "resolution_graphs.png") { + + //Set ROOT style + gStyle->SetPadLeftMargin(0.1); // Set left margin + gStyle->SetPadRightMargin(0.0); // Set right margin + gStyle->SetPadTopMargin(0.0); // Set top margin + gStyle->SetPadBottomMargin(0.1);// Set bottom margin + gStyle->SetTitleAlign(13); + gStyle->SetTitleX(0.12); // Place the title on the top right + gStyle->SetTitleY(0.985); // Place the title on the top right + gStyle->SetTitleSize(0.08, "t"); + gStyle->SetTitleXOffset(1.0); // Adjust y-axis title offset + gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset + gStyle->SetOptStat(0); + + ROOT::RDataFrame d0("events",inFile, {"MCParticles","TaggerTrackerReconstructedParticles"}); + + auto filterDF = d0.Define("SimParticles", "MCParticles[MCParticles.generatorStatus==1 && MCParticles.PDG==11]") + .Filter("SimParticles.size()==1") + .Filter("TaggerTrackerReconstructedParticles.size()==1"); + + // Plot x,y,z momentum resolution as a function of the MCParticle momentum + auto momentumDF = filterDF + .Define("reco_momentum", "TaggerTrackerReconstructedParticles[0].momentum") + .Define("mc_momentum", "SimParticles[0].momentum") + .Define("px_rec", "reco_momentum.x") + .Define("py_rec", "reco_momentum.y") + .Define("pz_rec", "reco_momentum.z") + .Define("px_mc", "mc_momentum.x") + .Define("py_mc", "mc_momentum.y") + .Define("pz_mc", "mc_momentum.z") + // Calculate theta and phi for reco and MC + .Define("theta_rec", "std::atan2(std::sqrt(px_rec*px_rec + py_rec*py_rec), pz_rec)") + .Define("theta_mc", "std::atan2(std::sqrt(px_mc*px_mc + py_mc*py_mc), pz_mc)") + .Define("phi_rec", "std::atan2(py_rec, px_rec)") + .Define("phi_mc", "std::atan2(py_mc, px_mc)") + // Calculate resolutions + .Define("px_diff", "(px_rec - px_mc)") + .Define("py_diff", "(py_rec - py_mc)") + .Define("pz_res", "(pz_rec - pz_mc)/pz_mc") + .Define("E_rec", "TaggerTrackerReconstructedParticles[0].energy") + .Define("E_mc", "std::sqrt(px_mc*px_mc + py_mc*py_mc + pz_mc*pz_mc + SimParticles[0].mass*SimParticles[0].mass)") + .Define("E_res", "(E_rec - E_mc)/E_mc") + .Define("theta_diff", "(theta_rec - theta_mc)") + .Define("phi_diff", "ROOT::VecOps::DeltaPhi(phi_rec, phi_mc)"); + + //Print the size of the original DataFrame + std::cout << "Original DataFrame size: " << d0.Count().GetValue() << std::endl; + //Print the size of the filtered DataFrame + std::cout << "Filtered DataFrame size: " << filterDF.Count().GetValue() << std::endl; + + int momentumBins = 100; + float momentumXRange[2] = {-0.1, 0.1}; + float momentumYRange[2] = {-0.1, 0.1}; + float momentumZRange[2] = {-10, 0}; + + int momentumResolutionBins = 100; + float momentumDifferenceXRange[2] = {-0.05, 0.05}; + float momentumDifferenceYRange[2] = {-0.05, 0.05}; + float momentumResolutionZRange[2] = {-0.1, 0.1}; + + int energyBins = 100; + int thetaBins = 100; + int phiBins = 100; + float energyRange[2] = {3.5, 10}; // GeV + float thetaRange[2] = {3.134, TMath::Pi()}; // radians from 3.1 to pi + float phiRange[2] = {-TMath::Pi(), TMath::Pi()}; // radians from -pi to pi + + int resolutionBins = 100; + float energyResolutionRange[2] = {-0.2, 0.2}; + float thetaResolutionRange[2] = {-0.005, 0.005}; + float phiResolutionRange[2] = {-TMath::Pi()/2, TMath::Pi()/2}; // radians from -pi/2 to pi/2 + + // Plot reconstructed vs montecarlo momentum components + auto px_Hist = momentumDF.Histo2D({"px_vs_px", "Reconstructed vs MC px; px reconstructed [GeV]; px MC [GeV]", momentumBins, momentumXRange[0], momentumXRange[1], momentumBins, momentumXRange[0], momentumXRange[1]}, "px_rec", "px_mc"); + auto py_Hist = momentumDF.Histo2D({"py_vs_py", "Reconstructed vs MC py; py reconstructed [GeV]; py MC [GeV]", momentumBins, momentumYRange[0], momentumYRange[1], momentumBins, momentumYRange[0], momentumYRange[1]}, "py_rec", "py_mc"); + auto pz_Hist = momentumDF.Histo2D({"pz_vs_pz", "Reconstructed vs MC pz; pz reconstructed [GeV]; pz MC [GeV]", momentumBins, momentumZRange[0], momentumZRange[1], momentumBins, momentumZRange[0], momentumZRange[1]}, "pz_rec", "pz_mc"); + + // Plot individual momentum resolutions + auto px_diff_Hist = momentumDF.Histo1D({"px_diff", "px difference; px difference [GeV]; Entries", momentumResolutionBins, momentumDifferenceXRange[0], momentumDifferenceXRange[1]}, "px_diff"); + auto py_diff_Hist = momentumDF.Histo1D({"py_diff", "py difference; py difference [GeV]; Entries", momentumResolutionBins, momentumDifferenceYRange[0], momentumDifferenceYRange[1]}, "py_diff"); + auto pz_res_Hist = momentumDF.Histo1D({"pz_res", "pz resolution; pz resolution [GeV]; Entries", momentumResolutionBins, momentumResolutionZRange[0], momentumResolutionZRange[1]}, "pz_res"); + + // Plot reconstructed vs montecarlo energy, theta and phi + auto E_Hist = momentumDF.Histo2D({"E_vs_E", "Reconstructed vs MC energy; E reconstructed [GeV]; E MC [GeV]", energyBins, energyRange[0], energyRange[1], energyBins, energyRange[0], energyRange[1]}, "E_rec", "E_mc"); + auto theta_Hist = momentumDF.Histo2D({"theta_vs_theta", "Reconstructed vs MC theta; theta reconstructed [rad]; theta MC [rad]", thetaBins, thetaRange[0], thetaRange[1], thetaBins, thetaRange[0], thetaRange[1]}, "theta_rec", "theta_mc"); + auto phi_Hist = momentumDF.Histo2D({"phi_vs_phi", "Reconstructed vs MC phi; phi reconstructed [rad]; phi MC [rad]", phiBins, phiRange[0], phiRange[1], phiBins, phiRange[0], phiRange[1]}, "phi_rec", "phi_mc"); + + auto E_res_Hist = momentumDF.Histo1D({"E_res", "E resolution; E resolution [GeV]; Entries", resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "E_res"); + auto theta_diff_Hist = momentumDF.Histo1D({"theta_diff", "theta difference; theta difference [rad]; Entries", resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_diff"); + auto phi_diff_Hist = momentumDF.Histo1D({"phi_diff", "phi difference; phi difference [rad]; Entries", resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_diff"); + + // Plot Reconstructed energy, theta and phi resolutions as a function of each mc value of energy, thata and phi + auto E_res_vs_E_Hist = momentumDF.Histo2D({"E_res_vs_E", "E resolution vs E MC; E MC [GeV]; E resolution [GeV]", energyBins, energyRange[0], energyRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "E_mc", "E_res"); + auto E_res_vs_theta_Hist = momentumDF.Histo2D({"E_res_vs_theta", "E resolution vs theta MC; theta MC [rad]; E resolution [GeV]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "theta_mc", "E_res"); + auto E_res_vs_phi_Hist = momentumDF.Histo2D({"E_res_vs_phi", "E resolution vs phi MC; phi MC [rad]; E resolution [GeV]", phiBins, phiRange[0], phiRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "phi_mc", "E_res"); + auto theta_diff_vs_E_Hist = momentumDF.Histo2D({"theta_diff_vs_E", "theta difference vs E MC; E MC [GeV]; theta difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "E_mc", "theta_diff"); + auto theta_diff_vs_theta_Hist = momentumDF.Histo2D({"theta_diff_vs_theta", "theta difference vs theta MC; theta MC [rad]; theta difference [rad]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_mc", "theta_diff"); + auto theta_diff_vs_phi_Hist = momentumDF.Histo2D({"theta_diff_vs_phi", "theta difference vs phi MC; phi MC [rad]; theta difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "phi_mc", "theta_diff"); + auto phi_diff_vs_E_Hist = momentumDF.Histo2D({"phi_diff_vs_E", "phi difference vs E MC; E MC [GeV]; phi difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "E_mc", "phi_diff"); + auto phi_diff_vs_theta_Hist = momentumDF.Histo2D({"phi_diff_vs_theta", "phi difference vs theta MC; theta MC [rad]; phi difference [rad]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "theta_mc", "phi_diff"); + auto phi_diff_vs_phi_Hist = momentumDF.Histo2D({"phi_diff_vs_phi", "phi difference vs phi MC; phi MC [rad]; phi difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_mc", "phi_diff"); + + // Create canvas for momentum component plots + TCanvas *cMomentum = new TCanvas("momentum_canvas", "Momentum Resolution", 3000, 1600); + cMomentum->Divide(3, 2); + cMomentum->cd(1); + px_Hist->Draw("colz"); + cMomentum->cd(2); + py_Hist->Draw("colz"); + cMomentum->cd(3); + pz_Hist->Draw("colz"); + cMomentum->cd(4); + px_diff_Hist->Draw(); + cMomentum->cd(5); + py_diff_Hist->Draw(); + cMomentum->cd(6); + pz_res_Hist->Draw(); + cMomentum->SetGrid(); + cMomentum->Update(); + // Save the canvas as a PNG file + cMomentum->SaveAs(momentumCanvasName); + + // Create canvas for energy, theta and phi resolution plots + TCanvas *cEnergyThetaPhi = new TCanvas("energy_theta_phi_canvas", "Energy, Theta and Phi Resolution", 3000, 1600); + cEnergyThetaPhi->Divide(3, 2); + cEnergyThetaPhi->cd(1); + E_Hist->Draw("colz"); + cEnergyThetaPhi->cd(2); + theta_Hist->Draw("colz"); + cEnergyThetaPhi->cd(3); + phi_Hist->Draw("colz"); + cEnergyThetaPhi->cd(4); + E_res_Hist->Draw(); + cEnergyThetaPhi->cd(5); + theta_diff_Hist->Draw(); + cEnergyThetaPhi->cd(6); + phi_diff_Hist->Draw(); + cEnergyThetaPhi->SetGrid(); + cEnergyThetaPhi->Update(); + // Save the canvas as a PNG file + cEnergyThetaPhi->SaveAs(energyThetaPhiCanvasName); + + // Create canvas for resolution vs MC values + TCanvas *cResolutionVsMC = new TCanvas("resolution_vs_mc_canvas", "Resolution vs MC Values", 3000, 1600); + cResolutionVsMC->Divide(3, 3); + cResolutionVsMC->cd(1); + E_res_vs_E_Hist->Draw("colz"); + cResolutionVsMC->cd(2); + E_res_vs_theta_Hist->Draw("colz"); + cResolutionVsMC->cd(3); + E_res_vs_phi_Hist->Draw("colz"); + cResolutionVsMC->cd(4); + theta_diff_vs_E_Hist->Draw("colz"); + cResolutionVsMC->cd(5); + theta_diff_vs_theta_Hist->Draw("colz"); + cResolutionVsMC->cd(6); + theta_diff_vs_phi_Hist->Draw("colz"); + cResolutionVsMC->cd(7); + phi_diff_vs_E_Hist->Draw("colz"); + cResolutionVsMC->cd(8); + phi_diff_vs_theta_Hist->Draw("colz"); + cResolutionVsMC->cd(9); + phi_diff_vs_phi_Hist->Draw("colz"); + cResolutionVsMC->SetGrid(); + cResolutionVsMC->Update(); + // Save the canvas as a PNG file + cResolutionVsMC->SaveAs(relationCanvasName); + + // Fit Gaussians to the E vs E histogram slices + TObjArray* fitE_vs_E = new TObjArray(); + TObjArray* fitTheta_vs_E = new TObjArray(); + TObjArray* fitPhi_vs_theta = new TObjArray(); + E_res_vs_E_Hist->FitSlicesY(nullptr, 1, -1, 0, "Q", fitE_vs_E); + theta_diff_vs_E_Hist->FitSlicesY(nullptr, 1, -1, 0, "Q", fitTheta_vs_E); + phi_diff_vs_theta_Hist->FitSlicesY(nullptr, 1, -1, 0, "Q", fitPhi_vs_theta); + + // Create graphs containing the fitted means and standard deviations + TH1* hE_vs_E_mean = (TH1*)fitE_vs_E->At(1); // mean values (index 1) + TH1* hE_vs_E_stddev = (TH1*)fitE_vs_E->At(2); // stddev values (index 2) + TH1* hTheta_vs_E_mean = (TH1*)fitTheta_vs_E->At(1); // mean values (index 1) + TH1* hTheta_vs_E_stddev = (TH1*)fitTheta_vs_E->At(2); // stddev values (index 2) + TH1* hPhi_vs_theta_mean = (TH1*)fitPhi_vs_theta->At(1); // mean values (index 1) + TH1* hPhi_vs_theta_stddev = (TH1*)fitPhi_vs_theta->At(2); // stddev values (index 2) + + // Create a canvas for the resolution graphs + TCanvas *cResolutionGraphs = new TCanvas("resolution_graphs_canvas", "Resolution Graphs", 1200, 800); + cResolutionGraphs->Divide(3, 2); + cResolutionGraphs->cd(1); + hE_vs_E_mean->SetTitle("Mean Energy Offset vs E MC; Energy MC [GeV]; Mean Energy Offset [GeV]"); + hE_vs_E_mean->SetMarkerStyle(20); + hE_vs_E_mean->SetMarkerColor(kBlue); + hE_vs_E_mean->SetMaximum(0.02); // Adjust maximum for better visibility + hE_vs_E_mean->SetMinimum(-0.02); // Adjust minimum for better visibility + hE_vs_E_mean->Draw(); + cResolutionGraphs->cd(4); + hE_vs_E_stddev->SetTitle("Energy Resolution vs E MC; Energy MC [GeV]; Energy Resolution [GeV]"); + hE_vs_E_stddev->SetMarkerStyle(20); + hE_vs_E_stddev->SetMarkerColor(kRed); + hE_vs_E_stddev->SetMaximum(0.04); // Adjust maximum for better visibility + hE_vs_E_stddev->SetMinimum(0); // Adjust minimum for better visibility + hE_vs_E_stddev->Draw(); + cResolutionGraphs->cd(2); + hTheta_vs_E_mean->SetTitle("Mean Theta Offset vs E MC; Energy MC [GeV]; Mean Theta Offset [rad]"); + hTheta_vs_E_mean->SetMarkerStyle(20); + hTheta_vs_E_mean->SetMarkerColor(kBlue); + hTheta_vs_E_mean->SetMaximum(0.0003); // Adjust maximum for better visibility + hTheta_vs_E_mean->SetMinimum(-0.0003); // Adjust minimum for better visibility + hTheta_vs_E_mean->Draw(); + cResolutionGraphs->cd(5); + hTheta_vs_E_stddev->SetTitle("Std Dev Theta Offset vs E MC; Energy MC [GeV]; Std Dev Theta Offset [rad]"); + hTheta_vs_E_stddev->SetMarkerStyle(20); + hTheta_vs_E_stddev->SetMarkerColor(kRed); + hTheta_vs_E_stddev->SetMaximum(0.0005); // Adjust maximum for better visibility + hTheta_vs_E_stddev->SetMinimum(0); // Adjust minimum for better visibility + hTheta_vs_E_stddev->Draw(); + cResolutionGraphs->cd(3); + hPhi_vs_theta_mean->SetTitle("Mean Phi Offset vs theta MC; theta MC [rad]; Mean Phi Offset [rad]"); + hPhi_vs_theta_mean->SetMarkerStyle(20); + hPhi_vs_theta_mean->SetMarkerColor(kBlue); + hPhi_vs_theta_mean->SetMaximum(0.5); // Adjust maximum for better visibility + hPhi_vs_theta_mean->SetMinimum(0.5); // Adjust minimum for better visibility + hPhi_vs_theta_mean->Draw(); + cResolutionGraphs->cd(6); + hPhi_vs_theta_stddev->SetTitle("Std Dev Phi Offset vs theta MC; theta MC [rad]; Std Dev Phi Offset [rad]"); + hPhi_vs_theta_stddev->SetMarkerStyle(20); + hPhi_vs_theta_stddev->SetMarkerColor(kRed); + hPhi_vs_theta_stddev->SetMaximum(TMath::Pi()/2); // Adjust maximum for better visibility + hPhi_vs_theta_stddev->SetMinimum(0); // Adjust minimum for better visibility + hPhi_vs_theta_stddev->Draw(); + cResolutionGraphs->SetGrid(); + cResolutionGraphs->Update(); + // Save the canvas as a PNG file + cResolutionGraphs->SaveAs(resolutionGraphsCanvasName); + +} + From 91b5041c34c3b7341a52ea2ade93162a4cd01b97 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 28 Jul 2025 16:24:52 +0100 Subject: [PATCH 43/65] Change phi plots to degrees --- benchmarks/beamline/reconstructionAnalysis.C | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/benchmarks/beamline/reconstructionAnalysis.C b/benchmarks/beamline/reconstructionAnalysis.C index b96daca9..305fa2b2 100644 --- a/benchmarks/beamline/reconstructionAnalysis.C +++ b/benchmarks/beamline/reconstructionAnalysis.C @@ -47,8 +47,10 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip // Calculate theta and phi for reco and MC .Define("theta_rec", "std::atan2(std::sqrt(px_rec*px_rec + py_rec*py_rec), pz_rec)") .Define("theta_mc", "std::atan2(std::sqrt(px_mc*px_mc + py_mc*py_mc), pz_mc)") - .Define("phi_rec", "std::atan2(py_rec, px_rec)") - .Define("phi_mc", "std::atan2(py_mc, px_mc)") + .Define("phi_rec_rad", "std::atan2(py_rec, px_rec)") + .Define("phi_mc_rad", "std::atan2(py_mc, px_mc)") + .Define("phi_rec", "TMath::RadToDeg()*phi_rec_rad") + .Define("phi_mc", "TMath::RadToDeg()*phi_mc_rad") // Calculate resolutions .Define("px_diff", "(px_rec - px_mc)") .Define("py_diff", "(py_rec - py_mc)") @@ -57,7 +59,7 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip .Define("E_mc", "std::sqrt(px_mc*px_mc + py_mc*py_mc + pz_mc*pz_mc + SimParticles[0].mass*SimParticles[0].mass)") .Define("E_res", "(E_rec - E_mc)/E_mc") .Define("theta_diff", "(theta_rec - theta_mc)") - .Define("phi_diff", "ROOT::VecOps::DeltaPhi(phi_rec, phi_mc)"); + .Define("phi_diff", "TMath::RadToDeg()*ROOT::VecOps::DeltaPhi(phi_rec_rad, phi_mc_rad)"); //Print the size of the original DataFrame std::cout << "Original DataFrame size: " << d0.Count().GetValue() << std::endl; @@ -79,12 +81,12 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip int phiBins = 100; float energyRange[2] = {3.5, 10}; // GeV float thetaRange[2] = {3.134, TMath::Pi()}; // radians from 3.1 to pi - float phiRange[2] = {-TMath::Pi(), TMath::Pi()}; // radians from -pi to pi + float phiRange[2] = {-180, 180}; // degrees from -180 to 180 int resolutionBins = 100; float energyResolutionRange[2] = {-0.2, 0.2}; float thetaResolutionRange[2] = {-0.005, 0.005}; - float phiResolutionRange[2] = {-TMath::Pi()/2, TMath::Pi()/2}; // radians from -pi/2 to pi/2 + float phiResolutionRange[2] = {-90, 90}; // degrees from -90 to 90 // Plot reconstructed vs montecarlo momentum components auto px_Hist = momentumDF.Histo2D({"px_vs_px", "Reconstructed vs MC px; px reconstructed [GeV]; px MC [GeV]", momentumBins, momentumXRange[0], momentumXRange[1], momentumBins, momentumXRange[0], momentumXRange[1]}, "px_rec", "px_mc"); @@ -233,14 +235,14 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip hPhi_vs_theta_mean->SetTitle("Mean Phi Offset vs theta MC; theta MC [rad]; Mean Phi Offset [rad]"); hPhi_vs_theta_mean->SetMarkerStyle(20); hPhi_vs_theta_mean->SetMarkerColor(kBlue); - hPhi_vs_theta_mean->SetMaximum(0.5); // Adjust maximum for better visibility - hPhi_vs_theta_mean->SetMinimum(0.5); // Adjust minimum for better visibility + hPhi_vs_theta_mean->SetMaximum(20); // Adjust maximum for better visibility + hPhi_vs_theta_mean->SetMinimum(-20); // Adjust minimum for better visibility hPhi_vs_theta_mean->Draw(); cResolutionGraphs->cd(6); hPhi_vs_theta_stddev->SetTitle("Std Dev Phi Offset vs theta MC; theta MC [rad]; Std Dev Phi Offset [rad]"); hPhi_vs_theta_stddev->SetMarkerStyle(20); hPhi_vs_theta_stddev->SetMarkerColor(kRed); - hPhi_vs_theta_stddev->SetMaximum(TMath::Pi()/2); // Adjust maximum for better visibility + hPhi_vs_theta_stddev->SetMaximum(60); // Adjust maximum for better visibility hPhi_vs_theta_stddev->SetMinimum(0); // Adjust minimum for better visibility hPhi_vs_theta_stddev->Draw(); cResolutionGraphs->SetGrid(); @@ -248,5 +250,8 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip // Save the canvas as a PNG file cResolutionGraphs->SaveAs(resolutionGraphsCanvasName); + // Check to see if resolutions pass tests. + + } From 7723f89085f78f4725e1d4a4602adf37f1303b32 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 29 Jul 2025 19:22:02 +0100 Subject: [PATCH 44/65] Extra workflow steps and fixes --- benchmarks/beamline/RegressionModel.py | 9 +-- benchmarks/beamline/Snakefile | 70 ++++++++++++++++---- benchmarks/beamline/reconstructionAnalysis.C | 21 +++--- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index fd388241..8b48fe69 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -39,6 +39,7 @@ def project_numpy(self, arr): class RegressionModel(nn.Module): def __init__(self, project=True): super(RegressionModel, self).__init__() + self.project = project self.project_to_x0 = ProjectToX0Plane() if project else None self.fc1 = nn.Linear(4, 512) self.fc2 = nn.Linear(512, 64) @@ -67,8 +68,8 @@ def forward(self, x): def _core_forward(self, x): # Core fully connected layers - x = torch.tanh(self.fc1(x)) - x = torch.tanh(self.fc2(x)) + x = torch.relu(self.fc1(x)) + x = torch.relu(self.fc2(x)) x = self.fc3(x) return x @@ -105,7 +106,7 @@ def makeModel(project=True): # Create the model model = RegressionModel(project=project) # Define the optimizer - optimizer = optim.Adam(model.parameters(), lr=0.0001) + optimizer = optim.Adam(model.parameters(), lr=0.002) # Define the loss function criterion = nn.MSELoss() @@ -117,7 +118,7 @@ def trainModel(epochs, train_loader, val_loader, device): # Check shape of input data to see if projection needs to be done if train_loader.dataset.tensors[0].shape[1] == 6: project = True - if train_loader.dataset.tensors[0].shape[1] == 4: + elif train_loader.dataset.tensors[0].shape[1] == 4: project = False else: raise ValueError("Input data must have shape (N, 6) or (N, 4)") diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index db099a8a..e31c53c1 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -120,16 +120,17 @@ rule beamline_training_sim: --runType run \ --random.seed 1 \ --inputFiles {input.events} \ + --numberOfEvents 1000000 \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ --outputFile {output} \ - --physics.rangecut 1000*m + --physics.rangecut 100*m """ # Run EICrecon and create input tensors for reconstruction. rule beamline_reconstruction_tensors_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", + output_collections="BackwardsBeamlineHits,TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", @@ -158,8 +159,19 @@ rule beamline_reconstruction_particles_eicrecon: """ # Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles -#rule beamline_reconstruction_particles_test: - +rule beamline_reconstruction_particles_test: + input: + script=workflow.source_path("reconstructionAnalysis.C"), + data=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + output: + momentumCanvas=ANALYSISDIR+"momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"resolution_graphs_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' + """ ########################################################################################## ### Rules for training new onnx reconstruction neural network @@ -189,23 +201,55 @@ rule beamline_steering_reconstruction_training: onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", shell: """ - python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 400 + python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 100 """ -# Tests new regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. -rule beamline_steering_reconstruction_test: +# Run eicrecon with the newly trained neural network +rule beamline_steering_reconstruction_particles_trained_eicrecon: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", + ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: - script=workflow.source_path("TestModel.py"), - data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", - model=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", + data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", + output: + eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + shell: + """ + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} + """ + +# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles +rule beamline_reconstruction_particles_trained_test: + input: + script=workflow.source_path("reconstructionAnalysis.C"), + data=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", output: - directory=directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") + momentumCanvas=ANALYSISDIR+"trained_momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"trained_energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"trained_relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"trained_resolution_graphs_{CAMPAIGN}.png", shell: """ - mkdir {output.directory} - python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output.directory} + root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' """ +# Tests new regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +#rule beamline_steering_reconstruction_test: +# input: +# script=workflow.source_path("TestModel.py"), +# data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", +# model=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", +# output: +# directory=directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") +# shell: +# """ +# mkdir {output.directory} +# python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output.directory} +# """ + ########################################################################################## # Combine results ########################################################################################## diff --git a/benchmarks/beamline/reconstructionAnalysis.C b/benchmarks/beamline/reconstructionAnalysis.C index 305fa2b2..2b4ed743 100644 --- a/benchmarks/beamline/reconstructionAnalysis.C +++ b/benchmarks/beamline/reconstructionAnalysis.C @@ -10,6 +10,7 @@ #include "TStyle.h" void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", + float beamEnergy = 10.0, TString momentumCanvasName = "momentum_resolution.png", TString energyThetaPhiCanvasName = "energy_theta_phi_resolution.png", TString relationCanvasName = "relation_resolution.png", @@ -69,7 +70,7 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip int momentumBins = 100; float momentumXRange[2] = {-0.1, 0.1}; float momentumYRange[2] = {-0.1, 0.1}; - float momentumZRange[2] = {-10, 0}; + float momentumZRange[2] = {-beamEnergy, 0}; int momentumResolutionBins = 100; float momentumDifferenceXRange[2] = {-0.05, 0.05}; @@ -79,7 +80,7 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip int energyBins = 100; int thetaBins = 100; int phiBins = 100; - float energyRange[2] = {3.5, 10}; // GeV + float energyRange[2] = {3.5, beamEnergy}; // GeV float thetaRange[2] = {3.134, TMath::Pi()}; // radians from 3.1 to pi float phiRange[2] = {-180, 180}; // degrees from -180 to 180 @@ -101,22 +102,22 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip // Plot reconstructed vs montecarlo energy, theta and phi auto E_Hist = momentumDF.Histo2D({"E_vs_E", "Reconstructed vs MC energy; E reconstructed [GeV]; E MC [GeV]", energyBins, energyRange[0], energyRange[1], energyBins, energyRange[0], energyRange[1]}, "E_rec", "E_mc"); auto theta_Hist = momentumDF.Histo2D({"theta_vs_theta", "Reconstructed vs MC theta; theta reconstructed [rad]; theta MC [rad]", thetaBins, thetaRange[0], thetaRange[1], thetaBins, thetaRange[0], thetaRange[1]}, "theta_rec", "theta_mc"); - auto phi_Hist = momentumDF.Histo2D({"phi_vs_phi", "Reconstructed vs MC phi; phi reconstructed [rad]; phi MC [rad]", phiBins, phiRange[0], phiRange[1], phiBins, phiRange[0], phiRange[1]}, "phi_rec", "phi_mc"); + auto phi_Hist = momentumDF.Histo2D({"phi_vs_phi", "Reconstructed vs MC phi; phi reconstructed [deg]; phi MC [deg]", phiBins, phiRange[0], phiRange[1], phiBins, phiRange[0], phiRange[1]}, "phi_rec", "phi_mc"); auto E_res_Hist = momentumDF.Histo1D({"E_res", "E resolution; E resolution [GeV]; Entries", resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "E_res"); auto theta_diff_Hist = momentumDF.Histo1D({"theta_diff", "theta difference; theta difference [rad]; Entries", resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_diff"); - auto phi_diff_Hist = momentumDF.Histo1D({"phi_diff", "phi difference; phi difference [rad]; Entries", resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_diff"); + auto phi_diff_Hist = momentumDF.Histo1D({"phi_diff", "phi difference; phi difference [deg]; Entries", resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_diff"); // Plot Reconstructed energy, theta and phi resolutions as a function of each mc value of energy, thata and phi auto E_res_vs_E_Hist = momentumDF.Histo2D({"E_res_vs_E", "E resolution vs E MC; E MC [GeV]; E resolution [GeV]", energyBins, energyRange[0], energyRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "E_mc", "E_res"); auto E_res_vs_theta_Hist = momentumDF.Histo2D({"E_res_vs_theta", "E resolution vs theta MC; theta MC [rad]; E resolution [GeV]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "theta_mc", "E_res"); - auto E_res_vs_phi_Hist = momentumDF.Histo2D({"E_res_vs_phi", "E resolution vs phi MC; phi MC [rad]; E resolution [GeV]", phiBins, phiRange[0], phiRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "phi_mc", "E_res"); + auto E_res_vs_phi_Hist = momentumDF.Histo2D({"E_res_vs_phi", "E resolution vs phi MC; phi MC [deg]; E resolution [GeV]", phiBins, phiRange[0], phiRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "phi_mc", "E_res"); auto theta_diff_vs_E_Hist = momentumDF.Histo2D({"theta_diff_vs_E", "theta difference vs E MC; E MC [GeV]; theta difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "E_mc", "theta_diff"); auto theta_diff_vs_theta_Hist = momentumDF.Histo2D({"theta_diff_vs_theta", "theta difference vs theta MC; theta MC [rad]; theta difference [rad]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_mc", "theta_diff"); - auto theta_diff_vs_phi_Hist = momentumDF.Histo2D({"theta_diff_vs_phi", "theta difference vs phi MC; phi MC [rad]; theta difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "phi_mc", "theta_diff"); + auto theta_diff_vs_phi_Hist = momentumDF.Histo2D({"theta_diff_vs_phi", "theta difference vs phi MC; phi MC [deg]; theta difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "phi_mc", "theta_diff"); auto phi_diff_vs_E_Hist = momentumDF.Histo2D({"phi_diff_vs_E", "phi difference vs E MC; E MC [GeV]; phi difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "E_mc", "phi_diff"); - auto phi_diff_vs_theta_Hist = momentumDF.Histo2D({"phi_diff_vs_theta", "phi difference vs theta MC; theta MC [rad]; phi difference [rad]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "theta_mc", "phi_diff"); - auto phi_diff_vs_phi_Hist = momentumDF.Histo2D({"phi_diff_vs_phi", "phi difference vs phi MC; phi MC [rad]; phi difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_mc", "phi_diff"); + auto phi_diff_vs_theta_Hist = momentumDF.Histo2D({"phi_diff_vs_theta", "phi difference vs theta MC; theta MC [rad]; phi difference [deg]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "theta_mc", "phi_diff"); + auto phi_diff_vs_phi_Hist = momentumDF.Histo2D({"phi_diff_vs_phi", "phi difference vs phi MC; phi MC [deg]; phi difference [deg]", phiBins, phiRange[0], phiRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_mc", "phi_diff"); // Create canvas for momentum component plots TCanvas *cMomentum = new TCanvas("momentum_canvas", "Momentum Resolution", 3000, 1600); @@ -235,8 +236,8 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip hPhi_vs_theta_mean->SetTitle("Mean Phi Offset vs theta MC; theta MC [rad]; Mean Phi Offset [rad]"); hPhi_vs_theta_mean->SetMarkerStyle(20); hPhi_vs_theta_mean->SetMarkerColor(kBlue); - hPhi_vs_theta_mean->SetMaximum(20); // Adjust maximum for better visibility - hPhi_vs_theta_mean->SetMinimum(-20); // Adjust minimum for better visibility + hPhi_vs_theta_mean->SetMaximum(5); // Adjust maximum for better visibility + hPhi_vs_theta_mean->SetMinimum(-5); // Adjust minimum for better visibility hPhi_vs_theta_mean->Draw(); cResolutionGraphs->cd(6); hPhi_vs_theta_stddev->SetTitle("Std Dev Phi Offset vs theta MC; theta MC [rad]; Std Dev Phi Offset [rad]"); From 322768a9d4fa1d206d27de18fc4a8c79a4c2724a Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 30 Jul 2025 08:27:38 +0100 Subject: [PATCH 45/65] Swapping to huberloss helps --- benchmarks/beamline/RegressionModel.py | 4 ++-- benchmarks/beamline/Snakefile | 10 +++++----- benchmarks/beamline/reconstructionAnalysis.C | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/beamline/RegressionModel.py index 8b48fe69..4b5fb972 100644 --- a/benchmarks/beamline/RegressionModel.py +++ b/benchmarks/beamline/RegressionModel.py @@ -106,9 +106,9 @@ def makeModel(project=True): # Create the model model = RegressionModel(project=project) # Define the optimizer - optimizer = optim.Adam(model.parameters(), lr=0.002) + optimizer = optim.Adam(model.parameters(), lr=0.0004) # Define the loss function - criterion = nn.MSELoss() + criterion = nn.HuberLoss(delta=0.2) # Huber loss for regression tasks return model, optimizer, criterion diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index e31c53c1..47a1657d 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -198,10 +198,10 @@ rule beamline_steering_reconstruction_training: scriptmodel=workflow.source_path("RegressionModel.py"), data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", output: - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", shell: """ - python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 100 + python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 1000 """ # Run eicrecon with the newly trained neural network @@ -212,12 +212,12 @@ rule beamline_steering_reconstruction_particles_trained_eicrecon: ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", output: - eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", shell: """ - eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=1000000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ diff --git a/benchmarks/beamline/reconstructionAnalysis.C b/benchmarks/beamline/reconstructionAnalysis.C index 2b4ed743..73686666 100644 --- a/benchmarks/beamline/reconstructionAnalysis.C +++ b/benchmarks/beamline/reconstructionAnalysis.C @@ -85,8 +85,8 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip float phiRange[2] = {-180, 180}; // degrees from -180 to 180 int resolutionBins = 100; - float energyResolutionRange[2] = {-0.2, 0.2}; - float thetaResolutionRange[2] = {-0.005, 0.005}; + float energyResolutionRange[2] = {-0.1, 0.1}; + float thetaResolutionRange[2] = {-0.003, 0.003}; float phiResolutionRange[2] = {-90, 90}; // degrees from -90 to 90 // Plot reconstructed vs montecarlo momentum components @@ -208,8 +208,8 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip hE_vs_E_mean->SetTitle("Mean Energy Offset vs E MC; Energy MC [GeV]; Mean Energy Offset [GeV]"); hE_vs_E_mean->SetMarkerStyle(20); hE_vs_E_mean->SetMarkerColor(kBlue); - hE_vs_E_mean->SetMaximum(0.02); // Adjust maximum for better visibility - hE_vs_E_mean->SetMinimum(-0.02); // Adjust minimum for better visibility + hE_vs_E_mean->SetMaximum(0.01); // Adjust maximum for better visibility + hE_vs_E_mean->SetMinimum(-0.01); // Adjust minimum for better visibility hE_vs_E_mean->Draw(); cResolutionGraphs->cd(4); hE_vs_E_stddev->SetTitle("Energy Resolution vs E MC; Energy MC [GeV]; Energy Resolution [GeV]"); From 02389789a1fd5cbb2486f1ebfb7c0e360aaf25e4 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 17:14:37 +0100 Subject: [PATCH 46/65] Move reconstruction to separate benchmark --- benchmarks/beamline/Snakefile | 173 ------------------ .../ProcessData.py | 0 .../RegressionModel.py | 0 .../SteeringRegression.py | 0 .../filterHEPMC3.py | 0 .../processData.C | 0 .../reconstructionAnalysis.C | 1 + 7 files changed, 1 insertion(+), 173 deletions(-) rename benchmarks/{beamline => lowq2_reconstruction}/ProcessData.py (100%) rename benchmarks/{beamline => lowq2_reconstruction}/RegressionModel.py (100%) rename benchmarks/{beamline => lowq2_reconstruction}/SteeringRegression.py (100%) rename benchmarks/{beamline => lowq2_reconstruction}/filterHEPMC3.py (100%) rename benchmarks/{beamline => lowq2_reconstruction}/processData.C (100%) rename benchmarks/{beamline => lowq2_reconstruction}/reconstructionAnalysis.C (99%) diff --git a/benchmarks/beamline/Snakefile b/benchmarks/beamline/Snakefile index 10425fbc..507d6473 100644 --- a/benchmarks/beamline/Snakefile +++ b/benchmarks/beamline/Snakefile @@ -90,168 +90,6 @@ rule beamline_acceptance_analysis: "{output.entrys_canvas}")' """ -########################################################################################## -### Rules for checking the reconstruction of the electron momentum -########################################################################################## - -# Filter LowQ2 events from xrootd server - Using the acceptance events didn't work -# ToDo: Do a proper investigation into why the feature to target mapping isn't unique -rule filter_hepmc_for_training: - input: - warmup="warmup/epic_ip6_extended.edm4hep.root", - script=workflow.source_path("filterHEPMC3.py"), - output: - SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", - params: - events="root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/SIDIS/pythia6-eic/1.0.0/18x275/q2_0to1/pythia_ep_noradcor_18x275_q2_0.000000001_1.0_run1.ab.hepmc3.tree.root", - shell: - """ - python {input.script} --outFile {output} --inFile {params.events} - """ - -# Run pythia6 Low-Q2 events through the simulation -rule beamline_training_sim: - input: - warmup="warmup/epic_ip6_extended.edm4hep.root", - events=SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", - output: - SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", - shell: - """ - exec npsim \ - --runType run \ - --random.seed 1 \ - --inputFiles {input.events} \ - --numberOfEvents 1000000 \ - --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ - --outputFile {output} \ - --physics.rangecut 100*m - """ - -# Run EICrecon and create input tensors for reconstruction. -rule beamline_reconstruction_tensors_eicrecon: - params: - xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="BackwardsBeamlineHits,TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", - ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" - input: - data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", - output: - eicreconfile=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - shell: - """ - eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ - -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} - """ - -# Run EICrecon to create TaggerTrackerReconstructedParticles -rule beamline_reconstruction_particles_eicrecon: - params: - xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", - ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" - input: - data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - output: - eicreconfile=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", - shell: - """ - eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ - -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} - """ - -# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles -rule beamline_reconstruction_particles_test: - input: - script=workflow.source_path("reconstructionAnalysis.C"), - data=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", - output: - momentumCanvas=ANALYSISDIR+"momentum_resolution_{CAMPAIGN}.png", - energyThetaPhiCanvas=ANALYSISDIR+"energy_theta_phi_resolution_{CAMPAIGN}.png", - relationCanvas=ANALYSISDIR+"relation_resolution_{CAMPAIGN}.png", - resolutionGraphsCanvas=ANALYSISDIR+"resolution_graphs_{CAMPAIGN}.png", - shell: - """ - root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' - """ - -########################################################################################## -### Rules for training new onnx reconstruction neural network -########################################################################################## - -# Processes the simulation output data for training -rule beamline_steering_reconstruction_preparation: - input: - script=workflow.source_path("processData.C"), - data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - output: - rootfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", - shell: - """ - root -l -b -q '{input.script}("{input.data}", "{output.rootfile}",18.0)' - """ - - -# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. -rule beamline_steering_reconstruction_training: - input: - script=workflow.source_path("SteeringRegression.py"), - scriptdata=workflow.source_path("ProcessData.py"), - scriptmodel=workflow.source_path("RegressionModel.py"), - data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", - output: - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", - shell: - """ - python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 1000 - """ - -# Run eicrecon with the newly trained neural network -rule beamline_steering_reconstruction_particles_trained_eicrecon: - params: - xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", - ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" - input: - data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", - output: - eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", - shell: - """ - eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=1000000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ - -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} - """ - -# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles -rule beamline_reconstruction_particles_trained_test: - input: - script=workflow.source_path("reconstructionAnalysis.C"), - data=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", - output: - momentumCanvas=ANALYSISDIR+"trained_momentum_resolution_{CAMPAIGN}.png", - energyThetaPhiCanvas=ANALYSISDIR+"trained_energy_theta_phi_resolution_{CAMPAIGN}.png", - relationCanvas=ANALYSISDIR+"trained_relation_resolution_{CAMPAIGN}.png", - resolutionGraphsCanvas=ANALYSISDIR+"trained_resolution_graphs_{CAMPAIGN}.png", - shell: - """ - root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' - """ - -# Tests new regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. -#rule beamline_steering_reconstruction_test: -# input: -# script=workflow.source_path("TestModel.py"), -# data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", -# model=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}.onnx", -# output: -# directory=directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") -# shell: -# """ -# mkdir {output.directory} -# python {input.script} --dataFiles {input.data} --modelFile {input.model} --outDir {output.directory} -# """ - ########################################################################################## # Combine results ########################################################################################## @@ -277,17 +115,6 @@ rule beamline: cp -r {input} {output} """ -rule reconstruction: - input: - directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") - output: - directory("results/beamline/reconstruction_{CAMPAIGN}/") - shell: - """ - mkdir {output} - cp -r {input} {output} - """ - ########################################################################################## # Defualt running ########################################################################################## diff --git a/benchmarks/beamline/ProcessData.py b/benchmarks/lowq2_reconstruction/ProcessData.py similarity index 100% rename from benchmarks/beamline/ProcessData.py rename to benchmarks/lowq2_reconstruction/ProcessData.py diff --git a/benchmarks/beamline/RegressionModel.py b/benchmarks/lowq2_reconstruction/RegressionModel.py similarity index 100% rename from benchmarks/beamline/RegressionModel.py rename to benchmarks/lowq2_reconstruction/RegressionModel.py diff --git a/benchmarks/beamline/SteeringRegression.py b/benchmarks/lowq2_reconstruction/SteeringRegression.py similarity index 100% rename from benchmarks/beamline/SteeringRegression.py rename to benchmarks/lowq2_reconstruction/SteeringRegression.py diff --git a/benchmarks/beamline/filterHEPMC3.py b/benchmarks/lowq2_reconstruction/filterHEPMC3.py similarity index 100% rename from benchmarks/beamline/filterHEPMC3.py rename to benchmarks/lowq2_reconstruction/filterHEPMC3.py diff --git a/benchmarks/beamline/processData.C b/benchmarks/lowq2_reconstruction/processData.C similarity index 100% rename from benchmarks/beamline/processData.C rename to benchmarks/lowq2_reconstruction/processData.C diff --git a/benchmarks/beamline/reconstructionAnalysis.C b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C similarity index 99% rename from benchmarks/beamline/reconstructionAnalysis.C rename to benchmarks/lowq2_reconstruction/reconstructionAnalysis.C index 73686666..5d3702e5 100644 --- a/benchmarks/beamline/reconstructionAnalysis.C +++ b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C @@ -252,6 +252,7 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip cResolutionGraphs->SaveAs(resolutionGraphsCanvasName); // Check to see if resolutions pass tests. + } From 6a27a3ece3fb5032f790f4ea529da9490366e8c7 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 17:18:32 +0100 Subject: [PATCH 47/65] include lowq2_reconstruction in snakefile --- Snakefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Snakefile b/Snakefile index 4d29a433..ad676385 100644 --- a/Snakefile +++ b/Snakefile @@ -33,10 +33,11 @@ def find_epic_libraries(): # include: "benchmarks/backgrounds/Snakefile" # include: "benchmarks/backwards_ecal/Snakefile" # include: "benchmarks/barrel_ecal/Snakefile" -include: "benchmarks/beamline/Snakefile" +# include: "benchmarks/beamline/Snakefile" # include: "benchmarks/calo_pid/Snakefile" # include: "benchmarks/campaign/Snakefile" # include: "benchmarks/ecal_gaps/Snakefile" +include : "benchmarks/lowq2_reconstruction/Snakefile" # include: "benchmarks/material_scan/Snakefile" # include: "benchmarks/tracking_performances/Snakefile" # include: "benchmarks/tracking_performances_dis/Snakefile" From fafb5e10f8f93f79ae8d5c330a3ea4d91a8c0f66 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 17:22:38 +0100 Subject: [PATCH 48/65] Rename processData to cleanData --- benchmarks/lowq2_reconstruction/{processData.C => cleanData.C} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename benchmarks/lowq2_reconstruction/{processData.C => cleanData.C} (94%) diff --git a/benchmarks/lowq2_reconstruction/processData.C b/benchmarks/lowq2_reconstruction/cleanData.C similarity index 94% rename from benchmarks/lowq2_reconstruction/processData.C rename to benchmarks/lowq2_reconstruction/cleanData.C index e366993a..43501580 100644 --- a/benchmarks/lowq2_reconstruction/processData.C +++ b/benchmarks/lowq2_reconstruction/cleanData.C @@ -5,7 +5,7 @@ #include "edm4hep/SimTrackerHit.h" #include -void processData(const TString inputFile="/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestcurrent.edm4hep.root", const TString outputFile="test.root", const double BeamEnergy=18.0, const int desired_cellID = 66757, const bool appendTruth = true) { +void cleanData(const TString inputFile="/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestcurrent.edm4hep.root", const TString outputFile="test.root", const double BeamEnergy=18.0, const int desired_cellID = 66757, const bool appendTruth = true) { float momentum_tolerance = 0.01; // Define the momentum tolerance for filtering From ec179b42222fa12a6d512c70ff08fe4713d6462c Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 17:23:57 +0100 Subject: [PATCH 49/65] Rename ProcessData to LoadData --- benchmarks/lowq2_reconstruction/{ProcessData.py => LoadData.py} | 0 benchmarks/lowq2_reconstruction/SteeringRegression.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename benchmarks/lowq2_reconstruction/{ProcessData.py => LoadData.py} (100%) diff --git a/benchmarks/lowq2_reconstruction/ProcessData.py b/benchmarks/lowq2_reconstruction/LoadData.py similarity index 100% rename from benchmarks/lowq2_reconstruction/ProcessData.py rename to benchmarks/lowq2_reconstruction/LoadData.py diff --git a/benchmarks/lowq2_reconstruction/SteeringRegression.py b/benchmarks/lowq2_reconstruction/SteeringRegression.py index 2b059310..33820613 100644 --- a/benchmarks/lowq2_reconstruction/SteeringRegression.py +++ b/benchmarks/lowq2_reconstruction/SteeringRegression.py @@ -1,6 +1,6 @@ import torch import argparse -from ProcessData import create_arrays +from LoadData import create_arrays from torch.utils.data import DataLoader, TensorDataset from RegressionModel import trainModel From 5dc2f28327612a45b43d8a6c1fa486da60aa7f2c Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 17:25:15 +0100 Subject: [PATCH 50/65] Add lowq2_reconstruction snakefile --- benchmarks/lowq2_reconstruction/Snakefile | 164 ++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 benchmarks/lowq2_reconstruction/Snakefile diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile new file mode 100644 index 00000000..78d773a2 --- /dev/null +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -0,0 +1,164 @@ +SIMOUTDIR="sim_output/lowq2_reconstruction/" +ANALYSISDIR=SIMOUTDIR+"analysis/" + +########################################################################################## +### Rules for checking the reconstruction of the electron momentum +########################################################################################## + +# Filter LowQ2 events from xrootd server - Using the acceptance events didn't work +# ToDo: Do a proper investigation into why the feature to target mapping isn't unique +rule filter_hepmc_for_training: + input: + warmup="warmup/epic_ip6_extended.edm4hep.root", + script=workflow.source_path("filterHEPMC3.py"), + output: + SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", + params: + events="root://dtn-eic.jlab.org//volatile/eic/EPIC/EVGEN/SIDIS/pythia6-eic/1.0.0/18x275/q2_0to1/pythia_ep_noradcor_18x275_q2_0.000000001_1.0_run1.ab.hepmc3.tree.root", + shell: + """ + python {input.script} --outFile {output} --inFile {params.events} + """ + +# Run pythia6 Low-Q2 events through the simulation +rule beamline_training_sim: + input: + warmup="warmup/epic_ip6_extended.edm4hep.root", + events=SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", + output: + SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", + shell: + """ + exec npsim \ + --runType run \ + --random.seed 1 \ + --inputFiles {input.events} \ + --numberOfEvents 1000000 \ + --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --outputFile {output} \ + --physics.rangecut 100*m + """ + +# Run EICrecon and create input tensors for reconstruction. +rule beamline_reconstruction_tensors_eicrecon: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="BackwardsBeamlineHits,TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", + ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" + input: + data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", + output: + eicreconfile=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + shell: + """ + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} + """ + +# Run EICrecon to create TaggerTrackerReconstructedParticles +rule beamline_reconstruction_particles_eicrecon: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", + ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" + input: + data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + output: + eicreconfile=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + shell: + """ + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} + """ + +# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles +rule beamline_reconstruction_particles_test: + input: + script=workflow.source_path("reconstructionAnalysis.C"), + data=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + output: + momentumCanvas=ANALYSISDIR+"momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"resolution_graphs_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' + """ + +########################################################################################## +### Rules for training new onnx reconstruction neural network +########################################################################################## + +# Processes the simulation output data for training +rule beamline_steering_reconstruction_preparation: + input: + script=workflow.source_path("cleanData.C"), + data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + output: + rootfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + shell: + """ + root -l -b -q '{input.script}("{input.data}", "{output.rootfile}",18.0)' + """ + + +# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +rule beamline_steering_reconstruction_training: + input: + script=workflow.source_path("SteeringRegression.py"), + scriptdata=workflow.source_path("LoadData.py"), + scriptmodel=workflow.source_path("RegressionModel.py"), + data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + output: + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", + shell: + """ + python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 1000 + """ + +# Run eicrecon with the newly trained neural network +rule beamline_steering_reconstruction_particles_trained_eicrecon: + params: + xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", + output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", + ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" + input: + data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", + output: + eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", + shell: + """ + eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=1000000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} + """ + +# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles +rule beamline_reconstruction_particles_trained_test: + input: + script=workflow.source_path("reconstructionAnalysis.C"), + data=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + output: + momentumCanvas=ANALYSISDIR+"trained_momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"trained_energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"trained_relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"trained_resolution_graphs_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' + """ + +########################################################################################## +# Combine results +########################################################################################## +rule reconstruction: + input: + directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") + output: + directory("results/beamline/reconstruction_{CAMPAIGN}/") + shell: + """ + mkdir {output} + cp -r {input} {output} + """ \ No newline at end of file From 1fcba191d06f8bec7699e7495e5b512edb518c81 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 18:38:24 +0100 Subject: [PATCH 51/65] Update CI bits --- .gitlab-ci.yml | 2 ++ benchmarks/beamline/config.yml | 8 ----- benchmarks/lowq2_reconstruction/Snakefile | 40 +++++++++++++++-------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27d30f33..5616529a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -127,6 +127,7 @@ include: - local: 'benchmarks/calo_pid/config.yml' - local: 'benchmarks/campaign/config.yml' - local: 'benchmarks/ecal_gaps/config.yml' + - local: 'benchmarks/lowq2_reconstruction/config.yml' - local: 'benchmarks/tracking_detectors/config.yml' - local: 'benchmarks/tracking_performances/config.yml' - local: 'benchmarks/tracking_performances_dis/config.yml' @@ -164,6 +165,7 @@ deploy_results: - "collect_results:campaign" - "collect_results:ecal_gaps" - "collect_results:lfhcal" + - "collect_results:lowq2_reconstruction" - "collect_results:material_scan" - "collect_results:pid" - "collect_results:rich" diff --git a/benchmarks/beamline/config.yml b/benchmarks/beamline/config.yml index 5eef1937..9309cbf3 100644 --- a/benchmarks/beamline/config.yml +++ b/benchmarks/beamline/config.yml @@ -7,14 +7,6 @@ sim:beamline:beamspot: sim_output/beamline/acceptanceTestlocal.edm4hep.root \ sim_output/beamline/beamlineTestlocal.edm4hep.root -sim:beamline:acceptance: - extends: .det_benchmark - stage: simulate - script: - - | - snakemake $SNAKEMAKE_FLAGS --cores 2 \ - sim_output/beamline/acceptanceTestlocal.edm4hep.root - bench:beamline: extends: .det_benchmark stage: benchmarks diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile index 78d773a2..85067fc6 100644 --- a/benchmarks/lowq2_reconstruction/Snakefile +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -21,7 +21,7 @@ rule filter_hepmc_for_training: """ # Run pythia6 Low-Q2 events through the simulation -rule beamline_training_sim: +rule lowq2_training_sim: input: warmup="warmup/epic_ip6_extended.edm4hep.root", events=SIMOUTDIR+"Low-Q2_Training_Events.hepmc3.tree.root", @@ -40,7 +40,7 @@ rule beamline_training_sim: """ # Run EICrecon and create input tensors for reconstruction. -rule beamline_reconstruction_tensors_eicrecon: +rule lowq2_reconstruction_tensors_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", output_collections="BackwardsBeamlineHits,TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", @@ -56,7 +56,7 @@ rule beamline_reconstruction_tensors_eicrecon: """ # Run EICrecon to create TaggerTrackerReconstructedParticles -rule beamline_reconstruction_particles_eicrecon: +rule lowq2_reconstruction_particles_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", @@ -72,7 +72,7 @@ rule beamline_reconstruction_particles_eicrecon: """ # Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles -rule beamline_reconstruction_particles_test: +rule lowq2_reconstruction_particles_test: input: script=workflow.source_path("reconstructionAnalysis.C"), data=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", @@ -91,12 +91,12 @@ rule beamline_reconstruction_particles_test: ########################################################################################## # Processes the simulation output data for training -rule beamline_steering_reconstruction_preparation: +rule lowq2_steering_reconstruction_preparation: input: script=workflow.source_path("cleanData.C"), data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", output: - rootfile=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + rootfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Data{CAMPAIGN}.root", shell: """ root -l -b -q '{input.script}("{input.data}", "{output.rootfile}",18.0)' @@ -104,28 +104,28 @@ rule beamline_steering_reconstruction_preparation: # Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. -rule beamline_steering_reconstruction_training: +rule lowq2_steering_reconstruction_training: input: script=workflow.source_path("SteeringRegression.py"), scriptdata=workflow.source_path("LoadData.py"), scriptmodel=workflow.source_path("RegressionModel.py"), - data=ANALYSISDIR+"BeamlineSteeringReconstructionData{CAMPAIGN}.root", + data=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Data{CAMPAIGN}.root", output: - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", + onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test{CAMPAIGN}_{N}.onnx", shell: """ python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 1000 """ # Run eicrecon with the newly trained neural network -rule beamline_steering_reconstruction_particles_trained_eicrecon: +rule lowq2_steering_reconstruction_particles_trained_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - onnxfile=ANALYSISDIR+"BeamlineSteeringReconstructionTest{CAMPAIGN}_{N}.onnx", + onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test{CAMPAIGN}_{N}.onnx", output: eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", shell: @@ -152,13 +152,27 @@ rule beamline_reconstruction_particles_trained_test: ########################################################################################## # Combine results ########################################################################################## + + + rule reconstruction: input: directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") output: - directory("results/beamline/reconstruction_{CAMPAIGN}/") + directory("results/lowq2_reconstruction/reconstruction_{CAMPAIGN}/") shell: """ mkdir {output} cp -r {input} {output} - """ \ No newline at end of file + """ + +########################################################################################## +# Defualt running +########################################################################################## +rule lowq2_reconstruction_local: + input: + "results/lowq2_reconstruction/test_local/" + +rule lowq2_reconstruction_retrain: + input: + "results/lowq2_reconstruction/retrain_local/" \ No newline at end of file From c0855b8b1efb9f33d66e7a79e69da31b83194183 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 18:50:53 +0100 Subject: [PATCH 52/65] Temporarily comment out other benchmarks --- .gitlab-ci.yml | 118 ++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5616529a..ed9e5a9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -121,72 +121,72 @@ common:setup: - runner_system_failure include: - - local: 'benchmarks/backgrounds/config.yml' - - local: 'benchmarks/backwards_ecal/config.yml' - - local: 'benchmarks/beamline/config.yml' - - local: 'benchmarks/calo_pid/config.yml' - - local: 'benchmarks/campaign/config.yml' - - local: 'benchmarks/ecal_gaps/config.yml' + # - local: 'benchmarks/backgrounds/config.yml' + # - local: 'benchmarks/backwards_ecal/config.yml' + # - local: 'benchmarks/beamline/config.yml' + # - local: 'benchmarks/calo_pid/config.yml' + # - local: 'benchmarks/campaign/config.yml' + # - local: 'benchmarks/ecal_gaps/config.yml' - local: 'benchmarks/lowq2_reconstruction/config.yml' - - local: 'benchmarks/tracking_detectors/config.yml' - - local: 'benchmarks/tracking_performances/config.yml' - - local: 'benchmarks/tracking_performances_dis/config.yml' - - local: 'benchmarks/barrel_ecal/config.yml' - - 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' - - local: 'benchmarks/pid/config.yml' - - local: 'benchmarks/rich/config.yml' - - local: 'benchmarks/b0_tracker/config.yml' - - local: 'benchmarks/insert_muon/config.yml' - - local: 'benchmarks/insert_tau/config.yml' - - local: 'benchmarks/zdc_sigma/config.yml' - - local: 'benchmarks/zdc_lambda/config.yml' - - local: 'benchmarks/insert_neutron/config.yml' - - local: 'benchmarks/femc_electron/config.yml' - - local: 'benchmarks/femc_photon/config.yml' - - local: 'benchmarks/femc_pi0/config.yml' - - local: 'benchmarks/nhcal_acceptance/config.yml' - - local: 'benchmarks/nhcal_basic_distribution/config.yml' + # - local: 'benchmarks/tracking_detectors/config.yml' + # - local: 'benchmarks/tracking_performances/config.yml' + # - local: 'benchmarks/tracking_performances_dis/config.yml' + # - local: 'benchmarks/barrel_ecal/config.yml' + # - 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' + # - local: 'benchmarks/pid/config.yml' + # - local: 'benchmarks/rich/config.yml' + # - local: 'benchmarks/b0_tracker/config.yml' + # - local: 'benchmarks/insert_muon/config.yml' + # - local: 'benchmarks/insert_tau/config.yml' + # - local: 'benchmarks/zdc_sigma/config.yml' + # - local: 'benchmarks/zdc_lambda/config.yml' + # - local: 'benchmarks/insert_neutron/config.yml' + # - local: 'benchmarks/femc_electron/config.yml' + # - local: 'benchmarks/femc_photon/config.yml' + # - local: 'benchmarks/femc_pi0/config.yml' + # - local: 'benchmarks/nhcal_acceptance/config.yml' + # - local: 'benchmarks/nhcal_basic_distribution/config.yml' deploy_results: allow_failure: true stage: deploy needs: - - "collect_results:backgrounds" - - "collect_results:backwards_ecal" - - "collect_results:barrel_ecal" - - "collect_results:beamline" - - "collect_results:calo_pid" - - "collect_results:campaign" - - "collect_results:ecal_gaps" - - "collect_results:lfhcal" + # - "collect_results:backgrounds" + # - "collect_results:backwards_ecal" + # - "collect_results:barrel_ecal" + # - "collect_results:beamline" + # - "collect_results:calo_pid" + # - "collect_results:campaign" + # - "collect_results:ecal_gaps" + # - "collect_results:lfhcal" - "collect_results:lowq2_reconstruction" - - "collect_results:material_scan" - - "collect_results:pid" - - "collect_results:rich" - - "collect_results:tracking_performance" - - "collect_results:tracking_performance_campaigns" - - "collect_results:zdc_sigma" - - "collect_results:zdc_lambda" - - "collect_results:insert_neutron" - - "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" - - "collect_results:zdc_pi0" - - "collect_results:femc_electron" - - "collect_results:femc_photon" - - "collect_results:femc_pi0" - - "collect_results:nhcal_acceptance" - - "collect_results:nhcal_basic_distribution" + # - "collect_results:material_scan" + # - "collect_results:pid" + # - "collect_results:rich" + # - "collect_results:tracking_performance" + # - "collect_results:tracking_performance_campaigns" + # - "collect_results:zdc_sigma" + # - "collect_results:zdc_lambda" + # - "collect_results:insert_neutron" + # - "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" + # - "collect_results:zdc_pi0" + # - "collect_results:femc_electron" + # - "collect_results:femc_photon" + # - "collect_results:femc_pi0" + # - "collect_results:nhcal_acceptance" + # - "collect_results:nhcal_basic_distribution" script: - snakemake $SNAKEMAKE_FLAGS --cores 1 results/metadata.json - find results -print | sort | tee summary.txt From 999fb63d860e8efbd3897974eec2c944e50c1a84 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 19:12:28 +0100 Subject: [PATCH 53/65] Fix namings maybe --- benchmarks/lowq2_reconstruction/Snakefile | 40 ++++++++--------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile index 85067fc6..538b31c6 100644 --- a/benchmarks/lowq2_reconstruction/Snakefile +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -64,7 +64,7 @@ rule lowq2_reconstruction_particles_eicrecon: input: data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", output: - eicreconfile=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + eicreconfile=ANALYSISDIR+"Low-Q2_test_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ @@ -75,12 +75,12 @@ rule lowq2_reconstruction_particles_eicrecon: rule lowq2_reconstruction_particles_test: input: script=workflow.source_path("reconstructionAnalysis.C"), - data=ANALYSISDIR+"Low-Q2_Training_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + data=ANALYSISDIR+"Low-Q2_{STAGE}_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", output: - momentumCanvas=ANALYSISDIR+"momentum_resolution_{CAMPAIGN}.png", - energyThetaPhiCanvas=ANALYSISDIR+"energy_theta_phi_resolution_{CAMPAIGN}.png", - relationCanvas=ANALYSISDIR+"relation_resolution_{CAMPAIGN}.png", - resolutionGraphsCanvas=ANALYSISDIR+"resolution_graphs_{CAMPAIGN}.png", + momentumCanvas=ANALYSISDIR+"{STAGE}_momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"{STAGE}_energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", shell: """ root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' @@ -127,39 +127,25 @@ rule lowq2_steering_reconstruction_particles_trained_eicrecon: data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test{CAMPAIGN}_{N}.onnx", output: - eicreconfile=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", + eicreconfile=ANALYSISDIR+"Low-Q2_retrain_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", shell: """ eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=1000000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ -# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles -rule beamline_reconstruction_particles_trained_test: - input: - script=workflow.source_path("reconstructionAnalysis.C"), - data=ANALYSISDIR+"Low-Q2_Trained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", - output: - momentumCanvas=ANALYSISDIR+"trained_momentum_resolution_{CAMPAIGN}.png", - energyThetaPhiCanvas=ANALYSISDIR+"trained_energy_theta_phi_resolution_{CAMPAIGN}.png", - relationCanvas=ANALYSISDIR+"trained_relation_resolution_{CAMPAIGN}.png", - resolutionGraphsCanvas=ANALYSISDIR+"trained_resolution_graphs_{CAMPAIGN}.png", - shell: - """ - root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' - """ ########################################################################################## # Combine results ########################################################################################## - - - -rule reconstruction: +rule lowq2_reconstruction: input: - directory(ANALYSISDIR+"NN_Test_{CAMPAIGN}/") + momentumCanvas=ANALYSISDIR+"{STAGE}_momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"{STAGE}_energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", output: - directory("results/lowq2_reconstruction/reconstruction_{CAMPAIGN}/") + directory("results/lowq2_reconstruction/{STAGE}_{CAMPAIGN}/") shell: """ mkdir {output} From 2e50f839871ea54c059a19f07a08edc6e934e008 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 19:31:33 +0100 Subject: [PATCH 54/65] Add failure conditions on resolutions --- benchmarks/lowq2_reconstruction/config.yml | 36 +++++++++++++ .../reconstructionAnalysis.C | 54 ++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 benchmarks/lowq2_reconstruction/config.yml diff --git a/benchmarks/lowq2_reconstruction/config.yml b/benchmarks/lowq2_reconstruction/config.yml new file mode 100644 index 00000000..dfb44eb5 --- /dev/null +++ b/benchmarks/lowq2_reconstruction/config.yml @@ -0,0 +1,36 @@ +sim:lowq2_reconstruction: + extends: .det_benchmark + stage: simulate + script: + - | + snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB \ + sim_output/lowq2_reconstruction/Low-Q2_Training_SimEvents_local.edm4hep.root \ + +bench:lowq2_reconstruction: + extends: .det_benchmark + stage: benchmarks + needs: + - "sim:lowq2_reconstruction" + script: + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB lowq2_reconstruction_local + +retrain:lowq2_reconstruction: + extends: .det_benchmark + stage: retrain + needs: + - "bench:lowq2_reconstruction" + script: + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB lowq2_reconstruction_retrain + when: on_failure + +collect_results:lowq2_reconstruction: + extends: .det_benchmark + stage: collect + needs: + - "bench:lowq2_reconstruction" + - "retrain:lowq2_reconstruction" + script: + - ls -lrht + - mv results{,_save}/ # move results directory out of the way to preserve it + - snakemake $SNAKEMAKE_FLAGS --cores 1 --delete-all-output lowq2_reconstruction_local + - mv results{_save,}/ diff --git a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C index 5d3702e5..3c81beb6 100644 --- a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C +++ b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C @@ -250,10 +250,60 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip cResolutionGraphs->Update(); // Save the canvas as a PNG file cResolutionGraphs->SaveAs(resolutionGraphsCanvasName); - - // Check to see if resolutions pass tests. + // Get mean and error on the mean of E, theta and phi resolutions + double mean_E_res = E_res_Hist->GetMean(); + double mean_theta_res = theta_diff_Hist->GetMean(); + double mean_phi_res = phi_diff_Hist->GetMean(); + double mean_E_res_error = E_res_Hist->GetMeanError(); + double mean_theta_res_error = theta_diff_Hist->GetMeanError(); + double mean_phi_res_error = phi_diff_Hist->GetMeanError(); + + // Get standard deviation of E, theta and phi resolutions + double stddev_E_res = E_res_Hist->GetStdDev(); + double stddev_theta_res = theta_diff_Hist->GetStdDev(); + double stddev_phi_res = phi_diff_Hist->GetStdDev(); + + // Print the resolutions + std::cout << "Mean E resolution: " << mean_E_res << " +/- " << stddev_E_res << std::endl; + std::cout << "Mean theta resolution: " << mean_theta_res << " +/- " << stddev_theta_res << std::endl; + std::cout << "Mean phi resolution: " << mean_phi_res << " +/- " << stddev_phi_res << std::endl; + + int pass = 0; + + // Fail if mean is more than two error on the mean away from zero + if(std::abs(mean_E_res) > 2 * mean_E_res_error) { + std::cout << "Mean E resolution is more than two errors on the mean away from zero!" << std::endl; + pass = 1; + } + if(std::abs(mean_theta_res) > 2 * mean_theta_res_error) { + std::cout << "Mean theta resolution is more than two errors on the mean away from zero!" << std::endl; + pass = 1; + } + if(std::abs(mean_phi_res) > 2 * mean_phi_res_error) { + std::cout << "Mean phi resolution is more than two errors on the mean away from zero!" << std::endl; + pass = 1; + } + + // Resolution limits + double E_res_limit = 0.05; // 5% resolution + double theta_res_limit = 0.01; // 1% resolution + double phi_res_limit = 0.3; // 30% resolution + // Fail if standard deviation is more than the limit + if(std::abs(stddev_E_res) > E_res_limit) { + std::cout << "Standard deviation of E resolution is more than the limit of " << E_res_limit << "!" << std::endl; + pass = 1; + } + if(std::abs(stddev_theta_res) > theta_res_limit) { + std::cout << "Standard deviation of theta resolution is more than the limit of " << theta_res_limit << "!" << std::endl; + pass = 1; + } + if(std::abs(stddev_phi_res) > phi_res_limit) { + std::cout << "Standard deviation of phi resolution is more than the limit of " << phi_res_limit << "!" << std::endl; + pass = 1; + } + return pass; // Return 0 if all tests passed, 1 if any test failed } From e44eac722e15ebf71a2ffcd43be1dea011a5b379 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 19:59:24 +0100 Subject: [PATCH 55/65] Fix benchmark stage --- benchmarks/lowq2_reconstruction/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/lowq2_reconstruction/config.yml b/benchmarks/lowq2_reconstruction/config.yml index dfb44eb5..d89289b4 100644 --- a/benchmarks/lowq2_reconstruction/config.yml +++ b/benchmarks/lowq2_reconstruction/config.yml @@ -16,7 +16,7 @@ bench:lowq2_reconstruction: retrain:lowq2_reconstruction: extends: .det_benchmark - stage: retrain + stage: benchmarks needs: - "bench:lowq2_reconstruction" script: From 1fddc4840515dd5336295f9573a767cb050508f1 Mon Sep 17 00:00:00 2001 From: simonge Date: Mon, 4 Aug 2025 22:24:30 +0100 Subject: [PATCH 56/65] Fix naming --- benchmarks/lowq2_reconstruction/Snakefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile index 538b31c6..dfadf88e 100644 --- a/benchmarks/lowq2_reconstruction/Snakefile +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -35,6 +35,7 @@ rule lowq2_training_sim: --inputFiles {input.events} \ --numberOfEvents 1000000 \ --compactFile $DETECTOR_PATH/epic_ip6_extended.xml \ + --printLevel WARNING \ --outputFile {output} \ --physics.rangecut 100*m """ @@ -111,7 +112,7 @@ rule lowq2_steering_reconstruction_training: scriptmodel=workflow.source_path("RegressionModel.py"), data=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Data{CAMPAIGN}.root", output: - onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test{CAMPAIGN}_{N}.onnx", + onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test_{CAMPAIGN}.onnx", shell: """ python {input.script} --dataFiles {input.data} --outModelFile {output.onnxfile} --epochs 1000 @@ -125,9 +126,9 @@ rule lowq2_steering_reconstruction_particles_trained_eicrecon: ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", - onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test{CAMPAIGN}_{N}.onnx", + onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test_{CAMPAIGN}.onnx", output: - eicreconfile=ANALYSISDIR+"Low-Q2_retrain_Particles_{CAMPAIGN}_{N}.eicrecon.edm4hep.root", + eicreconfile=ANALYSISDIR+"Low-Q2_retrained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=1000000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ @@ -159,6 +160,6 @@ rule lowq2_reconstruction_local: input: "results/lowq2_reconstruction/test_local/" -rule lowq2_reconstruction_retrain: +rule lowq2_reconstruction_retrained: input: - "results/lowq2_reconstruction/retrain_local/" \ No newline at end of file + "results/lowq2_reconstruction/retrained_local/" \ No newline at end of file From 7e38cdc2c0c698467d3549b23ba1a1a5b362e2da Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 5 Aug 2025 08:08:54 +0100 Subject: [PATCH 57/65] Allow int return to flag fail --- benchmarks/lowq2_reconstruction/reconstructionAnalysis.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C index 3c81beb6..e3938f74 100644 --- a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C +++ b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C @@ -9,7 +9,7 @@ #include "TCanvas.h" #include "TStyle.h" -void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", +int reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", float beamEnergy = 10.0, TString momentumCanvasName = "momentum_resolution.png", TString energyThetaPhiCanvasName = "energy_theta_phi_resolution.png", From 673af74711e1d1d6c3701d84f0116e7b2d59ad20 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 5 Aug 2025 12:35:10 +0100 Subject: [PATCH 58/65] Separate resolution check from plot creation --- benchmarks/lowq2_reconstruction/Snakefile | 52 ++++-- .../lowq2_reconstruction/checkResolutions.C | 103 ++++++++++++ .../reconstructionAnalysis.C | 154 +++++++++++------- 3 files changed, 238 insertions(+), 71 deletions(-) create mode 100644 benchmarks/lowq2_reconstruction/checkResolutions.C diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile index dfadf88e..533c50a8 100644 --- a/benchmarks/lowq2_reconstruction/Snakefile +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -6,7 +6,7 @@ ANALYSISDIR=SIMOUTDIR+"analysis/" ########################################################################################## # Filter LowQ2 events from xrootd server - Using the acceptance events didn't work -# ToDo: Do a proper investigation into why the feature to target mapping isn't unique +# ToDo: Do a proper investigation into how the feature to target mapping isn't unique rule filter_hepmc_for_training: input: warmup="warmup/epic_ip6_extended.edm4hep.root", @@ -72,20 +72,6 @@ rule lowq2_reconstruction_particles_eicrecon: -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ -# Test the resolution of TaggerTrackerReconstructedParticles compared to their MCParticles -rule lowq2_reconstruction_particles_test: - input: - script=workflow.source_path("reconstructionAnalysis.C"), - data=ANALYSISDIR+"Low-Q2_{STAGE}_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", - output: - momentumCanvas=ANALYSISDIR+"{STAGE}_momentum_resolution_{CAMPAIGN}.png", - energyThetaPhiCanvas=ANALYSISDIR+"{STAGE}_energy_theta_phi_resolution_{CAMPAIGN}.png", - relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", - resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", - shell: - """ - root -l -b -q '{input.script}("{input.data}", 18, "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' - """ ########################################################################################## ### Rules for training new onnx reconstruction neural network @@ -104,7 +90,7 @@ rule lowq2_steering_reconstruction_preparation: """ -# Trains a regression model to predict the MCParticle momentum from the B2eR beamline virtual tracker hits. +# Trains a regression model to predict the MCParticle momentum from the lowq2 tagger tracker hits. rule lowq2_steering_reconstruction_training: input: script=workflow.source_path("SteeringRegression.py"), @@ -135,16 +121,50 @@ rule lowq2_steering_reconstruction_particles_trained_eicrecon: -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ +########################################################################################## +# Test and check resolutions +########################################################################################## + +# Produce hitsograms of th resolution of TaggerTrackerReconstructedParticles compared to their MCParticles +rule lowq2_reconstruction_particles_test: + input: + script=workflow.source_path("reconstructionAnalysis.C"), + data=ANALYSISDIR+"Low-Q2_{STAGE}_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + output: + rootfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.root", + momentumCanvas=ANALYSISDIR+"{STAGE}_momentum_resolution_{CAMPAIGN}.png", + energyThetaPhiCanvas=ANALYSISDIR+"{STAGE}_energy_theta_phi_resolution_{CAMPAIGN}.png", + relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", + resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", + shell: + """ + root -l -b -q '{input.script}("{input.data}", 18, "{output.rootfile}", "{output.momentumCanvas}", "{output.energyThetaPhiCanvas}", "{output.relationCanvas}", "{output.resolutionGraphsCanvas}")' + """ + +# Check the resloutions and offsets are within tollerance +rule lowq2_reconstruction_particles_check: + input: + script=workflow.source_path("checkResolutions.C"), + rootfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.root" + output: + jsonfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.json" + shell: + """ + root -l -b -q '{input.script}("{input.rootfile}", "{output.jsonfile}")' + """ + ########################################################################################## # Combine results ########################################################################################## rule lowq2_reconstruction: input: + rootfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.root", momentumCanvas=ANALYSISDIR+"{STAGE}_momentum_resolution_{CAMPAIGN}.png", energyThetaPhiCanvas=ANALYSISDIR+"{STAGE}_energy_theta_phi_resolution_{CAMPAIGN}.png", relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", + jsonfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.json" output: directory("results/lowq2_reconstruction/{STAGE}_{CAMPAIGN}/") shell: diff --git a/benchmarks/lowq2_reconstruction/checkResolutions.C b/benchmarks/lowq2_reconstruction/checkResolutions.C new file mode 100644 index 00000000..2f6e4fca --- /dev/null +++ b/benchmarks/lowq2_reconstruction/checkResolutions.C @@ -0,0 +1,103 @@ +#include "TFile.h" +#include "TH1F.h" +#include "TMath.h" +#include +#include + +int checkResolutions(const TString inputFile="/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestcurrent.edm4hep.root", const TString outputFile="test.json") { + + int fail = 0; + + TFile *file = TFile::Open(inputFile); + if (!file || file->IsZombie()) { + std::cerr << "Error opening file: " << inputFile << std::endl; + return 1; // Return error code + } + + TH1F* E_res_Hist = (TH1F*)file->Get("E_res"); + TH1F* theta_diff_Hist = (TH1F*)file->Get("theta_diff"); + TH1F* phi_diff_Hist = (TH1F*)file->Get("phi_diff"); + + + double mean_E_res = E_res_Hist->GetMean(); + double mean_theta_res = theta_diff_Hist->GetMean(); + double mean_phi_res = phi_diff_Hist->GetMean(); + double mean_E_res_error = E_res_Hist->GetMeanError(); + double mean_theta_res_error = theta_diff_Hist->GetMeanError(); + double mean_phi_res_error = phi_diff_Hist->GetMeanError(); + + // Get standard deviation of E, theta and phi resolutions + double stddev_E_res = E_res_Hist->GetStdDev(); + double stddev_theta_res = theta_diff_Hist->GetStdDev(); + double stddev_phi_res = phi_diff_Hist->GetStdDev(); + double stddev_E_res_error = E_res_Hist->GetStdDevError(); + double stddev_theta_res_error = theta_diff_Hist->GetStdDevError(); + double stddev_phi_res_error = phi_diff_Hist->GetStdDevError(); + + // Print the resolutions + std::cout << "Mean E offset: " << mean_E_res << " +/- " << mean_E_res_error << std::endl; + std::cout << "Mean theta offset: " << mean_theta_res << " +/- " << mean_theta_res_error << std::endl; + std::cout << "Mean phi offset: " << mean_phi_res << " +/- " << mean_phi_res_error << std::endl; + std::cout << "Standard deviation of E resolution: " << stddev_E_res << " +/- " << stddev_E_res_error << std::endl; + std::cout << "Standard deviation of theta resolution: " << stddev_theta_res << " +/- " << stddev_theta_res_error << std::endl; + std::cout << "Standard deviation of phi resolution: " << stddev_phi_res << " +/- " << stddev_phi_res_error << std::endl; + + // Fail if mean is more than 20% of the standard deviation away from zero + if(std::abs(mean_E_res) > 0.2 * stddev_E_res) { + std::cout << "Mean E offset is more than 20\% (" << 0.2 * stddev_E_res << ") of the standard deviation away from zero!" << std::endl; + fail = 1; + } + if(std::abs(mean_theta_res) > 0.2 * stddev_theta_res) { + std::cout << "Mean theta offset is more than 20\% (" << 0.2 * stddev_theta_res << ") of the standard deviation away from zero!" << std::endl; + fail = 1; + } + if(std::abs(mean_phi_res) > 0.2 * stddev_phi_res) { + std::cout << "Mean phi offset is more than 20\% (" << 0.2 * stddev_phi_res << ") of the standard deviation away from zero!" << std::endl; + fail = 1; + } + + // Resolution limits + double E_res_limit = 0.05; // 5% resolution + double theta_res_limit = 0.001; // 1 mrad resolution + double phi_res_limit = 30; // 30 degrees resolution + + // Fail if standard deviation is more than the limit + if(std::abs(stddev_E_res) > E_res_limit) { + std::cout << "E resolution is more than the limit of " << E_res_limit << "!" << std::endl; + fail = 1; + } + if(std::abs(stddev_theta_res) > theta_res_limit) { + std::cout << "Theta resolution is more than the limit of " << theta_res_limit << " radians!" << std::endl; + fail = 1; + } + if(std::abs(stddev_phi_res) > phi_res_limit) { + std::cout << "Phi resolution is more than the limit of " << phi_res_limit << " degrees!" << std::endl; + fail = 1; + } + + // Create json output file containing the resolutions, errors and offsets + std::ofstream jsonFile(outputFile); + if (!jsonFile.is_open()) { + std::cerr << "Error opening output file: " << outputFile << std::endl; + return 1; // Return error code + } + jsonFile << "{\n"; + jsonFile << " \"mean_E_res\": " << mean_E_res << ",\n"; + jsonFile << " \"mean_theta_res\": " << mean_theta_res << ",\n"; + jsonFile << " \"mean_phi_res\": " << mean_phi_res << ",\n"; + jsonFile << " \"mean_E_res_error\": " << mean_E_res_error << ",\n"; + jsonFile << " \"mean_theta_res_error\": " << mean_theta_res_error << ",\n"; + jsonFile << " \"mean_phi_res_error\": " << mean_phi_res_error << ",\n"; + jsonFile << " \"stddev_E_res\": " << stddev_E_res << ",\n"; + jsonFile << " \"stddev_theta_res\": " << stddev_theta_res << ",\n"; + jsonFile << " \"stddev_phi_res\": " << stddev_phi_res << ",\n"; + jsonFile << " \"stddev_E_res_error\": " << stddev_E_res_error << ",\n"; + jsonFile << " \"stddev_theta_res_error\": " << stddev_theta_res_error << ",\n"; + jsonFile << " \"stddev_phi_res_error\": " << stddev_phi_res_error << ",\n"; + jsonFile << " \"fail\": " << fail << "\n"; + jsonFile << "}\n"; + jsonFile.close(); + + return fail; + +} \ No newline at end of file diff --git a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C index e3938f74..2caaf993 100644 --- a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C +++ b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C @@ -9,8 +9,9 @@ #include "TCanvas.h" #include "TStyle.h" -int reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", +void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", float beamEnergy = 10.0, + TString outFile = "reconstruction_results.root", TString momentumCanvasName = "momentum_resolution.png", TString energyThetaPhiCanvasName = "energy_theta_phi_resolution.png", TString relationCanvasName = "relation_resolution.png", @@ -250,60 +251,103 @@ int reconstructionAnalysis(TString inFile = "/home/simong/EIC/script cResolutionGraphs->Update(); // Save the canvas as a PNG file cResolutionGraphs->SaveAs(resolutionGraphsCanvasName); + + TFile *f = new TFile(outFile,"RECREATE"); + cMomentum->Write(); + cEnergyThetaPhi->Write(); + cResolutionVsMC->Write(); + cResolutionGraphs->Write(); + px_Hist->Write(); + py_Hist->Write(); + pz_Hist->Write(); + px_diff_Hist->Write(); + py_diff_Hist->Write(); + pz_res_Hist->Write(); + E_Hist->Write(); + theta_Hist->Write(); + phi_Hist->Write(); + E_res_Hist->Write(); + theta_diff_Hist->Write(); + phi_diff_Hist->Write(); + E_res_vs_E_Hist->Write(); + E_res_vs_theta_Hist->Write(); + E_res_vs_phi_Hist->Write(); + theta_diff_vs_E_Hist->Write(); + theta_diff_vs_theta_Hist->Write(); + theta_diff_vs_phi_Hist->Write(); + phi_diff_vs_E_Hist->Write(); + phi_diff_vs_theta_Hist->Write(); + phi_diff_vs_phi_Hist->Write(); + + hE_vs_E_mean->Write(); + hE_vs_E_stddev->Write(); + hTheta_vs_E_mean->Write(); + hTheta_vs_E_stddev->Write(); + hPhi_vs_theta_mean->Write(); + hPhi_vs_theta_stddev->Write(); + + f->Close(); + + - // Get mean and error on the mean of E, theta and phi resolutions - double mean_E_res = E_res_Hist->GetMean(); - double mean_theta_res = theta_diff_Hist->GetMean(); - double mean_phi_res = phi_diff_Hist->GetMean(); - double mean_E_res_error = E_res_Hist->GetMeanError(); - double mean_theta_res_error = theta_diff_Hist->GetMeanError(); - double mean_phi_res_error = phi_diff_Hist->GetMeanError(); - - // Get standard deviation of E, theta and phi resolutions - double stddev_E_res = E_res_Hist->GetStdDev(); - double stddev_theta_res = theta_diff_Hist->GetStdDev(); - double stddev_phi_res = phi_diff_Hist->GetStdDev(); - - // Print the resolutions - std::cout << "Mean E resolution: " << mean_E_res << " +/- " << stddev_E_res << std::endl; - std::cout << "Mean theta resolution: " << mean_theta_res << " +/- " << stddev_theta_res << std::endl; - std::cout << "Mean phi resolution: " << mean_phi_res << " +/- " << stddev_phi_res << std::endl; - - int pass = 0; - - // Fail if mean is more than two error on the mean away from zero - if(std::abs(mean_E_res) > 2 * mean_E_res_error) { - std::cout << "Mean E resolution is more than two errors on the mean away from zero!" << std::endl; - pass = 1; - } - if(std::abs(mean_theta_res) > 2 * mean_theta_res_error) { - std::cout << "Mean theta resolution is more than two errors on the mean away from zero!" << std::endl; - pass = 1; - } - if(std::abs(mean_phi_res) > 2 * mean_phi_res_error) { - std::cout << "Mean phi resolution is more than two errors on the mean away from zero!" << std::endl; - pass = 1; - } - - // Resolution limits - double E_res_limit = 0.05; // 5% resolution - double theta_res_limit = 0.01; // 1% resolution - double phi_res_limit = 0.3; // 30% resolution - - // Fail if standard deviation is more than the limit - if(std::abs(stddev_E_res) > E_res_limit) { - std::cout << "Standard deviation of E resolution is more than the limit of " << E_res_limit << "!" << std::endl; - pass = 1; - } - if(std::abs(stddev_theta_res) > theta_res_limit) { - std::cout << "Standard deviation of theta resolution is more than the limit of " << theta_res_limit << "!" << std::endl; - pass = 1; - } - if(std::abs(stddev_phi_res) > phi_res_limit) { - std::cout << "Standard deviation of phi resolution is more than the limit of " << phi_res_limit << "!" << std::endl; - pass = 1; - } - - return pass; // Return 0 if all tests passed, 1 if any test failed + // // Get mean and error on the mean of E, theta and phi resolutions + // double mean_E_res = E_res_Hist->GetMean(); + // double mean_theta_res = theta_diff_Hist->GetMean(); + // double mean_phi_res = phi_diff_Hist->GetMean(); + // double mean_E_res_error = E_res_Hist->GetMeanError(); + // double mean_theta_res_error = theta_diff_Hist->GetMeanError(); + // double mean_phi_res_error = phi_diff_Hist->GetMeanError(); + + // // Get standard deviation of E, theta and phi resolutions + // double stddev_E_res = E_res_Hist->GetStdDev(); + // double stddev_theta_res = theta_diff_Hist->GetStdDev(); + // double stddev_phi_res = phi_diff_Hist->GetStdDev(); + // double stddev_E_res_error = E_res_Hist->GetStdDevError(); + // double stddev_theta_res_error = theta_diff_Hist->GetStdDevError(); + // double stddev_phi_res_error = phi_diff_Hist->GetStdDevError(); + + // // Print the resolutions + // std::cout << "Mean E offset: " << mean_E_res << " +/- " << mean_E_res_error << std::endl; + // std::cout << "Mean theta offset: " << mean_theta_res << " +/- " << mean_theta_res_error << std::endl; + // std::cout << "Mean phi offset: " << mean_phi_res << " +/- " << mean_phi_res_error << std::endl; + // std::cout << "Standard deviation of E resolution: " << stddev_E_res << " +/- " << stddev_E_res_error << std::endl; + // std::cout << "Standard deviation of theta resolution: " << stddev_theta_res << " +/- " << stddev_theta_res_error << std::endl; + // std::cout << "Standard deviation of phi resolution: " << stddev_phi_res << " +/- " << stddev_phi_res_error << std::endl; + + // int pass = 0; + + // // Fail if mean is more than 20% of the standard deviation away from zero + // if(std::abs(mean_E_res) > 0.2 * stddev_E_res) { + // std::cout << "Mean E offset is more than 20\% (" << 0.2 * stddev_E_res << ") of the standard deviation away from zero!" << std::endl; + // pass = 1; + // } + // if(std::abs(mean_theta_res) > 0.2 * stddev_theta_res) { + // std::cout << "Mean theta offset is more than 20\% (" << 0.2 * stddev_theta_res << ") of the standard deviation away from zero!" << std::endl; + // pass = 1; + // } + // if(std::abs(mean_phi_res) > 0.2 * stddev_phi_res) { + // std::cout << "Mean phi offset is more than 20\% (" << 0.2 * stddev_phi_res << ") of the standard deviation away from zero!" << std::endl; + // pass = 1; + // } + + // // Resolution limits + // double E_res_limit = 0.05; // 5% resolution + // double theta_res_limit = 0.0001; // 1 mrad resolution + // double phi_res_limit = 30; // 30 degrees resolution + + // // Fail if standard deviation is more than the limit + // if(std::abs(stddev_E_res) > E_res_limit) { + // std::cout << "Standard deviation of E resolution is more than the limit of " << E_res_limit << "!" << std::endl; + // pass = 1; + // } + // if(std::abs(stddev_theta_res) > theta_res_limit) { + // std::cout << "Standard deviation of theta resolution is more than the limit of " << theta_res_limit << " radians!" << std::endl; + // pass = 1; + // } + // if(std::abs(stddev_phi_res) > phi_res_limit) { + // std::cout << "Standard deviation of phi resolution is more than the limit of " << phi_res_limit << " degrees!" << std::endl; + // pass = 1; + // } + } From 9dbd3aa82ecbc31919b348d5238e3172887f6245 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 12 Aug 2025 14:02:00 +0100 Subject: [PATCH 59/65] Fix naming and add onnx --- benchmarks/lowq2_reconstruction/Snakefile | 7 +++++++ benchmarks/lowq2_reconstruction/config.yml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile index 533c50a8..1c11c3f3 100644 --- a/benchmarks/lowq2_reconstruction/Snakefile +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -157,6 +157,12 @@ rule lowq2_reconstruction_particles_check: ########################################################################################## # Combine results ########################################################################################## +def get_onnx_input(wildcards): + if wildcards.STAGE == "retrained": + return ANALYSISDIR + f"Low-Q2_Steering_Reconstruction_Test_{wildcards.CAMPAIGN}.onnx" + else: + return [] + rule lowq2_reconstruction: input: rootfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.root", @@ -165,6 +171,7 @@ rule lowq2_reconstruction: relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", jsonfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.json" + onnxfile=get_onnx_input output: directory("results/lowq2_reconstruction/{STAGE}_{CAMPAIGN}/") shell: diff --git a/benchmarks/lowq2_reconstruction/config.yml b/benchmarks/lowq2_reconstruction/config.yml index d89289b4..7ceb44f8 100644 --- a/benchmarks/lowq2_reconstruction/config.yml +++ b/benchmarks/lowq2_reconstruction/config.yml @@ -20,7 +20,7 @@ retrain:lowq2_reconstruction: needs: - "bench:lowq2_reconstruction" script: - - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB lowq2_reconstruction_retrain + - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB lowq2_reconstruction_retrained when: on_failure collect_results:lowq2_reconstruction: From 7606250c4ae8350cf21a888a6ce21c10352ca7e6 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 19 Aug 2025 21:34:02 +0100 Subject: [PATCH 60/65] Merge updates from training-CI branch --- benchmarks/beamline/TestModel.py | 302 ------------------ benchmarks/lowq2_reconstruction/Snakefile | 43 +-- .../reconstructionAnalysis.C | 50 ++- 3 files changed, 55 insertions(+), 340 deletions(-) delete mode 100644 benchmarks/beamline/TestModel.py diff --git a/benchmarks/beamline/TestModel.py b/benchmarks/beamline/TestModel.py deleted file mode 100644 index ed9ae87a..00000000 --- a/benchmarks/beamline/TestModel.py +++ /dev/null @@ -1,302 +0,0 @@ -import onnxruntime as ort -import argparse -import numpy as np -from ProcessData import create_arrays -from RegressionModel import ProjectToX0Plane -import matplotlib.pyplot as plt -from matplotlib.colors import LogNorm -from scipy.stats import norm -from scipy.optimize import curve_fit - -# Parse arguments -parser = argparse.ArgumentParser(description='Train a regression model for the Tagger.') -parser.add_argument('--modelFile', type=str, default="regression_model.onnx", help='Path to the ONNX model file') -parser.add_argument('--dataFiles', type=str, nargs='+', help='Path to the data files') -parser.add_argument('--outDir', type=str, default=".", help='Output directory') -args = parser.parse_args() -modelFile = args.modelFile -dataFiles = args.dataFiles -outDir = args.outDir -outGraphFile = outDir + "/output_vs_target.png" -outGraphFile2 = outDir + "/transformed_output_vs_target.png" -outGraphFile3 = outDir + "/transformed_cut_output_vs_target.png" -outGraphFile4 = outDir + "/projected_output_vs_target.png" -correlationFile = outDir + "/correlations.png" -projectedCorrelationFile = outDir + "/projected_correlations.png" -differenceCorrelationFile = outDir + "/difference_correlations.png" - -input_data, target_data = create_arrays(dataFiles) - -target_data = np.array(target_data) - -# Load the ONNX model -session = ort.InferenceSession(modelFile) - -# Run the model on the input data -input_name = session.get_inputs()[0].name -output_name = session.get_outputs()[0].name -input_data = np.array(input_data,dtype=np.float32) -output = session.run([output_name], {input_name: input_data}) -output = np.array(output[0]) - -out_theta = np.arctan2(np.sqrt(output[:,0]**2 + output[:,1]**2),output[:,2]) -out_phi = np.arctan2(output[:,1],output[:,0]) -out_mag = np.sqrt(output[:,0]**2 + output[:,1]**2 + output[:,2]**2) -in_theta = np.arctan2(np.sqrt(target_data[:,0]**2 + target_data[:,1]**2),target_data[:,2]) -in_phi = np.arctan2(target_data[:,1],target_data[:,0]) -in_mag = np.sqrt(target_data[:,0]**2 + target_data[:,1]**2 + target_data[:,2]**2) - - -thetadiff = out_theta - in_theta -phidiff = out_phi - in_phi -# Move phidiff to within -pi/2 and pi/2 -phidiff = (phidiff + np.pi) % (2 * np.pi) - np.pi -magdiff = out_mag - in_mag - -diff = (target_data - output)/target_data -diffrange = [[-5,5],[-5,5],[-0.5,0.5]] -datarange = [[-0.02,0.02],[-0.02,0.02],[-1,0]] - -# Use the 'seismic' colormap -cmap = plt.get_cmap('seismic') - -# Creates histograms to compare the target and output data -fig, axs = plt.subplots(3, 3, figsize=(12, 12)) -for i in range(3): - # 2D histograms showing trends in the data - axs[0,i].hist2d(target_data[:,i], output[:,i], bins=100, range=[datarange[i],datarange[i]], cmap="seismic", norm=LogNorm(), label="Output vs Target") - axs[0,i].set_xlabel(f"Variable {i} Target") - axs[0,i].set_ylabel(f"Variable {i} Output") - - axs[1,i].hist(diff[:,i], bins=100, alpha=0.5, range=diffrange[i], label="Difference") - axs[1,i].set_xlabel(f"Variable {i} Difference") - axs[1,i].set_ylabel("Counts") - - axs[2,i].hist2d(target_data[:,i], diff[:,i], bins=100, range=[datarange[i],diffrange[i]], cmap="seismic", norm=LogNorm(), label="Difference vs Target") - axs[2,i].set_xlabel(f"Variable {i} Target") - axs[2,i].set_ylabel(f"Variable {i} Difference") - -plt.show() -plt.savefig(outGraphFile) - -# Creates histograms to compare theta, phi and mag target and output data -fig2, axs2 = plt.subplots(3, 3, figsize=(12, 12)) - -thetarange = [np.pi-0.01,np.pi] -phirange = [-np.pi,np.pi] -magrange = [0,1] - -thetadiffrange = [-0.01,0.01] -phidiffrange = [-np.pi,np.pi] -magdiffrange = [-0.1,0.1] - -# 2D histograms showing trends in the data -axs2[0,0].hist2d(out_theta, in_theta, bins=100, range=[thetarange,thetarange], cmap="seismic", norm=LogNorm(), label="Output vs Target") -axs2[0,0].set_xlabel("Theta Target") -axs2[0,0].set_ylabel("Theta Output") - -axs2[0,1].hist2d(out_phi, in_phi, bins=100, range=[phirange,phirange], cmap="seismic", norm=LogNorm(), label="Output vs Target") -axs2[0,1].set_xlabel("Phi Target") -axs2[0,1].set_ylabel("Phi Output") - -axs2[0,2].hist2d(out_mag, in_mag, bins=100, range=[magrange,magrange], cmap="seismic", norm=LogNorm(), label="Output vs Target") -axs2[0,2].set_xlabel("Mag Target") -axs2[0,2].set_ylabel("Mag Output") - -axs2[1,0].hist(thetadiff, bins=100, alpha=0.5, range=thetadiffrange, label="Difference") -axs2[1,0].set_xlabel("Theta Difference") -axs2[1,0].set_ylabel("Counts") - -axs2[1,1].hist(phidiff, bins=100, alpha=0.5, range=phidiffrange, label="Difference") -axs2[1,1].set_xlabel("Phi Difference") -axs2[1,1].set_ylabel("Counts") - -axs2[1,2].hist(magdiff, bins=100, alpha=0.5, range=magdiffrange, label="Difference") -axs2[1,2].set_xlabel("Mag Difference") -axs2[1,2].set_ylabel("Counts") - -axs2[2,0].hist2d(in_theta, thetadiff, bins=100, range=[thetarange,thetadiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") -axs2[2,0].set_xlabel("Theta Target") -axs2[2,0].set_ylabel("Theta Difference") - -axs2[2,1].hist2d(in_phi, phidiff, bins=100, range=[phirange,phidiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") -axs2[2,1].set_xlabel("Phi Target") -axs2[2,1].set_ylabel("Phi Difference") - -axs2[2,2].hist2d(in_mag, magdiff, bins=100, range=[magrange,magdiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") -axs2[2,2].set_xlabel("Mag Target") -axs2[2,2].set_ylabel("Mag Difference") - -plt.show() -plt.savefig(outGraphFile2) - -# Create histograms where the theta value has been cut at less than 3.14 -fig3, axs3 = plt.subplots(3, 3, figsize=(12, 12)) - -out_theta_cut = out_theta[out_theta < 3.14] -in_theta_cut = in_theta[out_theta < 3.14] -thetadiff_cut = thetadiff[out_theta < 3.14] - -out_phi_cut = out_phi[out_theta < 3.14] -in_phi_cut = in_phi[out_theta < 3.14] -phidiff_cut = phidiff[out_theta < 3.14] - -out_mag_cut = out_mag[out_theta < 3.14] -in_mag_cut = in_mag[out_theta < 3.14] -magdiff_cut = magdiff[out_theta < 3.14] - -axs3[0,0].hist2d(out_theta_cut, in_theta_cut, bins=100, range=[thetarange,thetarange], cmap="seismic", norm=LogNorm(), label="Output vs Target") -axs3[0,0].set_xlabel("Theta Target") -axs3[0,0].set_ylabel("Theta Output") - -axs3[0,1].hist2d(out_phi_cut, in_phi_cut, bins=100, range=[phirange,phirange], cmap="seismic", norm=LogNorm(), label="Output vs Target") -axs3[0,1].set_xlabel("Phi Target") -axs3[0,1].set_ylabel("Phi Output") - -axs3[0,2].hist2d(out_mag_cut, in_mag_cut, bins=100, range=[magrange,magrange], cmap="seismic", norm=LogNorm(), label="Output vs Target") -axs3[0,2].set_xlabel("Mag Target") -axs3[0,2].set_ylabel("Mag Output") - -axs3[1,0].hist(thetadiff_cut, bins=100, alpha=0.5, range=thetadiffrange, label="Difference") -axs3[1,0].set_xlabel("Theta Difference") -axs3[1,0].set_ylabel("Counts") - -axs3[1,1].hist(phidiff_cut, bins=100, alpha=0.5, range=phidiffrange, label="Difference") -axs3[1,1].set_xlabel("Phi Difference") -axs3[1,1].set_ylabel("Counts") - -axs3[1,2].hist(magdiff_cut, bins=100, alpha=0.5, range=magdiffrange, label="Difference") -axs3[1,2].set_xlabel("Mag Difference") -axs3[1,2].set_ylabel("Counts") - -axs3[2,0].hist2d(in_theta_cut, thetadiff_cut, bins=100, range=[thetarange,thetadiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") -axs3[2,0].set_xlabel("Theta Target") -axs3[2,0].set_ylabel("Theta Difference") - -axs3[2,1].hist2d(in_phi_cut, phidiff_cut, bins=100, range=[phirange,phidiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") -axs3[2,1].set_xlabel("Phi Target") -axs3[2,1].set_ylabel("Phi Difference") - -axs3[2,2].hist2d(in_mag_cut, magdiff_cut, bins=100, range=[magrange,magdiffrange], cmap="seismic", norm=LogNorm(), label="Difference vs Target") -axs3[2,2].set_xlabel("Mag Target") -axs3[2,2].set_ylabel("Mag Difference") - -plt.show() -plt.savefig(outGraphFile3) - -# Create plots where a Gaussian has been fitted to the data -# Function to fit a Gaussian and plot the results -def plot_gaussian_fit(ax, data, range, xlabel, ylabel): - def gaussian(x, mu, sigma, amplitude): - return amplitude * np.exp(-0.5 * ((x - mu) / sigma) ** 2) - - hist, bin_edges = np.histogram(data, bins=100, range=range, density=True) - bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 - - popt, _ = curve_fit(gaussian, bin_centers, hist, p0=[0, 0.01, 1]) - mu, sigma, amplitude = popt - print(f"mu={mu}, sigma={sigma}, amplitude={amplitude}") - - x = np.linspace(range[0], range[1], 100) - ax.plot(x, gaussian(x, *popt), 'k', linewidth=2) - ax.set_xlabel(xlabel) - ax.set_ylabel(ylabel) - ax.hist(data, bins=100, alpha=0.5, range=range, edgecolor='black', density=True) - ax.legend([f'Fit: $\mu$={mu:.5f}, $\sigma$={sigma:.5f}']) - -# Create histograms with Gaussian fits -fig4, axs4 = plt.subplots(3, 1, figsize=(8, 12)) - -plot_gaussian_fit(axs4[0], thetadiff, thetadiffrange, "Theta Difference", "Density") -plot_gaussian_fit(axs4[1], phidiff_cut, phidiffrange, "Phi Difference", "Density") -plot_gaussian_fit(axs4[2], magdiff, magdiffrange, "Mag Difference", "Density") - -plt.show() -plt.savefig(outGraphFile4) - - -# Look at the correlations between all of the variables in target_data and projected_inputs -# Create a comparison variable array -comparisson_variables = np.concatenate((input_data, target_data), axis=1) - -# Project inputs onto the X0 plane -projected_inputs = ProjectToX0Plane().project_numpy(input_data) - -# Concatenate the projected inputs and target data -projected_comparisson_variables = np.concatenate((projected_inputs, target_data), axis=1) -diff_cut = diff[(abs(projected_comparisson_variables[:, 3]) < 0.02)] -projected_comparisson_variables_cut = projected_comparisson_variables[(abs(projected_comparisson_variables[:, 3]) < 0.02)] -diff_cut = diff_cut[(abs(projected_comparisson_variables_cut[:, 2]+0.025) < 0.028)] -projected_comparisson_variables_cut = projected_comparisson_variables_cut[(abs(projected_comparisson_variables_cut[:, 2]+0.025) < 0.028)] # Filter for px < 0.1 - -# Calculate limits for each variable based on the data -limits = { - "ox": [np.min(comparisson_variables[:, 0]), np.max(comparisson_variables[:, 0])], - "oy": [np.min(comparisson_variables[:, 1]), np.max(comparisson_variables[:, 1])], - "y": [-200, 200], - "z": [-17000,-9000], - "px": [np.min(projected_comparisson_variables_cut[:, 2]), np.max(projected_comparisson_variables_cut[:, 2])], - "py": [np.min(projected_comparisson_variables_cut[:, 3]), np.max(projected_comparisson_variables_cut[:, 3])], - "opx": [np.min(comparisson_variables[:, 3]), np.max(comparisson_variables[:, 3])], - "opy": [np.min(comparisson_variables[:, 4]), np.max(comparisson_variables[:, 4])], - "opz": [np.min(comparisson_variables[:, 5]), np.max(comparisson_variables[:, 5])], - "Px": [np.min(projected_comparisson_variables_cut[:, 4]), np.max(projected_comparisson_variables_cut[:, 4])], - "Py": [np.min(projected_comparisson_variables_cut[:, 5]), np.max(projected_comparisson_variables_cut[:, 5])], - "Pz": [np.min(projected_comparisson_variables_cut[:, 6]), np.max(projected_comparisson_variables_cut[:, 6])], -} - - -labels = ["ox","oy", "z", "opx", "opy", "opz", "Px", "Py", "Pz"] -fig5, axs5 = plt.subplots(9, 9, figsize=(30, 30)) -for i in range(9): - for j in range(9): - if i == j: - axs5[j, i].hist(comparisson_variables[:, i], range=limits[labels[i]], bins=100, alpha=0.5, label=labels[i]) - axs5[j, i].set_xlabel(labels[i]) - axs5[j, i].set_ylabel("Counts") - #set log scale for y-axis if the data is skewed - axs5[j, i].set_yscale('log') - else: - axs5[j, i].hist2d(comparisson_variables[:, i], comparisson_variables[:, j], range=[limits[labels[i]],limits[labels[j]]], bins=100, cmap="seismic", norm=LogNorm()) - axs5[j, i].set_xlabel(labels[i]) - axs5[j, i].set_ylabel(labels[j]) -plt.tight_layout() -plt.savefig(correlationFile) -plt.show() - - - -# Plot the correlations between all of the variables in target_data and projected_inputs -projected_labels = ["y", "z", "px", "py", "Px", "Py", "Pz"] -fig6, axs6 = plt.subplots(7, 7, figsize=(30, 30)) -for i in range(7): - for j in range(7): - if i == j: - axs6[j, i].hist(projected_comparisson_variables_cut[:, i], range=limits[projected_labels[i]], bins=100, alpha=0.5, label=projected_labels[i]) - axs6[j, i].set_xlabel(projected_labels[i]) - axs6[j, i].set_ylabel("Counts") - #set log scale for y-axis if the data is skewed - axs6[j, i].set_yscale('log') - else: - axs6[j, i].hist2d(projected_comparisson_variables_cut[:, i], projected_comparisson_variables_cut[:, j], range=[limits[projected_labels[i]],limits[projected_labels[j]]], bins=100, cmap="seismic", norm=LogNorm()) - axs6[j, i].set_xlabel(projected_labels[i]) - axs6[j, i].set_ylabel(projected_labels[j]) - - -plt.tight_layout() -plt.savefig(projectedCorrelationFile) -plt.show() - -# Plot the correlations between the output diferences and projected inputs -output_labels = ["pred_PX", "pred_PY", "pred_PZ"] -fig7, axs7 = plt.subplots(3, 7, figsize=(15, 8)) -for i in range(3): - for j in range(7): - axs7[i, j].hist2d(projected_comparisson_variables_cut[:, j], diff_cut[:, i], range=[limits[projected_labels[j]],diffrange[i]], bins=100, cmap="seismic", norm=LogNorm()) - axs7[i, j].set_xlabel(projected_labels[j]) - axs7[i, j].set_ylabel(output_labels[i]) - - -plt.tight_layout() -plt.savefig(differenceCorrelationFile) -plt.show() diff --git a/benchmarks/lowq2_reconstruction/Snakefile b/benchmarks/lowq2_reconstruction/Snakefile index 1c11c3f3..ea36b6cd 100644 --- a/benchmarks/lowq2_reconstruction/Snakefile +++ b/benchmarks/lowq2_reconstruction/Snakefile @@ -40,43 +40,45 @@ rule lowq2_training_sim: --physics.rangecut 100*m """ -# Run EICrecon and create input tensors for reconstruction. -rule lowq2_reconstruction_tensors_eicrecon: +# Run EICrecon to create TaggerTrackerReconstructedParticles +rule lowq2_reconstruction_particles_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="BackwardsBeamlineHits,TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", + output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", output: - eicreconfile=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + eicreconfile=ANALYSISDIR+"Low-Q2_test_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ - eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -jana:nevents=500000 \ -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ -# Run EICrecon to create TaggerTrackerReconstructedParticles -rule lowq2_reconstruction_particles_eicrecon: + +########################################################################################## +### Rules for training new onnx reconstruction neural network +########################################################################################## + +# Run EICrecon and create input tensors for reconstruction. +rule lowq2_reconstruction_tensors_eicrecon: params: xml=os.getenv("DETECTOR_PATH")+"/epic_ip6_extended.xml", - output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", + output_collections="BackwardsBeamlineHits,TaggerTrackerFeatureTensor,TaggerTrackerTargetTensor,MCParticles,EventHeader", ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: - data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", output: - eicreconfile=ANALYSISDIR+"Low-Q2_test_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", + eicreconfile=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ - eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + eicrecon -Ppodio:output_file={output.eicreconfile} -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + -jana:nevents=500000 -jana:nskip=500000 \ -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ - -########################################################################################## -### Rules for training new onnx reconstruction neural network -########################################################################################## - # Processes the simulation output data for training rule lowq2_steering_reconstruction_preparation: input: @@ -89,7 +91,6 @@ rule lowq2_steering_reconstruction_preparation: root -l -b -q '{input.script}("{input.data}", "{output.rootfile}",18.0)' """ - # Trains a regression model to predict the MCParticle momentum from the lowq2 tagger tracker hits. rule lowq2_steering_reconstruction_training: input: @@ -111,13 +112,13 @@ rule lowq2_steering_reconstruction_particles_trained_eicrecon: output_collections="TaggerTrackerReconstructedParticles,MCParticles,EventHeader", ignore_plugins="janatop,LUMISPECCAL,ECTOF,BTOF,FOFFMTRK,RPOTS,B0TRK,MPGD,ECTRK,DRICH,DIRC,pid,tracking,EEMC,BEMC,FEMC,EHCAL,BHCAL,FHCAL,B0ECAL,ZDC,BTRK,BVTX,PFRICH,richgeo,evaluator,pid_lut,reco,rootfile" input: - data=ANALYSISDIR+"Low-Q2_Training_Tensors_{CAMPAIGN}.eicrecon.edm4hep.root", + data=SIMOUTDIR+"Low-Q2_Training_SimEvents_{CAMPAIGN}.edm4hep.root", onnxfile=ANALYSISDIR+"Low-Q2_Steering_Reconstruction_Test_{CAMPAIGN}.onnx", output: eicreconfile=ANALYSISDIR+"Low-Q2_retrained_Particles_{CAMPAIGN}.eicrecon.edm4hep.root", shell: """ - eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=1000000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ + eicrecon -Ppodio:output_file={output.eicreconfile} -Pjana:nevents=500000 -PLOWQ2:TaggerTrackerTransportationPreML:beamE=18.0 -PLOWQ2:TaggerTrackerTransportationPostML:beamE=18.0 -Pdd4hep:xml_files={params.xml} \ -PLOWQ2:TaggerTrackerTransportationInference:modelPath={input.onnxfile} -Ppodio:output_collections={params.output_collections} -Pplugins_to_ignore={params.ignore_plugins} {input.data} """ @@ -170,8 +171,8 @@ rule lowq2_reconstruction: energyThetaPhiCanvas=ANALYSISDIR+"{STAGE}_energy_theta_phi_resolution_{CAMPAIGN}.png", relationCanvas=ANALYSISDIR+"{STAGE}_relation_resolution_{CAMPAIGN}.png", resolutionGraphsCanvas=ANALYSISDIR+"{STAGE}_resolution_graphs_{CAMPAIGN}.png", - jsonfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.json" - onnxfile=get_onnx_input + jsonfile=ANALYSISDIR+"Low-Q2_{STAGE}_Resolution_Results_{CAMPAIGN}.json", + onnxfile=get_onnx_input, output: directory("results/lowq2_reconstruction/{STAGE}_{CAMPAIGN}/") shell: diff --git a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C index 2caaf993..8a0c5aa0 100644 --- a/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C +++ b/benchmarks/lowq2_reconstruction/reconstructionAnalysis.C @@ -9,13 +9,14 @@ #include "TCanvas.h" #include "TStyle.h" -void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scripts/tagger_inference_new5.root", - float beamEnergy = 10.0, +void reconstructionAnalysis(TString inFile = "/home/simong/EIC/detector_benchmarks_anl/sim_output/lowq2_reconstruction/analysis/Low-Q2_retrained_Particles_new.eicrecon.edm4hep.root", + float beamEnergy = 18.0, TString outFile = "reconstruction_results.root", TString momentumCanvasName = "momentum_resolution.png", TString energyThetaPhiCanvasName = "energy_theta_phi_resolution.png", TString relationCanvasName = "relation_resolution.png", - TString resolutionGraphsCanvasName = "resolution_graphs.png") { + TString resolutionGraphsCanvasName = "resolution_graphs.png", + std::string particleCollectionName = "TaggerTrackerReconstructedParticles") { //Set ROOT style gStyle->SetPadLeftMargin(0.1); // Set left margin @@ -30,15 +31,15 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip gStyle->SetTitleYOffset(1.0); // Adjust y-axis title offset gStyle->SetOptStat(0); - ROOT::RDataFrame d0("events",inFile, {"MCParticles","TaggerTrackerReconstructedParticles"}); + ROOT::RDataFrame d0("events",inFile, {"MCParticles",particleCollectionName}); auto filterDF = d0.Define("SimParticles", "MCParticles[MCParticles.generatorStatus==1 && MCParticles.PDG==11]") .Filter("SimParticles.size()==1") - .Filter("TaggerTrackerReconstructedParticles.size()==1"); + .Filter(particleCollectionName+".size()==1"); // Plot x,y,z momentum resolution as a function of the MCParticle momentum auto momentumDF = filterDF - .Define("reco_momentum", "TaggerTrackerReconstructedParticles[0].momentum") + .Define("reco_momentum", particleCollectionName+"[0].momentum") .Define("mc_momentum", "SimParticles[0].momentum") .Define("px_rec", "reco_momentum.x") .Define("py_rec", "reco_momentum.y") @@ -57,7 +58,7 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip .Define("px_diff", "(px_rec - px_mc)") .Define("py_diff", "(py_rec - py_mc)") .Define("pz_res", "(pz_rec - pz_mc)/pz_mc") - .Define("E_rec", "TaggerTrackerReconstructedParticles[0].energy") + .Define("E_rec", particleCollectionName+"[0].energy") .Define("E_mc", "std::sqrt(px_mc*px_mc + py_mc*py_mc + pz_mc*pz_mc + SimParticles[0].mass*SimParticles[0].mass)") .Define("E_res", "(E_rec - E_mc)/E_mc") .Define("theta_diff", "(theta_rec - theta_mc)") @@ -109,26 +110,29 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip auto theta_diff_Hist = momentumDF.Histo1D({"theta_diff", "theta difference; theta difference [rad]; Entries", resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_diff"); auto phi_diff_Hist = momentumDF.Histo1D({"phi_diff", "phi difference; phi difference [deg]; Entries", resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_diff"); - // Plot Reconstructed energy, theta and phi resolutions as a function of each mc value of energy, thata and phi - auto E_res_vs_E_Hist = momentumDF.Histo2D({"E_res_vs_E", "E resolution vs E MC; E MC [GeV]; E resolution [GeV]", energyBins, energyRange[0], energyRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "E_mc", "E_res"); - auto E_res_vs_theta_Hist = momentumDF.Histo2D({"E_res_vs_theta", "E resolution vs theta MC; theta MC [rad]; E resolution [GeV]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "theta_mc", "E_res"); - auto E_res_vs_phi_Hist = momentumDF.Histo2D({"E_res_vs_phi", "E resolution vs phi MC; phi MC [deg]; E resolution [GeV]", phiBins, phiRange[0], phiRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "phi_mc", "E_res"); - auto theta_diff_vs_E_Hist = momentumDF.Histo2D({"theta_diff_vs_E", "theta difference vs E MC; E MC [GeV]; theta difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "E_mc", "theta_diff"); - auto theta_diff_vs_theta_Hist = momentumDF.Histo2D({"theta_diff_vs_theta", "theta difference vs theta MC; theta MC [rad]; theta difference [rad]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_mc", "theta_diff"); - auto theta_diff_vs_phi_Hist = momentumDF.Histo2D({"theta_diff_vs_phi", "theta difference vs phi MC; phi MC [deg]; theta difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "phi_mc", "theta_diff"); - auto phi_diff_vs_E_Hist = momentumDF.Histo2D({"phi_diff_vs_E", "phi difference vs E MC; E MC [GeV]; phi difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "E_mc", "phi_diff"); - auto phi_diff_vs_theta_Hist = momentumDF.Histo2D({"phi_diff_vs_theta", "phi difference vs theta MC; theta MC [rad]; phi difference [deg]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "theta_mc", "phi_diff"); - auto phi_diff_vs_phi_Hist = momentumDF.Histo2D({"phi_diff_vs_phi", "phi difference vs phi MC; phi MC [deg]; phi difference [deg]", phiBins, phiRange[0], phiRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_mc", "phi_diff"); + // Plot Reconstructed energy, theta and phi resolutions as a function of each reconstructed value of energy, thata and phi + auto E_res_vs_E_Hist = momentumDF.Histo2D({"E_res_vs_E", "E resolution vs E reconstructed; E reconstructed [GeV]; E resolution [GeV]", energyBins, energyRange[0], energyRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "E_rec", "E_res"); + auto E_res_vs_theta_Hist = momentumDF.Histo2D({"E_res_vs_theta", "E resolution vs theta reconstructed; theta reconstructed [rad]; E resolution [GeV]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "theta_rec", "E_res"); + auto E_res_vs_phi_Hist = momentumDF.Histo2D({"E_res_vs_phi", "E resolution vs phi reconstructed; phi reconstructed [deg]; E resolution [GeV]", phiBins, phiRange[0], phiRange[1], resolutionBins, energyResolutionRange[0], energyResolutionRange[1]}, "phi_rec", "E_res"); + auto theta_diff_vs_E_Hist = momentumDF.Histo2D({"theta_diff_vs_E", "theta difference vs E reconstructed; E reconstructed [GeV]; theta difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "E_rec", "theta_diff"); + auto theta_diff_vs_theta_Hist = momentumDF.Histo2D({"theta_diff_vs_theta", "theta difference vs theta reconstructed; theta reconstructed [rad]; theta difference [rad]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "theta_rec", "theta_diff"); + auto theta_diff_vs_phi_Hist = momentumDF.Histo2D({"theta_diff_vs_phi", "theta difference vs phi reconstructed; phi reconstructed [deg]; theta difference [rad]", phiBins, phiRange[0], phiRange[1], resolutionBins, thetaResolutionRange[0], thetaResolutionRange[1]}, "phi_rec", "theta_diff"); + auto phi_diff_vs_E_Hist = momentumDF.Histo2D({"phi_diff_vs_E", "phi difference vs E reconstructed; E reconstructed [GeV]; phi difference [rad]", energyBins, energyRange[0], energyRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "E_rec", "phi_diff"); + auto phi_diff_vs_theta_Hist = momentumDF.Histo2D({"phi_diff_vs_theta", "phi difference vs theta reconstructed; theta reconstructed [rad]; phi difference [deg]", thetaBins, thetaRange[0], thetaRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "theta_rec", "phi_diff"); + auto phi_diff_vs_phi_Hist = momentumDF.Histo2D({"phi_diff_vs_phi", "phi difference vs phi reconstructed; phi reconstructed [deg]; phi difference [deg]", phiBins, phiRange[0], phiRange[1], resolutionBins, phiResolutionRange[0], phiResolutionRange[1]}, "phi_rec", "phi_diff"); // Create canvas for momentum component plots TCanvas *cMomentum = new TCanvas("momentum_canvas", "Momentum Resolution", 3000, 1600); cMomentum->Divide(3, 2); cMomentum->cd(1); px_Hist->Draw("colz"); + gPad->SetLogz(); cMomentum->cd(2); py_Hist->Draw("colz"); + gPad->SetLogz(); cMomentum->cd(3); pz_Hist->Draw("colz"); + gPad->SetLogz(); cMomentum->cd(4); px_diff_Hist->Draw(); cMomentum->cd(5); @@ -145,10 +149,13 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip cEnergyThetaPhi->Divide(3, 2); cEnergyThetaPhi->cd(1); E_Hist->Draw("colz"); + gPad->SetLogz(); cEnergyThetaPhi->cd(2); theta_Hist->Draw("colz"); + gPad->SetLogz(); cEnergyThetaPhi->cd(3); phi_Hist->Draw("colz"); + gPad->SetLogz(); cEnergyThetaPhi->cd(4); E_res_Hist->Draw(); cEnergyThetaPhi->cd(5); @@ -165,22 +172,31 @@ void reconstructionAnalysis(TString inFile = "/home/simong/EIC/scrip cResolutionVsMC->Divide(3, 3); cResolutionVsMC->cd(1); E_res_vs_E_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(2); E_res_vs_theta_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(3); E_res_vs_phi_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(4); theta_diff_vs_E_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(5); theta_diff_vs_theta_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(6); theta_diff_vs_phi_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(7); phi_diff_vs_E_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(8); phi_diff_vs_theta_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->cd(9); phi_diff_vs_phi_Hist->Draw("colz"); + gPad->SetLogz(); cResolutionVsMC->SetGrid(); cResolutionVsMC->Update(); // Save the canvas as a PNG file From 06ba1dc11e96f005efb0942ae61a9368c65b580a Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 19 Aug 2025 21:47:42 +0100 Subject: [PATCH 61/65] Fix beamline running --- benchmarks/beamline/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/beamline/config.yml b/benchmarks/beamline/config.yml index 9309cbf3..02dec3fc 100644 --- a/benchmarks/beamline/config.yml +++ b/benchmarks/beamline/config.yml @@ -1,4 +1,4 @@ -sim:beamline:beamspot: +sim:beamline: extends: .det_benchmark stage: simulate script: @@ -11,7 +11,7 @@ bench:beamline: extends: .det_benchmark stage: benchmarks needs: - - ["sim:beamline:beamspot", "sim:beamline:acceptance"] + - ["sim:beamline"] script: - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB beamline_local From f4f68e91574b73d437269cc08acdc9f5cdcd23d4 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 19 Aug 2025 21:50:40 +0100 Subject: [PATCH 62/65] Require successful beamline benchmark before triggering reconstruction tests --- benchmarks/lowq2_reconstruction/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/lowq2_reconstruction/config.yml b/benchmarks/lowq2_reconstruction/config.yml index 7ceb44f8..373dff8f 100644 --- a/benchmarks/lowq2_reconstruction/config.yml +++ b/benchmarks/lowq2_reconstruction/config.yml @@ -10,7 +10,7 @@ bench:lowq2_reconstruction: extends: .det_benchmark stage: benchmarks needs: - - "sim:lowq2_reconstruction" + - ["sim:lowq2_reconstruction","bench:beamline"] script: - snakemake $SNAKEMAKE_FLAGS --cores $MAX_CORES_PER_JOB lowq2_reconstruction_local From 0a8965192fa62f2d1847fcd4e2e25e0094b55566 Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 19 Aug 2025 21:56:57 +0100 Subject: [PATCH 63/65] Remove optional projection from old inputs --- .../lowq2_reconstruction/RegressionModel.py | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/benchmarks/lowq2_reconstruction/RegressionModel.py b/benchmarks/lowq2_reconstruction/RegressionModel.py index 4b5fb972..16af78f3 100644 --- a/benchmarks/lowq2_reconstruction/RegressionModel.py +++ b/benchmarks/lowq2_reconstruction/RegressionModel.py @@ -37,10 +37,9 @@ def project_numpy(self, arr): return projected.cpu().numpy() class RegressionModel(nn.Module): - def __init__(self, project=True): + def __init__(self): super(RegressionModel, self).__init__() - self.project = project - self.project_to_x0 = ProjectToX0Plane() if project else None + self.project_to_x0 = ProjectToX0Plane() self.fc1 = nn.Linear(4, 512) self.fc2 = nn.Linear(512, 64) self.fc3 = nn.Linear(64, 3) # Output layer for @@ -54,8 +53,7 @@ def __init__(self, project=True): def forward(self, x): # Conditionally apply projection - if self.project and self.project_to_x0 is not None: - x = self.project_to_x0(x) + x = self.project_to_x0(x) # Normalize inputs x = (x - self.input_mean) / self.input_std @@ -85,12 +83,8 @@ def preprocess_data(model, data_loader, adapt=True): targets = data_loader.dataset.tensors[1] - # Apply projection if project_to_x0 is not None - if model.project_to_x0 is not None: - projected_inputs = model.project_to_x0(inputs) - else: - projected_inputs = inputs # Skip projection if not needed - + projected_inputs = model.project_to_x0(inputs) + # Compute normalization parameters if adapt: model.adapt(projected_inputs, targets) @@ -102,9 +96,9 @@ def preprocess_data(model, data_loader, adapt=True): # Replace the dataset with preprocessed data data_loader.dataset.tensors = (normalized_inputs, normalized_targets) -def makeModel(project=True): +def makeModel(): # Create the model - model = RegressionModel(project=project) + model = RegressionModel() # Define the optimizer optimizer = optim.Adam(model.parameters(), lr=0.0004) # Define the loss function @@ -113,18 +107,9 @@ def makeModel(project=True): return model, optimizer, criterion def trainModel(epochs, train_loader, val_loader, device): - - project = True - # Check shape of input data to see if projection needs to be done - if train_loader.dataset.tensors[0].shape[1] == 6: - project = True - elif train_loader.dataset.tensors[0].shape[1] == 4: - project = False - else: - raise ValueError("Input data must have shape (N, 6) or (N, 4)") - - model, optimizer, criterion = makeModel(project=project) - + + model, optimizer, criterion = makeModel() + model.to(device) # Preprocess training and validation data From 0f8f68df4bd67b317e360ac8724113180c63bb7c Mon Sep 17 00:00:00 2001 From: simonge Date: Tue, 19 Aug 2025 22:08:52 +0100 Subject: [PATCH 64/65] Reinclude beamline benchmarks --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 238d589c..aa6a893f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -124,7 +124,7 @@ common:setup: include: # - local: 'benchmarks/backgrounds/config.yml' # - local: 'benchmarks/backwards_ecal/config.yml' - # - local: 'benchmarks/beamline/config.yml' + - local: 'benchmarks/beamline/config.yml' # - local: 'benchmarks/calo_pid/config.yml' # - local: 'benchmarks/campaign/config.yml' # - local: 'benchmarks/ecal_gaps/config.yml' From 349cc5341fc6827805681cdf54ebb1077e9d7c1b Mon Sep 17 00:00:00 2001 From: simonge Date: Wed, 20 Aug 2025 09:50:22 +0100 Subject: [PATCH 65/65] Re-enable in snakefile too --- Snakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snakefile b/Snakefile index ad676385..6c22f20e 100644 --- a/Snakefile +++ b/Snakefile @@ -33,7 +33,7 @@ def find_epic_libraries(): # include: "benchmarks/backgrounds/Snakefile" # include: "benchmarks/backwards_ecal/Snakefile" # include: "benchmarks/barrel_ecal/Snakefile" -# include: "benchmarks/beamline/Snakefile" +include: "benchmarks/beamline/Snakefile" # include: "benchmarks/calo_pid/Snakefile" # include: "benchmarks/campaign/Snakefile" # include: "benchmarks/ecal_gaps/Snakefile"