Skip to content
Snippets Groups Projects
Commit 643c5286 authored by Jameson Rollins's avatar Jameson Rollins
Browse files

Merge branch 'summary' into 'master'

See merge request jameson.rollins/locklost!96
parents 9750f3d5 1b290495
No related branches found
No related tags found
No related merge requests found
......@@ -3,7 +3,10 @@ import argparse
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict
from gpstime import gpsnow, gpstime
import pytz
import logging
from . import config
from .event import find_events
......@@ -16,82 +19,107 @@ EPOCHS = {
'week': int(gpsnow()) - 7*24*3600,
}
if config.IFO == 'H1':
local_tz = pytz.timezone('US/Pacific')
if config.IFO == 'L1':
local_tz = pytz.timezone('US/Eastern')
def grab_data(gps):
"""Returns relevant lockloss summary data within three time ranges.
Looks through O3 lockloss data and returns counts for the locklosses
from each state, the duration in the final state, and the first saturated
suspension channel for the three most common locklosses. Does this for the
run, the last 30 days, and the last week.
Looks through O3 lockloss data and returns counts for the specificed check
functions. Does this for the run, the last 30 days, and the last week.
"""
shift_times = {
'H1': [np.arange(0, 8), np.arange(8, 16), np.arange(16, 24)],
'L1': [np.concatenate(([23, 24], np.arange(22, 8))), np.arange(8, 12), np.arange(12, 22)]
}
shift_names = ['owl', 'day', 'eve']
shifts = {
'owl': {
'time': np.arange(0, 8),
'counts': 0,
},
'day': {
'time': np.arange(8, 16),
'counts': 0,
},
'eve': {
'time': np.arange(16, 24),
'counts': 0,
},
shift_names[x]: {
'time': shift_times[config.IFO][x],
'counts': 0
} for x in range(3)
}
transitions = []
transitions = defaultdict(int)
observe_durations = []
saturations = {
'als': [],
'darm': [],
'observe': [],
'H1':
{
'LOCKING_ALS': defaultdict(int),
'ACQUIRE_DRM1_1F': defaultdict(int),
'Observe': defaultdict(int),
},
'L1':
{
'Observe': defaultdict(int)
},
}
five_sats = []
tags = {
'MAINTENANCE': 0,
'ADS_EXCURSION': 0,
'BOARD_SAT': 0,
'BRS_GLITCH': 0,
'SEISMIC': 0,
'WINDY': 0,
'Unknown': 0
}
for i, event in enumerate(find_events(after=gps, state='0-600')):
transitions.append(event.transition_index[0])
tag_count = defaultdict(int)
tags = [
'MAINTENANCE',
'ADS_EXCURSION',
'BOARD_SAT',
'BRS_GLITCH',
'EARTHQUAKE',
'MICROSEISMIC',
'ANTHROPOGENIC',
'WINDY',
'Unknown',
]
event_count = 0
for event in find_events(after=gps, state='0-{}'.format(config.GRD_NOMINAL_STATE[0])):
transitions[event.transition_index[0]] += 1
# check event/append to relevant lists
observe_durations = check_durations(event, observe_durations)
saturations, five_sats = check_saturations(event, saturations, five_sats)
shifts = check_shift(event, shifts)
tags = check_tags(event, tags)
tag_count = check_tags(event, tags, tag_count)
event_count += 1
return transitions, observe_durations, saturations, five_sats, shifts, tags
logging.info("Events analyzed: {}".format(event_count))
return transitions, observe_durations, saturations, five_sats, shifts, tag_count
def check_tags(event, tags):
ref_sum = sum(tags.values())
for tag_key in tags:
if event.has_tag(tag_key):
tags[tag_key] += 1
def check_tags(event, tags, tag_count):
"""Checks event for most relevant tag to the lockloss.
Checks event for tags, then increments the and returns the 'tag_count'
dictionary just once based on tag priority. Priority is given by the 'tags'
list.
"""
for tag in tags:
if event.has_tag(tag):
tag_count[tag] += 1
break
if sum(tags.values()) == ref_sum:
tags['Unknown'] += 1
return tags
else:
tag_count['Unknown'] += 1
return tag_count
def check_shift(event, shifts):
"""Checks which operating shift event happened during.
Checks which operator shift the lockloss gps happened during and increments
a counter for that shift (for locklosses from Observe).
"""
if not event.has_tag('OBSERVE'):
return shifts
gt = gpstime.fromgps(event.gps)
if event.has_tag('OBSERVE'):
for key in shifts.keys():
if gt.hour in shifts[key]['time']:
shifts[key]['counts'] += 1
break
gt = gt.astimezone(local_tz)
for key in shifts:
if gt.hour in shifts[key]['time']:
shifts[key]['counts'] += 1
break
return shifts
def check_durations(event, durations):
"""Check if lockloss was from Observe and log lock duration."""
"""Checks if lockloss was from Observe and logs lock duration. """
if event.has_tag('OBSERVE'):
previous = event.previous_state
if previous:
......@@ -100,28 +128,41 @@ def check_durations(event, durations):
def check_saturations(event, saturations, five_sats):
"""Checks for first five saturating suspensions around time of lockloss.
Checks if lockloss is from one of the top three lockloss states, then
logs which five suspensions saturated first into five_sats.
"""
sat_path = event.path('saturations.csv')
sat_conditions = {
'H1': {
'Observe': event.has_tag('OBSERVE'),
'ACQUIRE_DRM1_1F': event.transition_index[0] == 101,
'LOCKING_ALS': event.transition_index[0] == 15,
},
'L1': {
'Observe': event.has_tag('OBSERVE'),
},
}
if os.path.exists(sat_path):
sats = get_five_sats(sat_path)
five_sats.append(sats)
sat_conditions = {
'observe': event.has_tag('OBSERVE'),
'darm': event.transition_index[0] == 101,
'als': event.transition_index[0] == 15,
}
for key, condition in sat_conditions.items():
for key, condition in sat_conditions[config.IFO].items():
if condition:
if os.path.exists(sat_path):
saturations[key].append(sats[0])
saturations[config.IFO][key][sats[0]] += 1
else:
saturations[key].append('No saturations')
saturations[config.IFO][key]['No saturations'] += 1
return saturations, five_sats
def get_five_sats(sat_path):
"""Returns shortened names of first (up to) five saturating suspension channels.
"""Returns shortened names of first five channels located at sat_path.
Returns list containing up to the first five saturations in a given
lockloss. Locklosses with no saturations will not be called due to logic
in check_saturations.
"""
all_sats = np.genfromtxt(
sat_path,
......@@ -131,8 +172,7 @@ def get_five_sats(sat_path):
)
five_sats = np.array([])
all_sats = np.array(all_sats, ndmin=1)
sat_lim = min([all_sats.size, 5])
for sat in all_sats[:sat_lim]:
for sat in all_sats:
# create shortened channel name (excluding IFO, quadrant, characters)
sat_123 = sat.split('-')
sat1 = sat_123[0]
......@@ -142,6 +182,8 @@ def get_five_sats(sat_path):
# Make sure the degenerate channels don't get added
if channel_shorthand not in five_sats:
five_sats = np.append(five_sats, channel_shorthand)
if len(five_sats) == 5:
break
return five_sats
......@@ -152,7 +194,6 @@ def plot_summary(path, epoch):
Plots histograms for lockloss state transitions, time lengths in Observing,
and first saturating suspension channel for the three most common lockloss
states. Saves these to the example_plots directory.
"""
epoch_path = os.path.join(path, epoch)
try:
......@@ -160,39 +201,19 @@ def plot_summary(path, epoch):
except FileExistsError:
pass
transitions, observe_durations, saturations, five_sats, shifts, tags = grab_data(EPOCHS[epoch])
state_occurence = np.array([])
sat_occurence = {'als': [], 'darm': [], 'observe': []}
sat_names = {'observe': 'Observe', 'darm': 'ACQUIRE_DRM1_1F', 'als': 'LOCKING_ALS'}
sat_positions = {'als': 0, 'darm': 0, 'observe': 0}
# Collecting str values for the histogram x-axis and counting number
# of values in each 'bin'
# Transition state bin structuring
states = set(transitions)
for state in states:
state_occurence = np.append(state_occurence, transitions.count(state))
state_bar = sorted(zip(states, state_occurence)) # sorting states numerically
states, state_occurence = zip(*state_bar)
state_position = np.arange(len(states)) # for histogram later
str_states = [str(i) for i in states] # for histogram later
# Saturating suspension channel bin structuring
sat_sets = {key:set(val) for key, val in saturations.items()}
for key, sat_set in sat_sets.items():
for channel in sat_set:
sat_occurence[key] = np.append(sat_occurence[key], saturations[key].count(channel))
sat_bar = sorted(zip(sat_set, sat_occurence[key]))
sat_sets[key], sat_occurence[key] = zip(*sat_bar)
sat_positions[key] = np.arange(len(sat_set))
transitions, observe_durations, saturations, five_sats, shifts, tag_count = grab_data(EPOCHS[epoch])
plotutils.set_rcparams()
# Transition state plot
fig, ax = plt.subplots(1, figsize=(22, 16))
state_position = np.arange(len(transitions))
sort_keys = list(transitions.keys())
sort_keys.sort()
sort_values = [transitions[x] for x in sort_keys if bool(sort_keys)]
fig, ax = plt.subplots(1, figsize=(22,16))
ax.bar(
state_position,
state_occurence,
sort_values,
align='center',
)
ax.set_xlabel('State from which lockloss has occurred', labelpad=10)
......@@ -200,13 +221,12 @@ def plot_summary(path, epoch):
ax.set_title('O3 lockloss occurences by final state: %s' % (epoch))
ax.set_xticks(state_position)
ax.tick_params(axis='x', which='major', labelsize=18)
ax.set_xticklabels(str_states, rotation=45, ha='right')
ax.set_xticklabels(sort_keys, rotation=45, ha='right')
ax.set_xlim([-1, state_position.size])
plt.gcf().text(0.02, 0.02, "Created: {}".format(gpsnow()), fontsize=16)
fig.tight_layout()
plot_name = 'Lockloss_states'
outpath_plot = os.path.join(epoch_path, plot_name)
outpath_plot = os.path.join(epoch_path, 'Lockloss_states')
fig.savefig(outpath_plot, bbox_inches='tight')
plt.close()
......@@ -230,29 +250,32 @@ def plot_summary(path, epoch):
plt.gcf().text(0.02, 0.02, "Created: {}".format(gpsnow()), fontsize=16)
fig.tight_layout()
plot_name = 'Lock_durations'
outpath_plot = os.path.join(epoch_path, plot_name)
outpath_plot = os.path.join(epoch_path, 'Lock_durations')
fig.savefig(outpath_plot, bbox_inches='tight')
plt.close()
# Saturating suspension channel plot
for key, occurence in sat_occurence.items():
fig, ax = plt.subplots(1, figsize=(22, 16))
for state, sat_dict in saturations[config.IFO].items():
sat_position = np.arange(len(sat_dict))
sort_keys = list(sat_dict.keys())
sort_keys.sort()
sort_values = [sat_dict[x] for x in sort_keys if bool(sort_keys)]
fig, ax = plt.subplots(1, figsize=(22,16))
ax.bar(
sat_positions[key],
occurence,
sat_position,
sort_values,
align='center',
)
ax.set_xlabel('First suspension to saturate before lockloss', labelpad=10)
ax.set_ylabel('Number of locklosses')
ax.set_title('%s locklosses by saturating suspension: %s' % (sat_names[key], epoch))
ax.set_xticks(sat_positions[key])
ax.set_xticklabels(sat_sets[key], rotation=45, ha='right')
ax.set_xlim([-1,sat_positions[key].size])
ax.set_title('%s locklosses by saturating suspension: %s' % (state, epoch))
ax.set_xticks(sat_position)
ax.set_xticklabels(sort_keys, rotation=45, ha='right')
ax.set_xlim([-1, sat_position.size])
plt.gcf().text(0.02, 0.02, "Created: {}".format(gpsnow()), fontsize=16)
fig.tight_layout()
plot_name = '%s_lockloss_saturations' % (sat_names[key])
plot_name = '%s_lockloss_saturations' % (state)
outpath_plot = os.path.join(epoch_path, plot_name)
fig.savefig(outpath_plot, bbox_inches='tight')
plt.close()
......@@ -276,37 +299,28 @@ def plot_summary(path, epoch):
plt.gcf().text(0.02, 0.02, "Created: {}".format(gpsnow()), fontsize=16)
fig.tight_layout()
plot_name = 'lockloss_by_shift'
outpath_plot = os.path.join(epoch_path, plot_name)
outpath_plot = os.path.join(epoch_path, 'Lockloss_by_shift')
fig.savefig(outpath_plot, bbox_inches='tight')
plt.close()
# Associated tag plot
temp_dict = dict(tags)
for key, value in tags.items():
if value == 0:
del temp_dict[key]
tags = temp_dict
ts = tags.keys()
counts = tags.values()
fig, ax = plt.subplots(1, figsize=(22, 16))
shift_x = np.arange(0, len(tags))
fig, ax = plt.subplots(1, figsize=(22,16))
shift_x = np.arange(0, len(tag_count))
ax.bar(
shift_x,
counts,
tag_count.values(),
align='center',
)
ax.set_xlabel('Lockloss tags', labelpad=10)
ax.set_ylabel('Number of locklosses')
ax.set_title('Number of locklosses with given tag: %s' % (epoch))
ax.set_xticks(shift_x)
ax.set_xticklabels(ts, rotation=45, ha='right')
ax.set_xticklabels(tag_count.keys(), rotation=45, ha='right')
ax.set_xlim([-1, shift_x.size])
plt.gcf().text(0.02, 0.02, "Created: {}".format(gpsnow()), fontsize=16)
fig.tight_layout()
plot_name = 'lockloss_by_tag'
outpath_plot = os.path.join(epoch_path, plot_name)
outpath_plot = os.path.join(epoch_path, 'Lockloss_by_tag')
fig.savefig(outpath_plot, bbox_inches='tight')
plt.close()
......@@ -319,9 +333,7 @@ def _parser_add_arguments(parser):
def main(args=None):
"""Generate lockloss summary plots
"""
"""Generate lockloss summary plots."""
if not args:
parser = argparse.ArgumentParser()
_parser_add_arguments(parser)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment