Skip to content
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- added `Grid::repair` to repair bugs that survived by writing bugged grids to
disk, for example <https://github.com/NNPDF/pineappl/issues/338>. The CLI
offers this functionality via `pineappl write --repair` and it can also be
accessed via Python

### Fixed

- added a missing implementation for a branch in `Grid::merge` that was
Expand Down
53 changes: 53 additions & 0 deletions pineappl/src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,26 @@ impl Grid {
.for_each(|subgrid| subgrid.scale(factor));
}

/// Repair the grid if it was written by bugged versions to disk.
///
/// Returns `true` if this operations did anything. Currently, this scans for these problems:
/// - <https://github.com/NNPDF/pineappl/issues/338>
pub fn repair(&mut self) -> bool {
let mut repaired = false;

for subgrid in &mut self.subgrids {
// if the subgrid states it isn't empty and also doesn't return any elements it's
// broken; we need to fix that to avoid <https://github.com/NNPDF/pineappl/issues/338>
if !subgrid.is_empty() && subgrid.indexed_iter().count() == 0 {
*subgrid = EmptySubgridV1.into();

repaired = true;
}
}

repaired
}

/// Scales each subgrid by a factor which is the product of the given values `alphas`, `alpha`,
/// `logxir`, and `logxif`, each raised to the corresponding powers for each subgrid. In
/// addition, every subgrid is scaled by a factor `global` independently of its order.
Expand Down Expand Up @@ -1591,6 +1611,39 @@ mod tests {
assert_eq!(grid.orders().len(), 1);
}

#[test]
fn grid_repair() {
use super::super::packed_array::PackedArray;
// create emtpy grid
let mut grid = Grid::new(
BinsWithFillLimits::from_fill_limits([0.0, 0.25, 0.5, 0.75, 1.0].to_vec()).unwrap(),
vec![Order::new(0, 2, 0, 0, 0)],
vec![channel![1.0 * (2, 2) + 1.0 * (4, 4)]],
PidBasis::Pdg,
vec![Conv::new(ConvType::UnpolPDF, 2212); 2],
v0::default_interps(false, 2),
vec![Kinematics::Scale(0), Kinematics::X(0), Kinematics::X(1)],
Scales {
ren: ScaleFuncForm::Scale(0),
fac: ScaleFuncForm::Scale(0),
frg: ScaleFuncForm::NoScale,
},
);
let was_repaired = grid.repair();
assert!(!was_repaired);
// insert nothing
let x = vec![
0.015625, 0.03125, 0.0625, 0.125, 0.1875, 0.25, 0.375, 0.5, 0.75, 1.0,
];
let mut ar = PackedArray::new(vec![1, 10, 10]);
ar[[0, 0, 0]] = 0.;
let sg: SubgridEnum =
ImportSubgridV1::new(ar, vec![vec![0.0], x.clone(), x.clone()]).into();
grid.subgrids_mut()[[0, 0, 0]] = sg;
let was_repaired = grid.repair();
assert!(was_repaired);
}

#[test]
fn grid_merge_orders() {
let mut grid = Grid::new(
Expand Down
19 changes: 18 additions & 1 deletion pineappl_cli/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ enum OpsArg {
MulBinNorm(f64),
Optimize(bool),
OptimizeFkTable(FkAssumptions),
Repair(bool),
RewriteChannel((usize, Channel)),
RewriteOrder((usize, Order)),
RotatePidBasis(PidBasis),
Expand Down Expand Up @@ -85,7 +86,7 @@ impl FromArgMatches for MoreArgs {
});
}
}
"merge_channel_factors" | "optimize" | "split_channels" | "upgrade" => {
"merge_channel_factors" | "optimize" | "repair" | "split_channels" | "upgrade" => {
let arguments: Vec<Vec<_>> = matches
.remove_occurrences(&id)
.unwrap()
Expand All @@ -98,6 +99,7 @@ impl FromArgMatches for MoreArgs {
args[index] = Some(match id.as_str() {
"merge_channel_factors" => OpsArg::MergeChannelFactors(arg[0]),
"optimize" => OpsArg::Optimize(arg[0]),
"repair" => OpsArg::Repair(arg[0]),
"split_channels" => OpsArg::SplitChannels(arg[0]),
"upgrade" => OpsArg::Upgrade(arg[0]),
_ => unreachable!(),
Expand Down Expand Up @@ -394,6 +396,17 @@ impl Args for MoreArgs {
.try_map(|s| s.parse::<FkAssumptions>()),
),
)
.arg(
Arg::new("repair")
.action(ArgAction::Append)
.default_missing_value("true")
.help("Repair bugs saved in the grid")
.long("repair")
.num_args(0..=1)
.require_equals(true)
.value_name("ENABLE")
.value_parser(clap::value_parser!(bool)),
)
.arg(
Arg::new("rewrite_channel")
.action(ArgAction::Append)
Expand Down Expand Up @@ -583,6 +596,9 @@ impl Subcommand for Opts {
// UNWRAP: this cannot fail because we only modify the normalizations
.unwrap();
}
OpsArg::Repair(true) => {
grid.repair();
}
OpsArg::RewriteChannel((index, new_channel)) => {
// TODO: check that `index` is valid
grid.channels_mut()[*index] = new_channel.clone();
Expand Down Expand Up @@ -618,6 +634,7 @@ impl Subcommand for Opts {
OpsArg::SplitChannels(true) => grid.split_channels(),
OpsArg::Upgrade(true) => grid.upgrade(),
OpsArg::MergeChannelFactors(false)
| OpsArg::Repair(false)
| OpsArg::Optimize(false)
| OpsArg::SplitChannels(false)
| OpsArg::Upgrade(false) => {}
Expand Down
18 changes: 18 additions & 0 deletions pineappl_cli/tests/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Options:
--mul-bin-norm <NORM> Multiply all bin normalizations with the given factor
--optimize[=<ENABLE>] Optimize internal data structure to minimize memory and disk usage [possible values: true, false]
--optimize-fk-table <OPTIMI> Optimize internal data structure of an FkTable to minimize memory and disk usage [possible values: Nf6Ind, Nf6Sym, Nf5Ind, Nf5Sym, Nf4Ind, Nf4Sym, Nf3Ind, Nf3Sym]
--repair[=<ENABLE>] Repair bugs saved in the grid [possible values: true, false]
--rewrite-channel <IDX> <CHAN> Rewrite the definition of the channel with index IDX
--rewrite-order <IDX> <ORDER> Rewrite the definition of the order with index IDX
--rotate-pid-basis <BASIS> Rotate the PID basis for this grid [possible values: PDG, EVOL]
Expand Down Expand Up @@ -876,6 +877,23 @@ fn multiple_arguments() {
.stdout(MULTIPLE_ARGUMENTS_STR);
}

#[test]
fn repair() {
let output = NamedTempFile::new("repaired-grid.pineappl.lz4").unwrap();

Command::cargo_bin("pineappl")
.unwrap()
.args([
"write",
"--repair",
"../test-data/LHCB_WP_7TEV_opt.pineappl.lz4",
output.path().to_str().unwrap(),
])
.assert()
.success()
.stdout("");
}

#[test]
fn rewrite_channels() {
let output = NamedTempFile::new("ckm_channels.pineappl.lz4").unwrap();
Expand Down
5 changes: 5 additions & 0 deletions pineappl_py/src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,11 @@ impl PyGrid {
pub fn split_channels(&mut self) {
self.grid.split_channels();
}

/// Repair the grid if it was written by bugged versions to disk.
pub fn repair(&mut self) -> bool {
self.grid.repair()
}
}

/// Register submodule in parent.
Expand Down
1 change: 1 addition & 0 deletions pineappl_py/tests/test_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def test_set_subgrid(self, fake_grids):
node_values=[np.array([90.0]), xs],
)
g.set_subgrid(0, 0, 0, subgrid.into())
assert not g.repair()

xs = np.linspace(0.1, 1, 2)
Q2s = np.linspace(10, 20, 2)
Expand Down