File indexing completed on 2025-12-16 09:27:45
0001
0002
0003 #include <iostream>
0004 #include "DD4hep/Detector.h"
0005 #include "DDRec/CellIDPositionConverter.h"
0006 #include "edm4hep/MCParticleCollection.h"
0007 #include "edm4hep/SimTrackerHitCollection.h"
0008 #include "edm4hep/SimCalorimeterHitCollection.h"
0009 #include "edm4hep/Vector3f.h"
0010 #include "edm4hep/Vector3d.h"
0011 #include "ROOT/RDataFrame.hxx"
0012 #include "ROOT/RDF/RInterface.hxx"
0013 #include "ROOT/RVec.hxx"
0014 #include "shared_functions.h"
0015 #include "TCanvas.h"
0016 #include "TStyle.h"
0017 #include "TF1.h"
0018 #include "TEllipse.h"
0019
0020 using RVecS = ROOT::VecOps::RVec<string>;
0021 using RNode = ROOT::RDF::RNode;
0022
0023 int beamlineAnalysis( TString inFile = "/home/simong/EIC/detector_benchmarks_anl/sim_output/beamline/acceptanceTestXS3.edm4hep.root",
0024 TString outFile = "output.root",
0025 std::string compactName = "/home/simong/EIC/epic/install/share/epic/epic_ip6_extended.xml",
0026 TString beamspotCanvasName = "beamspot_canvas.png",
0027 TString x_pxCanvasName = "x_px_canvas.png",
0028 TString y_pyCanvasName = "y_py_canvas.png",
0029 TString fittedPositionMeansCanvasName = "fitted_means_stddevs.png",
0030 TString fittedMomentumMeansCanvasName = "fitted_momentum_means_stddevs.png",
0031 TString pipeParamsCanvasName = "pipe_parameters.png"
0032 ){
0033
0034
0035 gStyle->SetPadLeftMargin(0.1);
0036 gStyle->SetPadRightMargin(0.0);
0037 gStyle->SetPadTopMargin(0.0);
0038 gStyle->SetPadBottomMargin(0.1);
0039 gStyle->SetTitleAlign(13);
0040 gStyle->SetTitleX(0.12);
0041 gStyle->SetTitleY(0.985);
0042 gStyle->SetTitleSize(0.08, "t");
0043 gStyle->SetTitleXOffset(1.0);
0044 gStyle->SetTitleYOffset(1.0);
0045 gStyle->SetOptStat(0);
0046
0047
0048 ROOT::EnableImplicitMT();
0049
0050 int pass = 0;
0051
0052
0053 dd4hep::Detector& detector = dd4hep::Detector::getInstance();
0054 detector.fromCompact(compactName);
0055
0056 ROOT::RDataFrame d0("events",inFile, {"BackwardsBeamlineHits"});
0057 RNode d1 = d0;
0058 RVecS colNames = d1.GetColumnNames();
0059
0060
0061
0062 int nEntries = d1.Count().GetValue();
0063 float acceptableLoss = 0.999;
0064 float acceptableEntries = nEntries * acceptableLoss;
0065
0066
0067 std::string readoutName = "BackwardsBeamlineHits";
0068
0069 std::cout << "Running lazy RDataframe execution" << std::endl;
0070
0071 if(Any(colNames==readoutName)){
0072
0073 d1 = d1.Define("pipeID",getSubID("pipe",detector),{readoutName})
0074 .Define("endID",getSubID("end",detector),{readoutName})
0075 .Define("pipeParameters",getVolumeParametersFromCellID(detector),{readoutName})
0076 .Define("pipeRadius",[](const ROOT::VecOps::RVec<volParams>& params) {
0077 ROOT::VecOps::RVec<double> radii;
0078 for (const auto& param : params) {
0079 radii.push_back(param.radius);
0080 }
0081 return radii;
0082 }, {"pipeParameters"})
0083 .Define("isConeSegment",[](const ROOT::VecOps::RVec<volParams>& params) {
0084 ROOT::VecOps::RVec<bool> cones;
0085 for (const auto& param : params) {
0086 cones.push_back(param.isConeSegment);
0087 }
0088 return cones;
0089 }, {"pipeParameters"})
0090 .Define("xdet",[](const ROOT::VecOps::RVec<volParams>& params) {
0091 ROOT::VecOps::RVec<double> xPos;
0092 for (const auto& param : params) {
0093 xPos.push_back(param.xPos);
0094 }
0095 return xPos;
0096 }, {"pipeParameters"})
0097 .Define("zdet",[](const ROOT::VecOps::RVec<volParams>& params) {
0098 ROOT::VecOps::RVec<double> zPos;
0099 for (const auto& param : params) {
0100 zPos.push_back(param.zPos);
0101 }
0102 return zPos;
0103 }, {"pipeParameters"})
0104 .Define("rotation",[](const ROOT::VecOps::RVec<volParams>& params) {
0105 ROOT::VecOps::RVec<double> rotation;
0106 for (const auto& param : params) {
0107 rotation.push_back(param.rotation);
0108 }
0109 return rotation;
0110 }, {"pipeParameters"});
0111
0112
0113
0114 d1 = d1.Define("xpos_global","BackwardsBeamlineHits.position.x")
0115 .Define("ypos_global","BackwardsBeamlineHits.position.y")
0116 .Define("zpos_global","BackwardsBeamlineHits.position.z")
0117 .Define("px_global","BackwardsBeamlineHits.momentum.x")
0118 .Define("py_global","BackwardsBeamlineHits.momentum.y")
0119 .Define("pz_global","BackwardsBeamlineHits.momentum.z");
0120
0121 d1 = d1.Define("hitPosMom",globalToLocal(detector),{readoutName})
0122 .Define("xpos","hitPosMom[0]")
0123 .Define("ypos","hitPosMom[1]")
0124 .Define("zpos","hitPosMom[2]")
0125 .Define("xmomMag","hitPosMom[3]")
0126 .Define("ymomMag","hitPosMom[4]")
0127 .Define("zmomMag","hitPosMom[5]")
0128 .Define("momMag","sqrt(xmomMag*xmomMag+ymomMag*ymomMag+zmomMag*zmomMag)")
0129 .Define("xmom","xmomMag/momMag")
0130 .Define("ymom","ymomMag/momMag")
0131 .Define("zmom","zmomMag/momMag");
0132
0133 }
0134 else{
0135 std::cout << "Collection " << readoutName << " not found in file" << std::endl;
0136 return 1;
0137 }
0138
0139
0140 auto maxPipeRadius = 2*d1.Max("pipeRadius").GetValue();
0141
0142 std::cout << "Executing Analysis and creating histograms" << std::endl;
0143
0144
0145 std::map<TString,ROOT::RDF::RResultPtr<TH2D>> hHistsxy;
0146 std::map<TString,ROOT::RDF::RResultPtr<TH2D>> hHistsxyZoom;
0147 std::map<TString,ROOT::RDF::RResultPtr<TH2D>> hHistsxpx;
0148 std::map<TString,ROOT::RDF::RResultPtr<TH2D>> hHistsypy;
0149
0150 std::map<TString,double> xMeans;
0151 std::map<TString,double> yMeans;
0152 std::map<TString,double> xStdDevs;
0153 std::map<TString,double> yStdDevs;
0154 std::map<TString,double> pxMeans;
0155 std::map<TString,double> pyMeans;
0156 std::map<TString,double> pxStdDevs;
0157 std::map<TString,double> pyStdDevs;
0158
0159
0160 std::map<TString,double> xMeanFit;
0161 std::map<TString,double> yMeanFit;
0162 std::map<TString,double> xMeanFitErr;
0163 std::map<TString,double> yMeanFitErr;
0164 std::map<TString,double> xStdDevFit;
0165 std::map<TString,double> yStdDevFit;
0166 std::map<TString,double> xStdDevFitErr;
0167 std::map<TString,double> yStdDevFitErr;
0168 std::map<TString,double> pxMeanFit;
0169 std::map<TString,double> pyMeanFit;
0170 std::map<TString,double> pxMeanFitErr;
0171 std::map<TString,double> pyMeanFitErr;
0172 std::map<TString,double> pxStdDevFit;
0173 std::map<TString,double> pyStdDevFit;
0174 std::map<TString,double> pxStdDevFitErr;
0175 std::map<TString,double> pyStdDevFitErr;
0176
0177 std::map<TString,double> pipeRadii;
0178 std::map<TString,double> pipeXPos;
0179 std::map<TString,double> pipeZPos;
0180 std::map<TString,double> pipeRotation;
0181 std::map<TString,bool> pipeIsConeSegment;
0182
0183
0184 auto xmin_ptr = d1.Min("xpos");
0185 auto xmax_ptr = d1.Max("xpos");
0186 auto ymin_ptr = d1.Min("ypos");
0187 auto ymax_ptr = d1.Max("ypos");
0188 auto pxmin_ptr = d1.Min("xmom");
0189 auto pxmax_ptr = d1.Max("xmom");
0190 auto pymin_ptr = d1.Min("ymom");
0191 auto pymax_ptr = d1.Max("ymom");
0192
0193
0194 auto xmin = xmin_ptr.GetValue();
0195 auto xmax = xmax_ptr.GetValue();
0196 auto ymin = ymin_ptr.GetValue();
0197 auto ymax = ymax_ptr.GetValue();
0198 auto pxmin = pxmin_ptr.GetValue();
0199 auto pxmax = pxmax_ptr.GetValue();
0200 auto pymin = pymin_ptr.GetValue();
0201 auto pymax = pymax_ptr.GetValue();
0202
0203
0204 for(int i=0; i<=7; i++){
0205
0206 std::string name = "pipeID";
0207 name += std::to_string(i);
0208 auto filterDF = d1.Define("xposf","xpos[pipeID=="+std::to_string(i)+"]")
0209 .Define("yposf","ypos[pipeID=="+std::to_string(i)+"]")
0210 .Define("xmomf","xmom[pipeID=="+std::to_string(i)+"]")
0211 .Define("ymomf","ymom[pipeID=="+std::to_string(i)+"]")
0212 .Define("pipeRadiusf","pipeRadius[pipeID=="+std::to_string(i)+"]")
0213 .Define("isConeSegmentf","isConeSegment[pipeID=="+std::to_string(i)+"]")
0214 .Define("xdetf","xdet[pipeID=="+std::to_string(i)+"]")
0215 .Define("zdetf","zdet[pipeID=="+std::to_string(i)+"]")
0216 .Define("rotationf","rotation[pipeID=="+std::to_string(i)+"]");
0217
0218
0219
0220 auto xminf = filterDF.Min("xposf").GetValue();
0221 auto xmaxf = filterDF.Max("xposf").GetValue();
0222 auto yminf = filterDF.Min("yposf").GetValue();
0223 auto ymaxf = filterDF.Max("yposf").GetValue();
0224 auto pxminf = filterDF.Min("xmomf").GetValue();
0225 auto pxmaxf = filterDF.Max("xmomf").GetValue();
0226 auto pyminf = filterDF.Min("ymomf").GetValue();
0227 auto pymaxf = filterDF.Max("ymomf").GetValue();
0228
0229 if(xminf == xmaxf) {
0230 xminf -= 0.01;
0231 xmaxf += 0.01;
0232 pass = 1;
0233 std::cout << "Warning: xminf == xmaxf for pipe ID " << i << ", likely no hits." << std::endl;
0234 }
0235 if(yminf == ymaxf) {
0236 yminf -= 0.01;
0237 ymaxf += 0.01;
0238 pass = 1;
0239 std::cout << "Warning: yminf == ymaxf for pipe ID " << i << ", likely no hits." << std::endl;
0240 }
0241 if(pxminf == pxmaxf) {
0242 pxminf -= 0.01;
0243 pxmaxf += 0.01;
0244 pass = 1;
0245 std::cout << "Warning: pxminf == pxmaxf for pipe ID " << i << ", likely no hits." << std::endl;
0246 }
0247 if(pyminf == pymaxf) {
0248 pyminf -= 0.01;
0249 pymaxf += 0.01;
0250 pass = 1;
0251 std::cout << "Warning: pyminf == pymaxf for pipe ID " << i << ", likely no hits." << std::endl;
0252 }
0253
0254
0255 xMeans[name] = filterDF.Mean("xposf").GetValue();
0256 yMeans[name] = filterDF.Mean("yposf").GetValue();
0257 xStdDevs[name] = filterDF.StdDev("xposf").GetValue();
0258 yStdDevs[name] = filterDF.StdDev("yposf").GetValue();
0259 pxMeans[name] = filterDF.Mean("xmomf").GetValue();
0260 pyMeans[name] = filterDF.Mean("ymomf").GetValue();
0261 pxStdDevs[name] = filterDF.StdDev("xmomf").GetValue();
0262 pyStdDevs[name] = filterDF.StdDev("ymomf").GetValue();
0263
0264
0265 double halfrange = std::max({xMeans[name]-xminf, xmaxf-xMeans[name], yMeans[name]-yminf, ymaxf-yMeans[name]});
0266 double xMinZoom = xMeans[name] - halfrange;
0267 double xMaxZoom = xMeans[name] + halfrange;
0268 double yMinZoom = yMeans[name] - halfrange;
0269 double yMaxZoom = yMeans[name] + halfrange;
0270
0271 TString beamspotName = "Beamspot ID"+std::to_string(i)+";x offset [cm]; y offset [cm]";
0272 TString xyname = name+";x Offset [cm]; y Offset [cm]";
0273 TString xname = name+";x Offset [cm]; x trajectory component";
0274 TString yname = name+";y Offset [cm]; y trajectory component";
0275 hHistsxy[name] = filterDF.Histo2D({beamspotName,beamspotName,400,-maxPipeRadius,maxPipeRadius,400,-maxPipeRadius,maxPipeRadius},"xposf","yposf");
0276 hHistsxyZoom[name] = filterDF.Histo2D({xyname,xyname,100,xMinZoom,xMaxZoom,100,yMinZoom,yMaxZoom},"xposf","yposf");
0277 hHistsxpx[name] = filterDF.Histo2D({xname,xname,400,xmin,xmax,400,pxmin,pxmax},"xposf","xmomf");
0278 hHistsypy[name] = filterDF.Histo2D({yname,yname,400,ymin,ymax,400,pymin,pymax},"yposf","ymomf");
0279
0280
0281 pipeRadii[name] = filterDF.Max("pipeRadiusf").GetValue();
0282 std::cout << "Pipe ID: " << name << " Radius: " << pipeRadii[name] << std::endl;
0283 pipeXPos[name] = filterDF.Max("xdetf").GetValue();
0284 pipeZPos[name] = filterDF.Max("zdetf").GetValue();
0285 pipeRotation[name] = filterDF.Max("rotationf").GetValue();
0286 pipeIsConeSegment[name] = filterDF.Max("isConeSegmentf").GetValue();
0287
0288
0289 auto xhist = hHistsxy[name]->ProjectionX();
0290 auto yhist = hHistsxy[name]->ProjectionY();
0291 auto pxhist = hHistsxpx[name]->ProjectionY();
0292 auto pyhist = hHistsypy[name]->ProjectionY();
0293
0294 if(hHistsxy[name]->GetEntries() > 0){
0295 xhist->Fit("gaus","Q");
0296 yhist->Fit("gaus","Q");
0297 pxhist->Fit("gaus","Q");
0298 pyhist->Fit("gaus","Q");
0299
0300 xMeanFit[name] = xhist->GetFunction("gaus")->GetParameter(1);
0301 yMeanFit[name] = yhist->GetFunction("gaus")->GetParameter(1);
0302 xMeanFitErr[name] = xhist->GetFunction("gaus")->GetParError(1);
0303 yMeanFitErr[name] = yhist->GetFunction("gaus")->GetParError(1);
0304 xStdDevFit[name] = xhist->GetFunction("gaus")->GetParameter(2);
0305 yStdDevFit[name] = yhist->GetFunction("gaus")->GetParameter(2);
0306 xStdDevFitErr[name] = xhist->GetFunction("gaus")->GetParError(2);
0307 yStdDevFitErr[name] = yhist->GetFunction("gaus")->GetParError(2);
0308 pxMeanFit[name] = pxhist->GetFunction("gaus")->GetParameter(1);
0309 pyMeanFit[name] = pyhist->GetFunction("gaus")->GetParameter(1);
0310 pxMeanFitErr[name] = pxhist->GetFunction("gaus")->GetParError(1);
0311 pyMeanFitErr[name] = pyhist->GetFunction("gaus")->GetParError(1);
0312 pxStdDevFit[name] = pxhist->GetFunction("gaus")->GetParameter(2);
0313 pyStdDevFit[name] = pyhist->GetFunction("gaus")->GetParameter(2);
0314 pxStdDevFitErr[name] = pxhist->GetFunction("gaus")->GetParError(2);
0315 pyStdDevFitErr[name] = pyhist->GetFunction("gaus")->GetParError(2);
0316 } else {
0317 xMeanFit[name] = 0;
0318 yMeanFit[name] = 0;
0319 xMeanFitErr[name] = 0;
0320 yMeanFitErr[name] = 0;
0321 xStdDevFit[name] = 0;
0322 yStdDevFit[name] = 0;
0323 xStdDevFitErr[name] = 0;
0324 yStdDevFitErr[name] = 0;
0325 pxMeanFit[name] = 0;
0326 pyMeanFit[name] = 0;
0327 pxMeanFitErr[name] = 0;
0328 pyMeanFitErr[name] = 0;
0329 pxStdDevFit[name] = 0;
0330 pyStdDevFit[name] = 0;
0331 pxStdDevFitErr[name] = 0;
0332 pyStdDevFitErr[name] = 0;
0333 }
0334
0335 }
0336
0337
0338
0339
0340 TCanvas *cXY = new TCanvas("beamspot_canvas","beamspot_canvas",3000,1600);
0341 cXY->Divide(4,2);
0342 int i=1;
0343 for(auto [name,h] : hHistsxy){
0344
0345
0346 if(h->GetEntries() < acceptableEntries){
0347 std::cout << "Warning: Only " << h->GetEntries()/nEntries << " of particles contributing to histogram " << name
0348 << " , which is below the accepted threshold of " << acceptableEntries/nEntries << std::endl;
0349 pass = 1;
0350 } else{
0351 std::cout << "Histogram " << name << " has " << h->GetEntries() << " entries." << std::endl;
0352 }
0353
0354
0355 auto pipeRadius = pipeRadii[name];
0356 cXY->cd(i++);
0357
0358 h->Draw("col");
0359
0360
0361 if (pipeIsConeSegment[name] && pipeRadius > 0) {
0362 TEllipse *circle = new TEllipse(0,0,pipeRadius);
0363 circle->SetLineColor(kRed);
0364 circle->SetLineWidth(2);
0365 circle->SetFillStyle(0);
0366 circle->Draw("same");
0367 }
0368
0369
0370 TPad *pad = new TPad("zoomPad", "Zoomed View", 0.65, 0.65, 1.0, 1.0);
0371 pad->SetFillStyle(0);
0372 pad->Draw();
0373 pad->cd();
0374
0375 hHistsxyZoom[name]->SetTitle("");
0376 hHistsxyZoom[name]->GetXaxis()->SetTitle("");
0377 hHistsxyZoom[name]->GetYaxis()->SetTitle("");
0378 hHistsxyZoom[name]->Draw("col");
0379 cXY->cd();
0380 }
0381
0382
0383 TCanvas *cX = new TCanvas("x_px_canvas","x_px_canvas",3000,1600);
0384 cX->Divide(4,2);
0385
0386 i=1;
0387
0388 for(auto& h : hHistsxpx){
0389 cX->cd(i++);
0390 h.second->Draw("col");
0391 }
0392
0393
0394 TCanvas *cY = new TCanvas("y_py_canvas","y_py_canvas",3000,1600);
0395 cY->Divide(4,2);
0396
0397 i=1;
0398 for(auto& h : hHistsypy){
0399 cY->cd(i++);
0400 h.second->Draw("col");
0401 }
0402
0403
0404 cXY->SaveAs(beamspotCanvasName);
0405 cX->SaveAs(x_pxCanvasName);
0406 cY->SaveAs(y_pyCanvasName);
0407
0408
0409
0410
0411
0412
0413 TH1F* hFittedXMeans = CreateFittedHistogram("hFittedXMeans",
0414 "Mean X Offset [cm]",
0415 xMeanFit,
0416 xMeanFitErr,
0417 "Pipe ID");
0418
0419 TH1F* hFittedXStdDevs = CreateFittedHistogram("hFittedXStdDevs",
0420 "Std Deviation X Offset [cm]",
0421 xStdDevFit,
0422 xStdDevFitErr,
0423 "Pipe ID");
0424
0425
0426 TH1F* hFittedYMeans = CreateFittedHistogram("hFittedYMeans",
0427 "Mean Y Offset [cm]",
0428 yMeanFit,
0429 yMeanFitErr,
0430 "Pipe ID");
0431
0432 TH1F* hFittedYStdDevs = CreateFittedHistogram("hFittedYStdDevs",
0433 "Std Deviation Y Offset [cm]",
0434 yStdDevFit,
0435 yStdDevFitErr,
0436 "Pipe ID");
0437
0438 TH1F* hFittedPxMeans = CreateFittedHistogram("hFittedPxMeans",
0439 "Mean Px",
0440 pxMeanFit,
0441 pxMeanFitErr,
0442 "Pipe ID");
0443 TH1F* hFittedPyMeans = CreateFittedHistogram("hFittedPyMeans",
0444 "Mean Py",
0445 pyMeanFit,
0446 pyMeanFitErr,
0447 "Pipe ID");
0448 TH1F* hFittedPxStdDevs = CreateFittedHistogram("hFittedPxStdDevs",
0449 "Std Deviation Px",
0450 pxStdDevFit,
0451 pxStdDevFitErr,
0452 "Pipe ID");
0453 TH1F* hFittedPyStdDevs = CreateFittedHistogram("hFittedPyStdDevs",
0454 "Std Deviation Py",
0455 pyStdDevFit,
0456 pyStdDevFitErr,
0457 "Pipe ID");
0458
0459
0460
0461 TCanvas *cFittedMeans = new TCanvas("cFittedMeans", "Fitted Beamspot Means and Std Deviation", 1200, 800);
0462 cFittedMeans->Divide(2, 2);
0463 cFittedMeans->cd(1);
0464 hFittedXMeans->Draw("E1");
0465 cFittedMeans->cd(2);
0466 hFittedXStdDevs->Draw("E1");
0467 cFittedMeans->cd(3);
0468 hFittedYMeans->Draw("E1");
0469 cFittedMeans->cd(4);
0470 hFittedYStdDevs->Draw("E1");
0471 cFittedMeans->SetGrid();
0472 cFittedMeans->Update();
0473
0474 cFittedMeans->SaveAs(fittedPositionMeansCanvasName);
0475
0476
0477 TCanvas *cFittedMomentumMeans = new TCanvas("cFittedMomentumMeans", "Fitted Momentum Means and Std Deviation", 1200, 800);
0478 cFittedMomentumMeans->Divide(2, 2);
0479 cFittedMomentumMeans->cd(1);
0480 hFittedPxMeans->Draw("E1");
0481 cFittedMomentumMeans->cd(2);
0482 hFittedPxStdDevs->Draw("E1");
0483 cFittedMomentumMeans->cd(3);
0484 hFittedPyMeans->Draw("E1");
0485 cFittedMomentumMeans->cd(4);
0486 hFittedPyStdDevs->Draw("E1");
0487 cFittedMomentumMeans->SetGrid();
0488 cFittedMomentumMeans->Update();
0489
0490 cFittedMomentumMeans->SaveAs(fittedMomentumMeansCanvasName);
0491
0492
0493
0494
0495
0496 TH1F* hPipeRadii = CreateFittedHistogram("hPipeRadii",
0497 "Radius [cm]",
0498 pipeRadii,
0499 {},
0500 "Pipe ID");
0501 TH1F* hPipeXPos = CreateFittedHistogram("hPipeXPos",
0502 "X Position [cm]",
0503 pipeXPos,
0504 {},
0505 "Pipe ID");
0506 TH1F* hPipeZPos = CreateFittedHistogram("hPipeZPos",
0507 "Z Position [cm]",
0508 pipeZPos,
0509 {},
0510 "Pipe ID");
0511 TH1F* hPipeRotations = CreateFittedHistogram("hPipeRotations",
0512 "Rotation [rad]",
0513 pipeRotation,
0514 {},
0515 "Pipe ID");
0516
0517
0518 TCanvas *cPipeParams = new TCanvas("cPipeParams", "Pipe Parameters", 1200, 400);
0519 cPipeParams->Divide(4, 1);
0520 cPipeParams->cd(1);
0521 hPipeRadii->Draw("");
0522 cPipeParams->cd(2);
0523 hPipeXPos->Draw("");
0524 cPipeParams->cd(3);
0525 hPipeZPos->Draw("");
0526 cPipeParams->cd(4);
0527 hPipeRotations->Draw("");
0528 cPipeParams->SetGrid();
0529 cPipeParams->Update();
0530
0531 cPipeParams->SaveAs(pipeParamsCanvasName);
0532
0533
0534 TFile *f = new TFile(outFile,"RECREATE");
0535 cXY->Write();
0536 cX->Write();
0537 cY->Write();
0538 cFittedMeans->Write();
0539 cFittedMomentumMeans->Write();
0540 cPipeParams->Write();
0541
0542 f->Close();
0543
0544 std::cout << "Analysis complete. Results saved to " << outFile << std::endl;
0545 return pass;
0546
0547
0548
0549
0550
0551 }