```python
import numpy as np
import pandas as pd
import linearsolve as ls
import matplotlib.pyplot as plt
plt.style.use('classic')
%matplotlib inline
```

# Class 17: Exogenous Shocks in the New-Keynesian Model

In this notebook, we will compute impulse responses to exogenous shocks in the new-Keynesian model and interpret the results.

## The New-Keynesian Model

The most basic version of the New-Keynesian Model can be expressed as:

\begin{align}
y_t & = E_t y_{t+1}  - \left( r_{t} - \bar{r}\right) + g_t\\
i_{t} & = r_{t} + E_t \pi_{t+1}\\
i_{t} & = \bar{r} + \pi^T + \phi_{\pi}\big(\pi_t - \pi^T\big) + \phi_{y}\big(y_t - \bar{y}\big) + v_t\\
\pi_t -\pi^T & = \beta \left( E_t\pi_{t+1} - \pi^T\right)  + \kappa (y_t -\bar{y})+ u_t,
\end{align}

where: $y_t$ is (log) output, $r_t$ is the real interest rate, $i_t$ is the nominal interest rate, $\pi_t$ is the rate of inflation between periods $t-1$ and $t$, $\bar{r}$ is the long-run average real interest rate or the *natural rate of interest*, $\beta$ is the household's subjective discount factor, and $\pi^T$ is the central bank's inflation target. The coeffieints $\phi_{\pi}$ and $\phi_{y}$ reflect the degree of intensity to which the central bank *endogenously* adjusts the nominal interest rate in response to movements in inflation and output.

The variables $g_t$, $u_t$, and $v_t$ represent exogenous shocks to aggregate demand, inflation, and monetary policy. They follow AR(1) processes:

\begin{align}
g_{t+1} & = \rho_g g_{t} + \epsilon^g_{t+1}\\
u_{t+1} & = \rho_u u_{t} + \epsilon^u_{t+1}\\
v_{t+1} & = \rho_v v_{t} + \epsilon^v_{t+1}.
\end{align}

The goal is to compute impulse responses in the model to all three exogenous shocks. We will use the following parameterization:

| $\bar{y}$ | $\beta$ | $\bar{r}$    | $\kappa$ | $\pi^T$ | $\phi_{\pi}$ | $\phi_y$ | $\rho_g$ | $\rho_u$ | $\rho_v$ |
|-----------|---------|--------------|----------|---------|--------------|----------|----------|----------|----------|
| 0         | 0.995   | $-\log\beta$ | 0.1      | 0.02/4  | 1.5          | 0.5/4    | 0.5      | 0.5      | 0.5      |

## Solution

```python
# Create a variable called 'parameters' that stores the model parameter values in a Pandas Series. CELL PROVIDED
parameters = pd.Series()
parameters['y_bar'] = 0
parameters['beta'] = 0.995
parameters['r_bar'] = -np.log(parameters.beta)
parameters['kappa'] = 0.1
parameters['pi_T'] = 0.02/4
parameters['phi_pi'] = 1.5
parameters['phi_y'] = 0.5/4
parameters['rho_g'] = 0.5
parameters['rho_u'] = 0.5
parameters['rho_v'] = 0.5

# Create variable called 'var_names' that stores the variable names in a list with state variables ordered first
var_names = ['g','u','v','y','pi','i','r']

# Create variable called 'shock_names' that stores an exogenous shock name for each state variable.
shock_names = ['e_g','e_u','e_v']

# Define a function that evaluates the equilibrium conditions of the model solved for zero.
def equilibrium_equations(variables_forward,variables_current,parameters):

    # Parameters
    p = parameters

    # Current variables
    cur = variables_current

    # Forward variables
    fwd = variables_forward

    # IS equation
    is_equation = fwd.y - (cur.r -p.r_bar) + cur.g - cur.y

    # Fisher_equation
    fisher_equation = cur.r + fwd.pi - cur.i

    # Monetary policy
    monetary_policy = p.r_bar + p.pi_T + p.phi_pi*(cur.pi - p.pi_T) + p.phi_y*cur.y + cur.v - cur.i

    # Phillips curve
    phillips_curve = p.beta*(fwd.pi- p.pi_T) + p.kappa*cur.y + cur.u - (cur.pi-p.pi_T)

    # Demand process
    demand_process = p.rho_g*cur.g - fwd.g

    # Monetary policy process
    monetary_policy_process = p.rho_v*cur.v - fwd.v

    # Inflation process
    inflation_process = p.rho_u*cur.u - fwd.u

    # Stack equilibrium conditions into a numpy array
    return np.array([
            is_equation,
            fisher_equation,
            monetary_policy,
            phillips_curve,
            demand_process,
            monetary_policy_process,
            inflation_process
        ])

# Initialize the model into a variable named 'nk_model'
nk_model = ls.model(equations = equilibrium_equations,
                 n_states=3,
                 var_names=var_names,
                 shock_names=shock_names,
                 parameters = parameters)

# Compute the steady state numerically using .compute_ss() method of nk_model
guess = [0,0,0,0,0.01,0.01,0.01]
nk_model.compute_ss(guess)
```

```python
# Find the log-linear approximation around the non-stochastic steady state and solve using .approximate_and_solve() method of nk_model
# set argumement 'log_linear' to False
nk_model.approximate_and_solve(log_linear=False)

# Print the solution to the model
print(nk_model.solved())
```

    Solution to the linear system:

    g[t+1] = 0.5·g[t]+e_g[t+1]

    u[t+1] = 0.5·u[t]+e_u[t+1]

    v[t+1] = 0.5·v[t]+e_v[t+1]

      y[t] = 1.2136·g[t]-2.4151·u[t]-1.2136·v[t]

     pi[t] = 0.2415·g[t]+1.5094·u[t]-0.2415·v[t]

      i[t] = 0.514·g[t]+1.9623·u[t]+0.486·v[t]

      r[t] = 0.3932·g[t]+1.2075·u[t]+0.6068·v[t]

**Questions**

1. Will a positive shock to $g_t$ increase or decrease $y_t$?
2. Will a positive shock to $g_t$ increase or decrease $\pi_t$?
3. Will a positive shock to $u_t$ increase or decrease $y_t$?
4. Will a positive shock to $u_t$ increase or decrease $\pi_t$?

**Answers**

1. The coefficient on $g_t$ in the solution for $y_t$ is *positive* so the shock will *increase* $y_t$ <!-- answer -->
2. The coefficient on $g_t$ in the solution for $\pi_t$ is *positive* so the shock will *increase* $\pi_t$ <!-- answer -->
1. The coefficient on $u_t$ in the solution for $y_t$ is *negative* so the shock will *decrease* $y_t$ <!-- answer -->
4. The coefficient on $u_t$ in the solution for $\pi_t$ is *positive* so the shock will *increase* $\pi_t$ <!-- answer -->

## Impulse Responses

Compute a 21 period impulse response of the model's variables to a:

* 0.01 unit shock to $g_t$
* 0.01/4 unit shock to $u_t$
* 0.01/4 unit shock to $v_t$

```python
# Compute impulse responses
nk_model.impulse(T=21,t0=5,shocks=[0.01,0.01/4,0.01/4])
```

### Demand Shock

Plot the computed impulses responses of the nominal interest rate, the real interest rate, output, and inflation to the 0.01 unit demand shock. Express inflation and interest rates in *annualized* (e.g., multiplied by 4) terms.

```python
# Create figure. PROVIDED
fig = plt.figure(figsize=(12,8))

# Create upper-left axis. PROVIDED
ax1 = fig.add_subplot(2,2,1)
# Create upper-right axis. PROVIDED
ax2 = fig.add_subplot(2,2,2)
# Create lower-left axis. PROVIDED
ax3 = fig.add_subplot(2,2,3)
# Create lower-right axis. PROVIDED
ax4 = fig.add_subplot(2,2,4)

# Set axis 1 ylabel
ax1.set_ylabel('% dev from steady state')
# Set axis 2 ylabel
ax2.set_ylabel('% dev from steady state')
# Set axis 3 ylabel
ax3.set_ylabel('% dev from steady state')
# Set axis 4 ylabel
ax4.set_ylabel('% dev from steady state')

# Set axis 1 limits
ax1.set_ylim([-0.5,2.5])
# Set axis 2 limits
ax2.set_ylim([-0.5,2.5])
# Set axis 3 limits
ax3.set_ylim([-0.5,1.5])
# Set axis 4 limits
ax4.set_ylim([-0.5,1.5])

# Plot the nominal interest rate, real interest rate, output, and inflation
(nk_model.irs['e_g']['i']*400).plot(ax=ax1,lw=4,alpha=0.75,title='Nominal Interest',grid=True)
(nk_model.irs['e_g']['r']*400).plot(ax=ax2,lw=4,alpha=0.75,title='Real Interest',grid=True)
(nk_model.irs['e_g']['y']*100).plot(ax=ax3,lw=4,alpha=0.75,title='Output',grid=True)
(nk_model.irs['e_g']['pi']*400).plot(ax=ax4,lw=4,alpha=0.75,title='Inflation',grid=True)
```

### Inflation Shock

Plot the computed impulses responses of the nominal interest rate, the real interest rate, output, and inflation to the 0.01/4 unit inflation shock. Express inflation and interest rates in *annualized* (e.g., multiplied by 4) terms.

```python
# Create figure. PROVIDED
fig = plt.figure(figsize=(12,8))

# Create upper-left axis. PROVIDED
ax1 = fig.add_subplot(2,2,1)
# Create upper-right axis. PROVIDED
ax2 = fig.add_subplot(2,2,2)
# Create lower-left axis. PROVIDED
ax3 = fig.add_subplot(2,2,3)
# Create lower-right axis. PROVIDED
ax4 = fig.add_subplot(2,2,4)

# Set axis 1 ylabel
ax1.set_ylabel('% dev from steady state')
# Set axis 2 ylabel
ax2.set_ylabel('% dev from steady state')
# Set axis 3 ylabel
ax3.set_ylabel('% dev from steady state')
# Set axis 4 ylabel
ax4.set_ylabel('% dev from steady state')

# Set axis 1 limits
ax1.set_ylim([-.5,2.5])
# Set axis 2 limits
ax2.set_ylim([-0.5,2.5])
# Set axis 3 limits
ax3.set_ylim([-1,0.5])
# Set axis 4 limits
ax4.set_ylim([-0.5,2])

# Plot the nominal interest rate, real interest rate, output, and inflation
(nk_model.irs['e_u']['i']*400).plot(ax=ax1,lw=4,alpha=0.75,title='Nominal Interest',grid=True)
(nk_model.irs['e_u']['r']*400).plot(ax=ax2,lw=4,alpha=0.75,title='Real Interest',grid=True)
(nk_model.irs['e_u']['y']*100).plot(ax=ax3,lw=4,alpha=0.75,title='Output',grid=True)
(nk_model.irs['e_u']['pi']*400).plot(ax=ax4,lw=4,alpha=0.75,title='Inflation',grid=True)
```

### Monetary Policy Shock

Plot the computed impulses responses of the nominal interest rate, the real interest rate, output, and inflation to the 0.01/4 unit monetary policy shock. Express inflation and interest rates in *annualized* (e.g., multiplied by 4) terms.

```python
# Create figure. CELL PROVIDED
fig = plt.figure(figsize=(12,8))

# Create upper-left axis. PROVIDED
ax1 = fig.add_subplot(2,2,1)
# Create upper-right axis. PROVIDED
ax2 = fig.add_subplot(2,2,2)
# Create lower-left axis. PROVIDED
ax3 = fig.add_subplot(2,2,3)
# Create lower-right axis. PROVIDED
ax4 = fig.add_subplot(2,2,4)

# Set axis 1 ylabel
ax1.set_ylabel('% dev from steady state')
# Set axis 2 ylabel
ax2.set_ylabel('% dev from steady state')
# Set axis 3 ylabel
ax3.set_ylabel('% dev from steady state')
# Set axis 4 ylabel
ax4.set_ylabel('% dev from steady state')

# Set axis 1 limits
ax1.set_ylim([-0.2,0.8])
# Set axis 2 limits
ax2.set_ylim([-0.2,0.8])
# Set axis 3 limits
ax3.set_ylim([-0.4,0.1])
# Set axis 4 limits
ax4.set_ylim([-0.4,0.1])

# Plot the nominal interest rate, real interest rate, output, and inflation
(nk_model.irs['e_v']['i']*400).plot(ax=ax1,lw=4,alpha=0.75,title='Nominal Interest',grid=True)
(nk_model.irs['e_v']['r']*400).plot(ax=ax2,lw=4,alpha=0.75,title='Real Interest',grid=True)
(nk_model.irs['e_v']['y']*100).plot(ax=ax3,lw=4,alpha=0.75,title='Output',grid=True)
(nk_model.irs['e_v']['pi']*400).plot(ax=ax4,lw=4,alpha=0.75,title='Inflation',grid=True)
```


# 1. Вязкость

## 1.1 Вязкость дегазированной нефти, корреляция Стендинга
$$
\mu_o = (0,32 + \frac{1,8 *10^7}{\gamma_{API}^{4,53}})(\frac{360}{1,8T-260})^a
$$
где:

$\gamma_{API} = \frac{141,5}{\gamma_o} - 131,5$ - плотность нефти в $^\circ API$

$a = 10^{0,43 + 8,33 / \gamma_{API}} $

T - температура в $^\circ K$

Графическая корреляция была установлена Билом. Математическую интерполяции корреляции Била дал Стендинг.

$\textbf{Источник}$ : $\underline{Дж.П. Брилл, Х.Мукеражди "Многофазный~поток~в~скважинах"}$

```python
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from sympy import *
from math import *
```

```python
# Функция для построения графиков
def prep_plot(func,t_set,gamma_o,plot_title,plot_xlab,plot_ylab):
    for t in t_set:
        visc__cP = get_dead_oil_viscosity__Standing(gamma_o_set, t)
        plt.plot([go*1000 for go in gamma_o_set], visc__cP, label='t=%1.0f$^{\circ}\mathrm{C}$'%t)
    plt.legend()
    plt.title(plot_title)
    plt.xlabel(plot_xlab)
    plt.ylabel(plot_ylab)
```

```python
# Функция рассчитывающая вязкость по Стендингу
def get_dead_oil_viscosity__Standing(gamma_o, t__C):
    gamma_API = 141.5 / gamma_o - 131.5
    viscosity__cP = (0.32 + 1.8 * (10 ** 7) / (gamma_API ** 4.53)) * \
    (360 / (1.8 * (t__C + 273) - 260)) ** (10 ** (0.43 + 8.33 / (gamma_API)))
    return viscosity__cP
```

```python
# Код для построения графиков
plt.figure(figsize=(15,6))
t_set = np.array(range(40, 120, 20))
gamma_o_set = np.arange( 0.78, 1, 0.002)

# Рисуем первый график
plt.subplot(121)
prep_plot(func=get_dead_oil_viscosity__Standing, t_set=t_set, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')

# Рисуем второй график(для наглядности ограничиваем плотность дегазированной нефти)
plt.subplot(122)
gamma_o_set = np.arange( 0.775, 0.9, 0.002)
prep_plot(func=get_dead_oil_viscosity__Standing, t_set=t_set, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')
plt.show()
```

## 1.2 Вязкость дегазированной нефти, корреляция Беггза и Робинсона
$$
\mu_o = 10 ^ x -1
$$

$$
x = \frac{10 ^{3,0324 - 0,02023 \gamma_{API}}}{(1,8T + 32)^{1,163}}
$$

где:

$\gamma_{API} = \frac{141,5}{\gamma_o} - 131,5$ - плотность нефти в $^\circ API$

T - температура в $^\circ C$

$\textbf{Источник}$ : $\underline{Дж.П. Брилл, Х.Мукеражди "Многофазный~поток~в~скважинах"}$

```python
# функция расчитывающая вязкость
def get_dead_oil_viscosity__Beggs(gamma_o, t__C):
    gamma_API = 141.5 / gamma_o - 131.5
    x = 10 ** (3.0324 - 0.02023 * gamma_API) / \
    (1.8 * (t__C) + 32) ** 1.163
    viscosity__cP = 10 ** x - 1
    return viscosity__cP
```

```python
# Входные параметры
t_set = np.array(range(40, 120, 20))
gamma_o_set = np.arange( 0.78, 1, 0.002)

# Рисуем график
plt.figure(figsize=(15,6))

plt.subplot(121)
prep_plot(func=get_dead_oil_viscosity__Beggs, t_set=t_set, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')

# Для наглядности ограничиваем плотность нефти
gamma_o_set = np.arange( 0.775, 0.9, 0.002)
plt.subplot(122)
prep_plot(func=get_dead_oil_viscosity__Beggs, t_set=t_set, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')
```

## 1.3 Вязкость дегазированной нефти, корреляция Гласe
$$
\mu_o = 3.141 * 10 ^{10} * (1,8T +32)^{-3,444} (\lg\gamma_{API})^{a}
$$

где:

$
a = 10,313\lg(1,8T+32)-36,447
$

$\gamma_{API} = \frac{141,5}{\gamma_o} - 131,5$ - плотность нефти в $^\circ API$

T - температура в $^\circ C$

 Методика справедлива в интервале:
\begin{align*}
10 < T < 149; \\
20,1 < \gamma_{API} < 48,1
\end{align*}

$\textbf{Источник}$ : $\underline{Дж.П. Брилл, Х.Мукеражди "Многофазный~поток~в~скважинах"}$

```python
def get_dead_oil_viscosity__Glaso(gamma_o, t__C):
    gamma_API = 141.5 / gamma_o - 131.5
    a = 10.313 * log10(1.8 * t__C + 32) - 36.447
    viscosity__cP = (3.141 * 10**10)* (1.8 * t__C + 32)**(-3.444) * (log10(gamma_API))**a
    return viscosity__cP
```

```python
# Входные параметры
t_set1 = np.array(range(10, 90, 20))
gamma_o_set = np.arange(0.7879,0.9334, 0.002)
t_set2 = np.array([90, 110, 130, 149])

# Рисуем графики
plt.figure(figsize=(15,6))

plt.subplot(121)
prep_plot(func=get_dead_oil_viscosity__Glaso, t_set=t_set1, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')

plt.subplot(122)
prep_plot(func=get_dead_oil_viscosity__Glaso, t_set=t_set2, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')
```

## 1.4 Вязкость дегазированной нефти, корреляция Картоатмодью и Шмидта
$$
\mu_o = (16 * 10^8)(1,8T +32)^{-2,8177} (\lg\gamma_{API})^{a}
$$

где:

$
a = 5,7526\lg(1,8T+32)-26,9718
$

$\gamma_{API} = \frac{141,5}{\gamma_o} - 131,5$ - плотность нефти в $^\circ API$

T - температура в $^\circ C$

Данная методика обьединяет корреляции 1.1 - 1.3

$\textbf{Источник}$ : $\underline{Дж.П. Брилл, Х.Мукеражди "Многофазный~поток~в~скважинах"}$

```python
def get_dead_oil_viscosity__Schmidt(gamma_o, t__C):
    gamma_API = 141.5 / gamma_o - 131.5
    a = 5.7526 * log10(1.8 * t__C + 32) - 26.9718
    viscosity__cP = (16 * 10**8)* (1.8 * t__C + 32)**(-2.8177) * (log10(gamma_API))**a
    return viscosity__cP
```

```python
# Входные параметры
t_set1 = np.array(range(10, 90, 20))
gamma_o_set = np.arange(0.7879,0.9334, 0.002)
t_set2 = np.array([90, 110, 130, 149])

# Рисуем графики
plt.figure(figsize=(15,6))

plt.subplot(121)
prep_plot(func=get_dead_oil_viscosity__Schmidt, t_set=t_set1, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')

plt.subplot(122)
prep_plot(func=get_dead_oil_viscosity__Schmidt, t_set=t_set2, gamma_o=gamma_o_set,
          plot_title='Давление насыщения от газосодержания',
          plot_xlab='Плотность, кг/м3',plot_ylab='Вязкость, сПз')
```

```python

```


# Reorganizing the system matrices from  (Lin, Parker, 1999)

The objective is to describe how the system matrices presented in [1] were reorganized, in order to become more suitable to model multi-stage gearboxes with planetary and parallel stages. The reasons for reorganizing them are:
* the ring gear is assumed to be fixed, so its degrees-of-freedom should be removed from the system matrices
* these matrices will be combined for different stages in a finite-element manner, that would be easier if the state vector for each element were ordered from input to output elements, which in our case are the planet carrier and the sun gear, with the  $N$ planet gears and ring gear in between.

##### References:
[1] Lin, J., & Parker, R. G. (1999). Analytical characterization of the unique properties of planetary gear free vibration. Journal of Vibration and Acoustics, Transactions of the ASME, 121(3), 316–321. http://doi.org/10.1115/1.2893982 \
[2] Cooley, C. G., & Parker, R. G. (2012). Vibration Properties of High-Speed Planetary Gears With Gyroscopic Effects. Journal of Vibration and Acoustics, 134(6). http://doi.org/10.1115/1.4006646

```python
from sympy import *
init_printing()

def symb(x, y, z = ''):
    return symbols('{0}_{1}{2}'.format(x, y, z), type = float)
```

## Displacement vector

The rotational coordinates are represented indirectly in the following way:
$$u_j = r_j \theta_j,$$
where $r_j$ is the base radius of sun, ring, and planets gears or the center distance for the carrier

```python
N = 3 # number of planet gears
N_DOF = 3*(N + 3) # number of degrees of freedom
#            carrier,  ring,  sun + planets
idx_list = ['c'     , 'r'  , 's'] + list(range(1, N + 1))

q = Matrix([symb('q', v) for v in idx_list])
q_ = lambda idx: Matrix(symbols('x_{0} y_{0} u_{0}'.format(idx)))
q, q_('j')
```

$\displaystyle \left( \left[\begin{matrix}q_{c}\\q_{r}\\q_{s}\\q_{1}\\q_{2}\\q_{3}\end{matrix}\right], \  \left[\begin{matrix}x_{j}\\y_{j}\\u_{j}\end{matrix}\right]\right)$

## Inertia matrix

First one needs to write the system matrices, defined in the Appendix of [1]. The inertia matrix can be seen below:
\begin{align}
    \mathbf{M} = \mathrm{diag}(\mathbf{M}_c, \mathbf{M}_r, \mathbf{M}_s, \mathbf{M}_1, \ldots, \mathbf{M}_N) \\
    \mathbf{M}_j = \mathrm{diag}(m_j, m_j, I_j/r_j^2), \quad j = c, r, s, 1, \ldots, N
\end{align}
where:
* $N$ is the number of planets
* $m_j$ is the mass of an element (sun, planet, ring, carrier)
* $I_j$ is the mass moment of inertia of an element
* $r_j$ is the base radius for sun, ring, and planet gears or the center distance for the carrier

```python
M  = diag(*[symb('M', v) for v in idx_list])
M_ = lambda j: diag(symb('m', j), symb('m', j), symb('I', j)/symb('r', j)**2)

M, M_('j')
```

$\displaystyle \left( \left[\begin{matrix}M_{c} & 0 & 0 & 0 & 0 & 0\\0 & M_{r} & 0 & 0 & 0 & 0\\0 & 0 & M_{s} & 0 & 0 & 0\\0 & 0 & 0 & M_{1} & 0 & 0\\0 & 0 & 0 & 0 & M_{2} & 0\\0 & 0 & 0 & 0 & 0 & M_{3}\end{matrix}\right], \  \left[\begin{matrix}m_{j} & 0 & 0\\0 & m_{j} & 0\\0 & 0 & \frac{I_{j}}{r_{j}^{2}}\end{matrix}\right]\right)$

## Gyroscopic matrix:

```python
G  = diag(*[symb('G', v) for v in idx_list])
G_ = lambda j: Matrix(3, 3, [0, -2*symb('m', j), 0, 2*symb('m', j), 0, 0, 0, 0, 0])

G, G_('j')
```

$\displaystyle \left( \left[\begin{matrix}G_{c} & 0 & 0 & 0 & 0 & 0\\0 & G_{r} & 0 & 0 & 0 & 0\\0 & 0 & G_{s} & 0 & 0 & 0\\0 & 0 & 0 & G_{1} & 0 & 0\\0 & 0 & 0 & 0 & G_{2} & 0\\0 & 0 & 0 & 0 & 0 & G_{3}\end{matrix}\right], \  \left[\begin{matrix}0 & - 2 m_{j} & 0\\2 m_{j} & 0 & 0\\0 & 0 & 0\end{matrix}\right]\right)$

## Stiffness matrix

### Centripetal component

```python
K_Omega  = diag(*[symb('K', 'Omega', v) for v in idx_list])
K_Omega_ = lambda j: diag(symb('m', j), symb('m', j), 0)

K_Omega, K_Omega_('j')
```

$\displaystyle \left( \left[\begin{matrix}K_{Omegac} & 0 & 0 & 0 & 0 & 0\\0 & K_{Omegar} & 0 & 0 & 0 & 0\\0 & 0 & K_{Omegas} & 0 & 0 & 0\\0 & 0 & 0 & K_{Omega1} & 0 & 0\\0 & 0 & 0 & 0 & K_{Omega2} & 0\\0 & 0 & 0 & 0 & 0 & K_{Omega3}\end{matrix}\right], \  \left[\begin{matrix}m_{j} & 0 & 0\\0 & m_{j} & 0\\0 & 0 & 0\end{matrix}\right]\right)$

### Bearing component:

```python
func = lambda x: symb('K', x, 'b') if(not isinstance(x, int)) else 0

K_b  = diag(*[func(v) for v in idx_list])
K_b_ = lambda idx: diag(symb('k', idx, 'x'), symb('k', idx, 'y'), symb('k', idx, 'u'))
K_b, K_b_('j')
```

$\displaystyle \left( \left[\begin{matrix}K_{cb} & 0 & 0 & 0 & 0 & 0\\0 & K_{rb} & 0 & 0 & 0 & 0\\0 & 0 & K_{sb} & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0 & 0\end{matrix}\right], \  \left[\begin{matrix}k_{jx} & 0 & 0\\0 & k_{jy} & 0\\0 & 0 & k_{ju}\end{matrix}\right]\right)$

where $k_{ix}$ and $k_{iy}$ are the bearing stiffness components in the $x$ and $y$ directions. The torsional components of the bearing stiffness are null, i.e. $k_{iu} = 0$, for $i = c, r, s$. The stiffness of the planet bearings can be found on the sub-matrices $K_c$ of the mesh stiffness matrix.
### Mesh component:
The mesh stiffness matrix is defined in the figure below, where it is sub-divided in blocks with colors, red, grey, and blue, called diagonal 1, diagonal 2, and off-diagonal, respectively.
\begin{align}
    \begin{bmatrix}
       \sum \mathbf{K}_{c1}^n & \mathbf{0}             & \mathbf{0}             & \mathbf{K}_{c2}^1 & \mathbf{K}_{c2}^2 & \ldots & \mathbf{K}_{c2}^N\\
                              & \sum \mathbf{K}_{r1}^n & \mathbf{0}             & \mathbf{K}_{r2}^1 & \mathbf{K}_{r2}^2 & \ldots & \mathbf{K}_{r2}^N\\
                              &                        & \sum \mathbf{K}_{s1}^n & \mathbf{K}_{s2}^1 & \mathbf{K}_{s2}^2 & \ldots & \mathbf{K}_{s2}^N \\
                              &                        &                        & \mathbf{K}_{pp}^1 & \mathbf{0}       & \ldots & \mathbf{0} \\
       {\rm symmetric}        &                        &                        &                   & \ddots           & \ldots & \vdots \\
                              &                        &                        &                   &                  &        & \mathbf{K}_{pp}^N
    \end{bmatrix}
\end{align}

#### Diagonal 1

```python
K_d1 = diag(Sum(symb('K', 'c1', '^j'), (symbols('j'), 1, N)),
            Sum(symb('K', 'r1', '^j'), (symbols('j'), 1, N)),
            Sum(symb('K', 's1', '^j'), (symbols('j'), 1, N)))
K_d1
```

$\displaystyle \left[\begin{matrix}\sum_{j=1}^{3} K^{j}_{c1} & 0 & 0\\0 & \sum_{j=1}^{3} K^{j}_{r1} & 0\\0 & 0 & \sum_{j=1}^{3} K^{j}_{s1}\end{matrix}\right]$

#### Diagonal 2

```python
K_d2 = diag(*[symb('K', 'pp', '^{}'.format(v + 1)) for v in range(N)])
K_d2
```

$\displaystyle \left[\begin{matrix}K^{1}_{pp} & 0 & 0\\0 & K^{2}_{pp} & 0\\0 & 0 & K^{3}_{pp}\end{matrix}\right]$

#### Off-diagonal

```python
K_od = Matrix([[symb('K', 'c2', '^{}'.format(v + 1)) for v in range(N)],
               [symb('K', 'r2', '^{}'.format(v + 1)) for v in range(N)],
               [symb('K', 's2', '^{}'.format(v + 1)) for v in range(N)]])
K_od
```

$\displaystyle \left[\begin{matrix}K^{1}_{c2} & K^{2}_{c2} & K^{3}_{c2}\\K^{1}_{r2} & K^{2}_{r2} & K^{3}_{r2}\\K^{1}_{s2} & K^{2}_{s2} & K^{3}_{s2}\end{matrix}\right]$

#### Assembly

```python
K_m = BlockMatrix([[          K_d1 , K_od],
                   [transpose(K_od), K_d2]])
K_m = Matrix(K_m)

if(not K_m.is_symmetric()):
    print('error.')
K_m
```

$\displaystyle \left[\begin{matrix}\sum_{j=1}^{3} K^{j}_{c1} & 0 & 0 & K^{1}_{c2} & K^{2}_{c2} & K^{3}_{c2}\\0 & \sum_{j=1}^{3} K^{j}_{r1} & 0 & K^{1}_{r2} & K^{2}_{r2} & K^{3}_{r2}\\0 & 0 & \sum_{j=1}^{3} K^{j}_{s1} & K^{1}_{s2} & K^{2}_{s2} & K^{3}_{s2}\\K^{1}_{c2} & K^{1}_{r2} & K^{1}_{s2} & K^{1}_{pp} & 0 & 0\\K^{2}_{c2} & K^{2}_{r2} & K^{2}_{s2} & 0 & K^{2}_{pp} & 0\\K^{3}_{c2} & K^{3}_{r2} & K^{3}_{s2} & 0 & 0 & K^{3}_{pp}\end{matrix}\right]$

The submatrices of $\mathbf{K}_m$ will be defined later on, after we reorganize the overall system matrices.
## Reorganizing the system matrices:
The following steps will be implemented using matrices that should be pre and post-multiply the system matrices and submatrices in the following way:
\begin{align}
    \mathbf{A}_\mathrm{new} = \mathbf{Y}^T \mathbf{A}_\mathrm{old} \mathbf{Y}
\end{align}

### Eliminating the ring element:
One can do that using linear algebra with the matrix $\mathbf{R}$ described as follows:

```python
N_eo = N + 3 # number of elements (original)
N_e  = N_eo - 1 # number of elements (new)

W = eye(N_e, N_eo)
for idx in range(N_e - 1):
    c1 = N_e - idx
    c2 = c1 - 1
    W = W.elementary_col_op(op = 'n<->m', col1 = c1, col2 = c2)
# q_2 = W * q:
W*q, W, q
```

$\displaystyle \left( \left[\begin{matrix}q_{c}\\q_{s}\\q_{1}\\q_{2}\\q_{3}\end{matrix}\right], \  \left[\begin{matrix}1 & 0 & 0 & 0 & 0 & 0\\0 & 0 & 1 & 0 & 0 & 0\\0 & 0 & 0 & 1 & 0 & 0\\0 & 0 & 0 & 0 & 1 & 0\\0 & 0 & 0 & 0 & 0 & 1\end{matrix}\right], \  \left[\begin{matrix}q_{c}\\q_{r}\\q_{s}\\q_{1}\\q_{2}\\q_{3}\end{matrix}\right]\right)$

### Changing the element order:
One can do that with the matrix $\mathbf{V}$, which is defined as:

```python
N1 = N_e - 1

V = zeros(N_e)
V[0   , 0 ] = 1
V[  N1, 1 ] = 1
V[1:N1, 2:] = eye(N)

# q_3 = V * q_2
W*q, V, V*W*q
```

$\displaystyle \left( \left[\begin{matrix}q_{c}\\q_{s}\\q_{1}\\q_{2}\\q_{3}\end{matrix}\right], \  \left[\begin{matrix}1 & 0 & 0 & 0 & 0\\0 & 0 & 1 & 0 & 0\\0 & 0 & 0 & 1 & 0\\0 & 0 & 0 & 0 & 1\\0 & 1 & 0 & 0 & 0\end{matrix}\right], \  \left[\begin{matrix}q_{c}\\q_{1}\\q_{2}\\q_{3}\\q_{s}\end{matrix}\right]\right)$

Both steps can be merged into a single matrix \mathbf{S}$, defined as:

```python
S = V*W
S = S.pinv()
pre_post = lambda X, A: transpose(X)*A*X
S
```

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

Thus the system matrices can be redefined as follows:
### Inertia matrix

```python
M = pre_post(S, M)
M
```

$\displaystyle \left[\begin{matrix}M_{c} & 0 & 0 & 0 & 0\\0 & M_{1} & 0 & 0 & 0\\0 & 0 & M_{2} & 0 & 0\\0 & 0 & 0 & M_{3} & 0\\0 & 0 & 0 & 0 & M_{s}\end{matrix}\right]$

### Gyroscopic matrix

```python
G = pre_post(S, G)
G
```

$\displaystyle \left[\begin{matrix}G_{c} & 0 & 0 & 0 & 0\\0 & G_{1} & 0 & 0 & 0\\0 & 0 & G_{2} & 0 & 0\\0 & 0 & 0 & G_{3} & 0\\0 & 0 & 0 & 0 & G_{s}\end{matrix}\right]$

### Stiffness matrix

#### Centripetal component

```python
K_Omega = pre_post(S, K_Omega)
K_Omega
```

$\displaystyle \left[\begin{matrix}K_{Omegac} & 0 & 0 & 0 & 0\\0 & K_{Omega1} & 0 & 0 & 0\\0 & 0 & K_{Omega2} & 0 & 0\\0 & 0 & 0 & K_{Omega3} & 0\\0 & 0 & 0 & 0 & K_{Omegas}\end{matrix}\right]$

#### Bearing component:

```python
K_b = pre_post(S, K_b)
K_b
```

$\displaystyle \left[\begin{matrix}K_{cb} & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 0\\0 & 0 & 0 & 0 & K_{sb}\end{matrix}\right]$

#### Mesh component:

```python
K_m = pre_post(S, K_m)
K_m
```

$\displaystyle \left[\begin{matrix}\sum_{j=1}^{3} K^{j}_{c1} & K^{1}_{c2} & K^{2}_{c2} & K^{3}_{c2} & 0\\K^{1}_{c2} & K^{1}_{pp} & 0 & 0 & K^{1}_{s2}\\K^{2}_{c2} & 0 & K^{2}_{pp} & 0 & K^{2}_{s2}\\K^{3}_{c2} & 0 & 0 & K^{3}_{pp} & K^{3}_{s2}\\0 & K^{1}_{s2} & K^{2}_{s2} & K^{3}_{s2} & \sum_{j=1}^{3} K^{j}_{s1}\end{matrix}\right]$

## Combine planet elements
That can be done by considering the center of mass of the $N$ planet gears, with the following matrix:

```python
N1 = N_e - 1
N_c = N_e - (N - 1) # number of elements (combined planets)
C = zeros(N_c, N_e)

C[0, 0] = 1
C[N_c - 1, N_e - 1] = 1
C[1, 1:N1] = ones(1,N)*1/N
C = C.pinv()

C.pinv()*S.pinv()*q, C.pinv(), S.pinv()*q
```

$\displaystyle \left( \left[\begin{matrix}q_{c}\\\frac{q_{1}}{3} + \frac{q_{2}}{3} + \frac{q_{3}}{3}\\q_{s}\end{matrix}\right], \  \left[\begin{matrix}1 & 0 & 0 & 0 & 0\\0 & \frac{1}{3} & \frac{1}{3} & \frac{1}{3} & 0\\0 & 0 & 0 & 0 & 1\end{matrix}\right], \  \left[\begin{matrix}q_{c}\\q_{1}\\q_{2}\\q_{3}\\q_{s}\end{matrix}\right]\right)$

### Inertia matrix
The planets have the same mass and mass moment of inertia.

```python
M_C = pre_post(C, M)
M_C = M_C.subs([(symb('M', v + 1), symb('M','')) for v in range(N)])
M_C
```

$\displaystyle \left[\begin{matrix}M_{c} & 0 & 0\\0 & 3 M_{} & 0\\0 & 0 & M_{s}\end{matrix}\right]$

### Gyroscopic matrix

```python
G_C = pre_post(C, G)
G_C = G_C.subs([(symb('G', v + 1), symb('G','')) for v in range(N)])
G_C
```

$\displaystyle \left[\begin{matrix}G_{c} & 0 & 0\\0 & 3 G_{} & 0\\0 & 0 & G_{s}\end{matrix}\right]$

### Stiffness matrix

#### Centripetal component

```python
K_Omega_C = pre_post(C, K_Omega)
K_Omega_C = K_Omega_C.subs([(symb('K', 'Omega', v + 1), symb('K', 'Omega','')) for v in range(N)])
K_Omega_C
```

$\displaystyle \left[\begin{matrix}K_{Omegac} & 0 & 0\\0 & 3 K_{\Omega} & 0\\0 & 0 & K_{Omegas}\end{matrix}\right]$

#### Bearing component:

```python
K_b_C = pre_post(C, K_b)
K_b_C
```

$\displaystyle \left[\begin{matrix}K_{cb} & 0 & 0\\0 & 0 & 0\\0 & 0 & K_{sb}\end{matrix}\right]$

#### Mesh component

```python
K_m_C = pre_post(C, K_m)
K_m_C
```

$\displaystyle \left[\begin{matrix}\sum_{j=1}^{3} K^{j}_{c1} & K^{1}_{c2} + K^{2}_{c2} + K^{3}_{c2} & 0\\K^{1}_{c2} + K^{2}_{c2} + K^{3}_{c2} & K^{1}_{pp} + K^{2}_{pp} + K^{3}_{pp} & K^{1}_{s2} + K^{2}_{s2} + K^{3}_{s2}\\0 & K^{1}_{s2} + K^{2}_{s2} + K^{3}_{s2} & \sum_{j=1}^{3} K^{j}_{s1}\end{matrix}\right]$

## Adapting it to represent a parallel gear pair
To do that, we should consider only the gear pair composed by the sun and the first planet gear. That can be done with the following matrix:

```python
V = zeros(2, N_e)
V[0, 1] = 1
V[1, N_e - 1] = 1
V = V.pinv()
V.pinv()*S.pinv()*q, V.pinv(), S.pinv()*q
```

$\displaystyle \left( \left[\begin{matrix}q_{1}\\q_{s}\end{matrix}\right], \  \left[\begin{matrix}0 & 1 & 0 & 0 & 0\\0 & 0 & 0 & 0 & 1\end{matrix}\right], \  \left[\begin{matrix}q_{c}\\q_{1}\\q_{2}\\q_{3}\\q_{s}\end{matrix}\right]\right)$

where the indexes 1 and $s$ now represent the wheel and pinion gears, respectively.
### Inertia matrix

```python
M_par = pre_post(V, M)
M_par.subs([(symb('s',''), symb('w',''))])
```

$\displaystyle \left[\begin{matrix}M_{1} & 0\\0 & M_{s}\end{matrix}\right]$

### Gyroscopic matrix

```python
G_par = pre_post(V, G)
G_par
```

$\displaystyle \left[\begin{matrix}G_{1} & 0\\0 & G_{s}\end{matrix}\right]$

### Stiffness matrix

#### Centripetal component

```python
K_Omega_par = pre_post(V, K_Omega)
K_Omega_par
```

$\displaystyle \left[\begin{matrix}K_{Omega1} & 0\\0 & K_{Omegas}\end{matrix}\right]$

#### Bearing component

```python
K_b_par = pre_post(V, K_b)
K_b_par
```

$\displaystyle \left[\begin{matrix}0 & 0\\0 & K_{sb}\end{matrix}\right]$

```python
K_m_par = pre_post(V, K_m)
K_m_par = K_m_par.as_mutable()
K_m_par[1, 1] = symb('K', 's1', '^1')
K_m_par
```

$\displaystyle \left[\begin{matrix}K^{1}_{pp} & K^{1}_{s2}\\K^{1}_{s2} & K^{1}_{s1}\end{matrix}\right]$

Originally, the term $K_{pp}^1$ is given by:
\begin{align}
    \mathbf{K}_{pp}^n = \mathbf{K}_{c3}^n + \mathbf{K}_{r3}^n + \mathbf{K}_{s3}^n,
\end{align}
where:
* $\mathbf{K}_{c3}^n$ represents the bearing stiffness of the $n$-th planet gear
* $\mathbf{K}_{r3}^n$ represents the mesh stiffness of the $n$-th ring-planet gear pair
* $\mathbf{K}_{s3}^n$ represents the mesh stiffness of the $n$-th sun-planet gear pair

For the current case of a parallel gear pair, the term $\mathbf{K}_{r3}^n$ representing ring intercations should be excluded from $\mathbf{K}_m$. Doing that leads to:

```python
K_m_par[0, 0] = symb('K', 'c3', '^1') + symb('K', 's3', '^1')
K_m_par
```

$\displaystyle \left[\begin{matrix}K^{1}_{c3} + K^{1}_{s3} & K^{1}_{s2}\\K^{1}_{s2} & K^{1}_{s1}\end{matrix}\right]$

## Submatrices of the mesh stiffness matrix $\mathbf{K}_m$
The following symplifying assumptions:
* the pressure angles of sun and ring gears are equal, i.e. $\alpha_s = \alpha_r = \alpha_n$
* the mesh stiffness for the $N$ sun-planet pairs are equal, the same happens for the $N$ ring-planet pairs
* the planetary gear is cyclically symmetric, with the $j$-th planet being at circumferential location $\psi_j$, with:
\begin{align}
    \psi_j = (j - 1) \psi, \qquad \psi = \frac{2 \pi}{N}
\end{align}

```python
psi_ = lambda j: (j - 1)*symb('psi', '')
j = symbols('j', type = int)
K_c1 = lambda x: symb('k', 'p')*Matrix([[ 1           , 0           , -sin(psi_(x))],
                                        [ 0           , 1           ,  cos(psi_(x))],
                                        [-sin(psi_(x)), cos(psi_(x)),  1           ]])
k_tmp = K_c1(j)
if(not k_tmp.is_symmetric()):
    print('error.')
k_tmp
```

$\displaystyle \left[\begin{matrix}k_{p} & 0 & - k_{p} \sin{\left(\psi_{} \left(j - 1\right) \right)}\\0 & k_{p} & k_{p} \cos{\left(\psi_{} \left(j - 1\right) \right)}\\- k_{p} \sin{\left(\psi_{} \left(j - 1\right) \right)} & k_{p} \cos{\left(\psi_{} \left(j - 1\right) \right)} & k_{p}\end{matrix}\right]$

where $k_p$ is the planet bearing stiffness.

```python
K_c2 = lambda x: symb('k', 'p')*Matrix([[-cos(psi_(x)),  sin(psi_(x)), 0],
                                        [-sin(psi_(x)), -cos(psi_(x)), 0],
                                        [ 0           , -1           , 0]])
k_tmp = K_c2(j)
k_tmp
```

$\displaystyle \left[\begin{matrix}- k_{p} \cos{\left(\psi_{} \left(j - 1\right) \right)} & k_{p} \sin{\left(\psi_{} \left(j - 1\right) \right)} & 0\\- k_{p} \sin{\left(\psi_{} \left(j - 1\right) \right)} & - k_{p} \cos{\left(\psi_{} \left(j - 1\right) \right)} & 0\\0 & - k_{p} & 0\end{matrix}\right]$

```python
K_c3 = symb('k', 'p')*diag(1, 1, 0)
K_c3
```

$\displaystyle \left[\begin{matrix}k_{p} & 0 & 0\\0 & k_{p} & 0\\0 & 0 & 0\end{matrix}\right]$

Notice that the planet bearings are isotropic, presenting the same stiffness in directions $x$ and $y$, different from the other bearing stiffness submatrices that present different $x$, $y$, and $u$ components. One can rewrite the matrices $\mathbf{K}_{ci}^n$ for anysotropic bearings in the following way:
\begin{align}
    \mathbf{K}_{ci}^n(\mathrm{new}) = \frac{1}{k_{pj}}\mathbf{K}_{ci}^n(\mathrm{old}) \mathrm{diag}(k_{px}, k_{py}, k_{pu})
\end{align}

```python
def K_c(i, j):
    if(i == 1):
        k_old = K_c1(j)
    elif(i == 2):
        k_old = K_c2(j)
    elif(i == 3):
        k_old = K_c3
    else:
        print('ERROR inf K_c(), i = {} is out of range.'.format(i))

    return (1/symb('k', 'p'))*diag(symb('k', 'p', 'x'), symb('k', 'p', 'y'), 0*symb('k', 'p', 'u'))*k_old

K_c(1, j), K_c(2, j), K_c(3, j)
```

$\displaystyle \left( \left[\begin{matrix}k_{px} & 0 & - k_{px} \sin{\left(\psi_{} \left(j - 1\right) \right)}\\0 & k_{py} & k_{py} \cos{\left(\psi_{} \left(j - 1\right) \right)}\\0 & 0 & 0\end{matrix}\right], \  \left[\begin{matrix}- k_{px} \cos{\left(\psi_{} \left(j - 1\right) \right)} & k_{px} \sin{\left(\psi_{} \left(j - 1\right) \right)} & 0\\- k_{py} \sin{\left(\psi_{} \left(j - 1\right) \right)} & - k_{py} \cos{\left(\psi_{} \left(j - 1\right) \right)} & 0\\0 & 0 & 0\end{matrix}\right], \  \left[\begin{matrix}k_{px} & 0 & 0\\0 & k_{py} & 0\\0 & 0 & 0\end{matrix}\right]\right)$

```python
psi, alpha_n = symbols('psi alpha_n', type = float)
psi_s = lambda x: psi_(x) - alpha_n
psi_r = lambda x: psi_(x) + alpha_n

K_r1 = lambda x: symb('k', 'r')*Matrix([[               sin(psi_r(x))**2, -cos(psi_r(x))*sin(psi_r(x)), -sin(psi_r(x))],
                                        [-cos(psi_r(x))*sin(psi_r(x))   ,  cos(psi_r(x))**2           ,  cos(psi_r(x))],
                                        [-              sin(psi_r(x))   ,  cos(psi_r(x))              ,  1]])
k_tmp = K_r1(j)
if(not k_tmp.is_symmetric()):
    print('error.')
k_tmp
```

$\displaystyle \left[\begin{matrix}k_{r} \sin^{2}{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & - k_{r} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & - k_{r} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)}\\- k_{r} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & k_{r} \cos^{2}{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & k_{r} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)}\\- k_{r} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & k_{r} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & k_{r}\end{matrix}\right]$

where $k_{r}$ is the mesh stiffness between ring and planet gears.

```python
K_r2 = lambda x: symb('k', 'r')*Matrix([[-sin(psi_r(x))*sin(alpha_n),  sin(psi_r(x))*cos(alpha_n),  sin(psi_r(x))],
                                        [ cos(psi_r(x))*sin(alpha_n), -cos(psi_r(x))*cos(alpha_n), -cos(psi_r(x))],
                                        [               sin(alpha_n), -              cos(alpha_n), -1]])
k_tmp = K_r2(j)
k_tmp
```

$\displaystyle \left[\begin{matrix}- k_{r} \sin{\left(\alpha_{n} \right)} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & k_{r} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} \cos{\left(\alpha_{n} \right)} & k_{r} \sin{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)}\\k_{r} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & - k_{r} \cos{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)} & - k_{r} \cos{\left(\alpha_{n} + \psi_{} \left(j - 1\right) \right)}\\k_{r} \sin{\left(\alpha_{n} \right)} & - k_{r} \cos{\left(\alpha_{n} \right)} & - k_{r}\end{matrix}\right]$

```python
K_r3 = lambda x: symb('k', 'r')*Matrix([[ sin(alpha_n)**2          , -sin(alpha_n)*cos(alpha_n), -sin(alpha_n)],
                                        [-sin(alpha_n)*cos(alpha_n),  cos(alpha_n)**2          ,  cos(alpha_n)],
                                        [-sin(alpha_n)             ,  cos(alpha_n)             ,  1]])
k_tmp = K_r3('j')
if(not k_tmp.is_symmetric()):
    print('error.')
k_tmp
```

$\displaystyle \left[\begin{matrix}k_{r} \sin^{2}{\left(\alpha_{n} \right)} & - k_{r} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} & - k_{r} \sin{\left(\alpha_{n} \right)}\\- k_{r} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} & k_{r} \cos^{2}{\left(\alpha_{n} \right)} & k_{r} \cos{\left(\alpha_{n} \right)}\\- k_{r} \sin{\left(\alpha_{n} \right)} & k_{r} \cos{\left(\alpha_{n} \right)} & k_{r}\end{matrix}\right]$

```python
K_s1 = lambda x: symb('k', 's')*Matrix([[               sin(psi_s(x))**2, -cos(psi_s(x))*sin(psi_s(x)), -sin(psi_s(x))],
                                        [-cos(psi_s(x))*sin(psi_s(x))   ,  cos(psi_s(x))**2           ,  cos(psi_s(x))],
                                        [-              sin(psi_s(x))   ,  cos(psi_s(x))              ,  1]])
k_tmp = K_s1(j)
if(not k_tmp.is_symmetric()):
    print('error.')
k_tmp
```

$\displaystyle \left[\begin{matrix}k_{s} \sin^{2}{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)}\\k_{s} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s} \cos^{2}{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)}\\k_{s} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s}\end{matrix}\right]$

where $k_{sj}$ is the mesh stiffness between the sun and the $j$-th planet

```python
K_s2 = lambda x: symb('k', 's')*Matrix([[ sin(psi_s(x))*sin(alpha_n),  sin(psi_s(x))*cos(alpha_n), -sin(psi_s(x))],
                                        [-cos(psi_s(x))*sin(alpha_n), -cos(psi_s(x))*cos(alpha_n),  cos(psi_s(x))],
                                        [-              sin(alpha_n), -              cos(alpha_n),  1]])
K_s2(j)
```

$\displaystyle \left[\begin{matrix}- k_{s} \sin{\left(\alpha_{n} \right)} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & - k_{s} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} \cos{\left(\alpha_{n} \right)} & k_{s} \sin{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)}\\- k_{s} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & - k_{s} \cos{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)} & k_{s} \cos{\left(\alpha_{n} - \psi_{} \left(j - 1\right) \right)}\\- k_{s} \sin{\left(\alpha_{n} \right)} & - k_{s} \cos{\left(\alpha_{n} \right)} & k_{s}\end{matrix}\right]$

```python
K_s3 = lambda x: symb('k', 's')*Matrix([[ sin(alpha_n)**2          ,  sin(alpha_n)*cos(alpha_n), -sin(alpha_n)],
                                        [ sin(alpha_n)*cos(alpha_n),  cos(alpha_n)**2          , -cos(alpha_n)],
                                        [-sin(alpha_n)             , -cos(alpha_n)             ,  1]])
k_tmp = K_s3(j)
if(not k_tmp.is_symmetric()):
    print('error.')
k_tmp
```

$\displaystyle \left[\begin{matrix}k_{s} \sin^{2}{\left(\alpha_{n} \right)} & k_{s} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} & - k_{s} \sin{\left(\alpha_{n} \right)}\\k_{s} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} & k_{s} \cos^{2}{\left(\alpha_{n} \right)} & - k_{s} \cos{\left(\alpha_{n} \right)}\\- k_{s} \sin{\left(\alpha_{n} \right)} & - k_{s} \cos{\left(\alpha_{n} \right)} & k_{s}\end{matrix}\right]$

```python
K_pp = lambda x: K_c3 + K_r3(x) + K_s3(x)
k_tmp = K_pp(j)
if(not k_tmp.is_symmetric()):
    print('error.')
k_tmp
```

$\displaystyle \left[\begin{matrix}k_{p} + k_{r} \sin^{2}{\left(\alpha_{n} \right)} + k_{s} \sin^{2}{\left(\alpha_{n} \right)} & - k_{r} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} + k_{s} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} & - k_{r} \sin{\left(\alpha_{n} \right)} - k_{s} \sin{\left(\alpha_{n} \right)}\\- k_{r} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} + k_{s} \sin{\left(\alpha_{n} \right)} \cos{\left(\alpha_{n} \right)} & k_{p} + k_{r} \cos^{2}{\left(\alpha_{n} \right)} + k_{s} \cos^{2}{\left(\alpha_{n} \right)} & k_{r} \cos{\left(\alpha_{n} \right)} - k_{s} \cos{\left(\alpha_{n} \right)}\\- k_{r} \sin{\left(\alpha_{n} \right)} - k_{s} \sin{\left(\alpha_{n} \right)} & k_{r} \cos{\left(\alpha_{n} \right)} - k_{s} \cos{\left(\alpha_{n} \right)} & k_{r} + k_{s}\end{matrix}\right]$

## Rotational coordinates
The rotation coordinates are:
\begin{align}
u_j = r_j \theta_j,
\end{align}
where $\theta_j$ and $r_j$ are the component rotation and the base radius of a component, respectively. For the planet carrier, one should use the center distance instead. One can write the equations of motion in terms of the component rotations, by using the following matrix:

```python
R = lambda x: diag(1, 1, symb('r', x))
R(j)
```

$\displaystyle \left[\begin{matrix}1 & 0 & 0\\0 & 1 & 0\\0 & 0 & r_{j}\end{matrix}\right]$

```python
pre_post(R(j), K_b_(j))
```

$\displaystyle \left[\begin{matrix}k_{jx} & 0 & 0\\0 & k_{jy} & 0\\0 & 0 & k_{ju} r_{j}^{2}\end{matrix}\right]$

This operation does not affect the gyroscopic and centripetal submatrices $\mathbf{G}_j$ and $\mathbf{K}_{\Omega,j}$.

```python

```


```python
import numpy as np
import pandas as pd
from scipy.optimize import fsolve
import pickle

%matplotlib notebook
import matplotlib.pyplot as plt
```

To understand the effects of noise (or limiting mag) on one's ability to recover the time of explosion (or better, the time of first light for a young SN), we construct a simple generative model to simulate the flux from the SN.

In [Zheng & Filippenko (2017)](http://adsabs.harvard.edu/abs/2017ApJ...838L...4Z), a broken power-law parameterization of SN Ia light curves is introduced. This parameterization is somewhat physically motivated, however, there are some assumptions that break down for some SNe Ia. A major advantage of this method, however, is that it covers the peak and turn-over of SN light curves, so there is no need to artificially truncate the data (to ~5 d after explosion for example) in order to only fit the early rise.

The formulation is:

$$L = A' \left(\frac{t - t_0}{t_b}\right)^{\alpha_\mathrm{r}} \left[1 + \left(\frac{t - t_0}{t_b}\right)^{s\alpha_\mathrm{d}}\right]^{-2/s}$$

which has a peak value when:

$$ t_p = t_b \times \left(-\frac{\alpha_1 + 1}{\alpha_2 + 1}\right)^{1/[s(\alpha_1 - \alpha_2)]} $$

and:

$$ \begin{align} \alpha_\mathrm{r} & = & 2(\alpha_1 + 1) \\ \alpha_\mathrm{d} & = & \alpha_1 - \alpha_2 \end{align}. $$

With this parameterization, the challenge is to figure out reasonable values from which to draw the various parameters.

From theoretical arguments, we expect that $\alpha_\mathrm{r} \approx 2$, and empirical results largely show this to be true, however, a small handful of SNe have been observed to have $\alpha_\mathrm{r} \approx 1$. Therefore we select $\alpha_\mathrm{r}$ from $[0.75, 2.5]$.

$t_b$ is related to rise time, and we draw this from $\mathcal{N}(18,1)$, based on the Ganeshalingham et al. 2010.

$s$ is a smoothing parameter that does not have a strong physical prior. It should be of order unity, and we draw this from a truncated Gaussian $\mathcal{N}(1.2,0.3)$ that does not go below 0. The precise choice of $s$ is not super important as this is largely degenerate with $\alpha_\mathrm{d}$, which is selected based on $\Delta{m}_{15}$.

We draw the absolute magnitude and decline rate from a multivariate normal distribution:

$$X \sim \mathcal{N}\left(\begin{bmatrix} -19.3 \\ 1.1 \end{bmatrix}, \begin{bmatrix} 0.04 & 0.042 \\ 0.042 & 0.09\end{bmatrix} \right)$$

From this, we can determine $\alpha_\mathrm{d}$ by setting $\Delta{m}_{15} = 2.5 \log\left(\frac{L(t=t_p)}{L(t=t_p+15)}\right)$, which in turn, allows a determination of $A'$ from the distance to the SN and $M$.

We assume a Hubble constant $H_0 = 72 \, \mathrm{km \, s^{-1} \, Mpc^{-1}}$, and limit our analysis to $d < 100 \mathrm{Mpc}$. This corresponds to $z = (H_0\, d)/c \approx 0.024$.

```python
def delta_m15_root(alpha_d, t_p=18, alpha_r=2, s=1, dm15=1.1):
    '''Root solver for alpha_d based on Delta m15

    Using Eqn. 4 from Zheng & Filippenko (2017), ApJL, 838, 4, it is
    possible to calculate the ratio of flux from a SN Ia at t = t_peak
    and t = t_peak + 15. If t_p, alpha_r, and s are known, then the
    ratio of flux should equal Delta m15. The scipy.optimize root
    finder fsolve is used to solve for the value of alpha_d.

    Parameters
    ----------
    alpha_d : float
        Power-law index for the late-time decline of the SN

    t_p : float, optional (default=18)
        Time to peak of the SN light curve

    alpha_r : float, optional (default=2)
        Power-law index for initial rise of the SN light curve

    s : float, optional (default=1)
        Smoothing parameter for the light curve

    dm15 : float, optional (default=1.1)
        Delta m15

    Returns
    -------
    alpha_d_root
        The value of alpha_d that results in a SN light curve
        with a 15 day decline rate = Delta m15
    '''

    t_b = t_p/((-alpha_r/2)/(alpha_r/2 - alpha_d))**(1/(s*(alpha_d)))

    Ltp = (t_p/t_b)**alpha_r * (1 + (t_p/t_b)**(s*alpha_d))**(-2/s)
    Ltp_15 = ((t_p + 15)/t_b)**alpha_r * (1 + ((t_p + 15)/t_b)**(s*alpha_d))**(-2/s)

    return 2.5*np.log10(Ltp/Ltp_15) - dm15

class SimSnIa():

    def __init__(self, name=None):
        '''initialize the simulated SN

        Attributes
        ----------
        name_ : str (default=None)
            Name of the SN object
        '''
        self.name_ = name

    def draw_dist_in_volume(self, d_max=100, H_0=72):
        '''simulate SN at a random distance within a fixed volume

        Parameters
        ----------
        d_max : int, optional (default=100)
            Maximum distance for the simulated SNe, units in Mpc

        H_0 : float, optional (default=72)
            Value of the Hubble constant (in km/s/Mpc) used to convert the
        distance to the SN to a redshift, z.

        Attributes
        ----------
        dist_ : float
            Distance to the SN in Mpc

        z_ : float
            Redshift to the SN

        mu_ : float
            distance modulus to the SN
        '''

        self.dist_ = np.random.uniform()**(1/3)*d_max
        self.z_ = H_0*self.dist_/2.997942e5
        self.mu_ = 5*np.log10(self.dist_) + 25

    def draw_alpha_r(self, alpha_low=1, alpha_high=2.5):
        '''draw random value for early rise power-law index

        Select a random value from a flat distribution between
        alpha_low and alpha_high to determine the power-law index
        for the initial rise of the SN light curve.

        Parameters
        ----------
        alpha_low : float, optional (default=1)
            Minimum value for the power-law index of the early rise

        alpha_high : float, optional (default=2.5)
            Maximum value for the power-law index of the early rise

        Attributes
        ----------
        alpha_r_ : float
            Power-law index for initial rise of the SN light curve
        '''

        self.alpha_r_ = np.random.uniform(alpha_low, alpha_high)

    def draw_rise_time(self, mu_rise=18, sig_rise=1):
        '''draw random value for the light curve rise time

        Select a random value from a gaussian distribution with
        mean, mu_rise (default=18), and standard deviation,
        sig_rise (default=1). The defaults are selected based on the
        results from Ganeshalingam et al. 2011, MNRAS, 416, 2607
        which found that the rise time for SNe Ia can be described
        as ~ N(18.03, 0.0576).

        Parameters
        ----------
        mu_rise : float, optional (default=18)
            Mean value for the rise time of SN Ia

        sig_rise : float, optional (default=1)
            Standard deviation of the rise time distribution for
            SNe Ia

        Attributes
        ----------
        t_p_ : float
            Time for the light curve to reach peak brightness
        '''

        self.t_p_ = np.random.normal(mu_rise, sig_rise)

    def draw_smoothing_parameter(self, mu_s=2, sig_s=0.5):
        '''draw random value for the smoothing parameter

        Select a random value from a truncated gaussian distribution
        with mean, mu_s (default=2), and standard deviation,
        sig_s (default=0.5). This parameter is not physical, and
        is largely degenerate with alpha_decline. It is drawn from
        a guassian distribution while alpha_decline is selected to
        ensure a physical value of delta m15.

        Parameters
        ----------
        mu_s : float, optional (default=2)
            Mean value for the smoothing parameter

        sig_s : float, optional (default=0.5)
            Standard deviation of the smoothing parameter

        Attributes
        ----------
        s_ : float
            Smoothing parameter for the light curve
        '''
        s = -1
        while s < 0:
            s = np.random.normal(mu_s, sig_s)

        self.s_ = s

    def draw_mb_deltam15(self, pkl_file='phillips_kde.pkl'):
        '''Draw random M_b and Delta m15 values

        Draw from a KDE estimate based on Burns et al. 2018 to get
        M_b and Delta m15 for a "normal" SN Ia.

        Parameters
        ----------
        pkl_file : str, filename (defaualt='phillips_kde.pkl')
            Pickle file that contains the KDE estimate of the
            Phillips relation

        Attributes
        ----------
        M_b_ : float
            Rest-frame absolute magnitude in the B band at the
            time of peak brightness
        dm15_ : float
            Delta m15 for the SN
        '''
        with open(pkl_file, 'rb') as file:
            sn_tuple = pickle.load(file)

        kde, phillips_scaler = sn_tuple
        scaled_sample = kde.sample(1)[0]

        self.dm15_, self.M_b_= phillips_scaler.inverse_transform(scaled_sample)

    def calc_alpha_d(self, alpha_d_guess=2):
        '''Calculate the value of alpha_d based on Delta m15

        Parameters
        ----------
        alpha_d_guess : float, optional (default=2)
            Initial guess to solve for the root of the alpha_d eqn

        Attributes
        ----------
        alpha_d_ : float
            Power-law index for the late-time decline of the SN
        '''
        if not (hasattr(self, 't_p_') and hasattr(self, 'alpha_r_') and
                hasattr(self, 's_') and hasattr(self, 'dm15_')):
            self.draw_alpha_r()
            self.draw_rise_time()
            self.draw_smoothing_parameter()
            self.draw_mb_deltam15()

        alpha_d = fsolve(delta_m15_root, alpha_d_guess,
                         args=(self.t_p_, self.alpha_r_,
                               self.s_, self.dm15_))

        self.alpha_d_ = float(alpha_d)

    def calc_a_prime(self):
        '''Calculate the value of Aprime

        Determine the normalization constant to generate a
        SN light curve with peak flux equal to the luminosity
        associated with M_b.

        Attributes
        ----------
        t_b_ : float
            "break time" for the broken power-law model

        a_prime_ : float
            Amplitude for the SN light curve
        '''
        if not (hasattr(self, 'alpha_d_') and hasattr(self, 'mu_')):
            self.draw_dist_in_volume()
            self.calc_alpha_d()

        m_peak = self.M_b_ + self.mu_

        f_peak = 10**(0.4*(25-m_peak))

        t_b = self.t_p_/((-self.alpha_r_/2)/(self.alpha_r_/2 - self.alpha_d_))**(1/(self.s_*(self.alpha_d_)))

        model_peak = ((self.t_p_)/t_b)**self.alpha_r_ * (1 + ((self.t_p_)/t_b)**(self.s_*self.alpha_d_))**(-2/self.s_)

        a_prime = f_peak/model_peak

        self.t_b_ = t_b
        self.a_prime_ = a_prime

    def calc_ft(self, t_obs, t_exp=0):
        '''Calculate the model flux at input times t_obs

        Use Eqn. 4 of Zheng & Filippenko 2017 to determine the
        flux from the SN at all input times t_obs.

        Parameters
        ----------
        t_obs : array-like of shape = [n_obs]
            Times at which to calculate the flux from the SN

        t_exp : float, optional (default=0)
            Time of explosion for the SN model

        Attributes
        ----------
        t_obs_ : array-like of shape = [n_obs]
            Times at which the SN flux is measured

        t_exp_ : float
            SN time of explosion

        model_flux : array-like of shape = [n_obs]
            The model flux at all times t_obs, assuming no noise
            contributes to the signal from the SN
        '''
        if not hasattr(self, 'a_prime_'):
            self.calc_a_prime()

        pre_explosion = np.logical_not(t_obs > t_exp)

        model_flux = np.empty_like(t_obs)
        model_flux[pre_explosion] = 0

        t_rest = t_obs[~pre_explosion]/(1 + self.z_)
        model_flux[~pre_explosion] = self.a_prime_ * (((t_rest - t_exp)/self.t_b_)**self.alpha_r_ *
                                     (1 + ((t_rest - t_exp)/self.t_b_)**(self.s_*self.alpha_d_))**(-2/self.s_))

        self.t_obs_ = t_obs
        self.t_exp_ = t_exp
        self.model_flux_ = model_flux

    def calc_noisy_lc(self, sigma_sys=20):
        '''Calculate SN light curve with systematic and statistical noise

        Parameters
        ----------
        sigma_sys : float, optional (default=20)
            Systematic noise term to noisify the light curve. Telescope
            system is assumed to have a zero-point of 25, such that
            m = 25 - 2.5*log10(flux). Thus,
            sigma_sys(5-sigma limiting mag) = 10**(0.4*(25 - m_lim))/5.
            Default corresponds to a limiting mag of 20.

        Attributes
        ----------
        cnts : array-like of shape = [n_obs]
            noisy flux from the SN light curve

        cnts_unc : array-like of shape = [n_obs]
            uncertainty on the noisy flux measurements
        '''
        if not hasattr(self, 'model_flux_'):
            self.calc_ft()

        cnts = np.zeros_like(self.t_obs_)
        cnts_unc = np.zeros_like(self.t_obs_)

        pre_explosion = np.logical_not(self.t_obs_ > self.t_exp_)
        cnts[pre_explosion] = np.random.normal(0, sigma_sys, size=sum(pre_explosion))
        cnts_unc[pre_explosion] = np.ones_like(self.t_obs_)[pre_explosion]*sigma_sys

        sn_flux = self.model_flux_[~pre_explosion]
        sn_with_random_noise = sn_flux + np.random.normal(np.zeros_like(sn_flux), np.sqrt(sn_flux))
        sn_with_random_plus_sys = sn_with_random_noise + np.random.normal(0, sigma_sys, size=len(sn_flux))

        # total uncertainty = systematic + Poisson
        sn_uncertainties = np.hypot(np.sqrt(np.maximum(sn_with_random_noise,
                                                       np.zeros_like(sn_with_random_noise))),
                                    sigma_sys)

        cnts[~pre_explosion] = sn_with_random_plus_sys
        cnts_unc[~pre_explosion] = sn_uncertainties

        self.cnts_ = cnts
        self.cnts_unc_ = cnts_unc
```

To account for the noise in the telescope system, a systematic contribution is added to the SN flux, where the magnitude of the systematic term is related to the limiting magnitude of the telescope. For example, when we adopt:
$$ m = 25 - 2.5\log(\mathrm{counts}),$$
for a $m = 20\,\mathrm{mag}$ $5\sigma$ limit, the counts = 100 and therefore `sigma_sys` = 20.

Using this generative model, we can incorporate the effects of a detection limit via the `sigma_sys` variable. In particular, smaller aperture telescopes will have larger values of sigma_sys as follows:

| $m_\mathrm{lim}$ | counts | `sigma_sys` |
|   ----           |   ---- |     ----    |
|   22.0           |   15.8 |     3.17    |
|   21.5           |   25.1 |     5.02    |
|   21.0           |   39.8 |     7.96    |
|   20.0           |  100.0 |    20.00    |
|   19.0           |  251.2 |    50.23    |
|   18.0           |  631.0 |   126.19    |
|   17.0           | 1584.9 |   316.98    |
|   16.0           | 3981.1 |   796.21    |

```python
sn1 = SimSnIa()
sn1.calc_a_prime()
sn1.calc_ft(np.arange(-10,35))
sn1.calc_noisy_lc(sigma_sys=100)

plt.plot(np.arange(-10,35), sn1.model_flux_)
plt.errorbar(np.arange(-10,35), sn1.cnts_, sn1.cnts_unc_, fmt='o')
```

    <IPython.core.display.Javascript object>

    <ErrorbarContainer object of 3 artists>

```python
np.random.seed(42)

fig, ax = plt.subplots(figsize=(10,6))

for i in range(10):
    sn = SimSnIa()
    sn.draw_dist_in_volume(d_max=600)
    dist = 0
    if i == 4:
        dist = 380
        sn.dist_ = dist
    elif i == 7:
        dist = 200
        sn.dist_ = dist
    elif i == 9:
        dist = 400
        sn.dist_ = dist
    elif i == 1:
        dist = 329
        sn.dist_ = dist
    elif i == 6:
        dist = 750
        sn.dist_ = dist

    if dist != 0:
        sn.z_ = 72*sn.dist_/2.9979e5
        sn.mu_ = 5*np.log10(sn.dist_)+25

    sn.draw_alpha_r()
    sn.draw_rise_time()
    sn.draw_smoothing_parameter()
    sn.draw_mb_deltam15()
    sn.calc_alpha_d()
    sn.calc_a_prime()

    if i == 0:
        t_start = np.random.randint(3)
    else:
        t_start += np.random.randint(10)
    t_obs = np.arange(0, 75, 3, dtype=float) + np.random.uniform(-0.25/24,0.25/24,size=25)

    sn.calc_ft(t_obs)
    sn.calc_noisy_lc(sigma_sys=8)

    mag = 25 - 2.5*np.log10(sn.cnts_)
    mag_unc = 1.0857*sn.cnts_unc_/sn.cnts_

    print(np.nanmin(mag))
    if np.nanmin(mag) < 18.7:
        ax.plot((t_obs+t_start)*(1+sn.z_), mag, 'o',
                 c='0.8', mec='k', mew=1, ms=10)
        ax.plot((t_obs+t_start)*(1+sn.z_), mag, '-',
                 lw=0.7, alpha=0.6, zorder=-10)

    else:
        ax.plot((t_obs+t_start)*(1+sn.z_), mag, '+',
                 c='0.2', mew=2, ms=10)
        ax.plot((t_obs+t_start)*(1+sn.z_), mag, '-',
                 lw=0.7, alpha=0.6, zorder=-10)

ax.hlines(18.7, 0, 100, linestyles='--')
ax.set_ylim(21.5, 17)
ax.set_xlim(0,100)
ax.minorticks_on()
ax.tick_params(which='both',top=True, right=True, labelsize=14)
fig.tight_layout()
# fig.savefig("/Users/adamamiller/Desktop/BTS.pdf")
```

    <IPython.core.display.Javascript object>

    19.0228961392684
    18.073529498238475
    19.203951020400595
    18.707302133213854
    18.586685269022183
    18.890464844957492
    20.45735681630653
    17.1900276123825
    19.104823656352064
    19.29701342649765

    /Users/adamamiller/miniconda3/envs/emcee3/lib/python3.7/site-packages/ipykernel_launcher.py:45: RuntimeWarning: invalid value encountered in log10

```python

```


# Computational Astrophysics
## Partial Differential Equations. 06
## Multidimensional Advection Equation

---
## Eduard Larrañaga

Observatorio Astronómico Nacional\
Facultad de Ciencias\
Universidad Nacional de Colombia

---

### About this notebook

In this notebook we present some of the techniques used to solve the linear advection equation.

`A. Garcia. Numerical Methods for Physics. (1999). Chapter 6 - 7 `

---

##  The Linear 1-D Advection Equation

The linear advection equation is

\begin{equation}
\label{eq:advect}
\partial_t a + v \partial_x a = 0
\end{equation}

where $a(t,x)$ is some scalar quantity and $v$ is the constant velocity at
which it is advected ($v > 0$ advects to the right).

The solution to
this equation is to simply take the initial data, $u(t=0,x)$,
and displace it to the right at a speed $v$.  The shape of the initial
data is preserved in the advection. Direct substitution shows that $u(x - vt)$ is a solution to
advection equation for any choice of u. This means that
the solution is constant along the lines $x = v t$
(the curves along which the solution is constant are called the
characteristics).

Many hyperbolic systems of PDEs,
e.g. the equations of hydrodynamics, can be written in a form that
looks like a system of (nonlinear) advection equations, so the
advection equation provides important insight into the methods used
for these systems.

---

## Linear Multidimensional Advection Equation

The two-dimensional linear advection equation for a function $a=a(t,x,y)$ is

\begin{equation}
\partial_t a + u \partial_x a + v \partial_y a = 0
\end{equation}

where $u$ is the velocity in the $x$-direction and $v$ is the velocity in the $y$-direction. We denote the average of $a(t,x,y)$ in a zone $i,j$ as $a_{i,j}$.The index $i$ labels the $x$-direction while $j$ labels the $y$-direction.

---

### Finite Volumes Method

Since $u$ and $v$ are considered as constants, we can
move them inside the derivatives,

\begin{equation}
\partial_t a + \partial_x (u a) + \partial_y (v a) = 0.
\end{equation}

We define the average of $a$ in a zone by integrating it over the
2D-volume

\begin{equation}
a_{i,j} = \frac{1}{\Delta x \Delta y}
   \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}} \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}}
   a(x,y,t) \, dx \, dy .
\end{equation}

Integrating the advection equation over $x$ and $y$, we obtain

\begin{align}
\frac{1}{\Delta x \Delta y}
  \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}}
  \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}} a_t \, dx \, dy =
  &- \frac{1}{\Delta x \Delta y}
       \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}} \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}}
      (u a)_x \, dx \, dy \nonumber \\
  &- \frac{1}{\Delta x \Delta y}
       \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}} \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}}
      (v a)_y \, dx \, dy
\end{align}

Integration of the left hand side gives

\begin{align}
 \frac{\partial a_{i,j}}{\partial t} =
  &- \frac{1}{\Delta x\Delta y} \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}}
     \left \{ (u a)_{i+\frac{1}{2},j} - (u a)_{i-\frac{1}{2},j} \right \} dy \nonumber \\
  &- \frac{1}{\Delta x\Delta y} \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}}
     \left \{ (v a)_{i,j+\frac{1}{2}} - (v a)_{i,j-\frac{1}{2}} \right \} dx
\end{align}

Using a difference approximation for the time derivative we obtain

\begin{align}
 a_{i,j}^{n+1} - a_{i,j}^n =
  &- \frac{1}{\Delta x\Delta y} \int_{t^n}^{t^{n+1}} \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}}
     \left \{ (u a)_{i+\frac{1}{2},j} - (u a)_{i-\frac{1}{2},j} \right \} dy dt \nonumber \\
  &- \frac{1}{\Delta x\Delta y} \int_{t^n}^{t^{n+1}} \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}}
     \left \{ (v a)_{i,j+\frac{1}{2}} - (v a)_{i,j-\frac{1}{2}} \right \} dx dt
\end{align}

The flux through a given interface is defined as the average over the face
of that interface and time,

1. x-face:
\begin{equation}
\langle (ua)_{i+\frac{1}{2},j}\rangle_{(t)} = \frac{1}{\Delta y \Delta t}
    \int_{t^n}^{t^{n+1}} \int_{y_{j-\frac{1}{2}}}^{y_{j+\frac{1}{2}}} (ua)_{i+\frac{1}{2},j}\, dy dt
\end{equation}

2. y-face
\begin{equation}
\langle (va)_{i,j+\frac{1}{2}}\rangle_{(t)} = \frac{1}{\Delta x \Delta t}
    \int_{t^n}^{t^{n+1}} \int_{x_{i-\frac{1}{2}}}^{x_{i+\frac{1}{2}}} (va)_{i,j+\frac{1}{2}}\, dx dt
\end{equation}

where $\langle . \rangle_{(t)}$ denotes the time-average over the face.

Following the description of 1D advection, we replace the average in
time with the flux at the midpoint in time and the average over the face
with the flux at the center of the face,

\begin{equation}
\langle (ua)_{i+\frac{1}{2},j} \rangle_{(t)} \approx (ua)_{i+\frac{1}{2},j}^{n+\frac{1}{2}}
\end{equation}

and therefore,
\begin{equation}
a_{i,j}^{n+1} = a_{i,j}^n - \Delta t \left [
   \frac{(ua)_{i+\frac{1}{2},j}^{n+\frac{1}{2}} - (ua)_{i-\frac{1}{2},j}^{n+\frac{1}{2}}}{\Delta x} +
   \frac{(va)_{i,j+\frac{1}{2}}^{n+\frac{1}{2}} - (va)_{i,j-\frac{1}{2}}^{n+\frac{1}{2}}}{\Delta y} \right ]
\end{equation}

In this advection problem in which $u$ and $v$ are constant, it is only necessary to find the values of $a$ at the interfaces, $a^{n+1/2}_{i\pm 1/2 , j}$ on x-interfaces and $a^{n+1/2}_{i, j \pm 1/2}$ on y-interfaces. There are two methods for computing these interface states: **dimensionally split** and **unsplit** methods.

---
### Dimensional Split

Dimensionally split methods are easier to code, since each dimension is operated on independent of the others. This means that you can use a 1D method for each direction.

Strang splitting is a second-order accurate in time method in which we alternate the order of the dimensional updates each timestep. An update through $\Delta t$ consists of an update in $x$ followed by an update in $y$,

\begin{eqnarray}
 \frac{a_{i,j}^\star - a_{i,j}^n}{\Delta t} &=&
  - \frac{ u a_{i+\frac{1}{2},j}^{n+\frac{1}{2}} - u a_{i-\frac{1}{2},j}^{n+\frac{1}{2}} }{\Delta x} \label{eq:splitx}\\
 \frac{a_{i,j}^{n+1} - a_{i,j}^\star}{\Delta t} &=&
  - \frac{ v a_{i,j+\frac{1}{2}}^{\star,n+\frac{1}{2}} - v a_{i,j-\frac{1}{2}}^{\star,n+\frac{1}{2}} }{\Delta y} \label{eq:splity}
\end{eqnarray}

In order to construct the interface states, it is possible to use the same procedure described for 1D advection, i.e. considering the left and right states at each interface and then, solving the Riemann problem to find the unique state on the interface.

It is important to note that **each dimensional sweep is done without knowledge of the other dimensions**.

```python

```


# Das Solow-Modell

Das Solow Modell ist einfaches Modell, das die langfristige Entwicklung einer geschlossenen Volkswirtschaft beschreibt.

## Produktion und Produktion pro Kopf
Das Solow-Modell basiert auf einer aggregierten Produktionsfunktion $Y$ mit den Inputfaktoren Kapital $K$ und Arbeit $N$:
\begin{align}
Y=F(K,N)
\end{align}
Die aggregierte Produktionsfunktion hat per Annahme folgende Eigenschaften:
* positive, aber fallende Grenzerträge: eine Erhöhung eines einzelnen Inputfaktors führt immer zu mehr Output führt. Dieser Zuwachs wird aber kleiner, je mehr vom von diesem Faktor bereits verwendet wird.
    * mathematisch: $\frac{\partial Y}{\partial K}>0$, $\frac{\partial Y}{\partial N}>0$, $\frac{\partial^2 Y}{\partial K^2}<0$, $\frac{\partial^2 Y}{\partial N^2}<0$
* konstante Skalenerträge: einer Verdoppelung beides Inputfaktoren führt zu einer Verdoppelung der Produktion.
    * mathematisch: $F(x\cdot K,x\cdot N)=x\cdot F(K,N)$

Eine einfache Produktionsfunktion, die beide Eigenschaften erfüllt, ist die Cobb-Douglas Produktionsfunktion:

$$Y=F(K,N)=K^\alpha N ^{1-\alpha}$$

Der Parameter $\alpha\in(0,1)$ ist die Elastizität des Output bezüglich des Kapitals und gibt an, um wie viel Prozent der Output steigt, wenn das Kapital um einen Prozent erhöht wird.

In der Regel betrachten wir die Volkswirtschaft im Solow Modell aber nicht in aggregierten Einheiten sondern in pro-Kopf (bzw. pro-Arbeiter) Einheiten an.
Dazu definieren wir
* Kapital pro Arbeiter: $k=\frac{K}{N}$ und wird als Kapitalintensität bezeichnet
* Output pro Arbeiter: $y=\frac{Y}{N}$

Berechnen wir den Output pro Arbeiter ergibt sich
\begin{align}
y   &=\frac{Y}{N}&\\
    &=\frac{1}{N}F(K,N)                       & \text{konstante Skalenerträge: }F(x\cdot K,x\cdot N)=x\cdot F(K,N) \text{ für } x=\frac{1}{N}\\
    &=F\left(\frac{1}{N}K,\frac{1}{N}N\right) &\\
    &=F(k,1) &
\end{align}

Da das zweite Argument $(1)$ fix ist, schreiben wir der Einfachheit halber $y=F(k,1)=f(k)$.
Der Output pro Arbeiter hängt also nur von der Kapitalintensität ab.
Für das Beispiel der Cobb-Douglas-Funktion mit $F(K,N)=K^{\alpha}N^{1-\alpha}$ ergibt sich

\begin{align}
y=f(k) = F(k,1)= k^\alpha 1^{1-\alpha}=k^\alpha.
\end{align}

Um ein Gefühl für aggregierte Produktionsfunktion und Output pro Kopf zu bekommen, betrachten wir die folgenden Grafiken.
In der linken Grafik wird die aggregierte Produktionsfunktion dargestellt, in der rechten der Output pro Kopf.
Sie können den Parameter $\alpha$ über einen Regler verändern.

```python
%matplotlib inline
from ipywidgets import interactive
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d

from mpl_toolkits.mplot3d import Axes3D

from matplotlib import cm

plt.rcParams['figure.figsize'] = [24/2.54, 18/2.54] # Größe der Grafik anpassen
plt.rcParams['font.size']=12 # Schriftgröße
```

```python
def f(alpha):
    fig = plt.figure(figsize=plt.figaspect(0.5))

    ax = fig.add_subplot(1, 2, 1, projection='3d')

    K=np.linspace(start=0, stop=10) # mögliche Werte für K
    N=np.linspace(start=0, stop=10) # mögliche Werte für N
    K, N = np.meshgrid(K, N)
    Y= np.multiply(K**(alpha) , N**(1-alpha)) # Berechne Output, a**b bedeutet a^b
    y_int = K[1,:]**(alpha)
    cont = ax.plot_wireframe(K, N, Y, rstride=10, cstride=10)
    ax.plot3D(K[1,:],np.ones_like(K[1,:]), y_int,'red')

    ax.set_xlabel('Kapital')
    ax.set_ylabel('Arbeit')
    ax.set_zlabel('Output')
    ax.view_init(15, -135)

    ax = fig.add_subplot(1, 2, 2)
    k = np.geomspace(0.0001, 10, num=50) # mögliche Werte für Kapitalintensität
    y = k**alpha # Output pro Arbeiter

    ax.plot(k, y,'red')
    ax.set_xlabel('Kapitalintensität')
    ax.set_ylabel('Output pro Arbeiter')
    ax.axis(ymin=0, ymax=4)

    plt.show()

interactive_plot = interactive(f, # Name der Funktion, die die Grafik erstellt
                               # Slider für die Inputs der Grafik-Funktion
                               alpha=widgets.FloatSlider(value=0.4, description='$\\alpha$',
                                                            max=1, min=0, step=0.05)
                              )

output = interactive_plot.children[-1]
interactive_plot
```

    interactive(children=(FloatSlider(value=0.4, description='$\\alpha$', max=1.0, step=0.05), Output()), _dom_cla…

Zum Üben gibt es hier einige vertiefende Aufgaben.

### Vertiefende Aufgaben
1. Zeigen Sie, dass die Cobb-Douglas Produktionsfunktion $Y=K^\alpha N^{1-\alpha}$ in der Tat positive und fallende Grenzerträge und konstante Skalenerträge aufweist.
2. Wie verändert sich die (Cobb-Douglas) Produktionsfunktion, wenn $\alpha$ nahe oder bei 0 bzw. 1 liegt? Wie hoch sind die Grenzerträge von Arbeit und Kapital in beiden Fällen? Nutzen Sie dazu die interaktiven Grafiken!
3. Zeigen Sie, das $\alpha$ die Elastizität des Outputs bezüglich des Kapitals ist: $\varepsilon_{Y,K}=\frac{\partial Y}{\partial K}/\frac{Y}{K}=\alpha$.
4. Wie ist die Elastizität des Outputs bezüglich des Kapitals zu interpretieren? Was sagt Ihnen ein Wert von $\alpha=0.4$?

## Veränderung des Kapitalstocks

Bis jetzt haben wir uns angeschaut, wie die Volkswirtschaft in einer einzelnen Periode produziert.
Zu jedem Zeitpunkt können wir die Volkswirtschaft anhand des Kapitalstocks und der Arbeiter beschreiben.
Jetzt wollen wir verschiedene Perioden mit einigen einfachen Regeln für die Entwicklung von Kapitalstock und Arbeitern miteinander verbinden.
Um zu verdeutlichen, von welcher Periode wir sprechen, verwenden wir den Zeitindex $t$, ob $t$ für Monate oder Jahre steht ist nicht wichtig. Unter Verwendung von Zeitindizes lautet

* die aggregierte Produktionsfunktion $Y_t=K_t^\alpha N_t^{1-\alpha}$
* der Output pro Kopf $y_t=k_t^\alpha$

Für den Moment nehmen wir an, dass die Anzahl der Arbeiter konstant ist, also gilt $N_t=N$.
Wir lockern diese Annahme später, für den Einstieg macht sie uns das Leben aber deutlich einfacher.

Wir nehmen an, dass ein fixer Anteil $\delta\in(0,1)$ (auch Abnutzungsrate) des Kapitalstocks jedes Jahr abgenutzt wird, also kaputt geht. Im nächsten Jahr ist also noch ein Anteil von $(1-\delta)$ des alten Kapitalstocks vorhanden.
Wir nehmen an, dass die Volkswirtschaft geschlossen ist.
Die gesamtwirtschaftliche Ersparnis entspricht also den gesamtwirtschaftlichen Investitionen.
Wir nehmen an, dass ein konstanter Anteil $s\in(0,1)$ (auch Sparquote) des Einkommens gespart bzw. investiert wird.
Das bedeutet, dass die Investitionen $I_t=sY_t$ betragen, der Rest des Einkommens wird konsumiert.
Die Änderung des aggregierten Kapitalstocks $\Delta K_{t+1}=K_{t+1}-K_t$ ist also gleich der Investitionen abzüglich der Abnutzung:

\begin{align}
\Delta K_{t+1}=sY_t-\delta K_t
\end{align}

Die Änderung der Kapitalintensität (Kapital pro Arbeiter) können wir berechnen, indem wir durch $N$ teilen:

\begin{align}
\Delta k_{t+1} &=\frac{\Delta K_{t+1}}{N}\\
               &=\frac{sY_t-\delta K_t}{N}\\
               &=s\frac{Y_t}{N}-\delta\frac{K_t}{N}\\
               &=sy_t-\delta k_t
\end{align}

Die Änderung der Kapitalintensität ist also gleich der Investitionen je Arbeiter abzüglich der Abnutzung.
Wenn wir auf beiden Seiten $k_t$ abziehen, bekommen wir eine einfache Gleichung, mit der wir den Kapitalstock der nächsten Periode berechnen können:

\begin{align}
\Delta k_{t+1}=k_{t+1}-k_t&=sy_t-\delta k_t & +k_t\\
        k_{t+1}&=(1-\delta)k_t+sy_t
\end{align}

Wir haben gesehen, dass für den Spezialfall der Cobb-Douglas Produktionsfunktion gilt: $y_t=f(k_t)=k_t^\alpha$.
Damit können wir $k_{t+1}$ genauer beschreiben:

\begin{align}
        k_{t+1}&=(1-\delta)k_t+sk_t^\alpha
\end{align}

Mit dieser einfachen Rechenregel können wir für eine gegebene Kapitalintensität heute $k_t$ die Kapitalintensität morgen $k_{t+1}$ berechnen. Wie sich die Kapitalintensität für verschiedene Startwerte $k_0$ entwickelt, zeigt die folgende Grafik.
Die Parameter für die Outputelastizität des Kapitals $\alpha$, die Abnutzungsrate $\delta$ und die Sparquote $s$ können Sie über Schieberegler verändern.

```python
def f(alpha, delta, savr):
    max_time=50
    t = np.arange(0,max_time,1) # Zeitpunkte
    n_paths=20
    k = np.zeros((n_paths,max_time))

    k[:,0] = np.linspace(start=0, stop=10, num=n_paths) # verschiedene Anfangswerte für Kapitalintensität

    k_star=(savr/delta)**(1/(1-alpha))

    for tt in t[1:max_time]: # loop über alle Zeitpunkte außer 0
        y=k[:,tt-1]**alpha
        k[:,tt] = (1-delta)*k[:,tt-1]+savr*y

    plt.plot(k.T)
    plt.annotate('$k^*$',(max_time-0.5,k_star),)
    plt.ylim(0,10)
    plt.ylabel('Kapitalintensität')
    plt.xlabel('Zeit t')
    plt.show()

interactive_plot = interactive(f, # Name der Funktion, die die Grafik erstellt
                               # Slider für die Inputs der Grafik-Funktion
                               alpha=widgets.FloatSlider(value=0.4, description='$\\alpha$',max=0.95, min=0.05, step=0.05),
                               delta=widgets.FloatSlider(value=0.4, description='$\\delta$',max=0.95, min=0.05, step=0.05),
                               savr=widgets.FloatSlider(value=0.4, description='$s$',max=0.95, min=0.05, step=0.05)
                              )

output = interactive_plot.children[-1]
interactive_plot
```

    interactive(children=(FloatSlider(value=0.4, description='$\\alpha$', max=0.95, min=0.05, step=0.05), FloatSli…

Die Grafik zeigt, dass sich unabhängig die Kapitalintensität unabhängig vom Startwert im Laufe der Zeit gegen einen (bestimmten) langfristigen Wert $k^*$ konvergiert.
Die einzige Ausnahme ist der Spezialfall $k_0=0$. In diesem Fall gibt es zu Beginn gar kein Kapital, wodurch nichts produziert und damit gespart werden kann.
Die Kapitalintensität steckt in diesem Fall für immer bei $k_t=0$ fest.
Für alle anderen Startwerte stellt sich Laufe der Zeit eine bestimmte langfristige Kapitalintensität $k^*$ ein.
Wie wir diese bestimmen können und wie diese von den Parametern abhängt ist das Thema des nächsten Kapitels.

## Steady State
Wir haben gesehen, dass sich im Solow-Modell unabhängig vom aktuellen Kapitalstock $k_t$ langfristig eine Kapitalintensität $k^*$ einstellt. Diesen Wert bezeichnen wir auch als Steady State Kapitalintensität. Das gleiche gilt für den Output pro Kopf, der gegen $y^*$ konvergiert. Im Folgenden wollen wir diese Kapitalintensität bestimmen.
Im Steady State ist die Kapitalintensität konstant, die Änderung also per Definition 0.
\begin{align}
\Delta k_{t+1}=0&= sy^*-\delta k^*         &   y^*=(k^*)^\alpha\\
                &= s(k^*)^\alpha-\delta k^*
\end{align}

In dieser Gleichung gibt es nur eine Unbekannte, nämlich die Steady State Kapitalintensität $k^*$, nach der wir auflösen:

\begin{align}
k^* = \left(\frac{s}{\delta} \right)^\frac{1}{1-\alpha}
\end{align}

Für gegebene Parameter können wir also nun die Steady State Kapitalintensität berechnen.
Inwiefern die Politik diese Parameter beeinflussen kann, und ob es einen "optimalen" Steady State gibt, ist das Thema des nächsten Kapitels.
Um die Inhalte aus diesem Kapitel zu wiederholen, können Sie folgende vertiefende Aufgaben lösen:

### Vertiefende Aufgaben
1. Leiten Sie aus der aggregierten Produktionsfunktion und der Veränderung des aggregierten Kapitalstocks die entsprechende Intensitätsform, also Produktion und Veränderung des Kapitalstocks je Arbeiter, her.
2. Zeigen Sie, dass für die Steady State Kapitalintensität gilt $k^*=\left(\frac{s}{\delta}\right)^{\frac{1}{1-\alpha}}$
3. Nutzen Sie die interaktive Grafik, um herauszufinden, wie Sparrate $s$, Outputelastizität bezüglich des Kapitals $\alpha$ und Abnutzungsrate $\delta$ die langfristige Kapitalintensität $k^*$ beeinflussen.
4. Überprüfen Sie Ihre Ergebnisse, indem Sie zeigen, dass die ersten Ableitungen der langfristigen Kapitalintensität wie folgt nach den Parametern wie folgt lauten:
    1. Ableitung nach der Sparquote $s$: $$\frac{\partial k^*}{\partial s}=\frac{1}{s}\cdot\frac{1}{1-\alpha}k^*>0$$
    2. Ableitung nach der Abnutzungsrate $\delta$: $$\frac{\partial k^*}{\partial \delta}=-\frac{1}{\delta}\cdot\frac{1}{1-\alpha}\cdot k^*<0$$
    3. Ableitung nach der Outputelastizität bezüglich des Kapitals $\alpha$: $$\frac{\partial k^*}{\partial \alpha}=\ln\left(\frac{s}{\delta}\right)\cdot \frac{1}{(1-\alpha)^2}\cdot k^*$$
5. Unter welcher Bedingung ist die Ableitung $\frac{\partial k^*}{\partial \alpha}$ positiv bzw. negativ? Warum?

## Die Goldene Regel
Im letzten Kapitel haben wir die Steady State Kapitalintensität hergeleitet:
$k^* = \left(\frac{s}{\delta} \right)^\frac{1}{1-\alpha}$.
Per Definition ergeben sich damit
1. der Output pro Arbeiter im Steady State $y^*=f(k^*)=\left(\left(\frac{s}{\delta}\right)^{\frac{1}{1-\alpha}}\right)^{\alpha}=\left(\frac{s}{\delta}\right)^{\frac{\alpha}{1-\alpha}}$ und
2. Kosum pro Arbeiter im Steady State $c^*=(1-s)\cdot y^*=(1-s)\cdot \left(\frac{s}{\delta}\right)^{\frac{\alpha}{1-\alpha}}$

Der Steady Konsum pro Arbeiter hängt also nur von den Parametern (und nicht dem ursprünglichen Kapitalstock $k_0$) ab.

Von den Parametern ist an dieser Stelle die Sparquote von besonderem Interesse, da sie (per Annahme) durch Politikmaßnahmen,
z.B. Sparanreizen, beeinflusst werden kann.
Daher stellt sich die Frage, ob es eine optimale Sparquote gibt, die den Steady State Konsum pro Kopf maximiert.
Mathematisch handelt es sich bei dieser Sparquote um die Lösung für das Optimierungsproblem
$$\max_s c^*=(1-s)\cdot \left(\frac{s}{\delta}\right)^{\frac{\alpha}{1-\alpha}}$$
Wir können uns dieses Optimierungsproblem auch grafisch veranschaulichen.
Dargestellt sind Output pro Kopf $y_t=k_t^{\alpha}$, Abnutzung pro Kopf $\delta k_t$ und Ersparnis pro Kopf $sk_t^\alpha$
Der Konsum im Steady State ist in rot eingetragen und ist gleich der Differenz aus Output pro Kopf und Ersparnis pro Kopf im Steady State.
Für den Moment ist nur der Regler für die Sparquote interessant. Verändern Sie diesen, wird der Steady State Konsum berechnet. Wenn Sie hier $s=\alpha$ einstellen, wird der Steady State Konsum maximal und entspricht der Goldenen Regel.

```python
def f(alpha, delta, sav_r): # was soll mit den Werten aus den Widgets passieren
    plt.figure(2)

    k = np.geomspace(0.0001, 100, num=100) # Kapitalintensität

    output = k**(alpha) # Output/Kopf k^alpha
    depreciation = k * delta # Abschreibung/Abnutzung
    saving = output * sav_r # Ersparnis

    # Steady State
    ss_k = (sav_r/delta)**(1/(1-alpha)) # Kapitalintensität
    ss_y = ss_k ** alpha # Output
    ss_s = ss_y * sav_r # Ersparnis

    # Golden Rule
    gr_s=alpha # Sparquote nach der goldenen Regel
    gr_k=(gr_s/delta)**(1/(1-alpha)) # Kapitalintensität nach der goldenen Regel
    gr_y=gr_k**alpha # Output nach der goldenen Regel
    gr_s=gr_s*gr_y # Ersparnisse/Investitionen nach der goldenen Regel

    plt.plot([ss_k,ss_k],[ss_y, ss_s],marker='o', markersize=1, color="red") # zeichne Konsum im Steady State ein
    plt.annotate('Steady State Konsum',(ss_k+1,0.5*(ss_y+ss_s)),)

    plt.plot([gr_k,gr_k],[gr_y, gr_s],marker='o', markersize=1, color="black") # zeichne Konsum im Steady State ein
    plt.annotate('Goldene Regel Konsum',(gr_k+1,gr_s-0.125),)

    outp, = plt.plot(k, output, label='Output')
    abnu, = plt.plot(k, depreciation, label='Abnutzung')
    savi, = plt.plot(k, saving, label='Ersparnis')

    plt.ylim(0, 5)
    plt.xlim(k[0],k[-1])
    plt.xlabel('Kapitalintensität')
    plt.ylabel('Euro pro Kopf')
    plt.legend(handles=[outp, abnu, savi], bbox_to_anchor=(0,1.02,1,0.2), loc="lower left",mode="expand",ncol=3,frameon=False)
#    plt.grid()
    plt.show()

interactive_plot = interactive(f, # Name der Funktion, die die Grafik erstellt
                               # Slider für die Inputs der Grafik-Funktion
                               alpha=widgets.FloatSlider(value=0.3, description='$\\alpha$', max=0.4, min=0, step=0.01),
                               delta=widgets.FloatSlider(value=0.05, description='$\\delta$', max=0.1, min=0.01, step=0.01),
                               sav_r=widgets.FloatSlider(value=0.5, description='$s$', max=1, min=0, step=0.05),
                              )

output = interactive_plot.children[-1]
#output.layout.height = '350px'
interactive_plot
```

    interactive(children=(FloatSlider(value=0.3, description='$\\alpha$', max=0.4, step=0.01), FloatSlider(value=0…

Für jede Parameterkombination ist der Steady State Konsum die Strecke zwischen Steady State Investition/Abnutzung und Output.
Die schwarze Strecke ist der Steady State Konsum nach der Goldenen Regel, also der maximale Steady State Konsum, der durch Variation der Sparrate erreicht werden kann. Es lässt sich zeigen, dass für diese Sparquote gilt
$$s_g=\alpha.$$

## Vertiefende Aufgaben
1. Lösen Sie das Optimierungsproblem $\max_s c^*=(1-s)\cdot\left(\frac{s}{\delta}\right)^{\frac{\alpha}{1-\alpha}}$ und zeigen Sie, dass $s_{g}=\alpha$.
2. Bestimmen Sie die Steady State Kapitalintensität, den Output und den Konsum nach der Goldenen Regel.

```python
%load_ext watermark

# python, ipython, packages, and machine characteristics
%watermark -v -m -p ipywidgets,matplotlib,numpy,watermark

# date
print (" ")
%watermark -u -n -t -z
```

    Python implementation: CPython
    Python version       : 3.7.4
    IPython version      : 7.8.0

    ipywidgets  : 7.5.1
    matplotlib  : 3.1.1
    numpy       : 1.16.5
    mpl_toolkits: unknown
    watermark   : 2.1.0

    Compiler    : MSC v.1915 64 bit (AMD64)
    OS          : Windows
    Release     : 10
    Machine     : AMD64
    Processor   : Intel64 Family 6 Model 142 Stepping 12, GenuineIntel
    CPU cores   : 8
    Architecture: 64bit

    Last updated: Sun Jan 17 2021 15:28:02Mitteleuropäische Zeit

```python

```