BlockE.cc
1 /*
2  * MoMEMta: a modular implementation of the Matrix Element Method
3  * Copyright (C) 2017 Universite catholique de Louvain (UCL), Belgium
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <momemta/Logging.h>
20 #include <momemta/Module.h>
21 #include <momemta/ParameterSet.h>
22 #include <momemta/Solution.h>
23 #include <momemta/Types.h>
24 #include <momemta/Math.h>
25 
92 class BlockE: public Module {
93  public:
94 
95  BlockE(PoolPtr pool, const ParameterSet& parameters): Module(pool, parameters.getModuleName()) {
96 
97  sqrt_s = parameters.globalParameters().get<double>("energy");
98 
99  s13 = get<double>(parameters.get<InputTag>("s13"));
100  s24 = get<double>(parameters.get<InputTag>("s24"));
101  s_hat = get<double>(parameters.get<InputTag>("s_hat"));
102  y_tot = get<double>(parameters.get<InputTag>("y_tot"));
103 
104  m1 = parameters.get<double>("m1", 0.);
105  m2 = parameters.get<double>("m2", 0.);
106 
107  p3 = get<LorentzVector>(parameters.get<InputTag>("p3"));
108  p4 = get<LorentzVector>(parameters.get<InputTag>("p4"));
109 
110  if (parameters.exists("branches")) {
111  auto branches_tags = parameters.get<std::vector<InputTag>>("branches");
112  for (auto& t: branches_tags)
113  m_branches.push_back(get<LorentzVector>(t));
114  }
115  };
116 
117  virtual Status work() override {
118 
119  solutions->clear();
120 
121  const double s = SQ(sqrt_s);
122 
123  const double sq_m1 = SQ(m1);
124  const double sq_m2 = SQ(m2);
125  const double sq_m3 = p3->M2();
126  const double sq_m4 = p4->M2();
127 
128  // Don't spend time on unphysical part of phase-space
129  if (sq_m1 + sq_m3 >= *s13 || sq_m2 + sq_m4 >= *s24 || *s13 + *s24 >= *s_hat || *s_hat >= s)
130  return Status::NEXT;
131 
132  const double sqrt_shat = std::sqrt(*s_hat);
133 
134  const double p3x = p3->Px();
135  const double p3y = p3->Py();
136  const double p3z = p3->Pz();
137  const double E3 = p3->E();
138 
139  const double p4x = p4->Px();
140  const double p4y = p4->Py();
141  const double p4z = p4->pz();
142  const double E4 = p4->E();
143 
144  // Total visible momentum
145  LorentzVector pb = *p3 + *p4;
146  for (size_t i = 0; i < m_branches.size(); i++) {
147  pb += *m_branches[i];
148  }
149 
150  const double Eb = pb.E();
151  const double pbx = pb.Px();
152  const double pby = pb.Py();
153  const double pbz = pb.Pz();
154 
155  const double Etot = sqrt_shat * std::cosh(*y_tot) - Eb;
156  const double ptotz = sqrt_shat * std::sinh(*y_tot) - pbz;
157 
158  const double X = 0.5 * (- sq_m1 - sq_m3 + *s13);
159  const double Y = 0.5 * (- sq_m2 - sq_m4 + *s24);
160 
161 
162  /* Solve the linear system:
163  * p1x + p2x = - pbx
164  * p1y + p2y = - pby
165  * p1z + p2z = ptotz
166  * p3x p1x + p3y p1y + p3z p1z = - X + E3 E1
167  * p4x p2x + p4z p2z = - Y + E4 E2 -p4y p2y
168  *
169  * The solutions are parameterised by E2 and p2y:
170  * p1x = A1x E2 + B1x p2y + C1x
171  * p1y = - p2y - pby
172  * p1z = A1z E2 + B1z p2y + C1z
173  * p2x = - A1x E2 - B1x p2y - (C1x + pbx)
174  * p2z = - A1z E2 - B1z p2y - (C1z - ptotz)
175  *
176  * where one has used that E1 = Etot - E2
177  */
178 
179  const double den = p3z * p4x - p3x * p4z;
180 
181  const double A1x = - (E4 * p3z - E3 * p4z) / den;
182  const double B1x = (p3z * p4y - p3y * p4z) / den;
183  const double C1x = - (p4z * (E3 * Etot - p3z * ptotz + p3y * pby - X) - p3z * (Y - p4x * pbx)) / den;
184 
185  const double A1z = (E4 * p3x - E3 * p4x) / den;
186  const double B1z = (p3y * p4x - p3x * p4y) / den;
187  const double C1z = (p4x * (E3 * Etot + p3y * pby + p3x * pbx - X) - p3x * (Y + p4z * ptotz)) / den;
188 
189 
190  /* Now, insert those expressions into the mass-shell conditions for p1 and p2:
191  * (Etot - E2)^2 - p1x^2 - p1y^2 - p1z^2 - m1^2 = 0 (1)
192  * E2^2 - p2x^2 - p2y^2 - p2z^2 - m2^2 = 0 (2)
193  *
194  * Using the above, (1) is written as:
195  * a20 E2^2 + a02 p2y^2 + a11 E2 p2y + a10 E2 + a01 p2y + a00 = 0
196  *
197  * From (2)-(1), is it possible to write p2y = a E2 + b. This is then inserted into (1),
198  * yielding a quadratic equation in E2 only.
199  */
200 
201  const double fac = 2 * (A1x * pbx - A1z * ptotz - Etot);
202  const double a = - 2 * (B1x * pbx - B1z * ptotz - pby) / fac;
203  const double b = - (SQ(Etot) + pow(C1x + pbx, 2) + pow(C1z - ptotz, 2) + sq_m2 - SQ(C1x) - SQ(pby) - SQ(C1z) - sq_m1) / fac;
204 
205  const double a20 = 1 - SQ(A1x) - SQ(A1z);
206  const double a02 = - (SQ(B1x) + SQ(B1z) + 1);
207  const double a11 = - 2 * (A1x * B1x + A1z * B1z);
208  const double a10 = - 2 * (A1x * C1x + A1z * C1z + Etot);
209  const double a01 = - 2 * (B1x * C1x + B1z * C1z + pby);
210  const double a00 = SQ(Etot) - (SQ(C1x) + SQ(C1z) + SQ(pby) + sq_m1);
211 
212  std::vector<double> p2y_sol;
213  const bool foundSolution = solveQuadratic(a02 + SQ(a) * a20 + a * a11,
214  2 * a * b * a20 + b * a11 + a01 + a * a10,
215  SQ(b) * a20 + b * a10 + a00,
216  p2y_sol);
217 
218  if (!foundSolution)
219  return Status::NEXT;
220 
221  for (const double p2y: p2y_sol) {
222  const double E2 = a * p2y + b;
223 
224  if (E2 <= 0)
225  continue;
226 
227  const double E1 = Etot - E2;
228 
229  if (E1 <= 0)
230  continue;
231 
232  const double p1x = A1x * E2 + B1x * p2y + C1x;
233  const double p1y = - p2y - pby;
234  const double p1z = A1z * E2 + B1z * p2y + C1z;
235  const LorentzVector p1(p1x, p1y, p1z, E1);
236 
237  const double p2x = - p1x - pbx;
238  const double p2z = - p1z + ptotz;
239  const LorentzVector p2(p2x, p2y, p2z, E2);
240 
241  // Check if solutions are physical
242  const LorentzVector tot = p1 + p2 + pb;
243  const double q1Pz = std::abs(tot.Pz() + tot.E()) / 2.;
244  const double q2Pz = std::abs(tot.Pz() - tot.E()) / 2.;
245 
246  if (q1Pz > sqrt_s/2 || q2Pz > sqrt_s/2)
247  continue;
248 
249  if (!ApproxComparison(tot.Pt(), 0.)) {
250 #ifndef NDEBUG
251  LOG(trace) << "[BlockE] Throwing solution because total Pt is incorrect. "
252  << "Expected " << 0. << ", got " << tot.Pt();
253 #endif
254  continue;
255  }
256 
257  if (!ApproxComparison(p1.M() / p1.E(), m1 / p1.E())) {
258 #ifndef NDEBUG
259  LOG(trace) << "[BlockE] Throwing solution because p1 has an invalid mass. " <<
260  "Expected " << m1 << ", got " << p1.M();
261 #endif
262  continue;
263  }
264 
265  if (!ApproxComparison(p2.M() / p2.E(), m2 / p2.E())) {
266 #ifndef NDEBUG
267  LOG(trace) << "[BlockE] Throwing solution because p2 has an invalid mass. " <<
268  "Expected " << m2 << ", got " << p2.M();
269 #endif
270  continue;
271  }
272 
273  if (!ApproxComparison((p1 + *p3).M2(), *s13)) {
274 #ifndef NDEBUG
275  LOG(trace) << "[BlockE] Throwing solution because of invalid invariant mass. " <<
276  "Expected " << *s13 << ", got " << (p1 + *p3).M2();
277 #endif
278  continue;
279  }
280 
281  if (!ApproxComparison((p2 + *p4).M2(), *s24)) {
282 #ifndef NDEBUG
283  LOG(trace) << "[BlockE] Throwing solution because of invalid invariant mass. " <<
284  "Expected " << *s24 << ", got " << (p2 + *p4).M2();
285 #endif
286  continue;
287  }
288 
289  const double jacobian = 1. / (64 * SQ(M_PI) * s * std::abs(E4*(p1z*p2y*p3x - p1y*p2z*p3x - p1z*p2x*p3y + p1x*p2z*p3y + p1y*p2x*p3z - p1x*p2y*p3z) + E2*p1z*p3y*p4x - E1*p2z*p3y*p4x - E2*p1y*p3z*p4x + E1*p2y*p3z*p4x - E2*p1z*p3x*p4y + E1*p2z*p3x*p4y + E2*p1x*p3z*p4y - E1*p2x*p3z*p4y + (E2*p1y*p3x - E1*p2y*p3x - E2*p1x*p3y + E1*p2x*p3y)*p4z + E3*(-(p1z*p2y*p4x) + p1y*p2z*p4x + p1z*p2x*p4y - p1x*p2z*p4y - p1y*p2x*p4z + p1x*p2y*p4z)));
290 
291  Solution s { {p1, p2}, jacobian, true };
292  solutions->push_back(s);
293  }
294 
295  return solutions->size() > 0 ? Status::OK : Status::NEXT;
296  }
297 
298  private:
299  double sqrt_s;
300 
301  // Inputs
302  Value<double> s13;
303  Value<double> s24;
304  Value<double> s_hat;
305  Value<double> y_tot;
306  double m1;
307  double m2;
308  Value<LorentzVector> p3, p4;
309  std::vector<Value<LorentzVector>> m_branches;
310 
311  // Outputs
312  std::shared_ptr<SolutionCollection> solutions = produce<SolutionCollection>("solutions");
313 };
314 
315 REGISTER_MODULE(BlockE)
316  .Input("s_hat")
317  .Input("y_tot")
318  .Input("s13")
319  .Input("s24")
320  .Input("p3")
321  .Input("p4")
322  .OptionalInputs("branches")
323  .Output("solutions")
324  .GlobalAttr("energy:double")
325  .Attr("m1:double=0")
326  .Attr("m2:double=0");
Final (main) Block E, describing
Definition: BlockE.cc:92
bool ApproxComparison(double value, double expected)
Compare two doubles and return true if they are approximatively equal.
Definition: Math.cc:409
Generic solution structure representing a set of particles, along with its jacobian.
Definition: Solution.h:28
Mathematical functions.
bool solveQuadratic(const double a, const double b, const double c, std::vector< double > &roots, bool verbose=false)
Finds the real solutions to .
Definition: Math.cc:26
virtual Status work() override
Main function.
Definition: BlockE.cc:117
An identifier of a module&#39;s output.
Definition: InputTag_fwd.h:37
Parent class for all the modules.
Definition: Module.h:37
A class encapsulating a lua table.
Definition: ParameterSet.h:82
#define SQ(x)
Compute .
Definition: Math.h:25
Module(PoolPtr pool, const std::string &name)
Constructor.
Definition: Module.h:61