diff --git a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Board.h b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Board.h
index 1426611bfb004b08af1e43107f61ffa84a1dbb53..a96bdd6ae6876550bd03f62dc887ccac6bdd08ed 100644
--- a/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Board.h
+++ b/Trigger/TrigConfiguration/TrigConfData/TrigConfData/L1Board.h
@@ -23,7 +23,7 @@ namespace TrigConf {
    class L1Board final : public DataStructure {
    public:
 
-      enum class BoardType { CTPIN, TOPO, MUCTPI };
+      enum class BoardType { CTPIN, TOPO, MUCTPI, MERGER };
 
       /** Constructor */
       L1Board();
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Board.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Board.cxx
index 059497974f3a80a74e7837a2d06d99353b803f08..94fff7c58bf7f96c777ff55ef1162581e50b1d86 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Board.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Board.cxx
@@ -43,6 +43,8 @@ TrigConf::L1Board::update()
       m_boardType = BoardType::CTPIN;
    } else if( boardType == "TOPO" ) {
       m_boardType = BoardType::TOPO;
+   } else if( boardType == "MERGER" ) {
+      m_boardType = BoardType::MERGER;
    } else {
       throw std::runtime_error("Unknown board type " + boardType);
    }
@@ -76,6 +78,8 @@ TrigConf::L1Board::type() const
       return "MUCTPI";
    case BoardType::TOPO:
       return "TOPO";
+   case BoardType::MERGER:
+      return "MERGER";
    }
    return "";
 }
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx
index 6a016b7a86c9f6e516b004e5d25c6bd8b9bb63f1..3b206408e3d7953a6f366b24ab350321424b8d3e 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Connector.cxx
@@ -42,16 +42,20 @@ TrigConf::L1Connector::update()
    }
 
    // triggerlines
+   bool hasMultipleFPGAs = ! hasChild("triggerlines.clock0"); // connector from merger board (no fpga)
    if(m_type == ConnectorType::ELECTRICAL) {
-      m_maxFpga = m_maxClock = 2;
+      m_maxClock = 2;
+      m_maxFpga = hasMultipleFPGAs ? 2 : 1;
    }
 
    for( size_t fpga = 0; fpga < m_maxFpga; ++fpga ) {
       for( size_t clock = 0; clock < m_maxClock; ++clock ) {
          std::string path = "triggerlines";
          if( m_type == ConnectorType::ELECTRICAL ) {
-            path += ".fpga";
-            path += std::to_string(fpga);
+            if(hasMultipleFPGAs) {
+               path += ".fpga";
+               path += std::to_string(fpga);
+            }
             path += ".clock";
             path += std::to_string(clock);            
          }
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
index 014f8a7a368d23fa031146e95549a47a4567514e..52d4542057ad154596517f8b1916042ab9037dec 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1Menu.cxx
@@ -322,11 +322,16 @@ TrigConf::L1Menu::algorithmFromTriggerline(const std::string & triggerlineName)
       category = triggerlineName.substr(0,pos);
       outputName = triggerlineName.substr(pos+1);
    }
+   const static std::vector<string> topoTypes {"TOPO", "R2TOPO", "MULTTOPO", "MUTOPO"};
+   if( std::none_of(cbegin(topoTypes), cend(topoTypes), [&category](const std::string & str){return str==category;}) ) {
+      std::string msg = "L1Menu::algorithmFromTriggerLine(" + triggerlineName + "): triggerline " + triggerlineName + " is not produced by a topo algorithm.";
+      throw std::runtime_error(msg);
+   }
    try {
       return * m_algorithmsByOutput.at(category).at(outputName);
    }
    catch(std::exception & ex) {
-      std::cerr << "No output " << outputName << " defined by any algorithm of category " << category << " in the L1 menu. (It was asked for " << triggerlineName << ")" << std::endl;
+      std::cerr << "L1Menu::algorithmFromTriggerLine(): No output " << outputName << " defined by any algorithm of category " << category << " in the L1 menu. (It was asked for " << triggerlineName << ")" << std::endl;
       throw;
    }
 }
diff --git a/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx b/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx
index ce80fe7f5c983f1e60fe54517a415e22457a2662..9afe50ca08dd85e521f5272c7dc8cd8c5431c77a 100644
--- a/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx
+++ b/Trigger/TrigConfiguration/TrigConfData/src/L1ThresholdBase.cxx
@@ -64,7 +64,7 @@ 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" };
+   static const std::string noSpecialImp[] = { "JET", "XS", "jJ", "gXE", "jXE", "TOPO", "MULTTOPO", "MUTOPO", "R2TOPO", "ZB", "ALFA" };
    bool useBaseClass = std::find(std::begin(noSpecialImp), std::end(noSpecialImp),type) !=
      std::end(noSpecialImp);
 
diff --git a/Trigger/TrigConfiguration/TrigConfIO/test/testAllKeysReading.sh b/Trigger/TrigConfiguration/TrigConfIO/test/testAllKeysReading.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8d5a67f2f062b3cbc9bd13b9af650723717e0335
--- /dev/null
+++ b/Trigger/TrigConfiguration/TrigConfIO/test/testAllKeysReading.sh
@@ -0,0 +1,12 @@
+smk=1
+while ( [[ smk -le 1 ]] )
+do
+    cmd="TestTriggerMenuAccess --smk ${smk}"
+    echo "executing $cmd"
+    $cmd 
+    #2>1 > /dev/null
+    STATUS=$status
+    
+    let smk++
+done
+
diff --git a/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx b/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx
index 547375efe06246e2bc312fd5f38a7f08c3ded9b9..1c6b5f943f6822539d1f3b9aa8fac5f6fec1520c 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/utils/TestTriggerMenuAccess.cxx
@@ -7,6 +7,7 @@
 #include <iomanip>
 
 #include "TrigConfIO/JsonFileLoader.h"
+#include "TrigConfIO/TrigDBMenuLoader.h"
 #include "TrigConfData/HLTMenu.h"
 #include "TrigConfData/L1Menu.h"
 #include "TrigConfData/L1Threshold.h"
@@ -98,7 +99,7 @@ testL1Menu_Boards(const TrigConf::L1Menu & l1menu) {
    cout << "Board " << boardName << " of type " << board.type() << " has " << board.size() << " connectors configured: ";
    for( auto & connName : board.connectorNames() ) { cout << connName << " "; }
    cout << endl;
-   return true;   
+   return true;
 }
 
 
@@ -118,6 +119,12 @@ testL1Menu_Connectors(const TrigConf::L1Menu & l1menu) {
          for( auto & tl : conn.triggerLines() ) {
             cout << "   Triggerline " << tl.name() << " bits=["  << tl.startbit() << ".." << tl.endbit() << "] is a muon threshold " << endl;            
          }
+      } else if( connName == "AlfaCtpin" ) {
+         for( size_t clock : { 0, 1 } ) {
+            for( auto & tl : conn.triggerLines(0, clock) ) {
+               cout << "   Triggerline " << tl.name() << " (clock " << clock << ", bit "  << tl.startbit() << ") is an ALFA threshold " << endl;
+            }
+         }
       } else if( conn.type() == TrigConf::L1Connector::ConnectorType::CTPIN ) {
          for( auto & tl : conn.triggerLines() ) {
             cout << "   Triggerline " << tl.name() << " bits=["  << tl.startbit() << ".." << tl.endbit() << "] is a legacy threshold " << endl;            
@@ -130,8 +137,9 @@ testL1Menu_Connectors(const TrigConf::L1Menu & l1menu) {
                  << "] is produced by topo algorithm " << topoAlg.name() << endl;
          }
       } else if( conn.type() == TrigConf::L1Connector::ConnectorType::ELECTRICAL ) {
-         for( size_t fpga : { 0 ,1 } ) {
-            for( size_t clock : { 0 ,1 } ) {
+         cout << "JOERG 3" << endl;
+         for( size_t fpga : { 0, 1 } ) {
+            for( size_t clock : { 0, 1 } ) {
                for( auto & tl : conn.triggerLines(fpga, clock) ) {
                   const string & tlName = tl.name();
                   auto & topoAlg = l1menu.algorithmFromTriggerline(tlName);
@@ -403,7 +411,7 @@ testL1Menu_Extrainfo(const TrigConf::L1Menu & l1menu)
 
 
 bool
-testL1Menu(const string & filename, bool printdetail = false)
+testL1Menu(const TrigConf::L1Menu & l1menu, bool printdetail = false)
 {
    cout << endl
         << "==========================" << endl
@@ -414,9 +422,6 @@ testL1Menu(const string & filename, bool printdetail = false)
 
    cout << "Printing detail " << (printdetail ? "yes" : "no") << endl;
    section("Menu loading");
-   TrigConf::L1Menu l1menu;
-   TrigConf::JsonFileLoader fileLoader;
-   fileLoader.loadFile( filename, l1menu);
    cout << "Loaded the L1 menu " << l1menu.name() << endl;
 
    bool result = true;
@@ -431,7 +436,7 @@ testL1Menu(const string & filename, bool printdetail = false)
 
 
 
-bool testHLTMenu(const string & filename) {
+bool testHLTMenu(const TrigConf::HLTMenu & hltmenu) {
 
    cout << "===========================" << endl
         << "=====                 =====" << endl
@@ -439,9 +444,6 @@ bool testHLTMenu(const string & filename) {
         << "=====                 =====" << endl
         << "===========================" << endl << endl;
 
-   TrigConf::HLTMenu hltmenu;
-   TrigConf::JsonFileLoader fileLoader;
-   fileLoader.loadFile( filename, hltmenu);
    cout << "Loaded the HLT menu " << hltmenu.name() << endl;
    cout << "Menu has " << hltmenu.size() << " chains, going to print the first 3." << endl;
    int np = 3;
@@ -496,9 +498,83 @@ bool testHLTMenu(const string & filename) {
    Main function just to get the filename and which type
  */
 
+void usage() {
+
+  cout << "The program needs to be run with the following specifications:\n\n";
+  cout << "TestTriggerMenuAccess <options>\n";
+  cout << "\n";
+  cout << "[Input options]\n";
+  cout << "  -f|--file             file1        ... input json file to test\n";
+  cout << "  --smk                 smk          ... smk \n";
+  cout << "  --db                  dbalias      ... dbalias (default TRIGGERDBDEV1) \n";
+  cout << "[Other options]\n";
+  cout << "  -h|--help                                           ... this help\n";
+  cout << "\n";
+  cout << "If no input is specified, the default LS2_v1 menu file will be taken from the release\n\n";
+}
+
 int main(int argc, char** argv) {
-   string filename(""); 
-   if(argc==1) {
+   bool help { false };
+   string filename{""};
+   unsigned int smk{0};
+   std::string  dbalias {"TRIGGERDBDEV1"};
+   std::vector<std::string> knownParameters { "file", "f", "smk", "db", "help", "h" };
+
+   std::string currentParameter("");
+   std::string listofUnknownParameters = "";
+   std::string listofUnknownArguments = "";
+   for(int i=1; i<argc; i++) {
+
+      std::string currentWord(argv[i]);
+      bool isParam = currentWord[0]=='-'; // string starts with a '-', so it is a parameter name
+
+      // get the parameter name
+      int firstChar = currentWord.find_first_not_of('-');
+      string paramName = currentWord.substr(firstChar);
+
+      // check if the parameter is known
+      if ( isParam && std::find(knownParameters.begin(), knownParameters.end(), paramName) == knownParameters.end() ) {
+         listofUnknownParameters += " " + currentWord;
+         continue;
+      }
+
+      if(isParam) {
+         currentParameter = "";
+         // check the boolean parameters
+         if(paramName == "h" || paramName == "help" ) { help = true; continue; }
+         currentParameter = paramName;
+         continue;
+      }
+
+      // inputs
+      if(currentParameter == "file" || currentParameter == "f") {
+         filename = currentWord;
+         continue;
+      }
+      if(currentParameter == "smk") {
+         smk = stoul(currentWord);
+         continue;
+      }
+      if(currentParameter == "db") {
+         dbalias = currentWord;
+         continue;
+      }
+      listofUnknownArguments += " " + currentWord;
+   }
+
+   if ( not listofUnknownParameters.empty() ) {
+      cerr << "Unknown parameter(s):" << listofUnknownParameters << endl;
+      usage();
+      return 1;
+   }
+
+   if ( not listofUnknownArguments.empty() ) {
+      cerr << "Unknown argument(s):" << listofUnknownArguments << endl;
+      usage();
+      return 1;
+   }
+
+   if( filename.empty() && smk==0 ) {
       // no filename specified, going to take the L1 menu from the release
       const char* env_AV = std::getenv("AtlasVersion");
       const char* env_xmlpath = std::getenv("XMLPATH");
@@ -516,32 +592,41 @@ int main(int argc, char** argv) {
          }
       }
       if(filename == "") {
-         cout << "No filename specified and no default L1 menu file found in the release" << endl;
-         cout << "Please use " << argv[0] << " <filename.json>" << endl;
-         return 1;
-      }
-   } else if (argc==2) {
-      struct stat buffer;
-      if (stat (argv[1], &buffer) == 0) {
-         filename = string(argv[1]);
-      }
-      if(filename == "") {
-         cout << "Can't find file " << argv[1] << endl;
+         cout << "No filename or smk specified and no default L1 menu file found in the release" << endl;
+         usage();
          return 1;
       }
    }
 
-   // file loader
-   TrigConf::JsonFileLoader fileLoader;
-   string filetype = fileLoader.getFileType( filename );
+   if(help) {
+      usage();
+      return 0;
+   }
+
    bool success(false);
-   if(filetype == "l1menu") {
-      success = testL1Menu(filename);
-   } else if(filetype == "hltmenu") {
-      success = testHLTMenu(filename);
+   if(smk!=0) {
+      // load from db
+      TrigConf::L1Menu l1menu;
+      TrigConf::TrigDBMenuLoader dbLoader(dbalias);
+      dbLoader.loadL1Menu( smk, l1menu);
+      success = testL1Menu(l1menu);
    } else {
-      cout << "File " << filename << " is neither an L1 or an HLT menu json file" << endl;
-   }   
+      // load from file
+      TrigConf::JsonFileLoader fileLoader;
+      string filetype = fileLoader.getFileType( filename );
+      if(filetype == "l1menu") {
+         TrigConf::L1Menu l1menu;
+         fileLoader.loadFile( filename, l1menu);
+         success = testL1Menu(l1menu);
+      } else if(filetype == "hltmenu") {
+         TrigConf::HLTMenu hltmenu;
+         fileLoader.loadFile( filename, hltmenu);
+         success = testHLTMenu(hltmenu);
+      } else {
+         cout << "File " << filename << " is neither an L1 or an HLT menu json file" << endl;
+      }
+   }
+
    cout << "Finished " << (success ? "successfully" : "with failures") << endl;
    return success ? 0 : 1;
 }
diff --git a/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx b/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
index a7a80c4e96de03b2e8945ae4cffa1619e3ed126a..20e8be13a51551f27a2f8b69d0b7b678f3d6a550 100644
--- a/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
+++ b/Trigger/TrigConfiguration/TrigConfIO/utils/TriggerMenuRW.cxx
@@ -33,7 +33,7 @@ public:
    unsigned int l1psk { 0 };
    unsigned int hltpsk { 0 };
    unsigned int bgsk { 0 };
-   std::string  dbalias { "TRIGGERDBDEV2" };
+   std::string  dbalias { "TRIGGERDBDEV1" };
 
    // output
    bool         write { false }; // flag to enable writing
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/CTP.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/CTP.py
index 680ce6e150c2c7897488279851a36a7c0444143c..b97fbb2548715f836236062b536b51996dbe06fd 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/CTP.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/CTP.py
@@ -17,8 +17,7 @@ class CTP(object):
         self.random          = Random( names = ['Random0', 'Random1', 'Random2', 'Random3'], cuts = [1, 1, 1, 1] )
         self.bunchGroupSet   = BunchGroupSet()
         self.counters        = MenuMonCountersCollection()   # monitoring counters in the menu
-        
-        
+
     def setBunchGroupSetName(self, name):
         self.bunchGroupSet.name = name
         return self.bunchGroupSet
@@ -40,6 +39,19 @@ class CTP(object):
         MonitorDef.applyItemCounter( menuItems )
         pass
 
+    def checkConnectorAvailability(self, availableConnectors, menuToLoad):
+        inputConnectorList = []
+        inputConnectorList += self.inputConnectors["optical"].values()
+        inputConnectorList += self.inputConnectors["electrical"].values()
+        inputConnectorList += self.inputConnectors["ctpin"]["slot7"].values()
+        inputConnectorList += self.inputConnectors["ctpin"]["slot8"].values()
+        inputConnectorList += self.inputConnectors["ctpin"]["slot9"].values()
+        for connName in inputConnectorList:
+            if connName != '' and connName not in availableConnectors:
+                msg = "Connector '%s' requested in L1/Config/CTPConfig.py not defined as menu input. Please add it to L1/Menu/Menu_%s_inputs.py" % (connName, menuToLoad)
+                log.error(msg)
+                raise RuntimeError(msg)
+
     def json(self):
         confObj = odict()
         confObj["inputs"] = self.inputConnectors
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Connectors.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Connectors.py
index 04c47d93a27398f37dc0c611f12bb20c98fb5195..29ad08861f65dcababcad1067955833a2f00aea2 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Connectors.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Connectors.py
@@ -35,6 +35,7 @@ class CType(Enum):
 class CFormat(Enum):
     MULT = (1, 'multiplicity')
     TOPO = (2, 'topological')
+    SIMPLE = (3, 'simple')
     def __init__(self, _, cformat ):
         self.cformat = cformat
 
@@ -44,6 +45,8 @@ class CFormat(Enum):
             return CFormat.MULT
         elif label == 'topological':
             return CFormat.TOPO
+        elif label == 'simple':
+            return CFormat.SIMPLE
         else:
             raise NotImplementedError
 
@@ -58,6 +61,9 @@ class MenuConnectorsCollection(object):
     def __iter__(self):
         return iter(self.connectors.values())
 
+    def __contains__(self, name):
+        return name in self.connectors
+
     def addConnector(self, connDef):
         name, cformat, ctype, legacy, boardName = map(connDef.__getitem__,["name", "format", "type", "legacy", "board"])
 
@@ -66,48 +72,11 @@ class MenuConnectorsCollection(object):
 
         log.debug("Adding connector %s, format %s, legacy set to %s, and connType %s", name, cformat, legacy, ctype)
         if CType.from_str(ctype) is CType.ELEC:
-            newConnector = ElectricalConnector(name, cformat, legacy)
+            newConnector = ElectricalConnector(name, cformat, legacy, connDef)
         else:
-            newConnector = Connector(name, cformat, ctype, legacy)
+            newConnector = OpticalConnector(name, cformat, ctype, legacy, connDef)
         self.connectors[name] = newConnector
 
-        
-        # treat differently depending on the "format", which can be: 'topological' or 'multiplicity'        
-        if connDef["format"] == 'multiplicity':
-            # multiplicity connectors contain all the triggerlines in a flat "thresholds" list
-            startbit = 0
-            for thrName in connDef["thresholds"]:
-                nbits = connDef["nbitsDefault"]
-                if type(thrName)==tuple:
-                    (thrName,nbits) = thrName
-                if thrName is None:
-                    startbit += nbits
-                    continue
-                tl = TriggerLine( name = thrName, startbit = startbit, nbits = nbits)
-                startbit += nbits
-                newConnector.addTriggerLine(tl)
-
-        elif connDef["format"] == 'topological':
-            # topological connectors when they are electrical
-            # connectors contain the triggerlines in up to four
-            # algorithm groups, each corresponding to a different (fpga,clock) setting
-
-            currentTopoCategory = AlgCategory.getCategoryFromBoardName(boardName)
-
-            if newConnector.ctype == CType.ELEC:
-                for thrG in connDef["algorithmGroups"]:
-                    (newConnector.fpga, newConnector.clock) = map(thrG.__getitem__,["fpga","clock"])
-                    fpga,clock = map(thrG.__getitem__,["fpga","clock"])
-                    for topo in thrG["algorithms"]:
-                        bit = topo.outputbits[0] if isinstance(topo.outputbits, tuple) else topo.outputbits
-                        for (i, tl) in enumerate(topo.outputlines):
-                            # for topoological triggerlines the names have to be prefixed as they are in the item definitions
-                            tlname = currentTopoCategory.prefix + tl
-                            newConnector.addTriggerLine( TriggerLine( name = tlname, startbit = bit+i, nbits = 1 ), fpga, clock )
-
-        else:
-            raise RuntimeError("Property 'format' of connector %s is '%s' but must be either 'multiplicity' or 'topological'")
-
         if newConnector.ctype == CType.CTPIN:
             try:
                 zbThr = connDef["zeroBias"]
@@ -131,17 +100,19 @@ class MenuConnectorsCollection(object):
 
 
 class Connector(object):
-    __slots__ = [ 'name', 'cformat', 'ctype', 'legacy', 'triggerLines']
-    def __init__(self, name, cformat, ctype, legacy):
+    __slots__ = [ 'name', 'cformat', 'ctype', 'legacy', 'boardName', 'triggerLines']
+    def __init__(self, connDef):
         """
         @param name name of the connector
         @param cformat can be 'topological' or 'multiplicity'
         @param ctype can be 'ctpin', 'electrical', or 'optical'
         """
+        name, cformat, ctype, legacy, boardName = map(connDef.__getitem__,["name", "format", "type", "legacy", "board"])
         self.name    = name
         self.cformat = CFormat.from_str(cformat)
         self.ctype   = CType.from_str(ctype)
         self.legacy  = bool(legacy)
+        self.boardName = boardName
         self.triggerLines = []
 
     def addTriggerLine(self, tl):
@@ -162,15 +133,74 @@ class Connector(object):
         return confObj
 
 
+class OpticalConnector(Connector):
+    __slots__ = [ 'name', 'cformat', 'ctype', 'legacy', 'triggerLines']
+    def __init__(self, name, cformat, ctype, legacy, connDef):
+        """
+        @param name name of the connector
+        @param cformat can be 'topological' or 'multiplicity'
+        @param ctype can be 'ctpin', 'electrical', or 'optical'
+        """
+        super(OpticalConnector,self).__init__(connDef = connDef)
+
+        # treat differently depending on the "format", which can be: 'topological' or 'multiplicity'
+        if connDef["format"] == 'multiplicity':
+            # multiplicity connectors contain all the triggerlines in a flat "thresholds" list
+            startbit = 0
+            for thrName in connDef["thresholds"]:
+                nbits = connDef["nbitsDefault"]
+                if type(thrName)==tuple:
+                    (thrName,nbits) = thrName
+                if thrName is None:
+                    startbit += nbits
+                    continue
+                tl = TriggerLine( name = thrName, startbit = startbit, nbits = nbits)
+                startbit += nbits
+                self.addTriggerLine(tl)
+        else:
+            raise RuntimeError("Property 'format' of connector %s is '%s' but must be either 'multiplicity' or 'topological'" % (name,connDef["format"]))
+
+
 class ElectricalConnector(Connector):
-    def __init__(self, name, cformat, legacy):
+    def __init__(self, name, cformat, legacy, connDef):
         """
         @param name name of the connector
         @param cformat can be 'topological' or 'multiplicity'
         """
-        super(ElectricalConnector,self).__init__(name = name, cformat = cformat, ctype = 'electrical', legacy = legacy)
+        super(ElectricalConnector,self).__init__(connDef = connDef)
         self.triggerLines = { 0 : {0:[],1:[]}, 1 : {0:[],1:[]} }
 
+        if self.cformat == CFormat.TOPO:
+            # topological connectors when they are electrical
+            # connectors contain the triggerlines in up to four
+            # algorithm groups, each corresponding to a different (fpga,clock) setting
+            currentTopoCategory = AlgCategory.getCategoryFromBoardName(self.boardName)
+            for thrG in connDef["algorithmGroups"]:
+                fpga,clock = map(thrG.__getitem__,["fpga","clock"])
+                for topo in thrG["algorithms"]:
+                    bit = topo.outputbits[0] if isinstance(topo.outputbits, tuple) else topo.outputbits
+                    for (i, tl) in enumerate(topo.outputlines):
+                        # for topoological triggerlines the names have to be prefixed as they are in the item definitions
+                        tlname = currentTopoCategory.prefix + tl
+                        self.addTriggerLine( TriggerLine( name = tlname, startbit = bit+i, nbits = 1 ), fpga, clock )
+        elif self.cformat == CFormat.SIMPLE:
+            for sigG in connDef["signalGroups"]:
+                clock = sigG["clock"]
+                startbit = 0
+                for signal in sigG["signals"]:
+                    nbits = connDef["nbitsDefault"]
+                    if type(signal)==tuple:
+                        (signal,nbits) = signal
+                    if signal is None:
+                        startbit += nbits
+                        continue
+                    tl = TriggerLine( name = signal, startbit = startbit, nbits = nbits)
+                    startbit += nbits
+                    self.addTriggerLine(tl, 0, clock)
+        else:
+            raise RuntimeError("Property 'format' of connector %s is '%s' but must be either 'multiplicity' or 'topological'" % (name,connDef["format"]))
+
+
     def addTriggerLine(self, tl, fpga, clock):
         self.triggerLines[fpga][clock].append( tl )
 
@@ -184,12 +214,18 @@ class ElectricalConnector(Connector):
         if self.legacy:
             confObj["legacy"] = self.legacy
         confObj["triggerlines"] = odict()
-        for fpga in [0,1]:
-            fpgas = "fpga%i" % fpga
-            confObj["triggerlines"][fpgas] = odict()
+        if self.cformat == CFormat.TOPO:
+            for fpga in [0,1]:
+                fpgas = "fpga%i" % fpga
+                confObj["triggerlines"][fpgas] = odict()
+                for clock in [0,1]:
+                    clocks = "clock%i" % clock
+                    confObj["triggerlines"][fpgas][clocks] = [tl.json() for tl in self.triggerLines[fpga][clock]]
+        elif self.cformat == CFormat.SIMPLE:
+            confObj["triggerlines"] = odict()
             for clock in [0,1]:
                 clocks = "clock%i" % clock
-                confObj["triggerlines"][fpgas][clocks] = [tl.json() for tl in self.triggerLines[fpga][clock]]
+                confObj["triggerlines"][clocks] = [tl.json() for tl in self.triggerLines[0][clock]]
         return confObj
 
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py
index fb8a2a6f2d1dac158c027f7624cd8766a8c8fb58..607f415ff1573930b07c948d653d04775ece8c84 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Base/Thresholds.py
@@ -65,7 +65,7 @@ class MenuThresholdsCollection( object ):
 
     def json(self):
         confObj = odict()
-        for ttype in (ThrType.Run3Types() + ThrType.NIMTypes() + [ThrType.TOPO, ThrType.MUTOPO ]):
+        for ttype in (ThrType.Run3Types() + ThrType.NIMTypes() + [ThrType.TOPO, ThrType.MUTOPO] + [ThrType.ALFA]):
             confObj[ttype.name] = odict()
             confObj[ttype.name]["type"] = ttype.name
             confObj[ttype.name]["thresholds"] = odict()
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/CTPInputConfig.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/CTPInputConfig.py
index 6ee7e5c975ec29caa48d09f44f0509b8da567737..d663ac6ebbbd430225a1f9fe9b330ef911a9d215 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/CTPInputConfig.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/CTPInputConfig.py
@@ -14,12 +14,12 @@ class CTPInputConfig:
     def cablingLayout():
         inputLayout = odict()
         inputLayout["optical"] = odict([
-            ( "connector0", "Topo1" ),
-            ( "connector1", "Topo2" ),
-            ( "connector2", "MuctpiMult" )
+            ( "connector0", "Topo1Opt0" ),
+            ( "connector1", "Topo1Opt1" ),
+            ( "connector2", "MuCTPiOpt0" )
         ])
         inputLayout["electrical"] = odict([
-            ( "connector0", "Topo3" ),
+            ( "connector0", "Topo3El" ),
             ( "connector1", "LegacyTopo0" ),
             ( "connector2", "AlfaCtpin" )
         ])
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/ThresholdDef.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/ThresholdDef.py
index a2e1133ad06c3237ae924ee31625a1798418efe4..f0245e030e61508608cf8872b05db8cf395499b0 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/ThresholdDef.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Config/ThresholdDef.py
@@ -1,6 +1,6 @@
 # Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
 
-from ..Base.Thresholds import Threshold, MuonThreshold, EMThreshold, TauThreshold, JetThreshold, XEThreshold, MBTSThreshold, MBTSSIThreshold, NimThreshold, ThresholdValue
+from ..Base.Thresholds import MuonThreshold, EMThreshold, TauThreshold, JetThreshold, XEThreshold, MBTSThreshold, MBTSSIThreshold, NimThreshold, ThresholdValue
 
 class ThresholdDef:
 
@@ -262,8 +262,8 @@ class ThresholdDef:
         LUT5offset = 26
         for i, alfa in enumerate( ['B7R1L', 'B7R1U', 'A7R1L', 'A7R1U', 'A7L1L', 'A7L1U', 'B7L1L', 'B7L1U'] ):
             phaseOffset = 32 * (i%2)
-            Threshold('ALFA_%s'    % alfa, 'ALFA', mapping = LUT1offset + i/2 + phaseOffset, run = 3 )
-            Threshold('ALFA2_%s'   % alfa, 'ALFA', mapping = LUT2offset + i/2 + phaseOffset, run = 3 )
-            Threshold('ALFA3_%s'   % alfa, 'ALFA', mapping = LUT3offset + i/2 + phaseOffset, run = 3 )
-            Threshold('ALFA4_%s'   % alfa, 'ALFA', mapping = LUT4offset + i/2 + phaseOffset, run = 3 )
-            Threshold('ALFA_%s_OD' % alfa, 'ALFA', mapping = LUT5offset + i/2 + phaseOffset, run = 3 )
+            NimThreshold('ALFA_%s'    % alfa, 'ALFA', mapping = LUT1offset + i/2 + phaseOffset )
+            NimThreshold('ALFA2_%s'   % alfa, 'ALFA', mapping = LUT2offset + i/2 + phaseOffset )
+            NimThreshold('ALFA3_%s'   % alfa, 'ALFA', mapping = LUT3offset + i/2 + phaseOffset )
+            NimThreshold('ALFA4_%s'   % alfa, 'ALFA', mapping = LUT4offset + i/2 + phaseOffset )
+            NimThreshold('ALFA_%s_OD' % alfa, 'ALFA', mapping = LUT5offset + i/2 + phaseOffset )
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/L1MenuConfig.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/L1MenuConfig.py
index ce5cc26854a54e58ae12a45cde9a532c5ade7a17..108cafb81f1a374bb77774e73f2d18ec0569bd94 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/L1MenuConfig.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/L1MenuConfig.py
@@ -255,21 +255,27 @@ class L1MenuConfig(object):
         log.info("Reading TriggerMenuMT.Menu.Menu_%s", self.menuToLoad)
         menumodule = __import__('TriggerMenuMT.L1.Menu.Menu_%s' % self.menuToLoad, globals(), locals(), ['defineMenu'], 0)
         menumodule.defineMenu()
-        log.info("... L1 menu '%s' contains:", self.menuToLoad)
+        log.info("... L1 menu '%s' contains %i items", self.menuToLoad, len(L1MenuFlags.items()))
 
         log.info("Reading TriggerMenuMT.Menu.Menu_%s_inputs", self.menuToLoad)
         topomenumodule = __import__('TriggerMenuMT.L1.Menu.Menu_%s_inputs' % self.menuToLoad, globals(), locals(), ['defineMenu'], 0)
-        topomenumodule.defineInputsMenu()
+        topomenumodule.defineInputsMenu() # this adds the inputs definition (boards) to L1MenuFlags.boards
+        connectorCount = 0
         algoCount = 0
         for boardName, boardDef in L1MenuFlags.boards().items():
             if "connectors" in boardDef:
+                connectorCount += len(boardDef["connectors"])
                 for c in boardDef["connectors"]:
                     if "thresholds" in c:
                         algoCount += len(c["thresholds"])
-                    else:
+                    elif "algorithmGroups" in c:
                         for t in c["algorithmGroups"]:
                             algoCount += len(t["algorithms"])
-        log.info("... L1Topo menu '%s' contains %i algorithms", self.menuToLoad, algoCount)
+                    else:
+                        for t in c["signalGroups"]:
+                            algoCount += len(t["signals"])
+        log.info("... L1Topo menu '%s' contains %i boards (%s)", self.menuToLoad, len(L1MenuFlags.boards()), ', '.join(L1MenuFlags.boards().keys()))
+        log.info("    with %i connectors and %i input signals", connectorCount, algoCount)
 
         try:
             log.info("Reading TriggerMenuMT.Menu.Menu_%s_inputs_legacy", self.menuToLoad)
@@ -483,10 +489,8 @@ class L1MenuConfig(object):
         list_of_undefined_thresholds = []
         # new thresholds
         for (boardName, boardDef) in L1MenuFlags.boards().items():
-            if boardName.startswith("Ctpin"):
-                continue
             for connDef in boardDef["connectors"]:
-                if connDef["format"] != "multiplicity":
+                if connDef["type"] == "ctpin" or connDef["format"] != "multiplicity":
                     continue
                 for thrName in connDef["thresholds"]:
                     if type(thrName) == tuple:
@@ -500,11 +504,29 @@ class L1MenuConfig(object):
                     else:
                         self.l1menu.addThreshold( threshold )
 
+        # signals from merger boards like AlfaCtpin
+        for (boardName, boardDef) in L1MenuFlags.boards().items():
+            for connDef in boardDef["connectors"]:
+                if connDef["format"] != "simple":
+                    continue
+                for sGrp in connDef["signalGroups"]:
+                    for thrName in sGrp["signals"]:
+                        if type(thrName) == tuple:
+                            (thrName, _) = thrName
+                        if thrName is None or thrName in self.l1menu.thresholds:
+                            continue
+                        threshold = self.getDefinedThreshold(thrName)
+                        if threshold is None:
+                            log.error('Threshold %s is required in menu on board %s, connector %s, but it is not defined', (thrName, boardName, connDef['name']) )
+                            list_of_undefined_thresholds += [ thrName ]
+                        else:
+                            self.l1menu.addThreshold( threshold )
+
         # ctpin thresholds
         for (boardName, boardDef) in allBoards:
-            if not boardName.startswith("Ctpin"):
-                continue
             for connDef in boardDef["connectors"]:
+                if connDef["type"] != "ctpin":
+                    continue
                 for entry in connDef["thresholds"]:
                     if type(entry) == dict:
                         # section that defines topo legacy thresholds 
@@ -614,13 +636,17 @@ class L1MenuConfig(object):
         # assign mapping to thresholds according to their use in the menu
         self.mapThresholds()
 
-        # update the prescales that are not 1
-        #self.updateItemPrescales()
+        # ------------------
+        # CTP
+        # ------------------
+        self.l1menu.ctp.checkConnectorAvailability(self.l1menu.connectors, self.menuToLoad)
 
         # set the ctp monitoring (only now after the menu is defined)
         self.l1menu.setupCTPMonitoring()
 
+        # ------------------
         # final consistency check
+        # ------------------
         self.l1menu.check()
 
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8.py
index 4aadf7d0802bba1bbe8e1c6d492be369f2b77ddd..7fe12371ce13c288d92e7d3839f1c0151966ae80 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8.py
@@ -383,7 +383,12 @@ def defineMenu():
         #ATR-17320
         'L1_CEP-CJ60',
         'L1_CEP-CJ50' ,
-        'L1_CEP-CJ50.ETA21'
+        'L1_CEP-CJ50.ETA21',
+
+        'L1_ALFA_ANY',
+        'L1_ALFA_ELAST15', 'L1_ALFA_ELAST18',
+        'L1_ALFA_B7L1U','L1_ALFA_B7L1L','L1_ALFA_A7L1U','L1_ALFA_A7L1L','L1_ALFA_A7R1U','L1_ALFA_A7R1L','L1_ALFA_B7R1U','L1_ALFA_B7R1L', # L1_ALFA_Calib
+        'L1_ALFA_SYST9', 'L1_ALFA_SYST10', 'L1_ALFA_SYST11', 'L1_ALFA_SYST12', 'L1_ALFA_SYST17', 'L1_ALFA_SYST18', # L1_ALFA_SYS, L1_ALFA_SYS_Calib
 
         ]
     
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8_inputs.py b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8_inputs.py
index 71c27e2df737ce43a016970c819164bedd0c0686..9ba531fffcf73034790ea34f8113dca2f93c364d 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8_inputs.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/L1/Menu/Menu_MC_pp_v8_inputs.py
@@ -11,6 +11,7 @@ def defineInputsMenu():
     ctpinBoards = odict() # Ctpin/Slot9 (CTPCAL, NIM1, NIM2)
     topoBoards = odict()  # Topo1, Topo2, Topo3
     muctpiBoard = odict() # MuCTPi
+    alfaBoard = odict() # ALFA
 
 
     #-----------------------------------
@@ -320,6 +321,39 @@ def defineInputsMenu():
         ]
     })
 
+
+    alfaBoard["AlfaCtpin"] = odict()
+    alfaBoard["AlfaCtpin"]["connectors"] = []
+    alfaBoard["AlfaCtpin"]["connectors"].append({
+        "name" : "AlfaCtpin",
+        "format" : "simple",
+        "nbitsDefault" : 1,
+        "type" : "electrical",
+        "legacy" : False,
+        "signalGroups" : [
+            {
+                "clock" : 0,
+                "signals" : [
+                    (None,2), "ALFA_B7R1L", "ALFA_A7R1L", "ALFA_A7L1L", "ALFA_B7L1L",
+                    (None,2), "ALFA2_B7R1L", "ALFA2_A7R1L", "ALFA2_A7L1L", "ALFA2_B7L1L",
+                    (None,2), "ALFA3_B7R1L", "ALFA3_A7R1L", "ALFA3_A7L1L", "ALFA3_B7L1L",
+                    (None,2), "ALFA4_B7R1L", "ALFA4_A7R1L", "ALFA4_A7L1L", "ALFA4_B7L1L"
+                ]
+            },
+            {
+                "clock" : 1,
+                "signals" : [
+                    (None,2), "ALFA_B7R1U", "ALFA_A7R1U", "ALFA_A7L1U", "ALFA_B7L1U",
+                    (None,2), "ALFA2_B7R1U", "ALFA2_A7R1U", "ALFA2_A7L1U", "ALFA2_B7L1U",
+                    (None,2), "ALFA3_B7R1U", "ALFA3_A7R1U", "ALFA3_A7L1U", "ALFA3_B7L1U",
+                    (None,2), "ALFA4_B7R1U", "ALFA4_A7R1U", "ALFA4_A7L1U", "ALFA4_B7L1U",
+                    (None,2), "ALFA_B7R1U_OD", "ALFA_A7R1U_OD", "ALFA_A7L1U_OD", "ALFA_B7L1U_OD"
+                ]
+            }
+        ]
+    })
+
+
     L1MenuFlags.boards().clear()
 
     L1MenuFlags.boards().update( topoBoards )   # Topo1, Topo2, Topo3
@@ -328,3 +362,5 @@ def defineInputsMenu():
 
     L1MenuFlags.boards().update( ctpinBoards )  # CTPIN/Slot9 NIM1, NIM2, CALREQ
 
+    L1MenuFlags.boards().update( alfaBoard )  # ALFA
+