From b104ea435c56c7a721ce02c0ea6cd993e3feb0c5 Mon Sep 17 00:00:00 2001 From: Giovanni Marchiori Date: Tue, 27 Feb 2024 09:31:30 +0100 Subject: [PATCH 1/6] add code to perform MVA calibration of cluster energies --- RecFCCeeCalorimeter/CMakeLists.txt | 10 +- .../src/components/CalibrateCaloClusters.cpp | 368 ++++++++++++++++++ .../src/components/CalibrateCaloClusters.h | 142 +++++++ cmake/FindONNXRuntime.cmake | 14 + 4 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp create mode 100644 RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h create mode 100644 cmake/FindONNXRuntime.cmake diff --git a/RecFCCeeCalorimeter/CMakeLists.txt b/RecFCCeeCalorimeter/CMakeLists.txt index 2e7bae6c..79475801 100644 --- a/RecFCCeeCalorimeter/CMakeLists.txt +++ b/RecFCCeeCalorimeter/CMakeLists.txt @@ -2,6 +2,13 @@ # Package: RecFCCeeCalorimeter ################################################################################ +# ONNX DEPENDENCIES +find_package(ONNXRuntime REQUIRED) +# includes +include_directories("${ONNXRUNTIME_INCLUDE_DIRS}") +# libs +link_directories("${ONNXRUNTIME_LIBRARY_DIRS}") + file(GLOB _module_sources src/components/*.cpp) gaudi_add_module(k4RecFCCeeCalorimeterPlugins SOURCES ${_module_sources} @@ -16,8 +23,8 @@ gaudi_add_module(k4RecFCCeeCalorimeterPlugins DD4hep::DDG4 ROOT::Core ROOT::Hist + ${ONNXRUNTIME_LIBRARY} ) - install(TARGETS k4RecFCCeeCalorimeterPlugins EXPORT k4RecCalorimeterTargets RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin @@ -51,3 +58,4 @@ add_test(NAME FCCeeLAr_benchmarkCalibration WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) set_test_env(FCCeeLAr_benchmarkCalibration) + diff --git a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp new file mode 100644 index 00000000..33cb62dd --- /dev/null +++ b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp @@ -0,0 +1,368 @@ +#include "CalibrateCaloClusters.h" + +// Key4HEP +#include "k4Interface/IGeoSvc.h" + +// FCC Detectors +#include "detectorCommon/DetUtils_k4geo.h" + +// DD4hep +#include "DD4hep/Detector.h" + +// our EDM +#include "edm4hep/Cluster.h" +#include "edm4hep/ClusterCollection.h" +#include "edm4hep/CalorimeterHitCollection.h" + +DECLARE_COMPONENT(CalibrateCaloClusters) + +// convert vector data with given shape into ONNX runtime tensor +template +Ort::Value vec_to_tensor(std::vector &data, const std::vector &shape) +{ + Ort::MemoryInfo mem_info = + Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + auto tensor = Ort::Value::CreateTensor(mem_info, data.data(), data.size(), shape.data(), shape.size()); + return tensor; +} + +CalibrateCaloClusters::CalibrateCaloClusters(const std::string &name, + ISvcLocator *svcLoc) + : GaudiAlgorithm(name, svcLoc), + m_geoSvc("GeoSvc", "CalibrateCaloClusters") +{ + declareProperty("inClusters", m_inClusters, + "Input cluster collection"); + declareProperty("outClusters", m_outClusters, + "Calibrated (output) cluster collection"); +} + +StatusCode CalibrateCaloClusters::initialize() +{ + // Initialize base class + { + StatusCode sc = GaudiAlgorithm::initialize(); + if (sc.isFailure()) + { + return sc; + } + } + + // Check if readouts exist + { + bool readoutMissing = false; + for (size_t i = 0; i < m_readoutNames.size(); ++i) + { + auto readouts = m_geoSvc->getDetector()->readouts(); + if (readouts.find(m_readoutNames.value().at(i)) == readouts.end()) + { + readoutMissing = true; + } + } + if (readoutMissing) + { + error() << "Missing readout, exiting!" << endmsg; + return StatusCode::FAILURE; + } + } + + // Check if readout related variables have the same size + if (m_systemIDs.size() != m_readoutNames.size()) + { + error() << "Sizes of the systemIDs vector and readoutNames vector do not match, exiting!" << endmsg; + return StatusCode::FAILURE; + } + if (m_systemIDs.size() != m_numLayers.size()) + { + error() << "Sizes of systemIDs vector and numLayers vector do not match, exiting!" << endmsg; + return StatusCode::FAILURE; + } + if (m_systemIDs.size() != m_layerFieldNames.size()) + { + error() << "Sizes of systemIDs vector and layerFieldNames vector do not match, exiting!" << endmsg; + return StatusCode::FAILURE; + } + if (m_systemIDs.size() != m_firstLayerIDs.size()) + { + error() << "Sizes of systemIDs vector and firstLayerIDs vector do not match, exiting!" << endmsg; + return StatusCode::FAILURE; + } + + // calculate total number of layers summed over the various subsystems + m_numLayersTotal = 0; + for (size_t i = 0; i < m_numLayers.size(); ++i) + { + m_numLayersTotal += m_numLayers[i]; + } + + // Initialize the calibration tool + StatusCode sc = readCalibrationFile(m_calibrationFile); + if (sc.isFailure()) + { + error() << "Initialization of calibration tool correction functions not successful!" << endmsg; + return sc; + } + info() << "Initialized the calibration" << endmsg; + return StatusCode::SUCCESS; +} + +StatusCode CalibrateCaloClusters::execute() +{ + verbose() << "-------------------------------------------" << endmsg; + + // Get the input collection with clusters + const edm4hep::ClusterCollection *inClusters = m_inClusters.get(); + + // Initialize output clusters + edm4hep::ClusterCollection *outClusters = initializeOutputClusters(inClusters); + if (!outClusters) + { + error() << "Something went wrong in initialization of the output cluster collection, exiting!" << endmsg; + return StatusCode::FAILURE; + } + if (inClusters->size() != outClusters->size()) + { + error() << "Sizes of input and output cluster collections does not match, exiting!" << endmsg; + return StatusCode::FAILURE; + } + + // Apply calibration + { + StatusCode sc = calibrateClusters(inClusters, outClusters); + if (sc.isFailure()) + { + return sc; + } + } + + return StatusCode::SUCCESS; +} + +StatusCode CalibrateCaloClusters::finalize() +{ + if (ortSession) + delete ortSession; + if (ortEnv) + delete ortEnv; + + return GaudiAlgorithm::finalize(); +} + +edm4hep::ClusterCollection *CalibrateCaloClusters::initializeOutputClusters( + const edm4hep::ClusterCollection *inClusters) +{ + edm4hep::ClusterCollection *outClusters = m_outClusters.createAndPut(); + + for (auto const &inCluster : *inClusters) + { + auto outCluster = inCluster.clone(); + // verbose() << "Cluster energy before calibration:" << outCluster.getEnergy() << endmsg; + outClusters->push_back(outCluster); + } + + return outClusters; +} + +StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibrationFile) +{ + // set logging level based on output level of this alg + OrtLoggingLevel loggingLevel = ORT_LOGGING_LEVEL_WARNING; + MSG::Level outputLevel = this->msgStream().level(); + switch (outputLevel) + { + case MSG::Level::FATAL: // 6 + loggingLevel = ORT_LOGGING_LEVEL_FATAL; // 4 + break; + case MSG::Level::ERROR: // 5 + loggingLevel = ORT_LOGGING_LEVEL_ERROR; // 3 + break; + case MSG::Level::WARNING: // 4 + loggingLevel = ORT_LOGGING_LEVEL_WARNING; // 2 + break; + case MSG::Level::INFO: // 3 + loggingLevel = ORT_LOGGING_LEVEL_WARNING; // 2 (ORT_LOGGING_LEVEL_INFO too verbose..) + break; + case MSG::Level::DEBUG: // 2 + loggingLevel = ORT_LOGGING_LEVEL_INFO; // 1 + break; + case MSG::Level::VERBOSE: // 1 + loggingLevel = ORT_LOGGING_LEVEL_VERBOSE; // 0 + break; + } + try + { + ortEnv = new Ort::Env(loggingLevel, "ONNX runtime environment"); + Ort::SessionOptions session_options; + session_options.SetIntraOpNumThreads(1); + ortSession = new Ort::Experimental::Session(*ortEnv, const_cast(calibrationFile), session_options); + } + catch (const Ort::Exception &exception) + { + error() << "ERROR setting up ONNX runtime environment: " << exception.what() << endmsg; + return StatusCode::FAILURE; + } + + // print name/shape of inputs + // use default allocator (CPU) + Ort::AllocatorWithDefaultOptions allocator; + debug() << "Input Node Name/Shape (" << input_names.size() << "):" << endmsg; + for (std::size_t i = 0; i < ortSession->GetInputCount(); i++) + { + // input_names.emplace_back(ortSession->GetInputNameAllocated(i, allocator).get()); + input_names.emplace_back(ortSession->GetInputName(i, allocator)); + input_shapes = ortSession->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape(); + debug() << "\t" << input_names.at(i) << " : "; + for (std::size_t k = 0; k < input_shapes.size() - 1; k++) + { + debug() << input_shapes[k] << "x"; + } + debug() << input_shapes[input_shapes.size() - 1] << endmsg; + } + // some models might have negative shape values to indicate dynamic shape, e.g., for variable batch size. + for (auto &s : input_shapes) + { + if (s < 0) + { + s = 1; + } + } + + // print name/shape of outputs + // std::vector output_names; + debug() << "Output Node Name/Shape (" << output_names.size() << "):" << endmsg; + for (std::size_t i = 0; i < ortSession->GetOutputCount(); i++) + { + // output_names.emplace_back(ortSession->GetOutputNameAllocated(i, allocator).get()); + output_names.emplace_back(ortSession->GetOutputName(i, allocator)); + output_shapes = ortSession->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape(); + debug() << "\t" << output_names.at(i) << " : "; + for (std::size_t k = 0; k < output_shapes.size() - 1; k++) + { + debug() << output_shapes[k] << "x"; + } + debug() << output_shapes[output_shapes.size() - 1] << endmsg; + } + + // the output should be a single value (the correction) + // and the inputs should be n(layers)+1 (fractions + total E) + // the first dimension of the tensors are the number of clusters + // to be calibrated simultaneously (-1 = dynamic) + // we will calibrate once at a time + if (input_shapes.size() != 2 || + output_shapes.size() != 2 || + input_shapes[1] != (m_numLayersTotal + 1) || + output_shapes[1] != 1) + { + error() << "The input or output shapes in the calibration files do not match the expected architecture" << endmsg; + return StatusCode::FAILURE; + } + + return StatusCode::SUCCESS; +} + +StatusCode CalibrateCaloClusters::calibrateClusters(const edm4hep::ClusterCollection *inClusters, + edm4hep::ClusterCollection *outClusters) +{ + + // this vector will contain the input features for the calibration + // i.e. the fraction of energy in each layer and the total energy + std::vector energiesInLayers(m_numLayersTotal + 1); + + // loop over the input clusters and perform the calibration + for (size_t j = 0; j < inClusters->size(); ++j) + { + + // retrieve total cluster energy + float ecl = (inClusters->at(j)).getEnergy(); + if (ecl <= 0.) + { + warning() << "Energy in calorimeter <= 0, ignoring energy correction!" << endmsg; + continue; + } + verbose() << "Cluster energy before calibration: " << ecl << endmsg; + + // calculate cluster energy in each layer and normalize by total cluster energy + calcEnergiesInLayers(inClusters->at(j), energiesInLayers); + for (size_t k = 0; k < m_numLayersTotal; ++k) + { + energiesInLayers[k] /= ecl; + } + energiesInLayers[m_numLayersTotal] = ecl; + verbose() << "Calibration inputs:" << endmsg; + for (size_t k = 0; k < energiesInLayers.size(); ++k) + { + verbose() << " f" << k << " : " << energiesInLayers[k] << endmsg; + } + + // run the MVA calibration and correct the cluster energy + float corr = 1.0; + // Create a single Ort tensor + std::vector input_tensors; + input_tensors.emplace_back(vec_to_tensor(energiesInLayers, input_shapes)); + + // double-check the dimensions of the input tensor + // assert(input_tensors[0].IsTensor() && input_tensors[0].GetTensorTypeAndShapeInfo().GetShape() == input_shapes); + + // pass data through model + try + { + std::vector output_tensors = ortSession->Run(input_names, + input_tensors, + output_names, + Ort::RunOptions{nullptr}); + + // double-check the dimensions of the output tensors + // NOTE: the number of output tensors is equal to the number of output nodes specifed in the Run() call + // assert(output_tensors.size() == output_names.size() && output_tensors[0].IsTensor()); + + float *outputData = output_tensors[0].GetTensorMutableData(); + corr = outputData[0]; + } + catch (const Ort::Exception &exception) + { + error() << "ERROR running model inference: " << exception.what() << endmsg; + return StatusCode::FAILURE; + } + + verbose() << "Calibration output: " << corr << endmsg; + outClusters->at(j).setEnergy(ecl * corr); + verbose() << "Corrected cluster energy: " << ecl * corr << endmsg; + } + + return StatusCode::SUCCESS; +} + +void CalibrateCaloClusters::calcEnergiesInLayers(edm4hep::Cluster cluster, + std::vector &energiesInLayer) +{ + // reset vector with energies per layer + std::fill(energiesInLayer.begin(), energiesInLayer.end(), 0.0); + + // loop over the various readouts/subsystems/... + int startPositionToFill = 0; + for (size_t k = 0; k < m_readoutNames.size(); k++) + { + if (k > 0) + { + startPositionToFill += m_numLayers[k - 1]; + } + int systemID = m_systemIDs[k]; + int firstLayer = m_firstLayerIDs[k]; + std::string layerField = m_layerFieldNames[k]; + std::string readoutName = m_readoutNames[k]; + dd4hep::DDSegmentation::BitFieldCoder *decoder = m_geoSvc->getDetector()->readout(readoutName).idSpec().decoder(); + + // for each readout, loop over the cells and calculate the energies in the layers + // of that subsystem + for (auto cell = cluster.hits_begin(); cell != cluster.hits_end(); ++cell) + { + dd4hep::DDSegmentation::CellID cellID = cell->getCellID(); + if (decoder->get(cellID, "system") != systemID) + { + continue; + } + int layer = decoder->get(cellID, layerField); + energiesInLayer[startPositionToFill + layer - firstLayer] += cell->getEnergy(); + } + } +} diff --git a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h new file mode 100644 index 00000000..3626afb0 --- /dev/null +++ b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h @@ -0,0 +1,142 @@ +#ifndef RECFCCEECALORIMETER_CALIBRATECALOCLUSTERS_H +#define RECFCCEECALORIMETER_CALIBRATECALOCLUSTERS_H + +// Key4HEP +#include "k4FWCore/DataHandle.h" + +// Gaudi +#include "GaudiAlg/GaudiAlgorithm.h" +#include "GaudiKernel/ToolHandle.h" +#include "GaudiKernel/MsgStream.h" +class IGeoSvc; + +// EDM4HEP +namespace edm4hep { + class Cluster; + class ClusterCollection; + class CalorimeterHitCollection; +} + +// DD4HEP +namespace dd4hep { + namespace DDSegmentation { + class BitFieldCoder; + } +} + +// ONNX +#include "onnxruntime/core/session/experimental_onnxruntime_cxx_api.h" + +/** @class CalibrateCaloClusters + * + * Apply an MVA energy calibration to the clusters reconstructed in the calorimeter. + * + * @author Giovanni Marchiori + */ + +class CalibrateCaloClusters : public GaudiAlgorithm { + +public: + CalibrateCaloClusters(const std::string& name, ISvcLocator* svcLoc); + + StatusCode initialize(); + + StatusCode execute(); + + StatusCode finalize(); + +private: + /** + * Initialize output calorimeter cluster collection. + * + * @param[in] inClusters Pointer to the input cluster collection. + * + * @return Pointer to the output cluster collection. + */ + edm4hep::ClusterCollection* initializeOutputClusters(const edm4hep::ClusterCollection* inClusters); + + /** + * Initialize vectors of upstream and downstream correction functions. + * + * @return Status code. + */ + StatusCode readCalibrationFile(const std::string& calibrationFileName); + + /** + * Apply the calibration to the input clusters. + * + * @param[in] inClusters Pointer to the input cluster collection. + * @param[out] outClusters Pointer to the output cluster collection. + * + * @return Status code. + */ + StatusCode calibrateClusters(const edm4hep::ClusterCollection* inClusters, + edm4hep::ClusterCollection* outClusters); + + /** + * Get sum of energy from cells in each layer. + * This energy is not calibrated. + * + * @param[in] cluster Pointer to cluster of interest. + * @param[out] energiesInLayer Reference to vector that will contain the energies + */ + void calcEnergiesInLayers(edm4hep::Cluster cluster, + std::vector& energiesInLayer); + + + /// Handle for input calorimeter clusters collection + DataHandle m_inClusters { + "inClusters", Gaudi::DataHandle::Reader, this + }; + /// Handle for corrected (output) calorimeter clusters collection + DataHandle m_outClusters { + "outClusters", Gaudi::DataHandle::Writer, this + }; + + /// Pointer to the geometry service + ServiceHandle m_geoSvc; + + /// IDs of the detectors + Gaudi::Property> m_systemIDs { + this, "systemIDs", {4}, "IDs of systems" + }; + /// Names of the detector readouts, corresponding to system IDs + Gaudi::Property> m_readoutNames { + this, "readoutNames", {"ECalBarrelModuleThetaMerged"}, + "Names of the detector readout, corresponding to systemID" + }; + /// Name of the layer field + Gaudi::Property> m_layerFieldNames { + this, "layerFieldNames", {"layer"}, + "Identifier of layers, corresponding to systemID" + }; + /// Numbers of layers of the detectors + Gaudi::Property> m_numLayers { + this, "numLayers", {12}, "Numbers of layers of the systems" + }; + /// IDs of the first layers of the detectors + Gaudi::Property> m_firstLayerIDs { + this, "firstLayerIDs", {0}, "IDs of first layers in the systems" + }; + + /// File with the calibration model + // Gaudi::Property> m_calibrationFiles { + // this, "calibrationFiles", {}, "Files with the calibration parameters"}; + Gaudi::Property m_calibrationFile { + this, "calibrationFile", {}, "File with the calibration parameters"}; + + // total number of layers summed over the various subsystems + // should be equal to the number of input features of the MVA + size_t m_numLayersTotal; + + // the ONNX runtime session for applying the calibration, + // the environment, and the input and output shapes and names + Ort::Experimental::Session* ortSession = nullptr; + Ort::Env* ortEnv = nullptr; + std::vector input_shapes; + std::vector output_shapes; + std::vector input_names; + std::vector output_names; +}; + +#endif /* RECFCCEECALORIMETER_CALIBRATECALOCLUSTERS_H */ diff --git a/cmake/FindONNXRuntime.cmake b/cmake/FindONNXRuntime.cmake new file mode 100644 index 00000000..b8f5ba15 --- /dev/null +++ b/cmake/FindONNXRuntime.cmake @@ -0,0 +1,14 @@ +find_path(ONNXRUNTIME_INCLUDE_DIR onnxruntime/core/session/onnxruntime_cxx_inline.h + HINTS $ENV{ONNXRUNTIME_ROOT_DIR}/include ${ONNXRUNTIME_ROOT_DIR}/include) + +find_library(ONNXRUNTIME_LIBRARY NAMES onnxruntime + HINTS $ENV{ONNXRUNTIME_ROOT_DIR}/lib ${ONNXRUNTIME_ROOT_DIR}/lib) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ONNXRuntime DEFAULT_MSG ONNXRUNTIME_INCLUDE_DIR ONNXRUNTIME_LIBRARY) + +mark_as_advanced(ONNXRUNTIME_FOUND ONNXRUNTIME_INCLUDE_DIR ONNXRUNTIME_LIBRARY) + +set(ONNXRUNTIME_INCLUDE_DIRS ${ONNXRUNTIME_INCLUDE_DIR}) +set(ONNXRUNTIME_LIBRARIES ${ONNXRUNTIME_LIBRARY}) +get_filename_component(ONNXRUNTIME_LIBRARY_DIRS ${ONNXRUNTIME_LIBRARY} PATH) From 6c5d8361b9601404d59bcf632a8343d8e0a1e910 Mon Sep 17 00:00:00 2001 From: Giovanni Marchiori Date: Wed, 28 Feb 2024 16:01:20 +0100 Subject: [PATCH 2/6] move find_package to main cmake file and update list of dependencies in README --- CMakeLists.txt | 1 + README.md | 2 +- RecFCCeeCalorimeter/CMakeLists.txt | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e9cf2cf..72b6280a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ find_package(k4FWCore) # implicit: Gaudi find_package(EDM4HEP 0.10.1) # implicit: Podio find_package(DD4hep) find_package(k4geo) +find_package(ONNXRuntime) #--------------------------------------------------------------- diff --git a/README.md b/README.md index 3a196e6c..d639069f 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,4 @@ source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh * k4FWCore * DD4hep * k4geo - +* ONNXRuntime diff --git a/RecFCCeeCalorimeter/CMakeLists.txt b/RecFCCeeCalorimeter/CMakeLists.txt index 79475801..b9be7b18 100644 --- a/RecFCCeeCalorimeter/CMakeLists.txt +++ b/RecFCCeeCalorimeter/CMakeLists.txt @@ -3,7 +3,6 @@ ################################################################################ # ONNX DEPENDENCIES -find_package(ONNXRuntime REQUIRED) # includes include_directories("${ONNXRUNTIME_INCLUDE_DIRS}") # libs From 3c4bdac02560121c8914f2f236a3af8e2d7ac501 Mon Sep 17 00:00:00 2001 From: Giovanni Marchiori Date: Wed, 28 Feb 2024 17:06:33 +0100 Subject: [PATCH 3/6] implement comments from PR review --- .../src/components/CalibrateCaloClusters.cpp | 111 ++++++++---------- .../src/components/CalibrateCaloClusters.h | 32 ++--- 2 files changed, 67 insertions(+), 76 deletions(-) diff --git a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp index 33cb62dd..fc6e2af6 100644 --- a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp +++ b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp @@ -28,7 +28,7 @@ Ort::Value vec_to_tensor(std::vector &data, const std::vector & CalibrateCaloClusters::CalibrateCaloClusters(const std::string &name, ISvcLocator *svcLoc) - : GaudiAlgorithm(name, svcLoc), + : Gaudi::Algorithm(name, svcLoc), m_geoSvc("GeoSvc", "CalibrateCaloClusters") { declareProperty("inClusters", m_inClusters, @@ -41,7 +41,7 @@ StatusCode CalibrateCaloClusters::initialize() { // Initialize base class { - StatusCode sc = GaudiAlgorithm::initialize(); + StatusCode sc = Gaudi::Algorithm::initialize(); if (sc.isFailure()) { return sc; @@ -49,17 +49,10 @@ StatusCode CalibrateCaloClusters::initialize() } // Check if readouts exist + for (size_t i = 0; i < m_readoutNames.size(); ++i) { - bool readoutMissing = false; - for (size_t i = 0; i < m_readoutNames.size(); ++i) - { - auto readouts = m_geoSvc->getDetector()->readouts(); - if (readouts.find(m_readoutNames.value().at(i)) == readouts.end()) - { - readoutMissing = true; - } - } - if (readoutMissing) + auto readouts = m_geoSvc->getDetector()->readouts(); + if (readouts.find(m_readoutNames.value().at(i)) == readouts.end()) { error() << "Missing readout, exiting!" << endmsg; return StatusCode::FAILURE; @@ -106,7 +99,7 @@ StatusCode CalibrateCaloClusters::initialize() return StatusCode::SUCCESS; } -StatusCode CalibrateCaloClusters::execute() +StatusCode CalibrateCaloClusters::execute(const EventContext& evtCtx) const { verbose() << "-------------------------------------------" << endmsg; @@ -140,23 +133,22 @@ StatusCode CalibrateCaloClusters::execute() StatusCode CalibrateCaloClusters::finalize() { - if (ortSession) - delete ortSession; - if (ortEnv) - delete ortEnv; + if (m_ortSession) + delete m_ortSession; + if (m_ortEnv) + delete m_ortEnv; - return GaudiAlgorithm::finalize(); + return Gaudi::Algorithm::finalize(); } edm4hep::ClusterCollection *CalibrateCaloClusters::initializeOutputClusters( - const edm4hep::ClusterCollection *inClusters) + const edm4hep::ClusterCollection *inClusters) const { edm4hep::ClusterCollection *outClusters = m_outClusters.createAndPut(); for (auto const &inCluster : *inClusters) { auto outCluster = inCluster.clone(); - // verbose() << "Cluster energy before calibration:" << outCluster.getEnergy() << endmsg; outClusters->push_back(outCluster); } @@ -170,31 +162,33 @@ StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibra MSG::Level outputLevel = this->msgStream().level(); switch (outputLevel) { - case MSG::Level::FATAL: // 6 + case MSG::Level::FATAL: // 6 loggingLevel = ORT_LOGGING_LEVEL_FATAL; // 4 break; - case MSG::Level::ERROR: // 5 + case MSG::Level::ERROR: // 5 loggingLevel = ORT_LOGGING_LEVEL_ERROR; // 3 break; - case MSG::Level::WARNING: // 4 + case MSG::Level::WARNING: // 4 loggingLevel = ORT_LOGGING_LEVEL_WARNING; // 2 break; - case MSG::Level::INFO: // 3 + case MSG::Level::INFO: // 3 loggingLevel = ORT_LOGGING_LEVEL_WARNING; // 2 (ORT_LOGGING_LEVEL_INFO too verbose..) break; - case MSG::Level::DEBUG: // 2 + case MSG::Level::DEBUG: // 2 loggingLevel = ORT_LOGGING_LEVEL_INFO; // 1 break; - case MSG::Level::VERBOSE: // 1 + case MSG::Level::VERBOSE: // 1 loggingLevel = ORT_LOGGING_LEVEL_VERBOSE; // 0 break; + default: + break; } try { - ortEnv = new Ort::Env(loggingLevel, "ONNX runtime environment"); + m_ortEnv = new Ort::Env(loggingLevel, "ONNX runtime environment"); Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(1); - ortSession = new Ort::Experimental::Session(*ortEnv, const_cast(calibrationFile), session_options); + m_ortSession = new Ort::Experimental::Session(*m_ortEnv, const_cast(calibrationFile), session_options); } catch (const Ort::Exception &exception) { @@ -205,21 +199,20 @@ StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibra // print name/shape of inputs // use default allocator (CPU) Ort::AllocatorWithDefaultOptions allocator; - debug() << "Input Node Name/Shape (" << input_names.size() << "):" << endmsg; - for (std::size_t i = 0; i < ortSession->GetInputCount(); i++) + debug() << "Input Node Name/Shape (" << m_input_names.size() << "):" << endmsg; + for (std::size_t i = 0; i < m_ortSession->GetInputCount(); i++) { - // input_names.emplace_back(ortSession->GetInputNameAllocated(i, allocator).get()); - input_names.emplace_back(ortSession->GetInputName(i, allocator)); - input_shapes = ortSession->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape(); - debug() << "\t" << input_names.at(i) << " : "; - for (std::size_t k = 0; k < input_shapes.size() - 1; k++) + m_input_names.emplace_back(m_ortSession->GetInputName(i, allocator)); + m_input_shapes = m_ortSession->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape(); + debug() << "\t" << m_input_names.at(i) << " : "; + for (std::size_t k = 0; k < m_input_shapes.size() - 1; k++) { - debug() << input_shapes[k] << "x"; + debug() << m_input_shapes[k] << "x"; } - debug() << input_shapes[input_shapes.size() - 1] << endmsg; + debug() << m_input_shapes[m_input_shapes.size() - 1] << endmsg; } // some models might have negative shape values to indicate dynamic shape, e.g., for variable batch size. - for (auto &s : input_shapes) + for (auto &s : m_input_shapes) { if (s < 0) { @@ -228,19 +221,17 @@ StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibra } // print name/shape of outputs - // std::vector output_names; - debug() << "Output Node Name/Shape (" << output_names.size() << "):" << endmsg; - for (std::size_t i = 0; i < ortSession->GetOutputCount(); i++) + debug() << "Output Node Name/Shape (" << m_output_names.size() << "):" << endmsg; + for (std::size_t i = 0; i < m_ortSession->GetOutputCount(); i++) { - // output_names.emplace_back(ortSession->GetOutputNameAllocated(i, allocator).get()); - output_names.emplace_back(ortSession->GetOutputName(i, allocator)); - output_shapes = ortSession->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape(); - debug() << "\t" << output_names.at(i) << " : "; - for (std::size_t k = 0; k < output_shapes.size() - 1; k++) + m_output_names.emplace_back(m_ortSession->GetOutputName(i, allocator)); + m_output_shapes = m_ortSession->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape(); + debug() << "\t" << m_output_names.at(i) << " : "; + for (std::size_t k = 0; k < m_output_shapes.size() - 1; k++) { - debug() << output_shapes[k] << "x"; + debug() << m_output_shapes[k] << "x"; } - debug() << output_shapes[output_shapes.size() - 1] << endmsg; + debug() << m_output_shapes[m_output_shapes.size() - 1] << endmsg; } // the output should be a single value (the correction) @@ -248,10 +239,10 @@ StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibra // the first dimension of the tensors are the number of clusters // to be calibrated simultaneously (-1 = dynamic) // we will calibrate once at a time - if (input_shapes.size() != 2 || - output_shapes.size() != 2 || - input_shapes[1] != (m_numLayersTotal + 1) || - output_shapes[1] != 1) + if (m_input_shapes.size() != 2 || + m_output_shapes.size() != 2 || + m_input_shapes[1] != (m_numLayersTotal + 1) || + m_output_shapes[1] != 1) { error() << "The input or output shapes in the calibration files do not match the expected architecture" << endmsg; return StatusCode::FAILURE; @@ -261,7 +252,7 @@ StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibra } StatusCode CalibrateCaloClusters::calibrateClusters(const edm4hep::ClusterCollection *inClusters, - edm4hep::ClusterCollection *outClusters) + edm4hep::ClusterCollection *outClusters) const { // this vector will contain the input features for the calibration @@ -298,18 +289,18 @@ StatusCode CalibrateCaloClusters::calibrateClusters(const edm4hep::ClusterCollec float corr = 1.0; // Create a single Ort tensor std::vector input_tensors; - input_tensors.emplace_back(vec_to_tensor(energiesInLayers, input_shapes)); + input_tensors.emplace_back(vec_to_tensor(energiesInLayers, m_input_shapes)); // double-check the dimensions of the input tensor - // assert(input_tensors[0].IsTensor() && input_tensors[0].GetTensorTypeAndShapeInfo().GetShape() == input_shapes); + // assert(input_tensors[0].IsTensor() && input_tensors[0].GetTensorTypeAndShapeInfo().GetShape() == m_input_shapes); // pass data through model try { - std::vector output_tensors = ortSession->Run(input_names, - input_tensors, - output_names, - Ort::RunOptions{nullptr}); + std::vector output_tensors = m_ortSession->Run(m_input_names, + input_tensors, + m_output_names, + Ort::RunOptions{nullptr}); // double-check the dimensions of the output tensors // NOTE: the number of output tensors is equal to the number of output nodes specifed in the Run() call @@ -333,7 +324,7 @@ StatusCode CalibrateCaloClusters::calibrateClusters(const edm4hep::ClusterCollec } void CalibrateCaloClusters::calcEnergiesInLayers(edm4hep::Cluster cluster, - std::vector &energiesInLayer) + std::vector &energiesInLayer) const { // reset vector with energies per layer std::fill(energiesInLayer.begin(), energiesInLayer.end(), 0.0); diff --git a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h index 3626afb0..f5d8a13e 100644 --- a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h +++ b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.h @@ -5,7 +5,7 @@ #include "k4FWCore/DataHandle.h" // Gaudi -#include "GaudiAlg/GaudiAlgorithm.h" +#include "GaudiKernel/Algorithm.h" #include "GaudiKernel/ToolHandle.h" #include "GaudiKernel/MsgStream.h" class IGeoSvc; @@ -34,16 +34,16 @@ namespace dd4hep { * @author Giovanni Marchiori */ -class CalibrateCaloClusters : public GaudiAlgorithm { +class CalibrateCaloClusters : public Gaudi::Algorithm { public: CalibrateCaloClusters(const std::string& name, ISvcLocator* svcLoc); - StatusCode initialize(); + virtual StatusCode initialize(); - StatusCode execute(); + virtual StatusCode execute(const EventContext& evtCtx) const; - StatusCode finalize(); + virtual StatusCode finalize(); private: /** @@ -53,7 +53,7 @@ class CalibrateCaloClusters : public GaudiAlgorithm { * * @return Pointer to the output cluster collection. */ - edm4hep::ClusterCollection* initializeOutputClusters(const edm4hep::ClusterCollection* inClusters); + edm4hep::ClusterCollection* initializeOutputClusters(const edm4hep::ClusterCollection* inClusters) const; /** * Initialize vectors of upstream and downstream correction functions. @@ -71,7 +71,7 @@ class CalibrateCaloClusters : public GaudiAlgorithm { * @return Status code. */ StatusCode calibrateClusters(const edm4hep::ClusterCollection* inClusters, - edm4hep::ClusterCollection* outClusters); + edm4hep::ClusterCollection* outClusters) const; /** * Get sum of energy from cells in each layer. @@ -81,15 +81,15 @@ class CalibrateCaloClusters : public GaudiAlgorithm { * @param[out] energiesInLayer Reference to vector that will contain the energies */ void calcEnergiesInLayers(edm4hep::Cluster cluster, - std::vector& energiesInLayer); + std::vector& energiesInLayer) const; /// Handle for input calorimeter clusters collection - DataHandle m_inClusters { + mutable DataHandle m_inClusters { "inClusters", Gaudi::DataHandle::Reader, this }; /// Handle for corrected (output) calorimeter clusters collection - DataHandle m_outClusters { + mutable DataHandle m_outClusters { "outClusters", Gaudi::DataHandle::Writer, this }; @@ -131,12 +131,12 @@ class CalibrateCaloClusters : public GaudiAlgorithm { // the ONNX runtime session for applying the calibration, // the environment, and the input and output shapes and names - Ort::Experimental::Session* ortSession = nullptr; - Ort::Env* ortEnv = nullptr; - std::vector input_shapes; - std::vector output_shapes; - std::vector input_names; - std::vector output_names; + Ort::Experimental::Session* m_ortSession = nullptr; + Ort::Env* m_ortEnv = nullptr; + std::vector m_input_shapes; + std::vector m_output_shapes; + std::vector m_input_names; + std::vector m_output_names; }; #endif /* RECFCCEECALORIMETER_CALIBRATECALOCLUSTERS_H */ From d29850f9f2529c153c345615f6ff2b34437e9b4c Mon Sep 17 00:00:00 2001 From: Giovanni Marchiori Date: Wed, 28 Feb 2024 17:10:15 +0100 Subject: [PATCH 4/6] implement comments from PR review --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b779add0..47860c00 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ source /cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh * k4FWCore * DD4hep * k4geo - * ONNXRuntime From c0283a029517b013249c8a8b2f64e9b8fa10457a Mon Sep 17 00:00:00 2001 From: Giovanni Marchiori Date: Wed, 28 Feb 2024 17:40:29 +0100 Subject: [PATCH 5/6] fix a couple of warnings --- RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp index fc6e2af6..01b408bb 100644 --- a/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp +++ b/RecFCCeeCalorimeter/src/components/CalibrateCaloClusters.cpp @@ -101,6 +101,8 @@ StatusCode CalibrateCaloClusters::initialize() StatusCode CalibrateCaloClusters::execute(const EventContext& evtCtx) const { + (void) evtCtx; // event context not used + verbose() << "-------------------------------------------" << endmsg; // Get the input collection with clusters @@ -241,7 +243,7 @@ StatusCode CalibrateCaloClusters::readCalibrationFile(const std::string &calibra // we will calibrate once at a time if (m_input_shapes.size() != 2 || m_output_shapes.size() != 2 || - m_input_shapes[1] != (m_numLayersTotal + 1) || + m_input_shapes[1] != ((int)(m_numLayersTotal + 1)) || m_output_shapes[1] != 1) { error() << "The input or output shapes in the calibration files do not match the expected architecture" << endmsg; From 665a66793830438a97a9223ffb4a3e3aed208cdf Mon Sep 17 00:00:00 2001 From: Giovanni Marchiori Date: Wed, 28 Feb 2024 17:41:35 +0100 Subject: [PATCH 6/6] get rid of a warning from another class (not related to this PR..) --- RecCalorimeter/src/components/CorrectCaloClusters.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RecCalorimeter/src/components/CorrectCaloClusters.cpp b/RecCalorimeter/src/components/CorrectCaloClusters.cpp index adb1bccb..939c7d99 100644 --- a/RecCalorimeter/src/components/CorrectCaloClusters.cpp +++ b/RecCalorimeter/src/components/CorrectCaloClusters.cpp @@ -182,9 +182,9 @@ edm4hep::ClusterCollection* CorrectCaloClusters::initializeOutputClusters( for (auto const& inCluster: *inClusters) { auto outCluster = inCluster.clone(); verbose() << "Cluster position:" << endmsg; - verbose() << " x: " << outCluster.position().x << endmsg; - verbose() << " y: " << outCluster.position().y << endmsg; - verbose() << " z: " << outCluster.position().z << endmsg; + verbose() << " x: " << outCluster.getPosition().x << endmsg; + verbose() << " y: " << outCluster.getPosition().y << endmsg; + verbose() << " z: " << outCluster.getPosition().z << endmsg; outClusters->push_back(outCluster); }