From ae101e833499de4445cc8dbf6747e11c57a3753f Mon Sep 17 00:00:00 2001
From: Joerg Stelzer <joerg.stelzer@cern.ch>
Date: Mon, 15 Feb 2021 17:23:39 +0000
Subject: [PATCH] L1 triggr menu Run2 to Run3 converter

---
 .../TrigConfData/TrigConfData/L1Menu.h        |   5 +-
 .../TrigConfData/src/L1CTP.cxx                |   7 +-
 .../TrigConfData/src/L1Menu.cxx               |  18 +-
 .../TrigConfData/src/L1PrescalesSet.cxx       |   2 +-
 .../{JsonFileWriter.h => JsonFileWriterL1.h}  |   8 +-
 .../TrigConfIO/src/JsonFileWriterHLT.cxx      |   4 +-
 ...sonFileWriter.cxx => JsonFileWriterL1.cxx} |  62 +-
 .../TrigConfIO/utils/TriggerMenuRW.cxx        |   4 +-
 .../TrigConfStorage/CMakeLists.txt            |   2 +-
 .../TrigConfStorage/src/MasterTableLoader.cxx |  16 +-
 .../TrigConfStorage/src/MasterTableLoader.h   |  10 +-
 .../TrigConfStorage/src/XMLMenuLoader.cxx     |   1 -
 .../TrigConfStorage/src/test/ReadWrite.cxx    | 123 ++--
 .../src/test/Run2toRun3Converters.h           |  11 -
 ...erters.cxx => Run2toRun3ConvertersHLT.cxx} |  99 ----
 .../src/test/Run2toRun3ConvertersHLT.h        |   9 +
 .../src/test/Run2toRun3ConvertersL1.cxx       | 529 ++++++++++++++++++
 .../src/test/Run2toRun3ConvertersL1.h         |  25 +
 18 files changed, 706 insertions(+), 229 deletions(-)
 rename Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/{JsonFileWriter.h => JsonFileWriterL1.h} (82%)
 rename Trigger/TrigConfiguration/TrigConfIO/src/{JsonFileWriter.cxx => JsonFileWriterL1.cxx} (93%)
 delete mode 100644 Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.h
 rename Trigger/TrigConfiguration/TrigConfStorage/src/test/{Run2toRun3Converters.cxx => Run2toRun3ConvertersHLT.cxx} (60%)
 create mode 100644 Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.h
 create mode 100644 Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.cxx
 create mode 100644 Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.h

diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h
index 8e7f9217a131..05dccca5f200 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h
@@ -132,7 +132,6 @@ namespace TrigConf {
       /** Board names */
       std::vector<std::string> boardNames() const;
 
-
       /** Access to connector by name */
       const TrigConf::L1Connector & connector(const std::string & connectorName) const;
 
@@ -148,6 +147,8 @@ namespace TrigConf {
       /** print overview of L1 Menu */
       void printMenu(bool full = false) const;
 
+      bool isRun2() const { return m_run<3; }
+
    private:
 
       /** Update the internal data after modification of the data object */
@@ -156,6 +157,8 @@ namespace TrigConf {
       /** the supermasterkey */
       unsigned int m_smk {0};
 
+      unsigned int m_run{3}; // this variable is set to 2 for L1 menus from Run 2 which have a much reduced content 
+
       /** connector by name */ 
       std::map<std::string, TrigConf::L1Connector> m_connectors{};
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx
index 4a6b69032195..095d448ccb0d 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx
@@ -26,9 +26,10 @@ TrigConf::L1CTP::load()
    for(size_t i=0; i<3; ++i) {
       m_electrical[i] = electrical.get_optional<std::string>("connector" + std::to_string(i)).get_value_or("");
    }
-   auto optical = inputs.get_child("optical");
-   for(size_t i=0; i<12; ++i) {
-      m_optical[i] = optical.get_optional<std::string>("connector" + std::to_string(i)).get_value_or("");
+   if(auto optical = inputs.get_child_optional("optical")) {
+      for(size_t i=0; i<12; ++i) {
+         m_optical[i] = optical->get_optional<std::string>("connector" + std::to_string(i)).get_value_or("");
+      }
    }
    for( auto & mon : data().get_child("monitoring.ctpmon") ) {
       std::string monName = mon.first;
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
index 47786a9c5100..f8bd3fa463e3 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 #include "TrigConfData/L1Menu.h"
@@ -25,9 +25,12 @@ TrigConf::L1Menu::update()
    if(! isInitialized() || empty() ) {
       return;
    }
+
+   m_run = getAttribute_optional<int>("run").value_or(3);
+   
+   // thresholds
    try {
       m_name = getAttribute("name");
-      // thresholds
       for( const std::string path : {"thresholds", "thresholds.legacyCalo" } ) {
          for( auto & thrByType : data().get_child( path ) ) {
             const std::string & thrType = thrByType.first;
@@ -63,8 +66,8 @@ TrigConf::L1Menu::update()
       throw;
    }
 
+   // boards
    try {
-      // boards
       for( auto & board : data().get_child( "boards" ) ) {
          m_boards.emplace( std::piecewise_construct,
                            std::forward_as_tuple(board.first),
@@ -76,8 +79,8 @@ TrigConf::L1Menu::update()
       throw;
    }
 
+   // connectors
    try {
-      // connectors
       for( auto & conn : data().get_child( "connectors" ) ) {
          auto res = m_connectors.emplace( std::piecewise_construct,
                                           std::forward_as_tuple(conn.first),
@@ -94,9 +97,10 @@ TrigConf::L1Menu::update()
       throw;
    }
 
+   // algorithms
    try {
-      // algorithms
-      for( const std::string algoCategory : { "TOPO", "MULTTOPO", "MUTOPO", "R2TOPO" } ) {
+      auto topoCategories = isRun2() ? std::vector<std::string> {"R2TOPO"} : std::vector<std::string> {"TOPO", "MUTOPO", "MULTTOPO", "R2TOPO"};
+      for( const std::string algoCategory : topoCategories ) {
          auto & v = m_algorithmsByCategory[algoCategory] = std::vector<TrigConf::L1TopoAlgorithm>();
          if(algoCategory == "MULTTOPO") {
             for( auto & alg : data().get_child( "topoAlgorithms." + algoCategory + ".multiplicityAlgorithms" ) ) {
@@ -131,8 +135,8 @@ TrigConf::L1Menu::update()
       throw;
    }
 
+   // CTP
    try {
-      // CTP
       m_ctp.setData(data().get_child("ctp"));
    }
    catch(std::exception & ex) {
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1PrescalesSet.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1PrescalesSet.cxx
index c2d6e75ecdb8..61152f233e14 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1PrescalesSet.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1PrescalesSet.cxx
@@ -68,6 +68,6 @@ TrigConf::L1PrescalesSet::prescales() const {
 */
 double
 TrigConf::L1PrescalesSet::getPrescaleFromCut(uint32_t cut) const {
-   return 0xFFFFFF / ( 0x1000000 - cut );
+   return static_cast<double>(0xFFFFFF) / ( 0x1000000 - cut );
 }
 
diff --git a/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriter.h b/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriterL1.h
similarity index 82%
rename from Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriter.h
rename to Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriterL1.h
index dc654f4b47dc..d3a6a5accc68 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriter.h
+++ b/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriterL1.h
@@ -7,8 +7,8 @@
  * To validate correct loading of the L1 menu
  */
 
-#ifndef TRIGCONFSTORAGE_JSONFILEWRITER_H
-#define TRIGCONFSTORAGE_JSONFILEWRITER_H
+#ifndef TRIGCONFSTORAGE_JSONFILEWRITERL1_H
+#define TRIGCONFSTORAGE_JSONFILEWRITERL1_H
 
 #include "TrigConfBase/TrigConfMessaging.h"
 #include "TrigConfData/L1Menu.h"
@@ -20,11 +20,11 @@ namespace TrigConf {
    /**
     * @brief Loader of trigger configurations from Json files
     */
-   class JsonFileWriter : public TrigConfMessaging {
+   class JsonFileWriterL1 : public TrigConfMessaging {
    public:
 
       /** Constructor */
-      JsonFileWriter();
+      JsonFileWriterL1();
 
       bool writeJsonFile(const std::string & filename, const L1Menu & l1menu) const;
       bool writeJsonFile(const std::string & filename, const L1BunchGroupSet & l1bgs) const;
diff --git a/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterHLT.cxx b/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterHLT.cxx
index cebb43cc8149..eb1a38ac4df2 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterHLT.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterHLT.cxx
@@ -14,7 +14,7 @@ using json = nlohmann::json;
 using namespace std;
 
 TrigConf::JsonFileWriterHLT::JsonFileWriterHLT() :
-   TrigConfMessaging( "JsonFileWriter")
+   TrigConfMessaging( "JsonFileWriterHLT")
 {}
 
 
@@ -87,4 +87,4 @@ TrigConf::JsonFileWriterHLT::writeJsonFile(const std::string & filename, const H
 
    TRG_MSG_INFO("Saved file " << filename);
    return true;
-}
\ No newline at end of file
+}
diff --git a/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriter.cxx b/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterL1.cxx
similarity index 93%
rename from Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriter.cxx
rename to Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterL1.cxx
index 2d397e3744ac..ddbeb68508d5 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriter.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriterL1.cxx
@@ -1,8 +1,6 @@
-/*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
-*/
+// Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 
-#include "TrigConfIO/JsonFileWriter.h"
+#include "TrigConfIO/JsonFileWriterL1.h"
 
 #include <iomanip>
 #include <fstream>
@@ -13,13 +11,13 @@ using json = nlohmann::json;
 
 using namespace std;
 
-TrigConf::JsonFileWriter::JsonFileWriter() : 
-   TrigConfMessaging( "JsonFileWriter")
+TrigConf::JsonFileWriterL1::JsonFileWriterL1() : 
+   TrigConfMessaging( "JsonFileWriterL1")
 {}
 
 
 bool
-TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Menu & l1menu) const
+TrigConf::JsonFileWriterL1::writeJsonFile(const std::string & filename, const L1Menu & l1menu) const
 {
 
    json items({});
@@ -36,7 +34,7 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
          jItem["legacy"] = *legacy;
       items[item.name()] = jItem;
    };
- 
+
    json thresholds({});
    for(const std::string & thrType : l1menu.thresholdTypes()) {
       json jThresholsByType({});
@@ -93,6 +91,7 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
             jThr["thrValues"] = json::array_t({});
             for(auto & rv : EMThr.thrValues()) {
                json jRV({});
+               jRV["value"] = static_cast<unsigned int>(rv.value());
                jRV["etamin"] = rv.etaMin();
                jRV["etamax"] = rv.etaMax();
                jRV["phimin"] = 0; // never used, so not read 
@@ -104,7 +103,6 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
                }
                jRV["isobits"] = isobits;
                jRV["priority"] = rv.priority();
-               jRV["value"] = (unsigned int)rv.value();
                jThr["thrValues"] += jRV;
             }
          } catch(std::bad_cast&) {};
@@ -114,13 +112,13 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
             jThr["thrValues"] = json::array_t({});
             for(auto & rv : JThr.thrValues()) {
                json jRV({});
+               jRV["value"] = static_cast<unsigned int>(rv.value());
                jRV["etamin"] = rv.etaMin();
                jRV["etamax"] = rv.etaMax();
                jRV["phimin"] = 0; // never used, so not read 
                jRV["phimax"] = 64; // never used, so not read
-               jRV["priority"] = rv.priority();
                jRV["window"] = JThr.window(0);
-               jRV["value"] = (unsigned int)rv.value();
+               jRV["priority"] = rv.priority();
                jThr["thrValues"] += jRV;
             }
          } catch(std::bad_cast&) {};
@@ -130,10 +128,10 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
             //jThr["thrValues"] = json::array_t({});
             for(auto & rv : teThr.thrValues()) {
                json jRV({});
+               jRV["value"] = static_cast<unsigned int>(rv.value());
                jRV["etamin"] = rv.etaMin();
                jRV["etamax"] = rv.etaMax();
                jRV["priority"] = rv.priority();
-               jRV["value"] = (unsigned int)rv.value();
                jThr["thrValues"] += jRV;
             }
          } catch(std::bad_cast&) {};
@@ -151,13 +149,13 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
             jThr["reta"] = TrigConf::Selection::wpToString(eEMThr.reta());
             jThr["rhad"] = TrigConf::Selection::wpToString(eEMThr.rhad());
             jThr["wstot"] = TrigConf::Selection::wpToString(eEMThr.wstot());
-            jThr["thrValues"] = json::array_t({});
+            jThr["thrValues"] = json::value_t::array;
             for(auto & rv : eEMThr.thrValues()) {
                json jRV({});
+               jRV["value"] = static_cast<unsigned int>(rv.value());
                jRV["etamin"] = rv.etaMin();
                jRV["etamax"] = rv.etaMax();
                jRV["priority"] = rv.priority();
-               jRV["value"] = (unsigned int)rv.value();
                jThr["thrValues"] += jRV;
             }
          } catch(std::bad_cast&) {};
@@ -167,10 +165,10 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
             jThr["ranges"] = json::array_t({});
             for(auto & rv : jJThr.thrValues()) {
                json jRV({});
+               jThr["value"] = int(jJThr.thrValue(rv.etaMin()));
                jRV["etamin"] = rv.etaMin();
                jRV["etamax"] = rv.etaMax();
                jThr["ranges"] += jRV;
-               jThr["value"] = int(jJThr.thrValue(rv.etaMin()));
             }
          } catch(std::bad_cast&) {};
 
@@ -208,6 +206,7 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
 
       if(thrType == "MU") {
          auto & muinfo = l1menu.thrExtraInfo().MU();
+         jThrType["exclusionLists"] = json::value_t::object;
          for(auto & listName : muinfo.exclusionListNames()) {
             jThrType["exclusionLists"][listName] = json::array_t({});
             for(auto & x : muinfo.exclusionList(listName)) {
@@ -331,7 +330,7 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
    };
 
 
-   json boards{};
+   json boards;
    for( auto & bname : l1menu.boardNames() ) {
       auto & bdef = l1menu.board(bname);
       boards[bname] = json{ {"connectors", bdef.connectorNames()}, {"type", bdef.type()} };
@@ -340,7 +339,7 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
    };
 
 
-   json connectors{};
+   json connectors;
    for( auto & cname : l1menu.connectorNames() ) {
       auto jConn = json{};
       auto & cdef = l1menu.connector(cname);
@@ -378,7 +377,7 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
       connectors[cname] = jConn;
    }
 
-   json ctp({});
+   json ctp;
    {
       for(size_t slot=7; slot<=9; ++slot) {
          std::string sName = "slot" + std::to_string(slot);
@@ -404,15 +403,15 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
       ctp["monitoring"] = json({{"ctpmon",ctpmon}});
    }
 
-   json jtopo({});
+   json jtopo;
    {
       std::map<L1TopoAlgorithm::AlgorithmType,std::string> algTypeNames = {
          {L1TopoAlgorithm::AlgorithmType::SORTING, "sortingAlgorithms"},
          {L1TopoAlgorithm::AlgorithmType::DECISION, "decisionAlgorithms"},
          {L1TopoAlgorithm::AlgorithmType::MULTIPLICITY, "multiplicityAlgorithms"}
       };
-
-      for( const std::string topoCat : {"TOPO", "MUTOPO", "MULTTOPO", "R2TOPO"} ) {
+      auto topoCategories = l1menu.isRun2() ? std::vector<std::string> {"R2TOPO"} : std::vector<std::string> {"TOPO", "MUTOPO", "MULTTOPO", "R2TOPO"};
+      for( const std::string topoCat : topoCategories ) {
          for(auto & algName : l1menu.topoAlgorithmNames(topoCat)) {
             json jalg = json::object_t({});
             auto & alg = l1menu.algorithm(algName,topoCat);
@@ -474,8 +473,11 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
       }
    }
 
-   json j({});
+   json j;
    j["filetype"] = "l1menu";
+   if(l1menu.isRun2()) {
+      j["run"] = 2;
+   }
    j["name"] = l1menu.name();
    j["items"] = items;
    j["thresholds"] = thresholds;
@@ -494,19 +496,19 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Me
 
 
 bool 
-TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const TrigConf::L1BunchGroupSet & l1bgs) const {
+TrigConf::JsonFileWriterL1::writeJsonFile(const std::string & filename, const TrigConf::L1BunchGroupSet & l1bgs) const {
 
-   json j({});
+   json j;
    j["filetype"] = "bunchgroupset";
    j["name"]  = l1bgs.name();
 
-   json groups({});
+   json groups;
    for (size_t i = 0 ; i< l1bgs.size(); ++i) {
       auto group = l1bgs.getBunchGroup(i);
       json jgroup({});
       jgroup["name"] = group->name();
       jgroup["id"] = group->id();
-      jgroup["info"] = std::to_string(group->size()) + "bunchs, " + std::to_string(group->nGroups()) + " groups";
+      jgroup["info"] = std::to_string(group->size()) + " bunches, " + std::to_string(group->nGroups()) + " groups";
       json trains = json::array();
       for (auto [first, len]: group->trains()) {
          json train({});
@@ -524,13 +526,13 @@ TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const Trig
    return true;
 }
 
-bool TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const TrigConf::L1PrescalesSet & l1ps) const {
-   json j({});
+bool TrigConf::JsonFileWriterL1::writeJsonFile(const std::string & filename, const TrigConf::L1PrescalesSet & l1ps) const {
+   json j;
    j["filetype"] = "l1prescale";
    j["name"]  = l1ps.name();
-   json cuts({});
+   json cuts;
    for ( auto [itemName, ps]: l1ps.prescales()){
-      json cut({});
+      json cut;
       cut["cut"] = ps.cut;
       cut["enabled"] = ps.enabled;
       cut["info"] = "prescale: " + std::to_string(ps.prescale);
diff --git a/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx b/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
index 02843ecb1412..2189df1e4345 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
@@ -6,7 +6,7 @@
 #include <vector>
 
 #include "TrigConfIO/JsonFileLoader.h"
-#include "TrigConfIO/JsonFileWriter.h"
+#include "TrigConfIO/JsonFileWriterL1.h"
 #include "TrigConfIO/JsonFileWriterHLT.h"
 #include "TrigConfIO/TrigDBMenuLoader.h"
 #include "TrigConfIO/TrigDBJobOptionsLoader.h"
@@ -178,7 +178,7 @@ namespace {
          }
          filename += ".fromDS.json";
          if ( kind=="L1Menu" ) {
-            TrigConf::JsonFileWriter fileWriter;
+            TrigConf::JsonFileWriterL1 fileWriter;
             const auto & l1menu = dynamic_cast<const TrigConf::L1Menu &>(ds);
             return fileWriter.writeJsonFile(filename, l1menu); 
          } else if ( kind == "HLTMenu") {
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/CMakeLists.txt b/Trigger/TrigConfiguration/TrigConfStorage/CMakeLists.txt
index ce1638de850d..35b801d9ccbe 100644
--- a/Trigger/TrigConfiguration/TrigConfStorage/CMakeLists.txt
+++ b/Trigger/TrigConfiguration/TrigConfStorage/CMakeLists.txt
@@ -27,7 +27,7 @@ atlas_add_executable( TrigConfConsistencyChecker
                       LINK_LIBRARIES TrigConfStorage )
 
 atlas_add_executable( TrigConfReadWrite
-                      src/test/ReadWrite.cxx src/test/Run2toRun3Converters.cxx
+                      src/test/ReadWrite.cxx src/test/Run2toRun3ConvertersL1.cxx src/test/Run2toRun3ConvertersHLT.cxx
                       LINK_LIBRARIES L1TopoConfig TrigConfJobOptData TrigConfStorage TrigConfData TrigConfIO TrigCompositeUtilsLib )
 
 atlas_add_executable( TrigConfCoolFix
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.cxx b/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.cxx
index 53f81e0f7e95..0182282f3055 100755
--- a/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.cxx
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.cxx
@@ -18,7 +18,7 @@
 using namespace std;
 
 bool
-TrigConf::MasterTableLoader::loadMasterKeys(int SuperMasterKey, int& Lvl1MasterKey) {
+TrigConf::MasterTableLoader::loadMasterKeys(int SuperMasterKey, int& Lvl1MasterKey, std::string & menuName) {
    try {
 
       startSession();
@@ -36,8 +36,10 @@ TrigConf::MasterTableLoader::loadMasterKeys(int SuperMasterKey, int& Lvl1MasterK
       //Output data and types
       coral::AttributeList attList;
       attList.extend<int>( "SMT_L1_MASTER_TABLE_ID" );
+      attList.extend<std::string>( "SMT_NAME" );
       query->defineOutput(attList);
       query->addToOutputList( "SMT_L1_MASTER_TABLE_ID" );
+      query->addToOutputList( "SMT_NAME" );
 
       coral::ICursor& cursor = query->execute();
 
@@ -50,6 +52,7 @@ TrigConf::MasterTableLoader::loadMasterKeys(int SuperMasterKey, int& Lvl1MasterK
 	
       const coral::AttributeList& row = cursor.currentRow();
       Lvl1MasterKey = row["SMT_L1_MASTER_TABLE_ID"].data<int>();
+      menuName = row["SMT_NAME"].data<std::string>();
 
       delete query;
       commitSession();
@@ -81,7 +84,8 @@ TrigConf::MasterTableLoader::load(ThresholdConfig& thrcfg) {
          return false;
       }
       int Lvl1MasterKey(0);
-      loadMasterKeys(SuperMasterKey, Lvl1MasterKey);
+      std::string menuName{""};
+      loadMasterKeys(SuperMasterKey, Lvl1MasterKey, menuName);
       thrcfg.setLvl1MasterTableId(Lvl1MasterKey);
    }
    try {
@@ -109,12 +113,13 @@ TrigConf::MasterTableLoader::load(CTPConfig& ctpc) {
       int Lvl1MasterKey(0);
 
       msg() << "Load L1 master key" << std::endl;
-
-      loadMasterKeys(SuperMasterKey, Lvl1MasterKey);
+      std::string menuName{""};
+      loadMasterKeys(SuperMasterKey, Lvl1MasterKey, menuName);
 
       msg() << "Loaded L1 master key" << Lvl1MasterKey << std::endl;
 
       ctpc.setLvl1MasterTableId(Lvl1MasterKey);
+      ctpc.setName(menuName);
    }
    try {
       CTPConfigLoader& ctpLoader = 
@@ -144,7 +149,8 @@ TrigConf::MasterTableLoader::load(Muctpi& m) {
          return false;
       }
       int Lvl1MasterKey(0);
-      loadMasterKeys(SuperMasterKey, Lvl1MasterKey);
+      std::string menuName{""};
+      loadMasterKeys(SuperMasterKey, Lvl1MasterKey, menuName);
       m.setLvl1MasterTableId(Lvl1MasterKey);
    }
    try {
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.h b/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.h
index ee4eb3da830d..5507a1ab0df8 100644
--- a/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.h
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/MasterTableLoader.h
@@ -1,15 +1,14 @@
 /*
-  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef TrigConf_MasterTableLoader
 #define TrigConf_MasterTableLoader
-/*
-  MasterTableLoader.h
-*/
+
 #include "TrigConfStorage/IMasterTableLoader.h"
 #include "TrigConfStorage/DBLoader.h"
 
+#include <string>
 
 namespace TrigConf {
 
@@ -34,7 +33,8 @@ namespace TrigConf {
       bool load(CTPConfig& ctpc);
       bool load(Muctpi& muctpi);
       bool load(TXC::L1TopoMenu& l1topo);
-      bool loadMasterKeys(int SuperMasterKey, int& Lvl1MasterKey); 
+   private:
+      bool loadMasterKeys(int SuperMasterKey, int& Lvl1MasterKey, std::string & menuName); 
 
       /**@brief next run configuration key (depreciated)*/
       int mt_id_for_next_run();
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/XMLMenuLoader.cxx b/Trigger/TrigConfiguration/TrigConfStorage/src/XMLMenuLoader.cxx
index 3fd6b362df4b..aed8b5903fb6 100755
--- a/Trigger/TrigConfiguration/TrigConfStorage/src/XMLMenuLoader.cxx
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/XMLMenuLoader.cxx
@@ -74,7 +74,6 @@ TrigConf::XMLMenuLoader::load(Menu& menu) {
             if(sval.find("TAP") != string::npos) monMask |= TAP;
             if(sval.find("TAV") != string::npos) monMask |= TAV;
          }
-         //cout << "JOERG " << sval << " ==> " << monMask << endl;
          item->setMonitor( monMask );
       }
 
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/test/ReadWrite.cxx b/Trigger/TrigConfiguration/TrigConfStorage/src/test/ReadWrite.cxx
index e30268258923..55ebc06ccf56 100644
--- a/Trigger/TrigConfiguration/TrigConfStorage/src/test/ReadWrite.cxx
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/test/ReadWrite.cxx
@@ -42,7 +42,8 @@
 #include "TrigConfHLTData/HLTPrescaleSet.h"
 #include "TrigConfJobOptData/JobOptionTable.h"
 
-#include "Run2toRun3Converters.h"
+#include "Run2toRun3ConvertersL1.h"
+#include "Run2toRun3ConvertersHLT.h"
 
 #include "CoolKernel/DatabaseId.h"
 #include "CoolKernel/Exception.h"
@@ -68,48 +69,48 @@ using namespace std;
 using namespace TrigConf;
 
 void printhelp(std::ostream & o, std::ostream& (*lineend) ( std::ostream& os )) {
-  o << "================================================================================\n";
-  o << "The program needs to be run with the following specifications:\n" << lineend;
-  o << "TrigConfReadWrite <options>\n";
-  o << "\n";
-  o << "[Global options]\n";
-  o << "  -i|--input        input [input [input]]             ... source of configuration, format see below (mandatory)\n";
-  o << "  -2|--comp         input [input [input]]             ... source of a second configuration for comparison\n";
-  o << "  -o|--output xml|r3json|cool [output[;cooldb]] [run] ... output format, name (for cool optional run number)\n";
-  o << "                                                      ... absolute output file name must contain '/', cooldb can be appended COMP200|OFLP200\n";
-  o << "  -v|--loglevel     <string>                          ... log level [NIL, VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL, ALWAYS]\n";
-  o << "  -l|--log          <string>                          ... name of a log file\n";
-  o << "  --jo                                                ... read and write job options where possible\n";
-  o << "  --fw                                                ... read ctp firmware\n";
-  o << "  -p|--print        <int>                             ... print configuration with detail 0...5 (default 1)\n";
-  o << "  -h|--help                                           ... this output\n";
-  o << "  --nomerge                                           ... internally don't merge L2 and EF (by default merge is enabled)\n";
-  o << "\n\n";
-  o << "Input can be specified the following\n";
-  o << "  -i [l1menu.xml] [hltmenu2.xml]                                    ... to read L1 and/or HLT menu from XML [file names must end with '.xml'\n";
-  o << "  -i <TRIGDB_ALIAS>|<TRIGDB_connection> smk[,l1psk,hltpsk,bgsk]     ... to read the menu from a trigger db via alias or explicit connection specification (ORACLE or SQlite)\n";
-  o << "  -i <COOLDB_ALIAS>|<COOLDB_connection>|cool.db run[,lb]            ... to read the menu from COOL for a certain run and possibly LB [file names must end with '.db']\n";
-  o << "\n";
-  o << "The cool dbconnection can be specified as one of the following\n";
-  o << "     - via alias   : COOLONL_TRIGGER    (use COOLONL_TRIGGER/COMP200 for Run 1 data)";
-  o << "     - from sqlite : cool.db            (use cool.db;COMP200 for Run 1 data)";
-  o << "\n";
-  o << "\n";
-  o << "Input for comparison can be specified the same way, using the '-2' or '--comp' option\n";
-  o << "\n";
-  o << "\n";
-  o << "Output formats can be xml or cool. In case a second input is specified for comparison, the output will be on screen or an xml file with the differences\n";
-  o << "  -o xml test                                     ... will produce LVL1config_test.xml and/or HLTconfig_test.xml. When\n";
-  o << "                                                      comparing two menus this will produce Diff_test.xml. In this case the\n";
-  o << "                                                      specification of '-o test' is sufficient\n";
-  o << " -o r3json test                                   ... will produce Run3 JSON LVL1Menu_test.json and HLTMenu_test.json\n";
-  o << "  -o cool                                         ... will produce trig_cool.db with cool db instance CONDBR2 and infinite IOV\n";
-  o << "  -o cool 200000                                  ... will produce trig_cool.db with cool db instance CONDBR2 and run number 200000\n";
-  o << "  -o cool test [200000]                           ... will produce trig_cool_test.db with cool db instance CONDBR2 [and run number 200000]\n";
-  o << "  -o cool ../test.db [200000]                     ... will produce ../test.db with cool db instance CONDBR2 [and run number 200000]\n";
-  o << "  -o cool 'test;COMP200' [200000]                 ... will produce Menu_test.db with cool db instance COMP200 [and run number 200000]\n";
-  o << "\n";
-  o << "================================================================================\n";
+   o << "================================================================================\n";
+   o << "The program needs to be run with the following specifications:\n" << lineend;
+   o << "TrigConfReadWrite <options>\n";
+   o << "\n";
+   o << "[Global options]\n";
+   o << "  -i|--input        input [input [input]]             ... source of configuration, format see below (mandatory)\n";
+   o << "  -2|--comp         input [input [input]]             ... source of a second configuration for comparison\n";
+   o << "  -o|--output xml|r3json|cool [output[;cooldb]] [run] ... output format, name (for cool optional run number)\n";
+   o << "                                                      ... absolute output file name must contain '/', cooldb can be appended COMP200|OFLP200\n";
+   o << "  -v|--loglevel     <string>                          ... log level [NIL, VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL, ALWAYS]\n";
+   o << "  -l|--log          <string>                          ... name of a log file\n";
+   o << "  --jo                                                ... read and write job options where possible\n";
+   o << "  --fw                                                ... read ctp firmware\n";
+   o << "  -p|--print        <int>                             ... print configuration with detail 0...5 (default 1)\n";
+   o << "  -h|--help                                           ... this output\n";
+   o << "  --nomerge                                           ... internally don't merge L2 and EF (by default merge is enabled)\n";
+   o << "\n\n";
+   o << "Input can be specified the following\n";
+   o << "  -i [l1menu.xml] [hltmenu2.xml]                                    ... to read L1 and/or HLT menu from XML [file names must end with '.xml'\n";
+   o << "  -i <TRIGDB_ALIAS>|<TRIGDB_connection> smk[,l1psk,hltpsk,bgsk]     ... to read the menu from a trigger db via alias or explicit connection specification (ORACLE or SQlite)\n";
+   o << "  -i <COOLDB_ALIAS>|<COOLDB_connection>|cool.db run[,lb]            ... to read the menu from COOL for a certain run and possibly LB [file names must end with '.db']\n";
+   o << "\n";
+   o << "The cool dbconnection can be specified as one of the following\n";
+   o << "     - via alias   : COOLONL_TRIGGER    (use COOLONL_TRIGGER/COMP200 for Run 1 data)";
+   o << "     - from sqlite : cool.db            (use cool.db;COMP200 for Run 1 data)";
+   o << "\n";
+   o << "\n";
+   o << "Input for comparison can be specified the same way, using the '-2' or '--comp' option\n";
+   o << "\n";
+   o << "\n";
+   o << "Output formats can be xml or cool. In case a second input is specified for comparison, the output will be on screen or an xml file with the differences\n";
+   o << "  -o xml [<test>]                                 ... will produce LVL1config_test.xml and/or HLTconfig_test.xml. When\n";
+   o << "                                                      comparing two menus this will produce Diff_test.xml. In this case the\n";
+   o << "                                                      specification of '-o test' is sufficient\n";
+   o << "  -o r3json [<test>]                              ... will produce Run 3 config files L1PrescalesSet[_<test>].json, BunchGroups[_<test>].json, L1Menu[_<test>].json, HLTPrescalesSet[_<test>].json, and HLTMenu[_<test>].json\n";
+   o << "  -o cool                                         ... will produce trig_cool.db with cool db instance CONDBR2 and infinite IOV\n";
+   o << "  -o cool 200000                                  ... will produce trig_cool.db with cool db instance CONDBR2 and run number 200000\n";
+   o << "  -o cool test [200000]                           ... will produce trig_cool_test.db with cool db instance CONDBR2 [and run number 200000]\n";
+   o << "  -o cool ../test.db [200000]                     ... will produce ../test.db with cool db instance CONDBR2 [and run number 200000]\n";
+   o << "  -o cool 'test;COMP200' [200000]                 ... will produce Menu_test.db with cool db instance COMP200 [and run number 200000]\n";
+   o << "\n";
+   o << "================================================================================\n";
 }
 
 class JobConfig {
@@ -130,10 +131,12 @@ public:
    string       l1xmlOutFile { "LVL1Config.xml" };
    string       l1topoOutFile { "L1TopoConfig.xml" };
    string       hltxmlOutFile { "HLTConfig.xml" };
+
+   string       l1JsonOutFile {"L1Menu.json"};
+   string       bgkJsonOutFile {"BunchGroups.json"};
+   string       l1PSJsonOutFile { "L1PrescalesSet.json" };
    string       hltJsonOutFile { "HLTMenu.json" };
    string       hltPSJsonOutFile { "HLTPrescalesSet.json" };
-   string       bgkJsonOutFile {"BunchGroups.json"};
-   string       l1PSJsonOutFile {"L1PrescalesSet.json"};
 
    string       coolInputConnection { "" };
    string       coolOutputConnection { "" };
@@ -322,10 +325,12 @@ JobConfig::parseProgramOptions(int argc, char* argv[]) {
          l1xmlOutFile  = "LVL1config_" + outBase + ".xml";
          l1topoOutFile = "L1TopoConfig_" + outBase + ".xml";
          hltxmlOutFile = "HLTconfig_" + outBase + ".xml";
+
+         l1JsonOutFile = "L1Menu_" + outBase + ".json";
+         bgkJsonOutFile = "BunchGroups_" + outBase + ".json";
+         l1PSJsonOutFile = "L1PrescaleSet_" + outBase + ".json";
          hltJsonOutFile = "HLTMenu_" + outBase + ".json";
          hltPSJsonOutFile = "HLTPrescalesSet_" + outBase + ".json";
-         bgkJsonOutFile   = "BunchGroups_" + outBase + ".json";
-         l1PSJsonOutFile   = "L1PrescaleSet_" + outBase + ".json";
       }
    }
 
@@ -683,14 +688,15 @@ int main( int argc, char* argv[] ) {
       /*------------------
        * to JSON
        *-----------------*/
-      // TODO add L1 menu
+      if(ctpc && l1tm) {
+         convertRun2L1MenuToRun3(ctpc, l1tm, gConfig.l1JsonOutFile);
+         convertRun2L1PrescalesToRun3(ctpc, gConfig.l1PSJsonOutFile);
+         convertRun2BunchGroupsToRun3(ctpc, gConfig.bgkJsonOutFile);
+      }
       if(hltFrame) {
          convertRun2HLTMenuToRun3(hltFrame, gConfig.hltJsonOutFile);
          convertRun2HLTPrescalesToRun3(hltFrame, gConfig.hltPSJsonOutFile);
-         convertRun2BunchGroupsToRun3(ctpc, gConfig.bgkJsonOutFile);
-         convertRun2L1PrescalesToRun3(ctpc, gConfig.l1PSJsonOutFile);
       }
-
    }
 
    if ( (gConfig.output & JobConfig::COOL) != 0 ) {
@@ -706,18 +712,21 @@ int main( int argc, char* argv[] ) {
       unsigned int runNr = gConfig.coolOutputRunNr;
       if(runNr == 0) { runNr = 0x80000000; } // infinite range
 
-      if(ctpc)
+      if(ctpc) {
          coolWriter->writeL1Payload(runNr, *ctpc);
+      }
       try{
-	if(hltFrame)
-	  coolWriter->writeHLTPayload(runNr, *hltFrame, configSource);
+         if(hltFrame) {
+   	      coolWriter->writeHLTPayload(runNr, *hltFrame, configSource);
+         }
       }
       catch(const cool::StorageTypeStringTooLong& e){
-	      log << "FATAL: Unable to write data to COOL";
-      	exit(1);
+         log << "FATAL: Unable to write data to COOL";
+         exit(1);
       }
-       if(mck)
+      if(mck) {
          coolWriter->writeMCKPayload(runNr, mck, release, info);
+      }
    }
 
    delete ctpc;
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.h b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.h
deleted file mode 100644
index a925168509a4..000000000000
--- a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
-*/
-
-#include "TrigConfData/HLTMenu.h"
-#include "TrigConfHLTData/HLTFrame.h"
-#include "TrigConfL1Data/CTPConfig.h"
-void convertRun2HLTMenuToRun3(const TrigConf::HLTFrame* frame, const std::string& filename);
-void convertRun2HLTPrescalesToRun3(const TrigConf::HLTFrame* frame, const std::string& filename);
-void convertRun2BunchGroupsToRun3(const TrigConf::CTPConfig* frame, const std::string& filename);
-void convertRun2L1PrescalesToRun3(const TrigConf::CTPConfig* frame, const std::string& filename);
\ No newline at end of file
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.cxx b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.cxx
similarity index 60%
rename from Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.cxx
rename to Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.cxx
index d1c488d2821a..4cd45e01f635 100644
--- a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3Converters.cxx
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.cxx
@@ -4,9 +4,7 @@
 
 #include <numeric>
 #include "TrigConfData/HLTMenu.h"
-#include "TrigConfData/L1BunchGroupSet.h"
 #include "TrigConfData/DataStructure.h"
-#include "TrigConfData/L1PrescalesSet.h"
 #include "TrigCompositeUtils/HLTIdentifier.h"
 #define BOOST_BIND_GLOBAL_PLACEHOLDERS // Needed to silence Boost pragma message
 #include <boost/property_tree/json_parser.hpp>
@@ -15,15 +13,8 @@
 #include "TrigConfHLTData/HLTStreamTag.h"
 #include "TrigConfHLTData/HLTSignature.h"
 #include "TrigConfHLTData/HLTTriggerElement.h"
-#include "TrigConfL1Data/CTPConfig.h"
-#include "TrigConfL1Data/BunchGroupSet.h"
-#include "TrigConfL1Data/BunchGroup.h"
-
 
 #include "TrigConfIO/JsonFileWriterHLT.h"
-#include "TrigConfIO/JsonFileWriter.h"
-
-
 
 template<typename COLL>
 boost::property_tree::ptree asArray( const COLL& data) {
@@ -137,12 +128,10 @@ void convertRun2HLTMenuToRun3(const TrigConf::HLTFrame* frame, const std::string
   convertHLTMenu(frame, menu);
 
   TrigConf::JsonFileWriterHLT writer;
-  std::cout << "Saving file: " << filename << std::endl;
   writer.writeJsonFile(filename, menu);
 
 }
 
-
 void convertRun2HLTPrescalesToRun3(const TrigConf::HLTFrame* frame, const std::string& filename) {
    using ptree = boost::property_tree::ptree;
    ptree top;
@@ -166,94 +155,6 @@ void convertRun2HLTPrescalesToRun3(const TrigConf::HLTFrame* frame, const std::s
    convertHLTMenu(frame, menu);
 
    TrigConf::JsonFileWriterHLT writer;
-   std::cout << "Saving file: " << filename << std::endl;
    writer.writeJsonFile(filename, menu, psk);
 }
 
-std::vector<std::pair<int, int>> toRanges(const std::vector<int>& bunches) {
-   // converts sequence of bunches into the pairs of continous ranges
-   if ( bunches.empty() )
-      return {};
-   if (bunches.size() == 1) {
-       return { {bunches.front(), bunches.front()} };
-   }
-
-   // nontrival ranges
-   std::vector<std::pair<int, int>> ranges;
-   std::vector<int> sorted = bunches;
-   std::sort(sorted.begin(), sorted.end());
-
-   std::vector<int> differences;
-   std::adjacent_difference( sorted.begin(), sorted.end(), std::back_inserter(differences));
-
-   int start = sorted.front();
-
-   for (size_t i = 1; i < differences.size(); ++i) {
-      if (differences[i] == 1) continue;
-      ranges.emplace_back( std::pair(start, sorted[i-1]) );
-      start = sorted[i];
-   }
-   ranges.emplace_back( std::pair(start, sorted.back()) );
-   return ranges;
-}
-
-
-void convertRun2BunchGroupsToRun3(const TrigConf::CTPConfig* frame, const std::string& filename) {
-   using ptree = boost::property_tree::ptree;
-   ptree top;
-   top.put("filetype", "bunchgroupset");
-   top.put("name", frame->bunchGroupSet().name());
-
-   ptree pGroups;
-   const std::vector<TrigConf::BunchGroup>& bgVec = frame->bunchGroupSet().bunchGroups();
-   for (auto group : bgVec) {
-      auto ranges = toRanges(group.bunches());
-      ptree pGroup;
-      pGroup.put("name", group.name());
-      pGroup.put("id", group.internalNumber());
-      pGroup.put("info", std::to_string(group.bunches().size()) + " bunches, " + ranges.size() + " groups" );
-
-      ptree pBCIDS;
-
-      for ( auto [start, end] : ranges) {
-         ptree pTrain;
-         pTrain.put("first", start );
-         pTrain.put("length", 1+end-start );
-         pBCIDS.push_back(std::make_pair("", pTrain));
-      }
-      pGroup.push_back(std::make_pair("bcids", pBCIDS));
-
-      pGroups.push_back(std::make_pair(std::string("BGRP")+std::to_string(group.internalNumber()), pGroup));
-   }
-   top.push_back(std::make_pair("bunchGroups", pGroups));
-   TrigConf::L1BunchGroupSet bgs(std::move(top));
-   TrigConf::JsonFileWriter writer;
-   std::cout << "Saving file: " << filename << std::endl;
-   writer.writeJsonFile(filename, bgs);
-
-
-}
-
-void convertRun2L1PrescalesToRun3(const TrigConf::CTPConfig* frame, const std::string& filename) {
-   using ptree = boost::property_tree::ptree;
-   ptree top;
-   top.put("filetype", "l1prescale");
-   top.put("name", frame->prescaleSet().name());
-   ptree pCuts;
-   for (size_t id = 0; id < frame->prescaleSet().prescales_float().size(); ++id) {
-      ptree pCut;
-      auto itemPtr = frame->menu().item(id);
-      if ( itemPtr != nullptr ) {
-         pCut.put("cut", frame->prescaleSet().cuts().at(id));
-         pCut.put("enabled", frame->prescaleSet().prescales_float().at(id) > 0.0 );
-         pCut.put("info", "prescale: "+std::to_string(frame->prescaleSet().prescales_float().at(id)));
-         pCuts.push_back(std::make_pair(itemPtr->name(), pCut));
-      }
-   }
-   top.push_back(std::make_pair("cutValues", pCuts));
-
-   TrigConf::L1PrescalesSet ps(std::move(top));
-   TrigConf::JsonFileWriter writer;
-   std::cout << "Saving file: " << filename << std::endl;
-   writer.writeJsonFile(filename, ps);
-}
\ No newline at end of file
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.h b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.h
new file mode 100644
index 000000000000..09a737af9704
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersHLT.h
@@ -0,0 +1,9 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TrigConfData/HLTMenu.h"
+#include "TrigConfHLTData/HLTFrame.h"
+
+void convertRun2HLTMenuToRun3(const TrigConf::HLTFrame* frame, const std::string& filename);
+void convertRun2HLTPrescalesToRun3(const TrigConf::HLTFrame* frame, const std::string& filename);
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.cxx b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.cxx
new file mode 100644
index 000000000000..094ec340d9b7
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.cxx
@@ -0,0 +1,529 @@
+// Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+
+#include "Run2toRun3ConvertersL1.h"
+#include <numeric>
+#include <unordered_set>
+#include <boost/tokenizer.hpp>
+#include <tuple>
+#include <stdexcept>
+
+#include "TrigConfData/L1BunchGroupSet.h"
+#include "TrigConfData/DataStructure.h"
+#include "TrigConfData/L1PrescalesSet.h"
+#define BOOST_BIND_GLOBAL_PLACEHOLDERS // Needed to silence Boost pragma message
+#include <boost/property_tree/json_parser.hpp>
+#include "TrigConfL1Data/CTPConfig.h"
+#include "TrigConfL1Data/TriggerThreshold.h"
+#include "TrigConfL1Data/TriggerItem.h"
+#include "TrigConfL1Data/TriggerItemNode.h"
+#include "TrigConfL1Data/ClusterThresholdValue.h"
+#include "TrigConfL1Data/JetThresholdValue.h"
+#include "TrigConfL1Data/HelperFunctions.h"
+#include "TrigConfL1Data/CaloInfo.h"
+
+#include "TrigConfL1Data/BunchGroupSet.h"
+#include "TrigConfL1Data/BunchGroup.h"
+
+#include "TrigConfIO/JsonFileWriterL1.h"
+
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json; // to be changed to ordered_json
+using ptree = boost::property_tree::ptree;
+
+namespace {
+   std::tuple<std::string, std::vector<std::string>> 
+   decodeItemDefinition(const TrigConf::TriggerItem * item) {
+      std::string definition{""};
+      std::vector<std::string> bgps;
+      const TrigConf::TriggerItemNode * topNode = item->topNode();
+      std::vector<const TrigConf::TriggerItemNode*> nodes;
+      topNode->getAllFinalNodes(nodes);
+      std::map<unsigned int, const TrigConf::TriggerItemNode*> nodeMap;
+      for(auto * node : nodes) {
+         nodeMap[node->position()] = node;
+      }
+      // build tokens with separators ()&|! and <space>. Keeps all separators except <space> in the list of tokens 
+      for ( auto & tok : boost::tokenizer<boost::char_separator<char> > (item->definition(), boost::char_separator<char>(" ", "()&|!")) ) {
+         if(isdigit(tok[0])) {
+            // replace by threshold
+            int idx = std::stoi(tok);
+            const TrigConf::TriggerItemNode* node = nodeMap[idx];
+            if(node->triggerThreshold()->type()=="BGRP") {
+               bgps.push_back(node->thresholdName());
+               if(definition.back()=='&') {
+                  definition.pop_back();
+               }
+            } else {
+               definition += node->thresholdName() + "[x" + std::to_string(node->multiplicity()) + "]";
+            }
+         } else  {
+            // keep token ()&|!
+            definition += tok;
+         }
+      }
+      // remove outer parentheses
+      if(definition.front()=='(' and definition.back()==')') {
+         definition = definition.substr(1,definition.size()-2);
+      }
+      return make_tuple(definition, bgps);
+   }
+}
+
+
+/**
+ * Conversion of L1 menu
+ **/
+void
+convertRun2L1MenuToRun3(const TrigConf::CTPConfig* ctpConfig, const TXC::L1TopoMenu* topoMenu, 
+                        const std::string& filename, bool writeTmpFile)
+{
+
+   if( !ctpConfig ) {
+      std::cout << "No CTPConfig, no L1Menu file will be produced" << std::endl;
+      return;
+   }
+
+   // items
+   json items;
+   for( const TrigConf::TriggerItem * sourceItem: ctpConfig->menu().items() ) {
+      json item({});
+      item["name"] = sourceItem->name();
+      item["legacy"] = true;
+      item["ctpid"] = sourceItem->ctpId();
+      auto [definition, bunchgroups] = decodeItemDefinition(sourceItem);
+      item["definition"] = definition;
+      item["bunchgroups"] = bunchgroups;
+      item["triggerType"] = TrigConf::uint2bin(sourceItem->triggerType(),8);
+      item["partition"] = sourceItem->partition();
+      std::string smon("LF:");
+      smon += (sourceItem->monitor() & 0x04 ? '1' : '0');
+      smon += (sourceItem->monitor() & 0x02 ? '1' : '0');
+      smon += (sourceItem->monitor() & 0x01 ? '1' : '0');
+      smon += "|HF:";
+      smon += (sourceItem->monitor() & 0x20 ? '1' : '0');
+      smon += (sourceItem->monitor() & 0x10 ? '1' : '0');
+      smon += (sourceItem->monitor() & 0x08 ? '1' : '0');
+      item["monitor"] = smon;
+      items[sourceItem->name()] = item;
+   };
+
+   // thresholds
+   json thresholds;
+   std::unordered_set<std::string> legacyThrTypes{"EM", "TAU", "JET", "XE", "XS", "TE", "ZB", "R2TOPO"};
+   for( const TrigConf::TriggerThreshold * sourceThr : ctpConfig->menu().thresholdVector()) {
+      std::string thrType = sourceThr->type();
+      if(thrType=="BGRP" || thrType=="RNDM") {
+         continue;
+      }
+      if(thrType=="MUON") {
+         thrType = "MU";
+      } 
+      else if(thrType=="TOPO") {
+         thrType = "R2TOPO";
+      }
+
+      bool isLegacyCalo = legacyThrTypes.count(thrType) > 0;
+      json &jThisType = isLegacyCalo ? thresholds["legacyCalo"][thrType] : thresholds[thrType];
+      bool firstOfItsType = jThisType.empty();
+      if (firstOfItsType)
+      {
+         jThisType["type"] = thrType;
+      }
+      json thr;
+      size_t mapping = sourceThr->mapping();
+      thr["mapping"] = mapping;
+      if(thrType=="MU") {
+         if (firstOfItsType)
+         {
+            jThisType["type"] = "MU";
+            jThisType["exclusionLists"] = json::object_t{};
+            jThisType["roads"]["rpc"] = json::object_t{};
+            jThisType["roads"]["tgc"] = json::object_t{};
+         }
+         int ptCut = sourceThr->triggerThresholdValue(0,0)->ptCutCount();
+         thr["baThr"] = ptCut;
+         thr["ecThr"] = ptCut;
+         thr["fwThr"] = ptCut;
+         thr["baIdx"] = mapping;
+         thr["ecIdx"] = mapping;
+         thr["fwIdx"] = mapping;
+         thr["tgcFlags"] = "";
+         thr["region"] = "ALL";
+         jThisType["roads"]["rpc"][std::to_string(ptCut)] = mapping;
+         jThisType["roads"]["tgc"][std::to_string(ptCut)] = mapping;
+      }
+      else if (thrType == "EM")
+      {
+         thr["thrValues"] = json::array_t{};
+         for (const TrigConf::TriggerThresholdValue *tv : sourceThr->thresholdValueVector())
+         {
+            auto cl = dynamic_cast<const TrigConf::ClusterThresholdValue *>(tv);
+            json jtv;
+            jtv["value"] = static_cast<unsigned int>(tv->ptcut());
+            std::string isobits = "00000";
+            auto isomask = cl->isolationMask();
+            for (size_t b = 0; b < 5; ++b)
+            {
+               if (isomask & (1 << b))
+               {
+                  isobits[4 - b] = '1';
+               }
+            }
+            jtv["isobits"] = isobits;
+            jtv["etamin"] = tv->etamin();
+            jtv["etamax"] = tv->etamax();
+            jtv["phimin"] = tv->phimin();
+            jtv["phimax"] = tv->phimax();
+            jtv["priority"] = static_cast<unsigned int>(tv->priority());
+            thr["thrValues"] += jtv;
+         }
+      }
+      else if (thrType == "TAU")
+      {
+         auto cl = dynamic_cast<const TrigConf::ClusterThresholdValue*>(sourceThr->triggerThresholdValue(0,0));
+         int ptCut = (int)cl->ptcut();
+         thr["value"] = ptCut;
+         std::string isobits = "00000";
+         auto isomask = cl->isolationMask();
+         for (size_t b = 0; b < 5; ++b)
+         {
+            if (isomask & (1 << b))
+            {
+               isobits[4 - b] = '1';
+            }
+         }
+         thr["isobits"] = isobits;
+      }
+      else if (thrType == "JET")
+      {
+         thr["thrValues"] = json::array_t{};
+         for (const TrigConf::TriggerThresholdValue *tv : sourceThr->thresholdValueVector())
+         {
+            auto jetThrVal = dynamic_cast<const TrigConf::JetThresholdValue *>(sourceThr->triggerThresholdValue(0, 0));
+            json jtv;
+            jtv["value"] = static_cast<unsigned int>(tv->ptcut());
+            jtv["etamin"] = tv->etamin();
+            jtv["etamax"] = tv->etamax();
+            jtv["phimin"] = tv->phimin();
+            jtv["phimax"] = tv->phimax();
+            jtv["window"] = jetThrVal->windowSize() == TrigConf::JetWindowSize::SMALL ? 4 : 8; // 4 and 8 are the two possible jet window sizes
+            jtv["priority"] = static_cast<unsigned int>(tv->priority());
+            thr["thrValues"] += jtv;
+         }
+      }
+      else if (thrType == "ZB")
+      {
+         thr["seed"] = sourceThr->zbSeedingThresholdName();
+         thr["seedMultiplicity"] = sourceThr->zbSeedingThresholdMulti();
+         thr["seedBcdelay"] = sourceThr->bcDelay();
+      }
+      jThisType["thresholds"][sourceThr->name()] = thr;
+   }
+
+   // exra info for thresholds
+   const TrigConf::CaloInfo& ci = ctpConfig->menu().caloInfo();
+   thresholds["legacyCalo"]["EM"]["ptMinToTopo"] = ci.minTobEM().ptmin;
+   thresholds["legacyCalo"]["EM"]["resolutionMeV"] = (int)(1000/ci.globalEmScale());
+   thresholds["legacyCalo"]["TAU"]["ptMinToTopo"] = ci.minTobTau().ptmin;
+   thresholds["legacyCalo"]["JET"]["ptMinToTopoSmallWindow"] = ci.minTobJetSmall().ptmin;
+   thresholds["legacyCalo"]["JET"]["ptMinToTopoLargeWindow"] = ci.minTobJetLarge().ptmin;
+   json isoHAforEM{ {"thrtype", "HAIsoForEMthr"}, {"Parametrization", json::array_t{}} };
+   json isoEMforEM{ {"thrtype", "EMIsoForEMthr"}, {"Parametrization", json::array_t{}} };
+   json isoEMforTAU{ {"thrtype", "EMIsoForTAUthr"}, {"Parametrization", json::array_t{}} };
+   for(const TrigConf::IsolationParam & iso : ci.isolationHAIsoForEMthr()) {
+      json p{ {"etamax", iso.etamax()}, {"etamin", iso.etamin()}, {"isobit", iso.isobit()}, {"mincut", iso.mincut()}, 
+              {"offset", iso.offset()}, {"priority", iso.priority()}, {"slope", iso.slope()}, {"upperlimit", iso.upperlimit()} };
+      isoHAforEM["Parametrization"] += p;
+   }
+   for(const TrigConf::IsolationParam & iso : ci.isolationEMIsoForEMthr()) {
+      json p{ {"etamax", iso.etamax()}, {"etamin", iso.etamin()}, {"isobit", iso.isobit()}, {"mincut", iso.mincut()}, 
+              {"offset", iso.offset()}, {"priority", iso.priority()}, {"slope", iso.slope()}, {"upperlimit", iso.upperlimit()} };
+      isoEMforEM["Parametrization"] += p;
+   }
+   for(const TrigConf::IsolationParam & iso : ci.isolationEMIsoForTAUthr()) {
+      json p{ {"etamax", iso.etamax()}, {"etamin", iso.etamin()}, {"isobit", iso.isobit()}, {"mincut", iso.mincut()}, 
+              {"offset", iso.offset()}, {"priority", iso.priority()}, {"slope", iso.slope()}, {"upperlimit", iso.upperlimit()} };
+      isoEMforTAU["Parametrization"] += p;
+   }
+   thresholds["legacyCalo"]["EM"]["isolation"]["HAIsoForEMthr"] = isoHAforEM;
+   thresholds["legacyCalo"]["EM"]["isolation"]["EMIsoForEMthr"] = isoEMforEM;
+   thresholds["legacyCalo"]["TAU"]["isolation"]["EMIsoForTAUthr"] = isoEMforTAU;
+   const TrigConf::METSigParam &xs = ci.metSigParam();
+   thresholds["legacyCalo"]["XS"]["significance"] = json::object_t{
+       {"xsSigmaScale", xs.xsSigmaScale()}, {"xsSigmaOffset", xs.xsSigmaOffset()}, {"xeMin", xs.xeMin()}, {"xeMax", xs.xeMax()}, {"teSqrtMin", xs.teSqrtMin()}, {"teSqrtMax", xs.teSqrtMax()}};
+
+   // boards
+   json boards;
+   boards["Ctpin7"] = json::object_t{ {"type", "CTPIN"}, {"legacy", true},
+                                      {"connectors", std::vector<std::string>{"EM1", "EM2", "TAU1", "TAU2"}} };
+   boards["Ctpin8"] = json::object_t{ {"type", "CTPIN"}, {"legacy", true},
+                                      {"connectors", std::vector<std::string>{"JET1", "JET2", "EN1", "EN2"}} };
+   boards["Ctpin9"] = json::object_t{ {"type", "CTPIN"}, {"legacy", true},
+                                      {"connectors", std::vector<std::string>{"MUCTPI", "CTPCAL", "NIM1", "NIM2"}} };
+   boards["LegacyTopo0"] = json::object_t{ {"type", "TOPO"}, {"legacy", true},
+                                           {"connectors", std::vector<std::string>{"LegacyTopo0"}} };
+   boards["LegacyTopo1"] = json::object_t{ {"type", "TOPO"}, {"legacy", true},
+                                           {"connectors", std::vector<std::string>{"LegacyTopo1"}} };
+
+   // connectors
+   json connectors;
+   std::map<std::string,std::vector<const TrigConf::TriggerThreshold*>> triggerlinesMap;
+   for( const TrigConf::TriggerThreshold * sourceThr : ctpConfig->menu().thresholdVector()) {
+      if(sourceThr->isInternal()) {
+         continue;
+      }
+      std::string cableName = sourceThr->cableName();
+      if(cableName=="ALFA") {
+         cableName = "AlfaCtpin";
+      }
+      else if(cableName=="TOPO1") {
+         cableName = "LegacyTopo0";
+      }
+      else if(cableName=="TOPO2") {
+         cableName = "LegacyTopo1";
+      }
+      triggerlinesMap[cableName].push_back(sourceThr);
+   }
+   for( auto & [type, triggerlines] : triggerlinesMap) {
+      std::sort(std::begin(triggerlines), std::end(triggerlines),
+                [](const TrigConf::TriggerThreshold *a, const TrigConf::TriggerThreshold *b) { return a->cableStart() < b->cableStart(); });
+      for( const TrigConf::TriggerThreshold * thr : triggerlines) {
+         if(!connectors.contains(type)) {
+            if(thr->input()=="ctpcore") {
+               connectors[type]["type"] =  "electrical";
+               if(type=="AlfaCtpin") {
+                  connectors[type]["triggerlines"]["clock0"] = json::array_t{};
+                  connectors[type]["triggerlines"]["clock1"] = json::array_t{};
+               } else {
+                  connectors[type]["triggerlines"]["fpga0"]["clock0"] = json::array_t{};
+                  connectors[type]["triggerlines"]["fpga0"]["clock1"] = json::array_t{};
+                  connectors[type]["triggerlines"]["fpga1"]["clock0"] = json::array_t{};
+                  connectors[type]["triggerlines"]["fpga1"]["clock1"] = json::array_t{};
+               }
+            } else if(thr->input()=="ctpin") {
+               connectors[type]["type"] =  "ctpin";
+               connectors[type]["triggerlines"] = json::array_t{};
+            } else {
+               throw std::runtime_error("Unknown connector type" + thr->input());
+            }
+            connectors[type]["legacy"] = true;
+         }
+         size_t start = thr->cableStart();
+         size_t nbits = thr->cableEnd() - thr->cableStart() + 1;
+         if(thr->input()=="ctpcore") {
+            unsigned int clock = thr->clock();
+            if(type=="AlfaCtpin") {
+               json tl{{"name", thr->name()}, {"startbit", start}, {"nbits", nbits}};
+               connectors[type]["triggerlines"]["clock" + std::to_string(clock)] += tl;
+            } else {
+               size_t fpga = 0;
+               if(start>=16) {
+                  start -= 16;
+                  fpga++;
+               }
+               json tl{{"name", "R2TOPO_" + thr->name()}, {"startbit", start}, {"nbits", nbits}};
+               connectors[type]["triggerlines"]["fpga" + std::to_string(fpga)]["clock" + std::to_string(clock)] += tl;
+            }
+         } else {
+            json tl{{"name", thr->name()}, {"startbit", start}, {"nbits", nbits}};
+            connectors[type]["triggerlines"] += tl;
+         }
+      }
+   }
+
+   // algorithms
+   json decAlgos;
+   json sortAlgos;
+   for(const TXC::L1TopoConfigAlg & alg : topoMenu->getL1TopoConfigAlgs()) {
+      json jAlg;
+      jAlg["algId"] = alg.algoID();
+      jAlg["klass"] = alg.type();
+      jAlg["fixedParameters"]["generics"] = json::object_t{};
+      size_t pos{0};
+      for(const TXC::FixedParameter & fixP : alg.getFixedParameters()) {
+         try
+         {
+            auto vInt = std::stoi(fixP.value);
+            jAlg["fixedParameters"]["generics"][fixP.name] = json::object_t{{"value", vInt}, {"position", pos++}};
+         }
+         catch (std::invalid_argument &)
+         {
+            jAlg["fixedParameters"]["generics"][fixP.name] = json::object_t{{"value", fixP.value}, {"position", pos++}};
+         }
+      }
+      jAlg["variableParameters"] = json::array_t{};
+      for(const TXC::RegisterParameter & regP : alg.getParameters()) {
+         jAlg["variableParameters"] += json::object_t{{"name", regP.name}, {"selection", regP.selection}, {"value", std::stoi(regP.value)}};
+      }
+      if(alg.isSortAlg()) {
+         jAlg["input"] = alg.getInputNames()[0];
+         jAlg["output"] = alg.output();
+         sortAlgos[alg.name()] = jAlg;
+      } else if(alg.isDecAlg()) {
+         jAlg["input"] = alg.getInputNames();
+         jAlg["output"] = alg.getOutputNames();
+         decAlgos[alg.name()] = jAlg;
+      }
+   }
+   json topo;
+   topo["R2TOPO"]["decisionAlgorithms"] = decAlgos;
+   topo["R2TOPO"]["sortingAlgorithms"] = sortAlgos;
+
+   // ctp
+   json ctp;
+   ctp["inputs"]["ctpin"]["slot7"] = json::object_t{{"connector0", "EM1"}, {"connector1", "EM2"}, 
+                                                    {"connector2", "TAU1"}, {"connector3", "TAU2"}};
+   ctp["inputs"]["ctpin"]["slot8"] = json::object_t{{"connector0", "JET1"}, {"connector1", "JET2"}, 
+                                                    {"connector2", "EN1"}, {"connector3", "EN2"}};
+   ctp["inputs"]["ctpin"]["slot9"] = json::object_t{{"connector0", "MUCTPI"}, {"connector1", "CTPCAL"}, 
+                                                    {"connector2", "NIM1"}, {"connector3", "NIM2"}};
+   ctp["inputs"]["electrical"] = json::object_t{{"connector0", "AlfaCtpin"}, {"connector1", "LegacyTopo0"}, {"connector2", "LegacyTopo1"}};
+   ctp["monitoring"]["ctpmon"] = json::object_t{};
+
+   // putting the menu together
+   json menu({});
+   menu["filetype"] = "l1menu";
+   menu["run"] = 2;
+   menu["name"] = ctpConfig->name();
+   menu["items"] = items;
+   menu["thresholds"] = thresholds;
+   menu["topoAlgorithms"] = topo;
+   menu["boards"] = boards;
+   menu["connectors"] = connectors;
+   menu["ctp"] = ctp;
+
+   if(writeTmpFile) {
+      std::ofstream outfile("tmp" + filename);
+      outfile << std::setw(4) << menu << std::endl;
+      std::cout << "Wrote tmp" << filename << std::endl;
+   }
+   std::stringstream ss;
+   ss << menu;
+   ptree top;
+   boost::property_tree::read_json(ss, top);
+   TrigConf::L1Menu l1menu(std::move(top));
+   TrigConf::JsonFileWriterL1 writer;
+   writer.writeJsonFile(filename, l1menu);
+}
+
+/**
+ * Conversion of bunchgroup set
+ **/
+std::vector<std::pair<int, int>>
+toRanges(const std::vector<int>& bunches) {
+   // converts sequence of bunches into the pairs of continous ranges
+   if ( bunches.empty() ) {
+      return {};
+   }
+   if (bunches.size() == 1) {
+      return { {bunches.front(), bunches.front()} };
+   }
+
+   // nontrival ranges
+   std::vector<std::pair<int, int>> ranges;
+   std::vector<int> sorted = bunches;
+   std::sort(sorted.begin(), sorted.end());
+
+   std::vector<int> differences;
+   std::adjacent_difference( sorted.begin(), sorted.end(), std::back_inserter(differences));
+
+   int start = sorted.front();
+
+   for (size_t i = 1; i < differences.size(); ++i) {
+      if (differences[i] == 1) continue;
+      ranges.emplace_back( std::pair(start, sorted[i-1]) );
+      start = sorted[i];
+   }
+   ranges.emplace_back( std::pair(start, sorted.back()) );
+   return ranges;
+}
+
+void
+convertRun2BunchGroupsToRun3(const TrigConf::CTPConfig* ctpConfig, const std::string& filename, bool writeTmpFile)
+{
+   const TrigConf::BunchGroupSet & bgs = ctpConfig->bunchGroupSet();
+   if(bgs.bunchGroups().size()==0) {
+      std::cout << "BunchgroupSet is empty, no file will be produced" << std::endl;
+      return;
+   }
+
+   json bgset;
+   bgset["filetype"] ="bunchgroupset";
+   bgset["name"] = bgs.name();
+
+   json jGroups;
+   const std::vector<TrigConf::BunchGroup>& bgVec = bgs.bunchGroups();
+   for (auto & group : bgVec) {
+      auto ranges = toRanges(group.bunches());
+      json jGroup;
+      jGroup["name"] =group.name();
+      jGroup["id"] =group.internalNumber();
+      jGroup["info"] = std::to_string(group.bunches().size()) + " bunches, " + std::to_string(ranges.size()) + " groups";
+
+      json jBCIDS = json::array_t{};
+      for ( auto [start, end] : ranges) {
+         jBCIDS += json{{"first", start}, {"length", 1+end-start}};
+      }
+      jGroup["bcids"] = jBCIDS;
+
+      jGroups[std::string("BGRP")+std::to_string(group.internalNumber())] = jGroup;
+   }
+   bgset["bunchGroups"] = jGroups;
+
+   if(writeTmpFile) {
+      std::ofstream outfile("tmp" + filename);
+      outfile << std::setw(4) << bgset << std::endl;
+      std::cout << "Wrote tmp" << filename << std::endl;
+   }
+   std::stringstream ss;
+   ss << bgset;
+   ptree top;
+   boost::property_tree::read_json(ss, top);
+   TrigConf::L1BunchGroupSet newbgs(std::move(top));
+   TrigConf::JsonFileWriterL1 writer;
+   writer.writeJsonFile(filename, newbgs);
+}
+
+/**
+ * Conversion of L1 prescales set
+ **/
+void
+convertRun2L1PrescalesToRun3(const TrigConf::CTPConfig* ctpConfig, const std::string& filename, bool writeTmpFile) {
+   const TrigConf::PrescaleSet & l1pss = ctpConfig->prescaleSet();
+   if(l1pss.isNull()) {
+      std::cout << "L1PrescaleSet is empty, no file will be produced" << std::endl;
+      return;
+   }
+
+   json psset;
+   psset["filetype"] = "l1prescale";
+   psset["name"] = l1pss.name();
+   json jCuts;
+   for (size_t id = 0; id < l1pss.prescales_float().size(); ++id) {
+      json jCut;
+      auto itemPtr = ctpConfig->menu().item(id);
+      if ( itemPtr != nullptr ) {
+         int32_t cut = l1pss.cuts().at(id);
+         jCut["cut"] = abs(cut);
+         jCut["enabled"] = cut > 0;
+         double ps = static_cast<double>(0xFFFFFF) / ( 0x1000000 - cut );
+         jCut["info"] = "prescale: "+std::to_string(ps);
+         jCuts[itemPtr->name()] = jCut;
+      }
+   }
+   psset["cutValues"] = jCuts;
+
+   if(writeTmpFile) {
+      std::ofstream outfile("tmp" + filename);
+      outfile << std::setw(4) << psset << std::endl;
+      std::cout << "Wrote tmp" << filename << std::endl;
+   }
+   std::stringstream ss;
+   ss << psset;
+   ptree top;
+   boost::property_tree::read_json(ss, top);
+   TrigConf::L1PrescalesSet ps(std::move(top));
+   TrigConf::JsonFileWriterL1 writer;
+   writer.writeJsonFile(filename, ps);
+}
diff --git a/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.h b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.h
new file mode 100644
index 000000000000..4580ea04ff62
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfStorage/src/test/Run2toRun3ConvertersL1.h
@@ -0,0 +1,25 @@
+// Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+#include "TrigConfL1Data/CTPConfig.h"
+#include "L1TopoConfig/L1TopoMenu.h"
+
+/** @brief Run 2 to Run 3 L1 menu converter
+ *  Converts Run 2 L1 menu and L1Topo menu into run 3 format and writes out a json file
+ * @param ctpConfig pointer to L1 menu
+ * @param topoMenu pointer to L1Topo menu (can be empty)
+ * @param filename name of the output json file
+ * @param writeTmpFile writes out a temporary version of the json file (before filling the L1Menu structure)
+ */
+void convertRun2L1MenuToRun3(const TrigConf::CTPConfig* ctpConfig, const TXC::L1TopoMenu * topoMenu,
+                             const std::string& filename, bool writeTmpFile = false);
+
+/** @brief Run 2 to Run 3 bunchgroup converter
+ * @param ctpConfig pointer to L1 menu (which contains the bunchgroups)
+ * @param filename name of the output json file
+ */
+void convertRun2BunchGroupsToRun3(const TrigConf::CTPConfig* ctpConfig, const std::string& filename, bool writeTmpFile = false);
+
+/** @brief Run 2 to Run 3 L1 prescale converter
+ * @param ctpConfig pointer to L1 menu (which contains the prescales)
+ * @param filename name of the output json file
+ */
+void convertRun2L1PrescalesToRun3(const TrigConf::CTPConfig* ctpConfig, const std::string& filename, bool writeTmpFile = false);
-- 
GitLab