diff --git a/CHANGELOG.md b/CHANGELOG.md index 92d282ac..7bfe0b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 . 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 diff --git a/pineappl/src/grid.rs b/pineappl/src/grid.rs index 82244a6c..4980b194 100644 --- a/pineappl/src/grid.rs +++ b/pineappl/src/grid.rs @@ -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: + /// - + 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 + 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. @@ -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( diff --git a/pineappl_cli/src/write.rs b/pineappl_cli/src/write.rs index 89303e1e..57dfdea5 100644 --- a/pineappl_cli/src/write.rs +++ b/pineappl_cli/src/write.rs @@ -41,6 +41,7 @@ enum OpsArg { MulBinNorm(f64), Optimize(bool), OptimizeFkTable(FkAssumptions), + Repair(bool), RewriteChannel((usize, Channel)), RewriteOrder((usize, Order)), RotatePidBasis(PidBasis), @@ -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> = matches .remove_occurrences(&id) .unwrap() @@ -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!(), @@ -394,6 +396,17 @@ impl Args for MoreArgs { .try_map(|s| s.parse::()), ), ) + .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) @@ -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(); @@ -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) => {} diff --git a/pineappl_cli/tests/write.rs b/pineappl_cli/tests/write.rs index 44ac9c2e..f15e5689 100644 --- a/pineappl_cli/tests/write.rs +++ b/pineappl_cli/tests/write.rs @@ -24,6 +24,7 @@ Options: --mul-bin-norm Multiply all bin normalizations with the given factor --optimize[=] Optimize internal data structure to minimize memory and disk usage [possible values: true, false] --optimize-fk-table Optimize internal data structure of an FkTable to minimize memory and disk usage [possible values: Nf6Ind, Nf6Sym, Nf5Ind, Nf5Sym, Nf4Ind, Nf4Sym, Nf3Ind, Nf3Sym] + --repair[=] Repair bugs saved in the grid [possible values: true, false] --rewrite-channel Rewrite the definition of the channel with index IDX --rewrite-order Rewrite the definition of the order with index IDX --rotate-pid-basis Rotate the PID basis for this grid [possible values: PDG, EVOL] @@ -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(); diff --git a/pineappl_py/src/grid.rs b/pineappl_py/src/grid.rs index 72a59e86..8135df41 100644 --- a/pineappl_py/src/grid.rs +++ b/pineappl_py/src/grid.rs @@ -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. diff --git a/pineappl_py/tests/test_grid.py b/pineappl_py/tests/test_grid.py index 376431c2..02deb561 100644 --- a/pineappl_py/tests/test_grid.py +++ b/pineappl_py/tests/test_grid.py @@ -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)