Skip to content

Commit 933bdc5

Browse files
Merge pull request #388 from diana-hep/what-a-nuisance
Expanded nuisance parameter support
2 parents 7cc6f17 + 3c60ccb commit 933bdc5

File tree

15 files changed

+2016
-406
lines changed

15 files changed

+2016
-406
lines changed

examples/tutorial_particle_physics/2a_parton_level_analysis.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@
450450
"outputs": [],
451451
"source": [
452452
"lhe.set_smearing(\n",
453-
" pdgids=[1,2,3,4,5,6,9,22,-1,-2,-3,-4,-5,-6], # Partons giving rise to jets\n",
453+
" pdgids=[1,2,3,4,5,6,9,21,-1,-2,-3,-4,-5,-6], # Partons giving rise to jets\n",
454454
" energy_resolution_abs=0.,\n",
455455
" energy_resolution_rel=0.1,\n",
456456
" pt_resolution_abs=None,\n",

examples/tutorial_particle_physics/A1_systematic_uncertainties.ipynb

Lines changed: 1113 additions & 65 deletions
Large diffs are not rendered by default.

madminer/analysis.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __init__(self, filename, disable_morphing=False, include_nuisance_parameters
4343
self.morphing_matrix,
4444
self.observables,
4545
self.n_samples,
46-
_,
46+
self.systematics,
4747
self.reference_benchmark,
4848
self.nuisance_parameters,
4949
self.n_events_generated_per_benchmark,
@@ -263,14 +263,17 @@ def xsecs(
263263
account. Otherwise, the list has to have the same number of elements as thetas, and each entry can specify
264264
nuisance parameters at nominal value (None) or a value of the nuisance parameters (ndarray).
265265
266-
include_nuisance_benchmarks : bool, optional
267-
Whether to include nuisance benchmarks if thetas is None. Default value: True.
266+
partition : {"train", "test", "validation", "all"}, optional
267+
Which event partition to use. Default: "all".
268268
269269
test_split : float, optional
270270
Fraction of events reserved for testing. Default value: 0.2.
271271
272-
partition : {"train", "test", "validation", "all"}, optional
273-
Which events to use. Default: "all".
272+
validation_split : float, optional
273+
Fraction of weighted events reserved for validation. Default value: 0.2.
274+
275+
include_nuisance_benchmarks : bool, optional
276+
Whether to include nuisance benchmarks if thetas is None. Default value: True.
274277
275278
batch_size : int, optional
276279
Size of the batches of events that are loaded into memory at the same time. Default value: 100000.
@@ -906,7 +909,7 @@ def _find_closest_benchmark(self, theta):
906909
if theta is None:
907910
return None
908911

909-
benchmarks = self._benchmark_array()[:self.n_benchmarks_phys]
912+
benchmarks = self._benchmark_array()[: self.n_benchmarks_phys]
910913
distances = [np.linalg.norm(benchmark - theta) for benchmark in benchmarks]
911914

912915
# Don't use benchmarks where we don't actually have events

madminer/core.py

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def __init__(self):
3939
self.default_benchmark = None
4040
self.morpher = None
4141
self.export_morphing = False
42-
self.systematics = None
42+
self.systematics = OrderedDict()
4343

4444
def add_parameter(
4545
self,
@@ -368,57 +368,75 @@ def set_morphing(
368368
morpher.n_components - n_predefined_benchmarks,
369369
)
370370

371-
def set_systematics(self, scale_variation=None, scales="together", pdf_variation=None):
371+
def reset_systematics(self):
372+
self.systematics = OrderedDict()
373+
374+
def add_systematics(
375+
self,
376+
effect,
377+
systematic_name=None,
378+
norm_variation=1.1,
379+
scale="mu",
380+
scale_variations=(0.5, 1.0, 2.0),
381+
pdf_variation="CT10",
382+
):
372383
"""
373-
Prepares the simulation of the effect of different nuisance parameters, including scale variations and PDF
374-
changes.
375384
376385
Parameters
377386
----------
378-
scale_variation : None or tuple of float, optional
379-
If not None, the regularization and / or factorization scales are varied. A tuple like (0.5,1.,2.)
380-
specifies the factors with which they are varied. Default value: None.
381-
382-
scales : {"together", "independent", "mur", "muf"}, optional
383-
Whether only the regularization scale ("mur"), only the factorization scale ("muf"), both simultanously
384-
("together") or both independently ("independent") are varied. Default value: "together".
385-
386-
pdf_variation : None or str, optional
387-
If not None, the PDFs are varied. The option is passed along to the `--pdf` option
387+
effect : {"norm", "scale", "pdf"}
388+
Type of the nuisance parameter. If "norm", it will affect the overall normalization of one or multiple
389+
samples in the process. If "scale", the nuisance parameter effect will be determined by varying
390+
factorization or regularization scales (depending on scale_variation and scales). If "pdf", the effect
391+
of the nuisance parameters will be determined by varying the PDF used.
392+
393+
systematic_name : None or str, optional
394+
395+
scale : {"mu", "mur", "muf"}, optional
396+
If type is "scale", this sets whether only the regularization scale ("mur"), only the factorization scale
397+
("muf"), or both simulatenously ("mu") are varied. Default value:
398+
"mu".
399+
400+
norm_variation : float, optional
401+
If type is "norm", this sets the relative effect of the nuisance parameter on the cross section at the
402+
"plus 1 sigma" variation. 1.1 corresponds to a 10% increase, 0.9 to a 10% decrease relative to the nominal
403+
cross section. Default value: 1.1.
404+
405+
scale_variations : tuple of float, optional
406+
If type is "scale", this sets how the regularization and / or factorization scales are varied. A tuple
407+
like (0.5,1.,2.) specifies the factors with which they are varied. Default value: (0.5,1.,2.0).
408+
409+
pdf_variation : str, optional
410+
If type is "pdf", defines the PDF set for the variation. The option is passed along to the `--pdf` option
388411
of MadGraph's systematics module. See https://cp3.irmp.ucl.ac.be/projects/madgraph/wiki/Systematics for a
389-
list. The option "CT10" would, as an example, run over all the eigenvectors of the CTEQ10 set.
412+
list. The option "CT10" would, as an example, run over all the eigenvectors of the CTEQ10 set. Default
413+
value: "CT10".
390414
391415
Returns
392416
-------
393417
None
394418
395419
"""
396420

397-
# Check input
398-
if scales not in ["together", "independent", "mur", "muf"]:
399-
raise ValueError("Unknown value {} for argument scales".format(scales))
400-
401-
# Save systematics setup
402-
self.systematics = OrderedDict()
403-
404-
if scale_variation is not None:
405-
scale_variation_string = ",".join([str(factor) for factor in scale_variation])
406-
407-
if scales == "together":
408-
self.systematics["mu"] = scale_variation_string
409-
elif scales == "independent":
410-
self.systematics["muf"] = scale_variation_string
411-
self.systematics["mur"] = scale_variation_string
412-
elif scales == "mur":
413-
self.systematics["mur"] = scale_variation_string
414-
elif scales == "muf":
415-
self.systematics["muf"] = scale_variation_string
416-
417-
if pdf_variation is not None:
418-
self.systematics["pdf"] = pdf_variation
419-
420-
if len(self.systematics) == 0:
421-
self.systematics = None
421+
# Default name
422+
if systematic_name is None:
423+
i = 0
424+
while "{}_{}".format(effect, i) in list(six.iterkeys(self.systematics)):
425+
i += 1
426+
systematic_name = "{}_{}".format(type, i)
427+
systematic_name = systematic_name.replace(" ", "_")
428+
systematic_name = systematic_name.replace("-", "_")
429+
430+
if effect == "pdf":
431+
self.systematics[systematic_name] = ("pdf", pdf_variation)
432+
elif effect == "scale":
433+
scale_variation_string = ",".join([str(factor) for factor in scale_variations])
434+
assert scale in ["mu", "mur", "muf"]
435+
self.systematics[systematic_name] = ("scale", scale, scale_variation_string)
436+
elif effect == "norm":
437+
self.systematics[systematic_name] = ("norm", norm_variation)
438+
else:
439+
raise ValueError("Unknown systematic type {}, has to be one of 'norm', 'scale', or 'pdf'!".format(type))
422440

423441
def load(self, filename, disable_morphing=False):
424442
"""
@@ -489,13 +507,13 @@ def load(self, filename, disable_morphing=False):
489507
logger.info("Did not find morphing setup.")
490508

491509
# Systematics setup
492-
if self.systematics is None:
510+
if len(self.systematics) == 0:
493511
logger.info("Did not find systematics setup.")
494512
else:
495513
logger.info("Found systematics setup with %s nuisance parameter groups", len(self.systematics))
496514

497515
for key, value in six.iteritems(self.systematics):
498-
logger.debug(" %s: %s", key, value)
516+
logger.debug(" %s: %s", key, " / ".join(str(x) for x in value))
499517

500518
def save(self, filename):
501519
"""
@@ -635,6 +653,7 @@ def run(
635653
temp_directory=None,
636654
initial_command=None,
637655
python2_override=False,
656+
systematics=None,
638657
):
639658

640659
"""
@@ -713,6 +732,9 @@ def run(
713732
Python 2.6 or Python 2.7. If you use systematics, make sure that the python interface of LHAPDF was compiled
714733
with the Python version you are using. Default: False.
715734
735+
systematics : None or list of str, optional
736+
If list of str, defines which systematics are used for this run.
737+
716738
Returns
717739
-------
718740
None
@@ -737,6 +759,7 @@ def run(
737759
temp_directory=temp_directory,
738760
initial_command=initial_command,
739761
python2_override=python2_override,
762+
systematics=systematics,
740763
)
741764

742765
def run_multiple(
@@ -755,6 +778,7 @@ def run_multiple(
755778
temp_directory=None,
756779
initial_command=None,
757780
python2_override=False,
781+
systematics=None,
758782
):
759783

760784
"""
@@ -825,6 +849,9 @@ def run_multiple(
825849
Python 2.6 or Python 2.7. If you use systematics, make sure that the python interface of LHAPDF was compiled
826850
with the Python version you are using. Default: False.
827851
852+
systematics : None or list of str, optional
853+
If list of str, defines which systematics are used for these runs.
854+
828855
Returns
829856
-------
830857
None
@@ -867,6 +894,14 @@ def run_multiple(
867894
]
868895
)
869896

897+
# Systematics
898+
if systematics is None:
899+
systematics_used = self.systematics
900+
else:
901+
systematics_used = OrderedDict()
902+
for key in systematics:
903+
systematics_used[key] = self.systematics[key]
904+
870905
# Loop over settings
871906
i = 0
872907
mg_scripts = []
@@ -898,9 +933,9 @@ def run_multiple(
898933
logger.info(" Log file: %s", log_file_run)
899934

900935
# Check input
901-
if run_card_file is None and self.systematics is not None:
936+
if run_card_file is None and self._check_pdf_or_scale_variation(systematics_used):
902937
logger.warning(
903-
"Warning: No run card given, but systematics set up. The correct systematics"
938+
"Warning: No run card given, but PDF or scale variation set up. The correct systematics"
904939
" settings are not set automatically. Make sure to set them correctly!"
905940
)
906941

@@ -918,7 +953,7 @@ def run_multiple(
918953
export_run_card(
919954
template_filename=run_card_file,
920955
run_card_filename=mg_process_directory + "/" + new_run_card_file,
921-
systematics=self.systematics,
956+
systematics=systematics_used,
922957
)
923958

924959
# Copy Pythia card
@@ -983,3 +1018,9 @@ def run_multiple(
9831018
"folders:\n\n%s\n\n",
9841019
expected_event_files,
9851020
)
1021+
1022+
def _check_pdf_or_scale_variation(self, systematics):
1023+
for value in six.itervalues(systematics):
1024+
if value[0] in ["pdf", "scale"]:
1025+
return True
1026+
return False

0 commit comments

Comments
 (0)