From c6792b8cb94b2a28fa1260b3573285f934150817 Mon Sep 17 00:00:00 2001 From: jokonig Date: Tue, 1 Jul 2025 20:18:49 +0200 Subject: [PATCH] [EMCAL-567] Add class to handle EMCal temperature calibration - Gain of EMCal cell is temperature dependent. - The temperature is stored in the ccdb about every 3min for 8 sensors per SM - New class downloads temperature values from ccdb and averages them per SM - Per cell, the energy dependence was determined in run2. These values are stored in the ccdb and also read in at initializeFromCCDB - As the temperature values are accumulated per run, the end of run timestamp is taken - 60s of safety margin --- Detectors/EMCAL/calibration/CMakeLists.txt | 2 + .../EMCALTempCalibExtractor.h | 93 +++++++++++++ .../src/EMCALTempCalibExtractor.cxx | 127 ++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h create mode 100644 Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx diff --git a/Detectors/EMCAL/calibration/CMakeLists.txt b/Detectors/EMCAL/calibration/CMakeLists.txt index 68c8fd1eb69c7..7fec9fcef0f93 100644 --- a/Detectors/EMCAL/calibration/CMakeLists.txt +++ b/Detectors/EMCAL/calibration/CMakeLists.txt @@ -20,6 +20,7 @@ o2_add_library(EMCALCalibration src/PedestalCalibDevice.cxx src/PedestalProcessorDevice.cxx src/PedestalProcessorData.cxx + src/EMCALTempCalibExtractor.cxx PUBLIC_LINK_LIBRARIES O2::CCDB O2::EMCALBase O2::EMCALCalib O2::EMCALReconstruction @@ -46,6 +47,7 @@ o2_target_root_dictionary(EMCALCalibration include/EMCALCalibration/EMCDCSProcessor.h include/EMCALCalibration/EMCALPedestalHelper.h include/EMCALCalibration/PedestalProcessorData.h + include/EMCALCalibration/EMCALTempCalibExtractor.h LINKDEF src/EMCALCalibrationLinkDef.h) o2_add_executable(emcal-channel-calib-workflow diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h new file mode 100644 index 0000000000000..5dbaec4c933f8 --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h @@ -0,0 +1,93 @@ +// 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. + +/// \class EMCALTempCalibExtractor +/// \brief Calculate gain correction factors based on the temperature and the cell-by-cell temperature dependent correction factors (slope and intercept) +/// \author Joshua König +/// \ingroup EMCALCalib +/// \since June 30, 2025 + +#ifndef EMCALTEMPCALIBEXTRACTOR_H_ +#define EMCALTEMPCALIBEXTRACTOR_H_ + +#include +#include +#include +#include "CCDB/BasicCCDBManager.h" +#include "EMCALCalib/ElmbData.h" +#include "EMCALCalib/TempCalibrationParams.h" +#include "EMCALBase/Geometry.h" + +namespace o2 +{ +namespace emcal +{ + +class EMCALTempCalibExtractor +{ + + public: + /// \brief Constructor + EMCALTempCalibExtractor() + { + LOG(info) << "initialized EMCALTempCalibExtractor"; + try { + // Try to access geometry initialized ountside + mGeometry = o2::emcal::Geometry::GetInstance(); + } catch (o2::emcal::GeometryNotInitializedException& e) { + mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); // fallback option + } + }; + /// \brief Destructor + ~EMCALTempCalibExtractor() = default; + + /// \brief Initialize temperature data and slope for each cell from the ccdb + /// \param path path to the slope data + /// \param timestamp timestamp for the ccdb objects or runnumber (will detect automatically if its a runnumber and convert it) + void InitializeFromCCDB(std::string path, uint64_t timestamp); + + /// \brief get average temperature in a supermodule + /// \param iSM SM number + /// \param ElmbData object where temperature sensor values are stored + /// \return average temperature in a supermodule + float getTemperatureForSM(const unsigned short iSM, o2::emcal::ElmbData* ElmbData) const; + + /// \brief get gain calibration factor depending on the temperature and the slope of the cell + /// \param cellID cell ID + /// \return gain calibration factor + float getGainCalibFactor(const unsigned short cellID) const; + + /// \brief set temperature range in which sensor ddata is assumed to be good + /// \param low lower temperature + /// \param high upper temperature + void setAcceptedEnergyRange(float low, float high); + + /// \brief set if median (true) or mean (false) should be used for averaging of the temperature in a SM + void setUseMedian(const bool tmp) { mUseMedian = tmp; } + + /// \brief get sensor IDs for a specific supermodule + /// \param iSM SM number + /// \return vector of sensor IDs + std::vector getSensorsForSM(const unsigned short iSM) const; + + private: + static constexpr unsigned short mNCells = 17664; ///< Number of EMCal cells + std::array mGainCalibFactors; ///< gain calibration factors that are calculated based on the temperature and the slopes for each cell + o2::emcal::Geometry* mGeometry; ///< pointer to the EMCal geometry + std::array mAcceptedTempRange = {15., 30.}; ///< Temperature range where sensors are believed to send good data. Temperatures outside this range will be rejected + bool mUseMedian = true; /// switch to decide if temperature within a SM should be calculated as the mean or the median of the individual sensor data +}; + +} // namespace emcal + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx b/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx new file mode 100644 index 0000000000000..b6dc2b0a7c0a7 --- /dev/null +++ b/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx @@ -0,0 +1,127 @@ +// 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. + +#include "EMCALCalibration/EMCALTempCalibExtractor.h" +#include "EMCALCalib/CalibDB.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include + +namespace o2 +{ +namespace emcal +{ + +void EMCALTempCalibExtractor::InitializeFromCCDB(std::string path, uint64_t timestamp) +{ + + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + uint64_t maxRunNr = 1000000; + if (timestamp < maxRunNr) { + LOG(info) << "assuming input is run " << timestamp << " will convert it to timstamp"; + auto [sor, eor] = ccdbMgr.getRunDuration(timestamp); + uint64_t sixtySec = 60000; + timestamp = eor - sixtySec; // safety margin of 1min at EOR + LOG(info) << "set timestamp to " << timestamp; + } + + o2::emcal::CalibDB calibdb("http://alice-ccdb.cern.ch"); + std::map metadata; + auto tempSensorData = calibdb.readTemperatureSensorData(timestamp, metadata); + + // also obtain cell dependent correction factors + TempCalibrationParams* params = ccdbMgr.getForTimeStamp(path, timestamp); + + std::map mapSMTemperature; + for (unsigned short i = 0; i < mNCells; ++i) { + const unsigned short iSM = mGeometry->GetSuperModuleNumber(i); + if (mapSMTemperature.count(iSM) == 0) { + mapSMTemperature[iSM] = getTemperatureForSM(iSM, tempSensorData); + } + float corrFac = params->getTempCalibParamA0(i) + params->getTempCalibParamSlope(i) * mapSMTemperature[iSM]; + mGainCalibFactors[i] = corrFac; + } +} + +float EMCALTempCalibExtractor::getTemperatureForSM(const unsigned short iSM, o2::emcal::ElmbData* ElmbData) const +{ + if (iSM < 0 || iSM > 20) { + LOG(error) << "SM " << iSM << "does not exist!"; // could be replaced with a proper exception + return 0.; + } + std::vector vecSensorID = getSensorsForSM(iSM); + + // Obtain temperature for these sensors + std::vector vecTemperature; + for (const auto& iSensor : vecSensorID) { + float temp = ElmbData->getMean(iSensor); + if (temp < mAcceptedTempRange[0] || temp > mAcceptedTempRange[1]) { + continue; + } + vecTemperature.push_back(temp); + } + + const unsigned int nEntries = vecTemperature.size(); + if (nEntries == 0) { + LOG(warning) << "No sensor data between " << mAcceptedTempRange[0] << " and " << mAcceptedTempRange[1] << "degree found... for SM " << iSM << " Setting to default 20 degree"; + return 20.; // + } + + // get median energy + float tempSM = 0.; + if (mUseMedian) { + std::sort(vecTemperature.begin(), vecTemperature.end()); + if (nEntries % 2 == 0) { + // even number of elements: average the two middle ones + tempSM = (vecTemperature[nEntries / 2 - 1] + vecTemperature[nEntries / 2]) / 2.0; + } else { + // odd number of elements: return the middle one + tempSM = vecTemperature[nEntries / 2]; + } + } else { // use Mean temperature + float sum = std::accumulate(vecTemperature.begin(), vecTemperature.end(), 0.0); + tempSM = sum / vecTemperature.size(); + } + return tempSM; +} + +float EMCALTempCalibExtractor::getGainCalibFactor(const unsigned short cellID) const +{ + if (cellID >= mNCells) { + LOG(error) << "cell ID" << cellID << " does not exist"; + return 1.; + } + return mGainCalibFactors[cellID]; +} + +std::vector EMCALTempCalibExtractor::getSensorsForSM(const unsigned short iSM) const +{ + unsigned short nSensors = 8; + if (iSM == 10 || iSM == 11 || iSM == 19 || iSM == 20) { + nSensors = 4; + } + + std::vector vecSensorID; + for (unsigned short iELMBSensor = iSM * 8; iELMBSensor < iSM * 8 + nSensors; iELMBSensor++) { + vecSensorID.push_back(iELMBSensor); + } + return vecSensorID; +} + +void EMCALTempCalibExtractor::setAcceptedEnergyRange(float low, float high) +{ + mAcceptedTempRange[0] = low; + mAcceptedTempRange[1] = high; +} + +} // namespace emcal + +} // namespace o2 \ No newline at end of file