cepgen is hosted by Hepforge, IPPP Durham
CepGen 1.2.5
Central exclusive processes event generator
Loading...
Searching...
No Matches
GridHandler.cpp
Go to the documentation of this file.
1/*
2 * CepGen: a central exclusive processes event generator
3 * Copyright (C) 2018-2024 Laurent Forthomme
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <gsl/gsl_errno.h>
20#include <gsl/gsl_math.h>
21
22#include <limits>
23
26
27//#define GRID_HANDLER_DEBUG 1
28
29namespace cepgen {
30 template <size_t D, size_t N>
31 GridHandler<D, N>::GridHandler(const GridType& grid_type) : grid_type_(grid_type) {
32 for (size_t i = 0; i < D; ++i)
33 accel_.emplace_back(gsl_interp_accel_alloc(), gsl_interp_accel_free);
34 }
35
36 template <size_t D, size_t N>
38 if (!init_)
39 throw CG_FATAL("GridHandler") << "Grid extrapolator called but not initialised!";
40
41 values_t out{};
42 coord_t coord = in_coords;
43 switch (grid_type_) {
45 std::transform(coord.begin(), coord.end(), coord.begin(), [](const auto& c) { return log10(c); });
46 } break;
47 case GridType::square: {
48 std::transform(coord.begin(), coord.end(), coord.begin(), [](const auto& c) { return c * c; });
49 } break;
50 default:
51 break;
52 }
53 //--- dimension of the vector space coordinate to evaluate
54 switch (D) {
55 case 1: {
56 for (size_t i = 0; i < N; ++i) {
57 int res = gsl_spline_eval_e(splines_1d_.at(i).get(), coord.at(0), accel_.at(0).get(), &out[i]);
58 if (res != GSL_SUCCESS) {
59 out[i] = 0.;
60 CG_WARNING("GridHandler") << "Failed to evaluate the value (N=" << i << ") "
61 << "for x = " << in_coords.at(0) << " in grid with boundaries " << boundaries()
62 << ". GSL error: " << gsl_strerror(res);
63 }
64 }
65 } break;
66 case 2: {
67#ifdef GSL_VERSION_ABOVE_2_1
68 const double x = coord.at(0), y = coord.at(1);
69 for (size_t i = 0; i < N; ++i) {
70 int res = gsl_spline2d_eval_e(splines_2d_.at(i).get(), x, y, accel_.at(0).get(), accel_.at(1).get(), &out[i]);
71 if (res != GSL_SUCCESS) {
72 out[i] = 0.;
73 CG_WARNING("GridHandler") << "Failed to evaluate the value (N=" << i << ") "
74 << "for x = " << x << " / y = " << y << " in grid with boundaries "
75 << boundaries() << ". GSL error: " << gsl_strerror(res);
76 }
77 }
78#else
79 //--- retrieve the indices of the bin in the set
80 coord_t before(D), after(D);
81 findIndices(coord, before, after);
82 //--- find boundaries values
83 const gridpoint_t &ext_11 = values_raw_.at({before[0], before[1]}),
84 &ext_12 = values_raw_.at({before[0], after[1]}),
85 &ext_21 = values_raw_.at({after[0], before[1]}),
86 &ext_22 = values_raw_.at({after[0], after[1]});
87 //--- now that we have the boundaries, we may interpolate
88 coord_t c_d(D);
89 for (size_t i = 0; i < D; ++i)
90 c_d[i] = (after[i] != before[i]) ? (coord.at(i) - before[i]) / (after[i] - before[i]) : 0.;
91 const gridpoint_t ext_1 = ext_11 * (1. - c_d[0]) + ext_21 * c_d[0];
92 const gridpoint_t ext_2 = ext_12 * (1. - c_d[0]) + ext_22 * c_d[0];
93 out = ext_1 * (1. - c_d[1]) + ext_2 * c_d[1];
94#endif
95 } break;
96 case 3: {
97 //--- retrieve the indices of the bin in the set
98 coord_t before(D), after(D);
99 findIndices(coord, before, after);
100 //--- find boundaries values
101 const gridpoint_t &ext_111 = values_raw_.at({before[0], before[1], before[2]}),
102 &ext_112 = values_raw_.at({before[0], before[1], after[2]}),
103 &ext_121 = values_raw_.at({before[0], after[1], before[2]}),
104 &ext_122 = values_raw_.at({before[0], after[1], after[2]}),
105 &ext_211 = values_raw_.at({after[0], before[1], before[2]}),
106 &ext_212 = values_raw_.at({after[0], before[1], after[2]}),
107 &ext_221 = values_raw_.at({after[0], after[1], before[2]}),
108 &ext_222 = values_raw_.at({after[0], after[1], after[2]});
109 //--- now that we have the boundaries, we may interpolate
110 coord_t c_d(D);
111 for (size_t i = 0; i < D; ++i)
112 c_d[i] = (after[i] != before[i]) ? (coord.at(i) - before[i]) / (after[i] - before[i]) : 0.;
113 const gridpoint_t ext_11 = ext_111 * (1. - c_d[0]) + ext_211 * c_d[0];
114 const gridpoint_t ext_12 = ext_112 * (1. - c_d[0]) + ext_212 * c_d[0];
115 const gridpoint_t ext_21 = ext_121 * (1. - c_d[0]) + ext_221 * c_d[0];
116 const gridpoint_t ext_22 = ext_122 * (1. - c_d[0]) + ext_222 * c_d[0];
117 const gridpoint_t ext_1 = ext_11 * (1. - c_d[1]) + ext_21 * c_d[1];
118 const gridpoint_t ext_2 = ext_12 * (1. - c_d[1]) + ext_22 * c_d[1];
119 out = ext_1 * (1. - c_d[2]) + ext_2 * c_d[2];
120 } break;
121 default:
122 throw CG_FATAL("GridHandler") << "Unsupported number of dimensions: " << N << ".\n\t"
123 << "Please contact the developers to add such a new feature.";
124 }
125 return out;
126 }
127
128 template <size_t D, size_t N>
130 auto mod_coord = coord;
131 if (grid_type_ != GridType::linear)
132 for (auto& c : mod_coord)
133 switch (grid_type_) {
135 c = log10(c);
136 break;
137 case GridType::square:
138 c *= c;
139 break;
140 default:
141 break;
142 }
143 if (values_raw_.count(mod_coord) != 0)
144 CG_WARNING("GridHandler") << "Duplicate coordinate detected for x=" << coord << ".";
145 values_raw_[mod_coord] = value;
146 init_ = false;
147 }
148
149 template <size_t D, size_t N>
151 if (values_raw_.empty())
152 throw CG_ERROR("GridHandler") << "Empty grid.";
153 gsl_set_error_handler_off();
154 //--- start by building grid coordinates from raw values
155 for (auto& c : coords_)
156 c.clear();
157 for (const auto& val : values_raw_) {
158 unsigned short i = 0;
159 for (const auto& c : val.first) {
160 if (std::find(coords_.at(i).begin(), coords_.at(i).end(), c) == coords_.at(i).end())
161 coords_.at(i).emplace_back(c);
162 ++i;
163 }
164 }
165 for (auto& c : coords_)
166 std::sort(c.begin(), c.end());
167#ifdef GRID_HANDLER_DEBUG
168 CG_DEBUG("GridHandler").log([&](auto& dbg) {
169 dbg << "Grid dump:";
170 // debugging of the grid coordinates
171 unsigned short i = 0;
172 for (const auto& cs : coords_) {
173 dbg << "\n>> coordinate " << (i++) << " has " << utils::s("member", cs.size(), true) << ":";
174 unsigned short j = 0;
175 for (const auto& val : cs)
176 dbg << (j++ % 20 == 0 ? "\n " : " ") << val;
177 }
178 });
179#endif
180 //--- particularise by dimension
181 switch (D) {
182 case 1: { //--- x |-> (f1,...)
183 const gsl_interp_type* type = gsl_interp_cspline;
184 //const gsl_interp_type* type = gsl_interp_steffen;
185#ifdef GSL_VERSION_ABOVE_2_1
186 const unsigned short min_size = gsl_interp_type_min_size(type);
187#else
188 const unsigned short min_size = type->min_size;
189#endif
190 if (min_size >= values_raw_.size())
191 throw CG_FATAL("GridHandler") << "Not enough points for \"" << type->name << "\" type of interpolation.\n\t"
192 << "Minimum required: " << min_size << ", got " << values_raw_.size() << "!";
193 for (size_t i = 0; i < N; ++i) {
194 values_[i].reset(new double[values_raw_.size()]);
195 splines_1d_.emplace_back(gsl_spline_alloc(type, values_raw_.size()), gsl_spline_free);
196 }
197 // transform a map(coord -> values) to
198 // - 1 vector(coordinates)
199 // - N vectors(values)
200 std::vector<double> x_vec;
201 size_t i = 0;
202 for (const auto& vals : values_raw_) {
203 x_vec.emplace_back(vals.first.at(0));
204 unsigned short j = 0;
205 for (const auto& val : vals.second)
206 values_[j++].get()[i] = val;
207 ++i;
208 }
209 // initialise spline interpolation objects (one for each value)
210 for (size_t j = 0; j < splines_1d_.size(); ++j)
211 gsl_spline_init(splines_1d_.at(j).get(), &x_vec[0], values_[j].get(), values_raw_.size());
212 } break;
213 case 2: { //--- (x,y) |-> (f1,...)
214#ifdef GSL_VERSION_ABOVE_2_1
215 if (values_raw_.size() < gsl_interp2d_type_min_size(gsl_interp2d_bicubic))
216 CG_WARNING("GridHandler") << "The grid size is too small (" << values_raw_.size() << " < "
217 << gsl_interp2d_type_min_size(gsl_interp2d_bicubic)
218 << ") for bicubic interpolation. Switching to a bilinear interpolation mode.";
219 //const gsl_interp2d_type* type =
220 // (values_raw_.size() > gsl_interp2d_type_min_size(gsl_interp2d_bicubic) ? gsl_interp2d_bicubic
221 // : gsl_interp2d_bilinear);
222 const gsl_interp2d_type* type = gsl_interp2d_bilinear;
223 splines_2d_.clear();
224 for (size_t i = 0; i < N; ++i) {
225 values_[i].reset(new double[coords_.at(0).size() * coords_.at(1).size()]);
226 splines_2d_.emplace_back(gsl_spline2d_alloc(type, coords_.at(0).size(), coords_.at(1).size()),
227 gsl_spline2d_free);
228 }
229
230 // second loop over all points to populate the grid
231 for (const auto& val : values_raw_) {
232 double val_x = val.first.at(0), val_y = val.first.at(1);
233 // retrieve the index of the bin in the set
234 const unsigned short id_x =
235 std::distance(coords_.at(0).begin(), std::lower_bound(coords_.at(0).begin(), coords_.at(0).end(), val_x));
236 const unsigned short id_y =
237 std::distance(coords_.at(1).begin(), std::lower_bound(coords_.at(1).begin(), coords_.at(1).end(), val_y));
238 for (size_t i = 0; i < splines_2d_.size(); ++i)
239 gsl_spline2d_set(splines_2d_.at(i).get(), values_[i].get(), id_x, id_y, val.second[i]);
240 }
241
242 // initialise spline interpolation objects (one for each value)
243 const coord_t &x_vec = coords_.at(0), &y_vec = coords_.at(1);
244 for (size_t i = 0; i < splines_2d_.size(); ++i)
245 gsl_spline2d_init(
246 splines_2d_.at(i).get(), &x_vec[0], &y_vec[0], values_[i].get(), x_vec.size(), y_vec.size());
247#else
248 CG_WARNING("GridHandler") << "GSL version ≥ 2.1 is required for spline bilinear interpolation.\n\t"
249 << "Version " << GSL_VERSION << " is installed on this system!\n\t"
250 << "Will use a simple bilinear approximation instead.";
251#endif
252 } break;
253 default:
254 break;
255 }
256 init_ = true;
257 CG_DEBUG("GridHandler").log([&](auto& log) {
258 log << "Grid evaluator initialised with boundaries: " << boundaries() << ".";
259#ifdef GRID_HANDLER_DEBUG
260 log << "\n"
261 << "Values handled:\n"
262 << values_raw_;
263#endif
264 });
265 }
266
267 template <size_t D, size_t N>
268 std::array<Limits, D> GridHandler<D, N>::boundaries() const {
269 std::array<Limits, D> out;
270 const auto min_val = min(), max_val = max();
271 for (size_t i = 0; i < D; ++i)
272 out[i] = {min_val[i], max_val[i]};
273 return out;
274 }
275
276 template <size_t D, size_t N>
277 std::array<double, D> GridHandler<D, N>::min() const {
278 std::array<double, D> out{};
279 size_t i = 0;
280 for (const auto& c : coords_) { // loop over all dimensions
281 const auto& min = std::min_element(c.begin(), c.end());
282 out[i++] = min != c.end() ? *min : std::numeric_limits<double>::infinity();
283 }
284 return out;
285 }
286
287 template <size_t D, size_t N>
288 std::array<double, D> GridHandler<D, N>::max() const {
289 std::array<double, D> out{};
290 size_t i = 0;
291 for (const auto& c : coords_) { // loop over all dimensions
292 const auto& max = std::max_element(c.begin(), c.end());
293 out[i++] = max != c.end() ? *max : std::numeric_limits<double>::infinity();
294 }
295 return out;
296 }
297
298 template <size_t D, size_t N>
299 void GridHandler<D, N>::findIndices(const coord_t& coord, coord_t& min, coord_t& max) const {
300 for (size_t i = 0; i < D; ++i) {
301 const auto& c_i = coords_.at(i); // extract all coordinates registered for this dimension
302 if (coord.at(i) < *c_i.begin()) { // under the range
303 CG_DEBUG_LOOP("GridHandler:indices") << "Coordinate " << i << " in underflow range "
304 << "(" << coord.at(i) << " < " << *c_i.begin() << ").";
305 min[i] = max[i] = *c_i.begin();
306 } else if (coord.at(i) > *c_i.rbegin()) { // over the range
307 CG_DEBUG_LOOP("GridHandler:indices") << "Coordinate " << i << " in overflow range "
308 << "(" << coord.at(i) << " > " << *c_i.rbegin() << ").";
309 min[i] = max[i] = *c_i.rbegin();
310 } else { // in between two coordinates
311 auto it_coord = std::lower_bound(c_i.begin(), c_i.end(), coord.at(i));
312 max[i] = *it_coord;
313 if (it_coord != c_i.begin())
314 it_coord--;
315 min[i] = *it_coord;
316 }
317 }
318 }
319
320 //----------------------------------------------------------------------------
321 // grid manipulation utility
322 //----------------------------------------------------------------------------
323
324 template <size_t D, size_t N>
325 typename GridHandler<D, N>::gridpoint_t GridHandler<D, N>::gridpoint_t::operator*(double c) const {
326 gridpoint_t out = *this;
327 std::transform(out.begin(), out.end(), out.begin(), [&c](const auto& a) { return a * c; });
328 return out;
329 }
330
331 template <size_t D, size_t N>
332 typename GridHandler<D, N>::gridpoint_t GridHandler<D, N>::gridpoint_t::operator+(const gridpoint_t& rhs) const {
333 gridpoint_t out = *this;
334 for (size_t i = 0; i < out.size(); ++i)
335 out[i] += rhs[i];
336 return out;
337 }
338
339 template class GridHandler<1, 1>;
340 template class GridHandler<1, 2>;
341 template class GridHandler<2, 2>;
342 template class GridHandler<3, 1>;
343} // namespace cepgen
#define CG_FATAL(mod)
Definition Exception.h:61
#define CG_ERROR(mod)
Definition Exception.h:60
#define CG_WARNING(mod)
Definition Message.h:228
#define CG_DEBUG_LOOP(mod)
Definition Message.h:224
#define CG_DEBUG(mod)
Definition Message.h:220
A generic class for grid interpolation.
Definition GridHandler.h:44
void initialise()
Initialise the grid and all useful interpolators/accelerators.
void insert(coord_t coord, values_t value)
Insert a new value in the grid.
std::vector< double > coord_t
Coordinates container.
Definition GridHandler.h:46
std::vector< std::unique_ptr< gsl_interp_accel, void(*)(gsl_interp_accel *)> > accel_
Definition GridHandler.h:67
std::array< Limits, D > boundaries() const
Grid boundaries (collection of (min,max))
std::array< double, D > max() const
Highest bound of the grid coordinates.
values_t eval(coord_t in_coords) const
Interpolate a point to a given coordinate.
std::array< double, N > values_t
Value(s) at a given coordinate.
Definition GridHandler.h:47
std::array< double, D > min() const
Lowest bound of the grid coordinates.
GridHandler(const GridType &grid_type)
Build a grid interpolator from a grid type.
std::string s(const std::string &word, float num, bool show_number)
Add a trailing "s" when needed.
Definition String.cpp:228
Common namespace for this Monte Carlo generator.
GridType
Interpolation type for the grid coordinates.
Definition GridHandler.h:39