SpacePointAlg.cxx
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////
2 ///
3 /// \file SpacePointAlg.cxx
4 ///
5 /// \brief Algorithm for generating space points from hits.
6 ///
7 /// \author H. Greenlee
8 ///
9 ////////////////////////////////////////////////////////////////////////
10 
13 #include "cetlib_except/exception.h"
21 #include <algorithm>
22 #include <cassert>
23 #include <cmath>
24 #include <iostream>
25 #include <map>
26 //\todo Remove include of BackTrackerService.h once this algorithm is stripped of test for MC
32 
33 //----------------------------------------------------------------------
34 // Constructor.
35 //
36 namespace trkf {
37 
39  : fMaxDT{pset.get<double>("MaxDT")}
40  , fMaxS{pset.get<double>("MaxS")}
41  , fMinViews{pset.get<int>("MinViews")}
42  , fEnableU{pset.get<bool>("EnableU")}
43  , fEnableV{pset.get<bool>("EnableV")}
44  , fEnableW{pset.get<bool>("EnableW")}
45  , fFilter{pset.get<bool>("Filter")}
46  , fMerge{pset.get<bool>("Merge")}
47  , fPreferColl{pset.get<bool>("PreferColl")}
48  , fTickOffsetU{pset.get<double>("TickOffsetU", 0.)}
49  , fTickOffsetV{pset.get<double>("TickOffsetV", 0.)}
50  , fTickOffsetW{pset.get<double>("TickOffsetW", 0.)}
51  {
52  // Only allow one of fFilter and fMerge to be true.
53 
54  if (fFilter && fMerge)
55  throw cet::exception("SpacePointAlg") << "Filter and Merge flags are both true.\n";
56 
57  // Report.
58 
59  std::cout << "SpacePointAlg configured with the following parameters:\n"
60  << " MaxDT = " << fMaxDT << "\n"
61  << " MaxS = " << fMaxS << "\n"
62  << " MinViews = " << fMinViews << "\n"
63  << " EnableU = " << fEnableU << "\n"
64  << " EnableV = " << fEnableV << "\n"
65  << " EnableW = " << fEnableW << "\n"
66  << " Filter = " << fFilter << "\n"
67  << " Merge = " << fMerge << "\n"
68  << " PreferColl = " << fPreferColl << "\n"
69  << " TickOffsetU = " << fTickOffsetU << "\n"
70  << " TickOffsetV = " << fTickOffsetV << "\n"
71  << " TickOffsetW = " << fTickOffsetW << std::endl;
72  }
73 
74  //----------------------------------------------------------------------
75  // Print geometry and properties constants.
76  //
77  void
79  {
80  // Generate info report on first call only.
81 
82  static bool first = true;
83  bool report = first;
84  first = false;
85 
86  // Get services.
87 
89 
90  // Calculate and print geometry information.
91 
92  if (report) mf::LogInfo("SpacePointAlg") << "Updating geometry constants.\n";
93 
94  for (unsigned int cstat = 0; cstat < geom->Ncryostats(); ++cstat) {
95 
96  // Loop over TPCs.
97 
98  unsigned int const ntpc = geom->Cryostat(cstat).NTPC();
99 
100  for (unsigned int tpc = 0; tpc < ntpc; ++tpc) {
101  const geo::TPCGeo& tpcgeom = geom->Cryostat(cstat).TPC(tpc);
102 
103  // Loop over planes.
104 
105  unsigned int const nplane = tpcgeom.Nplanes();
106 
107  for (unsigned int plane = 0; plane < nplane; ++plane) {
108  geo::PlaneID planeid(cstat, tpc, plane);
109  const geo::PlaneGeo& pgeom = tpcgeom.Plane(planeid);
110 
111  // Fill view-dependent quantities.
112 
113  geo::View_t view = pgeom.View();
114  std::string viewname = "?";
115  if (view == geo::kU) { viewname = "U"; }
116  else if (view == geo::kV) {
117  viewname = "V";
118  }
119  else if (view == geo::kZ) {
120  viewname = "Z";
121  }
122  else
123  throw cet::exception("SpacePointAlg") << "Bad view = " << view << "\n";
124 
125  std::string sigtypename = "?";
126  geo::SigType_t sigtype = geom->SignalType(planeid);
127  if (sigtype == geo::kInduction)
128  sigtypename = "Induction";
129  else if (sigtype == geo::kCollection)
130  sigtypename = "Collection";
131  else
132  throw cet::exception("SpacePointAlg") << "Bad signal type = " << sigtype << "\n";
133 
134  std::string orientname = "?";
135  geo::Orient_t orient = pgeom.Orientation();
136  if (orient == geo::kVertical)
137  orientname = "Vertical";
138  else if (orient == geo::kHorizontal)
139  orientname = "Horizontal";
140  else
141  throw cet::exception("SpacePointAlg") << "Bad orientation = " << orient << "\n";
142 
143  if (report) {
144  const double* xyz = tpcgeom.PlaneLocation(plane);
145  mf::LogInfo("SpacePointAlg")
146  << "\nCryostat, TPC, Plane: " << cstat << "," << tpc << ", " << plane << "\n"
147  << " View: " << viewname << "\n"
148  << " SignalType: " << sigtypename << "\n"
149  << " Orientation: " << orientname << "\n"
150  << " Plane location: " << xyz[0] << "\n"
151  << " Plane pitch: " << tpcgeom.Plane0Pitch(plane) << "\n"
152  << " Wire angle: " << tpcgeom.Plane(plane).Wire(0).ThetaZ() << "\n"
153  << " Wire pitch: " << tpcgeom.WirePitch() << "\n"
154  << " Time offset: " << detProp.GetXTicksOffset(plane, tpc, cstat) << "\n";
155  }
156 
157  if (orient != geo::kVertical)
158  throw cet::exception("SpacePointAlg") << "Horizontal wire geometry not implemented.\n";
159  } // end loop over planes
160  } // end loop over tpcs
161  } // end loop over cryostats
162  }
163 
164  //----------------------------------------------------------------------
165  // Get corrected time for the specified hit.
166  double
168  const recob::Hit& hit) const
169  {
170  // Get services.
171 
173 
174  // Correct time for trigger offset and plane-dependent time offsets.
175 
176  double t = hit.PeakTime() -
177  detProp.GetXTicksOffset(hit.WireID().Plane, hit.WireID().TPC, hit.WireID().Cryostat);
178  if (hit.View() == geo::kU)
179  t -= fTickOffsetU;
180  else if (hit.View() == geo::kV)
181  t -= fTickOffsetV;
182  else if (hit.View() == geo::kW)
183  t -= fTickOffsetW;
184 
185  return t;
186  }
187 
188  //----------------------------------------------------------------------
189  // Spatial separation of hits (zero if two or fewer).
190  double
192  {
193  // Get geometry service.
194 
196 
197  // Trivial case - fewer than three hits.
198 
199  if (hits.size() < 3) return 0.;
200 
201  // Error case - more than three hits.
202 
203  if (hits.size() > 3) {
204  mf::LogError("SpacePointAlg") << "Method separation called with more than three htis.";
205  return 0.;
206  }
207 
208  // Got exactly three hits.
209 
210  // Calculate angles and distance of each hit from origin.
211 
212  double dist[3] = {0., 0., 0.};
213  double sinth[3] = {0., 0., 0.};
214  double costh[3] = {0., 0., 0.};
215  unsigned int cstats[3];
216  unsigned int tpcs[3];
217  unsigned int planes[3];
218 
219  for (int i = 0; i < 3; ++i) {
220 
221  // Get tpc, plane, wire.
222 
223  const recob::Hit& hit = *(hits[i]);
224  const geo::WireGeo& wgeom = geom->WireIDToWireGeo(hit.WireID());
225  cstats[i] = hit.WireID().Cryostat;
226  tpcs[i] = hit.WireID().TPC;
227  planes[i] = hit.WireID().Plane;
228 
229  // Check tpc and plane errors.
230 
231  for (int j = 0; j < i; ++j) {
232 
233  if (cstats[j] != hit.WireID().Cryostat) {
234  mf::LogError("SpacePointAlg")
235  << "Method separation called with hits from multiple cryostats..";
236  return 0.;
237  }
238 
239  if (tpcs[j] != hit.WireID().TPC) {
240  mf::LogError("SpacePointAlg")
241  << "Method separation called with hits from multiple tpcs..";
242  return 0.;
243  }
244 
245  if (planes[j] == hit.WireID().Plane) {
246  mf::LogError("SpacePointAlg")
247  << "Method separation called with hits from the same plane..";
248  return 0.;
249  }
250  }
251 
252  // Get angles and distance of wire.
253 
254  double hl = wgeom.HalfL();
255  double xyz[3];
256  double xyz1[3];
257  wgeom.GetCenter(xyz);
258  wgeom.GetCenter(xyz1, hl);
259  double s = (xyz1[1] - xyz[1]) / hl;
260  double c = (xyz1[2] - xyz[2]) / hl;
261  sinth[hit.WireID().Plane] = s;
262  costh[hit.WireID().Plane] = c;
263  dist[hit.WireID().Plane] = xyz[2] * s - xyz[1] * c;
264  }
265 
266  double S = ((sinth[1] * costh[2] - costh[1] * sinth[2]) * dist[0] +
267  (sinth[2] * costh[0] - costh[2] * sinth[0]) * dist[1] +
268  (sinth[0] * costh[1] - costh[0] * sinth[1]) * dist[2]);
269  return S;
270  }
271 
272  //----------------------------------------------------------------------
273  // Check hits for compatibility.
274  // Check hits pairwise for different views and maximum time difference.
275  // Check three hits for spatial compatibility.
276  bool
278  const art::PtrVector<recob::Hit>& hits,
279  bool useMC) const
280  {
282 
283  int nhits = hits.size();
284 
285  // Fewer than two or more than three hits can never be compatible.
286 
287  bool result = nhits >= 2 && nhits <= 3;
288  bool mc_ok = true;
289  unsigned int tpc = 0;
290  unsigned int cstat = 0;
291 
292  if (result) {
293 
294  // First do pairwise tests.
295  // Do double loop over hits.
296 
297  for (int ihit1 = 0; result && ihit1 < nhits - 1; ++ihit1) {
298  const recob::Hit& hit1 = *(hits[ihit1]);
299  geo::WireID hit1WireID = hit1.WireID();
300  geo::View_t view1 = hit1.View();
301 
302  double t1 = hit1.PeakTime() -
303  detProp.GetXTicksOffset(hit1WireID.Plane, hit1WireID.TPC, hit1WireID.Cryostat);
304 
305  // If using mc information, get a collection of track ids for hit 1.
306  // If not using mc information, this section of code will trigger the
307  // insertion of a single invalid HitMCInfo object into fHitMCMap.
308 
309  const HitMCInfo& mcinfo1 = fHitMCMap[(useMC ? &hit1 : 0)];
310  const std::vector<int>& tid1 = mcinfo1.trackIDs;
311  bool only_neg1 = tid1.size() > 0 && tid1.back() < 0;
312 
313  // Loop over second hit.
314 
315  for (int ihit2 = ihit1 + 1; result && ihit2 < nhits; ++ihit2) {
316  const recob::Hit& hit2 = *(hits[ihit2]);
317  geo::WireID hit2WireID = hit2.WireID();
318  geo::View_t view2 = hit2.View();
319 
320  // Test for same tpc and different views.
321 
322  result = result && hit1WireID.TPC == hit2WireID.TPC && view1 != view2 &&
323  hit1WireID.Cryostat == hit2WireID.Cryostat;
324  if (result) {
325 
326  // Remember which tpc and cryostat we are in.
327 
328  tpc = hit1WireID.TPC;
329  cstat = hit1WireID.Cryostat;
330 
331  double t2 = hit2.PeakTime() - detProp.GetXTicksOffset(
332  hit2WireID.Plane, hit2WireID.TPC, hit2WireID.Cryostat);
333 
334  // Test maximum time difference.
335 
336  result = result && std::abs(t1 - t2) <= fMaxDT;
337 
338  // Test mc truth.
339 
340  if (result && useMC) {
341 
342  // Test whether hits have a common parent track id.
343 
344  const HitMCInfo& mcinfo2 = fHitMCMap[&hit2];
345  std::vector<int> tid2 = mcinfo2.trackIDs;
346  bool only_neg2 = tid2.size() > 0 && tid2.back() < 0;
347  std::vector<int>::iterator it = std::set_intersection(
348  tid1.begin(), tid1.end(), tid2.begin(), tid2.end(), tid2.begin());
349  tid2.resize(it - tid2.begin());
350 
351  // Hits are compatible if they have parents in common.
352  // If the only parent id in common is negative (-999),
353  // then hits are compatible only if both hits have only
354  // negative parent tracks.
355 
356  bool only_neg3 = tid2.size() > 0 && tid2.back() < 0;
357  mc_ok = tid2.size() > 0 && (!only_neg3 || (only_neg1 && only_neg2));
358  result = result && mc_ok;
359 
360  // If we are still OK, check that either hit is
361  // the nearest neighbor of the other.
362 
363  if (result) {
364  result = mcinfo1.pchit[hit2WireID.Plane] == &hit2 ||
365  mcinfo2.pchit[hit1WireID.Plane] == &hit1;
366  }
367  }
368  }
369  }
370  }
371 
372  // If there are exactly three hits, and they pass pairwise tests, check
373  // for spatial compatibility.
374 
375  if (result && nhits == 3) {
376 
377  // Loop over hits.
378 
379  double dist[3] = {0., 0., 0.};
380  double sinth[3] = {0., 0., 0.};
381  double costh[3] = {0., 0., 0.};
382 
383  for (int i = 0; i < 3; ++i) {
384 
385  // Get tpc, plane, wire.
386 
387  const recob::Hit& hit = *(hits[i]);
388  geo::WireID hitWireID = hit.WireID();
389 
390  const geo::WireGeo& wgeom = geom->WireIDToWireGeo(hit.WireID());
391  if ((hitWireID.TPC != tpc) || (hitWireID.Cryostat != cstat))
392  throw cet::exception("SpacePointAlg") << "compatible(): geometry mismatch\n";
393 
394  // Get angles and distance of wire.
395 
396  double hl = wgeom.HalfL();
397  double xyz[3];
398  double xyz1[3];
399  wgeom.GetCenter(xyz);
400  wgeom.GetCenter(xyz1, hl);
401  double s = (xyz1[1] - xyz[1]) / hl;
402  double c = (xyz1[2] - xyz[2]) / hl;
403  sinth[hit.WireID().Plane] = s;
404  costh[hit.WireID().Plane] = c;
405  dist[hit.WireID().Plane] = xyz[2] * s - xyz[1] * c;
406  }
407 
408  // Do space cut.
409 
410  double S = ((sinth[1] * costh[2] - costh[1] * sinth[2]) * dist[0] +
411  (sinth[2] * costh[0] - costh[2] * sinth[0]) * dist[1] +
412  (sinth[0] * costh[1] - costh[0] * sinth[1]) * dist[2]);
413 
414  result = result && std::abs(S) < fMaxS;
415  }
416  }
417 
418  // Done.
419 
420  return result;
421  }
422 
423  //----------------------------------------------------------------------
424  // Fill one space point using a colleciton of hits.
425  // Assume points have already been tested for compatibility.
426  //
427  void
429  const art::PtrVector<recob::Hit>& hits,
430  std::vector<recob::SpacePoint>& sptv,
431  int sptid) const
432  {
434 
435  double timePitch = detProp.GetXTicksCoefficient();
436 
437  int nhits = hits.size();
438 
439  // Remember associated hits internally.
440 
441  if (fSptHitMap.find(sptid) != fSptHitMap.end())
442  throw cet::exception("SpacePointAlg") << "fillSpacePoint(): hit already present!\n";
443  fSptHitMap[sptid] = hits;
444 
445  // Calculate position and error matrix.
446 
447  double xyz[3] = {0., 0., 0.};
448  double errxyz[6] = {0., 0., 0., 0., 0., 0.};
449 
450  // Calculate x using drift times.
451  // Loop over all hits and calculate the weighted average drift time.
452  // Also calculate time variance and chisquare.
453 
454  double sumt2w = 0.;
455  double sumtw = 0.;
456  double sumw = 0.;
457 
458  for (art::PtrVector<recob::Hit>::const_iterator ihit = hits.begin(); ihit != hits.end();
459  ++ihit) {
460 
461  const recob::Hit& hit = **ihit;
462  geo::WireID hitWireID = hit.WireID();
463 
464  // Correct time for trigger offset and view-dependent time offsets.
465 
466  double t0 = detProp.GetXTicksOffset(hitWireID.Plane, hitWireID.TPC, hitWireID.Cryostat);
467  double t = hit.PeakTime() - t0;
468  double et = hit.SigmaPeakTime();
469  double w = 1. / (et * et);
470 
471  sumt2w += w * t * t;
472  sumtw += w * t;
473  sumw += w;
474  }
475 
476  double drift_time = 0.;
477  double var_time = 0.;
478  double chisq = 0.;
479  if (sumw != 0.) {
480  drift_time = sumtw / sumw;
481  //var_time = sumt2w / sumw - drift_time * drift_time;
482  var_time = 1. / sumw;
483  if (var_time < 0.) var_time = 0.;
484  chisq = sumt2w - sumtw * drift_time;
485  if (chisq < 0.) chisq = 0.;
486  }
487  xyz[0] = drift_time * timePitch;
488  errxyz[0] = var_time * timePitch * timePitch;
489 
490  // Calculate y, z using wires (need at least two hits).
491 
492  if (nhits >= 2) {
493 
494  // Calculate y and z by chisquare minimization of wire coordinates.
495 
496  double sw = 0.; // sum w_i
497  double sus = 0.; // sum w_i u_i sin_th_i
498  double suc = 0.; // sum w_i u_i cos_th_i
499  double sc2 = 0.; // sum w_i cos2_th_i
500  double ss2 = 0.; // sum w_i sin2_th_i
501  double ssc = 0.; // sum w_i sin_th_i cos_th_i
502 
503  // Loop over points.
504 
505  for (art::PtrVector<recob::Hit>::const_iterator ihit = hits.begin(); ihit != hits.end();
506  ++ihit) {
507 
508  const recob::Hit& hit = **ihit;
509  geo::WireID hitWireID = hit.WireID();
510  const geo::WireGeo& wgeom = geom->WireIDToWireGeo(hit.WireID());
511 
512  // Calculate angle and wire coordinate in this view.
513 
514  double hl = wgeom.HalfL();
515  double cen[3];
516  double cen1[3];
517  wgeom.GetCenter(cen);
518  wgeom.GetCenter(cen1, hl);
519  double s = (cen1[1] - cen[1]) / hl;
520  double c = (cen1[2] - cen[2]) / hl;
521  double u = cen[2] * s - cen[1] * c;
522  double eu = geom->WirePitch(hitWireID.Plane, hitWireID.TPC) / std::sqrt(12.);
523  double w = 1. / (eu * eu);
524 
525  // Summations
526 
527  sw += w;
528  sus += w * u * s;
529  suc += w * u * c;
530  sc2 += w * c * c;
531  ss2 += w * s * s;
532  ssc += w * s * c;
533  }
534 
535  // Calculate y,z
536 
537  double denom = sc2 * ss2 - ssc * ssc;
538  if (denom != 0.) {
539  xyz[1] = (-suc * ss2 + sus * ssc) / denom;
540  xyz[2] = (sus * sc2 - suc * ssc) / denom;
541  errxyz[2] = ss2 / denom;
542  errxyz[4] = ssc / denom;
543  errxyz[5] = sc2 / denom;
544  }
545 
546  // Make space point.
547 
548  recob::SpacePoint spt(xyz, errxyz, chisq, sptid);
549  sptv.push_back(spt);
550  }
551  return;
552  }
553 
554  /// Fill a collection of space points.
555  ///
556  /// Arguments:
557  ///
558  /// spts - Collection of space points to fill.
559  /// sptalg - Space point algorithm object.
560  ///
561  /// This method uses the hits contained in this track to construct
562  /// space points.
563  ///
564  /// This method does not have any knowledge of what constitutes a
565  /// good space point, except that Hits are required to be
566  /// consecutive when sorted by path distance, and space points are
567  /// required to pass compatibility tests used by the space point
568  /// algorithm object. This method will make space points from
569  /// either two or three Hits (even for three-plane detectors), if
570  /// the space point algorithm is configured to allow it.
571  ///
572  void
574  std::vector<recob::SpacePoint>& spts,
575  std::multimap<double, KHitTrack> const& trackMap) const
576  {
577  // Loop over KHitTracks.
578 
580  art::PtrVector<recob::Hit> compatible_hits;
581  for (std::multimap<double, KHitTrack>::const_iterator it = trackMap.begin();
582  it != trackMap.end();
583  ++it) {
584  const KHitTrack& track = (*it).second;
585 
586  // Extrack Hit from track.
587 
588  const std::shared_ptr<const KHitBase>& hit = track.getHit();
589  const KHitWireX* phit = dynamic_cast<const KHitWireX*>(&*hit);
590  if (phit != 0) {
591  const art::Ptr<recob::Hit> prhit = phit->getHit();
592 
593  // Test this hit for compatibility.
594 
595  hits.push_back(prhit);
596  bool ok = this->compatible(detProp, hits);
597  if (!ok) {
598 
599  // The new hit is not compatible. Make a space point out of
600  // the last known compatible hits, provided there are at least
601  // two.
602 
603  if (compatible_hits.size() >= 2) {
604  this->fillSpacePoint(detProp, compatible_hits, spts, this->numHitMap());
605  compatible_hits.clear();
606  }
607 
608  // Forget about any previous hits.
609 
610  hits.clear();
611  hits.push_back(prhit);
612  }
613 
614  // Update the list of known compatible hits.
615 
616  compatible_hits = hits;
617  }
618  }
619 
620  // Maybe make one final space point.
621 
622  if (compatible_hits.size() >= 2) {
623  this->fillSpacePoint(detProp, compatible_hits, spts, this->numHitMap());
624  }
625  }
626 
627  //----------------------------------------------------------------------
628  // Fill one space point using a colleciton of hits.
629  // Assume points have already been tested for compatibility.
630  // This version assumes there can be multiple hits per view,
631  // and gives unequal weight to different hits.
632  //
633  void
635  const art::PtrVector<recob::Hit>& hits,
636  std::vector<recob::SpacePoint>& sptv,
637  int sptid) const
638  {
640 
641  // Calculate time pitch.
642 
643  double timePitch = detProp.GetXTicksCoefficient(); // cm / tick
644 
645  // Figure out which tpc we are in.
646 
647  unsigned int tpc0 = 0;
648  unsigned int cstat0 = 0;
649  int nhits = hits.size();
650  if (nhits > 0) {
651  tpc0 = hits.front()->WireID().TPC;
652  cstat0 = hits.front()->WireID().Cryostat;
653  }
654 
655  // Remember associated hits internally.
656 
657  if (fSptHitMap.count(sptid) != 0)
658  throw cet::exception("SpacePointAlg") << "fillComplexSpacePoint(): hit already present!\n";
659  fSptHitMap[sptid] = hits;
660 
661  // Do a preliminary scan of hits.
662  // Determine weight given to hits in each view.
663 
664  unsigned int nplanes = geom->Cryostat(cstat0).TPC(tpc0).Nplanes();
665  std::vector<int> numhits(nplanes, 0);
666  std::vector<double> weight(nplanes, 0.);
667 
668  for (art::PtrVector<recob::Hit>::const_iterator ihit = hits.begin(); ihit != hits.end();
669  ++ihit) {
670 
671  const recob::Hit& hit = **ihit;
672  geo::WireID hitWireID = hit.WireID();
673  // kept as assertions for performance reasons
674  assert(hitWireID.Cryostat == cstat0);
675  assert(hitWireID.TPC == tpc0);
676  assert(hitWireID.Plane < nplanes);
677  ++numhits[hitWireID.Plane];
678  }
679 
680  for (unsigned int plane = 0; plane < nplanes; ++plane) {
681  double np = numhits[plane];
682  if (np > 0.) weight[plane] = 1. / (np * np * np);
683  }
684 
685  // Calculate position and error matrix.
686 
687  double xyz[3] = {0., 0., 0.};
688  double errxyz[6] = {0., 0., 0., 0., 0., 0.};
689 
690  // Calculate x using drift times.
691  // Loop over all hits and calculate the weighted average drift time.
692  // Also calculate time variance and chisquare.
693 
694  double sumt2w = 0.;
695  double sumtw = 0.;
696  double sumw = 0.;
697 
698  for (art::PtrVector<recob::Hit>::const_iterator ihit = hits.begin(); ihit != hits.end();
699  ++ihit) {
700 
701  const recob::Hit& hit = **ihit;
702  geo::WireID hitWireID = hit.WireID();
703 
704  // Correct time for trigger offset and view-dependent time offsets.
705 
706  double t0 = detProp.GetXTicksOffset(hitWireID.Plane, hitWireID.TPC, hitWireID.Cryostat);
707  double t = hit.PeakTime() - t0;
708  double et = hit.SigmaPeakTime();
709  double w = weight[hitWireID.Plane] / (et * et);
710 
711  sumt2w += w * t * t;
712  sumtw += w * t;
713  sumw += w;
714  }
715 
716  double drift_time = 0.;
717  double var_time = 0.;
718  double chisq = 0.;
719  if (sumw != 0.) {
720  drift_time = sumtw / sumw;
721  var_time = sumt2w / sumw - drift_time * drift_time;
722  if (var_time < 0.) var_time = 0.;
723  chisq = sumt2w - sumtw * drift_time;
724  if (chisq < 0.) chisq = 0.;
725  }
726  xyz[0] = drift_time * timePitch;
727  errxyz[0] = var_time * timePitch * timePitch;
728 
729  // Calculate y, z using wires (need at least two hits).
730 
731  if (nhits >= 2) {
732 
733  // Calculate y and z by chisquare minimization of wire coordinates.
734 
735  double sw = 0.; // sum w_i
736  double sus = 0.; // sum w_i u_i sin_th_i
737  double suc = 0.; // sum w_i u_i cos_th_i
738  double sc2 = 0.; // sum w_i cos2_th_i
739  double ss2 = 0.; // sum w_i sin2_th_i
740  double ssc = 0.; // sum w_i sin_th_i cos_th_i
741 
742  // Loop over points.
743 
744  for (art::PtrVector<recob::Hit>::const_iterator ihit = hits.begin(); ihit != hits.end();
745  ++ihit) {
746 
747  const recob::Hit& hit = **ihit;
748  geo::WireID hitWireID = hit.WireID();
749  const geo::WireGeo& wgeom = geom->WireIDToWireGeo(hit.WireID());
750 
751  // Calculate angle and wire coordinate in this view.
752 
753  double hl = wgeom.HalfL();
754  double cen[3];
755  double cen1[3];
756  wgeom.GetCenter(cen);
757  wgeom.GetCenter(cen1, hl);
758  double s = (cen1[1] - cen[1]) / hl;
759  double c = (cen1[2] - cen[2]) / hl;
760  double u = cen[2] * s - cen[1] * c;
761  double eu = geom->WirePitch(hitWireID.Plane, hitWireID.TPC) / std::sqrt(12.);
762  double w = weight[hitWireID.Plane] / (eu * eu);
763 
764  // Summations
765 
766  sw += w;
767  sus += w * u * s;
768  suc += w * u * c;
769  sc2 += w * c * c;
770  ss2 += w * s * s;
771  ssc += w * s * c;
772  }
773 
774  // Calculate y,z
775 
776  double denom = sc2 * ss2 - ssc * ssc;
777  if (denom != 0.) {
778  xyz[1] = (-suc * ss2 + sus * ssc) / denom;
779  xyz[2] = (sus * sc2 - suc * ssc) / denom;
780  errxyz[2] = ss2 / denom;
781  errxyz[4] = ssc / denom;
782  errxyz[5] = sc2 / denom;
783  }
784 
785  // Make space point.
786 
787  recob::SpacePoint spt(xyz, errxyz, chisq, sptid);
788  sptv.push_back(spt);
789  }
790  }
791 
792  //----------------------------------------------------------------------
793  // Fill a vector of space points for all compatible combinations of hits
794  // from an input vector of hits (non-mc-truth version).
795  //
796  void
798  detinfo::DetectorPropertiesData const& detProp,
799  const art::PtrVector<recob::Hit>& hits,
800  std::vector<recob::SpacePoint>& spts) const
801  {
802  makeSpacePoints(clockData, detProp, hits, spts, false);
803  }
804 
805  //----------------------------------------------------------------------
806  // Fill a vector of space points for all compatible combinations of hits
807  // from an input vector of hits (mc-truth version).
808  //
809  void
811  detinfo::DetectorPropertiesData const& detProp,
812  const art::PtrVector<recob::Hit>& hits,
813  std::vector<recob::SpacePoint>& spts) const
814  {
815  makeSpacePoints(clockData, detProp, hits, spts, true);
816  }
817 
818  //----------------------------------------------------------------------
819  // Fill a vector of space points for all compatible combinations of hits
820  // from an input vector of hits (general version).
821  //
822  void
824  detinfo::DetectorPropertiesData const& detProp,
825  const art::PtrVector<recob::Hit>& hits,
826  std::vector<recob::SpacePoint>& spts,
827  bool useMC) const
828  {
830 
831  fSptHitMap.clear();
832 
833  // Print diagnostic information.
834 
835  update(detProp);
836 
837  // First make sure result vector is empty.
838 
839  spts.erase(spts.begin(), spts.end());
840 
841  // Statistics.
842 
843  int n2 = 0; // Number of two-hit space points.
844  int n3 = 0; // Number of three-hit space points.
845  int n2filt = 0; // Number of two-hit space points after filtering/merging.
846  int n3filt = 0; // Number of three-hit space pointe after filtering/merging.
847 
848  // Sort hits into maps indexed by [cryostat][tpc][plane][wire].
849  // If using mc information, also generate maps of sim::IDEs and mc
850  // position indexed by hit.
851 
852  std::vector<std::vector<std::vector<std::multimap<unsigned int, art::Ptr<recob::Hit>>>>> hitmap;
853  fHitMCMap.clear();
854 
855  unsigned int ncstat = geom->Ncryostats();
856  hitmap.resize(ncstat);
857  for (unsigned int cstat = 0; cstat < ncstat; ++cstat) {
858  unsigned int ntpc = geom->Cryostat(cstat).NTPC();
859  hitmap[cstat].resize(ntpc);
860  for (unsigned int tpc = 0; tpc < ntpc; ++tpc) {
861  int nplane = geom->Cryostat(cstat).TPC(tpc).Nplanes();
862  hitmap[cstat][tpc].resize(nplane);
863  }
864  }
865 
866  for (art::PtrVector<recob::Hit>::const_iterator ihit = hits.begin(); ihit != hits.end();
867  ++ihit) {
868  const art::Ptr<recob::Hit>& phit = *ihit;
869  geo::View_t view = phit->View();
870  if ((view == geo::kU && fEnableU) || (view == geo::kV && fEnableV) ||
871  (view == geo::kZ && fEnableW)) {
872  geo::WireID phitWireID = phit->WireID();
873  hitmap[phitWireID.Cryostat][phitWireID.TPC][phitWireID.Plane].insert(
874  std::make_pair(phitWireID.Wire, phit));
875  }
876  }
877 
878  // Fill mc information, including IDEs and closest neighbors
879  // of each hit.
880  ///\todo Why are we still checking on whether this is MC or not?
881  ///\todo Such checks should not be in reconstruction code.
882  if (useMC) {
884 
885  // First loop over hits and fill track ids and mc position.
886  for (unsigned int cstat = 0; cstat < ncstat; ++cstat) {
887  for (unsigned int tpc = 0; tpc < geom->Cryostat(cstat).NTPC(); ++tpc) {
888  int nplane = geom->Cryostat(cstat).TPC(tpc).Nplanes();
889  for (int plane = 0; plane < nplane; ++plane) {
890  for (std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator ihit =
891  hitmap[cstat][tpc][plane].begin();
892  ihit != hitmap[cstat][tpc][plane].end();
893  ++ihit) {
894  const art::Ptr<recob::Hit>& phit = ihit->second;
895  const recob::Hit& hit = *phit;
896  HitMCInfo& mcinfo = fHitMCMap[&hit]; // Default HitMCInfo.
897 
898  // Fill default nearest neighbor information (i.e. none).
899 
900  mcinfo.pchit.resize(nplane, 0);
901  mcinfo.dist2.resize(nplane, 1.e20);
902 
903  // Get sim::IDEs for this hit.
904 
905  std::vector<sim::IDE> ides = bt_serv->HitToAvgSimIDEs(clockData, phit);
906 
907  // Get sorted track ids. for this hit.
908 
909  mcinfo.trackIDs.reserve(ides.size());
910  for (std::vector<sim::IDE>::const_iterator i = ides.begin(); i != ides.end(); ++i)
911  mcinfo.trackIDs.push_back(i->trackID);
912  sort(mcinfo.trackIDs.begin(), mcinfo.trackIDs.end());
913 
914  // Get position of ionization for this hit.
915 
916  try {
917  mcinfo.xyz = bt_serv->SimIDEsToXYZ(ides);
918  }
919  catch (cet::exception& x) {
920  mcinfo.xyz.clear();
921  }
922  } // end loop over ihit
923  } // end loop oer planes
924  } // end loop over TPCs
925  } // end loop over cryostats
926 
927  // Loop over hits again and fill nearest neighbor information for real.
928  for (unsigned int cstat = 0; cstat < ncstat; ++cstat) {
929  for (unsigned int tpc = 0; tpc < geom->Cryostat(cstat).NTPC(); ++tpc) {
930  int nplane = geom->Cryostat(cstat).TPC(tpc).Nplanes();
931  for (int plane = 0; plane < nplane; ++plane) {
932  for (std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator ihit =
933  hitmap[cstat][tpc][plane].begin();
934  ihit != hitmap[cstat][tpc][plane].end();
935  ++ihit) {
936  const art::Ptr<recob::Hit>& phit = ihit->second;
937  const recob::Hit& hit = *phit;
938  HitMCInfo& mcinfo = fHitMCMap[&hit];
939  if (mcinfo.xyz.size() != 0) {
940  assert(mcinfo.xyz.size() == 3);
941 
942  // Fill nearest neighbor information for this hit.
943 
944  for (int plane2 = 0; plane2 < nplane; ++plane2) {
945  for (std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator jhit =
946  hitmap[cstat][tpc][plane2].begin();
947  jhit != hitmap[cstat][tpc][plane2].end();
948  ++jhit) {
949  const art::Ptr<recob::Hit>& phit2 = jhit->second;
950  const recob::Hit& hit2 = *phit2;
951  const HitMCInfo& mcinfo2 = fHitMCMap[&hit2];
952 
953  if (mcinfo2.xyz.size() != 0) {
954  assert(mcinfo2.xyz.size() == 3);
955  double dx = mcinfo.xyz[0] - mcinfo2.xyz[0];
956  double dy = mcinfo.xyz[1] - mcinfo2.xyz[1];
957  double dz = mcinfo.xyz[2] - mcinfo2.xyz[2];
958  double dist2 = dx * dx + dy * dy + dz * dz;
959  if (dist2 < mcinfo.dist2[plane2]) {
960  mcinfo.dist2[plane2] = dist2;
961  mcinfo.pchit[plane2] = &hit2;
962  }
963  } // end if mcinfo2.xyz valid
964  } // end loop over jhit
965  } // end loop over plane2
966  } // end if mcinfo.xyz valid.
967  } // end loop over ihit
968  } // end loop over plane
969  } // end loop over tpc
970  } // end loop over cryostats
971  } // end if MC
972 
973  // use mf::LogDebug instead of MF_LOG_DEBUG because we reuse it in many lines
974  // insertions are protected by mf::isDebugEnabled()
975  mf::LogDebug debug("SpacePointAlg");
976  if (mf::isDebugEnabled()) {
977  debug << "Total hits = " << hits.size() << "\n\n";
978 
979  for (unsigned int cstat = 0; cstat < ncstat; ++cstat) {
980  for (unsigned int tpc = 0; tpc < geom->Cryostat(cstat).NTPC(); ++tpc) {
981  int nplane = hitmap[cstat][tpc].size();
982  for (int plane = 0; plane < nplane; ++plane) {
983  debug << "TPC, Plane: " << tpc << ", " << plane
984  << ", hits = " << hitmap[cstat][tpc][plane].size() << "\n";
985  }
986  }
987  } // end loop over cryostats
988  } // if debug
989 
990  // Make empty multimap from hit pointer on preferred
991  // (most-populated or collection) plane to space points that
992  // include that hit (used for sorting, filtering, and
993  // merging).
994 
995  typedef const recob::Hit* sptkey_type;
996  std::multimap<sptkey_type, recob::SpacePoint> sptmap;
997  std::set<sptkey_type> sptkeys; // Keys of multimap.
998 
999  // Loop over TPCs.
1000  for (unsigned int cstat = 0; cstat < ncstat; ++cstat) {
1001  for (unsigned int tpc = 0; tpc < geom->Cryostat(cstat).NTPC(); ++tpc) {
1002 
1003  geo::TPCID tpcid(cstat, tpc);
1004 
1005  // Sort maps in increasing order of number of hits.
1006  // This is so that we can do the outer loops over hits
1007  // over the views with fewer hits.
1008  //
1009  // If config parameter PreferColl is true, treat the colleciton
1010  // plane as if it had the most hits, regardless of how many
1011  // hits it actually has. This will force space points to be
1012  // filtered and merged with respect to the collection plane
1013  // wires. It will also force space points to be sorted by
1014  // collection plane wire.
1015 
1016  int nplane = hitmap[cstat][tpc].size();
1017  std::vector<int> index(nplane);
1018 
1019  for (int i = 0; i < nplane; ++i)
1020  index[i] = i;
1021 
1022  for (int i = 0; i < nplane - 1; ++i) {
1023 
1024  for (int j = i + 1; j < nplane; ++j) {
1025  bool icoll =
1026  fPreferColl && geom->SignalType(geo::PlaneID(tpcid, index[i])) == geo::kCollection;
1027  bool jcoll =
1028  fPreferColl && geom->SignalType(geo::PlaneID(tpcid, index[j])) == geo::kCollection;
1029  if ((hitmap[cstat][tpc][index[i]].size() > hitmap[cstat][tpc][index[j]].size() &&
1030  !jcoll) ||
1031  icoll) {
1032  int temp = index[i];
1033  index[i] = index[j];
1034  index[j] = temp;
1035  }
1036  }
1037  } // end loop over i
1038 
1039  // how many views with hits?
1040  // This will allow for the special case where we might have only 2 planes of information and
1041  // still want space points even if a three plane TPC
1042  std::vector<std::multimap<unsigned int, art::Ptr<recob::Hit>>>& hitsByPlaneVec =
1043  hitmap[cstat][tpc];
1044  int nViewsWithHits(0);
1045 
1046  for (int i = 0; i < nplane; i++) {
1047  if (hitsByPlaneVec[index[i]].size() > 0) nViewsWithHits++;
1048  }
1049 
1050  // If two-view space points are allowed, make a double loop
1051  // over hits and produce space points for compatible hit-pairs.
1052 
1053  if ((nViewsWithHits == 2 || nplane == 2) && fMinViews <= 2) {
1054 
1055  // Loop over pairs of views.
1056  for (int i = 0; i < nplane - 1; ++i) {
1057  unsigned int plane1 = index[i];
1058 
1059  if (hitmap[cstat][tpc][plane1].empty()) continue;
1060 
1061  for (int j = i + 1; j < nplane; ++j) {
1062  unsigned int plane2 = index[j];
1063 
1064  if (hitmap[cstat][tpc][plane2].empty()) continue;
1065 
1066  // Get angle, pitch, and offset of plane2 wires.
1067  const geo::WireGeo& wgeo2 = geom->Cryostat(cstat).TPC(tpc).Plane(plane2).Wire(0);
1068  double hl2 = wgeo2.HalfL();
1069  double xyz21[3];
1070  double xyz22[3];
1071  wgeo2.GetCenter(xyz21, -hl2);
1072  wgeo2.GetCenter(xyz22, hl2);
1073  double s2 = (xyz22[1] - xyz21[1]) / (2. * hl2);
1074  double c2 = (xyz22[2] - xyz21[2]) / (2. * hl2);
1075  double dist2 = -xyz21[1] * c2 + xyz21[2] * s2;
1076  double pitch2 = geom->WirePitch(plane2, tpc, cstat);
1077 
1078  if (!fPreferColl &&
1079  hitmap[cstat][tpc][plane1].size() > hitmap[cstat][tpc][plane2].size())
1080  throw cet::exception("SpacePointAlg")
1081  << "makeSpacePoints(): hitmaps with incompatible size\n";
1082 
1083  // Loop over pairs of hits.
1084 
1086  hitvec.reserve(2);
1087 
1088  for (std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator ihit1 =
1089  hitmap[cstat][tpc][plane1].begin();
1090  ihit1 != hitmap[cstat][tpc][plane1].end();
1091  ++ihit1) {
1092 
1093  const art::Ptr<recob::Hit>& phit1 = ihit1->second;
1094  geo::WireID phit1WireID = phit1->WireID();
1095  const geo::WireGeo& wgeo = geom->WireIDToWireGeo(phit1WireID);
1096 
1097  // Get endpoint coordinates of this wire.
1098  // (kept as assertions for performance reasons)
1099  assert(phit1WireID.Cryostat == cstat);
1100  assert(phit1WireID.TPC == tpc);
1101  assert(phit1WireID.Plane == plane1);
1102  double hl1 = wgeo.HalfL();
1103  double xyz1[3];
1104  double xyz2[3];
1105  wgeo.GetCenter(xyz1, -hl1);
1106  wgeo.GetCenter(xyz2, hl1);
1107 
1108  // Find the plane2 wire numbers corresponding to the endpoints.
1109 
1110  double wire21 = (-xyz1[1] * c2 + xyz1[2] * s2 - dist2) / pitch2;
1111  double wire22 = (-xyz2[1] * c2 + xyz2[2] * s2 - dist2) / pitch2;
1112 
1113  int wmin = std::max(0., std::min(wire21, wire22));
1114  int wmax = std::max(0., std::max(wire21, wire22) + 1.);
1115 
1116  std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator
1117  ihit2 = hitmap[cstat][tpc][plane2].lower_bound(wmin),
1118  ihit2end = hitmap[cstat][tpc][plane2].upper_bound(wmax);
1119 
1120  for (; ihit2 != ihit2end; ++ihit2) {
1121 
1122  const art::Ptr<recob::Hit>& phit2 = ihit2->second;
1123 
1124  // Check current pair of hits for compatibility.
1125  // By construction, hits should always have compatible views
1126  // and times, but may not have compatible mc information.
1127 
1128  hitvec.clear();
1129  hitvec.push_back(phit1);
1130  hitvec.push_back(phit2);
1131  bool ok = compatible(detProp, hitvec, useMC);
1132  if (ok) {
1133 
1134  // Add a space point.
1135 
1136  ++n2;
1137 
1138  // make a dummy vector of recob::SpacePoints
1139  // as we are filtering or merging and don't want to
1140  // add the created SpacePoint to the final collection just yet
1141  // This dummy vector will hold just one recob::SpacePoint,
1142  // which will go into the multimap and then the vector
1143  // will go out of scope.
1144 
1145  std::vector<recob::SpacePoint> sptv;
1146  fillSpacePoint(detProp, hitvec, sptv, sptmap.size());
1147  sptkey_type key = &*phit2;
1148  sptmap.insert(std::pair<sptkey_type, recob::SpacePoint>(key, sptv.back()));
1149  sptkeys.insert(key);
1150  }
1151  }
1152  }
1153  }
1154  }
1155  } // end if fMinViews <= 2
1156 
1157  // If three-view space points are allowed, make a triple loop
1158  // over hits and produce space points for compatible triplets.
1159 
1160  if (nplane >= 3 && fMinViews <= 3) {
1161 
1162  // Loop over triplets of hits.
1163 
1165  hitvec.reserve(3);
1166 
1167  unsigned int plane1 = index[0];
1168  unsigned int plane2 = index[1];
1169  unsigned int plane3 = index[2];
1170 
1171  // Get angle, pitch, and offset of plane1 wires.
1172 
1173  const geo::WireGeo& wgeo1 = geom->Cryostat(cstat).TPC(tpc).Plane(plane1).Wire(0);
1174  double hl1 = wgeo1.HalfL();
1175  double xyz11[3];
1176  double xyz12[3];
1177  wgeo1.GetCenter(xyz11, -hl1);
1178  wgeo1.GetCenter(xyz12, hl1);
1179  double s1 = (xyz12[1] - xyz11[1]) / (2. * hl1);
1180  double c1 = (xyz12[2] - xyz11[2]) / (2. * hl1);
1181  double dist1 = -xyz11[1] * c1 + xyz11[2] * s1;
1182  double pitch1 = geom->WirePitch(plane1, tpc, cstat);
1183  const double TicksOffset1 = detProp.GetXTicksOffset(plane1, tpc, cstat);
1184 
1185  // Get angle, pitch, and offset of plane2 wires.
1186 
1187  const geo::WireGeo& wgeo2 = geom->Cryostat(cstat).TPC(tpc).Plane(plane2).Wire(0);
1188  double hl2 = wgeo2.HalfL();
1189  double xyz21[3];
1190  double xyz22[3];
1191  wgeo2.GetCenter(xyz21, -hl2);
1192  wgeo2.GetCenter(xyz22, hl2);
1193  double s2 = (xyz22[1] - xyz21[1]) / (2. * hl2);
1194  double c2 = (xyz22[2] - xyz21[2]) / (2. * hl2);
1195  double dist2 = -xyz21[1] * c2 + xyz21[2] * s2;
1196  double pitch2 = geom->WirePitch(plane2, tpc, cstat);
1197  const double TicksOffset2 = detProp.GetXTicksOffset(plane2, tpc, cstat);
1198 
1199  // Get angle, pitch, and offset of plane3 wires.
1200 
1201  const geo::WireGeo& wgeo3 = geom->Cryostat(cstat).TPC(tpc).Plane(plane3).Wire(0);
1202  double hl3 = wgeo3.HalfL();
1203  double xyz31[3];
1204  double xyz32[3];
1205  wgeo3.GetCenter(xyz31, -hl3);
1206  wgeo3.GetCenter(xyz32, hl3);
1207  double s3 = (xyz32[1] - xyz31[1]) / (2. * hl3);
1208  double c3 = (xyz32[2] - xyz31[2]) / (2. * hl3);
1209  double dist3 = -xyz31[1] * c3 + xyz31[2] * s3;
1210  double pitch3 = geom->WirePitch(plane3, tpc, cstat);
1211  const double TicksOffset3 = detProp.GetXTicksOffset(plane3, tpc, cstat);
1212 
1213  // Get sine of angle differences.
1214 
1215  double s12 = s1 * c2 - s2 * c1; // sin(theta1 - theta2).
1216  double s23 = s2 * c3 - s3 * c2; // sin(theta2 - theta3).
1217  double s31 = s3 * c1 - s1 * c3; // sin(theta3 - theta1).
1218 
1219  // Loop over hits in plane1.
1220 
1221  std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator
1222  ihit1 = hitmap[cstat][tpc][plane1].begin(),
1223  ihit1end = hitmap[cstat][tpc][plane1].end();
1224  for (; ihit1 != ihit1end; ++ihit1) {
1225 
1226  unsigned int wire1 = ihit1->first;
1227  const art::Ptr<recob::Hit>& phit1 = ihit1->second;
1228  geo::WireID phit1WireID = phit1->WireID();
1229  const geo::WireGeo& wgeo = geom->WireIDToWireGeo(phit1WireID);
1230 
1231  // Get endpoint coordinates of this wire from plane1.
1232  // (kept as assertions for performance reasons)
1233  assert(phit1WireID.Cryostat == cstat);
1234  assert(phit1WireID.TPC == tpc);
1235  assert(phit1WireID.Plane == plane1);
1236  assert(phit1WireID.Wire == wire1);
1237  double hl1 = wgeo.HalfL();
1238  double xyz1[3];
1239  double xyz2[3];
1240  wgeo.GetCenter(xyz1, -hl1);
1241  wgeo.GetCenter(xyz2, hl1);
1242 
1243  // Get corrected time and oblique coordinate of first hit.
1244 
1245  double t1 = phit1->PeakTime() - TicksOffset1;
1246  double u1 = wire1 * pitch1 + dist1;
1247 
1248  // Find the plane2 wire numbers corresponding to the endpoints.
1249 
1250  double wire21 = (-xyz1[1] * c2 + xyz1[2] * s2 - dist2) / pitch2;
1251  double wire22 = (-xyz2[1] * c2 + xyz2[2] * s2 - dist2) / pitch2;
1252 
1253  int wmin = std::max(0., std::min(wire21, wire22));
1254  int wmax = std::max(0., std::max(wire21, wire22) + 1.);
1255 
1256  std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator
1257  ihit2 = hitmap[cstat][tpc][plane2].lower_bound(wmin),
1258  ihit2end = hitmap[cstat][tpc][plane2].upper_bound(wmax);
1259 
1260  for (; ihit2 != ihit2end; ++ihit2) {
1261 
1262  int wire2 = ihit2->first;
1263  const art::Ptr<recob::Hit>& phit2 = ihit2->second;
1264 
1265  // Get corrected time of second hit.
1266 
1267  double t2 = phit2->PeakTime() - TicksOffset2;
1268 
1269  // Check maximum time difference with first hit.
1270 
1271  bool dt12ok = std::abs(t1 - t2) <= fMaxDT;
1272  if (dt12ok) {
1273 
1274  // Test first two hits for compatibility before looping
1275  // over third hit.
1276 
1277  hitvec.clear();
1278  hitvec.push_back(phit1);
1279  hitvec.push_back(phit2);
1280  bool h12ok = compatible(detProp, hitvec, useMC);
1281  if (h12ok) {
1282 
1283  // Get oblique coordinate of second hit.
1284 
1285  double u2 = wire2 * pitch2 + dist2;
1286 
1287  // Predict plane3 oblique coordinate and wire number.
1288 
1289  double u3pred = (-u1 * s23 - u2 * s31) / s12;
1290  double w3pred = (u3pred - dist3) / pitch3;
1291  double w3delta = std::abs(fMaxS / (s12 * pitch3));
1292  int w3min = std::max(0., std::ceil(w3pred - w3delta));
1293  int w3max = std::max(0., std::floor(w3pred + w3delta));
1294 
1295  std::map<unsigned int, art::Ptr<recob::Hit>>::const_iterator
1296  ihit3 = hitmap[cstat][tpc][plane3].lower_bound(w3min),
1297  ihit3end = hitmap[cstat][tpc][plane3].upper_bound(w3max);
1298 
1299  for (; ihit3 != ihit3end; ++ihit3) {
1300 
1301  int wire3 = ihit3->first;
1302  const art::Ptr<recob::Hit>& phit3 = ihit3->second;
1303 
1304  // Get corrected time of third hit.
1305 
1306  double t3 = phit3->PeakTime() - TicksOffset3;
1307 
1308  // Check time difference of third hit compared to first two hits.
1309 
1310  bool dt123ok = std::abs(t1 - t3) <= fMaxDT && std::abs(t2 - t3) <= fMaxDT;
1311  if (dt123ok) {
1312 
1313  // Get oblique coordinate of third hit and check spatial separation.
1314 
1315  double u3 = wire3 * pitch3 + dist3;
1316  double S = s23 * u1 + s31 * u2 + s12 * u3;
1317  bool sok = std::abs(S) <= fMaxS;
1318  if (sok) {
1319 
1320  // Test triplet for compatibility.
1321 
1322  hitvec.clear();
1323  hitvec.push_back(phit1);
1324  hitvec.push_back(phit2);
1325  hitvec.push_back(phit3);
1326  bool h123ok = compatible(detProp, hitvec, useMC);
1327  if (h123ok) {
1328 
1329  // Add a space point.
1330 
1331  ++n3;
1332 
1333  // make a dummy vector of recob::SpacePoints
1334  // as we are filtering or merging and don't want to
1335  // add the created SpacePoint to the final collection just yet
1336  // This dummy vector will hold just one recob::SpacePoint,
1337  // which will go into the multimap and then the vector
1338  // will go out of scope.
1339 
1340  std::vector<recob::SpacePoint> sptv;
1341  fillSpacePoint(detProp, hitvec, sptv, sptmap.size() - 1);
1342  sptkey_type key = &*phit3;
1343  sptmap.insert(
1344  std::pair<sptkey_type, recob::SpacePoint>(key, sptv.back()));
1345  sptkeys.insert(key);
1346  }
1347  }
1348  }
1349  }
1350  }
1351  }
1352  }
1353  }
1354  } // end if fMinViews <= 3
1355 
1356  // Do Filtering.
1357 
1358  if (fFilter) {
1359 
1360  // Transfer (some) space points from sptmap to spts.
1361 
1362  spts.reserve(spts.size() + sptkeys.size());
1363 
1364  // Loop over keys of space point map.
1365  // Space points that have the same key are candidates for filtering.
1366 
1367  for (std::set<sptkey_type>::const_iterator i = sptkeys.begin(); i != sptkeys.end(); ++i) {
1368  sptkey_type key = *i;
1369 
1370  // Loop over space points corresponding to the current key.
1371  // Choose the single best space point from among this group.
1372 
1373  double best_chisq = 0.;
1374  const recob::SpacePoint* best_spt = 0;
1375 
1377  sptmap.lower_bound(key);
1378  j != sptmap.upper_bound(key);
1379  ++j) {
1380  const recob::SpacePoint& spt = j->second;
1381  if (best_spt == 0 || spt.Chisq() < best_chisq) {
1382  best_spt = &spt;
1383  best_chisq = spt.Chisq();
1384  }
1385  }
1386 
1387  // Transfer best filtered space point to result vector.
1388 
1389  if (!best_spt)
1390  throw cet::exception("SpacePointAlg") << "makeSpacePoints(): no best point\n";
1391  spts.push_back(*best_spt);
1392  if (fMinViews <= 2)
1393  ++n2filt;
1394  else
1395  ++n3filt;
1396  }
1397  } // end if filtering
1398 
1399  // Do merging.
1400 
1401  else if (fMerge) {
1402 
1403  // Transfer merged space points from sptmap to spts.
1404 
1405  spts.reserve(spts.size() + sptkeys.size());
1406 
1407  // Loop over keys of space point map.
1408  // Space points that have the same key are candidates for merging.
1409 
1410  for (std::set<sptkey_type>::const_iterator i = sptkeys.begin(); i != sptkeys.end(); ++i) {
1411  sptkey_type key = *i;
1412 
1413  // Loop over space points corresponding to the current key.
1414  // Make a collection of hits that is the union of the hits
1415  // from each candidate space point.
1416 
1418  sptmap.lower_bound(key),
1419  jSPTend =
1420  sptmap.upper_bound(key);
1421 
1422  art::PtrVector<recob::Hit> merged_hits;
1423  for (; jSPT != jSPTend; ++jSPT) {
1424  const recob::SpacePoint& spt = jSPT->second;
1425 
1426  // Loop over hits from this space points.
1427  // Add each hit to the collection of all hits.
1428 
1429  const art::PtrVector<recob::Hit>& spt_hits = getAssociatedHits(spt);
1430  merged_hits.reserve(merged_hits.size() +
1431  spt_hits.size()); // better than nothing, but not ideal
1433  k != spt_hits.end();
1434  ++k) {
1435  const art::Ptr<recob::Hit>& hit = *k;
1436  merged_hits.push_back(hit);
1437  }
1438  }
1439 
1440  // Remove duplicates.
1441 
1442  std::sort(merged_hits.begin(), merged_hits.end());
1444  std::unique(merged_hits.begin(), merged_hits.end());
1445  merged_hits.erase(it, merged_hits.end());
1446 
1447  // Construct a complex space points using merged hits.
1448 
1449  fillComplexSpacePoint(detProp, merged_hits, spts, sptmap.size() + spts.size() - 1);
1450 
1451  if (fMinViews <= 2)
1452  ++n2filt;
1453  else
1454  ++n3filt;
1455  }
1456  } // end if merging
1457 
1458  // No filter, no merge.
1459 
1460  else {
1461 
1462  // Transfer all space points from sptmap to spts.
1463 
1464  spts.reserve(spts.size() + sptkeys.size());
1465 
1466  // Loop over space points.
1467 
1469  j != sptmap.end();
1470  ++j) {
1471  const recob::SpacePoint& spt = j->second;
1472  spts.push_back(spt);
1473  }
1474 
1475  // Update statistics.
1476 
1477  n2filt = n2;
1478  n3filt = n3;
1479  }
1480  } // end loop over tpcs
1481  } // end loop over cryostats
1482 
1483  if (mf::isDebugEnabled()) {
1484  debug << "\n2-hit space points = " << n2 << "\n"
1485  << "3-hit space points = " << n3 << "\n"
1486  << "2-hit filtered/merged space points = " << n2filt << "\n"
1487  << "3-hit filtered/merged space points = " << n3filt;
1488  } // if debug
1489  }
1490 
1491  //----------------------------------------------------------------------
1492  // Get hits associated with a particular space point, based on most recent
1493  // call of any make*SpacePoints method.
1496  {
1497  // It is an error if no hits are associated with this space point (throw exception).
1498 
1499  std::map<int, art::PtrVector<recob::Hit>>::const_iterator it = fSptHitMap.find(spt.ID());
1500  if (it == fSptHitMap.end()) {
1501  mf::LogWarning("SpacePointAlg")
1502  << "Looking for ID " << spt.ID() << " from " << fSptHitMap.size() << std::endl;
1503  throw cet::exception("SpacePointAlg") << "No Hits associated with space point.\n";
1504  }
1505  return (*it).second;
1506  }
1507 
1508 } // end namespace trkf
code to link reconstructed objects back to the MC truth information
intermediate_table::iterator iterator
Geometry description of a TPC wireThe wire is a single straight segment on a wire plane...
Definition: WireGeo.h:65
void reserve(size_type n)
Definition: PtrVector.h:337
Basic Kalman filter track class, plus one measurement on same surface.
const std::shared_ptr< const KHitBase > & getHit() const
Measurement.
Definition: KHitTrack.h:53
std::vector< const recob::Hit * > pchit
Pointer to nearest neighbor hit (indexed by plane).
typename data_t::iterator iterator
Definition: PtrVector.h:54
std::map< const recob::Hit *, HitMCInfo > fHitMCMap
double fTickOffsetU
Tick offset for plane U.
static QCString result
Encapsulate the construction of a single cyostat.
double fMaxDT
Maximum time difference between planes.
double GetXTicksCoefficient(int t, int c) const
WireGeo const & Wire(unsigned int iwire) const
Definition: PlaneGeo.cxx:506
bool fFilter
Filter flag.
enum geo::_plane_proj View_t
Enumerate the possible plane projections.
std::string string
Definition: nybbler.cc:12
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
Planes which measure V.
Definition: geo_types.h:130
double GetXTicksOffset(int p, int t, int c) const
geo::WireID WireID() const
Definition: Hit.h:233
enum geo::_plane_orient Orient_t
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:165
iterator begin()
Definition: PtrVector.h:217
Kalman filter wire-time measurement on a SurfWireX surface.
void update(detinfo::DetectorPropertiesData const &detProp) const
The data type to uniquely identify a Plane.
Definition: geo_types.h:472
std::vector< double > dist2
Distance to nearest neighbor hit (indexed by plane).
Geometry information for a single TPC.
Definition: TPCGeo.h:38
bool fEnableW
Enable flag (W).
iterator erase(iterator position)
Definition: PtrVector.h:504
std::vector< int > trackIDs
Parent trackIDs.
const art::Ptr< recob::Hit > & getHit() const
Get original hit.
Definition: KHitWireX.h:48
Definition: 044_section.h:5
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:212
Planes which measure Z direction.
Definition: geo_types.h:132
geo::View_t View() const
View for the plane of the hit.
Definition: Hit.h:232
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:580
intermediate_table::const_iterator const_iterator
SigType_t SignalType(geo::PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
double ThetaZ() const
Returns angle of wire with respect to z axis in the Y-Z plane in radians.
Definition: WireGeo.h:250
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
const art::PtrVector< recob::Hit > & getAssociatedHits(const recob::SpacePoint &spt) const
weight
Definition: test.py:257
art framework interface to geometry description
void makeMCTruthSpacePoints(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const art::PtrVector< recob::Hit > &hits, std::vector< recob::SpacePoint > &spts) const
double separation(const art::PtrVector< recob::Hit > &hits) const
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
T abs(T value)
Planes which measure U.
Definition: geo_types.h:129
View_t View() const
Which coordinate does this plane measure.
Definition: PlaneGeo.h:184
Planes that are in the horizontal plane.
Definition: geo_types.h:140
SpacePointAlg(const fhicl::ParameterSet &pset)
typename data_t::const_iterator const_iterator
Definition: PtrVector.h:55
bool fEnableU
Enable flag (U).
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:435
Signal from induction planes.
Definition: geo_types.h:145
std::map< int, art::PtrVector< recob::Hit > > fSptHitMap
def key(type, name=None)
Definition: graph.py:13
Planes that are in the vertical plane (e.g. ArgoNeuT).
Definition: geo_types.h:141
std::vector< double > xyz
Location of ionization (all tracks).
enum geo::_plane_sigtype SigType_t
void makeSpacePoints(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, const art::PtrVector< recob::Hit > &hits, std::vector< recob::SpacePoint > &spts) const
T get(std::string const &key) const
Definition: ParameterSet.h:271
Geometry information for a single wire plane.The plane is represented in the geometry by a solid whic...
Definition: PlaneGeo.h:82
iterator end()
Definition: PtrVector.h:231
void fillComplexSpacePoint(detinfo::DetectorPropertiesData const &detProp, const art::PtrVector< recob::Hit > &hits, std::vector< recob::SpacePoint > &sptv, int sptid) const
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:181
Double32_t Chisq() const
Definition: SpacePoint.h:78
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
bool isDebugEnabled()
double WirePitch(unsigned plane=0) const
Definition: TPCGeo.cxx:396
double fMaxS
Maximum space separation between wires.
static int max(int a, int b)
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:493
double correctedTime(detinfo::DetectorPropertiesData const &detProp, const recob::Hit &hit) const
bool fPreferColl
Sort by collection wire.
double Plane0Pitch(unsigned int p) const
Definition: TPCGeo.cxx:324
size_type size() const
Definition: PtrVector.h:302
void fillSpacePoints(detinfo::DetectorPropertiesData const &detProp, std::vector< recob::SpacePoint > &spts, std::multimap< double, KHitTrack > const &trackMap) const
Fill a collection of space points.
Detector simulation of raw signals on wires.
Orient_t Orientation() const
What is the orientation of the plane.
Definition: PlaneGeo.h:187
Encapsulate the geometry of a wire.
double HalfL() const
Returns half the length of the wire [cm].
Definition: WireGeo.h:128
reference front()
Definition: PtrVector.h:373
float PeakTime() const
Time of the signal peak, in tick units.
Definition: Hit.h:218
Declaration of signal hit object.
double fTickOffsetV
Tick offset for plane V.
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
Encapsulate the construction of a single detector plane.
Contains all timing reference information for the detector.
constexpr double dist(const TReal *x, const TReal *y, const unsigned int dimension)
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Definition: CryostatGeo.cxx:93
std::vector< sim::IDE > HitToAvgSimIDEs(detinfo::DetectorClocksData const &clockData, recob::Hit const &hit) const
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
double fTickOffsetW
Tick offset for plane W.
std::vector< double > SimIDEsToXYZ(std::vector< sim::IDE > const &ides) const
bool compatible(detinfo::DetectorPropertiesData const &detProp, const art::PtrVector< recob::Hit > &hits, bool useMC=false) const
int fMinViews
Mininum number of views per space point.
list x
Definition: train.py:276
int numHitMap() const
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:72
float SigmaPeakTime() const
Uncertainty for the signal peak, in tick units.
Definition: Hit.h:219
ID_t ID() const
Definition: SpacePoint.h:75
Planes which measure W (third view for Bo, MicroBooNE, etc).
Definition: geo_types.h:131
PlaneGeo const & Plane(geo::View_t view) const
Return the plane in the tpc with View_t view.
Definition: TPCGeo.cxx:263
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:48
void GetCenter(double *xyz, double localz=0.0) const
Fills the world coordinate of a point on the wire.
Definition: WireGeo.cxx:73
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:406
Algorithm for generating space points from hits.
bool fMerge
Merge flag.
void fillSpacePoint(detinfo::DetectorPropertiesData const &detProp, const art::PtrVector< recob::Hit > &hits, std::vector< recob::SpacePoint > &sptv, int sptid) const
void clear()
Definition: PtrVector.h:533
const double * PlaneLocation(unsigned int p) const
Definition: TPCGeo.cxx:382
static QCString * s
Definition: config.cpp:1042
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
QTextStream & endl(QTextStream &s)
Encapsulate the construction of a single detector plane.
Signal from collection planes.
Definition: geo_types.h:146
bool fEnableV
Enable flag (V).
WireGeo const & WireIDToWireGeo(geo::WireID const &wireid) const