-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathio.py
More file actions
166 lines (147 loc) · 5.11 KB
/
io.py
File metadata and controls
166 lines (147 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""
I/O helpers for GENIE ROOT files.
"""
import cppyy
from ._genie_bindings import NtpMCEventRecord
from contextlib import contextmanager
import contextlib
import os, ctypes # to silence banners
# Load libc for low-level file descriptor dup2
libc = ctypes.CDLL(None)
@contextmanager
def silence_cpp():
"""
Temporarily redirect both Python and C++ stdout/stderr to /dev/null.
Useful to silence ROOT/GENIE banner spam.
"""
devnull_fd = os.open(os.devnull, os.O_WRONLY)
# Duplicate original FDs
orig_stdout_fd = os.dup(1)
orig_stderr_fd = os.dup(2)
try:
# Redirect to /dev/null
os.dup2(devnull_fd, 1)
os.dup2(devnull_fd, 2)
# Also silence Python’s sys streams
with open(os.devnull, "w") as dn, \
contextlib.redirect_stdout(dn), \
contextlib.redirect_stderr(dn):
yield
finally:
# Restore original file descriptors
os.dup2(orig_stdout_fd, 1)
os.dup2(orig_stderr_fd, 2)
os.close(orig_stdout_fd)
os.close(orig_stderr_fd)
os.close(devnull_fd)
# Using contextmanager for nice `with...` syntax
@contextmanager
def open_file(filename, treename):
"""Open a ROOT file and return (TFile, TTree). Closes upon end"""
tf = cppyy.gbl.TFile.Open(filename)
if not tf or tf.IsZombie():
raise OSError(f"Failed to open ROOT file: {filename}")
try:
tt = tf.Get(treename)
yield tf, tt
finally:
tf.Close()
def attach_record(tree, branch_name=None):
"""
Attach a genie::NtpMCEventRecord branch.
Parameters
----------
tree : TTree
branch_name : str or None
If given, attach directly. If None, auto-discover the first branch
whose class is 'genie::NtpMCEventRecord'.
Returns
-------
(tree, record, branch_name)
"""
import re
from ._genie_bindings import NtpMCEventRecord
if branch_name is None:
# scan tree for NtpMCEventRecord branches
blist = tree.GetListOfBranches()
for b in blist:
cname = b.GetClassName()
if re.search("NtpMCEventRecord", cname):
branch_name = b.GetName()
break
if branch_name is None:
raise RuntimeError("No NtpMCEventRecord branch found.")
# We need to define a tiny struct here to construct a pointer to the entry
# because the CAFs stored it as a pointer
if not hasattr(cppyy.gbl, "NtpMCEventRecordHolder"):
cppyy.cppdef("""
struct NtpMCEventRecordHolder {
genie::NtpMCEventRecord * rec = nullptr;
};
""")
with silence_cpp(): # No GENIE banners please - these are via cout
#rec = cppyy.gbl.genie.NtpMCEventRecord()
holder = cppyy.gbl.NtpMCEventRecordHolder()
#res = tree.SetBranchAddress(branch_name, rec)
res = tree.SetBranchAddress(branch_name, holder)
tree._ntp_ptr = holder
return tree, holder, branch_name
def attach_record_with_flux(tree, branch_name=None, flux_name=None):
"""
Attach a genie::NtpMCEventRecord branch alongside the bsim::Dk2Nu flux record
Parameters
----------
tree : TTree
branch_name : str or None
flux_name : str or None
If given, attach directly. If None, auto-discover the first branch
whose class is 'genie::NtpMCEventRecord' (branch) of 'bsim::Dk2Nu' (flux).
Returns
-------
(tree, event record, flux record)
"""
import re
from ._genie_bindings import NtpMCEventRecord, Dk2Nu
if branch_name is None:
# scan tree for NtpMCEventRecord branches
blist = tree.GetListOfBranches()
for b in blist:
cname = b.GetClassName()
if re.search("NtpMCEventRecord", cname):
branch_name = b.GetName()
break
if branch_name is None:
raise RuntimeError("No NtpMCEventRecord branch found.")
if flux_name is None:
# scan tree for Dk2Nu branches
blist = tree.GetListOfBranches()
for b in blist:
cname = b.GetClassName()
if re.search("Dk2Nu", cname):
flux_name = b.GetName()
break
if flux_name is None:
raise RuntimeError("No NtpMCEventRecord branch found.")
# We need to define a tiny struct here to construct a pointer to the entry
if not hasattr(cppyy.gbl, "NtpMCEventRecordHolder"):
cppyy.cppdef("""
struct NtpMCEventRecordHolder {
genie::NtpMCEventRecord * rec = nullptr;
};
""")
if not hasattr(cppyy.gbl, "Dk2NuHolder"):
cppyy.cppdef("""
struct Dk2NuHolder {
bsim::Dk2Nu * dk2nu = nullptr;
};
""")
with silence_cpp(): # No GENIE banners please - these are via cout
#rec = cppyy.gbl.genie.NtpMCEventRecord()
holder = cppyy.gbl.NtpMCEventRecordHolder()
fholder = cppyy.gbl.Dk2NuHolder()
#res = tree.SetBranchAddress(branch_name, rec)
res = tree.SetBranchAddress(branch_name, holder)
flx = tree.SetBranchAddress(flux_name, fholder)
tree._ntp_ptr = holder
tree._flx_ptr = fholder
return tree, holder, fholder