AmpGen 2.1
Loading...
Searching...
No Matches
DecayChainStack.h
Go to the documentation of this file.
1#include <vector>
2#include <cmath>
3#include <algorithm>
4#include <numeric>
5#include <stack>
6
7#include "AmpGen/EventType.h"
8#include "AmpGen/Particle.h"
9#include "AmpGen/MsgService.h"
11#include "AmpGen/SmallVector.h"
12#include "AmpGen/Event.h"
13#include <TRandom3.h>
14
15namespace AmpGen {
16
18 {
19 public:
21 DecayChainStackBase( const Particle& particle );
23 virtual Event makeEvent(TRandom3* rndm) const = 0;
24 virtual double maxWeight() const = 0;
25 virtual unsigned NP() const = 0;
26 virtual bool operator==( const DecayChainStackBase& other ) const = 0;
27 };
28
29 template <unsigned N> class DecayChainStack : public DecayChainStackBase {
30
31 struct Node {
32 enum Type { BW, Flat, Stable, QuasiStable };
33 Type type = Type::Flat;
34 unsigned l;
35 unsigned r;
36 std::pair<double, double> range;
37 std::pair<double, double> phi_range;
38 double bwMass = 0;
39 double bwWidth = 0;
42 Node(Type type = Type::Flat, const unsigned& l=0, const unsigned& r=0) : type(type), l(l), r(r) {};
43 void print() const
44 {
45 std::string type_string = "";
46 switch( type )
47 {
48 case Type::BW : type_string = "BW "; break;
49 case Type::Flat : type_string = "Flat "; break;
50 case Type::Stable : type_string = "Stable "; break;
51 case Type::QuasiStable : type_string = "QuasiStable"; break;
52 };
53 INFO( type_string << " decay indices: [" << l << ", " << r << "], range : [" << range.first << ", " << range.second << "] "
54 ", [" << vectorToString( lfs, ",") << "], [" << vectorToString( rfs, ",") << "]" );
55 }
56 template <typename T>
57 T operator()( const T& s ) const
58 {
59 switch( type ) {
60 case Type::BW :
61 return ( bwMass* bwWidth ) /( (phi_range.second-phi_range.first )* ( (s - bwMass*bwMass)*(s-bwMass*bwMass) + bwMass*bwMass*bwWidth*bwWidth) );
62
63 case Type::Flat : return 1/(range.second - range.first);
64 case Type::Stable : return 1;
65 case Type::QuasiStable : return 1;
66 };
67 return 0;
68 }
69
70 double s_nom() const
71 {
72 switch( type ) {
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;
77 };
78 return 0;
79 }
80 double operator()( TRandom3* random ) const
81 {
82 switch( type ) {
83 case Type::BW :
84 {
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 ) );
89 }
90 case Type::Flat :
91 return (range.second-range.first) * random->Rndm() + range.first;
92 case Type::Stable:
93 return range.first;
94 case Type::QuasiStable:
95 return range.first;
96 }
97 return 0;
98 }
99 void set( const Particle* particle )
100 {
101 auto props = particle->props();
102 bwWidth = props->width();
103 bwMass = props->mass();
104 INFO("Setting particle: " << particle->name() << " " << particle->isStable() << " " << particle->isQuasiStable() << " " << particle->lineshapeContains({"FormFactor", "None","TD"}) );
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;
108 else {
109 type = Type::BW;
110 phi_range.first = atan((range.first - bwMass*bwMass)/(bwMass*bwWidth));
111 phi_range.second = atan((range.second - bwMass*bwMass)/(bwMass*bwWidth));
112 }
113 }
114 void setRange( const double& min, const double& max )
115 {
116 if( type == Type::BW or type == Type::Flat )
117 {
118 range = std::make_pair(min, max);
119 if( type == Type::BW )
120 {
121 phi_range.first = atan((range.first - bwMass*bwMass)/(bwMass*bwWidth));
122 phi_range.second = atan((range.second - bwMass*bwMass)/(bwMass*bwWidth));
123 }
124 }
125 }
126 bool operator==( const Node& other ) const
127 {
128 return type == other.type &&
129 range == other.range &&
130 phi_range == other.phi_range &&
131 bwMass == other.bwMass &&
132 bwWidth == other.bwWidth &&
133 l == other.l &&
134 r == other.r &&
135 lfs == other.lfs &&
136 rfs == other.rfs;
137 }
138 };
139 std::array<double, N> m_m0;
140 std::array<Node, N-1> m_nodes;
141 double m_rhoMax=1;
142
143 public:
144
145 DecayChainStack( const Particle& particle )
146 {
147 auto fs = particle.getFinalStateParticles();
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 )
154 {
155 auto [current,index] = toDo.top();
156 toDo.pop();
157 if( current->daughters().size() != 2 )
158 {
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) );
163 }
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) );
178 }
179 if( NamedParameter<bool>("DecayChainStack::AggressiveOptimisation", false ) )
180 {
181 WARNING("Using aggressive optimisation of phase space, can cause problems for relative normalisation of different decay topologies");
186 for( auto& n : m_nodes )
187 {
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 ) );
191 }
192 }
193 }
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;
198
199 auto rho = rho_2(n->range.second, s1, s2 );
200 // INFO(m_nodes.size() - ( n-m_nodes.rbegin()) + N -1 << " " << rho << " " << n->l << " " << n->r << " " << n->range.first << " " << n->range.second << " " << n->type <<
201 // " s(max): " << n->range.second <<
202 // " s1(min): " << s1 << " s2(min): " << s2 );
203 m_rhoMax *= rho;
204 n->lfs = n->l < N ? SmallVector<unsigned, N>{n->l} : concat( m_nodes[n->l - N].lfs, m_nodes[n->l - N].rfs);
205 n->rfs = n->r < N ? SmallVector<unsigned, N>{n->r} : concat( m_nodes[n->r - N].lfs, m_nodes[n->r - N].rfs);
206 }
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);
210 }
211 virtual double maxWeight() const { return m_rhoMax; }
212 virtual unsigned NP() const { return N; }
213 virtual bool operator==( const DecayChainStackBase& other ) const
214 {
215 auto op = dynamic_cast<const DecayChainStack<N>*>( &other );
216 return op && this->m_m0 == op->m_m0 && this->m_nodes == op->m_nodes;
217 }
218 /*
219 double genPdf(const double* event) const
220 {
221 double g = 1;
222 for( const auto& node : m_nodes ){
223 //INFO( "s(" << vectorToString( node.lfs, ",") << "," << vectorToString(node.rfs,",") << ") = " << s(event, node.lfs, node.rfs) );
224 g *= node(s(event, node.lfs, node.rfs));
225 }
226 return g;
227 }
228 */
229 template <typename T>
230 T genPdf(const T* event) const
231 {
232 T g = 1;
233 for( const auto& node : m_nodes ){
234 //INFO( "s(" << vectorToString( node.lfs, ",") << "," << vectorToString(node.rfs,",") << ") = " << s(event, node.lfs, node.rfs) );
235 g *= node(s(event, node.lfs, node.rfs));
236 }
237 return g;
238 }
239 template <typename T>
240 T s(const T* event,
241 const SmallVector<unsigned, N>& lfs,
242 const SmallVector<unsigned, N>& rfs) const
243 {
244 std::array<T,4> P = {0.};
245 for( const auto& it : lfs )
246 {
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 ];
251 }
252 for( const auto& it : rfs )
253 {
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 ];
258 }
259 return P[3] * P[3] - P[0] * P[0] - P[1] * P[1] - P[2] * P[2];
260 }
261
262 double rho_2( const double& s0, const double& s1, const double& s2 ) const
263 {
264 return 1 - 2 * ( s1 + s2 )/s0 + ( s1 - s2 )*( s1 - s2 ) / (s0*s0);
265 }
266 void boost( double* output, double nx, double ny, double nz, double g, double vg ) const
267 {
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;
273 }
274 void set( double* output, double px, double py, double pz, double E) const
275 {
276 output[0] = px;
277 output[1] = py;
278 output[2] = pz;
279 output[3] = E;
280 }
281
282 std::pair<double, std::array<double, 2*N-1>> proposal( TRandom3* rndm ) const
283 {
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];
287 weight = 1;
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;
295 }
296 weight = std::sqrt( weight );
297 return rt;
298 }
299 void debug( TRandom3* rndm ) const
300 {
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];
304 weight = 1;
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;
313 }
314 weight = std::sqrt( weight );
315 }
316
317 void fill_from_state( double* event, const std::array<double, 2*N-1>& state, TRandom3* rndm ) const
318 {
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)
321 {
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);
335
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 );
338
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 );
341 }
342 }
343
344 virtual AmpGen::Event makeEvent(TRandom3* rndm) const
345 {
346 std::array<double, 2 * N - 1> state = {0};
347 double rho_v = 1 ;
348 do {
349 auto rt = proposal( rndm );
350 rho_v = std::get<0>(rt);
351 state = std::get<1>(rt);
352 } while( rho_v < m_rhoMax * rndm->Uniform() );
353 AmpGen::Event event( N * 4 );
354 fill_from_state( event, state, rndm );
355 return event;
356 }
357 };
358
360 {
361 auto fs = particle.getFinalStateParticles();
362 switch ( fs.size() )
363 {
364 case(1 ) : return new DecayChainStack<1>( particle );
365 case(2 ) : return new DecayChainStack<2>( particle );
366 case(3 ) : return new DecayChainStack<3>( particle );
367 case(4 ) : return new DecayChainStack<4>( particle );
368 case(5 ) : return new DecayChainStack<5>( particle );
369 case(6 ) : return new DecayChainStack<6>( particle );
370 case(7 ) : return new DecayChainStack<7>( particle );
371 case(8 ) : return new DecayChainStack<8>( particle );
372 case(9 ) : return new DecayChainStack<9>( particle );
373 case(10) : return new DecayChainStack<10>( particle );
374 }
375 return nullptr;
376 }
377
378}
virtual unsigned NP() const =0
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.
Definition Event.h:18
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.
Definition Particle.h:103
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.
Definition Particle.h:271
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.
Definition MsgService.h:80
#define INFO(X)
Used for printing information messages, and will always be printed.
Definition MsgService.h:75
#define WARNING(X)
Used for printing warning messages, can be switched off using WARNINGLEVEL.
Definition MsgService.h:97
#define FATAL(X)
Used for printing fatal errors messages, and will always be printed and will terminate the process af...
Definition MsgService.h:87
DecayChainStackBase * make_decay_chain_stack(const Particle &particle)
std::string vectorToString(iterator_type begin, iterator_type end, const std::string &delim, functor_type fcn)
Definition Utilities.h:25
double phi(const Event &evt, int i, int j, int k, int w)
static const double fs
Definition Units.h:17
std::string type_string()
Utility classes for compile-time metaprogramming, such as identifying the types of arguments for gene...
Definition MetaUtils.h:18