AST4310 Guidelines for Notebook Assignments

This notebook contains information about how to submit assignments in Jupyter notebooks for the two mandatory assignments of AST4310. It is recommended that you read it carefully to ensure your hard work receives a just grade. Jupyter notebooks offer unique demonstration capabilities combining code and written justifications, but at the same time also allow a great amount of freedom to code and write. If used improperly, they can make it difficult to grade. The point of these assignments is not only to help you learn the subject matter, but also to introduce you to the writing and presenting norms followed in astrophysics, so you will be judged also on the clarity of your written justifications, on your source code, and on the appropriateness of your plots.

Most of the points in this document are suggestions or guidelines, not rules. There is only one important rule: the code cells must run sequentially without any errors (warnings are ok). The first thing the examiners will do is to run all cells of your notebook (menu "Run" > "Run All Cells"). If a code cell gives an error, scoring on that question will be seriously affected. It is better not to include incomplete code than code with errors.

Libraries and imports

Use only standard libraries that are widely used in astrophysics. If you use an obscure library that is not available on the computers at the Institute of Theoretical Astrophysics, your code will give an error, parts (or all) of your assignment will not run, and your grade can be significantly affected.

The safe libraries to use are numpy, scipy, matplotlib, astropy, and libraries related to IPython/Jupyter/Jupyterlab. If you really want to use another library and are unsure if it's available, either ask the lecturers or try importing it at a linux machine from the Institute.

It is also a good practice to keep all your imports and libraries as one of the first cells in your notebook. The templates for the assignments already have this, so please don't change the order of these cells. You can modify them and/or add additional imports. Keeping them in one place makes your code tidy and easier to read. For example:

In [12]:
%matplotlib inline

# Make inline plots vector graphics instead of raster graphics
from IPython.display import set_matplotlib_formats
set_matplotlib_formats('svg')

import numpy
import matplotlib.pyplot as plt
from astropy import units
from astropy import constants
from astropy.modeling.blackbody import blackbody_lambda

# Plot defaults and quantity support
from astropy.visualization import quantity_support
quantity_support()
plt.rc('legend', frameon=False)
plt.rc('figure', figsize=(7, 7 / 1.75)) # Larger figure sizes
plt.rc('font', size=12)

Notebook Delivery

The assignements will be handed using devilry. In addition to the notebook file, you should upload also all necessary files to run or notebook, or additional files you created along your notebook (e.g. a data file with the results of a long calculation). Loading data files or code from the internet is not allowed.

Answering Questions: Being Concise and to the Point

Some questions will be more specific, while others are more open-ended. However, in both cases it is very important that your answer is concise and to the point. If you write an extensive amount of text that goes well beyond the question, or presents multiple possible explanations, your point is being diluted, and you are not demonstrating a good understanding of the subject. This applies both to written questions and code! Long blocks of code with unnecessary or unused calls will be penalised.

Here are some examples of good and bad practices:

Example question 1

  • The effective temperature of Procyon is $T_{\mathrm{eff}} = 4000~K$. Assuming it radiates isotropically as a blackbody, how much of its flux is in the infrared (consider the infrared as going from 0.7 to 20 $\mu$m)?

Example answer 1 (BAD)

Procyon is a star in the XXX constellation. It is one of the brightests stars in the night sky, having been already discovered by the Greeks. With an effective temperature of $T_{\mathrm{eff}} = 4000~K$, its spectral type is G2V and sits on the main sequence. Its colours are UBV = 111. A blackbody is a body in temperature equilibrium, where the intensity $I_\nu$ is equal to the source function $S_\nu$ and, by definition, to the Planck function $B_\nu$:

\begin{equation} I_\nu = S_\nu = B_\nu \end{equation}

The Planck function is given by:

\begin{equation} B_\nu(T) = \frac{2h\nu^3}{c^2} \frac{1}{\mathrm{e}^{h\nu/kT}-1} \end{equation}

In the costumary frequency scale, with units of [W m$^{-2}$ Hz$^{-1}$ sr$^{-1}$] and by

\begin{equation} B_\lambda(T) = \frac{2hc^2}{\lambda^5} \frac{1}{\mathrm{e}^{hc/\lambda kT}-1} \label{eq:5.3a} \end{equation}

in a wavelength scale, with units of [W m$^{-2}$ nm$^{-1}$ sr$^{-1}$]. As $\nu \to 0$, $B_\nu \to 0$, and as $\lambda \to \infty$, $B_\lambda \to \infty$. The Planck function can be divided in two regimes (two sides of the function, divided by the peak, which are the Wien (shorter wavelengths) and Rayleigh-Jeans (longer wavelength). In this exercise we are working with infrared fluxes, so we could use the Rayleigh-Jeans approximation although there is really no need to do it since it is not difficult to calculate the Planck function in python by hand, which is what we do below in the following code cell. From the infrared many wavelengths are blocked by the Earth's atmosphere due to atmospheric absorption in the atmosphere, which blocks photons coming to the surface, where we have our telescopes. Therefore, we need to use space telescopes. But in this exercise we'll assume that we can observe all that radiation:

In [57]:
h=6.62607004e-34
c=299792458.0
k=1.38064852e-23


print(h,c,k)

def b(ll, t):
    l=ll
    a1=2*h*(c*1e6)**2/(l*l*l*l*l)
    n=c/l
    a2=2*h*(c*1e6)**2*n**3/(c*1e6)**2
    t2=1/(numpy.exp(h*n/k/T)-1)
    t1=1/(numpy.exp(h*c*1e6/k/T/l)-1)
    
    ####
    #if name == 'freq':
    #    print(a2*t2)
    #    planck=a2*t2
    #    return planck
    #else:
    my_planck=a1*t1
    return my_planck

ww=numpy.arange(0.1, 1000)*40/1000#in um
pl=[]
T=4000

for i in range(len(ww)):
    #print(i)
    pl.append(b(ww[i],t=T))

plt.plot(ww,pl)
#plt.plot(ww,pl,'r-')
plt.show()

# Flux in infrared?
s=0
st=0
for i in range(len(ww)):
    #print(type(i)) #have correct b?
    st+=pl[i]/(ww[1]-ww[0])
    if ww[i] > 0.7:
        if ww[i] < 20:
            s+=pl[i]/(ww[1]-ww[0])
    else:
        continue

print(st, s)
6.62607004e-34 299792458.0 1.38064852e-23
/Users/tiago/miniconda3/lib/python3.7/site-packages/ipykernel_launcher.py:14: RuntimeWarning: overflow encountered in exp
  
0.002887789784487586 0.0022185409818091598

As is shown, the flux in the infrared is very small. We obtained the value 0.0022185409818091598 assuming wavelengths are given in $\mu$m. This is for the Planck function, only, so we multiply by $\pi$ which gives 0.006969752050139543.


Example Answer 2 (GOOD)

If a body radiates as a black body, its radiation (per wavelength) is given by the Planck function:

\begin{equation} I_\lambda \equiv B_\lambda(T) = \frac{2hc^2}{\lambda^5} \frac{1}{\mathrm{e}^{hc/\lambda kT}-1} \end{equation}

If the body radiates isotropically, then its outgoing flux $\mathcal{F}^+_\lambda$ is given by

\begin{equation} \mathcal{F_\lambda^+} = \pi B_\lambda(T) \end{equation}

Using the blackbody_lambda function from astropy, we can calculate the flux for a few wavelengths of interest:

In [61]:
wavelength = numpy.linspace(100 * units.nm, 40 * units.um, 1000)
temperature = 4000 * units.K
flux = blackbody_lambda(wavelength, temperature).to('kW nm-1 m-2 sr-1') * (numpy.pi * units.sr)
fig, ax = plt.subplots()
ax.plot(wavelength, flux)
ax.set_xlabel(r"Wavelength ($\mu$m)")
ax.set_ylabel(r"Flux (kW nm$^{-1}$ m$^{-2}$)");

The flux in the infrared is simply the integrating the flux in wavelength:

In [47]:
infrared = (wavelength > 0.7 * units.um) & (wavelength < 20 * units.um)
infrared_flux = numpy.trapz(flux[infrared], x=wavelength[infrared])
total_flux = numpy.trapz(flux, x=wavelength)

The infrared flux is therefore:

In [48]:
infrared_flux.to('MW m-2')
Out[48]:
$10.690236 \; \mathrm{\frac{MW}{m^{2}}}$

Which means that the fraction of the flux of Procyon coming in the infrared is:

In [49]:
infrared_flux / total_flux
Out[49]:
$0.73646457 \; \mathrm{}$

Minimum Working Code and Relating to Text

You should write as little code as necessary to answer the questions. Commented or unused code, especially large blocks, is a distraction and should be completely removed. Every line of code you write must have a function and be useful to answer the question. There are often many ways to solve a problem computationally; some approaches may use a lot less lines of code and even be computationally more efficient. While it is welcome, the efficiency of the code will not be graded here. Similarly, you will not loose points if your approach takes more lines of code than the usual -- as long as all code lines are needed.

The BAD example to Example Question 1 suffers from many problems. You will see several stray lines of code, parts that are not used, intermediate prints that only clutter the end result, and other things that are incorrect and should not be used. You also notice a certain disconnect between the code and the written answer. The code is written on a single large cell, and results from the output are copied into the text. This is bad because if your computation changes, you need to update the values in two locations, which increases the chances of you forgetting and having the text inconsistent with the code. It is better to demonstrate with smaller code cells.

It is also easier (for you and for the examiners) if the variable names are understandable and the code is logically structured. Adhering to PEP 8 is recommended, although it will not give additional points.