diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 20cb6fa..23c68df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,7 +41,7 @@ repos: - id: pydocstyle files: ^src/ additional_dependencies: - - toml + - tomli - repo: https://github.com/pre-commit/pre-commit rev: v4.5.1 hooks: diff --git a/extras/theories/theory_nnlo.yaml b/extras/theories/theory_nnlo.yaml index 0ef0f0a..8c3dc2a 100644 --- a/extras/theories/theory_nnlo.yaml +++ b/extras/theories/theory_nnlo.yaml @@ -1,44 +1,44 @@ ID: NNLO Comments: Example of NNLO theory -CKM: 0.97428 0.22530 0.003470 0.22520 0.97345 0.041000 0.00862 0.04030 0.999152 +CKM: 0.97367 0.22431 3.82e-3 0.221 0.975 41.1e-3 8.6e-3 41.5e-3 1.010 DAMP: 0 EScaleVar: 1 FNS: FONLL-C -GF: 1.1663787e-05 +GF: 1.1663788e-5 HQ: POLE IC: 1 IB: 0 MP: 0.938 -MW: 80.398 -MZ: 91.1876 +MZ: 91.1880 +MW: 80.3692 MaxNfAs: 5 MaxNfPdf: 5 -ModEv: TRN +ModEv: EXA +IterEv: 60 ModSV: expanded NfFF: 4 PTO: 2 Q0: 1.65 QED: 0 -Qedref: 1.777 -Qmb: 4.92 -Qmc: 1.51 -Qmt: 172.5 -Qref: 91.2 -SIN2TW: 0.23126 +Qedref: 91.1880 +Qref: 91.1880 +alphas: 0.118 +alphaqed: 0.0075700 +SIN2TW: 0.22348 SxOrd: LL SxRes: 0 TMC: 1 XIF: 1.0 XIR: 1.0 -alphaqed: 0.007496252 -alphas: 0.118 global_nx: 0 -kbThr: 1.0 +mc: 1.502 +Qmc: 1.502 kcThr: 1.0 -ktThr: 1.0 -mb: 4.92 -mc: 1.51 +mb: 4.936 +Qmb: 4.936 +kbThr: 1.0 mt: 172.5 +Qmt: 172.5 nfref: null nf0: null fact_to_ren_scale_ratio: 1.0 diff --git a/src/pinefarm/cli/autogen.py b/src/pinefarm/cli/autogen.py index cd92f77..7fe754c 100644 --- a/src/pinefarm/cli/autogen.py +++ b/src/pinefarm/cli/autogen.py @@ -31,10 +31,12 @@ def runcards(dataset, target, select_obs=None, name=None): output = configs.configs["paths"]["runcards"] / f"{target}_{name}" - if target == "NNLOJET": + if target.upper() == "NNLOJET": output_runcards = generate_pinecard_from_nnpdf( dataset, output_path=output, observables=select_obs ) + else: + raise ValueError(f"Target {target} not recognized") rich.print("Pinecards written to: ") rich.print(" " + "\n".join(str(i) for i in output_runcards)) diff --git a/src/pinefarm/external/__init__.py b/src/pinefarm/external/__init__.py index 3423915..5d3d9de 100644 --- a/src/pinefarm/external/__init__.py +++ b/src/pinefarm/external/__init__.py @@ -27,7 +27,7 @@ def decide_external_tool(dsname: str): """ # The decisions are usually based on the existence of a `.yaml` file with a specific name # or a prefix in the pinecard - if dsname.startswith("NNLOJET"): + if dsname.upper().startswith("NNLOJET"): from .nnlojet import NNLOJET return NNLOJET, "blue" diff --git a/src/pinefarm/external/nnlojet/nnpdf_interface.py b/src/pinefarm/external/nnlojet/nnpdf_interface.py index ca7efb4..db21b69 100755 --- a/src/pinefarm/external/nnlojet/nnpdf_interface.py +++ b/src/pinefarm/external/nnlojet/nnpdf_interface.py @@ -28,7 +28,7 @@ yaml.default_flow_style = False yaml.indent(sequence=4, offset=2, mapping=4) -HISTOGRAM_VARIABLES = {"y", "etay", "eta", "pT", "pT2", "M2"} +HISTOGRAM_VARIABLES = {"y", "etay", "eta", "pT", "pT2", "M2", "ET"} def _legacy_nnpdf_translation(df, proc_type): @@ -88,23 +88,28 @@ def _1d_histogram(kin_df, hist_var): def _nnlojet_observable(observable, process): """Try to automatically understand the NNLOJET observables given the NNPDF process and obs.""" observable = observable.lower() + process = process.upper() if observable in ("eta", "y", "etay"): - if process.upper().startswith("Z"): + if process.startswith("Z"): return "yz" - if process.upper().startswith("WP") and not process.upper().endswith("J"): + if process.startswith("WP") and not process.endswith("J"): return "ylp" - if process.upper().startswith("WM") and not process.upper().endswith("J"): + if process.startswith("WM") and not process.endswith("J"): return "ylm" + if process.startswith("GJ"): + return "y_gam" if observable == "pt": - if process.upper().startswith("Z"): + if process.startswith("Z"): return "ptz" - if process.upper().startswith("W"): + if process.startswith("W"): return "ptw" - if observable == "m" and process.upper().startswith("Z"): + if observable == "m" and process.startswith("Z"): return "mll" if observable == "m2": print("\033[91m [WARNING] \033[0m Changed M2 to M in the selectors") return "mll" + if observable == "et" and process.startswith("GJ"): + return "pt_gam" raise ValueError(f"Observable {observable} not recognized for process {process}") @@ -193,9 +198,17 @@ def _generate_nnlojet_pinecard(runname, process, energy, experiment, histograms) """Generate a pinecard for NNLOJET runs from an NNPDF dataset.""" selectors = select_selectors(experiment, process) histograms = deepcopy(histograms) + photon = {} + # Digest the process variable if process.startswith("Z0"): process = process.replace("Z0", "Z") + if process.startswith("G"): + # Defaults for: 2302.00510 + photon = { + "photon_isolation": "etsum[R=0.4, ETmax_const=4.8, ETmax_epsilon=0.0042, ET_threshold=0]", + "photon_fragmentation": "BFG2", + } # Digest the histogram variable for histo in histograms: @@ -204,7 +217,7 @@ def _generate_nnlojet_pinecard(runname, process, energy, experiment, histograms) ret = { "runname": runname, - "process": {"proc": process, "sqrts": energy}, + "process": {"proc": process, "sqrts": energy, **photon}, "pdf": "NNPDF40_nnlo_as_01180", "techcut": 1e-7, "histograms": histograms, @@ -227,11 +240,21 @@ def _generate_nnlojet_pinecard(runname, process, energy, experiment, histograms) }, "selectors": selectors, } + + # Add the scale + if process.startswith("Z"): + ret["scales"] = {"mur": "etz", "muf": "etz"} + elif process.startswith("W"): + ret["scales"] = {"mur": "etw", "muf": "etw"} + elif process.startswith("G"): + # Defaults for: 2302.00510 R=0.4 + ret["scales"] = {"mur": "pt_gam", "muf": "[pt_gam, 0.871*sqrt_pt_gam]"} + return ret def generate_pinecard_from_nnpdf( - nnpdf_dataset, scale="etz", output_path=".", observables=None + nnpdf_dataset, scale=None, output_path=".", observables=None ): """Generate a NNLOJET pinecard from an NNPDF dataset. @@ -277,6 +300,8 @@ def generate_pinecard_from_nnpdf( if "M2" in hist_vars: if len(kin_df["M2"]["mid"].unique()) == 1: hist_vars.remove("M2") + elif process.startswith("PH"): + process = process.replace("PH", "GJ") # Create the histogram depending on whether this is a 1D or 2D distribution (or total) histograms = None @@ -286,10 +311,10 @@ def generate_pinecard_from_nnpdf( elif len(hist_vars) == 2: # Let's see whether we know how to do this 2D distribution - if "M2" in hist_vars: - svar = "M2" - elif "y" in hist_vars: - svar = "y" + for var_i_know in ["M2", "y", "eta"]: + if var_i_know in hist_vars: + svar = var_i_know + break else: raise NotImplementedError(f"Don't know how to do this 2D: {hist_vars}") hist_vars.remove(svar) @@ -349,7 +374,7 @@ def generate_pinecard_from_nnpdf( processes = [process] if process.startswith("WPWM"): processes = [process.replace("WP", ""), process.replace("WM", "")] - if process == "DY": + elif process == "DY": processes = ["Z0", "WP", "WM"] parent_folder = output_path.parent @@ -360,7 +385,9 @@ def generate_pinecard_from_nnpdf( runname = nnpdf_dataset.replace(process, proc) ret = _generate_nnlojet_pinecard(runname, proc, energy, experiment, histograms) - ret["scales"] = {"mur": scale, "muf": scale} + if scale is not None: + # Default to etz + ret["scales"] = {"mur": scale, "muf": scale} # Beautify before dumping data = CommentedMap(ret) diff --git a/src/pinefarm/external/nnlojet/runcardgen.py b/src/pinefarm/external/nnlojet/runcardgen.py index 1ecc2fc..fe69df7 100755 --- a/src/pinefarm/external/nnlojet/runcardgen.py +++ b/src/pinefarm/external/nnlojet/runcardgen.py @@ -222,14 +222,19 @@ def parse_input_yaml(yaml_path): def _fill_process(process): - """Fill process options.""" + """Fill process block given the metadata for the process""" process_name = process["proc"] sqrts = process["sqrts"] - """Fill process block given the metadata for the process""" + jet = process.get("jet", "none[0]") # Can be None + fill_photon = "" + if process_name.startswith("G"): + fill_photon = f""" + photon_isolation = {process['photon_isolation']} + photon_fragmentation = {process['photon_fragmentation']}""" return f""" PROCESS {process_name} collider = pp sqrts = {sqrts} - jet = none[0] + jet = {jet}{fill_photon} decay_type = 1 END_PROCESS """ @@ -239,6 +244,7 @@ def _fill_run(runname, pdf, mode_line, techcut=1e-7, multi_channel=3): """Fil run options.""" if multi_channel == 0: multi_channel = ".false." + # Note, scale coefficients need to be set to true to fill the grid return f""" RUN {runname.upper()} PDF = {pdf}[0] @@ -268,6 +274,7 @@ def _fill_parameters(theory_parameters): return f""" PARAMETERS {ptext} +hard_photon_alpha0 = .true. ! only useful for GJ runs END_PARAMETERS """ diff --git a/src/pinefarm/external/nnlojet/runner.py b/src/pinefarm/external/nnlojet/runner.py index 2bb089a..7f7105a 100644 --- a/src/pinefarm/external/nnlojet/runner.py +++ b/src/pinefarm/external/nnlojet/runner.py @@ -24,6 +24,11 @@ def __init__(self, pinecard, theorycard, *args, **kwargs): # Save the yaml dictionary from the NNLOJET pinecard self._yaml_dict = safe_load(yaml_card.open("r")) + @staticmethod + def install(): + """NNLOJET should be installed manually.""" + pass + def preparation(self): """Run the preparation step for NNLOJET.""" # Update the yaml card according to the theory diff --git a/src/pinefarm/install.py b/src/pinefarm/install.py index 4b9e3c0..ac44a5f 100644 --- a/src/pinefarm/install.py +++ b/src/pinefarm/install.py @@ -300,7 +300,6 @@ def lhapdf(): This is currently needed by every tool due to postprocessing requirements. """ - def installed(): """Define availability condition.""" try: