# Experiment 5: Air Track III---Conservation of Energy
### Objectives
- To learn to take good measurements
- To learn how to estimate and propagate errors
- To learn how to take measurements to verify a theory
- To learn how to measure potential and kinetic energies

### Equipment
- One Airtrack, Blower, and Cart
- One accessory kit containing: one pulley, one mass hanger, and masses for cart and hanger.
- One string (150 cm long)
- One photogate
- One 30 cm ruler
- One scale
- One vernier caliper
- One two-meter stick
- One 3” x 5/32” rod
- One 6” x 5/32” rod
- One 6 mm x 44 mm spring
- One flat washer
- One 2x4
- One 1x4
- One set of slotted weights: (Eight 100g and four 50g)

### Safety
- **Be careful placing the carts on the track. Do not damage the track.**
- Please place paper underneath the carts when they are resting on the track without the air turned on. **Do not slide the carts on the track without the air turned on.**
- **Do not launch the cart unless a rubber-band bumper is secured to the opposite end of the track.**

# Introduction
There are two kinds of energy in mechanical systems: Potential energy and Kinetic energy. *Potential* energy is stored energy. When this energy is released, it can be converted into *kinetic* energy---energy of motion. In this lab you will conduct two experiments to determine if all of the potential energy stored in a system can be converted into kinetic energy.

In the first experiment, you will convert gravitational potential energy to kinetic energy (using the hanging mass and pulley). In the second, you will convert spring potential energy to kinetic energy.

# Theory
### Kinematics
First and foremost, you will need to compute the velocity of the aitrack glider. The information you will have, however, is velocity computed by a photogate. The photogate measures the
velocity of the glider as it passes through by timing how long the infrared LED is blocked by the object (sometimes called a “flag”) passing through and uses the definition of velocity:
\begin{equation}
v \equiv \frac{\Delta x}{\Delta t}
\tag{1}
\end{equation}

where $\Delta x$ is the length of the flag and $\Delta t$ is the time the LED was blocked.

### Gravitational Potential Energy
Recall that the amount of energy stored in an object in a gravitational field is

\begin{equation}
U_{g} = m g \Delta y
\tag{2}
\end{equation}

where *m* is the mass of the object, *g* is the gravitational constant, and $ \Delta y$ is the change in height of that object.

### Spring Potential Energy
The amount of energy stored in a compressed spring is

\begin{equation}
U_{s} = \frac{1}{2} k (\Delta x)^2
\tag{3}
\end{equation}

where *k* is the spring constant. You will need to measure this spring constant for your spring. You can find this constant by recalling Hooke’s Law:

\begin{equation}
F = -k \Delta x
\tag{4}
\end{equation}

The force you apply to the spring is directly related to the compression, $ \Delta x$, of that spring (the negative sign is a reminder that the force is opposes the spring compression). By plotting the force vs. compression for different weights, you can plot a straight line. The slope of that straight line will be the spring constant (if you have plotted the correct variable on the correct axis---recall the definition of the slope of a straight line and you’ll figure it out).

### Kinetic Energy
Kinetic energy is the energy of motion. For objects moving much slower than the speed of light, we can use the formula:

\begin{equation}
K = \frac{1}{2} m v^2
\tag{5}
\end{equation}

where *m* is the mass of all the objects moving at speed v.

### Uncertainty Analysis
Your Lab Manual and the previous labs can guide you in estimating the uncertainties in your measurements and propagating those uncertainties into your computed energies. The uncertainty in the masses of the glider and hanging mass will be determined by the accuracy of the scale. In some cases, it might be easier to use the standard deviation of a large number of measurements as your velocity measurement uncertainty. The uncertainty in your spring constant and kinetic and potential energies, however, will have to be computed using the three equations in your Lab Manual.

In order to minimize random errors, it is extremely important that each of your measurements be performed several times.

# Experimental Procedure

### Setting up the Air Track
Ensure that the pulley is securely inserted into the top hole in the bracket at the far end of the track, that it spins freely, and that the hanging mass does not strike the table as it falls. You will need to level the airtrack for two of the experiments. Look back to the second lab for instructions if you’ve forgotten how to do this.

### Setting up a Photogate
1. Turn on the PASCO 850 Interface and start the PASCO Capstone software.
2. Plug a photogate into a Digital input.
3. Click the “Hardware Setup” tab in the left “Tools” palette, left-click the jack on the diagram where you inserted the plug, and select “Photogate” from the drop-down menu.
4. You should see a tab labeled “Timer Setup.” Open that tab and set up a pre-configured timer:
    1. Select the photogate you just installed.
    2. You will be using this photogate with a single flag.
    3. The computer need only keep track of the speed through the gate.
    4. A text box requesting the length of the flag in meters will appear. Measure the flag as best you can and enter that information in the box.
    5. Give this sensor a name such as “Photogate 1” or similar.

```python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from P201_Functions import *

# Flag length measurement (m) and its uncertainty
flag_len = 0.1
delta_x = 0.005
```

When you adjust the height of the photogate, make sure that it is triggered only by the flag. The string should not trigger the photogate. You can tell when the photogate is triggered by looking for the red LED to light when the infrared LED is blocked. Ensure that the red LED only lights when the proper portion of the cart passes through.

When choosing the positions for your photogate, think carefully about whether you want the cart to be coasting through the gate or experiencing a force as it travels through the gate. Also keep in mind that the airtrack only *reduces* friction, it does not eliminate it.

### Measuring the Spring Constant
It will be easiest to measure the compression of the spring while it is on a long rod. This will keep the spring from bending while weights are applied to the spring. Place one end of the rod on the table, then place the spring on the rod, and then place a washer on top of the spring. You should then be able to measure the compression of the spring as a function of the masses you apply. A series of ten 100 g weights should give you a good graph. (It might be instructive to start with ten 10 g weights before applying the remaining nine 100g weights to see if the spring constant is truly linear.)

**Don’t forget to include the mass of the washer and do not exceed 1.1 kg of mass on the spring!**

(Ask your instructor how to remove and use one of the end-brackets of the air-track to support the bottom portion of the rod if that would help make your compression measurements easier).

You must use PASCO Capstone to create the graph of "Weight vs. Compression":
1. Start the Capstone program and click “Table & Graph” in the main window.
2. In the first table column, click `<Select Measurement>` and then select “Create New” “User-Entered Data” in the cascading menus.
3. Rename the column either “Weight” or “Measured Compression” when the “User Data 1/2” title is highlighted in blue and put the appropriate units (“N” or “m”).
4. In the graph, you can then click on the `<Select Measurement>` button on each axis and select the data you would like on that axis.

**For reasons you will soon discover, plot the *compression* on the x-axis.** You will need to print
the graphs you produce for each team member. **Be sure to properly label your graphs!**

```python
# Weight vs. Compression Graph

# Reads the name of the csv file and gets the data
df = pd.read_csv("./ExampleFiles/Spring Constant.csv")

# Prints information about the file
#df.info()
print(df)
print()

# Defines the x and y values
weight = df.filter(['Weight (N)'], axis=1).dropna()
compr = df.filter(['Compression (m)'], axis=1).dropna()

# Create a figure of reasonable size and resolution, white background, black edge color
fig=plt.figure(figsize=(7,5), dpi= 100, facecolor='w', edgecolor='k')

# Gets the data values for x and y
x_data = compr.values.reshape(-1, 1)
y_data = weight.values.reshape(-1, 1)

xi = df['Compression (m)'].to_numpy()
yi = df['Weight (N)'].to_numpy()

# Creates the base plot with titles
plt.plot(x_data,y_data,'b.',label='Raw Data')
plt.ylabel('Weight (N)')
plt.xlabel('Measured Compression (m)')
plt.title('Weight vs. Compression')

# Takes the x and y values to make a trendline
#intercept, slope = linear_fit_plot(x_data,y_data)
intercept, slope, dintercept, dslope = linear_fit_plot_errors(xi,yi,0.006,0.024)

# Adds the legend to the plot
plt.legend()

# Displays the plot
plt.show()

print()
```

## Experiment 1: Gravitational Potential Energy vs. Kinetic Energy
For this experiment, repeat the set up from the last lab. You will be comparing the initial potential energy of the mass hanger to **the sum of** the final kinetic energies of both the cart and the mass hanger (the instant before it strikes the ground). Do four trials (for statistics) for four different combinations of masses (the hanger mass must change each time).

Where should the photogate be placed for this experiment?

```python
# Experiment 1 Raw Data
# Cart 1

# Create an empty numpy array to hold the raw data
raw_data_1 = np.empty((4,2))

# Set the trial number column identifiers for each Trial
raw_data_1[0][0]=1
raw_data_1[1][0]=2
raw_data_1[2][0]=3
raw_data_1[3][0]=4

# Create a Pandas dataframe, and convert the Trial number column to integer format
df1 = pd.DataFrame(raw_data_1, columns=["Trial",
                                        "Measured v_f (m/s)"])

df1['Trial'] = df1['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of the cart (kg) and its uncertainty
m_cart = 0.18995
delta_m_cart = 5e-06

# Mass of hanging mass (kg) and its uncertainty
m_hang = 0.01175
delta_m_hang = 5e-06

# Height of the spring (m) and its uncertainty
h = 0.952
delta_h = 0.0005

# Predicted Final Velocity (m/s) and its uncertainty
v_p = 1.028
delta_v_p = 0.0007081

# Enter the measured values of v_f (m/s)
df1['Measured v_f (m/s)'] = [0.98,0.98,0.97,0.99]

###########################################

# calculates the mass of the cart with the mass of the hanger and its uncertainty
m_total = m_cart + m_hang
delta_m_total = np.sqrt(2) * delta_m_cart

# Calculates the uncertainty of the velocity using standard deviation
uncertainty_v = np.std(df1['Measured v_f (m/s)'])

# prints out the cart data
print("Data for: ")
print("m_cart = %0.5f kg              𝛿m_cart = %g" % (m_cart, delta_m_cart))
print("m_hang = %0.5f kg              𝛿m_hang = %g" % (m_hang,delta_m_hang))
print("h = %0.3f m                      𝛿h = %0.4f" % (h,delta_h))
print("Predicted v_f = %0.5f m/s      𝛿v_p_f = %0.4f" % (v_p,delta_v_p))
print("")

# Display the dataframe
from IPython.display import display
print ("Cart 1")
display(df1)
# Print statements for uncertainty of final velocity
print("𝛿v = %0.5f m/s" % (uncertainty_v))
```

    Data for:
    m_cart = 0.18995 kg              𝛿m_cart = 5e-06
    m_hang = 0.01175 kg              𝛿m_hang = 5e-06
    h = 0.952 m                      𝛿h = 0.0005
    Predicted v_f = 1.02800 m/s      𝛿v_p_f = 0.0007

    Cart 1

      Trial
      Measured v_f (m/s)

      0
      1
      0.98

      1
      2
      0.98

      2
      3
      0.97

      3
      4
      0.99

    𝛿v = 0.00707 m/s

***

```python
# Experiment 1 Raw Data
# Cart 2

# Create an empty numpy array to hold the raw data
raw_data_2 = np.empty((4,2))

# Set the trial number column identifiers for each Trial
raw_data_2[0][0]=1
raw_data_2[1][0]=2
raw_data_2[2][0]=3
raw_data_2[3][0]=4

# Create a Pandas dataframe, and convert the Trial number column to integer format
df2 = pd.DataFrame(raw_data_2, columns=["Trial",
                                        "Measured v_f (m/s)"])

df2['Trial'] = df2['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of the cart (kg) and its uncertainty
m_cart = 0.18995
delta_m_cart = 5e-06

# Mass of hanging mass (kg) and its uncertainty
m_hang = 0.01665
delta_m_hang = 5e-06

# Height of the spring (m) and its uncertainty
h = 0.952
delta_h = 0.0005

# Predicted Final Velocity (m/s) and its uncertainty
v_p = 1.209
delta_v_p = 0.0007081

# Enter the measured values of v_f (m/s)
df2['Measured v_f (m/s)'] = [1.17,1.18,1.17,1.16]

###########################################

# calculates the mass of the cart with the mass of the hanger and its uncertainty
m_total = m_cart + m_hang
delta_m_total = np.sqrt(2) * delta_m_cart

# Calculates the uncertainty of the velocity using standard deviation
uncertainty_v = np.std(df2['Measured v_f (m/s)'])

# prints out the cart data
print("Data for: ")
print("m_cart = %0.5f kg              𝛿m_cart = %g" % (m_cart, delta_m_cart))
print("m_hang = %0.5f kg              𝛿m_hang = %g" % (m_hang,delta_m_hang))
print("h = %0.3f m                      𝛿h = %0.4f" % (h,delta_h))
print("Predicted v_f = %0.5f m/s      𝛿v_p_f = %0.4f" % (v_p,delta_v_p))
print("")

# Display the dataframe
from IPython.display import display
print ("Cart 2")
display(df2)
# Print statements for uncertainty of final velocity
print("𝛿v = %0.5f m/s" % (uncertainty_v))
```

    Data for:
    m_cart = 0.18995 kg              𝛿m_cart = 5e-06
    m_hang = 0.01665 kg              𝛿m_hang = 5e-06
    h = 0.952 m                      𝛿h = 0.0005
    Predicted v_f = 1.20900 m/s      𝛿v_p_f = 0.0007

    Cart 2

      Trial
      Measured v_f (m/s)

      0
      1
      1.17

      1
      2
      1.18

      2
      3
      1.17

      3
      4
      1.16

    𝛿v = 0.00707 m/s

***

```python
# Experiment 1 Raw Data
# Cart 3

# Create an empty numpy array to hold the raw data
raw_data_3 = np.empty((4,2))

# Set the trial number column identifiers for each Trial
raw_data_3[0][0]=1
raw_data_3[1][0]=2
raw_data_3[2][0]=3
raw_data_3[3][0]=4

# Create a Pandas dataframe, and convert the Trial number column to integer format
df3 = pd.DataFrame(raw_data_3, columns=["Trial",
                                        "Measured v_f (m/s)"])

df3['Trial'] = df3['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of the cart (kg) and its uncertainty
m_cart = 0.18995
delta_m_cart = 5e-06

# Mass of hanging mass (kg) and its uncertainty
m_hang = 0.00965
delta_m_hang = 5e-06

# Height of the spring (m) and its uncertainty
h = 0.820
delta_h = 0.0005

# Predicted Final Velocity (m/s) and its uncertainty
v_p = 0.9367
delta_v_p = 0.0007081

# Enter the measured values of v_f (m/s)
df3['Measured v_f (m/s)'] = [0.83,0.85,0.82,0.83]

###########################################

# calculates the mass of the cart with the mass of the hanger and its uncertainty
m_total = m_cart + m_hang
delta_m_total = np.sqrt(2) * delta_m_cart

# Calculates the uncertainty of the velocity using standard deviation
uncertainty_v = np.std(df3['Measured v_f (m/s)'])

# prints out the cart data
print("Data for: ")
print("m_cart = %0.5f kg              𝛿m_cart = %g" % (m_cart, delta_m_cart))
print("m_hang = %0.5f kg              𝛿m_hang = %g" % (m_hang,delta_m_hang))
print("h = %0.3f m                      𝛿h = %0.4f" % (h,delta_h))
print("Predicted v_f = %0.5f m/s      𝛿v_p_f = %0.4f" % (v_p,delta_v_p))
print("")

# Display the dataframe
from IPython.display import display
print ("Cart 3")
display(df3)
# Print statements for uncertainty of final velocity
print("𝛿v = %0.5f m/s" % (uncertainty_v))
```

    Data for:
    m_cart = 0.18995 kg              𝛿m_cart = 5e-06
    m_hang = 0.00965 kg              𝛿m_hang = 5e-06
    h = 0.820 m                      𝛿h = 0.0005
    Predicted v_f = 0.93670 m/s      𝛿v_p_f = 0.0007

    Cart 3

      Trial
      Measured v_f (m/s)

      0
      1
      0.83

      1
      2
      0.85

      2
      3
      0.82

      3
      4
      0.83

    𝛿v = 0.01090 m/s

***

```python
# Experiment 1 Raw Data
# Cart 4

# Create an empty numpy array to hold the raw data
raw_data_4 = np.empty((4,2))

# Set the trial number column identifiers for each Trial
raw_data_4[0][0]=1
raw_data_4[1][0]=2
raw_data_4[2][0]=3
raw_data_4[3][0]=4

# Create a Pandas dataframe, and convert the Trial number column to integer format
df4 = pd.DataFrame(raw_data_4, columns=["Trial",
                                        "Measured v_f (m/s)"])

df4['Trial'] = df4['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of the cart (kg) and its uncertainty
m_cart = 0.18995
delta_m_cart = 5e-06

# Mass of hanging mass (kg) and its uncertainty
m_hang = 0.01000
delta_m_hang = 5e-06

# Height of the spring (m) and its uncertainty
h = 0.95
delta_h = 0.0005

# Predicted Final Velocity (m/s) and its uncertainty
v_p = 0.9666
delta_v_p = 0.0007081

# Enter the measured values of v_f (m/s)
df4['Measured v_f (m/s)'] = [0.93,0.93,0.92,0.94]

###########################################

# calculates the mass of the cart with the mass of the hanger and its uncertainty
m_total = m_cart + m_hang
delta_m_total = np.sqrt(2) * delta_m_cart

# Calculates the uncertainty of the velocity using standard deviation
uncertainty_v = np.std(df4['Measured v_f (m/s)'])

# prints out the cart data
print("Data for: ")
print("m_cart = %0.5f kg              𝛿m_cart = %g" % (m_cart, delta_m_cart))
print("m_hang = %0.5f kg              𝛿m_hang = %g" % (m_hang,delta_m_hang))
print("h = %0.3f m                      𝛿h = %0.4f" % (h,delta_h))
print("Predicted v_f = %0.5f m/s      𝛿v_p_f = %0.4f" % (v_p,delta_v_p))
print("")

# Display the dataframe
from IPython.display import display
print ("Cart 4")
display(df4)
# Print statements for uncertainty of final velocity
print("𝛿v = %0.5f m/s" % (uncertainty_v))
```

    Data for:
    m_cart = 0.18995 kg              𝛿m_cart = 5e-06
    m_hang = 0.01000 kg              𝛿m_hang = 5e-06
    h = 0.950 m                      𝛿h = 0.0005
    Predicted v_f = 0.96660 m/s      𝛿v_p_f = 0.0007

    Cart 4

      Trial
      Measured v_f (m/s)

      0
      1
      0.93

      1
      2
      0.93

      2
      3
      0.92

      3
      4
      0.94

    𝛿v = 0.00707 m/s

***

```python
# Initial Potential Energy vs. Final Kinetic Energy Plot

# Reads the name of the csv file and gets the data
df = pd.read_csv("./ExampleFiles/Lab 5 Graphs - PEi vs KEf.csv")

# Prints information about the file
#df.info()
print(df)
print()

# Defines the x and y values
pe = df.filter(['Ug (J)'], axis=1).dropna()
ke = df.filter(['Kf (J)'], axis=1).dropna()

# Create a figure of reasonable size and resolution, white background, black edge color
fig=plt.figure(figsize=(7,5), dpi= 100, facecolor='w', edgecolor='k')

# Gets the data values for x and y
x_data = pe.values.reshape(-1, 1)
y_data = ke.values.reshape(-1, 1)

xi = df['Ug (J)'].to_numpy()
yi = df['Kf (J)'].to_numpy()

# Creates the base plot with titles
plt.plot(x_data,y_data,'b.',label='Raw Data')
#plt.errorbar(x_data,y_data,Delta_x,Delta_t,'b.',label='Raw Data')
plt.ylabel('Kinetic Energy (J)')
plt.xlabel('Gravitational Potential Energy(J)')
plt.title('Initial Potential Energy vs. Final Kinetic Energy')

# Takes the x and y values to make a trendline
intercept, slope, dintercept, dslope = linear_fit_plot_errors(xi,yi,0.07763,0.15120)

# Adds the legend to the plot
plt.legend()

# Displays the plot
plt.show()

print("")
```

***
***

## Experiment 2: Spring Potential Energy vs. Kinetic Energy
For this experiment, you will store potential energy in a compressed spring and launch the cart down the airtrack. You will need to use the 3” rod to support the spring to launch the cart (See the diagram below). The best way to launch the cart is to use a fingernail to hold the cart back on the compressed spring and then let the cart slip out from under your fingernail. You might devise a better method, but it is important to release the cart as quickly as possible.

**Do not fully compress the spring!** The last few millimeters are non-linear. But don’t be too gentle: at least compress the spring 1 cm.

You will need to perform four trial launches for each value of compression, $x_{s}$, that you determine is necessary (minimum of two). Consider carefully what your uncertainty, $ \delta x_{s}$, in compression is over each set of trials. This will be important in calculating your uncertainty in the stored potential energy.

Where should the photogate be placed for *this* experiment?

```python
# Experiment 2 Raw Data
# Cart 1

# Create an empty numpy array to hold the raw data
raw_data_5 = np.empty((4,2))

# Set the trial number column identifiers for each Trial
raw_data_5[0][0]=1
raw_data_5[1][0]=2
raw_data_5[2][0]=3
raw_data_5[3][0]=4

# Create a Pandas dataframe, and convert the Trial number column to integer format
df5 = pd.DataFrame(raw_data_5, columns=["Trial",
                                        "Measured v_f (m/s)"])

df5['Trial'] = df5['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of the cart (kg) and its uncertainty
m_cart = 0.18995
delta_m_cart = 5e-06

# Value of compression (m) and its uncertainty
x_s = 0.01
delta_xs = 0.005

# Predicted Final Velocity (m/s) and its uncertainty
v_p = 0.4104
delta_v_p = 0.2052

# Enter the measured values of v_f (m/s)
df5['Measured v_f (m/s)'] = [0.54,0.44,0.50,0.50]

###########################################

# Calculates the uncertainty of the velocity using standard deviation
uncertainty_v = np.std(df5['Measured v_f (m/s)'])

# prints out the cart data
print("Data for: ")
print("m_cart = %0.5f kg              𝛿m_cart = %g" % (m_cart, delta_m_cart))
print("x_s = %0.2f m                     𝛿x_s = %0.4f" % (x_s,delta_xs))
print("Predicted v_f = %0.5f m/s      𝛿v_p_f = %0.4f" % (v_p,delta_v_p))
print("")

# Display the dataframe
from IPython.display import display
print ("Cart 1")
display(df5)
# Print statements for uncertainty of final velocity
print("𝛿v = %0.5f m/s" % (uncertainty_v))
```

    Data for:
    m_cart = 0.18995 kg              𝛿m_cart = 5e-06
    x_s = 0.01 m                     𝛿x_s = 0.0050
    Predicted v_f = 0.41040 m/s      𝛿v_p_f = 0.2052

    Cart 1

      Trial
      Measured v_f (m/s)

      0
      1
      0.54

      1
      2
      0.44

      2
      3
      0.50

      3
      4
      0.50

    𝛿v = 0.03571 m/s

***

```python
# Experiment 2 Raw Data
# Cart 2

# Create an empty numpy array to hold the raw data
raw_data_6 = np.empty((4,2))

# Set the trial number column identifiers for each Trial
raw_data_6[0][0]=1
raw_data_6[1][0]=2
raw_data_6[2][0]=3
raw_data_6[3][0]=4

# Create a Pandas dataframe, and convert the Trial number column to integer format
df6 = pd.DataFrame(raw_data_6, columns=["Trial",
                                        "Measured v_f (m/s)"])

df6['Trial'] = df6['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of the cart (kg) and its uncertainty
m_cart = 0.18995
delta_m_cart = 5e-06

# Value of compression (m) and its uncertainty
x_s = 0.02
delta_xs = 0.005

# Predicted Final Velocity (m/s) and its uncertainty
v_p = 0.6739
delta_v_p = 0.16

# Enter the measured values of v_f (m/s)
df6['Measured v_f (m/s)'] = [0.82,0.81,0.83,0.82]

###########################################

# Calculates the uncertainty of the velocity using standard deviation
uncertainty_v = np.std(df6['Measured v_f (m/s)'])

# prints out the cart data
print("Data for: ")
print("m_cart = %0.5f kg              𝛿m_cart = %g" % (m_cart, delta_m_cart))
print("x_s = %0.2f m                     𝛿x_s = %0.4f" % (x_s,delta_xs))
print("Predicted v_f = %0.5f m/s      𝛿v_p_f = %0.4f" % (v_p,delta_v_p))
print("")

# Display the dataframe
from IPython.display import display
print ("Cart 2")
display(df6)
# Print statements for uncertainty of final velocity
print("𝛿v = %0.5f m/s" % (uncertainty_v))
```

    Data for:
    m_cart = 0.18995 kg              𝛿m_cart = 5e-06
    x_s = 0.02 m                     𝛿x_s = 0.0050
    Predicted v_f = 0.67390 m/s      𝛿v_p_f = 0.1600

    Cart 2

      Trial
      Measured v_f (m/s)

      0
      1
      0.82

      1
      2
      0.81

      2
      3
      0.83

      3
      4
      0.82

    𝛿v = 0.00707 m/s

***
***

## Experiment 3: Spring Potential Energy vs. Gravitational Potential Energy
**This experiment is optional.**

Remove the photogate and prop the leg on the far end of the airtrack (away from the spring launcher) on a wooden “shim” (a 2x4). By compressing the spring, you can launch the cart up the inclined airtrack. You will need to find an accurate method for determining the change in height between the starting and ending positions of the glider. Should you use a direct measurement (from a meter stick) or an indirect measurement (knowing the angle and measuring the distance the cart moved along the track)?

If you perform this experiment, you must record the data and describe it carefully in your report.

# Your Report

### Experimental Procedure
You will need to write a page or two describing how you set up your experiment and collected
your data. Specifically, you must mention:

- How did you measure the spring constant of your spring?
- Describe the setup you used to compare gravitational-potential and kinetic energies.
    - How did you ensure that the cart’s potential energy did not change?
    - How did you measure the height of your mass hanger and keep that height consistent between runs?
    - Where did you place your photogate and what did you use for the “flag?”
    - How did you compute the velocity of the mass hanger for its kinetic energy?
- Describe the setup you used to compare spring-potential and kinetic energies.
    - How did you measure the amount you compressed the spring? What was your uncertainty in that measurement?
    - Where did you place your photogate and what did you use for your “flag?”

Write it so that someone else could repeat your experiment by just reading this section. Talk
about any problems you encountered and how you handled them.

### Results and Conclusion
This will be your longest section. Use good paragraph structure to compare each experimental
result with each theoretical prediction:

- How do your initial and final energies compare for each experiment?
- Compute the spring constant for your spring given the results in the second experiment. How does this compare to the value you measured initially?
- Does your data support or refute Conservation of Mechanical Energy? What, if anything, might have interfered with perfect agreement?

As always, talk about your uncertainties, how they affected your data and how you might be able to reduce them. Make sure that you discuss all possible sources of uncertainties in your experiments.

### Appendix
*Include your signed raw data sheet(s) and sample calculations.*


- original: https://pytorch.org/tutorials/beginner/transformer_tutorial.html
- date: 2020-0807
- modified: Shin Asakawa <asakawa@ieee.org>

```python
# 2020年8月8日 JST 現在 colab にインストールされている torchtext のバージョンが 0.3.0 と少々古い
# update しないとエラーになるので，一旦このセルを実行して torchtext を最新版にした上で runtime を
# 再起動されたい。-- asakawa
!pip install --upgrade torchtext
```

    Collecting torchtext
      Downloading https://files.pythonhosted.org/packages/b9/f9/224b3893ab11d83d47fde357a7dcc75f00ba219f34f3d15e06fe4cb62e05/torchtext-0.7.0-cp36-cp36m-manylinux1_x86_64.whl (4.5MB)
         |████████████████████████████████| 4.5MB 4.7MB/s
    Requirement already satisfied, skipping upgrade: torch in /usr/local/lib/python3.6/dist-packages (from torchtext) (1.6.0+cu101)
    Requirement already satisfied, skipping upgrade: tqdm in /usr/local/lib/python3.6/dist-packages (from torchtext) (4.41.1)
    Collecting sentencepiece
      Downloading https://files.pythonhosted.org/packages/d4/a4/d0a884c4300004a78cca907a6ff9a5e9fe4f090f5d95ab341c53d28cbc58/sentencepiece-0.1.91-cp36-cp36m-manylinux1_x86_64.whl (1.1MB)
         |████████████████████████████████| 1.1MB 51.9MB/s
    Requirement already satisfied, skipping upgrade: numpy in /usr/local/lib/python3.6/dist-packages (from torchtext) (1.18.5)
    Requirement already satisfied, skipping upgrade: requests in /usr/local/lib/python3.6/dist-packages (from torchtext) (2.23.0)
    Requirement already satisfied, skipping upgrade: future in /usr/local/lib/python3.6/dist-packages (from torch->torchtext) (0.16.0)
    Requirement already satisfied, skipping upgrade: chardet<4,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests->torchtext) (3.0.4)
    Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests->torchtext) (2020.6.20)
    Requirement already satisfied, skipping upgrade: idna<3,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests->torchtext) (2.10)
    Requirement already satisfied, skipping upgrade: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests->torchtext) (1.24.3)
    Installing collected packages: sentencepiece, torchtext
      Found existing installation: torchtext 0.3.1
        Uninstalling torchtext-0.3.1:
          Successfully uninstalled torchtext-0.3.1
    Successfully installed sentencepiece-0.1.91 torchtext-0.7.0

```python
# from https://github.com/dmlc/xgboost/issues/1715
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
```

```python
%matplotlib inline
```

## ``nn.Transformer`` と ``TorchText`` を用いた Seq2Seq (系列-to-系列) モデル

このチュートリアルでは，[nn.Transformer](https://pytorch.org/docs/master/nn.html?highlight=nn%20transformer#torch.nn.Transformer) モジュールを用いた sequence-to-sequnce (訳注:日本語では `seq2seq モデル` などと呼ばれます) モデルの訓練方法を示します。

PyTorch リリース 1.2 には，[Attention is All You Need](https://arxiv.org/pdf/1706.03762.pdf) (訳注:初めてトランスフォーマーを提案した論文) に基づいた標準的なトランスフォーマーモジュールが含まれます。
トランスフォーマーは並列化が容易で，seq2seq モデルを凌ぐ性能が示されています。
``nn.Transfomer`` モジュールは，注意機構に基づいて，入出力情報間大域的依存性を解消する機構です
(最近の別実装は [nn.MultiheadAttention](https://pytorch.org/docs/master/nn.html?highlight=multiheadattention#torch.nn.MultiheadAttention))。
``nn.Transformer`` は単一要素で構成されており，本チュートリアル内の [nn.TransformerEncoder](https://pytorch.org/docs/master/nn.html?highlight=nn%20transformerencoder#torch.nn.TransformerEncoder) のごとく，修正，構成が容易です。

</div?

Define the model
----------------

In this tutorial, we train ``nn.TransformerEncoder`` model on a language modeling task.
The language modeling task is to assign a probability for the likelihood of a given word (or a sequence of words) to follow a sequence of words.
A sequence of tokens are passed to the embedding layer first, followed by a positional encoding layer to account for the order of the word (see the next paragraph for more details).
The  ``nn.TransformerEncoder`` consists of multiple layers of [nn.TransformerEncoderLayer](https://pytorch.org/docs/master/nn.html?highlight=transformerencoderlayer#torch.nn.TransformerEncoderLayer).
Along with the input sequence, a square attention mask is required because the self-attention layers in ``nn.TransformerEncoder`` are only allowed to attend the earlier positions in the sequence.
For the language modeling task, any tokens on the future positions should be masked. To have the actual words, the output of ``nn.TransformerEncoder`` model is sent to the final Linear layer, which is followed by a log-Softmax function.

```python
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

class TransformerModel(nn.Module):

    def __init__(self, ntoken, ninp, nhead, nhid, nlayers, dropout=0.5):
        super(TransformerModel, self).__init__()
        from torch.nn import TransformerEncoder, TransformerEncoderLayer
        self.model_type = 'Transformer'
        self.src_mask = None
        self.pos_encoder = PositionalEncoding(ninp, dropout)
        encoder_layers = TransformerEncoderLayer(ninp, nhead, nhid, dropout)
        self.transformer_encoder = TransformerEncoder(encoder_layers, nlayers)
        self.encoder = nn.Embedding(ntoken, ninp)
        self.ninp = ninp
        self.decoder = nn.Linear(ninp, ntoken)

        self.init_weights()

    def _generate_square_subsequent_mask(self, sz):
        mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
        mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
        return mask

    def init_weights(self):
        initrange = 0.1
        self.encoder.weight.data.uniform_(-initrange, initrange)
        self.decoder.bias.data.zero_()
        self.decoder.weight.data.uniform_(-initrange, initrange)

    def forward(self, src):
        if self.src_mask is None or self.src_mask.size(0) != len(src):
            device = src.device
            mask = self._generate_square_subsequent_mask(len(src)).to(device)
            self.src_mask = mask

        src = self.encoder(src) * math.sqrt(self.ninp)
        src = self.pos_encoder(src)
        output = self.transformer_encoder(src, self.src_mask)
        output = self.decoder(output)
        return output
```

``PositionalEncoding`` module injects some information about the
relative or absolute position of the tokens in the sequence. The
positional encodings have the same dimension as the embeddings so that
the two can be summed. Here, we use ``sine`` and ``cosine`` functions of
different frequencies.

### (訳注) Transformer: Attention is all you need

$$
\text{MultiHead}\left(Q,K,V\right)=\text{Concat}\left(\mathop{head}_1,\ldots,\mathop{head}_h\right)W^O
$$

where, $\text{head}_i =\text{Attention}\left(QW_i^Q,KW_i^K,VW_i^V\right)$

The projections are parameter matrices

- $W_i^Q\in\mathbb{R}^{d_{\mathop{model}}\times d_k}$,
- $W_i^K \in\mathbb{R}^{d_{\mathop{model}}\times d_k}$,
- $W_i^V\in\mathbb{R}^{d_{\mathop{model}}\times d_v}$,
- $W^O\in\mathbb{R}^{hd_v\times d_{\mathop{model}}}$. $h=8$
- $d_k=d_v=\frac{d_{\mathop{model}}}{h}=64$

$$\text{FFN}(x)=\max\left(0,xW_1+b_1\right)W_2+b_2$$

### (訳注) 位置符号器 Position encoders
トランスフォーマーの入力には，上述の単語表現に加えて，位置符号器からの信号も重ね合わされる。
位置 $i$ の信号は次式で周波数領域へと変換される:

$$
\begin{align}
\text{PE}_{(\text{pos},2i)} &= \sin\left(\frac{\text{pos}}{10000^{\frac{2i}{d_{\text{model}}}}}\right)\\
\text{PE}_{(\text{pos},2i+1)} &= \cos\left(\frac{\text{pos}}{10000^{\frac{2i}{d_{\text{model}}}}}\right)
\end{align}
$$

位置符号器による位置表現は，$i$ 番目の位置情報をワンホット表現するのではなく，周波数領域に変換することで周期情報を表現する試みと見なし得るだろう。

```python
class PositionalEncoding(nn.Module):

    def __init__(self, d_model, dropout=0.1, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)

        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer('pe', pe)

    def forward(self, x):
        x = x + self.pe[:x.size(0), :]
        return self.dropout(x)
```

Load and batch data
-------------------

The training process uses Wikitext-2 dataset from ``torchtext``. The
vocab object is built based on the train dataset and is used to numericalize
tokens into tensors. Starting from sequential data, the ``batchify()``
function arranges the dataset into columns, trimming off any tokens remaining
after the data has been divided into batches of size ``batch_size``.
For instance, with the alphabet as the sequence (total length of 26)
and a batch size of 4, we would divide the alphabet into 4 sequences of
length 6:

\begin{align}\begin{bmatrix}
  \text{A} & \text{B} & \text{C} & \ldots & \text{X} & \text{Y} & \text{Z}
  \end{bmatrix}
  \Rightarrow
  \begin{bmatrix}
  \begin{bmatrix}\text{A} \\ \text{B} \\ \text{C} \\ \text{D} \\ \text{E} \\ \text{F}\end{bmatrix} &
  \begin{bmatrix}\text{G} \\ \text{H} \\ \text{I} \\ \text{J} \\ \text{K} \\ \text{L}\end{bmatrix} &
  \begin{bmatrix}\text{M} \\ \text{N} \\ \text{O} \\ \text{P} \\ \text{Q} \\ \text{R}\end{bmatrix} &
  \begin{bmatrix}\text{S} \\ \text{T} \\ \text{U} \\ \text{V} \\ \text{W} \\ \text{X}\end{bmatrix}
  \end{bmatrix}\end{align}

These columns are treated as independent by the model, which means that
the dependence of ``G`` and ``F`` can not be learned, but allows more
efficient batch processing.

```python
import torchtext
from torchtext.data.utils import get_tokenizer
TEXT = torchtext.data.Field(tokenize=get_tokenizer("basic_english"),
                            init_token='<sos>',
                            eos_token='<eos>',
                            lower=True)
train_txt, val_txt, test_txt = torchtext.datasets.WikiText2.splits(TEXT)
TEXT.build_vocab(train_txt)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

def batchify(data, bsz):
    data = TEXT.numericalize([data.examples[0].text])
    # Divide the dataset into bsz parts.
    nbatch = data.size(0) // bsz
    # Trim off any extra elements that wouldn't cleanly fit (remainders).
    data = data.narrow(0, 0, nbatch * bsz)
    # Evenly divide the data across the bsz batches.
    data = data.view(bsz, -1).t().contiguous()
    return data.to(device)

batch_size = 20
eval_batch_size = 10
train_data = batchify(train_txt, batch_size)
val_data = batchify(val_txt, eval_batch_size)
test_data = batchify(test_txt, eval_batch_size)
```

    /usr/local/lib/python3.6/dist-packages/torchtext/data/field.py:150: UserWarning: Field class will be retired in the 0.8.0 release and moved to torchtext.legacy. Please see 0.7.0 release notes for further information.
      warnings.warn('{} class will be retired in the 0.8.0 release and moved to torchtext.legacy. Please see 0.7.0 release notes for further information.'.format(self.__class__.__name__), UserWarning)

    downloading wikitext-2-v1.zip

    wikitext-2-v1.zip: 100%|██████████| 4.48M/4.48M [00:00<00:00, 8.92MB/s]

    extracting

    /usr/local/lib/python3.6/dist-packages/torchtext/data/example.py:78: UserWarning: Example class will be retired in the 0.8.0 release and moved to torchtext.legacy. Please see 0.7.0 release notes for further information.
      warnings.warn('Example class will be retired in the 0.8.0 release and moved to torchtext.legacy. Please see 0.7.0 release notes for further information.', UserWarning)

```python
torchtext.__version__  # 0.3.1  --> 0.7.0
```

    '0.7.0'

### Functions to generate input and target sequence

``get_batch()`` function generates the input and target sequence for
the transformer model. It subdivides the source data into chunks of
length ``bptt``. For the language modeling task, the model needs the
following words as ``Target``. For example, with a ``bptt`` value of 2,
we’d get the following two Variables for ``i`` = 0:

It should be noted that the chunks are along dimension 0, consistent
with the ``S`` dimension in the Transformer model. The batch dimension
``N`` is along dimension 1.

```python
bptt = 35
def get_batch(source, i):
    seq_len = min(bptt, len(source) - 1 - i)
    data = source[i:i+seq_len]
    target = source[i+1:i+1+seq_len].view(-1)
    return data, target
```

Initiate an instance
--------------------

The model is set up with the hyperparameter below. The vocab size is
equal to the length of the vocab object.

```python
ntokens = len(TEXT.vocab.stoi) # the size of vocabulary
emsize = 200 # embedding dimension
nhid = 200 # the dimension of the feedforward network model in nn.TransformerEncoder
nlayers = 2 # the number of nn.TransformerEncoderLayer in nn.TransformerEncoder
nhead = 2 # the number of heads in the multiheadattention models
dropout = 0.2 # the dropout value
model = TransformerModel(ntokens, emsize, nhead, nhid, nlayers, dropout).to(device)
```

Run the model
-------------

[CrossEntropyLoss](https://pytorch.org/docs/master/nn.html?highlight=crossentropyloss#torch.nn.CrossEntropyLoss) is applied to track the loss and [SGD](https://pytorch.org/docs/master/optim.html?highlight=sgd#torch.optim.SGD) implements stochastic gradient descent method as the optimizer.
The initial learning rate is set to 5.0. [StepLR](https://pytorch.org/docs/master/optim.html?highlight=steplr#torch.optim.lr_scheduler.StepLR) is applied to adjust the learn rate through epochs.
During the training, we use [nn.utils.clip_grad_norm](https://pytorch.org/docs/master/nn.html?highlight=nn%20utils%20clip_grad_norm#torch.nn.utils.clip_grad_norm_) function to scale all the gradient together to prevent exploding.

```python
criterion = nn.CrossEntropyLoss()
lr = 5.0 # learning rate
optimizer = torch.optim.SGD(model.parameters(), lr=lr)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.95)

import time
def train():
    model.train() # Turn on the train mode
    total_loss = 0.
    start_time = time.time()
    ntokens = len(TEXT.vocab.stoi)
    for batch, i in enumerate(range(0, train_data.size(0) - 1, bptt)):
        data, targets = get_batch(train_data, i)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output.view(-1, ntokens), targets)
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 0.5)
        optimizer.step()

        total_loss += loss.item()
        log_interval = 200
        if batch % log_interval == 0 and batch > 0:
            cur_loss = total_loss / log_interval
            elapsed = time.time() - start_time
            print('| epoch {:3d} | {:5d}/{:5d} batches | '
                  'lr {:02.2f} | ms/batch {:5.2f} | '
                  'loss {:5.2f} | ppl {:8.2f}'.format(
                    epoch, batch, len(train_data) // bptt, scheduler.get_lr()[0],
                    elapsed * 1000 / log_interval,
                    cur_loss, math.exp(cur_loss)))
            total_loss = 0
            start_time = time.time()

def evaluate(eval_model, data_source):
    eval_model.eval() # Turn on the evaluation mode
    total_loss = 0.
    ntokens = len(TEXT.vocab.stoi)
    with torch.no_grad():
        for i in range(0, data_source.size(0) - 1, bptt):
            data, targets = get_batch(data_source, i)
            output = eval_model(data)
            output_flat = output.view(-1, ntokens)
            total_loss += len(data) * criterion(output_flat, targets).item()
    return total_loss / (len(data_source) - 1)
```

Loop over epochs. Save the model if the validation loss is the best
we've seen so far. Adjust the learning rate after each epoch.

```python
best_val_loss = float("inf")
epochs = 3 # The number of epochs
best_model = None

for epoch in range(1, epochs + 1):
    epoch_start_time = time.time()
    train()
    val_loss = evaluate(model, val_data)
    print('-' * 89)
    print('| end of epoch {:3d} | time: {:5.2f}s | valid loss {:5.2f} | '
          'valid ppl {:8.2f}'.format(epoch, (time.time() - epoch_start_time),
                                     val_loss, math.exp(val_loss)))
    print('-' * 89)

    if val_loss < best_val_loss:
        best_val_loss = val_loss
        best_model = model

    scheduler.step()
```

    /usr/local/lib/python3.6/dist-packages/torch/optim/lr_scheduler.py:351: UserWarning: To get the last learning rate computed by the scheduler, please use `get_last_lr()`.
      "please use `get_last_lr()`.", UserWarning)

    | epoch   1 |   200/ 2981 batches | lr 5.00 | ms/batch 17.66 | loss  8.07 | ppl  3190.20
    | epoch   1 |   400/ 2981 batches | lr 5.00 | ms/batch 16.16 | loss  6.81 | ppl   904.06
    | epoch   1 |   600/ 2981 batches | lr 5.00 | ms/batch 16.21 | loss  6.37 | ppl   583.67
    | epoch   1 |   800/ 2981 batches | lr 5.00 | ms/batch 16.27 | loss  6.23 | ppl   508.50
    | epoch   1 |  1000/ 2981 batches | lr 5.00 | ms/batch 16.31 | loss  6.11 | ppl   451.57
    | epoch   1 |  1200/ 2981 batches | lr 5.00 | ms/batch 16.34 | loss  6.09 | ppl   440.42
    | epoch   1 |  1400/ 2981 batches | lr 5.00 | ms/batch 16.40 | loss  6.04 | ppl   420.47
    | epoch   1 |  1600/ 2981 batches | lr 5.00 | ms/batch 16.40 | loss  6.04 | ppl   421.31
    | epoch   1 |  1800/ 2981 batches | lr 5.00 | ms/batch 16.40 | loss  5.96 | ppl   388.35
    | epoch   1 |  2000/ 2981 batches | lr 5.00 | ms/batch 16.44 | loss  5.95 | ppl   385.04
    | epoch   1 |  2200/ 2981 batches | lr 5.00 | ms/batch 16.47 | loss  5.85 | ppl   346.06
    | epoch   1 |  2400/ 2981 batches | lr 5.00 | ms/batch 16.56 | loss  5.89 | ppl   361.99
    | epoch   1 |  2600/ 2981 batches | lr 5.00 | ms/batch 16.59 | loss  5.89 | ppl   362.61
    | epoch   1 |  2800/ 2981 batches | lr 5.00 | ms/batch 16.65 | loss  5.79 | ppl   328.52
    -----------------------------------------------------------------------------------------
    | end of epoch   1 | time: 51.73s | valid loss  5.69 | valid ppl   296.12
    -----------------------------------------------------------------------------------------
    | epoch   2 |   200/ 2981 batches | lr 4.51 | ms/batch 16.83 | loss  5.80 | ppl   331.27
    | epoch   2 |   400/ 2981 batches | lr 4.51 | ms/batch 16.78 | loss  5.77 | ppl   320.24
    | epoch   2 |   600/ 2981 batches | lr 4.51 | ms/batch 16.84 | loss  5.60 | ppl   270.97
    | epoch   2 |   800/ 2981 batches | lr 4.51 | ms/batch 16.89 | loss  5.63 | ppl   278.71
    | epoch   2 |  1000/ 2981 batches | lr 4.51 | ms/batch 16.90 | loss  5.59 | ppl   266.43
    | epoch   2 |  1200/ 2981 batches | lr 4.51 | ms/batch 17.00 | loss  5.60 | ppl   271.78
    | epoch   2 |  1400/ 2981 batches | lr 4.51 | ms/batch 17.02 | loss  5.62 | ppl   276.21
    | epoch   2 |  1600/ 2981 batches | lr 4.51 | ms/batch 17.05 | loss  5.65 | ppl   285.20
    | epoch   2 |  1800/ 2981 batches | lr 4.51 | ms/batch 17.08 | loss  5.58 | ppl   265.18
    | epoch   2 |  2000/ 2981 batches | lr 4.51 | ms/batch 17.20 | loss  5.61 | ppl   272.27
    | epoch   2 |  2200/ 2981 batches | lr 4.51 | ms/batch 17.21 | loss  5.50 | ppl   245.18
    | epoch   2 |  2400/ 2981 batches | lr 4.51 | ms/batch 17.25 | loss  5.57 | ppl   261.72
    | epoch   2 |  2600/ 2981 batches | lr 4.51 | ms/batch 17.29 | loss  5.58 | ppl   264.56
    | epoch   2 |  2800/ 2981 batches | lr 4.51 | ms/batch 17.19 | loss  5.50 | ppl   245.27
    -----------------------------------------------------------------------------------------
    | end of epoch   2 | time: 53.38s | valid loss  5.55 | valid ppl   256.81
    -----------------------------------------------------------------------------------------
    | epoch   3 |   200/ 2981 batches | lr 4.29 | ms/batch 17.23 | loss  5.54 | ppl   253.60
    | epoch   3 |   400/ 2981 batches | lr 4.29 | ms/batch 17.06 | loss  5.55 | ppl   256.59
    | epoch   3 |   600/ 2981 batches | lr 4.29 | ms/batch 17.05 | loss  5.35 | ppl   210.74
    | epoch   3 |   800/ 2981 batches | lr 4.29 | ms/batch 17.12 | loss  5.41 | ppl   223.24
    | epoch   3 |  1000/ 2981 batches | lr 4.29 | ms/batch 17.05 | loss  5.37 | ppl   214.11
    | epoch   3 |  1200/ 2981 batches | lr 4.29 | ms/batch 17.13 | loss  5.40 | ppl   220.69
    | epoch   3 |  1400/ 2981 batches | lr 4.29 | ms/batch 17.09 | loss  5.43 | ppl   228.11
    | epoch   3 |  1600/ 2981 batches | lr 4.29 | ms/batch 17.08 | loss  5.46 | ppl   235.35
    | epoch   3 |  1800/ 2981 batches | lr 4.29 | ms/batch 17.08 | loss  5.40 | ppl   222.21
    | epoch   3 |  2000/ 2981 batches | lr 4.29 | ms/batch 17.07 | loss  5.43 | ppl   229.29
    | epoch   3 |  2200/ 2981 batches | lr 4.29 | ms/batch 17.07 | loss  5.32 | ppl   203.94
    | epoch   3 |  2400/ 2981 batches | lr 4.29 | ms/batch 17.08 | loss  5.39 | ppl   220.12
    | epoch   3 |  2600/ 2981 batches | lr 4.29 | ms/batch 17.14 | loss  5.41 | ppl   224.23
    | epoch   3 |  2800/ 2981 batches | lr 4.29 | ms/batch 17.15 | loss  5.34 | ppl   208.53
    -----------------------------------------------------------------------------------------
    | end of epoch   3 | time: 53.56s | valid loss  5.52 | valid ppl   249.41
    -----------------------------------------------------------------------------------------

Evaluate the model with the test dataset
-------------------------------------

Apply the best model to check the result with the test dataset.

```python
test_loss = evaluate(best_model, test_data)
print('=' * 89)
print('| End of training | test loss {:5.2f} | test ppl {:8.2f}'.format(
    test_loss, math.exp(test_loss)))
print('=' * 89)
```

    =========================================================================================
    | End of training | test loss  5.43 | test ppl   227.81
    =========================================================================================

```python

```


# Tutorial-IllinoisGRMHD: Convert_to_HydroBase ETKThorn

## Authors: Leo Werneck & Zach Etienne

**This module is currently under development**

## In this tutorial module we generate the Convert_to_HydroBase ETK thorn files, compatible with our latest implementation of IllinoisGRMHD

### Required and recommended citations:

* **(Required)** Etienne, Z. B., Paschalidis, V., Haas R., Mösta P., and Shapiro, S. L. IllinoisGRMHD: an open-source, user-friendly GRMHD code for dynamical spacetimes. Class. Quantum Grav. 32 (2015) 175009. ([arxiv:1501.07276](http://arxiv.org/abs/1501.07276)).
* **(Required)** Noble, S. C., Gammie, C. F., McKinney, J. C., Del Zanna, L. Primitive Variable Solvers for Conservative General Relativistic Magnetohydrodynamics. Astrophysical Journal, 641, 626 (2006) ([astro-ph/0512420](https://arxiv.org/abs/astro-ph/0512420)).
* **(Recommended)** Del Zanna, L., Bucciantini N., Londrillo, P. An efficient shock-capturing central-type scheme for multidimensional relativistic flows - II. Magnetohydrodynamics. A&A 400 (2) 397-413 (2003). DOI: 10.1051/0004-6361:20021641 ([astro-ph/0210618](https://arxiv.org/abs/astro-ph/0210618)).

If using the version of `IllinoisGRMHD` with piecewise polytropic *or* tabulated (coming soon!) EOS support, then the following citation is also required:

* **(Required)** Etienne, Z. B., Werneck, L., Paschalidis, V., Haas R., Mösta P., and Shapiro, S. L., *IllinoisGRMHD github repository* (2019). Source Code URL: https://github.com/zachetienne/nrpytutorial/tree/master/IllinoisGRMHD/.

### Dependencies

The files generated in this tutorial notebook depends on the following files:

* `IllinoisGRMHD_headers.h` \[[**tutorial**](Tutorial-IllinoisGRMHD__IllinoisGRMHD_headers.ipynb)\]
* `IllinoisGRMHD_EoS_lowlevel_functs.C` \[[**tutorial**](Tutorial-IllinoisGRMHD__EoS_lowlevel_functs.ipynb)\]

# Table of Contents
$$\label{toc}$$

This module is organized as follows

0. [Step 0](#src_dir): **Source directory creation**
1. [Step 1](#convert_to_hydrobase__src): **The `Convert_to_HydroBase.C` file**
    1. [Step 1.a](#initialize_eos_struct__compute_eps): *Initializing the EOS struct, computing $\epsilon$*
    1. [Step 1.b](#valencia_vi_from_vi): *Computing the Valencia 3-velocity from $v^{i}$*
    1. [Step 1.c](#computing_lorentz_factor__update_bi): *Computing the Lorentz factor $\gamma_{v}$ and updating $B^{i}$*
1. [Step 2](#convert_to_hydrobase__param): **The `param.ccl` file**
1. [Step 3](#convert_to_hydrobase__interface): **The `interface.ccl` file**
1. [Step 4](#convert_to_hydrobase__schedule): **The `schedule.ccl` file**
1. [Step 5](#convert_to_hydrobase__make): **The `make.code.defn` file**
1. [Step 6](#code_validation): **Code validation**
1. [Step 7](#latex_pdf_output): **Output this notebook to $\LaTeX$-formatted PDF file**

# Step 0: Source directory creation \[Back to [top](#toc)\]
$$\label{src_dir}$$

We will now use the [cmdline_helper.py NRPy+ module](Tutorial-Tutorial-cmdline_helper.ipynb) to create the source directory within the `IllinoisGRMHD` NRPy+ directory, if it does not exist yet.

```python
# Step 0: Creation of the IllinoisGRMHD source directory
# Step 0a: Load up cmdline_helper and create the directory
import os,sys
nrpy_dir_path = os.path.join("..","..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

import cmdline_helper as cmd
basedir = os.path.join("..","Convert_to_HydroBase")
cmd.mkdir(basedir)
srcdir = os.path.join(basedir,"src")
cmd.mkdir(srcdir)
```

# Step 1: The `Convert_to_HydroBase.C` file \[Back to [top](#toc)\]
$$\label{convert_to_hydrobase__src}$$

## Step 1.a: Initializing the EOS struct, computing $\epsilon$ \[Back to [top](#toc)\]
$$\label{initialize_eos_struct__compute_eps}$$

Our goal here is to compute $\epsilon$, the specific internal energy. `IllinoisGRMHD` implements a hybrid EOS (see equation (20) in [Etienne *et al*.](https://arxiv.org/pdf/1501.07276.pdf))

$$
P = P_{\rm cold} + P_{\rm th} = P_{\rm cold} + \left(\Gamma_{\rm th} - 1\right)\rho_{b}\epsilon_{\rm th}\ ,
$$

where $P_{\rm cold}$ is determined using a single or piecewise polytropic EOS. We assume $\epsilon = \epsilon_{\rm cold} + \epsilon_{\rm th}$, i.e.

$$
P = P_{\rm cold} + \left(\Gamma_{\rm th} - 1\right)\rho_{b}\left(\epsilon - \epsilon_{\rm cold}\right)\ ,
$$

so that we obtain

$$
\boxed{\epsilon = \epsilon_{\rm cold} + \frac{P - P_{\rm cold}}{\rho_{b}\left(\Gamma_{\rm th}-1\right)}}\ .
$$

Below, $P_{\rm cold}$ and $\epsilon_{\rm cold}$ are determined using the `compute_P_cold__eps_cold()` function, which is documented in the [EoS_lowlevel_functs `IllinoisGRMHD` tutorial notebook](Tutorial-IllinoisGRMHD__EoS_lowlevel_functs.ipynb).

```python
%%writefile $srcdir/Convert_to_HydroBase.C
#include "cctk.h"
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <sys/time.h>
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IllinoisGRMHD_headers.h"

void Convert_to_HydroBase(CCTK_ARGUMENTS) {

  DECLARE_CCTK_ARGUMENTS;
  DECLARE_CCTK_PARAMETERS;

  // Generally, we only need the HydroBase variables for diagnostic purposes, so we run the below loop only at iterations in which diagnostics are run.
  if(Convert_to_HydroBase_every==0 || cctk_iteration%Convert_to_HydroBase_every!=0) return;

  /***************
   * PPEOS Patch *
   ***************
   * We will need to set up our EOS in
   * order to be able to compute eps below
   */
  eos_struct eos;
  initialize_EOS_struct_from_input(eos);

#pragma omp parallel for
  for(int k=0;k<cctk_lsh[2];k++)
    for(int j=0;j<cctk_lsh[1];j++)
      for(int i=0;i<cctk_lsh[0];i++) {
        int index = CCTK_GFINDEX3D(cctkGH,i,j,k);
        /* Note that we currently do not set Abar, Y_e, temperature, entropy, Avec[3], Aphi, Avec_stag[3], Aphi_stag */
        CCTK_REAL PRIMS[MAXNUMVARS];
        int ww=0;
        PRIMS[ww] = rho_b[index]; ww++;
        PRIMS[ww] = P[index];     ww++;
        PRIMS[ww] = vx[index];    ww++;
        PRIMS[ww] = vy[index];    ww++;
        PRIMS[ww] = vz[index];    ww++;
        PRIMS[ww] = Bx[index];    ww++;
        PRIMS[ww] = By[index];    ww++;
        PRIMS[ww] = Bz[index];    ww++;

        rho[index]   = PRIMS[RHOB];
        press[index] = PRIMS[PRESSURE];

        /***************
         * PPEOS Patch *
         ***************
         * For our hybrid piecewise polytropic EOS, we have
         * .------------------------------------------------------.
         * | eps = eps_cold + (P - P_cold)/( rho*(Gamma_th - 1) ) |
         * .------------------------------------------------------.
         */
        /* Compute P_cold and eps_cold */
        CCTK_REAL P_cold, eps_cold;
        compute_P_cold__eps_cold(eos,PRIMS[RHOB], P_cold,eps_cold);

        /* Compute eps as described above */
        eps[index] = (PRIMS[PRESSURE]-P_cold)/PRIMS[RHOB]/(Gamma_th-1.0);
```

    Overwriting ../Convert_to_HydroBase/src/Convert_to_HydroBase.C

## Step 1.b: Computing the Valencia 3-velocity from $v^{i}$ \[Back to [top](#toc)\]
$$\label{valencia_vi_from_vi}$$

In `IllinoisGRMHD`, we use the 3-velocity

$$
v^{i}\equiv\frac{u^{i}}{u^{0}}\ ,
$$

where $u^{\mu}$ is the fluid 4-velocity. The [HydroBase ETK Thorn](http://einsteintoolkit.org/thornguide/EinsteinBase/HydroBase/documentation.html), however, uses the Valencia formalism, which adopts the 3-velocity as measured by a normal observers (also referred to as the Eulerian 3-velocity), $v^{i}_{(n)}$, which is given by (see equation (14) in [Mösta *et al*.](https://arxiv.org/pdf/1304.5544.pdf))

$$
v^{i}_{(n)} = \frac{u^{i}}{\alpha u^{0}} + \frac{\beta^{i}}{\alpha}\ .
$$

Thus, we can read off the relation

$$
\boxed{v^{i}_{(n)} = \frac{1}{\alpha}\left(v^{i} + \beta^{i}\right)}\ ,
$$

which we implement below.

```python
%%writefile -a $srcdir/Convert_to_HydroBase.C

        // IllinoisGRMHD defines v^i = u^i/u^0.

        // Meanwhile, the ET/HydroBase formalism, called the Valencia
        // formalism, splits the 4 velocity into a purely spatial part
        // and a part that is normal to the spatial hypersurface:
        // u^a = G (n^a + U^a), (Eq. 14 of arXiv:1304.5544; G=W, U^a=v^a)
        // where n^a is the unit normal vector to the spatial hypersurface,
        // n_a = {-\alpha,0,0,0}, and U^a is the purely spatial part, which
        // is defined in HydroBase as the vel[] vector gridfunction.
        // Then u^a n_a = - \alpha u^0 = G n^a n_a = -G, and
        // of course \alpha u^0 = 1/sqrt(1+γ^ij u_i u_j) = \Gamma,
        // the standard Lorentz factor.

        // Note that n^i = - \beta^i / \alpha, so
        // u^a = \Gamma (n^a + U^a)
        // -> u^i = \Gamma ( U^i - \beta^i / \alpha )
        // which implies
        // v^i = u^i/u^0
        //     = \Gamma/u^0 ( U^i - \beta^i / \alpha ) <- \Gamma = \alpha u^0
        //     = \alpha ( U^i - \beta^i / \alpha )
        //     = \alpha U^i - \beta^i
        CCTK_REAL lapseL=alp[index];
        CCTK_REAL lapseL_inv=1.0/lapseL;
        vel[CCTK_GFINDEX4D(cctkGH,i,j,k,0)] = (PRIMS[VX] + betax[index])*lapseL_inv;
        vel[CCTK_GFINDEX4D(cctkGH,i,j,k,1)] = (PRIMS[VY] + betay[index])*lapseL_inv;
        vel[CCTK_GFINDEX4D(cctkGH,i,j,k,2)] = (PRIMS[VZ] + betaz[index])*lapseL_inv;
```

    Appending to ../Convert_to_HydroBase/src/Convert_to_HydroBase.C

## Step 1.c: Computing the Lorentz factor $\gamma_{v}$ and updating $B^{i}$ \[Back to [top](#toc)\]
$$\label{computing_lorentz_factor__update_bi}$$

We now want to compute the Lorentz factor $\gamma_{v}=\left(1+\gamma_{ij}u^{i}u^{j}\right)^{-1/2}$. Below we implement the quantity

$$
A = 1 - \frac{1}{\left(\alpha u^{0}\right)^{2}} = \gamma_{ij}\left(\frac{v^{i}+\beta^{i}}{\alpha}\right)\left(\frac{v^{j}+\beta^{j}}{\alpha}\right)\ ,
$$

which imply that $\gamma_{v}=\alpha u^{0}$. First let us show that this is indeed the case. Consider

$$
\begin{align}
A
&= \gamma_{ij}\left(\frac{v^{i}+\beta^{i}}{\alpha}\right)\left(\frac{v^{j}+\beta^{j}}{\alpha}\right)\\
&= \frac{\gamma_{ij}}{\alpha^{2}}\left[\frac{\gamma^{ik}u_{k}}{u^{0}} - \beta^{i} + \beta^{i}\right]\left[\frac{\gamma^{j\ell}u_{\ell}}{u^{0}} - \beta^{j} + \beta^{j}\right]\\
&=\frac{\gamma_{ij}u^{i}u^{j}}{\left(\alpha u^{0}\right)^{2}}\\
&=\frac{\left(\alpha u^{0}\right)^{2}-1}{\left(\alpha u^{0}\right)^{2}}\\
&=1 - \frac{1}{\left(\alpha u^{0}\right)^{2}}\ \\
\implies &\boxed{A = 1 - \frac{1}{\left(\alpha u^{0}\right)^{2}}}\ ,
\end{align}
$$

where when going from line 1 to 2 and from line 3 to 4 we have used eqs. (53) and (56) from [Duez *et al.*](https://arxiv.org/pdf/astro-ph/0503420.pdf), respectively. Keep in mind that the equation we are going to implement below is

$$
\boxed{{\rm one\_minus\_one\_over\_alpha\_u0\_squared} = \gamma_{ij}\left(\frac{v^{i}+\beta^{i}}{\alpha}\right)\left(\frac{v^{j}+\beta^{j}}{\alpha}\right)}\ ,
$$

but it is important to know that this equation also equals $A$ above.

Finally, we update the `Bvec` array with the appropriate (`Bx`,`By`,`Bz`) values.

```python
%%writefile -a $srcdir/Convert_to_HydroBase.C

        // \alpha u^0 = 1/sqrt(1+γ^ij u_i u_j) = \Gamma = w_lorentz
        // First compute u^0:
        // Derivation of first equation:
        // \gamma_{ij} (v^i + \beta^i)(v^j + \beta^j)/(\alpha)^2
        //   = \gamma_{ij} 1/(u^0)^2 ( \gamma^{ik} u_k \gamma^{jl} u_l /(\alpha)^2 <- Using Eq. 53 of arXiv:astro-ph/0503420
        //   = 1/(u^0 \alpha)^2 u_j u_l \gamma^{jl}  <- Since \gamma_{ij} \gamma^{ik} = \delta^k_j
        //   = 1/(u^0 \alpha)^2 ( (u^0 \alpha)^2 - 1 ) <- Using Eq. 56 of arXiv:astro-ph/0503420
        //   = 1 - 1/(u^0 \alpha)^2 <= 1
        CCTK_REAL shiftxL = betax[index];
        CCTK_REAL shiftyL = betay[index];
        CCTK_REAL shiftzL = betaz[index];

        CCTK_REAL gxxL = gxx[index];
        CCTK_REAL gxyL = gxy[index];
        CCTK_REAL gxzL = gxz[index];
        CCTK_REAL gyyL = gyy[index];
        CCTK_REAL gyzL = gyz[index];
        CCTK_REAL gzzL = gzz[index];

        CCTK_REAL one_minus_one_over_alpha_u0_squared = (gxxL* SQR(PRIMS[VX] + shiftxL) +
                                                         2.0*gxyL*(PRIMS[VX] + shiftxL)*(PRIMS[VY] + shiftyL) +
                                                         2.0*gxzL*(PRIMS[VX] + shiftxL)*(PRIMS[VZ] + shiftzL) +
                                                         gyyL* SQR(PRIMS[VY] + shiftyL) +
                                                         2.0*gyzL*(PRIMS[VY] + shiftyL)*(PRIMS[VZ] + shiftzL) +
                                                         gzzL* SQR(PRIMS[VZ] + shiftzL) )*SQR(lapseL_inv);
        /*** Check for superluminal velocity ***/
        //FIXME: Instead of >1.0, should be one_minus_one_over_alpha_u0_squared > ONE_MINUS_ONE_OVER_GAMMA_SPEED_LIMIT_SQUARED, for consistency with conserv_to_prims routines

        if(one_minus_one_over_alpha_u0_squared > 1.0) {
          CCTK_VInfo(CCTK_THORNSTRING,"Convert_to_HydroBase WARNING: Found superluminal velocity. This should have been caught by IllinoisGRMHD.");
        }

        // A = 1.0-one_minus_one_over_alpha_u0_squared = 1-(1-1/(al u0)^2) = 1/(al u0)^2
        // 1/sqrt(A) = al u0
        CCTK_REAL alpha_u0 = 1.0/sqrt(1.0-one_minus_one_over_alpha_u0_squared);
        if(std::isnan(alpha_u0*lapseL_inv)) printf("BAD FOUND NAN ALPHAU0 CALC: %.15e %.15e %.15e\n",alpha_u0,lapseL_inv,one_minus_one_over_alpha_u0_squared);

        w_lorentz[index] = alpha_u0;

        Bvec[CCTK_GFINDEX4D(cctkGH,i,j,k,0)] = PRIMS[BX_CENTER];
        Bvec[CCTK_GFINDEX4D(cctkGH,i,j,k,1)] = PRIMS[BY_CENTER];
        Bvec[CCTK_GFINDEX4D(cctkGH,i,j,k,2)] = PRIMS[BZ_CENTER];

      }
}

```

    Appending to ../Convert_to_HydroBase/src/Convert_to_HydroBase.C

# Step 2: The `param.ccl` file \[Back to [top](#toc)\]
$$\label{convert_to_hydrobase__param}$$

The `param.ccl` file sets up parameters that are used by the `Convert_to_HydroBase` ETK thorn. These parameters are accessed as globals after calling the `DECLARE_CCTK_PARAMETERS` macro.

When declaring a parameter, we have the following notation

TYPE name "Comment" STEERABLE=?
{
minimum allowed value:maximum allowed value :: "Comment"
other possible value #1                     :: "Comment"
other possible value #2                     :: "Comment"
         ...                                ::    ...
final possible value                        :: "Comment"
} Default value

The *STEERABLE* option is either set to never (default value, for which the keyword is not necessary at all) or to ALWAYS, which allows for the value of the parameter to change after set to its initial value. For example, when declaring the $\Gamma_{\rm th}$ parameter below, we have

REAL Gamma_th "thermal gamma parameter"
{
 0:* :: "Physical values"
-1   :: "forbidden value to make sure it is explicitly set in the parfile"
} -1

This declares the *REAL* (usually a macro for double) $\Gamma_{\rm th}$ parameter as ${\rm Gamma\_th}$. We allow this parameter to have only positive values, hence the notation `0:*`. The asterisk means "any positive value is allowed". The notation `*:*` then means "any value is allowed. The second line above sets the value "$-1$" to be a forbidden value, which if read after the code starts would produce an error saying that this value has not been initialized properly. Finally, the default value of the variable is set to "$-1$", the forbidden value. Because the STEERABLE keyword is not explicitly set to ALWAYS above, this means that once set, the value of $\Gamma_{\rm th}$ cannot be changed.

The `shares: THORN` syntax tells the ETK that the `Convert_to_HydroBase` thorn will share the parameters from the `THORN` thorn. For example, the following code

shares: ADMBase
USES CCTK_INT lapse_timelevels
USES CCTK_INT shift_timelevels
USES CCTK_INT metric_timelevels

will tell the ETK that the variables `lapse_timelevels`, `shift_timelevels`, and `metric_timelevels` from the [ADMBase ETK thorn](http://einsteintoolkit.org/thornguide/EinsteinBase/ADMBase/documentation.html) should also be available for the `Convert_to_HydroBase` thorn once the `DECLARE_CCTK_PARAMETERS` macro has been executed.

For more details on how to build a `param.ccl` file, please refer to the [official Cactus documentation](https://einsteintoolkit.org/usersguide/UsersGuidech12.html).

```python
%%writefile $basedir/param.ccl
# Parameter definitions for thorn convert_to_HydroBase
# $Header:$

#############################################################################
### import HydroBase & ADMBase parameters

shares: HydroBase
USES CCTK_INT timelevels

shares: ADMBase
USES CCTK_INT lapse_timelevels
USES CCTK_INT shift_timelevels
USES CCTK_INT metric_timelevels

shares: IllinoisGRMHD
USES KEYWORD neos
USES KEYWORD Gamma_th
USES KEYWORD K_ppoly_tab0
USES KEYWORD rho_ppoly_tab_in[10]
USES KEYWORD Gamma_ppoly_tab_in[10]
#############################################################################

private:
INT Convert_to_HydroBase_every "How often to convert IllinoisGRMHD primitive variables to HydroBase (Valencia formulation) primitive variables? Needed for some ET-based diagnostics. NOT needed for pure IllinoisGRMHD runs."
{
 0:* :: "zero (disable) or positive (every N iterations)"
} 0

```

    Overwriting ../Convert_to_HydroBase/param.ccl

# Step 3: The `interface.ccl` file \[Back to [top](#toc)\]
$$\label{convert_to_hydrobase__interface}$$

The `interface.ccl` file sets up a few different things of how `Convert_to_HydroBase` interfaces with the rest of the toolkit. For starters, it tells which thorns are used by `Convert_to_HydroBase`, which is done using the keyword `inherits`.

It also requests to use files from different thorns using the `uses include header: HEADER_FILE.h` command.

For more details on how to build a `interface.ccl` file, please refer to the [official Cactus documentation](https://einsteintoolkit.org/usersguide/UsersGuidech12.html).

```python
%%writefile $basedir/interface.ccl
# Interface definition for thorn Convert_to_HydroBase
# $Header:$

implements: Convert_to_HydroBase
inherits: grid HydroBase ADMBase IllinoisGRMHD

uses include header: IllinoisGRMHD_headers.h

```

    Overwriting ../Convert_to_HydroBase/interface.ccl

# Step 4: The `schedule.ccl` file \[Back to [top](#toc)\]
$$\label{convert_to_hydrobase__schedule}$$

The `schedule.ccl` file sets up the structure of a given ETK run. It tells when the functions of `Convert_to_HydroBase` must be called and in what order. The general form of a scheduling is

schedule name IN thorn_name as alias after afunct_1 ... after afunct_n before bfunct_1 ... before bfunct_m
{
LANG:    function programming language
OPTIONS: options
SYNC:    sync options
} "Comment"

Following the notation above, the schedule keyword sets up the function whose name is name defined IN the ETK thorn thorn_name. We are also allowed to set an alias, if the function name is too long, but this is optional. We then set exactly when the function should be called, i.e. by saying it should be called before and/or after other functions.

Inside curly braces, we set up the programming language of the function, e.g. C, options, e.g. LOCAL, and whether or not variables need to be synchronized with other functions. Finally, one can place a commend outside the curly braces, explaining the schedule just implemented.

For more details on how to build a `schedule.ccl` file, please refer to the [official Cactus documentation](https://einsteintoolkit.org/usersguide/UsersGuidech12.html).

```python
%%writefile $basedir/schedule.ccl
# Schedule definitions for thorn Convert_to_HydroBase
# $Header:$

SCHEDULE Convert_to_HydroBase AT CCTK_INITIAL AFTER SetTmunu
{
  LANG: C
} "Convert IllinoisGRMHD-native variables to HydroBase"

SCHEDULE Convert_to_HydroBase AT CCTK_ANALYSIS BEFORE compute_bi_b2_Poyn_fluxET BEFORE particle_tracerET BEFORE VolumeIntegralGroup BEFORE convert_to_MHD_3velocity AFTER ML_BSSN_evolCalcGroup
{
  OPTIONS: GLOBAL-EARLY,LOOP-LOCAL
  LANG: C
} "Convert IllinoisGRMHD-native variables to HydroBase"

```

    Overwriting ../Convert_to_HydroBase/schedule.ccl

# Step 5: The `make.code.defn` file \[Back to [top](#toc)\]
$$\label{convert_to_hydrobase__make}$$

The `make.code.defn` file tells the toolkit which files should be compiled during the ETK build.

```python
%%writefile $srcdir/make.code.defn
# Main make.code.defn file for thorn Convert_to_HydroBase
# $Header:$

# Source files in this directory
SRCS = Convert_to_HydroBase.C

# Subdirectories containing source files
SUBDIRS =

```

    Overwriting ../Convert_to_HydroBase/src/make.code.defn

# Step 6: Code validation \[Back to [top](#toc)\]
$$\label{code_validation}$$

First we download the original `IllinoisGRMHD` source code and then compare it to the source code generated by this tutorial notebook.

```python
# # Verify if the code generated by this tutorial module
# # matches the original IllinoisGRMHD source code

# # First download the original IllinoisGRMHD source code
# import urllib
# from os import path

# original_IGM_file_url  = "https://bitbucket.org/zach_etienne/wvuthorns/raw/5611b2f0b17135538c9d9d17c7da062abe0401b6/IllinoisGRMHD/src/A_i_rhs_no_gauge_terms.C"
# original_IGM_file_name = "A_i_rhs_no_gauge_terms-original.C"
# original_IGM_file_path = os.path.join(IGM_src_dir_path,original_IGM_file_name)

# # Then download the original IllinoisGRMHD source code
# # We try it here in a couple of ways in an attempt to keep
# # the code more portable
# try:
#     original_IGM_file_code = urllib.request.urlopen(original_IGM_file_url).read().decode("utf-8")
#     # Write down the file the original IllinoisGRMHD source code
#     with open(original_IGM_file_path,"w") as file:
#         file.write(original_IGM_file_code)
# except:
#     try:
#         original_IGM_file_code = urllib.urlopen(original_IGM_file_url).read().decode("utf-8")
#         # Write down the file the original IllinoisGRMHD source code
#         with open(original_IGM_file_path,"w") as file:
#             file.write(original_IGM_file_code)
#     except:
#         # If all else fails, hope wget does the job
#         !wget -O $original_IGM_file_path $original_IGM_file_url

# # Perform validation
# Validation__A_i_rhs_no_gauge_terms__C  = !diff $original_IGM_file_path $outfile_path__A_i_rhs_no_gauge_terms__C

# if Validation__A_i_rhs_no_gauge_terms__C == []:
#     # If the validation passes, we do not need to store the original IGM source code file
#     !rm $original_IGM_file_path
#     print("Validation test for A_i_rhs_no_gauge_terms.C: PASSED!")
# else:
#     # If the validation fails, we keep the original IGM source code file
#     print("Validation test for A_i_rhs_no_gauge_terms.C: FAILED!")
#     # We also print out the difference between the code generated
#     # in this tutorial module and the original IGM source code
#     print("Diff:")
#     for diff_line in Validation__A_i_rhs_no_gauge_terms__C:
#         print(diff_line)
```

# Step 7: Output this notebook to $\LaTeX$-formatted PDF file \[Back to [top](#toc)\]
$$\label{latex_pdf_output}$$

The following code cell converts this Jupyter notebook into a proper, clickable $\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename
[Tutorial-IllinoisGRMHD__ETKThorn__Convert_to_HydroBase.pdf](Tutorial-IllinoisGRMHD__ETKThorn__Convert_to_HydroBase.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means).

```python
latex_nrpy_style_path = os.path.join(nrpy_dir_path,"latex_nrpy_style.tplx")
#!jupyter nbconvert --to latex --template $latex_nrpy_style_path --log-level='WARN' Tutorial-IllinoisGRMHD__ETKThorn__Convert_to_HydroBase.ipynb
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__ETKThorn__Convert_to_HydroBase.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__ETKThorn__Convert_to_HydroBase.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD__ETKThorn__Convert_to_HydroBase.tex
!rm -f Tut*.out Tut*.aux Tut*.log
```