diff --git a/CepGen/IO/GridHandler.h b/CepGen/IO/GridHandler.h index 18ba207..5bea829 100644 --- a/CepGen/IO/GridHandler.h +++ b/CepGen/IO/GridHandler.h @@ -1,303 +1,316 @@ #ifndef CepGen_IO_GridHandler_h #define CepGen_IO_GridHandler_h #include <gsl/gsl_version.h> #ifdef GSL_MAJOR_VERSION # if GSL_MAJOR_VERSION > 2 || ( GSL_MAJOR_VERSION == 2 && GSL_MINOR_VERSION >= 1 ) # include <gsl/gsl_interp2d.h> # include <gsl/gsl_spline2d.h> # define GOOD_GSL 1 # endif #endif #include <gsl/gsl_interp.h> #include <gsl/gsl_spline.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_math.h> #include "CepGen/Core/Exception.h" #include <vector> #include <map> namespace CepGen { class StructureFunctions; enum struct GridType { linear = 0, logarithmic = 1, square = 2 }; /// A generic class for D-dimensional grid interpolation /// \param N Number of values handled per point template <size_t D,size_t N=1> class GridHandler { public: typedef std::vector<double> coord_t; typedef std::array<double,N> values_t; public: explicit GridHandler( const GridType& grid_type ) : grid_type_( grid_type ), accel_{} { for ( size_t i = 0; i < D; ++i ) accel_.emplace_back( gsl_interp_accel_alloc(), gsl_interp_accel_free ); } ~GridHandler() {} /// Interpolate a point to a given coordinate values_t eval( coord_t in_coords ) const { values_t out; coord_t coord = in_coords; switch ( grid_type_ ) { case GridType::logarithmic: { for ( auto& c : coord ) c = log10( c ); } break; case GridType::square: { for ( auto& c : coord ) c *= c; } break; default: break; } //--- dimension of the vector space coordinate to evaluate switch ( D ) { case 1: { for ( size_t i = 0; i < N; ++i ) { int res = gsl_spline_eval_e( splines_1d_.at( i ).get(), coord.at( 0 ), accel_.at( 0 ).get(), &out[i] ); if ( res != GSL_SUCCESS ) { out[i] = 0.; CG_WARNING( "GridHandler" ) << "Failed to evaluate the grid value (N=" << i << ") " << "for x = " << in_coords.at( 0 ) << ". " << "GSL error: " << gsl_strerror( res ); } } } break; case 2: { #ifdef GOOD_GSL const double x = coord.at( 0 ), y = coord.at( 1 ); for ( size_t i = 0; i < N; ++i ) { int res = gsl_spline2d_eval_e( splines_2d_.at( i ).get(), x, y, accel_.at( 0 ).get(), accel_.at( 1 ).get(), &out[i] ); if ( res != GSL_SUCCESS ) { out[i] = 0.; CG_WARNING( "GridHandler" ) << "Failed to evaluate the grid value (N=" << i << ") " << "for x = " << in_coords.at( 0 ) << " / y = " << in_coords.at( 1 ) << ". " << "GSL error: " << gsl_strerror( res ); } } #else //--- retrieve the indices of the bin in the set coord_t before, after; findIndices( coord, before, after ); //--- find boundaries values const gridpoint_t& ext_11 = values_raw_.at( { before[0], before[1] } ), &ext_12 = values_raw_.at( { before[0], after[1] } ), &ext_21 = values_raw_.at( { after[0], before[1] } ), &ext_22 = values_raw_.at( { after[0], after[1] } ); //--- now that we have the boundaries, we may interpolate coord_t c_d( D ); for ( size_t i = 0; i < D; ++i ) c_d[i] = ( after[i] != before[i] ) ? ( coord.at( i )-before[i] )/( after[i]-before[i] ) : 0.; const gridpoint_t ext_1 = ext_11*( 1.-c_d[0] ) + ext_21*c_d[0]; const gridpoint_t ext_2 = ext_12*( 1.-c_d[0] ) + ext_22*c_d[0]; out = ext_1*( 1.-c_d[1] )+ext_2*c_d[1]; #endif } break; case 3: { //--- retrieve the indices of the bin in the set coord_t before, after; findIndices( coord, before, after ); //--- find boundaries values const gridpoint_t& ext_111 = values_raw_.at( { before[0], before[1], before[2] } ), &ext_112 = values_raw_.at( { before[0], before[1], after[2] } ), &ext_121 = values_raw_.at( { before[0], after[1], before[2] } ), &ext_122 = values_raw_.at( { before[0], after[1], after[2] } ), &ext_211 = values_raw_.at( { after[0], before[1], before[2] } ), &ext_212 = values_raw_.at( { after[0], before[1], after[2] } ), &ext_221 = values_raw_.at( { after[0], after[1], before[2] } ), &ext_222 = values_raw_.at( { after[0], after[1], after[2] } ); //--- now that we have the boundaries, we may interpolate coord_t c_d( D ); for ( size_t i = 0; i < D; ++i ) c_d[i] = ( after[i] != before[i] ) ? ( coord.at( i )-before[i] )/( after[i]-before[i] ) : 0.; const gridpoint_t ext_11 = ext_111*( 1.-c_d[0] ) + ext_211*c_d[0]; const gridpoint_t ext_12 = ext_112*( 1.-c_d[0] ) + ext_212*c_d[0]; const gridpoint_t ext_21 = ext_121*( 1.-c_d[0] ) + ext_221*c_d[0]; const gridpoint_t ext_22 = ext_122*( 1.-c_d[0] ) + ext_222*c_d[0]; const gridpoint_t ext_1 = ext_11*( 1.-c_d[1] ) + ext_21*c_d[1]; const gridpoint_t ext_2 = ext_12*( 1.-c_d[1] ) + ext_22*c_d[1]; out = ext_1*( 1.-c_d[2] )+ext_2*c_d[2]; } break; default: throw CG_FATAL( "GridHandler" ) << "Unsupported number of dimensions: " << N << ".\n\t" << "Please contact the developers to add such a new feature."; } return out; } /// Insert a new value in the grid void insert( coord_t coord, values_t value ) { auto mod_coord = coord; if ( grid_type_ != GridType::linear ) for ( auto& c : mod_coord ) switch ( grid_type_ ) { case GridType::logarithmic: c = log10( c ); break; case GridType::square: c *= c; break; default: break; } values_raw_[mod_coord] = value; } /// Return the list of values handled in the grid std::map<coord_t,values_t> values() const { return values_raw_; } /// Initialise the grid and all useful interpolators/accelerators void init() { if ( values_raw_.empty() ) CG_ERROR( "GridHandler" ) << "Empty grid."; gsl_set_error_handler_off(); //--- start by building grid coordinates from raw values for ( auto& c : coords_ ) c.clear(); for ( const auto& val : values_raw_ ) { unsigned short i = 0; for ( const auto& c : val.first ) { if ( std::find( coords_.at( i ).begin(), coords_.at( i ).end(), c ) == coords_.at( i ).end() ) coords_.at( i ).emplace_back( c ); ++i; } } for ( auto& c : coords_ ) std::sort( c.begin(), c.end() ); { //--- debugging of the grid coordinates std::ostringstream os; unsigned short i = 0; for ( const auto& cs : coords_ ) { os << "\n>> coordinate " << (i++) << " has " << cs.size() << " member" << ( cs.size() > 1 ? "s" : "" ) << ":"; unsigned short j = 0; for ( const auto& val : cs ) os << ( j++ % 20 == 0 ? "\n " : " " ) << val; } CG_DEBUG( "GridHandler" ) << "Grid dump:" << os.str(); } //--- particularise by dimension switch ( D ) { case 1: { //--- x |-> (f1,...) const gsl_interp_type* type = gsl_interp_cspline; //const gsl_interp_type* type = gsl_interp_steffen; #ifdef GOOD_GSL const unsigned short min_size = gsl_interp_type_min_size( type ); #else const unsigned short min_size = type->min_size; #endif if ( min_size >= values_raw_.size() ) throw CG_FATAL( "GridHandler" ) << "Not enough points for \"" << type->name << "\" type of interpolation.\n\t" << "Minimum required: " << min_size << ", got " << values_raw_.size() << "!"; for ( size_t i = 0; i < N; ++i ) { values_[i].reset( new double[values_raw_.size()] ); splines_1d_.emplace_back( gsl_spline_alloc( type, values_raw_.size() ), gsl_spline_free ); } std::vector<double> x_vec; unsigned short i = 0; for ( const auto& vals : values_raw_ ) { x_vec.emplace_back( vals.first.at( 0 ) ); unsigned short j = 0; for ( const auto& val : vals.second ) values_[j++].get()[i++] = val; } for ( unsigned short i = 0; i < splines_1d_.size(); ++i ) gsl_spline_init( splines_1d_.at( i ).get(), &x_vec[0], values_[i].get(), values_raw_.size() ); } break; case 2: { //--- (x,y) |-> (f1,...) #ifdef GOOD_GSL const gsl_interp2d_type* type = gsl_interp2d_bilinear; splines_2d_.clear(); for ( size_t i = 0; i < N; ++i ) { values_[i].reset( new double[coords_.at( 0 ).size() * coords_.at( 1 ).size()] ); splines_2d_.emplace_back( gsl_spline2d_alloc( type, coords_.at( 0 ).size(), coords_.at( 1 ).size() ), gsl_spline2d_free ); } // second loop over all points to populate the grid for ( const auto& val : values_raw_ ) { double val_x = val.first.at( 0 ), val_y = val.first.at( 1 ); // retrieve the index of the bin in the set const unsigned short id_x = std::distance( coords_.at( 0 ).begin(), std::lower_bound( coords_.at( 0 ).begin(), coords_.at( 0 ).end(), val_x ) ); const unsigned short id_y = std::distance( coords_.at( 1 ).begin(), std::lower_bound( coords_.at( 1 ).begin(), coords_.at( 1 ).end(), val_y ) ); for ( unsigned short i = 0; i < splines_2d_.size(); ++i ) gsl_spline2d_set( splines_2d_.at( i ).get(), values_[i].get(), id_x, id_y, val.second[i] ); } // initialise splines objects const coord_t& x_vec = coords_.at( 0 ), &y_vec = coords_.at( 1 ); for ( unsigned short i = 0; i < splines_2d_.size(); ++i ) gsl_spline2d_init( splines_2d_.at( i ).get(), &x_vec[0], &y_vec[0], values_[i].get(), x_vec.size(), y_vec.size() ); #else CG_WARNING( "GridHandler" ) << "GSL version ≥ 2.1 is required for spline bilinear interpolation.\n\t" << "Version " << GSL_VERSION << " is installed on this system!\n\t" << "Will use a simple bilinear approximation instead."; #endif } break; } } + std::array<std::pair<double,double>,D> boundaries() const { + std::array<std::pair<double,double>,D> out; + unsigned short i = 0; + for ( const auto& c : coords_ ) { + if ( c.size() < 1 ) + out[i++] = { std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity() }; + else if ( c.size() < 2 ) + out[i++] = { *c.begin(), std::numeric_limits<double>::infinity() }; + else + out[i++] = { *c.begin(), *c.rbegin() }; + } + return out; + } protected: GridType grid_type_; /// List of coordinates and associated value(s) in the grid std::map<coord_t,values_t> values_raw_; std::vector<std::unique_ptr<gsl_interp_accel,void(*)( gsl_interp_accel* )> > accel_; std::vector<std::unique_ptr<gsl_spline,void(*)( gsl_spline* )> > splines_1d_; #ifdef GOOD_GSL std::vector<std::unique_ptr<gsl_spline2d,void(*)( gsl_spline2d* )> > splines_2d_; #endif std::array<coord_t,D> coords_; std::array<std::unique_ptr<double[]>,N> values_; private: void findIndices( const coord_t& coord, coord_t& min, coord_t& max ) const { min.reserve( D ); max.reserve( D ); for ( size_t i = 0; i < D; ++i ) { const auto& c = coords_.at( i ); if ( coord.at( i ) < c.front() ) min[i] = max[i] = c.front(); else if ( coord.at( i ) > c.back() ) min[i] = max[i] = c.back(); else { auto it_coord = std::lower_bound( c.begin(), c.end(), coord.at( i ) ); min[i] = *it_coord; max[i] = ( it_coord != c.end() ) ? *( it_coord++ ) : *it_coord; } } } struct gridpoint_t : values_t { gridpoint_t() : values_t() {} gridpoint_t( const values_t& arr ) : values_t( arr ) {} gridpoint_t operator*( double c ) const { gridpoint_t out = *this; for ( auto& a : out ) a *= c; return out; } gridpoint_t operator+( const gridpoint_t& rhs ) const { gridpoint_t out = *this; for ( size_t i = 0; i < out.size(); ++i ) out[i] += rhs[i]; return out; } }; }; } #endif