text: #include "Geometry/HGCalCommonData/interface/HGCalGeomRotation.h"
  #include "DataFormats/ForwardDetId/interface/ForwardSubdetector.h"
  #include "DataFormats/ForwardDetId/interface/HFNoseTriggerDetId.h"
  #include "DataFormats/ForwardDetId/interface/HGCScintillatorDetId.h"
  #include "DataFormats/ForwardDetId/interface/HGCSiliconDetIdToROC.h"
  #include "DataFormats/ForwardDetId/interface/HGCalDetId.h"
  #include "DataFormats/ForwardDetId/interface/HGCalTriggerDetId.h"
  #include "FWCore/ParameterSet/interface/FileInPath.h"
  #include "L1Trigger/L1THGCal/interface/HGCalTriggerGeometryBase.h"
  #include "DataFormats/ForwardDetId/interface/HGCalTriggerModuleDetId.h"
  #include "DataFormats/ForwardDetId/interface/HGCalTriggerBackendDetId.h"
  #include "DataFormats/ForwardDetId/interface/HFNoseDetIdToModule.h"
  
  #include <fstream>
  #include <vector>
  
  #include <nlohmann/json.hpp>
  using json = nlohmann::json;
  
  class HGCalTriggerGeometryV9Imp3 : public HGCalTriggerGeometryBase {
  public:
    HGCalTriggerGeometryV9Imp3(const edm::ParameterSet& conf);
  
    void initialize(const HGCalGeometry*, const HGCalGeometry*, const HGCalGeometry*) final;
    void initialize(const HGCalGeometry*, const HGCalGeometry*, const HGCalGeometry*, const HGCalGeometry*) final;
    void reset() final;
  
    unsigned getTriggerCellFromCell(const unsigned) const final;
    unsigned getModuleFromCell(const unsigned) const final;
    unsigned getModuleFromTriggerCell(const unsigned) const final;
  
    geom_set getCellsFromTriggerCell(const unsigned) const final;
    geom_set getCellsFromModule(const unsigned) const final;
    geom_set getTriggerCellsFromModule(const unsigned) const final;
  
    geom_ordered_set getOrderedCellsFromModule(const unsigned) const final;
    geom_ordered_set getOrderedTriggerCellsFromModule(const unsigned) const final;
  
    geom_set getNeighborsFromTriggerCell(const unsigned) const final;
  
    geom_set getStage1FpgasFromStage2Fpga(const unsigned) const final;
    geom_set getStage2FpgasFromStage1Fpga(const unsigned) const final;
  
    geom_set getStage1LinksFromStage2Fpga(const unsigned) const final;
    unsigned getStage1FpgaFromStage1Link(const unsigned) const final;
    unsigned getStage2FpgaFromStage1Link(const unsigned) const final;
    geom_set getStage1LinksFromStage1Fpga(const unsigned) const final;
    geom_set getLpgbtsFromStage1Fpga(const unsigned) const final;
    unsigned getStage1FpgaFromLpgbt(const unsigned) const final;
    geom_set getModulesFromLpgbt(const unsigned) const final;
    geom_set getLpgbtsFromModule(const unsigned) const final;
    unsigned getStage1FpgaFromModule(const unsigned module_id) const final;
  
    unsigned getLinksInModule(const unsigned module_id) const final;
    unsigned getModuleSize(const unsigned module_id) const final;
  
    GlobalPoint getTriggerCellPosition(const unsigned) const final;
    GlobalPoint getModulePosition(const unsigned) const final;
  
    bool validCell(const unsigned) const final;
    bool validTriggerCell(const unsigned) const final;
    bool disconnectedModule(const unsigned) const final;
    unsigned lastTriggerLayer() const final { return last_trigger_layer_; }
    unsigned triggerLayer(const unsigned) const final;
  
  private:
    // HSc trigger cell grouping
    unsigned hSc_triggercell_size_ = 2;
    static constexpr unsigned hSc_num_panels_per_sector_ = 12;
    static constexpr unsigned hSc_tcs_per_module_phi_ = 4;
    static constexpr unsigned hSc_front_layers_split_ = 12;
    static constexpr unsigned hSc_back_layers_split_ = 8;
    static constexpr unsigned hSc_layer_for_split_ = 40;
    static constexpr int hSc_tc_layer0_min_ = 24;
    static constexpr int ntc_per_wafer_ = 48;
    static constexpr int nSectors_ = 3;
  
    edm::FileInPath jsonMappingFile_;
  
    // rotation class
    HGCalGeomRotation geom_rotation_120_ = {HGCalGeomRotation::SectorType::Sector120Degrees};
  
    // module related maps
    std::unordered_map<unsigned, unsigned> links_per_module_;
  
    std::unordered_multimap<unsigned, unsigned> stage2_to_stage1links_;
    std::unordered_map<unsigned, bool> stage1links_samesector_;
    std::unordered_map<unsigned, unsigned> stage1link_to_stage2_;
    std::unordered_map<unsigned, unsigned> stage1link_to_stage1_;
    std::unordered_multimap<unsigned, unsigned> stage1_to_stage1links_;
    std::unordered_multimap<unsigned, unsigned> stage1_to_lpgbts_;
    std::unordered_map<unsigned, unsigned> lpgbt_to_stage1_;
    std::unordered_multimap<unsigned, unsigned> lpgbt_to_modules_;
    std::unordered_multimap<unsigned, unsigned> module_to_lpgbts_;
    std::unordered_map<unsigned, unsigned> module_to_stage1_;
  
    // Disconnected modules and layers
    std::unordered_set<unsigned> disconnected_layers_;
    std::vector<unsigned> trigger_layers_;
    std::vector<unsigned> trigger_nose_layers_;
    unsigned last_trigger_layer_ = 0;
  
    // layer offsets
    unsigned heOffset_ = 0;
    unsigned noseLayers_ = 0;
    unsigned totalLayers_ = 0;
  
    void fillMaps();
    bool validCellId(unsigned det, unsigned cell_id) const;
    bool validTriggerCellFromCells(const unsigned) const;
  
    int detIdWaferType(unsigned det, unsigned layer, short waferU, short waferV) const;
    void layerWithoutOffsetAndSubdetId(unsigned& layer, int& subdetId, bool isSilicon) const;
    unsigned packLayerSubdetWaferId(unsigned layer, int subdet, int waferU, int waferV) const;
    void unpackLayerSubdetWaferId(unsigned wafer, unsigned& layer, int& subdet, int& waferU, int& waferV) const;
    HGCalGeomRotation::WaferCentring getWaferCentring(unsigned layer, int subdet) const;
    void etaphiMappingFromSector0(int& ieta, int& iphi, unsigned sector) const;
    unsigned tcEtaphiMappingToSector0(int& tc_ieta, int& tc_iphi) const;
    void getScintillatoriEtaiPhi(int& ieta, int& iphi, int tc_eta, int tc_phi, unsigned layer) const;
    unsigned layerWithOffset(unsigned) const;
    unsigned getNextSector(const unsigned sector) const;
    unsigned getPreviousSector(const unsigned sector) const;
  };
  
  HGCalTriggerGeometryV9Imp3::HGCalTriggerGeometryV9Imp3(const edm::ParameterSet& conf)
      : HGCalTriggerGeometryBase(conf),
        hSc_triggercell_size_(conf.getParameter<unsigned>("ScintillatorTriggerCellSize")),
        jsonMappingFile_(conf.getParameter<edm::FileInPath>("JsonMappingFile")) {
    std::vector<unsigned> tmp_vector = conf.getParameter<std::vector<unsigned>>("DisconnectedLayers");
    std::move(tmp_vector.begin(), tmp_vector.end(), std::inserter(disconnected_layers_, disconnected_layers_.end()));
  }
  
  void HGCalTriggerGeometryV9Imp3::reset() {
    stage2_to_stage1links_.clear();
    stage1links_samesector_.clear();
    stage1link_to_stage2_.clear();
    stage1link_to_stage1_.clear();
    stage1_to_stage1links_.clear();
    stage1_to_lpgbts_.clear();
    lpgbt_to_stage1_.clear();
    lpgbt_to_modules_.clear();
    module_to_lpgbts_.clear();
    module_to_stage1_.clear();
  }
  
  void HGCalTriggerGeometryV9Imp3::initialize(const HGCalGeometry* hgc_ee_geometry,
                                              const HGCalGeometry* hgc_hsi_geometry,
                                              const HGCalGeometry* hgc_hsc_geometry) {
    setEEGeometry(hgc_ee_geometry);
    setHSiGeometry(hgc_hsi_geometry);
    setHScGeometry(hgc_hsc_geometry);
    heOffset_ = eeTopology().dddConstants().layers(true);
    totalLayers_ = heOffset_ + hsiTopology().dddConstants().layers(true);
    trigger_layers_.resize(totalLayers_ + 1);
    trigger_layers_[0] = 0;  // layer number 0 doesn't exist
    unsigned trigger_layer = 1;
    for (unsigned layer = 1; layer < trigger_layers_.size(); layer++) {
      if (disconnected_layers_.find(layer) == disconnected_layers_.end()) {
        // Increase trigger layer number if the layer is not disconnected
        trigger_layers_[layer] = trigger_layer;
        trigger_layer++;
      } else {
        trigger_layers_[layer] = 0;
      }
    }
    last_trigger_layer_ = trigger_layer - 1;
    fillMaps();
  }
  
  void HGCalTriggerGeometryV9Imp3::initialize(const HGCalGeometry* hgc_ee_geometry,
                                              const HGCalGeometry* hgc_hsi_geometry,
                                              const HGCalGeometry* hgc_hsc_geometry,
                                              const HGCalGeometry* hgc_nose_geometry) {
    setEEGeometry(hgc_ee_geometry);
    setHSiGeometry(hgc_hsi_geometry);
    setHScGeometry(hgc_hsc_geometry);
    setNoseGeometry(hgc_nose_geometry);
  
    heOffset_ = eeTopology().dddConstants().layers(true);
    totalLayers_ = heOffset_ + hsiTopology().dddConstants().layers(true);
  
    trigger_layers_.resize(totalLayers_ + 1);
    trigger_layers_[0] = 0;  // layer number 0 doesn't exist
    unsigned trigger_layer = 1;
    for (unsigned layer = 1; layer < trigger_layers_.size(); layer++) {
      if (disconnected_layers_.find(layer) == disconnected_layers_.end()) {
        // Increase trigger layer number if the layer is not disconnected
        trigger_layers_[layer] = trigger_layer;
        trigger_layer++;
      } else {
        trigger_layers_[layer] = 0;
      }
    }
    last_trigger_layer_ = trigger_layer - 1;
    fillMaps();
  
    noseLayers_ = noseTopology().dddConstants().layers(true);
  
    trigger_nose_layers_.resize(noseLayers_ + 1);
    trigger_nose_layers_[0] = 0;  // layer number 0 doesn't exist
    unsigned trigger_nose_layer = 1;
    for (unsigned layer = 1; layer < trigger_nose_layers_.size(); layer++) {
      trigger_nose_layers_[layer] = trigger_nose_layer;
      trigger_nose_layer++;
    }
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getTriggerCellFromCell(const unsigned cell_id) const {
    unsigned det = DetId(cell_id).det();
    unsigned trigger_cell_id = 0;
    // Scintillator
    if (det == DetId::HGCalHSc) {
      // Very rough mapping from cells to TC
      HGCScintillatorDetId cell_sc_id(cell_id);
      int ieta = ((cell_sc_id.ietaAbs() - 1) / hSc_triggercell_size_ + 1) * cell_sc_id.zside();
      int iphi = (cell_sc_id.iphi() - 1) / hSc_triggercell_size_ + 1;
      trigger_cell_id = HGCScintillatorDetId(cell_sc_id.type(), cell_sc_id.layer(), ieta, iphi);
    }
    // HFNose
    else if (det == DetId::Forward && DetId(cell_id).subdetId() == ForwardSubdetector::HFNose) {
      HFNoseDetId cell_nose_id(cell_id);
      trigger_cell_id = HFNoseTriggerDetId(HGCalTriggerSubdetector::HFNoseTrigger,
                                           cell_nose_id.zside(),
                                           cell_nose_id.type(),
                                           cell_nose_id.layer(),
                                           cell_nose_id.waferU(),
                                           cell_nose_id.waferV(),
                                           cell_nose_id.triggerCellU(),
                                           cell_nose_id.triggerCellV());
    }
    // Silicon
    else if (det == DetId::HGCalEE || det == DetId::HGCalHSi) {
      HGCSiliconDetId cell_si_id(cell_id);
      trigger_cell_id = HGCalTriggerDetId(
          det == DetId::HGCalEE ? HGCalTriggerSubdetector::HGCalEETrigger : HGCalTriggerSubdetector::HGCalHSiTrigger,
          cell_si_id.zside(),
          cell_si_id.type(),
          cell_si_id.layer(),
          cell_si_id.waferU(),
          cell_si_id.waferV(),
          cell_si_id.triggerCellU(),
          cell_si_id.triggerCellV());
    }
    return trigger_cell_id;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getModuleFromCell(const unsigned cell_id) const {
    return getModuleFromTriggerCell(getTriggerCellFromCell(cell_id));
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getModuleFromTriggerCell(const unsigned trigger_cell_id) const {
    unsigned det = DetId(trigger_cell_id).det();
    int zside = 0;
    unsigned tc_type = 1;
    unsigned layer = 0;
    unsigned module_id = 0;
  
    // Scintillator
    if (det == DetId::HGCalHSc) {
      HGCScintillatorDetId trigger_cell_sc_id(trigger_cell_id);
      tc_type = trigger_cell_sc_id.type();
      layer = trigger_cell_sc_id.layer();
      zside = trigger_cell_sc_id.zside();
      int tc_eta = trigger_cell_sc_id.ietaAbs();
      int tc_phi = trigger_cell_sc_id.iphi();
      unsigned sector = tcEtaphiMappingToSector0(tc_eta, tc_phi);
      int ieta = 0;
      int iphi = 0;
      getScintillatoriEtaiPhi(ieta, iphi, tc_eta, tc_phi, layer);
      module_id =
          HGCalTriggerModuleDetId(HGCalTriggerSubdetector::HGCalHScTrigger, zside, tc_type, layer, sector, ieta, iphi);
    }
    // HFNose
    else if (det == DetId::HGCalTrigger and
             HGCalTriggerDetId(trigger_cell_id).subdet() == HGCalTriggerSubdetector::HFNoseTrigger) {
      HFNoseTriggerDetId trigger_cell_trig_id(trigger_cell_id);
      tc_type = trigger_cell_trig_id.type();
      layer = trigger_cell_trig_id.layer();
      zside = trigger_cell_trig_id.zside();
      int waferu = trigger_cell_trig_id.waferU();
      int waferv = trigger_cell_trig_id.waferV();
      unsigned sector = geom_rotation_120_.uvMappingToSector0(
          getWaferCentring(layer, HGCalTriggerSubdetector::HFNoseTrigger), waferu, waferv);
      module_id =
          HGCalTriggerModuleDetId(HGCalTriggerSubdetector::HFNoseTrigger, zside, tc_type, layer, sector, waferu, waferv);
    }
    // Silicon
    else {
      HGCalTriggerDetId trigger_cell_trig_id(trigger_cell_id);
      unsigned subdet = trigger_cell_trig_id.subdet();
      tc_type = trigger_cell_trig_id.type();
      layer = trigger_cell_trig_id.layer();
      zside = trigger_cell_trig_id.zside();
      int waferu = trigger_cell_trig_id.waferU();
      int waferv = trigger_cell_trig_id.waferV();
      unsigned sector = geom_rotation_120_.uvMappingToSector0(getWaferCentring(layer, subdet), waferu, waferv);
      module_id = HGCalTriggerModuleDetId(HGCalTriggerSubdetector(subdet), zside, tc_type, layer, sector, waferu, waferv);
    }
    return module_id;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getCellsFromTriggerCell(
      const unsigned trigger_cell_id) const {
    DetId trigger_cell_det_id(trigger_cell_id);
    unsigned det = trigger_cell_det_id.det();
    geom_set cell_det_ids;
    // Scintillator
    if (det == DetId::HGCalHSc) {
      HGCScintillatorDetId trigger_cell_sc_id(trigger_cell_id);
      int ieta0 = (trigger_cell_sc_id.ietaAbs() - 1) * hSc_triggercell_size_ + 1;
      int iphi0 = (trigger_cell_sc_id.iphi() - 1) * hSc_triggercell_size_ + 1;
      for (int ietaAbs = ieta0; ietaAbs < ieta0 + (int)hSc_triggercell_size_; ietaAbs++) {
        int ieta = ietaAbs * trigger_cell_sc_id.zside();
        for (int iphi = iphi0; iphi < iphi0 + (int)hSc_triggercell_size_; iphi++) {
          unsigned cell_id = HGCScintillatorDetId(trigger_cell_sc_id.type(), trigger_cell_sc_id.layer(), ieta, iphi);
          if (validCellId(DetId::HGCalHSc, cell_id))
            cell_det_ids.emplace(cell_id);
        }
      }
    }
    // HFNose
    else if (det == DetId::HGCalTrigger and
             HGCalTriggerDetId(trigger_cell_id).subdet() == HGCalTriggerSubdetector::HFNoseTrigger) {
      HFNoseTriggerDetId trigger_cell_nose_id(trigger_cell_id);
      int layer = trigger_cell_nose_id.layer();
      int zside = trigger_cell_nose_id.zside();
      int type = trigger_cell_nose_id.type();
      int waferu = trigger_cell_nose_id.waferU();
      int waferv = trigger_cell_nose_id.waferV();
      std::vector<int> cellus = trigger_cell_nose_id.cellU();
      std::vector<int> cellvs = trigger_cell_nose_id.cellV();
      for (unsigned ic = 0; ic < cellus.size(); ic++) {
        HFNoseDetId cell_det_id(zside, type, layer, waferu, waferv, cellus[ic], cellvs[ic]);
        cell_det_ids.emplace(cell_det_id);
      }
    }
    // Silicon
    else {
      HGCalTriggerDetId trigger_cell_trig_id(trigger_cell_id);
      unsigned subdet = trigger_cell_trig_id.subdet();
      if (subdet == HGCalTriggerSubdetector::HGCalEETrigger || subdet == HGCalTriggerSubdetector::HGCalHSiTrigger) {
        DetId::Detector cell_det = (subdet == HGCalTriggerSubdetector::HGCalEETrigger ? DetId::HGCalEE : DetId::HGCalHSi);
        int layer = trigger_cell_trig_id.layer();
        int zside = trigger_cell_trig_id.zside();
        int type = trigger_cell_trig_id.type();
        int waferu = trigger_cell_trig_id.waferU();
        int waferv = trigger_cell_trig_id.waferV();
        std::vector<int> cellus = trigger_cell_trig_id.cellU();
        std::vector<int> cellvs = trigger_cell_trig_id.cellV();
        for (unsigned ic = 0; ic < cellus.size(); ic++) {
          HGCSiliconDetId cell_det_id(cell_det, zside, type, layer, waferu, waferv, cellus[ic], cellvs[ic]);
          cell_det_ids.emplace(cell_det_id);
        }
      }
    }
    return cell_det_ids;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getCellsFromModule(const unsigned module_id) const {
    geom_set cell_det_ids;
    geom_set trigger_cells = getTriggerCellsFromModule(module_id);
  
    for (auto trigger_cell_id : trigger_cells) {
      geom_set cells = getCellsFromTriggerCell(trigger_cell_id);
      cell_det_ids.insert(cells.begin(), cells.end());
    }
    return cell_det_ids;
  }
  
  HGCalTriggerGeometryBase::geom_ordered_set HGCalTriggerGeometryV9Imp3::getOrderedCellsFromModule(
      const unsigned module_id) const {
    geom_ordered_set cell_det_ids;
    geom_ordered_set trigger_cells = getOrderedTriggerCellsFromModule(module_id);
    for (auto trigger_cell_id : trigger_cells) {
      geom_set cells = getCellsFromTriggerCell(trigger_cell_id);
      cell_det_ids.insert(cells.begin(), cells.end());
    }
    return cell_det_ids;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getTriggerCellsFromModule(
      const unsigned module_id) const {
    HGCalTriggerModuleDetId hgc_module_id(module_id);
    unsigned subdet = hgc_module_id.triggerSubdetId();
  
    geom_set trigger_cell_det_ids;
    // Scintillator
    if (subdet == HGCalTriggerSubdetector::HGCalHScTrigger) {
      int ieta0 = hgc_module_id.eta();
      int iphi0 = hgc_module_id.phi();
  
      unsigned layer = hgc_module_id.layer();
      etaphiMappingFromSector0(ieta0, iphi0, hgc_module_id.sector());
      int split = (layer > hSc_layer_for_split_) ? hSc_back_layers_split_ : hSc_front_layers_split_;
      if (ieta0 == 1) {
        ieta0 = ieta0 + split;
      } else {
        ieta0 = ieta0 + 1;
      }
      iphi0 = (iphi0 * hSc_tcs_per_module_phi_) + hSc_tc_layer0_min_ + 1;
      int total_tcs = hSc_num_panels_per_sector_ * hSc_tcs_per_module_phi_ * nSectors_;
      if (iphi0 > total_tcs) {
        iphi0 = iphi0 - total_tcs;
      }
  
      int hSc_tcs_per_module_eta = (layer > hSc_layer_for_split_) ? hSc_back_layers_split_ : hSc_front_layers_split_;
  
      for (int ietaAbs = ieta0; ietaAbs < ieta0 + (int)hSc_tcs_per_module_eta; ietaAbs++) {
        int ieta = ietaAbs * hgc_module_id.zside();
        for (int iphi = iphi0; iphi < iphi0 + (int)hSc_tcs_per_module_phi_; iphi++) {
          unsigned trigger_cell_id = HGCScintillatorDetId(hgc_module_id.type(), hgc_module_id.layer(), ieta, iphi);
          if (validTriggerCellFromCells(trigger_cell_id))
            trigger_cell_det_ids.emplace(trigger_cell_id);
        }
      }
    }
    // HFNose
    else if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {
      HFNoseDetId module_nose_id(module_id);
      HFNoseDetIdToModule hfn;
      std::vector<HFNoseTriggerDetId> ids = hfn.getTriggerDetIds(module_nose_id);
      for (auto const& idx : ids) {
        if (validTriggerCellFromCells(idx.rawId()))
          trigger_cell_det_ids.emplace(idx);
      }
    }
    // Silicon
    else {
      HGCSiliconDetIdToROC tc2roc;
      int moduleU = hgc_module_id.moduleU();
      int moduleV = hgc_module_id.moduleV();
      unsigned layer = hgc_module_id.layer();
  
      //Rotate to sector
      geom_rotation_120_.uvMappingFromSector0(getWaferCentring(layer, subdet), moduleU, moduleV, hgc_module_id.sector());
  
      DetId::Detector det = (subdet == HGCalTriggerSubdetector::HGCalEETrigger ? DetId::HGCalEE : DetId::HGCalHSi);
  
      unsigned wafer_type = detIdWaferType(det, layer, moduleU, moduleV);
      int nroc = (wafer_type == HGCSiliconDetId::HGCalFine ? 6 : 3);
      // Loop on ROCs in wafer
      for (int roc = 1; roc <= nroc; roc++) {
        // loop on TCs in ROC
        auto tc_uvs = tc2roc.getTriggerId(roc, wafer_type);
        for (const auto& tc_uv : tc_uvs) {
          HGCalTriggerDetId trigger_cell_id(
              subdet, hgc_module_id.zside(), wafer_type, layer, moduleU, moduleV, tc_uv.first, tc_uv.second);
          if (validTriggerCellFromCells(trigger_cell_id.rawId()))
            trigger_cell_det_ids.emplace(trigger_cell_id);
        }
      }
    }
  
    return trigger_cell_det_ids;
  }
  
  HGCalTriggerGeometryBase::geom_ordered_set HGCalTriggerGeometryV9Imp3::getOrderedTriggerCellsFromModule(
      const unsigned module_id) const {
    HGCalTriggerModuleDetId hgc_module_id(module_id);
    unsigned subdet = hgc_module_id.triggerSubdetId();
  
    geom_ordered_set trigger_cell_det_ids;
  
    // Scintillator
    if (subdet == HGCalTriggerSubdetector::HGCalHScTrigger) {
      int ieta0 = hgc_module_id.eta();
      int iphi0 = hgc_module_id.phi();
  
      unsigned layer = hgc_module_id.layer();
      etaphiMappingFromSector0(ieta0, iphi0, hgc_module_id.sector());
      int split = (layer > hSc_layer_for_split_) ? hSc_back_layers_split_ : hSc_front_layers_split_;
      if (ieta0 == 1) {
        ieta0 = ieta0 + split;
      } else {
        ieta0 = ieta0 + 1;
      }
      iphi0 = (iphi0 * hSc_tcs_per_module_phi_) + hSc_tc_layer0_min_ + 1;
      int total_tcs = hSc_num_panels_per_sector_ * hSc_tcs_per_module_phi_ * nSectors_;
      if (iphi0 > total_tcs) {
        iphi0 = iphi0 - total_tcs;
      }
  
      int hSc_tcs_per_module_eta = (layer > hSc_layer_for_split_) ? hSc_back_layers_split_ : hSc_front_layers_split_;
  
      for (int ietaAbs = ieta0; ietaAbs < ieta0 + (int)hSc_tcs_per_module_eta; ietaAbs++) {
        int ieta = ietaAbs * hgc_module_id.zside();
        for (int iphi = iphi0; iphi < iphi0 + (int)hSc_tcs_per_module_phi_; iphi++) {
          unsigned trigger_cell_id = HGCScintillatorDetId(hgc_module_id.type(), hgc_module_id.layer(), ieta, iphi);
          if (validTriggerCellFromCells(trigger_cell_id))
            trigger_cell_det_ids.emplace(trigger_cell_id);
        }
      }
    }
  
    // HFNose
    else if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {
      HFNoseDetId module_nose_id(module_id);
      HFNoseDetIdToModule hfn;
      std::vector<HFNoseTriggerDetId> ids = hfn.getTriggerDetIds(module_nose_id);
      for (auto const& idx : ids) {
        if (validTriggerCellFromCells(idx.rawId()))
          trigger_cell_det_ids.emplace(idx);
      }
    }
    // Silicon
    else {
      HGCSiliconDetIdToROC tc2roc;
      int moduleU = hgc_module_id.moduleU();
      int moduleV = hgc_module_id.moduleV();
      unsigned layer = hgc_module_id.layer();
  
      //Rotate to sector
      geom_rotation_120_.uvMappingFromSector0(getWaferCentring(layer, subdet), moduleU, moduleV, hgc_module_id.sector());
  
      DetId::Detector det = (subdet == HGCalTriggerSubdetector::HGCalEETrigger ? DetId::HGCalEE : DetId::HGCalHSi);
  
      unsigned wafer_type = detIdWaferType(det, layer, moduleU, moduleV);
      int nroc = (wafer_type == HGCSiliconDetId::HGCalFine ? 6 : 3);
      // Loop on ROCs in wafer
      for (int roc = 1; roc <= nroc; roc++) {
        // loop on TCs in ROC
        auto tc_uvs = tc2roc.getTriggerId(roc, wafer_type);
        for (const auto& tc_uv : tc_uvs) {
          HGCalTriggerDetId trigger_cell_id(
              subdet, hgc_module_id.zside(), wafer_type, layer, moduleU, moduleV, tc_uv.first, tc_uv.second);
          if (validTriggerCellFromCells(trigger_cell_id.rawId()))
            trigger_cell_det_ids.emplace(trigger_cell_id);
        }
      }
    }
  
    return trigger_cell_det_ids;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getNeighborsFromTriggerCell(
      const unsigned trigger_cell_id) const {
    throw cms::Exception("FeatureNotImplemented") << "Neighbor search is not implemented in HGCalTriggerGeometryV9Imp3";
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getLinksInModule(const unsigned module_id) const {
    HGCalTriggerModuleDetId module_det_id(module_id);
    unsigned subdet = module_det_id.triggerSubdetId();
  
    unsigned links = 0;
    // HF Nose
    if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {
      links = 1;
    }
    // TO ADD HFNOSE : getLinksInModule
    // Silicon and Scintillator
    else {
      int packed_module =
          packLayerSubdetWaferId(module_det_id.layer(), subdet, module_det_id.moduleU(), module_det_id.moduleV());
      links = links_per_module_.at(packed_module);
    }
    return links;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getModuleSize(const unsigned module_id) const {
    unsigned nWafers = 1;
    return nWafers;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getNextSector(const unsigned sector) const {
    unsigned next_sector = 0;
    if (sector < 2) {
      next_sector = sector + 1;
    }
    return next_sector;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getPreviousSector(const unsigned sector) const {
    unsigned previous_sector = 2;
    if (sector > 0) {
      previous_sector = sector - 1;
    }
    return previous_sector;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getStage1FpgasFromStage2Fpga(
      const unsigned stage2_id) const {
    geom_set stage1_ids;
  
    geom_set stage1_links = getStage1LinksFromStage2Fpga(stage2_id);
    for (const auto& stage1_link : stage1_links) {
      stage1_ids.emplace(getStage1FpgaFromStage1Link(stage1_link));
    }
  
    return stage1_ids;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getStage2FpgasFromStage1Fpga(
      const unsigned stage1_id) const {
    geom_set stage2_ids;
  
    geom_set stage1_links = getStage1LinksFromStage1Fpga(stage1_id);
    for (const auto& stage1_link : stage1_links) {
      stage2_ids.emplace(getStage2FpgaFromStage1Link(stage1_link));
    }
  
    return stage2_ids;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getStage1LinksFromStage2Fpga(
      const unsigned stage2_id) const {
    geom_set stage1link_ids;
    HGCalTriggerBackendDetId id(stage2_id);
    auto stage2_itrs = stage2_to_stage1links_.equal_range(id.label());
    for (auto stage2_itr = stage2_itrs.first; stage2_itr != stage2_itrs.second; stage2_itr++) {
      unsigned label = stage2_itr->second;
      if (stage1links_samesector_.at(label) == true) {  //link and stage2 FPGA are the same sector
        stage1link_ids.emplace(
            HGCalTriggerBackendDetId(id.zside(), HGCalTriggerBackendDetId::BackendType::Stage1Link, id.sector(), label));
      } else {  //link is from the next sector (anti-clockwise)
        stage1link_ids.emplace(HGCalTriggerBackendDetId(
            id.zside(), HGCalTriggerBackendDetId::BackendType::Stage1Link, getNextSector(id.sector()), label));
      }
    }
  
    return stage1link_ids;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getStage1FpgaFromStage1Link(const unsigned link_id) const {
    HGCalTriggerBackendDetId id(link_id);
    unsigned stage1_label = stage1link_to_stage1_.at(id.label());
  
    return HGCalTriggerBackendDetId(
        id.zside(), HGCalTriggerBackendDetId::BackendType::Stage1FPGA, id.sector(), stage1_label);
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getStage2FpgaFromStage1Link(const unsigned link_id) const {
    HGCalTriggerBackendDetId id(link_id);
    bool same_sector = stage1link_to_stage2_.at(id.label());
    unsigned sector = id.sector();
  
    if (!same_sector) {
      sector = getPreviousSector(sector);
    }
  
    return HGCalTriggerBackendDetId(id.zside(), HGCalTriggerBackendDetId::BackendType::Stage2FPGA, sector, 0);
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getStage1LinksFromStage1Fpga(
      const unsigned stage1_id) const {
    geom_set stage1link_ids;
    HGCalTriggerBackendDetId id(stage1_id);
  
    auto stage1_itrs = stage1_to_stage1links_.equal_range(id.label());
    for (auto stage1_itr = stage1_itrs.first; stage1_itr != stage1_itrs.second; stage1_itr++) {
      stage1link_ids.emplace(HGCalTriggerBackendDetId(
          id.zside(), HGCalTriggerBackendDetId::BackendType::Stage1Link, id.sector(), stage1_itr->second));
    }
  
    return stage1link_ids;
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getLpgbtsFromStage1Fpga(const unsigned stage1_id) const {
    geom_set lpgbt_ids;
    HGCalTriggerBackendDetId id(stage1_id);
  
    auto stage1_itrs = stage1_to_lpgbts_.equal_range(id.label());
    for (auto stage1_itr = stage1_itrs.first; stage1_itr != stage1_itrs.second; stage1_itr++) {
      lpgbt_ids.emplace(HGCalTriggerBackendDetId(
          id.zside(), HGCalTriggerBackendDetId::BackendType::LpGBT, id.sector(), stage1_itr->second));
    }
  
    return lpgbt_ids;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getStage1FpgaFromLpgbt(const unsigned lpgbt_id) const {
    HGCalTriggerBackendDetId id(lpgbt_id);
    unsigned stage1_label = lpgbt_to_stage1_.at(id.label());
  
    return HGCalTriggerBackendDetId(
        id.zside(), HGCalTriggerBackendDetId::BackendType::Stage1FPGA, id.sector(), stage1_label);
  }
  
  HGCalTriggerGeometryBase::geom_set HGCalTriggerGeometryV9Imp3::getModulesFromLpgbt(const unsigned lpgbt_id) const {
    geom_set modules;
    HGCalTriggerBackendDetId id(lpgbt_id);
  
    auto lpgbt_itrs = lpgbt_to_modules_.equal_range(id.label());
    for (auto lpgbt_itr = lpgbt_itrs.first; lpgbt_itr != lpgbt_itrs.second; lpgbt_itr++) {
      unsigned layer = 0;
      int moduleU = 0;
      int moduleV = 0;
      int subdet = 0;
      unpackLayerSubdetWaferId(lpgbt_itr->second, layer, subdet, moduleU, moduleV);
      unsigned det = 0;
      switch (subdet) {
        case HGCalTriggerSubdetector::HGCalEETrigger:
          det = DetId::HGCalEE;
          break;
        case HGCalTriggerSubdetector::HGCalHSiTrigger:
          det = DetId::HGCalHSi;
          break;
        case HGCalTriggerSubdetector::HGCalHScTrigger:
          det = DetId::HGCalHSc;
          break;
        default:
          det = DetId::HGCalEE;
          break;
      }
  
      int type = detIdWaferType(det, layer, moduleU, moduleV);
      modules.emplace(HGCalTriggerModuleDetId(
          HGCalTriggerSubdetector(subdet), id.zside(), type, layer, id.sector(), moduleU, moduleV));
    }
  
    return modules;
  }
  
  HGCalTriggerGeometryV9Imp3::geom_set HGCalTriggerGeometryV9Imp3::getLpgbtsFromModule(const unsigned module_id) const {
    geom_set lpgbt_ids;
    HGCalTriggerModuleDetId id(module_id);
  
    auto module_itrs = module_to_lpgbts_.equal_range(
        packLayerSubdetWaferId(id.layer(), id.triggerSubdetId(), id.moduleU(), id.moduleV()));
    for (auto module_itr = module_itrs.first; module_itr != module_itrs.second; module_itr++) {
      lpgbt_ids.emplace(HGCalTriggerBackendDetId(
          id.zside(), HGCalTriggerBackendDetId::BackendType::LpGBT, id.sector(), module_itr->second));
    }
  
    return lpgbt_ids;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::getStage1FpgaFromModule(const unsigned module_id) const {
    HGCalTriggerModuleDetId id(module_id);
  
    unsigned stage1_label =
        module_to_stage1_.at(packLayerSubdetWaferId(id.layer(), id.triggerSubdetId(), id.moduleU(), id.moduleV()));
  
    return HGCalTriggerBackendDetId(
        id.zside(), HGCalTriggerBackendDetId::BackendType::Stage1FPGA, id.sector(), stage1_label);
  }
  
  GlobalPoint HGCalTriggerGeometryV9Imp3::getTriggerCellPosition(const unsigned trigger_cell_det_id) const {
    unsigned det = DetId(trigger_cell_det_id).det();
  
    // Position: barycenter of the trigger cell.
    Basic3DVector<float> triggerCellVector(0., 0., 0.);
    const auto cell_ids = getCellsFromTriggerCell(trigger_cell_det_id);
    // Scintillator
    if (det == DetId::HGCalHSc) {
      for (const auto& cell : cell_ids) {
        triggerCellVector += hscGeometry()->getPosition(cell).basicVector();
      }
    }
    // HFNose
    else if (det == DetId::HGCalTrigger and
             HGCalTriggerDetId(trigger_cell_det_id).subdet() == HGCalTriggerSubdetector::HFNoseTrigger) {
      for (const auto& cell : cell_ids) {
        HFNoseDetId cellDetId(cell);
        triggerCellVector += noseGeometry()->getPosition(cellDetId).basicVector();
      }
    }
    // Silicon
    else {
      for (const auto& cell : cell_ids) {
        HGCSiliconDetId cellDetId(cell);
        triggerCellVector += (cellDetId.det() == DetId::HGCalEE ? eeGeometry()->getPosition(cellDetId)
                                                                : hsiGeometry()->getPosition(cellDetId))
                                 .basicVector();
      }
    }
    return GlobalPoint(triggerCellVector / cell_ids.size());
  }
  
  GlobalPoint HGCalTriggerGeometryV9Imp3::getModulePosition(const unsigned module_det_id) const {
    unsigned subdet = HGCalTriggerModuleDetId(module_det_id).triggerSubdetId();
    // Position: barycenter of the module.
    Basic3DVector<float> moduleVector(0., 0., 0.);
    const auto cell_ids = getCellsFromModule(module_det_id);
    // Scintillator
    if (subdet == HGCalTriggerSubdetector::HGCalHScTrigger) {
      for (const auto& cell : cell_ids) {
        moduleVector += hscGeometry()->getPosition(cell).basicVector();
      }
    }
    // HFNose
    else if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {
      for (const auto& cell : cell_ids) {
        HFNoseDetId cellDetId(cell);
        moduleVector += noseGeometry()->getPosition(cellDetId).basicVector();
      }
    }  // Silicon
    else {
      for (const auto& cell : cell_ids) {
        HGCSiliconDetId cellDetId(cell);
        moduleVector += (cellDetId.det() == DetId::HGCalEE ? eeGeometry()->getPosition(cellDetId)
                                                           : hsiGeometry()->getPosition(cellDetId))
                            .basicVector();
      }
    }
  
    return GlobalPoint(moduleVector / cell_ids.size());
  }
  
  void HGCalTriggerGeometryV9Imp3::fillMaps() {
    // read json mapping file
    json mapping_config;
    std::ifstream json_input_file(jsonMappingFile_.fullPath());
    if (!json_input_file.is_open()) {
      throw cms::Exception("MissingDataFile") << "Cannot open HGCalTriggerGeometry L1TMapping file\n";
    }
    json_input_file >> mapping_config;
  
    try {
      //Stage 2 to Stage 1 links mapping
      for (unsigned stage2_id = 0; stage2_id < mapping_config.at("Stage2").size(); stage2_id++) {
        for (unsigned link_id = 0; link_id < mapping_config.at("Stage2").at(stage2_id).at("Stage1Links").size();
             link_id++) {
          stage2_to_stage1links_.emplace(stage2_id, link_id);
          stage1links_samesector_.emplace(
              link_id, mapping_config.at("Stage2").at(stage2_id).at("Stage1Links").at(link_id).at("SameSector"));
        }
      }
    } catch (const json::exception& e) {
      edm::LogError("HGCalTriggerGeometryV9Imp3")
          << "The mapping input json file does not have the expected structure for the Stage2 block";
    }
  
    try {
      for (unsigned link_id = 0; link_id < mapping_config.at("Stage1Links").size(); link_id++) {
        //Stage 1 links to Stage 1 FPGAs mapping
        stage1link_to_stage1_.emplace(link_id, mapping_config.at("Stage1Links").at(link_id).at("Stage1"));
  
        //Stage 1 links to Stage 2 mapping
        stage1link_to_stage2_.emplace(link_id, mapping_config.at("Stage1Links").at(link_id).at("Stage2SameSector"));
      }
    } catch (const json::exception& e) {
      edm::LogError("HGCalTriggerGeometryV9Imp3")
          << "The mapping input json file does not have the expected structure for the Stage1Links block";
    }
  
    try {
      for (unsigned stage1_id = 0; stage1_id < mapping_config.at("Stage1").size(); stage1_id++) {
        //Stage 1 to Stage 1 links mapping
        for (auto& link_id : mapping_config.at("Stage1").at(stage1_id).at("Stage1Links")) {
          stage1_to_stage1links_.emplace(stage1_id, link_id);
        }
  
        //Stage 1 to lpgbt mapping
        for (auto& lpgbt_id : mapping_config.at("Stage1").at(stage1_id).at("lpgbts")) {
          stage1_to_lpgbts_.emplace(stage1_id, lpgbt_id);
        }
      }
  
    } catch (const json::exception& e) {
      edm::LogError("HGCalTriggerGeometryV9Imp3")
          << "The mapping input json file does not have the expected structure for the Stage1 block";
    }
  
    try {
      for (unsigned lpgbt_id = 0; lpgbt_id < mapping_config.at("lpgbt").size(); lpgbt_id++) {
        //lpgbt to Stage 1 mapping
        unsigned stage1_id = mapping_config.at("lpgbt").at(lpgbt_id).at("Stage1");
        lpgbt_to_stage1_.emplace(lpgbt_id, stage1_id);
  
        //lpgbt to module mapping
        for (auto& modules : mapping_config.at("lpgbt").at(lpgbt_id).at("Modules")) {
          unsigned layer = modules.at("layer");
          int subdetId = 0;
          bool isSilicon = modules.at("isSilicon");
          layerWithoutOffsetAndSubdetId(layer, subdetId, isSilicon);
          unsigned packed_value = packLayerSubdetWaferId(layer, subdetId, modules.at("u"), modules.at("v"));
          lpgbt_to_modules_.emplace(lpgbt_id, packed_value);
  
          //fill subsiduary module to stage 1 mapping
          auto result = module_to_stage1_.emplace(packed_value, stage1_id);
          if (result.second == false &&
              stage1_id != result.first->second) {  //check that the stage1_id is the same as in the existing map
            edm::LogError("HGCalTriggerGeometryV9Imp3") << "One module is connected to two separate Stage1 FPGAs";
          }
        }
      }
  
    } catch (const json::exception& e) {
      edm::LogError("HGCalTriggerGeometryV9Imp3")
          << "The mapping input json file does not have the expected structure for the lpGBT block";
    }
  
    try {
      //module to lpgbt mapping
      for (unsigned module = 0; module < mapping_config.at("Module").size(); module++) {
        unsigned num_elinks = 0;  //Sum number of e-links in each module over lpGBTs
        unsigned layer = mapping_config.at("Module").at(module).at("layer");
        unsigned moduleU = mapping_config.at("Module").at(module).at("u");
        unsigned moduleV = mapping_config.at("Module").at(module).at("v");
        bool isSilicon = mapping_config.at("Module").at(module).at("isSilicon");
        int subdetId = 0;
        layerWithoutOffsetAndSubdetId(layer, subdetId, isSilicon);
  
        for (auto& lpgbt : mapping_config.at("Module").at(module).at("lpgbts")) {
          module_to_lpgbts_.emplace(packLayerSubdetWaferId(layer, subdetId, moduleU, moduleV), lpgbt.at("id"));
          num_elinks += unsigned(lpgbt.at("nElinks"));
        }
        int packed_module = packLayerSubdetWaferId(layer, subdetId, moduleU, moduleV);
        links_per_module_.emplace(packed_module, num_elinks);
      }
    } catch (const json::exception& e) {
      edm::LogError("HGCalTriggerGeometryV9Imp3")
          << "The mapping input json file does not have the expected structure for the Module block";
    }
  
    json_input_file.close();
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::packLayerSubdetWaferId(unsigned layer, int subdet, int waferU, int waferV) const {
    unsigned packed_value = 0;
  
    packed_value |=
        ((waferU & HGCalTriggerModuleDetId::kHGCalModuleUMask) << HGCalTriggerModuleDetId::kHGCalModuleUOffset);
    packed_value |=
        ((waferV & HGCalTriggerModuleDetId::kHGCalModuleVMask) << HGCalTriggerModuleDetId::kHGCalModuleVOffset);
    packed_value |= ((subdet & HGCalTriggerModuleDetId::kHGCalTriggerSubdetMask)
                     << HGCalTriggerModuleDetId::kHGCalTriggerSubdetOffset);
    packed_value |= ((layer & HGCalTriggerModuleDetId::kHGCalLayerMask) << HGCalTriggerModuleDetId::kHGCalLayerOffset);
    return packed_value;
  }
  
  void HGCalTriggerGeometryV9Imp3::unpackLayerSubdetWaferId(
      unsigned wafer, unsigned& layer, int& subdet, int& waferU, int& waferV) const {
    waferU = (wafer >> HGCalTriggerModuleDetId::kHGCalModuleUOffset) & HGCalTriggerModuleDetId::kHGCalModuleUMask;
    waferV = (wafer >> HGCalTriggerModuleDetId::kHGCalModuleVOffset) & HGCalTriggerModuleDetId::kHGCalModuleVMask;
    subdet =
        (wafer >> HGCalTriggerModuleDetId::kHGCalTriggerSubdetOffset) & HGCalTriggerModuleDetId::kHGCalTriggerSubdetMask;
    layer = (wafer >> HGCalTriggerModuleDetId::kHGCalLayerOffset) & HGCalTriggerModuleDetId::kHGCalLayerMask;
  }
  
  void HGCalTriggerGeometryV9Imp3::etaphiMappingFromSector0(int& ieta, int& iphi, unsigned sector) const {
    if (sector == 0) {
      return;
    }
    if (sector == 2) {
      iphi = iphi + hSc_num_panels_per_sector_;
    } else if (sector == 1) {
      iphi = iphi + (2 * hSc_num_panels_per_sector_);
    }
  }
  
  HGCalGeomRotation::WaferCentring HGCalTriggerGeometryV9Imp3::getWaferCentring(unsigned layer, int subdet) const {
    if (subdet == HGCalTriggerSubdetector::HGCalEETrigger) {  // CE-E
      return HGCalGeomRotation::WaferCentring::WaferCentred;
    } else if (subdet == HGCalTriggerSubdetector::HGCalHSiTrigger) {
      if ((layer % 2) == 1) {  // CE-H Odd
        return HGCalGeomRotation::WaferCentring::CornerCentredY;
      } else {  // CE-H Even
        return HGCalGeomRotation::WaferCentring::CornerCentredMercedes;
      }
    } else if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {  //HFNose
      return HGCalGeomRotation::WaferCentring::WaferCentred;
    } else {
      edm::LogError("HGCalTriggerGeometryV9Imp3")
          << "HGCalTriggerGeometryV9Imp3: trigger sub-detector expected to be silicon";
      return HGCalGeomRotation::WaferCentring::WaferCentred;
    }
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::tcEtaphiMappingToSector0(int& tc_ieta, int& tc_iphi) const {
    unsigned sector = 0;
  
    if (tc_iphi > hSc_tc_layer0_min_ && tc_iphi <= hSc_tc_layer0_min_ + ntc_per_wafer_) {
      sector = 0;
    } else if (tc_iphi > hSc_tc_layer0_min_ + ntc_per_wafer_ && tc_iphi <= hSc_tc_layer0_min_ + 2 * ntc_per_wafer_) {
      sector = 2;
    } else {
      sector = 1;
    }
  
    if (sector == 0) {
      tc_iphi = tc_iphi - hSc_tc_layer0_min_;
    } else if (sector == 2) {
      tc_iphi = tc_iphi - (hSc_tc_layer0_min_ + ntc_per_wafer_);
    } else if (sector == 1) {
      if (tc_iphi <= hSc_tc_layer0_min_) {
        tc_iphi = tc_iphi + nSectors_ * ntc_per_wafer_;
      }
      tc_iphi = tc_iphi - (nSectors_ * ntc_per_wafer_ - hSc_tc_layer0_min_);
    }
  
    return sector;
  }
  
  void HGCalTriggerGeometryV9Imp3::getScintillatoriEtaiPhi(
      int& ieta, int& iphi, int tc_eta, int tc_phi, unsigned layer) const {
    iphi = (tc_phi - 1) / hSc_tcs_per_module_phi_;  //Phi index 1-12
  
    int split = hSc_front_layers_split_;
    if (layer > hSc_layer_for_split_) {
      split = hSc_back_layers_split_;
    }
    if (tc_eta <= split) {
      ieta = 0;
    } else {
      ieta = 1;
    }
  }
  
  bool HGCalTriggerGeometryV9Imp3::validTriggerCell(const unsigned trigger_cell_id) const {
    return validTriggerCellFromCells(trigger_cell_id);
  }
  
  bool HGCalTriggerGeometryV9Imp3::disconnectedModule(const unsigned module_id) const {
    bool disconnected = false;
    HGCalTriggerModuleDetId id(module_id);
    if (module_to_stage1_.find(packLayerSubdetWaferId(id.layer(), id.triggerSubdetId(), id.moduleU(), id.moduleV())) ==
        module_to_stage1_.end()) {
      disconnected = true;
    }
    if (disconnected_layers_.find(layerWithOffset(module_id)) != disconnected_layers_.end()) {
      disconnected = true;
    }
    return disconnected;
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::triggerLayer(const unsigned id) const {
    unsigned layer = layerWithOffset(id);
  
    if (DetId(id).det() == DetId::HGCalTrigger and
        HGCalTriggerDetId(id).subdet() == HGCalTriggerSubdetector::HFNoseTrigger) {
      if (layer >= trigger_nose_layers_.size())
        return 0;
      return trigger_nose_layers_[layer];
    }
    if (layer >= trigger_layers_.size())
      return 0;
    return trigger_layers_[layer];
  }
  
  bool HGCalTriggerGeometryV9Imp3::validCell(unsigned cell_id) const {
    bool is_valid = false;
    unsigned det = DetId(cell_id).det();
    switch (det) {
      case DetId::HGCalEE:
        is_valid = eeTopology().valid(cell_id);
        break;
      case DetId::HGCalHSi:
        is_valid = hsiTopology().valid(cell_id);
        break;
      case DetId::HGCalHSc:
        is_valid = hscTopology().valid(cell_id);
        break;
      case DetId::Forward:
        is_valid = noseTopology().valid(cell_id);
        break;
      default:
        is_valid = false;
        break;
    }
    return is_valid;
  }
  
  bool HGCalTriggerGeometryV9Imp3::validTriggerCellFromCells(const unsigned trigger_cell_id) const {
    // Check the validity of a trigger cell with the
    // validity of the cells. One valid cell in the
    // trigger cell is enough to make the trigger cell
    // valid.
    const geom_set cells = getCellsFromTriggerCell(trigger_cell_id);
    bool is_valid = false;
    for (const auto cell_id : cells) {
      unsigned det = DetId(cell_id).det();
      is_valid |= validCellId(det, cell_id);
      if (is_valid)
        break;
    }
    return is_valid;
  }
  
  bool HGCalTriggerGeometryV9Imp3::validCellId(unsigned subdet, unsigned cell_id) const {
    bool is_valid = false;
    switch (subdet) {
      case DetId::HGCalEE:
        is_valid = eeTopology().valid(cell_id);
        break;
      case DetId::HGCalHSi:
        is_valid = hsiTopology().valid(cell_id);
        break;
      case DetId::HGCalHSc:
        is_valid = hscTopology().valid(cell_id);
        break;
      case DetId::Forward:
        is_valid = noseTopology().valid(cell_id);
        break;
      default:
        is_valid = false;
        break;
    }
    return is_valid;
  }
  
  int HGCalTriggerGeometryV9Imp3::detIdWaferType(unsigned det, unsigned layer, short waferU, short waferV) const {
    int wafer_type = 0;
    switch (det) {
      case DetId::HGCalEE:
        wafer_type = eeTopology().dddConstants().getTypeHex(layer, waferU, waferV);
        break;
      case DetId::HGCalHSi:
        wafer_type = hsiTopology().dddConstants().getTypeHex(layer, waferU, waferV);
        break;
      case DetId::HGCalHSc:
        wafer_type = hscTopology().dddConstants().getTypeTrap(layer);
        break;
      default:
        break;
    };
    return wafer_type;
  }
  
  void HGCalTriggerGeometryV9Imp3::layerWithoutOffsetAndSubdetId(unsigned& layer, int& subdetId, bool isSilicon) const {
    if (!isSilicon) {
      layer = layer - heOffset_;
      subdetId = HGCalTriggerSubdetector::HGCalHScTrigger;
    } else {
      if (layer > heOffset_) {
        subdetId = HGCalTriggerSubdetector::HGCalHSiTrigger;
        layer = layer - heOffset_;
      } else {
        subdetId = HGCalTriggerSubdetector::HGCalEETrigger;
      }
    }
  }
  
  unsigned HGCalTriggerGeometryV9Imp3::layerWithOffset(unsigned id) const {
    unsigned det = DetId(id).det();
    unsigned layer = 0;
  
    if (det == DetId::HGCalTrigger) {
      unsigned subdet = HGCalTriggerDetId(id).subdet();
      if (subdet == HGCalTriggerSubdetector::HGCalEETrigger) {
        layer = HGCalTriggerDetId(id).layer();
      } else if (subdet == HGCalTriggerSubdetector::HGCalHSiTrigger) {
        layer = heOffset_ + HGCalTriggerDetId(id).layer();
      } else if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {
        layer = HFNoseTriggerDetId(id).layer();
      }
    } else if (det == DetId::HGCalHSc) {
      layer = heOffset_ + HGCScintillatorDetId(id).layer();
    } else if (det == DetId::Forward) {
      unsigned subdet = HGCalTriggerModuleDetId(id).triggerSubdetId();
      if (subdet == HGCalTriggerSubdetector::HGCalEETrigger) {
        layer = HGCalTriggerModuleDetId(id).layer();
      } else if (subdet == HGCalTriggerSubdetector::HGCalHSiTrigger ||
                 subdet == HGCalTriggerSubdetector::HGCalHScTrigger) {
        layer = heOffset_ + HGCalDetId(id).layer();
      } else if (subdet == HGCalTriggerSubdetector::HFNoseTrigger) {
        layer = HGCalTriggerModuleDetId(id).layer();
      }
    }
    return layer;
  }
  
  DEFINE_EDM_PLUGIN(HGCalTriggerGeometryFactory, HGCalTriggerGeometryV9Imp3, "HGCalTriggerGeometryV9Imp3");
  