Calculate Numerical Diffusivity
============================

This ipython notebook calculates the numerical diffusivity on a tracer, associated to a specific configuration of the MITgcm. In particular, t was developed to calculate the equavalent diffusivity $\kappa$, defined (here) as $\kappa = \kappa_{pres}+\kappa_{num}$, where $\kappa_{pres}$
is the prescibed or explicit tracer diffusivity one imposes on the model and $k_{num}$ is the additional diffusivity due to numerical truncation errors. Note that there are two $\kappa_{pres}$ and therefore two $\kappa$, one for the horizontal dimensions and one for the vertical one.

These calculations try to reproduce the method used by [1] Abernathy et al. 2010, [2] Hill et al. 2011[3], Leibensperger and Plumb, 2013 to determine the numerical diffusivity MITgcm Southern Ocean configurations [1,2] and a baroclinic flow simulation simulation [3].

The method goes as follows:

From the evolution equation for the variance of the tracer concentration in the model output

\begin{equation}
\frac{1}{2}\frac{\partial{\overline{q^{2}}}}{\partial{t}}=-\kappa_{h} \overline{|\nabla_h q|^2}-\kappa_{v} \overline{(\frac{\partial{q}}{\partial {z}})^{2}}
\end{equation}

one can fit by a least squares regression, suitable values of $\kappa_h$ and $\kappa_v$ that satisfy the equation.

```
#KRM
import os

import numpy as np

import matplotlib.pyplot as plt

from scipy.stats import nanmean

from math import *

%matplotlib inline

import numpy as np

import matplotlib.colors as mcolors

from netCDF4 import Dataset

import pylab as pl

from decimal import getcontext, Decimal, Inexact
```

```
#'''
#NAME
#    Custom Colormaps for Matplotlib
#PURPOSE
#    This program shows how to implement make_cmap which is a function that
#    generates a colorbar.  If you want to look at different color schemes,
#    check out https://kuler.adobe.com/create.
#PROGRAMMER(S)
#    Chris Slocum
#REVISION HISTORY
#    20130411 -- Initial version created
#    20140313 -- Small changes made and code posted online
#    20140320 -- Added the ability to set the position of each color
#'''

def make_cmap(colors, position=None, bit=False):
    #'''
    #make_cmap takes a list of tuples which contain RGB values. The RGB
    #values may either be in 8-bit [0 to 255] (in which bit must be set to
    #rue when called) or arithmetic [0 to 1] (default). make_cmap returns
    #a cmap with equally spaced colors.
    #Arrange your tuples so that the first color is the lowest value for the
    #colorbar and the last is the highest.
    #position contains values from 0 to 1 to dictate the location of each color.
    #'''
    import matplotlib as mpl
    import numpy as np
    bit_rgb = np.linspace(0,1,256)
    if position == None:
        position = np.linspace(0,1,len(colors))
    else:
        if len(position) != len(colors):
            sys.exit("position length must be the same as colors")
        elif position[0] != 0 or position[-1] != 1:
            sys.exit("position must start with 0 and end with 1")
    if bit:
        for i in range(len(colors)):
            colors[i] = (bit_rgb[colors[i][0]],
                         bit_rgb[colors[i][1]],
                         bit_rgb[colors[i][2]])
    cdict = {'red':[], 'green':[], 'blue':[]}
    for pos, color in zip(position, colors):
        cdict['red'].append((pos, color[0], color[0]))
        cdict['green'].append((pos, color[1], color[1]))
        cdict['blue'].append((pos, color[2], color[2]))

    cmap = mpl.colors.LinearSegmentedColormap('my_colormap',cdict,256)
    return cmap
```

```
# Get field from MITgcm netCDF output
#
# statefile : string with /path/to/state.0000000000.t001.nc
# fieldname : string with the variable name as written on the netCDF file ('Temp', 'S','Eta', etc.)
#
def getField(statefile, fieldname):

    StateOut = Dataset(statefile)

    Fld = StateOut.variables[fieldname][:]

    shFld = np.shape(Fld)

    if len(shFld) == 1:

        return Fld

    if len(shFld) == 2:

        Fld2 = np.reshape(Fld,(shFld[0],shFld[1])) # reshape to pcolor order
        return Fld2

    elif len(shFld) == 3:

        Fld2 = np.zeros((shFld[0],shFld[1],shFld[2]))
        Fld2 = np.reshape(Fld,(shFld[0],shFld[1],shFld[2])) # reshape to pcolor order
        return Fld2

    elif len(shFld) == 4:

        Fld2 = np.zeros((shFld[0],shFld[1],shFld[2],shFld[3]))
        Fld2 = np.reshape(Fld,(shFld[0],shFld[1],shFld[2],shFld[3])) # reshape to pcolor order
        return Fld2

    else:

        print (' Check size of field ')

```

```

def dsum(iterable):

    "Full precision summation using Decimal objects for intermediate values"
    # Transform (exactly) a float to m * 2 ** e where m and e are integers.
    # Convert (mant, exp) to a Decimal and add to the cumulative sum.
    # If the precision is too small for exact conversion and addition,
    # then retry with a larger precision.
    from decimal import getcontext, Decimal, Inexact
    getcontext().traps[Inexact] = True

    total = Decimal(0)
    for x in iterable:
        mant, exp = frexp(x)
        mant, exp = int(mant * 2.0 ** 53), exp-53
        while True:
            try:
                total += mant * Decimal(2) ** exp
                break
            except Inexact:
                getcontext().prec += 1
    return float(total)

```

Important input parameters:
------------------------

```
nx = 480  # number of x points in domain
ny = 120  # number of y points in domain
nz = 43   # number of z levels in domain
nt = 37   # number of output files (n time)
```

Load output filenames:

```
### Load output filenames ###

filename = '/ocean/kramosmu/MITgcm/CanyonUpwelling/480x360x43_UpwLinRampWind_4Tr_BCC/output_0001/state.0000000000.glob.nc'
StateOut = Dataset(filename)

filename2 = '/ocean/kramosmu/MITgcm/CanyonUpwelling/480x360x43_UpwLinRampWind_4Tr_BCC/output_0001/grid.glob.nc'
GridOut = Dataset(filename2)
#for dimobj in StateOut.variables.values():
#    print dimobj
filename3 = '/ocean/kramosmu/MITgcm/CanyonUpwelling/480x360x43_UpwLinRampWind_4Tr_BCC/output_0001/ptracers.0000000000.glob.nc'
PtracersOut = Dataset(filename3)
#for dimobj in PtracersOut.variables.values():
#    print dimobj

```

Make some plots

```
plt.rcParams.update({'font.size':13})

tracer1=getField(filename3,'O2')

tracer1msk = np.ma.masked_values(tracer1, 0)

MASK = np.ma.getmask(tracer1msk)

### PLOT TRACER 1 at zlev and time###

zlev =5
time =5

plt.figure(figsize=(10,7))

ax=plt.gca()
ax.set_axis_bgcolor((205/255.0, 201/255.0, 201/255.0))

plt.contourf(tracer1msk[time,zlev,:,:],30, cmap='rainbow')
cb = plt.colorbar()
cb.set_label('C', position=(0, 1),rotation=0)

z=StateOut.variables['Z']

plt.title("Tracer at z=%d m" % -z[zlev])

plt.figure(figsize=(10,7))

ax=plt.gca()
ax.set_axis_bgcolor((205/255.0, 201/255.0, 201/255.0))

plt.contourf(tracer1msk[time,:,:,60],30, cmap='rainbow')
plt.gca().invert_yaxis()
cb = plt.colorbar()
cb.set_label('C', position=(0, 1),rotation=0)

z=StateOut.variables['Z']

plt.title("Tracer at x=60 nx")

```

Calculate the volume of the domain
-------------------------------
The volume of a tracer cell (remember we have an Arakawa C grid, so this changes depending on which kind of cell we are thinking about) is given by

$V(i,j,k)=depth \times area = (hfacC(i,j,k)\times dRf(k)) \times rA(i,j) = (hfacC(i,j,k)\times dRf(k)) \times dXg(i,j) \times dYg(i,j)$,

where hfacC is the fraction of the cell that is open (not occupied with land). So, the total volume of the domain is

$\sum\limits_{i=1}^{nx}{\sum\limits_{j=1}^{ny}{\sum\limits_{k=1}^{nz}{(hfacC(i,j,k)\times dRf(k)) \times rA(i,j)}}}$

```
DomVolume = 0.0

hFacC = getField(filename2,'HFacC')
drF   = getField(filename2,'drF')
rAC   = getField(filename2,'rA')

for ii in np.arange(nx):

    for jj in np.arange(ny):

        for kk in np.arange(nz):

            DomVolume = DomVolume + hFacC[kk,jj,ii]*drF[kk]*rAC[jj,ii]

print('\n The total volume is %e m^3 \n' %DomVolume)

```

     The total volume is 4.205218e+12 m^3

1st Term: The volume-weighted average of the squared concentration
-------------------------------------------------------------
The first term in the variance evolution equation is $\frac{1}{2}\frac{\partial{\overline{q^{2}}}}{\partial{t}}$. Note that we care about the time derivative of the variance, so that the mean concentration that usually appears in the definition of variance will not play a role here, since it is constant in time (we are not putting in or letting out any tracer).

We are going to calculate $\overline{q^2}$, the volume-weighted average of the squared concentration, and then the time derivative of that using a centered difference scheme.

```
### Calculate the volume-weighted mean of q^2

q2mean = np.zeros((nt,1))

for tt in np.arange(nt):
    q2sum = 0.0
    for kk in np.arange(nz):

        for jj in np.arange(ny):

            q2sum = q2sum + np.sum(((hFacC[kk,jj,:]*drF[kk]*rAC[jj,:])/DomVolume)*(tracer1[tt,kk,jj,:])**2)

    q2mean[tt] = q2sum

    print('volume-weighted mean of q^2 nt=%d is %e  \n' %(tt, q2mean[tt]))

```

    volume-weighted mean of q^2 nt=0 is 4.748420e+03

    volume-weighted mean of q^2 nt=1 is 4.756569e+03

    volume-weighted mean of q^2 nt=2 is 4.752646e+03

    volume-weighted mean of q^2 nt=3 is 4.752232e+03

    volume-weighted mean of q^2 nt=4 is 4.754435e+03

    volume-weighted mean of q^2 nt=5 is 4.754446e+03

    volume-weighted mean of q^2 nt=6 is 4.753132e+03

    volume-weighted mean of q^2 nt=7 is 4.753143e+03

    volume-weighted mean of q^2 nt=8 is 4.753315e+03

    volume-weighted mean of q^2 nt=9 is 4.752507e+03

    volume-weighted mean of q^2 nt=10 is 4.751642e+03

    volume-weighted mean of q^2 nt=11 is 4.751041e+03

    volume-weighted mean of q^2 nt=12 is 4.750143e+03

    volume-weighted mean of q^2 nt=13 is 4.749022e+03

    volume-weighted mean of q^2 nt=14 is 4.747833e+03

    volume-weighted mean of q^2 nt=15 is 4.746476e+03

    volume-weighted mean of q^2 nt=16 is 4.744982e+03

    volume-weighted mean of q^2 nt=17 is 4.743395e+03

    volume-weighted mean of q^2 nt=18 is 4.741590e+03

    volume-weighted mean of q^2 nt=19 is 4.739640e+03

    volume-weighted mean of q^2 nt=20 is 4.737647e+03

    volume-weighted mean of q^2 nt=21 is 4.735496e+03

    volume-weighted mean of q^2 nt=22 is 4.733157e+03

    volume-weighted mean of q^2 nt=23 is 4.730762e+03

    volume-weighted mean of q^2 nt=24 is 4.728278e+03

    volume-weighted mean of q^2 nt=25 is 4.725636e+03

    volume-weighted mean of q^2 nt=26 is 4.722898e+03

    volume-weighted mean of q^2 nt=27 is 4.720092e+03

    volume-weighted mean of q^2 nt=28 is 4.717169e+03

    volume-weighted mean of q^2 nt=29 is 4.714144e+03

    volume-weighted mean of q^2 nt=30 is 4.711042e+03

    volume-weighted mean of q^2 nt=31 is 4.707858e+03

    volume-weighted mean of q^2 nt=32 is 4.704578e+03

    volume-weighted mean of q^2 nt=33 is 4.701229e+03

    volume-weighted mean of q^2 nt=34 is 4.697810e+03

    volume-weighted mean of q^2 nt=35 is 4.694310e+03

    volume-weighted mean of q^2 nt=36 is 4.690739e+03

```
### Calculate time derivative
dt = (3600.*12.) # half day

dqdt = np.zeros(nt-2)

for tt in np.arange(nt-2):

    dqdt[tt] = (q2mean[tt+2]-q2mean[tt])/(dt+dt) # approximation of time derivative of v-w mean q^2

```

2nd Term: The volume-weighted average of the squared horizontal gradient
------------------------------------------------------------------
The second term in the variance evolution equation is $-\kappa_{h} \overline{|\nabla_h q|^2}$. Next, we calculate the square of the horizontal gradient $|\nabla_h q|^2=(\frac{\partial{q}}{\partial{x}})^2+(\frac{\partial{q}}{\partial{y}})^2$.

Spatial derivatives are approximated using a centered-difference scheme.

```

### Calculate mean of (dq/dx)^2 + (dq/dy)^2)

dxG = getField(filename2,'dxG')
dyG = getField(filename2,'dyG')

qmeanDh = np.zeros((nt,1))
qmeanDx = np.zeros((nt,1))
qmeanDy = np.zeros((nt,1))

for tt in np.arange(nt):

    sumDx = 0.0

    tracer2dx = tracer1[tt,:,:,2:nx]
    tracer0dx = tracer1[tt,:,:,0:nx-2]
    tracerDerX = ((tracer2dx-tracer0dx)/(2.0*dxG[:,1:nx-1]))**2
    #print tracerDerX

    for kk in np.arange(nz):
        for jj in np.arange(ny):
            sumDx = sumDx + np.sum((hFacC[kk,jj,1:nx-1]*drF[kk]*rAC[jj,1:nx-1])*(tracerDerX[kk,jj,:]))
        #print(sumDx)

    qmeanDx[tt] = sumDx/DomVolume

    print('The v-w mean of (dq/dx)^2 at nt=%d is %e \n' % (tt,qmeanDx[tt]))

for tt in np.arange(nt):

    sumDy = 0.0

    tracer2dy = tracer1[tt,:,2:ny,:]
    tracer0dy = tracer1[tt,:,0:ny-2,:]
    tracerDerY = ((tracer2dy-tracer0dy)/(2.0*dyG[1:ny-1,:]))**2

    for kk in np.arange(nz):
        for ii in np.arange(nx):
            sumDy = sumDy + np.sum((hFacC[kk,1:ny-1,ii]*drF[kk]*rAC[1:ny-1,ii])*(tracerDerY[kk,:,ii]))
        #print(sumDy)

    qmeanDy[tt] = sumDy/DomVolume

    print('The v-w mean of (dq/dy)^2 at nt=%d is %e \n' % (tt,qmeanDy[tt]))

qmeanDh = qmeanDx + qmeanDy

```

    The v-w mean of (dq/dx)^2 at nt=0 is 4.034261e-09

    The v-w mean of (dq/dx)^2 at nt=1 is 4.083236e-09

    The v-w mean of (dq/dx)^2 at nt=2 is 4.097932e-09

    The v-w mean of (dq/dx)^2 at nt=3 is 4.112076e-09

    The v-w mean of (dq/dx)^2 at nt=4 is 4.122707e-09

    The v-w mean of (dq/dx)^2 at nt=5 is 4.129387e-09

    The v-w mean of (dq/dx)^2 at nt=6 is 4.135393e-09

    The v-w mean of (dq/dx)^2 at nt=7 is 4.140423e-09

    The v-w mean of (dq/dx)^2 at nt=8 is 4.144909e-09

    The v-w mean of (dq/dx)^2 at nt=9 is 4.149382e-09

    The v-w mean of (dq/dx)^2 at nt=10 is 4.153325e-09

    The v-w mean of (dq/dx)^2 at nt=11 is 4.157115e-09

    The v-w mean of (dq/dx)^2 at nt=12 is 4.159474e-09

    The v-w mean of (dq/dx)^2 at nt=13 is 4.162074e-09

    The v-w mean of (dq/dx)^2 at nt=14 is 4.164404e-09

    The v-w mean of (dq/dx)^2 at nt=15 is 4.165952e-09

    The v-w mean of (dq/dx)^2 at nt=16 is 4.167517e-09

    The v-w mean of (dq/dx)^2 at nt=17 is 4.168834e-09

    The v-w mean of (dq/dx)^2 at nt=18 is 4.170027e-09

    The v-w mean of (dq/dx)^2 at nt=19 is 4.171000e-09

    The v-w mean of (dq/dx)^2 at nt=20 is 4.171309e-09

    The v-w mean of (dq/dx)^2 at nt=21 is 4.171370e-09

    The v-w mean of (dq/dx)^2 at nt=22 is 4.170988e-09

    The v-w mean of (dq/dx)^2 at nt=23 is 4.170812e-09

    The v-w mean of (dq/dx)^2 at nt=24 is 4.170793e-09

    The v-w mean of (dq/dx)^2 at nt=25 is 4.170865e-09

    The v-w mean of (dq/dx)^2 at nt=26 is 4.171215e-09

    The v-w mean of (dq/dx)^2 at nt=27 is 4.171621e-09

    The v-w mean of (dq/dx)^2 at nt=28 is 4.172339e-09

    The v-w mean of (dq/dx)^2 at nt=29 is 4.173181e-09

    The v-w mean of (dq/dx)^2 at nt=30 is 4.173976e-09

    The v-w mean of (dq/dx)^2 at nt=31 is 4.174881e-09

    The v-w mean of (dq/dx)^2 at nt=32 is 4.175694e-09

    The v-w mean of (dq/dx)^2 at nt=33 is 4.176713e-09

    The v-w mean of (dq/dx)^2 at nt=34 is 4.177971e-09

    The v-w mean of (dq/dx)^2 at nt=35 is 4.179558e-09

    The v-w mean of (dq/dx)^2 at nt=36 is 4.181456e-09

    The v-w mean of (dq/dy)^2 at nt=0 is 5.839977e-07

    The v-w mean of (dq/dy)^2 at nt=1 is 5.850888e-07

    The v-w mean of (dq/dy)^2 at nt=2 is 5.849502e-07

    The v-w mean of (dq/dy)^2 at nt=3 is 5.851254e-07

    The v-w mean of (dq/dy)^2 at nt=4 is 5.854212e-07

    The v-w mean of (dq/dy)^2 at nt=5 is 5.856096e-07

    The v-w mean of (dq/dy)^2 at nt=6 is 5.858870e-07

    The v-w mean of (dq/dy)^2 at nt=7 is 5.863068e-07

    The v-w mean of (dq/dy)^2 at nt=8 is 5.867181e-07

    The v-w mean of (dq/dy)^2 at nt=9 is 5.871297e-07

    The v-w mean of (dq/dy)^2 at nt=10 is 5.876651e-07

    The v-w mean of (dq/dy)^2 at nt=11 is 5.882482e-07

    The v-w mean of (dq/dy)^2 at nt=12 is 5.888198e-07

    The v-w mean of (dq/dy)^2 at nt=13 is 5.894476e-07

    The v-w mean of (dq/dy)^2 at nt=14 is 5.901115e-07

    The v-w mean of (dq/dy)^2 at nt=15 is 5.907567e-07

    The v-w mean of (dq/dy)^2 at nt=16 is 5.914231e-07

    The v-w mean of (dq/dy)^2 at nt=17 is 5.921284e-07

    The v-w mean of (dq/dy)^2 at nt=18 is 5.928249e-07

    The v-w mean of (dq/dy)^2 at nt=19 is 5.935090e-07

    The v-w mean of (dq/dy)^2 at nt=20 is 5.942113e-07

    The v-w mean of (dq/dy)^2 at nt=21 is 5.949072e-07

    The v-w mean of (dq/dy)^2 at nt=22 is 5.955840e-07

    The v-w mean of (dq/dy)^2 at nt=23 is 5.962508e-07

    The v-w mean of (dq/dy)^2 at nt=24 is 5.969071e-07

    The v-w mean of (dq/dy)^2 at nt=25 is 5.975439e-07

    The v-w mean of (dq/dy)^2 at nt=26 is 5.981661e-07

    The v-w mean of (dq/dy)^2 at nt=27 is 5.987823e-07

    The v-w mean of (dq/dy)^2 at nt=28 is 5.993891e-07

    The v-w mean of (dq/dy)^2 at nt=29 is 5.999802e-07

    The v-w mean of (dq/dy)^2 at nt=30 is 6.005613e-07

    The v-w mean of (dq/dy)^2 at nt=31 is 6.011350e-07

    The v-w mean of (dq/dy)^2 at nt=32 is 6.017001e-07

    The v-w mean of (dq/dy)^2 at nt=33 is 6.022564e-07

    The v-w mean of (dq/dy)^2 at nt=34 is 6.028067e-07

    The v-w mean of (dq/dy)^2 at nt=35 is 6.033455e-07

    The v-w mean of (dq/dy)^2 at nt=36 is 6.038719e-07

3rd Term: The volume-weighted average of the squared vertical derivative
------------------------------------------------------------------
The third term in the variance evolution equation is $-\kappa_{v} \overline{(\frac{\partial{q}}{\partial{z}})^2}$. Next, we calculate the square of the vertical gradient $(\frac{\partial{q}}{\partial{z}})^2$.

The vertical derivative is approximated using a centered-difference scheme.

```
### Calculate mean of (dq/dz)^2

qmeanDz = np.zeros((nt,1))

for tt in np.arange(nt):
    qsumDz = 0.0
    for ii in np.arange(nx):

        for jj in np.arange(ny):

            for kk in np.arange(nz-2):

                if (hFacC[kk,jj,ii]*drF[kk])+(hFacC[kk+1,jj,ii]*drF[kk+1]) == 0:

                    continue

                else :

                   qsumDz =qsumDz +(hFacC[kk,jj,ii]*drF[kk]*rAC[jj,ii])*(((tracer1[tt,kk+2,jj,ii]-tracer1[tt,kk,jj,ii])/((hFacC[kk+1,jj,ii]*drF[kk+1])+(hFacC[kk,jj,ii]*drF[kk])))**2)

    qmeanDz[tt] = qsumDz/DomVolume

    print('Mean (dq/dz)^2 at nt=%d is %e \n' %(tt, qmeanDz[tt]))

```

    Mean (dq/dz)^2 at nt=0 is 1.889592e-01

    Mean (dq/dz)^2 at nt=1 is 1.888139e-01

    Mean (dq/dz)^2 at nt=2 is 1.887896e-01

    Mean (dq/dz)^2 at nt=3 is 1.887755e-01

    Mean (dq/dz)^2 at nt=4 is 1.886880e-01

    Mean (dq/dz)^2 at nt=5 is 1.886365e-01

    Mean (dq/dz)^2 at nt=6 is 1.886157e-01

    Mean (dq/dz)^2 at nt=7 is 1.885373e-01

    Mean (dq/dz)^2 at nt=8 is 1.884423e-01

    Mean (dq/dz)^2 at nt=9 is 1.883601e-01

    Mean (dq/dz)^2 at nt=10 is 1.882548e-01

    Mean (dq/dz)^2 at nt=11 is 1.881200e-01

    Mean (dq/dz)^2 at nt=12 is 1.879749e-01

    Mean (dq/dz)^2 at nt=13 is 1.878100e-01

    Mean (dq/dz)^2 at nt=14 is 1.876207e-01

    Mean (dq/dz)^2 at nt=15 is 1.874122e-01

    Mean (dq/dz)^2 at nt=16 is 1.871805e-01

    Mean (dq/dz)^2 at nt=17 is 1.869234e-01

    Mean (dq/dz)^2 at nt=18 is 1.866459e-01

    Mean (dq/dz)^2 at nt=19 is 1.863452e-01

    Mean (dq/dz)^2 at nt=20 is 1.860183e-01

    Mean (dq/dz)^2 at nt=21 is 1.856688e-01

    Mean (dq/dz)^2 at nt=22 is 1.852978e-01

    Mean (dq/dz)^2 at nt=23 is 1.849016e-01

    Mean (dq/dz)^2 at nt=24 is 1.844818e-01

    Mean (dq/dz)^2 at nt=25 is 1.840405e-01

    Mean (dq/dz)^2 at nt=26 is 1.835766e-01

    Mean (dq/dz)^2 at nt=27 is 1.830900e-01

    Mean (dq/dz)^2 at nt=28 is 1.825829e-01

    Mean (dq/dz)^2 at nt=29 is 1.820552e-01

    Mean (dq/dz)^2 at nt=30 is 1.815069e-01

    Mean (dq/dz)^2 at nt=31 is 1.809395e-01

    Mean (dq/dz)^2 at nt=32 is 1.803536e-01

    Mean (dq/dz)^2 at nt=33 is 1.797497e-01

    Mean (dq/dz)^2 at nt=34 is 1.791287e-01

    Mean (dq/dz)^2 at nt=35 is 1.784912e-01

    Mean (dq/dz)^2 at nt=36 is 1.778380e-01

```

```

```

fig1=plt.figure(figsize=(21,3))

plt.subplot(1,3,1)
ax=plt.gca()
ax.set_axis_bgcolor((205/255.0, 201/255.0, 201/255.0))

plt.plot(qmeanDh[1:len(qmeanDh)-1],dqdt)

plt.xlabel('$<|\partial_{h}q|^2>$')

plt.ylabel('$\partial{<q^2>}/\partial{t}$')

plt.subplot(1,3,2)
ax=plt.gca()
ax.set_axis_bgcolor((205/255.0, 201/255.0, 201/255.0))

plt.plot(qmeanDz[1:len(qmeanDz)-1],dqdt)

plt.xlabel('$<\partial{q^2}/\partial{z}>$')

plt.ylabel('$\partial{<q^2>}/\partial{t}$')

plt.subplot(1,3,3)
ax=plt.gca()
ax.set_axis_bgcolor((205/255.0, 201/255.0, 201/255.0))

plt.plot(np.arange(0,nt*0.5,0.5),q2mean)

plt.xlabel('days')

plt.ylabel('$<q^2>$')

plt.tight_layout()
```

```
import pandas as pd
import statsmodels.api as sm

dt = 1*3600.0*12.0 # 1/2 day

x = np.column_stack((qmeanDh[1:len(qmeanDh)-1], qmeanDz[1:len(qmeanDz)-1])) #variance of space derivatives
#x = qmeanDh[1:len(qmeanDh)-1]
y = dqdt[0:len(dqdt)]

## fit a OLS model with intercept
#x = sm.add_constant(x)
est = sm.OLS(y, x).fit()

est.summary()

```

<table class="simpletable">
<caption>OLS Regression Results</caption>
<tr>
  <th>Dep. Variable:</th>            <td>y</td>        <th>  R-squared:         </th> <td>   0.903</td>
</tr>
<tr>
  <th>Model:</th>                   <td>OLS</td>       <th>  Adj. R-squared:    </th> <td>   0.897</td>
</tr>
<tr>
  <th>Method:</th>             <td>Least Squares</td>  <th>  F-statistic:       </th> <td>   153.8</td>
</tr>
<tr>
  <th>Date:</th>             <td>Tue, 26 Aug 2014</td> <th>  Prob (F-statistic):</th> <td>1.88e-17</td>
</tr>
<tr>
  <th>Time:</th>                 <td>15:16:27</td>     <th>  Log-Likelihood:    </th> <td>  337.24</td>
</tr>
<tr>
  <th>No. Observations:</th>      <td>    35</td>      <th>  AIC:               </th> <td>  -670.5</td>
</tr>
<tr>
  <th>Df Residuals:</th>          <td>    33</td>      <th>  BIC:               </th> <td>  -667.4</td>
</tr>
<tr>
  <th>Df Model:</th>              <td>     2</td>      <th>                     </th>     <td> </td>
</tr>
</table>
<table class="simpletable">
<tr>
   <td></td>     <th>coef</th>     <th>std err</th>      <th>t</th>      <th>P>|t|</th> <th>[95.0% Conf. Int.]</th>
</tr>
<tr>
  <th>x1</th> <td>-1754.9657</td> <td>  171.393</td> <td>  -10.239</td> <td> 0.000</td> <td>-2103.667 -1406.264</td>
</tr>
<tr>
  <th>x2</th> <td>    0.0054</td> <td>    0.001</td> <td>    9.853</td> <td> 0.000</td> <td>    0.004     0.007</td>
</tr>
</table>
<table class="simpletable">
<tr>
  <th>Omnibus:</th>       <td>18.397</td> <th>  Durbin-Watson:     </th> <td>   1.962</td>
</tr>
<tr>
  <th>Prob(Omnibus):</th> <td> 0.000</td> <th>  Jarque-Bera (JB):  </th> <td>  33.935</td>
</tr>
<tr>
  <th>Skew:</th>          <td> 1.195</td> <th>  Prob(JB):          </th> <td>4.28e-08</td>
</tr>
<tr>
  <th>Kurtosis:</th>      <td> 7.190</td> <th>  Cond. No.          </th> <td>1.16e+07</td>
</tr>
</table>

```

```

```

```

```

```


```python
%matplotlib inline
```

序列模型和长短时记忆网络(LSTM)
===================================================

到目前为止，我们已经看到了各种各样的前馈网络(feed-forward networks)。
也就是说，根本不存在由网络维护的状态(state)。
这可能不是我们想要的行为。序列模型(Sequence models)是NLP的核心：
它们是在输入之间通过时间存在某种依赖关系的模型。
序列模型的经典例子是用于词性标注(part-of-speech tagging)的
隐马尔可夫模型(Hidden Markov Model)。
另一个例子是条件随机场(conditional random field)。

递归神经网络(recurrent neural network)是一种保持某种状态的网络。
例如，它的输出可以作为下一个输入的一部分使用，以便信息可以在序列通过网络时在序列中传播。
在LSTM的情况下，对于序列中的每个元素，都有相应的隐藏状态($h_t$)，
原则上可以包含来自序列中较早的任意点的信息。
我们可以利用隐藏状态来预测语言模型中的单词、词性标注(part-of-speech tags)以及无数其他事物。

Pytorch中的LSTM
~~~~~~~~~~~~~~~~~

在开始这个示例之前，请注意以下几点。Pytorch的LSTM期望它的所有输入都是3D张量。
这些张量的每个轴(axes)的语义很重要。第一个轴是序列本身，第二个轴索引batch中的样例，
以及第三个轴索引输入的元素。我们还没有讨论过mini-batching，所以让我们忽略这一点，
并假设我们总是只有1维在第二轴。如果我们想在"The cow jumped"这个句子上运行序列模型，
我们的输入应该看起来像这样：

\begin{align}\begin{bmatrix}
   \overbrace{q_\text{The}}^\text{row vector} \\
   q_\text{cow} \\
   q_\text{jumped}
   \end{bmatrix}\end{align}

不过，请记住，还有一个额外的第二维,其size为1。

此外，您可以一次遍历一遍序列，在这种情况下，第一轴的size也是1。

让我们看一个快速的例子。

```python
# Author: Robert Guthrie

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

torch.manual_seed(1)
```

```python
lstm = nn.LSTM(3, 3)  # Input dim is 3, output dim is 3
inputs = [torch.randn(1, 3) for _ in range(5)]  # make a sequence of length 5

# initialize the hidden state.
hidden = (torch.randn(1, 1, 3),
          torch.randn(1, 1, 3))
for i in inputs:
    # Step through the sequence one element at a time.
    # after each step, hidden contains the hidden state.
    out, hidden = lstm(i.view(1, 1, -1), hidden)

# alternatively, we can do the entire sequence all at once.
# the first value returned by LSTM is all of the hidden states throughout
# the sequence. the second is just the most recent hidden state
# (compare the last slice of "out" with "hidden" below, they are the same)
# The reason for this is that:
# "out" will give you access to all hidden states in the sequence
# "hidden" will allow you to continue the sequence and backpropagate,
# by passing it as an argument  to the lstm at a later time
# Add the extra 2nd dimension
inputs = torch.cat(inputs).view(len(inputs), 1, -1)
hidden = (torch.randn(1, 1, 3), torch.randn(1, 1, 3))  # clean out hidden state
out, hidden = lstm(inputs, hidden)
print(out)
print(hidden)
```

样例: 一种用于词性标注的LSTM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在这个小节中, 我们将使用 LSTM 来获得词性标注(part of speech tags)。
我们将不会使用Viterbi 或 Forward-Backward 或 其他任何类似的技术,
但是作为一个对读者稍微有挑战性的练习, 当你了解了这一切如何运转的时候
再考虑一下如何使用 Viterbi。

模型如下: 假定我们的输入语句是 $w_1, \dots, w_M$, 其中 $w_i \in V$, 我们的词汇库。
另外, 假定 $T$ 是我们的标记集合, 以及 $y_i$ 是单词 $w_i$ 的标记。
把我们对单词 $w_i$ 的标记的预测记为 $\hat{y}_i$ 。

这是一个结构预测，模型，其中我们的输出是序列 $\hat{y}_1, \dots, \hat{y}_M$,
其中 $\hat{y}_i \in T$ 。

为了进行预测, 在句子上传递一个LSTM(pass an LSTM over the sentence)。
在时间步(timestep) $i$ 的隐藏状态记为 $h_i$ 。
另外，给每个tag分配一个唯一的index (就像在词嵌入章节中的 word\_to\_ix 一样)。
然后，我们预测 $\hat{y}_i$ 的规则是：

\begin{align}\hat{y}_i = \text{argmax}_j \  (\log \text{Softmax}(Ah_i + b))_j\end{align}

也就是说, 对 隐藏状态的仿射映射 取 对数软最大化(log softmax),
并且预测出的tag是这个向量中的最大值对应的tag。
请注意，这立即意味着 $A$ 的目标空间的维数为 $|T|$ 。

准备数据:

```python
def prepare_sequence(seq, to_ix):
    idxs = [to_ix[w] for w in seq]
    return torch.tensor(idxs, dtype=torch.long)

training_data = [
    ("The dog ate the apple".split(), ["DET", "NN", "V", "DET", "NN"]),
    ("Everybody read that book".split(), ["NN", "V", "DET", "NN"])
]
word_to_ix = {}
for sent, tags in training_data:
    for word in sent:
        if word not in word_to_ix:
            word_to_ix[word] = len(word_to_ix)
print(word_to_ix)
tag_to_ix = {"DET": 0, "NN": 1, "V": 2}

# These will usually be more like 32 or 64 dimensional.
# We will keep them small, so we can see how the weights change as we train.
EMBEDDING_DIM = 6
HIDDEN_DIM = 6
```

创建模型:

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

    def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size):
        super(LSTMTagger, self).__init__()
        self.hidden_dim = hidden_dim

        self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)

        # The LSTM takes word embeddings as inputs, and outputs hidden states
        # with dimensionality hidden_dim.
        self.lstm = nn.LSTM(embedding_dim, hidden_dim)

        # The linear layer that maps from hidden state space to tag space
        self.hidden2tag = nn.Linear(hidden_dim, tagset_size)
        self.hidden = self.init_hidden()

    def init_hidden(self):
        # Before we've done anything, we dont have any hidden state.
        # Refer to the Pytorch documentation to see exactly
        # why they have this dimensionality.
        # The axes semantics are (num_layers, minibatch_size, hidden_dim)
        return (torch.zeros(1, 1, self.hidden_dim),
                torch.zeros(1, 1, self.hidden_dim))

    def forward(self, sentence):
        embeds = self.word_embeddings(sentence)
        lstm_out, self.hidden = self.lstm(
            embeds.view(len(sentence), 1, -1), self.hidden)
        tag_space = self.hidden2tag(lstm_out.view(len(sentence), -1))
        tag_scores = F.log_softmax(tag_space, dim=1)
        return tag_scores
```

训练模型:

```python
model = LSTMTagger(EMBEDDING_DIM, HIDDEN_DIM, len(word_to_ix), len(tag_to_ix))
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)

# See what the scores are before training
# Note that element i,j of the output is the score for tag j for word i.
# Here we don't need to train, so the code is wrapped in torch.no_grad()
with torch.no_grad():
    inputs = prepare_sequence(training_data[0][0], word_to_ix)
    tag_scores = model(inputs)
    print(tag_scores)

for epoch in range(300):  # again, normally you would NOT do 300 epochs, it is toy data
    for sentence, tags in training_data:
        # Step 1. Remember that Pytorch accumulates gradients.
        # We need to clear them out before each instance
        model.zero_grad()

        # Also, we need to clear out the hidden state of the LSTM,
        # detaching it from its history on the last instance.
        model.hidden = model.init_hidden()

        # Step 2. Get our inputs ready for the network, that is, turn them into
        # Tensors of word indices.
        sentence_in = prepare_sequence(sentence, word_to_ix)
        targets = prepare_sequence(tags, tag_to_ix)

        # Step 3. Run our forward pass.
        tag_scores = model(sentence_in)

        # Step 4. Compute the loss, gradients, and update the parameters by
        #  calling optimizer.step()
        loss = loss_function(tag_scores, targets)
        loss.backward()
        optimizer.step()

# See what the scores are after training
with torch.no_grad():
    inputs = prepare_sequence(training_data[0][0], word_to_ix)
    tag_scores = model(inputs)

    # The sentence is "the dog ate the apple".  i,j corresponds to score for tag j
    # for word i. The predicted tag is the maximum scoring tag.
    # Here, we can see the predicted sequence below is 0 1 2 0 1
    # since 0 is index of the maximum value of row 1,
    # 1 is the index of maximum value of row 2, etc.
    # Which is DET NOUN VERB DET NOUN, the correct sequence!
    print(tag_scores)
```

练习: 使用字符级特征增强LSTM语义标注
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在上面的例子中，每个单词都有一个嵌入，作为序列模型的输入。
让我们用 从单词的字符派生出来的表示 来增强 单词嵌入。
我们希望这会有很大的帮助，因为词缀(affixes)之类的字符级信息对词性(part-of-speech)有很大的影响。
例如，带有词缀 *-ly* 的词在英语中几乎总是被标记为副词(adverbs)。

为了做到这一点, 令 $c_w$ 是单词 $w$ 的字符级表示(character-level representation)。
像之前一样，令 $x_w$ 是单词嵌入。 然后，我们序列模型的输入是 $x_w$ 和 $c_w$
的串接(concatenation)。因此，如果 $x_w$ 有 5 个维度, 并且 $c_w$ 的纬度是 3 ,
那么我们的 LSTM 应该接受维数为8的输入。

为了获得字符级表示, 在一个单词的若干字符上做LSTM ,并且令 $c_w$ 是这个LSTM的最终隐藏状态。

提示:

* 你的新模型将会有两个LSTM。原来的LSTM输出POS标签分数(POS tag scores),
  新的LSTM输出每个单词的字符级表示。
* 为了在字符集上建一个序列模型, 你必须嵌入字符(embed characters)。
  字符嵌入将会成为字符级LSTM的输入。


# Use Deuterium to bound the Gradient Norm

```python
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from deuterium import Variable, to_vec, random_symbols, get_gradients
from sympy import sympify
from scipy.optimize import shgo

import symengine as se
from sklearn.metrics import accuracy_score
import sys
sys.setrecursionlimit(1_000_000)
import warnings
warnings.filterwarnings("ignore")
```

Define some utility functions, notably the loss functions and tempered sigmoid activation functions.

```python
to_data = np.vectorize(lambda x: x.data)

def sigmoid(x, s=1, T=1, o=0):
        return (s/(1+np.exp(-T*x)))-o

def tanh(x):
    return sigmoid(x, 2, 2, 1)

bce_loss = lambda y_pred, y_true: -np.mean(np.multiply(y_true, np.log(y_pred)) + np.multiply((1 - y_true), np.log(1 - y_pred)))
normalize = lambda x: (x-x.min())/(x.max()-x.min())

mse_loss = lambda y_pred, y_true: 1
```

Define the network architecture

```python
IN=100
INTERMEDIATE=100
```

```python
# This creates symbolic representations for all the layers
x = to_vec(np.array(random_symbols(IN, "x")).reshape((1,IN)))
y = to_vec(np.array(random_symbols(1, "y")))

w1 = to_vec(np.array(random_symbols(IN*INTERMEDIATE, "w1")).reshape(IN, INTERMEDIATE))
b = to_vec(np.array(random_symbols(INTERMEDIATE, "b")).reshape(1, INTERMEDIATE))
w2 = to_vec(np.array(random_symbols(INTERMEDIATE, "w2")).reshape(INTERMEDIATE,1))
```

```python
# This replaces some of the variables with *values*
# w1 (weights layer 1)
# w2 (weights layer 2)
# b (bias terms)
# y (label)

w1 = to_vec(np.random.normal(size=IN*INTERMEDIATE).reshape(IN, INTERMEDIATE))
b = to_vec(np.random.normal(size=INTERMEDIATE).reshape(1, INTERMEDIATE))
w2 = to_vec(np.random.normal(size=INTERMEDIATE).reshape(INTERMEDIATE,1))
y = to_vec(np.array(1))

```

Symbolically calculate the network output

```python
layer_1 = (x@w1)+b
y_pred = (layer_1@w2)
loss = bce_loss(y_pred, y)
```

Obtain the gradients w.r.t all inputs

```python
loss.backward()
```

```python
x_grad = np.array([i.grad for i in x.flatten().tolist()])
y_grad = np.array([i.grad for i in y.flatten().tolist()])
w1_grad = np.array([i.grad for i in w1.flatten().tolist()])
b_grad = np.array([i.grad for i in b.flatten().tolist()])
w2_grad = np.array([i.grad for i in w2.flatten().tolist()])

full_grad = to_vec(np.concatenate((x_grad, y_grad, w1_grad, b_grad, w2_grad)))

```

```python
# the gradient we care about is just wrt the weights

my_grad = to_vec(np.concatenate((w1_grad, b_grad, w2_grad)))
len(my_grad)
#sympify(my_grad)
```

    10200

```python
import symengine.lib.symengine_wrapper as sew

def op(e, depth):
    if isinstance(e, sew.RealDouble):
        print(' '* depth, 'found double:', e)
    elif isinstance(e, sew.Symbol):
        print(' '* depth, 'found symbol:', e)
    elif isinstance(e, sew.Mul):
        print(' '* depth, 'found Mul\t # args:', len(e.args))
        for a in e.args:
            op(a, depth+1)
    elif isinstance(e, sew.Pow):
        print(' '* depth, 'found Pow\t # args:', len(e.args))
        for a in e.args:
            op(a, depth+1)
    elif isinstance(e, sew.Add):
        print(' '* depth, 'found Add\t # args:', len(e.args))
        for a in e.args:
            op(a, depth+1)
    else:
        print('fail:', type(e))

```

```python
def int_analysis(e):
    if isinstance(e, sew.RealDouble):
        return (float(e), float(e))
    elif isinstance(e, sew.Integer):
        return (float(e), float(e))
    elif isinstance(e, sew.Symbol):
        return (-1, 1)
    elif isinstance(e, sew.Mul):
        results = [int_analysis(a) for a in e.args]
        lowers = [r[0] for r in results]
        uppers = [r[1] for r in results]

        out_lower = lowers[0]
        out_upper = uppers[0]

        for i in range(1, len(e.args)):
            options = [out_lower*lowers[i],
                       out_lower*uppers[i],
                       out_upper*lowers[i],
                       out_upper*uppers[i]]
            out_lower = np.min(options)
            out_upper = np.max(options)

        return out_lower, out_upper
    elif isinstance(e, sew.Pow):
        a, b = e.args
        a_lower, a_upper = int_analysis(a)
        b_lower, b_upper = int_analysis(b)
        options = [a_lower**b_lower,
                   a_upper**b_lower,
                   a_lower**b_upper,
                   a_upper**b_upper]
        return (np.min(options), np.max(options))
    elif isinstance(e, sew.Add):
        results = [int_analysis(a) for a in e.args]
        lowers = [r[0] for r in results]
        uppers = [r[1] for r in results]
        return np.sum(lowers), np.sum(uppers)
    else:
        print('fail:', type(e))
```

```python
%%time
lower, upper = int_analysis(np.linalg.norm(my_grad, ord=2).data)
upper - lower
```

    CPU times: user 27.7 s, sys: 20 ms, total: 27.7 s
    Wall time: 27.8 s

    0.0037524471764013856

# Optimize the Gradient Norm one Element at a Time

```python
%%time

print(my_grad_norm.data.free_symbols)
intervals = [(-1, 1) for _ in np.sum(my_grad).data.free_symbols]

elems = []
for g in my_grad:
    gp = g**2
    f = se.Lambdify(list(gp.data.free_symbols), gp.data)
    sol = shgo(f, intervals)
    elems.append(sol.fun)

print(elems)
np.sqrt(np.sum(elems))
```

    {x_1, x_0}
    [0.0, 0.0, 0.0, 0.0, 0.037232887245820745, 0.1297134117750557, 3.3288945997629245e-13, 4.543889776067925e-16]
    CPU times: user 20.4 ms, sys: 34 µs, total: 20.4 ms
    Wall time: 28.9 ms

    0.4085906252243311

# Optimize the Gradient Norm all at once

```python
my_grad_norm = np.linalg.norm(my_grad, ord=2)
my_grad_norm.data.free_symbols
my_grad_norm_func = se.Lambdify(list(my_grad_norm.data.free_symbols), my_grad_norm.data)
```

```python
%%time

print(my_grad_norm.data.free_symbols)
intervals = [(-1, 1) for _ in my_grad_norm.data.free_symbols]
sol = shgo(my_grad_norm_func, intervals)
sol.fun
```

    {x_1, x_0}
    CPU times: user 6.76 ms, sys: 0 ns, total: 6.76 ms
    Wall time: 6.66 ms

    0.5778750969544236

```python

```


# Annual modulation of DM-electron scattering
Want to do a cross check of the result of Lee et al. on the fourier coefficients for annual modulation. These are defined by expressing the rate as,

\begin{equation}
\frac{dR}{dE_R} = A_0 + \sum_{i = 1}^{\infty} A_n \cos{n\omega(t-t_n)}\
\end{equation}

Initially I was unable to reproduce Fig. 6 from https://arxiv.org/abs/1508.07361 but then I realised they do a slightly strange thing and plot the fourier coefficients $A_1/A_0$ and $A_2/A_0$ as a function of $E_d$ which is defined as $E_d = E_R + E_B$. This is the energy deposited in a semiconductor but is not the energy of the recoiling electron. So when they plot values of $E_d$ for xenon going down to 1 eV, this corresponds to negative values of $E_R$? (Since $E_B > 12.4$ eV)

### First lets look at $g(v_{\rm min})$
Calculate for the SHM rather than SHM++ here for a straightforward comparison (the Sausage messes up the higher order modes a bit)

```python
import sys
sys.path.append('../erec')
from numpy import *
import matplotlib.pyplot as plt
from LabFuncs import *
from Params import *
from DMFuncs import *
from PlotFuncs import *
from HaloFuncs import *
from Params import *
from AtomicFuncs import *

# v_mins
n = 1000
vmin_fine = linspace(0.01,800.0,n)

# Times
ndays = 100
days = linspace(0.0,365.0-365.0/ndays,ndays)

# Calculate gmin
sig_iso = SHM.Dispersion*array([1,1,1])
gmin_fine = zeros(shape=(ndays,n))
gmin_fine_gf = zeros(shape=(ndays,n))
for i in range(0,ndays):
    gmin_fine[i,:] = gvmin_Isotropic(vmin_fine,day=days[i],sig=SHM.Dispersion,v_esc=SHM.EscapeSpeed,v_LSR=SHM.RotationSpeed)
    gmin_fine_gf[i,:] = gvmin_Triaxial(vmin_fine,day=days[i],sig=sig_iso,GravFocus=True,v_esc=SHM.EscapeSpeed,v_LSR=SHM.RotationSpeed)
    print(i)
```

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99

### And calculate the fourier coefficients:

```python
from numpy.fft import fft

# Function to get nc fourier components
def FourierComponents(gmin,nc):
    nv = shape(gmin)[1]
    nt = shape(gmin)[0]
    a0 = zeros(shape=nv)
    b = zeros(shape=(nc,nv))
    a = zeros(shape=(nc,nv))
    for i in range(0,nv):
        y = gmin[:,i]
        yfft = fft(y)/(1.0*nt)
        yfft = yfft[0:(nc+1)]
        a0[i] = real(yfft[0])
        a[:,i] = 2*real(yfft[1:])
        b[:,i] = -2*imag(yfft[1:])
    return a0,a,b

fig,ax1 = MySquarePlot(r"$v_{\rm min}$",r'$A_i/A_0$')

A0,a_1,b_1 = FourierComponents(gmin_fine,5)
A1 = sqrt(a_1[0,:]**2.0+b_1[0,:]**2.0)
A2 = sqrt(a_1[1,:]**2.0+b_1[1,:]**2.0)
plt.plot(vmin_fine,A1/A0,'r--',lw=3)
plt.plot(vmin_fine,A2/A0,'r--',lw=3,label='without grav. focusing')

A0,a_1,b_1 = FourierComponents(gmin_fine_gf,5)
A1 = sqrt(a_1[0,:]**2.0+b_1[0,:]**2.0)
A2 = sqrt(a_1[1,:]**2.0+b_1[1,:]**2.0)
plt.plot(vmin_fine,A1/A0,'b-',lw=3)
plt.plot(vmin_fine,A2/A0,'b-',lw=3,label='with grav. focusing')

plt.legend(frameon=False,fontsize=30)

plt.yscale('log')
plt.xlim([1,800])
plt.ylim([1e-4,1e1])
plt.gcf().text(0.14,0.45,r'$i=1$',fontsize=30,rotation=0)
plt.gcf().text(0.14,0.22,r'$i=2$',fontsize=30,rotation=0)

plt.show()
```

## So notably we have the phase flip at $v_{\rm min} \simeq 200$ km/s. The gravitational focusing softens the transition somewhat so we don't have a sharp flip in the phase but rather it changes smoothly from Dec to Jun

## Now let's look at the fourier coefficients for the DM-rate
We'll define a new function here rather than the main one in `erec/DMFuncs.py` because we'll need to modify it as we'll see in a second

```python
E_r_vals = logspace(-1.0,3.0,500)/1000.0
sigma_e = 1.0e-37
rho_0 = 0.55
Atom = Xe131
nq = 20
DMFormFactor = HeavyMediator

# modifiying this so we can play with the binding energies E_B
def ElectronRecoilRate_modified(E_B_vals,m_DM):

    nsh = size(E_B_vals)
    Efine,qfine,fion_fine = Atom.IonisationFormFactor()

    # Constants
    m_DM_keV = m_DM*1000 # keV
    ne = size(E_r_vals)
    m_N_kg = Atom.MassNumber*m_p_kg # keV
    N_T = 1.0/m_N_kg # kg
    mu = m_DM_keV*m_e/(m_e+m_DM_keV) # keV
    n_DM = rho_0*1e6/m_DM_keV # cm**-3.0
    vmax = ((850.0)*1000)/3.0e8 # natural units

    dRdlnE = zeros(shape=(ndays,ne))
    for it in range(0,ndays):
        for sh in range(0,nsh): # over orbitals
            E_B = E_B_vals[sh]
            Emax = 0.5*m_DM_keV*vmax**2.0
            if Emax>E_r_vals[0]:
                imax = arange(0,ne)[E_r_vals<Emax][-1]
                for i in range(0,imax+1): # over energies
                    E_r = E_r_vals[i]
                    if E_r<(0.5*m_DM_keV*vmax**2.0-E_B):

                        # Form factor
                        qmax = m_DM_keV*vmax*(1.0+sqrt(1-2*(E_r+E_B)/(m_DM_keV*vmax**2.0)))
                        qmin = m_DM_keV*vmax*(1.0-sqrt(1-2*(E_r+E_B)/(m_DM_keV*vmax**2.0)))
                        qvals = logspace(log10(qmin),log10(qmax),nq)
                        fion = interp(qvals,qfine,fion_fine[argmin(abs(Efine-E_r)),:,sh])

                        # interpolate at required vmin and calculate cross section
                        vmin_vals = vmin_ER(E_B,E_r,qvals,m_DM)
                        gmin = interp(vmin_vals,vmin_fine,gmin_fine_gf[it,:])/(1000*100)
                        qfunc = qvals*fion*gmin*DMFormFactor(qvals)
                        dsigma = (100*3.0e8)**2*(sigma_e/(8*mu**2.0))*trapz(qfunc,qvals) # cm^2

                        # rate
                        dRdlnE[it,i] += N_T*n_DM*\
                                AtomicFuncs.FermiFactor(E_r)*dsigma # kg^-1 s^-1
    dRdlnE *= (365*3600*24) # kg^-1 yr^-1
    return dRdlnE
```

### Now fix the binding energies (to what they should be) and plot the $A_1$ and $A_2$ for two masses

```python
# xenon binding energies
E_B_vals = array([12,26,76])/1000.0

R1 = ElectronRecoilRate_modified(E_B_vals,10.0)
R2 = ElectronRecoilRate_modified(E_B_vals,100.0)

fig,ax1,ax2 = MyDoublePlot(r"$E_R$ [eV]",r'$A_i/A_0$',r"$E_R$ [eV]",wspace=0.05,size_y=5)

A0,a_1,b_1 = FourierComponents(R1,5)
A1 = sqrt(a_1[0,:]**2.0+b_1[0,:]**2.0)
A2 = sqrt(a_1[1,:]**2.0+b_1[1,:]**2.0)
ax1.plot(E_r_vals*1000,abs(A1/A0),'k-',lw=3,label=r'$|A_1/A_0|$')
ax1.plot(E_r_vals*1000,abs(A2/A0),'k--',lw=3,label=r'$|A_2/A_0|$')
ax1.legend(frameon=False,fontsize=30)

A0,a_1,b_1 = FourierComponents(R2,5)
A1 = sqrt(a_1[0,:]**2.0+b_1[0,:]**2.0)
A2 = sqrt(a_1[1,:]**2.0+b_1[1,:]**2.0)
ax2.plot(E_r_vals*1000,abs(A1/A0),'k-',lw=3)
ax2.plot(E_r_vals*1000,abs(A2/A0),'k--',lw=3)

ax1.set_yscale('log')
ax2.set_yscale('log')
ax1.set_xscale('log')
ax2.set_xscale('log')
ax1.set_xlim([1,500])
ax2.set_xlim([1,500])
ax1.set_ylim([1e-3,1])
ax2.set_ylim([1e-3,1])
ax2.set_yticklabels([])

ax1.set_title(r'$m_{\rm DM} = 10$ MeV',fontsize=30)
ax2.set_title(r'$m_{\rm DM} = 100$ MeV',fontsize=30)

plt.show()
```

### This clearly is notably different from Fig. 6 of Lee et al. Although the tails of these curves seem to more or less agree.

### Now we try again but do as they do and plot vs $E_d = E_R + E_B$. This requires us to set $E_B$ to zero so that we have control over $E_d$ going down below the binding energies of the xenon electrons

```python
# xenon binding energies
E_B_vals = array([0.0,0.0,0.0])

R1 = ElectronRecoilRate_modified(E_B_vals,10.0)
R2 = ElectronRecoilRate_modified(E_B_vals,100.0)

fig,ax1,ax2 = MyDoublePlot(r"$E_d$ [eV]",r'$A_i/A_0$',r"$E_d$ [eV]",wspace=0.05,size_y=5)

A0,a_1,b_1 = FourierComponents(R1,5)
A1 = sqrt(a_1[0,:]**2.0+b_1[0,:]**2.0)
A2 = sqrt(a_1[1,:]**2.0+b_1[1,:]**2.0)
ax1.plot(E_r_vals*1000,abs(A1/A0),'k-',lw=3,label=r'$|A_1/A_0|$')
ax1.plot(E_r_vals*1000,abs(A2/A0),'k--',lw=3,label=r'$|A_2/A_0|$')
ax1.legend(frameon=False,fontsize=30)

A0,a_1,b_1 = FourierComponents(R2,5)
A1 = sqrt(a_1[0,:]**2.0+b_1[0,:]**2.0)
A2 = sqrt(a_1[1,:]**2.0+b_1[1,:]**2.0)
ax2.plot(E_r_vals*1000,abs(A1/A0),'k-',lw=3)
ax2.plot(E_r_vals*1000,abs(A2/A0),'k--',lw=3)

ax1.set_yscale('log')
ax2.set_yscale('log')
ax1.set_xscale('log')
ax2.set_xscale('log')
ax1.set_xlim([1,500])
ax2.set_xlim([1,500])
ax1.set_ylim([1e-3,1])
ax2.set_ylim([1e-3,1])
ax2.set_yticklabels([])

ax1.set_title(r'$m_{\rm DM} = 10$ MeV',fontsize=30)
ax2.set_title(r'$m_{\rm DM} = 100$ MeV',fontsize=30)

plt.show()
```

### Which is essentially what they get. It's still not perfect agreement which I would guess comes down to $|f_{\rm ion}(q)|^2$ since their form factor for xenon 5p still doesn't quite match mine.

# The final point is that I think in a xenon experiment the phase flip will never appear because $E_B$ is always too high
I think the energies plotted in Lee et al. are not physical? Unless one is talking about a semiconductor experiment. In a germanium experiment the binding energy is low so the $v_{\rm min}(E_R,q)$ function extends much deeper into $g(v_{\rm min})$ so will be able to pick up the region before the phase flip.

```python
# p orbitals
n_p = array([2]*2+[3]*3+[4]*3+[5]*4)
Z_p = array([58.7712,22.6065,48.9702,13.4997,9.8328,40.2591,7.1841,5.1284,21.5330,3.4469,2.2384,1.14588])
c_5p = array([-0.005879,0.149040,-0.018716,-0.266839,-0.031096,-0.024100,0.267374,0.161460,0.059721,-0.428353,-0.542284,-0.201667])

n = 1000

E_r = 0.01/1000.0
q_vals = logspace(0.0,3.5,n)

Jun2 = argmin(abs(days-152.0))
Dec2 = argmin(abs(days-336.0))

np = 100
F1 = f_nl_ion_sq(q_vals,E_r,1,c_5p,n_p,Z_p,np=np)
vmin_vals = vmin_ER(12.4/1000.0,E_r,q_vals,100.0)
gmin1 = interp(vmin_vals,vmin_fine,gmin_fine_gf[Jun2,:])*3e8/1000
gmin2 = interp(vmin_vals,vmin_fine,gmin_fine_gf[Dec2,:])*3e8/1000

plt.rcParams['axes.linewidth'] = 2.5
plt.rc('text', usetex=True)
plt.rc('font', family='serif',size=30)
fig = plt.figure(figsize=(15,13))
ax1 = fig.add_subplot(111)

col1 = 'forestgreen'
col2 = 'navy'

ax1.semilogx(q_vals,vmin_vals,'--', color=col1,lw=4,zorder=100)

ax1.tick_params(which='both',axis='y', labelcolor=col1,width=2,length=13,right=True,top=True,direction='in',pad=10)
ax1.tick_params(axis='x',width=2,length=13,right=True,top=True,direction='in',pad=10)
ax1.set_ylim([0,1000])
ax1.plot([q_vals[0],q_vals[-1]],[770,770],'k--',lw=2.5)
ax1.set_xlim([q_vals[0],q_vals[-1]])

#dat = loadtxt('test.txt',delimiter=',')
#ax1.plot(dat[:,0],dat[:,1],'k-')

y2 = F1*(gmin1-gmin2)
ypos = abs(y2)[y2>0]
ypos[(ypos<1e-11)&(q_vals[y2>0]<200.0)] = 0.0
ypos[(ypos<1e-7)&(q_vals[y2>0]<7.0)] = 0.0

yneg = abs(y2)[y2<0]
#yneg[(yneg<1e-11)] = 0.0

ax2 = ax1.twinx()
ax2.fill_between(q_vals[y2>0],ypos,color=col2,alpha=0.5)
ax2.loglog(q_vals[y2>0],ypos, color=col2,lw=4,zorder=-1)

ax2.fill_between(q_vals[y2<0],yneg,color='red',alpha=0.5)
ax2.loglog(q_vals[y2<0],yneg, color='red',lw=4)

ax2.tick_params(axis='y', labelcolor=col2,width=2,length=13,right=True,top=True,direction='in',pad=10)
ax2.set_ylim([1e-17,1e-3])

#dat = loadtxt('test2.txt',delimiter=',')
#ax1.plot(dat[:,0],dat[:,1],'k-')

#dat = loadtxt('test_chris.txt',delimiter=',')
#ax2.plot(dat[:,0],dat[:,1],'r-')

ax1.tick_params(which='minor',direction='in',width=1,length=10,right=True,top=True)
ax2.tick_params(which='minor',direction='in',width=1,length=10,right=True,top=True)

ax2.set_ylabel(r'$\pm\bigg|c |f^{5p}_{\rm ion}(q)|^2 \bigg[ g(v_{\rm min},{\rm Jun 2})-g(v_{\rm min},{\rm Dec 2})\bigg]\bigg|$', color=col2,fontsize=40,labelpad=40)
ax1.set_xlabel(r'$q$ [keV]',fontsize=45)
ax1.set_ylabel(r'$v_{\rm min}$ [km s$^{-1}$]', color=col1,fontsize=40)

plt.gcf().text(0.45,0.8,r'$E_R\simeq 0$ eV',fontsize=40)
plt.gcf().text(0.45,0.75,r'$m_{\rm DM} = 100 $ MeV',fontsize=40)

plt.gcf().text(0.35,0.15,r'{\bf +}',fontsize=40,color='navy')
plt.gcf().text(0.49,0.16,r'{\bf --}',fontsize=40,color='red')
plt.gcf().text(0.63,0.15,r'{\bf +}',fontsize=40,color='navy')

plt.show()
pltname = 'vmin_vs_q_phaseflip'
fig.savefig('../plots/'+pltname+'.pdf',bbox_inches='tight')
fig.savefig('../plots/plots_png/'+pltname+'.png',bbox_inches='tight')
```

```python

```


# Polynomial Hyperelasticity

## Overview

The polynomial hyperelastic model is a model in which the strain energy is a polynomial function of the invariants of the deformation tensor.  The polynomial hyperelastic model

- is a phenomenologial model,
- has an arbitrary number of parameters, though most materials use fewer than 4
- is typically fit to uniaxial extension/compression, equibiaxial extension/compression, or shear experimental data
- usually valid for strains less than 100%

## See Also

- [User Defined Materials](UserMaterial.ipynb)
- [Linear Elastic Material](LinearElastic.ipynb)
- [Mooney-Rivlien Hyperelasticity](MooneyRivlin.ipynb)

## Contents

1. Fundamental Equations
2. Model Implementation
3. Model Verification

## Fundamental Equations

The strain energy in the polynomial hyperelastic model is defined as

$$
W = \sum_{i,j=0}^N c_{ij}\left(\overline{I}_1 - 3\right)^i \left(\overline{I}_2 - 3\right)^j +
  + \frac{1}{D_1}\left(J-1\right)^2
$$

where the $c_{ij}$ and $D_1$ are material parameters, the $\overline{I}_i$ are the isochoric invariants of the right Cauchy deformation tensor $\pmb{C} = \pmb{F}^T{\cdot}\pmb{F}$, where $\pmb{F}$ is deformation gradient tensor, and $J$ is the determinant of the deformation gradient.

### The Second Piola-Kirchhoff Stress Tensor

$$
  \pmb{T} = 2\frac{\partial W}{\partial \pmb{C}}
$$

For isotropic hyperelasticity $W=W\left(\overline{I}_1, \overline{I}_2, J\right)$ and

$$
  \pmb{T} = 2\left(\frac{\partial W}{\partial \overline{I}_1}\frac{\partial \overline{I}_1}{\partial \pmb{C}} +
                   \frac{\partial W}{\partial \overline{I}_2}\frac{\partial \overline{I}_2}{\partial \pmb{C}} +
                   \frac{\partial W}{\partial J}\frac{\partial J}{\partial \pmb{C}}\right)
         = \pmb{A} \cdot \pmb{B}
$$

where

$$
  \pmb{A} = \left[\frac{\partial W}{\partial \overline{I}_1} \quad
              \frac{\partial W}{\partial \overline{I}_2} \quad
              \frac{\partial W}{\partial J}\right]
$$

and

\begin{align}
  \pmb{B} &= \left[
    \frac{\partial \overline{I}_1}{\partial \pmb{C}} \quad
    \frac{\partial \overline{I}_2}{\partial \pmb{C}} \quad
    \frac{\partial J}{\partial \pmb{C}}
  \right] \\
 &= \left[
    J^{-2/3}\left(\pmb{I} - \frac{1}{3}I_1 \pmb{C}^{-1} \right) \quad
    J^{-4/3}\left(
      I_1\pmb{I} - \pmb{C} - \frac{2}{3}I_2\pmb{C}^{-1} \right)\quad
    \frac{1}{2}J\pmb{C}^{-1} \right]
\end{align}

Elastic stiffness in the material frame is given by

\begin{align}
  \mathbb{L} &= 4\frac{\partial^2 W}{\partial\pmb{C}\partial\pmb{C}}
            = 4\frac{\partial}{\partial \pmb{C}}\left( \pmb{A}\cdot\pmb{B}\right) \\
            &= 4\left(\frac{\partial \pmb{A}}{\partial \pmb{C}}\cdot\pmb{B} +
                      \pmb{A}\cdot\frac{\partial \pmb{B}}{\partial \pmb{C}}\right)
\end{align}

where

\begin{equation}
  \frac{\partial \pmb{A}}{\partial \pmb{C}} = \pmb{H} \cdot \pmb{B}, \quad
  \pmb{H} = \begin{bmatrix}
    \frac{\partial^2 W}{\partial \overline{I}_1 \partial\overline{I}_1} &
    \frac{\partial^2 W}{\partial \overline{I}_1 \partial \overline{I}_2} &
    \frac{\partial^2 W}{\partial \overline{I}_1 \partial J} \\
    \frac{\partial^2 W}{\partial \overline{I}_2 \partial \overline{I}_1} &
    \frac{\partial^2 W}{\partial \overline{I}_2 \partial \overline{I}_2} &
    \frac{\partial^2 W}{\partial \overline{I}_2 \partial J} \\
    \frac{\partial^2 W}{\partial J \partial \overline{I}_1} &
    \frac{\partial^2 W}{\partial J \partial \overline{I}_2} &
    \frac{\partial^2 W}{\partial J \partial J}
  \end{bmatrix}
\end{equation}

and

\begin{equation}
  \frac{\partial \pmb{B}}{\partial \pmb{C}} = \begin{Bmatrix}
    \frac{1}{3}J^{-2/3}\left[
      \pmb{I}\pmb{C}^{-1}
      - \pmb{C}^{-1}\pmb{I}
      - I_1\left(
        \pmb{C}^{-1}\odot\pmb{C}{^-1}
        - \frac{1}{3}\pmb{C}^{-1}\pmb{C}{^-1}\right)
    \right] \\
    \frac{2}{3}J^{-4/3}\left[
      \frac{3}{2}\left(\mathbb{I}_1 - \mathbb{I}_2\right)
      + \left(\pmb{C}^{-1}\pmb{C}
        + \pmb{C}\pmb{C}^{-1}
      \right)
      - I_1\left(
        \pmb{C}^{-1}\pmb{I} + \pmb{I}\pmb{C}^{-1}
      \right)
      - I_2\left(
        \pmb{C}^{-1}\odot\pmb{C}^{-1}
        - \frac{2}{3}\pmb{C}^{-1}\pmb{C}^{-1}
      \right)
    \right] \\
    \frac{1}{4} J\left(
      \pmb{C}^{-1}\pmb{C}^{-1} - 2\pmb{C}^{-1}\odot\pmb{C}^{-1}
    \right)
  \end{Bmatrix}
\end{equation}

The operator $\odot$ is defined such that

$$
\mathbb{X}_{ijkl} = \pmb{A} \odot \pmb{B} = (A_{ik} B_{jl} + A_{il} B_{jk}) / 2
$$

### Requirements of Objectivity

The constitutive equations for the hyperelastic material evaluate the stress
directly in the reference configuration. The components of the stress are
identified as the components of the Second Piola-Kirchhoff stress $\pmb{T}$.
The push forward of the Second Piola-Kirchhoff stress gives the Cauchy stress
$\pmb{\sigma}$ in the spatial configuration, as required by most finite
element packages.  The push forward of the corresponding material stiffness
$\mathbb{L}$ does not, however, correspond to the rate of Cauchy stress but
the Truesdell rate of the Cauchy stress.  Furthermore, the rate of Cauchy
stress is not objective, requiring finite element packages to use other
so-called objective stress rates in the incremental solution of the momentum
equation.  In the following sections, it is demonstrated that the push forward
of the material stiffness does not correspond to the rate of Cauchy stress and
equations relating the stiffness corresponding to the Jaumann rate of the
Kirchhoff stress to the push forward of the material stiffness are developed.

The rate of change of Cauchy stress

\begin{align}
  \dot{\pmb{\sigma}} &= \frac{d}{dt}\left(
    \frac{1}{J}\pmb{F}\cdot\pmb{T}\cdot{\pmb{F}}^T
  \right) \\
  &= \frac{1}{J}\left[
    -\frac{\dot{J}}{J}\pmb{F}\cdot\pmb{T}\cdot{\pmb{F}}^T
    + \dot{\pmb{F}}\cdot\pmb{T}\cdot{\pmb{F}}^T
    + \pmb{F}
      \cdot\frac{d\pmb{T}}{d\pmb{E}}{:}\dot{\pmb{E}}
    \cdot{\pmb{F}}^T
   + \pmb{F}\cdot\pmb{T}\cdot\dot{\pmb{F}}^T
  \right]
\end{align}

With the following identities

\begin{equation}
  \mathrm{tr}{\pmb{d}} = \frac{\dot{J}}{J} \quad
  \dot{\pmb{F}} = \pmb{L}\cdot\pmb{F} \quad
  \dot{\pmb{E}} = \pmb{F}^T\cdot\pmb{d}\cdot\pmb{F} \quad
  \dot{\pmb{F}}^T = \pmb{F}^T\cdot\pmb{L}^T
\end{equation}

\begin{align}
  \dot{\pmb{\sigma}} &= \frac{1}{J}\left[
    -\mathrm{tr}{\pmb{d}}\pmb{\sigma}
    + \pmb{L}\cdot\pmb{F}\cdot\pmb{T}\cdot{\pmb{F}}^T
    + \pmb{F}\cdot\mathbb{L}{:}\left(
      \pmb{F}^T\cdot\pmb{d}\cdot\pmb{F}
    \right)\cdot\pmb{F}^T
    + \pmb{F}\cdot\pmb{T}\cdot\pmb{F}^T\cdot\pmb{L}^T
  \right] \\
  &= -\mathrm{tr}{\pmb{d}}\pmb{\sigma}
    + \pmb{L}\cdot\pmb{\sigma}
    + \frac{1}{J}\left(
      \pmb{F}\cdot\pmb{F}\cdot
      \mathbb{L}
      \cdot\pmb{F}^T\cdot\pmb{F}^T
    \right){:}\pmb{d}
    + \pmb{\sigma}\cdot\pmb{L}^T \\
  &= -\mathrm{tr}{\pmb{d}}\pmb{\sigma}
    + \pmb{L}\cdot\pmb{\sigma}
    + \mathbb{C}{:}\pmb{d}
    + \pmb{\sigma}\cdot\pmb{L}^T
\end{align}

rearranging

\begin{align}
  \dot{\pmb{\sigma}}
  - \pmb{\sigma}\cdot\pmb{L}^T
  - \pmb{L}\cdot\pmb{\sigma}
  + \mathrm{tr}{\pmb{d}}\pmb{\sigma}
  &= \mathbb{C}{:}\pmb{d} \\
  \mathring{\pmb{\sigma}} &= \mathbb{C}{:}\pmb{d}
\end{align}

where

\begin{align}
  \mathring{\pmb{\sigma}} = \dot{\pmb{\sigma}}
  - \pmb{\sigma}\cdot\pmb{L}^T
  - \pmb{L}\cdot\pmb{\sigma}
  + \mathrm{tr}{\pmb{d}}\pmb{\sigma}
\end{align}

is the Truesdell rate of the Cauchy stress.  The Truesdell rate of the Cauchy
stress is related to the Lie derivative of the Kirchhoff stress by

\begin{equation}
  \mathring{\pmb{\sigma}} = \frac{1}{J}\pmb{F}\cdot\dot{\pmb{T}}\pmb{F}^T
  = \frac{1}{J}\pmb{F}\cdot\left[\frac{d}{dt}\left(
      J\pmb{F}^{-1}\cdot\pmb{\sigma}\pmb{F}^{-T}
    \right)\right]\cdot\pmb{F}^T
  = \frac{1}{J}\pmb{F}\cdot\left[\frac{d}{dt}\left(
      \pmb{F}^{-1}\cdot\pmb{\tau}\pmb{F}^{-T}
    \right)\right]\cdot\pmb{F}^{T}
  = \frac{1}{J}\mathscr{L}\left({\pmb{\tau}}\right)
\end{equation}

Thus, the push forward of the material time derivative of $\pmb{T}$ is the
Truesdell rate of the Cauchy stress.  Correspondingly, the Truesdell rate of
the Kirchhoff stress is related to the push forward of the material stiffness
$\mathbb{L}$ by

\begin{equation}
  \mathscr{L}\left({\pmb{\tau}}\right) = J\mathbb{C}{:}\pmb{d}
\end{equation}

In many finite element packages, the Jaumann rate of the Kirchhoff stress, and
not the Truesdell rate, is required. Thus, the elastic stiffness must
correspond to the Jaummann rate.. The Jaumann rate of the Kirchhoff stress is
given by

\begin{align}
  \stackrel{\nabla}{\pmb{\tau}} &= {
    \dot{\pmb{\tau}}
    + \pmb{\tau}\cdot\pmb{w}
    - \pmb{w}\cdot\pmb{\tau}
  } \\
  &= \dot{J}\pmb{\sigma}
    + J\dot{\pmb{\sigma}}
    + J\pmb{\sigma}\cdot\pmb{w}
    - \pmb{w}\cdot J\pmb{\sigma} \\
  &= J\left(\mathrm{tr}{\pmb{d}}\pmb{\sigma}
    + \dot{\pmb{\sigma}}
    + \pmb{\sigma}\cdot\pmb{w}
    - \pmb{w}\cdot J\pmb{\sigma}\right) \\
  &= J\mathbb{D}{:}\pmb{d}
\end{align}

where $\mathbb{D}$ is the stiffness corresponding to the Jaumann rate.
Subtracting $\mathring{\pmb{\tau}}$ from $\stackrel{\nabla}{\pmb{\tau}}$, the Jaumann stiffness
can be cast in terms of $\mathbb{C}$

\begin{align}
  \left(\mathbb{D} - \mathbb{C}\right){:}\pmb{d} &=
  \left(\mathrm{tr}{\pmb{d}}\pmb{\sigma}
    + \dot{\pmb{\sigma}}
    + \pmb{\sigma}\cdot\pmb{w}
    - \pmb{w}\cdot\pmb{\sigma}\right)
  -
  \left(\dot{\pmb{\sigma}}
    - \pmb{\sigma}\cdot{\pmb{L}}^T
    - \pmb{L}\cdot\pmb{\sigma}
    + \mathrm{tr}{\pmb{d}}\pmb{\sigma}\right) \\
  &= \pmb{\sigma}\cdot\pmb{w}
  + \pmb{\sigma}\cdot{\pmb{L}}^T
  + \pmb{L}\cdot\pmb{\sigma}
  - \pmb{w}\cdot\pmb{\sigma} \\
  &= \pmb{\sigma}\cdot\pmb{d} + \pmb{d}\cdot\pmb{\sigma} \\
\end{align}

Using indicial notation, and the fact that $\pmb{d}$ and $\pmb{\sigma}$
are symmetric,

\begin{align}
  \left(D_{ijkl} - C_{ijkl}\right)d_{kl}
  &= \sigma_{im}d_{mj} + d_{im}\sigma_{mj} \\
  &= \left(
    \sigma_{ik}\delta_{jl} + \delta_{il}\sigma_{jk}
  \right)d_{kl}
\end{align}

from which

\begin{align}
  \left(
    D_{ijkl}
    - C_{ijkl}
    - \sigma_{ik}\delta_{jl}
    - \delta_{il}\sigma_{jk} \right)d_{kl} = 0_{ij}
\end{align}

Since the preceding equation must hold for all $d_{kl}$, the stiffness
corresponding to the Jaumman rate of the Kirchhoff stress is related to the
stiffness corresponding to the Truesdell rate of the Kirchhoff stress as

\begin{align}
  D_{ijkl} = C_{ijkl} + \sigma_{ik}\delta_{jl}
    + \delta_{il}\sigma_{jk}
\end{align}

Note that the stiffness must be made minor
symmetric

\begin{align}
  D_{ijkl} = C_{ijkl} + \frac{1}{2}\left(
    \sigma_{ik}\delta_{jl} +
    \sigma_{il}\delta_{jk} +
    \delta_{il}\sigma_{jk} +
    \delta_{ik}\sigma_{jl}
  \right)
\end{align}

In symbolic notation,

$$
\mathbb{D} = \mathbb{C} + \pmb{\sigma}\odot\pmb{I} + \pmb{I}\odot\pmb{\sigma}
$$

which is the correct stiffness corresponding to the Jaumann rate of the Kirchhoff
stress, in terms of the push forward of the material stiffness.  Equations relating $\mathbb{C}$ to stiffnesses corresponding to other objective rates are derived similarly.

## Matmodlab Implementation

A polynomial material model is implemented as a standard Matmodlab material in `matmodlab2/materials/polyhyper.py` as a subclass of `Material` class.  The model defines the following (required) attributes and methods

- `name`: name by which the model is referenced
- `eval`: method the updates the material state

Additionally, several helper functions are imported from various locations in Matmodlab:

- `matmodlab.materials.tensor`

   - `symsq`: Computes $\pmb{F}^T{\cdot}\pmb{F}$ and returns the result as a 6D first order tensor
   - `det`, `inv`: Computes the determinant and inverse of second order tensors (stored as matrix or array)
   - `invariants`: Computes the invariants of a second order tensor
   - `push`: Performs the push forward operation (for both second and fourth order tensors)
   - `dyad`: Computes the dyadic product of two vectors or second order tensors
   - `symshuffle`: Computes the product $X_{ijkl} = .5 (A_{ik} B_{jl} + A_{il} B_{jk}$)
   - `II1`, `II5`: Fourth order identity tensors
   - `I6`: Identity tensor stored as an array of length 6

The model implementation can be viewed by executing the following cell.

```python
%pycat ../matmodlab2/materials/polyhyper.py
```

```python
%pylab inline
from bokeh.io import output_notebook
from bokeh.plotting import *
from matmodlab2 import *
output_notebook()

def create_figure(**kwargs):
    TOOLS = ('resize,crosshair,pan,wheel_zoom,box_zoom,'
             'reset,box_select,lasso_select')
    TOOLS = 'resize,pan,wheel_zoom,box_zoom,reset,save'
    return figure(tools=TOOLS, **kwargs)
```

    Populating the interactive namespace from numpy and matplotlib
    Setting up the Matmodlab notebook environment

    Loading BokehJS ...

## Verification

### Uniaxial Stress

For an incompressible isotropic material, uniaxial stress is produced by the following deformation state

$$
[F] = \begin{bmatrix}\lambda && \\ & \frac{1}{\sqrt{\lambda}} & \\ & & \frac{1}{\sqrt{\lambda}} \end{bmatrix}
$$

The stress difference $\sigma_{\text{axial}} - \sigma_{\text{lateral}}$ is given by

```python
from sympy import Symbol, Matrix, Rational, symbols, sqrt
lam = Symbol('lambda')
F = Matrix(3, 3, [lam, 0, 0, 0, 1/sqrt(lam), 0, 0, 0, 1/sqrt(lam)])
B = Matrix(3, 3, F.dot(F.T))
Bsq = Matrix(3, 3, B.dot(B))
I = Matrix(3, 3, lambda i,j: 1 if i==j else 0)
I1 = B.trace()
I2 = ((B.trace()) ** 2 - Bsq.trace()) / 2
J = F.det()
X = J ** Rational(1, 3)
C1, C2, D1 = symbols('C10 C01 D1')
I1B = I1 / X ** 2
I2B = I2 / X ** 4

S = 2 / J * (1 / X ** 2 * (C1 + I1B * C2) * B - 1 / X ** 4 * C2 * Bsq) \
    + (2 / D1 * (J - 1) - 2 * (C1 * I1B + 2 * C2 * I2B) / 3) * I
(S[0,0] - S[1,1]).simplify()
```

    2*C01*lambda - 2*C01/lambda**2 + 2*C10*lambda**2 - 2*C10/lambda

We now exercise the Mooney-Rivlin material model using Matmodlab

```python
# Hyperelastic parameters, D1 set to a large number to force incompressibility
parameters = {'D1': 1.e-5, 'C10': 1e6, 'C01': .1e6}

# stretch to 300%
lam = linspace(.5, 3, 50)

# Set up the simulator
mps = MaterialPointSimulator('test1')
mps.material = PolynomialHyperelasticMaterial(**parameters)

# Drive the *incompressible* material through a path of uniaxial stress by
# prescribing the deformation gradient.
Fij = lambda x: (x, 0, 0, 0, 1/sqrt(x), 0, 0, 0, 1/sqrt(x))
mps.run_step('F', components=Fij(lam[0]), frames=10)
mps.run_step('F', components=Fij(1), frames=1)
mps.run_step('F', components=Fij(lam[-1]), frames=20)

# plot the analytic solution and the simulation
p = create_figure(x_axis_label='Stretch', y_axis_label='Stress')
C10, C01 = parameters['C10'], parameters['C01']

# analytic solution for true and engineering stress
s = 2*C01*lam - 2*C01/lam**2 + 2*C10*lam**2 - 2*C10/lam

# plot the analytic solutions
p.line(lam, s, color='blue', legend='True', line_width=2)
p.line(lam, s/lam, color='green', legend='Engineering', line_width=2)

lam_ = np.exp(mps.get('E.XX'))
ss = mps.get('S.XX') - mps.get('S.ZZ')
p.circle(lam_, ss, color='orange', legend='Simulation, True')
p.circle(lam_, ss/lam_, color='red', legend='Simulation, Engineering')
p.legend.location = 'top_left'

show(p)

# check the actual solutions
assert abs(amax(ss) - amax(s)) / amax(s) < 1e-6
assert abs(amin(ss) - amin(s)) / amin(s) < 1e-6
```

```python

```


- - - -
# Mechpy
a mechanical engineer's toolbox

To view this notebook, use the [nbviewer](http://nbviewer.jupyter.org/github/nagordon/mechpy/blob/master/mechpy.ipynb)
- - - -

- - - -
### Modules

## 1) [Statics](#Statics)
* [Example 1: A simple supported beam with shear-bending plots](#Statics-Example-1)
* [Example 2: Vector calculation method to calculate 3-D moments](#Statics-Example-2)
* [Distributed Loads Calculations](#Distributed-Loads-Calculations)

## 2) [Materials](#Materials)
* [composite mechanics](#Composite-Mechanics)

## 3) Kinematics
* [double_pendulum](http://matplotlib.org/examples/animation/double_pendulum_animated.html)

## 4) Dynamics
* [dynamics](#Dynamics)
* [Python Vibration Codes](http://vibrationdata.com/software.htm)
* [Dynamics Notes](#Dynamics-Vibrations-Notes)

## Appendix A: [Engineering Mathematics with Python](#Engineering-Mathematics-with-Python)
[Differential Equations](#Differential-Equations)
[Linear Algebra](#Linear-Algebra)
[Signal Processing](#Signal-Processing)
[Finite Element Method](#Finite-Element-Method)
* [solids FEM example](#FEM-Example-1)

[Units](#Units)

- - - -
- - - -

## References
Hibbler - Statics
Hibbler - Mechanics of Materials

## Python Initilaization with module imports

```python
# setup
import numpy as np
import sympy as sp
import scipy
from pprint import pprint
sp.init_printing(use_latex='mathjax')

import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (12, 8)  # (width, height)
plt.rcParams['font.size'] = 14
plt.rcParams['legend.fontsize'] = 16
from matplotlib import patches

get_ipython().magic('matplotlib')  # seperate window
get_ipython().magic('matplotlib inline') # inline plotting
```

    Using matplotlib backend: Qt4Agg

- - - -
# Statics
[index](#Mechpy)
- - - -

The sum of the forces is zero
$$
\Sigma F_x =0 , \Sigma F_y =0 , \Sigma F_z =0
$$
The sum of the moments is zero
$$
\Sigma M_x =0 , \Sigma M_y =0 , \Sigma M_z =0
$$

Dot Product

$$
\vec{A} \bullet \vec{B} = ABcos\left(\theta\right)= A_xB_x + A_yB_y+A_zB_z
$$

Cross-Product

$$
\vec{C}=\vec{A} \times \vec{B} =
\begin{vmatrix}
    \widehat{i} & \widehat{j} & \widehat{k}\\
    A_{x} & A_{y} & A_{z}\\
    B_{x} & B_{y} & B_{z}
\end{vmatrix}
$$

Moment of Force
$$
M_0 = Fd \\
\vec{M_0}=\vec{r}\times \vec{F} =
\begin{vmatrix}
    \widehat{i} & \widehat{j} & \widehat{k}\\
    r_{x} & r_{y} & r_{z}\\
    F_{x} & F_{y} & F_{z}
\end{vmatrix}
$$

Moment of Force about a Specified axis
$$
\vec{M_a}=\vec{u}\bullet\vec{r}\times \vec{F} =
\begin{vmatrix}
    u_{x} & u_{y} & u_{z} \\
    r_{x} & r_{y} & r_{z} \\
    F_{x} & F_{y} & F_{z}
\end{vmatrix}
$$

### Statics-Example 1
#### A simple supported beam with shear-bending plots

```python
from mechpy.statics import simple_support
simple_support()
```

### Statics-Example 2
### Vector calculation method to calculate 3-D moments

$
\vec{M_{R_0}}=\Sigma \left( \vec{r} \times \vec{F} \right) = \vec{r_A}\times\vec{F_1} +\vec{r_A}\times\vec{F_2} + \vec{r_B}\times\vec{F_3}
$

```python
from mechpy.statics import moment_calc
moment_calc()
```

### Distributed Loads Calculations

$
F_R = \Sigma F=\int_L w(x) \,dx  = \int_A dA \,dx
$

example, hibbler, pg 187

$$
F_R=\int_A dA \, =  \int_{0}^{2} \, 60x^2 \, dx = 160 N
$$

$$
 \overline{x} = \frac{\int_A x \, dA}{\int_A dA} =  \frac{\int_{0}^{2} x60x^2 \, dx}{\int_{0}^{2} \, 60x^2 \, dx} = \frac{240}{160}
$$

```python
x = sp.var('x')
w = 60*x**2# N/m
sp.plot(w, (x,0,2));
```

```python
w.subs(x,2)
```

$$240$$

```python
sp.Integral(w,(x,0,2))
```

$$\int_{0}^{2} 60 x^{2}\, dx$$

```python
sp.integrate(w)
```

$$20 x^{3}$$

```python
sp.integrate(w,(x,0,2))
```

$$160$$

```python
sp.Integral(x*w,(x,0,2))
```

$$\int_{0}^{2} 60 x^{3}\, dx$$

```python
sp.integrate(w*x)
```

$$15 x^{4}$$

```python
sp.integrate(x*w,(x,0,2))
```

$$240$$

```python
FR= float(sp.integrate(w,(x,0,2)))
xbar = float(sp.integrate(x*w,(x,0,2)))/FR
print('center of load of an exponential distributed load is %f' % xbar)
```

    center of load of an exponential distributed load is 1.500000

```python
#hibbler pg 346

import numpy as np

r = np.array([    0, 3 , 5.25])
F = np.array([-13.5, 0 ,6.376])
M = -np.cross(r,F)

# moments
print('M_x = %f \nM_y = %f \nM_z = %f' % (M[0], M[1], M[2] ))
```

    M_x = -19.128000
    M_y = 70.875000
    M_z = -40.500000

# Materials
[index](#Mechpy)

## Stress and Strain
Stress is a tensor that can be broken into

$$
\overline{\sigma}=\begin{bmatrix}
 \sigma_{xx} & \sigma_{xy} & \sigma_{xz}\\
 \sigma_{yx} & \sigma_{yy} & \sigma_{yz}\\
 \sigma_{zx} & \sigma_{zy} & \sigma_{zz}
 \end{bmatrix}
$$

## Factors of safety
In aerospace, typically 1.2 for civilian aircraft and 1.15 for military

$$FS=\frac{\sigma_{yield}}{\sigma}-1$$

## Fastener Notes and Formulas

Finding the centroid of a bolt with coordinates, $\overline{x},\overline{y}$
$$ \overline{x}=\frac{\sum_{i=1}^{n_b}{A_i x_i} }{\sum_{i=1}^{n_b}{A_i} } \ \ \overline{y}=\frac{\sum_{i=1}^{n_b}{A_i y_i} }{\sum_{i=1}^{n_b}{A_i}}$$

Joint/Polar Moment of Inertia, $r=$ distance from centroid to fastener
$$J= \int{r^2dA}= \sum_{i=1}^{n_b}{A_k r_k^2}$$

Bearing Stress on a bolt
$$\sigma^i_{bearing}=\frac{V_{max}}{Dt}$$

Shear Stress on each bolt i due to shear force
$$\tau_f^i = \frac{P}{\sum_{i=1}^{n_b}{A_i} }$$
Where $A_i=$ the area of ith bolt, $n_b=$number of bolts, and $P=$ shear force

Shear Stress on each bolt i due to moment
$$\tau_t^i = \frac{T r_i}{J} $$

### Modes of failure of fastened Joints
1. Tensile Plate Failure across the net section between rivets/bolts
2. Failure of rivets through shear
3. Compression failure between rivet and plate
4. Edge shear-out at rivet hole
5. Edge tearing at rivet hole

#### 1.

$$\sigma_t =\frac{F_s}{(b-nd)t}$$

#### 2.

#### 3.

#### 4.

#### 5.

## Adhesive Joints

With members, or adherends, joined with adhesives, either the member will fail due to tensile loads or the adhesive will fail in shear.

The simple solution to finding the stress of bonded surfaces is taking the average stress
$$\tau_{avg}=\frac{P}{bL}$$, is not an accurate way to model maximum stress. A good rule of thumb based on the calculations below is
$$\tau_{max}=2.08\tau_{avg}$$

The maximum shearing stress of an adhesive layer, $\tau_{max}$, can be computed as
$$\tau_{max}=K_s\tau_{avg}=K_s\left(\frac{P}{bL_L}\right)$$
with $P$ as applied load, $b$ as the width ofthe adhesive layer, and $L_L$ as the length ofthe adhesive layer. The stress distribution factor, $K_s$, can be defined as $K_s=\frac{cL}{tanh(CL/2)}$ where $c=\sqrt{\frac{2G_a}{Et_mt_a}}$, where the shear modulus, $G_a=\frac{\tau}{\gamma}$, and $E$ as the modulus of elasticity.

The max shearing stress, $\tau_{max}$ in a scarf joint can be found with
$$\tau_{max}=K_s\tau_{avg}=K_s\left[ \frac{Pcos\theta}{\left(\frac{bt}{sin\theta} \right)  } \right] = K_s\left(  \frac{P}{bt} sin\theta cos\theta \right)$$
where $t$ is the thickness of the adherend members and $\theta=tan^{-1}\frac{t}{L_s}$ is the scarf angle

*Mechanical Design of Machine Elements and Machines by Collins, Jack A., Busby, Henry R., Staab, George H. (2009)*

```python

```

```python
%matplotlib inline
```

```python
## Bolted Joint Example

# fastener Location
from mechpy.design import fastened_joint
fx = [0,1,2,3,0,1,2,3]
fy = [0,0,0,0,1,1,1,1]
# Force magnitude(x,y)
P = [-300,-500]
# Force location
l = [2,1]
df = fastened_joint(fx, fy, P, l)

df.plot(kind='scatter', x='x', y='y');
#df.plot(style='o', x='x', y='y')
plt.plot(df.xbar[0],df.ybar[0],'*')
df
#ax = plt.gca()
#ax.arrow(l[0], l[1], Pnorm[0],Pnorm[1], head_width=0.05, head_length=0.1, fc='k', ec='k')
#x.arrow(xbar, ybar, Pnorm[0],0, head_width=0.05, head_length=0.1, fc='k', ec='k')
#ax.arrow(xbar, ybar, 0,Pnorm[1], head_width=0.05, head_length=0.1, fc='k', ec='k')
```

## Composite Mechanics
[index](#Mechpy)

```python
from mechpy.math import T3rot, T6rot
from mechpy.composites import qbar_transformtion, composite_plate, vary_ply_direction_plot, laminate_gen
```

```python
from mechpy.math import T2rot
T2rot(45)
```

    array([[ 0.52532199, -0.85090352],
           [ 0.85090352,  0.52532199]])

```python
T6rot(45,45,45)
```

    matrix([[ 0.25      ,  0.25      ,  0.5       , -0.35355339, -0.35355339,
              0.25      ],
            [ 0.02144661,  0.72855339,  0.25      ,  0.4267767 , -0.0732233 ,
             -0.125     ],
            [ 0.72855339,  0.02144661,  0.25      , -0.0732233 ,  0.4267767 ,
             -0.125     ],
            [-0.25      , -0.25      ,  0.5       ,  0.35355339,  0.35355339,
              0.75      ],
            [ 0.85355339, -0.14644661, -0.70710678,  0.35355339, -0.35355339,
              0.35355339],
            [-0.14644661,  0.85355339, -0.70710678, -0.35355339,  0.35355339,
              0.35355339]])

```python
from IPython.html.widgets import *

plt.figure(figsize=(12,8))
x = [-1,1, 0,-1,]
y = [-1,-1,1,-1]
xy = np.array([x,y])
plt.xlim([-11.1,11.1])
plt.ylim([-11.1,11.1])
xyR = np.dot(T2rot(30),xy)
#plt.plot(xyR[0,:],xyR[1,:])
def rot2(th, xt,yt,zt):
    xyR = np.dot(T2rot(th),xy*zt)
    xyR[0,:]+=xt
    xyR[1,:]+=yt
    plt.plot(xyR[0,:],xyR[1,:])
    plt.axis('square')
    plt.xlim([-11.1,11.1])
    plt.ylim([-11.1,11.1])
    plt.show()

interact(rot2, th=(0,np.pi,np.pi/90), yt=(1,10,1), xt=(1,10,1), zt=(1,10,1));
```

```python
print(T6rot(45,45,45))
```

    [[ 0.25        0.25        0.5        -0.35355339 -0.35355339  0.25      ]
     [ 0.02144661  0.72855339  0.25        0.4267767  -0.0732233  -0.125     ]
     [ 0.72855339  0.02144661  0.25       -0.0732233   0.4267767  -0.125     ]
     [-0.25       -0.25        0.5         0.35355339  0.35355339  0.75      ]
     [ 0.85355339 -0.14644661 -0.70710678  0.35355339 -0.35355339  0.35355339]
     [-0.14644661  0.85355339 -0.70710678 -0.35355339  0.35355339  0.35355339]]

```python
vary_ply_direction_plot()
```

```python
qbar_transformtion()
```

```python
help(laminate_gen)
```

    Help on function laminate_gen in module mechpy.composites:

    laminate_gen(lamthk=1.5, symang=[45, 0, 90], plyratio=2.0, matrixlayers=False, nonsym=False)
        ## function created to quickly create laminates based on given parameters
        lamthk=1.5    # total #thickness of laminate
        symang = [45,0,90, 30]  #symmertic ply angle
        plyratio=2.0  # lamina/matrix
        matrixlayers=False  # add matrix layers between lamina plys
        nonsym=False    # symmetric

        #ply ratio can be used to vary the ratio of thickness between a matrix ply
             and lamina ply. if the same thickness is desired, plyratio = 1,
             if lamina is 2x as thick as matrix plyratio = 2

```python
laminate_gen()
```

$$\left ( \left [ 0.25, \quad 0.25, \quad 0.25, \quad 0.25, \quad 0.25, \quad 0.25\right ], \quad \left [ 45, \quad 0, \quad 90, \quad 90, \quad 0, \quad 45\right ], \quad \left [ 3.0, \quad 3.0, \quad 3.0, \quad 3.0, \quad 3.0, \quad 3.0\right ], \quad \left [ 45, \quad 0, \quad 90, \quad 90, \quad 0, \quad 45\right ]\right )$$

```python
composite_plate()
```

```python
from ipywidgets import IntSlider
IntSlider()

```

```python
# Principal Stresses

sx  = 63.66
sy  = 0
sz  = 0
txy = 63.66
txz = 0
tyz = 0

S = np.matrix([[sx, txy, txz],
            [txy, sy, tyz],
            [txy, txz, sz]])

print(S)
```

    [[ 63.66  63.66   0.  ]
     [ 63.66   0.     0.  ]
     [ 63.66   0.     0.  ]]

```python
principal_stresses = np.linalg.eigvals(S)
print(principal_stresses)
```

    [   0.          -39.34404372  103.00404372]

```python
import sympy as sp
from sympy.abc import tau, sigma
#s,s11,s22,s33,s12 = sp.var('s,s11,s22,s33,s12')
s,s11,s22,s33,s12,s13 = sp.symbols('sigma, sigma11,sigma22,sigma33,sigma12,sigma13')
s = sp.Matrix([[s11,s12,0],[s12,s22,0],[0,0,s33]])
s
```

$$\left[\begin{matrix}\sigma_{11} & \sigma_{12} & 0\\\sigma_{12} & \sigma_{22} & 0\\0 & 0 & \sigma_{33}\end{matrix}\right]$$

```python
s**2
```

$$\left[\begin{matrix}\sigma_{11}^{2} + \sigma_{12}^{2} & \sigma_{11} \sigma_{12} + \sigma_{12} \sigma_{22} & 0\\\sigma_{11} \sigma_{12} + \sigma_{12} \sigma_{22} & \sigma_{12}^{2} + \sigma_{22}^{2} & 0\\0 & 0 & \sigma_{33}^{2}\end{matrix}\right]$$

```python
s.eigenvals()  # hmm looks familiar
```

$$\left \{ \sigma_{33} : 1, \quad \frac{\sigma_{11}}{2} + \frac{\sigma_{22}}{2} - \frac{1}{2} \sqrt{\sigma_{11}^{2} - 2 \sigma_{11} \sigma_{22} + 4 \sigma_{12}^{2} + \sigma_{22}^{2}} : 1, \quad \frac{\sigma_{11}}{2} + \frac{\sigma_{22}}{2} + \frac{1}{2} \sqrt{\sigma_{11}^{2} - 2 \sigma_{11} \sigma_{22} + 4 \sigma_{12}^{2} + \sigma_{22}^{2}} : 1\right \}$$

```python
s1 = s.subs(s11,2.2).subs(s22,3).subs(s33,sp.pi).subs(s12,7.3)
s1
```

$$\left[\begin{matrix}2.2 & 7.3 & 0\\7.3 & 3 & 0\\0 & 0 & \pi\end{matrix}\right]$$

```python
# or
s2 = s.evalf(subs={s11:2.2, s22:3, s33:sp.pi, s12:7.3})
s2
```

$$\left[\begin{matrix}2.2 & 7.3 & 0\\7.3 & 3.0 & 0\\0 & 0 & 3.14159265358979\end{matrix}\right]$$

```python
s1.eigenvals()
```

$$\left \{ \pi : 1, \quad \frac{13}{5} + \frac{\sqrt{5345}}{10} : 1, \quad - \frac{\sqrt{5345}}{10} + \frac{13}{5} : 1\right \}$$

```python
s2.eigenvals()
```

$$\left \{ \frac{314159265358979}{100000000000000} : 1, \quad \frac{13}{5} + \frac{\sqrt{5345}}{10} : 1, \quad - \frac{\sqrt{5345}}{10} + \frac{13}{5} : 1\right \}$$

```python
s2.inv()
```

$$\left[\begin{matrix}-0.0642535874919684 & 0.156350396230456 & 0\\0.156350396230456 & -0.0471192974941101 & 0\\0 & 0 & 0.318309886183791\end{matrix}\right]$$

```python
C = sp.symbols('C1:100')
C
```

$$\left ( C_{1}, \quad C_{2}, \quad C_{3}, \quad C_{4}, \quad C_{5}, \quad C_{6}, \quad C_{7}, \quad C_{8}, \quad C_{9}, \quad C_{10}, \quad C_{11}, \quad C_{12}, \quad C_{13}, \quad C_{14}, \quad C_{15}, \quad C_{16}, \quad C_{17}, \quad C_{18}, \quad C_{19}, \quad C_{20}, \quad C_{21}, \quad C_{22}, \quad C_{23}, \quad C_{24}, \quad C_{25}, \quad C_{26}, \quad C_{27}, \quad C_{28}, \quad C_{29}, \quad C_{30}, \quad C_{31}, \quad C_{32}, \quad C_{33}, \quad C_{34}, \quad C_{35}, \quad C_{36}, \quad C_{37}, \quad C_{38}, \quad C_{39}, \quad C_{40}, \quad C_{41}, \quad C_{42}, \quad C_{43}, \quad C_{44}, \quad C_{45}, \quad C_{46}, \quad C_{47}, \quad C_{48}, \quad C_{49}, \quad C_{50}, \quad C_{51}, \quad C_{52}, \quad C_{53}, \quad C_{54}, \quad C_{55}, \quad C_{56}, \quad C_{57}, \quad C_{58}, \quad C_{59}, \quad C_{60}, \quad C_{61}, \quad C_{62}, \quad C_{63}, \quad C_{64}, \quad C_{65}, \quad C_{66}, \quad C_{67}, \quad C_{68}, \quad C_{69}, \quad C_{70}, \quad C_{71}, \quad C_{72}, \quad C_{73}, \quad C_{74}, \quad C_{75}, \quad C_{76}, \quad C_{77}, \quad C_{78}, \quad C_{79}, \quad C_{80}, \quad C_{81}, \quad C_{82}, \quad C_{83}, \quad C_{84}, \quad C_{85}, \quad C_{86}, \quad C_{87}, \quad C_{88}, \quad C_{89}, \quad C_{90}, \quad C_{91}, \quad C_{92}, \quad C_{93}, \quad C_{94}, \quad C_{95}, \quad C_{96}, \quad C_{97}, \quad C_{98}, \quad C_{99}\right )$$

```python

```

```python

```

```python

```

```python
from mechpy.math import ode1
ode1()
```

    0 [ 1.] 1.0
    0.1 [ 0.1353353] 0.135335283237
    0.2 [ 0.01831564] 0.0183156388887
    0.30000000000000004 [ 0.00247875] 0.00247875217667
    0.4 [ 0.00033546] 0.000335462627903
    0.5 [  4.53999512e-05] 4.53999297625e-05
    0.6 [  6.14421567e-06] 6.14421235333e-06
    0.7 [  8.31529279e-07] 8.31528719104e-07
    0.7999999999999999 [  1.12535271e-07] 1.12535174719e-07
    0.8999999999999999 [  1.52300000e-08] 1.52299797447e-08
    0.9999999999999999 [  2.06116213e-09] 2.06115362244e-09
    1.0999999999999999 [  2.78957342e-10] 2.78946809287e-10

## Dynamics Vibrations Notes
**Jul 1, 2015**

## Introduction

modal analysis is similar to frequency analysis. In frequency analysis a complex signal is resolved into a set of simple sine waves with individual frequency and amplitude and phase parameters. In modal analysis, a complex deflection pattern of a vibrating structure is resolved into a set of simple mode shapes with the same individual parameters.

## Structural Dynamics Background

Most systems are actually multiple degrees of freedom (MDOF) and have some non-linearity, but can be simplified with a superposition of SDOF linear systems

Newtons law states that acceleration is a function of the applied force and the mass of the object, or
$$
[inertial forces] + [Dissipative forces] + [Restoring Forces] = [External Forces] \\
m\ddot{x} + c\dot{x} + kx = f(t)  \\
\zeta<1 is\ underdamped  \\
$$

some other dynamic characteristics are
$$
\omega = frequency  \\
\zeta = damping     \\
\{\phi\} = mode shape  \\
\omega^{2}_{n}=\frac{k}{m} = natural frequency \\
\zeta = \frac{c}{\sqrt{2km}}    \\
H(\omega)=Frequency\ Response \\
\phi(\omega)=Phase
$$

## Damping Model

Where there is energy dissipation, there is damping. The system can be broken into the system inputs/excitation, a system G(s), and the output response, in Laplace or space

The transfer function is a math model defining the input/output relationship of a physical system. Another definition is the Laplace transform ( $\mathcal{L}$) of the output divided by the Laplace transform of the input.

The frequency response function (FRF) is defined in a similar manner such that FRF is the fourier transform ($ \mathcal{F} $) of the input divided by the fourier transform of the output

$$
Transfer\ Function=\frac{Output}{Input} \\
G(s) = \frac{Y(s)}{X(s)}
$$

## Modal Testing

These relationships can be further explained by the modal test process. The measurements taken during a test are frequency response function measurements. The parameter estimation routines are curve fits in the Laplace domain and result in transfer functions.

Frequency Response Matrix

$$
\begin{bmatrix}
 H_{11} & H_{12} & \cdots & H_{1n} \\
 H_{21} & H_{22} & \cdots & H_{2n} \\
 \vdots  & \vdots  & \ddots & \vdots  \\
 H_{n1} & H_{n2} & \cdots & H_{nn}
\end{bmatrix}
$$

## Random Notes

The signal-analysis approach is done by measuring vibration with accelerometers and determine the frequency spectrum. The other moethod is a system -analysis where a dual-channel FFT anlayzer is used to measure the ratio of the response to the input giving the frequency response function (FRF)

a modal model allows the analysis of structural systems

a mode shape is a deflection-pattern associated with a particular modal frequency or pole location. It is not tangible or easily observed. The actual displacement of the structure will be a sum of all the mode shapes. A harmonic exitation close to the modal frequency, 95% of the displacement may be due to the particular modeshape

Modal Descriptions Assumes Linearity
 * Superposition of the component waves will result in the final wave. A swept sinosoid will give the same result as a broadband excitation

 * Homogeneity is when a measured FRF is independent of excitation level

 * Reciprocity implies that the FRF measured between any two DOFs is independent of which of them for excitation or response

 * small deflections - cannot predict buckling or catastrophic failure

 * casual - the structure will not vibrate before it is excited

 * stable - the vibrations will die out when the excitation is removd

 * time-invariant - the dynamic characteristics will not change during the measurments

## The Lumped-Parameter Model and Modal Theory

[Physical Coordinates] = [Modal Matrix][Modal Coordinates]

$$
[x] = [\phi][q]
$$

## Keywords and Notations

$$
m=mass        \\
k=stiffness   \\
c = damping coefficient  \\
c_c = critical damping coefficient  \\
$$

## Finite-Element-Method
[index](#Mechpy)

### FEM-Example-1

```python
from mechpy.fem import cst_fem
cst_fem(structure='9node')
```

## Linear Algebra with Python
[index](#Mechpy)

Python's numpy package allows python, a generic computing language to perform powerful mathematical calculations. Although python's math syntax is not as obvious as MATLAB's, the functionality is comparable. This document is designed to be an intro to that syntax

Some references

http://nbviewer.ipython.org/github/carljv/cython_testing/blob/master/cython_linalg.ipynb

We can either use scipy, which includes numpy,
http://docs.scipy.org/doc/

or use numpy directly
http://docs.scipy.org/doc/numpy/

Since there are many ways to solve linear algebra problems, (eg Octave/Matlab, julia, scipy, numpy)
I tend to prefer the most matlabesc approaches due to the ubiquity of Matlab and the simplicity of the syntax, which frankly, python suffers with.

The major difference between arrays and matrices in python is that arrays are n-dimensions, where matrices are only up to 2-dimensions
m

```python
import numpy as np
from scipy import linalg
```

Pythons list is a generic data storage object. it can be easily extended to a numpy array, which is specialized for numerical and scientific computation

```python
np.zeros((5,3))
```

    array([[ 0.,  0.,  0.],
           [ 0.,  0.,  0.],
           [ 0.,  0.,  0.],
           [ 0.,  0.,  0.],
           [ 0.,  0.,  0.]])

```python
np.array([[1,2],[3,4]])
```

    array([[1, 2],
           [3, 4]])

```python
np.matrix(np.zeros((5,3)))
```

    matrix([[ 0.,  0.,  0.],
            [ 0.,  0.,  0.],
            [ 0.,  0.,  0.],
            [ 0.,  0.,  0.],
            [ 0.,  0.,  0.]])

```python
np.matrix([[1,2],[3,4]])
```

    matrix([[1, 2],
            [3, 4]])

```python
# Matrix multiplication can be achieved using the dot method
i = [[1,0,0],[0,1,0],[0,0,1]]  # identiy matrix
a = [[4,3,1],[5,7,2],[2,2,2]]
np.dot(i,a)

```

    array([[4, 3, 1],
           [5, 7, 2],
           [2, 2, 2]])

```python
#Or, matrix multiplication can be done if a matrix is explicitly defined
np.matrix(i)*np.matrix(a)
```

    matrix([[4, 3, 1],
            [5, 7, 2],
            [2, 2, 2]])

```python
# Notice, when arrays are mutliplied, we get the dot product
np.array(i)*np.array(a)
```

    array([[4, 0, 0],
           [0, 7, 0],
           [0, 0, 2]])

```python
# convert an array to a matrix
m = np.matrix(a)
m
```

    matrix([[4, 3, 1],
            [5, 7, 2],
            [2, 2, 2]])

```python
m.T  # transpose
```

    matrix([[4, 5, 2],
            [3, 7, 2],
            [1, 2, 2]])

```python
m.I  # inverse
```

    matrix([[ 0.55555556, -0.22222222, -0.05555556],
            [-0.33333333,  0.33333333, -0.16666667],
            [-0.22222222, -0.11111111,  0.72222222]])

```python
m**2
```

    matrix([[33, 35, 12],
            [59, 68, 23],
            [22, 24, 10]])

```python
np.array(a)**2
```

    array([[16,  9,  1],
           [25, 49,  4],
           [ 4,  4,  4]])

```python
m
```

    matrix([[4, 3, 1],
            [5, 7, 2],
            [2, 2, 2]])

```python
m[:,2]
```

    matrix([[1],
            [2],
            [2]])

```python
m[2,:]
```

    matrix([[2, 2, 2]])

```python
m[:2,:2]
```

    matrix([[4, 3],
            [5, 7]])

```python
m[1:,1:]
```

    matrix([[7, 2],
            [2, 2]])

## Sympy Linear Algebra

```python
# import sympy
import sympy as sp
#from sympy.mpmath import *
```

```python
x = sp.Symbol('x')   # x = var('x')
M = sp.Matrix([[2,x],[x,3]])
M
```

$$\left[\begin{matrix}2 & x\\x & 3\end{matrix}\right]$$

```python
M.eigenvals()
```

$$\left \{ - \frac{1}{2} \sqrt{4 x^{2} + 1} + \frac{5}{2} : 1, \quad \frac{1}{2} \sqrt{4 x^{2} + 1} + \frac{5}{2} : 1\right \}$$

```python
M.eigenvects()
```

$$\left [ \left ( - \frac{1}{2} \sqrt{4 x^{2} + 1} + \frac{5}{2}, \quad 1, \quad \left [ \left[\begin{matrix}- \frac{x}{\frac{1}{2} \sqrt{4 x^{2} + 1} - \frac{1}{2}}\\1\end{matrix}\right]\right ]\right ), \quad \left ( \frac{1}{2} \sqrt{4 x^{2} + 1} + \frac{5}{2}, \quad 1, \quad \left [ \left[\begin{matrix}- \frac{x}{- \frac{1}{2} \sqrt{4 x^{2} + 1} - \frac{1}{2}}\\1\end{matrix}\right]\right ]\right )\right ]$$

```python
M.eigenvects()[1][0]
```

$$\frac{1}{2} \sqrt{4 x^{2} + 1} + \frac{5}{2}$$

```python
Mval = M.eigenvects()[1][0]
Mval.evalf(subs={x:3.14})
```

$$5.67955971794838$$

```python
print(sp.latex(M))
```

    \left[\begin{matrix}2 & x\\x & 3\end{matrix}\right]

copy and paste into markdown

$ \left[\begin{matrix}2 & x\\x & 3\end{matrix}\right] $

## Signal Processing
Page 174 Introduction for python for Science - David Pine

```python
import numpy as np
from scipy import fftpack
import matplotlib.pyplot as plt
width = 2.0
freq = 0.5
t = np.linspace(-10, 10, 101) # linearly space time array
g = np.exp(-np.abs(t)/width)*np.sin(2.0 * np.pi * freq * t)
dt = t[1]-t[0] # increment between times in time array
G = fftpack.fft(g) # FFT of g
f = fftpack.fftfreq(g.size, d=dt) # frequenies f[i] of g[i]
f = fftpack.fftshift(f) # shift frequencies from min to max
G = fftpack.fftshift(G) # shift G order to coorespond to f
fig = plt.figure(1, figsize=(8,6), frameon=False)
ax1 = fig.add_subplot(211)
ax1.plot(t, g)
ax1.set_xlabel('t')
ax1.set_ylabel('g(t)')
ax2 = fig.add_subplot(212)
ax2.plot(f, np.real(G), color='dodgerblue', label='real part')
ax2.plot(f, np.imag(G), color='coral', label='imaginary part')
ax2.legend()
ax2.set_xlabel('f')
ax2.set_ylabel('G(f)')
plt.show()
```

# Engineering Mathematics with Python
[index](#Mechpy)

```python
from numpy import *
```

```python
r_[1:11]
```

    array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

```python
arange(1,11)
```

    array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

```python
linspace(1,10,10)
```

    array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.])

# Units
[index](#Mechpy)

```python
from mechunits import uc1
# uc1 uses sympy
```

```python
uc1(1.0,'psi','kPa')
```

    1.00 psi = 6.89  kPa

```python
uc1(1.0,'newton','pound')
```

    1.00 newton = 0.22  pound

```python
from mechunits import uc2
# uses pint
uc2(17.5,'lbf','newton')
```

    17.5 lbf = 77.84387826705874 newton

77.84387826705874 newton

```python
uc2(300,'pascal','psi')
```

    300 pascal = 0.043511321319062775 psi

0.043511321319062775 psi

```python
from mechunits import in_mm
in_mm()
```

         0 in - 0.000000 in - 0.000000 mm
      1/16 in - 0.062500 in - 1.587500 mm
       1/8 in - 0.125000 in - 3.175000 mm
      3/16 in - 0.187500 in - 4.762500 mm
       1/4 in - 0.250000 in - 6.350000 mm
      5/16 in - 0.312500 in - 7.937500 mm
       3/8 in - 0.375000 in - 9.525000 mm
      7/16 in - 0.437500 in - 11.112500 mm
       1/2 in - 0.500000 in - 12.700000 mm
      9/16 in - 0.562500 in - 14.287500 mm
       5/8 in - 0.625000 in - 15.875000 mm
     11/16 in - 0.687500 in - 17.462500 mm
       3/4 in - 0.750000 in - 19.050000 mm
     13/16 in - 0.812500 in - 20.637500 mm
       7/8 in - 0.875000 in - 22.225000 mm
     15/16 in - 0.937500 in - 23.812500 mm
         1 in - 1.000000 in - 25.400000 mm

```python
%load_ext version_information
```

```python
%version_information pydy, numpy, scipy, matplotlib
```

Software	Version

Python	2.7.11 64bit [MSC v.1500 64 bit (AMD64)]

IPython	4.0.3

OS	Windows 10 10.0.10586

pydy	0.3.1

numpy	1.10.1

scipy	0.16.0

matplotlib	1.5.1

Sun Jan 31 19:55:35 2016 Pacific Standard Time

```python

```


```python
import os
import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# plotly and cufflinks
import plotly.offline as py
import plotly.graph_objs as go
import cufflinks as cf
py.init_notebook_mode(connected=True)
cf.go_offline()

import xarray as xr
```

# Linear Difference Equations
based on James D. Hamilton

$\newcommand{\bm}[1]{\boldsymbol{#1}}$

## 2nd-order Difference Equation

Standard form:
$$y_t = \phi_1 y_{t-1} + \phi_2 y_{t-2} + w_t$$

### Vector Form
\begin{equation}
\bm{\xi}_t = \bm{F}\ \bm{\xi}_{t-1} + \bm{v_t}
\end{equation}
where
\begin{equation}
\bm{\xi}_t =
\left[
\begin{array}{c}
y_t \\
y_{t-1}
\end{array}
\right],
\quad
\bm{F} =
\left[
\begin{array}{cc}
\phi_1 & \phi_2 \\
1 & 0
\end{array}
\right],
\quad
\bm{v}_t =
\left[
\begin{array}{c}
w_t \\
0
\end{array}
\right],
\end{equation}

### Using Lag Operator

$$(1 - \phi_1 L - \phi_2 L^2)y_t = w_t$$

### Eigenvalues:

$|F - \lambda I| = 0$ lead to
$$\lambda^2 - \phi_1 \lambda - \phi_2 = 0$$

* Let $\lambda_1$ and $\lambda_2$ be eignevalues. Then,
$$(\lambda - \lambda_1) (\lambda - \lambda_2) = 0$$
* Factorise the difference equation using the eigenvalues
$$(1 - \lambda_1 L)(1-\lambda_2 L) = w_t$$
* Not to be confused with $1- \phi_1 z - \phi_2 z^2 = 0$, obtained by replacing $L$ by $z$. $z$ and $\lambda$ are reciprocal each other:
$$z = \lambda^{-1}$$

## Eigenvalue Diagram

* The dynamics of a 2nd-order difference equation can be summarised in terms of the eigenvalues as in the figure below.
* The eigenvalues are district unless $\phi_1^2 + 4\phi^2 = 0$.
* It is reproduction of Figure 1.5 of Hamilton.

```python
def fn_q(p):
    return -1.0/4*p**2

# basic variables
p_l = np.arange(-3,-2,0.01)
p_m = np.arange(-2,2,0.01)
p_r = np.arange(2,3,0.01)
p_lm = np.hstack((p_l,p_m))
p_mr = np.hstack((p_m,p_r))
p_lmr = np.hstack((p_l,p_m,p_r))

# plots
xrange = [np.min(p_lmr), np.max(p_lmr)]
yrange = xrange
pwidth = 800

ago = [] # collection of plots
ago.append(go.Scatter(x=np.hstack((p_l,p_mr,np.flip(p_lmr))),
                      y=np.hstack((fn_q(p_l),1+p_mr,3*np.ones(p_lmr.shape))),fill='toself',fillcolor='rgba(150,150,100,0.5)'))
ago.append(go.Scatter(x=np.hstack((p_lm,p_r,np.flip(p_lmr))),
                      y=np.hstack((1-p_lm,fn_q(p_r),3*np.ones(p_lmr.shape))),fill='toself',fillcolor='rgba(150,100,150,0.5)'))
ago.append(go.Scatter(x=np.hstack((p_l,p_m,p_r,np.flip(p_lmr))),
                      y=np.hstack((fn_q(p_l),-np.ones(p_m.shape),fn_q(p_r),-3*np.ones(p_lmr.shape))),fill='toself',fillcolor='rgba(100,150,150,0.5)'))
ago.append(go.Scatter(x=p_lmr, y=fn_q(p_lmr)))
ago.append(go.Scatter(x=p_lm, y=1.0 - p_lm))
ago.append(go.Scatter(x=p_mr, y=1.0 + p_mr))
ago.append(go.Scatter(x=p_m, y=-np.ones(p_m.shape)))

annotations=[
    dict(x=-2.3,y=-1,text=r'(-2,-1)',showarrow=False),
    dict(x=+2.3,y=-1,text=r'(2,-1)',showarrow=False),
    dict(x=0,y=1.1,text=r'(0,1)',showarrow=False),
    dict(x=0.5,y=0.5,text=r'$\phi_2=1-\phi_1$',ax=40),
    dict(x=-0.5,y=0.5,text=r'$\phi_2=1+\phi_1$',ax=-40),
    dict(x=2.5,y=-2.5**2/4,text=r'$\phi_1^2+4\phi_2=0$', ax=-60, ay=20),
    dict(x=-2,y=1.5,text=r'$\text{real eigenvalues}\\ \quad\quad\lambda_2<-1\\$',showarrow=False),
    dict(x=2,y=1.5,text=r'$\text{real eigenvalues}\\ \quad\quad\lambda_1>1\\$',showarrow=False),
    dict(x=0,y=0.3,text=r'$\text{real eigenvalues}\\ \quad|\lambda_j|<1\\$',showarrow=False),
    dict(x=0,y=-0.5,text=r'$\text{complex eigenvalues}\\ \quad\quad|\lambda_j|<1\\$',showarrow=False),
    dict(x=0,y=-2,text=r'$\text{complex eigenvalues}\\ \quad\quad|\lambda_j|>1\\$',showarrow=False),
    dict(x=xrange[1],y=0,text=r'$\phi_1$',showarrow=False),
    dict(x=0,y=2,text=r'$\phi_2$',showarrow=False),
]

# update with selected font
[d.update(dict(font=dict(family='Courier New, monospace', size=16))) for d in annotations]

# layout and plot
layout = go.Layout(height=pwidth, width=pwidth,
                   xaxis=dict(range=xrange,showgrid=False, showticklabels=False),
                   yaxis=dict(range=yrange,showgrid=False, showticklabels=False),
                   annotations=annotations,
                   showlegend=False,
                   hovermode=False)
py.iplot(go.Figure(data=ago, layout=layout))
```

# Autoregressive Processes

## AR(1)
\begin{equation}
Y_t = c + \phi Y_{t-1} + \sigma \epsilon_t
\end{equation}

```python
def fn_ar1(c,phi,sigma,y0, eps1):
    return c + phi * y0 + sigma * eps1

c = 0.1
phi = 0.99
sigma = 0.1

n_scen = 10
dim_scen = 'scen'
y0 = 0.0
dim_t = 't'
n_t = 100

aY = [y0 * xr.DataArray(np.ones(n_path), dims=(dim_scen))]
for n in range(n_t):
    y0 = aY[-1]
    eps1 = xr.DataArray(np.random.normal(0.0, 1.0, n_path), dims=(dim_scen))
    y1 = fn_ar1(c, phi, sigma, y0, eps1)
    aY.append(y1)

T = xr.DataArray(np.arange(0,n_t+1), dims=(dim_t))
Y = xr.concat(aY, dim=(dim_t))

```

```python

```

```python
[x for x in Y.get_index(dim_scen)]
```

```python
Y[dict(scen=0)]
```

```python

```