```python
### Running in Google Colab? You'll want to uncomment and run these cell once each time you start this notebook.

"""
!wget https://raw.githubusercontent.com/psheehan/CIERA-HS-Program/master/Projects/SGRB-AfterglowModeling/t90.txt
!wget https://raw.githubusercontent.com/psheehan/CIERA-HS-Program/master/Projects/SGRB-AfterglowModeling/090510_OmuJy.txt
!wget https://raw.githubusercontent.com/psheehan/CIERA-HS-Program/master/Projects/SGRB-AfterglowModeling/130603b_OmuJy.txt
!wget https://github.com/psheehan/CIERA-HS-Program/blob/master/Projects/SGRB-AfterglowModeling/multiwave_OmuJy.txt
"""
```

# Short Gamma-Ray Burst Afterglow Fitting

```python
# Loading some packages to get us started

%load_ext autoreload
%autoreload 2
%matplotlib inline
%config InlineBackend.figure_format='retina'

from IPython.display import Image
import numpy as np
import matplotlib.pyplot as plt
import astropy.io.ascii as ascii
from astropy import cosmology
from astropy import units as u
from astropy.cosmology import FlatLambdaCDM

cosmo = FlatLambdaCDM(H0=70 * u.km / u.s / u.Mpc, Tcmb0=2.725 * u.K, Om0=0.3)
```

# Introduction

---

## Gamma-Rays

Consider the electromagnetic spectrum:

On the far right end of the spectrum, we see radio waves. Radio waves are at the lowest energy end of the spectrum. As we move across the spectrum to the left, we increase in energy and frequency. Visible light appears near the middle of the spectrum. Gamma-rays are the highest energy class of light and are only produced on Earth by high energy processes like fusion or gamma decay.

---

## Gamma-Ray Bursts

In the 1960s, astronomers started detecting bursts of gamma-ray emission. The high energy flashes did not repeat and were randomly scattered across the sky. After much work, astronomers realized that these bursts of gamma-rays were coming from other galaxies. To produce the high energy $\textbf{photons}$ (packets of light) that make up gamma-ray bursts (GRBs) a source with a lot of energy is needed! All of these clues led astronomers to believe that are the result of explosions in our universe.

GRBs are not $\textbf{isotropic}$ (spherically symmetric). In fact, much of the explosion's energy is focused along the two poles. The beam that the gamma-rays are accelerated along is referred to as the $\textbf{jet}$. The structure of the explosion means that in order to detect a GRB we must be directly in the path of the GRB.

# Exercise 1

## Short vs. Long Gamma-Ray Bursts (GRBs)

It became clear that there were two populations of GRBs - those with short durations and long durations. Below, we will plot the durations of GRBs discovered by the space telescope $\textit{Swift}$ from January 2004 to January 2020. Your job is to run the two cells below, examine the plot and choose a cutoff between short and long duration GRBs.

```python
# data file includes a list of GRBs and the duration of the burst, T90
swift_cat = ascii.read('t90.txt')

# T90 is the amount of time for 90% of the burst's light to be absorbed
t90 = swift_cat['T90']

# If you print the list of GRB names, you'll see that each GRB is named by the date
# it was detected on.
grb_name = swift_cat['GRBname']
```

```python
b=np.logspace(-1,3,40)
plt.hist(t90,b,color='xkcd:lilac')
plt.loglog()
plt.tick_params(direction = 'in', length = 10, labelsize = 16, right = True)
plt.tick_params(which = 'minor', direction = 'in', length = 6,  right = True)
plt.xlabel('Time (seconds)')
plt.ylabel('Count')
plt.title('Histogram of Burst Duration')
plt.show()
```

#### Question 1: Looking at the plot, are you convinced that there are two distinct populations of GRBs? Why?

Answer:

#### How many seconds would you choose as a cutoff between short and long GRBs? Keep in mind that the axes are in 'log' units.

Answer:

In reality, we use a few other parameters to classify GRBs as short or long. However, duration of the burst is still a useful tool to classify GRBs.

For this project, we will be focusing on short gamma-ray bursts.

## Gamma-Ray Burst Afterglows

Accompanying each GRB is a multiwavelength afterglow. Afterglows as produced by fast-moving ejecta from the explosion colliding with gas that exists in the space between stars. The relativistic (aka VERY fast) collisions between the ejecta and the gas produce what is called $\textbf{synchrotron emission}$. Though we won't go into the mechanics of synchrotron emission here, it's important to know that it creates a $\textbf{jetted multiwavelength afterglow}$. Below is a diagram of an top hat afterglow. In the far right part of the diagram, you see the explosion (jet) colliding with the gas (ambient medium) to produce an afterglow.

Studying observations of GRB afterglows can teach us about the properties of the burst, including how dense the environment around the explosion is. Below are answers to questions you may have as you simulate afterglow lightcurves.

#### 1. What does multiwavelength mean?
Multiwavelength means that light is emitted in more than one part of the electromagnetic spectrum (see the first figure!). An afterglow emits in the radio, the visible (hereafter called "optical") and the X-ray.

Identifying the multiwavelength components of the afterglow is important because our X-ray, optical and radio detectors are better at localizing sources than gamma-ray detectors. A better localization allows astronomers to study the host environment of the burst and build a better understanding of what precipitated the explosion.

#### 2. What is a lightcurve?
A lightcurve is a plot of time (x-axis) vs brightness (y-axis). Lightcurves are a ubiquitous tool for studying time-domain astronomy. A lightcurve shows us if a source dims, brightens, or stays the same over time. For a steady source like a galaxy, a lightcurve will be flat. A supernova's lightcurve will rise quickly and then fall over time. The lightcurve of a variable star (a star which brightens and dims periodically) will look like this:

Short GRB afterglow lightcurves are most like supernova lightcurves.

#### 3. What is a jet and why is it important?

A jet is a collimated source of relativistic emission. Among other things, a jet is characterized by it's opening angle and shape.

Both the shape and the opening angle of the jet determine if the afterglow will be detectable to an $\textbf{off-axis}$ (not directly in the path of the jet) observer.

#### 4. What does an afterglow jet look like?

Good question! The short answer is that astronomers don't know for sure yet but have many theories. The simulations we'll be using below allow the user to try out a few different jet shapes. These jet shapes include:

$\textbf{Top Hat Jet}$: flux concentrated equally across a narrow beam, outside of which no flux escapes.

$\textbf{Gaussian Jet}$: Similar to a top hat jet except a greater amount of flux is concentrated in the center of the beam.

$\textbf{Power Law Jet}$: Flux concentrated in the middle of the jet but decays as you move to larger viewing angles.

## Using $\texttt{afterglowpy}$

We'll be using the Python package $\texttt{afterglowpy}$ (Ryan et al. 2015, Ryan et al. 2019) to simulate multiwavelength afterglow (AG) lightcurves and match them to actual observations.

From https://github.com/geoffryan/afterglowpy/blob/master/README.md:

jetType can be:
1. -1 (Top Hat)
2. 0 (Gaussian)
3. 1 (Power Law w/ core)
4. 2 (Gaussian w/ core)
5. 3 (Cocoon)
6. 4 (Smooth Power Law).

# Exercise 2a)

The first three cells are mostly complete and will produce an optical Top Hat jet AG lightcurve plot. Using these cells as an example, produce a AG lightcurves for a Gaussian Jet and a Smooth Power Law Jet.

```python
# in order to do run the cells below, uncomment these two lines, run this cell, and then
# recomment since you only have to do it once!

!pip install afterglowpy

import afterglowpy as grb
```

```python
# Example based on Github code by Geoff Ryan (github.com/geoffryan/afterglowpy/)

# JET PARAMETERS:

thetaObs = 0.05  # Viewing angle in radians
E0 = 1.0e51     # Isotropic-equivalent energy in erg
thetaC = 0.2    # Half-opening angle in radians, range of 0.015 - 0.021 radians
thetaW = 0.1     # Truncation angle, unused for top-hat
b = 0.01           # power law index, unused for top-hat
n0 = 1.0         # circumburst density in cm^{-3}
p = 2.2          # electron energy distribution index
eps_e = 0.1      # epsilon_e
eps_B = 0.01     # epsilon_B
xi_N = 1.0       # Fraction of electrons accelerated
specType = 0 # global cooling time, no inverse compton
q = 0 # keep as 0
ts = 0 # keep as 0
z = 0.356         # redshift, keep this

dist = cosmo.luminosity_distance(z) # outputs distances in Mpc, keep this
dL = dist.value * 3.086e24 # Luminosity distance of burst in cm, keep this
```

```python
# creating a grid of times for the x-axis of our afterglow lightcurves

print("Choose time range")

ta = 2.0 * grb.day2sec
tb = 50 * grb.day2sec
tsec = np.geomspace(ta, tb, num=100)

# Calculate flux in a single optical band (all times have same frequency)
nu = np.empty(tsec.shape)
nu[:] = 4.8e14

# For convenience, place positional arguments in an array and keywords into a dict

Y = np.array([thetaObs, E0, thetaC, thetaW, b, specType, q, ts, n0, p, eps_e, eps_B, xi_N, dL])
Z = {'z': z}

# Calculate!

jetType_top = -1     # top hat jet

print("Calculate Top Hat Jet")
Fnu_top = grb.fluxDensity(tsec, nu, jetType_top, 0, *Y, **Z)

# Change time units back to days

t = tsec * grb.sec2day

# Plot!
print("Plot!")

plt.plot(t,Fnu_top)
plt.yscale('log')
plt.xlabel('Time since Burst (Days)')
plt.ylabel(r'$F_{nu}$ (mJy)')
plt.title('Example 1: Top Hat Optical Afterglow Lightcurve')
plt.show()
```

# Exercise 2b

Calculate Top Hat, Gaussian and Power Law jet afterglow lightcurves.

```python
# Set up array of relevant times
ta = 2.0 * grb.day2sec
tb = 50.0 * grb.day2sec
tsec = np.geomspace(ta, tb, num=100)

# Calculate flux in a single optical band (all times have same frequency)

nu = np.empty(tsec.shape)
nu[:] = 4.8e14 # approximate r-band frequency

# recall number that corresponds to Gaussian jet
jetType_gau =

# recall number that corresponds to Power Law jet
jetType_pl =

# Calculate the brightness of your jet!

print('Calculate Top Hat Jet')
Fnu_top = grb.fluxDensity() # fill in
print('Calculate Gaussian Jet')
Fnu_gau = grb.fluxDensity() # fill in
print('Calculate Power Law Jet')
Fnu_pl = grb.fluxDensity() # fill in

# change time back to days

t = tsec * grb.sec2day

print("Plot!")

plt.plot(t,Fnu_top, ls = ':', lw=4, label = 'Top Hat') # fill in label
plt.plot(t,Fnu_gau, label = 'Gaussian') # fill in label
plt.plot(t,Fnu_pl, ls = '-.', label = 'Power Law') # fill in label
plt.yscale('log')
plt.xlabel('Days since burst') # fill in
plt.ylabel(r'$F_{nu}$ (muJy)') # fill in
plt.title('Afterglow Models')
plt.legend(fontsize=14)
plt.show()
```

# Exercise 3a

Replot your four afterglows from Exercise 2 with the real optical afterglow dataset of the SGRB 130603B. Determine which afterglow jet model best fits the data. Explain why in the cell below.

```python
# Load in SGRB 130603B dataset

obs = np.loadtxt('130603b_OmuJy.txt')

time = obs[:,0]
filt = obs[:,2]
flux = obs[:,3]
ferr = obs[:,4]
det = obs[:,5]

# Add condition to only choose points at a certain wavelengths where something was detected

cond = (filt > 0.5) & (filt < 0.7) & (det == 1)

time=time[cond]
fil = filt[cond]
fl = flux[cond] * 1e-6 # convert micro-Jansky to Jansky to match model units
flerr = ferr[cond] * 1e-6 # convert micro-Jansky to Jansky

# Plotting the data with error bars

plt.errorbar(time,fl,yerr=flerr,capsize=3,fmt='o',color='xkcd:shocking pink')

# Re-plot your Top Hat, Gaussian, Power Law and Gaussian w/ core afterglow models

plt.plot(t, , ls = ':',lw=4, label = 'Top Hat') # fill in
plt.plot(t, , label = 'Gaussian') # fill in
plt.plot(t, , ls = '-.', label = 'Power Law') # fill in
plt.yscale('log')

plt.xlabel('Days since burst')
plt.ylabel(r'$F_{nu}$ (Jy)')
plt.legend()
plt.show()
```

#### Question: Which jet type(s) fit the optical afterglow of GRB 130603B the best?

Answer:

## Exercise 3a Challenge: Choose the model you think fits the data best and adjust the jet parameters (what goes into Y) to determine the best fit by eye.

Recommended: E0, thetaC, thetaW. Try it below and record your best fits to the data.

```python
thetaObs = 0.0  # Viewing angle in radians
E0 = 1.0e53      # Isotropic-equivalent energy in erg
thetaC = 0.1     # Half-opening angle in radians
thetaW = 0.1     # Truncation angle, unused for top-hat
b = .01            # power law index, unused for top-hat
n0 = 1.0         # circumburst density in cm^{-3}
p = 2.2          # electron energy distribution index
eps_e = 0.1      # epsilon_e
eps_B = 0.01     # epsilon_B
xi_N = 1.0       # Fraction of electrons accelerated
specType = 0 # global cooling time, no inverse compton

# repeat process to get new lightcurves

# plot

```

# Exercise 4

Now determine the best energy fit value for SGRB 090510.

```python
thetaObs = 0.05  # Viewing angle in radians
E0 = # choose     # Isotropic-equivalent energy in erg
thetaC = 0.1     # Half-opening angle in radians
thetaW = 0.1     # Truncation angle, unused for top-hat
b = .01            # power law index, unused for top-hat
n0 = 1.0         # circumburst density in cm^{-3}
p = 2.2          # electron energy distribution index
eps_e = 0.1      # epsilon_e
eps_B = 0.01     # epsilon_B
xi_N = 1.0       # Fraction of electrons accelerated
```

```python
# Load in SGRB 090510 dataset (see 130603B for example)

obs = np.loadtxt('090510_OmuJy.txt')

time = obs[:,0]
filt = obs[:,1]
flux = obs[:,2]
ferr = obs[:,3]
det = obs[:,4]

cond =

time =
fil =
fl =
flerr =
```

```python
# Choose time range

ta = grb.day2sec * # choose lower time
tb = grb.day2sec * # choose upper time
tsec = np.geomspace() # write

print('Time range chosen')

# Calculate flux in a single optical band (all times have same frequency)

nu = np.empty(tsec.shape)
nu[:] = 4.8e14 # approximate r-band frequency

Y = np.array([thetaObs, E0, thetaC, thetaW, b, specType, q, ts, n0, p, eps_e, eps_B, xi_N, dL])
Z = {'z': z}

# Calculate!

Fnu_gau =

print("Calculated Gaussian Jet flux")

# Convert time back to days and plot!

t =

print("Plot Observed Data")

plt.errorbar(time,fl,yerr=flerr,capsize=3,fmt='o',color='xkcd:shocking pink')

print("Plot Models")

plt.plot(t,Fnu_gau, label = 'Gaussian')
plt.yscale('log')
plt.xlabel('Time since burst (days)')
plt.ylabel(r'$F_{nu}$ (Jy)')
plt.title('090510 Afterglow Models')
plt.legend(fontsize=14)
plt.show()
```

# Exercise 4

## Creating multiwavelength afterglow lightcurves

SGRB 050724 was the first short gamma-ray burst whose afterglow was detected in the X-ray, optical, near-infrared and radio parts of the EM spectrum. As time went by, astronomers started sampling the afterglows more frequenctly across wavelengths. Here, we will compare the multiwavelength observations from the 130603B with our lightcurves for different jet types. Data from Table 1, Fong, Berger, Metzger et al. 2013 (https://iopscience.iop.org/article/10.1088/0004-637X/780/2/118/pdf).

## Unit conversions

In order to produce lightcurves in the correct units, we must provide the models with the correct units of frequency, $\nu$. We can calculate $\nu$ from wavelength $\lambda$ by:

\begin{equation}
c = \lambda * \nu
\end{equation}

Where $c$ is the speed of light ($c=3*10^{10}$ cm/s), $\nu$ is frequency, and $\lambda$ is wavelength.

Below is the electromagnetic spectrum with approximate values for $\lambda$. As $\texttt{afterglowpy}$ only accepts $\nu$ values, you'll need to convert to frequency to calculate your multiwavelength afterglow lightcurves. Below is an EM spectrum with frequency and wavelength so you can check you have approximately the correct values. However, I'll give you the exact wavelength values below to calculate.

# Part 4a)

Calculating frequency of each band

```python
# calculate X-ray, optical, near infrared and radio frequencies in Hz given

c = 3e10 # units = cm/s

lambda_xray = 1.24e-7 # cm units
nu_xray =

lambda_opt = 0.62e-4 # cm units
nu_opt =

lambda_nir = 1.2e-4 # cm units
nu_nir =

lambda_rad = 4.5e0 # cm units
nu_rad =
```

```python
# Preliminary Jet parameters - leave for now, can play with later!

thetaObs = 0.05  # Viewing angle in radians
E0 = 1.0e51    # Isotropic-equivalent energy in erg
thetaC = 0.1     # Half-opening angle in radians
thetaW = 0.1     # Truncation angle, unused for top-hat
b = .01            # power law index, unused for top-hat
n0 = 1.0         # circumburst density in cm^{-3}
p = 2.2          # electron energy distribution index
eps_e = 0.1      # epsilon_e
eps_B = 0.01     # epsilon_B
xi_N = 1.0       # Fraction of electrons accelerated
specType = 0 # global cooling time, no inverse compton
```

# Part 4b)

Load in observed multiwavelength data and sort it part filter (X-ray, Optical, Near-Infrared, and Radio) by detection (q = 1) or upper limit (q = 0). Upper limits can be useful because they can help us rule out models that are too bright.

```python
# Load in data

obs = np.loadtxt('multiwave_OmuJy.txt')

dt = obs[:,0] # time in days
filt = obs[:,1] # filter in microns (1 micron = 10000 centimeters)

print(filt) # print to see which part of the spectrum the data was taken at

flux = obs[:,2] # flux in microJansky
ferr = obs[:,3] # flux error in microJansky

det = obs[:,4] # detection = 1, upper limit = 0

# Separate data by filter and if detection or upper limit

```

# Part 4c)

Create multiwavelength models using frequencies calculated in Part 4a) and plot with observations.

```python
# Choose appropriate time range

ta = grb.day2sec * # choose lower time
tb = grb.day2sec * # choose upper time
tsec = np.geomspace() # write

# calculate multiwavelength lightcurves

print("Calc Radio")
FnuR = grb.fluxDensity()
print("Calc Optical")
FnuO = grb.fluxDensity()
print("Calc Near Infrared")
FnuN = grb.fluxDensity()
print("Calc X-ray")
FnuX = grb.fluxDensity()

# plot data by filter - make sure to use different markers for detections and upper limits

# plot models

print("Plot")

t =

plt.plot(t,FnuR, label = 'Radio')
plt.plot(t,FnuO, label = 'Optical')
plt.plot(t,FnuN, label = 'Near-Infrared')
plt.plot(t,FnuX, label = 'X-ray')

plt.yscale('log')
plt.xlabel('') # add in
plt.ylabel(r'') # add in
plt.title('') # add in
plt.legend(fontsize=14)
plt.show()
```

# Part 4d)

Play with your Jet parameters and jetType to get the best fit to all of the detections and upper limits.

```python
# Work space for 4d
```

#### Question: Which jet type fits the data best?

Answer:

# Nice job!

If you finished early, here's a challenge:

We have been determining the best models by eye for these exercises. However, in practice astronomers like to use statistics to make sure we are finding the best fit. To do this, we must:

1. Determine how to quantitatively measuring how good a fit is
2. Minimize or maximize this quantity to make sure we have the best fit possible

Feel free to ask more questions! I'm happy to help provide resources for smart ways to do this.

```python

```


```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

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

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)

```

Toggle cell visibility here.

## State feedback control - sledenje referenčni vrednosti

Za sistem definiran z enačbo:

$$
\dot{x}=\underbrace{\begin{bmatrix}-3&4\\0&2\end{bmatrix}}_{A}x+\underbrace{\begin{bmatrix}0\\1\end{bmatrix}}_{B}u.
$$

je dana zahteva, da prva spremenljivka stanj sledi sinusoidni referenčni funkciji s frekvenco 6 rad/s ($\approx 1$ Hz) brez odstopanja v amplitudi.

V prvem koraku dodamo integrator (z uvedbo fiktivne spremenljivke stanje; postopek je razložen v interaktivnem primeru [Krmiljenje povratne zveze stanj - zmogljivost krmiljenja](SS-31-Krmiljenje_povratne_zveze_stanj_zmogljivost)) tako, da preverimo da razširjen sistem ostane vodljiv, kar je pomembno, da se zaprtozančna prenosna funkcija od reference do $x_1$ začne pri vrednosti 0 dB. Končni razširjen sistem tako zapišemo kot:

$$
\dot{x}_a=\underbrace{\begin{bmatrix}-3&4&0\\0&2&0\\1&0&0\end{bmatrix}}_{A_a}x_a+\underbrace{\begin{bmatrix}0\\1\\0\end{bmatrix}}_{B_a}u+\underbrace{\begin{bmatrix}0\\0\\-1\end{bmatrix}}_{B_{\text{ref}}}x_{1r}\,.
$$

Z namenom zagotovitve dani zahtevi je ključno, da si predstavljamo obliko prenosne funkcije, ki zagotavlja zahtevan odziv, tj. 0 dB od $\omega=0$ do najmanj $\omega=6$ in fazo 0 deg v enakem intervalu frekvenc. Ob upoštevanju učinka polov v območje frekvenc izvedn danega intervala je rešitev ta, da prilagodimo pole tako, da le-ti ležijo v območju frekvenc višjih od 65 rad/s.

Izbrani poli so tako $\lambda_{1,2,3}= 65$ rad/s, matrika ojačanja pa $K_a = \begin{bmatrix}3024.75&194&68656.25\end{bmatrix}^T$.

Krmiljeni sistem zapišemo kot:

$$
\dot{x}_a=\underbrace{\begin{bmatrix}-3&4&0\\-3024.75&-192&-68656.25\\1&0&0\end{bmatrix}}_{A_a-B_aK_a}x_a+\underbrace{\begin{bmatrix}0\\1\\0\end{bmatrix}}_{B_a}v+\underbrace{\begin{bmatrix}0\\0\\-1\end{bmatrix}}_{B_{\text{ref}}}x_{1r}
$$

V tem primeru je prikazana simulacija skupaj z Bodejeveim diagramom prenosne funkcije od reference $x_{1r}$ do spremenljivke stanja $x_1$.

### Kako upravljati s tem interaktivnim primerom?
Zanimivo bi bilo doseči tudi odziv brez odstopka v faznem delu signala. Kako daleč je potrebno, za dosego tega scenarija, prestaviti pole?

```python
%matplotlib inline
import control as 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
```

```python
# Preparatory cell

A = numpy.matrix('-3 4 0; 0 2 0; 1 0 0')
B = numpy.matrix('0; 1; 0')
Br = numpy.matrix('0; 0; -1')
C = numpy.matrix('1 0 0')
X0 = numpy.matrix('0; 0; 0')
K = numpy.matrix([842.25,104,10718.75])

Aw = matrixWidget(3,3)
Aw.setM(A)
Bw = matrixWidget(3,1)
Bw.setM(B)
Brw = matrixWidget(3,1)
Brw.setM(Br)
Cw = matrixWidget(1,3)
Cw.setM(C)
X0w = matrixWidget(3,1)
X0w.setM(X0)
Kw = matrixWidget(1,3)
Kw.setM(K)

eig1c = matrixWidget(1,1)
eig2c = matrixWidget(2,1)
eig3c = matrixWidget(1,1)
eig1c.setM(numpy.matrix([-65]))
eig2c.setM(numpy.matrix([[-65],[0]]))
eig3c.setM(numpy.matrix([-65]))
```

```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= ['Nastavi K', 'Nastavi lastne vrednosti'],
    value= 'Nastavi K',
    description='',
    disabled=False
)

# Define the number of complex eigenvalues for the observer
selc = widgets.Dropdown(
    options= ['brez kompleksnih lastnih vrednosti', 'dve kompleksni lastni vrednosti'],
    value= 'brez kompleksnih lastnih vrednosti',
    description='Lastne vrednosti:',
    disabled=False
)

#define type of ipout
selu = widgets.Dropdown(
    options=['impulzna funkcija', 'koračna funkcija', 'sinusoidna funkcija', 'kvadratni val'],
    value='impulzna funkcija',
    description='Vhod:',
    disabled=False,
    style = {'description_width': 'initial'}
)
# Define the values of the input
u = widgets.FloatSlider(
    value=1,
    min=0,
    max=20.0,
    step=0.1,
    description='Referenca:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.1f',
)
period = widgets.FloatSlider(
    value=1,
    min=0.01,
    max=4,
    step=0.01,
    description='Perioda: ',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.2f',
)
```

```python
# Support functions

def eigen_choice(selc):
    if selc == 'brez kompleksnih lastnih vrednosti':
        eig1c.children[0].children[0].disabled = False
        eig2c.children[1].children[0].disabled = True
        eigc = 0
    if selc == 'dve kompleksni lastni vrednosti':
        eig1c.children[0].children[0].disabled = True
        eig2c.children[1].children[0].disabled = False
        eigc = 2
    return eigc

def method_choice(selm):
    if selm == 'Nastavi K':
        method = 1
        selc.disabled = True
    if selm == 'Nastavi lastne vrednosti':
        method = 2
        selc.disabled = False
    return method
```

```python
def main_callback(Aw, Bw, Brw, X0w, K, eig1c, eig2c, eig3c, u, period, selm, selc, selu, DW):
    A, B, Br = Aw, Bw, Brw
    sols = numpy.linalg.eig(A)
    eigc = eigen_choice(selc)
    method = method_choice(selm)

    if method == 1:
        sol = numpy.linalg.eig(A-B*K)
    if method == 2:
        if eigc == 0:
            K = control.acker(A, B, [eig1c[0,0], eig2c[0,0], eig3c[0,0]])
            Kw.setM(K)
        if eigc == 2:
            K = control.acker(A, B, [eig1c[0,0],
                                      numpy.complex(eig2c[0,0],eig2c[1,0]),
                                      numpy.complex(eig2c[0,0],-eig2c[1,0])])
            Kw.setM(K)
        sol = numpy.linalg.eig(A-B*K)
    print('Lastne vrednosti sistema so:',round(sols[0][0],4),',',round(sols[0][1],4),'in',round(sols[0][2],4))
    print('Lastne vrednosti krmiljenega sistema so:',round(sol[0][0],4),',',round(sol[0][1],4),'in',round(sol[0][2],4))

    sys = sss(A-B*K,Br,C,0)
    T = numpy.linspace(0, 6, 1000)

    if selu == 'impulzna funkcija': #selu
        U = [0 for t in range(0,len(T))]
        U[0] = u
        T, yout, xout = control.forced_response(sys,T,U,X0w)
    if selu == 'koračna funkcija':
        U = [u for t in range(0,len(T))]
        T, yout, xout = control.forced_response(sys,T,U,X0w)
    if selu == 'sinusoidna funkcija':
        U = u*numpy.sin(2*numpy.pi/period*T)
        T, yout, xout = control.forced_response(sys,T,U,X0w)
    if selu == 'kvadratni val':
        U = u*numpy.sign(numpy.sin(2*numpy.pi/period*T))
        T, yout, xout = control.forced_response(sys,T,U,X0w)

    fig = plt.figure(num='Bodejev diagram', figsize=(16,10))
    control.bode_plot(sys)
    fig.suptitle('Bodejev diagram', fontsize=16)

    plt.figure(num='Simulacija', figsize=(16,4))
    plt.title('Odziv prve spremenljivke stanj')
    plt.ylabel('$X_1$ vs ref')
    plt.plot(T,xout[0],T,U,'r--')
    plt.xlabel('$t$ [s]')
    plt.legend(['$x_1$','Referenca'])
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.grid()

alltogether = widgets.VBox([widgets.HBox([selm,
                                          selc,
                                          selu]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([widgets.Label('K:',border=3), Kw,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('Lastne vrednosti:',border=3),
                                          eig1c,
                                          eig2c,
                                          eig3c,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('X0:',border=3), X0w]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([u,
                                          period,
                                          START]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([widgets.Label('Dinamična matrika Aa:',border=3),
                                          Aw,
                                          widgets.Label('Vhodna matrika Ba:',border=3),
                                          Bw,
                                          widgets.Label('Referenčna matrika Br:',border=3),
                                          Brw])])
out = widgets.interactive_output(main_callback, {'Aw':Aw, 'Bw':Bw, 'Brw':Brw, 'X0w':X0w, 'K':Kw, 'eig1c':eig1c, 'eig2c':eig2c, 'eig3c':eig3c,
                                                 'u':u, 'period':period, 'selm':selm, 'selc':selc, 'selu':selu, 'DW':DW})
out.layout.height = '1050px'
display(out, alltogether)
```

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

    VBox(children=(HBox(children=(Dropdown(options=('Nastavi K', 'Nastavi lastne vrednosti'), value='Nastavi K'), …


# PHY321: Classical Mechanics 1

**Homework 5, due Monday  February 17**

Date: **Feb 8, 2021**

### Practicalities about  homeworks and projects

1. You can work in groups (optimal groups are often 2-3 people) or by yourself. If you work as a group you can hand in one answer only if you wish. **Remember to write your name(s)**!

2. Homeworks are available Wednesday/Thursday the week before the deadline. The deadline is at the Friday lecture.

3. How do I(we)  hand in?  You can hand in the paper and pencil exercises as a hand-written document. For this homework this applies to exercises 1-5. Alternatively, you can hand in everyhting (if you are ok with typing mathematical formulae using say Latex) as a jupyter notebook at D2L. The numerical exercise(s) (exercise 6 here) should always be handed in as a jupyter notebook by the deadline at D2L.

### Introduction to homework 5

This week's sets of classical pen and paper and computational
exercises are a continuation of the topics from the previous homework set. We keep dealing with simple motion problems and conservation laws; energy, momentum and angular momentum. These conservation laws are central in Physics and understanding them properly lays the foundation for understanding and analyzing more complicated physics problems.
The relevant reading background is
1. chapters 3 and 4 of Taylor (there are many good examples there) and

2. chapters 10-14 of Malthe-Sørenssen.

In both textbooks there are many nice worked out examples. Malthe-Sørenssen's text contains also several coding examples you may find useful.

The numerical homework focuses on another motion problem where you can
use the code you developed in homework 4, almost entirely. Please take
a look at the posted solution (jupyter-notebook) for homework 4. You
need only to change the forces at play. The problem at hand is again a
classic one, a block fastened to a spring moving back and forth along the $x$-axis. It is a simpler problem compared to the previous homework. It allows us however to introduce damping (friction) and study more realistic situations.

### Exercise 1 (15 pt), Work-energy theorem and conservation laws

This exercise was partly discussed during the lectures. It has not yet been edited in the online notes.
We will study a classical electron which moves in the $x$-direction along a surface. The force from the surface is

$$
\boldsymbol{F}(x)=-F_0\sin{(\frac{2\pi x}{b})}\boldsymbol{e}_x.
$$

The constant $b$ represents the distance between atoms at the surface of the material, $F_0$ is a constant and $x$ is the position of the electron.

* 1a (2pt) Is this a conservative force? And if so, what does that imply?

This is indeed a conservative force since it depends only on position and its **curl** is zero. This means that energy is conserved and the integral over the work done by the force is independent of the path taken.
* 1b (4pt) Use the work-energy theorem to find the velocity $v(x)$.

Using the work-energy theorem we can find the work $W$ done when moving an electron from a position $x_\
0$ to a final position $x$ through the integral

$$
W=-\int_{x_0}^x \boldsymbol{F}(x')dx' =  \int_{x_0}^x F_0\sin{(\frac{2\pi x'}{b})} dx',
$$

which results in

$$
W=\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}-\cos{(\frac{2\pi x_0}{b})}\right].
$$

Since this is related to the change in kinetic energy we have, with $v_0$ being the initial velocity at a  time $t_0$,

$$
v  = \pm\sqrt{\frac{2}{m}\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}-\cos{(\frac{2\pi x_0}{b})}\right]+v_0^2}.
$$

* 1c (4pt) With the above expression for the force, find the potential energy.

The potential energy, due to energy conservation is

$$
V(x)=V(x_0)+\frac{1}{2}mv_0^2-\frac{1}{2}mv^2,
$$

with $v$ given by the previous answer.
We can now, in order to find a more explicit expression for the potential energy at a given value $x$, define a zero level value for the potential. The potential is defined , using the work-energy theorem , as

$$
V(x)=V(x_0)+\int_{x_0}^x (-F(x'))dx',
$$

and if you recall the definition of the indefinite integral, we can rewrite this as

$$
V(x)=\int (-F(x'))dx'+C,
$$

where $C$ is an undefined constant. The force is defined as the gradient of the potential, and in that case the undefined constant vanishes. The constant does not affect the force we derive from the potential.

We have then

$$
V(x)=V(x_0)-\int_{x_0}^x \boldsymbol{F}(x')dx',
$$

which results in

$$
V(x)=\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}-\cos{(\frac{2\pi x_0}{b})}\right]+V(x_0).
$$

We can now define

$$
\frac{F_0b}{2\pi}\cos{(\frac{2\pi x_0}{b})}=V(x_0),
$$

which gives

$$
V(x)=\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}\right].
$$

* 1d (5pt) Make a plot of the potential energy and discuss the equilibrium points where the force on the electron is zero. Discuss the physical interpretation of stable and unstable equilibrium points. Use energy conservation.

The following Python code plots the potential

```python
%matplotlib inline

import numpy as np
import pandas as pd
from math import *
import matplotlib.pyplot as plt
Deltax = 0.01
#set up arrays
xinitial = -2.0
xfinal = 2.0
n = ceil((xfinal-xinitial)/Deltax)
x = np.zeros(n)
for i in range(n):
    x[i] = xinitial+i*Deltax
V = np.zeros(n)
# Setting values for the constants.
F0 = 1.0; b = 1.0;
# Defining the potential
V = F0*b/(2*pi)*np.cos(2*pi*x/b)
# Plot position as function of time
fig, ax = plt.subplots()
ax.set_ylabel('x')
ax.set_xlabel('V')
ax.plot(x, V)
fig.tight_layout()
plt.show()
```

We have stable equilibrium  points for every minimum of the $\cos$ function and unstable equilibrium points where it has its maximimum values. At the minimum the particle has the lowest potential energy and the largest kinetic energy whereas at the maxima it has the largest potential energy and lowest kinetic energy.
### Exsercise 2 (15pt), Rocket, Momentum and mass

Taylor exercise 3.11.    This exercise was partly discussed during the lectures, see the notes on [Energy and Momentum etc, see the part on Momentum conservation ](https://mhjensen.github.io/Physics321/doc/pub/energyconserv/html/energyconserv.html). Taylor's chapter 3.2 covers also this example.

* 3.11 a and b

Consider the rocket of mass $M$ moving with velocity $v$. After a
brief instant, the velocity of the rocket is $v+\Delta v$ and the mass
is $M-\Delta M$. Momentum conservation gives

$$
\begin{eqnarray*}
Mv&=&(M-\Delta M)(v+\Delta v)+\Delta M(v-v_e)\\
0&=&-\Delta Mv+M\Delta v+\Delta M(v-v_e),\\
0&=&M\Delta v-\Delta Mv_e.
\end{eqnarray*}
$$

In the second step we ignored the term $\Delta M\Delta v$ since we
assume it is small. The last equation gives

$$
\begin{eqnarray}
\Delta v&=&\frac{v_e}{M}\Delta M,\\
\nonumber
\frac{dv}{dt}&=&\frac{v_e}{M}\frac{dM}{dt}.
\end{eqnarray}
$$

Integrating the expression with lower limits $v_0=0$ and $M_0$, one finds

$$
\begin{eqnarray*}
v&=&v_e\int_{M_0}^M \frac{dM'}{M'}\\
v&=&-v_e\ln(M/M_0)\\
&=&-v_e\ln[(M_0-\alpha t)/M_0].
\end{eqnarray*}
$$

We have ignored gravity here. If we add gravity as the external force, we get when integrating an additional terms $-gt$, that is

$$
v=-v_e\ln[(M_0-\alpha t)/M_0]-gt.
$$

* 3.11c

Inserting numbers $v_e=3000$ m/s, $M_0/M=2$ and $g=9.8$ m/s$^{2}$, we find $v=900$ m/s. With $g=0$ the corresponding number is $2100$ m/s, so gravity reduces the speed acquired in the first two minutes to a little less than half its weight-free value.

* 3.11d

If the thrust $\Delta Mv_e$ is less than the weight $mg$, the rocket will just sit on the ground until it has shed enough mass that the thrust can overcome the weight, definitely not a good design.

### Exercise 3 (10pt), More Rockets

Taylor exercises 3.13 (5pt) and 3.14 (5pt). This is a continuation of the previous exercise and most of the relevant background material can be found in Taylor chapter 3.2.

Taking the velocity from the previous exercise and integrating over time we find the height

$$
y(t) = y(t_0=0)+\int_0^tv(t')dt',
$$

which gives

$$
y(t) = v_et\ln{M_0}-v_e\int_0^t \ln{M(t')}dt'-\frac{1}{2}gt^2.
$$

To do the integral over time we recall that $M(t')=M_0-\Delta M t'$. We assumed that $\Delta M=k$ is a constant. We obtain then that the integral gives

$$
\int_0^t \ln{M(t')}dt' = \frac{1}{k}\left(M_0\ln{M_0}-M\ln{M}\right)-t,
$$

where we used that $M_0-M=kt$. We have assumed that mass decreases by a constant $k$ times time $t$.
Inserting into $y(t)$ we obtain then

$$
y(t) = v_et-\frac{1}{2}gt^2-\frac{mv_e}{k}\ln{(\frac{M_0}{M})}.
$$

Using the numbers from the previous exercise with $t=2$ min we obtain that $y\approx 40$ km.

For exercise 3.14 (5pt) we have the equation of motion which reads $Ma=kv_e-bv$ or

$$
\frac{Mdv}{kv_e-bv}=dt.
$$

We have that $dM/dt =-k$ (assumed a constant rate for mass change). We can then replace $dt$ by $-dM/k$ and we have

$$
\frac{kdv}{kv_e-bv}=-\frac{dM}{M}.
$$

Integrating gives

$$
v = \frac{kv_e}{b}\left[1-(\frac{M}{M_0})^{b/k}\right].
$$

### Exercise 4 (10pt), Center of mass

Taylor exercise 3.20. Here Taylor's chapter 3.3 can be of use. This relation will turn out to be very useful when we discuss systems of many classical particles.

The definition of the center of mass for $N$ objects can be written as

$$
M\boldsymbol{R}=\sum_{i=1}^Nm_i\boldsymbol{r}_i,
$$

where $m_i$ and $\boldsymbol{r}_i$ are the masses and positions of object $i$, respectively.

Assume now that we have a collection of $N_1$ objects with masses $m_{1i}$ and positions $\boldsymbol{r}_{1i}$
with $i=1,\dots,N_1$ and  a collection of $N_2$ objects with masses $m_{2j}$ and positions $\boldsymbol{r}_{2j}$
with $j=1,\dots,N_2$.

The total mass of the two-body system is $M=M_1+M_2=\sum_{i=1}^{N_1}m_{1i}+\sum_{j=1}^{N_2}m_{2j}$. The center of mass position $\boldsymbol{R}$ of the whole system satisfies then

$$
M\boldsymbol{R}=\sum_{i=1}^{N_1}m_{1i}\boldsymbol{r}_{1i}+\sum_{j=1}^{N_2}m_{2j}\boldsymbol{r}_{2j}=M_1\boldsymbol{R}_1+M_2\boldsymbol{R}_2,
$$

where $\boldsymbol{R}_1$ and $\boldsymbol{R}_2$ are the the center of mass positions of the two separate bodies and the second equality follows from our rewritten definition of the center of mass applied to each body separately. This is the required result.

### Exercise 5 (10pt), Sliding Block  and Spring (please not again), Scaling the Equations and getting started with numerical project

The relevant material with codes etc is covered by the [Lecture Notes on Oscillations](https://mhjensen.github.io/Physics321/doc/pub/harmonic/html/harmonic.html). Taylor's chapter 5, in particular sections 5.1 amd 5.2. 5.4 is relevant for the bonus exercise.
We start now with our next numerical application with simple rewrites of our equations. The system we will look at is that of a block fastened to a spring (which in turn is tied to a wall).
The force acting on the block from the spring in the $x$-drection only is

$$
m\frac{d^2x(t)}{dt^2}=F(x) = -k(x-b),
$$

where $k$ is a material specific constant and $b$ is the equilibrium position. The block has mass $m$ and $t$ is time. Define the initial time as $t_0$. We will for simplicity set the equilibrium position to zero, that is $b=0$.

* 5a (3pt) Does this force conserve energy? If so, with given initial position and velocity $x_0$ and $v_0$, respectively, find the expression for energy conservation in terms of the potential and kinetic energies.

This force depdens only on the position and its **curl** is also zero. Energy is thus conserved.
Integrating the force gives us a potential energy $1/2kx^2$. With initial conditions for the position $x_0$ and the velocity $v_0$ we have that

$$
\frac{1}{2}kx^2+\frac{1}{2}mv^2=\frac{1}{2}kx_0^2+\frac{1}{2}mv_0^2.
$$

* 5b (3pt) Define a constant $\omega_0=\sqrt{k/m}$ and show that you can write the acceleration as $a(t) = -\omega_0^2 x(t)$. What is the dimentionality of $\omega_0$? (it is normally called a natural frequency).

The dimensionality of $\omega_0$ is inverse time. The acceleration is

$$
a=\frac{d^2x(t)}{dt^2}=\frac{F}{m} = -\frac{k}{m}x=-\omega_0^2x.
$$

* 5c (4pt) Introduce now a dimensionless time $\tau = t\omega_0$. Show that you can rewrite the  equation for the acceleration in terms of two first-order differential equations

Starting with

$$
a=\frac{d^2x}{dt^2}=\frac{F}{m} = -\frac{k}{m}x=-\omega_0^2x,
$$

and introducing a dimensionless time we have

$$
\omega_0^2\frac{d^2x}{d\tau^2}=-\omega_0^2x,
$$

and dividing by $\omega_0^2$ we have

$$
\frac{d^2x(t)}{d\tau^2}=\frac{dv}{d\tau}=-x.
$$

This gives us our first differential equation

$$
\frac{dv}{d\tau} = -x,
$$

and using the definition of velocity we have

$$
\frac{dx}{d\tau} = v.
$$

Both equations (velocity and position) have dimensionality length.
Note that in principle we should have relabeled $v$ as $\overline{v}$ to indicate that it has dimension length and not length divided by time.

### Exercises 6 and 7

The results are detailed in the lecture notes on oscillations. The text here is simply an iteration of what we have discussed during the lectures.

We consider only the case where the damping force is proportional to
the velocity. This is counter to dragging friction, where the force is
proportional in strength to the normal force and independent of
velocity, and is also inconsistent with wind resistance, where the
magnitude of the drag force is proportional the square of the
velocity. Rolling resistance does seem to be mainly proportional to
the velocity. However, the main motivation for considering damping
forces proportional to the velocity is that the math is more
friendly. This is because the differential equation is linear,
i.e. each term is of order $x$, $\dot{x}$, $\ddot{x}\cdots$, or even
terms with no mention of $x$, and there are no terms such as $x^2$ or
$x\ddot{x}$. The equations of motion for a spring with damping force
$-b\dot{x}$ are

$$
\begin{equation}
m\ddot{x}+b\dot{x}+kx=0.
\label{_auto1} \tag{1}
\end{equation}
$$

Just to make the solution a bit less messy, we rewrite this equation as

$$
\begin{equation}
\label{eq:dampeddiffyq} \tag{2}
\ddot{x}+2\beta\dot{x}+\omega_0^2x=0,~~~~\beta\equiv b/2m,~\omega_0\equiv\sqrt{k/m}.
\end{equation}
$$

Both $\beta$ and $\omega$ have dimensions of inverse time. To find solutions (see appendix C in the text) you must make an educated guess at the form of the solution. To do this, first realize that the solution will need an arbitrary normalization $A$ because the equation is linear. Secondly, realize that if the form is

$$
\begin{equation}
x=Ae^{rt}
\label{_auto2} \tag{3}
\end{equation}
$$

that each derivative simply brings out an extra power of $r$. This
means that the $Ae^{rt}$ factors out and one can simply solve for an
equation for $r$. Plugging this form into Eq. ([2](#eq:dampeddiffyq)),

$$
\begin{equation}
r^2+2\beta r+\omega_0^2=0.
\label{_auto3} \tag{4}
\end{equation}
$$

Because this is a quadratic equation there will be two solutions,

$$
\begin{equation}
r=-\beta\pm\sqrt{\beta^2-\omega_0^2}.
\label{_auto4} \tag{5}
\end{equation}
$$

We refer to the two solutions as $r_1$ and $r_2$ corresponding to the
$+$ and $-$ roots. As expected, there should be two arbitrary
constants involved in the solution,

$$
\begin{equation}
x=A_1e^{r_1t}+A_2e^{r_2t},
\label{_auto5} \tag{6}
\end{equation}
$$

where the coefficients $A_1$ and $A_2$ are determined by initial
conditions.

The roots listed above, $\sqrt{\omega_0^2-\beta_0^2}$, will be
imaginary if the damping is small and $\beta<\omega_0$. In that case,
$r$ is complex and the factor $e{rt}$ will have some oscillatory
behavior. If the roots are real, there will only be exponentially
decaying solutions. There are three cases:

### Underdamped: $\beta<\omega_0$

$$
\begin{eqnarray}
x&=&A_1e^{-\beta t}e^{i\omega't}+A_2e^{-\beta t}e^{-i\omega't},~~\omega'\equiv\sqrt{\omega_0^2-\beta^2}\\
\nonumber
&=&(A_1+A_2)e^{-\beta t}\cos\omega't+i(A_1-A_2)e^{-\beta t}\sin\omega't.
\end{eqnarray}
$$

Here we have made use of the identity
$e^{i\omega't}=\cos\omega't+i\sin\omega't$. Because the constants are
arbitrary, and because the real and imaginary parts are both solutions
individually, we can simply consider the real part of the solution
alone:

$$
\begin{eqnarray}
\label{eq:homogsolution} \tag{7}
x&=&B_1e^{-\beta t}\cos\omega't+B_2e^{-\beta t}\sin\omega't,\\
\nonumber
\omega'&\equiv&\sqrt{\omega_0^2-\beta^2}.
\end{eqnarray}
$$

### Critical dampling: $\beta=\omega_0$

In this case the two terms involving $r_1$ and $r_2$ are identical
because $\omega'=0$. Because we need to arbitrary constants, there
needs to be another solution. This is found by simply guessing, or by
taking the limit of $\omega'\rightarrow 0$ from the underdamped
solution. The solution is then

$$
\begin{equation}
\label{eq:criticallydamped} \tag{8}
x=Ae^{-\beta t}+Bte^{-\beta t}.
\end{equation}
$$

The critically damped solution is interesting because the solution
approaches zero quickly, but does not oscillate. For a problem with
zero initial velocity, the solution never crosses zero. This is a good
choice for designing shock absorbers or swinging doors.

### Overdamped: $\beta>\omega_0$

$$
\begin{eqnarray}
x&=&A_1\exp{-(\beta+\sqrt{\beta^2-\omega_0^2})t}+A_2\exp{-(\beta-\sqrt{\beta^2-\omega_0^2})t}
\end{eqnarray}
$$

This solution will also never pass the origin more than once, and then
only if the initial velocity is strong and initially toward zero.

Given $b$, $m$ and $\omega_0$, find $x(t)$ for a particle whose
initial position is $x=0$ and has initial velocity $v_0$ (assuming an
underdamped solution).

The solution is of the form,

$$
\begin{eqnarray*}
x&=&e^{-\beta t}\left[A_1\cos(\omega' t)+A_2\sin\omega't\right],\\
\dot{x}&=&-\beta x+\omega'e^{-\beta t}\left[-A_1\sin\omega't+A_2\cos\omega't\right].\\
\omega'&\equiv&\sqrt{\omega_0^2-\beta^2},~~~\beta\equiv b/2m.
\end{eqnarray*}
$$

From the initial conditions, $A_1=0$ because $x(0)=0$ and $\omega'A_2=v_0$. So

$$
x=\frac{v_0}{\omega'}e^{-\beta t}\sin\omega't.
$$

Let us remind ourselves about the differential equation we want to solve (the general case with damping due to friction)

$$
m\frac{d^2x}{dt^2} + b\frac{dx}{dt}+kx(t) =0.
$$

We divide by $m$ and introduce $\omega_0^2=\sqrt{k/m}$ and obtain

$$
\frac{d^2x}{dt^2} + \frac{b}{m}\frac{dx}{dt}+\omega_0^2x(t) =0.
$$

Thereafter we introduce a dimensionless time $\tau = t\omega_0$ (check
that the dimensionality is correct) and rewrite our equation as

$$
\frac{d^2x}{d\tau^2} + \frac{b}{m\omega_0}\frac{dx}{d\tau}+x(\tau) =0,
$$

which gives us

$$
\frac{d^2x}{d\tau^2} + \frac{b}{m\omega_0}\frac{dx}{d\tau}+x(\tau) =0.
$$

We then define $\gamma = b/(2m\omega_0)$ and rewrite our equations as

$$
\frac{d^2x}{d\tau^2} + 2\gamma\frac{dx}{d\tau}+x(\tau) =0.
$$

This is the equation we will code below. The first version employs the Euler-Cromer method.

```python
# Common imports
import numpy as np
import pandas as pd
from math import *
import matplotlib.pyplot as plt
import os

# Where to save the figures and data files
PROJECT_ROOT_DIR = "Results"
FIGURE_ID = "Results/FigureFiles"
DATA_ID = "DataFiles/"

if not os.path.exists(PROJECT_ROOT_DIR):
    os.mkdir(PROJECT_ROOT_DIR)

if not os.path.exists(FIGURE_ID):
    os.makedirs(FIGURE_ID)

if not os.path.exists(DATA_ID):
    os.makedirs(DATA_ID)

def image_path(fig_id):
    return os.path.join(FIGURE_ID, fig_id)

def data_path(dat_id):
    return os.path.join(DATA_ID, dat_id)

def save_fig(fig_id):
    plt.savefig(image_path(fig_id) + ".png", format='png')

from pylab import plt, mpl
plt.style.use('seaborn')
mpl.rcParams['font.family'] = 'serif'

DeltaT = 0.001
#set up arrays
tfinal = 20 # in years
n = ceil(tfinal/DeltaT)
# set up arrays for t, v, and x
t = np.zeros(n)
v = np.zeros(n)
x = np.zeros(n)
# Initial conditions as simple one-dimensional arrays of time
x0 =  1.0
v0 = 0.0
x[0] = x0
v[0] = v0
gamma = 0.0
# Start integrating using Euler-Cromer's method
for i in range(n-1):
    # Set up the acceleration
    # Here you could have defined your own function for this
    a =  -2*gamma*v[i]-x[i]
    # update velocity, time and position
    v[i+1] = v[i] + DeltaT*a
    x[i+1] = x[i] + DeltaT*v[i+1]
    t[i+1] = t[i] + DeltaT
# Plot position as function of time
fig, ax = plt.subplots()
#ax.set_xlim(0, tfinal)
ax.set_ylabel('x[m]')
ax.set_xlabel('t[s]')
ax.plot(t, x)
fig.tight_layout()
save_fig("BlockEulerCromer")
plt.show()
```

When setting up the value of $\gamma$ we see that for $\gamma=0$ we get the simple oscillatory motion with no damping.
Choosing $\gamma < 1$ leads to the classical underdamped case with oscillatory motion, but where the motion comes to an end.

Choosing $\gamma =1$ leads to what normally is called critical damping and $\gamma> 1$ leads to critical overdamping.
Try it out and try also to change the initial position and velocity. Setting $\gamma=1$
yields a situation, as discussed above, where the solution approaches quickly zero and does not oscillate. With zero initial velocity it will never cross zero.


```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

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

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)
```

Toggle cell visibility here.

```python
%matplotlib notebook

import numpy as np
import control as control
import matplotlib.pyplot as plt
import ipywidgets as widgets
import sympy as sym
# from IPython.display import Markdown # For displaying Markdown and LaTeX code

sym.init_printing()
continuous_update=False
```

## Errore a regime - Sistemi in feedback unitario

Data la funzione di trasferimento dell'input $I(s)$ e la funzione di trasferimento del sistema ad anello aperto $G(s)$, l'errore a regime $e(\infty)$ del sistema ad anello chiuso può, in caso di feedback unitario, essere determinato da:

\begin{equation}
    e(\infty)=\lim_{s\to0}\frac{sI(s)}{1+G(s)}.
\end{equation}

Nel caso di una funzione di ingresso a gradino $I(s)=\frac{1}{s}$ si ottiene:

\begin{equation}
    e_{step}(\infty)=\frac{1}{1+\lim_{s\to0}G(s)},
\end{equation}

nel caso di una funzione di ingresso a rampa $I(s)=\frac{1}{s^2}$:

\begin{equation}
    e_{ramp}(\infty)=\frac{1}{\lim_{s\to0}sG(s)},
\end{equation}

e nel caso di una funzione di ingresso parabolica $I(s)=\frac{1}{s^3}$:

\begin{equation}
    e_{parabolic}(\infty)=\frac{1}{\lim_{s\to0}s^2G(s)}.
\end{equation}

### Sistemi senza integratori

Un esempio di una funzione di trasferimento $G(s)$ di un sistema senza integratori può essere:

\begin{equation}
    G(s) = \frac{K}{as^2 + bs + c}
\end{equation}

L'errore a regime nel caso di sistemi senza integratori è infinito per gli ingressi a rampa e parabolici.

### Sistemi con un integratore

Un esempio di una funzione di trasferimento $G(s)$ di un sistema con un integratore può essere:

\begin{equation}
    G(s) = \frac{K(as^2 + bs + c)}{s(ds^2 + es + fc)}
\end{equation}

L'errore a regime nel caso di sistemi con un integratore è infinito per gli ingressi parabolici.

---

### Come usare questo notebook?

- Scegli tra il sistema senza integratori a un sistema con un unico integratore.
- Sposta i cursori per modificare i valori di $a$, $b$, $c$ (coefficienti della funzione di trasferimento) e $K$ (amplificazione).

```python
style = {'description_width': 'initial'}

layout1 = widgets.Layout(width='auto', height='auto') #set width and height

systemSelect = widgets.ToggleButtons(
    options=[('nessun integratore', 0), ('un integratore', 1)],
    description='Sistema: ',style=style)
functionSelect = widgets.ToggleButtons(
    options=[('gradino', 0), ('rampa', 1), ('parabola', 2)],
    description='Input: ',style=style)

fig=plt.figure(num='Errore a regime')
fig.set_size_inches((9.8,3))
fig.set_tight_layout(True)
f1 = fig.add_subplot(1, 1, 1)

f1.grid(which='both', axis='both', color='lightgray')

f1.set_ylabel('Input, output')
f1.set_xlabel('$t$ [s]')

inputf, = f1.plot([],[])
responsef, = f1.plot([],[])
errorf, = f1.plot([],[])

ann1=f1.annotate("", xy=([0], [0]), xytext=([0], [0]))
ann2=f1.annotate("", xy=([0], [0]), xytext=([0], [0]))

display(systemSelect)
display(functionSelect)

def create_draw_functions(K,a,b,c,index_system,index_input):

    num_of_samples = 1000
    total_time = 150
    t = np.linspace(0, total_time, num_of_samples) # time for which response is calculated (start, stop, step)

    if index_system == 0:

        Wsys = control.tf([K], [a, b, c])
        ess, G_s, s, n  = sym.symbols('e_{step}(\infty), G(s), s, n')
        sys1 = control.feedback(Wsys)

    elif index_system == 1:

        Wsys = control.tf([K,K,K*a], [1, b, c, 0])
        ess, G_s, s, n  = sym.symbols('e_{step}(\infty), G(s), s, n')
        sys1 = control.feedback(Wsys)

    global inputf, responsef, ann1, ann2

    if index_input==0:
        infunction = np.ones(len(t))
        infunction[0]=0
        tout, yout = control.step_response(sys1,t)
        s=sym.Symbol('s')
        if index_system == 0:
            limit_val = sym.limit((K/(a*s**2+b*s+c)),s,0)
        elif index_system == 1:
            limit_val = sym.limit((K*s*s+K*s+K*a)/(s*s*s+b*s*s+c*s),s,0)
        e_inf=1/(1+limit_val)

    elif index_input==1:
        infunction=t;
        tout, yout, xx = control.forced_response(sys1, t, infunction)
        if index_system == 0:
            limit_val = sym.limit(s*(K/(a*s**2+b*s+c)),s,0)
        elif index_system == 1:
            limit_val = sym.limit(s*((K*s*s+K*s+K*a)/(s*s*s+b*s*s+c*s)),s,0)
        e_inf=1/limit_val

    elif index_input==2:
        infunction=t*t
        tout, yout, xx = control.forced_response(sys1, t, infunction)
        if index_system == 0:
            limit_val = sym.limit(s*s*(K/(a*s**2+b*s+c)),s,0)
        elif index_system == 1:
            limit_val = sym.limit(s*s*((K*s*s+K*s+K*a)/(s*s*s+b*s*s+c*s)),s,0)
        e_inf=1/limit_val

    ann1.remove()
    ann2.remove()

    if type(e_inf) == sym.numbers.ComplexInfinity:
        print('L\'errore a regime è infinito.')
    elif e_inf==0:
        print('L\'errore a regime è zero.')
    else:
        print('L\'errore a regime è uguale a %f.'% (e_inf,))

#     if type(e_inf) == sym.numbers.ComplexInfinity:
#         display(Markdown('Steady-state error is infinite.'))
#     elif e_inf==0:
#         display(Markdown('Steady-state error is zero.'))
#     else:
#         display(Markdown('Steady-state error is equal to %f.'%(e_inf,)))

    if type(e_inf) != sym.numbers.ComplexInfinity and e_inf>0:
        ann1=plt.annotate("", xy=(tout[-60],infunction[-60]), xytext=(tout[-60],yout[-60]), arrowprops=dict(arrowstyle="|-|", connectionstyle="arc3"))
        ann2=plt.annotate("$e(\infty)$", xy=(145, 1.), xytext=(145, (yout[-60]+(infunction[-60]-yout[-60])/2)))
    elif type(e_inf) == sym.numbers.ComplexInfinity:
        ann1=plt.annotate("", xy=(0,0), xytext=(0,0), arrowprops=dict(arrowstyle="|-|", connectionstyle="arc3"))
        ann2=plt.annotate("", xy=(134, 1.), xytext=(134, (1 - infunction[-10])/2 + infunction[-10]))
    elif type(e_inf) != sym.numbers.ComplexInfinity and e_inf==0:
        ann1=plt.annotate("", xy=(0,0), xytext=(0,0), arrowprops=dict(arrowstyle="|-|", connectionstyle="arc3"))
        ann2=plt.annotate("", xy=(134, 1.), xytext=(134, (1 - yout[-10])/2 + yout[-10]))

    f1.lines.remove(inputf)
    f1.lines.remove(responsef)

    inputf, = f1.plot(t,infunction,label='input',color='C0')
    responsef, = f1.plot(tout,yout,label='output',color='C1')

    f1.relim()
    f1.autoscale_view()

    f1.legend()

K_slider=widgets.IntSlider(min=1,max=8,step=1,value=1,description='$K$',continuous_update=False)
a_slider=widgets.IntSlider(min=0,max=8,step=1,value=1,description='$a$',continuous_update=False)
b_slider=widgets.IntSlider(min=0,max=8,step=1,value=1,description='$b$',continuous_update=False)
c_slider=widgets.IntSlider(min=1,max=8,step=1,value=1,description='$c$',continuous_update=False)

input_data=widgets.interactive_output(create_draw_functions,
                                     {'K':K_slider,'a':a_slider,'b':b_slider,'c':c_slider,
                                      'index_system':systemSelect,'index_input':functionSelect})

def update_sliders(index):
    global K_slider, a_slider, b_slider, c_slider

    Kval=[1, 1, 1]
    aval=[1, 1, 1]
    bval=[2, 2, 2]
    cval=[6, 6, 6]

    K_slider.value=Kval[index]
    a_slider.value=aval[index]
    b_slider.value=bval[index]
    c_slider.value=cval[index]

input_data2=widgets.interactive_output(update_sliders,
                                       {'index':functionSelect})

display(K_slider,a_slider,b_slider,c_slider,input_data)
```

    <IPython.core.display.Javascript object>

    ToggleButtons(description='Sistema: ', options=(('nessun integratore', 0), ('un integratore', 1)), style=Toggl…

    ToggleButtons(description='Input: ', options=(('gradino', 0), ('rampa', 1), ('parabola', 2)), style=ToggleButt…

    IntSlider(value=1, continuous_update=False, description='$K$', max=8, min=1)

    IntSlider(value=1, continuous_update=False, description='$a$', max=8)

    IntSlider(value=2, continuous_update=False, description='$b$', max=8)

    IntSlider(value=6, continuous_update=False, description='$c$', max=8, min=1)

    Output()


            prepared by Özlem Salehi (QTurkey)

This cell contains some macros. If there is a problem with displaying mathematical formulas, please run this cell to load these macros.

$\newcommand{\Mod}[1]{\ (\mathrm{mod}\ #1)}$
$ \newcommand{\bra}[1]{\langle #1|} $
$ \newcommand{\ket}[1]{|#1\rangle} $
$ \newcommand{\braket}[2]{\langle #1|#2\rangle} $
$ \newcommand{\dot}[2]{ #1 \cdot #2} $
$ \newcommand{\biginner}[2]{\left\langle #1,#2\right\rangle} $
$ \newcommand{\mymatrix}[2]{\left( \begin{array}{#1} #2\end{array} \right)} $
$ \newcommand{\myvector}[1]{\mymatrix{c}{#1}} $
$ \newcommand{\myrvector}[1]{\mymatrix{r}{#1}} $
$ \newcommand{\mypar}[1]{\left( #1 \right)} $
$ \newcommand{\mybigpar}[1]{ \Big( #1 \Big)} $
$ \newcommand{\sqrttwo}{\frac{1}{\sqrt{2}}} $
$ \newcommand{\dsqrttwo}{\dfrac{1}{\sqrt{2}}} $
$ \newcommand{\onehalf}{\frac{1}{2}} $
$ \newcommand{\donehalf}{\dfrac{1}{2}} $
$ \newcommand{\hadamard}{ \mymatrix{rr}{ \sqrttwo & \sqrttwo \\ \sqrttwo & -\sqrttwo }} $
$ \newcommand{\vzero}{\myvector{1\\0}} $
$ \newcommand{\vone}{\myvector{0\\1}} $
$ \newcommand{\stateplus}{\myvector{ \sqrttwo \\  \sqrttwo } } $
$ \newcommand{\stateminus}{ \myrvector{ \sqrttwo \\ -\sqrttwo } } $
$ \newcommand{\myarray}[2]{ \begin{array}{#1}#2\end{array}} $
$ \newcommand{\X}{ \mymatrix{cc}{0 & 1 \\ 1 & 0}  } $
$ \newcommand{\Z}{ \mymatrix{rr}{1 & 0 \\ 0 & -1}  } $
$ \newcommand{\Htwo}{ \mymatrix{rrrr}{ \frac{1}{2} & \frac{1}{2} & \frac{1}{2} & \frac{1}{2} \\ \frac{1}{2} & -\frac{1}{2} & \frac{1}{2} & -\frac{1}{2} \\ \frac{1}{2} & \frac{1}{2} & -\frac{1}{2} & -\frac{1}{2} \\ \frac{1}{2} & -\frac{1}{2} & -\frac{1}{2} & \frac{1}{2} } } $
$ \newcommand{\CNOT}{ \mymatrix{cccc}{1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0} } $
$ \newcommand{\norm}[1]{ \left\lVert #1 \right\rVert } $
$ \newcommand{\pstate}[1]{ \lceil \mspace{-1mu} #1 \mspace{-1.5mu} \rfloor } $

# Cirq installation and test

## Install Cirq

Type

    !pip install cirq

directly inside the cell of a Jupyter notebook.

You may also visit the following links for further information.

- https://pypi.org/project/cirq/

- https://quantumai.google/cirq/install?hl=en

__*Restart the kernel*__ (check "Kernel" menu) to apply the changes to the current notebook.

```python
!pip install cirq
```

    Collecting cirq
      Downloading cirq-0.11.0-py3-none-any.whl (7.6 kB)
    Collecting cirq-google==0.11.0
      Downloading cirq_google-0.11.0-py3-none-any.whl (380 kB)
         |████████████████████████████████| 380 kB 505 kB/s eta 0:00:01
    Collecting cirq-core==0.11.0
      Downloading cirq_core-0.11.0-py3-none-any.whl (1.5 MB)
         |████████████████████████████████| 1.5 MB 2.8 MB/s eta 0:00:01
    Collecting protobuf~=3.13.0
      Downloading protobuf-3.13.0-cp38-cp38-manylinux1_x86_64.whl (1.3 MB)
         |████████████████████████████████| 1.3 MB 7.2 MB/s eta 0:00:01
    Collecting google-api-core[grpc]<2.0.0dev,>=1.14.0
      Downloading google_api_core-1.31.0-py2.py3-none-any.whl (93 kB)
         |████████████████████████████████| 93 kB 718 kB/s eta 0:00:011
    Collecting sortedcontainers~=2.0
      Downloading sortedcontainers-2.4.0-py2.py3-none-any.whl (29 kB)
    Requirement already satisfied: scipy in /home/dev/.local/lib/python3.8/site-packages (from cirq-core==0.11.0->cirq) (1.7.0)
    Requirement already satisfied: matplotlib~=3.0 in /home/dev/.local/lib/python3.8/site-packages (from cirq-core==0.11.0->cirq) (3.4.2)
    Collecting typing-extensions
      Downloading typing_extensions-3.10.0.0-py3-none-any.whl (26 kB)
    Collecting tqdm
      Downloading tqdm-4.61.2-py2.py3-none-any.whl (76 kB)
         |████████████████████████████████| 76 kB 4.0 MB/s eta 0:00:011
    Collecting networkx~=2.4
      Using cached networkx-2.5.1-py3-none-any.whl (1.6 MB)
    Requirement already satisfied: numpy~=1.16 in /home/dev/.local/lib/python3.8/site-packages (from cirq-core==0.11.0->cirq) (1.21.0)
    Requirement already satisfied: requests~=2.18 in /usr/lib/python3/dist-packages (from cirq-core==0.11.0->cirq) (2.22.0)
    Requirement already satisfied: sympy in /home/dev/.local/lib/python3.8/site-packages (from cirq-core==0.11.0->cirq) (1.8)
    Requirement already satisfied: pandas in /home/dev/.local/lib/python3.8/site-packages (from cirq-core==0.11.0->cirq) (1.3.0)
    Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from protobuf~=3.13.0->cirq-google==0.11.0->cirq) (45.2.0)
    Requirement already satisfied: six>=1.9 in /usr/lib/python3/dist-packages (from protobuf~=3.13.0->cirq-google==0.11.0->cirq) (1.14.0)
    Collecting googleapis-common-protos<2.0dev,>=1.6.0
      Downloading googleapis_common_protos-1.53.0-py2.py3-none-any.whl (198 kB)
         |████████████████████████████████| 198 kB 7.9 MB/s eta 0:00:01
    Requirement already satisfied: packaging>=14.3 in /home/dev/.local/lib/python3.8/site-packages (from google-api-core[grpc]<2.0.0dev,>=1.14.0->cirq-google==0.11.0->cirq) (21.0)
    Collecting google-auth<2.0dev,>=1.25.0
      Downloading google_auth-1.33.1-py2.py3-none-any.whl (152 kB)
         |████████████████████████████████| 152 kB 7.4 MB/s eta 0:00:01
    Requirement already satisfied: pytz in /usr/lib/python3/dist-packages (from google-api-core[grpc]<2.0.0dev,>=1.14.0->cirq-google==0.11.0->cirq) (2019.3)
    Collecting grpcio<2.0dev,>=1.29.0; extra == "grpc"
      Downloading grpcio-1.39.0-cp38-cp38-manylinux2014_x86_64.whl (4.3 MB)
         |████████████████████████████████| 4.3 MB 8.9 MB/s eta 0:00:01
    Requirement already satisfied: cycler>=0.10 in /home/dev/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core==0.11.0->cirq) (0.10.0)
    Requirement already satisfied: python-dateutil>=2.7 in /home/dev/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core==0.11.0->cirq) (2.8.2)
    Requirement already satisfied: pyparsing>=2.2.1 in /home/dev/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core==0.11.0->cirq) (2.4.7)
    Requirement already satisfied: kiwisolver>=1.0.1 in /home/dev/.local/lib/python3.8/site-packages (from matplotlib~=3.0->cirq-core==0.11.0->cirq) (1.3.1)
    Requirement already satisfied: pillow>=6.2.0 in /usr/lib/python3/dist-packages (from matplotlib~=3.0->cirq-core==0.11.0->cirq) (7.0.0)
    Requirement already satisfied: decorator<5,>=4.3 in /home/dev/.local/lib/python3.8/site-packages (from networkx~=2.4->cirq-core==0.11.0->cirq) (4.4.2)
    Requirement already satisfied: mpmath>=0.19 in /home/dev/.local/lib/python3.8/site-packages (from sympy->cirq-core==0.11.0->cirq) (1.2.1)
    Collecting cachetools<5.0,>=2.0.0
      Downloading cachetools-4.2.2-py3-none-any.whl (11 kB)
    Collecting pyasn1-modules>=0.2.1
      Downloading pyasn1_modules-0.2.8-py2.py3-none-any.whl (155 kB)
         |████████████████████████████████| 155 kB 9.7 MB/s eta 0:00:01
    Collecting rsa<5,>=3.1.4; python_version >= "3.6"
      Downloading rsa-4.7.2-py3-none-any.whl (34 kB)
    Collecting pyasn1<0.5.0,>=0.4.6
      Downloading pyasn1-0.4.8-py2.py3-none-any.whl (77 kB)
         |████████████████████████████████| 77 kB 4.4 MB/s eta 0:00:011
    Installing collected packages: protobuf, sortedcontainers, typing-extensions, tqdm, networkx, cirq-core, googleapis-common-protos, cachetools, pyasn1, pyasn1-modules, rsa, google-auth, grpcio, google-api-core, cirq-google, cirq
      Attempting uninstall: networkx
        Found existing installation: networkx 1.11
        Uninstalling networkx-1.11:
          Successfully uninstalled networkx-1.11
    Successfully installed cachetools-4.2.2 cirq-0.11.0 cirq-core-0.11.0 cirq-google-0.11.0 google-api-core-1.31.0 google-auth-1.33.1 googleapis-common-protos-1.53.0 grpcio-1.39.0 networkx-2.5.1 protobuf-3.13.0 pyasn1-0.4.8 pyasn1-modules-0.2.8 rsa-4.7.2 sortedcontainers-2.4.0 tqdm-4.61.2 typing-extensions-3.10.0.0

## Check Cirq installation

```python
import cirq

print(cirq.google.Foxtail)
# should print:
# (0, 0)───(0, 1)───(0, 2)───(0, 3)───(0, 4)───(0, 5)───(0, 6)───(0, 7)───(0, 8)───(0, 9)───(0, 10)
# │        │        │        │        │        │        │        │        │        │        │
# │        │        │        │        │        │        │        │        │        │        │
# (1, 0)───(1, 1)───(1, 2)───(1, 3)───(1, 4)───(1, 5)───(1, 6)───(1, 7)───(1, 8)───(1, 9)───(1, 10)
```

    (0, 0)───(0, 1)───(0, 2)───(0, 3)───(0, 4)───(0, 5)───(0, 6)───(0, 7)───(0, 8)───(0, 9)───(0, 10)
    │        │        │        │        │        │        │        │        │        │        │
    │        │        │        │        │        │        │        │        │        │        │
    (1, 0)───(1, 1)───(1, 2)───(1, 3)───(1, 4)───(1, 5)───(1, 6)───(1, 7)───(1, 8)───(1, 9)───(1, 10)

    /tmp/ipykernel_6304/3009575250.py:3: DeprecationWarning: cirq.google was used but is deprecated.
     it will be removed in cirq v0.14.
     Use cirq_google instead.

      print(cirq.google.Foxtail)

## Execute An Example Program

1) Create a quantum circuit

```python
import cirq

# Pick a qubit.
qubit = cirq.GridQubit(0, 0)

# Create a circuit
circuit = cirq.Circuit(
    cirq.X(qubit)**0.5,  # Square root of NOT.
    cirq.measure(qubit, key='m')  # Measurement.
)
```

2) Draw the circuit

_Run the cell once more if the figure is not shown_

```python
print("Circuit:")
print(circuit)
```

    Circuit:
    (0, 0): ───X^0.5───M('m')───

3) Execute the circuit 20 times in the local simulator and print the observed the outcomes

```python
# Simulate the circuit several times.
simulator = cirq.Simulator()
result = simulator.run(circuit, repetitions=20)
print("Results:")
print(result)
```

    Results:
    m=00101000100101110110

4) Print a histogram of results

```python
# Print a histogram of results
results = result.histogram(key='m')
print(results)
```

    Counter({0: 11, 1: 9})

Reference: https://pypi.org/project/cirq/

## Successfully Installed


```python
from IPython.core.display import display_html
from urllib.request import urlopen

display_html(urlopen('http://bit.ly/1GioOFw').read(), raw=True)
```

<link href='http://fonts.googleapis.com/css?family=EB+Garamond' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Lato:100,100italic' rel='stylesheet' type='text/css'>

<style>
	@font-face{
   		font-family: "Computer Modern";
        	src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');}

	h1 {
        	font-family: 'Lato', sans-serif;}

	h2 {
        	font-family: 'Lato', serif;}

	h3{
		font-family:   'Lato', serif;
        	margin-top:    12px;
		margin-bottom: 3px;}

        h4{
		font-family: 'Lato', serif;}

	h5 {
        	font-family: 'Lato', serif;}

	div.text_cell_render{
        	font-family:  'EB Garamond', 'Computer Modern', 'Helvetica Neue', Arial, Helvetica, Geneva, sans-serif;
	        line-height:  140%;
	        font-size:    14pt;}

	.CodeMirror{
        	font-family: 'Consolas';}

	.text_cell_render h1 {
	        font-weight:   100;
	        font-size:     50pt;
		line-height:   100%;
	        color:         #CD2305;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.text_cell_render h2 {
        	font-weight:   100;
        	font-size:     30pt;}

	.text_cell_render h3 {
        	font-weight:   100;
        	font-size:     20pt;}

	.text_cell_render h4 {
        	font-weight:   100;
        	font-size:     16pt;}

	.text_cell_render h5 {
	        font-weight:   100;
	        font-size:     16pt;
	        color:         #CD2305;
	        font-style:    italic;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.text_cell_render h6 {
	        font-weight:   100;
	        font-size:     14pt;
	        color:         #CD2305;
	        font-style:    italic;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.warning{
	    	background-color: #fcf2f2;
	    	border-color: #dFb5b4;
	    	border-left: 5px solid #dfb5b4;
	    	padding: 0.5em;}
</style>

# Recapitulación de resultados empleados

## Control predictivo

## Implementación del retardo distribuido y sus problemas

## Solución por medio de estabilización simultanea

## Solución por medio de introducción de dinámicas

# Implementación de control predictivo: ejemplo escalar

## Resultados del articulo "Some problems arising in the implementation of distributed-delay control laws"

# Implementación del método de estabilización simultanea

## Ejemplo 1 - Doble integrador retardado

Para el sistema:

$$
\dot{x}(t) =
\begin{pmatrix}
0 & 1 \\
0 & 0
\end{pmatrix}
x(t) +
\begin{pmatrix}
0 \\
1
\end{pmatrix}
u(t - h)
$$

con $h = 1$.

El cual, bajo una ley de control de la forma:

$$
u(t) = k \left[ x(t) + \int_{-h}^0 e^{-A(\theta + h)} B u(t + \theta) d\theta \right]
$$

tiene un polinomio caracteristico:

$$
s^2 +  \left( h k_1 - k_2 \right) s - k_1
$$

Al cual podemos aplicar el criterio de estabilidad de Routh-Hurwitz y obtener:

$$
\begin{align}
k_1 &< 0 \\
k_2 &< h k_1
\end{align}
$$

Por lo que la gráfica de D-particiones del sistema en lazo cerrado se verá:

Por otro lado, para analizar el comportamiento del controlador, sustituimos los datos en la ecuación del controlador:

$$
\begin{align}
u(t) &=
\begin{pmatrix}
k_1 & k_2
\end{pmatrix} x(t) +
\begin{pmatrix}
k_1 & k_2
\end{pmatrix}
\int_{-h}^0 e^{-A(\theta + h)} B u(t + \theta) d\theta \\
&=
\begin{pmatrix}
k_1 & k_2
\end{pmatrix}
\begin{pmatrix}
x_1(t) \\
x_2(t)
\end{pmatrix} +
\begin{pmatrix}
k_1 & k_2
\end{pmatrix}
\int_{-h}^0 e^{-A(\theta + h)} B u(t + \theta) d\theta \\
\end{align}
$$

$$
\begin{align}
u(t) &=
\begin{pmatrix}
k_1 & k_2
\end{pmatrix}
\begin{pmatrix}
x_1(t) \\
x_2(t)
\end{pmatrix} +
\int_{-h}^0
\begin{pmatrix}
k_1 & k_2
\end{pmatrix}
\begin{pmatrix}
1 & - (\theta  +h) \\
0 & 1
\end{pmatrix}
\begin{pmatrix}
0 \\
1
\end{pmatrix} u(t + \theta) d\theta \\
&= k_1 x_1(t) + k_2 x_2(t) - \int_{-h}^0 k_1 \theta u(t + \theta) d\theta - \int_{-h}^0 k_1 h u(t + \theta) d\theta + \int_{-h}^0 k_2 u(t + \theta) d\theta \\
\end{align}
$$

y al aplicar la transformada de Laplace, tenemos:

$$
u(s) = k_1 x_1(s) + k_2 x_2(s) - h k_1 \frac{e^{-hs}}{s} u(s) + k_1 \frac{1 - e^{-hs}}{s^2} u(s) - h k_1 \frac{1 - e^{-hs}}{s} u(s) + k_2 \frac{1 - e^{-hs}}{s} u(s)
$$

por lo que al pasar a un solo lado todos los terminos de $u(s)$:

$$
\begin{align}
\left[ 1 + h k_1 \frac{e^{-hs}}{s} - k_1 \frac{1 - e^{-hs}}{s^2} + h k_1 \frac{1 - e^{-hs}}{s} - k_2 \frac{1 - e^{-hs}}{s} \right] u(s) &= k_1 x_1(s) + k_2 x_2(s) \\
\left[ 1 + \frac{h k_1 e^{-hs}}{s} - \frac{k_1}{s^2} + \frac{k_1 e^{-hs}}{s^2} + \frac{h k_1}{s} - \frac{h k_1 e^{-hs}}{s} - \frac{k_2}{s} + \frac{k_2 e^{-hs}}{s} \right] u(s) &= k_1 x_1(s) + k_2 x_2(s) \\
\left[ 1  - \frac{k_1}{s^2} + \frac{k_1 e^{-hs}}{s^2} + \frac{h k_1}{s} - \frac{k_2}{s} + \frac{k_2 e^{-hs}}{s} \right] u(s) &= k_1 x_1(s) + k_2 x_2(s) \\
\left[ 1  + \frac{k_1 e^{-hs} - k_1}{s^2} + \frac{h k_1 + k_2 e^{-hs} - k_2}{s} \right] u(s) &= k_1 x_1(s) + k_2 x_2(s)
\end{align}
$$

obtenemos el polinomio caracteristico de la ecuación de control:

$$
1  + \frac{k_1 e^{-hs} - k_1}{s^2} + \frac{h k_1 + k_2 e^{-hs} - k_2}{s}
$$

y al sustituir $s = j \omega$, obtendremos dos ecuaciones, correspondientes a la parte real e imaginaria:

$$
\begin{align}
k_1 \left[ \omega h - \sin{(\omega h)} \right] - k_2 \left[ \omega - \cos{(\omega h)} \right] &= 0 \\
- k_1 \left[ 1 - \cos{(\omega h)} \right] + k_2 \left[ \omega \sin{(\omega h)} \right] - \omega^2 &= 0 \\
\end{align}
$$

por lo que podemos despejar $k_2$ de ambas ecuaciones y obtener:

$$
k_2 = \frac{k_1 \left[ \omega h - \sin{(\omega h)} \right]}{\omega - \cos{(\omega h)}} = \frac{k_1 \left[ 1 - \cos{(\omega h)} \right] + \omega^2}{\omega \sin{(\omega h)}}
$$

y haciendo un poco de algebra, podemos obtener:

$$
\frac{k_1 \left[ \omega h - \sin{(\omega h)} \right]}{\omega - \cos{(\omega h)}} = \frac{k_1 \left[ 1 - \cos{(\omega h)} \right] + \omega^2}{\omega \sin{(\omega h)}}
$$

$$
\frac{k_1 \left[ \omega h - \sin{(\omega h)} \right] \left[ \omega \sin{(\omega h)} \right]}{\omega - \cos{(\omega h)}} - k_1 \left[ 1 - \cos{(\omega h)} \right] = \omega^2
$$

$$
k_1 \frac{\left[ \omega h - \sin{(\omega h)} \right] \left[ \omega \sin{(\omega h)} \right] - \left[ 1 - \cos{(\omega h)} \right] \left[ \omega - \cos{(\omega h)} \right]}{\omega - \cos{(\omega h)}} = \omega^2
$$

$$
k_1 = \frac{\omega^2 \left[ \omega - \cos{(\omega h)} \right]}{\left[ \omega h - \sin{(\omega h)} \right] \left[ \omega \sin{(\omega h)} \right] - \left[ 1 - \cos{(\omega h)} \right] \left[ \omega - \cos{(\omega h)} \right]}
$$

Si sustituimos un punto por debajo de esta curva, $(k_1, k_2) = (0, 0)$, podemos ver que el polinomio caracteristico es trivialmente estable por el criterio de Routh-Hurwitz:

$$
P(s) = 1
$$

por lo que la gráfica de D-particiones para el controlador queda:

Y el sistema con este controlador será estable para los valores de $k_1$ y $k_2$ escogidos tal que se encuentren en la intersección de estas dos regiones:

## Ejemplo 2 - Oscilador armónico retardado

Para el sistema:

$$
\dot{x}(t) =
\begin{pmatrix}
0 & 1 \\
-1 & 0
\end{pmatrix}
x(t) +
\begin{pmatrix}
0 \\
1
\end{pmatrix}
u(t - h)
$$

con $h = 1$.

En lazo cerrado tiene un cuasiplolinomio:

$$
s^2 + \frac{1}{2} s\left[ -j k_1 \left( e^{jh} - e^{-jh} \right) - k_2 \left( e^{jh} + e^{-jh} \right) \right] + \frac{1}{2} \left[ -k_1 \left( e^{jh} + e^{-jh} \right) + j k_2 \left( e^{jh} - e^{-jh} \right) \right]
$$

Con una gráfica de D-particiones:

Para el controlador tenemos un cuasipolinomio caracteristico:

$$
s^2 + 1 + k_1 j \left( s \sin{(h)} - \cos{(h)} + e^{-sh} \right) + k_2 \left( \sin{(h)} + s \cos{(h)} - s e^{-sh} \right)
$$

con una gráfica de D-Particiones:

# Implementación del método de introducción de dinámicas

IPythonNotebooks/Sistemas con retardo en la entrada/Trabajo Final - Sistemas con retardo en la entrada.ipynb