diff --git a/docs/source/data_theory/projections.rst b/docs/source/data_theory/projections.rst index 926ff55b..d1704b6f 100644 --- a/docs/source/data_theory/projections.rst +++ b/docs/source/data_theory/projections.rst @@ -73,10 +73,14 @@ and systematic uncertainties) as ``.yaml`` files in the standard SMEFiT format w .. code-block:: bash - smefit PROJ --lumi /path/to/projection_runcard.yaml - -where the desired luminosity in :math:`{\rm fb}^{-1}` can be specified by replacing ````. The ``projection_runcard`` -specifies which datasets need to be extrapolated, by which factor to reduce the systematics, and sets the necessary paths: + smefit PROJ --lumi --noise /path/to/projection_runcard.yaml + +where ```` specifies the luminosity of the projection in :math:`{\rm fb}^{-1}`. The noise level ```` +can be either ``L0`` are ``L1`` corresponding to either level 0 or level 1 projections respectively. In level 0 projections, +the experimental central value coincides exactly with the theory prediction, while the experimental central values are fluctuated around +the theory prediction according to the experimental uncertainties in case of level 1. If ```` is not specified, level 0 +is assumed. If ```` is not specified, the original luminosities are kept and the uncertainties are not rescaled. +The ``projection_runcard`` specifies which datasets need to be extrapolated, by which factor to reduce the systematics, and sets the necessary paths: .. code-block:: yaml diff --git a/src/smefit/cli/__init__.py b/src/smefit/cli/__init__.py index 618b28e4..6a077851 100644 --- a/src/smefit/cli/__init__.py +++ b/src/smefit/cli/__init__.py @@ -217,24 +217,18 @@ def report(report_card: pathlib.Path): type=float, default=None, required=False, - help="Adjusts the statistical uncertainties according to the specified luminosity", + help="Adjusts the statistical uncertainties according to the specified luminosity. If not specified, the original " + "uncertainties are kept and the central values are fluctuates according to the specified noise level.", ) @click.option( - "--closure", - type=bool, - is_flag=True, - default=False, - help="Produces datasets under the SM", + "--noise", + type=str, + default="L0", + required=False, + help="Noise level for the projection, choose between L0 or L1. Assumes L0 by default.", ) -def projection(projection_card: pathlib.Path, lumi: float, closure: bool): +def projection(projection_card: pathlib.Path, lumi: float, noise: str): r"""Compute projection for specified dataset""" - if (lumi is not None) ^ closure: - projection_setup = Projection.from_config(projection_card) - projection_setup.build_projection(lumi, closure) - else: - print(lumi, closure) - print( - "Usage: specify exclusively either a luminosity in fb-1 after --lumi or run a SM closure test with --closure" - ) - sys.exit() + projection_setup = Projection.from_config(projection_card) + projection_setup.build_projection(lumi, noise) diff --git a/src/smefit/projections/__init__.py b/src/smefit/projections/__init__.py index 0e10c9f1..8998934d 100644 --- a/src/smefit/projections/__init__.py +++ b/src/smefit/projections/__init__.py @@ -183,15 +183,18 @@ def rescale_stat(stat, lumi_old, lumi_new): fred_stat = np.sqrt(lumi_old / lumi_new) return stat * fred_stat - def build_projection(self, lumi_new, closure): + def build_projection(self, lumi_new=None, noise="L0"): """ Constructs runcard for projection by updating the central value and statistical and systematic uncertainties Parameters ---------- - lumi_new: float - Adjusts the statistical uncertainties according to the specified luminosity + lumi_new: float, optional + Adjusts the statistical uncertainties according to the specified luminosity lumi_new. If not specified, + the uncertainties are left unchanged and the central values are fluctuated according to the noise level + noise: str + Noise level for the projection, choose between L0 or L1 closure: bool Set to true for a L1 closure test (no rescaling, only cv gets fluctuated according to original uncertainties) @@ -266,7 +269,7 @@ def build_projection(self, lumi_new, closure): th_covmat = self.datasets.ThCovMat[idxs, idxs] - if not closure: + if lumi_new is not None: # if all stats are zero, we only have access to the total error which we rescale by 1/3 (compromise) no_stats = not np.any(stat) if no_stats: @@ -310,8 +313,10 @@ def build_projection(self, lumi_new, closure): if self.use_theory_covmat: newcov += th_covmat - # add L1 noise to cv - cv_projection = np.random.multivariate_normal(cv[idxs], newcov) + # add Gaussian noise to central values in case of L1 and leave them unchanged in case of L0 + cv_projection = cv[idxs] + if noise == "L1": + cv_projection = np.random.multivariate_normal(cv[idxs], newcov) # replace cv with updated central values if len(cv_projection) > 1: @@ -323,7 +328,7 @@ def build_projection(self, lumi_new, closure): projection_folder.mkdir(exist_ok=True) if projection_folder != self.commondata_path: - if not closure: + if lumi_new is not None: with open( f"{projection_folder}/{dataset_name}_proj.yaml", "w" ) as file: @@ -338,7 +343,7 @@ def build_projection(self, lumi_new, closure): sys.exit() # copy corresponding theory predictions with _proj appended to filename - if not closure: + if lumi_new is not None: shutil.copy( self.theory_path / f"{dataset_name}.json", self.theory_path / f"{dataset_name}_proj.json",