MyMarlinTPC  170316
RowBasedHitFinderProcessor.cc
Go to the documentation of this file.
2 
3 //C++
4 #include <iostream>
5 #include <algorithm>
6 
7 //LCIO
8 #include <EVENT/LCFloatVec.h>
9 #include <IMPL/TrackerDataImpl.h>
10 #include <IMPL/TrackerPulseImpl.h>
11 #include <IMPL/TrackerHitImpl.h>
12 #include <IMPL/LCFlagImpl.h>
13 
14 // GEAR
15 #include <gear/TPCParameters.h>
16 #include <gear/TPCModule.h>
17 #include <gearimpl/GlobalPadIndex.h>
18 
19 //MARLIN
20 #include <marlin/Global.h>
21 
22 //Streamlog
23 #include <streamlog/streamlog.h>
24 
25 //lccd
26 #include <lccd_exceptions.h>
27 
28 // the exception by TPCCondData in MarlinTPC
29 #include "TPCCondDataExeptions.h"
30 #include "TPCConditions.h"
31 
32 // the common flag word definitions in MarlinTPC
33 #include "FlagwordDefinitions.h"
34 
35 using namespace std;
36 using namespace lcio;
37 using namespace gear;
38 using namespace marlin;
39 using namespace tpcconddata;
40 
41 namespace marlintpc
42 {
43 
45 
46 
47 RowBasedHitFinderProcessor::RowBasedHitFinderProcessor() : Processor("RowBasedHitFinderProcessor"), _channelCorrectionListener(NULL), _tpcConditionsListener(NULL)
48 
49 {
50  _description = "RowBasedHitFinderProcessor calculates TrackerHits from TrackerPulses" ;
51 
52  // register steering parameters: name, description, class-variable, default value
53  registerInputCollection(LCIO::TRACKERPULSE,
54  "InputTrackerPulses" ,
55  "Name of the input TrackerPulses collection" ,
57  string("TPCPulses")) ;
58 
59  registerOutputCollection(LCIO::TRACKERHIT,
60  "OutputTrackerHits" ,
61  "Name of the output TrackerHits collection" ,
63  string("TPCHits")) ;
64 
65  registerOptionalParameter("InputTPCConditions",
66  "Optional: Name of the input collection containing the TPC conditions data",
68  TPCConditions::getDefaultColName()) ;
69 
70  registerProcessorParameter("WriteOutputToStorage" ,
71  "if true, the output hits collection is written to storage (default: true)" ,
73  true);
74 
75  registerProcessorParameter("ChannelCorrectionCollectionName" ,
76  "Name of the conditions collection in which the channel correction is stored",
78  tpcconddata::ChannelCorrection::getDefaultColName());
79 
80  registerOptionalParameter("MaxEmptyPadsOverride" ,
81  "This parameter is depreciated and will be removed in the next release." ,
82  _deprecated ,
83  1) ;
84 
85  registerOptionalParameter("MaxDeadChannelsOverride" ,
86  "Maximum number of empty consecutive pads in hit (default: 1)" ,
88  1) ;
89 
90  registerOptionalParameter("MinHitSizeOverride" ,
91  "Minimum size of hit / Minimum number of Pads (default: 1)" ,
93  1) ;
94 
95  registerOptionalParameter("MinMaxPulseSliceHeightOverride",
96  "Minimum size of the maximum slice value of the maximal pulse (in charge) in a hit.",
98  float(12.)) ;
99 
100  registerOptionalParameter("MaxTimeSpreadOverride" ,
101  "Maximum time between pulses in a hit in ns (default: 200.)" ,
103  float(200.)) ;
104 
105  registerOptionalParameter("VDriftOverride" ,
106  "Optional: Set drift velocity in case there is no conditions data in mm/mu s" ,
108  float(0.)) ;
109 
110  registerOptionalParameter("DeadPadsOverride",
111  "Optional: the gear pad indices of dead channels",
114 
115  registerProcessorParameter("ChargeConversionIsCalibrated" ,
116  "Set true, if the charge conversion factor from ADC values to primary electrons is known (default:true).",
118  bool(true)) ;
119 
120  registerOptionalParameter("ChargeConversionFactorOverride" ,
121  "Optional: The charge conversion factor from ADC values to primary electrons",
123  float(1.)) ;
124 }
125 
126 
127 
129 {
130  printParameters();
131  // get drift velocity, either measured value from calibration
132  // or nominal value given as process parameter
133  if(parameterSet("VDriftOverride"))
134  {
135  streamlog_out(WARNING) << "the drift velocity is taken from the steering file. please change" << endl;
136  }
137  else // CAUTION! this is not only true for this value, but currently it's the only one in use
138  {
139  try
140  {
141  _tpcConditionsListener = new TPCConditionsListener(_inputTPCConditionsCollectionName);
142  }
143 
144  catch(lccd::LCCDException &)
145  {
146  streamlog_out(ERROR) << "Collection named \"" << _inputTPCConditionsCollectionName << "\" with TPCConditions is not available." << std::endl;
147  streamlog_out(ERROR) << "Use the ConditionsProcessor to provide it, or set the VDriftOverride parameter if this is not possible." << std::endl;
148  throw;
149  }
150  }
151 
152  if(parameterSet("DeadPadsOverride"))
153  {
154  streamlog_out(WARNING) << "the indices of dead pads are taken from the steering file. please change" << endl;
155  }
156  else
157  {
158  try
159  {
160  _channelCorrectionListener = new ChannelCorrectionListener(_channelCorrectionCollectionName);
161  }
162  catch(lccd::LCCDException &)
163  {
164  streamlog_out(ERROR) << "Collection named \"" << _channelCorrectionCollectionName << "\" with ChannelCorrections is not available." << std::endl;
165  streamlog_out(ERROR) << "Use the ConditionsProcessor to provide it, or set the DeadPadsOverride parameter if this is not possible." << std::endl;
166  throw;
167  }
168  }
169 
170  if(parameterSet("ChargeConversionFactorOverride"))
171  {
172  streamlog_out(WARNING) << "the conversion factor from adc counts to charge is taken from the steering file. please change" << endl;
173  }
174  else if(_tpcConditionsListener == 0)
175  {
176  try
177  {
178  _tpcConditionsListener = new TPCConditionsListener(_inputTPCConditionsCollectionName);
179  }
180  catch(lccd::LCCDException &)
181  {
182  streamlog_out(ERROR) << "Collection named \"" << _inputTPCConditionsCollectionName << "\" with TPCConditions is not available." << std::endl;
183  streamlog_out(ERROR) << "Use the ConditionsProcessor to provide it, or set the ChargeConversionFactorOverride parameter if this is not possible." << std::endl;
184  throw;
185  }
186  }
187 
188  if(parameterSet("MaxEmptyPadsOverride"))
189  {
190  streamlog_out(WARNING) << "The parameter [MaxEmptyPadsOverride] is deprecated and has no effect! It will be removed in the next release. Please use [MaxDeadChannelsOverride]." << endl;
191  }
192 }
193 
194 
195 
197 {
198  run->parameters().setValue(_processorName + "_revision", "$Rev: 1842 $");
199 
200  for(ProcParamMap::iterator i = _map.begin(); i != _map.end(); i++)
201  {
202  if(!i->second->isOptional() || i->second->valueSet())
203  run->parameters().setValue(_processorName + "_" + i->second->name(), i->second->value());
204  }
205 }
206 
207 
208 
210 {
230  // get the input pulse collection
231  LCCollection* inputPulses;
232  try
233  {
234  inputPulses = evt->getCollection(_inputTrackerPulsesCollectionName);
235  }
236  catch(DataNotAvailableException &e)
237  {
238  // if no pulses are available, skip to next event
239  streamlog_out(DEBUG) << "No pulses available in event " << evt->getEventNumber() << endl;
240  return;
241  }
242 
243  // create output hit collection and set the persistency flag
244  LCCollectionVec* tpcHitCollection = new LCCollectionVec(LCIO::TRACKERHIT);
245  tpcHitCollection->setTransient(!_outputHitsPersistent);
246 
247 
248  // set the flags that cellID1 should be stored
249  lcio::LCFlagImpl trkFlag(0);
250  trkFlag.setBit(lcio::LCIO::RTHBIT_ID1);
251  tpcHitCollection->setFlag(trkFlag.getFlag());
252 
253  // the main parameter for the geometry description is the gear parameter:
254  const gear::TPCParameters& tpcParameters = Global::GEAR->getTPCParameters();
255 
276  map<int, map<int, vector<TrackerPulse*>*>*> theRowSortedPulsesPerModule;
277  map<int, vector<hitCandidate> > theHitCandidatesPerModule;
278 
279 
280  // (A): sort pulses
281  _sortPulses(inputPulses, tpcParameters, theRowSortedPulsesPerModule);
282 
283  // (B)
284  _findHitCandidates(theRowSortedPulsesPerModule, tpcParameters, theHitCandidatesPerModule);
285 
286  // Clean up the maps
287  map<int, map<int, vector<TrackerPulse*>* >* >::iterator aModuleWithPulses;
288  map<int, vector<TrackerPulse*>* >::iterator aRowWithPulses;
289  // outer loop:: the modules
290  for(aModuleWithPulses = theRowSortedPulsesPerModule.begin();
291  aModuleWithPulses != theRowSortedPulsesPerModule.end(); ++aModuleWithPulses)
292  {
293  // inner loop:: the rows
294  for(aRowWithPulses = aModuleWithPulses->second->begin();
295  aRowWithPulses != aModuleWithPulses->second->end(); ++aRowWithPulses)
296  {
297  delete aRowWithPulses->second;
298  }
299  delete aModuleWithPulses->second;
300  }
301 
302  // (C)
303  _createHitCollection(theHitCandidatesPerModule, tpcParameters, tpcHitCollection);
304 
305  evt->addCollection(tpcHitCollection, _outputTrackerHitsCollectionName);
306 
307  streamlog_out(DEBUG4) << "Found " << tpcHitCollection->getNumberOfElements() << " hits in the event." << endl;
308 
309  streamlog_out(DEBUG3) << "There are " << inputPulses->getNumberOfElements()
310  << " pulses to begin with, and a total of "
311  << tpcHitCollection->getNumberOfElements() << " hits found." << endl;
312 }
313 
314 
315 
317 {
318 }
319 
320 
321 
323 {
324  if(_channelCorrectionListener != 0) // release memory, if needed
326 
327 }
328 
329 
330 
331 void RowBasedHitFinderProcessor::_sortPulses(LCCollection* inputPulses, const gear::TPCParameters& tpcParameters,
332  map<int, map<int, vector<TrackerPulse*>* >* >& rowSortedPulsesPerModule)
333 {
343  pair<map<int, map<int, vector<TrackerPulse*>*>*>::iterator, bool> moduleInserter;
344  pair<map<int, vector<TrackerPulse*>*>::iterator, bool> rowInserter;
345 
346  for(int aPulse = 0; aPulse < inputPulses->getNumberOfElements(); ++aPulse)
347  {
348  TrackerPulse* inputPulse = dynamic_cast<TrackerPulse*>(inputPulses->getElementAt(aPulse));
349  if(inputPulse&&(!_padIsDead(inputPulse->getCellID0(),inputPulse->getCellID1()))) // the cast worked
350  {
351  const int moduleID = inputPulse->getCellID1();
352  const int padID = inputPulse->getCellID0();
353 
354  const TPCModule& theModule = tpcParameters.getModule(moduleID);
355  const int rowID = theModule.getRowNumber(padID);
356 
357  // first create/use the module insertion iterator
358  map<int, vector<TrackerPulse*>*>* rmap = new map<int, vector<TrackerPulse*>*>;
359  moduleInserter =
360  rowSortedPulsesPerModule.insert(pair<int, map<int, vector<TrackerPulse*>* >* >
361  (moduleID, rmap));
362  if(!moduleInserter.second)
363  delete rmap;
364  // use the newly created/already existing map pointer to insert the row
365  vector<TrackerPulse*>* pmap = new vector<TrackerPulse*>;
366  rowInserter = ((moduleInserter.first)->second)->insert
367  (pair<int, vector<TrackerPulse*>* >(rowID, pmap));
368  if(!rowInserter.second)
369  delete pmap;
370 
371  // and finally add the Pulse itself
372  ((rowInserter.first)->second)->push_back(inputPulse);
373  }
374  } // end input pulse loop
375 }
376 
377 
378 
380 (map<int, map<int, vector<TrackerPulse*>* >* >& rowPulsesPerModule, const gear::TPCParameters& tpcParameters,
381  map<int, vector<hitCandidate> >& theHitCandidateMap)
382 {
390  // two iterator types are needed: for the modules and for the rows
391  map<int, map<int, vector<TrackerPulse*>* >* >::iterator aModuleWithPulses;
392  map<int, vector<TrackerPulse*>* >::iterator aRowWithPulses;
393 
394  // outer loop:: the modules
395  for(aModuleWithPulses = rowPulsesPerModule.begin(); aModuleWithPulses != rowPulsesPerModule.end(); ++aModuleWithPulses)
396  {
397  int moduleID = 0;
398  vector<hitCandidate> theHitCandidates;
399 
400  // inner loop:: the rows
401  for(aRowWithPulses = aModuleWithPulses->second->begin(); aRowWithPulses != aModuleWithPulses->second->end(); ++aRowWithPulses)
402  {
403  streamlog_out(DEBUG) << "Starting search of neighbouring pulses in in row: " << aRowWithPulses->first << endl;
404 
405  // third loop: now row wise, find hit candidates and remove the pulses consecutively
406  while(aRowWithPulses->second->size() != 0)
407  {
408  // first find and remove the pulse with the highest charge from the list
409  TrackerPulse* theSeedPulse = _findHighestPulseInRow(*(aRowWithPulses->second));
410  moduleID = theSeedPulse->getCellID1();
411 
412  streamlog_out(DEBUG) << "Found a highest pulse with charge = " << theSeedPulse->getCharge()
413  << " on pad " << theSeedPulse->getCellID0() << " and with time " << theSeedPulse->getTime() << endl;
414  // time saver, conclude the search,
415  // if the maximal pulse height is below the minimum requirement
416  if(!_maxPulseHeightCheck(theSeedPulse))
417  {
418  streamlog_out(DEBUG) << "Pulse is rejected, since it is too small. Continue in next row. " << endl;
419  break;
420  }
421 
422  // take the found pulse as prototype hit
423  hitCandidate theProtoHit(theSeedPulse);
424 
425  // look in the adjacent pads for further pulses, store them in a vector
426  // this function modifies the initial list by removing pulses!
427  // check if it's needed before
428  if(aRowWithPulses->second->size() != 0)
429  _addNeighbourPulses(theProtoHit, tpcParameters, *(aRowWithPulses->second));
430 
431  theHitCandidates.push_back(theProtoHit);
432  }
433  }
434  theHitCandidateMap.insert(pair<int, vector<hitCandidate> >(moduleID, theHitCandidates));
435 
436  streamlog_out(DEBUG) << "On module " << aModuleWithPulses->first
437  << " there are " << theHitCandidates.size() << " hits found." << endl;
438 
439  } // end outer loop of modules
440 }
441 
442 
443 
444 TrackerPulse* RowBasedHitFinderProcessor::_findHighestPulseInRow(vector<TrackerPulse*>& pulsesInARow)
445 {
446  TrackerPulse* highestPulse = pulsesInARow.at(0);
447 
448  vector<TrackerPulse*>::iterator eraser = pulsesInARow.begin();
449  for(vector<TrackerPulse*>::iterator iter = pulsesInARow.begin(); iter < pulsesInARow.end(); ++iter)
450  {
451  if(fabs((*iter)->getCharge()) > fabs(highestPulse->getCharge()))
452  {
453  highestPulse = *iter;
454  eraser = iter;
455  }
456  }
457  pulsesInARow.erase(eraser);
458  return highestPulse;
459 }
460 
461 
462 
463 bool RowBasedHitFinderProcessor::_maxPulseHeightCheck(TrackerPulse* theSeedPulse)
464 {
465  EVENT::TrackerData* trackerData = theSeedPulse->getTrackerData();
466  vector<float> theADCSpectrum = trackerData->getChargeValues();
467 
468  return *max_element(theADCSpectrum.begin(), theADCSpectrum.end()) >= _getMinMaxPulseSliceHeight();
469 }
470 
471 
472 
474 (hitCandidate& theCandidate, const gear::TPCParameters& tpcParameters, vector<TrackerPulse*>& pulsesInARow)
475 {
476  // the initial values for searching
477  const int theStartPadIndex = (theCandidate.getMaximumPulse())->getCellID0();
478  const int theModuleIndex = (theCandidate.getMaximumPulse())->getCellID1();
479  const TPCModule& theModule = tpcParameters.getModule(theModuleIndex);
480  const float theStartTime = (theCandidate.getMaximumPulse())->getTime();
481  const float theHighestCharge = theCandidate.getMaxPulseHeight();
482 
483  // the temporary vars for searching
484  unsigned int numberOfDeadChannels = 0;
485  float tempCharge = theHighestCharge;
486  int tempPadIndex = theStartPadIndex;
487  float tempTime = theStartTime;
488  bool finishedLeft = false; // arbitrary choice: start with searching "left", used to change search direction
489  bool stopSearch = false; // loop control
490 
491  // start loop until no further pulses are found
492  while(!stopSearch)
493  {
494  try // to get the index of the next pad
495  {
496  if(!finishedLeft)
497  tempPadIndex = theModule.getLeftNeighbour(tempPadIndex);
498  else
499  tempPadIndex = theModule.getRightNeighbour(tempPadIndex);
500  }
501  catch(...) // FIXME: what kind of exception is thrown?
502  {
503  streamlog_out(DEBUG) << "The hit candidate is at the border!" << endl;
505 
506  if(!finishedLeft)
507  {
508  finishedLeft = true;
509  tempPadIndex = theStartPadIndex;
510  }
511  else
512  stopSearch = true;
513  }
514 
515 
516  if(_padIsDead(tempPadIndex, theModuleIndex)) // no pulse found because of dead pad
517  {
518  numberOfDeadChannels++; // increase the counter
519  //std::cout<<"found dead pad: module="<<theModuleIndex<<"; row="<< theModule.getRowNumber(tempPadIndex)<<"; pad="<<theModule.getPadNumber(tempPadIndex)<<std::endl;
520  }
521  else
522  {
523  // if more than one pulse matches the requirements, the best match needs to be found
524  // store this in a temporary vector that is searched for the best match
525  vector<vector<TrackerPulse*>::iterator> tempPulses;
526  tempPulses.clear();
527 
528  // check the pulses if (a) they are on the right pad and (b) in the right time window
529  for(vector<TrackerPulse*>::iterator iter = pulsesInARow.begin(); iter < pulsesInARow.end(); ++iter)
530  {
531  if(((*iter)->getCellID0() == tempPadIndex) && (fabs((*iter)->getTime() - tempTime) < _getMaxTimeSpread()))
532  tempPulses.push_back(iter);
533  }
534 
535  // now make distinction on the number of matched pulses
536  if(tempPulses.size() > 0) // if several pulses can be matched, find the best match!
537  {
538  if(tempPulses.size() > 1) // possibly a multiple hit in z !?
540 
541  // this function returns the iterator directly, if only one pulse is passed to it
542  vector<TrackerPulse*>::iterator bestPulseMatch = _findBestNeighbourPulseMatch(tempPulses, tempTime);
543 
544  theCandidate.addPulse(*bestPulseMatch); // store the pulse into hit candidate
545  tempTime = (*bestPulseMatch)->getTime(); // set the new time reference
546  pulsesInARow.erase(bestPulseMatch); // delete the stored pulse from list to disallow double-counting
547  tempPulses.clear();
548 
549  // do a quick check if there is a rise in charge -> indication for multiple hits
550  float thisCharge = (*bestPulseMatch)->getCharge();
551  if(fabs(thisCharge) > fabs(tempCharge))
553  tempCharge = thisCharge;
554 
555  if(numberOfDeadChannels > 0) // there was at least one dead channel in between
556  {
557  hitflag::setDeadChannel(theCandidate.qualityFlag);
558  numberOfDeadChannels = 0; // reset the counter, if needed
559  }
560  }
561  else // nothing found
562  {
563  tempCharge = 0;
564 
565  /* in two cases the search will be abandoned:
566  / a) the pad isn't dead
567  / b) the allowed number of consecutive dead channels is exceeded */
568  // case a) the pad isn't dead -> "end"
569 
570  if(numberOfDeadChannels > 0)
572 
573  if(!finishedLeft) // but if the other direction isn't searched yet -> start from scratch
574  {
575  finishedLeft = true;
576  tempTime = theStartTime;
577  numberOfDeadChannels = 0;
578  tempPadIndex = theStartPadIndex;
579  tempCharge = theHighestCharge;
580  }
581  else // and end
582  stopSearch = true;
583 
584  }
585  }
586 
587  // cover case b); unfortunately this can only be done *after* evaluation if the pad is dead
588  // the "end" code is duplicated; the only other way is a goto (as far as i can think now)
589  if(numberOfDeadChannels > _getMaxDeadChannels()) // this is a dead end!
590  {
592  if(!finishedLeft) // but if the other direction isn't searched yet -> start from scratch
593  {
594  finishedLeft = true;
595  tempTime = theStartTime;
596  numberOfDeadChannels = 0;
597  tempPadIndex = theStartPadIndex;
598  tempCharge = theHighestCharge;
599  }
600  else // and end
601  stopSearch = true;
602  }
603  }// search loop
604  streamlog_out(DEBUG) << "A hit candidate with a width of " << theCandidate.getWidth() << " is found." << endl;
605 }
606 
607 
608 
609 vector<TrackerPulse*>::iterator RowBasedHitFinderProcessor::_findBestNeighbourPulseMatch
610 (vector<vector<TrackerPulse*>::iterator>& pulseMatches, float theTime)
611 {
612  // searches pulses on neighboring pads
613  // if only one is found, this is taken
614  // if there are multiple candidates, the one with the least time difference is taken
615  if(pulseMatches.size() == 1)
616  return pulseMatches.at(0);
617  else
618  {
619  vector<TrackerPulse*>::iterator bestMatch = *(pulseMatches.begin());
620  float minTime = fabs((*bestMatch)->getTime() - theTime);
621 
622  for(vector<vector<TrackerPulse*>::iterator>::iterator searchIter = pulseMatches.begin();
623  searchIter < pulseMatches.end(); ++searchIter)
624  {
625  if(fabs((**searchIter)->getTime() - theTime) < minTime)
626  {
627  minTime = fabs((**searchIter)->getTime() - theTime);
628  bestMatch = *searchIter;
629  }
630  }
631  streamlog_out(DEBUG) << "In the hit more than one pulse was found to match to the protohit,"
632  "the best match in time was chosen." << endl;
633  return bestMatch;
634  }
635 }
636 
637 
638 
640 (map<int, vector<hitCandidate> >& theHitCandidateMap, const gear::TPCParameters& tpcParameters, LCCollectionVec* tpcHitCollection)
641 {
650  map<int, vector<hitCandidate> >::iterator hitCandidatesPerModule;
651 
652  // loop over modules
653  for(hitCandidatesPerModule = theHitCandidateMap.begin();
654  hitCandidatesPerModule != theHitCandidateMap.end(); ++hitCandidatesPerModule)
655  {
656  //loop over hitCandidates:
657  for(vector<hitCandidate>::iterator candidate = hitCandidatesPerModule->second.begin();
658  candidate < hitCandidatesPerModule->second.end(); ++candidate)
659  {
660  // check validity in terms of what is defined in the steering file
661  if(_hitIsValid(*candidate) == false)
662  continue;
663 
664  // calculate the center of gravity in both measurement plane coordinates
665  float covMatrix[6] = {0., 0., 0., 0., 0., 0.};
666  Vector3D positionGlobal3D = _calculatePositionsAndErrors(*candidate, tpcParameters, covMatrix);
667 
668  const double position[3] = { positionGlobal3D[0], positionGlobal3D[1], positionGlobal3D[2] };
669 
670  TrackerHitImpl* theHit = new TrackerHitImpl();
671  // theHit->setType(int type) ???
672  theHit->setPosition(position);
673  theHit->setCovMatrix(covMatrix);
674  theHit->setEDep(_convertCharge(*candidate));
675  // streamlog_out(WARNING)<< " Position standard deviations [" << sqrt(theHit->getCovMatrix().at(0)) << "|" << sqrt(theHit->getCovMatrix().at(2)) << "|" << sqrt(theHit->getCovMatrix().at(5)) << "]" << endl;
677  {
678  theHit->setEDep(_convertCharge(*candidate));
679  theHit->setEDepError(sqrt(pow(_getChargeConversionFactor(),2)*candidate->getChargeMeasError()+_convertCharge(*candidate)));
680  }
681  else
682  {
683  theHit->setEDep(candidate->getCharge());
684  theHit->setEDepError(sqrt(candidate->getChargeMeasError()));
685  }
686  //use the cellIDs of the pad, which is closest to the hit position, for the hit
687  theHit->setCellID0((tpcParameters.getNearestPad( position[0],position[1])).getModuleID());
688  theHit->setCellID1((tpcParameters.getNearestPad( position[0],position[1])).getPadIndex());
689 
690  vector<TrackerPulse*> hitPulses = candidate->getPulses();
691  for(vector<TrackerPulse*>::const_iterator aHitPulse = hitPulses.begin();
692  aHitPulse < hitPulses.end(); ++aHitPulse)
693  {
694  theHit->rawHits().push_back(*aHitPulse);
695 
696  // determine the quality bits from the pulses to let hit inherit them
697  int pulseQuality = (*aHitPulse)->getQuality();
698  if(pulseflag::isSplit(pulseQuality)) // split pulse?
699  hitflag::setSplitPulse(candidate->qualityFlag);
700  if(pulseflag::isOverflow(pulseQuality)) // overflow
701  hitflag::setOverflowPulse(candidate->qualityFlag);
702  if(pulseflag::isAnomalousShape(pulseQuality)) // anomalous shape
703  hitflag::setAnomalousPulseShape(candidate->qualityFlag);
704  if(pulseflag::isPlateauCutoff(pulseQuality)) // anomalous shape
705  hitflag::setPlateauCutoffPulse(candidate->qualityFlag);
706 
707  }
708  theHit->setQuality(candidate->qualityFlag);
709 
710  // the time is inherited from the pulse with the largest charge
711  theHit->setTime(candidate->getMaximumPulse()->getTime());
712 
713  streamlog_out(DEBUG) << "The hit coordinates are [" << position[0] << "|" << position[1] << "|" << position[2] << "]" << endl
714  << " with standard deviations [" << sqrt(theHit->getCovMatrix().at(0)) << "|" << sqrt(theHit->getCovMatrix().at(2)) << "|" << sqrt(theHit->getCovMatrix().at(5)) << "]" << endl
715  << " with variances [" << theHit->getCovMatrix().at(0) << "|" << theHit->getCovMatrix().at(2) << "|" << theHit->getCovMatrix().at(5) << "]" << endl
716  << ", the charge is " << theHit->getEDep() << " +- " << theHit->getEDepError() << " and has the quality byte: [" << theHit->getQuality() << "]" << endl;
717  streamlog_out(DEBUG) << "The detailed list of hit flags: " << " has dead channel [" << hitflag::hasDeadChannel(theHit->getQuality())
718  << "], has noisy channel [" << hitflag::hasNoisyChannel(theHit->getQuality())
719  << "], is at the border of a module [" << hitflag::isAtModuleBorder(theHit->getQuality())
720  << "], has an overflow pulse [" << hitflag::hasOverflowPulse(theHit->getQuality())
721  << "], contains a split pulse [" << hitflag::hasSplitPulse(theHit->getQuality())
722  << "], has an anomalous shaped pulse [" << hitflag::hasAnomalousPulseShape(theHit->getQuality())
723  << "], has an plateau pulse [" << hitflag::hasPlateauCutoffPulse(theHit->getQuality())
724  << "], is next to a dead channel [" << hitflag::isNextToDeadChannel(theHit->getQuality())
725  << "], and is a multiple hit candidate [" << hitflag::isMultipleHitCandidate(theHit->getQuality()) << "]." << endl;
726 
727 
728  tpcHitCollection->addElement(theHit);
729  }
730  }
731  return;
732 }
733 
734 
735 
737 {
738  return (int)cand.getWidth() >= _getMinHitSize();
739 }
740 
741 
742 
743 Vector3D RowBasedHitFinderProcessor::_calculatePositionsAndErrors(const hitCandidate& cand, const gear::TPCParameters& tpcParameters, float* covMatrix)
744 {
745  // calculate the center of gravity for the measurement coordinate
746  const TrackerPulse* maxPulse = cand.getMaximumPulse();
747  const gear::TPCModule& module = tpcParameters.getModule(maxPulse->getCellID1());
748  const gear::PadRowLayout2D& localPadPlane = module.getLocalPadLayout();
749 
750  // check whether the module geometry is described in cartesian coordinates
751  // if not, then throw an exception; this isn't in the manual -- or: the convention is to use cartesian coordinates in LCIO and the global geometry
752  if(module.getCoordinateType() != PadRowLayout2D::CARTESIAN)
753  throw EVENT::Exception("The module does not return cartesian coordinates; this is against the general convention. Better fix it and catch the exception.");
754 
755  // now check the coordinate system type of the pad plane; this defines the natural system for all calculations
756  // define two bools for easier reading
757  bool cartesianPadPlane = (localPadPlane.getCoordinateType() == PadRowLayout2D::CARTESIAN);
758  bool polarPadPlane = !cartesianPadPlane;
759 
760  /*** NOTE: if the pad plane is polar, then the globalToLocal transformation also changes the coordinate type in GEAR !!! ***/
761 
762  vector<TrackerPulse*> thePulses = cand.getPulses();
763 
764  double chargeInPulse = 0.;
765  double chargeErrorInPulse = 0.;
766  double totalCharge = 0.;
767 
768  double localPosition[2] = { 0., 0.};
769  double localPositionError[2] = { 0., 0.};
770 
771  Vector2D localPadCenter, globalPadCenter;
772 
773  for(vector<TrackerPulse*>::const_iterator aPulse = thePulses.begin(); aPulse < thePulses.end(); ++aPulse)
774  {
775  globalPadCenter = module.getPadCenter((*aPulse)->getCellID0());
776  localPadCenter = module.globalToLocal(globalPadCenter[0], globalPadCenter[1]);
777  chargeInPulse = (*aPulse)->getCharge();
778  totalCharge += chargeInPulse;
779  for(unsigned int i = 0; i < 2; ++i)
780  {
781  localPosition[i] += chargeInPulse * localPadCenter[i];
782  }
783  }
784 
785  Vector2D globalPosition2D = module.localToGlobal(localPosition[0] / totalCharge, localPosition[1] / totalCharge);
786  double xPosition = globalPosition2D[0];
787  double yPosition = globalPosition2D[1];
788 
789  // coordinate system: z=0 at cathode, for large prototype the anode is in negative z-direction
790  // z-position is therefore: zAnode (max drift length, positive) - driftTime * driftVelocity
791  double zPosition = Global::GEAR->getTPCParameters().getMaxDriftLength()
792  - _getDriftVelocity() * (maxPulse->getTime()) / 1000. ; // time is in [ns], drift velocity in [mm/mus]
793 
794  // error calculation :: distinguish two cases!
795  // as the variance is the square of the standard deviation, one needs to be careful with the squaring
796  covMatrix[5] = pow(_getDriftVelocity(),2) * (maxPulse->getCovMatrix()).at(2) / 1000000.; // error propagation from maximum pulse
797  double tempDebug1 =0.0;
798  double tempDebug2 =0.0;
799  if(polarPadPlane)
800  {
801  for(vector<TrackerPulse*>::const_iterator aPulse = thePulses.begin(); aPulse < thePulses.end(); ++aPulse)
802  {
803  globalPadCenter = module.getPadCenter((*aPulse)->getCellID0());
804  localPadCenter = module.globalToLocal(globalPadCenter[0], globalPadCenter[1]);
805  chargeInPulse = (*aPulse)->getCharge();
806  chargeErrorInPulse = ((*aPulse)->getCovMatrix())[0];
807  streamlog_out(DEBUG4) << "chargeErrorInPulse =" << chargeErrorInPulse <<endl;
808  localPositionError[1] += pow(chargeInPulse*module.getPadWidth((*aPulse)->getCellID0())/totalCharge,2)/12
809  +pow((localPadCenter[1]*totalCharge - localPosition[1]) / (totalCharge*totalCharge),2)*chargeErrorInPulse;
810  tempDebug1+=pow(chargeInPulse*module.getPadWidth((*aPulse)->getCellID0())/totalCharge,2) / 12;
811  tempDebug2+=pow((localPadCenter[1]*totalCharge - localPosition[1]) / (totalCharge*totalCharge),2)*chargeErrorInPulse;
812 
813  }
814  // localPositionError[1]=tempDebug2;
815  streamlog_out(DEBUG4) << "totalCharge =" << totalCharge <<endl;
816  streamlog_out(DEBUG4) << "localPosition[0]/totalCharge =" << localPosition[0]/totalCharge <<endl;
817  streamlog_out(DEBUG4) << "localPosition[1]/totalCharge =" << localPosition[1]/totalCharge <<endl;
818  streamlog_out(DEBUG4) << "error from pad width =" << sqrt(tempDebug1) <<endl;
819  streamlog_out(DEBUG4) << "error from charge=" << sqrt(tempDebug2) <<endl;
820  double meanPhi = localPosition[1] / totalCharge;
821  streamlog_out(DEBUG4) << "meanPhi="<< meanPhi<<endl;
822  double meanRadius = localPosition[0] / totalCharge;
823  double radiusErrorSquare = pow(module.getRowHeight(module.getRowNumber(maxPulse->getCellID0())), 2) / 12.;
824  covMatrix[0] = pow(cos(meanPhi), 2) * radiusErrorSquare + pow(meanRadius, 2) * pow(sin(meanPhi), 2) * localPositionError[1];
825  covMatrix[1] = sin(meanPhi) * cos(meanPhi) * (radiusErrorSquare - pow(meanRadius, 2) * localPositionError[1]);
826  covMatrix[2] = pow(sin(meanPhi), 2) * radiusErrorSquare + pow(meanRadius, 2) * pow(cos(meanPhi), 2) * localPositionError[1];
827 
828  }
829  else if(cartesianPadPlane)
830  {
831  for(vector<TrackerPulse*>::const_iterator aPulse = thePulses.begin(); aPulse < thePulses.end(); ++aPulse)
832  {
833  globalPadCenter = module.getPadCenter((*aPulse)->getCellID0());
834  localPadCenter = module.globalToLocal(globalPadCenter[0], globalPadCenter[1]);
835  chargeInPulse = (*aPulse)->getCharge();
836  chargeErrorInPulse = ((*aPulse)->getCovMatrix())[0];
837  localPositionError[0] += pow(chargeInPulse*module.getPadWidth((*aPulse)->getCellID0())/totalCharge,2)/12
838  +pow((localPadCenter[0]*totalCharge-localPosition[0])/(totalCharge*totalCharge)*chargeErrorInPulse,2);
839 
840  }
841  covMatrix[0] = localPositionError[0];
842  covMatrix[2] = pow(module.getRowHeight(module.getRowNumber(maxPulse->getCellID0())),2) / 12.;
843  }
844 
845  /*
846  double padWidth = module.getPadWidth(maxPulse->getCellID0());
847  if(polarPadPlane)
848  {
849  double radiusErrorSquare = pow(module.getRowHeight(module.getRowNumber(maxPulse->getCellID0())), 2) / 12.;
850  double meanPhi = localPosition[1] / totalCharge;
851  double meanRadius = localPosition[0] / totalCharge;
852  double phiErrorSquare = 0.0;
853  double charge = 0.0;
854  const double singlePadPhiError = padWidth / sqrt(12.);
855  if(cand.getWidth() == 1)
856  phiErrorSquare = pow(singlePadPhiError, 2);
857  else
858  {
859  for(vector<TrackerPulse*>::const_iterator aPulse = thePulses.begin(), endPulse = thePulses.end(); aPulse != endPulse; ++aPulse)
860  {
861  charge = (*aPulse)->getCharge();
862  phiErrorSquare += pow((charge * singlePadPhiError) , 2);
863 
864  }
865  phiErrorSquare /= pow(totalCharge, 2);
866  }
867  // OK, the diagonal errors have been determined; now do the coordinate transformation, since the geometry can't do it for matrices
868  covMatrix[0] = pow(cos(meanPhi), 2) * radiusErrorSquare + pow(meanRadius, 2) * pow(sin(meanPhi), 2) * phiErrorSquare;
869  covMatrix[1] = sin(meanPhi) * cos(meanPhi) * (radiusErrorSquare - pow(meanRadius, 2) * phiErrorSquare);
870  covMatrix[2] = pow(sin(meanPhi), 2) * radiusErrorSquare + pow(meanRadius, 2) * pow(cos(meanPhi), 2) * phiErrorSquare;
871 
872  }
873  else if(cartesianPadPlane)
874  {
875  streamlog_out(WARNING) << "Please make sure that the global x and y are aligned with the local one; in addition: y is the row coordinate, x within row. Otherwise the errors are non-sensical." << endl;
876  double xError = 0.0;
877  double meanX = localPosition[0] / totalCharge;
878  if(cand.getWidth() == 1){
879  xError = pow(padWidth,2) / 12.;
880  }
881 
882  else
883  {
884  for(vector<TrackerPulse*>::const_iterator aPulse = thePulses.begin(), endPulse = thePulses.end(); aPulse != endPulse; ++aPulse)
885  {
886  xError += pow((localPadPlane.getPadCenter((*aPulse)->getCellID0())[0] - meanX) , 2);
887  }
888  }
889  covMatrix[0] = xError;
890  covMatrix[2] = pow(module.getRowHeight(module.getRowNumber(maxPulse->getCellID0())),2) / 12.;
891 
892  }
893  */
894  return Vector3D(xPosition, yPosition, zPosition);
895 }
896 
897 
898 
900 {
901  return _getChargeConversionFactor() * (cand.getCharge());
902 }
903 
904 
905 
907 : qualityFlag(0), _maxPulse(NULL), _maxPulseCharge(0.0), _numberOfDeadChannels(0)
908 {
909  _thePulses.clear();
910  addPulse(aPulse);
911 }
912 
913 
914 
916 {
917  _thePulses.push_back(aPulse);
918  if(fabs(aPulse->getCharge()) > fabs(_maxPulseCharge))
919  {
920  _maxPulseCharge = aPulse->getCharge();
921  _maxPulse = aPulse;
922  }
923 }
924 
925 
927 {
928  _numberOfDeadChannels++;
929 }
930 
931 
932 
934 {
935  double tmpCharge = 0.0;
936  for(vector<TrackerPulse*>::const_iterator aPulse = _thePulses.begin(); aPulse < _thePulses.end(); ++aPulse)
937  tmpCharge += (*aPulse)->getCharge();
938  return tmpCharge;
939 }
940 
942 {
943  double tmpChargeErrorSqr = 0.0;
944  for(vector<TrackerPulse*>::const_iterator aPulse = _thePulses.begin(); aPulse < _thePulses.end(); ++aPulse)
945  tmpChargeErrorSqr += ((*aPulse)->getCovMatrix())[0];
946  return tmpChargeErrorSqr;
947 }
948 
949 
951 {
952  return _maxPulseCharge;
953 }
954 
955 
956 
958 {
959  return _thePulses.size();
960 }
961 
962 
963 
964 bool RowBasedHitFinderProcessor::_pulseComparator(TrackerPulse* p0, TrackerPulse* p1)
965 {
966  return fabs(p0->getCharge()) < fabs(p1->getCharge());
967 }
968 
969 
970 
972 {
973  if(parameterSet("MaxDeadChannelsOverride"))
974  {
976  }
977  else
978  {
979  throw tpcconddata::ConditionsObjectException(string("[RowBasedHitFinderProcessor]::_getDeadChannels() Exception: number can't be found!"));
980  }
981 }
982 
983 
984 
986 {
987  if(parameterSet("MinHitSizeOverride"))
988  {
989  return _minHitSizeOverride;
990  }
991  else
992  {
993  throw tpcconddata::ConditionsObjectException(string("[RowBasedHitFinderProcessor]::_getMinHitSize() Exception: Number can't be found!"));
994  }
995 }
996 
997 
998 
1000 {
1001  if(parameterSet("MinMaxPulseSliceHeightOverride"))
1002  {
1004  }
1005  else
1006  {
1007  throw tpcconddata::ConditionsObjectException(string("[RowBasedHitFinderProcessor]::_getMinMaxPulseSliceHeight() Exception: Number can't be found!"));
1008  return 0.;
1009  }
1010 }
1011 
1012 
1013 
1015 {
1016  if(parameterSet("MaxTimeSpreadOverride"))
1017  {
1018  return _maxTimeSpreadOverride;
1019  }
1020  else
1021  {
1022  throw tpcconddata::ConditionsObjectException(string("[RowBasedHitFinderProcessor]::_getMaxTimeSpread() Exception: Number can't be found!"));
1023  return 0.;
1024  }
1025 }
1026 
1027 
1028 
1030 {
1031  if(parameterSet("VDriftOverride"))
1032  {
1033  return _driftVelocityOverride;
1034  }
1035  else
1036  {
1037  try
1038  {
1039  return _tpcConditionsListener->getDriftVelocity();
1040  }
1041  catch(...)
1042  {
1043  streamlog_out(ERROR) << "[getDriftVelocity failed] The Listener for TPCConditions could not be used. Maybe the conditions object is not available?" << endl;
1044  streamlog_out(ERROR) << " Possibly you need to use the override parameterin the steering file called (\"VDriftOverride\")." << endl;
1045  abort();
1046  }
1047  }
1048 }
1049 
1050 
1051 
1052 bool RowBasedHitFinderProcessor::_padIsDead(const int padID, const int moduleID)
1053 {
1054  if(parameterSet("DeadPadsOverride")) // this is just for a single module!
1055  {
1056  return (_deadChannelIndicesOverride.end() != find(_deadChannelIndicesOverride.begin(), _deadChannelIndicesOverride.end(), padID));
1057  }
1058  else
1059  {
1060  pair<int, int> tmpPair;
1061  tmpPair.first = padID; // CellID0
1062  tmpPair.second = moduleID; // CellID1
1063  bool returnvalue=false;
1064  try
1065  {
1066  if (_channelCorrectionListener->isDeadFromGearID(tmpPair)||_channelCorrectionListener->isNoisyFromGearID(tmpPair))
1067  //if (_channelCorrectionListener->isDeadFromGearID(tmpPair))
1068  {
1069  returnvalue=true;
1070  }
1071 
1072 
1073  }
1074  catch (CondDataNotAvailableException &e)
1075  {
1076  }
1077  return returnvalue;
1078  }
1079 }
1080 
1081 
1082 
1083 bool RowBasedHitFinderProcessor::_padIsNoisy(const int moduleID, const int padID)
1084 {
1085 
1086  if(parameterSet("DeadPadsOverride")) // this is just for a single module!
1087  return (_deadChannelIndicesOverride.end() != find(_deadChannelIndicesOverride.begin(), _deadChannelIndicesOverride.end(), padID));
1088  else
1089  {
1090  pair<int, int> tmpPair;
1091  tmpPair.first = padID; // CellID0
1092  tmpPair.second = moduleID; // CellID1
1093  bool returnvalue;
1094  try
1095  {
1096  returnvalue=_channelCorrectionListener->isNoisyFromGearID(tmpPair);
1097  streamlog_out(MESSAGE)<<"found noisy pad"<<endl;
1098  }
1099  catch (CondDataNotAvailableException &e)
1100  {
1101  returnvalue=false;
1102  }
1103  return returnvalue;
1104  }
1105 
1106 }
1107 
1108 
1109 
1111 {
1112  if(parameterSet("ChargeConversionFactorOverride"))
1113  {
1115  }
1116  else
1117  {
1118  return _tpcConditionsListener->getADCtoPrimaryElectronsFactor();
1119  }
1120 }
1121 }// namespace marlintpc
bool isMultipleHitCandidate(int r)
bool hasAnomalousPulseShape(int p)
void _createHitCollection(std::map< int, std::vector< RowBasedHitFinderProcessor::hitCandidate > > &, const gear::TPCParameters &, IMPL::LCCollectionVec *)
void _addNeighbourPulses(hitCandidate &, const gear::TPCParameters &, std::vector< EVENT::TrackerPulse *> &)
void _findHitCandidates(std::map< int, std::map< int, std::vector< EVENT::TrackerPulse *> * > * > &, const gear::TPCParameters &, std::map< int, std::vector< RowBasedHitFinderProcessor::hitCandidate > > &)
RowBasedHitFinderProcessor aRowBasedHitFinderProcessor
EVENT::TrackerPulse * _findHighestPulseInRow(std::vector< EVENT::TrackerPulse *> &)
void setNextToDeadChannel(int &q)
double _convertCharge(const RowBasedHitFinderProcessor::hitCandidate &)
void setMultipleHitCandidate(int &r)
void setPlateauCutoffPulse(int &t)
std::vector< EVENT::TrackerPulse * >::iterator _findBestNeighbourPulseMatch(std::vector< std::vector< EVENT::TrackerPulse *>::iterator > &, float)
void setOverflowPulse(int &m)
void _sortPulses(EVENT::LCCollection *, const gear::TPCParameters &, std::map< int, std::map< int, std::vector< EVENT::TrackerPulse *> * > * > &)
virtual void processRunHeader(lcio::LCRunHeader *run)
bool _hitIsValid(const RowBasedHitFinderProcessor::hitCandidate &)
ChannelCorrectionListener * _channelCorrectionListener
const std::vector< EVENT::TrackerPulse * > & getPulses() const
bool _pulseComparator(EVENT::TrackerPulse *, EVENT::TrackerPulse *)
bool isNextToDeadChannel(int q)
gear::Vector3D _calculatePositionsAndErrors(const RowBasedHitFinderProcessor::hitCandidate &, const gear::TPCParameters &, float *)
bool hasPlateauCutoffPulse(int t)
void setAtModuleBorder(int &k)
void setAnomalousPulseShape(int &p)
virtual void processEvent(lcio::LCEvent *evt)