Source code for limb_darkening_laws

# These are implementations heavily based on those in  ExoTIC-LD
# (ads:2024JOSS....9.6816G). Many thanks to the authors for their work!
import jax

jax.config.update("jax_enable_x64", True)
from functools import partial

import jax.numpy as jnp

from squishyplanet import OblateSystem

mu_grid = jnp.linspace(0, 1, 500)


[docs] @partial(jax.jit, static_argnames=("return_profile")) def linear_ld_law(u1, return_profile=False): """Linear limb-darkening law. .. math:: \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) Args: u1 (float): Linear limb-darkening coefficient return_profile (bool, default=False): Whether to return a dictionary describing the intensity profile Returns: Array: u coefficients used by squishyplanet to compute the intensity profile (here it's just [u1, 0], since we always need at least two coefficients) dict (if return_profile=True): Dictionary describing the intensity profile """ u_coeffs = jnp.array([u1, 0]) if return_profile: # squishyplanet normalizes intensity profiles so that their integrated flux is 1 # but when plotting we need to renormalize to where mu=0 norm = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=jnp.array([1.0]) ) poly_model = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=mu_grid ) return { "mu_grid": mu_grid, "true_intensity_profile": 1 - u1 * (1 - mu_grid), "squishyplanet_intensity_profile": poly_model / norm, "u_coeffs": u_coeffs, } return u_coeffs
[docs] @partial(jax.jit, static_argnames=("return_profile")) def quadratic_ld_law(u1, u2, return_profile=False): """Quadratic limb-darkening law. .. math:: \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\mu)^2 Args: u1 (float): Linear limb-darkening coefficient u2 (float): Quadratic limb-darkening coefficient return_profile (bool, default=False): Whether to return a dictionary describing the intensity profile Returns: Array: u coefficients used by squishyplanet to compute the intensity profile (here it's just [u1, u2], without modification: this is a silly function included only to give a consistent interface to the limb-darkening laws) dict (if return_profile=True): Dictionary describing the intensity profile """ u_coeffs = jnp.array([u1, u2]) if return_profile: # squishyplanet normalizes intensity profiles so that their integrated flux is 1 # but when plotting we need to renormalize to where mu=0 norm = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=jnp.array([1.0]) ) poly_model = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=mu_grid ) return { "mu_grid": mu_grid, "true_intensity_profile": 1 - u1 * (1 - mu_grid) - u2 * (1 - mu_grid) ** 2, "squishyplanet_intensity_profile": poly_model / norm, "u_coeffs": u_coeffs, } return u_coeffs
[docs] @partial(jax.jit, static_argnames=("return_profile")) def kipping_ld_law(q1, q2, return_profile=False): """Kipping limb-darkening law. A restriction of the quadratic law from Kipping 2013 that guaratees a monotonic increasing intensity profile .. math:: \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\mu)^2 where .. math:: u_1 = 2 \\sqrt{q_1} q_2 .. math:: u_2 = \\sqrt{q_1} (1 - 2 q_2) Args: q1 (float): Kipping limb-darkening coefficient q2 (float): Kipping limb-darkening coefficient return_profile (bool, default=False): Whether to return a dictionary describing the intensity profile Returns: Array or dict: If `return_profile` is False, returns an array of `u` coefficients used by squishyplanet to compute the intensity profile. If `return_profile` is True, returns a dictionary describing the intensity profile. """ u1 = 2.0 * q1**0.5 * q2 u2 = q1**0.5 * (1 - 2.0 * q2) u_coeffs = jnp.array([u1, u2]) if return_profile: # squishyplanet normalizes intensity profiles so that their integrated flux is 1 # but when plotting we need to renormalize to where mu=0 norm = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=jnp.array([1.0]) ) poly_model = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=mu_grid ) return { "mu_grid": mu_grid, "true_intensity_profile": 1 - u1 * (1 - mu_grid) - u2 * (1 - mu_grid) ** 2, "squishyplanet_intensity_profile": poly_model / norm, "u_coeffs": u_coeffs, } return u_coeffs
[docs] @partial(jax.jit, static_argnames=("return_profile")) def squareroot_ld_law(u1, u2, order=12, return_profile=False): """Square root limb-darkening law. .. math:: \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\sqrt{\\mu}) Args: u1 (float): Linear limb-darkening coefficient u2 (float): Square root limb-darkening coefficient order (int, default=5): Order of the polynomial fit to the intensity profile return_profile (bool, default=False): Whether to return a dictionary describing the intensity profile Returns: Array: u coefficients of the least-squares polynomial fit to the intensity profile created by the limb-darkening law across a dense grid of mu values dict (if return_profile=True): Dictionary describing the intensity profile """ intensity_profile = 1 - u1 * (1 - mu_grid) - u2 * (1 - mu_grid**0.5) u_coeffs = OblateSystem.fit_limb_darkening_profile( intensities=intensity_profile, mus=mu_grid, order=order ) if return_profile: # squishyplanet normalizes intensity profiles so that their integrated flux is 1 # but when plotting we need to renormalize to where mu=0 norm = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=jnp.array([1.0]) ) poly_model = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=mu_grid ) return { "mu_grid": mu_grid, "true_intensity_profile": intensity_profile, "squishyplanet_intensity_profile": poly_model / norm, "u_coeffs": u_coeffs, } return u_coeffs
[docs] @partial(jax.jit, static_argnames=("return_profile")) def nonlinear_3param_ld_law(u1, u2, u3, order=12, return_profile=False): """Non-linear 3-parameter limb-darkening law. .. math:: \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu) - u_2 (1 - \\mu^{1.5}) - u_3 (1 - \\mu^2) Args: u1 (float): Linear limb-darkening coefficient u2 (float): Square root limb-darkening coefficient u3 (float): Square limb-darkening coefficient order (int, default=5): Order of the polynomial fit to the intensity profile return_profile (bool, default=False): Whether to return a dictionary describing the intensity profile Returns: Array: u coefficients of the least-squares polynomial fit to the intensity profile created by the limb-darkening law across a dense grid of mu values dict (if return_profile=True): Dictionary describing the intensity profile """ intensity_profile = ( 1 - u1 * (1 - mu_grid) - u2 * (1 - mu_grid**1.5) - u3 * (1 - mu_grid**2) ) u_coeffs = OblateSystem.fit_limb_darkening_profile( intensities=intensity_profile, mus=mu_grid, order=order ) if return_profile: # squishyplanet normalizes intensity profiles so that their integrated flux is 1 # but when plotting we need to renormalize to where mu=0 norm = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=jnp.array([1.0]) ) poly_model = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=mu_grid ) return { "mu_grid": mu_grid, "true_intensity_profile": intensity_profile, "squishyplanet_intensity_profile": poly_model / norm, "u_coeffs": u_coeffs, } return u_coeffs
[docs] @partial(jax.jit, static_argnames=(["order", "return_profile"])) def nonlinear_4param_ld_law(u1, u2, u3, u4, order=12, return_profile=False): """Non-linear 4-parameter limb-darkening law. .. math:: \\frac{I(\\mu)}{I(\\mu = 1)} = 1 - u_1 (1 - \\mu^{0.5}) - u_2 (1 - \\mu) - u_2 (1 - \\mu^{1.5}) - u_3 (1 - \\mu^2) Args: u1 (float): Linear limb-darkening coefficient u2 (float): Square root limb-darkening coefficient u3 (float): Square limb-darkening coefficient u4 (float): Square limb-darkening coefficient order (int, default=5): Order of the polynomial fit to the intensity profile return_profile (bool, default=False): Whether to return a dictionary describing the intensity profile Returns: Array: u coefficients of the least-squares polynomial fit to the intensity profile created by the limb-darkening law across a dense grid of mu values dict (if return_profile=True): Dictionary describing the intensity profile """ intensity_profile = ( 1 - u1 * (1 - mu_grid**0.5) - u2 * (1 - mu_grid) - u3 * (1 - mu_grid**1.5) - u4 * (1 - mu_grid**2) ) u_coeffs = OblateSystem.fit_limb_darkening_profile( intensities=intensity_profile, mus=mu_grid, order=order ) if return_profile: # squishyplanet normalizes intensity profiles so that their integrated flux is 1 # but when plotting we need to renormalize to where mu=0 norm = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=jnp.array([1.0]) ) poly_model = OblateSystem.limb_darkening_profile( ld_u_coeffs=u_coeffs, mu=mu_grid ) return { "mu_grid": mu_grid, "true_intensity_profile": intensity_profile, "squishyplanet_intensity_profile": poly_model / norm, "u_coeffs": u_coeffs, } return u_coeffs