Skip to content

Commit

Permalink
Merge pull request #8 from MadAnalysis/improve_cutflow
Browse files Browse the repository at this point in the history
Improvement on code structure
  • Loading branch information
jackaraz authored Sep 30, 2022
2 parents 24f5f1d + 6ee934f commit 0baf63e
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 115 deletions.
6 changes: 6 additions & 0 deletions docs/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
* Improve the logger output
([#7](https://github.com/MadAnalysis/ma5_expert/pull/7)).

* Improve CLs computer.
([#8](https://github.com/MadAnalysis/ma5_expert/pull/8))

* Improved cutflow structure.
([#8](https://github.com/MadAnalysis/ma5_expert/pull/8))

## Bug fixes
* Make exceptions accessible through `ma5.system` module
([#7](https://github.com/MadAnalysis/ma5_expert/pull/7)).
Expand Down
46 changes: 42 additions & 4 deletions src/ma5_expert/backend/ma5_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,40 @@
import os, sys

from ma5_expert.system.exceptions import MadAnalysisPath
from typing import Optional, Text
from typing import Optional, Text, Union
from enum import Enum, auto


class PADType(Enum):
PAD = auto()
PADForSFS = auto()
PAD = "PAD"
PADForSFS = "PADForSFS"

def __str__(self):
return self.value

def __repr__(self):
return self.value


class ExpectationAssumption(Enum):
APRIORI = "apriori"
APOSTERIORI = "aposteriori"

def __str__(self):
return self.value

def __repr__(self):
return self.value

@classmethod
def get(cls, value):
if isinstance(value, ExpectationAssumption):
return value
elif isinstance(value, str):
if value.lower() == str(ExpectationAssumption.APRIORI):
return ExpectationAssumption.APRIORI
else:
return ExpectationAssumption.APOSTERIORI


@dataclass
Expand Down Expand Up @@ -72,14 +99,21 @@ def __post_init__(self):
if not self.ma5_main.session_info.has_padsfs:
self.ma5_main.session_info.has_padsfs = self.enforce_padforsfs

def get_run_recast(self, sample_path: Text, padtype: PADType):
def get_run_recast(
self,
sample_path: Text,
padtype: PADType,
expectation_assumption: Union[ExpectationAssumption, Text] = ExpectationAssumption.APRIORI,
):
"""
Get run recast class
Parameters
----------
sample_path: Text
PAth of the MadAnalysis workspace
expectation_assumption: ExpectationAssumption
assumption on expectation
Returns
-------
Expand All @@ -88,6 +122,10 @@ def get_run_recast(self, sample_path: Text, padtype: PADType):
from logging import DEBUG
from madanalysis.misc.run_recast import RunRecast

self.ma5_main.recasting.expectation_assumption = str(
ExpectationAssumption.get(expectation_assumption)
)

run_recast = RunRecast(self.ma5_main, sample_path)
if padtype == PADType.PAD:
run_recast.pad = os.path.join(self.madanalysis_path, "tools/PAD")
Expand Down
85 changes: 19 additions & 66 deletions src/ma5_expert/cutflow/cut.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from math import sqrt

from ma5_expert.system.exceptions import InvalidInput

from typing import List, Any, Union, Tuple, Text, Dict, Sequence, Optional
from dataclasses import dataclass, field
from typing import Any, Text, Optional
import logging

log = logging.getLogger("ma5_expert")


@dataclass
class Cut:
"""
Cut object
Expand All @@ -34,59 +33,15 @@ class Cut:
luminosity [fb^-1]
"""

def __init__(
self,
name: Optional[Text] = "__unknown_cut__",
Nentries: Optional[int] = None,
sumw: Optional[float] = None,
sumw2: Optional[float] = None,
previous_cut: Optional[Any] = None,
initial_cut: Optional[Any] = None,
xsec: Optional[float] = None,
Nevents: Optional[float] = None,
lumi: Optional[float] = None,
):

self.id = name # Name of the cut
self.Nentries = Nentries if Nentries is not None else 0 # Number of MC events
self._sumW = sumw # sum of weights
self._sumW2 = sumw2 # sum of square of the weights
self._initial_cut = initial_cut
self._previous_cut = previous_cut
self._lumi = lumi
self._xsection = xsec

if Nevents is not None:
self._Nevents = Nevents

@property
def sumW(self):
"""
Sum of weights
"""
return self._sumW

@property
def xsec(self) -> float:
"""
Cross section [pb]
"""
if hasattr(self, "_xsection"):
return self._xsection
else:
return -1

@xsec.setter
def xsec(self, val: float) -> None:
self._xsection = xsec
if hasattr(self, "_Nevents"):
delattr(self, "_Nevents")

@property
def lumi(self):
if self._lumi is not None:
return self._lumi
return -1
name: Optional[Text] = "__unknown_cut__"
Nentries: Optional[int] = None
sumW: Optional[float] = None
sumW2: Optional[float] = None
_previous_cut: Optional[Any] = field(default=None, repr=False)
_initial_cut: Optional[Any] = field(default=None, repr=False)
xsec: Optional[float] = None
_Nevents: Optional[float] = field(default=None, repr=False)
lumi: float = -1.0

@property
def eff(self):
Expand All @@ -98,7 +53,7 @@ def eff(self):
try:
return self.sumW / self._initial_cut.sumW
except ZeroDivisionError as err:
return 0.
return 0.0
else:
try:
return float(self.Nevents) / self._initial_cut.Nevents
Expand Down Expand Up @@ -149,7 +104,7 @@ def mc_rel_eff(self):
try:
return self.Nentries / self._previous_cut.Nentries
except ZeroDivisionError as err:
log.warning("Previous entry has no MC event")
log.warning(f"Previous entry (cut named {self._previous_cut.name}) has no MC event")
return 0.0

return -1
Expand All @@ -159,21 +114,19 @@ def mc_unc(self) -> float:
"""
Monte Carlo uncertainty
"""
if self.Nentries > 0 and self._lumi is not None:
return self.Nevents * sqrt(
self.eff * (1.0 - self.eff) / float(self.Nentries)
)
if self.Nentries > 0 and self.lumi > 0:
return self.Nevents * sqrt(self.eff * (1.0 - self.eff) / float(self.Nentries))

return 0.0

@property
def Nevents(self) -> float:
if hasattr(self, "_Nevents"):
if self._Nevents is not None:
return self._Nevents
else:
if self.lumi >= 0.0:
if self.xsec >= 0.0:
return self.xsec * self.eff * 1000.0 * self._lumi
return self.xsec * self.eff * 1000.0 * self.lumi
else:
return self.eff * self._initial_cut.Nevents
else:
Expand All @@ -183,7 +136,7 @@ def Nevents(self) -> float:
def __repr__(self):
nentries = self.Nentries if self.Nentries is not None else -1
txt = (
f" * {self.id} : \n"
f" * {self.name} : \n"
+ f" - Number of Entries : {nentries:.0f}\n"
+ f" - Number of Events : {self.Nevents:.3f} ± {self.mc_unc:.3f}(ΔMC)\n"
+ f" - Cut & Rel Efficiency : {self.eff:.3f}, {self.rel_eff:.3f}\n"
Expand Down
8 changes: 4 additions & 4 deletions src/ma5_expert/cutflow/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def xsec(self):
@xsec.setter
def xsec(self, val: float):
for cut in self:
cut.xsec = xsec
cut.xsec = val

@property
def lumi(self):
Expand All @@ -62,7 +62,7 @@ def lumi(self):
@lumi.setter
def lumi(self, val: float):
for cut in self:
cut._lumi = lumi
cut._lumi = val

@property
def CutNames(self):
Expand All @@ -78,11 +78,11 @@ def items(self):
return ((ix, cut) for ix, cut in enumerate(self._data))

def keys(self):
return (cut.id for cut in self._data)
return (cut.name for cut in self._data)

def getCut(self, id):
for cut in self:
if cut.id == id:
if cut.name == id:
return cut

return None
Expand Down
18 changes: 9 additions & 9 deletions src/ma5_expert/cutflow/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
log = logging.getLogger("ma5_expert")


class Collection(object):
class Collection:
def __init__(self, cutflow_path="", saf_file=False, **kwargs):
"""
Expand Down Expand Up @@ -83,12 +83,12 @@ def _readCollection(
name="Initial",
Nentries=int(cutflow[i].split()[0])
+ int(cutflow[i].split()[1]),
sumw=float(cutflow[i + 1].split()[0])
sumW=float(cutflow[i + 1].split()[0])
+ float(cutflow[i + 1].split()[1]),
sumw2=float(cutflow[i + 2].split()[0])
sumW2=float(cutflow[i + 2].split()[0])
+ float(cutflow[i + 2].split()[1]),
xsec=xsec,
Nevents=nevents,
_Nevents=nevents,
lumi=self.lumi,
)
currentSR.addCut(current_cut)
Expand All @@ -99,13 +99,13 @@ def _readCollection(
name=cutflow[i].split('"')[1],
Nentries=int(cutflow[i + 1].split()[0])
+ int(cutflow[i + 1].split()[1]),
sumw=float(cutflow[i + 2].split()[0])
sumW=float(cutflow[i + 2].split()[0])
+ float(cutflow[i + 2].split()[1]),
sumw2=float(cutflow[i + 3].split()[0])
sumW2=float(cutflow[i + 3].split()[0])
+ float(cutflow[i + 3].split()[1]),
xsec=xsec,
previous_cut=currentSR[-1],
initial_cut=currentSR[0],
_previous_cut=currentSR[-1],
_initial_cut=currentSR[0],
lumi=self.lumi,
)
currentSR.addCut(current_cut)
Expand Down Expand Up @@ -196,5 +196,5 @@ def get_alive(self):
def regiondata(self):
regdat = {}
for k, i in self.items():
regdat[k] = i.regiondata[i.id]
regdat[k] = i.regiondata[i.name]
return regdat
26 changes: 12 additions & 14 deletions src/ma5_expert/cutflow/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def write_comparison_table(self, *args, **kwargs):
txt += "\\\ \\hline\n"
# write cutflow
for cutID, cut in self.ref_sample[SR].items():
name = cut.id
name = cut.name
if "$" not in name:
name = name.replace("_", " ")
txt += " " + name.ljust(40, " ") + "& "
Expand All @@ -183,7 +183,7 @@ def write_comparison_table(self, *args, **kwargs):
if raw:
txt += tmp.format(
scientific_LaTeX(cut.Nentries, sty=event_style),
cut.raw_rel_eff,
cut.mc_rel_eff,
)
else:
if not (MCunc and cut.Nentries > 0):
Expand Down Expand Up @@ -222,7 +222,7 @@ def write_comparison_table(self, *args, **kwargs):
if raw:
txt += tmp.format(
scientific_LaTeX(smp[cutID].Nentries, sty=event_style),
smp[cutID].raw_rel_eff,
smp[cutID].mc_rel_eff,
)
else:
if not (MCunc and smp[cutID].Nentries > 0):
Expand Down Expand Up @@ -252,12 +252,15 @@ def write_comparison_table(self, *args, **kwargs):
+ " "
)
if raw:
rel_eff = abs(
1 - (smp[cutID].raw_rel_eff / cut.raw_rel_eff)
)
try:
rel_eff = abs(
1 - (smp[cutID].mc_rel_eff / cut.mc_rel_eff)
)
except ZeroDivisionError as err:
rel_eff = -1
txt += tmp.format(
scientific_LaTeX(smp[cutID].Nentries, sty=event_style),
smp[cutID].raw_rel_eff,
smp[cutID].mc_rel_eff,
rel_eff * 100.0,
)
else:
Expand Down Expand Up @@ -420,7 +423,7 @@ def write_signal_comparison_table(self, *args, **kwargs):
txt += "\\\ \\hline\n"
# write cutflow
for cutID, cut in self.ref_sample[SR].items():
name = cut.id
name = cut.name
if "$" not in name:
name = name.replace("_", " ")
txt += " " + name.ljust(40, " ") + "& "
Expand Down Expand Up @@ -578,12 +581,7 @@ def WriteMake(self, file, make=True):
+ "clean:\n"
+ "\trm -f *.aux *.log *.out *.toc *.blg *.dvi *.t1 *.1 *.mp *spl *.lol *Notes.bib\n"
)
if make:
try:
file.close()
os.system("make")
except:
print("Compilation failed.")

else:
raise ValueError("Can not find " + file.name)

Expand Down
Loading

0 comments on commit 0baf63e

Please sign in to comment.