Skip to content

Commit 039679f

Browse files
Merge pull request #344 from NNPDF/expose-evolve-capi
Expose `grid.evolve` to the C-API
2 parents afae3be + 1a43bc5 commit 039679f

File tree

15 files changed

+1026
-71
lines changed

15 files changed

+1026
-71
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# .github/actions/cache-test-data/action.yml
2+
3+
name: Cache Test Data
4+
description: Caches and downloads test data
5+
runs:
6+
using: "composite"
7+
steps:
8+
- name: Cache test data
9+
id: cache
10+
uses: actions/cache@v4
11+
with:
12+
path: test-data
13+
key: test-data-v21
14+
- name: Download test data if cache miss
15+
if: steps.cache.outputs.cache-hit != 'true'
16+
run: |
17+
cd maintainer
18+
./download-test-data.sh
19+
shell: bash

.github/workflows/capi.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ jobs:
2323
cargo cinstall --verbose --prefix=/usr/local/ --libdir=/usr/local/lib
2424
ldconfig
2525
26+
- name: Get test data
27+
uses: ./.github/actions/cache-test-data
28+
2629
- name: Test C++ examples
2730
run: |
2831
cd examples/cpp

.github/workflows/python.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,8 @@ jobs:
2323
- name: Set up Rust
2424
run: |
2525
rustup component add llvm-tools
26-
2726
- name: Get test data
28-
id: cache-test-data
29-
uses: actions/cache@v4
30-
with:
31-
path: test-data
32-
key: test-data-v20
33-
- name: Download test data
34-
if: steps.cache-test-data.outputs.cache-hit != 'true'
35-
run: |
36-
cd maintainer
37-
./download-test-data.sh
38-
27+
uses: ./.github/actions/cache-test-data
3928
- name: Test
4029
env:
4130
# `-C link-dead-code` is needed to prevent 'warning: XX functions have mismatched data' warnings

.github/workflows/rust.yml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,7 @@ jobs:
2121
- uses: actions/checkout@v4
2222

2323
- name: Get test data
24-
id: cache-test-data
25-
uses: actions/cache@v4
26-
with:
27-
path: test-data
28-
key: test-data-v20
29-
- name: Download test data
30-
if: steps.cache-test-data.outputs.cache-hit != 'true'
31-
run: |
32-
cd maintainer
33-
./download-test-data.sh
24+
uses: ./.github/actions/cache-test-data
3425

3526
- name: Set RUSTDOCFLAGS
3627
run: |

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/cpp/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ PROGRAMS = \
1414
convolve-grid-deprecated \
1515
convolve-grid \
1616
get-subgrids \
17+
evolve-grid \
18+
evolve-grid-identity \
1719
deprecated \
1820
display-channels-deprecated \
1921
display-channels \
@@ -51,6 +53,12 @@ deprecated: deprecated.cpp
5153
get-subgrids: get-subgrids.cpp
5254
$(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@
5355

56+
evolve-grid: evolve-grid.cpp
57+
$(CXX) $(CXXFLAGS) $< $(LHAPDF_DEPS) $(PINEAPPL_DEPS) -o $@
58+
59+
evolve-grid-identity: evolve-grid-identity.cpp
60+
$(CXX) $(CXXFLAGS) $< $(LHAPDF_DEPS) $(PINEAPPL_DEPS) -o $@
61+
5462
display-channels-deprecated: display-channels-deprecated.cpp
5563
$(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@
5664

examples/cpp/evolve-grid-identity.cpp

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
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

Comments
 (0)