25 virtual unsigned NP()
const = 0;
32 enum Type { BW, Flat, Stable, QuasiStable };
33 Type type = Type::Flat;
36 std::pair<double, double> range;
37 std::pair<double, double> phi_range;
42 Node(Type type = Type::Flat,
const unsigned& l=0,
const unsigned& r=0) : type(type), l(l), r(r) {};
51 case Type::QuasiStable :
type_string =
"QuasiStable";
break;
53 INFO(
type_string <<
" decay indices: [" << l <<
", " << r <<
"], range : [" << range.first <<
", " << range.second <<
"] "
57 T operator()(
const T&
s )
const
61 return ( bwMass* bwWidth ) /( (phi_range.second-phi_range.first )* ( (
s - bwMass*bwMass)*(
s-bwMass*bwMass) + bwMass*bwMass*bwWidth*bwWidth) );
63 case Type::Flat :
return 1/(range.second - range.first);
64 case Type::Stable :
return 1;
65 case Type::QuasiStable :
return 1;
73 case Type::BW :
return bwMass * bwMass;
74 case Type::Flat :
return (range.second + range.first ) * 0.5;
75 case Type::Stable :
return range.first;
76 case Type::QuasiStable :
return range.first;
80 double operator()( TRandom3* random )
const
85 auto y = tan( ( phi_range.second - phi_range.first ) * random->Rndm() );
86 auto mG = bwMass * bwWidth;
87 auto s0 = bwMass * bwMass;
88 return s0 + mG * ( mG * y + range.first - s0 ) / ( mG - y * ( range.first - s0 ) );
91 return (range.second-range.first) * random->Rndm() + range.first;
94 case Type::QuasiStable:
101 auto props = particle->
props();
102 bwWidth = props->
width();
103 bwMass = props->mass();
105 if( particle->
isStable() ) type = Type::Stable;
106 else if( particle->
isQuasiStable() ) type = Type::QuasiStable;
107 else if( particle->
lineshapeContains({
"FormFactor",
"None",
"TD"}) ) type == Type::Flat;
110 phi_range.first = atan((range.first - bwMass*bwMass)/(bwMass*bwWidth));
111 phi_range.second = atan((range.second - bwMass*bwMass)/(bwMass*bwWidth));
114 void setRange(
const double& min,
const double& max )
116 if( type == Type::BW or type == Type::Flat )
118 range = std::make_pair(min, max);
119 if( type == Type::BW )
121 phi_range.first = atan((range.first - bwMass*bwMass)/(bwMass*bwWidth));
122 phi_range.second = atan((range.second - bwMass*bwMass)/(bwMass*bwWidth));
128 return type == other.type &&
129 range == other.range &&
130 phi_range == other.phi_range &&
131 bwMass == other.bwMass &&
132 bwWidth == other.bwWidth &&
139 std::array<double, N> m_m0;
140 std::array<Node, N-1> m_nodes;
148 double minMass = particle.
mass();
149 for(
int i = 0 ; i != N; ++i ){ m_m0[i] =
fs[i]->mass(); minMass -= m_m0[i]; }
150 std::stack<std::pair<std::shared_ptr<Particle>,
unsigned>> toDo;
151 toDo.emplace( std::make_shared<Particle>(particle), 0);
152 unsigned counter = 0;
153 while( toDo.size() != 0 )
155 auto [current,index] = toDo.top();
157 if( current->daughters().size() != 2 )
159 std::vector<Particle> particles;
160 auto dp = current->daughters();
161 for(
unsigned int i = 1; i != dp.size(); ++i ) particles.push_back( *dp[i] );
162 current = std::make_shared<Particle>( current->name(), *dp[0],
Particle(
"NonResS0",particles) );
164 INFO( index <<
" " << *current );
165 if( current->daughters().size() != 2 )
FATAL(
"Should be fixed..");
166 auto fs = current->quasiStableTree().daughters();
167 auto min_mass = std::accumulate(
fs.begin(),
fs.end(), 0., [](
double acc,
auto& p){ return acc + p->mass(); } );
168 auto G = current->isQuasiStable() ? 0 : current->props()->mass() * current->props()->width() * 10;
169 auto s0 = current->mass() * current->mass();
170 auto d1_index = current->daughter(0)->index();
171 auto d2_index = current->daughter(1)->index();
172 if( !current->daughter(0)->isStable() ){ d1_index = ++counter + N; toDo.emplace( current->daughter(0), d1_index-N); }
173 if( !current->daughter(1)->isStable() ){ d2_index = ++counter + N; toDo.emplace( current->daughter(1), d2_index-N); }
174 m_nodes[index] = Node(Node::Type::Flat, d1_index, d2_index);
175 if( index == 0 or current->isQuasiStable() ) m_nodes[index].range = std::make_pair(s0 - G, s0 + G );
176 else m_nodes[index].range = std::make_pair( pow(min_mass,2), pow(minMass + min_mass,2 ) );
177 m_nodes[index].set( &(*current) );
181 WARNING(
"Using aggressive optimisation of phase space, can cause problems for relative normalisation of different decay topologies");
186 for(
auto& n : m_nodes )
188 if( n.l > N && n.r > N ){
189 m_nodes[n.l-N].setRange( m_nodes[n.l-N].range.first, n.range.second + m_nodes[n.r-N].range.first - 2 * std::sqrt( n.range.second * m_nodes[n.r-N].range.first ) );
190 m_nodes[n.r-N].setRange( m_nodes[n.r-N].range.first, n.range.second + m_nodes[n.l-N].range.first - 2 * std::sqrt( n.range.second * m_nodes[n.l-N].range.first ) );
194 auto concat = [](
auto v1,
const auto& v2 ){ v1.insert( v1.end(), v2.begin(), v2.end() );
return v1; };
195 for(
auto n = m_nodes.rbegin(); n != m_nodes.rend(); ++n ){
196 auto s1 = n->l < N ? m_m0[n->l]*m_m0[n->l] : m_nodes[n->l - N].range.first;
197 auto s2 = n->r < N ? m_m0[n->r]*m_m0[n->r] : m_nodes[n->r - N].range.first;
199 auto rho =
rho_2(n->range.second, s1, s2 );
207 for(
auto& node : m_nodes ) node.print();
208 if( m_rhoMax <= 0 )
ERROR(
"RhoMax^2 < 0! " << m_rhoMax );
209 m_rhoMax = std::sqrt(m_rhoMax);
212 virtual unsigned NP()
const {
return N; }
216 return op && this->m_m0 == op->m_m0 && this->m_nodes == op->m_nodes;
229 template <
typename T>
233 for(
const auto& node : m_nodes ){
235 g *= node(
s(event, node.lfs, node.rfs));
239 template <
typename T>
244 std::array<T,4> P = {0.};
245 for(
const auto& it : lfs )
247 P[0] +=
event[4*it + 0 ];
248 P[1] +=
event[4*it + 1 ];
249 P[2] +=
event[4*it + 2 ];
250 P[3] +=
event[4*it + 3 ];
252 for(
const auto& it : rfs )
254 P[0] +=
event[4*it + 0 ];
255 P[1] +=
event[4*it + 1 ];
256 P[2] +=
event[4*it + 2 ];
257 P[3] +=
event[4*it + 3 ];
259 return P[3] * P[3] - P[0] * P[0] - P[1] * P[1] - P[2] * P[2];
262 double rho_2(
const double& s0,
const double& s1,
const double& s2 )
const
264 return 1 - 2 * ( s1 + s2 )/s0 + ( s1 - s2 )*( s1 - s2 ) / (s0*s0);
266 void boost(
double* output,
double nx,
double ny,
double nz,
double g,
double vg )
const
268 double nv = output[0] * nx + output[1] * ny + output[2] * nz;
269 output[0] += ( (g-1) * nv + vg * output[3] ) * nx ;
270 output[1] += ( (g-1) * nv + vg * output[3] ) * ny ;
271 output[2] += ( (g-1) * nv + vg * output[3] ) * nz ;
272 output[3] = g * output[3] + vg * nv;
274 void set(
double* output,
double px,
double py,
double pz,
double E)
const
282 std::pair<double, std::array<double, 2*N-1>>
proposal( TRandom3* rndm )
const
284 std::pair<double, std::array<double, 2*N-1>> rt;
285 auto& [weight, state ] = rt;
286 for(
unsigned int i = 0 ; i != N ; ++i ) state[i] = m_m0[i] * m_m0[i];
288 for(
int i = m_nodes.size()-1 ; i >= 0; --i ){
289 state[i + N] = m_nodes[i](rndm);
290 const auto&
s = state[i+N];
291 const auto& sl = state[m_nodes[i].l];
292 const auto& sr = state[m_nodes[i].r];
293 double v =
rho_2(
s, sl, sr) * (
s > sl + sr + 2 * std::sqrt(sl*sr) );
294 weight = v < 0 ? 0 : v * weight;
296 weight = std::sqrt( weight );
301 std::pair<double, std::array<double, 2*N-1>> rt;
302 auto& [weight, state ] = rt;
303 for(
unsigned int i = 0 ; i != N ; ++i ) state[i] = m_m0[i] * m_m0[i];
305 for(
int i = m_nodes.size()-1 ; i >= 0; --i ){
306 state[i + N] = m_nodes[i](rndm);
307 const auto&
s = state[i+N];
308 const auto& sl = state[m_nodes[i].l];
309 const auto& sr = state[m_nodes[i].r];
310 double v =
rho_2(
s, sl, sr) * (
s > sl + sr + 2 * std::sqrt(sl*sr) );
311 INFO(
"Node[" << i <<
"] w = " << v <<
"s[" << m_nodes[i].l <<
" " << m_nodes[i].r <<
"] = " <<
s <<
" [ sl= " << sl <<
" sr= " << sr <<
"]" );
312 weight = v < 0 ? 0 : v * weight;
314 weight = std::sqrt( weight );
317 void fill_from_state(
double* event,
const std::array<double, 2*N-1>& state, TRandom3* rndm )
const
319 for(
int i = 0 ; i != N; ++i ) event[4*i+3] = m_m0[i];
320 for(
auto n = m_nodes.rbegin(); n != m_nodes.rend(); ++n)
322 const auto& sl = state[n->l];
323 const auto& sr = state[n->r];
324 const auto&
s = state[ &(*n) - &(*m_nodes.begin()) + N ];
325 const auto nz = 2 * rndm->Rndm() - 1;
326 const auto phi = 2*M_PI*rndm->Rndm();
327 const auto p = 0.5 * std::sqrt(
s *
rho_2(
s, sl, sr ) );
328 const auto sZ = std::sqrt( 1 - nz*nz );
329 const auto nx = sZ * std::cos(
phi);
330 const auto ny = sZ * std::sin(
phi);
331 const auto gl = std::sqrt( 1 + p*p/sl);
332 const auto vgl = p / std::sqrt(sl);
333 const auto gr = std::sqrt( 1 + p*p/sr);
334 const auto vgr = p / std::sqrt(sr);
336 if( n->lfs.size == 1 )
set( event + 4 * n->lfs[0], p*nx, p*ny, p*nz, std::sqrt( sl + p*p ) );
337 else for(
const auto& l : n->lfs )
boost(event + 4 *l, nx , ny, nz, gl, vgl );
339 if( n->rfs.size == 1 )
set( event + 4 *n->rfs[0], -p*nx, -p*ny, -p*nz, std::sqrt( sr + p*p ) );
340 else for(
const auto& l : n->rfs )
boost(event + 4 *l, -nx, -ny, -nz, gr, vgr );
346 std::array<double, 2 * N - 1> state = {0};
350 rho_v = std::get<0>(rt);
351 state = std::get<1>(rt);
352 }
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
void set(double *output, double px, double py, double pz, double E) const
std::pair< double, std::array< double, 2 *N-1 > > proposal(TRandom3 *rndm) const
double rho_2(const double &s0, const double &s1, const double &s2) const
virtual bool operator==(const DecayChainStackBase &other) const
DecayChainStack(const Particle &particle)
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.
A parameter with value specified by the user at runtime, either in an options file or via the command...
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...
DecayChainStackBase * make_decay_chain_stack(const Particle &particle)
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...