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
../_images/tutorials_Plots_for_Operational_Review_8_1.png
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)
../_images/tutorials_Plots_for_Operational_Review_10_0.png
[ ]:
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)
../_images/tutorials_Plots_for_Operational_Review_11_0.png

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
../_images/tutorials_Plots_for_Operational_Review_13_1.png
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
../_images/tutorials_Plots_for_Operational_Review_15_1.png
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)
../_images/tutorials_Plots_for_Operational_Review_19_0.png
../_images/tutorials_Plots_for_Operational_Review_19_1.png
../_images/tutorials_Plots_for_Operational_Review_19_2.png
../_images/tutorials_Plots_for_Operational_Review_19_3.png
../_images/tutorials_Plots_for_Operational_Review_19_4.png
../_images/tutorials_Plots_for_Operational_Review_19_5.png

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)
../_images/tutorials_Plots_for_Operational_Review_21_0.png
../_images/tutorials_Plots_for_Operational_Review_21_1.png
../_images/tutorials_Plots_for_Operational_Review_21_2.png
../_images/tutorials_Plots_for_Operational_Review_21_3.png
../_images/tutorials_Plots_for_Operational_Review_21_4.png
../_images/tutorials_Plots_for_Operational_Review_21_5.png

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")}'
    )
../_images/tutorials_Plots_for_Operational_Review_23_0.png
../_images/tutorials_Plots_for_Operational_Review_23_1.png
../_images/tutorials_Plots_for_Operational_Review_23_2.png
../_images/tutorials_Plots_for_Operational_Review_23_3.png
../_images/tutorials_Plots_for_Operational_Review_23_4.png
../_images/tutorials_Plots_for_Operational_Review_23_5.png
[ ]: