Skip to content

Commit 2c58d49

Browse files
committed
add the possibility of getting also the CV
1 parent 145aa15 commit 2c58d49

File tree

3 files changed

+95
-28
lines changed

3 files changed

+95
-28
lines changed

validphys2/src/validphys/core.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from __future__ import generator_stop
1010

1111
from collections import namedtuple
12+
from contextlib import contextmanager
1213
import re
1314
import enum
1415
import functools
@@ -78,10 +79,33 @@ def __dir__(self):
7879
PDFSETS = _PDFSETS()
7980

8081
class PDF(TupleComp):
82+
"""Wrapper class for the validphys PDF object to easily manage
83+
both Monte Carlo and Hessian sets
84+
85+
Offers a context manager (``enable_central_value``) to include the central value
86+
in Monte Carlo sets.
87+
88+
Examples
89+
--------
90+
>>> from validphys.api import API
91+
>>> from validphys.convolution import predictions
92+
>>> args = {"dataset_input":{"dataset": "ATLASTTBARTOT"}, "theoryid":200, "use_cuts":"internal"}
93+
>>> ds = API.dataset(**args)
94+
>>> pdf = API.pdf(pdf="NNPDF40_nnlo_as_01180")
95+
>>> with pdf.enable_central_value():
96+
>>> preds_with_cv = predictions(ds, pdf)
97+
>>> preds_no_cv = predictions(ds, pdf)
98+
>>> len(preds_with_cv.columns)
99+
101
100+
>>> len(preds_no_cv.columns)
101+
100
102+
"""
81103

82104
def __init__(self, name):
83105
self.name = name
84106
self._plotname = name
107+
self._lhapdfset = None
108+
self._include_cv = False
85109
super().__init__(name)
86110

87111

@@ -145,13 +169,14 @@ def rescale_factor(self):
145169
else:
146170
return 1
147171

148-
@functools.lru_cache(maxsize=16)
149172
def load(self):
150-
return LHAPDFSet(self.name, self.nnpdf_error)
173+
if self._lhapdfset is None:
174+
self._lhapdfset = LHAPDFSet(self.name, self.nnpdf_error)
175+
return self._lhapdfset
151176

152177
@functools.lru_cache(maxsize=2)
153178
def load_t0(self):
154-
"""Load the PDF as a t0 set"""
179+
"""Reload the PDF as a t0 set"""
155180
return LHAPDFSet(self.name, LHAPDFSet.erType_ER_MCT0)
156181

157182
def __str__(self):
@@ -164,7 +189,7 @@ def __len__(self):
164189

165190
@property
166191
def nnpdf_error(self):
167-
"""Return the NNPDF error tag, used to build the `LHAPDFSet` objeect"""
192+
"""Return the NNPDF error tag, used to build the `LHAPDFSet` object"""
168193
error = self.ErrorType
169194
if error == "replicas":
170195
return LHAPDFSet.erType_ER_MC
@@ -203,6 +228,8 @@ def grid_values_index(self):
203228
len(pdf))`` for Monte Carlo sets, because replica 0 is not selected
204229
and ``range(0, len(pdf))`` for hessian sets.
205230
231+
If ``include_cv`` is set to True, add a central value column for member 0
232+
for Monte Carlo error sets
206233
207234
Returns
208235
-------
@@ -215,7 +242,10 @@ def grid_values_index(self):
215242
"""
216243
err = self.nnpdf_error
217244
if err is LHAPDFSet.erType_ER_MC:
218-
return range(1, len(self))
245+
if self._include_cv:
246+
return ["CV"] + list(range(1, len(self)))
247+
else:
248+
return range(1, len(self))
219249
elif err in (LHAPDFSet.erType_ER_SYMEIG, LHAPDFSet.erType_ER_EIG, LHAPDFSet.erType_ER_EIG90):
220250
return range(0, len(self))
221251
else:
@@ -228,6 +258,17 @@ def get_members(self):
228258
"""
229259
return len(self.grid_values_index)
230260

261+
@contextmanager
262+
def enable_central_value(self):
263+
"""Context manager within which the central value is included
264+
regardless of the error type of the PDF set"""
265+
# Get a reference to the base PDF set of this class
266+
pdfset = self.load()
267+
pdfset.include_cv = True
268+
self._include_cv = True
269+
yield
270+
self._include_cv = False
271+
pdfset.include_cv = False
231272

232273

233274
kinlabels_latex = CommonData.kinLabel_latex.asdict()

validphys2/src/validphys/lhapdfset.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,40 @@ class PDFErrorType(NamedTuple):
6262

6363

6464
class LHAPDFSet:
65-
"""Wrapper for the lhapdf python interface"""
65+
"""Wrapper for the lhapdf python interface.
66+
67+
Once instantiated this class will load the PDF set according to whether it is to be
68+
treated as a T0 set (only the CV) or not.
69+
70+
It is possible to control the LHAPDF verbosity with the flag ``lhapdf_verbosity``.
71+
72+
For Monte Carlo sets the central value (member 0) is by default not included when taking
73+
the resutls for all members (i.e., when using ``grid_values``).
74+
However, it is possible to add member 0 by changing the ``include_cv`` attribute to True.
75+
76+
Temporarily: it exposes all libNNPDF attributes that were exposed and used prior to
77+
the introduction of this class
78+
"""
6679

6780
def __init__(self, name, error_type, lhapdf_verbosity=0):
81+
log.info("PDF: %s ErrorType: %s", name, error_type.description)
6882
if isinstance(error_type, int):
6983
# libNNPDF error types were int
7084
error_type = _libNNPDF_errors[error_type]
7185
self._name = name
7286
self._error_type = error_type
87+
# If at this point we already know this is a T0 set, load only the CV
88+
if error_type.t0:
89+
self._lhapdf_set = [lhapdf.mkPDF(name)]
90+
else:
91+
self._lhapdf_set = lhapdf.mkPDFs(name)
7392
self._flavors = None
74-
self._lhapdf_set = lhapdf.mkPDFs(name)
7593
self._libNNPDF_set = None
76-
self.legacy_interface()
77-
log.info("PDF: %s ErrorType: %s", name, error_type.description)
94+
self.include_cv = False
95+
# Set the verbosity of LHAPDF
7896
lhapdf.setVerbosity(lhapdf_verbosity)
97+
# Prepare a Legacy Interface
98+
self.legacy_interface()
7999

80100
def legacy_interface(self):
81101
"""Setting some methods and attribute as per libNNPDF specs"""
@@ -105,7 +125,7 @@ def members(self):
105125
"""
106126
if self.is_t0:
107127
return self._lhapdf_set[0:1]
108-
if self.is_monte_carlo:
128+
if self.is_monte_carlo and not self.include_cv:
109129
return self._lhapdf_set[1:]
110130
return self._lhapdf_set
111131

@@ -139,11 +159,15 @@ def flavors(self):
139159
self._flavors = self.members[0].flavors()
140160
return self._flavors
141161

142-
def grid_values(self, flavors, xgrid, qgrid):
162+
def grid_values(self, flavors: np.ndarray, xgrid: np.ndarray, qgrid: np.ndarray):
143163
"""Reimplementation of libNNPDF grid_values
144164
The return shape is
145165
(members, flavors, xgrid, qgrid)
146166
167+
Return
168+
------
169+
ndarray of shape (members, flavors, xgrid, qgrid)
170+
147171
Examples
148172
--------
149173
>>> import numpy as np

validphys2/src/validphys/results.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ class NNPDFDataResult(Result):
4848
"""A result fills its values from a pandas dataframe
4949
For legacy (libNNPDF) compatibility, falls back to libNNPDF attributes"""
5050

51-
def __init__(self, dataobj=None, central_value=None):
51+
def __init__(self, dataobj):
5252
# This class is used by both validphys and libNNPDF objects
53-
# when central_value is not explictly passed, fallback to
54-
# libNNPDF object .get_cv()
55-
if central_value is None:
53+
# At this point only the result of the ThPredictions is a vp object
54+
# which includes a special CV column which needs to be pop'd
55+
try:
56+
central_value = dataobj.pop("CV")
57+
except AttributeError:
5658
central_value = dataobj.get_cv()
5759
self._central_value = np.array(central_value).squeeze()
5860

@@ -78,8 +80,8 @@ def std_error(self):
7880

7981

8082
class DataResult(NNPDFDataResult):
81-
def __init__(self, dataobj, covmat, sqrtcovmat, central_value=None):
82-
super().__init__(dataobj, central_value=central_value)
83+
def __init__(self, dataobj, covmat, sqrtcovmat):
84+
super().__init__(dataobj)
8385
self._covmat = covmat
8486
self._sqrtcovmat = sqrtcovmat
8587

@@ -105,17 +107,19 @@ class ThPredictionsResult(NNPDFDataResult):
105107
"""Class holding theory prediction
106108
For legacy purposes it still accepts libNNPDF datatypes, but prefers python-pure stuff
107109
"""
108-
def __init__(self, dataobj, stats_class, label=None, central_value=None):
110+
def __init__(self, dataobj, stats_class, label=None):
111+
super().__init__(dataobj)
109112
self.stats_class = stats_class
110113
self.label = label
111-
# Ducktype the input into numpy arrays
114+
# Ducktype the input into numpy arrays for the rawdata
115+
# TODO: once all of them are dataframes, the rawdata could be made of
116+
# dataframes as well
112117
try:
113118
self._std_error = dataobj.std(axis=1).to_numpy()
114119
self._rawdata = dataobj.to_numpy()
115120
except AttributeError:
116121
self._std_error = dataobj.get_error()
117122
self._rawdata = dataobj.get_data()
118-
super().__init__(dataobj, central_value=central_value)
119123

120124
@property
121125
def std_error(self):
@@ -145,20 +149,18 @@ def from_convolution(cls, pdf, dataset):
145149
datasets = (dataset,)
146150

147151
try:
148-
all_preds = []
149-
all_centrals = []
150-
for d in datasets:
151-
all_preds.append(predictions(d, pdf))
152-
all_centrals.append(central_predictions(d, pdf))
152+
with pdf.enable_central_value():
153+
th_predictions = pd.concat(predictions(d, pdf) for d in datasets)
153154
except PredictionsRequireCutsError as e:
154155
raise PredictionsRequireCutsError("Predictions from FKTables always require cuts, "
155156
"if you want to use the fktable intrinsic cuts set `use_cuts: 'internal'`") from e
156-
th_predictions = pd.concat(all_preds)
157-
central_values = pd.concat(all_centrals)
157+
# For Hessian sets
158+
if "CV" not in th_predictions:
159+
th_predictions["CV"] = th_predictions[0]
158160

159161
label = cls.make_label(pdf, dataset)
160162

161-
return cls(th_predictions, pdf.stats_class, label, central_value=central_values)
163+
return cls(th_predictions, pdf.stats_class, label)
162164

163165

164166
class PositivityResult(StatsResult):

0 commit comments

Comments
 (0)