Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a script for the standard data processing of RNO-G data #790

Open
wants to merge 69 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
4f16eb1
Rename folder in examples
fschlueter Jan 16, 2025
3bc55d1
Delete station_simulation folder from NuRadioReco examples. A new exa…
fschlueter Jan 16, 2025
91373db
Remove redandent script
fschlueter Jan 16, 2025
2fa050e
Remove subfolder
fschlueter Jan 16, 2025
2b6442f
Update script to run simple reconstruction
fschlueter Jan 16, 2025
1dbe95c
Add a first version of a standard reconstruction and processing scrip…
fschlueter Jan 16, 2025
9fa0976
Minor modifications. Remove one function
fschlueter Jan 17, 2025
77ea69b
Adding logging and improvements to config file
fschlueter Jan 17, 2025
7a8c086
Improve logging
fschlueter Jan 17, 2025
b3b8fa8
Fix unit test
fschlueter Jan 17, 2025
8ad78ad
Merge branch 'develop' into add_rnog_data_processing_script
fschlueter Jan 17, 2025
6736e04
apply hardware respone incorperator in phase_only mode. some more cha…
fschlueter Jan 17, 2025
f6d4aec
add first version of beam forming fitter
cg-laser Jan 21, 2025
24a1384
further improvements
cg-laser Jan 21, 2025
e02aa9e
update reco script
cg-laser Jan 23, 2025
5c47f45
Added "glitch" parameter
avijai2000 Jan 23, 2025
ef62272
Add files via upload
avijai2000 Jan 23, 2025
37f253e
Update glitch_removal.py
avijai2000 Jan 23, 2025
8550ffc
Update parameters.py
avijai2000 Jan 23, 2025
8d9bab0
Update glitch_removal.py
avijai2000 Jan 23, 2025
a47f26e
Merge remote-tracking branch 'origin/channelBlockOffsetFitter-auto' i…
fschlueter Jan 24, 2025
8c68e06
Rename new glitch module
fschlueter Jan 24, 2025
207e705
Add blockoffset removal explicitly in the processing sequence
fschlueter Jan 24, 2025
04d6367
Move channelGlitchDetector.py module to RNO_G
fschlueter Jan 24, 2025
3cf363a
Improve channelGlitchDetector module. Change parameters. Add helper f…
fschlueter Jan 24, 2025
52ac6d1
Add begin argument to glitch module. Changed args -> kwargs in script…
fschlueter Jan 24, 2025
4daecf1
Merge branch 'add-decorator' into add_rnog_data_processing_script
fschlueter Jan 24, 2025
af5f695
Introduce channelParametersRNOG
fschlueter Jan 24, 2025
e80dcff
Move glitch detectio before offset subtractor
fschlueter Jan 24, 2025
90ede8f
Update channelSignalReconstructor.py
avijai2000 Jan 24, 2025
6da1b9b
Update parameters.py
avijai2000 Jan 24, 2025
36d3743
Update event.py
avijai2000 Jan 24, 2025
511f623
Update parameters.py
avijai2000 Jan 25, 2025
a8d9f02
Update channelSignalReconstructor.py
avijai2000 Jan 25, 2025
b7687e6
fix a few strange things
philippwindischhofer Jan 27, 2025
b9873ae
calculate glitch test statistic
philippwindischhofer Jan 27, 2025
7c4c93e
Merge branch 'develop' into add_rnog_data_processing_script
fschlueter Jan 27, 2025
5c1451d
Fix
fschlueter Jan 27, 2025
3940ab6
Cleanup channelSignalReconstructor
fschlueter Jan 27, 2025
0035a60
Update station.py
avijai2000 Jan 27, 2025
43bd34a
Update event.py
avijai2000 Jan 27, 2025
27ed6d3
Add RNO-G data provider module
fschlueter Jan 28, 2025
3f808c7
Fix reader argument
fschlueter Jan 28, 2025
c74ecac
better documentation
philippwindischhofer Jan 28, 2025
b04bb05
very dirty, temporary hack
philippwindischhofer Jan 28, 2025
13515f3
draft summary plot
philippwindischhofer Jan 28, 2025
0d72885
nicer plot
philippwindischhofer Jan 28, 2025
6e16340
Update station.py
avijai2000 Jan 28, 2025
2a07096
Undo bad hack.
philippwindischhofer Jan 29, 2025
a416951
channel parameter
philippwindischhofer Jan 29, 2025
e6e0d57
Merge branch 'develop' into add_rnog_data_processing_script
fschlueter Jan 29, 2025
fb184a0
Adabt the rnog_standard_data_processing script to use the new dataPro…
fschlueter Jan 29, 2025
d93b300
make Felix happy
philippwindischhofer Jan 29, 2025
25c6ff3
add summary prinout at the end of the run
philippwindischhofer Jan 30, 2025
46139c9
fix conflicts and felix' code again: radomir wonders if he tests it b…
philippwindischhofer Jan 30, 2025
1a05f82
notation
philippwindischhofer Jan 30, 2025
fb98c55
Rewriting of rnog_standard_data_processing (again). Some fixes
fschlueter Jan 31, 2025
a3c26f0
Merge branch 'add_rnog_data_processing_script' into rnog_glitch_detec…
philippwindischhofer Jan 31, 2025
c424185
missing import
philippwindischhofer Jan 31, 2025
e640cea
returns a list when nargs is used
philippwindischhofer Jan 31, 2025
47bdcaf
logger doesn't have end
philippwindischhofer Jan 31, 2025
26e942f
make felix extra happy
philippwindischhofer Jan 31, 2025
50e9db0
make felix even happier
philippwindischhofer Jan 31, 2025
e2399da
Merge pull request #806 from nu-radio/rnog_glitch_detection
philippwindischhofer Jan 31, 2025
743d2fa
restructure example, provide additional example code
cg-laser Jan 31, 2025
6b1ea3f
remove previous file
cg-laser Feb 3, 2025
f32c0f0
Update event.py
avijai2000 Feb 3, 2025
e2c5fab
Update station.py
avijai2000 Feb 3, 2025
7243bf3
Update parameters.py
avijai2000 Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions NuRadioReco/examples/RNOG/data_analysis_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import argparse
import logging
import time
import os
import numpy as np
from matplotlib import pyplot as plt
import NuRadioReco.modules.channelResampler
import NuRadioReco.modules.RNO_G.dataProviderRNOG
import NuRadioReco.modules.io.eventWriter
import NuRadioReco.modules.channelResampler
import NuRadioReco.detector.RNO_G.rnog_detector
from NuRadioReco.utilities import units, logging as nulogging

from NuRadioReco.examples.RNOG.processing import process_event

logger = logging.getLogger("NuRadioReco.example.RNOG.rnog_standard_data_processing")
logger.setLevel(nulogging.LOGGING_STATUS)


if __name__ == "__main__":

parser = argparse.ArgumentParser(description='Run standard RNO-G data processing')

parser.add_argument('--filenames', type=str, nargs="*",
help='Specify root data files if not specified in the config file')
parser.add_argument('--outputfile', type=str, nargs=1, default=None)

args = parser.parse_args()
nulogging.set_general_log_level(logging.WARNING)

if args.outputfile is None:
if len(args.filenames) > 1:
raise ValueError("Please specify an output file")

path = args.filenames[0]

if path.endswith(".root"):
args.outputfile = path.replace(".root", ".nur")
elif os.path.isdir(path):
args.outputfile = os.path.join(path, "output.nur")
else:
args.outputfile = args.outputfile[0]

logger.status(f"writing output to {args.outputfile}")

# Initialize detector class
det = NuRadioReco.detector.RNO_G.rnog_detector.Detector()

# Initialize io modules
dataProviderRNOG = NuRadioReco.modules.RNO_G.dataProviderRNOG.dataProvideRNOG()
dataProviderRNOG.begin(files=args.filenames, det=det)

eventWriter = NuRadioReco.modules.io.eventWriter.eventWriter()
eventWriter.begin(filename=args.outputfile)

# initialize additional modules
channelResampler = NuRadioReco.modules.channelResampler.channelResampler()
channelResampler.begin()

# For time logging
t_total = 0

# Loop over all events (the reader module has options to select events -
# see class documentation or module arguements in config file)
for idx, evt in enumerate(dataProviderRNOG.run()):

if (idx + 1) % 50 == 0:
logger.info(f'"Processing events: {idx + 1}\r')

t0 = time.time()
# perform standard RNO-G data processing
process_event(evt, det)

########################
########################
#
# add more usercode here
#
########################
########################

# the following code is just an example of how to access station and channel information:

station = evt.get_station() # this assumes that there is only one station per event. If multiple stations are present, use evt.get_stations() and iterate over them

# the following code is just an example of how to access reconstructed parameters
# (the ones that were calculated in the processing steps by the channelSignalReconstructor)
from NuRadioReco.framework.parameters import channelParameters as chp
max_SNR = 0
for channel in station.iter_channels():
SNR = channel[chp.SNR]['peak_2_peak_amplitude'] # alternatively, channel.get_parameter(chp.SNR)
max_SNR = max(max_SNR, SNR)
signal_amplitude = channel[chp.maximum_amplitude_envelope]
signal_time = channel[chp.signal_time]

print(f"Channel {channel.get_id()}: SNR={SNR:.1f}, signal amplitude={signal_amplitude/units.mV:.2f}mV, signal time={signal_time/units.ns:.2f}ns")


# the following code is just an example of how to access the channel waveforms and plot them
# we do it only for high-SNR events
if max_SNR > 5:
# iterate over some channels in station and plot them
fig, ax = plt.subplots(4, 2)
for channel_id in range(4): # iterate over the first 4 channels
channel = station.get_channel(channel_id)
ax[channel_id, 0].plot(channel.get_times()/units.ns, channel.get_trace()/units.mV) # plot the timetrace in nanoseconds vs. microvolts
ax[channel_id, 1].plot(channel.get_frequencies()/units.MHz, np.abs(channel.get_frequency_spectrum())/units.mV) # plot the frequency spectrum in MHz vs. microvolts
ax[channel_id, 0].set_xlabel('Time [ns]')
ax[channel_id, 0].set_ylabel('Voltage [mV]')
ax[channel_id, 1].set_xlabel('Frequency [MHz]')
ax[channel_id, 1].set_ylabel('Voltage [mV]')
plt.tight_layout()
fig.savefig(f'channel_traces_{idx}.png')
plt.close(fig)

# before saving events to disk, it is advisable to downsample back to the two-times the maximum frequency available in the data, i.e., back to the Nquist frequency
# this will save disk space and make the data processing faster. The preprocessing applied a 600MHz low-pass filter, so we can downsample to 2GHz without losing information
channelResampler.run(evt, station, det, sampling_rate=2 * units.GHz)

# it is advisable to only save the full waveform information for events that pass certain analysis cuts
# this will save disk space and make the data processing faster
# Here, we implement a simple SNR cut as an example
interesting_event = False
if(max_SNR > 5):
interesting_event = True #determined by some analysis cuts
# Write event - the RNO-G detector class is not stored within the nur files.
if interesting_event:
# save full waveform information
print("saving full waveform information")
eventWriter.run(evt, det=None, mode={'Channels':True, "ElectricFields":True})
else:
# only save meta information but no traces to save disk space
print("saving only meta information")
eventWriter.run(evt, det=None, mode={'Channels':False, "ElectricFields":False})

logger.debug("Time for event: %f", time.time() - t0)
t_total += time.time() - t0

dataProviderRNOG.end()
eventWriter.end()

logger.status(
f"Processed {idx + 1} events:"
f"\n\tTotal time: {t_total:.2f}s"
f"\n\tTime per event: {t_total / (idx + 1):.2f}s")
105 changes: 105 additions & 0 deletions NuRadioReco/examples/RNOG/make_glitch_summary_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import argparse, numpy as np
from NuRadioReco.modules.io import eventReader
import NuRadioReco.framework.parameters as parameters

def plot(plot_data, outpath, fs=13, zoomfact=1.1):

channel_colors = {
0: "tab:blue",
1: "tab:orange",
2: "tab:green",
3: "tab:red"
}

num_bins = 10

channels = [key for key in plot_data.keys() if isinstance(key, int)]
num_channels = len(channels)

import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.ticker import AutoMinorLocator

fig = plt.figure(figsize = (6, 5), layout = "constrained")
gs = GridSpec(num_channels, 2, figure = fig, width_ratios = [0.8, 0.2])

for ind, channel in enumerate(channels):
color = channel_colors.get(channel, "black")

evt_nums = plot_data["evt_num"]
channel_ts = plot_data[channel]
yscale = zoomfact * np.max(np.abs(channel_ts))

ax = fig.add_subplot(gs[ind, 0])
ax_hist = fig.add_subplot(gs[ind, 1], sharey = ax)
ax.set_ylim(-yscale, yscale)
ax.set_xlim(min(evt_nums), max(evt_nums))
ax_hist.set_xlim(0.0, 5 * len(channel_ts) / num_bins)

ax.text(0.02, 0.95, f"CH {channel}", fontsize = fs, transform = ax.transAxes, color = color, ha = "left", va = "top")
ax.set_ylabel("Test stat.", fontsize = fs)

ax.scatter(evt_nums, channel_ts, s = 2, color = color)
ax_hist.hist(channel_ts, histtype = "step", bins = num_bins, orientation = "horizontal", color = color)

ax.tick_params(axis = "x", direction = "in", which = "both", bottom = True, top = True, labelbottom = ind == len(channels) - 1,
labelsize = fs)
ax.tick_params(axis = "y", direction = "in", which = "both", left = True, right = True, labelsize = fs)

ax.axhline(0.0, color = "gray", ls = "dashed", lw = 1.0)
ax_hist.axhline(0.0, color = "gray", ls = "dashed", lw = 1.0)

ax_hist.tick_params(axis = "x", direction = "in", which = "both", bottom = True, top = True, labelbottom = ind == len(channels) - 1,
labelsize = fs)
ax_hist.tick_params(axis = "y", direction = "in", which = "both", left = True, right = True, labelsize = fs,
labelleft = False)

ax.fill_between(evt_nums, 0.0, yscale, facecolor = "tab:red", alpha = 0.25, zorder = 0)
ax.fill_between(evt_nums, 0.0, -yscale, facecolor = "tab:green", alpha = 0.25, zorder = 0)

ax_hist.fill_between(evt_nums, 0.0, yscale, facecolor = "tab:red", alpha = 0.25, zorder = 0)
ax_hist.fill_between(evt_nums, 0.0, -yscale, facecolor = "tab:green", alpha = 0.25, zorder = 0)

ax_hist.text(0.95, 0.95, "Glitching", ha = "right", va = "top", color = "tab:red", fontsize = 7, transform = ax_hist.transAxes)
ax_hist.text(0.95, 0.05, "No Glitching", ha = "right", va = "bottom", color = "tab:green", fontsize = 7, transform = ax_hist.transAxes)

ax.set_xlabel("Event Number", fontsize = fs)
ax_hist.set_xlabel("Evts. / bin", fontsize = fs)

fig.savefig(outpath)
plt.close()

def make_glitch_summary_plot(nur_path, plot_path, channels=[0, 1, 2, 3]):

reader = eventReader.eventReader()
reader.begin(nur_path)

plot_data = {"evt_num": []}

for evt in reader.run():
station = evt.get_station()
plot_data["evt_num"].append(evt.get_id())

for channel in channels:
ch_data = station.get_channel(channel)
ch_data.add_parameter_type(parameters.channelParametersRNOG)
ch_number = ch_data.get_id()

ts = ch_data.get_parameter(parameters.channelParametersRNOG.glitch_test_statistic)

if ch_number not in plot_data:
plot_data[ch_number] = []
plot_data[ch_number].append(ts)

plot(plot_data, plot_path)

if __name__ == "__main__":

parser = argparse.ArgumentParser()
parser.add_argument("--nur", type = str, action = "store", dest = "nur_path")
parser.add_argument("--plot", type = str, action = "store", dest = "plot_path")
args = vars(parser.parse_args())

make_glitch_summary_plot(**args)
89 changes: 89 additions & 0 deletions NuRadioReco/examples/RNOG/processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import logging
import NuRadioReco.modules.channelResampler
import NuRadioReco.modules.channelBandPassFilter
import NuRadioReco.modules.channelCWNotchFilter
import NuRadioReco.modules.channelSignalReconstructor

import NuRadioReco.modules.RNO_G.dataProviderRNOG
import NuRadioReco.modules.RNO_G.hardwareResponseIncorporator
import NuRadioReco.modules.io.eventWriter

import NuRadioReco.detector.RNO_G.rnog_detector
from NuRadioReco.utilities import units


logger = logging.getLogger("NuRadioReco.example.RNOG.rnog_standard_data_processing")
logger.setLevel(logging.INFO)

channelResampler = NuRadioReco.modules.channelResampler.channelResampler()
channelResampler.begin()

channelBandPassFilter = NuRadioReco.modules.channelBandPassFilter.channelBandPassFilter()
channelBandPassFilter.begin()

channelCWNotchFilter = NuRadioReco.modules.channelCWNotchFilter.channelCWNotchFilter()
channelCWNotchFilter.begin()

hardwareResponseIncorporator = NuRadioReco.modules.RNO_G.hardwareResponseIncorporator.hardwareResponseIncorporator()
hardwareResponseIncorporator.begin()

channelSignalReconstructor = NuRadioReco.modules.channelSignalReconstructor.channelSignalReconstructor(log_level=logging.WARNING)
channelSignalReconstructor.begin()


def process_event(evt, det):
"""
Recommended preprocessing for RNO-G events

Parameters
----------
evt : NuRadioReco.event.Event
Event to process
det : NuRadioReco.detector.detector.Detector
Detector object
"""

# loop over all stations in the event. Typically we only have a single station per event.
for station in evt.get_stations():
# The RNO-G detector changed over time (e.g. because certain hardware components were replaced).
# The time-dependent detector description has this information but needs to be updated with the
# current time of the event.
det.update(station.get_station_time())

# The first step is to upsample the data to a higher sampling rate. This will e.g. allow to
# determine the maximum amplitude and time of the signal more accurately. Studies showed that
# upsampling to 5 GHz is good enough for most task.
# In general, we need to find a good compromise between the data size (and thus processing time)
# and the required accuracy.
# Also remember to always downsample the data again before saving it to disk to avoid unnecessary
# large files.
channelResampler.run(evt, station, det, sampling_rate=5 * units.GHz)


# Our antennas are only sensitive in a certain frequency range. Hence, we should apply a bandpass filter
# in the range where the antennas are sensitive. This will reduce the noise in the data and make the
# signal more visible. The optimal frequency range and filter type depends on the antenna type and
# the expected signal. For the RNO-G antennas, a bandpass filter between 100 MHz and 600 MHz is a good
# general choice.
channelBandPassFilter.run(
evt, station, det,
passband=[0.1 * units.GHz, 0.6 * units.GHz],
filter_type='butter', order=10)

# The signal chain amplifies and disperses the signal. This module will correct for the effects of the
# analog signal chain, i.e., everything between the antenna and the ADC. This will typically increase the
# signal-to-noise ratio and make the signal more visible.
hardwareResponseIncorporator.run(evt, station, det, sim_to_data=False, mode='phase_only')

# The antennas often pick up continuous wave (CW) signals from noise various sources. These signals can be
# very strong and can make it difficult to see other signals. This module will remove the CW signals from
# the data by dynamically identifying and removing the contaminated frequency bins.
# An alternative module is the channelSineWaveFilter, which only removes the noise contribution from the CW
# but not the thermal noise of that frequency. However, this is more computationally expensive.
channelCWNotchFilter.run(evt, station, det)

# The data is now preprocessed and ready for further analysis. The next steps depend on the analysis
# you want to perform. For example, you can now search for signals in the data, determine the arrival
# direction of the signal, or reconstruct the energy of the signal.
# The channelSignalReconstructor module is a good starting point for the signal reconstruction.
channelSignalReconstructor.run(evt, station, det)
Loading
Loading