Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-10-15 08:17:25

0001 #ifndef CROSSINGCORRECT_H
0002 #define CROSSINGCORRECT_H
0003 //
0004 // Crossing correction for pt-ordered (dy,dphi) maps (README_Crossing.md, sec 8).
0005 // Self-contained (ROOT TH2D only) so corral (CalcRm::Calculate) and Finalize use the
0006 // identical code. Copy or include this file verbatim.
0007 //
0008 // Requirements on the fill (Increment/PairInfo): pt-ordering flips dphi ONLY (no dy flip).
0009 // Then the dphi-symmetrized map is the same with and without pt-ordering, and it is the
0010 // only symmetrization pt-ordering requires. dy is never touched here.
0011 //
0012 #include <iostream>
0013 #include <cmath>
0014 #include <cstdlib>
0015 #include "TH2D.h"
0016 
0017 //------------------------------------------------------------
0018 // Which dphi side of the pt-ordered map holds the crossing loss:
0019 // +1 = dphi>0, -1 = dphi<0, 0 = no crossing correction (a neutral leg).
0020 // PairInfo's pt-ordering already folds in the charge order (pi+pi- is flipped into
0021 // pi-pi+ orientation, LS pairs are ordered by pt), so for any charged-charged pair the
0022 // dirty side depends only on the field sign. The dphi sign convention of the ordering is
0023 // chosen (2026-09-25, as in STAR) so the damage lands just BELOW dphi=0 at field>0 and
0024 // 0..180 is untouched. Empirical anchor: field=+1.4 -> LS and ULS damage on dphi<0
0025 // (it was on dphi>0 with the first, complementary conditions; README_Crossing sec 8).
0026 inline int CrossingDirtySide(double field, double chg1, double chg2){
0027     if (chg1==0. || chg2==0.) return  0;
0028     if (field==0.){ std::cout<<"CrossingDirtySide .. field is zero or not set! exit...."<<std::endl; exit(0); }
0029     return (field>0.) ? -1 : +1;
0030 }
0031 
0032 //------------------------------------------------------------
0033 // In place. x axis = dy (untouched), y axis = dphi in degrees, covering 360 deg with
0034 // DPHINB%4==0 so that dphi=0 and +-90 are bin edges and every bin has a distinct mirror.
0035 // Each dphi bin is paired with its mirror (dphi -> -dphi, mod 360; away side 180+x <-> 180-x),
0036 // at the same dy.
0037 //   near side (|dphi|<90), dirtySide=+-1: the dirty bin takes the clean bin's content AND error
0038 //        (the two are then 100% correlated copies); the clean bin is unchanged.
0039 //   away side (|dphi|>90): both bins get the mean, error sqrt(e1^2+e2^2)/2.
0040 //   dirtySide=0 (a neutral leg, never pt-ordered): NOTHING is done -- we don't symmetrize what we
0041 //        did not pt-order (user, 2026-09-25; README_Crossing sec 0).
0042 // Valid bin <=> denominator hden (rho2(M) of the same slice, same binning) > 0. A bin with an
0043 // empty denominator is invalid (its CF is undefined); rho2(S)=0 over a filled denominator is a
0044 // VALID zero. Invalid bins are not averaged: if one partner is invalid both get the valid one;
0045 // if both are invalid both keep their sentinels. On the near side the dirty bin copies the clean
0046 // bin even if that is invalid (sentinel and all).
0047 // The validity of the CORRECTED map follows the same rules; get it by correcting a 0/1 mask of
0048 // hden>0 with itself as hden: CrossingCorrect(mask, dirtySide, mask).
0049 inline void CrossingCorrect(TH2D* h, int dirtySide, const TH2D* hden){
0050     if (!h || !hden){ std::cout<<"CrossingCorrect .. null histogram! exit...."<<std::endl; exit(0); }
0051     if (hden->GetNbinsX()!=h->GetNbinsX() || hden->GetNbinsY()!=h->GetNbinsY()){
0052         std::cout<<"CrossingCorrect .. "<<h->GetName()<<": denominator binning differs. exit...."<<std::endl; exit(0);
0053     }
0054     TAxis* ax   = h->GetYaxis();
0055     int    nphi = ax->GetNbins();
0056     double lo   = ax->GetXmin();
0057     double w    = ax->GetBinWidth(1);
0058     if (nphi%4!=0 || std::fabs(ax->GetXmax()-lo-360.)>1e-6 || ax->GetXbins()->GetSize()>0){
0059         std::cout<<"CrossingCorrect .. "<<h->GetName()<<": need uniform dphi binning over 360 deg with nbins%4==0, have "
0060             <<nphi<<" bins ["<<lo<<","<<ax->GetXmax()<<"). exit...."<<std::endl; exit(0);
0061     }
0062     if (dirtySide<-1 || dirtySide>1){
0063         std::cout<<"CrossingCorrect .. dirtySide must be -1, 0 or +1, have "<<dirtySide<<". exit...."<<std::endl; exit(0);
0064     }
0065     if (dirtySide==0) return;   // not pt-ordered -> not symmetrized
0066     for (int iy=1;iy<=nphi;iy++){
0067         double c    = ax->GetBinCenter(iy);
0068         double cw   = c;                            // center wrapped to [-180,180)
0069         while (cw>= 180.) cw -= 360.;
0070         while (cw< -180.) cw += 360.;
0071         double cm   = -cw;                          // mirror center, wrapped back into [lo,lo+360)
0072         while (cm<  lo      ) cm += 360.;
0073         while (cm>= lo+360. ) cm -= 360.;
0074         int jy      = ax->FindFixBin(cm);
0075         if (jy<1 || jy>nphi || std::fabs(ax->GetBinCenter(jy)-cm)>1e-6*w || jy==iy){
0076             std::cout<<"CrossingCorrect .. "<<h->GetName()<<": dphi bin "<<iy<<" has no distinct mirror bin. exit...."<<std::endl; exit(0);
0077         }
0078         if (jy<iy) continue;                        // each pair once; read both before writing
0079         bool nearSide   = std::fabs(cw)<90.;
0080         for (int ix=1;ix<=h->GetNbinsX();ix++){
0081             double vi = h->GetBinContent(ix,iy), ei = h->GetBinError(ix,iy);
0082             double vj = h->GetBinContent(ix,jy), ej = h->GetBinError(ix,jy);
0083             bool   oki = hden->GetBinContent(ix,iy)>0., okj = hden->GetBinContent(ix,jy)>0.;
0084             if (nearSide){
0085                 bool iDirty = (cw*dirtySide>0.);    // bin iy is on the dirty side, jy is its clean mirror
0086                 if (iDirty){ h->SetBinContent(ix,iy,vj); h->SetBinError(ix,iy,ej); }
0087                 else       { h->SetBinContent(ix,jy,vi); h->SetBinError(ix,jy,ei); }
0088             } else {
0089                 double v,e;
0090                 if      (oki && okj){ v = 0.5*(vi+vj); e = 0.5*std::sqrt(ei*ei+ej*ej); }
0091                 else if (oki)       { v = vi;         e = ei; }
0092                 else if (okj)       { v = vj;         e = ej; }
0093                 else                { continue; }   // both invalid: keep sentinels
0094                 h->SetBinContent(ix,iy,v); h->SetBinError(ix,iy,e);
0095                 h->SetBinContent(ix,jy,v); h->SetBinError(ix,jy,e);
0096             }
0097         }
0098     }
0099 }
0100 
0101 //------------------------------------------------------------
0102 // Check: number of in-range bins whose content or error differs from its dphi mirror at the
0103 // same dy (0 for any map that has been through CrossingCorrect, and for sums/averages of such maps
0104 // up to rounding; tol is relative). Assumes the binning CrossingCorrect accepted.
0105 inline int CrossingAsymmetry(TH2D* h, double tol=1e-9){
0106     TAxis* ax   = h->GetYaxis();
0107     int    nphi = ax->GetNbins();
0108     double lo   = ax->GetXmin();
0109     int    nasym= 0;
0110     for (int iy=1;iy<=nphi;iy++){
0111         double cm   = -ax->GetBinCenter(iy);
0112         while (cm<  lo      ) cm += 360.;
0113         while (cm>= lo+360. ) cm -= 360.;
0114         int jy      = ax->FindFixBin(cm);
0115         for (int ix=1;ix<=h->GetNbinsX();ix++){
0116             double vi = h->GetBinContent(ix,iy), vj = h->GetBinContent(ix,jy);
0117             double ei = h->GetBinError(ix,iy),   ej = h->GetBinError(ix,jy);
0118             if (std::fabs(vi-vj)>tol*(std::fabs(vi)+std::fabs(vj)) || std::fabs(ei-ej)>tol*(ei+ej)) ++nasym;
0119         }
0120     }
0121     return nasym;
0122 }
0123 
0124 #endif