Repository: cms-sw/cmssw
Pull request #48137: [HGCAL] Clean HGCal configuration & calibration
Author: login: IzaakWN
Labels: name: alca-approved
name: reconstruction-approved
name: fully-signed
name: tests-approved
name: db-approved
name: orp-approved
name: upgrade-approved
name: code-checks-approved
name: heterogeneous-approved
name: hgcal-dpg-approved
name: changes-dataformats
Created: 2025-05-21T15:06:07Z | Merged: 2025-05-25T17:47:28Z

# PR description

This PR cleans up the HGCal electronics configuration & calibration. This facilitate system tests in the lab and at test beams, as well as simulating the full-HGCal electronics.

We use JSONs to configure and calibrate the HGCal DIGIs. To make these JSONs more flexible and reusable, we now allow patterns for the keys. (Note that that the JSONs are not final yet, so it is not written in CondDB _yet_.)

### Related PR
* Building on https://github.com/cms-sw/cmssw/pull/45665

### Summary of changes:
* The matching between a given module or FED ID to key patterns:
  * Updated ESProducers
    * [`RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc`](https://github.com/cms-sw/cmssw/blob/master/RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc)
    * [`RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc`](https://github.com/cms-sw/cmssw/blob/master/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc)
  * Using help functions in [`CondFormats/HGCalObjects/interface/HGCalESProducerTools.h`](https://github.com/CMS-HGCAL/cmssw/blob/4716efb1944551431d0ffc1396c66223fa2963de/CondFormats/HGCalObjects/interface/HGCalESProducerTools.h).
  * Allowed patterns:
    * Glob/fnmatch patterns (like `ML-*`) as keys for module typecodes in JSON, and similar for FED ID (like `*` or `[0-9]`).
    * Numerical ranges (like `10-55`) for FED IDs.
  * You can find examples of the new JSON format in [this commit](https://gitlab.cern.ch/hgcal-dpg/calibrations/-/commit/da6c82bb467a74428bf8c38a375de5026f997010).
* Remove redundant/commented-out fields in FED/ECON-D config JSONs, as well as in `CondFormats/HGCalObjects/interface/HGCalConfiguration.h`:
  * `econds` list
  * `Gain` list
  * `passthrough`
* Remove redundant `HGCalConfigParam` SoA / ESProducer (it was only propagating gain. We do not need to track the HGCROC gain anymore, as the gain settings are expected to be stable, and we will have regular automized local calibrations to match the data):
  * [`RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitConfigurationESProducer.cc`](https://github.com/cms-sw/cmssw/blob/master/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitConfigurationESProducer.cc)
  * [`CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h`](https://github.com/cms-sw/cmssw/blob/master/CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h#L17)
* Add/rename ModuleIndexer functions for specific ECON-D modules:
  * `getNFED()` → `getNumFEDs()`
  * `getMaxModuleSize(typecode)` → `getNumModules(typecode)`
  * `getMaxERxSize(typecode)` → `getNumERxs(typecode)`
  * ...
* Updated tester for the ESProducers (see below).

# PR validation

Validated with a standalone tester for the config & calibration ESProducers in `RecoLocalCalo/HGCalRecAlgos/`
```bash
cmsRun $CMSSW_BASE/src/RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py
```

Validated with the full RAW → FedRaw → DIGI → RecHit data processing chain on September 2024 testbeam data.


Diff:
diff --git a/CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h b/CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h
index 2408362cde691..0fde07ce89323 100644
--- a/CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h
+++ b/CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h
@@ -3,7 +3,6 @@
 
 #include "DataFormats/Portable/interface/PortableHostCollection.h"
 #include "CondFormats/HGCalObjects/interface/HGCalCalibParamSoA.h"
-#include "CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h"
 
 namespace hgcalrechit {
 
@@ -11,10 +10,6 @@ namespace hgcalrechit {
   //   pedestal, CM_slope, CM_ped, BXm1_kappa
   using HGCalCalibParamHost = PortableHostCollection<HGCalCalibParamSoA>;
 
-  // SoA with ROC-level configuration parameters in host memory:
-  //   gain
-  using HGCalConfigParamHost = PortableHostCollection<HGCalConfigParamSoA>;
-
 }  // namespace hgcalrechit
 
 #endif  // CondFormats_HGCalObjects_interface_HGCalCalibrationParameterHost_h
diff --git a/CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h b/CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h
deleted file mode 100644
index 298b33642a22c..0000000000000
--- a/CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef CondFormats_HGCalObjects_interface_HGCalConfigParamSoA_h
-#define CondFormats_HGCalObjects_interface_HGCalConfigParamSoA_h
-
-#include <Eigen/Core>
-#include <Eigen/Dense>
-
-#include "DataFormats/SoATemplate/interface/SoACommon.h"
-#include "DataFormats/SoATemplate/interface/SoALayout.h"
-#include "DataFormats/SoATemplate/interface/SoAView.h"
-
-//#include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h"
-
-namespace hgcalrechit {
-
-  // Generate structure of ROC-level arrays (SoA) layout with RecHit dataformat
-  GENERATE_SOA_LAYOUT(HGCalConfigParamSoALayout,
-                      SOA_COLUMN(uint8_t, gain)  // for ADC to charge (fC) conversion (80, 160, 320 fC)
-  )
-  using HGCalConfigParamSoA = HGCalConfigParamSoALayout<>;
-
-}  // namespace hgcalrechit
-
-#endif  // CondFormats_HGCalObjects_interface_HGCalConfigParamSoA_h
diff --git a/CondFormats/HGCalObjects/interface/HGCalConfiguration.h b/CondFormats/HGCalObjects/interface/HGCalConfiguration.h
index c545a86c097c7..75da5d20d588d 100644
--- a/CondFormats/HGCalObjects/interface/HGCalConfiguration.h
+++ b/CondFormats/HGCalObjects/interface/HGCalConfiguration.h
@@ -10,11 +10,6 @@
 // @short configuration for ECON eRX (one half of HGROC)
 struct HGCalROCConfig {
   uint32_t charMode;  // characterization mode; determines data fields in ROC dataframe
-  uint8_t gain;       // pre-amp gain used (1: 80 fC, 2: 160 fC, 4: 320 fC)
-  //uint32_t clockPhase;     // fine adjustment of the phase within the 40 MHz
-  //uint32_t L1AcceptOffset; // coarse adjustment to get the peak in the right place
-  //uint32_t injChannels;    // injected channels for injection scan: 2b word to identify if connected or not+info no capacitor chosen
-  //uint32_t injCharge;      // injected charge for injection scan: convert it to a float in units of fC offline (DAC setting?)
   COND_SERIALIZABLE;
 };
 
@@ -22,7 +17,6 @@ struct HGCalROCConfig {
 struct HGCalECONDConfig {
   //std::string typecode;
   uint32_t headerMarker;  // begin of event marker/identifier for ECON-D
-  uint32_t passThrough;   //pass through mode (this is just as check as it'll be in the ECON-D header anyway)
   std::vector<HGCalROCConfig> rocs;
   COND_SERIALIZABLE;
 };
@@ -32,7 +26,6 @@ struct HGCalFedConfig {
   bool mismatchPassthroughMode;  // ignore ECON-D packet mismatches
   uint32_t cbHeaderMarker;       // begin of event marker/identifier for capture block
   uint32_t slinkHeaderMarker;    // begin of event marker/identifier for S-link
-  //uint32_t delay; // delay
   std::vector<HGCalECONDConfig> econds;
   COND_SERIALIZABLE;
 };
@@ -40,7 +33,7 @@ struct HGCalFedConfig {
 /**
  *  @short Main HGCal configuration with a tree structure of vectors of
  *         HGCalFedConfig/HGCalECONDConfig/HGCalROCConfig structs as follows:
- %         config.feds[dense_fed_idx].econds[dense_econd_idx].rocs[dense_eRx_idx]
+ *         config.feds[dense_fed_idx].econds[dense_econd_idx].rocs[dense_eRx_idx]
  **/
 class HGCalConfiguration {
 public:
diff --git a/CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h b/CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h
index 43734df5112d0..e1a46439339a5 100644
--- a/CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h
+++ b/CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h
@@ -16,24 +16,24 @@
 #include "FWCore/MessageLogger/interface/MessageLogger.h"
 
 /**
-   @short this structure holds the indices and types in the readout sequence
-   as the 12 capture blocks may not all be used and the each capture block may also be under-utilized
-   a lookup table is used to hold the compact index
+ * @short this structure holds the indices and types in the readout sequence
+ * as the 12 capture blocks may not all be used and the each capture block may also be under-utilized
+ * a lookup table is used to hold the compact index
  */
 struct HGCalFEDReadoutSequence {
   uint32_t id;
-  ///>look-up table (capture block, econd idx) -> internal dense index
+  /// look-up table (capture block, econd idx) -> internal dense index
   std::vector<int> moduleLUT_;
-  ///>dense sequence of modules in the readout: the type is the one in use in the cell mapping
+  /// dense sequence of modules in the readout: the type is the one in use in the cell mapping
   std::vector<int> readoutTypes_;
-  ///>dense sequence of offsets for modules, e-Rx and channel data
+  /// dense sequence of offsets for modules, e-Rx and channel data
   std::vector<uint32_t> modOffsets_, erxOffsets_, chDataOffsets_, enabledErx_;
   COND_SERIALIZABLE;
 };
 
 /**
-   @short utility class to assign dense readout module indexing
-   the class holds the information on the expected readout sequence (module types) per FED and their offset in the SoAs of data
+ * @short utility class to assign dense readout module indexing
+ * the class holds the information on the expected readout sequence (module types) per FED and their offset in the SoAs of data
  */
 class HGCalMappingModuleIndexer {
 public:
@@ -42,10 +42,10 @@ class HGCalMappingModuleIndexer {
   ~HGCalMappingModuleIndexer() = default;
 
   /**
-     @short for a new module it adds it's type to the readaout sequence vector
-     if the fed id is not yet existing in the mapping it's added
-     a dense indexer is used to create the necessary indices for the new module
-     unused indices will be set with -1
+   * @short for a new module it adds it's type to the readaout sequence vector
+   * if the fed id is not yet existing in the mapping it's added
+   * a dense indexer is used to create the necessary indices for the new module
+   * unused indices will be set with -1
    */
   void processNewModule(uint32_t fedid,
                         uint16_t captureblockIdx,
@@ -56,13 +56,13 @@ class HGCalMappingModuleIndexer {
                         std::string const &typecode);
 
   /**
-     @short to be called after all the modules have been processed
+   * @short to be called after all the modules have been processed
    */
   void finalize();
 
   /**
-     @short decodes silicon or sipm type and cell type for the detector id 
-     from the typecode string
+   * @short decodes silicon or sipm type and cell type for the detector id 
+   * from the typecode string
    */
   static std::pair<bool, int> convertTypeCode(std::string_view typecode) {
     if (typecode.size() < 5)
@@ -70,7 +70,7 @@ class HGCalMappingModuleIndexer {
     bool isSiPM = {typecode.find("TM") != std::string::npos ? true : false};
     int celltype;
     if (isSiPM) {
-      celltype = 0;  // Assign SiPM type coarse or molded with next version of modulelocator
+      celltype = 0;  // assign SiPM type coarse or molded with next version of modulelocator
     } else {
       celltype = {typecode[4] == '1' ? 0 : typecode[4] == '2' ? 1 : 2};
     }
@@ -78,9 +78,9 @@ class HGCalMappingModuleIndexer {
   }
 
   /**
-     @short returns the index for the n-th module in the readout sequence of a FED
-     if the index in the readout sequence is unknown alternative methods which take the (capture block, econd idx) are provided
-     which will find first what should be the internal dense index (index in the readout sequence)
+   * @short returns the index for the n-th module in the readout sequence of a FED
+   * if the index in the readout sequence is unknown alternative methods which take the (capture block, econd idx) are provided
+   * which will find first what should be the internal dense index (index in the readout sequence)
    */
   uint32_t getIndexForModule(uint32_t fedid, uint32_t modid) const {
     return fedReadoutSequences_[fedid].modOffsets_[modid];
@@ -93,8 +93,8 @@ class HGCalMappingModuleIndexer {
   //  return getIndexForModule(id.localFEDId(),id.captureBlock(),id.econdIdx());
   //};
   uint32_t getIndexForModule(std::string const &typecode) const {
-    const auto &[fedid, modId] = getIndexForFedAndModule(typecode);  // (fedId,modId)
-    return getIndexForModule(fedid, modId);
+    const auto &[fedid, modid] = getIndexForFedAndModule(typecode);  // (fedId,modId)
+    return getIndexForModule(fedid, modid);
   };
   uint32_t getIndexForModuleErx(uint32_t fedid, uint32_t modid, uint32_t erxidx) const {
     return fedReadoutSequences_[fedid].erxOffsets_[modid] + erxidx;
@@ -116,37 +116,50 @@ class HGCalMappingModuleIndexer {
                      : getIndexForModuleData(
                            id.localFEDId(), id.captureBlock(), id.econdIdx(), id.econdeRx(), id.halfrocChannel());
   };
-  uint32_t getIndexForModuleData(std::string typecode) const {
+  uint32_t getIndexForModuleData(std::string const &typecode) const {
     const auto &[fedid, modid] = getIndexForFedAndModule(typecode);
     return getIndexForModuleData(fedid, modid, 0, 0);
   };
   std::pair<uint32_t, uint32_t> getIndexForFedAndModule(std::string const &typecode) const;
 
   /**
-     @short return number maximum index of FED, ECON-D Module, eRx ROC
+   * @short return number maximum index of FED, ECON-D Module, eRx ROC
    */
-  uint32_t getNFED() const {  // return total number of FEDs that actually exist
+  uint32_t getNumFEDs() const {
     return count_if(fedReadoutSequences_.begin(), fedReadoutSequences_.end(), [](auto fedrs) {
       return fedrs.readoutTypes_.size() != 0;
     });
-  }
-  uint32_t getMaxFEDSize() const { return fedReadoutSequences_.size(); }
+  }  ///< return total number of FEDs that actually exist
+  uint32_t getMaxFEDSize() const {
+    return fedReadoutSequences_.size();
+  }  ///< maximum FED index (fedReadoutSequences_ includes non existing FED IDs)
   uint32_t getMaxModuleSize() const {
     return maxModulesIdx_;
-  }  // total number of ECON-Ds (useful for setting ECON-D SoA size)
-  uint32_t getMaxModuleSize(uint32_t fedid) const {  // number of ECON-Ds for given FED id
+  }  ///< total number of ECON-Ds (useful for setting ECON-D SoA size)
+  uint32_t getNModules(uint32_t fedid) const {
     return fedReadoutSequences_[fedid].readoutTypes_.size();
-  }
+  }  ///< number of ECON-Ds for given FED id
   uint32_t getMaxERxSize() const {
     return maxErxIdx_;
-  }  // total number of eRx half-ROCs (useful for setting config SoA size)
-  uint32_t getMaxERxSize(uint32_t fedid, uint32_t modid) const {  // number of eRx half-ROCs for given FED & ECON-D ids
+  }  ///< total number of eRx half-ROCs (useful for setting config SoA size)
+  uint32_t getNumERxs(uint32_t fedid, uint32_t modid) const {
     auto modtype_val = fedReadoutSequences_[fedid].readoutTypes_[modid];
     return globalTypesNErx_[modtype_val];
-  }
+  }  ///< number of eRx half-ROCs for given FED & ECON-D ids
+  uint32_t getNumERxs(std::string const &typecode) const {
+    const auto &[fedid, modid] = getIndexForFedAndModule(typecode);
+    return getNumERxs(fedid, modid);
+  }  ///< number of eRx half-ROCs for a given ECON-D typecode
   uint32_t getMaxDataSize() const {
     return maxDataIdx_;
-  }  // total number of channels (useful for setting calib SoA size)
+  }  ///< total number of channels (useful for setting calib SoA size)
+  uint32_t getNumChannels(uint32_t fedid, uint32_t modid) const {
+    return HGCalMappingCellIndexer::maxChPerErx_ * getNumERxs(fedid, modid);
+  }  ///< total number of channels for given FED & ECON-D ids
+  uint32_t getNumChannels(std::string const &typecode) const {
+    const auto &[fedid, modid] = getIndexForFedAndModule(typecode);
+    return getNumChannels(fedid, modid);
+  }  ///< total number of channels for a given ECON-D typecode
 
   /**
      @short return type ECON-D Module
@@ -160,7 +173,7 @@ class HGCalMappingModuleIndexer {
   }
 
   /**
-     @short getters for private members
+   * @short getters for private members
    */
   HGCalDenseIndexerBase const &getFEDIndexer() const { return modFedIndexer_; }
   std::vector<HGCalFEDReadoutSequence> const &getFEDReadoutSequences() const { return fedReadoutSequences_; }
@@ -176,27 +189,27 @@ class HGCalMappingModuleIndexer {
   uint32_t maxModulesIndex() const { return maxModulesIdx_; }
   std::map<std::string, std::pair<uint32_t, uint32_t>> const &getTypecodeMap() const { return typecodeMap_; }
 
-  ///< max number of main buffers/capture blocks per FED
+  /// max number of main buffers/capture blocks per FED
   constexpr static uint32_t maxCBperFED_ = 10;
-  ///< max number of ECON-Ds processed by a main buffer/capture block
+  /// max number of ECON-Ds processed by a main buffer/capture block
   constexpr static uint32_t maxECONDperCB_ = 12;
 
 private:
-  ///< internal indexer
+  /// internal indexer
   HGCalDenseIndexerBase modFedIndexer_;
-  ///< the sequence of FED readout sequence descriptors
+  /// the sequence of FED readout sequence descriptors
   std::vector<HGCalFEDReadoutSequence> fedReadoutSequences_;
-  ///< global counters for types of modules, number of e-Rx and words
+  /// global counters for types of modules, number of e-Rx and words
   std::vector<uint32_t> globalTypesCounter_, globalTypesNErx_, globalTypesNWords_;
-  ///< base offsets to apply per module type with different granularity : module, e-Rx, channel data
+  /// base offsets to apply per module type with different granularity : module, e-Rx, channel data
   std::vector<uint32_t> moduleOffsets_, erxOffsets_, dataOffsets_;
-  ///< global counters (sizes of vectors)
+  /// global counters (sizes of vectors)
   uint32_t nfeds_, maxDataIdx_, maxErxIdx_, maxModulesIdx_;
-  ///< map from module type code string to (fedIdx,modIdx) pair (implemented to retrieve dense index offset)
+  /// map from module type code string to (fedIdx,modIdx) pair (implemented to retrieve dense index offset)
   std::map<std::string, std::pair<uint32_t, uint32_t>> typecodeMap_;
 
   /**
-     @short given capture block and econd indices returns the dense indexer
+   * @short given capture block and econd indices returns the dense indexer
    */
   uint32_t denseIndexingFor(uint32_t fedid, uint16_t captureblockIdx, uint16_t econdIdx) const {
     if (fedid > nfeds_)
@@ -211,12 +224,11 @@ class HGCalMappingModuleIndexer {
   }
 
   /**
-     @short when finalize is called, empty entries are removed and they may need to be re-assigned for the real final number of modules
+   * @short when finalize is called, empty entries are removed and they may need to be re-assigned for the real final number of modules
    */
   void reassignTypecodeLocation(uint32_t fedid, uint32_t cur_modIdx, uint32_t new_modIx) {
     std::pair<uint32_t, uint32_t> val(fedid, cur_modIdx), newval(fedid, new_modIx);
-
-    for (auto it : typecodeMap_) {
+    for (const auto &it : typecodeMap_) {
       if (it.second != val)
         continue;
       typecodeMap_[it.first] = newval;
diff --git a/CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h b/CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h
index 2ea273cc98cac..370c4bd78ca5a 100644
--- a/CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h
+++ b/CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h
@@ -5,7 +5,6 @@
 #include "HeterogeneousCore/AlpakaInterface/interface/config.h"
 #include "HeterogeneousCore/AlpakaInterface/interface/memory.h"
 #include "CondFormats/HGCalObjects/interface/HGCalCalibParamSoA.h"
-#include "CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h"
 #include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"
 
 namespace ALPAKA_ACCELERATOR_NAMESPACE {
@@ -14,7 +13,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
 
     using namespace ::hgcalrechit;
     using HGCalCalibParamDevice = PortableCollection<HGCalCalibParamSoA>;
-    using HGCalConfigParamDevice = PortableCollection<HGCalConfigParamSoA>;
 
   }  // namespace hgcalrechit
 
diff --git a/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationParameterHost.cc b/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationParameterHost.cc
index d88467f732756..1cac9d689e4b4 100644
--- a/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationParameterHost.cc
+++ b/CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationParameterHost.cc
@@ -2,4 +2,3 @@
 #include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"
 
 TYPELOOKUP_DATA_REG(hgcalrechit::HGCalCalibParamHost);
-TYPELOOKUP_DATA_REG(hgcalrechit::HGCalConfigParamHost);
diff --git a/CondFormats/HGCalObjects/src/alpaka/T_EventSetup_HGCalCalibrationParameterDevice.cc b/CondFormats/HGCalObjects/src/alpaka/T_EventSetup_HGCalCalibrationParameterDevice.cc
index ab849e7d65fc2..3823ed2ee7563 100644
--- a/CondFormats/HGCalObjects/src/alpaka/T_EventSetup_HGCalCalibrationParameterDevice.cc
+++ b/CondFormats/HGCalObjects/src/alpaka/T_EventSetup_HGCalCalibrationParameterDevice.cc
@@ -2,4 +2,3 @@
 #include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"
 
 TYPELOOKUP_ALPAKA_DATA_REG(hgcalrechit::HGCalCalibParamDevice);
-TYPELOOKUP_ALPAKA_DATA_REG(hgcalrechit::HGCalConfigParamDevice);
diff --git a/CondFormats/HGCalObjects/src/classes_def.xml b/CondFormats/HGCalObjects/src/classes_def.xml
index bd8c4b32d8981..77358db8c0808 100644
--- a/CondFormats/HGCalObjects/src/classes_def.xml
+++ b/CondFormats/HGCalObjects/src/classes_def.xml
@@ -43,10 +43,6 @@
 
   <class name="HGCalROCConfig" class_version="0">
     <field name="charMode" mapping="blob"/>
-    <field name="clockPhase" mapping="blob"/>
-    <field name="L1AcceptOffset" mapping="blob"/>
-    <field name="injChannels" mapping="blob"/>
-    <field name="injCharge" mapping="blob"/>
   </class>
 
   <class name="HGCalDenseIndexerBase" class_version="0">
diff --git a/RecoLocalCalo/HGCalRecAlgos/BuildFile.xml b/RecoLocalCalo/HGCalRecAlgos/BuildFile.xml
index 019bc6c23b729..5a59e1bf4d2de 100644
--- a/RecoLocalCalo/HGCalRecAlgos/BuildFile.xml
+++ b/RecoLocalCalo/HGCalRecAlgos/BuildFile.xml
@@ -1,5 +1,6 @@
 <use name="clhep"/>
 <use name="root"/>
+<use name="json"/>
 <use name="rootminuit"/>
 <use name="DataFormats/HGCRecHit"/>
 <use name="DataFormats/ParticleFlowReco"/>
diff --git a/RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h b/RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h
new file mode 100644
index 0000000000000..94c119e596d63
--- /dev/null
+++ b/RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h
@@ -0,0 +1,19 @@
+#ifndef RecoLocalCalo_HGCalRecAlgos_HGCalESProducerTools_h
+#define RecoLocalCalo_HGCalRecAlgos_HGCalESProducerTools_h
+
+#include <string>
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name);
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name);
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname);
+
+}  // namespace hgcal
+
+#endif
diff --git a/RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h b/RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h
index 1514ad6ede370..83145c6651f1d 100644
--- a/RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h
+++ b/RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h
@@ -32,7 +32,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     HGCalRecHitDevice calibrate(Queue& queue,
                                 HGCalDigiHost const& host_digis,
                                 HGCalCalibParamDevice const& device_calib,
-                                HGCalConfigParamDevice const& device_config,
                                 HGCalMappingCellParamDevice const& device_mapping,
                                 HGCalDenseIndexInfoDevice const& device_index) const;
 
diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc
index 7fc4222e3458c..95c99a8f7425a 100644
--- a/RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc
+++ b/RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc
@@ -6,19 +6,21 @@
 #include "FWCore/Framework/interface/ESProducts.h"
 #include "FWCore/Utilities/interface/ESGetToken.h"
 #include "FWCore/Utilities/interface/do_nothing_deleter.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
 #include "FWCore/ParameterSet/interface/FileInPath.h"
 #include "FWCore/ParameterSet/interface/ParameterSet.h"
+
 #include "CondFormats/HGCalObjects/interface/HGCalConfiguration.h"
 #include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h"
 #include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h"
 #include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h"  // depends on HGCalElectronicsMappingRcd
-#include <string>                                                          // for std::to_string
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"    // for json, search_modkey, search_fedkey
+
+#include <string>   // for std::to_string
 #include <fstream>  // needed to read json file with std::ifstream
-#include <nlohmann/json.hpp>
-using json = nlohmann::json;
 
 /**
-   @short plugin parses HGCAL electronics configuration from JSON file
+ * @short ESProducer to parse HGCAL electronics configuration from JSON file
  */
 class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSetupRecordIntervalFinder {
 public:
@@ -28,8 +30,6 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
         modjson_(iConfig.getParameter<std::string>("modjson")) {
     if (iConfig.exists("bePassthroughMode"))
       bePassthroughMode_ = iConfig.getParameter<int32_t>("bePassthroughMode");
-    if (iConfig.exists("econPassthroughMode"))
-      econPassthroughMode_ = iConfig.getParameter<int32_t>("econPassthroughMode");
     if (iConfig.exists("cbHeaderMarker"))
       cbHeaderMarker_ = iConfig.getParameter<int32_t>("cbHeaderMarker");
     if (iConfig.exists("slinkHeaderMarker"))
@@ -38,10 +38,7 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
       econdHeaderMarker_ = iConfig.getParameter<int32_t>("econdHeaderMarker");
     if (iConfig.exists("charMode"))
       charMode_ = iConfig.getParameter<int32_t>("charMode");
-    if (iConfig.exists("gain"))
-      gain_ = iConfig.getParameter<int32_t>("gain");
     auto cc = setWhatProduced(this);
-    //findingRecord<HGCalModuleConfigurationRcd>();
     indexToken_ = cc.consumes(iConfig.getParameter<edm::ESInputTag>("indexSource"));
   }
 
@@ -49,12 +46,10 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
     edm::ParameterSetDescription desc;
     desc.add<edm::ESInputTag>("indexSource", edm::ESInputTag(""))
         ->setComment("Label for module indexer to set SoA size");
-    //desc.add<edm::FileInPath>("filename")->setComment("JSON file with FED configuration parameters");
     desc.add<std::string>("fedjson", "")->setComment("JSON file with FED configuration parameters");
     desc.add<std::string>("modjson", "")->setComment("JSON file with ECOND configuration parameters");
     desc.addOptional<int32_t>("bePassthroughMode", -1)
         ->setComment("Manual override for mismatch passthrough mode in the BE");
-    desc.addOptional<int32_t>("econPassthroughMode", -1)->setComment("Manual override passthrough mode in the ECON-D");
     desc.addOptional<int32_t>("cbHeaderMarker", -1)
         ->setComment("Manual override for capture block header marker (BEO, e.g 0x7f)");
     desc.addOptional<int32_t>("slinkHeaderMarker", -1)
@@ -62,54 +57,37 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
     desc.addOptional<int32_t>("econdHeaderMarker", -1)
         ->setComment("Manual override for ECON-D header marker (BEO, e.g 0x154)");
     desc.addOptional<int32_t>("charMode", -1)->setComment("Manual override for ROC characterization mode");
-    desc.addOptional<int32_t>("gain", -1)->setComment("Manual override for ROC gain");
     descriptions.addWithDefaultLabel(desc);
   }
 
-  static bool checkkeys(const json& data,
-                        const std::string& firstkey,
-                        const std::vector<std::string>& keys,
-                        const std::string& fname) {
-    // check if json contains key
-    bool iscomplete = true;
-    for (auto const& key : keys) {
-      if (not data[firstkey].contains(key)) {
-        edm::LogWarning("HGCalConfigurationESProducer::checkkeys")
-            << " JSON is missing key '" << key << "' for " << firstkey << "!"
-            << " Please check file " << fname;
-        iscomplete = false;
-      }
-    }
-    return iscomplete;
-  }
-
+  // @short get hexadecimal value, and override if value_override>=0
   static int32_t gethex(const std::string& value, const int32_t value_override) {
-    // get value, and override if value_override>=0
     return (value_override >= 0 ? value_override : std::stoi(value, nullptr, 16));
   }
 
+  // @short get integer value, and override if value_override>=0
   static int32_t getint(const int32_t value, const int32_t value_override) {
-    // get value, and override if value_override>=0
     return (value_override >= 0 ? value_override : value);
   }
 
   std::unique_ptr<HGCalConfiguration> produce(const HGCalModuleConfigurationRcd& iRecord) {
     auto const& moduleMap = iRecord.get(indexToken_);
+    edm::LogInfo("HGCalConfigurationESProducer")
+        << "produce: fedjson_=" << fedjson_ << ",\n         modjson_=" << modjson_;
 
     // retrieve values from custom JSON format (see HGCalCalibrationESProducer)
     edm::FileInPath fedfip(fedjson_);  // e.g. HGCalCommissioning/LocalCalibration/data/config_feds.json
     edm::FileInPath modfip(modjson_);  // e.g. HGCalCommissioning/LocalCalibration/data/config_econds.json
-    std::ifstream fedfile(fedfip.fullPath());
-    std::ifstream modfile(modfip.fullPath());
-    const json fed_config_data = json::parse(fedfile);
-    const json mod_config_data = json::parse(modfile);
+    std::ifstream fedfile(fedjson_);
+    std::ifstream modfile(modjson_);
+    const json fed_config_data = json::parse(fedfile, nullptr, true, /*ignore_comments*/ true);
+    const json mod_config_data = json::parse(modfile, nullptr, true, /*ignore_comments*/ true);
 
     // consistency check
-    uint32_t nfeds = moduleMap.getNFED();
+    uint32_t nfeds = moduleMap.getNumFEDs();
     uint32_t ntot_mods = 0, ntot_rocs = 0;
-    const std::vector<std::string> fedkeys = {
-        "mismatchPassthroughMode", "cbHeaderMarker", "slinkHeaderMarker", "econds"};
-    const std::vector<std::string> modkeys = {"headerMarker", "passthrough", "Gain", "CalibrationSC"};
+    const std::vector<std::string> fedkeys = {"mismatchPassthroughMode", "cbHeaderMarker", "slinkHeaderMarker"};
+    const std::vector<std::string> modkeys = {"headerMarker", "CalibrationSC"};
     if (nfeds != fed_config_data.size())
       edm::LogWarning("HGCalConfigurationESProducer")
           << "Total number of FEDs found in JSON file " << fedjson_ << " (" << fed_config_data.size()
@@ -121,48 +99,43 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
     std::unique_ptr<HGCalConfiguration> config_ = std::make_unique<HGCalConfiguration>();
     for (std::size_t fedid = 0; fedid < moduleMap.getMaxFEDSize(); ++fedid) {
       // sanity checks
-      if (moduleMap.getFEDReadoutSequences()[fedid].readoutTypes_.empty())  // check if FED exists (non-empty)
-        continue;                                                           // skip non-existent FED
-      std::string sfedid = std::to_string(fedid);                           // key in JSON dictionary must be string
-      if (!fed_config_data.contains(sfedid))
-        edm::LogWarning("HGCalConfigurationESProducer")
-            << " Did not find FED index " << sfedid << " in JSON file " << fedjson_ << "...";
-      checkkeys(fed_config_data, sfedid, fedkeys, fedjson_);  // check required keys are in the JSON, warn otherwise
+      if (moduleMap.getFEDReadoutSequences()[fedid].readoutTypes_.empty())         // check if FED exists (non-empty)
+        continue;                                                                  // skip non-existent FED
+      const auto fedkey = hgcal::search_fedkey(fedid, fed_config_data, fedjson_);  // search matching key
+      hgcal::check_keys(
+          fed_config_data, fedkey, fedkeys, fedjson_);  // check required keys are in the JSON, warn otherwise
 
       // fill FED configurations
       HGCalFedConfig fed;
-      fed.mismatchPassthroughMode = getint(fed_config_data[sfedid]["mismatchPassthroughMode"],
+      fed.mismatchPassthroughMode = getint(fed_config_data[fedkey]["mismatchPassthroughMode"],
                                            bePassthroughMode_);  // ignore ECON-D packet mismatches
-      fed.cbHeaderMarker = gethex(fed_config_data[sfedid]["cbHeaderMarker"],
+      fed.cbHeaderMarker = gethex(fed_config_data[fedkey]["cbHeaderMarker"],
                                   cbHeaderMarker_);  // begin of event marker/identifier for capture block
-      fed.slinkHeaderMarker = gethex(fed_config_data[sfedid]["slinkHeaderMarker"],
+      fed.slinkHeaderMarker = gethex(fed_config_data[fedkey]["slinkHeaderMarker"],
                                      slinkHeaderMarker_);  // begin of event marker/identifier for S-link
 
-      // loop over ECON-D modules in JSON
-      for (const std::string typecode :
-           fed_config_data[sfedid]["econds"]) {  // loop over module typecodes in JSON file (e.g. "ML-F3PT-TX-0003")
+      // loop over module typecodes (e.g. "ML-F3PT-TX-0003")
+      for (const auto& [typecode, ids] : moduleMap.getTypecodeMap()) {
+        auto [fedid_, imod] = ids;
+        if (fedid_ != fedid)
+          continue;
 
         // sanity checks for ECON-Ds
         ntot_mods++;
-        const auto& [fedid2, imod] = moduleMap.getIndexForFedAndModule(typecode);
-        if (fedid != fedid2)
-          edm::LogWarning("HGCalConfigurationESProducer")
-              << " FED index from HGCalMappingModuleIndexer (" << fedid << ") does not match that of the JSON file ("
-              << fedid2 << ", " << fedjson_ << ") for ECON-D module with typecode " << typecode << " and id=" << imod
-              << "!";
-        checkkeys(mod_config_data, typecode, modkeys, modjson_);  // check required keys are in the JSON, warn otherwise
+        const auto modkey = hgcal::search_modkey(typecode, mod_config_data, modjson_);  // search matching key
+        hgcal::check_keys(
+            mod_config_data, modkey, modkeys, modjson_);  // check required keys are in the JSON, warn otherwise
         if (imod >= fed.econds.size())
           fed.econds.resize(imod + 1);
 
         // fill ECON-D configuration
+        // headerMarker: begin of event marker/identifier for capture block
         HGCalECONDConfig mod;
-        mod.headerMarker = gethex(mod_config_data[typecode]["headerMarker"],
-                                  econdHeaderMarker_);  // begin of event marker/identifier for capture block
-        mod.passThrough = getint(mod_config_data[typecode]["passthrough"], econPassthroughMode_);
+        mod.headerMarker = gethex(mod_config_data[modkey]["headerMarker"], econdHeaderMarker_);
 
         // sanity checks for eRx half-ROCs
-        uint32_t nrocs = moduleMap.getMaxERxSize(fedid, imod);
-        uint32_t nrocs2 = mod_config_data[typecode]["Gain"].size();
+        uint32_t nrocs = moduleMap.getNumERxs(fedid, imod);
+        uint32_t nrocs2 = mod_config_data[modkey]["CalibrationSC"].size();
         if (nrocs != nrocs2)
           edm::LogWarning("HGCalConfigurationESProducer")
               << " Number of eRx ROCs for ECON-D " << typecode << " in " << fedjson_ << " (" << nrocs2
@@ -174,9 +147,7 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
         for (uint32_t iroc = 0; iroc < nrocs; iroc++) {
           ntot_rocs++;
           HGCalROCConfig roc;
-          roc.gain = (uint8_t)mod_config_data[typecode]["Gain"][iroc];
-          //roc.charMode = getint(mod_config_data[typecode]["characMode"],charMode_);
-          roc.charMode = getint(mod_config_data[typecode]["CalibrationSC"][iroc], charMode_);
+          roc.charMode = getint(mod_config_data[modkey]["CalibrationSC"][iroc], charMode_);
           mod.rocs[iroc] = roc;  // add to ECON-D's vector<HGCalROCConfig> of eRx half-ROCs
         }
         fed.econds[imod] = mod;  // add to FED's vector<HGCalECONDConfig> of ECON-D modules
@@ -206,15 +177,13 @@ class HGCalConfigurationESProducer : public edm::ESProducer, public edm::EventSe
   }
 
   edm::ESGetToken<HGCalMappingModuleIndexer, HGCalElectronicsMappingRcd> indexToken_;
-  const std::string fedjson_;         // JSON file
-  const std::string modjson_;         // JSON file
-  int32_t bePassthroughMode_ = -1;    // for manual override
-  int32_t cbHeaderMarker_ = -1;       // for manual override
-  int32_t slinkHeaderMarker_ = -1;    // for manual override
-  int32_t econdHeaderMarker_ = -1;    // for manual override
-  int32_t econPassthroughMode_ = -1;  // for manual override
-  int32_t charMode_ = -1;             // for manual override
-  int32_t gain_ = -1;                 // for manual override
+  const std::string fedjson_;       // JSON file
+  const std::string modjson_;       // JSON file
+  int32_t bePassthroughMode_ = -1;  // for manual override
+  int32_t cbHeaderMarker_ = -1;     // for manual override
+  int32_t slinkHeaderMarker_ = -1;  // for manual override
+  int32_t econdHeaderMarker_ = -1;  // for manual override
+  int32_t charMode_ = -1;           // for manual override
 };
 
 DEFINE_FWK_EVENTSETUP_SOURCE(HGCalConfigurationESProducer);
diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc
index d3dbec79c7209..3eb10ee525ccd 100644
--- a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc
+++ b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc
@@ -192,7 +192,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
   HGCalRecHitDevice HGCalRecHitCalibrationAlgorithms::calibrate(Queue& queue,
                                                                 HGCalDigiHost const& host_digis,
                                                                 HGCalCalibParamDevice const& device_calib,
-                                                                HGCalConfigParamDevice const& device_config,
                                                                 HGCalMappingCellParamDevice const& device_mapping,
                                                                 HGCalDenseIndexInfoDevice const& device_index) const {
     LogDebug("HGCalRecHitCalibrationAlgorithms") << "\n\nINFO -- Start of calibrate\n\n" << std::endl;
diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc
index 74785aec56b2f..821418e2d4950 100644
--- a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc
+++ b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc
@@ -1,3 +1,4 @@
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
 #include "FWCore/ParameterSet/interface/FileInPath.h"
 #include "FWCore/ParameterSet/interface/ParameterSet.h"
 
@@ -9,20 +10,17 @@
 #include "HeterogeneousCore/AlpakaInterface/interface/memory.h"
 
 #include "DataFormats/HGCalDigi/interface/HGCalElectronicsId.h"
-#include "CondFormats/HGCalObjects/interface/HGCalConfiguration.h"
 #include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h"
-#include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h"
-#include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h"      // depends on HGCalElectronicsMappingRcd
-#include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"  // for HGCalConfigParamHost
+#include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"
 #include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"
+#include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h"
+#include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h"  // depends on HGCalElectronicsMappingRcd
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"    // for json, search_modkey
 
 #include <string>
-#include <iostream>
-#include <iomanip>  // for std::setw
-#include <sstream>
+//#include <iostream>
+//#include <sstream>
 #include <fstream>  // needed to read json file with std::ifstream
-#include <nlohmann/json.hpp>
-using json = nlohmann::json;
 
 namespace ALPAKA_ACCELERATOR_NAMESPACE {
 
@@ -34,7 +32,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
           : ESProducer(iConfig), filename_(iConfig.getParameter<edm::FileInPath>("filename")) {
         auto cc = setWhatProduced(this);
         indexToken_ = cc.consumes(iConfig.getParameter<edm::ESInputTag>("indexSource"));
-        configToken_ = cc.consumes(iConfig.getParameter<edm::ESInputTag>("configSource"));
       }
 
       static void fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
@@ -42,15 +39,13 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
         desc.add<edm::FileInPath>("filename")->setComment("Path to JSON file with calibration parameters");
         desc.add<edm::ESInputTag>("indexSource", edm::ESInputTag(""))
             ->setComment("Label for module indexer to set SoA size");
-        desc.add<edm::ESInputTag>("configSource", edm::ESInputTag(""))
-            ->setComment("Label for ROC configuration parameters");
         descriptions.addWithDefaultLabel(desc);
       }
 
+      // @short fill SoA column with data from vector for any type with some offset
       template <typename T>
       static void fill_SoA_column(
           T* column_SoA, const std::vector<T>& values, const int offset, const int nrows, int arr_offset = 0) {
-        // fill SoA column with data from vector for any type
         const int nrows_vals = values.size();
         if (arr_offset < 0) {
           arr_offset = 0;
@@ -68,6 +63,7 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
         std::copy(begin, end, &column_SoA[offset]);
       }
 
+      // @short fill full SoA column with data from vector for any type
       template <typename T, typename P>
       static void fill_SoA_eigen_row(P& soa, const std::vector<std::vector<T>>& values, const size_t row) {
         if (row >= values.size())
@@ -80,9 +76,10 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
           soa(i) = values[row][i];
       }
 
+      // @short create the ESProducer product: a SoA with channel-level calibration constants
       std::optional<hgcalrechit::HGCalCalibParamHost> produce(const HGCalModuleConfigurationRcd& iRecord) {
         auto const& moduleMap = iRecord.get(indexToken_);
-        auto const& config = iRecord.get(configToken_);
+        edm::LogInfo("HGCalCalibrationESProducer") << "produce: filename=" << filename_.fullPath().c_str();
 
         // load dense indexing
         const uint32_t nchans = moduleMap.getMaxDataSize();  // channel-level size
@@ -90,69 +87,62 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
 
         // load calib parameters from JSON
         std::ifstream infile(filename_.fullPath().c_str());
-        json calib_data = json::parse(infile);
-        for (const auto& it : calib_data.items()) {  // loop over module typecodes in JSON file
-          const std::string& module = it.key();      // module typecode, e.g. "ML-F3PT-TX-0003"
-          if (module == "Metadata")
-            continue;  // ignore metadata fields
-          const auto& [ifed, imod] = moduleMap.getIndexForFedAndModule(module);
-          const uint32_t offset =
-              moduleMap.getIndexForModuleData(module);  // convert module typecode to dense index for this module
-          const uint32_t nrows =
-              calib_data[module]["Channel"].size();  // number of channels to compare with JSON arrays
-          const uint32_t nrocs =
-              config.feds[ifed].econds[imod].rocs.size();  // number of channels to compare with JSON arrays
+        json calib_data = json::parse(infile, nullptr, true, /*ignore_comments*/ true);
+        for (const auto& it : moduleMap.getTypecodeMap()) {  // loop over all module typecodes
+          std::string const& module = it.first;              // module typecode, e.g. "ML-F3PT-TX-0003"
+
+          // retrieve matching key (glob patterns allowed)
+          const auto modkey = hgcal::search_modkey(module, calib_data, filename_.fullPath());
+          auto calib_data_ = calib_data[modkey];
+
+          // get dimensions
+          const auto firstkey = calib_data_.begin().key();
+          const uint32_t offset = moduleMap.getIndexForModuleData(module);  // first channel index
+          const uint32_t nchans = moduleMap.getNumChannels(module);         // number of channels in mapper
+          uint32_t nrows = calib_data_[firstkey].size();                    // number of channels in JSON
 
           // check number of channels & ROCs make sense
-          uint32_t nchans = (nrows % 39 == 0 ? 39 : 37);  // number of channels per eRx (37 excl. common modes)
-          if (nrows % 37 != 0 and nrows % 39 != 0) {
+          if (nrows % 37 != 0) {
             edm::LogWarning("HGCalCalibrationESProducer")
-                << " nchannels%nchannels_per_erX!=0 nchannels=" << nrows << ", nchannels_per_erX=37 or 39!";
+                << "nchannels=" << nrows << ", which is not divisible by 37 (#channels per e-Rx)!";
           }
-
-          // loop over ECON eRx blocks to fill columns for gain-dependent calibration parameters
-          for (std::size_t iroc = 0; iroc < nrocs; ++iroc) {
-            const uint32_t offset_arr = iroc * nchans;        // dense index offset for JSON array (input to SoA)
-            const uint32_t offset_soa = offset + offset_arr;  // dense index offset for SoA
-            if (offset_arr + nchans > nrows) {
-              edm::LogWarning("HGCalCalibrationESProducer")
-                  << " offset + nchannels_per_eRx = " << offset_arr << " + " << nchans << " = " << offset_arr + nchans
-                  << " > " << nrows << " = nchannels ";
-            }
-            fill_SoA_column<float>(
-                product.view().ADC_ped(), calib_data[module]["ADC_ped"], offset_soa, nchans, offset_arr);
-            fill_SoA_column<float>(product.view().Noise(), calib_data[module]["Noise"], offset_soa, nchans, offset_arr);
-            fill_SoA_column<float>(
-                product.view().CM_slope(), calib_data[module]["CM_slope"], offset_soa, nchans, offset_arr);
-            fill_SoA_column<float>(
-                product.view().CM_ped(), calib_data[module]["CM_ped"], offset_soa, nchans, offset_arr);
-            fill_SoA_column<float>(
-                product.view().BXm1_slope(), calib_data[module]["BXm1_slope"], offset_soa, nchans, offset_arr);
+          if (nchans != nrows) {
+            edm::LogWarning("HGCalCalibrationESProducer")
+                << "nchannels does not match between module indexer ('" << module << "'," << nchans << ") and JSON ('"
+                << modkey << "'," << nrows << "')!";
+            nrows = std::min(nrows, nchans);  // take smallest to avoid overlap
           }
+
+          // fill calibration parameters for ADC, CM, TOT, MIPS scale, ...
+          fill_SoA_column<float>(product.view().ADC_ped(), calib_data_["ADC_ped"], offset, nrows);
+          fill_SoA_column<float>(product.view().Noise(), calib_data_["Noise"], offset, nrows);
+          fill_SoA_column<float>(product.view().CM_slope(), calib_data_["CM_slope"], offset, nrows);
+          fill_SoA_column<float>(product.view().CM_ped(), calib_data_["CM_ped"], offset, nrows);
+          fill_SoA_column<float>(product.view().BXm1_slope(), calib_data_["BXm1_slope"], offset, nrows);
+          fill_SoA_column<float>(product.view().TOTtoADC(), calib_data_["TOTtoADC"], offset, nrows);
+          fill_SoA_column<float>(product.view().TOT_ped(), calib_data_["TOT_ped"], offset, nrows);
+          fill_SoA_column<float>(product.view().TOT_lin(), calib_data_["TOT_lin"], offset, nrows);
+          fill_SoA_column<float>(product.view().TOT_P0(), calib_data_["TOT_P0"], offset, nrows);
+          fill_SoA_column<float>(product.view().TOT_P1(), calib_data_["TOT_P1"], offset, nrows);
+          fill_SoA_column<float>(product.view().TOT_P2(), calib_data_["TOT_P2"], offset, nrows);
+          fill_SoA_column<float>(product.view().MIPS_scale(), calib_data_["MIPS_scale"], offset, nrows);
+          fill_SoA_column<unsigned char>(product.view().valid(), calib_data_["Valid"], offset, nrows);
+
           // default parameters for fine-grain TDC, coarse-grain TDC and time-walk corrections that allow passthrough
-          if (calib_data[module].find("TOA_CTDC") == calib_data[module].end())
-            calib_data[module]["TOA_CTDC"] = std::vector<std::vector<float>>(nrows, std::vector<float>(32, 0.));
-          if (calib_data[module].find("TOA_FTDC") == calib_data[module].end())
-            calib_data[module]["TOA_FTDC"] = std::vector<std::vector<float>>(nrows, std::vector<float>(8, 0.));
-          if (calib_data[module].find("TOA_TW") == calib_data[module].end())
-            calib_data[module]["TOA_TW"] = std::vector<std::vector<float>>(nrows, std::vector<float>(3, 0.));
-          //
-          // fill columns for gain-independent calibration parameters
-          fill_SoA_column<float>(product.view().TOTtoADC(), calib_data[module]["TOTtoADC"], offset, nrows);
-          fill_SoA_column<float>(product.view().TOT_ped(), calib_data[module]["TOT_ped"], offset, nrows);
-          fill_SoA_column<float>(product.view().TOT_lin(), calib_data[module]["TOT_lin"], offset, nrows);
-          fill_SoA_column<float>(product.view().TOT_P0(), calib_data[module]["TOT_P0"], offset, nrows);
-          fill_SoA_column<float>(product.view().TOT_P1(), calib_data[module]["TOT_P1"], offset, nrows);
-          fill_SoA_column<float>(product.view().TOT_P2(), calib_data[module]["TOT_P2"], offset, nrows);
-          fill_SoA_column<float>(product.view().MIPS_scale(), calib_data[module]["MIPS_scale"], offset, nrows);
-          fill_SoA_column<unsigned char>(product.view().valid(), calib_data[module]["Valid"], offset, nrows);
-          //
+          // (for backwards compatibility of older calibration JSONs)
+          if (calib_data_.find("TOA_CTDC") == calib_data_.end())
+            calib_data_["TOA_CTDC"] = std::vector<std::vector<float>>(nrows, std::vector<float>(32, 0.));
+          if (calib_data_.find("TOA_FTDC") == calib_data_.end())
+            calib_data_["TOA_FTDC"] = std::vector<std::vector<float>>(nrows, std::vector<float>(8, 0.));
+          if (calib_data_.find("TOA_TW") == calib_data_.end())
+            calib_data_["TOA_TW"] = std::vector<std::vector<float>>(nrows, std::vector<float>(3, 0.));
+
           // fill vectors for ToA correction parameters
           for (size_t n = 0; n < nrows; n++) {
             auto vi = product.view()[offset + n];
-            fill_SoA_eigen_row<float>(vi.TOA_CTDC(), calib_data[module]["TOA_CTDC"], n);
-            fill_SoA_eigen_row<float>(vi.TOA_FTDC(), calib_data[module]["TOA_FTDC"], n);
-            fill_SoA_eigen_row<float>(vi.TOA_TW(), calib_data[module]["TOA_TW"], n);
+            fill_SoA_eigen_row<float>(vi.TOA_CTDC(), calib_data_["TOA_CTDC"], n);
+            fill_SoA_eigen_row<float>(vi.TOA_FTDC(), calib_data_["TOA_FTDC"], n);
+            fill_SoA_eigen_row<float>(vi.TOA_TW(), calib_data_["TOA_TW"], n);
           }
         }
 
@@ -161,7 +151,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
 
     private:
       edm::ESGetToken<HGCalMappingModuleIndexer, HGCalElectronicsMappingRcd> indexToken_;
-      edm::ESGetToken<HGCalConfiguration, HGCalModuleConfigurationRcd> configToken_;
       const edm::FileInPath filename_;
     };
 
diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitConfigurationESProducer.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitConfigurationESProducer.cc
deleted file mode 100644
index 676a32c71faa6..0000000000000
--- a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitConfigurationESProducer.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "FWCore/Framework/interface/MakerMacros.h"
-#include "FWCore/Framework/interface/SourceFactory.h"
-#include "FWCore/Framework/interface/ESHandle.h"
-#include "FWCore/Framework/interface/ESProducer.h"
-#include "FWCore/Framework/interface/ESTransientHandle.h"
-#include "FWCore/Framework/interface/EventSetupRecordIntervalFinder.h"
-#include "FWCore/ParameterSet/interface/ParameterSet.h"
-#include "FWCore/Utilities/interface/ESGetToken.h"
-#include "DataFormats/Math/interface/libminifloat.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/HGCalObjects/interface/HGCalConfiguration.h"
-#include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h"
-#include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h"
-#include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h"      // depends on HGCalElectronicsMappingRcd
-#include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"  // for HGCalConfigParamHost
-#include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"
-
-#include <string>
-#include <iostream>  // for std::cout
-#include <iomanip>   // for std::setw
-
-namespace ALPAKA_ACCELERATOR_NAMESPACE {
-
-  namespace hgcalrechit {
-
-    class HGCalConfigurationESProducer : public ESProducer {
-    public:
-      HGCalConfigurationESProducer(const edm::ParameterSet& iConfig) : ESProducer(iConfig) {
-        if (iConfig.exists("gain"))
-          gain_ = iConfig.getParameter<int>("gain");
-        auto cc = setWhatProduced(this);  //HGCalConfigurationESProducer::produce
-        indexToken_ = cc.consumes(iConfig.getParameter<edm::ESInputTag>("indexSource"));
-        configToken_ = cc.consumes(iConfig.getParameter<edm::ESInputTag>("configSource"));
-      }
-
-      static void fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
-        edm::ParameterSetDescription desc;
-        desc.add<edm::ESInputTag>("indexSource", edm::ESInputTag(""))
-            ->setComment("Label for module indexer to set SoA size");
-        desc.add<edm::ESInputTag>("configSource", edm::ESInputTag(""))
-            ->setComment("Label for ROC configuration parameters");
-        desc.addOptional<int>("gain", 2)->setComment(
-            "Manual override for gain for all modules (1: 80 fC, 2: 160 fC, 4: 320 fC)");
-        descriptions.addWithDefaultLabel(desc);
-      }
-
-      std::optional<hgcalrechit::HGCalConfigParamHost> produce(const HGCalModuleConfigurationRcd& iRecord) {
-        auto const& config = iRecord.get(configToken_);
-        auto const& moduleMap = iRecord.get(indexToken_);
-
-        // load dense indexing
-        const uint32_t nERx = moduleMap.getMaxERxSize();  // half-ROC-level size
-        hgcalrechit::HGCalConfigParamHost product(nERx, cms::alpakatools::host());
-
-        // fill SoA with gain
-        if (gain_ > 0) {  // fill with single value from user override
-          for (uint32_t iroc = 0; iroc < nERx; iroc++) {
-            product.view()[iroc].gain() = gain_;
-          }
-        } else {  // fill with ROC-dependent value from JSON via HGCalConfiguration
-          for (uint32_t ifed = 0; ifed < config.feds.size(); ++ifed) {
-            for (uint32_t imod = 0; imod < config.feds[ifed].econds.size(); ++imod) {
-              for (uint32_t iroc = 0; iroc < config.feds[ifed].econds[imod].rocs.size(); ++iroc) {
-                //uint32_t i = getIndexForModuleErx(ifed,imod,iroc); // dense index for ROCs
-                uint32_t iroc_dense = moduleMap.getIndexForModuleErx(ifed, imod, iroc);  // dense index for eRx half-ROC
-                product.view()[iroc_dense].gain() = config.feds[ifed].econds[imod].rocs[iroc].gain;
-              }
-            }
-          }
-        }
-
-        return product;
-      }  // end of produce()
-
-    private:
-      edm::ESGetToken<HGCalMappingModuleIndexer, HGCalElectronicsMappingRcd> indexToken_;
-      edm::ESGetToken<HGCalConfiguration, HGCalModuleConfigurationRcd> configToken_;
-      int32_t gain_ = -1;  // manual override of YAML files
-    };
-
-  }  // namespace hgcalrechit
-
-}  // namespace ALPAKA_ACCELERATOR_NAMESPACE
-
-DEFINE_FWK_EVENTSETUP_ALPAKA_MODULE(hgcalrechit::HGCalConfigurationESProducer);
diff --git a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc
index e7c7b93ee7c37..ec91965f8381f 100644
--- a/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc
+++ b/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc
@@ -50,10 +50,8 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
   private:
     void produce(device::Event&, device::EventSetup const&) override;
     edm::ESWatcher<HGCalElectronicsMappingRcd> calibWatcher_;
-    edm::ESWatcher<HGCalModuleConfigurationRcd> configWatcher_;
     const edm::EDGetTokenT<hgcaldigi::HGCalDigiHost> digisToken_;
     edm::ESGetToken<hgcalrechit::HGCalCalibParamHost, HGCalModuleConfigurationRcd> calibToken_;
-    device::ESGetToken<hgcalrechit::HGCalConfigParamDevice, HGCalModuleConfigurationRcd> configToken_;
     device::ESGetToken<hgcal::HGCalMappingCellParamDevice, HGCalElectronicsMappingRcd> mappingToken_;
     device::ESGetToken<hgcal::HGCalDenseIndexInfoDevice, HGCalDenseIndexInfoRcd> indexingToken_;
     const device::EDPutToken<hgcalrechit::HGCalRecHitDevice> recHitsToken_;
@@ -65,7 +63,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
       : EDProducer(iConfig),
         digisToken_{consumes<hgcaldigi::HGCalDigiHost>(iConfig.getParameter<edm::InputTag>("digis"))},
         calibToken_{esConsumes(iConfig.getParameter<edm::ESInputTag>("calibSource"))},
-        configToken_{esConsumes(iConfig.getParameter<edm::ESInputTag>("configSource"))},
         mappingToken_{esConsumes(iConfig.getParameter<edm::ESInputTag>("mappingSource"))},
         indexingToken_{esConsumes(iConfig.getParameter<edm::ESInputTag>("indexingSource"))},
         recHitsToken_{produces()},
@@ -82,7 +79,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     edm::ParameterSetDescription desc;
     desc.add<edm::InputTag>("digis", edm::InputTag("hgcalDigis", "DIGI", "TEST"));
     desc.add("calibSource", edm::ESInputTag{})->setComment("Label for calibration parameters");
-    desc.add("configSource", edm::ESInputTag{})->setComment("Label for ROC configuration 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<int>("n_blocks", -1);
@@ -96,7 +92,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
 
     // Read digis
     auto const& hostCalibParamProvider = iSetup.getData(calibToken_);
-    auto const& deviceConfigParamProvider = iSetup.getData(configToken_);
     auto const& deviceMappingCellParamProvider = iSetup.getData(mappingToken_);
     auto const& deviceIndexingParamProvider = iSetup.getData(indexingToken_);
     auto const& hostDigisIn = iEvent.get(digisToken_);
@@ -104,10 +99,6 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     //printout new conditions if available
     LogDebug("HGCalCalibrationParameter").log([&](auto& log) {
       if (calibWatcher_.check(iSetup)) {
-        for (int i = 0; i < deviceConfigParamProvider.view().metadata().size(); i++) {
-          log << "idx = " << i << ", "
-              << "gain = " << deviceConfigParamProvider.view()[i].gain() << ", ";
-        }
         for (int i = 0; i < hostCalibParamProvider.view().metadata().size(); i++) {
           log << "idx = " << i << ", "
               << "ADC_ped = " << hostCalibParamProvider.view()[i].ADC_ped() << ", "
@@ -167,17 +158,12 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     alpaka::memcpy(queue, deviceCalibParam.buffer(), hostCalibParam.const_buffer());
 
 #ifdef HGCAL_PERF_TEST
-    auto tmpRecHits = calibrator_.calibrate(queue, hostDigis, deviceCalibParam, deviceConfigParamProvider);
+    auto tmpRecHits = calibrator_.calibrate(queue, hostDigis, deviceCalibParam);
     HGCalRecHitDevice recHits(oldSize, queue);
     alpaka::memcpy(queue, recHits.buffer(), tmpRecHits.const_buffer(), oldSize);
 #else
-    auto recHits = calibrator_.calibrate(queue,
-                                         hostDigis,
-                                         deviceCalibParam,
-                                         deviceConfigParamProvider,
-                                         deviceMappingCellParamProvider,
-                                         deviceIndexingParamProvider);
-
+    auto recHits = calibrator_.calibrate(
+        queue, hostDigis, deviceCalibParam, deviceMappingCellParamProvider, deviceIndexingParamProvider);
 #endif
 
 #ifdef EDM_ML_DEBUG
diff --git a/RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc b/RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
new file mode 100644
index 0000000000000..c3e7ea9e43b94
--- /dev/null
+++ b/RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
@@ -0,0 +1,101 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    cms::Exception ex("InvalidData");
+    ex << "Could not find matching key for '" << module << "' in '" << name << "'! Returning first key '"
+       << data.begin().key() << "'...";
+    ex.addContext("Calling hgcal::search_modkey()");
+    throw ex;
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_fedkey()");
+      throw ex;
+    }
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
+    while (it != data.end()) {
+      std::string fedkey = it.key();
+
+      // try as numerical range
+      int low, high;
+      char dash;
+      std::istringstream iss(fedkey.c_str());
+      iss >> low >> dash >> high;              // parse [integer][character][integer] pattern
+      if (iss.eof() and dash == '-') {         // matches pattern
+        if (low <= fedid and fedid <= high) {  // matches numerical range
+          matchedkey = fedkey;
+          break;
+        }
+
+        // try as glob pattern
+      } else {
+        const std::string sfedid = std::to_string(fedid);
+        std::regex re(edm::glob2reg(fedkey));
+        if (std::regex_match(sfedid, re)) {  // found matching key !
+          matchedkey = fedkey;
+          break;
+        }
+      }
+
+      ++it;
+    }
+    if (it == data.end()) {
+      cms::Exception ex("InvalidData");
+      ex << "Could not find matching key for '" << fedid << "' in '" << name << "'! Returning first key '" << matchedkey
+         << "'...";
+      ex.addContext("Calling hgcal::search_fedkey()");
+    } else {
+      edm::LogInfo("search_fedkey") << "search_fedkey: Matched module='" << fedid << "' to fedkey='" << matchedkey
+                                    << "'";
+    }
+
+    return matchedkey;  // no matching key found in whole JSON map
+  }
+
+  // @short check if JSON data contains key
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname) {
+    bool iscomplete = true;
+    for (auto const& key : keys) {
+      if (not data[firstkey].contains(key)) {
+        edm::LogWarning("checkkeys") << " JSON is missing key '" << key << "' for " << firstkey << "!"
+                                     << " Please check file " << fname;
+        iscomplete = false;
+      }
+    }
+    return iscomplete;
+  }
+
+}  // namespace hgcal
diff --git a/RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc b/RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc
index 96109343b3535..9dd12147cd8df 100644
--- a/RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc
+++ b/RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc
@@ -1,29 +1,35 @@
 // Author: Izaak Neutelings (March 2024)
 // Based on: https://github.com/CMS-HGCAL/cmssw/blob/hgcal-condformat-HGCalNANO-13_2_0_pre3_linearity/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducer.cc
+
+// includes for CMSSW
 #include "FWCore/Framework/interface/MakerMacros.h"
 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
 #include "FWCore/ParameterSet/interface/ParameterSet.h"
 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
 #include "FWCore/MessageLogger/interface/MessageLogger.h"
 #include "FWCore/Utilities/interface/InputTag.h"
-#include "FWCore/Utilities/interface/StreamID.h"
+
+// includes for Alpaka
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/stream/EDProducer.h"
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/EDPutToken.h"
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/ESGetToken.h"
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/Event.h"
 #include "HeterogeneousCore/AlpakaInterface/interface/config.h"
-#include "HeterogeneousCore/AlpakaInterface/interface/CopyToDevice.h"
-#include <iomanip>  // for std::setw
-#include <future>
 
-// includes for size, calibration, and configuration parameters
+// includes for HGCal, calibration, and configuration parameters
 #include "FWCore/Framework/interface/ESWatcher.h"
 #include "CondFormats/HGCalObjects/interface/HGCalConfiguration.h"
 #include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h"
 #include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h"
 #include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h"
 #include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"
-#include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"  // also for HGCalConfigParamDevice
+#include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"  // for json, search_fedkey
+
+// standard includes
+#include <string>
+#include <vector>
+#include <iomanip>  // for std::setw
 
 namespace ALPAKA_ACCELERATOR_NAMESPACE {
 
@@ -35,20 +41,24 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     static void fillDescriptions(edm::ConfigurationDescriptions&);
 
   private:
+    const int maxchans_, maxfeds_;
+    const std::string fedjson_;  // JSON file of FED configuration
     void produce(device::Event&, device::EventSetup const&) override;
     void beginRun(edm::Run const&, edm::EventSetup const&) override;
     edm::ESWatcher<HGCalModuleConfigurationRcd> configWatcher_;
     edm::ESGetToken<HGCalMappingModuleIndexer, HGCalElectronicsMappingRcd> indexerToken_;
     edm::ESGetToken<HGCalConfiguration, HGCalModuleConfigurationRcd> configToken_;
-    device::ESGetToken<hgcalrechit::HGCalConfigParamDevice, HGCalModuleConfigurationRcd> configParamToken_;
     device::ESGetToken<hgcalrechit::HGCalCalibParamDevice, HGCalModuleConfigurationRcd> calibParamToken_;
   };
 
-  HGCalRecHitESProducersTest::HGCalRecHitESProducersTest(const edm::ParameterSet& iConfig) : EDProducer(iConfig) {
+  HGCalRecHitESProducersTest::HGCalRecHitESProducersTest(const edm::ParameterSet& iConfig)
+      : EDProducer(iConfig),
+        maxchans_(iConfig.getParameter<int>("maxchans")),
+        maxfeds_(iConfig.getParameter<int>("maxfeds")),
+        fedjson_(iConfig.getParameter<std::string>("fedjson")) {
     std::cout << "HGCalRecHitESProducersTest::HGCalRecHitESProducersTest" << std::endl;
     indexerToken_ = esConsumes(iConfig.getParameter<edm::ESInputTag>("indexSource"));
     configToken_ = esConsumes(iConfig.getParameter<edm::ESInputTag>("configSource"));
-    configParamToken_ = esConsumes(iConfig.getParameter<edm::ESInputTag>("configParamSource"));
     calibParamToken_ = esConsumes(iConfig.getParameter<edm::ESInputTag>("calibParamSource"));
   }
 
@@ -56,9 +66,10 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     edm::ParameterSetDescription desc;
     desc.add("indexSource", edm::ESInputTag{})->setComment("Label for module indexer to set SoA size");
     desc.add("configSource", edm::ESInputTag{})->setComment("Label for HGCal configuration for unpacking raw data");
-    desc.add("configParamSource", edm::ESInputTag{})
-        ->setComment("Label for ROC configuration parameters for calibrations");
     desc.add("calibParamSource", edm::ESInputTag{})->setComment("Label for calibration parameters");
+    desc.add<int>("maxchans", 200)->setComment("Maximum number of channels to print");
+    desc.add<int>("maxfeds", 25)->setComment("Maximum number of FED IDs to test");
+    desc.add<std::string>("fedjson", "")->setComment("JSON file with FED configuration parameters");
     descriptions.addWithDefaultLabel(desc);
   }
 
@@ -77,13 +88,13 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
     auto queue = iEvent.queue();
     auto const& moduleMap = iSetup.getData(indexerToken_);
     auto const& config = iSetup.getData(configToken_);  // HGCalConfiguration
-    auto const& configParamDevice = iSetup.getData(configParamToken_);
-    //printf("HGCalRecHitESProducersTest::produce: time to load configParamDevice from config ESProducers: %f seconds\n", duration(start,now()));
     auto const& calibParamDevice = iSetup.getData(calibParamToken_);
     //printf("HGCalRecHitESProducersTest::produce: time to load calibParamDevice from calib ESProducers: %f seconds\n", duration(start,now()));
 
     // Check if there are new conditions and read them
+    std::string line = "HGCalRecHitESProducersTest::produce " + std::string(90, '-');
     if (configWatcher_.check(iSetup)) {
+      std::cout << line << std::endl;
       std::cout << "HGCalRecHitESProducersTest::produce: moduleMap.getMaxDataSize()=" << moduleMap.getMaxDataSize()
                 << ", moduleMap.getMaxERxSize()=" << moduleMap.getMaxERxSize() << std::endl;
 
@@ -91,6 +102,7 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
       auto nfeds = config.feds.size();  // number of FEDs
       std::cout << "HGCalRecHitESProducersTest::produce: config=" << config << std::endl;
       std::cout << "HGCalRecHitESProducersTest::produce: nfeds=" << nfeds << ", config=" << config << std::endl;
+      std::cout << "HGCalRecHitESProducersTest::produce: configuration:" << std::endl;
       for (std::size_t fedid = 0; fedid < nfeds; ++fedid) {
         auto fed = config.feds[fedid];   // HGCalFedConfig
         auto nmods = fed.econds.size();  // number of ECON-Ds for this FED
@@ -106,23 +118,14 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
         }
       }
 
-      // Alpaka ESProducer for SoA with configuration parameters with gains
-      int size = configParamDevice.view().metadata().size();
-      std::cout << "HGCalRecHitESProducersTest::produce: device size=" << size << std::endl;
-      std::cout << "  imod  gain" << std::endl;
-      for (int imod = 0; imod < size; imod++) {
-        if (imod >= 250)
-          break;
-        std::cout << std::setw(6) << imod << std::setw(6) << uint32_t(configParamDevice.view()[imod].gain())
-                  << std::endl;
-      }
-
       // Alpaka ESProducer for SoA with calibration parameters with pedestals, etc.
-      size = calibParamDevice.view().metadata().size();
+      std::cout << line << std::endl;
+      int size = calibParamDevice.view().metadata().size();
       std::cout << "HGCalRecHitESProducersTest::produce: device size=" << size << std::endl;
+      std::cout << "HGCalRecHitESProducersTest::produce: calibration constants:" << std::endl;
       std::cout << "   idx    hex     ADC_ped   CM_slope   CM_ped   BXm1_slope" << std::endl;
       for (int idx = 0; idx < size; idx++) {
-        if (idx >= 250)
+        if (idx >= maxchans_)
           break;
         std::cout << std::setw(6) << idx << std::setw(7) << int2hex(idx) << std::dec << std::setw(12)
                   << calibParamDevice.view()[idx].ADC_ped() << std::setw(11) << calibParamDevice.view()[idx].CM_slope()
@@ -130,6 +133,41 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
                   << calibParamDevice.view()[idx].BXm1_slope() << std::endl;
       }
     }
+
+    // test JSON parser
+    json fed_data;
+    std::cout << line << std::endl;
+    std::cout << "HGCalRecHitESProducersTest::produce: testing search_fedkey with " << fedjson_ << std::endl;
+    if (fedjson_ == "") {
+      fed_data = json::parse(R"({
+        // numerical range
+        "8-20": { },
+        // glob pattern
+        "[0-9]": { },
+        // glob pattern
+        "2[0-9]": { },
+        // glob wildcard (default)
+        "*": { }
+      })",
+                             nullptr,
+                             true,
+                             /*ignore_comments*/ true);
+    } else {
+      edm::FileInPath fedfip(fedjson_);  // e.g. HGCalCommissioning/LocalCalibration/data/config_feds.json
+      std::ifstream fedfile(fedjson_);
+      fed_data = json::parse(fedfile, nullptr, true, /*ignore_comments*/ true);
+    }
+    std::vector<std::string> fedkeys;
+    for (int fedid = 0; fedid <= maxfeds_; fedid++) {
+      const auto fedkey = hgcal::search_fedkey(fedid, fed_data, fedjson_);  // search matching key
+      fedkeys.push_back(fedkey);
+    }
+    std::cout << "   fedid   fedkey" << std::endl;
+    for (int fedid = 0; fedid <= maxfeds_; fedid++) {
+      std::cout << std::setw(8) << fedid << "   '" + fedkeys[fedid] + "'" << std::endl;
+    }
+
+    std::cout << line << std::endl;
   }
 
 }  // namespace ALPAKA_ACCELERATOR_NAMESPACE
diff --git a/RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py b/RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py
index 02590cf9f4011..01bc325e696e7 100644
--- a/RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py
+++ b/RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py
@@ -1,50 +1,59 @@
 # Author: Izaak Neutelings (March 2023)
 # Instructions:
 #   export SCRAM_ARCH="el9_amd64_gcc12"
-#   cmsrel CMSSW_14_1_0_pre4
-#   cd CMSSW_14_1_0_pre4/src/
+#   cmsrel CMSSW_15_1_0_pre1
+#   cd CMSSW_15_1_0_pre1/src/
 #   cmsenv
-#   git cms-merge-topic -u CMS-HGCAL:dev/hackathon_base_CMSSW_14_1_X
-#   git clone https://github.com/pfs/Geometry-HGCalMapping.git  $CMSSW_BASE/src/Geometry/HGCalMapping/data
 #   git clone https://gitlab.cern.ch/hgcal-dpg/hgcal-comm.git HGCalCommissioning
 #   scram b -j8
 #   cmsRun $CMSSW_BASE/src/RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py
 # Sources:
+#   https://gitlab.cern.ch/hgcal-dpg/hgcal-comm/-/blob/master/Configuration/test/step_RAW2DIGI.py?ref_type=heads
+#   https://gitlab.cern.ch/hgcal-dpg/hgcal-comm/-/blob/master/Configuration/python/SysValEras_cff.py?ref_type=heads
 #   https://github.com/CMS-HGCAL/cmssw/blob/hgcal-condformat-HGCalNANO-13_2_0_pre3/DPGAnalysis/HGCalTools/python/tb2023_cfi.py
-#   https://gitlab.cern.ch/hgcal-dpg/hgcal-comm/-/blob/master/SystemTestEventFilters/test/test_raw2reco.py
 #   https://github.com/CMS-HGCAL/cmssw/blob/dev/hackathon_base_CMSSW_14_1_0_pre0/SimCalorimetry/HGCalSimProducers/test/hgcalRealistiDigis_cfg.py
 import os
 import FWCore.ParameterSet.Config as cms
 
 # USER OPTIONS
 from FWCore.ParameterSet.VarParsing import VarParsing
-datadir = os.path.join(os.environ.get('CMSSW_BASE',''),"src/HGCalCommissioning/LocalCalibration/data")
+modmapdir = os.path.join(os.environ.get('CMSSW_BASE',''),"src/HGCalCommissioning/Configuration/data")
+configdir = "/eos/cms/store/group/dpg_hgcal/tb_hgcal/DPG/calibrations/SepTB2024"
 options = VarParsing('standard')
-options.register('geometry', 'Extended2026D94', VarParsing.multiplicity.singleton, VarParsing.varType.string,
+options.register('geometry', 'ExtendedRun4D104', VarParsing.multiplicity.singleton, VarParsing.varType.string,
                  info="geometry to use")
-options.register('fedconfig',f"{datadir}/config_feds.json",mytype=VarParsing.varType.string,
+options.register('maxchans', 200, mytype=VarParsing.varType.int,
+                 info="maximum number of channels to print out")
+options.register('maxfeds', 30, mytype=VarParsing.varType.int,
+                 info="maximum number of FED IDs to test")
+options.register('fedconfig', f"{configdir}/config/config_feds_hackathon.json", mytype=VarParsing.varType.int,
                  info="Path to configuration (JSON format)")
-options.register('modconfig',f"{datadir}/config_econds.json",mytype=VarParsing.varType.string,
+options.register('modconfig', f"{configdir}/config/config_econds_hackathon.json", mytype=VarParsing.varType.string,
                  info="Path to configuration (JSON format)")
-options.register('params',f"{datadir}/level0_calib_params.json",mytype=VarParsing.varType.string,
+options.register('params',
+                 #f"{configdir}/level0_calib_Relay1727210224.json",
+                 f"{configdir}/level0_calib_hackathon.json",
+                 mytype=VarParsing.varType.string,
                  info="Path to calibration parameters (JSON format)")
 options.register('modules',
+                 # see https://github.com/cms-data/Geometry-HGCalMapping
+                 # or https://gitlab.cern.ch/hgcal-dpg/hgcal-comm/-/tree/master/Configuration/data/ModuleMaps
                  #"Geometry/HGCalMapping/data/ModuleMaps/modulelocator_test.txt", # test beam with six modules
-                 #"Geometry/HGCalMapping/data/ModuleMaps/modulelocator_test_2mods.txt", # only first two modules, fedId=49
-                 "HGCalCommissioning/Configuration/data/ModuleMaps/modulelocator_test_2mods.txt", # fedId=0
+                 #f"{modmapdir}/ModuleMaps/modulelocator_test_2mods.txt", # fedId=0
+                 f"{modmapdir}/ModuleMaps/modulelocator_Sep2024TBv2.txt", # 3 layers (9 modules)
                  mytype=VarParsing.varType.string,
                  info="Path to module mapper. Absolute, or relative to CMSSW src directory")
-options.register('sicells','Geometry/HGCalMapping/data/CellMaps/WaferCellMapTraces.txt',mytype=VarParsing.varType.string,
+options.register('sicells', 'Geometry/HGCalMapping/data/CellMaps/WaferCellMapTraces.txt', mytype=VarParsing.varType.string,
                  info="Path to Si cell mapper. Absolute, or relative to CMSSW src directory")
-options.register('sipmcells','Geometry/HGCalMapping/data/CellMaps/channels_sipmontile.hgcal.txt',mytype=VarParsing.varType.string,
+options.register('sipmcells', 'Geometry/HGCalMapping/data/CellMaps/channels_sipmontile.hgcal.txt', mytype=VarParsing.varType.string,
                  info="Path to SiPM-on-tile cell mapper. Absolute, or relative to CMSSW src directory")
 options.parseArguments()
+if options.params.startswith('/eos/'):
+  options.params = os.path.relpath(options.params,os.path.join(os.environ.get('CMSSW_BASE',''),"src"))
 if len(options.files)==0:
   options.files=['file:/eos/cms/store/group/dpg_hgcal/comm_hgcal/psilva/hackhathon/23234.103_TTbar_14TeV+2026D94Aging3000/step2.root']
   #options.files=['file:/eos/cms/store/group/dpg_hgcal/comm_hgcal/psilva/hackhathon/23234.103_TTbar_14TeV+2026D94Aging3000/step2.root']
   #options.files=['file:/afs/cern.ch/user/y/yumiao/public/HGCAL_Raw_Data_Handling/Data/Digis/testFakeDigisSoA.root']
-  #options.files=['file:/home/hgcdaq00/CMSSW/data/23234.103_TTbar_14TeV+2026D94Aging3000/step2.root'] # on DAQ PC
-  #options.files=['file:/home/hgcdaq00/CMSSW/data/testFakeDigisSoA.root'] # on DAQ PC
 print(f">>> Geometry:      {options.geometry!r}")
 print(f">>> Input files:   {options.files!r}")
 print(f">>> Module map:    {options.modules!r}")
@@ -69,15 +78,21 @@
 # INPUT
 process.source = cms.Source(
   "PoolSource",
-  fileNames = cms.untracked.vstring(options.files),
-  duplicateCheckMode = cms.untracked.string("noDuplicateCheck")
+  fileNames=cms.untracked.vstring(options.files),
+  duplicateCheckMode=cms.untracked.string("noDuplicateCheck")
 )
 #process.maxEvents = cms.untracked.PSet( input = cms.untracked.int32(options.maxEvents) )
 process.maxEvents.input = 1
 
 # MESSAGE LOGGER
 process.load("FWCore.MessageService.MessageLogger_cfi")
-process.MessageLogger.cerr.threshold = ''
+#process.MessageLogger.debugModules = ['*'] #"hgCalCalibrationESProducer", "hgCalConfigurationESProducer"]
+process.MessageLogger.cerr.threshold = 'INFO'
+process.MessageLogger.HGCalConfigurationESProducer = { } # enable logger
+process.MessageLogger.HGCalCalibrationESProducer = { }
+process.MessageLogger.search_modkey = { }
+process.MessageLogger.search_fedkey = { }
+process.MessageLogger.check_keys = { }
 process.MessageLogger.cerr.FwkReport.reportEvery = 500
 
 # GEOMETRY
@@ -94,13 +109,13 @@
 #process.load("RecoLocalCalo.HGCalRecAlgos.hgCalConfigurationESProducer_cfi")
 process.hgcalConfigESProducer = cms.ESSource( # ESProducer to load configurations for unpacker
   'HGCalConfigurationESProducer',
-  fedjson=cms.string(options.fedconfig), # JSON with FED configuration parameters
-  modjson=cms.string(options.modconfig), # JSON with ECON-D configuration parameters
+  fedjson=cms.string(options.fedconfig),  # JSON with FED configuration parameters
+  modjson=cms.string(options.modconfig),  # JSON with ECON-D configuration parameters
   #passthroughMode=cms.int32(0),          # ignore mismatch
   #cbHeaderMarker=cms.int32(0x7f),        # capture block
-  ##cbHeaderMarker=cms.int32(0x5f),        # capture block
+  #cbHeaderMarker=cms.int32(0x5f),        # capture block
   #slinkHeaderMarker=cms.int32(0x55),     # S-link
-  ##slinkHeaderMarker=cms.int32(0x2a),     # S-link
+  #slinkHeaderMarker=cms.int32(0x2a),     # S-link
   #econdHeaderMarker=cms.int32(0x154),    # ECON-D
   #charMode=cms.int32(1),
   indexSource=cms.ESInputTag('hgCalMappingESProducer','')
@@ -110,17 +125,10 @@
 process.load('Configuration.StandardSequences.Accelerators_cff')
 #process.load('HeterogeneousCore.AlpakaCore.ProcessAcceleratorAlpaka_cfi')
 #process.load('HeterogeneousCore.CUDACore.ProcessAcceleratorCUDA_cfi')
-process.hgcalConfigParamESProducer = cms.ESProducer( # ESProducer to load configurations parameters from YAML file, like gain
-  'hgcalrechit::HGCalConfigurationESProducer@alpaka',
-  gain=cms.int32(1), # to switch between 80, 160, 320 fC calibration
-  #charMode=cms.int32(1),
-  indexSource=cms.ESInputTag('hgCalMappingESProducer','')
-)
 process.hgcalCalibParamESProducer = cms.ESProducer( # ESProducer to load calibration parameters from JSON file, like pedestals
   'hgcalrechit::HGCalCalibrationESProducer@alpaka',
-  filename=cms.string(options.params),
-  indexSource=cms.ESInputTag('hgCalMappingESProducer',''),
-  configSource=cms.ESInputTag(''),
+  filename=cms.FileInPath(options.params),
+  indexSource=cms.ESInputTag('hgCalMappingESProducer','')
 )
 
 # MAIN PROCESS
@@ -130,8 +138,11 @@
   #'alpaka_serial_sync::TestHGCalRecHitProducer', # CPU
   indexSource=cms.ESInputTag('hgCalMappingESProducer', ''),
   configSource=cms.ESInputTag('hgcalConfigESProducer', ''),
-  configParamSource=cms.ESInputTag('hgcalConfigParamESProducer', ''),
   calibParamSource=cms.ESInputTag('hgcalCalibParamESProducer', ''),
+  maxchans=cms.int32(options.maxchans),  # maximum number of channels to print out
+  maxfeds=cms.int32(options.maxfeds),    # maximum number of FED IDs to test
+  #fedjson=cms.string(options.fedconfig),  # JSON with FED configuration parameters
+  fedjson=cms.string(""), # use hardcoded JSON instead
 )
 process.t = cms.Task(process.testHGCalRecHitESProducers)
 process.p = cms.Path(process.t)


Reviews:
user: login: makortel
body: Quick glance
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:14:10Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:33:22Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:41:10Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:41:30Z
user: login: makortel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:44:53Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:45:44Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:45:59Z
user: login: makortel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:48:51Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:49:08Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T18:57:19Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:52:17Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:52:52Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:53:03Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:53:16Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:53:30Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:54:27Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T19:54:37Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T04:53:19Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T04:53:42Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:52:42Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T06:07:43Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T07:22:11Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T07:23:44Z
user: login: IzaakWN
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T07:26:37Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T08:14:39Z
user: login: fwyzard
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T08:15:44Z

Review comments:
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: This necessitates
```xml
<use name="json"/>
```
to `CondFormats/HGCalObjects/BuildFile.xml`
created_at: 2025-05-21T18:03:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: Elsewhere we tend to use `std::regex` and imitating globbing by replacing `*` in a glob pattern with `.*` for a regex.
created_at: 2025-05-21T18:04:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '\e[1m" << module << "\e[0m' in '"
+                                   << name << "'! Returning first key '\e[1m" << data.begin().key() << "\e[0m'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: What if `data` is empty?
created_at: 2025-05-21T18:04:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '\e[1m" << module << "\e[0m' in '"
+                                   << name << "'! Returning first key '\e[1m" << data.begin().key() << "\e[0m'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  inline std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty())
+      edm::LogError("search_fedkey") << "search_fedkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: What if `data` is empty?
created_at: 2025-05-21T18:05:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: These functions (except _maybe_ for the last `check_keys()`) would likely be better as out-of-line functions given that they are rather long and have complicated logic.
created_at: 2025-05-21T18:07:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,29 +1,38 @@
 // Author: Izaak Neutelings (March 2024)
 // Based on: https://github.com/CMS-HGCAL/cmssw/blob/hgcal-condformat-HGCalNANO-13_2_0_pre3_linearity/RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducer.cc
+
+// includes for CMSSW
 #include "FWCore/Framework/interface/MakerMacros.h"
 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
 #include "FWCore/ParameterSet/interface/ParameterSet.h"
 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
 #include "FWCore/MessageLogger/interface/MessageLogger.h"
 #include "FWCore/Utilities/interface/InputTag.h"
 #include "FWCore/Utilities/interface/StreamID.h"
+
+// includes for Alpaka
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/stream/EDProducer.h"
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/EDPutToken.h"
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/ESGetToken.h"
 #include "HeterogeneousCore/AlpakaCore/interface/alpaka/Event.h"
 #include "HeterogeneousCore/AlpakaInterface/interface/config.h"
 #include "HeterogeneousCore/AlpakaInterface/interface/CopyToDevice.h"
-#include <iomanip>  // for std::setw
-#include <future>
 
-// includes for size, calibration, and configuration parameters
+// includes for HGCal, calibration, and configuration parameters
 #include "FWCore/Framework/interface/ESWatcher.h"
+#include "CondFormats/HGCalObjects/interface/HGCalESProducerTools.h"  // for json, search_fedkey
 #include "CondFormats/HGCalObjects/interface/HGCalConfiguration.h"
 #include "CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h"
 #include "CondFormats/DataRecord/interface/HGCalElectronicsMappingRcd.h"
 #include "CondFormats/DataRecord/interface/HGCalModuleConfigurationRcd.h"
 #include "CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h"
-#include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"  // also for HGCalConfigParamDevice
+#include "CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h"
+
+// standard includes
+//#include <future>
path: RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc
user: login: makortel
body: I'd suggest to remove this commented out line because it would raise questions
```suggestion
```

created_at: 2025-05-21T18:10:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: Actually, what motivates placing these function in `CondFormats/HGCalObjects`? AFAICT they are presently used only in `RecoLocalCalo/HGCalRecAlgos`. The functions seem to be intimately tied to the JSON presentation of the data. Is JSON planned to be used for the eventual CondDB storage?
created_at: 2025-05-21T18:13:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: It is a bit early to say if whether the final format will be JSONs, but I did intentionally put the help functions to match module typecodes to keys in `CondFormats/HGCalObjects` instead `RecoLocalCalo/HGCalRecAlgos` because I thought we might keep this way of matching keys (the typecode contains information about the sensor thickness, number of channels, etc.).
created_at: 2025-05-21T18:33:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: fwyzard
body: as in `FWCore/Utilities/interface/RegexMatch.h` / `FWCore/Utilities/src/RegexMatch.cc`.
created_at: 2025-05-21T18:41:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '\e[1m" << module << "\e[0m' in '"
+                                   << name << "'! Returning first key '\e[1m" << data.begin().key() << "\e[0m'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: It would have thrown a `LogError` a few lines above and then crash. :)

Should I change the `LogError` in L17–18 to something like the following?
```
throw cms::Exception("search_modkey") << "..." ;
```
Or do you have another suggestion how to handle this?
created_at: 2025-05-21T18:41:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: From dependency management point of view it would be better to keep the dependencies in the smallest possible scope. Presently we have 13 packages that depend (directly or indirectly) on `json`, this change would add 28 packages more there.

The file naming of "tools to produce the condformats" also hints towards the "condformats" not to be the best placement (I mean, we separate the production of data from the data formats on purpose).
created_at: 2025-05-21T18:44:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: I'll move the main code to `CondFormats/HGCalObjects/src/HGCalESProducerTools.cc`.
created_at: 2025-05-21T18:45:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,79 +76,73 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
           soa(i) = values[row][i];
       }
 
+      // @short create the ESProducer product: a SoA with channel-level calibration constants
       std::optional<hgcalrechit::HGCalCalibParamHost> produce(const HGCalModuleConfigurationRcd& iRecord) {
         auto const& moduleMap = iRecord.get(indexToken_);
-        auto const& config = iRecord.get(configToken_);
+        edm::LogInfo("HGCalCalibrationESProducer") << "produce: filename=" << filename_.fullPath().c_str();
 
         // load dense indexing
         const uint32_t nchans = moduleMap.getMaxDataSize();  // channel-level size
         hgcalrechit::HGCalCalibParamHost product(nchans, cms::alpakatools::host());
 
         // load calib parameters from JSON
         std::ifstream infile(filename_.fullPath().c_str());
-        json calib_data = json::parse(infile);
-        for (const auto& it : calib_data.items()) {  // loop over module typecodes in JSON file
-          const std::string& module = it.key();      // module typecode, e.g. "ML-F3PT-TX-0003"
-          if (module == "Metadata")
-            continue;  // ignore metadata fields
-          const auto& [ifed, imod] = moduleMap.getIndexForFedAndModule(module);
-          const uint32_t offset =
-              moduleMap.getIndexForModuleData(module);  // convert module typecode to dense index for this module
-          const uint32_t nrows =
-              calib_data[module]["Channel"].size();  // number of channels to compare with JSON arrays
-          const uint32_t nrocs =
-              config.feds[ifed].econds[imod].rocs.size();  // number of channels to compare with JSON arrays
+        json calib_data = json::parse(infile, nullptr, true, /*ignore_comments*/ true);
+        for (const auto& it : moduleMap.getTypecodeMap()) {  // loop over all module typecodes
+          std::string module = it.first;                     // module typecode, e.g. "ML-F3PT-TX-0003"
path: RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc
user: login: fwyzard
body: can this be a `const&` ?
```suggestion
          std::string const& module = it.first;                     // module typecode, e.g. "ML-F3PT-TX-0003"
```
created_at: 2025-05-21T18:45:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '\e[1m" << module << "\e[0m' in '"
+                                   << name << "'! Returning first key '\e[1m" << data.begin().key() << "\e[0m'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: makortel
body: Throwing an exception would be the preferred way. I'd suggest to use more general name for the exception category (e.g. `InvalidData` or `InvalidJSON` or similar). If you want to include the function name in the message, you could do it along
```cpp
cms::Exception ex("InvalidData");
ex << "...";
ex.addContext("Calling hgcal::search_modkey()");
throw ex;
```
created_at: 2025-05-21T18:48:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: Okay. I'll move `CondFormats/HGCalObjects/interface/HGCalESProducerTools.h` to `RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h`.
created_at: 2025-05-21T18:49:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: fwyzard
body: We (used to ?) have a rule in CMSSW against the use of bold, colours, etc. in the log messages.

One reason is that it will potentially lead to an escalation of more and more "visible" messages.

Other, more technical reasons, are that the ANSI escape sequences are not portable (not all terminal supports them) and will show poorly in the web-based log viewers (like the logs in the IBs and PRs).

If you _really_ need to have bold or coloured text, I'd suggest to at least see if the [`fmt`](https://github.com/fmtlib/fmt) library can offer a portable solution.
created_at: 2025-05-21T18:57:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: It was useful for testing. I just removed it now. 😔 https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b
created_at: 2025-05-21T19:52:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,79 +76,73 @@ namespace ALPAKA_ACCELERATOR_NAMESPACE {
           soa(i) = values[row][i];
       }
 
+      // @short create the ESProducer product: a SoA with channel-level calibration constants
       std::optional<hgcalrechit::HGCalCalibParamHost> produce(const HGCalModuleConfigurationRcd& iRecord) {
         auto const& moduleMap = iRecord.get(indexToken_);
-        auto const& config = iRecord.get(configToken_);
+        edm::LogInfo("HGCalCalibrationESProducer") << "produce: filename=" << filename_.fullPath().c_str();
 
         // load dense indexing
         const uint32_t nchans = moduleMap.getMaxDataSize();  // channel-level size
         hgcalrechit::HGCalCalibParamHost product(nchans, cms::alpakatools::host());
 
         // load calib parameters from JSON
         std::ifstream infile(filename_.fullPath().c_str());
-        json calib_data = json::parse(infile);
-        for (const auto& it : calib_data.items()) {  // loop over module typecodes in JSON file
-          const std::string& module = it.key();      // module typecode, e.g. "ML-F3PT-TX-0003"
-          if (module == "Metadata")
-            continue;  // ignore metadata fields
-          const auto& [ifed, imod] = moduleMap.getIndexForFedAndModule(module);
-          const uint32_t offset =
-              moduleMap.getIndexForModuleData(module);  // convert module typecode to dense index for this module
-          const uint32_t nrows =
-              calib_data[module]["Channel"].size();  // number of channels to compare with JSON arrays
-          const uint32_t nrocs =
-              config.feds[ifed].econds[imod].rocs.size();  // number of channels to compare with JSON arrays
+        json calib_data = json::parse(infile, nullptr, true, /*ignore_comments*/ true);
+        for (const auto& it : moduleMap.getTypecodeMap()) {  // loop over all module typecodes
+          std::string module = it.first;                     // module typecode, e.g. "ML-F3PT-TX-0003"
path: RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc
user: login: IzaakWN
body: Thanks, done in https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b.
created_at: 2025-05-21T19:52:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: Done in https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b.
created_at: 2025-05-21T19:53:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '\e[1m" << module << "\e[0m' in '"
+                                   << name << "'! Returning first key '\e[1m" << data.begin().key() << "\e[0m'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  inline std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty())
+      edm::LogError("search_fedkey") << "search_fedkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: See discussion above. Done in https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b.
created_at: 2025-05-21T19:53:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
+using json = nlohmann::ordered_json;  // ordered_json preserves key insertion order
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  inline std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      edm::LogError("search_modkey") << "search_modkey: '" << name
+                                     << "' does not have the expected map/dict structure!";
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      int match = fnmatch(it.key().c_str(), module.c_str(), FNM_EXTMATCH);
+      if (match != FNM_NOMATCH) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='\e[1m" << module << "'\e[0m to modkey='\e[1m"
+                                      << it.key() << "\e[0m'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '\e[1m" << module << "\e[0m' in '"
+                                   << name << "'! Returning first key '\e[1m" << data.begin().key() << "\e[0m'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: Done in https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b.
created_at: 2025-05-21T19:53:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: Done in https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b.
created_at: 2025-05-21T19:54:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+#ifndef CondFormats_HGCalObjects_HGCalESProducerTools_h
+#define CondFormats_HGCalObjects_HGCalESProducerTools_h
+
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include <string>
+#include <sstream>    // for std::istringstream
+#include <fnmatch.h>  // to match glob patterns
+#include <nlohmann/json.hpp>
path: CondFormats/HGCalObjects/interface/HGCalESProducerTools.h
user: login: IzaakWN
body: Done in https://github.com/cms-sw/cmssw/pull/48137/commits/5ba71768546ae894cbffa898b9c8b1bd9cad9a5b.
created_at: 2025-05-21T19:54:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: fwyzard
body: Why does it return the first key, in this case, instead of throwing an exception?
created_at: 2025-05-22T04:53:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_fedkey()");
+      throw ex;
+    }
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
+    while (it != data.end()) {
+      std::string fedkey = it.key();
+
+      // try as numerical range
+      int low, high;
+      char dash;
+      std::istringstream iss(fedkey.c_str());
+      iss >> low >> dash >> high;              // parse [integer][character][integer] pattern
+      if (iss.eof() and dash == '-') {         // matches pattern
+        if (low <= fedid and fedid <= high) {  // matches numerical range
+          matchedkey = fedkey;
+          break;
+        }
+
+        // try as glob pattern
+      } else {
+        const std::string sfedid = std::to_string(fedid);
+        std::regex re(edm::glob2reg(fedkey));
+        if (std::regex_match(sfedid, re)) {  // found matching key !
+          matchedkey = fedkey;
+          break;
+        }
+      }
+
+      ++it;
+    }
+    if (it == data.end())
+      edm::LogError("search_fedkey") << "search_fedkey: Could not find matching key for '" << fedid << "' in '" << name
+                                     << "'! Returning first key '" << matchedkey << "'...";
+    else
+      edm::LogInfo("search_fedkey") << "search_fedkey: Matched module='" << fedid << "' to fedkey='" << matchedkey
+                                    << "'";
+
+    return matchedkey;  // no matching key found in whole JSON map
+  }
+
+  // @short check if JSON data contains key
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname) {
+    bool iscomplete = true;
+    for (auto const& key : keys) {
+      if (not data[firstkey].contains(key)) {
+        edm::LogWarning("checkkeys") << " JSON is missing key '" << key << "' for " << firstkey << "!"
+                                     << " Please check file " << fname;
+        iscomplete = false;
+      }
+    }
+    return iscomplete;
+  }
+
+}  // namespace hgcal
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: fwyzard
body: Can you keep a newline on the last line ?
created_at: 2025-05-22T04:53:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: IzaakWN
body: The intention was to warn the user with the error message, but allow them to continue with the first key as a non-fatal fallback.
created_at: 2025-05-22T05:52:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: fwyzard
body: Unfortunately most `LogError` messages are basically ignored in production workflows, if not outright suppressed.

Is it meaningful for the client code to continue using the first key instead of a matching key ?
Or is it an error condition that should be checked and handled explicitly ?
created_at: 2025-05-22T06:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_fedkey()");
+      throw ex;
+    }
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
+    while (it != data.end()) {
+      std::string fedkey = it.key();
+
+      // try as numerical range
+      int low, high;
+      char dash;
+      std::istringstream iss(fedkey.c_str());
+      iss >> low >> dash >> high;              // parse [integer][character][integer] pattern
+      if (iss.eof() and dash == '-') {         // matches pattern
+        if (low <= fedid and fedid <= high) {  // matches numerical range
+          matchedkey = fedkey;
+          break;
+        }
+
+        // try as glob pattern
+      } else {
+        const std::string sfedid = std::to_string(fedid);
+        std::regex re(edm::glob2reg(fedkey));
+        if (std::regex_match(sfedid, re)) {  // found matching key !
+          matchedkey = fedkey;
+          break;
+        }
+      }
+
+      ++it;
+    }
+    if (it == data.end())
+      edm::LogError("search_fedkey") << "search_fedkey: Could not find matching key for '" << fedid << "' in '" << name
+                                     << "'! Returning first key '" << matchedkey << "'...";
+    else
+      edm::LogInfo("search_fedkey") << "search_fedkey: Matched module='" << fedid << "' to fedkey='" << matchedkey
+                                    << "'";
+
+    return matchedkey;  // no matching key found in whole JSON map
+  }
+
+  // @short check if JSON data contains key
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname) {
+    bool iscomplete = true;
+    for (auto const& key : keys) {
+      if (not data[firstkey].contains(key)) {
+        edm::LogWarning("checkkeys") << " JSON is missing key '" << key << "' for " << firstkey << "!"
+                                     << " Please check file " << fname;
+        iscomplete = false;
+      }
+    }
+    return iscomplete;
+  }
+
+}  // namespace hgcal
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: IzaakWN
body: Done: https://github.com/cms-sw/cmssw/pull/48137/commits/dafa02849c285fcd2e4ba132baf0a21f0d1b1c59!
created_at: 2025-05-22T07:22:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: IzaakWN
body: Okay, we decided to just throw an error in both `search_modkey` and `search_fedkey `, see https://github.com/cms-sw/cmssw/pull/48137/commits/dafa02849c285fcd2e4ba132baf0a21f0d1b1c59.
created_at: 2025-05-22T07:23:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_fedkey()");
+      throw ex;
+    }
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
+    while (it != data.end()) {
+      std::string fedkey = it.key();
+
+      // try as numerical range
+      int low, high;
+      char dash;
+      std::istringstream iss(fedkey.c_str());
+      iss >> low >> dash >> high;              // parse [integer][character][integer] pattern
+      if (iss.eof() and dash == '-') {         // matches pattern
+        if (low <= fedid and fedid <= high) {  // matches numerical range
+          matchedkey = fedkey;
+          break;
+        }
+
+        // try as glob pattern
+      } else {
+        const std::string sfedid = std::to_string(fedid);
+        std::regex re(edm::glob2reg(fedkey));
+        if (std::regex_match(sfedid, re)) {  // found matching key !
+          matchedkey = fedkey;
+          break;
+        }
+      }
+
+      ++it;
+    }
+    if (it == data.end())
+      edm::LogError("search_fedkey") << "search_fedkey: Could not find matching key for '" << fedid << "' in '" << name
+                                     << "'! Returning first key '" << matchedkey << "'...";
+    else
+      edm::LogInfo("search_fedkey") << "search_fedkey: Matched module='" << fedid << "' to fedkey='" << matchedkey
+                                    << "'";
+
+    return matchedkey;  // no matching key found in whole JSON map
+  }
+
+  // @short check if JSON data contains key
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname) {
+    bool iscomplete = true;
+    for (auto const& key : keys) {
+      if (not data[firstkey].contains(key)) {
+        edm::LogWarning("checkkeys") << " JSON is missing key '" << key << "' for " << firstkey << "!"
+                                     << " Please check file " << fname;
+        iscomplete = false;
+      }
+    }
+    return iscomplete;
+  }
+
+}  // namespace hgcal
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: IzaakWN
body: It seem like `scram build code-format` prefers no newline at the end here? https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44914/code-format.patch

Is there a way around it, or shall I remove the newline again to have the code checks succeed?
created_at: 2025-05-22T07:26:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_fedkey()");
+      throw ex;
+    }
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
+    while (it != data.end()) {
+      std::string fedkey = it.key();
+
+      // try as numerical range
+      int low, high;
+      char dash;
+      std::istringstream iss(fedkey.c_str());
+      iss >> low >> dash >> high;              // parse [integer][character][integer] pattern
+      if (iss.eof() and dash == '-') {         // matches pattern
+        if (low <= fedid and fedid <= high) {  // matches numerical range
+          matchedkey = fedkey;
+          break;
+        }
+
+        // try as glob pattern
+      } else {
+        const std::string sfedid = std::to_string(fedid);
+        std::regex re(edm::glob2reg(fedkey));
+        if (std::regex_match(sfedid, re)) {  // found matching key !
+          matchedkey = fedkey;
+          break;
+        }
+      }
+
+      ++it;
+    }
+    if (it == data.end())
+      edm::LogError("search_fedkey") << "search_fedkey: Could not find matching key for '" << fedid << "' in '" << name
+                                     << "'! Returning first key '" << matchedkey << "'...";
+    else
+      edm::LogInfo("search_fedkey") << "search_fedkey: Matched module='" << fedid << "' to fedkey='" << matchedkey
+                                    << "'";
+
+    return matchedkey;  // no matching key found in whole JSON map
+  }
+
+  // @short check if JSON data contains key
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname) {
+    bool iscomplete = true;
+    for (auto const& key : keys) {
+      if (not data[firstkey].contains(key)) {
+        edm::LogWarning("checkkeys") << " JSON is missing key '" << key << "' for " << firstkey << "!"
+                                     << " Please check file " << fname;
+        iscomplete = false;
+      }
+    }
+    return iscomplete;
+  }
+
+}  // namespace hgcal
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: fwyzard
body: Mhm, `clang-format` is asking to not have an empty new line, while I think the red sign on the github diff is about a file that ends without any newline on the last line.
created_at: 2025-05-22T08:14:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,95 @@
+#include "RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h"
+#include "FWCore/MessageLogger/interface/MessageLogger.h"
+#include "FWCore/Utilities/interface/RegexMatch.h"
+#include <sstream>  // for std::istringstream
+#include <regex>
+
+namespace hgcal {
+
+  // @short search first match to a given typecode key in a JSON (following insertion order)
+  // allow glob patterns, e.g. 'ML-*', 'M[LH]-[A-Z]3*', etc.
+  std::string search_modkey(const std::string& module, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_modkey()");
+      throw ex;
+    }
+    for (auto it = data.begin(); it != data.end(); ++it) {
+      std::regex re(edm::glob2reg(it.key()));
+      if (std::regex_match(module, re)) {  // found matching key !
+        edm::LogInfo("search_modkey") << "search_modkey: Matched module='" << module << "' to modkey='" << it.key()
+                                      << "'";
+        return it.key();  // return matching key
+      }
+    }
+    edm::LogError("search_modkey") << "search_modkey: Could not find matching key for '" << module << "' in '" << name
+                                   << "'! Returning first key '" << data.begin().key() << "'...";
+    return data.begin().key();  // no matching key found in whole JSON map
+  }
+
+  // @short search first match to a given FED index in a JSON (following insertion order)
+  // allow glob patterns like. '1*', '1[0-5]', etc. and
+  // allow numerical ranges like '0-20', '20-40', etc.
+  std::string search_fedkey(const int& fedid, const json& data, const std::string& name = "") {
+    if (!data.is_object() or data.empty()) {
+      cms::Exception ex("InvalidData");
+      ex << "'" << name << "' does not have the expected map/dict structure!";
+      ex.addContext("Calling hgcal::search_fedkey()");
+      throw ex;
+    }
+    auto it = data.begin();
+    std::string matchedkey = data.begin().key();  // use first key as default
+    while (it != data.end()) {
+      std::string fedkey = it.key();
+
+      // try as numerical range
+      int low, high;
+      char dash;
+      std::istringstream iss(fedkey.c_str());
+      iss >> low >> dash >> high;              // parse [integer][character][integer] pattern
+      if (iss.eof() and dash == '-') {         // matches pattern
+        if (low <= fedid and fedid <= high) {  // matches numerical range
+          matchedkey = fedkey;
+          break;
+        }
+
+        // try as glob pattern
+      } else {
+        const std::string sfedid = std::to_string(fedid);
+        std::regex re(edm::glob2reg(fedkey));
+        if (std::regex_match(sfedid, re)) {  // found matching key !
+          matchedkey = fedkey;
+          break;
+        }
+      }
+
+      ++it;
+    }
+    if (it == data.end())
+      edm::LogError("search_fedkey") << "search_fedkey: Could not find matching key for '" << fedid << "' in '" << name
+                                     << "'! Returning first key '" << matchedkey << "'...";
+    else
+      edm::LogInfo("search_fedkey") << "search_fedkey: Matched module='" << fedid << "' to fedkey='" << matchedkey
+                                    << "'";
+
+    return matchedkey;  // no matching key found in whole JSON map
+  }
+
+  // @short check if JSON data contains key
+  bool check_keys(const json& data,
+                  const std::string& firstkey,
+                  const std::vector<std::string>& keys,
+                  const std::string& fname) {
+    bool iscomplete = true;
+    for (auto const& key : keys) {
+      if (not data[firstkey].contains(key)) {
+        edm::LogWarning("checkkeys") << " JSON is missing key '" << key << "' for " << firstkey << "!"
+                                     << " Please check file " << fname;
+        iscomplete = false;
+      }
+    }
+    return iscomplete;
+  }
+
+}  // namespace hgcal
path: RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc
user: login: fwyzard
body: As a check, could you try to
  - remove the final empty line
  - run `dos2unix` on the file
?

Or open it with `vi` and write it with no other changes ?
created_at: 2025-05-22T08:15:44Z
author_association: CONTRIBUTOR

PR comments:
user: login: cmsbuild
created_at: 2025-05-21T15:06:31Z
body: cms-bot internal usage<!-- bot cache: {"commits":{"01d8f5d2fd45cd4090335b1e5b21c6d5af68e220":{"files":["CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc"],"squashed":true,"time":1747837186},"06e7a210444cc9c809ab52f6f5a4dda2aa156020":{"files":["CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc"],"squashed":true,"time":1747837200},"27d276e3f54cad070f3161f51ca45bc053b75bab":{"files":["RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc"],"squashed":true,"time":1747898511},"3d24632a7bc0ca2c237e9d61c62772071bb30b12":{"files":["CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc"],"squashed":true,"time":1747837529},"4716efb1944551431d0ffc1396c66223fa2963de":{"files":["RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc"],"squashed":true,"time":1747825577},"524385fed609627c8ff5332e7ac6017cf609203f":{"files":["RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc"],"squashed":true,"time":1747845975},"5ba71768546ae894cbffa898b9c8b1bd9cad9a5b":{"files":["RecoLocalCalo/HGCalRecAlgos/BuildFile.xml","RecoLocalCalo/HGCalRecAlgos/interface/HGCalESProducerTools.h","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc","RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc"],"squashed":true,"time":1747849833},"7a717fa7ec5d0f4b08f79d23c40d568d0509ec9e":{"files":["CondFormats/HGCalObjects/interface/HGCalCalibrationParameterHost.h","CondFormats/HGCalObjects/interface/HGCalConfigParamSoA.h","CondFormats/HGCalObjects/interface/alpaka/HGCalCalibrationParameterDevice.h","CondFormats/HGCalObjects/src/T_EventSetup_HGCalCalibrationParameterHost.cc","CondFormats/HGCalObjects/src/alpaka/T_EventSetup_HGCalCalibrationParameterDevice.cc","RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc","RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc","RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py"],"squashed":true,"time":1747652268},"7b65071a83b636e47f39dbc4fac5b559077fc611":{"files":["CondFormats/HGCalObjects/interface/HGCalConfiguration.h","CondFormats/HGCalObjects/interface/HGCalESProducerTools.h","CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","CondFormats/HGCalObjects/src/classes_def.xml","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/test/alpaka/HGCalRecHitESProducersTest.cc","RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py"],"squashed":true,"time":1747824832},"9ad503df54066058e9b888e756ec9b86117491fc":{"files":["CondFormats/HGCalObjects/interface/HGCalConfiguration.h","CondFormats/HGCalObjects/interface/HGCalESProducerTools.h","CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/test/testHGCalRecHitESProducers_cfg.py"],"squashed":true,"time":1747765545},"a135183c2d6c8bab82a0cebbf56d7ef457dfec48":{"files":["CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc"],"squashed":true,"time":1747837250},"a93c8b8e1fcdae0d40f23390f251ba29aa9ec0c1":{"files":["CondFormats/HGCalObjects/interface/HGCalMappingModuleIndexer.h","RecoLocalCalo/HGCalRecAlgos/interface/alpaka/HGCalRecHitCalibrationAlgorithms.h","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitCalibrationAlgorithms.dev.cc","RecoLocalCalo/HGCalRecAlgos/plugins/HGCalConfigurationESProducer.cc","RecoLocalCalo/HGCalRecAlgos/plugins/alpaka/HGCalRecHitProducers.cc"],"squashed":true,"time":1747837253},"bcd62e2f06f420257d87fb1cf7833049a4b446e4":{"files":[],"squashed":false,"time":1747898511},"dafa02849c285fcd2e4ba132baf0a21f0d1b1c59":{"files":["RecoLocalCalo/HGCalRecAlgos/src/HGCalESProducerTools.cc"],"squashed":true,"time":1747891252}},"emoji":{"2898697204":"+1","2898697367":"+1","2898702935":"+1","2898704518":"+1","2900871543":"+1","2901436846":"+1","2901641186":"+1","2901654835":"+1","2903319100":"+1","2907722336":"+1","2907971369":"+1"},"last_seen_sha":"bcd62e2f06f420257d87fb1cf7833049a4b446e4","signatures":{"2900871543":"27d276e3f54cad070f3161f51ca45bc053b75bab","2901436846":"27d276e3f54cad070f3161f51ca45bc053b75bab","2901641186":"27d276e3f54cad070f3161f51ca45bc053b75bab","2903319100":"27d276e3f54cad070f3161f51ca45bc053b75bab","2907722336":"27d276e3f54cad070f3161f51ca45bc053b75bab","2907971369":"27d276e3f54cad070f3161f51ca45bc053b75bab"}} -->
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T15:08:08Z
body: -code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44895

ERROR: Unable to merge PR.

See log https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44895/cms-checkout-topic.log

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T16:28:06Z
body: +code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44900

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T16:28:29Z
body: A new Pull Request was created by  @IzaakWN for master.

It involves the following packages:

- CondFormats/HGCalObjects (**alca, db, upgrade**)
- RecoLocalCalo/HGCalRecAlgos (**reconstruction, upgrade**)


@Moanwar, @atpathak, @cmsbuild, @francescobrivio, @jfernan2, @mandrenguyen, @perrotta, @srimanob, @subirsarkar can you please review it and eventually sign? Thanks.
@JanChyczynski, @PonIlya, @ReyerBand, @apsallid, @argiro, @bsunanda, @cseez, @denizsun, @edjtscott, @felicepantaleo, @hatakeyamak, @lgray, @missirol, @mmusich, @pfs, @rchatter, @rovere, @rsreds, @salimcerci, @sameasy, @seemasharmafnal, @sethzenz, @thomreis, @tocheng, @vandreev11, @wang0jin, @youyingli, @yuanchao this is something you requested to watch as well.
@antoniovilela, @mandrenguyen, @rappoccio, @sextonkennedy you are the release manager for this.

cms-bot commands are listed <a href="http://cms-sw.github.io/cms-bot-cmssw-cmds.html">here</a>

author_association: CONTRIBUTOR
user: login: pfs
created_at: 2025-05-21T17:19:10Z
body: assign hgcal-dpg
author_association: CONTRIBUTOR
user: login: pfs
created_at: 2025-05-21T17:19:14Z
body: please test
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T17:19:30Z
body: New categories assigned: hgcal-dpg

@cseez,@felicepantaleo,@pfs,@rovere you have been requested to review this Pull request/Issue and eventually sign? Thanks
author_association: CONTRIBUTOR
user: login: jfernan2
created_at: 2025-05-21T17:21:48Z
body: assign heterogeneous
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T17:22:05Z
body: New categories assigned: heterogeneous

@fwyzard,@makortel you have been requested to review this Pull request/Issue and eventually sign? Thanks
author_association: CONTRIBUTOR
user: login: jfernan2
created_at: 2025-05-21T17:22:33Z
body: please test
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T18:48:42Z
body: +code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44905

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T18:49:06Z
body: Pull request #48137 was updated. @Moanwar, @atpathak, @cmsbuild, @cseez, @felicepantaleo, @francescobrivio, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @perrotta, @pfs, @rovere, @srimanob, @subirsarkar can you please check and sign again.

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T19:52:39Z
body: +code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44907

- Found files with invalid states:
  - CondFormats/HGCalObjects/interface/HGCalESProducerTools.h:
    - Added: 06e7a210444cc9c809ab52f6f5a4dda2aa156020
    - Modified: a135183c2d6c8bab82a0cebbf56d7ef457dfec48
    - Deleted: 5ba71768546ae894cbffa898b9c8b1bd9cad9a5b

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-21T19:53:05Z
body: Pull request #48137 was updated. @Moanwar, @atpathak, @cmsbuild, @cseez, @felicepantaleo, @francescobrivio, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @perrotta, @pfs, @rovere, @srimanob, @subirsarkar can you please check and sign again.

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-22T07:22:49Z
body: -code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44914

- Found files with invalid states:
  - CondFormats/HGCalObjects/interface/HGCalESProducerTools.h:
    - Added: 06e7a210444cc9c809ab52f6f5a4dda2aa156020
    - Modified: a135183c2d6c8bab82a0cebbf56d7ef457dfec48
    - Deleted: 5ba71768546ae894cbffa898b9c8b1bd9cad9a5b

Code check has found code style and quality issues which could be resolved by applying following patch(s)

- **code-format**:
https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44914/code-format.patch
e.g. `curl -k https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44914/code-format.patch | patch -p1`
You can also run `scram build code-format` to apply code format directly

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-22T09:27:17Z
body: +code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44915

- Found files with invalid states:
  - CondFormats/HGCalObjects/interface/HGCalESProducerTools.h:
    - Added: 06e7a210444cc9c809ab52f6f5a4dda2aa156020
    - Modified: a135183c2d6c8bab82a0cebbf56d7ef457dfec48
    - Deleted: 5ba71768546ae894cbffa898b9c8b1bd9cad9a5b

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-22T09:27:38Z
body: Pull request #48137 was updated. @Moanwar, @atpathak, @cmsbuild, @cseez, @felicepantaleo, @francescobrivio, @fwyzard, @jfernan2, @makortel, @mandrenguyen, @perrotta, @pfs, @rovere, @srimanob, @subirsarkar can you please check and sign again.

author_association: CONTRIBUTOR
user: login: fwyzard
created_at: 2025-05-22T11:26:56Z
body: Looks OK to me.
Could you just squash the commits into a single one, or into a small number with a meaningful history ? 
author_association: CONTRIBUTOR
user: login: fwyzard
created_at: 2025-05-22T11:27:01Z
body: +heterogeneous
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-22T12:33:16Z
body: +code-checks

Logs: https://cmssdt.cern.ch/SDT/code-checks/cms-sw-PR-48137/44919

author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-22T12:33:40Z
body: Pull request #48137 was updated. @Moanwar, @atpathak, @cmsbuild, @cseez, @felicepantaleo, @francescobrivio, @jfernan2, @mandrenguyen, @perrotta, @pfs, @rovere, @srimanob, @subirsarkar can you please check and sign again.

author_association: CONTRIBUTOR
user: login: IzaakWN
created_at: 2025-05-22T12:34:44Z
body: > Looks OK to me. Could you just squash the commits into a single one, or into a small number with a meaningful history ?

Done: https://github.com/cms-sw/cmssw/pull/48137/commits/bcd62e2f06f420257d87fb1cf7833049a4b446e4.
author_association: CONTRIBUTOR
user: login: pfs
created_at: 2025-05-22T14:25:57Z
body: +1
author_association: CONTRIBUTOR
user: login: jfernan2
created_at: 2025-05-22T15:20:20Z
body: +1
author_association: CONTRIBUTOR
user: login: pfs
created_at: 2025-05-22T15:24:17Z
body: please test
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-22T18:30:13Z
body: +1

**Size**: This PR adds an extra 16KB to repository
**Summary**: https://cmssdt.cern.ch/SDT/jenkins-artifacts/pull-request-integration/PR-9534aa/46331/summary.html
**COMMIT**: bcd62e2f06f420257d87fb1cf7833049a4b446e4
**CMSSW**: CMSSW_15_1_X_2025-05-22-1100/el8_amd64_gcc12
**User test area**: For local testing, you can use `/cvmfs/cms-ci.cern.ch/week0/cms-sw/cmssw/48137/46331/install.sh` to create a dev area with all the needed externals and cmssw changes.


## Comparison Summary



**Summary**:
 - You potentially [removed 2 lines](https://cmssdt.cern.ch/SDT/jenkins-artifacts/baseLineComparisons/CMSSW_15_1_X_2025-05-22-1100+9534aa/68661/validateJR/logRootQA-events.log) from the logs
 - [Reco comparison results: 6 differences found in the comparisons](https://cmssdt.cern.ch/SDT/jenkins-artifacts/baseLineComparisons/CMSSW_15_1_X_2025-05-22-1100+9534aa/68661/validateJR.html)
 - DQMHistoTests: Total files compared: 50
 - DQMHistoTests: Total histograms compared: 4038193
 - DQMHistoTests: [Total failures: 6](https://cmssdt.cern.ch/SDT/jenkins-artifacts/baseLineComparisons/CMSSW_15_1_X_2025-05-22-1100+9534aa/68661/validateJR.html)
 - DQMHistoTests: Total nulls: 0
 - DQMHistoTests: Total successes: 4038167
 - DQMHistoTests: Total skipped: 20
 - DQMHistoTests: Total Missing objects: 0
 - DQMHistoSizes: Histogram memory added: 0.0 KiB( 49 files compared)
 - Checked 215 log files, 184 edm output root files, 50 DQM output files
 - [TriggerResults](https://cmssdt.cern.ch/SDT/jenkins-artifacts/baseLineComparisons/CMSSW_15_1_X_2025-05-22-1100+9534aa/68661/triggerResults): no differences found

author_association: CONTRIBUTOR
user: login: perrotta
created_at: 2025-05-23T05:38:11Z
body: +1
author_association: CONTRIBUTOR
user: login: Moanwar
created_at: 2025-05-25T09:42:13Z
body: +Upgrade
author_association: CONTRIBUTOR
user: login: cmsbuild
created_at: 2025-05-25T09:42:33Z
body: This pull request is fully signed and it will be integrated in one of the next master IBs (tests are also fine). This pull request will now be reviewed by the release team before it's merged. @antoniovilela, @sextonkennedy, @rappoccio, @mandrenguyen (and backports should be raised in the release meeting by the corresponding L2)
author_association: CONTRIBUTOR
user: login: mandrenguyen
created_at: 2025-05-25T17:47:03Z
body: +1
author_association: CONTRIBUTOR