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() << ", "