Example: Plots for 2026 KPF Operational Review
The plots bleow are being made for the KPF Operational Review in January, 2026. The code is posted to show the methods and to develop additional plots.
[1]:
import os, re
from modules.quicklook.src.analyze_time_series import AnalyzeTimeSeries
import operator
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
import time
from datetime import datetime, timedelta, timezone, date
from astropy.table import Table
import pandas as pd
%matplotlib inline
Update this configuration parameter to set the output directory for plots.
[2]:
plot_dir = '/code/KPF-Pipeline/AWH_notebooks/report_plots/'
[3]:
myTS = AnalyzeTimeSeries(backend='psql')
INFO: Starting AnalyzeTimeSeries
INFO: Starting KPF_TSDB
INFO: Jupyter Notebook environment detected.
INFO: Base data directory: /data/L0
INFO: Backend: psql
INFO: Table prefix: tsdb_
INFO: PSQL server: 127.0.0.1
INFO: PSQL username: timeseriesdba
INFO: PSQL user role: admin
INFO: Metadata table exists.
INFO: Metadata table read.
INFO: Data tables exist.
Time Series Plots
Ion Pump Pressure
[4]:
start_date = datetime(2022, 11, 9)
end_date = datetime(2026, 2, 6)
fig_path = f'{plot_dir}/ccd_pressure_time_series.png'
myTS.plot_time_series_multipanel('ccd_pressure', start_date=start_date, end_date=end_date, show_plot=True, clean=True)
myTS.plot_time_series_multipanel('ccd_pressure', start_date=start_date, end_date=end_date, fig_path=fig_path, clean=True)
INFO: Plotting from config: /code/KPF-Pipeline/static/tsdb_plot_configs/CCDs/ccd_pressure.yaml
INFO: Plotting from config: /code/KPF-Pipeline/static/tsdb_plot_configs/CCDs/ccd_pressure.yaml
Spectrometer Bench Temperature
[5]:
start_date = datetime(2022, 11, 9)
end_date = datetime(2026, 2, 6)
dict1 = {'col': 'kpfmet.TEMP', 'plot_type': 'scatter', 'unit': 'K', 'plot_attr': {'label': 'Hallway', 'marker': '.', 'linewidth': 0.5}}
dict2 = {'col': 'kpfmet.BENCH_TOP_COLL', 'plot_type': 'scatter', 'unit': 'K', 'plot_attr': {'label': r'Spectrometer Bench', 'marker': '.', 'linewidth': 0.5, 'color': 'darkgreen'}}
thispanelvars = [dict1]
thispaneldict = {'ylabel': 'Hallway\nTemperature ($^{\circ}$C)',
'legend_frac_size': 0.18}
halltemppanel = {'panelvars': thispanelvars,
'paneldict': thispaneldict}
thispanelvars = [dict2]
thispaneldict = {'ylabel': 'Spectrometer\nTemperature ($^{\circ}$C)',
'legend_frac_size': 0.18}
spectrometertemppanel = {'panelvars': thispanelvars,
'paneldict': thispaneldict}
panel_arr = [halltemppanel, spectrometertemppanel]
plotdict = {
"description": "Etalon RVs (autocal)",
"plot_type": "time_series_multipanel",
"panel_arr": panel_arr
}
fig_path = f'{plot_dir}/ccd_temperature_time_series.png'
myTS.plot_time_series_multipanel(plotdict, start_date=start_date, end_date=end_date, show_plot=True, clean=True)
myTS.plot_time_series_multipanel(plotdict, start_date=start_date, end_date=end_date, fig_path=fig_path, clean=True)
[ ]:
start_date = datetime(2022, 11, 9)
end_date = datetime(2026, 2, 6)
dict1 = {'col': 'RNGREEN1', 'plot_type': 'scatter', 'unit': 'e-', 'plot_attr': {'label': r'Green Amplifier 1', 'marker': '.', 'linewidth': 0.5, 'color': 'limegreen'}}
dict2 = {'col': 'RNGREEN2', 'plot_type': 'scatter', 'unit': 'e-', 'plot_attr': {'label': r'Green Amplifier 2', 'marker': '.', 'linewidth': 0.5, 'color': 'darkgreen'}}
dict3 = {'col': 'RNRED1', 'plot_type': 'scatter', 'unit': 'e-', 'plot_attr': {'label': r'Red Amplifier 1', 'marker': '.', 'linewidth': 0.5, 'color': 'indianred'}}
dict4 = {'col': 'RNRED2', 'plot_type': 'scatter', 'unit': 'e-', 'plot_attr': {'label': r'Red Amplifier 2', 'marker': '.', 'linewidth': 0.5, 'color': 'darkred'}}
thispanelvars = [dict1,dict2]
thispaneldict = {'ylabel': 'Green CCD\nRead Noise (e-)',
'yscale': 'log',
'ymin': 3,
'ymax': 40,
'title': 'CCD Read Noise',
'read_speed': 'regular',
'object_like': 'autocal',
'legend_frac_size': 0.18}
panel1 = {'panelvars': thispanelvars,
'paneldict': thispaneldict}
thispanelvars = [dict3,dict4]
thispaneldict = {'ylabel': 'Red CCD\nRead Noise (e-)',
'yscale': 'log',
'ymin': 3,
'ymax': 40,
'read_speed': 'regular',
'object_like': 'autocal',
'legend_frac_size': 0.18}
panel2 = {'panelvars': thispanelvars,
'paneldict': thispaneldict}
panel_arr = [panel1, panel2]
plotdict = {
"description": "CCD Read Noise",
"plot_title": "CCD Read Noise",
"plot_type": "time_series_multipanel",
"panel_arr": panel_arr
}
fig_path = f'{plot_dir}/ccd_readnoise_time_series.png'
myTS.plot_time_series_multipanel(plotdict, start_date=start_date, end_date=end_date, show_plot=True, clean=True)
myTS.plot_time_series_multipanel(plotdict, start_date=start_date, end_date=end_date, fig_path=fig_path, clean=True)
DRP Version
[7]:
start_date = datetime(2022, 11, 9)
end_date = datetime(2026, 2, 6)
fig_path = f'{plot_dir}/drp_tag_time_series.png'
myTS.plot_time_series_multipanel('drptag', start_date=start_date, end_date=end_date, show_plot=True, clean=True)
myTS.plot_time_series_multipanel('drptag', start_date=start_date, end_date=end_date, fig_path=fig_path, clean=True)
INFO: Plotting from config: /code/KPF-Pipeline/static/tsdb_plot_configs/DRP/drptag.yaml
INFO: Plotting from config: /code/KPF-Pipeline/static/tsdb_plot_configs/DRP/drptag.yaml
Etalon Temperature Excursions:
[8]:
start_date = datetime(2025, 4, 25)
end_date = datetime(2025, 7, 1)
#end_date = datetime(2025, 4, 30)
fig_path = f'{plot_dir}/etalon_time_series.png'
myTS.plot_time_series_multipanel('etalon', start_date=start_date, end_date=end_date, show_plot=True, clean=True)
myTS.plot_time_series_multipanel('etalon', start_date=start_date, end_date=end_date, fig_path=fig_path, clean=True)
INFO: Plotting from config: /code/KPF-Pipeline/static/tsdb_plot_configs/Subsys/etalon.yaml
INFO: Plotting from config: /code/KPF-Pipeline/static/tsdb_plot_configs/Subsys/etalon.yaml
Performance by Date Plots
Temperature Plots
[8]:
date_ranges = [
(datetime(2022, 11, 9), datetime(2026, 1, 1), 'All KPF Eras', 'kpfera_all'),
(datetime(2022, 11, 9), datetime(2024, 2, 3), 'KPF Era 1.0', 'kpfera_1_0'),
(datetime(2024, 2, 23), datetime(2024, 11, 1), 'KPF Era 2.0', 'kpfera_2_0'),
(datetime(2024, 11, 26), datetime(2025, 3, 28), 'KPF Era 2.6', 'kpfera_2_6'),
(datetime(2025, 4, 23), datetime(2025, 8, 29), 'KPF Era 3.0', 'kpfera_3_0'),
(datetime(2025, 10, 30), datetime(2026, 2, 6), 'KPF Era 4.0', 'kpfera_4_0'),
]
[9]:
spec_config = [
{'col': 'kpfgreen.STA_CCD_T', 'name': r'Green CCD $\Delta$T > 10 mK', 'op': '>', 'threshold': -99.99},
{'col': 'kpfgreen.STA_CCD_T', 'name': r'Green CCD $\Delta$T > 100 mK', 'op': '>', 'threshold': -99.9},
{'col': 'kpfgreen.STA_CCD_T', 'name': r'Green CCD $\Delta$T > 1000 mK', 'op': '>', 'threshold': -99.0},
{'col': 'kpfred.STA_CCD_T', 'name': r'Red CCD $\Delta$T > 10 mK', 'op': '>', 'threshold': -99.99},
{'col': 'kpfred.STA_CCD_T', 'name': r'Red CCD $\Delta$T > 100 mK', 'op': '>', 'threshold': -99.9},
{'col': 'kpfred.STA_CCD_T', 'name': r'Red CCD $\Delta$T > 1000 mK', 'op': '>', 'threshold': -99.0},
]
for start_date, end_date, era_name, fig_path_stub in date_ranges:
columns_to_display = list(dict.fromkeys(['datecode'] + [d['col'] for d in spec_config]))
df = myTS.db.dataframe_from_db(start_date=start_date, end_date=end_date, columns=columns_to_display)
plot_title = f'CCD Temperature Performance: {era_name}, {start_date.strftime("%Y-%m-%d")} - {end_date.strftime("%Y-%m-%d")}'
fig_path = f'{plot_dir}/ccd_temp_{fig_path_stub}.png'
show_plot = True
summary_by_datecode = myTS.performance_by_datecode(df, spec_config)
myTS.plot_performance_by_datecode(summary_by_datecode, spec_config, datecode_col='datecode',
plot_title=plot_title, show_plot=show_plot, fig_path=fig_path)
Pressure Plots
[10]:
date_ranges = [
(datetime(2022, 11, 9), datetime(2026, 1, 1), 'All KPF Eras', 'kpfera_all'),
(datetime(2022, 11, 9), datetime(2024, 2, 3), 'KPF Era 1.0', 'kpfera_1_0'),
(datetime(2024, 2, 23), datetime(2024, 11, 1), 'KPF Era 2.0', 'kpfera_2_0'),
(datetime(2024, 11, 26), datetime(2025, 3, 28), 'KPF Era 2.6', 'kpfera_2_6'),
(datetime(2025, 4, 23), datetime(2025, 8, 29), 'KPF Era 3.0', 'kpfera_3_0'),
(datetime(2025, 10, 30), datetime(2026, 2, 6), 'KPF Era 4.0', 'kpfera_4_0'),
]
spec_config = [
{
"name": "Green P < 0.0001 mbar",
"cols": ["kpfgreen.COL_PRESS", "kpfgreen.ECH_PRESS"],
"bool_expr": "(max(c0,c1)*1.333 > 0.0001)", # for failure
},
{
"name": "Red P < 0.0001 mbar",
"cols": ["kpfred.COL_PRESS", "kpfred.ECH_PRESS"],
"bool_expr": "(max(c0,c1)*1.333 > 0.0001)", # for failure
}
]
for start_date, end_date, era_name, fig_path_stub in date_ranges:
columns_to_display = list(dict.fromkeys(['datecode'] + [c for spec in spec_config for c in spec['cols']]))
df = myTS.db.dataframe_from_db(start_date=start_date, end_date=end_date, columns=columns_to_display)
plot_title = f'CCD Cryostat Pressure Performance: {start_date.strftime("%Y-%m-%d")} - {end_date.strftime("%Y-%m-%d")}'
fig_path = f'{plot_dir}/ccd_pressure_{fig_path_stub}.png'
summary_by_datecode = myTS.performance_by_datecode(df, spec_config)
myTS.plot_performance_by_datecode(summary_by_datecode, spec_config, datecode_col='datecode', plot_title=plot_title, show_plot=True)
Temperature Change per Day
[11]:
# 1. Define the stats we want to calculate
stats_config = {
'kpfmet.ECHELLE_TOP': ['range', 'mean']
}
# 2. Define the specifications
spec_config = [
{
'col': 'kpfmet.ECHELLE_TOP_range',
'name': r'|$T_\mathrm{max}$-$T_\mathrm{min}$| (mK)',
'stat_name': r'|$T_\mathrm{max}$-$T_\mathrm{min}$| < 10 mK',
'op': '>',
'threshold': 10.0, # Now defined in mK
'multiplier': 1000 # Used by the plotter below
}
]
for start_date, end_date, era_name, fig_path_stub in date_ranges:
# --- DATA FETCHING ---
# We must fetch the RAW columns needed for stats + the RAW columns needed for simple specs
# We do NOT fetch the "_range" columns because they don't exist in the DF yet
raw_cols_needed = ['datecode', 'kpfmet.ECHELLE_TOP', 'kpfmet.ECHELLE_BOTTOM', 'kpfgreen.STA_CCD_T']
df = myTS.db.dataframe_from_db(
start_date=start_date,
end_date=end_date,
columns=raw_cols_needed
)
if df.empty:
print(f"No data found for {era_name}")
continue
# --- ANALYSIS ---
# This creates 'kpfmet.ECHELLE_TOP_range' and 'Echelle Top Stability (K)_violation'
summary_by_datecode = myTS.performance_by_datecode(
df,
spec_config,
stats_config=stats_config
)
# --- PLOTTING ---
plot_title = f'Echelle & CCD Performance: {era_name}'
fig_path = f'{plot_dir}/kpf_echelle_temp_{fig_path_stub}.png'
# The updated plotter will now see 'kpfmet.ECHELLE_TOP_range' as a float (trend line)
# and the CCD specs as booleans (status matrix)
show_plot=True
myTS.plot_performance_by_datecode(
summary_by_datecode,
spec_config,
use_semilog=True,
ymin=0.001, # 1 mK
show_plot=True,
fig_path=fig_path,
plot_title = f'Echelle Temperature Range per Day: {era_name}, {start_date.strftime("%Y-%m-%d")} - {end_date.strftime("%Y-%m-%d")}'
)
[ ]: