Skip to content
Snippets Groups Projects
Commit 73e22965 authored by Oli Patane's avatar Oli Patane Committed by Camilla Compton
Browse files

Updated suspension saturation thresholds, added saturation labels, make lpy...

Updated suspension saturation thresholds, added saturation labels, make lpy plot for ETMX L3 if no saturating suspensions
parent a9d44556
No related branches found
No related tags found
No related merge requests found
......@@ -159,71 +159,66 @@ def get_saturation_threshold(chan, gps, IFO):
SATURATION_THRESHOLD_16 = (2**16) / 2
SATURATION_THRESHOLD_18 = (2**18) / 2
SATURATION_THRESHOLD_20 = (2**20) / 2
SATURATION_THRESHOLD_28 = (2**28) / 2
# Initializing bit channel lists so we don't run into issues when returning
TWENTYEIGHT_BIT_CHANNELS = []
TWENTY_BIT_CHANNELS = []
EIGHTEEN_BIT_CHANNELS = []
SIXTEEN_BIT_CHANNELS = [
'SUS-RM',
'SUS-ZM',
'SUS-OM1',
'SUS-OM2',
'SUS-OM3'
]
if IFO == 'L1':
# This corresponds to a change from 18 to 20-bit DAC for ETMX L3 channels.
# ETMX L3 channels after this date have counts that are four times higher
CHANGE_DAC_DATE = 1188518418
# This corresponds to a change from 18 to 20-bit DAC for ETMY L3 channels.
# ETMY L3 channels after this date have counts that are four times higher
CHANGE_DAC_DATE = 1188518418 # 2017/09/04
# This gets the DAC configuration in line with https://alog.ligo-la.caltech.edu/aLOG/index.php?callRep=63133
# Exact DAC times should be added later to better analyze old events
CHANGE_DAC_DATE2 = 1358033599
CHANGE_DAC_DATE2 = 1358033599 # 2023/01/17
# https://alog.ligo-la.caltech.edu/aLOG/index.php?callRep=63808
CHANGE_DAC_DATE3 = 1361911279
CHANGE_DAC_DATE3 = 1361911279 # 2023/03/03
elif IFO == 'H1':
# This corresponds to a change from 18 to 20-bit DAC for ETMX L3 channels.
# ETMX L3 channels after this date have counts that are four times higher
CHANGE_DAC_DATE = 1224961218
CHANGE_DAC_DATE = 1224961218 # 2018/10/30
# This gets the DAC configuration in line with https://alog.ligo-wa.caltech.edu/aLOG/index.php?callRep=66836
# Exact DAC times should be added later to better analyze old events
CHANGE_DAC_DATE2 = 1358024479
CHANGE_DAC_DATE2 = 1358024479 # 2023/01/17
# Change from 20-bit to 28-bit DACs on H1 EX L1, L2, L3 only
# See https://alog.ligo-wa.caltech.edu/aLOG/index.php?callRep=80167
CHANGE_DAC_DATE3 = 1410642901 # 2024/09/18
else:
# Assuming missed channel is 18-bit
return SATURATION_THRESHOLD_18
# Before CHANGE_DAC_DATE (L1 2017/09/04, H1 2018/10/30)
if gps < CHANGE_DAC_DATE:
SIXTEEN_BIT_CHANNELS = [
'SUS-RM1_M1',
'SUS-RM2_M1',
'SUS-ZM1_M1',
'SUS-ZM2_M1',
'SUS-OM1_M1',
'SUS-OM2_M1',
'SUS-OM3_M1'
]
if any(x in chan for x in SIXTEEN_BIT_CHANNELS):
return SATURATION_THRESHOLD_16
else:
return SATURATION_THRESHOLD_18
# Between CHANGE_DAC_DATE and CHANGE_DAC_DATE2
# (L1 2017/09/04-2023/01/17, H1 2018/10/30-2023/01/17)
elif gps >= CHANGE_DAC_DATE and gps < CHANGE_DAC_DATE2:
# ETM's are 20-bit and were changed at a date within our wanted
# lockloss time range
SIXTEEN_BIT_CHANNELS = [
'SUS-RM1_M1',
'SUS-RM2_M1',
'SUS-ZM1_M1',
'SUS-ZM2_M1',
'SUS-OM1_M1',
'SUS-OM2_M1',
'SUS-OM3_M1'
]
if IFO == 'H1':
ETM_L3_CHANNELS = 'SUS-ETMX_L3'
elif IFO == 'L1':
if IFO == 'L1':
ETM_L3_CHANNELS = 'SUS-ETMY_L3'
else:
# Assuming missed channel is 18-bit
return SATURATION_THRESHOLD_18
elif IFO == 'H1':
ETM_L3_CHANNELS = 'SUS-ETMX_L3'
if ETM_L3_CHANNELS in chan:
return SATURATION_THRESHOLD_20
elif any(x in chan for x in SIXTEEN_BIT_CHANNELS):
return SATURATION_THRESHOLD_16
else:
return SATURATION_THRESHOLD_18
else:
# Between CHANGE_DAC_DATE2 and CHANGE_DAC_DATE3
# (L1 2023/01/17-2023/03/03, H1 2023/01/17-2024/09/18)
elif gps >= CHANGE_DAC_DATE2:
if IFO == 'L1':
TWENTY_BIT_CHANNELS = [
'SUS-ETM',
......@@ -245,26 +240,17 @@ def get_saturation_threshold(chan, gps, IFO):
'SUS-OMC',
'SUS-IM'
]
SIXTEEN_BIT_CHANNELS = [
'SUS-ZM',
'SUS-OM1',
'SUS-OM2',
'SUS-OM3',
'SUS-RM'
]
if gps >= CHANGE_DAC_DATE3:
TWENTY_BIT_CHANNELS = TWENTY_BIT_CHANNELS + EIGHTEEN_BIT_CHANNELS
elif IFO == 'H1':
TWENTY_BIT_CHANNELS = [
'SUS-ETMX_L1',
'SUS-ETMY_L1',
'SUS-ETMX_L2',
'SUS-ETMY_L2',
'SUS-ETMX_L3',
'SUS-ETMY_L1',
'SUS-ETMY_L2',
'SUS-ETMY_L3',
'SUS-ITMX_L2',
'SUS-ITMY_L2',
'SUS-ITMX_L3',
'SUS-ITMY_L2',
'SUS-ITMY_L3',
'SUS-BS_M2',
'SUS-SRM_M2',
......@@ -291,25 +277,33 @@ def get_saturation_threshold(chan, gps, IFO):
'SUS-BS_M1',
'SUS-OMC',
]
SIXTEEN_BIT_CHANNELS = [
'SUS-ZM',
'SUS-OM1',
'SUS-OM2'
'SUS-OM3',
'SUS-RM'
]
else:
# Assuming missed channel is 18-bit
return SATURATION_THRESHOLD_18
if any(x in chan for x in TWENTY_BIT_CHANNELS):
return SATURATION_THRESHOLD_20
elif any(x in chan for x in EIGHTEEN_BIT_CHANNELS):
return SATURATION_THRESHOLD_18
elif any(x in chan for x in SIXTEEN_BIT_CHANNELS):
return SATURATION_THRESHOLD_16
else:
# Assuming missed channel is 18-bit
return SATURATION_THRESHOLD_18
# After CHANGE_DAC_DATE3 (L1 2023/03/03, H1 2024/09/18)
if gps >= CHANGE_DAC_DATE3:
if IFO == 'L1':
TWENTY_BIT_CHANNELS = TWENTY_BIT_CHANNELS + EIGHTEEN_BIT_CHANNELS
elif IFO == 'H1':
TWENTYEIGHT_BIT_CHANNELS = [
'SUS-ETMX_L1',
'SUS-ETMX_L2',
'SUS-ETMX_L3'
]
for new_susstage in TWENTYEIGHT_BIT_CHANNELS:
for susstage in TWENTY_BIT_CHANNELS:
if new_susstage == susstage:
TWENTY_BIT_CHANNELS.remove(susstage)
# Return saturation thresholds
if any(x in chan for x in TWENTYEIGHT_BIT_CHANNELS):
return SATURATION_THRESHOLD_28
elif any(x in chan for x in TWENTY_BIT_CHANNELS):
return SATURATION_THRESHOLD_20
elif any(x in chan for x in EIGHTEEN_BIT_CHANNELS):
return SATURATION_THRESHOLD_18
elif any(x in chan for x in SIXTEEN_BIT_CHANNELS):
return SATURATION_THRESHOLD_16
else:
# Assuming missed channel is 18-bit
return SATURATION_THRESHOLD_18
ANALOG_BOARD_CHANNELS = [
......
......@@ -31,13 +31,13 @@ def find_lpy(event):
sat_channel = ''
if not os.path.exists(inpath_csv):
logger.info("LPY plot bypassed (no saturating suspensions).")
return
with open(inpath_csv, 'r') as f:
first_sat = f.readline()
if first_sat:
sat_channel, sat_time = first_sat.split(' ', 1)
logger.info('No saturating suspensions, plotting ETMX L3 LPY')
sat_channel = f'{config.IFO}:SUS-ETMX_L3_MASTER_OUT_UR_DQ'
else:
with open(inpath_csv, 'r') as f:
first_sat = f.readline()
if first_sat:
sat_channel, sat_time = first_sat.split(' ', 1)
# generate the LPY mapping and channels to query from base channel
lpy_map = channel2lpy_coeffs(sat_channel)
......@@ -59,8 +59,13 @@ def find_lpy(event):
for title in titles:
for corner, coeff in lpy_map[title].items():
idx = channels.index(get_sus_channel(base_channel, corner))
saturation_threshold = config.get_saturation_threshold(get_sus_channel(base_channel, corner), gps, config.IFO)
lpy[title] += (bufs[idx].data * coeff) / (4 * saturation_threshold)
# If ETMX, make threshold < saturation value so we can see better
if base_channel[0:11] == 'H1:SUS-ETMX':
threshold = 524288
lpy[title] += (bufs[idx].data * coeff) / (4 * threshold)
else:
threshold = config.get_saturation_threshold(get_sus_channel(base_channel, corner), gps, config.IFO)
lpy[title] += (bufs[idx].data * coeff) / (4 * threshold)
colors = ['#2c7fb8', '#e66101', '#5e3c99']
fig, axs = plt.subplots(3, sharex=True, figsize=(27, 16))
......@@ -73,12 +78,16 @@ def find_lpy(event):
alpha=0.8,
lw=2,
)
if base_channel[0:11] == 'H1:SUS-ETMX':
label = '524288 cts (sat threshold = 134217728)'
else:
label = 'saturation threshold'
ax.axhline(
1,
color='red',
linestyle='--',
lw=3,
label='saturation threshold',
label=label,
)
ax.axhline(
-1,
......
......@@ -35,11 +35,13 @@ def find_saturations(event):
shift = -0.5*sat_search_window[0]
while expand is True:
relative_datasets = []
saturation_values = []
for idx, buf in enumerate(bufs):
channel_name = str(buf.channel)
saturation_threshold = config.get_saturation_threshold(channel_name, gps, config.IFO)
reduced_data = buf.data/(saturation_threshold)
relative_datasets.append(reduced_data)
saturation_values.append(saturation_threshold)
srate = buf.sample_rate
early_thresh = int(shift*srate)
if any(abs(reduced_data[:early_thresh]) >= 0.9):
......@@ -65,11 +67,18 @@ def find_saturations(event):
channel_name = str(buf.channel)
srate = buf.sample_rate
reduced_dataset = relative_datasets[idx]
saturation_value = saturation_values[idx]
t = np.arange(segment[0], segment[1], 1/srate)
newtime_idx = max(np.nonzero(t <= gps)[0])
sat_idxs = np.nonzero(abs(reduced_dataset[:newtime_idx]) >= 1)[0]
if len(sat_idxs) > 0:
saturations.append([channel_name, t[min(sat_idxs)], t, reduced_dataset])
saturations.append([
channel_name,
t[min(sat_idxs)],
t,
reduced_dataset,
saturation_value
])
if not saturations:
logger.info("No saturated suspension channels before lockloss.")
......@@ -104,7 +113,7 @@ def find_saturations(event):
val[2]-gps,
val[3],
color=config.SATURATION_CM[idx],
label=distill_channel_name(val[0]),
label=f'{distill_channel_name(val[0])}',
alpha=0.8,
lw=2,
)
......@@ -124,7 +133,7 @@ def find_saturations(event):
textcoords='offset points',
horizontalalignment='center',
verticalalignment='bottom',
bbox=dict(boxstyle="round", fc="w", ec="black", alpha=0.95),
bbox=dict(boxstyle="round", fc="w", ec="black", alpha=0.95)
)
# draw lines on plot for saturation thresholds, lock loss time
......@@ -147,6 +156,39 @@ def find_saturations(event):
lw=2
)
# Add labels listing saturation values above top red dashed line
chan_sats = []
for idx, val in enumerate(saturations[0:8]):
short_name = distill_channel_name(val[0])[:-3]
sat_val = '{:.2e}'.format(val[4])
name_sat = f'{short_name}: {sat_val}'
for j in range(0, len(chan_sats)):
if short_name in chan_sats[j]:
break
if sat_val in chan_sats[j]:
sat_split = chan_sats[j].split(':')
sat_chan_val = f'{sat_split[0]}, {short_name}:{sat_split[1]}'
chan_sats[j] = sat_chan_val
break
else:
chan_sats.append(name_sat)
saturation_thresh = 'Saturation thresholds:'
for sat_set in chan_sats:
saturation_thresh = f'{saturation_thresh} {sat_set};'
saturation_thresh = saturation_thresh[:-1]
# Find x and y coords for saturation text label that are set
# proportionally wrt to the y=1 horizontal line and the plot limits
xmin, xmax = ax.get_xlim()
sat_text_x = ((xmax - xmin)*0.03) + xmin
sat_text_y = ((ymax-ymin)*0.015) + 1
plt.text(
sat_text_x,
sat_text_y,
saturation_thresh,
color='red',
size=22
)
ax.legend(handles[::-1], labels[::-1], bbox_to_anchor=(1.1, 0.96), title='optic/stage/corner')
fig.tight_layout(pad=0.05)
ax.set_title('Saturating suspension channels (ordered by time of saturation)', y=1.04)
......@@ -166,8 +208,8 @@ def find_saturations(event):
def set_ylims(saturations, xmax, buffer):
"""Calculate ylims for the section of SUS channels occuring before lock loss time.
"""Calculate ylims for the section of SUS channels occuring before
lock loss time.
"""
ymaxs = []
ymins = []
......
......@@ -89,7 +89,15 @@
</div>
</div>
% else:
<p>LPY plots not created due to lack of saturating suspension channels.</p>
<hr />
<div class="container">
<div class="row">
<h3>Length-Pitch-Yaw Plots</h3>
<br />
<p>Length, pitch, and yaw drives reconstructed from osem DAC counts for ETMX L3 since no suspension stages saturated before lockloss time.</p>
% include('plots.tpl', plots=event_plot_urls(event, 'lpy'), size=6)
</div>
</div>
% end
<!-- PI Monitor -->
......
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