import numpy as np
import scipy.ndimage
[docs]class Visualizer:
[docs] def __init__(self, size=1000):
'''Initializes the visualizer.
Parameters
----------
size : int
size x size is the size of the image to draw
'''
self.size = size
# Cache pixel_dists for annulus drawing
xv, yv = np.meshgrid(range(size), range(size))
self.pixel_dists = np.sqrt((xv - 0.5*size)**2 + (yv - 0.5*size)**2)
def _draw_annulus(self, r1, r2, color_intensities, min_radius, max_radius):
physical_dists = self.m_per_pix * self.pixel_dists
in_annulus = np.logical_and(physical_dists > r1, physical_dists < r2)
self.canvas[in_annulus] = np.array(color_intensities)
def _draw_layer(self, r1, r2, color_intensities, min_radius, max_radius):
min_y = round((r1 - min_radius)/self.m_per_pix)
if min_y < 0: min_y = 0
min_y = int(min_y)
max_y = round((r2 - min_radius)/self.m_per_pix)
if max_y > self.size: max_y = self.size
max_y = int(max_y)
self.canvas[min_y : max_y, :] = np.array(color_intensities)
def _draw_star(self, Rstar, max_radius, star_color, margin=0.3):
Rstar_pix = Rstar/self.m_per_pix
x_center = (Rstar + margin*max_radius)/self.m_per_pix
y_center = self.size/2
xv, yv = np.meshgrid(range(self.size), range(self.size))
pixel_dists = np.sqrt((xv - x_center)**2 + (yv - y_center)**2)
self.canvas[pixel_dists > Rstar_pix - 1] = 0
edge = np.logical_and(pixel_dists > Rstar_pix - 1, pixel_dists < Rstar_pix)
self.canvas[edge] = (Rstar_pix - pixel_dists[edge])[:, np.newaxis] * np.array(star_color)
[docs] def draw(self, transit_info, color_bins, star_color=[1, 1, 1],
method='disk', star_radius=None, star_margin=0.5,
max_dist=None, blur_std=1):
'''
Draws an image of a transiting exoplanet.
Parameters
----------
transit_info : dict
the dictionary returned by compute_depths in TransitDepthCalculator
when full_output = True
color_bins : array-like, shape (3,2)
Wavelength bins to use for the R, G, B channels. For example, if
color_bins[0] is [3e-6, 4e-6], the red channel will reflect all
light transmitted through the atmosphere between 3 and 4 microns.
star_color : array-like, length 3, optional
R, G, B values of the star light, with [1,1,1] being white
method : str, optional
Either 'disk' to draw the entire planetary disk with atmosphere, or
'layers' to draw a 1D atmospheric profile--essentially an extreme
zoom-in on the disk.
star_radius : float, optional
Stellar radius, in meters. If given, the stellar limb will be drawn
star_margin : float, optional
Distance from left side of canvas to stellar limb is star_margin * max_dist
max_dist : float, optional
Maximum distance from planet center to draw, in meters
blur_std : float, optional
STD of Gaussian blur to apply, in pixels
Returns
-------
canvas : array, shape (self.size, self.size, 3)
The image of the planet. Can be displayed with plt.imshow()
'''
self.canvas = np.zeros((self.size, self.size, 3))
radii = np.sort(transit_info["radii"])[::-1]
if max_dist is None:
max_dist = np.max(radii)
lambda_grid = transit_info["unbinned_wavelengths"]
absorption_fraction = 1 - np.exp(-transit_info["tau_los"])
if method == 'disk':
draw_method = self._draw_annulus
self.m_per_pix = 2.0 * max_dist / self.size
elif method == 'layers':
draw_method = self._draw_layer
self.m_per_pix = (max_dist - min(radii))/self.size
if star_radius is not None:
raise ValueError("Cannot draw star when using layers")
else:
raise ValueError("Method must be 'disk' or 'layers'")
for i in range(len(radii) - 1):
color_intensities = []
for j, (start, end) in enumerate(color_bins):
lambda_cond = np.logical_and(lambda_grid > start, lambda_grid < end)
transmitted_light = 1 - absorption_fraction[:,i][lambda_cond]
rel_intensity = np.mean(transmitted_light)
color_intensities.append(rel_intensity)
color_intensities = star_color * np.array(color_intensities)
draw_method(radii[i+1], radii[i], color_intensities, min(radii), max_dist)
draw_method(max(radii), np.inf, star_color, min(radii), max_dist)
if star_radius is not None:
self._draw_star(star_radius, max_dist, star_color, margin=star_margin)
self.canvas = scipy.ndimage.gaussian_filter(
self.canvas, [blur_std, blur_std, 0])
return self.canvas, self.m_per_pix