```python
from IPython.display import HTML

HTML('''
<form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code for QISKit exercises."></form>''')
```

<form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code for QISKit exercises."></form>

# 10. Quantum error correction

In the last chapter, we saw how the impossibility of copying an unknown quantum state adds a layer of security to quantum communication when compared to classical ones. At first sight, this feature turns into a drawback once we step into the realm of error correction. In fact, classically one can protect information by making many copies of the bits of interest. The probability that an error will happen to all the copies quickly becomes extremely unlikely with an increasing number of copies. However, the no-cloning theorem prohibits us from using the same technique for quantum bits.
Interestingly, error correcting procedure exists also in quantum computation. In this case, quantum entanglement and quantum superposition are used as resources to fight against unwanted errors. In this chapter, we take a closer look at quantum error correction for the simple cases of a bit-flip error and a phase error following a five-qubit protocol outlined in Ref. [1].

## 10.1 The bit-flip error in the three-bit code

$$\text{1. Error correcting circuit for a bit-flip error.}$$

Let us analyze the case of transmission of quantum information, in the form of a qubit, through a noisy channel. Because of the presence of noise, an error could occur during the communication which could change the state of the qubit in an unpredictable way. We must be able to restore its state without measuring the qubit.

Let us make a few assumptions. First of all, we are going to assume that if more than one qubits are transferred, errors happen independently on each qubit. Also, here we are going to consider only single bit-flip ($X$) errors.

Take the qubit we want to transfer to be in the state

\begin{equation}
\lvert \psi \rangle= \alpha\lvert 0\rangle +\beta \lvert 1\rangle
\end{equation}

As a first step, we encode this qubit in a three-qubit entangled state using two extra ancilla qubits in the state $\lvert 0 \rangle$ and CNOT gates from the first qubits to the ancillas ($CX_{12}CX_{13}$)

\begin{equation}
\lvert\psi_1\rangle=\alpha\lvert000\rangle+\beta\lvert110\rangle
\end{equation}

The three qubits are then sent through the noisy communication channel and retrieved on the receiver's side. During the communication, a bit-flip error could have occurred on any of the three qubits giving one of the following states

$\lvert \psi_{2} \rangle =\alpha\lvert 000\rangle+\beta\lvert 111\rangle$

$\lvert \psi_{2} \rangle =\alpha\lvert100\rangle+\beta\lvert011\rangle$

$\lvert\psi_{2} \rangle =\alpha\lvert010\rangle+\beta\lvert101\rangle$

$\lvert\psi_{2} \rangle =\alpha\lvert001\rangle+\beta\lvert110\rangle$

In order to correct the state received at the other end of the quantum communication channel from possible errors, two more ancilla qubits in the state $\lvert 0\rangle$ are added to the system. Moreover, they are entangled with the three bit received by using the following gates: $CX_{14}CX_{24}CX_{15}CX_{35}$. The state of the system is now

$\lvert\psi_{3} \rangle =\alpha \lvert000 \rangle \lvert 00\rangle+\beta\lvert111 \rangle \lvert 00\rangle$

$\lvert\psi_{3} \rangle =\alpha\lvert001 \rangle \lvert 01\rangle+\beta\lvert110 \rangle \lvert 01\rangle$

$\lvert\psi_{3} \rangle =\alpha\lvert010 \rangle \lvert 10\rangle+\beta\lvert101 \rangle \lvert 10\rangle$

$\lvert\psi_{3} \rangle =\alpha\lvert100 \rangle \lvert 11\rangle+\beta\lvert011 \rangle \lvert 11\rangle$

The two ancilla qubits just added are then measured. They give the "error syndrom", which can be used to diagnose and then correct any error occurred on the three qubits. There are four possible outcomes for the error syndrome:

$00$: $\lvert\psi_{4} \rangle =\alpha \lvert000 \rangle +\beta\lvert111 \rangle $

$01$: $\lvert\psi_{4} \rangle =\alpha\lvert001 \rangle +\beta\lvert110 \rangle$

$10$: $\lvert\psi_{4} \rangle =\alpha\lvert010 \rangle +\beta\lvert101 \rangle$

$11$: $\lvert\psi_{4} \rangle =\alpha\lvert100 \rangle +\beta\lvert011 \rangle$

Therefore, the state of the three qubits can be corrected by applying the corresponding gate

$00$: No correction is needed

$01$: Apply $X$ gate to the third qubit

$10$: Apply $X$ gate to the second qubit

$11$: Apply $X$ gate to the first qubit

After which the state of the system will be

\begin{equation}
\lvert\psi_5\rangle=\alpha\lvert000\rangle+\beta\lvert111\rangle
\end{equation}

As originally sent. Now, to find the state of the qubit which was intended to be transferred, we decode it from the three-qubit state by disentangling the qubit with the two ancillas. Thus we apply $CX_{12}CX_{13}$ and obtain

\begin{equation}
\lvert\psi_6\rangle = \alpha\lvert0\rangle+\beta\lvert1\rangle = \lvert\psi \rangle
\end{equation}

We have successfully communicated the state of the qubit $\lvert\psi \rangle$ through a noisy channel, protecting it from any single bit-flip error.

###  QISKit: implement the quantum error correction code for bit-flip error

####  1) Correct an X error on the second qubit in the state $ \frac{1}{\sqrt{2}}\left( \lvert 0 \rangle + \lvert 1 \rangle \right)$

```python
from qiskit import *

# Quantum program setup
Q_program = QuantumProgram()

# Creating registers
q = Q_program.create_quantum_register('q', 5)
c0 = Q_program.create_classical_register('c0', 1)
c1 = Q_program.create_classical_register('c1', 1)
c2 = Q_program.create_classical_register('c2', 1)

# Creates the quantum circuit
bit_flip = Q_program.create_circuit('bit_flip', [q], [c0,c1,c2])

# Prepares qubit in the desired initial state
bit_flip.h(q[0])

# Encodes the qubit in a three-qubit entangled state
bit_flip.cx(q[0], q[1])
bit_flip.cx(q[0], q[2])

# Bit-flip error on the second qubit
bit_flip.x(q[1])

# Adds additional two qubits for error-correction
bit_flip.cx(q[0], q[3])
bit_flip.cx(q[1], q[3])
bit_flip.cx(q[0], q[4])
bit_flip.cx(q[2], q[4])

# Measure the two additional qubits
bit_flip.measure(q[3], c0[0])
bit_flip.measure(q[4], c1[0])

# Do error correction
bit_flip.x(q[1]).c_if(c0, 1)
bit_flip.x(q[2]).c_if(c1, 1)

# Decodes the qubit from the three-qubit entangled state
bit_flip.cx(q[0], q[1])
bit_flip.cx(q[0], q[2])

# Check the state of the initial qubit
bit_flip.measure(q[0], c2[0])

# Shows gates of the circuit
circuits = ['bit_flip']
print(Q_program.get_qasms(circuits)[0])

# Parameters for execution on simulator
backend = 'local_qasm_simulator'
shots = 1024 # the number of shots in the experiment

# Run the algorithm
result = Q_program.execute(circuits, backend=backend, shots=shots)

#Shows the results obtained from the quantum algorithm
counts = result.get_counts('bit_flip')

print('\nThe measured outcomes of the circuits are:', counts)
```

    OPENQASM 2.0;
    include "qelib1.inc";
    qreg q[5];
    creg c0[1];
    creg c1[1];
    creg c2[1];
    h q[0];
    cx q[0],q[1];
    cx q[0],q[2];
    x q[1];
    cx q[0],q[3];
    cx q[1],q[3];
    cx q[0],q[4];
    cx q[2],q[4];
    measure q[3] -> c0[0];
    measure q[4] -> c1[0];
    if(c0==1) x q[1];
    if(c1==1) x q[2];
    cx q[0],q[1];
    cx q[0],q[2];
    measure q[0] -> c2[0];

    The measured outcomes of the circuits are: {'0 0 1': 501, '1 0 1': 523}

## 10.2 The phase error in the three-bit code

$$\text{2. Error correcting circuit for a phase error.}$$

Consider now the case of a phase error. Phase error transforms the basis states as

\begin{eqnarray}
\lvert 0 &\rangle&\rightarrow U\lvert0\rangle= e^{i\phi}\lvert 0 \rangle  \notag \\
\lvert 1 &\rangle&\rightarrow U\lvert1\rangle=e^{-i\phi}\lvert 1 \rangle
\end{eqnarray}

Let us see how we can protect a qubit in the generic state $\lvert \psi \rangle=\lvert \alpha \lvert0\rangle+ \beta \lvert1\rangle$ from this kind of errors. We are going to assume again that phase error occurs independently on each qubit and that only single phase error may occur.

Before sending the qubit through the noisy channel, we encode it in an entangled state similar to the previous one by using two ancillas in the state $\lvert 0 \rangle$.  To do so we apply $CX_{12}CX_{13}$ and then Hadamard gate on all three qubits, obtaining the encoded state

\begin{eqnarray}
\lvert \psi_1 \rangle = \frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ (\lvert0\rangle+\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle) \right] +  \beta \left[ (\lvert0\rangle-\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle) \right] \right\}
\end{eqnarray}

The three qubits are then sent through the noisy communication channel and retrieved on the receiver's side. During the communication, a phase error could have occurred on any of the three qubits giving one of the following states

$\lvert \psi_{2} \rangle = \frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ (\lvert0\rangle+\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle) \right] +  \beta \left[ (\lvert0\rangle-\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle) \right] \right\}$

$\lvert \psi_{2} \rangle = \frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ (e^{i\phi}\lvert0\rangle+e^{-i\phi}\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle) \right] +  \beta \left[ (e^{i\phi} \lvert0\rangle-e^{-i\phi}\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle) \right] \right\}$

$\lvert \psi_{2} \rangle =\frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ (\lvert0\rangle+\lvert1\rangle)(e^{i\phi}\lvert0\rangle+e^{-i\phi}\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle) \right] +  \beta \left[ (\lvert0\rangle-\lvert1\rangle)(e^{i\phi}\lvert0\rangle-e^{-i\phi}\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle) \right] \right\}$

$\lvert \psi_{2} \rangle = \frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ (\lvert0\rangle+\lvert1\rangle)(\lvert0\rangle+\lvert1\rangle)(e^{i\phi} \lvert0\rangle+e^{-i\phi}\lvert1\rangle) \right] +  \beta \left[ (\lvert0\rangle-\lvert1\rangle)(\lvert0\rangle-\lvert1\rangle)(e^{i\phi} \lvert0\rangle-e^{-i\phi}\lvert1\rangle) \right] \right\}$

In order to correct the state received at the other end of the quantum communication channel from possible errors, we first apply the Hadamard gate on all three qubits again. Giving:

$\lvert \psi_{3} \rangle =  \alpha \lvert000\rangle +  \beta \lvert111\rangle$

$\lvert \psi_3 \rangle = \frac{1}{\sqrt{2^3}}\left\{ \alpha \left[   (e^{i\phi}\lvert0\rangle + e^{i \phi} \lvert1\rangle + e^{-i\phi}\lvert0\rangle - e^{-i\phi}\lvert1\rangle) \lvert00\rangle \right] +  \beta \left[ (e^{i\phi}\lvert0\rangle + e^{i\phi}\lvert1\rangle - e^{-i\phi}\lvert 0 \rangle + e^{-i\phi}\lvert 1 \rangle) \lvert11\rangle \right] \right\}$

$\lvert \psi_{3} \rangle =\frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ \lvert0\rangle  (e^{i\phi}\lvert0\rangle + e^{i \phi} \lvert1\rangle + e^{-i\phi}\lvert0\rangle - e^{-i\phi}\lvert1\rangle)  \lvert0\rangle \right] +  \beta \left[ \lvert1\rangle  (e^{i\phi}\lvert0\rangle + e^{i \phi} \lvert1\rangle + e^{-i\phi}\lvert0\rangle - e^{-i\phi}\lvert1\rangle)  \lvert1\rangle \right] \right\}$

$\lvert \psi_3 \rangle = \frac{1}{\sqrt{2^3}}\left\{ \alpha \left[ \lvert00\rangle  (e^{i\phi}\lvert0\rangle + e^{i \phi} \lvert1\rangle + e^{-i\phi}\lvert0\rangle - e^{-i\phi}\lvert1\rangle) \right] +  \beta \left[ \lvert11\rangle (e^{i\phi}\lvert0\rangle + e^{i\phi}\lvert1\rangle - e^{-i\phi}\lvert 0 \rangle + e^{-i\phi}\lvert 1 \rangle) \right] \right\}$

Expanding the phase as $e^{i \phi} = \cos \phi + i \sin \phi$ and simplifying terms we obtain

$\lvert \psi_{3} \rangle =  \alpha \lvert000\rangle +  \beta \lvert111\rangle$

$\lvert \psi_3 \rangle =\alpha(\cos \phi \lvert000 \rangle+i\sin \phi \lvert100\rangle)+\beta(\cos \phi \lvert111\rangle+i\sin \phi \lvert011\rangle)$

$\lvert \psi_3 \rangle =\alpha(\cos \phi \lvert000 \rangle+i\sin \phi \lvert010\rangle)+\beta(\cos \phi \lvert111\rangle+i\sin \phi \lvert101\rangle)$

$\lvert \psi_3 \rangle =\alpha(\cos \phi \lvert000 \rangle+i\sin \phi \lvert001\rangle)+\beta(\cos \phi \lvert111\rangle+i\sin \phi \lvert110\rangle)$

Two more ancilla qubits in the state $\lvert 0\rangle$ are added to the system. Moreover, they are entangled with the three bit received by using the following gates: $CX_{14}CX_{24}CX_{15}CX_{35}$. The state of the system is now

$\lvert\psi_{4} \rangle =\alpha \lvert000 \rangle \lvert 00\rangle+\beta\lvert111 \rangle \lvert 00\rangle$

$\lvert\psi_{4} \rangle =\alpha(\cos \phi \lvert000 \rangle \lvert 00\rangle+i\sin \phi \lvert100 \rangle \lvert 11\rangle)+\beta(\cos \phi \lvert111 \rangle \lvert 00\rangle+i\sin \phi \lvert011 \rangle \lvert 11\rangle)$

$\lvert\psi_{4} \rangle =\alpha(\cos \phi \lvert000 \rangle \lvert 00\rangle+i\sin \phi \lvert010 \rangle \lvert 10\rangle)+\beta(\cos \phi \lvert111 \rangle \lvert 00\rangle+i\sin \phi \lvert101 \rangle \lvert 10\rangle)$

$\lvert\psi_{4} \rangle =\alpha(\cos \phi \lvert000  \rangle \lvert 00\rangle+i\sin \phi \lvert001 \rangle \lvert 01\rangle)+\beta(\cos \phi \lvert111 \rangle \lvert 00\rangle+i\sin \phi \lvert110 \rangle \lvert 01\rangle)$

The two ancilla qubits just added are then measured. They give the "error syndrome", which can be used to diagnose and then correct any error occurred on the three qubits. There are four possible outcomes for the error syndrome:

$00$: $\lvert\psi_{5} \rangle =\alpha \lvert000 \rangle +\beta\lvert111 \rangle $

$11$: $\lvert\psi_{5} \rangle =\alpha\lvert100 \rangle +\beta\lvert011 \rangle$

$10$: $\lvert\psi_{5} \rangle =\alpha\lvert010 \rangle +\beta\lvert101 \rangle$

$01$: $\lvert\psi_{5} \rangle =\alpha\lvert001 \rangle +\beta\lvert110 \rangle$

Therefore, the state of the three qubits can be corrected by applying the corresponding gate

$00$: No correction is needed

$11$: Apply $X$ gate to the first qubit

$10$: Apply $X$ gate to the second qubit

$01$: Apply $X$ gate to the third qubit

After which the state of the system will be

\begin{equation}
\lvert\psi_6\rangle=\alpha\lvert000\rangle+\beta\lvert111\rangle
\end{equation}

As originally sent. Now, to find the state of the qubit which was intended to be transferred, we decode it from the three-qubit state by disentangling the qubit with the two ancillas. Thus we apply $CX_{12}CX_{13}$ and obtain

\begin{equation}
\lvert\psi_7\rangle = \alpha\lvert0\rangle+\beta\lvert1\rangle = \lvert\psi \rangle
\end{equation}

We have successfully communicated the state of the qubit $\lvert\psi \rangle$ through a noisy channel, protecting it from any single phase error.

###  QISKit: implement the quantum error correction code for phase error

####  2) Correct a Z error on the second qubit in the state $ \frac{1}{\sqrt{2}}\left( \lvert 0 \rangle + \lvert 1 \rangle \right)$

```python
from qiskit import *

# Quantum program setup
Q_program = QuantumProgram()

# Creating registers
q = Q_program.create_quantum_register('q', 5)
c0 = Q_program.create_classical_register('c0', 1)
c1 = Q_program.create_classical_register('c1', 1)
c2 = Q_program.create_classical_register('c2', 1)

# Creates the quantum circuit
bit_flip = Q_program.create_circuit('bit_flip', [q], [c0,c1,c2])

# Prepares qubit in the desired initial state
bit_flip.h(q[0])

# Encodes the qubit in a three-qubit entangled state
bit_flip.cx(q[0], q[1])
bit_flip.cx(q[0], q[2])

# Go to Hadamard basis
bit_flip.h(q[0])
bit_flip.h(q[1])
bit_flip.h(q[2])

# Phase error on the second qubit
bit_flip.z(q[1])

# Converts phase error in bit-flip error
bit_flip.h(q[0])
bit_flip.h(q[1])
bit_flip.h(q[2])

# Adds additional two qubits for error-correction
bit_flip.cx(q[0], q[3])
bit_flip.cx(q[1], q[3])
bit_flip.cx(q[0], q[4])
bit_flip.cx(q[2], q[4])

# Measure the two additional qubits
bit_flip.measure(q[3], c0[0])
bit_flip.measure(q[4], c1[0])

# Do error correction
bit_flip.x(q[1]).c_if(c0, 1)
bit_flip.x(q[2]).c_if(c1, 1)

# Decodes the qubit from the three-qubit entangled state
bit_flip.cx(q[0], q[1])
bit_flip.cx(q[0], q[2])

# Check the state of the initial qubit
bit_flip.measure(q[0], c2[0])

# Shows gates of the circuit
circuits = ['bit_flip']
print(Q_program.get_qasms(circuits)[0])

# Parameters for execution on simulator
backend = 'local_qasm_simulator'
shots = 1024 # the number of shots in the experiment

# Run the algorithm
result = Q_program.execute(circuits, backend=backend, shots=shots)

#Shows the results obtained from the quantum algorithm
counts = result.get_counts('bit_flip')

print('\nThe measured outcomes of the circuits are:', counts)
```

    OPENQASM 2.0;
    include "qelib1.inc";
    qreg q[5];
    creg c0[1];
    creg c1[1];
    creg c2[1];
    h q[0];
    cx q[0],q[1];
    cx q[0],q[2];
    h q[0];
    h q[1];
    h q[2];
    z q[1];
    h q[0];
    h q[1];
    h q[2];
    cx q[0],q[3];
    cx q[1],q[3];
    cx q[0],q[4];
    cx q[2],q[4];
    measure q[3] -> c0[0];
    measure q[4] -> c1[0];
    if(c0==1) x q[1];
    if(c1==1) x q[2];
    cx q[0],q[1];
    cx q[0],q[2];
    measure q[0] -> c2[0];

    The measured outcomes of the circuits are: {'0 0 1': 550, '1 0 1': 474}

## Exercises

Show the evolution of the state of the system for the transmission of the state $\lvert 1 \rangle$ through a noisy communication channel. Consider the case where:

A bit-flip error occurs to the first qubit of the encoded state

A phase error occurs to the third qubit of the encoded state

Write a QISKit program that implements the exercises described above

## References

[1] A. M. Steane, arXiv:quant-ph/0304016v2 (2003).


# Standalone Fishbone-Moncrief C Code

We start with the NRPy+ expressions generated in the [Tutorial-FishboneMoncriefID](Tutorial-FishboneMoncriefID.ipynb), and output them to the C file "FishboneMoncriefID/FMstandalone.h".

Further, $\Gamma = \alpha u^0$ is given by (as shown [here](Tutorial-u0_smallb_Poynting-Cartesian.ipynb)):
$$
\Gamma = \alpha u^0 = \sqrt{\frac{1}{1 - \gamma_{ij}v^i_{(n)}v^j_{(n)}}}.
$$

```
import sympy as sp
from outputC import *
import indexedexp as ixp
import finite_difference as fin
import FishboneMoncriefID.FishboneMoncriefID as fmid

# Step 1: Set up the Fishbone-Moncrief initial data. This sets all the ID gridfunctions.
fmid.FishboneMoncriefID("Spherical")

gammaDD = ixp.zerorank2()

DIM = 3
for i in range(DIM):
    for j in range(DIM):
        if i<=j:
            gammaDD[i][j] = fmid.IDgammaDD[i][j]
        else:
            gammaDD[i][j] = fmid.IDgammaDD[j][i]

# gamma_{ij} v^i_{(n)} v^j_{(n)}
Gammacontraction = sp.sympify(0)
for i in range(DIM):
    for j in range(DIM):
        Gammacontraction += gammaDD[i][j] * fmid.IDValencia3velocityU[i] * fmid.IDValencia3velocityU[j]

Gammafactor = sp.sqrt(1 / (1 - Gammacontraction))

# -={ F-M quantities: Generate C code from expressions and output to file }=-
FishboneMoncrief_to_print = [\
                     lhrh(lhs="alpha",rhs=fmid.IDalpha),\
                     lhrh(lhs="betaU0",rhs=fmid.IDbetaU[0]),\
                     lhrh(lhs="betaU1",rhs=fmid.IDbetaU[1]),\
                     lhrh(lhs="betaU2",rhs=fmid.IDbetaU[2]),\
                     lhrh(lhs="Gammafactor",rhs=Gammafactor),\
                     lhrh(lhs="Gamma_times_ValenciavU0",rhs=Gammafactor*fmid.IDValencia3velocityU[0]),\
                     lhrh(lhs="Gamma_times_ValenciavU1",rhs=Gammafactor*fmid.IDValencia3velocityU[1]),\
                     lhrh(lhs="Gamma_times_ValenciavU2",rhs=Gammafactor*fmid.IDValencia3velocityU[2]),\
                     lhrh(lhs="uKS4U1",rhs=fmid.uKS4U[1]),\
                     lhrh(lhs="uKS4U2",rhs=fmid.uKS4U[2]),\
                     lhrh(lhs="uKS4U3",rhs=fmid.uKS4U[3]),\
                     lhrh(lhs="uBL4U1",rhs=fmid.uBL4U[1]),\
                     lhrh(lhs="uBL4U2",rhs=fmid.uBL4U[2]),\
                     lhrh(lhs="uBL4U3",rhs=fmid.uBL4U[3])
                     ]
print(fmid.uKS4U[3])
fin.FD_outputC("FishboneMoncriefID/FM_standalone.h",FishboneMoncrief_to_print,params="outCverbose=False,CSE_enable=False")
```

    -2*M*a*xx0*(-2*M*a*xx0*sqrt((-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)/(a**2*cos(xx1)**2 + xx0**2))*sqrt(sqrt(4*M*(a**2*cos(xx1)**2 + xx0**2)**2*(-2*M*xx0 + a**2 + xx0**2)*(-2*M*a**2*r_at_max_density + a**2*r_at_max_density**2 - a*sqrt(M*r_at_max_density)*(-a**2 + r_at_max_density**2) + r_at_max_density**4)**2/(r_at_max_density**3*(-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)**2*(-3*M*r_at_max_density + 2*a*sqrt(M*r_at_max_density) + r_at_max_density**2)**2*sin(xx1)**2) + 1)/2 - 1/2)*Abs(sin(xx1))/(-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2) - sqrt((-2*M*xx0 + a**2 + xx0**2)/(-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2))*sqrt(a**2*cos(xx1)**2 + xx0**2)*sqrt(sqrt(4*M*(a**2*cos(xx1)**2 + xx0**2)**2*(-2*M*xx0 + a**2 + xx0**2)*(-2*M*a**2*r_at_max_density + a**2*r_at_max_density**2 - a*sqrt(M*r_at_max_density)*(-a**2 + r_at_max_density**2) + r_at_max_density**4)**2/(r_at_max_density**3*(-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)**2*(-3*M*r_at_max_density + 2*a*sqrt(M*r_at_max_density) + r_at_max_density**2)**2*sin(xx1)**2) + 1)/2 + 1/2))/((a**2*cos(xx1)**2 + xx0**2)*(-2*M*xx0 + a**2 + xx0**2)) + sqrt((-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)/(a**2*cos(xx1)**2 + xx0**2))*(-4*M**2*a**2*xx0**2/((a**2*cos(xx1)**2 + xx0**2)*(-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)*(-2*M*xx0 + a**2 + xx0**2)) + (a**2*cos(xx1)**2 + xx0**2)/((-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)*sin(xx1)**2))*sqrt(sqrt(4*M*(a**2*cos(xx1)**2 + xx0**2)**2*(-2*M*xx0 + a**2 + xx0**2)*(-2*M*a**2*r_at_max_density + a**2*r_at_max_density**2 - a*sqrt(M*r_at_max_density)*(-a**2 + r_at_max_density**2) + r_at_max_density**4)**2/(r_at_max_density**3*(-a**2*(-2*M*xx0 + a**2 + xx0**2)*sin(xx1)**2 + (a**2 + xx0**2)**2)**2*(-3*M*r_at_max_density + 2*a*sqrt(M*r_at_max_density) + r_at_max_density**2)**2*sin(xx1)**2) + 1)/2 - 1/2)*Abs(sin(xx1))
    Wrote to file "FishboneMoncriefID/FM_standalone.h"

```
%%writefile FishboneMoncriefID/FM_standalone.c

#include "stdio.h"
#include "stdlib.h"
#include "math.h"

const double a = 0.9375;
const double M = 1.0;
const double r_at_max_density = 12.0;
const double r_in = 6.0;

int main(int argc, const char *argv[]) {

    // Step 0a: Read command-line input, error out if nonconformant
    double xx0,xx1,xx2;
/*
    if(argc != 4) {
        printf("Error: Expected three command-line arguments: ./FM_standalone r theta phi\n");
        exit(1);
    }
    xx0 = strtod(argv[1],NULL);
    xx1 = strtod(argv[2],NULL);
    xx2 = strtod(argv[3],NULL);
*/

//    printf("# Output: r,th,ph, alpha, betaU0, betaU1, betaU2, Gamma, Gamma*vValenciaU0, Gamma*vValenciaU1, Gamma*vValenciaU2\n");
    for(double xx0=1.6;xx0<50.0;xx0+=0.2) {
        xx1 = 1.56463634120e0; //M_PI/2.0;
        xx2 = 0.0;
        double alpha,betaU0,betaU1,betaU2,Gammafactor,Gamma_times_ValenciavU0,Gamma_times_ValenciavU1,Gamma_times_ValenciavU2;
        double uKS4U1,uKS4U2,uKS4U3,uBL4U1,uBL4U2,uBL4U3;
#include "FM_standalone.h"
        if(xx0 < r_in) {
            Gammafactor = 1.0;
            Gamma_times_ValenciavU0 = Gamma_times_ValenciavU1 = Gamma_times_ValenciavU2 = 0.0;
            uKS4U1 = uKS4U2 = uKS4U3 = 0.0;
            uBL4U1 = uBL4U2 = uBL4U3 = 0.0;
        }
        printf("%e %e %e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e %.15e\n",
               xx0,xx1,xx2,
               alpha,betaU0,betaU1,betaU2,
               Gammafactor,
               Gamma_times_ValenciavU0, // util1(1) in FMtorus.f90; util(1,i,j,k) near the write statement
               Gamma_times_ValenciavU1, // util1(3) in FMtorus.f90.
               Gamma_times_ValenciavU2, // util1(2) in FMtorus.f90.
               uKS4U1,uKS4U2,uKS4U3,
               uBL4U1,uBL4U2,uBL4U3);
    }
    return 0;
}
```

    Overwriting FishboneMoncriefID/FM_standalone.c

```
!gcc -O2 FishboneMoncriefID/FM_standalone.c -o FM_standalone -lm
```

```
!./FM_standalone > out.txt
```

```
%matplotlib inline
import sympy as sp
import matplotlib.pyplot as plt
import mpmath as mp
import csv

def file_reader(filename,list_of_cols,delim=" "):
    with open(filename) as file:
        reader = csv.reader(file, delimiter=delim)
        data  = list(zip(*reader))
#         print(data)
        # data is a tuple of strings. Tuples are immutable, and we need to perform math on
        #   the data, so here we convert tuple to lists of floats:
#        data_output = [[sp.sympify(0) for i in range(len(list_of_cols))] for j in range(len(data[0]))]
        data_output = [[sp.sympify(0) for i in range(len(data[0]))] for j in range(len(list_of_cols))]
        for i in range(len(data[0])):
            for j in range(len(list_of_cols)):
#                 print(i,j,data[list_of_cols[j]][i])
                data_output[j][i] = float(data[list_of_cols[j]][i])
        return data_output

NRPy_data_output = file_reader('out.txt',       [0,7,8,9,10])
std_data_output  = file_reader('torus_cuts.csv',[0,4,1,3,2])

ylabels = ['Lorentz Gamma_{KS}=G','G*v^r_{KS,Val.}','G*v^{\\theta}_{KS,Val.}','G*v^{\phi}_{KS,Val.}']

for i in range(len(ylabels)):
    # https://matplotlib.org/gallery/text_labels_and_annotations/legend.html#sphx-glr-gallery-text-labels-and-annotations-legend-py
    fig, ax = plt.subplots()
    plt.title("NRPy's FM solve with FMtorus.f90: "+ylabels[i])
    plt.xlabel("r/M")
    plt.ylabel(ylabels[i])
    ax.plot(NRPy_data_output[0], NRPy_data_output[i+1], 'k--', label='NRPyFMSolve')
    ax.plot(std_data_output[0],   std_data_output[i+1], 'k-',  label='FMtorus.f90')
    legend = ax.legend(loc='upper right', shadow=True, fontsize='x-large')
    legend.get_frame().set_facecolor('C1')
    plt.show()
```


```python
%matplotlib inline
```

# Plotting sin

This is a general example demonstrating a Matplotlib plot output, embedded
rST, the use of math notation and cross-linking to other examples. It would be
useful to compare the :download:`source Python file <plot_0_sin.py>` with the
output below.

Source files for gallery examples should start with a triple-quoted header
docstring. Anything before the docstring is ignored by Sphinx-Gallery and will
not appear in the rendered output, nor will it be executed. This docstring
requires a rST header, which is used as the title of the example and
to correctly build cross-referencing links.

Code and embedded rST text blocks follow the docstring. The first block
immediately after the docstring is deemed a code block, by default, unless you
specify it to be a text block using a line of ``#``'s or ``#%%`` (see below).
All code blocks get executed by Sphinx-Gallery and any output, including plots
will be captured. Typically, code and text blocks are interspersed to provide
narrative explanations of what the code is doing or interpretations of code
output.

Mathematical expressions can be included as LaTeX, and will be rendered with
MathJax. To include displayed math notation, use the directive ``.. math::``.
To include inline math notation use the ``$` role. For example, we are
about to plot the following function:

\begin{align}x \rightarrow \sin(x)\end{align}

Here the function :math:$\sin` is evaluated at each point the variable
$x$ is defined. When including LaTeX in a Python string, ensure that you
escape the backslashes or use a `raw docstring <python:strings>`. You do
not need to do this in text blocks (see below).

```python
# Code source: Óscar Nájera
# License: BSD 3 clause

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)

plt.plot(x, y)
plt.xlabel(r'$x$')
plt.ylabel(r'$\sin(x)$')
plt.savefig("results/sin_demo.png")
plt.close()
```

To include embedded rST, use a line of >= 20 ``#``'s or ``#%%`` between your
rST and your code (see `embedding_rst`). This separates your example
into distinct text and code blocks. You can continue writing code below the
embedded rST text block:

```python
print('This example shows a sin plot!')
```

.. admonition:: results/sin_demo
   :class: dropdown, seealso

   .. image:: ../../examples/results/sin_demo.png

.. |docstring| replace:: """


# Markdown Cell

## Sub heading

This is just a paragraph.

```python
# code cell
name = "Jonathan"
```

```python

```

# Tips and Tricks

A markdown cell lets you do many _useful things_.

```python
# Add this to python2 code to make life easier -- safe to use in python3 as well.
from __future__ import absolute_import, division, print_function

```

```python
import numpy as np
# don't do:
# from numpy import *
```

```python
max('a')
```

    'a'

```python
np.max('a')
```

```python
%matplotlib inline
%config InlineBackend.figure_format='retina'
```

## Imports

```python
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os
from sklearn.datasets import load_boston
from pivottablejs import pivot_ui
import qgrid
import sys
import warnings
warnings.filterwarnings('ignore')
```

# Keyboard shortcuts

Practice doing these a few times and try to force yourself to use them whenever you can.

## Help (h)
For help in the Notebook, `ESC` + `h`
(doesn't work in Lab)

## Select multiple cells

j, k, and arrow keys while holding shift selects multiple cells.

```python
first = 1
```

```python
second = 2
```

```python
third = 3
```

## Split a cell with -

Well, ctrl + shift + -, but remember it by horizontal.

## Add a new cell (a)bove

## Add a new cell (b)elow

```python

```

## (d)(d)elete

```python

```

```python

```

## (c)opy cells

## (v)aste cells

# Pivot Tables w/ pandas

A library and example from: http://nicolas.kruchten.com/content/2015/09/jupyter_pivottablejs/

```python
df = pd.read_csv("../data/mps.csv", encoding="ISO-8859-1")
```

```python
# recommend using .head()
df.head(10)
```

      Name
      Party
      Province
      Age
      Gender

      0
      Liu, Laurin
      NDP
      Quebec
      22.0
      Female

      1
      Mourani, Maria
      Bloc Quebecois
      Quebec
      43.0
      Female

      2
      Sellah, Djaouida
      NDP
      Quebec
      NaN
      Female

      3
      St-Denis, Lise
      NDP
      Quebec
      72.0
      Female

      4
      Fry, Hedy
      Liberal
      British Columbia
      71.0
      Female

      5
      Turmel, Nycole
      NDP
      Quebec
      70.0
      Female

      6
      Sgro, Judy
      Liberal
      Ontario
      68.0
      Female

      7
      Raynault, Francine
      NDP
      Quebec
      67.0
      Female

      8
      Davidson, Patricia
      Conservative
      Ontario
      66.0
      Female

      9
      Smith, Joy
      Conservative
      Manitoba
      65.0
      Female

# Enhanced Pandas Dataframe Display

```python
# Province, Party, Average, Age, Heatmap
```

```python

```

```python
pivot_ui(df)
```

```python
df_dict = load_boston()
features = pd.DataFrame(data=df_dict.data, columns = df_dict.feature_names)
target = pd.DataFrame(data=df_dict.target, columns = ['MEDV'])
df = pd.concat([features, target], axis=1)
```

```python
df['Zone'] = df['ZN'].astype('category')
df['CHAS'] = df['CHAS'].astype('category')
df.head()
```

      CRIM
      ZN
      INDUS
      CHAS
      NOX
      RM
      AGE
      DIS
      RAD
      TAX
      PTRATIO
      B
      LSTAT
      MEDV
      Zone

      0
      0.00632
      18.0
      2.31
      0.0
      0.538
      6.575
      65.2
      4.0900
      1.0
      296.0
      15.3
      396.90
      4.98
      24.0
      18.0

      1
      0.02731
      0.0
      7.07
      0.0
      0.469
      6.421
      78.9
      4.9671
      2.0
      242.0
      17.8
      396.90
      9.14
      21.6
      0.0

      2
      0.02729
      0.0
      7.07
      0.0
      0.469
      7.185
      61.1
      4.9671
      2.0
      242.0
      17.8
      392.83
      4.03
      34.7
      0.0

      3
      0.03237
      0.0
      2.18
      0.0
      0.458
      6.998
      45.8
      6.0622
      3.0
      222.0
      18.7
      394.63
      2.94
      33.4
      0.0

      4
      0.06905
      0.0
      2.18
      0.0
      0.458
      7.147
      54.2
      6.0622
      3.0
      222.0
      18.7
      396.90
      5.33
      36.2
      0.0

## QGrid

Interactive pandas dataframes: https://github.com/quantopian/qgrid

```python

```

```python
qgrid_widget = qgrid.show_grid(df[['CRIM',
                                   'Zone',
                                   'INDUS',
                                   'CHAS',
                                   'NOX',
                                   'AGE',
                                   'RAD',
                                   'LSTAT',
                                   'MEDV',
                                  ]], show_toolbar=True)
```

```python
qgrid_widget
```

    QgridWidget(grid_options={'fullWidthRows': True, 'syncColumnCellResize': True, 'forceFitColumns': True, 'defau…

```python
df2 = qgrid_widget.get_changed_df()
```

```python
df2.head(7)
```

      CRIM
      Zone
      INDUS
      CHAS
      NOX
      AGE
      RAD
      LSTAT
      MEDV

      0
      0.00632
      18.0
      2.31
      0.0
      0.538
      56.2
      1.0
      4.98
      24.0

      1
      0.02731
      0.0
      7.07
      0.0
      0.469
      78.9
      2.0
      9.14
      21.6

      2
      0.02729
      0.0
      7.07
      0.0
      0.469
      61.1
      2.0
      4.03
      34.7

      3
      0.03237
      0.0
      2.18
      0.0
      0.458
      45.8
      3.0
      2.94
      33.4

      4
      0.06905
      0.0
      2.18
      0.0
      0.458
      54.2
      3.0
      5.33
      36.2

      5
      0.02985
      0.0
      2.18
      0.0
      0.458
      58.7
      3.0
      5.21
      28.7

      35
      0.06417
      0.0
      5.96
      0.0
      0.499
      68.2
      5.0
      9.68
      18.9

```python
df.head(7)
```

      CRIM
      ZN
      INDUS
      CHAS
      NOX
      RM
      AGE
      DIS
      RAD
      TAX
      PTRATIO
      B
      LSTAT
      MEDV
      Zone

      0
      0.00632
      18.0
      2.31
      0.0
      0.538
      6.575
      65.2
      4.0900
      1.0
      296.0
      15.3
      396.90
      4.98
      24.0
      18.0

      1
      0.02731
      0.0
      7.07
      0.0
      0.469
      6.421
      78.9
      4.9671
      2.0
      242.0
      17.8
      396.90
      9.14
      21.6
      0.0

      2
      0.02729
      0.0
      7.07
      0.0
      0.469
      7.185
      61.1
      4.9671
      2.0
      242.0
      17.8
      392.83
      4.03
      34.7
      0.0

      3
      0.03237
      0.0
      2.18
      0.0
      0.458
      6.998
      45.8
      6.0622
      3.0
      222.0
      18.7
      394.63
      2.94
      33.4
      0.0

      4
      0.06905
      0.0
      2.18
      0.0
      0.458
      7.147
      54.2
      6.0622
      3.0
      222.0
      18.7
      396.90
      5.33
      36.2
      0.0

      5
      0.02985
      0.0
      2.18
      0.0
      0.458
      6.430
      58.7
      6.0622
      3.0
      222.0
      18.7
      394.12
      5.21
      28.7
      0.0

      6
      0.08829
      12.5
      7.87
      0.0
      0.524
      6.012
      66.6
      5.5605
      5.0
      311.0
      15.2
      395.60
      12.43
      22.9
      12.5

# Tab

```python
import numpy as np
```

```python
np.random.chisquare()
```

```python
# pure tab right ↓ less useful
np.random.choice()
```

## shift-tab

```python
# shift-tab right ↓ more useful
np.linspace(start=50, endpoint=False, stop=100)
```

    array([50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60., 61., 62.,
           63., 64., 65., 66., 67., 68., 69., 70., 71., 72., 73., 74., 75.,
           76., 77., 78., 79., 80., 81., 82., 83., 84., 85., 86., 87., 88.,
           89., 90., 91., 92., 93., 94., 95., 96., 97., 98., 99.])

```python

```

```python

```

```python

```

## shift-tab-tab

## shift-tab-tab-tab

```python
np.linspace(50, 150, num=100, endpoint=False, )
```

## shift-tab-tab-tab-tab

```python
plt.plot(np.linspace(start, stop,), )
```

## ?

```python
np.linspace?
```

```python
?np.linspace
```

## ??

(Lab can scroll if you click)

```python
np.linspace??
```

```python
!subl ~/miniconda3/envs/dspy3/lib/python3.6/site-packages/numpy/core/function_base.py
```

```python

```

```python
import textwrap
def example_function():
    """Docstring for example function"""

    print(textwrap.dedent("""
    This is a multi-lined string
    that I want to write inside of a function.
    Notice what happens when I print this.
        And when something is indented more."""))

example_function()
```

    This is a multi-lined string
    that I want to write inside of a function.
    Notice what happens when I print this.
        And when something is indented more.

## Inspect _everything_ and Find and Replace

```python
# But first find and replace
def silly_function(xval):
    """Takes a value and returns the value."""
    xval_sq = xval ** 2.0
    1 + 10
    xval_abs = np.sqrt(xval_sq)
    return xval_abs
```

```python
silly_function(2,)
```

```python
silly_function?
```

```python
silly_function??
```

```python
!ls ../data/
```

    coal_prod_2008.csv           coalpublic2012.xls
    coal_prod_2009.csv           collincountyforeclosures.csv
    coal_prod_2010.csv           example.csv
    coal_prod_2011.csv           mps.csv
    coal_prod_2012.csv           nbextensions.png
    coal_prod_cleaned.csv        raw_players.csv.gz
    coal_prod_cleaned.csv.zip

```python
coal_years = !ls ../data/coal_prod_20*.csv
```

```python
coal_years
```

    ['../data/coal_prod_2008.csv',
     '../data/coal_prod_2009.csv',
     '../data/coal_prod_2010.csv',
     '../data/coal_prod_2011.csv',
     '../data/coal_prod_2012.csv']

## Line numbers (lowercase "L")

Type lowercase "L" to have line numbers; shift-L for line numbers notebook-wide.

## Move blocks of code around

### Indent/dedent

    Cmd + [
    Cmd + ]

### Comment

    Cmd + /

```python
example_dict = {}

# Indent/dedent/comment
for index in range(5):
    example_dict["one"] = 1
    example_dict["two"] = 2
    example_dict["three"] = 3
    example_dict["four"] = 4
```

## Multicursor magic

Hold down `option`, click and drag (for big cursor).

cmd + click == wherever you click.

Shift command P -- command palette.

```python
example_dict["one_better_neat"] = 1
example_dict["two_better_neat"] = 2
example_dict["three_better_neat"] = 3
example_dict["four_better_neat"] = 4
```

## Monospace

You can also get `monospaced` fonts by indenting 4 spaces:

    mkdir toc
    cd toc

## Syntax Highlighting

Wrap with triple-backticks and language:

```bash
mkdir toc
cd toc
wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh
```

```SQL
SELECT first_name,
       last_name,
       year_of_birth
FROM presidents
WHERE year_of_birth > 1800;
```

## Headings and LaTeX

With text and $\LaTeX$ support.

$$\begin{align}
        B'&=-\nabla \times E,\\
        E'&=\nabla \times B - 4\pi j
\end{align}$$

# Magics

 - `%` $\equiv$ inline magic
 - `%%` $\equiv$ cell magic

```latex
%%latex

If you want to get crazier$\ldots$

\begin{equation}
\oint_S {E_n dA = \frac{1}{{\varepsilon _0 }}} Q_\textrm{inside}
\end{equation}
```

If you want to get crazier$\ldots$

\begin{equation}
\oint_S {E_n dA = \frac{1}{{\varepsilon _0 }}} Q_\textrm{inside}
\end{equation}

```bash
%%bash
wget http://www.ast.cam.ac.uk/~rfc/vpfit11.1.tar.gz
mkdir -p vpfit11
cd vpfit11
tar -xvzf ../vpfit11.1.tar.gz
```

    --2019-01-17 18:49:14--  http://www.ast.cam.ac.uk/~rfc/vpfit11.1.tar.gz
    Resolving www.ast.cam.ac.uk (www.ast.cam.ac.uk)... 131.111.71.71
    Connecting to www.ast.cam.ac.uk (www.ast.cam.ac.uk)|131.111.71.71|:80... connected.
    HTTP request sent, awaiting response... 301 Moved Permanently
    Location: https://www.ast.cam.ac.uk:443/~rfc/vpfit11.1.tar.gz [following]
    --2019-01-17 18:49:14--  https://www.ast.cam.ac.uk/~rfc/vpfit11.1.tar.gz
    Connecting to www.ast.cam.ac.uk (www.ast.cam.ac.uk)|131.111.71.71|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 3761497 (3.6M) [application/x-gzip]
    Saving to: ‘vpfit11.1.tar.gz.1’

         0K .......... .......... .......... .......... ..........  1%  113K 32s
        50K .......... .......... .......... .......... ..........  2%  340K 21s
       100K .......... .......... .......... .......... ..........  4%  321K 17s
       150K .......... .......... .......... .......... ..........  5% 64.5M 13s
       200K .......... .......... .......... .......... ..........  6%  340K 12s
       250K .......... .......... .......... .......... ..........  8%  334K 12s
       300K .......... .......... .......... .......... ..........  9% 15.6M 10s
       350K .......... .......... .......... .......... .......... 10%  340K 10s
       400K .......... .......... .......... .......... .......... 12%  332K 10s
       450K .......... .......... .......... .......... .......... 13%  334K 9s
       500K .......... .......... .......... .......... .......... 14%  164K 10s
       550K .......... .......... .......... .......... .......... 16% 86.0M 9s
       600K .......... .......... .......... .......... .......... 17% 80.3M 8s
       650K .......... .......... .......... .......... .......... 19%  334K 8s
       700K .......... .......... .......... .......... .......... 20%  170K 9s
       750K .......... .......... .......... .......... .......... 21%  165K 9s
       800K .......... .......... .......... .......... .......... 23%  112K 10s
       850K .......... .......... .......... .......... .......... 24% 70.5M 9s
       900K .......... .......... .......... .......... .......... 25%  343K 9s
       950K .......... .......... .......... .......... .......... 27%  103K 10s
      1000K .......... .......... .......... .......... .......... 28% 88.9K 10s
      1050K .......... .......... .......... .......... .......... 29%  111K 11s
      1100K .......... .......... .......... .......... .......... 31%  112K 11s
      1150K .......... .......... .......... .......... .......... 32%  106K 11s
      1200K .......... .......... .......... .......... .......... 34%  110K 12s
      1250K .......... .......... .......... .......... .......... 35%  112K 12s
      1300K .......... .......... .......... .......... .......... 36%  169K 12s
      1350K .......... .......... .......... .......... .......... 38%  112K 12s
      1400K .......... .......... .......... .......... .......... 39%  170K 11s
      1450K .......... .......... .......... .......... .......... 40%  164K 11s
      1500K .......... .......... .......... .......... .......... 42%  313K 11s
      1550K .......... .......... .......... .......... .......... 43%  158K 11s
      1600K .......... .......... .......... .......... .......... 44%  332K 10s
      1650K .......... .......... .......... .......... .......... 46%  169K 10s
      1700K .......... .......... .......... .......... .......... 47%  337K 10s
      1750K .......... .......... .......... .......... .......... 49%  343K 9s
      1800K .......... .......... .......... .......... .......... 50%  502K 9s
      1850K .......... .......... .......... .......... .......... 51%  915K 9s
      1900K .......... .......... .......... .......... .......... 53%  342K 8s
      1950K .......... .......... .......... .......... .......... 54%  335K 8s
      2000K .......... .......... .......... .......... .......... 55%  325K 8s
      2050K .......... .......... .......... .......... .......... 57% 14.9M 7s
      2100K .......... .......... .......... .......... .......... 58%  339K 7s
      2150K .......... .......... .......... .......... .......... 59%  369K 7s
      2200K .......... .......... .......... .......... .......... 61% 2.99M 6s
      2250K .......... .......... .......... .......... .......... 62%  224K 6s
      2300K .......... .......... .......... .......... .......... 63%  100M 6s
      2350K .......... .......... .......... .......... .......... 65%  626K 5s
      2400K .......... .......... .......... .......... .......... 66%  337K 5s
      2450K .......... .......... .......... .......... .......... 68%  367K 5s
      2500K .......... .......... .......... .......... .......... 69%  367K 5s
      2550K .......... .......... .......... .......... .......... 70% 2.27M 4s
      2600K .......... .......... .......... .......... .......... 72%  362K 4s
      2650K .......... .......... .......... .......... .......... 73%  342K 4s
      2700K .......... .......... .......... .......... .......... 74% 3.69M 4s
      2750K .......... .......... .......... .......... .......... 76%  364K 3s
      2800K .......... .......... .......... .......... .......... 77%  346K 3s
      2850K .......... .......... .......... .......... .......... 78% 3.79M 3s
      2900K .......... .......... .......... .......... .......... 80%  334K 3s
      2950K .......... .......... .......... .......... .......... 81%  826K 3s
      3000K .......... .......... .......... .......... .......... 83%  533K 2s
      3050K .......... .......... .......... .......... .......... 84%  289K 2s
      3100K .......... .......... .......... .......... .......... 85%  112M 2s
      3150K .......... .......... .......... .......... .......... 87%  167K 2s
      3200K .......... .......... .......... .......... .......... 88% 62.3M 2s
      3250K .......... .......... .......... .......... .......... 89%  225K 1s
      3300K .......... .......... .......... .......... .......... 91%  339K 1s
      3350K .......... .......... .......... .......... .......... 92%  252K 1s
      3400K .......... .......... .......... .......... .......... 93%  338K 1s
      3450K .......... .......... .......... .......... .......... 95%  524K 1s
      3500K .......... .......... .......... .......... .......... 96%  340K 0s
      3550K .......... .......... .......... .......... .......... 98%  421K 0s
      3600K .......... .......... .......... .......... .......... 99%  172K 0s
      3650K .......... .......... ...                             100% 1.68M=13s

    2019-01-17 18:49:28 (274 KB/s) - ‘vpfit11.1.tar.gz.1’ saved [3761497/3761497]

    x ./
    x ./#temp2#
    x ./atom.dat
    x ./atomtab.f
    x ./autovpin.f
    x ./bintofits.f
    x ./calcn.f
    x ./case.f
    x ./chanwav.f
    x ./chisum26.f
    x ./cmax1.f
    x ./cms_src.f
    x ./com_cons.f
    x ./dcholin.f
    x ./dexpf.f
    x ./dlycabscft.f
    x ./docs/
    x ./dotick.f
    x ./dpoly.f
    x ./dsepvar.f
    x ./eigen.f
    x ./erfcc.f
    x ./externals.f
    x ./fl_gen.f
    x ./gen_vpin.f
    x ./ginsert.f
    x ./gu_tabsort.f
    x ./hedit.f
    x ./linfit.f
    x ./lm_udchole.f
    x ./lyregset.f
    x ./makefile
    x ./medianv.f
    x ./MMatomdat_atom_noiso.dat
    x ./pda_che2d.f
    x ./pda_dp1vlu.f
    x ./pda_dpcoef.f
    x ./pda_dpolft.f
    x ./pda_qsiad.f
    x ./pda_src.f
    x ./pda_xermsg.f
    x ./pgfiles/
    x ./pldef.f
    x ./plset.f
    x ./pr_mdian1.f
    x ./prg_polyd.f
    x ./probks.f
    x ./._rd_ablin.f
    x ./rd_ablin.f
    x ./rd_adnois.f
    x ./rd_advprof.f
    x ./rd_alias.f
    x ./rd_bclimsreset.f
    x ./rd_comalias.f
    x ./rd_compreset.f
    x ./rd_cuchstr.f
    x ./rd_cufilnm.f
    x ./rd_cuplot.f
    x ./rd_cuwvrest.f
    x ./rd_cydata.f
    x ./rd_daswap.f
    x ./rd_dblid.f
    x ./rd_dmanip.f
    x ./rd_drebin.f
    x ./rd_drebing.f
    x ./rd_dsetbad.f
    x ./rd_dvcont.f
    x ./rd_esoftread.f
    x ./rd_estcolb.f
    x ./rd_ewwav.f
    x ./rd_extabs.f
    x ./rd_fluxbb.f
    x ./rd_flxval.f
    x ./rd_gen.f
    x ./rd_gprof.f
    x ./rd_gprofe.f
    x ./rd_gpsim.f
    x ./rd_hdust.f
    x ./rd_intinfo.f
    x ./rd_intvtab.f
    x ./rd_jbsim.f
    x ./rd_markreg.f
    x ./rd_mattrd.f
    x ./rd_meantau.f
    x ./rd_medvp.f
    x ./rd_moderrs.f
    x ./rd_multiset.f
    x ./rd_pgannot.f
    x ./rd_plcset.f
    x ./rd_pmpset.f
    x ./rd_prhelp.dat
    x ./rd_prhelp.f
    x ./rd_qdatafiles.f
    x ./rd_readlinwav.f
    x ./rd_sclsig.f
    x ./rd_scrng.f
    x ./rd_scwval.f
    x ./rd_seekmax2.f
    x ./rd_setintv.f
    x ./rd_sizes.f
    x ./rd_splvtick.f
    x ./rd_stackz.f
    x ./rd_start.dat
    x ./rd_stplot.f
    x ./rd_taueff.f
    x ./rd_tgplots.f
    x ./rd_tickvpfl.f
    x ./rd_ulimse.f
    x ./rd_ulinfo.f
    x ./rd_wrfits.f
    x ./rd_wrwcfits.f
    x ./rd_wvnewt.f
    x ./rd_wvsc.f
    x ./rd_xcdc.f
    x ./rd_zcorr.f
    x ./rd_ztabcf.f
    x ./rhedit.f
    x ./sepvar.f
    x ./splot.f
    x ./spvoigt.f
    x ./svbksbd.f
    x ./svdajc.f
    x ./svdcmpd.f
    x ./svdfit.f
    x ./tempccc.f
    x ./tickpg.f
    x ./tikset.f
    x ./tstfit3d.f
    x ./udchole.f
    x ./varythis.f
    x ./voigt_king.f
    x ./._voigt_subroutine.f
    x ./voigt_subroutine.f
    x ./vp_abfind.f
    x ./vp_abundset.f
    x ./vp_addline.f
    x ./vp_addxlink.f
    x ./vp_archwav.f
    x ./vp_asciin.f
    x ./vp_ationsep.f
    x ./vp_atomass.f
    x ./vp_blfixf.f
    x ./vp_cfcopy.f
    x ./vp_chanwav.f
    x ./vp_charlims.f
    x ./vp_chdisprof.f
    x ./vp_checkb.f
    x ./vp_checkn.f
    x ./vp_chipconv.f
    x ./vp_chsation.f
    x ./vp_chspread.f
    x ./vp_cmssrc.f
    x ./vp_curse.f
    x ./vp_cval.f
    x ./vp_dattim.f
    x ./vp_dattim_alt.f
    x ./vp_deriv.f
    x ./vp_dlinin.f
    x ./vp_dlset.f
    x ./vp_dlyccft.f
    x ./vp_dotick.f
    x ./vp_emlsmry.f
    x ./vp_empval.f
    x ./vp_emscchk.f
    x ./vp_emtset.f
    x ./vp_endorder.f
    x ./vp_endrej.f
    x ./vp_errej.f
    x ./vp_ewred.f
    x ./vp_f13fin.f
    x ./vp_f13finx.f
    x ./vp_f13read.f
    x ./vp_f_sigscl.f
    x ./vp_fgf13write.f
    x ./vp_flchderivs.f
    x ./vp_fnzbin.f
    x ./vp_getargs.f
    x ./vp_getres.f
    x ./vp_gglwcoeff.f
    x ./vp_gsclfts.f
    x ./vp_gwatfits.f
    x ./vp_initval.f
    x ./vp_ionregcheck.f
    x ./vp_ksvnorm.f
    x ./vp_lineid.f
    x ./vp_linlim.f
    x ./vp_listfiles.f
    x ./vp_lycont.f
    x ./vp_lyconte.f
    x ./vp_minpol.f
    x ./vp_minquad.f
    x ./vp_nearlin.f
    x ./vp_newtfxl.f
    x ./vp_pareorder.f
    x ./vp_parmcest.f
    x ./vp_pgbegin.f
    x ./vp_pgcurs.f
    x ./vp_plcset.f
    x ./vp_poisson.f
    x ./vp_presetsys.f
    x ./vp_psfinterp.f
    x ./vp_rdbwvfts.f
    x ./._vp_rdfitsext.f
    x ./vp_rdfitsext.f
    x ./vp_rdlines.f
    x ./vp_rdspecial.f
    x ./vp_rdwaveval.f
    x ./vp_readfits.f
    x ./vp_runstst.f
    x ./vp_rwlims.f
    x ./vp_scontf.f
    x ./vp_setintv.f
    x ./vp_setoptn.f
    x ./vp_setup.dat
    x ./vp_sizes.f
    x ./vp_smry.f
    x ./vp_spdatin.f
    x ./vp_splot.dat
    x ./vp_spread.f
    x ./vp_spvoigte.f
    x ./vp_srcpath.f
    x ./vp_startval.f
    x ./vp_stration.f
    x ./vp_stripcmt.f
    x ./vp_subchspread.f
    x ./vp_sumlinclear.f
    x ./vp_sysdel.f
    x ./vp_tdcolset.f
    x ./vp_tiedb.f
    x ./vp_tiedrop.f
    x ./vp_tieval.f
    x ./vp_trimattab.f
    x ./vp_tsetxref.f
    x ./vp_ucheck.f
    x ./vp_ucoptv.f
    x ./vp_ucprinerr.f
    x ./vp_ucprinit.f
    x ./vp_usdef.f
    x ./vp_vreset.f
    x ./vp_wrt25.f
    x ./vp_wval.f
    x ./vp_wvalch.f
    x ./vp_wvalsubch.f
    x ./vp_wverass.f
    x ./vpbrentres.f
    x ./vpf_bvalsp.f
    x ./vpf_dvresn.f
    x ./vpfit.f
    x ./vpgti.f
    x ./._vppreset.cmd
    x ./vppreset.cmd
    x ./vpreset.awk
    x ./wcor.f
    x ./wval.f
    x ./xmakefile
    x ./pgfiles/Al.pg
    x ./pgfiles/AlSi.pg
    x ./pgfiles/CaII.pg
    x ./pgfiles/CI2.pg
    x ./pgfiles/CIH2.pg
    x ./pgfiles/CII.pg
    x ./pgfiles/CII.pg~
    x ./pgfiles/._CIIOI.pg
    x ./pgfiles/CIIOI.pg
    x ./pgfiles/CIIOI.pg~
    x ./pgfiles/CIISi.pg
    x ./pgfiles/CIV.pg
    x ./pgfiles/CIVSiIV.pg
    x ./pgfiles/CrZn.pg
    x ./pgfiles/FeII.pg
    x ./pgfiles/FeIIhi.pg
    x ./pgfiles/hc.pg
    x ./pgfiles/HC4.pg
    x ./pgfiles/HC4.pg~
    x ./pgfiles/HCNlo.pg
    x ./pgfiles/hcnosi.pg
    x ./pgfiles/hcsi.pg
    x ./pgfiles/HD.pg
    x ./pgfiles/HDs.pg
    x ./pgfiles/HHC4N5O6.pg
    x ./pgfiles/HHC4O6.pg
    x ./pgfiles/HHC4O6Si4.pg
    x ./pgfiles/HHC4Si4.pg
    x ./pgfiles/HHCSi34.pg
    x ./pgfiles/HHCSi34.pg~
    x ./pgfiles/HHO6.pg
    x ./pgfiles/hiHCNO.pg
    x ./pgfiles/hiHCOSi.pg
    x ./pgfiles/hiion.pg
    x ./pgfiles/hiSi.pg
    x ./pgfiles/HNI.pg
    x ./pgfiles/HOhi.pg
    x ./pgfiles/HQhi.pg
    x ./pgfiles/HSSi.pg
    x ./pgfiles/Hvhi.pg
    x ./pgfiles/LaCIV.pg
    x ./pgfiles/LaN.pg
    x ./pgfiles/laonly.pg
    x ./pgfiles/LL.pg
    x ./pgfiles/LLO.pg
    x ./pgfiles/llyman.pg
    x ./pgfiles/loc.pg
    x ./pgfiles/local.pg
    x ./pgfiles/lohi.pg
    x ./pgfiles/lointhi.pg
    x ./pgfiles/lointz.pg
    x ./pgfiles/loion.pg
    x ./pgfiles/loloz.pg
    x ./pgfiles/lorare.pg
    x ./pgfiles/lowz.pg
    x ./pgfiles/lowzFeII.pg
    x ./pgfiles/ly4.pg
    x ./pgfiles/lyabg.pg
    x ./pgfiles/lyman.pg
    x ./pgfiles/lyman8.pg
    x ./pgfiles/MgFeMn.pg
    x ./pgfiles/MgICI.pg
    x ./pgfiles/MgII.pg
    x ./pgfiles/MgIIFeII.pg
    x ./pgfiles/NaCa.pg
    x ./pgfiles/NiII.pg
    x ./pgfiles/nitrogen.pg
    x ./pgfiles/NV.pg
    x ./pgfiles/OI.pg
    x ./pgfiles/OIArI.pg
    x ./pgfiles/oneline.pg
    x ./pgfiles/OSipair.pg
    x ./pgfiles/OVI.pg
    x ./pgfiles/OVIHI.pg
    x ./pgfiles/range.pg
    x ./pgfiles/sdlaion.pg
    x ./pgfiles/SiII.pg
    x ./pgfiles/SiIIfs.pg
    x ./pgfiles/SiIIl.pg
    x ./pgfiles/SiIV.pg
    x ./pgfiles/stloion.pg
    x ./pgfiles/strong.pg
    x ./pgfiles/TiII.pg
    x ./pgfiles/ultralow.pg
    x ./pgfiles/vhiHCNO.pg
    x ./docs/rdgen11.1.pdf
    x ./docs/sample.txt.gz
    x ./docs/._vpfit11.1.pdf
    x ./docs/vpfit11.1.pdf

# Installing crap in notebooks

https://jakevdp.github.io/blog/2017/12/05/installing-python-packages-from-jupyter/

```python
import sys
!conda install --prefix {sys.prefix} numpy -y
```

```python
# # Install a pip package in the current Jupyter kernel
# import sys
# !{sys.executable} -m pip install numpy
```

```python

```

```python
normal_argument = 12.4
second_argument = 98.4

arg_with_spaces = "the secret to life"
```

```bash
%%bash -s {normal_argument} {second_argument}
echo "This script knows the value of the argument: $1"
echo "It also has no trouble with the second argument: $2"
```

    This script knows the value of the argument: 12.4
    It also has no trouble with the second argument: 98.4

```bash
%%bash -s "$arg_with_spaces"
echo "This bash script knows $1."
```

    This bash script knows the secret to life.

```python
ls
```

    00-Overview.ipynb                Example-pycat.ipynb
    01-Tips-and-tricks.ipynb         Untitled.ipynb
    02-NBExtensions.ipynb            delete.ipynb
    03-Interactive-Splines.ipynb     ex1.png
    04-Autoreload.ipynb              history.ipynb
    05-SQL-Example.ipynb             jupyterlab-example.md
    06-Advanced-Visualization.ipynb  pivottablejs.html
    07-Some_basics.ipynb             presidents.sqlite
    08-More_basics.ipynb             vpfit10/
    09-Extras.ipynb                  vpfit10.2.tar.gz
    10-R-stuff.ipynb                 vpfit10.2.tar.gz.1
    11-older-stuff.ipynb             vpfit10.2.tar.gz.2
    12-old-pandas.ipynb              vpfit10.2.tar.gz.3
    3d-example-plot.html             vpfit11/
    Advanced-Notebook-Tricks.ipynb   vpfit11.1.tar.gz
    Basic-Plotting.ipynb             vpfit11.1.tar.gz.1
    Data_Cleaning.ipynb

```python
!ls
```

    00-Overview.ipynb               Example-pycat.ipynb
    01-Tips-and-tricks.ipynb        Untitled.ipynb
    02-NBExtensions.ipynb           delete.ipynb
    03-Interactive-Splines.ipynb    ex1.png
    04-Autoreload.ipynb             history.ipynb
    05-SQL-Example.ipynb            jupyterlab-example.md
    06-Advanced-Visualization.ipynb pivottablejs.html
    07-Some_basics.ipynb            presidents.sqlite
    08-More_basics.ipynb            vpfit10
    09-Extras.ipynb                 vpfit10.2.tar.gz
    10-R-stuff.ipynb                vpfit10.2.tar.gz.1
    11-older-stuff.ipynb            vpfit10.2.tar.gz.2
    12-old-pandas.ipynb             vpfit10.2.tar.gz.3
    3d-example-plot.html            vpfit11
    Advanced-Notebook-Tricks.ipynb  vpfit11.1.tar.gz
    Basic-Plotting.ipynb            vpfit11.1.tar.gz.1
    Data_Cleaning.ipynb

```python
%ls
```

    00-Overview.ipynb                Example-pycat.ipynb
    01-Tips-and-tricks.ipynb         Untitled.ipynb
    02-NBExtensions.ipynb            delete.ipynb
    03-Interactive-Splines.ipynb     ex1.png
    04-Autoreload.ipynb              history.ipynb
    05-SQL-Example.ipynb             jupyterlab-example.md
    06-Advanced-Visualization.ipynb  pivottablejs.html
    07-Some_basics.ipynb             presidents.sqlite
    08-More_basics.ipynb             vpfit10/
    09-Extras.ipynb                  vpfit10.2.tar.gz
    10-R-stuff.ipynb                 vpfit10.2.tar.gz.1
    11-older-stuff.ipynb             vpfit10.2.tar.gz.2
    12-old-pandas.ipynb              vpfit10.2.tar.gz.3
    3d-example-plot.html             vpfit11/
    Advanced-Notebook-Tricks.ipynb   vpfit11.1.tar.gz
    Basic-Plotting.ipynb             vpfit11.1.tar.gz.1
    Data_Cleaning.ipynb

```python
ls vpfit10/
```

```python
tailthing = "*.ipynb"
```

```python
tailthing
```

```python
!ls {tailthing}
```

    00-Overview.ipynb               10-R-stuff.ipynb
    01-Tips-and-tricks.ipynb        11-older-stuff.ipynb
    02-NBExtensions.ipynb           12-old-pandas.ipynb
    03-Interactive-Splines.ipynb    Advanced-Notebook-Tricks.ipynb
    04-Autoreload.ipynb             Basic-Plotting.ipynb
    05-SQL-Example.ipynb            Data_Cleaning.ipynb
    06-Advanced-Visualization.ipynb Example-pycat.ipynb
    07-Some_basics.ipynb            Untitled.ipynb
    08-More_basics.ipynb            delete.ipynb
    09-Extras.ipynb                 history.ipynb

```python
output = !ls
```

```python
output
```

    ['00-Overview.ipynb',
     '01-Tips-and-tricks.ipynb',
     '02-NBExtensions.ipynb',
     '03-Interactive-Splines.ipynb',
     '04-Autoreload.ipynb',
     '05-SQL-Example.ipynb',
     '06-Advanced-Visualization.ipynb',
     '07-Some_basics.ipynb',
     '08-More_basics.ipynb',
     '09-Extras.ipynb',
     '10-R-stuff.ipynb',
     '11-older-stuff.ipynb',
     '12-old-pandas.ipynb',
     '3d-example-plot.html',
     'Advanced-Notebook-Tricks.ipynb',
     'Basic-Plotting.ipynb',
     'Data_Cleaning.ipynb',
     'Example-pycat.ipynb',
     'Untitled.ipynb',
     'delete.ipynb',
     'ex1.png',
     'history.ipynb',
     'jupyterlab-example.md',
     'pivottablejs.html',
     'presidents.sqlite',
     'vpfit10',
     'vpfit10.2.tar.gz',
     'vpfit10.2.tar.gz.1',
     'vpfit10.2.tar.gz.2',
     'vpfit10.2.tar.gz.3',
     'vpfit11',
     'vpfit11.1.tar.gz',
     'vpfit11.1.tar.gz.1']

```python
%env
```

    {'SSH_AGENT_PID': '2443',
     'TERM_PROGRAM': 'iTerm.app',
     'TERM': 'xterm-color',
     'SHELL': '/bin/bash',
     'initials': 'jbw',
     'CLICOLOR': '1',
     'TMPDIR': '/var/folders/yk/2rgr0zrj0614jntythcnlyvw0000gn/T/',
     'CONDA_SHLVL': '1',
     'Apple_PubSub_Socket_Render': '/private/tmp/com.apple.launchd.cUdZIggvpB/Render',
     'CONDA_PROMPT_MODIFIER': '(dspy3) ',
     'BASH': '/Users/jonathan/.dotfiles',
     'TERM_PROGRAM_VERSION': '3.2.6',
     'OLDPWD': '/Users/jonathan',
     'TERM_SESSION_ID': 'w0t0p0:D95C8318-F139-47A5-AE4D-2B20E6014310',
     'USER': 'jonathan',
     'CONDA_EXE': '/Users/jonathan/miniconda3/bin/conda',
     'SSH_AUTH_SOCK': '/var/folders/yk/2rgr0zrj0614jntythcnlyvw0000gn/T//ssh-mm7alB0U2YiW/agent.2441',
     '__CF_USER_TEXT_ENCODING': '0x0:0:0',
     'PATH': '/Users/jonathan/miniconda3/envs/dspy3/bin:/Users/jonathan/miniconda3/envs/dspy3/bin:/Users/jonathan/miniconda3/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/opt/X11/bin',
     'CONDA_PREFIX': '/Users/jonathan/miniconda3/envs/dspy3',
     'PWD': '/Users/jonathan/github/jupyter-tips-and-tricks',
     'EDITOR': 'vim',
     'LANG': 'en_US.UTF-8',
     'ITERM_PROFILE': 'Default',
     'XPC_FLAGS': '0x0',
     'ITERM_ORIG_PS1': '(dspy3) \\n# \\w\\n# \\[\\e[0;32m\\]\\h \\[\\e[0m\\]\\[\\e[0;36m\\]${git_branch}\\[\\e[0;31m\\]$git_dirty\\[\\e[0m\\]$ ',
     'PS1': '(dspy3) \\n# \\w\\n# \\[\\e[0;32m\\]\\h \\[\\e[0m\\]\\[\\e[0;36m\\]${git_branch}\\[\\e[0;31m\\]$git_dirty\\[\\e[0m\\]$ ',
     'PS3': '#? ',
     'HISTCONTROL': 'ignoreboth',
     'PS2': '> ',
     'XPC_SERVICE_NAME': '0',
     'GITAWAREPROMPT': '/Users/jonathan/.bash/git-aware-prompt',
     'SHLVL': '1',
     'PS4': '+',
     'HOME': '/Users/jonathan',
     'COLORFGBG': '15;0',
     'ITERM_PREV_PS1': '\\[\x1b]133;D;$?\x07\x1b]133;A\x07\\](dspy3) \\n# \\w\\n# \\[\\e[0;32m\\]\\h \\[\\e[0m\\]\\[\\e[0;36m\\]${git_branch}\\[\\e[0;31m\\]$git_dirty\\[\\e[0m\\]$ \\[\x1b]133;B\x07\\]',
     'ITERM_SESSION_ID': 'w0t0p0:D95C8318-F139-47A5-AE4D-2B20E6014310',
     'CONDA_PYTHON_EXE': '/Users/jonathan/miniconda3/bin/python',
     'LOGNAME': 'jonathan',
     'CONDA_DEFAULT_ENV': 'dspy3',
     'PROMPT_COMMAND': '__bp_precmd_invoke_cmd; echo -ne "\\033]0;${USER}@${HOSTNAME%%.*} ${PWD}"; echo -ne "\\007"; __bp_interactive_mode',
     'DISPLAY': '/private/tmp/com.apple.launchd.a8oASgcyPu/org.macosforge.xquartz:0',
     'COLORTERM': 'truecolor',
     '_': '/Users/jonathan/miniconda3/envs/dspy3/bin/jupyter',
     'GIT_PYTHON_REFRESH': 'quiet',
     'JPY_PARENT_PID': '52021',
     'PAGER': 'cat',
     'GIT_PAGER': 'cat',
     'MPLBACKEND': 'module://ipykernel.pylab.backend_inline'}

```python

```

## Danger zone

    _, _N and _iN

```python
!pwd
```

    /Users/jonathan/github/jupyter-tips-and-tricks/notebooks

```python
a = 3
a
```

    3

```python
_i56
```

    'a = 3\na'

```python
_56
```

    3

```python
_
```

    75

```python
b = 5
b * 15
```

    75

```python
c = a * b
```

```python

```

```python

```

```python

```

```python
%notebook new-name.ipynb
```

```python

```

```python

```

```python
!subl 01-Tips-and-tricks.ipynb
```

```python

```

```python

```

```python

```

```python

```

```python

```


# `GiRaFFE_NRPy`: Solving the Induction Equation

## Author: Patrick Nelson

**Notebook Status:** Validated

**Validation Notes:** This code has been validated by showing that it converges to the exact answer at the expected order.

### NRPy+ Source Code for this module:
* [GiRaFFE_NRPy/GiRaFFE_NRPy_Afield_flux_handwritten.py](../../edit/in_progress/GiRaFFE_NRPy/GiRaFFE_NRPy_Afield_flux_handwritten.py)

## Introduction

This notebook documents the function from the original `GiRaFFE` that calculates the flux for $A_i$ according to the method of Harten, Lax, von Leer, and Einfeldt (HLLE), assuming that we have calculated the values of the velocity and magnetic field on the cell faces according to the piecewise-parabolic method (PPM) of [Colella and Woodward (1984)](https://crd.lbl.gov/assets/pubs_presos/AMCS/ANAG/A141984.pdf), modified for the case of GRFFE.

Our goal in this module is to write the code necessary to solve the induction equation
$$
\partial_t A_i = \underbrace{\epsilon_{ijk} v^j B^k}_{\rm Flux\ terms} - \underbrace{\partial_i \left(\alpha \Phi - \beta^j A_j \right)}_{\rm Gauge\ terms}.
$$
To properly handle the flux terms and avoid problems with shocks, we cannot simply take a cross product of the velocity and magnetic field at the cell centers. Instead, we must solve the Riemann problem at the cell faces using the reconstructed values of the velocity and magnetic field on either side of the cell faces. The reconstruction is done using the piecewise-parabolic method (PPM) (see [here](Tutorial-GiRaFFE_NRPy-PPM.ipynb)); in this module, we will assume that that step has already been done. Metric quantities are assumed to have been interpolated to cell faces, as is done in [this](Tutorial-GiRaFFE_NRPy-Metric_Face_Values.ipynb) tutorial.

Tóth's [paper](https://www.sciencedirect.com/science/article/pii/S0021999100965197?via%3Dihub), Eqs. 30 and 31, are one of the first implementations of such a scheme. The original GiRaFFE used a 2D version of the algorithm from [Del Zanna, et al. (2002)](https://arxiv.org/abs/astro-ph/0210618); but since we are not using staggered grids, we can greatly simplify this algorithm with respect to the version used in the original `GiRaFFE`. Instead, we will adapt the implementations of the algorithm used in [Mewes, et al. (2020)](https://arxiv.org/abs/2002.06225) and [Giacomazzo, et al. (2011)](https://arxiv.org/abs/1009.2468), Eqs. 3-11.

We first write the flux contribution to the induction equation RHS as
$$
\partial_t A_i = -E_i,
$$
where the electric field $E_i$ is given in ideal MHD (of which FFE is a subset) as
$$
-E_i = \epsilon_{ijk} v^j B^k,
$$
where $v^i$ is the drift velocity,  $B^i$ is the magnetic field, and $\epsilon_{ijk} = \sqrt{\gamma} [ijk]$ is the Levi-Civita tensor.
In Cartesian coordinates,
\begin{align}
-E_x &= [F^y(B^z)]_x = -[F^z(B^y)]_x \\
-E_y &= [F^z(B^x)]_y = -[F^x(B^z)]_y \\
-E_z &= [F^x(B^y)]_z = -[F^y(B^x)]_z, \\
\end{align}
where
$$
[F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i).
$$
To compute the actual contribution to the RHS in some direction $i$, we average the above listed field as calculated on the $+j$, $-j$, $+k$, and $-k$ faces. That is, at some point $(i,j,k)$ on the grid,
\begin{align}
-E_x(x_i,y_j,z_k) &= \frac{1}{4} \left( [F_{\rm HLL}^y(B^z)]_{x(i,j+1/2,k)}+[F_{\rm HLL}^y(B^z)]_{x(i,j-1/2,k)}-[F_{\rm HLL}^z(B^y)]_{x(i,j,k+1/2)}-[F_{\rm HLL}^z(B^y)]_{x(i,j,k-1/2)} \right) \\
-E_y(x_i,y_j,z_k) &= \frac{1}{4} \left( [F_{\rm HLL}^z(B^x)]_{y(i,j,k+1/2)}+[F_{\rm HLL}^z(B^x)]_{y(i,j,k-1/2)}-[F_{\rm HLL}^x(B^z)]_{y(i+1/2,j,k)}-[F_{\rm HLL}^x(B^z)]_{y(i-1/2,j,k)} \right) \\
-E_z(x_i,y_j,z_k) &= \frac{1}{4} \left( [F_{\rm HLL}^x(B^y)]_{z(i+1/2,j,k)}+[F_{\rm HLL}^x(B^y)]_{z(i-1/2,j,k)}-[F_{\rm HLL}^y(B^x)]_{z(i,j+1/2,k)}-[F_{\rm HLL}^y(B^x)]_{z(i,j-1/2,k)} \right). \\
\end{align}
Note the use of $F_{\rm HLL}$ here. This change signifies that the quantity output here is from the HLLE Riemann solver. Note also the indices on the fluxes. Values of $\pm 1/2$ indicate that these are computed on cell faces using the reconstructed values of $v^i$ and $B^i$ and the interpolated values of the metric gridfunctions. So,
$$
F_{\rm HLL}^i(B^j) = \frac{c_{\rm min} F_{\rm R}^i(B^j) + c_{\rm max} F_{\rm L}^i(B^j) - c_{\rm min} c_{\rm max} (B_{\rm R}^j-B_{\rm L}^j)}{c_{\rm min} + c_{\rm max}}.
$$

The speeds $c_\min$ and $c_\max$ are characteristic speeds that waves can travel through the plasma. In GRFFE, the expressions defining them reduce a function of only the metric quantities. $c_\min$ is the negative of the minimum amongst the speeds $c_-$ and $0$ and $c_\max$ is the maximum amongst the speeds $c_+$ and $0$. The speeds $c_\pm = \left. \left(-b \pm \sqrt{b^2-4ac}\right)\middle/ \left(2a\right) \right.$ must be calculated on both the left and right faces, where
$$a = 1/\alpha^2,$$
$$b = 2 \beta^i / \alpha^2$$
and $$c = g^{ii} - (\beta^i)^2/\alpha^2.$$
An outline of a general finite-volume method is as follows, with the current step in bold:
1. The Reconstruction Step - Piecewise Parabolic Method
    1. Within each cell, fit to a function that conserves the volume in that cell using information from the neighboring cells
        * For PPM, we will naturally use parabolas
    1. Use that fit to define the state at the left and right interface of each cell
    1. Apply a slope limiter to mitigate Gibbs phenomenon
1. Interpolate the value of the metric gridfunctions on the cell faces
1. **Solve the Riemann Problem - Harten, Lax, von Leer, Einfeldt(This notebook, $E_i$ only)**
    1. **Use the left and right reconstructed states to calculate the unique state at boundary**

We will assume in this notebook that the reconstructed velocities and magnetic fields are available on cell faces as input. We will also assume that the metric gridfunctions have been interpolated on the cell faces.

Solving the Riemann problem, then, consists of two substeps: First, we compute the flux through each face of the cell. Then, we add the average of these fluxes to the right-hand side of the evolution equation for the vector potential.

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

This notebook is organized as follows

0. [Step 1](#prelim): Preliminaries
1. [Step 2](#code): Write the C code
    1. [Step 2.a](#hydro_speed): GRFFE characteristic wave speeds
    1. [Step 2.b](#fluxes): Compute the HLLE fluxes
1. [Step 3](#code_validation): Code Validation
1. [Step 4](#latex_pdf_output): Output this notebook to $\LaTeX$-formatted PDF file

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

This first block of code just sets up a subdirectory within `GiRaFFE_standalone_Ccodes/` to which we will write the C code.

```python
# Step 0: 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)

import cmdline_helper as cmd     # NRPy+: Multi-platform Python command-line interface
Ccodesdir = "GiRaFFE_standalone_Ccodes/RHSs"
cmd.mkdir(os.path.join(Ccodesdir))
```

# Step 2: Write the C code \[Back to [top](#toc)\]
$$\label{code}$$

Recall that there were three equations that we need to code; note also that they are all cyclic permutations of each other. Thus, we only need to code one of them, as we can simply permute the inputs into the function to calculate all three equations. We will use the $E_z$ equation:
$$
-E_z(x_i,y_j,z_k) = \frac{1}{4} \left( [F_{\rm HLL}^x(B^y)]_{z(i+1/2,j,k)}+[F_{\rm HLL}^x(B^y)]_{z(i-1/2,j,k)}-[F_{\rm HLL}^y(B^x)]_{z(i,j+1/2,k)}-[F_{\rm HLL}^y(B^x)]_{z(i,j-1/2,k)} \right),
$$
where
$$
-E_z = [F^x(B^y)]_z = -[F^y(B^x)]_z
$$
and
$$
[F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i).
$$

## Step 2.a: GRFFE characteristic wave speeds \[Back to [top](#toc)\]
$$\label{hydro_speed}$$

Next, we will find the speeds at which the hydrodynamics waves propagate. We start from the speed of light (since FFE deals with very diffuse plasmas), which is $c=1.0$ in our chosen units. We then find the speeds $c_+$ and $c_-$ on each face with the function `find_cp_cm`; then, we find minimum and maximum speeds possible from among those.

Below is the source code for `find_cp_cm`, edited to work with the NRPy+ version of GiRaFFE. One edit we need to make in particular is to the term `psim4*gupii` in the definition of `c`; that was written assuming the use of the conformal metric $\tilde{g}^{ii}$. Since we are not using that here, and are instead using the ADM metric, we should not multiply by $\psi^{-4}$.

```c
static inline void find_cp_cm(REAL &cplus,REAL &cminus,const REAL v02,const REAL u0,
                              const REAL vi,const REAL lapse,const REAL shifti,
                              const REAL gammadet,const REAL gupii) {
  const REAL u0_SQUARED=u0*u0;
  const REAL ONE_OVER_LAPSE_SQUARED = 1.0/(lapse*lapse);
  // sqrtgamma = psi6 -> psim4 = gammadet^(-1.0/3.0)
  const REAL psim4 = pow(gammadet,-1.0/3.0);
  //Find cplus, cminus:
  const REAL a = u0_SQUARED * (1.0-v02) + v02*ONE_OVER_LAPSE_SQUARED;
  const REAL b = 2.0* ( shifti*ONE_OVER_LAPSE_SQUARED * v02 - u0_SQUARED * vi * (1.0-v02) );
  const REAL c = u0_SQUARED*vi*vi * (1.0-v02) - v02 * ( gupii -
                                                               shifti*shifti*ONE_OVER_LAPSE_SQUARED);
  REAL detm = b*b - 4.0*a*c;
  //ORIGINAL LINE OF CODE:
  //if(detm < 0.0) detm = 0.0;
  //New line of code (without the if() statement) has the same effect:
  detm = sqrt(0.5*(detm + fabs(detm))); /* Based on very nice suggestion from Roland Haas */

  cplus = 0.5*(detm-b)/a;
  cminus = -0.5*(detm+b)/a;
  if (cplus < cminus) {
    const REAL cp = cminus;
    cminus = cplus;
    cplus = cp;
  }
}
```
Comments documenting this have been excised for brevity, but are reproduced in $\LaTeX$ [below](#derive_speed).

We could use this code directly, but there's substantial improvement we can make by changing the code into a NRPyfied form. Note the `if` statement; NRPy+ does not know how to handle these, so we must eliminate it if we want to leverage NRPy+'s full power. (Calls to `fabs()` are also cheaper than `if` statements.) This can be done if we rewrite this, taking inspiration from the other eliminated `if` statement documented in the above code block:
```c
  cp = 0.5*(detm-b)/a;
  cm = -0.5*(detm+b)/a;
  cplus  = 0.5*(cp+cm+fabs(cp-cm));
  cminus = 0.5*(cp+cm-fabs(cp-cm));
```
This can be simplified further, by substituting `cp` and `cm` into the below equations and eliminating terms as appropriate. First note that `cp+cm = -b/a` and that `cp-cm = detm/a`. Thus,
```c
  cplus  = 0.5*(-b/a + fabs(detm/a));
  cminus = 0.5*(-b/a - fabs(detm/a));
```
This fulfills the original purpose of the `if` statement in the original code because we have guaranteed that $c_+ \geq c_-$.

This leaves us with an expression that can be much more easily NRPyfied. So, we will rewrite the following in NRPy+, making only minimal changes to be proper Python. However, it turns out that we can make this even simpler. In GRFFE, $v_0^2$ is guaranteed to be exactly one. In GRMHD, this speed was calculated as $$v_{0}^{2} = v_{\rm A}^{2} + c_{\rm s}^{2}\left(1-v_{\rm A}^{2}\right),$$ where the Alfvén speed $v_{\rm A}^{2}$ $$v_{\rm A}^{2} = \frac{b^{2}}{\rho_{b}h + b^{2}}.$$ So, we can see that when the density $\rho_b$ goes to zero, $v_{0}^{2} = v_{\rm A}^{2} = 1$. Then
\begin{align}
a &= (u^0)^2 (1-v_0^2) + v_0^2/\alpha^2 \\
&= 1/\alpha^2 \\
b &= 2 \left(\beta^i v_0^2 / \alpha^2 - (u^0)^2 v^i (1-v_0^2)\right) \\
&= 2 \beta^i / \alpha^2 \\
c &= (u^0)^2 (v^i)^2 (1-v_0^2) - v_0^2 \left(\gamma^{ii} - (\beta^i)^2/\alpha^2\right) \\
&= -\gamma^{ii} + (\beta^i)^2/\alpha^2,
\end{align}
are simplifications that should save us some time; we can see that $a \geq 0$ is guaranteed. Note that we also force `detm` to be positive. Thus, `detm/a` is guaranteed to be positive itself, rendering the calls to `nrpyAbs()` superfluous. Furthermore, we eliminate any dependence on the Valencia 3-velocity and the time component of the four-velocity, $u^0$. This leaves us free to solve the quadratic in the familiar way: $$c_\pm = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$$.

In flat spacetime, where $\alpha=1$, $\beta^i=0$, and $\gamma^{ij} = \delta^{ij}$, $c_+ > 0$ and $c_- < 0$. For the HLLE solver, we will need both `cmax` and `cmin` to be positive; we also want to choose the speed that is larger in magnitude because overestimating the characteristic speeds will help damp unwanted oscillations. (However, in GRFFE, we only get one $c_+$ and one $c_-$, so we only need to fix the signs here.)

We will now write a function in C similar to the one used in the old `GiRaFFE`. Notice that since we eliminated the dependence on velocities, none of the input quantities are different on either side of the face. While the original `GiRaFFE` used two functions, the simplifications we've made will allow us to get away with only one without obfuscating anything, with the only downside being that it's a little less obvious how this is related to the GRMHD implementation. We use the same technique as above to replace the `if` statements inherent to the `MAX()` and `MIN()` functions.

This function requires the inverse metric; the following code will easily generate all the needed expressions:
```
from outputC import outputC      # NRPy+: Core C code output module
import indexedexp as ixp         # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
gammaDD = ixp.declarerank2("gammaDD","sym01",DIM=3)
gammaUU,gammaDET = ixp.symm_matrix_inverter3x3(gammaDD)
outputC([gammaUU[0][0],gammaUU[1][1],gammaUU[2][2]],["gammaUU00","gammaUU11","gammaUU22"])
```
We modify the output slightly, by putting the final expressions for `gammaUU00`, `gammaUU11`, and `gammaUU22` in a switch statement and changing the assignment to a common variable `gammaUUii`, allowing us to only compute the value needed.

```python
from outputC import outputC # NRPy+: Core C code output module
import sympy as sp               # SymPy: The Python computer algebra package upon which NRPy+ depends
import indexedexp as ixp         # NRPy+: Symbolic indexed expression (e.g., tensors, vectors, etc.) support
import GiRaFFE_NRPy.GiRaFFE_NRPy_Characteristic_Speeds as chsp

gammaDD = ixp.declarerank2("gammaDD","sym01",DIM=3)
betaU = ixp.declarerank1("betaU",DIM=3)
alpha = sp.sympify("alpha")

for flux_dirn in range(3):
    chsp.find_cmax_cmin(flux_dirn,gammaDD,betaU,alpha)
    Ccode_kernel = outputC([chsp.cmax,chsp.cmin],["cmax","cmin"],"returnstring",params="outCverbose=False,CSE_sorting=none")
    Ccode_kernel = Ccode_kernel.replace("cmax","*cmax").replace("cmin","*cmin")
    Ccode_kernel = Ccode_kernel.replace("betaU0","betaUi").replace("betaU1","betaUi").replace("betaU2","betaUi")

    with open(os.path.join(Ccodesdir,"compute_cmax_cmin_dirn"+str(flux_dirn)+".h"),"w") as file:
        file.write(Ccode_kernel)
```

```python
%%writefile $Ccodesdir/calculate_E_field_flat_all_in_one.h
void find_cmax_cmin(const REAL gammaDD00, const REAL gammaDD01, const REAL gammaDD02,
                    const REAL gammaDD11, const REAL gammaDD12, const REAL gammaDD22,
                    const REAL betaUi, const REAL alpha, const int flux_dirn,
                    REAL *cmax, REAL *cmin) {
    switch(flux_dirn) {
        case 0:
#include "compute_cmax_cmin_dirn0.h"
            break;
        case 1:
#include "compute_cmax_cmin_dirn1.h"
            break;
        case 2:
#include "compute_cmax_cmin_dirn2.h"
            break;
        default:
            printf("Invalid parameter flux_dirn!"); *cmax = 1.0/0.0; *cmin = 1.0/0.0;
            break;
    }
}
```

    Overwriting GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

## Step 2.b: Compute the HLLE fluxes \[Back to [top](#toc)\]
$$\label{fluxes}$$

First, we write a standard HLLE solver based on eq. 3.15 in [the HLLE paper](https://epubs.siam.org/doi/pdf/10.1137/1025002),
$$
F^{\rm HLL} = \frac{c_{\rm min} F_{\rm R} + c_{\rm max} F_{\rm L} - c_{\rm min} c_{\rm max} (U_{\rm R}-U_{\rm L})}{c_{\rm min} + c_{\rm max}}
$$

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

REAL HLLE_solve(REAL F0B1_r, REAL F0B1_l, REAL U_r, REAL U_l, REAL cmin, REAL cmax) {
  // Eq. 3.15 of https://epubs.siam.org/doi/abs/10.1137/1025002?journalCode=siread
  // F_HLLE = (c_min F_R + c_max F_L - c_min c_max (U_R-U_L)) / (c_min + c_max)
  return (cmin*F0B1_r + cmax*F0B1_l - cmin*cmax*(U_r-U_l)) / (cmin+cmax);
}

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

Here, we will write declare the function that will compute the fluxes and pass them to the HLLE solver, then add that result to the $A_i$ right-hand side. We pass the inputs necessary for to compute $E_z$. This includes the standard NRPy+ `params`, as well as pointers to the needed components of the Valencia three-velocity and magnetic field; note that `Br1=Brflux_dirn` and `Bl1=Blflux_dirn` must be set that way to get correct results. We also pass the output `A2_rhs` (again, this will be changed to reflect the actual component we want to calculate) and parameters `SIGN` ($\pm 1$) and flux_dirn (`0`, `1`, or `2`, needed to compute the offsets for memory access).

With the `#include`, we set our parameters as usual; if the declaration for a variable cannot be found elsewhere in the code, it was defined here.

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

/*
Calculate the electric flux on both faces in the input direction.
The input count is an integer that is either 0 or 1. If it is 0, this implies
that the components are input in order of a backwards permutation  and the final
results will need to be multiplied by -1.0. If it is 1, then the permutation is forwards.
 */
void calculate_E_field_flat_all_in_one(const paramstruct *params,
                                       const REAL *Vr0,const REAL *Vr1,
                                       const REAL *Vl0,const REAL *Vl1,
                                       const REAL *Br0,const REAL *Br1,
                                       const REAL *Bl0,const REAL *Bl1,
                                       const REAL *Brflux_dirn,
                                       const REAL *Blflux_dirn,
                                       const REAL *gamma_faceDD00, const REAL *gamma_faceDD01, const REAL *gamma_faceDD02,
                                       const REAL *gamma_faceDD11, const REAL *gamma_faceDD12, const REAL *gamma_faceDD22,
                                       const REAL *beta_faceU0, const REAL *beta_faceU1, const REAL *alpha_face,
                                       REAL *A2_rhs,const REAL SIGN,const int flux_dirn) {
    // This function is written to be generic and compute the contribution for all three AD RHSs.
    // However, for convenience, the notation used in the function itself is for the contribution
    // to AD2, specifically the [F_HLL^x(B^y)]_z term, with reconstructions in the x direction. This
    // corresponds to flux_dirn=0 and count=1 (which corresponds to SIGN=+1.0).
    // Thus, Az(i,j,k) += 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)) are solved here.
    // The other terms are computed by cyclically permuting the indices when calling this function.
#include "../set_Cparameters.h"

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

Again, we are going to use the notation for the RHS of `A_z` (or `AD2`), specifically the $[F_{\rm HLL}^x(B^y)]_{z(i+1/2,j,k)}+[F_{\rm HLL}^x(B^y)]_{z(i-1/2,j,k)}$ terms, corresponding to `flux_dirn=0` and `count=1` (which itself corresponds to `SIGN=1.0`). Specifically, `flux_dirn=0,1,2` is determined by which of $i,j,k$ is offset by $\pm 1/2$, respectively, and `SIGN` corresponds to the sign in from the $F^i(B^j)$ terms, which is determined by the permutation of $(i,j,k)$ represented by $-E_k = F^i(B^j)$.

We begin a straightforward loop over the grid interior; then, we must read in all necessary input values from memory.
Now, recall that, when reconstructing the $x$-direction, we reconstructed to the $i-1/2$ face, or $(i-1/2,j,k)$ for a memory location corresponding to `i,j,k`. So, since we must read in values corresponding to $(i-1/2,j,k)$ and $(i+1/2,j,k)$, we read from `i,j,k` and `i+1,j,k`, respectively, permuting as necessary dictated by the input value of `flux_dirn`.

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

#pragma omp parallel for
    for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++) {
        for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++) {
            for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++) {
                // First, we set the index from which we will read memory. indexp1 is incremented by
                // one point in the direction of reconstruction. These correspond to the faces at at
                // i-1/2 and i+1/2, respectively.

                // Now, we read in memory. We need the x and y components of velocity and magnetic field on both
                // the left and right sides of the interface at *both* faces.
                // Here, the point (i0,i1,i2) corresponds to the point (i-1/2,j,k)
                const int index           = IDX3S(i0,i1,i2);
                const double alpha        = alpha_face[index];
                const double betaU0       = beta_faceU0[index];
                const double betaU1       = beta_faceU1[index];
                const double v_rU0        = alpha*Vr0[index]-betaU0;
                const double v_rU1        = alpha*Vr1[index]-betaU1;
                const double B_rU0        = Br0[index];
                const double B_rU1        = Br1[index];
                const double B_rflux_dirn = Brflux_dirn[index];
                const double v_lU0        = alpha*Vl0[index]-betaU0;
                const double v_lU1        = alpha*Vl1[index]-betaU1;
                const double B_lU0        = Bl0[index];
                const double B_lU1        = Bl1[index];
                const double B_lflux_dirn = Blflux_dirn[index];
                // We will also need need the square root of the metric determinant here at this point:
                const REAL gxx = gamma_faceDD00[index];
                const REAL gxy = gamma_faceDD01[index];
                const REAL gxz = gamma_faceDD02[index];
                const REAL gyy = gamma_faceDD11[index];
                const REAL gyz = gamma_faceDD12[index];
                const REAL gzz = gamma_faceDD22[index];
                const REAL sqrtgammaDET = sqrt( gxx*gyy*gzz
                                             -  gxx*gyz*gyz
                                             +2*gxy*gxz*gyz
                                             -  gyy*gxz*gxz
                                             -  gzz*gxy*gxy );

                // *******************************
                // REPEAT ABOVE, but at i+1, which corresponds to point (i+1/2,j,k)
                //     Recall that the documentation here assumes flux_dirn==0, but the
                //     algorithm is generalized so that any flux_dirn or velocity/magnetic
                //     field component can be computed via permuting the inputs into this
                //     function.
                const int indexp1            = IDX3S(i0+(flux_dirn==0),i1+(flux_dirn==1),i2+(flux_dirn==2));
                const double alpha_p1        = alpha_face[indexp1];
                const double betaU0_p1       = beta_faceU0[indexp1];
                const double betaU1_p1       = beta_faceU1[indexp1];
                const double v_rU0_p1        = alpha_p1*Vr0[indexp1]-betaU0_p1;
                const double v_rU1_p1        = alpha_p1*Vr1[indexp1]-betaU1_p1;
                const double B_rU0_p1        = Br0[indexp1];
                const double B_rU1_p1        = Br1[indexp1];
                const double B_rflux_dirn_p1 = Brflux_dirn[indexp1];
                const double v_lU0_p1        = alpha_p1*Vl0[indexp1]-betaU0_p1;
                const double v_lU1_p1        = alpha_p1*Vl1[indexp1]-betaU1_p1;
                const double B_lU0_p1        = Bl0[indexp1];
                const double B_lU1_p1        = Bl1[indexp1];
                const double B_lflux_dirn_p1 = Blflux_dirn[indexp1];
                // We will also need need the square root of the metric determinant here at this point:
                const REAL gxx_p1 = gamma_faceDD00[indexp1];
                const REAL gxy_p1 = gamma_faceDD01[indexp1];
                const REAL gxz_p1 = gamma_faceDD02[indexp1];
                const REAL gyy_p1 = gamma_faceDD11[indexp1];
                const REAL gyz_p1 = gamma_faceDD12[indexp1];
                const REAL gzz_p1 = gamma_faceDD22[indexp1];
                const REAL sqrtgammaDET_p1 = sqrt( gxx_p1*gyy_p1*gzz_p1
                                                -  gxx_p1*gyz_p1*gyz_p1
                                                +2*gxy_p1*gxz_p1*gyz_p1
                                                -  gyy_p1*gxz_p1*gxz_p1
                                                -  gzz_p1*gxy_p1*gxy_p1 );

                // *******************************

                // DEBUGGING:
//                 if(flux_dirn==0 && SIGN>0 && i1==Nxx_plus_2NGHOSTS1/2 && i2==Nxx_plus_2NGHOSTS2/2) {
//                     printf("index=%d & indexp1=%d\n",index,indexp1);
//                 }

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

Here, we we calculate the flux and state vectors for the electric field. The flux vector is here given as
$$
[F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i),
$$
or
$$
[F^x(B^y)]_z = \sqrt{\gamma} (v^x B^y - v^y B^x)
$$
in our specific case.
Here, $v^i$ is the drift velocity and $B^i$ is the magnetic field.

The state vector is simply the magnetic field component specified by the notation $[F^i(B^j)]_k$ (that is, $j$). Here, then, the notation $[F^x(B^y)]_z$ dictates the use of $B^y$. The way we shuffle inputs to produce the other terms we will need conveniently guarantees that by using the $B^y$ component here, we always choose the correct component in other cases.

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

                // Since we are computing A_z, the relevant equation here is:
                // -E_z(x_i,y_j,z_k) = 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)
                //                           -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) )
                // We will construct the above sum one half at a time, first with SIGN=+1, which
                // corresponds to flux_dirn = 0, count=1, and
                //  takes care of the terms:
                //  [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)

                // ( Note that we will repeat the above with flux_dirn = 1, count = 0, with SIGN=-1
                //   AND with the input components switched (x->y,y->x) so that we get the term
                // -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k)
                // thus completing the above sum. )

                // Here, [F_HLL^i(B^j)]_k = (v^i B^j - v^j B^i) in general.

                // Calculate the flux vector on each face for each component of the E-field:
                // The F(B) terms are as Eq. 6 in Giacomazzo: https://arxiv.org/pdf/1009.2468.pdf
                // [F^i(B^j)]_k = \sqrt{\gamma} (v^i B^j - v^j B^i)
                // Therefore since we want [F_HLL^x(B^y)]_z,
                // we will code     (v^x           B^y   - v^y           B^x) on both left and right faces.
                const REAL F0B1_r = sqrtgammaDET*(v_rU0*B_rU1 - v_rU1*B_rU0);
                const REAL F0B1_l = sqrtgammaDET*(v_lU0*B_lU1 - v_lU1*B_lU0);

                // Compute the state vector for these terms:
                const REAL U_r = B_rflux_dirn;
                const REAL U_l = B_lflux_dirn;

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

Finally, we call the HLLE solver we wrote earlier.

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

                REAL cmin,cmax;
                // Basic HLLE solver:
                find_cmax_cmin(gxx,gxy,gxz,
                               gyy,gyz,gzz,
                               betaU0,alpha,flux_dirn,
                               &cmax, &cmin);
                const REAL FHLL_0B1 = HLLE_solve(F0B1_r, F0B1_l, U_r, U_l, cmin, cmax);

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

Then, we set the flux and state vectors as before and pass them to the HLLE solver, but at the point `i+1`.

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

                // ************************************
                // ************************************
                // REPEAT ABOVE, but at point i+1
                // Calculate the flux vector on each face for each component of the E-field:
                const REAL F0B1_r_p1 = sqrtgammaDET_p1*(v_rU0_p1*B_rU1_p1 - v_rU1_p1*B_rU0_p1);
                const REAL F0B1_l_p1 = sqrtgammaDET_p1*(v_lU0_p1*B_lU1_p1 - v_lU1_p1*B_lU0_p1);

                // Compute the state vector for this flux direction
                const REAL U_r_p1 = B_rflux_dirn_p1;
                const REAL U_l_p1 = B_lflux_dirn_p1;
                //const REAL U_r_p1 = B_rU1_p1;
                //const REAL U_l_p1 = B_lU1_p1;
                // Basic HLLE solver, but at the next point:
                find_cmax_cmin(gxx_p1,gxy_p1,gxz_p1,
                               gyy_p1,gyz_p1,gzz_p1,
                               betaU0_p1,alpha_p1,flux_dirn,
                               &cmax, &cmin);
                const REAL FHLL_0B1p1 = HLLE_solve(F0B1_r_p1, F0B1_l_p1, U_r_p1, U_l_p1, cmin, cmax);
                // ************************************
                // ************************************

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

With the Riemann problem solved, we add the contributions to the RHSs. Here is where we multiply by the input `SIGN`; note that we also multiply by $1/4$, because the algorithm calls for an average of the four terms. That is, our final value for $-E_z$ will be the average of that calculated at the $\pm x$ and $\pm y$ faces of the cell.

Finally, we close out our `for` loops.

```python
%%writefile -a $Ccodesdir/calculate_E_field_flat_all_in_one.h

                // With the Riemann problem solved, we add the contributions to the RHSs:
                // -E_z(x_i,y_j,z_k) &= 0.25 ( [F_HLL^x(B^y)]_z(i+1/2,j,k)+[F_HLL^x(B^y)]_z(i-1/2,j,k)
                //                            -[F_HLL^y(B^x)]_z(i,j+1/2,k)-[F_HLL^y(B^x)]_z(i,j-1/2,k) )
                // (Eq. 11 in https://arxiv.org/pdf/1009.2468.pdf)
                // This code, as written, solves the first two terms for flux_dirn=0. Calling this function for count=0
                // and flux_dirn=1 flips x for y to solve the latter two, switching to SIGN=-1 as well.

                // Here, we finally add together the output of the HLLE solver at i-1/2 and i+1/2
                // We also multiply by the SIGN dictated by the order of the input vectors and divide by 4.
                A2_rhs[index] += SIGN*0.25*(FHLL_0B1 + FHLL_0B1p1);
                // flux dirn = 0 ===================>   i-1/2       i+1/2
                //               Eq 11 in Giacomazzo:
                //               -FxBy(avg over i-1/2 and i+1/2) + FyBx(avg over j-1/2 and j+1/2)
                //               Eq 6 in Giacomazzo:
                //               FxBy = vxBy - vyBx
                //             ->
                //               FHLL_0B1 = vyBx - vxBy

            } // END LOOP: for(int i0=NGHOSTS; i0<NGHOSTS+Nxx0; i0++)
        } // END LOOP: for(int i1=NGHOSTS; i1<NGHOSTS+Nxx1; i1++)
    } // END LOOP: for(int i2=NGHOSTS; i2<NGHOSTS+Nxx2; i2++)
}

```

    Appending to GiRaFFE_standalone_Ccodes/RHSs/calculate_E_field_flat_all_in_one.h

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

To validate the code in this tutorial we check for agreement between the files

1. that were written in this tutorial and
1. those that are generated by the python module

```python
# Define the directory that we wish to validate against:
valdir = "GiRaFFE_NRPy/GiRaFFE_Ccode_library/RHSs/"

import GiRaFFE_NRPy.GiRaFFE_NRPy_Afield_flux_handwritten as Af
Af.GiRaFFE_NRPy_Afield_flux(valdir)

import difflib
import sys

print("Printing difference between original C code and this code...")
# Open the files to compare
files = ["calculate_E_field_flat_all_in_one.h"]

for file in files:
    print("Checking file " + file)
    with open(os.path.join(valdir,file)) as file1, open(os.path.join(Ccodesdir,file)) as file2:
        # Read the lines of each file
        file1_lines = file1.readlines()
        file2_lines = file2.readlines()
        num_diffs = 0
        for line in difflib.unified_diff(file1_lines, file2_lines, fromfile=os.path.join(valdir+file), tofile=os.path.join(Ccodesdir+file)):
            sys.stdout.writelines(line)
            num_diffs = num_diffs + 1
        if num_diffs == 0:
            print("No difference. TEST PASSED!")
        else:
            print("ERROR: Disagreement found with .py file. See differences above.")
            sys.exit(1)
```

    Printing difference between original C code and this code...
    Checking file calculate_E_field_flat_all_in_one.h
    No difference. TEST PASSED!

# Step 4: 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-GiRaFFE_NRPy-Afield_flux.pdf](Tutorial-GiRaFFE_NRPy-Afield_flux.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)

```python
import cmdline_helper as cmd    # NRPy+: Multi-platform Python command-line interface
cmd.output_Jupyter_notebook_to_LaTeXed_PDF("Tutorial-GiRaFFE_NRPy-Afield_flux",location_of_template_file=os.path.join(".."))
```

    pdflatex: security risk: running with elevated privileges
    pdflatex: security risk: running with elevated privileges
    pdflatex: security risk: running with elevated privileges
    Created Tutorial-GiRaFFE_NRPy-Afield_flux.tex, and compiled LaTeX file to
        PDF file Tutorial-GiRaFFE_NRPy-Afield_flux.pdf