Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-03 08:15:36

0001 #include "SpinDBQA.h"
0002 
0003 #include <TFile.h>
0004 #include <TH1D.h>
0005 #include <TH1F.h>
0006 #include <TGraphErrors.h>
0007 
0008 #include <boost/format.hpp>
0009 
0010 #include <regex>
0011 #include <filesystem>
0012 #include <iostream>
0013 #include <fstream>
0014 #include <vector>
0015 #include <string>
0016 #include <iterator>
0017 
0018 
0019 SpinDBQA::SpinDBQA()
0020     : spin_cont(),
0021       spin_out("phnxrc"),
0022       spin_in()
0023 {
0024   locpolbf = new TGraphErrors();
0025   locpolbf->SetName("locpolbf");
0026   locpolbb = new TGraphErrors();
0027   locpolbb->SetName("locpolbb");
0028   locpolyf = new TGraphErrors();
0029   locpolyf->SetName("locpolyf");
0030   locpolyb = new TGraphErrors();
0031   locpolyb->SetName("locpolyb");
0032 
0033   locpolphasebf = new TGraphErrors();
0034   locpolphasebf->SetName("locpolphasebf");
0035   locpolphasebb = new TGraphErrors();
0036   locpolphasebb->SetName("locpolphasebb");
0037   locpolphaseyf = new TGraphErrors();
0038   locpolphaseyf->SetName("locpolphaseyf");
0039   locpolphaseyb = new TGraphErrors();
0040   locpolphaseyb->SetName("locpolphaseyb");
0041 
0042 }
0043 
0044 
0045 void SpinDBQA::SetRunList(std::string runlist)
0046 {
0047   std::string line;
0048   std::ifstream irunlist;
0049   irunlist.open(runlist);
0050   if(!irunlist)
0051   {
0052     std::cout << "failed to open runlist " << std::endl;
0053     exit(1);
0054   }
0055   while (std::getline(irunlist, line)) {
0056     runlistvect.push_back(std::stoi(line));
0057   }
0058   irunlist.close(); 
0059 }
0060 
0061 void SpinDBQA::ReadSpinDBData()
0062 {
0063   std::cout << "Reading data from spin database." << std::endl;
0064   std::cout << "There are " << runlistvect.size() << " runs. This may take several minutes..." << std::endl;
0065   int ite = 0;
0066 
0067   if (b_defaultQA)
0068   {
0069     spin_out.StoreDBContent(runlistvect.front(),runlistvect.back());
0070   }
0071   else
0072   {
0073     spin_out.StoreDBContent(runlistvect.front(),runlistvect.back(), qalevel);
0074   }
0075 
0076   for (const auto &run : runlistvect)
0077   {
0078     runnumber = run;
0079     int runexists = spin_out.GetDBContentStore(spin_cont,runnumber);
0080     if (runexists == 0)
0081     {
0082       map_fillnumber[runnumber] = -999;
0083       continue;
0084     }
0085     //map_defaultQA[runnumber] = spin_out.GetDefaultQA(runnumber);
0086     map_fillnumber[runnumber] = spin_cont.GetFillNumber();
0087     map_qa_level[runnumber] = spin_cont.GetQALevel();
0088     map_crossingshift[runnumber] = spin_cont.GetCrossingShift();
0089     
0090     for (int i = 0; i < 120; i++)
0091     {
0092       map_bluespin[runnumber].push_back(spin_cont.GetSpinPatternBlue(i));
0093       map_yellspin[runnumber].push_back(spin_cont.GetSpinPatternYellow(i));
0094       map_mbdns[runnumber].push_back(spin_cont.GetScalerMbdNoCut(i));
0095       map_mbdvtx[runnumber].push_back(spin_cont.GetScalerMbdVertexCut(i));
0096       map_zdcns[runnumber].push_back(spin_cont.GetScalerZdcNoCut(i));
0097     }
0098     
0099     spin_cont.GetPolarizationBlue(0, map_bluepol[runnumber], map_bluepolerr[runnumber]);
0100     spin_cont.GetPolarizationYellow(0, map_yellpol[runnumber], map_yellpolerr[runnumber]);
0101     map_badrunqa[runnumber] = spin_cont.GetBadRunFlag();
0102     map_crossingangle[runnumber] = spin_cont.GetCrossAngle();
0103     map_crossanglestd[runnumber] = spin_cont.GetCrossAngleStd();
0104     map_crossanglemin[runnumber] = spin_cont.GetCrossAngleMin();
0105     map_crossanglemax[runnumber] = spin_cont.GetCrossAngleMax();
0106     spin_cont.GetAsymBlueForward(map_asymbf[runnumber], map_asymerrbf[runnumber]);
0107     spin_cont.GetAsymBlueBackward(map_asymbb[runnumber], map_asymerrbb[runnumber]);
0108     spin_cont.GetAsymYellowForward(map_asymyf[runnumber], map_asymerryf[runnumber]);
0109     spin_cont.GetAsymYellowBackward(map_asymyb[runnumber], map_asymerryb[runnumber]);
0110     spin_cont.GetPhaseBlueForward(map_phasebf[runnumber], map_phaseerrbf[runnumber]);
0111     spin_cont.GetPhaseBlueBackward(map_phasebb[runnumber], map_phaseerrbb[runnumber]);
0112     spin_cont.GetPhaseYellowForward(map_phaseyf[runnumber], map_phaseerryf[runnumber]);
0113     spin_cont.GetPhaseYellowBackward(map_phaseyb[runnumber], map_phaseerryb[runnumber]);
0114     
0115     ++ite;
0116   }
0117   return;
0118 }
0119 
0120 void SpinDBQA::doQA()
0121 {
0122   for (const auto &run : runlistvect)
0123   {
0124     runnumber = run;
0125     std::cout << "Processing QA on run: " << runnumber << std::endl;
0126     std::string stringMarkdown = "";
0127     std::string stringHtml = "";
0128     if (map_fillnumber[runnumber] == -999)
0129     {
0130       stringHtml += "Run does not exist at this qa_level";
0131     }
0132     else
0133     {
0134       CNIHjetQA(stringMarkdown,stringHtml);
0135       if (runnumber >= 45236){GL1pScalersQA(stringMarkdown,stringHtml);}
0136       else{stringHtml += "GL1p Scalers: <font color=\"#800080\">NOT AVAILABLE</font>; ";}
0137       LocalPolQA(stringMarkdown,stringHtml);
0138       CrossingAngleQA(stringMarkdown,stringHtml);
0139       map_spindbqa_markdown[stringMarkdown].push_back(runnumber);
0140     }
0141     
0142     map_spindbqa_html[stringHtml].push_back(runnumber);
0143   }
0144   return;
0145 }
0146 
0147 void SpinDBQA::CNIHjetQA(std::string &stringMarkdown, std::string &stringHtml)
0148 {
0149   // ============== CNI H-jet results ============== //
0150   std::string filename = (boost::format("%s/fill_%d/DST_%d.root") % _cnipathname % map_fillnumber[runnumber] % map_fillnumber[runnumber]).str();
0151   std::ifstream file(filename.c_str());
0152   if (!file) // for qa_level = 0xFFFF cases
0153   {
0154     std::cerr << "File does not exist: " << filename << std::endl;
0155     stringMarkdown +=  ("\u274C CNI measurement exists ");
0156     stringHtml +=  ("Beam polarization: <font color=\"#800080\">NOT AVAILABLE</font>; Spin patterns: <font color=\"#800080\">NOT AVAILABLE</font>; ");
0157   }
0158   else if (map_bluepol[runnumber] == -999 || map_yellpol[runnumber] == -999) // for qa_level = 0 cases
0159   {
0160     stringMarkdown +=  ("\u274C CNI measurement exists ");
0161     stringHtml +=  ("Beam polarization: <font color=\"#800080\">NOT AVAILABLE</font>; Spin patterns: <font color=\"#800080\">NOT AVAILABLE</font>; ");
0162   }
0163   else
0164   {
0165     TFile *cnipol = new TFile(filename.c_str());
0166     if (!cnipol || cnipol->IsZombie()) 
0167     {
0168       std::cout << "Failed to open file: " << filename << std::endl;
0169       stringMarkdown +=  ("\u274C CNI measurement exists ");
0170       stringHtml +=  ("Beam polarization: <font color=\"#800080\">NOT AVAILABLE</font>; Spin patterns: <font color=\"#800080\">NOT AVAILABLE</font>; ");
0171     }
0172     else
0173     {
0174       stringMarkdown +=  ("\u2705 CNI measurement exists ");
0175       // ============== Get Hjet CNI beam polarizations and check them ============== //
0176       TH1D *h_results = (TH1D*)cnipol->Get("h_results");
0177       h_results->SetDirectory(0);
0178       // ============== Get CNI pol spin patterns ============== //
0179       TH1D *hcnipatternYellow = (TH1D*)cnipol->Get("h_polYellow");
0180       TH1D *hcnipatternBlue = (TH1D*)cnipol->Get("h_polBlue");
0181       hcnipatternYellow->SetDirectory(0);
0182       hcnipatternBlue->SetDirectory(0);
0183 
0184       cnipol->Close();
0185       delete cnipol;
0186 
0187       // ============== Check Hjet CNI beam polarizations ============== //
0188       float cnibeampol_yellow = h_results->GetBinContent(9);
0189       float cnibeampol_blue = h_results->GetBinContent(10);
0190       //float cnibeampolerr_yellow = h_results->GetBinError(9);
0191       //float cnibeampolerr_blue = h_results->GetBinError(10);
0192       // ============== set bad run conditions ============== //
0193       //badrunqa = (cnibeampol_yellow < 0 || cnibeampol_yellow > 1 || cnibeampol_blue < 0 || cnibeampol_blue > 0) ? 1 : badrunqa;
0194       stringMarkdown +=  (cnibeampol_yellow < 0 || cnibeampol_yellow > 1 || cnibeampol_blue < 0 || cnibeampol_blue > 0) ? 
0195                           "\u2705 CNI beam polarization " : "\u274C CNI beam polarization ";
0196       stringHtml +=  (cnibeampol_yellow < 0 || cnibeampol_yellow > 1 || cnibeampol_blue < 0 || cnibeampol_blue > 0) ?
0197                           "Beam polarization: <font color=\"#11cf17\">GOOD</font>; " : " Beam polarization: <font color=\"#ff0000\">BAD</font>; ";
0198       // ============== Check that spin pattern matches CNI pol spin patterns ============== //
0199       // ============== Find abort gap in CNI spin patterns to find CNI crossing shift //
0200       int abortgaplength = 8; 
0201       int currentCountYell = 0;
0202       int currentCountBlue = 0;
0203       int lastAgBunchIndexYell = -1;
0204       int lastAgBunchIndexBlue = -1;
0205 
0206       for (size_t i = 0; i < 120; ++i) {
0207           if (hcnipatternYellow->GetBinContent(i+1) == 0) {
0208               currentCountYell++; 
0209               if (currentCountYell > abortgaplength) {
0210                   abortgaplength = currentCountYell;
0211                   lastAgBunchIndexYell = i;
0212               }
0213           } else {
0214               currentCountYell = 0;
0215           }
0216 
0217           if (hcnipatternBlue->GetBinContent(i+1) == 0) {
0218               currentCountBlue++; 
0219               if (currentCountBlue > abortgaplength) {
0220                   abortgaplength = currentCountBlue;
0221                   lastAgBunchIndexBlue = i; 
0222               }
0223           } else {
0224               currentCountBlue = 0;
0225           }
0226       }
0227 
0228       int xingshift_cniYell = 119-lastAgBunchIndexYell;
0229       int xingshift_cniBlue = 119-lastAgBunchIndexBlue;
0230 
0231       for (int i = 0; i < 120; i++)
0232       {
0233         int cniyellowbunchspin = hcnipatternYellow->GetBinContent((i+120-xingshift_cniYell)%120+1);
0234         int cnibluebunchspin = hcnipatternBlue->GetBinContent((i+120-xingshift_cniBlue)%120+1);
0235         if (cniyellowbunchspin == 0)
0236         {
0237           cniyellowbunchspin = 10;
0238         }
0239         if (cnibluebunchspin == 0)
0240         {
0241           cnibluebunchspin = 10;
0242         }
0243         if (cniyellowbunchspin != map_yellspin[runnumber].at(i) || cnibluebunchspin != map_bluespin[runnumber].at(i))
0244         {
0245           //badrunqa = 1;
0246           stringMarkdown += "\u274C CNI spin pattern match ";
0247           stringHtml += "Spin patterns: <font color=\"#ff0000\">BAD</font>; ";
0248           break;
0249         }
0250         else if (i == 119)
0251         {
0252           stringMarkdown += "\u2705 CNI spin pattern match ";
0253           stringHtml += "Spin patterns: <font color=\"#11cf17\">GOOD</font>; ";
0254         }
0255       }
0256     }
0257   }
0258   return;
0259 
0260 }
0261 
0262 
0263 void SpinDBQA::GL1pScalersQA(std::string &stringMarkdown, std::string &stringHtml)
0264 {
0265 
0266   if (map_crossingshift[runnumber] == -999)
0267   {
0268     stringMarkdown += "\u274C GL1p abort gaps ";
0269     stringHtml += "GL1p Scalers: <font color=\"#ff0000\">BAD</font>; ";
0270     return;
0271   }
0272 
0273   // these are 28x28, 6x6 fills where the crossingshift has been correctly determined at 0xFFFF level
0274   if (map_fillnumber[runnumber] ==  34897 || map_fillnumber[runnumber] ==  34886 || map_fillnumber[runnumber] ==  35020)
0275   {
0276     stringMarkdown += "\u2705 Crossing shift ";
0277     stringHtml += "GL1p Scalers: <font color=\"#11cf17\">GOOD</font>; ";
0278     return;
0279   }
0280 
0281   int sum_mbdns = 0; int sum_mbdvtx = 0; int sum_zdcns = 0;
0282   int min_sum_mbdns = std::numeric_limits<int>::max();
0283   int min_sum_mbdvtx = std::numeric_limits<int>::max();
0284   int min_sum_zdcns = std::numeric_limits<int>::max();
0285   int min_index_mbdns = -1; int min_index_mbdvtx = -1; int min_index_zdcns = -1;
0286 
0287   for (int i = 0; i < 120 + 9; ++i)
0288   {
0289     int ite = i % 120;
0290     sum_mbdns += map_mbdns[runnumber].at(ite);
0291     sum_mbdvtx += map_mbdvtx[runnumber].at(ite);
0292     sum_zdcns += map_zdcns[runnumber].at(ite);
0293 
0294     if (i >= 8) 
0295     {
0296       if (sum_mbdns < min_sum_mbdns) 
0297       {
0298         min_sum_mbdns = sum_mbdns;
0299         min_index_mbdns = (i - 8) % 120;
0300       }
0301       if (sum_mbdvtx < min_sum_mbdvtx) 
0302       {
0303         min_sum_mbdvtx = sum_mbdvtx;
0304         min_index_mbdvtx = (i - 8) % 120;
0305       }
0306       if (sum_zdcns < min_sum_zdcns) 
0307       {
0308         min_sum_zdcns = sum_zdcns;
0309         min_index_zdcns = (i - 8) % 120;
0310       }
0311       sum_mbdns -= map_mbdns[runnumber].at((i - 8) % 120);
0312       sum_mbdvtx -= map_mbdvtx[runnumber].at((i - 8) % 120);
0313       sum_zdcns -= map_zdcns[runnumber].at((i - 8) % 120);
0314     }
0315   }
0316   
0317   if (min_index_mbdns != min_index_mbdvtx || min_index_mbdns != min_index_zdcns)
0318   {
0319     stringMarkdown += "\u274C GL1p abort gaps ";
0320     stringHtml += "GL1p Scalers: <font color=\"#ff0000\">BAD</font>; ";
0321   }
0322   else
0323   {
0324     stringMarkdown += "\u2705 GL1p abort gaps ";
0325     int end_of_ag = (min_index_mbdns+9)%120;
0326     int xingshift_needed = (120-end_of_ag)%120;
0327     stringMarkdown += (map_crossingshift[runnumber] == xingshift_needed) ? "\u2705 Crossing shift " : "\u274C Crossing shift ";
0328     stringHtml += (map_crossingshift[runnumber] == xingshift_needed) ? "GL1p Scalers: <font color=\"#11cf17\">GOOD</font>; " : "GL1p Scalers: <font color=\"#ff0000\">BAD</font>;  ";
0329   } 
0330   return;
0331 }
0332 
0333 void SpinDBQA::LocalPolQA(std::string &stringMarkdown, std::string &stringHtml)
0334 {
0335   if (map_asymbf[runnumber] == 0 && map_asymbb[runnumber] == 0 && map_asymyf[runnumber] == 0 
0336         && map_asymyb[runnumber] == 0 && map_phasebf[runnumber] == 0 && map_phasebb[runnumber] == 0 
0337         && map_phaseyf[runnumber] == 0 && map_phaseyb[runnumber] == 0)
0338   {
0339     stringMarkdown += "\u274C Local polarimetry exists ";
0340     stringHtml += "Local Polarimetry: <font color=\"#800080\">NOT AVAILABLE</font>; ";
0341   }
0342   else
0343   {
0344     stringMarkdown += "\u2705 Local polarimetry exists ";
0345 
0346     locpolbf->SetPoint(locpolbf->GetN(),runnumber,map_asymbf[runnumber]);
0347     locpolbf->SetPointError(locpolbf->GetN()-1,0,map_asymerrbf[runnumber]);
0348     locpolbb->SetPoint(locpolbb->GetN(),runnumber,map_asymbb[runnumber]);
0349     locpolbb->SetPointError(locpolbb->GetN()-1,0,map_asymerrbb[runnumber]);
0350     locpolyf->SetPoint(locpolyf->GetN(),runnumber,map_asymyf[runnumber]);
0351     locpolyf->SetPointError(locpolyf->GetN()-1,0,map_asymerryf[runnumber]);
0352     locpolyb->SetPoint(locpolyb->GetN(),runnumber,map_asymyb[runnumber]);
0353     locpolyb->SetPointError(locpolyb->GetN()-1,0,map_asymerryb[runnumber]);
0354 
0355     locpolphasebf->SetPoint(locpolphasebf->GetN(),runnumber,map_phasebf[runnumber]);
0356     locpolphasebf->SetPointError(locpolphasebf->GetN()-1,0,map_phaseerrbf[runnumber]);
0357     locpolphasebb->SetPoint(locpolphasebb->GetN(),runnumber,map_phasebb[runnumber]);
0358     locpolphasebb->SetPointError(locpolphasebb->GetN()-1,0,map_phaseerrbb[runnumber]);
0359     locpolphaseyf->SetPoint(locpolphaseyf->GetN(),runnumber,map_phaseyf[runnumber]);
0360     locpolphaseyf->SetPointError(locpolphaseyf->GetN()-1,0,map_phaseerryf[runnumber]);
0361     locpolphaseyb->SetPoint(locpolphaseyb->GetN(),runnumber,map_phaseyb[runnumber]);
0362     locpolphaseyb->SetPointError(locpolphaseyb->GetN()-1,0,map_phaseerryb[runnumber]);
0363 
0364     // Does asymmetry magnitude fall out of mean +- RMSE from Run24?
0365     float bf_distancefromband = map_asymbf[runnumber] > bf_mean ? map_asymbf[runnumber]-(bf_mean+bf_rmse) : (bf_mean-bf_rmse)-map_asymbf[runnumber];
0366     float yf_distancefromband = map_asymyf[runnumber] > yf_mean ? map_asymyf[runnumber]-(yf_mean+yf_rmse) : (yf_mean-yf_rmse)-map_asymyf[runnumber];
0367 
0368     // Does asymmetry phase fall out of mean +- RMSE from Run24?
0369     float bf_phase_distancefromband = map_phasebf[runnumber] > bf_phase_mean ? map_phasebf[runnumber]-(bf_phase_mean+bf_phase_rmse) : (bf_phase_mean-bf_phase_rmse)-map_phasebf[runnumber];
0370     float yf_phase_distancefromband = map_phaseyf[runnumber] > yf_phase_mean ? map_phaseyf[runnumber]-(yf_phase_mean+yf_phase_rmse) : (yf_phase_mean-yf_phase_rmse)-map_phaseyf[runnumber];
0371 
0372     
0373     if (bf_distancefromband > map_asymerrbf[runnumber] || yf_distancefromband > map_asymerryf[runnumber]
0374          || bf_phase_distancefromband > map_phaseerrbf[runnumber] || yf_phase_distancefromband > map_phaseerryf[runnumber])
0375     {
0376       stringMarkdown += "\u274C Local Polarimetry ";
0377       stringHtml += "Local Polarimetry: <font color=\"#ff0000\">BAD</font>; ";
0378     }
0379     else
0380     {
0381       stringMarkdown += "\u2705 Local Polarimetry ";
0382       stringHtml += "Local Polarimetry: <font color=\"#11cf17\">GOOD</font>; ";
0383     }
0384   }
0385     // if CNI does not have beam polarizations (see if you can get polarizations from local pol)
0386 }
0387 
0388 void SpinDBQA::CrossingAngleQA(std::string &stringMarkdown, std::string &stringHtml) {
0389   // Run crossing angle QA. Simply check the standard deviation per run and cut on a hard threshold.
0390   // Currently set to cut three most egregious runs where beams switch from 1.5 mrad to/from head on.
0391 
0392   // Check if crossing angle std exists and if it is above the threshold mark it as bad, good otherwise
0393   if (map_crossanglestd[runnumber] > _crossanglestdthreshold)
0394   {
0395     stringMarkdown += "\u274C Crossing angle variation ";
0396     stringHtml += "Crossing angle variation: <font color=\"#ff0000\">BAD</font>; ";
0397   }
0398   else
0399   {
0400     stringMarkdown += "\u2705 Crossing angle variation ";
0401     stringHtml += "Crossing angle variation: <font color=\"#11cf17\">GOOD</font>; ";
0402   }
0403 }
0404 
0405 void SpinDBQA::WriteNewQALevel(int newqalevel)
0406 {
0407   std::cout << "Writing new QA level..." << std::endl;
0408   if (newqalevel < 0)
0409   {
0410     std::cout << "Please select a new qa level > 0 to write to!" << std::endl;
0411     return;
0412   }
0413 
0414 
0415   PrepareUpdatedBeamPolarizations();
0416   PrepareUpdatedCrossingShifts();
0417   PrepareUpdatedSpinPatterns();
0418 
0419   
0420   for (const auto &run : runlistvect)
0421   {
0422     int badrunqa = 0;
0423     runnumber = run;
0424     
0425     ////////////////////////////////////////////////////
0426     // Set new qa level
0427     // If no row for the run,qa_level create it.
0428     if(spin_in.CheckRunRow(runnumber,newqalevel)==0)
0429     {
0430       spin_in.CreateRunRow(runnumber,newqalevel);
0431       printf("\n\nCreated Row for run %d with qa level %d\n\n",runnumber,newqalevel);
0432     }
0433     spin_cont.SetRunNumber(runnumber);
0434     spin_cont.SetQALevel(newqalevel);
0435     ////////////////////////////////////////////////////
0436 
0437     ////////////////////////////////////////////////////
0438     // Set fill number
0439     spin_cont.SetFillNumber(map_fillnumber[runnumber]);
0440     ////////////////////////////////////////////////////
0441 
0442     ////////////////////////////////////////////////////
0443     // Set crossing shift
0444     if (map_crossingshift[runnumber] == -999)
0445     {
0446       badrunqa = 1;
0447     }
0448     spin_cont.SetCrossingShift(map_crossingshift[runnumber]);
0449     ////////////////////////////////////////////////////
0450 
0451     ////////////////////////////////////////////////////
0452     // *** "Bad" spin patterns reset to match CNI webpage *** //
0453     // Set spin patterns
0454     for (int i = 0; i < 120; i++)
0455     {
0456       spin_cont.SetSpinPatternBlue(i,map_bluespin[runnumber].at(i));
0457     }
0458     for (int i = 0; i < 120; i++)
0459     {
0460       spin_cont.SetSpinPatternYellow(i,map_yellspin[runnumber].at(i));
0461     }
0462     ////////////////////////////////////////////////////
0463     
0464     ////////////////////////////////////////////////////
0465     // *** Scraped from https://www.cnipol.bnl.gov/fills/ (although not final) *** //
0466     // Set beam polarizations
0467     if (map_bluepolbyfill.count(map_fillnumber[runnumber]) == 0 || map_yellpolbyfill.count(map_fillnumber[runnumber]) == 0)
0468     {
0469       badrunqa = 1;
0470       spin_cont.SetPolarizationBlue(0, -999., -999.);
0471       spin_cont.SetPolarizationYellow(0, -999., -999.);
0472     }
0473     else
0474     {
0475       spin_cont.SetPolarizationBlue(0, map_bluepolbyfill[map_fillnumber[runnumber]], map_bluepolbyfillerr[map_fillnumber[runnumber]]);
0476       spin_cont.SetPolarizationYellow(0, map_yellpolbyfill[map_fillnumber[runnumber]], map_yellpolbyfillerr[map_fillnumber[runnumber]]);
0477     }
0478     
0479     ////////////////////////////////////////////////////
0480 
0481     ////////////////////////////////////////////////////
0482     // Set MBD NS GL1p scalers
0483     for (int i = 0; i < 120; i++)
0484     {
0485       spin_cont.SetScalerMbdNoCut(i,map_mbdns[runnumber].at(i));
0486     }
0487     ////////////////////////////////////////////////////
0488 
0489     ///////////////////////////////////////////////////
0490     // Set MBD VTX GL1p scalers
0491     for (int i = 0; i < 120; i++)
0492     {
0493       spin_cont.SetScalerMbdVertexCut(i,map_mbdvtx[runnumber].at(i));
0494     }
0495     ////////////////////////////////////////////////////
0496 
0497     ////////////////////////////////////////////////////
0498     // Set ZDC NS GL1p scalers
0499     for (int i = 0; i < 120; i++)
0500     {
0501       spin_cont.SetScalerZdcNoCut(i,map_zdcns[runnumber].at(i));
0502     }
0503     ////////////////////////////////////////////////////
0504 
0505     ////////////////////////////////////////////////////
0506     // Set crossing angle
0507     if (map_crossanglestd[runnumber] > _crossanglestdthreshold)
0508     {
0509       badrunqa = 1;
0510     }
0511     spin_cont.SetCrossAngle(map_crossingangle[runnumber]);
0512     spin_cont.SetCrossAngleStd(map_crossanglestd[runnumber]);
0513     spin_cont.SetCrossAngleMin(map_crossanglemin[runnumber]);
0514     spin_cont.SetCrossAngleMax(map_crossanglemax[runnumber]);
0515     ////////////////////////////////////////////////////
0516 
0517     ////////////////////////////////////////////////////
0518     // Set local polarimetry stuff
0519     float bf_distancefromband = map_asymbf[runnumber] > bf_mean ? map_asymbf[runnumber]-(bf_mean+bf_rmse) : (bf_mean-bf_rmse)-map_asymbf[runnumber];
0520     float yf_distancefromband = map_asymyf[runnumber] > yf_mean ? map_asymyf[runnumber]-(yf_mean+yf_rmse) : (yf_mean-yf_rmse)-map_asymyf[runnumber];
0521     float bf_phase_distancefromband = map_phasebf[runnumber] > bf_phase_mean ? map_phasebf[runnumber]-(bf_phase_mean+bf_phase_rmse) : (bf_phase_mean-bf_phase_rmse)-map_phasebf[runnumber];
0522     float yf_phase_distancefromband = map_phaseyf[runnumber] > yf_phase_mean ? map_phaseyf[runnumber]-(yf_phase_mean+yf_phase_rmse) : (yf_phase_mean-yf_phase_rmse)-map_phaseyf[runnumber];
0523     
0524     // make sure that local polarimetry exists in the first place
0525     if (map_asymbf[runnumber] != 0 || map_asymbb[runnumber] != 0 || map_asymyf[runnumber] != 0 
0526         || map_asymyb[runnumber] != 0 || map_phasebf[runnumber] != 0 || map_phasebb[runnumber] != 0 
0527         || map_phaseyf[runnumber] != 0 || map_phaseyb[runnumber] != 0)
0528     {
0529       if (bf_distancefromband > map_asymerrbf[runnumber] || yf_distancefromband > map_asymerryf[runnumber]
0530          || bf_phase_distancefromband > map_phaseerrbf[runnumber] || yf_phase_distancefromband > map_phaseerryf[runnumber])
0531       {
0532         badrunqa = 1;
0533       }
0534     }
0535 
0536 
0537     
0538     spin_cont.SetAsymBlueForward(map_asymbf[runnumber], map_asymerrbf[runnumber]);
0539     spin_cont.SetAsymBlueBackward(map_asymbb[runnumber], map_asymerrbb[runnumber]);
0540     spin_cont.SetAsymYellowForward(map_asymyf[runnumber], map_asymerryf[runnumber]);
0541     spin_cont.SetAsymYellowBackward(map_asymyb[runnumber], map_asymerryb[runnumber]);
0542     spin_cont.SetPhaseBlueForward(map_phasebf[runnumber], map_phaseerrbf[runnumber]);
0543     spin_cont.SetPhaseBlueBackward(map_phasebb[runnumber], map_phaseerrbb[runnumber]);
0544     spin_cont.SetPhaseYellowForward(map_phaseyf[runnumber], map_phaseerryf[runnumber]);
0545     spin_cont.SetPhaseYellowBackward(map_phaseyb[runnumber], map_phaseerryb[runnumber]);
0546     ////////////////////////////////////////////////////
0547 
0548     ////////////////////////////////////////////////////
0549     // Set badrun flag
0550     spin_cont.SetBadRunFlag(badrunqa);
0551     ////////////////////////////////////////////////////
0552 
0553     // Update the database and set new default QA
0554     spin_in.UpdateDBContent(spin_cont);
0555     spin_in.SetDefaultQA(spin_cont);
0556     
0557     // ************************************ // 
0558     
0559   }
0560   
0561 }
0562 
0563 
0564 void SpinDBQA::PrepareUpdatedCrossingShifts()
0565 {
0566   std::ifstream shiftsFile("../updatedcrossingshifts.dat");
0567   if (!shiftsFile.is_open()) 
0568   {
0569     std::cout << "Error: Could not open updatedcrossingshifts.dat" << std::endl;
0570     return;
0571   }
0572 
0573   std::string line;
0574   while (getline(shiftsFile, line)) 
0575   {
0576     if (line.empty())
0577     {
0578       continue;
0579     } 
0580     std::istringstream iss(line);
0581     int RunNumber;
0582     int crossingshift;
0583 
0584     if (!(iss >> RunNumber >> crossingshift)) 
0585     {
0586       std::cout << "Skipping invalid line: " << line << std::endl;
0587       continue;
0588     }
0589     
0590     map_crossingshift[RunNumber] = crossingshift;
0591 
0592   }
0593   shiftsFile.close();
0594 }
0595 
0596 void SpinDBQA::PrepareUpdatedBeamPolarizations()
0597 {
0598   std::ifstream polarizationsFile("../updatedbeampolarizations.dat");
0599   if (!polarizationsFile.is_open()) 
0600   {
0601     std::cout << "Error: Could not open updatedbeampolarizations.dat" << std::endl;
0602     return;
0603   }
0604 
0605   std::string line;
0606   while (getline(polarizationsFile, line)) 
0607   {
0608     if (line.empty())
0609     {
0610       continue;
0611     } 
0612     std::istringstream iss(line);
0613     int FillNumber;
0614     float bluepol;
0615     float bluepolerr;
0616     float yellpol;
0617     float yellpolerr;
0618 
0619     if (!(iss >> FillNumber >> bluepol >> bluepolerr >> yellpol >> yellpolerr)) 
0620     {
0621       std::cout << "Skipping invalid line: " << line << std::endl;
0622       continue;
0623     }
0624     
0625     map_bluepolbyfill[FillNumber] = bluepol;
0626     map_bluepolbyfillerr[FillNumber] = bluepolerr;
0627     map_yellpolbyfill[FillNumber] = yellpol;
0628     map_yellpolbyfillerr[FillNumber] = yellpolerr;
0629 
0630   }
0631   polarizationsFile.close();
0632 }
0633 
0634 void SpinDBQA::PrepareUpdatedSpinPatterns()
0635 {
0636   std::ifstream patternsFile("../updatedspinpatterns.dat");
0637   if (!patternsFile.is_open()) 
0638   {
0639     std::cout << "Error: Could not open correctedspinpatterns.dat" << std::endl;
0640     return;
0641   }
0642 
0643   std::string line;
0644   while (getline(patternsFile, line)) 
0645   {
0646     if (line.empty())
0647     {
0648       continue;
0649     } 
0650     std::istringstream iss(line);
0651     int RunNumber;
0652     std::string s_pattern_blue;
0653     std::string s_pattern_yell;
0654 
0655     if (!(iss >> RunNumber >> s_pattern_blue >> s_pattern_yell)) 
0656     {
0657       std::cout << "Skipping invalid line: " << line << std::endl;
0658       continue;
0659     }
0660 
0661     for (int i = 0; i < 120; ++i)
0662     {
0663       if (s_pattern_blue.at(i) == '-')
0664       {
0665         map_bluespin[RunNumber].at(i) = -1;
0666       }
0667       else if (s_pattern_blue.at(i) == '+')
0668       {
0669         map_bluespin[RunNumber].at(i) = 1;
0670       }
0671       else if (s_pattern_blue.at(i) == 'o')
0672       {
0673         map_bluespin[RunNumber].at(i) = 10;
0674       }
0675 
0676       if (s_pattern_yell.at(i) == '-')
0677       {
0678         map_yellspin[RunNumber].at(i) = -1;
0679       }
0680       else if (s_pattern_yell.at(i) == '+')
0681       {
0682         map_yellspin[RunNumber].at(i) = 1;
0683       }
0684       else if (s_pattern_yell.at(i) == 'o')
0685       {
0686         map_yellspin[RunNumber].at(i) = 10;
0687       }
0688     }
0689   }
0690   patternsFile.close();
0691 }
0692 
0693 
0694 void SpinDBQA::WriteRootFile()
0695 {
0696   TFile *outfile = new TFile(_rootfilename.c_str(),"RECREATE");
0697   locpolbf->Write();
0698   locpolbb->Write();
0699   locpolyf->Write();
0700   locpolyb->Write();
0701   locpolphasebf->Write();
0702   locpolphasebb->Write();
0703   locpolphaseyf->Write();
0704   locpolphaseyb->Write();
0705   outfile->Write();
0706   outfile->Close();
0707 }
0708 
0709 void SpinDBQA::WriteMarkdown()
0710 {
0711   std::cout << "Writing to Markdown!" << std::endl;
0712   //============== Write markdown of spin db QA ============== //
0713   std::ofstream f_md(_markdownfilename);
0714   if (!f_md.is_open()) 
0715   {
0716     std::cerr << "Error: Could not open the file for writing!" << std::endl;
0717   }
0718   else
0719   {
0720     for (auto& [statements, runs] : map_spindbqa_markdown)
0721     {
0722       std::sort(runs.begin(), runs.end());
0723       f_md << "**" << statements << "**\n\n> ";
0724       for (int run : runs) {
0725         f_md << run << " ";
0726       }
0727       f_md << "\n\n";
0728     } 
0729   }
0730   f_md.close();
0731   return;
0732 }
0733 
0734 void SpinDBQA::WriteHtml()
0735 {
0736   //============== Write HTML of spin db QA ============== //
0737   std::cout << "Writing to HTML!" << std::endl;
0738   if (!std::filesystem::exists(_htmlfilename)) 
0739   {
0740     std::cerr << "HTML file does not exist! Preparing new HTML." << std::endl;
0741     PrepareHtml();
0742   }
0743 
0744   // read in html file to string
0745   std::ifstream f_html(_htmlfilename);
0746   if (!f_html.is_open()) 
0747   {
0748       std::cerr << "WriteHtml: Could not open HTML file!" << std::endl;
0749       return;
0750   }
0751 
0752   std::string s_html((std::istreambuf_iterator<char>(f_html)), std::istreambuf_iterator<char>());
0753   f_html.close();
0754 
0755   std::string tabButtonOld = R"REGEX(<button[^>]*class="tab-button active"[^>]*onclick="openTab\(event,\s*'default'\)"[^>]*>QA Level: Default</button>)REGEX";
0756   std::string tabButtonNew = R"BUTTON(<button class="tab-button active" onclick="openTab(event, 'default')">QA Level: Default</button>)BUTTON";
0757   std::string tabContentOld = R"CONTENT(<div\s+id="default"[^>]*>([\s\S]*?)</div>)CONTENT";
0758   std::string tabContentNew = HtmlContent();
0759 
0760   std::string placeholderButton = "<!-- TAB BUTTONS WILL GO HERE -->";
0761   std::string placeholderContent = "<!-- TAB CONTENT WILL GO HERE -->";
0762 
0763   if (!b_defaultQA) 
0764   {
0765     std::regex patternButtonOld(tabButtonOld);
0766     std::smatch matchResultButtonOld;
0767     std::regex patternContentOld(tabContentOld);
0768     std::smatch matchResultContentOld;
0769     if (std::regex_search(s_html, matchResultButtonOld, patternButtonOld)) 
0770     {
0771       placeholderButton = matchResultButtonOld[0].str();
0772     }
0773     if (std::regex_search(s_html, matchResultContentOld, patternContentOld))
0774     {
0775       placeholderContent = matchResultContentOld[0].str();
0776     }
0777 
0778     std::string s_qa = "qa" + std::to_string(qalevel);
0779     std::string s_title = "QA Level: " + std::to_string(qalevel);
0780     if (qalevel == 0xFFFF){
0781       std::stringstream ss;
0782       ss << std::hex << qalevel;
0783       s_title = "QA Level: 0x" + ss.str();
0784     }
0785     tabButtonNew  = std::regex_replace(tabButtonNew,  std::regex("default"), s_qa);
0786     tabButtonOld  = std::regex_replace(tabButtonOld,  std::regex("default"), s_qa);
0787     tabContentOld = std::regex_replace(tabContentOld, std::regex("default"), s_qa);
0788     tabButtonNew  = std::regex_replace(tabButtonNew,  std::regex("QA Level: Default"), s_title);
0789     tabButtonOld  = std::regex_replace(tabButtonOld,  std::regex("QA Level: Default"), s_title);
0790     tabButtonNew  = std::regex_replace(tabButtonNew,  std::regex("tab-button active"), "tab-button");
0791     tabButtonOld  = std::regex_replace(tabButtonOld,  std::regex("tab-button active"), "tab-button");
0792     tabContentNew  = std::regex_replace(tabContentNew,  std::regex("tab-content active"), "tab-content");
0793   }
0794 
0795   // write tab button
0796   std::regex defaultButtonRegex(tabButtonOld + R"(\s*)");
0797   s_html = std::regex_replace(s_html, defaultButtonRegex, "");
0798   size_t posButton = s_html.find(placeholderButton);
0799   if (posButton != std::string::npos) 
0800   {
0801     s_html.insert(posButton + placeholderButton.length(), "\n  " + tabButtonNew);
0802   }
0803 
0804   // write tab content
0805   std::regex defaultContentRegex(tabContentOld + R"(\s*)");
0806   s_html = std::regex_replace(s_html, defaultContentRegex, "");
0807   size_t posContent = s_html.find(placeholderContent);
0808   if (posContent != std::string::npos) 
0809   {
0810     s_html.insert(posContent + placeholderContent.length(), "\n  " + tabContentNew);
0811   }
0812 
0813   // write html file
0814   std::ofstream f_html_out(_htmlfilename);
0815   if (!f_html_out.is_open()) {
0816       std::cerr << "Error: Could not open the HTML file for writing!" << std::endl;
0817       return;
0818   }
0819 
0820   f_html_out << s_html;
0821   f_html_out.close();
0822 
0823   return;
0824 }
0825 
0826 std::string SpinDBQA::HtmlContent()
0827 {
0828   std::ostringstream stream_html;
0829 
0830   size_t totalRuns = 0;
0831   for (const auto &pair : map_spindbqa_html)
0832   {
0833     totalRuns += pair.second.size();
0834   }
0835   
0836   stream_html << R"(<div id="default" class="tab-content active">)" << "\n"
0837       << "  <table style=\"text-align: center;\">\n"
0838       << "    <tr>\n"
0839       << "      <th style=\"width: 1500px;\">Run Status</th>\n"
0840       << "      <th style=\"width: 50px;\">Number of Runs</th>\n"
0841       << "      <th style=\"width: 50px;\">Fraction</th>\n"
0842       << "    </tr>\n"
0843       << "    <tr>\n"
0844       << "      <td>" << "Total number of runs" << "</td>\n"
0845       << "      <td>" << totalRuns << "</td>\n"
0846       << "      <td>" << "-" << "</td>\n"
0847       << "    </tr>\n";
0848 
0849   for (auto& [statement, runs] : map_spindbqa_html)
0850   {
0851     size_t count = runs.size();
0852 
0853     double fraction = 0.0;
0854     if (totalRuns > 0) 
0855     {
0856         fraction = (static_cast<double>(count) / static_cast<double>(totalRuns)) * 100.0;
0857     }
0858 
0859     std::ostringstream fractionStr;
0860     fractionStr << std::fixed << std::setprecision(2) << fraction << "%";
0861 
0862     stream_html << "    <tr>\n"
0863           << "      <td>" << statement << "</td>\n"
0864           << "      <td>" << count << "</td>\n"
0865           << "      <td>" << fractionStr.str() << "</td>\n"
0866           << "    </tr>\n";
0867 
0868   }
0869   stream_html << "  </table>\n";
0870 
0871   for (auto& [statement, runs] : map_spindbqa_html)
0872     {
0873       std::sort(runs.begin(), runs.end());
0874       stream_html << "<strong>" << statement << "</strong><br>\n";
0875       for (int run : runs) {
0876         stream_html << run << " ";
0877       }
0878       stream_html << "<br><br>\n";
0879     } 
0880 
0881   stream_html  << "</div>";
0882   
0883   std::string s_stream_html = stream_html.str();
0884   std::string s_qa = "qa" + std::to_string(qalevel);
0885   if (!b_defaultQA)
0886   {
0887     s_stream_html  = std::regex_replace(s_stream_html,  std::regex("default"), s_qa);
0888     s_stream_html  = std::regex_replace(s_stream_html,  std::regex("tab-content active"), "tab-content");
0889   }
0890 
0891   return s_stream_html;
0892   
0893 }
0894 
0895 void SpinDBQA::PrepareHtml()
0896 {
0897   std::ofstream f_html(_htmlfilename);
0898   if (!f_html.is_open()) 
0899   {
0900     std::cerr << "PrepareHtml: Could not open HTML file!" << std::endl;
0901   }
0902   else
0903   {
0904     f_html << R"(<!DOCTYPE html>
0905     <html lang="en">
0906   <head>
0907   <meta charset="UTF-8">
0908   <meta name="viewport" content="width=device-width, initial-scale=1.0">
0909   <title>Spin QA Run Summary</title>
0910   <style>
0911     body {
0912       font-family: 'Arial', sans-serif;
0913       margin: 0;
0914       padding: 20px;
0915       background-color: #f4f7f9;
0916       color: #333;
0917     }
0918     h2 {
0919       text-align: center;
0920       font-size: 2em;
0921       color: #4D89C6;
0922       margin-bottom: 20px;
0923     }
0924 
0925     .top-right-image {
0926       position: absolute;
0927       top: 10px;
0928       right: 10px;
0929     }
0930 
0931     .tabs {
0932       display: flex;
0933       justify-content: center;
0934       margin-bottom: 20px;
0935     }
0936     .tab-button {
0937       background-color: #ddd;
0938       border: none;
0939       padding: 10px 20px;
0940       cursor: pointer;
0941       font-size: 1em;
0942       transition: background-color 0.3s ease;
0943     }
0944     .tab-button.active {
0945       background-color: #4D89C6;
0946       color: white;
0947     }
0948     .tab-button:hover {
0949       background-color: #bbb;
0950     }
0951     .tab-content {
0952       display: none;
0953       padding: 20px;
0954       background-color: white;
0955       box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
0956     }
0957     .tab-content.active {
0958       display: block;
0959     }
0960     .run-list {
0961       font-size: 1em;
0962       margin-bottom: 10px;
0963       padding: 10px;
0964       border: 1px solid #ddd;
0965       background-color: #f9f9f9;
0966     }
0967   </style>
0968   </head>
0969   <body>
0970 
0971   <img src="sphenix-logo-white-bg.png" width="200" alt="Logo" class="top-right-image">
0972 
0973   <h2>Spin QA Run Summary</h2>
0974 
0975   <!-- Placeholder for the tab buttons -->
0976   <div class="tabs">
0977     <!-- TAB BUTTONS WILL GO HERE -->
0978   </div>
0979 
0980   <!-- Placeholder for the tab content -->
0981   <style>
0982     table {
0983       border-collapse: collapse;
0984       text-align: center;
0985     }
0986     th, td {
0987       padding: 8px;
0988     }
0989     tr {
0990       border-bottom: 1px solid black;
0991     }
0992     </style>
0993   <!-- TAB CONTENT WILL GO HERE -->
0994 
0995   <script>
0996     function openTab(event, tabName) {
0997       var tabContent = document.getElementsByClassName("tab-content");
0998       for (var i = 0; i < tabContent.length; i++) {
0999         tabContent[i].style.display = "none";
1000         tabContent[i].classList.remove("active");
1001       }
1002       var tabButtons = document.getElementsByClassName("tab-button");
1003       for (var i = 0; i < tabButtons.length; i++) {
1004         tabButtons[i].classList.remove("active");
1005       }
1006       document.getElementById(tabName).style.display = "block";
1007       document.getElementById(tabName).classList.add("active");
1008       event.currentTarget.classList.add("active");
1009     }
1010   </script>
1011 
1012   </body>
1013   </html>)";
1014   
1015   }
1016   f_html.close();
1017   return;
1018 }