diff --git a/CepGen/Core/Generator.cpp b/CepGen/Core/Generator.cpp index d14629e..3fc35cd 100644 --- a/CepGen/Core/Generator.cpp +++ b/CepGen/Core/Generator.cpp @@ -1,184 +1,179 @@ #include "CepGen/Generator.h" #include "CepGen/Parameters.h" #include "CepGen/Version.h" #include "CepGen/Core/Integrator.h" #include "CepGen/Core/Exception.h" #include "CepGen/Core/Timer.h" +#include "CepGen/Core/utils.h" #include "CepGen/Physics/PDG.h" #include "CepGen/Processes/ProcessesHandler.h" #include "CepGen/Event/Event.h" #include <fstream> #include <chrono> #include <atomic> namespace cepgen { namespace utils { std::atomic<int> gSignal; } Generator::Generator() : parameters_( new Parameters ), result_( -1. ), result_error_( -1. ) { CG_DEBUG( "Generator:init" ) << "Generator initialized"; try { printHeader(); } catch ( const Exception& e ) { e.dump(); } //--- random number initialization std::chrono::system_clock::time_point time = std::chrono::system_clock::now(); srandom( time.time_since_epoch().count() ); } Generator::Generator( Parameters* ip ) : parameters_( ip ), result_( -1. ), result_error_( -1. ) {} Generator::~Generator() { if ( parameters_->generation().enabled && parameters_->process() && parameters_->numGeneratedEvents() > 0 ) CG_INFO( "Generator" ) << "Mean generation time / event: " << parameters_->totalGenerationTime()*1.e3/parameters_->numGeneratedEvents() << " ms."; } size_t Generator::numDimensions() const { if ( !parameters_->process() ) return 0; return parameters_->process()->numDimensions(); } void Generator::clearRun() { if ( parameters_->process() ) { parameters_->process()->first_run = true; parameters_->process()->addEventContent(); parameters_->process()->setKinematics( parameters_->kinematics ); } result_ = result_error_ = -1.; { std::ostringstream os; for ( const auto& pr : cepgen::proc::ProcessesHandler::get().modules() ) os << " " << pr; CG_DEBUG( "Generator:clearRun" ) << "Processes handled:" << os.str() << "."; } } void Generator::setParameters( const Parameters& ip ) { parameters_.reset( new Parameters( (Parameters&)ip ) ); // copy constructor } void Generator::printHeader() { std::string tmp; std::ostringstream os; os << "version " << version() << std::endl; std::ifstream hf( "README" ); if ( !hf.good() ) throw CG_WARNING( "Generator" ) << "Failed to open README file."; while ( true ) { if ( !hf.good() ) break; getline( hf, tmp ); os << "\n " << tmp; } hf.close(); CG_INFO( "Generator" ) << os.str(); } double Generator::computePoint( double* x ) { double res = integrand::eval( x, numDimensions(), (void*)parameters_.get() ); std::ostringstream os; - for ( unsigned int i = 0; i < numDimensions(); ++i ) + for ( size_t i = 0; i < numDimensions(); ++i ) os << x[i] << " "; CG_DEBUG( "Generator:computePoint" ) << "Result for x[" << numDimensions() << "] = { " << os.str() << "}:\n\t" << res << "."; return res; } void Generator::computeXsection( double& xsec, double& err ) { CG_INFO( "Generator" ) << "Starting the computation of the process cross-section."; integrate(); xsec = result_; err = result_error_; - //CG_INFO( "Generator" ) << "Total cross section: " if ( xsec < 1.e-2 ) CG_INFO( "Generator" ) << "Total cross section: " << xsec*1.e3 << " +/- " << err*1.e3 << " fb."; else if ( xsec < 0.5e3 ) CG_INFO( "Generator" ) << "Total cross section: " << xsec << " +/- " << err << " pb."; else if ( xsec < 0.5e6 ) CG_INFO( "Generator" ) << "Total cross section: " << xsec*1.e-3 << " +/- " << err*1.e-3 << " nb."; else if ( xsec < 0.5e9 ) CG_INFO( "Generator" ) << "Total cross section: " << xsec*1.e-6 << " +/- " << err*1.e-6 << " µb."; else CG_INFO( "Generator" ) << "Total cross section: " << xsec*1.e-9 << " +/- " << err*1.e-9 << " mb."; } void Generator::integrate() { clearRun(); // first destroy and recreate the integrator instance - if ( !integrator_ ) - integrator_ = std::unique_ptr<Integrator>( new Integrator( numDimensions(), integrand::eval, parameters_.get() ) ); - else if ( integrator_->dimensions() != numDimensions() ) - integrator_.reset( new Integrator( numDimensions(), integrand::eval, parameters_.get() ) ); + if ( !integrator_ || integrator_->dimensions() != numDimensions() ) + integrator_.reset( new Integrator( numDimensions(), integrand::eval, *parameters_ ) ); CG_DEBUG( "Generator:newInstance" ) << "New integrator instance created\n\t" << "Considered topology: " << parameters_->kinematics.mode << " case\n\t" << "Will proceed with " << numDimensions() << "-dimensional integration."; - const int res = integrator_->integrate( result_, result_error_ ); - if ( res != 0 ) - throw CG_FATAL( "Generator" ) - << "Error while computing the cross-section!\n\t" - << "GSL error: " << gsl_strerror( res ) << "."; + integrator_->integrate( result_, result_error_ ); } std::shared_ptr<Event> Generator::generateOneEvent() { integrator_->generateOne(); return parameters_->process()->last_event; } void Generator::generate( std::function<void( const Event&, unsigned long )> callback ) { const utils::Timer tmr; CG_INFO( "Generator" ) << parameters_->generation().maxgen << " events will be generated."; integrator_->generate( parameters_->generation().maxgen, callback ); const double gen_time_s = tmr.elapsed(); CG_INFO( "Generator" ) - << parameters_->numGeneratedEvents() << " events generated " + << parameters_->numGeneratedEvents() << " event" << utils::s( parameters_->numGeneratedEvents() ) + << " generated " << "in " << gen_time_s << " s " << "(" << gen_time_s/parameters_->numGeneratedEvents()*1.e3 << " ms/event)."; } } diff --git a/CepGen/Core/Integrator.cpp b/CepGen/Core/Integrator.cpp index 36b0bb3..1acd9d9 100644 --- a/CepGen/Core/Integrator.cpp +++ b/CepGen/Core/Integrator.cpp @@ -1,491 +1,494 @@ #include "CepGen/Core/Integrator.h" #include "CepGen/Core/GridParameters.h" #include "CepGen/Core/utils.h" #include "CepGen/Core/Exception.h" #include "CepGen/Parameters.h" #include "CepGen/Processes/GenericProcess.h" #include "CepGen/Hadronisers/GenericHadroniser.h" #include "CepGen/Event/Event.h" #include <thread> #include <math.h> #include <gsl/gsl_monte_miser.h> #define COORD(s,i,j) ((s)->xi[(i)*(s)->dim + (j)]) namespace cepgen { - Integrator::Integrator( unsigned int ndim, double integrand( double*, size_t, void* ), Parameters* params ) : - ps_bin_( INVALID_BIN ), input_params_( params, []( Parameters* ){} ), - function_( new gsl_monte_function{ integrand, ndim, (void*)input_params_.get() } ), - rng_( gsl_rng_alloc( input_params_->integration().rng_engine ), gsl_rng_free ), + Integrator::Integrator( unsigned int ndim, double integrand( double*, size_t, void* ), Parameters& params ) : + ps_bin_( INVALID_BIN ), input_params_( params ), + function_( new gsl_monte_function{ integrand, ndim, (void*)&input_params_ } ), + rng_( gsl_rng_alloc( input_params_.integration().rng_engine ) ), grid_( new GridParameters( ndim ) ) { //--- initialise the random number generator - unsigned long seed = ( input_params_->integration().rng_seed > 0 ) - ? input_params_->integration().rng_seed + unsigned long seed = ( input_params_.integration().rng_seed > 0 ) + ? input_params_.integration().rng_seed : time( nullptr ); // seed with time gsl_rng_set( rng_.get(), seed ); //--- a bit of printout for debugging CG_DEBUG( "Integrator:build" ) << "Number of integration dimensions: " << function_->dim << ",\n\t" - << "Number of function calls: " << input_params_->integration().ncvg << ",\n\t" + << "Number of function calls: " << input_params_.integration().ncvg << ",\n\t" << "Random numbers generator: " << gsl_rng_name( rng_.get() ) << "."; - switch ( input_params_->integration().type ) { + switch ( input_params_.integration().type ) { case IntegratorType::Vegas: CG_DEBUG( "Integrator:build" ) << "Vegas parameters:\n\t" - << "Number of iterations in Vegas: " << input_params_->integration().vegas.iterations << ",\n\t" - << "α-value: " << input_params_->integration().vegas.alpha << ",\n\t" - << "Verbosity: " << input_params_->integration().vegas.verbose << ",\n\t" - << "Grid interpolation mode: " << (Integrator::VegasMode)input_params_->integration().vegas.mode << "."; + << "Number of iterations in Vegas: " << input_params_.integration().vegas.iterations << ",\n\t" + << "α-value: " << input_params_.integration().vegas.alpha << ",\n\t" + << "Verbosity: " << input_params_.integration().vegas.verbose << ",\n\t" + << "Grid interpolation mode: " << (Integrator::VegasMode)input_params_.integration().vegas.mode << "."; break; case IntegratorType::MISER: CG_DEBUG( "Integrator:build" ) << "MISER parameters:\n\t" - << "Number of calls: " << input_params_->integration().miser.min_calls << ", " - << "per bisection: " << input_params_->integration().miser.min_calls_per_bisection << ",\n\t" - << "Estimate fraction: " << input_params_->integration().miser.estimate_frac << ",\n\t" - << "α-value: " << input_params_->integration().miser.alpha << ",\n\t" - << "Dither: " << input_params_->integration().miser.dither << "."; + << "Number of calls: " << input_params_.integration().miser.min_calls << ", " + << "per bisection: " << input_params_.integration().miser.min_calls_per_bisection << ",\n\t" + << "Estimate fraction: " << input_params_.integration().miser.estimate_frac << ",\n\t" + << "α-value: " << input_params_.integration().miser.alpha << ",\n\t" + << "Dither: " << input_params_.integration().miser.dither << "."; break; case IntegratorType::plain: break; } } Integrator::~Integrator() {} //----------------------------------------------------------------------------------------------- // cross section computation part //----------------------------------------------------------------------------------------------- - int + void Integrator::integrate( double& result, double& abserr ) { int res = -1; //--- integration bounds std::vector<double> x_low( function_->dim, 0. ), x_up( function_->dim, 1. ); //--- launch integration - switch ( input_params_->integration().type ) { + switch ( input_params_.integration().type ) { case IntegratorType::plain: { std::unique_ptr<gsl_monte_plain_state,void(*)( gsl_monte_plain_state* )> pln_state( gsl_monte_plain_alloc( function_->dim ), gsl_monte_plain_free ); res = gsl_monte_plain_integrate( function_.get(), &x_low[0], &x_up[0], - function_->dim, input_params_->integration().ncvg, + function_->dim, input_params_.integration().ncvg, rng_.get(), pln_state.get(), &result, &abserr ); } break; case IntegratorType::Vegas: { //----- warmup (prepare the grid) - res = warmupVegas( x_low, x_up, 25000 ); + warmupVegas( x_low, x_up, 25000 ); //----- integration unsigned short it_chisq = 0; do { res = gsl_monte_vegas_integrate( function_.get(), &x_low[0], &x_up[0], - function_->dim, 0.2 * input_params_->integration().ncvg, + function_->dim, 0.2 * input_params_.integration().ncvg, rng_.get(), veg_state_.get(), &result, &abserr ); CG_LOG( "Integrator:integrate" ) << "\t>> at call " << ( ++it_chisq ) << ": " << Form( "average = %10.6f " "sigma = %10.6f chi2 = %4.3f.", result, abserr, gsl_monte_vegas_chisq( veg_state_.get() ) ); } while ( fabs( gsl_monte_vegas_chisq( veg_state_.get() )-1. ) - > input_params_->integration().vegas_chisq_cut-1. ); + > input_params_.integration().vegas_chisq_cut-1. ); CG_DEBUG( "Integrator:integrate" ) << "Vegas grid information:\n\t" << "ran for " << veg_state_->dim << " dimensions, and generated " << veg_state_->bins_max << " bins.\n\t" << "Integration volume: " << veg_state_->vol << "."; grid_->r_boxes = std::pow( veg_state_->bins, function_->dim ); } break; case IntegratorType::MISER: { std::unique_ptr<gsl_monte_miser_state,void(*)( gsl_monte_miser_state* )> mis_state( gsl_monte_miser_alloc( function_->dim ), gsl_monte_miser_free ); - gsl_monte_miser_params_set( mis_state.get(), &input_params_->integration().miser ); + gsl_monte_miser_params_set( mis_state.get(), &input_params_.integration().miser ); res = gsl_monte_miser_integrate( function_.get(), &x_low[0], &x_up[0], - function_->dim, input_params_->integration().ncvg, + function_->dim, input_params_.integration().ncvg, rng_.get(), mis_state.get(), &result, &abserr ); } break; } - input_params_->integration().result = result; - input_params_->integration().err_result = abserr; + input_params_.integration().result = result; + input_params_.integration().err_result = abserr; - if ( input_params_->hadroniser() ) - input_params_->hadroniser()->setCrossSection( result, abserr ); + if ( input_params_.hadroniser() ) + input_params_.hadroniser()->setCrossSection( result, abserr ); - return res; + if ( res != GSL_SUCCESS ) + throw CG_FATAL( "Integrator:integrate" ) + << "Error while computing the cross-section!\n\t" + << "GSL error: " << gsl_strerror( res ) << "."; } - int + void Integrator::warmupVegas( std::vector<double>& x_low, std::vector<double>& x_up, unsigned int ncall ) { // start by preparing the grid/state veg_state_.reset( gsl_monte_vegas_alloc( function_->dim ) ); - gsl_monte_vegas_params_set( veg_state_.get(), &input_params_->integration().vegas ); + gsl_monte_vegas_params_set( veg_state_.get(), &input_params_.integration().vegas ); // then perform a first integration with the given calls count double result = 0., abserr = 0.; - int res = gsl_monte_vegas_integrate( function_.get(), + const int res = gsl_monte_vegas_integrate( function_.get(), &x_low[0], &x_up[0], - function_->dim, ncall, - rng_.get(), veg_state_.get(), + function_->dim, ncall, rng_.get(), veg_state_.get(), &result, &abserr ); // ensure the operation was successful if ( res != GSL_SUCCESS ) throw CG_ERROR( "Integrator:vegas" ) << "Failed to warm-up the Vegas grid.\n\t" << "GSL error: " << gsl_strerror( res ) << "."; CG_INFO( "Integrator:vegas" ) << "Finished the Vegas warm-up."; - return res; } //----------------------------------------------------------------------------------------------- // events generation part //----------------------------------------------------------------------------------------------- void Integrator::generateOne( std::function<void( const Event&, unsigned long )> callback ) { if ( !grid_->gen_prepared ) computeGenerationParameters(); std::vector<double> x( function_->dim, 0. ); //--- correction cycles if ( ps_bin_ != INVALID_BIN ) { bool has_correction = false; while ( !correctionCycle( x, has_correction ) ) {} if ( has_correction ) { storeEvent( x, callback ); return; } } double weight = 0.; //--- normal generation cycle while ( true ) { double y = -1.; //----- select a and reject if fmax is too small while ( true ) { // ... ps_bin_ = uniform() * grid_->max; y = uniform() * grid_->f_max_global; grid_->num[ps_bin_] += 1; if ( y <= grid_->f_max[ps_bin_] ) break; } // shoot a point x in this bin std::vector<unsigned short> grid_n = grid_->n_map.at( ps_bin_ ); for ( unsigned int i = 0; i < function_->dim; ++i ) x[i] = ( uniform() + grid_n[i] ) * GridParameters::INV_M_BIN; // get weight for selected x value weight = eval( x ); if ( weight <= 0. ) continue; if ( weight > y ) break; } if ( weight <= grid_->f_max[ps_bin_] ) ps_bin_ = INVALID_BIN; else { //--- if weight is higher than local or global maximum, // init correction cycle grid_->f_max_old = grid_->f_max[ps_bin_]; grid_->f_max[ps_bin_] = weight; grid_->f_max_diff = weight-grid_->f_max_old; if ( weight > grid_->f_max_global ) grid_->f_max_global = weight; grid_->correc = ( grid_->num[ps_bin_]-1. ) * grid_->f_max_diff / grid_->f_max_global - 1.; CG_DEBUG("Integrator::generateOne") << "Correction " << grid_->correc << " will be applied for phase space bin " << ps_bin_ << "."; } // return with an accepted event if ( weight > 0. ) storeEvent( x, callback ); } void Integrator::generate( unsigned long num_events, std::function<void( const Event&, unsigned long )> callback ) { if ( num_events < 1 ) - num_events = input_params_->generation().maxgen; + num_events = input_params_.generation().maxgen; try { - while ( input_params_->numGeneratedEvents() < num_events ) + while ( input_params_.numGeneratedEvents() < num_events ) generateOne( callback ); } catch ( const Exception& e ) { return; } } bool Integrator::correctionCycle( std::vector<double>& x, bool& has_correction ) { CG_DEBUG_LOOP( "Integrator:correction" ) << "Correction cycles are started.\n\t" << "bin = " << ps_bin_ << "\t" << "correc = " << grid_->correc << "\t" << "corre2 = " << grid_->correc2 << "."; if ( grid_->correc >= 1. ) grid_->correc -= 1.; if ( uniform() < grid_->correc ) { grid_->correc = -1.; std::vector<double> xtmp( function_->dim ); // Select x values in phase space bin const std::vector<unsigned short> grid_n = grid_->n_map.at( ps_bin_ ); for ( unsigned int k = 0; k < function_->dim; ++k ) xtmp[k] = ( uniform() + grid_n[k] ) * GridParameters::INV_M_BIN; const double weight = eval( xtmp ); // Parameter for correction of correction if ( weight > grid_->f_max[ps_bin_] ) { grid_->f_max2 = std::max( grid_->f_max2, weight ); grid_->correc += 1.; grid_->correc2 -= 1.; } // Accept event if ( weight >= grid_->f_max_diff*uniform() + grid_->f_max_old ) { x = xtmp; has_correction = true; return true; } return false; } // Correction if too big weight is found while correction // (All your bases are belong to us...) if ( grid_->f_max2 > grid_->f_max[ps_bin_] ) { grid_->f_max_old = grid_->f_max[ps_bin_]; grid_->f_max[ps_bin_] = grid_->f_max2; grid_->f_max_diff = grid_->f_max2-grid_->f_max_old; grid_->correc = ( grid_->num[ps_bin_]-1. ) * grid_->f_max_diff / grid_->f_max_global; if ( grid_->f_max2 >= grid_->f_max_global ) { grid_->correc *= grid_->f_max2 / grid_->f_max_global; grid_->f_max_global = grid_->f_max2; } grid_->correc -= grid_->correc2; grid_->correc2 = 0.; grid_->f_max2 = 0.; return false; } return true; } bool Integrator::storeEvent( const std::vector<double>& x, std::function<void( const Event&, unsigned long )> callback ) { //--- start by computing the matrix element for that point const double weight = eval( x ); //--- reject if unphysical if ( weight <= 0. ) return false; { - if ( input_params_->numGeneratedEvents() % input_params_->generation().gen_print_every == 0 ) { + if ( input_params_.numGeneratedEvents() % input_params_.generation().gen_print_every == 0 ) { CG_INFO( "Integrator:store" ) - << "Generated events: " << input_params_->numGeneratedEvents(); - input_params_->process()->last_event->dump(); + << "Generated events: " << input_params_.numGeneratedEvents(); + input_params_.process()->last_event->dump(); } - const Event& last_event = *input_params_->process()->last_event; + const Event& last_event = *input_params_.process()->last_event; if ( callback ) - callback( last_event, input_params_->numGeneratedEvents() ); - input_params_->addGenerationTime( last_event.time_total ); + callback( last_event, input_params_.numGeneratedEvents() ); + input_params_.addGenerationTime( last_event.time_total ); } return true; } //----------------------------------------------------------------------------------------------- // initial preparation run before the generation of unweighted events //----------------------------------------------------------------------------------------------- void Integrator::computeGenerationParameters() { - input_params_->setStorage( false ); + input_params_.setStorage( false ); - if ( input_params_->generation().treat - && input_params_->integration().type != IntegratorType::Vegas ) { + if ( input_params_.generation().treat + && input_params_.integration().type != IntegratorType::Vegas ) { CG_INFO( "Integrator:setGen" ) << "Treat switched on without a proper Vegas grid; running a warm-up beforehand."; std::vector<double> x_low( function_->dim, 0. ), x_up( function_->dim, 1. ); - int res = warmupVegas( x_low, x_up, 25000 ); - if ( res != GSL_SUCCESS ) + try { + warmupVegas( x_low, x_up, 25000 ); + } catch ( const Exception& ) { throw CG_FATAL( "Integrator::setGen" ) << "Failed to perform a Vegas warm-up.\n\t" << "Try to re-run while disabling integrand treatment..."; + } } CG_INFO( "Integrator:setGen" ) - << "Preparing the grid (" << input_params_->generation().num_points << " points/bin) " + << "Preparing the grid (" << input_params_.generation().num_points << " points/bin) " << "for the generation of unweighted events."; - const double inv_num_points = 1./input_params_->generation().num_points; + const double inv_num_points = 1./input_params_.generation().num_points; std::vector<double> x( function_->dim, 0. ); std::vector<unsigned short> n( function_->dim, 0 );; // ... double sum = 0., sum2 = 0., sum2p = 0.; //--- main loop for ( unsigned int i = 0; i < grid_->max; ++i ) { unsigned int jj = i; for ( unsigned int j = 0; j < function_->dim; ++j ) { unsigned int tmp = jj*GridParameters::INV_M_BIN; n[j] = jj-tmp*GridParameters::M_BIN; jj = tmp; } grid_->n_map[i] = n; if ( CG_EXCEPT_MATCH( "Integrator:setGen", debugInsideLoop ) ) { std::ostringstream os; for ( const auto& ni : n ) os << ni << " "; CG_DEBUG_LOOP( "Integrator:setGen" ) << "n-vector for bin " << i << ": " << os.str(); } double fsum = 0., fsum2 = 0.; - for ( unsigned int j = 0; j < input_params_->generation().num_points; ++j ) { + for ( unsigned int j = 0; j < input_params_.generation().num_points; ++j ) { for ( unsigned int k = 0; k < function_->dim; ++k ) x[k] = ( uniform()+n[k] ) * GridParameters::INV_M_BIN; const double weight = eval( x ); grid_->f_max[i] = std::max( grid_->f_max[i], weight ); fsum += weight; fsum2 += weight*weight; } const double av = fsum*inv_num_points, av2 = fsum2*inv_num_points, sig2 = av2-av*av; sum += av; sum2 += av2; sum2p += sig2; grid_->f_max_global = std::max( grid_->f_max_global, grid_->f_max[i] ); // per-bin debugging loop if ( CG_EXCEPT_MATCH( "Integrator:setGen", debugInsideLoop ) ) { const double sig = sqrt( sig2 ); const double eff = ( grid_->f_max[i] != 0. ) ? grid_->f_max[i]/av : 1.e4; std::ostringstream os; for ( unsigned int j = 0; j < function_->dim; ++j ) os << ( j != 0 ? ", " : "" ) << n[j]; CG_DEBUG_LOOP( "Integrator:setGen" ) << "In iteration #" << i << ":\n\t" << "av = " << av << "\n\t" << "sig = " << sig << "\n\t" << "fmax = " << grid_->f_max[i] << "\n\t" << "eff = " << eff << "\n\t" << "n = (" << os.str() << ")"; } } // end of main loop const double inv_max = 1./grid_->max; sum *= inv_max; sum2 *= inv_max; sum2p *= inv_max; const double sig = sqrt( sum2-sum*sum ), sigp = sqrt( sum2p ); double eff1 = 0.; for ( unsigned int i = 0; i < grid_->max; ++i ) eff1 += sum/grid_->max*grid_->f_max[i]; const double eff2 = sum/grid_->f_max_global; CG_DEBUG( "Integrator:setGen" ) << "Average function value = sum = " << sum << "\n\t" << "Average squared function value = sum2 = " << sum2 << "\n\t" << "Overall standard deviation = sig = " << sig << "\n\t" << "Average standard deviation = sigp = " << sigp << "\n\t" << "Maximum function value = f_max = " << grid_->f_max_global << "\n\t" << "Average inefficiency = eff1 = " << eff1 << "\n\t" << "Overall inefficiency = eff2 = " << eff2; grid_->gen_prepared = true; - input_params_->setStorage( true ); + input_params_.setStorage( true ); CG_INFO( "Integrator:setGen" ) << "Grid prepared! Now launching the production."; } //------------------------------------------------------------------------------------------------ // helper / alias methods //------------------------------------------------------------------------------------------------ unsigned short Integrator::dimensions() const { if ( !function_ ) return 0; return function_->dim; } double Integrator::eval( const std::vector<double>& x ) { - if ( !input_params_->generation().treat ) - return function_->f( (double*)&x[0], function_->dim, (void*)input_params_.get() ); + if ( !input_params_.generation().treat ) + return function_->f( (double*)&x[0], function_->dim, (void*)&input_params_ ); //--- treatment of the integration grid double w = grid_->r_boxes; std::vector<double> x_new( x.size() ); for ( unsigned short j = 0; j < function_->dim; ++j ) { //--- find surrounding coordinates and interpolate const double z = x[j]*veg_state_->bins; const unsigned int id = z; // coordinate of point before const double rel_pos = z-id; // position between coordinates (norm.) const double bin_width = ( id == 0 ) ? COORD( veg_state_, 1, j ) : COORD( veg_state_, id+1, j )-COORD( veg_state_, id, j ); //--- build new coordinate from linear interpolation x_new[j] = COORD( veg_state_, id+1, j )-bin_width*( 1.-rel_pos ); w *= bin_width; } - return w*function_->f( (double*)&x_new[0], function_->dim, (void*)input_params_.get() ); + return w*function_->f( (double*)&x_new[0], function_->dim, (void*)&input_params_ ); } double Integrator::uniform() const { return gsl_rng_uniform( rng_.get() ); } //------------------------------------------------------------------------------------------------ std::ostream& operator<<( std::ostream& os, const IntegratorType& type ) { switch ( type ) { case IntegratorType::plain: return os << "plain"; case IntegratorType::Vegas: return os << "Vegas"; case IntegratorType::MISER: return os << "MISER"; } return os; } std::ostream& operator<<( std::ostream& os, const Integrator::VegasMode& mode ) { switch ( mode ) { case Integrator::VegasMode::importance: return os << "importance"; case Integrator::VegasMode::importanceOnly: return os << "importance-only"; case Integrator::VegasMode::stratified: return os << "stratified"; } return os; } } diff --git a/CepGen/Core/Integrator.h b/CepGen/Core/Integrator.h index 0f5e408..40259c3 100644 --- a/CepGen/Core/Integrator.h +++ b/CepGen/Core/Integrator.h @@ -1,112 +1,115 @@ #ifndef CepGen_Core_Integrator_h #define CepGen_Core_Integrator_h #include <vector> #include <memory> #include <functional> #include <string.h> #include <gsl/gsl_monte.h> #include <gsl/gsl_monte_vegas.h> #include <gsl/gsl_rng.h> namespace cepgen { class Parameters; class Event; class GridParameters; namespace utils { class Timer; } /// Flavour of integration algorithm enum class IntegratorType { plain = 0, ///< Simple trial-and-error algorithm Vegas = 1, ///< Vegas algorithm (G.P. Lepage, 1977 \cite Lepage:1977sw) MISER = 2 ///< MISER stratified sampling algorithm }; /// Monte-Carlo integrator instance class Integrator { public: enum class VegasMode { importance = 1, importanceOnly = 0, stratified = -1 }; /** * Book the memory slots and structures for the integrator * \note Three integration algorithms are currently supported: * * the plain algorithm randomly sampling points in the phase space * * the Vegas algorithm developed by P. Lepage, as documented in \cite Lepage:1977sw * * the MISER algorithm developed by W.H. Press and G.R. Farrar, as documented in \cite Press:1989vk. * \param[in] ndim Number of dimensions on which the function will be integrated * \param[in] integrand Function to be integrated * \param[inout] params Run parameters to define the phase space on which this integration is performed (embedded in an Parameters object) */ - Integrator( unsigned int ndim, double integrand(double*,size_t,void*), Parameters* params ); + Integrator( unsigned int ndim, double integrand(double*,size_t,void*), Parameters& params ); /// Class destructor ~Integrator(); /** * Algorithm to perform the n-dimensional Monte Carlo integration of a given function. * \author This C++ implementation: GSL * \param[out] result_ The cross section as integrated for the given phase space restrictions * \param[out] abserr_ The error associated to the computed cross section - * \return 0 if the integration was performed successfully */ - int integrate( double& result_, double& abserr_ ); + void integrate( double& result_, double& abserr_ ); /// Dimensional size of the phase space unsigned short dimensions() const; /// Generate a single event void generateOne( std::function<void( const Event&, unsigned long )> callback = nullptr ); /// Launch the event generation for a given number of events void generate( unsigned long num_events = 0, std::function<void( const Event&, unsigned long )> callback = nullptr ); private: /** * Store the event characterized by its _ndim-dimensional point in * the phase space to the output file * \brief Store the event in the output file * \param[in] x The d-dimensional point in the phase space defining the unique event to store * \return A boolean stating whether or not the event could be saved */ bool storeEvent( const std::vector<double>& x, std::function<void( const Event&, unsigned long )> callback = nullptr ); /// Start the correction cycle on the grid /// \param x Point in the phase space considered /// \param has_correction Correction cycle started? bool correctionCycle( std::vector<double>& x, bool& has_correction ); /// Prepare Vegas for an integration/event generation cycle - int warmupVegas( std::vector<double>& x_low, std::vector<double>& x_up, unsigned int ncall ); + void warmupVegas( std::vector<double>& x_low, std::vector<double>& x_up, unsigned int ncall ); /** * Set all the generation mode variables and align them to the * integration grid set while computing the cross-section * \brief Prepare the class for events generation */ void computeGenerationParameters(); /// Generate a uniformly distributed (between 0 and 1) random number double uniform() const; /// Compute the function value at the given phase space point double eval( const std::vector<double>& x ); /// Selected bin at which the function will be evaluated int ps_bin_; static constexpr int INVALID_BIN = -1; /// List of parameters to specify the integration range and the /// physics determining the phase space - std::shared_ptr<Parameters> input_params_; + Parameters& input_params_; /// GSL structure storing the function to be integrated by this /// integrator instance (along with its parameters) std::unique_ptr<gsl_monte_function> function_; + struct gsl_rng_deleter + { + inline void operator()( gsl_rng* rng ) { gsl_rng_free( rng ); } + }; /// Instance of random number generator service - std::unique_ptr<gsl_rng,void(*)( gsl_rng* )> rng_; + std::unique_ptr<gsl_rng,gsl_rng_deleter> rng_; /// Set of parameters for the integration/event generation grid std::unique_ptr<GridParameters> grid_; /// A trivial deleter for the Vegas integrator struct gsl_monte_vegas_deleter { void operator()( gsl_monte_vegas_state* state ) { gsl_monte_vegas_free( state ); } }; /// A Vegas integrator state for integration (optional) and/or /// "treated" event generation std::unique_ptr<gsl_monte_vegas_state,gsl_monte_vegas_deleter> veg_state_; }; std::ostream& operator<<( std::ostream&, const IntegratorType& ); std::ostream& operator<<( std::ostream&, const Integrator::VegasMode& ); } #endif diff --git a/CepGen/Core/utils.h b/CepGen/Core/utils.h index 4728216..bb63dee 100644 --- a/CepGen/Core/utils.h +++ b/CepGen/Core/utils.h @@ -1,38 +1,50 @@ #ifndef CepGen_Core_utils_h #define CepGen_Core_utils_h #include <string> +#include <vector> +#include <numeric> namespace cepgen { - /// Add a closing "s" when needed - inline const char* s( unsigned short num ) { return ( num > 1 ) ? "s" : ""; } /// Format a string using a printf style format descriptor. std::string Form( const std::string fmt, ... ); /// Human-readable boolean printout inline const char* yesno( const bool& test ) { return ( test ) ? "\033[32;1myes\033[0m" : "\033[31;1mno\033[0m"; } //inline const char* boldify( const char* str ) { const std::string out = std::string( "\033[33;1m" ) + std::string( str ) + std::string( "\033[0m" ); return out.c_str(); } /// Boldify a string for TTY-type output streams inline std::string boldify( const std::string& str ) { return Form( "\033[1m%s\033[0m", str.c_str() ); } /// Boldify a string for TTY-type output streams inline std::string boldify( const char* str ) { return boldify( std::string( str ) ); } /// Boldify a double floating point number for TTY-type output streams inline std::string boldify( const double& dbl ) { return boldify( Form("%.2f", dbl ) ); } /// Boldify an integer for TTY-type output streams inline std::string boldify( const int& i ) { return boldify( Form("% d", i ) ); } /// Boldify an unsigned integer for TTY-type output streams inline std::string boldify( const unsigned int& ui ) { return boldify( Form("%d", ui ) ); } /// Boldify an unsigned long integer for TTY-type output streams inline std::string boldify( const unsigned long& ui ) { return boldify( Form("%lu", ui ) ); } /// TTY-type enumeration of colours enum class Colour { gray = 30, red = 31, green = 32, yellow = 33, blue = 34, purple = 35 }; /// Colourise a string for TTY-type output streams inline std::string colourise( const std::string& str, const Colour& col ) { return Form( "\033[%d%s\033[0m", (int)col, str.c_str() ); } /// Replace all occurences of a text by another size_t replace_all( std::string& str, const std::string& from, const std::string& to ); + namespace utils + { + /// Add a trailing "s" when needed + inline const char* s( unsigned short num ) { return ( num > 1 ) ? "s" : ""; } + /// Helper to print a vector + template<class T> std::string repr( const std::vector<T>& vec, const std::string& sep = "," ) { + return std::accumulate( std::next( vec.begin() ), vec.end(), + std::to_string( *vec.begin() ), [&sep]( std::string str, T xv ) { + return std::move( str )+sep+std::to_string( xv ); + } ); + } + } } /// Provide a random number generated along a uniform distribution between 0 and 1 #define drand() static_cast<double>( rand()/RAND_MAX ) #endif