File indexing completed on 2024-09-10 02:59:04
0001 #include "SimCalorimetry/EcalSelectiveReadoutAlgos/interface/EcalSelectiveReadoutSuppressor.h"
0002 #include "SimCalorimetry/EcalSelectiveReadoutAlgos/interface/EcalSelectiveReadout.h"
0003 #include "DataFormats/EcalDigi/interface/EEDataFrame.h"
0004 #include "DataFormats/EcalDigi/interface/EBDataFrame.h"
0005 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0006
0007
0008 #include "Geometry/Records/interface/CaloGeometryRecord.h"
0009 #include "Geometry/CaloGeometry/interface/CaloSubdetectorGeometry.h"
0010 #include "Geometry/CaloGeometry/interface/CaloCellGeometry.h"
0011 #include "Geometry/CaloGeometry/interface/CaloGeometry.h"
0012 #include "DataFormats/GeometryVector/interface/GlobalPoint.h"
0013
0014 #include "DataFormats/EcalDetId/interface/EcalSubdetector.h"
0015
0016
0017 #include "FWCore/Utilities/interface/Exception.h"
0018
0019 #include <cmath>
0020 #include <iostream>
0021 #include <limits>
0022 #include <memory>
0023
0024 using namespace boost;
0025 using namespace std;
0026
0027 const int EcalSelectiveReadoutSuppressor::nFIRTaps = 6;
0028
0029 EcalSelectiveReadoutSuppressor::EcalSelectiveReadoutSuppressor(const edm::ParameterSet& params,
0030 edm::ConsumesCollector iC)
0031 : ttThresOnCompressedEt_(false), ievt_(0), geoToken_(iC.esConsumes()) {
0032 int defTtf = params.getParameter<int>("defaultTtf");
0033 if (defTtf < 0 || defTtf > 7) {
0034 throw cms::Exception("InvalidParameter") << "Value of EcalSelectiveReadoutProducer module parameter defaultTtf, "
0035 << defaultTtf_ << ", is out of the valid range 0..7\n";
0036 } else {
0037 defaultTtf_ = (EcalSelectiveReadout::ttFlag_t)defTtf;
0038 }
0039
0040 trigPrimBypass_ = params.getParameter<bool>("trigPrimBypass");
0041 trigPrimBypassMode_ = params.getParameter<int>("trigPrimBypassMode");
0042 trigPrimBypassWithPeakFinder_ = params.getParameter<bool>("trigPrimBypassWithPeakFinder");
0043 trigPrimBypassLTH_ = params.getParameter<double>("trigPrimBypassLTH");
0044 trigPrimBypassHTH_ = params.getParameter<double>("trigPrimBypassHTH");
0045 if (trigPrimBypass_) {
0046 edm::LogWarning("Digitization") << "Beware a simplified trigger primitive "
0047 "computation is used for the ECAL selective readout";
0048 if (trigPrimBypassMode_ != 0 && trigPrimBypassMode_ != 1) {
0049 throw cms::Exception("InvalidParameter")
0050 << "Invalid value for EcalSelectiveReadoutProducer parameter 'trigPrimBypassMode_'."
0051 " Valid values are 0 and 1.\n";
0052 }
0053 ttThresOnCompressedEt_ = (trigPrimBypassMode_ == 1);
0054 }
0055 }
0056
0057 void EcalSelectiveReadoutSuppressor::setSettings(const EcalSRSettings* settings) {
0058 firstFIRSample = settings->ecalDccZs1stSample_[0];
0059 weights = settings->dccNormalizedWeights_[0];
0060 symetricZS = settings->symetricZS_[0];
0061 actions_ = settings->actions_;
0062
0063
0064
0065 if (actions_.size() == 4) {
0066 for (int i = 0; i < 4; ++i) {
0067 actions_.push_back(actions_[i] | 0x4);
0068 }
0069 }
0070
0071 bool actionValid = actions_.size() == 8;
0072 for (size_t i = 0; i < actions_.size(); ++i) {
0073 if (actions_[i] < 0 || actions_[i] > 7)
0074 actionValid = false;
0075 }
0076
0077 if (!actionValid) {
0078 throw cms::Exception("InvalidParameter")
0079 << "EcalSelectiveReadoutProducer module parameter 'actions' is "
0080 "not valid. It must be a vector of 8 integer values comprised between 0 and 7\n";
0081 }
0082
0083 double adcToGeV = settings->ebDccAdcToGeV_;
0084 thrUnit[BARREL] = adcToGeV / 4.;
0085
0086 adcToGeV = settings->eeDccAdcToGeV_;
0087 thrUnit[ENDCAP] = adcToGeV / 4.;
0088 ecalSelectiveReadout = std::make_unique<EcalSelectiveReadout>(settings->deltaEta_[0], settings->deltaPhi_[0]);
0089 const int eb = 0;
0090 const int ee = 1;
0091 initCellThresholds(settings->srpLowInterestChannelZS_[eb],
0092 settings->srpLowInterestChannelZS_[ee],
0093 settings->srpHighInterestChannelZS_[eb],
0094 settings->srpHighInterestChannelZS_[ee]);
0095 }
0096
0097 void EcalSelectiveReadoutSuppressor::setTriggerMap(const EcalTrigTowerConstituentsMap* map) {
0098 theTriggerMap = map;
0099 ecalSelectiveReadout->setTriggerMap(map);
0100 }
0101
0102 void EcalSelectiveReadoutSuppressor::setElecMap(const EcalElectronicsMapping* map) {
0103 ecalSelectiveReadout->setElecMap(map);
0104 }
0105
0106 void EcalSelectiveReadoutSuppressor::setGeometry(const CaloGeometry* caloGeometry) {
0107 #ifndef ECALSELECTIVEREADOUT_NOGEOM
0108 ecalSelectiveReadout->setGeometry(caloGeometry);
0109 #endif
0110 }
0111
0112 void EcalSelectiveReadoutSuppressor::initCellThresholds(double barrelLowInterest,
0113 double endcapLowInterest,
0114 double barrelHighInterest,
0115 double endcapHighInterest) {
0116
0117
0118 int lowInterestThr[2];
0119
0120 int highInterestThr[2];
0121
0122
0123 lowInterestThr[BARREL] = internalThreshold(barrelLowInterest, BARREL);
0124
0125
0126
0127 highInterestThr[BARREL] = internalThreshold(barrelHighInterest, BARREL);
0128
0129
0130
0131 lowInterestThr[ENDCAP] = internalThreshold(endcapLowInterest, ENDCAP);
0132
0133
0134
0135 highInterestThr[ENDCAP] = internalThreshold(endcapHighInterest, ENDCAP);
0136
0137
0138
0139 const int FORCED_MASK = EcalSelectiveReadout::FORCED_MASK;
0140
0141 for (int iSubDet = 0; iSubDet < 2; ++iSubDet) {
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161 for (size_t i = 0; i < 8; ++i) {
0162 srFlags[iSubDet][i] = actions_[i];
0163 if ((actions_[i] & ~FORCED_MASK) == 0)
0164 zsThreshold[iSubDet][i] = numeric_limits<int>::max();
0165 else if ((actions_[i] & ~FORCED_MASK) == 1)
0166 zsThreshold[iSubDet][i] = lowInterestThr[iSubDet];
0167 else if ((actions_[i] & ~FORCED_MASK) == 2)
0168 zsThreshold[iSubDet][i] = highInterestThr[iSubDet];
0169 else
0170 zsThreshold[iSubDet][i] = numeric_limits<int>::min();
0171 }
0172
0173
0174
0175
0176 }
0177 }
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189 int EcalSelectiveReadoutSuppressor::internalThreshold(double thresholdInGeV, int iSubDet) const {
0190 double thr_ = thresholdInGeV / thrUnit[iSubDet];
0191
0192
0193
0194
0195
0196 int thr;
0197 if (thr_ >= 0x7FF + .5) {
0198 thr = numeric_limits<int>::max();
0199 } else if (thr_ <= -0x7FF - .5) {
0200 thr = numeric_limits<int>::min();
0201 } else {
0202 thr = lround(thr_);
0203 }
0204 return thr;
0205 }
0206
0207
0208
0209 bool EcalSelectiveReadoutSuppressor::accept(edm::DataFrame const& frame, int thr) {
0210
0211 const vector<int>& w = getFIRWeigths();
0212
0213
0214 int acc = 0;
0215 bool gain12saturated = false;
0216 const int gain12 = 0x01;
0217 const int lastFIRSample = firstFIRSample + nFIRTaps - 1;
0218
0219 int iWeight = 0;
0220 for (int iSample = firstFIRSample - 1; iSample < lastFIRSample; ++iSample, ++iWeight) {
0221 if (iSample >= 0 && iSample < (int)frame.size()) {
0222 EcalMGPASample sample(frame[iSample]);
0223 if (sample.gainId() != gain12)
0224 gain12saturated = true;
0225
0226
0227 acc += sample.adc() * w[iWeight];
0228 } else {
0229 edm::LogWarning("DccFir") << __FILE__ << ":" << __LINE__
0230 << ": Not enough samples in data frame or 'ecalDccZs1stSample' module "
0231 "parameter is not valid...";
0232 }
0233 }
0234
0235 if (symetricZS) {
0236 if (acc < 0)
0237 acc = -acc;
0238 }
0239
0240
0241
0242
0243
0244
0245 acc = ((acc + (1 << 30)) >> 8) - (1 << (30 - 8));
0246
0247
0248
0249
0250
0251 const bool result = (acc >= thr) || gain12saturated;
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261 return result;
0262 }
0263
0264 void EcalSelectiveReadoutSuppressor::run(const edm::EventSetup& eventSetup,
0265 const EcalTrigPrimDigiCollection& trigPrims,
0266 EBDigiCollection& barrelDigis,
0267 EEDigiCollection& endcapDigis) {
0268 EBDigiCollection selectedBarrelDigis;
0269 EEDigiCollection selectedEndcapDigis;
0270
0271 run(eventSetup, trigPrims, barrelDigis, endcapDigis, &selectedBarrelDigis, &selectedEndcapDigis, nullptr, nullptr);
0272
0273
0274 barrelDigis.swap(selectedBarrelDigis);
0275 endcapDigis.swap(selectedEndcapDigis);
0276 }
0277
0278 void EcalSelectiveReadoutSuppressor::run(const edm::EventSetup& eventSetup,
0279 const EcalTrigPrimDigiCollection& trigPrims,
0280 const EBDigiCollection& barrelDigis,
0281 const EEDigiCollection& endcapDigis,
0282 EBDigiCollection* selectedBarrelDigis,
0283 EEDigiCollection* selectedEndcapDigis,
0284 EBSrFlagCollection* ebSrFlags,
0285 EESrFlagCollection* eeSrFlags) {
0286 ++ievt_;
0287 if (!trigPrimBypass_ || ttThresOnCompressedEt_) {
0288 setTtFlags(trigPrims);
0289 } else {
0290 setTtFlags(eventSetup, barrelDigis, endcapDigis);
0291 }
0292
0293 ecalSelectiveReadout->runSelectiveReadout0(ttFlags);
0294
0295 if (selectedBarrelDigis) {
0296 selectedBarrelDigis->reserve(barrelDigis.size() / 20);
0297
0298
0299 for (EBDigiCollection::const_iterator digiItr = barrelDigis.begin(); digiItr != barrelDigis.end(); ++digiItr) {
0300 int interestLevel = ecalSelectiveReadout->getCrystalInterest(EBDigiCollection::DetId(digiItr->id())) &
0301 ~EcalSelectiveReadout::FORCED_MASK;
0302 if (accept(*digiItr, zsThreshold[BARREL][interestLevel])) {
0303 selectedBarrelDigis->push_back(digiItr->id(), digiItr->begin());
0304 }
0305 }
0306 }
0307
0308
0309 if (selectedEndcapDigis) {
0310 selectedEndcapDigis->reserve(endcapDigis.size() / 20);
0311 for (EEDigiCollection::const_iterator digiItr = endcapDigis.begin(); digiItr != endcapDigis.end(); ++digiItr) {
0312 int interestLevel = ecalSelectiveReadout->getCrystalInterest(EEDigiCollection::DetId(digiItr->id())) &
0313 ~EcalSelectiveReadout::FORCED_MASK;
0314 if (accept(*digiItr, zsThreshold[ENDCAP][interestLevel])) {
0315 selectedEndcapDigis->push_back(digiItr->id(), digiItr->begin());
0316 }
0317 }
0318 }
0319
0320 if (ievt_ <= 10) {
0321 int neb = (selectedBarrelDigis ? selectedBarrelDigis->size() : 0);
0322 if (selectedEndcapDigis)
0323 LogDebug("EcalSelectiveReadout")
0324
0325 << "Number of EB digis passing the SR: " << neb << " / " << barrelDigis.size() << "\n";
0326 if (selectedEndcapDigis)
0327 LogDebug("EcalSelectiveReadout")
0328
0329 << "\nNumber of EE digis passing the SR: " << selectedEndcapDigis->size() << " / " << endcapDigis.size()
0330 << "\n";
0331 }
0332
0333 if (ebSrFlags)
0334 ebSrFlags->reserve(34 * 72);
0335 if (eeSrFlags)
0336 eeSrFlags->reserve(624);
0337
0338 for (int iZ = -1; iZ <= 1; iZ += 2) {
0339
0340 for (unsigned iEta = 1; iEta <= nBarrelTriggerTowersInEta / 2; ++iEta) {
0341 for (unsigned iPhi = 1; iPhi <= nTriggerTowersInPhi; ++iPhi) {
0342 const EcalTrigTowerDetId id(iZ, EcalBarrel, iEta, iPhi);
0343 EcalSelectiveReadout::towerInterest_t interest = ecalSelectiveReadout->getTowerInterest(id);
0344 if (interest < 0) {
0345 throw cms::Exception("EcalSelectiveReadout") << __FILE__ << ":" << __LINE__ << ": "
0346 << "unknown SR flag. for "
0347 << " TT " << id << ". Most probably a bug.";
0348 }
0349 int flag;
0350
0351
0352
0353 flag = srFlags[BARREL][interest];
0354
0355 if (ebSrFlags)
0356 ebSrFlags->push_back(EBSrFlag(id, flag));
0357 }
0358 }
0359
0360
0361 EcalScDetId id;
0362 for (int iX = 1; iX <= 20; ++iX) {
0363 for (int iY = 1; iY <= 20; ++iY) {
0364 if (EcalScDetId::validDetId(iX, iY, iZ))
0365 id = EcalScDetId(iX, iY, iZ);
0366 else
0367 continue;
0368
0369 EcalSelectiveReadout::towerInterest_t interest = ecalSelectiveReadout->getSuperCrystalInterest(id);
0370
0371 if (interest >= 0) {
0372 int flag;
0373
0374
0375
0376 flag = srFlags[ENDCAP][interest];
0377
0378 if (eeSrFlags)
0379 eeSrFlags->push_back(EESrFlag(id, flag));
0380 } else if (iX < 9 || iX > 12 || iY < 9 || iY > 12) {
0381 edm::LogError("EcalSelectiveReadout") << __FILE__ << ":" << __LINE__ << ": "
0382 << "negative interest in EE for SC " << id << "\n";
0383 }
0384 }
0385 }
0386 }
0387 }
0388
0389 void EcalSelectiveReadoutSuppressor::setTtFlags(const EcalTrigPrimDigiCollection& trigPrims) {
0390 for (size_t iEta0 = 0; iEta0 < nTriggerTowersInEta; ++iEta0) {
0391 for (size_t iPhi0 = 0; iPhi0 < nTriggerTowersInPhi; ++iPhi0) {
0392 ttFlags[iEta0][iPhi0] = defaultTtf_;
0393 }
0394 }
0395 for (EcalTrigPrimDigiCollection::const_iterator trigPrim = trigPrims.begin(); trigPrim != trigPrims.end();
0396 ++trigPrim) {
0397 int iEta = trigPrim->id().ieta();
0398 unsigned int iEta0;
0399 if (iEta < 0) {
0400 iEta0 = iEta + nTriggerTowersInEta / 2;
0401 } else {
0402 iEta0 = iEta + nTriggerTowersInEta / 2 - 1;
0403 }
0404
0405 unsigned int iPhi0 = trigPrim->id().iphi() - 1;
0406
0407 if (!ttThresOnCompressedEt_) {
0408 ttFlags[iEta0][iPhi0] = (EcalSelectiveReadout::ttFlag_t)trigPrim->ttFlag();
0409 } else {
0410 int compressedEt = trigPrim->compressedEt();
0411 if (compressedEt < trigPrimBypassLTH_) {
0412 ttFlags[iEta0][iPhi0] = EcalSelectiveReadout::TTF_LOW_INTEREST;
0413 } else if (compressedEt < trigPrimBypassHTH_) {
0414 ttFlags[iEta0][iPhi0] = EcalSelectiveReadout::TTF_MID_INTEREST;
0415 } else {
0416 ttFlags[iEta0][iPhi0] = EcalSelectiveReadout::TTF_HIGH_INTEREST;
0417 }
0418 }
0419 }
0420 }
0421
0422 vector<int> EcalSelectiveReadoutSuppressor::getFIRWeigths() {
0423 if (firWeights.empty()) {
0424 firWeights = vector<int>(nFIRTaps, 0);
0425 const static int maxWeight = 0xEFF;
0426 for (unsigned i = 0; i < min((size_t)nFIRTaps, weights.size()); ++i) {
0427 firWeights[i] = lround(weights[i] * (1 << 10));
0428 if (abs(firWeights[i]) > maxWeight) {
0429 firWeights[i] = firWeights[i] < 0 ? -maxWeight : maxWeight;
0430 }
0431 }
0432 }
0433 return firWeights;
0434 }
0435
0436 void EcalSelectiveReadoutSuppressor::setTtFlags(const edm::EventSetup& es,
0437 const EBDigiCollection& ebDigis,
0438 const EEDigiCollection& eeDigis) {
0439 double trigPrim[nTriggerTowersInEta][nTriggerTowersInPhi];
0440
0441
0442
0443
0444 const CaloSubdetectorGeometry* eeGeometry = nullptr;
0445 const CaloSubdetectorGeometry* ebGeometry = nullptr;
0446
0447 auto const& geo = es.getData(geoToken_);
0448 eeGeometry = geo.getSubdetectorGeometry(DetId::Ecal, EcalEndcap);
0449 ebGeometry = geo.getSubdetectorGeometry(DetId::Ecal, EcalBarrel);
0450
0451
0452
0453 bzero(trigPrim, sizeof(trigPrim));
0454
0455 for (EBDigiCollection::const_iterator it = ebDigis.begin(); it != ebDigis.end(); ++it) {
0456 EBDataFrame frame(*it);
0457 const EcalTrigTowerDetId& ttId = theTriggerMap->towerOf(frame.id());
0458
0459
0460
0461
0462 const int iTTEta0 = iTTEta2cIndex(ttId.ieta());
0463 const int iTTPhi0 = iTTPhi2cIndex(ttId.iphi());
0464 double theta = ebGeometry->getGeometry(frame.id())->getPosition().theta();
0465 double e = frame2Energy(frame);
0466 if (!trigPrimBypassWithPeakFinder_ || ((frame2Energy(frame, -1) < e) && (frame2Energy(frame, 1) < e))) {
0467 trigPrim[iTTEta0][iTTPhi0] += e * sin(theta);
0468 }
0469 }
0470
0471 for (EEDigiCollection::const_iterator it = eeDigis.begin(); it != eeDigis.end(); ++it) {
0472 EEDataFrame frame(*it);
0473 const EcalTrigTowerDetId& ttId = theTriggerMap->towerOf(frame.id());
0474 const int iTTEta0 = iTTEta2cIndex(ttId.ieta());
0475 const int iTTPhi0 = iTTPhi2cIndex(ttId.iphi());
0476
0477
0478
0479
0480 double theta = eeGeometry->getGeometry(frame.id())->getPosition().theta();
0481 double e = frame2Energy(frame);
0482 if (!trigPrimBypassWithPeakFinder_ || ((frame2Energy(frame, -1) < e) && (frame2Energy(frame, 1) < e))) {
0483 trigPrim[iTTEta0][iTTPhi0] += e * sin(theta);
0484 }
0485 }
0486
0487
0488 int innerTTEtas[] = {0, 1, 54, 55};
0489 for (unsigned iRing = 0; iRing < sizeof(innerTTEtas) / sizeof(innerTTEtas[0]); ++iRing) {
0490 int iTTEta0 = innerTTEtas[iRing];
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501
0502
0503
0504
0505
0506 for (unsigned iTTPhi0 = 0; iTTPhi0 < nTriggerTowersInPhi - 1; iTTPhi0 += 2) {
0507 double et = .5 * (trigPrim[iTTEta0][iTTPhi0] + trigPrim[iTTEta0][iTTPhi0 + 1]);
0508
0509
0510
0511 trigPrim[iTTEta0][iTTPhi0] = et;
0512 trigPrim[iTTEta0][iTTPhi0 + 1] = et;
0513 }
0514 }
0515
0516 for (unsigned iTTEta0 = 0; iTTEta0 < nTriggerTowersInEta; ++iTTEta0) {
0517 for (unsigned iTTPhi0 = 0; iTTPhi0 < nTriggerTowersInPhi; ++iTTPhi0) {
0518 if (trigPrim[iTTEta0][iTTPhi0] > trigPrimBypassHTH_) {
0519 ttFlags[iTTEta0][iTTPhi0] = EcalSelectiveReadout::TTF_HIGH_INTEREST;
0520 } else if (trigPrim[iTTEta0][iTTPhi0] > trigPrimBypassLTH_) {
0521 ttFlags[iTTEta0][iTTPhi0] = EcalSelectiveReadout::TTF_MID_INTEREST;
0522 } else {
0523 ttFlags[iTTEta0][iTTPhi0] = EcalSelectiveReadout::TTF_LOW_INTEREST;
0524 }
0525
0526
0527
0528
0529 }
0530 }
0531 }
0532
0533 template <class T>
0534 double EcalSelectiveReadoutSuppressor::frame2Energy(const T& frame, int offset) const {
0535
0536
0537 double weights[] = {0., -1 / 3., -1 / 3., -1 / 3., 0., 1.};
0538
0539 double adc2GeV = 0.;
0540 if (typeid(frame) == typeid(EBDataFrame)) {
0541 adc2GeV = 0.035;
0542 } else if (typeid(frame) == typeid(EEDataFrame)) {
0543 adc2GeV = 0.060;
0544 } else {
0545
0546 cerr << "Severe error. " << __FILE__ << ":" << __LINE__ << ": "
0547 << "this is a bug. Please report it.\n";
0548 }
0549
0550 double acc = 0;
0551
0552 const int n = min<int>(frame.size(), sizeof(weights) / sizeof(weights[0]));
0553
0554 double gainInv[] = {12., 1., 6., 12.};
0555
0556
0557 for (int i = offset; i < n; ++i) {
0558 int iframe = i + offset;
0559 if (iframe >= 0 && iframe < frame.size()) {
0560 acc += weights[i] * frame[iframe].adc() * gainInv[frame[iframe].gainId()] * adc2GeV;
0561
0562
0563
0564 }
0565 }
0566
0567 return acc;
0568 }
0569
0570 void EcalSelectiveReadoutSuppressor::printTTFlags(ostream& os, int iEvent, bool withHeader) const {
0571 const char tccFlagMarker[] = {'?', '.', 'S', '?', 'C', 'E', 'E', 'E', 'E'};
0572 const int nEta = EcalSelectiveReadout::nTriggerTowersInEta;
0573 const int nPhi = EcalSelectiveReadout::nTriggerTowersInPhi;
0574
0575 if (withHeader) {
0576 os << "# TCC flag map\n#\n"
0577 "# +-->Phi "
0578 << tccFlagMarker[1 + 0]
0579 << ": 000 (low interest)\n"
0580 "# | "
0581 << tccFlagMarker[1 + 1]
0582 << ": 001 (mid interest)\n"
0583 "# | "
0584 << tccFlagMarker[1 + 2]
0585 << ": 010 (not valid)\n"
0586 "# V Eta "
0587 << tccFlagMarker[1 + 3]
0588 << ": 011 (high interest)\n"
0589 "# "
0590 << tccFlagMarker[1 + 4] << ": 1xx forced readout (Hw error)\n";
0591 }
0592
0593 if (iEvent >= 0) {
0594 os << "#\n#Event " << iEvent << "\n";
0595 }
0596
0597 for (int iEta = 0; iEta < nEta; ++iEta) {
0598 for (int iPhi = 0; iPhi < nPhi; ++iPhi) {
0599 os << tccFlagMarker[ttFlags[iEta][iPhi] + 1];
0600 }
0601 os << "\n";
0602 }
0603 }