Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:18:13

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