CMS 3D CMS Logo

Fourvec_Constrainer.cc
Go to the documentation of this file.
1 //
2 //
3 // File: src/Fourvec_Constrainer.cc
4 // Purpose: Do a kinematic fit for a set of 4-vectors, given a set
5 // of mass constraints.
6 // Created: Jul, 2000, sss, based on run 1 mass analysis code.
7 //
8 // CMSSW File : src/Fourvec_Constrainer.cc
9 // Original Author : Scott Stuart Snyder <snyder@bnl.gov> for D0
10 // Imported to CMSSW by Haryo Sumowidagdo <Suharyo.Sumowidagdo@cern.ch>
11 //
12 
45 #include <cmath>
46 #include <iostream>
47 
48 using std::cos;
49 using std::exp;
50 using std::ostream;
51 using std::sin;
52 using std::sqrt;
53 using std::vector;
54 
55 namespace hitfit {
56 
57  //*************************************************************************
58  // Argument handling.
59  //
60 
62  //
63  // Purpose: Constructor.
64  //
65  // Inputs:
66  // defs - The Defaults instance from which to initialize.
67  //
68  : _use_e(defs.get_bool("use_e")),
69  _e_com(defs.get_float("e_com")),
70  _ignore_met(defs.get_bool("ignore_met")),
71  _chisq_constrainer_args(defs) {}
72 
74  //
75  // Purpose: Return the use_e parameter.
76  // See the header for documentation.
77  //
78  {
79  return _use_e;
80  }
81 
83  //
84  // Purpose: Return the e_com parameter.
85  // See the header for documentation.
86  //
87  {
88  return _e_com;
89  }
90 
92  //
93  // Purpose: Return the ignore_met parameter.
94  // See the header for documentation.
95  //
96  {
97  return _ignore_met;
98  }
99 
101  //
102  // Purpose: Return the contained subobject parameters.
103  //
104  {
106  }
107 
108  //*************************************************************************
109  // Variable layout.
110  //
111  // We need to map the quantities we fit onto the vectors of well- and
112  // poorly-measured quantities.
113  //
114 
115  //
116  // The well-measured variables consist of three variables for each
117  // object. If we are using transverse momentum constraints,
118  // these fill be followed by the two cartesian components of kt.
119  //
120  // Each object is represented by three variables: the momentum (or 1/p
121  // if the muon flag was set), and the two spherical angles, phi and eta.
122  // Here is how they're ordered.
123  //
127  typedef enum { p_offs = 0, phi_offs = 1, eta_offs = 2 } Offsets;
128 
133  typedef enum { x_offs = 0, y_offs = 1 } Kt_Offsets;
134 
135  //
136  // If there is a neutrino, then it is at index 1 of the poorly-measured
137  // set (otherwise, that set is empty).
138  //
143  typedef enum { nu_z = 1 } Unmeasured_Variables;
144 
145  namespace {
146 
153  int obj_index(int i)
154  //
155  // Purpose: Return the starting variable index for object I.
156  //
157  // Inputs:
158  // i - The object index.
159  //
160  // Returns:
161  // The index in the well-measured set of the first variable
162  // for object I.
163  //
164  {
165  return i * 3 + 1;
166  }
167 
168  } // unnamed namespace
169 
170  //*************************************************************************
171  // Object management.
172  //
173 
175  //
176  // Purpose: Constructor.
177  //
178  // Inputs:
179  // args - The parameter settings for this instance.
180  //
181  : _args(args) {}
182 
184  //
185  // Purpose: Specify an additional constraint S for the problem.
186  // The format for S is described in the header.
187  //
188  // Inputs:
189  // s - The constraint to add.
190  //
191  {
192  _constraints.push_back(Constraint(s));
193  }
194 
196  //
197  // Purpose: Specify the combination of objects that will be returned by
198  // constrain() as the mass. The format of S is the same as for
199  // normal constraints. The LHS specifies the mass to calculate;
200  // the RHS should be zero.
201  // This should only be called once.
202  //
203  // Inputs:
204  // s - The constraint defining the mass.
205  //
206  {
207  assert(_mass_constraint.empty());
208  _mass_constraint.push_back(Constraint(s));
209  }
210 
220  std::ostream& operator<<(std::ostream& s, const Fourvec_Constrainer& c)
221  //
222  // Purpose: Print the object to S.
223  //
224  // Inputs:
225  // s - The stream to which to write.
226  // c - The object to write.
227  //
228  // Returns:
229  // The stream S.
230  //
231  {
232  s << "Constraints: (e_com = " << c._args.e_com() << ") ";
233  if (c._args.use_e())
234  s << "(E)";
235  s << "\n";
236 
237  for (std::vector<Constraint>::size_type i = 0; i < c._constraints.size(); i++)
238  s << " " << c._constraints[i] << "\n";
239 
240  if (!c._mass_constraint.empty()) {
241  s << "Mass constraint:\n";
242  s << c._mass_constraint[0] << "\n";
243  }
244  return s;
245  }
246 
247  //*************************************************************************
248  // Event packing and unpacking.
249  //
250 
251  namespace {
252 
263  void adjust_fourvecs(Fourvec_Event& ev, bool use_e_flag)
264  //
265  // Purpose: For all objects in EV, adjust their 4-momenta
266  // to have their requested masses.
267  //
268  // Inputs:
269  // ev - The event on which to operate.
270  // use_e_flag - If true, keep E and scale 3-momentum.
271  // If false, keep the 3-momentum and scale E.
272  //
273  {
274  int nobjs = ev.nobjs();
275  for (int i = 0; i < nobjs; i++) {
276  const FE_Obj& obj = ev.obj(i);
277  Fourvec p = obj.p;
278  if (use_e_flag)
279  adjust_p_for_mass(p, obj.mass);
280  else
281  adjust_e_for_mass(p, obj.mass);
282  ev.set_obj_p(i, p);
283  }
284  }
285 
302  Fourvec get_p_eta_phi_vec(const Column_Vector& c, int ndx, const FE_Obj& obj, bool use_e_flag)
303  //
304  // Purpose: Convert object NDX from its representation in the set
305  // of well-measured variables C to a 4-vector.
306  //
307  // Inputs:
308  // c - The vector of well-measured variables.
309  // ndx - The index of the object in which we're interested.
310  // obj - The object from the Fourvec_Event.
311  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
312  //
313  // Returns:
314  // The object's 4-momentum.
315  //
316  {
317  // Get the energy and momentum of the object.
318  double e, p;
319 
320  if (use_e_flag) {
321  // We're using E as a fit variable. Get it directly.
322  e = c(ndx + p_offs);
323 
324  // Take into account the muon case.
325  if (obj.muon_p)
326  e = 1 / e;
327 
328  // Find the momentum given the energy.
329  if (obj.mass == 0)
330  p = e;
331  else {
332  double xx = e * e - obj.mass * obj.mass;
333  if (xx >= 0)
334  p = sqrt(xx);
335  else
336  p = 0;
337  }
338  } else {
339  // We're using P as a fit variable. Fetch it.
340  p = c(ndx + p_offs);
341 
342  // Take into account the muon case.
343  if (obj.muon_p)
344  p = 1 / p;
345 
346  // Find the energy given the momentum.
347  e = (obj.mass == 0 ? p : sqrt(obj.mass * obj.mass + p * p));
348  }
349 
350  // Get angular variables.
351  double phi = c(ndx + phi_offs);
352  double eta = c(ndx + eta_offs);
353  if (fabs(eta) > 50) {
354  // Protect against ridiculously large etas
355  eta = eta > 0 ? 50 : -50;
356  }
357  double exp_eta = exp(eta);
358  double iexp_eta = 1 / exp_eta;
359  double sin_theta = 2 / (exp_eta + iexp_eta);
360  double cos_theta = (exp_eta - iexp_eta) / (exp_eta + iexp_eta);
361 
362  // Form the 4-momentum.
363  return Fourvec(p * sin_theta * cos(phi), p * sin_theta * sin(phi), p * cos_theta, e);
364  }
365 
382  void set_p_eta_phi_vec(const FE_Obj& obj, Column_Vector& c, int ndx, bool use_e_flag)
383  //
384  // Purpose: Initialize the variables in the well-measured set C describing
385  // object NDX from its Fourvec_Event representation OBJ.
386  //
387  // Inputs:
388  // obj - The object from the Fourvec_Event.
389  // c - The vector of well-measured variables.
390  // ndx - The index of the object in which we're interested.
391  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
392  //
393  //
394  {
395  if (use_e_flag)
396  c(ndx + p_offs) = obj.p.e();
397  else
398  c(ndx + p_offs) = obj.p.vect().mag();
399 
400  if (obj.muon_p)
401  c(ndx + p_offs) = 1 / c(ndx + p_offs);
402  c(ndx + phi_offs) = obj.p.phi();
403  c(ndx + eta_offs) = obj.p.pseudoRapidity();
404  }
405 
415  Matrix error_matrix(double p_sig, double phi_sig, double eta_sig)
416  //
417  // Purpose: Set up the 3x3 error matrix for an object.
418  //
419  // Inputs:
420  // p_sig - The momentum uncertainty.
421  // phi_sig - The phi uncertainty.
422  // eta_sig - The eta uncertainty.
423  //
424  // Returns:
425  // The object's error matrix.
426  //
427  {
428  Matrix err(3, 3, 0);
429  err(1 + p_offs, 1 + p_offs) = p_sig * p_sig;
430  err(1 + phi_offs, 1 + phi_offs) = phi_sig * phi_sig;
431  err(1 + eta_offs, 1 + eta_offs) = eta_sig * eta_sig;
432  return err;
433  }
434 
460  void pack_event(const Fourvec_Event& ev,
461  bool use_e_flag,
462  bool use_kt_flag,
463  Column_Vector& xm,
464  Column_Vector& ym,
465  Matrix& G_i,
467  //
468  // Purpose: Take the information from a Fourvec_Event EV and pack
469  // it into vectors of well- and poorly-measured variables.
470  // Also set up the error matrices.
471  //
472  // Inputs:
473  // ev - The event to pack.
474  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
475  // use_kt_flag - True if we're to pack kt variables.
476  //
477  // Outputs:
478  // xm - Vector of well-measured variables.
479  // ym - Vector of poorly-measured variables.
480  // G_i - Error matrix for well-measured variables.
481  // Y - Inverse error matrix for poorly-measured variables.
482  //
483  {
484  // Number of objects in the event.
485  int nobjs = ev.nobjs();
486 
487  int n_measured_vars = nobjs * 3;
488  if (use_kt_flag)
489  n_measured_vars += 2;
490 
491  // Clear the error matrix.
492  G_i = Matrix(n_measured_vars, n_measured_vars, 0);
493 
494  // Loop over objects.
495  for (int i = 0; i < nobjs; i++) {
496  const FE_Obj& obj = ev.obj(i);
497  int this_index = obj_index(i);
498  set_p_eta_phi_vec(obj, xm, this_index, use_e_flag);
499  G_i.sub(this_index, this_index, error_matrix(obj.p_error, obj.phi_error, obj.eta_error));
500  }
501 
502  if (use_kt_flag) {
503  // Set up kt.
504  int kt_ndx = obj_index(nobjs);
505  xm(kt_ndx + x_offs) = ev.kt().x();
506  xm(kt_ndx + y_offs) = ev.kt().y();
507 
508  // And its error matrix.
509  G_i(kt_ndx + x_offs, kt_ndx + x_offs) = ev.kt_x_error() * ev.kt_x_error();
510  G_i(kt_ndx + y_offs, kt_ndx + y_offs) = ev.kt_y_error() * ev.kt_y_error();
511  G_i(kt_ndx + x_offs, kt_ndx + y_offs) = ev.kt_xy_covar();
512  G_i(kt_ndx + y_offs, kt_ndx + x_offs) = ev.kt_xy_covar();
513  }
514 
515  // Handle a neutrino.
516  if (ev.has_neutrino()) {
517  ym(nu_z) = ev.nu().z();
518  Y = Diagonal_Matrix(1, 0);
519  }
520  }
521 
541  void unpack_event(
542  Fourvec_Event& ev, const Column_Vector& x, const Column_Vector& y, bool use_e_flag, bool use_kt_flag)
543  //
544  // Purpose: Update the contents of EV from the variable sets X and Y.
545  //
546  // Inputs:
547  // ev - The event.
548  // x - Vector of well-measured variables.
549  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
550  // use_kt_flag - True if we're too unpack kt variables.
551  //
552  // Outputs:
553  // ev - The event after updating.
554  //
555  {
556  // Do all the objects.
557  Fourvec sum = Fourvec(0);
558  for (int j = 0; j < ev.nobjs(); j++) {
559  const FE_Obj& obj = ev.obj(j);
560  ev.set_obj_p(j, get_p_eta_phi_vec(x, obj_index(j), obj, use_e_flag));
561  sum += obj.p;
562  }
563 
564  if (use_kt_flag) {
565  int kt_ndx = obj_index(ev.nobjs());
566  Fourvec kt = Fourvec(x(kt_ndx + x_offs), x(kt_ndx + y_offs), 0, 0);
567  Fourvec nu = kt - sum;
568  if (ev.has_neutrino()) {
569  nu.setPz(y(nu_z));
570  adjust_e_for_mass(nu, 0);
571  ev.set_nu_p(nu);
572  } else {
573  adjust_e_for_mass(nu, 0);
574  ev.set_x_p(nu);
575  }
576  }
577  }
578 
579  } // unnamed namespace
580 
581  //*************************************************************************
582  // Constraint evaluation.
583  //
584 
585  namespace {
586 
615  double dot_and_gradient(
616  const Fourvec& v1, const Fourvec& v2, bool use_e_flag, double v1_x[3], double v2_x[3], bool& badflag)
617  //
618  // Purpose: Compute the dot product v1.v2 and its gradients wrt
619  // p, phi, and theta of each 4-vector.
620  //
621  // Inputs:
622  // v1 - The first 4-vector in the dot product.
623  // v2 - The second 4-vector in the dot product.
624  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
625  //
626  // Outputs:
627  // v1_x - Gradients of the dot product wrt v1's p, phi, theta.
628  // v2_x - Gradients of the dot product wrt v2's p, phi, theta.
629  // badflag - Set to true for the singular case (vectors vanish).
630  //
631  // Returns:
632  // The dot product.
633  //
634  {
635  // Calculate the dot product.
636  double dot = v1 * v2;
637 
638  double p1 = v1.vect().mag();
639  double p2 = v2.vect().mag();
640  double e1 = v1.e();
641  double e2 = v2.e();
642  double pt1 = v1.vect().perp();
643  double pt2 = v2.vect().perp();
644 
645  // Protect against the singular case.
646  badflag = false;
647  if (p1 == 0 || p2 == 0 || e1 == 0 || e2 == 0 || pt1 == 0 || pt2 == 0) {
648  badflag = true;
649  v1_x[p_offs] = v1_x[phi_offs] = v1_x[eta_offs] = 0;
650  v2_x[p_offs] = v2_x[phi_offs] = v2_x[eta_offs] = 0;
651  return false;
652  }
653 
654  // Calculate the gradients.
655  v1_x[p_offs] = (dot - v1.m2() * e2 / e1) / p1;
656  v2_x[p_offs] = (dot - v2.m2() * e1 / e2) / p2;
657 
658  if (use_e_flag) {
659  v1_x[p_offs] *= e1 / p1;
660  v2_x[p_offs] *= e2 / p2;
661  }
662 
663  v1_x[phi_offs] = v1(1) * v2(0) - v1(0) * v2(1);
664  v2_x[phi_offs] = -v1_x[phi_offs];
665 
666  double fac = v1(0) * v2(0) + v1(1) * v2(1);
667  v1_x[eta_offs] = pt1 * v2(2) - v1(2) / pt1 * fac;
668  v2_x[eta_offs] = pt2 * v1(2) - v2(2) / pt2 * fac;
669 
670  return dot;
671  }
672 
694  void addin_obj_gradient(int constraint_no, int sign, int base_index, const double grad[], Matrix& Bx)
695  //
696  // Purpose: Tally up the dot product gradients for an object
697  // into Bx.
698  //
699  // Inputs:
700  // constraint_no-The number of the constraint.
701  // base_index - The index in the well-measured variable list
702  // of the first variable for this object.
703  // sign - The sign with which these gradients should be
704  // added into Bx, either +1 or -1. (I.e., which
705  // side of the constraint equation.)
706  // grad - The gradients for this object, vs. p, phi, theta.
707  // Bx - The well-measured variable gradients.
708  //
709  // Outputs:
710  // Bx - The well-measured variable gradients, updated.
711  //
712  {
713  Bx(base_index + p_offs, constraint_no) += sign * grad[p_offs];
714  Bx(base_index + phi_offs, constraint_no) += sign * grad[phi_offs];
715  Bx(base_index + eta_offs, constraint_no) += sign * grad[eta_offs];
716  }
717 
745  void addin_nu_gradient(int constraint_no, int sign, int kt_ndx, const double grad[], Matrix& Bx, Matrix& By)
746  //
747  // Purpose: Tally up the dot product gradients for a neutrino
748  // into Bx and By.
749  //
750  // Inputs:
751  // constraint_no-The number of the constraint.
752  // sign - The sign with which these gradients should be
753  // added into Bx, either +1 or -1. (I.e., which
754  // side of the constraint equation.)
755  // kt_ndx - The index of the kt variables in the variables array.
756  // grad - The gradients for this object, vs. p, phi, theta.
757  // Bx - The well-measured variable gradients.
758  // By - The poorly-measured variable gradients.
759  //
760  // Outputs:
761  // Bx - The well-measured variable gradients, updated.
762  // By - The poorly-measured variable gradients, updated.
763  //
764  {
765  Bx(kt_ndx + x_offs, constraint_no) += sign * grad[p_offs]; // Really p for now.
766  Bx(kt_ndx + y_offs, constraint_no) += sign * grad[phi_offs]; // Really phi for now.
767  By(nu_z, constraint_no) += sign * grad[eta_offs]; // Really theta ...
768  }
769 
796  void addin_gradient(
797  const Fourvec_Event& ev, int constraint_no, int sign, int obj_no, const double grad[], Matrix& Bx, Matrix& By)
798  //
799  // Purpose: Tally up the dot product gradients for an object (which may
800  // or may not be a neutrino) into Bx and By.
801  //
802  // Inputs:
803  // ev - The event we're fitting.
804  // constraint_no-The number of the constraint.
805  // sign - The sign with which these gradients should be
806  // added into Bx, either +1 or -1. (I.e., which
807  // side of the constraint equation.)
808  // obj_no - The number of the object.
809  // grad - The gradients for this object, vs. p, phi, theta.
810  // Bx - The well-measured variable gradients.
811  // By - The poorly-measured variable gradients.
812  //
813  // Outputs:
814  // Bx - The well-measured variable gradients, updated.
815  // By - The poorly-measured variable gradients, updated.
816  //
817  {
818  if (obj_no >= ev.nobjs()) {
819  assert(obj_no == ev.nobjs());
820  addin_nu_gradient(constraint_no, sign, obj_index(obj_no), grad, Bx, By);
821  } else
822  addin_obj_gradient(constraint_no, sign, obj_index(obj_no), grad, Bx);
823  }
824 
855  void addin_gradients(const Fourvec_Event& ev,
856  int constraint_no,
857  int sign,
858  int i,
859  const double igrad[],
860  int j,
861  const double jgrad[],
862  Matrix& Bx,
863  Matrix& By)
864  //
865  // Purpose: Tally up the gradients from a single dot product into Bx and By.
866  //
867  // Inputs:
868  // ev - The event we're fitting.
869  // constraint_no-The number of the constraint.
870  // sign - The sign with which these gradients should be
871  // added into Bx, either +1 or -1. (I.e., which
872  // side of the constraint equation.)
873  // i - The number of the first object.
874  // igrad - The gradients for the first object, vs. p, phi, theta.
875  // j - The number of the second object.
876  // jgrad - The gradients for the second object, vs. p, phi, theta.
877  // Bx - The well-measured variable gradients.
878  // By - The poorly-measured variable gradients.
879  //
880  // Outputs:
881  // Bx - The well-measured variable gradients, updated.
882  // By - The poorly-measured variable gradients, updated.
883  //
884  {
885  addin_gradient(ev, constraint_no, sign, i, igrad, Bx, By);
886  addin_gradient(ev, constraint_no, sign, j, jgrad, Bx, By);
887  }
888 
901  void add_mass_terms(const Fourvec_Event& ev, const vector<Constraint>& constraints, Row_Vector& F)
902  //
903  // Purpose: Add the m^2 terms into the constraint values.
904  //
905  // Inputs:
906  // ev - The event we're fitting.
907  // constraints - The list of constraints.
908  // F - Vector of constraint values.
909  //
910  // Outputs:
911  // F - Vector of constraint values, updated.
912  //
913  {
914  for (std::vector<Constraint>::size_type i = 0; i < constraints.size(); i++)
915  F(i + 1) += constraints[i].sum_mass_terms(ev);
916  }
917 
951  bool calculate_mass_constraints(const Fourvec_Event& ev,
952  const Pair_Table& pt,
953  const vector<Constraint>& constraints,
954  bool use_e_flag,
955  Row_Vector& F,
956  Matrix& Bx,
957  Matrix& By)
958  //
959  // Purpose: Calculate the mass constraints and gradients.
960  // Note: At this stage, the gradients are calculated not
961  // quite with respect to the fit variables; instead, for
962  // all objects (including the neutrino) we calculate
963  // the gradients with respect to p, phi, theta. They'll
964  // be converted via appropriate Jacobian transformations
965  // later.
966  //
967  // Inputs:
968  // ev - The event we're fitting.
969  // pt - Table of cached pair assignments.
970  // constraints - The list of constraints.
971  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
972  //
973  // Outputs:
974  // (nb. these should be passed in correctly dimensioned.)
975  // F - Vector of constraint values.
976  // Bx - The well-measured variable gradients.
977  // By - The poorly-measured variable gradients.
978  //
979  {
980  int npairs = pt.npairs();
981  for (int p = 0; p < npairs; p++) {
982  const Objpair& objpair = pt.get_pair(p);
983  int i = objpair.i();
984  int j = objpair.j();
985  double igrad[3], jgrad[3];
986  bool badflag = false;
987  double dot = dot_and_gradient(ev.obj(i).p, ev.obj(j).p, use_e_flag, igrad, jgrad, badflag);
988  if (badflag)
989  return false;
990 
991  for (std::vector<Constraint>::size_type k = 0; k < constraints.size(); k++)
992  if (objpair.for_constraint(k)) {
993  F(k + 1) += objpair.for_constraint(k) * dot;
994  addin_gradients(ev, k + 1, objpair.for_constraint(k), i, igrad, j, jgrad, Bx, By);
995  }
996  }
997 
998  add_mass_terms(ev, constraints, F);
999  return true;
1000  }
1001 
1024  void add_nuterm(unsigned ndx, const Fourvec& v, Matrix& Bx, bool use_e_flag, int kt_ndx)
1025  //
1026  // Purpose: Carry out the Jacobian transformation for
1027  // (p_nu^x,p_nu_y) -> (kt^x, kt_y) for a given object.
1028  //
1029  // Inputs:
1030  // ndx - Index of the object for which to transform gradients.
1031  // v - The object's 4-momentum.
1032  // Bx - The well-measured variable gradients.
1033  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
1034  // kt_ndx - The index of the kt variables in the variables array.
1035  //
1036  // Outputs:
1037  // Bx - The well-measured variable gradients, updated.
1038  //
1039  {
1040  double px = v.px();
1041  double py = v.py();
1042  double cot_theta = v.pz() / v.vect().perp();
1043 
1044  for (int j = 1; j <= Bx.num_col(); j++) {
1045  double dxnu = Bx(kt_ndx + x_offs, j);
1046  double dynu = Bx(kt_ndx + y_offs, j);
1047 
1048  if (dxnu != 0 || dynu != 0) {
1049  double fac = 1 / v.vect().mag();
1050  if (use_e_flag)
1051  fac = v.e() * fac * fac;
1052  Bx(ndx + p_offs, j) -= (px * dxnu + py * dynu) * fac;
1053  Bx(ndx + phi_offs, j) += py * dxnu - px * dynu;
1054  Bx(ndx + eta_offs, j) -= (px * dxnu + py * dynu) * cot_theta;
1055  }
1056  }
1057  }
1058 
1082  void convert_neutrino(const Fourvec_Event& ev, bool use_e_flag, Matrix& Bx, Matrix& By)
1083  //
1084  // Purpose: Carry out the Jacobian transformations the neutrino.
1085  // First, convert from spherical (p, phi, theta) coordinates
1086  // to rectangular (x, y, z). Then convert from neutrino pt
1087  // components to kt components.
1088  //
1089  // Inputs:
1090  // ev - The event we're fitting.
1091  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
1092  // Bx - The well-measured variable gradients.
1093  // By - The poorly-measured variable gradients.
1094  //
1095  // Outputs:
1096  // Bx - The well-measured variable gradients, updated.
1097  // By - The poorly-measured variable gradients, updated.
1098  //
1099  {
1100  int nconstraints = Bx.num_col();
1101 
1102  const Fourvec& nu = ev.nu();
1103 
1104  // convert neutrino from polar coordinates to rectangular.
1105  double pnu2 = nu.vect().mag2();
1106  double pnu = sqrt(pnu2);
1107  double ptnu2 = nu.vect().perp2();
1108  double ptnu = sqrt(ptnu2);
1109 
1110  // Doesn't matter whether we use E or P here, since nu is massless.
1111 
1112  double thfac = nu.z() / pnu2 / ptnu;
1113  double fac[3][3];
1114  fac[0][0] = nu(0) / pnu;
1115  fac[0][1] = nu(1) / pnu;
1116  fac[0][2] = nu(2) / pnu;
1117  fac[1][0] = -nu(1) / ptnu2;
1118  fac[1][1] = nu(0) / ptnu2;
1119  fac[1][2] = 0;
1120  fac[2][0] = nu(0) * thfac;
1121  fac[2][1] = nu(1) * thfac;
1122  fac[2][2] = -ptnu / pnu2;
1123 
1124  int kt_ndx = obj_index(ev.nobjs());
1125  for (int j = 1; j <= nconstraints; j++) {
1126  double tmp1 = fac[0][0] * Bx(kt_ndx + x_offs, j) + fac[1][0] * Bx(kt_ndx + y_offs, j) + fac[2][0] * By(nu_z, j);
1127  Bx(kt_ndx + y_offs, j) =
1128  fac[0][1] * Bx(kt_ndx + x_offs, j) + fac[1][1] * Bx(kt_ndx + y_offs, j) + fac[2][1] * By(nu_z, j);
1129  By(nu_z, j) = fac[0][2] * Bx(kt_ndx + x_offs, j) + fac[2][2] * By(nu_z, j);
1130 
1131  Bx(kt_ndx + x_offs, j) = tmp1;
1132  }
1133 
1134  // Add nu terms.
1135  for (int j = 0; j < ev.nobjs(); j++) {
1136  add_nuterm(obj_index(j), ev.obj(j).p, Bx, use_e_flag, kt_ndx);
1137  }
1138  }
1139 
1160  void calculate_kt_constraints(const Fourvec_Event& ev, bool use_e_flag, Row_Vector& F, Matrix& Bx)
1161  //
1162  // Purpose: Calculate the overall kt constraints (kt = 0) for the case
1163  // where there is no neutrino.
1164  //
1165  // Inputs:
1166  // ev - The event we're fitting.
1167  // use_e_flag - If true, we're using E as the fit variable, otherwise p.
1168  // F - Vector of constraint values.
1169  // Bx - The well-measured variable gradients.
1170  //
1171  // Outputs:
1172  // F - Vector of constraint values, updated.
1173  // Bx - The well-measured variable gradients, updated.
1174  //
1175  {
1176  Fourvec tmp = Fourvec(0);
1177  int base = F.num_col() - 2;
1178  int nobjs = ev.nobjs();
1179  for (int j = 0; j < nobjs; j++) {
1180  const Fourvec& obj = ev.obj(j).p;
1181  tmp += obj;
1182 
1183  int ndx = obj_index(j);
1184  double p = obj.vect().mag();
1185  double cot_theta = obj.z() / obj.vect().perp();
1186 
1187  Bx(ndx + p_offs, base + 1) = obj(0) / p;
1188  Bx(ndx + phi_offs, base + 1) = -obj(1);
1189  Bx(ndx + eta_offs, base + 1) = obj(0) * cot_theta;
1190 
1191  Bx(ndx + p_offs, base + 2) = obj(1) / p;
1192  Bx(ndx + phi_offs, base + 2) = obj(0);
1193  Bx(ndx + eta_offs, base + 2) = obj(1) * cot_theta;
1194 
1195  if (use_e_flag) {
1196  Bx(ndx + p_offs, base + 1) *= obj.e() / p;
1197  Bx(ndx + p_offs, base + 2) *= obj.e() / p;
1198  }
1199  }
1200 
1201  int kt_ndx = obj_index(nobjs);
1202  Bx(kt_ndx + x_offs, base + 1) = -1;
1203  Bx(kt_ndx + y_offs, base + 2) = -1;
1204 
1205  F(base + 1) = tmp(0) - ev.kt().x();
1206  F(base + 2) = tmp(1) - ev.kt().y();
1207  }
1208 
1222  void ddtheta_to_ddeta(int i, double cos_theta, Matrix& Bx)
1223  //
1224  // Purpose: Do the Jacobian transformation from theta -> eta
1225  // for a single object.
1226  //
1227  // Inputs:
1228  // i - The index of the object.
1229  // cos_theta - cos(theta) for the object.
1230  // Bx - The well-measured variable gradients.
1231  //
1232  // Outputs:
1233  // Bx - The well-measured variable gradients, updated.
1234  //
1235  {
1236  double sin_theta = sqrt(1 - cos_theta * cos_theta);
1237  for (int j = 1; j <= Bx.num_col(); j++)
1238  Bx(i, j) *= -sin_theta; /* \sin\theta = 1 / \cosh\eta */
1239  }
1240 
1241  } // unnamed namespace
1242 
1269  //
1270  // Purpose: Constraint evaluator.
1271  //
1272  {
1273  public:
1274  // Constructor, destructor.
1276  const vector<Constraint>& constraints,
1279 
1280  // Evaluate constraints at the point described by X and Y (well-measured
1281  // and poorly-measured variables, respectively). The results should
1282  // be stored in F. BX and BY should be set to the gradients of F with
1283  // respect to X and Y, respectively.
1284  //
1285  // Return true if the point X, Y is accepted.
1286  // Return false if it is rejected (i.e., in an unphysical region).
1287  // The constraints need not be evaluated in that case.
1288  bool eval(const Column_Vector& x, const Column_Vector& y, Row_Vector& F, Matrix& Bx, Matrix& By) override;
1289 
1290  // Calculate the constraint functions and gradients.
1291  bool calculate_constraints(Row_Vector& F, Matrix& Bx, Matrix& By) const;
1292 
1293  private:
1294  // The event we're fitting.
1296 
1297  // Vector of constraints.
1298  const vector<Constraint>& _constraints;
1299 
1300  // Argument values.
1302 
1303  // The pair table.
1305  };
1306 
1317  const vector<Constraint>& constraints,
1319  //
1320  // Purpose: Constructor.
1321  //
1322  // Inputs:
1323  // ev - The event we're fitting.
1324  // constraints - The list of constraints.
1325  // args - The parameter settings.
1326  //
1327  //
1328  : Constraint_Calculator(constraints.size() + ((ev.has_neutrino() || args.ignore_met()) ? 0 : 2)),
1329  _ev(ev),
1330  _constraints(constraints),
1331  _args(args),
1332  _pt(constraints, ev)
1333 
1334  {}
1335 
1351  //
1352  // Purpose: Calculate the constraint functions and gradients.
1353  //
1354  // Outputs:
1355  // F - Vector of constraint values.
1356  // Bx - The well-measured variable gradients.
1357  // By - The poorly-measured variable gradients.
1358  //
1359  {
1360  // Clear the matrices.
1361  Bx = Matrix(Bx.num_row(), Bx.num_col(), 0);
1362  By = Matrix(By.num_row(), By.num_col(), 0);
1363  F = Row_Vector(F.num_col(), 0);
1364 
1365  const double p_eps = 1e-10;
1366 
1367  if (_ev.has_neutrino() && _ev.nu().z() > _args.e_com()) {
1368  return false;
1369  }
1370 
1371  int nobjs = _ev.nobjs();
1372 
1373  // Reject the point if any of the momenta get too small.
1374  for (int j = 0; j < nobjs; j++) {
1375  if (_ev.obj(j).p.perp() <= p_eps || _ev.obj(j).p.e() <= p_eps) {
1376  return false;
1377  }
1378  }
1379 
1380  if (!calculate_mass_constraints(_ev, _pt, _constraints, _args.use_e(), F, Bx, By))
1381  return false;
1382 
1383  if (_ev.has_neutrino())
1384  convert_neutrino(_ev, _args.use_e(), Bx, By);
1385  else if (!_args.ignore_met()) {
1386  /* kt constraints */
1387  calculate_kt_constraints(_ev, _args.use_e(), F, Bx);
1388  }
1389 
1390  /* convert d/dtheta to d/deta */
1391  for (int j = 0; j < nobjs; j++) {
1392  ddtheta_to_ddeta(obj_index(j) + eta_offs, _ev.obj(j).p.cosTheta(), Bx);
1393  }
1394 
1395  /* handle muons */
1396  for (int j = 0; j < nobjs; j++) {
1397  const FE_Obj& obj = _ev.obj(j);
1398  if (obj.muon_p) {
1399  // Again, E vs. P doesn't matter here since we assume muons to be massless.
1400  double pmu2 = obj.p.vect().mag2();
1401  int ndx = obj_index(j) + p_offs;
1402  for (int k = 1; k <= Bx.num_col(); k++)
1403  Bx(ndx, k) = -Bx(ndx, k) * pmu2;
1404  }
1405  }
1406 
1407  return true;
1408  }
1409 
1440  const Column_Vector& x, const Column_Vector& y, Row_Vector& F, Matrix& Bx, Matrix& By)
1441  //
1442  // Purpose: Evaluate constraints at the point described by X and Y
1443  // (well-measured and poorly-measured variables, respectively).
1444  // The results should be stored in F. BX and BY should be set
1445  // to the gradients of F with respect to X and Y, respectively.
1446  //
1447  // Inputs:
1448  // x - Vector of well-measured variables.
1449  // y - Vector of poorly-measured variables.
1450  //
1451  // Outputs:
1452  // F - Vector of constraint values.
1453  // Bx - The well-measured variable gradients.
1454  // By - The poorly-measured variable gradients.
1455  //
1456  {
1457  int nobjs = _ev.nobjs();
1458 
1459  const double p_eps = 1e-10;
1460  const double eta_max = 10;
1461 
1462  // Give up if we've gone into an obviously unphysical region.
1463  for (int j = 0; j < nobjs; j++)
1464  if (x(obj_index(j) + p_offs) < p_eps || fabs(x(obj_index(j) + eta_offs)) > eta_max) {
1465  return false;
1466  }
1467 
1468  unpack_event(_ev, x, y, _args.use_e(), _ev.has_neutrino() || !_args.ignore_met());
1469 
1470  return calculate_constraints(F, Bx, By);
1471  }
1472 
1473  //*************************************************************************
1474  // Mass calculation.
1475  //
1476 
1477  namespace {
1478 
1496  double calculate_sigm(
1497  const Matrix& Q, const Matrix& R, const Matrix& S, const Column_Vector& Bx, const Column_Vector& By)
1498  //
1499  // Purpose: Do error propagation to find the uncertainty in the final mass.
1500  //
1501  // Inputs:
1502  // Q - The final error matrix for the well-measured variables.
1503  // R - The final error matrix for the poorly-measured variables.
1504  // S - The final cross error matrix for the two sets of variables.
1505  // Bx - The well-measured variable gradients.
1506  // By - The poorly-measured variable gradients.
1507  //
1508  {
1509  double sig2 = scalar(Bx.T() * Q * Bx);
1510 
1511  if (By.num_row() > 0) {
1512  sig2 += scalar(By.T() * R * By);
1513  sig2 += 2 * scalar(Bx.T() * S * By);
1514  }
1515 
1516  assert(sig2 >= 0);
1517  return sqrt(sig2);
1518  }
1519 
1542  void calculate_mass(Fourvec_Event& ev,
1543  const vector<Constraint>& mass_constraint,
1544  const Fourvec_Constrainer_Args& args,
1545  double chisq,
1546  const Matrix& Q,
1547  const Matrix& R,
1548  const Matrix& S,
1549  double& m,
1550  double& sigm)
1551  //
1552  // Purpose: Calculate the final requested mass and its uncertainty.
1553  //
1554  // Inputs:
1555  // ev - The event we're fitting.
1556  // mass_constraint- The description of the mass we're to calculate.
1557  // args - Parameter settings.
1558  // chisq - The chisq from the fit.
1559  // Q - The final error matrix for the well-measured variables.
1560  // R - The final error matrix for the poorly-measured variables.
1561  // S - The final cross error matrix for the two sets of variables.
1562  //
1563  // Outputs:
1564  // m - The mass.
1565  // sigm - Its uncertainty.
1566  //
1567  {
1568  // Don't do anything if the mass wasn't specified.
1569  if (mass_constraint.empty()) {
1570  m = 0;
1571  sigm = 0;
1572  return;
1573  }
1574 
1575  // Do the constraint calculation.
1576  int n_measured_vars = ev.nobjs() * 3 + 2;
1577  int n_unmeasured_vars = 0;
1578  if (ev.has_neutrino())
1579  n_unmeasured_vars = 1;
1580 
1581  Row_Vector F(1);
1582  Matrix Bx(n_measured_vars, 1);
1583  Matrix By(n_unmeasured_vars, 1);
1584 
1585  Fourvec_Constraint_Calculator cc(ev, mass_constraint, args);
1586  cc.calculate_constraints(F, Bx, By);
1587 
1588  // Calculate the mass.
1589  //assert (F(1) >= 0);
1590  if (F(1) >= 0.)
1591  m = sqrt(F(1) * 2);
1592  else {
1593  m = 0.;
1594  chisq = -100.;
1595  }
1596 
1597  // And the uncertainty.
1598  // We can only do this if the fit converged.
1599  if (chisq < 0)
1600  sigm = 0;
1601  else {
1602  //assert (F(1) > 0);
1603  Bx = Bx / (m);
1604  By = By / (m);
1605 
1606  sigm = calculate_sigm(Q, R, S, Bx, By);
1607  }
1608  }
1609 
1610  } // unnamed namespace
1611 
1613  Fourvec_Event& ev, double& m, double& sigm, Column_Vector& pullx, Column_Vector& pully)
1614  //
1615  // Purpose: Do a constrained fit for EV. Returns the requested mass and
1616  // its error in M and SIGM, and the pull quantities in PULLX and
1617  // PULLY. Returns the chisq; this will be < 0 if the fit failed
1618  // to converge.
1619  //
1620  // Inputs:
1621  // ev - The event we're fitting.
1622  //
1623  // Outputs:
1624  // ev - The fitted event.
1625  // m - Requested invariant mass.
1626  // sigm - Uncertainty on m.
1627  // pullx - Pull quantities for well-measured variables.
1628  // pully - Pull quantities for poorly-measured variables.
1629  //
1630  // Returns:
1631  // The fit chisq, or < 0 if the fit didn't converge.
1632  //
1633  {
1634  adjust_fourvecs(ev, _args.use_e());
1635 
1636  bool use_kt = ev.has_neutrino() || !_args.ignore_met();
1637  int nobjs = ev.nobjs();
1638  int n_measured_vars = nobjs * 3;
1639  int n_unmeasured_vars = 0;
1640  int n_constraints = _constraints.size();
1641 
1642  if (use_kt) {
1643  n_measured_vars += 2;
1644 
1645  if (ev.has_neutrino())
1646  n_unmeasured_vars = 1;
1647  else
1648  n_constraints += 2;
1649  }
1650 
1651  Matrix G_i(n_measured_vars, n_measured_vars);
1652  Diagonal_Matrix Y(n_unmeasured_vars);
1653  Column_Vector x(n_measured_vars);
1654  Column_Vector y(n_unmeasured_vars);
1655  pack_event(ev, _args.use_e(), use_kt, x, y, G_i, Y);
1656 
1657  Column_Vector xm = x;
1658  Column_Vector ym = y;
1659 
1660  // ??? Should allow for using a different underlying fitter here.
1662 
1664 
1665  Matrix Q;
1666  Matrix R;
1667  Matrix S;
1668  double chisq = fitter.fit(cc, xm, x, ym, y, G_i, Y, pullx, pully, Q, R, S);
1669 
1670  unpack_event(ev, x, y, _args.use_e(), use_kt);
1671 
1672  calculate_mass(ev, _mass_constraint, _args, chisq, Q, R, S, m, sigm);
1673 
1674  return chisq;
1675  }
1676 
1677 } // namespace hitfit
size
Write out results.
Define an abstract interface for getting parameter settings.
Represent a mass constraint equation. Mass constraints come in two varieties, either saying that the ...
Definition: Constraint.h:73
void adjust_e_for_mass(Fourvec &v, double mass)
Adjust the energy component of four-vector v (leaving the three-vector part unchanged) so that the fo...
Definition: fourvec.cc:119
Represent an event for kinematic fitting as a collection of four-momenta. Each object is represented ...
base
Main Program
Definition: newFWLiteAna.py:92
Sin< T >::type sin(const T &t)
Definition: Sin.h:22
Fourvec_Constrainer(const Fourvec_Constrainer_Args &args)
Constructor.
CLHEP::HepVector Column_Vector
Definition: matutil.h:63
Concrete realization of the Constraint_Calculator class. Evaluate constraints at the point described ...
Define matrix types for the HitFit package, and supply a few additional operations.
assert(be >=bs)
uint16_t size_type
Abstract base class for evaluating constraints. Users derive from this class and implement the eval()...
T dot(const Basic3DVector &v) const
Scalar product, or "dot" product, with a vector of same type.
CLHEP::HepMatrix Matrix
Definition: matutil.h:62
Fourvec_Constrainer_Args(const Defaults &defs)
Constructor, initialize from a Defaults object.
T sqrt(T t)
Definition: SSEVec.h:19
Cos< T >::type cos(const T &t)
Definition: Cos.h:22
Minimize a subject to a set of constraints. Based on the SQUAW algorithm.
Minimize a subject to a set of constraints, based on the SQUAW algorithm.
CLHEP::HepLorentzVector Fourvec
Typedef for a HepLorentzVector.
Definition: fourvec.h:55
Hold on to parameters for the Chisq_Constrainer class.
const Fourvec_Constrainer_Args _args
void adjust_p_for_mass(Fourvec &v, double mass)
Adjust the three-vector part of v, leaving the energy unchanged,.
Definition: fourvec.cc:95
std::vector< Constraint > _mass_constraint
Fourvec_Constraint_Calculator(Fourvec_Event &ev, const vector< Constraint > &constraints, const Fourvec_Constrainer_Args &args)
Constructor.
bool calculate_constraints(Row_Vector &F, Matrix &Bx, Matrix &By) const
Calculate the constraint functions and gradients.
int nobjs() const
Return the number of objects in the event not including any neutrinos.
CLHEP::HepDiagMatrix Diagonal_Matrix
Definition: matutil.h:64
A lookup table to speed up constraint evaluation using Fourvec_Constrainer.
Definition: Pair_Table.h:95
bool eval(const Column_Vector &x, const Column_Vector &y, Row_Vector &F, Matrix &Bx, Matrix &By) override
Evaluate constraints at the point described by x and y (well-measured and poorly-measured variables...
bool has_neutrino() const
Return TRUE is this event contains a neutrino, otherwise returns FALSE.
Chisq_Constrainer_Args _chisq_constrainer_args
std::vector< Constraint > _constraints
double constrain(Fourvec_Event &ev, double &m, double &sigm, Column_Vector &pullx, Column_Vector &pully)
Do a constrained fit for event ev. Returns the requested mass and its uncertainty in m and sigm...
Define an interface for getting parameter settings.
Definition: Defaults.h:57
std::ostream & operator<<(std::ostream &s, const Constraint_Intermed &ci)
Output stream operator, print the content of this Constraint_Intermed to an output stream...
float x
const Fourvec_Constrainer_Args & _args
Do a kinematic fit for a set of four-vectors, given a set of mass constraints.
void mass_constraint(std::string s)
Specify a combination of objects that will be returned by the constrain() method as mass...
Represent a single object in a Fourvec_Event, this is just a dumb data container. Each object in a Fo...
Definition: Fourvec_Event.h:90
static uInt32 F(BLOWFISH_CTX *ctx, uInt32 x)
Definition: blowfish.cc:163
tmp
align.sh
Definition: createJobs.py:716
const vector< Constraint > & _constraints
void add_constraint(std::string s)
Specify an additional constraint s for the problem. The format for s is described in the class descri...
Do a kinematic fit for a set of four-momenta, given a set of mass constraints.
const Chisq_Constrainer_Args & chisq_constrainer_args() const
A lookup table to speed up constraint evaluation.
Row-vector class. CLHEP doesn&#39;t have a row-vector class, so HitFit uses its own. This is only a simpl...
Definition: matutil.h:79
Hold on to parameters for the Fourvec_Constrainer class.
double scalar(const CLHEP::HepGenMatrix &m)
Return the matrix as a scalar. Raise an assertion if the matris is not .
Definition: matutil.cc:166
Represent an event for kinematic fitting as a collection of four-momenta.