{
 "cells": [
  {
   "cell_type": "raw",
   "id": "eaf29ef7",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _cookbook-bcep:\n",
    "\n",
    ".. include:: <isonum.txt>\n",
    "\n",
    "Line Profile Variations of a Be Star\n",
    "====================================\n",
    "\n",
    "This :ref:`cookbook` recipe demonstrates how to simulate the spectral line-profile variations (lpv) of a pulsating Be star, using the MSG Python interface. Synthesizing a spectrum for a pulsating star involves constructing an ensemble of elements representing the stellar surface; subjecting these elements to perturbations from one or more modes; and then integrating the specific intensity over the elements visible to the observer. This process is similar to the disk integration in equation :math:numref:`eq:obs-flux` of the :ref:`msg-fundamentals` chapter, but a further complication is that one must correctly account for Doppler shifts arising from the motion of the elements along the line-of-sight.\n",
    "\n",
    "The open-source :mad-star:`BRUCE and KYLIE codes <bruce>` :ads_citep:`townsend:1997` perform these tasks. BRUCE constructs and perturbs surface elements, while KYLIE then performs the specific intensity calculations and visible-disk integration. In this recipe the KYLIE functionality is re-implemented in Python.\n",
    "\n",
    "Preparation\n",
    "-----------\n",
    "\n",
    "Download :download:`be-star-lpv.tar.bz2` and unpack it in your working directory. This archive contains the following items:\n",
    "\n",
    "* :file:`elements-*` |---| surface element data produced by BRUCE\n",
    "* :file:`sg-6678.h5` |---| :f-schema:`specgrid` file covering the HeI :math:`\\lambda 6678` line profile [#specgrid]_\n",
    "* :file:`input.bdf` |---| BRUCE input file used to create :file:`elements-*`\n",
    "\n",
    "Note that :file:`input.bdf` isn't needed for the recipe; it's included for those who wish to recreate :file:`elements-*`, or are curious about their 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 as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "import scipy.io as si\n",
    "\n",
    "import astropy.constants as con\n",
    "\n",
    "import pymsg\n",
    "\n",
    "# Set plot parameters\n",
    "\n",
    "%matplotlib inline\n",
    "plt.rcParams.update({'font.size': 16})"
   ]
  },
  {
   "cell_type": "raw",
   "id": "3fa2cc43",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Create a SpecGrid\n",
    "-----------------\n",
    "\n",
    "Next, create a :py:class:`pymsg.SpecGrid` object for interpolating specific intensities:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "68e6177c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create SpecGrid object\n",
    "\n",
    "specgrid = pymsg.SpecGrid('sg-6678.h5')"
   ]
  },
  {
   "cell_type": "raw",
   "id": "1a08b07c",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Define a Synthesis Function\n",
    "---------------------------\n",
    "\n",
    "Now define a function that synthesizes a light curve given the following parameters:\n",
    "\n",
    "* `lam` |---| wavelength abscissa array (Angstroms)\n",
    "* `d` |---| distance to star (meters)\n",
    "* `bruce_file` |---| name of BRUCE output file\n",
    "* `specgrid` |---| :py:class:`pymsg.SpecGrid` object\n",
    "\n",
    "This function is quite simple: it simply adds up the specific intensities from each surface element, appropriately Doppler-shifted and weighted by the elements's projected area and the observer solid angle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "02d6f490",
   "metadata": {},
   "outputs": [],
   "source": [
    "def synthesize_spectrum(lam, d, bruce_file, specgrid):\n",
    "    \n",
    "    # Initialize the flux array\n",
    "    \n",
    "    n_lam = len(lam)\n",
    "    \n",
    "    F = np.zeros(n_lam-1)\n",
    "    \n",
    "    # Open the BRUCE output file\n",
    "    \n",
    "    with si.FortranFile(bruce_file, 'r') as f:\n",
    "        \n",
    "        # Read header\n",
    "        \n",
    "        record = f.read_record('<i4', '<f4')\n",
    "    \n",
    "        n_vis = record[0][0]\n",
    "        time = record[1][0]\n",
    "        \n",
    "        # Read elements and accumulate flux\n",
    "    \n",
    "        for i in range(n_vis):\n",
    "            \n",
    "            Teff, V_proj, A_proj, g, mu = f.read_reals('<f4')\n",
    "            \n",
    "            # Evaluate specific intenensity\n",
    "            \n",
    "            x = {'Teff': Teff, 'log(g)': np.log10(g)+2.}\n",
    "        \n",
    "            dI = specgrid.intensity(x, mu, lam*(1+V_proj/con.c.value))\n",
    "        \n",
    "            # Accumulate flux\n",
    "        \n",
    "            F += A_proj * dI * (1./d)**2\n",
    "        \n",
    "    # Return the time and flux\n",
    "    \n",
    "    return time, F"
   ]
  },
  {
   "cell_type": "raw",
   "id": "5b4d9d5f",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Evaluate the Spectra\n",
    "--------------------\n",
    "\n",
    "Using this function, evaluate a time-series of spectra:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "affede99",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Set up parameters\n",
    "\n",
    "d = 10*con.pc.value\n",
    "\n",
    "lam = np.linspace(6673, 6682, 101)\n",
    "lam_mid = 0.5*(lam[:-1] + lam[1:])\n",
    "\n",
    "n_time = 20\n",
    "\n",
    "# Evaluate spectra\n",
    "\n",
    "time = []\n",
    "F = []\n",
    "\n",
    "for i in range(n_time):\n",
    "    \n",
    "    bruce_file = f'elements-{i+1:03d}'\n",
    "    \n",
    "    result = synthesize_spectrum(lam, 10*con.pc.value, bruce_file, specgrid)\n",
    "\n",
    "    time += [result[0]]\n",
    "    F += [result[1]]"
   ]
  },
  {
   "cell_type": "raw",
   "id": "93b3427b",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Plot the Spectra\n",
    "----------------\n",
    "\n",
    "As a final step, plot the spectra (with a vertical offset between times, to allow comparison):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8ac1f847",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'Flux (erg/cm^2/s/Angstrom)')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 120628 characters]\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot spectra\n",
    "\n",
    "plt.figure()\n",
    "\n",
    "cmap = mpl.colormaps['cool']\n",
    "\n",
    "fig = plt.figure()\n",
    "\n",
    "offset=0.2*(np.max(F[0]) - np.min(F[0]))\n",
    "\n",
    "for i in range(n_time):\n",
    "    \n",
    "    plt.plot(lam_mid, 1E8*(F[i]+i*offset), color=cmap(i/(n_time-1)))\n",
    "\n",
    "plt.xlabel('Wavelength (Angstrom)')\n",
    "plt.ylabel('Flux (erg/cm^2/s/Angstrom)')"
   ]
  },
  {
   "cell_type": "raw",
   "id": "e59af876",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "The lpv in the figure take the form of dips of enhanced absorption that travel across the line profile from blue to red. This is similar to the variations observed in pulsating Be stars (see, e.g., :ads_citealt:`stefl:2003`).\n",
    "\n",
    ".. rubric:: Footnotes\n",
    "\n",
    ".. [#specgrid] Generated from a solar-metallicity `Kurucz\n",
    "  <http://kurucz.harvard.edu/grids>`__ atmosphere grid (ap00k2odfnew),\n",
    "  with intensity spectra synthesized using SYNSPEC :ads_citep:`hubeny:2017`."
   ]
  }
 ],
 "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
}
