diff --git a/doc/sphinx/source/tutorials/run-qed-fit.rst b/doc/sphinx/source/tutorials/run-qed-fit.rst index 322b466c63..6b1432aee1 100644 --- a/doc/sphinx/source/tutorials/run-qed-fit.rst +++ b/doc/sphinx/source/tutorials/run-qed-fit.rst @@ -3,32 +3,232 @@ ========================== How to run a QED fit ========================== +This tutorial describes how to run a QED fit using the LuxQED approach, as +described in `arXiv:1607.04266 `_ and +`arXiv:1708.01256 `_. -It is possible to perform a QED fit adding the key ``fiatlux`` to the runcard. In this way -a photon PDF will be generated using the FiatLux public library that implements the LuxQED -(see :cite:p:`Manohar:2016nzj` and :cite:p:`Manohar:2017eqh`) approach. -The parameters to be added are the following: +Setting up the runcard +---------------------- + +It is possible to perform a QED fit by adding a ``fiatlux`` block to the +runcard. The following code snippet shows an example of a QED fit configuration: .. code-block:: yaml fiatlux: - luxset: NNPDF40_nnlo_as_01180 + luxset: 251127-jcm-lh-qed-001 additional_errors: true luxseed: 1234567890 -``luxset`` is the PDF set used to generate the photon PDF with `FiatLux `. -The code will generate as many photon replicas as the number of replicas contained in the ``luxset``. Therefore, if the user -tries to generate a replica with ID higher than the maximum ID of the ``luxset``, the code will -raise an error. Moreover, being the LuxQED approach an iterated prcedure, and given that some replicas -do not pass the ``postfit`` selection criteria, the user should make sure that the number of replicas in -the ``luxset`` is high enough such that in the final iteration there will be a number of replicas -higher than the final replicas desired. -``additional_errors`` is a parameter that switches on and off the additional errors of the LuxQED approach, -while ``luxseed`` is the seed used to generate such errors. -This errors should be switched on only in the very last iteration of the procedure. +The parameters contained in the ``fiatlux`` block are: + +* ``luxset`` + The name of the PDF set used to generate the photon PDF with `FiatLux + `_. The code will use as many + photon replicas as the number of replicas contained in the ``luxset``. If + the user tries to generate a replica with ID higher than the maximum ID of + the ``luxset``, the code will start reusing photon replica from the first. + Being the LuxQED approach an iterated procedure, and given that some + replicas do not pass the ``postfit`` selection criteria, the user should + make sure that the number of replicas in the ``luxset`` is high enough + such that in the final iteration there will be a number of replicas higher + than the final replicas desired. + +* ``additional_errors`` + Boolean flag to switch on and off the additional errors of the LuxQED approach. + + .. note:: + + The ``additional_errors`` flag should be switched on only in the very last + iteration of the procedure. + +* ``luxseed`` + The seed used to generate the additional errors of the LuxQED as in ``additional_errors``. + +The code uses both the ``fiatlux`` block and the ``theoryid`` specified in the +runcard to identify the photon PDF set. As explained below, the code searches +for precomputed photon PDF sets using the pair of ``luxset`` and ``theoryid`` +parameters, first locally and then on the NNPDF server (see +:ref:`photon-resources` for details). The photon PDF set will be named +``photon_theoryID__fit_``. + +.. _photon-resources: + +Running with Photon PDF sets +----------------------------- + +The generation of a photon PDF set can add significant overhead to the fitting +procedure. Moreover, the same photon PDF set might be used in different fits. To +minimize the overhead due to the generation of photon PDF sets and avoid +redundant computations, the code looks for precomputed photon resources either +locally or on the NNPDF server. If a desired photon PDF set does not exist in +either of the two locations, it will be computed on the fly and stored locally. +The following sections describe how to use existing photon PDF sets or generate +new ones. + +Using available Photon PDF sets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Before running a QED fit, it is strongly advised to prepare the fit using +``vp-setupfit``, as explained in :ref:`this tutorial `. This will +ensure that all the required resources are available, including the photon PDF. +The desired photon PDF is specified by the ``luxset`` parameter in the +``fiatlux`` block and the ``theoryid``, as explained above. The code will first +look for the photon PDF set in the local directory specified in the +:ref:`profile file `. If the set is not found locally, it will try to +download it from the NNPDF server. The following is an example of running +``vp-setupfit`` using the ``fiatlux`` block shown above: + +.. code-block:: bash + + $ vp-setupfit qed_example_runcard.yml + [INFO]: Could not find a resource (photonQED): Could not find Photon QED set /user/envs/nnpdf/share/NNPDF/photons_qed/photon_theoryID_702_fit_251127-jcm-lh-qed-001 in theory: 702. Attempting to download it. + [INFO]: Downloading https://data.nnpdf.science/photons/photon_theoryID_702_fit_251127-jcm-lh-qed-001.tar. + [==================================================] (100%) + [INFO]: Extracting archive to /opt/homebrew/Caskroom/miniconda/base/envs/nnpdf/share/NNPDF/photons_qed + [INFO]: Photon QED set found for 702 with luxset 251127-jcm-lh-qed-001. + [WARNING]: Using q2min from runcard + [WARNING]: Using w2min from runcard + Using Keras backend + [INFO]: All requirements processed and checked successfully. Executing actions. + ... + +This will download and extract the photon PDF set in the local +``photons_qed_path`` specified in the :ref:`profile file `. + +The ``vp-list`` utility tool can be used to list all the available photon PDF +sets locally and on the NNPDF server. To list the available photon PDF sets, +just run: + +.. code-block:: bash + + $ vp-list photons + [INFO]: The following photons are available locally: + - theoryID_702_fit_251127-jcm-lh-qed-001 + [INFO]: The following photons are downloadable: + - theoryID_702_fit_251127-jcm-lh-qed-002 + +In this example, there is one photon PDF set available locally, and one photon +resource available on the NNPDF server precomputed with theory ID 702 and +``251127-jcm-lh-qed-002`` as input PDF. The user can manually download a photon +PDF set using the ``vp-get`` tool as explained :ref:`here `. For +example: + +.. code-block:: bash + + $ vp-get photonQED 702 251127-jcm-lh-qed-002 + [INFO]: Could not find a resource (photonQED): Could not find Photon QED set /user/envs/nnpdf/share/NNPDF/photons_qed/photon_theoryID_702_fit_251127-jcm-lh-qed-002 in theory: 702. Attempting to download it. + [INFO]: Downloading https://data.nnpdf.science/photons/photon_theoryID_702_fit_251127-jcm-lh-qed-002.tar. + [==================================================] (100%) + [INFO]: Extracting archive to /user/envs/nnpdf/share/NNPDF/photons_qed + PosixPath('/user/envs/nnpdf/share/NNPDF/photons_qed/photon_theoryID_702_fit_251127-jcm-lh-qed-002') -Whenever the photon PDF is generated, it will remain constant during the fit and will be considered in the momentum sum rule. +As in the case of ``vp-setupfit``, this will download and extract the photon PDF +set in the local ``photons_qed_path`` specified in the :ref:`profile file `. +Once the photon PDF set is available locally, the user can proceed to run the +fit with ``n3fit`` as usual. .. warning:: - At the moment it is not possible to run QED fits in parallel, as the FiatLux library cannot run in parallel. + If ``vp-setupfit`` is not run before ``n3fit``, and the photon PDF set is not + available locally, the code will **not** attempt to download it from the server, + but will directly proceed to compute it on the fly. See the :ref:`next section ` for + more details. + +.. _generating-photon-sets: +Generating new Photon PDF sets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If the desired photon PDF set is not available locally nor on the NNPDF server, the +code will generate the required photon PDF set replicas on the fly using `FiatLux `_. +This can be done either during the ``vp-setupfit`` step, which precomputes all photon +replicas before starting the fit, or during the fitting step with ``n3fit``, which +generates the photon replica as needed for each replica being fitted. + +.. note:: + + Generating photon PDF sets on the fly can add significant overhead to the + fitting procedure. It is therefore strongly advised to precompute the photon + PDF set using ``vp-setupfit`` before running the fit with ``n3fit``. + +In either case, the generated photon PDF set will be stored locally in the +``photons_qed_path`` specified in the :ref:`profile file `, so that +it can be reused in future fits. The folder containing the photon replicas will +be named as ``photon_theoryID__fit_`` where ```` and +```` are the values specified in the runcard. The folder contains a ``npz`` +file for each photon replica, named as ``replica_.npz``. Each replica +file contains the photon PDF grid computed with FiatLux at :math:`Q_{\rm init} = 100` GeV, +prior to the evolution through EKO. + +.. important:: + + Automatic upload to the NNPDF server through ``vp-upload`` is **not** + supported at the moment. The user should manually create a ``tar`` archive + file containing the photon replicas and upload it to server. Refer to the + :ref:`profile file ` to find the remote URL where photon PDF sets are stored. + + +Using ``vp-setupfit`` (preferred) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + In order to trigger the computation of the photon PDF set during ``vp-setupfit``, + the user needs to add the flag ``compute_in_setupfit: true`` in the ``fiatlux`` block + discussed above. The following is an example of running ``vp-setupfit`` with + this flag enabled: + + .. code-block:: bash + + $ vp-setupfit qed_example_runcard.yml + [INFO]: Could not find a resource (photonQED): Could not find Photon QED set /user/envs/nnpdf/share/NNPDF/photons_qed/photon_theoryID_703_fit_251127-jcm-lh-qed-001 in theory: 703. Attempting to download it. + [ERROR]: Resource not in the remote repository: Photon QED set for TheoryID 703 and luxset 251127-jcm-lh-qed-001 is not available in the remote server. + [WARNING]: Photon QED set for theory 703 with luxset 251127-jcm-lh-qed-001 not found. It will be computed in vp-setupfit. + [INFO]: Forcing photon computation with FiatLux during setupfit. + [WARNING]: Using q2min from runcard + [WARNING]: Using w2min from runcard + Using Keras backend + [INFO]: All requirements processed and checked successfully. Executing actions. + ... + + In addition, the user can also specify the optional parameter ``eps_base`` to + the ``fiatlux`` block to set the base precision of the FiatLux computation, + which controls the precision on final integration of double integral. By + default, it is set to ``1e-5``. If the ``compute_in_setupfit`` flag is set to + ``true`` despite the photon PDF being available locally, the code will + recompute and overwrite the existing photon PDF set + + .. tip:: + + During ``vp-setupfit``, the code tries to distribute the computation of the + photon replicas among the available CPU cores as specified in the + ``maxcores`` key of the runcard. This can significantly speed up the + computation of the photon PDF set. Make sure to set ``maxcores`` to a value + compatible with the available hardware. For example, using ``maxcores: 64`` + will try to compute up to 64 photon replicas in parallel using 64 GB of RAM. + + Once the preparation step is completed, and the photon PDF set is generated and stored + locally, the user can proceed to run the fit with ``n3fit`` as usual. + +Using ``n3fit`` (discouraged) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + If the user prefers to compute the photon PDF set during the fitting step with + ``n3fit``, no additional flag is needed in the runcard and ``vp-setupfit`` is + not required beforehand (unless other resources are needed such as the + :ref:`theory covariance matrix `). The code will check for + the presence of the photon PDF set locally before starting the fit for each + replica. If it is not found, it will proceed to compute the photon replica as + needed for each replica being fitted. The following is an example of running + ``n3fit`` where the photon PDF set is computed during the fitting step: + + .. code-block:: bash + + $ n3fit qed_example_runcard.yml 1 + [INFO]: Creating replica output folder in /user/runcards/qed_example_runcard/nnfit/replica_1 + [WARNING]: Using q2min from runcard + [WARNING]: Using w2min from runcard + Using Keras backend + [WARNING]: No Photon QED set found for Theory 703 with luxset 251127-jcm-lh-qed-001. It will be computed using FiatLux. This may impact performance. It is recommended to precompute the photon set before running the fit. Refer to https://docs.nnpdf.science/tutorials/run-qed-fit.html for more details on precomputing photon PDF sets. + [INFO]: All requirements processed and checked successfully. Executing actions. + + .. warning:: + + Computing the photon PDF set during `n3fit` with multiple replicas or using GPUs + has not been tested and may lead to unexpected behaviour. It is strongly advised to + precompute the photon PDF set using ``vp-setupfit`` before running the fit with ``n3fit``. diff --git a/doc/sphinx/source/vp/download.rst b/doc/sphinx/source/vp/download.rst index 133b019b25..493c3d642c 100644 --- a/doc/sphinx/source/vp/download.rst +++ b/doc/sphinx/source/vp/download.rst @@ -6,8 +6,8 @@ Downloading resources ``validphys`` is designed so that, by default, resources stored in known remote locations are downloaded automatically and seamlessly used where necessary. Available resources include PDF sets, completed fits, theories, and results of -past ``validphys`` runs that have been :ref:`uploaded to the server `. -The ``vp-get`` tool, :ref:`described below `, +past ``validphys`` runs that have been :ref:`uploaded to the server `. +The ``vp-get`` tool, :ref:`described below `, can be used to download the same items manually. Automatic operation @@ -15,7 +15,7 @@ Automatic operation By default when some resource such as a PDF is required by ``validphys`` (or derived tools such as ``vp-setupfit``), the code will first look for it in some -local directory specified in the [profile file](nnprofile). If it is not found +local directory specified in the :ref:`profile file `. If it is not found there, it will try to download it from some remote repository (also specified in the profile). @@ -70,6 +70,21 @@ Theories Theories (specified by the ``theoryid`` key) are downloaded and uncompressed. +Photon PDF sets + Photon PDF sets can be downloaded if they have been previously generated + using `FiatLux `_. A photon PDF set + is uniquely identified by the input PDF set used to generate the photon replicas + and the theory ID. See :ref:`this tutorial ` for more details on + how to set up a QED fit. + +EKOs + Evolution kernels (EKOs) can be downloaded if they have been previously + generated using EKO. + +Hyperparameter scans + Results of the hyperparameter scans generated in the hyperoptimization + procedure. + ``validphys`` output files Files produced by ``validphys`` can be used as input to subsequent validphys analyses (for example χ² tables are used for αs fits). The user needs to @@ -80,7 +95,7 @@ Theories .. _vp-get: -The `vp-get` tool +The ``vp-get`` tool ----------------- The ``vp-get`` tool can be used to download resources manually, in the same way @@ -99,8 +114,11 @@ They correspond to the resources described :ref:`above ` $ vp-get --list Available resource types: + - eko - fit + - hyperscan - pdf + - photonQED - theoryID - vp_output_file @@ -118,6 +136,15 @@ information on it and bail out: $ vp-get fit NNPDF31_nlo_as_0118_1000 FitSpec(name='NNPDF31_nlo_as_0118_1000', path=PosixPath('/home/zah/anaconda3/envs/nnpdf-dev/share/NNPDF/results/NNPDF31_nlo_as_0118_1000')) +.. note:: + + In order to download photon PDF sets, the user needs to specify both the input PDF + set and the theory ID. For example: + + .. code:: bash + + $ vp-get photonQED 40000000 NNPDF40_nnlo_as_01180 + Downloading resources in code (``validphys.loader``) ---------------------------------------------------- @@ -164,10 +191,10 @@ Conversely the ``Loader`` class will only search locally. ----> 1 l.check_dataset('NMC', theoryid=151) ~/nngit/nnpdf/validphys2/src/validphys/loader.py in check_dataset(self, name, rules, sysnum, theoryid, cfac, frac, cuts, use_fitcommondata, fit, weight) - 416 + 416 417 if not isinstance(theoryid, TheoryIDSpec): --> 418 theoryid = self.check_theoryID(theoryid) - 419 + 419 420 theoryno, _ = theoryid ~/nngit/nnpdf/validphys2/src/validphys/loader.py in check_theoryID(self, theoryID) @@ -175,7 +202,7 @@ Conversely the ``Loader`` class will only search locally. 289 raise TheoryNotFound(("Could not find theory %s. " --> 290 "Folder '%s' not found") % (theoryID, theopath) ) 291 return TheoryIDSpec(theoryID, theopath) - 292 + 292 TheoryNotFound: Could not find theory 151. Folder '/home/zah/anaconda3/share/NNPDF/data/theory_151' not found @@ -184,10 +211,9 @@ Output files uploaded to the ``validphys`` can be retrieved specifying their pat (starting from the report ID). They will be either downloaded (when using ``FallbackLoader``) or retrieved from the cache: -.. code:: python +.. code:: python from validphys.loader import FallbackLoader as Loader l = Loader() l.check_vp_output_file('qTpvLZLwS924oAsmpMzhFw==/figures/f_ns0_fitunderlyinglaw_plot_closure_pdf_histograms_0.pdf') PosixPath('/home/zah/anaconda3/share/NNPDF/vp-cache/qTpvLZLwS924oAsmpMzhFw==/figures/f_ns0_fitunderlyinglaw_plot_closure_pdf_histograms_0.pdf') - diff --git a/doc/sphinx/source/vp/nnprofile.rst b/doc/sphinx/source/vp/nnprofile.rst index b917164914..e4242867be 100644 --- a/doc/sphinx/source/vp/nnprofile.rst +++ b/doc/sphinx/source/vp/nnprofile.rst @@ -39,16 +39,16 @@ the code. These should be specified in YAML format. It is possible to set the special key ``RELATIVE_TO_PYTHON``, in this case the code will use as share folder the share folder of the current environment (for instance ``${CONDA_PREFIX}/share/NNPDF``). -``theories_path`` - The path in the user's system where the theory files (FKtables and ekos) - are to be found, and stored when :ref:`downloaded `. - Defaults to ``nnpdf_share/theories``. - ``results_path`` A path where completed fits are to be retrieved from, and stored when :ref:`downloaded `. Defaults to ``nnpdf_share/results``. +``theories_path`` + The path in the user's system where the theory files (FKtables and ekos) + are to be found, and stored when :ref:`downloaded `. + Defaults to ``nnpdf_share/theories``. + ``data_path`` List of paths where to read the data from. Regardless of the content of this variable, the installation path of the ``nnpdf_data`` package @@ -58,6 +58,10 @@ the code. These should be specified in YAML format. ``validphys_cache_path`` A path where to store downloaded validphys resources. +``photons_qed_path`` + A path where to store downloaded photon PDF sets generated with FiatLux. See + :ref:`this tutorial ` for more details. + ``fit_urls`` A list of URLs where to search completed fits from. @@ -81,6 +85,12 @@ the code. These should be specified in YAML format. ``nnpdf_pdfs_index`` The name of the remote PDF index. Shouldn't be changed. +``photon_qed_urls`` + A list of URLs pointing to repositories where photon PDF sets are stored. + +``photon_qed_index`` + The name of the remote photon PDF index. Shouldn't be changed. + ``upload_host`` The SSH host (with user name as in ``user@host``) used to upload ``validphys`` reports and fits. diff --git a/n3fit/src/n3fit/checks.py b/n3fit/src/n3fit/checks.py index ab74c14b15..e8f17e2c70 100644 --- a/n3fit/src/n3fit/checks.py +++ b/n3fit/src/n3fit/checks.py @@ -9,7 +9,7 @@ from n3fit.hyper_optimization import penalties as penalties_module from n3fit.hyper_optimization.rewards import IMPLEMENTED_LOSSES, IMPLEMENTED_STATS from reportengine.checks import CheckError, make_argcheck -from validphys.loader import FallbackLoader +from validphys.loader import FallbackLoader, Loader from validphys.pdfbases import check_basis log = logging.getLogger(__name__) @@ -455,10 +455,21 @@ def check_deprecated_options(fitting): @make_argcheck -def check_multireplica_qed(replicas, fiatlux): +def check_photonQED_exists(theoryid, fiatlux): + """Check that the Photon QED set for this theoryid and luxset exists""" if fiatlux is not None: - if len(replicas) > 1: - raise CheckError("At the moment, running a multireplica QED fits is not allowed.") + luxset = fiatlux['luxset'] + try: + _ = Loader().check_photonQED(theoryid.id, luxset) + log.info(f"Photon QED set found for {theoryid.id} with luxset {luxset}.") + except FileNotFoundError: + log.warning( + f"No Photon QED set found for {theoryid} with luxset {luxset}. It " + "will be computed using FiatLux. This may impact performance. It " + "is recommended to precompute the photon set before running the fit. " + "Refer to https://docs.nnpdf.science/tutorials/run-qed-fit.html for more details " + "on precomputing photon PDF sets." + ) @make_argcheck diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index e0fd1af9b5..312885de4c 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -13,7 +13,7 @@ # Action to be called by validphys # All information defining the NN should come here in the "parameters" dict -@n3fit.checks.check_multireplica_qed +@n3fit.checks.check_photonQED_exists @n3fit.checks.check_polarized_configs def performfit( *, diff --git a/n3fit/src/n3fit/scripts/vp_setupfit.py b/n3fit/src/n3fit/scripts/vp_setupfit.py index d63429cc70..849207ecc8 100644 --- a/n3fit/src/n3fit/scripts/vp_setupfit.py +++ b/n3fit/src/n3fit/scripts/vp_setupfit.py @@ -1,8 +1,8 @@ #!/usr/bin/env python """ - setup-fit - prepare and apply data cuts before fit - setup-fit constructs the fit [results] folder where data used by nnfit - will be stored. +setup-fit - prepare and apply data cuts before fit +setup-fit constructs the fit [results] folder where data used by nnfit +will be stored. """ # Implementation notes @@ -37,10 +37,10 @@ from reportengine import colors from validphys.app import App from validphys.config import Config, ConfigError, Environment, EnvironmentError_ -from validphys.loader import Loader, TheoryNotFound +from validphys.loader import FallbackLoader, PhotonQEDNotFound, TheoryNotFound from validphys.utils import yaml_safe -l = Loader() +loader = FallbackLoader() SETUPFIT_FIXED_CONFIG = dict( actions_=[ @@ -57,6 +57,7 @@ 'validphys.filters', 'validphys.results', 'validphys.theorycovariance.construction', + 'validphys.photon.compute', ] SETUPFIT_DEFAULTS = dict(use_cuts='internal') @@ -155,7 +156,7 @@ def from_yaml(cls, o, *args, **kwargs): SETUPFIT_FIXED_CONFIG['theory']['theoryid'] = closuredict['faketheoryid'] # download theoryid since it will be used in the fit try: - l.check_theoryID(file_content['theory']['theoryid']) + loader.check_theoryID(file_content['theory']['theoryid']) except TheoryNotFound as e: log.warning(e) filter_action = 'datacuts::closuretest::theory::fitting filter' @@ -184,9 +185,33 @@ def from_yaml(cls, o, *args, **kwargs): # Check fiatlux configuration fiatlux = file_content.get('fiatlux') if fiatlux is not None: - SETUPFIT_FIXED_CONFIG['actions_'].append('fiatlux check_luxset') - if fiatlux.get("additional_errors"): - SETUPFIT_FIXED_CONFIG['actions_'].append('fiatlux check_additional_errors') + luxset = fiatlux['luxset'] + theoryid = file_content['theory']['theoryid'] + compute_in_setupfit = fiatlux.get('compute_in_setupfit', False) + try: + _ = loader.check_photonQED(theoryid, luxset) + log.info(f"Photon QED set found for {theoryid} with luxset {luxset}.") + except PhotonQEDNotFound: + if compute_in_setupfit: + log.warning( + f"Photon QED set for theory {theoryid} with luxset {luxset} not found. " + "It will be computed in vp-setupfit." + ) + else: + log.warning( + f"No photon set found for theory {theoryid} with luxset {luxset}. Consider " + "using `compute_in_setupfit` in the runcard to compute all replicas of the photon " + "in vp-setupfit. Otherwise n3fit " + "will take care of the photon computation. May impact performance." + ) + + if compute_in_setupfit: + log.info("Forcing photon computation with FiatLux during setupfit.") + # Since the photon will be computed, check that the luxset and additional_errors exist + SETUPFIT_FIXED_CONFIG['actions_'].append('fiatlux check_luxset_exists') + if fiatlux.get("additional_errors"): + SETUPFIT_FIXED_CONFIG['actions_'].append('fiatlux check_additional_errors') + SETUPFIT_FIXED_CONFIG['actions_'].append('fiatlux::theory compute_photon_to_disk') # Check positivity bound if file_content.get('positivity_bound') is not None: diff --git a/nnpdf_data/nnpdf_data/utils.py b/nnpdf_data/nnpdf_data/utils.py index fea46e285a..7013cfdbf3 100644 --- a/nnpdf_data/nnpdf_data/utils.py +++ b/nnpdf_data/nnpdf_data/utils.py @@ -102,6 +102,7 @@ def get_nnpdf_profile(profile_path=None): "validphys_cache_path", "hyperscan_path", "ekos_path", + "photons_qed_path", ]: # if there are any problems setting or getting these variable erroring out is more than justified absolute_var = nnpdf_share / pathlib.Path(profile_dict[var]).expanduser() diff --git a/validphys2/serverscripts/index-photon.py b/validphys2/serverscripts/index-photon.py new file mode 100755 index 0000000000..927fdb5d2d --- /dev/null +++ b/validphys2/serverscripts/index-photon.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +Generate an index with the existing internal or unpublished PDFS. +""" + +import pathlib +import json + +root = '/home/nnpdf/WEB/photons' + +glob = '*.tar' + +indexname = 'photondata.json' + +if __name__ == '__main__': + p = pathlib.Path(root) + files = p.glob(glob) + files = [f.name for f in files] + with (p/indexname).open('w') as f: + json.dump({'files':files}, f, separators=(',',':')) \ No newline at end of file diff --git a/validphys2/src/validphys/filters.py b/validphys2/src/validphys/filters.py index add8a3bf4b..7cedcea9a0 100644 --- a/validphys2/src/validphys/filters.py +++ b/validphys2/src/validphys/filters.py @@ -479,6 +479,14 @@ def check_luxset(luxset): log.info(f'{luxset} Lux pdf checked.') +def check_luxset_exists(fiatlux): + """Check that the Photon QED set for this theoryid and luxset exists""" + if fiatlux is not None: + luxset = fiatlux['luxset'] + luxset.load() + log.info(f'{luxset} Lux pdf checked.') + + def check_unpolarized_bc(unpolarized_bc): """Check that unpolarized PDF bound can be loaded normally.""" unpolarized_bc.load() diff --git a/validphys2/src/validphys/loader.py b/validphys2/src/validphys/loader.py index ed10582319..b27e4222dc 100644 --- a/validphys2/src/validphys/loader.py +++ b/validphys2/src/validphys/loader.py @@ -80,6 +80,10 @@ class EkoNotFound(LoadFailedError): pass +class PhotonQEDNotFound(LoadFailedError): + pass + + class TheoryMetadataNotFound(LoadFailedError): pass @@ -143,16 +147,19 @@ def __init__(self, profile=None): theories_path = pathlib.Path(profile["theories_path"]) resultspath = pathlib.Path(profile["results_path"]) ekos_path = pathlib.Path(profile["ekos_path"]) + photons_qed = pathlib.Path(profile["photons_qed_path"]) # Create the theories and results paths if they don't exist already theories_path.mkdir(exist_ok=True, parents=True) ekos_path.mkdir(exist_ok=True, parents=True) resultspath.mkdir(exist_ok=True, parents=True) + photons_qed.mkdir(exist_ok=True, parents=True) # And save them up self.commondata_folders = tuple(datapaths) self._theories_path = theories_path self._ekos_path = ekos_path + self._photons_qed_path = photons_qed self.resultspath = resultspath self._extremely_old_fits = set() self.nnprofile = profile @@ -210,6 +217,14 @@ def available_ekos(self): eko_path.parent.name.split("_")[1] for eko_path in self._theories_path.glob("*/eko.tar") } + @functools.cached_property + def available_photons(self): + """Return a string token for each of the available theories""" + return { + photon_path.name.split("photon_")[1] + for photon_path in self._photons_qed_path.glob("photon_*") + } + @property @functools.lru_cache def available_datasets(self): @@ -306,6 +321,16 @@ def check_eko(self, theoryID): raise EkoNotFound(f"Could not find eko {eko_path} in theory: {theoryID}") return eko_path + @functools.lru_cache + def check_photonQED(self, theoryID, luxset): + """Check the Photon QED set exists and return the path to it""" + photon_qed_path = self._photons_qed_path / f"photon_theoryID_{int(theoryID)}_fit_{luxset}" + if not photon_qed_path.exists(): + raise PhotonQEDNotFound( + f"Could not find Photon QED set {photon_qed_path} in theory: {int(theoryID)}" + ) + return photon_qed_path + @property def theorydb_folder(self): """Checks theory db file exists and returns path to it""" @@ -816,6 +841,16 @@ def eko_index(self): def eko_urls(self): return self.nnprofile['eko_urls'] + @property + @_key_or_loader_error + def photon_qed_index(self): + return self.nnprofile['photon_qed_index'] + + @property + @_key_or_loader_error + def photon_qed_urls(self): + return self.nnprofile['photon_qed_urls'] + @property @_key_or_loader_error def nnpdf_pdfs_urls(self): @@ -886,6 +921,13 @@ def remote_ekos(self): rt = self.remote_files(self.eko_urls, self.eko_index, thing="ekos") return {k[len(token) :]: v for k, v in rt.items()} + @property + @functools.lru_cache + def remote_photons(self): + token = 'photon_' + rt = self.remote_files(self.photon_qed_urls, self.photon_qed_index, thing="photons") + return {k[len(token) :]: v for k, v in rt.items()} + @property @functools.lru_cache def remote_nnpdf_pdfs(self): @@ -920,6 +962,10 @@ def downloadable_theories(self): def downloadable_ekos(self): return list(self.remote_ekos) + @property + def downloadable_photons(self): + return list(self.remote_photons) + @property def lhapdf_pdfs(self): return lhaindex.expand_index_names('*') @@ -1102,6 +1148,18 @@ def download_eko(self, thid): target_path = self._ekos_path / f"eko_{int(thid)}.tar" download_file(remote[thid], target_path) + def download_photonQED(self, thid, luxset: str): + """Download the Photon set for a given theory ID""" + remote = self.remote_photons + key = f"theoryID_{thid}_fit_{luxset}" + if key not in remote: + raise PhotonQEDNotFound( + f"Photon QED set for TheoryID {thid} and luxset {luxset} is not available in the remote server." + ) + # Check that we have the theory we need + target_path = self._photons_qed_path + download_and_extract(remote[key], target_path) + def download_vp_output_file(self, filename, **kwargs): try: root_url = self.nnprofile['reports_root_url'] diff --git a/validphys2/src/validphys/nnprofile_default.yaml b/validphys2/src/validphys/nnprofile_default.yaml index 85e4a351f4..f09ffe9559 100644 --- a/validphys2/src/validphys/nnprofile_default.yaml +++ b/validphys2/src/validphys/nnprofile_default.yaml @@ -30,6 +30,7 @@ theories_path: theories hyperscan_path: hyperscan validphys_cache_path: vp-cache ekos_path: ekos +photons_qed_path: photons_qed # It is possible to add extra folders in which to find data by filling up the data_path variable # the default path within the nnpdf_data package will always be added albeit with lower priority. @@ -61,6 +62,11 @@ eko_urls: eko_index: 'ekodata.json' +photon_qed_urls: + - 'https://data.nnpdf.science/photons/' + +photon_qed_index: 'photondata.json' + lhapdf_urls: - 'http://lhapdfsets.web.cern.ch/lhapdfsets/current/' nnpdf_pdfs_urls: diff --git a/validphys2/src/validphys/photon/compute.py b/validphys2/src/validphys/photon/compute.py index a4a3038d8b..d3effa8ec1 100644 --- a/validphys2/src/validphys/photon/compute.py +++ b/validphys2/src/validphys/photon/compute.py @@ -1,8 +1,10 @@ """Module that calls fiatlux to add the photon PDF.""" +from functools import lru_cache import logging import tempfile +from joblib import Parallel, delayed import numpy as np from scipy.integrate import trapezoid from scipy.interpolate import interp1d @@ -11,6 +13,8 @@ from eko import basis_rotation from eko.io import EKO from n3fit.io.writer import XGRID +from validphys.core import FKTableSpec +from validphys.loader import Loader, PhotonQEDNotFound from validphys.n3fit_data import replica_luxseed from . import structure_functions as sf @@ -21,6 +25,7 @@ # not the complete fiatlux runcard since some parameters are set in the code FIATLUX_DEFAULT = { "apfel": False, + "eps_base": 1e-5, "eps_rel": 1e-1, # extra precision on any single integration. "mum_proton": 2.792847356, # proton magnetic moment, from # http://pdglive.lbl.gov/DataBlock.action?node=S016MM which itself @@ -49,51 +54,52 @@ class Photon: - """Photon class computing the photon array with the LuxQED approach.""" - - def __init__(self, theoryid, lux_params, replicas): + def __init__( + self, + theoryid, + lux_params, + replicas: list[int], + save_to_disk=False, + force_computation=False, + q_in=100.0, + ): self.theoryid = theoryid self.lux_params = lux_params - - theory = theoryid.get_description() - fiatlux_runcard = FIATLUX_DEFAULT - # TODO: for the time being, Qedref=Qref and so alphaem running will always trigger - # This may be changed in the future in favor of a bool em_running in the runcard - fiatlux_runcard["qed_running"] = True - fiatlux_runcard["mproton"] = float(theory["MP"]) - - # precision on final integration of double integral - if "eps_base" in lux_params: - fiatlux_runcard["eps_base"] = lux_params["eps_base"] - log.warning(f"Using fiatlux parameter eps_base from runcard") - else: - fiatlux_runcard["eps_base"] = 1e-5 - log.info(f"Using default value for fiatlux parameter eps_base") - self.replicas = replicas - - # structure functions + self.save_to_disk = save_to_disk + self.q_in = q_in self.luxpdfset = lux_params["luxset"].load() - self.additional_errors = lux_params["additional_errors"] - self.luxseed = lux_params["luxseed"] - - if theory["PTO"] > 0: - path_to_F2 = theoryid.path / "fastkernel/FIATLUX_DIS_F2.pineappl.lz4" - path_to_FL = theoryid.path / "fastkernel/FIATLUX_DIS_FL.pineappl.lz4" + self.luxpdfset_members = self.luxpdfset.n_members - 1 + self.path_to_eko_photon = self.theoryid.path / "eko_photon.tar" - self.path_to_eko_photon = theoryid.path / "eko_photon.tar" - with EKO.read(self.path_to_eko_photon) as eko: - self.q_in = np.sqrt(eko.mu20) - - # set fiatlux - self.lux = {} - - mb_thr = theory["kbThr"] * theory["mb"] - mt_thr = theory["ktThr"] * theory["mt"] if theory["MaxNfPdf"] == 6 else 1e100 + # Compute or load photon_qin + if force_computation: + photon_in = self._compute_photon_set() + else: + try: + photon_in = self._load_photon() + except PhotonQEDNotFound: + photon_in = self._compute_photon_set() + self.photon_qin = photon_in + + def _load_photon(self): + """Load the photon resource using the Loader class.""" + loader = Loader() + path_to_photon = loader.check_photonQED(self.theoryid.id, self.luxpdfset._name) + log.info(f"Loading photon QED set from {path_to_photon}") + + # Load the needed replicas + photon_qin_array = {} + for replica in self.replicas: + # As input replica for the photon computation we take the MOD of the luxset_members to + # avoid failing due to limited number of replicas in the luxset + photonreplica = (replica % self.luxpdfset_members) or self.luxpdfset_members + photon_qin = np.load(path_to_photon / f"replica_{photonreplica}.npz")["photon_qin"] + photon_qin_array[replica] = photon_qin - self.interpolator = [] - self.integral = [] + return photon_qin_array + def _compute_photon_set(self): try: import fiatlux except ModuleNotFoundError as e: @@ -102,100 +108,193 @@ def __init__(self, theoryid, lux_params, replicas): "Please install fiatlux: `pip install nnpdf[qed]` or `pip install fiatlux`" ) from e - for replica in self.replicas: - # As input replica for the photon computation we take the MOD of the luxset_members to - # avoid failing due to limited number of replicas in the luxset - luxset_members = self.luxpdfset.n_members - 1 # - 1 because rep0 is included - photonreplica = (replica % luxset_members) or luxset_members - - f2lo = sf.F2LO(self.luxpdfset.members[photonreplica], theory) - - if theory["PTO"] > 0: - f2 = sf.InterpStructureFunction(path_to_F2, self.luxpdfset.members[photonreplica]) - fl = sf.InterpStructureFunction(path_to_FL, self.luxpdfset.members[photonreplica]) - if not np.isclose(f2.q2_max, fl.q2_max): - log.error( - "FKtables for FIATLUX_DIS_F2 and FIATLUX_DIS_FL have two different q2_max" - ) - fiatlux_runcard["q2_max"] = float(f2.q2_max) - else: - f2 = f2lo - fl = sf.FLLO() - # using a default value for q2_max - fiatlux_runcard["q2_max"] = 1e8 - - alpha = Alpha(theory, fiatlux_runcard["q2_max"]) + ######### + replicas = self.replicas + luxset = self.lux_params['luxset'].load() + fiatlux_runcard = self._setup_fiatlux_runcard() + ######### + + # Set fiatlux parameters + theory = self.theoryid.get_description() + mb_thr = theory["kbThr"] * theory["mb"] + mt_thr = theory["ktThr"] * theory["mt"] if theory["MaxNfPdf"] == 6 else 1e100 + f2lo_func = lambda member: sf.F2LO(member, theory) + if theory["PTO"] > 0: + path_to_F2 = self.theoryid.path / "fastkernel/FIATLUX_DIS_F2.pineappl.lz4" + path_to_FL = self.theoryid.path / "fastkernel/FIATLUX_DIS_FL.pineappl.lz4" + f2_func = lambda member: sf.InterpStructureFunction(path_to_F2, member) + fl_func = lambda member: sf.InterpStructureFunction(path_to_FL, member) + + # Use central replica to check q2_max + f2 = f2_func(luxset.central_member) + fl = fl_func(luxset.central_member) + if not np.isclose(f2.q2_max, fl.q2_max): + log.error( + "FKtables for FIATLUX_DIS_F2 and FIATLUX_DIS_FL have two different q2_max" + ) + raise ValueError( + "FKtables for FIATLUX_DIS_F2 and FIATLUX_DIS_FL have two different q2_max" + ) + fiatlux_runcard["q2_max"] = float(f2.q2_max) + else: + f2_func = f2lo_func + fl_func = lambda _: sf.FLLO() + + alpha = Alpha(theory, fiatlux_runcard["q2_max"]) + + if self.save_to_disk: + loader = Loader() + path_to_photon = ( + loader._photons_qed_path / f"photon_theoryID_{self.theoryid.id}_fit_{luxset._name}" + ) + path_to_photon.mkdir(parents=True, exist_ok=True) + + photon_qin_array = {} + for replica in replicas: + # Avoid failing due to limited number of replicas in the luxset + photonreplica = (replica % self.luxpdfset_members) or self.luxpdfset_members + f2lo = f2lo_func(luxset.members[photonreplica]) + f2 = f2_func(luxset.members[photonreplica]) + fl = fl_func(luxset.members[photonreplica]) with tempfile.NamedTemporaryFile(mode="w") as tmp: yaml.dump(fiatlux_runcard, tmp) - self.lux[replica] = fiatlux.FiatLux(tmp.name) + lux = fiatlux.FiatLux(tmp.name) + # we have a dict but fiatlux wants a yaml file # TODO : once that fiatlux will allow dictionaries # pass directly fiatlux_runcard + lux.PlugAlphaQED(alpha.alpha_em, alpha.qref) + lux.InsertInelasticSplitQ([mb_thr, mt_thr]) + lux.PlugStructureFunctions(f2.fxq, fl.fxq, f2lo.fxq) - self.lux[replica].PlugAlphaQED(alpha.alpha_em, alpha.qref) - self.lux[replica].InsertInelasticSplitQ([mb_thr, mt_thr]) - self.lux[replica].PlugStructureFunctions(f2.fxq, fl.fxq, f2lo.fxq) + photon_qin = np.array([lux.EvaluatePhoton(x, self.q_in**2).total for x in XGRID]) + photon_qin += self._generate_errors(replica) - photon_array = self.compute_photon_array(replica, photonreplica) - self.interpolator.append( - interp1d(XGRID, photon_array, fill_value="extrapolate", kind="cubic") - ) - self.integral.append(trapezoid(photon_array, XGRID)) + # fiatlux computes x * gamma(x) + photon_qin /= XGRID - self.integral = np.stack(self.integral, axis=-1) + if self.save_to_disk: + np.savez_compressed( + path_to_photon / f"replica_{photonreplica}.npz", photon_qin=photon_qin + ) + log.info(f"Saved photon replica {photonreplica} to {path_to_photon}") - def compute_photon_array(self, replica, photonreplica): - r""" - Compute the photon PDF for every point in the grid xgrid. + photon_qin_array[replica] = photon_qin - Parameters - ---------- - replica: int - replica id + return photon_qin_array - Returns - ------- - compute_photon_array: numpy.array - photon PDF at the fitting scale Q0 + def _setup_fiatlux_runcard(self): + fiatlux_runcard = FIATLUX_DEFAULT + + # TODO: for the time being, Qedref=Qref and so alphaem running will always trigger + # This may be changed in the future in favor of a bool em_running in the runcard + fiatlux_runcard["qed_running"] = True + fiatlux_runcard["mproton"] = float(self.theoryid.get_description()["MP"]) + + # precision on final integration of double integral + if "eps_base" in self.lux_params: + fiatlux_runcard["eps_base"] = self.lux_params["eps_base"] + log.warning(f"Using fiatlux parameter eps_base from runcard") + else: + log.info( + f"Using default value for fiatlux parameter eps_base = {fiatlux_runcard['eps_base']}" + ) + return fiatlux_runcard + + @property + def error_matrix(self): + """Generate error matrix to be used in generate_errors.""" + if not self.lux_params["additional_errors"]: + return None + extra_set = self.lux_params["additional_errors"].load() + qs = [self.q_in] * len(XGRID) + res_central = np.array(extra_set.central_member.xfxQ(22, XGRID, qs)) + res = [] + for idx_member in range(101, 107 + 1): + tmp = np.array(extra_set.members[idx_member].xfxQ(22, XGRID, qs)) + res.append(tmp - res_central) + # first index must be x, while second one must be replica index + return np.stack(res, axis=1) + + def _generate_errors(self, replica): """ - # Compute photon PDF - log.info(f"Computing photon") - photon_qin = np.array( - [self.lux[replica].EvaluatePhoton(x, self.q_in**2).total for x in XGRID] - ) - photon_qin += self.generate_errors(replica) - # fiatlux computes x * gamma(x) - photon_qin /= XGRID - # TODO : the different x points could be even computed in parallel + Generate LUX additional errors according to the procedure + described in sec. 2.5 of https://arxiv.org/pdf/1712.07053.pdf + """ + if self.error_matrix is None: + return np.zeros_like(XGRID) + log.info(f"Generating photon additional errors") + luxseed = self.lux_params.get("luxseed", None) + seed = replica_luxseed(replica, luxseed) + rng = np.random.default_rng(seed=seed) + u, s, _ = np.linalg.svd(self.error_matrix, full_matrices=False) + errors = u @ (s * rng.normal(size=7)) + return errors + + @lru_cache(maxsize=None) + def _evolve(self): + """Perform the EKOs to evolve the photon from q_in to Q0.""" + log.info(f"Evolving photon from q_in={self.q_in} GeV to Q0 using EKO...") + photon_qin_array = self.photon_qin + interpolator = [] + integral = [] - # Load eko and reshape it with EKO.read(self.path_to_eko_photon) as eko_photon: + # Check that qin mathces with the one in the EKO + if not np.isclose(self.q_in, np.sqrt(eko_photon.mu20)): + log.error( + f"Photon q_in {self.q_in} does not match the one in the EKO {np.sqrt(eko_photon.mu20)}" + ) + raise ValueError("Photon q_in does not match the one in the EKO") + # TODO : if the eko has not the correct grid we have to reshape it # it has to be done inside vp-setupfit + for replica in self.replicas: + photonreplica = (replica % self.luxpdfset_members) or self.luxpdfset_members + + # NB: the eko should contain a single operator + for _, elem in eko_photon.items(): + eko_op = elem.operator + + pdfs_init = np.zeros_like(eko_op[0, 0]) + for j, pid in enumerate(basis_rotation.flavor_basis_pids): + if pid == 22: + pdfs_init[j] = photon_qin_array[replica] + ph_id = j + elif pid not in self.luxpdfset.flavors: + continue + else: + pdfs_init[j] = np.array( + [ + self.luxpdfset.xfxQ(x, self.q_in, photonreplica, pid) / x + for x in XGRID + ] + ) + + pdfs_final = np.einsum("ajbk,bk", eko_op, pdfs_init) + + photon_Q0 = pdfs_final[ph_id] + photon_array = XGRID * photon_Q0 + interpolator.append( + interp1d(XGRID, photon_array, fill_value="extrapolate", kind="cubic") + ) + integral.append(trapezoid(photon_array, XGRID)) + + integral = np.stack(integral, axis=-1) + return integral, interpolator - # NB: the eko should contain a single operator - for _, elem in eko_photon.items(): - eko_op = elem.operator - - pdfs_init = np.zeros_like(eko_op[0, 0]) - for j, pid in enumerate(basis_rotation.flavor_basis_pids): - if pid == 22: - pdfs_init[j] = photon_qin - ph_id = j - elif pid not in self.luxpdfset.flavors: - continue - else: - pdfs_init[j] = np.array( - [self.luxpdfset.xfxQ(x, self.q_in, photonreplica, pid) / x for x in XGRID] - ) - - pdfs_final = np.einsum("ajbk,bk", eko_op, pdfs_init) - - photon_Q0 = pdfs_final[ph_id] + @property + def integral(self): + """Return the integral values.""" + integral, _ = self._evolve() + return integral - # we want x * gamma(x) - return XGRID * photon_Q0 + @property + def interpolator(self): + """Return the interpolator functions.""" + _, interpolator = self._evolve() + return interpolator def __call__(self, xgrid): """ @@ -219,31 +318,28 @@ def __call__(self, xgrid): axis=1, ) - @property - def error_matrix(self): - """Generate error matrix to be used in generate_errors.""" - if not self.additional_errors: - return None - extra_set = self.additional_errors.load() - qs = [self.q_in] * len(XGRID) - res_central = np.array(extra_set.central_member.xfxQ(22, XGRID, qs)) - res = [] - for idx_member in range(101, 107 + 1): - tmp = np.array(extra_set.members[idx_member].xfxQ(22, XGRID, qs)) - res.append(tmp - res_central) - # first index must be x, while second one must be replica index - return np.stack(res, axis=1) - def generate_errors(self, replica): - """ - Generate LUX additional errors according to the procedure - described in sec. 2.5 of https://arxiv.org/pdf/1712.07053.pdf - """ - if self.error_matrix is None: - return np.zeros_like(XGRID) - log.info(f"Generating photon additional errors") - seed = replica_luxseed(replica, self.luxseed) - rng = np.random.default_rng(seed=seed) - u, s, _ = np.linalg.svd(self.error_matrix, full_matrices=False) - errors = u @ (s * rng.normal(size=7)) - return errors +def compute_photon_to_disk(theoryid, fiatlux, maxcores): + """Function to compute the photon PDF set.""" + luxset = fiatlux['luxset'].load() + force_compute = fiatlux.get('compute_in_setupfit', False) + replicas = list(range(1, luxset.n_members)) + + # Return None and avoid pickling issues with the photon class. + def wrapper_fn(replica, theoryid, fiatlux, force_compute): + _ = Photon( + theoryid, + fiatlux, + replicas=[replica], + save_to_disk=force_compute, + force_computation=force_compute, + ) + return None + + log.info(f"Starting computation of the photon using {maxcores} effective cores...") + _ = Parallel(n_jobs=maxcores)( + delayed(wrapper_fn)( + replica=replica, theoryid=theoryid, fiatlux=fiatlux, force_compute=force_compute + ) + for replica in replicas + ) diff --git a/validphys2/src/validphys/scripts/vp_get.py b/validphys2/src/validphys/scripts/vp_get.py index 51f88ed4c9..2323db834d 100644 --- a/validphys2/src/validphys/scripts/vp_get.py +++ b/validphys2/src/validphys/scripts/vp_get.py @@ -21,8 +21,6 @@ from reportengine import colors from validphys.loader import FallbackLoader as Loader, LoadFailedError - - log = logging.getLogger() log.setLevel(logging.INFO) log.addHandler(colors.ColorHandler()) @@ -51,22 +49,24 @@ def main(): sys.exit(1) p.add_argument('resource_type', help="Type of the resource to be obtained. " "See --list for a list of resource types.") - p.add_argument('resource_name', help="Identifier of the resource.") + p.add_argument('resource_name', help="Identifier of the resource.", nargs='+') p.add_argument('--list', action=ListAction, loader=l, nargs=0, help="List available resources and exit.") args = p.parse_args() - tp = args.resource_type name = args.resource_name + if len(name) > 1 and tp != 'photonQED': + sys.exit("Only 'photonQED' resource type requires theoryID and luxset.") + try: f = getattr(l, f'check_{tp}') except AttributeError as e: sys.exit(f"No such resource {tp}") try: - res = f(name) + res = f(*name) except LoadFailedError as e: print(ErrorWithAlternatives(f"Could not find resource ({tp}): '{name}'.", name)) sys.exit("Failed to download resource.") diff --git a/validphys2/src/validphys/tests/photon/test_compute.py b/validphys2/src/validphys/tests/photon/test_compute.py index 274f6673fa..db93bda3cb 100644 --- a/validphys2/src/validphys/tests/photon/test_compute.py +++ b/validphys2/src/validphys/tests/photon/test_compute.py @@ -37,12 +37,16 @@ def test_parameters_init(): # we are not testing the photon here so we make it faster fiatlux_runcard['eps_base'] = 1e-1 - photon = Photon(test_theory, fiatlux_runcard, [1, 2, 3]) + photon = Photon( + test_theory, fiatlux_runcard, [1, 2, 3], save_to_disk=False, force_computation=True + ) np.testing.assert_equal(photon.replicas, [1, 2, 3]) np.testing.assert_equal(photon.luxpdfset._name, fiatlux_runcard["luxset"].name) - np.testing.assert_equal(photon.additional_errors.name, "LUXqed17_plus_PDF4LHC15_nnlo_100") - np.testing.assert_equal(photon.luxseed, fiatlux_runcard["luxseed"]) + np.testing.assert_equal( + photon.lux_params["additional_errors"].name, "LUXqed17_plus_PDF4LHC15_nnlo_100" + ) + np.testing.assert_equal(photon.lux_params["luxseed"], fiatlux_runcard["luxseed"]) np.testing.assert_equal(photon.path_to_eko_photon, test_theory.path / "eko_photon.tar") np.testing.assert_equal(photon.q_in, 100.0) @@ -58,7 +62,9 @@ def test_photon(): theory = test_theory.get_description() for replica in [1, 2, 3]: - photon = Photon(test_theory, fiatlux_runcard, [replica]) + photon = Photon( + test_theory, fiatlux_runcard, [replica], save_to_disk=False, force_computation=True + ) # set up fiatlux path_to_F2 = test_theory.path / "fastkernel/FIATLUX_DIS_F2.pineappl.lz4"