@@ -113,6 +113,18 @@ struct zdc2stagecalibration {
113113 // 4 sums of QxA,QyA,QxC,QyC. Feature 0 is unity, so its sum is N.
114114 static constexpr int kQRecenteringFlatnessNMoments = kQRecenteringMultidimNFeatures + kQRecenteringNComponents ;
115115
116+ // Structured regression diagnostic basis. The existing 35 cubic features are
117+ // kept unchanged and augmented with truncated-cubic 1D basis functions for
118+ // time, Vx, Vy and Vz. The additional basis is intentionally overcomplete so
119+ // that the final subset and regularisation can be chosen offline without
120+ // another derivation pass.
121+ static constexpr int kQRecenteringStructuredNKnots = 9 ;
122+ static constexpr int kQRecenteringStructuredNFeatures = kQRecenteringMultidimNFeatures + 4 * kQRecenteringStructuredNKnots ;
123+ static constexpr int kQRecenteringStructuredNMatrixMoments = kQRecenteringStructuredNFeatures * (kQRecenteringStructuredNFeatures + 1 ) / 2 ;
124+ static constexpr int kQRecenteringStructuredNMoments = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + kQRecenteringNComponents ;
125+ // Per conditioning bin store sum(features), sum(Q) and sum(Q^2).
126+ static constexpr int kQRecenteringStructuredFlatnessNMoments = kQRecenteringStructuredNFeatures + 2 * kQRecenteringNComponents ;
127+
116128 void init (o2::framework::InitContext&)
117129 {
118130 rctChecker.init (rctCut.cfgEvtRCTFlagCheckerLabel , rctCut.cfgEvtRCTFlagCheckerZDCCheck , rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad );
@@ -133,9 +145,11 @@ struct zdc2stagecalibration {
133145 AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments , 0.0 , static_cast <double >(kQRecenteringNMoments ), " regression moment" };
134146 AxisSpec qRecenteringMultidimMomentAxis = {kQRecenteringMultidimNMoments , 0.0 , static_cast <double >(kQRecenteringMultidimNMoments ), " multidimensional regression moment" };
135147 AxisSpec qRecenteringFlatnessMomentAxis = {kQRecenteringFlatnessNMoments , 0.0 , static_cast <double >(kQRecenteringFlatnessNMoments ), " flatness sufficient-statistic index" };
148+ AxisSpec qRecenteringStructuredMomentAxis = {kQRecenteringStructuredNMoments , 0.0 , static_cast <double >(kQRecenteringStructuredNMoments ), " structured regression moment" };
149+ AxisSpec qRecenteringStructuredFlatnessMomentAxis = {kQRecenteringStructuredFlatnessNMoments , 0.0 , static_cast <double >(kQRecenteringStructuredFlatnessNMoments ), " structured flatness sufficient-statistic index" };
136150 AxisSpec centralityAxis = {80 , 0.0 , 80.0 , " centrality (%)" };
137151 AxisSpec resolutionCentralityAxis = {50 , 0.0 , 50.0 , " centrality (%)" };
138- AxisSpec vertexXYAxis = {100 , -0.5 , 0.5 , " vertex x/y (cm)" };
152+ AxisSpec vertexXYAxis = {100 , -0.1 , 0.1 , " vertex x/y (cm)" };
139153 AxisSpec vertexZAxis = {100 , -10.0 , 10.0 , " vertex z (cm)" };
140154 AxisSpec qComponentAxis = {4 , 0.0 , 4.0 , " Q component" };
141155 AxisSpec qCorrelationComponentAxis = {4 , 0.0 , 4.0 , " A-C correlation component" };
@@ -213,6 +227,19 @@ struct zdc2stagecalibration {
213227 histos.add (" QRecenteringCalibration/hFlatnessMomentsVx" , " Flatness sufficient statistics vs v_{x};centrality (%);v_{x} (cm);statistic index" , kTH3D , {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis});
214228 histos.add (" QRecenteringCalibration/hFlatnessMomentsVy" , " Flatness sufficient statistics vs v_{y};centrality (%);v_{y} (cm);statistic index" , kTH3D , {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis});
215229 histos.add (" QRecenteringCalibration/hFlatnessMomentsVz" , " Flatness sufficient statistics vs v_{z};centrality (%);v_{z} (cm);statistic index" , kTH3D , {centralityAxis, vertexZAxis, qRecenteringFlatnessMomentAxis});
230+
231+ // Additional train/validation sufficient statistics for the structured
232+ // regression test. These do not modify the current 35-feature correction.
233+ histos.add (" QRecenteringCalibration/hRegressionMomentsStructuredTrain" , " Structured Q recentering moments (train);centrality (%);moment index" , kTH2D , {centralityAxis, qRecenteringStructuredMomentAxis});
234+ histos.add (" QRecenteringCalibration/hRegressionMomentsStructuredValidation" , " Structured Q recentering moments (validation);centrality (%);moment index" , kTH2D , {centralityAxis, qRecenteringStructuredMomentAxis});
235+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain" , " Structured flatness statistics vs time (train);centrality (%);time from SOR (h);statistic index" , kTH3D , {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis});
236+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain" , " Structured flatness statistics vs v_{x} (train);centrality (%);v_{x} (cm);statistic index" , kTH3D , {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis});
237+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain" , " Structured flatness statistics vs v_{y} (train);centrality (%);v_{y} (cm);statistic index" , kTH3D , {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis});
238+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain" , " Structured flatness statistics vs v_{z} (train);centrality (%);v_{z} (cm);statistic index" , kTH3D , {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis});
239+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation" , " Structured flatness statistics vs time (validation);centrality (%);time from SOR (h);statistic index" , kTH3D , {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis});
240+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation" , " Structured flatness statistics vs v_{x} (validation);centrality (%);v_{x} (cm);statistic index" , kTH3D , {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis});
241+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation" , " Structured flatness statistics vs v_{y} (validation);centrality (%);v_{y} (cm);statistic index" , kTH3D , {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis});
242+ histos.add (" QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation" , " Structured flatness statistics vs v_{z} (validation);centrality (%);v_{z} (cm);statistic index" , kTH3D , {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis});
216243 }
217244
218245 histos.add (" QRecenteringQA/pQBeforeVsCentrality" , " Q before recentering vs centrality;centrality (%);Q component;<Q>" , kTProfile2D , {centralityAxis, qComponentAxis});
@@ -374,6 +401,42 @@ struct zdc2stagecalibration {
374401 t * x * y, t * x * z, t * y * z, x * y * z};
375402 }
376403
404+ std::array<double , kQRecenteringStructuredNFeatures > makeQRecenteringStructuredFeatures (float timeFromSOR, float vx, float vy, float vz) const
405+ {
406+ const auto baseFeatures = makeQRecenteringMultidimFeatures (timeFromSOR, vx, vy, vz);
407+ std::array<double , kQRecenteringStructuredNFeatures > features{};
408+
409+ for (int i = 0 ; i < kQRecenteringMultidimNFeatures ; ++i) {
410+ features[i] = baseFeatures[i];
411+ }
412+
413+ const double runHours = (eorTimestamp > sorTimestamp) ? static_cast <double >(eorTimestamp - sorTimestamp) * 1 .e -3 / 3600.0 : static_cast <double >(cfgMaxRunHours.value );
414+ const double t = (runHours > 0.0 ) ? (2.0 * static_cast <double >(timeFromSOR) / runHours - 1.0 ) : 0.0 ;
415+ const double x = static_cast <double >(vx) / 0.1 ;
416+ const double y = static_cast <double >(vy) / 0.1 ;
417+ const double z = static_cast <double >(vz) / 10.0 ;
418+
419+ // Internal knots in the normalized [-1,1] coordinate. Together with the
420+ // existing 1,u,u^2,u^3 terms this is a cubic regression-spline basis.
421+ constexpr std::array<double , kQRecenteringStructuredNKnots > knots = {
422+ -0.8 , -0.6 , -0.4 , -0.2 , 0.0 , 0.2 , 0.4 , 0.6 , 0.8 };
423+
424+ int index = kQRecenteringMultidimNFeatures ;
425+ auto appendSplineTerms = [&](double value) {
426+ for (int i = 0 ; i < kQRecenteringStructuredNKnots ; ++i) {
427+ const double delta = value - knots[i];
428+ features[index++] = delta > 0.0 ? delta * delta * delta : 0.0 ;
429+ }
430+ };
431+
432+ appendSplineTerms (t);
433+ appendSplineTerms (x);
434+ appendSplineTerms (y);
435+ appendSplineTerms (z);
436+
437+ return features;
438+ }
439+
377440 using MyCollisions = o2::soa::Join<o2::aod::Collisions, o2::aod::EvSels, o2::aod::Mults, o2::aod::FT0sCorrected, o2::aod::CentFT0Cs>;
378441
379442 void process (MyCollisions::iterator const & collision, o2::aod::FT0s const & /* ft0s*/ , o2::aod::FV0As const & /* fv0s*/ , BCsRun3 const & /* bcs*/ , o2::aod::Zdcs const & /* zdcs*/ )
@@ -769,6 +832,91 @@ struct zdc2stagecalibration {
769832 histos.fill (HIST (" QRecenteringCalibration/hFlatnessMomentsVy" ), centrality, vy, index + 0.5 , value);
770833 histos.fill (HIST (" QRecenteringCalibration/hFlatnessMomentsVz" ), centrality, vz, index + 0.5 , value);
771834 }
835+
836+ // Structured regression moments. Events are deterministically split
837+ // 50/50 using a mixed global-BC key, avoiding any dependence on the
838+ // regression variables themselves.
839+ const auto structuredFeatures = makeQRecenteringStructuredFeatures (timeFromSOR, vx, vy, vz);
840+ uint64_t splitKey = globalBC + 0x9e3779b97f4a7c15ULL ;
841+ splitKey = (splitKey ^ (splitKey >> 30 )) * 0xbf58476d1ce4e5b9ULL ;
842+ splitKey = (splitKey ^ (splitKey >> 27 )) * 0x94d049bb133111ebULL ;
843+ splitKey = splitKey ^ (splitKey >> 31 );
844+ const bool isValidation = (splitKey & 1ULL ) != 0ULL ;
845+
846+ int structuredMoment = 0 ;
847+ for (int i = 0 ; i < kQRecenteringStructuredNFeatures ; ++i) {
848+ for (int j = i; j < kQRecenteringStructuredNFeatures ; ++j) {
849+ const double value = structuredFeatures[i] * structuredFeatures[j];
850+ if (isValidation) {
851+ histos.fill (HIST (" QRecenteringCalibration/hRegressionMomentsStructuredValidation" ), centrality, structuredMoment + 0.5 , value);
852+ } else {
853+ histos.fill (HIST (" QRecenteringCalibration/hRegressionMomentsStructuredTrain" ), centrality, structuredMoment + 0.5 , value);
854+ }
855+ ++structuredMoment;
856+ }
857+ }
858+
859+ for (int component = 0 ; component < kQRecenteringNComponents ; ++component) {
860+ for (int i = 0 ; i < kQRecenteringStructuredNFeatures ; ++i) {
861+ const int index = kQRecenteringStructuredNMatrixMoments + component * kQRecenteringStructuredNFeatures + i;
862+ const double value = structuredFeatures[i] * qValues[component];
863+ if (isValidation) {
864+ histos.fill (HIST (" QRecenteringCalibration/hRegressionMomentsStructuredValidation" ), centrality, index + 0.5 , value);
865+ } else {
866+ histos.fill (HIST (" QRecenteringCalibration/hRegressionMomentsStructuredTrain" ), centrality, index + 0.5 , value);
867+ }
868+ }
869+
870+ const int q2Index = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + component;
871+ const double q2Value = qValues[component] * qValues[component];
872+ if (isValidation) {
873+ histos.fill (HIST (" QRecenteringCalibration/hRegressionMomentsStructuredValidation" ), centrality, q2Index + 0.5 , q2Value);
874+ } else {
875+ histos.fill (HIST (" QRecenteringCalibration/hRegressionMomentsStructuredTrain" ), centrality, q2Index + 0.5 , q2Value);
876+ }
877+ }
878+
879+ for (int i = 0 ; i < kQRecenteringStructuredNFeatures ; ++i) {
880+ const double value = structuredFeatures[i];
881+ if (isValidation) {
882+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation" ), centrality, timeFromSOR, i + 0.5 , value);
883+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation" ), centrality, vx, i + 0.5 , value);
884+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation" ), centrality, vy, i + 0.5 , value);
885+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation" ), centrality, vz, i + 0.5 , value);
886+ } else {
887+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain" ), centrality, timeFromSOR, i + 0.5 , value);
888+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain" ), centrality, vx, i + 0.5 , value);
889+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain" ), centrality, vy, i + 0.5 , value);
890+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain" ), centrality, vz, i + 0.5 , value);
891+ }
892+ }
893+
894+ for (int component = 0 ; component < kQRecenteringNComponents ; ++component) {
895+ const int qIndex = kQRecenteringStructuredNFeatures + component;
896+ const int q2Index = kQRecenteringStructuredNFeatures + kQRecenteringNComponents + component;
897+ const double qValue = qValues[component];
898+ const double q2Value = qValue * qValue;
899+
900+ if (isValidation) {
901+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation" ), centrality, timeFromSOR, qIndex + 0.5 , qValue);
902+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation" ), centrality, vx, qIndex + 0.5 , qValue);
903+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation" ), centrality, vy, qIndex + 0.5 , qValue);
904+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation" ), centrality, vz, qIndex + 0.5 , qValue);
905+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation" ), centrality, timeFromSOR, q2Index + 0.5 , q2Value);
906+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation" ), centrality, vx, q2Index + 0.5 , q2Value);
907+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation" ), centrality, vy, q2Index + 0.5 , q2Value);
908+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation" ), centrality, vz, q2Index + 0.5 , q2Value);
909+ } else {
910+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain" ), centrality, timeFromSOR, qIndex + 0.5 , qValue);
911+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain" ), centrality, vx, qIndex + 0.5 , qValue);
912+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain" ), centrality, vy, qIndex + 0.5 , qValue);
913+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain" ), centrality, vz, qIndex + 0.5 , qValue);
914+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain" ), centrality, timeFromSOR, q2Index + 0.5 , q2Value);
915+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain" ), centrality, vx, q2Index + 0.5 , q2Value);
916+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain" ), centrality, vy, q2Index + 0.5 , q2Value);
917+ histos.fill (HIST (" QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain" ), centrality, vz, q2Index + 0.5 , q2Value);
918+ }
919+ }
772920 }
773921 }
774922
0 commit comments