- - - -
# Mechpy Tutorials
a mechanical engineering toolbox

source code - https://github.com/nagordon/mechpy
documentation - https://nagordon.github.io/mechpy/web/

- - - -

Neal Gordon
2017-02-20

- - - -

## material testing analysis

This quick tutorial shows some simple scripts for analyzing material test data

## Python Initilaization with module imports

```python
# setup
import numpy as np
import sympy as sp
import pandas as pd
import scipy
from pprint import pprint
sp.init_printing(use_latex='mathjax')

import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (12, 8)  # (width, height)
plt.rcParams['font.size'] = 14
plt.rcParams['legend.fontsize'] = 16
from matplotlib import patches

get_ipython().magic('matplotlib')  # seperate window
get_ipython().magic('matplotlib inline') # inline plotting
```

    Using matplotlib backend: Qt4Agg

## Reading raw test data example 1

This example shows how to read multiple csv files and plot them together

```python
import glob as gb
from matplotlib.pyplot import *
%matplotlib inline

csvdir='./examples/'
```

```python
e=[]
y=[]

for s in specimen:
    files = gb.glob(csvdir + '*.csv')  # select all csv files
    fig, ax = subplots()
    title(s)
    Pult = []

    for f in files:
        d1 = pd.read_csv(f, skiprows=1)
        d1 = d1[1:]  # remove first row of string
        d1.columns = ['t', 'load', 'ext']  # rename columns
        d1.head()
        # remove commas in data
        for d in d1.columns:
            #d1.dtypes
            d1[d] = d1[d].map(lambda x: float(str(x).replace(',','')))
        Pult.append(np.max(d1.load))
        plot(d1.ext, d1.load)
        ylabel('Pult, lbs')
        xlabel('extension, in')

    e.append(np.std(Pult))
    y.append(np.average(Pult)     )
    show()

# bar chart
barwidth = 0.35       # the width of the bars

fig, ax = subplots()
x = np.arange(len(specimen))
ax.bar(x,  y, width=barwidth, yerr=e)

#ax.set_xticks(x)
xticks(x+barwidth/2, specimen, rotation='vertical')
title('Pult with sample average and stdev of n=3')
ylabel('Pult, lbs')
margins(0.05)
show()
```

## Reading test data - example 2

This example shows how to read a different format of data and plot

```python
f = 'Aluminum_loops.txt'
d1 = pd.read_csv(f, skiprows=4,delimiter='\t')
d1 = d1[1:]  # remove first row of string
d1.columns = ['time', 'load', 'cross','ext','strain','stress']  # rename columns
d1.head()
# remove commas in data
for d in d1.columns:
    #d1.dtypes
    d1[d] = d1[d].map(lambda x: float(str(x).replace(',','')))
plot(d1.ext, d1.load)
ylabel('stress')
xlabel('strain')
```

```python
d1.head()
```

      time
      load
      cross
      ext
      strain
      stress

      1
      0.124
      -0.009699
      0.001906
      0.000047
      0.000002
      -0.000157

      2
      0.224
      0.063070
      0.006730
      0.000826
      0.000032
      0.001023

      3
      0.324
      0.141036
      0.011673
      0.001650
      0.000065
      0.002288

      4
      0.424
      0.222520
      0.016736
      0.002506
      0.000098
      0.003611

      5
      0.524
      0.302994
      0.021679
      0.003338
      0.000131
      0.004916

## another example of plotting data

```python
f = 'al_MTS_test.csv'
d1 = pd.read_csv(f, skiprows=3,delimiter=',')
d1 = d1[1:]  # remove first row of string
d1 = d1[['Time','Axial Force', 'Axial Fine Displacement', 'Axial Length']]
d1.columns = ['time', 'load', 'strain','cross']  # rename columns
# remove commas in data
for d in d1.columns:
    #d1.dtypes
    d1[d] = d1[d].map(lambda x: float(str(x).replace(',','')))
plot(d1.strain, d1.load)
ylabel('stress')
xlabel('strain')
```

## Finding the "first" peak and delta-10 threshhold limit on force-displacement data of an aluminum coupon

 http://nbviewer.jupyter.org/github/demotu/BMC/blob/master/notebooks/DataFiltering.ipynb

```python

%matplotlib inline
from scipy import signal
from pylab import plot, xlabel, ylabel, title, rcParams, figure
import numpy as np
pltwidth = 16
pltheight = 8
rcParams['figure.figsize'] = (pltwidth, pltheight)

csv = np.genfromtxt('./stress_strain1.csv', delimiter=",")
disp = csv[:,0]
force = csv[:,1]
print('number of data points = %i' % len(disp))

def moving_average(x, window):
    """Moving average of 'x' with window size 'window'."""
    y = np.empty(len(x)-window+1)
    for i in range(len(y)):
        y[i] = np.sum(x[i:i+window])/window
    return y

plt1 = plot(disp, force);
xlabel('displacement');
ylabel('force');

```

```python
figure()
mywindow = 1000  # the larger the filter window, the more agressive the filtering
force2 = moving_average(force, mywindow)
x2 = range(len(force2))
plot(x2,  force2);
title('Force smoothed with moving average filter');
```

```python

# Find f' using diff to find the first intersection of the 0

# mvavgforce = mvavgforce[:len(mvavgforce)/2]
force2p = np.diff(force2)
x2p = range(len(force2p))
plot(x2p, force2p);
title('Slope of the smoothed curve')
```

```python
i = np.argmax(force2p<0)
### or
# i = where(force2p<0)[0][0]
#### or
# for i, f in enumerate(force2p):
#     if f < 0:
#         break
```

```python
plot(x2p,  force2p, i,force2p[i],'o', markersize=15);
title('find the point at which the slope goes negative, indicating a switch in the slope direction');
```

```python
plot(x2,  force2, i,force2[i],'o',markersize=15);
title('using that index, plot on the force-displacement curve');
```

```python
#Now, we need to find the next point from here that is 10 less.
delta = 1

i2 = np.argmax(force2[i]-delta > force2[i:])

# If that point does not exist on the immediate downward sloping path,
#then just choose the max point. In this case, 10 would exist very
#far away from the point and not be desireable
if i2 > i:
    i2=0
plot(x2,  force2, i,force2[i],'o', i2+i, force2[i2+i] ,'*', markersize=15);
```

```python

```

```python

```

```python
disp
```

    array([  0.00000000e+00,   9.99999000e-03,   1.99999810e-02, ...,
             4.21189994e+02,   4.21200003e+02,   4.21210013e+02])

## Modulus

```python
# remove nan
disp = disp[~np.isnan(force)]
force = force[~np.isnan(force)]
```

```python
A = 0.1 # area
stress = force/A / 1e3
strain = disp/25.4 * 1e-3
```

```python
plt.plot(strain, stress)
```

```python
stress_range = np.array([5, 15])
PL = 0.0005
E_tan = stress/strain
```

```python
assert(len(stress)==len(strain))
```

```python
i = (stress > stress_range[0])  &   (stress < stress_range[1])

stress_mod = stress[i]
strain_mod = strain[i]
```

```python
fit = np.polyfit(strain_mod,stress_mod,1)
fit_fn = np.poly1d(fit)
fit_fn
```

    poly1d([  1.97261266e+04,  -3.22210726e+00])

```python
PLi = np.argmax( (stress - (fit_fn(strain-PL)) < 0) )
PLi
```

    6095

```python
# fit_fn is now a function which takes in x and returns an estimate for y
#plt.text(4,4,fit_fn)
plt.plot(strain ,stress, 'y')
plot(strain, fit_fn(strain-PL) , '--k', strain[PLi], stress[PLi],'o')
plt.xlim(0, np.max(strain))
plt.ylim(0, np.max(stress))
```

```python
print('ultimate stress %f' % np.max(stress))
print('ultimate strain %f' % np.max(strain))

print('strain proportion limit %f' % strain[PLi])
print('stress proportion limit %f' % stress[PLi])
```

    ultimate stress 140.783900
    ultimate strain 0.016583
    strain proportion limit 0.002400
    stress proportion limit 30.838765

```python
E_tan = E_tan[~np.isinf(E_tan)]
strainE = strain[1:]
plot(strainE, E_tan,'b',  strainE[PLi], E_tan[PLi],'o')
plt.ylim([0,25000])
plt.title('Tangent Modulus')
```

```python

```


## Histograms of Oriented Gradients (HOG)

As we saw with the ORB algorithm, we can use keypoints in images to do keypoint-based matching to detect objects in images. These type of algorithms work great when you want to detect objects that have a lot of consistent internal features that are not affected by the background. For example, these algorithms work well for facial detection because faces have a lot of consistent internal features that don’t get affected by the image background, such as the eyes, nose, and mouth. However, these type of algorithms don’t work so well when attempting to do more general object recognition, say for example, pedestrian detection in images. The reason is that people don’t have consistent internal features, like faces do, because the body shape and style of every person is different (see Fig. 1). This means that every person is going to have a different set of internal features, and so we need something that can more generally describe a person.

Fig. 1. - Pedestrians.

One option is to try to detect pedestrians by their contours instead.  Detecting objects in images by their contours (boundaries) is very challenging because we have to deal with the difficulties brought about by the contrast between the background and the foreground. For example, suppose you wanted to detect a pedestrian in an image that is walking in front of a white building and she is wearing a white coat and black pants (see Fig. 2). We can see in Fig. 2, that since the background of the image is mostly white, the black pants are going to have a very high contrast, but the coat, since it is white as well, is going to have very low contrast. In this case, detecting the edges of pants is going to be easy but detecting the edges of the coat is going to be very difficult. This is where **HOG** comes in. HOG stands for **Histograms of Oriented Gradients** and it was first introduced by Navneet Dalal and Bill Triggs in 2005.

Fig. 2. - High and Low Contrast.

The HOG algorithm works by creating histograms of the distribution of gradient orientations in an image and then normalizing them in a very special way. This special normalization is what makes HOG so effective at detecting the edges of objects even in cases where the contrast is very low. These normalized histograms are put together into a feature vector, known as the HOG descriptor, that can be used to train a machine learning algorithm, such as a Support Vector Machine (SVM), to detect objects in images based on their boundaries (edges). Due to its great success and reliability, HOG has become one of the most widely used algorithms in computer vison for object detection.

In this notebook, you will learn:

* How the HOG algorithm works
* How to use OpenCV to create a HOG descriptor
* How to visualize the HOG descriptor.

# The HOG Algorithm

As its name suggests, the HOG algorithm, is based on creating histograms from the orientation of image gradients. The HOG algorithm is implemented in a series of steps:

1. Given the image of particular object, set a detection window (region of interest) that covers the entire object in the image (see Fig. 3).

2. Calculate the magnitude and direction of the gradient for each individual pixel in the detection window.

3. Divide the detection window into connected *cells* of pixels, with all cells being of the same size (see Fig. 3). The size of the cells is a free parameter and it is usually chosen so as to match the scale of the features that want to be detected. For example, in a 64 x 128 pixel detection window, square cells 6 to 8 pixels wide are suitable for detecting human limbs.

4. Create a Histogram for each cell, by first grouping the gradient directions of all pixels in each cell into a particular number of orientation (angular) bins; and then adding up the gradient magnitudes of the gradients in each angular bin (see Fig. 3). The number of bins in the histogram is a free parameter and it is usually set to 9 angular bins.

5. Group adjacent cells into *blocks* (see Fig. 3). The number of cells in each block is a free parameter and all blocks must be of the same size. The distance between each block (known as the stride) is a free parameter but it is usually set to half the block size, in which case you will get overlapping blocks (*see video below*).  The HOG algorithm has been shown empirically to work better with overlapping blocks.

6. Use the cells contained within each block to normalize the cell histograms in that block (see Fig. 3). If you have overlapping blocks this means that most cells will be normalized with respect to different blocks (*see video below*). Therefore, the same cell may have several different normalizations.

7. Collect all the normalized histograms from all the blocks into a single feature vector called the HOG descriptor.

8. Use the resulting HOG descriptors from many images of the same type of object to train a machine learning algorithm, such as an SVM, to detect those type of objects in images. For example, you could use the HOG descriptors from many images of pedestrians to train an SVM to detect pedestrians in images. The training is done with both positive a negative examples of the object you want detect in the image.

9. Once the SVM has been trained, a sliding window approach is used to try to detect and locate objects in images.  Detecting an object in the image entails finding the part of the image that looks similar to the HOG pattern learned by the SVM.

Fig. 3. - HOG Diagram.

Vid. 1. - HOG Animation.

# Why The HOG Algorithm Works

As we learned above, HOG creates histograms by adding the magnitude of the gradients in particular orientations in localized portions of the image called *cells*.  By doing this we guarantee that stronger gradients will contribute more to the magnitude of their respective angular bin, while the effects of weak and randomly oriented gradients resulting from noise are minimized. In this manner the histograms tell us the dominant gradient orientation of each cell.

### Dealing with contrast

Now, the magnitude of the dominant orientation can vary widely due to variations in local illumination and the contrast between the background and the foreground.

To account for the background-foreground contrast differences, the HOG algorithm tries to detect edges locally.  In order to do this, it defines groups of cells, called **blocks**, and normalizes the histograms using this local group of cells. By normalizing locally, the HOG algorithm can detect the edges in each block very reliably; this is called **block normalization**.

In addition to using block normalization, the HOG algorithm also uses overlapping blocks to increase its performance. By using overlapping blocks, each cell contributes several independent components to the final HOG descriptor, where each component corresponds to a cell being normalized with respect to a different block. This may seem redundant but, it has been shown empirically that by normalizing each cell several times with respect to different local blocks, the performance of the HOG algorithm increases dramatically.

### Loading Images and Importing Resources

The first step in building our HOG descriptor is to load the required packages into Python and to load our image.

We start by using OpenCV to load an image of a triangle tile. Since, the `cv2.imread()` function loads images as BGR we will convert our image to RGB so we can display it with the correct colors. As usual we will convert our BGR image to Gray Scale for analysis.

```python
import cv2
import numpy as np
import matplotlib.pyplot as plt

# Set the default figure size
plt.rcParams['figure.figsize'] = [17.0, 7.0]

# Load the image
image = cv2.imread('./images/triangle_tile.jpeg')

# Convert the original image to RGB
original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Convert the original image to gray scale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Print the shape of the original and gray scale images
print('The original image has shape: ', original_image.shape)
print('The gray scale image has shape: ', gray_image.shape)

# Display the images
plt.subplot(121)
plt.imshow(original_image)
plt.title('Original Image')
plt.subplot(122)
plt.imshow(gray_image, cmap='gray')
plt.title('Gray Scale Image')
plt.show()
```

# Creating The HOG Descriptor

We will be using OpenCV’s `HOGDescriptor` class to create the HOG descriptor. The parameters of the HOG descriptor are setup using the `HOGDescriptor()` function. The parameters of the `HOGDescriptor()` function and their default values are given below:

`cv2.HOGDescriptor(win_size = (64, 128),
                  block_size = (16, 16),
                  block_stride = (8, 8),
                  cell_size = (8, 8),
                  nbins = 9,
                  win_sigma = DEFAULT_WIN_SIGMA,
                  threshold_L2hys = 0.2,
                  gamma_correction = true,
                  nlevels = DEFAULT_NLEVELS)`

Parameters:

* **win_size** – *Size*
Size of detection window in pixels (*width, height*). Defines the region of interest. Must be an integer multiple of cell size.

* **block_size** – *Size*
Block size in pixels (*width, height*). Defines how many cells are in each block. Must be an integer multiple of cell size and it must be smaller than the detection window. The smaller the block the finer detail you will get.

* **block_stride** – *Size*
Block stride in pixels (*horizontal, vertical*). It must be an integer multiple of cell size. The `block_stride` defines the distance between adjecent blocks, for example, 8 pixels horizontally and 8 pixels vertically. Longer `block_strides` makes the algorithm run faster (because less blocks are evaluated) but the algorithm may not perform as well.

* **cell_size** – *Size*
Cell size in pixels (*width, height*). Determines the size fo your cell. The smaller the cell the finer detail you will get.

* **nbins** – *int*
Number of bins for the histograms. Determines the number of angular bins used to make the histograms. With more bins you capture more gradient directions. HOG uses unsigned gradients, so the angular bins will have values between 0 and 180 degrees.

* **win_sigma** – *double*
Gaussian smoothing window parameter. The performance of the HOG algorithm can be improved by smoothing the pixels near the edges of the blocks by applying a Gaussian spatial window to each pixel before computing the histograms.

* **threshold_L2hys** – *double*
L2-Hys (Lowe-style clipped L2 norm) normalization method shrinkage. The L2-Hys method is used to normalize the blocks and it consists of an L2-norm followed by clipping and a renormalization. The clipping limits the maximum value of the descriptor vector for each block to have the value of the given threshold (0.2 by default). After the clipping the descriptor vector is renormalized as described in *IJCV*, 60(2):91-110, 2004.

* **gamma_correction** – *bool*
Flag to specify whether the gamma correction preprocessing is required or not. Performing gamma correction slightly increases the performance of the HOG algorithm.

* **nlevels** – *int*
Maximum number of detection window increases.

As we can see, the  `cv2.HOGDescriptor()`function supports a wide range of parameters. The first few arguments (`block_size, block_stride, cell_size`, and `nbins`) are probably the ones you are most likely to change. The other parameters can be safely left at their default values and you will get good results.

In the code below, we will use the `cv2.HOGDescriptor()`function to set the cell size, block size, block stride, and the number of bins for the histograms of the HOG descriptor. We will then use `.compute(image)`method to compute the HOG descriptor (feature vector) for the given `image`.

```python
# Specify the parameters for our HOG descriptor

# Cell Size in pixels (width, height). Must be smaller than the size of the detection window
# and must be chosen so that the resulting Block Size is smaller than the detection window.
cell_size = (6, 6)

# Number of cells per block in each direction (x, y). Must be chosen so that the resulting
# Block Size is smaller than the detection window
num_cells_per_block = (2, 2)

# Block Size in pixels (width, height). Must be an integer multiple of Cell Size.
# The Block Size must be smaller than the detection window
block_size = (num_cells_per_block[0] * cell_size[0],
              num_cells_per_block[1] * cell_size[1])

# Calculate the number of cells that fit in our image in the x and y directions
x_cells = gray_image.shape[1] // cell_size[0]
y_cells = gray_image.shape[0] // cell_size[1]

# Horizontal distance between blocks in units of Cell Size. Must be an integer and it must
# be set such that (x_cells - num_cells_per_block[0]) / h_stride = integer.
h_stride = 1

# Vertical distance between blocks in units of Cell Size. Must be an integer and it must
# be set such that (y_cells - num_cells_per_block[1]) / v_stride = integer.
v_stride = 1

# Block Stride in pixels (horizantal, vertical). Must be an integer multiple of Cell Size
block_stride = (cell_size[0] * h_stride, cell_size[1] * v_stride)

# Number of gradient orientation bins
num_bins = 9

# Specify the size of the detection window (Region of Interest) in pixels (width, height).
# It must be an integer multiple of Cell Size and it must cover the entire image. Because
# the detection window must be an integer multiple of cell size, depending on the size of
# your cells, the resulting detection window might be slightly smaller than the image.
# This is perfectly ok.
win_size = (x_cells * cell_size[0] , y_cells * cell_size[1])

# Print the shape of the gray scale image for reference
print('\nThe gray scale image has shape: ', gray_image.shape)
print()

# Print the parameters of our HOG descriptor
print('HOG Descriptor Parameters:\n')
print('Window Size:', win_size)
print('Cell Size:', cell_size)
print('Block Size:', block_size)
print('Block Stride:', block_stride)
print('Number of Bins:', num_bins)
print()

# Set the parameters of the HOG descriptor using the variables defined above
hog = cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, num_bins)

# Compute the HOG Descriptor for the gray scale image
hog_descriptor = hog.compute(gray_image)
```

# Number of Elements In The HOG Descriptor

The resulting HOG Descriptor (feature vector), contains the normalized histograms from all cells from all blocks in the detection window concatenated in one long vector. Therefore, the size of the HOG feature vector will be given by the total number of blocks in the detection window, multiplied by the number of cells per block, times the number of orientation bins:

\begin{equation}
\mbox{total_elements} = (\mbox{total_number_of_blocks})\mbox{ } \times \mbox{ } (\mbox{number_cells_per_block})\mbox{ } \times \mbox{ } (\mbox{number_of_bins})
\end{equation}

If we don’t have overlapping blocks (*i.e.* the `block_stride`equals the `block_size`), the total number of blocks can be easily calculated by dividing the size of the detection window by the block size. However, in the general case we have to take into account the fact that we have overlapping blocks. To find the total number of blocks in the general case (*i.e.* for any `block_stride` and `block_size`), we can use the formula given below:

\begin{equation}
\mbox{Total}_i = \left( \frac{\mbox{block_size}_i}{\mbox{block_stride}_i} \right)\left( \frac{\mbox{window_size}_i}{\mbox{block_size}_i} \right) - \left [\left( \frac{\mbox{block_size}_i}{\mbox{block_stride}_i} \right) - 1 \right]; \mbox{  for  } i = x,y
\end{equation}

Where Total$_x$, is the total number of blocks along the width of the detection window, and Total$_y$, is the total number of blocks along the height of the detection window. This formula for Total$_x$ and Total$_y$, takes into account the extra blocks that result from overlapping. After calculating Total$_x$ and Total$_y$, we can get the total number of blocks in the detection window by multiplying Total$_x$  $\times$ Total$_y$. The above formula can be simplified considerably because the `block_size`, `block_stride`, and `window_size`are all defined in terms of the `cell_size`. By making all the appropriate substitutions and cancelations the above formula reduces to:

\begin{equation}
\mbox{Total}_i = \left(\frac{\mbox{cells}_i - \mbox{num_cells_per_block}_i}{N_i}\right) + 1\mbox{  }; \mbox{  for  } i = x,y
\end{equation}

Where cells$_x$ is the total number of cells along the width of the detection window, and cells$_y$, is the total number of cells along the height of the detection window. And $N_x$ is the horizontal block stride in units of `cell_size` and $N_y$ is the vertical block stride in units of `cell_size`.

Let's calculate what the number of elements for the HOG feature vector should be and check that it matches the shape of the HOG Descriptor calculated above.

```python
# Calculate the total number of blocks along the width of the detection window
tot_bx = np.uint32(((x_cells - num_cells_per_block[0]) / h_stride) + 1)

# Calculate the total number of blocks along the height of the detection window
tot_by = np.uint32(((y_cells - num_cells_per_block[1]) / v_stride) + 1)

# Calculate the total number of elements in the feature vector
tot_els = (tot_bx) * (tot_by) * num_cells_per_block[0] * num_cells_per_block[1] * num_bins

# Print the total number of elements the HOG feature vector should have
print('\nThe total number of elements in the HOG Feature Vector should be: ',
      tot_bx, 'x',
      tot_by, 'x',
      num_cells_per_block[0], 'x',
      num_cells_per_block[1], 'x',
      num_bins, '=',
      tot_els)

# Print the shape of the HOG Descriptor to see that it matches the above
print('\nThe HOG Descriptor has shape:', hog_descriptor.shape)
print()
```

# Visualizing The HOG Descriptor

We can visualize the HOG Descriptor by plotting the histogram associated with each cell as a collection of vectors. To do this, we will plot each bin in the histogram as a single vector whose magnitude is given by the height of the bin and its orientation is given by the angular bin that its associated with. Since any given cell might have multiple histograms associated with it, due to the overlapping blocks, we will choose to average all the histograms for each cell to produce a single histogram for each cell.

OpenCV has no easy way to visualize the HOG Descriptor, so we have to do some manipulation first in order to visualize it. We will start by reshaping the HOG Descriptor in order to make our calculations easier.  We will then compute the average histogram of each cell and finally we will convert the histogram bins into vectors. Once we have the vectors, we plot the corresponding vectors for each cell in an image.

The code below produces an interactive plot so that you can interact with the figure. The figure contains:
* the grayscale image,
* the HOG Descriptor (feature vector),
* a zoomed-in portion of the HOG Descriptor, and
* the histogram of the selected cell.

**You can click anywhere on the gray scale image or the HOG Descriptor image to select a particular cell**. Once you click on either image a *magenta* rectangle will appear showing the cell you selected. The Zoom Window will show you a zoomed in version of the HOG descriptor around the selected cell; and the histogram plot will show you the corresponding histogram for the selected cell. The interactive window also has buttons at the bottom that allow for other functionality, such as panning, and giving you the option to save the figure if desired. The home button returns the figure to its default value.

**NOTE**: If you are running this notebook in the Udacity workspace, there is around a 2 second lag in the interactive plot. This means that if you click in the image to zoom in, it will take about 2 seconds for the plot to refresh.

```python
%matplotlib notebook

import copy
import matplotlib.patches as patches

# Set the default figure size
plt.rcParams['figure.figsize'] = [9.8, 9]

# Reshape the feature vector to [blocks_y, blocks_x, num_cells_per_block_x, num_cells_per_block_y, num_bins].
# The blocks_x and blocks_y will be transposed so that the first index (blocks_y) referes to the row number
# and the second index to the column number. This will be useful later when we plot the feature vector, so
# that the feature vector indexing matches the image indexing.
hog_descriptor_reshaped = hog_descriptor.reshape(tot_bx,
                                                 tot_by,
                                                 num_cells_per_block[0],
                                                 num_cells_per_block[1],
                                                 num_bins).transpose((1, 0, 2, 3, 4))

# Print the shape of the feature vector for reference
print('The feature vector has shape:', hog_descriptor.shape)

# Print the reshaped feature vector
print('The reshaped feature vector has shape:', hog_descriptor_reshaped.shape)

# Create an array that will hold the average gradients for each cell
ave_grad = np.zeros((y_cells, x_cells, num_bins))

# Print the shape of the ave_grad array for reference
print('The average gradient array has shape: ', ave_grad.shape)

# Create an array that will count the number of histograms per cell
hist_counter = np.zeros((y_cells, x_cells, 1))

# Add up all the histograms for each cell and count the number of histograms per cell
for i in range (num_cells_per_block[0]):
    for j in range(num_cells_per_block[1]):
        ave_grad[i:tot_by + i,
                 j:tot_bx + j] += hog_descriptor_reshaped[:, :, i, j, :]

        hist_counter[i:tot_by + i,
                     j:tot_bx + j] += 1

# Calculate the average gradient for each cell
ave_grad /= hist_counter

# Calculate the total number of vectors we have in all the cells.
len_vecs = ave_grad.shape[0] * ave_grad.shape[1] * ave_grad.shape[2]

# Create an array that has num_bins equally spaced between 0 and 180 degress in radians.
deg = np.linspace(0, np.pi, num_bins, endpoint = False)

# Each cell will have a histogram with num_bins. For each cell, plot each bin as a vector (with its magnitude
# equal to the height of the bin in the histogram, and its angle corresponding to the bin in the histogram).
# To do this, create rank 1 arrays that will hold the (x,y)-coordinate of all the vectors in all the cells in the
# image. Also, create the rank 1 arrays that will hold all the (U,V)-components of all the vectors in all the
# cells in the image. Create the arrays that will hold all the vector positons and components.
U = np.zeros((len_vecs))
V = np.zeros((len_vecs))
X = np.zeros((len_vecs))
Y = np.zeros((len_vecs))

# Set the counter to zero
counter = 0

# Use the cosine and sine functions to calculate the vector components (U,V) from their maginitudes. Remember the
# cosine and sine functions take angles in radians. Calculate the vector positions and magnitudes from the
# average gradient array
for i in range(ave_grad.shape[0]):
    for j in range(ave_grad.shape[1]):
        for k in range(ave_grad.shape[2]):
            U[counter] = ave_grad[i,j,k] * np.cos(deg[k])
            V[counter] = ave_grad[i,j,k] * np.sin(deg[k])

            X[counter] = (cell_size[0] / 2) + (cell_size[0] * i)
            Y[counter] = (cell_size[1] / 2) + (cell_size[1] * j)

            counter = counter + 1

# Create the bins in degress to plot our histogram.
angle_axis = np.linspace(0, 180, num_bins, endpoint = False)
angle_axis += ((angle_axis[1] - angle_axis[0]) / 2)

# Create a figure with 4 subplots arranged in 2 x 2
fig, ((a,b),(c,d)) = plt.subplots(2,2)

# Set the title of each subplot
a.set(title = 'Gray Scale Image\n(Click to Zoom)')
b.set(title = 'HOG Descriptor\n(Click to Zoom)')
c.set(title = 'Zoom Window', xlim = (0, 18), ylim = (0, 18), autoscale_on = False)
d.set(title = 'Histogram of Gradients')

# Plot the gray scale image
a.imshow(gray_image, cmap = 'gray')
a.set_aspect(aspect = 1)

# Plot the feature vector (HOG Descriptor)
b.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 5)
b.invert_yaxis()
b.set_aspect(aspect = 1)
b.set_facecolor('black')

# Define function for interactive zoom
def onpress(event):

    #Unless the left mouse button is pressed do nothing
    if event.button != 1:
        return

    # Only accept clicks for subplots a and b
    if event.inaxes in [a, b]:

        # Get mouse click coordinates
        x, y = event.xdata, event.ydata

        # Select the cell closest to the mouse click coordinates
        cell_num_x = np.uint32(x / cell_size[0])
        cell_num_y = np.uint32(y / cell_size[1])

        # Set the edge coordinates of the rectangle patch
        edgex = x - (x % cell_size[0])
        edgey = y - (y % cell_size[1])

        # Create a rectangle patch that matches the the cell selected above
        rect = patches.Rectangle((edgex, edgey),
                                  cell_size[0], cell_size[1],
                                  linewidth = 1,
                                  edgecolor = 'magenta',
                                  facecolor='none')

        # A single patch can only be used in a single plot. Create copies
        # of the patch to use in the other subplots
        rect2 = copy.copy(rect)
        rect3 = copy.copy(rect)

        # Update all subplots
        a.clear()
        a.set(title = 'Gray Scale Image\n(Click to Zoom)')
        a.imshow(gray_image, cmap = 'gray')
        a.set_aspect(aspect = 1)
        a.add_patch(rect)

        b.clear()
        b.set(title = 'HOG Descriptor\n(Click to Zoom)')
        b.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 5)
        b.invert_yaxis()
        b.set_aspect(aspect = 1)
        b.set_facecolor('black')
        b.add_patch(rect2)

        c.clear()
        c.set(title = 'Zoom Window')
        c.quiver(Y, X, U, V, color = 'white', headwidth = 0, headlength = 0, scale_units = 'inches', scale = 1)
        c.set_xlim(edgex - cell_size[0], edgex + (2 * cell_size[0]))
        c.set_ylim(edgey - cell_size[1], edgey + (2 * cell_size[1]))
        c.invert_yaxis()
        c.set_aspect(aspect = 1)
        c.set_facecolor('black')
        c.add_patch(rect3)

        d.clear()
        d.set(title = 'Histogram of Gradients')
        d.grid()
        d.set_xlim(0, 180)
        d.set_xticks(angle_axis)
        d.set_xlabel('Angle')
        d.bar(angle_axis,
              ave_grad[cell_num_y, cell_num_x, :],
              180 // num_bins,
              align = 'center',
              alpha = 0.5,
              linewidth = 1.2,
              edgecolor = 'k')

        fig.canvas.draw()

# Create a connection between the figure and the mouse click
fig.canvas.mpl_connect('button_press_event', onpress)
plt.show()
```

# Understanding The Histograms

Let's take a look at a couple of snapshots of the above figure to see if the histograms for the selected cell make sense. Let's start looking at a cell that is inside a triangle and not near an edge:

Fig. 4. - Histograms Inside a Triangle.

In this case, since the triangle is nearly all of the same color there shouldn't be any dominant gradient in the selected cell. As we can clearly see in the Zoom Window and the histogram, this is indeed the case. We have many gradients but none of them clearly dominates over the other.

Now let’s take a look at a cell that is near a horizontal edge:

Fig. 5. - Histograms Near a Horizontal Edge.

Remember that edges are areas of an image where the intensity changes abruptly. In these cases, we will have a high intensity gradient in some particular direction. This is exactly what we see in the corresponding histogram and Zoom Window for the selected cell.  In the Zoom Window, we can see that the dominant gradient is pointing up, almost at 90 degrees, since that’s the direction in which there is a sharp change in intensity. Therefore, we should expect to see the 90-degree bin in the histogram to dominate strongly over the others. This is in fact what we see.

Now let’s take a look at a cell that is near a vertical edge:

Fig. 6. - Histograms Near a Vertical Edge.

In this case we expect the dominant gradient in the cell to be horizontal, close to 180 degrees, since that’s the direction in which there is a sharp change in intensity. Therefore, we should expect to see the 170-degree bin in the histogram to dominate strongly over the others. This is what we see in the histogram but we also see that there is another dominant gradient in the cell, namely the one in the 10-degree bin. The reason for this, is because the HOG algorithm is using unsigned gradients, which means 0 degrees and 180 degrees are considered the same. Therefore, when the histograms are being created, angles between 160 and 180 degrees, contribute proportionally to both the 10-degree bin and the 170-degree bin. This results in there being two dominant gradients in the cell near the vertical edge instead of just one.

To conclude let’s take a look at a cell that is near a diagonal edge.

Fig. 7. - Histograms Near a Diagonal Edge.

To understand what we are seeing, let’s first remember that gradients have an *x*-component, and a *y*-component, just like vectors. Therefore, the resulting orientation of a gradient is going to be given by the vector sum of its components. For this reason, on vertical edges the gradients are horizontal, because they only have an x-component, as we saw in Figure 4. While on horizontal edges the gradients are vertical, because they only have a y-component, as we saw in Figure 3. Consequently, on diagonal edges, the gradients are also going to be diagonal because both the *x* and *y* components are non-zero.  Since the diagonal edges in the image are close to 45 degrees, we should expect to see a dominant gradient orientation in the 50-degree bin. This is in fact what we see in the histogram but, just like in Figure 4., we see there are two dominant gradients instead of just one. The reason for this is that when the histograms are being created, angles that are near the boundaries of bins, contribute proportionally to the adjacent bins.  For example, a gradient with an angle of 40 degrees, is right in the middle of the 30-degree and 50-degree bin. Therefore, the magnitude of the gradient is split evenly into the 30-degree and 50-degree bin. This results in there being two dominant gradients in the cell near the diagonal edge instead of just one.

Now that you know how HOG is implemented, in the workspace you will find a notebook named *Examples*. In there, you will be able set your own paramters for the HOG descriptor for various images. Have fun!


```python
from IPython.display import Image
from IPython.core.display import HTML
from sympy import *; x,h,y,t = symbols("x h y t")
Image(url= "https://i.imgur.com/sYiOEXf.png")
```

```python
expr = 30*t - 4.9*t**2
def F(t):
    return expr
F(t)
```

$\displaystyle - 4.9 t^{2} + 30 t$

```python
diff(F(t)).subs(t,1),diff(F(t)).subs(t,3.5)
```

    (20.2000000000000, -4.30000000000000)

```python
(20.2000000000000-4.30000000000000)/2
```

    7.949999999999999

```python
Image(url= "https://i.imgur.com/Yq78S6Y.png")
```


# Deflection Profile Parametric Study for El Ghadioui2020
El Ghadioui2020_Structural behaviour of CFRP reinforced concrete members under bending and shear loads

```python
%matplotlib widget
# %matplotlib qt
```

```python
from bmcs_cross_section.api import MKappa, TShape, EC2, BarLayer, ReinfLayer
from bmcs_beam.api import BoundaryConditions, BoundaryConfig, DeflectionProfile, BeamDesign, LoadDeflectionParamsStudy
from os.path import join
import numpy as np
import pandas as pd
import sympy as sp
import matplotlib.pyplot as plt
import bmcs_utils.api as bu
```

```python
# Beam B-M-C-K1 - CFRP Bars
excel_file = join('../verification_data/el_ghadioui2020', 'fig_3.xlsx')
data = pd.read_excel(excel_file, engine='openpyxl')
ver_data = [data['B-M-C-K1_deflection'], data['B-M-C-K1_load']]
```

```python
# Beam B-M-C-K1 - CFRP Bars

# C 50/60
def get_dp(f_ck = None, E=None, low_kappa = None, high_kappa = None, n_kappa=None, n_load_steps=None, eps_tu=None):
    if eps_tu is None:
        eps_tu = 0.00008

    if n_load_steps is None:
        n_load_steps = 31

    if E is None:
        E = 33525

    if high_kappa is None:
        high_kappa = 0.00011

    if n_kappa is None:
        n_kappa = 200

    if low_kappa is None:
        low_kappa = 0

    mc = MKappa(low_kappa = low_kappa, high_kappa = high_kappa, n_kappa = n_kappa)

    if f_ck:
        f_ck = 90

        E = EC2.get_E_cm(f_ck)

        fctk_0_05 = EC2.get_f_ctk_0_05(f_ck)
        f_ctd = fctk_0_05
        # f_ctd = 0.85 * fctk_0_05 / 1.5
        f_ctd_fl = max((1.6 - 200 /1000) * f_ctd, f_ctd)

        mc.cs_design.matrix_.trait_set(
        E_cc=E,
        E_ct=E,
        eps_cr = f_ctd_fl/E,
        eps_tu = 2* f_ctd_fl/E,
        eps_cy = EC2.get_eps_c3(f_ck),
        eps_cu = EC2.get_eps_cu3(f_ck),
        mu=0.0,
        factor = 1
        )
    else:

        # mc.cs_design.matrix='EC2 with plateau'
        mc.cs_design.matrix_.trait_set(
                E_cc=E,
                E_ct=E,
                eps_cr=0.00004, #0.000125 * f_ck/30, # f_ctm /E,
                eps_cy=0.00175,
                eps_cu=0.0035,
                eps_tu=eps_tu,
                mu=0.0,
        #     f_cd = 30, # f_ck * 0.85/1.5,
            factor = 1
            )
    mc.cross_section_shape_.B = 400
    mc.cross_section_shape_.H = 200

    bl1 = ReinfLayer(name='carbon', z=35, A=2 * 70.14, matmod='carbon')
    bl1.matmod_.trait_set(E=135121, f_t=1891, factor = 1.)
    mc.cross_section_layout.add_layer(bl1)

    dp = DeflectionProfile(mc=mc, n_load_steps = n_load_steps)

    dp.add_fw_exp(ver_data[1], ver_data[0])

    dp.beam_design.beam_conf_name = BoundaryConfig.FOUR_PB
    dp.beam_design.beam_conf_name.first_load_distance = 1925
    dp.beam_design.L = 4700
    # dp.beam_design.L = 5000 # This gives perfect results
    dp.F_scale = 2/1000
    return dp

get_dp().interact()
```

    VBox(children=(HBox(children=(VBox(children=(Tree(layout=Layout(align_items='stretch', border='solid 1px black…

```python
dp = get_dp()
empty = dp.mc.kappa_t[np.where(dp.mc.kappa_t < 0)]
```

```python
dp.mc.kappa_t
```

    array([0.00000000e+00, 5.52763819e-07, 1.10552764e-06, 1.65829146e-06,
           2.21105528e-06, 2.76381910e-06, 3.31658291e-06, 3.86934673e-06,
           4.42211055e-06, 4.97487437e-06, 5.52763819e-06, 6.08040201e-06,
           6.63316583e-06, 7.18592965e-06, 7.73869347e-06, 8.29145729e-06,
           8.84422111e-06, 9.39698492e-06, 9.94974874e-06, 1.05025126e-05,
           1.10552764e-05, 1.16080402e-05, 1.21608040e-05, 1.27135678e-05,
           1.32663317e-05, 1.38190955e-05, 1.43718593e-05, 1.49246231e-05,
           1.54773869e-05, 1.60301508e-05, 1.65829146e-05, 1.71356784e-05,
           1.76884422e-05, 1.82412060e-05, 1.87939698e-05, 1.93467337e-05,
           1.98994975e-05, 2.04522613e-05, 2.10050251e-05, 2.15577889e-05,
           2.21105528e-05, 2.26633166e-05, 2.32160804e-05, 2.37688442e-05,
           2.43216080e-05, 2.48743719e-05, 2.54271357e-05, 2.59798995e-05,
           2.65326633e-05, 2.70854271e-05, 2.76381910e-05, 2.81909548e-05,
           2.87437186e-05, 2.92964824e-05, 2.98492462e-05, 3.04020101e-05,
           3.09547739e-05, 3.15075377e-05, 3.20603015e-05, 3.26130653e-05,
           3.31658291e-05, 3.37185930e-05, 3.42713568e-05, 3.48241206e-05,
           3.53768844e-05, 3.59296482e-05, 3.64824121e-05, 3.70351759e-05,
           3.75879397e-05, 3.81407035e-05, 3.86934673e-05, 3.92462312e-05,
           3.97989950e-05, 4.03517588e-05, 4.09045226e-05, 4.14572864e-05,
           4.20100503e-05, 4.25628141e-05, 4.31155779e-05, 4.36683417e-05,
           4.42211055e-05, 4.47738693e-05, 4.53266332e-05, 4.58793970e-05,
           4.64321608e-05, 4.69849246e-05, 4.75376884e-05, 4.80904523e-05,
           4.86432161e-05, 4.91959799e-05, 4.97487437e-05, 5.03015075e-05,
           5.08542714e-05, 5.14070352e-05, 5.19597990e-05, 5.25125628e-05,
           5.30653266e-05, 5.36180905e-05, 5.41708543e-05, 5.47236181e-05,
           5.52763819e-05, 5.58291457e-05, 5.63819095e-05, 5.69346734e-05,
           5.74874372e-05, 5.80402010e-05, 5.85929648e-05, 5.91457286e-05,
           5.96984925e-05, 6.02512563e-05, 6.08040201e-05, 6.13567839e-05,
           6.19095477e-05, 6.24623116e-05, 6.30150754e-05, 6.35678392e-05,
           6.41206030e-05, 6.46733668e-05, 6.52261307e-05, 6.57788945e-05,
           6.63316583e-05, 6.68844221e-05, 6.74371859e-05, 6.79899497e-05,
           6.85427136e-05, 6.90954774e-05, 6.96482412e-05, 7.02010050e-05,
           7.07537688e-05, 7.13065327e-05, 7.18592965e-05, 7.24120603e-05,
           7.29648241e-05, 7.35175879e-05, 7.40703518e-05, 7.46231156e-05,
           7.51758794e-05, 7.57286432e-05, 7.62814070e-05, 7.68341709e-05,
           7.73869347e-05, 7.79396985e-05, 7.84924623e-05, 7.90452261e-05,
           7.95979899e-05, 8.01507538e-05, 8.07035176e-05, 8.12562814e-05,
           8.18090452e-05, 8.23618090e-05, 8.29145729e-05, 8.34673367e-05,
           8.40201005e-05, 8.45728643e-05, 8.51256281e-05, 8.56783920e-05,
           8.62311558e-05, 8.67839196e-05, 8.73366834e-05, 8.78894472e-05,
           8.84422111e-05, 8.89949749e-05, 8.95477387e-05, 9.01005025e-05,
           9.06532663e-05, 9.12060302e-05, 9.17587940e-05, 9.23115578e-05,
           9.28643216e-05, 9.34170854e-05, 9.39698492e-05, 9.45226131e-05,
           9.50753769e-05, 9.56281407e-05, 9.61809045e-05, 9.67336683e-05,
           9.72864322e-05, 9.78391960e-05, 9.83919598e-05, 9.89447236e-05,
           9.94974874e-05, 1.00050251e-04, 1.00603015e-04, 1.01155779e-04,
           1.01708543e-04, 1.02261307e-04, 1.02814070e-04, 1.03366834e-04,
           1.03919598e-04, 1.04472362e-04, 1.05025126e-04, 1.05577889e-04,
           1.06130653e-04, 1.06683417e-04, 1.07236181e-04, 1.07788945e-04,
           1.08341709e-04, 1.08894472e-04, 1.09447236e-04, 1.10000000e-04])

```python
dp = get_dp()
kappa_pos = dp.mc.kappa_t[np.where(dp.mc.kappa_t >= 0)]
kappa_pos
```

    array([0.00000000e+00, 5.52763819e-07, 1.10552764e-06, 1.65829146e-06,
           2.21105528e-06, 2.76381910e-06, 3.31658291e-06, 3.86934673e-06,
           4.42211055e-06, 4.97487437e-06, 5.52763819e-06, 6.08040201e-06,
           6.63316583e-06, 7.18592965e-06, 7.73869347e-06, 8.29145729e-06,
           8.84422111e-06, 9.39698492e-06, 9.94974874e-06, 1.05025126e-05,
           1.10552764e-05, 1.16080402e-05, 1.21608040e-05, 1.27135678e-05,
           1.32663317e-05, 1.38190955e-05, 1.43718593e-05, 1.49246231e-05,
           1.54773869e-05, 1.60301508e-05, 1.65829146e-05, 1.71356784e-05,
           1.76884422e-05, 1.82412060e-05, 1.87939698e-05, 1.93467337e-05,
           1.98994975e-05, 2.04522613e-05, 2.10050251e-05, 2.15577889e-05,
           2.21105528e-05, 2.26633166e-05, 2.32160804e-05, 2.37688442e-05,
           2.43216080e-05, 2.48743719e-05, 2.54271357e-05, 2.59798995e-05,
           2.65326633e-05, 2.70854271e-05, 2.76381910e-05, 2.81909548e-05,
           2.87437186e-05, 2.92964824e-05, 2.98492462e-05, 3.04020101e-05,
           3.09547739e-05, 3.15075377e-05, 3.20603015e-05, 3.26130653e-05,
           3.31658291e-05, 3.37185930e-05, 3.42713568e-05, 3.48241206e-05,
           3.53768844e-05, 3.59296482e-05, 3.64824121e-05, 3.70351759e-05,
           3.75879397e-05, 3.81407035e-05, 3.86934673e-05, 3.92462312e-05,
           3.97989950e-05, 4.03517588e-05, 4.09045226e-05, 4.14572864e-05,
           4.20100503e-05, 4.25628141e-05, 4.31155779e-05, 4.36683417e-05,
           4.42211055e-05, 4.47738693e-05, 4.53266332e-05, 4.58793970e-05,
           4.64321608e-05, 4.69849246e-05, 4.75376884e-05, 4.80904523e-05,
           4.86432161e-05, 4.91959799e-05, 4.97487437e-05, 5.03015075e-05,
           5.08542714e-05, 5.14070352e-05, 5.19597990e-05, 5.25125628e-05,
           5.30653266e-05, 5.36180905e-05, 5.41708543e-05, 5.47236181e-05,
           5.52763819e-05, 5.58291457e-05, 5.63819095e-05, 5.69346734e-05,
           5.74874372e-05, 5.80402010e-05, 5.85929648e-05, 5.91457286e-05,
           5.96984925e-05, 6.02512563e-05, 6.08040201e-05, 6.13567839e-05,
           6.19095477e-05, 6.24623116e-05, 6.30150754e-05, 6.35678392e-05,
           6.41206030e-05, 6.46733668e-05, 6.52261307e-05, 6.57788945e-05,
           6.63316583e-05, 6.68844221e-05, 6.74371859e-05, 6.79899497e-05,
           6.85427136e-05, 6.90954774e-05, 6.96482412e-05, 7.02010050e-05,
           7.07537688e-05, 7.13065327e-05, 7.18592965e-05, 7.24120603e-05,
           7.29648241e-05, 7.35175879e-05, 7.40703518e-05, 7.46231156e-05,
           7.51758794e-05, 7.57286432e-05, 7.62814070e-05, 7.68341709e-05,
           7.73869347e-05, 7.79396985e-05, 7.84924623e-05, 7.90452261e-05,
           7.95979899e-05, 8.01507538e-05, 8.07035176e-05, 8.12562814e-05,
           8.18090452e-05, 8.23618090e-05, 8.29145729e-05, 8.34673367e-05,
           8.40201005e-05, 8.45728643e-05, 8.51256281e-05, 8.56783920e-05,
           8.62311558e-05, 8.67839196e-05, 8.73366834e-05, 8.78894472e-05,
           8.84422111e-05, 8.89949749e-05, 8.95477387e-05, 9.01005025e-05,
           9.06532663e-05, 9.12060302e-05, 9.17587940e-05, 9.23115578e-05,
           9.28643216e-05, 9.34170854e-05, 9.39698492e-05, 9.45226131e-05,
           9.50753769e-05, 9.56281407e-05, 9.61809045e-05, 9.67336683e-05,
           9.72864322e-05, 9.78391960e-05, 9.83919598e-05, 9.89447236e-05,
           9.94974874e-05, 1.00050251e-04, 1.00603015e-04, 1.01155779e-04,
           1.01708543e-04, 1.02261307e-04, 1.02814070e-04, 1.03366834e-04,
           1.03919598e-04, 1.04472362e-04, 1.05025126e-04, 1.05577889e-04,
           1.06130653e-04, 1.06683417e-04, 1.07236181e-04, 1.07788945e-04,
           1.08341709e-04, 1.08894472e-04, 1.09447236e-04, 1.10000000e-04])

```python
emp = []
np.concatenate([emp, kappa_pos])
```

    array([0.00000000e+00, 5.52763819e-07, 1.10552764e-06, 1.65829146e-06,
           2.21105528e-06, 2.76381910e-06, 3.31658291e-06, 3.86934673e-06,
           4.42211055e-06, 4.97487437e-06, 5.52763819e-06, 6.08040201e-06,
           6.63316583e-06, 7.18592965e-06, 7.73869347e-06, 8.29145729e-06,
           8.84422111e-06, 9.39698492e-06, 9.94974874e-06, 1.05025126e-05,
           1.10552764e-05, 1.16080402e-05, 1.21608040e-05, 1.27135678e-05,
           1.32663317e-05, 1.38190955e-05, 1.43718593e-05, 1.49246231e-05,
           1.54773869e-05, 1.60301508e-05, 1.65829146e-05, 1.71356784e-05,
           1.76884422e-05, 1.82412060e-05, 1.87939698e-05, 1.93467337e-05,
           1.98994975e-05, 2.04522613e-05, 2.10050251e-05, 2.15577889e-05,
           2.21105528e-05, 2.26633166e-05, 2.32160804e-05, 2.37688442e-05,
           2.43216080e-05, 2.48743719e-05, 2.54271357e-05, 2.59798995e-05,
           2.65326633e-05, 2.70854271e-05, 2.76381910e-05, 2.81909548e-05,
           2.87437186e-05, 2.92964824e-05, 2.98492462e-05, 3.04020101e-05,
           3.09547739e-05, 3.15075377e-05, 3.20603015e-05, 3.26130653e-05,
           3.31658291e-05, 3.37185930e-05, 3.42713568e-05, 3.48241206e-05,
           3.53768844e-05, 3.59296482e-05, 3.64824121e-05, 3.70351759e-05,
           3.75879397e-05, 3.81407035e-05, 3.86934673e-05, 3.92462312e-05,
           3.97989950e-05, 4.03517588e-05, 4.09045226e-05, 4.14572864e-05,
           4.20100503e-05, 4.25628141e-05, 4.31155779e-05, 4.36683417e-05,
           4.42211055e-05, 4.47738693e-05, 4.53266332e-05, 4.58793970e-05,
           4.64321608e-05, 4.69849246e-05, 4.75376884e-05, 4.80904523e-05,
           4.86432161e-05, 4.91959799e-05, 4.97487437e-05, 5.03015075e-05,
           5.08542714e-05, 5.14070352e-05, 5.19597990e-05, 5.25125628e-05,
           5.30653266e-05, 5.36180905e-05, 5.41708543e-05, 5.47236181e-05,
           5.52763819e-05, 5.58291457e-05, 5.63819095e-05, 5.69346734e-05,
           5.74874372e-05, 5.80402010e-05, 5.85929648e-05, 5.91457286e-05,
           5.96984925e-05, 6.02512563e-05, 6.08040201e-05, 6.13567839e-05,
           6.19095477e-05, 6.24623116e-05, 6.30150754e-05, 6.35678392e-05,
           6.41206030e-05, 6.46733668e-05, 6.52261307e-05, 6.57788945e-05,
           6.63316583e-05, 6.68844221e-05, 6.74371859e-05, 6.79899497e-05,
           6.85427136e-05, 6.90954774e-05, 6.96482412e-05, 7.02010050e-05,
           7.07537688e-05, 7.13065327e-05, 7.18592965e-05, 7.24120603e-05,
           7.29648241e-05, 7.35175879e-05, 7.40703518e-05, 7.46231156e-05,
           7.51758794e-05, 7.57286432e-05, 7.62814070e-05, 7.68341709e-05,
           7.73869347e-05, 7.79396985e-05, 7.84924623e-05, 7.90452261e-05,
           7.95979899e-05, 8.01507538e-05, 8.07035176e-05, 8.12562814e-05,
           8.18090452e-05, 8.23618090e-05, 8.29145729e-05, 8.34673367e-05,
           8.40201005e-05, 8.45728643e-05, 8.51256281e-05, 8.56783920e-05,
           8.62311558e-05, 8.67839196e-05, 8.73366834e-05, 8.78894472e-05,
           8.84422111e-05, 8.89949749e-05, 8.95477387e-05, 9.01005025e-05,
           9.06532663e-05, 9.12060302e-05, 9.17587940e-05, 9.23115578e-05,
           9.28643216e-05, 9.34170854e-05, 9.39698492e-05, 9.45226131e-05,
           9.50753769e-05, 9.56281407e-05, 9.61809045e-05, 9.67336683e-05,
           9.72864322e-05, 9.78391960e-05, 9.83919598e-05, 9.89447236e-05,
           9.94974874e-05, 1.00050251e-04, 1.00603015e-04, 1.01155779e-04,
           1.01708543e-04, 1.02261307e-04, 1.02814070e-04, 1.03366834e-04,
           1.03919598e-04, 1.04472362e-04, 1.05025126e-04, 1.05577889e-04,
           1.06130653e-04, 1.06683417e-04, 1.07236181e-04, 1.07788945e-04,
           1.08341709e-04, 1.08894472e-04, 1.09447236e-04, 1.10000000e-04])

```python
# # Test f_ck
# """ [
#     {'f_ck': 30, 'low_kappa':0 , 'high_kappa':0.00012 },
#     {'f_ck': 50, 'low_kappa':0 , 'high_kappa':0.00012 },
#     {'f_ck': 70, 'low_kappa':-0.00001 , 'high_kappa':0.00011},
#     {'f_ck': 90, 'low_kappa':-0.00002 , 'high_kappa':0.00011},
#     ] """

# f_cks = [30, 50, 70, 90]
# dps = []
# dp_container = DP()
# for f_ck in f_cks:
#     dps.append(get_dp(f_ck=f_ck).interact())

# params_config = [['dp',   dp_container, dps],]

# LoadDeflectionParamsStudy(dp).run(params_config, ver_data)

# _, ax = plt.subplots()
# f, w = dp.get_Fw()
# ax.plot(w, f*2/1000)
# ax.plot(ver_data[0], ver_data[1])
```

```python
# Test E (concrete)
E_list = np.array([30000, 35000, 40000, 45000])

dp = get_dp()

# following fix is just to keep the sig, and not eps the same when changing E in MULTI-LINEAR Model
E = dp.mc.cs_design.matrix_.E_cc
eps_cy = dp.mc.cs_design.matrix_.eps_cy
eps_cr= dp.mc.cs_design.matrix_.eps_cr
sig_cy = E * eps_cy
sig_cr = E * eps_cr

sig_cy_list = np.full_like(E_list, sig_cy)
sig_cr_list = np.full_like(E_list, sig_cr)

params_config = [
    [['E_cc', dp.mc.cs_design.matrix_, E_list],
     ['E_ct', dp.mc.cs_design.matrix_, E_list]],
]
# params_config = [
#     [['E_cc', dp.mc.cs_design.matrix_, E_list],
#      ['E_ct', dp.mc.cs_design.matrix_, E_list],
#      ['eps_cy', dp.mc.cs_design.matrix_, sig_cy_list / E_list],
#      ['eps_cr', dp.mc.cs_design.matrix_, sig_cr_list / E_list]],
# ]
LoadDeflectionParamsStudy(dp).run(params_config, ver_data)
```

    Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …

    Parametric study is running...
    E_cc, E_ct: (30000, 30000), (35000, 35000), (40000, 40000), (45000, 45000),
    Parametric study finished.

```python
# Test E_carbon
E_carbon = [130000, 170000, 210000, 250000]

dp = get_dp(n_kappa = 500)
E_cc = dp.mc.cs_design.matrix_.E_cc
E_list = [E_cc, E_cc + 0.01, E_cc, E_cc + 0.01] # Just to update state because changing E_carbon doesn't update the state automatically
params_config = [
    [['E', dp.mc.cross_section_layout.items[0].matmod_, E_carbon],
     ['E_cc', dp.mc.cs_design.matrix_, E_list]]
]
LoadDeflectionParamsStudy(dp).run(params_config, ver_data)
```

    Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …

    Parametric study is running...
    E, E_cc: (130000, 33525.0), (170000, 33525.01), (210000, 33525.0), (250000, 33525.01),
    Parametric study finished.

```python
# Test carbon strength
f_t_list = [1800, 2200, 2600, 3000]
dp = get_dp(high_kappa = 0.00018)
E_cc = dp.mc.cs_design.matrix_.E_cc
E_list = [E_cc, E_cc + 0.01, E_cc, E_cc + 0.01] # Just to update state because changing E_carbon doesn't update the state automatically
params_config = [
    [['f_t', dp.mc.cross_section_layout.items[0].matmod_, f_t_list],
     ['E_cc', dp.mc.cs_design.matrix_, E_list]]
]
LoadDeflectionParamsStudy(dp).run(params_config, ver_data)
```

    Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …

    Parametric study is running...
    f_t, E_cc: (1800, 33525.0), (2200, 33525.01), (2600, 33525.0), (3000, 33525.01),
    Parametric study finished.

```python
# Test concrete compressive strength
f_cm_list = np.array([30, 50, 70, 90])

dp = get_dp(n_kappa=500, high_kappa=0.00013)

E = dp.mc.cs_design.matrix_.E_cc
E_list = np.full_like(f_cm_list, E)

params_config = [
     ['eps_cy', dp.mc.cs_design.matrix_, f_cm_list / E_list],
]
LoadDeflectionParamsStudy(dp).run(params_config, ver_data)
```

    Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …

    Parametric study is running...
    eps_cy: (0.0008948545861297539), (0.0014914243102162564), (0.002087994034302759), (0.0026845637583892616),
    Parametric study finished.

```python
# Test concrete tensile strength
f_ctd_list = np.array([0, 2.8, 4, 4.5, 5])

dp = get_dp(n_kappa=500, high_kappa=0.00013, eps_tu = 2*0.00008, n_load_steps=250)

E = dp.mc.cs_design.matrix_.E_cc
E_list = np.full_like(f_ctd_list, E)

params_config = [
     ['eps_cr', dp.mc.cs_design.matrix_, f_ctd_list / E_list],
]
LoadDeflectionParamsStudy(dp).run(params_config, ver_data)
```

    Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …

    Parametric study is running...
    eps_cr: (0.0), (8.351976137211036e-05), (0.00011931394481730052), (0.00013422818791946307), (0.00014914243102162565),
    Parametric study finished.


# Growth and the ribosomal content of cells - scan of parameters

See other notebook for full description.

```python
#start python session
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sympy import *
import matplotlib
matplotlib.rcParams['pdf.fonttype'] = 42
matplotlib.rcParams['ps.fonttype'] = 42
```

```python

#definitions of functions to plot things in the following

colorPC='k'#'#4A71B7'
colorPR='k'#'#70AD44'
colorGR='k'
colorGRopt='orange'
colorPCcon='magenta'
alphac=0.8

def grsolution(beta,gamma_maxin,nu,a_0,phiO,phiR,fracactive):
 pf_translationrate=3600./7459.
 gamma_max=gamma_maxin*fracactive*pf_translationrate
 grsol1s=((-beta*(gamma_max*phiR - nu*phiO - nu*phiR + nu) + np.sqrt(beta*(-4*a_0*gamma_max*nu*phiO*phiR - 4*a_0*gamma_max*nu*phiR*phiR + 4*a_0*gamma_max*nu*phiR + beta*gamma_max**2*phiR*phiR + 2*beta*gamma_max*nu*phiO*phiR + 2*beta*gamma_max*nu*phiR*phiR - 2*beta*gamma_max*nu*phiR + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiR - 2*beta*nu*nu*phiO + beta*nu*nu*phiR*phiR - 2*beta*nu*nu*phiR + beta*nu*nu)))/(2*(a_0 - beta)))

 #grsol1s=((-beta*(gamma_max*phiR - nu*phiO - nu*phiR + nu) + np.sqrt(beta*(-4*a_0*gamma_max*nu*phiO*phiR - 4*a_0*gamma_max*nu*phiR**2 + 4*a_0*gamma_max*nu*phiR + beta*gamma_max**2*phiR*phiR + 2*beta*gamma_max*nu*phiO*phiR + 2*beta*gamma_max*nu*phiR*phiR - 2*beta*gamma_max*nu*phiR + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiR - 2*beta*nu*nu*phiO + beta*nu*nu*phiR*phiR - 2*beta*nu*nu*phiR + beta*nu*nu)))/(2*(a_0 - beta)))

 a_solution1=beta*(nu*(1-phiO-phiR)/grsol1s-1)
 return [grsol1s,a_solution1,gamma_max*a_solution1/(a_solution1+a_0)/fracactive/pf_translationrate]

#solution for optimal growht
#output: fraction ribosomes, precursor level, growth-rate
def optphiR_solution(beta,gamma_maxin,nu,a0,phiO,fracactive):
 pf_translationrate=3600./7459.
 gamma_max=gamma_maxin*fracactive*pf_translationrate
 phiRopt=-(2*a0*gamma_max*nu*phiO - 2*a0*gamma_max*nu - beta*gamma_max*nu*phiO + beta*gamma_max*nu - beta*nu*nu*phiO + beta*nu**2 - gamma_max*phiO*np.sqrt(a0*beta*gamma_max*nu) + gamma_max*np.sqrt(a0*beta*gamma_max*nu) + nu*phiO*np.sqrt(a0*beta*gamma_max*nu) - nu*np.sqrt(a0*beta*gamma_max*nu))/(4*a0*gamma_max*nu - beta*gamma_max*gamma_max - 2*beta*gamma_max*nu - beta*nu**2)
 #print((beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt*phiRopt + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max*gamma_max*phiRopt*phiRopt + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt*phiRopt - 2*beta*gamma_max*nu*phiRopt + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiRopt - 2*beta*nu*nu*phiO + beta*nu*nu*phiRopt*phiRopt - 2*beta*nu*nu*phiRopt + beta*nu*nu)))
 #print(np.sqrt(beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt*phiRopt + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max*gamma_max*phiRopt*phiRopt + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt*phiRopt - 2*beta*gamma_max*nu*phiRopt + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiRopt - 2*beta*nu*nu*phiO + beta*nu*nu*phiRopt*phiRopt - 2*beta*nu*nu*phiRopt + beta*nu*nu)))
 #phiRopt=optimalgrowth_sol1=-(2*a0*gamma_max*nu*phiO - 2*a0*gamma_max*nu - beta*gamma_max*nu*phiO + beta*gamma_max*nu - beta*nu**2*phiO + beta*nu**2 - gamma_max*phiO*sqrt(a0*beta*gamma_max*nu) + gamma_max*sqrt(a0*beta*gamma_max*nu) + nu*phiO*sqrt(a0*beta*gamma_max*nu) - nu*sqrt(a0*beta*gamma_max*nu))/(4*a0*gamma_max*nu - beta*gamma_max**2 - 2*beta*gamma_max*nu - beta*nu**2)
 grsol1s=((-beta*(gamma_max*phiRopt - nu*phiO - nu*phiRopt + nu) + np.sqrt(beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt*phiRopt + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max*gamma_max*phiRopt*phiRopt + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt*phiRopt - 2*beta*gamma_max*nu*phiRopt + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiRopt - 2*beta*nu*nu*phiO + beta*nu*nu*phiRopt*phiRopt - 2*beta*nu*nu*phiRopt + beta*nu*nu)))/(2*(a0 - beta)))
 #grsol1s=((-beta*(gamma_max*phiRopt - nu*phiO - nu*phiRopt + nu) + sqrt(beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt**2 + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max**2*phiRopt**2 + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt**2 - 2*beta*gamma_max*nu*phiRopt + beta*nu**2*phiO**2 + 2*beta*nu**2*phiO*phiRopt - 2*beta*nu**2*phiO + beta*nu**2*phiRopt**2 - 2*beta*nu**2*phiRopt + beta*nu**2)))/(2*(a0 - beta)))
 a_solution1=beta*(nu*(1-phiO-phiRopt)/grsol1s-1)
 translc=grsol1s*0+gamma_max*a_solution1/(a_solution1+a0)/fracactive/pf_translationrate
 return[phiRopt,a_solution1,grsol1s,translc]

def constphiR_solution(beta,gamma_maxin,nu,a0,phiO,phiRin,fracactive):
 pf_translationrate=3600./7459.
 gamma_max=gamma_maxin*fracactive*pf_translationrate
 phiRopt=phiRin
 #-(2*a0*gamma_max*nu*phiO - 2*a0*gamma_max*nu - beta*gamma_max*nu*phiO + beta*gamma_max*nu - beta*nu*nu*phiO + beta*nu**2 - gamma_max*phiO*np.sqrt(a0*beta*gamma_max*nu) + gamma_max*np.sqrt(a0*beta*gamma_max*nu) + nu*phiO*np.sqrt(a0*beta*gamma_max*nu) - nu*np.sqrt(a0*beta*gamma_max*nu))/(4*a0*gamma_max*nu - beta*gamma_max*gamma_max - 2*beta*gamma_max*nu - beta*nu**2)
 #print((beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt*phiRopt + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max*gamma_max*phiRopt*phiRopt + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt*phiRopt - 2*beta*gamma_max*nu*phiRopt + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiRopt - 2*beta*nu*nu*phiO + beta*nu*nu*phiRopt*phiRopt - 2*beta*nu*nu*phiRopt + beta*nu*nu)))
 #print(np.sqrt(beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt*phiRopt + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max*gamma_max*phiRopt*phiRopt + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt*phiRopt - 2*beta*gamma_max*nu*phiRopt + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiRopt - 2*beta*nu*nu*phiO + beta*nu*nu*phiRopt*phiRopt - 2*beta*nu*nu*phiRopt + beta*nu*nu)))
 #phiRopt=optimalgrowth_sol1=-(2*a0*gamma_max*nu*phiO - 2*a0*gamma_max*nu - beta*gamma_max*nu*phiO + beta*gamma_max*nu - beta*nu**2*phiO + beta*nu**2 - gamma_max*phiO*sqrt(a0*beta*gamma_max*nu) + gamma_max*sqrt(a0*beta*gamma_max*nu) + nu*phiO*sqrt(a0*beta*gamma_max*nu) - nu*sqrt(a0*beta*gamma_max*nu))/(4*a0*gamma_max*nu - beta*gamma_max**2 - 2*beta*gamma_max*nu - beta*nu**2)
 grsol1s=((-beta*(gamma_max*phiRopt - nu*phiO - nu*phiRopt + nu) + np.sqrt(beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt*phiRopt + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max*gamma_max*phiRopt*phiRopt + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt*phiRopt - 2*beta*gamma_max*nu*phiRopt + beta*nu*nu*phiO*phiO + 2*beta*nu*nu*phiO*phiRopt - 2*beta*nu*nu*phiO + beta*nu*nu*phiRopt*phiRopt - 2*beta*nu*nu*phiRopt + beta*nu*nu)))/(2*(a0 - beta)))
 #grsol1s=((-beta*(gamma_max*phiRopt - nu*phiO - nu*phiRopt + nu) + sqrt(beta*(-4*a0*gamma_max*nu*phiO*phiRopt - 4*a0*gamma_max*nu*phiRopt**2 + 4*a0*gamma_max*nu*phiRopt + beta*gamma_max**2*phiRopt**2 + 2*beta*gamma_max*nu*phiO*phiRopt + 2*beta*gamma_max*nu*phiRopt**2 - 2*beta*gamma_max*nu*phiRopt + beta*nu**2*phiO**2 + 2*beta*nu**2*phiO*phiRopt - 2*beta*nu**2*phiO + beta*nu**2*phiRopt**2 - 2*beta*nu**2*phiRopt + beta*nu**2)))/(2*(a0 - beta)))
 a_solution1=beta*(nu*(1-phiO-phiRopt)/grsol1s-1)
 translc=grsol1s*0+gamma_max*a_solution1/(a_solution1+a0)/fracactive/pf_translationrate
 return[grsol1s*0+phiRopt,a_solution1,grsol1s,translc]

#solution for constant nutrient abundance levels
#output: fraction ribosomes, precursor level, growth-rate
def constPCsolution(beta,gamma_maxin,nu,a_0,avaluec,phiO,fracactive):
    pf_translationrate=3600./7459.
    gamma_max=gamma_maxin*fracactive*pf_translationrate
    sol1c=beta*nu*(-a_0*phiO + a_0 - avaluec*phiO + avaluec)/(a_0*beta*nu + avaluec*avaluec*gamma_max + avaluec*beta*gamma_max + avaluec*beta*nu)
    #sol2c=beta*nu*(-a_0*phiO + a_0 - avaluec*phiO + avaluec)/(a_0*beta*nu + avaluec**2*gamma_max + avaluec*beta*gamma_max + avaluec*beta*nu)
    translc=sol1c*0.+a_0*gamma_max*avaluec/(avaluec+a_0)
    return [sol1c,sol1c*0.+avaluec,gamma_max*sol1c*avaluec/(avaluec+a_0)/fracactive/pf_translationrate,translc]

#it seems like widgets need to be first activatedfor jupythero labbook
#jupyter labextension install @jupyter-widgets/jupyterlab-manager

%matplotlib inline
from ipywidgets import interactive

import matplotlib.pyplot as plt
import numpy as np

#variation of different parameters
#a0v=0.005
##gamma_maxv=5.7
#betav=1/20.
#nuv=1
#phiOv=0.3

a0v=0.0013*20#
gamma_maxv=20
betav=1
nuv=10
phiOv=0.35
fracactivev=0.65

#0.7 to account for non-active ribosome part, other part for other proteins needed for translation (RNA-seq)

def plotfunction(a0v=a0v, nuv=nuv, phiOv=phiOv,gamma_maxv=gamma_maxv,betav=betav,fracactivev=fracactivev):
    fig, axs = plt.subplots(2,3, figsize=(2.8*3,5.3))
    axs[0,0].set_xlim(0,2)
    axs[0,0].set_title("Change nutrient quality")
    axs[0,1].set_xlim(0,2)
    axs[0,1].set_title("Change fraction other proteins")
    axs[0,2].set_xlim(0,2)
    axs[0,2].set_title("Change translation rate")
    axs[0,0].set_ylabel('ribosome fraction $\\alpha_R$')
    axs[0,0].set_xlabel("growth-rate $\lambda\, (1/h)$")
    axs[0,1].set_ylabel('ribosome fraction $\\alpha_R$')
    axs[0,1].set_xlabel("growth-rate $\lambda\, (1/h)$")
    axs[0,2].set_ylabel('ribosome fraction $\\alpha_R$')
    axs[0,2].set_xlabel("growth-rate $\lambda\, (1/h)$")
    axs[1,0].set_xlim(0,2)
    axs[1,1].set_xlim(0,2)
    axs[1,2].set_xlim(0,2)
    axs[1,0].set_ylabel('translatioon rate (AA/s)')
    axs[1,0].set_xlabel("growth-rate $\lambda\, (1/h)$")
    axs[1,1].set_ylabel('translatioon rate (AA/s)')
    axs[1,1].set_xlabel("growth-rate $\lambda\, (1/h)$")
    axs[1,2].set_ylabel('translatioon rate (AA/s)')
    axs[1,2].set_xlabel("growth-rate $\lambda\, (1/h)$")

    #for reference, plot slope 1/translation rate
    xgrr=np.linspace(0,2,20)

    pf_translationrate=3600./7459.

    axs[0,0].plot(xgrr,100*xgrr/gamma_maxv/pf_translationrate/fracactivev,ls='-',color='magenta')
    axs[0,1].plot(xgrr,100*xgrr/gamma_maxv/pf_translationrate/fracactivev,ls='-',color='magenta')
    axs[0,2].plot(xgrr,100*xgrr/gamma_maxv/pf_translationrate/fracactivev,ls='-',color='magenta')

    ####
    #change nutrient quality
    nuarray=np.linspace(0.005,40,100)
    gropt_alphaR,gropt_pc,gropt_gr,gropt_transl=optphiR_solution(betav,gamma_maxv,nuv,a0v,phiOv,fracactivev)
    axs[0,0].axvline(gropt_gr,ls='--')
    gropt_alphaR,gropt_pc,gropt_gr,gropt_transl=optphiR_solution(betav,gamma_maxv,nuarray,a0v,phiOv,fracactivev)
    #ribosome fraction
    rlinet=[gropt_gr,gropt_alphaR]

    axs[0,0].plot(gropt_gr,100*gropt_alphaR,ls='-',color='k')
    #translation
    axs[1,0].plot(gropt_gr,gropt_transl,ls='-',color='k')
    axs[1,0].plot(gropt_gr,gropt_gr*0+gamma_maxv,ls='--',color='k')
    #offset
    try:
        indc=np.where(np.logical_and(gropt_gr>=1, gropt_gr<=2))
        fitc=np.polyfit(gropt_gr[indc],gropt_alphaR[indc],1)
        xgrr=np.linspace(0,2,20)
        axs[0,0].plot(xgrr,xgrr*fitc[0]+fitc[1],color='k',ls='--')
    except:
        pass
    #axs[1].set_ylabel('translation (fract. max.)')
    #axs[1].set_xlabel("growth-rate $\lambda\, (1/h)$")

    #####
    #change fraction other proteins
    phi0array=np.linspace(0.,0.8,100)
    gropt_alphaR,gropt_pc,gropt_gr,gropt_transl=optphiR_solution(betav,gamma_maxv,nuv,a0v,phiOv,fracactivev)
    axs[0,1].axvline(gropt_gr*100,ls='--')

    gropt_alphaR,gropt_pc,gropt_gr,gropt_transl=optphiR_solution(betav,gamma_maxv,nuv,a0v,phi0array,fracactivev)
    #ribosome fraction
    axs[0,1].plot(gropt_gr,100*gropt_alphaR,ls='-',color='k')
    #translation
    axs[1,1].plot(gropt_gr,gropt_transl,ls='-',color='k')
    axs[1,1].plot(gropt_gr,gropt_gr*0+gamma_maxv,ls='--',color='k')

    #offset
    try:
        fitc=np.polyfit(gropt_gr[int(phi0array.shape[0]/2.):],gropt_alphaR[int(phi0array.shape[0]/2.):],1)
        xgrr=np.linspace(0,2,20)
        axs[0,1].plot(xgrr,xgrr*fitc[0]+fitc[1],color='k',ls='--')
    except:
        pass

    ####
    #change translation rate
    gropt_alphaR,gropt_pc,gropt_gr,gropt_transl=optphiR_solution(betav,gamma_maxv,nuv,a0v,phiOv,fracactivev)
    axs[0,2].axvline(gropt_gr*100,ls='--')

    gammaarray=np.linspace(0.,gamma_maxv,100)
    gropt_alphaR,gropt_pc,gropt_gr,gropt_transl=optphiR_solution(betav,gammaarray,nuv,a0v,phiOv,fracactivev)
    #ribosome fraction
    axs[0,2].plot(gropt_gr,100*gropt_alphaR,ls='-',color='k')
    #translation

    axs[1,2].plot(gropt_gr,gropt_transl,ls='--',color='k')
    axs[1,2].plot(gropt_gr,gammaarray,ls='--',color='k')

    #rline for comparison
    axs[0,2].plot(rlinet[0],100*rlinet[1],ls='-',color='gray')

    #fit Mori at al.
    grr=np.linspace(0,2,100)
    #axs[0,0].plot(grr,0.0967+0.2206*grr,color='r',label='Mori et al',alpha=0.5)
    #axs[1,0].plot(,color='r',label='observ transl.')
    #take data from RNAseq (c limitation)
    grdatac=np.array([0.91,0.75, 0.56, 0.51, 0.3 ])
    ribosabundanc=np.array([19.18590608, 14.22365961, 12.50443378, 10.73007257,  8.74428159])
    axs[0,0].plot(grdatac,ribosabundanc,marker='s',ls='',color='r')
    fitc=np.polyfit(grdatac,ribosabundanc,1)
    grrc=np.linspace(0,2,100)
    axs[0,0].plot(grrc,grrc*fitc[0]+fitc[1],color='r',label='RNA-seq',alpha=1)
    axs[0,0].legend()

    if 3>2:#TRANSLATION SPEED - DAI AT AL.
        grdatac=np.array([1.83,1.28,1.12,0.98,0.75,0.69,0.69,0.55,0.5,0.46,0.41,0.34,0.38,0.33,0.29,0.2,0.23,0.13,0.035])
        elongr=np.array([16.7,16.3,16.1,15.9,14.9,15,14.7,13.7,13.1,12.6,13,12.4,12.4,12,12.1,11.6,12.3,10.7,9.4])

        axs[1,0].plot(grdatac,elongr,marker='s',ls='',color='r')
        #axs[1,0].legend()

    plt.tight_layout()
    plt.show()

interactive_plot = interactive(plotfunction, a0v=(0.001,0.15,0.001),nuv=(0.1,50,0.1),phiOv=(0.1, 0.8,0.025), gamma_maxv=(0.5, 25, 0.1), betav=(0.001,1.5,0.001),fracactivev=(0.4,0.8,0.01))
output = interactive_plot.children[-1]
output.layout.height = '350px'
interactive_plot

```

    interactive(children=(FloatSlider(value=0.026, description='a0v', max=0.15, min=0.001, step=0.001), FloatSlide…

```python

```


*This notebook contains course material from [CBE30338](https://jckantor.github.io/CBE30338)
by Jeffrey Kantor (jeff at nd.edu); the content is available [on Github](https://github.com/jckantor/CBE30338.git).
The text is released under the [CC-BY-NC-ND-4.0 license](https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode),
and code is released under the [MIT license](https://opensource.org/licenses/MIT).*

< [One Compartment Pharmacokinetics](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/03.05-One-Compartment-Pharmacokinetics.ipynb) | [Contents](toc.ipynb) | [Interacting Tanks](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/03.07-Interacting-Tanks.ipynb) >

# Second Order Models

A standard form for a generic second-order model for a stable linear system is given  by

$$\tau^2\frac{d^2y}{dt^2} + 2\zeta\tau\frac{dy}{dt} + y = K u$$

where $y$ and $u$ are **deviation variables**.  The parameters have a generic interpretation that are commonly used to describe the qualitative characteristics of these systems.

| Parameter | Units | Description |
| :-: | :-: | :-: |
| $K$ | $\frac{\mbox{units of } y}{\mbox{units of }u}$ | Steady State Gain |
| $\tau \gt 0$ | time | Time Constant |
| $\zeta \geq 0$ | dimensionless | Damping Factor |

The standard form assumes that a zero input (i.e, $u(t) = 0$) results in a zero response ($y(t) = 0$). In practice, the nominal or quiescent value of $y$ or $u$ may different from zero. In that case we would write

$$\tau^2\frac{d^2y}{dt^2} + 2\zeta\tau\frac{dy}{dt} + y - y_{ref} = K\left(u(t) - u_{ref}\right)$$

where $u_{ref}$ and $y_{ref}$ represent constant reference values.

## Step Response

The step response corresponds to a system that is initially at steady-state where $u = u_{ref}$ and $y = y_{ref}$.  At time $t=0$ the input is incremented by a constant value U, i.e. $u = u_{ref} + U$ for $t \geq 0$.  The subsequent response $y(t) - y_{ref}$ is the **step response**.

Second order linear systems have elegant analytical solutions expressed using exponential and trignometric functions. There are four distinct cases that depend on the value of the damping factor $\zeta$:

* Overdamped
* Critically damped
* Underdamped
* Undamped Oscillations

### Overdamped ($\zeta > 1$)

An overdamped response tends to be sluggish, and with a potentially a large difference in time scales $\tau_1$ and $\tau_2$. The geometric mean of $\tau_1$ and $\tau_2$ is $\tau$.  The value of $\zeta$ determines the differences.

$$y(t) = y_{ref} + KU\left(1 - \frac{\tau_1e^{-t/\tau_1} - \tau_2e^{-t/\tau_2}}{\tau_1 - \tau_2}\right)$$

where $\tau_1$ and $\tau_2$ are found by factor the polynomial

$$\tau^2s^2 + 2\zeta\tau s + 1 = (\tau_1s + 1)(\tau_2s + 1)$$

For $\zeta \geq 1$ the solutions are given by

\begin{align}
\tau_1 & = \frac{\tau}{\zeta - \sqrt{\zeta^2-1}} \\
\tau_2 & = \frac{\tau}{\zeta + \sqrt{\zeta^2-1}}
\end{align}

```python
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact
```

```python
def overdamped(K, tau, zeta):
    t = np.linspace(0,20)
    tau_1 = tau/(zeta - np.sqrt(zeta**2 - 1))
    tau_2 = tau/(zeta + np.sqrt(zeta**2 - 1))

    y = K*(1 - ((tau_1*np.exp(-t/tau_1) - tau_2*np.exp(-t/tau_2))/(tau_1 - tau_2)))
    plt.plot(t,y)
    plt.grid()

interact(overdamped, K=(0.5,2), tau=(0.5,2), zeta=(1.01,2));
```

    A Jupyter Widget

### Critically Damped ($\zeta = 1$)

$$y(t) = y_{ref} + KU\left[1 - \left(1 + \frac{t}{\tau}\right)e^{-t/\tau}\right]$$

```python
def criticallydamped(K, tau):
    t = np.linspace(0,20)
    y = K*(1 - (1 + t/tau)*np.exp(-t/tau))
    plt.plot(t,y)
    plt.grid()

criticallydamped(K=2, tau=2)
```

### Underdamped ($0 \lt \zeta \lt 1$)

One version of the solution can be written

$$y(t) = y_{ref} + KU\left(1 - e^{-\zeta t/\tau}\left[\cos\left(\frac{\sqrt{1-\zeta^2}}{\tau}t\right) + \frac{\zeta}{\sqrt{1-\zeta^2}}\sin\left(\frac{\sqrt{1-\zeta^2}}{\tau}t\right)\right] \right)$$

This can be expressed a bit more compactly by introducing a frequency

$$\omega = \frac{\sqrt{1-\zeta^2}}{\tau}$$

which results in

$$y(t) = y_{ref} + KU\left[1 - e^{-\zeta t/\tau}\left(\cos\left(\omega t\right) + \frac{\zeta}{\sqrt{1-\zeta^2}}\,\sin\left(\omega t\right) \right)\right]$$

```python
def underdamped(K, tau, zeta):
    t = np.linspace(0,20)
    c = np.cos(np.sqrt(1-zeta**2)*t/tau)
    s = np.sin(np.sqrt(1-zeta**2)*t/tau)

    y = K*(1 - np.exp(-zeta*t/tau)*(c + zeta*s/np.sqrt(1-zeta**2)))
    plt.plot(t,y)
    plt.grid()

interact(underdamped, K=(0.5,3), tau=(0.5,3), zeta=(0,0.999))
```

    A Jupyter Widget

    <function __main__.underdamped>

### Undamped ($\zeta = 0$)

Finally, there is the special case of an undamped oscillation

$$y(t) = y_{ref} + KU\left[1 - \cos\left(\omega t\right) \right]$$

where $\omega = 1/\tau$.

## Simulation

A second-order differential equation can be simulated as a system of two first order differential equations. The key is to introduce a new variable $v = \frac{dy}{dt}$.

$$\begin{align*}
\frac{dy}{dt} & = v \\
\frac{dv}{dt} & = -\frac{1}{\tau^2}(y-y_{ref}) - \frac{2\zeta}{\tau}v + K\left(u(t)-u_{ref}\right)
\end{align*}$$

```python
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from ipywidgets import interact

def simulation(yref=0, U=1, K=1, tau=1, zeta=0.2):

    def deriv(X,t):
        y,v = X
        ydot = v
        vdot = -(y-yref)/tau/tau - 2*zeta*v/tau + K*U/tau/tau
        return[ydot,vdot]

    # simulation
    t = np.linspace(0,20*tau,1000)
    y = odeint(deriv, [yref,0], t)[:,0]

    # plot steady state line and bounds
    plt.figure(figsize=(12,6))

    # plot solution
    plt.plot(t,y,lw=3)
    plt.title('Step Response of a Second Order System')
    plt.xlabel('Time')
    plt.ylabel('y')

    # plot limits
    plt.ylim(plt.ylim()[0],1.1*plt.ylim()[1])
    plt.xlim(t[0],t[-1])
    dy = np.diff(plt.ylim())

    # arrow props
    ap1 = dict(arrowstyle="->")
    ap2 = dict(arrowstyle="<->")

    if zeta < 1:
        #overshoot
        os = np.exp(-np.pi*zeta/np.sqrt(1-zeta**2))

        # time to first peak
        tp = np.pi*tau/np.sqrt(1-zeta**2)
        yp = (1+os)*K*U + yref

        plt.text(tp,yp+0.02*dy,"Overshoot\n b/a = {0:0.2f}".format(os), ha='center')
        plt.annotate('',xy=(tp,K*U+yref),xytext=(tp,yp),arrowprops=ap2)
        plt.text(tp,(K*U+yref+yp)/2,' b')
        plt.annotate('',xy=(tp,yref),xytext=(tp,K*U+yref),arrowprops=ap2)
        plt.text(tp,K*U/2+yref,' a')
        plt.annotate("Time to first\n peak = {0:.2f}".format(tp),
                    xy=(tp,yref), xytext=(1.2*tp,0.2*K*U+yref),arrowprops=ap1)

        # rise time
        tr = t[np.where(np.diff(np.sign(y-yref-K*U))*np.sign(K*U)>0)[0][0]]
        if tr < plt.xlim()[1]:
            plt.plot([tr,tr],[0.3*K*U+yref,K*U+yref],'r:')
            plt.annotate('',xy=(plt.xlim()[0],0.4*K*U+yref),xytext=(tr,0.4*K*U+yref),
                         arrowprops=ap2)
            plt.text(plt.xlim()[0]+tr/2,0.42*K*U+yref+0.02*dy,
                    'Rise Time\n = {0:.2f}'.format(tr),ha='center')

        # period
        P = 2*np.pi*tau/np.sqrt(1-zeta**2)
        if tr + P < plt.xlim()[1]:
            plt.plot([tr,tr],[0.3*K*U+yref,K*U+yref],'r:')
            plt.plot([tr+P,tr+P],[0.3*K*U+yref,K*U+yref],'r:')
            plt.annotate('',xy=(tr,0.4*K*U+yref),xytext=(tr+P,0.4*K*U+yref),arrowprops=ap2)
            plt.text(tr+P/2,0.42*K*U+yref+0.02*dy,'Period = {0:.2f}'.format(P), ha='center')

        # second peak
        if tp + P < plt.xlim()[1]:
            plt.annotate('',xy=(tp+P,K*U+yref),xytext=(tp+P,K*U*(1+os**3)+yref),
                        arrowprops=ap2)
            plt.text(tp+P,K*U*(1+os**3/2)+yref,' c')
            plt.text(tp+P,K*U*(1+os**3)+yref+0.02*dy,
                    'Decay Ratio\n c/b = {0:.2f}'.format(os**2),va='bottom',ha='center')

        # settling time
        ts = -np.log(0.05)*np.sqrt(1-zeta**2)*tau/zeta
        if ts < plt.xlim()[1]:
            plt.fill_between(t[t>ts],0.95*K*U+yref,1.05*K*U+yref,alpha=0.4,color='y')
            plt.text(ts,1.05*K*U+yref+0.02*dy,
                     'Settling Time\n = {0:.2f}'.format(ts),ha='center')

    plt.plot(plt.xlim(),[yref,yref],'k--')
    plt.plot(plt.xlim(),[K*U+yref,K*U+yref],'k--')

interact(simulation, yref = (-10,10,0.1), U=(0.01,5,0.01),
         K = (-5,5,0.01), zeta=(0.01,3,0.01), tau = (0.1,5.0,0.01));
```

    A Jupyter Widget

## Performance Indicators for Underdamped Systems

For an underdamped second order system, the desired performance metrics are given by the following by formulas in the following table.

| Quantity           | Symbol | Expression/Value                                       |
| :----------------: | :----: | :----------------------------------------------------: |
| Rise Time          | $t_r$  | Time to first SS crossing                              |
| Time to first peak | $t_p$  | $\frac{\pi\tau}{\sqrt{1-\zeta^2}}$                     |
| Overshoot          | OS     | $\exp\left(-\frac{\pi\zeta}{\sqrt{1-\zeta^2}}\right)$  |
| Decay Ratio        | DR     | $\exp\left(-\frac{2\pi\zeta}{\sqrt{1-\zeta^2}}\right)$ |
| Period             |        | $\frac{2\pi\tau}{\sqrt{1-\zeta^2}}$                    |
| Setting Time       | $t_s$  | Time to +/- 5% of SS                                   |

## Estimating Parameters for an Underdamped System

### Starting with a Physical Model

A dynamical model for a u-tube manometer is given by

$$\frac{d^2h'}{dt^2} + \frac{6\mu}{R^2\rho}\frac{dh'}{dt} + \frac{3}{2}\frac{g}{L} h' = \frac{3}{4\rho L} p'(t)$$

where $h'$ is the liquid level displacement from an equilibrium position due to a pressure difference $p'(t)$.

| Parameter | Symbol |
| :-: | :-: |
| radius | $R$ |
| liquid length | $L$ |
| gravity | $g$ |
| density | $\rho$ |
| viscosity | $\mu$ |

What is the gain $K$? Time constant $\tau$?  Damping factor $\zeta$? How would choose the radius for the fastest response without overshoot?

### Starting with a Step Response

Underdamped systems have clearly identifiable and measureable characteristics that can be used to identify parameters $K$, $\tau$, and $\zeta$. One procedure, for example, is to execute a step response experiment. Then,

1. Measure overshoot, then estimate damping factor $\zeta$ using a chart of of this equation (or by directly solving the equation for $\zeta$):
$$OS = \frac{a}{b} = \exp\left(\frac{-\pi\zeta}{\sqrt{1-\zeta^2}}\right)$$
2. Measure time-to-first-peak $t_p$. Given $t_p$ and $\zeta$, solve for
$$\tau = \frac{t_p}{\pi}\sqrt{1 - \zeta^2}$$
Alternatively, given period $P$,
$$\tau = \frac{P}{2\pi}\sqrt{1 - \zeta^2}$$

```python
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint

zeta = np.linspace(0,0.999,100)
os = np.exp(-np.pi*zeta/np.sqrt(1-zeta**2))
dr = np.exp(-2*np.pi*zeta/np.sqrt(1-zeta**2))
pd = np.sqrt(1-zeta**2)

plt.figure(figsize=(8,8))
plt.plot(zeta, os, lw=3)
plt.plot(zeta, dr, lw=3)
plt.plot(zeta, pd, lw=3)
plt.axis('square')
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.title('Performance Characteristics of Underdamped Second Order Systems')
plt.xlabel('$\zeta$')
plt.ylabel('Performance Characteristic')
plt.text(0.35, 0.4, 'Overshoot')
plt.text(0.05, 0.2, 'Decay Ratio')
plt.text(0.70, 0.8, 'Natural Period / Period')
plt.gca().set_xticks(np.arange(0,1,0.1), minor=True)
plt.gca().set_yticks(np.arange(0,1,0.1), minor=True)
plt.grid(b=True, which='major')
plt.grid(b=True, which='minor')

```

```python

```

```python

```

< [One Compartment Pharmacokinetics](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/03.05-One-Compartment-Pharmacokinetics.ipynb) | [Contents](toc.ipynb) | [Interacting Tanks](http://nbviewer.jupyter.org/github/jckantor/CBE30338/blob/master/notebooks/03.07-Interacting-Tanks.ipynb) >


## First Assignment

#### 1) Apply the appropriate string methods to the **x** variable (as '.upper') to change it exactly to: "$Dichlorodiphenyltrichloroethane$".

```python
x = "DiClOrod  IFeNi  lTRicLOr oETaNo  DiChlorod iPHeny lTrichL oroEThaNe"
```

```python
x.replace (' ','')[27:].title()
```

    'Dichlorodiphenyltrichloroethane'

```python

```

#### 2) Assign respectively the values: 'word', 15, 3.14 and 'list' to variables A, B, C and D in a single line of code. Then, print them in that same order on a single line separated by a space, using only one print statement.

```python
d1 = {'A':'word', 'B':15, 'C':3.14, 'D':'list'{
print(d1)
```

    {'A': 'word', 'B': 15, 'C': 3.14, 'D': 'list'}

#### 3) Use the **input()** function to receive an input in the form **'68.4 1.71'**, that is, two floating point numbers in a line separated by space. Then, assign these numbers to the variables **w** and **h** respectively, which represent an individual's weight and height (hint: take a look at the '.split()' method). With this data, calculate the individual's Body Mass Index (BMI) from the following relationship:

\begin{equation}
BMI = \dfrac{weight}{height^2}
\end{equation}

```python
h = float(input("Enter your height"))

w = float(input("Enter your weight"))

bmi = w / ((h/100)**2)

print(f"BMI is {bmi}")
```

    Enter your height 171
    Enter your weight 68.4

    BMI is 23.39181286549708

#### This value can also be classified according to ranges of values, following to the table below. Use conditional structures to classify and print the classification assigned to the individual.

<\center>

(source: https://healthtravelguide.com/bmi-calculator/)

```python
if bmi <= 18.5:
    print("underweight")
elif bmi <= 24.9:
    print("normal weight")
elif bmi <= 29.9:
    print("pre-obesity")
elif bmi <= 34.9:
    print("obesity class I")
elif bmi <= 39.9:
    print("obesity class II")
else:
    print("obesity class III")
```

    underweight

#### 4) Receive an integer as an input and, using a loop, calculate the factorial of this number, that is, the product of all the integers from one to the number provided.

```python
import math
m = (int(input("Enter a number:")))
fact = math.factorial(m)
print("the factorial of", m, "is", fact)

```

    Enter a number: 4

    the factorial of 4 is 24

#### 5) Using a while loop and the input function, read an indefinite number of integers until the number read is -1. Present the sum of all these numbers in the form of a print, excluding the -1 read at the end.

```python
num_sum=0

inputs = []
while True:
    inp = input("Insert integer: ")

    if inp == "-1":
        break
    inp = int(inp)
    inputs.append(inp)
    num_sum += inp

print (inputs, num_sum)

```

    Insert integer:  4
    Insert integer:  4
    Insert integer:  -1

    [4, 4] 8

```python
x = (int(input("Enter an integer:")))
num_sum=0
count=0

while True:
    x = (int(input("Enter an integer:")))
    print(x)
    num_sum += x
    count += 1
    print (num_sum)
    if x < -1:
        break
```

    Enter an integer: 3
    Enter an integer: 4

#### 6) Read the **first name** of an employee, his **amount of hours worked** and the **salary per hour** in a single line separated by commas. Next, calculate the **total salary** for this employee and show it to two decimal places.

```python
my_employee = ['Hans', 38.5, 75.56]
type(my_employee)
dir(my_employee)
total = (my_employee[1]*my_employee[2])
format_total = "{:.2f}".format(total)
print(format_total)

```

    2909.06

#### 7) Read three floating point values **A**, **B** and **C** respectively. Then calculate itens a, b, c, d and e:

```python
A = 12.45
B = 14.32
C = 9.45

a = A + B
b = B + C
c = A + C
d = A / 2 * B
e = A ** 2

print(a)
print(b)
print(c)
print(d)
print(e)

```

    26.77
    23.77
    21.9
    89.142
    155.00249999999997

    a) the area of the triangle rectangle with A as the base and C as the height.

```python
s = (A + B + C) / 2

area_triangle = (s*(s-A)*(s-B)*(s-C)) ** 0.5
print('The area of the triangle is %0.2f' %area_triangle)
```

    The area of the triangle is 58.00

    b) the area of the circle of radius C. (pi = 3.14159)

```python
from math import pi
print ("The area of the circle with radius " + str(C) + " is: " + str(pi * C**2))
```

    The area of the circle with radius 9.45 is: 280.55207794720246

    c) the area of the trapezoid that has A and B for bases and C for height.

```python
area_trapezoid = ((A + B) / 2) * C
print("The area of the trapezoid is:", area_trapezoid)
```

    Area is: 126.48825

    d) the area of the square that has side B.

```python
area_square = B ** 2
print("The area of the square is:", area_square)
```

    The area of the square is: 205.0624

    e) the area of the rectangle that has sides A and B.

```python
area_rectangle = A * B
print("The area of the rectangle is:", area_rectangle)
```

    The area of the rectangle is: 178.284

#### 8) Read **the values a, b and c** and calculate the **roots of the second degree equation** $ax^{2}+bx+c=0$ using [this formula](https://en.wikipedia.org/wiki/Quadratic_equation). If it is not possible to calculate the roots, display the message **“There are no real roots”**.

```python
import cmath

a = (int(input("Enter an value a:")))
b = (int(input("Enter an value b:")))
c = (int(input("Enter an value c:")))

x = (b**2) - (4*a*c)

if x > 0:
    sol1 = (-b-cmath.sqrt(x))/(2*a)
    sol2 = (-b+cmath.sqrt(x))/(2*a)
    print("The solution are {0} and {1}".format(sol1,sol2))
else:
    print('there are no real roots')

```

    Enter an value a: -20
    Enter an value b: 3
    Enter an value c: -10

    there are no real roots

#### 9) Read four floating point numerical values corresponding to the coordinates of two geographical coordinates in the cartesian plane. Each point will come in a line with its coordinates separated by space. Then calculate and show the distance between these two points.

(obs: $d=\sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}$)

```python
import math

coord_1 = 41.4430566, 19.3345678
x_1, y_1 = coord_1

coord_2 = 52.2296756, 14.9254481
x_2, y_2 = coord_2

d = math.sqrt(((x_1 - x_2)**2) + ((y_1 - y_2)**2))
print(d)

```

    11.652960395542804

#### 10) Read **two floating point numbers** on a line that represent **coordinates of a cartesian point**. With this, use **conditional structures** to determine if you are at the origin, printing the message **'origin'**; in one of the axes, printing **'x axis'** or **'y axis'**; or in one of the four quadrants, printing **'q1'**, **'q2**', **'q3'** or **'q4'**.

```python
coord = 41.4430566, 19.3345678
x, y = coord

if x == 0 and y == 0:
    print('origin')
elif x==0: print('x axis')
elif y == 0: print('y axis')
elif x < 0 and y < 0: print('q3')
elif x > 0 and y < 0: print('q2')
elif x > 0 and y > 0: print('q1')
else: print('q4')

```

    q1

#### 11) Read an integer that represents a phone code for international dialing.
#### Then, inform to which country the code belongs to, considering the generated table below:
(You just need to consider the first 10 entries)

```python
import pandas as pd
df = pd.read_html('https://en.wikipedia.org/wiki/Telephone_numbers_in_Europe')[1]
df = df.iloc[:,:2]
df.head(20)
```

      Country
      Country calling code

      0
      Austria
      43

      1
      Belgium
      32

      2
      Bulgaria
      359

      3
      Croatia
      385

      4
      Cyprus
      357

      5
      Czech Republic
      420

      6
      Denmark
      45

      7
      Estonia
      372

      8
      Finland
      358

      9
      France
      33

      10
      Germany
      49

      11
      Greece
      30

      12
      Hungary
      36

      13
      Iceland
      354

      14
      Ireland
      353

      15
      Italy
      39

      16
      Latvia
      371

      17
      Liechtenstein
      423

      18
      Lithuania
      370

      19
      Luxembourg
      352

```python
codes = {"43": 'Austria', "32": 'Belgium', "359": 'Bulgaria', "385": 'Croatia', "357": 'Cyprus', "420": 'Czech Republic', "45": 'Denmark', "372": 'Estonia', "358": 'Finnland', "33": 'France', "49": 'Germany'}
my_code = input("what is your code number")
codes[my_code]

```

    what is your code number 385

    'Croatia'

#### 12) Write a piece of code that reads 6 numbers in a row. Next, show the number of positive values entered. On the next line, print the average of the values to one decimal place.

```python
newTuple = (-1, -2, 3, -4, 7, 6)

print("Positive Tuple Items = ", newTuple)

print("\nThe Positive Numbers in this newTuple Tuple are:")
for posnum in newTuple:
    if(posnum >= 0):
        print(posnum, end = "  ")

def ave_pos(posnum):
    count = 0
    sum = 0.0
    for x in newTuple:
        sum_pos = sum_pos + x
        count += 1
    return sum_pos / count

response = (ave_pos);
print(response)
##print("{:.1f}".format(response))

```

    Positive Tuple Items =  (-1, -2, 3, -4, 7, 6)

    The Positive Numbers in this newTuple Tuple are:
    3  7  6  <function ave_pos at 0x11693c310>

#### 13) Read an integer **N**. Then print the **square of each of the even values**, from 1 to N, including N, if applicable, arranged one per line.

```python
def printSquares(N):

    square = 0; prev_x = 0;

    for x in range(0, N):

        square = (square + x + prev_x)

        print(square, end = " ")
        prev_x = x

N = (int(input("Enter an integer:")));
printSquares(N);
```

    Enter an integer: 6

    0 1 4 9 16 25

#### 14) Using **input()**, read an integer and print its classification as **'even / odd'** and **'positive / negative'** . The two classes for the number must be printed on the same line separated by a space. In the case of zero, print only **'null'**.

```python
num = int(input())
while num == 0:
       print("null")

if num > 0:

    if (num % 2) == 0:
        print("Positive even number")
    else:
        print("Positive odd number")
else:

    if (num % 2) == 0:
        print("is Even negative".format(num))
if (num % 2) != 0:
    print("is Odd negative".format(num))

```

     -33

    is Odd negative

```python
##version 2

my_new_integer = int(input())
if my_new_integer %2 and my_new_integer > 0:
    print("even positive")
elif my_new_integer == 0:
    print ("null")
elif my_new_integer < 0 and my_new_integer %2 == 0:
    print ("even negative")
elif my_new_integer > 0 and my_new_integer %2 != 0:
    print ("odd positive")
else:
    print ("odd negative")
```

     -33

    odd negative

## Challenge
#### 15) Ordering problems are recurrent in the history of programming. Over time, several algorithms have been developed to fulfill this function. The simplest of these algorithms is the [**Bubble Sort**](https://en.wikipedia.org/wiki/Bubble_sort), which is based on comparisons of elements two by two in a loop of passes through the elements. Your mission, if you decide to accept it, will be to input six whole numbers ramdonly ordered. Then implement the **Bubble Sort** principle to order these six numbers **using only loops and conditionals**.
#### At the end, print the six numbers in ascending order on a single line separated by spaces.

```python
import random
def bubblesort(a):
  i = len(a)
  while i!=0:
    j = 0
    while j!=i-1:
      if(a[j+1]<a[j]):
        temp = a[j+1]
        a[j+1] =a[j]
        a[j]=temp
      j+=1
    i-=1

a=random.sample(range(0,1000),6)
bubblesort(a)
a
```

    [97, 281, 313, 346, 352, 377]

```python

```