Skip to content

Commit bf6b1f8

Browse files
jlenainhashkar
andauthored
DQM output format and parsing script (#147)
* Add script file to parse a DQM FITS file and fill the ZODB with it. * Automatically fetch DQM output archive from DIRAC in script * Extract tar gz archive automatically * Changing the format of the DQM output FITS files * Lint * Do not display PixelTimeline objects * Improve script with argument parser, to provide a run number to process. * Add exceptions, remove DQM results at end of script * Streamline the WriteAllResults method * Add exception handling * Found a way to include TRIGGER-STATISTICS in output FITS file * Change Bokeh app to reflect change in output format for DQM FITS files * Streamline list of processors in DQM * Don't test for pattern not to display in parent key, but in child key of DQM dict * Add START-TIMES in rejected list of displays * Adapt Bokeh app to first find the run id with the most displays before starting * Print caught errors on update * Reset displays on update * Changed caught error message when filling HDU for output DQM FITS file * Handle further type of exceptions for pedestal values * No camera display for integrated pedestal values --------- Co-authored-by: Jean-Philippe Lenain <[email protected]> Co-authored-by: Halim Ashkar <[email protected]>
1 parent b4a2ef7 commit bf6b1f8

File tree

6 files changed

+201
-118
lines changed

6 files changed

+201
-118
lines changed

src/nectarchain/dqm/bokeh_app/app_hooks.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import collections
2+
import re
23

34
import numpy as np
45
from ctapipe.coordinates import EngineeringCameraFrame
@@ -9,11 +10,13 @@
910
from ctapipe_io_nectarcam import constants
1011

1112
NOTINDISPLAY = [
12-
"Results_TriggerStatistics",
13-
"Results_MeanWaveForms_HighGain",
14-
"Results_MeanWaveForms_LowGain",
15-
"Results_CameraMonitoring",
13+
"TRIGGER-.*",
14+
"PED-INTEGRATION-.*",
15+
"START-TIMES",
16+
"WF-.*",
17+
".*PixTimeline-.*",
1618
]
19+
TEST_PATTERN = "(?:% s)" % "|".join(NOTINDISPLAY)
1720

1821
geom = CameraGeometry.from_name("NectarCam-003")
1922
geom = geom.transform_to(EngineeringCameraFrame())
@@ -27,8 +30,8 @@ def get_rundata(src, runid):
2730
def make_camera_displays(db, source, runid):
2831
displays = collections.defaultdict(dict)
2932
for parentkey in db[runid].keys():
30-
if parentkey not in NOTINDISPLAY:
31-
for childkey in db[runid][parentkey].keys():
33+
for childkey in db[runid][parentkey].keys():
34+
if not re.match(TEST_PATTERN, childkey):
3235
print(f"Run id {runid} Preparing plot for {parentkey}, {childkey}")
3336
displays[parentkey][childkey] = make_camera_display(
3437
source, parent_key=parentkey, child_key=childkey

src/nectarchain/dqm/bokeh_app/main.py

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import re
2+
13
import numpy as np
4+
from app_hooks import TEST_PATTERN, get_rundata, make_camera_displays
25

36
# bokeh imports
47
from bokeh.layouts import layout, row
@@ -12,15 +15,6 @@
1215

1316
from nectarchain.dqm.db_utils import DQMDB
1417

15-
from .app_hooks import get_rundata, make_camera_displays
16-
17-
NOTINDISPLAY = [
18-
"Results_TriggerStatistics",
19-
"Results_MeanWaveForms_HighGain",
20-
"Results_MeanWaveForms_LowGain",
21-
"Results_CameraMonitoring",
22-
]
23-
2418
geom = CameraGeometry.from_name("NectarCam-003")
2519
geom = geom.transform_to(EngineeringCameraFrame())
2620

@@ -29,19 +23,32 @@ def update_camera_displays(attr, old, new):
2923
runid = run_select.value
3024
new_rundata = get_rundata(db, runid)
3125

26+
# Reset each display
27+
for k in displays.keys():
28+
for kk in displays[k].keys():
29+
displays[k][kk].image = np.zeros(shape=constants.N_PIXELS)
30+
3231
for parentkey in db[runid].keys():
33-
if parentkey not in NOTINDISPLAY:
34-
for childkey in db[runid][parentkey].keys():
32+
for childkey in db[runid][parentkey].keys():
33+
if not re.match(TEST_PATTERN, childkey):
3534
print(f"Run id {runid} Updating plot for {parentkey}, {childkey}")
36-
# try:
35+
3736
image = new_rundata[parentkey][childkey]
3837
image = np.nan_to_num(image, nan=0.0)
3938
try:
4039
displays[parentkey][childkey].image = image
41-
except ValueError:
40+
except ValueError as e:
41+
print(
42+
f"Caught {type(e).__name__} for {childkey}, filling display"
43+
f"with zeros. Details: {e}"
44+
)
4245
image = np.zeros(shape=displays[parentkey][childkey].image.shape)
4346
displays[parentkey][childkey].image = image
44-
except KeyError:
47+
except KeyError as e:
48+
print(
49+
f"Caught {type(e).__name__} for {childkey}, filling display"
50+
f"with zeros. Details: {e}"
51+
)
4552
image = np.zeros(shape=constants.N_PIXELS)
4653
displays[parentkey][childkey].image = image
4754
# TODO: TRY TO USE `stream` INSTEAD, ON UPDATES:
@@ -51,7 +58,20 @@ def update_camera_displays(attr, old, new):
5158

5259
db = DQMDB(read_only=True).root
5360
runids = sorted(list(db.keys()))
54-
runid = runids[-1]
61+
62+
# First, get the run id with the most populated result dictionary
63+
runid_max = runids[-1]
64+
largest = 0
65+
for runid in runids:
66+
larger = 0
67+
for k in db[runid].keys():
68+
length = len(db[runid][k])
69+
if length > larger:
70+
larger = length
71+
if larger > largest:
72+
largest = larger
73+
runid_max = runid
74+
runid = runid_max
5575

5676
# runid_input = NumericInput(value=db.root.keys()[-1], title="NectarCAM run number")
5777
run_select = Select(value=runid, title="NectarCAM run number", options=runids)
Lines changed: 27 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import numpy as np
12
from astropy.io import fits
23
from astropy.table import Table
34

@@ -32,58 +33,35 @@ def PlotResults(
3233
):
3334
print("Processor 5")
3435

35-
def WriteAllResults(self, path, DICT):
36-
data2 = Table()
37-
data1 = Table()
38-
data0 = Table()
36+
@staticmethod
37+
def _create_hdu(name, content):
3938
data = Table()
40-
hdu, hdu0, hdu1, hdu2 = None, None, None, None
39+
try:
40+
data[name] = content
41+
except TypeError:
42+
try:
43+
data = Table(content)
44+
except ValueError:
45+
# We may have caught just a single float value, try to pack it into
46+
# the FITS output
47+
content = np.array([content])
48+
data = Table(content)
49+
hdu = fits.BinTableHDU(data)
50+
hdu.name = name
51+
return hdu
52+
53+
def WriteAllResults(self, path, DICT):
4154
hdulist = fits.HDUList()
4255
for i, j in DICT.items():
43-
if (i == "Results_TriggerStatistics"):
44-
for n2, m2 in j.items():
45-
data2[n2] = m2
46-
hdu2 = fits.BinTableHDU(data2)
47-
hdu2.name = "Trigger"
48-
49-
elif (i == "Results_MeanWaveForms_HighGain") or (
50-
i == "Results_MeanWaveForms_LowGain"
51-
):
52-
for n1, m1 in j.items():
53-
data1[n1] = m1
54-
hdu1 = fits.BinTableHDU(data1)
55-
hdu1.name = "MWF"
56-
57-
elif (i == "Results_PixelTimeline_HighGain") or (i == "Results_PixelTimeline_LowGain"):
58-
for n0, m0 in j.items():
59-
data0[n0] = m0
60-
hdu0 = fits.BinTableHDU(data0)
61-
hdu0.name = "BPX"
62-
63-
else:
64-
for n, m in j.items():
65-
data[n] = m
66-
hdu = fits.BinTableHDU(data)
67-
hdu.name = "Camera"
68-
if hdu2:
69-
hdulist.append(hdu2)
70-
else:
71-
print("No trigger statistics requests")
72-
if hdu1:
73-
hdulist.append(hdu1)
74-
else:
75-
print("No MWF studies requests")
76-
if hdu0:
77-
hdulist.append(hdu0)
78-
else:
79-
print("No Pixel Timeline studies requests")
80-
if hdu:
81-
hdulist.append(hdu)
82-
else:
83-
print("No Camera studies requests")
56+
for name, content in j.items():
57+
try:
58+
hdu = self._create_hdu(name, content)
59+
hdulist.append(hdu)
60+
except TypeError as e:
61+
print(f"Caught {type(e).__name__}, skipping {name}. Details: {e}")
62+
pass
8463

85-
86-
FileName = path + '_Results.fits'
64+
FileName = path + "_Results.fits"
8765
print(FileName)
8866
hdulist.writeto(FileName, overwrite=True)
89-
return None
67+
hdulist.info()

src/nectarchain/dqm/start_dqm.py

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ def main():
7070
output_path = args.output_paths
7171
print("Output path:", output_path)
7272

73-
# Defining and printing the paths of the input files.
74-
7573
if args.runnb is not None:
7674
# Grab runs automatically from DIRAC is the -r option is provided
7775
from nectarchain.data.management import DataManagement
@@ -152,34 +150,22 @@ def CreateFigFolder(name, type):
152150

153151
# LIST OF PROCESSES TO RUN
154152
####################################################################################
155-
a = TriggerStatistics(HIGH_GAIN)
156-
b = MeanWaveFormsHighLowGain(HIGH_GAIN)
157-
c = MeanWaveFormsHighLowGain(LOW_GAIN)
158-
d = MeanCameraDisplayHighLowGain(HIGH_GAIN)
159-
e = MeanCameraDisplayHighLowGain(LOW_GAIN)
160-
f = ChargeIntegrationHighLowGain(HIGH_GAIN)
161-
g = ChargeIntegrationHighLowGain(LOW_GAIN)
162-
h = CameraMonitoring(HIGH_GAIN)
163-
i = PixelParticipationHighLowGain(HIGH_GAIN)
164-
j = PixelParticipationHighLowGain(LOW_GAIN)
165-
k = PixelTimelineHighLowGain(HIGH_GAIN)
166-
ll = PixelTimelineHighLowGain(LOW_GAIN)
167-
168-
processors = list()
169-
170-
processors.append(a)
171-
processors.append(b)
172-
processors.append(c)
173-
processors.append(d)
174-
processors.append(e)
175-
processors.append(f)
176-
processors.append(g)
177-
processors.append(h)
178-
processors.append(i)
179-
processors.append(j)
180-
processors.append(k)
181-
processors.append(ll)
153+
processors = [
154+
TriggerStatistics(HIGH_GAIN),
155+
MeanWaveFormsHighLowGain(HIGH_GAIN),
156+
MeanWaveFormsHighLowGain(LOW_GAIN),
157+
MeanCameraDisplayHighLowGain(HIGH_GAIN),
158+
MeanCameraDisplayHighLowGain(LOW_GAIN),
159+
ChargeIntegrationHighLowGain(HIGH_GAIN),
160+
ChargeIntegrationHighLowGain(LOW_GAIN),
161+
CameraMonitoring(HIGH_GAIN),
162+
PixelParticipationHighLowGain(HIGH_GAIN),
163+
PixelParticipationHighLowGain(LOW_GAIN),
164+
PixelTimelineHighLowGain(HIGH_GAIN),
165+
PixelTimelineHighLowGain(LOW_GAIN),
166+
]
182167

168+
# LIST OF DICT RESULTS
183169
NESTED_DICT = {} # The final results dictionary
184170

185171
NESTED_DICT_KEYS = [
@@ -197,8 +183,6 @@ def CreateFigFolder(name, type):
197183
"Results_PixelTimeline_LowGain",
198184
]
199185

200-
# NESTED_DICT_KEYS = ["Results_PixelParticipation_HighGain"]
201-
202186
# START
203187
for p in processors:
204188
Pix, Samp = p.DefineForRun(reader1)
@@ -257,7 +241,7 @@ def CreateFigFolder(name, type):
257241
plt.close()
258242

259243
end = time.time()
260-
print("Processing time:", end - start)
244+
print(f"Processing time: {end-start:.2f} s.")
261245

262246
# TODO
263247
# Reduce code by using loops: for figs and results

src/nectarchain/dqm/trigger_statistics.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -90,22 +90,18 @@ def FinishRun(self):
9090

9191
def GetResults(self):
9292
self.TriggerStat_Results_Dict["TRIGGER-TYPES"] = self.triggers
93-
self.TriggerStat_Results_Dict[
94-
"TRIGGER-STATISTICS"
95-
] = "All: %s, Physical: %s, Pedestals: %s, Others: %s, Wrong times: %s" % (
96-
len(self.event_times),
97-
len(self.event_phy_times),
98-
len(self.event_ped_times),
99-
len(self.event_other_times),
100-
len(self.event_wrong_times),
101-
)
102-
self.TriggerStat_Results_Dict[
103-
"START-TIMES"
104-
] = "Run start time: %s, First event: %s, Last event: %s" % (
105-
self.run_start1,
106-
self.run_start,
107-
self.run_end,
108-
)
93+
self.TriggerStat_Results_Dict["TRIGGER-STATISTICS"] = {
94+
"All": [len(self.event_times)],
95+
"Physical": [len(self.event_phy_times)],
96+
"Pedestals": [len(self.event_ped_times)],
97+
"Others": [len(self.event_other_times)],
98+
"Wrong times": [len(self.event_wrong_times)],
99+
}
100+
self.TriggerStat_Results_Dict["START-TIMES"] = {
101+
"Run start time": self.run_start1,
102+
"First event": self.run_start,
103+
"Last event": self.run_end,
104+
}
109105
return self.TriggerStat_Results_Dict
110106

111107
def PlotResults(self, name, FigPath):

0 commit comments

Comments
 (0)