diff --git a/src/pineko/check.py b/src/pineko/check.py index 5a93ae79..68365ae1 100644 --- a/src/pineko/check.py +++ b/src/pineko/check.py @@ -65,3 +65,33 @@ def check_grid_and_eko_compatible(pineappl_grid, operators, xif): # x-grid if not np.all(in1d(np.unique(operators["targetgrid"]), np.array(x_grid))): raise ValueError("x grid in pineappl grid and eko operator are NOT compatible!") + + +def contains_fact(grid): + """Check whether factorization scale-variations are available in the pineappl grid. + + Parameters + ---------- + grid: pineappl.grid.Grid + Pineappl grid + """ + order_list = [order.as_tuple() for order in grid.orders()] + for order in order_list: + if order[-1] != 0: + return + raise ValueError("Factorization scale variations are not available for this grid") + + +def contains_ren(grid): + """Check whether renormalization scale-variations are available in the pineappl grid. + + Parameters + ---------- + grid: pineappl.grid.Grid + Pineappl grid + """ + order_list = [order.as_tuple() for order in grid.orders()] + for order in order_list: + if order[-2] != 0: + return + raise ValueError("Renormalization scale variations are not available for this grid") diff --git a/src/pineko/cli/__init__.py b/src/pineko/cli/__init__.py index 0965db72..943ede4a 100644 --- a/src/pineko/cli/__init__.py +++ b/src/pineko/cli/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- -"""CLI entry point.""" from . import check, compare, convolute, opcard, theory_ from ._base import command diff --git a/src/pineko/cli/check.py b/src/pineko/cli/check.py index 2b5e2c54..676d6804 100644 --- a/src/pineko/cli/check.py +++ b/src/pineko/cli/check.py @@ -9,22 +9,58 @@ from ._base import command -@command.command("check") -@click.argument("pineappl_path", metavar="PINEAPPL", type=click.Path(exists=True)) -@click.argument("eko_path", metavar="EKO", type=click.Path(exists=True)) +@command.group("check") +def subcommand(): + """Check grid and operator properties.""" + + +@subcommand.command("compatibility") +@click.argument("grid_path", metavar="PINEAPPL", type=click.Path(exists=True)) +@click.argument("operator_path", metavar="EKO", type=click.Path(exists=True)) @click.option("--xif", default=1.0, help="factorization scale variation") -def subcommand(pineappl_path, eko_path, xif): +def sub_compatibility(grid_path, operator_path, xif): """Check PineAPPL grid and EKO compatibility. In order to be compatible, the grid provided in PINEAPPL and the operator provided in EKO, have to expose the same x grid and Q2 grid. XIF is the factorization scale variation. + """ - pineappl_grid = pineappl.grid.Grid.read(pineappl_path) - operators = eko.output.Output.load_tar(eko_path) + pineappl_grid = pineappl.grid.Grid.read(grid_path) + operators = eko.output.Output.load_tar(operator_path) try: check.check_grid_and_eko_compatible(pineappl_grid, operators, xif) rich.print("[green]Success:[/] grids are compatible") except ValueError as e: rich.print("[red]Error:[/]", e) + + +@subcommand.command("scvar") +@click.argument("grid_path", metavar="PINEAPPL", type=click.Path(exists=True)) +@click.argument( + "scale", + metavar="SCALE", + type=click.Choice(["ren", "fact"]), +) +def sub_scvar(grid_path, scale): + """Check if PineAPPL grid contains requested scale variations.""" + grid = pineappl.grid.Grid.read(grid_path) + if scale == "ren": + try: + check.contains_ren(grid) + rich.print( + "[green]Success:[/] grids contain renormalization scale variations" + ) + except ValueError as e: + rich.print("[red]Error:[/]", e) + elif scale == "fact": + try: + check.contains_fact(grid) + rich.print( + "[green]Success:[/] grids contain factorization scale variations" + ) + except ValueError as e: + rich.print("[red]Error:[/]", e) + else: + raise ValueError("Scale variation to check can be one between xir and xif") diff --git a/src/pineko/cli/convolute.py b/src/pineko/cli/convolute.py index 5687ca35..ba6a2c70 100644 --- a/src/pineko/cli/convolute.py +++ b/src/pineko/cli/convolute.py @@ -1,14 +1,16 @@ # -*- coding: utf-8 -*- """CLI entry point to convolution.""" import click +import pineappl +import rich from .. import evolve from ._base import command @command.command("convolute") -@click.argument("pineappl", type=click.Path(exists=True)) -@click.argument("eko", type=click.Path(exists=True)) +@click.argument("grid_path", type=click.Path(exists=True)) +@click.argument("op_path", type=click.Path(exists=True)) @click.argument("fktable", type=click.Path()) @click.argument("max_as", type=int) @click.argument("max_al", type=int) @@ -23,10 +25,10 @@ help="the flavor assumptions to be used", show_default=True, ) -def subcommand(pineappl, eko, fktable, max_as, max_al, xir, xif, pdf, assumptions): +def subcommand(grid_path, op_path, fktable, max_as, max_al, xir, xif, pdf, assumptions): """Convolute PineAPPL grid and EKO into an FK table. - PINEAPPL and EKO are the path to the respective elements to convolute, and + GRID_PATH and OP_PATH are the path to the respective elements to convolute, and FKTABLE is the path where to dump the output. MAX_AS and MAX_AL are used to specify the order in QCD and QED @@ -38,9 +40,17 @@ def subcommand(pineappl, eko, fktable, max_as, max_al, xir, xif, pdf, assumption PDF is an optional PDF set compatible with the EKO to compare grid and FK table. """ + grid = pineappl.grid.Grid.read(grid_path) + rich.print( + rich.panel.Panel.fit("Computing ...", style="magenta", box=rich.box.SQUARE), + f" {grid_path}\n", + f"+ {op_path}\n", + f"= {fktable}\n", + f"with max_as={max_as}, max_al={max_al}, xir={xir}, xif={xif}", + ) _grid, _fk, comp = evolve.evolve_grid( - pineappl, - eko, + grid, + op_path, fktable, max_as, max_al, diff --git a/src/pineko/evolve.py b/src/pineko/evolve.py index 6844f042..75fbe9e1 100644 --- a/src/pineko/evolve.py +++ b/src/pineko/evolve.py @@ -77,7 +77,7 @@ def write_operator_card(pineappl_grid, default_card, card_path, xi): def evolve_grid( - pineappl_path, + grid, eko_path, fktable_path, max_as, @@ -92,7 +92,7 @@ def evolve_grid( Parameters ---------- - pineappl_path : str + grid : pineappl.grid.Grid unconvoluted grid eko_path : str evolution operator @@ -113,32 +113,23 @@ def evolve_grid( comparison_pdf : None or str if given, a comparison table (with / without evolution) will be printed """ - rich.print( - rich.panel.Panel.fit("Computing ...", style="magenta", box=rich.box.SQUARE), - f" {pineappl_path}\n", - f"+ {eko_path}\n", - f"= {fktable_path}\n", - f"with max_as={max_as}, max_al={max_al}, xir={xir}, xif={xif}", - ) - # load - pineappl_grid = pineappl.grid.Grid.read(str(pineappl_path)) - _x_grid, _pids, mur2_grid, _muf2_grid = pineappl_grid.axes() + _x_grid, _pids, mur2_grid, _muf2_grid = grid.axes() operators = eko.output.Output.load_tar(eko_path) - check.check_grid_and_eko_compatible(pineappl_grid, operators, xif) + check.check_grid_and_eko_compatible(grid, operators, xif) # rotate to evolution (if doable and necessary) if np.allclose(operators["inputpids"], br.flavor_basis_pids): operators.to_evol() elif not np.allclose(operators["inputpids"], br.evol_basis_pids): raise ValueError("The EKO is neither in flavor nor in evolution basis.") # do it - order_mask = pineappl.grid.Order.create_mask(pineappl_grid.orders(), max_as, max_al) + order_mask = pineappl.grid.Order.create_mask(grid.orders(), max_as, max_al) # TODO this is a hack to not break the CLI # the problem is that the EKO output still does not contain the theory/operators card and # so I can't compute alpha_s *here* if xir != 1 if np.isclose(xir, 1.0) and alphas_values is None: mur2_grid = list(operators["Q2grid"].keys()) alphas_values = [op["alphas"] for op in operators["Q2grid"].values()] - fktable = pineappl_grid.convolute_eko( + fktable = grid.convolute_eko( operators, xir * xir * mur2_grid, alphas_values, @@ -154,8 +145,8 @@ def evolve_grid( comparison = None if comparison_pdf is not None: comparison = comparator.compare( - pineappl_grid, fktable, max_as, max_al, comparison_pdf, xir, xif + grid, fktable, max_as, max_al, comparison_pdf, xir, xif ) fktable.set_key_value("results_fk", comparison.to_string()) fktable.set_key_value("results_fk_pdfset", comparison_pdf) - return pineappl_grid, fktable, comparison + return grid, fktable, comparison diff --git a/src/pineko/theory.py b/src/pineko/theory.py index efcb534f..3362fe3c 100644 --- a/src/pineko/theory.py +++ b/src/pineko/theory.py @@ -11,11 +11,12 @@ import eko import numpy as np +import pineappl import rich import yaml from eko import strong_coupling as sc -from . import configs, evolve, parser, theory_card +from . import check, configs, evolve, parser, theory_card logger = logging.getLogger(__name__) @@ -346,6 +347,15 @@ def fk(self, name, grid_path, tcard, pdf): do_log = self.activate_logging( paths["logs"]["fk"], f"{self.theory_id}-{name}-{pdf}.log" ) + # check if grid contains SV if theory is requesting them + xir = tcard["XIR"] + xif = tcard["XIF"] + ftr = tcard["fact_to_ren_scale_ratio"] + pineappl_grid = pineappl.grid.Grid.read(grid_path) + if not np.isclose(xir, 1.0): + check.contains_ren(pineappl_grid) + if not (np.isclose(xif, 1.0) and np.isclose(ftr, 1.0)): + check.contains_fact(pineappl_grid) # setup data eko_filename = self.ekos_path() / f"{name}.tar" fk_filename = self.fks_path / f"{name}.{parser.EXT}" @@ -362,8 +372,6 @@ def fk(self, name, grid_path, tcard, pdf): # q2_grid = ocard["Q2grid"] operators = eko.output.Output.load_tar(eko_filename) q2_grid = operators["Q2grid"].keys() - xir = tcard["XIR"] - xif = tcard["XIF"] # PineAPPL wants alpha_s = 4*pi*a_s alphas_values = [ 4.0 * np.pi * astrong.a_s(xir * xir * Q2 / xif / xif) for Q2 in q2_grid @@ -374,8 +382,16 @@ def fk(self, name, grid_path, tcard, pdf): logger.info("Start computation of %s", name) logger.info("max_as=%d, max_al=%d, xir=%f, xif=%f", max_as, max_al, xir, xif) start_time = time.perf_counter() + + rich.print( + rich.panel.Panel.fit("Computing ...", style="magenta", box=rich.box.SQUARE), + f" {grid_path}\n", + f"+ {eko_filename}\n", + f"= {fk_filename}\n", + f"with max_as={max_as}, max_al={max_al}, xir={xir}, xif={xif}", + ) _grid, _fk, comparison = evolve.evolve_grid( - grid_path, + pineappl_grid, eko_filename, fk_filename, max_as, diff --git a/tests/test_check.py b/tests/test_check.py new file mode 100644 index 00000000..e7374fa7 --- /dev/null +++ b/tests/test_check.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +import pathlib + +import eko +import numpy as np +import pineappl +import pytest + +import pineko.check + + +class Fake_grid: + def __init__(self, order_list): + self.orderlist = order_list + + def orders(self): + return self.orderlist + + +class Order: + def __init__(self, order_tup): + self.orders = order_tup + + def as_tuple(self): + return self.orders + + +def test_contains_fact(): + first_order = Order((0, 0, 0, 0)) + second_order = Order((1, 1, 0, 0)) + third_order = Order((0, 0, 1, 1)) + order_list = [first_order, second_order, third_order] + mygrid = Fake_grid(order_list) + assert pineko.check.contains_fact(mygrid) is None + order_list.pop(-1) + mygrid_nofact = Fake_grid(order_list) + with pytest.raises(ValueError): + pineko.check.contains_fact(mygrid_nofact) + + +def test_contains_ren(): + first_order = Order((0, 0, 0, 0)) + second_order = Order((1, 1, 0, 0)) + third_order = Order((0, 0, 1, 1)) + order_list = [first_order, second_order, third_order] + mygrid = Fake_grid(order_list) + assert pineko.check.contains_ren(mygrid) is None + order_list.pop(-1) + mygrid_nofact = Fake_grid(order_list) + with pytest.raises(ValueError): + pineko.check.contains_ren(mygrid_nofact)