```python
from IPython.core.display import Math
import sympy as sym
from sympy.interactive import printing
printing.init_printing()

```

```python
#F,m,a,g,x,y, Vx, Vy, t = sym.symbols('F m a g x y t')
#F = a*m
```

```python
#import sys
from IPython.core.display import Math
from sympy import symbols, latex, cos, sin
#from prettyPy import prettyPy as pp
display(Math(latex('\\mathbf{F=m*a}') ))
display(Math(latex('\\mathbf{F_{g}=-m*g} ') ))
display(Math(latex('\\mathbf{F_{g}= F_{x}+F_{y}} = 0 + {(-g)}') ))
display(Math(r'{F_x=}\frac{d^2x}{dt^2} = 0 '))
display(Math(r'{F_y=}\frac{d^2y}{dt^2} = -g'))
display(Math(latex('\\mathbf{Vx_{0}=}'r'\frac{dx}{dt}') ))
display(Math(latex('\\mathbf{V_{x0}t+}'r'{x_{0}}={x_{t}}' ) ))
display(Math(r'{-g=}\frac{d^2y}{dt^2}'))
display(Math(r'{-gt+V_{y0}t=}\frac{dy}{dt}'))
display(Math(r'$\frac{-g}{2}{t^2}+V_{y0}t+{y_0 =} y_t$'))
display(Math(r'${x_0 =} {y_0 = 0}$'))
display(Math(latex('\\mathbf{x_{t}}={V_{x0}t}' ) ))
display(Math(r'${y_t}=\frac{-g}{2}{t^2}+V_{y0}t $'))
```

$$\mathbf{F=m*a}$$

$$\mathbf{F_{g}=-m*g} $$

$$\mathbf{F_{g}= F_{x}+F_{y}} = 0 + {(-g)}$$

$${F_x=}\frac{d^2x}{dt^2} = 0 $$

$${F_y=}\frac{d^2y}{dt^2} = -g$$

$$\mathbf{Vx_{0}=}\frac{dx}{dt}$$

$$\mathbf{V_{x0}t+}{x_{0}}={x_{t}}$$

$${-g=}\frac{d^2y}{dt^2}$$

$${-gt+V_{y0}t=}\frac{dy}{dt}$$

$$\frac{-g}{2}{t^2}+V_{y0}t+{y_0 =} y_t$$

$${x_0 =} {y_0 = 0}$$

$$\mathbf{x_{t}}={V_{x0}t}$$

$${y_t}=\frac{-g}{2}{t^2}+V_{y0}t $$

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

```python
m = 0.43 # [kg]
x0 = 0  #[m]
y0 = 0  #[m]
vx0 = 15*np.sqrt(3) #[m/s]
vy0 = 15 # [m/s]
g = 9.81 # [m/s^2]
display(Math(latex('\\mathbf{Vx_{0}=30*cos(30)}') ))
display(Math(latex('\\mathbf{Vx_{0}=}'r'\frac{30}{2}' r'\sqrt{3}=' r'15\sqrt{3} {  m/s}') ))
display(Math(latex('\\mathbf{Vy_{0}=30*sin(30)} ') ))
display(Math(latex('\\mathbf{Vy_{0}=}'r'\frac{30}{2}='  r'{15 m/s}') ))

```

$$\mathbf{Vx_{0}=30*cos(30)}$$

$$\mathbf{Vx_{0}=}\frac{30}{2}\sqrt{3}=15\sqrt{3} {  m/s}$$

$$\mathbf{Vy_{0}=30*sin(30)} $$

$$\mathbf{Vy_{0}=}\frac{30}{2}={15 m/s}$$

```python
dt = 0.001 #[s]
%matplotlib notebook
t = np.arange(0, 3.05, dt)
plt.figure()
plt.plot(vx0*t, -g/2*t**2+vy0*t)
plt.xlim(0,80)
plt.ylim(0,12)
plt.grid()
plt.show()
plt.title(' Exercício 1')
plt.xlabel('x (m)', size = 20)
plt.ylabel('y (m)', size = 20)
```

    <IPython.core.display.Javascript object>

    Text(0,0.5,'y (m)')

```python
display(Math(r'\frac{dvx}{dt} = 0 '))
display(Math(r'$\frac{dx}{dt} = Vx_t'))
display(Math(r'\frac{dvy}{dt} = -g '))
display(Math(r'$\frac{dy}{dt} = Vy_t'))
display(Math(r'$\frac{dx}{dt}= \lim_{\Delta t \to 0} \frac{x(t+\Delta t) -x(t)}{\Delta t}$'))
display(Math(r'$\frac{dx}{dt}  \approx \frac{x(t+\Delta t) -x(t)}{\Delta t} \to x(t+\Delta t) \approx x(t) + \Delta t \frac{dx}{dt}$'))
```

$$\frac{dvx}{dt} = 0 $$

$$\frac{dx}{dt} = Vx_t$$

$$\frac{dvy}{dt} = -g $$

$$\frac{dy}{dt} = Vy_t$$

$$\frac{dx}{dt}= \lim_{\Delta t \to 0} \frac{x(t+\Delta t) -x(t)}{\Delta t}$$

$$\frac{dx}{dt}  \approx \frac{x(t+\Delta t) -x(t)}{\Delta t} \to x(t+\Delta t) \approx x(t) + \Delta t \frac{dx}{dt}$$

```python
x = x0
y = y0
vx = vx0
vy = vy0
r = np.array([x,y])
while y >= 0:
    dxdt = vx
    x = x + dt * dxdt
    dydt = vy
    y = y + dt * dydt
    dvxdt = 0
    vx = vx + dt * dvxdt
    dvydt = -g
    vy = vy + dt * dvydt
    r = np.vstack((r, np.array([x,y])))
```

```python
plt.figure()
plt.plot(r[:,0],r[:,1])
plt.xlim(0,80)
plt.ylim(0,12)
plt.grid()
plt.show()
plt.title(' Exercício 2')
plt.xlabel('x (m)', size = 20)
plt.ylabel('y (m)', size = 20)
plt.show()
```

    <IPython.core.display.Javascript object>

```python
display(Math(r'$\frac{d^2x}{dt^2} = 0 ' r'          ;\frac{dy}{dt} = -g '  r'    ;\frac{dvx}{dt} = 0 '))
display(Math(r'x_t=x(t-dt)+ dt*dx/dt') )
display(Math(r'$\frac{d^2x}{dt^2} = 0 ' r'          ;\frac{dy}{dt} = -g '  r'    ;\frac{dvx}{dt} = 0 '))
display(Math(r'$\frac{dx}{dt} = Vx'  r'             ;\frac{dy}{dt} = Vy'))
display(Math(r'$\frac{dy}{dt}$'))
```

$$\frac{d^2x}{dt^2} = 0           ;\frac{dy}{dt} = -g     ;\frac{dvx}{dt} = 0 $$

$$x_t=x(t-dt)+ dt*dx/dt$$

$$\frac{d^2x}{dt^2} = 0           ;\frac{dy}{dt} = -g     ;\frac{dvx}{dt} = 0 $$

$$\frac{dx}{dt} = Vx             ;\frac{dy}{dt} = Vy$$

$$\frac{dy}{dt}$$

```python
from IPython.display import Image
Image("C:\\2tri2018\\Ex2.png")
```

```python
display(Math(r'{\lambda^2}+\frac{b}{m}\lambda = 0 '))
```

$${\lambda^2}+\frac{b}{m}\lambda = 0 $$

```python
display(Math(r'{raizes \ \lambda=0} \ e \ {\ \lambda=\frac{-b}{m}}'))
```

$${raizes \ \lambda=0} \ e \ {\ \lambda=\frac{-b}{m}}$$

```python
display(Math(r'{x_{n1}t = Ae^{0t} = A}'))
display(Math(r'{x_{n2}t = Be^{\frac{-b}{m}t}}'))
```

$${x_{n1}t = Ae^{0t} = A}$$

$${x_{n2}t = Be^{\frac{-b}{m}t}}$$

```python
display(Math(r'{x(t) =A+ Be^{\frac{-b}{m}t}}'))
```

$${x(t) =A+ Be^{\frac{-b}{m}t}}$$

```python
display(Math(r'{x_0 =A+ B = 0}'))
```

$${x_0 =A+ B = 0}$$

```python
display(Math(r'{vx_0 =\frac{dx_0}{dt}=\frac{-Bb}{m}e^{\frac{-b}{m}0}}'))
display(Math(r'{B=\frac{-vx_0m}{b} \to {A=\frac{vx_0m}{b}}}'))
```

$${vx_0 =\frac{dx_0}{dt}=\frac{-Bb}{m}e^{\frac{-b}{m}0}}$$

$${B=\frac{-vx_0m}{b} \to {A=\frac{vx_0m}{b}}}$$

```python
display(Math(r'{x(t) =\frac{vx_0m}{b}-\frac{vx_0m}{b} e^{\frac{-b}{m}t}}'))
```

$${x(t) =\frac{vx_0m}{b}-\frac{vx_0m}{b} e^{\frac{-b}{m}t}}$$

```python
display(Math(r'{x(t) =\frac{vx_0m}{b}(1 -e^{\frac{-b}{m}t})}'))
```

$${x(t) =\frac{vx_0m}{b}(1 -e^{\frac{-b}{m}t})}$$

```python
display(Math(r'$\frac{d^2y}{dt^2} = -\frac{b}{m} \frac{dy}{dt}'))
```

$$\frac{d^2y}{dt^2} = -\frac{b}{m} \frac{dy}{dt}$$

```python
display(Math(r'{y_n(t) =A+ Be^{-\frac{b}{m}t}}'))
display(Math(r'$\frac{d^2y}{dt^2} = -\frac{b}{m} \frac{dy_t}{dt} -g'))
display(Math(r'$\frac{dy_t}{dt}= -\frac{mg}{b}'))
display(Math(r'$y(t)= A+B-\frac{mg}{b}t'))
```

$${y_n(t) =A+ Be^{-\frac{b}{m}t}}$$

$$\frac{d^2y}{dt^2} = -\frac{b}{m} \frac{dy_t}{dt} -g$$

$$\frac{dy_t}{dt}= -\frac{mg}{b}$$

$$y(t)= A+B-\frac{mg}{b}t$$

```python
display(Math(r'{y(t) =A+ Be^{-\frac{b}{m}t}}-\frac{mg}{b}t'))
```

$${y(t) =A+ Be^{-\frac{b}{m}t}}-\frac{mg}{b}t$$

```python
display(Math(r'{y_0 =A+ B = 0}'))
```

$${y_0 =A+ B = 0}$$

```python
display(Math(r'{vy_0 =\frac{dy_0}{dt}=\frac{-Bb}{m}-{\frac{-mg}{b}}}'))
display(Math(r'{B=-\frac{m^2g}{b^2} - \frac{vy_0m}{b}}'))
display(Math(r'{A=\frac{m^2g}{b^2} +\frac{vy_0m}{b}}'))
```

$${vy_0 =\frac{dy_0}{dt}=\frac{-Bb}{m}-{\frac{-mg}{b}}}$$

$${B=-\frac{m^2g}{b^2} - \frac{vy_0m}{b}}$$

$${A=\frac{m^2g}{b^2} +\frac{vy_0m}{b}}$$

```python
display(Math(r'{y(t) =(\frac{m^2g}{b^2} +\frac{vy_0m}{b})+ (-\frac{m^2g}{b^2} - \frac{vy_0m}{b})e^{-\frac{b}{m}t}}-\frac{mg}{b}t'))
display(Math(r'{y(t) =(\frac{m^2g}{b^2} +\frac{vy_0m}{b}) (1 - e^{-\frac{b}{m}t})}-\frac{mg}{b}t'))
```

$${y(t) =(\frac{m^2g}{b^2} +\frac{vy_0m}{b})+ (-\frac{m^2g}{b^2} - \frac{vy_0m}{b})e^{-\frac{b}{m}t}}-\frac{mg}{b}t$$

$${y(t) =(\frac{m^2g}{b^2} +\frac{vy_0m}{b}) (1 - e^{-\frac{b}{m}t})}-\frac{mg}{b}t$$

```python
b = 0.006 # [Ns/m]
t = np.arange(0, 3.05, 0.01)
plt.figure()
plt.plot(vx0*m/b*(1-np.exp(-b/m*t)),
         (vy0*m/b+g*m**2/b**2)*(1-np.exp(-b/m*t)) - g*m/b*t)
plt.xlim(0,80)
plt.ylim(0,12)
plt.title(' Exercício 3 - Analítico')
plt.xlabel('x (m)', size = 20)
plt.ylabel('y (m)', size = 20)
plt.grid()
plt.show()
```

    <IPython.core.display.Javascript object>

```python
display(Math(r'$\frac{d^2x}{dt^2} = -\frac{b}{m} \frac{dx}{dt}'))
display(Math(r'$\frac{d^2y}{dt^2} = -\frac{b}{m} \frac{dy}{dt}-g'))

```

$$\frac{d^2x}{dt^2} = -\frac{b}{m} \frac{dx}{dt}$$

$$\frac{d^2y}{dt^2} = -\frac{b}{m} \frac{dy}{dt}-g$$

```python
display(Math(r'$\frac{dv_x}{dt} = -\frac{b}{m} v_x'))
display(Math(r'$\frac{dx}{dt} =  v_x'))
display(Math(r'$\frac{dv_y}{dt} = -\frac{b}{m} v_y - g'))
display(Math(r'$\frac{dv}{dt} =  v_y'))
display(Math(r'$\frac{dx}{dt}  \approx \frac{x(t+\Delta t) -x(t)}{\Delta t} \to x(t+\Delta t) \approx x(t) + \Delta t \frac{dx}{dt}$'))
```

$$\frac{dv_x}{dt} = -\frac{b}{m} v_x$$

$$\frac{dx}{dt} =  v_x$$

$$\frac{dv_y}{dt} = -\frac{b}{m} v_y - g$$

$$\frac{dv}{dt} =  v_y$$

$$\frac{dx}{dt}  \approx \frac{x(t+\Delta t) -x(t)}{\Delta t} \to x(t+\Delta t) \approx x(t) + \Delta t \frac{dx}{dt}$$

```python
#m = 0.43 # [kg]
#x0 = 0  #[m]
#y0 = 0  #[m]
#vx0 = 15*np.sqrt(3) #[m/s]
#vy0 = 15 # [m/s]
#g = 9.81 # [m/s^2]
#dt = 0.001 #[s]
#%matplotlib notebook
#t = np.arange(0, 3.05, dt)
x = x0
y = y0
vx = vx0
vy = vy0
r = np.array([x,y])
while y >= 0:
    dxdt = vx
    x = x + dt * dxdt
    dydt = vy
    y = y + dt * dydt
    dvxdt = -b/m*vx
    vx = vx + dt * dvxdt
    dvydt = -g - b/m*vy
    vy = vy + dt * dvydt
    r = np.vstack((r, np.array([x,y])))
```

```python
plt.figure()
plt.plot(r[:,0],r[:,1])
plt.xlim(0,80)
plt.ylim(0,12)
plt.title(' Exercício 4 - numérico')
plt.xlabel('x (m)', size = 20)
plt.ylabel('y (m)', size = 20)
plt.grid()
plt.show()
```

    <IPython.core.display.Javascript object>

```python
from IPython.display import Image
Image("C:\\2tri2018\\Ex4a.png")
```

```python
from IPython.display import Image
Image("C:\\2tri2018\\Ex4b.png")
```

```python
x = x0
y = y0
vx = vx0
vy = vy0
r = np.array([x,y])
while y >= 0:
    dxdt = vx
    x = x + dt * dxdt
    dydt = vy
    y = y + dt * dydt
    dvxdt = -b/m*np.sqrt(vx**2+vy**2)*vx
    vx = vx + dt * dvxdt
    dvydt = -b/m*np.sqrt(vx**2+vy**2)*vy - g
    vy = vy + dt * dvydt
    r = np.vstack((r, np.array([x,y])))

plt.figure()
plt.plot(r[:,0],r[:,1])
plt.xlim(0,80)
plt.ylim(0,12)
plt.title(' Exercício Modelo')
plt.xlabel('x (m)', size = 20)
plt.ylabel('y (m)', size = 20)
plt.grid()
plt.show()
```

    <IPython.core.display.Javascript object>

```python
from IPython.display import Image
Image("C:\\2tri2018\\Ex4b.png")
```

```python
display(Math(latex('SOLOÇÃO__NUMÉRICA') ))
display(Math(r'$\frac{dv_x}{dt} = -\frac{b}{m} \frac{1}{\sqrt{vx^2 + vy^2}} v_x'))
display(Math(r'$\frac{dx}{dt} =  v_x'))
display(Math(r'$\frac{dv_y}{dt} = -\frac{b}{m} \frac{1}{\sqrt{vx^2 + vy^2}} v_y - g'))
display(Math(r'$\frac{dv}{dt} =  v_y'))
```

$$SOLOÇÃO__NUMÉRICA$$

$$\frac{dv_x}{dt} = -\frac{b}{m} \frac{1}{\sqrt{vx^2 + vy^2}} v_x$$

$$\frac{dx}{dt} =  v_x$$

$$\frac{dv_y}{dt} = -\frac{b}{m} \frac{1}{\sqrt{vx^2 + vy^2}} v_y - g$$

$$\frac{dv}{dt} =  v_y$$

```python
x = x0
y = y0
vx = vx0
vy = vy0
r = np.array([x,y])
while y >= 0:
    dxdt = vx
    x = x + dt * dxdt
    dydt = vy
    y = y + dt * dydt
    dvxdt = -(b/m)*(1/np.sqrt(vx**2+vy**2))*vx
    vx = vx + dt * dvxdt
    dvydt = -(b/m)*(1/np.sqrt(vx**2+vy**2))*vy - g
    vy = vy + dt * dvydt
    r = np.vstack((r, np.array([x,y])))

plt.figure()
plt.plot(r[:,0],r[:,1])
plt.xlim(0,80)
plt.ylim(0,12)
plt.title(' Exercício Modelo')
plt.xlabel('x (m)', size = 20)
plt.ylabel('y (m)', size = 20)
plt.grid()
plt.show()
```

    <IPython.core.display.Javascript object>

```python

```


# Complex vibration modes

Complex vibration modes arise in experimental research and numerical simulations when non proportional damping is adopted. In such cases a state space formulation of the second order differential dynamic equilibrium equation is the preferred way to adress the problem.

This notebook is inspired in one of [Pete Avitabile's Modal Space articles](http://macl.caeds.eng.uml.edu/umlspace/mspace.html), namely the one discussing the difference between [complex modes and real normal modes](http://sem.org/ArtDownLoad/msmj02.pdf). Additional information about state space formulation in structural dynamics can be found for example [here](http://dspace.mit.edu/bitstream/handle/1721.1/38777/35332523.pdf).

## Table of contents

[Preamble](#Preamble)

[Dynamic equilibrium equation](#Dynamic-equilibrium-equation)

[State space formulation](#State-space-formulation)

[Dynamic system setup](#Dynamic-system-setup)

[Undamped system](#Undamped-system)

[Proportionally damped system](#Proportionally-damped-system)

[Non proportionally damped system](#Non-proportionally-damped-system)

[Conclusions](#Conclusions)

[Odds and ends](#Odds-and-ends)

## Preamble

We will start by setting up the computational environment for this notebook. Furthermore, we will need numpy and scipy for the numerical simulations and matplotlib for the plots:

```python
import sys
import numpy as np
import scipy as sp
import matplotlib as mpl

print('System: {}'.format(sys.version))
print('numpy version: {}'.format(np.__version__))
print('scipy version: {}'.format(sp.__version__))
print('matplotlib version: {}'.format(mpl.__version__))
```

    System: 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]
    numpy version: 1.12.1
    scipy version: 0.19.0
    matplotlib version: 2.0.2

We will also need a couple of specific modules and a litle "IPython magic" to show the plots:

```python
from numpy import linalg as LA
import matplotlib.pyplot as plt

%matplotlib inline
```

[Back to top](#top)

## Dynamic equilibrium equation

In structural dynamics the second order differential dynamic equilibrium equation can be written in terms of generalized coordinates (d[isplacement]) and their first (v[elocity]) and second (a[cceleration]) time derivatives:

\begin{equation}
\mathbf{M} \times \mathbf{a(t)} + \mathbf{C} \times \mathbf{v(t)} + \mathbf{K} \times \mathbf{d(t)} = \mathbf{F(t)}
\end{equation}

where:

> $\mathbf{M}$ is the mass matrix
> $\mathbf{C}$ is the damping matrix
> $\mathbf{K}$ is the stiffness matrix
> $\mathbf{a(t)}$ is the acceleration vector
> $\mathbf{v(t)}$ is the velocity vector
> $\mathbf{d(t)}$ is the displacement vector
> $\mathbf{F(t)}$ is the force input vector

All these matrices are of size $NDOF \times NDOF$, where $NDOF$ is the number of generalized degrees of freedom of the dynamic system.

[Back to top](#top)

## State space formulation

In a [state space formulation](http://en.wikipedia.org/wiki/State-space_representation) the second order differential dynamic equilibrium equation is turned into a system of first order differential dynamic equilibrium equations:

\begin{equation}
\begin{matrix}
\mathbf{\dot{x}(t)} = \mathbf{A} \cdot \mathbf{x(t)} + \mathbf{B} \cdot \mathbf{u(t)} \\
\mathbf{y(t)} = \mathbf{C} \cdot \mathbf{x(t)} + \mathbf{D} \cdot \mathbf{u(t)}
\end{matrix}
\end{equation}

where

> $\mathbf{A}$ is the system matrix
> $\mathbf{B}$ is the imput matrix
> $\mathbf{C}$ is the output matrix
> $\mathbf{D}$ is the feedthrough matrix
> $\mathbf{x(t)}$ is the state vector
> $\mathbf{y(t)}$ is the output vector
> $\mathbf{u(t)}$ is the input vector

The state vector, of size $2 \times NDOF$ by $1$, has the following form:

\begin{equation}
\mathbf{x(t)} = \left[ \begin{matrix}
\mathbf{u(t)} \\
\mathbf{\dot{u}(t)}
\end{matrix} \right]
\end{equation}

The system matrix, of size $2 \times NDOF$ by $2 \times NDOF$, is built using the M, C and K matrices:

\begin{equation*}
\mathbf{A} = \left[ \begin{matrix}
\mathbf{0} & \mathbf{I} \\
-\mathbf{M}^{-1} \cdot \mathbf{K} & -\mathbf{M}^{-1} \cdot \mathbf{C}
\end{matrix} \right]
\end{equation*}

The loading matrix, of size $2 \times NDOF$ by $1$, is composed of 0's and 1's.

[Back to top](#top)

## Dynamic system setup

In this example we will use the folowing mass and stiffness matrices:

```python
MM = np.matrix(np.diag([2,3]))
print(MM)
```

    [[2 0]
     [0 3]]

```python
KK = np.matrix([[2, -1],[-1, 1]])
print(KK)
```

    [[ 2 -1]
     [-1  1]]

Let us perform the eigenanalysis of the (undamped) second order differential dynamic equilibrium equation for later comparison of results:

```python
W2, F1 = LA.eig(LA.solve(MM,KK)) # eigenanalysis
ix = np.argsort(np.absolute(W2)) # sort eigenvalues in ascending order
W2 = W2[ix] # sorted eigenvalues
F1 = F1[:,ix] # sorted eigenvectors
print(np.round_(W2, 4))
print(np.round_(F1, 4))
```

    [ 0.1396  1.1937]
    [[ 0.5025  0.9325]
     [ 0.8646 -0.3613]]

The angular frequencies are computed as the square root of the eigenvalues:

```python
print(np.sqrt(W2))
```

    [ 0.37365812  1.09257171]

The modal vectors, the columns of the modal matrix, have unit norm:

```python
print(LA.norm(F1, axis=0))
```

    [ 1.  1.]

[Back to top](#top)

## Undamped system

In the undamped system, the damping matrix is all zeros:

```python
C0 = np.matrix(np.zeros_like(MM))
print(C0)
```

    [[0 0]
     [0 0]]

The system matrix is the following:

```python
A = np.bmat([[np.zeros_like(MM), np.identity(MM.shape[0])], [LA.solve(-MM,KK), LA.solve(-MM,C0)]])
print(A)
```

    [[ 0.          0.          1.          0.        ]
     [ 0.          0.          0.          1.        ]
     [-1.          0.5         0.          0.        ]
     [ 0.33333333 -0.33333333  0.          0.        ]]

Performing the eigenanalysis on this matrix yields the following complex valued results:

```python
w0, v0 = LA.eig(A)
ix = np.argsort(np.absolute(w0))
w0 = w0[ix]
v0 = v0[:,ix]
print(np.round_(w0, 4))
print(np.round_(v0, 4))
```

    [ 0.+0.3737j  0.-0.3737j -0.+1.0926j -0.-1.0926j]
    [[ 0.4707+0.j      0.4707-0.j     -0.0000-0.6296j -0.0000+0.6296j]
     [ 0.8099+0.j      0.8099-0.j      0.0000+0.2439j  0.0000-0.2439j]
     [-0.0000+0.1759j -0.0000-0.1759j  0.6878+0.j      0.6878-0.j    ]
     [-0.0000+0.3026j -0.0000-0.3026j -0.2665-0.j     -0.2665+0.j    ]]

As we can see, the eigenvalues come in complex conjugate pairs. Therefore we can take for instance only the ones in the [upper half-plane](http://en.wikipedia.org/wiki/Upper_half-plane):

```python
print(np.round_(w0[[0,2]], 4))
```

    [ 0.+0.3737j -0.+1.0926j]

In this case, since damping is zero, the real part of the complex eigenvalues is also zero (apart from [round-off errors](http://en.wikipedia.org/wiki/Round-off_error)) and the imaginary part is equal to the angular frequency computed previously for the dynamic system:

```python
print(w0[[0,2]].imag)
```

    [ 0.37365812  1.09257171]

The columns of the modal matrix, the modal vectors, also come in conjugate pairs. Each vector has unit norm as in the dynamic system:

```python
print(LA.norm(v0[:,[0,2]], axis=0))
```

    [ 1.  1.]

Moreover, we can check that the modal matrix is composed of four blocks, each with $NDOF \times NDOF$ dimension. Some column reordering is necessary in order to match both modal matrices:

```python
AA = v0[:2,[0,2]]
AB = AA.conjugate()
BA = np.multiply(AA,w0[[0,2]])
BB = BA.conjugate()
v0_new = np.bmat([[AA, AB], [BA, BB]])
print(np.round_(v0_new[:,[0,2,1,3]], 4))
```

    [[ 0.4707+0.j      0.4707-0.j     -0.0000-0.6296j -0.0000+0.6296j]
     [ 0.8099+0.j      0.8099-0.j      0.0000+0.2439j  0.0000-0.2439j]
     [-0.0000+0.1759j -0.0000-0.1759j  0.6878+0.j      0.6878-0.j    ]
     [ 0.0000+0.3026j  0.0000-0.3026j -0.2665+0.j     -0.2665-0.j    ]]

To help visualize the complex valued modal vectors we will do a polar plot of the corresponding amplitudes and angles:

```python
fig, ax = plt.subplots(1, 2, subplot_kw=dict(polar=True))
for mode in range(2):
    ax[mode].set_title('Mode #{}'.format(mode+1))
    for dof in range(2):
        r = np.array([0, np.absolute(v0[dof,2*mode])])
        t = np.array([0, np.angle(v0[dof,2*mode])])
        ax[mode].plot(t, r, 'o-', label='DOF #{}'.format(dof+1))
plt.legend(loc='lower left', bbox_to_anchor=(1., 0.))
plt.show()
```

[Back to top](#top)

## Proportionally damped system

In a proportionally damped system, the damping matrix is proportional to the mass and stiffness matrices:

\begin{equation*}
\mathbf{C} = \alpha \times \mathbf{M} + \beta \times \mathbf{K}
\end{equation*}

Let us assume $\alpha$ to be 0.1 and $\beta$ to be 0.1. This yields the following damping matrix:

```python
C1 = 0.1*MM+0.1*KK
print(C1)
```

    [[ 0.4 -0.1]
     [-0.1  0.4]]

This damping matrix is orthogonal because the mass and stiffness matrices are also orthogonal:

```python
print(np.round_(F1.T*C1*F1, 4))
```

    [[ 0.3131  0.    ]
     [ 0.      0.4674]]

The system matrix is the following:

```python
A = np.bmat([[np.zeros_like(MM), np.identity(MM.shape[0])], [LA.solve(-MM,KK), LA.solve(-MM,C1)]])
print(A)
```

    [[ 0.          0.          1.          0.        ]
     [ 0.          0.          0.          1.        ]
     [-1.          0.5        -0.2         0.05      ]
     [ 0.33333333 -0.33333333  0.03333333 -0.13333333]]

The eigenanalysis yields the eigenvalues and eigenvectors:

```python
w1, v1 = LA.eig(A)
ix = np.argsort(np.absolute(w1))
w1 = w1[ix]
v1 = v1[:,ix]
print(np.round_(w1, 4))
print(np.round_(v1, 4))
```

    [-0.0570+0.3693j -0.0570-0.3693j -0.1097+1.0871j -0.1097-1.0871j]
    [[-0.4707+0.j     -0.4707-0.j      0.0632+0.6264j  0.0632-0.6264j]
     [-0.8099+0.j     -0.8099-0.j     -0.0245-0.2427j -0.0245+0.2427j]
     [ 0.0268-0.1738j  0.0268+0.1738j -0.6878+0.j     -0.6878-0.j    ]
     [ 0.0461-0.2991j  0.0461+0.2991j  0.2665-0.j      0.2665+0.j    ]]

As we can see, the eigenvalues come in complex conjugate pairs. Let us take only the ones in the [upper half-plane](http://en.wikipedia.org/wiki/Upper_half-plane):

```python
print(np.round_(w1[[0,2]], 4))
```

    [-0.0570+0.3693j -0.1097+1.0871j]

These complex eigenvalues can be decomposed into angular frequency and damping coefficient:

```python
zw = -w1.real # damping coefficient time angular frequency
wD = w1.imag # damped angular frequency
zn = 1./np.sqrt(1.+(wD/-zw)**2) # the minus sign is formally correct!
wn = zw/zn # undamped angular frequency
print('Angular frequency: {}'.format(wn[[0,2]]))
print('Damping coefficient: {}'.format(zn[[0,2]]))
```

    Angular frequency: [ 0.37365812  1.09257171]
    Damping coefficient: [ 0.15249507  0.10039217]

The columns of the modal matrix, the modal vectors, also come in conjugate pairs, each vector having unit norm:

```python
print(LA.norm(v1[:,[0,2]], axis=0))
```

    [ 1.  1.]

Moreover, the modal matrix is composed of four blocks, each with $NDOF \times NDOF$ dimension. Some column reordering is necessary in order to match both modal matrices:

```python
AA = v1[:2,[0,2]]
AB = AA.conjugate()
BA = np.multiply(AA,w1[[0,2]])
BB = BA.conjugate()
v1_new = np.bmat([[AA, AB], [BA, BB]])
print(np.round_(v1_new[:,[0,2,1,3]], 4))
```

    [[-0.4707+0.j     -0.4707-0.j      0.0632+0.6264j  0.0632-0.6264j]
     [-0.8099+0.j     -0.8099-0.j     -0.0245-0.2427j -0.0245+0.2427j]
     [ 0.0268-0.1738j  0.0268+0.1738j -0.6878+0.j     -0.6878-0.j    ]
     [ 0.0461-0.2991j  0.0461+0.2991j  0.2665-0.j      0.2665+0.j    ]]

We will visualize again the complex valued modal vectors with a polar plot of the corresponding amplitudes and angles:

```python
fig, ax = plt.subplots(1, 2, subplot_kw=dict(polar=True))
for mode in range(2):
    ax[mode].set_title('Mode #{}'.format(mode+1))
    for dof in range(2):
        r = np.array([0, np.absolute(v1[dof,2*mode])])
        t = np.array([0, np.angle(v1[dof,2*mode])])
        ax[mode].plot(t, r, 'o-', label='DOF #{}'.format(dof+1))
plt.legend(loc='lower left', bbox_to_anchor=(1., 0.))
plt.show()
```

[Back to top](#top)

## Non proportionally damped system

In non proportionally damped systems the damping matrix is not proportional neither to the mass matrix nor the stiffness matrix. Let us consider the following damping matrix:

```python
C2 = np.matrix([[0.4, -0.1],[-0.1, 0.1]])
print(C2)
```

    [[ 0.4 -0.1]
     [-0.1  0.1]]

Non proportinal damping carries the fact that the damping matrix is not orthogonal anymore:

```python
print(np.round_(F1.T*C2*F1, 4))
```

    [[ 0.0889  0.0937]
     [ 0.0937  0.4282]]

The system matrix is the following:

```python
A = np.bmat([[np.zeros_like(MM), np.identity(MM.shape[0])], [LA.solve(-MM,KK), LA.solve(-MM,C2)]])
print(A)
```

    [[ 0.          0.          1.          0.        ]
     [ 0.          0.          0.          1.        ]
     [-1.          0.5        -0.2         0.05      ]
     [ 0.33333333 -0.33333333  0.03333333 -0.03333333]]

The eigenanalysis yields the eigenvalues and eigenvectors:

```python
w2, v2 = LA.eig(A)
ix = np.argsort(np.absolute(w2))
w2 = w2[ix]
v2 = v2[:,ix]
print(np.round_(w2, 4))
print(np.round_(v2, 4))
```

    [-0.0162+0.3736j -0.0162-0.3736j -0.1005+1.0872j -0.1005-1.0872j]
    [[ 0.4703-0.0167j  0.4703+0.0167j  0.0580+0.6276j  0.0580-0.6276j]
     [ 0.8099+0.j      0.8099-0.j     -0.0473-0.2382j -0.0473+0.2382j]
     [-0.0014+0.1759j -0.0014-0.1759j -0.6881+0.j     -0.6881-0.j    ]
     [-0.0131+0.3026j -0.0131-0.3026j  0.2638-0.0275j  0.2638+0.0275j]]

As we can see, the eigenvalues come in complex conjugate pairs. Again, let us take only the ones in the [upper half-plane](http://en.wikipedia.org/wiki/Upper_half-plane):

```python
print(np.round_(w2[[0,2]], 4))
```

    [-0.0162+0.3736j -0.1005+1.0872j]

These complex eigenvalues can be decomposed into angular frequency and damping coefficient much like in the propotional damping case:

```python
zw = -w2.real # damping coefficient time angular frequency
wD = w2.imag # damped angular frequency
zn = 1./np.sqrt(1.+(wD/-zw)**2) # the minus sign is formally correct!
wn = zw/zn # undamped angular frequency
print('Angular frequency: {}'.format(wn[[0,2]]))
print('Damping coefficient: {}'.format(zn[[0,2]]))
```

    Angular frequency: [ 0.37392474  1.09179266]
    Damping coefficient: [ 0.04326203  0.09204121]

Again, the columns of the modal matrix, the modal vectors, come in conjugate pairs, and each vector has unit norm:

```python
print(LA.norm(v2[:,[0,2]], axis=0))
```

    [ 1.  1.]

Moreover, the modal matrix is composed of four blocks, each with $NDOF \times NDOF$ dimension. Some column reordering is necessary in order to match both modal matrices:

```python
AA = v2[:2,[0,2]]
AB = AA.conjugate()
BA = np.multiply(AA,w2[[0,2]])
BB = BA.conjugate()
v2_new = np.bmat([[AA, AB], [BA, BB]])
print(np.round_(v2_new[:,[0,2,1,3]], 4))
```

    [[ 0.4703-0.0167j  0.4703+0.0167j  0.0580+0.6276j  0.0580-0.6276j]
     [ 0.8099+0.j      0.8099-0.j     -0.0473-0.2382j -0.0473+0.2382j]
     [-0.0014+0.1759j -0.0014-0.1759j -0.6881-0.j     -0.6881+0.j    ]
     [-0.0131+0.3026j -0.0131-0.3026j  0.2638-0.0275j  0.2638+0.0275j]]

Once more we will visualize the complex valued modal vectors through a polar plot of the corresponding amplitudes and angles:

```python
fig, ax = plt.subplots(1, 2, subplot_kw=dict(polar=True))
for mode in range(2):
    ax[mode].set_title('Mode #{}'.format(mode+1))
    for dof in range(2):
        r = np.array([0, np.absolute(v2[dof,2*mode])])
        t = np.array([0, np.angle(v2[dof,2*mode])])
        ax[mode].plot(t, r, 'o-', label='DOF #{}'.format(dof+1))
plt.legend(loc='lower left', bbox_to_anchor=(1., 0.))
plt.show()
```

[Back to top](#top)

## Conclusions

Several conclusion can be drawn from this very simple example. First of all, damping changes the vibration frequencies and mode shapes. Furthermore, the polar plots show clearly that:

1. In the undamped system the vibration modes are fully "orthogonal";
2. In the proportionally damped system the vibration modes are no longer "orthogonal";
3. In the non proportionally damped system each DOF has a different phase angle.

[Back to top](#top)

## Odds and ends

This notebook was created by Paulo Xavier Candeias.

[Back to top](#top)


$$\text{Pg. 368 #1, 7, 15, 19, 25, 29, 37, 42, 43, 53} \\
\text{Eric Nguyen} \\
\text{14 Jan 2019}$$

$$\begin{align}
1. \quad y' &= (\ln x)7^x \\
7. \quad y' &= (\ln 7)7^{x^4+2} \cdot 4x^3 \\
15. \quad y' &= \dfrac{1}{\ln 17} \cdot \dfrac{1}{x} \\
19. \quad F'(x) &= \frac{1}{\ln{10}} \cdot \frac{1}{6x - 7} \cdot 6 \\
&= \frac{6}{(6x - 7) \ln 10} \\
25. \quad y' &= 6^x \cdot \left(\frac{d}{dx} \log_7{x}\right) + \left(\frac{d}{dx} 6^x\right) \cdot \log_7{x} \quad \text{Product rule.} \\
&= 6^x \cdot \left(\frac{1}{\ln{7}} \cdot \frac{1}{x}\right) + \left(\ln{6} \cdot 6^x\right) \cdot \log_7{x} \\
&= \frac{6^x}{x \cdot \ln{7}} + \ln{6} \cdot 6^x \cdot \log_7{x} \\
29. \quad g'(x) &= \frac{(4x + 1) \cdot \frac{d}{dx} (7^x) - \left[7^x \cdot \frac{d}{dx} (4x + 1)\right]}{(4x + 1)^2} \\
&= \frac{(4x + 1) \cdot (\ln{7} \cdot 7^x) - \left(7^x \cdot 4\right)}{(4x + 1)^2} \\
\text{37a}. \quad V'(t) &= 5200 \cdot \frac{d}{dt} (0.80)^t \\
&= 5200 \cdot \ln{0.80} \cdot (0.80)^t
\end{align}$$

```python
%matplotlib inline
%config InlineBackend.figure_format='retina'
from numpy import power, linspace, log
from matplotlib.pyplot import plot
V = lambda t: 5200 * power(0.80, t)
dV = lambda t: 5200 * log(0.80) * power(0.80, t)
xp = linspace(0, 25, 100)
_ = plot(xp, V(xp))
_ = plot(xp, dV(xp))
```

$37b. \quad V'(t) \text{ shows the rate of change in the office machine's salvage value slowly approaches 0.}$
$42. \quad 5$
$43. \quad 6.9$
$53. \quad y' = \dfrac{1}{\ln 3} \cdot \dfrac{1}{\log{x}} \cdot \dfrac{1}{\ln{10}} \cdot \dfrac{1}{x}$


# Introduction

The first step, and notably the most difficult, is to define the kinematic relationships (i.e. motion) among rigid bodies in the system. Here will we make use of `ReferenceFrame` objects to describe the four frames in the problem, set their orientations, and then construct vectors in the frames that position various important `Point`s. Finally, we will specify the linear and angular velocities of the frames and points using generalized speeds.

# Setup

First, import the necessary functions from SymPy that will allow us to construct time varying vectors in the four reference frames.

```python
from __future__ import print_function, division
from sympy import symbols, simplify
from sympy.physics.mechanics import dynamicsymbols, ReferenceFrame, Point
```

SymPy has a rich printing system. Here we initialize printing so that all of the mathematical equations are rendered in standard mathematical notation.

```python
from sympy.physics.vector import init_vprinting
init_vprinting(use_latex='mathjax', pretty_print=False)
```

The IPython notebook can display rich content. We will use the Image function to import some images for reference.

```python
from IPython.display import Image
```

# Reference Frames and Orientation

Our first step is to define three reference frames, one each for the lower leg, upper leg, and torso. These reference frames hold the information that defines how each frame is oriented relative to each other in addition to their relative angular velocites and angular accelerations. We start by creating four reference frames for each frame in the system. The initialization of the torso frame is left for a later exercise.

```python
inertial_frame = ReferenceFrame('I')
```

```python
lower_leg_frame = ReferenceFrame('L')
```

```python
upper_leg_frame = ReferenceFrame('U')
```

Now we need to specify how the frames are oriented with respect to each other. To do this, we need to define the three generalized coordinates, $\theta_1(t)$, $\theta_2(t)$, and $\theta_3(t)$, for the ankle, knee, and hip angles which are time varying quantities.

```python
theta1, theta2, theta3 = dynamicsymbols('theta1, theta2, theta3')
```

We can start by setting the orientation of the lower leg relative to the inertial reference frame. We'd like the lower leg to rotate through the angle $\theta_1$ relative to the inertial frame. $\theta_1$ is the plantarflexion angle of the ankle. The `ReferenceFrame.orient()` method makes this easy. Simply give the base frame, `Axis` type of rotation, and a tuple containing the rotation angle, and the vector about which to rotate, in this case the Z unit vector of the inertial reference frame.

```python
Image('figures/ankle_joint.png')
```

```python
lower_leg_frame.orient(inertial_frame, 'Axis', (theta1, inertial_frame.z))
```

We now can see that the direction cosine matrix relating the two reference frames has been set on both frames.

```python
lower_leg_frame.dcm(inertial_frame)
```

```python
inertial_frame.dcm(lower_leg_frame)
```

We can rotate the upper leg relative to the lower leg through the knee flexion angle, $\theta_2$, in a similar fashion.

```python
Image('figures/knee_joint.png')
```

```python
upper_leg_frame.orient(lower_leg_frame, 'Axis', (theta2, lower_leg_frame.z))
```

Now that we have two reference frames chained, we can show the direction cosine matrices of the uppper leg (one end of the chain) to the inertial reference frame (the other end of the chain).

```python
upper_leg_frame.dcm(inertial_frame)
```

Notice that thedirection cosine matrix isn't in its simplest form. We can be simplified with using the `simplify()` function.

```python
simplify(upper_leg_frame.dcm(inertial_frame))
```

## Exercise

Now as an excercise, define a `torso_frame` with the name `T` and rotate it relative to the upper leg through the hip extension angle, $\theta_3$. Lastly show the simplified direction cosine matrix of the torso with respect to the inertial reference frame.

```python
Image('figures/hip_joint.png')
```

```python
# Enter and run your answer here

```

```python
%load exercise_solutions/n03_kinematics_rotate-frame.py
```

# Point and Locations

To derive the equations of motion of a system we need to have defined the velocities of the mass centers of each rigid body. We will first define points that locate the joints: ankle, knee, and hip to simplify the mass centers of the lower leg, upper leg, and torso.

## Joints

The ankle will be our based point and all other point will be defined with respect to the ankle. The ankle will be considered fixed in the inertial reference frame, i.e. $^I\mathbf{v}^A=0$

```python
ankle = Point('A')
```

The knee is defined with respect to the ankle with a vector that is the length from the ankle to the knee, $l_L$, in the direction of the lower leg $\hat{\mathbf{l}}_y$ unit vector. We need the constant length and a new point $K$.

```python
lower_leg_length = symbols('l_L')
```

```python
knee = Point('K')
```

The position is set with the `Point.set_pos()` method, giving the reference point and the vector.

```python
knee.set_pos(ankle, lower_leg_length * lower_leg_frame.y)
```

The position from any other point can then be found using the `Point.pos_from()` method.

```python
knee.pos_from(ankle)
```

It is also possible to express the returned vector in another reference frame. For example, you may want to know the position of the knee in the coordinates of the inertial reference frame.

```python
knee.pos_from(ankle).express(inertial_frame).simplify()
```

In a similar fashion the hip is defined with respect to the knee.

```python
upper_leg_length = symbols('l_U')
hip = Point('H')
hip.set_pos(knee, upper_leg_length * upper_leg_frame.y)
hip.pos_from(ankle)
```

The vector from the ankle to the hip point can be expressed in the inertial reference frame with:

```python
hip.pos_from(ankle).express(inertial_frame).simplify()
```

## Center of mass locations

### Exercise

The mass centers of the three bodies can be located in a similar fashion. The distances $d_L,d_U,d_T$ locate the mass centers relative to the distal end of the body segments. First define some new symbols:

```python

```

Now define the point $L_o$ and set the position of the `lower_leg_mass_center`.

```python

```

Position the `upper_leg_mass_center` ($U_o$).

```python

```

Position the `torso_mass_center` ($T_o$).

```python

```

```python
%load exercise_solutions/n03_kinematics_define-com.py
```

# Kinematical Differential Equations

At this point, we are going to select three generalized speeds, $\omega_1$, $\omega_2$, and $\omega_3$, (the angular velocities of the joints) so that the time derivative of the generalized coordinates are equal to the genearlized speeds. These relationships are called the kinematical differential equations and allow for the remaining equations of motion to easily be derived in first order form. This step is explicit when using Kane's method, which we are going to use. First create the time varying symbols:

```python
omega1, omega2, omega3 = dynamicsymbols('omega1, omega2, omega3')
```

To enforce the relationships: $\omega_n = \dot{\theta}_n$ we define these three equations $\omega_n - \dot{\theta}_n = 0$:

```python
kinematical_differential_equations = [omega1 - theta1.diff(),
                                      omega2 - theta2.diff(),
                                      omega3 - theta3.diff()]
kinematical_differential_equations
```

# Angular Velocities

### Exercise

Now we can use the generalized speeds to define the angular velocities of the reference frames. Due to our definitions of rotations these are simply $\omega_n \hat{\mathbf{k}}$.

Hint: Remember how we located the joint centers and center of mass locations. The syntax is very similar here.

Set the angular velocity of the `lower_leg_frame`.

```python
lower_leg_frame.set_ang_vel(inertial_frame,omega1*inertial_frame.z)
```

```python
lower_leg_frame.ang_vel_in(inertial_frame)
```

Set the angular velocity of the `upper_leg_frame`.

```python

```

Set the angular velocity of the `torso_frame`.

```python

```

```python
%load exercise_solutions/n03_kinematics_define-angular-velocity.py
```

# Linear Velocities

Finally, the linear velocities of the mass centers are needed. Starting at the ankle which has a velocity of 0.

```python
ankle.set_vel(inertial_frame, 0)
```

Working our way up the chain we can make use of the fact that the joint points are located on two rigid bodies. Any fixed point in a reference frame can be computed if the linear velocity of another point on that frame is known and the frame's angular velocity is known.

$$^I\mathbf{v}^{P_2} = ^I\mathbf{v}^{P_1} + ^I\omega^A \times \mathbf{r}^{\frac{P_2}{P_1}}$$

The `Point.v2pt_theory()` method makes it easy to do this calculation.

```python
lower_leg_mass_center.v2pt_theory(ankle, inertial_frame, lower_leg_frame)
lower_leg_mass_center.vel(inertial_frame)
```

```python
knee.v2pt_theory(ankle, inertial_frame, lower_leg_frame)
knee.vel(inertial_frame)
```

```python
upper_leg_mass_center.v2pt_theory(knee, inertial_frame, upper_leg_frame)
upper_leg_mass_center.vel(inertial_frame)
```

```python
hip.v2pt_theory(knee, inertial_frame, upper_leg_frame)
hip.vel(inertial_frame)
```

```python
torso_mass_center.v2pt_theory(hip, inertial_frame, torso_frame)
torso_mass_center.vel(inertial_frame)
```


```python
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
import scipy.fftpack
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# import ipyvolume as ipv
%matplotlib nbagg
```

A common problem in magnetics is the study of the Heisenberg Hamiltonian in 3-D space, commonly on a regular lattice:

\begin{equation}
\mathcal{H} = -\frac{1}{2}\sum_{ij}S_{i} J_{ij} S_{j} - h \sum_{i} S_i - \sum_{i} S_i \sum_{j} \frac{3(S_j \cdot \hat{r}_{ij})\hat{r}_{ij}}{r_{ij}^5} - \frac{S_j}{r_{ij}^3}
\end{equation}

Commonly the dipolar field calculation is often neglected for large scale calculatiins because the computational complexity of calculating the interaction has $\mathcal{O}(N^2)$ complexity. In some cases, particularly where modelling 2-D systems, it is approximated by including an energy term in the Hamiltonian which has the same form as the Uniaxial Anisotropy, however it has been shown that in 3-D cases this is *not* sufficient, and can give incorrect results when modelling magnetisation dynamics with the Landau-Lifshitz-Gilbert equation. The dipolar interaction is crucial to the stabilisation of many interesting features in the magnetic field, such as domain walls, vortices and skyrmions.

In this notebook, we aim to show how the field can be calculated much more quickly, using a Fast Fourier Transform (FFT) convolution approach.

To start, lets consider a three-dimensional cubic lattice of spins, with $n_x \times n_y \times n_z$ in total.

If we index each axis as (i, j, k) We can give each spin a unique index, given by its position in the lattice, as
\begin{equation}
\text{I} = n_x\times n_y\times k + n_x\times j + i
\end{equation}

The position of a given spin $I$ is then just given by:
\begin{equation}
\mathbf{r_I} = (i\,a_x, j\,a_y, k\,a_z)
\end{equation}
where $a_x, a_y, a_z$ are the distances between adjacent cubic unit cells along each coordinate axis.

Below, we just plot the magnetisation for a uniformly magnetised sample:

```python
import numpy as np

nx = ny = nz = 5
lenx = 2*nx - 1
leny = 2*ny - 1
lenz = 2*nz - 1
shape = (lenx, leny, lenz)
ax = 1
ay = 1
az = 1

np.random.seed(0)

mx = np.random.uniform(-1, 1, (nz, ny, nz))
my = np.random.uniform(-1, 1, (nz, ny, nz))
mz = np.random.uniform(-1, 1, (nz, ny, nz))
mod = np.sqrt(mx ** 2 + my ** 2 + mz ** 2)
mx /= mod
my /= mod
mz /= mod
Hx = np.zeros_like(mx)
Hy = np.zeros_like(my)
Hz = np.zeros_like(mz)
Hx_fft = np.zeros_like(mx)
Hy_fft = np.zeros_like(my)
Hz_fft = np.zeros_like(mz)
```

```python
def H_direct(mx, my, mz, Hx, Hy, Hz):
    Hx[:] = 0
    Hy[:] = 0
    Hz[:] = 0
    for k in range(nz):
        for j in range(ny):
            for i in range(nx):
                for kp in range(nz):
                    for jp in range(ny):
                        for ip in range(nx):
                            x = (i - ip)*ax
                            y = (j - jp)*ay
                            z = (k - kp)*az
                            dr = np.sqrt(x**2 + y**2 + z**2)
                            # Spins don't interact with themselves, so skip if on same position
                            if (dr == 0):
                                continue
                            r = np.array([x, y, z])
                            S = np.array([mx[kp, jp, ip], my[kp, jp, ip], mz[kp, jp, ip]])
                            H = 1e-7 * (3*(r.dot(S))*r / dr**5 - S/dr**3)
                            Hx[k, j, i] += H[0]
                            Hy[k, j, i] += H[1]
                            Hz[k, j, i] += H[2]

def Nxx_calc(x, y, z):
    x2 = x*x
    y2 = y*y
    z2 = z*z
    r = np.sqrt(x2 + y2 + z2)
    if r == 0:
        return 0.0
    else:
        return (2 * x2 - y2 - z2) / r**5

def Nxy_calc(x, y, z):
    r = np.sqrt(x*x + y*y + z*z)
    if r == 0:
        return 0.0
    else:
        return 3*x*y / r**5

def ComputeTensor(nx, ny, nz, ax, ay, az):
    lenx = 2*nx - 1
    leny = 2*ny - 1
    lenz = 2*nz - 1
    total = lenx*leny*lenz
    rtotal = lenx*leny*(lenz//2 + 1)
    shape = (lenx, leny, lenz)
    rshape = (lenx, leny, lenz//2 + 1)
    Nxx = np.zeros(shape)
    Nxy = np.zeros_like(Nxx)
    Nxz = np.zeros_like(Nxx)
    Nyy = np.zeros_like(Nxx)
    Nyz = np.zeros_like(Nxx)
    Nzz = np.zeros_like(Nxx)
    for k in range(lenz):
        for i in range(lenx):
            for j in range(leny):
                # Notice that this spans the whole range of -nx*ax to nx*ax
                x = (i - nx + 1) * ax
                y = (j - ny + 1) * ay
                z = (k - nz + 1) * az
                Nxx[k, j, i] = Nxx_calc(x, y, z)
                Nyy[k, j, i] = Nxx_calc(y, x, z)
                Nzz[k, j, i] = Nxx_calc(z, y, x)
                Nxy[k, j, i] = Nxy_calc(x, y, z)
                Nxz[k, j, i] = Nxy_calc(x, z, y)
                Nyz[k, j, i] = Nxy_calc(y, z, x)

    Nxxt = np.fft.rfftn(Nxx)
    Nxyt = np.fft.rfftn(Nxy)
    Nxzt = np.fft.rfftn(Nxz)
    Nyyt = np.fft.rfftn(Nyy)
    Nyzt = np.fft.rfftn(Nyz)
    Nzzt = np.fft.rfftn(Nzz)
    return (Nxx, Nxy, Nxz, Nyy, Nyz, Nzz), (Nxxt, Nxyt, Nxzt, Nyyt, Nyzt, Nzzt)

untransformed, tensor = ComputeTensor(nx, ny, nz, ax, ay, az)

def H_FFT(mx, my, mz, tensor, Hx, Hy, Hz):
    Hx[:] = 0
    Hy[:] = 0
    Hz[:] = 0
    Nxxt, Nxyt, Nxzt, Nyyt, Nyzt, Nzzt = tensor
    nz, ny, nx = mx.shape
    shape = (lenx, leny, lenz)
    mx_padded = np.zeros(shape)
    my_padded = np.zeros(shape)
    mz_padded = np.zeros(shape)
    mx_padded[:nx, :ny, :nz] = mx*1e-7

    my_padded[:nx, :ny, :nz] = my*1e-7
    mz_padded[:nx, :ny, :nz] = mz*1e-7
    Mxt = np.fft.rfftn(mx_padded)
    Myt = np.fft.rfftn(my_padded)
    Mzt = np.fft.rfftn(mz_padded)

    Hxt = (Nxxt*Mxt + Nxyt*Myt + Nxzt*Mzt)
    Hyt = (Nxyt*Mxt + Nyyt*Myt + Nyzt*Mzt)
    Hzt = (Nxzt*Mxt + Nyzt*Myt + Nzzt*Mzt)
    hxc = np.fft.irfftn(Hxt, s=mx_padded.shape)
    hyc = np.fft.irfftn(Hyt, s=mx_padded.shape)
    hzc = np.fft.irfftn(Hzt, s=mx_padded.shape)

    for k in range(nz):
        for j in range(ny):
            for i in range(nx):
                Hx[k, j, i] = hxc[k + nz - 1, j + ny - 1, i + nx - 1]
                Hy[k, j, i] = hyc[k + nz - 1, j + ny - 1, i + nx - 1]
                Hz[k, j, i] = hzc[k + nz - 1, j + ny - 1, i + nx - 1]
    return hxc, hyc, hzc

```

```python
H_direct(mx, my, mz, Hx, Hy, Hz)
```

```python
hxc, hyc, hzc = H_FFT(mx, my, mz, tensor, Hx_fft, Hy_fft, Hz_fft)
```

```python
m_init = np.zeros(3*nx*ny*nz)
m_init[::3] = mx.flatten()
m_init[1::3] = my.flatten()
m_init[2::3] = mz.flatten()
```

```python
import fidimag
import fidimag.atomistic
mesh = fidimag.common.CuboidMesh(nx=nx, ny=ny,nz=nz, dx=ax, dy=ay, dz=az)
sim = fidimag.atomistic.Sim(mesh)
sim.set_m(m_init, normalise=False)
sim.add(fidimag.atomistic.Demag())
demag = sim.interactions[0]
field = demag.compute_field()
```

```python
print('fidimag Hx')
print(field[::3])
print('direct Hx')
print(Hx.flatten())
print('FFT Hx')
print(Hx_fft.flatten())
```

    fidimag Hx
    [ 6.53308572e-08  1.92661797e-07 -9.47893814e-08 -3.09789151e-08
      1.65141599e-07 -8.41042525e-09  2.94130655e-07 -1.30226257e-07
     -1.54693131e-07  3.97684434e-07  9.45515392e-08  4.25471581e-07
     -2.87538910e-08 -2.35206038e-07  1.52605218e-07 -3.44893958e-07
      1.93547006e-07 -2.27057694e-08  3.08505049e-07  2.88079446e-07
      7.26757625e-08  4.19783241e-07  1.60409338e-07 -1.23664835e-07
      7.34071655e-08 -4.35881462e-08 -5.69198656e-08 -3.33477381e-07
      8.06648751e-08  3.38261942e-07 -1.86836749e-09 -2.04293985e-07
     -6.41179183e-08 -2.18836893e-07  2.68295743e-07  5.24611863e-08
      4.15187528e-08  1.88585863e-07  2.72776569e-07  4.28371891e-07
     -1.25644322e-07  5.85113312e-08 -1.37425638e-07  1.69641724e-07
      5.52095050e-08 -1.83174683e-07  1.25866644e-07 -2.71735579e-07
     -1.49131047e-07  1.04323961e-08  7.52541356e-08  1.71581817e-07
     -2.63854470e-07  1.69491607e-08  1.09543499e-07  1.20740555e-07
     -3.86811492e-07  3.96243873e-08 -2.61848786e-08  1.17442445e-07
     -1.88388919e-07 -5.47236935e-07  1.42236868e-07 -1.50089426e-07
     -1.29699668e-07  1.22577702e-07 -3.55042775e-07  1.37404184e-07
     -1.15579557e-07  1.82570428e-07  1.42350708e-07  1.89715027e-07
      1.21974181e-07  9.38991353e-08  3.00998285e-07 -2.55917992e-07
     -2.57097973e-07 -2.60828997e-07 -2.96772102e-07  2.60624827e-08
      1.33465630e-08 -3.54721132e-07  1.58010539e-07  9.50196289e-09
      1.75485095e-07  2.00641281e-07 -4.42875159e-07  9.15733061e-08
     -3.86461788e-08  4.38591300e-08  3.70077994e-07 -5.74974078e-07
      2.18943391e-07 -4.24080230e-07  3.34870105e-08  5.68495251e-08
     -2.63559836e-07  9.46100119e-08 -5.59556681e-07  1.05092625e-07
     -9.04924815e-08  2.30870400e-07  1.36795930e-07  4.48034112e-08
      1.63957467e-07  4.01225609e-08  1.63114451e-07 -1.06051081e-07
      5.41503639e-09  2.42057320e-08  2.83447888e-07 -1.98853027e-08
      2.25131710e-08  2.70730928e-07 -2.62724998e-07  3.41873861e-07
     -4.07700922e-08  1.53843815e-07  1.38106831e-07  7.22867784e-08
      2.34955980e-07  2.26966299e-07  9.32649219e-08 -1.95459124e-07
      5.46440238e-08]
    direct Hx
    [ 6.53308572e-08  1.92661797e-07 -9.47893814e-08 -3.09789151e-08
      1.65141599e-07 -8.41042525e-09  2.94130655e-07 -1.30226257e-07
     -1.54693131e-07  3.97684434e-07  9.45515392e-08  4.25471581e-07
     -2.87538910e-08 -2.35206038e-07  1.52605218e-07 -3.44893958e-07
      1.93547006e-07 -2.27057694e-08  3.08505049e-07  2.88079446e-07
      7.26757625e-08  4.19783241e-07  1.60409338e-07 -1.23664835e-07
      7.34071655e-08 -4.35881462e-08 -5.69198656e-08 -3.33477381e-07
      8.06648751e-08  3.38261942e-07 -1.86836749e-09 -2.04293985e-07
     -6.41179183e-08 -2.18836893e-07  2.68295743e-07  5.24611863e-08
      4.15187528e-08  1.88585863e-07  2.72776569e-07  4.28371891e-07
     -1.25644322e-07  5.85113312e-08 -1.37425638e-07  1.69641724e-07
      5.52095050e-08 -1.83174683e-07  1.25866644e-07 -2.71735579e-07
     -1.49131047e-07  1.04323961e-08  7.52541356e-08  1.71581817e-07
     -2.63854470e-07  1.69491607e-08  1.09543499e-07  1.20740555e-07
     -3.86811492e-07  3.96243873e-08 -2.61848786e-08  1.17442445e-07
     -1.88388919e-07 -5.47236935e-07  1.42236868e-07 -1.50089426e-07
     -1.29699668e-07  1.22577702e-07 -3.55042775e-07  1.37404184e-07
     -1.15579557e-07  1.82570428e-07  1.42350708e-07  1.89715027e-07
      1.21974181e-07  9.38991353e-08  3.00998285e-07 -2.55917992e-07
     -2.57097973e-07 -2.60828997e-07 -2.96772102e-07  2.60624827e-08
      1.33465630e-08 -3.54721132e-07  1.58010539e-07  9.50196289e-09
      1.75485095e-07  2.00641281e-07 -4.42875159e-07  9.15733061e-08
     -3.86461788e-08  4.38591300e-08  3.70077994e-07 -5.74974078e-07
      2.18943391e-07 -4.24080230e-07  3.34870105e-08  5.68495251e-08
     -2.63559836e-07  9.46100119e-08 -5.59556681e-07  1.05092625e-07
     -9.04924815e-08  2.30870400e-07  1.36795930e-07  4.48034112e-08
      1.63957467e-07  4.01225609e-08  1.63114451e-07 -1.06051081e-07
      5.41503639e-09  2.42057320e-08  2.83447888e-07 -1.98853027e-08
      2.25131710e-08  2.70730928e-07 -2.62724998e-07  3.41873861e-07
     -4.07700922e-08  1.53843815e-07  1.38106831e-07  7.22867784e-08
      2.34955980e-07  2.26966299e-07  9.32649219e-08 -1.95459124e-07
      5.46440238e-08]
    FFT Hx
    [ 6.53308572e-08  1.92661797e-07 -9.47893814e-08 -3.09789151e-08
      1.65141599e-07 -8.41042525e-09  2.94130655e-07 -1.30226257e-07
     -1.54693131e-07  3.97684434e-07  9.45515392e-08  4.25471581e-07
     -2.87538910e-08 -2.35206038e-07  1.52605218e-07 -3.44893958e-07
      1.93547006e-07 -2.27057694e-08  3.08505049e-07  2.88079446e-07
      7.26757625e-08  4.19783241e-07  1.60409338e-07 -1.23664835e-07
      7.34071655e-08 -4.35881462e-08 -5.69198656e-08 -3.33477381e-07
      8.06648751e-08  3.38261942e-07 -1.86836749e-09 -2.04293985e-07
     -6.41179183e-08 -2.18836893e-07  2.68295743e-07  5.24611863e-08
      4.15187528e-08  1.88585863e-07  2.72776569e-07  4.28371891e-07
     -1.25644322e-07  5.85113312e-08 -1.37425638e-07  1.69641724e-07
      5.52095050e-08 -1.83174683e-07  1.25866644e-07 -2.71735579e-07
     -1.49131047e-07  1.04323961e-08  7.52541356e-08  1.71581817e-07
     -2.63854470e-07  1.69491607e-08  1.09543499e-07  1.20740555e-07
     -3.86811492e-07  3.96243873e-08 -2.61848786e-08  1.17442445e-07
     -1.88388919e-07 -5.47236935e-07  1.42236868e-07 -1.50089426e-07
     -1.29699668e-07  1.22577702e-07 -3.55042775e-07  1.37404184e-07
     -1.15579557e-07  1.82570428e-07  1.42350708e-07  1.89715027e-07
      1.21974181e-07  9.38991353e-08  3.00998285e-07 -2.55917992e-07
     -2.57097973e-07 -2.60828997e-07 -2.96772102e-07  2.60624827e-08
      1.33465630e-08 -3.54721132e-07  1.58010539e-07  9.50196289e-09
      1.75485095e-07  2.00641281e-07 -4.42875159e-07  9.15733061e-08
     -3.86461788e-08  4.38591300e-08  3.70077994e-07 -5.74974078e-07
      2.18943391e-07 -4.24080230e-07  3.34870105e-08  5.68495251e-08
     -2.63559836e-07  9.46100119e-08 -5.59556681e-07  1.05092625e-07
     -9.04924815e-08  2.30870400e-07  1.36795930e-07  4.48034112e-08
      1.63957467e-07  4.01225609e-08  1.63114451e-07 -1.06051081e-07
      5.41503639e-09  2.42057320e-08  2.83447888e-07 -1.98853027e-08
      2.25131710e-08  2.70730928e-07 -2.62724998e-07  3.41873861e-07
     -4.07700922e-08  1.53843815e-07  1.38106831e-07  7.22867784e-08
      2.34955980e-07  2.26966299e-07  9.32649219e-08 -1.95459124e-07
      5.46440238e-08]

```python
print('fidimag Hy')
print(field[1::3])
print('direct Hy')
print(Hy.flatten())
print('FFT Hy')
print(Hy_fft.flatten())
```

    fidimag Hy
    [ 1.60298196e-07 -7.63396094e-08 -1.03204848e-08 -7.97268949e-08
     -1.21690053e-08  2.35561865e-08 -2.28469136e-07 -3.85678685e-08
      1.73467925e-07 -5.07936757e-08  2.67160801e-08 -3.57821989e-07
     -3.64178532e-07  3.77945439e-08  8.63439493e-08  4.02415837e-08
     -2.00562012e-07  2.05441306e-07  2.15749523e-07  2.44284751e-07
      3.05539309e-07 -1.36098956e-07  1.93615119e-08  1.21347277e-07
      1.13398272e-07  1.88291275e-07  3.21917388e-07 -4.13464028e-08
     -6.56337819e-10 -5.93982172e-08 -2.13306492e-08 -1.84822525e-09
      2.28178787e-07  4.96439646e-07  2.91264176e-08  1.01048356e-07
     -2.33289155e-07 -9.28610100e-08 -7.35941826e-08 -2.20118301e-07
      7.89646001e-09 -3.38307959e-07  1.03654575e-07  7.75714249e-08
      1.24428697e-07  3.26754864e-07 -6.47576809e-08  5.98292630e-08
      1.09383074e-07 -1.29173363e-07  2.58445723e-08  2.59863781e-08
      8.62200067e-08  5.18972487e-08  2.89855566e-07 -2.23028567e-08
     -1.24991193e-07  2.20771656e-07  2.03691272e-07 -1.53560148e-07
     -2.70613993e-07 -1.79743304e-07 -2.50074106e-07 -8.13691377e-08
      5.78904479e-08 -1.31172212e-08  3.85504626e-08  5.56307238e-10
     -1.57341653e-07 -2.52360301e-07 -3.22098033e-08 -1.11411055e-07
      1.62545209e-07  2.47681290e-07  1.68355418e-07 -1.66961006e-07
      1.87055544e-07  4.42814115e-08  5.07820875e-08  2.57358705e-07
      3.04338694e-07  3.19094777e-07  6.87384618e-08 -1.45288377e-07
     -2.92645910e-07  1.62378351e-07 -5.08780585e-08  3.93343968e-08
      7.20368477e-08  3.11449444e-07  3.35935194e-07  7.58393649e-08
      5.27999076e-08 -2.68662935e-07 -4.56830064e-07  2.96800535e-08
     -7.88781665e-08 -8.38834348e-08  2.68367110e-07  1.67984148e-07
     -1.15331507e-07 -1.94028467e-07 -9.84628927e-08 -7.67909570e-08
     -6.65097551e-08  4.31969563e-07  2.28996408e-07 -6.21031069e-08
      9.90193318e-09  1.86449343e-07  1.13645085e-07 -9.86207603e-09
      1.11183036e-07 -1.62260233e-07  1.51472894e-07 -5.92476890e-08
     -1.24726938e-07  1.29365932e-08 -5.29529579e-07 -1.54227447e-07
      6.85409528e-09 -2.57213535e-07 -3.15793600e-08 -1.51447334e-07
      2.81774317e-07]
    direct Hy
    [ 1.60298196e-07 -7.63396094e-08 -1.03204848e-08 -7.97268949e-08
     -1.21690053e-08  2.35561865e-08 -2.28469136e-07 -3.85678685e-08
      1.73467925e-07 -5.07936757e-08  2.67160801e-08 -3.57821989e-07
     -3.64178532e-07  3.77945439e-08  8.63439493e-08  4.02415837e-08
     -2.00562012e-07  2.05441306e-07  2.15749523e-07  2.44284751e-07
      3.05539309e-07 -1.36098956e-07  1.93615119e-08  1.21347277e-07
      1.13398272e-07  1.88291275e-07  3.21917388e-07 -4.13464028e-08
     -6.56337819e-10 -5.93982172e-08 -2.13306492e-08 -1.84822525e-09
      2.28178787e-07  4.96439646e-07  2.91264176e-08  1.01048356e-07
     -2.33289155e-07 -9.28610100e-08 -7.35941826e-08 -2.20118301e-07
      7.89646001e-09 -3.38307959e-07  1.03654575e-07  7.75714249e-08
      1.24428697e-07  3.26754864e-07 -6.47576809e-08  5.98292630e-08
      1.09383074e-07 -1.29173363e-07  2.58445723e-08  2.59863781e-08
      8.62200067e-08  5.18972487e-08  2.89855566e-07 -2.23028567e-08
     -1.24991193e-07  2.20771656e-07  2.03691272e-07 -1.53560148e-07
     -2.70613993e-07 -1.79743304e-07 -2.50074106e-07 -8.13691377e-08
      5.78904479e-08 -1.31172212e-08  3.85504626e-08  5.56307238e-10
     -1.57341653e-07 -2.52360301e-07 -3.22098033e-08 -1.11411055e-07
      1.62545209e-07  2.47681290e-07  1.68355418e-07 -1.66961006e-07
      1.87055544e-07  4.42814115e-08  5.07820875e-08  2.57358705e-07
      3.04338694e-07  3.19094777e-07  6.87384618e-08 -1.45288377e-07
     -2.92645910e-07  1.62378351e-07 -5.08780585e-08  3.93343968e-08
      7.20368477e-08  3.11449444e-07  3.35935194e-07  7.58393649e-08
      5.27999076e-08 -2.68662935e-07 -4.56830064e-07  2.96800535e-08
     -7.88781665e-08 -8.38834348e-08  2.68367110e-07  1.67984148e-07
     -1.15331507e-07 -1.94028467e-07 -9.84628927e-08 -7.67909570e-08
     -6.65097551e-08  4.31969563e-07  2.28996408e-07 -6.21031069e-08
      9.90193318e-09  1.86449343e-07  1.13645085e-07 -9.86207603e-09
      1.11183036e-07 -1.62260233e-07  1.51472894e-07 -5.92476890e-08
     -1.24726938e-07  1.29365932e-08 -5.29529579e-07 -1.54227447e-07
      6.85409528e-09 -2.57213535e-07 -3.15793600e-08 -1.51447334e-07
      2.81774317e-07]
    FFT Hy
    [ 1.60298196e-07 -7.63396094e-08 -1.03204848e-08 -7.97268949e-08
     -1.21690053e-08  2.35561865e-08 -2.28469136e-07 -3.85678685e-08
      1.73467925e-07 -5.07936757e-08  2.67160801e-08 -3.57821989e-07
     -3.64178532e-07  3.77945439e-08  8.63439493e-08  4.02415837e-08
     -2.00562012e-07  2.05441306e-07  2.15749523e-07  2.44284751e-07
      3.05539309e-07 -1.36098956e-07  1.93615119e-08  1.21347277e-07
      1.13398272e-07  1.88291275e-07  3.21917388e-07 -4.13464028e-08
     -6.56337819e-10 -5.93982172e-08 -2.13306492e-08 -1.84822525e-09
      2.28178787e-07  4.96439646e-07  2.91264176e-08  1.01048356e-07
     -2.33289155e-07 -9.28610100e-08 -7.35941826e-08 -2.20118301e-07
      7.89646001e-09 -3.38307959e-07  1.03654575e-07  7.75714249e-08
      1.24428697e-07  3.26754864e-07 -6.47576809e-08  5.98292630e-08
      1.09383074e-07 -1.29173363e-07  2.58445723e-08  2.59863781e-08
      8.62200067e-08  5.18972487e-08  2.89855566e-07 -2.23028567e-08
     -1.24991193e-07  2.20771656e-07  2.03691272e-07 -1.53560148e-07
     -2.70613993e-07 -1.79743304e-07 -2.50074106e-07 -8.13691377e-08
      5.78904479e-08 -1.31172212e-08  3.85504626e-08  5.56307238e-10
     -1.57341653e-07 -2.52360301e-07 -3.22098033e-08 -1.11411055e-07
      1.62545209e-07  2.47681290e-07  1.68355418e-07 -1.66961006e-07
      1.87055544e-07  4.42814115e-08  5.07820875e-08  2.57358705e-07
      3.04338694e-07  3.19094777e-07  6.87384618e-08 -1.45288377e-07
     -2.92645910e-07  1.62378351e-07 -5.08780585e-08  3.93343968e-08
      7.20368477e-08  3.11449444e-07  3.35935194e-07  7.58393649e-08
      5.27999076e-08 -2.68662935e-07 -4.56830064e-07  2.96800535e-08
     -7.88781665e-08 -8.38834348e-08  2.68367110e-07  1.67984148e-07
     -1.15331507e-07 -1.94028467e-07 -9.84628927e-08 -7.67909570e-08
     -6.65097551e-08  4.31969563e-07  2.28996408e-07 -6.21031069e-08
      9.90193318e-09  1.86449343e-07  1.13645085e-07 -9.86207603e-09
      1.11183036e-07 -1.62260233e-07  1.51472894e-07 -5.92476890e-08
     -1.24726938e-07  1.29365932e-08 -5.29529579e-07 -1.54227447e-07
      6.85409528e-09 -2.57213535e-07 -3.15793600e-08 -1.51447334e-07
      2.81774317e-07]

```python
print('fidimag Hz')
print(field[2::3])
print('direct Hz')
print(Hz.flatten())
print('FFT Hz')
print(Hz_fft.flatten())
```

    fidimag Hz
    [ 2.14549546e-07  2.40453893e-07 -5.76768863e-09 -1.49632597e-07
      5.58377274e-08  7.20672248e-08  3.02867401e-07  4.94236593e-08
     -3.14901576e-07 -2.35342441e-07 -7.44750951e-08  1.64408654e-07
     -1.38273976e-07 -2.94972215e-08  2.02530338e-08 -1.59306388e-07
     -2.33061324e-07  1.34934807e-07 -3.32179631e-07  2.57078165e-08
     -3.65048729e-07 -2.12705403e-07 -1.67843439e-08  2.64039410e-07
      1.50928259e-07 -1.07198954e-07  1.56898752e-07 -8.04811635e-08
      9.82094698e-09  1.65362606e-07 -3.68260708e-07 -3.69385627e-07
     -4.00052443e-07  1.00824233e-07 -1.77189701e-07 -1.63117892e-07
     -4.43041568e-07 -8.23824010e-07  6.24885600e-08  2.58253991e-07
      1.23940859e-07 -3.39074435e-07 -1.63675232e-07 -2.69661674e-07
      1.98438140e-09  1.72510239e-07  2.30198048e-08 -2.08974765e-07
     -5.91585903e-09 -2.11340158e-07 -1.55114030e-08  2.77745331e-07
      2.80716485e-07  2.32328706e-07  3.61579779e-07 -3.11513831e-07
      2.77285875e-07  2.05419057e-07  3.82772763e-07  5.38832718e-09
      1.77352686e-07  2.31730987e-07  1.92855313e-07  1.70667376e-07
      1.61848380e-07 -3.77795287e-08  9.78905200e-08  4.58287286e-07
      1.46164340e-07  2.07561297e-07 -8.31164871e-08  6.52127991e-08
      2.70051868e-07  3.13627093e-07  1.98637685e-07  1.79899535e-07
     -1.23655043e-07 -3.63292199e-08 -2.16700270e-07 -1.37506080e-07
     -1.74673914e-07 -2.45410351e-07 -5.32061255e-07  3.07401414e-08
     -6.49981783e-08  2.41963571e-07  1.42890305e-07 -5.20364289e-08
     -1.51772406e-08  1.59182363e-07  2.46895104e-08  2.97145548e-07
      2.13675440e-07  2.42778040e-07  2.73394164e-07  1.10716233e-08
     -1.10240798e-07  4.26084492e-09  8.49216783e-08 -3.15474792e-07
      1.53272717e-07  2.01743332e-07  2.38508480e-07 -8.63295401e-08
     -1.66255829e-07  3.87461429e-08  2.43267017e-07 -1.35902741e-07
      6.08076024e-08  4.69805937e-08 -1.06352046e-07  3.28969866e-08
     -2.31697674e-08 -1.79678764e-07 -1.23409820e-07 -1.38268604e-07
     -1.51846477e-07 -2.54585013e-07 -2.67743186e-07  7.93861360e-08
     -8.45052648e-08 -1.96206702e-07 -4.51021273e-08  1.61727092e-08
      9.49792761e-08]
    direct Hz
    [ 2.14549546e-07  2.40453893e-07 -5.76768863e-09 -1.49632597e-07
      5.58377274e-08  7.20672248e-08  3.02867401e-07  4.94236593e-08
     -3.14901576e-07 -2.35342441e-07 -7.44750951e-08  1.64408654e-07
     -1.38273976e-07 -2.94972215e-08  2.02530338e-08 -1.59306388e-07
     -2.33061324e-07  1.34934807e-07 -3.32179631e-07  2.57078165e-08
     -3.65048729e-07 -2.12705403e-07 -1.67843439e-08  2.64039410e-07
      1.50928259e-07 -1.07198954e-07  1.56898752e-07 -8.04811635e-08
      9.82094698e-09  1.65362606e-07 -3.68260708e-07 -3.69385627e-07
     -4.00052443e-07  1.00824233e-07 -1.77189701e-07 -1.63117892e-07
     -4.43041568e-07 -8.23824010e-07  6.24885600e-08  2.58253991e-07
      1.23940859e-07 -3.39074435e-07 -1.63675232e-07 -2.69661674e-07
      1.98438140e-09  1.72510239e-07  2.30198048e-08 -2.08974765e-07
     -5.91585903e-09 -2.11340158e-07 -1.55114030e-08  2.77745331e-07
      2.80716485e-07  2.32328706e-07  3.61579779e-07 -3.11513831e-07
      2.77285875e-07  2.05419057e-07  3.82772763e-07  5.38832718e-09
      1.77352686e-07  2.31730987e-07  1.92855313e-07  1.70667376e-07
      1.61848380e-07 -3.77795287e-08  9.78905200e-08  4.58287286e-07
      1.46164340e-07  2.07561297e-07 -8.31164871e-08  6.52127991e-08
      2.70051868e-07  3.13627093e-07  1.98637685e-07  1.79899535e-07
     -1.23655043e-07 -3.63292199e-08 -2.16700270e-07 -1.37506080e-07
     -1.74673914e-07 -2.45410351e-07 -5.32061255e-07  3.07401414e-08
     -6.49981783e-08  2.41963571e-07  1.42890305e-07 -5.20364289e-08
     -1.51772406e-08  1.59182363e-07  2.46895104e-08  2.97145548e-07
      2.13675440e-07  2.42778040e-07  2.73394164e-07  1.10716233e-08
     -1.10240798e-07  4.26084492e-09  8.49216783e-08 -3.15474792e-07
      1.53272717e-07  2.01743332e-07  2.38508480e-07 -8.63295401e-08
     -1.66255829e-07  3.87461429e-08  2.43267017e-07 -1.35902741e-07
      6.08076024e-08  4.69805937e-08 -1.06352046e-07  3.28969866e-08
     -2.31697674e-08 -1.79678764e-07 -1.23409820e-07 -1.38268604e-07
     -1.51846477e-07 -2.54585013e-07 -2.67743186e-07  7.93861360e-08
     -8.45052648e-08 -1.96206702e-07 -4.51021273e-08  1.61727092e-08
      9.49792761e-08]
    FFT Hz
    [ 2.14549546e-07  2.40453893e-07 -5.76768863e-09 -1.49632597e-07
      5.58377274e-08  7.20672248e-08  3.02867401e-07  4.94236593e-08
     -3.14901576e-07 -2.35342441e-07 -7.44750951e-08  1.64408654e-07
     -1.38273976e-07 -2.94972215e-08  2.02530338e-08 -1.59306388e-07
     -2.33061324e-07  1.34934807e-07 -3.32179631e-07  2.57078165e-08
     -3.65048729e-07 -2.12705403e-07 -1.67843439e-08  2.64039410e-07
      1.50928259e-07 -1.07198954e-07  1.56898752e-07 -8.04811635e-08
      9.82094698e-09  1.65362606e-07 -3.68260708e-07 -3.69385627e-07
     -4.00052443e-07  1.00824233e-07 -1.77189701e-07 -1.63117892e-07
     -4.43041568e-07 -8.23824010e-07  6.24885600e-08  2.58253991e-07
      1.23940859e-07 -3.39074435e-07 -1.63675232e-07 -2.69661674e-07
      1.98438140e-09  1.72510239e-07  2.30198048e-08 -2.08974765e-07
     -5.91585903e-09 -2.11340158e-07 -1.55114030e-08  2.77745331e-07
      2.80716485e-07  2.32328706e-07  3.61579779e-07 -3.11513831e-07
      2.77285875e-07  2.05419057e-07  3.82772763e-07  5.38832718e-09
      1.77352686e-07  2.31730987e-07  1.92855313e-07  1.70667376e-07
      1.61848380e-07 -3.77795287e-08  9.78905200e-08  4.58287286e-07
      1.46164340e-07  2.07561297e-07 -8.31164871e-08  6.52127991e-08
      2.70051868e-07  3.13627093e-07  1.98637685e-07  1.79899535e-07
     -1.23655043e-07 -3.63292199e-08 -2.16700270e-07 -1.37506080e-07
     -1.74673914e-07 -2.45410351e-07 -5.32061255e-07  3.07401414e-08
     -6.49981783e-08  2.41963571e-07  1.42890305e-07 -5.20364289e-08
     -1.51772406e-08  1.59182363e-07  2.46895104e-08  2.97145548e-07
      2.13675440e-07  2.42778040e-07  2.73394164e-07  1.10716233e-08
     -1.10240798e-07  4.26084492e-09  8.49216783e-08 -3.15474792e-07
      1.53272717e-07  2.01743332e-07  2.38508480e-07 -8.63295401e-08
     -1.66255829e-07  3.87461429e-08  2.43267017e-07 -1.35902741e-07
      6.08076024e-08  4.69805937e-08 -1.06352046e-07  3.28969866e-08
     -2.31697674e-08 -1.79678764e-07 -1.23409820e-07 -1.38268604e-07
     -1.51846477e-07 -2.54585013e-07 -2.67743186e-07  7.93861360e-08
     -8.45052648e-08 -1.96206702e-07 -4.51021273e-08  1.61727092e-08
      9.49792761e-08]

sandbox/fft-demag/Computing the Dipolar Field of a Regular Lattice of Heisenberg Spins.ipynb


```python
#Setup for Sympy
import sympy as sp
import numpy as np
```

So far we have seen Trapezoidal Rule and Simpson Rule. In general, we may approximate the integral $$I = \int_a^b w(x) f(x) dx$$
where $w \in C^0([a,b]), w>0$ and $w \in L^1((a,b))$ is a weight function, and $f \in L^1((a,b),w)$, by approximating $f$ as a polynomial.

We may approximate $f$ with $n$-th order Largrange polynomial (sampling points $(x_i)_{i=0}^{k+1}$):
\begin{equation}
f(x) \approx \sum_{i=0}^n f(x_i) \ell_i(x) \quad \ell_i(x) = \prod_{0\leq j \leq i, \, j \neq i} \frac{x-x_j}{x_i-x_j}
\end{equation}

Then
\begin{equation}
\int_a^b w(x)f(x)dx \approx I_n = \sum_{i=0}^n f(x_i) \left(\int_a^b w(x) \ell_i(x) dx \right) = \sum_{i=0}^n w_i f(x_i)
\end{equation}

We want to choose $(x_i)_{i=0}^n$ such that $I_n(f) = I(f)$ for $f$ polynomial of degree at most $m$, $m>n$ is maximised.

Turns out that if we choose roots of $\phi_{n+1}(x)$ (with $n+1$ degree), so that it belongs to the orthogonal basis of $L^2((a,b))$ (w.r.t. $\langle f,g \rangle = \int_a^b w(x) f(x)g(x) dx$, then $I_n(f) = I(f)$, $f$ any polynomial of degree at most $2n+1$. <br>
Moreover, we can check by substituting $f = (\ell_i)^2$ that $w_i > 0$. We can find $w_i$ by such substitution, but this is usually tedious. Instead, we find $w_i$ by substituting $f = 1, x, x^2, ..., x_n$ and solve the simultaneous equation.

# Example 1
Let $f \in L^2([-1,1])$ and $w(x) = 1+4|x|$. Construct $I_2(f)$.

1. Construct Orthogonal Basis: Recall the following formula:
$$\phi_j(x) = \bigg(x - \frac{\langle x\phi_{j-1}(x),\phi_{j-1}(x) \rangle}{\|\phi_{j-1}(x)\|^2} \bigg)\phi_{j-1}(x) - \frac{\|\phi_{j-1}(x)\|^2}{\|\phi_{j-2}(x)\|^2}\phi_{j-2}(x), j \geq 1$$
where $\phi_{-1}(x)=0$ and $\phi_{0}(x)=1$.

```python
# Define Symbols for inner product and norm
x = sp.symbols('x')
w = sp.Function('w')
f = sp.Function('f')
g = sp.Function('g')
```

```python
# inner product and norms
def inner(w,a,b,f,g):
    output = sp.integrate(w*f*g, (x,a,b))
    return output

def norm(w,a,b,f):
    return sp.sqrt(inner(w,a,b,f,f))
```

```python
# Define symbols for basis
phi = sp.Function('phi')
phiminus1 = sp.Function('phiminus1')
phiminus2 = sp.Function('phiminus2')
```

```python
# Iteration
def ortho(w,a,b,r):
    phiminus2 = 1
    phiminus1 = x - inner(w,a,b,x,1)/(norm(w,a,b,1))**2
    if r == 0:
        return [phiminus2]
    elif r == 1:
        return [phiminus2,phiminus1]
    else:
        philist = [phiminus2,phiminus1]
        for i in range(r-1):
            phi = (x - inner(w,a,b,x*phiminus1,phiminus1)/(norm(w,a,b,phiminus1))**2)*phiminus1 - ((norm(w,a,b,phiminus1)/norm(w,a,b,phiminus2))**2)*phiminus2
            phi = sp.simplify(phi)
            philist.append(phi)
            phiminus2 = phiminus1
            phiminus1 = phi
    return philist
```

```python
# Compute the basis, up to order 2+1 = 3
olist = ortho(1+4*sp.Abs(x),-1,1,3)
olist
```

    [1, x, x**2 - 4/9, x*(x**2 - 13/20)]

2. Compute the roots of $\phi_3(x)$, as $x_0, x_1, x_2$.

```python
rootlist = sp.solve(olist[3],x)
rootlist
```

    [0, -sqrt(65)/10, sqrt(65)/10]

3. Compute $b_0 = I(1), \, b_1 = I(x), \, b_2 = I(x^2)$

```python
w = 1+4*sp.Abs(x)
b0 = sp.integrate(w,(x,-1,1))
b1 = sp.integrate(x*w,(x,-1,1))
b2 = sp.integrate((x**2)*w,(x,-1,1))
b = sp.Matrix([b0,b1,b2])
b
```

$\displaystyle \left[\begin{matrix}6\\0\\\frac{8}{3}\end{matrix}\right]$

4. Compute $w_0, w_1, w_2$ by solving
\begin{equation}
\begin{cases}
w_0 + w_1 + w_2 = b_0 \\
w_0 x_0 + w_1 x_1 + w_2 x_2 = b_1 \\
w_0 x_0^2 + w_1 x_1^2 + w_2 x_2^2 = b_2
\end{cases}
\end{equation}

```python
A = sp.Matrix([[1,1,1],[rootlist[0],rootlist[1],rootlist[2]], [rootlist[0]**2,rootlist[1]**2,rootlist[2]**2]])
A
```

$\displaystyle \left[\begin{matrix}1 & 1 & 1\\0 & - \frac{\sqrt{65}}{10} & \frac{\sqrt{65}}{10}\\0 & \frac{13}{20} & \frac{13}{20}\end{matrix}\right]$

```python
w0, w1, w2 = sp.symbols('w0,w1,w2')
sp.linsolve((A,b),[w0,w1,w2])
```

$\displaystyle \left\{\left( \frac{74}{39}, \  \frac{80}{39}, \  \frac{80}{39}\right)\right\}$

# Please edit below

```python
w = sp.exp(-x)
a = 0
b = sp.oo
n = 2
```

```python
# Compute the basis, up to order n+1
olist = ortho(w,a,b,3)
olist
```

    [1, x - 1, (x - 3)*(x - 1) - 1, x**3 - 9*x**2 + 18*x - 6]

```python
# Compute root of phi(n+1)
rootlist = sp.solve(olist[2],x)
rootlist
```

    [2 - sqrt(2), sqrt(2) + 2]

```python
# Compute actual value of I(f) for f = 1, x,...
b0 = 1
b1 = 1
b = sp.Matrix([b0,b1])
b
```

$\displaystyle \left[\begin{matrix}1\\1\end{matrix}\right]$

```python
# Compute weight by solving equation
A = sp.Matrix([[1,1],[rootlist[0],rootlist[1]]])
w0, w1, w2 = sp.symbols('w0,w1,w2')
sp.linsolve((A,b),[w0,w1])
```

$\displaystyle \left\{\left( \frac{\sqrt{2}}{4} + \frac{1}{2}, \  \frac{1}{2} - \frac{\sqrt{2}}{4}\right)\right\}$

```python
# Integrate
integral = np.dot(np.array[w0], np.array[f(x) for x in rootlist])
```

M2AA3/M2AA3-Polynomials/Lesson 04 - Quadrature/.ipynb_checkpoints/Gaussian Quadrature-checkpoint.ipynb


# Basic use of Function_from_Expression

The module `fun_expr` defines a class `Function_from_Expression` to handle functions which are defined by `sympy`-expressions.

It is useful, to import this as

`
from fun_expr import Function_from_Expression as FE
`

It is not necessary to use `Function_from_Expression` at al. This class is a simple wrapper to make live easier if you need to define functions from expressions and dont prefer to use expressions instead.

```python
from sympy import *
init_printing()

from IPython.display import display, Math

from fun_expr import Function_from_Expression as FE
```

To define a function from an expression, i.e.

$$
  f(x) = x^2
$$

you need a variable ($x$), which must be as `symbol`. The right hand side of the given
definition of a function ($x^2$) is the expression.

```python
# define the variable of the function
x = Symbol('x')

# define the function
f = FE(x, x**2)

# show the result
f
```

`str(f)` shows that $f$ essentially is an object of type `Lambda` which is a `sympy`-generalisation of `lambda`

```python
str(f)
```

    'Lambda(x, x**2)'

The `type` of instances of `FE` is

```python
type(f)
```

    fun_expr.function_from_expression.Function_from_Expression

`f` can be applied to an argument:

```python
f(2)
```

The argument can be a more complicated `sympy` expression

```python
a,b,c = symbols('a,b,c')
expr = (a+b)/c

f(expr)
```

The function can be plotted. For this, `matplotlib` is used.

The magic command `%matplotlib inline` shows the plots inside the notebook.

The magic command `%config InlineBackend.figure_format='retina'` is useful for OSX

`numpy` is used to create numerical arrays

```python
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format='retina' # for OSX

import numpy as np
```

The simplest way to plot a function using `matplotlib` is to use sympys `plot`-command. This works since $f(x)$ is an expression.

```python
plot(f(x))
```

Often it is desireable to have more control over what is plotted:

```python
# define new plot
fig, ax = plt.subplots()

# the x-values of the plot
lx = np.linspace(-1,1)

# the y-values of the plot
ax.plot(lx,f.lambdified(lx),label=r'$y={f}$'.format(f=latex(f(x))))

# additional commands
ax.grid(True)
ax.axhline(0,c='k')
ax.axvline(0,c='k')
ax.legend(loc='best')
```

It is possible, to differentiate the function. If `f` is a function of a single variable `x`, this is simply

```python
f_1 = f.diff()
f_1
```

This only works, if `x` is the only symbol in the expression `f(x)`. It is better, to specify the variable of differentiation:

```python
f_1 = f.diff(x)
f_1
```

The second derivative of `f` w.r.t. `x` is

```python
f_2 = f.diff(x,2)
f_2
```

If a function `f` is differentiated w.r.t. a variable, the result is a function of the same varibles as `f`. Sometimes, an expression is needed as the result of a differentiation. In this cases, the flag `as_function=False` can be used

```python
f.diff(x, as_function=False)
```

Another possibility is to use the expr of the result:

```python
f.diff(x).expr
```

The function can be integrated. This usually leads to an expression:

```python
f.integrate()
```

```python
# or
f.integrate(x)
```

It is possible to integrate oven an interval $[a,b]$:

```python
f.integrate((x,2,4))
```

The integral of a function is an expression. If the integral is needed as a function, the flag
`as_function=True` can be used

```python
f.integrate(x, as_function=True)
```

The integral can only be a function, if there are variables left. Otherwise an Error is thrown:

```python
try:
    f.integrate((x,2,4),as_function=True)
except TypeError as e:
    print("Error: " + str(e))
```

    Error: no variable left to define a function

It is possible, to define functions of several variables (not every symbol needs to be a variable)

```python
x,y,z = symbols('x,y,z')
f = FE((x,y), x*y*z)
display(f)
# or, as equation
display(Math("f(x,y)="+latex(f(x,y))))
```

In the above case, `z` is a free symbol of `f`

```python
f.free_symbols
```

The `atoms` are the smallest parts of the expression `f(x)`:

```python
f.atoms()
```

```python
f.variables
```

```python
f.expr
```

Free symbols can be substituted by other values:

```python
f.subs(z,3)
```

Variables cant be substituted. They are bound to the function:

```python
x_0 = Symbol('x_0')

try:
    p = f.subs(x,x_0)
    display(Math('p(y)='+latex(p(x))))
except ValueError as e:
    print("Error: " + str(e))
```

    Error: Substitution of variables is not valid: (x, x_0)

If this is needed, it is better to define a new function:

```python
p = FE(y, f.expr.subs(x,x_0))
Math("p(y)="+latex(p(y)))
```

$$p(y)=x_{0} y z$$


*There are many difficult courses on the economics study on the University of Copenhagen. One of the exams with lowest grade distributions is the course "Microeconomics I".
We went to the exam on the 16th. january 2018, and we figured that solving and simulating the cournot-duopoly model as it was presented in the first assignment, would be our model of  interest. We will be comparing the results with the Stackelberg competition model, as this was a follow up question. This model can be seen as an extension to the Cournot model. We will first derive the general expressions and equations then we will plot using values from the exam. Lastly we will solve the model wrt. to output quantities, numerically.*

```python
###################################################################################
### Importing relevant packages and making sure that sympy print-outs look neat ###
###################################################################################
from scipy import linalg
from scipy import optimize
import sympy as sm
import matplotlib.pyplot as plt
import numpy as np
import ipywidgets as widgets
from ipywidgets import interact, interact_manual

sm.init_printing(use_unicode=True)
```

```python
###################################################################################
#################### Creating relevant notation and variables #####################
###################################################################################
profita = sm.symbols("pi_A")
mca = sm.symbols("MC_A")
qa = sm.symbols("q_A")
profitb = sm.symbols("pi_B")
mcb = sm.symbols("MC_B")
qb = sm.symbols("q_B")
q = sm.symbols("Q")
p = sm.symbols("P")
d = sm.symbols("D(P)")
a = sm.symbols("alpha")
b = sm.symbols("beta")

#Testing notatition
profita, mca, qa, q, p, d, a, b
```

```python
###################################################################################
############# Defining demand function, and defininig total quantity ##############
###################################################################################

# Pr. definition, total quantity is equal to the output of alle the firms (i.e. firm A & B)
q = qa + qb
demand = sm.Eq(q, a - b*p)

#We would now like to find the invese demand function.
inv_demand = sm.solve(demand,p)[0]

#Saving equations for later

print_inv_demand = sm.Eq(p, inv_demand)
demand, print_inv_demand
```

```python
###################################################################################
################ Defining maximization problem for the firm A & B #################
###################################################################################

#Both firms take each others actions into account.
profitsa_print = sm.Eq(profita, (p-mca)*qa)
profitsb_print = sm.Eq(profitb, (p-mcb)*qb)
profits_matrix = sm.Matrix([profitsa_print, profitsb_print])
profits_matrix
```

$$\left[\begin{matrix}\pi_{A} = q_{A} \left(- MC_{A} + P\right)\\\pi_{B} = q_{B} \left(- MC_{B} + P\right)\end{matrix}\right]$$

```python
###################################################################################
############### Inserting inverse demand in maximization problems #################
###################################################################################

profitsa = sm.Eq(profita, (inv_demand-mca)*qa)
profitsb = sm.Eq(profitb, (inv_demand-mcb)*qb)
matrix_profits = sm.Matrix([profitsa, profitsb])
matrix_profits
```

$$\left[\begin{matrix}\pi_{A} = q_{A} \left(- MC_{A} + \frac{\alpha - q_{A} - q_{B}}{\beta}\right)\\\pi_{B} = q_{B} \left(- MC_{B} + \frac{\alpha - q_{A} - q_{B}}{\beta}\right)\end{matrix}\right]$$

Now that the profit maximization problems are specified, the firms choose the quantity they want to produce.
This is done by differentiating the profit maximization problems wrt. to firm quantity. I.e. firm a chooses quantity A.

```python
###################################################################################
######################## Maximizing profit functions ##############################
###################################################################################

foca = sm.diff((inv_demand-mca)*qa,qa)
focb = sm.diff((inv_demand-mcb)*qb,qb)
foca_print = sm.Eq(foca, 0)
focb_print = sm.Eq(focb, 0)
matrix_foc = sm.Matrix([foca_print, focb_print])
matrix_foc
```

$$\left[\begin{matrix}- MC_{A} - \frac{q_{A}}{\beta} + \frac{\alpha - q_{A} - q_{B}}{\beta} = 0\\- MC_{B} - \frac{q_{B}}{\beta} + \frac{\alpha - q_{A} - q_{B}}{\beta} = 0\end{matrix}\right]$$

```python
###################################################################################
########################## Solving system of 2 equations ##########################
###################################################################################

sola = sm.solve(foca,qa)
solb = sm.solve(focb,qb)

#We now have a system of 2 equations and 2 unknowns. Solving the system:

sol = sm.solve([foca,focb],[qa,qb])

#saving solution for A and B (for later, since easier)
solution_a = sol[qa]
solution_b = sol[qb]
#for print outs
EQa = sm.Eq(qa, solution_a)
EQb = sm.Eq(qb, solution_b)

matrix_solve = sm.Matrix([EQa,EQb])
#The solution is:
matrix_solve
```

$$\left[\begin{matrix}q_{A} = - \frac{2 MC_{A} \beta}{3} + \frac{MC_{B} \beta}{3} + \frac{\alpha}{3}\\q_{B} = \frac{MC_{A} \beta}{3} - \frac{2 MC_{B} \beta}{3} + \frac{\alpha}{3}\end{matrix}\right]$$

Which is as expected. Firm A's production is increasing in baseline demand and firm B's production costs, while decreasing in its own production costs.
If firm B has higher costs, this will induce firm A to produce more aggresively.

```python
###################################################################################
###################### Plugging in and deriving firm profits ######################
###################################################################################

profits_final_a = sm.Eq(profita, sol[qa]*p - sol[qa]*mca)
profits_final_b = sm.Eq(profitb, sol[qb]*p - sol[qb]*mcb)

matrix_profits = sm.Matrix([profits_final_a, profits_final_b])
matrix_profits
```

$$\left[\begin{matrix}\pi_{A} = - MC_{A} \left(- \frac{2 MC_{A} \beta}{3} + \frac{MC_{B} \beta}{3} + \frac{\alpha}{3}\right) + P \left(- \frac{2 MC_{A} \beta}{3} + \frac{MC_{B} \beta}{3} + \frac{\alpha}{3}\right)\\\pi_{B} = - MC_{B} \left(\frac{MC_{A} \beta}{3} - \frac{2 MC_{B} \beta}{3} + \frac{\alpha}{3}\right) + P \left(\frac{MC_{A} \beta}{3} - \frac{2 MC_{B} \beta}{3} + \frac{\alpha}{3}\right)\end{matrix}\right]$$

```python

```

```python
#Recall demand function and definition of total quantity.
q_sol = sol[qa] + sol[qb]
#Set total quantity equal to demand
demand_sol = sm.Eq(q_sol, a - b*p)
demand_sol

```

```python
#We would now like to find the equillibrium price, so we can calculate profits (with prices).
price = sm.solve(demand_sol,p)[0]

#Calculating profits for the two firms.
profits_firm_a = price*sol[qa] - mca*sol[qa]
profits_firm_b = price*sol[qb] - mcb*sol[qb]
print_profits_firm_a = sm.Eq(profita, profits_firm_a)
print_profits_firm_b = sm.Eq(profitb, profits_firm_b)
matrix_profits_firm = sm.Matrix([print_profits_firm_a,print_profits_firm_b])
matrix_profits_firm
```

$$\left[\begin{matrix}\pi_{A} = - MC_{A} \left(- \frac{2 MC_{A} \beta}{3} + \frac{MC_{B} \beta}{3} + \frac{\alpha}{3}\right) + \frac{\left(\alpha + \beta \left(MC_{A} + MC_{B}\right)\right) \left(- \frac{2 MC_{A} \beta}{3} + \frac{MC_{B} \beta}{3} + \frac{\alpha}{3}\right)}{3 \beta}\\\pi_{B} = - MC_{B} \left(\frac{MC_{A} \beta}{3} - \frac{2 MC_{B} \beta}{3} + \frac{\alpha}{3}\right) + \frac{\left(\alpha + \beta \left(MC_{A} + MC_{B}\right)\right) \left(\frac{MC_{A} \beta}{3} - \frac{2 MC_{B} \beta}{3} + \frac{\alpha}{3}\right)}{3 \beta}\end{matrix}\right]$$

```python

```

```python
#Turning equations into solvable code:
price_function = sm.lambdify((a,b,mca,mcb),price)
quantity_function = sm.lambdify((a,b,mca,mcb),q_sol)
quantity_function_a = sm.lambdify((a,b,mca,mcb),solution_a)
quantity_function_b = sm.lambdify((a,b,mca,mcb),solution_b)
profits_function_a = sm.lambdify((a,b,mca,mcb),profits_firm_a)
profits_function_b = sm.lambdify((a,b,mca,mcb),profits_firm_b)
```

```python
#Making a calculator that calculates total quantity & equillibrium price

def calculator(a,b,mca,mcb):
    plt.plot(quantity_function(a,b,mca,mcb),price_function(a,b,mca,mcb))
    plt.xlabel('Quantity')
    plt.ylabel('price')
    plt.axvline(x=quantity_function(a,b,mca,mcb), ymin=0, ymax=0.5, color='g')
    plt.axhline(y=price_function(a,b,mca,mcb), xmin=0, xmax=0.5, color='g')
    plt.show()
    #the ymin & ymax makes sure that the line only crosses half into the plot. This is ideal as the plot adjusts automatically.
    return quantity_function(a,b,mca,mcb), price_function(a,b,mca,mcb)
interact(calculator, a=6000,b=100,mca=24,mcb=18);
```

    interactive(children=(IntSlider(value=6000, description='a', max=18000, min=-6000), IntSlider(value=100, descr…

```python
# Graph of the firms produced quantities and profits

def firm_curves(a,b,mca,mcb):
    print("red is firm A, blue is firm B")
    plt.plot(profits_function_a(a,b,mca,mcb))
    plt.xlabel('Firm A quantity')
    plt.ylabel('Firm A profits')
    plt.axvline(x=quantity_function_a(a,b,mca,mcb), color='r')
    plt.axhline(y=profits_function_a(a,b,mca,mcb), color='r')
    plt.show()
    plt.plot(profits_function_b(a,b,mca,mcb))
    plt.xlabel('firm B quantity')
    plt.ylabel('firm B profits')
    plt.axvline(x=quantity_function_b(a,b,mca,mcb))
    plt.axhline(y=profits_function_b(a,b,mca,mcb))
    plt.show()
    print("Quantity firm A, Profits firm A, Quantity firm B, Profits firm B")
    return quantity_function_a(a,b,mca,mcb), profits_function_a(a,b,mca,mcb), quantity_function_b(a,b,mca,mcb),profits_function_b(a,b,mca,mcb)

interact(firm_curves,a=6000,b=100,mca=24,mcb=18);

```

    interactive(children=(IntSlider(value=6000, description='a', max=18000, min=-6000), IntSlider(value=100, descr…

The calculator can be used to supplement the graphs below.

```python
#Profit functions for firm A
Firm_low_mc_a = lambda qa: qa*(a-qb_l-qa)/b - mca_l*qa
Firm_med_mc_a = lambda qa: qa*(a-qb_m-qa)/b - mca_m*qa
Firm_high_mc_a = lambda qa: qa*(a-qb_h-qa)/b - mca_h*qa
#profit functions for firm B

qa = np.linspace(0, 2000, 2000)
# Returns numbers between 0 and 2500. 0 is intuitive; you't can't produce negative amounts. 2500 is chosen according to visual preferences
#demand parameters
a = 6000
b = 100
### Firm A lines###
#different quantites by firm B
qb_l = 1400
qb_m = 1600
qb_h = 1800
#different marginal costs
mca_l = 18
mca_m = 24
mca_h = 30

###Firm B lines, when firm A faces changes to marginal costs####

#Profit functions for firm A
B_Firm_low_mc_a = lambda qb: qb*(a-qb-qa_low_mc)/b - mcb*qb
B_Firm_med_mc_a = lambda qb: qb*(a-qb-qa_med_mc)/b - mcb*qb
B_Firm_high_mc_a = lambda qb: qb*(a-qb-qa_high_mc)/b - mcb*qb
#profit functions for firm B
qb = np.linspace(0, 2500, 2000)
qa_low_mc = 1400
qa_med_mc = 1000
qa_high_mc = 600
mcb = 18

#Simulating how a change in the marginal costs would affect firm a behavior, taking into account that equillibrium price changes when firms
#are faced with different costs.
#We used the above calculator to simulate how the equillibrium price would change, if the marginal cost
#of firm a changed. We held the marginal cost of firm b constant.

plt.plot(qa, Firm_low_mc_a(qa), color = 'g')
plt.plot(qa, Firm_med_mc_a(qa), color = 'b')
plt.plot(qa, Firm_high_mc_a(qa), color = 'r')
plt.axhline(y=0.5, color ='k')
plt.axvline(x=0, color ='k')
plt.xlabel(" Firm A produced quantity ")
plt.ylabel("Firm A Profits")
plt.title('Firm A profit and quantity')
plt.text(0,-26000,'When Firm A experiences changes to marginal costs, Firm A adjusts produced quantity:')
plt.text(0,-28500,'Green line is marginal cost = 18, blue line is marginal cost = 24, red line is marginal cost = 30')
plt.grid()
plt.show()

plt.plot(qb, B_Firm_low_mc_a(qb), color = 'g')
plt.plot(qb, B_Firm_med_mc_a(qb), color = 'b')
plt.plot(qb, B_Firm_high_mc_a(qb), color = 'r')
plt.axhline(y=0.5, color ='k')
plt.axvline(x=0, color ='k')
plt.xlabel(" Firm B produced quantity ")
plt.ylabel("Firm B Profits")
plt.title('Firm B profit and quantity')
plt.text(0,-9000,'When Firm A experiences changes to marginal costs, Firm B adjusts produced quantity:')
plt.text(0,-11500, 'Green line is marginal cost = 18, blue line is marginal cost = 24, red line is marginal cost = 30.')
plt.text(0,-14000, 'It is seen that as Firm A inccur higher costs, firm B expands!')
plt.text(0,-16500, 'When the two firms have same costs, i.e. mca = mcb = 18, profits and quantities are identical.')
plt.grid()
plt.show()

```

```python
############################################################################################
####### Code for generating equal marginal cost Cournot and stackelberg equillibrium #######
############################################################################################

#### Code for generating profit functions for firm A, under Cournot and Stackelberg competition:
C_firm_a = lambda qa: qa*(a-C_qb-qa)/b - mca*qa
B_firm_a = lambda qa: qa*(a-B_qb-qa)/b - mca*qa
#Code for generating profit functions for firm A, under Cournot and Stackelberg competition:
C_firm_b = lambda qb: qb*(a-B_qa-qb)/b - mcb*qb
B_firm_b = lambda qb: qb*(a-C_qa-qa)/b - mcb*qb

# Generates numbers between 0 and 2500. 0 is intuitive; you't can't produce negative amounts. 2500 is chosen according to visual preference
qa = np.linspace(0, 2500, 2000)
qb = np.linspace(0, 2500, 2000)
#Setting parameters according to the values in the exam
a = 6000
mca = 24
mcb = 18
b = 100
# Firm A quantities
C_qa = 1000
B_qa = 1500
# Firm B quantites
C_qb = 1350
B_qb = 1600

#Now we want to plot the profit functions. The dashed lines will be Cournot competition and the filled lines will be Stackelberg competition.
plt.plot(qa, C_firm_a(qa), color='r')
plt.plot(qa, B_firm_a(qa), linestyle='dashed', color='r')
plt.plot(qb, C_firm_b(qb), color="b" )
plt.plot(qb, B_firm_b(qb), linestyle='dashed', color="b")
plt.axhline(y=0.5, color ='k')
plt.axvline(x=1, color ='k')
plt.xlabel("Produced quantity")
plt.ylabel("Profits")
plt.title('Profit and quantity under different models of competition', fontsize=13)
plt.text(0,-25000,'Firm A is the red lines, firm B is the blue ones.')
plt.text(0,-27500,'The dashed lines are their profits under Cournot competition, the full lines are under Stackelberg competition.')
plt.text(0,-30000,'In our exam the values for marginal costs were: Marginal cost firm A = 24, Marginal cost firm B = 18.')
plt.grid()
plt.show()

############################################################################################
####### Code for generating equal marginal cost Cournot and stackelberg equillibrium #######
############################################################################################

#### Code for generating profit functions for firm A, under Cournot and Stackelberg competition:
i_C_firm_a = lambda qa: qa*(a-i_C_qb-qa)/b - i_mca*qa
i_B_firm_a = lambda qa: qa*(a-i_B_qb-qa)/b - i_mca*qa
#Code for generating profit functions for firm A, under Cournot and Stackelberg competition:
i_C_firm_b = lambda qb: qb*(a-i_B_qa-qb)/b - i_mcb*qb
i_B_firm_b = lambda qb: qb*(a-i_C_qa-qa)/b - i_mcb*qb

#Setting parameters values
#marginal cost
i_mca = 18
i_mcb = 18
# Firm A quantities
i_C_qa = 1400
i_B_qa = 2100
# Firm B quantites
i_C_qb = 1400
i_B_qb = 525

#Now we want to plot the profit functions. The dashed lines will be Cournot competition and the filled lines will be Stackelberg competition.
plt.plot(qa, i_C_firm_a(qa), color='r')
plt.plot(qa, i_B_firm_a(qa), color='r')
plt.plot(qb, i_C_firm_b(qb), color="b" )
plt.plot(qb, i_B_firm_b(qb), linestyle='dashed', color="b")
plt.axhline(y=0.5, color ='k')
plt.axvline(x=0, color ='k')
plt.xlabel("Produced quantity")
plt.ylabel("Profits")
plt.title('Profit and quantity under different models of competition', fontsize=13)
plt.text(0,-25000,'Firm A is the red lines, firm B is the blue ones.')
plt.text(0,-27500,'The blue and red dotted (purple looking) line, is the identical firm cournot competition equillibrium, with mca=mcb=18')
plt.text(0,-30000,'The fully drawn red and blue line is the stackelberg equillibrium, where it is evident that red (firm A) has a market condition advantage')
plt.grid()
plt.show()
```

```python
############################################################################################
####### Code for solving the Cournot competition model with identical marginal costs #######
############################################################################################

# Importing scipy numpy array

from scipy import optimize,arange
from numpy import array

#Defining inverse demand, which is equal to price.

def price(qa,qb,b):
    return A/B-qa/B-b*qb/B

#Setting baseline parameters equal to the exam parameters.
A = 6000
B = 100

#Defining the cost function. Assuming constant marginal costs.

def costs(q,c):
    costs = c*q
    return costs

#Defining the profit function.

def profit_function(qa,qb,c1,b):
    return price(qa,qb,b)*qa-costs(qa,c1)

#Defining the reaction function. Scipy only has numerical minimizers, so we multiply profits by -1, minimize that.
#Minimizing negative profits, is the same as aximizing positive profits.
def react(qb,c1,b):
    qa = optimize.brute(lambda q: -profit_function(q,qb,c1,b), ((0,1,),))

    return qa[0]

# Creating a vector of parameters for the degree of homogeneity of products (b), and costs (c1,c2).
#The vector is then, V = [b,c1,c2]
def vector_react(q,parameters):
    return array(q)-array([react(q[1],parameters[1],parameters[0]),react(q[0],parameters[2],parameters[0])])

#Setting the remaining exam parameters. Homogeneity is 1. Marginal costs for firm A is 24. Marginal costs for firm B is 18.
parameters = [1.0,24,18]

#Setting initial guess:
q0 = [0, 0]

Solution_numerical = optimize.fsolve(vector_react, q0, args = (parameters))
#The solution is then:
Solution_numerical

```

    array([1000., 1600.])

*This project was about solving one of the assignments in our Microeconomics 1 exam paper. We derived the same results as the answer sheet, through both numerically and analytically solving the Cournot competition model. We had some plots where we used the Stackelberg competition model to graphically evaluate how the change in market conditions led to firm A having a major advantage over firm B, despite the fact that firm B had lower marginal costs than firm A*