diff --git a/examples/cpp/Makefile b/examples/cpp/Makefile index aef4689b5..9cae212b8 100644 --- a/examples/cpp/Makefile +++ b/examples/cpp/Makefile @@ -6,6 +6,7 @@ LHAPDF_DEPS != pkg-config --cflags --libs lhapdf PROGRAMS = \ fill-grid-deprecated \ fill-grid \ + fill-grid-pp2hadron \ fill-custom-grid-deprecated \ advanced-convolution-deprecated \ advanced-convolution \ @@ -72,6 +73,9 @@ fill-grid-deprecated: fill-grid-deprecated.cpp fill-grid: fill-grid.cpp $(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@ +fill-grid-pp2hadron: fill-grid-pp2hadron.cpp + $(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@ + merge-grids: merge-grids.cpp $(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@ diff --git a/examples/cpp/fill-grid-pp2hadron.cpp b/examples/cpp/fill-grid-pp2hadron.cpp new file mode 100644 index 000000000..d0b4341fd --- /dev/null +++ b/examples/cpp/fill-grid-pp2hadron.cpp @@ -0,0 +1,241 @@ +/// Example used in the PineAPPL v1 paper /// +#include +#include + +#include +#include +#include +#include + +struct Psp2to2Hadron { + double s; + double t; + double u; + double x1; + double x2; + double z; + double pt_hadron; + double y_hadron; + double jacobian; +}; + +double me_gg2qqbar(double /*s*/, double t, double u) { + double as2 = 0.118 * 0.118; + double PI2 = std::acos(-1.0) * std::acos(-1.0); // π^2 + // TODO: double-check + return (16 * PI2 * as2 / 6.0) * (u * u + t * t) / (u * t); +} + +Psp2to2Hadron pspgen_pp2hadron(std::mt19937& rng, double mmin, + double mmax, double pt_min, double pt_max, double abs_y_max) { + using std::acos; + using std::log; + using std::pow; + using std::exp; + using std::sqrt; + + double smin = mmin * mmin; + double smax = mmax * mmax; + + double r1 = std::generate_canonical(rng); + double r2 = std::generate_canonical(rng); + double r3 = std::generate_canonical(rng); + double r4 = std::generate_canonical(rng); + double r5 = std::generate_canonical(rng); + double r6 = std::generate_canonical(rng); + + double tau0 = smin / smax; + double tau = pow(tau0, r1); + double y = pow(tau, 1.0 - r2); + double x1 = y; + double x2 = tau / y; + double s = tau * smax; + + double jacobian = tau * log(tau0) * log(tau0) * r1; + + // `theta` integration + double cos_theta = 2.0 * r3 - 1.0; + jacobian *= 2.0; + + double t = -0.5 * s * (1.0 - cos_theta); + double u = -0.5 * s * (1.0 + cos_theta); + + // `phi` integration + jacobian *= 2.0 * acos(-1.0); + + // sample hadron `pT` uniformly in log scale + double log_pt_min = log(pt_min); + double log_pt_max = log(pt_max); + + double pt_hadron = exp(log_pt_min + (log_pt_max - log_pt_min) * r4); + jacobian *= pt_hadron * (log_pt_max - log_pt_min); + + // sample hadron rapidity uniformly + double y_hadron = 2.0 * abs_y_max * r5 - abs_y_max; + jacobian *= 2.0 * abs_y_max; + + // define the momentum fracion `z` + double z_min = pt_hadron * exp(-y_hadron) / sqrt(s); + double z_max_kin = pt_hadron * exp(y_hadron) / sqrt(s); + double z_max = std::min(1.0, z_max_kin); + + // ensure that `z` is physical + if ((z_min >= 1) || (z_min >= z_max)) { + return {s, t, u, x1, x2, 0.0, pt_hadron, y_hadron, 0.0}; + } + + // sample `z` uniformly between the kinematic limits + double z = z_min + (z_max - z_min) * r6; + jacobian *= (z_max - z_min); + + return {s, t, u, x1, x2, z, pt_hadron, y_hadron, jacobian}; +} + +void fill_grid(pineappl_grid* grid, std::size_t calls) { + using std::acosh; + using std::fabs; + using std::log; + using std::sqrt; + + auto rng = std::mt19937(); + double hbarc2 = 389379372.1; + + // define hadron kinematic ranges + double pt_min = 5.0; // GeV + double pt_max = 100.0; // GeV + double abs_y_max = 2.4; // rapidity range + + for (std::size_t i = 0; i != calls; ++i) { + auto tmp = pspgen_pp2hadron(rng, 3000.0, 14000.0, pt_min, pt_max, abs_y_max); + auto s = tmp.s; + auto t = tmp.t; + auto u = tmp.u; + auto x1 = tmp.x1; + auto x2 = tmp.x2; + auto z = tmp.z; + auto pt_hadron = tmp.pt_hadron; + auto y_hadron = tmp.y_hadron; + auto jacobian = tmp.jacobian; + + // skip if kinematically forbidden + if (jacobian == 0.0 || z <= 0.0) { + continue; + } + + // apply cuts on hadron kinematics + if ((pt_hadron < pt_min) || (pt_hadron > pt_max) || (fabs(y_hadron) > abs_y_max)) { + continue; + } + + jacobian *= hbarc2 / calls; + + // calculate the partonic cross-section + auto weight = jacobian * me_gg2qqbar(s, t, u); + + double q2 = pt_hadron * pt_hadron; + std::size_t order = 0; + std::size_t channel = 0; + + // define the tuple of kinematic variables `ntuples = (q2, x1, x2, z)` + std::vector ntuples = {q2, x1, x2, z}; + + // Fill the grid using hadron `pT` as the observable + pineappl_grid_fill2(grid, order, pt_hadron, channel, ntuples.data(), weight); + } +} + +int main() { + // --- + // Define the partonic channels and orders that will be filled into the grid + + // specify the number of convolutions: 2 for initial-state PDFs + 1 for FFs + std::size_t nb_convolutions = 3; + + // instantiate the channel object + auto* channels = pineappl_channels_new(nb_convolutions); + + // specify the contributing channel(s) and the corresponding factor(s) + // for the process `gg -> qqbar` we need to sum over the light quarks + std::vector pids; + std::vector factors; + for (int i = -3; i <= 3; ++i) { + if (i == 0) continue; + pids.insert(pids.end(), {21, 21, i}); + factors.push_back(1.0); + } + pineappl_channels_add(channels, pids.size() / nb_convolutions, pids.data(), + factors.data()); + + // specify the perturbative orders that will be filled into the grid + // orders specifies the power of the tuple `orders = (αs, α, lR, lF, lD)` + // in this example, we only fill the LO QCD + std::vector orders = {1, 0, 0, 0, 0}; + + // bin limits of the final-state hadron transverse momentum + std::vector bins = { + 5.0, 7.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, + 60.0, 70.0, 80.0, 90.0, 100.0 + }; + + // --- + // Construct the objects that are needed to fill the grid + + // choose the Evolution Basis to represent the grid + pineappl_pid_basis pid_basis = PINEAPPL_PID_BASIS_EVOL; + // define the types of hadrons and set them to be Unpolarised + std::vector convs = { + {PINEAPPL_CONV_TYPE_UNPOL_PDF, 2212}, // proton + {PINEAPPL_CONV_TYPE_UNPOL_PDF, 2212}, // proton + {PINEAPPL_CONV_TYPE_UNPOL_FF, 211}, // pion + }; + + // define the kinematics object `kinematics = (μ, x1, x2, x)` + pineappl_kinematics scales = {PINEAPPL_KINEMATICS_SCALE, 0}; + pineappl_kinematics x1 = {PINEAPPL_KINEMATICS_X, 0}; + pineappl_kinematics x2 = {PINEAPPL_KINEMATICS_X, 1}; + pineappl_kinematics z = {PINEAPPL_KINEMATICS_X, 2}; + std::vector kinematics = {scales, x1, x2, z}; + + // define the specificities of the interpolations `interpolations = (μ, x1, x2, z)` + pineappl_reweight_meth scales_reweight = PINEAPPL_REWEIGHT_METH_NO_REWEIGHT; + pineappl_reweight_meth moment_reweight = PINEAPPL_REWEIGHT_METH_APPL_GRID_X; + pineappl_map scales_mapping = PINEAPPL_MAP_APPL_GRID_H0; + pineappl_map moment_mapping = PINEAPPL_MAP_APPL_GRID_F2; + pineappl_interp_meth interpolation_meth = PINEAPPL_INTERP_METH_LAGRANGE; + std::vector interpolations = { + {1e2, 1e8, 40, 3, scales_reweight, scales_mapping, interpolation_meth}, // μ + {2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth}, // x1 + {2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth}, // x2 + {2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth}, // z + }; + + // define the values of the unphysical scales `mu_scales = (μR, μF, μD)` + // where here we do not consider the fragmentation scale μD + pineappl_scale_func_form scale_mu = {PINEAPPL_SCALE_FUNC_FORM_SCALE, 0}; + std::vector mu_scales = {scale_mu, scale_mu, scale_mu}; + + // --- + // Create the grid, fill it with Monte Carlo weights, and dump into disk + + auto* grid = pineappl_grid_new2(bins.size() - 1, bins.data(), orders.size() / 5, + orders.data(), channels, pid_basis, convs.data(), interpolations.size(), + interpolations.data(), kinematics.data(), mu_scales.data()); + + // delete no longer needed channel object + pineappl_channels_delete(channels); + + // fill the grid with phase-space points + fill_grid(grid, 100000); + + // add some metadata to the grid + pineappl_grid_set_key_value(grid, "x1_label", "pT"); + pineappl_grid_set_key_value(grid, "y_label", "dsig/dpT"); + pineappl_grid_set_key_value(grid, "x1_unit", "GeV"); + pineappl_grid_set_key_value(grid, "y_unit", "pb/GeV"); + + // write the grid into disk + pineappl_grid_write(grid, "pp2hadron-pt.pineappl.lz4"); + + // remove grid object from memory + pineappl_grid_delete(grid); +} diff --git a/examples/python/fill-grid-pp2hadron.py b/examples/python/fill-grid-pp2hadron.py new file mode 100644 index 000000000..bc8bddb50 --- /dev/null +++ b/examples/python/fill-grid-pp2hadron.py @@ -0,0 +1,263 @@ +"""Example used in the PineAPPL v1 paper.""" + +import math +import numpy as np + +from dataclasses import dataclass +from numpy.random import Generator, PCG64 +from pineappl.boc import ( + BinsWithFillLimits, + Channel, + Kinematics, + ScaleFuncForm, + Scales, + Order, +) +from pineappl.convolutions import Conv, ConvType +from pineappl.grid import Grid +from pineappl.interpolation import ( + Interp, + InterpolationMethod, + MappingMethod, + ReweightingMethod, +) +from pineappl.pids import PidBasis + + +RNG = Generator(PCG64()) + + +@dataclass +class Psp2to2Hadron: + s: np.ndarray # Mandelstam variable s + t: np.ndarray # Mandelstam variable s + u: np.ndarray # Mandelstam variable s + x1: np.ndarray # momemtum fraction of the 1st initial-state hadron + x2: np.ndarray # momemtum fraction of the 2nd initial-state hadron + z: np.ndarray # momentum fraction of the final-state hadron + pth: np.ndarray # transverse momentum of the final-state hadron + yh: np.ndarray # rapidity of the final-state hadron + jc: np.ndarray # jacobian factor + + +def create_grid(pt_bins: np.ndarray) -> Grid: + """Instantiate the Grid object for the single-inclusive pion production in pp.""" + # define the sub-partonic channels for gg -> qqbar + sub_channels = [([21, 21, pid], 1.0) for pid in range(-3, 4) if pid != 0] + channels = [Channel(sub_channels)] # construct the channel object + + # define the perturbative order that will be filled into the grid + # orders specifies the power of the tuple `orders = (αs, α, lR, lF, lD)` + # in this example, we only fill the LO QCD + orders = [Order(1, 0, 0, 0, 0)] + + # define the convolution object: 2 initial-state protons + 1 final-state pion + proton_conv = Conv( + convolution_types=ConvType(polarized=False, time_like=False), pid=2212 + ) + pion_conv = Conv( + convolution_types=ConvType(polarized=False, time_like=True), pid=211 + ) + + # define the kinematics object: (mu, x1, x2, z) + kinematics = [ + Kinematics.Scale(0), + Kinematics.X(0), + Kinematics.X(1), + Kinematics.X(2), + ] + + # define the specifities of the interpolations corresponding to the kinematics + scale_interp = Interp( + min=1e2, + max=1e8, + nodes=40, + order=3, + reweight_meth=ReweightingMethod.NoReweight, + map=MappingMethod.ApplGridH0, + interpolation_meth=InterpolationMethod.Lagrange, + ) # interpolation on the scale + x_interp = Interp( + min=2e-7, + max=1.0, + nodes=50, + order=3, + reweight_meth=ReweightingMethod.ApplGridX, + map=MappingMethod.ApplGridF2, + interpolation_meth=InterpolationMethod.Lagrange, + ) # interpolation on the momentum fraction x + interpolations = [scale_interp, x_interp, x_interp, x_interp] + + # Construc the scale object for the (muR, muF, muD) scales + scale_funcs = Scales( + ren=ScaleFuncForm.Scale(0), + fac=ScaleFuncForm.Scale(0), + frg=ScaleFuncForm.Scale(0), + ) + + # construct the bin object + bin_limits = BinsWithFillLimits.from_fill_limits(fill_limits=pt_bins) + + return Grid( + pid_basis=PidBasis.Evol, # use the Evolution basis to represent the grid + channels=channels, + orders=orders, + bins=bin_limits, + convolutions=[proton_conv, proton_conv, pion_conv], + interpolations=interpolations, + kinematics=kinematics, + scale_funcs=scale_funcs, + ) + + +def me_gg2qqbar(_s: np.ndarray, t: np.ndarray, u: np.ndarray) -> np.ndarray: + """Calculate the matrix element squared for the gg -> qqbar channel.""" + as2 = 0.118 * 0.118 + PI2 = math.pi * math.pi + # TODO: double-check + return (16 * PI2 * as2 / 6.0) * (np.power(u, 2) + np.power(t, 2)) / (u * t) + + +def psgen_pp2hadron( + n_calls: int, + mmin: float, + mmax: float, + pt_min: float, + pt_max: float, + abs_y_max: float, +) -> Psp2to2Hadron: + """Generate the kinematics for the entire phase-space.""" + + smin = mmin * mmin + smax = mmax * mmax + + r1: np.ndarray = RNG.uniform(0, 1, size=n_calls) + r2: np.ndarray = RNG.uniform(0, 1, size=n_calls) + r3: np.ndarray = RNG.uniform(0, 1, size=n_calls) + r4: np.ndarray = RNG.uniform(0, 1, size=n_calls) + r5: np.ndarray = RNG.uniform(0, 1, size=n_calls) + r6: np.ndarray = RNG.uniform(0, 1, size=n_calls) + + tau0 = smin / smax + tau = np.power(tau0, r1) + y = np.power(tau, 1.0 - r2) + x1 = y + x2 = tau / y + s = tau * smax + + jacobian = tau * np.log(tau0) * np.log(tau0) * r1 + + # `theta` integration + cos_theta = 2.0 * r3 - 1.0 + jacobian *= 2.0 + + t = -0.5 * s * (1.0 - cos_theta) + u = -0.5 * s * (1.0 + cos_theta) + + # `phi` integration + jacobian *= 2.0 * math.acos(-1.0) + + # sample hadron `pT` uniformly in log scale + log_pt_min = math.log(pt_min) + log_pt_max = math.log(pt_max) + + pt_hadron = np.exp(log_pt_min + (log_pt_max - log_pt_min) * r4) + jacobian *= pt_hadron * (log_pt_max - log_pt_min) + + # sample hadron rapidity uniformly + y_hadron = 2.0 * abs_y_max * r5 - abs_y_max + jacobian *= 2.0 * abs_y_max + + # define the momentum fracion `z` + z_min = pt_hadron * np.exp(-y_hadron) / np.sqrt(s) + z_max_kin = pt_hadron * np.exp(y_hadron) / np.sqrt(s) + z_max = np.minimum(z_max_kin, 1.0) + + # ensure that `z` is physical - set unphysical values to zero + check_kin1: np.ndarray = z_min >= 1 + check_kin2: np.ndarray = z_min >= z_max + z_min[check_kin1] = 0.0 + z_min[check_kin2] = 0.0 + jacobian[check_kin1] = 0.0 + jacobian[check_kin2] = 0.0 + + # sample `z` uniformly between the kinematic limits + z = z_min + (z_max - z_min) * r6 + jacobian *= z_max - z_min + + return Psp2to2Hadron( + s=s, t=t, u=u, x1=x1, x2=x2, z=z, pth=pt_hadron, yh=y_hadron, jc=jacobian + ) + + +def fill_grid(grid: Grid, n_calls: int, pt_bins: list) -> Grid: + """Fill the Grid with the phase-space points.""" + hbarc2 = 389379372.1 + abs_y_max = 2.4 # rapidity cut + + # compute the phase-space kinematics + ps = psgen_pp2hadron(n_calls, 3e3, 14e3, pt_bins[0], pt_bins[-1], abs_y_max) + + # calculate the partonic cross-section + jacobian = ps.jc * (hbarc2 / n_calls) + weight = jacobian * me_gg2qqbar(ps.s, ps.t, ps.u) + + # pass the kinematics as a list of n-tuples + scale = ps.pth * ps.pth + kinematics_ntuples = [ + np.array([mu, x1, x2, z]) for mu, x1, x2, z in zip(scale, ps.x1, ps.x2, ps.z) + ] + + # fill the grid by passing **at once** the weights and observables for a given order and channel + for pto_idx in range(len(grid.orders())): + for channel_idx in range(len(grid.channels())): + grid.fill_array( + order=pto_idx, + observables=ps.pth, + channel=channel_idx, + ntuples=kinematics_ntuples, + weights=weight, + ) + + return grid + + +def main() -> None: + pt_bins = [ + 5.0, + 7.0, + 10.0, + 15.0, + 20.0, + 25.0, + 30.0, + 35.0, + 40.0, + 45.0, + 50.0, + 60.0, + 70.0, + 80.0, + 90.0, + 100.0, + ] + n_calls = 100000 + grid_instance = create_grid(pt_bins=np.array(pt_bins)) + + # fill the grid + grid = fill_grid(grid=grid_instance, n_calls=n_calls, pt_bins=pt_bins) + + # add some metadata and write the Grid onto disk + grid.set_metadata("x1_label", "pT") + grid.set_metadata("y_label", "dsig/dpT") + grid.set_metadata("x1_unit", "GeV") + grid.set_metadata("y_unit", "pb/GeV") + + # write the Grid onto disk + grid.write_lz4("pp2hadron-pt-py.pineappl.lz4") + + return + + +if __name__ == "__main__": + main() diff --git a/pineappl_cli/src/helpers.rs b/pineappl_cli/src/helpers.rs index 0d85c55c2..542f44d53 100644 --- a/pineappl_cli/src/helpers.rs +++ b/pineappl_cli/src/helpers.rs @@ -171,6 +171,32 @@ const SCALES_VECTOR_REN_FRG: [(f64, f64, f64); 9] = [ (0.5, 1.0, 2.0), ]; +const SCALES_VECTOR_FAC_FRG: [(f64, f64, f64); 7] = [ + (1.0, 1.0, 1.0), + (1.0, 2.0, 2.0), + (2.0, 2.0, 2.0), + (2.0, 1.0, 1.0), + (1.0, 0.5, 0.5), + (0.5, 0.5, 0.5), + (0.5, 1.0, 1.0), +]; + +const SCALES_VECTOR_13: [(f64, f64, f64); 13] = [ + (1.0, 1.0, 1.0), + (0.5, 0.5, 1.0), + (0.5, 1.0, 0.5), + (0.5, 1.0, 1.0), + (1.0, 0.5, 0.5), + (1.0, 0.5, 1.0), + (1.0, 1.0, 0.5), + (1.0, 1.0, 2.0), + (1.0, 2.0, 1.0), + (1.0, 2.0, 2.0), + (2.0, 1.0, 1.0), + (2.0, 1.0, 2.0), + (2.0, 2.0, 1.0), +]; + const SCALES_VECTOR_27: [(f64, f64, f64); 27] = [ (1.0, 1.0, 1.0), (2.0, 2.0, 2.0), @@ -367,6 +393,8 @@ pub fn scales_vector(grid: &Grid, scales: usize) -> &[(f64, f64, f64)] { (_, ScaleFuncForm::NoScale, 9) => &SCALES_VECTOR_REN_FAC[..], (ScaleFuncForm::NoScale, _, 7) => &SCALES_VECTOR_REN_FRG[0..7], (ScaleFuncForm::NoScale, _, 9) => &SCALES_VECTOR_REN_FRG[..], + (_, _, 7) => &SCALES_VECTOR_FAC_FRG[..], + (_, _, 13) => &SCALES_VECTOR_13[..], (_, _, 17) => &SCALES_VECTOR_27[0..17], (_, _, 27) => &SCALES_VECTOR_27[..], _ => unreachable!(), diff --git a/pineappl_cli/src/uncert.rs b/pineappl_cli/src/uncert.rs index 33b795a49..d6e6480f0 100644 --- a/pineappl_cli/src/uncert.rs +++ b/pineappl_cli/src/uncert.rs @@ -30,7 +30,7 @@ struct Group { long, require_equals = true, value_name = "SCALES", - value_parser = PossibleValuesParser::new(["3", "7", "9", "17", "27"]).try_map(|s| s.parse::()) + value_parser = PossibleValuesParser::new(["3", "7", "9", "13", "17", "27"]).try_map(|s| s.parse::()) )] scale_abs: Option, /// Calculate scale uncertainties using the covariance method. @@ -40,7 +40,7 @@ struct Group { long, require_equals = true, value_name = "SCALES", - value_parser = PossibleValuesParser::new(["3", "7", "9", "17", "27"]).try_map(|s| s.parse::()) + value_parser = PossibleValuesParser::new(["3", "7", "9", "13", "17", "27"]).try_map(|s| s.parse::()) )] scale_cov: Option, /// Calculate the envelope of results where renormalization, factorization and fragmentation scales are varied. @@ -50,7 +50,7 @@ struct Group { long, require_equals = true, value_name = "SCALES", - value_parser = PossibleValuesParser::new(["3", "7", "9", "17", "27"]).try_map(|s| s.parse::()) + value_parser = PossibleValuesParser::new(["3", "7", "9", "13", "17", "27"]).try_map(|s| s.parse::()) )] scale_env: Option, } diff --git a/pineappl_cli/tests/uncert.rs b/pineappl_cli/tests/uncert.rs index b0bfcd630..7700833c6 100644 --- a/pineappl_cli/tests/uncert.rs +++ b/pineappl_cli/tests/uncert.rs @@ -14,9 +14,9 @@ Arguments: Options: --conv-fun[=] Calculate convolution function uncertainties - --scale-abs[=] Show absolute numbers of the scale-varied results [possible values: 3, 7, 9, 17, 27] - --scale-cov[=] Calculate scale uncertainties using the covariance method [possible values: 3, 7, 9, 17, 27] - --scale-env[=] Calculate the envelope of results where renormalization, factorization and fragmentation scales are varied [possible values: 3, 7, 9, 17, 27] + --scale-abs[=] Show absolute numbers of the scale-varied results [possible values: 3, 7, 9, 13, 17, 27] + --scale-cov[=] Calculate scale uncertainties using the covariance method [possible values: 3, 7, 9, 13, 17, 27] + --scale-env[=] Calculate the envelope of results where renormalization, factorization and fragmentation scales are varied [possible values: 3, 7, 9, 13, 17, 27] --cl Confidence level in per cent, for convolution function uncertainties [default: 68.26894921370858] -i, --integrated Show integrated numbers (without bin widths) instead of differential ones -o, --orders Select orders manually @@ -123,6 +123,20 @@ const SCALE_ABS_9_STR: &str = 7 4 4.5 2.7517266e1 2.7517266e1 2.7787333e1 2.7211003e1 2.7002241e1 2.8306905e1 2.8157972e1 2.6562471e1 2.6041156e1 2.8953268e1 "; +const SCALE_ABS_13_STR: &str = "b etal dsig/detal 1,1,1 0.5,0.5,1 0.5,1,0.5 0.5,1,1 1,0.5,0.5 1,0.5,1 1,1,0.5 1,1,2 1,2,1 1,2,2 2,1,1 2,1,2 2,2,1 + [] [pb] (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) + [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] +-+----+----+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +0 2 2.25 7.5459110e2 7.5459110e2 7.4296019e2 7.6796494e2 7.6796494e2 7.2595107e2 7.2595107e2 7.5459110e2 7.5459110e2 7.7529764e2 7.7529764e2 7.4384068e2 7.4384068e2 7.6745431e2 +1 2.25 2.5 6.9028342e2 6.9028342e2 6.7923774e2 7.0235002e2 7.0235002e2 6.6417441e2 6.6417441e2 6.9028342e2 6.9028342e2 7.0957480e2 7.0957480e2 6.8058382e2 6.8058382e2 7.0221920e2 +2 2.5 2.75 6.0025198e2 6.0025198e2 5.9046454e2 6.1100712e2 6.1100712e2 5.7747295e2 5.7747295e2 6.0025198e2 6.0025198e2 6.1750966e2 6.1750966e2 5.9160658e2 5.9160658e2 6.1056383e2 +3 2.75 3 4.8552235e2 4.8552235e2 4.7761552e2 4.9437007e2 4.9437007e2 4.6723687e2 4.6723687e2 4.8552235e2 4.8552235e2 4.9966237e2 4.9966237e2 4.7841022e2 4.7841022e2 4.9366919e2 +4 3 3.25 3.6195456e2 3.6195456e2 3.5611822e2 3.6870561e2 3.6870561e2 3.4843600e2 3.4843600e2 3.6195456e2 3.6195456e2 3.7261436e2 3.7261436e2 3.5652780e2 3.5652780e2 3.6783089e2 +5 3.25 3.5 2.4586691e2 2.4586691e2 2.4198770e2 2.5059003e2 2.5059003e2 2.3677625e2 2.3677625e2 2.4586691e2 2.4586691e2 2.5316566e2 2.5316566e2 2.4207028e2 2.4207028e2 2.4967698e2 +6 3.5 4 1.1586851e2 1.1586851e2 1.1418227e2 1.1824058e2 1.1824058e2 1.1166942e2 1.1166942e2 1.1586851e2 1.1586851e2 1.1930157e2 1.1930157e2 1.1396174e2 1.1396174e2 1.1746280e2 +7 4 4.5 2.7517266e1 2.7517266e1 2.7211003e1 2.8157972e1 2.8157972e1 2.6562471e1 2.6562471e1 2.7517266e1 2.7517266e1 2.8306905e1 2.8306905e1 2.7002241e1 2.7002241e1 2.7787333e1 +"; + const SCALE_ABS_17_STR: &str = "b etal dsig/detal 1,1,1 2,2,2 0.5,0.5,0.5 0.5,0.5,1 0.5,1,0.5 0.5,1,1 0.5,1,2 1,0.5,0.5 1,0.5,1 1,1,0.5 1,1,2 1,2,1 1,2,2 2,1,0.5 2,1,1 2,1,2 2,2,1 [] [pb] (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) (r,f,a) [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] [pb] @@ -348,6 +362,21 @@ fn scale_abs_9() { .stdout(SCALE_ABS_9_STR); } +#[test] +fn scale_abs_13() { + Command::cargo_bin("pineappl") + .unwrap() + .args([ + "uncert", + "--scale-abs=13", + "../test-data/LHCB_WP_7TEV_opt.pineappl.lz4", + "NNPDF31_nlo_as_0118_luxqed", + ]) + .assert() + .success() + .stdout(SCALE_ABS_13_STR); +} + #[test] fn scale_abs_17() { Command::cargo_bin("pineappl")