Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-10-25 09:54:12

0001 #include "L1Trigger/CSCTriggerPrimitives/interface/CSCMotherboard.h"
0002 #include <iostream>
0003 #include <memory>
0004 
0005 // Default values of configuration parameters.
0006 const unsigned int CSCMotherboard::def_mpc_block_me1a = 1;
0007 const unsigned int CSCMotherboard::def_alct_trig_enable = 0;
0008 const unsigned int CSCMotherboard::def_clct_trig_enable = 0;
0009 const unsigned int CSCMotherboard::def_match_trig_enable = 1;
0010 const unsigned int CSCMotherboard::def_match_trig_window_size = 7;
0011 const unsigned int CSCMotherboard::def_tmb_l1a_window_size = 7;
0012 
0013 CSCMotherboard::CSCMotherboard(unsigned endcap,
0014                                unsigned station,
0015                                unsigned sector,
0016                                unsigned subsector,
0017                                unsigned chamber,
0018                                CSCBaseboard::Parameters& conf)
0019     : CSCBaseboard(endcap, station, sector, subsector, chamber, conf) {
0020   // Normal constructor.  -JM
0021   // Pass ALCT, CLCT, and common parameters on to ALCT and CLCT processors.
0022   static std::atomic<bool> config_dumped{false};
0023 
0024   mpc_block_me1a_ = conf.tmbParams().getParameter<unsigned int>("mpcBlockMe1a");
0025   alct_trig_enable_ = conf.tmbParams().getParameter<unsigned int>("alctTrigEnable");
0026   clct_trig_enable_ = conf.tmbParams().getParameter<unsigned int>("clctTrigEnable");
0027   match_trig_enable_ = conf.tmbParams().getParameter<unsigned int>("matchTrigEnable");
0028   match_trig_window_size_ = conf.tmbParams().getParameter<unsigned int>("matchTrigWindowSize");
0029   tmb_l1a_window_size_ =  // Common to CLCT and TMB
0030       conf.tmbParams().getParameter<unsigned int>("tmbL1aWindowSize");
0031 
0032   // configuration handle for number of early time bins
0033   early_tbins = conf.tmbParams().getParameter<int>("tmbEarlyTbins");
0034 
0035   // whether to not reuse CLCTs that were used by previous matching ALCTs
0036   drop_used_clcts = conf.tmbParams().getParameter<bool>("tmbDropUsedClcts");
0037 
0038   // whether to readout only the earliest two LCTs in readout window
0039   readout_earliest_2 = conf.tmbParams().getParameter<bool>("tmbReadoutEarliest2");
0040 
0041   match_earliest_clct_only_ = conf.tmbParams().getParameter<bool>("matchEarliestClctOnly");
0042 
0043   infoV = conf.tmbParams().getParameter<int>("verbosity");
0044 
0045   alctProc = std::make_unique<CSCAnodeLCTProcessor>(endcap, station, sector, subsector, chamber, conf);
0046   clctProc = std::make_unique<CSCCathodeLCTProcessor>(endcap, station, sector, subsector, chamber, conf);
0047 
0048   // Check and print configuration parameters.
0049   checkConfigParameters();
0050   if (infoV > 0 && !config_dumped) {
0051     dumpConfigParams();
0052     config_dumped = true;
0053   }
0054 
0055   allLCTs_.setMatchTrigWindowSize(match_trig_window_size_);
0056 
0057   // get the preferred CLCT BX match array
0058   preferred_bx_match_ = conf.tmbParams().getParameter<std::vector<int>>("preferredBxMatch");
0059 
0060   // sort CLCT only by bx or by quality+bending for  ALCT-CLCT match
0061   sort_clct_bx_ = conf.tmbParams().getParameter<bool>("sortClctBx");
0062 
0063   // quality assignment
0064   qualityAssignment_ = std::make_unique<LCTQualityAssignment>(endcap, station, sector, subsector, chamber, conf);
0065 
0066   // quality control of stubs
0067   qualityControl_ = std::make_unique<LCTQualityControl>(endcap, station, sector, subsector, chamber, conf);
0068 
0069   // shower-trigger source
0070   showerSource_ = conf.showerParams().getParameter<std::vector<unsigned>>("source");
0071 
0072   unsigned csc_idx = CSCDetId::iChamberType(theStation, theRing) - 2;
0073   thisShowerSource_ = showerSource_[csc_idx];
0074 
0075   // shower readout window
0076   minbx_readout_ = CSCConstants::LCT_CENTRAL_BX - tmb_l1a_window_size_ / 2;
0077   maxbx_readout_ = CSCConstants::LCT_CENTRAL_BX + tmb_l1a_window_size_ / 2;
0078   assert(tmb_l1a_window_size_ / 2 <= CSCConstants::LCT_CENTRAL_BX);
0079 
0080   // enable the upgrade processors for ring 1 stations
0081   if (runPhase2_ and theRing == 1) {
0082     clctProc = std::make_unique<CSCUpgradeCathodeLCTProcessor>(endcap, station, sector, subsector, chamber, conf);
0083     if (enableAlctPhase2_) {
0084       alctProc = std::make_unique<CSCUpgradeAnodeLCTProcessor>(endcap, station, sector, subsector, chamber, conf);
0085     }
0086   }
0087 
0088   // set up helper class to check if ALCT and CLCT cross
0089   ignoreAlctCrossClct_ = conf.tmbParams().getParameter<bool>("ignoreAlctCrossClct");
0090   if (!ignoreAlctCrossClct_) {
0091     cscOverlap_ = std::make_unique<CSCALCTCrossCLCT>(endcap, station, theRing, ignoreAlctCrossClct_, conf.conf());
0092   }
0093 }
0094 
0095 void CSCMotherboard::clear() {
0096   // clear the processors
0097   if (alctProc)
0098     alctProc->clear();
0099   if (clctProc)
0100     clctProc->clear();
0101 
0102   lctV.clear();
0103 
0104   allLCTs_.clear();
0105 
0106   for (int bx = 0; bx < CSCConstants::MAX_LCT_TBINS; bx++) {
0107     showers_[bx].clear();
0108   }
0109 }
0110 
0111 // Set configuration parameters obtained via EventSetup mechanism.
0112 void CSCMotherboard::setConfigParameters(const CSCDBL1TPParameters* conf) {
0113   if (nullptr == conf) {
0114     return;
0115   }
0116   static std::atomic<bool> config_dumped{false};
0117 
0118   // Config. parameters for the TMB itself.
0119   mpc_block_me1a_ = conf->tmbMpcBlockMe1a();
0120   alct_trig_enable_ = conf->tmbAlctTrigEnable();
0121   clct_trig_enable_ = conf->tmbClctTrigEnable();
0122   match_trig_enable_ = conf->tmbMatchTrigEnable();
0123   match_trig_window_size_ = conf->tmbMatchTrigWindowSize();
0124   tmb_l1a_window_size_ = conf->tmbTmbL1aWindowSize();
0125 
0126   // Config. paramteres for ALCT and CLCT processors.
0127   alctProc->setConfigParameters(conf);
0128   clctProc->setConfigParameters(conf);
0129 
0130   // Check and print configuration parameters.
0131   checkConfigParameters();
0132   if (!config_dumped) {
0133     dumpConfigParams();
0134     config_dumped = true;
0135   }
0136   minbx_readout_ = CSCConstants::LCT_CENTRAL_BX - tmb_l1a_window_size_ / 2;
0137   maxbx_readout_ = CSCConstants::LCT_CENTRAL_BX + tmb_l1a_window_size_ / 2;
0138 }
0139 
0140 std::tuple<std::vector<CSCALCTDigi>, std::vector<CSCCLCTDigi>> CSCMotherboard::runCommon(
0141     const CSCWireDigiCollection* wiredc, const CSCComparatorDigiCollection* compdc, const RunContext& context) {
0142   // Step 1: Setup
0143   clear();
0144 
0145   setConfigParameters(context.parameters_);
0146 
0147   std::tuple<std::vector<CSCALCTDigi>, std::vector<CSCCLCTDigi>> retValue;
0148 
0149   // Check for existing processors
0150   if (!(alctProc && clctProc)) {
0151     edm::LogError("CSCMotherboard|SetupError") << "+++ run() called for non-existing ALCT/CLCT processor! +++ \n";
0152     return retValue;
0153   }
0154 
0155   assert(context.cscGeometry_);
0156   auto chamber = cscChamber(*context.cscGeometry_);
0157   // Step 2: Run the processors
0158   // run anodeLCT and cathodeLCT
0159   retValue = std::make_tuple(alctProc->run(wiredc, chamber), clctProc->run(compdc, chamber, context.lookupTableCCLUT_));
0160 
0161   // Step 2b: encode high multiplicity bits (independent of LCT construction)
0162   encodeHighMultiplicityBits();
0163 
0164   return retValue;
0165 }
0166 
0167 void CSCMotherboard::run(const CSCWireDigiCollection* wiredc,
0168                          const CSCComparatorDigiCollection* compdc,
0169                          const RunContext& context) {
0170   auto [alctV, clctV] = runCommon(wiredc, compdc, context);
0171   // if there are no ALCTs and no CLCTs, it does not make sense to run this TMB
0172   if (alctV.empty() and clctV.empty())
0173     return;
0174 
0175   // step 3: match the ALCTs to the CLCTs
0176   matchALCTCLCT();
0177 
0178   // Step 4: Select at most 2 LCTs per BX
0179   selectLCTs();
0180 }
0181 
0182 void CSCMotherboard::matchALCTCLCT() {
0183   // array to mask CLCTs
0184   bool used_clct_mask[CSCConstants::MAX_CLCT_TBINS] = {false};
0185 
0186   // Step 3: ALCT-centric ALCT-to-CLCT matching
0187   int bx_clct_matched = 0;  // bx of last matched CLCT
0188   for (int bx_alct = 0; bx_alct < CSCConstants::MAX_ALCT_TBINS; bx_alct++) {
0189     // There should be at least one valid CLCT or ALCT for a
0190     // correlated LCT to be formed.  Decision on whether to reject
0191     // non-complete LCTs (and if yes of which type) is made further
0192     // upstream.
0193     if (alctProc->getBestALCT(bx_alct).isValid()) {
0194       // Look for CLCTs within the match-time window.  The window is
0195       // centered at the ALCT bx; therefore, we make an assumption
0196       // that anode and cathode hits are perfectly synchronized.  This
0197       // is always true for MC, but only an approximation when the
0198       // data is analyzed (which works fairly good as long as wide
0199       // windows are used).  To get rid of this assumption, one would
0200       // need to access "full BX" words, which are not readily
0201       // available.
0202       bool is_matched = false;
0203       // we can use single value to best CLCT but here use vector to keep the
0204       // the future option to do multiple ALCT-CLCT matches wiht CLCT from different bx
0205       std::vector<unsigned> clctBx_qualbend_match;
0206       sortCLCTByQualBend(bx_alct, clctBx_qualbend_match);
0207 
0208       bool hasLocalShower = false;
0209       for (unsigned ibx = 1; ibx <= match_trig_window_size_ / 2; ibx++)
0210         hasLocalShower =
0211             (hasLocalShower or clctProc->getLocalShowerFlag(bx_alct - CSCConstants::ALCT_CLCT_OFFSET - ibx));
0212 
0213       // loop on the preferred "delta BX" array
0214       for (unsigned mbx = 0; mbx < match_trig_window_size_; mbx++) {
0215         // evaluate the preffered CLCT BX, taking into account that there is an offset in the simulation
0216         //bx_clct_run2 would be overflow when bx_alct is small but it is okay
0217         unsigned bx_clct_run2 = bx_alct + preferred_bx_match_[mbx] - CSCConstants::ALCT_CLCT_OFFSET;
0218         unsigned bx_clct_qualbend = clctBx_qualbend_match[mbx];
0219         unsigned bx_clct = (sort_clct_bx_ or not(hasLocalShower)) ? bx_clct_run2 : bx_clct_qualbend;
0220         // check that the CLCT BX is valid
0221         if (bx_clct >= CSCConstants::MAX_CLCT_TBINS)
0222           continue;
0223         // do not consider previously matched CLCTs
0224         if (drop_used_clcts && used_clct_mask[bx_clct])
0225           continue;
0226         // only consider >=4 layer CLCTs for ALCT-CLCT type LCTs
0227         // this condition is lowered to >=3 layers for CLCTs in the
0228         // matchALCTCLCTGEM function
0229         if (clctProc->getBestCLCT(bx_clct).getQuality() <= 3)
0230           continue;
0231         // a valid CLCT with sufficient layers!
0232         if (clctProc->getBestCLCT(bx_clct).isValid()) {
0233           if (infoV > 1)
0234             LogTrace("CSCMotherboard") << "Successful ALCT-CLCT match: bx_alct = " << bx_alct
0235                                        << "; bx_clct = " << bx_clct << "; mbx = " << mbx;
0236           // now correlate the ALCT and CLCT into LCT.
0237           // smaller mbx means more preferred!
0238           correlateLCTs(alctProc->getBestALCT(bx_alct),
0239                         alctProc->getSecondALCT(bx_alct),
0240                         clctProc->getBestCLCT(bx_clct),
0241                         clctProc->getSecondCLCT(bx_clct),
0242                         allLCTs_(bx_alct, mbx, 0),
0243                         allLCTs_(bx_alct, mbx, 1),
0244                         CSCCorrelatedLCTDigi::ALCTCLCT);
0245           // when the first LCT is valid, you can mask the matched CLCT and/or
0246           // move on to the next ALCT if match_earliest_clct_only_ is set to true
0247           if (allLCTs_(bx_alct, mbx, 0).isValid()) {
0248             is_matched = true;
0249             used_clct_mask[bx_clct] = true;
0250             bx_clct_matched = bx_clct;
0251             if (match_earliest_clct_only_)
0252               break;
0253           }
0254         }
0255       }
0256       // No CLCT within the match time interval found: report ALCT-only LCT
0257       // (use dummy CLCTs).
0258       if (!is_matched) {
0259         if (infoV > 1)
0260           LogTrace("CSCMotherboard") << "Unsuccessful ALCT-CLCT match (ALCT only): bx_alct = " << bx_alct
0261                                      << " first ALCT " << alctProc->getBestALCT(bx_alct);
0262         if (alct_trig_enable_)
0263           correlateLCTs(alctProc->getBestALCT(bx_alct),
0264                         alctProc->getSecondALCT(bx_alct),
0265                         clctProc->getBestCLCT(bx_alct),
0266                         clctProc->getSecondCLCT(bx_alct),
0267                         allLCTs_(bx_alct, 0, 0),
0268                         allLCTs_(bx_alct, 0, 1),
0269                         CSCCorrelatedLCTDigi::ALCTONLY);
0270       }
0271     }
0272     // No valid ALCTs; attempt to make CLCT-only LCT.  Use only CLCTs
0273     // which have zeroth chance to be matched at later cathode times.
0274     // (I am not entirely sure this perfectly matches the firmware logic.)
0275     // Use dummy ALCTs.
0276     else {
0277       int bx_clct = bx_alct - match_trig_window_size_ / 2;
0278       if (bx_clct >= 0 && bx_clct > bx_clct_matched) {
0279         if (clctProc->getBestCLCT(bx_clct).isValid() and clct_trig_enable_) {
0280           if (infoV > 1)
0281             LogTrace("CSCMotherboard") << "Unsuccessful ALCT-CLCT match (CLCT only): bx_clct = " << bx_clct;
0282           correlateLCTs(alctProc->getBestALCT(bx_alct),
0283                         alctProc->getSecondALCT(bx_alct),
0284                         clctProc->getBestCLCT(bx_clct),
0285                         clctProc->getSecondCLCT(bx_clct),
0286                         allLCTs_(bx_clct, 0, 0),
0287                         allLCTs_(bx_clct, 0, 1),
0288                         CSCCorrelatedLCTDigi::CLCTONLY);
0289         }
0290       }
0291     }
0292   }
0293 }
0294 
0295 // Returns vector of read-out correlated LCTs, if any.  Starts with
0296 // the vector of all found LCTs and selects the ones in the read-out
0297 // time window.
0298 std::vector<CSCCorrelatedLCTDigi> CSCMotherboard::readoutLCTs() const {
0299   // temporary container for further selection
0300   std::vector<CSCCorrelatedLCTDigi> tmpV;
0301 
0302   /*
0303     LCTs in the BX window [early_tbin,...,late_tbin] are considered good for physics
0304     The central LCT BX is time bin 8.
0305     For tmb_l1a_window_size set to 7 (Run-1, Run-2), the window is [5, 6, 7, 8, 9, 10, 11]
0306     For tmb_l1a_window_size set to 5 (Run-3), the window is [6, 7, 8, 9, 10]
0307     For tmb_l1a_window_size set to 3 (Run-4?), the window is [ 7, 8, 9]
0308   */
0309   const unsigned delta_tbin = tmb_l1a_window_size_ / 2;
0310   int early_tbin = CSCConstants::LCT_CENTRAL_BX - delta_tbin;
0311   int late_tbin = CSCConstants::LCT_CENTRAL_BX + delta_tbin;
0312   /*
0313      Special case for an even-numbered time-window,
0314      For instance tmb_l1a_window_size set to 6: [5, 6, 7, 8, 9, 10]
0315   */
0316   if (tmb_l1a_window_size_ % 2 == 0)
0317     late_tbin = CSCConstants::LCT_CENTRAL_BX + delta_tbin - 1;
0318   const int max_late_tbin = CSCConstants::MAX_LCT_TBINS - 1;
0319 
0320   // debugging messages when early_tbin or late_tbin has a suspicious value
0321   if (early_tbin < 0) {
0322     edm::LogWarning("CSCMotherboard|SuspiciousParameters")
0323         << "Early time bin (early_tbin) smaller than minimum allowed, which is 0. set early_tbin to 0.";
0324     early_tbin = 0;
0325   }
0326   if (late_tbin > max_late_tbin) {
0327     edm::LogWarning("CSCMotherboard|SuspiciousParameters")
0328         << "Late time bin (late_tbin) larger than maximum allowed, which is " << max_late_tbin
0329         << ". set early_tbin to max allowed";
0330     late_tbin = CSCConstants::MAX_LCT_TBINS - 1;
0331   }
0332 
0333   // Start from the vector of all found correlated LCTs and select
0334   // those within the LCT*L1A coincidence window.
0335   int bx_readout = -1;
0336   for (const auto& lct : lctV) {
0337     // extra check on invalid LCTs
0338     if (!lct.isValid()) {
0339       continue;
0340     }
0341 
0342     const int bx = lct.getBX();
0343     // Skip LCTs found too early relative to L1Accept.
0344     if (bx < early_tbin) {
0345       if (infoV > 1)
0346         LogDebug("CSCMotherboard") << " Do not report correlated LCT on key halfstrip " << lct.getStrip()
0347                                    << " and key wire " << lct.getKeyWG() << ": found at bx " << bx
0348                                    << ", whereas the earliest allowed bx is " << early_tbin;
0349       continue;
0350     }
0351 
0352     // Skip LCTs found too late relative to L1Accept.
0353     if (bx > late_tbin) {
0354       if (infoV > 1)
0355         LogDebug("CSCMotherboard") << " Do not report correlated LCT on key halfstrip " << lct.getStrip()
0356                                    << " and key wire " << lct.getKeyWG() << ": found at bx " << bx
0357                                    << ", whereas the latest allowed bx is " << late_tbin;
0358       continue;
0359     }
0360 
0361     // Do not report LCTs found in ME1/A if mpc_block_me1a is set.
0362     if (mpc_block_me1a_ and isME11_ and lct.getStrip() > CSCConstants::MAX_HALF_STRIP_ME1B) {
0363       continue;
0364     }
0365 
0366     // If (readout_earliest_2) take only LCTs in the earliest bx in the read-out window:
0367     // in digi->raw step, LCTs have to be packed into the TMB header, and
0368     // currently there is room just for two.
0369     if (readout_earliest_2) {
0370       if (bx_readout == -1 || bx == bx_readout) {
0371         tmpV.push_back(lct);
0372         if (bx_readout == -1)
0373           bx_readout = bx;
0374       }
0375     }
0376     // if readout_earliest_2 == false, save all LCTs
0377     else {
0378       tmpV.push_back(lct);
0379     }
0380   }
0381 
0382   // do a final check on the LCTs in readout
0383   qualityControl_->checkMultiplicityBX(tmpV);
0384   for (const auto& lct : tmpV) {
0385     qualityControl_->checkValid(lct);
0386     /*std::cout << "\n########################################## Emu LCT ##########################################\n" << std::endl;
0387     std::cout << "Emu LCT: " << lct << std::endl;
0388     std::cout << "\n########################################## THE END ##########################################\n" << std::endl;*/
0389   }
0390 
0391   return tmpV;
0392 }
0393 
0394 std::vector<CSCShowerDigi> CSCMotherboard::readoutShower() const {
0395   unsigned minBXdiff = 2 * tmb_l1a_window_size_;  //impossible value
0396   unsigned minBX = 0;
0397   std::vector<CSCShowerDigi> showerOut;
0398   for (unsigned bx = minbx_readout_; bx < maxbx_readout_; bx++) {
0399     unsigned bx_diff = (bx > bx - CSCConstants::LCT_CENTRAL_BX) ? bx - CSCConstants::LCT_CENTRAL_BX
0400                                                                 : CSCConstants::LCT_CENTRAL_BX - bx;
0401     if (showers_[bx].isValid() and bx_diff < minBXdiff) {
0402       minBXdiff = bx_diff;
0403       minBX = bx;
0404     }
0405   }
0406 
0407   for (unsigned bx = minbx_readout_; bx < maxbx_readout_; bx++)
0408     if (bx == minBX)
0409       showerOut.push_back(showers_[bx]);
0410   return showerOut;
0411 }
0412 
0413 void CSCMotherboard::correlateLCTs(const CSCALCTDigi& bALCT,
0414                                    const CSCALCTDigi& sALCT,
0415                                    const CSCCLCTDigi& bCLCT,
0416                                    const CSCCLCTDigi& sCLCT,
0417                                    CSCCorrelatedLCTDigi& bLCT,
0418                                    CSCCorrelatedLCTDigi& sLCT,
0419                                    int type) const {
0420   CSCALCTDigi bestALCT = bALCT;
0421   CSCALCTDigi secondALCT = sALCT;
0422   CSCCLCTDigi bestCLCT = bCLCT;
0423   CSCCLCTDigi secondCLCT = sCLCT;
0424 
0425   // extra check to make sure that both CLCTs have at least 4 layers
0426   // for regular ALCT-CLCT type LCTs. A check was already done on the
0427   // best CLCT, but not yet on the second best CLCT. The check on best
0428   // CLCT is repeated for completeness
0429   if (bestCLCT.getQuality() <= 3)
0430     bestCLCT.clear();
0431   if (secondCLCT.getQuality() <= 3)
0432     secondCLCT.clear();
0433 
0434   // if the best ALCT/CLCT is valid, but the second ALCT/CLCT is not,
0435   // the information is copied over
0436   copyValidToInValidALCT(bestALCT, secondALCT);
0437   copyValidToInValidCLCT(bestCLCT, secondCLCT);
0438 
0439   // ALCT-only LCTs
0440   const bool bestCase1(alct_trig_enable_ and bestALCT.isValid());
0441   // CLCT-only LCTs
0442   const bool bestCase2(clct_trig_enable_ and bestCLCT.isValid());
0443   /*
0444     Normal case: ALCT-CLCT matched LCTs. We require ALCT and CLCT to be valid.
0445     Optionally, we can check if the ALCT cross the CLCT. This check will always return true
0446     for a valid ALCT-CLCT pair in non-ME1/1 chambers. For ME1/1 chambers, it returns true,
0447     only when the ALCT-CLCT pair crosses and when the parameter "checkAlctCrossClct" is set to True.
0448     It is recommended to keep "checkAlctCrossClct" set to False, so that the EMTF receives
0449     all information, even if it's unphysical.
0450   */
0451   const bool bestCase3(match_trig_enable_ and bestALCT.isValid() and bestCLCT.isValid() and
0452                        doesALCTCrossCLCT(bestALCT, bestCLCT));
0453 
0454   // at least one of the cases must be valid
0455   if (bestCase1 or bestCase2 or bestCase3) {
0456     constructLCTs(bestALCT, bestCLCT, type, 1, bLCT);
0457   }
0458 
0459   // ALCT-only LCTs
0460   const bool secondCase1(alct_trig_enable_ and secondALCT.isValid());
0461   // CLCT-only LCTs
0462   const bool secondCase2(clct_trig_enable_ and secondCLCT.isValid());
0463   /*
0464     Normal case: ALCT-CLCT matched LCTs. We require ALCT and CLCT to be valid.
0465     Optionally, we can check if the ALCT cross the CLCT. This check will always return true
0466     for a valid ALCT-CLCT pair in non-ME1/1 chambers. For ME1/1 chambers, it returns true,
0467     only when the ALCT-CLCT pair crosses and when the parameter "checkAlctCrossClct" is set to True.
0468     It is recommended to keep "checkAlctCrossClct" set to False, so that the EMTF receives
0469     all information, even if it's unphysical.
0470   */
0471   const bool secondCase3(match_trig_enable_ and secondALCT.isValid() and secondCLCT.isValid() and
0472                          doesALCTCrossCLCT(secondALCT, secondCLCT));
0473 
0474   // at least one component must be different in order to consider the secondLCT
0475   if ((secondALCT != bestALCT) or (secondCLCT != bestCLCT)) {
0476     // at least one of the cases must be valid
0477     if (secondCase1 or secondCase2 or secondCase3)
0478       constructLCTs(secondALCT, secondCLCT, type, 2, sLCT);
0479   }
0480 }
0481 
0482 // copy the valid ALCT/CLCT information to the valid ALCT
0483 void CSCMotherboard::copyValidToInValidALCT(CSCALCTDigi& bestALCT, CSCALCTDigi& secondALCT) const {
0484   if (bestALCT.isValid() and !secondALCT.isValid())
0485     secondALCT = bestALCT;
0486 }
0487 
0488 // copy the valid CLCT information to the valid CLCT
0489 void CSCMotherboard::copyValidToInValidCLCT(CSCCLCTDigi& bestCLCT, CSCCLCTDigi& secondCLCT) const {
0490   if (bestCLCT.isValid() and !secondCLCT.isValid())
0491     secondCLCT = bestCLCT;
0492 }
0493 
0494 bool CSCMotherboard::doesALCTCrossCLCT(const CSCALCTDigi& alct, const CSCCLCTDigi& clct) const {
0495   if (ignoreAlctCrossClct_)
0496     return true;
0497   else
0498     return cscOverlap_->doesALCTCrossCLCT(alct, clct);
0499 }
0500 
0501 // This method calculates all the TMB words and then passes them to the
0502 // constructor of correlated LCTs.
0503 void CSCMotherboard::constructLCTs(
0504     const CSCALCTDigi& aLCT, const CSCCLCTDigi& cLCT, int type, int trknmb, CSCCorrelatedLCTDigi& thisLCT) const {
0505   thisLCT.setValid(true);
0506   thisLCT.setType(type);
0507   // make sure to shift the ALCT BX from 8 to 3 and the CLCT BX from 8 to 7!
0508   thisLCT.setALCT(getBXShiftedALCT(aLCT));
0509   thisLCT.setCLCT(getBXShiftedCLCT(cLCT));
0510   thisLCT.setPattern(encodePattern(cLCT.getPattern()));
0511   thisLCT.setMPCLink(0);
0512   thisLCT.setBX0(0);
0513   thisLCT.setSyncErr(0);
0514   thisLCT.setCSCID(theTrigChamber);
0515   thisLCT.setTrknmb(trknmb);
0516   thisLCT.setWireGroup(aLCT.getKeyWG());
0517   thisLCT.setStrip(cLCT.getKeyStrip());
0518   thisLCT.setBend(cLCT.getBend());
0519   // Bunch crossing: get it from cathode LCT if anode LCT is not there.
0520   int bx = aLCT.isValid() ? aLCT.getBX() : cLCT.getBX();
0521   thisLCT.setBX(bx);
0522   thisLCT.setQuality(qualityAssignment_->findQuality(aLCT, cLCT));
0523   if (runCCLUT_) {
0524     thisLCT.setRun3(true);
0525     // 4-bit slope value derived with the CCLUT algorithm
0526     thisLCT.setSlope(cLCT.getSlope());
0527     thisLCT.setQuartStripBit(cLCT.getQuartStripBit());
0528     thisLCT.setEighthStripBit(cLCT.getEighthStripBit());
0529     thisLCT.setRun3Pattern(cLCT.getRun3Pattern());
0530   }
0531 }
0532 
0533 // CLCT pattern number: encodes the pattern number itself
0534 unsigned int CSCMotherboard::encodePattern(const int ptn) const {
0535   const int kPatternBitWidth = 4;
0536 
0537   // In the TMB07 firmware, LCT pattern is just a 4-bit CLCT pattern.
0538   unsigned int pattern = (abs(ptn) & ((1 << kPatternBitWidth) - 1));
0539 
0540   return pattern;
0541 }
0542 
0543 void CSCMotherboard::selectLCTs() {
0544   // in each of the LCT time bins
0545   for (int bx = 0; bx < CSCConstants::MAX_LCT_TBINS; bx++) {
0546     unsigned nLCTs = 0;
0547 
0548     std::vector<CSCCorrelatedLCTDigi> tempV;
0549     // check each of the preferred combinations
0550     for (unsigned int mbx = 0; mbx < match_trig_window_size_; mbx++) {
0551       // select at most 2
0552       for (int i = 0; i < CSCConstants::MAX_LCTS_PER_CSC; i++) {
0553         if (allLCTs_(bx, mbx, i).isValid() and nLCTs < 2) {
0554           tempV.push_back(allLCTs_(bx, mbx, i));
0555           ++nLCTs;
0556         }
0557       }
0558     }
0559     // store the best 2
0560     for (const auto& lct : tempV) {
0561       lctV.push_back(lct);
0562     }
0563   }
0564 
0565   // Show the pre-selected LCTs. They're not final yet. Some selection is done in the readoutLCTs function
0566   if (infoV > 0) {
0567     for (const auto& lct : lctV) {
0568       LogDebug("CSCMotherboard") << "Selected LCT" << lct;
0569     }
0570   }
0571 }
0572 
0573 void CSCMotherboard::sortCLCTByQualBend(int bx_alct, std::vector<unsigned>& clctBxVector) {
0574   //find clct bx range in [centerbx-window_size/2, center_bx+window_size/2]
0575   //Then sort CLCT based quality+bend within the match window
0576   //if two CLCTs from different BX has same qual+bend, the in-time one has higher priority
0577   clctBxVector.clear();
0578   int clctQualBendArray[CSCConstants::MAX_CLCT_TBINS + 1] = {0};
0579   for (unsigned mbx = 0; mbx < match_trig_window_size_; mbx++) {
0580     unsigned bx_clct = bx_alct + preferred_bx_match_[mbx] - CSCConstants::ALCT_CLCT_OFFSET;
0581     int tempQualBend = 0;
0582     if (bx_clct >= CSCConstants::MAX_CLCT_TBINS)
0583       continue;
0584     if (!clctProc->getBestCLCT(bx_clct).isValid()) {
0585       clctQualBendArray[bx_clct] = tempQualBend;
0586       continue;
0587     }
0588     CSCCLCTDigi bestCLCT = clctProc->getBestCLCT(bx_clct);
0589     //for run2 pattern, ignore direction and use &0xe
0590     //for run3, slope=0 is straighest pattern
0591     int clctBend = bestCLCT.isRun3() ? (16 - bestCLCT.getSlope()) : (bestCLCT.getPattern() & 0xe);
0592     //shift quality to left for 4 bits
0593     int clctQualBend = clctBend | (bestCLCT.getQuality() << 5);
0594     clctQualBendArray[bx_clct] = clctQualBend;
0595     if (clctBxVector.empty())
0596       clctBxVector.push_back(bx_clct);
0597     else {
0598       for (auto it = clctBxVector.begin(); it != clctBxVector.end(); it++)
0599         if (clctQualBend > clctQualBendArray[*it]) {  //insert the Bx with better clct
0600           clctBxVector.insert(it, bx_clct);
0601           break;
0602         }
0603     }
0604   }
0605   //fill rest of vector with MAX_CLCT_TBINS
0606   for (unsigned bx = clctBxVector.size(); bx < match_trig_window_size_; bx++)
0607     clctBxVector.push_back(CSCConstants::MAX_CLCT_TBINS);
0608 }
0609 
0610 void CSCMotherboard::checkConfigParameters() {
0611   // Make sure that the parameter values are within the allowed range.
0612 
0613   // Max expected values.
0614   static constexpr unsigned int max_mpc_block_me1a = 1 << 1;
0615   static constexpr unsigned int max_alct_trig_enable = 1 << 1;
0616   static constexpr unsigned int max_clct_trig_enable = 1 << 1;
0617   static constexpr unsigned int max_match_trig_enable = 1 << 1;
0618   static constexpr unsigned int max_match_trig_window_size = 1 << 4;
0619   static constexpr unsigned int max_tmb_l1a_window_size = 1 << 4;
0620 
0621   // Checks.
0622   CSCBaseboard::checkConfigParameters(mpc_block_me1a_, max_mpc_block_me1a, def_mpc_block_me1a, "mpc_block_me1a");
0623   CSCBaseboard::checkConfigParameters(
0624       alct_trig_enable_, max_alct_trig_enable, def_alct_trig_enable, "alct_trig_enable");
0625   CSCBaseboard::checkConfigParameters(
0626       clct_trig_enable_, max_clct_trig_enable, def_clct_trig_enable, "clct_trig_enable");
0627   CSCBaseboard::checkConfigParameters(
0628       match_trig_enable_, max_match_trig_enable, def_match_trig_enable, "match_trig_enable");
0629   CSCBaseboard::checkConfigParameters(
0630       match_trig_window_size_, max_match_trig_window_size, def_match_trig_window_size, "match_trig_window_size");
0631   CSCBaseboard::checkConfigParameters(
0632       tmb_l1a_window_size_, max_tmb_l1a_window_size, def_tmb_l1a_window_size, "tmb_l1a_window_size");
0633   assert(tmb_l1a_window_size_ / 2 <= CSCConstants::LCT_CENTRAL_BX);
0634 }
0635 
0636 void CSCMotherboard::dumpConfigParams() const {
0637   std::ostringstream strm;
0638   strm << "\n";
0639   strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
0640   strm << "+                   TMB configuration parameters:                  +\n";
0641   strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
0642   strm << " mpc_block_me1a [block/not block triggers which come from ME1/A] = " << mpc_block_me1a_ << "\n";
0643   strm << " alct_trig_enable [allow ALCT-only triggers] = " << alct_trig_enable_ << "\n";
0644   strm << " clct_trig_enable [allow CLCT-only triggers] = " << clct_trig_enable_ << "\n";
0645   strm << " match_trig_enable [allow matched ALCT-CLCT triggers] = " << match_trig_enable_ << "\n";
0646   strm << " match_trig_window_size [ALCT-CLCT match window width, in 25 ns] = " << match_trig_window_size_ << "\n";
0647   strm << " tmb_l1a_window_size [L1Accept window width, in 25 ns bins] = " << tmb_l1a_window_size_ << "\n";
0648   strm << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
0649   LogDebug("CSCMotherboard") << strm.str();
0650 }
0651 
0652 CSCALCTDigi CSCMotherboard::getBXShiftedALCT(const CSCALCTDigi& aLCT) const {
0653   CSCALCTDigi aLCT_shifted = aLCT;
0654   aLCT_shifted.setBX(aLCT_shifted.getBX() - (CSCConstants::LCT_CENTRAL_BX - CSCConstants::ALCT_CENTRAL_BX));
0655   return aLCT_shifted;
0656 }
0657 
0658 CSCCLCTDigi CSCMotherboard::getBXShiftedCLCT(const CSCCLCTDigi& cLCT) const {
0659   CSCCLCTDigi cLCT_shifted = cLCT;
0660   cLCT_shifted.setBX(cLCT_shifted.getBX() - CSCConstants::ALCT_CLCT_OFFSET);
0661   return cLCT_shifted;
0662 }
0663 
0664 void CSCMotherboard::matchShowers(CSCShowerDigi* anode_showers, CSCShowerDigi* cathode_showers, bool andlogic) {
0665   CSCShowerDigi ashower, cshower;
0666   bool used_cshower_mask[CSCConstants::MAX_CLCT_TBINS] = {false};
0667   for (unsigned bx = 0; bx < CSCConstants::MAX_ALCT_TBINS; bx++) {
0668     ashower = anode_showers[bx];
0669     cshower = CSCShowerDigi();  //use empty shower digi to initialize cshower
0670     if (ashower.isValid()) {
0671       for (unsigned mbx = 0; mbx < match_trig_window_size_; mbx++) {
0672         int cbx = bx + preferred_bx_match_[mbx] - CSCConstants::ALCT_CLCT_OFFSET;
0673         //check bx range [0, CSCConstants::MAX_LCT_TBINS]
0674         if (cbx < 0 || cbx >= CSCConstants::MAX_CLCT_TBINS)
0675           continue;
0676         if (cathode_showers[cbx].isValid() and not used_cshower_mask[cbx]) {
0677           cshower = cathode_showers[cbx];
0678           used_cshower_mask[cbx] = true;
0679           break;
0680         }
0681       }
0682     } else
0683       cshower = cathode_showers[bx];  //if anode shower is not valid, use the cshower from this bx
0684 
0685     //matched HMT, with and/or logic
0686     unsigned matchHMT = 0;
0687     if (andlogic) {
0688       if (ashower.isTightInTime() and cshower.isTightInTime())
0689         matchHMT = 3;
0690       else if (ashower.isNominalInTime() and cshower.isNominalInTime())
0691         matchHMT = 2;
0692       else if (ashower.isLooseInTime() and cshower.isLooseInTime())
0693         matchHMT = 1;
0694     } else {
0695       if (ashower.isTightInTime() or cshower.isTightInTime())
0696         matchHMT = 3;
0697       else if (ashower.isNominalInTime() or cshower.isNominalInTime())
0698         matchHMT = 2;
0699       else if (ashower.isLooseInTime() or cshower.isLooseInTime())
0700         matchHMT = 1;
0701     }
0702     //LCTShower with showerType = 3
0703     showers_[bx] = CSCShowerDigi(matchHMT & 3,
0704                                  false,
0705                                  ashower.getCSCID(),
0706                                  bx,
0707                                  CSCShowerDigi::ShowerType::kLCTShower,
0708                                  ashower.getWireNHits(),
0709                                  cshower.getComparatorNHits());
0710   }
0711 }
0712 
0713 void CSCMotherboard::encodeHighMultiplicityBits() {
0714   // get the high multiplicity
0715   // for anode this reflects what is already in the anode CSCShowerDigi object
0716   CSCShowerDigi cathode_showers[CSCConstants::MAX_CLCT_TBINS];
0717   CSCShowerDigi anode_showers[CSCConstants::MAX_ALCT_TBINS];
0718   auto cshowers_v = clctProc->getAllShower();
0719   auto ashowers_v = alctProc->getAllShower();
0720 
0721   std::copy(cshowers_v.begin(), cshowers_v.end(), cathode_showers);
0722   std::copy(ashowers_v.begin(), ashowers_v.end(), anode_showers);
0723 
0724   // set the value according to source
0725   switch (thisShowerSource_) {
0726     case 0:
0727       std::copy(std::begin(cathode_showers), std::end(cathode_showers), std::begin(showers_));
0728       break;
0729     case 1:
0730       std::copy(std::begin(anode_showers), std::end(anode_showers), std::begin(showers_));
0731       break;
0732     case 2:
0733       matchShowers(anode_showers, cathode_showers, false);
0734       break;
0735     case 3:
0736       matchShowers(anode_showers, cathode_showers, true);
0737       break;
0738     default:
0739       std::copy(std::begin(anode_showers), std::end(anode_showers), std::begin(showers_));
0740       break;
0741   };
0742 }