{
 "cells": [
  {
   "cell_type": "raw",
   "id": "eaf29ef7",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _cookbook-spb:\n",
    "\n",
    ".. include:: <isonum.txt>\n",
    "\n",
    "Light Curves for an SPB Star\n",
    "============================\n",
    "\n",
    "This :ref:`cookbook` recipe demonstrates how to synthesize light curves for a slowly pulsating B-type (SPB) star, using the MSG Python interace. The light curves are based on surface perturbation data for a :math:`5\\,\\Msun` model, produced by the `GYRE <gyre_>`__ stellar oscillation code. \n",
    "\n",
    "Preparation\n",
    "-----------\n",
    "\n",
    "Download :download:`spb-light-curve.tar.bz2` and unpack it in your working directory. This archive contains the following items:\n",
    "\n",
    "* :file:`summary.h5` |---| surface perturbation data produced by GYRE\n",
    "* :file:`pygyre` |---| Python module for reading :file:`summary.h5`\n",
    "* :file:`pg-*.h5` |---| :f-schema:`photgrid` files for each passband [#photgrids]_\n",
    "* :file:`gyre.in` |---| GYRE input file used to create :file:`summary.h5`\n",
    "* :file:`spb.mesa` |---| stellar model file used to create :file:`summary.h5` [#model]_\n",
    "\n",
    "Note that :file:`gyre.in` and :file:`spb.mesa` aren't needed for the recipe; they're included for those who wish to recreate :file:`summary.h5`, or are curious about its provenance.\n",
    "\n",
    "Initialization\n",
    "--------------\n",
    "\n",
    "To start, import modules and set other parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2eb4244c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import modules\n",
    "\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import scipy.special as ss\n",
    "\n",
    "import astropy.constants as con\n",
    "\n",
    "import pymsg\n",
    "import pygyre\n",
    "\n",
    "# Set plot parameters\n",
    "\n",
    "%matplotlib inline\n",
    "plt.rcParams.update({'font.size': 16})"
   ]
  },
  {
   "cell_type": "raw",
   "id": "1a08b07c",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Define a Synthesis Function\n",
    "---------------------------\n",
    "\n",
    "Next, define a function that synthesizes a light curve given the following arguments:\n",
    "\n",
    "* :code:`phi` |---| oscillation phase array (radians)\n",
    "* :code:`i` |---| observer inclination angle (radians)\n",
    "* :code:`photgrid_name` |---| name of :f-schema:`photgrid` file\n",
    "\n",
    "This function follows the semi-analytical approach described in Section 2 of :ads_citet:`townsend:2003`; the comments in the function definition highlight the specific equations from that paper."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "02d6f490",
   "metadata": {},
   "outputs": [],
   "source": [
    "def synthesize_light_curve(phi, i, photgrid_name):\n",
    "    \n",
    "    # Create the PhotGrid object\n",
    "\n",
    "    photgrid = pymsg.PhotGrid(photgrid_name)\n",
    "    \n",
    "    # Read the GYRE output file into a table\n",
    "\n",
    "    tbl = pygyre.read_output('summary.h5')\n",
    "    \n",
    "    # Select the table row corresponding to the l=m=2, g_30 mode\n",
    "    \n",
    "    mask = (tbl['l'] == 2) & (tbl['m'] == 2) & (tbl['n_pg'] == -30)\n",
    "\n",
    "    row = tbl[mask][0]\n",
    "    \n",
    "    # Extract stellar parameters\n",
    "\n",
    "    M_star = row['M_star']\n",
    "    R_star = row['R_star']\n",
    "    L_star = row['L_star']\n",
    "\n",
    "    Teff = (L_star/(4*np.pi*con.sigma_sb.cgs.value*R_star**2))**0.25\n",
    "    g = con.G.cgs.value*M_star/R_star**2\n",
    "                                  \n",
    "    # Extract surface perturbation parameters      \n",
    "\n",
    "    l = row['l']\n",
    "    m = row['m']\n",
    "    \n",
    "    omega = row['omega']\n",
    "\n",
    "    dR_R = row['xi_r_ref']      # Delta_R (T03, eqn. 4)\n",
    "    dL_L = row['lag_L_ref']\n",
    "    dT_T = 0.25*(dL_L - 2*dR_R) # Delta_T (T03, eqn. 5)\n",
    "    dg_g = -(2 + omega**2)*dR_R # Delta_g (T03, eqn. 6)\n",
    "    \n",
    "    # Evaluate intensity moments & their partial derivatives (T03, eqn. 15)\n",
    "\n",
    "    x = {'Teff': Teff, 'log(g)': np.log10(g)}\n",
    "\n",
    "    I_0 = photgrid.D_moment(x, 0)\n",
    "    I_x = photgrid.D_moment(x, l)\n",
    "\n",
    "    dI_x_dlnT = photgrid.D_moment(x, l, deriv={'Teff': True}) * Teff\n",
    "    dI_x_dlng = photgrid.D_moment(x, l, deriv={'log(g)': True}) * np.log(np.exp(1.))\n",
    "\n",
    "    # Set up differential flux functions (T03, eqns. 12-14)\n",
    "\n",
    "    R_lm = (2+l)*(1-l) * I_x/I_0 * ss.sph_harm(m, l, 0., i)\n",
    "    T_lm = dI_x_dlnT/I_0 * ss.sph_harm(m, l, 0., i)\n",
    "    G_lm = dI_x_dlng/I_0 * ss.sph_harm(m, l, 0., i)\n",
    "    \n",
    "    # Construct the light curve (T03, eqn. 11)\n",
    "\n",
    "    dF_F = ((dR_R*R_lm + dT_T*T_lm + dg_g*G_lm) * np.exp(1j*phi)).real\n",
    "\n",
    "    # Return it\n",
    "    \n",
    "    return dF_F"
   ]
  },
  {
   "cell_type": "raw",
   "id": "5b4d9d5f",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Evaluate Light Curves\n",
    "---------------------\n",
    "\n",
    "Using this function, evaluate light curves in each passband:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "affede99",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Set up phase array\n",
    "\n",
    "phi = np.linspace(0, 4*np.pi, 1001)\n",
    "\n",
    "# Set observer inclination\n",
    "\n",
    "i = np.radians(75)\n",
    "\n",
    "# Evaluate light curves\n",
    "\n",
    "dF_F_Kepler = synthesize_light_curve(phi, i, 'pg-Kepler.h5')\n",
    "dF_F_TESS = synthesize_light_curve(phi, i, 'pg-TESS.h5')\n",
    "dF_F_Gaia_B = synthesize_light_curve(phi, i, 'pg-Gaia-B.h5')\n",
    "dF_F_Gaia_R = synthesize_light_curve(phi, i, 'pg-Gaia-R.h5')\n",
    "dF_F_JWST = synthesize_light_curve(phi, i, 'pg-JWST.h5')"
   ]
  },
  {
   "cell_type": "raw",
   "id": "93b3427b",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Note that the :file:`pg-JWST.h5` file is based on the JWST NIRCam F150W filter |---| chosen to demonstrate what the light curve will look like in the near infrared, but with the recognition that it's very unlikely JWST will ever observe an SPB star!\n",
    "\n",
    "Plot the Light Curves\n",
    "---------------------\n",
    "\n",
    "As a final step, plot the light curve in each filter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8ac1f847",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7f8a86fb86a0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 84500 characters]\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot light curves\n",
    "\n",
    "plt.figure()\n",
    "\n",
    "plt.plot(phi/(2*np.pi), dF_F_Gaia_B, label='Gaia B', color='blue')\n",
    "plt.plot(phi/(2*np.pi), dF_F_Kepler, label='Kepler', color='green')\n",
    "plt.plot(phi/(2*np.pi), dF_F_TESS, label='TESS', color='yellow')\n",
    "plt.plot(phi/(2*np.pi), dF_F_Gaia_R, label='Gaia R', color='orange')\n",
    "plt.plot(phi/(2*np.pi), dF_F_JWST, label='JWST', color='red')\n",
    "\n",
    "plt.xlabel('Phase (cycles)')\n",
    "plt.ylabel('Delta F/F')\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "raw",
   "id": "a4e8ca55",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "A clear trend can be seen in the light curves: from red wavelengths to blue (i.e., JWST |rarr| Gaia R |rarr| TESS |rarr| Kepler |rarr| Gaia B), the amplitude becomes larger and maximum/minimum light occur at later phases. The amplitude trend arises because the visible and NIR fall on the Rayleigh-Jeans tail of the star's spectral energy distribution, and therefore the flux variations due to temperature perturbations vary as :math:`\\lambda^{-4}`. The phase trend is a consequence of non-adiabatic effects that cause (for this particular mode) the radius perturbations to lag the temperature perturbations by :math:`\\sim 45^{\\circ}`.\n",
    "\n",
    "Note that the overall scaling of the light curves is not physically meaningful; as with any linear oscillation calculation, the normalization of perturbations is arbitrary. In the case of GYRE, this normalization is chosen to yield a mode inertia of :math:`M R^{2}`.\n",
    "\n",
    ".. rubric:: Footnotes\n",
    "\n",
    ".. [#photgrids] Generated by applying the :command:`specgrid_to_photgrid` tool to an extended-wavelength version of the demo grid.\n",
    ".. [#model] Generated using the `MESA <mesa_>`__ stellar evolution code."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.8.17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
