# Section 5: Modelling & Simulation

---

# Table of Contents

* [System Requirements (Part 1)](#System-Requirements)
  * [Model Introduction](#Model-Introduction)
  * [Requirements Analysis](#Requirements-Analysis)
  * [Visual System Mapping: Entity Relationship Diagram](#Visual-System-Mapping:-Entity-Relationship-Diagram)
  * [Visual System Mapping: Stock & Flow Diagram](#Visual-System-Mapping:-Stock-&-Flow-Diagram)
  * [Mathematical Specification](#Mathematical-Specification)
* [System Design (Part 2)](#System-Design)
  * [Differential Specification](#Differential-Specification)
  * [cadCAD Standard Notebook Layout](#cadCAD-Standard-Notebook-Layout)
    0. [Dependencies](#0.-Dependencies)
    1. [State Variables](#1.-State-Variables)
    2. [System Parameters](#2.-System-Parameters)
    3. [Policy Functions](#3.-Policy-Functions)
    4. [State Update Functions](#4.-State-Update-Functions)
    5. [Partial State Update Blocks](#5.-Partial-State-Update-Blocks)
    6. [Configuration](#6.-Configuration)
    7. [Execution](#7.-Execution)
    8. [Simulation Output Preparation](#8.-Simulation-Output-Preparation)
    9. [Simulation Analysis](#9.-Simulation-Analysis)
* [System Validation (Part 3)](#System-Validation)
    * [Policy Functions](#Policy-Functions)
    * [Model Improvements](#Model-Improvements)
      * [Differential Specification Updates](#Differential-Specification-Updates)
      * [Mathematical Specification Updates](#Mathematical-Specification-Updates)
    * [Model Limitations](#Model-Limitations)

---

# System Requirements

## Model Introduction

> Ecosystem: a biological community of interacting organisms and their physical environment.

## Requirements Analysis

[Link to Simulation Analysis](#9.-Simulation-Analysis)

Illustrative real-world model applications:
* Forecast animal food consumption, to determine the sustainability of a farming operation, and to plan for worst-case scenarios.
* Given a food supply of a number of standard crops, of varying cost, how do we optimize economic performance of the farming operation?
* Given the ecological impact of a certain crop on the fertility of the soil, how do we balance the economic performance and ecological sustainability of the farming operation?

### Questions

1. How long will our model ecosystem be able to sustain itself for?
2. What population size can our model ecosystem support?

### Assumptions

1. The population will increase over time.
2. The food supply will decrease over time.
3. There is some relationship between the population and food supply.

### Constraints / Scope

* The intention of this toy model is to allow us to learn about cadCAD, the modelling process, simulation configuration, and the engineering design process!

## Visual System Mapping: Entity Relationship Diagram

## Visual System Mapping: Stock & Flow Diagram

## Mathematical Specification

> ...differential equations play a prominent role in many disciplines including engineering, physics, economics, and biology.

### Differential Equations
* A population consumes a food source, and reproduces at a rate proportional to the food source.
* The food source is consumed at a rate proportional to the population.

\begin{align}
\large population_t &\large= population_{t-1} + {\Delta population} \quad \textrm{(sheep)} \tag{1} \\
\large food_t &\large= food_{t-1} + {\Delta food} \quad \textrm{(tons of grass)} \tag{2} \\
\end{align}

where the rate of change ($\Delta$) is:
\begin{align}
\large {\Delta population} &\large= \alpha * food_{t-1} \quad \textrm{(sheep/month)}  \\
\large {\Delta food} &\large= -\beta * population_{t-1} \quad \textrm{(tons of grass/month)}
\end{align}

# System Design

## Differential Specification

## cadCAD Standard Notebook Layout

# 0. Dependencies

```python
# Standard libraries: https://docs.python.org/3/library/
import math

# Analysis and plotting modules
import pandas as pd
# import plotly
```

```python
# cadCAD configuration modules
from cadCAD.configuration.utils import config_sim
from cadCAD.configuration import Experiment

# cadCAD simulation engine modules
from cadCAD.engine import ExecutionMode, ExecutionContext
from cadCAD.engine import Executor
```

# 1. State Variables

> A state variable is one of the set of variables that are used to describe the mathematical "state" of a dynamical system. ([Wikipedia](https://en.wikipedia.org/wiki/State_variable))

```python
initial_state = {
    'population': 50, # number of sheep
    'food': 1000 # tons of grass
}
initial_state
```

## **Time** as a system state

* 1 **timestep** == 1 month

# 2. System Parameters

> System parameterization is the process of choosing variables that impact the behaviour of the model. These parameters allow us to perform simulation techniques like parameter sweeps, Monte Carlo simulations, A/B tests, and see how the system behaves under a different model parameter set.

[Link to Simulation Analysis](#9.-Simulation-Analysis)

```python
system_params = {
    'reproduction_rate': [0.01], # sheep per month
    'consumption_rate': [0.1], # tons of grass per month
}
system_params
```

# 3. Policy Functions

> A Policy Function computes one or more signals to be passed to State Update Functions. They describe the logic  and behaviour of a system component  or mechanism.

We'll cover this in the next section!

# 4. State Update Functions

> We create State Update Functions to design the way our model state changes over time. These will usually represent the system differential specification.

```python
def state_update_function(params, substep, state_history, previous_state, policy_input):
    variable_value = 0
    return 'variable_name', variable_value
```

* `params` is a Python dictionary containing the **system parameters**
* `substep` is an integer value representing a step within a single `timestep`
* `state_history` is a Python list of all previous states
* `previous_state` is a Python dictionary that defines what the state of the system was at the **previous timestep** or **substep**
* `policy_input` is a Python dictionary of signals or actions from **policy functions**

```python
def new_population(current_population, alpha, food_supply):
    """
    The population state after one timestep, according to the differential equation (1):
    current_population + alpha * food_supply
    """
    return math.ceil(current_population + alpha * food_supply)
```

```python
math.ceil(5.5)
```

```python
# Relevant state variables
current_population = initial_state['population']
food_supply = initial_state['food']

# Relevant parameters
reproduction_rate = system_params['reproduction_rate'][0] # "alpha" in our differential equation

new_population(current_population, reproduction_rate, food_supply)
```

```python
def s_population(params, substep, state_history, previous_state, policy_input):
    """
    Update the population state according to the differential equation (1):
    current_population + alpha * food_supply
    """
    population = previous_state['population']
    alpha = params['reproduction_rate']
    food_supply = previous_state['food']

    return 'population', max(new_population(population, alpha, food_supply), 0)
```

```python
population = 60
print("A tuple!")
'population', max(math.ceil(population), 0)
```

```python
next_state = {
    # current_population + alpha * food_supply
    'population': math.ceil(50 + 0.01 * 1000),
    'food': 1000
}
next_state
```

```python
def s_food(params, substep, state_history, previous_state, policy_input):
    """
    Update the food supply state according to the differential equation (2):
    food supply - beta * population
    """
    food = previous_state['food'] - params['consumption_rate'] * previous_state['population']
    return 'food', max(food, 0)
```

```python
max(-10, 0)
```

# 5. Partial State Update Blocks
## Tying it all together

> A series of Partial State Update Blocks is a structure for composing State Update Functions and Policy Functions in series or parallel, as a representation of the system model.

**Updates run in series**

```python
partial_state_update_blocks = [
    # Run first
    {
        'policies': {}, # Ignore for now
        # State variables
        'variables': {
            'population': s_population
        }
    },
    # Run second
    {
        'policies': {}, # Ignore for now
        # State variables
        'variables': {
            'food': s_food
        }
    }
]
```

**Updates run in parallel**

```python
partial_state_update_blocks = [
    {
        'policies': {}, # Ignore for now
        # State variables
        'variables': {
            # Updated in parallel
            'population': s_population,
            'food': s_food
        }
    }
]
```

# 6. Configuration

> The configuration stage is about tying all the previous model components together and choosing how the simulation should run.

Configuration parameters:
* `'N': 1` - the number of times we'll run the simulation (you'll see them called "Monte Carlo runs" later in the course, when we look at tools to analyze system models)
* `'T': range(400)` - the number of timesteps the simulation will run for
* `'M': system_params` - the parameters of the system

```python
sim_config = config_sim({
    "N": 1,
    "T": range(400),
    "M": system_params
})
```

```python
range(400)
```

```python
list(range(400))[0:10]
```

```python
from cadCAD import configs
del configs[:] # Clear any prior configs
```

```python
experiment = Experiment()
experiment.append_configs(
    initial_state = initial_state,
    partial_state_update_blocks = partial_state_update_blocks,
    sim_configs = sim_config
)
configs[-1].__dict__
```

# 7. Execution

> The Execution Engine takes a model and configuration, and computes the simulation output.

## Configuring the cadCAD simulation execution

```python
exec_context = ExecutionContext()
```

```python
simulation = Executor(exec_context=exec_context, configs=configs)
```

## Time to simulate our ecosystem model!

```python
raw_result, tensor_field, sessions = simulation.execute()
```

# 8. Simulation Output Preparation
> The simulation results are returned as a list of Python dictionaries, which we then convert to a Pandas dataframe. At this stage of the process you'll manipulate and analyze your results to answer questions about your model.

```python
simulation_result = pd.DataFrame(raw_result)
```

```python
raw_result[:5]
```

```python
simulation_result.head()
```

# 9. Simulation Analysis

[Link to System Requirements](#Requirements-Analysis)

```python
pd.options.plotting.backend = "plotly"
```

After plotting the results, let's go and update the parameters, and then select `Cell` and `Run All Above`:

[Link to System Parameters](#2.-System-Parameters)

```python
simulation_result.plot(
    kind='line',
    x='timestep',
    y=['population','food']
)
```

```python
pd.set_option('display.max_rows', len(simulation_result))
display(simulation_result)
pd.reset_option('display.max_rows')
```

```python
# https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
simulation_result.query('food == 0').head()
```

# System Validation

## Policy Functions

An illustrative example:

```python
condition = True

def policy_function(params, substep, state_history, previous_state):
    '''
    This logic belongs in the policy function,
    but could also have been placed directly in the state update function.
    '''
    signal_value = 1 if condition else 0
    return {'signal_name': signal_value}
```

```python
def state_update_function(params, substep, state_history, previous_state, policy_input):
    state_value = policy_input['signal_name']
    return 'state_name', state_value
```

### Policy Aggregation

## Model Improvements

### Differential Specification Updates

State update functions `s_population()` and `s_food()` from the last part:

```python
def s_population(params, substep, state_history, previous_state, policy_input):
    population = previous_state['population'] + params['reproduction_rate'] * previous_state['food']
    return 'population', max(math.ceil(population), 0)

def s_food(params, substep, state_history, previous_state, policy_input):
    food = previous_state['food'] - params['consumption_rate'] * previous_state['population']
    return 'food', max(food, 0)
```

Adapting to use **policy functions** to drive the process, and **state update functions** to update the state according to the **differential specification**:

```python
def p_reproduction(params, substep, state_history, previous_state):
    population_reproduction = params['reproduction_rate'] * previous_state['food']
    return {'delta_population': population_reproduction}

def p_consumption(params, substep, state_history, previous_state):
    food_consumption = params['consumption_rate'] * previous_state['population']
    return {'delta_food': -food_consumption}
```

```python
def s_population(params, substep, state_history, previous_state, policy_input):
    population = previous_state['population'] + policy_input['delta_population']
    return 'population', max(math.ceil(population), 0)

def s_food(params, substep, state_history, previous_state, policy_input):
    food = previous_state['food'] + policy_input['delta_food']
    return 'food', max(food, 0)
```

### Mathematical Specification Updates

\begin{align}
\large population_t &\large= population_{t-1} + {\Delta population} \quad \textrm{(sheep)} \tag{1} \\
\large food_t &\large= food_{t-1} + {\Delta food} \quad \textrm{(tons of grass)} \tag{2}
\end{align}

where the rate of change ($\Delta$) is:
\begin{align}
\large {\Delta population} &\large= \alpha * food_{t-1} \quad \textrm{(sheep/month)} \\
\large {\Delta food} &\large= -\beta * population_{t-1} + \gamma \quad \textrm{(tons of grass/month)}
\end{align}

where:

$
\begin{align}
\alpha: \quad &\textrm{'reproduction_rate'}\\
\beta: \quad &\textrm{'consumption_rate'}\\
\gamma: \quad &\textrm{'growth_rate'}
\end{align}
$

* A population consumes a food source, and reproduces at a rate proportional to the food source $\alpha$ (alpha).
* The food source is consumed at a rate proportional to the population $\beta$ (beta), and grows at a constant rate $\gamma$ (gamma).

```python
initial_state = {
    'population': 50, # number of sheep
    'food': 1000 # tons of grass
}

system_params = {
    'reproduction_rate': [0.01], # number of sheep / month
    'consumption_rate': [0.01], # tons of grass / month
    'growth_rate': [10.0], # tons of grass / month
}
```

```python
from collections import Counter
```

```python
A = Counter({'delta_food': 5, 'delta_population': 10})
B = Counter({'delta_food': 5})
A + B
```

```python
A = Counter({'delta_food': 5, 'delta_population': 10})
B = Counter({'delta_food': -2})
A + B
```

```python
def p_growth(params, substep, state_history, previous_state):
    delta_food = params['growth_rate']
    return {'delta_food': delta_food}
```

```python
partial_state_update_blocks = [
    {
        'policies': {
            'reproduction': p_reproduction,
            'consumption': p_consumption, # Signal: `delta_food`
            'growth': p_growth # Signal: `delta_food`
        },
        'variables': {
            'population': s_population,
            'food': s_food # Receives policy_input of (consumption + growth) as `delta_food`
        }
    }
]
```

```python
del configs[:]

sim_config = config_sim({
    'N': 1,
    'T': range(400),
    'M': system_params
})

experiment.append_configs(
    initial_state = initial_state,
    partial_state_update_blocks = partial_state_update_blocks,
    sim_configs = sim_config
)
```

```python
exec_context = ExecutionContext()

simulation = Executor(exec_context=exec_context, configs=configs)
raw_result, tensor_field, sessions = simulation.execute()
```

```python
simulation_result = pd.DataFrame(raw_result)
simulation_result
```

```python
df = simulation_result.copy()
df = df[df.simulation == 0]
df
```

```python
df.plot(kind='line', x='timestep', y=['population','food'])
```

```python
df = df[['population', 'food']]
df.head()
```

```python
df.pct_change()
```

```python
diff = df.diff()
diff
```

```python
diff = diff.query('food <= 0')
diff
```

```python
df.iloc[75]
```

## Model Limitations

1. The population never dies.
2. The system reaches a steady state of no population or food supply change.

#### Addition of a population death rate, "epsilon" / $\epsilon$, that's dependent on the population size:

\begin{align}
\large population_t &\large= population_{t-1} + {\Delta population} \quad \textrm{(sheep)} \tag{1} \\
\large food_t &\large= food_{t-1} + {\Delta food} \quad \textrm{(tons of grass)} \tag{2}
\end{align}

where the rate of change ($\Delta$) is:
\begin{align}
\large {\Delta population} &\large= \alpha * food_{t-1} - \epsilon * population_{t-1} \quad \textrm{(sheep/month)} \\
\large {\Delta food} &\large= -\beta * population_{t-1} + \gamma \quad \textrm{(tons of grass/month)}
\end{align}

where:

$
\begin{align}
\alpha: \quad &\textrm{'reproduction_rate'}\\
\epsilon: \quad &\textrm{'death_rate'}\\
\beta: \quad &\textrm{'consumption_rate'}\\
\gamma: \quad &\textrm{'growth_rate'}\\
\end{align}
$

* A population consumes a food source, and reproduces at a rate proportional to the food source $\alpha$ (alpha), and dies at a rate proportional to the population size $\epsilon$ (epsilon).
* The food source is consumed at a rate proportional to the population $\beta$ (beta), and grows at a constant rate $\gamma$ (gamma).

```python
def p_death(params, substep, state_history, previous_state):
    population_death = params['death_rate'] * previous_state['population']
    return {'delta_population': -population_death}
```

```python
initial_state = {
    'population': 50, # number of sheep
    'food': 1000 # tons of grass
}

system_params = {
    'reproduction_rate': [0.01],
    'death_rate': [0.01],
    'consumption_rate': [0.01],
    'growth_rate': [10.0],
}
```

```python
partial_state_update_blocks = [
    {
        'policies': {
            'reproduction': p_reproduction,
            'death': p_death,
            'consumption': p_consumption,
            'growth': p_growth
        },
        'variables': {
            'population': s_population,
            'food': s_food
        }
    }
]
```

```python
sim_config = config_sim({
    'N': 1,
    'T': range(1000),
    'M': system_params
})

experiment.append_configs(
    initial_state = initial_state,
    partial_state_update_blocks = partial_state_update_blocks,
    sim_configs = sim_config
)
```

```python
exec_context = ExecutionContext()

simulation = Executor(exec_context=exec_context, configs=configs)
raw_result, tensor_field, sessions = simulation.execute()
```

```python
simulation_result = pd.DataFrame(raw_result)
```

```python
df = simulation_result.copy()
df = df[df.simulation == 1]
df
```

```python
df.plot(kind='line', x='timestep', y=['population','food'])
```

# Well done!


# KVLCC2 Model tests vs. Ikeda and SI

# Purpose
Comparison between damping coefficients (with and without speed) from model tests and predictions with Ikeda and Simplified Ikeda (SI).

# Methodology
Quickly describe assumptions and processing steps.

# WIP - improvements
(WORK IN PROGRESS)
Use this section only if the notebook is not final.

Notable TODOs:
* todo 1
* todo 2
* todo 3

## Results
Describe and comment the most important results.

# Suggested next steps
State suggested next steps, based on results obtained in this notebook.

# Setup

```python
# %load imports.py
"""
These is the standard setup for the notebooks.
"""

%matplotlib inline
%load_ext autoreload
%autoreload 2

from jupyterthemes import jtplot
jtplot.style(theme='onedork', context='notebook', ticks=True, grid=False)

import pandas as pd
pd.options.display.max_rows = 999
pd.options.display.max_columns = 999
pd.set_option("display.max_columns", None)
import numpy as np
import os
import matplotlib.pyplot as plt
from collections import OrderedDict
#plt.style.use('paper')

#import data
import copy
from mdldb.run import Run

from sklearn.pipeline import Pipeline
from rolldecayestimators.transformers import CutTransformer, LowpassFilterDerivatorTransformer, ScaleFactorTransformer, OffsetTransformer
from rolldecayestimators.direct_estimator_cubic import EstimatorQuadraticB, EstimatorCubic
from rolldecayestimators.ikeda_estimator import IkedaQuadraticEstimator
import rolldecayestimators.equations as equations
import rolldecayestimators.lambdas as lambdas
from rolldecayestimators.substitute_dynamic_symbols import lambdify
import rolldecayestimators.symbols as symbols
import sympy as sp

from sympy.physics.vector.printing import vpprint, vlatex
from IPython.display import display, Math, Latex

from sklearn.metrics import r2_score
from src.data import database
from mdldb import tables

```

    Duplicate key in file WindowsPath('C:/Users/maa/.matplotlib/stylelib/paper.mplstyle'), line 461 ('figure.figsize   : 5, 3   ## figure size in inches')
    Duplicate key in file WindowsPath('C:/Users/maa/.matplotlib/stylelib/paper.mplstyle'), line 462 ('figure.dpi       : 100        ## figure dots per inch')

```python
import pyscores2
import pyscores2.runScores2
import pyscores2.xml_hydrostatics
from pyscores2.output import OutputFile
from rolldecayestimators.ikeda import Ikeda, IkedaR

from rolldecayestimators.simplified_ikeda_class import SimplifiedIkeda, SimplifiedIkedaABS
from rolldecayestimators.simplified_ikeda import limits_kawahara
from pyscores2.runScores2 import Calculation
from pyscores2.indata import Indata
import joblib
from scipy.optimize import least_squares
from reports.paper_writing import save_fig
```

```python
db = database.get_db()
```

```python
sql = """
SELECT * from run
INNER JOIN loading_conditions
ON (run.loading_condition_id = loading_conditions.id)
INNER JOIN models
ON (run.model_number = models.model_number)
INNER JOIN ships
ON (run.ship_name = ships.name)
WHERE run.model_number='M5057-01-A' and run.test_type='roll decay' and run.project_number=40178362;
"""
df_rolldecays = pd.read_sql(sql=sql, con=db.engine)
df_rolldecays=df_rolldecays.loc[:,~df_rolldecays.columns.duplicated()]
df_rolldecays.set_index('id', inplace=True)

df_rolldecays['ship_speed'].fillna(0, inplace=True)

```

```python
df_rolldecays.head()
```

      project_number
      series_number
      run_number
      test_number
      model_number
      ship_name
      loading_condition_id
      ascii_name
      ship_speed
      comment
      file_path_ascii
      file_path_ascii_temp
      file_path_log
      file_path_hdf5
      date
      test_type
      facility
      angle1
      angle2
      Körfallstyp
      name
      lcg
      kg
      gm
      CW
      TF
      TA
      BWL
      KXX
      KZZ
      BTT1
      CP
      Volume
      A0
      RH
      scale_factor
      lpp
      beam
      ABULB
      BKX
      TWIN
      DCLR
      VDES
      RHBL
      ASKEG
      PD
      ARH
      CFP
      AIX
      PDTDES
      RTYPE
      SFP
      BKL
      BKB
      PROT
      D
      LSKEG
      RR
      XSKEG
      NDES
      AR
      BR
      BRA
      IRUD
      PTYPE
      XRUD
      AI
      HSKEG
      RSKEG
      LOA
      ship_type_id

      id

      21337
      40178362
      1
      94
      1
      M5057-01-A
      M5057-01-A
      166
      94.0
      0.0
      Roll decay, 0 kn
      NaN
      None
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      2018-04-03
      roll decay
      MDL
      None
      None
      None
      20.8
      11.2672
      18.6
      5.73
      None
      20.8
      20.8
      None
      23.2
      80.0
      None
      None
      312653.0
      0.99538
      None
      68.0
      320.0
      58.0
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None

      21338
      40178362
      1
      95
      1
      M5057-01-A
      M5057-01-A
      166
      95.0
      0.0
      Roll decay, 0 kn
      NaN
      None
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      2018-04-03
      roll decay
      MDL
      None
      None
      None
      20.8
      11.2672
      18.6
      5.73
      None
      20.8
      20.8
      None
      23.2
      80.0
      None
      None
      312653.0
      0.99538
      None
      68.0
      320.0
      58.0
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None

      21339
      40178362
      1
      96
      1
      M5057-01-A
      M5057-01-A
      166
      96.0
      0.0
      Roll decay, 0 kn
      NaN
      None
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      2018-11-28
      roll decay
      MDL
      None
      None
      None
      20.8
      11.2672
      18.6
      5.73
      None
      20.8
      20.8
      None
      23.2
      80.0
      None
      None
      312653.0
      0.99538
      None
      68.0
      320.0
      58.0
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None

      21340
      40178362
      1
      97
      1
      M5057-01-A
      M5057-01-A
      166
      97.0
      15.5
      Roll decay, 15.5 kn
      NaN
      None
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      \\sspa.local\gbg\LABmeasuredataMDL\40178362\00...
      2018-04-04
      roll decay
      MDL
      None
      None
      None
      20.8
      11.2672
      18.6
      5.73
      None
      20.8
      20.8
      None
      23.2
      80.0
      None
      None
      312653.0
      0.99538
      None
      68.0
      320.0
      58.0
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None
      None

```python
def calculate_ikeda(ikeda):

    output = pd.DataFrame()
    output['B_44_hat']   = ikeda.calculate_B44()
    output['B_W0_hat']   = float(ikeda.calculate_B_W0())
    output['B_W_hat']    = float(ikeda.calculate_B_W())
    output['B_F_hat']    = ikeda.calculate_B_F()
    output['B_E_hat']    = ikeda.calculate_B_E()
    output['B_BK_hat']   = ikeda.calculate_B_BK()
    output['B_L_hat']    = float(ikeda.calculate_B_L())
    output['Bw_div_Bw0'] = float(ikeda.calculate_Bw_div_Bw0())
    return output
```

```python
#df_rolldecays=df_rolldecays.loc[[21337,21338,21340,]].copy()
df_rolldecays=df_rolldecays.loc[[21338,21340,]].copy()
```

```python
resources={
    21338 : {
        'scores_indata_path':'../models/KVLCC2_speed.IN',
        'scores_outdata_path':'../data/interim/KVLCC2_speed.out',
        'roll_decay_model':'../models/KVLCC2_0_speed.pkl',
    },
    21340 : {
        'scores_indata_path':'../models/KVLCC2_speed.IN',
        'scores_outdata_path':'../data/interim/KVLCC2_speed.out',
        'roll_decay_model':'../models/KVLCC2_speed.pkl',
    }
}
```

```python
g=9.81
rho=1000

phi_as = np.deg2rad(np.linspace(0,10,30))
results = pd.DataFrame()

for id, row in df_rolldecays.iterrows():
    run = db.session.query(Run).get(int(row.name))
    run = database.load_run(run, save_as_example=False, prefer_hdf5=True)
    scale_factor = run.model.scale_factor

    resource = resources[id]

    ## Load ScoresII results
    indata = Indata()
    indata.open(indataPath='../models/KVLCC2_speed.IN')
    output_file = OutputFile(filePath='../data/interim/KVLCC2_speed.out')

    ## Compare with model test
    model = joblib.load(resource['roll_decay_model'])
    estimator = model['estimator']

    ## Non. Lin. linear equivalent damping
    GM = run.loading_condition.gm/scale_factor
    volume = run.loading_condition.Volume/(scale_factor**3)
    GM = run.loading_condition.gm/scale_factor
    beam = run.ship.beam/scale_factor

    meta_data = {
        'Volume':volume,
        'GM':GM,
        'rho':rho,
        'g':g,
    }
    parameters = estimator.result_for_database(meta_data = meta_data)
    B_e = lambdas.B_e_lambda_cubic(B_1=parameters['B_1'], B_2=parameters['B_2'], B_3=parameters['B_3'],
                                   omega0=parameters['omega0'], phi_a=phi_as)
    B_e_hat = lambdas.B_hat_lambda(B=B_e, Disp=volume, beam=beam, g=g, rho=rho)

    ## Run Ikeda
    w = parameters['omega0']
    scale_factor=run.model.scale_factor
    V = row.ship_speed*1.852/3.6/np.sqrt(scale_factor)

    if not run.ship.BKL:
        BKL=0
    else:
        BKL=run.ship.BKL/scale_factor

    if not run.ship.BKB:
        BKB = 0
    else:
        BKB=run.ship.BKB/scale_factor

    kg = run.loading_condition.kg/scale_factor

    fi_as = np.deg2rad(10)

    BKL_ = BKL*np.ones(len(phi_as))
    BKB_ = BKB*np.ones(len(phi_as))

    ikeda = IkedaR.load_scoresII(V=V, w=w, fi_a=phi_as, indata=indata, output_file=output_file,
                                scale_factor=scale_factor, BKL=BKL_, BKB=BKB_, kg=kg)

    #R = 0.15*run.ship.beam/scale_factor  # Just guessing...
    #ikeda.R = R

    df_ikeda = calculate_ikeda(ikeda=ikeda)
    df_ikeda['phi_as']=phi_as
    df_ikeda.set_index('phi_as',inplace=True)
    df_ikeda['phi_as_deg']=np.rad2deg(phi_as)
    df_ikeda['B_e_hat_model_test']=B_e_hat
    df_ikeda['B_e_hat_model_test']=df_ikeda['B_e_hat_model_test'].astype('float')
    df_ikeda['id']=id
    results=results.append(df_ikeda)

```

    c:\dev\evaluation\signal_lab\mdl_to_evaluation.py:106: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
      df_.units = units
    c:\python36-64\lib\re.py:212: FutureWarning: split() requires a non-empty pattern match.
      return _compile(pattern, flags).split(string, maxsplit)
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:283: RuntimeWarning: divide by zero encountered in true_divide
      Cf = 1.328*sqrt(2*pi*visc/(3.22*r_f**2*fi_a**2*w))
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:586: RuntimeWarning: invalid value encountered in sqrt
      gamma=sqrt(pi)*f3*(rmax+2*M/H*sqrt(B0**2*A0**2))/((2*Ts*(1-OG/Ts)*sqrt(H0_prim*sigma_prim)))
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:184: RuntimeWarning: invalid value encountered in true_divide
      CD = 22.5 * bBK / (pi * l * fi_a * f) + 2.4;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:190: RuntimeWarning: divide by zero encountered in true_divide
      So = (0.3 * pi * l * fi_a * f / bBK + 1.95) * bBK;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:190: RuntimeWarning: invalid value encountered in true_divide
      So = (0.3 * pi * l * fi_a * f / bBK + 1.95) * bBK;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:190: RuntimeWarning: invalid value encountered in multiply
      So = (0.3 * pi * l * fi_a * f / bBK + 1.95) * bBK;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:210: RuntimeWarning: invalid value encountered in true_divide
      Cp_minus = -22.5 * bBK / (pi * l * fi_a * f) - 1.2;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:223: RuntimeWarning: invalid value encountered in true_divide
      B44BK_L = 2 * LBK * l1 / (fi_a * w); #
    c:\dev\evaluation\signal_lab\mdl_to_evaluation.py:106: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
      df_.units = units
    c:\python36-64\lib\re.py:212: FutureWarning: split() requires a non-empty pattern match.
      return _compile(pattern, flags).split(string, maxsplit)
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:283: RuntimeWarning: divide by zero encountered in true_divide
      Cf = 1.328*sqrt(2*pi*visc/(3.22*r_f**2*fi_a**2*w))
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:586: RuntimeWarning: invalid value encountered in sqrt
      gamma=sqrt(pi)*f3*(rmax+2*M/H*sqrt(B0**2*A0**2))/((2*Ts*(1-OG/Ts)*sqrt(H0_prim*sigma_prim)))
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:184: RuntimeWarning: invalid value encountered in true_divide
      CD = 22.5 * bBK / (pi * l * fi_a * f) + 2.4;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:190: RuntimeWarning: divide by zero encountered in true_divide
      So = (0.3 * pi * l * fi_a * f / bBK + 1.95) * bBK;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:190: RuntimeWarning: invalid value encountered in true_divide
      So = (0.3 * pi * l * fi_a * f / bBK + 1.95) * bBK;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:190: RuntimeWarning: invalid value encountered in multiply
      So = (0.3 * pi * l * fi_a * f / bBK + 1.95) * bBK;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:210: RuntimeWarning: invalid value encountered in true_divide
      Cp_minus = -22.5 * bBK / (pi * l * fi_a * f) - 1.2;
    c:\dev\rolldecay-estimators\rolldecayestimators\ikeda_speed.py:223: RuntimeWarning: invalid value encountered in true_divide
      B44BK_L = 2 * LBK * l1 / (fi_a * w); #

```python
ymax = np.max([df_ikeda['B_44_hat'].max(),df_ikeda['B_e_hat_model_test'].max()])
for id,df_ikeda in results.groupby(by='id'):
    fig,ax=plt.subplots()
    interesting_ = ['B_L_hat','B_W_hat','B_F_hat','B_E_hat',]
    df_ikeda.plot.area(x='phi_as_deg', y=interesting_, ax=ax)

    df_ikeda.plot(x='phi_as_deg',y='B_e_hat_model_test', label='cubic model', ax=ax)

    resource = resources[id]
    model = joblib.load(resource['roll_decay_model'])
    estimator = model['estimator']
    X_amplitudes=estimator.X_amplitudes.copy()
    X_amplitudes['phi_a_deg'] = np.rad2deg(X_amplitudes['phi_a'])
    result_for_database = estimator.result_for_database(meta_data=meta_data)
    omega0=result_for_database['omega0']
    A_44=result_for_database['A_44']
    X_amplitudes['B']=X_amplitudes['B_n']*2*omega0*A_44/2

    X_amplitudes['B_hat'] = lambdas.B_hat_lambda(B=X_amplitudes['B'], Disp=volume, beam=beam, g=g, rho=rho)
    X_amplitudes.plot(x='phi_a_deg', y='B_hat', style='k.', label='model test', ax=ax)

    ax.legend()
    ax.set_xlabel('$\phi_a$ [deg] (Roll amplitude)')
    ax.set_ylabel('$\hat{B_e}$ [-] (Nonlin. linear equivalent damping)');

    s = df_rolldecays.loc[id]
    title='Ship speed: %0.1f [kts]' % s.ship_speed
    ax.set_title(title)
    ax.set_ylim(0,ymax)

    figure_name = 'KVLCC2_B_e_%0.1f' % s.ship_speed
    save_fig(fig, name=figure_name)

```

## Time

```python
def residual(x,df, omega0):
    """
    Residual function for least square fit
    """

    B_1 = x[0]
    B_2 = x[1]
    B_3 = x[2]

    phi_a = df.index
    B_e_pred = lambdas.B_e_lambda_cubic(B_1=B_1, B_2=B_2, B_3=B_3, omega0=omega0, phi_a=phi_a)
    B_e_true = df['B_44']
    error = B_e_true-B_e_pred

    return error

```

```python
for id,df_ikeda in results.groupby(by='id'):

    ## Convert to dimensional damping [Nm/s]
    df_ikeda['B_44'] = lambdas.B_from_hat_lambda(B_44_hat=df_ikeda['B_44_hat'], Disp=volume, beam=beam, g=g, rho=rho)

    ## Load the cubic model
    resource = resources[id]
    model = joblib.load(resource['roll_decay_model'])
    estimator = model['estimator']
    result_for_database = estimator.result_for_database(meta_data=meta_data)
    omega0=result_for_database['omega0']
    A_44=result_for_database['A_44']

    ## Use least square fit of B_44 as a function of phi_a to determine B_1, B_2 and B_3:
    x0 = [result_for_database['B_1'],
      result_for_database['B_2'],
      result_for_database['B_3'],
     ]
    kwargs = {
    'df':df_ikeda,
    'omega0':omega0,
    }

    result = least_squares(fun=residual, x0=x0, kwargs=kwargs, method='lm')
    assert result.success

    ## Feed the results into a cubic model:
    parameters = {
    'B_1A':result.x[0]/A_44,
    'B_2A':result.x[1]/A_44,
    'B_3A':result.x[2]/A_44,
    'C_1A':estimator.parameters['C_1A'],
    'C_3A':estimator.parameters['C_3A'],
    'C_5A':estimator.parameters['C_5A'],
    }

    model_ikeda = EstimatorCubic.load(**parameters, X=estimator.X)

    ## Plotting:
    fig,ax=plt.subplots()
    model_ikeda.plot_fit(label='ikeda', ax=ax)
    s = df_rolldecays.loc[id]
    title='Ship speed: %0.1f [kts]' % s.ship_speed
    ax.set_title(title)

    fig,ax=plt.subplots()
    model_ikeda.plot_damping(label='ikeda', ax=ax)
    model['estimator'].plot_damping(label='cubic model', include_model_test=False, ax=ax)
    ax.set_title(title)

    fig,ax=plt.subplots()
    model_ikeda.plot_error(ax=ax)
    ax.set_title(title)

```

```python
print(run.project.project_path)
```

    \\sspa.local\gbg\projekt\2017\40178362-HHI-121-(ML-106)-Improved-analysis-of-tes

```python

```


# Theoretical Foundations of Buffer Stock Saving

Generator: BufferStockTheory-make/notebooks_byname

For the following badges: GitHub does not allow click-through redirects; right-click to get the link, then paste into navigation bar

[](https://colab.research.google.com/github/econ-ark/REMARK/blob/master/REMARKs/BufferStockTheory/BufferStockTheory.ipynb)

[This notebook](https://github.com/econ-ark/REMARK/blob/master/REMARKs/BufferStockTheory/BufferStockTheory.ipynb) uses the [Econ-ARK/HARK](https://github.com/econ-ark/hark) toolkit to describe the main results and reproduce the figures in the paper [Theoretical Foundations of Buffer Stock Saving](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory)

If you are not familiar with the HARK toolkit, you may wish to browse the ["Gentle Introduction to HARK"](https://mybinder.org/v2/gh/econ-ark/DemARK/master?filepath=Gentle-Intro-To-HARK.ipynb) before continuing (since you are viewing this document, you presumably know a bit about [Jupyter Notebooks](https://jupyter-notebook-beginner-guide.readthedocs.io/en/latest/)).

For instructions on how to install the [Econ-ARK/HARK](https://github.com/econ-ark/hark) toolkit on your computer, please refer to the [QUICK START GUIDE](https://github.com/econ-ark/HARK/blob/master/README.md).

The main HARK tool used here is $\texttt{ConsIndShockModel.py}$, in which agents have CRRA utility and face idiosyncratic shocks to permanent and transitory income.  For an introduction to this module, see the [ConsIndShockModel.ipynb](https://econ-ark.org/notebooks) notebook at the [Econ-ARK](https://econ-ark.org) website.

```python
# This cell does some setup; please be patient, it may take 3-5 minutes

# The tools for navigating the filesystem
import sys
import os

# Determine the platform so we can do things specific to each
import platform
pform = ''
pform = platform.platform().lower()
if 'darwin' in pform:
    pf = 'darwin' # MacOS
if 'debian'in pform:
    pf = 'debian' # Probably cloud (MyBinder, CoLab, ...)
if 'ubuntu'in pform:
    pf = 'debian' # Probably cloud (MyBinder, CoLab, ...)
if 'win' in pform:
    pf = 'win'

# Test whether latex is installed (some of the figures require it)
from distutils.spawn import find_executable

iflatexExists=False

if find_executable('latex'):
    iflatexExists=True

# if not iflatexExists:
#     print('Some of the figures below require a full installation of LaTeX')

#     # If running on Mac or Win, user can be assumed to be able to install
#     # any missing packages in response to error messages; but not on cloud
#     # so load LaTeX by hand (painfully slowly)
#     if 'debian' in pf: # CoLab and MyBinder are both ubuntu
#         print('Installing LaTeX now; please wait 3-5 minutes')
#         from IPython.utils import io

#         with io.capture_output() as captured: # Hide hideously long output
#             os.system('apt-get update')
#             os.system('apt-get install texlive texlive-latex-extra texlive-xetex dvipng')
#             iflatexExists=True
#     else:
#         print('Please install a full distributon of LaTeX on your computer then rerun.')
#         print('A full distribution means textlive, texlive-latex-extras, texlive-xetex, dvipng, and ghostscript')
#         sys.exit()

# This is a jupytext paired notebook that autogenerates BufferStockTheory.py
# which can be executed from a terminal command line via "ipython BufferStockTheory.py"
# But a terminal does not permit inline figures, so we need to test jupyter vs terminal
# Google "how can I check if code is executed in the ipython notebook"

from IPython import get_ipython # In case it was run from python instead of ipython

# If the ipython process contains 'terminal' assume not in a notebook
def in_ipynb():
    try:
        if 'terminal' in str(type(get_ipython())):
            return False
        else:
            return True
    except NameError:
        return False

if in_ipynb():
    # Now install stuff aside from LaTeX (if not already installed)
    os.system('pip install econ-ark==0.10.0.dev3')
    os.system('pip install matplotlib')
    os.system('pip install numpy')
    os.system('pip install scipy')
    os.system('pip install ipywidgets')
    os.system('pip install jupyter_contrib_nbextensions')
    os.system('jupyter contrib nbextension install --user')
    os.system('jupyter nbextension enable codefolding/main')
    os.system('jupyter nbextension enable latex_envs/latex_envs')
    os.system('jupyter nbextension enable navigation-hotkeys')
    os.system('pip install cite2c')
    os.system('python -m cite2c.install')
else:
    print('In batch mode')

# Import related generic python packages
import numpy as np
from time import clock
mystr = lambda number : "{:.4f}".format(number)

import matplotlib
import matplotlib.pyplot as plt
from matplotlib.pyplot import plot, draw, show

# In order to use LaTeX to manage all text layout in our figures,
# we import rc settings from matplotlib.
from matplotlib import rc

plt.rc('font', family='serif')
plt.rc('text', usetex=iflatexExists)

# The warnings package allows us to ignore some harmless but alarming warning messages
import warnings
warnings.filterwarnings("ignore")

from copy import copy, deepcopy

# Determine whether to make the figures inline (for spyder or jupyter)
# vs whatever is the automatic setting that will apply if run from the terminal
if in_ipynb():
    # %matplotlib inline generates a syntax error when run from the shell
    # so do this instead
    get_ipython().run_line_magic('matplotlib', 'inline')
else:
    get_ipython().run_line_magic('matplotlib', 'auto')

# Code to allow a master "Generator" and derived "Generated" versions
Generator=False # Is this notebook the master or is it generated?

# Define (and create, if necessary) the figures directory "Figures"
if Generator:
    my_file_path = os.path.dirname(os.path.abspath("BufferStockTheory.ipynb")) # Find pathname to this file:
    Figures_HARK_dir = os.path.join(my_file_path,"Figures/") # LaTeX document assumes figures will be here
    Figures_HARK_dir = os.path.join(my_file_path,"/tmp/Figures/") # Uncomment to make figures outside of git path
    if not os.path.exists(Figures_HARK_dir):
        os.makedirs(Figures_HARK_dir)

if not in_ipynb(): # running in batch mode
    print('You appear to be running from a terminal')
    print('By default, figures will appear one by one')
```

```python
# Import HARK tools needed

from HARK.ConsumptionSaving.ConsIndShockModel import IndShockConsumerType
from HARK.utilities import plotFuncsDer, plotFuncs
```

## [The Problem](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-Problem)

The paper defines and calibrates a small set of parameters:

| Parameter | Description | Code | Value |
|:---:| ---         | ---  | :---: |
| $\Gamma$ | Permanent Income Growth Factor | $\texttt{PermGroFac}$ | 1.03 |
| $\mathsf{R}$ | Interest Factor | $\texttt{Rfree}$ | 1.04 |
| $\beta$ | Time Preference Factor | $\texttt{DiscFac}$ | 0.96 |
| $\rho$ | Coeﬃcient of Relative Risk Aversion| $\texttt{CRRA}$ | 2 |
| $\wp$ | Probability of Unemployment | $\texttt{UnempPrb}$ | 0.005 |
| $\mu$ | Income when Unemployed | $\texttt{IncUnemp}$ | 0. |
| $\sigma_\psi$ | Std Dev of Log Permanent Shock| $\texttt{PermShkStd}$ | 0.1 |
| $\sigma_\theta$ | Std Dev of Log Transitory Shock| $\texttt{TranShkStd}$ | 0.1 |

For a microeconomic consumer with 'Market Resources' (net worth plus current income) $M_{t}$, end-of-period assets $A_{t}$ will be the amount remaining after consumption of $C_{t}$.
\begin{eqnarray}
A_{t}   &=&M_{t}-C_{t}
\end{eqnarray}

The consumer's permanent noncapital income $P$ grows by a predictable factor $\Gamma$ and is subject to an unpredictable lognormally distributed multiplicative shock $\mathbb{E}_{t}[\psi_{t+1}]=1$,
\begin{eqnarray}
P_{t+1} & = & P_{t} \Gamma \psi_{t+1}
\end{eqnarray}

and actual income is permanent income multiplied by a logormal multiplicative transitory shock, $\mathbb{E}_{t}[\theta_{t+1}]=1$, so that next period's market resources are
\begin{eqnarray}
%M_{t+1} &=& B_{t+1} +P_{t+1}\theta_{t+1},  \notag
M_{t+1} &=& A_{t}\mathsf{R} +P_{t+1}\theta_{t+1}.  \notag
\end{eqnarray}

When the consumer has a CRRA utility function $u(c)=\frac{c^{1-\rho}}{1-\rho}$, the paper shows that the problem can be written in terms of ratios of money variables to permanent income, e.g. $m_{t} \equiv M_{t}/P_{t}$, and the Bellman form of [the problem reduces to](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-Related-Problem):

\begin{eqnarray*}
v_t(m_t) &=& \max_{c_t}~~ u(c_t) + \beta~\mathbb{E}_{t} [(\Gamma\psi_{t+1})^{1-\rho} v_{t+1}(m_{t+1}) ] \\
& s.t. & \\
a_t &=& m_t - c_t \\
m_{t+1} &=& R/(\Gamma \psi_{t+1}) a_t + \theta_{t+1} \\
\end{eqnarray*}

```python
# Define a parameter dictionary with baseline parameter values

# Set the baseline parameter values
PermGroFac = 1.03
Rfree      = 1.04
DiscFac    = 0.96
CRRA       = 2.00
UnempPrb   = 0.005
IncUnemp   = 0.0
PermShkStd = 0.1
TranShkStd = 0.1
# Import default parameter values
import HARK.ConsumptionSaving.ConsumerParameters as Params

# Make a dictionary containing all parameters needed to solve the model
base_params = Params.init_idiosyncratic_shocks

# Set the parameters for the baseline results in the paper
# using the variable values defined in the cell above
base_params['PermGroFac'] = [PermGroFac]   # Permanent income growth factor
base_params['Rfree']      = Rfree          # Interest factor on assets
base_params['DiscFac']    = DiscFac        # Time Preference Factor
base_params['CRRA']       = CRRA           # Coefficient of relative risk aversion
base_params['UnempPrb']   = UnempPrb       # Probability of unemployment (e.g. Probability of Zero Income in the paper)
base_params['IncUnemp']   = IncUnemp       # Induces natural borrowing constraint
base_params['PermShkStd'] = [PermShkStd]   # Standard deviation of log permanent income shocks
base_params['TranShkStd'] = [TranShkStd]   # Standard deviation of log transitory income shocks

# Some technical settings that are not interesting for our purposes
base_params['LivPrb']       = [1.0]   # 100 percent probability of living to next period
base_params['CubicBool']    = True    # Use cubic spline interpolation
base_params['T_cycle']      = 1       # No 'seasonal' cycles
base_params['BoroCnstArt']  = None    # No artificial borrowing constraint
```

## Convergence of the Consumption Rules

Under the given parameter values, [the paper's first figure](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Convergence-of-the-Consumption-Rules) depicts the successive consumption rules that apply in the last period of life $(c_{T}(m))$, the second-to-last period, and earlier periods $(c_{T-n})$.  $c(m)$ is the consumption function to which these converge as

\[
c(m) = \lim_{n \uparrow \infty} c_{T-n}(m)
\]

```python
# Create a buffer stock consumer instance by passing the dictionary to the class.
baseEx = IndShockConsumerType(**base_params)
baseEx.cycles = 100   # Make this type have a finite horizon (Set T = 100)

baseEx.solve()        # Solve the model
baseEx.unpackcFunc()  # Make the consumption function easily accessible

```

```python
# Plot the different periods' consumption rules.

m1 = np.linspace(0,9.5,1000) # Set the plot range of m
m2 = np.linspace(0,6.5,500)
c_m  = baseEx.cFunc[0](m1)   # c_m can be used to define the limiting inﬁnite-horizon consumption rule here
c_t1 = baseEx.cFunc[-2](m1) # c_t1 defines the second-to-last period consumption rule
c_t5 = baseEx.cFunc[-6](m1) # c_t5 defines the T-5 period consumption rule
c_t10 = baseEx.cFunc[-11](m1)  # c_t10 defines the T-10 period consumption rule
c_t0 = m2                            # c_t0 defines the last period consumption rule
plt.figure(figsize = (12,9))
plt.plot(m1,c_m,color="black")
plt.plot(m1,c_t1,color="black")
plt.plot(m1,c_t5,color="black")
plt.plot(m1,c_t10,color="black")
plt.plot(m2,c_t0,color="black")
plt.xlim(0,11)
plt.ylim(0,7)
plt.text(7,6,r'$c_{T}(m) = 45$ degree line',fontsize = 22,fontweight='bold')
plt.text(9.6,5.3,r'$c_{T-1}(m)$',fontsize = 22,fontweight='bold')
plt.text(9.6,2.6,r'$c_{T-5}(m)$',fontsize = 22,fontweight='bold')
plt.text(9.6,2.1,r'$c_{T-10}(m)$',fontsize = 22,fontweight='bold')
plt.text(9.6,1.7,r'$c(m)$',fontsize = 22,fontweight='bold')
plt.arrow(6.9,6.05,-0.6,0,head_width= 0.1,width=0.001,facecolor='black',length_includes_head='True')
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.text(0,7.05,"$c$",fontsize = 26)
plt.text(11.1,0,"$m$",fontsize = 26)
# Save the figures in several formats
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncsConverge.svg'))
if not in_ipynb():
    plt.ioff()
    plt.draw()
#    plt.show(block=False)
    plt.pause(1)
else:
     plt.show(block=True) # Change to False if you want to run uninterrupted

```

## Factors and Conditions

### [The Finite Human Wealth Condition](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Human-Wealth)

Human wealth for a perfect foresight consumer is defined as the present discounted value of future income:

\begin{eqnarray}
H_{t} & = & \mathbb{E}_{t}[P_{t} + \mathsf{R}^{-1} P_{t+1} + \mathsf{R}^{2} P_{t+2} ... ] \\
      & = & P_{t} \left(1 + (\Gamma/\mathsf{R}) + (\Gamma/\mathsf{R})^{2} ... \right)
\end{eqnarray}
which is an infinite number if $\Gamma/\mathsf{R} \geq 1$.  We say that the 'Finite Human Wealth Condition' (FHWC) holds if
$0 \leq (\Gamma/\mathsf{R}) < 1$.

### [Absolute Patience and the AIC](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#AIC)

The paper defines the Absolute Patience Factor as being equal to the ratio of $C_{t+1}/C_{t}$ for a perfect foresight consumer.  The Old English character "Þ" is used for this object in the paper, but "Þ" cannot currently be rendered conveniently in Jupyter notebooks, so we will substitute $\Phi$ here:

\begin{equation}
\Phi = (\mathsf{R} \beta)^{1/\rho}
\end{equation}

If $\Phi = 1$, a perfect foresight consumer will spend exactly the amount that can be sustained perpetually (given their current and future resources).  If $\Phi < 1$ (the consumer is 'absolutely impatient'; or, 'the absolute impatience condition holds'), the consumer is consuming more than the sustainable amount, so consumption will fall, and if the consumer is 'absolutely patient' with $\Phi > 1$ consumption will grow over time.

### [Growth Patience and the GIC](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#GIC)

For a [perfect foresight consumer](http://econ.jhu.edu/people/ccarroll/public/lecturenotes/consumption/PerfForesightCRRA), whether the ratio of consumption to the permanent component of income $P$ is rising, constant, or falling depends on the relative growth rates of consumption and permanent income, which is measured by the "Perfect Foresight Growth Patience Factor":

\begin{eqnarray}
\Phi_{\Gamma} & = & \Phi/\Gamma
\end{eqnarray}
and whether the ratio is falling or rising over time depends on whether $\Phi_{\Gamma}$ is below or above 1.

An analogous condition can be defined when there is uncertainty about permanent income.  Defining $\tilde{\Gamma} = (\mathbb{E}[\psi^{-1}])^{-1}\Gamma$, the 'Growth Impatience Condition' (GIC) is that
\begin{eqnarray}
  \Phi/\tilde{\Gamma} & < & 1
\end{eqnarray}

### [The Finite Value of Autarky Condition (FVAC)](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Autarky-Value)

The paper [shows](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Autarky-Value) that a consumer who planned to spend his permanent income $\{ p_{t}, p_{t+1}, ...\} $ in every period would have value defined by

\begin{equation}
v_{t}^{\text{autarky}} = u(p_{t})\left(\frac{1}{1-\beta \Gamma^{1-\rho} \mathbb{E}[\psi^{1-\rho}]}\right)
\end{equation}

and defines the 'Finite Value of Autarky Condition' as the requirement that the denominator of this expression be a positive finite number:

\begin{equation}
\beta \Gamma^{1-\rho} \mathbb{E}[\psi^{1-\rho}] < 1
\end{equation}

### [The Weak Return Impatience Condition (WRIC)](http://www.econ2.jhu.edu/people/ccarroll/papers/BufferStockTheory/#WRIC)

The 'Return Impatience Condition' $\Phi/\mathsf{R} < 1$ has long been understood to be required for the perfect foresight model to have a nondegenerate solution (when $\rho=1$, this reduces to $\beta < R$).  If the RIC does not hold, the consumer is so patient that the optimal consumption function approaches zero as the horizon extends.

When the probability of unemployment is $\wp$, the paper articulates an analogous (but weaker) condition:

\begin{eqnarray}
 \wp^{1/\rho} \Phi/\mathsf{R} & < & 1
\end{eqnarray}

# Key Results

## [Nondegenerate Solution Requires FVAC and WRIC](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Sufficient-Conditions-For-Nondegenerate-Solution)

A main result of the paper is that the conditions required for the model to have a nondegenerate solution ($0 < c(m) < \infty$ for feasible $m$) are that the Finite Value of Autarky (FVAC) and Weak Return Impatience Condition (WRAC) hold.

## [Natural Borrowing Constraint limits to Artificial Borrowing Constraint](http://www.econ2.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-Liquidity-Constrained-Solution-as-a-Limit)

Defining $\chi(\wp)$ as the consumption function associated with any particular value of $\wp$, and defining $\hat{\chi}$ as the consumption function that would apply in the absence of the zero-income shocks but in the presence of an 'artificial' borrowing constraint requiring $a \geq 0$, a la Deaton (1991), the paper shows that

\begin{eqnarray}
\lim_{\wp \downarrow 0}~\chi(\wp) & = & \hat{\chi}
\end{eqnarray}

That is, as $\wp$ approaches zero the problem with uncertainty becomes identical to the problem that instead has constraints.  (See [Precautionary Saving and Liquidity Constraints](http://econ.jhu.edu/people/ccarroll/papers/LiqConstr) for a full treatment of the relationship between precautionary saving and liquidity constraints).

## [$c(m)$ is Finite Even When Human Wealth Is Infinite](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#When-The-GIC-Fails)

In the perfect foresight model, if $\mathsf{R} < \Gamma$ the present discounted value of future labor income is infinite and so the limiting consumption function is $c(m) = \infty$ for all $m$.  Many models have no well-defined solution in this case.

The presence of uncertainty changes this: The limiting consumption function is finite for all values of $m$.

This is because uncertainty imposes a "natural borrowing constraint" that deters the consumer from borrowing against their unbounded future labor income.

A [table](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Sufficient-Conditions-For-Nondegenerate-Solution) puts this result in the context of implications of other conditions and restrictions.

## [If the GIC Holds, $\exists$ a finite 'target' $m$](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#onetarget)

Section [There Is Exactly One Target $m$ Ratio, Which Is Stable](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#onetarget) shows that, under parameter values for which the limiting consumption function exists, if the GIC holds then there will be a value $\check{m}$ such that:

\begin{eqnarray}
\mathbb{E}[m_{t+1}] & > & m_{t}~\text{if $m_{t} < \check{m}$} \\
\mathbb{E}[m_{t+1}] & < & m_{t}~\text{if $m_{t} > \check{m}$} \\
\mathbb{E}[m_{t+1}] & = & m_{t}~\text{if $m_{t} = \check{m}$}
\end{eqnarray}

## [If the GIC Fails, Target Wealth is Infinite ](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#The-GIC)

[A figure](http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#FVACnotGIC) depicts a solution when the **FVAC** (Finite Value of Autarky Condition) and **WRIC** hold (so that the model has a solution) but the **GIC** (Growth Impatience Condition) fails.  In this case the target wealth ratio is infinity.

The parameter values in this specific example are:

| Param | Description | Code | Value |
| :---: | ---         | ---  | :---: |
| $\Gamma$ | Permanent Income Growth Factor | $\texttt{PermGroFac}$ | 1.00 |
| $\mathrm{\mathsf{R}}$ | Interest Factor | $\texttt{Rfree}$ | 1.08 |

The figure is reproduced below.

```python
# Construct the "GIC fails" example.

GIC_fail_dictionary = dict(base_params)
GIC_fail_dictionary['Rfree']      = 1.08
GIC_fail_dictionary['PermGroFac'] = [1.00]

GICFailExample = IndShockConsumerType(
    cycles=0, # cycles=0 makes this an infinite horizon consumer
    **GIC_fail_dictionary)
```

    The given type violates the absolute impatience condition with the supplied parameter values; the AIF is 1.01823
    The given parameter values violate the growth impatience condition for this consumer type; the GIF is: 1.0088

The $\mathtt{IndShockConsumerType}$ tool automatically checks various parametric conditions, and will give a warning as well as the values of the factors if any conditions fail to be met.

We can also directly check the conditions, in which case results will be a little more verbose by default.

```python
# The checkConditions method does what it sounds like it would
GICFailExample.checkConditions(verbose=True)
```

    The given type violates the absolute impatience condition with the supplied parameter values; the AIF is 1.01823
        Therefore, the absolute amount of consumption is expected to grow over time
    The given parameter values violate the growth impatience condition for this consumer type; the GIF is: 1.0088
        Therefore, a target level of wealth does not exist.
    The weak return impatience factor value for the supplied parameter values satisfies the weak return impatience condition.
    The finite value of autarky factor value for the supplied parameter values satisfies the finite value of autarky condition.

    [!] For more information on the conditions, see Table 3 in "Theoretical Foundations of Buffer Stock Saving" at http://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/

Next we define the function $\mathrm{\mathbb{E}}_{t}[\Delta m_{t+1}]$ that shows the ‘sustainable’ level of spending at which $m$ is expected to remain unchanged.

```python
# Calculate "Sustainable" consumption that leaves expected m unchanged
# In the perfect foresight case, this is just permanent income plus interest income
# A small adjustment is required to take account of the consequences of uncertainty
InvEpShInvAct = np.dot(GICFailExample.PermShkDstn[0][0], GICFailExample.PermShkDstn[0][1]**(-1))
InvInvEpShInvAct = (InvEpShInvAct) ** (-1)
PermGroFacAct = GICFailExample.PermGroFac[0] * InvInvEpShInvAct
ER = GICFailExample.Rfree / PermGroFacAct
Er = ER - 1
mSSfunc = lambda m : 1 + (m-1)*(Er/ER)
```

```python
# Plot GICFailExample consumption function against the sustainable level of consumption

GICFailExample.solve() # Above, we set up the problem but did not solve it
GICFailExample.unpackcFunc()  # Make the consumption function easily accessible for plotting
m = np.linspace(0,5,1000)
c_m = GICFailExample.cFunc[0](m)
E_m = mSSfunc(m)
plt.figure(figsize = (12,8))
plt.plot(m,c_m,color="black")
plt.plot(m,E_m,color="black")
plt.xlim(0,5.5)
plt.ylim(0,1.6)
plt.text(0,1.63,"$c$",fontsize = 26)
plt.text(5.55,0,"$m$",fontsize = 26)
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.text(1,0.6,"$c(m_{t})$",fontsize = 18)
plt.text(1.5,1.2,"$\mathsf{E}_{t}[\Delta m_{t+1}] = 0$",fontsize = 18)
plt.arrow(0.98,0.62,-0.2,0,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(2.2,1.2,0.3,-0.05,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'FVACnotGIC.svg'))

# This figure reproduces the figure shown in the paper.
# The gap between the two functions actually increases with $m$ in the limit.
if not in_ipynb():
    plt.show(block=False)
    plt.pause(1)
else:
    plt.show(block=True) # Change to False if you want to run uninterrupted
```

As a foundation for the remaining figures, we define another instance of the class $\texttt{IndShockConsumerType}$, which has the same parameter values as the instance $\texttt{baseEx}$ defined previously but is solved to convergence (our definition of an infinite horizon agent type)

```python
# cycles=0 tells the solver to find the infinite horizon solution
baseEx_inf = IndShockConsumerType(cycles=0,**base_params)

baseEx_inf.solve()
baseEx_inf.unpackcFunc()
```

### [Target $m$, Expected Consumption Growth, and Permanent Income Growth](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#AnalysisoftheConvergedConsumptionFunction)

The next figure is shown in  [Analysis of the Converged Consumption Function](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#cGroTargetFig), which shows the expected consumption growth factor $\mathrm{\mathbb{E}}_{t}[c_{t+1}/c_{t}]$ for a consumer behaving according to the converged consumption rule.

```python
# Define a function to calculate expected consumption
def exp_consumption(a):
    '''
    Taking end-of-period assets as input, return expectation of next period's consumption
    Inputs:
       a: end-of-period assets
    Returns:
       expconsump: next period's expected consumption
    '''
    GrowFactp1 = baseEx_inf.PermGroFac[0]* baseEx_inf.PermShkDstn[0][1]
    Rnrmtp1 = baseEx_inf.Rfree / GrowFactp1
    # end-of-period assets plus normalized returns
    btp1 = Rnrmtp1*a
    # expand dims of btp1 and use broadcasted sum of a column and a row vector
    # to obtain a matrix of possible beginning-of-period assets next period
    mtp1 = np.expand_dims(btp1, axis=1) + baseEx_inf.TranShkDstn[0][1]
    part_expconsumption = GrowFactp1*baseEx_inf.cFunc[0](mtp1).T
    # finish expectation over permanent income shocks by right multiplying with
    # the weights
    part_expconsumption = np.dot(part_expconsumption, baseEx_inf.PermShkDstn[0][0])
    # finish expectation over transitory income shocks by right multiplying with
    # weights
    expconsumption = np.dot(part_expconsumption, baseEx_inf.TranShkDstn[0][0])
    # return expected consumption
    return expconsumption
```

```python
# Calculate the expected consumption growth factor
m1 = np.linspace(1,baseEx_inf.solution[0].mNrmSS,50) # m1 defines the plot range on the left of target m value (e.g. m <= target m)
c_m1 = baseEx_inf.cFunc[0](m1)
a1 = m1-c_m1
exp_consumption_l1 = []
for i in range(len(a1)):
    exp_consumption_tp1 = exp_consumption(a1[i])
    exp_consumption_l1.append(exp_consumption_tp1)

# growth1 defines the values of expected consumption growth factor when m is less than target m
growth1 = np.array(exp_consumption_l1)/c_m1

# m2 defines the plot range on the right of target m value (e.g. m >= target m)
m2 = np.linspace(baseEx_inf.solution[0].mNrmSS,1.9,50)

c_m2 = baseEx_inf.cFunc[0](m2)
a2 = m2-c_m2
exp_consumption_l2 = []
for i in range(len(a2)):
    exp_consumption_tp1 = exp_consumption(a2[i])
    exp_consumption_l2.append(exp_consumption_tp1)

# growth 2 defines the values of expected consumption growth factor when m is bigger than target m
growth2 = np.array(exp_consumption_l2)/c_m2
```

```python
# Define a function to construct the arrows on the consumption growth rate function
def arrowplot(axes, x, y, narrs=15, dspace=0.5, direc='neg',
              hl=0.01, hw=3, c='black'):
    '''
    The function is used to plot arrows given the data x and y.

    Input:
        narrs  :  Number of arrows that will be drawn along the curve

        dspace :  Shift the position of the arrows along the curve.
                  Should be between 0. and 1.

        direc  :  can be 'pos' or 'neg' to select direction of the arrows

        hl     :  length of the arrow head

        hw     :  width of the arrow head

        c      :  color of the edge and face of the arrow head
    '''

    # r is the distance spanned between pairs of points
    r = np.sqrt(np.diff(x)**2+np.diff(y)**2)
    r = np.insert(r, 0, 0.0)

    # rtot is a cumulative sum of r, it's used to save time
    rtot = np.cumsum(r)

    # based on narrs set the arrow spacing
    aspace = r.sum() / narrs

    if direc is 'neg':
        dspace = -1.*abs(dspace)
    else:
        dspace = abs(dspace)

    arrowData = [] # will hold tuples of x,y,theta for each arrow
    arrowPos = aspace*(dspace) # current point on walk along data
                                 # could set arrowPos to 0 if you want
                                 # an arrow at the beginning of the curve

    ndrawn = 0
    rcount = 1
    while arrowPos < r.sum() and ndrawn < narrs:
        x1,x2 = x[rcount-1],x[rcount]
        y1,y2 = y[rcount-1],y[rcount]
        da = arrowPos-rtot[rcount]
        theta = np.arctan2((x2-x1),(y2-y1))
        ax = np.sin(theta)*da+x1
        ay = np.cos(theta)*da+y1
        arrowData.append((ax,ay,theta))
        ndrawn += 1
        arrowPos+=aspace
        while arrowPos > rtot[rcount+1]:
            rcount+=1
            if arrowPos > rtot[-1]:
                break

    for ax,ay,theta in arrowData:
        # use aspace as a guide for size and length of things
        # scaling factors were chosen by experimenting a bit

        dx0 = np.sin(theta)*hl/2.0 + ax
        dy0 = np.cos(theta)*hl/2.0 + ay
        dx1 = -1.*np.sin(theta)*hl/2.0 + ax
        dy1 = -1.*np.cos(theta)*hl/2.0 + ay

        if direc is 'neg' :
            ax0 = dx0
            ay0 = dy0
            ax1 = dx1
            ay1 = dy1
        else:
            ax0 = dx1
            ay0 = dy1
            ax1 = dx0
            ay1 = dy0

        axes.annotate('', xy=(ax0, ay0), xycoords='data',
                xytext=(ax1, ay1), textcoords='data',
                arrowprops=dict( headwidth=hw, frac=1., ec=c, fc=c))
```

```python
# Plot consumption growth as a function of market resources
# Calculate Absolute Patience Factor Phi = lower bound of consumption growth factor
AbsPatientFac = (baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)

fig = plt.figure(figsize = (12,8))
ax = fig.add_subplot(111)
# Plot the Absolute Patience Factor line
ax.plot([0,1.9],[AbsPatientFac,AbsPatientFac],color="black")

# Plot the Permanent Income Growth Factor line
ax.plot([0,1.9],[baseEx_inf.PermGroFac[0],baseEx_inf.PermGroFac[0]],color="black")

# Plot the expected consumption growth factor on the left side of target m
ax.plot(m1,growth1,color="black")

# Plot the expected consumption growth factor on the right side of target m
ax.plot(m2,growth2,color="black")

# Plot the arrows
arrowplot(ax, m1,growth1)
arrowplot(ax, m2,growth2, direc='pos')

# Plot the target m
ax.plot([baseEx_inf.solution[0].mNrmSS,baseEx_inf.solution[0].mNrmSS],[0,1.4],color="black",linestyle="--")
ax.set_xlim(1,2.05)
ax.set_ylim(0.98,1.08)
ax.text(1,1.082,"Growth Rate",fontsize = 26,fontweight='bold')
ax.text(2.055,0.98,"$m_{t}$",fontsize = 26,fontweight='bold')
ax.text(1.9,1.01,"$\mathsf{E}_{t}[c_{t+1}/c_{t}]$",fontsize = 22,fontweight='bold')
ax.text(baseEx_inf.solution[0].mNrmSS,0.975, r'$\check{m}$', fontsize = 26,fontweight='bold')
ax.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
ax.text(1.9,0.998,r'$\Phi = (\mathrm{\mathsf{R}}\beta)^{1/\rho}$',fontsize = 22,fontweight='bold')
ax.text(1.9,1.03, r'$\Gamma$',fontsize = 22,fontweight='bold')
if Generator:
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.png'))
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.jpg'))
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.pdf'))
    fig.savefig(os.path.join(Figures_HARK_dir, 'cGroTargetFig.svg'))
if not in_ipynb():
    plt.show(block=False)
    plt.pause(1)
else:
    plt.show(block=True) # Change to False if you want to run uninterrupted
```

### [Consumption Function Bounds](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#AnalysisOfTheConvergedConsumptionFunction)
[The next figure](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#cFuncBounds)
illustrates theoretical bounds for the consumption function.

We define two useful variables: lower bound of $\kappa$ (marginal propensity to consume) and limit of $h$ (Human wealth), along with some functions such as limiting perfect foresight consumption functions ($\bar{c}(m)$), $\bar{\bar c}(m)$ and $\underline{c}(m)$.

```python
# Define k_lower, h_inf and perfect foresight consumption function, upper bound of consumption function and lower
# bound of consumption function.
k_lower = 1.0-(baseEx_inf.Rfree**(-1.0))*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)
h_inf = (1.0/(1.0-baseEx_inf.PermGroFac[0]/baseEx_inf.Rfree))
conFunc_PF = lambda m: (h_inf -1)* k_lower + k_lower*m
conFunc_upper = lambda m: (1 - baseEx_inf.UnempPrb ** (1.0/baseEx_inf.CRRA)*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree)*m
conFunc_lower = lambda m: (1 -(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree) * m
intersect_m = ((h_inf-1)* k_lower)/((1 - baseEx_inf.UnempPrb
            **(1.0/baseEx_inf.CRRA)*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree)-k_lower)
```

```python
# Plot the consumption function and its bounds

x1 = np.linspace(0,25,1000)
x3 = np.linspace(0,intersect_m,300)
x4 = np.linspace(intersect_m,25,700)
cfunc_m = baseEx_inf.cFunc[0](x1)
cfunc_PF_1 = conFunc_PF(x3)
cfunc_PF_2 = conFunc_PF(x4)
cfunc_upper_1 = conFunc_upper(x3)
cfunc_upper_2 = conFunc_upper(x4)
cfunc_lower = conFunc_lower(x1)
plt.figure(figsize = (12,8))
plt.plot(x1,cfunc_m, color="black")
plt.plot(x1,cfunc_lower, color="black",linewidth=2.5)
plt.plot(x3,cfunc_upper_1, color="black",linewidth=2.5)
plt.plot(x4,cfunc_PF_2 , color="black",linewidth=2.5)
plt.plot(x4,cfunc_upper_2 , color="black",linestyle="--")
plt.plot(x3,cfunc_PF_1 , color="black",linestyle="--")
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.xlim(0,25)
plt.ylim(0,1.12*conFunc_PF(25))
plt.text(0,1.12*conFunc_PF(25)+0.05,"$c$",fontsize = 22)
plt.text(25+0.1,0,"$m$",fontsize = 22)
plt.text(2.5,1,r'$c(m)$',fontsize = 22,fontweight='bold')
plt.text(6,5,r'$\overline{\overline{c}}(m)= \overline{\kappa}m = (1-\wp^{1/\rho}\Phi_{R})m$',fontsize = 22,fontweight='bold')
plt.text(2.2,3.8, r'$\overline{c}(m) = (m-1+h)\underline{\kappa}$',fontsize = 22,fontweight='bold')
plt.text(9,4.1,r'Upper Bound $ = $ Min $[\overline{\overline{c}}(m),\overline{c}(m)]$',fontsize = 22,fontweight='bold')
plt.text(7,0.7,r'$\underline{c}(m)= (1-\Phi_{R})m = \underline{\kappa}m$',fontsize = 22,fontweight='bold')
plt.arrow(2.45,1.05,-0.5,0.02,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(2.15,3.88,-0.5,0.1,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(8.95,4.15,-0.8,0.05,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(5.95,5.05,-0.4,0,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(14,0.70,0.5,-0.1,head_width= 0.05,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cFuncBounds.svg'))
if not in_ipynb():
    plt.show(block=False)
    plt.pause(1)
else:
    plt.show(block=True) # Change to False if you want to run uninterrupted
```

### [The Consumption Function and Target $m$](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#cFuncBounds)

This figure shows the $\mathrm{\mathbb{E}}_{t}[\Delta m_{t+1}]$ and consumption function $c(m_{t})$, along with the intrsection of these two functions, which defines the target value of $m$

```python
# This just plots objects that have already been constructed

m1 = np.linspace(0,4,1000)
cfunc_m = baseEx_inf.cFunc[0](m1)
mSSfunc = lambda m:(baseEx_inf.PermGroFac[0]/baseEx_inf.Rfree)+(1.0-baseEx_inf.PermGroFac[0]/baseEx_inf.Rfree)*m
mss = mSSfunc(m1)
plt.figure(figsize = (12,8))
plt.plot(m1,cfunc_m, color="black")
plt.plot(m1,mss, color="black")
plt.xlim(0,3)
plt.ylim(0,1.45)
plt.plot([baseEx_inf.solution[0].mNrmSS, baseEx_inf.solution[0].mNrmSS],[0,2.5],color="black",linestyle="--")
plt.tick_params(labelbottom=False, labelleft=False,left='off',right='off',bottom='off',top='off')
plt.text(0,1.47,r"$c$",fontsize = 26)
plt.text(3.02,0,r"$m$",fontsize = 26)
plt.text(2.3,0.95,r'$\mathsf{E}[\Delta m_{t+1}] = 0$',fontsize = 22,fontweight='bold')
plt.text(2.3,1.1,r"$c(m_{t})$",fontsize = 22,fontweight='bold')
plt.text(baseEx_inf.solution[0].mNrmSS,-0.05, r"$\check{m}$",fontsize = 26)
plt.arrow(2.28,1.12,-0.1,0.03,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(2.28,0.97,-0.1,0.02,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'cRatTargetFig.svg'))
if not in_ipynb():
    plt.show(block=False)
    plt.pause(1)
else:
    plt.show(block=True)
```

### [Upper and Lower Limits of the Marginal Propensity to Consume](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#MPCLimits)

The paper shows that as $m_{t}~\uparrow~\infty$ the consumption function in the presence of risk gets arbitrarily close to the perfect foresight consumption function.  Defining $\underline{\kappa}$ as the perfect foresight model's MPC, this implies that $\lim_{m_{t}~\uparrow~\infty} c^{\prime}(m) = \underline{\kappa}$.

The paper also derives an analytical limit $\bar{\kappa}$ for the MPC as $m$ approaches 0., its bounding value.  Strict concavity of the consumption function implies that the consumption function will be everywhere below a function $\bar{\kappa}m$, and strictly declining everywhere.  The last figure plots the MPC between these two limits.

```python
# The last figure shows the upper and lower limits of the MPC
plt.figure(figsize = (12,8))
# Set the plot range of m
m = np.linspace(0.001,8,1000)

# Use the HARK method derivative to get the derivative of cFunc, and the values are just the MPC
MPC = baseEx_inf.cFunc[0].derivative(m)

# Define the upper bound of MPC
MPCUpper = (1 - baseEx_inf.UnempPrb ** (1.0/baseEx_inf.CRRA)*(baseEx_inf.Rfree*baseEx_inf.DiscFac)**(1.0/baseEx_inf.CRRA)/baseEx_inf.Rfree)

# Define the lower bound of MPC
MPCLower = k_lower

plt.plot(m,MPC,color = 'black')
plt.plot([0,8],[MPCUpper,MPCUpper],color = 'black')
plt.plot([0,8],[MPCLower,MPCLower],color = 'black')
plt.xlim(0,8)
plt.ylim(0,1)
plt.text(1.5,0.6,r'$\kappa(m) \equiv c^{\prime}(m)$',fontsize = 26,fontweight='bold')
plt.text(6,0.87,r'$(1-\wp^{1/\rho}\Phi_{R})\equiv \overline{\kappa}$',fontsize = 26,fontweight='bold')
plt.text(0.5,0.07,r'$\underline{\kappa}\equiv(1-\Phi_{R})$',fontsize = 26,fontweight='bold')
plt.text(8.05,0,"$m$",fontsize = 26)
plt.arrow(1.45,0.61,-0.4,0,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(1.7,0.07,0.2,-0.01,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
plt.arrow(5.95,0.875,-0.2,0.03,head_width= 0.02,width=0.001,facecolor='black',length_includes_head='True')
if Generator:
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.png'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.jpg'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.pdf'))
    plt.savefig(os.path.join(Figures_HARK_dir, 'MPCLimits.svg'))
if not in_ipynb():
    plt.show(block=False)
    plt.pause(1)
else:
    plt.show(block=True) # Change to False if you want to run uninterrupted
```

# Summary

[Two tables in the paper](https://econ.jhu.edu/people/ccarroll/papers/BufferStockTheory/#Sufficient-Conditions-For-Nondegenerate-Solution) summarize the various definitions, and then articulate conditions required for the problem to have a nondegenerate solution.

The main other contribution of the paper is to show that, under parametric combinations where the solution is nondegenerate, if the Growth Impatience Condition holds there will be a target level of wealth.