Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use eko v0.15 #2181

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions n3fit/src/evolven3fit/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


def cli_evolven3fit(
configuration_folder, q_fin, q_points, op_card_info, theory_card_info, force, load, dump, ncores
configuration_folder, q_fin, q_points, op_card_info, theory_card_info, force, load, dump,
):
"""Evolves the fitted PDFs.

Expand Down Expand Up @@ -31,5 +31,4 @@ def cli_evolven3fit(
force,
load,
dump,
ncores,
)
37 changes: 29 additions & 8 deletions n3fit/src/evolven3fit/eko_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
from typing import Any, Dict, Optional

from ekobox.cards import _operator as default_op_card
import numpy as np

from eko.io import runcards
Expand Down Expand Up @@ -56,8 +55,7 @@ def construct_eko_cards(
theory["MaxNfAs"] = theory["MaxNfPdf"]

# The Legacy function is able to construct a theory card for eko starting from a NNPDF theory
legacy_class = runcards.Legacy(theory, {})
theory_card = legacy_class.new_theory
theory_card = runcards.Legacy(theory, {}).new_theory

# construct mugrid

Expand Down Expand Up @@ -129,8 +127,7 @@ def construct_eko_photon_cards(
theory["MaxNfAs"] = theory["MaxNfPdf"]

# The Legacy function is able to construct a theory card for eko starting from a NNPDF theory
legacy_class = runcards.Legacy(theory, {})
theory_card = legacy_class.new_theory
theory_card = runcards.Legacy(theory, {}).new_theory

# The photon needs to be evolved down to Q0
q_fin = theory["Q0"]
Expand Down Expand Up @@ -177,10 +174,34 @@ def build_opcard(op_card_dict, theory, x_grid, mu0, mugrid):
if op_card_dict is None:
op_card_dict = {}

op_card = default_op_card

op_card.update({"mu0": mu0, "mugrid": mugrid})
# Taken from cards.py https://github.com/NNPDF/eko/blob/master/src/ekobox/cards.py
# 7735fdb
op_card = dict(
init=(1.65, 4),
mugrid=[(100.0, 5)],
xgrid=np.geomspace(1e-7, 1.0, 50).tolist(),
configs=dict(
# These three values might be set by op_card_dict
ev_op_iterations=10,
n_integration_cores=1,
polarized=False,
#
ev_op_max_order=[10, 0],
interpolation_polynomial_degree=4,
interpolation_is_log=True,
scvar_method=None,
inversion_method=None,
evolution_method="iterate-exact",
time_like=False,
),
debug=dict(
skip_singlet=False,
skip_non_singlet=False,
),
)

op_card["init"] = (mu0, theory["nf0"])
op_card["mugrid"] = mugrid
op_card["xgrid"] = x_grid

# Specify the evolution options and defaults differently from TRN / EXA
Expand Down
125 changes: 52 additions & 73 deletions n3fit/src/evolven3fit/evolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import sys

from ekobox import apply, genpdf, info_file
from joblib import Parallel, delayed
import numpy as np
import psutil

import eko
from eko import basis_rotation, runner
from eko.interpolation import XGrid
from eko.io import manipulate
from reportengine.compat import yaml

from . import eko_utils, utils
Expand All @@ -24,19 +24,9 @@
"level": logging.DEBUG,
}

NUM_CORES = psutil.cpu_count(logical=False)


def evolve_fit(
fit_folder,
q_fin,
q_points,
op_card_dict,
theory_card_dict,
force,
eko_path,
dump_eko=None,
ncores=1,
fit_folder, q_fin, q_points, op_card_dict, theory_card_dict, force, eko_path, dump_eko=None
):
"""
Evolves all the fitted replica in fit_folder/nnfit
Expand Down Expand Up @@ -107,10 +97,6 @@ def evolve_fit(
raise ValueError(f"dump_eko not provided and {eko_path=} not found")

with eko.EKO.edit(eko_path) as eko_op:
x_grid_obj = eko.interpolation.XGrid(x_grid)
eko.io.manipulate.xgrid_reshape(eko_op, targetgrid=x_grid_obj, inputgrid=x_grid_obj)

with eko.EKO.read(eko_path) as eko_op:
# Read the cards directly from the eko to make sure they are consistent
theory = eko_op.theory_card
op = eko_op.operator_card
Expand All @@ -126,18 +112,57 @@ def evolve_fit(
info["XMax"] = float(x_grid[-1])
# Save the PIDs in the info file in the same order as in the evolution
info["Flavors"] = basis_rotation.flavor_basis_pids
info["NumFlavors"] = theory.heavy.num_flavs_max_pdf
info["NumFlavors"] = 5 # TODO: Maximum number in evol
dump_info_file(usr_path, info)

def _wrap_evolve(pdf, replica):
evolved_blocks = evolve_exportgrid(pdf, eko_op, x_grid)
dump_evolved_replica(evolved_blocks, usr_path, int(replica.removeprefix("replica_")))

# Choose the number of cores to be the Minimal value
nb_cores = min(NUM_CORES, abs(ncores))
Parallel(n_jobs=nb_cores)(
delayed(_wrap_evolve)(pdf, r) for r, pdf in initial_PDFs_dict.items()
)
# Read the information from all the sorted replicas into what eko wants
n_replicas = len(initial_PDFs_dict)
all_replicas = []
for rep_idx in range(1, n_replicas + 1):
# swap photon postion to match eko.basis_roation.flavor_basis_pids
pdfgrid = np.array(initial_PDFs_dict[f"replica_{rep_idx}"]["pdfgrid"])
pdfgrid = np.append(pdfgrid[:, -1].reshape(x_grid.size, 1), pdfgrid[:, :-1], axis=1)
# and divide by x
all_replicas.append(pdfgrid.T / x_grid)

# reshape the xgrid eko if necessary
if XGrid(x_grid) != eko_op.xgrid:
for _, elem in eko_op.items():
elem = manipulate.xgrid_reshape(
elem,
eko_op.xgrid,
op.configs.interpolation_polynomial_degree,
targetgrid=XGrid(x_grid),
inputgrid=XGrid(x_grid),
)

# output is {(Q2, nf): (replica, flavour, x)}
all_evolved, _ = apply.apply_grids(eko_op, np.array(all_replicas))

# Now, replica by replica, break into nf blocks
targetgrid = eko_op.xgrid.tolist()
by_nf = defaultdict(list)
for q2, nf in sorted(eko_op.evolgrid, key=lambda ep: ep[1]):
by_nf[nf].append(q2)
q2block_per_nf = {nf: sorted(q2s) for nf, q2s in by_nf.items()}

for replica in range(n_replicas):
blocks = []
for nf, q2grid in q2block_per_nf.items():

def pdf_xq2(pid, x, Q2):
x_idx = targetgrid.index(x)
pid_idx = info["Flavors"].index(pid)
return x * all_evolved[(Q2, nf)][replica][pid_idx][x_idx]

block = genpdf.generate_block(
pdf_xq2,
xgrid=targetgrid,
sorted_q2grid=q2grid,
pids=basis_rotation.flavor_basis_pids,
)
blocks.append(block)
dump_evolved_replica(blocks, usr_path, replica + 1)

# remove folder:
# The function dump_evolved_replica dumps the replica files in a temporary folder
Expand Down Expand Up @@ -169,52 +194,6 @@ def load_fit(usr_path):
return pdf_dict


def evolve_exportgrid(exportgrid, eko, x_grid):
"""
Evolves the provided exportgrid for the desired replica with the eko and returns the evolved block

Parameters
----------
exportgrid: dict
exportgrid of pdf at fitting scale
eko: eko object
eko operator for evolution
xgrid: list
xgrid to be used as the targetgrid
Returns
-------
: list(np.array)
list of evolved blocks
"""
# construct LhapdfLike object
pdf_grid = np.array(exportgrid["pdfgrid"]).transpose()
pdf_to_evolve = utils.LhapdfLike(pdf_grid, exportgrid["q20"], x_grid)
# evolve pdf
evolved_pdf = apply.apply_pdf(eko, pdf_to_evolve)
# generate block to dump
targetgrid = eko.bases.targetgrid.tolist()

# Finally separate by nf block (and order per nf/q)
by_nf = defaultdict(list)
for q, nf in sorted(eko.evolgrid, key=lambda ep: ep[1]):
by_nf[nf].append(q)
q2block_per_nf = {nf: sorted(qs) for nf, qs in by_nf.items()}

blocks = []
for nf, q2grid in q2block_per_nf.items():

def pdf_xq2(pid, x, Q2):
x_idx = targetgrid.index(x)
return x * evolved_pdf[(Q2, nf)]["pdfs"][pid][x_idx]

block = genpdf.generate_block(
pdf_xq2, xgrid=targetgrid, sorted_q2grid=q2grid, pids=basis_rotation.flavor_basis_pids
)
blocks.append(block)

return blocks


def dump_evolved_replica(evolved_blocks, usr_path, replica_num):
"""
Dump the evolved replica given by evolved_block as the replica num "replica_num" in
Expand Down
45 changes: 0 additions & 45 deletions n3fit/src/evolven3fit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,6 @@
from .q2grids import Q2GRID_DEFAULT, Q2GRID_NNPDF40


class LhapdfLike:
"""
Class which emulates lhapdf but only for an initial condition PDF (i.e. with only one q2 value).

Q20 is the fitting scale fo the pdf and it is the only available scale for the objects of this class.

X_GRID is the grid of x values on top of which the pdf is interpolated.

PDF_GRID is a dictionary containing the pdf grids at fitting scale for each pid.
"""

def __init__(self, pdf_grid, q20, x_grid):
self.pdf_grid = pdf_grid
self.q20 = q20
self.x_grid = x_grid
self.funcs = [
interp1d(self.x_grid, self.pdf_grid[pid], kind="cubic") for pid in range(len(PIDS_DICT))
]

def xfxQ2(self, pid, x, q2):
"""Return the value of the PDF for the requested pid, x value and, whatever the requested
q2 value, for the fitting q2.

Parameters
----------

pid: int
pid index of particle
x: float
x-value
q2: float
Q square value

Returns
-------
: float
x * PDF value
"""
return self.funcs[list(PIDS_DICT.values()).index(PIDS_DICT[pid])](x)

def hasFlavor(self, pid):
"""Check if the requested pid is in the PDF."""
return pid in PIDS_DICT


def read_runcard(usr_path):
"""Read the runcard and return the relevant information for evolven3fit"""
return yaml.safe_load((usr_path / "filter.yml").read_text(encoding="UTF-8"))
Expand Down
12 changes: 3 additions & 9 deletions n3fit/src/n3fit/scripts/evolven3fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def evolven3fit_new():
def main():
parser = ArgumentParser(
description="evolven3fit - a script with tools to evolve PDF fits",
usage="""evolven3fit [-h] [-q Q_FIN] [-p Q_POINTS] [-n N_CORES] [-e EV_OP_ITERATIONS] [--use-fhmruvv]
usage="""evolven3fit [-h] [-q Q_FIN] [-p Q_POINTS] [-n N_CORES] [-e EV_OP_ITERATIONS]
{produce_eko,produce_eko_photon,evolve} [fit folder]

Note that with the now removed apfel-based version of `evolven3fit` the syntax was
Expand All @@ -123,11 +123,7 @@ def main():
default=None,
help="ev_op_iterations for the EXA theory. Overrides the settings given in the theory card.",
)
parser.add_argument(
"--use-fhmruvv",
action="store_true",
help="Use the FHMRUVV N3LO splitting splitting functions",
)

subparsers = parser.add_subparsers(title="actions", dest="actions")
construct_eko_parser(subparsers)
construct_eko_photon_parser(subparsers)
Expand All @@ -141,9 +137,8 @@ def main():
if args.ev_op_iterations is not None:
op_card_info["configs"]["ev_op_iterations"] = args.ev_op_iterations

# Here we do not allow any modification of the theory card, for the moment.
theory_card_info = {}
if args.use_fhmruvv:
theory_card_info["use_fhmruvv"] = args.use_fhmruvv

if args.no_net:
loader = Loader()
Expand All @@ -169,7 +164,6 @@ def main():
args.force,
eko_path,
None,
args.n_cores,
)
else:
# If we are in the business of producing an eko, do some checks before starting:
Expand Down
11 changes: 0 additions & 11 deletions n3fit/src/n3fit/tests/test_evolven3fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,6 @@ def test_generate_q2grid():


def test_utils():
# Testing the fake LHAPDF class
q20 = 1.65**2
x_grid = np.geomspace(1.0e-7, 1.0, 30)
fake_grids = [[x * (1.0 - x) for x in x_grid] for _ in PIDS_DICT.keys()]
pdf_grid = {pid: v for pid, v in zip(range(len(PIDS_DICT)), fake_grids)}
my_PDF = utils.LhapdfLike(pdf_grid, q20, x_grid)
assert my_PDF.hasFlavor(6)
assert not my_PDF.hasFlavor(0)
for pid in PIDS_DICT:
for x in x_grid:
np.testing.assert_allclose(my_PDF.xfxQ2(pid, x, q20), x * (1.0 - x))
# Testing read_runcard
runcard = utils.read_runcard(REGRESSION_FOLDER)
assert isinstance(runcard["description"], str)
Expand Down
Loading