Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
RichTel40CableMapping.cpp 9.88 KiB
/*****************************************************************************\
* (c) Copyright 2000-2020 CERN for the benefit of the LHCb Collaboration      *
*                                                                             *
* This software is distributed under the terms of the GNU General Public      *
* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
*                                                                             *
* In applying this licence, CERN does not waive the privileges and immunities *
* granted to it by virtue of its status as an Intergovernmental Organization  *
* or submit itself to any jurisdiction.                                       *
\*****************************************************************************/

// local
#include "RichFutureDAQ/RichTel40CableMapping.h"

// RICH
#include "RichUtils/RichException.h"
#include "RichUtils/ToArray.h"
#include "RichUtils/ZipRange.h"

// Gaudi
#include "Gaudi/Algorithm.h"

// Messaging
#include "RichFutureUtils/RichMessaging.h"
#define debug( ... )                                                                                                   \
  if ( messenger() ) { ri_debug( __VA_ARGS__ ); }
#define verbo( ... )                                                                                                   \
  if ( messenger() ) { ri_verbo( __VA_ARGS__ ); }
#define warning( ... )                                                                                                 \
  if ( messenger() ) { ri_warning( __VA_ARGS__ ); }
#define info( ... )                                                                                                    \
  if ( messenger() ) { ri_info( __VA_ARGS__ ); }

// boost
#include "boost/container/static_vector.hpp"

// STL
#include <algorithm>
#include <limits>
#include <string>
#include <utility>
#include <vector>

using namespace Rich::Future::DAQ;
using namespace Rich::Detector;
using namespace Rich::DAQ;

void Tel40CableMapping::fillCableMaps( const LHCb::Detector::DeLHCb&
#ifdef USE_DD4HEP
                                           lhcb
#endif
                                       ,
                                       const Conds& C ) {

  // check status is (still) OK before continuing
  if ( !isInitialised() ) { return; }

  // panel data
  const auto rich_types  = std::array{Rich::Rich1, Rich::Rich1, Rich::Rich2, Rich::Rich2};
  const auto panel_types = std::array{Rich::top, Rich::bottom, Rich::aside, Rich::cside};

  // mapping version
  boost::container::static_vector<int, rich_types.size()> condVers;
  for ( const auto& cond : C ) {
    // Access parameter, with default value 0 if missing
    condVers.push_back( condition_param<int>( cond, "MappingVersion", 0 ) );
  }
  // Should all be the same for all panels.
  if ( !condVers.empty() && std::all_of( condVers.begin(), condVers.end(),
                                         [first = condVers.front()]( const auto& e ) { return e == first; } ) ) {
    m_mappingVer = condVers.front();
  } else {
    setIsInitialised( false );
    throw Rich::Exception( "Inconsistent mapping versions" );
  }
  debug( " -> Mapping Version ", m_mappingVer, endmsg );

  // extract the mappings
  for ( const auto&& [cond, rich, panel, name] : Ranges::ConstZip( C, rich_types, panel_types, ConditionPaths ) ) {

    // Number of links for this RICH panel
    const std::size_t nLinks = condition_param<int>( cond, "NumberOfLinks" );
    if ( nLinks > 0 ) {
      debug( " -> Found ", nLinks, " links for ", rich, " ", Rich::text( rich, panel ), endmsg );

      // Load links data
      const auto pmtTypes   = condition_param<std::vector<std::string>>( cond, "PMTTypes" );
      const auto modNames   = condition_param<std::vector<std::string>>( cond, "ModuleNames" );
      const auto modNums    = condition_param<std::vector<int>>( cond, "ModuleNumbers" );
      const auto pdmdbs     = condition_param<std::vector<int>>( cond, "PDMDBNumbers" );
      const auto pdmdbLinks = condition_param<std::vector<int>>( cond, "PDMDBLinks" );
      const auto sourceIDs  = condition_param<std::vector<int>>( cond, "Tel40SourceIDs" );
      const auto connectors = condition_param<std::vector<int>>( cond, "Tel40SConnectors" );
      const auto mpos       = condition_param<std::vector<int>>( cond, "Tel40MPOs" );
      const auto statuses   = condition_param<std::vector<int>>( cond, "Tel40LinkIsActive" );
      // sanity size check
      if ( nLinks != pmtTypes.size() ||   //
           nLinks != modNames.size() ||   //
           nLinks != modNums.size() ||    //
           nLinks != pdmdbs.size() ||     //
           nLinks != pdmdbLinks.size() || //
           nLinks != sourceIDs.size() ||  //
           nLinks != connectors.size() || //
           nLinks != mpos.size() ||       //
           nLinks != statuses.size() ) {
        setIsInitialised( false );
        throw Rich::Exception( "Inconsistent data sizes for '" + name + "'" );
      }

      // Find Max SourceID for this RICH/Side
      assert( !sourceIDs.empty() );
      const SourceID maxSID( *std::max_element( sourceIDs.begin(), sourceIDs.end() ) );
      // handle this silently to work around issues with older DB tags.
      if ( rich != maxSID.rich() || panel != maxSID.side() ) {
        setIsInitialised( false );
        continue;
      }

      // initialise the data storage
      auto& tel40CD = m_tel40ConnData.at( rich ).at( panel );
      tel40CD.resize( 1 + maxSID.payload() );
      verbo( " -> Reserved space for ", tel40CD.size(), " sourceIDs", endmsg );

      // loop over data and fill lookup structures
      for ( const auto&& [type, name, modN, pdmdb, pdmdbLink, sID, conn, mpo, status] : //
            Ranges::ConstZip( pmtTypes, modNames, modNums, pdmdbs, pdmdbLinks,          //
                              sourceIDs, connectors, mpos, statuses ) ) {
        // data
        assert( (std::size_t)conn <= ConnectionsPerTel40MPO );
        assert( sID >= 0 && sID < std::numeric_limits<SourceID::Type>::max() );
        const SourceID sourceID( sID );
        assert( rich == sourceID.rich() && panel == sourceID.side() );
        // connections in DB are numbered [1-12] so subtract one to convert to [0-11]
        // In first (unrealistic) implementation MPO was just [1,2] for the two groups
        // of 12 connections per sourceID. In the real DB they are [1,3] for the first
        // SourceID per Tel40, [5,7] for the second SourceID. As we just need to know
        // which of the two groups we have, map back to [1,2]
        assert( 1 == mpo || 2 == mpo || 3 == mpo || 5 == mpo || 7 == mpo );
        auto norm_mpo = []( const auto mpo ) {
          switch ( mpo ) {
          case 1:
            return 1;
          case 2:
            return 2;
          case 3:
            return 2;
          case 5:
            return 1;
          case 7:
            return 2;
          }
          return std::numeric_limits<int>::signaling_NaN();
        };
        const Tel40Connector link( ( ConnectionsPerTel40MPO * ( norm_mpo( mpo ) - 1 ) ) + ( conn - 1 ) );

        // PMT type
        const bool isLargePMT = ( "H" == type );

        // The cached RichSmartID for this entry
        LHCb::RichSmartID smartID( rich, panel, LHCb::RichSmartID::MaPMTID );
        smartID.setLargePMT( isLargePMT );

        // Is link active. Start with value in RICH condition.
        bool linkIsActive = ( 0 != status );
#ifdef USE_DD4HEP
        // Use link status flag from general LHCb condition instead of from RICH condition.
        const auto linksActive = lhcb.tell40link( sourceID.data() );
        if ( !linksActive.has_value() ) {
          warning( "Source ID '", sourceID.data(), "' is missing in LHCb Tel40 Link condition", endmsg );
        } else {
          // LHCb general condition has this source ID so use its value instead.
          linkIsActive = linksActive.value().isEnabled( link.data() );
        }
#else
        // with DetDesc just use RICH value
        // Eventually once DetDesc is dropped the use of the RICH status code can be dropped
        // entirely both here and from the condition itself in dd4hep.
#endif

        // link data struct
        const Tel40LinkData linkD( name,                   //
                                   smartID,                //
                                   sourceID,               //
                                   link,                   //
                                   isLargePMT,             //
                                   linkIsActive,           //
                                   PDModuleNumber( modN ), //
                                   PDMDBID( pdmdb ),       //
                                   PDMDBFrame( pdmdbLink ) );

        // fill the Tel40 connection data structure
        const std::size_t idx = sourceID.payload();
        if ( idx >= tel40CD.size() ) {
          throw Rich::Exception( "Tel40 Source ID '" + std::to_string( idx ) + "' exceeds expected range" );
        }
        auto& conData = tel40CD.at( idx );
        assert( (std::size_t)link.data() < Tel40CableMapping::MaxConnectionsPerTel40 );
        if ( conData.size() <= (std::size_t)link.data() ) { conData.resize( link.data() + 1 ); }
        conData.at( link.data() ) = linkD;
        if ( linkIsActive ) {
          ++conData.nActiveLinks;
        } else {
          conData.hasInactiveLinks = true;
        }
        verbo( "  -> ", linkD, endmsg );

        // fill Tel40 module data structure
        auto& d = m_tel40ModuleData.at( modN ).at( pdmdb ).at( pdmdbLink );
        assert( !d.isValid() ); // not yet initialised
        d = std::move( linkD ); // final use of linkD so move

        // fill the active links per source ID map
        auto& links = m_linksPerSourceID[sourceID]; // Intentionally get-or-create here
        // No entry for this link should already exist
        assert( std::find( links.begin(), links.end(), link ) == links.end() );
        // finally insert
        links.insert( link );
      }
    } // nLinks > 0

  } // conditions loop
}