# Variational Principle  using Symbolic Mathematics in Python

## 1. Introduction

The variational principle tells us that we can use a trial wavefunction to solve the Schrodinger equation using the following theorem:

$${{\int {{\Psi ^*}\hat H{\rm{ }}\Psi } d\tau } \over {\int {{\Psi ^*}\Psi } d\tau }} \ge {E_0}$$

We will use Sympy to solve the particle in a box problem by guessing a trial wavefunction using variational principle

```python
import sympy as sym
```

This exercise is a bit more self-guided than the other notebooks we have done. One of the most useful things you can do is **open last week's notebook to remember the commands in sympy**. Also, remember that google is your friend:

1. [Sympy tutorial](https://docs.sympy.org/latest/tutorial/index.html)
2. [Stack Overflow](https://stackoverflow.com/search?q=sympy+)
3. [Stack Exchange](https://stackexchange.com/)

## 2. Particle in a box

The wave function that we pick for a particle in a box needs to have the following properties

1. single valued
1. normalizable
1. function and its first derivative are continuous
1. boundary condition that the wave function goes to zero at the ends of the box

Particle in a box: a is a classical particle, red is real part, blue is imaginary part.

This particle only expericnes kinetic energy between the box, so the Hamiltonian for this system is

$$\hat H = {{ - {\hbar ^2}} \over {2m}}{{{d^2}} \over {d{x^2}}} + \left\{ {\matrix{{V(x) = 0} & {0 < x < a}  \cr {V(x) = \infty } & {x < 0\text{ }{\rm{ or}}\;x > a}  \cr } } \right.$$

For our purposes, that means we can consider the Hamiltonian to be

$$\hat H = {{ - {\hbar ^2}} \over {2m}}{{{d^2}} \over {d{x^2}}}$$

as long as we keep the limits of integration to be $(0,a)$

### 2.1 Trial Wave function

Although the particle in box has a well known solution

[https://en.wikipedia.org/wiki/Particle_in_a_box](https://en.wikipedia.org/wiki/Particle_in_a_box)

(or check your favorite pchem book)

We are going to guess a trial wave function:

$$\Phi (x) = \left( {{x \over a} - {{{x^3}} \over a}} \right) + \alpha \left( {{{{x^5}} \over {{a^5}}} - {1 \over 2}\left( {{{{x^7}} \over {{a^7}}} + {{{x^7}} \over {{a^7}}}} \right)} \right)$$

### 2.2 Exercise: Variational Theorem

We are going to follow the following plan:

1. Solve for the energy of the trial wave function above

$${E_{trial}} = {{\int\limits_0^a {\Phi (x){{ - {\hbar ^2}} \over {2m}}{{{d^2}} \over {d{x^2}}}\Phi (x)dx} } \over {\int\limits_0^a {\Phi {{(x)}^2}dx} }}$$

Your answer will be a function of $ m,a,\text{and } \alpha$ We will use $\alpha$ as the parameter we vary to minimize the energy and make a new trial wave function.

2. Minimize the trial energy
We will use a first derivative of the trial energy $${d \over {d\alpha }}{E_{trial}}(\alpha )$$ to find the value of $\alpha$ that gives you the lowest energy

3. Plot your new wavefunction compared to the ground state particle in a box: $${\psi _{true}}(x) = {\left( {{2 \over a}} \right)^{1/2}}\sin {{n\pi x} \over a}$$ Plot as a function of $x/a$ from $0$ to $1$. Assuming this has $m=m_e$, and $a=a_0$ use atomic (theorist) units to plot the function.

4. Compare your trial energy to the actual energy (using atomic units)
$${E_{true}}(n = 1) = {{{\hbar ^2}{\pi ^2}} \over {2m{a^2}}}$$

```python
import sympy as sym
a,x,m=sym.symbols('a,x,m')
sym.Rational(1/2)
from sympy.physics.units import hbar
sym.init_printing()
Phi,phi,alpha,hbar=sym.symbols("Phi,phi,alpha,hbar")
alpha,phi,Phi,hbar
```

```python
phi = ((x/a)-(x**3/a**3)+ alpha*((x**5/a**5)-1/2*(x**9/a**9+x**9/a**9)))
num1 = (phi*sym.Rational(-1/2))
num2 = sym.diff(sym.diff(phi,x),x)
den1 = (phi**2)
```

```python
Numerator = sym.integrate((num1*num2),(x,0,a))
```

```python
Denominator = sym.integrate((den1),(x,0,a))
```

```python
Numerator/Denominator
```

```python
E_trial = (Numerator/Denominator).subs(a,1)
E_trial
```

```python
sym.solveset(sym.diff(E_trial,alpha))
```

```python
E_trial.subs(alpha, -0.257747652303185)
```

```python
E_trial.subs(alpha, -4.73345207773701)
```

```python
Newphi = phi.subs(alpha, 5.02467668289965)
Newphi
```

```python
Newphi2 = Newphi.subs(a,1)
Newphi2
```

```python
sym.plot(Newphi2,(x,0,1))
```

```python
sym.plot((2**1/2)*sym.sin(sym.pi*x),(x,0,1))
```

```python
sym.plot((2**1/2)*sym.sin(sym.pi*x),Newphi2,(x,0,1))
```

```python
E_true = sym.pi**2/(2*m)
E_true
```

Your descriptions/explanations here

### 2.3 Exercise: New trial wavefunction

Determine the minimum energy of the particle in a box using a new trial wavefunction $$x^\alpha(x-a)^\alpha$$

1. Find the minimum energy, $E_{trial}$
2. Plot the new trial wavefunction and compare it to the true solution and the wavefunction you found above
3. Compare you new energy to the trial energy you found above
4. Which wavefunction is better? How do you know?

```python
#Plug in 1 for a, after you solve for values, first find the first derivative
#After you solve both plug them into E trial
```

```python
phhi = (x**alpha)*(x-a)**alpha
phhi
```

```python
num11 = (phhi*sym.Rational(1/2))
num21 = sym.diff(sym.diff(phhi,x),x)
den11 = (phhi**2)
Numerator2 = sym.integrate((num11*num21),(x,0,a))
Numerator2
```

```python
Denominator2 = sym.integrate((den11),(x,0,a))
Denominator2
```

$\displaystyle \frac{a a^{4 \alpha} e^{2 i \pi \alpha} \Gamma\left(2 \alpha + 1\right) {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)}}{\Gamma\left(2 \alpha + 2\right)}$

```python
E_trial2 = (Numerator2/Denominator2).subs(a,1)
E_trial2
```

$\displaystyle \frac{\alpha e^{- 2 i \pi \alpha} \Gamma\left(2 \alpha + 2\right) \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx}{2 \Gamma\left(2 \alpha + 1\right) {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)}}$

```python
sym.solveset(sym.diff(E_trial2,alpha))
```

$\displaystyle \left\{\alpha \mid \alpha \in \mathbb{C} \wedge \left(- 2 \alpha {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)} \operatorname{polygamma}{\left(0,2 \alpha + 1 \right)} \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx + 2 \alpha {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)} \operatorname{polygamma}{\left(0,2 \alpha + 2 \right)} \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx + \alpha {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)} \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 x^{2} - 4 x + 2 \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right) \log{\left(x \right)} + 2 \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right) \log{\left(x - 1 \right)} + 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx - 2 i \pi \alpha {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)} \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx - \alpha \frac{d}{d \alpha} {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)} \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx + {{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)} \int\limits_{0}^{1} \frac{x^{2 \alpha} \left(x - 1\right)^{2 \alpha} \left(4 \alpha x^{2} - 4 \alpha x + \alpha - 2 x^{2} + 2 x - 1\right)}{x^{2} \left(x - 1\right)^{2}}\, dx\right) \Gamma\left(2 \left(\alpha + 1\right)\right) = 0 \right\} \setminus \left\{\alpha \mid \alpha \in \mathbb{C} \wedge e^{2 i \pi \alpha} \Gamma\left(2 \alpha + 1\right) {{{}_{2}F_{1}\left(\begin{matrix} - 2 \alpha, 2 \alpha + 1 \\ 2 \alpha + 2 \end{matrix}\middle| {1} \right)}}^{2} = 0 \right\}$

Your descriptions/explanations here

### 2.4 Exercise: Design your own wavefunction!

**Now you get to make your own wavefunction!**

The only guidance I would give you is that it make sense mathematically and that it include $\alpha$ so that you can minimize the energy.

Remember that $a$ and $x$ are both length units, and that trigonometric, logarithmic, and exponential functions are all unitless

Using your new wavefunction:

1. Find the minimum energy, $E_{trial}$
2. Plot the new trial wavefunction and compare it to the true solution and the wavefunction you found above
3. Compare you new energy to the trial energy you found above
4. Which wavefunction is better? How do you know?

```python
# Your code here
```

Your descriptions/explanations here

# Reading Homework

Read the following sections in Kramer

- 4.2.3 Born-Oppenheimer approximation
- 4.3.2 Secular equation
- All of 4.5

For each subsection
- write down the subchapter name
- what was the most important idea
- draw an idea digram of the main idea

**Make sure to upload this to the assignment repository**

Example idea diagram:

```python

```


# Gabarito da prova I de Biomecânica I - 2017

> http://demotu.org/ensino/biomecanica-i/

As soluções das questões das provas são apresentadas em Python.

## Diurno

### Questão 1 [2 pontos]

** a) [0,5] Equações para posição, velocidade e aceleração da partícula: **

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set_context("notebook", font_scale=1.2, rc={"lines.linewidth": 2,
                                                "lines.markersize": 10})
from IPython.display import display, Math
from sympy import Symbol, latex, lambdify, real_roots
from sympy.physics.mechanics import dynamicsymbols
```

A posição de uma partícula é dada por:

$$ \overrightarrow{\mathbf{r}}(t) = (1 - 4t + 2t^2)\,\hat{\mathbf{i}} + (2t)\,\hat{\mathbf{j}} \quad [m] $$

Calcule para o movimento da marca no intervalo [0, 2] s:
 a) Equações para posição, velocidade e aceleração da partícula
 b) Posição, velocidade e aceleração linear mínimas e máximas de cada componente
 c) Gráficos para posição, velocidade e aceleração linear de cada componente versus o tempo

**b) [0,5] Posição, velocidade e aceleração linear mínimas e máximas de cada componente:**

```python
# declare the symbolic variables
t = Symbol('t')
# create a Cartesian coordinate system
from sympy.vector import CoordSys3D
O = CoordSys3D('')

# position (given)
r = (1 -4*t + 2*t**2)*O.i + (2*t)*O.j
display(Math(latex('\overrightarrow{\mathbf{r}}(t) = ') + latex(r)))
# velocity
v = r.diff(t, 1)
display(Math(latex('\overrightarrow{\mathbf{v}}(t) = ') + latex(v)))
# acceleration
a = v.diff(t, 1)
display(Math(latex('\overrightarrow{\mathbf{a}}(t) = ') + latex(a)))
```

$$\overrightarrow{\mathbf{r}}(t) = (2 t^{2} - 4 t + 1)\mathbf{\hat{i}_{}} + (2 t)\mathbf{\hat{j}_{}}$$

$$\overrightarrow{\mathbf{v}}(t) = (4 t - 4)\mathbf{\hat{i}_{}} + (2)\mathbf{\hat{j}_{}}$$

$$\overrightarrow{\mathbf{a}}(t) = (4)\mathbf{\hat{i}_{}}$$

Em geral, para achar máximos e mínimos de uma função, nós poderíamos calcular sua derivada, igualar à zero e resolver esta equação.
Note que para o presente problema, as derivadas da posição e velocidade já foram calculadas no item anterior. A aceleração tem um valor constante, logo ou pode-se dizer que não há mínimo e máximo ou que o mínimo e máximo é igual a esta constante.

```python
# create functions for calculation of numerical values
ri = lambdify(t, r.dot(O.i), 'numpy')
rj = lambdify(t, r.dot(O.j), 'numpy')
vi = lambdify(t, v.dot(O.i), 'numpy')
vj = lambdify(t, v.dot(O.j), 'numpy')
ai = lambdify(t, a.dot(O.i), 'numpy')
aj = lambdify(t, a.dot(O.j), 'numpy')
```

```python
print('Posição em i, [mínimo, máximo] em m:')
print('[{0}, {1}]'.format(ri(1), ri(0)))
print('Posição em j, [mínimo, máximo] em m:')
print('[{0}, {1}]'.format(rj(0), rj(2)))
print('Velocidade em i, [mínimo, máximo] em m/s:')
print('[{0}, {1}]'.format(vi(0), vi(2)))
print('Velocidade em j, [mínimo, máximo] em m/s:')
print('[{0}, {1}]'.format(vj(0), vj(2)))
print('Aceleração em i, [mínimo, máximo] em m/s/s:')
print('[{0}, {1}]'.format(ai(0), ai(2)))
print('Aceleração em j, [mínimo, máximo] em m/s/s:')
print('[{0}, {1}]'.format(aj(0), aj(2)))
```

    Posição em i, [mínimo, máximo] em m:
    [-1, 1]
    Posição em j, [mínimo, máximo] em m:
    [0, 4]
    Velocidade em i, [mínimo, máximo] em m/s:
    [-4, 4]
    Velocidade em j, [mínimo, máximo] em m/s:
    [2, 2]
    Aceleração em i, [mínimo, máximo] em m/s/s:
    [4, 4]
    Aceleração em j, [mínimo, máximo] em m/s/s:
    [0, 0]

**c) [1,0] Gráficos para posição, velocidade e aceleração linear de cada componente versus o tempo:**

```python
# plots using matplotlib
tn = np.arange(0, 2.02, .02)
fig, axs = plt.subplots(1, 3, figsize=(12, 5), sharex=True)
axs[0].plot(tn, ri(tn), 'b', label='$\hat{\mathbf{i}}$', linewidth=3)
axs[0].plot(tn, rj(tn), 'r', label='$\hat{\mathbf{j}}$', linewidth=3)
axs[0].set_title('Displacement [$\mathrm{m}$]')
axs[1].plot(tn, vi(tn), 'b', label='$\hat{\mathbf{i}}$', linewidth=3)
axs[1].plot(tn, vj(tn)*np.ones(101), 'r', label='$\hat{\mathbf{j}}$', linewidth=3)
axs[1].set_title('Velocity [$\mathrm{m/s}$]')
axs[2].plot(tn, ai(tn)*np.ones(101), 'b', label='$\hat{\mathbf{i}}$', linewidth=3)
axs[2].plot(tn, aj(tn)*np.ones(101), 'r', label='$\hat{\mathbf{j}}$', linewidth=3)
axs[2].set_title('Acceleration [$\mathrm{m/s^2}$]')
axs[2].legend(loc = 'center')
for axi in axs:
    axi.set_xlabel('Time [s]', fontsize=14)
    axi.grid(True)
```

### Questão 2 [3 pontos]

Considere as seguintes posições de marcas colocadas sobre uma coxa mensuradas por um sistema de captura do movimento: maléolo lateral (ml = [2.92, 10.10, 18.85]), maléolo medial (mm = [2.71, 10.22, 26.52]), cabeça da fíbula (fib = [5.05, 41.90, 15.41]), e côndilo medial da tíbia (tib = [8.29, 41.88, 26.52]). Estas posições estão na ordem x, y, z e são descritas no sistema de coordenadas do laboratório onde x aponta para a frente do sujeito, y aponta para cima e z aponta para o lado. Os centros articulares do tornozelo e joelho estão localizados respectivamente nos centros geométricos entre as marcas ml e mm e entre as marcas fib e tib. Um sistema de coordenadas anatômico para a perna pode ser definido como um eixo quase vertical apontando para cima e passando pelos centros articulares do tornozelo e joelho; outro eixo quase anteroposterior como o produto vetorial do eixo quase vertical e um vetor na direção médio-lateral passando por mm e ml; e o último eixo como o produto vetorial dos dois eixos anteriores, e a origem no centro articular do tornozelo.
 a. Calcule o sistema de coordenadas anatômico para a perna como descrito acima.
 b. Demonstre que a base deste sistema de coordenadas é ortonormal.

**a. [1,5] Calcule o sistema de coordenadas anatômico para a perna como descrito acima:**

```python
ml = np.array([2.92, 10.10, 18.85])
mm = np.array([2.71, 10.22, 26.52])
fib = np.array([5.05, 41.90, 15.41])
tib = np.array([8.29, 41.88, 26.52])

tornoz = (ml + mm)/2
joelho = (fib + tib)/2

v1 = joelho - tornoz        # first axis
v2 = np.cross(v1, mm - ml)  # second axis
v3 = np.cross(v2, v1)       # third axis

# Vector normalization
e1 = v1/np.linalg.norm(v1)
e2 = v2/np.linalg.norm(v2)
e3 = v3/np.linalg.norm(v3)

print('Origem:', '\nO =', tornoz)
print('Versores:', '\ne1 =', e1, '\ne2 =', e2, '\ne3 =', e3)
```

    Origem:
    O = [  2.815  10.16   22.685]
    Versores:
    e1 = [ 0.12043275  0.99126617 -0.05373394]
    e2 = [ 0.99246903 -0.11900497  0.02903508]
    e3 = [-0.02238689  0.05682604  0.99813307]

**b. [1,5] Demonstre que a base deste sistema de coordenadas é ortonormal:**

```python
print('\nTest of orthogonality (cross product between versors):',
      '\ne1 x e2:', np.linalg.norm(np.cross(e1, e2)),
      '\ne1 x e3:', np.linalg.norm(np.cross(e1, e3)),
      '\ne2 x e3:', np.linalg.norm(np.cross(e2, e3)))
print('\nNorm of each versor:',
      '\n||e1|| =', np.linalg.norm(e1),
      '\n||e2|| =', np.linalg.norm(e2),
      '\n||e3|| =', np.linalg.norm(e3))
```

    Test of orthogonality (cross product between versors):
    e1 x e2: 1.0
    e1 x e3: 1.0
    e2 x e3: 1.0

    Norm of each versor:
    ||e1|| = 1.0
    ||e2|| = 1.0
    ||e3|| = 1.0

### Questão 3 [3 pontos]

Durante um lançamento de disco, a distância entre o disco e o centro de rotação do atleta, antes do disco sair da mão do competidor, segue a seguinte relação:$r = \frac{\theta^2}{2}$ m. Além disso, sabe-se que o atleta rotaciona em torno do próprio eixo com uma velocidade angular $\omega = \frac{t}{3}$ rad/s, e que a altura do disco em relação ao solo varia de acordo com a relação: $h = 0,1t+1$ m.  Considerando isso, pede-se:

a) Encontre uma expressão do vetor que caracteriza a posição do disco antes de sair da mão do atleta em função do tempo, utilizando uma base fixa no solo. Deixe claro qual a origem da base que você utilizou.

b) Encontre a expressão do mesmo vetor  posição utilizando uma base em coordenadas polares fixa na posição do disco.

c) Encontre a expressão do vetor velocidade na base em coordenadas polares.

d) Encontre a expressão do vetor aceleração na base em coordenadas polares.

e) Esboce a trajetória seguida pelo disco. Esboce, em qualquer ponto da trajetória, os vetores que formam a base em coordenadas polares.

```python
import sympy as sym
from sympy.vector import CoordSys3D, express
sym.init_printing()
from sympy.plotting import plot_parametric, plot3d_parametric_line
from sympy.physics.mechanics import ReferenceFrame, Vector, dot
```

```python
O = sym.vector.CoordSys3D(' ')

t = sym.symbols('t')
er = sym.symbols('\hat{e}_r')
```

```python
omega = t/3
theta  = (t**2)/6
h = 0.1*t+1
r = (theta**2)/2
```

** a. [0,6] Encontre uma expressão do vetor que caracteriza a posição do disco antes de sair da mão do atleta em função do tempo, utilizando uma base fixa no solo. Deixe claro qual a origem da base que você utilizou.   **

```python
s = r*sym.cos(theta)*O.i+r*sym.sin(theta)*O.j+h*O.k
display(Math(latex('\overrightarrow{\mathbf{s}}(t) = ') + latex(s)))
```

$$\overrightarrow{\mathbf{s}}(t) = (\frac{t^{4}}{72} \cos{\left (\frac{t^{2}}{6} \right )})\mathbf{\hat{i}_{ }} + (\frac{t^{4}}{72} \sin{\left (\frac{t^{2}}{6} \right )})\mathbf{\hat{j}_{ }} + (0.1 t + 1)\mathbf{\hat{k}_{ }}$$

Com a origem da base localizada no eixo de rotação do atleta, no solo.

**b. [0,6] Encontre a expressão do mesmo vetor  posição utilizando uma base em coordenadas polares fixa na posição do disco.**

```python
P = O.orient_new_axis(' ', theta, O.k,vector_names=['e_r','e_{}','k'])
```

```python
#s = r*P.i + h*P.k
display(Math(latex('\overrightarrow{\mathbf{s}}(t) = ') + latex(sym.simplify(express(s,P)))))
```

$$\overrightarrow{\mathbf{s}}(t) = (\frac{t^{4}}{72})\mathbf{\hat{e_r}_{ }} + (0.1 t + 1)\mathbf{\hat{k}_{ }}$$

** c.[0,6] Encontre a expressão do vetor velocidade na base em coordenadas polares.**

```python
v = sym.diff(s,t)
display(Math(latex('\overrightarrow{\mathbf{v}}(t) = ') + latex(sym.simplify(express(v,P)))))
```

$$\overrightarrow{\mathbf{v}}(t) = (\frac{t^{3}}{18})\mathbf{\hat{e_r}_{ }} + (\frac{t^{5}}{216})\mathbf{\hat{e_{}}_{ }} + (0.1)\mathbf{\hat{k}_{ }}$$

** d.[0,6] Encontre a expressão do vetor aceleração na base em coordenadas polares.**

```python
a = sym.diff(v)
display(Math(latex('\overrightarrow{\mathbf{a}}(t) = ') + latex(sym.simplify(express(a,P)))))
```

$$\overrightarrow{\mathbf{a}}(t) = (\frac{t^{2}}{648} \left(- t^{4} + 108\right))\mathbf{\hat{e_r}_{ }} + (\frac{t^{4}}{24})\mathbf{\hat{e_{}}_{ }}$$

** e.[0,6] Esboce a trajetória seguida pelo disco. Esboce, em qualquer ponto da trajetória, os vetores que formam a base em coordenadas polares. **

```python
plot3d_parametric_line(s.dot(O.i),s.dot(O.j),s.dot(O.k), (t,4,10))
```

### Questão 4 [2 pontos]

Na tabela abaixo estãos gravadas as coordenadas do centro de massa durante os saltos em distância de três saltadores. Nesta tabela, a primeira coluna é o número de identificação do saltador, a segunda coluna é o instante em segundos, a terceira é a coordenada X (paralela ao solo) em metros e a quarta coluna é a coordenada Y (ortogonal ao solo) em metros. Considerando isso, pede-se para escrever um pseudocódigo de um programa de computador que execute as seguintes tarefas:

a) Dada uma matriz com os valores da tabela, monte uma matriz que contenha as coordenadas X do saltador 2 em uma coluna e as coordenadas Y do mesmo saltador em outra coluna. Além disso, monte um vetor com os instantes correspondentes a cada coordenada.

b) Dada a matriz com as coordenadas X e Y, e o vetor de tempo obtidos no item anterior, obtenha o vetor velocidade do saltador 2 em cada instante.

c) Tendo disponíveis as matrizes formadas nos itens anteriores, encontre uma base cuja origem seja a posição do saltador 2 e um dos seus vetores seja tangente à trajetória realizada pelo saltador.

```python
t = np.linspace(0,0.65,20)
r1 = np.transpose(np.array([10*t,-9.81/2*t**2+3.4*t]))
r2 = np.transpose(np.array([11*t,-9.81/2*t**2+3.5*t]))
r3 = np.transpose(np.array([9*t,-9.81/2*t**2+3*t]))
r = np.zeros((3*len(t),4))
for i in range(len(t)):
    r[3*(i):3*(i+1),:] = np.hstack((np.array([[1],[2],[3]]), np.array([[t[i]],[t[i]],[t[i]]]), np.vstack((r1[i,:],r2[i,:],r3[i,:]))))
r
```

    array([[ 1.        ,  0.        ,  0.        ,  0.        ],
           [ 2.        ,  0.        ,  0.        ,  0.        ],
           [ 3.        ,  0.        ,  0.        ,  0.        ],
           [ 1.        ,  0.03421053,  0.34210526,  0.11057517],
           [ 2.        ,  0.03421053,  0.37631579,  0.11399623],
           [ 3.        ,  0.03421053,  0.30789474,  0.09689096],
           [ 1.        ,  0.06842105,  0.68421053,  0.20966911],
           [ 2.        ,  0.06842105,  0.75263158,  0.21651122],
           [ 3.        ,  0.06842105,  0.61578947,  0.18230069],
           [ 1.        ,  0.10263158,  1.02631579,  0.29728182],
           [ 2.        ,  0.10263158,  1.12894737,  0.30754498],
           [ 3.        ,  0.10263158,  0.92368421,  0.25622919],
           [ 1.        ,  0.13684211,  1.36842105,  0.3734133 ],
           [ 2.        ,  0.13684211,  1.50526316,  0.38709751],
           [ 3.        ,  0.13684211,  1.23157895,  0.31867645],
           [ 1.        ,  0.17105263,  1.71052632,  0.43806354],
           [ 2.        ,  0.17105263,  1.88157895,  0.4551688 ],
           [ 3.        ,  0.17105263,  1.53947368,  0.36964249],
           [ 1.        ,  0.20526316,  2.05263158,  0.49123255],
           [ 2.        ,  0.20526316,  2.25789474,  0.51175886],
           [ 3.        ,  0.20526316,  1.84736842,  0.40912729],
           [ 1.        ,  0.23947368,  2.39473684,  0.53292033],
           [ 2.        ,  0.23947368,  2.63421053,  0.55686769],
           [ 3.        ,  0.23947368,  2.15526316,  0.43713085],
           [ 1.        ,  0.27368421,  2.73684211,  0.56312687],
           [ 2.        ,  0.27368421,  3.01052632,  0.59049529],
           [ 3.        ,  0.27368421,  2.46315789,  0.45365319],
           [ 1.        ,  0.30789474,  3.07894737,  0.58185218],
           [ 2.        ,  0.30789474,  3.38684211,  0.61264166],
           [ 3.        ,  0.30789474,  2.77105263,  0.45869429],
           [ 1.        ,  0.34210526,  3.42105263,  0.58909626],
           [ 2.        ,  0.34210526,  3.76315789,  0.62330679],
           [ 3.        ,  0.34210526,  3.07894737,  0.45225416],
           [ 1.        ,  0.37631579,  3.76315789,  0.58485911],
           [ 2.        ,  0.37631579,  4.13947368,  0.62249069],
           [ 3.        ,  0.37631579,  3.38684211,  0.43433279],
           [ 1.        ,  0.41052632,  4.10526316,  0.56914072],
           [ 2.        ,  0.41052632,  4.51578947,  0.61019335],
           [ 3.        ,  0.41052632,  3.69473684,  0.40493019],
           [ 1.        ,  0.44473684,  4.44736842,  0.5419411 ],
           [ 2.        ,  0.44473684,  4.89210526,  0.58641479],
           [ 3.        ,  0.44473684,  4.00263158,  0.36404636],
           [ 1.        ,  0.47894737,  4.78947368,  0.50326025],
           [ 2.        ,  0.47894737,  5.26842105,  0.55115499],
           [ 3.        ,  0.47894737,  4.31052632,  0.3116813 ],
           [ 1.        ,  0.51315789,  5.13157895,  0.45309816],
           [ 2.        ,  0.51315789,  5.64473684,  0.50441395],
           [ 3.        ,  0.51315789,  4.61842105,  0.24783501],
           [ 1.        ,  0.54736842,  5.47368421,  0.39145485],
           [ 2.        ,  0.54736842,  6.02105263,  0.44619169],
           [ 3.        ,  0.54736842,  4.92631579,  0.17250748],
           [ 1.        ,  0.58157895,  5.81578947,  0.3183303 ],
           [ 2.        ,  0.58157895,  6.39736842,  0.37648819],
           [ 3.        ,  0.58157895,  5.23421053,  0.08569872],
           [ 1.        ,  0.61578947,  6.15789474,  0.23372452],
           [ 2.        ,  0.61578947,  6.77368421,  0.29530346],
           [ 3.        ,  0.61578947,  5.54210526, -0.01259127],
           [ 1.        ,  0.65      ,  6.5       ,  0.1376375 ],
           [ 2.        ,  0.65      ,  7.15      ,  0.2026375 ],
           [ 3.        ,  0.65      ,  5.85      , -0.1223625 ]])

** a. [0,5] Dada uma matriz com os valores da tabela, monte uma matriz que contenha as coordenadas X do saltador 2 em uma coluna e as coordenadas Y do mesmo saltador em outra coluna. Além disso, monte um vetor com os instantes correspondentes a cada coordenada.**

```python
P = np.zeros((int(np.size(r,0)/3),2))
time = np.zeros((int(np.size(r,0)/3),1))
k = 0
for i in range(np.size(r,0)):
    if r[i,0] == 2:
        P[k,:] = np.array([r[i,2],r[i,3]])
        time[k] = r[i,1]
        k += 1
print('P = ' + str(P))
print('t = ' + str(time))
```

    P = [[ 0.          0.        ]
     [ 0.37631579  0.11399623]
     [ 0.75263158  0.21651122]
     [ 1.12894737  0.30754498]
     [ 1.50526316  0.38709751]
     [ 1.88157895  0.4551688 ]
     [ 2.25789474  0.51175886]
     [ 2.63421053  0.55686769]
     [ 3.01052632  0.59049529]
     [ 3.38684211  0.61264166]
     [ 3.76315789  0.62330679]
     [ 4.13947368  0.62249069]
     [ 4.51578947  0.61019335]
     [ 4.89210526  0.58641479]
     [ 5.26842105  0.55115499]
     [ 5.64473684  0.50441395]
     [ 6.02105263  0.44619169]
     [ 6.39736842  0.37648819]
     [ 6.77368421  0.29530346]
     [ 7.15        0.2026375 ]]
    t = [[ 0.        ]
     [ 0.03421053]
     [ 0.06842105]
     [ 0.10263158]
     [ 0.13684211]
     [ 0.17105263]
     [ 0.20526316]
     [ 0.23947368]
     [ 0.27368421]
     [ 0.30789474]
     [ 0.34210526]
     [ 0.37631579]
     [ 0.41052632]
     [ 0.44473684]
     [ 0.47894737]
     [ 0.51315789]
     [ 0.54736842]
     [ 0.58157895]
     [ 0.61578947]
     [ 0.65      ]]

** b. [0,5] Dada a matriz com as coordenadas X e Y, e o vetor de tempo obtidos no item anterior, obtenha o vetor velocidade do saltador 2 em cada instante. **

```python
deltaT = time[1] - time[0]
v = np.diff(P,1,0)/deltaT
print('v=' + str(v))
```

    v=[[ 11.           3.33219737]
     [ 11.           2.99659211]
     [ 11.           2.66098684]
     [ 11.           2.32538158]
     [ 11.           1.98977632]
     [ 11.           1.65417105]
     [ 11.           1.31856579]
     [ 11.           0.98296053]
     [ 11.           0.64735526]
     [ 11.           0.31175   ]
     [ 11.          -0.02385526]
     [ 11.          -0.35946053]
     [ 11.          -0.69506579]
     [ 11.          -1.03067105]
     [ 11.          -1.36627632]
     [ 11.          -1.70188158]
     [ 11.          -2.03748684]
     [ 11.          -2.37309211]
     [ 11.          -2.70869737]]

** c.[1,0] Tendo disponíveis as matrizes formadas nos itens anteriores, encontre uma base cuja origem seja a posição do saltador 2 e um dos seus vetores seja tangente à trajetória realizada pelo saltador.**

```python
vNorm = np.sqrt(v[:,0]**2+v[:,1]**2)
et = np.transpose([v[:,0]/vNorm, v[:,1]/vNorm])
print('e_t = ' + str(et))
```

    e_t = [[ 0.95705183  0.28991687]
     [ 0.96483966  0.26283917]
     [ 0.9719649   0.23512598]
     [ 0.97837746  0.20682736]
     [ 0.98403048  0.17800005]
     [ 0.98888128  0.14870716]
     [ 0.99289214  0.1190176 ]
     [ 0.99603114  0.08900539]
     [ 0.9982728   0.05874883]
     [ 0.99959864  0.02832953]
     [ 0.99999765 -0.00216866]
     [ 0.99946649 -0.0326608 ]
     [ 0.99800961 -0.06306203]
     [ 0.99563909 -0.09328876]
     [ 0.99237444 -0.12325979]
     [ 0.98824207 -0.15289736]
     [ 0.98327481 -0.18212813]
     [ 0.9775111  -0.21088399]
     [ 0.97099428 -0.2391027 ]]

```python
det = (np.diff(et,1,0)/deltaT)
C = np.transpose([det[:,0]/vNorm[0:-1], det[:,1]/vNorm[0:-1]])
CNorm = np.sqrt(C[:,0]**2+C[:,1]**2)
en = np.transpose([C[:,0]/CNorm, C[:,1]/CNorm])
print('e_n = ' + str(en))
```

    e_n = [[ 0.27640545 -0.96104112]
     [ 0.24900807 -0.96850141]
     [ 0.22099993 -0.97527382]
     [ 0.19243446 -0.98130983]
     [ 0.16337161 -0.9865646 ]
     [ 0.1338774  -0.9909979 ]
     [ 0.10402334 -0.99457486]
     [ 0.07388561 -0.99726672]
     [ 0.04354423 -0.9990515 ]
     [ 0.01308196 -0.99991443]
     [-0.01741675 -0.99984832]
     [-0.04786696 -0.99885372]
     [-0.07818438 -0.99693892]
     [-0.10828658 -0.99411972]
     [-0.13809403 -0.99041912]
     [-0.16753116 -0.98586678]
     [-0.19652719 -0.98049837]
     [-0.22501694 -0.97435485]]

## Noturno

### Questão 1 [3 pontos]

Considere um sujeito parado em pé com o braço na vertical, ao lado do tronco, e cotovelo flexionado 90 graus fazendo com que o ante-braço fique na horizontal e à frente de seu tronco. A partir desta posição inicial, o sujeito realiza mais uma flexão do cotovelo de 90 graus e para depois de 1 s. A equação de movimento para uma marca na mão do sujeito é:

$$ \overrightarrow{\mathbf{r}}(t) = (0.5 - 5t^3 + 7.5t^4 - 3t^5)\,\hat{\mathbf{i}} + (1.0 + 5t^3 - 7.5t^4 + 3t^5)\,\hat{\mathbf{j}} \quad [m]$$

Onde $\hat{\mathbf{i}}$ e $\hat{\mathbf{j}}$ representam os versores de um sistema de coordenadas cartesianas com sentidos positivos na horizontal para frente e na vertical para cima, respectivamente.

Calcule para o movimento da marca no intervalo [0, 1] s:
 a) Equações para velocidade e aceleração linear
 b) Posição, velocidade e aceleração linear mínimas e máximas de cada componente
 c) Gráficos para posição, velocidade e aceleração linear de cada componente versus o tempo
 d) Equações para posição, velocidade e aceleração angular do ante-braço

**a) [0,75] Equações para velocidade e aceleração linear:**

```python
# declare the symbolic variables
t = Symbol('t')
# create a Cartesian coordinate system
from sympy.vector import CoordSys3D
O = CoordSys3D('')

# position (given)
r = (0.5 - 5*t**3 + 7.5*t**4 - 3*t**5)*O.i + (1.0 + 5*t**3 - 7.5*t**4 + 3*t**5)*O.j
display(Math(latex('\overrightarrow{\mathbf{r}}(t) = ') + latex(r)))
# velocity
v = r.diff(t, 1)
display(Math(latex('\overrightarrow{\mathbf{v}}(t) = ') + latex(v)))
# acceleration
a = v.diff(t, 1)
display(Math(latex('\overrightarrow{\mathbf{a}}(t) = ') + latex(a)))
```

$$\overrightarrow{\mathbf{r}}(t) = (- 3 t^{5} + 7.5 t^{4} - 5 t^{3} + 0.5)\mathbf{\hat{i}_{}} + (3 t^{5} - 7.5 t^{4} + 5 t^{3} + 1.0)\mathbf{\hat{j}_{}}$$

$$\overrightarrow{\mathbf{v}}(t) = (- 15 t^{4} + 30.0 t^{3} - 15 t^{2})\mathbf{\hat{i}_{}} + (15 t^{4} - 30.0 t^{3} + 15 t^{2})\mathbf{\hat{j}_{}}$$

$$\overrightarrow{\mathbf{a}}(t) = (- 60 t^{3} + 90.0 t^{2} - 30 t)\mathbf{\hat{i}_{}} + (60 t^{3} - 90.0 t^{2} + 30 t)\mathbf{\hat{j}_{}}$$

**b) [0,75]Posição, velocidade e aceleração linear mínimas e máximas de cada componente:**

Em geral, para achar máximos e mínimos de uma função, nós poderíamos calcular sua derivada, igualar à zero e resolver esta equação.
Note que para o presente problema, as derivadas da posição e velocidade já foram calculadas no item anterior. Vamos calcular a derivada da aceleração (conhecida como jerk ou solavanco):

```python
# jerk
jerk = a.diff(t, 1)
display(Math(latex('\overrightarrow{\mathbf{jerk}}(t) = ') + latex(jerk)))
```

$$\overrightarrow{\mathbf{jerk}}(t) = (- 180 t^{2} + 180.0 t - 30)\mathbf{\hat{i}_{}} + (180 t^{2} - 180.0 t + 30)\mathbf{\hat{j}_{}}$$

- Note que as duas componentes da velocidade, da aceleração e do jerk são iguais a menos do sinal trocado entre elas. Isto quer dizer que o máximo numa das componentes é o mínimo na outra, e vice-versa. Então só precisamos achar mínimos e máximos para uma das componentes.
- Note que os polinômios para velocidade (grau 4) e para aceleração (grau 3) não possuem termos independentes de $t$ (grau zero), logo $t=0$ é uma raiz (máximo ou mínimo) destes polinômios.
- Para achar outras raízes, podemos fatorar (dividir) o polinômio para velocidade por $t^2$ e para aceleração por $t$ e resolver os polinômios de segunda ordem (e para achar os máximos e mínimos da aceleração, o polinômio para o jerk também é de segunda ordem!).

Por inspeção, a raiz do polinômio para velocidade, $15t^2-30t+15=0$, é $t=1s$ e a raiz do polinômio para aceleração, $60t^2-90t+30=0$, é $t=0.5s$.
As raizes do polinômio para jerk são:

```python
roots = real_roots(jerk.dot(O.j))
print(roots)
roots = [x.evalf() for x in roots]
print(roots)
```

    [-sqrt(3)/6 + 1/2, sqrt(3)/6 + 1/2]
    [0.211324865405187, 0.788675134594813]

Substituindo os valores correspondentes nas expressões para posição, velocidade e aceleração:

```python
# create functions for calculation of numerical values
ri = lambdify(t, r.dot(O.i), 'numpy')
rj = lambdify(t, r.dot(O.j), 'numpy')
vi = lambdify(t, v.dot(O.i), 'numpy')
vj = lambdify(t, v.dot(O.j), 'numpy')
ai = lambdify(t, a.dot(O.i), 'numpy')
aj = lambdify(t, a.dot(O.j), 'numpy')
```

```python
print('Posição horizontal, [mínimo, máximo] em m:')
print('[{0}, {1}]'.format(ri(1), ri(0)))
print('Posição vertical, [mínimo, máximo] em m:')
print('[{0}, {1}]'.format(rj(0), rj(1)))
print('Velocidade horizontal, [mínimo, máximo] em m/s:')
print('[{0}, {1}]'.format(vi(.5), vi(0)))
print('Velocidade vertical, [mínimo, máximo] em m/s:')
print('[{0}, {1}]'.format(vj(0), vj(.5)))
print('Aceleração horizontal, [mínimo, máximo] em m/s/s:')
print('[{0}, {1}]'.format(ai(roots[0]), ai(roots[1])))
print('Aceleração vertical, [mínimo, máximo] em m/s/s:')
print('[{0}, {1}]'.format(aj(roots[1]), aj(roots[0])))
```

    Posição horizontal, [mínimo, máximo] em m:
    [0.0, 0.5]
    Posição vertical, [mínimo, máximo] em m:
    [1.0, 1.5]
    Velocidade horizontal, [mínimo, máximo] em m/s:
    [-0.9375, 0.0]
    Velocidade vertical, [mínimo, máximo] em m/s:
    [0.0, 0.9375]
    Aceleração horizontal, [mínimo, máximo] em m/s/s:
    [-2.88675134594813, 2.88675134594813]
    Aceleração vertical, [mínimo, máximo] em m/s/s:
    [-2.88675134594813, 2.88675134594813]

**c) [0,75]Gráficos para posição, velocidade e aceleração linear de cada componente versus o tempo:**

```python
# plots using matplotlib
tn = np.arange(0, 1.01, .01)
fig, axs = plt.subplots(1, 3, figsize=(12, 5), sharex=True)
axs[0].plot(tn, ri(tn), 'b', label='$\hat{\mathbf{i}}$', linewidth=3)
axs[0].plot(tn, rj(tn), 'r', label='$\hat{\mathbf{j}}$', linewidth=3)
axs[0].set_title('Displacement [$\mathrm{m}$]')
axs[1].plot(tn, vi(tn), 'b', label='$\hat{\mathbf{i}}$', linewidth=3)
axs[1].plot(tn, vj(tn), 'r', label='$\hat{\mathbf{j}}$', linewidth=3)
axs[1].set_title('Velocity [$\mathrm{m/s}$]')
axs[2].plot(tn, ai(tn), 'b', label='$\hat{\mathbf{i}}$', linewidth=3)
axs[2].plot(tn, aj(tn), 'r', label='$\hat{\mathbf{j}}$', linewidth=3)
axs[2].set_title('Acceleration [$\mathrm{m/s^2}$]')
axs[0].legend(loc = 'center')
for axi in axs:
    axi.set_xlabel('Time [s]', fontsize=14)
    axi.grid(True)
```

** d)[0,75] Equações para posição, velocidade e aceleração angular do ante-braço:**

A partir da equação da posição ou do gráfico pode-se inferir que o cotovelo está na posição [0, 1] m e que o comprimento do ante-braço, L, é igual a 0,5 m.
A partir da posição linear, o ângulo do ante-braço com a horizontal, $\alpha$, pode ser calculado usando a função trigonométrica inversa da tangente ou do seno, por exemplo:

$$ \alpha(t) = \arcsin\left(\frac{1.0 + 5t^3 - 7.5t^4 + 3t^5 - 1}{0.5}\right) $$

```python
angle = dynamicsymbols('alpha')  # it's already alpha(t)
```

```python
vangle = angle.diff(t, 1)
```

```python
aangle = vangle.diff(t, 1)
```

```python
display(Math(latex(r'\dot{\alpha}(t)=') + latex(vangle)))
display(Math(latex(r'\ddot{\alpha}(t)=') + latex(aangle)))
```

$$\dot{\alpha}(t)=\frac{d}{d t} \alpha{\left (t \right )}$$

$$\ddot{\alpha}(t)=\frac{d^{2}}{d t^{2}}  \alpha{\left (t \right )}$$

As expressões acima são válidas como resposta, já que calcular a segunda derivada do `arcsin` é trabalhoso.

### Questão 2 [2 pontos]

Considere a situação em que uma bailarina gira em torno do próprio eixo. Nesta situação, a distância entre a mão da bailarina e o seu centro de rotação segue a seguinte relação: $r = \frac{2}{\theta}$ m. Além disso, a bailarina rotaciona em torno do próprio eixo com uma velocidade angular $\omega = \frac{t}{2}$ rad/s, e que a altura da sua mão em relação ao solo se mantém constante em 1,3 m.  Considerando isso, pede-se:

a) Encontre a expressão do vetor  posição da mão da bailarina utilizando uma base em coordenadas polares fixa na posição da mão.

b) Encontre a expressão do vetor velocidade na base em coordenadas polares.

c) Encontre a expressão do vetor aceleração na base em coordenadas polares.

d) Esboce a trajetória seguida pela mão. Esboce, em qualquer ponto da trajetória, os vetores que formam a base em coordenadas polares.

```python
O = sym.vector.CoordSys3D(' ')

t = sym.symbols('t')
er = sym.symbols('\hat{e}_r')
```

```python
omega = t/2
theta  = (t**2)/4
h = 1.3
r = 2/theta
```

** a. [0,5]Encontre a expressão do vetor  posição da mão da bailarina utilizando uma base em coordenadas polares fixa na posição da mão.     **

```python
s = r*sym.cos(theta)*O.i+r*sym.sin(theta)*O.j
```

```python
P = O.orient_new_axis(' ', theta, O.k,vector_names=['e_r','e_{theta}','k'])
```

```python
#s = r*P.i + h*P.k
display(Math(latex('\overrightarrow{\mathbf{s}}(t) = ') + latex(sym.simplify(express(s,P)))))
```

$$\overrightarrow{\mathbf{s}}(t) = (\frac{8}{t^{2}})\mathbf{\hat{e_r}_{ }}$$

** b. [0,5]Encontre a expressão do vetor velocidade na base em coordenadas polares.**

```python
v = sym.diff(s,t)
display(Math(latex('\overrightarrow{\mathbf{v}}(t) = ') + latex(sym.simplify(express(v,P)))))
```

$$\overrightarrow{\mathbf{v}}(t) = (- \frac{16}{t^{3}})\mathbf{\hat{e_r}_{ }} + (\frac{4}{t})\mathbf{\hat{e_{theta}}_{ }}$$

** c.[0,5] Encontre a expressão do vetor aceleração na base em coordenadas polares.**

```python
a = sym.diff(v)
display(Math(latex('\overrightarrow{\mathbf{a}}(t) = ') + latex(sym.simplify(express(a,P)))))
```

$$\overrightarrow{\mathbf{a}}(t) = (-2 + \frac{48}{t^{4}})\mathbf{\hat{e_r}_{ }} + (- \frac{12}{t^{2}})\mathbf{\hat{e_{theta}}_{ }}$$

** d. [0,5]Esboce a trajetória seguida pela mão. Esboce, em qualquer ponto da trajetória, os vetores que formam a base em coordenadas polares. **

```python
plot_parametric(s.dot(O.i), s.dot(O.j),(t,3,10))
```

### Questão 3 [3 pontos]

Considere as seguintes posições de marcas colocadas sobre uma coxa mensuradas por um sistema de captura do movimento: epicôndilo lateral (el = [2.92, 10.10, 18.85]) e epicôndilo medial (em = [2.71, 10.22, 26.52]). Além disso, foi estimada a posição da cabeça do trocanter deste fêmur (tr = [5.05, 41.90, 15.41]). Estas posições estão na ordem x, y, z e são descritas no sistema de coordenadas do laboratório onde x aponta para a frente do sujeito, y aponta para cima e z aponta para o lado. O centro articular do joelho está localizado no centro geométrico entre as marcas el e em o centro articular do quadril coincide com a cabeça do trocanter. Um sistema de coordenadas anatômico para a coxa pode ser definido como um eixo quase vertical apontando para cima e passando pelos centros articulares do joelho e do quadril; outro eixo quase anteroposterior apontando para frente como o produto vetorial do eixo quase vertical e um vetor na direção médio-lateral passando por el e em; e o último eixo como o produto vetorial dos dois eixos anteriores, e a origem no centro articular do joelho.
 a. Calcule o sistema de coordenadas anatômico para a coxa como descrito acima.
 b. Demonstre que a base deste sistema de coordenadas é ortonormal.

**a. [1,5] Calcule o sistema de coordenadas anatômico para a coxa como descrito acima:**

```python
el = np.array([2.92, 10.10, 18.85])
em = np.array([2.71, 10.22, 26.52])
tr = np.array([5.05, 41.90, 15.41])

quadri = tr
joelho = (el + em)/2

v1 = quadri - joelho        # first axis
v2 = np.cross(v1, em - el)  # second axis
v3 = np.cross(v2, v1)       # third axis

# Vector normalization
e1 = v1/np.linalg.norm(v1)
e2 = v2/np.linalg.norm(v2)
e3 = v3/np.linalg.norm(v3)

print('Origem:', '\nO =', joelho)
print('Versores:', '\ne1 =', e1, '\ne2 =', e2, '\ne3 =', e3)
```

    Origem:
    O = [  2.815  10.16   22.685]
    Versores:
    e1 = [ 0.06847494  0.97243612 -0.22288824]
    e2 = [ 0.99756392 -0.06375548  0.02831018]
    e3 = [-0.0133195   0.22428381  0.97443284]

**b. [1,5]Demonstre que a base deste sistema de coordenadas é ortonormal:**

```python
print('\nTest of orthogonality (cross product between versors):',
      '\ne1 x e2:', np.linalg.norm(np.cross(e1, e2)),
      '\ne1 x e3:', np.linalg.norm(np.cross(e1, e3)),
      '\ne2 x e3:', np.linalg.norm(np.cross(e2, e3)))
print('\nNorm of each versor:',
      '\n||e1|| =', np.linalg.norm(e1),
      '\n||e2|| =', np.linalg.norm(e2),
      '\n||e3|| =', np.linalg.norm(e3))
```

    Test of orthogonality (cross product between versors):
    e1 x e2: 1.0
    e1 x e3: 1.0
    e2 x e3: 1.0

    Norm of each versor:
    ||e1|| = 1.0
    ||e2|| = 1.0
    ||e3|| = 1.0

### Questão 4 [2 pontos]

Na tabela abaixo estãos gravadas as coordenadas do centro de massa durante os saltos em distância de três saltadores. Nesta tabela, a primeira coluna é o número de identificação do saltador, a segunda coluna é o instante em segundos, a terceira é a coordenada X (paralela ao solo) em metros e a quarta coluna é a coordenada Y (ortogonal ao solo) em metros. Considerando isso, pede-se para escrever um pseudocódigo de um programa de computador que execute as seguintes tarefas:

a) Dada uma matriz com os valores da tabela, monte uma matriz que contenha as coordenadas X do saltador 3 em uma coluna e as coordenadas Y do mesmo saltador em outra coluna. Além disso, monte um vetor com os instantes correspondentes a cada coordenada.

b) Dada a matriz com as coordenadas X e Y, e o vetor de tempo obtidos no item anterior, obtenha o vetor velocidade do saltador 3 em cada instante.

c) Tendo disponíveis as matrizes formadas nos itens anteriores, encontre uma base cuja origem seja a posição do saltador 3 e um dos seus vetores seja tangente à trajetória realizada pelo saltador.

```python
t = np.linspace(0,0.65,20)
r1 = np.transpose(np.array([10*t,-9.81/2*t**2+3.4*t]))
r2 = np.transpose(np.array([11*t,-9.81/2*t**2+3.5*t]))
r3 = np.transpose(np.array([9*t,-9.81/2*t**2+3*t]))
r = np.zeros((3*len(t),4))
for i in range(len(t)):
    r[3*(i):3*(i+1),:] = np.hstack((np.array([[1],[2],[3]]), np.array([[t[i]],[t[i]],[t[i]]]), np.vstack((r1[i,:],r2[i,:],r3[i,:]))))
r
```

    array([[ 1.        ,  0.        ,  0.        ,  0.        ],
           [ 2.        ,  0.        ,  0.        ,  0.        ],
           [ 3.        ,  0.        ,  0.        ,  0.        ],
           [ 1.        ,  0.03421053,  0.34210526,  0.11057517],
           [ 2.        ,  0.03421053,  0.37631579,  0.11399623],
           [ 3.        ,  0.03421053,  0.30789474,  0.09689096],
           [ 1.        ,  0.06842105,  0.68421053,  0.20966911],
           [ 2.        ,  0.06842105,  0.75263158,  0.21651122],
           [ 3.        ,  0.06842105,  0.61578947,  0.18230069],
           [ 1.        ,  0.10263158,  1.02631579,  0.29728182],
           [ 2.        ,  0.10263158,  1.12894737,  0.30754498],
           [ 3.        ,  0.10263158,  0.92368421,  0.25622919],
           [ 1.        ,  0.13684211,  1.36842105,  0.3734133 ],
           [ 2.        ,  0.13684211,  1.50526316,  0.38709751],
           [ 3.        ,  0.13684211,  1.23157895,  0.31867645],
           [ 1.        ,  0.17105263,  1.71052632,  0.43806354],
           [ 2.        ,  0.17105263,  1.88157895,  0.4551688 ],
           [ 3.        ,  0.17105263,  1.53947368,  0.36964249],
           [ 1.        ,  0.20526316,  2.05263158,  0.49123255],
           [ 2.        ,  0.20526316,  2.25789474,  0.51175886],
           [ 3.        ,  0.20526316,  1.84736842,  0.40912729],
           [ 1.        ,  0.23947368,  2.39473684,  0.53292033],
           [ 2.        ,  0.23947368,  2.63421053,  0.55686769],
           [ 3.        ,  0.23947368,  2.15526316,  0.43713085],
           [ 1.        ,  0.27368421,  2.73684211,  0.56312687],
           [ 2.        ,  0.27368421,  3.01052632,  0.59049529],
           [ 3.        ,  0.27368421,  2.46315789,  0.45365319],
           [ 1.        ,  0.30789474,  3.07894737,  0.58185218],
           [ 2.        ,  0.30789474,  3.38684211,  0.61264166],
           [ 3.        ,  0.30789474,  2.77105263,  0.45869429],
           [ 1.        ,  0.34210526,  3.42105263,  0.58909626],
           [ 2.        ,  0.34210526,  3.76315789,  0.62330679],
           [ 3.        ,  0.34210526,  3.07894737,  0.45225416],
           [ 1.        ,  0.37631579,  3.76315789,  0.58485911],
           [ 2.        ,  0.37631579,  4.13947368,  0.62249069],
           [ 3.        ,  0.37631579,  3.38684211,  0.43433279],
           [ 1.        ,  0.41052632,  4.10526316,  0.56914072],
           [ 2.        ,  0.41052632,  4.51578947,  0.61019335],
           [ 3.        ,  0.41052632,  3.69473684,  0.40493019],
           [ 1.        ,  0.44473684,  4.44736842,  0.5419411 ],
           [ 2.        ,  0.44473684,  4.89210526,  0.58641479],
           [ 3.        ,  0.44473684,  4.00263158,  0.36404636],
           [ 1.        ,  0.47894737,  4.78947368,  0.50326025],
           [ 2.        ,  0.47894737,  5.26842105,  0.55115499],
           [ 3.        ,  0.47894737,  4.31052632,  0.3116813 ],
           [ 1.        ,  0.51315789,  5.13157895,  0.45309816],
           [ 2.        ,  0.51315789,  5.64473684,  0.50441395],
           [ 3.        ,  0.51315789,  4.61842105,  0.24783501],
           [ 1.        ,  0.54736842,  5.47368421,  0.39145485],
           [ 2.        ,  0.54736842,  6.02105263,  0.44619169],
           [ 3.        ,  0.54736842,  4.92631579,  0.17250748],
           [ 1.        ,  0.58157895,  5.81578947,  0.3183303 ],
           [ 2.        ,  0.58157895,  6.39736842,  0.37648819],
           [ 3.        ,  0.58157895,  5.23421053,  0.08569872],
           [ 1.        ,  0.61578947,  6.15789474,  0.23372452],
           [ 2.        ,  0.61578947,  6.77368421,  0.29530346],
           [ 3.        ,  0.61578947,  5.54210526, -0.01259127],
           [ 1.        ,  0.65      ,  6.5       ,  0.1376375 ],
           [ 2.        ,  0.65      ,  7.15      ,  0.2026375 ],
           [ 3.        ,  0.65      ,  5.85      , -0.1223625 ]])

** a.[0,5]Dada uma matriz com os valores da tabela, monte uma matriz que contenha as coordenadas X do saltador 2 em uma coluna e as coordenadas Y do mesmo saltador em outra coluna. Além disso, monte um vetor com os instantes correspondentes a cada coordenada.**

```python
P = np.zeros((int(np.size(r,0)/3),2))
time = np.zeros((int(np.size(r,0)/3),1))
k = 0
for i in range(np.size(r,0)):
    if r[i,0] == 3:
        P[k,:] = np.array([r[i,2],r[i,3]])
        time[k] = r[i,1]
        k += 1
print('P = ' + str(P))
print('t = ' + str(time))
```

    P = [[ 0.          0.        ]
     [ 0.30789474  0.09689096]
     [ 0.61578947  0.18230069]
     [ 0.92368421  0.25622919]
     [ 1.23157895  0.31867645]
     [ 1.53947368  0.36964249]
     [ 1.84736842  0.40912729]
     [ 2.15526316  0.43713085]
     [ 2.46315789  0.45365319]
     [ 2.77105263  0.45869429]
     [ 3.07894737  0.45225416]
     [ 3.38684211  0.43433279]
     [ 3.69473684  0.40493019]
     [ 4.00263158  0.36404636]
     [ 4.31052632  0.3116813 ]
     [ 4.61842105  0.24783501]
     [ 4.92631579  0.17250748]
     [ 5.23421053  0.08569872]
     [ 5.54210526 -0.01259127]
     [ 5.85       -0.1223625 ]]
    t = [[ 0.        ]
     [ 0.03421053]
     [ 0.06842105]
     [ 0.10263158]
     [ 0.13684211]
     [ 0.17105263]
     [ 0.20526316]
     [ 0.23947368]
     [ 0.27368421]
     [ 0.30789474]
     [ 0.34210526]
     [ 0.37631579]
     [ 0.41052632]
     [ 0.44473684]
     [ 0.47894737]
     [ 0.51315789]
     [ 0.54736842]
     [ 0.58157895]
     [ 0.61578947]
     [ 0.65      ]]

** b. [0,5]Dada a matriz com as coordenadas X e Y, e o vetor de tempo obtidos no item anterior, obtenha o vetor velocidade do saltador 2 em cada instante. **

```python
deltaT = time[1] - time[0]
v = np.diff(P,1,0)/deltaT
print('v=' + str(v))
```

    v=[[ 9.          2.83219737]
     [ 9.          2.49659211]
     [ 9.          2.16098684]
     [ 9.          1.82538158]
     [ 9.          1.48977632]
     [ 9.          1.15417105]
     [ 9.          0.81856579]
     [ 9.          0.48296053]
     [ 9.          0.14735526]
     [ 9.         -0.18825   ]
     [ 9.         -0.52385526]
     [ 9.         -0.85946053]
     [ 9.         -1.19506579]
     [ 9.         -1.53067105]
     [ 9.         -1.86627632]
     [ 9.         -2.20188158]
     [ 9.         -2.53748684]
     [ 9.         -2.87309211]
     [ 9.         -3.20869737]]

** c.[1] Tendo disponíveis as matrizes formadas nos itens anteriores, encontre uma base cuja origem seja a posição do saltador 2 e um dos seus vetores seja tangente à trajetória realizada pelo saltador.**

```python
vNorm = np.sqrt(v[:,0]**2+v[:,1]**2)
et = np.transpose([v[:,0]/vNorm, v[:,1]/vNorm])
print('e_t = ' + str(et))
```

    e_t = [[ 0.95388373  0.30017633]
     [ 0.96361194  0.26730511]
     [ 0.9723631   0.23347376]
     [ 0.98004556  0.19877301]
     [ 0.98657506  0.16330846]
     [ 0.99187715  0.12719954]
     [ 0.99588937  0.09057789]
     [ 0.99856328  0.05358518]
     [ 0.99986599  0.01637061]
     [ 0.99978132 -0.02091209]
     [ 0.99831031 -0.05810779]
     [ 0.99547125 -0.09506314]
     [ 0.99129895 -0.13162972]
     [ 0.9858437  -0.16766693]
     [ 0.9791695  -0.20304454]
     [ 0.97135215 -0.23764471]
     [ 0.9624769  -0.27136361]
     [ 0.95263616 -0.30411238]
     [ 0.94192702 -0.33581764]]

```python
det = (np.diff(et,1,0)/deltaT)
C = np.transpose([det[:,0]/vNorm[0:-1], det[:,1]/vNorm[0:-1]])
CNorm = np.sqrt(C[:,0]**2+C[:,1]**2)
en = np.transpose([C[:,0]/CNorm, C[:,1]/CNorm])
print('e_n = ' + str(en))
```

    e_n = [[ 0.28378241 -0.9588887 ]
     [ 0.25042766 -0.96813531]
     [ 0.21615752 -0.9763585 ]
     [ 0.18107017 -0.98347018]
     [ 0.14527819 -0.98939085]
     [ 0.10890719 -0.99405192]
     [ 0.07209393 -0.99739785]
     [ 0.03498396 -0.99938787]
     [-0.00227113 -0.99999742]
     [-0.03951679 -0.99921891]
     [-0.07659862 -0.99706201]
     [-0.11336562 -0.99355334]
     [-0.14967318 -0.98873552]
     [-0.18538577 -0.98266582]
     [-0.22037929 -0.97541425]
     [-0.25454284 -0.9670615 ]
     [-0.28778006 -0.95769653]
     [-0.32000981 -0.94741423]]


Text provided under a Creative Commons Attribution license, CC-BY.  All code is made available under the FSF-approved MIT license.  (c) Lorena A. Barba, 2013. Thanks: Gilbert Forsyth for help writing the notebooks. NSF for support via CAREER award #1149784.
[@LorenaABarba](https://twitter.com/LorenaABarba)

12 steps to Navier-Stokes
=====
***

We continue our journey to solve the Navier-Stokes equation with Step 4. But don't continue unless you have completed the previous steps! In fact, this next step will be a combination of the two previous ones. The wonders of *code reuse*!

Step 4: Burgers' Equation
----
***

You can read about Burgers' Equation on its [wikipedia page](http://en.wikipedia.org/wiki/Burgers'_equation).

Burgers' equation in one spatial dimension looks like this:

$$\frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} = \nu \frac{\partial ^2u}{\partial x^2}$$

As you can see, it is a combination of non-linear convection and diffusion. It is surprising how much you learn from this neat little equation!

We can discretize it using the methods we've already detailed in Steps [1](http://nbviewer.ipython.org/urls/github.com/barbagroup/CFDPython/blob/master/lessons/01_Step_1.ipynb) to [3](http://nbviewer.ipython.org/urls/github.com/barbagroup/CFDPython/blob/master/lessons/04_Step_3.ipynb).  Using forward difference for time, backward difference for space and our 2nd-order method for the second derivatives yields:

$$\frac{u_i^{n+1}-u_i^n}{\Delta t} + u_i^n \frac{u_i^n - u_{i-1}^n}{\Delta x} = \nu \frac{u_{i+1}^n - 2u_i^n + u_{i-1}^n}{\Delta x^2}$$

As before, once we have an initial condition, the only unknown is $u_i^{n+1}$. We will step in time as follows:

$$u_i^{n+1} = u_i^n - u_i^n \frac{\Delta t}{\Delta x} (u_i^n - u_{i-1}^n) + \nu \frac{\Delta t}{\Delta x^2}(u_{i+1}^n - 2u_i^n + u_{i-1}^n)$$

###Initial and Boundary Conditions

To examine some interesting properties of Burgers' equation, it is helpful to use different initial and boundary conditions than we've been using for previous steps.

Our initial condition for this problem is going to be:

\begin{eqnarray}
u &=& -\frac{2 \nu}{\phi} \frac{\partial \phi}{\partial x} + 4 \\\
\phi &=& \exp \bigg(\frac{-x^2}{4 \nu} \bigg) + \exp \bigg(\frac{-(x-2 \pi)^2}{4 \nu} \bigg)
\end{eqnarray}

This has an analytical solution, given by:

\begin{eqnarray}
u &=& -\frac{2 \nu}{\phi} \frac{\partial \phi}{\partial x} + 4 \\\
\phi &=& \exp \bigg(\frac{-(x-4t)^2}{4 \nu (t+1)} \bigg) + \exp \bigg(\frac{-(x-4t -2 \pi)^2}{4 \nu(t+1)} \bigg)
\end{eqnarray}

Our boundary condition will be:

$$u(0) = u(2\pi)$$

This is called a *periodic* boundary condition. Pay attention! This will cause you a bit of headache if you don't tread carefully.

###Saving Time with SymPy

The initial condition we're using for Burgers' Equation can be a bit of a pain to evaluate by hand.  The derivative $\frac{\partial \phi}{\partial x}$ isn't too terribly difficult, but it would be easy to drop a sign or forget a factor of $x$ somewhere, so we're going to use SymPy to help us out.

[SymPy](http://sympy.org/en/) is the symbolic math library for Python.  It has a lot of the same symbolic math functionality as Mathematica with the added benefit that we can easily translate its results back into our Python calculations (it is also free and open source).

Start by loading the SymPy library, together with our favorite library, NumPy.

```python
%matplotlib inline
```

```python
import numpy as np
import sympy
```

We're also going to tell SymPy that we want all of its output to be rendered using $\LaTeX$. This will make our Notebook beautiful!

```python
from sympy import init_printing
init_printing(use_latex=True)
```

Start by setting up symbolic variables for the three variables in our initial condition and then type out the full equation for $\phi$.  We should get a nicely rendered version of our $\phi$ equation.

```python
x, nu, t = sympy.symbols('x nu t')
phi = sympy.exp(-(x-4*t)**2/(4*nu*(t+1))) + sympy.exp(-(x-4*t-2*np.pi)**2/(4*nu*(t+1)))
phi
```

It's maybe a little small, but that looks right.  Now to evaluate our partial derivative $\frac{\partial \phi}{\partial x}$ is a trivial task.

```python
phiprime = phi.diff(x)
phiprime
```

If you want to see the unrendered version, just use the Python print command.

```python
print(phiprime)
```

    -(-8*t + 2*x)*exp(-(-4*t + x)**2/(4*nu*(t + 1)))/(4*nu*(t + 1)) - (-8*t + 2*x - 12.5663706143592)*exp(-(-4*t + x - 6.28318530717959)**2/(4*nu*(t + 1)))/(4*nu*(t + 1))

###Now what?

Now that we have the Pythonic version of our derivative, we can finish writing out the full initial condition equation and then translate it into a usable Python expression.  For this, we'll use the *lambdify* function, which takes a SymPy symbolic equation and turns it into a callable function.

```python
from sympy.utilities.lambdify import lambdify

u = -2*nu*(phiprime/phi)+4
print(u)
```

    -2*nu*(-(-8*t + 2*x)*exp(-(-4*t + x)**2/(4*nu*(t + 1)))/(4*nu*(t + 1)) - (-8*t + 2*x - 12.5663706143592)*exp(-(-4*t + x - 6.28318530717959)**2/(4*nu*(t + 1)))/(4*nu*(t + 1)))/(exp(-(-4*t + x - 6.28318530717959)**2/(4*nu*(t + 1))) + exp(-(-4*t + x)**2/(4*nu*(t + 1)))) + 4

###Lambdify

To lambdify this expression into a useable function, we tell lambdify which variables to request and the function we want to plug them in to.

```python
ufunc = lambdify((t, x, nu), u)
print(ufunc(1,4,3))
```

    3.4917066420644494

###Back to Burgers' Equation

Now that we have the initial conditions set up, we can proceed and finish setting up the problem.  We can generate the plot of the initial condition using our lambdify-ed function.

```python
import matplotlib.pyplot as plt

###variable declarations
nx = 101
nt = 100
dx = 2*np.pi/(nx-1)
nu = .07
dt = dx*nu

x = np.linspace(0, 2*np.pi, nx)
#u = np.empty(nx)
un = np.empty(nx)
t = 0

u = np.asarray([ufunc(t, x0, nu) for x0 in x])
u
```

    array([ 4.        ,  4.06283185,  4.12566371,  4.18849556,  4.25132741,
            4.31415927,  4.37699112,  4.43982297,  4.50265482,  4.56548668,
            4.62831853,  4.69115038,  4.75398224,  4.81681409,  4.87964594,
            4.9424778 ,  5.00530965,  5.0681415 ,  5.13097336,  5.19380521,
            5.25663706,  5.31946891,  5.38230077,  5.44513262,  5.50796447,
            5.57079633,  5.63362818,  5.69646003,  5.75929189,  5.82212374,
            5.88495559,  5.94778745,  6.0106193 ,  6.07345115,  6.136283  ,
            6.19911486,  6.26194671,  6.32477856,  6.38761042,  6.45044227,
            6.51327412,  6.57610598,  6.63893783,  6.70176967,  6.76460125,
            6.82742866,  6.89018589,  6.95176632,  6.99367964,  6.72527549,
            4.        ,  1.27472451,  1.00632036,  1.04823368,  1.10981411,
            1.17257134,  1.23539875,  1.29823033,  1.36106217,  1.42389402,
            1.48672588,  1.54955773,  1.61238958,  1.67522144,  1.73805329,
            1.80088514,  1.863717  ,  1.92654885,  1.9893807 ,  2.05221255,
            2.11504441,  2.17787626,  2.24070811,  2.30353997,  2.36637182,
            2.42920367,  2.49203553,  2.55486738,  2.61769923,  2.68053109,
            2.74336294,  2.80619479,  2.86902664,  2.9318585 ,  2.99469035,
            3.0575222 ,  3.12035406,  3.18318591,  3.24601776,  3.30884962,
            3.37168147,  3.43451332,  3.49734518,  3.56017703,  3.62300888,
            3.68584073,  3.74867259,  3.81150444,  3.87433629,  3.93716815,  4.        ])

```python
plt.figure(figsize=(11,7), dpi=100)
plt.plot(x,u, marker='o', lw=2)
plt.xlim([0,2*np.pi])
plt.ylim([0,10])
```

This is definitely not the hat function we've been dealing with until now. We call it a "saw-tooth function".  Let's proceed forward and see what happens.

### Periodic Boundary Conditions

One of the big differences between Step 4 and the previous lessons is the use of *periodic* boundary conditions.  If you experiment with Steps 1 and 2 and make the simulation run longer (by increasing `nt`) you will notice that the wave will keep moving to the right until it no longer even shows up in the plot.

With periodic boundary conditions, when a point gets to the right-hand side of the frame, it *wraps around* back to the front of the frame.

Recall the discretization that we worked out at the beginning of this notebook:

$$u_i^{n+1} = u_i^n - u_i^n \frac{\Delta t}{\Delta x} (u_i^n - u_{i-1}^n) + \nu \frac{\Delta t}{\Delta x^2}(u_{i+1}^n - 2u_i^n + u_{i-1}^n)$$

What does $u_{i+1}^n$ *mean* when $i$ is already at the end of the frame?

Think about this for a minute before proceeding.

```python
for n in range(nt):
    un = u.copy()
    for i in range(nx-1):
        u[i] = un[i] - un[i] * dt/dx *(un[i] - un[i-1]) + nu*dt/dx**2*\
                (un[i+1]-2*un[i]+un[i-1])
    u[-1] = un[-1] - un[-1] * dt/dx * (un[-1] - un[-2]) + nu*dt/dx**2*\
                (un[0]-2*un[-1]+un[-2])

u_analytical = np.asarray([ufunc(nt*dt, xi, nu) for xi in x])
```

```python
plt.figure(figsize=(11,7), dpi=100)
plt.plot(x,u, marker='o', lw=2, label='Computational')
plt.plot(x, u_analytical, label='Analytical')
plt.xlim([0,2*np.pi])
plt.ylim([0,10])
plt.legend()
```

***

What next?
----

The subsequent steps, from 5 to 12, will be in two dimensions. But it is easy to extend the 1D finite-difference formulas to the partial derivatives in 2D or 3D. Just apply the definition — a partial derivative with respect to $x$ is the variation in the $x$ direction *while keeping $y$ constant*.

Before moving on to [Step 5](http://nbviewer.ipython.org/urls/github.com/barbagroup/CFDPython/blob/master/lessons/07_Step_5.ipynb), make sure you have completed your own code for steps 1 through 4 and you have experimented with the parameters and thought about what is happening. Also, we recommend that you take a slight break to learn about [array operations with NumPy](http://nbviewer.ipython.org/urls/github.com/barbagroup/CFDPython/blob/master/lessons/07_Step_5.ipynb).

```python
from IPython.core.display import HTML
def css_styling():
    styles = open("../styles/custom.css", "r").read()
    return HTML(styles)
css_styling()
```

<link href='http://fonts.googleapis.com/css?family=Fenix' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:300,400' rel='stylesheet' type='text/css'>
<style>
    @font-face {
        font-family: "Computer Modern";
        src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');
    }
    div.cell{
        width:800px;
        margin-left:6% !important;
        margin-right:auto;
    }
    h1 {
        font-family: 'Alegreya Sans', sans-serif;
    }
    h2 {
        font-family: 'Fenix', serif;
    }
    h3{
		font-family: 'Fenix', serif;
        margin-top:12px;
        margin-bottom: 3px;
       }
	h4{
		font-family: 'Fenix', serif;
       }
    h5 {
        font-family: 'Alegreya Sans', sans-serif;
    }
    div.text_cell_render{
        font-family: 'Alegreya Sans',Computer Modern, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
        line-height: 135%;
        font-size: 120%;
        width:700px;
        margin-left:auto;
        margin-right:auto;
    }
    .CodeMirror{
            font-family: "Source Code Pro";
			font-size: 90%;
    }
/*    .prompt{
        display: None;
    }*/
    .text_cell_render h1 {
        font-weight: 200;
        font-size: 50pt;
		line-height: 100%;
        color:#CD2305;
        margin-bottom: 0.5em;
        margin-top: 0.5em;
        display: block;
    }
    .text_cell_render h5 {
        font-weight: 300;
        font-size: 16pt;
        color: #CD2305;
        font-style: italic;
        margin-bottom: .5em;
        margin-top: 0.5em;
        display: block;
    }

    .warning{
        color: rgb( 240, 20, 20 )
        }
</style>

```python

```


# Algorithms Exercise 2

## Imports

```python
%matplotlib inline
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
```

## Peak finding

Write a function `find_peaks` that finds and returns the indices of the local maxima in a sequence. Your function should:

* Properly handle local maxima at the endpoints of the input array.
* Return a Numpy array of integer indices.
* Handle any Python iterable as input.

```python
def find_peaks(a):
    """Find the indices of the local maxima in a sequence."""
    maxima = []
    for x in range(0, len(a)):
        if(x==len(a)-1 or a[x]>a[x+1]) and a[x]>a[x-1]:
            maxima.append(x)
    return np.array(maxima)

```

```python
a = [1,3,6,4,5,1]
find_peaks(a)
```

    array([2, 4])

```python
p1 = find_peaks([2,0,1,0,2,0,1])
assert np.allclose(p1, np.array([0,2,4,6]))
p2 = find_peaks(np.array([0,1,2,3]))
assert np.allclose(p2, np.array([3]))
p3 = find_peaks([3,2,1,0])
assert np.allclose(p3, np.array([0]))
```

Here is a string with the first 10000 digits of $\pi$ (after the decimal). Write code to perform the following:

* Convert that string to a Numpy array of integers.
* Find the indices of the local maxima in the digits of $\pi$.
* Use `np.diff` to find the distances between consequtive local maxima.
* Visualize that distribution using an appropriately customized histogram.

```python
from sympy import pi, N
pi_digits_str = str(N(pi, 10001))[2:]
```

```python

q = pi_digits_str
r = []
for p in q:
    r.append(int(p))
a = np.array(r)
m = find_peaks(a)
m = find_peaks(a)
s = np.diff(m)
plt.hist(s,bins=50)

```

```python
?np.split
```

```python
assert True # use this for grading the pi digits histogram
```


This notebook is part of https://github.com/AudioSceneDescriptionFormat/splines, see also https://splines.readthedocs.io/.

[back to overview](kochanek-bartels.ipynb)

# Uniform Kochanek--Bartels Splines

As a starting point,
remember the
[tangent vectors of uniform Catmull--Rom splines](catmull-rom-properties.ipynb#Tangent-Vectors)
(see also equation 3 of
the Kochanek/Bartels (1984) paper):

\begin{equation*}
\boldsymbol{\dot{x}}_i = \frac{\boldsymbol{x}_{i+1} - \boldsymbol{x}_{i-1}}{2},
\end{equation*}

which can be re-written as

\begin{equation*}
\boldsymbol{\dot{x}}_i = \frac{
(\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}.
\end{equation*}

## Parameters

*TCB splines* are all about inserting the parameters
$T$, $C$ and $B$ into this equation.

### Tension

The usage of $T$ is shown in equation 4 of
the Kochanek/Bartels (1984) paper:

\begin{equation*}
\boldsymbol{\dot{x}}_i = (1 - T_i) \frac{
(\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}
\end{equation*}

### Continuity

Up to now,
the goal was to have a continuous first derivative at the control points,
i.e. the incoming and outgoing tangent vectors were identical:

\begin{equation*}
\boldsymbol{\dot{x}}_i = \boldsymbol{\dot{x}}_i^{(-)} = \boldsymbol{\dot{x}}_i^{(+)}
\end{equation*}

This also happens to be the requirement for a spline to be $C^1$ continuous.

The "continuity" parameter $C$
allows us to break this continuity if we so desire,
leading to different incoming and outgoing tangent vectors
(see equations 5 and 6 in
the Kochanek/Bartels (1984) paper):

\begin{align*}
\boldsymbol{\dot{x}}_i^{(-)} &= \frac{
(1 - C_i) (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(1 + C_i) (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}\\
\boldsymbol{\dot{x}}_i^{(+)} &= \frac{
(1 + C_i) (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(1 - C_i) (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}
\end{align*}

### Bias

The usage of $B$ is shown in equation 7 of
the Kochanek/Bartels (1984) paper:

\begin{equation*}
\boldsymbol{\dot{x}}_i = \frac{
(1 + B_i) (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(1 - B_i) (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}
\end{equation*}

### All Three Combined

To get the tangent vectors of a TCB spline,
the three equations can be combined
(see equations 8 and 9 in
the Kochanek/Bartels (1984) paper):

\begin{align*}
\boldsymbol{\dot{x}}_i^{(+)} &= \frac{
(1 - T_i) (1 + C_i) (1 + B_i) (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(1 - T_i) (1 - C_i) (1 - B_i) (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}\\
\boldsymbol{\dot{x}}_i^{(-)} &= \frac{
(1 - T_i) (1 - C_i) (1 + B_i) (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(1 - T_i) (1 + C_i) (1 - B_i) (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}
\end{align*}

Note

There is an error in equation (6.11) of
Ian Millington's paper.
All subscripts of $x$ are wrong,
most likely copy-pasted from the preceding equation.

To simplify the results we will get later, we introduce the following shorthands
(as suggested in
Millington's paper):

\begin{align*}
a_i &= (1 - T_i) (1 + C_i) (1 + B_i),\\
b_i &= (1 - T_i) (1 - C_i) (1 - B_i),\\
c_i &= (1 - T_i) (1 - C_i) (1 + B_i),\\
d_i &= (1 - T_i) (1 + C_i) (1 - B_i),
\end{align*}

which lead to the simplified equations

\begin{align*}
\boldsymbol{\dot{x}}_i^{(+)} &= \frac{
a_i (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
b_i (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}\\
\boldsymbol{\dot{x}}_i^{(-)} &= \frac{
c_i (\boldsymbol{x}_i - \boldsymbol{x}_{i-i}) +
d_i (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{2}
\end{align*}

## Calculation

The tangent vectors are sufficient to implement
Kochanek--Bartels splines
(via [Hermite splines](hermite.ipynb)).
In the rest of this notebook we are deriving
the basis matrix and the basis polynomials
for comparison with other spline types.

```python
import sympy as sp
sp.init_printing()
```

As in previous notebooks,
we are using some SymPy helper classes from
[utility.py](utility.py):

```python
from utility import NamedExpression, NamedMatrix
```

And again,
we are looking at the fifth spline segment
from $\boldsymbol{x}_4$ to $\boldsymbol{x}_5$
(which can easily be generalized to arbitrary segments).

```python
x3, x4, x5, x6 = sp.symbols('xbm3:7')
```

```python
control_values_KB = sp.Matrix([x3, x4, x5, x6])
control_values_KB
```

We need three additional parameters per vertex:
$T$, $C$ and $B$.
In our calculation, however,
only the parameters belonging to
$\boldsymbol{x}_4$ and
$\boldsymbol{x}_5$ are relevant:

```python
T4, T5 = sp.symbols('T4 T5')
C4, C5 = sp.symbols('C4 C5')
B4, B5 = sp.symbols('B4 B5')
```

Using the shorthands mentioned above ...

```python
a4 = NamedExpression('a4', (1 - T4) * (1 + C4) * (1 + B4))
b4 = NamedExpression('b4', (1 - T4) * (1 - C4) * (1 - B4))
c5 = NamedExpression('c5', (1 - T5) * (1 - C5) * (1 + B5))
d5 = NamedExpression('d5', (1 - T5) * (1 + C5) * (1 - B5))
display(a4, b4, c5, d5)
```

... we can define the tangent vectors:

```python
xd4 = NamedExpression(
    'xdotbm4^(+)',
    sp.S.Half * (a4.name * (x4 - x3) + b4.name * (x5 - x4)))
xd5 = NamedExpression(
    'xdotbm5^(-)',
    sp.S.Half * (c5.name * (x5 - x4)  + d5.name * (x6 - x5)))
display(xd4, xd5)
```

```python
display(xd4.subs_symbols(a4, b4))
display(xd5.subs_symbols(c5, d5))
```

### Basis Matrix

We try to find a transformation
from the control values defined above
to *Hermite control values*:

```python
control_values_H = sp.Matrix([x4, x5, xd4.name, xd5.name])
M_KBtoH = NamedMatrix(r'{M_{\text{KB$,4\to$H}}}', 4, 4)
NamedMatrix(control_values_H, M_KBtoH.name * control_values_KB)
```

If we substitute the above definitions of
$\boldsymbol{\dot{x}}_4$ and $\boldsymbol{\dot{x}}_5$,
we can obtain the matrix elements:

```python
M_KBtoH.expr = sp.Matrix([
    [expr.coeff(cv) for cv in control_values_KB]
    for expr in control_values_H.subs([xd4.args, xd5.args]).expand()])
M_KBtoH.pull_out(sp.S.Half)
```

Once we have a way to get Hermite control values,
we can use the Hermite basis matrix from the
[notebook about uniform cubic Hermite splines](hermite-uniform.ipynb#Basis-Matrix) ...

```python
M_H = NamedMatrix(
    r'{M_\text{H}}',
    sp.Matrix([[ 2, -2,  1,  1],
               [-3,  3, -2, -1],
               [ 0,  0,  1,  0],
               [ 1,  0,  0,  0]]))
M_H
```

... to calculate the basis matrix for Kochanek--Bartels splines:

```python
M_KB = NamedMatrix(r'{M_{\text{KB},4}}', M_H.name * M_KBtoH.name)
M_KB
```

```python
M_KB = M_KB.subs_symbols(M_H, M_KBtoH).doit()
M_KB.pull_out(sp.S.Half)
```

And for completeness' sake, its inverse looks like this:

```python
M_KB.I
```

### Basis Polynomials

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

Multiplication with the
[monomial basis](polynomials.ipynb)
leads to the basis functions:

```python
b_KB = NamedMatrix(
    r'{b_{\text{KB},4}}',
    sp.Matrix([t**3, t**2, t, 1]).T * M_KB.expr)
b_KB.T.pull_out(sp.S.Half)
```

To be able to plot the basis functions,
let's substitute $a_4$, $b_4$, $c_5$ and $d_5$ back in (which isn't pretty):

```python
b_KB = b_KB.subs_symbols(a4, b4, c5, d5).simplify()
b_KB.T.pull_out(sp.S.Half)
```

Let's use a helper function from [helper.py](helper.py):

```python
from helper import plot_basis
```

```python
labels = sp.symbols('xbm_i-1 xbm_i xbm_i+1 xbm_i+2')
```

To be able to plot the basis functions,
we have to choose some concrete TCB values.

```python
plot_basis(
    *b_KB.expr.subs({T4: 0, T5: 0, C4: 0, C5: 1, B4: 0, B5: 0}),
    labels=labels)
```

```python
plot_basis(
    *b_KB.expr.subs({T4: 0, T5: 0, C4: 0, C5: -0.5, B4: 0, B5: 0}),
    labels=labels)
```

Setting all TCB values to zero leads to the
[basis polynomials of uniform Catmull--Rom splines](catmull-rom-uniform.ipynb#Basis-Polynomials).


# BT2101 K-Means and Hierarchical Clustering

## 1 Goal

In this notebook, we will explore clustering method, an unsupervised learning method, including:
* K-Means Clustering
* Hierarchical Clustering

```python
# -*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from __future__ import division
from math import sqrt
%matplotlib inline
```

### 1.1 K-Means Clusting

K-Means Clustering steps:

1. Determine the number of k (i.e., number of clusters)
2. Randomly select k centroids (i.e. centers of clusters)
3. Assign each data point to its closest centroid
4. Recalculate the centroids as the average of all data points in a cluster
5. Assign data points to their closest centroids
6. Repeat Step 4 and 5 until the observations are not reassigned or the maximum number of iterations is reached

http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html

```python
# Load libraries and dataset
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

# Load data
filepath = './iris.csv'
colnames = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'class']
feature_list =  ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
#iris = pd.read_csv(filepath, header=None, names=colnames, dtype={'sepal_length':np.float64, 'sepal_width':np.float64, 'petal_length':np.float64, 'petal_width':np.float64})
iris = pd.read_csv(filepath)
features = iris[feature_list]

# Standardize features: Z-index
# http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
scaler = StandardScaler()
features_std = scaler.fit_transform(features)
```

```python
# Fit k-means clustering model: Suppose 3 clusters
cluster_3 = KMeans(n_clusters=3, random_state=0, n_jobs=-1)
model_3 = cluster_3.fit(features) # Clustering without feature standardization
model_std_3 = cluster_3.fit(features_std) # Clustering after feature standardization
```

```python
# View cluster centers for each cluster:
model_std_3.cluster_centers_
```

    array([[ 1.13597027,  0.09659843,  0.996271  ,  1.01717187],
           [-1.01457897,  0.84230679, -1.30487835, -1.25512862],
           [-0.05021989, -0.88029181,  0.34753171,  0.28206327]])

```python
# View predicted clusters:
model_std_3.labels_
```

    array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
           1, 1, 1, 1, 1, 1, 0, 0, 0, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0,
           2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2,
           2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0,
           0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0,
           0, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2])

```python
# Compare with true classes:
# Remember usually you have no output labels in the dataset used for clustering
iris['species'].values
```

    array(['setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'setosa', 'setosa', 'setosa', 'setosa',
           'setosa', 'setosa', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'versicolor',
           'versicolor', 'versicolor', 'versicolor', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica', 'virginica', 'virginica',
           'virginica', 'virginica', 'virginica'], dtype=object)

```python
# Predict which cluster is a new observation in:
new_obs = [[0.5, 0.5, 0.5, 0.5]]
model_std_3.predict(new_obs)
```

    array([0])

### How to choose k?
#### Elbow method
* Gauge how the heterogeneity within clusters changes for various of k.
* The heterogeneity within clusters is expected to decreases with more clusters.
* The heterogeneity is measured by within-clusters/groups sum of squares (WSS)

\begin{align}
WSS(k) = \mathop{\sum}_{i=1}^{N}\mathop{\sum}_{j=0}^{p}(x_{ij}-centroid(x_{kj}))^2
\end{align}

Suppose N observations and p features.

k is cluster id ($=1,...,K$).

$x_{ij}$ is $i^{th}$ observation $j^{th}$ feature.

$centroid(x_{kj})$ is $k^{th}$ centroid of feature $j$.

```python
# Import scipy used for calculate distances
from scipy.spatial.distance import cdist
```

References: https://docs.scipy.org/doc/scipy/reference/spatial.distance.html

```python
# Cluster data with 1-10 clusters and get centroids

N_cluster = range(1, 11) # Assume number of 1 to 10 clusters
WSS_list = [] # A list of WSS for 1 to 10 clusters

for k in N_cluster:

    # Run k-means model
    cluster = KMeans(n_clusters=k, random_state=0, n_jobs=-1) # Run the model with k clusters: k from 1 to 10
    model = cluster.fit(features_std) # Fit the data with standardized features
    centroids = model.cluster_centers_ # Get centroids for each cluster id (=0,...,k-1)
    labels = model.labels_ # Get labels (which cluster id) for each observation

    # Calculate WSS(k)
    squared_distance = cdist(features_std, centroids, 'sqeuclidean') # Calculate squared distance between each observation and k centroids
    #min_distance_cluster_id = np.argmin(squared_distance, axis=1) # Find the index of minimum squared distance for each observation
    min_distance = np.min(squared_distance, axis=1) # Find minimum squared distance for each observation
    WSS = np.sum(min_distance)

    WSS_list.append(WSS)

```

```python
# Show WSS for 1-10 clusters
WSS_list
```

    [600.0,
     223.73200573676345,
     140.96581663074699,
     114.42970777082233,
     91.14860119459556,
     80.23060704506958,
     70.93964372756813,
     62.96758488325033,
     55.70641053075773,
     47.47398415550427]

```python
# Plot the elbow curve
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(N_cluster, WSS_list, 'g*-')
ax.plot(N_cluster[1], WSS_list[1], marker='o', markersize=12, markeredgewidth=2, markeredgecolor='r', markerfacecolor='None')
ax.plot(N_cluster[2], WSS_list[2], marker='o', markersize=12, markeredgewidth=2, markeredgecolor='b', markerfacecolor='None')
plt.grid(True)
plt.xlabel('Number of clusters')
plt.ylabel('Within-cluster Sum of Squares (WSS)')
plt.title('Elbow curve for K-means clustering')

plt.show()
```

#### If you choose k=3, you can use pairplot to visualize the differences of these 3 clusters in terms of their attributes

References: https://seaborn.pydata.org/generated/seaborn.pairplot.html

```python
import seaborn as sns
from copy import deepcopy

# Run k-means model
cluster = KMeans(n_clusters=3, random_state=0, n_jobs=-1)
y_pred = cluster.fit_predict(features_std)

data = deepcopy(iris[feature_list])
data['cluster'] = y_pred
sns.pairplot( data, hue='cluster', diag_kind='kde', markers=['o','s','D'])
```

The original codes can be found at http://scikit-learn.org/stable/auto_examples/cluster/plot_kmeans_assumptions.html.

### 1.2 Hierarchical Clustering

Hierarchical Clustering Steps:

1. Start with n clusters (each record is its own cluster)
2. Merge two closest records into one cluster
3. At each successive step, the two clusters closest to each other are merged

```python
# Load Libraries
from sklearn.cluster import AgglomerativeClustering
```

```python
# Create meanshift object
HC = AgglomerativeClustering(n_clusters=3)
HC_model = HC.fit(features_std)
```

```python
# Show clusters
HC_model.labels_
```

    array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
           1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1,
           1, 1, 1, 1, 1, 1, 0, 0, 0, 2, 0, 2, 0, 2, 0, 2, 2, 0, 2, 0, 2, 0,
           2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 0, 0, 2,
           2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=int64)

```python
# Plot dendrogram for hierarchical clustering
from scipy.cluster.hierarchy import dendrogram, linkage
avg_link = linkage(features_std[1:20, :], method='average') # Linkage criterion: average distance
dendrogram(avg_link).items
```

References: https://docs.scipy.org/doc/scipy/reference/cluster.hierarchy.html

More information about hierarchical clustering can be found at:

[1] http://scikit-learn.org/stable/modules/generated/sklearn.cluster.AgglomerativeClustering.html

[2] https://docs.scipy.org/doc/scipy/reference/cluster.hierarchy.html

## 2 Assignments on K-Means Clustering (5 points)

**The purpose of this assignment is to know how to use scikit-learn package to do K-Means clustering**

#### Overview

This case requires to develop a customer segmentation to define marketing strategy. The sample Dataset summarizes the usage behavior of about 9000 active credit card holders during the last 6 months. The file is at a customer level with 18 behavioral variables.The dataset can be downloaded here (https://www.kaggle.com/arjunbhasin2013/ccdata/home). It is released under [CC0: Public Domain](https://creativecommons.org/publicdomain/zero/1.0/).

```python
# Load libraries and dataset
from sklearn import datasets
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.cluster import KMeans

# Load data
feature_list = ['BALANCE', 'PURCHASES', 'CASH_ADVANCE', 'CREDIT_LIMIT', 'PAYMENTS', 'MINIMUM_PAYMENTS']
creditcard = pd.read_csv('./CC GENERAL.csv')[['CUST_ID']+feature_list]
creditcard.head(n=5)
```

```python
creditcard.dropna(inplace=True)
creditcard.describe()
```

#### Question 1. Standardize all the features using `min-max` method (2 points)

An example of standardizing features using `z-index` method:
```python
# Standardize features: z-index standardization
feature_list = ['BALANCE', 'PURCHASES', 'CASH_ADVANCE', 'CREDIT_LIMIT', 'PAYMENTS', 'MINIMUM_PAYMENTS']
scaler = StandardScaler()
features_std = scaler.fit_transform(creditcard[feature_list])
```

References: http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html

```python
# Standardize features: min-max standardization
# Initialize a min-max scaler object to do standardization
# Standardize features using min-max method

features = creditcard[feature_list]
scalerMinMax =  MinMaxScaler()
features_std_MinMax = scalerMinMax.fit_transform(features)
```

#### Question 2. Fit 5-means clustering model with min-max standardized features: k=5 (2 points)
Please:
* Set `n_clusters=5`
* Set `random_state=12345`
* Set `n_jobs=-1` (This may not be necessary)

References: http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html

```python
# Fit 5-means clustering model: Suppose 5 clusters k=5
# Create a KMeans clustering model
# Do clustering on min-max standardized features
cluster_5 = KMeans(n_clusters = 5, random_state = 12345, n_jobs = -1)
model_std_5 = cluster_5.fit(features_std_MinMax) # Clustering after feature standardization
```

#### Question 3. Predict which cluster does a new data sample belong to (1 point)

* Suppose new data sample `new_obs=[[40, 90, 100, 1000, 1500, 50]]`
* Use `predict` function
* References: http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#sklearn.cluster.KMeans.predict

```python
# Predict which cluster is this new observation in:
new_obs = [[40, 90, 100, 1000, 1500, 50]]
model_std_5.predict(new_obs)
```

## 3 References
[1] Raschka, S. (2015). Python machine learning. Packt Publishing Ltd.

[2] Chris Albon. (2018). Machine Learning with Python Cookbook. O'Reilly.