diff --git a/Cards/Config/Hadronisation/pythia6_cff.py b/Cards/Config/Hadronisation/pythia6_cff.py index bd5e419..1f37834 100644 --- a/Cards/Config/Hadronisation/pythia6_cff.py +++ b/Cards/Config/Hadronisation/pythia6_cff.py @@ -1,13 +1,16 @@ from Config.containers_cfi import Module, Parameters pythia6 = Module('pythia6', seed = 1000, maxTrials = 1, preConfiguration = ( 'MSTU(21)=1', ), + tuningSettings = ( + 'MSEL=0', # user-defined process + ), processConfiguration = ( - #'commonSettings', 'tuningSettings', + 'tuningSettings', ), ) diff --git a/CepGen/Cards/PythonTypes.cpp b/CepGen/Cards/PythonTypes.cpp index c4f07fa..07d204b 100644 --- a/CepGen/Cards/PythonTypes.cpp +++ b/CepGen/Cards/PythonTypes.cpp @@ -1,246 +1,231 @@ #include "CepGen/Cards/PythonHandler.h" #include "CepGen/Core/ParametersList.h" #include "CepGen/Core/Exception.h" #include "CepGen/Core/utils.h" #if PY_MAJOR_VERSION < 3 # define PYTHON2 #endif namespace cepgen { namespace card { //------------------------------------------------------------------ // typed retrieval helpers //------------------------------------------------------------------ template<> bool PythonHandler::is<int>( PyObject* obj ) const { if ( !obj ) throwPythonError( "Failed to retrieve integer object!" ); #ifdef PYTHON2 return PyInt_Check( obj ); #else return PyLong_Check( obj ); #endif } template<> bool PythonHandler::is<bool>( PyObject* obj ) const { if ( !obj ) throwPythonError( "Failed to retrieve boolean object!" ); return PyBool_Check( obj ); } template<> bool PythonHandler::is<long>( PyObject* obj ) const { #ifdef PYTHON2 return PyInt_Check( obj ) || PyLong_Check( obj ); #else return PyLong_Check( obj ); #endif } template<> int PythonHandler::get<int>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve integer object!" ); if ( !is<int>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: integer \"" << obj->ob_type->tp_name << "\"."; #ifdef PYTHON2 return PyInt_AsLong( obj ); #else return PyLong_AsLong( obj ); #endif } template<> unsigned long PythonHandler::get<unsigned long>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve unsigned long object!" ); if ( !is<long>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: unsigned long \"" << obj->ob_type->tp_name << "\"."; #ifdef PYTHON2 return PyInt_AsUnsignedLongMask( obj ); #else if ( !PyLong_Check( obj ) ) throwPythonError( Form( "Object \"%s\" has invalid type: unsigned long != %s", key, obj->ob_type->tp_name ) ); return PyLong_AsUnsignedLong( obj ); #endif } template<> long long PythonHandler::get<long long>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve long long object!" ); if ( !is<long>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: long long != \"" << obj->ob_type->tp_name << "\"."; return PyLong_AsLongLong( obj ); } template<> bool PythonHandler::is<double>( PyObject* obj ) const { if ( !obj ) throwPythonError( "Failed to retrieve float object!" ); return PyFloat_Check( obj ); } template<> double PythonHandler::get<double>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve float object!" ); + CG_WARNING(""); if ( !is<double>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: double != \"" << obj->ob_type->tp_name << "\"."; return PyFloat_AsDouble( obj ); } template<> bool PythonHandler::is<std::string>( PyObject* obj ) const { if ( !obj ) throwPythonError( "Failed to retrieve string object!" ); #ifdef PYTHON2 return PyString_Check( obj ); #else return PyUnicode_Check( obj ); #endif } template<> std::string PythonHandler::get<std::string>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve string object!" ); if ( !is<std::string>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: string != \"" << obj->ob_type->tp_name << "\"."; #ifdef PYTHON2 const std::string out = PyString_AsString( obj ); // deprecated in python v3+ #else PyObject* pstr = PyUnicode_AsEncodedString( obj, "utf-8", "strict" ); // new if ( !pstr ) throwPythonError( "Failed to decode a Python object!" ); const std::string out = PyBytes_AS_STRING( pstr ); Py_CLEAR( pstr ); #endif return out; } template<> bool PythonHandler::is<Limits>( PyObject* obj ) const { if ( !obj ) throwPythonError( "Failed to retrieve limits object!" ); if ( !isVector<double>( obj ) ) return false; const size_t size = getVector<double>( obj ).size(); return ( size == 1 || size == 2 ); } template<> Limits PythonHandler::get<Limits>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve limits object!" ); if ( !is<Limits>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: limits != \"" << obj->ob_type->tp_name << "\"."; const auto vec = getVector<double>( obj ); if ( vec.size() == 1 ) return Limits{ vec.at( 0 ) }; return Limits{ vec.at( 0 ), vec.at( 1 ) }; } template<> bool PythonHandler::is<ParametersList>( PyObject* obj ) const { if ( !obj ) throwPythonError( "Failed to retrieve parameters list object!" ); return PyDict_Check( obj ); } template<> ParametersList PythonHandler::get<ParametersList>( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve parameters list object!" ); if ( !is<ParametersList>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: parameters list != \"" << obj->ob_type->tp_name << "\"."; ParametersList out; PyObject* pkey = nullptr, *pvalue = nullptr; Py_ssize_t pos = 0; while ( PyDict_Next( obj, &pos, &pkey, &pvalue ) ) { const std::string skey = is<std::string>( pkey ) ? get<std::string>( pkey ) : is<int>( pkey ) ? std::to_string( get<int>( pkey ) ) // integer-type key : "invalid"; if ( is<int>( pvalue ) ) out.set<int>( skey, get<int>( pvalue ) ); else if ( is<double>( pvalue ) ) out.set<double>( skey, get<double>( pvalue ) ); else if ( is<std::string>( pvalue ) ) out.set<std::string>( skey, get<std::string>( pvalue ) ); else if ( is<ParametersList>( pvalue ) ) out.set<ParametersList>( skey, get<ParametersList>( pvalue ) ); else if ( is<Limits>( pvalue ) ) out.set<Limits>( skey, get<Limits>( pvalue ) ); else if ( PyTuple_Check( pvalue ) || PyList_Check( pvalue ) ) { // vector if ( isVector<int>( pvalue ) ) out.set<std::vector<int> >( skey, getVector<int>( pvalue ) ); else if ( isVector<double>( pvalue ) ) out.set<std::vector<double> >( skey, getVector<double>( pvalue ) ); else if ( isVector<std::string>( pvalue ) ) out.set<std::vector<std::string> >( skey, getVector<std::string>( pvalue ) ); else if ( isVector<ParametersList>( pvalue ) ) out.set<std::vector<ParametersList> >( skey, getVector<ParametersList>( pvalue ) ); } else throwPythonError( "Invalid object retrieved as parameters list value!" ); } return out; } template<typename T> bool PythonHandler::isVector( PyObject* obj ) const { if ( !obj ) return false; if ( !PyTuple_Check( obj ) && !PyList_Check( obj ) ) return false; PyObject* pfirst = PyTuple_Check( obj ) ? PyTuple_GetItem( obj, 0 ) : PyList_GetItem( obj, 0 ); if ( !is<T>( pfirst ) ) return false; return true; } template<typename T> std::vector<T> PythonHandler::getVector( PyObject* obj ) const { - if ( !obj ) - throwPythonError( "Failed to retrieve list/tuple object!" ); if ( !isVector<T>( obj ) ) throw CG_ERROR( "PythonHandler:get" ) << "Object has invalid type: list/tuple != \"" << obj->ob_type->tp_name << "\"."; std::vector<T> vec; const bool tuple = PyTuple_Check( obj ); const Py_ssize_t num_entries = tuple ? PyTuple_Size( obj ) : PyList_Size( obj ); //--- check every single element inside the list/tuple for ( Py_ssize_t i = 0; i < num_entries; ++i ) { PyObject* pit = tuple ? PyTuple_GetItem( obj, i ) : PyList_GetItem( obj, i ); if ( !is<T>( pit ) ) throwPythonError( "Mixed types detected in vector" ); vec.emplace_back( get<T>( pit ) ); } return vec; } } } diff --git a/CepGen/Hadronisers/GenericHadroniser.cpp b/CepGen/Hadronisers/GenericHadroniser.cpp index 9781bca..4ea2614 100644 --- a/CepGen/Hadronisers/GenericHadroniser.cpp +++ b/CepGen/Hadronisers/GenericHadroniser.cpp @@ -1,53 +1,53 @@ #include "CepGen/Hadronisers/GenericHadroniser.h" #include "CepGen/Core/Exception.h" #include "CepGen/Core/ParametersList.h" namespace cepgen { namespace hadr { GenericHadroniser::GenericHadroniser( const ParametersList& plist, const std::string& name ) : name_( name ), seed_ ( plist.get<int>( "seed", -1ll ) ), max_trials_( plist.get<int>( "maxTrials", 1 ) ) { CG_DEBUG( "Hadroniser:init" ) << "\"" << name_ << "\"-type hadroniser built with:\n\t" << "* seed = " << seed_ << "\n\t" << "* maximum trials: " << max_trials_; } void GenericHadroniser::readStrings( const std::vector<std::string>& params ) { if ( params.empty() ) return; std::ostringstream os; for ( const auto& p : params ) { readString( p ); os << "\n\t '" << p << "'"; } CG_DEBUG( "Hadroniser:configure" ) << "Feeding \"" << name_ << "\" hadroniser with:" << os.str(); } std::string GenericHadroniser::name() const { return name_; } } std::ostream& operator<<( std::ostream& os, const hadr::GenericHadroniser& hadr ) { - return os << hadr.name().c_str(); + return os << hadr.name(); } std::ostream& operator<<( std::ostream& os, const hadr::GenericHadroniser* hadr ) { - return os << hadr->name().c_str(); + return os << hadr->name(); } } diff --git a/CepGen/Hadronisers/Pythia6Hadroniser.cpp b/CepGen/Hadronisers/Pythia6Hadroniser.cpp index 16cfd44..4a6d035 100644 --- a/CepGen/Hadronisers/Pythia6Hadroniser.cpp +++ b/CepGen/Hadronisers/Pythia6Hadroniser.cpp @@ -1,341 +1,335 @@ #include "CepGen/Hadronisers/GenericHadroniser.h" #include "CepGen/Hadronisers/HadronisersHandler.h" #include "CepGen/Event/Event.h" #include "CepGen/Event/Particle.h" #include "CepGen/Physics/PDG.h" #include "CepGen/Core/ParametersList.h" //FIXME #include "CepGen/Core/Exception.h" #include "CepGen/Core/utils.h" #include <algorithm> extern "C" { /// Get the particle's mass in GeV from the Pythia6 module extern double pymass_( int& ); /// Get the resonant particle's width in GeV from the Pythia6 module //extern double pywidt_( int& ); /// Launch the Pythia6 fragmentation extern void pyexec_(); /// Set a parameter value to the Pythia6 module extern void pygive_( const char*, int ); extern void pyckbd_(); /// List all the particles in the event in a human-readable format extern void pylist_( int& ); /// Join two coloured particles in a colour singlet extern void pyjoin_( int&, int& ); /// Get a particle's human-readable name from the Pythia6 module extern void pyname_( int&, char*, int ); /// Get integer-valued event information from the Pythia6 module extern int pyk_( int&, int& ); /// Get real-valued event information from the Pythia6 module extern double pyp_( int&, int& ); /// Purely virtual method to call at the end of the run void pystop_() { CG_INFO( "Pythia6Hadroniser" ) << "End of run"; } /// Particles content of the event extern struct { /// Number of particles in the event int n; int npad; /// Particles' general information (status, PDG id, mother, daughter 1, daughter 2) int k[5][4000]; /// Particles' kinematics, in GeV (px, py, pz, E, M) double p[5][4000]; /// Primary vertex for the particles double v[5][4000]; } pyjets_; } namespace cepgen { namespace hadr { /** * Full interface to the Pythia6 @cite Sjostrand:2006za algorithm. It can be used in a single particle decay mode as well as a full event hadronisation using the string model, as in Jetset. * @brief Pythia6 hadronisation algorithm */ class Pythia6Hadroniser : public GenericHadroniser { public: Pythia6Hadroniser( const ParametersList& ); void setParameters( const Parameters& ) override {} - void readString( const char* param ) override; + inline void readString( const char* param ) override { pygive( param ); } void init() override {} bool run( Event& ev, double& weight, bool full ) override; void setCrossSection( double xsec, double xsec_err ) override {} //bool hadronise( const Particle* ); private: static constexpr unsigned short MAX_PART_STRING = 3; static constexpr unsigned short MAX_STRING_EVENT = 2; /// Maximal number of characters to fetch for the particle's name static constexpr unsigned short NAME_CHR = 16; inline static double pymass(int pdgid_) { return pymass_(pdgid_); } //inline static double pywidt(int pdgid_) { return pywidt_(pdgid_); } inline static void pyckbd() { pyckbd_(); } inline static void pygive( const std::string& line ) { pygive_( line.c_str(), line.length() ); } inline static void pylist( int mlist ) { pylist_( mlist ); } inline static int pyk( int id, int qty ) { return pyk_( id, qty ); } inline static double pyp( int id, int qty ) { return pyp_( id, qty ); } inline static std::string pyname( int pdgid ) { char out[NAME_CHR]; std::string s; pyname_( pdgid, out, NAME_CHR ); s = std::string( out, NAME_CHR ); s.erase( remove( s.begin(), s.end(), ' ' ), s.end() ); return s; } /** * \brief Connect entries with colour flow information - * \param[in] njoin_ Number of particles to join in the colour flow - * \param[in] ijoin_ List of particles unique identifier to join in the colour flow + * \param[in] njoin Number of particles to join in the colour flow + * \param[in] ijoin List of particles unique identifier to join in the colour flow */ inline static void pyjoin( int njoin, int ijoin[2] ) { return pyjoin_( njoin, *ijoin ); } bool prepareHadronisation( Event& ); std::pair<short,short> pickPartonsContent() const; struct EventProperties { unsigned int str_in_evt = 0; unsigned int num_part_in_str[MAX_STRING_EVENT] = { 0 }; }; EventProperties fillParticles( const Event& ) const; }; Pythia6Hadroniser::Pythia6Hadroniser( const ParametersList& plist ) : GenericHadroniser( plist, "pythia6" ) - {} - - void - Pythia6Hadroniser::readString( const char* param ) - { - pygive( param ); - } + {CG_INFO("");} bool Pythia6Hadroniser::run( Event& ev, double& weight, bool full ) { full = true; //FIXME weight = 1.; if ( full ) prepareHadronisation( ev ); if ( utils::Logger::get().level >= utils::Logger::Level::debug ) { CG_DEBUG( "Pythia6Hadroniser" ) << "Dump of the event before the hadronisation:"; ev.dump(); } //--- fill Pythia 6 common blocks auto prop = fillParticles( ev ); CG_DEBUG( "Pythia6Hadroniser" ) << "Passed the string construction stage.\n\t" << " " << prop.str_in_evt << " string objects were identified and constructed."; unsigned int oldnpart = pyjets_.n; pyexec_(); int criteria = oldnpart+1; for ( unsigned int i = 0; i < MAX_STRING_EVENT; ++i ) criteria += prop.num_part_in_str[i]; if ( pyjets_.k[1][criteria] == 2212 && pyjets_.k[0][criteria] == 1 ) { CG_WARNING( "Pythia6Hadroniser" ) << "System is non-inelastic."; return false; } // We filter the first particles already present in the event for ( unsigned int p = oldnpart; p < (unsigned int)pyjets_.n; ++p ) { const pdgid_t pdg_id = abs( pyjets_.k[1][p] ); const short charge = pyjets_.k[1][p]/(short)pdg_id; ParticleProperties prop; if ( full ) try { prop = PDG::get()( pdg_id ); } catch ( const Exception& ) { prop = ParticleProperties{ pdg_id, pyname( pdg_id ), pyname( pdg_id ), (short)pyk( p+1, 12 ), // colour factor pymass( pdg_id ), -1., //pmas( pdg_id, 2 ), (short)pyk( p+1, 6 ), // charge false }; PDG::get().define( prop ); } const Particle::Role role = pyjets_.k[2][p] != 0 ? ev[pyjets_.k[2][p]-1].role() // child particle inherits its mother's role : Particle::Role::UnknownRole; auto& pa = ev.addParticle( role ); pa.setId( p ); pa.setPdgId( pdg_id, charge ); pa.setStatus( (Particle::Status)pyjets_.k[0][p] ); pa.setMomentum( Particle::Momentum( pyjets_.p[0][p], pyjets_.p[1][p], pyjets_.p[2][p], pyjets_.p[3][p] ) ); pa.setMass( pyjets_.p[4][p] ); if ( role != Particle::Role::UnknownRole ) { auto& moth = ev[pyjets_.k[2][p]-1]; moth.setStatus( role == Particle::Role::CentralSystem ? Particle::Status::Resonance : Particle::Status::Fragmented ); pa.addMother( moth ); } } return true; } bool Pythia6Hadroniser::prepareHadronisation( Event& ev ) { CG_DEBUG( "Pythia6Hadroniser" ) << "Hadronisation preparation called."; for ( const auto& part : ev.particles() ) { if ( part.status() != Particle::Status::Unfragmented ) continue; //--- only loop over all protons to be fragmented part.dump(); const auto partons = pickPartonsContent(); const double mx2 = part.mass2(); const double mq = pymass( partons.first ), mq2 = mq*mq; const double mdq = pymass( partons.second ), mdq2 = mdq*mdq; //--- choose random direction in MX frame const double phi = 2.*M_PI*drand(), theta = acos( 2.*drand()-1. ); // theta angle //--- compute momentum of decay particles from MX const double px = std::sqrt( std::pow( mx2-mdq2+mq2, 2 ) / ( 4.*mx2 ) - mq2 ); //--- build 4-vectors and boost decay particles // const auto pq = Particle::Momentum::fromPThetaPhi( px, theta, phi, std::hypot( px, mq ) ); const auto pq = Particle::Momentum::fromPThetaPhi( px, theta, phi, part.mass() ); CG_INFO("")<<pq << "|" << -pq; //--- singlet auto singl_mom = part.momentum(); singl_mom.lorentzBoost( pq ); auto& quark = ev.addParticle( part.role() ); quark.addMother( ev[part.id()] ); quark.setPdgId( partons.first ); quark.setStatus( Particle::Status::FinalState ); quark.setMomentum( singl_mom ); //--- doublet auto doubl_mom = part.momentum(); doubl_mom.lorentzBoost( -pq ); auto& diquark = ev.addParticle( part.role() ); diquark.addMother( ev[part.id()] ); diquark.setPdgId( partons.second ); diquark.setStatus( Particle::Status::FinalState ); diquark.setMomentum( doubl_mom ); std::cout << part.momentum()-(singl_mom+doubl_mom) << "|" << part.numDaughters() << std::endl; ev[part.id()].setStatus( Particle::Status::Fragmented ); } ev.dump(); return true; } Pythia6Hadroniser::EventProperties Pythia6Hadroniser::fillParticles( const Event& ev ) const { pyjets_.n = 0; //--- initialising the string fragmentation variables EventProperties out; out.str_in_evt = 0; int jlpsf[MAX_STRING_EVENT][MAX_PART_STRING] = { 0 }; for ( const auto& role : ev.roles() ) { // loop on roles unsigned int part_in_str = 0; bool role_has_string = false; for ( const auto& part : ev[role] ) { unsigned int np = part.id(); pyjets_.p[0][np] = part.momentum().px(); pyjets_.p[1][np] = part.momentum().py(); pyjets_.p[2][np] = part.momentum().pz(); pyjets_.p[3][np] = part.energy(); pyjets_.p[4][np] = part.mass(); pyjets_.k[0][np] = part.status() <= Particle::Status::Undefined ? 21 // incoming beam : (int)part.status(); pyjets_.k[1][np] = part.integerPdgId(); pyjets_.k[2][np] = part.mothers().empty() ? 0 // no mother : *( part.mothers().begin() )+1; // mother const auto& daug = part.daughters(); if ( daug.empty() ) pyjets_.k[3][np] = pyjets_.k[4][np] = 0; // no daughters else { pyjets_.k[3][np] = *daug.begin()+1; // daughter 1 pyjets_.k[4][np] = *daug.rbegin()+1; // daughter 2 } for ( int i = 0; i < 5; ++i ) pyjets_.v[i][np] = 0.; if ( part.status() == Particle::Status::DebugResonance ) { pyjets_.k[0][np] = 1; //FIXME PYTHIA/JETSET workaround jlpsf[out.str_in_evt][part_in_str++] = part.id()+1; out.num_part_in_str[out.str_in_evt]++; role_has_string = true; } else if ( part.status() == Particle::Status::Undecayed ) pyjets_.k[0][np] = 2; // intermediate resonance pyjets_.n++; } //--- at most one string per role if ( role_has_string ) out.str_in_evt++; } //--- loop over the strings to bind everything together for ( unsigned short i = 0; i < out.str_in_evt; ++i ) { if ( out.num_part_in_str[i] < 2 ) continue; std::ostringstream dbg; for ( unsigned short j = 0; j < out.num_part_in_str[i]; ++j ) if ( jlpsf[i][j] != -1 ) dbg << Form( "\n\t * %2d (pdgId=%4d)", jlpsf[i][j], pyjets_.k[1][jlpsf[i][j]-1] ); CG_INFO( "Pythia6Hadroniser" ) << "Joining " << out.num_part_in_str[i] << " particle" << utils::s( out.num_part_in_str[i] ) << " with " << ev[jlpsf[i][0]].role() << " role" << " in a same string (id=" << i << ")" << dbg.str(); pyjoin( out.num_part_in_str[i], jlpsf[i] ); } return out; } std::pair<short,short> Pythia6Hadroniser::pickPartonsContent() const { const double ranudq = drand(); if ( ranudq < 1./9. ) return { PDG::down, 2203 }; // (d,uu1) if ( ranudq < 5./9. ) return { PDG::up, 2101 }; // (u,ud0) return { PDG::up, 2103 }; // (u,ud1) } } } // register hadroniser and define alias REGISTER_HADRONISER( pythia6, Pythia6Hadroniser )