From d99339acae1998e5337c56f8b1656395a8d9631e Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 12 Mar 2026 19:28:21 +0100 Subject: [PATCH 1/8] Fix various issues with the Fortran API --- examples/fortran/dyaa.f90 | 2 +- examples/fortran/pineappl.f90 | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/fortran/dyaa.f90 b/examples/fortran/dyaa.f90 index 2368cfba..e10bcaba 100644 --- a/examples/fortran/dyaa.f90 +++ b/examples/fortran/dyaa.f90 @@ -109,7 +109,7 @@ program dyaa ! above in 'channels') ! - for PDF parameters 'x1, x2, q2', ! - with the given 'weight' - call pineappl_grid_fill2(grid, order_idx, abs(yll), channel_idx, [ x1, x2, q2 ], weight) + call pineappl_grid_fill2(grid, order_idx, abs(yll), channel_idx, [ q2, x1, x2 ], weight) end do ! set metadata - this isn't strictly needed, but usually useful (plot script, ...) diff --git a/examples/fortran/pineappl.f90 b/examples/fortran/pineappl.f90 index 00ce1501..c708e524 100644 --- a/examples/fortran/pineappl.f90 +++ b/examples/fortran/pineappl.f90 @@ -173,7 +173,7 @@ subroutine channels_add(channels, combinations, pdg_id_combinations, factors) & type (c_ptr) function channels_new(convolutions) bind(c, name = 'pineappl_channels_new') use iso_c_binding - integer (c_int32_t), value :: convolutions + integer (c_size_t), value :: convolutions end function integer (c_size_t) function grid_bin_count(grid) bind(c, name = 'pineappl_grid_bin_count') @@ -434,9 +434,9 @@ function c_f_string(c_str) result(f_str) type (pineappl_channels) function pineappl_channels_new(convolutions) implicit none - integer (c_int32_t), value :: convolutions + integer, intent(in) :: convolutions - pineappl_channels_new = pineappl_channels(channels_new(convolutions)) + pineappl_channels_new = pineappl_channels(channels_new(int(convolutions, c_size_t))) end function integer function pineappl_grid_bin_count(grid) @@ -538,7 +538,7 @@ function pineappl_grid_convolve(grid, xfx, alphas, pdfs_state, alphas_state, ord alphas_state, & [(logical(order_mask(i), c_bool), i = 1, size(order_mask))], & [(logical(channel_mask(i), c_bool), i = 1, size(channel_mask))], & - [(int(bin_indices, c_size_t), i = 1, size(bin_indices))], & + [(int(bin_indices(i), c_size_t), i = 1, size(bin_indices))], & int(nb_scales, c_size_t), & mu_scales, & res & @@ -788,10 +788,10 @@ subroutine pineappl_channels_add(channels, combinations, pdg_id_combinations, fa implicit none - type (pineappl_channels), intent(in) :: channels - integer, intent(in) :: combinations - integer, dimension(2 * combinations), intent(in) :: pdg_id_combinations - real (dp), dimension(combinations), intent(in) :: factors + type (pineappl_channels), intent(in) :: channels + integer, intent(in) :: combinations + integer, dimension(*), intent(in) :: pdg_id_combinations + real (dp), dimension(combinations), intent(in) :: factors call channels_add(channels%ptr, int(combinations, c_size_t), pdg_id_combinations, factors) end subroutine From a0d112873434268a65157a96481c09508261ab9b Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 12 Mar 2026 21:45:42 +0100 Subject: [PATCH 2/8] Added `pineappl_grid_set_metadata` and `pineappl_grid_metadata` methods --- pineappl_capi/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pineappl_capi/src/lib.rs b/pineappl_capi/src/lib.rs index 97ea2e50..0ad79285 100644 --- a/pineappl_capi/src/lib.rs +++ b/pineappl_capi/src/lib.rs @@ -1860,6 +1860,54 @@ pub unsafe extern "C" fn pineappl_channels_entry( .for_each(|(from, to)| *to = *from); } +/// Return the value for `key` stored in `grid`. If `key` isn't found, `NULL` will be returned. +/// After usage the string must be deallocated using [`pineappl_string_delete`]. +/// +/// # Safety +/// +/// If `grid` does not point to a valid `Grid` object, for example when `grid` is the `NULL` +/// pointer, this function is not safe to call. The parameter `key` must be non-`NULL` and a valid +/// C string. +#[no_mangle] +#[must_use] +pub unsafe extern "C" fn pineappl_grid_metadata( + grid: *const Grid, + key: *const c_char, +) -> *mut c_char { + let grid = unsafe { &*grid }; + let key = unsafe { CStr::from_ptr(key) }.to_string_lossy(); + + if let Some(value) = grid.metadata().get(key.as_ref()) { + CString::new(value.as_str()).unwrap().into_raw() + } else { + std::ptr::null_mut() + } +} + +/// Sets an internal key-value pair for the grid. +/// +/// # Safety +/// +/// If `grid` does not point to a valid `Grid` object, for example when `grid` is the null pointer, +/// this function is not safe to call. The parameters `key` and `value` must be non-`NULL` and +/// valid C strings. +#[no_mangle] +pub unsafe extern "C" fn pineappl_grid_set_metadata( + grid: *mut Grid, + key: *const c_char, + value: *const c_char, +) { + let grid = unsafe { &mut *grid }; + let key = unsafe { CStr::from_ptr(key) } + .to_string_lossy() + .into_owned(); + let value = unsafe { CStr::from_ptr(value) } + .to_string_lossy() + .into_owned(); + + grid.metadata_mut().insert(key, value); +} + /// An extension of `pineappl_grid_order_params` that accounts for the order of the fragmentation /// logs. /// From a7b1e4a77283b52c9b5b22a74ce58540b91c40d3 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 12 Mar 2026 21:46:27 +0100 Subject: [PATCH 3/8] Propagate new metadata manipulation into the Fortran API --- examples/fortran/dyaa.f90 | 14 +++++++------- examples/fortran/pineappl.f90 | 36 +++++++++++++++++++++++++++++++++++ examples/fortran/test.f90 | 7 +++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/examples/fortran/dyaa.f90 b/examples/fortran/dyaa.f90 index e10bcaba..48f054d9 100644 --- a/examples/fortran/dyaa.f90 +++ b/examples/fortran/dyaa.f90 @@ -113,14 +113,14 @@ program dyaa end do ! set metadata - this isn't strictly needed, but usually useful (plot script, ...) - call pineappl_grid_set_key_value(grid, 'arxiv', '1310.7291') - call pineappl_grid_set_key_value(grid, 'description', 'CMS double-differential Drell—Yan cross section at 7 TeV') - call pineappl_grid_set_key_value(grid, 'hepdata', '10.17182/hepdata.62207.v1/t8') - call pineappl_grid_set_key_value(grid, 'x1_label', 'yll') - call pineappl_grid_set_key_value(grid, 'x1_label_tex', '$y_{\ell\bar{\ell}}$') + call pineappl_grid_set_metadata(grid, 'arxiv', '1310.7291') + call pineappl_grid_set_metadata(grid, 'description', 'CMS double-differential Drell—Yan cross section at 7 TeV') + call pineappl_grid_set_metadata(grid, 'hepdata', '10.17182/hepdata.62207.v1/t8') + call pineappl_grid_set_metadata(grid, 'x1_label', 'yll') + call pineappl_grid_set_metadata(grid, 'x1_label_tex', '$y_{\ell\bar{\ell}}$') ! rapidity doesn't have a unit (other observables could be GeV, TeV, ...) - call pineappl_grid_set_key_value(grid, 'x1_unit', '') - call pineappl_grid_set_key_value(grid, 'y_unit', 'pb') + call pineappl_grid_set_metadata(grid, 'x1_unit', '') + call pineappl_grid_set_metadata(grid, 'y_unit', 'pb') ! optimize the grid representation (makes the file smaller) call pineappl_grid_optimize(grid) diff --git a/examples/fortran/pineappl.f90 b/examples/fortran/pineappl.f90 index c708e524..df5d702f 100644 --- a/examples/fortran/pineappl.f90 +++ b/examples/fortran/pineappl.f90 @@ -271,6 +271,13 @@ function grid_key_value(grid, key) bind(c, name = 'pineappl_grid_key_value') type (c_ptr) :: grid_key_value end function + function grid_metadata(grid, key) bind(c, name = 'pineappl_grid_metadata') + use iso_c_binding + type (c_ptr), value :: grid + character (c_char) :: key(*) + type (c_ptr) :: grid_metadata + end function + function grid_channels(grid) bind(c, name = 'pineappl_grid_channels') use iso_c_binding type (c_ptr), value :: grid @@ -358,6 +365,12 @@ subroutine grid_set_key_value(grid, key, valju) bind(c, name = 'pineappl_grid_se character (c_char) :: key(*), valju(*) end subroutine + subroutine grid_set_metadata(grid, key, valju) bind(c, name = 'pineappl_grid_set_metadata') + use iso_c_binding + type (c_ptr), value :: grid + character (c_char) :: key(*), valju(*) + end subroutine + subroutine grid_set_remapper(grid, dimensions, normalizations, limits) bind(c, name = 'pineappl_grid_set_remapper') use iso_c_binding type (c_ptr), value :: grid @@ -605,6 +618,18 @@ function pineappl_grid_key_value(grid, key) result(res) res = c_f_string(grid_key_value(grid%ptr, key // c_null_char)) end function + function pineappl_grid_metadata(grid, key) result(res) + use iso_c_binding + + implicit none + + type (pineappl_grid), intent(in) :: grid + character (*), intent(in) :: key + character (:), allocatable :: res + + res = c_f_string(grid_metadata(grid%ptr, key // c_null_char)) + end function + type (pineappl_channels) function pineappl_grid_channels(grid) implicit none @@ -752,6 +777,17 @@ subroutine pineappl_grid_set_key_value(grid, key, valju) call grid_set_key_value(grid%ptr, key // c_null_char, valju // c_null_char) end subroutine + subroutine pineappl_grid_set_metadata(grid, key, valju) + use iso_c_binding + + implicit none + + type (pineappl_grid), intent(in) :: grid + character (*), intent(in) :: key, valju + + call grid_set_metadata(grid%ptr, key // c_null_char, valju // c_null_char) + end subroutine + subroutine pineappl_grid_set_remapper(grid, dimensions, normalizations, limits) use iso_c_binding diff --git a/examples/fortran/test.f90 b/examples/fortran/test.f90 index 6a8299d2..247b4bd3 100644 --- a/examples/fortran/test.f90 +++ b/examples/fortran/test.f90 @@ -247,6 +247,13 @@ program test_pineappl call pineappl_grid_set_key_value(grid, "set_key_value", "set_key_value: success") + call pineappl_grid_set_metadata(grid, "set_metadata", "set_metadata: success") + + if (pineappl_grid_metadata(grid, "set_metadata") /= "set_metadata: success") then + write(*, *) "pineappl_grid_metadata(): '", pineappl_grid_metadata(grid, "set_metadata"), "'" + error stop "error: pineappl_grid_metadata" + end if + ! NOTE: At this point we have the bins: [0, 1, 2, 3] call pineappl_grid_set_remapper(grid, 2, [1.0_dp, 1.0_dp, 1.0_dp], & [0.0_dp, 1.0_dp, 10.0_dp, 11.0_dp, 1.0_dp, 3.0_dp, 11.0_dp, 13.0_dp, 15.0_dp, 20.0_dp]) From 06b1e67b9678f51db7ba5a8e35ffdde6bfc210c8 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Thu, 12 Mar 2026 21:47:03 +0100 Subject: [PATCH 4/8] Propagate new metadata manipulation into the OOP C++ API --- examples/cpp/set-subgrids.cpp | 10 ++++++++++ examples/object-oriented-cpp/PineAPPL.hpp | 10 +++++----- examples/object-oriented-cpp/dyaa.cpp | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/examples/cpp/set-subgrids.cpp b/examples/cpp/set-subgrids.cpp index e629a9b5..66ca923a 100644 --- a/examples/cpp/set-subgrids.cpp +++ b/examples/cpp/set-subgrids.cpp @@ -207,6 +207,16 @@ int main() { ); pineappl_grid_optimize(grid); + // --- + // Metadata testing + pineappl_grid_set_metadata(grid, "comment", "This is a toy SIDIS grid"); + pineappl_grid_set_metadata(grid, "experiment", "ToyExp"); + + char* comment = pineappl_grid_metadata(grid, "comment"); + char* experiment = pineappl_grid_metadata(grid, "experiment"); + pineappl_string_delete(comment); + pineappl_string_delete(experiment); + // --- // Write the grid to disk - the filename can be anything ... std::string filename = "sidis-toygrid.pineappl.lz4"; diff --git a/examples/object-oriented-cpp/PineAPPL.hpp b/examples/object-oriented-cpp/PineAPPL.hpp index b517b28c..e3d5875a 100644 --- a/examples/object-oriented-cpp/PineAPPL.hpp +++ b/examples/object-oriented-cpp/PineAPPL.hpp @@ -229,8 +229,8 @@ struct Grid { * @param key key * @param value value */ - void set_key_value(const std::string &key, const std::string &value) const { - pineappl_grid_set_key_value(this->raw, key.c_str(), value.c_str()); + void set_metadata(const std::string &key, const std::string &value) const { + pineappl_grid_set_metadata(this->raw, key.c_str(), value.c_str()); } /** @@ -238,9 +238,9 @@ struct Grid { * @param key key * @return value */ - std::string get_key_value(const std::string &key) const { - auto *value = pineappl_grid_key_value(this->raw, key.c_str()); - std::string res(value); + std::string get_metadata(const std::string &key) const { + auto *value = pineappl_grid_metadata(this->raw, key.c_str()); + std::string res(value != nullptr ? value : ""); // delete the allocated object pineappl_string_delete(value); return res; diff --git a/examples/object-oriented-cpp/dyaa.cpp b/examples/object-oriented-cpp/dyaa.cpp index bdffc142..05435c4b 100644 --- a/examples/object-oriented-cpp/dyaa.cpp +++ b/examples/object-oriented-cpp/dyaa.cpp @@ -199,10 +199,10 @@ int main() { } // store some metadata in the grid - grid.set_key_value("events", "10000000"); + grid.set_metadata("events", "10000000"); // read out the stored value and print it on stdout - const auto value = grid.get_key_value("events"); + const auto value = grid.get_metadata("events"); std::printf("Finished running %s events.\n", value.c_str()); // write the grid to disk - with `.lz4` suffix the grid is automatically LZ4 From c336a465c95d1db76880b41d88e66d26b5d54f50 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Tue, 17 Mar 2026 15:05:39 +0100 Subject: [PATCH 5/8] Increase Newton's max iteration to account for variations --- pineappl/src/interpolation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pineappl/src/interpolation.rs b/pineappl/src/interpolation.rs index 9266f5f2..d9bbaff7 100644 --- a/pineappl/src/interpolation.rs +++ b/pineappl/src/interpolation.rs @@ -20,7 +20,7 @@ mod applgrid { let mut yp = y; let mut deltap = f64::INFINITY; - for _ in 0..10 { + for _ in 0..15 { let x = (-yp).exp(); let delta = (1.0 - x).mul_add(-5.0, y - yp); if (delta == 0.0) || ((delta.abs() < 2e-15) && (delta.abs() >= deltap.abs())) { From 0573b5b9df3379605cd2dd975f8ac7c8014e641a Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Tue, 17 Mar 2026 21:07:36 +0100 Subject: [PATCH 6/8] Add a test that checks the Newton's convergence --- .github/actions/cache-test-data/action.yml | 2 +- maintainer/download-test-data.sh | 1 + pineappl_cli/tests/write.rs | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/actions/cache-test-data/action.yml b/.github/actions/cache-test-data/action.yml index 8f69eb25..f0629ee6 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-v23 + key: test-data-v24 - 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 30206fe4..926abfed 100755 --- a/maintainer/download-test-data.sh +++ b/maintainer/download-test-data.sh @@ -52,6 +52,7 @@ files=( 'https://data.nnpdf.science/pineappl/test-data/ZEUS_2JET_319GEV_374PB-1_DIF_ETQ2_BIN6.tar' 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_8TEV.pineappl.lz4' 'https://data.nnpdf.science/pineappl/test-data/LHCB_WP_8TEV.tar' + 'https://data.nnpdf.science/pineappl/test-data/test_newton_convergence.pineappl.lz4' 'https://ploughshare.web.cern.ch/ploughshare/db/applfast/applfast-atlas-dijets-fnlo-arxiv-1312.3524/grids/applfast-atlas-dijets-fnlo-arxiv-1312.3524-xsec000.tab.gz' 'https://ploughshare.web.cern.ch/ploughshare/db/applfast/applfast-h1-dijets-appl-arxiv-0010054/grids/applfast-h1-dijets-appl-arxiv-0010054-xsec000.appl' 'https://ploughshare.web.cern.ch/ploughshare/db/applfast/applfast-h1-incjets-fnlo-arxiv-0706.3722/grids/applfast-h1-incjets-fnlo-arxiv-0706.3722-xsec000.tab.gz' diff --git a/pineappl_cli/tests/write.rs b/pineappl_cli/tests/write.rs index 9fa725bc..3da52717 100644 --- a/pineappl_cli/tests/write.rs +++ b/pineappl_cli/tests/write.rs @@ -682,6 +682,23 @@ fn optimize() { .stdout(""); } +#[test] +fn optimize_newton_convergence() { + let output = NamedTempFile::new("optimized.pineappl.lz4").unwrap(); + + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "write", + "--optimize", + "../test-data/test_newton_convergence.pineappl.lz4", + output.path().to_str().unwrap(), + ]) + .assert() + .success() + .stdout(""); +} + #[test] fn set_bins() { let output = NamedTempFile::new("set_bins.pineappl.lz4").unwrap(); From 5b2a96794fb4cc5cd0b54bc6d8f291be5b28d94c Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Tue, 17 Mar 2026 21:17:32 +0100 Subject: [PATCH 7/8] Test Newton's convergence with a Unit test targeting #372 --- pineappl/src/interpolation.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pineappl/src/interpolation.rs b/pineappl/src/interpolation.rs index d9bbaff7..aed1c761 100644 --- a/pineappl/src/interpolation.rs +++ b/pineappl/src/interpolation.rs @@ -804,4 +804,14 @@ mod tests { ulps = 4 ); } + + #[test] + fn issue_372() { + assert_approx_eq!( + f64, + applgrid::fx2(3.751520963950722), + 0.4221667753589648, + ulps = 4 + ); + } } From 898c828f28f3434c2052bc15e390623f102b46d5 Mon Sep 17 00:00:00 2001 From: Radonirinaunimi Date: Wed, 18 Mar 2026 21:47:14 +0100 Subject: [PATCH 8/8] Update changelogs --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 425b7d54..963f0808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- added an actual implementation of `pineappl_grid_metadata` and + `pineappl_grid_set_metadata` in the APIs + +### Fixed + +- fixed a bug in the Newton's convergence method by raising the maximum number + of iteration +- fixed a bug in the implementation of `pineappl_channels_add` of the Fortran + API + ### Changed - raised MSRV to 1.94.0