# Lab 6: Collisions in 2D
### Objectives
- To learn to take good measurements
- To learn how to estimate and propagate errors
- To learn how to take measurements to verify a theory
- To experiment with collisions

### Equipment
- One Collision table set up with Spark Timer and Carbon Mat
- A number of recording sheets
- One protractor
- One 30 cm ruler
- One scale

### Safety
- **Electrical Hazard**: The Spark Timer generates dots on the recording sheet by passing high voltage from one puck, through the carbon mat, to the other puck.
    - The red and black cables must always be securely connected between the Spark Timer and the Table and they must be in good condition.
    - Do not depress the foot switch without both pucks on the table.
    - **Do not touch the metal pucks while the footswitch is depressed.**
    - Alert your instructor if the cables are in poor condition, are not remaining seated, or if the sparking is clearly audible.
- Use the least possible force when sending a puck along the table. Ask your instructor to help you assemble a rubber-band launcher if you prefer---they will give you more reproducible results as well.
- Do not touch the latex tubes connecting the compressed air distributor to the air pucks. Latex is damaged by oils and UV light (it is also possible---though unlikely---that you might have an allergy to latex).
- Do not depress the footswitch until you are ready to take data. Leaving a puck in one place for longer than 20 seconds while generating spark data can damage the carbon mat.
- Do not write on the paper while it is on the carbon mat.

# Introduction
One of the primary results of Newtonian Mechanics is that, if there are no external forces, the momentum of a system is conserved---even if mechanical energy is not. This experiment will allow you to test that theory by sending two pucks towards each other and recording their speeds and trajectories both before and after a collision.

The collision table has a couple of features to help you analyze collisions: The pucks use compressed air to act like hovercraft---greatly reducing friction. And a Spark Timer will generate dots at a frequency of your choosing so that you can locate each puck accurately and use simple kinematics to determine the puck’s velocity and direction.

Your experiments should be performed so that it is clear whether or not momentum is conserved during collisions which both conserve and do not conserve kinetic energy.

It will be to your benefit to be slow and careful while performing this lab.

# Theory

### Conservation of Momentum
The momentum of an object is the product of the mass and the velocity of that object. Because velocity is a vector, momentum must also be a vector:

\begin{equation}
\vec{p} \equiv m \vec{v}
\tag{1}
\end{equation}

One must keep track of both the x and y components of the momentum at all times.

The momentum of a system of particles is simply the sum of the momenta of each particle:

\begin{equation}
\vec{p_{sys}} \equiv \sum _{i} \vec{p_{i}}
\tag{2}
\end{equation}

Remember that these are vectors and must be added using vector addition. Consult chapter three of your textbook for more details.

Because Newton’s second law actually reads

\begin{equation}
\vec{F} \equiv \frac{d \vec{p}}{dt}
\tag{3}
\end{equation}

it can be interpreted to say “an external force on a system induces a change in momentum of that system.” Therefore, if there is no external force, there is no change in momentum. (If you assume the mass of the system is constant and you know how to work with derivatives, you should be able to re-derive the old version:  $\vec{F} \equiv m \vec{a}$ ).

In a collision, the most important forces will be internal to the system. In other words, every force will be one of an “action-reaction” pair which comes from within the system itself (e.g. one puck striking the other). Because there will be no external forces (forces that have their origin outside the system), by Newton’s second law, we expect the momentum of the system to remain constant.

To verify this, you will need to measure the masses of the pucks and their velocities both before and after each collision.

### Elastic and Inelastic Collisions
Although *momentum* is conserved in all collisions (when they are free from external forces), it is not always true that *kinetic energy* is conserved. When two cars collide on an icy street, they may bounce off of one another (for low-speed collisions), or they may stick to one another (for high-speed collisions).

If the cars bounce off each other, we expect the *total* kinetic energy to be conserved (so that the sum of the kinetic energies before the collision equals the sum afterwards, even if the individual speeds changed). This kind of collision is called a **totally elastic collision.**

If the cars stick to each other, we expect that some (or all) of the kinetic energy was lost (where did it go?) These kinds of collisions are called **inelastic collisions.**

To determine if the collision was totally elastic or inelastic, you will need to compute the kinetic energy of each puck

\begin{equation}
K = \frac{1}{2} m v^2
\tag{4}
\end{equation}

and add them together. Then compare the sum before the collision to the sum following the collision.

### Kinematics
Clearly, you will need to compute the velocity of the air puck. The information you will have, however, are dots generated by the Spark Timer. The dots will be generated at a rate you set (either with a knob or with the digital controller) and that rate will allow you to compute the puck’s velocity using basic kinematics:

\begin{equation}
v \equiv \frac{\Delta s}{\Delta t}
\tag{5}
\end{equation}

where $\Delta s$ is the distance *between* dots and $\Delta t$ is the time between sparks. The Spark Timer has *frequency* settings (measured in Hz or 1/s). Remember that the frequency is inversely related to the period:

\begin{equation}
f \equiv \frac{t}{\tau}
\tag{6}
\end{equation}

(You can remember this by thinking about tapping your finger on the desk: If you tap your finger 8 times per second, then the time between taps is 1/8 = 0.125 second. If you tap your finger more quickly, the frequency goes *up*, but the time between taps must go down.)

### Velocity of the Center of Mass
If the two pucks were connected with a thin rod, the center of mass of the system would be the place where you could balance that rod. We find the center of mass of two objects by a weighted average:

\begin{equation}
x_{cm} = \frac{m_{1} x_{1} + m_{2} x_{2}}{m_{1} + m_{2}} \quad \textrm{and} \quad y_{cm} = \frac{m_{1} y_{1} + m_{2} y_{2}}{m_{1} + m_{2}}
\tag{7}
\end{equation}

By taking derivatives, we can find the x- and y-velocities of the center of mass:

\begin{equation}
v_{x,cm} = \frac{m_{1} v_{x,1} + m_{2} v_{x,2}}{m_{1} + m_{2}} \quad \textrm{and} \quad v_{y,cm} = \frac{m_{1} v_{y,1} + m_{2} v_{y,2}}{m_{1} + m_{2}}
\tag{8}
\end{equation}

### Uncertainty Analysis
Your Lab Manual and the previous labs can guide you in estimating the uncertainties in your measurements and propagating those errors into your computed energies and momenta. The uncertainty in the masses of the air puck will be determined by the accuracy of the scale.

The uncertainties in your velocities will come from how accurately you can measure distances and times. The uncertainty in the Spark Timer’s frequency is 1% of the frequency you chose. To reduce the uncertainty in your distance between dots, you can measure the distance, *L*, between *n* dots. The average distance between dots is then $\Delta s = L/(n-1)$. Your uncertainty in that distance is then $\delta (\Delta s) = \delta s/(n-1)$.

In some cases, it might be easier to use the standard deviation of a large number of
measurements as your measurement uncertainty.

# Experimental Procedure

### Setting up the Air Table
Before you begin, you will need to level the air table. To make this process easier, you should have all three feet on the same surface (do not have two feet on one table and one on another).

1. Remove one puck from the table and place the second puck in the center of the table directly under the air distributor.
2. Plug in/turn on the air compressor.
3. Hold the puck steady under the distributor and, carefully, release the puck without disturbing it.
4. If the puck tends to move to one side, you will need to raise (unscrew) the foot nearest that side of the table. In some cases, you will need to adjust two feet simultaneously.
5. When the table is level, the puck will meander slowly in no particular direction.

The more time you spend leveling the table (within reason), the better your results will be.

Important points to remember:

- Keep the compressor as far from the table as possible. The compressor will vibrate the table which may influence your results. If possible, keep the compressor on the floor.
- Keep the compressor as far from the Spark Timer as possible. The electric motor will generate Electro-Magnetic Interference (EMI) which can interfere with the proper working of the Spark Timer.
- Be gentle with the carbon mat. This is the source of your data. If you damage it, you may not get clear marks on the recording paper.
- Be very careful if you must detach the pucks from their tubes. Doing this improperly may cause the conductive chain to retract into the tubing---making it especially difficult to retrieve.

When you are ready to take data, place a sheet of blank newsprint paper over the carbon mat and then place both pucks on the paper.

### Totally Elastic Collisions
One puck will be launched from one edge of the table towards a stationary puck at the center of the table so that they collide near the center of the table. Before taking data, you should turn on the air compressor and practice launching the pucks a few times *without* using the Spark Timer.

Once you are ready to take data, turn on the Spark Timer and pick a frequency (you will have to use a few trials to find the frequency that gives you a good line of dots). The person launching the puck should be the one to use the foot pedal. Immediately after the puck is launched, hold the foot pedal down until a second or so after the collision. **Your data will appear on the underside of the paper sheet.**

Some things to consider before analyzing your data:

- Try aligning your coordinate system so that the x-axis points along the path of one of the pucks.
- On what part of the path should you take your measurements to compute the velocity of the puck both before and after the collision? How many dots should you use? How should you determine the uncertainty in this measurement?

```python
# Raw Data 1: The Elastic Collision

# Import Numpy and Pandas with standard names
import pandas as pd
import numpy as np

# Create an empty numpy array to hold the raw data
raw_data_1 = np.empty((3,6))

# Create a Pandas dataframe
df1 = pd.DataFrame(raw_data_1, columns=[" ",
                                        "v_x (m/s)",
                                        "v_y (m/s)",
                                        "p_x (kg m/s)",
                                        "p_y (kg m/s)",
                                         "Kinetic (J)"])

df1[' '] = ["Initial (“incoming”)","Mass I (“outgoing”)", "Mass II (“outgoing”)"]

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Spark Timer Frequency (s) and its uncertainty (1% of f)
f1 = 0.05
delta_f1 = 0.0005

# Incoming Mass (kg) and its uncertainty
m_in = 0.55840
delta_m_in = 0.005

# Dot Spacing of Incoming (m) and its uncertainty
Delta_s = 0.009
unc_Delta_s = 0.0005

# Outgoing Mass I (kg) and its uncertainty
m1 = 0.55840
delta_m1 = 0.005

# Dot Spacing of Outgoing I(m) and its uncertainty
Delta_s1 = 0.008
unc_Delta_s1 = 0.0005

# Angle of Outgoing I
theta1 = 19

# Outgoing Mass II (kg) and its uncertainty
m2 = 0.54277
delta_m2 = 0.005

# Dot Spacing of Outgoing II (m) and its uncertainty
Delta_s2 = 0.005
unc_Delta_s2 = 0.0005

# Angle of Outgoing II
theta2 = 308

# Velocities in the x and y directions
df1['v_x (m/s)'] = [0.18,0.151,0.0616]
df1['v_y (m/s)'] = [0,0.052,0.0780]

# Momentum in the x and y directions
df1['p_x (kg m/s)'] = [0.1005,0.0843,0.0334]
df1['p_y (kg m/s)'] = [0,0.0290,0.0423]

# Kinetic energy of the pucks
df1['Kinetic (J)'] = [0.00905,0.00715,0.00271]

###########################################

# Print statements for the data
print("Spark Timer Frequency: f1 = %0.2f s        𝛿f1 = %0.4f" % (f1, delta_f1))
print('-'*80)

print("Incoming Mass:         m = %0.4f kg      𝛿m = %0.3f" % (m_in, delta_m_in))
print("Dot Spacing:           Δs = %0.3f m       𝛿Δs = %0.4f" % (Delta_s, unc_Delta_s))
print('-'*80)

print("Outgoing Mass (I):     m1 = %0.4f kg     𝛿m1 = %0.3f" % (m1, delta_m1))
print("Dot Spacing:           Δs1 = %0.3f m      𝛿Δs1 = %0.4f" % (Delta_s1, unc_Delta_s1))
print("Angle:                 θ1 = %0.1f°" % (theta1))
print('-'*80)

print("Outgoing Mass (II):    m2 = %0.4f kg     𝛿m2 = %0.3f" % (m2, delta_m2))
print("Dot Spacing:           Δs2 = %0.3f m      𝛿Δs2 = %0.4f" % (Delta_s2, unc_Delta_s2))
print("Angle:                 θ2 = %0.1f°" % (theta2))
print()

from IPython.display import display
display(df1)
print ("Table 1: The Elastic Collision")
print()
```

    Spark Timer Frequency: f1 = 0.05 s        𝛿f1 = 0.0005
    --------------------------------------------------------------------------------
    Incoming Mass:         m = 0.5584 kg      𝛿m = 0.005
    Dot Spacing:           Δs = 0.009 m       𝛿Δs = 0.0005
    --------------------------------------------------------------------------------
    Outgoing Mass (I):     m1 = 0.5584 kg     𝛿m1 = 0.005
    Dot Spacing:           Δs1 = 0.008 m      𝛿Δs1 = 0.0005
    Angle:                 θ1 = 19.0°
    --------------------------------------------------------------------------------
    Outgoing Mass (II):    m2 = 0.5428 kg     𝛿m2 = 0.005
    Dot Spacing:           Δs2 = 0.005 m      𝛿Δs2 = 0.0005
    Angle:                 θ2 = 308.0°

      v_x (m/s)
      v_y (m/s)
      p_x (kg m/s)
      p_y (kg m/s)
      Kinetic (J)

      0
      Initial (“incoming”)
      0.1800
      0.000
      0.1005
      0.0000
      0.00905

      1
      Mass I (“outgoing”)
      0.1510
      0.052
      0.0843
      0.0290
      0.00715

      2
      Mass II (“outgoing”)
      0.0616
      0.078
      0.0334
      0.0423
      0.00271

    Table 1: The Elastic Collision

```python
# Raw Data 2: The Elastic Collision

# Create an empty numpy array to hold the raw data
raw_data_2 = np.empty((2,4))

# Create a Pandas dataframe
df2 = pd.DataFrame(raw_data_2, columns=[" ",
                                        "v (m/s)",
                                        "p(kg m/s)",
                                         "Kinetic (J)"])

df2[' '] = ["Initial (before collision)","Final (after collision)"]

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

df2['v (m/s)'] = [0.18,0.26]

df2['p(kg m/s)'] = [0.1005,0.1436]

df2['Kinetic (J)'] = [0.00905,0.00986]

###########################################

from IPython.display import display
display(df2)
print ("Table 2: Elastic Collision")
print()
```

      v (m/s)
      p(kg m/s)
      Kinetic (J)

      0
      Initial (before collision)
      0.18
      0.1005
      0.00905

      1
      Final (after collision)
      0.26
      0.1436
      0.00986

    Table 2: Elastic Collision

***

### Inelastic Collisions
The procedure for creating an inelastic collision is exactly the same as for creating an elastic collision except that you must modify the pucks slightly by making them “sticky.” This is accomplished by wrapping Velcro strips around each puck. It is not necessary that they Velcro actually adheres to the puck, but as long as it is wrapped tightly enough, it won’t come off.

The inelastic collision will have a complication: after the two pucks stick together, they may “orbit” each other as they move away from the collision location. You will need to determine the center-of-mass velocity of these pucks as they move away from the collision.

```python
# Raw Data 3: The Inelastic Collision

# Create an empty numpy array to hold the raw data
raw_data_3 = np.empty((4,6))

# Create a Pandas dataframe
df3 = pd.DataFrame(raw_data_3, columns=[" ",
                                        "v_x (m/s)",
                                        "v_y (m/s)",
                                        "p_x (kg m/s)",
                                        "p_y (kg m/s)",
                                         "Kinetic (J)"])

df3[' '] = ["Initial (""Incoming"")","Mass I (“outgoing”)", "Mass II (“outgoing”)", "CM"]

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Spark Timer Frequency (s) and its uncertainty (1% of f)
f1 = 0.05
delta_f1 = 0.0005

# Incoming Mass (kg) and its uncertainty
m_in = 0.56121
delta_m_in = 0.005

# Dot Spacing of Incoming (m) and its uncertainty
Delta_s = 0.015
unc_Delta_s = 0.0005

# Outgoing Mass I (kg) and its uncertainty
m1 = 0.56121
delta_m1 = 0.005

# Dot Spacing of Outgoing I(m) and its uncertainty
Delta_s1 = 0.01
unc_Delta_s1 = 0.0005

# Angle of Outgoing I
theta1 = 14

# Outgoing Mass II (kg) and its uncertainty
m2 = 0.5456
delta_m2 = 0.005

# Dot Spacing of Outgoing II (m) and its uncertainty
Delta_s2 = 0.005
unc_Delta_s2 = 0.0005

# Angle of Outgoing II
theta2 = -30

# Velocities in the x and y directions
df3['v_x (m/s)'] = [0.3,0.194,0.0866,0.1411]
df3['v_y (m/s)'] = [0,0.0484, 0.05, 0.04919]

# Momentum in the x and y directions
df3['p_x (kg m/s)'] = [0.1684,0.1089,0.0472,0.1562]
df3['p_y (kg m/s)'] = [0,0.0217,0.0273,0.0544]

# Kinetic energy of the pucks
df3['Kinetic (J)'] = [0.0253,0.01122,0.00273,0.02767]

###########################################

# Print statements for the data
print("Spark Timer Frequency: f1 = %0.2f s        𝛿f1 = %0.4f" % (f1, delta_f1))
print('-'*80)

print("Incoming Mass:         m = %0.4f kg      𝛿m = %0.3f" % (m_in, delta_m_in))
print("Dot Spacing:           Δs = %0.3f m       𝛿Δs = %0.4f" % (Delta_s, unc_Delta_s))
print('-'*80)

print("Outgoing Mass (I):     m1 = %0.4f kg     𝛿m1 = %0.3f" % (m1, delta_m1))
print("Dot Spacing:           Δs1 = %0.3f m      𝛿Δs1 = %0.4f" % (Delta_s1, unc_Delta_s1))
print("Angle:                 θ1 = %0.1f°" % (theta1))
print('-'*80)

print("Outgoing Mass (II):    m2 = %0.4f kg     𝛿m2 = %0.3f" % (m2, delta_m2))
print("Dot Spacing:           Δs2 = %0.3f m      𝛿Δs2 = %0.4f" % (Delta_s2, unc_Delta_s2))
print("Angle:                 θ2 = %0.1f°" % (theta2))
print()

from IPython.display import display
display(df3)
print ("Table 3: The Inelastic Collision")
print()
```

    Spark Timer Frequency: f1 = 0.05 s        𝛿f1 = 0.0005
    --------------------------------------------------------------------------------
    Incoming Mass:         m = 0.5612 kg      𝛿m = 0.005
    Dot Spacing:           Δs = 0.015 m       𝛿Δs = 0.0005
    --------------------------------------------------------------------------------
    Outgoing Mass (I):     m1 = 0.5612 kg     𝛿m1 = 0.005
    Dot Spacing:           Δs1 = 0.010 m      𝛿Δs1 = 0.0005
    Angle:                 θ1 = 14.0°
    --------------------------------------------------------------------------------
    Outgoing Mass (II):    m2 = 0.5456 kg     𝛿m2 = 0.005
    Dot Spacing:           Δs2 = 0.005 m      𝛿Δs2 = 0.0005
    Angle:                 θ2 = -30.0°

      v_x (m/s)
      v_y (m/s)
      p_x (kg m/s)
      p_y (kg m/s)
      Kinetic (J)

      0
      Initial (Incoming)
      0.3000
      0.00000
      0.1684
      0.0000
      0.02530

      1
      Mass I (“outgoing”)
      0.1940
      0.04840
      0.1089
      0.0217
      0.01122

      2
      Mass II (“outgoing”)
      0.0866
      0.05000
      0.0472
      0.0273
      0.00273

      3
      CM
      0.1411
      0.04919
      0.1562
      0.0544
      0.02767

    Table 3: The Inelastic Collision

```python
# Raw Data 4: Inelastic Collision

# Create an empty numpy array to hold the raw data
raw_data_4 = np.empty((2,4))

# Create a Pandas dataframe
df4 = pd.DataFrame(raw_data_4, columns=[" ",
                                        "v (m/s)",
                                        "p(kg m/s)",
                                         "Kinetic (J)"])

df4[' '] = ["Initial (before collision)","Final (after collision)"]

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

df4['v (m/s)'] = [0,0]

df4['p(kg m/s)'] = [0,0]

df4['Kinetic (J)'] = [0,0]

###########################################

from IPython.display import display
display(df4)
print ("Table 4: Inelastic Collision")
print()
```

      v (m/s)
      p(kg m/s)
      Kinetic (J)

      0
      Initial (before collision)
      0
      0
      0

      1
      Final (after collision)
      0
      0
      0

    Table 4: Inelastic Collision

***
Diagrams?
***

# Your Report

### Introduction
Write a few sentences about what you set out to measure and how you will compare the measured values with theory.

### Theory
Write few paragraphs about how you computed the momenta and kinetic energies for the pucks and what you expect them to be for your experiments. Use equations where necessary. As always, talk about how you computed your errors.

[**Extra Credit**: define “coefficient of restitution” AND....]

### Experimental Procedure
Write a paragraph or two about how you took your data. Write it so that someone else could repeat your experiment by just reading this section. What experiments did you perform? How did you measure the velocities of the pucks? Talk about any problems you encountered and how you handled them.

### Results and Conclusion
This will be your longest section. Use good paragraph structure to link the results together:

- How do your initial and final momenta compare for your experiments?
- How do your initial and final kinetic energies compare for your experiments?
- Does your data support or refute Conservation of Momentum? What, if anything, might have interfered with perfect agreement?
- If conservation of momentum is correct, what was your percent error and why was it not zero?
- Did your “elastic collisions” truly conserve kinetic energy? What was your percent error and why was it not zero?

[**Extra Credit**: ...AND Compute the coefficient of restitution for your elastic and inelastic
collisions]

As always, talk about your uncertainties and how you might be able to reduce them. Make sure that you discuss all possible sources of *experimental* error in your experiments.

### Appendix
*Include your signed raw data sheet(s) and Sample Calculations.*


```python
%load_ext magic_markdown
```

```python
%%mmd
MagicMarkdown cell magic uses the **mmd** command.
```

MagicMarkdown cell magic uses the **mmd** command.

```python
%%mmd
This is markdown with some python stuff. simply put a python expression into a mustache: {{5+10}}.
```

This is markdown with some python stuff. simply put a python expression into a mustache: 15.

```python
%%mmd
The cell is interpreted as Markdown, so html will naturally work.<br>
<b>some bold  text!</b>
```

The cell is interpreted as Markdown, so html will naturally work.

some bold  text!

```python
from IPython.display import JSON, Latex
```

```python
%%mmd
Additionally some of the other types that are defined in IPython.display can be used. Namely:
 - HTML
 - JSON
 - Image
 - SVG
 - Latex
 - Markdown

That means whenever your python expression return an element of these types they
{{
    JSON({"a":10, "b":[1,4,5]})
}}

<br><br>

For LaTeX all \ have to be escaped:

{{Latex("\\begin{align}F(x) &= \\int^a_b\\frac{x^2}{5} \\end{align}")}}
```

Additionally some of the other types that are defined in IPython.display can be used. Namely:
 - HTML
 - JSON
 - Image
 - SVG
 - Latex
 - Markdown

That means whenever your python expression return an element of these types they

    <IPython.core.display.JSON object>

For LaTeX all \ have to be escaped:

\begin{align}F(x) &= \int^a_b\frac{x^2}{5} \end{align}

```python
from random import randint

number_count = randint(500, 1000)
random_nums = [randint(0, 1000) for a in range(0, number_count)]
```

```python
%%mmd
There are {{number_count}} numbers. The highest is *{{max(random_nums)}}*
```

There are 948 numbers. The highest is *1000*

```python

```


```python
pip install matminer
```

    Requirement already satisfied: matminer in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (0.6.4)
    Requirement already satisfied: monty>=3.0.2 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (4.0.0)
    Requirement already satisfied: jsonschema>=3.2.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (3.2.0)
    Requirement already satisfied: pandas>=1.0.4 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (1.2.1)
    Requirement already satisfied: tqdm>=4.46.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (4.56.0)
    Requirement already satisfied: numpy>=1.19.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (1.19.2)
    Requirement already satisfied: requests>=2.23.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (2.25.1)
    Requirement already satisfied: future>=0.18.2 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (0.18.2)
    Requirement already satisfied: plotly>=4.8.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (4.14.3)
    Requirement already satisfied: sympy>=1.6 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (1.7.1)
    Requirement already satisfied: scikit-learn>=0.23.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (0.23.2)
    Requirement already satisfied: six>=1.15.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (1.15.0)
    Requirement already satisfied: pymongo>=3.10.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (3.11.2)
    Requirement already satisfied: pymatgen>=2020.4.29 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (2020.8.13)
    Requirement already satisfied: pint>=0.11 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matminer) (0.16.1)
    Requirement already satisfied: attrs>=17.4.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from jsonschema>=3.2.0->matminer) (20.3.0)
    Requirement already satisfied: pyrsistent>=0.14.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from jsonschema>=3.2.0->matminer) (0.17.3)
    Requirement already satisfied: setuptools in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from jsonschema>=3.2.0->matminer) (52.0.0.post20210125)
    Requirement already satisfied: python-dateutil>=2.7.3 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pandas>=1.0.4->matminer) (2.8.1)
    Requirement already satisfied: pytz>=2017.3 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pandas>=1.0.4->matminer) (2021.1)
    Requirement already satisfied: packaging in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pint>=0.11->matminer) (20.9)
    Requirement already satisfied: retrying>=1.3.3 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from plotly>=4.8.1->matminer) (1.3.3)
    Requirement already satisfied: scipy>=1.5.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (1.6.0)
    Requirement already satisfied: tabulate in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (0.8.7)
    Requirement already satisfied: networkx>=2.2 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (2.5)
    Requirement already satisfied: spglib>=1.9.9.44 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (1.16.0)
    Requirement already satisfied: palettable>=3.1.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (3.3.0)
    Requirement already satisfied: matplotlib>=1.5 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (3.3.4)
    Requirement already satisfied: ruamel.yaml>=0.15.6 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from pymatgen>=2020.4.29->matminer) (0.16.12)
    Requirement already satisfied: pillow>=6.2.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matplotlib>=1.5->pymatgen>=2020.4.29->matminer) (8.1.0)
    Requirement already satisfied: kiwisolver>=1.0.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matplotlib>=1.5->pymatgen>=2020.4.29->matminer) (1.3.1)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matplotlib>=1.5->pymatgen>=2020.4.29->matminer) (2.4.7)
    Requirement already satisfied: cycler>=0.10 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from matplotlib>=1.5->pymatgen>=2020.4.29->matminer) (0.10.0)
    Requirement already satisfied: decorator>=4.3.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from networkx>=2.2->pymatgen>=2020.4.29->matminer) (4.4.2)
    Requirement already satisfied: chardet<5,>=3.0.2 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from requests>=2.23.0->matminer) (4.0.0)
    Requirement already satisfied: urllib3<1.27,>=1.21.1 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from requests>=2.23.0->matminer) (1.26.3)
    Requirement already satisfied: certifi>=2017.4.17 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from requests>=2.23.0->matminer) (2020.12.5)
    Requirement already satisfied: idna<3,>=2.5 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from requests>=2.23.0->matminer) (2.10)
    Requirement already satisfied: ruamel.yaml.clib>=0.1.2 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from ruamel.yaml>=0.15.6->pymatgen>=2020.4.29->matminer) (0.2.2)
    Requirement already satisfied: threadpoolctl>=2.0.0 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from scikit-learn>=0.23.1->matminer) (2.1.0)
    Requirement already satisfied: joblib>=0.11 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from scikit-learn>=0.23.1->matminer) (1.0.0)
    Requirement already satisfied: mpmath>=0.19 in c:\users\sterg\anaconda3\envs\cgcnn\lib\site-packages (from sympy>=1.6->matminer) (1.1.0)
    Note: you may need to restart the kernel to use updated packages.

```python
conda develop "C:/Users/sterg/Documents/GitHub/sparks-baird/phylo-mat/code/cgcnn"
```

    added C:\Users\sterg\Documents\GitHub\sparks-baird\phylo-mat\code\cgcnn
    completed operation for: C:\Users\sterg\Documents\GitHub\sparks-baird\phylo-mat\code\cgcnn

    Note: you may need to restart the kernel to use updated packages.

```python
import os
from shutil import copyfile
import pandas as pd
```

```python
import numpy as np
```

```python
import glob
```

```python
from pymatgen import Structure
```

```python
from matminer.featurizers.structure import CGCNNFeaturizer
```

```python
properties = pd.read_csv("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH/id_prop.csv",header=None)
properties.shape
```

    (9507, 2)

```python
structures = []
#for structure_file in os.listdir("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH/*.cif"):
for structure_file in glob.glob("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH/*.cif"):
    #structure_path = 'C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-only-K_VRH/'+structure_file
    structure_path = structure_file
    structure = Structure.from_file(structure_path)
    structures.append(structure)
df = pd.DataFrame({"K_VRH": properties[1], "structure": structures})
print(df) # make sure the dataframe appears like you intended
df.to_pickle("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH.p")
```

    C:\Users\sterg\anaconda3\envs\cgcnn\lib\site-packages\pymatgen\io\cif.py:1123: UserWarning: Issues encountered while parsing CIF: Some fractional co-ordinates rounded to ideal values to avoid issues with finite precision.
      warnings.warn("Issues encountered while parsing CIF: %s" % "\n".join(self.warnings))

          K_VRH                                          structure
    0      89.0                                    [[0. 0. 0.] Cs]
    1     164.0  [[0. 0. 0.] Ba, [2.89438158 2.04663685 5.01321...
    2     104.0  [[ 1.69333912  0.9776498  10.60299963] Hf, [-4...
    3      33.0  [[2.07616833 0.94954182 2.511614  ] Nb, [4.152...
    4      96.0  [[0. 0. 0.] Al, [1.9651759  1.13459484 0.75350...
    ...     ...                                                ...
    9502  115.0  [[1.46542997 0.84606639 1.959571  ] W, [0.    ...
    9503   33.0  [[3.52723706 2.49413324 6.1093538 ] Mn, [1.175...
    9504   19.0  [[0. 0. 0.] Mn, [1.15572927 0.81722401 2.00178...
    9505   90.0  [[0. 0. 0.] Mn, [3.54876808 2.50935797 6.14664...
    9506   79.0  [[0. 0. 0.] Ni, [ 2.4427789   1.45429054 -2.34...

    [9507 rows x 2 columns]

```python
df.structure[0]
```

    Structure Summary
    Lattice
        abc : 5.29144986 5.291449860000001 5.29144986
     angles : 109.47122063 109.47122063 109.47122063
     volume : 114.05180557995794
          A : 4.988826771224387 0.0 -1.7638166196089895
          B : -2.4944133847827348 4.320450719439106 -1.7638166196089895
          C : 0.0 0.0 5.29144986
    PeriodicSite: Cs (0.0000, 0.0000, 0.0000) [0.0000, 0.0000, 0.0000]

```python
featurizer = CGCNNFeaturizer(task='regression', atom_init_fea=None, pretrained_name='bulk-moduli', warm_start_file=None, warm_start_latest=False, save_model_to_dir=None, save_checkpoint_to_dir=None, checkpoint_interval=100, del_checkpoint=True)
#featurizer = CGCNNFeaturizer(task='regression', atom_init_fea=None, pretrained_name=None, warm_start_file="C:/Users/sterg/Documents/GitHub/cgcnn/model_best.pth.tar", warm_start_latest=False, save_model_to_dir=None, save_checkpoint_to_dir=None, checkpoint_interval=100, del_checkpoint=True)
#featurizer = CGCNNFeaturizer(task='regression', atom_init_fea=None, pretrained_name=None, warm_start_file=None, warm_start_latest=False, save_model_to_dir=None, save_checkpoint_to_dir=None, checkpoint_interval=100, del_checkpoint=True)
```

```python
featurizer.__init__
```

```python
featurizer.fit(df.structure,df.K_VRH)
```

```python
%tb
```

```python
dir(featurizer)
featurizer.model
```

    CrystalGraphConvNetWrapper(
      (embedding): Linear(in_features=92, out_features=64, bias=True)
      (convs): ModuleList(
        (0): ConvLayer(
          (fc_full): Linear(in_features=169, out_features=128, bias=True)
          (sigmoid): Sigmoid()
          (softplus1): Softplus(beta=1, threshold=20)
          (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (softplus2): Softplus(beta=1, threshold=20)
        )
        (1): ConvLayer(
          (fc_full): Linear(in_features=169, out_features=128, bias=True)
          (sigmoid): Sigmoid()
          (softplus1): Softplus(beta=1, threshold=20)
          (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (softplus2): Softplus(beta=1, threshold=20)
        )
        (2): ConvLayer(
          (fc_full): Linear(in_features=169, out_features=128, bias=True)
          (sigmoid): Sigmoid()
          (softplus1): Softplus(beta=1, threshold=20)
          (bn1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (bn2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (softplus2): Softplus(beta=1, threshold=20)
        )
      )
      (conv_to_fc): Linear(in_features=64, out_features=128, bias=True)
      (conv_to_fc_softplus): Softplus(beta=1, threshold=20)
      (fc_out): Linear(in_features=128, out_features=1, bias=True)
    )

```python
features = featurizer.featurize_many(df.structure,ignore_errors=True,return_errors=False,pbar=True)
```

    CGCNNFeaturizer:   0%|          | 0/9507 [00:00<?, ?it/s]

```python
X=np.array(features)
print(X.shape)
X[2500]
```

    (9507, 64)

    array([1.37573373, 1.23462927, 1.16196263, 1.36911905, 1.31720543,
           1.40154791, 1.24330628, 1.43035173, 1.63459027, 1.51304066,
           1.21675014, 1.31668794, 1.42409384, 1.47255003, 1.4079994 ,
           1.28732741, 1.32336462, 1.28203249, 1.36411798, 1.47177756,
           1.30156195, 1.47156298, 1.19416833, 1.24935377, 1.31119406,
           1.29552698, 1.27407646, 1.47710788, 1.50354016, 1.61430931,
           1.32183707, 1.26828754, 1.25710285, 1.39060116, 1.24898636,
           1.35720718, 1.22399402, 1.27981389, 1.22088349, 1.26210034,
           1.19351184, 1.25716746, 1.26547062, 1.31304634, 1.23823953,
           1.27775526, 1.14454794, 1.39645159, 1.26624322, 1.28061473,
           1.31844151, 1.40172279, 1.49506128, 1.30037975, 1.26275694,
           1.20761442, 1.27975655, 1.38074481, 1.35537803, 1.46591377,
           1.27543914, 1.216537  , 1.38761342, 1.70867193])

```python
pwd
```

    'C:\\Users\\sterg\\Documents\\GitHub\\sparks-baird\\phylo-mat\\code\\matminer_examples\\matminer_examples\\machine_learning-nb'

```python
savepath = "../../../cgcnn/data/K_VRH-features.csv"
copypath = "../../../data/K_VRH-features.csv"
np.savetxt(savepath, X, delimiter=",")
copyfile(savepath,copypath)
```

    '../../../data/K_VRH-features.csv'

```python
X.shape
```

    (9507, 64)

# Code Graveyard

```python
featurizer = CGCNNFeaturizer(task='regression', atom_init_fea=None, pretrained_name=None, warm_start_file="../../../cgcnn/model_best.pth.tar", warm_start_latest=False, save_model_to_dir=None, save_checkpoint_to_dir=None, checkpoint_interval=100, del_checkpoint=True)
```

```python
dir(featurizer)
```

```python
#featurizer = CGCNNFeaturizer(task='regression', atom_init_fea=None, pretrained_name=None, warm_start_file=None, warm_start_latest=False, save_model_to_dir=None, save_checkpoint_to_dir=None, checkpoint_interval=100, del_checkpoint=True)
```

```python
import os
import pandas as pd
from pymatgen import Structure
```

```python
properties = pd.read_csv("id_prop.csv")
structures = []
for i, structure_file in enumerate(os.listdir("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH/*.cif"):
    property = get_property_from_index(i)
    structure = Structure.from_file(structure_file)
    properties.append(property)
    structures.append(structure)
    df = pd.DataFrame({“some_property”: properties, “structure”: structures})
    print(df) # make sure the dataframe appears like you intended
    df.to_pickle("/path/where/u/want/to/save/ur/dataframe.p")
```

```python
properties = []
structures = []
for i, structure_file in enumerate(os.listdir("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH/*.cif"):
    property = get_property_from_index(i)
    structure = Structure.from_file(structure_file)
    properties.append(property)
    structures.append(structure)
    df = pd.DataFrame({“some_property”: properties, “structure”: structures})
    print(df) # make sure the dataframe appears like you intended
    df.to_pickle("/path/where/u/want/to/save/ur/dataframe.p")
```

```python
properties = pd.read_csv("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH/id_prop.csv",header=None)
```

```python
props = properties[1]
```

```python
from matminer.datasets import load_dataset
```

```python
structures = []
for structure_file in os.listdir("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-only-K_VRH/"):
    structure_path = 'C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-only-K_VRH/'+structure_file
    structure = Structure.from_file(structure_path)
    structures.append(structure)
df = pd.DataFrame({"K_VRH": properties[1], "structure": structures})
print(df) # make sure the dataframe appears like you intended
df.to_pickle("C:/Users/sterg/Documents/GitHub/cgcnn/data/cif-K_VRH.p")
```

```python
df = load_dataset("boltztrap_mp");
```

```python
featurizer.fit(df.structure,df.s_p)
```

```python
%tb
```

```python
#featurizer.featurize_dataframe(df.mpid,df.structure)
```

```python
df
```

```python
%matplotlib inline
from matplotlib import pyplot as plt
from matminer.datasets import load_dataset
from matminer.featurizers.base import MultipleFeaturizer
#from matminer.featurizers.composition import ElementProperty, Stoichiometry, ValenceOrbital, IonProperty
#from matminer.featurizers.structure import (SiteStatsFingerprint, StructuralHeterogeneity,
#                                            ChemicalOrdering, StructureComposition, MaximumPackingEfficiency)
from matminer.featurizers.conversions import DictToObject
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import ShuffleSplit, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from scipy import stats
from tqdm import tqdm_notebook as tqdm
import numpy as np
```

```python
pip install ipywidgets
```

```python
dto = DictToObject(target_col_id='structure', overwrite_data=True)
```

```python
df = dto.featurize_dataframe(df, 'structure')
```

```python
featurizer.fit(df.structure,df.s_p)
```

```python

```

```python
X = featurizer.featurize_many(df['structure'], ignore_errors=False)
```

```python
X = np.array(X)
print('Input data shape:', X.shape)
```

```python

```


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

Promijeni vidljivost ovdje.

```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
```

## Pneumatsko upravljanje položaja

Kugla se nalazi unutar vertikalne cijevi na temelju strujanja zraka $u$ i spojena je putem opruge krutosti $K$ na dno cijevi; kugla je podvrgnuta gravitaciji i viskoznom trenju s koeficijentom $B$. Sila $F$ koja na kuglu djeluje protokom zraka proporcionalna je protoku zraka $u$ (u m/s) uz konstantu $G$; protok zraka može biti samo pozitivan (ulazak u cijev). S obzirom na masu kugle $m=2$ g, $B=1$ g/s, $K=2$ g/$\text{s}^2$ i $G=3$ Ns/m, cilj je dizajnirati regulator s ulazom $u$ i visinom položaja kugle $z$ kao izlazom sustava prema sljedećim zahtjevima:
- nulta pogreška u stacionarnom stanju (u odzivu na ulaz željene visine);
- maksimalno prekoračenje 30%
- vrijeme smirivanja za 5% pojasa tolerancije manje od 8 sekundi.

Jednadžba dinamike koja opisuje sustav je:

$$
    m\ddot{z} = -Kz -B\dot{z} + Gu
$$

Definiranjem vektora stanja kao $x=\begin{bmatrix} x_1 & x_2 \end{bmatrix}^T=\begin{bmatrix} z & \dot{z} \end{bmatrix}^T$, možemo pisati:

\begin{cases}
    \dot{x} = \begin{bmatrix} 0 & 1 \\ -\frac{K}{m} & -\frac{B}{m} \end{bmatrix}x + \begin{bmatrix} 0 \\ \frac{G}{m} \end{bmatrix}u \\
    y = \begin{bmatrix} 1 & 0 \end{bmatrix}x
\end{cases}

Polovi sustava su $-0.25\pm0.97i$.

### Dizajn regulatora
#### Dizajn kontrolera

Da bi se zadovoljila nulta pogreška u stacionarnom stanju, dodajemo integrator u sustav proširivanjem vektora stanja s $\dot{x_3} = x_1 - y_d$, gdje je $y_d$ referentni signal. Tada je prošireni sustav:

\begin{cases}
    \dot{x_a} = \begin{bmatrix} 0 & 1 & 0 \\ -\frac{K}{m} & -\frac{B}{m} & 0 \\ 1 & 0 & 0 \end{bmatrix}x_a + \begin{bmatrix} 0 & 0 \\ \frac{G}{m} & 0 \\ 0 & -1 \end{bmatrix}u \\
    y_a = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 0 & 1 \end{bmatrix}x_a \, .
\end{cases}

Budući da su polovi sustava spori u odnosu na zadane zahtjeve, povećavamo prirodnu frekvenciju kompleksnih polova stavljajući ih u $-0.5\pm1.94$, a, također, integrator zamjenjujemo s polom u $-2$.

#### Dizajn promatrača
Za procjenu stanja, razvijamo promatrač za podsustav $(x_1,\, x_2)$, a zatim uzimamo samo procjenu za $x_2$, jer se ostala stanja mjere izravno. Odabiremo $-5$ kao mjesto polova standardnog promatrača.

### Kako koristiti ovaj interaktivni primjer?
- Provjerite rad regulatora relativnom varijacijom mase kugle (parametar: delta m) i, ako je potrebno, pokušajte ispraviti poziciju pola.
- Testirajte regulator s početnom pogreškom u estimiranom stanju.
- Testirajte regulator s različitim tipovima referentnog ulaza.

```python
# Preparatory cell
b = 1E-03
k = 2E-03
G = 3
m = 2E-03
Aa = numpy.matrix([[0, 1, 0],
                   [-k/m, -b/m, 0],
                   [1, 0, 0]])
Ba = numpy.matrix([[0, 0],
                   [G/m, 0],
                   [0,-1]])
Ca = numpy.matrix([[1, 0, 0],
                   [0, 0, 1]])
A = Aa[0:2,0:2]
B = Ba[0:2,0]
C = Ca[0,0:2]

X0 = numpy.matrix('0.0')
K = numpy.matrix([8/15,-4.4,-4])
L = numpy.matrix([[23],[66]])

X0w = matrixWidget(1,1)
X0w.setM(X0)
Kw = matrixWidget(1,3)
Kw.setM(K)
Lw = matrixWidget(2,1)
Lw.setM(L)

eig1c = matrixWidget(1,1)
eig2c = matrixWidget(2,1)
eig3c = matrixWidget(1,1)
eig1c.setM(numpy.matrix([-2.0]))
eig2c.setM(numpy.matrix([[-0.5],[-1.94]]))
eig3c.setM(numpy.matrix([-2.0]))

eig1o = matrixWidget(1,1)
eig2o = matrixWidget(2,1)
eig1o.setM(numpy.matrix([-5.]))
eig2o.setM(numpy.matrix([[-5.],[0.]]))
```

```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= ['Postavi K i L', 'Postavi svojstvene vrijednosti'],
    value= 'Postavi svojstvene vrijednosti',
    description='',
    disabled=False
)

# Define the number of complex eigenvalues
sele = widgets.Dropdown(
    options= ['0 kompleksnih svojstvenih vrijednosti', '2 kompleksne svojstvene vrijednosti'],
    value= '2 kompleksne svojstvene vrijednosti',
    description='Kompleksne svojstvene vrijednosti:',
    disabled=False
)

#define type of ipout
selu = widgets.Dropdown(
    options=['impuls', 'step', 'sinus', 'Pravokutni val'],
    value='step',
    description='Tip smetnje:',
    style = {'description_width': 'initial'},
    disabled=False
)
# Define the values of the input
u = widgets.FloatSlider(
    value=0.1,
    min=0,
    max=0.8,
    step=0.1,
    description='Referentni signal [m]:',
    style = {'description_width': 'initial'},
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.1f',
)
mw = widgets.FloatSlider(
    value=0,
    min=-30,
    max=30,
    step=1,
    description='delta m [%]:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.1f',
)
period = widgets.FloatSlider(
    value=2,
    min=0.1,
    max=3,
    step=0.05,
    description='Period [s]: ',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.2f',
)
simTime = widgets.FloatText(
    value=8,
    description='',
    disabled=False
)
```

```python
# Support functions

def eigen_choice(sele):
    if sele == '0 kompleksnih svojstvenih vrijednosti':
        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 kompleksne svojstvene vrijednosti':
        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 == 'Postavi K i L':
        method = 1
        sele.disabled = True
    if selm == 'Postavi svojstvene vrijednosti':
        method = 2
        sele.disabled = False
    return method

def simulation(m,Kw,L,uw,selu,period,simTime,X0w):
    Aa = numpy.matrix([[0, 1, 0],
                   [-k/(0.002*(1+m/100.0)), -b/(0.002*(1+m/100.0)), 0],
                   [1, 0, 0]])
    Ba = numpy.matrix([[0, 0],
                       [G/(0.002*(1+m/100.0)), 0],
                       [0,-1]])
    A = Aa[0:2,0:2]
    B = Ba[0:2,0]

    e1 = numpy.linalg.eig(Aa-Ba[:,0]*Kw)[0]
    e1n = numpy.array([numpy.linalg.norm(e1[i]) for i in range(3)])
    e2 = numpy.linalg.eig(A-L*C)[0]
    e2n = numpy.array([numpy.linalg.norm(e2[i]) for i in range(2)])
    # print(e1n,e2n)

    n1 = 0
    if all(e1n > 0):
        if all(numpy.real(e1)<0):
            n1 = int(max(e1n)/2/numpy.pi*1000)
    if n1==0:
        n1 = 10000

    n2 = 0
    if all(e2n > 0):
        if all(numpy.real(e2)<0):
            n2 = int(max(e2n)/2/numpy.pi*1000)
    if n2==0:
        n2 = 10000
    if n1 >= n2:
        n = n1
    else:
        n = n2

    T = numpy.linspace(0,simTime,n)
    ts = T[1]-T[0]
    if selu == 'impuls': #selu
        R = [0 for t in range(0,len(T))]
        R[0] = uw
    if selu == 'step':
        R = [uw for t in range(0,len(T))]
    if selu == 'sinus':
        R = uw*numpy.sin(2*numpy.pi/period*T)
        #print(R)
    if selu == 'Pravokutni val':
        R = uw*numpy.sign(numpy.sin(2*numpy.pi/period*T))
        #print(R)

    Aad = (numpy.eye(3)+ts*Aa)
    Bad = ts*Ba
    Ad = (numpy.eye(2)+ts*(A-L*C))
    Bd = ts*B
    Ld = ts*L
    xa = numpy.matrix([[0],[0],[0]])
    xe = numpy.matrix([[0],[X0w]])
    x = numpy.vstack((xa,xe))
    X = [x]
    U = []

    for i in range(len(T)):
        u = (-Kw*numpy.matrix([[xa[0,0]],[xe[1,0]],[xa[2,0]]]))[0,0]
        if u < 0:
            u = 0

        xe = Ad*xe+Bd*u+Ld*xa[0,0]
        xa = Aad*xa+Bad*numpy.matrix([[u],[R[i]]])

        X.append(numpy.vstack((xa,xe)))
        U.append(u)

    xout = [
        [X[i][0,0] for i in range(len(T))],
        [X[i][1,0] for i in range(len(T))],
        [X[i][2,0] for i in range(len(T))],
        [X[i][3,0] for i in range(len(T))],
        [X[i][4,0] for i in range(len(T))]
    ]
    return T, U, R, xout, e1, e2
```

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

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

    if simTime != 0:
        pass
    else:
        simTime = 7

    T, U, R, xout, sol1, sol2 = simulation(mw,K,L,u,selu,period,simTime,X0w[0,0])

    if selu == 'step':
        found = False
        ST5 = 0
        for i in range(len(T)):
            if R[i] != 0:
                if abs((xout[0][i]-R[i])/R[i]) <= 0.05 and not found:
                    ST5 = T[i]
                    found = True
                if abs((xout[0][i]-R[i])/R[i]) > 0.05:
                    found = False
        OV = 0
        for i in range(len(T)):
            if R[i] != 0:
                if R[0]>0:
                    if (xout[0][i]-R[i]) > 0:
                        if abs((xout[0][i]-R[i])/R[i])*100 > OV:
                            OV = abs((xout[0][i]-R[i])/R[i])*100
                else:
                    if (xout[0][i]-R[i]) < 0:
                        if abs((xout[0][i]-R[i])/R[i])*100 > OV:
                            OV = abs((xout[0][i]-R[i])/R[i])*100
    else:
        ST5 = 'Nije definirano'
        OV = 'Nije definirano'
    print('Svojstvene vrijednosti sustava zatvorene petlje:',sol1)
    print('Svojstvene vrijednosti promatrača:',sol2)
    print('Informacije o koraku: \n\tVrijeme smirivanja (5%) [s]=',ST5,'\n\tPrekoračenje (%)=',OV)

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

    fig.add_subplot(221)
    plt.title('Izlazni odziv')
    plt.ylabel('Izlaz [m]')
    plt.plot(T,xout[0])
    plt.plot(T,R,'g--')
    plt.xlabel('$t$ [s]')
    plt.axvline(x=0,color='black',linewidth=0.8)
    plt.axhline(y=0,color='black',linewidth=0.8)
    plt.legend(['$y$','Reference'])
    plt.grid()

    fig.add_subplot(222)
    plt.title('Ulaz')
    plt.ylabel('$u$ [N]')
    plt.plot(T,U)
    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('Odziv stanja')
    plt.ylabel('Stanja')
    plt.plot(T,xout[0],
             T,xout[1],
             T,xout[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('Pogreška procjene')
    plt.ylabel('Pogreška')
    plt.plot(T,numpy.array(xout[1])-numpy.array(xout[4]))
    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_{2}$'])
    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('Svojstvene vrijednosti:',border=3),
                                          eig1c,
                                          eig2c,
                                          eig3c,
                                          widgets.Label(' ',border=3),
                                          widgets.Label(' ',border=3),
                                          widgets.Label('X0 est.:',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('Svojstvene vrijednosti:',border=3),
                                          eig1o,
                                          eig2o,
                                          widgets.Label(' ',border=3),
                                          widgets.VBox([widgets.Label('Vrijeme simulacije [s]:',border=3)]),
                                          widgets.VBox([simTime])]),
                            widgets.Label(' ',border=3),
                            widgets.HBox([u,
                                          mw,
                                          period,
                                          START])])
out2 = widgets.interactive_output(main_callback2, {'mw':mw, 'X0w':X0w, 'K':Kw, 'L':Lw,
                                                 'eig1c':eig1c, 'eig2c':eig2c, 'eig3c':eig3c, 'eig1o':eig1o, 'eig2o':eig2o,
                                                 'u':u, 'period':period, 'selm':selm, 'sele':sele, 'selu':selu, 'simTime':simTime, 'DW':DW})
out2.layout.height = '860px'
display(out2, alltogether2)
```

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

    VBox(children=(HBox(children=(Dropdown(index=1, options=('Postavi K i L', 'Postavi svojstvene vrijednosti'), v…