diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1CTP.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1CTP.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f47669c86026cc321a6f3e5ffa4e3cf7aef6f1b
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1CTP.h
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGCONFDATA_L1CTP_H
+#define TRIGCONFDATA_L1CTP_H
+
+#include "TrigConfData/DataStructure.h"
+
+#include <map>
+
+namespace TrigConf {
+
+   /** @brief a TriggerLine entry describes the location of a threshold multiplicity on a cable (connector)
+    *
+    * for electrical connections from L1Topo boards it also knows
+    * which fpga they come from and which clock signal they have (those signals run on doubled clock)
+    */
+
+   /** @brief L1 board configuration */
+   class L1CTP final : public DataStructure {
+   public:
+
+      L1CTP() = default;
+      /** Constructor initialized with configuration data 
+       * @param data The data containing the L1 CTP configuration 
+       */
+      L1CTP(const std::string & name, const ptree & data);
+
+      L1CTP(const L1CTP &) = delete;
+      L1CTP& operator=(const L1CTP&) = delete;
+      L1CTP(L1CTP&&) = delete;
+
+      ~L1CTP() = default;
+
+      /** @brief name of ctpin connector
+       * @param slot CTPIN board 7..9 
+       * @param conn CTPIN connector 0..3 on each board
+       */
+      const std::string & ctpin(size_t slot, size_t conn) const;
+
+      const std::string & electrical(size_t conn) const;
+
+      const std::string & optical(size_t conn) const;
+
+      const std::map<std::string, std::pair<size_t,std::string>> ctpMon() const { return m_ctpmon; }
+
+   private:
+
+      virtual void update() { load(); };
+      void load();
+      std::string m_ctpin[3][4];
+      std::string m_electrical[3];
+      std::string m_optical[12];
+
+      std::map<std::string, std::pair<size_t,std::string>> m_ctpmon;
+   };
+}
+
+#endif
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Connector.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Connector.h
index 7cb5f6fe1e3b827a93623fc052c5cc782427081c..69cd439b7ec972f06603675d78524a47c9b696d0 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Connector.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Connector.h
@@ -63,8 +63,10 @@ namespace TrigConf {
       /** Accessor to the number of trigger lines */
       std::size_t size() const;
 
+      std::string type() const;
+
       /** Accessor to the connector type */
-      ConnectorType type() const;
+      ConnectorType connectorType() const;
 
       /** names of all trigger lines */
       std::vector<std::string> triggerLineNames() const;
@@ -84,7 +86,14 @@ namespace TrigConf {
 
       const TrigConf::TriggerLine & triggerLine( const std::string & lineName ) const;
 
+      bool legacy() const { return m_isLegacy; }
+      
+      [[deprecated("Use legacy() instead.")]]
       bool isLegacy() const { return m_isLegacy; }
+      
+      std::size_t maxFpga() const { return m_maxFpga; }
+
+      std::size_t maxClock() const { return m_maxClock; }
 
    private:
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Item.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Item.h
index 6870b781f86f5ce3760fad5821b0b652449e4023..26ea08d19cbbfcb7f9782ec6f59646a35501b9b3 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Item.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Item.h
@@ -62,6 +62,10 @@ namespace TrigConf {
        */
       unsigned char triggerTypeAsUChar() const;
 
+      /** Accessor to the item legacy flag
+       */
+      std::optional<bool> legacy() const;
+
       /** Accessor to the item logic
        *
        * The current description of the logic is rather complex and
@@ -72,7 +76,8 @@ namespace TrigConf {
    private:
 
       /** Update the internal data after modification of the data object */
-      virtual void update();
+      virtual void update() { load(); };
+      void load();
 
       std::vector<std::string> m_bunchgroups{};
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h
index 719e4ed293f383dc90870483347b2fcf45c376fc..cfee18231a7400694a2d56443766a267fda8c285 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Menu.h
@@ -13,6 +13,7 @@
 #include "TrigConfData/L1TopoAlgorithm.h"
 #include "TrigConfData/L1Threshold.h"
 #include "TrigConfData/L1ThrExtraInfo.h"
+#include "TrigConfData/L1CTP.h"
 
 #include <vector>
 #include <map>
@@ -140,6 +141,9 @@ namespace TrigConf {
       /** Name of connector from name of threshold or triggerline */
       const std::string & connectorNameFromThreshold(const std::string & thresholdName) const;
 
+      /** the CTP configuration */
+      const TrigConf::L1CTP & ctp() const { return m_ctp; }
+
       /** print overview of L1 Menu */
       void printMenu(bool full = false) const;
 
@@ -171,6 +175,8 @@ namespace TrigConf {
       std::map<std::string, std::map<std::string, TrigConf::L1TopoAlgorithm*>> m_algorithmsByName{}; // map from category and algorithm name to algorithm 
       std::map<std::string, std::map<std::string, TrigConf::L1TopoAlgorithm*>> m_algorithmsByOutput{}; // map from category and output name to algorithm
 
+      TrigConf::L1CTP m_ctp;
+
    };
 
 }
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThrExtraInfo.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThrExtraInfo.h
index 4b10ee02af45c70b46cc2ae0b55311a8a6b8f0a5..53bed1710af5ecf850ad5de0611a6d336fe0cf8d 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThrExtraInfo.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThrExtraInfo.h
@@ -81,8 +81,8 @@ namespace TrigConf {
       virtual ~L1ThrExtraInfo_JETLegacy() = default;
       virtual std::string className() const { return "L1ThrExtraInfo_JETLegacy"; }
       unsigned int jetScale() const { return 1000 / resolutionMeV(); }
-      float ptMinToTopoLargeWindow() const { return m_ptMinToTopoLargeWindowMeV / 1000.0f; }
-      float ptMinToTopoSmallWindow() const { return m_ptMinToTopoSmallWindowMeV / 1000.0f; }
+      double ptMinToTopoLargeWindow() const { return m_ptMinToTopoLargeWindowMeV / 1000.0; }
+      double ptMinToTopoSmallWindow() const { return m_ptMinToTopoSmallWindowMeV / 1000.0; }
       unsigned int ptMinToTopoLargeWindowMeV() const { return m_ptMinToTopoLargeWindowMeV; }
       unsigned int ptMinToTopoSmallWindowMeV() const { return m_ptMinToTopoSmallWindowMeV; }
       unsigned int ptMinToTopoLargeWindowCounts() const { return energyInCounts( m_ptMinToTopoLargeWindowMeV, resolutionMeV() ); }
@@ -149,7 +149,8 @@ namespace TrigConf {
       public:
          Isolation( const boost::property_tree::ptree & );
          int isolation() const { return m_isolation; }
-         float isolation_f() const { return m_isolation/100.; }
+         double isolation_d() const { return m_isolation/100.; }
+         unsigned int maxEt() const { return m_maxEt; }
       private:
          int m_isolation {0};
          unsigned int m_maxEt { 0 };
@@ -161,8 +162,8 @@ namespace TrigConf {
       float ptMinToTopo() const { return m_ptMinToTopoMeV/1000.0f; }
       unsigned int ptMinToTopoMeV() const { return m_ptMinToTopoMeV; }
       unsigned int ptMinToTopoCounts() const { return energyInCounts( m_ptMinToTopoMeV, resolutionMeV() ); }
-      const Isolation & isolation(TrigConf::Isolation::WP wp, int eta) const;
-      const ValueWithEtaDependence<TrigConf::Isolation> & isolation(TrigConf::Isolation::WP wp) const;
+      const Isolation & isolation(TrigConf::Isolation::WP wp, int eta) const { return m_isolation.at(wp).at(eta); }
+      const ValueWithEtaDependence<Isolation> & isolation(TrigConf::Isolation::WP wp) const  { return m_isolation.at(wp); }
    private:
       /** Update the internal members */
       void load();
@@ -177,18 +178,18 @@ namespace TrigConf {
          L1ThrExtraInfoBase(thrTypeName, data) { load(); }
       virtual ~L1ThrExtraInfo_jJ() = default;
       virtual std::string className() const { return "L1ThrExtraInfo_jJ"; }
-      float ptMinToTopoLarge(int eta = 0) const { return ptMinToTopoLargeMeV(eta) / 1000.0f; }
-      float ptMinToTopoSmall(int eta = 0) const { return ptMinToTopoSmallMeV(eta) / 1000.0f; }
-      unsigned int ptMinToTopoLargeMeV(int eta = 0) const { return m_ptMinToTopoLargeMeV.at(eta); }
-      unsigned int ptMinToTopoSmallMeV(int eta = 0) const { return m_ptMinToTopoSmallMeV.at(eta); }
+      double ptMinToTopoLarge(int eta = 0) const { return ptMinToTopoLargeMeV(eta) / 1000.0; }
+      double ptMinToTopoSmall(int eta = 0) const { return ptMinToTopoSmallMeV(eta) / 1000.0; }
+      unsigned int ptMinToTopoLargeMeV(int eta = 0) const { return m_ptMinToTopoMeV.at(eta).second; }
+      unsigned int ptMinToTopoSmallMeV(int eta = 0) const { return m_ptMinToTopoMeV.at(eta).first; }
       unsigned int ptMinToTopoLargeCounts(int eta = 0) const { return energyInCounts( ptMinToTopoLargeMeV(eta), resolutionMeV() ); }
       unsigned int ptMinToTopoSmallCounts(int eta = 0) const { return energyInCounts( ptMinToTopoSmallMeV(eta), resolutionMeV() ); }
+      const ValueWithEtaDependence<std::pair<unsigned int,unsigned int>> & ptMinToTopoMeV() const { return m_ptMinToTopoMeV; }
    private:
       /** Update the internal members */
       void load();
       /** jJ specific data */
-      ValueWithEtaDependence<unsigned int> m_ptMinToTopoSmallMeV{"jJptMinTopoLarge"};
-      ValueWithEtaDependence<unsigned int> m_ptMinToTopoLargeMeV{"jJptMinTopoSmall"};
+      ValueWithEtaDependence<std::pair<unsigned int,unsigned int>> m_ptMinToTopoMeV{"jJptMinTopo"};
    };
 
 
@@ -231,7 +232,7 @@ namespace TrigConf {
       std::vector<unsigned int> knownRpcPtValues() const;
       std::vector<unsigned int> knownTgcPtValues() const;
       std::vector<std::string> exclusionListNames() const;
-      const std::map<std::string, std::vector<unsigned int>> & exlusionList(const std::string & listName) const;
+      const std::map<std::string, std::vector<unsigned int>> & exclusionList(const std::string & listName) const;
    private:
       /** Update the internal members */
       void load();
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Threshold.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Threshold.h
index 41418eeb3d15c5fc55f2a4cf83d58240619b3575..0acecd90f5bb0f83e5a8a3a1d9888cbdeeab51c0 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Threshold.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Threshold.h
@@ -16,8 +16,8 @@ namespace TrigConf {
     ************************************/
    class L1Threshold_EM final : public L1Threshold_Calo {
    public:
-      L1Threshold_EM( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) { load(); }
+      L1Threshold_EM( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) { load(); }
       virtual ~L1Threshold_EM() = default;
       // class name
       virtual std::string className() const override { return "L1Threshold_EM"; }
@@ -36,8 +36,8 @@ namespace TrigConf {
 
    class L1Threshold_TAU final : public L1Threshold_Calo {
    public:
-      L1Threshold_TAU( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) { load(); }
+      L1Threshold_TAU( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) { load(); }
       virtual ~L1Threshold_TAU() = default;
       virtual std::string className() const override { return "L1Threshold_TAU"; }
       // access functions
@@ -54,54 +54,80 @@ namespace TrigConf {
 
    class L1Threshold_JET final : public L1Threshold_Calo {
    public:
-      L1Threshold_JET( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) {};
+      L1Threshold_JET( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) { load(); };
       virtual ~L1Threshold_JET() = default;
       virtual std::string className() const override { return "L1Threshold_JET"; }
+      unsigned int window(int eta = 0) const;
+   protected:
+      virtual void update() override {
+         L1Threshold_Calo::update();
+         load();
+      }
+   private:
+      ValueWithEtaDependence<unsigned int> m_etaDepWindow{""}; ///< eta-dependent threshold value in MeV
+      void load();
    };
 
    class L1Threshold_XE final : public L1Threshold_Calo {
    public:
-      L1Threshold_XE( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) {};
+      L1Threshold_XE( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) {};
       virtual ~L1Threshold_XE() = default;
       virtual std::string className() const override { return "L1Threshold_XE"; }
    };
 
    class L1Threshold_XS final : public L1Threshold_Calo {
    public:
-      L1Threshold_XS( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) {};
+      L1Threshold_XS( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) {};
       virtual ~L1Threshold_XS() = default;
       virtual std::string className() const override { return "L1Threshold_XS"; }
    };
 
    class L1Threshold_TE final : public L1Threshold_Calo {
    public:
-      L1Threshold_TE( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) {};
+      L1Threshold_TE( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) {};
       virtual ~L1Threshold_TE() = default;
       virtual std::string className() const override { return "L1Threshold_TE"; }
    };
 
    /************************************
     *
-    *  NIM and internal thresholds
+    *  ZB, NIM and internal thresholds
     *
     ************************************/
+   class L1Threshold_ZB final : public L1Threshold {
+   public:
+      L1Threshold_ZB( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold(name, type, extraInfo, data) { load(); };
+      virtual ~L1Threshold_ZB() = default;
+      virtual std::string className() const override { return "L1Threshold_ZB"; }
+      const std::string & seed() const { return m_seed; }
+      unsigned int seedBcdelay() const { return m_seedBcdelay; }
+      unsigned int seedMultiplicity() const { return m_seedMultiplicity; }
+   protected:
+      virtual void update() override { load(); }
+   private:
+      std::string m_seed{""};
+      unsigned int m_seedBcdelay{0};
+      unsigned int m_seedMultiplicity{1};
+      void load();
+   };
 
    class L1Threshold_NIM final : public L1Threshold {
    public:
-      L1Threshold_NIM( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold(name, type, m_extraInfo, data) {};
+      L1Threshold_NIM( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold(name, type, extraInfo, data) {};
       virtual ~L1Threshold_NIM() = default;
       virtual std::string className() const override { return "L1Threshold_NIM"; }
    };
 
    class L1Threshold_internal final : public L1Threshold {
    public:
-      L1Threshold_internal( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold(name, type, m_extraInfo, data) {};
+      L1Threshold_internal( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold(name, type, extraInfo, data) {};
       virtual ~L1Threshold_internal() = default;
       virtual std::string className() const override { return "L1Threshold_internal"; }
    };
@@ -113,8 +139,8 @@ namespace TrigConf {
     ************************************/
    class L1Threshold_eEM final : public L1Threshold_Calo {
    public:
-      L1Threshold_eEM( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) { load(); }
+      L1Threshold_eEM( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) { load(); }
       virtual ~L1Threshold_eEM() = default;
       virtual std::string className() const override { return "L1Threshold_eEM"; }
       // access functions
@@ -136,8 +162,8 @@ namespace TrigConf {
 
    class L1Threshold_eTAU final : public L1Threshold_Calo {
    public:
-      L1Threshold_eTAU( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold_Calo(name, type, m_extraInfo, data) { load(); }
+      L1Threshold_eTAU( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) { load(); }
       virtual ~L1Threshold_eTAU() = default;
       virtual std::string className() const override { return "L1Threshold_eTAU"; }
    protected:
@@ -149,9 +175,20 @@ namespace TrigConf {
       void load();
    };
 
-
-
-
+   class L1Threshold_jJ final : public L1Threshold_Calo {
+   public:
+      L1Threshold_jJ( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold_Calo(name, type, extraInfo, data) { load(); }
+      virtual ~L1Threshold_jJ() = default;
+      virtual std::string className() const override { return "L1Threshold_jJ"; }
+   protected:
+      virtual void update() override {
+         L1Threshold_Calo::update();
+         load();
+      }
+   private:
+      void load();
+   };
 
 
    /************************************
@@ -161,8 +198,8 @@ namespace TrigConf {
     ************************************/
    class L1Threshold_MU final : public L1Threshold {
    public:
-      L1Threshold_MU( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> m_extraInfo, const ptree & data) :
-         L1Threshold(name, type, m_extraInfo, data) { load(); }
+      L1Threshold_MU( const std::string & name, const std::string & type, std::weak_ptr<L1ThrExtraInfoBase> extraInfo, const ptree & data) :
+         L1Threshold(name, type, extraInfo, data) { load(); }
       virtual ~L1Threshold_MU() = default;
       virtual std::string className() const override { return "L1Threshold_MU"; }
 
@@ -175,8 +212,10 @@ namespace TrigConf {
       unsigned int idxEndcap() const { return m_idxEndcap; }
       unsigned int idxForward() const { return m_idxForward; }
       const std::string & region() const { return m_region; }
-      const std::string & tgcFlag() const { return m_tgcFlag; }
+      const std::string & tgcFlags() const { return m_tgcFlags; }
       const std::string & rpcExclROIList() const { return m_rpcExclROIList; }
+      //std::optional<std::reference_wrapper<std::string>> rpcExclROIListOptional() const { m_rpcExclROIList.empty() ? std::nullopt :
+      //       return std::optional<std::reference_wrapper<std::string>>{m_rpcExclROIList} ;
    protected:
       virtual void update() override {
          L1Threshold::update();
@@ -193,7 +232,7 @@ namespace TrigConf {
       unsigned int m_idxForward{0};
       // the isolation requirement
       std::string m_region{""}; ///< comma-separated list of BA, EC, FW or the string ALL
-      std::string m_tgcFlag{""}; ///< a logical expression like 'F & C | F & H | C & H'
+      std::string m_tgcFlags{""}; ///< a logical expression like 'F & C | F & H | C & H'
       std::string m_rpcExclROIList{""}; ///< a string sepcifying the list of ROIs to be excluded (the lists are defined in the extraInfo_MU)
    };
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.h
index 2af1a9f53da62968ed839138ea3088d5be2ca31e..206bbf715052489a1a8d1dad4f7604c9653713aa 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <vector>
+#include <optional>
 
 namespace TrigConf {
 
@@ -54,12 +55,15 @@ namespace TrigConf {
       bool empty() const;
       size_t size() const;
       const T & at(int eta) const;
+      std::optional<std::reference_wrapper<const T>> outsideRangeValue() const;
       const_iterator begin() const noexcept;
       const_iterator end() const noexcept;
       void addRangeValue(const T & value, int etaMin, int etaMax, unsigned int priority, bool symmetric = true);
+      void setOutsideRangeValue(const T & value);
    private:
       const std::string m_name {""};
       std::vector<RangeValue> m_rangeValues{};
+      std::optional<T> m_outsideRangeValue {std::nullopt};
    };
 
 
@@ -91,6 +95,9 @@ namespace TrigConf {
 
       bool hasExtraInfo( const std::string & key = "") const;
 
+      std::optional<std::reference_wrapper<const TrigConf::DataStructure>>
+      getExtraInfo( const std::string & key) const;
+
       unsigned int resolutionMeV() const { 
          return m_resolutionMeV;
       }
@@ -286,6 +293,8 @@ namespace TrigConf {
    class Isolation {
    public:
       enum class WP { NONE = 0, LOOSE = 1, MEDIUM = 2, TIGHT = 3 };
+      static std::string wpToString(WP);
+      static WP stringToWP(const std::string &);
       Isolation() = default;
       Isolation( const boost::property_tree::ptree & );
       bool isDefined() const { return m_isDefined; } 
@@ -294,9 +303,9 @@ namespace TrigConf {
       int rhad()       const { return m_rhad; }
       int had()        const { return m_rhad; }
       int maxEt()      const { return m_maxEt; }
-      float reta_f()       const { return m_reta/100.; } 
-      float wstot_f()      const { return m_wstot/100.; }
-      float rhad_f()       const { return m_rhad/100.; }
+      double reta_d()       const { return m_reta/100.; } 
+      double wstot_d()     const { return m_wstot/100.; }
+      double rhad_d()       const { return m_rhad/100.; }
    private:
       bool m_isDefined { false };
       int m_reta { 0 };
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.icc b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.icc
index b73913f9b29c8e77129690f31dd421e863765c28..3e19b1fc218c40c5ea1dfaa56d247522ca22fc00 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.icc
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1ThresholdBase.icc
@@ -44,6 +44,19 @@ namespace TrigConf {
       m_rangeValues.emplace_back( std::move(rv) );
    }
 
+
+   template<class T>
+   void ValueWithEtaDependence<T>::setOutsideRangeValue(const T & value) {
+      m_outsideRangeValue = value;
+   }
+
+   template<class T>
+   std::optional<std::reference_wrapper<const T>> 
+   ValueWithEtaDependence<T>::outsideRangeValue() const {
+      return m_outsideRangeValue;
+   }
+
+
    template<class T>
    const T & ValueWithEtaDependence<T>::at(int eta) const {
       // the ranges are by definition such that the lower boundary is inclusive, while the upper boundary is exclusive
@@ -65,8 +78,10 @@ namespace TrigConf {
       }
       if( retVal ) {
          return * retVal;
+      } else if(m_outsideRangeValue) {
+         return *m_outsideRangeValue;
       } else {
-         throw std::runtime_error(name() + ": no value found with eta = " + std::to_string(eta));
+         throw std::out_of_range(name() + ": no value found with eta = " + std::to_string(eta));
       }
    }
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1TopoAlgorithm.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1TopoAlgorithm.h
index bc172ebd8f9653722d73807a4aca23f961eb24ad..42af64e0a1c3f866b9eab4f026d35f050cb75a3f 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1TopoAlgorithm.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1TopoAlgorithm.h
@@ -7,6 +7,7 @@
 
 #include "TrigConfData/DataStructure.h"
 #include <vector>
+#include <optional>
 
 namespace TrigConf {
 
@@ -22,11 +23,19 @@ namespace TrigConf {
       enum class AlgorithmType { SORTING, DECISION, MULTIPLICITY, UNKNOWN };
 
       struct VariableParameter {
-      VariableParameter(const std::string & _name, int _value, unsigned int _selection) 
-      : name(_name), value(_value), selection(_selection) {}
-         std::string name{""};
-         int value{0};
-         unsigned int selection{0};
+      public:
+         VariableParameter(const std::string & name, int value, unsigned int selection) 
+            : m_name(name), m_value(value), m_selection(selection) {}
+         VariableParameter(const std::string & name, int value) 
+            : m_name(name), m_value(value) {}
+         const std::string & name() const { return m_name; }
+         int value() const { return m_value; }
+         unsigned int selection() const { return m_selection.value_or(0); }
+         std::optional<unsigned int> selection_optional() const { return m_selection; }
+      private:
+         std::string m_name{""};
+         int m_value{0};
+         std::optional<unsigned int> m_selection{};
       };
 
       /** Constructor */
@@ -83,10 +92,14 @@ namespace TrigConf {
       /** print main info */
       void print(std::ostream & os = std::cout) const override;
 
+   protected:
+
+      virtual void update() override { load(); }
+
    private:
 
       /** Update the internal data after modification of the data object */
-      virtual void update() override;
+      void load();
 
       AlgorithmType m_type{ AlgorithmType::UNKNOWN };
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4a6b69032195aa403e0a4992dc3b1465b5762729
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1CTP.cxx
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TrigConfData/L1CTP.h"
+
+TrigConf::L1CTP::L1CTP(const std::string & name, const boost::property_tree::ptree & data) 
+   : DataStructure(name,data)
+{
+   load();
+}
+
+void
+TrigConf::L1CTP::load()
+{
+   if(! isInitialized() || empty() ) {
+      return;
+   }
+   auto inputs = data().get_child("inputs");
+   for(size_t slot = 7; slot<=9; ++slot) {
+      for(size_t conn=0; conn<4; ++conn) {
+         m_ctpin[slot-7][conn] = inputs.get_optional<std::string>("ctpin.slot" + std::to_string(slot) + ".connector" + std::to_string(conn)).get_value_or("");
+      }
+   }
+   auto electrical = inputs.get_child("electrical");
+   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("");
+   }
+   for( auto & mon : data().get_child("monitoring.ctpmon") ) {
+      std::string monName = mon.first;
+      size_t multiplicity = mon.second.get_child("multiplicity").get_value<size_t>();
+      std::string thr = mon.second.get_child("thr").get_value<std::string>();
+      m_ctpmon.emplace( std::piecewise_construct,
+                        std::forward_as_tuple(monName),
+                        std::forward_as_tuple(multiplicity, thr)
+                        );
+   }
+   
+
+}
+
+const std::string &
+TrigConf::L1CTP::ctpin(size_t slot, size_t conn) const
+{
+   if(slot<7 or slot>9) {
+      throw std::runtime_error("CTPIN slot must be between 7 and 9, but " + std::to_string(slot) + "was specified");
+   }
+   if(conn>3) {
+      throw std::runtime_error("CTPIN connector must be between 0 and 3, but " + std::to_string(conn) + "was specified");
+   }
+   return m_ctpin[slot-7][conn];
+}
+
+
+const std::string &
+TrigConf::L1CTP::electrical(size_t conn) const {
+   if(conn>2) {
+      throw std::runtime_error("Electrical connector must be between 0 and 2, but " + std::to_string(conn) + "was specified");
+   }
+   return m_electrical[conn];
+}
+
+const std::string &
+TrigConf::L1CTP::optical(size_t conn) const {
+   if(conn>11) {
+      throw std::runtime_error("Optical connector must be between 0 and 11, but " + std::to_string(conn) + "was specified");
+   }
+   return m_optical[conn];
+}
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx
index 3b206408e3d7953a6f366b24ab350321424b8d3e..3d7de39fe718199f18ac10d63949e17473a802ee 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx
@@ -57,7 +57,7 @@ TrigConf::L1Connector::update()
                path += std::to_string(fpga);
             }
             path += ".clock";
-            path += std::to_string(clock);            
+            path += std::to_string(clock);
          }
          const auto & triggerlines = data().get_child(path);
          m_triggerLines[fpga][clock].reserve(triggerlines.size());
@@ -75,8 +75,22 @@ TrigConf::L1Connector::update()
 }
 
 
-TrigConf::L1Connector::ConnectorType
+std::string
 TrigConf::L1Connector::type() const 
+{
+   switch( m_type ) {
+   case ConnectorType::ELECTRICAL:
+      return "electrical";
+   case ConnectorType::OPTICAL:
+      return "optical";
+   case ConnectorType::CTPIN:
+      return "ctpin";
+   }
+   return "";
+}
+
+TrigConf::L1Connector::ConnectorType
+TrigConf::L1Connector::connectorType() const 
 {
    return m_type;
 }
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Item.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Item.cxx
index e9288a4022bf7f6302b6e168598e59a8c40df05d..a4a6587d8f384dd37b7c7065bf429ede4276a967 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Item.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Item.cxx
@@ -10,7 +10,7 @@ TrigConf::L1Item::L1Item()
 TrigConf::L1Item::L1Item(const boost::property_tree::ptree & data) 
    : DataStructure(data)
 {
-   update();
+   load();
 }
 
 TrigConf::L1Item::~L1Item()
@@ -22,7 +22,7 @@ TrigConf::L1Item::className() const {
 }
 
 void
-TrigConf::L1Item::update()
+TrigConf::L1Item::load()
 {
    if(! isInitialized() || empty() ) {
       return;
@@ -74,6 +74,12 @@ TrigConf::L1Item::triggerType() const
    return getAttribute("triggerType");
 }
 
+std::optional<bool>
+TrigConf::L1Item::legacy() const
+{
+   return hasAttribute("legacy") ? std::optional<bool>{getAttribute<bool>("legacy")} : std::nullopt;
+}
+
 unsigned char
 TrigConf::L1Item::triggerTypeAsUChar() const
 {
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
index 27660fb9a7e0694fdb6f3367148b655a9a3e7ea8..3becff35226048a63f09857195565f11dcb2c7b7 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
@@ -25,7 +25,6 @@ TrigConf::L1Menu::update()
    if(! isInitialized() || empty() ) {
       return;
    }
-
    try {
       m_name = getAttribute("name");
       // thresholds
@@ -128,6 +127,15 @@ TrigConf::L1Menu::update()
       std::cerr << "ERROR: problem when building L1 menu structure (algorithms). " << ex.what() << std::endl;
       throw;
    }
+
+   try {
+      // CTP
+      m_ctp.setData(data().get_child("ctp"));
+   }
+   catch(std::exception & ex) {
+      std::cerr << "ERROR: problem when building L1 menu structure (CTP). " << ex.what() << std::endl;
+      throw;
+   }
 }
 
 unsigned int 
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1ThrExtraInfo.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1ThrExtraInfo.cxx
index 3a5cf6f72e77a97140c184718801a30ee2d23e68..9b2ba202ddabd3829102d3f4bbed7f0d94337c10 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1ThrExtraInfo.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1ThrExtraInfo.cxx
@@ -130,6 +130,10 @@ TrigConf::L1ThrExtraInfo::thrExtraInfo(const std::string & thrTypeName) const
 const TrigConf::IsolationLegacy &
 TrigConf::L1ThrExtraInfo_EMTAULegacy::isolation(const std::string & thrType, size_t bit) const
 {
+   if(bit<1 or bit>5) {
+      throw std::out_of_range("When accessing the legacy L1Calo EM or TAU isolation bit must be between 1 and 5, but bit=" 
+                              + std::to_string(bit) + " was requested");
+   }
    try {
       return m_isolation.at(thrType)[bit-1];
    }
@@ -227,6 +231,7 @@ TrigConf::L1ThrExtraInfo_eEMTAU::load()
  *******/
 TrigConf::L1ThrExtraInfo_eTAU::Isolation::Isolation( const boost::property_tree::ptree & pt ) {
    m_isolation = lround(100 * pt.get_optional<float>("isolation").get_value_or(0));
+   m_maxEt = pt.get_optional<unsigned int>("maxEt").get_value_or(0);
 }
 
 void
@@ -237,8 +242,7 @@ TrigConf::L1ThrExtraInfo_eTAU::load()
          m_ptMinToTopoMeV = lround(1000 * x.second.getValue<float>());
       } else if( x.first == "workingPoints" ) {
          for( auto & y : x.second.data() ) {
-            auto wp = (y.first == "Loose") ? TrigConf::Isolation::WP::LOOSE :
-               ( (y.first == "Medium") ? TrigConf::Isolation::WP::MEDIUM : TrigConf::Isolation::WP::TIGHT );
+            auto wp = TrigConf::Isolation::stringToWP(y.first);
             auto & iso = m_isolation.emplace(wp, string("eEM_WP_" + y.first)).first->second;
             for(auto & c : y.second ) {
                int etamin = c.second.get_optional<int>("etamin").get_value_or(-49);
@@ -269,8 +273,8 @@ TrigConf::L1ThrExtraInfo_jJ::load()
             auto small = k.second.get_child("small").get_value<float>();
             auto large = k.second.get_child("large").get_value<float>();
             auto priority = k.second.get_optional<unsigned int>("priority").get_value_or(0);            
-            m_ptMinToTopoSmallMeV.addRangeValue( lround(1000*small), etamin, etamax, priority, /*symmetric=*/ false);
-            m_ptMinToTopoLargeMeV.addRangeValue( lround(1000*large), etamin, etamax, priority, /*symmetric=*/ false);
+            m_ptMinToTopoMeV.addRangeValue( std::make_pair<unsigned int, unsigned int>(lround(1000*small),lround(1000*large)),
+                                            etamin, etamax, priority, /*symmetric=*/ false);
          }
       }
    }
@@ -359,7 +363,7 @@ TrigConf::L1ThrExtraInfo_MU::exclusionListNames() const
 
 
 const std::map<std::string, std::vector<unsigned int> > &
-TrigConf::L1ThrExtraInfo_MU::exlusionList(const std::string & listName) const
+TrigConf::L1ThrExtraInfo_MU::exclusionList(const std::string & listName) const
 {
    try {
       return m_roiExclusionLists.at(listName);
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Threshold.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Threshold.cxx
index 070e107bae45056866553c2edb5e34d8182269d0..87ddd649dd2cb9ea5d698a5bc84c0b3f6333e18b 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Threshold.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Threshold.cxx
@@ -62,6 +62,38 @@ TrigConf::L1Threshold_TAU::load()
    }
 }
 
+/**
+ * JET
+ */
+void
+TrigConf::L1Threshold_JET::load()
+{
+   if( const auto & thrVs = data().get_child_optional("thrValues") ) {
+      for( auto & x : thrVs.get() ) {
+         auto etamin = x.second.get_child("etamin").get_value<unsigned int>();
+         auto etamax = x.second.get_child("etamax").get_value<unsigned int>();
+         auto priority = x.second.get_child("priority").get_value<unsigned int>();
+         auto window = x.second.get_child("window").get_value<unsigned int>();
+         m_etaDepWindow.addRangeValue(window, etamin, etamax, priority, /*symmetric=*/ false);
+      }
+   }
+}
+unsigned int
+TrigConf::L1Threshold_JET::window(int eta) const {
+   return m_etaDepWindow.at(eta);
+}
+
+/**
+ * ZB
+ */
+void
+TrigConf::L1Threshold_ZB::load()
+{
+   m_seed = getAttribute("seed");
+   m_seedBcdelay = getAttribute<unsigned int>("seedBcdelay");
+   m_seedMultiplicity = getAttribute<unsigned int>("seedMultiplicity");
+}
+
 /******************************************
  *
  *  New L1Calo thresholds
@@ -87,6 +119,9 @@ void
 TrigConf::L1Threshold_eTAU::load()
 {}
 
+void
+TrigConf::L1Threshold_jJ::load()
+{}
 
 /******************************************
  *
@@ -116,7 +151,7 @@ TrigConf::L1Threshold_MU::load()
    m_idxForward = muInfo->tgcIdxForPt(m_ptForward);
 
    m_rpcExclROIList = getAttribute("rpcExclROIList", true, "");
-   m_tgcFlag = getAttribute("tgcFlags");
+   m_tgcFlags = getAttribute("tgcFlags");
    m_region = getAttribute("region");
 }
 
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx
index a8c997dad10cf4dcf01079e51575a85e89b796e5..35a021960131566c933692a39b067de37fb8f675 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx
@@ -2,6 +2,7 @@
 
 #include <algorithm>
 #include <cmath>
+#include <limits>
 
 
 #include "TrigConfData/L1ThresholdBase.h"
@@ -51,9 +52,15 @@ TrigConf::L1Threshold::createThreshold( const std::string & name, const std::str
    if( type == "eTAU" )
       return std::make_shared<L1Threshold_eTAU>( name, type, extraInfo, data );
 
+   if( type == "jJ" )
+      return std::make_shared<L1Threshold_jJ>( name, type, extraInfo, data );
+
    if( type == "MU" )
       return std::make_shared<L1Threshold_MU>( name, type, extraInfo, data );
 
+   if( type == "ZB" )
+      return std::make_shared<L1Threshold_ZB>( name, type, extraInfo, data );
+
    static const std::string NIMtypes[] = { "BCM", "BCMCMB", "LUCID", "ZDC", "BPTX", "CALREQ", "MBTS", "MBTSSI", "NIM" };
    bool isNIMtype = std::find(std::begin(NIMtypes), std::end(NIMtypes), type) !=
      std::end(NIMtypes);
@@ -64,7 +71,14 @@ TrigConf::L1Threshold::createThreshold( const std::string & name, const std::str
    if( type == "internal" )
       return std::make_shared<L1Threshold_internal>( name, type, extraInfo, data );
 
-   static const std::string noSpecialImp[] = { "JET", "XS", "jJ", "gXE", "jXE", "TOPO", "MULTTOPO", "MUTOPO", "R2TOPO", "ZB", "ALFA" };
+   static const std::string caloBaseImp[] = { "gXE", "jXE" };
+   bool useCaloBaseClass = std::find(std::begin(caloBaseImp), std::end(caloBaseImp),type) !=
+     std::end(caloBaseImp);
+
+   if( useCaloBaseClass )
+      return std::make_shared<L1Threshold_Calo>( name, type, extraInfo, data );
+
+   static const std::string noSpecialImp[] = { "JET", "XS", "TOPO", "MULTTOPO", "MUTOPO", "R2TOPO", "ALFA" };
    bool useBaseClass = std::find(std::begin(noSpecialImp), std::end(noSpecialImp),type) !=
      std::end(noSpecialImp);
 
@@ -121,9 +135,9 @@ TrigConf::L1ThrExtraInfoBase::load()
    if(! isInitialized() || empty() )
       return;
 
-   if( m_name == "internal" ) { // internal trigger have no extra info
-      return;
-   }
+   // if( m_name == "internal" ) { // internal trigger have no extra info
+   //    return;
+   // }
    for( auto & content : data() ) {
       if( content.first == "type" ||
           content.first == "thresholds" ) {
@@ -157,6 +171,12 @@ TrigConf::L1ThrExtraInfoBase::hasExtraInfo(const std::string & key) const
    return m_extraInfo.count(key)>0;
 }
 
+std::optional<std::reference_wrapper<const TrigConf::DataStructure>>
+TrigConf::L1ThrExtraInfoBase::getExtraInfo( const std::string & key) const
+{
+   bool hasKey = m_extraInfo.count(key)>0;
+   return hasKey ? std::optional<std::reference_wrapper<const TrigConf::DataStructure>>{m_extraInfo.at(key)} : std::nullopt;
+}
 
 
 
@@ -210,6 +230,14 @@ TrigConf::L1Threshold_Calo::load()
          m_etaDepThrValue.addRangeValue(value, etamin, etamax, priority, /*symmetric=*/ false);
       }
    }
+   if( const auto & ranges = data().get_child_optional("ranges") ) {
+      m_etaDepThrValue.setOutsideRangeValue(getAttribute("maxValue", true, std::numeric_limits<unsigned int>::max()));
+      for( auto & x : ranges.get() ) {
+         auto etamin = x.second.get_child("etamin").get_value<unsigned int>();
+         auto etamax = x.second.get_child("etamax").get_value<unsigned int>();
+         m_etaDepThrValue.addRangeValue(m_thrValue, etamin, etamax, /*priority=*/ 1, /*symmetric=*/ false);
+      }
+   }
 }
 
 /*
@@ -257,9 +285,6 @@ TrigConf::L1Threshold_Calo::thrValuesCounts() const {
    return thrValuesCounts;
 }
 
-
-
-
 TrigConf::IsolationLegacy::IsolationLegacy( const boost::property_tree::ptree & pt ) {
    m_isDefined = true;
    m_isobit = pt.get_child("isobit").get_value<int>();
@@ -280,12 +305,41 @@ TrigConf::operator<<(std::ostream & os, const TrigConf::IsolationLegacy & iso) {
    return os;
 }
 
+std::string
+TrigConf::Isolation::wpToString(TrigConf::Isolation::WP wp)
+{
+   if (wp == Isolation::WP::NONE)
+      return "None";
+   if (wp == Isolation::WP::LOOSE)
+      return "Loose";
+   if (wp == Isolation::WP::MEDIUM)
+      return "Medium";
+   if (wp == Isolation::WP::TIGHT)
+      return "Tight";
+   throw std::runtime_error("Unknown working point " + std::to_string(int(wp)));
+}
+
+TrigConf::Isolation::WP
+TrigConf::Isolation::stringToWP(const std::string & wpStr)
+{
+   if (wpStr == "None")
+      return Isolation::WP::NONE;
+   if (wpStr == "Loose")
+      return Isolation::WP::LOOSE;
+   if (wpStr == "Medium")
+      return Isolation::WP::MEDIUM;
+   if (wpStr == "Tight")
+      return Isolation::WP::TIGHT;
+   throw std::runtime_error("Unknown working point name " + wpStr);
+}
+
+
 TrigConf::Isolation::Isolation( const boost::property_tree::ptree & pt ) {
    m_isDefined = true;
    m_reta  = lround(100 * pt.get_optional<float>("reta").get_value_or(0));
    m_wstot = lround(100 * pt.get_optional<float>("wstot").get_value_or(0));
    m_rhad  = lround(100 * pt.get_optional<float>("rhad").get_value_or(0));
-   m_maxEt = pt.get_optional<unsigned int>("rhad").get_value_or(0);
+   m_maxEt = pt.get_optional<unsigned int>("maxEt").get_value_or(0);
 }
 
 std::ostream &
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1TopoAlgorithm.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1TopoAlgorithm.cxx
index 08ad0c1fed737772e9d85531061b48c62dbaf502..dee2e864741d2dc7a0f7a550d25a6bdf97aaa808 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1TopoAlgorithm.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1TopoAlgorithm.cxx
@@ -14,7 +14,7 @@ TrigConf::L1TopoAlgorithm::L1TopoAlgorithm(const std::string & algoName, Algorit
       throw std::runtime_error("Algorithm category must be TOPO, R2TOPO, MUTOPO or MULTTOPO, but is '" + algoCategory + "'");
    }
    m_name = algoName;
-   update();
+   load();
 }
 
 TrigConf::L1TopoAlgorithm::~L1TopoAlgorithm()
@@ -26,7 +26,7 @@ TrigConf::L1TopoAlgorithm::className() const {
 }
 
 void
-TrigConf::L1TopoAlgorithm::update()
+TrigConf::L1TopoAlgorithm::load()
 {
    if(! isInitialized() || empty() ) {
       return;
@@ -45,7 +45,9 @@ TrigConf::L1TopoAlgorithm::update()
          m_outputs.push_back(o.getValue());
       }
    } else if( m_type == AlgorithmType::MULTIPLICITY ) { // MULTIPLICITY algo
-      m_inputs.push_back(getAttribute("input"));
+      if(auto & input = getAttribute("input"); input!="null") {
+         m_inputs.push_back(input);
+      }
       m_outputs.push_back(getAttribute("output"));
    } else { // SORTING algo
       if( hasAttribute("input") ) {
@@ -62,7 +64,11 @@ TrigConf::L1TopoAlgorithm::update()
 
    if( m_type == AlgorithmType::DECISION || m_type == AlgorithmType::SORTING ) {
       for( auto & p : getList("variableParameters") ) {
-         m_parameters.emplace_back(p["name"], p.getAttribute<int>("value"), p.getAttribute<unsigned int>("selection", /*ignore-if-missing*/true, 0));
+         if(p.hasAttribute("selection")) {
+            m_parameters.emplace_back(p["name"], p.getAttribute<int>("value"), p.getAttribute<unsigned int>("selection"));
+         } else {
+            m_parameters.emplace_back(p["name"], p.getAttribute<int>("value"));
+         }
       }
    }
 
@@ -166,7 +172,7 @@ TrigConf::L1TopoAlgorithm::print(std::ostream & os) const
       auto pars = parameters();
       unsigned int idx{0};
       for( auto & p : pars ) {
-         os << "    " << idx++ << "  " << p.name << "[" << p.selection << "]  ==>  " <<  p.value << std::endl;
+         os << "    " << idx++ << "  " << p.name() << "[" << p.selection() << "]  ==>  " <<  p.value() << std::endl;
       }
    }
    os << std::endl;
diff --git a/Trigger/TrigConfiguration/TrigConfIO/CMakeLists.txt b/Trigger/TrigConfiguration/TrigConfIO/CMakeLists.txt
index f384ed7f3dadfc79ed974139a3c7100191ac7aaa..e8bf3c027ef8c03bd1b93f280b49decac76e53c6 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/CMakeLists.txt
+++ b/Trigger/TrigConfiguration/TrigConfIO/CMakeLists.txt
@@ -5,6 +5,7 @@ atlas_subdir( TrigConfIO )
 
 # External dependencies:
 find_package( Boost )
+find_package( nlohmann_json )
 find_package( CORAL COMPONENTS CoralBase CoralKernel RelationalAccess )
 
 # Component(s) in the package:
@@ -14,7 +15,7 @@ atlas_add_library( TrigConfIO
   INCLUDE_DIRS ${Boost_INCLUDE_DIRS}
   PRIVATE_INCLUDE_DIRS ${CORAL_INCLUDE_DIRS}
   LINK_LIBRARIES TrigConfData TrigConfBase
-  PRIVATE_LINK_LIBRARIES ${Boost_LIBRARIES} ${CORAL_LIBRARIES} -lstdc++fs
+  PRIVATE_LINK_LIBRARIES ${Boost_LIBRARIES} ${CORAL_LIBRARIES} nlohmann_json::nlohmann_json -lstdc++fs
   )
 
 atlas_add_library( TrigConfIOSA
@@ -24,7 +25,7 @@ atlas_add_library( TrigConfIOSA
   PRIVATE_INCLUDE_DIRS ${CORAL_INCLUDE_DIRS}
   LINK_LIBRARIES TrigConfDataSA TrigConfBase
   DEFINITIONS -DTRIGCONF_STANDALONE
-  PRIVATE_LINK_LIBRARIES ${Boost_LIBRARIES} ${CORAL_LIBRARIES} -lstdc++fs
+  PRIVATE_LINK_LIBRARIES ${Boost_LIBRARIES} ${CORAL_LIBRARIES} nlohmann_json::nlohmann_json -lstdc++fs
   )
 
 atlas_add_executable( TestTriggerMenuAccess utils/TestTriggerMenuAccess.cxx 
diff --git a/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriter.h b/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriter.h
new file mode 100644
index 0000000000000000000000000000000000000000..c62892c45e5af9f8b8f19601ab8a1376d51fc61e
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfIO/TrigConfIO/JsonFileWriter.h
@@ -0,0 +1,31 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * @brief Write Json file from L1Menu object
+ * To validate correct loading of the L1 menu
+ */
+
+#ifndef TRIGCONFSTORAGE_JSONFILEWRITER_H
+#define TRIGCONFSTORAGE_JSONFILEWRITER_H
+
+#include "TrigConfBase/TrigConfMessaging.h"
+#include "TrigConfData/L1Menu.h"
+
+namespace TrigConf {
+
+   /**
+    * @brief Loader of trigger configurations from Json files
+    */
+   class JsonFileWriter : public TrigConfMessaging {
+   public:
+
+      /** Constructor */
+      JsonFileWriter();
+
+      bool writeJsonFile(const std::string & filename, const L1Menu & l1menu) const;
+   };
+
+}
+#endif
diff --git a/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriter.cxx b/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..48ffb5972131fb8bf78999b0066158d23c78d78f
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfIO/src/JsonFileWriter.cxx
@@ -0,0 +1,493 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TrigConfIO/JsonFileWriter.h"
+
+#include <iomanip>
+#include <fstream>
+#include <algorithm>
+
+#include <nlohmann/json.hpp>
+using json = nlohmann::json;
+
+using namespace std;
+
+TrigConf::JsonFileWriter::JsonFileWriter() : 
+   TrigConfMessaging( "JsonFileWriter")
+{}
+
+
+bool
+TrigConf::JsonFileWriter::writeJsonFile(const std::string & filename, const L1Menu & l1menu) const
+{
+
+   json items({});
+   for( auto & item : l1menu ) {
+      json jItem({});
+      jItem["name"] = item.name();
+      jItem["ctpid"] = item.ctpId();
+      jItem["definition"] = item.definition();
+      jItem["monitor"] = item.monitor();
+      jItem["partition"] = item.partition();
+      jItem["triggerType"] = item.triggerType();
+      jItem["bunchgroups"] = json(item.bunchgroups());
+      if(auto legacy = item.legacy() )
+         jItem["legacy"] = *legacy;
+      items[item.name()] = jItem;
+   };
+ 
+   json thresholds({});
+   for(const std::string & thrType : l1menu.thresholdTypes()) {
+      json jThresholsByType({});
+      // first the thresholds of this type
+      for(auto & thr : l1menu.thresholds(thrType)) {
+         json jThr({});
+         jThr["mapping"] = thr->mapping();
+
+         if(thr->hasChild("sectors")) { // for MBTS_A and MBTS_C
+            std::vector<std::string> sectors; 
+            for(auto & s : thr->getList("sectors")) {
+               sectors.push_back(s.getValue());
+            }
+            jThr["sectors"] = sectors;
+         } 
+         if(thr->hasAttribute("voltage")) { // for MBTSII
+            jThr["voltage"] = thr->getAttribute<float>("voltage");
+         }
+
+         try {
+            if(thr->hasAttribute("value")) {
+               auto & caloThr = dynamic_cast<const TrigConf::L1Threshold_Calo &>(*thr); // for MBTSII
+               jThr["value"] = int(caloThr.thrValue());
+            }
+         } catch(std::bad_cast&) {};
+
+         try {
+            auto muThr = dynamic_cast<const TrigConf::L1Threshold_MU &>(*thr);
+            jThr["baThr"] = muThr.ptBarrel();
+            jThr["ecThr"] = muThr.ptEndcap();
+            jThr["fwThr"] = muThr.ptForward(); 
+            jThr["baIdx"] = muThr.idxBarrel();
+            jThr["ecIdx"] = muThr.idxEndcap();
+            jThr["fwIdx"] = muThr.idxForward();
+            jThr["region"] = muThr.region();
+            jThr["tgcFlags"] = muThr.tgcFlags();
+            if(const std::string & roiExcl = muThr.rpcExclROIList(); !roiExcl.empty()) {
+               jThr["rpcExclROIList"] = roiExcl;
+            }
+         } catch(std::bad_cast&) {};
+
+         try {
+            auto tauThr = dynamic_cast<const TrigConf::L1Threshold_TAU &>(*thr);
+            std::string isobits = "00000";
+            auto isomask = tauThr.isolationMask();
+            for(size_t b=0; b<5; ++b) {
+               if(isomask & (1<<b)) { isobits[4-b] = '1'; }
+            }
+            jThr["isobits"] = isobits;
+         } catch(std::bad_cast&) {};
+
+         try {
+            auto EMThr = dynamic_cast<const TrigConf::L1Threshold_EM &>(*thr);
+            jThr["thrValues"] = json::array_t({});
+            for(auto & rv : EMThr.thrValues()) {
+               json jRV({});
+               jRV["etamin"] = rv.etaMin();
+               jRV["etamax"] = rv.etaMax();
+               jRV["phimin"] = 0; // never used, so not read 
+               jRV["phimax"] = 64; // never used, so not read
+               std::string isobits = "00000";
+               auto isomask = EMThr.isolationMask(rv.etaMin());
+               for(size_t b=0; b<5; ++b) {
+                  if(isomask & (1<<b)) { isobits[4-b] = '1'; }
+               }
+               jRV["isobits"] = isobits;
+               jRV["priority"] = rv.priority();
+               jRV["value"] = (unsigned int)rv.value();
+               jThr["thrValues"] += jRV;
+            }
+         } catch(std::bad_cast&) {};
+
+         try {
+            auto JThr = dynamic_cast<const TrigConf::L1Threshold_JET &>(*thr);
+            jThr["thrValues"] = json::array_t({});
+            for(auto & rv : JThr.thrValues()) {
+               json jRV({});
+               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();
+               jThr["thrValues"] += jRV;
+            }
+         } catch(std::bad_cast&) {};
+
+         try {
+            auto teThr = dynamic_cast<const TrigConf::L1Threshold_TE &>(*thr);
+            //jThr["thrValues"] = json::array_t({});
+            for(auto & rv : teThr.thrValues()) {
+               json jRV({});
+               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&) {};
+
+         try {
+            auto zbThr = dynamic_cast<const TrigConf::L1Threshold_ZB &>(*thr);
+            jThr["seed"] = zbThr.seed();
+            jThr["seedBcdelay"] = zbThr.seedBcdelay();
+            jThr["seedMultiplicity"] = zbThr.seedMultiplicity();
+         } catch(std::bad_cast&) {};
+
+
+         try {
+            auto eEMThr = dynamic_cast<const TrigConf::L1Threshold_eEM &>(*thr);
+            jThr["reta"] = TrigConf::Isolation::wpToString(eEMThr.reta());
+            jThr["rhad"] = TrigConf::Isolation::wpToString(eEMThr.rhad());
+            jThr["wstot"] = TrigConf::Isolation::wpToString(eEMThr.wstot());
+            jThr["thrValues"] = json::array_t({});
+            for(auto & rv : eEMThr.thrValues()) {
+               json jRV({});
+               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&) {};
+
+         try {
+            auto jJThr = dynamic_cast<const TrigConf::L1Threshold_jJ &>(*thr);
+            jThr["ranges"] = json::array_t({});
+            for(auto & rv : jJThr.thrValues()) {
+               json jRV({});
+               jRV["etamin"] = rv.etaMin();
+               jRV["etamax"] = rv.etaMax();
+               jThr["ranges"] += jRV;
+               jThr["value"] = int(jJThr.thrValue(rv.etaMin()));
+            }
+         } catch(std::bad_cast&) {};
+
+         jThresholsByType[thr->name()] = jThr;
+      };
+      json jThrType({});
+      if(thrType != "internal") {
+         jThrType["thresholds"] = jThresholsByType;
+      }
+      jThrType["type"] = thrType;
+
+      // extra info 
+      auto & extraInfo = l1menu.thrExtraInfo().thrExtraInfo(thrType);
+      if(extraInfo.hasAttribute("ptMinToTopo")) { // for MBTSII
+         jThrType["ptMinToTopo"] = extraInfo.getAttribute<unsigned int>("ptMinToTopo");
+      }
+      if(extraInfo.hasAttribute("resolutionMeV")) { // for MBTSII
+         jThrType["resolutionMeV"] = extraInfo.getAttribute<unsigned int>("resolutionMeV");
+      }
+
+      // extra info using type specific accessors
+      if(thrType == "internal") {
+         jThrType["names"] = json::array_t({});
+         for(auto & thr : l1menu.thresholds(thrType)) {
+            jThrType["names"] += thr->name();
+         }
+         auto & extInfo = l1menu.thrExtraInfo().thrExtraInfo("internal");
+         if(auto randoms = extInfo.getExtraInfo("randoms")) {
+            for(size_t rc=0; rc<4; ++rc) {
+               jThrType["randoms"]["RNDM" + std::to_string(rc)]["cut"] = 
+                  randoms->get().getAttribute<unsigned int>("RNDM" + std::to_string(rc)+".cut");
+            }
+         }
+      }
+
+      if(thrType == "MU") {
+         auto & muinfo = l1menu.thrExtraInfo().MU();
+         for(auto & listName : muinfo.exclusionListNames()) {
+            jThrType["exclusionLists"][listName] = json::array_t({});
+            for(auto & x : muinfo.exclusionList(listName)) {
+               jThrType["exclusionLists"][listName] += json({ {"sectorName", x.first}, {"rois", x.second}});
+            }
+         }
+         for(auto & rpcPt : muinfo.knownRpcPtValues()) {
+            jThrType["roads"]["rpc"][std::to_string(rpcPt)] = muinfo.rpcIdxForPt(rpcPt);
+         }
+         for(auto & tgcPt : muinfo.knownTgcPtValues()) {
+            jThrType["roads"]["tgc"][std::to_string(tgcPt)] = muinfo.tgcIdxForPt(tgcPt);
+         }
+      }
+
+      if(thrType == "EM") {
+         auto & eminfo = l1menu.thrExtraInfo().EM();
+         for(const std::string & isoSet : { "EMIsoForEMthr", "HAIsoForEMthr" }) {
+            jThrType["isolation"][isoSet]["thrtype"] = isoSet;
+            jThrType["isolation"][isoSet]["Parametrization"] = json::array_t({});
+            for(size_t bit = 1; bit<=5; ++bit) {
+               auto & iso = eminfo.isolation(isoSet,bit);
+               json jIso({});
+               jIso["etamax"] = iso.etamax();
+               jIso["etamin"] = iso.etamin();
+               jIso["isobit"] = iso.isobit();
+               jIso["mincut"] = iso.mincut();
+               jIso["offset"] = iso.offset();
+               jIso["priority"] = iso.priority();
+               jIso["slope"] = iso.slope();
+               jIso["upperlimit"] = iso.upperlimit();
+               jThrType["isolation"][isoSet]["Parametrization"] += jIso;
+            }
+         }
+      }
+
+      if(thrType == "TAU") {
+         auto & tauinfo = l1menu.thrExtraInfo().TAU();
+         const std::string isoSet{ "EMIsoForTAUthr" };
+         jThrType["isolation"][isoSet]["thrtype"] = isoSet;
+         jThrType["isolation"][isoSet]["Parametrization"] = json::array_t({});
+         for(size_t bit = 1; bit<=5; ++bit) {
+            auto & iso = tauinfo.isolation(isoSet,bit);
+            json jIso({});
+            jIso["etamax"] = iso.etamax();
+            jIso["etamin"] = iso.etamin();
+            jIso["isobit"] = iso.isobit();
+            jIso["mincut"] = iso.mincut();
+            jIso["offset"] = iso.offset();
+            jIso["priority"] = iso.priority();
+            jIso["slope"] = iso.slope();
+            jIso["upperlimit"] = iso.upperlimit();
+            jThrType["isolation"][isoSet]["Parametrization"] += jIso;
+         }
+      }
+
+      if(thrType == "JET") {
+         auto & jetinfo = l1menu.thrExtraInfo().JET();
+         jThrType["ptMinToTopoSmallWindow"] = (int)jetinfo.ptMinToTopoSmallWindow();
+         jThrType["ptMinToTopoLargeWindow"] = (int)jetinfo.ptMinToTopoLargeWindow();
+      }
+
+      if(thrType == "XS") {
+         auto & xsinfo = l1menu.thrExtraInfo().XS();
+         jThrType["significance"]["xeMin"] = xsinfo.xeMin();
+         jThrType["significance"]["xeMax"] = xsinfo.xeMax();
+         jThrType["significance"]["teSqrtMin"] = xsinfo.teSqrtMin();
+         jThrType["significance"]["teSqrtMax"] = xsinfo.teSqrtMax();
+         jThrType["significance"]["xsSigmaScale"] = xsinfo.xsSigmaScale();
+         jThrType["significance"]["xsSigmaOffset"] = xsinfo.xsSigmaOffset();
+      }
+
+      if(thrType == "eEM") {
+         auto & eeminfo = l1menu.thrExtraInfo().eEM();
+         for( auto wp : {TrigConf::Isolation::WP::LOOSE, TrigConf::Isolation::WP::MEDIUM, TrigConf::Isolation::WP::TIGHT} ) {
+            auto wpstr = TrigConf::Isolation::wpToString(wp);
+            jThrType["workingPoints"][wpstr] = json::array_t({});
+            for(auto & iso : eeminfo.isolation(wp)) {
+               json jWPIso({});
+               jWPIso["reta"] = iso.value().reta_d();
+               jWPIso["rhad"] = iso.value().rhad_d();
+               jWPIso["wstot"] = iso.value().wstot_d();
+               jWPIso["maxEt"] = iso.value().maxEt();
+               jThrType["workingPoints"][wpstr] += jWPIso;
+            }
+         }
+      }
+
+      if(thrType == "eTAU") {
+         auto & eeminfo = l1menu.thrExtraInfo().eTAU();
+         for( auto wp : {TrigConf::Isolation::WP::LOOSE, TrigConf::Isolation::WP::MEDIUM, TrigConf::Isolation::WP::TIGHT} ) {
+            auto wpstr = TrigConf::Isolation::wpToString(wp);
+            jThrType["workingPoints"][wpstr] = json::array_t({});
+            for(auto & iso : eeminfo.isolation(wp)) {
+               json jWPIso({});
+               jWPIso["isolation"] = (int)iso.value().isolation_d();
+               jWPIso["maxEt"] = iso.value().maxEt();
+               jThrType["workingPoints"][wpstr] += jWPIso;
+            }
+         }
+      }
+
+      if(thrType == "jJ") {
+         auto & ei = l1menu.thrExtraInfo().jJ();
+         jThrType["ptMinToTopo"] = json::array_t({});
+         for(auto & x : ei.ptMinToTopoMeV() ) {
+            jThrType["ptMinToTopo"] += json({
+                  {"etamin",x.etaMin()},
+                  {"etamax",x.etaMax()},
+                  {"small",int(x.value().first/1000.)},
+                  {"large",int(x.value().second/1000.)}
+               });
+         }
+      }
+
+      std::vector<std::string> legacyCalo = {"EM", "JET", "TAU", "XE", "TE", "XS", "ZB", "R2TOPO"};
+      if( std::any_of(begin(legacyCalo), end(legacyCalo), [&thrType](const std::string &c) { return c==thrType; }) ) {
+         thresholds["legacyCalo"][thrType] = jThrType;
+      } else {
+         thresholds[thrType] = jThrType;
+      }
+   };
+
+
+   json boards{};
+   for( auto & bname : l1menu.boardNames() ) {
+      auto & bdef = l1menu.board(bname);
+      boards[bname] = json{ {"connectors", bdef.connectorNames()}, {"type", bdef.type()} };
+      if(bdef.legacy())
+         boards[bname]["legacy"] = true;
+   };
+
+
+   json connectors{};
+   for( auto & cname : l1menu.connectorNames() ) {
+      auto jConn = json{};
+      auto & cdef = l1menu.connector(cname);
+      jConn["type"] = cdef.type();
+      if(cdef.legacy())
+         jConn["legacy"] = true;
+      if(cdef.maxFpga() == 2) {
+         for(size_t fpga = 0; fpga<2; ++fpga) {
+            std::string fName = "fpga" + std::to_string(fpga);
+            for(size_t clock = 0; clock<2; ++clock) {
+               std::string cName = "clock" + std::to_string(clock);
+               jConn["triggerlines"][fName][cName] = json::array_t();
+               for(auto & tl : cdef.triggerLines(fpga, clock)) {
+                  jConn["triggerlines"][fName][cName] += json({ {"name", tl.name()}, {"nbits",tl.nbits()}, {"startbit", tl.startbit()} });
+               }
+            }
+         }
+      } else {
+         if(cdef.maxClock() == 2) {
+            // merger boards
+            for(size_t clock = 0; clock<cdef.maxClock(); ++clock) {
+               std::string cName = "clock" + std::to_string(clock);
+               jConn["triggerlines"][cName] = json::array_t();
+               for(auto & tl : cdef.triggerLines(0, clock)) {
+                  jConn["triggerlines"][cName] += json({ {"name", tl.name()}, {"nbits",tl.nbits()}, {"startbit", tl.startbit()} });
+               }
+            }            
+         } else {
+            jConn["triggerlines"] = json::array_t();
+            for(auto & tl : cdef.triggerLines()) {
+               jConn["triggerlines"] += json({ {"name", tl.name()}, {"nbits",tl.nbits()}, {"startbit", tl.startbit()} });
+            }
+         }
+      }
+      connectors[cname] = jConn;
+   }
+
+   json ctp({});
+   {
+      for(size_t slot=7; slot<=9; ++slot) {
+         std::string sName = "slot" + std::to_string(slot);
+         ctp["inputs"]["ctpin"][sName] = json({});
+         for(size_t conn=0; conn<=3; ++conn) {
+            ctp["inputs"]["ctpin"][sName]["connector" + std::to_string(conn)] = l1menu.ctp().ctpin(slot,conn);
+         }
+      }
+      for(size_t conn=0; conn<3; ++conn) {
+         if(l1menu.ctp().electrical(conn)=="")
+            continue;
+         ctp["inputs"]["electrical"]["connector" + std::to_string(conn)] = l1menu.ctp().electrical(conn);
+      }
+      for(size_t conn=0; conn<12; ++conn) {
+         if(l1menu.ctp().optical(conn)=="")
+            continue;
+         ctp["inputs"]["optical"]["connector" + std::to_string(conn)] = l1menu.ctp().optical(conn);
+      }
+      auto ctpmon = json({});
+      for(auto & mon : l1menu.ctp().ctpMon()) {
+         ctpmon[mon.first] = json({{"multiplicity",mon.second.first},{"thr",mon.second.second}});
+      }
+      ctp["monitoring"] = json({{"ctpmon",ctpmon}});
+   }
+
+   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"} ) {
+         for(auto & algName : l1menu.topoAlgorithmNames(topoCat)) {
+            json jalg = json::object_t({});
+            auto & alg = l1menu.algorithm(algName,topoCat);
+            jalg["algId"]     = alg.algId();
+            jalg["klass"]     = alg.klass();
+            // input
+            if(alg.type()==L1TopoAlgorithm::AlgorithmType::MULTIPLICITY) {
+               if(alg.inputs().size()>0) {
+                  jalg["input"] = alg.inputs()[0];
+               } else {
+                  jalg["input"] = nullptr;
+               }
+            } else if(alg.type()==L1TopoAlgorithm::AlgorithmType::SORTING) {
+               jalg["input"] = alg.inputs()[0];
+            } else {
+               jalg["input"] = alg.inputs();
+            }
+            // output
+            if( alg.type() == L1TopoAlgorithm::AlgorithmType::DECISION ) {
+               jalg["output"] = alg.outputs();
+            } else {
+               jalg["output"] = alg.outputs()[0];
+            }
+            // generic parameters
+            if(topoCat == "MULTTOPO") {
+               jalg["nbits"] = alg.getAttribute<unsigned int>("nbits");
+               jalg["threshold"] = alg.getAttribute("threshold");
+            } else {
+               auto ds = alg.generics();
+               for(auto & gpname : ds.getKeys()) {
+                  auto gp = ds.getObject(gpname);
+                  {
+                     if(gp.hasAttribute("position")) {
+                        jalg["fixedParameters"]["generics"][gpname]["position"] = gp.getAttribute<unsigned int>("position");
+                     }
+                     std::string pval = alg.genericParameter(gpname);
+                     try{
+                        int pvali = std::stoi(pval);
+                        jalg["fixedParameters"]["generics"][gpname]["value"] = pvali;
+                     } catch(std::invalid_argument &) {
+                        jalg["fixedParameters"]["generics"][gpname]["value"] = pval;
+                     }
+                  }
+               }
+            }
+            // variable parameters
+            if(topoCat != "MULTTOPO") {
+               jalg["variableParameters"] = json::array_t({});
+               for(const L1TopoAlgorithm::VariableParameter & vpar : alg.parameters()) {
+                  json jVPar({});
+                  jVPar["name"] = vpar.name();
+                  jVPar["value"] = vpar.value();
+                  if(auto sel = vpar.selection_optional()) { jVPar["selection"] = *sel; }
+                  jalg["variableParameters"] += jVPar;
+               }
+            }
+            jtopo[topoCat][algTypeNames[alg.type()]][algName] = jalg;
+         }
+      }
+   }
+
+   json j({});
+   j["filetype"] = "l1menu";
+   j["name"] = l1menu.name();
+   j["items"] = items;
+   j["thresholds"] = thresholds;
+   j["topoAlgorithms"] = jtopo;
+   j["boards"] = boards;
+   j["connectors"] = connectors;
+   j["ctp"] = ctp;
+
+
+   std::ofstream outfile(filename);
+   outfile << std::setw(4) << j << std::endl;
+
+   TRG_MSG_INFO("Saved file " << filename);
+   return true;
+}
diff --git a/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx b/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx
index 754fc90653503dc8e7b7d1d1e283f71e0239c3b6..742f8d80632fd53b06e819dc6e5a12a470c52117 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx
@@ -114,7 +114,7 @@ testL1Menu_Connectors(const TrigConf::L1Menu & l1menu) {
    cout << "L1 menu has " << l1menu.connectorNames().size() << " connectors configured" << endl;
    for( const string & connName : l1menu.connectorNames() ) {
       auto & conn = l1menu.connector(connName);
-      cout << "Connector " << connName << (conn.isLegacy() ? " (legacy)": "") << " has " << conn.size() << " trigger lines configured:" << endl;
+      cout << "Connector " << connName << (conn.legacy() ? " (legacy)": "") << " has " << conn.size() << " trigger lines configured:" << endl;
       if( connName == "MuCTPiOpt0" ) {
          for( auto & tl : conn.triggerLines() ) {
             cout << "   Triggerline " << tl.name() << " bits=["  << tl.startbit() << ".." << tl.endbit() << "] is a muon threshold " << endl;            
@@ -125,18 +125,18 @@ testL1Menu_Connectors(const TrigConf::L1Menu & l1menu) {
                cout << "   Triggerline " << tl.name() << " (clock " << clock << ", bit "  << tl.startbit() << ") is an ALFA threshold " << endl;
             }
          }
-      } else if( conn.type() == TrigConf::L1Connector::ConnectorType::CTPIN ) {
+      } else if( conn.connectorType() == TrigConf::L1Connector::ConnectorType::CTPIN ) {
          for( auto & tl : conn.triggerLines() ) {
             cout << "   Triggerline " << tl.name() << " bits=["  << tl.startbit() << ".." << tl.endbit() << "] is a legacy threshold " << endl;            
          }
-      } else if( conn.type() == TrigConf::L1Connector::ConnectorType::OPTICAL ) {
+      } else if( conn.connectorType() == TrigConf::L1Connector::ConnectorType::OPTICAL ) {
          for( auto & tl : conn.triggerLines() ) {
             const string & tlName = tl.name();
             auto & topoAlg = l1menu.algorithmFromTriggerline(tlName);
             cout << "   Triggerline " << tlName << " bits=["  << tl.startbit() << ".." << tl.endbit() 
                  << "] is produced by topo algorithm " << topoAlg.name() << endl;
          }
-      } else if( conn.type() == TrigConf::L1Connector::ConnectorType::ELECTRICAL ) {
+      } else if( conn.connectorType() == TrigConf::L1Connector::ConnectorType::ELECTRICAL ) {
          for( size_t fpga : { 0, 1 } ) {
             for( size_t clock : { 0, 1 } ) {
                for( auto & tl : conn.triggerLines(fpga, clock) ) {
@@ -374,19 +374,19 @@ testL1Menu_Extrainfo(const TrigConf::L1Menu & l1menu)
       for(auto & iso : ex.isolation(TrigConf::Isolation::WP::LOOSE)) {
          cout << "      range etaMin=" << iso.etaMin() << ", etaMax=" << iso.etaMax() 
               << ", priority=" << iso.priority() << ", symmetric=" << (iso.symmetric() ? "yes" : "no")
-              << ", isolation=" << iso.value() << endl;
+              << ", isolation=" << iso.value().isolation() << endl;
       }
       cout << "    working point Medium" << endl;
       for(auto & iso : ex.isolation(TrigConf::Isolation::WP::MEDIUM)) {
          cout << "      range etaMin=" << iso.etaMin() << ", etaMax=" << iso.etaMax() 
               << ", priority=" << iso.priority() << ", symmetric=" << (iso.symmetric() ? "yes" : "no")
-              << ", isolation=" << iso.value() << endl;
+              << ", isolation=" << iso.value().isolation() << endl;
       }
       cout << "    working point Tight" << endl;
       for(auto & iso : ex.isolation(TrigConf::Isolation::WP::TIGHT)) {
          cout << "      range etaMin=" << iso.etaMin() << ", etaMax=" << iso.etaMax() 
               << ", priority=" << iso.priority() << ", symmetric=" << (iso.symmetric() ? "yes" : "no")
-              << ", isolation=" << iso.value() << endl;
+              << ", isolation=" << iso.value().isolation() << endl;
       }
    }
 
@@ -399,7 +399,7 @@ testL1Menu_Extrainfo(const TrigConf::L1Menu & l1menu)
    cout << endl;
    if( const auto & list = exMU.exclusionListNames(); std::find(list.begin(), list.end(), "rpcFeet")!=list.end() ) {
       cout << "    exclusionList 'rpcFeet'" << endl;
-      for(auto & x : exMU.exlusionList("rpcFeet")) {
+      for(auto & x : exMU.exclusionList("rpcFeet")) {
          cout << "     sector " << x.first << ": ";
          for( auto roi : x.second ) cout << roi << " ";
          cout << endl;
diff --git a/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx b/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
index e8eca7e0ef0c2f7f22bc4366b69dd39e528527b7..c4eb23b808104d25d50dbd88c86baeec17456ac2 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
@@ -6,6 +6,7 @@
 #include <vector>
 
 #include "TrigConfIO/JsonFileLoader.h"
+#include "TrigConfIO/JsonFileWriter.h"
 #include "TrigConfIO/TrigDBMenuLoader.h"
 #include "TrigConfIO/TrigDBJobOptionsLoader.h"
 #include "TrigConfIO/TrigDBL1PrescalesSetLoader.h"
@@ -24,7 +25,7 @@ public:
    ~Config(){}
    Config(){}
 
-   std::vector<std::string> knownParameters { "file", "f", "smk", "l1psk", "hltpsk", "bgsk", "db", "write", "w", "help", "h", "detail", "d" };
+   std::vector<std::string> knownParameters { "file", "f", "smk", "l1psk", "hltpsk", "bgsk", "db", "write", "w", "Write", "W", "help", "h", "detail", "d" };
 
    // parameters
    // input
@@ -37,6 +38,7 @@ public:
 
    // output
    bool         write { false }; // flag to enable writing
+   bool         writeFromDataStructure { false }; // flag to enable writing of the L1Menu structure (if available)
    std::string  base { "" };
 
    // other
@@ -68,6 +70,7 @@ void Config::usage() {
   cout << "  --db                  dbalias                       ... dbalias (default " << dbalias << ") \n";
   cout << "[Output options]\n";
   cout << "  -w|--write            [base]                        ... to write out json files, e.g. L1menu[_<base>].json. base is optional.\n";
+  cout << "  -W|--Write            [base]                        ... to write out json files from the internal structure (only for L1Menu), e.g. L1menu[_<base>].json. base is optional.\n";
   cout << "[Other options]\n";
   cout << "  -h|--help                                           ... this help\n";
   cout << "  -d|--detail                                         ... prints detailed job options\n";
@@ -104,6 +107,7 @@ Config::parseProgramOptions(int argc, char* argv[]) {
          if(paramName == "h" || paramName == "help" ) { help = true; continue; }
          if(paramName == "d" || paramName == "detail" ) { detail = true; continue; }
          if(paramName == "w" || paramName == "write" ) { write = true; }
+         if(paramName == "W" || paramName == "Write" ) { writeFromDataStructure = true; }
          currentParameter = paramName;
          continue;
       }
@@ -137,7 +141,7 @@ Config::parseProgramOptions(int argc, char* argv[]) {
       }
 
       // output
-      if(currentParameter == "write" || currentParameter == "w") {
+      if(currentParameter == "write" || currentParameter == "w" || currentParameter == "Write" || currentParameter == "W") {
          base = currentWord;
          continue; 
       }
@@ -158,16 +162,24 @@ Config::parseProgramOptions(int argc, char* argv[]) {
 namespace {
    bool
    writeJsonFile(const TrigConf::DataStructure & ds, const std::string & kind, const Config & cfg) {
-      if( ! cfg.write )
-         return true;
-
-      std::string filename = kind;
-      if ( cfg.base != "" ) {
-         filename += "_" + cfg.base;
+      if( cfg.write ) {
+         std::string filename = kind;
+         if ( cfg.base != "" ) {
+            filename += "_" + cfg.base;
+         }
+         filename += ".json";
+         TrigConf::JsonFileLoader fileLoader;
+         return fileLoader.saveFile(filename, ds); 
+      } else if ( cfg.writeFromDataStructure && kind=="L1Menu" ) {
+         std::string filename = kind;
+         if ( cfg.base != "" ) {
+            filename += "_" + cfg.base;
+         }
+         filename += ".fromDS.json";
+         TrigConf::JsonFileWriter fileWriter;
+         const auto & l1menu = dynamic_cast<const TrigConf::L1Menu &>(ds);
+         return fileWriter.writeJsonFile(filename, l1menu); 
       }
-      filename += ".json";
-      TrigConf::JsonFileLoader fileLoader;
-      fileLoader.saveFile(filename, ds);
       return true;
    }
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py
index 607f415ff1573930b07c948d653d04775ece8c84..c02a03c2a082c8f36697f897660f4479373da3b6 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py
@@ -230,8 +230,8 @@ class LegacyThreshold( Threshold ):
                     ("isobits", thrV.isobits),
                     ("etamin", thrV.etamin),
                     ("etamax", thrV.etamax),
-                    ("phimin", thrV.phimax),
-                    ("phimax", thrV.phimin),
+                    ("phimin", thrV.phimin),
+                    ("phimax", thrV.phimax),
                     ("priority", thrV.priority)
                 ]) )
         elif self.ttype == ThrType.TAU:
@@ -244,8 +244,8 @@ class LegacyThreshold( Threshold ):
                     ("value", thrV.value),
                     ("etamin", thrV.etamin),
                     ("etamax", thrV.etamax),
-                    ("phimin", thrV.phimax),
-                    ("phimax", thrV.phimin),
+                    ("phimin", thrV.phimin),
+                    ("phimax", thrV.phimax),
                     ("window", thrV.window),
                     ("priority", thrV.priority)
                 ]) )
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDef.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDef.py
index f87b3af90d3f9144bfbffa541a5b853ca90db4df..093f101444e5db3fd481b3e0ec36dccecf543e11 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDef.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDef.py
@@ -87,8 +87,7 @@ class TopoAlgoDef:
         alg.addvariable('IsoMask', 0)
         alg.addvariable('MinEta', 0)
         alg.addvariable('MaxEta', _etamax) 
-        alg.addgeneric('DoIsoCut', '0')
-        #alg.addgeneric('DoEtaCut', '1')
+        alg.addgeneric('DoIsoCut', 0)
         tm.registerTopoAlgo(alg) 
 
         
@@ -99,8 +98,7 @@ class TopoAlgoDef:
         alg.addvariable('IsoMask', 3) 
         alg.addvariable('MinEta', 0)
         alg.addvariable('MaxEta', _etamax)
-        alg.addgeneric('DoIsoCut', '1')
-        #alg.addgeneric('DoEtaCut', '1')
+        alg.addgeneric('DoIsoCut', 1)
         tm.registerTopoAlgo(alg)
 
 
@@ -111,8 +109,7 @@ class TopoAlgoDef:
         alg.addvariable('IsoMask', 2) 
         alg.addvariable('MinEta', 0)
         alg.addvariable('MaxEta', _etamax)
-        alg.addgeneric('DoIsoCut', '1')
-        #alg.addgeneric('DoEtaCut', '1')
+        alg.addgeneric('DoIsoCut', 1)
         tm.registerTopoAlgo(alg)
 
         
@@ -757,8 +754,8 @@ class TopoAlgoDef:
                 setattr (d, k, x[k])
 
             toponame = "%iDETA%i-%s%s%s%s-%s%s%s%s"  % (d.minDeta, d.maxDeta,
-                                                        d.otype, str(d.ocut1) if d.ocut1 > 0 else "", d.olist, str(d.nleading1) if d.olist=="s" else "",
-                                                        d.otype, str(d.ocut2) if d.ocut2 > 0 else "", d.olist, str(d.nleading2) if d.olist=="s" else "")
+                                                        d.otype, d.ocut1 if d.ocut1 > 0 else "", d.olist, d.nleading1 if d.olist=="s" else "",
+                                                        d.otype, d.ocut2 if d.ocut2 > 0 else "", d.olist, d.nleading2 if d.olist=="s" else "")
             
             log.debug("Define %s", toponame)
             inputList = d.otype + d.olist
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDefLegacy.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDefLegacy.py
index 3bb0583614b3b8e861c2e27ab99496474f891dce..e4b95f0d621739707109dc92e5b49bb65942223c 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDefLegacy.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/TopoAlgoDefLegacy.py
@@ -79,8 +79,7 @@ class TopoAlgoDefLegacy:
         alg.addvariable('IsoMask', 0)
         alg.addvariable('MinEta', 0)
         alg.addvariable('MaxEta', _etamax) 
-        alg.addgeneric('DoIsoCut', '0')
-        #alg.addgeneric('DoEtaCut', '1')
+        alg.addgeneric('DoIsoCut', 0)
         tm.registerTopoAlgo(alg) 
 
         
@@ -91,8 +90,7 @@ class TopoAlgoDefLegacy:
         alg.addvariable('IsoMask', 3) 
         alg.addvariable('MinEta', 0)
         alg.addvariable('MaxEta', _etamax)
-        alg.addgeneric('DoIsoCut', '1')
-        #alg.addgeneric('DoEtaCut', '1')
+        alg.addgeneric('DoIsoCut', 1)
         tm.registerTopoAlgo(alg)
 
 
@@ -103,8 +101,7 @@ class TopoAlgoDefLegacy:
         alg.addvariable('IsoMask', 2) 
         alg.addvariable('MinEta', 0)
         alg.addvariable('MaxEta', _etamax)
-        alg.addgeneric('DoIsoCut', '1')
-        #alg.addgeneric('DoEtaCut', '1')
+        alg.addgeneric('DoIsoCut', 1)
         tm.registerTopoAlgo(alg)
 
         
@@ -595,7 +592,7 @@ class TopoAlgoDefLegacy:
             alg = AlgConf.TransverseMassInclusive1( name = toponame, inputs = [ inputList, 'XE'], outputs = [ toponame ], algoId = currentAlgoId )
             currentAlgoId += 1
             alg.addgeneric('InputWidth', HW.OutputWidthSortEM)
-            alg.addgeneric('MaxTob', str(d.nleading))
+            alg.addgeneric('MaxTob', d.nleading)
             alg.addgeneric('NumResultBits', 1)
             alg.addvariable('MinET1', str(d.ocut))
             alg.addvariable('MinET2', 0)
@@ -1177,7 +1174,7 @@ class TopoAlgoDefLegacy:
             alg = AlgConf.TransverseMassInclusive1( name = toponame, inputs = [ inputList, 'XE'], outputs = [ toponame ], algoId = currentAlgoId )
             currentAlgoId += 1
             alg.addgeneric('InputWidth', HW.OutputWidthSortEM)
-            alg.addgeneric('MaxTob', str(d.nleading))
+            alg.addgeneric('MaxTob', d.nleading)
             alg.addgeneric('NumResultBits', 1)
             alg.addvariable('MinET1', str(d.ocut))
             alg.addvariable('MinET2', 0)