Multi-band light curves

Multi-band light curves#

In the previous tutorial we saw how to instantiate a Star with a spectral map. Here, we demonstrate how to compute light curves from such a map, which could originate from observing a star at different waveband.

We will assume that the intensity of the quiescent photosphere and its spots can be modeled as blackbodies with different temperatures.

import jax.numpy as jnp
import numpy as np
import matplotlib.pyplot as plt


def planck(wavelength, temperature, normalize=False):
    h = 6.62607004e-34  # Planck constant
    c = 299792458  # speed of light
    k = 1.38064852e-23  # Boltzmann constant
    f = (2 * h * c**2 / wavelength**5) / (
        jnp.exp(h * c / (wavelength * k * temperature)) - 1
    )
    return f / jnp.max(f) if normalize else f


wv = np.linspace(0.5e-6, 3e-6, 200)

We will make measurements in separate bands with a wavelength response function defined as a Gaussian

def gaussian(x, mu, sigma):
    return jnp.exp(-0.5 * ((x - mu) / sigma) ** 2)


np.random.seed(1)
bands_mean = np.random.choice(wv, 3)
bands_response = gaussian(wv, bands_mean[:, None], 0.4e-6)

Let’s visualize some spectra together with our wavebands.

plt.subplot(
    111,
    xlabel="wavelength ($\mu$m)",
    ylabel="spectral radiance ($W/m^2/m$)",
    title="Blackbody radiation",
)
plt.plot(wv * 6, planck(wv, 2200, normalize=True), label="2200K")
plt.plot(wv * 6, planck(wv, 3000, normalize=True), label="3000K")
for i, b in enumerate(bands_response):
    plt.fill_between(wv * 6, b, alpha=0.5, label=f"band {i}")
_ = plt.legend()
../../_images/c5621433b71e347eab873700641eb73e1b04fe9e199df4bcb675b0d03d4cc203.png

We then define a spotted surface using a Gaussian Process, as shown in the GP tutorial

import jax
import tinygp
from spotter.kernels import GreatCircleDistance, ActiveLatitude
from spotter import Star, show

star = Star.from_sides(20, period=1.0, u=(0.1, 0.2))

spot_kernel = 0.001 * tinygp.kernels.Matern52(0.1, distance=GreatCircleDistance())
kernel = ActiveLatitude(
    kernel=spot_kernel,
    latitude=0.5,
    sigma=0.1,
    symetric=True,
)

gp = tinygp.GaussianProcess(kernel, star.x, mean=1.0, diag=1e-8)
spot = gp.sample(jax.random.PRNGKey(2))
spot = np.clip(spot, 0.6, 0.97)
spot = spot / np.max(spot)

Here, we decided to clip the surface to artificially create darker smaller size spots. It is useful to plot the pixel values to get a sense of the map distribution

plt.plot(spot.T, c="0.5")
plt.xlabel("pixel index")
_ = plt.ylabel("brightness")
../../_images/78388eb4da992886fdf5ffab6c220619391b4021ed09a8c1d4f8e5daad6b6eb0.png

As we can see there are two regions (corresponding to the active latitudes) where pixels have lower values, corresponding to the spots we just drawn.

From there we can define the spectra of the pixels by using the spot map as a temperature map of the surface. Finally, we multiply the spectra of the pixels by the response function of each waveband

# assuming a quiescent photosphere at 3000K
spectral_map = jax.vmap(lambda temp: planck(wv, temp))(spot * 3000)

measured_maps = (spectral_map[:, :, None] * bands_response.T[None, :, :]).mean(1)
star = star.set(y=measured_maps.T)

Let’s have a look at the resulting map (here in the first band)

plt.subplot(121, title="phase $0\degree$")
show(star[0])
plt.subplot(122, title="phase $180\degree$")
show(star[0], np.pi)
../../_images/0983df8a3b203ac1d7e003332ab43be7fe0441432a9b4a58fc6af7ebaecee352.png

We can now compute the normalized light curves observed at each waveband.

from spotter.light_curves import light_curve

time = np.linspace(0, 2.0, 300)
f = light_curve(star, time)
f /= np.mean(f, 1)[:, None]
for i in range(f.shape[0]):
    plt.plot(time, f[i], label=f"band {i}")
plt.legend()
plt.xlabel("time")
_ = plt.ylabel("normalized flux")
../../_images/15242e338abb810ebff80e1e897e97cc87ed8b1c974f932d3c7a7561a18ae42f.png