-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Common] add utility class to handle the checker thresholds
The ultility class provides the following functionalities: * stores interaction-rate-dependent threshold values * stores optional axis ranges on which the check should be restricted * provides the code to retrieve the threshold and range values from the custom parameters * provides a function to retrieve the optimal threshold values for a given interaction rate
- Loading branch information
1 parent
57965ff
commit ce72c93
Showing
2 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2019-2020 CERN and copyright holders of ALICE O2. | ||
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. | ||
// All rights not expressly granted are reserved. | ||
// | ||
// This software is distributed under the terms of the GNU General Public | ||
// License v3 (GPL Version 3), copied verbatim in the file "COPYING". | ||
// | ||
// In applying this license CERN does not waive the privileges and immunities | ||
// granted to it by virtue of its status as an Intergovernmental Organization | ||
// or submit itself to any jurisdiction. | ||
|
||
/// | ||
/// \file CheckerThresholdsConfig.h | ||
/// \author Andrea Ferrero | ||
/// \brief Utility class handling thresholds and axis ranges and retrieve them from the custom parameters | ||
/// | ||
|
||
#ifndef QC_MODULE_COMMON_CHECKERTHRESHOLDSCONFIG_H | ||
#define QC_MODULE_COMMON_CHECKERTHRESHOLDSCONFIG_H | ||
|
||
#include "QualityControl/Activity.h" | ||
#include "QualityControl/CustomParameters.h" | ||
|
||
#include <string> | ||
#include <optional> | ||
#include <array> | ||
#include <vector> | ||
|
||
using namespace o2::quality_control::core; | ||
|
||
namespace o2::quality_control_modules::common | ||
{ | ||
|
||
namespace internal | ||
{ | ||
class Thresholds; | ||
class XYRanges; | ||
} // namespace internal | ||
|
||
class CheckerThresholdsConfig | ||
{ | ||
public: | ||
CheckerThresholdsConfig(const CustomParameters& customParameters, const Activity& activity); | ||
~CheckerThresholdsConfig() = default; | ||
|
||
/// \brief function to retrieve the thresholds for a given interaction rate, if available | ||
std::array<std::optional<std::pair<double, double>>, 2> getThresholdsForPlot(const std::string& plotName, double rate); | ||
|
||
/// \brief optional X-Y ranges over which the check must be restricted | ||
std::array<std::optional<std::pair<double, double>>, 2> getRangesForPlot(const std::string& plotName); | ||
|
||
private: | ||
void initThresholdsForPlot(const std::string& plotName); | ||
void initRangesForPlot(const std::string& plotName); | ||
|
||
CustomParameters mCustomParameters; | ||
Activity mActivity; | ||
|
||
/// vectors of [min,max,rate] tuples. The first two values are the minimum and maximum threshold values, | ||
/// and the third is the associated reference interaction rate (optional) | ||
std::array<std::shared_ptr<internal::Thresholds>, 2> mDefaultThresholds; | ||
std::array<std::unordered_map<std::string, std::shared_ptr<internal::Thresholds>>, 2> mThresholds; | ||
|
||
/// \brief optional X-Y ranges over which the check must be restricted | ||
std::array<std::shared_ptr<internal::XYRanges>, 2> mDefaultRanges; | ||
std::array<std::unordered_map<std::string, std::shared_ptr<internal::XYRanges>>, 2> mRanges; | ||
}; | ||
|
||
} // namespace o2::quality_control_modules::common | ||
|
||
#endif // QC_MODULE_COMMON_CHECKERTHRESHOLDSCONFIG_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
// Copyright 2019-2020 CERN and copyright holders of ALICE O2. | ||
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. | ||
// All rights not expressly granted are reserved. | ||
// | ||
// This software is distributed under the terms of the GNU General Public | ||
// License v3 (GPL Version 3), copied verbatim in the file "COPYING". | ||
// | ||
// In applying this license CERN does not waive the privileges and immunities | ||
// granted to it by virtue of its status as an Intergovernmental Organization | ||
// or submit itself to any jurisdiction. | ||
|
||
/// | ||
/// \file CheckerThresholdsConfig.cxx | ||
/// \author Andrea Ferrero | ||
/// \brief Utility class handling thresholds and axis ranges and retrieve them from the custom parameters | ||
/// | ||
|
||
#include "Common/CheckerThresholdsConfig.h" | ||
#include "Common/Utils.h" | ||
#include "QualityControl/QcInfoLogger.h" | ||
#include <CommonUtils/StringUtils.h> | ||
|
||
namespace o2::quality_control_modules::common | ||
{ | ||
|
||
namespace internal | ||
{ | ||
|
||
class Thresholds | ||
{ | ||
public: | ||
/// \brief thersholds initialization from custom parameters | ||
Thresholds(const CustomParameters& customParameters, const std::string& plotName, const std::string& qualityLabel, const Activity& activity); | ||
|
||
/// \brief function to retrieve the thresholds for a given interaction rate, if available | ||
std::optional<std::pair<double, double>> getThresholdsForRate(double rate); | ||
|
||
private: | ||
/// vectors of [min,max,rate] tuples. The first two values are the minimum and maximum threshold values, | ||
/// and the third is the associated reference interaction rate (optional) | ||
std::vector<std::tuple<double, double, std::optional<double>>> mThresholds; | ||
}; | ||
|
||
Thresholds::Thresholds(const CustomParameters& customParameters, const std::string& plotName, const std::string& qualityLabel, const Activity& activity) | ||
{ | ||
// get configuration parameter associated to the key | ||
std::string parKey = std::string("thresholds") + qualityLabel; | ||
if (!plotName.empty()) { | ||
parKey += ":"; | ||
parKey += plotName; | ||
} | ||
|
||
std::string parValue = getFromExtendedConfig<std::string>(activity, customParameters, parKey); | ||
if (parValue.empty()) { | ||
return; | ||
} | ||
|
||
// extract information for each nominal interaction rate value | ||
auto interactionRatePoints = o2::utils::Str::tokenize(parValue, ';', false, true); | ||
for (const auto& interactionRatePoint : interactionRatePoints) { | ||
double thresholdMin = 0; | ||
double thresholdMax = 0; | ||
std::optional<double> nominalRate; | ||
|
||
// extract interaction rate and threshold pairs | ||
auto rateAndThresholds = o2::utils::Str::tokenize(interactionRatePoint, ':', false, true); | ||
std::string thresholdValues; | ||
if (rateAndThresholds.size() == 2) { | ||
// the token contains both the rate and threshold values | ||
// the rate is comverted from kHz to Hz | ||
try { | ||
nominalRate = std::stod(rateAndThresholds[0]) * 1000; | ||
} catch (std::exception& exception) { | ||
ILOG(Error, Support) << "Cannot convert values from string to double for " << qualityLabel << " thresholds of plot \"" << plotName << "\", string is " << rateAndThresholds[0] << ENDM; | ||
} | ||
thresholdValues = rateAndThresholds[1]; | ||
} else { | ||
// the token only contains threshold values, which are then valid for any rate | ||
thresholdValues = rateAndThresholds[0]; | ||
} | ||
|
||
// extract the comma-separated minimum and maximum threshold values | ||
std::vector<std::string> thresholdMinMax = o2::utils::Str::tokenize(thresholdValues, ',', false, true); | ||
if (thresholdMinMax.size() == 2) { | ||
try { | ||
thresholdMin = std::stod(thresholdMinMax[0]); | ||
thresholdMax = std::stod(thresholdMinMax[1]); | ||
} catch (std::exception& exception) { | ||
ILOG(Error, Support) << "Cannot convert values from string to double for " << qualityLabel << " thresholds of plot \"" << plotName << "\", string is " << thresholdValues << ENDM; | ||
} | ||
} | ||
|
||
mThresholds.emplace_back(std::make_tuple(thresholdMin, thresholdMax, nominalRate)); | ||
} | ||
} | ||
|
||
static std::pair<double, double> interpolateThresholds(double fraction, const std::tuple<double, double, std::optional<double>>& thresholdsLow, const std::tuple<double, double, std::optional<double>>& thresholdsHigh) | ||
{ | ||
double thresholdMin = std::get<0>(thresholdsLow) * (1.0 - fraction) + std::get<0>(thresholdsHigh) * fraction; | ||
double thresholdMax = std::get<1>(thresholdsLow) * (1.0 - fraction) + std::get<1>(thresholdsHigh) * fraction; | ||
return std::make_pair(thresholdMin, thresholdMax); | ||
} | ||
|
||
std::optional<std::pair<double, double>> Thresholds::getThresholdsForRate(double rate) | ||
{ | ||
std::optional<std::pair<double, double>> result; | ||
|
||
// index and corresponding rate of the element immediately below the requested rate | ||
int indexLow = -1; | ||
double rateLow = -1; | ||
|
||
// index and corresponding rate of the element immediately above the requested rate | ||
int indexHigh = -1; | ||
double rateHigh = -1; | ||
|
||
// search the closest element above and below the requested rate | ||
for (int index = 0; index < mThresholds.size(); index++) { | ||
const auto& element = mThresholds[index]; | ||
// skip if rate is not specified | ||
if (!std::get<2>(element)) { | ||
continue; | ||
} | ||
double rateCurrent = std::get<2>(element).value(); | ||
|
||
if (rateCurrent <= rate) { | ||
if (indexLow < 0 || rateLow < rateCurrent) { | ||
indexLow = index; | ||
rateLow = rateCurrent; | ||
} | ||
} | ||
|
||
if (rateCurrent >= rate) { | ||
if (indexHigh < 0 || rateHigh > rateCurrent) { | ||
indexHigh = index; | ||
rateHigh = rateCurrent; | ||
} | ||
} | ||
} | ||
|
||
if (indexLow >= 0 && indexHigh >= 0 && indexLow != indexHigh) { | ||
double fraction = (rate - rateLow) / (rateHigh - rateLow); | ||
result = interpolateThresholds(fraction, mThresholds[indexLow], mThresholds[indexHigh]); | ||
} else if (indexLow >= 0) { | ||
result = std::make_pair(std::get<0>(mThresholds[indexLow]), std::get<1>(mThresholds[indexLow])); | ||
} else if (indexHigh >= 0) { | ||
result = std::make_pair(std::get<0>(mThresholds[indexHigh]), std::get<1>(mThresholds[indexHigh])); | ||
} else if (mThresholds.size() > 0) { | ||
result = std::make_pair(std::get<0>(mThresholds[0]), std::get<1>(mThresholds[0])); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
class XYRanges | ||
{ | ||
public: | ||
/// \brief range initialization from custom parameters | ||
XYRanges(const CustomParameters& customParameters, const std::string& plotName, const std::string& axisName, const Activity& activity); | ||
|
||
/// \brief retrieve the axis range over which the check must be restricted | ||
std::optional<std::pair<double, double>> getRange() { return mRange; } | ||
|
||
private: | ||
/// \brief axis range over which the check must be restricted | ||
std::optional<std::pair<double, double>> mRange; | ||
}; | ||
|
||
XYRanges::XYRanges(const CustomParameters& customParameters, const std::string& plotName, const std::string& axisName, const Activity& activity) | ||
{ | ||
// build the key associated to the configuration parameter | ||
std::string parKey = std::string("range") + axisName; | ||
if (!plotName.empty()) { | ||
parKey += ":"; | ||
parKey += plotName; | ||
} | ||
|
||
std::string parValue = getFromExtendedConfig<std::string>(activity, customParameters, parKey); | ||
if (parValue.empty()) { | ||
return; | ||
} | ||
|
||
// extract min and max values of the range | ||
auto rangeMinMax = o2::utils::Str::tokenize(parValue, ',', false, true); | ||
if (rangeMinMax.size() != 2) { | ||
ILOG(Error, Support) << "Cannot parse values for " << axisName << " axis range of plot \"" << plotName << "\", string is " << parValue << ENDM; | ||
return; | ||
} | ||
|
||
try { | ||
mRange = std::make_pair<double, double>(std::stof(rangeMinMax[0]), std::stof(rangeMinMax[1])); | ||
} catch (std::exception& exception) { | ||
ILOG(Error, Support) << "Cannot convert values from string to double for " << axisName << " axis range of plot \"" << plotName << "\", string is " << parValue << ENDM; | ||
} | ||
} | ||
|
||
} // namespace internal | ||
|
||
CheckerThresholdsConfig::CheckerThresholdsConfig(const CustomParameters& customParameters, const Activity& activity) | ||
: mCustomParameters(customParameters), mActivity(activity) | ||
{ | ||
mDefaultThresholds[0] = std::make_shared<internal::Thresholds>(mCustomParameters, std::string(), std::string("Bad"), mActivity); | ||
mDefaultThresholds[1] = std::make_shared<internal::Thresholds>(mCustomParameters, std::string(), std::string("Medium"), mActivity); | ||
|
||
mDefaultRanges[0] = std::make_shared<internal::XYRanges>(mCustomParameters, std::string(), std::string("X"), mActivity); | ||
mDefaultRanges[1] = std::make_shared<internal::XYRanges>(mCustomParameters, std::string(), std::string("Y"), mActivity); | ||
} | ||
|
||
void CheckerThresholdsConfig::initThresholdsForPlot(const std::string& plotName) | ||
{ | ||
// if not done yet, read from the configuration the threshold settings specific to plotName | ||
using MapElementType = std::unordered_map<std::string, std::shared_ptr<internal::Thresholds>>::value_type; | ||
if (mThresholds[0].count(plotName) == 0) { | ||
mThresholds[0].emplace(MapElementType(plotName, std::make_shared<internal::Thresholds>(mCustomParameters, plotName, std::string("Bad"), mActivity))); | ||
mThresholds[1].emplace(MapElementType(plotName, std::make_shared<internal::Thresholds>(mCustomParameters, plotName, std::string("Medium"), mActivity))); | ||
} | ||
} | ||
|
||
std::array<std::optional<std::pair<double, double>>, 2> CheckerThresholdsConfig::getThresholdsForPlot(const std::string& plotName, double rate) | ||
{ | ||
initThresholdsForPlot(plotName); | ||
|
||
std::array<std::optional<std::pair<double, double>>, 2> result; | ||
|
||
// get thresholds for Bad and Medium quality | ||
for (size_t index = 0; index < 2; index++) { | ||
result[index] = mThresholds[index][plotName]->getThresholdsForRate(rate); | ||
if (!result[index]) { | ||
// try with default threshold settings if the plot-specific ones are not available | ||
result[index] = mDefaultThresholds[index]->getThresholdsForRate(rate); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
void CheckerThresholdsConfig::initRangesForPlot(const std::string& plotName) | ||
{ | ||
// if not done yet, read from the configuration the ranges settings specific to plotName | ||
using MapElementType = std::unordered_map<std::string, std::shared_ptr<internal::XYRanges>>::value_type; | ||
if (mRanges[0].count(plotName) == 0) { | ||
mRanges[0].emplace(MapElementType(plotName, std::make_shared<internal::XYRanges>(mCustomParameters, plotName, std::string("X"), mActivity))); | ||
mRanges[1].emplace(MapElementType(plotName, std::make_shared<internal::XYRanges>(mCustomParameters, plotName, std::string("Y"), mActivity))); | ||
} | ||
} | ||
|
||
std::array<std::optional<std::pair<double, double>>, 2> CheckerThresholdsConfig::getRangesForPlot(const std::string& plotName) | ||
{ | ||
initRangesForPlot(plotName); | ||
|
||
std::array<std::optional<std::pair<double, double>>, 2> result; | ||
|
||
// get ranges for X and Y axes | ||
for (size_t index = 0; index < 2; index++) { | ||
result[index] = mRanges[index][plotName]->getRange(); | ||
if (!result[index]) { | ||
// try with default threshold settings if the plot-specific ones are not available | ||
result[index] = mDefaultRanges[index]->getRange(); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
} // namespace o2::quality_control_modules::common |