From a0fa21194b4f35365c4af5fad1c71882e317a7c7 Mon Sep 17 00:00:00 2001
From: Christina Agapopoulou <christina.agapopoulou@cern.ch>
Date: Tue, 9 Apr 2024 14:48:28 +0200
Subject: [PATCH 1/5] Merge branch 'protect-against-missing-0-cond' into
 'master'

---
 Core/src/ConditionsGitReader.cpp             |   5 +
 Core/tests/CMakeLists.txt                    |   1 +
 Core/tests/src/test_condition_git_reader.cpp | 113 +++++++++++++++++++
 3 files changed, 119 insertions(+)
 create mode 100644 Core/tests/src/test_condition_git_reader.cpp

diff --git a/Core/src/ConditionsGitReader.cpp b/Core/src/ConditionsGitReader.cpp
index 29bd95c83..fca1c9916 100644
--- a/Core/src/ConditionsGitReader.cpp
+++ b/Core/src/ConditionsGitReader.cpp
@@ -101,6 +101,11 @@ int LHCb::Detector::ConditionsGitReader::getObject( const std::string& system_id
   try {
     std::tie( data, iov ) = m_condDB->get( {m_dbtag, path, time_point} );
   } catch ( std::runtime_error const& e ) { return 0; }
+  if ( !iov.valid() ) {
+    throw std::runtime_error(
+        fmt::format( "invalid IOV for {}:{}, cannot find data for point {} (probably missing 0 value)", m_dbtag, path,
+                     time_point ) );
+  }
 
   if ( !m_limitedIovPaths.empty() && m_limitedIovPaths.find( path ) != m_limitedIovPaths.end() ) {
     if ( iov.since != time_point ) {
diff --git a/Core/tests/CMakeLists.txt b/Core/tests/CMakeLists.txt
index da56121a0..d116c353a 100644
--- a/Core/tests/CMakeLists.txt
+++ b/Core/tests/CMakeLists.txt
@@ -29,6 +29,7 @@ add_executable(test_DDS
     src/test_DDS_tell40links.cpp
     src/test_DDS_interactionregion.cpp
     src/test_conddb_schema_handling.cpp
+    src/test_condition_git_reader.cpp
 )
 target_link_libraries(test_DDS
     Catch2::Catch2WithMain
diff --git a/Core/tests/src/test_condition_git_reader.cpp b/Core/tests/src/test_condition_git_reader.cpp
new file mode 100644
index 000000000..b0cb391a8
--- /dev/null
+++ b/Core/tests/src/test_condition_git_reader.cpp
@@ -0,0 +1,113 @@
+/*****************************************************************************\
+* (c) Copyright 2024 CERN for the benefit of the LHCb Collaboration           *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#include <Core/ConditionHelper.h>
+#include <Core/DeConditionCall.h>
+#include <Core/DeIOV.h>
+#include <Core/DetectorDataService.h>
+#include <Core/Keys.h>
+#include <DD4hep/Detector.h>
+#include <DD4hep/DetectorLoad.h>
+#include <Detector/Test/Fixture.h>
+#include <nlohmann/json.hpp>
+
+#include <catch2/catch.hpp>
+
+namespace {
+  struct DummyDeIOVObject final : LHCb::Detector::detail::DeIOVObject {
+    using DeIOVObject::DeIOVObject;
+    void applyToAllChildren( const std::function<void( LHCb::Detector::DeIOVElement<DeIOVObject> )>& ) const override {}
+  };
+
+  struct DummyConditionCall : LHCb::Detector::DeConditionCall {
+    using DeConditionCall::DeConditionCall;
+    virtual dd4hep::Condition operator()( const dd4hep::ConditionKey&,
+                                          dd4hep::cond::ConditionUpdateContext& context ) override final {
+      auto det = dd4hep::Detector::getInstance().detector( "test" );
+      return LHCb::Detector::DeIOV{new DummyDeIOVObject( det, context, 9000001, false )};
+    }
+  };
+  long create_conditions_recipes( dd4hep::Detector& description, xml_h e ) {
+    // Use the helper to load the XML, setup the callback according
+    LHCb::Detector::ConditionConfigHelper<DummyConditionCall> config_helper{description, "test", e};
+    config_helper.configure();
+    return 1;
+  }
+} // namespace
+DECLARE_XML_DOC_READER( Test_Dummy_cond, create_conditions_recipes )
+
+static constexpr const char* main_xml = R"(<lccdd>
+  <info name="Test" status="development"/>
+  <geometry open="true" close="false"/>
+  <includes>
+    <!-- FIXME: it would be nice to embed the materials needed for the test. but I didn't manage -->
+    <gdmlFile ref="tests/testscope/sc/elements.xml"/>
+    <gdmlFile ref="tests/testscope/sc/materials.xml"/>
+  </includes>
+  <define>
+    <constant name="world_side" value="10*m" />
+    <constant name="world_x" value="world_side/2" />
+    <constant name="world_y" value="world_side/2" />
+    <constant name="world_z" value="world_side/2" />
+  </define>
+  <detectors>
+    <detector name="test" type="DD4hep_VolumeAssembly" parent="/world">
+      <envelope material="Air">
+        <shape name="BiggerBox" type="Box" dx="100*cm" dy="100*cm" dz="100*cm"/>
+      </envelope>
+      <position x="0*cm" y="0*cm" z="0*cm"/>
+    </detector>
+  </detectors>
+  <plugins>
+    <plugin name="Test_Dummy_cond_XML_reader" type="xml">
+      <conditions_config prefix="Conditions/" />
+      <conditions path="test/no0cond.yml">
+        <condition name="cond"/>
+      </conditions>
+    </plugin>
+  </plugins>
+</lccdd>)";
+
+TEST_CASE( "ConditionGitReader.missing_0" ) {
+  Detector::Test::Fixture f;
+
+  auto& description = f.description();
+  dd4hep::DetectorLoad{description}.processXMLString( main_xml );
+
+  auto det = description.detector( "test" );
+  // the `!!` is needed because handles have `operator!` but not `operator bool`
+  REQUIRE( !!det );
+
+  std::vector<std::string>            detector_list{"/world", "test"};
+  LHCb::Detector::DetectorDataService dds( description, detector_list );
+  // this DB is invalid as it misses the special condition for time point 0
+  // and hitting it must throw an exception
+  dds.initialize( nlohmann::json{{"repository",
+                                  R"(json:{
+    "Conditions": { "test": { "no0cond.yml": {".condition": "", "1000": "cond: {value: 42}"} } },
+    ".schema.json": "{\"Conditions/test/no0cond.yml\":[\"cond\"]}"
+  })"}} );
+
+  // get a valid condition slice
+  auto slice = dds.get_slice( 2000 );
+  REQUIRE( slice );
+
+  // this is a condition actually in the DB
+  auto cond = slice->get( det, LHCb::Detector::item_key( "cond" ) ).get<nlohmann::json>();
+  REQUIRE( cond.is_object() );
+  CHECK( cond.contains( "value" ) );
+
+  // hit the problematic IOV
+  using Catch::Matchers::Contains;
+  CHECK_THROWS_WITH( dds.get_slice( 100 ), Contains( "Conditions/test/no0cond.yml" ) && Contains( "missing 0 value" ) );
+}
+
+#include "DD4hep/detail/Handle.inl"
+DD4HEP_INSTANTIATE_HANDLE_UNNAMED( DummyDeIOVObject, LHCb::Detector::detail::DeIOVObject, ConditionObject );
-- 
GitLab


From 961aa3e404c086a5a03deaaf8e27987a0705b35b Mon Sep 17 00:00:00 2001
From: Menglin Xu <menglin.xu@cern.ch>
Date: Mon, 15 Apr 2024 18:37:26 +0200
Subject: [PATCH 2/5] cherry-pick the changes in checksum at master branch

---
 compact/run3/2024.Q1.2-v00.00/checksums | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/compact/run3/2024.Q1.2-v00.00/checksums b/compact/run3/2024.Q1.2-v00.00/checksums
index 3e2a7a091..a5daa60a2 100644
--- a/compact/run3/2024.Q1.2-v00.00/checksums
+++ b/compact/run3/2024.Q1.2-v00.00/checksums
@@ -1,4 +1,4 @@
-/world_volume: 1337405057
+/world_volume: 699371414
 /world_volume/lvAfterMagnetRegion_3: 1374469345
 /world_volume/lvAfterMagnetRegion_3/lvRich2Master_1: 187033957
 /world_volume/lvAfterMagnetRegion_3/lvRich2Master_1/pvRich2EntryWinTubeLock: 1979723490
@@ -126,7 +126,7 @@
 /world_volume/lvDownstreamRegion_4/lvUX85SupportsDownstream_7/pvUX853CableDownstream04: 144779018
 /world_volume/lvDownstreamRegion_4/lvUX85SupportsDownstream_7/pvUX853DownstreamCollar: 954691355
 /world_volume/lvDownstreamRegion_4/lvUX85SupportsDownstream_7/pvUX854SlSup: 4271947702
-/world_volume/lvMagnetRegion_2: 15806978
+/world_volume/lvMagnetRegion_2: 1960380511
 /world_volume/lvMagnetRegion_2/MagnetEnvelope_0: 3963018011
 /world_volume/lvMagnetRegion_2/MagnetEnvelope_0/Left_Vertical_Part: 1575235178
 /world_volume/lvMagnetRegion_2/MagnetEnvelope_0/LowerCoil: 3675638069
@@ -140,7 +140,7 @@
 /world_volume/lvMagnetRegion_2/lvUX85InMagnet_1/pvUX853: 4181158773
 /world_volume/lvMagnetRegion_2/lvUX85InMagnet_1/pvUX85Bellow6900: 38089332
 /world_volume/lvMagnetRegion_2/lvUX85InMagnet_1/pvUX85Compensator2800: 3908543996
-/world_volume/lvMagnetRegion_2/lvUX85SupportsInMagnet_2: 3160524869
+/world_volume/lvMagnetRegion_2/lvUX85SupportsInMagnet_2: 2760019527
 /world_volume/lvMagnetRegion_2/lvUX85SupportsInMagnet_2/lvUX852CollarClipScrew11: 196374262
 /world_volume/lvMagnetRegion_2/lvUX85SupportsInMagnet_2/lvUX852CollarClipScrew12: 1974491522
 /world_volume/lvMagnetRegion_2/lvUX85SupportsInMagnet_2/lvUX852CollarClipScrew13: 3261745846
-- 
GitLab


From 7f8929a843b85b0766a8bfc5da69383b1917894d Mon Sep 17 00:00:00 2001
From: Tommaso Fulghesu <tommaso.fulghesu@cern.ch>
Date: Tue, 16 Apr 2024 09:26:26 +0200
Subject: [PATCH 3/5] Access to LHC condition information

---
 CMakeLists.txt                                |  1 +
 Core/tests/CMakeLists.txt                     |  1 +
 Core/tests/src/test_DDS_lhcinfo.cpp           | 70 +++++++++++++++++++
 Detector/LHCb/include/Detector/LHCb/DeLHCb.h  |  3 +
 Detector/LHCb/include/Detector/LHCb/LHCInfo.h | 29 ++++++++
 Detector/LHCb/src/DeLHCb.cpp                  | 13 +++-
 Detector/LHCb/src/LHCInfo.cpp                 | 33 +++++++++
 tests/ConditionsIOV/.schema.json              |  3 +
 .../Conditions/LHCb/Online/LHC.yml/.condition |  0
 .../Conditions/LHCb/Online/LHC.yml/0          |  3 +
 .../Conditions/LHCb/Online/LHC.yml/200        | 14 ++++
 11 files changed, 169 insertions(+), 1 deletion(-)
 create mode 100644 Core/tests/src/test_DDS_lhcinfo.cpp
 create mode 100644 Detector/LHCb/include/Detector/LHCb/LHCInfo.h
 create mode 100644 Detector/LHCb/src/LHCInfo.cpp
 create mode 100644 tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/.condition
 create mode 100644 tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/0
 create mode 100644 tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/200

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c97bd9a65..ade15a1a8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -99,6 +99,7 @@ add_library(DetectorLib SHARED
     Detector/LHCb/src/DeLHCbHandles.cpp
     Detector/LHCb/src/InteractionRegion.cpp
     Detector/LHCb/src/Tell40Links.cpp
+    Detector/LHCb/src/LHCInfo.cpp
     Detector/Rich/src/DeRich.cpp
     Detector/Rich/src/DeRichRadiator.cpp
     Detector/Rich/src/DeRichMapmt.cpp
diff --git a/Core/tests/CMakeLists.txt b/Core/tests/CMakeLists.txt
index d116c353a..3b3a14f58 100644
--- a/Core/tests/CMakeLists.txt
+++ b/Core/tests/CMakeLists.txt
@@ -28,6 +28,7 @@ add_executable(test_DDS
     src/test_DDS_limit_IOV.cpp
     src/test_DDS_tell40links.cpp
     src/test_DDS_interactionregion.cpp
+    src/test_DDS_lhcinfo.cpp
     src/test_conddb_schema_handling.cpp
     src/test_condition_git_reader.cpp
 )
diff --git a/Core/tests/src/test_DDS_lhcinfo.cpp b/Core/tests/src/test_DDS_lhcinfo.cpp
new file mode 100644
index 000000000..65d24768a
--- /dev/null
+++ b/Core/tests/src/test_DDS_lhcinfo.cpp
@@ -0,0 +1,70 @@
+/*****************************************************************************\
+* (c) Copyright 2024 CERN for the benefit of the LHCb Collaboration           *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#include <Core/DetectorDataService.h>
+#include <Core/Keys.h>
+#include <DD4hep/Detector.h>
+#include <Detector/LHCb/DeLHCb.h>
+#include <Detector/Test/Fixture.h>
+#include <cstdio>
+
+#include <catch2/catch.hpp>
+
+TEST_CASE( "LHCCondition loading" ) {
+  namespace fs = std::filesystem;
+  using Catch::Matchers::Contains;
+
+  Detector::Test::Fixture f;
+
+  auto& description = f.description();
+
+  description.fromXML( "compact/run3/trunk/LHCb.xml" );
+
+  REQUIRE( description.state() == dd4hep::Detector::READY );
+
+  auto det = description.detector( "/world" );
+  // the `!!` is needed because handles have `operator!` but not `operator bool`
+  REQUIRE( !!det );
+
+  LHCb::Detector::DetectorDataService dds( description, {"/world"} );
+  dds.initialize( nlohmann::json( {{"repository", "file:tests/ConditionsIOV"}} ) );
+
+  {
+    // get a condition slice where the condition exists
+    // - we should get the values from the condition
+
+    auto slice = dds.get_slice( 200 );
+    REQUIRE( slice );
+
+    LHCb::Detector::DeLHCb lhcb = slice->get( det, LHCb::Detector::Keys::deKey );
+    REQUIRE( !!lhcb );
+
+    auto lhcinfo = lhcb.lhcInfo();
+    REQUIRE( lhcinfo.has_value() );
+
+    CHECK( lhcinfo.value().fillnumber == 7974 );
+    CHECK( lhcinfo.value().lhcstate == "INJECTION" );
+    CHECK( lhcinfo.value().lhcenergy == 450 );
+    CHECK( abs( lhcinfo.value().lhcbclockphase - 0.46556840909089 ) < 1e-6 );
+  }
+
+  {
+    // get a condition slice where the condition doesn't exist
+    // - we should get the fallback values
+    auto slice = dds.get_slice( 0 );
+    REQUIRE( slice );
+
+    LHCb::Detector::DeLHCb lhcb = slice->get( det, LHCb::Detector::Keys::deKey );
+    REQUIRE( !!lhcb );
+
+    auto lhcinfo = lhcb.lhcInfo();
+    REQUIRE( !lhcinfo.has_value() );
+  }
+}
diff --git a/Detector/LHCb/include/Detector/LHCb/DeLHCb.h b/Detector/LHCb/include/Detector/LHCb/DeLHCb.h
index e942f3ebf..0d99ece88 100644
--- a/Detector/LHCb/include/Detector/LHCb/DeLHCb.h
+++ b/Detector/LHCb/include/Detector/LHCb/DeLHCb.h
@@ -14,6 +14,7 @@
 #include <Core/DeIOV.h>
 #include <Core/Keys.h>
 #include <Detector/LHCb/InteractionRegion.h>
+#include <Detector/LHCb/LHCInfo.h>
 #include <Detector/LHCb/Tell40Links.h>
 
 #include <fmt/core.h>
@@ -31,6 +32,7 @@ namespace LHCb::Detector {
       std::vector<DeIOV> children;
       Tell40Links        m_tell40links;
       std::optional<InteractionRegion> m_interactionRegion;
+      std::optional<LHCInfo>           m_lhcinfo;
     };
 
     // Utility method to lookup DeIOV object from the condition slice
@@ -46,6 +48,7 @@ namespace LHCb::Detector {
 
     /// Provide the position, spread, and tilt of the interaction region
     std::optional<InteractionRegion> interactionRegion() const { return this->access()->m_interactionRegion; }
+    std::optional<LHCInfo>           lhcInfo() const { return this->access()->m_lhcinfo; }
   };
 
   // Utility method to setup DeLHCb
diff --git a/Detector/LHCb/include/Detector/LHCb/LHCInfo.h b/Detector/LHCb/include/Detector/LHCb/LHCInfo.h
new file mode 100644
index 000000000..d432bd810
--- /dev/null
+++ b/Detector/LHCb/include/Detector/LHCb/LHCInfo.h
@@ -0,0 +1,29 @@
+/*****************************************************************************\
+* (c) Copyright 2023 CERN for the benefit of the LHCb Collaboration           *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#pragma once
+
+#include <nlohmann/json_fwd.hpp>
+
+namespace LHCb::Detector {
+  struct LHCInfo {
+
+    LHCInfo() = default;
+    LHCInfo( const nlohmann::json& obj );
+
+    unsigned int fillnumber{};
+    std::string  lhcstate{};
+    int          lhcenergy{};
+    float        lhcbclockphase{};
+    float        xangleh{};
+    float        xanglev{};
+    std::string  beamtype{};
+  };
+} // namespace LHCb::Detector
diff --git a/Detector/LHCb/src/DeLHCb.cpp b/Detector/LHCb/src/DeLHCb.cpp
index a17b7fa83..a8531146a 100644
--- a/Detector/LHCb/src/DeLHCb.cpp
+++ b/Detector/LHCb/src/DeLHCb.cpp
@@ -21,7 +21,6 @@
 LHCb::Detector::detail::DeLHCbObject::DeLHCbObject( const dd4hep::DetElement&             de,
                                                     dd4hep::cond::ConditionUpdateContext& ctxt )
     : DeIOVObject( de, ctxt, 9000000, false ) {
-
   {
     auto cond = ctxt.condition( hash_key( de, "Tell40Links" ), false );
     if ( cond.isValid() ) {
@@ -36,6 +35,13 @@ LHCb::Detector::detail::DeLHCbObject::DeLHCbObject( const dd4hep::DetElement&
       if ( !ir.is_null() ) { m_interactionRegion = ir; }
     }
   }
+  {
+    auto cond = ctxt.condition( hash_key( de, "LHC" ), false );
+    if ( cond.isValid() ) {
+      auto lhcinfo = cond.get<nlohmann::json>();
+      if ( !lhcinfo.is_null() ) { m_lhcinfo = lhcinfo; }
+    }
+  }
 }
 
 void LHCb::Detector::detail::DeLHCbObject::applyToAllChildren(
@@ -124,5 +130,10 @@ void LHCb::Detector::setup_DeLHCb_callback( dd4hep::Detector& description ) {
     depbuilder.add( hash_key( de, "InteractionRegion" ) );
   }
 
+  if ( !schema || schema->has( "Conditions/LHCb/Online/LHC.yml", "LHC" ) ) {
+    ( *requests )->addLocation( de, LHCb::Detector::item_key( "LHC" ), "Conditions/LHCb/Online/LHC.yml", "LHC" );
+    depbuilder.add( hash_key( de, "LHC" ) );
+  }
+
   ( *requests )->addDependency( depbuilder.release() );
 }
diff --git a/Detector/LHCb/src/LHCInfo.cpp b/Detector/LHCb/src/LHCInfo.cpp
new file mode 100644
index 000000000..78f4dbeac
--- /dev/null
+++ b/Detector/LHCb/src/LHCInfo.cpp
@@ -0,0 +1,33 @@
+/*****************************************************************************\
+* (c) Copyright 2023 CERN for the benefit of the LHCb Collaboration           *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#include <Detector/LHCb/LHCInfo.h>
+
+#include <Core/Utils.h>
+
+#include <DD4hep/DD4hepUnits.h>
+#include <DD4hep/Handle.h>
+
+#include <nlohmann/json.hpp>
+
+LHCb::Detector::LHCInfo::LHCInfo( const nlohmann::json& obj ) {
+  if ( !obj.contains( "FillNumber" ) || !obj.contains( "LHCState" ) || !obj.contains( "LHCEnergy" ) ||
+       !obj.contains( "LHCbClockPhase" ) ) {
+    throw std::runtime_error{"LHC condition is empty"};
+  }
+
+  fillnumber     = obj.at( "FillNumber" );
+  lhcstate       = obj.at( "LHCState" );
+  lhcenergy      = obj.at( "LHCEnergy" );
+  lhcbclockphase = obj.at( "LHCbClockPhase" );
+  if ( obj.contains( "XAngleH" ) ) xangleh = obj.at( "XAngleH" );
+  if ( obj.contains( "XAngleV" ) ) xanglev = obj.at( "XAngleV" );
+  if ( obj.contains( "BeamType" ) ) beamtype = obj.at( "BeamType" );
+}
diff --git a/tests/ConditionsIOV/.schema.json b/tests/ConditionsIOV/.schema.json
index ccc777d2c..610e08c52 100644
--- a/tests/ConditionsIOV/.schema.json
+++ b/tests/ConditionsIOV/.schema.json
@@ -1391,6 +1391,9 @@
   "Conditions/LHCb/Online/InteractionRegion.yml": [
     "InteractionRegion"
   ],
+  "Conditions/LHCb/Online/LHC.yml": [
+    "LHC"
+  ],
   "Conditions/LHCb/Online/Magnet.yml": [
     "Magnet"
   ],
diff --git a/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/.condition b/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/.condition
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/0 b/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/0
new file mode 100644
index 000000000..b7721432b
--- /dev/null
+++ b/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/0
@@ -0,0 +1,3 @@
+# Special case of empty condition
+---
+LHC:
diff --git a/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/200 b/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/200
new file mode 100644
index 000000000..ca461d4b2
--- /dev/null
+++ b/tests/ConditionsIOV/Conditions/LHCb/Online/LHC.yml/200
@@ -0,0 +1,14 @@
+# Example of LHC condition
+# ```
+# LHCCondition:
+#     fillnumber: [int]
+#     lhcbstate: [string]
+#     lhcbenergy: [int]
+#     lhcbclockphase: [float]
+# ```
+---
+LHC:
+        FillNumber: 7974
+        LHCState: INJECTION
+        LHCEnergy: 450
+        LHCbClockPhase: 0.46556840909089
-- 
GitLab


From e2acad9bd314f19bad165b74db568ac1a8c7cd32 Mon Sep 17 00:00:00 2001
From: Rosen Matev <rosen.matev@cern.ch>
Date: Fri, 19 Apr 2024 16:03:57 +0200
Subject: [PATCH 4/5] Prepare Detector v1r31

[ci skip]
---
 CMakeLists.txt        |  2 +-
 ReleaseNotes/v1r31.md | 11 +++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)
 create mode 100644 ReleaseNotes/v1r31.md

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ade15a1a8..0fd76aaa1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@
 ###############################################################################
 cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
 
-project(Detector VERSION 1.30)
+project(Detector VERSION 1.31)
 
 set(CMAKE_EXPORT_COMPILE_COMMANDS "ON")
 add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
diff --git a/ReleaseNotes/v1r31.md b/ReleaseNotes/v1r31.md
new file mode 100644
index 000000000..5a9f9bbf9
--- /dev/null
+++ b/ReleaseNotes/v1r31.md
@@ -0,0 +1,11 @@
+2024-04-19 Detector v1r31
+===
+
+This version uses LCG [105a](http://lcginfo.cern.ch/release/105a/) with ROOT 6.30.04.
+
+This version is released on the `2024-patches` branch.
+Built relative to Detector [v1r30](/../../tags/v1r30), with the following changes:
+
+- Cherry-pick the changes in checksum at master branch, !540 (@mexu)
+- Merge branch 'protect-against-missing-0-cond' into 'master', !538 (@cagapopo)
+- Access to LHC condition information, !418 (@tfulghes)
-- 
GitLab


From 8fb63b5d28d1f4347e2d257ba0e14c31153ffc2d Mon Sep 17 00:00:00 2001
From: Hangyi Wu <hangyi.wu@cern.ch>
Date: Tue, 23 Apr 2024 11:40:04 +0200
Subject: [PATCH 5/5] UT ChannelID to provide subsector and asic info

---
 Detector/UT/include/Detector/UT/ChannelID.h | 68 ++++++++++++++++-----
 Detector/UT/src/DeUT.cpp                    | 18 ++----
 2 files changed, 58 insertions(+), 28 deletions(-)

diff --git a/Detector/UT/include/Detector/UT/ChannelID.h b/Detector/UT/include/Detector/UT/ChannelID.h
index cd18567a6..5bf6af3dd 100644
--- a/Detector/UT/include/Detector/UT/ChannelID.h
+++ b/Detector/UT/include/Detector/UT/ChannelID.h
@@ -24,19 +24,23 @@ namespace LHCb::Detector::UT {
 
     /// Bitmasks for UT bitfield that are different from TT/IT
     enum struct Mask : unsigned {
-      strip        = 0x1ffL,
-      sector       = 0x200L,
-      module       = 0x1c00L,
-      face         = 0x2000L,
-      stave        = 0x3c000L,
-      layer        = 0xc0000L,
-      side         = 0x100000,
-      type         = 0x600000,
-      uniquelayer  = layer | side,
-      uniquestave  = uniquelayer | stave,
-      uniqueface   = uniquestave | face,
-      uniquemodule = uniqueface | module,
-      uniqueSector = uniquemodule | sector
+      strip           = 0x1ffL,
+      asic            = 0x180L,
+      subsector       = 0x100L,
+      sector          = 0x200L,
+      module          = 0x1c00L,
+      face            = 0x2000L,
+      stave           = 0x3c000L,
+      layer           = 0xc0000L,
+      side            = 0x100000,
+      type            = 0x600000,
+      uniquelayer     = layer | side,
+      uniquestave     = uniquelayer | stave,
+      uniqueface      = uniquestave | face,
+      uniquemodule    = uniqueface | module,
+      uniquesector    = uniquemodule | sector,
+      uniquesubsector = uniquesector | subsector,
+      uniqueasic      = uniquesector | asic
     };
 
     template <Mask m>
@@ -68,10 +72,10 @@ namespace LHCb::Detector::UT {
     /// constructor with side, halflayer, stave, face, module, sector, strip. New style
     ChannelID( const unsigned int iType, const unsigned int iSide, const unsigned int iHalflayer,
                const unsigned int iStave, const unsigned int iFace, const unsigned int iModule,
-               const unsigned int iSubsector, const unsigned int iStrip )
+               const unsigned int iSector, const unsigned int iStrip )
         : ChannelID{shift<Mask::type>( iType ) | shift<Mask::side>( iSide ) | shift<Mask::layer>( iHalflayer ) |
                     shift<Mask::stave>( iStave ) | shift<Mask::face>( iFace ) | shift<Mask::module>( iModule ) |
-                    shift<Mask::sector>( iSubsector ) | shift<Mask::strip>( iStrip )} {}
+                    shift<Mask::sector>( iSector ) | shift<Mask::strip>( iStrip )} {}
 
     /// cast
     constexpr operator unsigned int() const { return m_channelID; }
@@ -85,6 +89,9 @@ namespace LHCb::Detector::UT {
     /// Retrieve sector
     [[nodiscard]] constexpr unsigned int sector() const { return extract<Mask::sector>( m_channelID ); }
 
+    /// Retrieve subsector
+    [[nodiscard]] constexpr unsigned int subSector() const { return extract<Mask::subsector>( m_channelID ); }
+
     /// Retrieve detRegion
     [[nodiscard]] constexpr unsigned int detRegion() const { return stave() < 2 ? 2 : side() == 0 ? 1 : 3; }
 
@@ -94,6 +101,24 @@ namespace LHCb::Detector::UT {
     /// Retrieve unique layer
     [[nodiscard]] constexpr unsigned int uniqueLayer() const { return extract<Mask::uniquelayer>( m_channelID ); }
 
+    /// Trim to keep until sector part
+    [[nodiscard]] constexpr unsigned int getFullSector() const {
+      unsigned int m = (unsigned int)Mask::uniquesector | (unsigned int)Mask::type;
+      return channelID() & m;
+    }
+
+    /// Trim to keep until subsector part
+    [[nodiscard]] constexpr unsigned int getFullSubSector() const {
+      unsigned int m = (unsigned int)Mask::uniquesubsector | (unsigned int)Mask::type;
+      return channelID() & m;
+    }
+
+    /// Trim to keep until asic part
+    [[nodiscard]] constexpr unsigned int getFullAsic() const {
+      unsigned int m = (unsigned int)Mask::uniqueasic | (unsigned int)Mask::type;
+      return channelID() & m;
+    }
+
     /// Print method for python NOT NEEDED + SLOW IN C++ use operator<<
     [[nodiscard]] std::string toString() const;
 
@@ -109,11 +134,22 @@ namespace LHCb::Detector::UT {
     /// Retrieve strip
     [[nodiscard]] constexpr unsigned int strip() const { return extract<Mask::strip>( m_channelID ); }
 
+    /// Retrive asic
+    [[nodiscard]] constexpr unsigned int asic() const { return extract<Mask::asic>( m_channelID ); }
+
     /// Retrieve station
     [[nodiscard]] constexpr unsigned int station() const { return ( this->layer() / 2 + 1u ); }
 
     /// Retrieve unique sector
-    [[nodiscard]] constexpr unsigned int uniqueSector() const { return extract<Mask::uniqueSector>( m_channelID ); }
+    [[nodiscard]] constexpr unsigned int uniqueSector() const { return extract<Mask::uniquesector>( m_channelID ); }
+
+    /// Retrieve unique sub sector
+    [[nodiscard]] constexpr unsigned int uniqueSubSector() const {
+      return extract<Mask::uniquesubsector>( m_channelID );
+    }
+
+    /// Retrieve unique asic
+    [[nodiscard]] constexpr unsigned int uniqueAsic() const { return extract<Mask::uniqueasic>( m_channelID ); }
 
     /// Retrieve side
     [[nodiscard]] constexpr unsigned int side() const { return extract<Mask::side>( m_channelID ); }
diff --git a/Detector/UT/src/DeUT.cpp b/Detector/UT/src/DeUT.cpp
index a43c7ca1c..9fecde2d2 100644
--- a/Detector/UT/src/DeUT.cpp
+++ b/Detector/UT/src/DeUT.cpp
@@ -197,7 +197,7 @@ LHCb::Detector::UT::detail::DeUTModuleObject::DeUTModuleObject( const dd4hep::De
                   0,
                   0} {
   // type of stave
-  auto staveVolName = de.parent().parent().volume().name();
+  auto staveVolName = de.parent().parent().volume().name(); // lvStaveA/B/C
   m_type            = std::string( staveVolName + 8, staveVolName + std::strlen( staveVolName ) );
   // flatten
   for ( auto& group : de.children() )
@@ -327,17 +327,11 @@ LHCb::Detector::UT::detail::DeUTSensorObject::DeUTSensorObject( const dd4hep::De
   // UTSensorObject actually for half sensors for TypeBCD
 
   // get pitch size
-  auto        nameString = de.placement().volume().name();
-  std::string m_type( nameString + 8, nameString + std::strlen( nameString ) );
-  if ( m_type == "Norm" ) {
-    m_pitch      = dd4hep::_toDouble( "UTSensorAPitch" );
-    m_sensorType = 'A';
-  } else {
-    m_pitch = dd4hep::_toDouble( "UTSensorBCDPitch" );
-    if ( m_type == "Dual" ) m_sensorType = 'B';
-    if ( m_type == "Quad" ) m_sensorType = 'C';
-    if ( m_type == "Hole" ) m_sensorType = 'D';
-  }
+  auto        logVolName       = de.placement().volume().name();
+  auto        moduleLogVolName = de.parent().parent().parent().placement().volume().name();
+  std::string m_type( logVolName + 8, logVolName + std::strlen( logVolName ) ); // lvSensor(Norm|Dual|Quad|Hole)
+  m_sensorType = moduleLogVolName[std::strlen( moduleLogVolName ) - 1];         // lvModule(A|B|C|D)
+  m_pitch      = m_type == "Norm" ? dd4hep::_toDouble( "UTSensorAPitch" ) : dd4hep::_toDouble( "UTSensorBCDPitch" );
 
   m_uMaxLocal   = 0.5f * ( m_pitch * m_nStrip );
   m_uMinLocal   = -m_uMaxLocal;
-- 
GitLab