Markdown and $\LaTeX$
=====================

Markdown
========

## Jupyter Notebooks use the [Markdown](https://en.wikipedia.org/wiki/Markdown) language to render text in cells
Paragraphs are separated
by a blank line.

Two spaces at the end of a line
produces a line break.

> Markdown uses email-style
> characters for blockquoting.
Paragraphs are separated
by a blank line.

Two spaces at the end of a line
produces a line break.

> Markdown uses email-style
> characters for blockquoting.
# Main Heading
# Main Heading
## Sub-heading
### Sub-sub-heading
#### Sub-sub-sub-heading
##### Sub-sub-sub-sub-heading
## Sub-heading
### Sub-sub-heading
#### Sub-sub-sub-heading
##### Sub-sub-sub-sub-heading
Horizontal rule:

---
---
Text attributes: _italic_ , **bold**, `monospace`, and ~~strikethrough~~.
Text attributes: _italic_ , **bold**, `monospace`, and ~~strikethrough~~.
Bullet list:

  * john
  * paul
  * george
    * ringo
* john
* paul
* george
  * ringo
Numbered list (you need the "." after the number):

  1. john
  2. paul
  3. george
    1. ringo
Numbered list (you need the "." after the number):

  1. john
  2. paul
  3. george
    1. ringo
A web link:

[Words](url)

This is pretty much just taken from the [Wiki entry for Markdown](https://en.wikipedia.org/wiki/Markdown)
### This is all pretty much just taken from the [Wiki entry for Markdown](https://en.wikipedia.org/wiki/Markdown)

---

# ♡ $\LaTeX$ ♡

* $\LaTeX$ is based on the $\TeX$ typesetting language. The [$\TeX$](https://en.wikipedia.org/wiki/TeX) language has a long and deep history (1978).
* $\LaTeX$ is widely used in academia, especially in Astronomy.

## Jupyter Notebooks use the $\LaTeX$ language to typeset equations.

* Actually, they use a subset of $\LaTeX$ called [MathJax](https://en.wikipedia.org/wiki/MathJax).
Most LaTeX commands are prefixed with a "\". For example \pi is the
command to produce the lower case Greek letter pi.

The characters  # $ % & ~ _ ^ \ { } are special characters in LaTeX. If
you want to typeset them you need to put a \ in front of them. For
example \$ will typeset the symbol $

Special case: if you want the "\" character use \backslash
----

#  $\$$ $\rightarrow$ $\LaTeX$ equation mode
$ - in-line math mode

The circumfrence of a circle is $2 \pi r$
The circumfrence of a circle is $2 \pi r$
$$ - display mode

$$
2 \pi r
$$
$$
2 \pi r
$$
Do not put any bank lines around the equation. This will not work:

$$

2 \pi r
$$
$$

2 \pi r
$$
% - LaTeX comment character

$$
% - nothing here
2 \pi r
% - and nothing here
$$
$$
% - nothing here
2 \pi r
% - and nothing here
$$
Superscript: ^

$$
\pi r^{2}
$$
$$
\pi r^{2}
$$
Subscript: _

$$
x_{2}
$$
$$
x_{2}
$$
Fractions: \frac{}{}

$$
\frac{2}{\pi}
$$
$$
\frac{2}{\pi}
$$
Nice looking delimiters

Do not use () or []

$$
( \frac{2}{\pi} )
$$
$$
( \frac{2}{\pi} )
$$
Use '\left(' and '\right)' or '\left[' '\right]'

$$
\left( \frac{2}{\pi} \right)
$$
$$
\left( \frac{2}{\pi} \right)
$$
Font size (from small to big): \tiny \Tiny \small \normalsize \large \Large \LARGE \huge \Huge$$
\huge
\left( \frac{2}{\pi} \right)
$$
$$
\huge
\left( \frac{2}{\pi} \right)
$$
$$
\large
x_{2}
$$
$$
\large
x_{2}
$$
$$
\large
x_{\small 2}
$$
$$
\large
x_{\small 2}
$$
Spacing can make equations look nicer

\, - really, really small space
\: - really small space
\; - small space
\  - text space (space after backslash!) - depends on fontsize
\quad - big space
\qquad - really big space
\! - (negative) really, really, small space$$
f(x)\! =\! x^{2}\! +\! 2 \\
f(x) = x^{2}+2 \\
f(x)\, =\, x^{2}\,+\,2 \\
f(x)\: =\: x^{2}\:+\:2 \\
f(x)\; =\; x^{2}\;+\;2 \\
f(x)\ =\ x^{2}\ +\ 2 \\
f(x)\quad =\quad x^{2}\quad +\quad 2 \\
f(x)\qquad =\qquad x^{2}\qquad +\qquad 2 \\
$$
$$
f(x)\! =\! x^{2}\! +\! 2 \\
f(x) = x^{2}+2 \\
f(x)\, =\, x^{2}\,+\,2 \\
f(x)\: =\: x^{2}\:+\:2 \\
f(x)\; =\; x^{2}\;+\;2 \\
f(x)\ =\ x^{2}\ +\ 2 \\
f(x)\quad =\quad x^{2}\quad +\quad 2 \\
f(x)\qquad =\qquad x^{2}\qquad +\qquad 2 \\
$$
$$
\large
\sqrt{2x}
$$
$$
\large
f(x)=\sqrt{2x}
$$
$$
\large
f(x) = \sqrt{\,2x\,}
$$
$$
\large
f(x) = \sqrt{\,2x\,}
$$

# $\LaTeX$ environments

### Alignment, Matrices and Brackets

* The ampersand `&` is used to separate/align columns
* Use `\\` to end a row (except for the last row)
$$
\begin{align}
a_1 & = b_1 + c_1 \\
a_2 & = b_2 + c_2 + d_2
\end{align}
$$
$$
\begin{align}
a_1 & = b_1 + c_1 \\
a_2 & = b_2 + c_2 + d_2
\end{align}
$$
$$
\begin{matrix}
a & b \\
c & d
\end{matrix}
$$
$$
\begin{matrix}
a & b \\
c & d
\end{matrix}
$$
$$
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
$$
$$
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
$$
$$
\begin{bmatrix}
a & b \\
c & d
\end{bmatrix}
$$
$$
\begin{bmatrix}
a & b \\
c & d
\end{bmatrix}
$$

---
### These few commands, plus a list of [Latex Symbols](http://oeis.org/wiki/List_of_LaTeX_mathematical_symbols), are all you need for most $\LaTeX$ equations

### If you cannot find a symbol, you can try the [Detexify Latex](http://detexify.kirelabs.org/classify.html) drawing tool

### Note: Jupyter notebooks only support a [small subset of the full $\LaTeX$ language](https://docs.mathjax.org/en/latest/input/tex/differences.html).

* Mostly just the Math stuff (`mathmode`)
* Only symbols that **do not** require external packages `\usepackage{}`
* Here is the [Full list of supported commands](http://www.onemathematicalcat.org/MathJaxDocumentation/TeXSyntax.htm)
* You will definitely find some symbols that will not render in a notebook!

----

## Writing $\LaTeX$ equations from scratch can be a pain. There are some ways to make it easier:

### Use can use `SymPy` to make $\LaTeX$ equations for you!

```python
import sympy as sp
```

```python
z = sp.symbols('z')
```

```python
my_equation =  1/( ( z + 2 ) * ( z + 1 ) )

print(sp.latex(my_equation))
```
$$
\frac{1}{\left(z + 1\right) \left(z + 2\right)}
$$
$$
\frac{1}{\left(z + 1\right) \left(z + 2\right)}
$$

----
## My favorite trick is the Wikipedia $\LaTeX$ equation cheat:

* #### Navigate over to: [Plank's Law](https://en.wikipedia.org/wiki/Planck%27s_law)

### Double-click on this cell

$$ \Large

$$

# Magic!

----

### There are also a bunch of on-line $\LaTeX$ equation editors:

* https://www.tutorialspoint.com/latex_equation_editor.htm
* https://www.latex4technics.com/
* https://www.codecogs.com/latex/eqneditor.php
* http://atomurl.net/math/
* http://www.sciweavers.org/free-online-latex-equation-editor

----

## Use can use $\LaTeX$ in plots:

* You need to use the `raw` strings: (`r`)
* For example: `r"$e^{-\sqrt{x}}$"`

```python
import numpy as np
import matplotlib.pyplot as plt
```

```python
plt.style.use('ggplot')

my_x = np.linspace(0,2*np.pi,100)
my_y = np.sin(5*my_x) * np.exp(-np.sqrt(my_x))
```

```python
fig,ax = plt.subplots(1,1)
fig.set_size_inches(10,4)

fig.tight_layout()

ax.plot(my_x, my_y, color='r', marker='None', linestyle='-');

ax.set_xlabel(r"This is in units of 2$\pi$", fontsize=16)
ax.set_ylabel(r"This is Y", fontsize=16)

my_text = r"$y\ =\, \sin(5x)\, e^{-\sqrt{x}}$"

ax.text(2.0, 0.4, my_text, color='green', fontsize=36);
```

---

## Learn to tailor your google searches for $\LaTeX$

---

# This is just the beginning!

----

Your assignment
===============

# Part I

* ### Go to the [Overleaf](https://www.overleaf.com?r=f33161ec&rm=d&rs=b) site and create an account using your `uw.edu` address.
* ### It is important that you use your `uw.edu` address!

# Part II

  * ### Create a butterfly plot: http://en.wikipedia.org/wiki/Butterfly_curve_(transcendental)
  * ### Plot X vs. Y
  * ### Feel free to change the parameters to get a different butterfly
  * ### If you don't like the butterfly, you can do a fancier [parametic equation](https://en.wikipedia.org/wiki/Parametric_equation)
  * ### Make gratuitous use of $\LaTeX$ labels
   * ### Note: the `\over` command is not supported by Jupyter notebooks!
  * ### Save the plot to a png file
  * ### Upload the PNG file to Canvas (Assignment: CoolPlot)
  * ### I have started it for you below

  ----

```python
t = np.linspace(0, 12*np.pi, 3000)
```

```python

```

```python
fig,ax = plt.subplots(1,1)                    # One window
fig.set_size_inches(11,8.5)                   # (width,height) - letter paper landscape

fig.tight_layout()                            # Make better use of space on plot

```

```python
fig.savefig('MyCoolPlot.png', bbox_inches='tight')
```

## Due Mon Mar 8 - 1 pm

* upload `png` files to `CoolPlot` assignment

```python

```


```python
from __future__ import absolute_import, division, print_function
from collections import defaultdict
import numpy as np
import sympy
import matplotlib.pyplot as plt
import chempy
from chempy import Reaction, Substance, ReactionSystem
from chempy.kinetics.ode import get_odesys
from chempy.kinetics.analysis import plot_reaction_contributions
import pyodesys
from pyodesys.symbolic import ScaledSys
from pyneqsys.plotting import mpl_outside_legend
sympy.init_printing()
%matplotlib inline
{k: globals()[k].__version__ for k in 'sympy pyodesys chempy'.split()}
```

```python
# Never mind the next row, it contains all the reaction data of aqueous radiolysis at room temperature
_species, _reactions = ['H', 'H+', 'H2', 'e-(aq)', 'HO2-', 'HO2', 'H2O2', 'HO3', 'O-', 'O2', 'O2-', 'O3', 'O3-', 'OH', 'OH-', 'H2O'], [({1: 1, 14: 1}, {15: 1}, 140000000000.0, {}), ({15: 1}, {1: 1, 14: 1}, 2.532901323662559e-05, {}), ({6: 1}, {1: 1, 4: 1}, 0.11193605692841689, {}), ({1: 1, 4: 1}, {6: 1}, 50000000000.0, {}), ({6: 1, 14: 1}, {4: 1, 15: 1}, 13000000000.0, {}), ({4: 1, 15: 0}, {6: 1, 14: 1}, 58202729.542927094, {15: 1}), ({3: 1, 15: 0}, {0: 1, 14: 1}, 19.0, {15: 1}), ({0: 1, 14: 1}, {3: 1, 15: 1}, 18000000.0, {}), ({0: 1}, {1: 1, 3: 1}, 3.905960400662016, {}), ({1: 1, 3: 1}, {0: 1}, 23000000000.0, {}), ({13: 1, 14: 1}, {8: 1, 15: 1}, 13000000000.0, {}), ({8: 1, 15: 0}, {13: 1, 14: 1}, 103500715.55425139, {15: 1}), ({13: 1}, {8: 1, 1: 1}, 0.12589254117941662, {}), ({8: 1, 1: 1}, {13: 1}, 100000000000.0, {}), ({5: 1}, {1: 1, 10: 1}, 1345767.401963457, {}), ({1: 1, 10: 1}, {5: 1}, 50000000000.0, {}), ({5: 1, 14: 1}, {10: 1, 15: 1}, 50000000000.0, {}), ({10: 1, 15: 0}, {5: 1, 14: 1}, 18.619585312728415, {15: 1}), ({3: 1, 13: 1}, {14: 1}, 30000000000.0, {}), ({3: 1, 6: 1}, {13: 1, 14: 1}, 14000000000.0, {}), ({10: 1, 3: 1, 15: 0}, {4: 1, 14: 1}, 13000000000.0, {15: 1}), ({3: 1, 5: 1}, {4: 1}, 20000000000.0, {}), ({9: 1, 3: 1}, {10: 1}, 22200000000.0, {}), ({3: 2, 15: 0}, {2: 1, 14: 2}, 5000000000.0, {15: 2}), ({0: 1, 3: 1, 15: 0}, {2: 1, 14: 1}, 25000000000.0, {15: 1}), ({3: 1, 4: 1}, {8: 1, 14: 1}, 3500000000.0, {}), ({8: 1, 3: 1, 15: 0}, {14: 2}, 22000000000.0, {15: 1}), ({3: 1, 12: 1, 15: 0}, {9: 1, 14: 2}, 16000000000.0, {15: 1}), ({3: 1, 11: 1}, {12: 1}, 36000000000.0, {}), ({0: 1, 15: 0}, {2: 1, 13: 1}, 11.0, {15: 1}), ({0: 1, 8: 1}, {14: 1}, 10000000000.0, {}), ({0: 1, 4: 1}, {13: 1, 14: 1}, 90000000.0, {}), ({0: 1, 12: 1}, {9: 1, 14: 1}, 10000000000.0, {}), ({0: 2}, {2: 1}, 7750000000.0, {}), ({0: 1, 13: 1}, {15: 1}, 7000000000.0, {}), ({0: 1, 6: 1}, {13: 1, 15: 1}, 90000000.0, {}), ({0: 1, 9: 1}, {5: 1}, 21000000000.0, {}), ({0: 1, 5: 1}, {6: 1}, 10000000000.0, {}), ({0: 1, 10: 1}, {4: 1}, 20000000000.0, {}), ({0: 1, 11: 1}, {7: 1}, 38000000000.0, {}), ({13: 2}, {6: 1}, 3600000000.0, {}), ({5: 1, 13: 1}, {9: 1, 15: 1}, 6000000000.0, {}), ({10: 1, 13: 1}, {9: 1, 14: 1}, 8200000000.0, {}), ({2: 1, 13: 1}, {0: 1, 15: 1}, 40000000.0, {}), ({13: 1, 6: 1}, {5: 1, 15: 1}, 30000000.0, {}), ({8: 1, 13: 1}, {4: 1}, 20000000000.0, {}), ({4: 1, 13: 1}, {5: 1, 14: 1}, 7500000000.0, {}), ({12: 1, 13: 1}, {11: 1, 14: 1}, 2550000000.0, {}), ({12: 1, 13: 1}, {1: 1, 10: 2}, 5950000000.0, {}), ({11: 1, 13: 1}, {9: 1, 5: 1}, 110000000.0, {}), ({10: 1, 5: 1}, {9: 1, 4: 1}, 80000000.0, {}), ({5: 2}, {9: 1, 6: 1}, 800000.0, {}), ({8: 1, 5: 1}, {9: 1, 14: 1}, 6000000000.0, {}), ({5: 1, 6: 1}, {9: 1, 13: 1, 15: 1}, 0.5, {}), ({4: 1, 5: 1}, {9: 1, 13: 1, 14: 1}, 0.5, {}), ({12: 1, 5: 1}, {9: 2, 14: 1}, 6000000000.0, {}), ({11: 1, 5: 1}, {9: 1, 7: 1}, 500000000.0, {}), ({10: 2, 15: 0}, {9: 1, 6: 1, 14: 2}, 100.0, {15: 2}), ({8: 1, 10: 1, 15: 0}, {9: 1, 14: 2}, 600000000.0, {15: 1}), ({10: 1, 6: 1}, {9: 1, 13: 1, 14: 1}, 0.13, {}), ({10: 1, 4: 1}, {8: 1, 9: 1, 14: 1}, 0.13, {}), ({10: 1, 12: 1, 15: 0}, {9: 2, 14: 2}, 10000.0, {15: 1}), ({10: 1, 11: 1}, {9: 1, 12: 1}, 1500000000.0, {}), ({8: 2, 15: 0}, {4: 1, 14: 1}, 1000000000.0, {15: 1}), ({8: 1, 9: 1}, {12: 1}, 3600000000.0, {}), ({8: 1, 2: 1}, {0: 1, 14: 1}, 80000000.0, {}), ({8: 1, 6: 1}, {10: 1, 15: 1}, 500000000.0, {}), ({8: 1, 4: 1}, {10: 1, 14: 1}, 400000000.0, {}), ({8: 1, 12: 1}, {10: 2}, 700000000.0, {}), ({8: 1, 11: 1}, {9: 1, 10: 1}, 5000000000.0, {}), ({12: 1}, {8: 1, 9: 1}, 300.0, {}), ({1: 1, 12: 1}, {9: 1, 13: 1}, 90000000000.0, {}), ({12: 1, 6: 1}, {9: 1, 10: 1, 15: 1}, 1600000.0, {}), ({12: 1, 4: 1}, {9: 1, 10: 1, 14: 1}, 890000.0, {}), ({2: 1, 12: 1}, {0: 1, 9: 1, 14: 1}, 250000.0, {}), ({7: 1}, {9: 1, 13: 1}, 110000.0, {}), ({13: 1, 7: 1}, {9: 1, 6: 1}, 5000000000.0, {}), ({7: 2}, {9: 2, 6: 1}, 5000000000.0, {}), ({10: 1, 7: 1}, {9: 2, 14: 1}, 10000000000.0, {}), ({7: 1}, {1: 1, 12: 1}, 328.097819129701, {}), ({1: 1, 12: 1}, {7: 1}, 52000000000.0, {}), ({11: 1, 14: 1}, {10: 1, 5: 1}, 70.0, {}), ({11: 1, 4: 1}, {9: 1, 10: 1, 13: 1}, 2800000.0, {})]
```

```python
species = [Substance.from_formula(s) for s in _species]
reactions = [
    Reaction({_species[k]: v for k, v in reac.items()},
             {_species[k]: v for k, v in prod.items()}, param,
             {_species[k]: v for k, v in inact_reac.items()})
    for reac, prod, param, inact_reac in _reactions
]
```

```python
# radiolytic yields for gamma radiolysis of neat water
C_H2O = 55.5
YIELD_CONV = 1.0364e-07 # mol * eV / (J * molecules)
prod_rxns = [
    Reaction({'H2O': 1}, {'H+': 1,  'OH-': 1},            0.5  * YIELD_CONV / C_H2O),
    Reaction({'H2O': 1}, {'H+': 1, 'e-(aq)': 1, 'OH': 1}, 2.6  * YIELD_CONV / C_H2O),
    Reaction({'H2O': 1}, {'H':  2, 'H2O2': 1},            0.66 * YIELD_CONV / C_H2O, {'H2O': 1}),
    Reaction({'H2O': 1}, {'H2': 1, 'H2O2': 1},            0.74 * YIELD_CONV / C_H2O, {'H2O': 1}),
    Reaction({'H2O': 1}, {'H2': 1,   'OH': 2},            0.1  * YIELD_CONV / C_H2O, {'H2O': 1}),
    Reaction({'H2O': 1}, {'H2': 3,  'HO2': 2},            0.04 * YIELD_CONV / C_H2O, {'H2O': 3}),
]
```

```python
# The productions reactions have hardcoded rates corresponding to 1 Gy/s
rsys = ReactionSystem(prod_rxns + reactions, species)
rsys
```

```python
odesys, extra = get_odesys(rsys)
odesys.exprs[:3]  # take a look at the first three ODEs in the system
```

```python
j = odesys.get_jac()
j.shape
```

```python
def integrate_and_plot(odesys, c0_dict, integrator, ax=None, zero_conc=0, log10_t0=-12, tout=None,
                       print_info=False, **kwargs):
    if tout is None:
        tout = np.logspace(log10_t0, 6)
    c0 = [c0_dict.get(k, zero_conc)+zero_conc for k in _species]
    result = odesys.integrate(tout, c0, integrator=integrator, **kwargs)
    if ax is None:
        ax = plt.subplot(1, 1, 1)
    result.plot(ax=ax, title_info=2)
    ax.set_xscale('log'); ax.set_yscale('log')
    mpl_outside_legend(ax, prop={'size': 9})
    if print_info:
        print({k: v for k, v in result.info.items() if not k.startswith('internal')})
    return result
```

```python
c0_dict = defaultdict(float, {'H+': 1e-7, 'OH-': 1e-7, 'H2O': 55.5})
plt.figure(figsize=(16,6))
integrate_and_plot(odesys, c0_dict, integrator='cvode', first_step=1e-14, atol=1e-10, rtol=1e-10, autorestart=5)
plt.legend()
_ = plt.ylim([1e-16, 60])
```

```python
def integrate_and_plot_scaled(rsys, dep_scaling, *args, **kwargs):
    integrate_and_plot(get_odesys(
            rsys, SymbolicSys=ScaledSys, dep_scaling=dep_scaling)[0], *args, **kwargs)
```

```python
fig, axes = plt.subplots(2, 2, figsize=(14, 14))
solvers = 'cvode', 'odeint', 'gsl', 'scipy'
for idx, ax in enumerate(np.ravel(axes)):
    #if idx == 1:
    #    continue
    kw = {'method': 'vode'} if solvers[idx] == 'scipy' else {}
    integrate_and_plot_scaled(rsys, 1e3, c0_dict, solvers[idx], ax=ax, atol=1e-6, rtol=1e-6,
                              nsteps=4000, first_step=1e-10*extra['max_euler_step_cb'](0, c0_dict), **kw)
    ax.set_title(solvers[idx])
```

```python
integrate_and_plot(odesys, c0_dict, 'cvode', first_step=1e-12)
```

```python
integrate_and_plot(odesys, c0_dict, 'scipy')
```

```python
from pyodesys.symbolic import symmetricsys
logexp = (sympy.log, sympy.exp)
LogLogSys = symmetricsys(logexp, logexp, exprs_process_cb=lambda exprs: [
        sympy.powsimp(expr.expand(), force=True) for expr in exprs])
loglogsys, _ = get_odesys(rsys, SymbolicSys=LogLogSys)
```

```python
loglogsys.exprs[:3]
```

```python
integrate_and_plot(loglogsys, c0_dict, 'gsl', zero_conc=1e-26, first_step=1e-3, log10_t0=-13)
```

```python
ssys, _ = get_odesys(rsys, SymbolicSys=ScaledSys, dep_scaling=1e16)
```

```python
from chempy.kinetics._native import get_native
nsys = get_native(rsys, ssys, 'cvode')
```

```python
integrate_and_plot(nsys, c0_dict, 'cvode', nsteps=96000,
                   error_outside_bounds=True, get_dx_max_factor=-1.0, autorestart=2, atol=1e-9, rtol=1e-10)
```

```python
integrate_and_plot(nsys, c0_dict, 'cvode', nsteps=96000, tout=(1e-16, 1e6),
                   error_outside_bounds=True, get_dx_max_factor=-1.0, autorestart=2, atol=1e-9, rtol=1e-10,
                   print_info=True, first_step=1e-16)
```

```python
print(''.join(open(next(filter(lambda s: s.endswith('.cpp'), nsys._native._written_files))).readlines()[:42]))
```

```python
init_conc_H2O2 = c0_dict.copy()
init_conc_H2O2['H2O2'] = 0.01
odesys2 = odesys.__class__.from_other(odesys, lower_bounds=0)
res = integrate_and_plot(odesys2, init_conc_H2O2, integrator='cvode',
                         first_step=1e-16, atol=1e-12, rtol=1e-13, autorestart=5)
```

```python
%load_ext autoreload
%autoreload 2

```

```python
from chempy.kinetics.analysis import plot_reaction_contributions
```

```python
sks = ['H2O2', 'OH', 'HO2']
fig, axes = plt.subplots(1, len(sks), figsize=(16, 10))
selection = res.xout > 1e0
plot_reaction_contributions(res.xout[selection], res.yout[selection, :], extra['rate_exprs_cb'],
                            rsys, substance_keys=sks, axes=axes, combine_equilibria=True, total=True)
plt.tight_layout()
```

```python

```

```python

```


# How

## How to include code in markdown

To include code in markdown use three ` marks followed by the name of the
language:

`````{tip}
````
```<language>

<code>
```
````
`````

For example:

````
```python
import sympy as sym

x = sym.Symbol("x")
```
````

Would render as:

```python
import sympy as sym

x = sym.Symbol("x")
```

It is also possible to include code in markdown using an indented block:

For example:

```md
Here is some code:

    import sympy as sym

    x = sym.Symbol("x")
```

Would render as:

---

Here is some code:

    import sympy as sym

    x = sym.Symbol("x")

---

```{attention}
Using an indented block does not allow you to specify the language and can lead
to mistake when combining with other nested statement.
```

## How to write a tutorial

A tutorial should include step by step instructions with expected behaviours.
This should not focus on any deeper explanation.

An analogy of this is teaching a young toddler to build a toy train
track. They do not need to know the physics related to how the train will go
through the track. They need only to see how to lay the track pieces.

## How to write a how to guide

A how to guide should provide a quick and to the point description of how to
solve a specific problem.

An analogy of this would be a recipe. The recipe will not necessarily explain
how to chop an onion and/or why we are chopping an onion it will tell you
to chop an onion as a step of cooking a particular meal.

## How to write an explanation section

The explanation section should provide a deeper understanding of the concepts
under the code.

An analogy of this again related to a recipe would be a book on the chemistry of
taste and why a chopped onion adds a specific type of flavour to a meal.

## How to write a reference section

The reference section should provide an overview of the specific tools, commands
and indeed place for background reading as well (although this can also be
referred to in the explanation section).


# Two-Line Element Estimation Using Machine Learning
> A demo for TLE estimation using machine learning.

- toc: true
- badges: true
- comments: true
- categories: [Two-line elements, Mean elements, TLE, Estimation, Machine Learning]

## 1. Introduction

Two-line elements are widely used for space operations to predict the orbit with a moderate accuracy for 2-3 days. Local optimization methods, such as the nonlinear least squares method with differential corrections, can estimate a TLE as long as there exists an initial estimate that provides the desired precision. Global optimization methods to estimate TLEs are computationally intensive, and estimating a large number of them is prohibitive. In this paper, the feasibility of estimating TLEs using machine learning methods is investigated. First, a Monte-Carlo approach to estimate a TLE, when there are no initial estimates that provide the desired precision, is introduced. The proposed Monte-Carlo method is shown to estimate TLEs with root mean square errors below 1 km for space objects with varying area-to-mass ratios and orbital characteristics. Second, gradient boosting decision trees and fully-connected neural networks are trained to map orbital evolution of space objects to the associated TLEs using 8 million publicly available TLEs from the US space catalog. The desired precision in the mapping to estimate a TLE is achieved for one of the three test cases, which is a low area-to-mass ratio space object.

## 2. Background

### 2.1 Two-Line Elements

Two-line element sets (TLEs) and Simplified General Perturbations \#4 (SGP4) are widely used for space operations to predict trajectories of space objects with moderate accuracy (tens of kilometers) for **2-3 days**. SGP4 provides the reference trajectory that is updated by fitting the actual trajectory, and TLEs are the updated parameters that approximate it. SGP4 includes zonal terms up to **J5** of the gravitational potential of the Earth, **neutral atmosphere with exponential decay** and **partially modeled third-body mass interactions**. Bstar ($B^{*}$) is an element of a TLE that determines the effect of air drag on the trajectories of the space objects. Table 1 shows the parameters of a TLE. However, it should be noted that TLE parameters are mean elements that are estimated by fitting the SGP4 trajectory and observed trajectory, and their values can drastically changed compared to the osculating elements at epoch.

Parameters|Equations|Description
------------ | ------------- | -------------
$B^{*}$|$B^{*}$=$\frac{1}{2}\frac{C_{D}A}{m}\rho$| Determines the effect of air drag on the trajectories
$n$|$n$=$\sqrt{\frac{\mu}{a^{3}}}$| Defines the angular frequency of the orbit (shape of the orbit)
$i$|-| Defines the orientation of the orbital plane with respect to the Earth
$\Omega$|-| Defines the orientationof an orbit with respect to the $\hat{z}$ axis, which is parallel to the rotation axis of the Earth
$\omega$|-| Defines the orientation of the orbital plane with respect to the Earth around the axis parallel to the angular momentum vector of the orbit
$e$|-| Defines the shape of the orbit
$m$|-| Defines the location of a space object in its orbit

Table 1 : Parameters of a two-line element set.

### 2.2 Two-Line Element Estimation Methods

There are a few studies that investigate different approaches to estimate TLEs. As stated above, TLEs include the mean states estimated by fitting observations to the dynamics provided by SGP4, and they can only be used with SGP4 [1]. Keplerian orbital elements are used as initial estimates of TLEs by the differential corrections and nonlinear least squares methods [2]. Kalman filter is investigated to estimate TLEs using onboard Global Positioning System (GPS) data [3]. However, the above methods, which search for the local minimum of the objective function, which is the sum of the squares of the position and velocity errors, depend on the availability of a reasonable initial estimate of a TLE. Although methods, such as genetic algorithms [4] and invasive weed optimization [5], that do not require an initial estimate of the TLEs have been investigated, they are reported to be computationally intensive as they search for the global optimum. To summarize, the two main approaches in the literature to estimate a TLE use either computationally expensive global search or local search which depends on having an initial estimate. This represents a significant shortcoming in the current state-of-the-art of orbit determination using TLEs. The present work addresses this shortcoming.

### 2.3 Machine Learning

 This work utilizes machine learning methods, namely the gradient boosting trees and fully-connected neural networks, to approximate the inverse mapping of publicly available SGP4 algorithm [1] for LEO objects by learning to map the orbital evolution to the associated TLE. The capability of approximating such mapping using machine learning will enable to represent time series orbital data in latent space that can be used with orbit propagators. The gradient boosting trees are machine learning models that are suitable for determining non-linear and sharp decision boundaries. The boosting is achieved by adding weak learners, such as decision trees, to determine the complex decision boundaries. The first successful application of such an idea is adaptive boosting (AdaBoost). AdaBoost generates a sequence of classifiers, and each classifier puts more weight on the samples that are not classified accurately in the previous iteration. Such an approach enables each classifier to capture different features in the input data, and more complex decision boundaries can be defined in the end. The boosting idea is further improved by defining the framework as a numerical optimization in the function space. The objective function is minimized by adding weak learners using the steepest-descent method.

 Artificial neural networks are machine learning models that are suitable for determining smooth nonlinear decision boundaries. According to the universal approximation theory [6], any Borel measurable function can be approximated by a feedforward neural network as long as it includes enough hidden units [7]. In addition, a neural network may also approximate any discrete function irrespective of its dimensions given that dimension is finite. Although the universal approximation theory states that any closed and bounded continuous function can be approximated by multilayer perceptrons, the training scheme to approximate that function might fail due to overfitting or failure of the optimization algorithm [7].

## 3. Methods

### 3.1 Monte-Carlo approach

A Monte-Carlo approach that searches for the global minimum of the sum of the squares of the position and velocity errors to estimate a TLE without any initial data is presented. However, the proposed method is computationally intensive. Therefore, this work also investigates the feasibility of using machine learning to predict reasonable initial estimates of TLEs that can be utilized by the proposed Monte-Carlo approach. Figure 1 outlines the proposed method that extends the differential corrections method using the Monte-Carlo technique to estimate a TLE that satisfies a given criterion. The following procedure describes how to obtain such TLEs.

Figure 1:The Monte-Carlo approach to estimate a TLE.

The inner loop leverages the differential corrections to correct the initial parameters to fit the orbit using non-linear least squares method. The differential corrections scheme iteratively improves the sampled osculating TLE to match the position vector at the epoch time. The stopping criterion is selected as $10^{-8}$ and smaller for the magnitude of position error in kilometers. Equation below shows how a Jacobian matrix, which is computed by the finite-difference method, relates the change in the TLE to the change in the position and velocity vectors at the epoch.

\begin{equation}
\label{eq:diffCorrection}
\begin{split}
\delta tle_{epoch} &= \left(\frac{\partial f_{sgp4}}{\partial tle_{epoch}}\right)^{-1} \delta rv_{epoch}\\
&= (A_{t_{0}})^{-1} \delta b_{t_{0}}
\end{split}
\end{equation}

where $tle_{epoch}$ is the TLE parameters at the epoch, $f_{sgp4}$ is SGP4, $rv_{epoch}$ are the propagated position and velocity vectors at the epoch, $A_{t_{0}}$ is the Jacobian, and $\delta b_{t_{0}}$ is the residual at the epoch. Equation below shows the normal equation that relates all of the changes in the position and velocity vectors associated with the observational data available to the change in the TLE at epoch time [2]. In this work, all observational data are assumed to have the same weight, therefore, $W$ is assumed to be the identity matrix. Equation above is used for differential corrections in the inner loop and Equation below is used for differential corrections with nonlinear least squares method in the outer loop (Figure 1).

\begin{equation}
\label{eq:diffCorrectionNonlinear}
\begin{split}
\delta tle_{epoch} &= (A^{T}WA)^{-1}A^{T}Wb\\
&= \left(\sum_{i}^{N} A_{(t_{i})}^{T}A_{(t_{i})}\right)^{-1} \sum_{i}^{N} A_{(t_{i})}^{T}b_{(t_{i})}
\end{split}
\end{equation}

\noindent where $tle_{epoch}$ is the TLE parameters at the epoch, $f_{sgp4}$ is SGP4, $rv_{epoch}$ are the propagated position and velocity vectors at any time, $A_{t_{i}}$ is the Jacobian, and $\delta b_{t_{i}}$ is the residual at any time.

### 3.2 Machine Learning Assisted Estimation

#### 3.2.1 Feature Selection

In the present work, Equinoctial orbital elements and a parameter that we shall call *instance* (Table 2) that defines the sequence of the data with fixed time interval are selected as feature vectors (input data for the machine learning models). The above orbital elements are non-singular, and the magnitude of their values are bounded, thus well-suited for the training process. The Keplerian mean motion, in radians per hour, replaces semi-major axis of the Equinoctial elements. The feature vectors are generated from 8,206,374 TLEs which are all LEO objects that have semi-major axes of 8,378 km and smaller in the space catalog. TLEs are restricted to the LEO orbital regime to ensure that all space objects are subject to a significant orbital perturbation due to atmospheric drag.

Figure 2: Feature vectors selected for the machine learning models.

Parameters|Equations
------------ | -------------
Instance | [2.452, 2.402, ..., 0.049, 0.0]
Mean motion (rad/h) | $n = \sqrt{\frac{\mu}{a^{3}}}$
Ex | $E_{x} = ecos(\omega+\Omega)$
Ey | $E_{y} = esin(\omega+\Omega)$
Hx | $H_{x} = tan(\frac{i}{2})cos(\Omega)$
Hy | $H_{y} = tan(\frac{i}{2})sin(\Omega)$
Lv | $Lv = \nu + \omega + \Omega$

Table 2 : Feature vectors selected for the machine learning models.

#### 3.2.2 Boosting Trees

An optimized distributed gradient boosting library called XGBoost (version 0.7) is used for this work (Available at https://github.com/dmlc/xgboost).  A gradient boosting tree model is trained for each element of a TLE, namely Bstar ($B^{*}$), eccentricity ($e$), inclination ($i$), mean anomaly ($m$), mean motion ($n$), right ascension of the ascending node ($\Omega$), and the argument of perigee ($\omega$). The construction of individual gradient boosting trees for each TLE parameter is chosen because they cannot map to a multidimensional output for regression problems due to the architecture of the decision trees, whereas neural networks can.

Table 3 shows the gradient boosting tree hyperparameters that are optimized for training XGBoost models. The learning rate controls the step size for the gradient-based optimizer. The number of estimators controls the number of trees. The maximum depth limits the depth of each tree which is used to increase the complexity of the model. The minimum child weight is required to avoid overfitting of the training data. A node is split as long as the resultant split leads to a positive reduction in the loss function (mean square error in this work). Gamma can control the split of nodes. The rest of the parameters in Table 3 are used to avoid overfitting by forcing the model to be less complex. The hyperparameters of the gradient boosting trees are tuned empirically by considering the bias-variance tradeoff to ensure the trained models have generalization capability (Table 4). The grid search methods are not preferred because the models are trained with large amount of data.

Parameters| $B^{*}$ | $i$ | $\Omega$ | $e$ | $\omega$ | $m$ | $n$
------------ | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | -------------
Learning rate | 0.1  | 0.3 | 0.3 | 0.3 | 0.2 | 0.2 | 0.1
No. of estimators | 100 | 80 | 70 | 70 | 80 | 80 | 100
Maximum depth | 20 | 20 | 20 | 20 | 25 | 25 | 25
Min. child weight | 7 | 9 | 11 | 13 | 27 | 27 | 1
Gamma | - | - | - | - | 0.7 | 1.0 | -
Subsample | 0.7 | 0.7 | 0.6 | 0.7 | 0.1 | 0.2 | 1.0
Col. sample by tree | 0.7 | 0.8 | 0.7 | 0.7 | 0.2 | 0.3 | 1.0
Col. sample by level | 0.7 | 0.8 | 0.7 | 0.7 | 0.2 | 0.3 | 1.0
Alpha | 0.2 | 0.2 | 0.2 | 0.1 | 0.9 | 0.7 | 0.0

Table 3 : Hyperparameters of XGBoost.

Models| Training Squared Mean Error | Test Squared Mean Error
------------ | ------------- | -------------
$B^{*}$ | 3.9e-5  | 6.3e-5
$i$ | 7.1e-6 | 2.1e-5
$\Omega$ | 3.5e-4 | 2.9e-3
$e$ | 1.6e-6 | 1.3e-5
$\omega$ | 0.21 | 0.32
$m$ | 0.15 | 0.26
$n$ | 9.8e-9  | 2.2e-8

Table 4 : Bias-Variance trade-off for XGBoost.

#### 3.2.3 Deep Learning

Since neural networks can be trained with additional data without being trained from scratch, they are more versatile compared to other methods such as decision trees. In this paper, a fully-connected neural network architecture (Keras-version 2.2.0 using Tensorflow backend-version 1.8.0) is chosen to approximate the TLE estimation scheme (Figure 3). It should be noted that the inputs are standardized.

Figure 3: Fully-connected neural network architecture used for the work.

Table 5 shows the hyperparameters of the fully-connected neural network that are optimized for neural network models. The Adam optimizer is chosen due to its computational efficiency for problems with large amount of data, and it computes the moving average of the gradients and squared gradients. The parameters of the Adam optimizer that control the decay rate of the moving averages **{KA2015}** are $\beta_{1}$ and $\beta_{2}$. The learning rate controls the step size for the Adam optimizer while the decay parameter decays the learning rate at each epoch. The hyperparameters of the fully-connected neural networks are tuned empirically by considering the bias-variance tradeoff to ensure the trained models have generalization capability (Table 6). The grid search methods are not preferred because the models are trained with large amount of data.

Parameters| $B^{*}$ | $i$ | $\Omega$ | $e$ | $\omega$ | $m$ | $n$
------------ | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | -------------
Learning rate | 9e-5  | 9e-5 | 9e-5 | 9e-5 | 9e-5 | 9e-5 | 1e-5
Optimizer | Adam | Adam | Adam | Adam | Adam | Adam | Adam
$\beta_{1}$| 9e-4 | 9e-4 | 9e-4 | 9e-4 | 9e-4 | 9e-4 | 9e-4
$\beta_{2}$| 999e-6 | 999e-6 | 999e-6 | 999e-6 | 999e-6 | 999e-6 | 999e-6
Decay | 1e-07 | 1e-07 | 1e-07 | 1e-07 | 1e-07 | 1e-07 | 1e-07
Epoch number | 50 | 50 | 50 | 50 | 50 | 50 | 50

Table 5 : Hyperparameters of FC network.

Models| Training Squared Mean Error | Test Squared Mean Error
------------ | ------------- | -------------
$B^{*}$ | 1.4e-6  | 2.4e-6
$i$ | 1.3e-4 | 9.8e-5
$\Omega$ | 0.02 | 0.01
$e$ | 1.8e-7 | 2e-7
$\omega$ | 0.24 | 0.22
$m$ | 0.2 | 0.3
$n$ | 4.6e-9  | 5.3e-9

Table 6 : Bias-Variance trade-off for the FC network.

## 4. Results

### 4.1 Monte-Carlo Approach

A novel approach to estimate a TLE from a given ephemeris without any initial estimate of the TLE using a Monte-Carlo method is introduced. Three particular cases with varying area-to-mass ratios, orbital characteristics and solar activity phases, where the standard differential corrections with the nonlinear least squares method **{VC2008}** cannot generate a valid TLE, are chosen as test cases. Initial orbital parameters for the test cases are propagated forward in time using a three-degree-of-freedom (3-DOF) numerical orbit propagator over 1 day. The orbital evolution is defined in the Vehicle Velocity, Local Horizontal (VVLH) reference frame (Figure 4).The perturbations included are: air drag (Harris-Priester model), the Earth's aspherical gravitational potential with order and degree 20 (Holmes-Featherstone model), solar radiation pressure (Lambertian sphere model), and Sun and Moon third-body gravitational perturbations. Parameters of the test cases used for the numerical propagation are given in Table 7.

Figure 4: The Vehicle Velocity, Local Horizontal (VVLH) reference frame.

Parameters| Test case \#1 | Test case \#2| Test case \#3
------------ | ------------- | -------------|-------------
Epoch (UTC) | 2018-12-12T18:47 | 2014-01-15T10:37 | 2009-08-03T15:18
Period (min) | $99.71$ | $94.65$ | $97.32$
Mass (kg) | $800$ |$3$ |$1$
Area ($m^{2}$) | $6$ |$0.03$ |$0.1$
$n$ ($\frac{rad}{s}$) | $0.00105$ |$0.001106$ | $0.001076$
$i$ (deg) | $101.45$ |$85.12$ | $97.60$
$\Omega$ (deg) | $53.03$ |$90.16$ | $253.29$
$\omega$ (deg) | $9.33$ |$204.08$ | $235.32$
$m$ (deg) | $359.44$ |$196.9$ | $178.81$
$e$ | $0.022353$ |$0.011556$ | $0.025822$
$C_{D}$ | $2.2$ |$1.5$ | $0.8$

Table 7 : The parameters of the test cases used for the numerical propagator.

Figure 4 shows the absolute relative distance between the numerically propagated orbit and the SGP4propagated orbit when the inner loop is satisfied for the test cases.

Figure 4: the absolute relative distances with respect to numerically propagated orbit when Keplerian elements are used as TLEs.

Figure 5 shows the absolute relative distance between the numerically propagated orbit and the SGP4propagated orbit when the inner loop is satisfied for the test cases.

Figure 5: Absolute relative distance between the numerically propagated orbit and the SGP4propagated orbit when the inner loop is satisfied.

Figure 6 shows the absolute relative distance with respect to numerically propagated orbit when Kep-lerian elements are used as TLE for the test cases.

Figure 6: Absolute relative distance with respect to numerically propagated orbit when Kep-lerian elements are used as TLE.

In conclusion, the present Monte-Carlo method can estimate a TLE without any initial estimate. Such capability is beneficial to space situational awareness (SSA) because TLEs are required for establishing first contact with the spacecraft during the Launch and Early Orbit Phase (LEOP). Most ground stations have propriety software that requires TLEs to track the satellites. Although the proposed method addresses the difficulty related to unavailability of the initial estimate, the inner loop is iterated 50 times on the average to find an initial estimate with desired precision. Therefore, the feasibility of using machine learning methods to generate initial estimates with the desired precision is investigated.

### 4.2 Machine Learning Assisted TLE Estimation

The performances of machine learning methods differ based on the features selected. Two different machine learning models are trained to predict each mean elements of a TLE, and the best performing model is chosen to be validated by the errors in the orbital evolution. The error in the orbital evolution is computed by propagating TLEs estimated by the selected best machine learning models and the associated CSpOC TLEs backward in time using SGP4 for 50 orbital periods. Table 8 summarizes the best performing models for each mean element that are discussed in detail above.

Mean elements| Best machine learning models
------------ | -------------
$B^{*}$|Gradient boosting trees
$n$|Fully-connected neural network (unless the absolute residual between two models are larger than 0.0002 rad/min for values of $n$ between 0.053 rad/min and 0.067 rad/min)
$i$|Gradient boosting trees (unless the absolute residual between two models are larger than 0.014 rad)
$\Omega$|Gradient boosting trees
$\omega$|Gradient boosting trees
$m$|Fully-connected neural network
$e$|Fully-connected neural network

Table 8 : Best machine learning models for predicting mean elements of a TLE.

Figure 7 shows the absolute  relative  distances  with  respect  to  numerically  propagated  orbits  when TLEs estimated by machine learning models are used for the test cases.

Figure 7: Absolute  relative  distances  with  respect  to  numerically  propagated  orbits  when TLEs estimated by machine learning models are used.

Figure 8 shows the absolute  relative  distances  between  the  numerically  propagated  orbits  and  the SGP4 propagated orbits when the initial estimates that are computed by the machine learning models are improved by nonlinear least squares with differential corrections method for the test cases.

Figure 8: Absolute  relative  distances  between  the  numerically  propagated  orbits  and  the SGP4 propagated orbits when the initial estimates that are computed by the machine learning models are improved by nonlinear least squares with differential corrections method.

In conclusion, the present best machine learning models (selected based ontheir performance) that are trained with TLEs obtained from the official US Space Catalog have the potential to provide an initial estimate to estimate a TLE with desired precision. The inner loop, which searches for the global minimum by using brute force approach, in the Monte-Carlo TLE estimation method can be replaced by machine learning models because there isno secular error growth in the orbital evolution of TLEs predicted by machine learning models (test case #1). Therefore, the standard differential corrections with nonlinear least squares method can converge to a local minimum, and the computation time can be reduced significantly.

## 5. References

[1] Vallado, D., Crawford, P., Hujsak, R. and Kelso, T.S., 2006, August. Revisiting spacetrack report# 3. In AIAA/AAS Astrodynamics Specialist Conference and Exhibit (p. 6753).

[2] Vallado, D. and Crawford, P., 2008, August. SGP4 orbit determination. In AIAA/AAS Astrodynamics Specialist Conference and Exhibit (p. 6770).

[3] Montenbruck, O. and Gill, E., 2000, June. Real-Time estimation of SGP4 orbital elements from GPS navigation data. In International Symposium Space Flight Dynamics (pp. 26-30). Biarritz, France.

[4] Goh, S.T. and Low, K.S., 2018, March. Real-time estimation of satellite's two-line elements via positioning data. In 2018 IEEE Aerospace Conference (pp. 1-7). IEEE.

[5] Bolandi, H., Ashtari Larki, M.H., Sedighy, S.H., Zeighami, M.S. and Esmailzadeh, M., 2015. Estimation of Simplified General Perturbations model 4 orbital elements from global positioning system data by invasive weed optimization algorithm. Proceedings of the Institution of Mechanical Engineers, Part G: Journal of Aerospace Engineering, 229(8), pp.1384-1394.

[6] Cybenko, G., 1989. Approximation by superpositions of a sigmoidal function. Mathematics of control, signals and systems, 2(4), pp.303-314.

[7] Goodfellow, I., Bengio, Y. and Courville, A., 2016. Deep learning. MIT press.

[8] Kingma, D.P. and Ba, J., 2014. Adam: A method for stochastic optimization. arXiv preprint arXiv:1412.6980.

## 6. Code

Since the codes used to generate this research includes different components, codes are not shared in the blog post. Please contact me for the source code.