CMS 3D CMS Logo

EcalDeadChannelRecoveryNN.cc
Go to the documentation of this file.
3 
5 
6 #include <iostream>
7 #include <TMath.h>
8 #include <TDirectory.h>
9 
10 template <typename T>
12  for (int id = 0; id < 9; ++id) {
13  ctx_[id].mlp = nullptr;
14  }
15 
16  this->load();
17 }
18 
19 template <typename T>
21  for (int id = 0; id < 9; ++id) {
22  if (ctx_[id].mlp) {
23  // @TODO segfaults for an uknown reason
24  // delete ctx[id].mlp;
25  // delete ctx[id].tree;
26  }
27  }
28 }
29 
30 template <>
32 {
34 }
35 
36 template <>
38 {
40 }
41 
42 template <typename T>
45 
46  auto t = std::make_unique<TTree>("t", "dummy MLP tree");
47  t->SetDirectory(nullptr);
48 
49  t->Branch("z1", &(ctx.tmp[0]), "z1/D");
50  t->Branch("z2", &(ctx.tmp[1]), "z2/D");
51  t->Branch("z3", &(ctx.tmp[2]), "z3/D");
52  t->Branch("z4", &(ctx.tmp[3]), "z4/D");
53  t->Branch("z5", &(ctx.tmp[4]), "z5/D");
54  t->Branch("z6", &(ctx.tmp[5]), "z6/D");
55  t->Branch("z7", &(ctx.tmp[6]), "z7/D");
56  t->Branch("z8", &(ctx.tmp[7]), "z8/D");
57  t->Branch("zf", &(ctx.tmp[8]), "zf/D");
58 
59  ctx.tree = std::move(t);
60  //The TMultiLayerPerceptron will create a TEventList
61  // and it will attach itself to the gDirectory
62  // which will be some random TFile in the job
63  // Need to reset gDirectory back to what it was to
64  // avoid causing other code in other modules from
65  // crashing.
66  {
67  auto resetGDirectory = [](TDirectory* oldDir) {gDirectory = oldDir;};
68  std::unique_ptr<TDirectory, decltype(resetGDirectory)> guard(gDirectory, resetGDirectory);
69  gDirectory = nullptr;
70  ctx.mlp =
71  std::make_unique<TMultiLayerPerceptron>("@z1,@z2,@z3,@z4,@z5,@z6,@z7,@z8:10:5:zf", ctx.tree.get());
72  }
73  ctx.mlp->LoadWeights(path.c_str());
74 }
75 
76 template <>
78  std::string p = "RecoLocalCalo/EcalDeadChannelRecoveryAlgos/data/NNWeights/";
79 
80  this->load_file(ctx_[CellID::CC], p + "EB_ccNNWeights.txt");
81  this->load_file(ctx_[CellID::RR], p + "EB_rrNNWeights.txt");
82  this->load_file(ctx_[CellID::LL], p + "EB_llNNWeights.txt");
83  this->load_file(ctx_[CellID::UU], p + "EB_uuNNWeights.txt");
84  this->load_file(ctx_[CellID::DD], p + "EB_ddNNWeights.txt");
85  this->load_file(ctx_[CellID::RU], p + "EB_ruNNWeights.txt");
86  this->load_file(ctx_[CellID::RD], p + "EB_rdNNWeights.txt");
87  this->load_file(ctx_[CellID::LU], p + "EB_luNNWeights.txt");
88  this->load_file(ctx_[CellID::LD], p + "EB_ldNNWeights.txt");
89 }
90 
91 template <>
93  std::string p = "RecoLocalCalo/EcalDeadChannelRecoveryAlgos/data/NNWeights/";
94 
95  this->load_file(ctx_[CellID::CC], p + "EE_ccNNWeights.txt");
96  this->load_file(ctx_[CellID::RR], p + "EE_rrNNWeights.txt");
97  this->load_file(ctx_[CellID::LL], p + "EE_llNNWeights.txt");
98  this->load_file(ctx_[CellID::UU], p + "EE_uuNNWeights.txt");
99  this->load_file(ctx_[CellID::DD], p + "EE_ddNNWeights.txt");
100  this->load_file(ctx_[CellID::RU], p + "EE_ruNNWeights.txt");
101  this->load_file(ctx_[CellID::RD], p + "EE_rdNNWeights.txt");
102  this->load_file(ctx_[CellID::LU], p + "EE_luNNWeights.txt");
103  this->load_file(ctx_[CellID::LD], p + "EE_ldNNWeights.txt");
104 }
105 
106 template <typename T>
107 double EcalDeadChannelRecoveryNN<T>::recover(const T id, const EcalRecHitCollection& hit_collection, double Sum8Cut, bool *AcceptFlag)
108 {
109  // use the correct probability matrix
110  typedef class EcalCrystalMatrixProbality<T> P;
111 
112  double NewEnergy = 0.0;
113 
114  double NewEnergy_RelMC = 0.0;
115  double NewEnergy_RelDC = 0.0;
116 
117  double MNxN_RelMC[9] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } ;
118  double MNxN_RelDC[9] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } ;
119 
120  double sum8 = 0.0;
121 
122  double sum8_RelMC = makeNxNMatrice_RelMC(id, hit_collection, MNxN_RelMC,AcceptFlag);
123  double sum8_RelDC = makeNxNMatrice_RelDC(id, hit_collection, MNxN_RelDC,AcceptFlag);
124 
125  // Only if "AcceptFlag" is true call the ANN
126  if ( *AcceptFlag ) {
127  if (sum8_RelDC > Sum8Cut && sum8_RelMC > Sum8Cut) {
128  NewEnergy_RelMC = estimateEnergy(MNxN_RelMC);
129  NewEnergy_RelDC = estimateEnergy(MNxN_RelDC);
130 
131  // Matrices "MNxN_RelMC" and "MNxN_RelDC" have now the full set of energies, the original ones plus
132  // whatever "estimates" by the ANN for the "dead" xtal. Use those full matrices and calculate probabilities.
133  //
134  double SumMNxN_RelMC = MNxN_RelMC[LU] + MNxN_RelMC[UU] + MNxN_RelMC[RU] +
135  MNxN_RelMC[LL] + MNxN_RelMC[CC] + MNxN_RelMC[RR] +
136  MNxN_RelMC[LD] + MNxN_RelMC[DD] + MNxN_RelMC[RD] ;
137 
138  double frMNxN_RelMC[9]; for (int i=0; i<9; i++) { frMNxN_RelMC[i] = MNxN_RelMC[i] / SumMNxN_RelMC ; }
139 
140  double prMNxN_RelMC = P::Diagonal( frMNxN_RelMC[LU] ) * P::UpDown( frMNxN_RelMC[UU] ) * P::Diagonal( frMNxN_RelMC[RU] ) *
141  P::ReftRight( frMNxN_RelMC[LL] ) * P::Central( frMNxN_RelMC[CC] ) * P::ReftRight( frMNxN_RelMC[RR] ) *
142  P::Diagonal( frMNxN_RelMC[LD] ) * P::UpDown( frMNxN_RelMC[DD] ) * P::Diagonal( frMNxN_RelMC[RD] ) ;
143 
144  double SumMNxN_RelDC = MNxN_RelDC[LU] + MNxN_RelDC[UU] + MNxN_RelDC[RU] +
145  MNxN_RelDC[LL] + MNxN_RelDC[CC] + MNxN_RelDC[RR] +
146  MNxN_RelDC[LD] + MNxN_RelDC[DD] + MNxN_RelDC[RD] ;
147 
148  double frMNxN_RelDC[9]; for (int i=0; i<9; i++) { frMNxN_RelDC[i] = MNxN_RelDC[i] / SumMNxN_RelDC ; }
149 
150  double prMNxN_RelDC = P::Diagonal( frMNxN_RelDC[LU] ) * P::UpDown( frMNxN_RelDC[UU] ) * P::Diagonal( frMNxN_RelDC[RU] ) *
151  P::ReftRight( frMNxN_RelDC[LL] ) * P::Central( frMNxN_RelDC[CC] ) * P::ReftRight( frMNxN_RelDC[RR] ) *
152  P::Diagonal( frMNxN_RelDC[LD] ) * P::UpDown( frMNxN_RelDC[DD] ) * P::Diagonal( frMNxN_RelDC[RD] ) ;
153 
154  if ( prMNxN_RelDC > prMNxN_RelMC ) { NewEnergy = NewEnergy_RelDC ; sum8 = sum8_RelDC ; }
155  if ( prMNxN_RelDC <= prMNxN_RelMC ) { NewEnergy = NewEnergy_RelMC ; sum8 = sum8_RelMC ; }
156 
157 
158  // If the return value of "CorrectDeadChannelsNN" is one of the followin negative values then
159  // it corresponds to an error condition. See "CorrectDeadChannelsNN.cc" for possible values.
160  if ( NewEnergy == -1000000.0 ||
161  NewEnergy == -1000001.0 ||
162  NewEnergy == -2000000.0 ||
163  NewEnergy == -3000000.0 ||
164  NewEnergy == -3000001.0 ) { *AcceptFlag=false ; NewEnergy = 0.0 ; }
165  }
166  }
167 
168  // Protect against non physical high values
169  // From the distribution of (max.cont.xtal / Sum8) we get as limit 5 (hard) and 10 (softer)
170  // Choose 10 as highest possible energy to be assigned to the dead channel under any scenario.
171  if ( NewEnergy > 10.0 * sum8 ) { *AcceptFlag=false ; NewEnergy = 0.0 ; }
172 
173  return NewEnergy;
174 }
175 
176 template <typename T>
177 double EcalDeadChannelRecoveryNN<T>::estimateEnergy(double *M3x3Input, double epsilon) {
178  int missing[9];
179  int missing_index = 0;
180 
181  for (int i = 0; i < 9; i++) {
182  if (TMath::Abs(M3x3Input[i]) < epsilon) {
183  missing[missing_index++] = i;
184  } else {
185  // Generally the "dead" cells are allowed to have negative energies (since they will be estimated by the ANN anyway).
186  // But all the remaining "live" ones must have positive values otherwise the logarithm fails.
187 
188  if (M3x3Input[i] < 0.0) { return -2000000.0; }
189  }
190  }
191 
192  // Currently EXACTLY ONE AND ONLY ONE dead cell is corrected. Return -1000000.0 if zero DC's detected and -101.0 if more than one DC's exist.
193  int idxDC = -1 ;
194  if (missing_index == 0) { return -1000000.0; } // Zero DC's were detected
195  if (missing_index > 1) { return -1000001.0; } // More than one DC's detected.
196  if (missing_index == 1) { idxDC = missing[0]; }
197 
198  // Arrange inputs into an array of 8, excluding the dead cell;
199  int input_order[9] = { CC, RR, LL, UU, DD, RU, RD, LU, LD };
200  int input_index = 0;
201  Double_t input[8];
202 
203  for (int id : input_order) {
204  if (id == idxDC)
205  continue;
206 
207  input[input_index++] = TMath::Log(M3x3Input[id]);
208  }
209 
210  // Select the case to apply the appropriate NN and return the result.
211  M3x3Input[idxDC] = TMath::Exp(ctx_[idxDC].mlp->Evaluate(0, input));
212  return M3x3Input[idxDC];
213 }
214 
215 template <typename DetIdT>
216 double EcalDeadChannelRecoveryNN<DetIdT>::makeNxNMatrice_RelMC(DetIdT itID, const EcalRecHitCollection& hit_collection, double *MNxN_RelMC, bool* AcceptFlag) {
217  // Since ANN corrects within a 3x3 window, the possible candidate 3x3 windows that contain
218  // the "dead" crystal form a 5x5 window around it (totaly eight 3x3 windows overlapping).
219  // Get this 5x5 and locate the Max.Contain.Crystal within.
220 
221  // Get the 5x5 window around the "dead" crystal -> vector "NxNaroundDC"
222  std::vector<DetId> NxNaroundDC = topology_->getWindow(itID, 5, 5);
223 
224  DetIdT CellMax ; // Create a null DetId
225  double EnergyMax = 0.0;
226 
227  // Loop over all cells in the vector "NxNaroundDC", and for each cell find it's energy
228  // (from the EcalRecHits collection). Use this energy to detect the Max.Cont.Crystal.
229  std::vector<DetId>::const_iterator theCells;
230 
231  for (theCells = NxNaroundDC.begin(); theCells != NxNaroundDC.end(); ++theCells) {
232  DetIdT cell = DetIdT(*theCells);
233 
234  if (! cell.null()) {
235  EcalRecHitCollection::const_iterator goS_it = hit_collection.find(cell);
236 
237  if ( goS_it != hit_collection.end() && goS_it->energy() >= EnergyMax ) {
238  EnergyMax = goS_it->energy();
239  CellMax = cell;
240  }
241  }
242  }
243 
244  // No Max.Cont.Crystal found, return back with no changes.
245  if ( CellMax.null() ) { *AcceptFlag=false ; return 0.0 ; }
246 
247 #if 1
248  // Not a smart check, because why not just get 4x4 matrix and have a guaranteed hit?
249 
250  // Check that the "dead" crystal belongs to the 3x3 around Max.Cont.Crystal
251  bool dcIn3x3 = false ;
252 
253  std::vector<DetId> NxNaroundMaxCont = topology_->getWindow(CellMax,3,3);
254  std::vector<DetId>::const_iterator testCell;
255  for (testCell = NxNaroundMaxCont.begin(); testCell != NxNaroundMaxCont.end(); ++testCell) {
256  if ( itID == DetIdT(*testCell) ) { dcIn3x3 = true ; }
257  }
258 
259  // If the "dead" crystal is outside the 3x3 then do nothing.
260  if (!dcIn3x3) { *AcceptFlag=false ; return 0.0 ; }
261 #endif
262 
263  return makeNxNMatrice_RelDC(CellMax, hit_collection, MNxN_RelMC, AcceptFlag);
264 }
265 
266 template <>
267 double EcalDeadChannelRecoveryNN<EBDetId>::makeNxNMatrice_RelDC(EBDetId id, const EcalRecHitCollection& hit_collection, double *MNxN, bool* AcceptFlag) {
268  // Make an ANN 3x3 energy matrix around the crystal.
269  // If the "dead" crystal is at the EB boundary (+/- 85) do nothing since we need a full 3x3 around it.
270  if ( id.ieta() == 85 || id.ieta() == -85 ) { *AcceptFlag=false ; return 0.0 ; }
271 
272  // Define the ieta and iphi steps (zero, plus, minus)
273  int ietaZ = id.ieta() ;
274  int ietaP = ( ietaZ == -1 ) ? 1 : ietaZ + 1 ;
275  int ietaN = ( ietaZ == 1 ) ? -1 : ietaZ - 1 ;
276 
277  int iphiZ = id.iphi() ;
278  int iphiP = ( iphiZ == 360 ) ? 1 : iphiZ + 1 ;
279  int iphiN = ( iphiZ == 1 ) ? 360 : iphiZ - 1 ;
280 
281  for (int i=0; i<9; i++) { MNxN[i] = 0.0 ; }
282 
283  // Loop over all cells in the vector "window", and fill the MNxN matrix
284  // to be passed to the ANN for prediction.
285  std::vector<DetId> window = topology_->getWindow(id, 3, 3);
286 
287  std::vector<DetId>::const_iterator itCells;
288  for (itCells = window.begin(); itCells != window.end(); ++itCells) {
289  EBDetId cell = EBDetId(*itCells);
290 
291  if (! cell.null()) {
292  EcalRecHitCollection::const_iterator goS_it = hit_collection.find(cell);
293 
294  if (goS_it != hit_collection.end()) {
295  double energy = goS_it->energy();
296 
297  if ( cell.ieta() == ietaP && cell.iphi() == iphiP ) { MNxN[RU] = energy; }
298  else if ( cell.ieta() == ietaP && cell.iphi() == iphiZ ) { MNxN[RR] = energy; }
299  else if ( cell.ieta() == ietaP && cell.iphi() == iphiN ) { MNxN[RD] = energy; }
300 
301  else if ( cell.ieta() == ietaZ && cell.iphi() == iphiP ) { MNxN[UU] = energy; }
302  else if ( cell.ieta() == ietaZ && cell.iphi() == iphiZ ) { MNxN[CC] = energy; }
303  else if ( cell.ieta() == ietaZ && cell.iphi() == iphiN ) { MNxN[DD] = energy; }
304 
305  else if ( cell.ieta() == ietaN && cell.iphi() == iphiP ) { MNxN[LU] = energy; }
306  else if ( cell.ieta() == ietaN && cell.iphi() == iphiZ ) { MNxN[LL] = energy; }
307  else if ( cell.ieta() == ietaN && cell.iphi() == iphiN ) { MNxN[LD] = energy; }
308 
309  else { *AcceptFlag=false ; return 0.0 ;}
310  }
311  }
312  }
313 
314  // Get the sum of 8
315  double ESUMis = 0.0 ;
316  for (int i=0; i<9; i++) { ESUMis = ESUMis + MNxN[i] ; }
317 
318  *AcceptFlag=true ;
319  return ESUMis;
320 }
321 
322 template <>
323 double EcalDeadChannelRecoveryNN<EEDetId>::makeNxNMatrice_RelDC(EEDetId itID,const EcalRecHitCollection& hit_collection, double *MNxN, bool* AcceptFlag) {
324  // Make an ANN 3x3 energy matrix around the crystal.
325  // If the "dead" crystal is at the EB boundary (+/- 85) do nothing since we need a full 3x3 around it.
326 
327  // If the "dead" crystal is at the EE boundary (inner or outer ring) do nothing since we need a full 3x3 around it.
328  if ( EEDetId::isNextToRingBoundary(itID) ) { *AcceptFlag=false ; return 0.0 ; }
329 
330  // Define the ix and iy steps (zero, plus, minus)
331  int ixZ = itID.ix() ;
332  int ixP = ixZ + 1 ;
333  int ixN = ixZ - 1 ;
334 
335  int iyZ = itID.iy() ;
336  int iyP = iyZ + 1 ;
337  int iyN = iyZ - 1 ;
338 
339  for (int i=0; i<9; i++) { MNxN[i] = 0.0 ; }
340 
341  // Take the "dead" crystal as reference and get the 3x3 around it.
342  std::vector<DetId> NxNaroundRefXtal = topology_->getWindow(itID,3,3);
343 
344  // Loop over all cells in the vector "NxNaroundRefXtal", and fill the MNxN matrix
345  // to be passed to the ANN for prediction.
346  std::vector<DetId>::const_iterator itCells;
347 
348  for (itCells = NxNaroundRefXtal.begin(); itCells != NxNaroundRefXtal.end(); ++itCells) {
349  EEDetId cell = EEDetId(*itCells);
350 
351  if (!cell.null()) {
352  EcalRecHitCollection::const_iterator goS_it = hit_collection.find(cell);
353 
354  if ( goS_it != hit_collection.end() ) {
355  double energy = goS_it->energy();
356 
357  if ( cell.ix() == ixP && cell.iy() == iyP ) { MNxN[RU] = energy; }
358  else if ( cell.ix() == ixP && cell.iy() == iyZ ) { MNxN[RR] = energy; }
359  else if ( cell.ix() == ixP && cell.iy() == iyN ) { MNxN[RD] = energy; }
360 
361  else if ( cell.ix() == ixZ && cell.iy() == iyP ) { MNxN[UU] = energy; }
362  else if ( cell.ix() == ixZ && cell.iy() == iyZ ) { MNxN[CC] = energy; }
363  else if ( cell.ix() == ixZ && cell.iy() == iyN ) { MNxN[DD] = energy; }
364 
365  else if ( cell.ix() == ixN && cell.iy() == iyP ) { MNxN[LU] = energy; }
366  else if ( cell.ix() == ixN && cell.iy() == iyZ ) { MNxN[LL] = energy; }
367  else if ( cell.ix() == ixN && cell.iy() == iyN ) { MNxN[LD] = energy; }
368 
369  else { *AcceptFlag=false ; return 0.0 ;}
370  }
371  }
372  }
373 
374  // Get the sum of 8
375  double ESUMis = 0.0 ;
376  for (int i=0; i<9; i++) { ESUMis = ESUMis + MNxN[i] ; }
377 
378  *AcceptFlag=true ;
379  return ESUMis;
380 }
381 
382 
int ix() const
Definition: EEDetId.h:76
constexpr bool null() const
is this a null id ?
Definition: DetId.h:49
std::vector< EcalRecHit >::const_iterator const_iterator
double makeNxNMatrice_RelMC(DetIdT itID, const EcalRecHitCollection &hit_collection, double *MNxN_RelMC, bool *AccFlag)
double makeNxNMatrice_RelDC(DetIdT itID, const EcalRecHitCollection &hit_collection, double *MNxN_RelDC, bool *AccFlag)
static std::string const input
Definition: EdmProvDump.cc:44
int iphi() const
get the crystal iphi
Definition: EBDetId.h:53
T Abs(T a)
Definition: MathUtil.h:49
const CaloSubdetectorTopology * topology_
static bool isNextToRingBoundary(EEDetId id)
Definition: EEDetId.cc:375
double recover(const DetIdT id, const EcalRecHitCollection &hit_collection, double Sum8Cut, bool *AcceptFlag)
def window(xmin, xmax, ymin, ymax, x=0, y=0, width=100, height=100, xlogbase=None, ylogbase=None, minusInfinity=-1000, flipx=False, flipy=True)
Definition: svgfig.py:642
int iy() const
Definition: EEDetId.h:82
int ieta() const
get the crystal ieta
Definition: EBDetId.h:51
MultiLayerPerceptronContext ctx_[9]
const_iterator end() const
void setCaloTopology(const CaloTopology *topo)
double estimateEnergy(double *M3x3Input, double epsilon=0.0000001)
virtual std::vector< DetId > getWindow(const DetId &id, const int &northSouthSize, const int &eastWestSize) const
def load(fileName)
Definition: svgfig.py:546
const CaloSubdetectorTopology * getSubdetectorTopology(const DetId &id) const
access the subdetector Topology for the given subdetector directly
Definition: CaloTopology.cc:25
iterator find(key_type k)
std::string fullPath() const
Definition: FileInPath.cc:197
void load_file(MultiLayerPerceptronContext &ctx, std::string fn)
long double T
def move(src, dest)
Definition: eostools.py:510