Source code for simppler.basis

from abc import ABC, abstractmethod

import numpy as np

from simppler.utils import timeperi_to_timetrans, timetrans_to_timeperi


[docs] class Basis(ABC): """Abstract base class for an orbital basis"""
[docs] @abstractmethod def from_synth(self, p: dict, num_planets: int) -> dict: """ Function converting parameters from the :class:`SynthBasis` basis to the current basis. """ pass
[docs] @abstractmethod def to_synth(self, p: dict, num_planets: int) -> dict: """ Function converting parameters from the current basis to the :class:`SynthBasis` basis. """ pass
[docs] class SynthBasis(Basis): """Synth basis This is the 'synth' basis from radvel, which is passed to the Keplerian solver. All Basis classes must be able to convert to and from this basis with `to_synth()` and `from_synth()` methods. The parameters are: - ``per``: Period - ``tp``: Time of periastron - ``e``: Eccentricity - ``w``: Argument of periastron - ``k``: Semi-amplitude """ name = "synth" pstr = "per tp e w k"
[docs] def to_synth(self, p: dict, num_planets: int): return p
[docs] def from_synth(self, p_synth: dict, num_planets: int): return p_synth
[docs] class DefaultBasis(Basis): r"""Default basis Fitting basis used by default in RV models. The differences with the :class:`SynthBasis` are: - The time of conjunction (or transit for transiting planets) ``tc`` is used instead of the time of periastron ``tp``. This parameter is better defined for circular orbit. - ``e`` and ``w`` are replaced with ``secosw`` and ``sesinw`` (:math:`\sqrt{e}\cos{\omega}`, :math:`\sqrt{e}\sin{\omega}`) """ name = "default" pstr = "per tc secosw sesinw k"
[docs] def to_synth(self, p: dict, num_planets: int): p_synth = {} for i in range(1, num_planets + 1): p_synth[f"per{i}"] = p[f"per{i}"] e = p[f"secosw{i}"] ** 2 + p[f"sesinw{i}"] ** 2 w = np.arctan2(p[f"sesinw{i}"], p[f"secosw{i}"]) p_synth[f"tp{i}"] = timetrans_to_timeperi( p[f"tc{i}"], p_synth[f"per{i}"], e, w ) p_synth[f"e{i}"] = e p_synth[f"w{i}"] = w p_synth[f"k{i}"] = p[f"k{i}"] return p_synth
[docs] def from_synth(self, p_synth: dict, num_planets: int): p = {} for i in range(1, num_planets + 1): p[f"per{i}"] = p_synth[f"per{i}"] p[f"tc{i}"] = timeperi_to_timetrans( p_synth[f"tp{i}"], p_synth[f"per{i}"], p_synth[f"e{i}"], p_synth[f"w{i}"], ) p[f"secosw{i}"] = np.sqrt(p_synth[f"e{i}"]) * np.cos(p_synth[f"w{i}"]) p[f"sesinw{i}"] = np.sqrt(p_synth[f"e{i}"]) * np.sin(p_synth[f"w{i}"]) p[f"k{i}"] = p_synth[f"k{i}"] return p
[docs] class LogKBasis(Basis): """LogK Basis Same as the :class:`DefaultBasis`, but the logarithm of the semi-amplitude is fitted instead of the semi-amplitude. """ name = "logk" pstr = "per tc secosw sesinw logk"
[docs] def to_synth(self, p: dict, num_planets: int): p_synth = {} for i in range(1, num_planets + 1): p_synth[f"per{i}"] = p[f"per{i}"] e = p[f"secosw{i}"] ** 2 + p[f"sesinw{i}"] ** 2 w = np.arctan2(p[f"sesinw{i}"], p[f"secosw{i}"]) p_synth[f"tp{i}"] = timetrans_to_timeperi( p[f"tc{i}"], p_synth[f"per{i}"], e, w ) p_synth[f"e{i}"] = e p_synth[f"w{i}"] = w p_synth[f"k{i}"] = np.exp(p[f"logk{i}"]) return p_synth
[docs] def from_synth(self, p_synth: dict, num_planets: int): p = {} for i in range(1, num_planets + 1): p[f"per{i}"] = p_synth[f"per{i}"] p[f"tc{i}"] = timeperi_to_timetrans( p_synth[f"tp{i}"], p_synth[f"per{i}"], p_synth[f"e{i}"], p_synth[f"w{i}"], ) p[f"secosw{i}"] = np.sqrt(p_synth[f"e{i}"]) * np.cos(p_synth[f"w{i}"]) p[f"sesinw{i}"] = np.sqrt(p_synth[f"e{i}"]) * np.sin(p_synth[f"w{i}"]) p[f"logk{i}"] = np.log(p_synth[f"k{i}"]) return p
[docs] class LogPerBasis(Basis): """LogPer Basis Same as the :class:`DefaultBasis`, but the logarithm of the period is fitted instead of the semi-amplitude. """ name = "logper" pstr = "logper tc secosw sesinw k"
[docs] def to_synth(self, p: dict, num_planets: int): p_synth = {} for i in range(1, num_planets + 1): p_synth[f"per{i}"] = np.exp(p[f"logper{i}"]) e = p[f"secosw{i}"] ** 2 + p[f"sesinw{i}"] ** 2 w = np.arctan2(p[f"sesinw{i}"], p[f"secosw{i}"]) p_synth[f"tp{i}"] = timetrans_to_timeperi( p[f"tc{i}"], p_synth[f"per{i}"], e, w ) p_synth[f"e{i}"] = e p_synth[f"w{i}"] = w p_synth[f"k{i}"] = p[f"k{i}"] return p_synth
[docs] def from_synth(self, p_synth: dict, num_planets: int): p = {} for i in range(1, num_planets + 1): p[f"logper{i}"] = np.log(p_synth[f"per{i}"]) p[f"tc{i}"] = timeperi_to_timetrans( p_synth[f"tp{i}"], p_synth[f"per{i}"], p_synth[f"e{i}"], p_synth[f"w{i}"], ) p[f"secosw{i}"] = np.sqrt(p_synth[f"e{i}"]) * np.cos(p_synth[f"w{i}"]) p[f"sesinw{i}"] = np.sqrt(p_synth[f"e{i}"]) * np.sin(p_synth[f"w{i}"]) p[f"k{i}"] = p_synth[f"k{i}"] return p
[docs] class EccBasis(Basis): """Ecc Basis Same as the :class:`SynthBasis`, but ``e`` and ``w`` are fitted directly instead of ``secosw`` and ``sesinw``. """ name = "ecc" pstr = "per tc e w k"
[docs] def to_synth(self, p: dict, num_planets: int) -> dict: p_synth = {} for i in range(1, num_planets + 1): p_synth[f"per{i}"] = p[f"per{i}"] p_synth[f"tp{i}"] = timetrans_to_timeperi( p[f"tc{i}"], p_synth[f"per{i}"], p[f"e{i}"], p[f"w{i}"], ) p_synth[f"e{i}"] = p[f"e{i}"] p_synth[f"w{i}"] = p[f"w{i}"] p_synth[f"k{i}"] = p[f"k{i}"] return p_synth
[docs] def from_synth(self, p_synth: dict, num_planets: int) -> dict: p = {} for i in range(1, num_planets + 1): p[f"per{i}"] = p_synth[f"per{i}"] p[f"tc{i}"] = timeperi_to_timetrans( p_synth[f"tp{i}"], p_synth[f"per{i}"], p_synth[f"e{i}"], p_synth[f"w{i}"], ) p[f"e{i}"] = p_synth[f"e{i}"] p[f"w{i}"] = p_synth[f"w{i}"] p[f"k{i}"] = p_synth[f"k{i}"] return p
BASIS_DICT = { "synth": SynthBasis, "default": DefaultBasis, "logk": LogKBasis, "logper": LogPerBasis, "ecc": EccBasis, } BASIS_PARAM_DICT = {b.pstr: b for b in BASIS_DICT.values()}