Diana Software
MFastCoincidence.cc
Go to the documentation of this file.
1 #include "MFastCoincidence.hh"
2 #include "QEvent.hh"
3 #include "QRawEvent.hh"
4 #include "QEventList.hh"
5 #include "QBaseType.hh"
6 #include "QCoincidenceData.hh"
7 #include "QOFData.hh"
8 #include "QRunDataHandle.hh"
9 #include "QDetChannelCollectionHandle.hh"
10 #include "QJitter.hh"
11 #include "QJitterHandle.hh"
12 
13 #include <cmath>
14 #include <fstream>
15 #include <cstdlib>
16 #include <string>
17 #include <iostream>
18 #include <iomanip>
19 
20 using namespace std;
21 using namespace Diana;
22 
24 
25 inline bool FileExists(const std::string& name) {
26  if(FILE* file = fopen(name.c_str(), "r")) {
27  fclose(file);
28  return true;
29  }
30  else
31  return false;
32 }
33 
34 TimeNeighbour::TimeNeighbour(ULong64_t eventNumber, ULong64_t tag, int channel, double time, double energy) :
35  fEventNumber(eventNumber), fTag(tag), fChannel(channel), fTime(time), fEnergy(energy) {
36  }
37 
39  return a->fTime < b->fTime;
40 }
41 
43 
44  fCoincidenceWindow = GetDouble("CoincidenceWindow", 0.100);
45  // BEGIN space-based
46  fCoincidenceRadius = GetDouble("CoincidenceRadius", 1200.);
47  Info("Loaded CoincidenceRadius = %lf [mm]", fCoincidenceRadius);
48  if( fCoincidenceRadius < 0. )
49  Panic("Negative CoincidenceRadius! No multiplet would be created!");
50  fUseRunningRadius = GetBool("UseRunningRadius", true);
51  Info("Set UseRunningRadius = %s", fUseRunningRadius ? "true" : "false");
52  // END space-based
53  ev.Add<QCoincidenceData>("CoincidenceData");
54 
55  fEnergyLabel = GetString("EnergyLabel", "ApplyCalibration@Energy");
56  vector<string> defaults;
57  fPSALabels = GetVectorString("PSALabels", defaults);
58 
59  std::string timeVariableStr = GetString("TimeVariable", "Raw");
60  if( timeVariableStr == "Raw" ) {
62  } else if( timeVariableStr == "OF" ) {
64  } else {
66  Warn("Unrecognized time variable, setting it to Raw");
67  }
68 
69  fDataset = -1;
70 
71  fSyncTime = GetBool("SyncTime", false);
72  if( fSyncTime) {
73  fSyncType = GetString("SyncType", "coincidence");
74  if( fSyncType == "coincidence" ) {
75  fSyncSource = GetString("SyncSource", "jitter.txt");
76  fSyncTowerSource = GetString("SyncTowerSource", "jitter_tower.txt");
77  } else if( fSyncType == "delay" ) {
78  fSyncTowerSource = GetString("SyncTowerSource", "jitter_tower.txt");
79  } else {
80  Warn("Unrecognized SyncType variable, setting it to default (coincidence)");
81  fSyncType = "coincidence";
82  fSyncSource = GetString("SyncSource", "jitter.txt");
83  fSyncTowerSource = GetString("SyncTowerSource", "jitter_tower.txt");
84  }
85  }
86 
87  ev.Add<QDouble>("TotalEnergy");
88  ev.Add<QDouble>("Time");
89 
90  fUseRunningWindow = GetBool("UseRunningWindow", true);
91  fStorePosition = GetBool("StorePosition", true);
92  fThisRun = -1;
93 
94  ev.Require<QPulseInfo>("DAQ", "PulseInfo");
95  ev.Require<QHeader>("DAQ", "Header");
96 
97  if( !fPSALabels.empty() ) {
98  for( size_t p = 0; p < fPSALabels.size(); p++ ) {
100  }
101  }
102 
104 
105  fOFLabel = GetString("OFLabel", "COF");
106  if( fTimeVariable == TV_OF )
107  ev.Require<QOFData>(fOFLabel, "OFData");
108 
109  // BEGIN space-based
110  // load position in xyz space of the center of the crystals for space-based coincidences
111  fChannelPositionFile = GetString("ChannelPositionFile", "ChannelPosition.dat");
112  fUseChannelPosition = GetBool("UseChannelPosition", false);
113 
114  if( fUseChannelPosition ){
115  ev.Add<QDouble>("Radius"); // space-based
117  ifstream fCPFile(fChannelPositionFile.c_str());
118  string line;
119  stringstream ss;
120  int fChannel;
121  while(getline(fCPFile, line)) {
122  ss.str(line);
123  ss >> fChannel;
124  ss >> fChannelPositionX[fChannel] >> fChannelPositionY[fChannel] >> fChannelPositionZ[fChannel];
125  ss.str("");
126  ss.clear();
127  }
128  fCPFile.close();
129  }else{
130  Panic("UseChannelPosition = true but ASCII file %s does not exist. Modify ChannelPositionFile in cfg or set UseChannelPosition to false.", fChannelPositionFile.c_str() );
131  }
132  }else{
133  Warn("UseChannelPosition = false. Will skip all space-based selections.");
134  }// END space-based
135 
136 }
137 
139 
140  const QHeader& hdr = ev.Get<QHeader>("DAQ", "Header");
141  const QPulseInfo& pi = ev.Get<QPulseInfo>("DAQ", "PulseInfo");
142  const int run = hdr.GetRun();
143  const int chan = pi.GetChannelId();
144  QDouble energy = ev.GetByLabel<QDouble>(fEnergyLabel);
145 
146  // Dataset
147  if( fDataset < 0 ) {
148  GlobalHandle<QInt> dHandle("Dataset");
149  GlobalData().Get("", &dHandle, "");
150  fDataset = dHandle.Get();
151  }
152 
153  if( fThisRun != run ) {
154  fThisRun = run;
156  GlobalData().Get("DAQ", &rHandle, "");
157  fRunData = rHandle.Get();
158  }
159 
160  // Tower map
161  if( ChannelTowerMap.find(chan) == ChannelTowerMap.end() ) {
162  QDetChannelCollectionHandle dccHandle;
163  dccHandle.SetRun(run);
164  GlobalData().Get("",&dccHandle,"");
165 
166  int tower = dccHandle.Get().Get(chan).fTd.fTdTower;
167  int floor = dccHandle.Get().Get(chan).fTd.fTdFloor;
168  int position = dccHandle.Get().Get(chan).fTd.fTdPos;
169  ChannelTowerMap[chan] = tower;
170  ChannelFloorMap[chan] = floor;
171  ChannelPositionMap[chan] = position;
172  }
173 
174  double time = GetTime(ev);
175  if( GetIteration() == 1 ) {
176  // First iteration: fill neighbour map
177  int tower = ChannelTowerMap[chan];
178  ULong64_t evNumber = pi.GetMasterSample().GetEventNumber();
179  ULong64_t tag = evNumber*100 + tower;
180 
181  timeNeighbours.push_back(new TimeNeighbour(evNumber, tag, chan, time, energy));
182  } else {
183  // Second iteration: coincidences
184  // Initialize QCoincidenceData
185  QCoincidenceData& coincData = ev.Get<QCoincidenceData>("CoincidenceData");
186  coincData.fOrderInMultiple = 1;
187  coincData.fMultiplicity = 1;
188  coincData.fCoincidentChannels.clear();
189  double LargestDeltaT = 0;
190  double SmallestDeltaT = Q_DOUBLE_DEFAULT;
191  double tot_energy = energy;
192 
193  ev.Get<QDouble>("Time") = time;
194 
195  // Find the current QEvent in the neighbours vector
196  int tower = ChannelTowerMap[chan];
197  ULong64_t thisTag = pi.GetMasterSample().GetEventNumber()*100 + tower;
198  //std::vector<TimeNeighbour*>::iterator thisEv = std::find_if(timeNeighbours.begin(), timeNeighbours.end(), FindTimeNeighbour(thisTag));
199 
200  int p = 0;
201  int u = timeNeighbours.size()-1;
202  int m;
203  while(p <= u) {
204  m = (p+u)/2;
205  if( timeNeighbours[m]->fTag == thisTag )
206  break;
207  if( timeNeighbours[m]->fTime < time )
208  p = m + 1;
209  else
210  u = m - 1;
211  }
212 
213  TimeNeighbour* tn;
214 
215  // Main event time
216  double eTime = time;
217  std::set<int> tnSelected; // store here selected neighbors (their key in the timeNeighbours vector)
218  std::set<int>::iterator tnSelIT;
219  // std::map<int> tnSelectedDeltaT; // store here DeltaT with respect to closest event wrt main (backwards compatibility)
220 
221  // Loop backwards on the timeNeighbours starting from the main (excluded)
222  for( int s = m-1; s>= 0; s-- )
223  {
224  tn = timeNeighbours[s];
225  double oTime = tn->fTime;
226  double deltaT = oTime - eTime;
227  if( fabs(deltaT) < fCoincidenceWindow ){// time-coincident
228  if( fUseRunningWindow )
229  eTime = oTime;
230  tnSelected.insert( s );
231  }else{ // gone out of the coincidence window
232  break;
233  }
234  }
235  eTime = time;
236  // Loop forward on the timeNeighbours starting from the main (excluded)
237  for( size_t s = m+1; s < timeNeighbours.size(); s++ ) {
238  tn = timeNeighbours[s];
239  double oTime = tn->fTime;
240  double deltaT = oTime - eTime;
241  if( fabs(deltaT) < fCoincidenceWindow){// time-coincident
242  if( fUseRunningWindow )
243  eTime = oTime;
244  tnSelected.insert( s );
245  }else{ // gone out of the coincidence window
246  break;
247  }
248  }
249  // END finding time neighbors.
250 
251  // BEGIN space-based selection
252  if( fUseChannelPosition ){// enable space-based selection
253  std::set<int> tnSpaceBasedAccepted;
254  for( tnSelIT = tnSelected.begin(); tnSelIT != tnSelected.end(); ++tnSelIT )
255  {
256  tn = timeNeighbours[ *tnSelIT ];
257  int oChan = tn->fChannel;
258  double oDistance = GetDistanceMmChannel(chan,oChan);
259  if( oDistance <= fCoincidenceRadius )//add to the accepted event list
260  tnSpaceBasedAccepted.insert( *tnSelIT );
261  }
262  int EmergencyStop = -988; // DEBUG
263  if( fUseRunningRadius ){// enabled running radius (tracking)
264  bool AddedNewEvent;
265  if( EmergencyStop >= 0 )
266  Panic("Infinite loop detected! Due to bug in SpaceBased with RunningRadius");
267  std::set<int>::iterator tnSBA_IT; // iterator on Space Based Accepted events
268  do{
269  AddedNewEvent = false;
270  for( tnSBA_IT = tnSpaceBasedAccepted.begin(); tnSBA_IT != tnSpaceBasedAccepted.end(); ++tnSBA_IT )
271  {// for each (space-based) accepted event ...
272  tn = timeNeighbours[ *tnSBA_IT ];
273  int sbChan = tn->fChannel;
274  for( tnSelIT = tnSelected.begin(); tnSelIT != tnSelected.end(); ++tnSelIT )
275  {// ... loop over all the time-selected events (except the main)
276  int oChan = timeNeighbours[ *tnSelIT ]->fChannel;
277  double oDistance = GetDistanceMmChannel( sbChan, oChan );
278  if( oDistance < fCoincidenceRadius && tnSpaceBasedAccepted.end() == tnSpaceBasedAccepted.find( *tnSelIT ) )
279  {// if you find one that is WITHIN the radius buy NOT (yet) accepted ...
280  tnSpaceBasedAccepted.insert( *tnSelIT ); // ... accept it ...
281  AddedNewEvent = true; // ... and update this flag
282  }
283  }
284  if(AddedNewEvent){// DEBUG
285  //Warn("BREAK: Added new event because of RunningRadius to tag %d",tn->fTag);
286  EmergencyStop++;
287  break;
288  }
289  }
290  }while( AddedNewEvent );
291  } // ------------------------------ END RUNNING RADIUS
292  // overwrite time selection with time && space selection
293  tnSelected = tnSpaceBasedAccepted;
294  // compute maximum distance in the multiplet
295  double MaximumDistance = 0.;
296  std::set<int>::iterator tnSelIT_i;
297  std::set<int>::iterator tnSelIT_j;
298  for( tnSelIT_i = tnSelected.begin(); tnSelIT_i != tnSelected.end(); ++tnSelIT_i)
299  {
300  int iChan = timeNeighbours[ *tnSelIT_i ]->fChannel;
301  double iDistance = GetDistanceMmChannel( chan, iChan ); // distance from MAIN
302  if( iDistance > MaximumDistance )
303  MaximumDistance = iDistance;
304  for( tnSelIT_j = tnSelected.begin(); tnSelIT_j != tnSelected.end(); ++tnSelIT_j)
305  {
306  // make it faster: D_ij = D_ji and D_ii = 0 => compute only D_ij where i<j
307  if( *tnSelIT_i >= *tnSelIT_j )
308  continue;
309  int jChan = timeNeighbours[ *tnSelIT_j ]->fChannel;
310  double ijDistance = GetDistanceMmChannel( iChan, jChan );
311  if( ijDistance > MaximumDistance )
312  MaximumDistance = ijDistance;
313  }
314  }
315  // fill "radius" with the maximum distance between coincident channels in this multiplet
316  ev.Get<QDouble>("Radius") = MaximumDistance;
317  // this is just a cross-check: self-coincident events happen when there is a trigger on baseline fluctuation and a pulse 1s after
318  if( tnSelected.size() > 0 && MaximumDistance < 1e-3){//multiplicity > 1 && Radius = 0
319  Debug("Detected event with multiplicity > 1 and Radius < 0.001 mm. Tag %d. This is a normal behaviour.",thisTag);
320  /*
321  Debug("Main %d [tag=%d]\t NsTime/1e9 = %lf",chan, thisTag, double(hdr.GetTime().GetFromStartRunNs())/1e09);
322  Debug("Main %d [tag=%d]\t jitter-corrected-time = %lf",chan, thisTag, time);
323  for( tnSelIT_i = tnSelected.begin(); tnSelIT_i != tnSelected.end(); ++tnSelIT_i)
324  Debug("Main %d [tag=%d]\t Coincident %d [tag=%d]",chan, thisTag, timeNeighbours[ *tnSelIT_i ]->fChannel, timeNeighbours[ *tnSelIT_i ]->fTag);
325  */
326  }
327 
328  }
329  // END space-based
330 
331  // Write data on the QEvent
332  coincData.fMultiplicity = tnSelected.size() + 1; // # of coincident events + 1 main event
333  for( tnSelIT = tnSelected.begin(); tnSelIT != tnSelected.end(); ++tnSelIT )
334  { // looping over selected events
335  tn = timeNeighbours[ *tnSelIT ];
336  double oTime = tn->fTime;
337  double oEnergy = tn->fEnergy;
338  int oChan = tn->fChannel;
339  double deltaT = oTime - time; // this is ALWAYS the time difference wrt the main (can be > fCoincidenceWindow)
340  if( *tnSelIT < m )// raise OrderInMultiple just for events that happen BEFORE the main
341  coincData.fOrderInMultiple++;
342  if( fabs(deltaT) > fabs(LargestDeltaT) )
343  LargestDeltaT = deltaT;
344  if( fabs(deltaT) < fabs(SmallestDeltaT) )
345  SmallestDeltaT = deltaT;
346  tot_energy += oEnergy;
348  cc.fChannelId = oChan;
349  cc.fDeltaT = deltaT;
350  cc.fEnergy = oEnergy;
351  cc.fDeltaTower = ChannelTowerMap[oChan] - ChannelTowerMap[chan];
352  cc.fDeltaFloor = ChannelFloorMap[oChan] - ChannelFloorMap[chan];
354  cc.fEventNumber = tn->fEventNumber;
355  coincData.fCoincidentChannels.push_back(cc);
356  }
357 
358  coincData.fNearestCoincidentIndex = -1;
359  coincData.fFarthestCoincidentIndex = -1;
360  for(size_t c = 0; c < coincData.fCoincidentChannels.size(); c++) {
361  if(coincData.fCoincidentChannels[c].fDeltaT == SmallestDeltaT)
362  coincData.fNearestCoincidentIndex = c;
363  if(coincData.fCoincidentChannels[c].fDeltaT == LargestDeltaT)
364  coincData.fFarthestCoincidentIndex = c;
365  }
366  ev.Get<QDouble>("TotalEnergy") = tot_energy;
367  }
368 }
369 
371  if( GetIteration() == 1 ) {
372  SetRunAgain(true);
373  std::sort(timeNeighbours.begin(), timeNeighbours.end(), timeSort);
374  } else {
375  SetRunAgain(false);
376  for(size_t s = 0; s < timeNeighbours.size(); s++)
377  delete timeNeighbours[s];
378 
379  timeNeighbours.clear();
380  ChannelTowerMap.clear();
381  ChannelFloorMap.clear();
382  ChannelPositionMap.clear();
383  fChannelPositionX.clear();
384  fChannelPositionY.clear();
385  fChannelPositionZ.clear();
386  fSyncTimeDifference.clear();
387  }
388 }
389 
390 
392 {
393  const QHeader& header = ev.Get<QHeader>("DAQ","Header");
394  const QPulseInfo& pi = ev.Get<QPulseInfo>("DAQ","PulseInfo");
395  double time = double(header.GetTime().GetFromStartRunNs())/1e09;
396 
397  switch(fTimeVariable) {
398  case TV_RAW:
399  {
400  break;
401  }
402  case TV_OF:
403  {
404  const QOFData& of = ev.Get<QOFData>(fOFLabel.c_str(),"OFData");
405  double samplingFrequency = fRunData.GetChannelRunData(pi.GetChannelId()).fSamplingFrequency;
406  time += of.GetDelay()/1000 - double(pi.GetMasterSample().GetSampleIndex())/samplingFrequency;
407  break;
408  }
409 
410  }
411 
412  // response sinchronisation using by default jitter by coincidence and jitter by delay only for the reference channels
413  if(fSyncTime && fSyncType == "coincidence") {
414  const int channel = pi.GetChannelId();
415 
416  // get jitter handle for this channel
417  QJitterHandle jHandle(channel,fDataset);
418  GlobalData().Get("JitterByCoincidence",&jHandle,fSyncSource);
419 
420  if(!jHandle.IsValid()) {
421  if(fBlackList.count(channel)) return time;
422  fBlackList.insert(channel);
423  Error("Channel %d, has no jitter. Cannot be synced: %s",channel,jHandle.GetError().GetDescription().c_str());
424  return time;
425  }
426  else {
427  const QJitter& jitters = jHandle.Get();
428 
429  // get jitter for the reference channel of the given channel
430  QJitterHandle jdhandle(jitters.fRefChannel,fDataset);
431  GlobalData().Get("JitterByDelay",&jdhandle,fSyncTowerSource);
432 
433  if(!jdhandle.IsValid()) {
434  Error("Reference channel %d, has no jitter. The relative tower cannot be synced: %s",jitters.fRefChannel,jdhandle.GetError().GetDescription().c_str());
435  return time;
436  }
437 
438  const QJitter& jittersTower = jdhandle.Get();
439 
440 
441  time = time + jitters.fJitter - jittersTower.fJitter/1000.;
442 
443  }
444  }
445 
446  // response sinchronisation using only jitter by delay
447  if(fSyncTime && fSyncType == "delay") {
448  const int channel = pi.GetChannelId();
449 
450  // get jitter handle for this channel
451  QJitterHandle jHandle(channel,fDataset);
452  GlobalData().Get("JitterByDelay",&jHandle,fSyncTowerSource);
453 
454  if(!jHandle.IsValid()) {
455  if(fBlackList.count(channel)) return time;
456  fBlackList.insert(channel);
457  Error("Channel %d, has no jitter. Cannot be synced: %s",channel,jHandle.GetError().GetDescription().c_str());
458  return time;
459  }
460  else {
461  const QJitter& jitters = jHandle.Get();
462  time = time - jitters.fJitter/1000.;
463  }
464  }
465  return time;
466 }
467 
468 // space-based
469 double MFastCoincidence::GetDistanceMmChannel(const int ch1, const int ch2){
470  double x1,y1,z1,x2,y2,z2;
471  x1 = fChannelPositionX[ch1];
472  y1 = fChannelPositionY[ch1];
473  z1 = fChannelPositionZ[ch1];
474  x2 = fChannelPositionX[ch2];
475  y2 = fChannelPositionY[ch2];
476  z2 = fChannelPositionZ[ch2];
477  return sqrt( pow(x1-x2,2) + pow(y1-y2,2) + pow(z1-z2,2) );
478 }
QOptimumFilter of(ap, an,-1, false)
TF1 a
Definition: CheckOF.C:21
QVector b
Definition: CheckOF.C:21
QRunDataHandle rHandle(753)
const int channel
chanRunData fSamplingFrequency
REGISTER_MODULE(MFastCoincidence)
bool FileExists(const std::string &name)
bool timeSort(TimeNeighbour *a, TimeNeighbour *b)
Diana::QComplex pow(const Diana::QComplex &z, double a)
Raise a complex number to a real power.
Definition: QComplex.cc:246
#define Q_DOUBLE_DEFAULT
Definition: QDiana.hh:24
TimeVariable fTimeVariable
std::string fChannelPositionFile
std::map< int, double > fChannelPositionZ
void Init(Diana::QEvent &ev)
double GetTime(const Diana::QEvent &ev)
std::map< int, int > ChannelTowerMap
std::map< int, double > fChannelPositionY
double GetDistanceMmChannel(const int ch1, const int ch2)
void Do(Diana::QEvent &ev)
std::map< int, double > fChannelPositionX
std::string fSyncType
std::string fEnergyLabel
std::map< int, ChannelInfo > fSyncTimeDifference
std::string fOFLabel
std::vector< TimeNeighbour * > timeNeighbours
std::vector< std::string > fPSALabels
std::set< int > fBlackList
std::string fSyncSource
std::map< int, int > ChannelPositionMap
std::map< int, int > ChannelFloorMap
std::string fSyncTowerSource
base types wrapped into a QObject. Currently implemented types are QInt QDouble and QFloat....
Definition: QBaseType.hh:17
coincidence data
std::vector< QCoincidentChannel > fCoincidentChannels
data of coincident channel
diana event
Definition: QEvent.hh:46
const Q & GetByLabel(const QEventLabel &label) const
Get a QObject in read mode by label.
Definition: QEvent.hh:135
void RequireByLabel(const QEventLabel &label) const
notify the QEvent that we need a QObject, if not found an exception is thrown
Definition: QEvent.hh:242
void Require(const std::string &owner, const std::string &name) const
notify the QEvent that we need a QObject, if not found an exception is thrown
Definition: QEvent.hh:232
void Get(const char *owner, ReadHandle< Q > &handle) const
Get a QObject Handle in read mode.
Definition: QEvent.hh:74
void Add(WriteHandle< Q > &handle)
Add a QObject to the event.
Definition: QEvent.hh:193
Raw event: basic information like run number and time.
Definition: QHeader.hh:16
const Diana::QTime & GetTime() const
get time
Definition: QHeader.hh:28
int GetRun() const
destructor
Definition: QHeader.hh:22
global handle for jitter by coincidence
jitter by coincidence
Definition: QJitter.hh:17
int fRefChannel
Definition: QJitter.hh:28
double fJitter
Definition: QJitter.hh:29
Raw event: bolometer channel, trigger positions and types.
Definition: QPulseInfo.hh:18
global handle for QRunData
const QChannelRunData & GetChannelRunData(const int channel) const
get channel based run data quantities
Definition: QRunData.cc:339
ULong64_t fEventNumber
TimeNeighbour(ULong64_t, ULong64_t, int, double, double)
the Diana namespace is needed because sometimes we use Qt libraries, that use same class names of our...