Skip to content

Commit 5e03b56

Browse files
authored
Merge pull request #218 from asogaard/marc-analysis
Code for Marc Jacquart's analysis
2 parents 5cacc15 + 5797725 commit 5e03b56

24 files changed

Lines changed: 1291 additions & 30 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: upgrade_nmo_sensitivity
2+
server: cobalt.icecube.wisc.edu
3+
sources: |
4+
/data/user/jvmead/ug_sim/genie2/genie/step4/140021/
5+
/data/user/jvmead/ug_sim/genie2/genie/step4/141021/
6+
date: 20220607

examples/convert_i3_to_sqlite.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Minimum working example (MWE) to use SQLiteDataConverter
22
"""
33

4-
from graphnet.data.i3extractor import (
4+
from graphnet.data.extractors import (
55
I3FeatureExtractorIceCube86,
66
I3FeatureExtractorIceCubeUpgrade,
77
I3RetroExtractor,

src/graphnet/components/loss_functions.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,30 @@ def _forward(self, prediction: Tensor, target: Tensor) -> Tensor:
5757
"""Syntax similar to `.forward` for implentation in inheriting classes."""
5858

5959

60+
class MSELoss(LossFunction):
61+
"""Mean squared error loss."""
62+
63+
def _forward(self, prediction: Tensor, target: Tensor) -> Tensor:
64+
"""Implementation of loss calculation."""
65+
# Check(s)
66+
assert prediction.dim() == 2
67+
assert prediction.size() == target.size()
68+
69+
elements = torch.mean((prediction - target) ** 2, dim=-1)
70+
return elements
71+
72+
73+
class RMSELoss(MSELoss):
74+
"""Root mean squared error loss."""
75+
76+
def _forward(self, prediction: Tensor, target: Tensor) -> Tensor:
77+
"""Implementation of loss calculation."""
78+
# Check(s)
79+
elements = super()._forward(prediction, target)
80+
elements = torch.sqrt(elements)
81+
return elements
82+
83+
6084
class LogCoshLoss(LossFunction):
6185
"""Log-cosh loss function.
6286
@@ -281,7 +305,7 @@ def _forward(self, prediction: Tensor, target: Tensor) -> Tensor:
281305
return self._evaluate(p, t)
282306

283307

284-
class EuclideanDistance(LossFunction):
308+
class EuclideanDistanceLoss(LossFunction):
285309
def _forward(self, prediction: Tensor, target: Tensor) -> Tensor:
286310
"""Calculates the 3D Euclidean distance between predicted and target.
287311

src/graphnet/data/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class FEATURES:
2626
class TRUTH:
2727
ICECUBE86 = [
2828
"energy",
29+
"energy_track",
2930
"position_x",
3031
"position_y",
3132
"position_z",
@@ -36,6 +37,7 @@ class TRUTH:
3637
"sim_type",
3738
"interaction_type",
3839
"interaction_time", # Added for vertex reconstruction
40+
"inelasticity",
3941
"stopped_muon",
4042
]
4143
DEEPCORE = ICECUBE86

src/graphnet/data/dataconverter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from abc import ABC, abstractmethod
22

3-
from graphnet.data.i3extractor import (
3+
from graphnet.data.extractors import (
44
I3Extractor,
55
I3ExtractorCollection,
66
I3TruthExtractor,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .i3extractor import *
2+
from .i3featureextractor import *
3+
from .i3truthextractor import *
4+
from .i3retroextractor import *
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from abc import ABC, abstractmethod
2+
from typing import List
3+
4+
from graphnet.utilities.logging import get_logger
5+
6+
logger = get_logger()
7+
8+
try:
9+
from icecube import (
10+
icetray,
11+
dataio,
12+
) # pyright: reportMissingImports=false
13+
except ImportError:
14+
logger.info("icecube package not available.")
15+
16+
17+
class I3Extractor(ABC):
18+
"""Extracts relevant information from physics frames."""
19+
20+
def __init__(self, name):
21+
22+
# Member variables
23+
self._i3_file = None
24+
self._gcd_file = None
25+
self._gcd_dict = None
26+
self._calibration = None
27+
self._name = name
28+
29+
def set_files(self, i3_file, gcd_file):
30+
# @TODO: Is it necessary to set the `i3_file`? It is only used in one
31+
# place in `I3TruthExtractor`, and there only in a way that might
32+
# be solved another way.
33+
self._i3_file = i3_file
34+
self._gcd_file = gcd_file
35+
self._load_gcd_data()
36+
37+
def _load_gcd_data(self):
38+
"""Loads the geospatial information contained in the gcd-file."""
39+
gcd_file = dataio.I3File(self._gcd_file)
40+
g_frame = gcd_file.pop_frame(icetray.I3Frame.Geometry)
41+
c_frame = gcd_file.pop_frame(icetray.I3Frame.Calibration)
42+
self._gcd_dict = g_frame["I3Geometry"].omgeo
43+
self._calibration = c_frame["I3Calibration"]
44+
45+
@abstractmethod
46+
def __call__(self, frame) -> dict:
47+
"""Extracts relevant information from frame."""
48+
pass
49+
50+
@property
51+
def name(self) -> str:
52+
return self._name
53+
54+
55+
class I3ExtractorCollection(list):
56+
"""Class to manage multiple I3Extractors."""
57+
58+
def __init__(self, *extractors):
59+
# Check(s)
60+
for extractor in extractors:
61+
assert isinstance(extractor, I3Extractor)
62+
63+
# Base class constructor
64+
super().__init__(extractors)
65+
66+
def set_files(self, i3_file, gcd_file):
67+
for extractor in self:
68+
extractor.set_files(i3_file, gcd_file)
69+
70+
def __call__(self, frame) -> List[dict]:
71+
return [extractor(frame) for extractor in self]
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
from graphnet.data.extractors.i3extractor import I3Extractor
2+
from graphnet.utilities.logging import get_logger
3+
4+
logger = get_logger()
5+
try:
6+
from icecube import (
7+
dataclasses,
8+
) # pyright: reportMissingImports=false
9+
except ImportError:
10+
logger.info("icecube package not available.")
11+
12+
13+
class I3FeatureExtractor(I3Extractor):
14+
def __init__(self, pulsemap):
15+
self._pulsemap = pulsemap
16+
super().__init__(pulsemap)
17+
18+
def _get_om_keys_and_pulseseries(self, frame):
19+
"""Gets the indicies for the gcd_dict and the pulse series
20+
21+
Args:
22+
frame (i3 physics frame): i3 physics frame
23+
24+
Returns:
25+
om_keys (index): the indicies for the gcd_dict
26+
data (??) : the pulse series
27+
"""
28+
data = frame[self._pulsemap]
29+
try:
30+
om_keys = data.keys()
31+
except: # noqa: E722
32+
try:
33+
if "I3Calibration" in frame.keys():
34+
data = frame[self._pulsemap].apply(frame)
35+
om_keys = data.keys()
36+
else:
37+
frame["I3Calibration"] = self._calibration
38+
data = frame[self._pulsemap].apply(frame)
39+
om_keys = data.keys()
40+
del frame[
41+
"I3Calibration"
42+
] # Avoid adding unneccesary data to frame
43+
except: # noqa: E722
44+
data = dataclasses.I3RecoPulseSeriesMap.from_frame(
45+
frame, self._pulsemap
46+
)
47+
om_keys = data.keys()
48+
return om_keys, data
49+
50+
51+
class I3FeatureExtractorIceCube86(I3FeatureExtractor):
52+
def __call__(self, frame) -> dict:
53+
"""Extract features to be used as inputs to GNN models."""
54+
55+
output = {
56+
"charge": [],
57+
"dom_time": [],
58+
"dom_x": [],
59+
"dom_y": [],
60+
"dom_z": [],
61+
"width": [],
62+
"pmt_area": [],
63+
"rde": [],
64+
}
65+
66+
try:
67+
om_keys, data = self._get_om_keys_and_pulseseries(frame)
68+
except KeyError:
69+
return output
70+
71+
for om_key in om_keys:
72+
# Common values for each OM
73+
x = self._gcd_dict[om_key].position.x
74+
y = self._gcd_dict[om_key].position.y
75+
z = self._gcd_dict[om_key].position.z
76+
area = self._gcd_dict[om_key].area
77+
rde = self._get_relative_dom_efficiency(frame, om_key)
78+
79+
# Loop over pulses for each OM
80+
pulses = data[om_key]
81+
for pulse in pulses:
82+
output["charge"].append(pulse.charge)
83+
output["dom_time"].append(pulse.time)
84+
output["width"].append(pulse.width)
85+
output["pmt_area"].append(area)
86+
output["rde"].append(rde)
87+
output["dom_x"].append(x)
88+
output["dom_y"].append(y)
89+
output["dom_z"].append(z)
90+
91+
return output
92+
93+
def _get_relative_dom_efficiency(self, frame, om_key):
94+
if (
95+
"I3Calibration" in frame
96+
): # Not available for e.g. mDOMs in IceCube Upgrade
97+
rde = frame["I3Calibration"].dom_cal[om_key].relative_dom_eff
98+
else:
99+
try:
100+
rde = self._calibration.dom_cal[om_key].relative_dom_eff
101+
except: # noqa: E722
102+
rde = -1
103+
return rde
104+
105+
106+
class I3FeatureExtractorIceCubeDeepCore(I3FeatureExtractorIceCube86):
107+
"""..."""
108+
109+
110+
class I3FeatureExtractorIceCubeUpgrade(I3FeatureExtractorIceCube86):
111+
def __call__(self, frame) -> dict:
112+
"""Extract features to be used as inputs to GNN models."""
113+
114+
output = {
115+
"string": [],
116+
"pmt_number": [],
117+
"dom_number": [],
118+
"pmt_dir_x": [],
119+
"pmt_dir_y": [],
120+
"pmt_dir_z": [],
121+
"dom_type": [],
122+
}
123+
124+
try:
125+
om_keys, data = self._get_om_keys_and_pulseseries(frame)
126+
except KeyError: # Target pulsemap does not exist in `frame`
127+
return output
128+
129+
for om_key in om_keys:
130+
# Common values for each OM
131+
pmt_dir_x = self._gcd_dict[om_key].orientation.x
132+
pmt_dir_y = self._gcd_dict[om_key].orientation.y
133+
pmt_dir_z = self._gcd_dict[om_key].orientation.z
134+
string = om_key[0]
135+
dom_number = om_key[1]
136+
pmt_number = om_key[2]
137+
dom_type = self._gcd_dict[om_key].omtype
138+
139+
# Loop over pulses for each OM
140+
pulses = data[om_key]
141+
for _ in pulses:
142+
output["string"].append(string)
143+
output["pmt_number"].append(pmt_number)
144+
output["dom_number"].append(dom_number)
145+
output["pmt_dir_x"].append(pmt_dir_x)
146+
output["pmt_dir_y"].append(pmt_dir_y)
147+
output["pmt_dir_z"].append(pmt_dir_z)
148+
output["dom_type"].append(dom_type)
149+
150+
# Add features from IceCube86
151+
output_icecube86 = super().__call__(frame)
152+
output.update(output_icecube86)
153+
return output
154+
155+
156+
class I3PulseNoiseTruthFlagIceCubeUpgrade(I3FeatureExtractorIceCube86):
157+
def __call__(self, frame) -> dict:
158+
"""Extract features to be used as inputs to GNN models."""
159+
160+
output = {
161+
"string": [],
162+
"pmt_number": [],
163+
"dom_number": [],
164+
"pmt_dir_x": [],
165+
"pmt_dir_y": [],
166+
"pmt_dir_z": [],
167+
"dom_type": [],
168+
"truth_flag": [],
169+
}
170+
171+
try:
172+
om_keys, data = self._get_om_keys_and_pulseseries(frame)
173+
except KeyError: # Target pulsemap does not exist in `frame`
174+
return output
175+
176+
for om_key in om_keys:
177+
# Common values for each OM
178+
pmt_dir_x = self._gcd_dict[om_key].orientation.x
179+
pmt_dir_y = self._gcd_dict[om_key].orientation.y
180+
pmt_dir_z = self._gcd_dict[om_key].orientation.z
181+
string = om_key[0]
182+
dom_number = om_key[1]
183+
pmt_number = om_key[2]
184+
dom_type = self._gcd_dict[om_key].omtype
185+
186+
# Loop over pulses for each OM
187+
pulses = data[om_key]
188+
for truth_flag in pulses:
189+
output["string"].append(string)
190+
output["pmt_number"].append(pmt_number)
191+
output["dom_number"].append(dom_number)
192+
output["pmt_dir_x"].append(pmt_dir_x)
193+
output["pmt_dir_y"].append(pmt_dir_y)
194+
output["pmt_dir_z"].append(pmt_dir_z)
195+
output["dom_type"].append(dom_type)
196+
output["truth_flag"].append(truth_flag)
197+
198+
return output

0 commit comments

Comments
 (0)