|
| 1 | +#include <LHAPDF/PDF.h> |
| 2 | +#include <pineappl_capi.h> |
| 3 | + |
| 4 | +#include <algorithm> |
| 5 | +#include <cstdint> |
| 6 | +#include <cstdlib> |
| 7 | +#include <cassert> |
| 8 | +#include <cstddef> |
| 9 | +#include <string> |
| 10 | +#include <vector> |
| 11 | +#include <numeric> |
| 12 | + |
| 13 | +// NOTE: Uses the scale of the Grid as the starting scale such that we can use an IDENTITY EKO. |
| 14 | +double FAC0 = 6456.44; |
| 15 | + |
| 16 | +/** @brief This struct can contain arbitrary parameters that need to be passed to Evolution |
| 17 | + * Operator Callback (`generated_fake_ekos`). |
| 18 | + */ |
| 19 | +struct OperatorParams { |
| 20 | + std::vector<pineappl_conv_type> conv_types; |
| 21 | +}; |
| 22 | + |
| 23 | +std::vector<std::size_t> unravel_index(std::size_t flat_index, const std::vector<std::size_t>& shape) { |
| 24 | + std::size_t ndim = shape.size(); |
| 25 | + std::vector<std::size_t> coords(ndim); |
| 26 | + |
| 27 | + for (int i = ndim - 1; i >= 0; --i) { |
| 28 | + coords[i] = flat_index % shape[i]; |
| 29 | + flat_index /= shape[i]; |
| 30 | + } |
| 31 | + |
| 32 | + return coords; |
| 33 | +} |
| 34 | + |
| 35 | +extern "C" void generate_fake_ekos( |
| 36 | + std::size_t op_index, |
| 37 | + double /*fac1*/, |
| 38 | + const int* /*pids_in*/, |
| 39 | + const double* /*x_in*/, |
| 40 | + const int* /*pids_out*/, |
| 41 | + const double* /*x_out*/, |
| 42 | + const std::size_t* eko_shape, |
| 43 | + double* eko_buffer, |
| 44 | + void* params_state |
| 45 | +) { |
| 46 | + // select the type of convolution based on the Operator index |
| 47 | + OperatorParams* op_params = static_cast<OperatorParams*>(params_state); |
| 48 | + pineappl_conv_type _ = op_params->conv_types[op_index]; |
| 49 | + |
| 50 | + // NOTE: This has to work because the Evolution Operator is always 4D |
| 51 | + std::vector<std::size_t> shape(eko_shape, eko_shape + 4); |
| 52 | + // Compute the length of the flattened shape by multiplying the entries |
| 53 | + std::size_t flat_len = std::accumulate(shape.begin(), |
| 54 | + shape.end(), 1, std::multiplies<std::size_t>()); |
| 55 | + for (std::size_t i = 0; i != flat_len; i++) { |
| 56 | + std::vector<std::size_t> coords = unravel_index(i, shape); |
| 57 | + |
| 58 | + double delta_ik = (coords[0] == coords[2]) ? 1.0 : 0.0; |
| 59 | + double delta_jl = (coords[1] == coords[3]) ? 1.0 : 0.0; |
| 60 | + |
| 61 | + eko_buffer[i] = delta_ik * delta_jl; |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +void print_results(std::vector<double> dxsec_grid, std::vector<double> dxsec_fktable) { |
| 66 | + const int idx_width = 6; |
| 67 | + const int num_width = 15; |
| 68 | + const int dif_width = 15; |
| 69 | + |
| 70 | + // Print headers |
| 71 | + std::cout << std::setw(idx_width) << "Bin" |
| 72 | + << std::setw(num_width) << "Grid" |
| 73 | + << std::setw(num_width) << "FkTable" |
| 74 | + << std::setw(dif_width) << "reldiff" << std::endl; |
| 75 | + |
| 76 | + // Print dashed lines |
| 77 | + std::cout << std::setw(idx_width) << std::string(idx_width - 2, '-') |
| 78 | + << std::setw(num_width) << std::string(num_width - 2, '-') |
| 79 | + << std::setw(num_width) << std::string(num_width - 2, '-') |
| 80 | + << std::setw(dif_width) << std::string(dif_width - 2, '-') << std::endl; |
| 81 | + |
| 82 | + // Print the data |
| 83 | + std::cout << std::scientific << std::setprecision(6); |
| 84 | + for (size_t i = 0; i < dxsec_grid.size(); ++i) { |
| 85 | + double reldiff = (dxsec_fktable[i] - dxsec_grid[i]) / dxsec_grid[i]; |
| 86 | + std::cout << std::setw(idx_width) << i |
| 87 | + << std::setw(num_width) << dxsec_grid[i] |
| 88 | + << std::setw(num_width) << dxsec_fktable[i] |
| 89 | + << std::setw(dif_width) << reldiff |
| 90 | + << std::endl; |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +int main() { |
| 95 | + // TODO: How to get a Grid that can be evolved?? |
| 96 | + std::string filename = "../../test-data/LHCB_WP_7TEV_opt.pineappl.lz4"; |
| 97 | + |
| 98 | + // disable LHAPDF banners to guarantee deterministic output |
| 99 | + LHAPDF::setVerbosity(0); |
| 100 | + std::string pdfset = "NNPDF31_nlo_as_0118_luxqed"; |
| 101 | + auto pdf = std::unique_ptr<LHAPDF::PDF>(LHAPDF::mkPDF(pdfset, 0)); |
| 102 | + |
| 103 | + auto xfx = [](int32_t id, double x, double q2, void* pdf) { |
| 104 | + return static_cast <LHAPDF::PDF*> (pdf)->xfxQ2(id, x, q2); |
| 105 | + }; |
| 106 | + auto alphas = [](double q2, void* pdf) { |
| 107 | + return static_cast <LHAPDF::PDF*> (pdf)->alphasQ2(q2); |
| 108 | + }; |
| 109 | + |
| 110 | + std::vector<LHAPDF::PDF*> pdfs = {pdf.get(), pdf.get()}; |
| 111 | + void** pdf_states = reinterpret_cast<void**>(pdfs.data()); |
| 112 | + |
| 113 | + // read the grid from a file |
| 114 | + auto* grid = pineappl_grid_read(filename.c_str()); |
| 115 | + |
| 116 | + // Get the PID basis representation |
| 117 | + pineappl_pid_basis pid_basis = pineappl_grid_pid_basis(grid); |
| 118 | + assert(pid_basis == PINEAPPL_PID_BASIS_PDG); |
| 119 | + |
| 120 | + // Get the number of convolutions and their types |
| 121 | + std::size_t n_convs = pineappl_grid_convolutions_len(grid); |
| 122 | + std::vector<pineappl_conv_type> conv_types(n_convs); |
| 123 | + pineappl_grid_conv_types(grid, conv_types.data()); |
| 124 | + |
| 125 | + // Fill the vector of unique convolution types. If the Operators required for the Grid |
| 126 | + // are the same, then it suffices to only pass ONE single Operator. |
| 127 | + std::vector<pineappl_conv_type> unique_convs; |
| 128 | + for (std::size_t i = 0; i != n_convs; i++) { |
| 129 | + pineappl_conv_type conv = conv_types[i]; |
| 130 | + if (std::find(unique_convs.begin(), unique_convs.end(), conv) == unique_convs.end()) { |
| 131 | + unique_convs.push_back(conv); |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + // Get the shape of the evolve info objects |
| 136 | + std::vector<std::size_t> evinfo_shape(5); |
| 137 | + // NOTE: The argument of `pineappl_grid_evolve_info_shape` must follow the following orders: |
| 138 | + // - `grid`: PineAPPL Grid |
| 139 | + // - `order_mask`: array of booleans to mask the order(s) to apply the Evolution to, |
| 140 | + // `nullptr` selects all the orders |
| 141 | + // - `evinfo_shape`: placeholder to store the shape of the Evolution Operator |
| 142 | + pineappl_grid_evolve_info_shape(grid, nullptr, evinfo_shape.data()); |
| 143 | + |
| 144 | + // Get the values of the evolve info parameters. These contain, for example, the |
| 145 | + // information on the `x`-grid and `PID` used to interpolate the Grid. |
| 146 | + // NOTE: These are used to construct the Evolution Operator |
| 147 | + std::vector<double> fac1(evinfo_shape[0]); |
| 148 | + std::vector<double> frg1(evinfo_shape[1]); |
| 149 | + std::vector<int> pids_in(evinfo_shape[2]); |
| 150 | + std::vector<double> x_in(evinfo_shape[3]); |
| 151 | + std::vector<double> ren1(evinfo_shape[4]); |
| 152 | + // NOTE: The argument of `pineappl_grid_evolve_info` must follow the following orders: |
| 153 | + // - `grid`: PineAPPL Grid |
| 154 | + // - `order_mask`: array of booleans to mask the order(s) to apply the Evolution to, |
| 155 | + // `nullptr` selects all the orders |
| 156 | + // The rest of the arguments are placeholders to store data |
| 157 | + pineappl_grid_evolve_info(grid, nullptr, fac1.data(), |
| 158 | + frg1.data(), pids_in.data(), x_in.data(), ren1.data()); |
| 159 | + |
| 160 | + // ------------------ Construct the Operator Info ------------------ |
| 161 | + // The Operator Info is a vector with length `N_conv * N_Q2_slices` whose |
| 162 | + // elements are `OperatorInfo` objects. |
| 163 | + std::vector<pineappl_conv_type> convtypes(unique_convs.size()); |
| 164 | + std::vector<pineappl_operator_info> opinfo_slices(unique_convs.size() * fac1.size()); |
| 165 | + for (std::size_t i = 0; i != unique_convs.size(); i++) { |
| 166 | + for (std::size_t j = 0; j != fac1.size(); j++) { |
| 167 | + pineappl_operator_info opinfo = { |
| 168 | + FAC0, // fac0 |
| 169 | + fac1[j], // fac1 |
| 170 | + pid_basis, |
| 171 | + unique_convs[i], |
| 172 | + }; |
| 173 | + opinfo_slices[i * fac1.size() + j] = opinfo; |
| 174 | + } |
| 175 | + conv_types[i] = unique_convs[i]; |
| 176 | + } |
| 177 | + |
| 178 | + // ------------------ Construct the Evolution Operator ------------------ |
| 179 | + // Choose a different PID basis for the FK table |
| 180 | + // std::vector<int> pids_out = {-6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 21, 22}; |
| 181 | + std::vector<int> pids_out = pids_in; |
| 182 | + |
| 183 | + // Construct the values of alphas table |
| 184 | + std::vector<double> alphas_table; |
| 185 | + for (double q2 : ren1) { |
| 186 | + double alpha = alphas(q2, pdf.get()); |
| 187 | + alphas_table.push_back(alpha); |
| 188 | + } |
| 189 | + |
| 190 | + // Construct the Parameters that will get passed to the Callback |
| 191 | + OperatorParams* op_params = new OperatorParams; |
| 192 | + op_params->conv_types = convtypes; |
| 193 | + void* params = static_cast<void*>(op_params); |
| 194 | + |
| 195 | + std::vector<double> xi = {1.0, 1.0, 1.0}; |
| 196 | + // NOTE: The EKO has to have as shape: (pids_in, x_in, pids_out, x_out) |
| 197 | + std::vector<std::size_t> tensor_shape = {pids_in.size(), x_in.size(), pids_out.size(), x_in.size()}; |
| 198 | + |
| 199 | + // NOTE: The arguments of `pineappl_grid_evolve` must follow the following orders: |
| 200 | + // - `grid`: PineAPPL Grid |
| 201 | + // - `nb_slices`: the number of convolution(s)/Evolution Operator(s) required |
| 202 | + // - `slices`: callback that returns the evolution operator(s) in slices |
| 203 | + // - `operator_info`: operator info |
| 204 | + // - `pids_in`: PIDs basis representation of the Grid |
| 205 | + // - `x_in`: x-grid of the Grid |
| 206 | + // - `pids_out`: PIDs basis representation of the FK table |
| 207 | + // - `x_out`: x-grid of the FK table |
| 208 | + // - `state`: parameters that get passed to `operator` |
| 209 | + // - `order_mask`: array of booleans to mask the order(s) to apply the Evolution to, |
| 210 | + // `nullptr` selects all the orders |
| 211 | + // - `xi`: scale variation |
| 212 | + // - `ren1`: values of the renormalization scales |
| 213 | + // - `alphas_table`: values of alphas for each renormalization scales |
| 214 | + // - `eko_shape`: shape of the evolution operators |
| 215 | + pineappl_grid* fktable = pineappl_grid_evolve( |
| 216 | + grid, // `grid` |
| 217 | + unique_convs.size(), // `nb_slices` |
| 218 | + generate_fake_ekos, // `slices` |
| 219 | + opinfo_slices.data(), // `operator_info` |
| 220 | + pids_in.data(), // `pids_in` |
| 221 | + x_in.data(), // `x_in` |
| 222 | + pids_out.data(), // `pids_out` |
| 223 | + x_in.data(), // `x_out` |
| 224 | + tensor_shape.data(), // `eko_shape` |
| 225 | + params, // `state` |
| 226 | + nullptr, // `order_mask` |
| 227 | + xi.data(), // `xi` |
| 228 | + ren1.data(), // `ren1` |
| 229 | + alphas_table.data() // `alphas_table` |
| 230 | + ); |
| 231 | + |
| 232 | + // ------------------ Compare Grid & FK after convolution ------------------ |
| 233 | + // how many bins does this grid have? |
| 234 | + std::size_t bins = pineappl_grid_bin_count(grid); |
| 235 | + |
| 236 | + // [ convolve the Grid ] |
| 237 | + std::vector<double> mu_scales = { 1.0, 1.0, 1.0 }; |
| 238 | + std::vector<double> dxsec_grid(bins); |
| 239 | + pineappl_grid_convolve(grid, xfx, alphas, pdf_states, pdf.get(), |
| 240 | + nullptr, nullptr, nullptr, 1, |
| 241 | + mu_scales.data(), dxsec_grid.data()); |
| 242 | + |
| 243 | + // [ convolve the FK Table ] |
| 244 | + std::vector<double> dxsec_fktable(bins); |
| 245 | + auto as_one = [](double /*q2*/, void* /*pdf*/) { return 1.0; }; |
| 246 | + pineappl_grid_convolve(fktable, xfx, as_one, pdf_states, nullptr, |
| 247 | + nullptr, nullptr, nullptr, 1, |
| 248 | + mu_scales.data(), dxsec_fktable.data()); |
| 249 | + |
| 250 | + // Print the results |
| 251 | + print_results(dxsec_grid, dxsec_fktable); |
| 252 | + |
| 253 | + pineappl_grid_write(fktable, "evolved-grid-identity.pineappl.lz4"); |
| 254 | + |
| 255 | + pineappl_grid_delete(grid); |
| 256 | + pineappl_grid_delete(fktable); |
| 257 | +} |
0 commit comments