diff --git a/Cards/Config/Core.py b/Cards/Config/Core.py index f239c64..82be4de 100644 --- a/Cards/Config/Core.py +++ b/Cards/Config/Core.py @@ -1,173 +1,176 @@ #!/usr/bin/env python class PrintHelper(object): _indent = 0 _indent_size = 4 def indent(self): self._indent += self._indent_size def unindent(self): self._indent -= self._indent_size def indentation(self): return ' '*self._indent class Parameters(dict): '''A raw list of steering parameters''' __getattr__ = dict.get __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ def __init__(self, *args, **kwargs): self.update(*args, **kwargs) super(Parameters, self).__init__(*args, **kwargs) def __deepcopy__(self, memo): from copy import deepcopy return Parameters([(deepcopy(k, memo), deepcopy(v, memo)) for k, v in self.items()]) def dump(self, printer=PrintHelper()): out = self.__class__.__name__+'(\n' for k, v in self.items(): printer.indent() out += ('%s%s = ' % (printer.indentation(), k)) if v.__class__.__name__ not in ['Parameters', 'Module']: out += v.__repr__() else: out += v.dump(printer) out += ',\n' printer.unindent() out += printer.indentation()+')' return out def __repr__(self): return self.dump() def clone(self, *args, **kwargs): from copy import deepcopy out = deepcopy(self) for k in kwargs: out[k] = kwargs.get(k) return type(self)(out) def load(self, mod): mod = mod.replace('/', '.') module = __import__(mod) self.extend(sys.modules[mod]) class Module(Parameters): '''A named parameters set to steer a generic module''' def __init__(self, name, *args, **kwargs): super(Module, self).__init__(*args, **kwargs) self.mod_name = name def __len__(self): return dict.__len__(self)-1 # discard the name key def dump(self, printer=PrintHelper()): out = self.__class__.__name__+'('+self.mod_name.__repr__()+'\n' mod_repr = self.clone('') mod_repr.pop('mod_name', None) for k, v in mod_repr.items(): printer.indent() out += ('%s%s = ' % (printer.indentation(), k)) if v.__class__.__name__ not in ['Parameters', 'Module']: out += v.__repr__() else: out += v.dump(printer) out += ',\n' printer.unindent() out += printer.indentation()+')' return out def __repr__(self): return self.dump() def clone(self, name, **kwargs): out = Parameters(self).clone(**kwargs) out.mod_name = name return out class Logging: '''Logging verbosity''' Nothing = 0 Error = 1 Warning = 2 Information = 3 Debug = 4 DebugInsideLoop = 5 class StructureFunctions: '''Types of structure functions supported''' Electron = Parameters(id=1) ElasticProton = Parameters(id=2) SuriYennie = Parameters(id=11) SzczurekUleshchenko = Parameters(id=12) BlockDurandHa = Parameters(id=13) FioreBrasse = Parameters(id=101) ChristyBosted = Parameters(id=102) CLAS = Parameters(id=103) ALLM91 = Parameters(id=201) ALLM97 = Parameters(id=202) GD07p = Parameters(id=203) GD11p = Parameters(id=204) - MSTWgrid = Parameters(id=205) + MSTWgrid = Parameters( + id = 205, + gridPath = 'External/F2_Luxlike_fit/mstw_f2_scan_nnlo.dat', + ) LUXlike = Parameters( id = 301, #Q2cut = 10., #W2limits = (4.,1.), continuumSF = GD11p, resonancesSF = ChristyBosted, ) GenericLHAPDF = Parameters( id = 401, pdfSet = 'LUXqed17_plus_PDF4LHC15_nnlo_100', numFlavours = 5, ) class KTFlux: PhotonElastic = 0 PhotonElasticBudnev = 10 PhotonInelasticBudnev = 1 PhotonInelasticBudnev = 11 PhotonElasticHI = 100 GluonKMR = 20 class ProcessMode: '''Types of processes supported''' ElectronProton = 0 ElasticElastic = 1 ElasticInelastic = 2 InelasticElastic = 3 InelasticInelastic = 4 ProtonElectron = 5 ElectronElectron = 6 if __name__ == '__main__': import unittest class TestTypes(unittest.TestCase): def testModules(self): mod = Module('empty') self.assertEqual(len(mod), 0) mod.param1 = 'foo' self.assertEqual(len(mod), 1) # playing with modules clones mod_copy = mod.clone('notEmpty', param1 = 'boo', param2 = 'bar') self.assertEqual(mod.param1, 'foo') self.assertEqual(mod_copy.param1, 'boo') self.assertEqual(mod_copy.param2, 'bar') self.assertEqual(mod.param1+mod_copy.param2, 'foobar') def testParameters(self): params = Parameters( first = 'foo', second = 'bar', third = 42, fourth = (1, 2), ) params_copy = params.clone( second = 'bak', ) self.assertEqual(len(params), 4) self.assertEqual(params.first, params['first']) self.assertEqual(params['second'], 'bar') self.assertTrue(int(params.third) == params.third) self.assertEqual(len(params.fourth), 2) self.assertEqual(params.second, 'bar') # playing with parameters clones self.assertEqual(params_copy.second, 'bak') # check that the clone does not change value if the origin does # (i.e. we indeed have a deep copy and not a shallow one...) params.third = 43 self.assertEqual(params.third, 43) self.assertEqual(params_copy.third, 42) unittest.main() diff --git a/CepGen/Cards/PythonHandler.cpp b/CepGen/Cards/PythonHandler.cpp index 61dc4ad..b2e1c6b 100644 --- a/CepGen/Cards/PythonHandler.cpp +++ b/CepGen/Cards/PythonHandler.cpp @@ -1,699 +1,704 @@ #include "PythonHandler.h" #include "CepGen/Core/Exception.h" #ifdef PYTHON #include "CepGen/Core/TamingFunction.h" #include "CepGen/Core/Exception.h" #include "CepGen/Processes/GamGamLL.h" #include "CepGen/Processes/PPtoFF.h" #include "CepGen/Processes/PPtoWW.h" #include "CepGen/Processes/FortranKTProcess.h" #include "CepGen/StructureFunctions/StructureFunctionsBuilder.h" #include "CepGen/StructureFunctions/GenericLHAPDF.h" +#include "CepGen/StructureFunctions/MSTWGrid.h" #include "CepGen/StructureFunctions/Schaefer.h" #include "CepGen/Hadronisers/Pythia8Hadroniser.h" #include <algorithm> #include <frameobject.h> extern "C" { extern void nucl_to_ff_( double& ); } #if PY_MAJOR_VERSION < 3 # define PYTHON2 #endif namespace CepGen { namespace Cards { //----- specialization for CepGen input cards PythonHandler::PythonHandler( const char* file ) { setenv( "PYTHONPATH", ".:..:Cards", 1 ); std::string filename = getPythonPath( file ); const size_t fn_len = filename.length()+1; //Py_DebugFlag = 1; //Py_VerboseFlag = 1; #ifdef PYTHON2 char* sfilename = new char[fn_len]; snprintf( sfilename, fn_len, "%s", filename.c_str() ); #else wchar_t* sfilename = new wchar_t[fn_len]; swprintf( sfilename, fn_len, L"%s", filename.c_str() ); #endif if ( sfilename ) Py_SetProgramName( sfilename ); Py_InitializeEx( 1 ); if ( sfilename ) delete [] sfilename; if ( !Py_IsInitialized() ) throw CG_FATAL( "PythonHandler" ) << "Failed to initialise the Python cards parser!"; CG_INFO( "PythonHandler" ) << "Initialised the Python cards parser\n\t" << "Python version: " << Py_GetVersion() << "\n\t" << "Platform: " << Py_GetPlatform() << "."; PyObject* cfg = PyImport_ImportModule( filename.c_str() ); // new if ( !cfg ) throwPythonError( Form( "Failed to parse the configuration card %s", file ) ); PyObject* process = PyObject_GetAttrString( cfg, PROCESS_NAME ); // new if ( !process ) throwPythonError( Form( "Failed to extract a \"%s\" keyword from the configuration card %s", PROCESS_NAME, file ) ); //--- type of process to consider PyObject* pproc_name = getElement( process, MODULE_NAME ); // borrowed if ( !pproc_name ) throwPythonError( Form( "Failed to extract the process name from the configuration card %s", file ) ); const std::string proc_name = decode( pproc_name ); if ( proc_name == "lpair" ) params_.setProcess( new Process::GamGamLL ); else if ( proc_name == "pptoll" || proc_name == "pptoff" ) params_.setProcess( new Process::PPtoFF ); else if ( proc_name == "pptoww" ) params_.setProcess( new Process::PPtoWW ); else if ( proc_name == "patoll" ) params_.setProcess( new Process::FortranKTProcess( "nucltoff", "(p/A)(p/A) ↝ (g/ɣ)ɣ → f⁺f¯", nucl_to_ff_ ) ); else throw CG_FATAL( "PythonHandler" ) << "Unrecognised process: " << proc_name << "."; //--- process mode fillParameter( process, "mode", (int&)params_.kinematics.mode ); //--- process kinematics PyObject* pin_kinematics = getElement( process, "inKinematics" ); // borrowed if ( pin_kinematics ) parseIncomingKinematics( pin_kinematics ); PyObject* pout_kinematics = getElement( process, "outKinematics" ); // borrowed if ( pout_kinematics ) parseOutgoingKinematics( pout_kinematics ); Py_CLEAR( process ); PyObject* plog = PyObject_GetAttrString( cfg, "logger" ); // new if ( plog ) { parseLogging( plog ); Py_CLEAR( plog ); } //--- hadroniser parameters PyObject* phad = PyObject_GetAttrString( cfg, "hadroniser" ); // new if ( phad ) { parseHadroniser( phad ); Py_CLEAR( phad ); } //--- generation parameters PyObject* pint = PyObject_GetAttrString( cfg, "integrator" ); // new if ( pint ) { parseIntegrator( pint ); Py_CLEAR( pint ); } PyObject* pgen = PyObject_GetAttrString( cfg, "generator" ); // new if ( pgen ) { parseGenerator( pgen ); Py_CLEAR( pgen ); } //--- taming functions PyObject* ptam = PyObject_GetAttrString( cfg, "tamingFunctions" ); // new if ( ptam ) { parseTamingFunctions( ptam ); Py_CLEAR( ptam ); } PyObject* pkmr_path = PyObject_GetAttrString( cfg, "kmrGridPath" ); // new if ( pkmr_path ) { //strncpy( Constants::kmr_grid_path, decode( pkmr_path ).c_str(), 256 ); params_.kinematics.kmr_grid_path = decode( pkmr_path ); Py_CLEAR( pkmr_path ); } //--- finalisation Py_CLEAR( cfg ); } PythonHandler::~PythonHandler() { if ( Py_IsInitialized() ) Py_Finalize(); } void PythonHandler::parseIncomingKinematics( PyObject* kin ) { PyObject* ppz = getElement( kin, "pz" ); // borrowed if ( ppz && PyTuple_Check( ppz ) && PyTuple_Size( ppz ) == 2 ) { double pz0 = PyFloat_AsDouble( PyTuple_GetItem( ppz, 0 ) ); double pz1 = PyFloat_AsDouble( PyTuple_GetItem( ppz, 1 ) ); params_.kinematics.incoming_beams.first.pz = pz0; params_.kinematics.incoming_beams.second.pz = pz1; } double sqrt_s = -1.; fillParameter( kin, "cmEnergy", sqrt_s ); if ( sqrt_s != -1. ) params_.kinematics.setSqrtS( sqrt_s ); PyObject* psf = getElement( kin, "structureFunctions" ); // borrowed if ( psf ) parseStructureFunctions( psf ); std::vector<int> kt_fluxes; fillParameter( kin, "ktFluxes", kt_fluxes ); if ( kt_fluxes.size() > 0 ) params_.kinematics.incoming_beams.first.kt_flux = kt_fluxes.at( 0 ); if ( kt_fluxes.size() > 1 ) params_.kinematics.incoming_beams.second.kt_flux = kt_fluxes.at( 1 ); std::vector<int> hi_beam1, hi_beam2; fillParameter( kin, "heavyIonA", hi_beam1 ); if ( hi_beam1.size() == 2 ) params_.kinematics.incoming_beams.first.hi = Kinematics::HeavyIon{ (unsigned short)hi_beam1[0], (unsigned short)hi_beam1[1] }; fillParameter( kin, "heavyIonB", hi_beam2 ); if ( hi_beam2.size() == 2 ) params_.kinematics.incoming_beams.second.hi = Kinematics::HeavyIon{ (unsigned short)hi_beam2[0], (unsigned short)hi_beam2[1] }; } void PythonHandler::parseStructureFunctions( PyObject* psf ) { int str_fun = 0; fillParameter( psf, "id", str_fun ); params_.kinematics.structure_functions.reset( StructureFunctionsBuilder::get( (SF::Type)str_fun ) ); switch( (SF::Type)str_fun ) { case SF::Type::GenericLHAPDF: { auto sf = dynamic_cast<SF::GenericLHAPDF*>( params_.kinematics.structure_functions.get() ); fillParameter( psf, "pdfSet", sf->params.pdf_set ); fillParameter( psf, "numFlavours", (unsigned int&)sf->params.num_flavours ); } break; + case SF::Type::MSTWgrid: { + auto sf = dynamic_cast<MSTW::Grid*>( params_.kinematics.structure_functions.get() ); + fillParameter( psf, "gridPath", sf->params.grid_path ); + } break; case SF::Type::Schaefer: { auto sf = dynamic_cast<SF::Schaefer*>( params_.kinematics.structure_functions.get() ); fillParameter( psf, "Q2cut", sf->params.q2_cut ); std::vector<double> w2_lims; fillParameter( psf, "W2limits", w2_lims ); if ( w2_lims.size() != 0 ) { if ( w2_lims.size() != 2 ) throwPythonError( Form( "Invalid size for W2limits attribute: %d != 2!", w2_lims.size() ) ); else { sf->params.w2_lo = *std::min_element( w2_lims.begin(), w2_lims.end() ); sf->params.w2_hi = *std::max_element( w2_lims.begin(), w2_lims.end() ); } } PyObject* pcsf = getElement( psf, "continuumSF" ); // borrowed if ( pcsf ) fillParameter( pcsf, "id", sf->params.cont_model ); PyObject* prsf = getElement( psf, "resonancesSF" ); // borrowed if ( prsf ) fillParameter( prsf, "id", sf->params.res_model ); fillParameter( psf, "higherTwist", (bool&)sf->params.higher_twist ); } break; default: break; } } void PythonHandler::parseOutgoingKinematics( PyObject* kin ) { PyObject* ppair = getElement( kin, "pair" ); // borrowed if ( ppair ) { if ( isInteger( ppair ) ) { PDG pair = (PDG)asInteger( ppair ); params_.kinematics.central_system = { pair, pair }; } else if ( PyTuple_Check( ppair ) ) { if ( PyTuple_Size( ppair ) != 2 ) throw CG_FATAL( "PythonHandler" ) << "Invalid value for in_kinematics.pair!"; PDG pair1 = (PDG)asInteger( PyTuple_GetItem( ppair, 0 ) ); PDG pair2 = (PDG)asInteger( PyTuple_GetItem( ppair, 1 ) ); params_.kinematics.central_system = { pair1, pair2 }; } } PyObject* pparts = getElement( kin, "minFinalState" ); // borrowed if ( pparts && PyTuple_Check( pparts ) ) for ( unsigned short i = 0; i < PyTuple_Size( pparts ); ++i ) params_.kinematics.minimum_final_state.emplace_back( (PDG)asInteger( PyTuple_GetItem( pparts, i ) ) ); PyObject* pcuts = getElement( kin, "cuts" ); // borrowed if ( pcuts ) parseParticlesCuts( pcuts ); // for LPAIR/collinear matrix elements fillLimits( kin, "q2", params_.kinematics.cuts.initial.q2 ); // for the kT factorised matrix elements fillLimits( kin, "qt", params_.kinematics.cuts.initial.qt ); fillLimits( kin, "phiqt", params_.kinematics.cuts.initial.phi_qt ); fillLimits( kin, "ptdiff", params_.kinematics.cuts.central.pt_diff ); fillLimits( kin, "phiptdiff", params_.kinematics.cuts.central.phi_pt_diff ); fillLimits( kin, "rapiditydiff", params_.kinematics.cuts.central.rapidity_diff ); // generic phase space limits fillLimits( kin, "rapidity", params_.kinematics.cuts.central.rapidity_single ); fillLimits( kin, "eta", params_.kinematics.cuts.central.eta_single ); fillLimits( kin, "pt", params_.kinematics.cuts.central.pt_single ); fillLimits( kin, "ptsum", params_.kinematics.cuts.central.pt_sum ); fillLimits( kin, "invmass", params_.kinematics.cuts.central.mass_sum ); fillLimits( kin, "mx", params_.kinematics.cuts.remnants.mass_single ); } void PythonHandler::parseParticlesCuts( PyObject* cuts ) { if ( !PyDict_Check( cuts ) ) throwPythonError( "Particle cuts object should be a dictionary!" ); PyObject* pkey = nullptr, *pvalue = nullptr; Py_ssize_t pos = 0; while ( PyDict_Next( cuts, &pos, &pkey, &pvalue ) ) { const PDG pdg = (PDG)asInteger( pkey ); fillLimits( pvalue, "pt", params_.kinematics.cuts.central_particles[pdg].pt_single ); fillLimits( pvalue, "energy", params_.kinematics.cuts.central_particles[pdg].energy_single ); fillLimits( pvalue, "eta", params_.kinematics.cuts.central_particles[pdg].eta_single ); fillLimits( pvalue, "rapidity", params_.kinematics.cuts.central_particles[pdg].rapidity_single ); } } void PythonHandler::parseLogging( PyObject* log ) { fillParameter( log, "level", (int&)Logger::get().level ); std::vector<std::string> enabled_modules; fillParameter( log, "enabledModules", enabled_modules ); for ( const auto& mod : enabled_modules ) Logger::get().addExceptionRule( mod ); } void PythonHandler::parseIntegrator( PyObject* integr ) { if ( !PyDict_Check( integr ) ) throwPythonError( "Integrator object should be a dictionary!" ); PyObject* palgo = getElement( integr, MODULE_NAME ); // borrowed if ( !palgo ) throwPythonError( "Failed to retrieve the integration algorithm name!" ); std::string algo = decode( palgo ); if ( algo == "plain" ) params_.integrator.type = Integrator::Type::plain; else if ( algo == "Vegas" ) { params_.integrator.type = Integrator::Type::Vegas; fillParameter( integr, "alpha", (double&)params_.integrator.vegas.alpha ); fillParameter( integr, "iterations", params_.integrator.vegas.iterations ); fillParameter( integr, "mode", (int&)params_.integrator.vegas.mode ); fillParameter( integr, "verbosity", (int&)params_.integrator.vegas.verbose ); std::string vegas_logging_output = "cerr"; fillParameter( integr, "loggingOutput", vegas_logging_output ); if ( vegas_logging_output == "cerr" ) // redirect all debugging information to the error stream params_.integrator.vegas.ostream = stderr; else if ( vegas_logging_output == "cout" ) // redirect all debugging information to the standard stream params_.integrator.vegas.ostream = stdout; else params_.integrator.vegas.ostream = fopen( vegas_logging_output.c_str(), "w" ); } else if ( algo == "MISER" ) { params_.integrator.type = Integrator::Type::MISER; fillParameter( integr, "estimateFraction", (double&)params_.integrator.miser.estimate_frac ); fillParameter( integr, "minCalls", params_.integrator.miser.min_calls ); fillParameter( integr, "minCallsPerBisection", params_.integrator.miser.min_calls_per_bisection ); fillParameter( integr, "alpha", (double&)params_.integrator.miser.alpha ); fillParameter( integr, "dither", (double&)params_.integrator.miser.dither ); } else throwPythonError( Form( "Invalid integration algorithm: %s", algo.c_str() ) ); fillParameter( integr, "numFunctionCalls", params_.integrator.ncvg ); fillParameter( integr, "seed", (unsigned long&)params_.integrator.rng_seed ); unsigned int rng_engine; fillParameter( integr, "rngEngine", rng_engine ); switch ( rng_engine ) { case 0: default: params_.integrator.rng_engine = (gsl_rng_type*)gsl_rng_mt19937; break; case 1: params_.integrator.rng_engine = (gsl_rng_type*)gsl_rng_taus2; break; case 2: params_.integrator.rng_engine = (gsl_rng_type*)gsl_rng_gfsr4; break; case 3: params_.integrator.rng_engine = (gsl_rng_type*)gsl_rng_ranlxs0; break; } fillParameter( integr, "chiSqCut", params_.integrator.vegas_chisq_cut ); } void PythonHandler::parseGenerator( PyObject* gen ) { if ( !PyDict_Check( gen ) ) throwPythonError( "Generation information object should be a dictionary!" ); params_.generation.enabled = true; fillParameter( gen, "treat", params_.generation.treat ); fillParameter( gen, "numEvents", params_.generation.maxgen ); fillParameter( gen, "printEvery", params_.generation.gen_print_every ); fillParameter( gen, "numThreads", params_.generation.num_threads ); fillParameter( gen, "numPoints", params_.generation.num_points ); } void PythonHandler::parseTamingFunctions( PyObject* tf ) { if ( !PyList_Check( tf ) ) throwPythonError( "Taming functions list should be a list!" ); for ( Py_ssize_t i = 0; i < PyList_Size( tf ); ++i ) { PyObject* pit = PyList_GetItem( tf, i ); // borrowed if ( !pit ) continue; if ( !PyDict_Check( pit ) ) throwPythonError( Form( "Item %d has invalid type %s", i, pit->ob_type->tp_name ) ); PyObject* pvar = getElement( pit, "variable" ), *pexpr = getElement( pit, "expression" ); // borrowed params_.taming_functions->add( decode( pvar ).c_str(), decode( pexpr ).c_str() ); } } void PythonHandler::parseHadroniser( PyObject* hadr ) { if ( !PyDict_Check( hadr ) ) throwPythonError( "Hadroniser object should be a dictionary!" ); PyObject* pname = getElement( hadr, MODULE_NAME ); // borrowed if ( !pname ) throwPythonError( "Hadroniser name is required!" ); std::string hadr_name = decode( pname ); fillParameter( hadr, "maxTrials", params_.hadroniser_max_trials ); PyObject* pseed = getElement( hadr, "seed" ); // borrowed long long seed = -1ll; if ( pseed && isInteger( pseed ) ) { seed = PyLong_AsLongLong( pseed ); CG_DEBUG( "PythonHandler:hadroniser" ) << "Hadroniser seed set to " << seed; } if ( hadr_name == "pythia8" ) { params_.setHadroniser( new Hadroniser::Pythia8Hadroniser( params_ ) ); std::vector<std::string> config; auto pythia8 = dynamic_cast<Hadroniser::Pythia8Hadroniser*>( params_.hadroniser() ); pythia8->setSeed( seed ); fillParameter( hadr, "pythiaPreConfiguration", config ); pythia8->readStrings( config ); pythia8->init(); fillParameter( hadr, "pythiaConfiguration", config ); pythia8->readStrings( config ); fillParameter( hadr, "pythiaProcessConfiguration", config ); pythia8->readStrings( config ); } } //------------------------------------------------------------------ // Python API helpers //------------------------------------------------------------------ std::string PythonHandler::getPythonPath( const char* file ) { std::string s_filename = file; s_filename = s_filename.substr( 0, s_filename.find_last_of( "." ) ); // remove the extension std::replace( s_filename.begin(), s_filename.end(), '/', '.' ); // replace all '/' by '.' return s_filename; } void PythonHandler::throwPythonError( const std::string& message ) { PyObject* ptype = nullptr, *pvalue = nullptr, *ptraceback_obj = nullptr; // retrieve error indicator and clear it to handle ourself the error PyErr_Fetch( &ptype, &pvalue, &ptraceback_obj ); PyErr_Clear(); // ensure the objects retrieved are properly normalised and point to compatible objects PyErr_NormalizeException( &ptype, &pvalue, &ptraceback_obj ); std::ostringstream oss; oss << message; if ( ptype != nullptr ) { // we can start the traceback oss << "\n\tError: " #ifdef PYTHON2 << PyString_AsString( PyObject_Str( pvalue ) ); // deprecated in python v3+ #else << PyUnicode_AsUTF8( PyObject_Str( pvalue ) ); #endif PyTracebackObject* ptraceback = (PyTracebackObject*)ptraceback_obj; string tabul = "↪ "; if ( ptraceback != nullptr ) { while ( ptraceback->tb_next != nullptr ) { PyFrameObject* pframe = ptraceback->tb_frame; if ( pframe != nullptr ) { int line = PyCode_Addr2Line( pframe->f_code, pframe->f_lasti ); #ifdef PYTHON2 const char* filename = PyString_AsString( pframe->f_code->co_filename ); const char* funcname = PyString_AsString( pframe->f_code->co_name ); #else const char* filename = PyUnicode_AsUTF8( pframe->f_code->co_filename ); const char* funcname = PyUnicode_AsUTF8( pframe->f_code->co_name ); #endif oss << Form( "\n\t%s%s on %s (line %d)", tabul.c_str(), boldify( funcname ).c_str(), filename, line ); } else oss << Form( "\n\t%s issue in line %d", tabul.c_str(), ptraceback->tb_lineno ); tabul = string( " " )+tabul; ptraceback = ptraceback->tb_next; } } } Py_Finalize(); throw CG_FATAL( "PythonHandler:error" ) << oss.str(); } std::string PythonHandler::decode( PyObject* obj ) { std::string out; #ifdef PYTHON2 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!" ); out = PyBytes_AS_STRING( pstr ); Py_CLEAR( pstr ); #endif return out; } PyObject* PythonHandler::encode( const char* str ) { PyObject* obj = PyUnicode_FromString( str ); // new if ( !obj ) throwPythonError( Form( "Failed to encode the following string:\n\t%s", str ) ); return obj; } PyObject* PythonHandler::getElement( PyObject* obj, const char* key ) { PyObject* pout = nullptr, *nink = encode( key ); if ( !nink ) return pout; pout = PyDict_GetItem( obj, nink ); // borrowed Py_CLEAR( nink ); if ( pout ) CG_DEBUG( "PythonHandler:getElement" ) << "retrieved " << pout->ob_type->tp_name << " element \"" << key << "\" " << "from " << obj->ob_type->tp_name << " object\n\t" << "new reference count: " << pout->ob_refcnt; else CG_DEBUG( "PythonHandler:getElement" ) << "did not retrieve a valid element \"" << key << "\""; return pout; } void PythonHandler::fillLimits( PyObject* obj, const char* key, Limits& lim ) { PyObject* pobj = getElement( obj, key ); // borrowed if ( !pobj ) return; if ( !PyTuple_Check( pobj ) ) throw CG_FATAL( "PythonHandler:fillLimits" ) << "Invalid value retrieved for " << key << "."; if ( PyTuple_Size( pobj ) < 1 ) throw CG_FATAL( "PythonHandler:fillLimits" ) << "Invalid number of values unpacked for " << key << "!"; double min = PyFloat_AsDouble( PyTuple_GetItem( pobj, 0 ) ); lim.min() = min; if ( PyTuple_Size( pobj ) > 1 ) { double max = PyFloat_AsDouble( PyTuple_GetItem( pobj, 1 ) ); if ( max != -1 ) lim.max() = max; } } void PythonHandler::fillParameter( PyObject* parent, const char* key, bool& out ) { PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( !PyBool_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); fillParameter( parent, key, (int&)out ); } void PythonHandler::fillParameter( PyObject* parent, const char* key, int& out ) { PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; #ifdef PYTHON2 if ( !PyInt_Check( pobj ) && !PyBool_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); out = PyInt_AsLong( pobj ); #else if ( !PyLong_Check( pobj ) && !PyBool_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); out = PyLong_AsLong( pobj ); #endif } void PythonHandler::fillParameter( PyObject* parent, const char* key, unsigned long& out ) { PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( !PyLong_Check( pobj ) #ifdef PYTHON2 && !PyInt_Check( pobj ) #endif ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); if ( PyLong_Check( pobj ) ) out = PyLong_AsUnsignedLong( pobj ); #ifdef PYTHON2 else if ( PyInt_Check( pobj ) ) out = PyInt_AsUnsignedLongMask( pobj ); #endif } void PythonHandler::fillParameter( PyObject* parent, const char* key, unsigned int& out ) { PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; #ifdef PYTHON2 if ( !PyInt_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); out = PyInt_AsUnsignedLongMask( pobj ); #else if ( !PyLong_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); out = PyLong_AsUnsignedLong( pobj ); #endif } void PythonHandler::fillParameter( PyObject* parent, const char* key, double& out ) { PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( !PyFloat_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); out = PyFloat_AsDouble( pobj ); } void PythonHandler::fillParameter( PyObject* parent, const char* key, std::string& out ) { PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( ! #ifdef PYTHON2 PyString_Check( pobj ) #else PyUnicode_Check( pobj ) #endif ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); out = decode( pobj ); } void PythonHandler::fillParameter( PyObject* parent, const char* key, std::vector<double>& out ) { out.clear(); PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( !PyTuple_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); for ( Py_ssize_t i = 0; i < PyTuple_Size( pobj ); ++i ) { PyObject* pit = PyTuple_GetItem( pobj, i ); // borrowed if ( !PyFloat_Check( pit ) ) continue; out.emplace_back( PyFloat_AsDouble( pit ) ); } } void PythonHandler::fillParameter( PyObject* parent, const char* key, std::vector<std::string>& out ) { out.clear(); PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( !PyTuple_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type %s", key, pobj->ob_type->tp_name ) ); for ( Py_ssize_t i = 0; i < PyTuple_Size( pobj ); ++i ) { PyObject* pit = PyTuple_GetItem( pobj, i ); // borrowed out.emplace_back( decode( pit ) ); } } void PythonHandler::fillParameter( PyObject* parent, const char* key, std::vector<int>& out ) { out.clear(); PyObject* pobj = getElement( parent, key ); // borrowed if ( !pobj ) return; if ( !PyTuple_Check( pobj ) ) throwPythonError( Form( "Object \"%s\" has invalid type", key ) ); for ( Py_ssize_t i = 0; i < PyTuple_Size( pobj ); ++i ) { PyObject* pit = PyTuple_GetItem( pobj, i ); if ( !isInteger( pit ) ) throwPythonError( Form( "Object %d has invalid type", i ) ); out.emplace_back( asInteger( pit ) ); } } bool PythonHandler::isInteger( PyObject* obj ) { #ifdef PYTHON2 return PyInt_Check( obj ); #else return PyLong_Check( obj ); #endif } int PythonHandler::asInteger( PyObject* obj ) { #ifdef PYTHON2 return PyInt_AsLong( obj ); #else return PyLong_AsLong( obj ); #endif } } } #endif diff --git a/CepGen/IO/FortranInterface.cpp b/CepGen/IO/FortranInterface.cpp index f099364..7b27b6c 100644 --- a/CepGen/IO/FortranInterface.cpp +++ b/CepGen/IO/FortranInterface.cpp @@ -1,73 +1,67 @@ #include "CepGen/StructureFunctions/StructureFunctionsBuilder.h" #include "CepGen/StructureFunctions/MSTWGrid.h" #include "CepGen/Processes/GenericKTProcess.h" #include "CepGen/Core/Exception.h" #ifdef __cplusplus extern "C" { #endif void cepgen_structure_functions_( int& sfmode, double& q2, double& xbj, double& f2, double& fl ) { using namespace CepGen; SF::Type sf_mode = (SF::Type)sfmode; CG_DEBUG( "cepgen_structure_functions" ) << sf_mode; - if ( sf_mode == SF::Type::MSTWgrid ) { - StructureFunctions sf = MSTW::Grid::get( "External/F2_Luxlike_fit/mstw_f2_scan_nnlo.dat" ).eval( q2, xbj ); - f2 = sf.F2; - fl = sf.FL; - return; - } StructureFunctions* sf = StructureFunctionsBuilder::get( sf_mode ); StructureFunctions val = ( *sf )( q2, xbj ); f2 = val.F2; fl = val.FL; delete sf; } /*bool lhapdf_init = false; double cepgen_coll_flux_( int& fmode, double& q2, double& x ) { if ( !lhapdf_init ) { LHAPDF::initPDFSet( set, LHAPDF::LHGRID, 0 ); } }*/ double cepgen_kt_flux_( int& fmode, double& kt2, double& x, int& sfmode, double& mx ) { using namespace CepGen; using namespace CepGen::Process; return GenericKTProcess::flux( (GenericKTProcess::Flux)fmode, kt2, x, *StructureFunctionsBuilder::get( (SF::Type)sfmode ), mx ); } double cepgen_kt_flux_hi_( int& fmode, double& kt2, double& x, int& a, int& z ) { using namespace CepGen; using namespace CepGen::Process; return GenericKTProcess::flux( (GenericKTProcess::Flux)fmode, kt2, x, Kinematics::HeavyIon{ ( unsigned short )a, ( unsigned short )z } ); } double cepgen_particle_mass_( int& pdg_id ) { return CepGen::ParticleProperties::mass( (CepGen::PDG)pdg_id ); } double cepgen_particle_charge_( int& pdg_id ) { return CepGen::ParticleProperties::charge( pdg_id ); } #ifdef __cplusplus } #endif diff --git a/CepGen/StructureFunctions/MSTWGrid.cpp b/CepGen/StructureFunctions/MSTWGrid.cpp index 205c48c..e73648f 100644 --- a/CepGen/StructureFunctions/MSTWGrid.cpp +++ b/CepGen/StructureFunctions/MSTWGrid.cpp @@ -1,115 +1,118 @@ #include "CepGen/StructureFunctions/MSTWGrid.h" #include "CepGen/Core/Exception.h" #include "CepGen/Core/utils.h" #include "CepGen/StructureFunctions/StructureFunctions.h" #include <fstream> #include <gsl/gsl_errno.h> #include <gsl/gsl_math.h> namespace MSTW { const unsigned int Grid::good_magic = 0x5754534d; // MSTW in ASCII Grid& Grid::get( const char* filename ) { - static Grid instance( filename ); + Parameterisation p; + p.grid_path = filename; + static Grid instance( p ); return instance; } - Grid::Grid( const char* filename ) : - CepGen::GridHandler<2>( CepGen::GridType::kLogarithmic ) + Grid::Grid( const Parameterisation& param ) : + CepGen::StructureFunctions( CepGen::SF::Type::MSTWgrid ), + CepGen::GridHandler<2>( CepGen::GridType::kLogarithmic ), + params( param ) { std::set<double> q2_vals, xbj_vals; { // file readout part - std::ifstream file( filename, std::ios::binary | std::ios::in ); + std::ifstream file( params.grid_path, std::ios::binary | std::ios::in ); if ( !file.is_open() ) - throw CG_FATAL( "Grid" ) << "Impossible to load grid file \"" << filename << "%s\"!"; + throw CG_FATAL( "Grid" ) << "Impossible to load grid file \"" << params.grid_path << "\"!"; file.read( reinterpret_cast<char*>( &header_ ), sizeof( header_t ) ); // first checks on the file header if ( header_.magic != good_magic ) throw CG_FATAL( "Grid" ) << "Wrong magic number retrieved: " << header_.magic << ", expecting " << good_magic << "."; if ( header_.nucleon != header_t::proton ) throw CG_FATAL( "Grid" ) << "Only proton structure function grids can be retrieved for this purpose!"; // retrieve all points and evaluate grid boundaries sfval_t val; while ( file.read( reinterpret_cast<char*>( &val ), sizeof( sfval_t ) ) ) { q2_vals.insert( log10( val.x ) ); xbj_vals.insert( log10( val.y ) ); #ifndef GOOD_GSL x_vals_.emplace_back( val.x ); y_vals_.emplace_back( val.y ); #endif values_raw_.emplace_back( val ); } file.close(); } if ( q2_vals.size() < 2 || xbj_vals.size() < 2 ) throw CG_FATAL( "Grid" ) << "Invalid grid retrieved!"; initGSL( q2_vals, xbj_vals ); CG_INFO( "Grid" ) << "MSTW@" << header_.order << " grid evaluator built " << "for " << header_.nucleon << " structure functions (" << header_.cl << ")\n\t" << " Q² in range [" << pow( 10., *q2_vals.begin() ) << ":" << pow( 10., *q2_vals.rbegin() ) << "]\n\t" << "xBj in range [" << pow( 10., *xbj_vals.begin() ) << ":" << pow( 10., *xbj_vals.rbegin() ) << "]."; } - CepGen::StructureFunctions - Grid::eval( double q2, double xbj ) const + Grid& + Grid::operator()( double q2, double xbj ) { const sfval_t val = CepGen::GridHandler<2>::eval( q2, xbj ); - CepGen::StructureFunctions sf; - sf.F2 = val.value[0]; - sf.FL = val.value[1]; - return sf; + F2 = val.value[0]; + FL = val.value[1]; + return *this; } std::ostream& operator<<( std::ostream& os, const sfval_t& val ) { return os << Form( "Q² = %.5e GeV²\txbj = %.4f\tF₂ = % .6e\tFL = % .6e", val.x, val.y, val.value[0], val.value[1] ); } std::ostream& operator<<( std::ostream& os, const Grid::header_t::order_t& order ) { switch ( order ) { case Grid::header_t::lo: return os << "LO"; case Grid::header_t::nlo: return os << "nLO"; case Grid::header_t::nnlo: return os << "nnLO"; } return os; } std::ostream& operator<<( std::ostream& os, const Grid::header_t::cl_t& cl ) { switch ( cl ) { case Grid::header_t::cl68: return os << "68% C.L."; case Grid::header_t::cl95: return os << "95% C.L."; } return os; } std::ostream& operator<<( std::ostream& os, const Grid::header_t::nucleon_t& nucl ) { switch ( nucl ) { case Grid::header_t::proton: return os << "proton"; case Grid::header_t::neutron: return os << "neutron"; } return os; } } diff --git a/CepGen/StructureFunctions/MSTWGrid.h b/CepGen/StructureFunctions/MSTWGrid.h index 7f83c33..1180500 100644 --- a/CepGen/StructureFunctions/MSTWGrid.h +++ b/CepGen/StructureFunctions/MSTWGrid.h @@ -1,52 +1,58 @@ -#ifndef CepGen_IO_MSTWGridHandler_h -#define CepGen_IO_MSTWGridHandler_h +#ifndef CepGen_StructureFunctions_MSTWGrid_h +#define CepGen_StructureFunctions_MSTWGrid_h #include "CepGen/IO/GridHandler.h" +#include "CepGen/StructureFunctions/StructureFunctions.h" /// Martin-Stirling-Thorne-Watt PDFs structure functions namespace MSTW { /// \note x/y = Q2/xbj given by the parent typedef CepGen::GridHandler<2>::grid_t sfval_t; std::ostream& operator<<( std::ostream&, const sfval_t& ); /// A \f$F_{2,L}\f$ grid interpolator - class Grid : public CepGen::GridHandler<2> + class Grid : public CepGen::StructureFunctions, private CepGen::GridHandler<2> { public: /// Grid header information as parsed from the file struct header_t { enum order_t : unsigned short { lo = 0, nlo = 1, nnlo = 2 }; enum cl_t : unsigned short { cl68 = 0, cl95 = 1 }; enum nucleon_t : unsigned short { proton = 1, neutron = 2 }; unsigned int magic; order_t order; cl_t cl; nucleon_t nucleon; }; + struct Parameterisation { + Parameterisation() : grid_path( "External/F2_Luxlike_fit/mstw_f2_scan_nnlo.dat" ) {} + std::string grid_path; + }; public: /// Retrieve the grid interpolator (singleton) - static Grid& get( const char* filename = "External/F2_Luxlike_fit/mstw_f2_scan_nnlo.dat" ); + static Grid& get( const char* path = "External/F2_Luxlike_fit/mstw_f2_scan_nnlo.dat" ); /// Compute the structure functions at a given \f$Q^2/x_{\rm Bj}\f$ - CepGen::StructureFunctions eval( double q2, double xbj ) const; + Grid& operator()( double q2, double xbj ) override; /// Retrieve the grid's header information header_t header() const { return header_; } + Parameterisation params; public: Grid( const Grid& ) = delete; void operator=( const GridHandler& ) = delete; private: - explicit Grid( const char* ); + explicit Grid( const Parameterisation& ); static const unsigned int good_magic; header_t header_; }; std::ostream& operator<<( std::ostream&, const Grid::header_t::order_t& ); std::ostream& operator<<( std::ostream&, const Grid::header_t::cl_t& ); std::ostream& operator<<( std::ostream&, const Grid::header_t::nucleon_t& ); } #endif diff --git a/CepGen/StructureFunctions/StructureFunctionsBuilder.cpp b/CepGen/StructureFunctions/StructureFunctionsBuilder.cpp index 54a3180..12edcac 100644 --- a/CepGen/StructureFunctions/StructureFunctionsBuilder.cpp +++ b/CepGen/StructureFunctions/StructureFunctionsBuilder.cpp @@ -1,38 +1,38 @@ #include "CepGen/StructureFunctions/StructureFunctionsBuilder.h" #include "CepGen/StructureFunctions/ALLM.h" #include "CepGen/StructureFunctions/BlockDurandHa.h" #include "CepGen/StructureFunctions/FioreBrasse.h" #include "CepGen/StructureFunctions/ChristyBosted.h" #include "CepGen/StructureFunctions/CLAS.h" #include "CepGen/StructureFunctions/GenericLHAPDF.h" #include "CepGen/StructureFunctions/SuriYennie.h" #include "CepGen/StructureFunctions/SzczurekUleshchenko.h" #include "CepGen/StructureFunctions/Schaefer.h" #include "CepGen/StructureFunctions/MSTWGrid.h" namespace CepGen { StructureFunctions* StructureFunctionsBuilder::get( const SF::Type& sf_type ) { switch ( sf_type ) { case SF::Type::Electron: - case SF::Type::MSTWgrid: //FIXME - default: - case SF::Type::ElasticProton: return new StructureFunctions(); + case SF::Type::ElasticProton: + default: return new StructureFunctions(); case SF::Type::SzczurekUleshchenko: return new SF::SzczurekUleshchenko(); case SF::Type::SuriYennie: return new SF::SuriYennie(); case SF::Type::FioreBrasse: return new SF::FioreBrasse(); case SF::Type::ChristyBosted: return new SF::ChristyBosted(); case SF::Type::CLAS: return new SF::CLAS(); case SF::Type::BlockDurandHa: return new SF::BlockDurandHa(); case SF::Type::ALLM91: return new SF::ALLM( SF::ALLM::Parameterisation::allm91() ); case SF::Type::ALLM97: return new SF::ALLM( SF::ALLM::Parameterisation::allm97() ); case SF::Type::GD07p: return new SF::ALLM( SF::ALLM::Parameterisation::gd07p() ); case SF::Type::GD11p: return new SF::ALLM( SF::ALLM::Parameterisation::gd11p() ); case SF::Type::Schaefer: return new SF::Schaefer(); case SF::Type::GenericLHAPDF: return new SF::GenericLHAPDF(); + case SF::Type::MSTWgrid: return &MSTW::Grid::get(); } } }