Source code for evolven3fit.eko_utils

import logging
from typing import Any, Dict, Optional

import numpy as np

from eko.io import runcards
from eko.matchings import Atlas, nf_default
from eko.quantities.heavy_quarks import MatchingScales

from . import utils

_logger = logging.getLogger(__name__)

EVOLVEN3FIT_CONFIGS_DEFAULTS_TRN = {
    "ev_op_iterations": 1,
    "ev_op_max_order": (1, 0),
    "evolution_method": "truncated",
    "inversion_method": "expanded",
    "n_integration_cores": 1,
}

EVOLVEN3FIT_CONFIGS_DEFAULTS_EXA = {
    "ev_op_max_order": (1, 0),
    "evolution_method": "iterate-exact",
    "inversion_method": "exact",
    "n_integration_cores": 1,
}

NFREF_DEFAULT = 5


def _eko_theory_from_nnpdf_theory(nnpdf_theory):
    """Takes an NNPDF theory and returns an eko theory"""
    return runcards.Legacy(nnpdf_theory, {}).new_theory


[docs]def construct_eko_cards( nnpdf_theory, q_fin, q_points, x_grid, op_card_dict: Optional[Dict[str, Any]] = None, theory_card_dict: Optional[Dict[str, Any]] = None, legacy40: bool = False, ): """ Return the theory and operator cards used to construct the eko. nnpdf_theory is a NNPDF theory card for which we are computing the operator card and eko q_fin is the final point of the q grid while q_points is the number of points of the grid. x_grid is the x grid to be used. op_card_dict and theory_card_dict are optional updates that can be provided respectively to the operator card and to the theory card. """ theory, thresholds = load_theory(nnpdf_theory, theory_card_dict) mu0 = theory["Q0"] # eko needs a value for Qedref and for max nf alphas theory["Qedref"] = theory["Qref"] theory["MaxNfAs"] = theory["MaxNfPdf"] theory_card = _eko_theory_from_nnpdf_theory(theory) # construct mugrid # Generate the q2grid, if q_fin and q_points are None, use `nf0` to select a default q2_grid = utils.generate_q2grid( mu0, q_fin, q_points, { theory["mc"]: thresholds["c"], theory["mb"]: thresholds["b"], theory["mt"]: thresholds["t"], }, theory["nf0"], legacy40=legacy40, ) masses = np.array([theory["mc"], theory["mb"], theory["mt"]]) ** 2 thresholds_ratios = np.array([thresholds["c"], thresholds["b"], thresholds["t"]]) ** 2 atlas = Atlas( matching_scales=MatchingScales(masses * thresholds_ratios), origin=(mu0**2, theory["nf0"]) ) # Create the eko operator q2grid # This is a grid which contains information on (q, nf) # in VFNS values at the matching scales need to be doubled so that they are considered in both sides ep = 1e-4 mugrid = [] for q2 in q2_grid: q = float(np.sqrt(q2)) if nf_default(q2 + ep, atlas) != nf_default(q2 - ep, atlas): nf_l = int(nf_default(q2 - ep, atlas)) nf_u = int(nf_default(q2 + ep, atlas)) mugrid.append((q, nf_l)) mugrid.append((q, nf_u)) else: mugrid.append((q, int(nf_default(q2, atlas)))) # construct operator card op_card = build_opcard(op_card_dict, theory, x_grid, mu0, mugrid) return theory_card, op_card
[docs]def construct_eko_photon_cards( nnpdf_theory, q_fin, x_grid, q_gamma, op_card_dict: Optional[Dict[str, Any]] = None, theory_card_dict: Optional[Dict[str, Any]] = None, ): """ Return the theory and operator cards used to construct the eko_photon. nnpdf_theory is a NNPDF theory card for which we are computing the operator card and eko q_fin is the final point of the q grid while q_points is the number of points of the grid. x_grid is the x grid to be used. op_card_dict and theory_card_dict are optional updates that can be provided respectively to the operator card and to the theory card. """ theory, _ = load_theory(nnpdf_theory, theory_card_dict) # if is eko_photon then mu0 = q_gamma mu0 = float(q_gamma) # Now make sure the Legacy class still gets a Qedref, which is equal to Qref theory["Qedref"] = theory["Qref"] theory["MaxNfAs"] = theory["MaxNfPdf"] theory_card = _eko_theory_from_nnpdf_theory(theory) # The photon needs to be evolved down to Q0 q_fin = theory["Q0"] nf_fin = theory["nf0"] # construct mugrid mugrid = [(q_fin, nf_fin)] # construct operator card op_card = build_opcard(op_card_dict, theory, x_grid, mu0, mugrid) return theory_card, op_card
[docs]def load_theory(nnpdf_theory, theory_card_dict): """loads and returns the theory dictionary and the thresholds""" if theory_card_dict is None: theory_card_dict = {} # theory_card construction theory = dict(nnpdf_theory) theory.pop("FNS") theory.update(theory_card_dict) if "nfref" not in theory: theory["nfref"] = NFREF_DEFAULT # Prepare the thresholds according to MaxNfPdf thresholds = {"c": theory["kcThr"], "b": theory["kbThr"], "t": theory["ktThr"]} if theory["MaxNfPdf"] < 5: thresholds["b"] = np.inf if theory["MaxNfPdf"] < 6: thresholds["t"] = np.inf # Setting the thresholds in the theory card to inf if necessary theory.update({"kbThr": thresholds["b"], "ktThr": thresholds["t"]}) return theory, thresholds
[docs]def build_opcard(op_card_dict, theory, x_grid, mu0, mugrid): """Build the operator card. The user provided options should be given as part of ``op_card_dict`` """ if op_card_dict is None: op_card_dict = {} # 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, # ... but these we fix here 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 configs = op_card["configs"] if theory.get("ModEv") == "TRN": configs.update(EVOLVEN3FIT_CONFIGS_DEFAULTS_TRN) elif theory.get("ModEv") == "EXA": # Set the default from the theory card unless it was given in the input op_card_dict.setdefault("ev_op_iterations", theory.get("IterEv")) configs.update(EVOLVEN3FIT_CONFIGS_DEFAULTS_EXA) configs["ev_op_iterations"] = op_card_dict["ev_op_iterations"] # Note that every entry that is not a dictionary should not be # touched by the user and indeed an user cannot touch them for key in op_card: if key in op_card_dict and isinstance(op_card[key], dict): op_card[key].update(op_card_dict[key]) elif key in op_card_dict: _logger.warning("Entry %s is not a dictionary and will be ignored", key) op_card = runcards.OperatorCard.from_dict(op_card) return op_card