{
 "cells": [
  {
   "cell_type": "raw",
   "id": "eaf29ef7",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _walkthroughs-python:\n",
    "\n",
    "Python Walkthrough\n",
    "==================\n",
    "\n",
    "This section walks through using the MSG Python interface\n",
    "to evaluate spectra and photometric colors for a model of Sirius. The code fragments are presented as a\n",
    "sequence of `Jupyter <https://jupyter.org/>`__ notebook cells, but pasting all of them\n",
    "into a Python script should work also. Alternatively, the entire notebook (including this text) is available in the source distribution at :file:`$MSG_DIR/docs/source/user-guide/walkthroughs/python-walkthrough.ipynb`.\n",
    "\n",
    "Preparation\n",
    "-----------\n",
    "\n",
    "Before starting Jupyter, make sure that you have installed the :py:mod:`pymsg` package as described in the :ref:`quick-start` and/or :ref:`installation` chapters. Also, ensure that the `NumPy\n",
    "<https://numpy.org/>`__, `Matplotlib <https://matplotlib.org/>`__ and\n",
    "`Astropy <https://www.astropy.org/>`__ packages are installed on your\n",
    "system. Finally, confirm that the :envvar:`MSG_DIR` environment\n",
    "variable is set.\n",
    "\n",
    "Initialization\n",
    "--------------\n",
    "\n",
    "Launch Jupyter, create a new Python3 notebook, and initialize the notebook \n",
    "with the following statements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2eb4244c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import standard modules\n",
    "\n",
    "import os\n",
    "import numpy as np\n",
    "import astropy.constants as con\n",
    "import astropy.units as unt\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Import pymsg\n",
    "\n",
    "import pymsg\n",
    "\n",
    "# Set plot parameters\n",
    "\n",
    "%matplotlib inline\n",
    "plt.rcParams.update({'font.size': 16})"
   ]
  },
  {
   "cell_type": "raw",
   "id": "5b4d9d5f",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Loading & Inspecting the Demo Grid\n",
    "----------------------------------\n",
    "\n",
    "For demonstration purposes MSG includes a low-resolution :f-schema:`specgrid` (spectroscopic grid) file, :file:`$MSG_DIR/data/grids/sg-demo.h5`\\ [#grids]_. Load this demo grid into memory\\ [#memory]_ by creating a\n",
    "new :py:class:`pymsg.SpecGrid` object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "88947edc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the SpecGrid\n",
    "\n",
    "MSG_DIR = os.environ['MSG_DIR']\n",
    "GRID_DIR = os.path.join(MSG_DIR, 'data', 'grids')\n",
    "\n",
    "specgrid_file_name = os.path.join(GRID_DIR, 'sg-demo.h5')\n",
    "\n",
    "specgrid = pymsg.SpecGrid(specgrid_file_name)"
   ]
  },
  {
   "cell_type": "raw",
   "id": "556f2bd2",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "This object has a number of (read-only) properties that inform us about\n",
    "the parameter space spanned by the grid:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b4d6d202",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Grid parameters:\n",
      "  Teff [3500.0 -> 50000.0]\n",
      "  log(g) [0.0 -> 5.0]\n",
      "  lam [2999.9999999999977 -> 9003.490078514782]\n"
     ]
    }
   ],
   "source": [
    "# Inspect grid parameters\n",
    "\n",
    "print('Grid parameters:')\n",
    "\n",
    "for label in specgrid.axis_labels:\n",
    "    print(f'  {label} [{specgrid.axis_x_min[label]} -> {specgrid.axis_x_max[label]}]')\n",
    "\n",
    "print(f'  lam [{specgrid.lam_min} -> {specgrid.lam_max}]')"
   ]
  },
  {
   "cell_type": "raw",
   "id": "796adf25",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Here, ``Teff`` and ``log(g)`` correspond (respectively) to the\n",
    ":math:`\\Teff/\\kelvin` and :math:`\\log_{10}(g/\\cm\\,\\second^{-2})` photospheric parameters, while\n",
    "``lam`` is wavelength :math:`\\lambda/\\angstrom`.\n",
    "\n",
    "Plotting the Flux\n",
    "-----------------\n",
    "\n",
    "With the grid loaded, let's evaluate and plot a flux spectrum for\n",
    "Sirius A. First, store the photospheric parameters for the star in a dict:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6f8b6aba",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9909.16258994498 4.277426775965449\n"
     ]
    }
   ],
   "source": [
    "# Set Sirius A's fundamental parameters\n",
    "\n",
    "M = 2.02 *con.M_sun\n",
    "R = 1.71 * con.R_sun\n",
    "L = 25.4 * con.L_sun\n",
    "\n",
    "p = 0.379\n",
    "d = 1/p * con.pc \n",
    "\n",
    "# Set photospheric parameters dict\n",
    "\n",
    "Teff = (L/(4*np.pi*R**2*con.sigma_sb))**0.25/unt.K\n",
    "logg = np.log10(con.G*M/R**2/(unt.cm/unt.s**2))\n",
    "\n",
    "x = {'Teff': Teff, 'log(g)': logg}\n",
    "print(Teff, logg)"
   ]
  },
  {
   "cell_type": "raw",
   "id": "ebb2bf63",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "(the mass, radius and luminosity data are taken from :ads_citealp:`liebert:2005`, while the parallax comes from `Simbad <http://simbad.cds.unistra.fr/simbad/sim-basic?Ident=sirius&submit=SIMBAD+search>`__). Then\n",
    "set up a uniformly-spaced wavelength abcissa for a spectrum spanning the visible range,\n",
    ":math:`3\\,000\\,\\angstrom` to :math:`7\\,000\\,\\angstrom`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "273ccc70",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set up the wavelength abscissa\n",
    "\n",
    "lam_min = 3000.\n",
    "lam_max = 7000.\n",
    "\n",
    "lam = np.linspace(lam_min, lam_max, 501)\n",
    "\n",
    "lam_c = 0.5*(lam[1:] + lam[:-1])"
   ]
  },
  {
   "cell_type": "raw",
   "id": "e47705fd",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Here, the array ``lam`` defines the boundaries of 500 wavelength bins\n",
    ":math:`\\{\\lambda_{i},\\lambda_{i+1}\\}` (:math:`i=1,\\ldots,500`) and the\n",
    "array ``lam_c`` stores the central wavelength of each bin.\n",
    "\n",
    "With all our parameters defined, evaluate the observed flux using a\n",
    "call to the :py:meth:`pymsg.SpecGrid.flux` method, and then plot it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "98437c97",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, '$F^{\\\\mathrm{o}}_{\\\\lambda}\\\\ ({\\\\rm erg\\\\,cm^{-2}\\\\,s^{-1}}\\\\,\\\\AA^{-1})$')"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 42292 characters]\n",
      "text/plain": [
       "<Figure size 576x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Evaluate the observed flux\n",
    "\n",
    "F_obs = (R/d)**2*specgrid.flux(x, lam)\n",
    "\n",
    "# Plot\n",
    "\n",
    "plt.figure(figsize=[8,8])\n",
    "plt.plot(lam_c, F_obs)\n",
    "\n",
    "plt.xlabel(r'$\\lambda ({\\AA})$')\n",
    "plt.ylabel(r'$F^{\\mathrm{o}}_{\\lambda}\\ ({\\rm erg\\,cm^{-2}\\,s^{-1}}\\,\\AA^{-1})$')"
   ]
  },
  {
   "cell_type": "raw",
   "id": "19c11a46",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "(See equation :math:numref:`eq:obs-flux-elem` of the :ref:`msg-fundamentals` chapter to understand the origin of the expression for :code:`F_obs`). The plot looks about right --- we can clearly see the Balmer series, starting with H\\ :math:`\\alpha` at :math:`6563\\,\\angstrom`.\n",
    "\n",
    "Plotting the Intensity\n",
    "----------------------\n",
    "\n",
    "Sometimes we want to know the specific intensity of the radiation\n",
    "emerging from each element of a star's surface; an example might be when we're\n",
    "modeling a rotationally distorted star, whose surface geometry and properties vary with latitude. For this, we can use the :py:meth:`pymsg.SpecGrid.intensity` method.\n",
    "\n",
    "Here's a demonstration of this method in action, plotting the specific\n",
    "intensity across the H\\ :math:`\\alpha` line profile for ten different\n",
    "values of the emergence direction cosine :math:`\\mu=0.1,0.2,\\ldots,1.0`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4cf4572a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7ff643465670>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 103524 characters]\n",
      "text/plain": [
       "<Figure size 576x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Set up the wavelength abscissa\n",
    "\n",
    "lam_min = 6300.\n",
    "lam_max = 6800.\n",
    "\n",
    "lam = np.linspace(lam_min, lam_max, 100)\n",
    "\n",
    "lam_c = 0.5*(lam[1:] + lam[:-1])\n",
    "\n",
    "# Loop over mu\n",
    "\n",
    "plt.figure(figsize=[8,8])\n",
    "\n",
    "for mu in np.linspace(1.0, 0.1, 10):\n",
    "\n",
    "    # Evaluate the intensity\n",
    "\n",
    "    I = specgrid.intensity(x, mu, lam)\n",
    "\n",
    "    # Plot\n",
    "\n",
    "    if mu==0.1 or mu==1.0:\n",
    "        label=r'$\\mu={:3.1f}$'.format(mu)\n",
    "    else:\n",
    "        label=None\n",
    "\n",
    "    plt.plot(lam_c, I, label=label)\n",
    "\n",
    "plt.xlabel(r'$\\lambda ({\\AA})$')\n",
    "plt.ylabel(r'$I_{\\lambda}\\ ({\\rm erg\\,cm^{-2}\\,s^{-1}}\\,\\AA^{-1}\\,sr^{-1})$')\n",
    "\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "raw",
   "id": "6a518cfb",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "We can clearly see that limb-darkening in the line core is much weaker\n",
    "than in the continuum --- exactly what we expect from such a strong\n",
    "line.\n",
    "\n",
    "Evaluating Magnitudes & Colors\n",
    "------------------------------\n",
    "\n",
    "As a final step in this walkthrough, let's evaluate the magnitude and\n",
    "colors of Sirius A in the Johnson photometric system. We can do this by \n",
    "creating a new :py:class:`pymsg.PhotGrid` object for each passband:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "46efce2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load the PhotGrids\n",
    "\n",
    "PASS_DIR = os.path.join(MSG_DIR, 'data', 'passbands')\n",
    "filters = ['U', 'B', 'V']\n",
    "\n",
    "photgrids = {}\n",
    "\n",
    "for filter in filters:\n",
    "    passband_file_name = os.path.join(PASS_DIR, f'pb-Generic-Johnson.{filter}-Vega.h5')\n",
    "    photgrids[filter] = pymsg.PhotGrid(specgrid_file_name, passband_file_name)"
   ]
  },
  {
   "cell_type": "raw",
   "id": "3d0680d8",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "(for convenience, we store the :py:class:`pymsg.PhotGrid` objects in a\n",
    "dict, keyed by filter name).  In the calls to the object constructor\n",
    ":py:meth:`pymsg.PhotGrid`, the first argument is the name of a\n",
    ":f-schema:`specgrid` file (here, the demo grid), and the second argument is the\n",
    "name of a :f-schema:`passband` file; a limited set of these files is\n",
    "provided in the :file:`$MSG_DIR/data/passbands` subdirectory [#passbands]_. \n",
    "The observed fluxes of Sirius A are then found using the :py:meth:`pymsg.PhotGrid.flux` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "51c4d152",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the observed fluxes (each normalized to the passband\n",
    "# zero-point flux)\n",
    "\n",
    "F_obs = {}\n",
    "\n",
    "for filter in filters:\n",
    "    F_obs[filter] = (R/d)**2*photgrids[filter].flux(x)"
   ]
  },
  {
   "cell_type": "raw",
   "id": "ac6a863b",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "To convert these into apparent magnitudes, we apply\n",
    ":wiki:`Pogson's <N._R._Pogson>` logarithmic formula:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "28ed8101",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "V=-1.4466779855804992, U-B=-0.05964681835037444, B-V=0.001121934559611848\n"
     ]
    }
   ],
   "source": [
    "# Evaluate apparent magnitudes and print out magnitude & colors\n",
    "\n",
    "mags = {}\n",
    "\n",
    "for filter in filters:\n",
    "    mags[filter] = -2.5*np.log10(F_obs[filter])\n",
    "\n",
    "print(f\"V={mags['V']}, U-B={mags['U']-mags['B']}, B-V={mags['B']-mags['V']}\")"
   ]
  },
  {
   "cell_type": "raw",
   "id": "0c3c147e",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "Reassuringly, the resulting values are within 10 millimag of Sirius A's\n",
    "apparent V-band magnitude and colors, as given by `Simbad <http://simbad.cds.unistra.fr/simbad/sim-basic?Ident=sirius&submit=SIMBAD+search>`__.\n",
    "\n",
    ".. rubric:: Footnotes\n",
    "\n",
    ".. [#grids] Larger grids can be downloaded separately; see the `grid files <grid-files_>`__ web page.\n",
    "    \t    \n",
    ".. [#memory] Behind the scenes, the data in the :f-schema:`specgrid` file are loaded on demand; see\n",
    "             the :ref:`data-caching` chapter for further details.\n",
    "             \n",
    ".. [#passbands] Passband definition files for other instruments/photometric systems \n",
    "                can be downloaded separately; see the `passband files <passband-files_>`__ web page."
   ]
  }
 ],
 "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
}
