## 20 Sept 2019

RULES

Date: Level 2 heading ##

Example Heading: Level 3 heading ###

Method Heading: Level 4 heading ####

### References

1. [Forester_W._Isen;_J._Moura]_DSP_for_MATLAB_and_La Volume II(z-lib.org)
2. H. K. Dass, Advanced Engineering Mathematics
3. [Forester_W._Isen;_J._Moura]_DSP_for_MATLAB_and_La Volume I(z-lib.org)
4. [John_G_Proakis;_Dimitris_G_Manolakis]_Digital_Sig(z-lib.org)

### Imports

```python
import numpy as np
from sympy import oo
import math
import sympy as sp
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
from IPython.display import display
from IPython.display import display_latex
from sympy import latex
import math
from scipy import signal
from datetime import datetime
from  matplotlib import patches
from matplotlib.figure import Figure
from matplotlib import rcParams
```

### Setup

```python
sp.init_printing(use_latex = True)

z, f, i = sp.symbols('z f i')
x, k = sp.symbols('x k')
```

### Methods

```python
# Usage: display_equation('u_x', x)
def display_equation(idx, symObj):
    if(isinstance(idx, str)):
        eqn = '\\[' + idx + ' = ' + latex(symObj) + '\\]'
        display_latex(eqn, raw=True)
    else:
        eqn = '\\[' + latex(idx) + ' = ' + latex(symObj) + '\\]'
        display_latex(eqn, raw=True)
    return
```

```python
# Usage: display_full_latex('u_x')
def display_full_latex(idx):
    if(isinstance(idx, str)):
        eqn = '\\[' + idx + '\\]'
        display_latex(eqn, raw=True)
    else:
        eqn = '\\[' + latex(idx) + '\\]'
        display_latex(eqn, raw=True)
    return
```

```python
# Usage: display_full_latex('u_x')
def display_full_latex(idx):
    if(isinstance(idx, str)):
        eqn = '\\[' + idx + '\\]'
        display_latex(eqn, raw=True)
    else:
        eqn = '\\[' + latex(idx) + '\\]'
        display_latex(eqn, raw=True)
    return
```

```python
def ztrans(a, b):
    F = sp.summation(f/z**k, ( k, a, b ))
    return F
```

```python
def display_ztrans(f, k, limits = (-4, 4)):
    F = sp.summation(f/z**k, ( k, -oo, oo ))
    display_equation('f(k)', f)
    display_equation('F(k)_{\infty}', F)

    F = sp.summation(f/z**k, (k, limits[0], limits[1]))
    display_equation('F(k)_{'+ str(limits[0]) + ',' + str(limits[1]) + '}', F)
    return
```

```python
def sum_of_GP(a, r):
    return sp.simplify(a/(1-r))
```

```python
#a = Denominator [2, 1, 1] -> 2z^2 + z + 1
#b = Numerator [1, 0, 3] -> z^2 + 3

# Credit: https://www.dsprelated.com/showcode/244.php

def zplane(b,a,filename=None):
    """Plot the complex z-plane given a transfer function.
    """

    # get a figure/plot
    ax = plt.subplot(111)

    # create the unit circle
    uc = patches.Circle((0,0), radius=1, fill=False,
                        color='black', ls='dashed')
    ax.add_patch(uc)

    # The coefficients are less than 1, normalize the coeficients
    if np.max(b) > 1:
        kn = np.max(b)
        b = b/float(kn)
    else:
        kn = 1

    if np.max(a) > 1:
        kd = np.max(a)
        a = a/float(kd)
    else:
        kd = 1

    # Get the poles and zeros
    p = np.roots(a)
    z = np.roots(b)
    k = kn/float(kd)

    # Plot the zeros and set marker properties
    t1 = plt.plot(z.real, z.imag, 'go', ms=10)
    plt.setp( t1, markersize=10.0, markeredgewidth=1.0,
              markeredgecolor='k', markerfacecolor='g')

    # Plot the poles and set marker properties
    t2 = plt.plot(p.real, p.imag, 'rx', ms=10)
    plt.setp( t2, markersize=12.0, markeredgewidth=3.0,
              markeredgecolor='b', markerfacecolor='b')

    ax.spines['left'].set_position('center')
    ax.spines['bottom'].set_position('center')
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)

    # set the ticks
    r = 1.5; plt.axis('scaled'); plt.axis([-r, r, -r, r])
    ticks = [-1, -.5, .5, 1]; plt.xticks(ticks); plt.yticks(ticks)

    if filename is None:
        plt.show()
    else:
        plt.savefig(filename)

    return z, p, k
```

### Z Transform

```python
display_full_latex('X(z) = \sum_{-\infty}^{\infty} x[n]z^{-n}')
```

\[X(z) = \sum_{-\infty}^{\infty} x[n]z^{-n}\]

### Tests

#### Convert Symbolic to Numeric

```python
f = x**2
f = sp.lambdify(x, f, 'numpy')
f(2)
```

```python
display_equation('f(x)', sp.summation(3**k, ( k, -oo, oo )))
```

\[f(x) = \sum_{k=-\infty}^{\infty} 3^{k}\]

```python
display_equation('F(z)', sp.summation(3**k/z**k, ( k, -oo, oo )))
```

\[F(z) = \sum_{k=-\infty}^{\infty} 3^{k} z^{- k}\]

#### Partial Fractions

```python
f = 1/(x**2 + x - 6)
display_equation('f(x)', f)
f = sp.apart(f)
display_equation('f(x)_{canonical}', f)
```

\[f(x) = \frac{1}{x^{2} + x - 6}\]

\[f(x)_{canonical} = - \frac{1}{5 \left(x + 3\right)} + \frac{1}{5 \left(x - 2\right)}\]

#### Piecewise

```python
f1 = 5**k
f2 = 3**k
f = sp.Piecewise((f1, k < 0), (f2, k >= 0))
display_equation('f(k)', f)
```

\[f(k) = \begin{cases} 5^{k} & \text{for}\: k < 0 \\3^{k} & \text{otherwise} \end{cases}\]

## 21 Sept 2019

#### Positive Time / Causal

```python
f1 = k **2
f2 = 3**k
f = f1 * sp.Heaviside(k)
# or
#f = sp.Piecewise((0, k < 0), (f1, k >= 0))
display_equation('f(k)', f)
sp.plot(f, (k, -10, 10))
```

#### Stem Plot

```python
x = np.linspace(0.1, 2 * np.pi, 41)
y = np.sin(x)

plt.stem(x, y)
plt.show()
```

#### zplane Plot

```python
b = np.array([1, 1, 0, 0])
a = np.array([1, 1, 1])
zplane(b,a)
```

### Filter

```python
g = (1 + z**-2)/(1-1.2*z**-1+0.81*z**-2)
display_equation('F(z)', g)
b = np.array([1,1])
a = np.array([1,-1.2,0.81])
x = np.ones((1, 8))
# Response
y = signal.lfilter(b, a, x)
# Reverse
signal.lfilter(a, b, y)
```

\[F(z) = \frac{1 + \frac{1}{z^{2}}}{1 - \frac{1.2}{z} + \frac{0.81}{z^{2}}}\]

    array([[1., 1., 1., 1., 1., 1., 1., 1.]])

### [1] Example 2.2

```python
radFreq = np.arange(0, 2*np.pi, 2*np.pi/499)
g = np.exp(1j*radFreq)
Zxform= 1/(1-0.7*g**(-1))

plt.plot(radFreq/np.pi,abs(Zxform))
plt.title('Graph')
plt.xlabel('Frequency, Units of π')
plt.ylabel('H(x)')
plt.grid(True)
plt.show()
```

### [2] Chapter 19, Example 5

```python
f = 3**(-k)
display_ztrans(f, k, (-4, 3))
```

\[f(k) = 3^{- k}\]

\[F(k)_{\infty} = \sum_{k=-\infty}^{\infty} 3^{- k} z^{- k}\]

\[F(k)_{-4,3} = 81 z^{4} + 27 z^{3} + 9 z^{2} + 3 z + 1 + \frac{1}{3 z} + \frac{1}{9 z^{2}} + \frac{1}{27 z^{3}}\]

### [2] Example 9

```python
f1 = 5**k
f2 = 3**k
f = sp.Piecewise((f1, k < 0), (f2, k >= 0))
display_ztrans(f, k, (-3, 3))
```

\[f(k) = \begin{cases} 5^{k} & \text{for}\: k < 0 \\3^{k} & \text{otherwise} \end{cases}\]

\[F(k)_{\infty} = \sum_{k=-\infty}^{\infty} \begin{cases} 5^{k} z^{- k} & \text{for}\: k < 0 \\3^{k} z^{- k} & \text{otherwise} \end{cases}\]

\[F(k)_{-3,3} = \frac{z^{3}}{125} + \frac{z^{2}}{25} + \frac{z}{5} + 1 + \frac{3}{z} + \frac{9}{z^{2}} + \frac{27}{z^{3}}\]

```python
p = sum_of_GP(z/5, z/5)
q = sum_of_GP(1, 3/z)
display_equation('F(z)', sp.ratsimp(q + p))
```

\[F(z) = - \frac{2 z}{z^{2} - 8 z + 15}\]

## 28 Sept, 2019

### [3] Folding formula

fperceived = [ f - fsampling * NINT( f / fsampling ) ]

## 9 Oct, 2019

### [3] Section 4.3

### Equations

```python
display_full_latex('F \\rightarrow analog')
display_full_latex('f \\rightarrow discrete')
display_full_latex('Nyquist frequency = F_s')
display_full_latex('Folding frequency = \\frac{F_s}{2}')
display_full_latex('F_{max} = \\frac{F_s}{2}')
display_full_latex('T = \\frac{1}{F_s}')
display_full_latex('f = \\frac{F}{F_s}')
display_full_latex('f_k = \\frac{k}{N}')
display_full_latex('F_k = F_0 + kF_s, k = \\pm 1, \\pm 2, ...')
display_full_latex('x_a(t) = Asin(2\\pi Ft + \\theta)')
display_full_latex('x(n) = Asin(\\frac{2\\pi nk}{N} + \\theta)')
display_full_latex('x(n) = Asin(2\\pi fn + \\theta)')
display_full_latex('x(n) = x_a (nT) = Acos(2\\pi \\frac{F_0 + kF_s}{F_s} n + \\theta)')
display_full_latex('t = nT')
display_full_latex('\\Omega = 2\\pi F')
display_full_latex('\\omega = 2\\pi f')
display_full_latex('\\omega = \\Omega T')
display_full_latex('x_q(n) = Q[x(n)]')
display_full_latex('e_q(n) = x_q(n) - x(n)')
display_full_latex('Interpolation function, g(t) = \\frac{sin2\\pi Bt}{2\\pi Bt}')
display_full_latex('x_a(t) = \\sum^\\infty _{n = - \\infty} x_a(\\frac{n}{F_s}).g(t - \\frac{n}{F_s})')
display_full_latex('\\Delta = \\frac{x_{max} - x_{min}}{L-1}, where L = Number of quantization levels')
display_full_latex('-\\frac{\\Delta}{2} \\leq e_q(n) \\leq \\frac{\\Delta}{2}')
display_full_latex('b \\geq log_2 L')
display_full_latex('SQNR = \\frac{3}{2}.2^{2b}')
display_full_latex('SQNR(dB) = 10log_{10}SQNR = 1.76 + 6.02b')
```

\[F \rightarrow analog\]

\[f \rightarrow discrete\]

\[Nyquist frequency = F_s\]

\[Folding frequency = \frac{F_s}{2}\]

\[F_{max} = \frac{F_s}{2}\]

\[T = \frac{1}{F_s}\]

\[f = \frac{F}{F_s}\]

\[f_k = \frac{k}{N}\]

\[F_k = F_0 + kF_s, k = \pm 1, \pm 2, ...\]

\[x_a(t) = Asin(2\pi Ft + \theta)\]

\[x(n) = Asin(\frac{2\pi nk}{N} + \theta)\]

\[x(n) = Asin(2\pi fn + \theta)\]

\[x(n) = x_a (nT) = Acos(2\pi \frac{F_0 + kF_s}{F_s} n + \theta)\]

\[t = nT\]

\[\Omega = 2\pi F\]

\[\omega = 2\pi f\]

\[\omega = \Omega T\]

\[x_q(n) = Q[x(n)]\]

\[e_q(n) = x_q(n) - x(n)\]

\[Interpolation function, g(t) = \frac{sin2\pi Bt}{2\pi Bt}\]

\[x_a(t) = \sum^\infty _{n = - \infty} x_a(\frac{n}{F_s}).g(t - \frac{n}{F_s})\]

\[\Delta = \frac{x_{max} - x_{min}}{L-1}, where L = Number of quantization levels\]

\[-\frac{\Delta}{2} \leq e_q(n) \leq \frac{\Delta}{2}\]

\[b \geq log_2 L\]

\[SQNR = \frac{3}{2}.2^{2b}\]

\[SQNR(dB) = 10log_{10}SQNR = 1.76 + 6.02b\]

```python
x = np.arange(0, 10, 1)
y = np.power(0.9, x) * np.heaviside(np.power(0.9, x), 1)

display_full_latex('x_a(t) = 0.9^t')
display_full_latex('x(n) = 0.9^n')

plt.stem(x, y)
plt.plot(x, y, 'g-')
plt.xticks(np.arange(0, 10, 1))
plt.yticks(np.arange(0, 1.2, 0.1))
plt.xlabel('n')
plt.ylabel('x(n)')
plt.grid(True)
plt.show()
```

## 14 Oct, 2019

```python
n = sp.symbols('n')

x = np.arange(0, 10, 1)
y = x * np.heaviside(x, 1)

f = sp.Piecewise((0, n < 0), (n, n >= 0))
display_equation('u_r(n)', f)

plt.stem(x, y)
plt.plot(x, y, 'g-')
plt.xticks(np.arange(0, 10, 1))
plt.yticks(np.arange(0, 10, 1))
plt.xlabel('n')
plt.ylabel('x(n)')
plt.grid(True)
plt.show()
```

```python
display_full_latex('E = \\sum^\\infty _{n = -\\infty} x|(n)|^2')
display_full_latex('P = \\lim_{N \\rightarrow \\infty} \\frac{1}{2N + 1} \\sum^ N _{n = -N} x|(n)|^2')
```

\[E = \sum^\infty _{n = -\infty} x|(n)|^2\]

\[P = \lim_{N \rightarrow \infty} \frac{1}{2N + 1} \sum^ N _{n = -N} x|(n)|^2\]

## 16 Oct, 2019

#### General form of the input-output relationships

```python
display_full_latex('y(n) = -\\sum^N _{k = 1}a_k y(n-k) + \\sum^M _{k = 0}b_k x(n-k)')
```

\[y(n) = -\sum^N _{k = 1}a_k y(n-k) + \sum^M _{k = 0}b_k x(n-k)\]

### [4] Example 3.2

```python
h = np.array([1, 2, 1, -1])
x = np.array([1, 2, 3, 1])
y = np.convolve(h, x, mode='full')
#y = signal.convolve(h, x, mode='full', method='auto')
print(y)

fig, (ax_orig, ax_h, ax_x) = plt.subplots(3, 1, sharex=True)
ax_orig.plot(h)
ax_orig.set_title('Impulse Response')
ax_orig.margins(0, 0.1)
ax_h.plot(x)
ax_h.set_title('Input Signal')
ax_h.margins(0, 0.1)
ax_x.plot(y)
ax_x.set_title('Output')
ax_x.margins(0, 0.1)
fig.tight_layout()
fig.show()
```

## 17 Oct, 2019

### Sum of an AP with common ratio r and first term a, starting from the zeroth term

```python
a, r = sp.symbols('a r')

s = sp.summation(a*r**k, ( k, 0, n ))
display_equation('S_n', s)
```

\[S_n = a \left(\begin{cases} n + 1 & \text{for}\: r = 1 \\\frac{- r^{n + 1} + 1}{- r + 1} & \text{otherwise} \end{cases}\right)\]

### Sum of positive powers of a

```python
a = sp.symbols('a')

s = sp.summation(a**k, ( k, 0, n ))
display_equation('S_n', s)
```

\[S_n = \begin{cases} n + 1 & \text{for}\: a = 1 \\\frac{- a^{n + 1} + 1}{- a + 1} & \text{otherwise} \end{cases}\]

### [3] 4.12.3 Single Pole IIR

```python
SR = 24
b = 1
p = 0.8
y = np.zeros((1, SR)).ravel()
x = np.zeros((1, SR + 1)).ravel()
x[0] = 1
y[0] = b * x[0]

for n in range(1, SR):
    y[n] = b * x[n] + p * y[n - 1]

plt.stem(y)
```

### Copying the method above for [4] 4.1 Averaging

```python
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
y[0] = b * x[0]

for n in range(1, len(x)):
    y[n] = (n/(n + 1)) * y[n - 1] + (1/(n + 1)) * x[n]

print(y[n], '\n')
```

    5.5

### My Recursive Averaging Implementation

```python
def avg(x, n):
    if (n < 0):
        return 0
    else:
        return (n/(n + 1)) * avg(x, n - 1) + (1/(n + 1)) * x[n]
```

```python
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
average = avg(x, len(x) - 1)
print(average)
```

    5.5

### Performance Comparism

```python
from timeit import timeit

code_rec = '''
import numpy as np

def avg(x, n):
    if (n < 0):
        return 0
    else:
        return (n/(n + 1)) * avg(x, n - 1) + (1/(n + 1)) * x[n]

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
average = avg(x, len(x) - 1)
'''

code_py = '''
import numpy as np

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
average = sum(x, len(x) - 1) / len(x)
'''

code_loop = '''
import numpy as np

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
sum = 0

for i in x:
    sum += i

average = sum/len(x)
'''

running_time_rec = timeit(code_rec, number = 100) / 100
running_time_py = timeit(code_py, number = 100) / 100
running_time_loop = timeit(code_loop, number = 100) / 100

print("Running time using my recursive average function: \n",running_time_rec, '\n')
print("Running time using python sum function: \n",running_time_py)
print("Running time using loop python function: \n",running_time_loop)
```

    Running time using my recursive average function:
     0.00019250600000020768

    Running time using python sum function:
     6.519000000366759e-06
    Running time using loop python function:
     8.873000000448883e-06

### [4] Example 4.1

```python
def rec_sqrt(x, n):
    if (n == -1):
        return 1
    else:
        return (1/2) * (rec_sqrt(x, n - 1) + (x[n]/rec_sqrt(x, n - 1)))
```

```python
A = 2
x = np.ones((1, 5)).ravel() * A
print(rec_sqrt(x, len(x) - 1))
```

    1.414213562373095

```python
b = np.array([1, 1, 1, 1, 1])
a = np.array([1, 0, 0])
zplane(b,a)
```

```python

```


# Design of a Cold Weather Fuel for a Camping Stove

The venerable alcohol stove has been invaluable camping accessory for generations. They are simple, reliable, and in a pinch, can be made from aluminum soda cans.

Alcohol stoves are typically fueled with denatured alcohol. Denatured alcohol, sometimes called methylated spirits, is a generally a mixture of ethanol and other alcohols and compounds designed to make it unfit for human consumption. An MSDS description of one [manufacturer's product](https://www.korellis.com/wordpress/wp-content/uploads/2016/05/Alcohol-Denatured.pdf) describes a roughly fifity/fifty mixture of ethanol and methanol.

The problem with alcohol stoves is they can be difficult to light in below freezing weather. The purpose of this notebook is to design of an alternative cold weather fuel that could be mixed from other materials commonly available from hardware or home improvement stores.

```
%%capture
!pip install -q pyomo
!apt-get install -y -qq glpk-utils
```

## Data

The following data was collected for potential fuels commonly available at hardware and home improvement stores. The data consists of price (\$/gal.) and parameters to predict vapor pressure using the Antoine equation,

\begin{align}
\log_{10}P^{vap}_{s}(T) & = A_s - \frac{B_s}{T + C_s}
\end{align}

where the subscript $s$ refers to species, temperature $T$ is in units of degrees Celcius, and pressure $P$ is in units of mmHg. The additional information for molecular weight and specific gravity will be needed to present the final results in volume fraction.

```
data = {
    'ethanol'          : {'MW':  46.07, 'SG': 0.791, 'A': 8.04494, 'B': 1554.3,  'C': 222.65},
    'methanol'         : {'MW':  32.04, 'SG': 0.791, 'A': 7.89750, 'B': 1474.08, 'C': 229.13},
    'isopropyl alcohol': {'MW':  60.10, 'SG': 0.785, 'A': 8.11778, 'B': 1580.92, 'C': 219.61},
    'acetone'          : {'MW':  58.08, 'SG': 0.787, 'A': 7.02447, 'B': 1161.0,  'C': 224.0},
    'xylene'           : {'MW': 106.16, 'SG': 0.870, 'A': 6.99052, 'B': 1453.43, 'C': 215.31},
    'toluene'          : {'MW':  92.14, 'SG': 0.865, 'A': 6.95464, 'B': 1344.8,  'C': 219.48},
}
```

## Denatured Alcohol

The first step is to determine the vapor pressure of denatured alcohol over a typical range of operating temperatures. For this we assume denatured alcohol is a 40/60 (mole fraction) mixture of ethanol and methanol.

```
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

def Pvap(T, s):
    return 10**(data[s]['A'] - data[s]['B']/(T + data[s]['C']))

def Pvap_denatured(T):
    return 0.4*Pvap(T, 'ethanol') + 0.6*Pvap(T, 'methanol')

T = np.linspace(0, 40, 200)

plt.plot(T, Pvap_denatured(T))
plt.title('Vapor Pressure of denatured alcohol')
plt.xlabel('temperature / °C')
plt.ylabel('pressure / mmHg')
print("Vapor Pressure at 0C =", round(Pvap_denatured(0),1), "mmHg")
```

## Cold Weather Product Requirements

We seek a cold weather fuel with increased vapor pressure at 0°C and lower, and also provides safe and normal operation of the alcohol stove at higher operating temperatures.

For this purpose, we seek a mixture of commonly available liquids with a vapor pressure of at least 22 mmHg at the lowest possible temperature, and no greater than the vapor pressure of denatured alcohol at temperatures 30°C and above.

```
for s in data.keys():
    plt.plot(T, Pvap(T,s))
plt.plot(T, Pvap_denatured(T), 'k', lw=3)
plt.legend(list(data.keys()) + ['denatured alcohol'])
plt.title('Vapor Pressure of selected compounds')
plt.xlabel('temperature / °C')
plt.ylabel('pressure / mmHg');
```

## Optimization Model

The first optimization model is to create a mixture that maximizes the vapor pressure at -10°C while having a vapor pressure less than or equal to denatured alcohol at 30°C and above.

The decision variables in the optimization model correspond to $x_s$, the mole fraction of each species $s \in S$ from the set of available species $S$. By definition, the mole fractions must satisfy

\begin{align}
x_s & \geq 0 & \forall s\in S \\
\sum_{s\in S} x_s & = 1
\end{align}

The objective is to maximize the vapor pressure at low temperatures, say -10°C, while maintaing a vapor pressure less than or equal to denatured alcohol at 30°C. Using Raoult's law for ideal mixtures,

\begin{align}
\max_{x_s} \sum_{s\in S} x_s P^{vap}_s(-10°C) \\
\end{align}
subject to
\begin{align}
\sum_{s\in S} x_s P^{vap}_s(30°C) & \leq P^{vap}_{denatured\ alcohol}(30°C) \\
\end{align}

This optimization model is implemented in Pyomo in the following cell.

```
import pyomo.environ as pyomo

m = pyomo.ConcreteModel()

S = data.keys()
m.x = pyomo.Var(S, domain=pyomo.NonNegativeReals)

def Pmix(T):
    return sum(m.x[s]*Pvap(T,s) for s in S)

m.obj = pyomo.Objective(expr = Pmix(-10), sense=pyomo.maximize)

m.cons = pyomo.ConstraintList()

m.cons.add(sum(m.x[s] for s in S)==1)
m.cons.add(Pmix(30) <= Pvap_denatured(30))
m.cons.add(Pmix(40) <= Pvap_denatured(40))

solver = pyomo.SolverFactory('glpk')
solver.solve(m)

print("Vapor Pressure at -10°C =", m.obj(), "mmHg")

T = np.linspace(-10,40,200)
plt.plot(T, Pvap_denatured(T), 'k', lw=3)
plt.plot(T, [Pmix(T)() for T in T], 'r', lw=3)
plt.legend(['denatured alcohol'] + ['cold weather blend'])
plt.title('Vapor Pressure of selected compounds')
plt.xlabel('temperature / °C')
plt.ylabel('pressure / mmHg');
```

## Display Composition

```
import pandas as pd

s = data.keys()
results = pd.DataFrame.from_dict(data).T
for s in S:
    results.loc[s,'mole fraction'] = m.x[s]()

MW = sum(m.x[s]()*data[s]['MW'] for s in S)
for s in S:
    results.loc[s,'mass fraction'] = m.x[s]()*data[s]['MW']/MW

vol = sum(m.x[s]()*data[s]['MW']/data[s]['SG'] for s in S)
for s in S:
    results.loc[s,'vol fraction'] = m.x[s]()*data[s]['MW']/data[s]['SG']/vol

results
```

      A
      B
      C
      MW
      SG
      mole fraction
      mass fraction
      vol fraction

      acetone
      7.02447
      1161.00
      224.00
      58.08
      0.787
      0.428164
      0.2906
      0.311695

      ethanol
      8.04494
      1554.30
      222.65
      46.07
      0.791
      0.000000
      0.0000
      0.000000

      isopropyl alcohol
      8.11778
      1580.92
      219.61
      60.10
      0.785
      0.000000
      0.0000
      0.000000

      methanol
      7.89750
      1474.08
      229.13
      32.04
      0.791
      0.000000
      0.0000
      0.000000

      toluene
      6.95464
      1344.80
      219.48
      92.14
      0.865
      0.000000
      0.0000
      0.000000

      xylene
      6.99052
      1453.43
      215.31
      106.16
      0.870
      0.571836
      0.7094
      0.688305

```

```


```python
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
from PlottingSpectrum import generate_SED

def weighted_fitting(x_s, y_s, errs):
    list_Y = []
    list_A = []
    list_C = []

    for i in range(len(x_s)):
        list_Y.append([y_s[i]])
        list_A.append([1, x_s[i]])

        C_row = []

        counter = 0
        while counter < len(x_s):
            if counter == i:
                C_row.append(errs[i])
            else:
                C_row.append(0)

            counter += 1

        list_C.append(C_row)

    A = Matrix(list_A)
    Y = Matrix(list_Y)
    C = Matrix(list_C)

    X = ((A.T*C.inv()*A).inv())*(A.T*C.inv()*Y)

    return X[0, 0], X[1, 0]
```

```python
def calc_chi(x_s, y_s, errs, b, m):
    chi = 0
    for i in range(len(x_s)):
        chi += ((y_s[i] - (b + m*x_s[i]))**2)/(errs[i]**2)

    return chi
```

```python
def read_file(name):
    lst_1 = []
    lst_2 = []
    lst_3 = []

    with open(name) as reader:
        for line in reader:
            list_str = line.split()
            if len(list_str) == 3:
                lst_1.append(float(list_str[0]))
                lst_2.append(float(list_str[1]))
                lst_3.append(float(list_str[2]))
    return lst_1, lst_2, lst_3
```

```python
x_s, y_s, errs = read_file('fitting_simple_data.txt')

b, m = weighted_fitting(x_s, y_s, errs)
#b = -10
#m = 6
print(b, m)
x_line = np.linspace(0, 12)
y_line = b + m*x_line

plt.figure(figsize=(15, 9))
plt.scatter(x_s, y_s)

plt.plot(x_line, y_line)
plt.show()

print(calc_chi(x_s, y_s, errs, b, m))
```

```python
def linear_interpolation(x_s, y_s, x_val):
    index = np.abs(x_s - x_val).argmin() #Index of x in x_s closest to x_val

    if x_s[index] > x_val:
        l_index = index - 1
        r_index = index
    else:
        l_index = index
        r_index = index + 1

    m = (y_s[r_index] - y_s[l_index])/(x_s[r_index] - x_s[l_index])
    b = y_s[r_index] - m*x_s[r_index]

    return b + m*x_val

def spectrum_likelihood(wave_obs, spec_obs, wave_theo, spec_theo):
    epsilon = 0

    for i in range(len(wave_obs)):
        interpol_flux = linear_interpolation(wave_theo, spec_theo, wave_obs[i])

        epsilon += ((spec_obs[i] - interpol_flux)**2)/(errs[i]**2)

    return epsilon
```

```python
wave_obs, spec_obs, errs = read_file('fitting_spectrum_1.txt')

plt.figure(figsize=(15, 9))
plt.loglog(wave_obs, spec_obs)
x_label = r"$\lambda$ ($\AA$)"
plt.xlabel(x_label, fontsize=14)
plt.xlim(10**3, 199600.0)
y_label = r"f$_v$  $(\mu Jy)$"
plt.ylabel(y_label, fontsize=14)
plt.ylim(10**-14, 10**-8)
plt.show()
```

```python
generate_SED(10, 1)
```

```python

```


```
from funciones_forma_lagrange import lagrangiana_1D, lagrangiana_2D, lagrangiana_3D
from funciones_forma_lagrange import recorrer_matriz_espiral, guardar_matriz_espiral
from funciones_forma_lagrange import recorrer_matriz_3d_espiral, guardar_matriz_3d_espiral
from sympy import Symbol, lambdify, latex
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from mayavi import mlab

x = Symbol('x')
y = Symbol('y')
z = Symbol('z')
```

# Generación rápida de funciones de forma bidimensionales y tridimensionales Lagrangianas

### Introducción

Las funciones de forma son usadas en el método de los elementos finitos, generalmente usan el sistema de coordenadas naturales $(\xi, \eta, \zeta)$.

Entre ellas están las funciones de forma Lagragianas, la fórmula para generarlos es la siguiente:

\begin{equation}
l^{(n)} = \prod_{i=0, i \neq j}^k \frac{x - x_{i}}{x_{j} - x_{i}}
\end{equation}

El dominio de las funciones de forma es $-1 \leqslant \xi \leqslant 1$ y el rango es $-1 \leqslant \eta \leqslant 1$.

```
import matplotlib.patches as mpatches

relleno = mpatches.Rectangle((-1, -1), 2, 2, color='#0099FF')

fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_patch(relleno)
plt.xlabel(r'$\xi$', fontsize=20)
plt.ylabel(r'$\eta$', fontsize=20)
plt.xlim(-1.5, 1.5)
plt.ylim(-1.5, 1.5)
plt.grid(True)
plt.show()
```

Debido a esto se llaman funciones de forma rectangulares.

### Notación

$l^{(1)}$ Función de forma unidimensional Lagrangiana

$l^{(2)}$ Función de forma bidimensional Lagrangiana

$l^{(3)}$ Función de forma trididimensional Lagrangiana

### Método

Las $l^{(2)}$ se generar a partir de $l^{(1)}$, multiplicando matrices:

\begin{equation}
\left [ l^{(1)} \right ] \left [ l^{(1)} \right ]^{T} = \left [ l^{(2)} \right ]
\end{equation}

En donde $\left [ l^{(1)} \right ]$ es una matriz columna con $j$-elementos, $\left [ l^{(1)} \right ]^{T}$ es una matriz fila con $j$-elementos y $\left [ l^{(2)} \right ]$ es una matriz cuadrada.

Si recorremos los elementos que forman parte de la matriz $\left [ l^{(2)} \right ]$, uno a uno desde $l^{(2)}_1$ hasta $l^{(2)}_n$ forman una espiral, si graficamos las funciones que forman la matriz en el orden anterior observamos que el punto $(\xi, \eta)$ en el que la función vale $1$ también hace un recorrido en espiral.

**Ejemplo 1**: Elemento rectangular de 4 nodos:

\begin{equation}
\left [
\begin{matrix}
l^{(1)}_1 \\\
l^{(1)}_2
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(1)}_1 & l^{(1)}_2
\end{matrix}
\right ] = \left [
\begin{matrix}
l^{(2)}_1 & l^{(2)}_4 \\\
l^{(2)}_2 & l^{(2)}_3
\end{matrix}
\right ]
\end{equation}

**Ejemplo 2**: Elemento rectangular de 9 nodos:

\begin{equation}
\left [
\begin{matrix}
l^{(1)}_1 \\\
l^{(1)}_2 \\\
l^{(1)}_3
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(1)}_1 & l^{(1)}_2 & l^{(1)}_3
\end{matrix}
\right ] = \left [
\begin{matrix}
l^{(2)}_1 & l^{(2)}_8 & l^{(2)}_7 \\\
l^{(2)}_2 & l^{(2)}_9 & l^{(2)}_6 \\\
l^{(2)}_3 & l^{(2)}_4 &  l^{(2)}_5
\end{matrix}
\right ]
\end{equation}

Las $l^{(3)}$ también se generar a partir de $l^{(1)}$, pero realizando una multiplicación con matrices 3D (ver **Matrices 3D**):

\begin{equation}
\left [ l^{(1)} \right ] \left [ l^{(1)} \right ]^{T} \left [ l^{(1)} \right ]^{T} = \left [ l^{(3)} \right ]
\end{equation}

En donde $\left [ l^{(1)} \right ]$ es una matriz columna con $j$-elementos, $\left [ l^{(1)} \right ]^{T}$ es una matriz fila con $j$-elementos y $\left [ l^{(3)} \right ]$ es una matriz 3D cúbica.

**Ejemplo 3**: Elemento hexaédrico de 8 nodos:

\begin{equation}
\left [
\begin{matrix}
l^{(1)}_1 \\\
l^{(1)}_2
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(1)}_1 & l^{(1)}_2
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(1)}_1 & l^{(1)}_2
\end{matrix}
\right ] = \left [
\left [
\begin{matrix}
l^{(3)}_1 & l^{(3)}_5 \\\
l^{(3)}_2 & l^{(3)}_6
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(3)}_4 & l^{(3)}_8 \\\
l^{(3)}_3 & l^{(3)}_7
\end{matrix}
\right ]
\right ]
\end{equation}

**Ejemplo 4**: Elemento hexaédrico de 27 nodos:

\begin{equation}
\left [
\begin{matrix}
l^{(1)}_1 \\\
l^{(1)}_2 \\\
l^{(1)}_3
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(1)}_1 & l^{(1)}_2 & l^{(1)}_3
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(1)}_1 & l^{(1)}_2 & l^{(1)}_3
\end{matrix}
\right ] = \left [
\left [
\begin{matrix}
l^{(3)}_1 & l^{(3)}_10 & l^{(3)}_19 \\\
l^{(3)}_2 & l^{(3)}_11 & l^{(3)}_20 \\\
l^{(3)}_3 & l^{(3)}_12 & l^{(3)}_21
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(3)}_8 & l^{(3)}_17 & l^{(3)}_26 \\\
l^{(3)}_9 & l^{(3)}_18 & l^{(3)}_27 \\\
l^{(3)}_4 & l^{(3)}_13 & l^{(3)}_22
\end{matrix}
\right ]
\left [
\begin{matrix}
l^{(3)}_7 & l^{(3)}_16 & l^{(3)}_25 \\\
l^{(3)}_6 & l^{(3)}_15 & l^{(3)}_24 \\\
l^{(3)}_5 & l^{(3)}_14 & l^{(3)}_23
\end{matrix}
\right ]
\right ]
\end{equation}

### Función de forma Lagrangiana unidimensional de 3 nodos

```
A1 = lagrangiana_1D(x, 3)

for i in range(len(A1)):
    print A1[i]

X1 = np.arange(-1, 1, 0.01)

for i in range(len(A1)):
    Y1 = lambdify(x, A1[i])
    plt.plot(X1, Y1(X1))
    plt.title('Funcion de forma de ' + str(i + 1) + ' nodos')
plt.show()
```

Las ecuaciones obtenidas son:

\begin{eqnarray}
l^{(1)}_1 &=& \frac{1}{2} \xi^{2} - \frac{1}{2} \xi \\\
l^{(1)}_2 &=& - \xi^{2} + 1 \\\
l^{(1)}_3 &=& \frac{1}{2} \xi^{2} + \frac{1}{2} \xi
\end{eqnarray}

### Función de forma rectangular Lagragiana de 9 nodos

```
A2 = lagrangiana_2D(x, y, 3)
recorrer_matriz_espiral(A2)
B2 = guardar_matriz_espiral(A2)

X2 = np.arange(-1, 1, 0.01)
Y2 = np.arange(-1, 1, 0.01)
X2, Y2 = np.meshgrid(X2, Y2)

for i in range(len(B2)):
    fig = plt.figure()
    Z2 = lambdify((x, y), B2[i])
    ax = fig.add_subplot(111, projection='3d')
    ax.plot_wireframe(X2, Y2, Z2(X2, Y2), rstride=10, cstride=10)
    ax.set_title('Funcion ' + str(i + 1))
plt.show()
```

Las ecuaciones obtenidas son:

\begin{eqnarray}
l^{(2)}_1 &=& \frac{1}{4} \xi^{2} \eta^{2} - \frac{1}{4} \xi^{2} \eta - \frac{1}{4} \xi \eta^{2} + \frac{1}{4} \xi \eta \\\
l^{(2)}_2 &=& - \frac{1}{2} \xi^{2} \eta^{2} + \frac{1}{2} \xi^{2} \eta + \frac{1}{2} \eta^{2} - \frac{1}{2} \eta \\\
l^{(2)}_3 &=& \frac{1}{4} \xi^{2} \eta^{2} - \frac{1}{4} \xi^{2} \eta + \frac{1}{4} \xi \eta^{2} - \frac{1}{4} \xi \eta \\\
l^{(2)}_4 &=& - \frac{1}{2} \xi^{2} \eta^{2} + \frac{1}{2} \xi^{2} - \frac{1}{2} \xi \eta^{2} + \frac{1}{2} \xi \\\
l^{(2)}_5 &=& \frac{1}{4} \xi^{2} \eta^{2} + \frac{1}{4} \xi^{2} \eta + \frac{1}{4} \xi \eta^{2} + \frac{1}{4} \xi \eta \\\
l^{(2)}_6 &=& - \frac{1}{2} \xi^{2} \eta^{2} - \frac{1}{2} \xi^{2} \eta + \frac{1}{2} \eta^{2} + \frac{1}{2} \eta \\\
l^{(2)}_7 &=& \frac{1}{4} \xi^{2} \eta^{2} + \frac{1}{4} \xi^{2} \eta - \frac{1}{4} \xi \eta^{2} - \frac{1}{4} \xi \eta \\\
l^{(2)}_8 &=& - \frac{1}{2} \xi^{2} \eta^{2} + \frac{1}{2} \xi^{2} + \frac{1}{2} \xi \eta^{2} - \frac{1}{2} \xi \\\
l^{(2)}_9 &=& \xi^{2} \eta^{2} - \xi^{2} - \eta^{2} + 1
\end{eqnarray}

### Función de forma hexaédrica Lagragiana de 27 nodos

```
A3 = lagrangiana_3D(x, y, z, 3)
recorrer_matriz_3d_espiral(A3)
B3 = guardar_matriz_3d_espiral(A3)

mlab.figure(size=(400, 400),bgcolor=(1, 1, 1))
mlab.clf()
xi, eta, zeta = np.ogrid[-1:1:100j, -1:1:100j, -1:1:100j]
s = 0.125*xi**2*eta**2*zeta**2 - 0.125*xi**2*eta**2*zeta - 0.125*xi**2*eta*zeta**2 + 0.125*xi**2*eta*zeta - 0.125*xi*eta**2*zeta**2 + 0.125*xi*eta**2*zeta + 0.125*xi*eta*zeta**2 - 0.125*xi*eta*zeta
p1 = mlab.contour3d(s, contours=40, transparent=True)
mlab.title('Funcion de forma hexaedrica l1', color=(0, 0, 0))

mlab.show()
```

    0.125*x**2*y**2*z**2 - 0.125*x**2*y**2*z - 0.125*x**2*y*z**2 + 0.125*x**2*y*z - 0.125*x*y**2*z**2 + 0.125*x*y**2*z + 0.125*x*y*z**2 - 0.125*x*y*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2*z + 0.25*x**2*y*z**2 - 0.25*x**2*y*z + 0.25*y**2*z**2 - 0.25*y**2*z - 0.25*y*z**2 + 0.25*y*z
    0.125*x**2*y**2*z**2 - 0.125*x**2*y**2*z - 0.125*x**2*y*z**2 + 0.125*x**2*y*z + 0.125*x*y**2*z**2 - 0.125*x*y**2*z - 0.125*x*y*z**2 + 0.125*x*y*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2 + 0.25*x**2*y*z**2 - 0.25*x**2*y - 0.25*x*y**2*z**2 + 0.25*x*y**2 + 0.25*x*y*z**2 - 0.25*x*y
    0.125*x**2*y**2*z**2 + 0.125*x**2*y**2*z - 0.125*x**2*y*z**2 - 0.125*x**2*y*z + 0.125*x*y**2*z**2 + 0.125*x*y**2*z - 0.125*x*y*z**2 - 0.125*x*y*z
    -0.25*x**2*y**2*z**2 - 0.25*x**2*y**2*z + 0.25*x**2*y*z**2 + 0.25*x**2*y*z + 0.25*y**2*z**2 + 0.25*y**2*z - 0.25*y*z**2 - 0.25*y*z
    0.125*x**2*y**2*z**2 + 0.125*x**2*y**2*z - 0.125*x**2*y*z**2 - 0.125*x**2*y*z - 0.125*x*y**2*z**2 - 0.125*x*y**2*z + 0.125*x*y*z**2 + 0.125*x*y*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2 + 0.25*x**2*y*z**2 - 0.25*x**2*y + 0.25*x*y**2*z**2 - 0.25*x*y**2 - 0.25*x*y*z**2 + 0.25*x*y
    0.5*x**2*y**2*z**2 - 0.5*x**2*y**2 - 0.5*x**2*y*z**2 + 0.5*x**2*y - 0.5*y**2*z**2 + 0.5*y**2 + 0.5*y*z**2 - 0.5*y
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2*z + 0.25*x**2*z**2 - 0.25*x**2*z + 0.25*x*y**2*z**2 - 0.25*x*y**2*z - 0.25*x*z**2 + 0.25*x*z
    0.5*x**2*y**2*z**2 - 0.5*x**2*y**2*z - 0.5*x**2*z**2 + 0.5*x**2*z - 0.5*y**2*z**2 + 0.5*y**2*z + 0.5*z**2 - 0.5*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2*z + 0.25*x**2*z**2 - 0.25*x**2*z - 0.25*x*y**2*z**2 + 0.25*x*y**2*z + 0.25*x*z**2 - 0.25*x*z
    0.5*x**2*y**2*z**2 - 0.5*x**2*y**2 - 0.5*x**2*z**2 + 0.5*x**2 + 0.5*x*y**2*z**2 - 0.5*x*y**2 - 0.5*x*z**2 + 0.5*x
    -0.25*x**2*y**2*z**2 - 0.25*x**2*y**2*z + 0.25*x**2*z**2 + 0.25*x**2*z - 0.25*x*y**2*z**2 - 0.25*x*y**2*z + 0.25*x*z**2 + 0.25*x*z
    0.5*x**2*y**2*z**2 + 0.5*x**2*y**2*z - 0.5*x**2*z**2 - 0.5*x**2*z - 0.5*y**2*z**2 - 0.5*y**2*z + 0.5*z**2 + 0.5*z
    -0.25*x**2*y**2*z**2 - 0.25*x**2*y**2*z + 0.25*x**2*z**2 + 0.25*x**2*z + 0.25*x*y**2*z**2 + 0.25*x*y**2*z - 0.25*x*z**2 - 0.25*x*z
    0.5*x**2*y**2*z**2 - 0.5*x**2*y**2 - 0.5*x**2*z**2 + 0.5*x**2 - 0.5*x*y**2*z**2 + 0.5*x*y**2 + 0.5*x*z**2 - 0.5*x
    -1.0*x**2*y**2*z**2 + 1.0*x**2*y**2 + 1.0*x**2*z**2 - 1.0*x**2 + 1.0*y**2*z**2 - 1.0*y**2 - 1.0*z**2 + 1.0
    0.125*x**2*y**2*z**2 - 0.125*x**2*y**2*z + 0.125*x**2*y*z**2 - 0.125*x**2*y*z - 0.125*x*y**2*z**2 + 0.125*x*y**2*z - 0.125*x*y*z**2 + 0.125*x*y*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2*z - 0.25*x**2*y*z**2 + 0.25*x**2*y*z + 0.25*y**2*z**2 - 0.25*y**2*z + 0.25*y*z**2 - 0.25*y*z
    0.125*x**2*y**2*z**2 - 0.125*x**2*y**2*z + 0.125*x**2*y*z**2 - 0.125*x**2*y*z + 0.125*x*y**2*z**2 - 0.125*x*y**2*z + 0.125*x*y*z**2 - 0.125*x*y*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2 - 0.25*x**2*y*z**2 + 0.25*x**2*y - 0.25*x*y**2*z**2 + 0.25*x*y**2 - 0.25*x*y*z**2 + 0.25*x*y
    0.125*x**2*y**2*z**2 + 0.125*x**2*y**2*z + 0.125*x**2*y*z**2 + 0.125*x**2*y*z + 0.125*x*y**2*z**2 + 0.125*x*y**2*z + 0.125*x*y*z**2 + 0.125*x*y*z
    -0.25*x**2*y**2*z**2 - 0.25*x**2*y**2*z - 0.25*x**2*y*z**2 - 0.25*x**2*y*z + 0.25*y**2*z**2 + 0.25*y**2*z + 0.25*y*z**2 + 0.25*y*z
    0.125*x**2*y**2*z**2 + 0.125*x**2*y**2*z + 0.125*x**2*y*z**2 + 0.125*x**2*y*z - 0.125*x*y**2*z**2 - 0.125*x*y**2*z - 0.125*x*y*z**2 - 0.125*x*y*z
    -0.25*x**2*y**2*z**2 + 0.25*x**2*y**2 - 0.25*x**2*y*z**2 + 0.25*x**2*y + 0.25*x*y**2*z**2 - 0.25*x*y**2 + 0.25*x*y*z**2 - 0.25*x*y
    0.5*x**2*y**2*z**2 - 0.5*x**2*y**2 + 0.5*x**2*y*z**2 - 0.5*x**2*y - 0.5*y**2*z**2 + 0.5*y**2 - 0.5*y*z**2 + 0.5*y

Las ecuaciones obtenidas son:

\begin{eqnarray}
l^{(3)}_1 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{8} \xi^{2} \eta^{2} \zeta - \frac{1}{8} \xi^{2} \eta \zeta^{2} + \frac{1}{8} \xi^{2} \eta \zeta - \frac{1}{8} \xi \eta^{2} \zeta^{2} + \frac{1}{8} \xi \eta^{2} \zeta + \frac{1}{8} \xi \eta \zeta^{2} - \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_2 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} \zeta + \frac{1}{4} \xi^{2} \eta \zeta^{2} - \frac{1}{4} \xi^{2} \eta \zeta + \frac{1}{4} \eta^{2} \zeta^{2} - \frac{1}{4} \eta^{2} \zeta - \frac{1}{4} \eta \zeta^{2} + \frac{1}{4} \eta \zeta \\\
l^{(3)}_3 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{8} \xi^{2} \eta^{2} \zeta - \frac{1}{8} \xi^{2} \eta \zeta^{2} + \frac{1}{8} \xi^{2} \eta \zeta + \frac{1}{8} \xi \eta^{2} \zeta^{2} - \frac{1}{8} \xi \eta^{2} \zeta - \frac{1}{8} \xi \eta \zeta^{2} + \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_12 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} + \frac{1}{4} \xi^{2} \eta \zeta^{2} - \frac{1}{4} \xi^{2} \eta - \frac{1}{4} \xi \eta^{2} \zeta^{2} + \frac{1}{4} \xi \eta^{2} + \frac{1}{4} \xi \eta \zeta^{2} - \frac{1}{4} \xi \eta \\\
l^{(3)}_21 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{8} \xi^{2} \eta^{2} \zeta - \frac{1}{8} \xi^{2} \eta \zeta^{2} - \frac{1}{8} \xi^{2} \eta \zeta + \frac{1}{8} \xi \eta^{2} \zeta^{2} + \frac{1}{8} \xi \eta^{2} \zeta - \frac{1}{8} \xi \eta \zeta^{2} - \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_20 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{4} \xi^{2} \eta^{2} \zeta + \frac{1}{4} \xi^{2} \eta \zeta^{2} + \frac{1}{4} \xi^{2} \eta \zeta + \frac{1}{4} \eta^{2} \zeta^{2} + \frac{1}{4} \eta^{2} \zeta - \frac{1}{4} \eta \zeta^{2} - \frac{1}{4} \eta \zeta \\\
l^{(3)}_19 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{8} \xi^{2} \eta^{2} \zeta - \frac{1}{8} \xi^{2} \eta \zeta^{2} - \frac{1}{8} \xi^{2} \eta \zeta - \frac{1}{8} \xi \eta^{2} \zeta^{2} - \frac{1}{8} \xi \eta^{2} \zeta + \frac{1}{8} \xi \eta \zeta^{2} + \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_10 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} + \frac{1}{4} \xi^{2} \eta \zeta^{2} - \frac{1}{4} \xi^{2} \eta + \frac{1}{4} \xi \eta^{2} \zeta^{2} - \frac{1}{4} \xi \eta^{2} - \frac{1}{4} \xi \eta \zeta^{2} + \frac{1}{4} \xi \eta \\\
l^{(3)}_11 &=& \frac{1}{2} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{2} \xi^{2} \eta^{2} - \frac{1}{2} \xi^{2} \eta \zeta^{2} + \frac{1}{2} \xi^{2} \eta - \frac{1}{2} \eta^{2} \zeta^{2} + \frac{1}{2} \eta^{2} + \frac{1}{2} \eta \zeta^{2} - \frac{1}{2} \eta \\\
l^{(3)}_8 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} \zeta + \frac{1}{4} \xi^{2} \zeta^{2} - \frac{1}{4} \xi^{2} \zeta + \frac{1}{4} \xi \eta^{2} \zeta^{2} - \frac{1}{4} \xi \eta^{2} \zeta - \frac{1}{4} \xi \zeta^{2} + \frac{1}{4} \xi \zeta \\\
l^{(3)}_9 &=& \frac{1}{2} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{2} \xi^{2} \eta^{2} \zeta - \frac{1}{2} \xi^{2} \zeta^{2} + \frac{1}{2} \xi^{2} \zeta - \frac{1}{2} \eta^{2} \zeta^{2} + \frac{1}{2} \eta^{2} \zeta + \frac{1}{2} \zeta^{2} - \frac{1}{2} \zeta \\\
l^{(3)}_13 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} \zeta + \frac{1}{4} \xi^{2} \zeta^{2} - \frac{1}{4} \xi^{2} \zeta - \frac{1}{4} \xi \eta^{2} \zeta^{2} + \frac{1}{4} \xi \eta^{2} \zeta + \frac{1}{4} \xi \zeta^{2} - \frac{1}{4} \xi \zeta \\\
l^{(3)}_22 &=& \frac{1}{2} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{2} \xi^{2} \eta^{2} - \frac{1}{2} \xi^{2} \zeta^{2} + \frac{1}{2} \xi^{2} + \frac{1}{2} \xi \eta^{2} \zeta^{2} - \frac{1}{2} \xi \eta^{2} - \frac{1}{2} \xi \zeta^{2} + \frac{1}{2} \xi \\\
l^{(3)}_27 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{4} \xi^{2} \eta^{2} \zeta + \frac{1}{4} \xi^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \zeta - \frac{1}{4} \xi \eta^{2} \zeta^{2} - \frac{1}{4} \xi \eta^{2} \zeta + \frac{1}{4} \xi \zeta^{2} + \frac{1}{4} \xi \zeta \\\
l^{(3)}_26 &=& \frac{1}{2} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{2} \xi^{2} \eta^{2} \zeta - \frac{1}{2} \xi^{2} \zeta^{2} - \frac{1}{2} \xi^{2} \zeta - \frac{1}{2} \eta^{2} \zeta^{2} - \frac{1}{2} \eta^{2} \zeta + \frac{1}{2} \zeta^{2} + \frac{1}{2} \zeta \\\
l^{(3)}_17 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{4} \xi^{2} \eta^{2} \zeta + \frac{1}{4} \xi^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \zeta + \frac{1}{4} \xi \eta^{2} \zeta^{2} + \frac{1}{4} \xi \eta^{2} \zeta - \frac{1}{4} \xi \zeta^{2} - \frac{1}{4} \xi \zeta \\\
l^{(3)}_18 &=& \frac{1}{2} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{2} \xi^{2} \eta^{2} - \frac{1}{2} \xi^{2} \zeta^{2} + \frac{1}{2} \xi^{2} - \frac{1}{2} \xi \eta^{2} \zeta^{2} + \frac{1}{2} \xi \eta^{2} + \frac{1}{2} \xi \zeta^{2} - \frac{1}{2} \xi \\\
l^{(3)}_14 &=& - \xi^{2} \eta^{2} \zeta^{2} + \xi^{2} \eta^{2} + \xi^{2} \zeta^{2} - \xi^{2} + \eta^{2} \zeta^{2} - \eta^{2} - \zeta^{2} + 1 \\\
l^{(3)}_7 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{8} \xi^{2} \eta^{2} \zeta + \frac{1}{8} \xi^{2} \eta \zeta^{2} - \frac{1}{8} \xi^{2} \eta \zeta - \frac{1}{8} \xi \eta^{2} \zeta^{2} + \frac{1}{8} \xi \eta^{2} \zeta - \frac{1}{8} \xi \eta \zeta^{2} + \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_6 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} \zeta - \frac{1}{4} \xi^{2} \eta \zeta^{2} + \frac{1}{4} \xi^{2} \eta \zeta + \frac{1}{4} \eta^{2} \zeta^{2} - \frac{1}{4} \eta^{2} \zeta + \frac{1}{4} \eta \zeta^{2} - \frac{1}{4} \eta \zeta \\\
l^{(3)}_5 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{8} \xi^{2} \eta^{2} \zeta + \frac{1}{8} \xi^{2} \eta \zeta^{2} - \frac{1}{8} \xi^{2} \eta \zeta + \frac{1}{8} \xi \eta^{2} \zeta^{2} - \frac{1}{8} \xi \eta^{2} \zeta + \frac{1}{8} \xi \eta \zeta^{2} - \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_14 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} - \frac{1}{4} \xi^{2} \eta \zeta^{2} + \frac{1}{4} \xi^{2} \eta - \frac{1}{4} \xi \eta^{2} \zeta^{2} + \frac{1}{4} \xi \eta^{2} - \frac{1}{4} \xi \eta \zeta^{2} + \frac{1}{4} \xi \eta \\\
l^{(3)}_23 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{8} \xi^{2} \eta^{2} \zeta + \frac{1}{8} \xi^{2} \eta \zeta^{2} + \frac{1}{8} \xi^{2} \eta \zeta + \frac{1}{8} \xi \eta^{2} \zeta^{2} + \frac{1}{8} \xi \eta^{2} \zeta + \frac{1}{8} \xi \eta \zeta^{2} + \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_24 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{4} \xi^{2} \eta^{2} \zeta - \frac{1}{4} \xi^{2} \eta \zeta^{2} - \frac{1}{4} \xi^{2} \eta \zeta + \frac{1}{4} \eta^{2} \zeta^{2} + \frac{1}{4} \eta^{2} \zeta + \frac{1}{4} \eta \zeta^{2} + \frac{1}{4} \eta \zeta \\\
l^{(3)}_25 &=& \frac{1}{8} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{8} \xi^{2} \eta^{2} \zeta + \frac{1}{8} \xi^{2} \eta \zeta^{2} + \frac{1}{8} \xi^{2} \eta \zeta - \frac{1}{8} \xi \eta^{2} \zeta^{2} - \frac{1}{8} \xi \eta^{2} \zeta - \frac{1}{8} \xi \eta \zeta^{2} - \frac{1}{8} \xi \eta \zeta \\\
l^{(3)}_16 &=& - \frac{1}{4} \xi^{2} \eta^{2} \zeta^{2} + \frac{1}{4} \xi^{2} \eta^{2} - \frac{1}{4} \xi^{2} \eta \zeta^{2} + \frac{1}{4} \xi^{2} \eta + \frac{1}{4} \xi \eta^{2} \zeta^{2} - \frac{1}{4} \xi \eta^{2} + \frac{1}{4} \xi \eta \zeta^{2} - \frac{1}{4} \xi \eta \\\
l^{(3)}_15 &=& \frac{1}{2} \xi^{2} \eta^{2} \zeta^{2} - \frac{1}{2} \xi^{2} \eta^{2} + \frac{1}{2} \xi^{2} \eta \zeta^{2} - \frac{1}{2} \xi^{2} \eta - \frac{1}{2} \eta^{2} \zeta^{2} + \frac{1}{2} \eta^{2} - \frac{1}{2} \eta \zeta^{2} + \frac{1}{2} \eta
\end{eqnarray}

```

```


# Multiple Features

```python
import pandas as pd
import numpy as np
```

```python
size =[2104,1416,1534,852]
nbr_bedrooms = [5,3,3,2]
nbr_floors = [1,2,2,1]
age = [45,40,30,36]
price = [460,232,315,178]
```

```python
d = {'size':size,'nbr_bedrooms':nbr_bedrooms,'nbr_floors':nbr_floors,'age':age,'price':price}
```

```python
df = pd.DataFrame(d)
```

## 1. Definitions

```python
df
```

      size
      nbr_bedrooms
      nbr_floors
      age
      price

      0
      2104
      5
      1
      45
      460

      1
      1416
      3
      2
      40
      232

      2
      1534
      3
      2
      30
      315

      3
      852
      2
      1
      36
      178

$y$ : the element to predict here is the **price**

$x_n$ : the features used to predict y **(size, nbr of bedrooms, nbr of floors, age )**

$n$ : number of features. here $n = 4$

$x^{(i)} : $ input of $i^{th}$ training example

* **ex:** $x^{(2)}$, with $x^{(2)}$ being a vector

```python
#(pandas is indexed at 0)
df.loc[1,['size','nbr_bedrooms','nbr_floors','age']]
```

    size            1416
    nbr_bedrooms       3
    nbr_floors         2
    age               40
    Name: 1, dtype: int64

$x^{(i)}_j$ : value of feature $j$ in $i^{th}$ training example
* **ex:** $x^{(2)}_3$

```python
#(pandas is indexed at 0)
df.iloc[2][2]
```

    2

## 2. Hypothesis

**General rule**

$h_\theta(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 + ... + \theta_nx_n$

**In our case**

$h_\theta(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 + \theta_3x_3 + \theta_4x_4$

### Writing the hypothesis formula in term on Matrices

with $x_0 = 1$. which gives $\theta_0x_0 = \theta_0$

Features vector as $x$

Parameters vector as $\theta$

$ x = \begin{bmatrix}x_0 \\x_1 \\x_2 \\x_3 \\x_4 \end{bmatrix}$     $\space \space \space$ $\theta = \begin{bmatrix} \theta_0 \\ \theta_1 \\\theta_2 \\ \theta_3 \\ \theta_4 \end{bmatrix}$

To end up with our hypothesis formula, we can think of it as the product of 2 vectors. $x$ and the transpose of $\theta$

$ x = \begin{bmatrix}x_0 \\x_1 \\x_2 \\x_3 \\x_4 \end{bmatrix}$     $\space \space \space$ $\theta^T = \begin{bmatrix} \theta_0 & \theta_1 &\theta_2 & \theta_3 & \theta_4 \end{bmatrix}$

So we can resume the formula to :
$h_\theta(x) = h_\theta(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 + ... + \theta_nx_n = \theta^Tx$

### Hypothesis computing in Python

```python
from sympy import *
```

```python
X = Matrix(['x0','x1','x2','x3','x4'])
X
```

$\displaystyle \left[\begin{matrix}x_{0}\\x_{1}\\x_{2}\\x_{3}\\x_{4}\end{matrix}\right]$

```python
T = Matrix([['T0','T1','T2','T3','T4']])
T
```

$\displaystyle \left[\begin{matrix}T_{0} & T_{1} & T_{2} & T_{3} & T_{4}\end{matrix}\right]$

```python
H = T.multiply(X)
H
```

$\displaystyle \left[\begin{matrix}T_{0} x_{0} + T_{1} x_{1} + T_{2} x_{2} + T_{3} x_{3} + T_{4} x_{4}\end{matrix}\right]$

```python

```

Andrew Ng - Coursera/Week 2/Multiple Features Regression.ipynb


# Vector CoordSys3D
> ### gradient

```python
import sympy as sym
import sympy.vector
sym.vector.CoordSys3D('')
```

$\displaystyle CoordSys3D\left(, \left( \left[\begin{matrix}1 & 0 & 0\\0 & 1 & 0\\0 & 0 & 1\end{matrix}\right], \  \mathbf{\hat{0}}\right)\right)$

```python
C = sym.vector.CoordSys3D('')
f = C.x * sym.sin(C.y)
f
```

$\displaystyle \mathbf{{x}_{}} \sin{\left(\mathbf{{y}_{}} \right)}$

```python
sym.vector.gradient(f)
```

$\displaystyle (\sin{\left(\mathbf{{y}_{}} \right)})\mathbf{\hat{i}_{}} + (\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)})\mathbf{\hat{j}_{}}$

```python
sym.vector.gradient(f).to_matrix(C)
```

$\displaystyle \left[\begin{matrix}\sin{\left(\mathbf{{y}_{}} \right)}\\\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\\0\end{matrix}\right]$

```python
sym.vector.gradient(f).subs(C.y, sym.pi)
```

$\displaystyle (- \mathbf{{x}_{}})\mathbf{\hat{j}_{}}$

```python
sym.vector.gradient(f).to_matrix(C).subs(C.y,sym.pi)
```

$\displaystyle \left[\begin{matrix}0\\- \mathbf{{x}_{}}\\0\end{matrix}\right]$

## Directional derivative vector u

```python
u = 3 * C.i + 4 * C.j + 5 * C.k
u
```

$\displaystyle (3)\mathbf{\hat{i}_{}} + (4)\mathbf{\hat{j}_{}} + (5)\mathbf{\hat{k}_{}}$

```python
u.normalize()
```

$\displaystyle (\frac{3 \sqrt{2}}{10})\mathbf{\hat{i}_{}} + (\frac{2 \sqrt{2}}{5})\mathbf{\hat{j}_{}} + (\frac{\sqrt{2}}{2})\mathbf{\hat{k}_{}}$

```python
f = C.i + 3*C.j + C.k
g = C.x * sym.cos(C.y)
u = 3* C.i + 4* C.j + C.k
sym.vector.dot(f,u)
```

$\displaystyle 16$

```python
sym.vector.dot(g,u)
```

$\displaystyle 3 \left(\mathbf{\hat{i}_{}} \cdot \left(\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\right)\right) + 4 \left(\mathbf{\hat{j}_{}} \cdot \left(\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\right)\right) + \mathbf{\hat{k}_{}} \cdot \left(\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\right)$

```python
sym.vector.dot(f,g)
```

$\displaystyle \mathbf{\hat{i}_{}} \cdot \left(\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\right) + 3 \left(\mathbf{\hat{j}_{}} \cdot \left(\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\right)\right) + \mathbf{\hat{k}_{}} \cdot \left(\mathbf{{x}_{}} \cos{\left(\mathbf{{y}_{}} \right)}\right)$

```python
sym.vector.gradient(f).to_matrix(C)
```

$\displaystyle \left[\begin{matrix}0\\0\\0\end{matrix}\right]$

```python
sym.vector.gradient(g).to_matrix(C)
```

$\displaystyle \left[\begin{matrix}\cos{\left(\mathbf{{y}_{}} \right)}\\- \mathbf{{x}_{}} \sin{\left(\mathbf{{y}_{}} \right)}\\0\end{matrix}\right]$

```python
sym.vector.gradient(g).dot(u)
```

$\displaystyle - 4 \mathbf{{x}_{}} \sin{\left(\mathbf{{y}_{}} \right)} + 3 \cos{\left(\mathbf{{y}_{}} \right)}$

```python
sym.vector.gradient(g).dot(u).subs([(C.x,1),(C.y,sym.pi)])
```

$\displaystyle -3$

# [Extreme Values and Saddle Points](https://www.youtube.com/watch?v=Teb28OFMVFc)
Extream values of f(x,y) can occur at
> Boundary points of the domain of f
> Critical Points($f_x = f_y = 0$)
> if
>> $ f_{xx} \lt 0 $ and $ f_{xx}f_{yy} - f{xy}^2 \gt 0 $ at (a,b) then local maximum
>> $ f_{xx} \gt 0 $ and $ f_{xx}f_{yy} - f{xy}^2 \gt 0 $ at (a,b) then local minimum
>> $ f_{xx}f_{yy} - f_{xy}^2 \lt 0$ at (a,b) then saddle point
>> $ f_{xx}f_{yy} - f_{xy}^2 = 0$ at (a,b) then inconclusive

```python
x = sym.symbols('x')
f = sym.symbols('f',cls=sym.Function)
f = C.x**3 + 3*C.x*C.y + C.y**3
sym.diff(f,C.x,C.x)
sym.vector.gradient(f)
```

$\displaystyle (3 \mathbf{{x}_{}}^{2} + 3 \mathbf{{y}_{}})\mathbf{\hat{i}_{}} + (3 \mathbf{{x}_{}} + 3 \mathbf{{y}_{}}^{2})\mathbf{\hat{j}_{}}$

```python
sym.diff(f,C.y,C.y)
```

$\displaystyle 6 x_1$

```python
sym.solve([sym.diff(f,C.x),sym.diff(f,C.y)])
```

    [{.x: -1, .y: -1},
     {.x: 0, .y: 0},
     {.x: -(1/2 - sqrt(3)*I/2)**2, .y: 1/2 - sqrt(3)*I/2},
     {.x: -(1/2 + sqrt(3)*I/2)**2, .y: 1/2 + sqrt(3)*I/2}]

```python
fxx = sym.diff(f,C.x,C.x)
fyy = sym.diff(f,C.y,C.y)
fxy = sym.diff(f,C.x,C.y)
fxx.subs([(C.x,-1),(C.y,-1)])
```

$\displaystyle -6$

```python
(fxx*fyy - fxy**2).subs([(C.x,-1),(C.y,-1)])
# therefore at(-1,-1) lcoal minimum
```

$\displaystyle 27$

# Lagrange Mulipliers
## [let find a point(a,b,c) on g(x,y,z) from Min or Max value](https://www.youtube.com/watch?v=lmD9p6J_-TA)
> Minimize $f(x,y,z)$ subject to the constraint $g(x,y,z)=0,  g(a,b,c) = 0$
>> Requires solving the two euqations $\nabla f = \lambda \nabla \text{ and } g(x,y,z) = 0 $
>> why $\lambda$:
>>> $\because$ each diffenrence Magnitude of gradient

> Example:
>> A space probe has the shape of an elipsoid $4x^2 + y^2 + 4z^2 = 16$ and after setting in the sun for an hour. the temperture on its surface is given by $`T(x,y,z) = 8x^2 + 4yz - 16z + 600$. Find the hottest point on the surfae
>
>>Note:
>>> that $ g = 4x^2 + y^2 + 4z^2 - 16 = 0 $ and $ f = T = 8x^2 + 4yz - 16z + 600 $ here

```python
C = sym.vector.CoordSys3D('')
```

```python
lam = sym.symbols('\lambda')
g = 4*C.x**2 + C.y**2 + 4*C.z**2 - 16
T = 8*C.x**2 + 4*C.y*C.z - 16*C.z + 600
print(f'Df: {sym.vector.gradient(T)} \nDg:{sym.vector.gradient(g)}')
```

    Df: 16*.x*.i + 4*.z*.j + (4*.y - 16)*.k
    Dg:8*.x*.i + 2*.y*.j + 8*.z*.k

```python
eq1 = sym.vector.gradient(T) - lam*sym.vector.gradient(g)
eq1 = eq1.to_matrix(C)
s1 = sym.solve(eq1)
s1
```

    [{.x: 0,
      .y: -4/((\lambda - 1)*(\lambda + 1)),
      .z: -2*\lambda/((\lambda - 1)*(\lambda + 1))},
     {.y: -4/3, .z: -4/3, \lambda: 2}]

```python
s2 = sym.solve(g)
print(g)
s2
```

    4*.x**2 + .y**2 + 4*.z**2 - 16

    [{.x: -sqrt(-.y**2 - 4*.z**2 + 16)/2}, {.x: sqrt(-.y**2 - 4*.z**2 + 16)/2}]

```python
sol = sym.solve([(sym.vector.gradient(T) - lam*sym.vector.gradient(g)).to_matrix(C), g])
sol[0]
g.subs(sol[0])
T.subs(sol[0])
```

$\displaystyle \frac{1928}{3}$

```python
for s in sol:
    print(T.subs(s))
```

    1928/3
    24*sqrt(3) + 600
    600 - 24*sqrt(3)
    600
    1928/3

# MUltiple Integrals
in rare cases these can be done symbolically
> $$ \int_0^1 \int_0^{1-x^2} \int_3^{4-x^2-y^2} xe^{-y}cos(z)dzdydx $$

```python
import scipy as sci
import scipy.integrate
import numpy as np

sci.integrate.tplquad(lambda x,y,z:x*np.exp(-y)*np.cos(z), 0,1,lambda x:0, lambda x:1-x**2, lambda x,y:3, lambda x,y:4-x**2-y**2)

```

    (0.8652852789233809, 3.877301196381849e-14)

# Integrals and vector Fields
> Work
>> $ W = \vec{F} \cdot \vec{c}$
>> $ d\,W = \vec{F} \cdot d\vec{r}$
>> $ d\,W = \vec{U} \cdot d\,\vec{r}$
>>> d : diferential  of $\vec{r}$
>>> $\vec{r}$ : position vector
>>> u : Potential Energy

## Line Inregrals(Scalar)
> Given $\vec{r(t)} = (g(t),h(t),k(t))$ the line integral $f(x,y,z)$ along the curve is
$$ \int_C f(x,y,z)dL = \int_a^b f(g(t),h(t),k(t)) |\frac{\vec{dl}}{dt}| dt $$

```python
t = sym.symbols('t')
x,y,z,f = sym.symbols('x y z f',cls=sym.Function,real=True)
x = x(t)
y = y(t)
z = z(t)
r = sym.Matrix([x,y,z])
f = f(x,y,z)
```

```python
sym.diff(r,t).norm()
sym.diff(r,t)
```

$\displaystyle \left[\begin{matrix}\frac{d}{d t} x{\left(t \right)}\\\frac{d}{d t} y{\left(t \right)}\\\frac{d}{d t} z{\left(t \right)}\end{matrix}\right]$

```python
f*sym.diff(r,t).norm()
```

$\displaystyle \sqrt{\left|{\frac{d}{d t} x{\left(t \right)}}\right|^{2} + \left|{\frac{d}{d t} y{\left(t \right)}}\right|^{2} + \left|{\frac{d}{d t} z{\left(t \right)}}\right|^{2}} f{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)}$

### 1. suppose
$
\vec{r(t)} = < cos(t), sin(t), t > \\
f(x,y,z) = 2xy + \sqrt{z}
$

```python
integrand = (
    f*sym.diff(r,t).norm()).subs(
        [(f,2*x*y+sym.sqrt(z)),
         (x,sym.cos(t)),
         (y,sym.sin(t)),(z,t)
        ])

integrand1=integrand.doit().simplify()

sym.integrate(integrand1,(t,0,2*sym.pi))
```

$\displaystyle \frac{8 \pi^{\frac{3}{2}}}{3}$

## 2. suppose
$
\vec{r(t)} = < 3cos(t), 2sin(t), e^{t/4} > \\
f(x,y,z) = 2xy + \sqrt{z}
$

```python
integrand2=(f*sym.diff(r,t).norm()).subs(
    [
        (f,2*x*y+sym.sqrt(z)),(x,3*sym.cos(t)),(y,sym.sin(t)),(z,sym.exp(t/4))
    ]
).doit().simplify()
sym.integrate(integrand2,(t,0,2*sym.pi))
```

$\displaystyle \frac{\int\limits_{0}^{2 \pi} 3 \sqrt{e^{\frac{t}{2}} + 144 \sin^{2}{\left(t \right)} + 16 \cos^{2}{\left(t \right)}} \sin{\left(2 t \right)}\, dt}{4} + \frac{\int\limits_{0}^{2 \pi} \sqrt{e^{\frac{t}{2}} + 144 \sin^{2}{\left(t \right)} + 16 \cos^{2}{\left(t \right)}} e^{\frac{t}{8}}\, dt}{4}$

```python
integrate_num = sym.lambdify([t],integrand2)
sci.integrate.quad(integrate_num,0,2*np.pi)
```

    (21.05530045675979, 3.960606771059637e-08)

# Line Integrals (Vector)
> Given $ \vec{r(t)} = < g(t),h(t),k(t) > $
the line integral of $ \vec{F}(x,y,z,) $  along the curve is
$$
\int_c \vec{F}(x,y,z)\cdot d\,\vec{r} = \int_a^b \vec{F}(g(t),h(t),k(t))\cdot \frac{d\,\vec{r}}{dt}dt
$$

```python
F1,F2,F3 = sym.symbols('F_1 F_2 F_3',cls=sym.Function, real=True)
F1 = F1(x,y,z)
F2 = F2(x,y,z)
F3 = F3(x,y,z)
r = sym.Matrix([x,y,z])
F = sym.Matrix([F1,F2,F3])
F
```

$\displaystyle \left[\begin{matrix}\operatorname{F_{1}}{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)}\\\operatorname{F_{2}}{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)}\\\operatorname{F_{3}}{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)}\end{matrix}\right]$

```python
F.dot(sym.diff(r,t))
```

$\displaystyle \operatorname{F_{1}}{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)} \frac{d}{d t} x{\left(t \right)} + \operatorname{F_{2}}{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)} \frac{d}{d t} y{\left(t \right)} + \operatorname{F_{3}}{\left(x{\left(t \right)},y{\left(t \right)},z{\left(t \right)} \right)} \frac{d}{d t} z{\left(t \right)}$

# 1. suppose
$
\vec{F} = \langle \sqrt{z} - 2x , \sqrt{y} \rangle \text{ and } \vec{r(t)} = \langle t, r^2,r^4 \rangle \text{ from } t = 0 \text{ to } t = 1
$

```python
integrand1 = (F.dot(sym.diff(r,t))).subs(
    [(F1,sym.sqrt(z)),(F2,-2*x),(F3,sym.sqrt(y)),(x,t),(y,t**2),(z,t**4)]
).doit().simplify()
```

```python
sym.integrate(integrand1,(t,0,1))
```

$\displaystyle - \frac{1}{5}$

```python
integrand1
```

$\displaystyle 4 t^{3} \sqrt{t^{2}} - 4 t^{2} + \sqrt{t^{4}}$

```python

```


# Derivatives, Partial Derivatives and Gradient Descent

## Learning Objectives

By the end of this session you should be able to...

1. Compute the derivative of a function of one variable
2. Compute the partial derivatives of a function of many variables
3. Use the method of gradient descent to find the minimum of a function of one variable

## Motivation: Why are derivatives, partial derivatives and the method of gradient descent important? What do these things have to do with Data Science?
Derivatives and partial derivatives are used to implement the method of gradient descent.

Gradient Descent is
* A method for finding the minimum of a function
* Used to train logistic regression and other models in machine learning
* The key to training deep learning models, i.e. neural networks.

## 1. Introduction to Derivatives

The process of finding a derivative is called **Differentiation**, which is a technique used to calculate the *slope* of the *tangent line* of a function $y = f(x)$ at any point $x$. The derivative tells us how rapidly, and in what direction $y$ changes with respect to $x$

### 1A. Activity - Derivative Tutorial:

1. Go through this [Derivative tutorial from Math Is Fun](https://www.mathsisfun.com/calculus/derivatives-introduction.html) (15 min)
1. When you're done, talk with a partner about topics you still have questions on. See if you can answer each other's questions. (5 min)
1. We'll then go over questions on the tutorial as a class (10 min)

### 1B. Review Diagram

Review the below diagram as a class, and compare with what you just learned in the above `Derivative Tutorial`.

## 2. The Derivative Formula

$f^\prime(x) \equiv \frac{d}{dx}f(x) \equiv \frac{\Delta y}{\Delta x}  = \frac{f(x + \Delta x) - f(x)}{\Delta x}$

The symbol $\equiv$ is read as "is defined as".

The above formula is meant to be evaluated in the limit as $\Delta x$ approaches 0.

So we should choose a very small number for $\Delta x$, say 1.e-10

### 2A. Activity: Write a Python function to compute the gradient of any one-dimensional function

* NOTE: foe one dimensional function, the *gradient* is the same thing as the *derivative*

* Use the derivative formula, above

* Use your function to evaluate the gradient of $f(x)=x^2$at $x = 3$ and $x = -2$

```python
# define function we want to compute the gradient of
def f(x):
    return x**2

# function that computes the gradient of a one dimensional function, evaluated at a given position
def gradient(func,x):
    '''Inputs:
            func, the function to take the derivative of
            x, the position (value of x) where the derivative is to be evaluated
        Output:
            the gradient of func(x) evaluated at position x

    '''
    delta_x = 1e-6
    return (f(x + delta_x) - f(x)) / delta_x

x = 3
print(f'Gradient at x = {x} is {gradient(func=f,x=x):.3}')
x = -2
print(f'Gradient at x = {x} is {gradient(func=f,x=x):.3}')
```

    Gradient at x = 3 is 6.0
    Gradient at x = -2 is -4.0

Note that these values match the derivative that we compute from the calculus formula $\frac{d}{dx}x^n = nx^{n-1}$,
which is $\frac{d}{dx}x^2 = 2x^{2-1} = 2x^1 = 2x$

$2*3 = 6$

$2 * -2 = -4$

### 2B. Table of Derivatives of Common Functions

Computing derivatives of more complicated functions than $x^n$ is a source of much toil and busy work (not necessarily a bad thing) in introductory calculus classes. But now that you understand what a derivative is, it's not cheating to take a shortcut! The second page of this PDF lists formulas for derivatives of common functions. The first page summarizes the rules for computing derivatives. Utilize as a resource going forward!

- https://www.qc.edu.hk/math/Resource/AL/Derivative%20Table.pdf

## 3. Partial Derivatives of Functions of Two or More Variables

Now we know how to calculate a derivative for a function of one variable. But what if we have a function of $N$ variables where $N>1$?

We calculate the *partial derivatives* of the function.
Calculating the partial derivatives for a function of $N$ variables consists of calculating $N$ ordinary derivatives: one for each variable, where the other variables are held constant.

### 3A. Activity - Partial Derivative Video

Let's watch this video introduction to Partial Derivatives from **Khan Academy**: https://youtu.be/AXqhWeUEtQU

**Note:** Here are some derivative shortcuts that will help in the video:

$\frac{d}{dx}x^2 = 2x$

$\frac{d}{x}sin(x) = cos(x)$

$\frac{d}{dx}x = 1$

### 3B. The Symbolic Python library `sympy` computes derivatives and partial derivatives of functions
You can install this library from the command line via `conda install sympy` in an Anaconda shell.

```python
from sympy import symbols, diff
# initialize x and y to be symbols to use in a function
x, y = symbols('x y', real=True)
f = (x**2)/y
# Find the partial derivatives of x and y
partial_fx = diff(f, x, evaluate=True) # partial derivative of f(x,y) with respect to x
partial_fy = diff(f, y, evaluate=True) # partial derivative of f(x,y) with respect to y
print(f'The partial derivative of f(x,y) w.r.t. (with respect to) x is {partial_fx}')
print(f'The partial derivative of f(x,y) w.r.t. x is {partial_fy}')
print(f'The partial derivative of f(x,y) w.r.t. x evaluated at the point (2,1) is {partial_fx.evalf(subs={x: 2, y: 1}):.2}')
print(f'The partial derivative of f(x,y) w.r.t. y evaluated at the point (2,1) is {partial_fy.evalf(subs={x: 2, y: 1}):.2}')
```

    The partial derivative of f(x,y) w.r.t. (with respect to) x is 2*x/y
    The partial derivative of f(x,y) w.r.t. x is -x**2/y**2
    The partial derivative of f(x,y) w.r.t. x evaluated at the point (2,1) is 4.0
    The partial derivative of f(x,y) w.r.t. y evaluated at the point (2,1) is -4.0

## 4. Calculating a Function's Partial Derivatives and Gradient

### 4A. Example: calculate the partial derivatives for a function of two variables

```python
def f(x, y):
    return x**2/y

eps = 1e-6
x = 2
y = 1
print((f(x + eps, y) - f(x, y)) / eps)
print((f(x, y + eps) - f(x, y)) / eps)
```

    4.0000010006480125
    -3.9999959997594203

Looks about right! This works rather well, but it is just an approximation. Also, you need to call `f()` at least once per parameter (not twice, since we could compute `f(x, y)` just once). This makes this approach difficult to control for large systems (for example neural networks).

### 4B. Activity: compute the gradient of a function of two variables
Write a Python function that computes the *gradient* of any *two-dimensional* function $f(x,y)$ by **completing the code in the block below**

* The *gradient* of a function is the vector composed of all the function's *partial derivatives*

* The gradient operation is denoted by the *nabla* symbol $\nabla$
* The *gradient* of a two-dimensional function is thus
    $\nabla f(x,y) = \left(\frac {\partial{f(x,y)}}{\partial{x}}, \frac{\partial{f(x,y)}}{\partial{y}}\right)$
* Your `gradient()` function should calculate partial derivatives using the derivative formula from section **2**

* Use your `gradient()` function to evaluate the gradient of the function $f(x, y) = \frac{x^2}{y}$ at the point $(2,1)$

```python
# define the 2-dimensional function for which you want to compute the gradient
def f_xy(x,y):
    # your code here
    return

# function to compute gradient of any two-dimensional function
def gradient(func, x, y):
    '''
    inputs:
        func: a continuous function of two variables
        x, y: the values of the two variables at the point
            where the gradient is to be computed
    outputs:
        partial_x, partial_y: the partial derivatives of the input function,
            evaluated at specified point (x,y)

    '''
    delta = 1.e-10
    partial_x = None # your code here
    partial_y = None # your code here

    return partial_x, partial_y

# output
print(f'The gradient of f(x,y) = x**2/y at the point (2,1) is {gradient(func=f_xy, x=2, y=1)}')
```

    The gradient of f(x,y) = x**2/y at the point (2,1) is (None, None)

## 5. The Heart of the Matter: the Method of Gradient Descent

In many machine learning and deep learning applications, we need to find the minimum of a "cost function" (sometimes also called an "error function" or a "loss function").
Whatever we want to call it, the cost function measures the error of a model on some training data when we compare the predicted *targets* against the true *targets*.

The *targets* are what the model is trying to predict. Examples are **numbers** -- like housing prices, or **class labels** -- like "dog", "cat" or "mouse")

Why do we want to find the minimum for a cost function? Given that a cost function measures how wrong our model is, we want to minimize that error!

In Machine Learning cost functions measure how badly our models are performing. We want to adjust the parameters of the model (also known as **weights**) to minimize our cost function, therefore minimizing error of the model!

We find find the optimal weights that minimize the cost function by using the **Method of Gradient Descent**. The Method of Gradient Descent repeatedly makes small adjustments to the weights that make the cost function smaller, until the cost function reaches a minimum. We'll learn more about models in future lessons, but for now, let's review the Gradient Descent process with the below images, given a cost function $J(w_0,w_1)$ that is a function of weights $w_0$ and $w_1$:

Look at that bottom right image above. Looks like we're using partial derivatives to determine the optimal values of the weights $w_0$ and $w_1$ that minimize the cost function $J(w_0,w_1)$. And we know exactly how to do that!

### 5A. Finding the minimum of a function via Gradient Descent

Assume we want to minimize the function $J$ which has two weights $w_0$ and $w_1$

We have two options to find the minimum of $J(w_0, w_1)$:

1. Take partial derivatives of $J(w_0, w_1)$ with relation to $w_0$ and $w_1$:

$\frac{\partial J(w_0, w_1)}{\partial w_0}$

$\frac{\partial J(w_0, w_1)}{\partial w_1}$

And find the appropriate weights such that the partial derivatives equal 0:

$\frac{\partial J(w_0, w_1)}{\partial w_0} = 0$

$\frac{\partial J(w_0, w_1)}{\partial w_1} = 0$

In this approach we would solve the resulting system of linear or non-linear equations.

2. Use the Gradient Descent algorithm:

First we need to define three things:

- Arbitrary random initial values for $w_0$ and $w_1$.
- A step-size alpha ($\alpha$) -- also called the *learning rate*, a small number like $1.e-5$
- A *threshold* for the weight updates

Next we search for the optimal values of the weights $w_0$ and $w_1$ by using a loop to repeatedly update the weights until we minimize the cost function. We terminate the loop when the weight updates become negligibly small, i.e. below the threshold that we set. For example, if we adopt a threshold of 0.0001 and the weight updates -- the changes in the weight parameters from one iteration to the next -- are both less than 0.0001, then we stop the loop and declare victory, i.e. that we have found optimal values of the weights that (approximately) minimize the loss function!

Let's review some pseudocode for how to implement this algorithm:

```
# initialization
initialize the following:
    - starting weight values -- an initial guess, could be random
    - the learning rate (alpha), a small positive number (we'll choose 1.e-5)
    - the current weight update -- initialize to 1
    - the threshold -- set this to 1.e-4

# weight update loop
while the weight update is greater than the threshold:
    adjust the weights by adding the weight updates based on the algorithm

# return the final values of the weights
```

How do we compute the `weight updates based on the algorithm`?
The updates are the second term on the right hand side of the equations below: they are proportional to the partial derivatives and in the opposite direction.

$w_0 = w_0 - \alpha \frac{\partial J(w_0, w_1)}{\partial w_0}$

$w_1 = w_1 - \alpha \frac{\partial J(w_0, w_1)}{\partial w_1}$

### 5B. Gradient descent in one dimension

#### Let's define an interesting function of one variable to minimize:

$f(x) = \frac{x^4}{50} - 2x^2 + x + 1$
#### We can compute the derivative of this function using the formula from calculus

$\frac{d}{dx}(x^n) = nx^{n-1}$

So that

$f^{'}(x) = \frac{4}{50}x^3 - 4x + 1$

```python
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
eps = 1.e-10

# define an interesting function to minimize
x = np.linspace(-10,10, 100)
fx =lambda x: (1/50)*x**4 - 2*x**2 + x + 1

# compute derivative of x by applying the calculus formula d/dx(x**n) = n*x**(n-1)
df_dx = lambda x: (4/50)*x**3 - 4*x + 1

# plot the function, and its derivative
plt.figure(figsize=(10,8))
plt.plot(x,fx(x),label = 'function f(x)')
plt.plot(x,df_dx(x),'r--',label = 'derivative df/dx')
plt.xlabel('$x$',FontSize=18)
plt.ylabel('$f(x)$, $f\'(x)$',FontSize=18)
plt.title('The function $f(x)$ and its derivative $f\'(x)$', FontSize=18)

plt.legend(prop={'size': 18})
plt.grid()
```

#### Recall our `gradient()` function from section 2, above

```python
def gradient(func,x):
    delta_x = 1.e-10
    return (func(x + delta_x) - func(x))/delta_x
```

```python
# test the `gradient` code by choosing a few x values
#     and see if the answers make sense
x = -10
print(f'The gradient of f(x) at x = {x} is {gradient(func=fx,x=-10):.3}')
```

    The gradient of f(x) at x = -10 is -39.0

####  Write a `gradient descent` function for a function of one variable
Follow the pseudo-code in section **5A**

```python
def gradient_descent(func,x_init):

    '''
    Inputs:
        func: a function of one variable
        x_init: the starting value of the variable
    Outputs:
        x: the value of the variable at which func reached a minimum
        n_iter: the number of iterations of the weight update loop
            before it was terminated
        x_values: the list of updated weights
        '''

    # Initialize
    alpha = 1.e-4
    threshold = 1.e-4
    delta_x = 1.
    x = x_init
    n_iter = 0
    x_values = []

    # run the weight update loop until it terminates
    while np.abs(delta_x) > threshold: #and n_iter < max_iter:

        # compute the derivative of the function
        df_dx = gradient(func,x)
        # compute the weight update
        delta_x = -alpha*df_dx
        # apply the weight update
        x = x + delta_x
        # append to the running list of updated weights
        x_values.append(x)
        # increment the iteration count
        n_iter+=1

    return x, n_iter, x_values
```

#### Minimize the function $f(x) = \frac{x^4}{50} - 2x^2 + x + 1$
Choose an initial value for $x$

Then run your `gradient_descent` code to to find the value of $x$ that minimizes the function.

```python
# choose a starting point -- could be random
# np.random.seed(42)
# x_init = np.random.uniform(-10,10,1)
x_init = 2.5
x_star, n_iter, x_values = gradient_descent(func=fx,x_init=x_init)
print(f'Starting at the point x = {x_init}, \n    \
we found that the function reaches a minimum at x_star = \
{x_star:.3} \n    after {n_iter} iterations')
```

    Starting at the point x = 2.5,
        we found that the function reaches a minimum at x_star = 6.81
        after 6853 iterations

#### Check that the derivative is reasonably close to zero when evaluated at the `x_star` found by your `gradient_descent` function
Remember that the derivative ranges between $[-40,+40]$ according to the above graph.

```python
# derivative, from calculus formula
print(f'Derivative from the calculus formula is {df_dx(x_star)}')

# derivative, from definition
print(f'Derivative from the definition is  {gradient(func=fx,x=x_star)}')
```

    Derivative from the calculus formula is -0.9987282580894323
    Derivative from the definition is  -0.9987388693843968

#### Visualize how gradient descent finds the minimum of the function

```python
x_values = np.array(x_values)
x = np.linspace(-10,10, 100)

plt.figure(figsize=(10,8))
plt.plot(x,fx(x),'b-',label = 'function f(x)')
plt.plot(np.array(x_values),fx(x_values),'m.',markersize = 10, label='gradient descent updates')
plt.plot(x_init,fx(x_init),'k.',markersize = 20, label = '$x_{init}$, starting point')
plt.plot(x_star,fx(x_star),'k*',markersize = 20, label = '$x_{star}$, minimum of the function',)
plt.xlabel('x',FontSize = 18)
plt.ylabel('f(x)',FontSize = 18)
plt.title('Minimizing a function via gradient descent',FontSize = 18)
plt.grid()
plt.legend(loc='best',prop={'size': 18});
```

### 5C. Activity:
In the above section, our `gradient_descent()` function required almost 7000 iterations (steps) to find the minimum of the input function.
See if you can tune the value(s) of the *hyperparameters* $alpha$ and $threshhold$ in the `gradient_descent()` code to get reasonably close to the same answer(s) in fewer than 700 steps.

## Resources

- [Derivative tutorial from Math Is Fun](https://www.mathsisfun.com/calculus/derivatives-introduction.html)
- [Derivative Table](https://www.qc.edu.hk/math/Resource/AL/Derivative%20Table.pdf)
- [Khan Academy - Partial Derivatives video](https://www.youtube.com/watch?v=AXqhWeUEtQU&feature=youtu.be)
- [Towards Data Science - Machine Learning Fundamentals: cost functions and gradient Descent](https://towardsdatascience.com/machine-learning-fundamentals-via-linear-regression-41a5d11f5220)

```python

```


For mathematical computation, comparison of LaTeX output is useful. Here is an example:

```python
import sympy
sympy.init_printing()
x, y = sympy.symbols(["x", "y"])
```

Should pass:

```python
x**2
```

```python
import random
n = int(random.uniform(1, 10000000000000))
```

The next cell nbval evalution should fail (in most cases), so we exclude this cell from the comparison with the `#NBVAL_IGNORE_OUTPUT` token:

```python
# NBVAL_IGNORE_OUTPUT
x**n
```

This addresses issue https://github.com/computationalmodelling/nbval/issues/27 .


```python
import numpy as np
import sympy as sy
```

## 1. Implizite Abbildung durch Kerne
### a)

Geben Sie eine allgemeine Formel für die Abbildung der Ebene in den Raum der geordneten
Monome zweiter Ordnung (d.h. Produkte in unterschiedlicher Reihenfolge bekommen
eine eigene Dimension, obwohl das Resultat identisch ist, z.B. x1 · x2 und x2 · x1 bilden
unterschiedliche Einträge im Vektor $\phi$) an.

$$
    \Phi : \mathbb{R}^{2} \rightarrow \mathbb{R}^{4}
    ~~~~ \text{d.h.} ~~~~~
    \begin{pmatrix}
        x_1 \\
        x_2
    \end{pmatrix}
    \rightarrow
    \begin{pmatrix}
        z_1 \\
        z_2 \\
        z_3 \\
        z_4
    \end{pmatrix}
    ~~~~ \text{mit} ~~~~~
    \Phi(\mathbf{x}) =
    \begin{pmatrix}
        \phi_1(\mathbf{x}) \\
        \phi_2(\mathbf{x}) \\
        \phi_3(\mathbf{x}) \\
        \phi_4(\mathbf{x})
    \end{pmatrix}
    =
    \begin{pmatrix}
        x_1^2 \\
        x_1  x_2 \\
        x_2^2 \\
        x_2  x_1
    \end{pmatrix}
$$

```python
def transform(x):
    xx = np.asarray(x).flatten()
    return np.outer(xx,xx).flatten()
```

```python
transform([sy.Symbol('x1'), sy.Symbol('x2')])
```

    array([x1**2, x1*x2, x1*x2, x2**2], dtype=object)

```python

```

    (5,)

```python

```


```python
import numpy as np
import sympy as sp
from sympy import symbols
```

```python
eps_cs, eps_cd, eps_ca, beta_ds_t_t_s, k_h, eps_cd0, t, t_s, h_0, A_c, u, alpha_ds1, alpha_ds2, f_cm, f_cmo, beta_RH, RH, RH_0, beta_as_t, eps_ca_infty, f_ck, kappa_cs, alpha_e, E_s, E_c_eff, E_cm, phi, S, I= symbols(r'\varepsilon_{cs}, \varepsilon_{cd}, \varepsilon_{ca}, \beta_{ds}{(t_ts)}, k_h, \varepsilon_{cd0}, t, t_s, h_0, A_c, u, \alpha_{ds1}, \alpha_{ds2}, f_{cm}, f_{cmo}, \beta_{RH}, RH, RH_0,  \beta_{as}{(t)}, \varepsilon_{ca}{(\infty)}, f_{ck}, \kappa_{cs}, \alpha_{e}, E_s, E_{c_eff}, E_{cm}, \varphi, S, I', real=True, nonnegative=True)
```

### Shrinkage curvature calculation based on section 3.1.4 Creep and shrinkage of the EC2

(6) The total shrinkage strain is composed of two components, the drying shrinkage strain and
the autogenous shrinkage strain. The drying shrinkage strain develops slowly, since it is a
function of the migration of the water through the hardened concrete. The autogenous
shrinkage strain develops during hardening of the concrete: the major part therefore develops in
the early days after casting. Autogenous shrinkage is a linear function of the concrete strength.
It should be considered specifically when new concrete is cast against hardened concrete.
Hence the values of the total shrinkage strain $\varepsilon_{cs}$ follow from

$$ \varepsilon_{cs} = \varepsilon_{cd} + \varepsilon_{ca} \hspace{10cm}           (3.8) $$

| Symbol | Unit | Description
| :-: | :-: | :- |
| $\varepsilon_{cs}$ | - | is the total shrinkage strain|
| $\varepsilon_{cd}$ | - | is the drying shrinkage strain |
| $\varepsilon_{ca}$ | - | is the autogenous shrinkage strain |

```python
eps_cs = eps_cd + eps_ca
eps_cs
```

$\displaystyle \varepsilon_{ca} + \varepsilon_{cd}$

The development of the drying shrinkage strain in time follows from:

 $$\varepsilon_{cd}(t) = \beta_{ds}{(t, t_s)} . k_h . \varepsilon_{cd,0} \hspace{10cm}(3.9)$$

```python
eps_cd = beta_ds_t_t_s * k_h * eps_cd0
eps_cd
```

$\displaystyle \beta_{ds}{(t_ts)} \varepsilon_{cd0} k_{h}$

Table 3.3 Values for kh in Expression (3.9)

| Symbol | Unit | Description
| :-: | :-: | :- |
| $k_h$ | - | is a coefficient depending on the notional size ho according to Table 3.3|
| $\varepsilon_{cd,0}$ | - | nominal unrestrained drying shrinkage values $\varepsilon_{cd,0}$ (in ‰ ) for concrete cement Class|
| $\beta_{ds}$ $(t, t_s)$ | - | coefficient |
| $h_0$ | mm | is the notional size of the cross-section|

| $h_0$ | k_h|
| :-: | :-:
| 100 | 1.0
| 200| 0.85
| 300 | 0.75
| $\leq$ 500 | 0.70

```python
import matplotlib.pyplot as plt
import numpy as np
h_0_ = [100, 200 ,300, 500, 800]
k_h_ = [ 1, 0.85, 0.75, 0.7, 0.7 ]
plt.plot(h_0_, k_h_)
h_0 = 10000
k_h = np.interp(h_0, h_0_, k_h_)
k_h
```

$$\beta_{ds}{(t, t_s)} = \frac{(t - t_s)}{(t - t_s)+0.04  \sqrt{h_0^3}}\hspace{7cm}(3.10)$$

$$h_0 = 2A_c/u \hspace{7cm}$$

| Symbol | Unit | Description
| :-: | :-: | :- |
| t | days | is the age of the concrete at the momnet considered |
| $t_{s}$ | days | is the age of the concrete at the beginning of drying shrinkage (or swelling). Normally this is at the end of curing. |
| $h_0$ | mm | is the notional size of the cross-section|
| $$A_c$$ | $$mm^2$$ | is the concrete cross-sectional area|
| u | mm | is the perimeter of that part of the cross section which is exposed to drying |

```python
beta_ds_t_t_s = (t - t_s) / ((t - t_s) + 0.04 * h_0 ** (3/2))
h_0 = 2 * A_c / u
beta_ds_t_t_s, h_0
```

    ((t - t_s)/(t - t_s + 40000.0), 2*A_c/u)

### B.2 Basic equations for determining the drying shrinkage strain

$$\varepsilon_{cd,0} = 0.85 \left[ (220 + 110 . \alpha_{ds1}).exp \left(-\alpha_{ds2} .\frac{f_{cm}}{f_{cmo}}\right)         \right] . 10^{-6} . \beta_{RH}\hspace{7cm}(B.11)$$

$$\beta_{RH} = 1.55\left[1-\left(\frac{RH}{RH_0}\right)^3\right]\hspace{13.5cm}(B.12)$$

| Symbol | Unit | Description
| :-: | :-: | :- |
| $f_{cm}$ | MPa | is the mean compressive strength |
| $f_{cmo}$ | Mpa | 10 |
| $\alpha_{ds1}$ | - | is a coefficient which depends on the type of cement (see 3.1.2 (6));3 for S, 4 for N, 6 for R|
| $\alpha_{ds2}$ | - | is a coefficient which depends on the type of cement; 0,13 for S, 0,12 for N, 0,11 for R|
| RH | % | is the ambient relative humidity |
| RH0 | % | 100 |

```python
beta_RH = 1.55 * (1 - (RH/RH_0)**3)
eps_cd0 = 0.85 * ((220 + 110 * alpha_ds1) * sp.exp(-alpha_ds2 * f_cm / f_cmo)) * 1e-6 * beta_RH
eps_cd0, beta_RH
```

    (8.5e-7*(110*\alpha_{ds1} + 220)*(-1.55*RH**3/RH_0**3 + 1.55)*exp(-\alpha_{ds2}*f_{cm}/f_{cmo}),
     -1.55*RH**3/RH_0**3 + 1.55)

#### The autogenous shrinkage strain follows from:

 $$\varepsilon_{ca}(t) = \beta_{as}(t)\varepsilon_{ca}(\infty) \hspace{10cm}(3.11)$$

$$\varepsilon_{ca}(\infty) = 2.5 (f_{ck} - 10)10^{-6}\hspace{10cm}(3.12)$$

$$\beta_{as}(t) = 1 - exp(-0.2t^{0.5})\hspace{10cm}(3.13)$$

| Symbol | Unit | Description
| :-: | :-: | :- |
| t | days | Time being considered |
| $f_{ck}$ | Mpa | Characteristic compressive cylinder strength of concrete at 28 days |

```python
eps_ca = beta_as_t * eps_ca_infty
eps_ca_infty = 2.5 * (f_ck - 10) * 1e-6
beta_as_t = 1 - sp.exp(- 0.2 * t ** 0.5)
eps_ca, eps_ca_infty, beta_as_t
```

    (\beta_{as}{(t)}*\varepsilon_{ca}{(\infty)},
     2.5e-6*f_{ck} - 2.5e-5,
     1 - exp(-0.2*t**0.5))

#### (6) Shrinkage curvatures may be assessed using Expression (7.21):

\begin{equation} \label{eq:8}
\alpha = \zeta \alpha_{\mathrm{II}} + (1-\zeta)\alpha_{\mathrm{I}}\hspace{11.8cm} (7.18)
\end{equation}
Where :

| Symbol | Unit | Description
| :-: | :-: | :- |
|  $\alpha$ | - | is the deformation parameter, for example deflection |
| $\alpha_{\mathrm{II}}, \alpha_{\mathrm{I}}$ | - | are coefficients for the uncracked and fully cracked conditions respectively |
|  $\zeta$ | - | is a distribution coefficient given by Equation below |

\begin{equation} \label{eq:9}
\zeta = 1 - \beta\left(\frac{\sigma_{\mathrm{sr}}}{\sigma_{s}}\right)^{2}\hspace{12.5cm}(7.19)
\end{equation}

| Symbol | Unit | Description
| :-: | :-: | :- |
|  $\zeta$ | - |  is 0 for uncracked sections |
| $\beta$| - | is a coefficient taking account of the influence of the duration of the loading or of repeated loading on the average strain = 1,0 for a single short-term loading, or = 0,5 for sustained loads or many cycles of repeated loading|
|  $\sigma_{s}$ | - | is tension reinforcement stress in cracked section|
|  $\sigma_{sr}$ | - | is tension reinforcement stress in loading conditions causing first cracking|

$$ E_{\mathrm{c,eff}} = \frac{E_{cm}}{1 + \varphi (\infty,t_0)}\hspace{10cm}(7.20)$$

$$ \frac{1}{r_{cs}} = \varepsilon_{cs} \alpha_e \frac{S}{I} \hspace{10cm}(7.21)$$

$$\alpha_e = E_s / E_{\mathrm{c,eff}}$$

| Symbol | Unit | Description
| :-: | :-: | :- |
| $E_{\mathrm{c,eff}}$ | - | is the effective modulus of elasticity of concrete |
| $E_{cm}$ | - | is the secant modulus of elasticity of concrete |
| $\varphi$ | - | is the final creep coefficient |
| $1/r_{cs}$ | - | is the curvature due to shrinkage |
| $\varepsilon_{cs}$ | - | is the free shrinkage strain (see 3.1.4) |
| S | - | is the first moment of area of the reinforcement about the centroid of the section|
| I | - | is the second moment of area of the section|
| $\alpha_e$ | % |is the effective modular ratio |

```python
E_c_eff = E_cm / (1 + phi)
alpha_e = E_s / E_c_eff
kappa_cs = eps_cs * alpha_e * S / I
E_c_eff, alpha_e, kappa_cs
```

    (E_{cm}/(\varphi + 1),
     E_s*(\varphi + 1)/E_{cm},
     E_s*S*(\varepsilon_{ca} + \varepsilon_{cd})*(\varphi + 1)/(E_{cm}*I))

```python
def get_shrinkage_strain(cement_type = 'R'):

        f_ck = 25
        t = 365*200
        t_s = 100

        if cement_type == 'S':
            alpha_ds1 = 3
            alpha_ds2 = .13
        elif cement_type == 'N':
            alpha_ds1 = 4
            alpha_ds2 = .12
        elif cement_type == 'R':
            alpha_ds1 = 6
            alpha_ds2 = .11

        RH = 55
        RH_0 = 100
        f_cm = f_ck + 8
        f_cmo = 10
        phi = 2.5 # where is the formula!
        E_s = 200
        E_cm = 30.5
        I = (300*700**3/12)
        A_c = 300*700
        u = 2000

        eps_ca_infty = 2.5 * (f_ck - 10) * 1e-6
        beta_as_t = 1 - sp.exp(- 0.2 * t ** 0.5)
        eps_ca = beta_as_t * eps_ca_infty

        beta_RH = 1.55 * (1 - (RH/RH_0)**3)
        h_0 = 2 * A_c / u
        beta_ds_t_t_s = (t - t_s) / ((t - t_s) + 0.04 * h_0 ** (3/2))
        h_0_ = [100, 200 ,300, 500, 800]
        k_h_ = [1, 0.85, 0.75, 0.7, 0.7]
        k_h = np.interp(h_0, h_0_, k_h_)
        eps_cd0 = 0.85 * ((220 + 110 * alpha_ds1) * sp.exp(-alpha_ds2 * f_cm / f_cmo)) * 1e-6 * beta_RH
        eps_cd = beta_ds_t_t_s * k_h * eps_cd0

        eps_cs = eps_cd + eps_ca
        return eps_cs

get_shrinkage_strain()
```

$\displaystyle 0.00060128022750331$

```python
def get_cracked_sh_curvature( E_s = 200, E_cm = 30.5, phi = 2.5, As = 2450, b = 300, d = 600, x = 322.7):
    E_c_eff = E_cm / (1 + phi)
    alpha_e = E_s / E_c_eff
    S = As*(d-x)
    I_cr = b*x**3/3 + alpha_e*As*(d-x)**2
    eps_cs = get_shrinkage_strain()
    sh_c_cr = (eps_cs*alpha_e*S)/I_cr
    return sh_c_cr

get_cracked_sh_curvature()
```

$\displaystyle 1.22008675788223 \cdot 10^{-6}$

```python
def get_uncracked_sh_curvature(eps_cs = 590e-6, E_s = 200e6, E_cm = 30.5e6, phi = 2.5, As = 2450, b = 300, d = 600, h = 700):
    E_c_eff = E_cm / (1 + phi)
    alpha_e = E_s / E_c_eff
    S = As*(d-h/2)
    I_cr = b*h**3/12

    sh_c_cr = (eps_cs*alpha_e*S)/I_cr
    return sh_c_cr

get_uncracked_sh_curvature()
```

    9.672131147540981e-07

```python
import sympy as sp
import numpy as np
def get_sh_curvature(f_ck = 25, M_ = 200e6, b = 300, d = 600, h = 700):
    beta = 0.5
    f_ctm = sp.Piecewise(
                            (0.3*f_ck**(2/3), f_ck < 50),
                            ((2.12*np.log((1+(f_ck+8)/10))), f_ck >= 50)
        )
    alpha_I = get_uncracked_sh_curvature()
    alpha_II = get_cracked_sh_curvature()

    M_cr = f_ctm * b*h**2/6
    zeta = 1 - beta*(M_cr/M_)**2
    sh_c = zeta*alpha_II + (1-zeta)*alpha_I
    return sh_c, zeta, f_ctm, M_cr

get_sh_curvature()
```

    (1.20760406549732e-6, 0.950636641167936, 2.56496392001505, 62841616.0403686)

```python
import sympy as sp
import numpy as np

class ShrinkagCurvature():

    E_s = 200
    E_cm = 30.5
    f_ck = 25
    f_cm = f_ck + 8
    A_s = 2450
    b = 300
    d = 600
    h = 700
    M = 200e6

    t = 365*20
    t_s = 10
    RH = 55
    RH_0 = 100
    f_cmo = 10
    phi = 2.5 # where is the formula!

    def get_shrinkage_strain(self, cement_type = 'R'):

        if cement_type == 'S':
            alpha_ds1 = 3
            alpha_ds2 = .13
        elif cement_type == 'N':
            alpha_ds1 = 4
            alpha_ds2 = .12
        elif cement_type == 'R':
            alpha_ds1 = 6
            alpha_ds2 = .11

        eps_ca_infty = 2.5 * (self.f_ck - 10) * 1e-6
        beta_as_t = 1 - sp.exp(- 0.2 * self.t ** 0.5)
        eps_ca = beta_as_t * eps_ca_infty

        beta_RH = 1.55 * (1 - (self.RH/self.RH_0)**3)
        A_c = self.b*self.h
        u = 2*(self.b+self.h)
        h_0 = 2 * A_c / u
        beta_ds_t_t_s = (self.t - self.t_s) / ((self.t - self.t_s) + 0.04 * h_0 ** (3/2))
        h_0_ = [100, 200 ,300, 500, 800]
        k_h_ = [1, 0.85, 0.75, 0.7, 0.7]
        k_h = np.interp(h_0, h_0_, k_h_)
        eps_cd0 = 0.85 * ((220 + 110 * alpha_ds1) * sp.exp(-alpha_ds2 * self.f_cm / self.f_cmo)) * 1e-6 * beta_RH
        eps_cd = beta_ds_t_t_s * k_h * eps_cd0

        eps_cs = eps_cd + eps_ca
        return eps_cs

    def get_cracked_sh_curvature(self, x = 322.7):

        E_c_eff = self.E_cm / (1 + self.phi)
        alpha_e = self.E_s / E_c_eff

        # solving the ax2+bx+c=0 of area moments about the neutral axis: to get
        coeff = [self.b/2,self.E_s/E_c_eff*self.A_s,-self.E_s/E_c_eff*self.A_s*self.d]
        x = np.where(np.roots(coeff)[:] > 0, np.roots(coeff),'problem')[1].astype(float)

        S = self.A_s*(self.d-x)
        I_cr = self.b*x**3/3 + alpha_e*self.A_s*(self.d-x)**2
        eps_cs = self.get_shrinkage_strain()
        sh_c_cr = (eps_cs*alpha_e*S)/I_cr
        return sh_c_cr

    def get_uncracked_sh_curvature(self):
        E_c_eff = self.E_cm / (1 + self.phi)
        alpha_e = self.E_s / E_c_eff
        S = self.A_s*(self.d-self.h/2)
        I_cr = self.b*self.h**3/12
        eps_cs = self.get_shrinkage_strain()
        sh_c_cr = (eps_cs*alpha_e*S)/I_cr
        return sh_c_cr

    def get_sh_curvature(self):
        M_ = self.M
        beta = 0.5
        f_ctm = sp.Piecewise(
                                (0.3*self.f_ck**(2/3), self.f_ck < 50),
                                ((2.12*np.log((1+(self.f_ck+8)/10))), self.f_ck >= 50)
            )

        alpha_I = self.get_uncracked_sh_curvature()
        alpha_II = self.get_cracked_sh_curvature()

        M_cr = f_ctm * self.b*self.h**2/6
        zeta = 1 - beta*(M_cr/M_)**2
        sh_c = zeta*alpha_II + (1-zeta)*alpha_I
        return sh_c
```

```python
shk = ShrinkagCurvature()
# shk.t = 10
print(shk.get_sh_curvature(),shk.get_cracked_sh_curvature(),shk.get_uncracked_sh_curvature(), shk.get_shrinkage_strain() )
```

    1.19251908597485e-6 1.20396761512713e-6 9.72043989524010e-7 0.000592946833609646

### Verification example

### Shrinkage curvature calculation based on DAfStb Unterausschuss Nichtmetallische Bewehrung - Herleitung von Biegeschlankheitskriterien
Vorbemerkung: Die nachfolgenden Ergebnisse stammen aus:
El Ghadioui, Redouan: „Bemessung carbonbewehrter Betonbauteile unter besonderer Betrachtung des
Gebrauchszustandes - Entwicklung eines Modells zur wirklichkeitsnahen Verformungsberechnung“,
Technische Universität Darmstadt, Dissertation, 2020

$$ E_{c,eff} = \frac{E_{cm}}{1 + \varphi_{eq} (\infty,t_0)}$$

```python
E_cm = 35000; phi_eq = 1.6
E_c_eff = E_cm / (1 + phi_eq)
E_c_eff
```

    13461.538461538461

$$h_0 = 2A_c/u$$

```python
A_c = 1000 * 300
u = 2 * 1300
h_0 = 2 * A_c / u
```

$$\alpha_1 = \left[\frac{35}{f_{cm}} \right]^{0.7} \hspace{1cm} \alpha_2 = \left[\frac{35}{f_{cm}} \right]^{0.2} \hspace{1cm} \alpha_3 = \left[\frac{35}{f_{cm}} \right]^{0.5} $$

```python
f_cm = 30
alpha1 = (35/f_cm)**0.7
alpha2 = (35/f_cm)**0.2
alpha3 = (35/f_cm)**0.5
alpha1, alpha2, alpha3
```

    (1.1139424459794183, 1.031310306477545, 1.0801234497346435)

$$ \varphi_{RH} = 1 + \frac{1 - RH/100}{0.1 . \sqrt[3]{h_0}} \hspace{2cm} for f_{cm} \leq 35 MPa$$

$$ \varphi_{RH} = \left[1 + \frac{1 - RH/100}{0.1 . \sqrt[3]{h_0}} . \alpha_1\right] . \alpha_2 \hspace{2cm} for f_{cm} > 35 MPa$$

```python
RH = 70
phi_RH = sp.Piecewise((1 + (1-RH/100)/(0.1 * h_0**(1/3)), f_cm <= 35),
                      ((1 + ((1-RH/100)/(0.1 * h_0**(1/3))) * alpha1) * alpha2, f_cm > 35))
phi_RH
```

$\displaystyle 1.48909732465087$

$$ \beta{(f_{cm})} = \frac{16.8}{\sqrt{f_{cm}}} $$

```python
beta_fcm = 16.8 / f_cm**0.5
beta_fcm
```

    3.0672463220289305

$$ \beta{(t_{0})} = \frac{1}{0.1 + t_0^{0.20}} $$

```python
t_s = 3
t = 100000000
t_0 = 10
t_1 = 60
t_3 = 365
```

```python
beta_t0 = 1 / (0.1 + t_0**0.20)
beta_t0
```

    0.593509431027676

$$ \beta_H = 1.5 [ 1 + (0.012 RH)^{18}] h_0 + 250 \leq 1500 \hspace{2cm} for f_{cm} \leq 35 $$

$$ \beta_H = 1.5 [ 1 + (0.012 RH)^{18}] h_0 + 250 \alpha_3 \leq 1500 \alpha_3 \hspace{2cm} for f_{cm} \leq 35 $$

```python
beta_H = sp.Piecewise((1.5*(1 + 0.012*RH)**18 * h_0 + 250, f_cm <=35),
                     (1.5*(1 + 0.012*RH)**18 * h_0 + 250 * alpha3, f_cm > 35))
beta_H
```

$\displaystyle 20229974.8400678$

$$ \beta_c (t,t_0) = \left[ \frac{(t - t_0)}{(\beta_H + t - t_0)} \right]^{0.3} $$

$$ \varphi_0 = \varphi_{RH} . \beta{(f_{cm})} . \beta{(t_{0})} $$

```python
phi_0 = phi_RH * beta_fcm * beta_t0
phi_0
```

$\displaystyle 2.66891899349227$

```python
beta_ctt0 = ((t-t_0)/(beta_H + t - t_0))** 0.3
beta_ctt0
```

$\displaystyle 0.938922885733641$

ANNEX B (Informative)
Creep and shrinkage strain
B.1 Basic equations for determining the creep coefficient
(1) The creep coefficient qJ(t,ta} may be calculated from:

$$ \varphi(t,t_0) = \varphi_0 . \beta_c (t,t_0) $$

```python
phi_tt0 = phi_0 * beta_ctt0
phi_tt0
```

$\displaystyle 2.50590912315908$

- Beanspruchungen

Eigenlast gk 36 % der gesamten charakteristischen Last pk

Ausbaulast Δgk 24 % der gesamten charakterischen Last pk

Nutzlast ψ2 ∙ qk 0,3 ∙ 40 % der gesamten charakteristischen Last pk

Bemessungslast GZT 1,35 ∙ (36 % + 24 %) + 1,5 ∙ 40 % = 141 %

Quasi-ständige Last GZG 36 % + 24 % + 0,3 ∙ 40 % = 72 %

Verhältnis GZG / GZT 72 / 141 = 51 %
- Relative Luftfeuchtigkeit
RH = 70 %
- Zeitpunkte
Dauer der Nachbehandlung: ts = 3 Tage

Eigenlast gk: t0 = 10 Tage

Ausbaulast Δgk: t1 = 60 Tage

Nutzlasten qk+Δqk: t2 = 365 Tage

$$ \varphi_{eq}(\infty,t_0) = \frac{\varphi(\infty,t_0).g_k +\varphi(\infty,t_1).\Delta g_k + \varphi(\infty,t_2) . \sum {\psi_{2,i}} . q_{k,i}} {g_k + \Delta g_k + \sum {\psi_{2,i}} . q_{k,i} }$$

```python
phi_eq = (0, .5, 1, 1.5 , 2, 2.5, 3)
E_c_eff = E_cm / (np.ones_like(phi_eq) + phi_eq)

import matplotlib.pyplot as plt
plt.plot(E_c_eff, phi_eq)
plt.xlabel('Ecm')
plt.ylabel('phi_eq')
plt.show()
```

```python
phi_t0 = 1
phi_t1 = 2
phi_t2 = 3

g_k = 0.36
Delta_gk = 0.24
psi_2i = 0.3
q_ki = 0.40
E_cm = 35000
phi_eq = (phi_t0 * g_k + phi_t1 * Delta_gk + phi_t2* psi_2i * q_ki) / (g_k + Delta_gk + psi_2i * q_ki)
E_c_eff = E_cm / (1 + phi_eq)
phi_eq
```

    1.6666666666666667

```python
eta = 0.51 # = GZG / GZT
```

```python
L = 6000
f_ck = 30
f_cd = 0.85 * f_cm / 1.5
M_Ed = 20
b = 1000
h = 300
d_2 = 80 #cover
d = h - d_2
mu_Ed = M_Ed / (b*d**2*f_cd)
mu_Ed
```

    2.4307243558580458e-08

```python
eps_fud = 2
mu_Ed_bal = 0.8095 * 3.5 / (3.5 + eps_fud) * (1 - 0.416 * 3.5 / (3.5 + eps_fud))
mu_Ed_bal
```

    0.37876571900826445

```python
A_sf1 = 5.02 * 100
A_sf2 = 5.02 * 100
E_sf = 200000
rho_1I = A_sf1 / (b * h)
rho_2I = A_sf2 / (b * h)
alpha_e = E_sf / E_c_eff
B_I = (alpha_e - 1) * (rho_1I + rho_2I)
A_I = B_I/h * (d + d_2)
k_xI = (0.5 + A_I)/(1 + B_I)
x_I = k_xI * h
z_sf1 = d - x_I
z_sf2 = d - d_2 - z_sf1

k_I = 1 + 12 * ( 0.5 - k_xI)**2\
    + 12 * (alpha_e - 1) * rho_1I * (d/h - k_xI)**2\
    + 12 * (alpha_e - 1) * rho_1I * A_sf2/A_sf1 * (k_xI - d_2/h)**2
I_I = k_I * b * h**3 / 12

S_I = A_sf1 * z_sf1 + A_sf2 * z_sf2 # z_sf2 < 0
```

```python
import numpy as np

rho_1II = A_sf1 / (b * h)
rho_2II = A_sf2 / (b * h)
alpha_e = E_sf / E_c_eff
A_II = alpha_e * (rho_1II + rho_2II)
k_xII = -A_II + np.sqrt(A_II ** 2 + 2 * alpha_e * (rho_1II + rho_2II * d_2 / d))
x_II = k_xII * d
z_sf1 = d - x_II
z_sf2 = d - d_2 - z_sf1

k_II = 4 * k_xII**3\
    + 12 * alpha_e * rho_1II * (1 - k_xII)**2\
    + 12 * alpha_e * rho_1II * A_sf2/A_sf1 * (k_xII - d_2/d)**2
I_II = k_II * b * d**3 / 12

S_II = A_sf1 * z_sf1 + A_sf2 * z_sf2 # z_sf2 < 0
```

$$ \frac{w_m}{L} = \frac{5}{4} . \frac{\eta . \mu_{Ed} . f_{cd}}{E_{c,eff}} . \frac{L}{d} .\left( \frac{\xi}{k_{\mathrm{II}}} + \frac{(1-\xi)}{k_{\mathrm{I}} . (h/d)^3} \right) + Schwinden $$

```python
M_Ed = 100e6
import sympy as sp
f_ctm = sp.Piecewise(
                                (0.3*f_ck**(2/3), f_ck < 50),
                                ((2.12*np.log((1+(f_ck+8)/10))), f_ck >= 50)
            )
M_cr = f_ctm * I_I / x_I
beta = 1 # one-time, short-term stressing; = 0.5 for permanent, repeated stressing
xi = 1 - beta * (M_cr / M_Ed) **2
w_m = (5/4) * (eta * mu_Ed * f_cd / E_c_eff) * (L / d) * (xi/k_II + (1-xi) / (k_I * (h/d)**3)) * L
M_cr, M_Ed, w_m
```

    (43120815.0779654, 100000000.0, 1.16070812063613e-5)

```python

```


```python
import numpy as np
import scipy.stats as si
import sympy as sy
from sympy.stats import Normal, cdf
```

```python
def black_scholes(S, K, T, r, q, sigma, option):

    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset
    #sigma: volatility of underlying asset
    #option: "call" or "put"

    if option is "call":
        d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
        d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

        call = (S * np.exp(-q * T) * si.norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(d2, 0.0, 1.0))
        return call

    elif option is "put":
        d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
        d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

        put = (K * np.exp(-r * T) * si.norm.cdf(-d2, 0.0, 1.0) - S * np.exp(-q * T) * si.norm.cdf(-d1, 0.0, 1.0))
        return put

    else:
        print("you've made an error")
```

```python
# Price of a put option expiring in one year with strike price €100
# Current share price is €50
# dividend yield of 0, risk free rate of 0.05 and volatility of 0.25

black_scholes(50, 100, 1, 0.05, 0, 0.25, option = 'put')
```

    45.15029495944084

```python

```

```python

```