diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9de54dd7..f416ed0c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,7 +25,7 @@ jobs: uses: actions/cache@v4 with: path: test-data - key: test-data-v11 + key: test-data-v12 - name: Download test data if: steps.cache-test-data.outputs.cache-hit != 'true' run: | @@ -55,6 +55,9 @@ jobs: curl -s -C - -O 'https://ploughshare.web.cern.ch/ploughshare/db/applfast/applfast-h1-dijets-appl-arxiv-0010054/grids/applfast-h1-dijets-appl-arxiv-0010054-xsec000.appl' curl -s -C - -O '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' curl -s -C - -O 'https://ploughshare.web.cern.ch/ploughshare/db/atlas/atlas-atlas-wpm-arxiv-1109.5141/grids/atlas-atlas-wpm-arxiv-1109.5141-xsec001.appl' + curl -s -C - -O 'https://data.nnpdf.science/pineappl/test-data/STAR_WMWP_510GEV_WM-AL-POL.pineappl.lz4' + curl -s -C - -O 'https://data.nnpdf.science/pineappl/test-data/STAR_WMWP_510GEV_WM-AL-POL_PolPDF.tar' + curl -s -C - -O 'https://data.nnpdf.science/pineappl/test-data/STAR_WMWP_510GEV_WM-AL-POL_UnpolPDF.tar' - name: Set RUSTDOCFLAGS run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 34c7909d..7f8c753a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- added new method `Grid::evolve_with_slice_iter2` which is able to perform + evolutions with two different EKOs + ### Fixed - fixed CI to build CAPI and CLI diff --git a/maintainer/generate-coverage.sh b/maintainer/generate-coverage.sh index 6f1b2ac4..bba5931d 100755 --- a/maintainer/generate-coverage.sh +++ b/maintainer/generate-coverage.sh @@ -27,6 +27,9 @@ wget --no-verbose --no-clobber -P test-data 'https://ploughshare.web.cern.ch/plo wget --no-verbose --no-clobber -P test-data 'https://ploughshare.web.cern.ch/ploughshare/db/applfast/applfast-h1-dijets-appl-arxiv-0010054/grids/applfast-h1-dijets-appl-arxiv-0010054-xsec000.appl' wget --no-verbose --no-clobber -P test-data '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' wget --no-verbose --no-clobber -P test-data 'https://ploughshare.web.cern.ch/ploughshare/db/atlas/atlas-atlas-wpm-arxiv-1109.5141/grids/atlas-atlas-wpm-arxiv-1109.5141-xsec001.appl' +wget --no-verbose --no-clobber -P test-data 'https://data.nnpdf.science/pineappl/test-data/STAR_WMWP_510GEV_WM-AL-POL.pineappl.lz4' +wget --no-verbose --no-clobber -P test-data 'https://data.nnpdf.science/pineappl/test-data/STAR_WMWP_510GEV_WM-AL-POL_PolPDF.tar' +wget --no-verbose --no-clobber -P test-data 'https://data.nnpdf.science/pineappl/test-data/STAR_WMWP_510GEV_WM-AL-POL_UnpolPDF.tar' # we compile with different flags and don't want to destroy the other target directory export CARGO_TARGET_DIR="$(mktemp -d)" diff --git a/maintainer/pineappl-ci/script.sh b/maintainer/pineappl-ci/script.sh index 633d9802..6a1d41ec 100755 --- a/maintainer/pineappl-ci/script.sh +++ b/maintainer/pineappl-ci/script.sh @@ -42,7 +42,7 @@ ldconfig cd .. # install PDF sets -for pdf in NNPDF31_nlo_as_0118_luxqed NNPDF40_nnlo_as_01180 NNPDF40_nlo_as_01180; do +for pdf in NNPDF31_nlo_as_0118_luxqed NNPDF40_nnlo_as_01180 NNPDF40_nlo_as_01180 NNPDF40_nlo_pch_as_01180; do curl "https://lhapdfsets.web.cern.ch/current/${pdf}.tar.gz" | tar xzf - -C /usr/local/share/LHAPDF done diff --git a/pineappl/src/evolution.rs b/pineappl/src/evolution.rs index cb789690..5829ead6 100644 --- a/pineappl/src/evolution.rs +++ b/pineappl/src/evolution.rs @@ -269,7 +269,7 @@ fn operator_slices( type X1aX1bOp2Tuple = (Vec>, Array2); fn ndarray_from_subgrid_orders_slice( - info: &OperatorSliceInfo, + fac1: f64, subgrids: &ArrayView1, orders: &[Order], order_mask: &[bool], @@ -353,7 +353,7 @@ fn ndarray_from_subgrid_orders_slice( for ((ifac1, ix1, ix2), value) in subgrid.indexed_iter() { let Mu2 { ren, fac } = subgrid.mu2_grid()[ifac1]; - if !approx_eq!(f64, xif * xif * fac, info.fac1, ulps = EVOLUTION_TOL_ULPS) { + if !approx_eq!(f64, xif * xif * fac, fac1, ulps = EVOLUTION_TOL_ULPS) { continue; } @@ -413,7 +413,7 @@ pub(crate) fn evolve_slice_with_one( for (subgrids_o, channel1) in subgrids_ol.axis_iter(Axis(1)).zip(grid.channels()) { let (mut x1, array) = ndarray_from_subgrid_orders_slice( - info, + info.fac1, &subgrids_o, grid.orders(), order_mask, @@ -556,7 +556,7 @@ pub(crate) fn evolve_slice_with_two( for (subgrids_o, channel1) in subgrids_oc.axis_iter(Axis(1)).zip(grid.channels()) { let (x1, array) = ndarray_from_subgrid_orders_slice( - info, + info.fac1, &subgrids_o, grid.orders(), order_mask, @@ -633,3 +633,151 @@ pub(crate) fn evolve_slice_with_two( .collect(), )) } + +pub(crate) fn evolve_slice_with_two2( + grid: &Grid, + operators: &[ArrayView4], + infos: &[OperatorSliceInfo], + order_mask: &[bool], + xi: (f64, f64), + alphas_table: &AlphasTable, +) -> Result<(Array3, Vec), GridError> { + let gluon_has_pid_zero = gluon_has_pid_zero(grid); + + // TODO: implement matching of different scales for different EKOs + let mut fac1_scales: Vec<_> = infos.iter().map(|info| info.fac1).collect(); + fac1_scales.sort_by(f64::total_cmp); + assert!(fac1_scales.windows(2).all(|scales| approx_eq!( + f64, + scales[0], + scales[1], + ulps = EVOLUTION_TOL_ULPS + ))); + let fac1 = fac1_scales[0]; + + // TODO: generalize by iterating up to `n` + let (pid_indices, pids01): (Vec<_>, Vec<_>) = izip!(0..2, operators, infos) + .map(|(d, operator, info)| { + pid_slices(operator, info, gluon_has_pid_zero, &|pid1| { + grid.channels() + .iter() + .flat_map(Channel::entry) + .any(|tuple| match d { + // TODO: `Channel::entry` should return a tuple of a `Vec` and an `f64` + 0 => tuple.0 == pid1, + 1 => tuple.1 == pid1, + _ => unreachable!(), + }) + }) + }) + .collect::, _>>()? + .into_iter() + .unzip(); + + let mut channels0: Vec<_> = pids01 + .iter() + .map(|pids| pids.iter().map(|&(pid0, _)| pid0)) + .multi_cartesian_product() + .collect(); + channels0.sort_unstable(); + channels0.dedup(); + let channels0 = channels0; + + let mut sub_fk_tables = Vec::with_capacity(grid.bin_info().bins() * channels0.len()); + + // TODO: generalize to `n` + let mut last_x1 = vec![Vec::new(); 2]; + let mut eko_slices = vec![Vec::new(); 2]; + + for subgrids_oc in grid.subgrids().axis_iter(Axis(1)) { + assert_eq!(infos[0].x0.len(), infos[1].x0.len()); + + let mut tables = + vec![Array2::zeros((infos[0].x0.len(), infos[1].x0.len())); channels0.len()]; + + for (subgrids_o, channel1) in subgrids_oc.axis_iter(Axis(1)).zip(grid.channels()) { + let (x1, array) = ndarray_from_subgrid_orders_slice( + fac1, + &subgrids_o, + grid.orders(), + order_mask, + xi, + alphas_table, + )?; + + for (last_x1, x1, pid_indices, slices, operator, info) in izip!( + &mut last_x1, + x1, + &pid_indices, + &mut eko_slices, + operators, + infos + ) { + if (last_x1.len() != x1.len()) + || last_x1 + .iter() + .zip(x1.iter()) + .any(|(&lhs, &rhs)| !approx_eq!(f64, lhs, rhs, ulps = EVOLUTION_TOL_ULPS)) + { + *slices = operator_slices(operator, info, pid_indices, &x1)?; + *last_x1 = x1; + } + } + + let mut tmp = Array2::zeros((last_x1[0].len(), infos[1].x0.len())); + + for (pids1, factor) in channel1 + .entry() + .iter() + .map(|&(pida1, pidb1, factor)| ([pida1, pidb1], factor)) + { + for (fk_table, ops) in + channels0 + .iter() + .zip(tables.iter_mut()) + .filter_map(|(pids0, fk_table)| { + izip!(pids0, &pids1, &pids01, &eko_slices) + .map(|(&pid0, &pid1, pids, slices)| { + pids.iter().zip(slices).find_map(|(&(p0, p1), op)| { + ((p0 == pid0) && (p1 == pid1)).then_some(op) + }) + }) + // TODO: avoid using `collect` + .collect::>>() + .map(|ops| (fk_table, ops)) + }) + { + // tmp = array * ops[1]^T + linalg::general_mat_mul(1.0, &array, &ops[1].t(), 0.0, &mut tmp); + // fk_table += factor * ops[0] * tmp + linalg::general_mat_mul(factor, ops[0], &tmp, 1.0, fk_table); + } + } + } + + sub_fk_tables.extend(tables.into_iter().map(|table| { + ImportOnlySubgridV2::new( + SparseArray3::from_ndarray(table.insert_axis(Axis(0)).view(), 0, 1), + vec![Mu2 { + // TODO: FK tables don't depend on the renormalization scale + //ren: -1.0, + ren: infos[0].fac0, + fac: infos[0].fac0, + }], + infos[0].x0.clone(), + infos[1].x0.clone(), + ) + .into() + })); + } + + Ok(( + Array1::from_iter(sub_fk_tables) + .into_shape((1, grid.bin_info().bins(), channels0.len())) + .unwrap(), + channels0 + .iter() + .map(|c| channel![c[0], c[1], 1.0]) + .collect(), + )) +} diff --git a/pineappl/src/grid.rs b/pineappl/src/grid.rs index 5d3c2b17..86e744b9 100644 --- a/pineappl/src/grid.rs +++ b/pineappl/src/grid.rs @@ -1489,6 +1489,145 @@ impl Grid { Ok(FkTable::try_from(grid).unwrap_or_else(|_| unreachable!())) } + /// Converts this `Grid` into an [`FkTable`] using `slices` that must iterate over a [`Result`] + /// of tuples of an [`OperatorSliceInfo`] and the corresponding sliced operator. The parameter + /// `order_mask` can be used to include or exclude orders from this operation, and must + /// correspond to the ordering given by [`Grid::orders`]. Orders that are not given are + /// enabled, and in particular if `order_mask` is empty all orders are activated. + /// + /// # Errors + /// + /// Returns a [`GridError::EvolutionFailure`] if either the `operator` or its `info` is + /// incompatible with this `Grid`. Returns a [`GridError::Other`] if the iterator from `slices` + /// return an error. + pub fn evolve_with_slice_iter2<'a, E: Into>( + &self, + slices_a: impl IntoIterator), E>>, + slices_b: impl IntoIterator), E>>, + order_mask: &[bool], + xi: (f64, f64), + alphas_table: &AlphasTable, + ) -> Result { + use super::evolution::EVOLVE_INFO_TOL_ULPS; + use itertools::izip; + + let mut lhs: Option = None; + let mut fac1 = Vec::new(); + + // TODO: simplify the ugly repetition below by offloading some ops into fn + for (result_a, result_b) in izip!(slices_a, slices_b) { + // Operate on `slices_a` + let (info_a, operator_a) = result_a.map_err(|err| GridError::Other(err.into()))?; + + let op_info_dim_a = ( + info_a.pids1.len(), + info_a.x1.len(), + info_a.pids0.len(), + info_a.x0.len(), + ); + + if operator_a.dim() != op_info_dim_a { + return Err(GridError::EvolutionFailure(format!( + "operator information {:?} does not match the operator's dimensions: {:?}", + op_info_dim_a, + operator_a.dim(), + ))); + } + + // Operate on `slices_b` + let (info_b, operator_b) = result_b.map_err(|err| GridError::Other(err.into()))?; + + let op_info_dim_b = ( + info_b.pids1.len(), + info_b.x1.len(), + info_b.pids0.len(), + info_b.x0.len(), + ); + + if operator_b.dim() != op_info_dim_b { + return Err(GridError::EvolutionFailure(format!( + "operator information {:?} does not match the operator's dimensions: {:?}", + op_info_dim_b, + operator_b.dim(), + ))); + } + + let views = [operator_a.view(), operator_b.view()]; + let infos = [info_a, info_b]; + + let (subgrids, channels) = if self.convolutions()[0] != Convolution::None + && self.convolutions()[1] != Convolution::None + { + evolution::evolve_slice_with_two2( + self, + &views, + &infos, + order_mask, + xi, + alphas_table, + ) + } else { + evolution::evolve_slice_with_one( + self, + &views[0], + &infos[1], + order_mask, + xi, + alphas_table, + ) + }?; + + let mut rhs = Self { + subgrids, + channels, + bin_limits: self.bin_limits.clone(), + orders: vec![Order::new(0, 0, 0, 0)], + subgrid_params: SubgridParams::default(), + more_members: self.more_members.clone(), + }; + + assert_eq!(infos[0].pid_basis, infos[1].pid_basis); + + // TODO: use a new constructor to set this information + rhs.set_pid_basis(infos[0].pid_basis); + + if let Some(lhs) = &mut lhs { + lhs.merge(rhs)?; + } else { + lhs = Some(rhs); + } + + // NOTE: The following should be shared by the 2 EKOs(?) + fac1.push(infos[0].fac1); + } + + // UNWRAP: if we can't compare two numbers there's a bug + fac1.sort_by(|a, b| a.partial_cmp(b).unwrap_or_else(|| unreachable!())); + + // make sure we've evolved all slices + if let Some(muf2) = self + .evolve_info(order_mask) + .fac1 + .into_iter() + .map(|mu2| xi.1 * xi.1 * mu2) + .find(|&grid_mu2| { + !fac1 + .iter() + .any(|&eko_mu2| approx_eq!(f64, grid_mu2, eko_mu2, ulps = EVOLVE_INFO_TOL_ULPS)) + }) + { + return Err(GridError::EvolutionFailure(format!( + "no operator for muf2 = {muf2} found in {fac1:?}" + ))); + } + + // TODO: convert this unwrap into error + let grid = lhs.unwrap(); + + // UNWRAP: merging evolved slices should be a proper FkTable again + Ok(FkTable::try_from(grid).unwrap_or_else(|_| unreachable!())) + } + /// Deletes bins with the corresponding `bin_indices`. Repeated indices and indices larger or /// equal the bin length are ignored. pub fn delete_bins(&mut self, bin_indices: &[usize]) { diff --git a/pineappl_cli/src/evolve.rs b/pineappl_cli/src/evolve.rs index b2fff58d..5b084ea5 100644 --- a/pineappl_cli/src/evolve.rs +++ b/pineappl_cli/src/evolve.rs @@ -424,13 +424,14 @@ mod eko { #[cfg(feature = "evolve")] fn evolve_grid( grid: &Grid, - eko: &Path, + ekos: &[&Path], use_alphas_from: &Pdf, orders: &[(u32, u32)], xir: f64, xif: f64, use_old_evolve: bool, ) -> Result { + use anyhow::bail; use eko::EkoSlices; use pineappl::evolution::{AlphasTable, OperatorInfo}; @@ -445,15 +446,20 @@ fn evolve_grid( }) .collect(); - let mut eko_slices = EkoSlices::new(eko)?; + let mut eko_slices: Vec<_> = ekos + .iter() + .map(|eko| EkoSlices::new(eko)) + .collect::>()?; let alphas_table = AlphasTable::from_grid(grid, xir, &|q2| use_alphas_from.alphas_q2(q2)); if use_old_evolve { + assert_eq!(eko_slices.len(), 1); + if let EkoSlices::V0 { fac1, info, operator, - } = eko_slices + } = eko_slices.remove(0) { let op_info = OperatorInfo { fac0: info.fac0, @@ -472,17 +478,32 @@ fn evolve_grid( #[allow(deprecated)] Ok(grid.evolve(operator.view(), &op_info, &order_mask)?) } else { - unimplemented!(); + bail!("`--use-old-evolve` can only be used with the old EKO format (`V0`)") } } else { - Ok(grid.evolve_with_slice_iter(&mut eko_slices, &order_mask, (xir, xif), &alphas_table)?) + match eko_slices.as_mut_slice() { + [eko] => { + Ok(grid.evolve_with_slice_iter(eko, &order_mask, (xir, xif), &alphas_table)?) + } + [eko_a, eko_b] => Ok(grid.evolve_with_slice_iter2( + eko_a, + eko_b, + &order_mask, + (xir, xif), + &alphas_table, + )?), + _ => unimplemented!( + "evolution with {} EKOs is not implemented", + eko_slices.len() + ), + } } } #[cfg(not(feature = "evolve"))] fn evolve_grid( _: &Grid, - _: &Path, + _: &[&Path], _: &Pdf, _: &[(u32, u32)], _: f64, @@ -508,6 +529,9 @@ pub struct Opts { output: PathBuf, /// LHAPDF ID(s) or name of the PDF(s)/FF(s). conv_funs: ConvFuns, + /// Additional path to the 2nd evolution kernel operator. + #[arg(value_hint = ValueHint::FilePath, long)] + ekob: Option, /// Relative threshold between the table and the converted grid when comparison fails. #[arg(default_value = "1e-3", long)] accuracy: f64, @@ -555,13 +579,17 @@ impl Subcommand for Opts { let fk_table = evolve_grid( &grid, - &self.eko, + &self.ekob.as_ref().map_or_else( + || vec![self.eko.as_path()], + |ekob| vec![self.eko.as_path(), ekob], + ), &conv_funs[cfg.use_alphas_from], &self.orders, self.xir, self.xif, self.use_old_evolve, )?; + let evolved_results = helpers::convolve_scales( fk_table.grid(), &mut conv_funs, diff --git a/pineappl_cli/tests/evolve.rs b/pineappl_cli/tests/evolve.rs index 6b6193c8..fcf25ad3 100644 --- a/pineappl_cli/tests/evolve.rs +++ b/pineappl_cli/tests/evolve.rs @@ -14,6 +14,7 @@ Arguments: LHAPDF ID(s) or name of the PDF(s)/FF(s) Options: + --ekob Additional path to the 2nd evolution kernel operator --accuracy Relative threshold between the table and the converted grid when comparison fails [default: 1e-3] --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] @@ -171,6 +172,16 @@ const CMS_TTB_8TEV_2D_TTM_TRAP_TOT_STR: &str = "b Grid FkTable rel. 0 2.1596192e2 2.1590144e2 -2.8005486e-4 "; +const STAR_WMWP_510GEV_WM_AL_POL: &str = "b Grid FkTable rel. diff +-+-----------+-----------+------------- +0 3.2222870e2 3.2226654e2 1.1745654e-4 +1 1.8038157e3 1.8037829e3 -1.8192479e-5 +2 3.4767572e3 3.4762728e3 -1.3933339e-4 +3 4.3157563e3 4.3154783e3 -6.4409623e-5 +4 3.6443947e3 3.6443481e3 -1.2807044e-5 +5 5.8386697e2 5.8336795e2 -8.5468266e-4 +"; + #[test] fn help() { Command::cargo_bin("pineappl") @@ -482,3 +493,22 @@ fn cms_ttb_8tev_2d_ttm_trap_tot() { .success() .stdout(CMS_TTB_8TEV_2D_TTM_TRAP_TOT_STR); } + +#[test] +fn star_wmwp_510gev_wm_al_pol() { + let output = NamedTempFile::new("fktable6.lz4").unwrap(); + + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "evolve", + "../test-data/STAR_WMWP_510GEV_WM-AL-POL.pineappl.lz4", + "../test-data/STAR_WMWP_510GEV_WM-AL-POL_PolPDF.tar", + output.path().to_str().unwrap(), + "240608-tr-pol-nlo-100,NNPDF40_nlo_pch_as_01180", + "--ekob=../test-data/STAR_WMWP_510GEV_WM-AL-POL_UnpolPDF.tar", + ]) + .assert() + .success() + .stdout(STAR_WMWP_510GEV_WM_AL_POL); +} diff --git a/pineappl_py/src/fk_table.rs b/pineappl_py/src/fk_table.rs index f3f30d3e..ec43717e 100644 --- a/pineappl_py/src/fk_table.rs +++ b/pineappl_py/src/fk_table.rs @@ -244,6 +244,50 @@ impl PyFkTable { .into_pyarray_bound(py) } + /// Convolute grid with two pdfs. + /// + /// **Usage:** `pineko`, `nnpdf` + /// + /// Parameters + /// ---------- + /// pdg_id1 : integer + /// PDG Monte Carlo ID of the hadronic particle `xfx1` is the PDF for + /// xfx1 : callable + /// lhapdf like callable with arguments `pid, x, Q2` returning x*pdf for :math:`x`-grid + /// pdg_id2 : integer + /// PDG Monte Carlo ID of the hadronic particle `xfx2` is the PDF for + /// xfx2 : callable + /// lhapdf like callable with arguments `pid, x, Q2` returning x*pdf for :math:`x`-grid + /// + /// Returns + /// ------- + /// numpy.ndarray(float) : + /// cross sections for all bins + #[pyo3(signature = (pdg_id1, xfx1, pdg_id2, xfx2, bin_indices = None, lumi_mask= None))] + pub fn convolve_with_two<'py>( + &self, + pdg_id1: i32, + xfx1: &PyAny, + pdg_id2: i32, + xfx2: &PyAny, + bin_indices: Option>, + lumi_mask: Option>, + py: Python<'py>, + ) -> &'py PyArray1 { + let mut xfx1 = |id, x, q2| f64::extract(xfx1.call1((id, x, q2)).unwrap()).unwrap(); + let mut xfx2 = |id, x, q2| f64::extract(xfx2.call1((id, x, q2)).unwrap()).unwrap(); + let mut alphas = |_| 1.0; + let mut lumi_cache = + LumiCache::with_two(pdg_id1, &mut xfx1, pdg_id2, &mut xfx2, &mut alphas); + self.fk_table + .convolve( + &mut lumi_cache, + &bin_indices.map_or(vec![], |b| b.to_vec().unwrap()), + &lumi_mask.map_or(vec![], |l| l.to_vec().unwrap()), + ) + .into_pyarray(py) + } + /// Optimize FK table storage /// /// In order to perform any relevant optimization, assumptions are needed, and they are passed diff --git a/pineappl_py/src/grid.rs b/pineappl_py/src/grid.rs index 5fe27849..f89c22da 100644 --- a/pineappl_py/src/grid.rs +++ b/pineappl_py/src/grid.rs @@ -611,6 +611,60 @@ impl PyGrid { .unwrap()) } + /// TODO + /// + /// Parameters + /// ---------- + /// slices : TODO + /// order_mask : TODO + /// + /// Returns + /// ------- + /// TODO + pub fn evolve_with_slice_iter2<'py>( + &self, + slices_a: &Bound<'py, PyIterator>, + slices_b: &Bound<'py, PyIterator>, + order_mask: PyReadonlyArray1, + xi: (f64, f64), + ren1: Vec, + alphas: Vec, + ) -> PyResult { + Ok(self + .grid + .evolve_with_slice_iter2( + slices_a.into_iter().map(|slice| { + let (info, op) = slice + .unwrap() + .extract::<(PyOperatorSliceInfo, PyReadonlyArray4)>() + .unwrap(); + Ok::<_, std::io::Error>(( + info.info, + // TODO: avoid copying + CowArray::from(op.as_array().to_owned()), + )) + }), + slices_b.into_iter().map(|slice| { + let (info, op) = slice + .unwrap() + .extract::<(PyOperatorSliceInfo, PyReadonlyArray4)>() + .unwrap(); + Ok::<_, std::io::Error>(( + info.info, + // TODO: avoid copying + CowArray::from(op.as_array().to_owned()), + )) + }), + // TODO: make `order_mask` a `Vec` + &order_mask.to_vec().unwrap(), + xi, + &AlphasTable { ren1, alphas }, + ) + .map(|fk_table| PyFkTable { fk_table }) + // TODO: avoid unwrap and convert `Result` into `PyResult` + .unwrap()) + } + /// Load grid from file. /// /// **Usage:** `pineko`, FKTable generation diff --git a/pineappl_py/tests/test_fk_table.py b/pineappl_py/tests/test_fk_table.py index e1fa93aa..4f9455e2 100644 --- a/pineappl_py/tests/test_fk_table.py +++ b/pineappl_py/tests/test_fk_table.py @@ -54,3 +54,22 @@ def test_convolve_with_one(self): assert False except: assert True + + # TODO: write a better test + try: + g.evolve_with_slice_iter2( + iter( + [(info, np.ndarray([0, 0, 0, 0])), (info, np.ndarray([0, 0, 0, 0]))] + ), + iter( + [(info, np.ndarray([0, 0, 0, 0])), (info, np.ndarray([0, 0, 0, 0]))] + ), + np.array([], dtype=bool), + (1.0, 1.0), + [], + [], + ) + + assert False + except: + assert True