Source code for species.plot.plot_evolution

"""
Module with functions for plotting the results obtained with the
:class:`~species.fit.fit_evolution.FitEvolution` class.
"""

from typing import List, Optional, Tuple

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

from typeguard import typechecked
from matplotlib.ticker import AutoMinorLocator

from species.read.read_isochrone import ReadIsochrone


[docs] @typechecked def plot_cooling( tag: str, n_samples: int = 50, cooling_param: str = "luminosity", xlim: Optional[Tuple[float, float]] = None, ylim: Optional[Tuple[float, float]] = None, xscale: Optional[str] = "linear", yscale: Optional[str] = "linear", title: Optional[str] = None, offset: Optional[Tuple[float, float]] = None, figsize: Optional[Tuple[float, float]] = (4.0, 2.5), output: Optional[str] = None, ) -> Tuple[mpl.figure.Figure, List[List[np.ndarray]], np.ndarray]: """ Function for plotting samples of cooling curves that are randomly drawn from the posterior distributions of the age and mass parameters that have been estimated with :class:`~species.fit.fit_evolution.FitEvolution`. Parameters ---------- tag : str Database tag where the samples are stored n_samples : int Number of randomly drawn cooling curves that will be plotted. cooling_param : str Type of cooling parameter that will be plotted ('luminosity' or 'radius'). xlim : tuple(float, float), None Limits of the wavelength axis. Automatic limits are used if the argument is set to ``None``. ylim : tuple(float, float), None Limits of the flux axis. Automatic limits are used if the argument is set to ``None``. xscale : str, None Scale of the x axis ('linear' or 'log'). The scale is set to ``'linear'`` if the argument is set to ``None``. yscale : str, None Scale of the x axis ('linear' or 'log'). The scale is set to ``'linear'`` if the argument is set to ``None``. title : str Title to show at the top of the plot. offset : tuple(float, float) Offset for the label of the x- and y-axis. figsize : tuple(float, float) Figure size. output : str, None Output filename for the plot. The plot is shown in an interface window if the argument is set to ``None``. Returns ------- matplotlib.figure.Figure The ``Figure`` object that can be used for further customization of the plot. np.ndarray Array with the cooling curves. The array contains :math:`L/L_\\odot` or radius as function of time for each companion and sample, so the shape is (n_companions, n_samples, n_ages). np.ndarray Array with the random indices that have been sampled from the posterior distribution. """ plt.close() if cooling_param not in ["luminosity", "radius"]: raise ValueError( "The argument of 'cooling_parameter' is " "not valid and should be set to " "'luminosity' or 'radius'." ) from species.data.database import Database species_db = Database() samples_box = species_db.get_samples(tag) samples = samples_box.samples attr = samples_box.attributes n_planets = attr["n_planets"] evolution_model = attr["evolution_model"] object_lbol = attr["object_lbol"] object_radius = attr["object_radius"] object_age = (np.mean(samples[:, 0]), np.std(samples[:, 0])) read_iso = ReadIsochrone(evolution_model) plt.rcParams["font.family"] = "serif" plt.rcParams["mathtext.fontset"] = "dejavuserif" if output is None: print("Plotting cooling curves...", end="", flush=True) else: print( f"Plotting cooling curves: {output}...", end="", flush=True, ) fig = plt.figure(1, figsize=figsize) gridsp = mpl.gridspec.GridSpec(n_planets, 1) gridsp.update(wspace=0, hspace=0.1, left=0, right=1, bottom=0, top=1) ax = [] for i in range(n_planets): ax.append(plt.subplot(gridsp[i, 0])) if xscale is None: xscale = "linear" if yscale is None: yscale = "linear" cool_curves = [] for i in range(n_planets): cool_curves.append([]) for i in range(n_planets): ax[i].set_xscale(xscale) ax[i].set_yscale(yscale) labelbottom = bool(i == n_planets - 1) ax[i].tick_params( axis="both", which="major", colors="black", labelcolor="black", direction="in", width=1, length=5, labelsize=12, top=True, bottom=True, left=True, right=True, labelbottom=labelbottom, ) ax[i].tick_params( axis="both", which="minor", colors="black", labelcolor="black", direction="in", width=1, length=3, labelsize=12, top=True, bottom=True, left=True, right=True, labelbottom=labelbottom, ) if xscale == "linear": ax[i].xaxis.set_minor_locator(AutoMinorLocator(5)) if i == n_planets - 1: ax[i].set_xlabel("Age (Myr)", fontsize=13) if cooling_param == "luminosity": ax[i].set_ylabel("$\\log(L/L_\\odot)$", fontsize=13) elif cooling_param == "radius": ax[i].set_ylabel("Radius ($R_\\mathrm{J}$)", fontsize=13) if xlim is not None: ax[i].set_xlim(xlim[0], xlim[1]) if ylim is not None: ax[i].set_ylim(ylim[0], ylim[1]) if offset is not None: ax[i].get_xaxis().set_label_coords(0.5, offset[0]) ax[i].get_yaxis().set_label_coords(offset[1], 0.5) ran_indices = np.random.randint(low=0, high=samples.shape[0], size=n_samples) for sample_idx in ran_indices: for planet_idx in range(n_planets): mass = samples[sample_idx, 1 + planet_idx] cool_box = read_iso.get_cooling_curve(mass=mass, ages=None) if cooling_param == "luminosity": cool_curves[planet_idx].append(cool_box.log_lum) elif cooling_param == "radius": cool_curves[planet_idx].append(cool_box.radius) ax[planet_idx].plot( cool_box.age, cool_curves[planet_idx][-1], lw=0.5, color="gray", alpha=0.5, ) for i in range(n_planets): if cooling_param == "luminosity": ax[i].errorbar( object_age[0], object_lbol[i][0], xerr=object_age[1], yerr=object_lbol[i][1], color="tab:orange", ) elif cooling_param == "radius" and isinstance(object_radius[i], np.ndarray): # Only plot the data if these were provided as optional # argument of object_radius when using FitEvolution ax[i].errorbar( object_age[0], object_radius[i][0], xerr=object_age[1], yerr=object_radius[i][1], color="tab:orange", ) if title is not None: ax[0].set_title(title, fontsize=18.0) if output is None: plt.show() else: plt.savefig(output, bbox_inches="tight") print(" [DONE]") return fig, cool_curves, ran_indices
[docs] @typechecked def plot_isochrones( tag: str, n_samples: int = 50, xlim: Optional[Tuple[float, float]] = None, ylim: Optional[Tuple[float, float]] = None, xscale: Optional[str] = "linear", yscale: Optional[str] = "linear", title: Optional[str] = None, offset: Optional[Tuple[float, float]] = None, figsize: Optional[Tuple[float, float]] = (4.0, 2.5), output: Optional[str] = None, ) -> Tuple[mpl.figure.Figure, List[List[np.ndarray]], np.ndarray]: """ Function for plotting samples of isochrones that are randomly drawn from the posterior distributions of the age and mass parameters that have been estimated with :class:`~species.fit.fit_evolution.FitEvolution`. For each isochrone, the parameters from a single posterior sample are used. Parameters ---------- tag : str Database tag where the samples are stored n_samples : int Number of randomly drawn cooling curves that will be plotted. xlim : tuple(float, float), None Limits of the wavelength axis. Automatic limits are used if the argument is set to ``None``. ylim : tuple(float, float), None Limits of the flux axis. Automatic limits are used if the argument is set to ``None``. xscale : str, None Scale of the x axis ('linear' or 'log'). The scale is set to ``'linear'`` if the argument is set to ``None``. yscale : str, None Scale of the x axis ('linear' or 'log'). The scale is set to ``'linear'`` if the argument is set to ``None``. title : str Title to show at the top of the plot. offset : tuple(float, float) Offset for the label of the x- and y-axis. figsize : tuple(float, float) Figure size. output : str, None Output filename for the plot. The plot is shown in an interface window if the argument is set to ``None``. Returns ------- matplotlib.figure.Figure The ``Figure`` object that can be used for further customization of the plot. np.ndarray Array with the isochrones. The array contains :math:`L/L_\\odot` as function of time for each companions and sample, so the shape is (n_companions, n_samples, n_masses). np.ndarray Array with the random indices that have been sampled from the posterior distribution. """ plt.close() from species.data.database import Database species_db = Database() samples_box = species_db.get_samples(tag) samples = samples_box.samples attr = samples_box.attributes n_planets = attr["n_planets"] evolution_model = attr["evolution_model"] object_lbol = attr["object_lbol"] object_mass = attr["object_mass"] read_iso = ReadIsochrone(evolution_model) plt.rcParams["font.family"] = "serif" plt.rcParams["mathtext.fontset"] = "dejavuserif" if output is None: print("Plotting isochrones...", end="", flush=True) else: print( f"Plotting isochrones: {output}...", end="", flush=True, ) fig = plt.figure(1, figsize=figsize) gridsp = mpl.gridspec.GridSpec(n_planets, 1) gridsp.update(wspace=0, hspace=0.1, left=0, right=1, bottom=0, top=1) ax = [] for i in range(n_planets): ax.append(plt.subplot(gridsp[i, 0])) if xscale is None: xscale = "linear" if yscale is None: yscale = "linear" isochrones = [] for i in range(n_planets): isochrones.append([]) for i in range(n_planets): labelbottom = bool(i == n_planets - 1) ax[i].tick_params( axis="both", which="major", colors="black", labelcolor="black", direction="in", width=1, length=5, labelsize=12, top=True, bottom=True, left=True, right=True, labelbottom=labelbottom, ) ax[i].tick_params( axis="both", which="minor", colors="black", labelcolor="black", direction="in", width=1, length=3, labelsize=12, top=True, bottom=True, left=True, right=True, labelbottom=labelbottom, ) if xscale == "linear": ax[i].xaxis.set_minor_locator(AutoMinorLocator(5)) if i == n_planets - 1: ax[i].set_xlabel("Mass ($M_\\mathrm{J}$)", fontsize=13) ax[i].set_ylabel("$\\log(L/L_\\odot)$", fontsize=13) ax[i].set_xscale(xscale) ax[i].set_yscale(yscale) if xlim is not None: ax[i].set_xlim(xlim[0], xlim[1]) if ylim is not None: ax[i].set_ylim(ylim[0], ylim[1]) if offset is not None: ax[i].get_xaxis().set_label_coords(0.5, offset[0]) ax[i].get_yaxis().set_label_coords(offset[1], 0.5) ran_indices = np.random.randint(low=0, high=samples.shape[0], size=n_samples) for sample_idx in ran_indices: for planet_idx in range(n_planets): age = samples[sample_idx, 0] iso_box = read_iso.get_isochrone(age=age, masses=None) isochrones[planet_idx].append(iso_box.log_lum) ax[planet_idx].plot( iso_box.mass, isochrones[planet_idx][-1], lw=0.5, color="gray", alpha=0.5, ) for i in range(n_planets): ax[i].errorbar( object_mass[i][0], object_lbol[i][0], xerr=object_mass[i][1], yerr=object_lbol[i][1], color="tab:orange", ) if title is not None: ax[0].set_title(title, fontsize=18.0) if output is None: plt.show() else: plt.savefig(output, bbox_inches="tight") print(" [DONE]") return fig, isochrones, ran_indices