Skip to content

Commit 181d1e5

Browse files
authored
Merge pull request #358 from NNPDF/fix-export-applgrid
Fix PineAPPL to APPLgrid conversion problems
2 parents 58d1c67 + 6f1fed1 commit 181d1e5

5 files changed

Lines changed: 116 additions & 42 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- added a missing implementation for a branch in `Grid::merge` that was
13+
triggered when exporting some PineAPPL grids generated from the 'pinejet'
14+
group
15+
- fixed wrong coupling orders when exporting to APPLgrid. This happened when
16+
the PineAPPL grid had orders that had any other ordering than 'LO', 'NLO',
17+
'NNLO'
18+
- fixed a bug that caused exported grids to compare unsuccessfully when the
19+
convolution functions were proton-anti-proton; APPLgrid doesn't store the
20+
types of convolution functions, so we simply convert the grid to use only
21+
proton PDFs
22+
1023
## [1.1.0] - 08/07/2025
1124

1225
### Added

maintainer/test-cli-export.sh

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/bash -x
2+
3+
set -euo pipefail
4+
5+
grids=(
6+
## Ploughshare
7+
8+
# Group: pinejet
9+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wm-arxiv-1109.5141/pinejet-atlas-wm-arxiv-1109.5141.tgz"
10+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wm-arxiv-1603.09222/pinejet-atlas-wm-arxiv-1603.09222.tgz"
11+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wm-arxiv-1612.03016/pinejet-atlas-wm-arxiv-1612.03016.tgz"
12+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wp-arxiv-1109.5141/pinejet-atlas-wp-arxiv-1109.5141.tgz"
13+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wp-arxiv-1603.09222/pinejet-atlas-wp-arxiv-1603.09222.tgz"
14+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wp-arxiv-1612.03016/pinejet-atlas-wp-arxiv-1612.03016.tgz"
15+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1109.5141/pinejet-atlas-z0-arxiv-1109.5141.tgz"
16+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1305.4192/pinejet-atlas-z0-arxiv-1305.4192.tgz"
17+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1404.1212/pinejet-atlas-z0-arxiv-1404.1212.tgz"
18+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1603.09222/pinejet-atlas-z0-arxiv-1603.09222.tgz"
19+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1606.01736/pinejet-atlas-z0-arxiv-1606.01736.tgz"
20+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1612.03016/pinejet-atlas-z0-arxiv-1612.03016.tgz"
21+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1710.05167/pinejet-atlas-z0-arxiv-1710.05167.tgz"
22+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cdf-z0-arxiv-0908.3914/pinejet-cdf-z0-arxiv-0908.3914.tgz"
23+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wm-arxiv-1206.2598/pinejet-cms-wm-arxiv-1206.2598.tgz"
24+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wm-arxiv-1312.6283/pinejet-cms-wm-arxiv-1312.6283.tgz"
25+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wm-arxiv-1603.01803/pinejet-cms-wm-arxiv-1603.01803.tgz"
26+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wp-arxiv-1206.2598/pinejet-cms-wp-arxiv-1206.2598.tgz"
27+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wp-arxiv-1312.6283/pinejet-cms-wp-arxiv-1312.6283.tgz"
28+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wp-arxiv-1603.01803/pinejet-cms-wp-arxiv-1603.01803.tgz"
29+
#"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-z0-arxiv-1310.7291/pinejet-cms-z0-arxiv-1310.7291.tgz" # fails due to static-scale optimization
30+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-d0-wm-arxiv-1309.2591/pinejet-d0-wm-arxiv-1309.2591.tgz"
31+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-d0-wp-arxiv-1309.2591/pinejet-d0-wp-arxiv-1309.2591.tgz"
32+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-d0-z0-arxiv-0702025/pinejet-d0-z0-arxiv-0702025.tgz"
33+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wm-arxiv-1505.07024/pinejet-lhcb-wm-arxiv-1505.07024.tgz"
34+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wm-arxiv-1511.08039/pinejet-lhcb-wm-arxiv-1511.08039.tgz"
35+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wp-arxiv-1505.07024/pinejet-lhcb-wp-arxiv-1505.07024.tgz"
36+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wp-arxiv-1511.08039/pinejet-lhcb-wp-arxiv-1511.08039.tgz"
37+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-z0-arxiv-1505.07024/pinejet-lhcb-z0-arxiv-1505.07024.tgz"
38+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-z0-arxiv-1511.08039/pinejet-lhcb-z0-arxiv-1511.08039.tgz"
39+
"https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-z0-arxiv-1607.06495/pinejet-lhcb-z0-arxiv-1607.06495.tgz"
40+
)
41+
42+
tmp=$(mktemp -d)
43+
cd "${tmp}"
44+
45+
trap "cd && rm -rf ${tmp}" EXIT SIGKILL
46+
47+
for grid in ${grids[@]}; do
48+
archive=${grid##*/}
49+
wget --no-verbose --no-clobber "${grid}" -O /tmp/"${archive}" || true
50+
mkdir subdir
51+
tar xzf /tmp/"${archive}" -C subdir
52+
53+
for grid in $(find subdir \( -name '*.lz4' \)); do
54+
if [[ $(basename $grid) =~ ^\..* ]]; then
55+
continue
56+
fi
57+
58+
converted_grid="${grid}".appl
59+
reimported_grid="${grid}".new.pineappl.lz4
60+
pineappl export --accuracy 1e-12 "${grid}" "${converted_grid}" NNPDF31_nnlo_as_0118_luxqed
61+
pineappl import --accuracy 1e-12 "${converted_grid}" "${reimported_grid}" NNPDF31_nnlo_as_0118_luxqed
62+
du -h "${converted_grid}" "${reimported_grid}"
63+
done
64+
65+
rm -r subdir
66+
done

pineappl/src/grid.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -944,17 +944,8 @@ impl Grid {
944944
.multi_slice_mut((s![.., .., index], s![.., .., other_index]));
945945

946946
for (lhs, rhs) in a.iter_mut().zip(b.iter_mut()) {
947-
if !rhs.is_empty() {
948-
if lhs.is_empty() {
949-
// we can't merge into an EmptySubgridV1
950-
*lhs = mem::replace(rhs, EmptySubgridV1.into());
951-
// transpose `lhs`
952-
todo!();
953-
} else {
954-
lhs.merge(rhs, Some((a_subgrid, b_subgrid)));
955-
*rhs = EmptySubgridV1.into();
956-
}
957-
}
947+
lhs.merge(rhs, Some((a_subgrid, b_subgrid)));
948+
*rhs = EmptySubgridV1.into();
958949
}
959950
}
960951
}

pineappl/src/subgrid.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,15 +418,21 @@ impl SubgridEnum {
418418
if other.is_empty() {
419419
return;
420420
}
421+
421422
if let Self::EmptySubgridV1(_) = self {
422-
if transpose.is_none() {
423+
if transpose.is_some() {
424+
// change the type of `self` to the type of `other`
423425
*self = other.clone();
426+
// TODO: emptying `self` could be done more efficiently, we're probably storing a
427+
// lot of zeros here
428+
self.scale(0.0);
424429
} else {
425-
todo!();
430+
*self = other.clone();
431+
return;
426432
}
427-
} else {
428-
self.merge_impl(other, transpose);
429433
}
434+
435+
self.merge_impl(other, transpose);
430436
}
431437
}
432438

pineappl_cli/src/export/applgrid.rs

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,19 @@ pub fn convert_into_applgrid(
111111
bail!("grid has non-consecutive bin limits, which APPLgrid does not support");
112112
}
113113

114-
if grid.convolutions().len() > 2 {
115-
bail!("APPLgrid does not support grids with more than two convolutions");
114+
match grid.convolutions() {
115+
[_] => {}
116+
[a, b] => {
117+
if a != b {
118+
if a.cc() == *b {
119+
// use charge conjugate to map hadron-anti-hadron grids to use the same single
120+
// convolution function
121+
let index = if a.pid() < 0 { 0 } else { 1 };
122+
grid.charge_conjugate(index);
123+
}
124+
}
125+
}
126+
_ => bail!("APPLgrid does not support grids with more than two convolutions"),
116127
}
117128

118129
// APPLgrid only understands PDG PIDs
@@ -156,9 +167,11 @@ pub fn convert_into_applgrid(
156167
.collect();
157168

158169
// `id` must end with '.config' for APPLgrid to know its type is `lumi_pdf`
159-
let id = "PineAPPL-Lumi.config";
170+
let id = format!("{}.config", output.file_stem()
171+
// UNWRAP: because we write to that file in the end, there always must be a file name
172+
.unwrap().to_string_lossy());
160173
// this object is managed by APPLgrid internally
161-
ffi::make_lumi_pdf(id, &combinations).into_raw();
174+
ffi::make_lumi_pdf(&id, &combinations).into_raw();
162175

163176
let limits: Vec<_> = grid
164177
.bwfl()
@@ -200,17 +213,17 @@ pub fn convert_into_applgrid(
200213
- lo_alphas;
201214

202215
let mut applgrid =
203-
ffi::make_empty_grid(&limits, id, lo_alphas.into(), loops.into(), "f2", "h0");
216+
ffi::make_empty_grid(&limits, &id, lo_alphas.into(), loops.into(), "f2", "h0");
204217

205-
let has_pdf1 = !grid.convolutions().is_empty();
206-
let has_pdf2 = grid.convolutions().get(1).is_some();
218+
// APPLgrid has either two or one convolution(s)
219+
let convolutions = grid.convolutions().len();
207220

208-
for (appl_order, order) in order_mask
221+
for order in order_mask
209222
.iter()
210223
.enumerate()
211224
.filter_map(|(index, keep)| keep.then_some(index))
212-
.enumerate()
213225
{
226+
let appl_order = grid.orders()[order].alphas - lo_alphas;
214227
let factor = TAU.powi(grid.orders()[order].alphas.into());
215228

216229
for (bin, subgrids) in grid
@@ -275,9 +288,7 @@ pub fn convert_into_applgrid(
275288
})
276289
.collect::<Result<_>>()?;
277290

278-
// in the DIS case APPLgrid always uses the first x dimension
279-
280-
let (x1_grid, x2_grid) = if has_pdf1 && has_pdf2 {
291+
let (x1_grid, x2_grid) = if convolutions == 2 {
281292
(
282293
grid.kinematics()
283294
.iter()
@@ -298,26 +309,13 @@ pub fn convert_into_applgrid(
298309
// TODO: convert this into an error
299310
.unwrap(),
300311
)
301-
} else if has_pdf1 {
302-
(
303-
grid.kinematics()
304-
.iter()
305-
.zip(subgrid.node_values())
306-
.find_map(|(kin, node_values)| {
307-
matches!(kin, &Kinematics::X(idx) if idx == 0)
308-
.then_some(node_values)
309-
})
310-
// TODO: convert this into an error
311-
.unwrap(),
312-
Vec::new(),
313-
)
314312
} else {
315313
(
316314
grid.kinematics()
317315
.iter()
318316
.zip(subgrid.node_values())
319317
.find_map(|(kin, node_values)| {
320-
matches!(kin, &Kinematics::X(idx) if idx == 1)
318+
matches!(kin, &Kinematics::X(idx) if idx == 0)
321319
.then_some(node_values)
322320
})
323321
// TODO: convert this into an error
@@ -376,7 +374,7 @@ pub fn convert_into_applgrid(
376374
weightgrid.as_mut(),
377375
appl_q2_idx,
378376
appl_x1_idx[indices[1]],
379-
if has_pdf1 && has_pdf2 {
377+
if convolutions == 2 {
380378
appl_x2_idx[indices[2]]
381379
} else {
382380
0

0 commit comments

Comments
 (0)