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

Remove Bokeh Templating from Readnoise Monitor #1238

Merged
merged 13 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
1 change: 0 additions & 1 deletion jwql/website/apps/jwql/monitor_pages/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from .monitor_cosmic_rays_bokeh import CosmicRayMonitor
from .monitor_readnoise_bokeh import ReadnoiseMonitor
233 changes: 136 additions & 97 deletions jwql/website/apps/jwql/monitor_pages/monitor_readnoise_bokeh.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,55 @@

::

from jwql.website.apps.jwql import monitor_pages
.
monitor_template = monitor_pages.ReadnoiseMonitor()
monitor_template.input_parameters = ('NIRCam', 'NRCA1_FULL')
"""

from datetime import datetime, timedelta
import os

from bokeh.embed import components
from bokeh.layouts import column, row
from bokeh.models import Panel, Tabs # bokeh <= 3.0
from bokeh.models import ColumnDataSource, HoverTool
# from bokeh.models import TabPanel, Tabs # bokeh >= 3.0
mfixstsci marked this conversation as resolved.
Show resolved Hide resolved
from bokeh.plotting import figure
import numpy as np

from jwql.bokeh_templating import BokehTemplate
from jwql.database.database_interface import session
from jwql.database.database_interface import FGSReadnoiseStats, MIRIReadnoiseStats, NIRCamReadnoiseStats, NIRISSReadnoiseStats, NIRSpecReadnoiseStats
from jwql.utils.constants import JWST_INSTRUMENT_NAMES_MIXEDCASE
from jwql.utils.constants import FULL_FRAME_APERTURES, JWST_INSTRUMENT_NAMES_MIXEDCASE

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))

class ReadnoiseMonitorData():
"""Class to hold bias data to be plotted

class ReadnoiseMonitor(BokehTemplate):
Parameters
----------

# Combine the input parameters into a single property because we
# do not want to invoke the setter unless all are updated
@property
def input_parameters(self):
return (self._instrument, self._aperture)
instrument : str
mfixstsci marked this conversation as resolved.
Show resolved Hide resolved
Instrument name (e.g. nircam)

@input_parameters.setter
def input_parameters(self, info):
self._instrument, self._aperture = info
self.pre_init()
self.post_init()
Attributes
----------

instrument : str
Instrument name (e.g. nircam)
aperture : str
Aperture name (e.g. apername)
mfixstsci marked this conversation as resolved.
Show resolved Hide resolved

"""

def __init__(self, instrument, aperture):
self.instrument = instrument
self.aperture = aperture
self.load_data()

def identify_tables(self):
"""Determine which database tables to use for the given instrument"""

mixed_case_name = JWST_INSTRUMENT_NAMES_MIXEDCASE[self._instrument.lower()]
mixed_case_name = JWST_INSTRUMENT_NAMES_MIXEDCASE[self.instrument.lower()]
self.stats_table = eval('{}ReadnoiseStats'.format(mixed_case_name))

def load_data(self):
Expand All @@ -59,102 +72,128 @@ def load_data(self):
# Query database for all data in readnoise stats with a matching aperture,
# and sort the data by exposure start time.
self.query_results = session.query(self.stats_table) \
.filter(self.stats_table.aperture == self._aperture) \
.filter(self.stats_table.aperture == self.aperture) \
.order_by(self.stats_table.expstart) \
.all()

session.close()

def pre_init(self):

# Start with default values for instrument and aperture because
# BokehTemplate's __init__ method does not allow input arguments
try:
dummy_instrument = self._instrument
dummy_aperture = self._aperture
except AttributeError:
self._instrument = 'NIRCam'
self._aperture = ''
class ReadNoiseFigure():
"""Generate tabbed plot displayed in JWQL web application
"""
def __init__(self, instrument):
instrument_apertures = FULL_FRAME_APERTURES[instrument.upper()]

self._embed = True
self.format_string = None
self.interface_file = os.path.join(SCRIPT_DIR, 'yaml', 'monitor_readnoise_interface.yaml')
self.tabs = []
for aperture in instrument_apertures:
readnoise_tab = ReadNoisePlotTab(instrument, aperture)
self.tabs.append(readnoise_tab.tab)

def post_init(self):
self.plot = Tabs(tabs=self.tabs)
self.tab_components = components(self.plot)

# Load the readnoise data
self.load_data()

# Update the mean readnoise figures
self.update_mean_readnoise_figures()
class ReadNoisePlotTab():
"""Class to make instrument/aperture panels
"""
def __init__(self, instrument, aperture):
self.instrument = instrument
self.aperture = aperture

# Update the readnoise difference image and histogram
self.update_readnoise_diff_plots()
self.db = ReadnoiseMonitorData(self.instrument, self.aperture)

def update_mean_readnoise_figures(self):
"""Updates the mean readnoise bokeh plots"""
self.plot_readnoise_amplifers()
self.plot_readnoise_difference_image()
self.plot_readnoise_histogram()

# Get the dark exposures info
filenames = [os.path.basename(result.uncal_filename).replace('_uncal.fits', '') for result in self.query_results]
expstarts_iso = np.array([result.expstart for result in self.query_results])
expstarts = np.array([datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') for date in expstarts_iso])
nints = [result.nints for result in self.query_results]
ngroups = [result.ngroups for result in self.query_results]
self.tab = Panel(child=column(row(*self.amp_plots),
self.diff_image_plot,
self.readnoise_histogram),
title=self.aperture)

# Update the mean readnoise figures for all amps
def plot_readnoise_amplifers(self):

self.amp_plots = []
for amp in ['1', '2', '3', '4']:
readnoise_vals = np.array([getattr(result, 'amp{}_mean'.format(amp)) for result in self.query_results])
self.refs['mean_readnoise_source_amp{}'.format(amp)].data = {'time': expstarts,
'time_iso': expstarts_iso,
'mean_rn': readnoise_vals,
'filename': filenames,
'nints': nints,
'ngroups': ngroups}
self.refs['mean_readnoise_figure_amp{}'.format(amp)].title.text = 'Amp {}'.format(amp)
self.refs['mean_readnoise_figure_amp{}'.format(amp)].hover.tooltips = [('file', '@filename'),
('time', '@time_iso'),
('nints', '@nints'),
('ngroups', '@ngroups'),
('readnoise', '@mean_rn')]

# Update plot limits if data exists
if len(readnoise_vals) != 0:
self.refs['mean_readnoise_xr_amp{}'.format(amp)].start = expstarts.min() - timedelta(days=3)
self.refs['mean_readnoise_xr_amp{}'.format(amp)].end = expstarts.max() + timedelta(days=3)
min_val, max_val = min(x for x in readnoise_vals if x is not None), max(x for x in readnoise_vals if x is not None)
if min_val == max_val:
self.refs['mean_readnoise_yr_amp{}'.format(amp)].start = min_val - 1
self.refs['mean_readnoise_yr_amp{}'.format(amp)].end = max_val + 1
else:
offset = (max_val - min_val) * .1
self.refs['mean_readnoise_yr_amp{}'.format(amp)].start = min_val - offset
self.refs['mean_readnoise_yr_amp{}'.format(amp)].end = max_val + offset

def update_readnoise_diff_plots(self):
"""Updates the readnoise difference image and histogram"""

amp_plot = figure(title='Amp {}'.format(amp), width=280, height=280, x_axis_type='datetime')
amp_plot.xaxis[0].ticker.desired_num_ticks = 4

readnoise_vals = np.array([getattr(result, 'amp{}_mean'.format(amp)) for result in self.db.query_results])
mfixstsci marked this conversation as resolved.
Show resolved Hide resolved

filenames = [os.path.basename(result.uncal_filename).replace('_uncal.fits', '') for result in self.db.query_results]
expstarts_iso = np.array([result.expstart for result in self.db.query_results])
expstarts = np.array([datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') for date in expstarts_iso])
nints = [result.nints for result in self.db.query_results]
ngroups = [result.ngroups for result in self.db.query_results]

source = ColumnDataSource(data=dict(
file=filenames,
expstarts=expstarts,
nints=nints,
ngroups=ngroups,
readnoise=readnoise_vals,
))

amp_plot.add_tools(HoverTool(tooltips=[("file", "@file"),
("time", "@expstarts"),
("nints", "@nints"),
("ngroups", "@ngroups"),
("readnoise", "@readnoise")]))

amp_plot.circle(x='expstarts', y='readnoise', source=source)

amp_plot.xaxis.axis_label = 'Date'
amp_plot.yaxis.axis_label = 'Mean Readnoise [DN]'

self.amp_plots.append(amp_plot)

def plot_readnoise_difference_image(self):
"""Updates the readnoise difference image"""

# Update the readnoise difference image and histogram, if data exists
if len(self.query_results) != 0:
# Get the most recent data; the entries were sorted by time when
# loading the database, so the last entry will always be the most recent.
diff_image_png = self.query_results[-1].readnoise_diff_image

self.diff_image_plot = figure(title='Readnoise Difference (most recent dark - pipeline reffile)',
height=500, width=500, sizing_mode='scale_width')

if len(self.db.query_results) != 0:
mfixstsci marked this conversation as resolved.
Show resolved Hide resolved
diff_image_png = self.db.query_results[-1].readnoise_diff_image
diff_image_png = os.path.join('/static', '/'.join(diff_image_png.split('/')[-6:]))
diff_image_n = np.array(self.query_results[-1].diff_image_n)
diff_image_bin_centers = np.array(self.query_results[-1].diff_image_bin_centers)

# Update the readnoise difference image and histogram
self.refs['readnoise_diff_image'].image_url(url=[diff_image_png], x=0, y=0, w=2048, h=2048, anchor="bottom_left")
self.refs['diff_hist_source'].data = {'n': diff_image_n,
'bin_centers': diff_image_bin_centers}
self.refs['diff_hist_xr'].start = diff_image_bin_centers.min()
self.refs['diff_hist_xr'].end = diff_image_bin_centers.max()
self.refs['diff_hist_yr'].start = diff_image_n.min()
self.refs['diff_hist_yr'].end = diff_image_n.max() + diff_image_n.max() * 0.05

# Update the readnoise difference image style
self.refs['readnoise_diff_image'].xaxis.visible = False
self.refs['readnoise_diff_image'].yaxis.visible = False
self.refs['readnoise_diff_image'].xgrid.grid_line_color = None
self.refs['readnoise_diff_image'].ygrid.grid_line_color = None
self.refs['readnoise_diff_image'].title.text_font_size = '22px'
self.refs['readnoise_diff_image'].title.align = 'center'
self.diff_image_plot.image_url(url=[diff_image_png], x=0, y=0, w=2048, h=2048, anchor="bottom_left")
self.diff_image_plot.xaxis.visible = False
self.diff_image_plot.yaxis.visible = False
self.diff_image_plot.xgrid.grid_line_color = None
self.diff_image_plot.ygrid.grid_line_color = None
self.diff_image_plot.title.text_font_size = '22px'
self.diff_image_plot.title.align = 'center'

def plot_readnoise_histogram(self):
"""Updates the readnoise histogram"""

diff_image_n = np.array(self.db.query_results[-1].diff_image_n)
mfixstsci marked this conversation as resolved.
Show resolved Hide resolved
diff_image_bin_centers = np.array(self.db.query_results[-1].diff_image_bin_centers)

hist_xr_start = diff_image_bin_centers.min()
hist_xr_end = diff_image_bin_centers.max()
hist_yr_start = diff_image_n.min()
hist_yr_end = diff_image_n.max() + diff_image_n.max() * 0.05

self.readnoise_histogram = figure(height=500, width=500,
x_range=(hist_xr_start, hist_xr_end),
y_range=(hist_yr_start, hist_yr_end),
sizing_mode='scale_width')

source = ColumnDataSource(data=dict(
x=diff_image_bin_centers,
y=diff_image_n,
))

self.readnoise_histogram.add_tools(HoverTool(tooltips=[("Data (x, y)", "(@x, @y)"),]))

self.readnoise_histogram.circle(x='x', y='y', source=source)

self.readnoise_histogram.xaxis.axis_label = 'Readnoise Difference [DN]'
self.readnoise_histogram.yaxis.axis_label = 'Number of Pixels'
self.readnoise_histogram.xaxis.axis_label_text_font_size = "15pt"
self.readnoise_histogram.yaxis.axis_label_text_font_size = "15pt"
Loading