From bf25afa2bf362272a5c216368cdc652bd07bad8b Mon Sep 17 00:00:00 2001 From: Lionel Garcia Date: Thu, 8 Aug 2024 15:19:41 -0400 Subject: [PATCH] docs: reorganise the doc + API docs + logo (#191) * docs: adding a clean API page (keep autoapi?) + open toctree * docs: more docstrings * docs: revamped tutorial to include more measurements (RV ,light curve, starry todo) * docs: removed unused * docs: fix main docstrings * docs: adding a logo * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: line too long * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: adding full api link * docs: fix objects refs * docs: temporary remove fail on warning * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: make autoapi index orphan (to avoid warnings in sphinx) + fix rst list format (sphinx warning) * docs: separate starry api * docs: remove why.md * fix: map_light_curve dosctring * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: reset `sphinx -W` option to fail on warning * docs: add about page to tutorials + modify transit tuto to reflect that * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: api ref * docs: add link to about * docs: put model_light_curve into function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: remove duplicate note about extra packages * fix: add @jiayindong suggestions and `light_curves.transforms` to API --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/_autoapi_templates/index.rst | 15 ++ docs/_static/favicon.png | Bin 1845 -> 2092 bytes docs/_static/logo.png | Bin 18175 -> 58831 bytes docs/api.md | 55 ++++++ docs/conf.py | 7 +- docs/guide.md | 15 -- docs/index.md | 28 ++- docs/install.md | 2 +- docs/tutorials.md | 28 --- docs/tutorials/about.ipynb | 79 ++++++++ docs/tutorials/getting-started.ipynb | 185 ++++++++++++++---- docs/tutorials/rv.ipynb | 37 ++-- docs/tutorials/transit.ipynb | 117 ++++++----- .../experimental/starry/light_curves.py | 2 +- src/jaxoplanet/experimental/starry/pijk.py | 25 ++- src/jaxoplanet/experimental/starry/surface.py | 29 ++- src/jaxoplanet/experimental/starry/ylm.py | 71 ++++++- src/jaxoplanet/light_curves/transforms.py | 3 + src/jaxoplanet/orbits/keplerian.py | 12 ++ 19 files changed, 502 insertions(+), 208 deletions(-) create mode 100644 docs/_autoapi_templates/index.rst create mode 100644 docs/api.md delete mode 100644 docs/guide.md delete mode 100644 docs/tutorials.md create mode 100644 docs/tutorials/about.ipynb diff --git a/docs/_autoapi_templates/index.rst b/docs/_autoapi_templates/index.rst new file mode 100644 index 00000000..e48a192b --- /dev/null +++ b/docs/_autoapi_templates/index.rst @@ -0,0 +1,15 @@ +:orphan: + +Full API Reference +================== + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + {% for page in pages|selectattr("is_top_level_object") %} + {{ page.include_path }} + {% endfor %} + +.. [#f1] Created with `sphinx-autoapi `_ diff --git a/docs/_static/favicon.png b/docs/_static/favicon.png index 787d536f7fd209d06220d1e6aa49157bbbc203b1..f77812b65a02f690724a8767f0f1fcfd4f5a0c9c 100644 GIT binary patch literal 2092 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 HN@xNAJds(7 diff --git a/docs/_static/logo.png b/docs/_static/logo.png index 89c4945224e5b1af14bc63d851837cbd531785bc..9f0aa21a6c103a721c1f6a9cf9de56acc7efaebe 100644 GIT binary patch literal 58831 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

1XWNswTeq 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

fS=>Q;80|=kGL+(nWhi9~fYp1!KQskJNJzBhH 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 diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000..b523409e --- /dev/null +++ b/docs/api.md @@ -0,0 +1,55 @@ +# API reference + +These pages contain the API reference for the `jaxoplanet` package. + +## Orbital systems + +The modules to define orbital systems are: + +- [keplerian](jaxoplanet.orbits.keplerian) : a module to define a Keplerian system made of a central object and its orbiting bodies. +- [transit](jaxoplanet.orbits.transit) : a module to define a system made of a star and its transiting planet, defined by the transit parameters assuming the planet transits at a constant velocity. + +## Limb-darkened stars + +Given an orbital system, the following modules can be used to compute different kind of light curves: + +- [limb-darkened](jaxoplanet.light_curves.limb_dark) : a module to compute occultation light curve of a star with polynomial limb darkening. +- [transforms](jaxoplanet.light_curves.transforms) : a module providing decorators to transform light curve functions. + + +## Non-uniform surfaces + + +```{warning} +While being stable, computing *starry* light curves of non-uniform surfaces is still experimental. +``` + +The following modules can be used to create systems of bodies with limb-darkened and non-uniform emitting surfaces: + +- [starry orbit](jaxoplanet.experimental.starry.orbit) : a module to define a system made of a central object and orbiting bodies, all with non-uniform emitting surfaces. (experimental) +- [starry light curve](jaxoplanet.experimental.starry.light_curves) : a module to compute the light curve of a non-uniform star whose surface is represented by a sum of spherical harmonics. + +And the following are lower-level modules to define and manipulate non-uniform surfaces: + +- [Ylm](jaxoplanet.experimental.starry.ylm) : a lower-level module to create and manipulate vectors in the spherical harmonic basis. +- [Pijk](jaxoplanet.experimental.starry.pijk) : a lower-level module to create and manipulate vectors in the polynomial basis. +- [Surface](jaxoplanet.experimental.starry.surface) : a module to manipulate the oriented surface of spherical bodies, represented by a sum of spherical harmonics. +- [visualization](jaxoplanet.experimental.starry.visualization) : a module to visualize the surface of non-uniform spherical bodies. + + +```{toctree} +:hidden: + +keplerian orbit +transit orbit +starry orbit +limb-darkened light curve +transforms +starry light curve +Surface +Ylm +Pijk +visualization +``` + +**Missing something?** Check the [full API reference](autoapi/jaxoplanet/index). diff --git a/docs/conf.py b/docs/conf.py index ee54b318..55abee52 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,7 @@ ] autoapi_dirs = ["../src"] -autoapi_ignore = ["*/experimental/*", "*_version*", "*/types*"] +autoapi_ignore = ["*_version*", "*/types*"] autoapi_options = [ "members", "undoc-members", @@ -29,6 +29,9 @@ "special-members", # "imported-members", ] +# autoapi_add_toctree_entry = False +autoapi_template_dir = "_autoapi_templates" + suppress_warnings = ["autoapi.python_import_resolution"] myst_enable_extensions = ["dollarmath", "colon_fence"] @@ -44,7 +47,7 @@ version = jaxoplanet.__version__ release = jaxoplanet.__version__ -exclude_patterns = ["_build"] +exclude_patterns = ["_build", "_autoapi_templates"] html_theme = "sphinx_book_theme" html_title = "jaxoplanet documentation" html_logo = "_static/logo.png" diff --git a/docs/guide.md b/docs/guide.md deleted file mode 100644 index 6e35cf20..00000000 --- a/docs/guide.md +++ /dev/null @@ -1,15 +0,0 @@ -(guide)= - -# User Guide - -The following pages give some background on the context within which `jaxoplanet` -exists, as well as detailed installation and API documentation. Click through -for all the details, or head over to the {ref}`tutorials` for a more hands-on -experience. - -```{toctree} -:maxdepth: 1 - -install -troubleshooting -``` diff --git a/docs/index.md b/docs/index.md index 85d139e2..67da25cd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,11 +4,29 @@ ## Table of contents ```{toctree} ---- -maxdepth: 1 ---- +:caption: User guide -guide -tutorials +install +troubleshooting +tutorials/getting-started.ipynb +``` + +```{toctree} +:caption: Tutorials + +tutorials/about.ipynb +tutorials/transit.ipynb +tutorials/rv.ipynb +tutorials/starry.ipynb +``` + +```{toctree} +:caption: Reference + +api +tutorials/autodiff.ipynb +tutorials/introduction-to-jax.ipynb +tutorials/core-from-scratch.ipynb contributing + ``` diff --git a/docs/install.md b/docs/install.md index 47634b1d..f51fccd1 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,6 +1,6 @@ (install)= -# Installation Guide +# Installation `jaxoplanet` is built on top of [`jax`](https://github.com/google/jax) so that's the primary dependency that you'll need. All of the methods below will install any diff --git a/docs/tutorials.md b/docs/tutorials.md deleted file mode 100644 index b3be97da..00000000 --- a/docs/tutorials.md +++ /dev/null @@ -1,28 +0,0 @@ -(tutorials)= - -# Tutorials - -The tutorials in this section are automatically executed with every change of -the code to make sure that they are always up-to-date. As a result, they are -designed to require only a relatively small amount of computation time; when -using `jaxoplanet` for real problems you will probably find that your run -times are longer. - -To execute a tutorial on your own, you can click on the buttons at the top right -or this page to launch the notebook using [Binder](https://mybinder.org), -[Colab](https://colab.research.google.com), or download the `.ipynb` file -directly. - -## Introductory Topics - -```{toctree} -:maxdepth: 1 - -tutorials/getting-started.ipynb -tutorials/autodiff.ipynb -tutorials/introduction-to-jax.ipynb -tutorials/transit.ipynb -tutorials/rv.ipynb -tutorials/starry.ipynb -tutorials/core-from-scratch.ipynb -``` diff --git a/docs/tutorials/about.ipynb b/docs/tutorials/about.ipynb new file mode 100644 index 00000000..eba2e11c --- /dev/null +++ b/docs/tutorials/about.ipynb @@ -0,0 +1,79 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# About these tutorials\n", + "\n", + "The tutorials in this section are automatically executed with every change of\n", + "the code to make sure that they are always up-to-date. As a result, they are\n", + "designed to require only a relatively small amount of computation time; when\n", + "using `jaxoplanet` for real problems you will probably find that your run\n", + "times are longer.\n", + "\n", + "To execute a tutorial on your own, you can click on the buttons at the top right\n", + "or this page to launch the notebook using [Binder](https://mybinder.org),\n", + "[Colab](https://colab.research.google.com), or download the `.ipynb` file\n", + "directly.\n", + "\n", + "## Extra dependencies\n", + "\n", + "Most of these tutorials use the probabilistic programming library `NumPyro` to make \n", + "inference based on real or synthetic datasets. Hence, you will need the following extra packages:\n", + "- [NumPyro](https://num.pyro.ai/en/stable/getting_started.html) : a lightweight probabilistic programming library.\n", + "- [NumPyro-ext](https://github.com/dfm/numpyro-ext) : a package that extends NumPyro with a set of helper functions, custom distributions and other utilities.\n", + "- [corner](https://corner.readthedocs.io/en/latest/) : a package to visualize multidimensional samples using a scatterplot matrix.\n", + "- [Arviz](https://python.arviz.org/en/stable/) : a package for exploratory analysis of Bayesian models\n", + "\n", + "In addition, the [Lightkurve](https://docs.lightkurve.org/) may be required to download real datasets from the Kepler and TESS missions.\n", + "\n", + "For reference, here is the version of the packages used to generate these tutorials:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import jax\n", + "import arviz\n", + "import numpy\n", + "import corner\n", + "import numpyro\n", + "import jaxoplanet\n", + "import numpyro_ext\n", + "\n", + "print(f\"jaxoplanet.__version__ = {jaxoplanet.__version__}\")\n", + "print(f\"numpy.__version__ = {numpy.__version__}\")\n", + "print(f\"numpyro.__version__ = {numpyro.__version__}\")\n", + "print(f\"numpyro_ext.__version__ = {numpyro_ext.__version__}\")\n", + "print(f\"jax.__version__ = {jax.__version__}\")\n", + "print(f\"corner.__version__ = {corner.__version__}\")\n", + "print(f\"arviz.__version__ = {arviz.__version__}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "jaxoplanet", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/getting-started.ipynb b/docs/tutorials/getting-started.ipynb index 3a519c67..2904c070 100644 --- a/docs/tutorials/getting-started.ipynb +++ b/docs/tutorials/getting-started.ipynb @@ -11,128 +11,182 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "1", + "metadata": { + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "import jax\n", + "\n", + "jax.config.update(\"jax_enable_x64\", True)" + ] + }, + { + "cell_type": "markdown", + "id": "2", "metadata": {}, "source": [ - "To set up a Keplerian orbital system in `jaxoplanet` we can define initialize a `Central` object (e.g. a star) and an orbiting `Body` object (e.g. a planet)." + "## Keplerian system\n", + "\n", + "In jaxoplanet, a Keplerian system can be instantiated with a [Central](jaxoplanet.orbits.keplerian.Central) object" ] }, { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ - "from jax import config\n", + "from jaxoplanet.orbits.keplerian import System, Central\n", "\n", - "config.update(\"jax_enable_x64\", True)\n", - "\n", - "from jaxoplanet.orbits.keplerian import Central, Body, System\n", - "from jaxoplanet.units import unit_registry as ureg\n", - "from jaxoplanet import units\n", - "import jax.numpy as jnp" + "system = System(Central()) # a central object with some default parameters" ] }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ - "We can initialize the `Central` object using two of radius, mass and/or density, otherwise `jaxoplanet` will populate these parameters with the default values and units of a Solar analogue star:" + "and add an orbiting [Body](jaxoplanet.orbits.keplerian.Body) " ] }, { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ - "Central()" + "system = system.add_body(period=0.1)" ] }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ - "We can instead choose to create a `Central` object using orbital parameters, for example for the Sun-Earth system:" + "As many arguments are optional, it's always a good idea to check the parameters of the system." ] }, { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ - "Sun = Central.from_orbital_properties(\n", - " period=1.0 * ureg.yr,\n", - " semimajor=1.0 * ureg.au,\n", - " radius=1.0 * ureg.R_sun,\n", - " body_mass=1.0 * ureg.M_earth,\n", - ")\n", - "Sun" + "system" ] }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ - "To create a Keplerian `Body` we must define either the orbital period or semi-major axis. There are also a number of optional orbital parameters we can set at this point:" + "For the reminder of this notebook, let's define a system consisting of an Earth-like planet orbiting a Sun-like star." ] }, { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ - "Earth = System(Sun).add_body(semimajor=1 * ureg.au).bodies[0]\n", - "Earth" + "from jaxoplanet.units import unit_registry as ureg\n", + "\n", + "sun = Central(\n", + " radius=1.0 * ureg.R_sun,\n", + " mass=1.0 * ureg.M_sun,\n", + ")\n", + "\n", + "system = System(sun).add_body(\n", + " semimajor=1.0 * ureg.au,\n", + " radius=1.0 * ureg.R_earth,\n", + " mass=1.0 * ureg.M_earth,\n", + ")\n", + "\n", + "earth = system.bodies[0]\n", + "\n", + "# checking the parameters of the system\n", + "system" ] }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ - "Note: The `eccentricity` by default is None (=circular orbit). This is not (entirely) equivalent to setting `eccentricity`=0. If we set the `eccentricity`=0 then we will have to explicitly define the argument of periastron (`omega_peri`) too!" + "```{note}\n", + "\n", + "Notice the use of the [jaxoplanet.units](jaxoplanet.units) module to handle physical units. Check *TODO* for an introduction to the unit system used by jaxoplanet.\n", + "\n", + "```" ] }, { "cell_type": "markdown", - "id": "10", + "id": "11", + "metadata": {}, + "source": [ + "# Radial velocity" + ] + }, + { + "cell_type": "markdown", + "id": "12", "metadata": {}, "source": [ - "Users familiar with `KeplarianOrbit`s within `exoplanet` (see [tutorial](https://docs.exoplanet.codes/en/latest/tutorials/data-and-models/)) can access the relative positions and velocities of the bodies in a similar way:" + "Then, one can access the relative position and velocity of the planet relative to the sun. " ] }, { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "13", "metadata": {}, "outputs": [], "source": [ + "import jax.numpy as jnp\n", "from matplotlib import pyplot as plt\n", "\n", "# Get the position of the planet and velocity of the star as a function of time\n", "t = jnp.linspace(0, 730, 5000)\n", - "x, y, z = Earth.relative_position(t)\n", - "vx, vy, vz = Earth.central_velocity(t)\n", + "x, y, z = earth.relative_position(t)\n", + "vx, vy, vz = earth.central_velocity(t)" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "```{note}\n", + "Axes and orbital parameters conventions follow that of the [*exoplanet* package](https://docs.exoplanet.codes/en/latest/tutorials/data-and-models/).\n", + "```\n", "\n", - "# Plot the coordinates\n", - "fig, axes = plt.subplots(2, 1, figsize=(8, 8), sharex=True)\n", + "And plot the results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axes = plt.subplots(2, 1, sharex=True)\n", "ax = axes[0]\n", "ax.plot(t, x.magnitude, label=\"x\")\n", "ax.plot(t, y.magnitude, label=\"y\")\n", "ax.plot(t, z.magnitude, label=\"z\")\n", - "ax.set_ylabel(\"position of orbiting body [$R_*$]\")\n", + "ax.set_ylabel(\"earth position [$R_*$]\")\n", "ax.legend(fontsize=10, loc=1)\n", "\n", "ax = axes[1]\n", @@ -141,14 +195,61 @@ "ax.plot(t, vz.magnitude, label=\"$v_z$\")\n", "ax.set_xlim(t.min(), t.max())\n", "ax.set_xlabel(\"time [days]\")\n", - "ax.set_ylabel(\"velocity of central [$R_*$/day]\")\n", + "ax.set_ylabel(\"central velocity [$R_*$/day]\")\n", "_ = ax.legend(fontsize=10, loc=1)" ] }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Light curve\n", + "\n", + "jaxoplanet contains module to compute occultation light curves of stars given different photosphere properties. For example, we can define a limb-darkened [light_curve](jaxoplanet.light_curves.limb_dark.light_curve) to compute the flux of a star with a polynomial limb darkening, allowing to express linear, quadratic and more complex laws.\n", + "\n", + "Using the limb-darkening coefficients from [Hestroffer and Magnan](https://www.physics.hmc.edu/faculty/esin/a101/limbdarkening.pdf) we compute the flux" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "from jaxoplanet.light_curves.limb_dark import light_curve\n", + "\n", + "u = (0.30505, 1.13123, -0.78604, 0.40560, 0.02297, -0.07880)\n", + "time = jnp.linspace(-0.5, 0.5, 1000)\n", + "\n", + "flux = 1.0 + light_curve(system, u)(time)" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "and plot the resulting light curve" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(time, flux)\n", + "plt.xlabel(\"time (days)\")\n", + "_ = plt.ylabel(\"relative flux\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", "metadata": {}, "outputs": [], "source": [] @@ -170,7 +271,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.10" + "version": "3.10.14" } }, "nbformat": 4, diff --git a/docs/tutorials/rv.ipynb b/docs/tutorials/rv.ipynb index 56298454..e82e9b16 100644 --- a/docs/tutorials/rv.ipynb +++ b/docs/tutorials/rv.ipynb @@ -6,17 +6,24 @@ "source": [ "(rv)=\n", "\n", - "# Radial Velocities Fitting" + "# Radial Velocities Fitting\n", + "\n", + "\n", + "In this tutorial we will learn how to use `jaxoplanet` to compute the radial velocities of a star hosting a single exoplanet, and how to fit this dataset using `numpyro`.\n", + "\n", + "```{note}\n", + "This tutorial requires some [extra packages](about.ipynb) that are not included in the `jaxoplanet` dependencies.\n", + "```\n", + "\n", + "## Setup\n", + "\n", + "We first setup the number of CPUs to use and enable the use of double-precision numbers with jax." ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "tags": [ - "hide-input" - ] - }, + "metadata": {}, "outputs": [], "source": [ "import jax\n", @@ -26,24 +33,6 @@ "jax.config.update(\"jax_enable_x64\", True)" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this tutorial we will learn how to use `jaxoplanet` to compute the radial velocities of a star hosting a single exoplanet, and how to fit this dataset using `numpyro`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{note}\n", - "This tutorial requires the installation of the following packages:\n", - "- [`numpyro`](https://num.pyro.ai)\n", - "- [`corner`](https://corner.readthedocs.io)\n", - "```" - ] - }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/tutorials/transit.ipynb b/docs/tutorials/transit.ipynb index 853738f6..09e559d1 100644 --- a/docs/tutorials/transit.ipynb +++ b/docs/tutorials/transit.ipynb @@ -11,13 +11,13 @@ "\n", "Like `exoplanet`, `jaxoplanet` includes methods for computing the light curves of transiting exoplanets. In this tutorial, we introduce these methods and use it alongside the `NumPyro` probabilistic programming library to do some transit fitting. Parts of this tutorial will follow the [Transit Fitting tutorial](https://gallery.exoplanet.codes/tutorials/transit/) for the `exoplanet` package.\n", "\n", - "In addition to `jaxoplanet` (and [`NumPy`](https://numpy.org/), [`Matplotlib`](https://matplotlib.org/stable/)), you'll need to install the following packages to run this tutorial:\n", - "- [`NumPyro`](https://num.pyro.ai/en/stable/getting_started.html)\n", - "- [`NumPyro-ext`](https://github.com/dfm/numpyro-ext)\n", - "- [`corner`](https://corner.readthedocs.io/en/latest/)\n", - "- [`Arviz`](https://python.arviz.org/en/stable/)\n", + "```{note}\n", + "This tutorial requires some [extra packages](about.ipynb) that are not included in the `jaxoplanet` dependencies.\n", + "```\n", "\n", - "Let's import the necessary packages and configure the setup." + "## Setup\n", + "\n", + "We first setup the number of CPUs to use and enable the use of double-precision numbers with jax. We also import the required packages." ] }, { @@ -45,17 +45,7 @@ "numpyro.set_platform(\"cpu\") # For CPU (use \"gpu\" for GPU)\n", "jax.config.update(\n", " \"jax_enable_x64\", True\n", - ") # For 64-bit precision since JAX defaults to 32-bit\n", - "\n", - "\n", - "print(f\"jaxoplanet.__version__ = {jaxoplanet.__version__}\")\n", - "print(f\"numpy.__version__ = {np.__version__}\")\n", - "print(f\"matplotlib.__version__ = {plt.matplotlib.__version__}\")\n", - "print(f\"numpyro.__version__ = {numpyro.__version__}\")\n", - "print(f\"numpyro_ext.__version__ = {numpyro_ext.__version__}\")\n", - "print(f\"jax.__version__ = {jax.__version__}\")\n", - "print(f\"corner.__version__ = {corner.__version__}\")\n", - "print(f\"arviz.__version__ = {az.__version__}\")" + ") # For 64-bit precision since JAX defaults to 32-bit" ] }, { @@ -63,7 +53,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's first compute a simple light curve." + "## Generating the data\n", + "\n", + "Let's first compute a simple light curve.\n", + "\n", + "The light curve calculation requires an orbit object. We'll use [TransitOrbit](jaxoplanet.orbits.transit.TransitOrbit) (similar to [SimpleTransitOrbit](https://docs.exoplanet.codes/en/latest/user/api/#exoplanet.orbits.SimpleTransitOrbit) in the exoplanet package), which is an orbit parameterized by the observables of a transiting system: period, speed/duration, time of transit, impact parameter, and radius ratio." ] }, { @@ -72,26 +66,21 @@ "metadata": {}, "outputs": [], "source": [ - "# The light curve calculation requires an orbit object.\n", - "# We'll use TransitOrbit (similar to SimpleTransitOrbit in the exoplanet package),\n", - "# which is an orbit parameterized by the observables of a transiting system:\n", - "# period, speed/duration, time of transit, impact parameter, and radius ratio.\n", "orbit = TransitOrbit(\n", " period=3.456, duration=0.12, time_transit=0.0, impact_param=0.0, radius_ratio=0.1\n", ")\n", "\n", - "\n", "# Compute a limb-darkened light curve for this orbit\n", "t = np.linspace(-0.1, 0.1, 1000)\n", "u = [0.1, 0.06] # Quadratic limb-darkening coefficients\n", "light_curve = limb_dark_light_curve(orbit, u)(t)\n", "\n", "# Plot the light curve\n", - "plt.figure(dpi=150)\n", "plt.plot(t, light_curve, lw=2)\n", "plt.xlabel(\"time [days]\")\n", "plt.ylabel(\"relative flux\")\n", - "plt.xlim(t.min(), t.max());" + "plt.xlim(t.min(), t.max())\n", + "plt.tight_layout()" ] }, { @@ -133,13 +122,12 @@ "y = y_true + yerr * random.normal(size=len(t))\n", "\n", "# Let's see what the light curve looks like\n", - "plt.figure(dpi=150)\n", "plt.plot(t, y_true, \"-k\", lw=1.0, label=\"truth\")\n", "plt.plot(t, y, \".k\", ms=2, label=\"data\")\n", "plt.xlabel(\"time [days]\")\n", "plt.ylabel(\"relative flux\")\n", "plt.xlim(t.min(), t.max())\n", - "plt.legend(loc=4);" + "_ = plt.legend(loc=4)" ] }, { @@ -159,6 +147,17 @@ "metadata": {}, "outputs": [], "source": [ + "def light_curve_model(time, params):\n", + " orbit = TransitOrbit(\n", + " period=params[\"period\"],\n", + " duration=params[\"duration\"],\n", + " time_transit=params[\"t0\"],\n", + " impact_param=params[\"b\"],\n", + " radius_ratio=params[\"r\"],\n", + " )\n", + " return limb_dark_light_curve(orbit, params[\"u\"])(time)\n", + "\n", + "\n", "def model(t, yerr, y=None):\n", " # Priors for the parameters we're fitting for\n", "\n", @@ -187,14 +186,9 @@ " u = numpyro.sample(\"u\", numpyro_ext.distributions.QuadLDParams())\n", "\n", " # The orbit and light curve\n", - " orbit = TransitOrbit(\n", - " period=period,\n", - " duration=duration,\n", - " time_transit=t0,\n", - " impact_param=b,\n", - " radius_ratio=r,\n", + " y_pred = light_curve_model(\n", + " t, {\"period\": period, \"duration\": duration, \"t0\": t0, \"b\": b, \"r\": r, \"u\": u}\n", " )\n", - " y_pred = limb_dark_light_curve(orbit, u)(t)\n", "\n", " # Let's track the light curve\n", " numpyro.deterministic(\"light_curve\", y_pred)\n", @@ -247,7 +241,7 @@ " truths=[T0, PERIOD, DURATION, ROR, B, U[0], U[1]],\n", " show_titles=True,\n", " title_kwargs={\"fontsize\": 10},\n", - " label_kwargs={\"fontsize\": 12},\n", + " label_kwargs={\"fontsize\": 10},\n", ")" ] }, @@ -324,14 +318,14 @@ "metadata": {}, "outputs": [], "source": [ - "plt.figure(dpi=150)\n", "plt.plot(t, y, \".k\", ms=2, label=\"data\")\n", "plt.plot(t, y_true, \"-k\", lw=1.0, label=\"truth\")\n", "plt.plot(t, opt_params[\"light_curve\"], \"--C0\", lw=1.0, label=\"MAP model\")\n", "plt.xlabel(\"time [days]\")\n", "plt.ylabel(\"relative flux\")\n", "plt.legend(fontsize=10, loc=4)\n", - "plt.xlim(t.min(), t.max());" + "plt.xlim(t.min(), t.max())\n", + "plt.tight_layout()" ] }, { @@ -437,11 +431,11 @@ "metadata": {}, "outputs": [], "source": [ - "az.plot_trace(\n", + "_ = az.plot_trace(\n", " inf_data,\n", " var_names=[\"t0\", \"period\", \"duration\", \"r\", \"b\", \"u\"],\n", " backend_kwargs={\"constrained_layout\": True},\n", - ");" + ")" ] }, { @@ -460,16 +454,18 @@ "metadata": {}, "outputs": [], "source": [ - "corner.corner(\n", + "fig = plt.figure(figsize=(12, 12))\n", + "_ = corner.corner(\n", " inf_data,\n", " var_names=[\"t0\", \"period\", \"duration\", \"r\", \"b\", \"u\"],\n", " truths=[T0, PERIOD, DURATION, ROR, B, U[0], U[1]],\n", " show_titles=True,\n", " quantiles=[0.16, 0.5, 0.84],\n", - " title_kwargs={\"fontsize\": 12},\n", - " label_kwargs={\"fontsize\": 15},\n", + " title_kwargs={\"fontsize\": 10},\n", + " label_kwargs={\"fontsize\": 10},\n", " title_fmt=\".4f\",\n", - ");" + " fig=fig,\n", + ")" ] }, { @@ -495,28 +491,24 @@ "metadata": {}, "outputs": [], "source": [ - "inferred_t0 = np.median(samples[\"t0\"])\n", - "inferred_period = np.median(samples[\"period\"])\n", - "inferred_duration = np.median(samples[\"duration\"])\n", - "inferred_r = np.median(samples[\"r\"])\n", - "inferred_b = np.median(samples[\"b\"])\n", - "inferred_u = np.median(samples[\"u\"], axis=0)\n", + "inferred_params = {\n", + " \"t0\": np.median(samples[\"t0\"]),\n", + " \"period\": np.median(samples[\"period\"]),\n", + " \"duration\": np.median(samples[\"duration\"]),\n", + " \"r\": np.median(samples[\"r\"]),\n", + " \"b\": np.median(samples[\"b\"]),\n", + " \"u\": np.median(samples[\"u\"], axis=0),\n", + "}\n", "\n", - "orbit = TransitOrbit(\n", - " period=inferred_period,\n", - " duration=inferred_duration,\n", - " time_transit=inferred_t0,\n", - " impact_param=inferred_b,\n", - " radius_ratio=inferred_r,\n", - ")\n", - "y_model = limb_dark_light_curve(orbit, inferred_u)(t)\n", "\n", - "fig, ax = plt.subplots(dpi=150)\n", + "y_model = light_curve_model(t, inferred_params)\n", + "\n", + "fig, ax = plt.subplots()\n", "\n", "# Plot the folded data\n", "t_fold = (\n", - " t - inferred_t0 + 0.5 * inferred_period\n", - ") % inferred_period - 0.5 * inferred_period\n", + " t - inferred_params[\"t0\"] + 0.5 * inferred_params[\"period\"]\n", + ") % inferred_params[\"period\"] - 0.5 * inferred_params[\"period\"]\n", "ax.errorbar(\n", " t_fold,\n", " y,\n", @@ -538,7 +530,8 @@ "ax.set_xlabel(\"time since transit [days]\")\n", "ax.set_ylabel(\"relative flux\")\n", "ax.legend(fontsize=10, loc=4)\n", - "ax.set_xlim(-inferred_duration, inferred_duration);" + "ax.set_xlim(-inferred_params[\"duration\"], inferred_params[\"duration\"])\n", + "plt.tight_layout()" ] }, { @@ -565,7 +558,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.10" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/src/jaxoplanet/experimental/starry/light_curves.py b/src/jaxoplanet/experimental/starry/light_curves.py index d8ae06e9..4a7c11ae 100644 --- a/src/jaxoplanet/experimental/starry/light_curves.py +++ b/src/jaxoplanet/experimental/starry/light_curves.py @@ -87,7 +87,7 @@ def map_light_curve( """Light curve of an occulted map. Args: - map (Map): map object + map (Map): Surface object r (float or None): radius of the occulting body, relative to the current map body xo (float or None): x position of the occulting body, relative to the current diff --git a/src/jaxoplanet/experimental/starry/pijk.py b/src/jaxoplanet/experimental/starry/pijk.py index ddb67d82..5ed8d483 100644 --- a/src/jaxoplanet/experimental/starry/pijk.py +++ b/src/jaxoplanet/experimental/starry/pijk.py @@ -12,19 +12,26 @@ class Pijk(eqx.Module): - """A class to represent and manipulate spherical harmonics in the polynomial basis. - Several indices are used throughout the class: - - Indices (i, j, k) represent the order of the polynomials of (x, y, z), for example - (1, 0, 2) represents x * z^2. - - Indices (l, m) represent the orders of the spherical harmonics. - - Index n represent the index of the polynomial in the flattened array. + r"""A class to represent and manipulate spherical harmonics in the + polynomial basis. Several indices are used throughout the class: - Flattened array `to_dense` and `from_dense` follow the convention from Luger et al. - (2019). More specifically: + * Indices :math:`(i, j, k)` represent the order of the polynomials of + :math:`(x, y, z)`, for example :math:`(1, 0, 2)` represents :math:`x\,z^2`. + + * Indices :math:`(l, m)` represent the orders of the spherical harmonics. + + * Index n represent the index of the polynomial in the flattened array. + + Flattened array ``todense`` and ``from_dense`` follow the convention from + Luger et al. (2019). More specifically: .. math:: - \tilde{p} = (1, x, y, z, x^2, xz, xy, yz, y^2, ...)^T + \tilde{p} = + \begin{pmatrix} + 1 & x & y & z & x^2 & xz & xy & yz & y^2 & + \cdot\cdot\cdot + \end{pmatrix}^\mathsf{T} """ diff --git a/src/jaxoplanet/experimental/starry/surface.py b/src/jaxoplanet/experimental/starry/surface.py index 135d2ebd..71111482 100644 --- a/src/jaxoplanet/experimental/starry/surface.py +++ b/src/jaxoplanet/experimental/starry/surface.py @@ -51,26 +51,26 @@ class Surface(eqx.Module): show_map(m) """ - # Ylm object representing the spherical harmonic expansion of the map. y: Ylm + """Ylm object representing the spherical harmonic expansion of the map""" - # Inclination of the map in radians. inc: Array + """Inclination of the map in radians""" - # Obliquity of the map in radians. obl: Array + """Obliquity of the map in radians.""" - # Tuple of limb darkening coefficients. u: tuple[Array, ...] + """Tuple of limb darkening coefficients.""" - # Rotation period of the map in days (attribute subject to change) period: Array | None + """Rotation period of the map in days (attribute subject to change)""" - # Amplitude of the map, a quantity proportional to map luminosity. amplitude: Array + """Amplitude of the map, a quantity proportional to map luminosity.""" - # Boolean to specify whether the Ylm coefficients should be normalized normalize: bool + """Boolean to specify whether the Ylm coefficients should be normalized""" def __init__( self, @@ -100,23 +100,26 @@ def __init__( self.normalize = normalize @property - def poly_basis(self): + def _poly_basis(self): return jax.jit(poly_basis(self.deg)) @property def udeg(self): + """Order of the polynomial limb darkening.""" return len(self.u) @property def ydeg(self): + """Degree of the spherical harmonic expansion.""" return self.y.ell_max @property def deg(self): + """Total degree of the spherical harmonic expansion (`udeg + ydeg`).""" return self.ydeg + self.udeg def _intensity(self, x, y, z, theta=0.0): - pT = self.poly_basis(x, y, z) + pT = self._poly_basis(x, y, z) Ry = left_project(self.ydeg, self.inc, self.obl, theta, 0.0, self.y.todense()) A1Ry = A1(self.ydeg).todense() @ Ry p_y = Pijk.from_dense(A1Ry, degree=self.ydeg) @@ -165,6 +168,14 @@ def intensity(self, lat: float, lon: float): return self._intensity(x, y, z) def rotational_phase(self, time: Array) -> Array: + """Returns the rotational phase of the map at a given time. + + Args: + time (ArrayLike): time in same units as the period + + Returns: + ArrayLike: rotational phase of the map at the given time + """ if self.period is None: return jnp.zeros_like(time) else: diff --git a/src/jaxoplanet/experimental/starry/ylm.py b/src/jaxoplanet/experimental/starry/ylm.py index e1ff631f..ab0c2780 100644 --- a/src/jaxoplanet/experimental/starry/ylm.py +++ b/src/jaxoplanet/experimental/starry/ylm.py @@ -1,3 +1,40 @@ +r"""A module to manipulate vectors in the spherical harmonic basis. + +The spherical harmonics basis is a set of orthogonal functions defined on the +unit sphere. In jaxoplanet, this basis is used to represent the intensity at the surface +of a spherical body, such as a star or a planet. We say that :math:`y` represents the +intensity of a surface in the spherical harmonics basis if the specific intensity at the +:math:`(x,y)` on the surface can be written as: + +.. math:: + + I(x, y) = \mathbf{\tilde{y}_n^\mathsf{T}} (x, y) \, \mathbf{y} + \quad, + +where :math:`\tilde{y}_n` is the **spherical harmonic basis**, +arranged in increasing degree and order: + +.. math:: + + \mathbf{\tilde{y}_n} = + \begin{pmatrix} + Y_{0, 0} & + Y_{1, -1} & Y_{1, 0} & Y_{1, 1} & + Y_{2, -2} & Y_{2, -1} & Y_{2, 0} & Y_{2, 1} & Y_{2, 2} & + \cdot\cdot\cdot + \end{pmatrix}^\mathsf{T} + \quad, + +where :math:`Y_{l, m} = Y_{l, m}(x, y)` is the spherical harmonic of degree :math:`l` +and order :math:`m`. For reference, in this basis the coefficient of the spherical +harmonic :math:`Y_{l, m}` is located at the index + +.. math:: + + n = l^2 + l + m + +""" + import math from collections import defaultdict from collections.abc import Mapping @@ -23,15 +60,16 @@ class Ylm(eqx.Module): spherical harmonic coefficients. Defaults to {(0, 0): 1.0}. """ - # coefficients of the spherical harmonic expansion of the map in the form - # {(l, m): coefficient} data: dict[tuple[int, int], Array] + """coefficients of the spherical harmonic expansion of the map in the form + `{(l, m): coefficient}`""" - # maximum degree of the spherical harmonic expansion ell_max: int = eqx.field(static=True) + """The maximum degree of the spherical harmonic coefficients.""" - # whether the spherical harmonic expansion is diagonal (all m=0) diagonal: bool = eqx.field(static=True) + """Whether are orders m of the spherical harmonic coefficients are zero. + Diagonal if only the degrees "l" are non-zero.""" def __init__( self, @@ -46,21 +84,22 @@ def __init__( @property def shape(self) -> tuple[int, ...]: - """The number of coefficients in the expansion. This sets the shape of + """The number of coefficients in the basis. This sets the shape of the output of `todense`.""" return (self.ell_max**2 + 2 * self.ell_max + 1,) @property def indices(self) -> list[tuple[int, int]]: + """List of (l,m) indices of the spherical harmonic coefficients.""" return list(self.data.keys()) def index(self, ell: Array, m: Array) -> Array: """Convert the degree and order of the spherical harmonic to the - corresponding index in the coefficient array.""" + corresponding index in the coefficients array.""" return ell * (ell + 1) + m def normalize(self) -> "Ylm": - """Return a new Ylm instance with normalized coefficients. + """Return a new Ylm instance with coefficients normalized to :math:`Y_{0,0}`. Returns: Ylm instance with normalized coefficients. @@ -76,15 +115,26 @@ def normalize(self) -> "Ylm": return Ylm(data=data) def tosparse(self) -> BCOO: + """Return a sparse (jax.experimental.sparse.BCOO) spherical harmonic + coefficients vector where the spherical harmonic :math:`Y_{l, m}` is located at + the index :math:`n = l^2 + l + m`. + """ indices, values = zip(*self.data.items(), strict=False) idx = jnp.array([self.index(ell, m) for ell, m in indices])[:, None] return BCOO((jnp.asarray(values), idx), shape=self.shape) def todense(self) -> Array: + """Return a dense spherical harmonic coefficients vector where the spherical + harmonic :math:`Y_{l, m}` is located at the index :math:`n = l^2 + l + m`. + """ return self.tosparse().todense() @classmethod def from_dense(cls, y: Array, normalize: bool = True) -> "Ylm": + """Create a Ylm object from a dense array of spherical harmonic coefficients + where the spherical harmonic :math:`Y_{l, m}` is located at the index + :math:`n = l^2 + l + m`. + """ data = {} for i, ylm in enumerate(y): ell = int(np.floor(np.sqrt(i))) @@ -115,7 +165,8 @@ def _mul(f: Ylm, g: Ylm) -> Ylm: """ Based closely on the implementation from the MIT-licensed spherical package: - https://github.com/moble/spherical/blob/0aa81c309cac70b90f8dfb743ce35d2cc9ae6dee/spherical/multiplication.py + https://github.com/moble/spherical/blob/0aa81c309cac70b90f8dfb743ce35d2cc9ae6dee/ + spherical/multiplication.py """ ellmax_f = f.ell_max ellmax_g = g.ell_max @@ -208,8 +259,8 @@ def func(contrast: float, r: float, lat: float = 0.0, lon: float = 0.0): """spot expansion in the spherical harmonics basis. Args: - contrast (float): spot contrast, defined as (1-c) where c is the intensity of - the center of the spot relative to the unspotted surface. A contrast of 1. + contrast (float): spot contrast, defined as (1-c) where c is the intensity + of the center of the spot relative to the unspotted surface. A contrast of 1. means that the spot intensity drops to zero at the center, 0. means that the intensity at the center of the spot is the same as the intensity of the unspotted surface. diff --git a/src/jaxoplanet/light_curves/transforms.py b/src/jaxoplanet/light_curves/transforms.py index eca8ecbb..6c2a0e9d 100644 --- a/src/jaxoplanet/light_curves/transforms.py +++ b/src/jaxoplanet/light_curves/transforms.py @@ -1,3 +1,6 @@ +"""A module providing decorators to transform light curve functions +""" + __all__ = ["integrate", "interpolate"] from functools import wraps diff --git a/src/jaxoplanet/orbits/keplerian.py b/src/jaxoplanet/orbits/keplerian.py index 29dcf14c..a4f0c375 100644 --- a/src/jaxoplanet/orbits/keplerian.py +++ b/src/jaxoplanet/orbits/keplerian.py @@ -1,3 +1,6 @@ +"""A module to define Keplerian systems of bodies. +""" + from collections.abc import Callable, Iterable, Sequence from typing import Any @@ -717,6 +720,15 @@ def add_body( central: Central | None = None, **kwargs: Any, ) -> "System": + """Add a body to the system and return a new system + + Args: + body (Body | None, optional): body to add. Defaults to None. + central (Central | None, optional): TODO. Defaults to None. + + Returns: + System: :py:class:`~jaxoplanet.orbits.keplerian.System` with the added body + """ body_: Body | OrbitalBody | None = body if body_ is None: body_ = Body(**kwargs)