respy showcase

```python
import io
import copy
import yaml
import numpy as np
import pandas as pd
import respy as rp
import seaborn as sns
import matplotlib.pyplot as plt
from IPython.display import Image
from pathlib import Path

from auxiliary.auxiliary import *
```

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical Framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

1. Package overview

### What is **respy**?

- open source Python framework
- simulation and estimation of some finite-horizon discrete choice dynamic programming models
- class of models: Eckstein-Keane-Wolpin models (Aguirregabiria & Mira, 2009)

### Class of models

- Eckstein-Keane-Wolpin models
- discrete choice dynamic life-cycle framework
- reward function structure: experience accumulation and wages
- economic applications: human capital investments, labor supply, occupational choice

**Model components**
- (any) number of choices and periods
- initial conditions: lagged choices, initial experience, observable characteristics
- unobserved heterogeneity: finite mixture models (types)
- measurement error

### Use cases

**For research ...**
- flexible model specification
- build and solve structural models in weeks or months
- extensive test suite

**For learning & teaching ...**
- extensive online documentation: tutorials, explanations, how-to guides
- variety of pre-implemented example models that scale from toy examples to full empirical models
- sandbox for theses & student projects
- lecture materials: https://ekw-lectures.readthedocs.io

### Steps of modeling

 1. Theoretical development
 2. Model implementation
     2.1. Specification
     2.2. Solution
     2.3. Simulation
 3. Calibration
      3.1. Likelihood- or simulation-based criterion
      3.2. Optimization $\rightarrow$ [estimagic](https://estimagic.readthedocs.io/en/latest/)
 4. Validation and policy evaluation

### Source code & documentation

- developed on GitHub: https://github.com/OpenSourceEconomics/respy

- online documentation: https://respy.readthedocs.io

**Installation via conda**

```bash
$ conda config --add channels conda-forge
$ conda install -c opensourceeconomics respy
```

#### Source code on GitHub: https://github.com/OpenSourceEconomics/respy

```python
Image(filename='figures/respy-github.png')
```

#### Testing

```python
Image(filename='figures/respy-ci.png')
```

#### Online documentation: https://respy.readthedocs.io

```python
Image(filename='figures/respy-docs.png')
```

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

2. Introductory tutorial

### Situational setup
- Robinson Crusoe is stranded on a small island.
- He goes fishing to make ends meet and will relax in his hammock when he is tired.
- He cannot relax to often as he won't be able to eat otherwise.
- The more he fishes, the better his fishing skills become.

2.1. Theoretical Framework: Robinson Crusoe on an island

### The economic problem

- Robinson chooses every period $t = 0, \dots, T$ to either go fishing, $a = 0$, or spend the day in the hammock, $a = 1$.

- If Robinson chooses to go fishing, he gains one additional unit of **experience** in the next period. Experience starts at zero.

- The utility of a choice, $U(s_t, a_t)$, depends on the state $s_t$, which contains information on the individual's characteristics, and the chosen alternative $a_t$.

- Robinson's utility for any given option can be denoted by

$$\begin{align}
    U(s_t, a_t) = \underbrace{W(s_t, a_t)}_{wage} + \underbrace{N(s_t, a_t)}_{non-pecuniary}
\end{align}
$$

```python
Image(filename="figures/tree_small.jpg")
```

### Working alternatives

- For working alternatives like fishing, utility consists of two components, a *wage* and a *non-pecuniary* component.

**Wage**

$$\begin{align}
        W(s_t, a_t)  &= r_{a} \exp\{x^w_{at} \beta^w_a + \epsilon_{at}\}\\
    \ln(W(s_t, a_t)) &= \ln(r_a) + x^w_{at} \beta^w_a + \epsilon_{at}
\end{align}$$

- Components:
    - $r_a$ is a market rental price for skill units.

    - $x^w_{at}$ and $\beta^w_a$ are the choice- and time-dependent covariates and returns related to the wage signaled by superscript $w$.

    - $\epsilon_{at}$ is a choice-specific random shock from the shock vector $\epsilon_t \sim \mathcal{N}(0, \Sigma)$ for all choices.

**Non-pecuniary rewards** (for working alternatives)

- Vector dot product of covariates $x_t^w$ and parameters $\beta^w$.
- Superscript $w$ signals that the components belong to working alternatives.

$$\begin{align}
    N^w(s_t, a_t) = x_t^w\beta^w
\end{align}$$

### Non-working alternatives

**Wage**
$$\begin{align}
W(s_t, a_t) = 0
\end{align}$$

**Non-pecuniary rewards**

- Shocks enter the equation *additively*
- Superscript $n$ stands for non-pecuniary
$$
    N^n(s_t, a_t) = x_t^n\beta^n + \epsilon_{at}
$$

- Robinson's choice set thus consists of a "working" alternative which awards him a pecuniary compensation or wage and a "leisure" or non-working alternative which he derives a utility from, but no experience or wage. Experience in this basic model starts at zero and increases by one for every period $t$ in $1, ..., T$ where he chooses to go fishing.

### Robinson's choice problem

- Robinson is forward-looking and maximizes the expected present value of utility over the remaining lifetime  $\rightarrow$ select the optimal sequence of choices $\{a_t\}^T_{t = 0}$.

- **Bellman equation**

$$\begin{align}
    V(s_{t})&= \max_{a_t} \, \{\underbrace{U(s_t, a_t)}_{\text{flow utility}}+ \delta \underbrace{\text{E}[ V(s_{t+1})]\}}_{\text{continuation value}}
\end{align}$$

- Realization of shocks becomes known in each period before Robinson makes his choice.
- As shocks in period $t + 1$ are unknown to the individual in period $t$, utility must be maximized given the joint distribution of shocks in period $t + 1$ which is a maximization problem over a two-dimensional integral.

- Denote the non-stochastic part of a state as $s^-$. Then, Robinson maximizes.

$$\begin{equation}
    V(s_t) = \max_{a_t}\{
        U(s_t, a_t) + \delta \int_{\epsilon_{1, t + 1}} \int_{\epsilon_{0, t + 1}}
        \max_{a_{t + 1}} V_{a_{t + 1}}(s^-_{t + 1}, \epsilon_{t + 1})
        f_\epsilon(\epsilon_{t + 1})
        d_{\epsilon_{0, t + 1}}, d_{\epsilon_{1, t + 1}}
    \}
\end{equation}$$

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

2.2. Specifying the model

**respy** models consists of two main inputs:

- `params`: (estimable) model parameters

- `options` : model settings (number of periods, implementation details)

### Model parameters: ``params``

- Parameter vector of the model

- Specified as a [MultiIndex pandas.DataFrame](https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html) with two index levels.
    1. *category*: which indicates certain parameters groups
    2. *name*: which indicates a specific parameter

- read in parameters from a .csv-file:

```python
params = """category,name,value
delta,delta,0.95
wage_fishing,exp_fishing,0.1
nonpec_fishing,constant,-1
nonpec_hammock,constant,2.5
nonpec_hammock,not_fishing_last_period,-1
shocks_sdcorr,sd_fishing,1
shocks_sdcorr,sd_hammock,1
shocks_sdcorr,corr_hammock_fishing,-0.2
lagged_choice_1_hammock,constant,1
"""
```

```python
params_basic = pd.read_csv(
    io.StringIO(params), sep=",", index_col=["category", "name"]
)

params_basic
```

      value

      category
      name

      delta
      delta
      0.95

      wage_fishing
      exp_fishing
      0.10

      nonpec_fishing
      constant
      -1.00

      nonpec_hammock
      constant
      2.50

      not_fishing_last_period
      -1.00

      shocks_sdcorr
      sd_fishing
      1.00

      sd_hammock
      1.00

      corr_hammock_fishing
      -0.20

      lagged_choice_1_hammock
      constant
      1.00

### Model options: ``options``

-  dictionary which contains additional information
    - number of periods in the model
    - size of the simulated sample
    - implementation details like seeds
    - covariates

```python
options_basic = {
    "n_periods": 10,
    "simulation_agents": 1000,
    "simulation_seed": 123,
    "covariates": {
        "constant": "1",
        "not_fishing_last_period": "lagged_choice_1 != 'fishing'",
    },
}
options_basic
```

    {'n_periods': 10,
     'simulation_agents': 1000,
     'simulation_seed': 123,
     'covariates': {'constant': '1',
      'not_fishing_last_period': "lagged_choice_1 != 'fishing'"}}

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

2.3. Simulating data

Using the `params` and `options` we can set up a simulator using the function `get_simulate_func` and subsequently simulate a data for our selected parametrization.

```python
simulate = rp.get_simulate_func(params_basic, options_basic)
df_basic = simulate(params_basic)
df_basic.head(5)
```

      Experience_Fishing
      Lagged_Choice_1
      Shock_Reward_Fishing
      Meas_Error_Wage_Fishing
      Shock_Reward_Hammock
      Meas_Error_Wage_Hammock
      Dense_Key
      Core_Index
      Choice
      Wage
      ...
      Nonpecuniary_Reward_Fishing
      Wage_Fishing
      Flow_Utility_Fishing
      Value_Function_Fishing
      Continuation_Value_Fishing
      Nonpecuniary_Reward_Hammock
      Wage_Hammock
      Flow_Utility_Hammock
      Value_Function_Hammock
      Continuation_Value_Hammock

      Identifier
      Period

      0
      0
      0
      hammock
      0.428556
      1
      0.427466
      1
      0
      1
      fishing
      1.535039
      ...
      -1
      1.535039
      0.535039
      19.006626
      19.443776
      1.5
      NaN
      1.833118
      18.704274
      17.759111

      1
      1
      fishing
      0.780145
      1
      1.850937
      1
      1
      0
      hammock
      NaN
      ...
      -1
      2.411250
      1.411250
      19.104983
      18.624982
      2.5
      NaN
      4.157512
      20.273393
      16.964086

      2
      1
      hammock
      2.063393
      1
      0.708209
      1
      2
      0
      fishing
      8.700606
      ...
      -1
      8.700606
      7.700606
      23.415469
      16.541961
      1.5
      NaN
      1.781221
      16.030649
      14.999397

      3
      2
      fishing
      -1.190336
      1
      -2.028002
      1
      3
      4
      fishing
      0.371452
      ...
      -1
      0.371452
      -0.628548
      13.904880
      15.298346
      2.5
      NaN
      0.751039
      13.874853
      13.814541

      4
      3
      fishing
      -0.049084
      1
      0.607816
      1
      4
      5
      hammock
      NaN
      ...
      -1
      1.285202
      0.285202
      13.405278
      13.810606
      2.5
      NaN
      3.105353
      14.884157
      12.398742

5 rows × 22 columns

**Choice shares**

```python
plot_choice_shares(df_basic)
```

**Transition matrix**

```python
data = pd.crosstab(df_basic.Lagged_Choice_1, df_basic.Choice, normalize=True)
sns.heatmap(data, cmap="Blues", annot=True);
```

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

2.4. Extending the Model

2.4.1. Extension: Covariates

**Covariates in the basic model**

1. The constant, which is the simplest covariate
2. The indicator whether Robinson went fishing last period.

**New covariate:** *age*

- assume Robinson's return to fishing decreases as he gets older.
- assume that he arrives at the island with 16 years.
- assume his age in each period, which we assume for now is one year, increases by one.

I. Edit ``params``

```python
params_age = params_basic.copy()
params_age.loc[("wage_fishing", "age"), "value"] = -0.08
params_age
```

      value

      category
      name

      delta
      delta
      0.95

      wage_fishing
      exp_fishing
      0.10

      nonpec_fishing
      constant
      -1.00

      nonpec_hammock
      constant
      2.50

      not_fishing_last_period
      -1.00

      shocks_sdcorr
      sd_fishing
      1.00

      sd_hammock
      1.00

      corr_hammock_fishing
      -0.20

      lagged_choice_1_hammock
      constant
      1.00

      wage_fishing
      age
      -0.08

II. Edit ``options``

```python
options_age = copy.deepcopy(options_basic)
options_age["covariates"].update({"age": "period + 16"})
options_age
```

    {'n_periods': 10,
     'simulation_agents': 1000,
     'simulation_seed': 123,
     'covariates': {'constant': '1',
      'not_fishing_last_period': "lagged_choice_1 != 'fishing'",
      'age': 'period + 16'}}

III. Simulate again

```python
simulate = rp.get_simulate_func(params_age, options_age)
df_age = simulate(params_age)
```

$\rightarrow$ **Result:** Including the negative effect of age reduces the proportion of individuals that choose fishing, increasingly so in later periods.

```python
plot_choice_shares(df_age)
```

2.4.2. Extension: Adding Choices

- after some time on the island, Robinson meets Friday who can help him improve his fishing.

- add the choice alternative `"friday"`

    - affects the utility of fishing.
    - choice should be available only once starting with the third period, and only after Robinson has been fishing before.

I. Edit `params` (loaded from .csv)

- `wage_fishing` now includes a return to contemplation with Friday.
- There is a new category,`nonpec_friday`, which captures the non-pecuniary reward for of spending a period with Friday.
- The shock shock variance-covariance matrix has been altered to include the additional option.

```python
params_friday = pd.read_csv("params_files/robinson_crusoe_friday.csv", index_col=["category", "name"])
params_friday
```

      value

      category
      name

      delta
      delta
      0.95

      wage_fishing
      exp_fishing
      0.10

      contemplation_with_friday
      0.40

      nonpec_fishing
      constant
      -1.00

      nonpec_friday
      constant
      -1.00

      not_fishing_last_period
      -1.00

      nonpec_hammock
      constant
      2.50

      not_fishing_last_period
      -1.00

      shocks_sdcorr
      sd_fishing
      1.00

      sd_friday
      1.00

      sd_hammock
      1.00

      corr_friday_fishing
      0.00

      corr_hammock_fishing
      0.00

      corr_hammock_friday
      0.00

      lagged_choice_1_hammock
      constant
      1.00

II. Adjust `options`

**Covariates**
- new covariate `"contemplation_with_friday"` is only affecting utility if ..
    - Robinson is experienced in fishing
    - only for one interaction with Friday

```python
options_friday = yaml.safe_load(open("option_files/options_friday.yml"))
options_friday["covariates"]
```

    {'constant': '1',
     'contemplation_with_friday': 'exp_friday == 1 and exp_fishing >= 1',
     'not_fishing_last_period': "lagged_choice_1 != 'fishing'"}

**Negative choice set**
- `"negative_choice_set"` can be used to restrict the choice Friday to the third and following periods

```python
options_friday["negative_choice_set"]
```

    {'friday': ['period < 2', 'exp_fishing == 0']}

III. Simulate

```python
simulate = rp.get_simulate_func(params_friday, options_friday)
df_friday = simulate(params_friday)
plot_choice_shares(df_friday, friday=True)
```

2.4.3. Extension: Initial Experience

- example of initial conditions: characteristics individuals enter the model with

- assume in period $t=0$, Robinson will have $0$, $1$, or $2$ periods of experience in fishing.

- this introduces a source of heterogeneity between agents

I. Edit `params`

- specify the distribution of initial experience via **probability mass functions**

- keyword is `"initial_exp_fishing_*"` in the *category*-level of the index, where the asterisk needs to be replaced with the experience level.

- the *name*-level, use `"probability"` to signal that the float in `"value"` is a probability.

- specification: Robinson has equal probability to start out with 0, 1 or 2 periods of experience.

```python
params_initial_exp = params_basic.copy()
params_initial_exp.loc[ ("initial_exp_fishing_0", "probability"), "value"] = 0.33
params_initial_exp.loc[("initial_exp_fishing_1", "probability"), "value"] = 0.33
params_initial_exp.loc[ ("initial_exp_fishing_2", "probability"), "value"] = 0.34
params_initial_exp
```

      value

      category
      name

      delta
      delta
      0.95

      wage_fishing
      exp_fishing
      0.10

      nonpec_fishing
      constant
      -1.00

      nonpec_hammock
      constant
      2.50

      not_fishing_last_period
      -1.00

      shocks_sdcorr
      sd_fishing
      1.00

      sd_hammock
      1.00

      corr_hammock_fishing
      -0.20

      lagged_choice_1_hammock
      constant
      1.00

      initial_exp_fishing_0
      probability
      0.33

      initial_exp_fishing_1
      probability
      0.33

      initial_exp_fishing_2
      probability
      0.34

II. Adjust `options`

$\rightarrow$ not necessary in this case!

III. Simulate

```python
simulate = rp.get_simulate_func(params_initial_exp, options_basic)
df_exp = simulate(params_initial_exp)
```

```python
plot_choice_prob_and_exp_level(df_basic)
plot_choice_prob_and_exp_level(df_exp)
```

Comparing the plots shows that the share of engagement in fishing increases compared to the baseline scenario, as two thirds of Robinsons land on the island with previous experience in fishing.

2.4.4. Extension: Observables

- add one observable characteristic to the baseline model, `"fishing_grounds"`

- assume Robinson can end up, with a certain probability, on the side of the island which has `"poor"` or `"rich"` fishing grounds.

- experiencing rich fishing grounds affects the **non-pecuniary reward** for fishing.

I. Add three additional rows to `params`

- The probability with which Robinson will find himself in rich and in poor fishing grounds.

- The return for being in a rich fishing ground, which here is set to be positive and constant.

```python
params_fish_ground = params_basic.copy()
params_fish_ground.loc[("observable_fishing_grounds_rich", "probability"), "value"] = 0.5
params_fish_ground.loc[("observable_fishing_grounds_poor", "probability"), "value"] = 0.5
params_fish_ground.loc[("nonpec_fishing", "rich_fishing_grounds"), "value"] = 0.5
params_fish_ground
```

      value

      category
      name

      delta
      delta
      0.95

      wage_fishing
      exp_fishing
      0.10

      nonpec_fishing
      constant
      -1.00

      nonpec_hammock
      constant
      2.50

      not_fishing_last_period
      -1.00

      shocks_sdcorr
      sd_fishing
      1.00

      sd_hammock
      1.00

      corr_hammock_fishing
      -0.20

      lagged_choice_1_hammock
      constant
      1.00

      observable_fishing_grounds_rich
      probability
      0.50

      observable_fishing_grounds_poor
      probability
      0.50

      nonpec_fishing
      rich_fishing_grounds
      0.50

II. Adjust covariates in `options`

```python
options_fish_ground = options_basic.copy()
options_fish_ground["covariates"] = {
    "constant": "1",
    "not_fishing_last_period": "lagged_choice_1 != 'fishing'",
    "rich_fishing_grounds": "fishing_grounds == 'rich'",
}
options_fish_ground
```

    {'n_periods': 10,
     'simulation_agents': 1000,
     'simulation_seed': 123,
     'covariates': {'constant': '1',
      'not_fishing_last_period': "lagged_choice_1 != 'fishing'",
      'rich_fishing_grounds': "fishing_grounds == 'rich'"}}

III. Simulate again

```python
simulate = rp.get_simulate_func(params_fish_ground, options_fish_ground)
df_fish_ground = simulate(params_fish_ground)
```

As can be expected, Robinson engages in fishing more often if he is stranded on the side of the island with rich fishing grounds.

```python
plot_fishing_grounds(df_fish_ground)
```

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

3. Pre-implemented example models

```python
params, options, data = rp.get_example_model(<model>)
```

### Toy models

- based on the story of Robinson Crusoe

- used in the [tutorials](https://respy.readthedocs.io/en/latest/tutorials/index.html) in the documentation

**Models**

- `robinson_crusoe_basic`

- `robinson_crusoe_extended`

**Advantages**

- simple model structure
- computationally feasible due to the small number of available choices and low number of periods in the models

$\rightarrow$ useful of learning & teaching

```python
_, _, data = rp.get_example_model("robinson_crusoe_basic")
plot_choice_shares(data)
_, _, data = rp.get_example_model("robinson_crusoe_extended")
plot_choice_shares(data, friday=True)
```

### Keane and Wolpin (1994)

**Source**

- Keane, M. P., & Wolpin, K. I. (1994). The Solution and Estimation of Discrete Choice Dynamic Programming Models by Simulation and Interpolation: Monte Carlo Evidence. *The Review of Economics and Statistics*, 648-672.

**Characteristics**
- four mutually exclusive alternatives that individuals can choose in each period (work in sectors `a` or `b`, invest in education, or stay home)
- time horizon of 40 periods

**Models**

- `kw_94_one`
- `kw_94_two`
- `kw_94_three`

```python
plot_choices_kw(example="1994")
```

### Keane and Wolpin (1997)

**Source**

* Keane, M. P., & Wolpin, K. I. (1997). The Career Decisions of Young Men. *Journal of Political Economy*, 105(3), 473-522.

**Characteristics**

* five mutually exclusive choice alternatives (occupations `white-collar`, `blue-collar`, `military`, school and staying home)
* basic and extended model specification with unobserved heterogeneity, measurement error, and initial experience
* time horizon of 50 periods
* empirical model calibrated to a sample of white men in the National Longitudinal Survey of Youth (NLSY)

**Models**

- `kw_97_basic`

- `kw_97_extended`

```python
plot_choices_kw(example="1997")
```

### Keane and Wolpin (2000)

**Source**

- Keane, M. P., & Wolpin, K. I. (2000). Eliminating Race Differences in School Attainment and Labor Market Success. *Journal of Labor Economics*, 18(4), 614-652.

**Model**

- `kw_2000`

The model is very similar to the extended model specification in Keane and Wolpin (1997).

 ### Outline

 1.  Package overview
 2.  Introductory tutorial
  2.1. Theoretical framework
  2.2. Specifying a model
  2.3. Simulating data
  2.4. Extending the model
 3. Pre-implemented example models
 4. Outlook

4. Outlook

#### Recent advancements

- Exogenous processes (childbirth, health shocks etc.)
- Revision of the state space
- State specific choice sets

#### Potential
- Consumption and saving decisions
- Continuous choices or time

### Related software projects

- [`estimagic`](https://estimagic.readthedocs.io/en/latest/) is a Python package that provides high-quality and user-friendly tools to fit large scale empirical models to data and make inferences about the estimated model parameters.

- [`ruspy`](https://ruspy.readthedocs.io/en/latest/) is an open-source Python package for the simulation and estimation of a prototypical infinite-horizon dynamic discrete choice model based on Rust (1987).

- [`grmpy`](https://grmpy.readthedocs.io/en/latest/) is an open-source Python package for the simulation and estimation of the generalized Roy model.

- [`econsa`](https://econsa.readthedocs.io/en/latest/) is an open-source Python package that facilitates uncertainty propagation and global sensitivity of computational economic models.analysis.

- [`robupy`](https://robupy.readthedocs.io/en/latest/) is an open-source Python package for robust decision making.

- [`gettsim`](https://gettsim.readthedocs.io/en/stable/) is an open-source Python package developed by OSE members providing a depiction of the German Taxes and Transfers System.

Thank you!

More information on OpenSourceEconomics software projects at https://open-econ.org.


```python
#remove cell visibility
from IPython.display import HTML
tag = HTML('''
Toggle cell visibility <a href="javascript:code_toggle()">here</a>.''')
display(tag)
```

Toggle cell visibility here.

```python
%matplotlib inline
import control
import numpy
import sympy as sym
from IPython.display import display, Markdown
import ipywidgets as widgets
import matplotlib.pyplot as plt

#print a matrix latex-like
def bmatrix(a):
     """Returns a LaTeX bmatrix - by Damir Arbula (ICCT project)

     :a: numpy array
     :returns: LaTeX bmatrix as a string
     """
     if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
     lines = str(a).replace('[', '').replace(']', '').splitlines()
     rv = [r'\begin{bmatrix}']
     rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
     rv +=  [r'\end{bmatrix}']
     return '\n'.join(rv)

# Display formatted matrix:
def vmatrix(a):
    if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
    lines = str(a).replace('[', '').replace(']', '').splitlines()
    rv = [r'\begin{vmatrix}']
    rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
    rv +=  [r'\end{vmatrix}']
    return '\n'.join(rv)

#matrixWidget is a matrix looking widget built with a VBox of HBox(es) that returns a numPy array as value !
class matrixWidget(widgets.VBox):
    def updateM(self,change):
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.M_[irow,icol] = self.children[irow].children[icol].value
                #print(self.M_[irow,icol])
        self.value = self.M_

    def dummychangecallback(self,change):
        pass

    def __init__(self,n,m):
        self.n = n
        self.m = m
        self.M_ = numpy.matrix(numpy.zeros((self.n,self.m)))
        self.value = self.M_
        widgets.VBox.__init__(self,
                             children = [
                                 widgets.HBox(children =
                                              [widgets.FloatText(value=0.0, layout=widgets.Layout(width='90px')) for i in range(m)]
                                             )
                                 for j in range(n)
                             ])

        #fill in widgets and tell interact to call updateM each time a children changes value
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
                self.children[irow].children[icol].observe(self.updateM, names='value')
        #value = Unicode('example@example.com', help="The email value.").tag(sync=True)
        self.observe(self.updateM, names='value', type= 'All')

    def setM(self, newM):
        #disable callbacks, change values, and reenable
        self.unobserve(self.updateM, names='value', type= 'All')
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].unobserve(self.updateM, names='value')
        self.M_ = newM
        self.value = self.M_
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].observe(self.updateM, names='value')
        self.observe(self.updateM, names='value', type= 'All')

                #self.children[irow].children[icol].observe(self.updateM, names='value')

#overlaod class for state space systems that DO NOT remove "useless" states (what "professor" of automatic control would do this?)
class sss(control.StateSpace):
    def __init__(self,*args):
        #call base class init constructor
        control.StateSpace.__init__(self,*args)
    #disable function below in base class
    def _remove_useless_states(self):
        pass
```

## Controllo della testina di un Hard-Disk

Il braccio degli Hard-Disk che sostiene la testina di lettura ruota su un cuscinetto viscoso ed è alimentato da un motore elettrico; una molla di richiamo con rigidità torsionale $k$ si oppone al suo movimento. Il cuscinetto possiede un leggero attrito viscoso, proporzionale alla velocità di rotazione del braccio della testa, con $b$ costante. L'attuatore del braccio è in grado di generare una coppia $T$ linearmente proporzionale alla corrente $I$ che scorre all'interno della bobina di attuazione: $T = k_iI$. L'inerzia del braccio è $J$. Un sensore misura la posizione angolare del braccio $\theta$.

Le costanti del sistema sono:
- $J = 0.001$ kg$\text{m}^2$,
- $k_i = 500$ Nm/A,
- $k = 1$ Nm/rad,
- $b = 0.04$ Nm/(rad/s).

L'obiettivo è progettare un regolatore per la posizione del braccio secondo le seguenti specifiche:
- zero overshoot,
- tempo di assestamento al 2% inferiore a 50 millisecondi,
- errore di regime in risposta a un cambiamento a gradino della posizione del braccio desiderata,
- la corrente di ingresso non deve superare i 5 A in risposta a un gradino di ingresso di 30 gradi.

L'equazione del sistema è:
$$
J\ddot{\theta} = k_iI - b\dot{\theta} - k\theta
$$

Definendo il vettore di stato come $x=\begin{bmatrix}x_1 & x_2\end{bmatrix}^T=\begin{bmatrix}\theta & \dot{\theta}\end{bmatrix}^T$ e $u=I$, l'equazione del sistema in forma matriciale diventa:

\begin{cases}
\dot{x} = \begin{bmatrix} 0 & 1 \\ -k & -b \end{bmatrix}x + \begin{bmatrix} 0 \\ k_i \end{bmatrix}u \\
y = \begin{bmatrix} 1 & 0 \end{bmatrix}x
\end{cases}

Per ottenere un errore di regime pari a zero, si aumenta l'impianto con un nuovo stato definito come $\dot{x_3}=y-y_d=x_1-y_d$. Le equazioni diventano quindi:

\begin{cases}
\dot{x_a} = \begin{bmatrix} 0 & 1 & 0 \\ -k & -b & 0 \\ 1 & 0 & 0 \end{bmatrix}x_a + \begin{bmatrix} 0 & 0 \\ k_i & 0 \\ 0 & -1 \end{bmatrix}\begin{bmatrix} u \\ y_d \end{bmatrix} \\
y = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 0 & 1 \end{bmatrix}x
\end{cases}

La matrice di controllabilità $\mathcal{C}$, considerando solo l'input $u$, è

```python
A = numpy.matrix('0 1 0; -1 -0.04 0; 1 0 0')
B = numpy.matrix('0 0; 500 0; 0 -1')
C = numpy.matrix('1 0 0; 0 0 1')
D = numpy.matrix('0 0; 0 0')

CM = control.ctrb(A,B[:,0])
display(Markdown(bmatrix(CM)))
# print(numpy.linalg.matrix_rank(CM))
```

\begin{bmatrix}
  0. & 500. & -20.\\
  500. & -20. & -499.2\\
  0. & 0. & 500.\\
\end{bmatrix}

e ha rango pari a 3 quindi il sistema è controllabile.
La matrice di osservabilità $\mathcal{O}$ è

```python
OM = control.obsv(A,C)
display(Markdown(bmatrix(OM)))
# print(numpy.linalg.matrix_rank(OM))
```

\begin{bmatrix}
  1. & 0. & 0.\\
  0. & 0. & 1.\\
  0. & 1. & 0.\\
  1. & 0. & 0.\\
  -1. & -0.04 & 0.\\
  0. & 1. & 0.\\
\end{bmatrix}

e ha rango pari a 3 quindi il sistema è osservabile.

### Design del regolatore
#### Design del controller

Si progetta il guadagno del feedback dello stato considerando solo l'input $u$ mentre per il riferimento si utilizza $y_d$. Le specifiche del transitorio possono essere rispettate se i poli dominanti ad anello chiuso sono posti in $-100.02\pm101i$ e imponendo che il terzo polo sia almeno simile in quanto a rapidità, come ad esempio in $-100$. Ciò si ottiene con la matrice del guadagno:
$$
K = \begin{bmatrix} 80.416 & 0.6 & 4041.0 \end{bmatrix}.
$$

#### Design dell'osservatore
Per avere una buona stima dello stato e, quindi, una rapida convergenza a zero dell'errore di stima, si posizionano i poli dell'osservatore in $-151$, $-152$ e $-153$ (più veloci dei poli del sistema controllato).
La matrice di guadagno dell'osservatore che ne risulta è:
$$
L = \begin{bmatrix} 300.96 & 3.028e-12 \\
 2.264e+04 & 4.55e-10 \\
 1.0 & 150.0 \end{bmatrix}.
$$

Le simulazioni sono mostrate di seguito. Si noti che aumentando il vettore di stato si assicura che il guadagno ad anello chiuso sia sempre 1.

### Come utilizzare questo notebook?
- Cerca di migliorare la risposta nel caso di un piccolo errore iniziale (circa 0,05) nei primi due elementi della condizione iniziale dell'osservatore (parametro X0 stim. Sotto).
- **N.B.** Introdurre un errore iniziale nella terza componente dello stato stimato ha poco senso poiché il terzo stato ha la dinamica esatta definita. Si sarebbe anche potuto sviluppare un osservatore solo per $x_2$ poiché si misura $x_1$ e si conosce $x_3$.

```python
# Preparatory cell

X0 = numpy.matrix('0.0; 0.0; 0.0')
K = numpy.matrix([8/15,-4.4,-4])
L = numpy.matrix([[300, 3.028E-12],[2.264E+04, 4.55E-10],[1, 150]])

Aw = matrixWidget(3,3)
Aw.setM(A)
Bw = matrixWidget(3,1)
Bw.setM(B)
Cw = matrixWidget(1,3)
Cw.setM(C)
X0w = matrixWidget(3,1)
X0w.setM(X0)
Kw = matrixWidget(1,3)
Kw.setM(K)
Lw = matrixWidget(3,2)
Lw.setM(L)

eig1c = matrixWidget(1,1)
eig2c = matrixWidget(2,1)
eig3c = matrixWidget(1,1)
eig1c.setM(numpy.matrix([-100.]))
eig2c.setM(numpy.matrix([[-100.02],[-101.]]))
eig3c.setM(numpy.matrix([-100.]))

eig1o = matrixWidget(1,1)
eig2o = matrixWidget(2,1)
eig3o = matrixWidget(1,1)
eig1o.setM(numpy.matrix([-150.]))
eig2o.setM(numpy.matrix([[-151.],[0.]]))
eig3o.setM(numpy.matrix([-152.]))
```

```python
# Misc

#create dummy widget
DW = widgets.FloatText(layout=widgets.Layout(width='0px', height='0px'))

#create button widget
START = widgets.Button(
    description='Test',
    disabled=False,
    button_style='', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Test',
    icon='check'
)

def on_start_button_clicked(b):
    #This is a workaround to have intreactive_output call the callback:
    #   force the value of the dummy widget to change
    if DW.value> 0 :
        DW.value = -1
    else:
        DW.value = 1
    pass
START.on_click(on_start_button_clicked)

# Define type of method
selm = widgets.Dropdown(
    options= [('Imposta K e L','Set K and L'), ('Imposta gli autovalori','Set the eigenvalues')],
    value= 'Set the eigenvalues',
    description='',
    disabled=False
)

# Define the number of complex eigenvalues
sele = widgets.Dropdown(
    options= [('0 autovalori complessi','0 complex eigenvalues'), ('2 autovalori complessi','2 complex eigenvalues')],
    value= '2 complex eigenvalues',
    description='Autovalori complessi:',
    style = {'description_width': 'initial'},
    disabled=False
)

#define type of ipout
selu = widgets.Dropdown(
    options=[('impulso','impulse'), ('gradino','step'), ('sinusoide','sinusoid'), ('onda quadra','square wave')],
    value='step',
    description='Riferimento:',
    style = {'description_width': 'initial'},
    disabled=False
)
# Define the values of the input
u = widgets.FloatSlider(
    value=30*numpy.pi/180,
    min=0,
    max=45*numpy.pi/180,
    step=0.05,
    description='Riferimento:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.1f',
)
period = widgets.FloatSlider(
    value=0.5,
    min=0.001,
    max=10,
    step=0.001,
    description='Periodo: ',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.2f',
)

gain_w2 = widgets.FloatText(
    value=1.,
#     description='',
    description='Guadagno inverso del riferimento:',
    style = {'description_width': 'initial'},
    disabled=True
)

simTime = widgets.FloatText(
    value=0.5,
    description='Tempo di simulazione (s):',
    style = {'description_width': 'initial'},
    disabled=False
)
```

```python
# Support functions

def eigen_choice(sele):
    if sele == '0 complex eigenvalues':
        eig1c.children[0].children[0].disabled = False
        eig2c.children[1].children[0].disabled = True
        eig1o.children[0].children[0].disabled = False
        eig2o.children[1].children[0].disabled = True
        eig = 0
    if sele == '2 complex eigenvalues':
        eig1c.children[0].children[0].disabled = True
        eig2c.children[1].children[0].disabled = False
        eig1o.children[0].children[0].disabled = True
        eig2o.children[1].children[0].disabled = False
        eig = 2
    return eig

def method_choice(selm):
    if selm == 'Set K and L':
        method = 1
        sele.disabled = True
    if selm == 'Set the eigenvalues':
        method = 2
        sele.disabled = False
    return method
```

```python
numU = 1

def main_callback2(Aw, Bw, X0w, K, L, eig1c, eig2c, eig3c, eig1o, eig2o, eig3o, u, period, selm, sele, selu, simTime, DW):
    eige = eigen_choice(sele)
    method = method_choice(selm)

    if method == 1:
        solc = numpy.linalg.eig(A-B[:,0:numU]*K)
        solo = numpy.linalg.eig(A-L*C)
    if method == 2:
        if eige == 0:
            K = control.acker(A, B[:,0:numU], [eig1c[0,0], eig2c[0,0], eig3c[0,0]])
            Kw.setM(K)
            L = control.place(A.T, C.T, [eig1o[0,0], eig2o[0,0], eig3o[0,0]]).T
            Lw.setM(L)
        if eige == 2:
            K = control.acker(A, B[:,0:numU], [eig3c[0,0],
                                     numpy.complex(eig2c[0,0],eig2c[1,0]),
                                     numpy.complex(eig2c[0,0],-eig2c[1,0])])
            Kw.setM(K)
            L = control.place(A.T, C.T, [eig3o[0,0],
                                         numpy.complex(eig2o[0,0],eig2o[1,0]),
                                         numpy.complex(eig2o[0,0],-eig2o[1,0])]).T
            Lw.setM(L)

    sys = control.ss(A,B,numpy.vstack((C,numpy.zeros((B.shape[1],C.shape[1])))),numpy.vstack((D,numpy.eye(B.shape[1]))))
    sysC = control.ss(numpy.zeros((1,1)),
                      numpy.zeros((1,numpy.shape(A)[0])),
                      numpy.zeros((numpy.shape(B[:,0:numU])[1],1)),
                      -K)

    sysE = control.ss(A-L*C,
                      numpy.hstack((L,B-L*D)),
                      numpy.eye(numpy.shape(A)[0]),
                      numpy.zeros((A.shape[0],C.shape[0]+B.shape[1])))

    sys_append = control.append(sys, sysE, sysC, control.ss(A,B,numpy.eye(A.shape[0]),numpy.zeros((A.shape[0],B.shape[1]))))
    Q = []
    # y in ingresso a sysE
    for i in range(C.shape[0]):
        Q.append([B.shape[1]+i+1, i+1])
    # u in ingresso a sysE
    for i in range(B.shape[1]):
        Q.append([B.shape[1]+C.shape[0]+i+1, C.shape[0]+i+1])
    # u in ingresso a sys
    for i in range(B[:,0:numU].shape[1]):
        Q.append([i+1, C.shape[0]+B.shape[1]+A.shape[0]+i+1])
    # u in ingresso al sistema che ha come uscite gli stati reali
    for i in range(B.shape[1]):
        Q.append([2*B.shape[1]+C.shape[0]+A.shape[0]+i+1, C.shape[0]+i+1])
    # xe in ingresso a sysC
    for i in range(A.shape[0]):
        Q.append([2*B.shape[1]+C.shape[0]+i+1, C.shape[0]+B.shape[1]+i+1])

    inputv = [i+numU+1 for i in range(B[:,numU:].shape[1])]
    outputv = [i+1 for i in range(numpy.shape(sys_append.C)[0])]
    # in order to avoid singular value exception
    try:
        sys_CL = control.connect(sys_append, Q, inputv, outputv)
    except:
        sys_CL = control.connect(sys_append, Q, inputv, outputv)

    dcgain = control.dcgain(sys_CL[0,0])
    gain_w2.value = dcgain
    if dcgain != 0:
        u1 = u/gain_w2.value
    else:
        print('Il guadagno impostato per il riferimento è 0 e quindi viene cambiato a 1')
        u1 = u/1
    print('Il guadagno statico del sistema in anello chiuso (dal riferimento all\'uscita) è: %.5f' %dcgain)

    X0w1 = numpy.zeros((A.shape[0],1))
    for j in range(A.shape[0]):
        X0w1 = numpy.vstack((X0w1,X0w[j]))
    X0w1 = numpy.vstack((X0w1,numpy.zeros((A.shape[0],1))))
    if simTime != 0:
        T = numpy.linspace(0, simTime, 10000)
    else:
        T = numpy.linspace(0, 1, 10000)

    if selu == 'impulse': #selu
        U = [0 for t in range(0,len(T))]
        U[0] = u
        U1 = [0 for t in range(0,len(T))]
        U1[0] = u1
        T, yout, xout = control.forced_response(sys_CL[:,0],T,U1,X0w1)
    if selu == 'step':
        U = [u for t in range(0,len(T))]
        U1 = [u1 for t in range(0,len(T))]
        T, yout, xout = control.forced_response(sys_CL[:,0],T,U1,X0w1)
    if selu == 'sinusoid':
        U = u*numpy.sin(2*numpy.pi/period*T)
        U1 = u1*numpy.sin(2*numpy.pi/period*T)
        T, yout, xout = control.forced_response(sys_CL[:,0],T,U1,X0w1)
    if selu == 'square wave':
        U = u*numpy.sign(numpy.sin(2*numpy.pi/period*T))
        U1 = u1*numpy.sign(numpy.sin(2*numpy.pi/period*T))
        T, yout, xout = control.forced_response(sys_CL[:,0],T,U1,X0w1)
    # N.B. i primi 3 stati di xout sono quelli del sistema, mentre gli ultimi 3 sono quelli dell'osservatore

    step_info_dict = control.step_info(sys_CL[0,0],SettlingTimeThreshold=0.02,T=T)
    print('Step info: \n\tTempo di salita =',step_info_dict['RiseTime'],'\n\tTempo di assestamento (2%) =',step_info_dict['SettlingTime'],'\n\tOvershoot (%)=',step_info_dict['Overshoot'])
    print('Massimo valore di U (% di 5A)=', max(abs(yout[C.shape[0]]))/(5)*100)

    fig = plt.figure(num='Simulation1', figsize=(14,12))

    fig.add_subplot(221)
    plt.title('Risposta dell\'uscita')
    plt.ylabel('Uscita')
    plt.plot(T,yout[0],T,U,'r--')
    plt.xlabel('$t$ [s]')
    plt.legend(['$y$','Riferimento'])
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.grid()

    fig.add_subplot(222)
    plt.title('Ingresso')
    plt.ylabel('$u$')
    plt.plot(T,yout[C.shape[0]],T,[5 for i in range(len(T))],'r--')
    plt.xlabel('$t$ [s]')
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.grid()

    fig.add_subplot(223)
    plt.title('Risposta degli stati')
    plt.ylabel('Stati')
    plt.plot(T,yout[C.shape[0]+B.shape[1]+B[:,numU:].shape[1]+A.shape[0]],
             T,yout[C.shape[0]+B.shape[1]+B[:,numU:].shape[1]+A.shape[0]+1],
             T,yout[C.shape[0]+B.shape[1]+B[:,numU:].shape[1]+A.shape[0]+2])
    plt.xlabel('$t$ [s]')
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.legend(['$x_{1}$','$x_{2}$','$x_{3}$'])
    plt.grid()

    fig.add_subplot(224)
    plt.title('Errori di stima')
    plt.ylabel('Errori')
    plt.plot(T,yout[C.shape[0]+B.shape[1]+B[:,numU:].shape[1]+A.shape[0]]-yout[C.shape[0]+B.shape[1]],
             T,yout[C.shape[0]+B.shape[1]+B[:,numU:].shape[1]+A.shape[0]+1]-yout[C.shape[0]+B.shape[1]+1],
             T,yout[C.shape[0]+B.shape[1]+B[:,numU:].shape[1]+A.shape[0]+2]-yout[C.shape[0]+B.shape[1]+2])
    plt.xlabel('$t$ [s]')
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.legend(['$e_{1}$','$e_{2}$','$e_{3}$'])
    plt.grid()
    #plt.tight_layout()

alltogether2 = widgets.VBox([widgets.HBox([selm,
                                          sele,
                                          selu]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([widgets.Label('K:',border=3), Kw,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('Autovalori:',border=3),
                                          eig1c,
                                          eig2c,
                                          eig3c,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('X0 stim.:',border=3), X0w]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([widgets.Label('L:',border=3), Lw,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('Autovalori:',border=3),
                                          eig1o,
                                          eig2o,
                                          eig3o,
                                          widgets.Label(' ',border=3)]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([
#                                 widgets.VBox([widgets.Label('Inverse reference gain:',border=3),
#                                 widgets.Label('Simulation time [s]:',border=3)]),
                            widgets.VBox([gain_w2,simTime])]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([u,
                                          period,
                                          START])])
out2 = widgets.interactive_output(main_callback2, {'Aw':Aw, 'Bw':Bw, 'X0w':X0w, 'K':Kw, 'L':Lw,
                                                 'eig1c':eig1c, 'eig2c':eig2c, 'eig3c':eig3c, 'eig1o':eig1o, 'eig2o':eig2o, 'eig3o':eig3o,
                                                 'u':u, 'period':period, 'selm':selm, 'sele':sele, 'selu':selu, 'simTime':simTime, 'DW':DW})
out2.layout.height = '870px'
display(out2, alltogether2)
```

    Output(layout=Layout(height='870px'))

    VBox(children=(HBox(children=(Dropdown(index=1, options=(('Imposta K e L', 'Set K and L'), ('Imposta gli autov…


```python
from IPython.core.display import HTML
from IPython.display import Image
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
""")
```

<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>

# *Circuitos Elétricos I - Semana 8*

# Circuitos de segunda ordem

## Circuito RLC em paralelo

### Aplicação das Leis de Kirchhoff

$$\begin{align}
   i_R + i_L + i_C &= i_s\\
   \frac{v}{R} + i_L + C\frac{d v}{d t} &= i_s\nonumber\\
    \frac{1}{R}\left[L\frac{d i_L}{d t}\right] + i_L + C \frac{d}{d t}\left[L\frac{d i_L}{d t}\right]  &= i_s\nonumber\\
    \frac{d^{2} i_L}{d t^{2}}+\frac{1}{RC} \frac{d i_L}{d t}+\frac{1}{L C} i_L &=\frac{i_s}{LC}
   \end{align}
$$

com $\alpha = \frac{1}{2RC}$ e $\omega_0 = \frac{1}{\sqrt{LC}}$, temos

$$
\begin{equation}\label{EDO1}
\frac{d^{2} i_L}{d t^{2}}+2\alpha \frac{d i_L}{d t}+\omega_0^2 i_L=i_{\mathrm{s}}\omega_0^2
\end{equation}
$$

A equação \ref{EDO1} é uma EDO de segunda ordem, linear e de coeficientes constantes.

Equação característica:

$$\begin{equation} s^2+2\alpha s + \omega_0^2=0 \end{equation} $$

Raízes da equação característica:

$$
\begin{align}
s_1 &=-\alpha +\sqrt{\alpha^2-\omega_0^2}\\
s_2 &=-\alpha -\sqrt{\alpha^2-\omega_0^2}
\end{align}
$$

### Resposta total

A resposta transiente de circuitos RLC em paralelo, seja ela natural ($i_L(\infty)=0$) ou ao degrau ($i_L(\infty)=i_s$), pode ser classificada nas três casos seguintes. Em todos os casos, assume-se que o transiente teve início no instante $t_0$ (instante do chaveamento).

#### Caso superamortecido ($\alpha>\omega_0\Rightarrow \text{raízes reais e distintas}$):

Solução geral da EDO:

$$ \begin{equation} i_L(t) =A_{1} e^{s_{1} (t-t_0)}+A_{2} e^{s_{2} (t-t_0)}+i_L(\infty) \end{equation}$$

Condições iniciais para $i_L(t)$:

$$
\begin{align}
i_L(t_0^+) &=A_1 + A_2 + i_L(\infty)\\
\frac{di_L(t_0^+)}{dt} &=s_1A_1 + s_2A_2 = \frac{1}{L} v_C(t_0^+)
\end{align}
$$

Solução do sistema em $A_1$ e $A_2$:

$$
\begin{align}
A_{1} &=\frac{\frac{1}{L} v_C(t_0^+)-s_{2}\left[i_L(t_0^+)-i_L(\infty)\right]}{s_{1}-s_{2}} \\
A_{2} &=\frac{-\frac{1}{L} v_C(t_0^+)+s_{1}\left[i_L(t_0^+)-i_L(\infty)\right]}{s_{1}-s_{2}}
\end{align}
$$

#### Caso subamortecido ($ \alpha<\omega_0\Rightarrow\text{raízes complexas e conjugadas} $):

Fazendo $\omega_d = \sqrt{\omega_0^2-\alpha^2}$ temos $s_1 = -\alpha +j\omega_d$ e $s_2 = -\alpha -j\omega_d$.

Solução geral da EDO:

$$\begin{equation} i_L(t) = e^{-\alpha(t-t_0)}\left\lbrace B_{1}\cos[\omega_d (t-t_0)] + B_{2}\sin[\omega_d (t-t_0)]  \right\rbrace + i_L(\infty) \end{equation}$$

Condições iniciais para $i_L(t)$:

$$
\begin{align}
i_L(t_0^+) &=B_1 + i_L(\infty)\\
\frac{di_L(t_0^+)}{dt} &= -\alpha B_1 +\omega_d B_2 = \frac{1}{L} v_C(t_0^+)
\end{align}
$$

Solução do sistema em $B_1$ e $B_2$:

$$
\begin{align}
B_{1} &=i_L(t_0^+)-i_L(\infty) \\
B_{2} &=\frac{\frac{1}{L} v_C(t_0^+)+\alpha\left[i_L(t_0^+)-i_L(\infty)\right]}{\omega_d}
\end{align}
$$

#### Caso criticamente amortecido ($\alpha=\omega_0\Rightarrow \text{raízes reais e idênticas } s_1=s_2=-\alpha$):

Solução geral da EDO:

$$ \begin{equation} i_L(t) =D_{1} e^{-\alpha(t-t_0)}+D_{2}(t-t_0) e^{-\alpha(t-t_0)}+i_L(\infty) \end{equation}$$

Condições iniciais para $i_L(t)$:

$$
\begin{align}
i_L(t_0^+) &=D_1 + i_L(\infty)\\
\frac{di_L(t_0^+)}{dt} &=-\alpha D_1 + D_2 = \frac{1}{L} v_C(t_0^+)
\end{align}
$$

Solução do sistema em $D_1$ e $D_2$:

$$
\begin{align}
D_{1} &=i_L(t_0^+)-i_L(\infty) \\
D_{2} &= \frac{1}{L} v_C(t_0^+)+\alpha\left[i_L(t_0^+)-i_L(\infty)\right]
\end{align}
$$

### Problema 1

Para o circuito a seguir, as chaves $ch1$ e $ch2$ estiveram abertas por um longo intervalo de tempo.

a. Considerando que apenas $ch1$ fecha em $t_0=0$s, determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.\
b. Qual o máximo valor (absoluto) atingido pela tensão aplicada aos terminais do capacitor para $t\geq 0^+$? Em que instante ele ocorre?\
c. Considerando que apenas $ch2$ fecha em $t_0=0$s, determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.

Link para a simulação do circuito: https://tinyurl.com/yf8pj57t

```python
import numpy as np
import sympy as sp
from utils import round_expr, symdisp, symplot
```

```python
help(symdisp)
```

    Help on function symdisp in module utils:

    symdisp(expr, var, unit=' ')
        Latex style display of sympy expressions

        :param expr: expression in latex [string]
        :param var: sympy variable, function, expression.
        :param unit: string indicating unit of var [string]

### Resolvendo o problema com o código em Python

**Resolução item (a):**

**Calcula $\alpha$ e $\omega_0$**

```python
# Parâmetros do circuito:
R = 1.64
L = 6e-3
C = 0.5e-3

# calculando α e ω0
α  = 1/(2*R*C)
ω0 = 1/np.sqrt(L*C)

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

$\displaystyle α =609.76\; rad/s$

$\displaystyle ω_0 =577.35\; rad/s$

**Define os valores iniciais $v_c(t_0^+)$ e $i_L(t_0^+)$ e o valor final $i_L(\infty)$ (obtidos da análise do circuito)**

```python
# informações obtidas pela análise do circuito

t0 = 0 # instante do chaveamento

iL_inf = -4 # valor final da corrente no indutor
iL_t0 = 2   # valor inicial da corrente no indutor
vc_t0 = 0   # valor inicial da tensão no capcitor
```

**Calcula as raízes da equação característica $s_1$ e $s_2$**

```python
# raízes da equação característica
s1 = -α + np.sqrt(α**2-ω0**2)
s2 = -α - np.sqrt(α**2-ω0**2)

print('Raízes da equação característica:')
symdisp('s_1 =', round(s1, 2), ' rad/s')
symdisp('s_2 =', round(s2, 2), ' rad/s')

# define variáveis da solução geral
t, A1, A2 = sp.symbols('t, A1, A2')

# expressão geral da resposta superamortecida
iL = A1*sp.exp(s1*(t-t0)) + A2*sp.exp(s2*(t-t0)) + iL_inf

print('Forma geral da solução:')
symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

    Raízes da equação característica:

$\displaystyle s_1 =-413.62\; rad/s$

$\displaystyle s_2 =-805.89\; rad/s$

    Forma geral da solução:

$\displaystyle i_L(t) = A_{1} e^{- 413.62 t} + A_{2} e^{- 805.89 t} - 4\;A$

**Resolve o sistema de equações para determinação das constantes $A_1$ e $A_2$**

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq(   A1 +    A2 + iL_inf, iL_t0)
eq2 = sp.Eq(s1*A1 + s2*A2,        vc_t0/L)

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

    Sistema de equações:

$\displaystyle (I): A_{1} + A_{2} - 4 = 2\; $

$\displaystyle (II): - 413.62 A_{1} - 805.89 A_{2} = 0.0\; $

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
A1 = np.array([sol[A1] for sol in soluc])
A2 = np.array([sol[A2] for sol in soluc])

A1 = A1[0]
A2 = A2[0]

print('Solução do sistema:')
symdisp('A_1 =', round(A1, 2), ' A')
symdisp('A_2 =', round(A2, 2), ' A')
```

    Solução do sistema:

$\displaystyle A_1 =12.33\; A$

$\displaystyle A_2 =-6.33\; A$

**Determina a expressão final para $i_L(t)$**

```python
# expressão geral da resposta subamortecida
iL = A1*sp.exp(s1*(t-t0)) + A2*sp.exp(s2*(t-t0)) + iL_inf

symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

$\displaystyle i_L(t) = -4 - 6.33 e^{- 805.89 t} + 12.33 e^{- 413.62 t}\;A$

**Plota gráfico de $i_L(t)$**

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.02,100)
symplot(t,iL, intervalo, 'iL(t)')
```

**Determina $v_C(t)$ a partir de $i_L(t)$**

```python
# tensão aplicada sobre o capacitor (= tensão sobre o indutor)
vC = L*sp.diff(iL, t)

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

$\displaystyle v_C(t) = 30.59 e^{- 805.89 t} - 30.59 e^{- 413.62 t}\; V$

**Plota gráfico de $v_C(t)$**

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.02,100)
symplot(t, vC, intervalo, 'vC(t)')
```

**Resolução item (b):**

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

```python
# define a equação dvC/dt = 0
eq1 = sp.Eq(sp.diff(vC, t),0)

round_expr(eq1, 2)
```

$\displaystyle - 24653.1 e^{- 805.89 t} + 12653.1 e^{- 413.62 t} = 0$

```python
# resolve a equação em t
ts = sp.solveset(eq1, t).args[0]

# calcula o valor de vC(t) para t = ts
vC_ts = vC.evalf(subs={t:ts})

print('ts = %.2e s'%ts)
print('vC(ts) = %.2f V'%vC_ts)
```

    ts = 1.70e-03 s
    vC(ts) = -7.37 V

**Resolução item (c):**

```python
# Parâmetros do circuito:
R = 6
L = 6e-3
C = 0.5e-3

# calculando α e ω0
α  = 1/(2*R*C)
ω0 = 1/np.sqrt(L*C)

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

$\displaystyle α =166.67\; rad/s$

$\displaystyle ω_0 =577.35\; rad/s$

```python
# informações obtidas pela análise do circuito
iL_inf = 0  # valor final da corrente no indutor
iL_t0 = 2   # valor inicial da corrente no indutor
vc_t0 = 0   # valor inicial da tensão no capcitor

t0 = 0 # instante do chaveamento
```

```python
# define a frequência de oscilação amortecida
ωd =np.sqrt(ω0**2-α**2)

symdisp('ω_d =', round(ωd, 2), ' rad/s')

# define variáveis da solução geral
t, B1, B2 = sp.symbols('t, B1, B2')

# expressão geral da resposta subamortecida
iL = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0)) + B2*sp.sin(ωd*(t-t0))) + iL_inf

print('Forma geral da solução:')
symdisp('i_L(t) = ', round_expr(iL,2), ' A')
```

$\displaystyle ω_d =552.77\; rad/s$

    Forma geral da solução:

$\displaystyle i_L(t) = \left(B_{1} \cos{\left(552.77 t \right)} + B_{2} \sin{\left(552.77 t \right)}\right) e^{- 166.67 t}\; A$

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq(   B1 + iL_inf, iL_t0)
eq2 = sp.Eq(-α*B1 + ωd*B2,  vc_t0/L)

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

    Sistema de equações:

$\displaystyle (I): B_{1} = 2\; $

$\displaystyle (II): - 166.67 B_{1} + 552.77 B_{2} = 0.0\; $

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
B1 = np.array([sol[B1] for sol in soluc])
B2 = np.array([sol[B2] for sol in soluc])

B1 = B1[0]
B2 = B2[0]

print('Solução do sistema:')
symdisp('B_1 =', round(B1, 2), ' A')
symdisp('B_2 =', round(B2, 2), ' A')
```

    Solução do sistema:

$\displaystyle B_1 =2.0\; A$

$\displaystyle B_2 =0.6\; A$

```python
# expressão geral da resposta subamortecida
iL = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0))+B2*sp.sin(ωd*(t-t0))) + iL_inf

symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

$\displaystyle i_L(t) = \left(0.6 \sin{\left(552.77 t \right)} + 2.0 \cos{\left(552.77 t \right)}\right) e^{- 166.67 t}\;A$

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, iL, intervalo, 'iL(t)')
```

```python
# tensão aplicada sobre o capacitor (= tensão sobre o indutor)
vC = L*sp.diff(iL, t)
vC = sp.simplify(vC)

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

$\displaystyle v_C(t) = - 7.24 e^{- 166.67 t} \sin{\left(552.77 t \right)}\; V$

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, vC, intervalo, 'vC(t)')
symplot(t, [iL, vC], intervalo, ['iL(t)', 'vC(t)'])
```

## Circuito RLC em série

### Aplicação das Leis de Kirchhoff

$$\begin{align}
   v_R + v_L + v_C &= v_s\\
   R i + L\frac{d i}{dt}+v_C &= v_s\nonumber\\
   R\left[C\frac{dv_C}{dt}\right] + L\frac{d}{dt}\left[C\frac{dv_C}{dt} \right] + v_C &= v_s\nonumber\\
    \frac{d^{2} v_{\mathrm{C}}}{d t^{2}}+\frac{R}{L} \frac{d v_{\mathrm{C}}}{d t}+\frac{1}{L C}   v_{\mathrm{C}} &=\frac{v_{\mathrm{s}}}{LC}
   \end{align}
$$

com $ \alpha = \frac{R}{2L}$ e $\omega_0 = \frac{1}{\sqrt{LC}}$, temos

$$ \begin{equation}\label{EDO2} \frac{d^{2} v_{\mathrm{C}}}{d t^{2}}+2\alpha \frac{d v_{\mathrm{C}}}{d t}+\omega_0^2 v_{\mathrm{C}}=v_{\mathrm{s}}\omega_0^2 \end{equation} $$

A equação \ref{EDO2} é uma EDO de segunda ordem, linear e de coeficientes constantes.

Equação característica:

$$\begin{equation} s^2+2\alpha s + \omega_0^2=0 \end{equation}$$

Raízes da equação característica:

$$
\begin{align}
s_1 &=-\alpha +\sqrt{\alpha^2-\omega_0^2}\\
s_2 &=-\alpha -\sqrt{\alpha^2-\omega_0^2}
\end{align}
$$

### Resposta total

A resposta transiente de circuitos RLC em série, seja ela natural ($v_C(\infty)=0$) ou ao degrau ($v_C(\infty)=v_s$), pode ser classificada nas três casos seguintes. Em todos os casos, assume-se que o transiente teve início no instante $t_0$ (instante do chaveamento).

#### Caso superamortecido ($\alpha>\omega_0\Rightarrow \text{raízes reais e distintas}$):

Solução geral da EDO:

$$\begin{equation} v_{\mathrm{C}}(t) =A_{1} e^{s_{1} (t-t_0)}+A_{2} e^{s_{2} (t-t_0)}+v_{\mathrm{C}}(\infty) \end{equation}$$

Condições iniciais para $v_{\mathrm{C}}(t)$:

$$
\begin{align}
v_{\mathrm{C}}(t_0^+) &=A_1 + A_2 + v_{\mathrm{C}}(\infty)\\
\frac{dv_{\mathrm{C}}(t_0^+)}{dt} &=s_1A_1 + s_2A_2 = \frac{1}{C} i_{\mathrm{L}}(t_0^+)
\end{align}
$$

Solução do sistema em $A_1$ e $A_2$:

$$
\begin{align}
A_{1} &=\frac{\frac{1}{C} i_{\mathrm{L}}(t_0^+)-s_{2}\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]}{s_{1}-s_{2}} \\
A_{2} &=\frac{-\frac{1}{C} i_{\mathrm{L}}(t_0^+)+s_{1}\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]}{s_{1}-s_{2}}
\end{align}
$$

#### Caso subamortecido ($\alpha<\omega_0\Rightarrow \text{raízes complexas e conjugadas}$):

Fazendo $\omega_d = \sqrt{\omega_0^2-\alpha^2}$ temos $s_1 = -\alpha +j\omega_d$ e $s_2 = -\alpha -j\omega_d$.

Solução geral da EDO:

$$\begin{equation} v_{\mathrm{C}}(t) = e^{-\alpha(t-t_0)}\left\lbrace B_{1}\cos[\omega_d (t-t_0)] + B_{2}\sin[\omega_d (t-t_0)]  \right\rbrace + v_{\mathrm{C}}(\infty) \end{equation}$$

Condições iniciais para $v_{\mathrm{C}}(t)$:

$$
\begin{align}
v_{\mathrm{C}}(t_0^+) &=B_1 + v_{\mathrm{C}}(\infty)\\
\frac{dv_{\mathrm{C}}(t_0^+)}{dt} &= -\alpha B_1 +\omega_d B_2 = \frac{1}{C} i_{\mathrm{L}}(t_0^+)
\end{align}
$$

Solução do sistema em $B_1$ e $B_2$:

$$
\begin{align}
B_{1} &=v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty) \\
B_{2} &=\frac{\frac{1}{C} i_{\mathrm{L}}(t_0^+)+\alpha\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]}{\omega_d}
\end{align}
$$

#### Caso criticamente amortecido ($\alpha=\omega_0\Rightarrow \text{raízes reais e idênticas } s_1=s_2=-\alpha$):

Solução geral da EDO:

$$ \begin{equation} v_{\mathrm{C}}(t) =D_{1} e^{-\alpha(t-t_0)}+D_{2}(t-t_0) e^{-\alpha(t-t_0)}+v_{\mathrm{C}}(\infty) \end{equation}$$

Condições iniciais para $v_{\mathrm{C}}(t)$:

$$
\begin{align}
v_{\mathrm{C}}(t_0^+) &=D_1 + v_{\mathrm{C}}(\infty)\\
\frac{dv_{\mathrm{C}}(t_0^+)}{dt} &=-\alpha D_1 + D_2 = \frac{1}{C} i_{\mathrm{L}}(t_0^+)
\end{align}
$$

Solução do sistema em $D_1$ e $D_2$:

$$
\begin{align}
D_{1} &=v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty) \\
D_{2} &= \frac{1}{C} i_{\mathrm{L}}(t_0^+)+\alpha\left[v_{\mathrm{C}}(t_0^+)-v_{\mathrm{C}}(\infty)\right]
\end{align}
$$

### Problema 2

No circuito da figura a seguir, a chave $ch1$ estive aberta por um longo intervalo de tempo antes de fechar, no instante $t_0=0$s.

a. Para qual valor de $R_x$ o circuito exibe uma resposta criticamente amortecida para $t\geq 0^+$?\
b. Considerando que $R_x$ vale exatamente o valor calculado em (a), determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.\
c. Considerando que $R_x$ vale um décimo do valor calculado em (a), determine a tensão sobre o capacitor $v_C(t)$ para $t\geq 0^+$.

Link para a simulação deste circuito: https://tinyurl.com/yjgkkzhu

Resolução item (b):

```python
# Parâmetros do circuito:
R =
L =
C =

# calculando α e ω0
α  = R/(2*L)
ω0 = 1/np.sqrt(L*C)

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

```python
# informações obtidas pela análise do circuito
vC_inf =    # valor final da tensão sobre o capacitor
iL_t0 =   # valor inicial da corrente no indutor
vC_t0 =     # valor inicial da tensão sobre o capacitor

t0 = 0 # instante do chaveamento
```

```python
# raízes da equação característica
s1 = -α
s2 = -α

# define variáveis da solução geral
t, D1, D2 = sp.symbols('t, D1, D2')

# expressão geral da resposta
vC = D1*sp.exp(s1*(t-t0)) + D2*(t-t0)*sp.exp(s2*(t-t0)) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq()
eq2 = sp.Eq()

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
D1 = np.array([sol[D1] for sol in soluc])
D2 = np.array([sol[D2] for sol in soluc])

D1 = D1[0]
D2 = D2[0]

print('Solução do sistema:')
symdisp('D_1 =', round(D1, 2), ' V')
symdisp('D_2 =', round(D2, 2), ' V')
```

```python
# expressão geral da resposta
vC = D1*sp.exp(s1*(t-t0)) + D2*(t-t0)*sp.exp(s2*(t-t0)) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

```python
# corrente passando pelo indutor
iL = C*sp.diff(vC, t)

symdisp('i_L(t) = ', round_expr(iL,2), 'A')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, vC, intervalo, 'vC(t)')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.05,100)
symplot(t, iL, intervalo, 'iL(t)')
```

Resolução item (c):

```python
# Parâmetros do circuito:
R = 65.02
L = 1
C = 100e-6

# calculando α e ω0
α  = R/(2*L)
ω0 = 1/np.sqrt(L*C)

symdisp('α =', round(α, 2), ' rad/s')
symdisp('ω_0 =', round(ω0, 2), ' rad/s')
```

$\displaystyle α =32.51\; rad/s$

$\displaystyle ω_0 =100.0\; rad/s$

```python
# informações obtidas pela análise do circuito
vC_inf = 75.84   # valor final da tensão sobre o capacitor
iL_t0 = 0        #  valor inicial da corrente no indutor
vC_t0 = 21.3     # valor inicial da tensão sobre o capacitor

t0 = 0 # instante do chaveamento
```

```python
# define a frequência de oscilação amortecida
ωd =np.sqrt(ω0**2-α**2)

symdisp('ω_d =', round(ωd, 2), ' rad/s')

# define variáveis da solução geral
t, B1, B2 = sp.symbols('t, B1, B2')

# expressão geral da resposta subamortecida
vC = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0)) + B2*sp.sin(ωd*(t-t0))) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

$\displaystyle ω_d =94.57\; rad/s$

$\displaystyle v_C(t) = \left(B_{1} \cos{\left(94.57 t \right)} + B_{2} \sin{\left(94.57 t \right)}\right) e^{- 32.51 t} + 75.84\; V$

```python
# define os sistema de equações com as condições iniciais
eq1 = sp.Eq(   B1 + vC_inf, vC_t0)
eq2 = sp.Eq(-α*B1 + ωd*B2,  iL_t0/C)

print('Sistema de equações:')
symdisp('(I): ', round_expr(eq1, 2),' ')
symdisp('(II): ', round_expr(eq2, 2),' ')
```

    Sistema de equações:

$\displaystyle (I): B_{1} + 75.84 = 21.3\; $

$\displaystyle (II): - 32.51 B_{1} + 94.57 B_{2} = 0.0\; $

```python
# resolve o sistema
soluc = sp.solve((eq1, eq2), dict=True)
B1 = np.array([sol[B1] for sol in soluc])
B2 = np.array([sol[B2] for sol in soluc])

B1 = B1[0]
B2 = B2[0]

print('Solução do sistema:')
symdisp('B_1 =', round(B1, 2), ' V')
symdisp('B_2 =', round(B2, 2), ' V')
```

    Solução do sistema:

$\displaystyle B_1 =-54.54\; V$

$\displaystyle B_2 =-18.75\; V$

```python
# expressão geral da resposta subamortecida
vC = sp.exp(-α*(t-t0))*(B1*sp.cos(ωd*(t-t0)) + B2*sp.sin(ωd*(t-t0))) + vC_inf

symdisp('v_C(t) = ', round_expr(vC, 2), ' V')
```

$\displaystyle v_C(t) = \left(- 18.75 \sin{\left(94.57 t \right)} - 54.54 \cos{\left(94.57 t \right)}\right) e^{- 32.51 t} + 75.84\; V$

```python
# corrente passando pelo indutor
iL = C*sp.diff(vC, t)

symdisp('i_L(t) = ', round_expr(iL.simplify(),5), 'A')
```

$\displaystyle i_L(t) = 0.57673 e^{- 32.51 t} \sin{\left(94.56796 t \right)}\;A$

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.25,100)
symplot(t, vC, intervalo, 'vC(t)')
```

```python
# plota gráfico da função
intervalo = np.linspace(t0,t0+0.25,100)
symplot(t, iL, intervalo, 'iL(t)')
```

Jupyter notebooks/Circuitos Eletricos I - Semana 8.2.ipynb


### Web-based Tools for Teaching and Research: Jupyter Notebooks and GitHub
A workshop of the Academy of Data Sciences at the College of Science
## Jupyter Notebooks & Python

Susanna Werth

---

# Lesson 6: Markdown & Magic

This lesson contains selected examples for **markdown code** and **magic comands** usful in jupyter notebooks.

---
# Writing Notebooks with Markdown

## Standard Markdown

To find out how to edit text nicely in markdown cells the Jupyter lab provides a great reference.

Go to top **Menu > Help > Markdown Reference**
and study the options, or make the tutorial.

## Add code to your markdown cell
This code will not be executed, but it can help to display it here.

``` python
some code in python
```

```markdown
# This works also for a big headline in markdown
```

## Embed images and adjust their size
The markdown reference indicates markdown code to embed an image (including the path to the subfolder where image is located).

```markdown

```
However, this command does not allow to adjust the size.

For that we have to use html code:

```html

```

which will include the image like this:

## Add highlighted boxes

For highlighted boxes, we can use a trick and enter html-warnings like this:

```html
<div class="alert alert-info">

**Box**

- Generates a highlighted box.
</div>
```

after executing the markdown code above in the markdown cell it will look  this:

<div class="alert alert-info">

**Box**

- Generates a highlighted box.
</div>

We can get different colors with different alert levels:
- alert-danger: red
- alert-success: green
- alert-info: blue
- alert-warning: yellow/orange

Try them in the markdown cell below!

---
# Using Magic Commands in a Notebook

Jupyter notebook code cells can contain special commands which are not valid Python code but affect the behavior of the notebook. These special commands are called **magic commands**.

## Bash commands

You can execute bash commands directly from a notebook, by entering an exclamation mark at the beginning of line:

```python
! pwd   # returns the current path
```

    /Users/swerth/Dropbox (Personal)/MyDropbox/Documents/Work/Teaching/Workshops/Material_JupyterWorkshop/JupyterMaterial

```python
! mkdir myNewFolder # makes new folder
```

```python
! rmdir myNewFolder # deletes the same folder
```

Tomorrow we will learn how to use git and GitHub. Remember, that you will be able to pull and push content using git, directly from a Jupyter notebook with this option.# Read a list of files with bash command directly into a variable
names = !ls ./img/*.png
names

```python
# Read a list of files with bash command directly into a variable
names = !ls ./img/*.png
names
```

    ['./img/indexing_lists_python.png',
     './img/jupyter.png',
     './img/loops_image_num.png',
     './img/python-operations-across-axes.png',
     './img/slicing_lists_python.png']

### Entire cell with bash commands

```bash
%%bash
echo "HELLO WORLD!"
```

    HELLO WORLD!

---
## More magic

Discriminate between
- line magics: start with `%` or `!`
- cell magics: start with `%%`

```python
# list available python magics
%lsmagic
```

    Available line magics:
    %alias  %alias_magic  %autoawait  %autocall  %automagic  %autosave  %bookmark  %cat  %cd  %clear  %colors  %conda  %config  %connect_info  %cp  %debug  %dhist  %dirs  %doctest_mode  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %lf  %lk  %ll  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %lx  %macro  %magic  %man  %matplotlib  %mkdir  %more  %mv  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %pip  %popd  %pprint  %precision  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %rep  %rerun  %reset  %reset_selective  %rm  %rmdir  %run  %save  %sc  %set_env  %store  %sx  %system  %tb  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode

    Available cell magics:
    %%!  %%HTML  %%SVG  %%bash  %%capture  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%markdown  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile

    Automagic is ON, % prefix IS NOT needed for line magics.

A good reference for the many magic commands are the iPython documentation pages: https://ipython.readthedocs.io/en/stable/interactive/magics.html

---
### Execute or load external Python scripts

```python
# uncomment the line below, execute and see what happens
```

```python
# %run ./data/fahr_to_celcius.py
```

```python
# %load ./data/fahr_to_celcius.py
```

### Embed HTML

You can embed html code in your markdown cell like this:

```python
%%HTML
<button type="button" id="loading-example-btn" data-loading-text="Loading..." class="btn btn-primary">
  Loading state
</button>
```

<button type="button" id="loading-example-btn" data-loading-text="Loading..." class="btn btn-primary">
  Loading state
</button>

### Embed Latex

You can embed latex code, for example to add equations, in your markdown cell like this:

```latex
%%latex
\begin{align}
    \frac{\partial u}{\partial t} + \nabla \cdot \left( \boldsymbol{v} u - D\nabla u \right) = f
\end{align}
```

\begin{align}
    \frac{\partial u}{\partial t} + \nabla \cdot \left( \boldsymbol{v} u - D\nabla u \right) = f
\end{align}

Find more awesome magic here: https://arogozhnikov.github.io/2016/09/10/jupyter-features.html

---
## Embed Youtube Videos & Images with Python code

```python
from IPython.display import YouTubeVideo
YouTubeVideo('NGFO0kdbZmk')
```

```python
from IPython.display import Image
Image(filename='./img/jupyter.png',width=200,height=200)
```

You can do much more with Jupyter Notebooks and there is a vast amount of tutorials, cheat sheets and documentation pages out there. Below some pages to start:

- Nbviewer documentation pages: https://nbviewer.jupyter.org/github/ipython/ipython/blob/master/examples/IPython%20Kernel/Index.ipynb
- Blog on Jupyter Notebook features: https://arogozhnikov.github.io/2016/09/10/jupyter-features.html