diff --git a/CHANGELOG.md b/CHANGELOG.md index 4994e0660..92d282ac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- added a missing implementation for a branch in `Grid::merge` that was + triggered when exporting some PineAPPL grids generated from the 'pinejet' + group +- fixed wrong coupling orders when exporting to APPLgrid. This happened when + the PineAPPL grid had orders that had any other ordering than 'LO', 'NLO', + 'NNLO' +- fixed a bug that caused exported grids to compare unsuccessfully when the + convolution functions were proton-anti-proton; APPLgrid doesn't store the + types of convolution functions, so we simply convert the grid to use only + proton PDFs + ## [1.1.0] - 08/07/2025 ### Added diff --git a/maintainer/test-cli-export.sh b/maintainer/test-cli-export.sh new file mode 100755 index 000000000..c1afbb059 --- /dev/null +++ b/maintainer/test-cli-export.sh @@ -0,0 +1,66 @@ +#!/bin/bash -x + +set -euo pipefail + +grids=( + ## Ploughshare + + # Group: pinejet + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wm-arxiv-1109.5141/pinejet-atlas-wm-arxiv-1109.5141.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wm-arxiv-1603.09222/pinejet-atlas-wm-arxiv-1603.09222.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wm-arxiv-1612.03016/pinejet-atlas-wm-arxiv-1612.03016.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wp-arxiv-1109.5141/pinejet-atlas-wp-arxiv-1109.5141.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wp-arxiv-1603.09222/pinejet-atlas-wp-arxiv-1603.09222.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-wp-arxiv-1612.03016/pinejet-atlas-wp-arxiv-1612.03016.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1109.5141/pinejet-atlas-z0-arxiv-1109.5141.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1305.4192/pinejet-atlas-z0-arxiv-1305.4192.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1404.1212/pinejet-atlas-z0-arxiv-1404.1212.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1603.09222/pinejet-atlas-z0-arxiv-1603.09222.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1606.01736/pinejet-atlas-z0-arxiv-1606.01736.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1612.03016/pinejet-atlas-z0-arxiv-1612.03016.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-atlas-z0-arxiv-1710.05167/pinejet-atlas-z0-arxiv-1710.05167.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cdf-z0-arxiv-0908.3914/pinejet-cdf-z0-arxiv-0908.3914.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wm-arxiv-1206.2598/pinejet-cms-wm-arxiv-1206.2598.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wm-arxiv-1312.6283/pinejet-cms-wm-arxiv-1312.6283.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wm-arxiv-1603.01803/pinejet-cms-wm-arxiv-1603.01803.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wp-arxiv-1206.2598/pinejet-cms-wp-arxiv-1206.2598.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wp-arxiv-1312.6283/pinejet-cms-wp-arxiv-1312.6283.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-cms-wp-arxiv-1603.01803/pinejet-cms-wp-arxiv-1603.01803.tgz" + #"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 + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-d0-wm-arxiv-1309.2591/pinejet-d0-wm-arxiv-1309.2591.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-d0-wp-arxiv-1309.2591/pinejet-d0-wp-arxiv-1309.2591.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-d0-z0-arxiv-0702025/pinejet-d0-z0-arxiv-0702025.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wm-arxiv-1505.07024/pinejet-lhcb-wm-arxiv-1505.07024.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wm-arxiv-1511.08039/pinejet-lhcb-wm-arxiv-1511.08039.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wp-arxiv-1505.07024/pinejet-lhcb-wp-arxiv-1505.07024.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-wp-arxiv-1511.08039/pinejet-lhcb-wp-arxiv-1511.08039.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-z0-arxiv-1505.07024/pinejet-lhcb-z0-arxiv-1505.07024.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-z0-arxiv-1511.08039/pinejet-lhcb-z0-arxiv-1511.08039.tgz" + "https://ploughshare.web.cern.ch/ploughshare/db/pinejet/pinejet-lhcb-z0-arxiv-1607.06495/pinejet-lhcb-z0-arxiv-1607.06495.tgz" +) + +tmp=$(mktemp -d) +cd "${tmp}" + +trap "cd && rm -rf ${tmp}" EXIT SIGKILL + +for grid in ${grids[@]}; do + archive=${grid##*/} + wget --no-verbose --no-clobber "${grid}" -O /tmp/"${archive}" || true + mkdir subdir + tar xzf /tmp/"${archive}" -C subdir + + for grid in $(find subdir \( -name '*.lz4' \)); do + if [[ $(basename $grid) =~ ^\..* ]]; then + continue + fi + + converted_grid="${grid}".appl + reimported_grid="${grid}".new.pineappl.lz4 + pineappl export --accuracy 1e-12 "${grid}" "${converted_grid}" NNPDF31_nnlo_as_0118_luxqed + pineappl import --accuracy 1e-12 "${converted_grid}" "${reimported_grid}" NNPDF31_nnlo_as_0118_luxqed + du -h "${converted_grid}" "${reimported_grid}" + done + + rm -r subdir +done diff --git a/pineappl/src/grid.rs b/pineappl/src/grid.rs index 48d2d84e4..82244a6c9 100644 --- a/pineappl/src/grid.rs +++ b/pineappl/src/grid.rs @@ -944,17 +944,8 @@ impl Grid { .multi_slice_mut((s![.., .., index], s![.., .., other_index])); for (lhs, rhs) in a.iter_mut().zip(b.iter_mut()) { - if !rhs.is_empty() { - if lhs.is_empty() { - // we can't merge into an EmptySubgridV1 - *lhs = mem::replace(rhs, EmptySubgridV1.into()); - // transpose `lhs` - todo!(); - } else { - lhs.merge(rhs, Some((a_subgrid, b_subgrid))); - *rhs = EmptySubgridV1.into(); - } - } + lhs.merge(rhs, Some((a_subgrid, b_subgrid))); + *rhs = EmptySubgridV1.into(); } } } diff --git a/pineappl/src/subgrid.rs b/pineappl/src/subgrid.rs index 799bb51ae..d575abaaf 100644 --- a/pineappl/src/subgrid.rs +++ b/pineappl/src/subgrid.rs @@ -418,15 +418,21 @@ impl SubgridEnum { if other.is_empty() { return; } + if let Self::EmptySubgridV1(_) = self { - if transpose.is_none() { + if transpose.is_some() { + // change the type of `self` to the type of `other` *self = other.clone(); + // TODO: emptying `self` could be done more efficiently, we're probably storing a + // lot of zeros here + self.scale(0.0); } else { - todo!(); + *self = other.clone(); + return; } - } else { - self.merge_impl(other, transpose); } + + self.merge_impl(other, transpose); } } diff --git a/pineappl_cli/src/export/applgrid.rs b/pineappl_cli/src/export/applgrid.rs index 109c9c098..a251a062b 100644 --- a/pineappl_cli/src/export/applgrid.rs +++ b/pineappl_cli/src/export/applgrid.rs @@ -111,8 +111,19 @@ pub fn convert_into_applgrid( bail!("grid has non-consecutive bin limits, which APPLgrid does not support"); } - if grid.convolutions().len() > 2 { - bail!("APPLgrid does not support grids with more than two convolutions"); + match grid.convolutions() { + [_] => {} + [a, b] => { + if a != b { + if a.cc() == *b { + // use charge conjugate to map hadron-anti-hadron grids to use the same single + // convolution function + let index = if a.pid() < 0 { 0 } else { 1 }; + grid.charge_conjugate(index); + } + } + } + _ => bail!("APPLgrid does not support grids with more than two convolutions"), } // APPLgrid only understands PDG PIDs @@ -156,9 +167,11 @@ pub fn convert_into_applgrid( .collect(); // `id` must end with '.config' for APPLgrid to know its type is `lumi_pdf` - let id = "PineAPPL-Lumi.config"; + let id = format!("{}.config", output.file_stem() + // UNWRAP: because we write to that file in the end, there always must be a file name + .unwrap().to_string_lossy()); // this object is managed by APPLgrid internally - ffi::make_lumi_pdf(id, &combinations).into_raw(); + ffi::make_lumi_pdf(&id, &combinations).into_raw(); let limits: Vec<_> = grid .bwfl() @@ -200,17 +213,17 @@ pub fn convert_into_applgrid( - lo_alphas; let mut applgrid = - ffi::make_empty_grid(&limits, id, lo_alphas.into(), loops.into(), "f2", "h0"); + ffi::make_empty_grid(&limits, &id, lo_alphas.into(), loops.into(), "f2", "h0"); - let has_pdf1 = !grid.convolutions().is_empty(); - let has_pdf2 = grid.convolutions().get(1).is_some(); + // APPLgrid has either two or one convolution(s) + let convolutions = grid.convolutions().len(); - for (appl_order, order) in order_mask + for order in order_mask .iter() .enumerate() .filter_map(|(index, keep)| keep.then_some(index)) - .enumerate() { + let appl_order = grid.orders()[order].alphas - lo_alphas; let factor = TAU.powi(grid.orders()[order].alphas.into()); for (bin, subgrids) in grid @@ -275,9 +288,7 @@ pub fn convert_into_applgrid( }) .collect::>()?; - // in the DIS case APPLgrid always uses the first x dimension - - let (x1_grid, x2_grid) = if has_pdf1 && has_pdf2 { + let (x1_grid, x2_grid) = if convolutions == 2 { ( grid.kinematics() .iter() @@ -298,26 +309,13 @@ pub fn convert_into_applgrid( // TODO: convert this into an error .unwrap(), ) - } else if has_pdf1 { - ( - grid.kinematics() - .iter() - .zip(subgrid.node_values()) - .find_map(|(kin, node_values)| { - matches!(kin, &Kinematics::X(idx) if idx == 0) - .then_some(node_values) - }) - // TODO: convert this into an error - .unwrap(), - Vec::new(), - ) } else { ( grid.kinematics() .iter() .zip(subgrid.node_values()) .find_map(|(kin, node_values)| { - matches!(kin, &Kinematics::X(idx) if idx == 1) + matches!(kin, &Kinematics::X(idx) if idx == 0) .then_some(node_values) }) // TODO: convert this into an error @@ -376,7 +374,7 @@ pub fn convert_into_applgrid( weightgrid.as_mut(), appl_q2_idx, appl_x1_idx[indices[1]], - if has_pdf1 && has_pdf2 { + if convolutions == 2 { appl_x2_idx[indices[2]] } else { 0