Skip to content

Commit

Permalink
feat(sensitivity): Add sensitivity plot
Browse files Browse the repository at this point in the history
  • Loading branch information
nritsche committed Sep 29, 2020
1 parent 2c9c9ac commit 67402cb
Show file tree
Hide file tree
Showing 7 changed files with 371 additions and 1 deletion.
4 changes: 4 additions & 0 deletions bondia/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@
FILE_TYPES = {
"delayspectrum": "delayspectrum_lsd_*.h5",
"ringmap": "ringmap_validation_freqs_lsd_*.h5",
"sensitivity": "sensitivity_lsd_*.h5",
"rfi": "rfi_mask_lsd_*.h5",
}
CONTAINER_TYPES: Dict[str, Type[Union[DelaySpectrum, RingMap]]] = {
"delayspectrum": containers.DelaySpectrum,
"ringmap": ccontainers.RingMap,
"sensitivity": containers.SystemSensitivity,
"rfi": containers.RFIMask,
}


Expand Down
39 changes: 39 additions & 0 deletions bondia/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .plot.delayspectrum import DelaySpectrumPlot
from .plot.ringmap import RingMapPlot
from .plot.sensitivity import SensitivityPlot


logger = logging.getLogger(__name__)
Expand All @@ -27,8 +28,13 @@ def populate_template(self, template):
self._data, self._config_plots.get("delayspectrum", {})
)
ringmap = RingMapPlot(self._data, self._config_plots.get("ringmap", {}))
sensitivity = SensitivityPlot(
self._data, self._config_plots.get("sensitivity", {})
)

self._plot[delay.id] = delay
self._plot[ringmap.id] = ringmap
self._plot[sensitivity.id] = sensitivity

# Load revision, lsd selectors and set initial values
rev_selector = pn.widgets.Select(
Expand All @@ -39,6 +45,7 @@ def populate_template(self, template):
)
delay.revision = rev_selector.value
ringmap.revision = rev_selector.value
sensitivity.revision = rev_selector.value
day_selector = pn.widgets.Select(
options=list(self._data.days(rev_selector.value)),
width=self._width_drawer_widgets,
Expand All @@ -47,6 +54,7 @@ def populate_template(self, template):
)
delay.lsd = day_selector.value
ringmap.lsd = day_selector.value
sensitivity.lsd = day_selector.value

def update_days(day_selector, event):
"""Update days depending on selected revision."""
Expand All @@ -58,9 +66,11 @@ def update_days(day_selector, event):
# Link selected day, revision to plots
rev_selector.link(delay, value="revision")
rev_selector.link(ringmap, value="revision")
rev_selector.link(sensitivity, value="revision")
rev_selector.link(day_selector, callbacks={"value": update_days})
day_selector.link(delay, value="lsd")
day_selector.link(ringmap, value="lsd")
day_selector.link(sensitivity, value="lsd")

# Add a title over the plots showing the selected day and rev (and keep it updated)
data_description = pn.pane.Markdown(
Expand Down Expand Up @@ -136,6 +146,35 @@ def toggle_ringmap(event):
components.append((f"plot_{ringmap.id}", ringmap.panel_row))
template.add_variable("title_ringmap", ringmap.name_)

self._toggle_plot[sensitivity.id] = pn.widgets.Toggle(
name=f"Deactivate {sensitivity.name_}",
button_type="success",
value=True,
width=self._width_drawer_widgets,
)

def toggle_sensitivity(event):
if event.new:
self._plot[sensitivity.id].panel_row = True
self._toggle_plot[sensitivity.id].button_type = "success"
self._toggle_plot[
sensitivity.id
].name = f"Deactivate {sensitivity.name_}"
else:
self._plot[sensitivity.id].panel_row = False
self._toggle_plot[sensitivity.id].button_type = "danger"
self._toggle_plot[sensitivity.id].name = f"Activate {sensitivity.name_}"

self._toggle_plot[sensitivity.id].param.watch(toggle_sensitivity, "value")
self._toggle_plot[sensitivity.id].param.trigger("value")
for t in self._toggle_plot.values():
t.param.trigger("value")
components.append(
(f"toggle_{sensitivity.id}", self._toggle_plot[sensitivity.id])
)
components.append((f"plot_{sensitivity.id}", sensitivity.panel_row))
template.add_variable("title_sensitivity", sensitivity.name_)

for name, c in components:
template.add_panel(name, c)
return template
Expand Down
253 changes: 253 additions & 0 deletions bondia/plot/sensitivity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import copy
import holoviews as hv
import logging
import numpy as np
import panel
import param

from holoviews.operation.datashader import datashade, rasterize
from holoviews.plotting.util import process_cmap
from matplotlib import cm as matplotlib_cm

from caput.config import Reader, Property
from ch_util import ephemeris

from .plot import BondiaPlot

# TODO: the ephemeris module will get moved to caput soon
from ..util.ephemeris import source_rise_set
from ..util.exception import DataError
from ..util.plotting import hv_image_with_gaps

logger = logging.getLogger(__name__)


class SensitivityPlot(param.Parameterized, BondiaPlot, Reader):
"""
Attributes
----------
lsd : int
Local stellar day.
transpose
Transpose the plot if True. Default `True`.
log
True for logarithmic color map (z-values). Default `False`.
colormap_range
(optional, if using datashader) Select limits of color map values (z-values). Default
`None`.
serverside_rendering
True to use datashader. Automatically selects colormap for every zoom level, sends
pre-rendered images to client. Default `True`.
"""

# Display text for polarization option: mean of XX and YY
mean_pol_text = "Mean(XX, YY)"

# Limits to display in ringmap heatmap
ylim = (400, 800)
xlim = (0, 360)
zlim = (0.01, 0.1)

# Config
_cache_reset_time = Property(
proptype=int, key="flag_cache_reset_seconds", default=86400
)
_cache_flags = Property(proptype=bool, key="cache_flags", default=False)

# parameters
transpose = param.Boolean(default=False)
logarithmic_colorscale = param.Boolean(default=True)
# Default: turn on datashader and disable colormap range
serverside_rendering = param.Selector(
objects=[None, rasterize, datashade], default=rasterize
)
colormap_range = param.Range(default=zlim, constant=False)

# Hide lsd, revision selectors by setting precedence < 0
lsd = param.Selector(precedence=-1)
revision = param.Selector(precedence=-1)

polarization = param.ObjectSelector()
mark_day_time = param.Boolean(default=True)
mask_rfi = param.Boolean(default=True)

def __init__(self, data, config, **params):
self.data = data
self.selections = None
self._chime_obs = ephemeris.chime_observer()

BondiaPlot.__init__(self, "Sensitivity")
param.Parameterized.__init__(self, **params)
self.read_config(config)

@param.depends("serverside_rendering", watch=True)
def update_serverside_rendering(self):
# Disable colormap range selection if using datashader (because it uses auto values)
self.param["colormap_range"].constant = self.serverside_rendering == datashade

def make_selection(self, data, key):
objects = list(data.index_map[key])
default = data.index_map[key][0]
return objects, default

@param.depends("lsd", watch=True)
def update_pol(self):
try:
rm = self.data.load_file(self.revision, self.lsd, "sensitivity")
except DataError as err:
logger.error(f"Unable to get available polarisations from file: {err}")
return
objects, value = self.make_selection(rm, "pol")
if "XX" in objects and "YY" in objects:
objects.append(self.mean_pol_text)
value = self.mean_pol_text
self.param["polarization"].objects = objects
self.polarization = value

@param.depends(
"lsd",
"transpose",
"logarithmic_colorscale",
"serverside_rendering",
"colormap_range",
"polarization",
"mark_day_time",
"mask_rfi",
)
def view(self):
try:
sens_container = self.data.load_file(self.revision, self.lsd, "sensitivity")
except DataError as err:
return panel.pane.Markdown(
f"Error: {str(err)}. Please report this problem."
)

# Index map for ra (x-axis)
sens_csd = ephemeris.csd(sens_container.time)
index_map_ra = (sens_csd % 1) * 360
axis_name_ra = "RA [degrees]"

# Index map for frequency (y-axis)
index_map_f = np.linspace(800.0, 400.0, 1024, endpoint=False)
axis_name_f = "Frequency [MHz]"

# Apply data selections
if self.polarization == self.mean_pol_text:
sel_pol = np.where(
(sens_container.index_map["pol"] == "XX")
| (sens_container.index_map["pol"] == "YY")
)[0]
sens = np.squeeze(sens_container.measured[:, sel_pol])
sens = np.squeeze(np.nanmean(sens, axis=1))
else:
sel_pol = np.where(sens_container.index_map["pol"] == self.polarization)[0]
sens = np.squeeze(sens_container.measured[:, sel_pol])

if self.mask_rfi:
try:
rfi_container = self.data.load_file(self.revision, self.lsd, "rfi")
except DataError as err:
return panel.pane.Markdown(
f"Error: {str(err)}. Please report this problem."
)
rfi = np.squeeze(rfi_container.mask[:])
sens *= np.where(rfi, np.nan, 1)

if self.transpose:
sens = sens.T
index_x = index_map_f
index_y = index_map_ra
axis_names = [axis_name_f, axis_name_ra]
xlim, ylim = self.ylim, self.xlim
else:
index_x = index_map_ra
index_y = index_map_f
axis_names = [axis_name_ra, axis_name_f]
xlim, ylim = self.xlim, self.ylim

image_opts = {
"clim": self.colormap_range,
"logz": self.logarithmic_colorscale,
"cmap": process_cmap("viridis", provider="matplotlib"),
"colorbar": True,
"xticks": [0, 60, 120, 180, 240, 300, 360],
}
overlay_opts = {
"xlim": xlim,
"ylim": ylim,
}

# Fill in missing data
img = hv_image_with_gaps(
index_x, index_y, sens, opts=image_opts, kdims=axis_names
).opts(**overlay_opts)

if self.serverside_rendering is not None:
# set colormap
cmap_inferno = copy.copy(matplotlib_cm.get_cmap("viridis"))

# Set z-axis normalization (other possible values are 'eq_hist', 'cbrt').
if self.logarithmic_colorscale:
normalization = "log"
else:
normalization = "linear"

# datashade/rasterize the image
img = self.serverside_rendering(
img,
cmap=cmap_inferno,
precompute=True,
x_range=xlim,
y_range=ylim,
normalization=normalization,
# TODO: set xticks like above
)

if self.mark_day_time:
# Calculate the sun rise/set times on this sidereal day (it's not clear to me there
# is exactly one of each per day, I think not)
sf_obs = self._chime_obs.skyfield_obs()

# Start and end times of the CSD
start_time = self._chime_obs.lsd_to_unix(self.lsd.lsd)
end_time = self._chime_obs.lsd_to_unix(self.lsd.lsd + 1)

times, rises = source_rise_set(
sf_obs,
ephemeris.skyfield_wrapper.ephemeris["sun"],
start_time,
end_time,
diameter=-10,
)
sun_rise = 0
sun_set = 0
for t, r in zip(times, rises):
if r:
sun_rise = (self._chime_obs.unix_to_lsd(t) % 1) * 360
else:
sun_set = (self._chime_obs.unix_to_lsd(t) % 1) * 360

# Highlight the day time data
opts = {
"color": "grey",
"alpha": 0.5,
"line_width": 1,
"line_color": "black",
"line_dash": "dashed",
}

span = hv.HSpan if self.transpose else hv.VSpan
if sun_rise < sun_set:
img *= span(sun_rise, sun_set).opts(**opts)
else:
img *= span(self.xlim[0], sun_set).opts(**opts)
img *= span(sun_rise, self.xlim[-1]).opts(**opts)

img.opts(
# Fix height, but make width responsive
height=500,
responsive=True,
bgcolor="lightgray",
)

return panel.Row(img, width_policy="max")
3 changes: 3 additions & 0 deletions bondia/templates/mdl.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<div align="center"><h4>Select Plots</h4></div>
<div>{{ embed(roots.toggle_delay_spectrum) }}</div>
<div>{{ embed(roots.toggle_ringmap) }}</div>
<div>{{ embed(roots.toggle_sensitivity) }}</div>
<hr>
<div align="center"><h4>Decision</h4></div>
<button class="mdl-button mdl-js-button mdl-button--raised" disabled>
Expand All @@ -58,6 +59,8 @@ <h4>{{ title_delay_spectrum }}</h4>
{{ embed(roots.plot_delay_spectrum) }}
<h4>{{ title_ringmap }}</h4>
{{ embed(roots.plot_ringmap) }}
<h4>{{ title_sensitivity }}</h4>
{{ embed(roots.plot_sensitivity) }}
</div>

<!-- List unused components here -->
Expand Down
3 changes: 2 additions & 1 deletion bondia/templates/mdl_tabs.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<div align="center"><h4>Select Plots</h4></div>
<div>{{ embed(roots.toggle_delay_spectrum) }}</div>
<div>{{ embed(roots.toggle_ringmap) }}</div>
<div>{{ embed(roots.toggle_sensitivity) }}</div>
<hr>
<div align="center"><h4>Decision</h4></div>
<button class="mdl-button mdl-js-button mdl-button--raised" disabled>
Expand Down Expand Up @@ -66,7 +67,7 @@
<section class="mdl-layout__tab-panel" id="sensitivity">
<div class="page-content">
{{ embed(roots.data_description2) }}
<p>Nothing here yet...</p>
{{ embed(roots.plot_sensitivity) }}
</div>
</section>
</main>
Expand Down
Loading

0 comments on commit 67402cb

Please sign in to comment.