From 0182c39dcc26d506c501ba38498a81bff3aab545 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Tue, 5 May 2026 22:01:25 +0200 Subject: [PATCH 1/9] Fix non-matching momentum fractions in exporting into APPLGrid --- pineappl_cli/src/export.rs | 24 +++++++++++-- pineappl_cli/src/export/applgrid.rs | 54 ++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/pineappl_cli/src/export.rs b/pineappl_cli/src/export.rs index 8a2ad659..c7a7e2d0 100644 --- a/pineappl_cli/src/export.rs +++ b/pineappl_cli/src/export.rs @@ -19,11 +19,16 @@ fn convert_into_applgrid( conv_funs: &mut [Pdf], _: usize, discard_non_matching_scales: bool, + discard_non_matching_momentum: bool, ) -> Result<(&'static str, Vec, usize, Vec)> { // TODO: check also scale-varied results - let (mut applgrid, order_mask) = - applgrid::convert_into_applgrid(grid, output, discard_non_matching_scales)?; + let (mut applgrid, order_mask) = applgrid::convert_into_applgrid( + grid, + output, + discard_non_matching_scales, + discard_non_matching_momentum, + )?; let results = applgrid::convolve_applgrid(applgrid.pin_mut(), conv_funs); Ok(("APPLgrid", results, 1, order_mask)) @@ -36,6 +41,7 @@ fn convert_into_applgrid( _: &mut [Pdf], _: usize, _: bool, + _: bool, ) -> Result<(&'static str, Vec, usize, Vec)> { Err(anyhow!( "you need to install `pineappl` with feature `applgrid`" @@ -48,11 +54,19 @@ fn convert_into_grid( conv_funs: &mut [Pdf], scales: usize, discard_non_matching_scales: bool, + discard_non_matching_momentum: bool, ) -> Result<(&'static str, Vec, usize, Vec)> { if let Some(extension) = output.extension() && (extension == "appl" || extension == "root") { - return convert_into_applgrid(output, grid, conv_funs, scales, discard_non_matching_scales); + return convert_into_applgrid( + output, + grid, + conv_funs, + scales, + discard_non_matching_scales, + discard_non_matching_momentum, + ); } Err(anyhow!("could not detect file format")) @@ -75,6 +89,9 @@ pub struct Opts { /// Discard non-matching scales that would otherwise lead to panics. #[arg(long)] discard_non_matching_scales: bool, + /// Discard non-matching momentum fractions that would otherwise lead to panics. + #[arg(long)] + discard_non_matching_momentum: bool, /// Set the number of scale variations to compare with if they are available. #[arg( default_value_t = 7, @@ -105,6 +122,7 @@ impl Subcommand for Opts { &mut conv_funs, self.scales, self.discard_non_matching_scales, + self.discard_non_matching_momentum, )?; for Order { diff --git a/pineappl_cli/src/export/applgrid.rs b/pineappl_cli/src/export/applgrid.rs index a1ac190a..10bc5278 100644 --- a/pineappl_cli/src/export/applgrid.rs +++ b/pineappl_cli/src/export/applgrid.rs @@ -96,6 +96,7 @@ pub fn convert_into_applgrid( grid: &mut Grid, output: &Path, discard_non_matching_scales: bool, + discard_non_matching_momentum: bool, ) -> Result<(UniquePtr, Vec)> { let dim = grid.bwfl().dimensions(); @@ -334,7 +335,13 @@ pub fn convert_into_applgrid( .iter() .position(|&x| subgrid::node_value_eq(x, x1)) .map_or_else( - || bail!("momentum fraction x1 = {x1} not found in APPLgrid"), + || { + if discard_non_matching_momentum { + Ok(-1) + } else { + bail!("momentum fraction x1 = {x1} not found in APPLgrid") + } + }, |idx| Ok(idx.try_into().unwrap()), ) }) @@ -346,7 +353,13 @@ pub fn convert_into_applgrid( .iter() .position(|&x| subgrid::node_value_eq(x, x2)) .map_or_else( - || bail!("momentum fraction x2 = {x2} not found in APPLgrid"), + || { + if discard_non_matching_momentum { + Ok(-1) + } else { + bail!("momentum fraction x2 = {x2} not found in APPLgrid") + } + }, |idx| Ok(idx.try_into().unwrap()), ) }) @@ -373,15 +386,40 @@ pub fn convert_into_applgrid( continue; } + let appl_x1_i = appl_x1_idx[indices[1]]; + if appl_x1_i == -1 { + if value != 0.0 { + println!( + "WARNING: discarding non-matching momentum fraction x1 = {}", + x1_grid[indices[1]] + ); + } + + continue; + } + + let appl_x2_i = if convolutions == 2 { + let i = appl_x2_idx[indices[2]]; + if i == -1 { + if value != 0.0 { + println!( + "WARNING: discarding non-matching momentum fraction x2 = {}", + x2_grid[indices[2]] + ); + } + + continue; + } + i + } else { + 0 + }; + ffi::sparse_matrix_set( weightgrid.as_mut(), appl_q2_idx, - appl_x1_idx[indices[1]], - if convolutions == 2 { - appl_x2_idx[indices[2]] - } else { - 0 - }, + appl_x1_i, + appl_x2_i, factor * value, ); } From e6a80da456d4e486c159c2c8b4e57e03eb46f5bf Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Tue, 5 May 2026 22:17:24 +0200 Subject: [PATCH 2/9] Adjust CLI test --- pineappl_cli/tests/export.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pineappl_cli/tests/export.rs b/pineappl_cli/tests/export.rs index bd25e451..44de63ea 100644 --- a/pineappl_cli/tests/export.rs +++ b/pineappl_cli/tests/export.rs @@ -15,12 +15,13 @@ Arguments: LHAPDF ID(s) or name of the PDF(s)/FF(s) to check the converted grid with Options: - --accuracy Relative threshold between the table and the converted grid when comparison fails [default: 1e-10] - --discard-non-matching-scales Discard non-matching scales that would otherwise lead to panics - -s, --scales Set the number of scale variations to compare with if they are available [default: 7] [possible values: 1, 3, 7, 9] - --digits-abs Set the number of fractional digits shown for absolute numbers [default: 7] - --digits-rel Set the number of fractional digits shown for relative numbers [default: 7] - -h, --help Print help + --accuracy Relative threshold between the table and the converted grid when comparison fails [default: 1e-10] + --discard-non-matching-scales Discard non-matching scales that would otherwise lead to panics + --discard-non-matching-momentum Discard non-matching momentum fractions that would otherwise lead to panics + -s, --scales Set the number of scale variations to compare with if they are available [default: 7] [possible values: 1, 3, 7, 9] + --digits-abs Set the number of fractional digits shown for absolute numbers [default: 7] + --digits-rel Set the number of fractional digits shown for relative numbers [default: 7] + -h, --help Print help "; #[cfg(feature = "applgrid")] From 8ba54161e590dc7289a92e321b48bb748d0376e0 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 7 May 2026 14:19:14 +0200 Subject: [PATCH 3/9] Combine scales and momentum fractions into `--discard-non-matching-momentum` --- pineappl_cli/src/export.rs | 36 ++++++++--------------------- pineappl_cli/src/export/applgrid.rs | 9 ++++---- pineappl_cli/tests/export.rs | 13 +++++------ 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/pineappl_cli/src/export.rs b/pineappl_cli/src/export.rs index c7a7e2d0..0ab7c9ca 100644 --- a/pineappl_cli/src/export.rs +++ b/pineappl_cli/src/export.rs @@ -18,17 +18,12 @@ fn convert_into_applgrid( grid: &mut Grid, conv_funs: &mut [Pdf], _: usize, - discard_non_matching_scales: bool, - discard_non_matching_momentum: bool, + discard_non_matching_values: bool, ) -> Result<(&'static str, Vec, usize, Vec)> { // TODO: check also scale-varied results - let (mut applgrid, order_mask) = applgrid::convert_into_applgrid( - grid, - output, - discard_non_matching_scales, - discard_non_matching_momentum, - )?; + let (mut applgrid, order_mask) = + applgrid::convert_into_applgrid(grid, output, discard_non_matching_values)?; let results = applgrid::convolve_applgrid(applgrid.pin_mut(), conv_funs); Ok(("APPLgrid", results, 1, order_mask)) @@ -41,7 +36,6 @@ fn convert_into_applgrid( _: &mut [Pdf], _: usize, _: bool, - _: bool, ) -> Result<(&'static str, Vec, usize, Vec)> { Err(anyhow!( "you need to install `pineappl` with feature `applgrid`" @@ -53,20 +47,12 @@ fn convert_into_grid( grid: &mut Grid, conv_funs: &mut [Pdf], scales: usize, - discard_non_matching_scales: bool, - discard_non_matching_momentum: bool, + discard_non_matching_values: bool, ) -> Result<(&'static str, Vec, usize, Vec)> { if let Some(extension) = output.extension() && (extension == "appl" || extension == "root") { - return convert_into_applgrid( - output, - grid, - conv_funs, - scales, - discard_non_matching_scales, - discard_non_matching_momentum, - ); + return convert_into_applgrid(output, grid, conv_funs, scales, discard_non_matching_values); } Err(anyhow!("could not detect file format")) @@ -86,12 +72,9 @@ pub struct Opts { /// Relative threshold between the table and the converted grid when comparison fails. #[arg(default_value = "1e-10", long)] accuracy: f64, - /// Discard non-matching scales that would otherwise lead to panics. - #[arg(long)] - discard_non_matching_scales: bool, - /// Discard non-matching momentum fractions that would otherwise lead to panics. - #[arg(long)] - discard_non_matching_momentum: bool, + /// Discard non-matching scales or momentum fractions that would otherwise lead to panics. + #[arg(long, aliases = ["discard-non-matching-scales", "discard-non-matching-momentum"])] + discard_non_matching_values: bool, /// Set the number of scale variations to compare with if they are available. #[arg( default_value_t = 7, @@ -121,8 +104,7 @@ impl Subcommand for Opts { &mut grid, &mut conv_funs, self.scales, - self.discard_non_matching_scales, - self.discard_non_matching_momentum, + self.discard_non_matching_values, )?; for Order { diff --git a/pineappl_cli/src/export/applgrid.rs b/pineappl_cli/src/export/applgrid.rs index 10bc5278..961aefc2 100644 --- a/pineappl_cli/src/export/applgrid.rs +++ b/pineappl_cli/src/export/applgrid.rs @@ -95,8 +95,7 @@ fn reconstruct_subgrid_params(grid: &Grid, order: usize, bin: usize) -> Result Result<(UniquePtr, Vec)> { let dim = grid.bwfl().dimensions(); @@ -279,7 +278,7 @@ pub fn convert_into_applgrid( .position(|&x| subgrid::node_value_eq(x, fac)) .map_or_else( || { - if discard_non_matching_scales { + if discard_non_matching_values { Ok(-1) } else { bail!( @@ -336,7 +335,7 @@ pub fn convert_into_applgrid( .position(|&x| subgrid::node_value_eq(x, x1)) .map_or_else( || { - if discard_non_matching_momentum { + if discard_non_matching_values { Ok(-1) } else { bail!("momentum fraction x1 = {x1} not found in APPLgrid") @@ -354,7 +353,7 @@ pub fn convert_into_applgrid( .position(|&x| subgrid::node_value_eq(x, x2)) .map_or_else( || { - if discard_non_matching_momentum { + if discard_non_matching_values { Ok(-1) } else { bail!("momentum fraction x2 = {x2} not found in APPLgrid") diff --git a/pineappl_cli/tests/export.rs b/pineappl_cli/tests/export.rs index 44de63ea..55c063c5 100644 --- a/pineappl_cli/tests/export.rs +++ b/pineappl_cli/tests/export.rs @@ -15,13 +15,12 @@ Arguments: LHAPDF ID(s) or name of the PDF(s)/FF(s) to check the converted grid with Options: - --accuracy Relative threshold between the table and the converted grid when comparison fails [default: 1e-10] - --discard-non-matching-scales Discard non-matching scales that would otherwise lead to panics - --discard-non-matching-momentum Discard non-matching momentum fractions that would otherwise lead to panics - -s, --scales Set the number of scale variations to compare with if they are available [default: 7] [possible values: 1, 3, 7, 9] - --digits-abs Set the number of fractional digits shown for absolute numbers [default: 7] - --digits-rel Set the number of fractional digits shown for relative numbers [default: 7] - -h, --help Print help + --accuracy Relative threshold between the table and the converted grid when comparison fails [default: 1e-10] + --discard-non-matching-values Discard non-matching scales or momentum fractions that would otherwise lead to panics + -s, --scales Set the number of scale variations to compare with if they are available [default: 7] [possible values: 1, 3, 7, 9] + --digits-abs Set the number of fractional digits shown for absolute numbers [default: 7] + --digits-rel Set the number of fractional digits shown for relative numbers [default: 7] + -h, --help Print help "; #[cfg(feature = "applgrid")] From 0a63f5f1b8096a0306a5e9299cdafeb24eb65a41 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 7 May 2026 14:46:20 +0200 Subject: [PATCH 4/9] Test export into APPLgrid --- pineappl_cli/tests/export.rs | 150 +++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/pineappl_cli/tests/export.rs b/pineappl_cli/tests/export.rs index 55c063c5..1c150f1a 100644 --- a/pineappl_cli/tests/export.rs +++ b/pineappl_cli/tests/export.rs @@ -132,6 +132,156 @@ fn export_applgrid() { .stdout(predicates::str::ends_with(EXPORT_APPLGRID_STR)); } +#[cfg(feature = "applgrid")] +const EXPORT_NNLO_AK5_PTJ_STR: &str = "b APPLgrid PineAPPL rel. diff +--+------------+------------+-------------- +0 7.1475199e8 7.1475253e8 7.6053554e-7 +1 2.5536645e8 2.5536667e8 8.6645764e-7 +2 8.0742409e7 8.0742409e7 1.2478907e-13 +3 4.0260433e7 4.0260445e7 2.8169024e-7 +4 1.9839883e7 1.9839887e7 2.1808465e-7 +5 1.0119027e7 1.0119028e7 1.3544486e-7 +6 5.5702376e6 5.5702379e6 5.8673922e-8 +7 3.1322190e6 3.1322207e6 5.3759875e-7 +8 1.9447742e6 1.9447743e6 4.2597541e-8 +9 1.1701564e6 1.1701565e6 7.4993014e-8 +10 7.2929160e5 7.2929198e5 5.1385162e-7 +11 4.7885350e5 4.7885359e5 1.8458927e-7 +12 3.0583779e5 3.0583781e5 7.0650601e-8 +13 2.0537268e5 2.0537272e5 1.9724071e-7 +14 1.3581347e5 1.3581349e5 1.4894264e-7 +15 9.3481731e4 9.3481745e4 1.5065258e-7 +16 6.1789306e4 6.1789317e4 1.7921741e-7 +17 4.2234799e4 4.2234800e4 3.7378332e-9 +18 2.9446997e4 2.9446998e4 2.7827810e-8 +19 2.0711158e4 2.0711158e4 3.6083760e-9 +20 1.4399288e4 1.4399288e4 -1.6959262e-9 +21 1.0204248e4 1.0204248e4 2.1560531e-13 +22 7.0541335e3 7.0541335e3 2.0916602e-13 +23 5.1274795e3 5.1274798e3 5.3440854e-8 +24 3.5785021e3 3.5785021e3 -7.3925543e-10 +25 2.5497806e3 2.5497806e3 2.0650148e-13 +26 1.8525134e3 1.8525134e3 -7.1513917e-10 +27 1.3124163e3 1.3124164e3 6.5466644e-8 +28 9.3353298e2 9.3353296e2 -2.4834690e-8 +29 6.8182565e2 6.8182566e2 1.9250868e-8 +30 4.8152109e2 4.8152109e2 2.0583535e-13 +31 3.4599110e2 3.4599110e2 2.0228264e-13 +32 2.4853801e2 2.4853801e2 1.9961810e-13 +33 1.8153858e2 1.8153858e2 1.9295676e-13 +34 1.2893001e2 1.2893001e2 1.8895996e-13 +35 9.2863897e1 9.2863897e1 -1.2712831e-11 +36 6.8219858e1 6.8219863e1 6.7323209e-8 +37 4.8499048e1 4.8499049e1 2.6932878e-9 +38 3.5260803e1 3.5260804e1 3.1629175e-8 +39 2.4875207e1 2.4875207e1 1.5327475e-9 +40 1.8248343e1 1.8248344e1 9.2649788e-8 +41 1.2848351e1 1.2848351e1 -3.4248875e-8 +42 9.3637433e0 9.3637433e0 1.0366541e-9 +43 6.5025357e0 6.5025357e0 2.9029490e-9 +44 4.7299678e0 4.7299683e0 1.1183387e-7 +45 3.3169392e0 3.3169393e0 1.6485298e-8 +46 2.3366424e0 2.3366426e0 7.2538949e-8 +47 1.6375345e0 1.6375347e0 1.3000311e-7 +48 1.1395783e0 1.1395784e0 8.0374843e-8 +49 7.7573740e-1 7.7573745e-1 6.3753925e-8 +50 5.3385646e-1 5.3385655e-1 1.7113346e-7 +51 3.5431304e-1 3.5431287e-1 -4.7859888e-7 +52 2.3984658e-1 2.3984660e-1 7.3858889e-8 +53 1.5742653e-1 1.5742660e-1 4.7452613e-7 +54 1.0381680e-1 1.0381681e-1 1.3036277e-7 +55 6.7367194e-2 6.7367194e-2 4.7924602e-9 +56 4.1754029e-2 4.1754049e-2 4.8244591e-7 +57 2.5167475e-2 2.5167484e-2 3.3871366e-7 +58 1.5430966e-2 1.5430971e-2 3.0639168e-7 +59 8.9218263e-3 8.9218286e-3 2.6013356e-7 +60 5.0265483e-3 5.0265486e-3 6.8485890e-8 +61 2.9066861e-3 2.9066853e-3 -2.9518261e-7 +62 1.4089133e-3 1.4089139e-3 4.5456056e-7 +63 6.7833493e-4 6.7833993e-4 7.3775731e-6 +64 1.0497365e-4 1.0497395e-4 2.8590177e-6 +"; + +#[test] +#[cfg(feature = "applgrid")] +fn export_nnlo_ak5_ptj_discard_non_matching_values() { + let output = NamedTempFile::new("nnlo.ak5_ptj.appl").unwrap(); + + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "export", + "--discard-non-matching-values", + "--accuracy=1e-5", + "../test-data/nnlo.ak5_ptj.pineappl.lz4", + output.path().to_str().unwrap(), + "NNPDF31_nlo_as_0118_luxqed", + ]) + .assert() + .success() + .stdout(predicates::str::ends_with(EXPORT_NNLO_AK5_PTJ_STR)); +} + +#[test] +#[cfg(feature = "applgrid")] +fn export_nnlo_ak5_ptj_discard_non_matching_scales_alias() { + let output = NamedTempFile::new("nnlo.ak5_ptj.appl").unwrap(); + + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "export", + "--discard-non-matching-scales", + "--accuracy=1e-5", + "../test-data/nnlo.ak5_ptj.pineappl.lz4", + output.path().to_str().unwrap(), + "NNPDF31_nlo_as_0118_luxqed", + ]) + .assert() + .success() + .stdout(predicates::str::ends_with(EXPORT_NNLO_AK5_PTJ_STR)); +} + +#[test] +#[cfg(feature = "applgrid")] +fn export_nnlo_ak5_ptj_discard_non_matching_momentum_alias() { + let output = NamedTempFile::new("nnlo.ak5_ptj.appl").unwrap(); + + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "export", + "--discard-non-matching-momentum", + "--accuracy=1e-5", + "../test-data/nnlo.ak5_ptj.pineappl.lz4", + output.path().to_str().unwrap(), + "NNPDF31_nlo_as_0118_luxqed", + ]) + .assert() + .success() + .stdout(predicates::str::ends_with(EXPORT_NNLO_AK5_PTJ_STR)); +} + +#[test] +#[cfg(feature = "applgrid")] +fn export_nnlo_ak5_ptj_no_discard_fails() { + let output = NamedTempFile::new("converted.appl").unwrap(); + + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "export", + "../test-data/nnlo.ak5_ptj.pineappl.lz4", + output.path().to_str().unwrap(), + "NNPDF31_nlo_as_0118_luxqed", + ]) + .assert() + .failure() + .stderr(predicates::str::contains( + "factorization scale muf2 = 33634.5450347851 not found in APPLgrid", + )); +} + #[test] #[cfg(feature = "applgrid")] fn export_dis_applgrid() { From 47d7a54b6e92a72e61efe11d190f3405db2cc69f Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 7 May 2026 14:50:15 +0200 Subject: [PATCH 5/9] Upload grid test and bump cache version --- .github/actions/cache-test-data/action.yml | 2 +- maintainer/download-test-data.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/cache-test-data/action.yml b/.github/actions/cache-test-data/action.yml index 855d6704..96b8a773 100644 --- a/.github/actions/cache-test-data/action.yml +++ b/.github/actions/cache-test-data/action.yml @@ -10,7 +10,7 @@ runs: uses: actions/cache@v4 with: path: test-data - key: test-data-v30 + key: test-data-v31 - name: Download test data if cache miss if: steps.cache.outputs.cache-hit != 'true' run: | diff --git a/maintainer/download-test-data.sh b/maintainer/download-test-data.sh index 63cab140..bde034e8 100755 --- a/maintainer/download-test-data.sh +++ b/maintainer/download-test-data.sh @@ -44,6 +44,7 @@ files=( 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_7TEV_opt.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_7TEV_v2.tar' 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_7TEV_v2_xif_2.tar' + 'https://data.nnpdf.science/pineappl/test-data/nnlo.ak5_ptj.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/NJetEvents_0-0-2.tab.gz' 'https://data.nnpdf.science/pineappl/test-data/NNPDF_POS_F2D_40.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/NUTEV_CC_NU_FE_SIGMARED.pineappl.lz4' From ddf48a09d0d7c1003ac9795dad378129640a6fbb Mon Sep 17 00:00:00 2001 From: Christopher Schwan Date: Sat, 9 May 2026 08:43:59 +0200 Subject: [PATCH 6/9] Improve help and error strings a bit --- pineappl_cli/src/export.rs | 2 +- pineappl_cli/src/export/applgrid.rs | 21 ++++++++++++--------- pineappl_cli/tests/export.rs | 10 ++++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/pineappl_cli/src/export.rs b/pineappl_cli/src/export.rs index 0ab7c9ca..47e4a42e 100644 --- a/pineappl_cli/src/export.rs +++ b/pineappl_cli/src/export.rs @@ -72,7 +72,7 @@ pub struct Opts { /// Relative threshold between the table and the converted grid when comparison fails. #[arg(default_value = "1e-10", long)] accuracy: f64, - /// Discard non-matching scales or momentum fractions that would otherwise lead to panics. + /// Discard non-matching scales and momentum fractions that would otherwise fail the export. #[arg(long, aliases = ["discard-non-matching-scales", "discard-non-matching-momentum"])] discard_non_matching_values: bool, /// Set the number of scale variations to compare with if they are available. diff --git a/pineappl_cli/src/export/applgrid.rs b/pineappl_cli/src/export/applgrid.rs index 961aefc2..66a00a8c 100644 --- a/pineappl_cli/src/export/applgrid.rs +++ b/pineappl_cli/src/export/applgrid.rs @@ -282,7 +282,7 @@ pub fn convert_into_applgrid( Ok(-1) } else { bail!( - "factorization scale muf2 = {fac} not found in APPLgrid", + "factorization scale muf2 = {fac} not found in APPLgrid; try exporting with `--discard-non-matching-values`", ) } }, @@ -338,7 +338,7 @@ pub fn convert_into_applgrid( if discard_non_matching_values { Ok(-1) } else { - bail!("momentum fraction x1 = {x1} not found in APPLgrid") + bail!("momentum fraction x1 = {x1} not found in APPLgrid; try exporting with `--discard-non-matching-values`") } }, |idx| Ok(idx.try_into().unwrap()), @@ -356,7 +356,7 @@ pub fn convert_into_applgrid( if discard_non_matching_values { Ok(-1) } else { - bail!("momentum fraction x2 = {x2} not found in APPLgrid") + bail!("momentum fraction x2 = {x2} not found in APPLgrid; try exporting with `--discard-non-matching-values`") } }, |idx| Ok(idx.try_into().unwrap()), @@ -375,10 +375,11 @@ pub fn convert_into_applgrid( if appl_q2_idx == -1 { if value != 0.0 { println!( - "WARNING: discarding non-matching scale muf2 = {}", + "WARNING: discarding non-matching scale muf2 = {} in subgrid {:?}", grid.scales() .fac - .calc(&subgrid.node_values(), grid.kinematics())[iq2] + .calc(&subgrid.node_values(), grid.kinematics())[iq2], + (order, bin, channel) ); } @@ -389,8 +390,9 @@ pub fn convert_into_applgrid( if appl_x1_i == -1 { if value != 0.0 { println!( - "WARNING: discarding non-matching momentum fraction x1 = {}", - x1_grid[indices[1]] + "WARNING: discarding non-matching momentum fraction x1 = {} in subgrid {:?}", + x1_grid[indices[1]], + (order, bin, channel) ); } @@ -402,8 +404,9 @@ pub fn convert_into_applgrid( if i == -1 { if value != 0.0 { println!( - "WARNING: discarding non-matching momentum fraction x2 = {}", - x2_grid[indices[2]] + "WARNING: discarding non-matching momentum fraction x2 = {} in subgrid {:?}", + x2_grid[indices[2]], + (order, bin, channel) ); } diff --git a/pineappl_cli/tests/export.rs b/pineappl_cli/tests/export.rs index 1c150f1a..f44256d1 100644 --- a/pineappl_cli/tests/export.rs +++ b/pineappl_cli/tests/export.rs @@ -16,7 +16,7 @@ Arguments: Options: --accuracy Relative threshold between the table and the converted grid when comparison fails [default: 1e-10] - --discard-non-matching-values Discard non-matching scales or momentum fractions that would otherwise lead to panics + --discard-non-matching-values Discard non-matching scales and momentum fractions that would otherwise fail the export -s, --scales Set the number of scale variations to compare with if they are available [default: 7] [possible values: 1, 3, 7, 9] --digits-abs Set the number of fractional digits shown for absolute numbers [default: 7] --digits-rel Set the number of fractional digits shown for relative numbers [default: 7] @@ -202,6 +202,10 @@ const EXPORT_NNLO_AK5_PTJ_STR: &str = "b APPLgrid PineAPPL rel. diff 64 1.0497365e-4 1.0497395e-4 2.8590177e-6 "; +#[cfg(feature = "applgrid")] +const EXPORT_NNLO_AK5_PTJ_NO_DISCARD_FAILS_STR: &str = "Error: factorization scale muf2 = 33634.5450347851 not found in APPLgrid; try exporting with `--discard-non-matching-values` +"; + #[test] #[cfg(feature = "applgrid")] fn export_nnlo_ak5_ptj_discard_non_matching_values() { @@ -277,9 +281,7 @@ fn export_nnlo_ak5_ptj_no_discard_fails() { ]) .assert() .failure() - .stderr(predicates::str::contains( - "factorization scale muf2 = 33634.5450347851 not found in APPLgrid", - )); + .stderr(EXPORT_NNLO_AK5_PTJ_NO_DISCARD_FAILS_STR); } #[test] From 08cc5e0d4ba8e4ccabce53e39f536d18501a55bb Mon Sep 17 00:00:00 2001 From: Christopher Schwan Date: Sat, 9 May 2026 09:43:44 +0200 Subject: [PATCH 7/9] Refactor `pineappl export` code --- pineappl_cli/src/export/applgrid.rs | 204 ++++++++++------------------ 1 file changed, 73 insertions(+), 131 deletions(-) diff --git a/pineappl_cli/src/export/applgrid.rs b/pineappl_cli/src/export/applgrid.rs index 66a00a8c..8e4d17e2 100644 --- a/pineappl_cli/src/export/applgrid.rs +++ b/pineappl_cli/src/export/applgrid.rs @@ -1,6 +1,7 @@ use anyhow::{Result, bail}; use cxx::{UniquePtr, let_cxx_string}; use float_cmp::approx_eq; +use itertools::izip; use lhapdf::Pdf; use ndarray::{Axis, s}; use pineappl::boc::{Channel, Kinematics, Order}; @@ -261,47 +262,31 @@ pub fn convert_into_applgrid( grid.channels().len().try_into().unwrap(), grid.convolutions().len() == 1, ); - let appl_q2: Vec<_> = (0..igrid.Ntau()).map(|i| igrid.getQ2(i)).collect(); - let appl_x1: Vec<_> = (0..igrid.Ny1()).map(|i| igrid.getx1(i)).collect(); - let appl_x2: Vec<_> = (0..igrid.Ny2()).map(|i| igrid.getx2(i)).collect(); + let appl_grids: Vec> = vec![ + (0..igrid.Ntau()).map(|i| igrid.getQ2(i)).collect(), + (0..igrid.Ny1()).map(|i| igrid.getx1(i)).collect(), + (0..igrid.Ny2()).map(|i| igrid.getx2(i)).collect(), + ]; for (channel, subgrid) in subgrids .iter() .enumerate() .filter(|(_, subgrid)| !subgrid.is_empty()) { - let appl_q2_idx: Vec<_> = grid.scales().fac.calc(&subgrid.node_values(), grid.kinematics()) - .iter() - .map(|&fac| { - appl_q2 - .iter() - .position(|&x| subgrid::node_value_eq(x, fac)) - .map_or_else( - || { - if discard_non_matching_values { - Ok(-1) - } else { - bail!( - "factorization scale muf2 = {fac} not found in APPLgrid; try exporting with `--discard-non-matching-values`", - ) - } - }, - |idx| Ok(idx.try_into().unwrap()), - ) - }) - .collect::>()?; - - let (x1_grid, x2_grid) = if convolutions == 2 { - ( - 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(), + let grids = vec![ + grid.scales() + .fac + .calc(&subgrid.node_values(), grid.kinematics()) + .into_owned(), + 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(), + if convolutions == 2 { grid.kinematics() .iter() .zip(subgrid.node_values()) @@ -310,118 +295,75 @@ pub fn convert_into_applgrid( .then_some(node_values) }) // TODO: convert this into an error - .unwrap(), - ) - } else { - ( - 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(), - ) - }; - - let appl_x1_idx: Vec<_> = x1_grid - .iter() - .map(|&x1| { - appl_x1 - .iter() - .position(|&x| subgrid::node_value_eq(x, x1)) - .map_or_else( - || { - if discard_non_matching_values { - Ok(-1) - } else { - bail!("momentum fraction x1 = {x1} not found in APPLgrid; try exporting with `--discard-non-matching-values`") - } - }, - |idx| Ok(idx.try_into().unwrap()), - ) - }) - .collect::>()?; - let appl_x2_idx: Vec<_> = x2_grid - .iter() - .map(|&x2| { - appl_x2 - .iter() - .position(|&x| subgrid::node_value_eq(x, x2)) - .map_or_else( - || { - if discard_non_matching_values { - Ok(-1) - } else { - bail!("momentum fraction x2 = {x2} not found in APPLgrid; try exporting with `--discard-non-matching-values`") - } - }, - |idx| Ok(idx.try_into().unwrap()), - ) - }) - .collect::>()?; + .unwrap() + } else { + Vec::new() + }, + ]; + + let appl_idx: Vec> = izip!(&grids, &appl_grids, ["factorization scale muf2", "momentum fraction x1", "momentum fraction x2"]) + .map(|(grid, appl_grid, label)| { + grid + .iter() + .map(|&value| { + appl_grid + .iter() + .position(|&appl_value| subgrid::node_value_eq(appl_value, value)) + .map_or_else( + || { + if discard_non_matching_values { + Ok(-1) + } else { + bail!("{label} = {value} not found in APPLgrid; try exporting with `--discard-non-matching-values`") + } + }, + |idx| Ok(idx.try_into().unwrap()), + ) + }) + .collect::>() + } + ).collect::>()?; let mut weightgrid = ffi::igrid_weightgrid(igrid.pin_mut(), channel); - for (indices, value) in subgrid.indexed_iter() { + 'looop: for (indices, value) in subgrid.indexed_iter() { // TODO: here we assume that all X are consecutive starting from the second // element and are in ascending order - let iq2 = indices[0]; - let appl_q2_idx = appl_q2_idx[iq2]; - - if appl_q2_idx == -1 { - if value != 0.0 { - println!( - "WARNING: discarding non-matching scale muf2 = {} in subgrid {:?}", - grid.scales() - .fac - .calc(&subgrid.node_values(), grid.kinematics())[iq2], - (order, bin, channel) - ); - } - - continue; - } - - let appl_x1_i = appl_x1_idx[indices[1]]; - if appl_x1_i == -1 { - if value != 0.0 { - println!( - "WARNING: discarding non-matching momentum fraction x1 = {} in subgrid {:?}", - x1_grid[indices[1]], - (order, bin, channel) - ); - } - continue; - } - - let appl_x2_i = if convolutions == 2 { - let i = appl_x2_idx[indices[2]]; - if i == -1 { + let appl_indices = [ + appl_idx[0][indices[0]], + appl_idx[1][indices[1]], + if convolutions == 2 { + appl_idx[2][indices[2]] + } else { + 0 + }, + ]; + + for (&appl_index, grid, &index, label) in izip!( + &appl_indices, + &grids, + &indices, + ["scale muf2", "momentum fraction x1", "momentum fraction x2"] + ) { + if appl_index == -1 { if value != 0.0 { println!( - "WARNING: discarding non-matching momentum fraction x2 = {} in subgrid {:?}", - x2_grid[indices[2]], + "WARNING: discarding non-matching {label} = {} in subgrid {:?}", + grid[index], (order, bin, channel) ); } - continue; + continue 'looop; } - i - } else { - 0 - }; + } ffi::sparse_matrix_set( weightgrid.as_mut(), - appl_q2_idx, - appl_x1_i, - appl_x2_i, + appl_indices[0], + appl_indices[1], + appl_indices[2], factor * value, ); } From f928b80ac2a2d1ee1a92ca132b2235ec8a3f6bf0 Mon Sep 17 00:00:00 2001 From: Christopher Schwan Date: Sat, 9 May 2026 10:10:14 +0200 Subject: [PATCH 8/9] Make test data smaller --- .github/actions/cache-test-data/action.yml | 2 +- maintainer/download-test-data.sh | 2 +- pineappl_cli/tests/export.rs | 80 +++------------------- 3 files changed, 10 insertions(+), 74 deletions(-) diff --git a/.github/actions/cache-test-data/action.yml b/.github/actions/cache-test-data/action.yml index 96b8a773..0e1fa8f2 100644 --- a/.github/actions/cache-test-data/action.yml +++ b/.github/actions/cache-test-data/action.yml @@ -10,7 +10,7 @@ runs: uses: actions/cache@v4 with: path: test-data - key: test-data-v31 + key: test-data-v32 - name: Download test data if cache miss if: steps.cache.outputs.cache-hit != 'true' run: | diff --git a/maintainer/download-test-data.sh b/maintainer/download-test-data.sh index bde034e8..f033a4c7 100755 --- a/maintainer/download-test-data.sh +++ b/maintainer/download-test-data.sh @@ -44,7 +44,7 @@ files=( 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_7TEV_opt.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_7TEV_v2.tar' 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_7TEV_v2_xif_2.tar' - 'https://data.nnpdf.science/pineappl/test-data/nnlo.ak5_ptj.pineappl.lz4' + 'https://data.nnpdf.science/pineappl/test-data/nnlo.ak5_ptj-bin63.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/NJetEvents_0-0-2.tab.gz' 'https://data.nnpdf.science/pineappl/test-data/NNPDF_POS_F2D_40.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/NUTEV_CC_NU_FE_SIGMARED.pineappl.lz4' diff --git a/pineappl_cli/tests/export.rs b/pineappl_cli/tests/export.rs index f44256d1..a00068c3 100644 --- a/pineappl_cli/tests/export.rs +++ b/pineappl_cli/tests/export.rs @@ -133,77 +133,13 @@ fn export_applgrid() { } #[cfg(feature = "applgrid")] -const EXPORT_NNLO_AK5_PTJ_STR: &str = "b APPLgrid PineAPPL rel. diff ---+------------+------------+-------------- -0 7.1475199e8 7.1475253e8 7.6053554e-7 -1 2.5536645e8 2.5536667e8 8.6645764e-7 -2 8.0742409e7 8.0742409e7 1.2478907e-13 -3 4.0260433e7 4.0260445e7 2.8169024e-7 -4 1.9839883e7 1.9839887e7 2.1808465e-7 -5 1.0119027e7 1.0119028e7 1.3544486e-7 -6 5.5702376e6 5.5702379e6 5.8673922e-8 -7 3.1322190e6 3.1322207e6 5.3759875e-7 -8 1.9447742e6 1.9447743e6 4.2597541e-8 -9 1.1701564e6 1.1701565e6 7.4993014e-8 -10 7.2929160e5 7.2929198e5 5.1385162e-7 -11 4.7885350e5 4.7885359e5 1.8458927e-7 -12 3.0583779e5 3.0583781e5 7.0650601e-8 -13 2.0537268e5 2.0537272e5 1.9724071e-7 -14 1.3581347e5 1.3581349e5 1.4894264e-7 -15 9.3481731e4 9.3481745e4 1.5065258e-7 -16 6.1789306e4 6.1789317e4 1.7921741e-7 -17 4.2234799e4 4.2234800e4 3.7378332e-9 -18 2.9446997e4 2.9446998e4 2.7827810e-8 -19 2.0711158e4 2.0711158e4 3.6083760e-9 -20 1.4399288e4 1.4399288e4 -1.6959262e-9 -21 1.0204248e4 1.0204248e4 2.1560531e-13 -22 7.0541335e3 7.0541335e3 2.0916602e-13 -23 5.1274795e3 5.1274798e3 5.3440854e-8 -24 3.5785021e3 3.5785021e3 -7.3925543e-10 -25 2.5497806e3 2.5497806e3 2.0650148e-13 -26 1.8525134e3 1.8525134e3 -7.1513917e-10 -27 1.3124163e3 1.3124164e3 6.5466644e-8 -28 9.3353298e2 9.3353296e2 -2.4834690e-8 -29 6.8182565e2 6.8182566e2 1.9250868e-8 -30 4.8152109e2 4.8152109e2 2.0583535e-13 -31 3.4599110e2 3.4599110e2 2.0228264e-13 -32 2.4853801e2 2.4853801e2 1.9961810e-13 -33 1.8153858e2 1.8153858e2 1.9295676e-13 -34 1.2893001e2 1.2893001e2 1.8895996e-13 -35 9.2863897e1 9.2863897e1 -1.2712831e-11 -36 6.8219858e1 6.8219863e1 6.7323209e-8 -37 4.8499048e1 4.8499049e1 2.6932878e-9 -38 3.5260803e1 3.5260804e1 3.1629175e-8 -39 2.4875207e1 2.4875207e1 1.5327475e-9 -40 1.8248343e1 1.8248344e1 9.2649788e-8 -41 1.2848351e1 1.2848351e1 -3.4248875e-8 -42 9.3637433e0 9.3637433e0 1.0366541e-9 -43 6.5025357e0 6.5025357e0 2.9029490e-9 -44 4.7299678e0 4.7299683e0 1.1183387e-7 -45 3.3169392e0 3.3169393e0 1.6485298e-8 -46 2.3366424e0 2.3366426e0 7.2538949e-8 -47 1.6375345e0 1.6375347e0 1.3000311e-7 -48 1.1395783e0 1.1395784e0 8.0374843e-8 -49 7.7573740e-1 7.7573745e-1 6.3753925e-8 -50 5.3385646e-1 5.3385655e-1 1.7113346e-7 -51 3.5431304e-1 3.5431287e-1 -4.7859888e-7 -52 2.3984658e-1 2.3984660e-1 7.3858889e-8 -53 1.5742653e-1 1.5742660e-1 4.7452613e-7 -54 1.0381680e-1 1.0381681e-1 1.3036277e-7 -55 6.7367194e-2 6.7367194e-2 4.7924602e-9 -56 4.1754029e-2 4.1754049e-2 4.8244591e-7 -57 2.5167475e-2 2.5167484e-2 3.3871366e-7 -58 1.5430966e-2 1.5430971e-2 3.0639168e-7 -59 8.9218263e-3 8.9218286e-3 2.6013356e-7 -60 5.0265483e-3 5.0265486e-3 6.8485890e-8 -61 2.9066861e-3 2.9066853e-3 -2.9518261e-7 -62 1.4089133e-3 1.4089139e-3 4.5456056e-7 -63 6.7833493e-4 6.7833993e-4 7.3775731e-6 -64 1.0497365e-4 1.0497395e-4 2.8590177e-6 +const EXPORT_NNLO_AK5_PTJ_STR: &str = "b APPLgrid PineAPPL rel. diff +-+------------+------------+------------ +0 6.7833493e-4 6.7833993e-4 7.3775731e-6 "; #[cfg(feature = "applgrid")] -const EXPORT_NNLO_AK5_PTJ_NO_DISCARD_FAILS_STR: &str = "Error: factorization scale muf2 = 33634.5450347851 not found in APPLgrid; try exporting with `--discard-non-matching-values` +const EXPORT_NNLO_AK5_PTJ_NO_DISCARD_FAILS_STR: &str = "Error: factorization scale muf2 = 46548084.443279915 not found in APPLgrid; try exporting with `--discard-non-matching-values` "; #[test] @@ -217,7 +153,7 @@ fn export_nnlo_ak5_ptj_discard_non_matching_values() { "export", "--discard-non-matching-values", "--accuracy=1e-5", - "../test-data/nnlo.ak5_ptj.pineappl.lz4", + "../test-data/nnlo.ak5_ptj-bin63.pineappl.lz4", output.path().to_str().unwrap(), "NNPDF31_nlo_as_0118_luxqed", ]) @@ -237,7 +173,7 @@ fn export_nnlo_ak5_ptj_discard_non_matching_scales_alias() { "export", "--discard-non-matching-scales", "--accuracy=1e-5", - "../test-data/nnlo.ak5_ptj.pineappl.lz4", + "../test-data/nnlo.ak5_ptj-bin63.pineappl.lz4", output.path().to_str().unwrap(), "NNPDF31_nlo_as_0118_luxqed", ]) @@ -257,7 +193,7 @@ fn export_nnlo_ak5_ptj_discard_non_matching_momentum_alias() { "export", "--discard-non-matching-momentum", "--accuracy=1e-5", - "../test-data/nnlo.ak5_ptj.pineappl.lz4", + "../test-data/nnlo.ak5_ptj-bin63.pineappl.lz4", output.path().to_str().unwrap(), "NNPDF31_nlo_as_0118_luxqed", ]) @@ -275,7 +211,7 @@ fn export_nnlo_ak5_ptj_no_discard_fails() { .unwrap() .args([ "export", - "../test-data/nnlo.ak5_ptj.pineappl.lz4", + "../test-data/nnlo.ak5_ptj-bin63.pineappl.lz4", output.path().to_str().unwrap(), "NNPDF31_nlo_as_0118_luxqed", ]) From 38ffad440dba00576641ccf25238e959260ee41d Mon Sep 17 00:00:00 2001 From: Christopher Schwan Date: Sat, 9 May 2026 10:22:10 +0200 Subject: [PATCH 9/9] Add CHANGELOG entry for `--discard-non-matching-values` --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01a801e0..ed9bb60c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,11 @@ Starting with this version, PineAPPL has an official logo! - instead of depending on a previous version of the PineAPPL crate, a new crate `pineappl_v0` is now responsible for loading files with file version `v0` - raised dependency on `pyo3` to 0.27, which drops support for PyPy 3.9 and 3.10 +- renamed the switch `--discard-non-matching-scales` to + `--discard-non-matching-values`, which now also discards momentum fraction + values unavailable to APPLgrid. Both `--discard-non-matching-scales` and the + new `--discard-non-matching-momenta` are aliases to the new option name to + guarantee backwards compatibility of the CLI. ## [1.3.3] - 01/03/2026