23 virtual unsigned NP()
const = 0;
29 enum Type { BW, Flat, Stable, QuasiStable };
30 Type type = Type::Flat;
33 std::pair<double, double> range;
34 std::pair<double, double> phi_range;
39 Node(Type type = Type::Flat,
const unsigned &l = 0,
const unsigned &r = 0) : type(type), l(l), r(r) {};
46 case Type::QuasiStable:
type_string =
"QuasiStable";
break;
48 INFO(
type_string <<
" decay indices: [" << l <<
", " << r <<
"], range : [" << range.first <<
", " << range.second
53 template <
typename T> T operator()(
const T &
s)
const {
56 return (bwMass * bwWidth)
57 / ((phi_range.second - phi_range.first) * ((
s - bwMass * bwMass) * (
s - bwMass * bwMass) + bwMass * bwMass * bwWidth * bwWidth));
59 case Type::Flat:
return 1 / (range.second - range.first);
60 case Type::Stable:
return 1;
61 case Type::QuasiStable:
return 1;
66 double s_nom()
const {
68 case Type::BW:
return bwMass * bwMass;
69 case Type::Flat:
return (range.second + range.first) * 0.5;
70 case Type::Stable:
return range.first;
71 case Type::QuasiStable:
return range.first;
75 double operator()(TRandom3 *random)
const {
78 auto y = tan((phi_range.second - phi_range.first) * random->Rndm());
79 auto mG = bwMass * bwWidth;
80 auto s0 = bwMass * bwMass;
81 return s0 + mG * (mG * y + range.first - s0) / (mG - y * (range.first - s0));
83 case Type::Flat:
return (range.second - range.first) * random->Rndm() + range.first;
84 case Type::Stable:
return range.first;
85 case Type::QuasiStable:
return range.first;
90 auto props = particle->
props();
91 bwWidth = props->
width();
92 bwMass = props->mass();
98 type = Type::QuasiStable;
103 phi_range.first = atan((range.first - bwMass * bwMass) / (bwMass * bwWidth));
104 phi_range.second = atan((range.second - bwMass * bwMass) / (bwMass * bwWidth));
107 void setRange(
const double &min,
const double &max) {
108 if(type == Type::BW or type == Type::Flat) {
109 range = std::make_pair(min, max);
110 if(type == Type::BW) {
111 phi_range.first = atan((range.first - bwMass * bwMass) / (bwMass * bwWidth));
112 phi_range.second = atan((range.second - bwMass * bwMass) / (bwMass * bwWidth));
117 return type == other.type && range == other.range && phi_range == other.phi_range && bwMass == other.bwMass && bwWidth == other.bwWidth && l == other.l
118 && r == other.r && lfs == other.lfs && rfs == other.rfs;
121 std::array<double, N> m_m0;
122 std::array<Node, N - 1> m_nodes;
128 double minMass = particle.
mass();
129 for(
int i = 0; i != N; ++i) {
130 m_m0[i] =
fs[i]->mass();
133 std::stack<std::pair<std::shared_ptr<Particle>,
unsigned>> toDo;
134 toDo.emplace(std::make_shared<Particle>(particle), 0);
135 unsigned counter = 0;
136 while(toDo.size() != 0) {
137 auto [current, index] = toDo.top();
139 if(current->daughters().size() != 2) {
140 std::vector<Particle> particles;
141 auto dp = current->daughters();
142 for(
unsigned int i = 1; i != dp.size(); ++i) particles.push_back(*dp[i]);
143 current = std::make_shared<Particle>(current->name(), *dp[0],
Particle(
"NonResS0", particles));
145 INFO(index <<
" " << *current);
146 if(current->daughters().size() != 2)
FATAL(
"Should be fixed..");
147 auto fs = current->quasiStableTree().daughters();
148 auto min_mass = std::accumulate(
fs.begin(),
fs.end(), 0., [](
double acc,
auto &p) { return acc + p->mass(); });
149 auto G = current->isQuasiStable() ? 0 : current->props()->mass() * current->props()->width() * 10;
150 auto s0 = current->mass() * current->mass();
151 auto d1_index = current->daughter(0)->index();
152 auto d2_index = current->daughter(1)->index();
153 if(!current->daughter(0)->isStable()) {
154 d1_index = ++counter + N;
155 toDo.emplace(current->daughter(0), d1_index - N);
157 if(!current->daughter(1)->isStable()) {
158 d2_index = ++counter + N;
159 toDo.emplace(current->daughter(1), d2_index - N);
161 m_nodes[index] = Node(Node::Type::Flat, d1_index, d2_index);
162 if(index == 0 or current->isQuasiStable())
163 m_nodes[index].range = std::make_pair(s0 - G, s0 + G);
165 m_nodes[index].range = std::make_pair(pow(min_mass, 2), pow(minMass + min_mass, 2));
166 m_nodes[index].set(&(*current));
168 if(aggressiveOptimisation) {
169 WARNING(
"Using aggressive optimisation of phase space, can cause problems for relative normalisation of different decay topologies");
174 for(
auto &n : m_nodes) {
175 if(n.l > N && n.r > N) {
176 m_nodes[n.l - N].setRange(m_nodes[n.l - N].range.first,
177 n.range.second + m_nodes[n.r - N].range.first - 2 * std::sqrt(n.range.second * m_nodes[n.r - N].range.first));
178 m_nodes[n.r - N].setRange(m_nodes[n.r - N].range.first,
179 n.range.second + m_nodes[n.l - N].range.first - 2 * std::sqrt(n.range.second * m_nodes[n.l - N].range.first));
183 auto concat = [](
auto v1,
const auto &v2) {
184 v1.insert(v1.end(), v2.begin(), v2.end());
187 for(
auto n = m_nodes.rbegin(); n != m_nodes.rend(); ++n) {
188 auto s1 = n->l < N ? m_m0[n->l] * m_m0[n->l] : m_nodes[n->l - N].range.first;
189 auto s2 = n->r < N ? m_m0[n->r] * m_m0[n->r] : m_nodes[n->r - N].range.first;
191 auto rho =
rho_2(n->range.second, s1, s2);
200 for(
auto &node : m_nodes) node.print();
201 if(m_rhoMax <= 0)
ERROR(
"RhoMax^2 < 0! " << m_rhoMax);
202 m_rhoMax = std::sqrt(m_rhoMax);
205 virtual unsigned NP()
const {
return N; }
208 return op && this->m_m0 == op->m_m0 && this->m_nodes == op->m_nodes;
221 template <
typename T> T
genPdf(
const T *event)
const {
223 for(
const auto &node : m_nodes) {
225 g *= node(
s(event, node.lfs, node.rfs));
230 std::array<T, 4> P = {0.};
231 for(
const auto &it : lfs) {
232 P[0] +=
event[4 * it + 0];
233 P[1] +=
event[4 * it + 1];
234 P[2] +=
event[4 * it + 2];
235 P[3] +=
event[4 * it + 3];
237 for(
const auto &it : rfs) {
238 P[0] +=
event[4 * it + 0];
239 P[1] +=
event[4 * it + 1];
240 P[2] +=
event[4 * it + 2];
241 P[3] +=
event[4 * it + 3];
243 return P[3] * P[3] - P[0] * P[0] - P[1] * P[1] - P[2] * P[2];
246 double rho_2(
const double &s0,
const double &s1,
const double &s2)
const {
return 1 - 2 * (s1 + s2) / s0 + (s1 - s2) * (s1 - s2) / (s0 * s0); }
247 void boost(
double *output,
double nx,
double ny,
double nz,
double g,
double vg)
const {
248 double nv = output[0] * nx + output[1] * ny + output[2] * nz;
249 output[0] += ((g - 1) * nv + vg * output[3]) * nx;
250 output[1] += ((g - 1) * nv + vg * output[3]) * ny;
251 output[2] += ((g - 1) * nv + vg * output[3]) * nz;
252 output[3] = g * output[3] + vg * nv;
254 void set(
double *output,
double px,
double py,
double pz,
double E)
const {
261 std::pair<double, std::array<double, 2 * N - 1>>
proposal(TRandom3 *rndm)
const {
262 std::pair<double, std::array<double, 2 * N - 1>> rt;
263 auto &[weight, state] = rt;
264 for(
unsigned int i = 0; i != N; ++i) state[i] = m_m0[i] * m_m0[i];
266 for(
int i = m_nodes.size() - 1; i >= 0; --i) {
267 state[i + N] = m_nodes[i](rndm);
268 const auto &
s = state[i + N];
269 const auto &sl = state[m_nodes[i].l];
270 const auto &sr = state[m_nodes[i].r];
271 double v =
rho_2(
s, sl, sr) * (
s > sl + sr + 2 * std::sqrt(sl * sr));
272 weight = v < 0 ? 0 : v * weight;
274 weight = std::sqrt(weight);
278 std::pair<double, std::array<double, 2 * N - 1>> rt;
279 auto &[weight, state] = rt;
280 for(
unsigned int i = 0; i != N; ++i) state[i] = m_m0[i] * m_m0[i];
282 for(
int i = m_nodes.size() - 1; i >= 0; --i) {
283 state[i + N] = m_nodes[i](rndm);
284 const auto &
s = state[i + N];
285 const auto &sl = state[m_nodes[i].l];
286 const auto &sr = state[m_nodes[i].r];
287 double v =
rho_2(
s, sl, sr) * (
s > sl + sr + 2 * std::sqrt(sl * sr));
288 INFO(
"Node[" << i <<
"] w = " << v <<
"s[" << m_nodes[i].l <<
" " << m_nodes[i].r <<
"] = " <<
s <<
" [ sl= " << sl <<
" sr= " << sr <<
"]");
289 weight = v < 0 ? 0 : v * weight;
291 weight = std::sqrt(weight);
294 void fill_from_state(
double *event,
const std::array<double, 2 * N - 1> &state, TRandom3 *rndm)
const {
295 for(
int i = 0; i != N; ++i) event[4 * i + 3] = m_m0[i];
296 for(
auto n = m_nodes.rbegin(); n != m_nodes.rend(); ++n) {
297 const auto &sl = state[n->l];
298 const auto &sr = state[n->r];
299 const auto &
s = state[&(*n) - &(*m_nodes.begin()) + N];
300 const auto nz = 2 * rndm->Rndm() - 1;
301 const auto phi = 2 * M_PI * rndm->Rndm();
302 const auto p = 0.5 * std::sqrt(
s *
rho_2(
s, sl, sr));
303 const auto sZ = std::sqrt(1 - nz * nz);
304 const auto nx = sZ * std::cos(
phi);
305 const auto ny = sZ * std::sin(
phi);
306 const auto gl = std::sqrt(1 + p * p / sl);
307 const auto vgl = p / std::sqrt(sl);
308 const auto gr = std::sqrt(1 + p * p / sr);
309 const auto vgr = p / std::sqrt(sr);
312 set(event + 4 * n->lfs[0], p * nx, p * ny, p * nz, std::sqrt(sl + p * p));
314 for(
const auto &l : n->lfs)
boost(event + 4 * l, nx, ny, nz, gl, vgl);
317 set(event + 4 * n->rfs[0], -p * nx, -p * ny, -p * nz, std::sqrt(sr + p * p));
319 for(
const auto &l : n->rfs)
boost(event + 4 * l, -nx, -ny, -nz, gr, vgr);
324 std::array<double, 2 * N - 1> state = {0};
328 rho_v = std::get<0>(rt);
329 state = std::get<1>(rt);
330 }
while(rho_v < m_rhoMax * rndm->Uniform());
virtual unsigned NP() const =0
DecayChainStackBase()=default
virtual ~DecayChainStackBase()
virtual Event makeEvent(TRandom3 *rndm) const =0
DecayChainStackBase(const Particle &particle)
virtual bool operator==(const DecayChainStackBase &other) const =0
virtual double maxWeight() const =0
void debug(TRandom3 *rndm) const
std::pair< double, std::array< double, 2 *N - 1 > > proposal(TRandom3 *rndm) const
DecayChainStack(const Particle &particle, bool aggressiveOptimisation)
void set(double *output, double px, double py, double pz, double E) const
double rho_2(const double &s0, const double &s1, const double &s2) const
virtual bool operator==(const DecayChainStackBase &other) const
void boost(double *output, double nx, double ny, double nz, double g, double vg) const
virtual AmpGen::Event makeEvent(TRandom3 *rndm) const
T genPdf(const T *event) const
T s(const T *event, const SmallVector< unsigned, N > &lfs, const SmallVector< unsigned, N > &rfs) const
virtual double maxWeight() const
virtual unsigned NP() const
void fill_from_state(double *event, const std::array< double, 2 *N - 1 > &state, TRandom3 *rndm) const
Encapsulates the final state particles of a single event.
Describes a particle, its decay process and subsequent decay products, which are also Particles.
std::vector< std::shared_ptr< Particle > > getFinalStateParticles(const bool &sort=true) const
Returns the final state particles for this decay process.
const ParticleProperties * props() const
Return the particleProperties object for this particle.
bool isQuasiStable() const
Check whether the particle is quasi-stable, i.e. may have some appreciable flight distance.
bool lineshapeContains(const std::vector< std::string > &container) const
Check if lineshape contains a substring.
std::string name() const
Name of the decaying particle.
bool isStable() const
Check whether this particle is stable, has any decay products.
double mass() const
Returns the (PDG) mass of the particle.
double width() const
Returns width of particle in MeV.
#define ERROR(X)
Used for printing errors messages, and will always be printed.
#define INFO(X)
Used for printing information messages, and will always be printed.
#define WARNING(X)
Used for printing warning messages, can be switched off using WARNINGLEVEL.
#define FATAL(X)
Used for printing fatal errors messages, and will always be printed and will terminate the process af...
std::string vectorToString(iterator_type begin, iterator_type end, const std::string &delim, functor_type fcn)
double phi(const Event &evt, int i, int j, int k, int w)
std::string type_string()
Utility classes for compile-time metaprogramming, such as identifying the types of arguments for gene...
DecayChainStackBase * make_decay_chain_stack(const Particle &particle, bool aggressiveOptimisation=false)