```python
from IPython.display import HTML
HTML("""
<br><br>
<a href=http://wwwgong.pythonanywhere.com/cuspea/default/list_talks target=new>
<font size=+3 color=blue>CUSPEA Talks</font>
</a>
<br><br>
<br>
""")
```

CUSPEA Talks

```python

```

# Fun with [Jupyter](http://jupyter.org/)

## Table of Contents

* [Motivation](#hid_why)
* [Introduction](#hid_intro)
* [Problem Statement](#hid_problem)
* [Import packages](#hid_pkg)
* [Estimate x range](#hid_guess)
* [Use IPython as a calculator](#hid_calculator)
* [Use Python programming to find solution](#hid_program)
* [Graph the solution with matplotlib](#hid_graph)
* [Solve equation precisely using SymPy](#hid_sympy)
* [Pandas for Big Data Analytics](#hid_panda)
* [Multimedia with HTML5 -Text, Image, Audio, Video](#hid_html5)
* [Interactive widgets](#hid_widget)
* [Working with SQLite Databases](#hid_sqlite)
* [References](#hid_ref)
* [Contributors](#hid_author)
* [Appendix](#hid_apend)
    - [How to install Jupyter Notebook](#hid_setup)
    - [How to share a notebook](#hid_share)

## Motivation

* Current Choice

* A New Option

> The __Jupyter Notebook__ is an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and explanatory text. Uses include: data cleaning and transformation, numerical simulation, statistical modeling, machine learning and much more.

Useful for many tasks

* Programming
* Blogging
* Learning
* Research
* Documenting work
* Collaborating
* Communicating
* Publishing results

or even

* Doing homework as a student

```python
HTML("")
```

See this [Demo Notebook](https://nbviewer.jupyter.org/github/waltherg/notebooks/blob/master/2013-12-03-Crank_Nicolson.ipynb)

## Introduction

By solving a simple math problem here, I hope to demonstrate many basic  features of Jupyter Notebook.

## Problem Statement

A hot startup is selling a hot product.

In 2014, it had 30 customers; 3 years later, its customer base expands to 250.

Question: What is its annual growth rate?

_Translate into math:_  Let $x$ be annual growth rate,

then
$$ 30 \cdot (1 + x)^3 = 250 $$

## Import packages   (check out from Library)

```python
# math function
import math

# create np array
import numpy as np

# pandas for data analysis
import pandas as pd

# plotting
import matplotlib.pyplot as plt
%matplotlib inline

# symbolic math
import sympy as sy

# html5
from IPython.display import HTML, SVG, YouTubeVideo

# widgets
from collections import OrderedDict
from IPython.display import display, clear_output
from ipywidgets import Dropdown

# csv file
import csv

# work with Sqlite database
import sqlite3
```

## Estimate x range   (in Elementary School)

If $x$ = 1, then _l.h.s_ = 240, therefore $x$ > 1

If $x$ = 1.2, then l.h.s =

```python
30*(1+1.2)**3
```

    319.4400000000001

therefore  we know $x$ range = (1.0, 1.2)

## Use IPython as a calculator   (in Middle School)

```python
# import math
```

```python
math.exp(math.log(250/30)/3) - 1
```

    1.0274006651911334

```python
10**(math.log10(250/30)/3) - 1
```

    1.0274006651911334

```python
math.pow(10, math.log10(250/30)/3) -1
```

    1.0274006651911334

** annual customer growth rate = 102% **

## Use Python programming to find solution   (in High School)

### use loop

```python
nstep = 100
x_min, x_max = 1.0, 1.2
dd = (x_max-x_min)/float(nstep)
x_l = [(x_min + i*dd) for i in range(nstep)]
type(x_l)
```

    list

```python
print(x_l)
```

    [1.0, 1.002, 1.004, 1.006, 1.008, 1.01, 1.012, 1.014, 1.016, 1.018, 1.02, 1.022, 1.024, 1.026, 1.028, 1.03, 1.032, 1.034, 1.036, 1.038, 1.04, 1.042, 1.044, 1.046, 1.048, 1.05, 1.052, 1.054, 1.056, 1.058, 1.06, 1.062, 1.064, 1.066, 1.068, 1.07, 1.072, 1.074, 1.076, 1.078, 1.08, 1.082, 1.084, 1.086, 1.088, 1.09, 1.092, 1.094, 1.096, 1.0979999999999999, 1.1, 1.1019999999999999, 1.104, 1.1059999999999999, 1.108, 1.1099999999999999, 1.1119999999999999, 1.1139999999999999, 1.1159999999999999, 1.1179999999999999, 1.1199999999999999, 1.1219999999999999, 1.1239999999999999, 1.126, 1.128, 1.13, 1.132, 1.134, 1.136, 1.138, 1.14, 1.142, 1.144, 1.146, 1.148, 1.15, 1.152, 1.154, 1.156, 1.158, 1.16, 1.162, 1.164, 1.166, 1.168, 1.17, 1.172, 1.174, 1.176, 1.178, 1.18, 1.182, 1.184, 1.186, 1.188, 1.19, 1.192, 1.194, 1.196, 1.198]

```python
for t in x_l:
    err = abs(30*(1+t)**3 - 250)
    if err <= 0.5:
        print("t={x}: error={e:.4f}".format(x=t,e=err))
```

    t=1.028: error=0.2218

### create a numpy array

```python
# import numpy as np
# import pandas as pd
```

```python
print(x_l)
```

    [1.0, 1.002, 1.004, 1.006, 1.008, 1.01, 1.012, 1.014, 1.016, 1.018, 1.02, 1.022, 1.024, 1.026, 1.028, 1.03, 1.032, 1.034, 1.036, 1.038, 1.04, 1.042, 1.044, 1.046, 1.048, 1.05, 1.052, 1.054, 1.056, 1.058, 1.06, 1.062, 1.064, 1.066, 1.068, 1.07, 1.072, 1.074, 1.076, 1.078, 1.08, 1.082, 1.084, 1.086, 1.088, 1.09, 1.092, 1.094, 1.096, 1.0979999999999999, 1.1, 1.1019999999999999, 1.104, 1.1059999999999999, 1.108, 1.1099999999999999, 1.1119999999999999, 1.1139999999999999, 1.1159999999999999, 1.1179999999999999, 1.1199999999999999, 1.1219999999999999, 1.1239999999999999, 1.126, 1.128, 1.13, 1.132, 1.134, 1.136, 1.138, 1.14, 1.142, 1.144, 1.146, 1.148, 1.15, 1.152, 1.154, 1.156, 1.158, 1.16, 1.162, 1.164, 1.166, 1.168, 1.17, 1.172, 1.174, 1.176, 1.178, 1.18, 1.182, 1.184, 1.186, 1.188, 1.19, 1.192, 1.194, 1.196, 1.198]

####  using arange()

```python
x = np.arange(1.0, 1.2, 0.005)
print(x)
```

    [ 1.     1.005  1.01   1.015  1.02   1.025  1.03   1.035  1.04   1.045
      1.05   1.055  1.06   1.065  1.07   1.075  1.08   1.085  1.09   1.095  1.1
      1.105  1.11   1.115  1.12   1.125  1.13   1.135  1.14   1.145  1.15
      1.155  1.16   1.165  1.17   1.175  1.18   1.185  1.19   1.195]

check its type

```python
type(x)
```

    numpy.ndarray

```python
len(x)
```

    40

```python
print(30*(1+x)**3 - 250)
```

    [-10.          -8.19549625  -6.38197     -4.55939875  -2.72776     -0.88703125
       0.96281      2.82178625   4.68992      6.56723375   8.45375     10.34949125
      12.25448     14.16873875  16.09229     18.02515625  19.96736     21.91892375
      23.87987     25.85022125  27.83        29.81922875  31.81793     33.82612625
      35.84384     37.87109375  39.90791     41.95431125  44.01032     46.07595875
      48.15125     50.23621625  52.33088     54.43526375  56.54939     58.67328125
      60.80696     62.95044875  65.10377     67.26694625]

```python
x_ge_0 = (30*(1+x)**3 - 250) >= 0
x_ge_0
```

    array([False, False, False, False, False, False,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True,  True,  True,
            True,  True,  True,  True], dtype=bool)

```python
x_lt_0 = (30*(1+x)**3 - 250) < 0
x_lt_0
```

    array([ True,  True,  True,  True,  True,  True, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False, False, False, False, False, False,
           False, False, False, False], dtype=bool)

x_ge_0 and x_lt_0 are logical array

```python
for t in x:
    err = abs(30*(1+t)**3 - 250)
    if err <= 1.0:
        print("t={x}: error={e:.4f}".format(x=t,e=err))
```

    t=1.0249999999999995: error=0.8870
    t=1.0299999999999994: error=0.9628

#### using [linspace](https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html)

```python
x1 = np.linspace(1.0, 1.2, 100)
x1
```

    array([ 1.        ,  1.0020202 ,  1.0040404 ,  1.00606061,  1.00808081,
            1.01010101,  1.01212121,  1.01414141,  1.01616162,  1.01818182,
            1.02020202,  1.02222222,  1.02424242,  1.02626263,  1.02828283,
            1.03030303,  1.03232323,  1.03434343,  1.03636364,  1.03838384,
            1.04040404,  1.04242424,  1.04444444,  1.04646465,  1.04848485,
            1.05050505,  1.05252525,  1.05454545,  1.05656566,  1.05858586,
            1.06060606,  1.06262626,  1.06464646,  1.06666667,  1.06868687,
            1.07070707,  1.07272727,  1.07474747,  1.07676768,  1.07878788,
            1.08080808,  1.08282828,  1.08484848,  1.08686869,  1.08888889,
            1.09090909,  1.09292929,  1.09494949,  1.0969697 ,  1.0989899 ,
            1.1010101 ,  1.1030303 ,  1.10505051,  1.10707071,  1.10909091,
            1.11111111,  1.11313131,  1.11515152,  1.11717172,  1.11919192,
            1.12121212,  1.12323232,  1.12525253,  1.12727273,  1.12929293,
            1.13131313,  1.13333333,  1.13535354,  1.13737374,  1.13939394,
            1.14141414,  1.14343434,  1.14545455,  1.14747475,  1.14949495,
            1.15151515,  1.15353535,  1.15555556,  1.15757576,  1.15959596,
            1.16161616,  1.16363636,  1.16565657,  1.16767677,  1.16969697,
            1.17171717,  1.17373737,  1.17575758,  1.17777778,  1.17979798,
            1.18181818,  1.18383838,  1.18585859,  1.18787879,  1.18989899,
            1.19191919,  1.19393939,  1.1959596 ,  1.1979798 ,  1.2       ])

```python
for t in x1:
    err = math.fabs(30*(1+t)**3 - 250)
    if err <= 1.0:
        print("t={x}: error={e:.4f}".format(x=t,e=err))
```

    t=1.0262626262626262: error=0.4208
    t=1.0282828282828282: error=0.3265

## Graph the solution using Matplotlib (in High School)

[matplotlib](http://matplotlib.org/contents.html?v=20170307111739)   is visualization pkg for python

```python
# import matplotlib.pyplot as plt
# %matplotlib inline
```

```python
x2 = np.linspace(1.0, 1.05, 100)
f1 = 30*(1+x2)**3 - 250

f2 = np.zeros_like(x2)    # draw a horizontal line at y=0
```

$x$ intersection of two lines f1/f2 gives the solution

```python
plt.xlabel(r'$x$')
plt.ylabel(r'$\delta$')
plt.grid(True)
plt.title('IPython is a great analytical tool')
plt.axis([0.995,1.055, -11, 9])
#plt.axis([1.02, 1.04, -11, 9])
plt.plot(x2, f1, 'r+')
plt.plot(x2, f2, 'b-', lw=2)
plt.show()
```

## Solve equation precisely using SymPy  (in High School)

```python
# from sympy import *
# import sympy as sy
```

```python
sy.var('x')
```

    x

```python
sy.solve(30*(1+x)**3 - 250, x)
```

    [-1 + 15**(2/3)/3,
     -1 + 15**(2/3)*(-1/2 - sqrt(3)*I/2)/3,
     -1 + 15**(2/3)*(-1/2 + sqrt(3)*I/2)/3]

Ignore other 2 solutions because they are complex numbers

```python
grow_rate = -1 + 15**(2/3)/3
grow_rate
```

    1.027400665191133

## Pandas for Big Data Analytics  (in College)

[pandas](http://pandas.pydata.org/pandas-docs/stable/?v=20170307111739) stands for powerful Python data analysis toolkit

```python
# import pandas as pd
```

```python
year = [2014, 2015, 2016, 2017]
```

```python
customer_count = [30*(1+grow_rate)**i  for i in range(4)]
print(customer_count)
```

    [30.0, 60.822019955733985, 123.31060371652346, 249.9999999999998]

```python
df = pd.DataFrame(list(zip(year,customer_count)), columns=['Year','Customers'])
df
```

      Year
      Customers

      0
      2014
      30.000000

      1
      2015
      60.822020

      2
      2016
      123.310604

      3
      2017
      250.000000

```python
df.head(2)
```

      Year
      Customers

      0
      2014
      30.00000

      1
      2015
      60.82202

```python
df.tail(2)
```

      Year
      Customers

      2
      2016
      123.310604

      3
      2017
      250.000000

#### Line chart

```python
plt.plot(df['Year'], df['Customers'])
```

#### Bar chart

```python
plt.xlabel('Year')
plt.ylabel('Customer Count')
plt.grid(True)
plt.title('Customer Growth')

plt.bar(df['Year'], df['Customers'])

plt.plot(df['Year'], df['Customers'], 'r-')
```

## Multimedia with HTML5 -Text, Image, Audio, Video   (Having graduated from all the schools)

```python
# from IPython.display import HTML, SVG, YouTubeVideo
```

###   create an HTML table dynamically with Python, and we display it in the (HTML-based) notebook.

Let me create a multiplication table

```python
HTML('''
<table style="border: 2px solid black;">
''' +
''.join(['<tr>' +
         ''.join(['<td>{row}x{col}={prod}</td>'.format(
            row=row, col=col, prod=row*col
            ) for col in range(10)]) +
         '</tr>' for row in range(10)]) +
'''
</table>
''')
```

0x0=0	0x1=0	0x2=0	0x3=0	0x4=0	0x5=0	0x6=0	0x7=0	0x8=0	0x9=0

1x0=0	1x1=1	1x2=2	1x3=3	1x4=4	1x5=5	1x6=6	1x7=7	1x8=8	1x9=9

2x0=0	2x1=2	2x2=4	2x3=6	2x4=8	2x5=10	2x6=12	2x7=14	2x8=16	2x9=18

3x0=0	3x1=3	3x2=6	3x3=9	3x4=12	3x5=15	3x6=18	3x7=21	3x8=24	3x9=27

4x0=0	4x1=4	4x2=8	4x3=12	4x4=16	4x5=20	4x6=24	4x7=28	4x8=32	4x9=36

5x0=0	5x1=5	5x2=10	5x3=15	5x4=20	5x5=25	5x6=30	5x7=35	5x8=40	5x9=45

6x0=0	6x1=6	6x2=12	6x3=18	6x4=24	6x5=30	6x6=36	6x7=42	6x8=48	6x9=54

7x0=0	7x1=7	7x2=14	7x3=21	7x4=28	7x5=35	7x6=42	7x7=49	7x8=56	7x9=63

8x0=0	8x1=8	8x2=16	8x3=24	8x4=32	8x5=40	8x6=48	8x7=56	8x8=64	8x9=72

9x0=0	9x1=9	9x2=18	9x3=27	9x4=36	9x5=45	9x6=54	9x7=63	9x8=72	9x9=81

### display image

```python
HTML("""

""")
```

### create a SVG graphics dynamically.

```python
SVG('''<svg width="600" height="80">''' +
''.join(['''<circle cx="{x}" cy="{y}" r="{r}"
        fill="red" stroke-width="2" stroke="black">
        </circle>'''.format(
            x=(30+3*i)*(10-i), y=30, r=3.*float(i)
        ) for i in range(10)]) +
'''</svg>''')
```

### embed an audio clip

```python
HTML("""

<table>
<tr>
<td>
<a href=https://talkpython.fm>TalkPython </a>
<h2> Guido van Rossum</h2> (Python Creator & Tsar)
<h3> Python past, present, and future </h3>
<br/>
<audio controls>
  <source src="https://downloads.talkpython.fm/static/episode_cache/100-guido-van-rossum.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</td>
<td>

</tr>
</table>
""")

```

TalkPython

 Guido van Rossum
 (Python Creator & Tsar)

 Python past, present, and future

<audio controls>
  <source src="https://downloads.talkpython.fm/static/episode_cache/100-guido-van-rossum.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>

### display a Youtube video by giving its identifier to YoutubeVideo.

#### SciPy 2013 Keynote: IPython

```python
YouTubeVideo('j9YpkSX7NNM')
```

## Interactive widgets

we illustrate the latest interactive features in IPython 2.0+.

This version brings graphical widgets in the notebook that can interact with Python objects.

We will create a drop-down menu allowing us to display one among several videos.

```python
# How to comment out multiple lines of code in python
"""
from collections import OrderedDict
from IPython.display import display, clear_output
from ipywidgets import Dropdown
"""
```

    ' \nfrom collections import OrderedDict\nfrom IPython.display import display, clear_output\nfrom ipywidgets import Dropdown\n'

```python
# We create a Dropdown widget, with a dictionary containing
# the keys (video name) and the values (Youtube identifier) of every menu item.

dw = Dropdown(options=OrderedDict([
                        ('SciPy 2012', 'iwVvqwLDsJo'),
                        ('PyCon 2012', '2G5YTlheCbw'),
                        ('SciPy 2013', 'j9YpkSX7NNM'),
                        ('Guido Van Rossum', 'EBRMq2Ioxsc'),
                        ('Mendelssohn Violin', 'o1dBg__wsuo')
        ]))

# We create a callback function that displays the requested Youtube video.
def on_value_change(name, val):
    clear_output()
    display(YouTubeVideo(val))
# Every time the user selects an item, the function
# `on_value_change` is called, and the `val` argument
# contains the value of the selected item.
dw.on_trait_change(on_value_change, 'value')
# We choose a default value.
dw.value = dw.options['Mendelssohn Violin']

# Finally, we display the widget.
display(dw)
```

    Widget Javascript not detected.  It may not be installed properly. Did you enable the widgetsnbextension? If not, then run "jupyter nbextension enable --py --sys-prefix widgetsnbextension"

## Working with SQLite Databases

read [blog](https://www.dataquest.io/blog/python-pandas-databases/) at [DataQuest](https://www.dataquest.io)

```python
# open connection to db
conn = sqlite3.connect("dataset/open_src.sqlite")

# create a cursor
cur = conn.cursor()

# select query
results = cur.execute("select * from os_history limit 100;").fetchall()
#print(results)

# count # of rows
results = cur.execute("select count(*) from os_history;").fetchall()
#print(results)

# store data from csv file into db
with open('dataset/open_src_move_v2_1.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        #print(row['Year'])
        insert_str="""
            insert into os_history(year,subject,subjecturl,person,picture,history)
            values ({year}, "{subject}", "{subject_url}", "{person}", "{picture}", "{history}")
        """
        #print(insert_str.format(year=row['Year'],subject=row['Subject'],subject_url=row['SubjectURL'],person=row['Person'],picture=row['Picture'],history=row['History']))
        cur.execute(insert_str.format(year=row['Year'],subject=row['Subject'],subject_url=row['SubjectURL'],person=row['Person'],picture=row['Picture'],history=row['History']))
    conn.commit()

# create a dataframe
df = pd.read_sql_query("select * from os_history limit 5;",conn)

# inspect data
df
```

      year
      subject
      subjecturl
      person
      picture
      history

      0
      1983
      GNU Project : gcc, Emacs, gdb
      https://en.wikipedia.org/wiki/GNU_Project
      Richard Stallman
      https://upload.wikimedia.org/wikipedia/commons...
      Launch of the free software movement and found...

      1
      1984
      X Window Systerm
      https://en.wikipedia.org/wiki/X_Window_System
      X.Org
      https://upload.wikimedia.org/wikipedia/commons...
      basic framework for a GUI environment

      2
      1983
      GNU Project : gcc, Emacs, gdb
      https://en.wikipedia.org/wiki/GNU_Project
      Richard Stallman
      https://upload.wikimedia.org/wikipedia/commons...
      Launch of the free software movement and found...

      3
      1984
      X Window Systerm
      https://en.wikipedia.org/wiki/X_Window_System
      X.Org
      https://upload.wikimedia.org/wikipedia/commons...
      basic framework for a GUI environment

      4
      1985
      GNU Manifesto: GNU's Not Unix
      https://en.wikipedia.org/wiki/GNU_Manifesto
      Richard Stallman
      https://upload.wikimedia.org/wikipedia/en/thum...
      disagreement between Stallman and Symbolics, I...

## References

### Websites

* [DataCamp - Jupyter Notebook Tutorial](https://www.datacamp.com/community/tutorials/tutorial-jupyter-notebook#gs.ClmI4Jc)

* http://docs.python.org

It goes without saying that Pythonâ€™s own online documentation is an excellent resource if you need to delve into the finer details of the language and modules. Just make sure youâ€™re looking at the documentation for Python 3 and not earlier versions.

* http://www.python.org/dev/peps

Python Enhancement Proposals (PEPs) are invaluable if you want to understand the motivation for adding new features to the Python language as well as subtle implementation details. This is especially true for some of the more advanced language features. In writing this book, the PEPs were often more useful than the official documentation.

* http://pyvideo.org

This is a large collection of video presentations and tutorials from past PyCon conferences, user group meetings, and more. It can be an invaluable resource for learning about modern Python development. Many of the videos feature Python core developers talking about the new features being added in Python 3.

* http://code.activestate.com/recipes/langs/python

The ActiveState Python recipes site has long been a resource for finding the solution to thousands of specific programming problems. As of this writing, it contains approximately 300 recipes specific to Python 3. Youâ€™ll find that many of its recipes either expand upon topics covered in this book or focus on more narrowly defined tasks. As such, itâ€™s a good companion.

* http://stackoverflow.com/questions/tagged/python

Stack Overflow currently has more than 175,000 questions tagged as Python-related (and almost 5000 questions specific to Python 3). Although the quality of the questions and answers varies, there is a lot of good material to be found.

### Books

*  [Learning IPython for Interactive Computing and Data Visualization - Second Edition (By Cyrille Rossant)](https://github.com/ipython-books/minibook-2nd-code)
*  [IPython Interactive Computing and Visualization Cookbook  (By Cyrille Rossant)](https://github.com/ipython-books/cookbook-code)
*  [Python Cookbook, 3rd Edition by David Beazley; Brian K. Jones](https://github.com/dabeaz/python-cookbook)
*  [Python for Data Analysis by Wes McKinney](https://github.com/wesm/pydata-book)

### Other Resources

* Idea
    - [Google Search](http://www.google.com)
* Text
    - [Wikipedia](https://www.wikipedia.org/)
* Image
    - [Google Images](https://www.google.com/imghp)
* Video
    - [YouTube](https://www.youtube.com/)

## Contributors

* wen.gong@oracle.com (first created on 2017-03-09)

## Appendix

### How to install Jupyter Notebook

I use Windows and follow this link to [install Ananconda Python distribution](https://www.tensorflow.org/install/)

* Follow the instructions on the [Anaconda download site](https://www.continuum.io/downloads) to download and install Anaconda.

* open a DOS command box, launch jupyter by typing
    > jupyter notebook

* wait till Jupyter homepage to open in a browser

* start to create your own Notebook

### How to share a notebook

share your learning or work via nbviewer at http://nbviewer.jupyter.org/

```python
HTML("")
```

```python

```

```python

```


```python
import rospy
import tf
from kuka_arm.srv import *
from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
from geometry_msgs.msg import Pose
from mpmath import *
from sympy import *
import numpy
import math

import collections
import types
import traceback
import pprint
pp = pprint.PrettyPrinter(indent=4)

#import sys
#sys.path.append("./kuka_arm/scripts/")

import IK_utils as iku
import IK_server as ik
#from IK_server import *
%load_ext autoreload
#%autoreload # Reload all modules now
%autoreload 1 # Reload all modules imported with %aimport every time before executing the Python code typed.
#%autoreload 2 # Reload all modules (except those excluded by %aimport) every time before executing the Python code typed.
%aimport IK_server
%aimport IK_utils
#List modules on the auto list
%aimport
```

    The autoreload extension is already loaded. To reload it, use:
      %reload_ext autoreload
    Modules to reload:
    IK_server IK_utils

    Modules to skip:

```python
%%HTML
<style> code {background-color : orange !important;} </style>
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))
```

<style> code {background-color : orange !important;} </style>
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))

```python

dictTestCases = {
    "JAllZero":[[[2.153, 0.0, 1.946], [0, 0, 0, 1.0]], # Gripper link (aka End Effector/EE) Requested Pose [Position, OrientationQuat]
                  [1.85, 0, 1.95], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]],  # InverseKinematic response [WC/Joint5 position, Joint1:6 angles]

    "2_-0p5":[[[1.359, 0.0, 2.664], [0, -0.2472, 0, 0.96891]], # Gripper link (aka End Effector/EE) Requested Pose [Position, OrientationQuat]
                  [1.093, 0, 2.519], [0.0, -0.5, 0.0, 0.0, 0.0, 0.0]],  # InverseKinematic response [WC/Joint5 position, Joint1:6 angles]

    "3_-1p0":[[[1.3696, 0.0, 3.488], [0, -0.47942, 0, 0.87758]], # Gripper link (aka End Effector/EE) Requested Pose [Position, OrientationQuat]
                  [1.2059, 0, 3.233], [0.0, 0.0, -1.0, 0.0, 0.0, 0.0]],  # InverseKinematic response [WC/Joint5 position, Joint1:6 angles]

    1:[[[2.16135,-1.42635,1.55109], [0.708611,0.186356,-0.157931,0.661967]],
                  [1.89451,-1.44302,1.69366], [-0.65,0.45,-0.36,0.95,0.79,0.49]],

    2:[[[-0.56754,0.93663,3.0038], [0.62073, 0.48318,0.38759,0.480629]],
                  [-0.638,0.64198,2.9988], [-0.79,-0.11,-2.33,1.94,1.14,-3.68]],

    3:[[[-1.3863,0.02074,0.90986], [0.01735,-0.2179,0.9025,0.371016]],
                  [-1.1669,-0.17989,0.85137], [-2.99,-0.12,0.94,4.06,1.29,-4.12]],
               }

```

```python
%autoreload
def RunSingleTestCase(testCase):
    jointAnglesExpRad, wristCenterExp, poseReqRaw = iku.ExtractRawTestValues(testCase)
    poseReq = iku.ConvertRawToPose(poseReqRaw)
    if (ik.g_debugPrint):
        print("@@@@@@@@@@@@@@@@@@@@@@@@ debugPrint @@@@@@@@@@@@@@@@@@@@@@@@")
        print("JAExpRad", jointAngleExpRad)

    req = type("IKReqest", (object,), {})
    req.poses = [poseReq]

    if (True):
        print("===================== ik ==========================")
        wristCenterCalcList, jointAnglesCalcRad = ik.IKCalculateJointAnglesFromPose(poseReq, ik.g_tf, ik.dh, ik.g_dictDHVals) # HERE IT IS FINALLY
        poseCalcUsingExpJA = ik.FKCalcPosefromJointAnglesArr(jointAnglesExpRad)
        poseCalcUsingCalcJA = ik.FKCalcPosefromJointAnglesArr(jointAnglesCalcRad)

    if (False):
        print("@@@@@@@@@@@@@@@@@@@@@@@@ debugPrint @@@@@@@@@@@@@@@@@@@@@@@@")
        print("JACalc[0] ", jointAnglesCalcRad[0])
        print("JACalc ", jointAnglesCalcRad)

    if (False):
        iku.FKCalcAllPosesfromJointAnglesArr(jointAnglesExpRad)

    print("======== RequestedPose:")
    print(poseReq)
    iku.PrintCompare("wristCenter", wristCenterCalcList, wristCenterExp)
    iku.PrintCompare("jointAnglesRad", jointAnglesCalcRad, jointAnglesExpRad)
    iku.PrintCompare("jointAnglesDeg", map(degrees,jointAnglesCalcRad), map(degrees, jointAnglesExpRad))
    iku.PrintCompare("Confirm Gripper FKPositions(expectedJointAngles)", iku.ConvertPoseToRawPosition(poseCalcUsingExpJA), iku.ConvertPoseToRawPosition(poseReq))
    iku.PrintCompare("Confirm Gripper FKQuaternions(expectedJointAngles)", iku.ConvertPoseToRawQuaternion(poseCalcUsingExpJA), iku.ConvertPoseToRawQuaternion(poseReq))
    iku.PrintCompare("Confirm Gripper FKPositions(calcJointAngles)", iku.ConvertPoseToRawPosition(poseCalcUsingCalcJA), iku.ConvertPoseToRawPosition(poseReq))
    iku.PrintCompare("Confirm Gripper FKQuaternions(calcJointAngles)", iku.ConvertPoseToRawQuaternion(poseCalcUsingCalcJA), iku.ConvertPoseToRawQuaternion(poseReq))
    return()
```

    Started individual transform processing...
    Finished.
    saving tfFile /home/cl/catkin_ws/src/RoboND-Kinematics-Project/kuka_arm/scripts/TransformContainer.pypickle

```python
%autoreload
testCase = dictTestCases["3_-1p0"]
#testCase = dictTestCases["JAllZero"]
#testCase = dictTestCases["2_-0p5"]
testCase = dictTestCases[1]
testCase = dictTestCases[2]
#testCase = dictTestCases[3]

RunSingleTestCase(testCase)
```

    ===================== ik ==========================
    ======== RequestedPose:
    position:
      x: -0.56754
      y: 0.93663
      z: 3.0038
    orientation:
      x: 0.62073
      y: 0.48318
      z: 0.38759
      w: 0.480629
    ========= wristCenter calc/expect/err:
    ['  -0.6380', '  +0.6420', '  +2.9987']
    ['  -0.6380', '  +0.6420', '  +2.9988']
    ['  +0.0000', '  -0.0000', '  +0.0001']

    ========= jointAnglesRad calc/expect/err:
    ['  +2.3531', '  -0.3893', '  -0.4617', '  -1.1464', '  +1.2034', '  +2.4552']
    ['  -0.7900', '  -0.1100', '  -2.3300', '  +1.9400', '  +1.1400', '  -3.6800']
    ['  -3.1431', '  +0.2793', '  -1.8683', '  +3.0864', '  -0.0634', '  -6.1352']

    ========= jointAnglesDeg calc/expect/err:
    ['+134.8227', ' -22.3041', ' -26.4516', ' -65.6836', ' +68.9499', '+140.6754']
    [' -45.2637', '  -6.3025', '-133.4992', '+111.1538', ' +65.3172', '-210.8485']
    ['-180.0863', ' +16.0015', '-107.0476', '+176.8374', '  -3.6327', '-351.5239']

    ========= Confirm Gripper FKPositions(expectedJointAngles) calc/expect/err:
    ['  -0.5629', '  +0.9329', '  +3.0076']
    ['  -0.5675', '  +0.9366', '  +3.0038']
    ['  -0.0047', '  +0.0037', '  -0.0038']

    ========= Confirm Gripper FKQuaternions(expectedJointAngles) calc/expect/err:
    ['  +0.6200', '  +0.4838', '  +0.3894', '  +0.4794']
    ['  +0.6207', '  +0.4832', '  +0.3876', '  +0.4806']
    ['  +0.0007', '  -0.0006', '  -0.0019', '  +0.0012']

    ========= Confirm Gripper FKPositions(calcJointAngles) calc/expect/err:
    ['  -0.5675', '  +0.9366', '  +3.0038']
    ['  -0.5675', '  +0.9366', '  +3.0038']
    ['  +0.0000', '  -0.0000', '  +0.0000']

    ========= Confirm Gripper FKQuaternions(calcJointAngles) calc/expect/err:
    ['  +0.6207', '  +0.4832', '  +0.3876', '  +0.4806']
    ['  +0.6207', '  +0.4832', '  +0.3876', '  +0.4806']
    ['  -0.0000', '  -0.0000', '  -0.0000', '  -0.0000']

    ()

```python
%autoreload
def RunAllTestCases(dictTestCases):
    for keyTestIndex, testCase in dictTestCases.iteritems():
        print("############################ Running test case {0} #####################################".format(keyTestIndex))
        RunSingleTestCase(testCase)
RunAllTestCases(dictTestCases)
```


```python
from sympy.matrices import Matrix, eye, zeros, ones, diag, GramSchmidt
import sympy as sp
import numpy as np
from Exercise import Exercise, Page, MarkdownBlock

from config import URL, TOKEN

Exercise.URL = URL
Exercise.TOKEN = TOKEN
```

```python
# Note: must use raw string here to escape backslashes
s = r"""
##Heading

What is $1 + 1$

$$1 + 1$$

$$
\overleftrightarrow{ABC}
$$

$$
\begin{bmatrix}
1 & 2 & 3\\
a & b & c
\end{bmatrix}
$$

$$
\frac{1}{2}
$$

"""

import re

m = MarkdownBlock(s)
e = Exercise(m)
e.play()
e.write("test")
print(e.data)
```

    Published succesfully, preview at: https://www.mscthesis.nl/preview?id=d3d6ac37-411c-49a5-81cc-a5697179431f
    {'id': '21036cf5-78b9-47a5-8952-d88e1453af88', 'html': '
Heading
\n
What is $1 + 1$
\n$$1 + 1$$\n$$\n\\overleftrightarrow{ABC}\n$$\n$$\n\\begin{bmatrix}\n1 & 2 & 3\\\\\na & b & c\n\\end{bmatrix}\n$$\n$$\n\\frac{1}{2}\n$$', 'default_feedback': 'Incorrect, no specific feedback provided matching your answer', 'answers': []}

```python
Exercise("hello").play)
```

```python
# direct-binding
x = []
for i in range(5):
    x.append((MarkdownBlock("###Header only"), [Exercise(f"Exercise {i},{j}") for j in range(3)]))
```

```python
# with reference-binding, 1 page (subsection)
{"title": "page_title", "content": [("textblock_id_0", ["exercise_id_0", "exercise_id_1"]), ("textblock_id_1", ["exercise_id_2", "exercise_id_3"])]}
```

```python
# with reference-binding, multi-page (section)
{"title": "section_0", "subsections": ["subsection_ref_id_0", "subsection_ref_id_1"]}

# Book / Course / Learning module
{
    "title": "Applied Linear Algebra for Machine Learning",
    "chapters": [
        {"title": "Elementary mathematical objects and operations",
        "sections": [{
            "title": "Scalar, Vector, Matrix",
            "subsections"]}
    ]
}

- "Applied Linear Algebra for Machine Learning"
    - "Chapter 1: Basic data types and operations"
        "Section 1: ..."
```

```python
s = r"""
##Problem Overview
Some text here...

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

"""

m = MarkdownBlock(s)
m.write("Problem Overview")

s = r"""
##The MNIST Dataset

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

<p align="center">

    <span class="img_caption" style="display: block; text-align: center;">Examples of hand-written digits</span>
</p>
"""

m = MarkdownBlock(s)
m.write("The MNIST Dataset")

s = r"""
##Problem Formalization

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

<p align="center">

    <span class="img_caption" style="display: block; text-align: center;">Examples of hand-written digits</span>
</p>
"""

m = MarkdownBlock(s)
m.write("Problem Formalization")

e = Exercise("What is $1 + 1$?")
e.add_answer(2, True, "Yes!")
e.write("add1")
e.write("add2")
e.write("add3")
```

```python
# Note: must use raw string here to escape backslashes
s = r"""
##Matrix Basics
"""

import re

m = MarkdownBlock(s)
m.write("Matrix Basics")

```

```python

```


# Tutorial-IllinoisGRMHD: reconstruct_set_of_prims_PPM.C

## Authors: Leo Werneck & Zach Etienne

**This module is currently under development**

## In this tutorial module we explain the piecewise parabolic method (PPM) used to reconstruct the primitive variables within 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/.

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

This module is organized as follows

0. [Step 0](#src_dir): **Source directory creation**
1. [Step 1](#introduction): **Introduction**
    1. [Step 1.a](#ppm): *The Piecewise Parabolic Method (PPM)*
    1. [Step 1.b](#loop_defines_reconstruction): *The `loop_defines_reconstruction.h` header file*
    1. [Step 1.c](#preamble_reconstruct_set_of_prims_ppm): *The preamble to the `reconstruct_set_of_prims_PPM.C` code file*
1. [Step 2](#reconstruct_set_of_prims_ppm): **The `reconstruct_set_of_prims_PPM()` function**
    1. [Step 2.a](#reading_the_input_gfs): *Reading the input gridfunctions*
    1. [Step 2.b](#computation_of_du): *Evaluation of $\delta U_{i}$*
    1. [Step 2.c](#computation_of_ur_and_ul): *Computing $U_{r}$ and $U_{l}$*
    1. [Step 2.d](#steepening_rhob): *Steepening $\rho_{b}$*
    1. [Step 2.e](#flattening_and_monotonizing): *Flattening and monotonizing*
    1. [Step 2.f](#shifting_ur_and_ul): *Shifting $U_{r}$ and $U_{l}$*
1. [Step 3](#slope_limit): **The `slope_limit()` function**
1. [Step 4](#steepen_rho): **The `steepen_rho()` function**
1. [Step 5](#monotonize): **The `monotonize()` function**
1. [Step 6](#compute_p_cold__Gamma_cold): **The `compute_P_cold__Gamma_cold()` function**
1. [Step 7](#ftilde_gf_compute): **The `ftilde_gf_compute()` function**
1. [Step 8](#ftilde_compute): **The `ftilde_compute()` function**
1. [Step 9](#code_validation): **Code validation**
    1. [Step 9.a](#loop_defines_reconstruction__h_validation): *`loop_defines_reconstruction.h`*
    1. [Step 9.b](#reconstruct_set_of_prims_ppm__c_validation): *`reconstruct_set_of_prims_PPM.C`*
1. [Step 10](#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: Add NRPy's directory to the path
# https://stackoverflow.com/questions/16780014/import-file-from-parent-directory
import os,sys
nrpy_dir_path = os.path.join("..","..")
if nrpy_dir_path not in sys.path:
    sys.path.append(nrpy_dir_path)

# Step 0b: Load up cmdline_helper and create the directory
import cmdline_helper as cmd
outdir = os.path.join("..","src")
cmd.mkdir(outdir)
```

# Step 1: Introduction \[Back to [top](#toc)\]
$$\label{introduction}$$

In this tutorial module we will go through the implementation of the piecewise parabolic method (PPM), introduced by [Colella & Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf) (which shall henceforth be our main reference), used by `IllinoisGRMHD`.

## Step 1.a: The Piecewise Parabolic Method (PPM) \[Back to [top](#toc)\]
$$\label{ppm}$$

The piecewise parabolic method (PPM) is an algorithm used to construct the values of primitive variables, $U$, at cell interfaces. The interpolation procedure alone can lead to unstable evolutions. To remedy this, we introduce three different techniques:

1. Steepening
1. Flatenning
1. Monotonizing

These algorithms are intended to also produce narrower profiles near the vicinity of a shock. These steps tend to reduce the third-order accuracy of the interpolation code, but only in cases where a third-order interpolation algorithm would produce *worse* results (e.g. at local extrema).

The algorithmic flow of the code is as follows:

1. **Read the input**: determine which primitives are to be "reconstructed" (i.e. interpolated)
2. **Slope-limited gradient**: this must be computed for each of the primitives that will be reconstructed. We have:

$$
\boxed{\delta U^{\rm slope-lim} \equiv
\left\{
\begin{matrix}
{\rm sign}\left(\delta_{m} U_{i}\right)\min\left(\left|\delta_{m} U_{i}\right|,c\left|\delta U_{i}\right|,c\left|\delta U_{i+1}\right|\right) & ,\ {\rm if}\ dU_{i}dU_{i+1} > 0\\
0 &,\ {\rm otherwise}
\end{matrix}
\right.}\ ,
$$

   where $\delta U^{\rm slope-lim}$ is referred to as the slope-limited gradient of $U$ and

\begin{align}
\delta U_{i} &\equiv U_{i} - U_{i-1}\ ,\\
\delta_{m} U_{i} &\equiv \frac{\delta U_{i} + \delta U_{i+1}}{2} = \frac{U_{i+1} - U_{i-1}}{2}\ .
\end{align}

3. **Perform the interpolation**: We wish to determine $U_{r}$ and $U_{l}$, the values of $U$ at the cell interfaces. From the given set of known values $\left\{U_{i-2},U_{i-1},U_{i},U_{i+1},U_{i+2}\right\}$, one *interpolates* (with third-order accuracy) the values

$$
\begin{matrix}
U_{i+1/2} \equiv U_{r,i} = U_{i+0} + \frac{1}{2}\left(U_{i+1} - U_{i+0}\right) + \frac{1}{6}\left(\delta U^{\rm slope-lim}_{i+0} - \delta U^{\rm slope-lim}_{i+1}\right)\\
U_{i-1/2} \equiv U_{l,i} = U_{i-1} + \frac{1}{2}\left(U_{i+0} - U_{i-1}\right) + \frac{1}{6}\left(\delta U^{\rm slope-lim}_{i-1} - \delta U^{\rm slope-lim}_{i+0}\right)
\end{matrix}
$$

4. **Compute $P_{\rm cold}$ and $\Gamma_{\rm cold}$**: In order to decide whether or not to apply the steepening procedure, we must evaluate the contact discontinuity condition:

$$
\boxed{\Gamma_{\rm cold} K_{0}\frac{\left|\rho_{i+1}-\rho_{i-1}\right|}{\min\left(\rho_{i+1},\rho_{i-1}\right)} \geq \frac{\left|\left(P_{\rm cold}\right)_{i+1}-\left(P_{\rm cold}\right)_{i-1}\right|}{\min\left[\left(P_{\rm cold}\right)_{i+1},\left(P_{\rm cold}\right)_{i-1}\right]}}\ ,
$$

with $K_{0}$ a problem dependent constant.

5. **Steepening**: *if necessary*, apply the steepening proceedure *only* to $U = \rho_{b}^{\color{red}\ddagger}$. This involves performing the replacement

$$
\boxed{
\begin{matrix}
\rho_{r}\to \rho_{r}(1-\eta) + \rho^{\rm MC}_{r}\eta\\
\rho_{l}\to \rho_{l}(1-\eta) + \rho^{\rm MC}_{l}\eta
\end{matrix}
}\ ,
$$

where

\begin{align}
\rho^{\rm MC}_{r,i+1} &= \rho_{i+1} - \frac{1}{2}\delta\rho^{\rm slope-lim}_{i+1}\ ,\\
\rho^{\rm MC}_{l,i+0} &= \rho_{i-1} + \frac{1}{2}\delta\rho^{\rm slope-lim}_{i-1}\ ,\\
\eta_{i} &= \max\left\{0,\min\left[\eta_{1}\left(\tilde\eta_{i}-\eta_{2}\right),1\right]\right\}\ ,
\end{align}

with $\eta_{1}$ and $\eta_{2}$ constants and

$$
\tilde\eta_{i} =
\left\{
\begin{matrix}
0&, \ {\rm if}\ \delta\rho_{i} = 0\ ,\\
-\frac{1}{6}\left(\frac{\delta^{2}\rho_{i+1} - \delta^{2}\rho_{i-1}}{2\delta\rho_{i}}\right)&, \ {\rm otherwise}\ ,
\end{matrix}
\right.
$$

and finally

\begin{align}
\delta\rho_{i+0} &= \frac{\rho_{i+1} - \rho_{i-1}}{2}\ ,\\
\delta^{2}\rho_{i-1} &= \rho_{i+0} - 2\rho_{i-1} + \rho_{i-2}\ ,\\
\delta^{2}\rho_{i+1} &= \rho_{i+2} - 2\rho_{i+1} + \rho_{i+0}\ .
\end{align}

$^{\color{red}\ddagger}$: note that the common notation is $\rho_{0}$, but we will use $\rho_{b}$ to represent the baryonic matter density.

6. **Flattening**: *if necessary*, apply the flattening procedure to *all* primitives which are to be reconstructed. In the flattening procedure we modify either $U_{r}$, $U_{l}$, or both of them, according to

$$
\boxed{
\begin{matrix}
U_{r,i+0} = U_{i+0}\tilde{f} + U_{r,i+0}\left(1-\tilde{f}\right)\\
U_{l,i+0} = U_{i+0}\tilde{f} + U_{l,i+0}\left(1-\tilde{f}\right)
\end{matrix}
}\ ,
$$

where

\begin{align}
\tilde{f} &= \min\left[1,w\max\left(0,q_{1}\right)\right]\ ,\\
w &=
\left\{
\begin{matrix}
1\ , &\ {\rm if}\ q_{2} > \epsilon_{2}\ {\rm and}\ q_{2}\left(v^{\rm flux\ dirn}_{i-1}-v^{\rm flux\ dirn}_{i+1}\right)>0\ \left({\rm inside\ shock}\right)\ ,\\
0\ , &\ {\rm otherwise}\ \left({\rm outside\ shock}\right)\ ,
\end{matrix}
\right.
\end{align}

and

\begin{align}
q_{1} &= \left(\frac{\delta P_{1}}{\delta P_{2}}-\omega_{1}\right)\omega_{2}\ ,\\
q_{2} &= \frac{\left|\delta P_{1}\right|}{\min\left(P_{i+1},P_{i-1}\right)}\ ,
\end{align}

with $\omega_{1}$ and $\omega_{2}$ constants and $\delta P_{n} \equiv P_{i+n} - P_{i-n}$.

7. **Monotonizing**: *if necessary*, apply the monotonizing procedure to *all* primitives which are to be reconstructed. We check three different cases, modifying $U_{r}$ and $U_{l}$ as follows:

$$
\boxed{
\begin{matrix}
\text{Case 1: if}\ \left(U_{r} - U\right)\left(U - U_{l}\right)\leq 0 \ \text{then:}\
\left\{
\begin{matrix}
U_{r} \to U\ ,\\
\ U_{l}\to U\ .
\end{matrix}
\right.\\
\text{Case 2: if}\ \delta U\left(U - \delta_{m}U\right) > \frac{\left(\delta U\right)^{2}}{6} \ \text{then:}\
\left\{
\begin{matrix}
U_{r} \to U_{r}\ ,\\
\ U_{l}\to 3U-2U_{r}\ .
\end{matrix}
\right.\\
\text{Case 3: if}\ \delta U\left(U - \delta_{m}U\right) < -\frac{\left(\delta U\right)^{2}}{6} \ \text{then:}\
\left\{
\begin{matrix}
U_{r} \to 3U-2U_{l}\ ,\\
\ U_{l}\to U_{l}\ .
\end{matrix}
\right.
\end{matrix}
}\ ,
$$

where

\begin{align}
\delta U &\equiv U_{r} - U_{l}\ ,\\
\delta_{m}U &\equiv \frac{U_{r} + U_{l}}{2}\ .
\end{align}

8. **Index shifting**: shift the indices of $U_{r}$ and $U_{l}$. We have, at this point

\begin{align}
U_{r,i} = U_{i+1/2}\ ,\\
U_{l,i} = U_{i-1/2}\ .
\end{align}

We then perform the following shift

$$
\boxed{
\begin{matrix}
U_{i-1/2+\epsilon} = U_{l,i}^{\rm old} = U_{r,i}^{\rm new}\ ,\\
U_{i-1/2-\epsilon} = U_{r,i-1}^{\rm old} = U_{l,i}^{\rm new}\ .\\
\end{matrix}
}
$$

## Step 1.b: The `loop_defines_reconstruction.h` header file \[Back to [top](#toc)\]
$$\label{loop_defines_reconstruction}$$

This header file defines useful quantities to be used throughout the `reconstruct_set_of_prims_PPM.C` code. They are:

1. LOOP_DEFINE $\rightarrow$ sets up a loop over the $x$, $y$, and $z$ directions, including the ghostzones
1. SET_INDEX_ARRAYS $\rightarrow$ for a given direction, $x^{j}$, and coordinate range, $i\in\left[i_\min,i_\max\right]$, finds the appropriate array indices
1. SET_INDEX_ARRAYS_3DBLOCK $\rightarrow$ finds the appropriate array indices in all directions of a given range

```python
%%writefile $outdir/loop_defines_reconstruction.h
#ifndef LOOP_DEFINES_RECONSTRUCTION_H_
#define LOOP_DEFINES_RECONSTRUCTION_H_

#define LOOP_DEFINE(gz_shift_lo,gz_shift_hi,  ext,flux_dirn,  ijkgz_lo_hi,gz_lo,gz_hi) \
  for(int rr=1;rr<=3;rr++) {                                            \
    ijkgz_lo_hi[rr][0]=          gz_lo[rr];                             \
    ijkgz_lo_hi[rr][1]=ext[rr-1]-gz_hi[rr];                             \
  }                                                                     \
  ijkgz_lo_hi[flux_dirn][0] += gz_shift_lo;                             \
  ijkgz_lo_hi[flux_dirn][1] -= gz_shift_hi;                             \
  /* The following line is valid C99 */                                 \
  _Pragma("omp parallel for private(U,dU,slope_lim_dU,Ur,Ul)")          \
  for(int k=ijkgz_lo_hi[3][0];k<ijkgz_lo_hi[3][1];k++)                  \
    for(int j=ijkgz_lo_hi[2][0];j<ijkgz_lo_hi[2][1];j++)                \
      for(int i=ijkgz_lo_hi[1][0];i<ijkgz_lo_hi[1][1];i++)

// This define only sets indices.
// FIXME: benchmark with and without the if() statement.
// FIXME: try without index_arr being defined in all directions.
#define SET_INDEX_ARRAYS(IMIN,IMAX,flux_dirn)                           \
  int max_shift=(MAXNUMINDICES/2);                                      \
  /* DEBUGGING ONLY:  if(IMIN<-max_shift || IMAX>max_shift) CCTK_VError(VERR_DEF_PARAMS,"FIX MAXNUMINDICES!"); */ \
  int index_arr[4][MAXNUMINDICES];                                      \
  for(int idx=IMIN;idx<=IMAX;idx++) {                                   \
    index_arr[flux_dirn][idx+max_shift]=                                \
      CCTK_GFINDEX3D(cctkGH,                                            \
                     i+idx*kronecker_delta[flux_dirn][0],               \
                     j+idx*kronecker_delta[flux_dirn][1],               \
                     k+idx*kronecker_delta[flux_dirn][2]);              \
  }

#define SET_INDEX_ARRAYS_3DBLOCK(IJKLOHI)                               \
  int max_shift=(MAXNUMINDICES/2);                                      \
  int index_arr_3DB[MAXNUMINDICES][MAXNUMINDICES][MAXNUMINDICES];       \
  for(int idx_k=IJKLOHI[4];idx_k<=IJKLOHI[5];idx_k++) for(int idx_j=IJKLOHI[2];idx_j<=IJKLOHI[3];idx_j++) for(int idx_i=IJKLOHI[0];idx_i<=IJKLOHI[1];idx_i++) { \
        index_arr_3DB[idx_k+max_shift][idx_j+max_shift][idx_i+max_shift]=CCTK_GFINDEX3D(cctkGH,i+idx_i,j+idx_j,k+idx_k); \
      }

#endif /* LOOP_DEFINES_RECONSTRUCTION_H_ */

```

    Overwriting ../src/loop_defines_reconstruction.h

## Step 1.c: The preamble to the `reconstruct_set_of_prims_PPM.C` code file \[Back to [top](#toc)\]
$$\label{preamble_reconstruct_set_of_prims_ppm}$$

We then initialize the `reconstruct_set_of_prims_PPM.C` code file with a basic preamble, some simple definitions, and function headers. Notice that these functions are defined in the other Steps of this tutorial notebook. See the [table of contents](#toc) above for more information.

```python
%%writefile $outdir/reconstruct_set_of_prims_PPM.C
/*****************************************
 * PPM Reconstruction Interface.
 * Zachariah B. Etienne (2013)
 *
 * This version of PPM implements the standard
 * Colella & Woodward PPM, though modified as in GRHydro
 * to have 3 ghostzones instead of 4.
 *****************************************/

#define MINUS2 0
#define MINUS1 1
#define PLUS0  2
#define PLUS1  3
#define PLUS2  4
#define MAXNUMINDICES 5
//      ^^^^^^^^^^^^^ Be _sure_ to define MAXNUMINDICES appropriately!

// You'll find the #define's for LOOP_DEFINE and SET_INDEX_ARRAYS inside:
#include "loop_defines_reconstruction.h"

static inline CCTK_REAL ftilde_compute(const int flux_dirn,const CCTK_REAL rho_b_atm,CCTK_REAL U[MAXNUMVARS][MAXNUMINDICES]);
static inline CCTK_REAL slope_limit(CCTK_REAL dU,CCTK_REAL dUp1);
static inline void steepen_rho(CCTK_REAL U[MAXNUMVARS][MAXNUMINDICES],CCTK_REAL slope_lim_dU[MAXNUMVARS][MAXNUMINDICES],
                               CCTK_REAL Gamma_th,CCTK_REAL P_cold,CCTK_REAL Gamma_cold,
                               CCTK_REAL *rho_br_ppm,CCTK_REAL *rho_bl_ppm);
static inline void compute_P_cold__Gamma_cold(CCTK_REAL rho_b,eos_struct &eos,   CCTK_REAL &P_cold,CCTK_REAL &Gamma_cold);
static inline void monotonize(CCTK_REAL U,CCTK_REAL &Ur,CCTK_REAL &Ul);
```

    Overwriting ../src/reconstruct_set_of_prims_PPM.C

# Step 2: The `reconstruct_set_of_prims_PPM()` function \[Back to [top](#toc)\]
$$\label{reconstruct_set_of_prims_ppm}$$

The `reconstruct_set_of_prims_PPM()` function receives as input the direction in which to perform the reconstruction ($\rm flux\_dirn$), the number of primitives which are to be reconstructed ($\rm num\_prims\_to\_reconstruct$), which primitives are to be reconstructed ($\rm which\_prims\_to\_reconstruct$), the equation of state ($\rm eos$), and the array which stores the primitives ($\rm in\_prims$). The reconstructed primitive values are then stored in the output arrays $\rm out\_prims\_r$ and $\rm out\_prims\_l$. **Note**: you can find more information on the ETK specific parameters (such as ${\rm cctkGH}$ and $\rm cctk\_lsh$) by looking at the [official Einstein Toolkit documentation](http://cactuscode.org/documentation/referencemanual/ReferenceManualch2.html).

Notice that we will start a loop that runs from 0 to $\rm num\_prims\_to\_reconstruct$, meaning that the discussion here will apply to *each* of the primitives that we wish to reconstruct.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

static void reconstruct_set_of_prims_PPM(const cGH *cctkGH,const int *cctk_lsh,const int flux_dirn,const int num_prims_to_reconstruct,const int *which_prims_to_reconstruct,eos_struct &eos,
                                         gf_and_gz_struct *IN_PRIMS,gf_and_gz_struct *OUT_PRIMS_R,gf_and_gz_struct *OUT_PRIMS_L,CCTK_REAL *ftilde_gf, CCTK_REAL *temporary) {

  DECLARE_CCTK_PARAMETERS;

  CCTK_REAL U[MAXNUMVARS][MAXNUMINDICES],dU[MAXNUMVARS][MAXNUMINDICES],slope_lim_dU[MAXNUMVARS][MAXNUMINDICES],
    Ur[MAXNUMVARS][MAXNUMINDICES],Ul[MAXNUMVARS][MAXNUMINDICES];
  int ijkgz_lo_hi[4][2];

  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    int whichvar=which_prims_to_reconstruct[ww];

    if(IN_PRIMS[whichvar].gz_lo[flux_dirn]!=0 || IN_PRIMS[whichvar].gz_hi[flux_dirn]!=0) {
      CCTK_VError(VERR_DEF_PARAMS,"TOO MANY GZ'S! WHICHVAR=%d: %d %d %d : %d %d %d DIRECTION %d",whichvar,
		  IN_PRIMS[whichvar].gz_lo[1],IN_PRIMS[whichvar].gz_lo[2],IN_PRIMS[whichvar].gz_lo[3],
		  IN_PRIMS[whichvar].gz_hi[1],IN_PRIMS[whichvar].gz_hi[2],IN_PRIMS[whichvar].gz_hi[3],flux_dirn);
    }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

## Step 2.a: Reading the input gridfunctions \[Back to [top](#toc)\]
$$\label{reading_the_input_gfs}$$

We start by reading in the input primitive gridfunction and storing them to a variable $U$. Notice that for a given direction and a given point $i$, we will know: $\left\{U_{i-2},U_{i-1},U_{i},U_{i+1},U_{i+2}\right\}$.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

    // *** LOOP 1: Interpolate to Ur and Ul, which are face values ***
    //  You will find that Ur depends on U at MINUS1,PLUS0, PLUS1,PLUS2, and
    //                     Ul depends on U at MINUS2,MINUS1,PLUS0,PLUS1.
    //  However, we define the below loop from MINUS2 to PLUS2. Why not split
    //     this up and get additional points? The reason is that later on,
    //     Ur and Ul depend on ftilde, which is defined from MINUS2 to PLUS2,
    //     so we would lose those points anyway.
    LOOP_DEFINE(2,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,IN_PRIMS[whichvar].gz_lo,IN_PRIMS[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(-2,2,flux_dirn);
      /* *** LOOP 1a: READ INPUT *** */
      // Read in a primitive at all gridpoints between m = MINUS2 & PLUS2, where m's direction is given by flux_dirn. Store to U.
      for(int ii=MINUS2;ii<=PLUS2;ii++) U[whichvar][ii] = IN_PRIMS[whichvar].gf[index_arr[flux_dirn][ii]];
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

## Step 2.b: Evaluation of $\delta U_{i}$ \[Back to [top](#toc)\]
$$\label{computation_of_du}$$

We will need $\delta U_{i} \equiv U_{i} - U_{i-1}$ in order to compute $U_{r}$ and $U_{l}$. We will then compute (notice the notation change $i\to i+0$ so that it is easier to understand the C code that follows):

\begin{align}
\delta U_{i-1} &= U_{i-1} - U_{i-2}\ ,\\
\delta U_{i+0} &= U_{i+0} - U_{i-1}\ ,\\
\delta U_{i+1} &= U_{i+1} - U_{i+0}  \ ,\\
\delta U_{i+2} &= U_{i+2} - U_{i+1}\ .
\end{align}

After evaluating the differences $\delta U$, we compute the slope-limited $\delta U$, $\delta U^{\rm slope-lim}$, using the [`slope_limit()` function](#slope_limit).

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

      /* *** LOOP 1b: DO COMPUTATION *** */
      /* First, compute simple dU = U(i) - U(i-1), where direction of i
       *         is given by flux_dirn, and U is a primitive variable:
       *         {rho_b,P,vx,vy,vz,Bx,By,Bz}. */
      // Note that for Ur and Ul at i, we must compute dU(i-1),dU(i),dU(i+1),
      //         and dU(i+2)
      dU[whichvar][MINUS1] = U[whichvar][MINUS1]- U[whichvar][MINUS2];
      dU[whichvar][PLUS0]  = U[whichvar][PLUS0] - U[whichvar][MINUS1];
      dU[whichvar][PLUS1]  = U[whichvar][PLUS1] - U[whichvar][PLUS0];
      dU[whichvar][PLUS2]  = U[whichvar][PLUS2] - U[whichvar][PLUS1];

      //static int iii=14, jjj=14, kkk=13;

      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("UUUU0 %d | %e %e %e %e %e\n",whichvar,U[whichvar][MINUS2],U[whichvar][MINUS1],U[whichvar][PLUS0],U[whichvar][PLUS1],U[whichvar][PLUS2]);

      // Then, compute slope-limited dU, using MC slope limiter:
      slope_lim_dU[whichvar][MINUS1]=slope_limit(dU[whichvar][MINUS1],dU[whichvar][PLUS0]);
      slope_lim_dU[whichvar][PLUS0] =slope_limit(dU[whichvar][PLUS0], dU[whichvar][PLUS1]);
      slope_lim_dU[whichvar][PLUS1] =slope_limit(dU[whichvar][PLUS1], dU[whichvar][PLUS2]);
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

## Step 2.c: Computing $U_{r}$ and $U_{l}$ \[Back to [top](#toc)\]
$$\label{computation_of_ur_and_ul}$$

We now compute $U_{r}$ and $U_{l}$. Keep in mind that $U_{r,i} = U_{i+1/2}$, while $U_{l,i} = U_{i-1/2}$. The implemented equation follows eq. A1 in [Duez *et al.* (2005)](http://arxiv.org/pdf/astro-ph/0503420.pdf), but with the standardd PPM coefficient of $\frac{1}{6}$ (i.e. eq. A1 with $\frac{1}{8}\to\frac{1}{6}$). Keep in mind that we simplify the equation slightly before implementing it:

\begin{align}
U_{r,i+0} &= U_{i+0} + \frac{1}{2}\left(U_{i+1} - U_{i+0}\right) + \frac{1}{6}\left(\delta U^{\rm slope-lim}_{i+0} - \delta U^{\rm slope-lim}_{i+1}\right) \Rightarrow \boxed{U_{r,i+0} = \frac{1}{2}\left(U_{i+1} + U_{i+0}\right) + \frac{1}{6}\left(\delta U^{\rm slope-lim}_{i+0} - \delta U^{\rm slope-lim}_{i+1}\right)}\ ,\\
U_{l,i+0} &= U_{i-1} + \frac{1}{2}\left(U_{i+0} - U_{i-1}\right) + \frac{1}{6}\left(\delta U^{\rm slope-lim}_{i-1} - \delta U^{\rm slope-lim}_{i+0}\right) \Rightarrow \boxed{U_{l,i+0} = \frac{1}{2}\left(U_{i+0} + U_{i-1}\right) + \frac{1}{6}\left(\delta U^{\rm slope-lim}_{i-1} - \delta U^{\rm slope-lim}_{i+0}\right)}\ .
\end{align}

After this step, the values of $U_{r,l,i+0}$ are stored as outputs.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

      // Finally, compute face values Ur and Ul based on the PPM prescription
      //   (Eq. A1 in http://arxiv.org/pdf/astro-ph/0503420.pdf, but using standard 1/6=(1.0/6.0) coefficient)
      // Ur[PLUS0] represents U(i+1/2)
      // We applied a simplification to the following line: Ur=U+0.5*(U(i+1)-U) + ... = 0.5*(U(i+1)+U) + ...
      Ur[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS1] + U[whichvar][PLUS0] ) + (1.0/6.0)*(slope_lim_dU[whichvar][PLUS0]  - slope_lim_dU[whichvar][PLUS1]);
      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("UURR0 %d | %e\n",whichvar,Ur[whichvar][PLUS0]);
      // Ul[PLUS0] represents U(i-1/2)
      // We applied a simplification to the following line: Ul=U(i-1)+0.5*(U-U(i-1)) + ... = 0.5*(U+U(i-1)) + ...
      Ul[whichvar][PLUS0] = 0.5*(U[whichvar][PLUS0] + U[whichvar][MINUS1]) + (1.0/6.0)*(slope_lim_dU[whichvar][MINUS1] - slope_lim_dU[whichvar][PLUS0]);
      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("UULL0 %d | %e\n",whichvar,Ul[whichvar][PLUS0]);

      /* *** LOOP 1c: WRITE OUTPUT *** */
      // Store right face values to {rho_br,Pr,vxr,vyr,vzr,Bxr,Byr,Bzr},
      //    and left face values to {rho_bl,Pl,vxl,vyl,vzl,Bxl,Byl,Bzl}
      OUT_PRIMS_R[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0];
      OUT_PRIMS_L[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0];
    }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

## Step 2.d: Steepening $\rho_{b}$ \[Back to [top](#toc)\]
$$\label{steepening_rhob}$$

Following the procedure described in [Step 4](#steepen_rho), we will now steepen $\rho_{b}$ using the [`steepen_rho()` function](#steepen_rho). Keep in mind that although we will loop over all primitives which are set for reconstruction, the steepening procedure is applied to $\rho_{b}$ *only*.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

    // *** LOOP 2: STEEPEN RHOB ***
    // Note that this loop applies ONLY to RHOB.
    if(whichvar==RHOB) {
      LOOP_DEFINE(2,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,IN_PRIMS[whichvar].gz_lo,IN_PRIMS[whichvar].gz_hi) {
	SET_INDEX_ARRAYS(-2,2,flux_dirn);
	// Set rho and P separately, since within this loop,
	//  1) steepen_rho() depends on RHOB(MINUS2,MINUS1,PLUS0,PLUS1,PLUS2)

	// Read in all primitives between MINUS2 & PLUS2. Store to U.
	for(int ii=MINUS2;ii<=PLUS2;ii++) U[RHOB][ii]     = IN_PRIMS[RHOB    ].gf[index_arr[flux_dirn][ii]];
	for(int ii=MINUS1;ii<=PLUS1;ii++) U[PRESSURE][ii] = IN_PRIMS[PRESSURE].gf[index_arr[flux_dirn][ii]];
	Ur[RHOB][PLUS0] = OUT_PRIMS_R[RHOB].gf[index_arr[flux_dirn][PLUS0]];
	Ul[RHOB][PLUS0] = OUT_PRIMS_L[RHOB].gf[index_arr[flux_dirn][PLUS0]];

	dU[whichvar][MINUS1] = U[whichvar][MINUS1]- U[whichvar][MINUS2];
	dU[whichvar][PLUS0]  = U[whichvar][PLUS0] - U[whichvar][MINUS1];
	dU[whichvar][PLUS1]  = U[whichvar][PLUS1] - U[whichvar][PLUS0];
	dU[whichvar][PLUS2]  = U[whichvar][PLUS2] - U[whichvar][PLUS1];

	slope_lim_dU[whichvar][MINUS1]=slope_limit(dU[whichvar][MINUS1],dU[whichvar][PLUS0]);
	//slope_lim_dU[whichvar][PLUS0] =slope_limit(dU[whichvar][PLUS0], dU[whichvar][PLUS1]);
	slope_lim_dU[whichvar][PLUS1] =slope_limit(dU[whichvar][PLUS1], dU[whichvar][PLUS2]);

	// Steepen rho
	// DEPENDENCIES: RHOB face values, RHOB(MINUS2,MINUS1,PLUS0,PLUS1,PLUS2), P(MINUS1,PLUS0,PLUS1), and slope_lim_dU[RHOB](MINUS1,PLUS1)
	CCTK_REAL P_cold,Gamma_cold;
	compute_P_cold__Gamma_cold(U[RHOB][PLUS0],eos,  P_cold,Gamma_cold);
	steepen_rho(U,slope_lim_dU,   Gamma_th,P_cold,Gamma_cold,  Ur[RHOB],Ul[RHOB]);

	// Output rho
	OUT_PRIMS_R[RHOB].gf[index_arr[flux_dirn][PLUS0]] = Ur[RHOB][PLUS0];
	OUT_PRIMS_L[RHOB].gf[index_arr[flux_dirn][PLUS0]] = Ul[RHOB][PLUS0];
      }
    }
  }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

## Step 2.e: Flattening and monotonizing  \[Back to [top](#toc)\]
$$\label{flattening_and_monotonizing}$$

The flattening procedure modifies $U_{r}$ and $U_{l}$ via

$$
\boxed{
\begin{matrix}
U_{r,i+0} = U_{i+0}\tilde{f} + U_{r,i+0}\left(1-\tilde{f}\right)\\
U_{l,i+0} = U_{i+0}\tilde{f} + U_{l,i+0}\left(1-\tilde{f}\right)
\end{matrix}
}\ ,
$$

where $\tilde{f}$ is computed by the `ftilde_compute()` function, described in [Step 8](#ftilde_compute).

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  /* ORIGINAL PPM REQUIRES AT LEAST 4 GHOSTZONES, which can add
   *  significantly to the size of AMR ref. boundaries.
   *  To reduce to 3 ghostzones, we comment the following lines out:
   * if ((P[indexp1] - P[indexm1]) <= 0.0) {
   * f = MAX(ftilde,ftilde_p1);
   * } else {
   * f = MAX(ftilde,ftilde_m1);
   * }
   */

  // *** LOOP 3: FLATTEN BASED ON FTILDE AND MONOTONIZE ***
  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    int whichvar=which_prims_to_reconstruct[ww];
    // ftilde() depends on P(MINUS2,MINUS1,PLUS1,PLUS2)
    LOOP_DEFINE(2,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,IN_PRIMS[whichvar].gz_lo,IN_PRIMS[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(0,0,flux_dirn);

      U[whichvar][PLUS0]  = IN_PRIMS[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      Ur[whichvar][PLUS0] = OUT_PRIMS_R[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      Ul[whichvar][PLUS0] = OUT_PRIMS_L[whichvar].gf[index_arr[flux_dirn][PLUS0]];

      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("READ %d | %e %e %e \n",whichvar,U[whichvar][PLUS0],Ur[whichvar][PLUS0],Ul[whichvar][PLUS0]);

      // ftilde_gf was computed in the function compute_ftilde_gf(), called before this routine
      CCTK_REAL ftilde = ftilde_gf[index_arr[flux_dirn][PLUS0]];
      // ...and then flatten (local operation)
      Ur[whichvar][PLUS0]   = U[whichvar][PLUS0]*ftilde + Ur[whichvar][PLUS0]*(1.0-ftilde);
      Ul[whichvar][PLUS0]   = U[whichvar][PLUS0]*ftilde + Ul[whichvar][PLUS0]*(1.0-ftilde);

      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("FLAT %d | %e %e %e %e \n",whichvar,U[whichvar][PLUS0],Ur[whichvar][PLUS0],Ul[whichvar][PLUS0],ftilde);
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Furthermore, we apply the `monotonize()` function, as described in [Step 5](#monotonize).

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

      // Then monotonize
      monotonize(U[whichvar][PLUS0],Ur[whichvar][PLUS0],Ul[whichvar][PLUS0]);

      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("MONO %d | %e %e \n",whichvar,Ur[whichvar][PLUS0],Ul[whichvar][PLUS0]);

      OUT_PRIMS_R[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ur[whichvar][PLUS0];
      OUT_PRIMS_L[whichvar].gf[index_arr[flux_dirn][PLUS0]] = Ul[whichvar][PLUS0];
    }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

## Step 2.f: Shifting $U_{r}$ and $U_{l}$  \[Back to [top](#toc)\]
$$\label{shifting_ur_and_ul}$$

At this point, we have

\begin{align}
U_{r,i} &= U_{i+1/2}\ ,\\
U_{l,i} &= U_{i-1/2}\ .
\end{align}

To keep things consistent, we shift indices to get

$$
\boxed{
\begin{matrix}
U_{i-1/2+\epsilon} = U_{l,i}^{\rm old} = U_{r,i}^{\rm new}\ ,\\
U_{i-1/2-\epsilon} = U_{r,i-1}^{\rm old} = U_{l,i}^{\rm new}\ ,\\
\end{matrix}
}
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

    // Ur depends on ftilde, which depends on points of U between MINUS2 and PLUS2
    OUT_PRIMS_R[whichvar].gz_lo[flux_dirn]+=2;
    OUT_PRIMS_R[whichvar].gz_hi[flux_dirn]+=2;
    // Ul depends on ftilde, which depends on points of U between MINUS2 and PLUS2
    OUT_PRIMS_L[whichvar].gz_lo[flux_dirn]+=2;
    OUT_PRIMS_L[whichvar].gz_hi[flux_dirn]+=2;
  }

  // *** LOOP 4: SHIFT Ur AND Ul ***
  /* Currently face values are set so that
   *      a) Ur(i) represents U(i+1/2), and
   *      b) Ul(i) represents U(i-1/2)
   *    Here, we shift so that the indices are consistent:
   *      a) U(i-1/2+epsilon) = oldUl(i)   = newUr(i)
   *      b) U(i-1/2-epsilon) = oldUr(i-1) = newUl(i)
   *    Note that this step is not strictly necessary if you keep
   *      track of indices when computing the flux. */
  for(int ww=0;ww<num_prims_to_reconstruct;ww++) {
    int whichvar=which_prims_to_reconstruct[ww];
    LOOP_DEFINE(3,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,IN_PRIMS[whichvar].gz_lo,IN_PRIMS[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(-1,0,flux_dirn);
      temporary[index_arr[flux_dirn][PLUS0]] = OUT_PRIMS_R[whichvar].gf[index_arr[flux_dirn][MINUS1]];
      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("TEMP %d || %d %d %d || %e\n",whichvar,index_arr[flux_dirn][PLUS0],index_arr[flux_dirn][MINUS1],CCTK_GFINDEX3D(cctkGH,14,14,13),temporary[index_arr[flux_dirn][PLUS0]]);
    }

    LOOP_DEFINE(3,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,IN_PRIMS[whichvar].gz_lo,IN_PRIMS[whichvar].gz_hi) {
      SET_INDEX_ARRAYS(0,0,flux_dirn);
      // Then shift so that Ur represents the gridpoint at i-1/2+epsilon,
      //                and Ul represents the gridpoint at i-1/2-epsilon.
      // Ur(i-1/2) = Ul(i-1/2)     = U(i-1/2+epsilon)
      // Ul(i-1/2) = Ur(i+1/2 - 1) = U(i-1/2-epsilon)
      OUT_PRIMS_R[whichvar].gf[index_arr[flux_dirn][PLUS0]] = OUT_PRIMS_L[whichvar].gf[index_arr[flux_dirn][PLUS0]];
      OUT_PRIMS_L[whichvar].gf[index_arr[flux_dirn][PLUS0]] = temporary[index_arr[flux_dirn][PLUS0]];
      //if( (i==iii) && (j==jjj) && (k==kkk) ) printf("LAST %d | %d %d %d || %d | %e %e %e \n",whichvar,i,j,k,flux_dirn,OUT_PRIMS_R[whichvar].gf[index_arr[flux_dirn][PLUS0]],OUT_PRIMS_L[whichvar].gf[index_arr[flux_dirn][PLUS0]],temporary[index_arr[flux_dirn][PLUS0]]);
    }
    // Ul was just shifted, so we lost another ghostzone.
    OUT_PRIMS_L[whichvar].gz_lo[flux_dirn]+=1;
    OUT_PRIMS_L[whichvar].gz_hi[flux_dirn]+=0;
    // As for Ur, we didn't need to get rid of another ghostzone,
    //    but we did ... seems wasteful!
    OUT_PRIMS_R[whichvar].gz_lo[flux_dirn]+=1;
    OUT_PRIMS_R[whichvar].gz_hi[flux_dirn]+=0;

  }
}
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 3: The `slope_limit()` function \[Back to [top](#toc)\]
$$\label{slope_limit}$$

We will now show the definition of $\delta U^{\rm slope-lim}$. The reason why we introduce this slope-limited procedure is twofold. First, it leads to steeper representations of discontinuities, and second it guarantees that $U_{i+1/2}$ lies inside the range $\left[U_{i},U_{i+1}\right]$.

We start by defining

\begin{align}
\delta U_{i} &\equiv U_{i} - U_{i-1}\ ,\\
\delta_{m} U_{i} &\equiv \frac{\delta U_{i} + \delta U_{i+1}}{2} = \frac{U_{i+1} - U_{i-1}}{2}\ .
\end{align}

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

// Set SLOPE_LIMITER_COEFF = 2.0 for MC, 1 for minmod
#define SLOPE_LIMITER_COEFF 2.0

//Eq. 60 in JOURNAL OF COMPUTATIONAL PHYSICS 123, 1-14 (1996)
//   [note the factor of 2 missing in the |a_{j+1} - a_{j}| term].
//   Recall that dU = U_{i} - U_{i-1}.
static inline CCTK_REAL slope_limit(CCTK_REAL dU,CCTK_REAL dUp1) {
  if(dU*dUp1 > 0.0) {
    //delta_m_U=0.5 * [ (u_(i+1)-u_i) + (u_i-u_(i-1)) ] = (u_(i+1) - u_(i-1))/2  <-- first derivative, second-order; this should happen most of the time (smooth flows)
    CCTK_REAL delta_m_U = 0.5*(dU + dUp1);
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Next we implement the slope-limited $\delta U$ as

$$
\boxed{\delta U^{\rm slope-lim} \equiv
\left\{
\begin{matrix}
{\rm sign}\left(\delta_{m} U_{i}\right)\min\left(\left|\delta_{m} U_{i}\right|,c\left|\delta U_{i}\right|,c\left|\delta U_{i+1}\right|\right) & ,\ {\rm if}\ dU_{i}dU_{i+1} > 0\\
0 &,\ {\rm otherwise}
\end{matrix}
\right.}\ .
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

    // EXPLANATION OF BELOW LINE OF CODE.
    // In short, sign_delta_a_j = sign(delta_m_U) = (0.0 < delta_m_U) - (delta_m_U < 0.0).
    //    If delta_m_U>0, then (0.0 < delta_m_U)==1, and (delta_m_U < 0.0)==0, so sign_delta_a_j=+1
    //    If delta_m_U<0, then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==1, so sign_delta_a_j=-1
    //    If delta_m_U==0,then (0.0 < delta_m_U)==0, and (delta_m_U < 0.0)==0, so sign_delta_a_j=0
    int sign_delta_m_U = (0.0 < delta_m_U) - (delta_m_U < 0.0);
    //Decide whether to use 2nd order derivative or first-order derivative, limiting slope.
    return sign_delta_m_U*MIN(fabs(delta_m_U),MIN(SLOPE_LIMITER_COEFF*fabs(dUp1),SLOPE_LIMITER_COEFF*fabs(dU)));
  }
  return 0.0;
}
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 4: The `steepen_rho()` function \[Back to [top](#toc)\]
$$\label{steepen_rho}$$

The steepening procedure withing the PPM algorithm is applied only to $\rho_{b}$. The idea here is to produce narrower profiles near the vicinity of a contact discontinuity.

**A NOTE ON NOTATION**: in the discussion below we will refer to $\rho$ as $\rho$ to keep the notation a bit lighter. No confusion should arise from this since there is no other quantity $\rho$ involved.

We start the algorithm by computing

\begin{align}
\delta\rho_{i+0} &= \frac{\rho_{i+1} - \rho_{i-1}}{2}\ ,\\
\delta^{2}\rho_{i-1} &= \rho_{i+0} - 2\rho_{i-1} + \rho_{i-2}\ ,\\
\delta^{2}\rho_{i+1} &= \rho_{i+2} - 2\rho_{i+1} + \rho_{i+0}\ .
\end{align}

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

// standard Colella-Woodward parameters:
//    K0 = 0.1d0, eta1 = 20.0, eta2 = 0.05, epsilon = 0.01d0
#define K0      0.1
#define ETA1   20.0
#define ETA2    0.05
#define EPSILON 0.01
static inline void steepen_rho(CCTK_REAL U[MAXNUMVARS][MAXNUMINDICES],CCTK_REAL slope_lim_dU[MAXNUMVARS][MAXNUMINDICES],CCTK_REAL Gamma_th,CCTK_REAL P_cold,CCTK_REAL Gamma_cold,
                               CCTK_REAL *rho_br_ppm,CCTK_REAL *rho_bl_ppm) {

  // Next compute centered differences d RHOB and d^2 RHOB
  CCTK_REAL d1rho_b     = 0.5*(U[RHOB][PLUS1] - U[RHOB][MINUS1]);
  CCTK_REAL d2rho_b_m1  = U[RHOB][PLUS0] - 2.0*U[RHOB][MINUS1] + U[RHOB][MINUS2];
  CCTK_REAL d2rho_b_p1  = U[RHOB][PLUS2] - 2.0*U[RHOB][PLUS1]  + U[RHOB][PLUS0];
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Then we evaluate

$$
\Gamma = \left.\left(\frac{\partial P}{\partial\rho}\right)\middle/\left(\frac{P}{\rho}\right)\right. = \Gamma_{\rm th} + \left(\Gamma_{\rm cold} - \Gamma_{\rm th}\right)\frac{P_{\rm cold}}{P}\ .
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  // Compute effective Gamma = (partial P / partial rho0)_s /(P/rho0)
  CCTK_REAL Gamma = Gamma_th + (Gamma_cold-Gamma_th)*P_cold/U[PRESSURE][PLUS0];
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Next the contact discontinuity condition, eq. (3.2) of [Colella & Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf), is checked:

$$
\Gamma K_{0}\frac{\left|\rho_{i+1}-\rho_{i-1}\right|}{\min\left(\rho_{i+1},\rho_{i-1}\right)} \geq \frac{\left|P_{i+1}-P_{i-1}\right|}{\min\left(P_{i+1},P_{i-1}\right)}\ ,
$$

where $K_{0}$ is a problem dependent constant. Keep in mind that we implement the quantity

$$
\boxed{{\rm contact\_discontinuity\_check} \equiv \Gamma K_{0}\left|\rho_{i+1}-\rho_{i-1}\right|\min\left(P_{i+1},P_{i-1}\right) - \left|P_{i+1}-P_{i-1}\right|\min\left(\rho_{i+1},\rho_{i-1}\right)}\ ,
$$

and verify whether ${\rm contact\_discontinuity\_check} \geq 0$ to verify the discontinuity condition. We also define the quantities

$$
\boxed{{\rm second\_deriv\_check} \equiv - \delta^{2}\rho_{i-1}\delta^{2}\rho_{i+1}}\ ,
$$

and

$$
\boxed{{\rm relative\_change\_check} \equiv 2\left|\delta\rho\right| - \epsilon\min\left(\rho_{i+1},\rho_{i-1}\right)}\ ,
$$

where again $\epsilon$ is a constant. The contact discontinuity condition is then satisfied when all three quantities inside boxes above are non-negative. When that is the case, we evaluate

$$
\boxed{\eta_{i} = \max\left\{0,\min\left[\eta_{1}\left(\tilde\eta_{i}-\eta_{2}\right),1\right]\right\}}\ ,
$$

where $\eta_{1}$ and $\eta_{2}$ are constants and

$$
\boxed{\tilde\eta_{i} =
\left\{
\begin{matrix}
0&, \ {\rm if}\ \delta\rho_{i} = 0\ ,\\
-\frac{1}{6}\left(\frac{\delta^{2}\rho_{i+1} - \delta^{2}\rho_{i-1}}{2\delta\rho_{i}}\right)&, \ {\rm otherwise}\ .
\end{matrix}
\right.}
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  CCTK_REAL contact_discontinuity_check = Gamma*K0*fabs(U[RHOB][PLUS1]-U[RHOB][MINUS1])*
    MIN(U[PRESSURE][PLUS1],U[PRESSURE][MINUS1])
    -fabs(U[PRESSURE][PLUS1]-U[PRESSURE][MINUS1])*MIN(U[RHOB][PLUS1],U[RHOB][MINUS1]);
  CCTK_REAL second_deriv_check = -d2rho_b_p1*d2rho_b_m1;
  CCTK_REAL relative_change_check = fabs(2.0*d1rho_b) - EPSILON*MIN(U[RHOB][PLUS1],U[RHOB][MINUS1]);

  if(contact_discontinuity_check >= 0.0 && second_deriv_check >= 0.0
     && relative_change_check >= 0.0) {

    CCTK_REAL eta_tilde=0.0;
    if (fabs(d1rho_b) > 0.0) {
      eta_tilde = -(1.0/6.0)*(d2rho_b_p1-d2rho_b_m1)/(2.0*d1rho_b);
    }
    CCTK_REAL eta = MAX(0.0,MIN(ETA1*(eta_tilde - ETA2),1.0));
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

We then apply the monotonized central (MC) scheme of [van Leer](https://www.sciencedirect.com/science/article/pii/002199917790095X) (see also [Step 3](#slope_limit) for a discussion on the quantities $\delta\rho^{\rm slope-lim}_{i}$ below),

\begin{align}
\rho^{\rm MC}_{r,i+1} &= \rho_{i+1} - \frac{1}{2}\delta\rho^{\rm slope-lim}_{i+1}\ ,\\
\rho^{\rm MC}_{l,i+0} &= \rho_{i-1} + \frac{1}{2}\delta\rho^{\rm slope-lim}_{i-1}\ ,
\end{align}

so that, finally, the steepening algorithm sets

$$
\begin{matrix}
\rho_{r}\to \rho_{r}(1-\eta) + \rho^{\rm MC}_{r}\eta\ ,\\
\rho_{l}\to \rho_{l}(1-\eta) + \rho^{\rm MC}_{l}\eta\ ,
\end{matrix}
$$

or, as implemented below:

$$
\boxed{\begin{matrix}
\rho_{r,i+0}\rightarrow \rho_{r,i+0}\left(1-\eta_{i+0}\right) + \rho^{\rm MC}_{r,i+1}\eta_{i+0}\ ,\\
\rho_{l,i+0}\rightarrow \rho_{r,i+0}\left(1-\eta_{i+0}\right) + \rho^{\rm MC}_{l,i+0}\eta_{i+0}\ .
\end{matrix}}
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

    // Next compute Urp1 and Ul for RHOB, using the MC prescription:
    // Ur_p1 = U_p1   - 0.5*slope_lim_dU_p1
    CCTK_REAL rho_br_mc_p1 = U[RHOB][PLUS1] - 0.5*slope_lim_dU[RHOB][PLUS1];
    // Ul = U_m1 + 0.5*slope_lim_dU_m1
    // Based on this line of code, Ur[index] = a_j - \delta_m a_j / 2. (cf. Eq. 65 in Marti & Muller's "PPM Method for 1D Relativistic Hydro." paper)
    //    So: Ur[indexp1] = a_{j+1} - \delta_m a_{j+1} / 2. This is why we have rho_br_mc[indexp1]
    CCTK_REAL rho_bl_mc    = U[RHOB][MINUS1] + 0.5*slope_lim_dU[RHOB][MINUS1];

    rho_bl_ppm[PLUS0] = rho_bl_ppm[PLUS0]*(1.0-eta) + rho_bl_mc*eta;
    rho_br_ppm[PLUS0] = rho_br_ppm[PLUS0]*(1.0-eta) + rho_br_mc_p1*eta;

  }
}
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 5: The `monotonize()` function \[Back to [top](#toc)\]
$$\label{monotonize}$$

The value $U_{i+1/2}$ will be assigned to $U_{l,i}$ and $U_{r,i-1}$ for most values of $i$, but in some cases this would lead to incorrect interpolation results. Near discontinuities, the value of either $U_{l}$, $U_{r}$, or both needs to be adjusted.

Consider, then, the following quantities:

\begin{align}
\delta U &\equiv U_{r} - U_{l}\ ,\\
\delta_{m}U &\equiv \frac{U_{r} + U_{l}}{2}\ .
\end{align}

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

static inline void monotonize(CCTK_REAL U,CCTK_REAL &Ur,CCTK_REAL &Ul) {
  CCTK_REAL dU = Ur - Ul;
  CCTK_REAL mU = 0.5*(Ur+Ul);
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Then, following eq. (1.10) of [Colella & Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf), we will check the following three cases:

$$
\boxed{\text{Case 1: if}\ \left(U_{r} - U\right)\left(U - U_{l}\right)\leq 0 \ \text{then:}\
\left\{
\begin{matrix}
U_{r} \to U\ ,\\
\ U_{l}\to U\ .
\end{matrix}
\right.}
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  if ( (Ur-U)*(U-Ul) <= 0.0) {
    Ur = U;
    Ul = U;
    return;
  }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

$$
\boxed{\text{Case 2: if}\ \delta U\left(U - \delta_{m}U\right) > \frac{\left(\delta U\right)^{2}}{6} \ \text{then:}\
\left\{
\begin{matrix}
U_{r} \to U_{r}\ ,\\
\ U_{l}\to 3U-2U_{r}\ .
\end{matrix}
\right.}
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  if ( dU*(U-mU) > (1.0/6.0)*SQR(dU)) {
    Ul = 3.0*U - 2.0*Ur;
    return;
  }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

$$
\boxed{\text{Case 3: if}\ \delta U\left(U - \delta_{m}U\right) < -\frac{\left(\delta U\right)^{2}}{6} \ \text{then:}\
\left\{
\begin{matrix}
U_{r} \to 3U-2U_{l}\ ,\\
\ U_{l}\to U_{l}\ .
\end{matrix}
\right.}
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  if ( dU*(U-mU) < -(1.0/6.0)*SQR(dU)) {
    Ur = 3.0*U - 2.0*Ul;
    return;
  }
}
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 6: The `compute_P_cold__Gamma_cold()` function \[Back to [top](#toc)\]
$$\label{compute_p_cold__Gamma_cold}$$

This part of the code evaluates $P_{\rm cold}$ and $\Gamma_{\rm cold}$ for the equations of state (EOS) presented in eqs. 13-16 of [Stephens *et al.* (2008)](http://arxiv.org/pdf/0802.0200.pdf).

First, if $\rho_{b} = 0$, then $P_{\rm cold} = 0$ and $\Gamma_{\rm cold}$ simply receives its tabulated value.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

static inline void compute_P_cold__Gamma_cold(CCTK_REAL rho_b,eos_struct &eos,   CCTK_REAL &P_cold,CCTK_REAL &Gamma_cold) {
  // This code handles equations of state of the form defined
  // in Eqs 13-16 in http://arxiv.org/pdf/0802.0200.pdf

  // Default in case rho_b == 0.0
  if(rho_b==0.0) { P_cold = 0.0; Gamma_cold = eos.Gamma_ppoly_tab[0]; return; }
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Next we consider the case where the EOS is given by a single-polytrope

$$
\boxed{P_{\rm cold} = \kappa \rho_{b}^{\Gamma_{\rm cold}}}\ ,
$$

and also the piecewise polytrope EOS

$$
\boxed{
P_{\rm cold} =
\left\{
\begin{matrix}
K_{0}\rho^{\Gamma_{0}} & , & \rho \leq \rho_{0}\\
K_{1}\rho^{\Gamma_{1}} & , & \rho_{0} \leq \rho \leq \rho_{1}\\
\vdots &  & \vdots\\
K_{j}\rho^{\Gamma_{j}} & , & \rho_{j-1} \leq \rho \leq \rho_{j}\\
\vdots &  & \vdots\\
K_{N-1}\rho^{\Gamma_{N-1}} & , & \rho_{N-2} \leq \rho \leq \rho_{N-1}\\
K_{N}\rho^{\Gamma_{N}} & , & \rho \geq \rho_{N-1}
\end{matrix}
\right.
}\ .
$$

Notice that we left the fact that $\Gamma_{i} \equiv \Gamma_{{\rm cold},i}$ implicit above.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  /***********************************
   * Piecewise Polytropic EOS Patch  *
   * Computing P_cold and Gamma_cold *
   ***********************************/
  int polytropic_index = find_polytropic_K_and_Gamma_index(eos,rho_b);
  Gamma_cold = eos.Gamma_ppoly_tab[polytropic_index];
  P_cold     = eos.K_ppoly_tab[polytropic_index]*pow(rho_b,Gamma_cold);

}
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 7: The `ftilde_gf_compute()` function \[Back to [top](#toc)\]
$$\label{ftilde_gf_compute}$$

This is the driver function of the `ftilde_compute()` function, setting up the dependencies needed to compute $\tilde{f}$. Please refer to the next step to see how $\tilde{f}$ is computed.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

#define OMEGA1   0.75
#define OMEGA2  10.0
#define EPSILON2 0.33
static void ftilde_gf_compute(const cGH *cctkGH,const int *cctk_lsh,const int flux_dirn,gf_and_gz_struct *in_prims,CCTK_REAL *ftilde_gf) {

  DECLARE_CCTK_PARAMETERS;

  int ijkgz_lo_hi[4][2];
  CCTK_REAL U[MAXNUMVARS][MAXNUMINDICES];
  /*Remove gcc unused variable warning/error Re: Pragma statement in loop define:*/
  CCTK_REAL dU,slope_lim_dU,Ur,Ul; dU=slope_lim_dU=Ur=Ul=0.0; dU*=0;
  // Compute ftilde, which is used for flattening left and right face values
  LOOP_DEFINE(2,2,  cctk_lsh,flux_dirn,  ijkgz_lo_hi,in_prims[VX+(flux_dirn-1)].gz_lo,in_prims[VX+(flux_dirn-1)].gz_hi) {
    SET_INDEX_ARRAYS(-2,2,flux_dirn);
    for(int ii=MINUS2;ii<=PLUS2;ii++) U[RHOB][ii]     = in_prims[RHOB    ].gf[index_arr[flux_dirn][ii]];
    for(int ii=MINUS2;ii<=PLUS2;ii++) U[PRESSURE][ii] = in_prims[PRESSURE].gf[index_arr[flux_dirn][ii]];
    U[VX+(flux_dirn-1)][MINUS1] = in_prims[VX+(flux_dirn-1)].gf[index_arr[flux_dirn][MINUS1]];
    U[VX+(flux_dirn-1)][PLUS1]  = in_prims[VX+(flux_dirn-1)].gf[index_arr[flux_dirn][PLUS1]];

    // Compute ftilde, which is used for flattening left and right face values
    // DEPENDENCIES: P(MINUS2,MINUS1,PLUS1,PLUS2) and v^m(MINUS1,PLUS1), where m=flux_dirn={1,2,3}={x,y,z}.
    ftilde_gf[index_arr[flux_dirn][PLUS0]] = ftilde_compute(flux_dirn,rho_b_atm,U);
  }
}
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 8: The `ftilde_compute()` function \[Back to [top](#toc)\]
$$\label{ftilde_compute}$$

We start by evaluating

\begin{align}
\delta P_{1} &\equiv P_{i+1} - P_{i-1}\ ,\\
\delta P_{2} &\equiv P_{i+2} - P_{i-2}\ .
\end{align}

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

static inline CCTK_REAL ftilde_compute(const int flux_dirn,const CCTK_REAL rho_b_atm,CCTK_REAL U[MAXNUMVARS][MAXNUMINDICES]) {
  CCTK_REAL dP1 = U[PRESSURE][PLUS1] - U[PRESSURE][MINUS1];
  CCTK_REAL dP2 = U[PRESSURE][PLUS2] - U[PRESSURE][MINUS2];
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Then we modify the standard PPM algorithm slightly by introducing the following conditions:

\begin{align}
{\rm if}\ \left|\frac{\delta P_{1}}{\delta_{m}P_{1}}\right| = 0\ {\rm or}\ \left|\left(\rho_{b}\right)_{i+1}+\left(\rho_{b}\right)_{i-1}\right| < 10\rho_{\rm atm}\ {\rm then\ set}\ \delta P_{1}=0\ ,\\
{\rm if}\ \left|\frac{\delta P_{2}}{\delta_{m}P_{2}}\right| = 0\ {\rm or}\ \left|\left(\rho_{b}\right)_{i+1}+\left(\rho_{b}\right)_{i-1}\right| < 10\rho_{\rm atm}\ {\rm then\ set}\ \delta P_{2}=0\ ,
\end{align}

where

\begin{align}
\delta_{m} P_{1} &\equiv \frac{P_{i+1} + P_{i-1}}{2}\ ,\\
\delta_{m} P_{2} &\equiv \frac{P_{i+2} + P_{i-2}}{2}\ .
\end{align}

Note that if the first condition above is satisfied then we are *not* inside a shock, while if the second condition is triggered *alone* there *may* be a shock.

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  // MODIFICATION TO STANDARD PPM:
  // Cure roundoff error issues when dP1==0 or dP2==0 to 15 or more significant digits.
  CCTK_REAL avg1=0.5*(U[PRESSURE][PLUS1] + U[PRESSURE][MINUS1]);
  CCTK_REAL avg2=0.5*(U[PRESSURE][PLUS2] + U[PRESSURE][MINUS2]);
  if(fabs(dP1)/avg1<1e-15 || fabs(U[RHOB][PLUS1] + U[RHOB][MINUS1])<10.0*rho_b_atm ) dP1=0.0; /* If this is triggered, there is NO shock. Also ignore "shocks" that appear entirely in the atmosphere (or regions with densities ~ 10*rho_b_atm). */
  if(fabs(dP2)/avg2<1e-15 || fabs(U[RHOB][PLUS2] + U[RHOB][MINUS2])<10.0*rho_b_atm ) dP2=0.0; /* If this is triggered alone, there may be a shock. Otherwise if triggered with above, NO shock. Also ignore "shocks" that appear entirely in the atmosphere (or regions with densities ~ 10*rho_b_atm). */
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

Next we set

$$
{\rm dP1\_over\_dP2} =
\left\{
\begin{matrix}
\frac{\delta P_{1}}{\delta P_{2}} &,\ {\rm if}\ \delta P_{2} \neq 0\\
1 &,\ {\rm otherwise}
\end{matrix}
\right.
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  CCTK_REAL dP1_over_dP2=1.0;
  if (dP2 != 0.0) dP1_over_dP2 = dP1/dP2;
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

We then construct

\begin{align}
q_{1} &= \left({\rm dP1\_over\_dP2}-\omega_{1}\right)\omega_{2}\ ,\\
q_{2} &= \frac{\left|\delta P_{1}\right|}{\min\left(P_{i+1},P_{i-1}\right)}\ .
\end{align}

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  CCTK_REAL q1 = (dP1_over_dP2-OMEGA1)*OMEGA2;
  CCTK_REAL q2 = fabs(dP1)/MIN(U[PRESSURE][PLUS1],U[PRESSURE][MINUS1]);
```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

We then initialize a new variable, $w$, to 0, i.e. we assume there is no shock. In the old version of `IllinoisGRMHD`, we would perform the following check:

$$
\text{OLD LOGIC:  }w =
\left\{
\begin{matrix}
1\ , &\ {\rm if}\ q_{2} > \epsilon_{2}\ {\rm and}\ q_{2}\left(v^{\rm flux\ dirn}_{i-1}-v^{\rm flux\ dirn}_{i+1}\right)>0, & \left({\rm inside\ shock}\right)\ ,\\
0\ , &\ {\rm otherwise}, & \left({\rm outside\ shock}\right)\ ,
\end{matrix}
\right.
$$

where $v^{\rm flux\ dirn}$ represents either $v^{x}$, $v^{y}$, or $v^{z}$, depending on the flux direction. This would, then, set $w=1$ when the shock condition above is satisfied. However, upon revision, a more robust logic is the following:

* Compute $\delta v \equiv v^{\rm flux\ dirn}_{i-1} - v^{\rm flux\ dirn}_{i+1}$.
* Compute $\delta_{m} v \equiv v^{\rm flux\ dirn}_{i-1} + v^{\rm flux\ dirn}_{i+1}$.
* Compute $v_{\rm rel\ err} \equiv \left|\delta v\right|/\delta_{m} v$.

Then, the shock condition becomes:

$$
{\rm if}\ v_{\rm rel\ err} > 10^{-15}\ {\rm and}\ q_{2}>\epsilon_{2}\ {\rm and}\ \delta v > 0 \implies \left({\rm inside\ shock}\right).
$$

This is a more robust test of whether we are in a shock or not, because we avoid problems in the atmosphere where the values of $v^{\rm flux\ dirn}_{i\pm1}$ can differ only by machine precision and cause an undesired trigger of the shock flag. We also take advantage of the fact that $q_{2}$ is necessarily positive, by definition, so it was not helping in the second Boolean comparison of the previous logic.

Finally, $\tilde{f}$ is given by

$$
\boxed{\tilde{f} = \min\left[1,w\max\left(0,q_{1}\right)\right]}\ .
$$

```python
%%writefile -a $outdir/reconstruct_set_of_prims_PPM.C

  // w==0 -> NOT inside a shock
  CCTK_REAL w=0.0;

  // w==1 -> inside a shock
  // Original code: if (q2 > EPSILON2 && q2*( (U[VX+(flux_dirn-1)][MINUS1]) - (U[VX+(flux_dirn-1)][PLUS1]) ) > 0.0) w = 1.0;
  //   comments: 1. q2 = (positive number) / (positive number) is always positive, so it's useless in the second Boolean comparison
  //   comments: 2. It may be the case that (U[VX+(flux_dirn-1)][MINUS1]) and (U[VX+(flux_dirn-1)][PLUS1]) differ at 1 part in 1e16 or so. We don't want the point to be marked as a shock due to this imperceptible difference.
  // New code addresses above comments by removing the "q2*" and computing the relative error between the velocities.
  const CCTK_REAL v_fluxdirn_minus1 = U[VX+(flux_dirn-1)][MINUS1];
  const CCTK_REAL v_fluxdirn_plus1  = U[VX+(flux_dirn-1)][PLUS1];
  const CCTK_REAL dv = v_fluxdirn_minus1 - v_fluxdirn_plus1;
  const CCTK_REAL avgabsv = 0.5*(fabs(v_fluxdirn_minus1) + fabs(v_fluxdirn_plus1));
  CCTK_REAL v_rel_error = 0.0; //dv/avgabsv;
  if(avgabsv != 0)  v_rel_error = fabs(dv)/avgabsv; // We should never flag a point as a shock if the velocities agree to
  if(v_rel_error > 1e-15 && q2 > EPSILON2 && dv > 0.0) w = 1.0;

  return MIN(1.0, w*MAX(0.0,q1));
}

```

    Appending to ../src/reconstruct_set_of_prims_PPM.C

# Step 9: 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.

## Step 9.a: `loop_defines_reconstruction.h` \[Back to [top](#toc)\]
$$\label{loop_defines_reconstruction__h_validation}$$

```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/loop_defines_reconstruction.h"
# original_IGM_file_name = "loop_defines_reconstruction-original.h"
# 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__loop_defines_reconstruction__h  = !diff $original_IGM_file_path $outfile_path__loop_defines_reconstruction__h

# if Validation__loop_defines_reconstruction__h == []:
#     # 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 loop_defines_reconstruction.h: PASSED!")
# else:
#     # If the validation fails, we keep the original IGM source code file
#     print("Validation test for loop_defines_reconstruction.h: 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__loop_defines_reconstruction__h:
#         print(diff_line)
```

## Step 9.b: `reconstruct_set_of_prims_PPM.C` \[Back to [top](#toc)\]
$$\label{reconstruct_set_of_prims_ppm__c_validation}$$

```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/reconstruct_set_of_prims_PPM.C"
# original_IGM_file_name = "reconstruct_set_of_prims_PPM-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__reconstruct_set_of_prims_PPM__C  = !diff $original_IGM_file_path $outfile_path__reconstruct_set_of_prims_PPM__C

# if Validation__reconstruct_set_of_prims_PPM__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 reconstruct_set_of_prims_PPM.C: PASSED!")
# else:
#     # If the validation fails, we keep the original IGM source code file
#     print("Validation test for reconstruct_set_of_prims_PPM.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__reconstruct_set_of_prims_PPM__C:
#         print(diff_line)
```

# Step 10: 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_reconstruct_set_of_prims_PPM.pdf](Tutorial-IllinoisGRMHD_reconstruct_set_of_prims_PPM.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__reconstruct_set_of_prims_PPM.ipynb
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD_reconstruct__set_of_prims_PPM.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD_reconstruct__set_of_prims_PPM.tex
#!pdflatex -interaction=batchmode Tutorial-IllinoisGRMHD_reconstruct__set_of_prims_PPM.tex
!rm -f Tut*.out Tut*.aux Tut*.log
```


```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

from IPython.display import HTML
tag = HTML('''
Toggle cell visibility <a href="javascript:code_toggle()">here</a>.''')
display(tag)

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)
```

Toggle cell visibility here.

```python
%matplotlib notebook
import numpy as np
import control as control
import matplotlib.pyplot as plt
import ipywidgets as widgets
import scipy.signal as signal
continuous_update=False
```

## Time response of the first-order system

The transfer function of the selected first-order system is defined as

\begin{equation}
    \frac{K_p}{\tau_p s+1},
\end{equation}

where $K_p$ and $\tau_p$ are parameters of the system.

System response depends on the input signal. In this example unit step function (with the Laplace transform $\frac{1}{s}$), unit impulse function (with the Laplace transform $1$), unit ramp function (with the Laplace transform $\frac{1}{s^2}$) and a sine signal (with the Laplace transform $\frac{1}{s^2+1}$) are used as possible input signals.

The plot below shows the input signal and the corresponding output signal for the chosen values of parameters $K_p$ and $\tau_p$.

### How to use this notebook?

Toogle between different input functions (unit step, unit impulse, unit ramp or sine). Move the sliders to change the values of $K_p$ and $\tau_p$.

```python
# sinus, step, ramp, x^2, sqrt(x)
functionSelect = widgets.ToggleButtons(
    options=[('unit step function', 0), ('unit impulse function', 1), ('unit ramp function', 2), ('sine function', 3)],
    description='Select: ')

fig = plt.figure(num='Time response of the first-order system')
fig.set_size_inches((9.8, 3))
fig.set_tight_layout(True)
f1 = fig.add_subplot(1, 1, 1)

f1.grid(which='both', axis='both', color='lightgray')

f1.set_xlabel('$t$ [s]')
f1.set_ylabel('input, output')

f1.axhline(0,Color='black',linewidth=0.5)
f1.axvline(0,Color='black',linewidth=0.5)

inputf, = f1.plot([],[])
responsef, = f1.plot([],[])
arrowf, = f1.plot([],[])

num_samples=2041

def create_draw_functions(Kp,taup,index):
    t=np.linspace(-0.1,5,num_samples)

    num=[Kp]
    den=[taup,1]
    Wsys=control.tf(num,den)

    global inputf,responsef, arrowf

    if index==0:
        yin=np.zeros(2041)
        yin[40:num_samples]=1
        tnew=np.linspace(0,5,2001)
        tout,yout=control.step_response(Wsys, T=tnew)
    elif index==1:
        yin=signal.unit_impulse(2001, 0)
        tnew=np.linspace(0,5,2001)
        t=tnew
        tout,yout=control.impulse_response(Wsys,tnew,X0=0)
    elif index==2:
        yin=np.zeros(num_samples)
        yin[40:num_samples]=np.linspace(0,5,2001)
        tnew=np.linspace(0,5,2001)
        tout,yout,xx=control.forced_response(Wsys,tnew,yin[40:])
    elif index==3:
        yin=np.sin(np.linspace(0,30,2001))
        tnew=np.linspace(0,30,2001)
        t=tnew
        tout,yout,xx=control.forced_response(Wsys,tnew,yin)

    f1.lines.remove(inputf)
    f1.lines.remove(responsef)
    f1.lines.remove(arrowf)

    inputf, = f1.plot(t,yin,color='C0',label='input')
    responsef, = f1.plot(tout,yout,color='C1',label='output')

    if index == 1:
        arrowf, = f1.plot([-0.1,0,0.1],[0.95,1,0.95],color='C0')
    else:
        arrowf, = f1.plot([],[])

    f1.legend()

    f1.relim()
    f1.relim()
    f1.autoscale_view()
    f1.autoscale_view()

Kp_slider = widgets.FloatSlider(value=1, min=0, max=2, step=0.1, description='$K_p$',
                          continuous_update=True, layout=widgets.Layout(width='auto', flex='5 5 auto'),readout_format='.1f')

taup_slider = widgets.FloatSlider(value=1, min=0, max=2, step=0.1, description='$\\tau_p$',
                          continuous_update=True, layout=widgets.Layout(width='auto', flex='5 5 auto'),readout_format='.1f')

input_data = widgets.interactive_output(create_draw_functions, {'Kp':Kp_slider,
                                                                'taup':taup_slider,
                                                                'index':functionSelect})

def update_sliders(index):
    global x0_slider

    Kpval = [1,1,1,1]
    Kp_slider.value = Kpval[index]
    taupval = [1,1,1,1]
    taup_slider.value = taupval[index]

input_data2 = widgets.interactive_output(update_sliders, {'index':functionSelect})

display(functionSelect)

display(Kp_slider,taup_slider,input_data)

# display(Markdown("The system can be represented as $f(x)=5$ for small excursions of x about x0."))
```

    <IPython.core.display.Javascript object>

    ToggleButtons(description='Select: ', options=(('unit step function', 0), ('unit impulse function', 1), ('unit…

    FloatSlider(value=1.0, description='$K_p$', layout=Layout(flex='5 5 auto', width='auto'), max=2.0, readout_for…

    FloatSlider(value=1.0, description='$\\tau_p$', layout=Layout(flex='5 5 auto', width='auto'), max=2.0, readout…

    Output()

```python

```