```python
import numpy as np
import numba
import matplotlib.pyplot as plt
import sympy as sym
plt.style.use('presentation.mplstyle')
%matplotlib notebook

def d2np(d):

    names = []
    numbers = ()
    dtypes = []
    for item in d:
        names += item
        if type(d[item]) == float:
            numbers += (d[item],)
            dtypes += [(item,float)]
        if type(d[item]) == int:
            numbers += (d[item],)
            dtypes += [(item,int)]
        if type(d[item]) == np.ndarray:
            numbers += (d[item],)
            dtypes += [(item,np.float64,d[item].shape)]
    return np.array([numbers],dtype=dtypes)
```

```python
i_1d,i_1q,i_2d,i_2q = sym.symbols('i_1d,i_1q,i_2d,i_2q')
di_1d,di_1q,di_2d,di_2q = sym.symbols('di_1d,di_1q,di_2d,di_2q')
L_1,L_2,C_dc = sym.symbols('L_1,L_2,C_dc')
R_1,R_2 = sym.symbols('R_1,R_2')
omega_1,omega_2 = sym.symbols('omega_1,omega_2')
v_1d,v_1q,v_2d,v_2q, v_dc= sym.symbols('v_1d,v_1q,v_2d,v_2q,v_dc')
eta_1d,eta_1q,eta_2d,eta_2q = sym.symbols('eta_1d,eta_1q,eta_2d,eta_2q')
p_1,q_1 = sym.symbols('p_1,q_1')

i_dc_1 = 3.0/4.0*(eta_1d*i_1d + eta_1q*i_1q)
i_dc_2 = 3.0/4.0*(eta_2d*i_2d + eta_2q*i_2q)

di_1d = 0.5*eta_1d*v_dc - R_1*i_1d + omega_1*i_1q - v_1d
di_1q = 0.5*eta_1q*v_dc - R_1*i_1q - omega_1*i_1d - v_1q
di_2d = 0.5*eta_2d*v_dc - R_2*i_2d + omega_2*i_2q - v_2d
di_2q = 0.5*eta_2q*v_dc - R_2*i_2q - omega_2*i_2d - v_2q
dv_dc = 1.0/C_dc*(-i_dc_1 - i_dc_2)

'''
'''

s = sym.solve([ di_1d,  di_1q,  di_2d,  di_2q],
              [eta_1d, eta_1q, eta_2d, eta_2q])

for item in s:
    print(item, '=', sym.simplify(s[item]))
```

    eta_1d = 2.0*(R_1*i_1d - i_1q*omega_1 + v_1d)/v_dc
    eta_1q = 2.0*(R_1*i_1q + i_1d*omega_1 + v_1q)/v_dc
    eta_2d = 2.0*(R_2*i_2d - i_2q*omega_2 + v_2d)/v_dc
    eta_2q = 2.0*(R_2*i_2q + i_2d*omega_2 + v_2q)/v_dc

```python
eq_p = p_1 - 3.0/2.0*(v_1d*i_1d + v_1q*i_1q)
eq_q = q_1 - 3.0/2.0*(v_1d*i_1q - v_1q*i_1d)

s = sym.solve([ eq_p,  eq_q],
              [i_1d,   i_1q])

for item in s:
    print(item, '=', sym.simplify(s[item]))
```

    i_1d = 0.666666666666667*(p_1*v_1d - q_1*v_1q)/(v_1d**2 + v_1q**2)
    i_1q = 0.666666666666667*(p_1*v_1q + q_1*v_1d)/(v_1d**2 + v_1q**2)

```python
s
```

    {i_1d: 0.666666666666667*(p_1*v_1d - q_1*v_1q)/(v_1d**2 + v_1q**2),
     i_1q: 0.666666666666667*(p_1*v_1q + q_1*v_1d)/(v_1d**2 + v_1q**2)}

```python

@numba.jit(nopython=True, cache=True)
def b2b_ctrl1(struct,i,m):
    '''
    Doubly Fed Induction Machine in with neglected dynamics and
    rotor side converter and control level 1 already implemented.
    i_rd = i_rd_ref and i_rq = i_rq_ref without dynamics
    '''

    x_idx = struct[i]['b2b_idx']
    v_dc = float(struct[i]['x'][x_idx+0,0])

    L_1 = struct[i]['L_1']
    L_2 = struct[i]['L_2']
    R_1 = struct[i]['R_1']
    R_2 = struct[i]['R_2']
    C_dc = struct[i]['C_dc']

    omega_1 = struct[i]['omega_1']
    omega_2 = struct[i]['omega_2']

    i_1d_ref = struct[i]['i_1d_ref']
    i_1q_ref = struct[i]['i_1q_ref']
    i_2d_ref = struct[i]['i_2d_ref']
    i_2q_ref = struct[i]['i_2q_ref']

    i_1d = i_1d_ref
    i_1q = i_1q_ref
    i_2d = i_2d_ref
    i_2q = i_2q_ref

    v_1d = struct[i]['v_1d']
    v_1q = struct[i]['v_1q']
    v_2d = struct[i]['v_2d']
    v_2q = struct[i]['v_2q']

    eta_1d = 2.0*(R_1*i_1d - L_1*i_1q*omega_1 + v_1d)/v_dc
    eta_1q = 2.0*(R_1*i_1q + L_1*i_1d*omega_1 + v_1q)/v_dc
    eta_2d = 2.0*(R_2*i_2d - L_2*i_2q*omega_2 + v_2d)/v_dc
    eta_2q = 2.0*(R_2*i_2q + L_2*i_2d*omega_2 + v_2q)/v_dc

    i_dc_1 = 3.0/4.0*(eta_1d*i_1d + eta_1q*i_1q)
    i_dc_2 = 3.0/4.0*(eta_2d*i_2d + eta_2q*i_2q)
    dv_dc = 1.0/C_dc*(-i_dc_1 - i_dc_2)

    struct[i]['eta_1d'] = eta_1d
    struct[i]['eta_1q'] = eta_1q
    struct[i]['eta_2d'] = eta_2d
    struct[i]['eta_2q'] = eta_2q
    struct[i]['i_dc_1'] = i_dc_1
    struct[i]['i_dc_2'] = i_dc_2

    struct[i]['p_1'] = 3.0/2.0*(v_1d*i_1d + v_1q*i_1q)
    struct[i]['q_1'] = 3.0/2.0*(v_1d*i_1q - v_1q*i_1d)

    struct[i]['p_2'] = 3.0/2.0*(v_2d*i_2d + v_2q*i_2q)
    struct[i]['q_2'] = 3.0/2.0*(v_2d*i_2q - v_2q*i_2d)

    struct[i]['f'][x_idx+0,0] = dv_dc

    return 0

```

```python
@numba.jit(nopython=True, cache=True)
def b2b_ctrl2(struct,i,m):
    '''
    Control level 2 for DC Voltage

    '''

    x_idx = struct[i]['b2b_ctrl_idx']
    xi_v_dc = float(struct[i]['x'][x_idx+0,0])

    S_b = struct[i]['S_b']
    V_dc_b = struct[i]['V_dc_b']

    K_v_p = struct[i]['K_v_p']
    K_v_i = struct[i]['K_v_i']

    v_dc = struct[i]['v_dc']

    v_dc_ref = struct[i]['v_dc_ref']
    p_1_ref = struct[i]['p_1_ref']
    q_1_ref = struct[i]['q_1_ref']
    p_2_ref = struct[i]['p_2_ref']
    q_2_ref = struct[i]['q_2_ref']

    v_1d = struct[i]['v_1d']
    v_1q = struct[i]['v_1q']
    v_2d = struct[i]['v_2d']
    v_2q = struct[i]['v_2q']

    error_v_dc = (v_dc - v_dc_ref)/V_dc_b

    p_ref = (K_v_p * error_v_dc + K_v_i*xi_v_dc)*S_b

    if struct[i]['vdc_ctrl'] == 1:
        p_ref_1 = p_ref

    if struct[i]['vdc_ctrl'] == 2:
        p_ref_2 = p_ref

    den = (v_1d**2 + v_1q**2)

    den_1 = 0.001
    if den_1 > 0.0:
        den_1 = (v_1d**2 + v_1q**2)

    den_2 = 0.001
    if den_2 > 0.0:
        den_2 = (v_2d**2 + v_2q**2)

    i_1d_ref = 2.0/3.0*(p_1_ref*v_1d - q_1_ref*v_1q)/den_1
    i_1q_ref = 2.0/3.0*(p_1_ref*v_1q + q_1_ref*v_1d)/den_1

    i_2d_ref = 2.0/3.0*(p_2_ref*v_2d - q_2_ref*v_1q)/den_2
    i_2q_ref = 2.0/3.0*(p_2_ref*v_2q + q_2_ref*v_1d)/den_2

    struct[i]['i_1d_ref'] = i_1d_ref
    struct[i]['i_1q_ref'] = i_1q_ref
    struct[i]['i_2d_ref'] = i_2d_ref
    struct[i]['i_2q_ref'] = i_2q_ref

    dxi_v_dc = error_v_dc

    struct[i]['f'][x_idx+0,0] = dxi_v_dc

    return 0

```

```python

```

```python
R_1 = R_2 = 0.1
L_1 = L_2 = 0.5e-3
Omega_b = 2.0*np.pi*50.0
C_dc = 2200.0e-6
omega_1 = omega_2 = Omega_b

d =dict(R_1 = R_1,
        R_2 = R_2,
        L_1 = L_1,
        L_2 = L_2,
        C_dc = C_dc,
        b2b_idx = 0,
        b2b_ctrl_idx = 1,
        v_dc = 800.0,
        omega_1 = omega_1,
        omega_2 = omega_2,
        i_1d_ref = 0.0,
        i_1q_ref = 100.0,
        i_2d_ref = 0.0,
        i_2q_ref = -100.0,
        i_dc_1 = 0.0,
        i_dc_2 = 0.0,
        eta_1d = 0.0,
        eta_1q = 0.0,
        eta_2d = 0.0,
        eta_2q = 0.0,
        v_1d = 0.0,
        v_1q = 325.0,
        v_2d = 0.0,
        v_2q = 325.0,
        p_1 = 0.0,
        q_1 = 0.0,
        p_2 = 0.0,
        q_2 = 0.0,
        x_idx = 0,
        xi_v_dc = 0.0,
        S_b = 0.5e6,
        V_dc_b = 800.0,
        K_v_p = 0.1,
        K_v_i = 0.0,
        v_dc_ref = 750.0,
        p_1_ref = 0.0,
        q_1_ref = 0.0,
        p_2_ref = 0.0,
        q_2_ref = 0.0,
        vdc_ctrl = 1,
        x = np.array([[800.0],[0.0]]),
        f = np.array([[0.0],[0.0]])
        )

struct = d2np(d)

i=0
m=2
b2b_ctrl1(struct,i,m)
b2b_ctrl2(struct,i,m)
print(struct[i]['p_1'])
print(struct[i]['p_2'])
print(struct[i]['i_dc_1'])
print(struct[i]['i_dc_2'])
print(struct[i]['f'])
```

    48750.0
    -48750.0
    62.8125
    -59.0625
    [[ -1.70454545e+03]
     [  6.25000000e-02]]

```python
struct = d2np(d)

sys_d = dict(x = np.array([[800.0],[0.0]]),
             f = np.zeros((2,1)))

sys_struct = d2np(sys_d)

@numba.jit(nopython=True, cache=True)
def f_eval(sys_struct,struct):
    N_states = 2
    for i in range(1):
        struct[i]['x'][:,0] = sys_struct[0]['x'][N_states*i:N_states*(i+1),0]
        b2b_ctrl1(struct,i,m)
        b2b_ctrl2(struct,i,m)
        sys_struct[0]['f'][N_states*i:N_states*(i+1),:] = struct[i]['f']
    return 0

```

```python
@numba.jit(nopython=True, cache=True)
def run(sys_struct,struct):
    N_steps = 1000
    N_states = 2

    Dt = 10.0e-3
    Omega_r = np.zeros((N_steps,1))
    Omega_t = np.zeros((N_steps,1))
    P_1 = np.zeros((N_steps,1))
    Q_1 = np.zeros((N_steps,1))
    P_2 = np.zeros((N_steps,1))
    Q_2 = np.zeros((N_steps,1))
    V_dr = np.zeros((N_steps,1))
    V_qr = np.zeros((N_steps,1))
    I_dr = np.zeros((N_steps,1))
    I_qr = np.zeros((N_steps,1))
    Tau_e = np.zeros((N_steps,1))
    T     = np.zeros((N_steps,1))
    X = np.zeros((N_steps,N_states))

    V_dc = np.zeros((N_steps,1))
    p_ref = 0.0
    q_ref = 0.0
    xi_p = 0.0
    xi_q = 0.0

    struct[0]['x'][:,0] = np.copy(sys_struct[0]['x'][0:2,0])

    for it in range(N_steps):
        t = Dt*float(it)

        # perturbations and references

        struct[0]['p_1_ref'] = 0.0
        struct[0]['p_2_ref'] = 0.0
        struct[0]['q_1_ref'] = 0.0
        struct[0]['q_2_ref'] = 0.0

        if t>2.0:
            struct[0]['p_1_ref'] = 1.0e6
        if t>3.0:
            struct[0]['p_2_ref'] = 0.1e6

        ## solver

        f_eval(sys_struct,struct)
        f1 = np.copy(sys_struct[0]['f'])
        x1 = np.copy(sys_struct[0]['x'])

        sys_struct[0]['x'][:]= np.copy(x1 + Dt*f1)
        f_eval(sys_struct,struct)
        f2 = np.copy(sys_struct[0]['f'])

        sys_struct[0]['x'][:]= np.copy(x1 + 0.5*Dt*(f1 + f2))

        for i in range(1):
            struct[i]['x'][:,0] = sys_struct[0]['x'][2*i:2*(i+1),0]

        T[it,0] = t
        V_dc[it,0] = float(struct[0]['v_dc'])
        X[it,:] = sys_struct[0]['x'][:].T
    return T,X,V_dc
%timeit run(sys_struct, struct)
```

    743 µs ± 172 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

```python
run(sys_struct, struct)
```

    (array([[ 0.  ],
            [ 0.01],
            [ 0.02],
            [ 0.03],
            [ 0.04],
            [ 0.05],
            [ 0.06],
            [ 0.07],
            [ 0.08],
            [ 0.09],
            [ 0.1 ],
            [ 0.11],
            [ 0.12],
            [ 0.13],
            [ 0.14],
            [ 0.15],
            [ 0.16],
            [ 0.17],
            [ 0.18],
            [ 0.19],
            [ 0.2 ],
            [ 0.21],
            [ 0.22],
            [ 0.23],
            [ 0.24],
            [ 0.25],
            [ 0.26],
            [ 0.27],
            [ 0.28],
            [ 0.29],
            [ 0.3 ],
            [ 0.31],
            [ 0.32],
            [ 0.33],
            [ 0.34],
            [ 0.35],
            [ 0.36],
            [ 0.37],
            [ 0.38],
            [ 0.39],
            [ 0.4 ],
            [ 0.41],
            [ 0.42],
            [ 0.43],
            [ 0.44],
            [ 0.45],
            [ 0.46],
            [ 0.47],
            [ 0.48],
            [ 0.49],
            [ 0.5 ],
            [ 0.51],
            [ 0.52],
            [ 0.53],
            [ 0.54],
            [ 0.55],
            [ 0.56],
            [ 0.57],
            [ 0.58],
            [ 0.59],
            [ 0.6 ],
            [ 0.61],
            [ 0.62],
            [ 0.63],
            [ 0.64],
            [ 0.65],
            [ 0.66],
            [ 0.67],
            [ 0.68],
            [ 0.69],
            [ 0.7 ],
            [ 0.71],
            [ 0.72],
            [ 0.73],
            [ 0.74],
            [ 0.75],
            [ 0.76],
            [ 0.77],
            [ 0.78],
            [ 0.79],
            [ 0.8 ],
            [ 0.81],
            [ 0.82],
            [ 0.83],
            [ 0.84],
            [ 0.85],
            [ 0.86],
            [ 0.87],
            [ 0.88],
            [ 0.89],
            [ 0.9 ],
            [ 0.91],
            [ 0.92],
            [ 0.93],
            [ 0.94],
            [ 0.95],
            [ 0.96],
            [ 0.97],
            [ 0.98],
            [ 0.99],
            [ 1.  ],
            [ 1.01],
            [ 1.02],
            [ 1.03],
            [ 1.04],
            [ 1.05],
            [ 1.06],
            [ 1.07],
            [ 1.08],
            [ 1.09],
            [ 1.1 ],
            [ 1.11],
            [ 1.12],
            [ 1.13],
            [ 1.14],
            [ 1.15],
            [ 1.16],
            [ 1.17],
            [ 1.18],
            [ 1.19],
            [ 1.2 ],
            [ 1.21],
            [ 1.22],
            [ 1.23],
            [ 1.24],
            [ 1.25],
            [ 1.26],
            [ 1.27],
            [ 1.28],
            [ 1.29],
            [ 1.3 ],
            [ 1.31],
            [ 1.32],
            [ 1.33],
            [ 1.34],
            [ 1.35],
            [ 1.36],
            [ 1.37],
            [ 1.38],
            [ 1.39],
            [ 1.4 ],
            [ 1.41],
            [ 1.42],
            [ 1.43],
            [ 1.44],
            [ 1.45],
            [ 1.46],
            [ 1.47],
            [ 1.48],
            [ 1.49],
            [ 1.5 ],
            [ 1.51],
            [ 1.52],
            [ 1.53],
            [ 1.54],
            [ 1.55],
            [ 1.56],
            [ 1.57],
            [ 1.58],
            [ 1.59],
            [ 1.6 ],
            [ 1.61],
            [ 1.62],
            [ 1.63],
            [ 1.64],
            [ 1.65],
            [ 1.66],
            [ 1.67],
            [ 1.68],
            [ 1.69],
            [ 1.7 ],
            [ 1.71],
            [ 1.72],
            [ 1.73],
            [ 1.74],
            [ 1.75],
            [ 1.76],
            [ 1.77],
            [ 1.78],
            [ 1.79],
            [ 1.8 ],
            [ 1.81],
            [ 1.82],
            [ 1.83],
            [ 1.84],
            [ 1.85],
            [ 1.86],
            [ 1.87],
            [ 1.88],
            [ 1.89],
            [ 1.9 ],
            [ 1.91],
            [ 1.92],
            [ 1.93],
            [ 1.94],
            [ 1.95],
            [ 1.96],
            [ 1.97],
            [ 1.98],
            [ 1.99],
            [ 2.  ],
            [ 2.01],
            [ 2.02],
            [ 2.03],
            [ 2.04],
            [ 2.05],
            [ 2.06],
            [ 2.07],
            [ 2.08],
            [ 2.09],
            [ 2.1 ],
            [ 2.11],
            [ 2.12],
            [ 2.13],
            [ 2.14],
            [ 2.15],
            [ 2.16],
            [ 2.17],
            [ 2.18],
            [ 2.19],
            [ 2.2 ],
            [ 2.21],
            [ 2.22],
            [ 2.23],
            [ 2.24],
            [ 2.25],
            [ 2.26],
            [ 2.27],
            [ 2.28],
            [ 2.29],
            [ 2.3 ],
            [ 2.31],
            [ 2.32],
            [ 2.33],
            [ 2.34],
            [ 2.35],
            [ 2.36],
            [ 2.37],
            [ 2.38],
            [ 2.39],
            [ 2.4 ],
            [ 2.41],
            [ 2.42],
            [ 2.43],
            [ 2.44],
            [ 2.45],
            [ 2.46],
            [ 2.47],
            [ 2.48],
            [ 2.49],
            [ 2.5 ],
            [ 2.51],
            [ 2.52],
            [ 2.53],
            [ 2.54],
            [ 2.55],
            [ 2.56],
            [ 2.57],
            [ 2.58],
            [ 2.59],
            [ 2.6 ],
            [ 2.61],
            [ 2.62],
            [ 2.63],
            [ 2.64],
            [ 2.65],
            [ 2.66],
            [ 2.67],
            [ 2.68],
            [ 2.69],
            [ 2.7 ],
            [ 2.71],
            [ 2.72],
            [ 2.73],
            [ 2.74],
            [ 2.75],
            [ 2.76],
            [ 2.77],
            [ 2.78],
            [ 2.79],
            [ 2.8 ],
            [ 2.81],
            [ 2.82],
            [ 2.83],
            [ 2.84],
            [ 2.85],
            [ 2.86],
            [ 2.87],
            [ 2.88],
            [ 2.89],
            [ 2.9 ],
            [ 2.91],
            [ 2.92],
            [ 2.93],
            [ 2.94],
            [ 2.95],
            [ 2.96],
            [ 2.97],
            [ 2.98],
            [ 2.99],
            [ 3.  ],
            [ 3.01],
            [ 3.02],
            [ 3.03],
            [ 3.04],
            [ 3.05],
            [ 3.06],
            [ 3.07],
            [ 3.08],
            [ 3.09],
            [ 3.1 ],
            [ 3.11],
            [ 3.12],
            [ 3.13],
            [ 3.14],
            [ 3.15],
            [ 3.16],
            [ 3.17],
            [ 3.18],
            [ 3.19],
            [ 3.2 ],
            [ 3.21],
            [ 3.22],
            [ 3.23],
            [ 3.24],
            [ 3.25],
            [ 3.26],
            [ 3.27],
            [ 3.28],
            [ 3.29],
            [ 3.3 ],
            [ 3.31],
            [ 3.32],
            [ 3.33],
            [ 3.34],
            [ 3.35],
            [ 3.36],
            [ 3.37],
            [ 3.38],
            [ 3.39],
            [ 3.4 ],
            [ 3.41],
            [ 3.42],
            [ 3.43],
            [ 3.44],
            [ 3.45],
            [ 3.46],
            [ 3.47],
            [ 3.48],
            [ 3.49],
            [ 3.5 ],
            [ 3.51],
            [ 3.52],
            [ 3.53],
            [ 3.54],
            [ 3.55],
            [ 3.56],
            [ 3.57],
            [ 3.58],
            [ 3.59],
            [ 3.6 ],
            [ 3.61],
            [ 3.62],
            [ 3.63],
            [ 3.64],
            [ 3.65],
            [ 3.66],
            [ 3.67],
            [ 3.68],
            [ 3.69],
            [ 3.7 ],
            [ 3.71],
            [ 3.72],
            [ 3.73],
            [ 3.74],
            [ 3.75],
            [ 3.76],
            [ 3.77],
            [ 3.78],
            [ 3.79],
            [ 3.8 ],
            [ 3.81],
            [ 3.82],
            [ 3.83],
            [ 3.84],
            [ 3.85],
            [ 3.86],
            [ 3.87],
            [ 3.88],
            [ 3.89],
            [ 3.9 ],
            [ 3.91],
            [ 3.92],
            [ 3.93],
            [ 3.94],
            [ 3.95],
            [ 3.96],
            [ 3.97],
            [ 3.98],
            [ 3.99],
            [ 4.  ],
            [ 4.01],
            [ 4.02],
            [ 4.03],
            [ 4.04],
            [ 4.05],
            [ 4.06],
            [ 4.07],
            [ 4.08],
            [ 4.09],
            [ 4.1 ],
            [ 4.11],
            [ 4.12],
            [ 4.13],
            [ 4.14],
            [ 4.15],
            [ 4.16],
            [ 4.17],
            [ 4.18],
            [ 4.19],
            [ 4.2 ],
            [ 4.21],
            [ 4.22],
            [ 4.23],
            [ 4.24],
            [ 4.25],
            [ 4.26],
            [ 4.27],
            [ 4.28],
            [ 4.29],
            [ 4.3 ],
            [ 4.31],
            [ 4.32],
            [ 4.33],
            [ 4.34],
            [ 4.35],
            [ 4.36],
            [ 4.37],
            [ 4.38],
            [ 4.39],
            [ 4.4 ],
            [ 4.41],
            [ 4.42],
            [ 4.43],
            [ 4.44],
            [ 4.45],
            [ 4.46],
            [ 4.47],
            [ 4.48],
            [ 4.49],
            [ 4.5 ],
            [ 4.51],
            [ 4.52],
            [ 4.53],
            [ 4.54],
            [ 4.55],
            [ 4.56],
            [ 4.57],
            [ 4.58],
            [ 4.59],
            [ 4.6 ],
            [ 4.61],
            [ 4.62],
            [ 4.63],
            [ 4.64],
            [ 4.65],
            [ 4.66],
            [ 4.67],
            [ 4.68],
            [ 4.69],
            [ 4.7 ],
            [ 4.71],
            [ 4.72],
            [ 4.73],
            [ 4.74],
            [ 4.75],
            [ 4.76],
            [ 4.77],
            [ 4.78],
            [ 4.79],
            [ 4.8 ],
            [ 4.81],
            [ 4.82],
            [ 4.83],
            [ 4.84],
            [ 4.85],
            [ 4.86],
            [ 4.87],
            [ 4.88],
            [ 4.89],
            [ 4.9 ],
            [ 4.91],
            [ 4.92],
            [ 4.93],
            [ 4.94],
            [ 4.95],
            [ 4.96],
            [ 4.97],
            [ 4.98],
            [ 4.99],
            [ 5.  ],
            [ 5.01],
            [ 5.02],
            [ 5.03],
            [ 5.04],
            [ 5.05],
            [ 5.06],
            [ 5.07],
            [ 5.08],
            [ 5.09],
            [ 5.1 ],
            [ 5.11],
            [ 5.12],
            [ 5.13],
            [ 5.14],
            [ 5.15],
            [ 5.16],
            [ 5.17],
            [ 5.18],
            [ 5.19],
            [ 5.2 ],
            [ 5.21],
            [ 5.22],
            [ 5.23],
            [ 5.24],
            [ 5.25],
            [ 5.26],
            [ 5.27],
            [ 5.28],
            [ 5.29],
            [ 5.3 ],
            [ 5.31],
            [ 5.32],
            [ 5.33],
            [ 5.34],
            [ 5.35],
            [ 5.36],
            [ 5.37],
            [ 5.38],
            [ 5.39],
            [ 5.4 ],
            [ 5.41],
            [ 5.42],
            [ 5.43],
            [ 5.44],
            [ 5.45],
            [ 5.46],
            [ 5.47],
            [ 5.48],
            [ 5.49],
            [ 5.5 ],
            [ 5.51],
            [ 5.52],
            [ 5.53],
            [ 5.54],
            [ 5.55],
            [ 5.56],
            [ 5.57],
            [ 5.58],
            [ 5.59],
            [ 5.6 ],
            [ 5.61],
            [ 5.62],
            [ 5.63],
            [ 5.64],
            [ 5.65],
            [ 5.66],
            [ 5.67],
            [ 5.68],
            [ 5.69],
            [ 5.7 ],
            [ 5.71],
            [ 5.72],
            [ 5.73],
            [ 5.74],
            [ 5.75],
            [ 5.76],
            [ 5.77],
            [ 5.78],
            [ 5.79],
            [ 5.8 ],
            [ 5.81],
            [ 5.82],
            [ 5.83],
            [ 5.84],
            [ 5.85],
            [ 5.86],
            [ 5.87],
            [ 5.88],
            [ 5.89],
            [ 5.9 ],
            [ 5.91],
            [ 5.92],
            [ 5.93],
            [ 5.94],
            [ 5.95],
            [ 5.96],
            [ 5.97],
            [ 5.98],
            [ 5.99],
            [ 6.  ],
            [ 6.01],
            [ 6.02],
            [ 6.03],
            [ 6.04],
            [ 6.05],
            [ 6.06],
            [ 6.07],
            [ 6.08],
            [ 6.09],
            [ 6.1 ],
            [ 6.11],
            [ 6.12],
            [ 6.13],
            [ 6.14],
            [ 6.15],
            [ 6.16],
            [ 6.17],
            [ 6.18],
            [ 6.19],
            [ 6.2 ],
            [ 6.21],
            [ 6.22],
            [ 6.23],
            [ 6.24],
            [ 6.25],
            [ 6.26],
            [ 6.27],
            [ 6.28],
            [ 6.29],
            [ 6.3 ],
            [ 6.31],
            [ 6.32],
            [ 6.33],
            [ 6.34],
            [ 6.35],
            [ 6.36],
            [ 6.37],
            [ 6.38],
            [ 6.39],
            [ 6.4 ],
            [ 6.41],
            [ 6.42],
            [ 6.43],
            [ 6.44],
            [ 6.45],
            [ 6.46],
            [ 6.47],
            [ 6.48],
            [ 6.49],
            [ 6.5 ],
            [ 6.51],
            [ 6.52],
            [ 6.53],
            [ 6.54],
            [ 6.55],
            [ 6.56],
            [ 6.57],
            [ 6.58],
            [ 6.59],
            [ 6.6 ],
            [ 6.61],
            [ 6.62],
            [ 6.63],
            [ 6.64],
            [ 6.65],
            [ 6.66],
            [ 6.67],
            [ 6.68],
            [ 6.69],
            [ 6.7 ],
            [ 6.71],
            [ 6.72],
            [ 6.73],
            [ 6.74],
            [ 6.75],
            [ 6.76],
            [ 6.77],
            [ 6.78],
            [ 6.79],
            [ 6.8 ],
            [ 6.81],
            [ 6.82],
            [ 6.83],
            [ 6.84],
            [ 6.85],
            [ 6.86],
            [ 6.87],
            [ 6.88],
            [ 6.89],
            [ 6.9 ],
            [ 6.91],
            [ 6.92],
            [ 6.93],
            [ 6.94],
            [ 6.95],
            [ 6.96],
            [ 6.97],
            [ 6.98],
            [ 6.99],
            [ 7.  ],
            [ 7.01],
            [ 7.02],
            [ 7.03],
            [ 7.04],
            [ 7.05],
            [ 7.06],
            [ 7.07],
            [ 7.08],
            [ 7.09],
            [ 7.1 ],
            [ 7.11],
            [ 7.12],
            [ 7.13],
            [ 7.14],
            [ 7.15],
            [ 7.16],
            [ 7.17],
            [ 7.18],
            [ 7.19],
            [ 7.2 ],
            [ 7.21],
            [ 7.22],
            [ 7.23],
            [ 7.24],
            [ 7.25],
            [ 7.26],
            [ 7.27],
            [ 7.28],
            [ 7.29],
            [ 7.3 ],
            [ 7.31],
            [ 7.32],
            [ 7.33],
            [ 7.34],
            [ 7.35],
            [ 7.36],
            [ 7.37],
            [ 7.38],
            [ 7.39],
            [ 7.4 ],
            [ 7.41],
            [ 7.42],
            [ 7.43],
            [ 7.44],
            [ 7.45],
            [ 7.46],
            [ 7.47],
            [ 7.48],
            [ 7.49],
            [ 7.5 ],
            [ 7.51],
            [ 7.52],
            [ 7.53],
            [ 7.54],
            [ 7.55],
            [ 7.56],
            [ 7.57],
            [ 7.58],
            [ 7.59],
            [ 7.6 ],
            [ 7.61],
            [ 7.62],
            [ 7.63],
            [ 7.64],
            [ 7.65],
            [ 7.66],
            [ 7.67],
            [ 7.68],
            [ 7.69],
            [ 7.7 ],
            [ 7.71],
            [ 7.72],
            [ 7.73],
            [ 7.74],
            [ 7.75],
            [ 7.76],
            [ 7.77],
            [ 7.78],
            [ 7.79],
            [ 7.8 ],
            [ 7.81],
            [ 7.82],
            [ 7.83],
            [ 7.84],
            [ 7.85],
            [ 7.86],
            [ 7.87],
            [ 7.88],
            [ 7.89],
            [ 7.9 ],
            [ 7.91],
            [ 7.92],
            [ 7.93],
            [ 7.94],
            [ 7.95],
            [ 7.96],
            [ 7.97],
            [ 7.98],
            [ 7.99],
            [ 8.  ],
            [ 8.01],
            [ 8.02],
            [ 8.03],
            [ 8.04],
            [ 8.05],
            [ 8.06],
            [ 8.07],
            [ 8.08],
            [ 8.09],
            [ 8.1 ],
            [ 8.11],
            [ 8.12],
            [ 8.13],
            [ 8.14],
            [ 8.15],
            [ 8.16],
            [ 8.17],
            [ 8.18],
            [ 8.19],
            [ 8.2 ],
            [ 8.21],
            [ 8.22],
            [ 8.23],
            [ 8.24],
            [ 8.25],
            [ 8.26],
            [ 8.27],
            [ 8.28],
            [ 8.29],
            [ 8.3 ],
            [ 8.31],
            [ 8.32],
            [ 8.33],
            [ 8.34],
            [ 8.35],
            [ 8.36],
            [ 8.37],
            [ 8.38],
            [ 8.39],
            [ 8.4 ],
            [ 8.41],
            [ 8.42],
            [ 8.43],
            [ 8.44],
            [ 8.45],
            [ 8.46],
            [ 8.47],
            [ 8.48],
            [ 8.49],
            [ 8.5 ],
            [ 8.51],
            [ 8.52],
            [ 8.53],
            [ 8.54],
            [ 8.55],
            [ 8.56],
            [ 8.57],
            [ 8.58],
            [ 8.59],
            [ 8.6 ],
            [ 8.61],
            [ 8.62],
            [ 8.63],
            [ 8.64],
            [ 8.65],
            [ 8.66],
            [ 8.67],
            [ 8.68],
            [ 8.69],
            [ 8.7 ],
            [ 8.71],
            [ 8.72],
            [ 8.73],
            [ 8.74],
            [ 8.75],
            [ 8.76],
            [ 8.77],
            [ 8.78],
            [ 8.79],
            [ 8.8 ],
            [ 8.81],
            [ 8.82],
            [ 8.83],
            [ 8.84],
            [ 8.85],
            [ 8.86],
            [ 8.87],
            [ 8.88],
            [ 8.89],
            [ 8.9 ],
            [ 8.91],
            [ 8.92],
            [ 8.93],
            [ 8.94],
            [ 8.95],
            [ 8.96],
            [ 8.97],
            [ 8.98],
            [ 8.99],
            [ 9.  ],
            [ 9.01],
            [ 9.02],
            [ 9.03],
            [ 9.04],
            [ 9.05],
            [ 9.06],
            [ 9.07],
            [ 9.08],
            [ 9.09],
            [ 9.1 ],
            [ 9.11],
            [ 9.12],
            [ 9.13],
            [ 9.14],
            [ 9.15],
            [ 9.16],
            [ 9.17],
            [ 9.18],
            [ 9.19],
            [ 9.2 ],
            [ 9.21],
            [ 9.22],
            [ 9.23],
            [ 9.24],
            [ 9.25],
            [ 9.26],
            [ 9.27],
            [ 9.28],
            [ 9.29],
            [ 9.3 ],
            [ 9.31],
            [ 9.32],
            [ 9.33],
            [ 9.34],
            [ 9.35],
            [ 9.36],
            [ 9.37],
            [ 9.38],
            [ 9.39],
            [ 9.4 ],
            [ 9.41],
            [ 9.42],
            [ 9.43],
            [ 9.44],
            [ 9.45],
            [ 9.46],
            [ 9.47],
            [ 9.48],
            [ 9.49],
            [ 9.5 ],
            [ 9.51],
            [ 9.52],
            [ 9.53],
            [ 9.54],
            [ 9.55],
            [ 9.56],
            [ 9.57],
            [ 9.58],
            [ 9.59],
            [ 9.6 ],
            [ 9.61],
            [ 9.62],
            [ 9.63],
            [ 9.64],
            [ 9.65],
            [ 9.66],
            [ 9.67],
            [ 9.68],
            [ 9.69],
            [ 9.7 ],
            [ 9.71],
            [ 9.72],
            [ 9.73],
            [ 9.74],
            [ 9.75],
            [ 9.76],
            [ 9.77],
            [ 9.78],
            [ 9.79],
            [ 9.8 ],
            [ 9.81],
            [ 9.82],
            [ 9.83],
            [ 9.84],
            [ 9.85],
            [ 9.86],
            [ 9.87],
            [ 9.88],
            [ 9.89],
            [ 9.9 ],
            [ 9.91],
            [ 9.92],
            [ 9.93],
            [ 9.94],
            [ 9.95],
            [ 9.96],
            [ 9.97],
            [ 9.98],
            [ 9.99]]), array([[  6.78132092e+04,   5.00062500e+00],
            [  6.78132092e+04,   5.00125000e+00],
            [  6.78132092e+04,   5.00187500e+00],
            ...,
            [  1.02399321e+06,   5.62375000e+00],
            [  1.02398550e+06,   5.62437500e+00],
            [  1.02397778e+06,   5.62500000e+00]]), array([[ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.],
            [ 800.]]))

```python
sys_struct['x'][:]= np.zeros((6,1))
struct['v_qs'] = 0.0
struct['v_ds'] = 690.0*np.sqrt(2.0/3.0)
struct['tau_t'] = 0.0
sys_struct[0]['x'][0,0] = Omega_b*0.9/struct[0]['N_tr']/struct[0]['N_pp']
sys_struct[0]['x'][3,0] = Omega_b*1.1/struct[1]['N_tr']/struct[0]['N_pp']
T,X,Tau_e,P_s_1,Q_s_1,P_r_1,Q_r_1,P_s_2,Q_s_2,P_r_2,Q_r_2,V_dr,V_qr,Omega_r,Omega_t,I_dr,I_qr = run(sys_struct, struct)
```

```python
fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(8, 5), sharex = True)
axes.plot(T,Tau_e)
fig.savefig('dfim_tau_e.svg', bbox_inches='tight')
```

```python
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 8), sharex = True)
axes[0].plot(T,P_s_1/1e6, label='$\sf p_{s1}$')
axes[0].plot(T,Q_s_1/1e6, label='$\sf q_{s1}$')
axes[0].plot(T,P_s_2/1e6, label='$\sf p_{s2}$')
axes[0].plot(T,Q_s_2/1e6, label='$\sf q_{s2}$')

axes[1].plot(T,P_r_1/1e6, label='$\sf p_{r1}$')
axes[1].plot(T,Q_r_1/1e6, label='$\sf q_{r1}$')
axes[1].plot(T,P_r_2/1e6, label='$\sf p_{r2}$')
axes[1].plot(T,Q_r_2/1e6, label='$\sf q_{r2}$')

axes[0].legend()
axes[1].legend()
fig.savefig('dfim_tau_e.svg', bbox_inches='tight')

```

```python
fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(8, 5), sharex = True)
axes.plot(T,Omega_t)
```

    <IPython.core.display.Javascript object>

    [<matplotlib.lines.Line2D at 0x7f3e5e630c88>]

```python
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 8), sharex = True)
axes[0].plot(T,V_dr, label='$\sf v_{dr}$')
axes[0].plot(T,V_qr, label='$\sf v_{qr}$')
```

    <IPython.core.display.Javascript object>

    [<matplotlib.lines.Line2D at 0x7f3e5f319080>]

```python
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 8), sharex = True)
axes[0].plot(T,Omega_t, label='$\sf v_{dr}$')

```

    <IPython.core.display.Javascript object>

    [<matplotlib.lines.Line2D at 0x7f3e5f0e29e8>]

```python
Omega_t[0]
```

    array([ 28.49112481])

```python
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 8), sharex = True)
axes[0].plot(T,I_dr, label='$\sf i_{dr}$')
axes[0].plot(T,I_qr, label='$\sf i_{qr}$')
```

    <IPython.core.display.Javascript object>

    [<matplotlib.lines.Line2D at 0x7f3e5e7fc128>]

```python
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 8), sharex = True)
axes[0].plot(T,X[:,5], label='$\sf x$')

```

    <IPython.core.display.Javascript object>

    [<matplotlib.lines.Line2D at 0x7f3e5e7fcf28>]

```python
np.random.normal(500e3,100e3)
```

    544736.9086734804

```python

```

```python

```

```python

```


$$
\newcommand{\set}[1]{\left\{#1\right\}}
\newcommand{\abs}[1]{\left\lvert#1\right\rvert}
\newcommand{\norm}[1]{\left\lVert#1\right\rVert}
\newcommand{\inner}[2]{\left\langle#1,#2\right\rangle}
\newcommand{\bra}[1]{\left\langle#1\right|}
\newcommand{\ket}[1]{\left|#1\right\rangle}
\newcommand{\braket}[2]{\left\langle#1|#2\right\rangle}
\newcommand{\ketbra}[2]{\left|#1\right\rangle\left\langle#2\right|}
\newcommand{\angleset}[1]{\left\langle#1\right\rangle}
\newcommand{\expected}[1]{\left\langle#1\right\rangle}
\newcommand{\dv}[2]{\frac{d#1}{d#2}}
$$

# Time Evolution Operator

_prepared by Israel Gelover_

The following definition is closely related to Postulate VII on time evolution.

### Definition 3.2

The _Time Evolution Operator_ $\hat{T}(t,t_0)$ is defined as:

\begin{equation*}
    \ket{\psi(t)} = \hat{T}(t,t_0)\ket{\psi(t_0)}
\end{equation*}

Note that postulate VII already tells us what the time evolution of a state is like, what we are looking for with this definition is to have a version defined in terms of a linear operator. That is, we want that when applying this operator to the state of the system at time $t_0$ it tells us what the state of the system will be at time $t$ in the future. Up to this point we have not said what this operator is, but we are describing how it should act in the event that
exist.

### Proposition 3.3

By Postulate I, we know that states are unit vectors, that is

\begin{equation*}
    1 = \braket{\psi(t_0)}{\psi(t_0)} = \braket{\psi(t)}{\psi(t)}
\end{equation*}

From the definition we have that

\begin{equation*}
    \ket{\psi(t)} = \hat{T}(t,t_0)\ket{\psi(t_0)}
\end{equation*}

Thus, rewriting $\ket{\psi(t)}$ we get

\begin{equation*}
    1 = \braket{\psi(t)}{\psi(t)} = \bra{\psi(t_0)}\hat{T}^\dagger(t,t_0) \hat{T}(t,t_0)\ket{\psi(t_0)}
\end{equation*}

That is, the operator $\hat{T}(t,t_0)$ preserves the inner product, therefore $\hat{T}(t,t_0)$ is unitary.

Let's see now that there is indeed a time evolution operator.

### Remark 3.4

If the Hamiltonian $\hat{H}$ does not explicitly depend on time $t$ (that is, the system is closed, meaning the energy does not change in time), then the time evolution operator is given by

\begin{equation*}
    \hat{T}(t,t_0) = e^{-\frac{i}{\hbar}(t-t_0)\hat{H}}
\end{equation*}

where the exponentiation of a matrix is defined as $e^{\hat{A}} = \sum_{n=0}^\infty \frac{\hat{A}^{n}}{n!}$, which again is a matrix.

Why? Let's elaborate a bit on the motivation for this claim. Let us consider the following differential equation: A constant $K$ multiplying the derivative of a real function $f$, equal to another constant that we will call $H$, multiplied by the same function $f$.

\begin{equation}\label{edif}
    \begin{split}
        K\dv{f}{t} &= Hf(t) \enspace \text{with } K,H \in \mathbb{R} \\
        \implies \dv{f}{t} &= \frac{H}{K}f(t) \\
        \implies f(t) &= e^{\frac{H}{K}(t-t_0)}f(t_0)
    \end{split}
\end{equation}

where the initial condition of the system is $f(t_0)$.

As an observation that we are going to need, we mention that a result of the Spectral Theory is that self-adjoint matrices are diagonalizable. On the other hand, if we consider Schrödinger Equation

\begin{equation}\label{inicial}
    i\hbar\dv{}{t}\ket{\psi(t)} = \hat{H}\ket{\psi(t)}
\end{equation}

where $\hat{H}$ is a diagonal matrix, what we obtain is a system of equations of type $K\dv{f}{t} = Hf(t)$.

How can we visualize this? Take as an example

\begin{equation*}
    \ket{\psi(t)} = \begin{pmatrix}\psi_1(t) \\ \psi_2(t)\end{pmatrix} \enspace \text{ and } \hat{H} = \begin{pmatrix}h_{11} & 0 \\ 0 & h_{22}\end{pmatrix}
\end{equation*}

in this case we have the following

\begin{equation*}
    \begin{split}
        i\hbar\dv{}{t}\ket{\psi(t)} &= \hat{H}\ket{\psi(t)} \\
        \implies i\hbar\dv{}{t}\begin{pmatrix}\psi_1(t) \\ \psi_2(t)\end{pmatrix} &= \begin{pmatrix}h_{11} & 0 \\ 0 & h_{22}\end{pmatrix}\begin{pmatrix}\psi_1(t) \\ \psi_2(t)\end{pmatrix} \\
        \implies \hbar\dv{}{t}\begin{pmatrix}\psi_1(t) \\ \psi_2(t)\end{pmatrix} &= \begin{pmatrix}h_{11}\psi_1(t) \\ h_{22}\psi_2(t)\end{pmatrix}
    \end{split}
\end{equation*}

that is, we have a system of equations $i\hbar\dv{}{t}\psi_j(t) = h_j\psi_j(t)$ with $j \in \set{1,2}$ that are clearly of the type $K\dv{f}{t} = Hf(t)$. Thus

\begin{equation*}
    \begin{split}
        i\hbar\dv{}{t}\ket{\psi(t)} &= \hat{H}\ket{\psi(t)} \\
        \implies \dv{}{t}\ket{\psi(t)} &= \frac{-i}{\hbar}\hat{H}\ket{\psi(t)} \\
        \implies \ket{\psi(t)} &= e^{-\frac{i}{\hbar}(t-t_0)\hat{H}}\ket{\psi(t_0)}
    \end{split}
\end{equation*}

where the initial condition is the vector $\ket{\psi(t_0)}$.

Being $\hat{H}$ diagonal, the exponential of this matrix is again a diagonal matrix whose entries on the diagonal are the exponential of each of the entries on the diagonal of $\hat{H}$. We can see this in the following way. Let

\begin{equation*}
    \hat{H} = \begin{pmatrix}
        h_{11} & 0 & \cdots & 0 \\ 0 & h_{22} & \cdots & 0 \\ \vdots & \vdots & \cdots & \vdots \\ 0 & 0 & \cdots & h_{mm}
    \end{pmatrix}
    \implies
    \hat{H}^n = \begin{pmatrix}
        h_{11}^n & 0 & \cdots & 0 \\ 0 & h_{22}^n & \cdots & 0 \\ \vdots & \vdots & \cdots & \vdots \\ 0 & 0 & \cdots & h_{mm}^n
    \end{pmatrix}
\end{equation*}

Let's calculate $e^{\hat{H}}$ according to the definition

\begin{equation*}
    \begin{split}
        e^{\hat{H}} &= \sum_{n=0}^\infty \frac{\hat{H}^{n}}{n!} \\
        &= \sum_{n=0}^\infty \frac{1}{n!} \begin{pmatrix}
            h_{11}^n & 0 & \cdots & 0 \\ 0 & h_{22}^n & \cdots & 0 \\ \vdots & \vdots & \cdots & \vdots \\ 0 & 0 & \cdots & h_{mm}^n
        \end{pmatrix} \\
        &= \begin{pmatrix}
            \sum_{n=0}^\infty \frac{h_{11}^n}{n!} & 0 & \cdots & 0 \\ 0 & \sum_{n=0}^\infty \frac{h_{22}^n}{n!} & \cdots & 0 \\ \vdots & \vdots & \cdots & \vdots \\ 0 & 0 & \cdots & \sum_{n=0}^\infty \frac{h_{mm}^n}{n!}
        \end{pmatrix}
    \end{split}
\end{equation*}

But let us recall that $\sum_{n=0}^\infty \frac{z^n}{n!}$ is the expression in _Taylor series_ of $e^{z}$, then we have

\begin{equation*}
    e^{\hat{H}} = \begin{pmatrix}
        e^{h_{11}} & 0 & \cdots & 0 \\ 0 & e^{h_{22}} & \cdots & 0 \\ \vdots & \vdots & \cdots & \vdots \\ 0 & 0 & \cdots & e^{h_{mm}}
    \end{pmatrix}
\end{equation*}

In summary, we can conclude that

\begin{equation}\label{temporal2}
    \ket{\psi(t)} = e^{-\frac{i}{\hbar}(t-t_0)\hat{H}}\ket{\psi(t_0)}
\end{equation}

and therefore the operator

\begin{equation*}
    \hat{T}(t,t_0) = e^{-\frac{i}{\hbar}(t-t_0)\hat{H}}
\end{equation*}

is the time evolution operator we are looking for.

### Proposition 3.5

Let $\hat{H}\ket{E} = E\ket{E}$, where $\ket{E}$ is an eigenvector with eigenvalue $E$, and $f$ an analytic function. Then

\begin{equation*}
    f(\hat{H})\ket{E} = f(E)\ket{E}
\end{equation*}

**Proof:**

Since $f$ is analytical we can express it in terms of its Taylor series, and for simplicity we are going to suppose that the expansion can be done around $0$, that is

\begin{equation*}
    \begin{split}
        f(\hat{H}) &= \sum_{j=0}^\infty \frac{f^{(n)}(0)}{j!}\hat{H}^n \\
        \implies f(\hat{H})\ket{E} &= \sum_{j=0}^\infty \frac{f^{(n)}(0)}{j!}\hat{H}^n\ket{E} \\
        &= \sum_{j=0}^\infty \frac{f^{(n)}(0)}{j!}E^n\ket{E} = f(E)\ket{E}
    \end{split}
\end{equation*}

This result will be particularly useful when we take $f$ as the exponential. Since, if we want to apply the exponential of an operator to any ket, we can write this vector in terms of the eigenvectors of the operator that we have in the exponential and in this way, it is enough to apply the exponential to the eigenvalues of the operator.


# Classical Image Processing

In this tutorial we will review some existing classical image processing algorithms.

Image processing has been around since the mid 20th century. Since its early beginnings it has been expanded from simple image enhancement techniques to pattern recognition, transformation, and machine learning. Its uses span many domains such as medical imaging, cryptanalysis, gaming, weather, construction, and traffic analysis.
In this section we will review a few of these classical techniques to serve as a brief introduction to the various image processing techniques and will help understand the differences as to how images are represented and transformed.

We will be using the **skimage** library to perform a lot of the image processing for us. But first, let's discuss a bit about how images are digitally represented and rendered.

An image is made up of pixels, where each pixel represents as either binary, grayscale, or color. An image can contain any number of pixels and in any configuration. An image can be represented as a square nxn image, or an nxm image, where n and m are integers representing the number of rows and columns of the image.

A binary image is represented by a single bit for each pixel, where the bit has a value of either 0 or 1. Where 0 represents the color Black, and 1 represents the color White.

A grayscale image is represented by 8 bits for each pixel, where the bits have gray scaled values between 0 and 255. Where 0 is Black and 255 is White, and all the colors in between intensify the grayscale values as the value increases.

In the following sections we will perform some common operations to various binary, grayscale, and color images. The operations will be as simple as just reading in a image and converting it from color to grayscale, and other operations such as feature and edge detection. For simplicity we will be using the skiimage library to perform some of these tasks for us.

## Contents
1. Reading and Transforming an Image
    1.1 Converting From Color to Grayscale
    1.2 Blurring
    1.3 Resizing
2. Edge Detection
    2.1 Canny
    2.2 Roberts
    2.3 Sobel
3. Thresholding

## 1. Reading and Transforming an Image
Reading in an image is as simple as reading in a text file. In these example we'll read in various sample images included in the **skimage** library.
In this example we will read in a color image and display it using Python pyplot.

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

# Load skimage library
from skimage import data

# Other sample images available: 'hubble_deep_field', 'astronaut', 'retina'
image_name = 'immunohistochemistry'

# Load image
caller = getattr(data, image_name)
image = caller()

# Plot and display image
plt.figure()
plt.title(image_name)
plt.imshow(image)
plt.show()
```

### 1.1 Convert a Color Image to Grayscale
There are many ways to convert an image from color to grayscale. Since grayscale colors are generally stored as 8-bit values and color images are stored as 24 bit values (8-bits per color - Red, Green, and Blue), this means that the conversion must reduce to the 8-bit grayscale representation. The general way to do this is by simply averaging the three color values:

Grayscale image = $\frac{Red(8bit) + Green(8bit) + Blue(8bit)}3$

This is commonly referred to as _downsampling_ an image.
Luckily for us we won't have to write our code to traverse each pixel as skimage already has a method, _rgb2gray_ , built to do that for us.

```python
from skimage.color import rgb2gray

# Convert color to grayscale image
grayscale_image = rgb2gray(image)

# Plot and show grayscaled image
plt.figure()
plt.title('Grayscale image')
plt.imshow(grayscale_image, cmap=plt.cm.gray)
plt.show()
```

### 1.2 Tinting an Image
If you ever need to highlight a portion of an image you can do so by tinting an image by scaling the color values of each pixel to various degrees of color. In the folloiwing example we will scale the color image by defining the color weight to apply, in this case we will apply a pure red so we will clear out the blue and green values.

**Note** The color values are oredered [Red, Green, Blue].

```python
# Tint images
from skimage import color
from skimage import img_as_float

# Define the color weight scale to red, [Red, Green, Blue]
red_tinting = [1, 0, 0]

# Plot and show image
plt.figure()
plt.title('Red tinted image')
plt.imshow(red_tinting * image)
plt.show()
```

## 2. Image Edge Detection
One of the most commonly used feature in image processing is edge detection. Detecting and extracting the edges of an image can be used identify and segment objects within an image. Use of this feature can also be found in areas outside of image processing such as computer vision and machine learning.

The following are some of the more popular edge detection techniques available.

### 2.1 Canny Edge Detector
The Canny edge detector is an image operator that performs multiple stages to maximize the detection of edges while providing a means to adjust the operator so the results can be fine tuned as needed.
The Canny edge detector steps are described in [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.3300&rep=rep1&type=pdf] and [http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.476.5736&rep=rep1&type=pdf]

Generally the steps as described in the skimage Canny implementation [https://scikit-image.org/docs/stable/auto_examples/edges/plot_canny.html] are as follows:
1. Apply Gaussian filter to blur the image. This will smooth the edges to easily detect edges by reducing the noise in the image.
2. Thinning of the edges down to by removing non-maximum pixels of the image gradient.
3. Apply a threshold to remove using hysteresis on the graudient magnitude

In the following example we will use the Canny filter to detect the edges of our grayscaled image.

```python
from scipy import ndimage as ndi

from skimage import feature

# Compute the Canny filter
canny = feature.canny(grayscale_image, sigma=5)

# Plot and show the image
plt.figure()
plt.title('Canny edge detection results')
plt.imshow(canny, cmap=plt.cm.gray)
plt.show()
```

### 2.2 Roberts Cross Edge Detection
This is used to detect the edge of an image using a diagonal mask which is used to approximate the gradient by computing the sum of the squares and the differences between diagonal pixels.
skimage implements a Roberts Cross edge detector by providing it a 2D image and it returns a 2D map array of the Roberts' cross edge. The convolution matrices used to implement Roberts' cross filter are as follows:

$
S_{left} =
\begin{bmatrix}
+1 & 0 \\
0 & -1
\end{bmatrix}$

$
S_{right} =
\begin{bmatrix}
0 & +1 \\
-1 & 0
\end{bmatrix}$

```python
# Import the filter library
from skimage import filters

# Load the test image
caller = getattr(data, 'moon')
image = caller()
grayscale_image = rgb2gray(image)

# Plot and show the original image
plt.title('Original Moon image')
plt.imshow(grayscale_image, cmap=plt.cm.gray)
plt.show()
```

```python
# Apply the Roberts cross filter
roberts_filter = filters.roberts(grayscale_image)

# Plot and show image
plt.title('Roberts cross filter')
plt.imshow(roberts_filter, cmap=plt.cm.gray)
plt.show()
```

### 2.3 Sobel Edge Detection
The Sobel filter, similar to Roberts cross filter above is used to detect edges, however Sobel uses two 3x3 kernels to approximate the derivates of the horizontal and vertical changes.  A standard example of Sobel's filter are as follows for the vertical and horizontal, respectively.

$
S_{vertical} =
\begin{bmatrix}
+1 & 0 & -1 \\
+2 & 0 & -2 \\
+1 & 0 & -1
\end{bmatrix}$

$
S_{horizontal} =
\begin{bmatrix}
+1 & +2 & +1 \\
0 & 0 & 0 \\
-1 & -2 & -1
\end{bmatrix}$

```python
# Apply Sobel filter to the grayscale image
sobel_filter = filters.sobel(grayscale_image)

# Plot and show image
plt.title('Sobel filter')
plt.imshow(sobel_filter, cmap=plt.cm.gray)
plt.show()
```

## 3. Thresholding
Thresholding an image is a way to convert a grayscale image to a binary image.
The skimage implements the Otsu's method of thresholding [https://en.wikipedia.org/wiki/Otsu's_method] which returns threshold value given a grayscaled image.
The image is then converted into a binary image by classifying all pixels into one of the two binary states 0 or 1. The classification of each pixel to one of these two states is determined by the threshold value.

Therefore, any pixel value less than the threshold value is set to 0, all others pixel values are set to 1.

$
\begin{equation}
  \text{Pixel value} =
  \begin{cases}
    0 & \text{if pixel value is less than threshold} \\
    1 & \text{otherwise}
  \end{cases}
\end{equation}$

In this next example we will obtain the threshold value using Otsu's method and apply that threshold value to a grayscaled image in order to covert it to a binary image.

```python
# Load grayscaled image
image = data.camera()
grayscale_image = rgb2gray(image)

plt.figure()
plt.title('Original')
plt.imshow(image, cmap=plt.cm.gray)
plt.show()
```

```python
# import Otsu's threshold method
from skimage.filters import threshold_otsu

# Obtain threshold value
tr = threshold_otsu(grayscale_image)

# Apply threshold to convert to binary
binary_image = grayscale_image > tr

plt.figure()
plt.title('Binary image')
plt.imshow(binary_image, cmap=plt.cm.gray)
plt.show()
```


# Training a model on forces and energies

In addition to the energy, machine learning models can also be used to model molecular forces.
These are $N_\mathrm{atoms} \times 3$ arrays describing the Cartesian force acting on each atom due to the overall
(potential) energy. They are formally defined as the negative gradient of the energy $E_\mathrm{pot}$ with respect to
the nuclear positions $\mathbf{R}$

\begin{equation}
\mathbf{F}^{(\alpha)} = -\frac{\partial E_\mathrm{pot}}{\partial \mathbf{R}^{(\alpha)}},
\end{equation}

where $\alpha$ is the index of the nucleus.

The above expression offers a straightforward way to include forces in machine learning models by simply defining a
model for the energy and taking the appropriate derivatives.
The resulting model can directly be trained on energies and forces.
Moreover, in this manner energy conservation and the correct behaviour under rotations of the molecule is guaranteed.

Using forces in addition to energies to construct a machine learning model offers several advantages.
Accurate force predictions are important for molecular dynamics simulations, which will be covered in the subsequent
tutorial. Forces also encode a greater wealth of information than the energies.
For every molecule, only one energy is present, while there are $3N_\mathrm{atoms}$ force entries.
This property, combined with the fact that reference forces can be computed at the same cost as energies, makes models
trained on forces and energies very data efficient.

In the following, we will show how to train such force models and how to use them in practical applications.

## Preparing the data

The process of preparing the data is similar to the tutorial on [QM9](tutorial_02_qm9.ipynb). We begin by importing all
relevant packages and generating a directory for the tutorial experiments.

```python
import torch
import torchmetrics
import schnetpack as spk
import schnetpack.transform as trn
import pytorch_lightning as pl
import os
import matplotlib.pyplot as plt
import numpy as np

forcetut = './forcetut'
if not os.path.exists(forcetut):
    os.makedirs(forcetut)
```

Next, the data needs to be loaded from a suitable dataset.
For convenience, we use the MD17 dataset class provided in SchNetPack, which automatically downloads and builds suitable
databases containing energies and forces for a range of small organic molecules.
In this case, we use the ethanol molecule as an example.

```python
from schnetpack.datasets import MD17

ethanol_data = MD17(
    os.path.join(forcetut,'ethanol.db'),
    molecule='ethanol',
    batch_size=10,
    num_train=1000,
    num_val=1000,
    transforms=[
        trn.ASENeighborList(cutoff=5.),
        trn.RemoveOffsets(MD17.energy, remove_mean=True, remove_atomrefs=False),
        trn.CastTo32()
    ],
    num_workers=1,
    pin_memory=True, # set to false, when not using a GPU
)
ethanol_data.prepare_data()
ethanol_data.setup()
```

    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:73: LightningDeprecationWarning: DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7.
      rank_zero_deprecation(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:77: LightningDeprecationWarning: DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7.
      rank_zero_deprecation(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:81: LightningDeprecationWarning: DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7.
      rank_zero_deprecation(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:114: LightningDeprecationWarning: DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7.
      rank_zero_deprecation(
    100%|██████████| 100/100 [00:12<00:00,  7.80it/s]
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:133: LightningDeprecationWarning: DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7.
      rank_zero_deprecation(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:152: LightningDeprecationWarning: DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7.
      rank_zero_deprecation(

The data is split into training (1000 points), validation (1000 points) and test set (remainder).
Once again, we subtract the mean of the energies in the training data with a preprocessing transform to precondition
our model. This only needs to be done for the energies, since the forces are obtained as derivatives and automatically
capture the scale of the data. The subtraction of atomic reference energies is not necessary here, since only molecules
of the same composition are used.

For custom datasets, the data would have to be loaded via the SchNetPack `ASEAtomsData` and `AtomsDataModule` classes
(see tutorial on [data preparation](tutorial_01_preparing_data.ipynb)). In this case, one needs to make sure that the
naming of properties is kept consistent with the config. The `schnetpack.properties` module provides standard names
for a wide range of properties. Here, we use the definitions provided with the `MD17` class.

In order to train force models, forces need to be included in the reference data.
Once the dataset has been loaded, this can be checked as follows:

```python
properties = ethanol_data.dataset[0]
print('Loaded properties:\n', *['{:s}\n'.format(i) for i in properties.keys()])
```

    Loaded properties:
     _idx
     energy
     forces
     _n_atoms
     _atomic_numbers
     _positions
     _cell
     _pbc

As can be seen, `energy` and `forces` are included in the properties dictionary. To have a look at the `forces` array
and check whether it has the expected dimensions, we can call:

```python
print('Forces:\n', properties[MD17.forces])
print('Shape:\n', properties[MD17.forces].shape)

```

    Forces:
     tensor([[ 1.4517e+00,  6.0192e+00,  5.2068e-07],
            [ 1.7953e+01, -5.1624e+00,  3.4900e-07],
            [-4.0884e+00,  2.2590e+01,  3.3088e-06],
            [-1.1416e+00, -9.7469e+00,  7.6473e+00],
            [-1.1416e+00, -9.7469e+00, -7.6473e+00],
            [-2.4821e+00,  4.9335e+00,  4.3700e+00],
            [-2.4821e+00,  4.9335e+00, -4.3700e+00],
            [-5.5148e+00, -3.0207e+00, -8.9093e-09],
            [-2.4393e+00, -1.0838e+01, -6.0721e-08]], dtype=torch.float64)
    Shape:
     torch.Size([9, 3])

## Building the model

After having prepared the data in the above way, we can now build and train the force model.
This is done in the same three steps as described in [QM9 tutorial](tutorial_02_qm9.ipynb):

1. Defining input modules
2. Building the representation
3. Defining an output module

For the representation we can use the same layers as in the previous tutorial:

```python
cutoff = 5.
n_atom_basis = 30

pairwise_distance = spk.atomistic.PairwiseDistances() # calculates pairwise distances between atoms
radial_basis = spk.nn.GaussianRBF(n_rbf=20, cutoff=cutoff)
schnet = spk.representation.SchNet(
    n_atom_basis=n_atom_basis, n_interactions=3,
    radial_basis=radial_basis,
    cutoff_fn=spk.nn.CosineCutoff(cutoff)
)
```

Since we want to model forces, we need an additional output module. We will still use the Atomwise to predict the
energy. However, since the forces should be described as the derivative of the energy, we have to indicate that the
corresponding derviative of the model should be computed.

This is done with the ``Forces`` module, which computes the negative derivative of the energy
 (specified by the supplied ``energy_key``) with respect to the atom positions.

```python
pred_energy = spk.atomistic.Atomwise(n_in=n_atom_basis, output_key=MD17.energy)
pred_forces = spk.atomistic.Forces(energy_key=MD17.energy, force_key=MD17.forces)
```

The input, representation and output modules are then assembled to the neural network potential:

```python
nnpot = spk.model.NeuralNetworkPotential(
    representation=schnet,
    input_modules=[pairwise_distance],
    output_modules=[pred_energy, pred_forces],
    postprocessors=[
        trn.CastTo64(),
        trn.AddOffsets(MD17.energy, add_mean=True, add_atomrefs=False)
    ]
)
```

## Training the model

Before we can train the model, the training task has to be defined, including the model, loss functions and
optimizers. First, the outputs of the models are connected to their respective loss functions using `ModelOutput`.
To train the model on energies and forces, we will use a combined loss function:

\begin{equation}
\mathcal{L}(E_\mathrm{ref},\mathbf{F}_\mathrm{ref},E_\mathrm{pred}, \mathbf{F}_\mathrm{pred}) = \frac{1}{n_\text{train}} \sum_{n=1}^{n_\text{train}} \left[  \rho_1 \left( E_\mathrm{ref} - E_\mathrm{pred} \right)^2 + \frac{\rho_2}{3N_\mathrm{atoms}} \sum^{N_\mathrm{atoms}}_\alpha \left\| \mathbf{F}_\mathrm{ref}^{(\alpha)} - \mathbf{F}_\mathrm{pred}^{(\alpha)} \right\|^2 \right],
\end{equation}

where we take the predicted forces to be:

\begin{equation}
\mathbf{F}_\mathrm{pred}^{(\alpha)} = -\frac{\partial E_\mathrm{pred}}{\partial \mathbf{R}^{(\alpha)}}.
\end{equation}

We have introduced the loss weights $\rho_i$ in order to control the tradeoff between energy and force loss.
In SchNetPack, we can implement such a weighted loss function by setting the loss weights of `ModelOutput`:

```python
output_energy = spk.task.ModelOutput(
    name=MD17.energy,
    loss_fn=torch.nn.MSELoss(),
    loss_weight=0.01,
    metrics={
        "MAE": torchmetrics.MeanAbsoluteError()
    }
)

output_forces = spk.task.ModelOutput(
    name=MD17.forces,
    loss_fn=torch.nn.MSELoss(),
    loss_weight=0.99,
    metrics={
        "MAE": torchmetrics.MeanAbsoluteError()
    }
)
```

Now, the training task can be assembled as in the last tutorial:

```python
task = spk.task.AtomisticTask(
    model=nnpot,
    outputs=[output_energy, output_forces],
    optimizer_cls=torch.optim.AdamW,
    optimizer_args={"lr": 1e-4}
)
```

Finally, we train the model using the PyTorch Lightning `Trainer` for 5 epochs.

```python
logger = pl.loggers.TensorBoardLogger(save_dir=forcetut)
callbacks = [
    spk.train.ModelCheckpoint(
        inference_path=os.path.join(forcetut, "best_inference_model"),
        save_top_k=1,
        monitor="val_loss"
    )
]

trainer = pl.Trainer(
    callbacks=callbacks,
    logger=logger,
    default_root_dir=forcetut,
    max_epochs=5, # for testing, we restrict the number of epochs
)
trainer.fit(task, datamodule=ethanol_data)
```

    GPU available: True, used: False
    TPU available: False, using: 0 TPU cores
    IPU available: False, using: 0 IPUs
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/trainer/trainer.py:1582: UserWarning: GPU available but not used. Set the gpus flag in your trainer `Trainer(gpus=1)` or script `--gpus=1`.
      rank_zero_warn(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/core/datamodule.py:469: LightningDeprecationWarning: DataModule.setup has already been called, so it will not be called again. In v1.6 this behavior will change to always call DataModule.setup.
      rank_zero_deprecation(

      | Name    | Type                   | Params
    ---------------------------------------------------
    0 | model   | NeuralNetworkPotential | 16.4 K
    1 | outputs | ModuleList             | 0
    ---------------------------------------------------
    16.4 K    Trainable params
    0         Non-trainable params
    16.4 K    Total params
    0.066     Total estimated model params size (MB)

    Validation sanity check: 0it [00:00, ?it/s]

    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/trainer/data_loading.py:116: UserWarning: The dataloader, val_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 8 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.
      rank_zero_warn(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 10. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.
      warning_cache.warn(
    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/pytorch_lightning/trainer/data_loading.py:116: UserWarning: The dataloader, train_dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 8 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.
      rank_zero_warn(

    Training: 0it [00:00, ?it/s]

    Validating: 0it [00:00, ?it/s]

    Validating: 0it [00:00, ?it/s]

    Validating: 0it [00:00, ?it/s]

    Validating: 0it [00:00, ?it/s]

    Validating: 0it [00:00, ?it/s]

Training will produce several files in the `model_path` directory, which is `forcetut` in our case.
The split is stored in `split.npz`.
Checkpoints are written to `checkpoints` periodically, which can be used to restart training.
A copy of the best model is stored as `best_inference_model`, which can directly be accessed using the `torch.load`
function.

You can have a look at the log using Tensorboard:
```
tensorboard --logdir=forcetut/default
```

It should be noted that the model trained here is used exclusively for demonstrative purposes. Accordingly, its size
and the training time have been reduced significantly. This puts strong constraints on the accuracy that can be
obtained. For practical applications, one would e.g. increase the number of features, the interaction layers, the
learning rate schedule and train until convergence (removing the `n_epochs` keyword from the `trainer`).
To quickly get started training state-of-the-art models, have a look at the command line interface,
which comes with a series of pre-built configurations.

## Using the model

Since all models in SchNetPack are stored in the same way, we can use the trained force model in exactly the same manner
as described in the [QM9 tutorial](tutorial_02_qm9.ipynb). To load the model stored in the `best_inference_model` file,
we use the `torch.load` function. It will automatically be moved to the device it was trained on.

```python
best_model = torch.load(os.path.join(forcetut, 'best_inference_model'))
```

## Interface to ASE

As was shown in the [QM9 tutorial](tutorial_02_qm9.ipynb), we use the `AtomsConverter` to directly operate on
ASE atoms objects.
Having access to molecular forces also makes it possible to perform a variety of different simulations.
The `SpkCalculator` offers a simple way to perform all computations available in the ASE package.
Below, we create an ASE calculator from the trained model and the previously generated `atoms` object
(see [Preparing the data](#Preparing-the-data)).
One important point is, that the MD17 dataset uses kcal/mol and kcal/mol/Å as units for energies and forces.
For the ASE interface, these need to be converted to the standard internal ASE units eV and eV/Å.
Therefore, we need to pass the units of the energies and forces predicted by the model to the calculator.

```python
from ase import Atoms

converter = spk.interfaces.AtomsConverter(neighbor_list=trn.ASENeighborList(cutoff=5.), dtype=torch.float32)

calculator = spk.interfaces.SpkCalculator(
    model=best_model,
    converter=converter,
    energy=MD17.energy,
    forces=MD17.forces,
    energy_units='kcal/mol',
    forces_units='kcal/mol/Ang'
)

structure = ethanol_data.test_dataset[0]
atoms = Atoms(numbers=structure[spk.properties.Z], positions=structure[spk.properties.R])
atoms.set_calculator(calculator)

print('Prediction:')
print('energy:', atoms.get_total_energy())
print('forces:', atoms.get_forces())
```

    Prediction:
    energy: -4214.8652794460095
    forces: [[ 3.56254085  1.82367757  1.55830998]
     [ 0.99315922 -0.86904191 -1.50720566]
     [ 0.03677444 -0.65425532 -0.1600646 ]
     [-1.86923201  1.35359331 -0.7541254 ]
     [ 0.55044083 -1.28889362 -1.43034696]
     [-1.82220995  0.88389853 -0.10380705]
     [-1.17435485 -0.84933002  1.70828506]
     [ 0.18244644 -0.75103004 -0.51986529]
     [-0.4595651   0.35138193  1.20882014]]

Among the simulations which can be done by using ASE and a force model are geometry optimisation,
normal mode analysis and simple molecular dynamics simulations.

The `AseInterface` of SchNetPack offers a convenient way to perform basic versions of these computations.
Only a file specifying the geometry of the molecule and a pretrained model are needed.

We will first generate a XYZ file containing an ethanol configuration:

```python
from ase import io

# Generate a directory for the ASE computations
ase_dir = os.path.join(forcetut, 'ase_calcs')

if not os.path.exists(ase_dir):
    os.mkdir(ase_dir)

# Write a sample molecule
molecule_path = os.path.join(ase_dir, 'ethanol.xyz')
io.write(molecule_path, atoms, format='xyz')
```

The `AseInterface` is initialized by passing the path to the molecule, the model and a computation directory.
In addition, the computation device for the force model and how energies and forces are called in the output,
as well as their units, need to be provided.

```python
ethanol_ase = spk.interfaces.AseInterface(
    molecule_path,
    ase_dir,
    best_model,
    converter=converter,
    energy=MD17.energy,
    forces=MD17.forces,
    energy_units='kcal/mol',
    forces_units='kcal/mol/Ang'
)
```

### Geometry optimization

For some applications it is neccessary to relax a molecule to an energy minimum.
In order to perform this optimization of the molecular geometry, we can simply call

```python
ethanol_ase.optimize(fmax=1e-2)
```

                    Step[ FC]     Time          Energy          fmax
    BFGSLineSearch:    0[  0] 17:46:06    -4214.865279        4.2949
    BFGSLineSearch:    1[  2] 17:46:06    -4215.271684        1.6134
    BFGSLineSearch:    2[  4] 17:46:07    -4215.343626        0.6966
    BFGSLineSearch:    3[  6] 17:46:07    -4215.392868        1.0173
    BFGSLineSearch:    4[  8] 17:46:07    -4215.460684        0.5116
    BFGSLineSearch:    5[ 10] 17:46:07    -4215.483194        0.6698
    BFGSLineSearch:    6[ 12] 17:46:07    -4215.522721        0.6229
    BFGSLineSearch:    7[ 14] 17:46:07    -4215.549195        0.5787
    BFGSLineSearch:    8[ 16] 17:46:07    -4215.558430        0.4029
    BFGSLineSearch:    9[ 17] 17:46:07    -4215.575628        0.3189
    BFGSLineSearch:   10[ 18] 17:46:07    -4215.584439        0.6328
    BFGSLineSearch:   11[ 20] 17:46:08    -4215.594228        0.5697
    BFGSLineSearch:   12[ 21] 17:46:08    -4215.600722        0.3565
    BFGSLineSearch:   13[ 23] 17:46:08    -4215.609511        0.3455
    BFGSLineSearch:   14[ 25] 17:46:08    -4215.616081        0.3602
    BFGSLineSearch:   15[ 27] 17:46:08    -4215.626357        0.5558
    BFGSLineSearch:   16[ 28] 17:46:08    -4215.631435        0.2492
    BFGSLineSearch:   17[ 29] 17:46:08    -4215.640436        0.1171
    BFGSLineSearch:   18[ 30] 17:46:08    -4215.644778        0.3833
    BFGSLineSearch:   19[ 32] 17:46:09    -4215.652222        0.1719
    BFGSLineSearch:   20[ 33] 17:46:09    -4215.657605        0.1314
    BFGSLineSearch:   21[ 34] 17:46:09    -4215.660171        0.0953
    BFGSLineSearch:   22[ 35] 17:46:09    -4215.661052        0.0463
    BFGSLineSearch:   23[ 36] 17:46:09    -4215.661293        0.0224
    BFGSLineSearch:   24[ 37] 17:46:09    -4215.661417        0.0287
    BFGSLineSearch:   25[ 39] 17:46:09    -4215.661642        0.0293
    BFGSLineSearch:   26[ 41] 17:46:09    -4215.661877        0.0568
    BFGSLineSearch:   27[ 43] 17:46:09    -4215.662069        0.0136
    BFGSLineSearch:   28[ 44] 17:46:09    -4215.662133        0.0201
    BFGSLineSearch:   29[ 45] 17:46:10    -4215.662158        0.0216
    BFGSLineSearch:   30[ 48] 17:46:10    -4215.662186        0.0109
    BFGSLineSearch:   31[ 50] 17:46:10    -4215.662251        0.0141
    BFGSLineSearch:   32[ 53] 17:46:10    -4215.662341        0.0200
    BFGSLineSearch:   33[ 55] 17:46:10    -4215.662424        0.0172
    BFGSLineSearch:   34[ 56] 17:46:10    -4215.662439        0.0032

Since we trained only a reduced model, the accuracy of energies and forces is not optimal and several steps are
needed to optimize the geometry.

### Normal mode analysis

Once the geometry was optimized, normal mode frequencies can be obtained from the Hessian (matrix of second derivatives)
of the molecule. The Hessian is a measure of the curvature of the potential energy surface and normal mode frequencies
are useful for determining, whether an optimization has reached a minimum. Using the `AseInterface`, normal mode
frequencies can be obtained via:

```python
ethanol_ase.compute_normal_modes()
```

    ---------------------
      #    meV     cm^-1
    ---------------------
      0    1.4i     11.6i
      1    0.8i      6.5i
      2    0.6i      4.9i
      3    0.1i      0.5i
      4    0.1       0.7
      5    0.1       0.9
      6   14.5     117.1
      7   36.0     290.2
      8   38.1     307.3
      9   55.8     449.7
     10   82.5     665.7
     11   95.6     770.8
     12  100.6     811.4
     13  109.9     886.7
     14  111.5     899.1
     15  124.6    1005.2
     16  136.7    1102.8
     17  154.4    1245.3
     18  160.1    1291.7
     19  163.2    1316.2
     20  166.6    1343.6
     21  365.4    2947.4
     22  370.3    2986.8
     23  373.1    3009.1
     24  375.7    3030.5
     25  387.9    3129.0
     26  406.7    3280.6
    ---------------------
    Zero-point energy: 1.915 eV

Imaginary frequencies indicate, that the geometry optimisation has not yet reached a minimum.
The `AseInterface` also creates an `normal_modes.xyz` file which can be used to visualize the vibrations with jmol.

### Molecular dynamics

Finally, it is also possible to basic run molecular dynamics simulations using this interface.
To do so, we first need to prepare the system, where we specify the simulation file.
This routine automatically initializes the velocities of the atoms to a random number corresponding to a certain average
kinetic energy.

```python
ethanol_ase.init_md(
    'simulation'
)
```

    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/ase/md/md.py:48: FutureWarning: Specify the temperature in K using the 'temperature_K' argument
      warnings.warn(FutureWarning(w))

The actual simulation is performed by calling the function `run_md` with a certain number of steps:

```python
ethanol_ase.run_md(1000)
```

During simulation, energies and geometries are logged to `simulation.log` and `simulation.traj`, respectively.

We can for example visualize the evolution of the systems total and potential energies as

```python
# Load logged results
results = np.loadtxt(os.path.join(ase_dir, 'simulation.log'), skiprows=1)

# Determine time axis
time = results[:,0]

# Load energies
energy_tot = results[:,1]
energy_pot = results[:,2]
energy_kin = results[:,3]

# Construct figure
plt.figure(figsize=(14,6))

# Plot energies
plt.subplot(2,1,1)
plt.plot(time, energy_tot, label='Total energy')
plt.plot(time, energy_pot, label='Potential energy')
plt.ylabel('E [eV]')
plt.legend()

plt.subplot(2,1,2)
plt.plot(time, energy_kin, label='Kinetic energy')
plt.ylabel('E [eV]')
plt.xlabel('Time [ps]')
plt.legend()

temperature = results[:,4]
print('Average temperature: {:10.2f} K'.format(np.mean(temperature)))

plt.show()
```

As can be seen, the potential and kinetic energies fluctuate, while the total energy (sum of potential and kinetic
energy) remains approximately constant. This is a good demonstration for the energy conservation obtained by modeling
forces as energy derivatives. Unfortunately, this also means that energy conservation is not a sufficient measure for
the quality of the potential.

However, frequently one is interested in simulations where the system is coupled to an external heat bath.
This is the same as saying that we wish to keep the average kinetic energy of the system and hence temperature close to a
certain value. Currently, the average temperature only depends on the random velocities drawn during the initialization
of the dynamics. Keeping a constant temperature average can be achieved by using a so-called thermostat.
In the `AseInterface`, simulations with a thermostat (to be precise a Langevin thermostat) can be carried out by
providing the `temp_bath` keyword. A simulation with e.g. the target temperature of 300K is performed via:

```python
ethanol_ase.init_md(
    'simulation_300K',
    temp_bath=300,
    reset=True
)
ethanol_ase.run_md(20000)
```

    /home/kschuett/anaconda3/envs/spkdev/lib/python3.8/site-packages/ase/md/md.py:48: FutureWarning: Specify the temperature in K using the 'temperature_K' argument
      warnings.warn(FutureWarning(w))

We can now once again plot total and potential energies.
Instead of the kinetic energy, we plot the temperature (both quantities are directly related).

```python
# Load logged results
results = np.loadtxt(os.path.join(ase_dir, 'simulation_300K.log'), skiprows=1)

# Determine time axis
time = results[:,0]
#0.02585
# Load energies
energy_tot = results[:,1]
energy_pot = results[:,2]

# Construct figure
plt.figure(figsize=(14,6))

# Plot energies
plt.subplot(2,1,1)
plt.plot(time, energy_tot, label='Total energy')
plt.plot(time, energy_pot, label='Potential energy')
plt.ylabel('Energies [eV]')
plt.legend()

# Plot Temperature
temperature = results[:,4]

# Compute average temperature
print('Average temperature: {:10.2f} K'.format(np.mean(temperature)))

plt.subplot(2,1,2)
plt.plot(time, temperature, label='Simulation')
plt.ylabel('Temperature [K]')
plt.xlabel('Time [ps]')
plt.plot(time, np.ones_like(temperature)*300, label='Target')
plt.legend()
plt.show()
```

Since our molecule is now subjected to external influences via the thermostat the total energy is no longer conserved.
However, the simulation temperature now fluctuates near to the requested 300K.
This can also be seen by computing the temperature average over time, which is now close to the desired value in contrast to the previous simulation.

## Summary

In this tutorial, we have trained a SchNet model on energies and forces using the MD17 ethanol dataset as an example.
We have then evaluated the performance of the model and performed geometry optimisation, normal mode analysis and basic molecular dynamic simulations using the SchNetPack ASE interface.

While these simulations can already be useful for practical applications, SchNetPack also comes with its own molecular dynamics package.
This package makes it possible to run efficient simulations on GPU and also offers access to advanced techniques, such as ring polymer dynamics.
In the next tutorial, we will cover how to perform molecular dynamics simulations directly with SchNetPack.