import pathlib
import shutil
import numpy as np
from scipy.interpolate import interp1d
from validphys.pdfbases import PIDS_DICT
from validphys.utils import yaml_safe
from .q2grids import Q2GRID_DEFAULT, Q2GRID_NNPDF40
[docs]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"))
[docs]def get_theoryID_from_runcard(usr_path):
"""Return the theoryID from the runcard"""
# read the runcard
my_runcard = read_runcard(usr_path)
return my_runcard["theory"]["theoryid"]
[docs]def generate_q2grid(Q0, Qfin, Q_points, match_dict, nf0=None, legacy40=False):
"""Generate the q2grid used in the final evolved pdfs or use the default grid if Qfin or Q_points is
not provided.
match_dict contains the couples (mass : factor) where factor is the number to be multiplied to mass
in order to obtain the relative matching scale.
"""
if Qfin is None and Q_points is None:
if legacy40:
return Q2GRID_NNPDF40
elif nf0 in (3, 4, 5):
return Q2GRID_DEFAULT
elif nf0 is None:
raise ValueError("In order to use a default grid, a value of nf0 must be provided")
else:
raise NotImplementedError(f"No default grid in Q available for {nf0=}")
elif Qfin is None or Q_points is None:
raise ValueError("q_fin and q_points must be specified either both or none of them")
else:
grids = []
Q_ini = Q0
num_points_list = []
for masses in match_dict:
match_scale = masses * match_dict[masses]
# Fraction of the total points to be included in this batch is proportional
# to the log of the ratio between the initial scale and final scale of the
# batch itself (normalized to the same log of the global initial and final
# scales)
if match_scale < Qfin:
frac_of_point = np.log(match_scale / Q_ini) / np.log(Qfin / Q0)
num_points = int(Q_points * frac_of_point)
num_points_list.append(num_points)
grids.append(np.geomspace(Q_ini**2, match_scale**2, num=num_points, endpoint=False))
Q_ini = match_scale
num_points = Q_points - sum(num_points_list)
grids.append(np.geomspace(Q_ini**2, Qfin**2, num=num_points))
return np.concatenate(grids).tolist()
[docs]def fix_info_path(usr_path):
"""Fix the location of the info file from the folder nnfit/usr_path to
just nnfit
Examples
--------
Starting from the info path
initial_info_file_path = "/myfolder/myfit/nnfit/myfit/myfit.info"
and using this function with usr_path = "/myfolder/myfit", one gets
final_info_file_path = "/myfolder/myfit/nnfit/myfit.info"
"""
nnfit = usr_path / "nnfit"
info_file = usr_path.stem + ".info"
info_file_path = nnfit / usr_path.stem / info_file
dest_path_info = nnfit / info_file
shutil.move(info_file_path, dest_path_info)
[docs]def fix_replica_path(usr_path, replica_num):
"""Fix the location of the dat file of the replica <replica_num> from the folder nnfit/usr_path to
just nnfit/replica_<replica_num>
Examples
--------
Starting from the replica 5 path
initial_replica_file_path = "/myfolder/myfit/nnfit/myfit/myfit_5.dat"
and using this function with usr_path = "/myfolder/myfit", one gets
final_replica_file_path = "/myfolder/myfit/nnfit/replica_5/myfit.dat"
"""
nnfit = usr_path / "nnfit"
replica_file_path = nnfit / usr_path.stem / f"{usr_path.stem}_{replica_num:04d}.dat"
dest_path_replica = nnfit / f"replica_{replica_num}" / f"{usr_path.stem}.dat"
shutil.move(replica_file_path, dest_path_replica)
[docs]def check_is_a_fit(config_folder):
"""Check if config_folder is a fit folder, i.e. if it contains the filter.yml file
and the nnfit folder."""
usr_path = pathlib.Path(config_folder)
filter_path = usr_path / "filter.yml"
if not filter_path.is_file():
raise ValueError(
"filter.yaml file not found: the path" + str(filter_path.absolute()) + " is not valid"
)
nnfitpath = usr_path / "nnfit"
if not nnfitpath.is_dir():
raise ValueError("nnfit folder not found: provided path is not valid")