ComPWA
Common Partial-Wave-Analysis Framework
HelicityKinematics.cpp
Go to the documentation of this file.
1 // Copyright (c) 2013, 2017 The ComPWA Team.
2 // This file is part of the ComPWA framework, check
3 // https://github.com/ComPWA/ComPWA/license.txt for details.
4 
5 #include <algorithm>
6 #include <cmath>
7 #include <numeric>
8 #include <sstream>
9 #include <stdexcept>
10 #include <tuple>
11 
12 #include "Core/Event.hpp"
13 #include "Core/Exceptions.hpp"
14 #include "Core/Logging.hpp"
15 #include "Core/FourMomentum.hpp"
16 #include "Core/Properties.hpp"
17 #include "Data/DataSet.hpp"
19 
20 #include "qft++/Vector4.h"
21 
22 namespace ComPWA {
23 namespace Physics {
24 namespace HelicityFormalism {
25 
27  std::vector<pid> initialState,
28  std::vector<pid> finalState,
31  initialState, finalState, partL, cmsP4, [&finalState]() {
32  std::vector<unsigned int> FinalStateEventPositionMapping;
33  for (unsigned int i = 0; i < finalState.size(); ++i) {
34  FinalStateEventPositionMapping.push_back(i);
35  }
36  return FinalStateEventPositionMapping;
37  }())) {}
38 
40  : HelicityKinematics(ki, 1.0) {
43 }
44 
46  double PhspVol)
47  : KinematicsInfo(ki), PhspVolume(PhspVol) {
48  LOG(INFO) << "HelicityKinematics::"
49  "HelicityKinematics() | Initialized kinematics "
50  "for reaction "
51  << KinematicsInfo;
52 }
53 
54 double HelicityKinematics::phspVolume() const { return PhspVolume; }
55 
57  const EventCollection &DataSample) const {
58  EventCollection PhspSample{DataSample.Pids};
59  LOG(INFO) << "HelicityKinematics::reduceToPhaseSpace(): "
60  "Remove all events outside PHSP boundary from data sample.";
61 
62  std::copy_if(DataSample.Events.begin(), DataSample.Events.end(),
63  std::back_inserter(PhspSample.Events), [this](auto evt) {
64  for (auto const &x : InvariantMassesSquared) {
65  auto bounds = InvMassBounds.at(x.second);
66  double mass =
67  this->calculateInvariantMassSquared(evt, x.first);
68  if (mass < bounds.first || mass > bounds.second)
69  return false;
70  }
71  for (auto const &x : Subsystems) {
72  auto angles = this->calculateHelicityAngles(evt, x.first);
73  if (angles.first < 0 || angles.first > M_PI)
74  return false;
75  if (angles.second < -M_PI || angles.second > M_PI)
76  return false;
77  }
78  return true;
79  });
80 
81  LOG(INFO) << "reduceToPhaseSpace(): Removed "
82  << DataSample.Events.size() - PhspSample.Events.size() << " from "
83  << DataSample.Events.size() << " Events ("
84  << (1.0 - PhspSample.Events.size() / DataSample.Events.size()) * 100
85  << "%).";
86 
87  return PhspSample;
88 }
89 
90 std::pair<double, double>
92  const SubSystem &SubSys) const {
93  FourMomentum FinalA, FinalB;
94  for (auto s : SubSys.getFinalStates().at(0)) {
95  unsigned int index = KinematicsInfo.convertFinalStateIDToPositionIndex(s);
96  FinalA += Event.FourMomenta[index];
97  }
98 
99  for (auto s : SubSys.getFinalStates().at(1)) {
100  unsigned int index = KinematicsInfo.convertFinalStateIDToPositionIndex(s);
101  FinalB += Event.FourMomenta[index];
102  }
103 
104  // Four momentum of the decaying resonance
105  FourMomentum State = FinalA + FinalB;
106 
107  QFT::Vector4<double> DecayingState(State);
108  QFT::Vector4<double> Daughter(FinalA);
109 
110  // calculate the recoil and parent recoil
111  auto const &RecoilState = SubSys.getRecoilState();
112 
113  // the first step is boosting everything into the rest system of the
114  // decaying state
115  Daughter.Boost(DecayingState);
116 
117  if (RecoilState.size() > 0) {
118  FourMomentum TempRecoil;
119  for (auto s : RecoilState) {
120  unsigned int index = KinematicsInfo.convertFinalStateIDToPositionIndex(s);
121  TempRecoil += Event.FourMomenta[index];
122  }
123  QFT::Vector4<double> Recoil(TempRecoil);
124 
125  Recoil.Boost(DecayingState);
126 
127  // rotate recoil so that recoil points in the -z direction
128  Daughter.RotateZ(-Recoil.Phi());
129  Daughter.RotateY(M_PI - Recoil.Theta());
130 
131  auto const &ParentRecoilState = SubSys.getParentRecoilState();
132  // in case there is no parent recoil, it is artificially along z
133  QFT::Vector4<double> ParentRecoil(0.0, 0.0, 0.0, 1.0);
134  if (ParentRecoilState.size() > 0) {
135  FourMomentum TempParentRecoil;
136  for (auto s : ParentRecoilState) {
137  unsigned int index =
139  TempParentRecoil += Event.FourMomenta[index];
140  }
141  ParentRecoil = TempParentRecoil;
142  }
143 
144  ParentRecoil.Boost(DecayingState);
145  ParentRecoil.RotateZ(-Recoil.Phi());
146  ParentRecoil.RotateY(M_PI - Recoil.Theta());
147 
148  // rotate around the z-axis so that the parent recoil lies in the x-z
149  // plane
150  Daughter.RotateZ(M_PI - ParentRecoil.Phi());
151  }
152 
153  double cosTheta = Daughter.CosTheta();
154  double phi = Daughter.Phi();
155 
156  return std::make_pair(std::acos(cosTheta), phi);
157 }
158 
160  const Event &Event, const IndexList &FinalStateIDs) const {
161  FourMomentum State;
162  for (auto s : FinalStateIDs) {
163  unsigned int index = KinematicsInfo.convertFinalStateIDToPositionIndex(s);
164  State += Event.FourMomenta[index];
165  }
166 
167  return State.invariantMassSquared();
168 }
169 
172  ComPWA::Data::DataSet Dataset;
173  if (!Subsystems.size()) {
174  LOG(ERROR) << "HelicityKinematics::convert() | No variables were requested "
175  "before. Therefore this function is doing nothing!";
176  return Dataset;
177  }
178  if (KinematicsInfo.getFinalStatePIDs() != DataSample.Pids) {
179  std::stringstream Message;
180  Message << "Pids in EventCollection and in Kinematics do not match";
181  Message << std::endl << " ";
182  Message << DataSample.Pids.size() << " PIDs in EventCollection:";
183  for (auto Pid : DataSample.Pids)
184  Message << " " << Pid;
185  Message << std::endl << " ";
186  Message << KinematicsInfo.getFinalStatePIDs().size()
187  << " PIDs in Kinematics:";
188  for (auto Pid : KinematicsInfo.getFinalStatePIDs())
189  Message << " " << Pid;
190  throw ComPWA::BadParameter(Message.str());
191  }
192  if (!DataSample.checkPidMatchesEvents()) {
193  throw ComPWA::BadParameter("HelicityKinematics::convert() | number of PIDs "
194  "not equal to number of four-momenta");
195  }
196 
197  for (auto const &Masses : InvariantMassesSquared) {
198  Dataset.Data.insert(std::make_pair(Masses.second, std::vector<double>()));
199  for (auto const &Event : DataSample.Events) {
200  auto Mass = calculateInvariantMassSquared(Event, Masses.first);
201  Dataset.Data.at(Masses.second).push_back(Mass);
202  }
203  }
204  for (auto const &SubSystem : Subsystems) {
205  Dataset.Data.insert(
206  std::make_pair(SubSystem.second.first, std::vector<double>()));
207  Dataset.Data.insert(
208  std::make_pair(SubSystem.second.second, std::vector<double>()));
209  for (auto const &event : DataSample.Events) {
210  auto Angles = calculateHelicityAngles(event, SubSystem.first);
211  Dataset.Data.at(SubSystem.second.first).push_back(Angles.first);
212  Dataset.Data.at(SubSystem.second.second).push_back(Angles.second);
213  }
214  }
215  for (auto const &Event : DataSample.Events) {
216  Dataset.Weights.push_back(Event.Weight);
217  }
218  return Dataset;
219 }
220 
221 std::string
223  std::sort(MomentaIDs.begin(), MomentaIDs.end());
224  std::stringstream Name;
225  Name << "mSq_(";
226  std::string comma("");
227  for (auto x : MomentaIDs) {
228  Name << comma << x;
229  comma = ",";
230  }
231  Name << ")";
232 
233  InvMassBounds.insert(
234  std::make_pair(Name.str(), calculateInvMassBounds(MomentaIDs)));
235  InvariantMassesSquared.insert(std::make_pair(MomentaIDs, Name.str()));
236  return Name.str();
237 }
238 
239 std::pair<std::string, std::string>
241  std::stringstream ss;
242  ss << SubSys;
243 
244  std::string ThetaName("theta" + ss.str());
245  std::string PhiName("phi" + ss.str());
246 
247  Subsystems.insert(std::make_pair(SubSys, std::make_pair(ThetaName, PhiName)));
248  return std::make_pair(ThetaName, PhiName);
249 }
250 
251 std::vector<std::pair<ComPWA::IndexList, ComPWA::IndexList>>
253  using ComPWA::IndexList;
254  std::vector<std::pair<IndexList, IndexList>> NewIndexLists;
255  if (A.size() < B.size())
256  throw std::runtime_error("HelicityKinematics::redistributeIndexLists(): A "
257  "cannot have less content than B!");
258  if (A.size() == 2 && B.size() == 0) {
259  NewIndexLists.push_back(std::make_pair(IndexList{A[0]}, IndexList{A[1]}));
260  } else if (A.size() - B.size() > 1) {
261  for (unsigned int i = 0; i < A.size(); ++i) {
262  IndexList TempB(B);
263  TempB.push_back(A[i]);
264  IndexList TempA(A);
265  TempA.erase(TempA.begin() + i);
266  NewIndexLists.push_back(std::make_pair(TempA, TempB));
267  }
268  }
269  return NewIndexLists;
270 }
271 
272 using IndexListTuple = std::tuple<IndexList, IndexList, IndexList, IndexList>;
273 
275  IndexList FinalStateA(std::get<0>(SubSys));
276  IndexList FinalStateB(std::get<1>(SubSys));
277  IndexList RecoilState(std::get<2>(SubSys));
278  IndexList ParentRecoilState(std::get<3>(SubSys));
279 
280  // create sorted entry
281  std::sort(FinalStateA.begin(), FinalStateA.end());
282  std::sort(FinalStateB.begin(), FinalStateB.end());
283  std::sort(RecoilState.begin(), RecoilState.end());
284  std::sort(ParentRecoilState.begin(), ParentRecoilState.end());
285 
286  IndexListTuple SortedTuple;
287  if (FinalStateA > FinalStateB)
288  SortedTuple = std::make_tuple(FinalStateB, FinalStateA, RecoilState,
289  ParentRecoilState);
290  else
291  SortedTuple = std::make_tuple(FinalStateA, FinalStateB, RecoilState,
292  ParentRecoilState);
293  return SortedTuple;
294 }
295 
297  std::vector<IndexListTuple> CurrentSubsystems;
298  LOG(INFO) << "creating all Subsystems!";
299 
300  std::vector<IndexListTuple> AllSubsystems;
301  // add current subsystems
302  for (auto const &SubSys : Subsystems) {
303  AllSubsystems.push_back(
305  SubSys.first.getFinalStates()[0]),
307  SubSys.first.getFinalStates()[1]),
309  SubSys.first.getRecoilState()),
311  SubSys.first.getParentRecoilState())));
312  }
313 
315  unsigned int i = 0;
316  std::generate(FinalStateIDs.begin(), FinalStateIDs.end(),
317  [&i]() -> unsigned int { return i++; });
318  for (auto const &x : redistributeIndexLists(FinalStateIDs, IndexList())) {
319  CurrentSubsystems.push_back(
320  std::make_tuple(x.first, x.second, IndexList(), IndexList()));
321  }
322 
323  while (CurrentSubsystems.size() > 0) {
324  auto TempSubsystems(CurrentSubsystems);
325  CurrentSubsystems.clear();
326  for (auto const &x : TempSubsystems) {
327  IndexList CurrentA(std::get<0>(x));
328  IndexList CurrentB(std::get<1>(x));
329  IndexList CurrentRecoil(std::get<2>(x));
330  IndexList CurrentParentRecoil(std::get<3>(x));
331  auto SortedEntry = sortSubsystem(x);
332  auto result = std::find_if(
333  AllSubsystems.begin(), AllSubsystems.end(),
334  [&SortedEntry, this](const IndexListTuple &element) -> bool {
335  return sortSubsystem(element) == SortedEntry;
336  });
337  if (result == AllSubsystems.end()) {
338  AllSubsystems.push_back(SortedEntry);
339  }
340 
341  // create more combinations on this level
342  for (auto const &ABNew : redistributeIndexLists(CurrentA, CurrentB)) {
343  CurrentSubsystems.push_back(std::make_tuple(
344  ABNew.first, ABNew.second, CurrentRecoil, CurrentParentRecoil));
345  }
346  // Try to go a level deeper for A
347  if (CurrentA.size() > 1) {
348  for (auto const &ABNew :
349  redistributeIndexLists(CurrentA, IndexList())) {
350  // use current B as recoil
351  // and shift recoil to parent recoil
352  CurrentSubsystems.push_back(std::make_tuple(ABNew.first, ABNew.second,
353  CurrentB, CurrentRecoil));
354  }
355  }
356  // same for B
357  if (CurrentB.size() > 1) {
358  for (auto const &ABNew :
359  redistributeIndexLists(CurrentB, IndexList())) {
360  CurrentSubsystems.push_back(std::make_tuple(ABNew.first, ABNew.second,
361  CurrentA, CurrentRecoil));
362  }
363  }
364  }
365  }
366  for (auto const &x : AllSubsystems) {
367  registerSubSystem(std::get<0>(x), std::get<1>(x), std::get<2>(x),
368  std::get<3>(x));
369  }
370 }
371 
372 std::tuple<std::string, std::string, std::string>
374  // We calculate the variables currently for two-body decays
375  if (NewSys.getFinalStates().size() != 2) {
376  std::stringstream ss;
377  ss << "HelicityKinematics::registerSubSystem(const SubSystem "
378  "&subSys): Number of final state particles = "
379  << NewSys.getFinalStates().size() << ", which is != 2";
380  throw std::runtime_error(ss.str());
381  }
382 
383  IndexList FS1 = NewSys.getFinalStates().at(0);
384  IndexList FS2 = NewSys.getFinalStates().at(1);
385  FS1.insert(FS1.end(), FS2.begin(), FS2.end());
386 
387  auto MassName = registerInvariantMassSquared(FS1);
388  auto AngleNames = registerHelicityAngles(NewSys);
389 
390  return std::make_tuple(MassName, AngleNames.first, AngleNames.second);
391 }
392 
393 std::tuple<std::string, std::string, std::string>
395  const std::vector<unsigned int> &FinalA,
396  const std::vector<unsigned int> &FinalB,
397  const std::vector<unsigned int> &Recoil,
398  const std::vector<unsigned int> &ParentRecoil) {
399  std::vector<std::vector<unsigned int>> ConvertedFinalStates;
400  ConvertedFinalStates.push_back(
402 
403  ConvertedFinalStates.push_back(
405 
406  std::vector<unsigned int> ConvertedRecoil =
408  std::vector<unsigned int> ConvertedParentRecoil =
410 
411  return registerSubSystem(
412  SubSystem(ConvertedFinalStates, ConvertedRecoil, ConvertedParentRecoil));
413 }
414 
415 const std::pair<double, double> &HelicityKinematics::getInvariantMassBounds(
416  const std::string &InvariantMassName) const {
417  return InvMassBounds.at(InvariantMassName);
418 }
419 
421  const IndexList &FinalStateIDs) const {
424  std::pair<double, double> lim(0.0, 0.0);
425  // Sum up masses of all final state particles
426  lim.first = KinematicsInfo.calculateFinalStateIDMassSum(FinalStateIDs);
427 
429  double RemainderMass(0.0);
430  for (auto x : KinematicsInfo.getFinalStateMasses())
431  RemainderMass += x;
432  RemainderMass -= lim.first;
433 
434  lim.first *= lim.first;
435  // to improve precision: (M - m)^2 -> S - 2 sqrt(S) m + m^2 with S=M^2
436  lim.second =
437  S - 2 * std::sqrt(S) * RemainderMass + RemainderMass * RemainderMass;
438 
439  // extend the invariant mass interval by the numeric double precision
440  // otherwise quite a few events at the phase space boundary can be lost
441  // due to numerical imprecision of event generators
442  // (especially drastic effect, if gammas are in the final state)
443  lim.first -= std::numeric_limits<double>::epsilon() * lim.first;
444  lim.second += std::numeric_limits<double>::epsilon() * lim.second;
445  return lim;
446 }
447 
448 } // namespace HelicityFormalism
449 } // namespace Physics
450 } // namespace ComPWA
ComPWA::Data::DataSet convert(const EventCollection &Events) const final
Creates a DataSet from Events.
ComPWA four momentum class.
HelicityKinematics(ParticleStateTransitionKinematicsInfo KinInfo, double PhspVol)
Parameter not existing.
Definition: Exceptions.hpp:62
IndexListTuple sortSubsystem(const IndexListTuple &SubSys)
std::unordered_map< SubSystem, std::pair< std::string, std::string > > Subsystems
Mapping of subsystems to the corresponding helicity angle variable names (theta, phi) ...
EventCollection reduceToPhaseSpace(const EventCollection &Events) const final
Returns a subset of Events that are within phase space boundaries.
std::unordered_map< IndexList, std::string > InvariantMassesSquared
Mapping of final state particle index lists to invariant mass variable name.
virtual const std::vector< unsigned int > & getParentRecoilState() const
Definition: SubSystem.cpp:56
ComPWA::DataMap Data
Definition: DataSet.hpp:18
ComPWA exceptions.
double calculateFinalStateIDMassSum(const std::vector< unsigned int > ids) const
std::unordered_map< std::string, std::pair< double, double > > InvMassBounds
Invariant mass bounds for each SubSystem.
std::vector< std::pair< ComPWA::IndexList, ComPWA::IndexList > > redistributeIndexLists(const ComPWA::IndexList &A, const ComPWA::IndexList &B)
std::vector< double > Weights
Definition: DataSet.hpp:19
const std::pair< double, double > & getInvariantMassBounds(const std::string &InvariantMassName) const
Get phase space bounds for the registered invariant mass with name InvariantMassName.
virtual const std::vector< unsigned int > & getRecoilState() const
Definition: SubSystem.cpp:52
std::vector< unsigned int > IndexList
Definition: SubSystem.hpp:20
std::pair< std::string, std::string > registerHelicityAngles(SubSystem System)
std::vector< Event > Events
Definition: Event.hpp:34
Implementation of the ComPWA::Kinematics interface for amplitude models using the helicity formalism...
std::set< ParticleProperties > ParticleList
Definition: Properties.hpp:84
std::tuple< IndexList, IndexList, IndexList, IndexList > IndexListTuple
virtual const std::vector< std::vector< unsigned int > > & getFinalStates() const
Definition: SubSystem.cpp:48
std::tuple< std::string, std::string, std::string > registerSubSystem(const SubSystem &NewSys)
Add NewSys to list of SubSystems and return a tuple of names, that id the registered kinematic variab...
std::pair< double, double > calculateHelicityAngles(const Event &Event, const SubSystem &SubSys) const
Calculates the pair of values of the Event Event for SubSystem SubSys.
double Weight
Definition: Event.hpp:22
ParticleStateTransitionKinematicsInfo KinematicsInfo
std::vector< FourMomentum > FourMomenta
Definition: Event.hpp:21
std::pair< double, double > calculateInvMassBounds(const IndexList &FinalStateIDs) const
EventCollection generate(unsigned int NumberOfEvents, const ComPWA::Kinematics &Kinematics, const ComPWA::PhaseSpaceEventGenerator &Generator, ComPWA::Intensity &Intensity, ComPWA::UniformRealNumberGenerator &RandomGenerator)
Definition: Generate.cpp:95
Data structure containing all kinematic information of a physics event.
Definition: Event.hpp:20
std::vector< pid > Pids
Definition: Event.hpp:33
double calculateInvariantMassSquared(const Event &Event, const IndexList &FinalStateIDs) const
Calculates the squared invariant mass of list of final state particles FinalStateIDs.
double invariantMassSquared() const
bool checkPidMatchesEvents() const
Definition: Event.hpp:26
Definition of a two-body decay node within a sequential decay tree.
Definition: SubSystem.hpp:31