Skip to content

Commit 16145b6

Browse files
Implement the backend logic for PDF interpolation with LHAPDF and NeoPDF
1 parent 41e8af5 commit 16145b6

8 files changed

Lines changed: 1871 additions & 118 deletions

File tree

Cargo.lock

Lines changed: 1181 additions & 109 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pineappl_cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ git-version = "0.3.5"
2828
itertools = "0.10.1"
2929
lhapdf = { package = "managed-lhapdf", version = "0.3.4" }
3030
lz4_flex = { optional = true, version = "0.9.2" }
31+
neopdf = "=0.3.0-alpha2"
3132
ndarray = "0.15.4"
3233
ndarray-npy = { default-features = false, features = ["npz"], optional = true, version = "0.8.1" }
3334
pineappl = { path = "../pineappl", version = "=1.3.0" }

pineappl_cli/src/convolve.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ pub struct Opts {
5858
impl Subcommand for Opts {
5959
fn run(&self, cfg: &GlobalConfiguration) -> Result<ExitCode> {
6060
let grid = helpers::read_grid(&self.input)?;
61-
let mut conv_funs_0 = helpers::create_conv_funs(&self.conv_funs[0])?;
61+
let mut conv_funs_0 =
62+
helpers::create_conv_funs_with_backend(&self.conv_funs[0], cfg.pdf_backend)?;
6263
let bins: Vec<_> = self.bins.iter().cloned().flatten().collect();
6364

64-
let results = helpers::convolve_scales(
65+
let results = helpers::convolve_scales_with_backend(
6566
&grid,
6667
&mut conv_funs_0,
6768
&self.conv_funs[0].conv_types,
@@ -91,8 +92,9 @@ impl Subcommand for Opts {
9192
.iter()
9293
.flat_map(|conv_funs| {
9394
let conv_types = &conv_funs.conv_types;
94-
let mut conv_funs = helpers::create_conv_funs(conv_funs).unwrap();
95-
helpers::convolve(
95+
let mut conv_funs =
96+
helpers::create_conv_funs_with_backend(conv_funs, cfg.pdf_backend).unwrap();
97+
helpers::convolve_with_backend(
9698
&grid,
9799
&mut conv_funs,
98100
conv_types,

pineappl_cli/src/helpers.rs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::pdf_backend::{self, Backend, ForcePositive, PdfBackend, PdfSetBackend};
12
use super::GlobalConfiguration;
23
use anyhow::{anyhow, bail, Context, Error, Result};
34
use itertools::Itertools;
@@ -64,6 +65,7 @@ impl FromStr for ConvFuns {
6465
}
6566
}
6667

68+
/// Creates convolution functions using LHAPDF backend (legacy).
6769
pub fn create_conv_funs(funs: &ConvFuns) -> Result<Vec<Pdf>> {
6870
Ok(funs
6971
.lhapdf_names
@@ -82,6 +84,22 @@ pub fn create_conv_funs(funs: &ConvFuns) -> Result<Vec<Pdf>> {
8284
.collect::<Result<_, _>>()?)
8385
}
8486

87+
/// Creates convolution functions using the specified backend.
88+
pub fn create_conv_funs_with_backend(
89+
funs: &ConvFuns,
90+
backend: Backend,
91+
) -> Result<Vec<Box<dyn PdfBackend>>> {
92+
funs.lhapdf_names
93+
.iter()
94+
.zip(&funs.members)
95+
.map(|(name, member)| {
96+
let member = member.unwrap_or(0);
97+
pdf_backend::create_pdf(name, member, backend)
98+
})
99+
.collect()
100+
}
101+
102+
/// Creates convolution functions for a PDF set using LHAPDF backend (legacy).
85103
pub fn create_conv_funs_for_set(
86104
funs: &ConvFuns,
87105
index_of_set: usize,
@@ -115,6 +133,30 @@ pub fn create_conv_funs_for_set(
115133
Ok((set, conv_funs))
116134
}
117135

136+
/// Creates convolution functions for a PDF set using the specified backend.
137+
///
138+
/// Returns a tuple of (PdfSetBackend, Vec<Vec<Box<dyn PdfBackend>>>).
139+
pub fn create_conv_funs_for_set_with_backend(
140+
funs: &ConvFuns,
141+
index_of_set: usize,
142+
backend: Backend,
143+
) -> Result<(Box<dyn PdfSetBackend>, Vec<Vec<Box<dyn PdfBackend>>>)> {
144+
let setname = &funs.lhapdf_names[index_of_set];
145+
let set = pdf_backend::create_pdf_set(setname, backend)?;
146+
147+
let set_members = set.mk_pdfs()?;
148+
let conv_funs = set_members
149+
.into_iter()
150+
.map(|member_pdf| {
151+
let mut conv_funs = create_conv_funs_with_backend(funs, backend)?;
152+
conv_funs[index_of_set] = member_pdf;
153+
Ok::<_, Error>(conv_funs)
154+
})
155+
.collect::<Result<_, _>>()?;
156+
157+
Ok((set, conv_funs))
158+
}
159+
118160
pub fn read_grid(input: &Path) -> Result<Grid> {
119161
Grid::read(File::open(input).context(format!("unable to open '{}'", input.display()))?)
120162
.context(format!("unable to read '{}'", input.display()))
@@ -238,6 +280,7 @@ pub enum ConvoluteMode {
238280
Normal,
239281
}
240282

283+
/// Performs convolution with scale variations using LHAPDF backend (legacy).
241284
pub fn convolve_scales(
242285
grid: &Grid,
243286
conv_funs: &mut [Pdf],
@@ -357,6 +400,131 @@ pub fn convolve_scales(
357400
}
358401
}
359402

403+
/// Performs convolution with scale variations using the backend abstraction.
404+
#[allow(clippy::too_many_arguments)]
405+
pub fn convolve_scales_with_backend(
406+
grid: &Grid,
407+
conv_funs: &mut [Box<dyn PdfBackend>],
408+
conv_types: &[ConvType],
409+
orders: &[(u8, u8)],
410+
bins: &[usize],
411+
channels: &[bool],
412+
scales: &[(f64, f64, f64)],
413+
mode: ConvoluteMode,
414+
cfg: &GlobalConfiguration,
415+
) -> Vec<f64> {
416+
let orders: Vec<_> = grid
417+
.orders()
418+
.iter()
419+
.map(|order| {
420+
orders.is_empty()
421+
|| orders
422+
.iter()
423+
.any(|other| (order.alphas == other.0) && (order.alpha == other.1))
424+
})
425+
.collect();
426+
427+
if cfg.force_positive {
428+
for fun in conv_funs.iter_mut() {
429+
fun.set_force_positive(ForcePositive::ClipNegative);
430+
}
431+
}
432+
433+
// TODO: promote this to an error
434+
assert!(
435+
cfg.use_alphas_from < conv_funs.len(),
436+
"expected `use_alphas_from` to be an integer within `[0, {})`, but got `{}`",
437+
conv_funs.len(),
438+
cfg.use_alphas_from
439+
);
440+
441+
// Get x_min/x_max before creating closures (requires mut)
442+
let x_min_max: Vec<_> = conv_funs
443+
.iter_mut()
444+
.map(|fun| (fun.x_min(), fun.x_max()))
445+
.collect();
446+
447+
// Create closures for xfx evaluation
448+
let mut funs: Vec<_> = conv_funs
449+
.iter()
450+
.zip(&x_min_max)
451+
.map(|(fun, &(x_min, x_max))| {
452+
move |id: i32, x: f64, q2: f64| {
453+
if !cfg.allow_extrapolation && (x < x_min || x > x_max) {
454+
0.0
455+
} else {
456+
fun.xfx_q2(id, x, q2)
457+
}
458+
}
459+
})
460+
.collect();
461+
462+
let xfx: Vec<_> = funs
463+
.iter_mut()
464+
.map(|fun| fun as &mut dyn FnMut(i32, f64, f64) -> f64)
465+
.collect();
466+
467+
// Create alphas closures
468+
let mut alphas_funs: Vec<_> = conv_funs
469+
.iter()
470+
.map(|fun| {
471+
let fun_ref = fun.as_ref();
472+
move |q2: f64| fun_ref.alphas_q2(q2)
473+
})
474+
.collect();
475+
476+
// Get particle IDs from the PDFs using the backend interface
477+
let convolutions: Vec<_> = conv_funs
478+
.iter()
479+
.zip(conv_types)
480+
.map(|(fun, &conv_type)| {
481+
let pid = fun.particle_id();
482+
Conv::new(conv_type, pid)
483+
})
484+
.collect();
485+
486+
let mut cache = ConvolutionCache::new(convolutions, xfx, &mut alphas_funs[cfg.use_alphas_from]);
487+
let mut results = grid.convolve(&mut cache, &orders, bins, channels, scales);
488+
489+
match mode {
490+
ConvoluteMode::Asymmetry => {
491+
let bin_count = grid.bwfl().len();
492+
493+
// calculating the asymmetry for a subset of bins doesn't work
494+
assert!((bins.is_empty() || (bins.len() == bin_count)) && (bin_count % 2 == 0));
495+
496+
results
497+
.iter()
498+
.skip((bin_count / 2) * scales.len())
499+
.zip(
500+
results
501+
.chunks_exact(scales.len())
502+
.take(bin_count / 2)
503+
.rev()
504+
.flatten(),
505+
)
506+
.map(|(pos, neg)| (pos - neg) / (pos + neg))
507+
.collect()
508+
}
509+
ConvoluteMode::Integrated => {
510+
results
511+
.iter_mut()
512+
.zip(
513+
grid.bwfl()
514+
.normalizations()
515+
.into_iter()
516+
.enumerate()
517+
.filter(|(index, _)| bins.is_empty() || bins.contains(index))
518+
.flat_map(|(_, norm)| iter::repeat(norm).take(scales.len())),
519+
)
520+
.for_each(|(value, norm)| *value *= norm);
521+
522+
results
523+
}
524+
ConvoluteMode::Normal => results,
525+
}
526+
}
527+
360528
pub fn scales_vector(grid: &Grid, scales: usize) -> &[(f64, f64, f64)] {
361529
let Scales { fac, frg, .. } = grid.scales();
362530

@@ -373,6 +541,7 @@ pub fn scales_vector(grid: &Grid, scales: usize) -> &[(f64, f64, f64)] {
373541
}
374542
}
375543

544+
/// Performs convolution using LHAPDF backend (legacy).
376545
pub fn convolve(
377546
grid: &Grid,
378547
conv_funs: &mut [Pdf],
@@ -397,6 +566,32 @@ pub fn convolve(
397566
)
398567
}
399568

569+
/// Performs convolution using the backend abstraction.
570+
#[allow(clippy::too_many_arguments)]
571+
pub fn convolve_with_backend(
572+
grid: &Grid,
573+
conv_funs: &mut [Box<dyn PdfBackend>],
574+
conv_types: &[ConvType],
575+
orders: &[(u8, u8)],
576+
bins: &[usize],
577+
lumis: &[bool],
578+
scales: usize,
579+
mode: ConvoluteMode,
580+
cfg: &GlobalConfiguration,
581+
) -> Vec<f64> {
582+
convolve_scales_with_backend(
583+
grid,
584+
conv_funs,
585+
conv_types,
586+
orders,
587+
bins,
588+
lumis,
589+
scales_vector(grid, scales),
590+
mode,
591+
cfg,
592+
)
593+
}
594+
400595
pub fn convolve_limits(grid: &Grid, bins: &[usize], mode: ConvoluteMode) -> Vec<Vec<(f64, f64)>> {
401596
let limits: Vec<_> = grid
402597
.bwfl()

pineappl_cli/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod helpers;
1414
mod import;
1515
mod merge;
1616
mod orders;
17+
pub mod pdf_backend;
1718
mod plot;
1819
mod pull;
1920
mod read;
@@ -41,6 +42,9 @@ pub struct GlobalConfiguration {
4142
/// Choose the PDF/FF set for the strong coupling.
4243
#[arg(default_value = "0", long, value_name = "IDX")]
4344
pub use_alphas_from: usize,
45+
/// Select the PDF interpolation backend: 'lhapdf' or 'neopdf'.
46+
#[arg(default_value = "lhapdf", long, value_name = "BACKEND")]
47+
pub pdf_backend: pdf_backend::Backend,
4448
}
4549

4650
#[enum_dispatch]

0 commit comments

Comments
 (0)