GBM-data-tools/plot/drm.py

312 lines
12 KiB
Python

# drm.py: Plot class for responses
#
# Authors: William Cleveland (USRA),
# Adam Goldstein (USRA) and
# Daniel Kocevski (NASA)
#
# Portions of the code are Copyright 2020 William Cleveland and
# Adam Goldstein, Universities Space Research Association
# All rights reserved.
#
# Written for the Fermi Gamma-ray Burst Monitor (Fermi-GBM)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import matplotlib.pyplot as plt
from .globals import *
from .gbmplot import GbmPlot, HeatMap, EffectiveArea
class ResponseMatrix(GbmPlot):
"""Class for plotting a response matrix.
Parameters:
data (:class:`~gbm.data.RSP`, optional): The response object
colorbar (bool, optional): If True, plot the colorbar for the
effective area scale. Default is True.
multi (bool, optional):
If True, plots a multiplot window showing the matrix and the integrated
effective area as a function of incident energy and recorded energy
num_contours (int, optional): Number of contours to plot. Default is 100
**kwargs: Options to pass to :class:`~.gbmplot.GbmPlot`
Attributes:
ax (:class:`matplotlib.axes`): The matplotlib axes object for the plot
canvas (Canvas Backend object): The plotting canvas, if set upon
initialization.
data (:class:`~gbm.plot.gbmplot.HeatMap'): The matrix plot object
fig (:class:`matplotlib.figure`): The matplotlib figure object
xlim (float, float): The plotting range of the x axis.
This attribute can be set.
xscale (str): The scale of the x axis, either 'linear' or 'log'.
This attribute can be set.
ylim (float, float): The plotting range of the y axis.
This attribute can be set.
yscale (str): The scale of the y axis, either 'linear' or 'log'.
This attribute can be set.
"""
_background = 'black'
def __init__(self, data=None, colorbar=True, multi=False, canvas=None,
axis=None, num_contours=100, **kwargs):
self._drm = None
self._colorbar = colorbar
self._multi = multi
# do the multiplot
if multi:
self._colorbar = False
axis, ax_x, ax_y = self._init_multiplot()
self._p = PhotonEffectiveArea(data=data, canvas=canvas, axis=ax_x,
**kwargs)
self._c = ChannelEffectiveArea(data=data, canvas=canvas, axis=ax_y,
orientation='horizontal', **kwargs)
ax_x.get_xaxis().set_visible(False)
ax_y.get_yaxis().set_visible(False)
super().__init__(canvas=canvas, axis=axis, **kwargs)
self._ax.set_facecolor(self._background)
# initialize the plot axes, labels, ticks, and scales
self._ax.set_xlabel('Photon Energy (keV)', fontsize=PLOTFONTSIZE)
self._ax.set_ylabel('Channel Energy (keV)', fontsize=PLOTFONTSIZE)
self._ax.xaxis.set_tick_params(labelsize=PLOTFONTSIZE)
self._ax.yaxis.set_tick_params(labelsize=PLOTFONTSIZE)
self._ax.set_xscale('log')
self._ax.set_yscale('log')
# plot data and/or background if set on init
if data is not None:
self.set_response(data, index=0, num_contours=num_contours)
self._ax.set_xlim(data.photon_bins[0][0], data.photon_bins[1][-1])
self._ax.set_ylim(data.ebounds['E_MIN'][0],
data.ebounds['E_MAX'][-1])
@property
def data(self):
return self._drm
def _init_multiplot(self):
# initialize the multiplot
# dimensions
left, width = 0.12, 0.55
bottom, height = 0.12, 0.55
bottom_h = left_h = left + width
matrix = [left, bottom, width, height]
histx = [left, bottom_h, width, 0.40]
histy = [left_h, bottom, 0.40, height]
# create the plot axes
main_ax = plt.axes(matrix)
ax_x = plt.axes(histx)
ax_y = plt.axes(histy)
return (main_ax, ax_x, ax_y)
def set_response(self, data, index=None, atime=None, **kwargs):
"""Set the response data.
Args:
data (:class:`~gbm.data.RSP`): The response object
index (int, optional): The index of the DRM to display
atime (float, optional): The time corresponding to a DRM
**kwargs: Arguments to pass to :class:`~.gbmplot.HeatMap`
"""
if index is not None:
drm = data.drm(index)
elif atime is not None:
drm = data.nearest_drm(atime)
else:
drm = data.drm(0)
self._drm = HeatMap(data.photon_bin_centroids, data.channel_centroids,
drm.T, self.ax, colorbar=self._colorbar, **kwargs)
# update the background color of the colorbar
if self._colorbar:
self._drm._artists[-1].patch.set_facecolor(self._background)
class PhotonEffectiveArea(GbmPlot):
"""Class for plotting the incident photon effective area
Parameters:
data (:class:`~gbm.data.RSP`, optional): The response object
**kwargs: Options to pass to :class:`~.gbmplot.GbmPlot`
Attributes:
ax (:class:`matplotlib.axes`): The matplotlib axes object for the plot
canvas (Canvas Backend object): The plotting canvas, if set upon
initialization.
data (:class:`~gbm.plot.gbmplot.EffectiveArea`): The effective area
plot object
fig (:class:`matplotlib.figure`): The matplotlib figure object
xlim (float, float): The plotting range of the x axis.
This attribute can be set.
xscale (str): The scale of the x axis, either 'linear' or 'log'.
This attribute can be set.
ylim (float, float): The plotting range of the y axis.
This attribute can be set.
yscale (str): The scale of the y axis, either 'linear' or 'log'.
This attribute can be set.
"""
def __init__(self, data=None, canvas=None, axis=None, **kwargs):
super().__init__(canvas=canvas, axis=axis, **kwargs)
self._data = None
# initialize the plot axes, labels, ticks, and scales
self._ax.set_xlabel('Photon Energy (keV)', fontsize=PLOTFONTSIZE)
self._ax.set_ylabel(r'Effective Area (cm$^2$)', fontsize=PLOTFONTSIZE)
self._ax.xaxis.set_tick_params(labelsize=PLOTFONTSIZE)
self._ax.yaxis.set_tick_params(labelsize=PLOTFONTSIZE)
self._ax.set_xscale('log')
self._ax.set_yscale('log')
# plot data and/or background if set on init
if data is not None:
self.set_response(data, index=0)
self._ax.set_xlim(data.photon_bins[0][0], data.photon_bins[1][-1])
@property
def data(self):
return self._data
def set_response(self, data, index=None, atime=None, **kwargs):
"""Set the response data.
Args:
data (:class:`~gbm.data.RSP`): The response object
index (int, optional): The index of the DRM to display
atime (float, optional): The time corresponding to a DRM
**kwargs: Arguments to pass to :class:`~.gbmplot.EffectiveArea`
"""
if (index is None) and (atime is None):
index = 0
_color, _alpha, _kwargs = self._settings()
effarea = data.photon_effective_area(index=index, atime=atime)
self._data = EffectiveArea(effarea, self._ax, color=_color,
alpha=_alpha, **_kwargs)
plt.draw()
def _settings(self):
"""The default settings for the plot. If a plot already
exists, use its settings instead.
"""
if self._data is None:
_color = DATA_COLOR
_alpha = None
_kwargs = {}
else:
_color = self._data.color
_alpha = self._data.alpha
_kwargs = self._data._kwargs
return (_color, _alpha, _kwargs)
class ChannelEffectiveArea(GbmPlot):
"""Class for plotting the recorded channel energy effective area.
Parameters:
data (:class:`~gbm.data.RSP`, optional): The response object
**kwargs: Options to pass to :class:`~.gbmplot.GbmPlot`
Attributes:
ax (:class:`matplotlib.axes`): The matplotlib axes object for the plot
canvas (Canvas Backend object): The plotting canvas, if set upon
initialization.
data (:class:`~gbm.plot.gbmplot.EffectiveArea`): The effective area
plot object
fig (:class:`matplotlib.figure`): The matplotlib figure object
xlim (float, float): The plotting range of the x axis.
This attribute can be set.
xscale (str): The scale of the x axis, either 'linear' or 'log'.
This attribute can be set.
ylim (float, float): The plotting range of the y axis.
This attribute can be set.
yscale (str): The scale of the y axis, either 'linear' or 'log'.
This attribute can be set.
"""
def __init__(self, data=None, canvas=None, axis=None,
orientation='vertical',
**kwargs):
super().__init__(canvas=canvas, axis=axis, **kwargs)
self._data = None
self._orientation = orientation
# initialize the plot axes, labels, ticks, and scales
if self._orientation == 'horizontal':
self._ax.set_ylabel('Channel Energy (keV)', fontsize=PLOTFONTSIZE)
self._ax.set_xlabel(r'Effective Area (cm$^2$)',
fontsize=PLOTFONTSIZE)
else:
self._ax.set_xlabel('Channel Energy (keV)', fontsize=PLOTFONTSIZE)
self._ax.set_ylabel(r'Effective Area (cm$^2$)',
fontsize=PLOTFONTSIZE)
self._ax.xaxis.set_tick_params(labelsize=PLOTFONTSIZE)
self._ax.yaxis.set_tick_params(labelsize=PLOTFONTSIZE)
self._ax.set_xscale('log')
self._ax.set_yscale('log')
# plot data and/or background if set on init
if data is not None:
self.set_response(data, index=0)
xrange = (data.ebounds['E_MIN'][0], data.ebounds['E_MAX'][-1])
if self._orientation == 'horizontal':
self._ax.set_ylim(xrange)
else:
self._ax.set_xlim(xrange)
@property
def data(self):
return self._data
def set_response(self, data, index=None, atime=None, **kwargs):
"""Set the response data.
Args:
data (:class:`~gbm.data.RSP`): The response object
index (int, optional): The index of the DRM to display
atime (float, optional): The time corresponding to a DRM
**kwargs: Arguments to pass to :class:`~.gbmplot.EffectiveArea`
"""
if (index is None) and (atime is None):
index = 0
_color, _alpha, _kwargs = self._settings()
effarea = data.photon_effective_area(index=index, atime=atime)
self._data = EffectiveArea(effarea, self._ax, color=_color,
alpha=_alpha, orientation=self._orientation,
**_kwargs)
def _settings(self):
"""The default settings for the plot. If a plor already
exists, use its settings instead.
"""
if self._data is None:
_color = DATA_COLOR
_alpha = None
_kwargs = {}
else:
_color = self._data.color
_alpha = self._data.alpha
_kwargs = self._data._kwargs
return (_color, _alpha, _kwargs)