VertexFitAlg.cxx
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////
2 ///
3 /// VertexFitAlg class
4 ///
5 /// Bruce Baller, baller@fnal.gov
6 ///
7 /// Algorithm for fitting a 3D vertex given a set of track hits
8 ///
9 ////////////////////////////////////////////////////////////////////////
10 
11 #include <array>
12 #include <cmath>
13 #include "TMinuit.h"
14 #include "TVector3.h"
15 
21 
22 namespace trkf{
23 
25 
26  /////////////////////////////////////////
27  void VertexFitAlg::fcnVtxPos(Int_t &, Double_t *, Double_t &fval, double *par, Int_t flag)
28  {
29  // Minuit function for fitting the vertex position and vertex track directions
30 
31  fval = 0;
32  double vWire = 0, DirX, DirY, DirZ, DirU, dX, dU, arg;
33  unsigned short ipl, lastpl, indx;
34 
35  for(unsigned short itk = 0; itk < fVtxFitMinStr.HitX.size(); ++itk) {
36  lastpl = 4;
37  // index of the track Y direction vector. Z direction is the next one
38  indx = 3 + 2 * itk;
39  for(unsigned short iht = 0; iht < fVtxFitMinStr.HitX[itk].size(); ++iht) {
40  ipl = fVtxFitMinStr.Plane[itk][iht];
41  if(ipl != lastpl) {
42  // get the vertex position in this plane
43  // vertex wire number in the Detector coordinate system (equivalent to WireCoordinate)
44  //vtx wir = vtx Y * OrthY + vtx Z * OrthZ - wire offset
45  vWire = par[1] * fVtxFitMinStr.OrthY[ipl] + par[2] * fVtxFitMinStr.OrthZ[ipl] - fVtxFitMinStr.FirstWire[ipl];
46 // if(flag == 1) mf::LogVerbatim("VF")<<"fcn vtx "<<par[0]<<" "<<par[1]<<" "<<par[2]<<" vWire "<<vWire<<" OrthY "<<fVtxFitMinStr.OrthY[ipl]<<" OrthZ "<<fVtxFitMinStr.OrthZ[ipl];
47  lastpl = ipl;
48  } // ipl != lastpl
49  DirY = par[indx];
50  DirZ = par[indx + 1];
51  // rotate the track direction DirY, DirZ into the wire coordinate of this plane. The OrthVectors in ChannelMapStandardAlg
52  // are divided by the wire pitch so we need to correct for that here
53  DirU = fVtxFitMinStr.WirePitch * (DirY * fVtxFitMinStr.OrthY[ipl] + DirZ * fVtxFitMinStr.OrthZ[ipl]);
54  // distance (cm) between the wire and the vertex in the wire coordinate system (U)
55  dU = fVtxFitMinStr.WirePitch * (fVtxFitMinStr.Wire[itk][iht] - vWire);
56  if(std::abs(DirU) < 1E-3 || std::abs(dU) < 1E-3) {
57  // vertex is on the wire
58  dX = par[0] - fVtxFitMinStr.HitX[itk][iht];
59  } else {
60  // project from vertex to the wire. We need to find dX/dU so first find DirX
61  DirX = 1 - DirY * DirY - DirZ * DirZ;
62  // DirX should be > 0 but the bounds on DirY and DirZ are +/- 1 so it is possible for a non-physical result.
63  if(DirX < 0) DirX = 0;
64  DirX = sqrt(DirX);
65  // Get the DirX sign from the relative X position of the hit and the vertex
66  if(fVtxFitMinStr.HitX[itk][iht] < par[0]) DirX = -DirX;
67  dX = par[0] + (dU * DirX / DirU) - fVtxFitMinStr.HitX[itk][iht];
68  }
69  arg = dX / fVtxFitMinStr.HitXErr[itk][iht];
70 // if(flag == 1) mf::LogVerbatim("VF")<<"fcn itk "<<itk<<" iht "<<iht<<" ipl "<<ipl<<" DirX "<<DirX<<" DirY "<<DirY<<" DirZ "<<DirZ
71 // <<" DirU "<<DirU<<" W "<<fVtxFitMinStr.Wire[itk][iht]<<" X "<<fVtxFitMinStr.HitX[itk][iht]<<" dU "<<dU<<" dX "<<dX<<" arg "<<arg;
72  fval += arg * arg;
73  } // iht
74  } //itk
75 
76  fval /= fVtxFitMinStr.DoF;
77 
78  // save the final chisq/dof in the struct on the last call
79  if(flag == 3) fVtxFitMinStr.ChiDoF = fval;
80 
81  } // fcnVtxPos
82 
83  /////////////////////////////////////////
84 
85  void VertexFitAlg::VertexFit(std::vector<std::vector<geo::WireID>> const& hitWID,
86  std::vector<std::vector<double>> const& hitX,
87  std::vector<std::vector<double>> const& hitXErr,
88  TVector3& VtxPos, TVector3& VtxPosErr,
89  std::vector<TVector3>& TrkDir, std::vector<TVector3>& TrkDirErr,
90  float& ChiDOF) const
91  {
92  // The passed set of hit WireIDs, X positions and X errors associated with a Track
93  // are fitted to a vertex position VtxPos. The fitted track direction vectors trkDir, TrkDirErr
94  // and ChiDOF are returned to the calling routine
95 
96  // assume failure
97  ChiDOF = 9999;
98 
99  // need at least hits for two tracks
100  if(hitX.size() < 2) return;
101  if(hitX.size() != hitWID.size()) return;
102  if(hitX.size() != hitXErr.size()) return;
103  if(hitX.size() != TrkDir.size()) return;
104 
105  // number of variables = 3 for the vertex position + 2 * number of track directions
106  const unsigned int ntrks = hitX.size();
107  const unsigned int npars = 3 + 2 * ntrks;
108  unsigned int npts = 0, itk;
109  for(itk = 0; itk < ntrks; ++itk) npts += hitX[itk].size();
110 
111  if(npts < ntrks) return;
112 
113  // Get the cryostat and tpc from the first hit
114  unsigned int cstat, tpc, nplanes, ipl, iht;
115  cstat = hitWID[0][0].Cryostat;
116  tpc = hitWID[0][0].TPC;
117  nplanes = geom->Cryostat(cstat).TPC(tpc).Nplanes();
118 
119  fVtxFitMinStr.Cstat = cstat;
120  fVtxFitMinStr.TPC = tpc;
121  fVtxFitMinStr.NPlanes = nplanes;
122  fVtxFitMinStr.WirePitch = geom->WirePitch(hitWID[0][0].Plane, tpc, cstat);
123 
124  // Put geometry conversion factors into the struct
125  for(ipl = 0; ipl < nplanes; ++ipl) {
126  fVtxFitMinStr.FirstWire[ipl] = -geom->WireCoordinate(0, 0, ipl, tpc, cstat);
127  fVtxFitMinStr.OrthY[ipl] = geom->WireCoordinate(1, 0, ipl, tpc, cstat) + fVtxFitMinStr.FirstWire[ipl];
128  fVtxFitMinStr.OrthZ[ipl] = geom->WireCoordinate(0, 1, ipl, tpc, cstat) + fVtxFitMinStr.FirstWire[ipl];
129  }
130  // and the vertex starting position
131  fVtxFitMinStr.VtxPos = VtxPos;
132 
133  // and the track direction and hits
134  fVtxFitMinStr.HitX = hitX;
135  fVtxFitMinStr.HitXErr = hitXErr;
136  fVtxFitMinStr.Plane.resize(ntrks);
137  fVtxFitMinStr.Wire.resize(ntrks);
138  for(itk = 0; itk < ntrks; ++itk) {
139  fVtxFitMinStr.Plane[itk].resize(hitX[itk].size());
140  fVtxFitMinStr.Wire[itk].resize(hitX[itk].size());
141  for(iht = 0; iht < hitWID[itk].size(); ++iht) {
142  fVtxFitMinStr.Plane[itk][iht] = hitWID[itk][iht].Plane;
143  fVtxFitMinStr.Wire[itk][iht] = hitWID[itk][iht].Wire;
144  }
145  } // itk
146  fVtxFitMinStr.Dir = TrkDir;
147 
148  fVtxFitMinStr.DoF = npts - npars;
149 
150  // define the starting parameters
151  std::vector<double> par(npars);
152  std::vector<double> stp(npars);
153  std::vector<double> parerr(npars);
154 
155  TMinuit *gMin = new TMinuit(npars);
156  gMin->SetFCN(VertexFitAlg::fcnVtxPos);
157  int errFlag = 0;
158  double arglist[10];
159 
160  // print level (-1 = none, 1 = yes)
161  arglist[0] = -1;
162  //
163  // ***** remember to delete gMin before returning on an error
164  //
165  gMin->mnexcm("SET PRINT", arglist, 1, errFlag);
166 
167  // the vertex position
168  unsigned short ipar;
169  for(ipar = 0; ipar < 3; ++ipar) {
170  par[ipar] = fVtxFitMinStr.VtxPos[ipar]; // in cm
171  stp[ipar] = 0.1; // 1 mm initial step
172  gMin->mnparm(ipar,"", par[ipar], stp[ipar], -1E6, 1E6, errFlag);
173  }
174  // use Y, Z track directions. There is no constraint that the direction vector is unit-normalized
175  // since we are only passing two of the components. Minuit could violate this requirement when
176  // fitting. fcnVtxPos prevents non-physical values and Minuit may throw error messages as a result.
177  for(itk = 0; itk < ntrks; ++itk) {
178  ipar = 3 + 2 * itk;
179  par[ipar] = fVtxFitMinStr.Dir[itk](1);
180  stp[ipar] = 0.03;
181  gMin->mnparm(ipar,"", par[ipar], stp[ipar], -1.05, 1.05, errFlag);
182  ++ipar;
183  par[ipar] = fVtxFitMinStr.Dir[itk](2);
184  stp[ipar] = 0.03;
185  gMin->mnparm(ipar,"", par[ipar], stp[ipar], -1.05, 1.05, errFlag);
186  } // itk
187 
188 /*
189  // Single call to fcnVtxPos for debugging it
190  std::cout<<"Starting: par ";
191  for(unsigned short ip = 0; ip < par.size(); ++ip) std::cout<<" "<<std::fixed<<std::setprecision(2)<<par[ip];
192  std::cout<<"\n";
193  // call fcn with starting parameters
194  arglist[0] = 1;
195  gMin->mnexcm("CALL", arglist, 1, errFlag);
196 */
197  // set strategy 0 for faster Minuit fitting
198  arglist[0] = 0.;
199  gMin->mnexcm("SET STRATEGY", arglist, 1, errFlag);
200 
201  // execute Minuit command: Migrad, max calls, tolerance
202  arglist[0] = 500; // max calls
203  arglist[1] = 1.; // tolerance on fval in fcn
204  gMin->mnexcm("MIGRAD", arglist, 2, errFlag);
205 
206  // call fcn to get final fit values
207  arglist[0] = 3;
208  gMin->mnexcm("CALL", arglist, 1, errFlag);
209  ChiDOF = fVtxFitMinStr.ChiDoF;
210 
211  // get the parameters
212  for(unsigned short ip = 0; ip < par.size(); ++ip) {
213  gMin->GetParameter(ip, par[ip], parerr[ip]);
214  }
215 
216  // return the vertex position and errors
217  for(ipar = 0; ipar < 3; ++ipar) {
218  VtxPos[ipar] = par[ipar];
219  VtxPosErr[ipar] = parerr[ipar];
220  }
221  // return the track directions and the direction errors if applicable
222  bool returnTrkDirErrs = (TrkDirErr.size() == TrkDir.size());
223  for(itk = 0; itk < ntrks; ++itk) {
224  ipar = 3 + 2 * itk;
225  double arg = 1 - par[ipar] * par[ipar] - par[ipar + 1] * par[ipar + 1];
226  if(arg < 0) arg = 0;
227  TrkDir[itk](0) = sqrt(arg);
228  TrkDir[itk](1) = par[ipar];
229  TrkDir[itk](2) = par[ipar + 1];
230  if(returnTrkDirErrs) {
231  // TODO This needs to be checked if there is a need for trajectory errors
232  double errY = parerr[ipar] / par[ipar];
233  double errZ = parerr[ipar + 1] / par[ipar + 1];
234  TrkDirErr[itk](0) = sqrt(arg * (errY * errY + errZ * errZ));
235  TrkDirErr[itk](1) = parerr[ipar];
236  TrkDirErr[itk](2) = parerr[ipar + 1];
237  }
238  } // itk
239 
240  delete gMin;
241 
242  } // VertexFit()
243 
244 } // namespace trkf
geo::Length_t WireCoordinate(double YPos, double ZPos, geo::PlaneID const &planeid) const
Returns the index of the nearest wire to the specified position.
std::vector< std::vector< unsigned short > > Wire
Encapsulate the construction of a single cyostat.
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:165
std::vector< std::vector< double > > HitX
struct vector vector
std::vector< std::vector< unsigned short > > Plane
std::array< double, 3 > OrthY
geo::Length_t WirePitch(geo::PlaneID const &planeid) const
Returns the distance between two consecutive wires.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
T abs(T value)
std::vector< TVector3 > Dir
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
std::array< double, 3 > FirstWire
Definition of data types for geometry description.
Class defining a plane for tracking. It provides various functionalities to convert track parameters ...
Definition: TrackingPlane.h:37
void VertexFit(std::vector< std::vector< geo::WireID >> const &hitWID, std::vector< std::vector< double >> const &hitX, std::vector< std::vector< double >> const &hitXErr, TVector3 &VtxPos, TVector3 &VtxPosErr, std::vector< TVector3 > &TrkDir, std::vector< TVector3 > &TrkDirErr, float &ChiDOF) const
static void fcnVtxPos(Int_t &, Double_t *, Double_t &fval, double *par, Int_t flag)
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Definition: CryostatGeo.cxx:93
art::ServiceHandle< geo::Geometry const > geom
Definition: VertexFitAlg.h:45
std::vector< std::vector< double > > HitXErr
std::array< double, 3 > OrthZ
static VertexFitMinuitStruct fVtxFitMinStr
Definition: VertexFitAlg.h:39
Encapsulate the construction of a single detector plane.