|
| 1 | +#include <cstdint> |
| 2 | +#include <pineappl_capi.h> |
| 3 | + |
| 4 | +#include <cassert> |
| 5 | +#include <cmath> |
| 6 | +#include <cstddef> |
| 7 | +#include <iostream> |
| 8 | +#include <random> |
| 9 | +#include <string> |
| 10 | +#include <tuple> |
| 11 | +#include <vector> |
| 12 | + |
| 13 | +using KinematicsTuple = std::tuple<double, double, double>; |
| 14 | + |
| 15 | +struct KinInterpolation { |
| 16 | + std::vector<double> x_interp; |
| 17 | + std::vector<double> z_interp; |
| 18 | +}; |
| 19 | + |
| 20 | +template<typename T> |
| 21 | +std::vector<T> geomspace(T start, T stop, int num, bool endpoint = false) { |
| 22 | + std::vector<T> result(num); |
| 23 | + |
| 24 | + if (num == 1) { |
| 25 | + result[0] = start; |
| 26 | + return result; |
| 27 | + } |
| 28 | + |
| 29 | + T log_start = std::log(start); |
| 30 | + T log_stop = std::log(stop); |
| 31 | + T step = (log_stop - log_start) / (endpoint ? (num - 1) : num); |
| 32 | + |
| 33 | + for (int i = 0; i < num; ++i) { |
| 34 | + result[i] = std::exp(log_start + i * step); |
| 35 | + } |
| 36 | + |
| 37 | + return result; |
| 38 | +} |
| 39 | + |
| 40 | +KinInterpolation kinematics_interpolation_points() { |
| 41 | + std::vector<double> x = geomspace(1e-5, 1.0, 50); |
| 42 | + std::vector<double> z = geomspace(1e-5, 1.0, 50); |
| 43 | + |
| 44 | + return {x, x}; |
| 45 | +} |
| 46 | + |
| 47 | +std::vector<double> generate_subgrid_arrays( |
| 48 | + std::mt19937& rng, |
| 49 | + std::vector<double> x, |
| 50 | + std::vector<double> z |
| 51 | +) { |
| 52 | + std::size_t kin_length = x.size() * z.size(); |
| 53 | + std::vector<double> subgrid(kin_length); |
| 54 | + |
| 55 | + // NOTE: `subgrid` is a flatten matrix whose layout was `[q2=1][x][z]`. |
| 56 | + // The order of the kinematics shoud match the kinematics declaration. |
| 57 | + for (std::size_t i = 0; i != kin_length; i++) { |
| 58 | + subgrid[i] = std::generate_canonical<double, 53>(rng); |
| 59 | + } |
| 60 | + |
| 61 | + return subgrid; |
| 62 | +} |
| 63 | + |
| 64 | +void fill_grid(pineappl_grid* grid, std::vector<KinematicsTuple> kin_tuples) { |
| 65 | + auto rng = std::mt19937(); |
| 66 | + |
| 67 | + auto* channels = pineappl_grid_channels(grid); |
| 68 | + std::size_t n_bins = pineappl_grid_bin_count(grid); |
| 69 | + std::size_t n_orders = pineappl_grid_order_count(grid); |
| 70 | + std::size_t n_channels = pineappl_channels_count(channels); |
| 71 | + |
| 72 | + // Get the kinematics |
| 73 | + KinInterpolation kins = kinematics_interpolation_points(); |
| 74 | + std::vector<double> x_interp = kins.x_interp; |
| 75 | + std::vector<double> z_interp = kins.z_interp; |
| 76 | + |
| 77 | + // Extract the shape of the subgrid - Q2 always passed as an array of ONE element |
| 78 | + std::vector<std::size_t> subgrid_shape = {1, x_interp.size(), z_interp.size()}; |
| 79 | + |
| 80 | + for (std::size_t b = 0; b != n_bins; b++) { |
| 81 | + for (std::size_t o = 0; o != n_orders; o++) { |
| 82 | + for (std::size_t c = 0; c != n_channels; c++) { |
| 83 | + // Construct the node values of {Q2, x_inter, z_interp} |
| 84 | + // NOTE: Pay attention to the order, it should match the kinematics declaration |
| 85 | + // and how the subgrid was constructed (see `generate_subgrid_array`). |
| 86 | + std::vector<double> node_values = { std::get<0>(kin_tuples[b]) }; |
| 87 | + node_values.insert(node_values.end(), x_interp.begin(), x_interp.end()); |
| 88 | + node_values.insert(node_values.end(), z_interp.begin(), z_interp.end()); |
| 89 | + |
| 90 | + // Mock the subgrids for a given bin, order, and channel |
| 91 | + std::vector<double> subgrid_arrays = generate_subgrid_arrays(rng, x_interp, z_interp); |
| 92 | + |
| 93 | + // set the subgrids |
| 94 | + pineappl_grid_set_subgrid( |
| 95 | + grid, |
| 96 | + b, o, c, // kinematics index |
| 97 | + node_values.data(), |
| 98 | + subgrid_arrays.data(), |
| 99 | + subgrid_shape.data() |
| 100 | + ); |
| 101 | + } |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + // Remove channels object from memory |
| 106 | + pineappl_channels_delete(channels); |
| 107 | +} |
| 108 | + |
| 109 | +int main() { |
| 110 | + // --- |
| 111 | + // Create all channels |
| 112 | + |
| 113 | + std::size_t nb_convolutions = 2; |
| 114 | + auto* channels = pineappl_channels_new(nb_convolutions); |
| 115 | + |
| 116 | + int32_t pids1[] = { 21, 21 }; |
| 117 | + double factors1[] = { 1.0 }; |
| 118 | + // define the channel #0 |
| 119 | + pineappl_channels_add(channels, 1, pids1, factors1); |
| 120 | + |
| 121 | + // create another channel this channel is the down-type-antidown-type quark channel; here we |
| 122 | + int32_t pids2[] = { 1, -1, 3, -3, 5, -5 }; |
| 123 | + // define the channel #1 |
| 124 | + pineappl_channels_add(channels, 3, pids2, nullptr); |
| 125 | + |
| 126 | + // --- |
| 127 | + // Specify the perturbative orders that will be filled into the grid |
| 128 | + std::vector<uint8_t> orders = { |
| 129 | + 1, 0, 0, 0, 0, // order #0: LO QCD |
| 130 | + 2, 0, 0, 0, 0, // order #1: NLO QCD |
| 131 | + }; |
| 132 | + |
| 133 | + // --- |
| 134 | + // Specify the bin limits |
| 135 | + |
| 136 | + // In SIDIS, a bin is defined as a tuple (Q2, x, z) values (3D). |
| 137 | + std::vector<KinematicsTuple> kin_obs = {std::make_tuple(1e3, 1e-5, 1e-2), std::make_tuple(1e4, 1e-2, 1e-3)}; |
| 138 | + // We are going to define some placeholder 1D bins that we'll overwrite later. |
| 139 | + std::vector<double> bins; |
| 140 | + for (std::size_t i = 0; i < kin_obs.size() + 1; ++i) { |
| 141 | + bins.push_back(static_cast<float>(i)); |
| 142 | + } |
| 143 | + |
| 144 | + // --- |
| 145 | + // Construct the objects that are needed to fill the Grid |
| 146 | + |
| 147 | + pineappl_pid_basis pid_basis = PINEAPPL_PID_BASIS_EVOL; |
| 148 | + pineappl_conv convs[] = { |
| 149 | + { PINEAPPL_CONV_TYPE_UNPOL_PDF, 2212 }, |
| 150 | + { PINEAPPL_CONV_TYPE_UNPOL_FF, 211 }, // Assumes Pion |
| 151 | + }; |
| 152 | + |
| 153 | + // Define the kinematics required for this process. In the following example we have ONE single |
| 154 | + // scale and two momentum fractions (corresponding to the two initial- and final-state hadrons). |
| 155 | + // The format of the kinematics is: { type, value }. |
| 156 | + pineappl_kinematics scales = { PINEAPPL_KINEMATICS_SCALE, 0 }; |
| 157 | + pineappl_kinematics x1 = { PINEAPPL_KINEMATICS_X, 0 }; |
| 158 | + pineappl_kinematics x2 = { PINEAPPL_KINEMATICS_X, 1 }; |
| 159 | + pineappl_kinematics kinematics[3] = { scales, x1, x2 }; |
| 160 | + |
| 161 | + // Define the specificities of the interpolations for each of the kinematic variables. |
| 162 | + pineappl_reweight_meth scales_reweight = PINEAPPL_REWEIGHT_METH_NO_REWEIGHT; // Reweighting method |
| 163 | + pineappl_reweight_meth moment_reweight = PINEAPPL_REWEIGHT_METH_APPL_GRID_X; |
| 164 | + pineappl_map scales_mapping = PINEAPPL_MAP_APPL_GRID_H0; // Mapping method |
| 165 | + pineappl_map moment_mapping = PINEAPPL_MAP_APPL_GRID_F2; |
| 166 | + pineappl_interp_meth interpolation_meth = PINEAPPL_INTERP_METH_LAGRANGE; |
| 167 | + pineappl_interp interpolations[3] = { |
| 168 | + { 1e2, 1e8, 40, 3, scales_reweight, scales_mapping, interpolation_meth }, // Interpolation fo `scales` |
| 169 | + { 2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth }, // Interpolation fo `x1` |
| 170 | + { 2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth }, // Interpolation fo `x2` |
| 171 | + }; |
| 172 | + |
| 173 | + // Define the unphysical scale objects |
| 174 | + pineappl_scale_func_form scale_mu = { PINEAPPL_SCALE_FUNC_FORM_SCALE, 0 }; |
| 175 | + pineappl_scale_func_form mu_scales[3] = { scale_mu, scale_mu, scale_mu }; |
| 176 | + |
| 177 | + // --- |
| 178 | + // Create the grid using the previously set information about orders, bins and channels |
| 179 | + |
| 180 | + auto* grid = pineappl_grid_new2(bins.size() - 1, bins.data(), orders.size() / 5, orders.data(), |
| 181 | + channels, pid_basis, convs, 3, interpolations, kinematics, mu_scales); |
| 182 | + |
| 183 | + pineappl_channels_delete(channels); |
| 184 | + |
| 185 | + // --- |
| 186 | + // Fill the grid with phase-space points |
| 187 | + fill_grid(grid, kin_obs); |
| 188 | + |
| 189 | + // --- |
| 190 | + // NOTE: We now have to remap the bins to 3D |
| 191 | + |
| 192 | + // We need to flatten the array of `KinematicsTuple` and define the normalizations |
| 193 | + std::vector<double> flat_kin_obs; |
| 194 | + for (const auto& kin : kin_obs) { |
| 195 | + flat_kin_obs.push_back(std::get<0>(kin)); |
| 196 | + flat_kin_obs.push_back(std::get<1>(kin)); |
| 197 | + flat_kin_obs.push_back(std::get<2>(kin)); |
| 198 | + } |
| 199 | + std::vector<double> normalizations(bins.size() - 1, 1.0); |
| 200 | + pineappl_grid_set_bwfl( |
| 201 | + grid, |
| 202 | + flat_kin_obs.data(), // lower bin limits |
| 203 | + flat_kin_obs.data(), // upper bin limits |
| 204 | + bins.size() - 1, // number of bins |
| 205 | + 3, // Dimension of the bins (Q2, x, z) |
| 206 | + normalizations.data() |
| 207 | + ); |
| 208 | + pineappl_grid_optimize(grid); |
| 209 | + |
| 210 | + // --- |
| 211 | + // Write the grid to disk - the filename can be anything ... |
| 212 | + std::string filename = "sidis-toygrid.pineappl.lz4"; |
| 213 | + pineappl_grid_write(grid, filename.c_str()); |
| 214 | + |
| 215 | + // destroy the object |
| 216 | + pineappl_grid_delete(grid); |
| 217 | + |
| 218 | + std::cout << "Generated " << filename << " containing a toy SIDIS.\n\n" |
| 219 | + "Try running the following command to check the bins:\n" |
| 220 | + " - pineappl read --bins " << filename << "\n"; |
| 221 | +} |
0 commit comments