diff --git a/CondCore/HGCalPlugins/plugins/BuildFile.xml b/CondCore/HGCalPlugins/plugins/BuildFile.xml new file mode 100644 index 0000000000000..da818d03b1241 --- /dev/null +++ b/CondCore/HGCalPlugins/plugins/BuildFile.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/CondCore/HGCalPlugins/plugins/plugin.cc b/CondCore/HGCalPlugins/plugins/plugin.cc new file mode 100644 index 0000000000000..fecb0b15e58dc --- /dev/null +++ b/CondCore/HGCalPlugins/plugins/plugin.cc @@ -0,0 +1,5 @@ +#include "CondCore/ESSources/interface/registration_macros.h" +#include "CondFormats/DataRecord/interface/HGCalCalibrationRcd.h" +#include "CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h" + +REGISTER_PLUGIN(HGCalCalibrationRcd, HGCalCalibrationPayload); diff --git a/CondFormats/DataRecord/interface/HGCalCalibrationRcd.h b/CondFormats/DataRecord/interface/HGCalCalibrationRcd.h new file mode 100644 index 0000000000000..982d16d3295fe --- /dev/null +++ b/CondFormats/DataRecord/interface/HGCalCalibrationRcd.h @@ -0,0 +1,20 @@ +#ifndef CondFormats_DataRecord_HGCalCalibrationRcd_h +#define CondFormats_DataRecord_HGCalCalibrationRcd_h + +#include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h" +#include "FWCore/Framework/interface/DependentRecordImplementation.h" +#include "FWCore/Utilities/interface/mplVector.h" + +/** + * EventSetup record carrying the persistent HGCal calibration payload. + * + * It depends on the electronics mapping record because the typed + * calibration payload must be expanded into the channel-indexed + * HGCalCalibParamHost representation. + */ +class HGCalCalibrationRcd + : public edm::eventsetup::DependentRecordImplementation< + HGCalCalibrationRcd, + edm::mpl::Vector> {}; + +#endif diff --git a/CondFormats/DataRecord/src/HGCalCalibrationRcd.cc b/CondFormats/DataRecord/src/HGCalCalibrationRcd.cc new file mode 100644 index 0000000000000..51c0afc36f1aa --- /dev/null +++ b/CondFormats/DataRecord/src/HGCalCalibrationRcd.cc @@ -0,0 +1,4 @@ +#include "CondFormats/DataRecord/interface/HGCalCalibrationRcd.h" +#include "FWCore/Framework/interface/eventsetuprecord_registration_macro.h" + +EVENTSETUP_RECORD_REG(HGCalCalibrationRcd); diff --git a/CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h b/CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h new file mode 100644 index 0000000000000..aefa92c33a5aa --- /dev/null +++ b/CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h @@ -0,0 +1,67 @@ +#ifndef CondFormats_HGCalObjects_HGCalCalibrationPayload_h +#define CondFormats_HGCalObjects_HGCalCalibrationPayload_h + +#include "CondFormats/Serialization/interface/Serializable.h" + +#include +#include + +/** + * Channel-level calibration constants belonging to one HGCal module + * typecode. + * + * TOA correction vectors are stored in flattened form: + * toaCTDC.size() = number of channels * 32 + * toaFTDC.size() = number of channels * 8 + * toaTW.size() = number of channels * 3 + */ +struct HGCalCalibrationModulePayload { + static constexpr unsigned int kCTDCSize = 32; + static constexpr unsigned int kFTDCSize = 8; + static constexpr unsigned int kTWSize = 3; + + std::string typecode; + + std::vector channel; + + std::vector adcPed; + std::vector noise; + std::vector cmPed; + std::vector cmSlope; + + std::vector bxm1Slope; + std::vector bxm1Ped; + + std::vector totToADC; + std::vector totPed; + std::vector totLin; + std::vector totP0; + std::vector totP1; + std::vector totP2; + + std::vector toaCTDC; + std::vector toaFTDC; + std::vector toaTW; + + std::vector mipsScale; + std::vector valid; + + COND_SERIALIZABLE; +}; + +/** + * Persistent HGCal calibration conditions payload. + * + * This object contains typed C++ calibration data. It does not contain + * JSON text. + */ +class HGCalCalibrationPayload { +public: + HGCalCalibrationPayload() = default; + + std::vector modules; + + COND_SERIALIZABLE; +}; + +#endif diff --git a/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationPayload.cc b/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationPayload.cc new file mode 100644 index 0000000000000..42aa51889c9ff --- /dev/null +++ b/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationPayload.cc @@ -0,0 +1,4 @@ +#include "CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h" +#include "FWCore/Utilities/interface/typelookup.h" + +TYPELOOKUP_DATA_REG(HGCalCalibrationPayload); diff --git a/CondFormats/HGCalObjects/src/classes_def.xml b/CondFormats/HGCalObjects/src/classes_def.xml index 6aa6fe55399bd..9350eac0ff2f3 100644 --- a/CondFormats/HGCalObjects/src/classes_def.xml +++ b/CondFormats/HGCalObjects/src/classes_def.xml @@ -72,4 +72,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CondFormats/HGCalObjects/src/headers.h b/CondFormats/HGCalObjects/src/headers.h index e0761918129a0..df07544e7e34d 100644 --- a/CondFormats/HGCalObjects/src/headers.h +++ b/CondFormats/HGCalObjects/src/headers.h @@ -1,3 +1,4 @@ +#include "CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h" #include "CondFormats/HGCalObjects/interface/HGCalConfiguration.h" #include "CondFormats/HGCalObjects/interface/HGCalDenseIndexerBase.h" #include "CondFormats/HGCalObjects/interface/HGCalMappingCellIndexer.h" diff --git a/CondFormats/HGCalObjects/test/testSerializationHGCalCondDataFormats.cc b/CondFormats/HGCalObjects/test/testSerializationHGCalCondDataFormats.cc index 6196c781d3ebf..c963f39e8f96e 100644 --- a/CondFormats/HGCalObjects/test/testSerializationHGCalCondDataFormats.cc +++ b/CondFormats/HGCalObjects/test/testSerializationHGCalCondDataFormats.cc @@ -2,6 +2,10 @@ #include "CondFormats/HGCalObjects/src/headers.h" int main() { + // calibration conditions + testSerialization(); + testSerialization(); + //dense indexers testSerialization(); testSerialization(); diff --git a/CondTools/HGCal/BuildFile.xml b/CondTools/HGCal/BuildFile.xml new file mode 100644 index 0000000000000..f14762dcd5d9f --- /dev/null +++ b/CondTools/HGCal/BuildFile.xml @@ -0,0 +1,4 @@ + + + + diff --git a/CondTools/HGCal/plugins/BuildFile.xml b/CondTools/HGCal/plugins/BuildFile.xml new file mode 100644 index 0000000000000..a228fb281f867 --- /dev/null +++ b/CondTools/HGCal/plugins/BuildFile.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/CondTools/HGCal/plugins/HGCalCalibrationDBWriter.cc b/CondTools/HGCal/plugins/HGCalCalibrationDBWriter.cc new file mode 100644 index 0000000000000..84723673b346d --- /dev/null +++ b/CondTools/HGCal/plugins/HGCalCalibrationDBWriter.cc @@ -0,0 +1,406 @@ +#include "CondCore/DBOutputService/interface/PoolDBOutputService.h" +#include "CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h" + +#include "FWCore/Framework/interface/Event.h" +#include "FWCore/Framework/interface/EventSetup.h" +#include "FWCore/Framework/interface/MakerMacros.h" +#include "FWCore/Framework/interface/global/EDAnalyzer.h" +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/ParameterSet/interface/ParameterSetDescription.h" +#include "FWCore/ServiceRegistry/interface/Service.h" +#include "FWCore/Utilities/interface/Exception.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + + using json = nlohmann::ordered_json; + + void checkModuleObject(const json& moduleData, const std::string& typecode) { + if (!moduleData.is_object()) { + throw cms::Exception("InvalidData") + << "Calibration entry for module '" << typecode + << "' is not a JSON object."; + } + } + + std::vector readFloatVector(const json& moduleData, + const std::string& key, + std::size_t nrows, + const std::string& typecode, + bool required = true) { + if (!moduleData.contains(key)) { + if (required) { + throw cms::Exception("InvalidData") + << "Missing required key '" << key + << "' for module '" << typecode << "'."; + } + + return std::vector(nrows, 0.f); + } + + if (!moduleData.at(key).is_array()) { + throw cms::Exception("InvalidData") + << "Field '" << key << "' for module '" << typecode + << "' is not an array."; + } + + auto values = moduleData.at(key).get>(); + + if (values.size() < nrows) { + throw cms::Exception("InvalidData") + << "Field '" << key << "' for module '" << typecode + << "' contains only " << values.size() + << " values, but at least " << nrows << " are required."; + } + + if (values.size() > nrows) { + edm::LogWarning("HGCalCalibrationDBWriter") + << "Field '" << key << "' for module '" << typecode + << "' contains " << values.size() << " values; using the first " + << nrows << " to preserve the existing JSON reconstruction behaviour."; + values.resize(nrows); + } + + return values; + } + + std::vector readChannelVector(const json& moduleData, + std::size_t nrows, + const std::string& typecode) { + if (!moduleData.contains("Channel")) { + std::vector channels(nrows); + std::iota(channels.begin(), channels.end(), 0); + return channels; + } + + if (!moduleData.at("Channel").is_array()) { + throw cms::Exception("InvalidData") + << "Field 'Channel' for module '" << typecode + << "' is not an array."; + } + + auto channels = moduleData.at("Channel").get>(); + + if (channels.size() < nrows) { + throw cms::Exception("InvalidData") + << "Field 'Channel' for module '" << typecode + << "' contains only " << channels.size() + << " values, but at least " << nrows << " are required."; + } + + if (channels.size() > nrows) { + edm::LogWarning("HGCalCalibrationDBWriter") + << "Field 'Channel' for module '" << typecode + << "' contains " << channels.size() << " values; using the first " + << nrows << "."; + channels.resize(nrows); + } + + return channels; + } + + std::vector readValidVector(const json& moduleData, + std::size_t nrows, + const std::string& typecode) { + if (!moduleData.contains("Valid") || + !moduleData.at("Valid").is_array()) { + throw cms::Exception("InvalidData") + << "Missing or invalid 'Valid' array for module '" + << typecode << "'."; + } + + const auto input = moduleData.at("Valid").get>(); + + if (input.size() < nrows) { + throw cms::Exception("InvalidData") + << "Field 'Valid' for module '" << typecode + << "' contains only " << input.size() + << " values, but at least " << nrows << " are required."; + } + + if (input.size() > nrows) { + edm::LogWarning("HGCalCalibrationDBWriter") + << "Field 'Valid' for module '" << typecode + << "' contains " << input.size() << " values; using the first " + << nrows << "."; + } + + std::vector output; + output.reserve(nrows); + + for (std::size_t channel = 0; channel < nrows; ++channel) { + const int value = input[channel]; + + if (value != 0 && value != 1) { + throw cms::Exception("InvalidData") + << "Field 'Valid' for module '" << typecode + << "', channel index " << channel + << " has value " << value << "; expected 0 or 1."; + } + + output.push_back(static_cast(value)); + } + + return output; + } + + std::vector readFlattenedMatrix(const json& moduleData, + const std::string& key, + std::size_t nrows, + std::size_t width, + const std::string& typecode, + bool required = false) { + if (!moduleData.contains(key)) { + if (required) { + throw cms::Exception("InvalidData") + << "Missing required key '" << key + << "' for module '" << typecode << "'."; + } + + return std::vector(nrows * width, 0.f); + } + + const auto& matrix = moduleData.at(key); + + if (!matrix.is_array() || matrix.size() < nrows) { + throw cms::Exception("InvalidData") + << "Field '" << key << "' for module '" << typecode + << "' must contain at least " << nrows << " rows."; + } + + if (matrix.size() > nrows) { + edm::LogWarning("HGCalCalibrationDBWriter") + << "Field '" << key << "' for module '" << typecode + << "' contains " << matrix.size() << " rows; using the first " + << nrows << "."; + } + + std::vector flattened; + flattened.reserve(nrows * width); + + for (std::size_t rowIndex = 0; rowIndex < nrows; ++rowIndex) { + const auto& row = matrix.at(rowIndex); + + if (!row.is_array() || row.size() != width) { + throw cms::Exception("InvalidData") + << "Field '" << key << "' for module '" << typecode + << "', row " << rowIndex << " contains " + << row.size() << " values; expected " << width << "."; + } + + const auto rowValues = row.get>(); + flattened.insert(flattened.end(), + rowValues.begin(), + rowValues.end()); + } + + return flattened; + } + +} // namespace + +class HGCalCalibrationDBWriter : public edm::global::EDAnalyzer<> { +public: + explicit HGCalCalibrationDBWriter(const edm::ParameterSet&); + ~HGCalCalibrationDBWriter() override = default; + + static void fillDescriptions(edm::ConfigurationDescriptions&); + +private: + void analyze(edm::StreamID, + const edm::Event&, + const edm::EventSetup&) const override {} + + void endJob() override; + + std::string recordName_; + HGCalCalibrationPayload payload_; +}; + +HGCalCalibrationDBWriter::HGCalCalibrationDBWriter( + const edm::ParameterSet& config) + : recordName_(config.getParameter("recordName")) { + const std::string jsonFile = + config.getParameter("jsonFile"); + + std::unique_ptr fileInput; + std::istream* input = &std::cin; + + if (jsonFile != "-") { + fileInput = std::make_unique(jsonFile); + + if (!fileInput->is_open()) { + throw cms::Exception("FileOpenError") + << "Could not open calibration JSON file '" + << jsonFile << "'."; + } + + input = fileInput.get(); + } + + json calibrationData; + + try { + calibrationData = + json::parse(*input, nullptr, true, /*ignore_comments=*/true); + } catch (const json::exception& error) { + throw cms::Exception("InvalidData") + << "Failed to parse calibration JSON from '" + << jsonFile << "': " << error.what(); + } + + if (!calibrationData.is_object() || calibrationData.empty()) { + throw cms::Exception("InvalidData") + << "The calibration input must be a non-empty JSON object."; + } + + payload_.modules.reserve(calibrationData.size()); + + for (const auto& item : calibrationData.items()) { + const std::string typecode = item.key(); + const json& moduleData = item.value(); + + checkModuleObject(moduleData, typecode); + + if (!moduleData.contains("ADC_ped") || + !moduleData.at("ADC_ped").is_array()) { + throw cms::Exception("InvalidData") + << "Missing or invalid 'ADC_ped' array for module '" + << typecode << "'."; + } + + // Match the existing JSON ESProducer: the first JSON field determines + // the number of rows used for this module. + const auto firstField = moduleData.begin(); + + if (firstField == moduleData.end() || !firstField.value().is_array()) { + throw cms::Exception("InvalidData") + << "The first field for module '" << typecode + << "' must be a channel-level array."; + } + + const std::size_t nrows = firstField.value().size(); + + if (nrows == 0) { + throw cms::Exception("InvalidData") + << "Module '" << typecode << "' contains no channels."; + } + + HGCalCalibrationModulePayload module; + module.typecode = typecode; + + module.channel = readChannelVector(moduleData, nrows, typecode); + + module.adcPed = + readFloatVector(moduleData, "ADC_ped", nrows, typecode); + module.noise = + readFloatVector(moduleData, "Noise", nrows, typecode); + module.cmPed = + readFloatVector(moduleData, "CM_ped", nrows, typecode); + module.cmSlope = + readFloatVector(moduleData, "CM_slope", nrows, typecode); + + module.bxm1Slope = + readFloatVector(moduleData, "BXm1_slope", nrows, typecode); + module.bxm1Ped = + readFloatVector(moduleData, + "BXm1_ped", + nrows, + typecode, + false); + + module.totToADC = + readFloatVector(moduleData, "TOTtoADC", nrows, typecode); + module.totPed = + readFloatVector(moduleData, "TOT_ped", nrows, typecode); + module.totLin = + readFloatVector(moduleData, "TOT_lin", nrows, typecode); + module.totP0 = + readFloatVector(moduleData, "TOT_P0", nrows, typecode); + module.totP1 = + readFloatVector(moduleData, "TOT_P1", nrows, typecode); + module.totP2 = + readFloatVector(moduleData, "TOT_P2", nrows, typecode); + + module.toaCTDC = readFlattenedMatrix( + moduleData, + "TOA_CTDC", + nrows, + HGCalCalibrationModulePayload::kCTDCSize, + typecode); + + module.toaFTDC = readFlattenedMatrix( + moduleData, + "TOA_FTDC", + nrows, + HGCalCalibrationModulePayload::kFTDCSize, + typecode); + + module.toaTW = readFlattenedMatrix( + moduleData, + "TOA_TW", + nrows, + HGCalCalibrationModulePayload::kTWSize, + typecode); + + module.mipsScale = + readFloatVector(moduleData, "MIPS_scale", nrows, typecode); + + module.valid = + readValidVector(moduleData, nrows, typecode); + + payload_.modules.push_back(std::move(module)); + } + + edm::LogInfo("HGCalCalibrationDBWriter") + << "Parsed typed calibration payload for " + << payload_.modules.size() << " module typecodes."; +} + +void HGCalCalibrationDBWriter::endJob() { + edm::Service poolDBService; + + if (!poolDBService.isAvailable()) { + throw cms::Exception("Configuration") + << "PoolDBOutputService is not configured."; + } + + poolDBService->createOneIOV( + payload_, + poolDBService->beginOfTime(), + recordName_); + + edm::LogInfo("HGCalCalibrationDBWriter") + << "Stored HGCalCalibrationPayload in record '" + << recordName_ << "'."; +} + +void HGCalCalibrationDBWriter::fillDescriptions( + edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription description; + + description + .add("jsonFile", "-") + ->setComment( + "Calibration JSON input file; use '-' to read from stdin."); + + description + .add("recordName", "HGCalCalibrationRcd") + ->setComment("Conditions record used by PoolDBOutputService."); + + descriptions.addWithDefaultLabel(description); +} + +DEFINE_FWK_MODULE(HGCalCalibrationDBWriter); diff --git a/CondTools/HGCal/test/writeHGCalCalibration_cfg.py b/CondTools/HGCal/test/writeHGCalCalibration_cfg.py new file mode 100644 index 0000000000000..53da1845c93e2 --- /dev/null +++ b/CondTools/HGCal/test/writeHGCalCalibration_cfg.py @@ -0,0 +1,75 @@ +import FWCore.ParameterSet.Config as cms +from FWCore.ParameterSet.VarParsing import VarParsing +from CondCore.CondDB.CondDB_cfi import CondDB + +options = VarParsing("analysis") + +options.register( + "jsonFile", + "-", + VarParsing.multiplicity.singleton, + VarParsing.varType.string, + "Calibration JSON input; use '-' for stdin", +) + +options.register( + "outputDB", + "sqlite_file:hgcal_calibration.db", + VarParsing.multiplicity.singleton, + VarParsing.varType.string, + "Output SQLite connection string", +) + +options.register( + "record", + "HGCalCalibrationRcd", + VarParsing.multiplicity.singleton, + VarParsing.varType.string, + "Conditions record name", +) + +options.register( + "outputTag", + "HGCalCalibration_v1", + VarParsing.multiplicity.singleton, + VarParsing.varType.string, + "Conditions tag name", +) + +options.parseArguments() + +process = cms.Process("WRITECALIB") + +process.load("FWCore.MessageLogger.MessageLogger_cfi") + +process.PoolDBOutputService = cms.Service( + "PoolDBOutputService", + CondDB.clone(connect=cms.string(options.outputDB)), + timetype=cms.untracked.string("runnumber"), + toPut=cms.VPSet( + cms.PSet( + record=cms.string(options.record), + tag=cms.string(options.outputTag), + ) + ), + loadBlobStreamer=cms.untracked.bool(False), +) + +process.source = cms.Source( + "EmptySource", + firstRun=cms.untracked.uint32(1), +) + +process.maxEvents = cms.untracked.PSet( + input=cms.untracked.int32(1) +) + +process.hgcalCalibrationDBWriter = cms.EDAnalyzer( + "HGCalCalibrationDBWriter", + jsonFile=cms.string(options.jsonFile), + recordName=cms.string(options.record), +) + +process.writeCalibration = cms.Path( + process.hgcalCalibrationDBWriter +) diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationDBESProducer.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationDBESProducer.cc new file mode 100644 index 0000000000000..852d40a3147fc --- /dev/null +++ b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationDBESProducer.cc @@ -0,0 +1,582 @@ +// HGCal calibration conditions reader: +// HGCalCalibrationPayload from CondDB -> HGCalCalibParamHost + +#include "FWCore/MessageLogger/interface/MessageLogger.h" +#include "FWCore/ParameterSet/interface/FileInPath.h" +#include "FWCore/ParameterSet/interface/ParameterSet.h" +#include "FWCore/Utilities/interface/Exception.h" +#include "FWCore/Utilities/interface/RegexMatch.h" + +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/ESGetToken.h" +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/ESProducer.h" +#include "HeterogeneousCore/AlpakaCore/interface/alpaka/ModuleFactory.h" +#include "HeterogeneousCore/AlpakaInterface/interface/config.h" +#include "HeterogeneousCore/AlpakaInterface/interface/host.h" +#include "HeterogeneousCore/AlpakaInterface/interface/memory.h" + +#include "CondFormats/DataRecord/interface/HGCalCalibrationRcd.h" +#include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h" + +#include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h" +#include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h" +#include "CondFormats/HGCalObjects/interface/HGCalCalibrationPayload.h" +#include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h" +#include "CondFormats/HGCalObjects/interface/HGCalMappingParameterHost.h" + +#include "DataFormats/ForwardDetId/interface/HGCSiliconDetId.h" +#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace ALPAKA_ACCELERATOR_NAMESPACE { + + namespace hgcalrechit { + + using namespace ::hgcal; + + namespace { + + const HGCalCalibrationModulePayload& findModulePayload( + const std::string& moduleTypecode, + const HGCalCalibrationPayload& payload) { + if (payload.modules.empty()) { + throw cms::Exception("InvalidData") + << "HGCalCalibrationPayload contains no module entries."; + } + + // Preserve the JSON search_modkey behaviour: + // first matching typecode or glob pattern wins. + for (const auto& module : payload.modules) { + const std::regex expression(edm::glob2reg(module.typecode)); + + if (std::regex_match(moduleTypecode, expression)) { + edm::LogInfo("HGCalCalibrationDBESProducer") + << "Matched module '" << moduleTypecode + << "' to conditions payload key '" + << module.typecode << "'."; + + return module; + } + } + + throw cms::Exception("InvalidData") + << "Could not find a calibration payload matching module '" + << moduleTypecode << "'."; + } + + template + void checkVectorSize(const std::vector& values, + std::size_t expected, + const std::string& field, + const std::string& typecode) { + if (values.size() != expected) { + throw cms::Exception("InvalidData") + << "Conditions field '" << field << "' for module '" + << typecode << "' contains " << values.size() + << " entries; expected " << expected << "."; + } + } + + void checkFlattenedMatrixSize(const std::vector& values, + std::size_t rows, + std::size_t width, + const std::string& field, + const std::string& typecode) { + const std::size_t expected = rows * width; + + if (values.size() != expected) { + throw cms::Exception("InvalidData") + << "Conditions field '" << field << "' for module '" + << typecode << "' contains " << values.size() + << " flattened values; expected " << expected + << " (" << rows << " x " << width << ")."; + } + } + + template + void fillFlattenedRow(EigenRow& output, + const std::vector& values, + std::size_t row, + std::size_t width) { + const std::size_t offset = row * width; + + for (std::size_t column = 0; column < width; ++column) { + output(column) = values[offset + column]; + } + } + + } // namespace + + class HGCalCalibrationDBESProducer : public ESProducer { + public: + explicit HGCalCalibrationDBESProducer( + const edm::ParameterSet& configuration) + : ESProducer(configuration), + filenameEnergy_( + configuration.getParameter( + "filenameEnergyLoss")) { + auto collector = setWhatProduced(this); + + payloadToken_ = + collector.consumes( + configuration.getParameter( + "payloadSource")); + + indexToken_ = + collector.consumesFrom( + configuration.getParameter( + "indexSource")); + + mapToken_ = + collector.consumesFrom< + ::hgcal::HGCalMappingModuleParamHost, + HGCalElectronicsMappingRcd>( + configuration.getParameter( + "mapSource")); + } + + static void fillDescriptions( + edm::ConfigurationDescriptions& descriptions) { + edm::ParameterSetDescription description; + + description + .add("filenameEnergyLoss") + ->setComment( + "JSON file containing energy-loss and thickness " + "corrections."); + + description + .add( + "payloadSource", + edm::ESInputTag("")) + ->setComment( + "HGCalCalibrationPayload from HGCalCalibrationRcd."); + + description + .add( + "indexSource", + edm::ESInputTag("")) + ->setComment("HGCal module indexer source."); + + description + .add( + "mapSource", + edm::ESInputTag("")) + ->setComment("HGCal module mapping source."); + + descriptions.addWithDefaultLabel(description); + } + + std::optional<::hgcalrechit::HGCalCalibParamHost> produce( + const HGCalCalibrationRcd& record) { + const auto& calibrationPayload = + record.get(payloadToken_); + + const auto& moduleIndexer = + record.get(indexToken_); + + const auto& moduleMapper = + record.get(mapToken_); + + const uint32_t totalChannels = + moduleIndexer.maxDataSize(); + + ::hgcalrechit::HGCalCalibParamHost product( + cms::alpakatools::host(), + totalChannels); + + std::ifstream energyFile( + filenameEnergy_.fullPath().c_str()); + + if (!energyFile.is_open()) { + throw cms::Exception("FileOpenError") + << "Could not open energy-loss file '" + << filenameEnergy_.fullPath() << "'."; + } + + json energyData = + json::parse( + energyFile, + nullptr, + true, + /*ignore_comments=*/true); + + const std::vector energyKeys = { + "dEdx", + "SF_thickness_Si", + "SF_thickness_SiPM"}; + + check_keys( + energyData, + energyKeys, + filenameEnergy_.fullPath()); + + const auto energyLosses = + energyData["dEdx"].get>(); + + if (energyLosses.size() != 47) { + edm::LogError("HGCalCalibrationDBESProducer") + << "Expected 47 energy-loss layers, but found " + << energyLosses.size() << " in " + << filenameEnergy_.fullPath() << "."; + } + + for (const auto& [moduleTypecode, ids] : + moduleIndexer.typecodeMap()) { + const auto [fedId, moduleId] = ids; + + const auto& module = + findModulePayload( + moduleTypecode, + calibrationPayload); + + const uint32_t moduleIndex = + moduleIndexer.getIndexForModule( + fedId, + moduleId); + + const uint32_t offset = + moduleIndexer.getIndexForModuleData( + moduleTypecode); + + const uint32_t mappedChannels = + moduleIndexer.getNumChannels( + moduleTypecode); + + uint32_t rows = + static_cast( + module.adcPed.size()); + + if (mappedChannels != rows) { + edm::LogWarning( + "HGCalCalibrationDBESProducer") + << "Channel count differs for module '" + << moduleTypecode << "': mapping has " + << mappedChannels + << ", conditions payload has " + << rows << "."; + + rows = std::min(rows, mappedChannels); + } + + checkVectorSize( + module.noise, + module.adcPed.size(), + "Noise", + module.typecode); + + checkVectorSize( + module.cmSlope, + module.adcPed.size(), + "CM_slope", + module.typecode); + + checkVectorSize( + module.cmPed, + module.adcPed.size(), + "CM_ped", + module.typecode); + + checkVectorSize( + module.bxm1Slope, + module.adcPed.size(), + "BXm1_slope", + module.typecode); + + checkVectorSize( + module.totToADC, + module.adcPed.size(), + "TOTtoADC", + module.typecode); + + checkVectorSize( + module.totPed, + module.adcPed.size(), + "TOT_ped", + module.typecode); + + checkVectorSize( + module.totLin, + module.adcPed.size(), + "TOT_lin", + module.typecode); + + checkVectorSize( + module.totP0, + module.adcPed.size(), + "TOT_P0", + module.typecode); + + checkVectorSize( + module.totP1, + module.adcPed.size(), + "TOT_P1", + module.typecode); + + checkVectorSize( + module.totP2, + module.adcPed.size(), + "TOT_P2", + module.typecode); + + checkVectorSize( + module.mipsScale, + module.adcPed.size(), + "MIPS_scale", + module.typecode); + + checkVectorSize( + module.valid, + module.adcPed.size(), + "Valid", + module.typecode); + + checkFlattenedMatrixSize( + module.toaCTDC, + module.adcPed.size(), + HGCalCalibrationModulePayload::kCTDCSize, + "TOA_CTDC", + module.typecode); + + checkFlattenedMatrixSize( + module.toaFTDC, + module.adcPed.size(), + HGCalCalibrationModulePayload::kFTDCSize, + "TOA_FTDC", + module.typecode); + + checkFlattenedMatrixSize( + module.toaTW, + module.adcPed.size(), + HGCalCalibrationModulePayload::kTWSize, + "TOA_TW", + module.typecode); + + fill_SoA_column( + product.view().ADC_ped(), + module.adcPed, + offset, + rows); + + fill_SoA_column( + product.view().Noise(), + module.noise, + offset, + rows); + + fill_SoA_column( + product.view().CM_slope(), + module.cmSlope, + offset, + rows); + + fill_SoA_column( + product.view().CM_ped(), + module.cmPed, + offset, + rows); + + fill_SoA_column( + product.view().BXm1_slope(), + module.bxm1Slope, + offset, + rows); + + fill_SoA_column( + product.view().TOTtoADC(), + module.totToADC, + offset, + rows); + + fill_SoA_column( + product.view().TOT_ped(), + module.totPed, + offset, + rows); + + fill_SoA_column( + product.view().TOT_lin(), + module.totLin, + offset, + rows); + + fill_SoA_column( + product.view().TOT_P0(), + module.totP0, + offset, + rows); + + fill_SoA_column( + product.view().TOT_P1(), + module.totP1, + offset, + rows); + + fill_SoA_column( + product.view().TOT_P2(), + module.totP2, + offset, + rows); + + fill_SoA_column( + product.view().MIPS_scale(), + module.mipsScale, + offset, + rows); + + fill_SoA_column( + product.view().valid(), + module.valid, + offset, + rows); + + for (std::size_t channel = 0; + channel < rows; + ++channel) { + auto output = + product.view()[offset + channel]; + + fillFlattenedRow( + output.TOA_CTDC(), + module.toaCTDC, + channel, + HGCalCalibrationModulePayload::kCTDCSize); + + fillFlattenedRow( + output.TOA_FTDC(), + module.toaFTDC, + channel, + HGCalCalibrationModulePayload::kFTDCSize); + + fillFlattenedRow( + output.TOA_TW(), + module.toaTW, + channel, + HGCalCalibrationModulePayload::kTWSize); + } + + const int layer = + moduleMapper.view().plane()[moduleIndex]; + + if (layer <= 0 || + static_cast(layer) > + energyLosses.size()) { + throw cms::Exception("InvalidData") + << "Invalid HGCal layer " << layer + << " for module '" << moduleTypecode + << "'."; + } + + float energyLoss = + energyLosses[layer - 1]; + + const bool isSiPM = + moduleMapper.view().isSiPM()[moduleIndex]; + + const int cellType = + moduleMapper.view().celltype()[moduleIndex]; + + const uint32_t detectorId = + moduleMapper.view().detid()[moduleIndex]; + + float thicknessScale = 0.f; + + if (isSiPM) { + thicknessScale = + energyData["SF_thickness_SiPM"][0]; + } else { + thicknessScale = + getThicknessCorrection( + energyData["SF_thickness_Si"], + detectorId, + cellType); + } + + if (thicknessScale <= 0.f) { + throw cms::Exception("ConfigError") + << "HGCal thickness scale factor is not positive."; + } + + energyLoss *= + (1.f / thicknessScale) * 1.e-3f; + + fill_SoA_column_single( + product.view().EM_scale().data(), + energyLoss, + offset, + rows); + } + + return product; + } + + private: + float getThicknessCorrection( + const std::vector& scaleFactors, + uint32_t detectorId, + int cellType) const { + using waferType = HGCSiliconDetId::waferType; + + const HGCSiliconDetId detId(detectorId); + const bool isCEE = + detId.det() == DetId::HGCalEE; + + uint32_t index = 0; + + if (cellType == waferType::HGCalHD120) { + index = isCEE ? 0 : 3; + } else if ( + cellType == waferType::HGCalHD200 || + cellType == waferType::HGCalLD200) { + index = isCEE ? 1 : 4; + } else if ( + cellType == waferType::HGCalLD300) { + index = isCEE ? 2 : 5; + } else { + throw cms::Exception("InvalidData") + << "Could not determine thickness correction " + << "for cell type " << cellType + << ", layer " << detId.layer() << "."; + } + + if (index >= scaleFactors.size()) { + throw cms::Exception("InvalidData") + << "Thickness-correction index " << index + << " exceeds the vector size " + << scaleFactors.size() << "."; + } + + return scaleFactors[index]; + } + + edm::ESGetToken< + HGCalCalibrationPayload, + HGCalCalibrationRcd> + payloadToken_; + + edm::ESGetToken< + HGCalMappingModuleIndexer, + HGCalElectronicsMappingRcd> + indexToken_; + + edm::ESGetToken< + hgcal::HGCalMappingModuleParamHost, + HGCalElectronicsMappingRcd> + mapToken_; + + const edm::FileInPath filenameEnergy_; + }; + + } // namespace hgcalrechit + +} // namespace ALPAKA_ACCELERATOR_NAMESPACE + +DEFINE_FWK_EVENTSETUP_ALPAKA_MODULE( + hgcalrechit::HGCalCalibrationDBESProducer); diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc index f3546241e3243..31e9249c3c892 100644 --- a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc +++ b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc @@ -26,8 +26,10 @@ #include "DataFormats/HGCalReco/interface/alpaka/HGCalSoARecHitsDeviceCollection.h" // includes for size, calibration, and configuration parameters +#include "CondFormats/DataRecord/interface/HGCalCalibrationRcd.h" #include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h" #include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h" +#include "CondFormats/DataRecord/interface/HGCalCalibrationRcd.h" #include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h" #include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h" #include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h" @@ -52,9 +54,23 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE { private: void acquire(device::Event const&, device::EventSetup const&) override; void produce(device::Event&, device::EventSetup const&) override; - edm::ESWatcher calibWatcher_; + edm::ESWatcher calibJsonWatcher_; + edm::ESWatcher calibSQLiteWatcher_; + const edm::EDGetTokenT digisToken_; - const edm::ESGetToken calibToken_; + const std::string calibSourceType_; + + std::optional< + edm::ESGetToken< + hgcalrechit::HGCalCalibParamHost, + HGCalModuleConfigurationRcd>> + calibJsonToken_; + + std::optional< + edm::ESGetToken< + hgcalrechit::HGCalCalibParamHost, + HGCalCalibrationRcd>> + calibSQLiteToken_; const device::ESGetToken mappingToken_; const device::ESGetToken indexingToken_; const device::ESGetToken moduleToken_; @@ -71,7 +87,7 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE { HGCalRecHitsProducer::HGCalRecHitsProducer(const edm::ParameterSet& iConfig) : SynchronizingEDProducer(iConfig), digisToken_{consumes(iConfig.getParameter("digis"))}, - calibToken_{esConsumes(iConfig.getParameter("calibSource"))}, + calibSourceType_{iConfig.getParameter("calibSourceType")}, mappingToken_{esConsumes(iConfig.getParameter("mappingSource"))}, indexingToken_{esConsumes(iConfig.getParameter("indexingSource"))}, moduleToken_{esConsumes()}, @@ -80,6 +96,26 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE { k_noise_{iConfig.getParameter("k_noise")}, n_hits_scale_{iConfig.getParameter("n_hits_scale")}, nsel_{cms::alpakatools::make_host_buffer()} { + const auto calibSource = + iConfig.getParameter("calibSource"); + + if (calibSourceType_ == "json") { + calibJsonToken_.emplace( + esConsumes< + hgcalrechit::HGCalCalibParamHost, + HGCalModuleConfigurationRcd>(calibSource)); + } else if (calibSourceType_ == "sqlite") { + calibSQLiteToken_.emplace( + esConsumes< + hgcalrechit::HGCalCalibParamHost, + HGCalCalibrationRcd>(calibSource)); + } else { + throw cms::Exception("Configuration") + << "Unsupported calibSourceType='" + << calibSourceType_ + << "'. Allowed values are 'json' and 'sqlite'."; + } + #ifndef HGCAL_PERF_TEST if (n_hits_scale_ > 1) { throw cms::Exception("RuntimeError") << "Build with `HGCAL_PERF_TEST` flag to activate `n_hits_scale`."; @@ -90,7 +126,10 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE { void HGCalRecHitsProducer::fillDescriptions(edm::ConfigurationDescriptions& descriptions) { edm::ParameterSetDescription desc; desc.add("digis", edm::InputTag("hgcalDigis", "DIGI", "TEST")); - desc.add("calibSource", edm::ESInputTag{})->setComment("Label for calibration parameters"); + desc.add("calibSourceType", "json") + ->setComment("Calibration backend: 'json' or 'sqlite'"); + desc.add("calibSource", edm::ESInputTag{}) + ->setComment("Label for calibration parameters"); desc.add("mappingSource", edm::ESInputTag{})->setComment("Label for cell mapping parameters"); desc.add("indexingSource", edm::ESInputTag{})->setComment("Label for cell dense indexer"); desc.add("k_noise", -100.)->setComment("ZS threshold for rechits (multiples of noise)"); @@ -104,7 +143,10 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE { auto& queue = iEvent.queue(); // Read digis - auto const& hostCalibParamProvider = iSetup.getData(calibToken_); + auto const& hostCalibParamProvider = + (calibSourceType_ == "sqlite") + ? iSetup.getData(*calibSQLiteToken_) + : iSetup.getData(*calibJsonToken_); auto const& deviceMappingCellParamProvider = iSetup.getData(mappingToken_); auto const& deviceIndexingParamProvider = iSetup.getData(indexingToken_); auto const& deviceModuleInfoProvider = iSetup.getData(moduleToken_); @@ -112,7 +154,12 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE { //printout new conditions if available LogDebug("HGCalCalibrationParameter").log([&](auto& log) { - if (calibWatcher_.check(iSetup)) { + const bool calibrationChanged = + (calibSourceType_ == "sqlite") + ? calibSQLiteWatcher_.check(iSetup) + : calibJsonWatcher_.check(iSetup); + + if (calibrationChanged) { for (int i = 0; i < hostCalibParamProvider.view().metadata().size(); i++) { log << "idx = " << i << ", " << "ADC_ped = " << hostCalibParamProvider.view()[i].ADC_ped() << ", "