10 #include "dqm_core/AlgorithmManager.h"
11 #include "dqm_core/exceptions.h"
12 #include "dqm_core/AlgorithmConfig.h"
13 #include "dqm_core/Result.h"
41 if (!
object.
IsA()->InheritsFrom(
"TH1")) {
42 throw dqm_core::BadConfig(ERS_HERE,
name,
"does not inherit from TH1");
44 std::unique_ptr<TH1>
histogram(
static_cast<TH1 *
>(
object.Clone()));
46 throw dqm_core::BadConfig(ERS_HERE,
name,
"dimension > 2");
48 const bool isOneDimensional = (
histogram->GetDimension() == 1);
62 if (checkZeroContent && (
histogram->Integral() == 0) && (
histogram->GetEntries() > 0)) {
63 ERS_DEBUG(1,
"Histogram " <<
histogram->GetName() <<
" is filled with zeros!");
77 const bool dontCountSigmaOutliers = checkSigmaDev ?
static_cast<bool>(
tools::GetFirstFromMap(
"DontCountSigmaOutliers",
config.getParameters(), 0)) :
false;
97 if (diffRef || normRef) {
98 if (diffRef && normRef) {
99 throw dqm_core::BadConfig(ERS_HERE,
name,
"Can either divide or subtract reference, not both");
102 refhist =
static_cast<TH1 *
>(
config.getReference());
103 }
catch(dqm_core::Exception &ex) {
104 throw dqm_core::BadRefHist(ERS_HERE,
name,
"Could not retrieve reference");
106 if (
histogram->GetDimension() != refhist->GetDimension()) {
107 throw dqm_core::BadRefHist(ERS_HERE,
name,
"Wrong dimension of reference");
109 if ((
histogram->GetNbinsX() != refhist->GetNbinsX()) || (
histogram->GetNbinsY() != refhist->GetNbinsY())) {
110 throw dqm_core::BadRefHist(ERS_HERE,
name,
"Non-matching number of bins of reference");
115 refhist->Scale(
histogram->Integral() / refhist->Integral());
117 if (diffRef)
histogram->Add(refhist, -1.0);
122 const int xminBin =
range[0];
123 const int xmaxBin =
range[1];
124 const int yminBin =
range[2];
125 const int ymaxBin =
range[3];
128 for (
int i = xminBin;
i <= xmaxBin; ++
i) {
129 for (
int j = yminBin; j <= ymaxBin; ++j) {
130 if (
histogram->GetBinContent(
i, j) == 0) ++zeroBins;
136 int totalBadBins = 0;
137 int totalUncountedBadBins = 0;
140 int currentIteration = 0;
141 const int maxIterations = 1000;
143 const int nBinsX =
histogram->GetNbinsX();
144 const int nBinsY =
histogram->GetNbinsY();
145 std::unique_ptr<TH1> knownOutliers(isOneDimensional ?
146 static_cast<TH1 *
>(
new TH1C(
"knownOutliers",
"knownOutliers", nBinsX, 0, nBinsX)) :
147 static_cast<TH1 *
>(
new TH2C(
"knownOutliers",
"knownOutliers", nBinsX, 0, nBinsX, nBinsY, 0, nBinsY)));
149 while (++currentIteration < maxIterations) {
151 int newUncountedBadBins = 0;
157 for (
int i = xminBin;
i <= xmaxBin; ++
i) {
158 for (
int j = yminBin; j <= ymaxBin; ++j) {
159 const bool isOutlier = knownOutliers->GetBinContent(
i, j);
160 if (isOutlier)
continue;
161 const double binContent =
histogram->GetBinContent(
i, j);
162 if ((binContent == 0) && ignore0)
continue;
166 sumsqr += binContent * binContent;
174 if (checkRelDev || checkAbsDev || checkAbsLimit || checkSigmaDev) {
175 for (
int i = xminBin;
i <= xmaxBin; ++
i) {
176 for (
int j = yminBin; j <= ymaxBin; ++j) {
177 const bool isOutlier = knownOutliers->GetBinContent(
i, j);
178 if (isOutlier)
continue;
179 const double binContent =
histogram->GetBinContent(
i, j);
180 const double binError = subtractBinError ?
histogram->GetBinError(
i, j) : 0.;
181 if ((binContent == 0) && ignore0)
continue;
183 bool foundOutlier =
false;
184 if (checkAbsLimit && !foundOutlier) {
185 if (binContent - binError > absLimit) {
190 if (checkAbsDev && !foundOutlier) {
191 if (std::fabs(binContent -
mean) - binError > absDev) {
196 if (checkRelDev && !foundOutlier) {
197 if (std::fabs(binContent -
mean) - binError > (relDev *
mean)) {
202 if (checkSigmaDev && !foundOutlier) {
203 if (std::fabs(binContent -
mean) - binError > (sigmaDev * stddev)) {
204 if (dontCountSigmaOutliers) ++newUncountedBadBins;
212 knownOutliers->SetBinContent(
i, j,
true);
217 if ((newBadBins == 0) && (newUncountedBadBins == 0))
break;
218 totalUncountedBadBins += newUncountedBadBins;
219 totalBadBins += newBadBins;
221 if (currentIteration == maxIterations) {
222 throw dqm_core::BadConfig(ERS_HERE,
name,
"maximum number of iterations reached while searching for outliers");
224 if (totalUncountedBadBins > 0) {
225 ERS_DEBUG(1,
"Histogram " <<
histogram->GetName() <<
" has " << totalUncountedBadBins <<
" bins exceeding sigma limit which are NOT counted as outliers, but which are omitted for calculating mean and stddev");
229 std::map<std::string, double>
results;
230 results[
"Number_of_outlier_bins"] = totalBadBins;
232 results[
"Corrected_standard_deviation"] = stddev;
233 results[
"Number_of_bins_equal_zero"] = zeroBins;
236 if (storeOutlierBins) {
237 int outlierIndex = 0;
238 for (
int i = xminBin;
i <= xmaxBin; ++
i) {
239 for (
int j = yminBin; j <= ymaxBin; ++j) {
240 if (knownOutliers->GetBinContent(
i, j)) {
242 results[std::string(
"Outlier_bin_")+
std::to_string(outlierIndex)] = knownOutliers->GetXaxis()->GetBinCenter(
i);
249 if (!isOneDimensional) {
250 throw dqm_core::BadConfig(ERS_HERE,
name,
"cannot check 2D histograms for flatness, please set CheckFlatness = 0");
252 std::unique_ptr<TF1> occupancyFit{};
254 const double xminAxis =
histogram->GetXaxis()->GetBinLowEdge(xminBin);
255 const double xmaxAxis =
histogram->GetXaxis()->GetBinUpEdge(xmaxBin);
256 const double width = (xmaxAxis - xminAxis);
257 const double center = (xmaxAxis + xminAxis) / 2;
260 occupancyFit = std::make_unique<TF1>(
"occupancyFit",
"[0]+[1]*sin([3]*(x-[4]-[5]))+[2]*cos(2*[3]*(x-[4]-[5]))");
261 occupancyFit->SetParName(0,
"constant");
262 occupancyFit->SetParName(1,
"asym");
263 occupancyFit->SetParName(2,
"sym");
264 occupancyFit->SetParName(3,
"scale");
265 occupancyFit->SetParName(4,
"offset");
266 occupancyFit->SetParName(5,
"phaseoffset");
267 occupancyFit->SetParLimits(5, -0.5 *
width, +0.5 *
width);
268 occupancyFit->FixParameter(3, 2 *
M_PI /
width);
269 occupancyFit->FixParameter(4, center -
width / 2);
271 occupancyFit = std::make_unique<TF1>(
"occupancyFit",
"[0]+[1]*(x-[3])+[2]*(x-[3])*(x-[3])");
272 occupancyFit->SetParName(0,
"constant");
273 occupancyFit->SetParName(1,
"asym");
274 occupancyFit->SetParName(2,
"sym");
275 occupancyFit->SetParName(3,
"offset");
277 occupancyFit->FixParameter(3, center);
280 occupancyFit->SetRange(xminAxis, xmaxAxis);
281 histogram->Fit(occupancyFit.get(),
"QNR");
284 double phaseOffset = 0;
287 phaseOffset = occupancyFit->GetParameter(
"phaseoffset");
288 maxBin = center +
width / 4 + phaseOffset;
291 maxBin = center +
width / 2;
294 const double constant = occupancyFit->GetParameter(
"constant");
300 asym = occupancyFit->GetParameter(
"asym");
302 const double parBuff = occupancyFit->GetParameter(
"sym");
303 occupancyFit->SetParameter(
"sym", 0);
304 asym = std::fabs(occupancyFit->Eval(maxBin) -
constant);
305 occupancyFit->SetParameter(
"sym", parBuff);
310 sym = occupancyFit->GetParameter(
"sym");
312 const double parBuff = occupancyFit->GetParameter(
"asym");
313 occupancyFit->SetParameter(
"asym", 0);
314 sym = std::fabs(occupancyFit->Eval(maxBin) -
constant);
315 occupancyFit->SetParameter(
"asym", parBuff);
322 const int ndf = occupancyFit->GetNDF();
323 const double chisquare = occupancyFit->GetChisquare();
324 const double chisquareNDF = (
ndf > 0) ? (chisquare /
ndf) : 0;
327 results[
"Max_rel_sym_deviation"] = sym;
328 results[
"Max_rel_asym_deviation"] = asym;
329 results[
"Chisquare_ndf"] = chisquareNDF;
330 results[
"Phase_offset"] = phaseOffset;
340 out <<
m_name <<
": Checks TH1-inherited histograms for bins which lie either Nsigma or AbsDev away from the mean (by default excludes bins with zero entries) or which exceed a given limit and removes them from the distribution.\n"
341 "Remaining (corrected for outliers) distribution is fitted with either a quadratic or a sinusoidal function (option FitCircular) and symmetric and asymmetric deviations from a flat distribution are computed by evaluating the quadratic and linear fit contributions respectively.\n"
343 "\tFitCircular:\tFit sinoidal function instead of quadratic. This is usefull for distributions expected to show circular behaviour (e.g. phi distributions) (default 0).\n"
344 "\tCheckFlatness:\tThis switch can be used to disable the flatness test and just check for outliers (default 1).\n"
345 "\tCheckAbsDev:\tCheck for absolute deviation from mean (default 0).\n"
346 "\tCheckRelDev:\tCheck for relative deviation from mean (default 0).\n"
347 "\tCheckAbsLimit:\tCheck for values exceeding given absolute limit (default 0).\n"
348 "\tCheckSigmaDev:\tCheck for deviation in units of standard deviations from mean (default 1).\n"
349 "\tDontCountSigmaDev:\tBins deviating by a given number of standard deviations are removed from the distribution for mean and flatness computation, but not counted as outliers (default 0).\n"
350 "\tCheckZeroContent:\tIf set to 1 the algorithm will check whether the histogram is filled with only zeros and return red in this case. WARNING this uses the TH1::Integral function which seems to be problematic in Lightweigth Histograms. (default 1).\n"
351 "\tMinStat:\tMinimum Statistics for the histogram to be checked (default 1).\n"
352 "\txmin and/or xmax:\tRestrict all counting and fitting to this x-axis interval (default: full axis range).\n"
353 "\tIgnore0:\tBins with 0 content are ignored for outlier computation (ignored for flatness-test anyway) (default 1).\n"
354 "\tSigmaDev:\tNumber of Standard Deviations a single bin has to differ from the mean of the distribution to be classified as outlier. Has to be given if \"CheckSigmaDev\" is set to 1. (default 5)\n"
355 "\tAbsDev:\tAbsolute value a single bin has to differ from the mean of the distribution to be classified as outlier. Has to be given if \"CheckAbsDev\" is set to 1.\n"
356 "\tRelDev:\tFraction of the mean value a single bin has to differ from the mean of the distribution to be classified as outlier (i.e. a bin is classified as outlier if it deviates more than |AbsDev*mean| from the mean of the distribution). Has to be given if \"CheckRelDev\" is set to 1.\n"
357 "\tAbsLimit:\tAbsolute limit a single bin has to exceed to be classified as outlier. Has to be given if \"CheckAbsLimit\" is set to 1.\n"
358 "\tDivideByReference:\tDivide test histogram by reference histogram and perform checks on the resulting ratio. (default 0)\n"
359 "\tSubtractReference:\tSubtract reference histogram from test histogram and perform checks on the resulting difference. Carefull! This yields pretty unstable results for the flatness tests! (default 0)\n"
360 "\tSubtractBinError:\tSubtract the absolute bin error from the difference between bin content and mean when checking for outliers. (default 0)\n"
361 "\tStoreOutlierBins:\tStore information on outlier bins in the output. (default 0)\n"
363 "\tNumber_of_outlier_bins:\tNumber of bins classified as outliers using the given thresholds.\n"
364 "\tCorrected_mean:\tMean of distribution ignoring outliers.\n"
365 "\tCorrected_standard_deviation:\tStandard Deviation of distribution ignoring outliers.\n"
366 "\tMax_rel_sym_deviation:\tMaximum relative symmetric deviation from a flat distribution.\n"
367 "\tMax_rel_asym_deviation:\tMaximum relative asymmetric deviation from a flat distribution.\n"
368 "\tNumber_of_bins_equal_zero:\tNumber of Bins with zero content." << std::endl;