Python Walkthrough

This section walks through using the MSG Python interface to evaluate spectra and photometric colors for a model of Sirius A. The code fragments are presented as a sequence of Jupyter notebook cells, but pasting all of them into a Python script should work also. Alternatively, the entire notebook (including this text) is available in the source distribution at $MSG_DIR/docs/source/user-guide/walkthroughs/python-walkthrough.ipynb.

Preparation

Before starting Jupyter, make sure that you have installed the pymsg package as described in the Quick Start and/or Installation chapters. Also, ensure that the NumPy, Matplotlib and Astropy packages are installed on your system. Finally, confirm that the MSG_DIR environment variable is set.

Initialization

Launch Jupyter, create a new Python3 notebook, and initialize the notebook with the following statements:

[1]:
# Import standard modules

import os
import numpy as np
import matplotlib.pyplot as plt
import astropy.constants as con

# Import pymsg

import pymsg

# Set constants

M_SUN = con.M_sun.cgs.value
R_SUN = con.R_sun.cgs.value
L_SUN = con.L_sun.cgs.value
PC = con.pc.cgs.value

G = con.G.cgs.value
C = con.c.cgs.value
SIGMA = con.sigma_sb.cgs.value

# Set plot parameters

plt.rcParams.update({'font.size': 12})

Loading & Inspecting the Demo Grid

For demonstration purposes MSG includes a low-resolution specgrid (spectroscopic grid) file, $MSG_DIR/data/grids/sg-demo.h5[1]. Load this demo grid into memory[2] by creating a new pymsg.SpecGrid object:

[2]:
# Create the SpecGrid object

MSG_DIR = os.environ['MSG_DIR']
GRID_DIR = os.path.join(MSG_DIR, 'data', 'grids')

specgrid_file_name = os.path.join(GRID_DIR, 'sg-demo.h5')

specgrid = pymsg.SpecGrid(specgrid_file_name)

This object has a number of (read-only) properties that inform us about the parameter space spanned by the grid:

[3]:
# Inspect grid parameters

print('Grid parameters:')

for label in specgrid.axis_labels:
    print(f'  {label} [{specgrid.axis_x_min[label]} -> {specgrid.axis_x_max[label]}]')

print(f'  lam [{specgrid.lam_min} -> {specgrid.lam_max}]')
Grid parameters:
  Teff [3500.0 -> 49000.0]
  log(g) [0.0 -> 5.0]
  lam [2999.9999999999977 -> 9003.490078514782]

Here, Teff and log(g) correspond (respectively) to the \(\Teff/\kelvin\) and \(\log_{10}(g/\cm\,\second^{-2})\) photospheric parameters, while lam is wavelength \(\lambda/\angstrom\).

Plotting the Flux

Let’s now evaluate and plot a flux spectrum (more precisely, an irradiance spectrum) for Sirius A. First, set up the star’s parameters:

[4]:
# Set Sirius A's fundamental parameters. Mass, radius & luminosity are from
# Liebert et al. (2005, ApJ, 630, 69), while parallax and radial velocity are from
# Simbad

M = 2.02*M_SUN
R = 1.71*R_SUN
L = 25.4*L_SUN

p = 0.379
v = -5.50e5

d = 1/p * PC
z = v/C

# Store photospheric parameters in a dict

Teff = ((L/(4*np.pi*R**2*SIGMA))**0.25)
logg = np.log10(G*M/R**2)

x = {'Teff': Teff, 'log(g)': logg}

Then create a uniformly-spaced wavelength abscissa for a spectrum spanning the visible range, \(3800\,\angstrom\) to \(7000\,\angstrom\).

[5]:
# Set up the wavelength abscissa

lam_min = 3800
lam_max = 7000

lam = np.linspace(lam_min, lam_max, 501)
lam_c = 0.5*(lam[1:] + lam[:-1])

Here, the array lam defines the boundaries of 500 wavelength bins \(\{\lambda_{i},\lambda_{i+1}\}\) (\(i=1,\ldots,500\)), and lam_c defines the central wavelength of each bin.

With all our parameters defined, evaluate the elemental flux using a call to the pymsg.SpecGrid.flux() method, and convert it into an irradiance using equation (8) from the MSG Fundamentals chapter:

[6]:
# Evaluate the elemental flux and irradiance

F_lam = specgrid.flux(x, z, lam)
F_lam_obs = (R/d)**2*F_lam

# Plot the irradiance spectrum

plt.figure()

plt.plot(lam_c, F_lam_obs)

plt.xlabel(r'$\lambda ({\AA})$')
plt.ylabel(r'$F^{\mathrm{obs}}_{\lambda}\ ({\rm erg\,cm^{-2}\,s^{-1}}\,\AA^{-1})$');
../../_images/user-guide_walkthroughs_python-walkthrough_11_0.png

The plot looks as expected — we can clearly see the Balmer series, starting with H\(\alpha\) \(\lambda6563\,\angstrom\).

Plotting the Intensity

Sometimes we want to know the specific intensity of the radiation emerging from an element of a star’s photosphere. For this, we can use the pymsg.SpecGrid.intensity() method.

Here’s a demonstration of this method in action, plotting the specific intensity across the H\(\alpha\) line profile for ten different values of the angle parameter \(\mu=0.1,0.2,\ldots,1.0\).

[7]:
# Set up the wavelength abscissa

lam_min = 6300.
lam_max = 6800.

lam_H = np.linspace(lam_min, lam_max, 100)
lam_H_c = 0.5*(lam_H[1:] + lam_H[:-1])

# Loop over mu, plotting the intensity spectra

plt.figure()

for mu in np.linspace(1.0, 0.1, 10):

    # Evaluate the intensity

    I_lam = specgrid.intensity(x, mu, z, lam_H)

    if mu==0.1 or mu==1.0:
        label=r'$\mu={:3.1f}$'.format(mu)
    else:
        label=None

    plt.plot(lam_H_c, I_lam, label=label)

plt.xlabel(r'$\lambda ({\AA})$')
plt.ylabel(r'$I_{\lambda}\ ({\rm erg\,cm^{-2}\,s^{-1}}\,\AA^{-1}\,sr^{-1})$')

plt.legend();
../../_images/user-guide_walkthroughs_python-walkthrough_13_0.png

We can clearly see that limb-darkening in the line core is much weaker than in the continuum, as we exact for such a strong line.

Evaluating Magnitudes & Colors

As a final step in this walkthrough, let’s evaluate the magnitude and colors of Sirius A in the UBV photometric system. We can do this by creating a new pymsg.PhotGrid object for each passband:

[8]:
# Create the PhotGrid objects

PASS_DIR = os.path.join(MSG_DIR, 'data', 'passbands')
filters = ['U', 'B', 'V']

photgrids = {}

for filter in filters:
    passband_file_name = os.path.join(PASS_DIR, f'pb-Generic-Johnson.{filter}-Vega.h5')
    photgrids[filter] = pymsg.PhotGrid(specgrid_file_name, passband_file_name)

(for convenience, we store the pymsg.PhotGrid objects in a dict, keyed by filter name). In the calls to the object constructor pymsg.PhotGrid(), the first argument is the name of a specgrid file (here, the demo grid), and the second argument is the name of a passband file; a limited set of these files is provided in the $MSG_DIR/data/passbands subdirectory[3]. The photometric irradiances of Sirius A are then found using the pymsg.PhotGrid.flux() method:

[9]:
# Evaluate the photometric irradiances (each normalized to the passband
# zero-point flux)

F_obs = {}

for filter in filters:
    F = photgrids[filter].flux(x)
    F_obs[filter] = (R/d)**2*F

To convert these into apparent magnitudes, we can apply Pogson’s logarithmic formula:

[10]:
# Evaluate apparent magnitudes and print out magnitude & colors

mags = {}

for filter in filters:
    mags[filter] = -2.5*np.log10(F_obs[filter])

print(f"V={mags['V']}, U-B={mags['U']-mags['B']}, B-V={mags['B']-mags['V']}")
V=-1.446677985580499, U-B=-0.059646818350374886, B-V=0.00112193455961207

Reassuringly, the resulting values are within 10 millimag of Sirius A’s apparent V-band magnitude and colors, as given by Simbad.

Footnotes