diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 5fc727b75..b8596915c 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -18,16 +18,7 @@ jobs: df -h # 100 largest packages, in ascending order dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 100 - sudo apt-get remove -y google-cloud-cli - sudo apt-get remove -y azure-cli - sudo apt-get remove -y microsoft-edge-stable - sudo apt-get remove -y '^dotnet-.*' - sudo apt-get remove -y '^temurin-.*-jdk' - sudo apt-get remove -y google-chrome-stable - sudo apt-get remove -y '^llvm-.*-dev' - sudo apt-get remove -y firefox - sudo apt-get remove -y powershell - sudo apt-get remove -y mono-devel + sudo apt-get remove -y microsoft-edge-stable azure-cli google-cloud-cli google-chrome-stable '^temurin-.*-jdk' '^llvm-.*-dev' firefox powershell sudo apt-get autoremove -y sudo apt-get clean # disk space after removing packages @@ -36,6 +27,7 @@ jobs: uses: redhat-actions/buildah-build@v2 with: image: pineappl-ci + tags: fastnlo-69d87cf4 latest containerfiles: maintainer/pineappl-ci/Containerfile context: maintainer/pineappl-ci/ oci: true diff --git a/maintainer/pineappl-ci/Containerfile b/maintainer/pineappl-ci/Containerfile index e1d8fea89..fda2a2a2f 100644 --- a/maintainer/pineappl-ci/Containerfile +++ b/maintainer/pineappl-ci/Containerfile @@ -4,7 +4,8 @@ FROM debian:11-slim ARG APPLGRID_V=1.6.36 ARG CARGOC_V=0.10.3 -ARG FASTNLO_V=2.5.0-2826 +ARG FASTNLO_R=69d87cf4 +ARG FASTNLO_V=2.6 ARG LHAPDF_V=6.5.4 ARG ZLIB_V=1.3.1 diff --git a/maintainer/pineappl-ci/script.sh b/maintainer/pineappl-ci/script.sh index 93b0b26b3..c1f6c8aba 100755 --- a/maintainer/pineappl-ci/script.sh +++ b/maintainer/pineappl-ci/script.sh @@ -3,11 +3,13 @@ set -euo pipefail pkgs=( + automake build-essential curl gfortran git libssl-dev + libtool openssl pkg-config ) @@ -85,8 +87,12 @@ cp src/*.h "${APPL_IGRID_DIR}" cd .. # install fastNLO -curl "https://fastnlo.hepforge.org/code/v25/fastnlo_toolkit-${FASTNLO_V}.tar.gz" | tar xzf - -cd "fastnlo_toolkit-${FASTNLO_V}" +# curl "https://fastnlo.hepforge.org/code/v25/fastnlo_toolkit-${FASTNLO_V}.tar.gz" | tar xzf - +git clone https://gitlab.etp.kit.edu/qcd-public/fastNLO.git +cd fastNLO +git checkout "${FASTNLO_R}" +cd "v${FASTNLO_V}/toolkit" +autoreconf -fi ./configure --prefix=/usr/local/ make -j V=1 make install diff --git a/pineappl_cli/Cargo.toml b/pineappl_cli/Cargo.toml index 7d5778374..180cba3ba 100644 --- a/pineappl_cli/Cargo.toml +++ b/pineappl_cli/Cargo.toml @@ -54,6 +54,6 @@ rustc-args = [ "--cfg feature=\"docs-only\"" ] [features] applgrid = ["dep:cxx", "dep:pineappl_applgrid"] evolve = ["dep:base64", "dep:either", "dep:tar", "dep:lz4_flex", "dep:ndarray-npy", "dep:serde", "dep:serde_yaml"] -fastnlo = ["dep:pineappl_fastnlo"] +fastnlo = ["dep:cxx", "dep:pineappl_fastnlo"] fktable = ["dep:flate2", "dep:tar"] static = ["lhapdf/static", "pineappl/static", "pineappl_applgrid?/static", "pineappl_fastnlo?/static"] diff --git a/pineappl_cli/src/export.rs b/pineappl_cli/src/export.rs index 5fc1c9563..1846645dc 100644 --- a/pineappl_cli/src/export.rs +++ b/pineappl_cli/src/export.rs @@ -12,6 +12,9 @@ use std::process::ExitCode; #[cfg(feature = "applgrid")] mod applgrid; +#[cfg(feature = "fastnlo")] +mod fastnlo; + #[cfg(feature = "applgrid")] fn convert_into_applgrid( output: &Path, @@ -42,6 +45,36 @@ fn convert_into_applgrid( )) } +#[cfg(feature = "fastnlo")] +fn convert_into_fastnlo( + output: &Path, + grid: &Grid, + conv_funs: &mut [Pdf], + _: usize, + discard_non_matching_scales: bool, +) -> Result<(&'static str, Vec, usize, Vec)> { + // TODO: check also scale-varied results + + let (mut fastnlo, order_mask) = + fastnlo::convert_into_fastnlo(grid, output, discard_non_matching_scales)?; + let results = fastnlo::convolve_fastnlo(fastnlo.pin_mut(), conv_funs); + + Ok(("fastNLO", results, 1, order_mask)) +} + +#[cfg(not(feature = "fastnlo"))] +fn convert_into_fastnlo( + _: &Path, + _: &Grid, + _: &mut [Pdf], + _: usize, + _: bool, +) -> Result<(&'static str, Vec, usize, Vec)> { + Err(anyhow!( + "you need to install `pineappl` with feature `fastnlo`" + )) +} + fn convert_into_grid( output: &Path, grid: &mut Grid, @@ -58,13 +91,27 @@ fn convert_into_grid( scales, discard_non_matching_scales, ); + } else if extension == "tab" + || (extension == "gz" + && output + .with_extension("") + .extension() + .map_or(false, |ext| ext == "tab")) + { + return convert_into_fastnlo( + output, + grid, + conv_funs, + scales, + discard_non_matching_scales, + ); } } Err(anyhow!("could not detect file format")) } -/// Converts PineAPPL grids to APPLgrid files. +/// Converts PineAPPL grids to APPLgrid/fastNLO files. #[derive(Parser)] pub struct Opts { /// Path to the input grid. diff --git a/pineappl_cli/src/export/fastnlo.rs b/pineappl_cli/src/export/fastnlo.rs new file mode 100644 index 000000000..13495e7fd --- /dev/null +++ b/pineappl_cli/src/export/fastnlo.rs @@ -0,0 +1,112 @@ +use anyhow::{bail, Result}; +use cxx::UniquePtr; +use float_cmp::assert_approx_eq; +use lhapdf::Pdf; +use pineappl::boc::Order; +use pineappl::grid::Grid; +use pineappl_fastnlo::ffi::{self, fastNLOLHAPDF}; +use std::path::Path; +use std::pin::Pin; + +pub fn convert_into_fastnlo( + grid: &Grid, + _output: &Path, + _discard_non_matching_scales: bool, +) -> Result<(UniquePtr, Vec)> { + let bwfl = grid.bwfl(); + let dim = bwfl.dimensions(); + + if dim > 3 { + bail!( + "grid has {} dimensions, but fastNLO only supports up to three-dimensional distributions", + dim + ); + } + + let bins = bwfl.bins(); + let left_bin_limits: Vec> = bins + .iter() + .map(|bin| bin.limits().iter().map(|&(l, _)| l).collect()) + .collect(); + let right_bin_limits: Vec> = bins + .iter() + .map(|bin| bin.limits().iter().map(|&(_, r)| r).collect()) + .collect(); + let normalizations = bwfl.normalizations(); + + let order_mask = Order::create_mask(grid.orders(), 3, 0, false); + let orders_with_mask: Vec<_> = grid + .orders() + .iter() + .cloned() + .zip(order_mask.iter().copied()) + .collect(); + let lo_alphas = orders_with_mask + .iter() + .filter_map(|&(Order { alphas, .. }, keep)| keep.then_some(alphas)) + .min() + // UNWRAP: this will fail for `Grid` with no orders, but this shouldn't happen + .unwrap(); + //let loops = orders_with_mask + // .iter() + // .filter_map(|&(Order { alphas, .. }, keep)| keep.then_some(alphas)) + // .max() + // .unwrap() + // - lo_alphas; + + let convolutions: Vec = grid.convolutions().iter().map(|conv| conv.pid()).collect(); + + // TODO: lift this restriction + assert_eq!(grid.convolutions().len(), 2); + + let channels: Vec> = grid + .channels() + .iter() + .map(|channel| { + channel + .entry() + .iter() + .map(|&(ref pids, factor)| { + assert_approx_eq!(f64, factor, 1.0, ulps = 4); + ffi::pair_int_int { + first: pids[0], + second: pids[1], + } + }) + .collect() + }) + .collect(); + + //for (fnlo_order, order) in order_mask + // .iter() + // .enumerate() + // .filter_map(|(index, keep)| keep.then_some(index)) + // .enumerate() + //{} + + let _fastnlo = ffi::make_fastnlo_create( + // UNWRAP: negative numbers and overflow should not happen + lo_alphas.try_into().unwrap(), + &left_bin_limits, + &right_bin_limits, + &normalizations, + // TODO: calculate channels for each order separately + // UNWRAP: negative numbers and overflow should not happen + channels.len().try_into().unwrap(), + // UNWRAP: negative numbers and overflow should not happen + channels.len().try_into().unwrap(), + // UNWRAP: negative numbers and overflow should not happen + channels.len().try_into().unwrap(), + &convolutions, + &channels, + ); + + todo!() +} + +pub fn convolve_fastnlo(_grid: Pin<&mut fastNLOLHAPDF>, conv_funs: &mut [Pdf]) -> Vec { + // TODO: add support for convolving an fastNLO table with two functions + assert_eq!(conv_funs.len(), 1); + + todo!() +} diff --git a/pineappl_cli/tests/export.rs b/pineappl_cli/tests/export.rs index bd25e451a..53716f4f8 100644 --- a/pineappl_cli/tests/export.rs +++ b/pineappl_cli/tests/export.rs @@ -5,7 +5,7 @@ use assert_cmd::Command; #[cfg(feature = "applgrid")] use assert_fs::NamedTempFile; -const HELP_STR: &str = "Converts PineAPPL grids to APPLgrid files +const HELP_STR: &str = "Converts PineAPPL grids to APPLgrid/fastNLO files Usage: pineappl export [OPTIONS] diff --git a/pineappl_cli/tests/main.rs b/pineappl_cli/tests/main.rs index 8f6b2e34d..67144d567 100644 --- a/pineappl_cli/tests/main.rs +++ b/pineappl_cli/tests/main.rs @@ -12,7 +12,7 @@ Commands: convolve Convolutes a PineAPPL grid with a PDF set diff Compares the numerical content of two grids with each other evolve Evolve a grid with an evolution kernel operator to an FK table - export Converts PineAPPL grids to APPLgrid files + export Converts PineAPPL grids to APPLgrid/fastNLO files help Display a manpage for selected subcommands import Converts APPLgrid/fastNLO/FastKernel files to PineAPPL grids merge Merges one or more PineAPPL grids together diff --git a/pineappl_fastnlo/build.rs b/pineappl_fastnlo/build.rs index 0eadd2b81..412ff1e52 100644 --- a/pineappl_fastnlo/build.rs +++ b/pineappl_fastnlo/build.rs @@ -65,7 +65,7 @@ fn main() { .file("src/fastnlo.cpp") .include(fnlo_include_path.trim()) .includes(lhapdf_include_paths) - .std("c++11") // apparently not supported by MSVC, but fastNLO probably can't be compiled on Windows + .std("c++17") .compile("fnlo-bridge"); println!("cargo:rerun-if-changed=src/lib.rs"); diff --git a/pineappl_fastnlo/src/fastnlo.cpp b/pineappl_fastnlo/src/fastnlo.cpp index 7cda9649f..93865738a 100644 --- a/pineappl_fastnlo/src/fastnlo.cpp +++ b/pineappl_fastnlo/src/fastnlo.cpp @@ -1,6 +1,7 @@ #include "pineappl_fastnlo/src/fastnlo.hpp" #include +#include #include #include @@ -65,6 +66,171 @@ std::unique_ptr make_fastnlo_lhapdf_with_name_file_set( return std::unique_ptr(new fastNLOLHAPDF(arg0, arg1, PDFSet)); } +std::unique_ptr make_fastnlo_create( + int alphas_lo, + rust::Slice const> left_bin_limits, + rust::Slice const> right_bin_limits, + rust::Slice normalizations, + int lo_channels, + int nlo_channels, + int nnlo_channels, + rust::Slice convolutions, + rust::Slice const> channels +) { + assert(left_bin_limits.size() == right_bin_limits.size()); + auto const bins = left_bin_limits.size(); + assert(bins == normalizations.size()); + assert(bins > 0); + auto const dimensions = left_bin_limits.at(0).size(); + assert(dimensions > 0); + assert(convolutions.size() <= 2); + assert(convolutions.size() >= 1); + + std::vector> bin_limits(dimensions); + + // TODO: check if this is the right ordering + for (std::size_t i = 0; i != dimensions; ++i) { + assert(left_bin_limits.at(i).size() == dimensions); + assert(right_bin_limits.at(i).size() == dimensions); + + //bin_limits.at(i).resize(2 * limits); + + //for (std::size_t j = 0; j != limits; ++j) { + // bin_limits.at(i).at(2 * j + 0) = left_bin_limits.at(j).at(i); + // bin_limits.at(i).at(2 * j + 1) = right_bin_limits.at(j).at(i); + //} + bin_limits.at(i).resize(bins + 1); + bin_limits.at(i).at(0) = left_bin_limits.at(0).front(); + + for (std::size_t j = 0; j != bins; ++j) { + bin_limits.at(i).at(j + 1) = right_bin_limits.at(j).at(i); + } + } + + fastNLO::GeneratorConstants gconst; + // TODO: add PineAPPL's version number + gconst.Name = "PineAPPL-fastNLO interface"; + + fastNLO::ProcessConstants pconst; + pconst.LeadingOrder = alphas_lo; + pconst.NPDF = convolutions.size(); + pconst.NSubProcessesLO = lo_channels; + pconst.NSubProcessesNLO = nlo_channels; + pconst.NSubProcessesNNLO = nnlo_channels; + + if (convolutions.size() == 1) { + pconst.IPDFdef1 = 2; + } else { + pconst.IPDFdef1 = 3; + } + + // TODO: is this the correct value to set the linear combinations ourselves? + pconst.IPDFdef2 = 0; + pconst.IPDFdef3LO = 2; + pconst.IPDFdef3NLO = 0; + pconst.IPDFdef3NNLO = 0; + + if (convolutions.size() == 1) { + // TODO: not yet implemented + assert(false); + } else { + pconst.NPDFDim = 2; + } + + std::vector>> linear_combinations(channels.size()); + for (std::size_t i = 0; i != channels.size(); ++i) { + std::vector> entries(channels.at(i).size()); + for (std::size_t j = 0; j != channels.at(i).size(); ++j) { + auto const first = channels.at(i).at(j).first; + auto const second = channels.at(i).at(j).second; + entries.at(j) = std::make_pair(first, second); + } + linear_combinations.at(i) = entries; + } + pconst.PDFCoeffLO = linear_combinations; + + fastNLO::ScenarioConstants sconst; + sconst.DifferentialDimension = dimensions; + sconst.DimensionIsDifferential = std::vector(dimensions, 0); + sconst.CalculateBinSize = false; + sconst.BinSize = std::vector(normalizations.begin(), normalizations.end()); + + switch (sconst.DifferentialDimension) { + case 1: + sconst.SingleDifferentialBinning = bin_limits.at(0); + break; + + case 2: + sconst.DoubleDifferentialBinning = bin_limits; + break; + + case 3: + sconst.TripleDifferentialBinning = bin_limits; + break; + + default: + // ASSERT: there are no or too many dimensions, which fastNLO doesn't support + assert(false); + } + sconst.FlexibleScaleTable = true; + + if (convolutions.size() == 1) { + sconst.PDF1 = convolutions.at(0); + // TODO: do we leave PDF2 unchanged (set to 'proton') for DIS? + } else { + sconst.PDF1 = convolutions.at(0); + sconst.PDF2 = convolutions.at(1); + } + + sconst.ReadBinningFromSteering = true; + sconst.IgnoreWarmupBinningCheck = true; + sconst.X_NNodeCounting = "NodesPerBin"; + sconst.Mu1_NNodeCounting = "NodesPerBin"; + sconst.Mu2_NNodeCounting = "NodesPerBin"; + + fastNLO::WarmupConstants wconst(sconst); + + for (std::size_t bin = 0; bin != bins; ++bin) { + std::vector limits; + + // TODO: the following code assumes one dimension + assert(dimensions == 1); + + // TODO: don't know the meaning of this field + limits.push_back(-1.0); + // left bin limit + limits.push_back(bin_limits.at(0).at(2 * bin + 0)); + // right bin limit + limits.push_back(bin_limits.at(0).at(2 * bin + 1)); + + wconst.Binning.push_back(limits); + } + + // these values are probably irrelevant but must nevertheless given + wconst.Values.resize(bins, std::vector{ + // bin index + 0, + // x-min + 2e-7, + // x-max + 1.0, + // scale1-min + 10.0, + // scale1-max + 100.0, + // scale2-min + 10.0, + // scale2-max + 100.0 + }); + for (std::size_t i = 0; i != wconst.Values.size(); ++i) { + wconst.Values.at(i).at(0) = static_cast (i); + } + // wconst.headerValues = ; + + return std::unique_ptr(new fastNLOCreate(gconst, pconst, sconst, wconst)); +} + rust::Vec GetCrossSection(fastNLOReader& reader, bool lNorm) { return std_vector_to_rust_vec(reader.GetCrossSection(lNorm)); diff --git a/pineappl_fastnlo/src/fastnlo.hpp b/pineappl_fastnlo/src/fastnlo.hpp index 832e8809f..ef666be80 100644 --- a/pineappl_fastnlo/src/fastnlo.hpp +++ b/pineappl_fastnlo/src/fastnlo.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,18 @@ std::unique_ptr make_fastnlo_lhapdf_with_name_file_set( int PDFSet ); +std::unique_ptr make_fastnlo_create( + int alphas_lo, + rust::Slice const> left_bin_limits, + rust::Slice const> right_bin_limits, + rust::Slice normalizations, + int lo_channels, + int nlo_channels, + int nnlo_channels, + rust::Slice convolutions, + rust::Slice const> channels +); + rust::Vec CalcPDFLinearCombination( fastNLOPDFLinearCombinations const& lc, fastNLOCoeffAddBase const& base, diff --git a/pineappl_fastnlo/src/lib.rs b/pineappl_fastnlo/src/lib.rs index 27bca84ab..3c15cb701 100644 --- a/pineappl_fastnlo/src/lib.rs +++ b/pineappl_fastnlo/src/lib.rs @@ -134,6 +134,12 @@ pub mod ffi { fn GetPDFPDG(&self, _: i32) -> i32; } + unsafe extern "C++" { + include!("fastnlotk/fastNLOCreate.h"); + + type fastNLOCreate; + } + unsafe extern "C++" { include!("pineappl_fastnlo/src/fastnlo.hpp"); @@ -189,6 +195,18 @@ pub mod ffi { _: &str, _: i32, ) -> UniquePtr; + + fn make_fastnlo_create( + alphas_lo: i32, + left_bin_limits: &[Vec], + right_bin_limits: &[Vec], + normalizations: &[f64], + lo_channels: i32, + nlo_channels: i32, + nnlo_channels: i32, + convolutions: &[i32], + channels: &[Vec], + ) -> UniquePtr; } }