# Evapotranspiration - lecture

- toc: true
- badges: true
- comments: false
- categories: [jupyter]

Dingman, "Physical Hydrology", chapter 6.

Globally, about 62% of the precipitation that falls on the continents is evapotranspirated, amounting to 73 thousand km$^3$/yr. Of this, about 42% (29 thousand km$^3$/yr) is transpiration, and about 3% is open-water evaporation. Most of the remainder is interception loss; soil evaporation is a minor component of the total.

### Potential Evapotranspiration

Potential Evapotranspiration (PET) is the rate at which evapotranspiration would occur from a large area completely and uniformly covered with growing vegetation with access to an unlimited supply of soil water and without advection or heat-storage effects.

Several characteristics of a vegetative surface have a strong influence on ET rate. a) the albedo of the surface, which determines the net radiation; b) the maximum leaf conductance; c) the atmospheric conductance, largely determined by vegetation height; d) presence or absence of intercepted water.

### Reference-Crop Evapotranspiration

Reference-crop evapotranspiration (RET) is the amount of water transpired by a short green crop, completely shading the ground, of uniform height, and never short of water.

The magnitude of PET is often calculated from meteorological data collected under conditions in which the actual ET rate **is less than the potential rate**. If ET had been occurring at the potential rate, the latent- and sensible-heat exchanges between air and the surface, and hence the air temperature and humidity, would have been considerably different. (Brutsaert 1982)

## Review of methods

There are a variety of ways to estimate evaporative flux in nature.  The following table categorizes each method based on the data that must be acquired to apply it:

These methods also vary in the timescales in which they are relevant, typically in correlation with the variety of data needed:
 - Thornthwaite and SCS Blaney-Criddle: monthly or seasonal estimations (minimal data)
 - Jensen-Haise: 5-day estimates (good enough timescale and data for irrigation scheduling)
 - Penman: daily estimates
 - Penman-Monteith: hourly estimates (requires a lot of data)

## Thornthwaite

Source: Ward & Trimble, "Environmental Hydrology", 2nd Edition, pages 107-108.

Thornthwaite (1948) developed an equation to predict monthly evapotranspiration from mean monthly tempera- ture and latitude data (Equation 4.27). The small amount of data needed is attractive because often ET needs to be predicted for sites where few weather data are available. Based on what we know about ET, we should be skeptical about the general applicability of such a simple equation. Thornthwaite (1948) was not satisfied with the proposed approach: “The mathematical development is far from satisfactory. It is empirical. ... The chief obstacle at present to the development of a rational equation is the lack of understanding of why potential ET corresponding to a given temperature is not the same everywhere.”
Taylor and Ashcroft (1972), as cited in Skaggs (1980), provided insight into the answer to Thornthwaite’s ques- tion. They said:
> This equation, being based entirely upon a temperature relationship, has the disadvantage of a rather flimsy phys- ical basis and has only weak theoretical justification. Since temperature and vapor pressure gradients are mod- ified by the movement of air and by the heating of the soil and surroundings, the formula is not generally valid, but must be tested empirically whenever the climate is appreciably different from areas in which it has been tested. ... In spite of these shortcomings, the method has been widely used. Because it is based entirely on temper- ature data that are available in a large number of localities, it can be applied in situations where the basic data of the Penman method are not available.

M.E. Jensen et al. (1990) warn that Thornthwaite’s method is generally only applicable to areas that have climates similar to that of the east central U.S., and it is not applicable to arid and semiarid regions.

Thornthwaite (1948) found that evapotranspiration could be predicted from an equation of the form

$$
\begin{equation}
E = 16\left[ \frac{10\,T^\text{monthly mean}}{I} \right]^a,
\end{equation}
$$
where
$$
\begin{equation}
I = \sum_{i=1}^{12} \left[ \frac{T_i^\text{monthly mean}}{5} \right]^{1.514},
\end{equation}
$$
and
$$
\begin{align}
a &= 6.75\times 10^{-7}I^3 \\
   &- 7.71\times 10^{-5}I^2 \nonumber\\
   &+ 1.792\times 10^{-2}I \nonumber\\
   &+ 0.49239 \nonumber
\end{align}
$$

 - $E$ is the monthly potential ET (mm)
 - $T_\text{monthly mean}$ is the mean monthly temperature in °C
 - $I$ is a heat index
 - $a$ is a location-dependent coefficient

## Penman

Sources:
Brutsaert, "Hydrology: An Introduction", pages 123-127.
Ward & Trimble, "Environmental Hydrology", 2nd Edition, subsections 4.5.2, 4.5.3, 4.5.5, 4.6.6.
Allen et al. (1998), ["Crop evapotranspiration - Guidelines for computing crop water requirements - FAO Irrigation and drainage paper 56"](http://www.fao.org/3/x0490e/x0490e07.htm#chapter%203%20%20%20meteorological%20data)

The Penman model is almost entirely a theory-based formula for predicting evaporative flux. It can run on a much finer timescale, and requires a much wider variety of data than most models. In addition to temperature, the Penman functions on measurements of radiation, wind speed, elevation above sea level, vapor-pressure deficit, and heat flux density to the ground. The potential ET (in mm d$^{-1}$) is given by:

$$
\begin{equation}
E = \frac{1}{\lambda}\left[ \frac{\Delta}{\Delta+\gamma}Q_{ne}+ \frac{\gamma}{\Delta+\gamma}E_A \right],
\end{equation}
$$

where $Q_n$ is the available energy flux density

$$
\begin{equation}
Q_n = R_n - G,
\end{equation}
$$

and $E_A$ is the drying power of the air

$$
\begin{equation}
E_A = 6.43\cdot f(u)\cdot\text{VPD}.
\end{equation}
$$

The constituents of the equations above are

 - $E$: potential evapotranspiration (mm d$^{-1}$)
 - $\Delta$: slope of the saturation water vapor pressure curve (kPa °C$^{-1}$)
 - $\gamma$: psychrometric constant (kPA °C$^{-1}$)
 - $\lambda$: latent heat of vaporization (MJ kg$^{-1}$)
 - $R_n$: net radiation (MJ m$^{-2} d^{-1}$)
 - $G$: heat flux density to the ground (MJ m$^{-2} d^{-1}$)
 - $f(u)$: wind function (dimensionless)
 - VPD: vapor pressure deficit (kPa)

and the number 6.43 adjusts the units of $E_A$ so it is in MJ m$^{-2} d^{-1}$.
In what follows, we will further discuss these constituents.

### Psychrometric Constant

The psychrometric constant $\gamma$ (kPA °C$^{-1}$) relates the partial pressure of water in air to the air temperature:

$$
  \begin{equation}
    \gamma = \frac{c_p\, P}{\lambda\cdot MW_\text{ratio}}
  \end{equation}
$$

  $$
  \begin{equation}
    P = 101.3-0.01055 H
  \end{equation}
  $$

  $$
  \begin{equation}
    \lambda = 2.501 - 2.361\times 10^{-3}\,T
  \end{equation}
  $$

* $MW_\text{ratio}=0.622$: ratio molecular weight of water vapor/dry air
* $P$: atmospheric pressure (kPa). Can be either measured or inferred from station height above sea level (m).
* $\lambda$: latent heat of water vaporization (MJ kg$^{-1}$)
* $c_p=0.001013$: specific heat capacity of moist air (MJ kg$^{-1}$ °C$^{-1}$)

### Net Radiation
Source: Ward & Trimble, "Environmental Hydrology", 2nd Edition, page 99.

$R_n$ (MJ m$^{-2} d^{-1}$) is net radiation, the balance between net short wave $R_s$ and the long wave $R_b$ components of the radiation:

$$R_n = (1-\alpha)R_s\!\! \downarrow -R_b \!\! \uparrow,$$

where $\alpha$ (dimensionless) is the albedo.
The net outgoing thermal radiation $R_b$ is given by

$$R_b = \left( a\frac{R_s}{R_{so}+b} \right)R_{bo},$$

where $R_{so}$ is the solar radiation on a cloudless day, and it depends on latitude and day of the year.
$R_{bo}$ is given by

$$R_{bo} = \epsilon\, \sigma\, T^4_{Kelvin},$$

where $\sigma=4.903\times 10^{-9}$ MJ m$^{-2}$ d$^{-1}$ K$^{-4}$, and $\epsilon$ is net net emissivity:

$$\epsilon=-0.02+0.261 \exp\left(-7.77\times10^{-4}T_{Celcius}^2\right).$$

The parameters $a$ and $b$ are determined for the climate of the area:
* $a=1.0$, $b=0.0$ for humid areas,
* $a=1.2$, $b=-0.2$ for arid areas,
* $a=1.1$, $b=-0.1$ for semihumid areas.

We can find below a table for $R_{so}$, from Ward & Trimble, "Environmental Hydrology", 2nd Edition, page 100.

### Heat Flux Density to the Ground

The heat flux density to the ground $G$ (MJ m$^{-2} d^{-1}$) can be calculated using

$$
  \begin{equation}
    G = 4.2\frac{T_{i+1}-T_{i-1}}{\Delta t},
  \end{equation}
$$

where $\Delta t$ is the time *in days* between midpoints of time periods $i+1$ and $i−1$, and $T$ is the air temperature (°C).
This expression is really a finite differences implementation of a time derivative:

$$
\displaystyle \frac{\text{d}T}{\text{d}t} = \lim_{\Delta t\rightarrow 0}\frac{T(t+\Delta t) - T(t-\Delta t)}{2\Delta t}.
$$

Later on, we will take advantage of numpy's `gradient` function to calculate $G$.

### Vapor Pressure

from: Ward & Trimble, "Environmental Hydrology", 2nd Edition, page 95.

The Vapor Pressure Deficit (VPD, in kPa) is the difference between saturation vapor pressure $e_s$ and actual vapor pressure $e_d$:

$$\text{VPD} = e_s - e_d.$$

For temperatures ranging from 0 to 50 °C, the saturation vapor pressure can be calculated with

$$
  \begin{equation}
    e_s = \exp \left[ \frac{16.78\, T -116.9}{T+237.3} \right],
  \end{equation}
$$

and the actual vapor pressure is given by

$$
  \begin{equation}
    e_d = e_s \frac{RH}{100},
  \end{equation}
$$

where $RH$ is the relative humidity (%), and the temperature $T$ in the equations above is in degrees Celcius.

We can see below a graph of $e_s(T)$ (Ward & Trimble, "Environmental Hydrology", 2nd Edition, page 96)

The factor $\Delta$ is the slope of $e_s(T)$. See the figure below from Brutsaert, where the saturation vapor pressure is called $e^*$ (Brutsaert, "Hydrology: An Introduction", page 28)):

There are a few ways of defining the function for $\Delta(T)$ (kPa °C$^{-1}$). Ward & Trimble give the following:

$$
  \begin{equation}
    \Delta = 0.200 \cdot (0.00738\,T + 0.8072)^7 - 0.000116,
  \end{equation}
$$

while differentiating the exponential expression given before yields:

$$
  \begin{equation}
    \Delta = \frac{\text{d} e_s}{\text{d}T} = e_s(T)\cdot \frac{4098.79}{(T+237.3)^2}.
  \end{equation}
$$

### Wind Function

Source: (Ward & Trimble, "Environmental Hydrology", 2nd Edition, page 108)

$$
  \begin{equation}
    f(u) = 0.26(1.0 + 0.54\, u_2)
  \end{equation}
$$

# Meaning of "potential" evapotranspiration

### Crop Coefficient

$$ E_{t} = k_c E_{tr,\, tp} $$

$E_{t}=$ actual ET
$k_c=$ crop coefficient
$E_{tr}=$ reference crop ET
$E_{tp}=$ potential ET

## Pitfalls

Different books and papers will present slightly different versions of the Penman equation. Basically, they differ in the units they use for the various components, and one should be vary aware of what inputs any given equation is expecting to get.

```python

```


# *FEniCS tutorial:* Heat equation with Dirichlet boundary conditions in two dimensions

In this demo, we solve the two-dimensional diffusion equation with Dirichlet
boundary conditions $u_D$ and source term $f$. Both are chosen so as to yield
an exact analytic result against which we can compare the numerical results.

$$
\begin{align}
  u'   &= \nabla^2 u + f     \quad\text{in the unit square} \\
  u    &= u_D  \hphantom{u+f}\quad\text{on the boundary} \\
  u    &= u_0  \hphantom{u+f}\quad\;\text{at $t = 0$}
\end{align}
$$
with
$$
\begin{align}
  u_D  &= 1 + x^2 + \alpha y^2 + \beta t \\
  u_0 &= u_D(t=0) \\[0.5ex]
  f    &= \beta - 2 (1 + \alpha)
\end{align}
$$

```python
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import time as tm

# import os
# import sys
# import re
# from IPython.display import Image
# from IPython.core.interactiveshell import InteractiveShell
# InteractiveShell.ast_node_interactivity = "all"
```

```python
from fenics import *
```

```python
## set up the problem and define the main simulation function, evolve()

# simulation parameters
# -- boundary function
alpha = 3         # parameter alpha
beta = 1.2        # parameter beta
# -- time step
T = 2.0           # total simulation time
n_steps = 10      # number of time steps
dt = T / n_steps  # size of time step
nip = 1           # number of intervals between plots
# -- mesh density
nx = ny = 8       # mesh density along axes

# create mesh and define function space
mesh = UnitSquareMesh(nx, ny)
V = FunctionSpace(mesh, 'P', 1)

# define boundary condition
u_D = Expression('1 + x[0]*x[0] + alpha*x[1]*x[1] + beta*t',
                 degree=2, alpha=alpha, beta=beta, t=0)
u_D_min = 1.
u_D_max = 1. + 1 + alpha + beta * T

def boundary(x, on_boundary):
    return on_boundary

bc = DirichletBC(V, u_D, boundary)

# define initial value
u_n = interpolate(u_D, V)

# define variational problem
u = TrialFunction(V)
v = TestFunction(V)
f = Constant(beta - 2 - 2*alpha)
F = u*v*dx + dt*dot(grad(u), grad(v))*dx - (u_n + dt*f)*v*dx
a, L = lhs(F), rhs(F)

# define time-evolution function
def evolve():

    # compute and report initial err at vertices
    t = 0
    u_D.t = t
    u_x = interpolate(u_D, V)
    err = np.abs(u_n.vector() - u_x.vector()).max()
    yield 0, u_n, err
    #print('idx = %2d: t = %.2f: err = %.3g' % (0, 0., err))

    # time-stepping
    u = Function(V)
    for n in range(1, n_steps + 1):

        # update current time
        t += dt
        u_D.t = t

        # compute solution
        solve(a == L, u, bc)

        # compute and report current state and max err at vertices
        if n % nip == 0:
            u_x = interpolate(u_D, V)
            err = np.abs(u.vector() - u_x.vector()).max()
            yield t, u, err
            #print('idx = %2d: t = %.2f: err = %.3g' % (n, t, err))

        # update previous solution
        u_n.assign(u)
```

```python
n_rows = 3
n_cols = 5
fig_wd = 15
# default sizing here yields unit aspect ratio
plt.figure(figsize = (fig_wd, fig_wd * n_rows // n_cols))

idx = 0
for t, u, e in evolve():
    idx += 1
    print('idx = %2d: t = %.2f: err = %.3g' % (idx, t, e))
    ax = plt.subplot(n_rows, n_cols, idx)
    plot(u, vmin=u_D_min, vmax=u_D_max)
```

```python

```


```python
!pip install qiskit
from qiskit import QuantumCircuit, execute, Aer
from qiskit.visualization import plot_histogram, plot_bloch_multivector
import math

def y_measure(qc, qubit, cbit):
    qc.sdg(qubit)
    qc.h(qubit)
    qc.measure(qubit, cbit)
    qc.h(qubit)
    qc.s(qubit)

qc = QuantumCircuit(1,1) # create quantum circuit with one qubit and one classical bit
initial_state = [1/math.sqrt(2), -1j/math.sqrt(2)] # define our initial state (1 in Y-basis)
qc.initialize(initial_state, 0) # add initialization operation to qubit 0
y_measure(qc, 0,0) # measure qubit 0 and store the results in classical bit 0

backend = Aer.get_backend('statevector_simulator') # Tell Qiskit how to simulate our circuit
result = execute(qc,backend).result() # Do the simulation, returning the result
display(plot_histogram(result.get_counts())) # Display the results in a histogram
plot_bloch_multivector(result.get_statevector())
```

```python

```


# 程序的基本结构（二）：操作符与函数

如果源代码是我们写出来的故事，那么“操作符与函数”就是故事中的事件，是电影中的一幕幕场景。

上一章里我们说过，数据就是程序里的角色，同类型的数据之间可以对话和互动，而操作符与函数就是他们之间互动的方式。我们先来看操作符。

## 常用操作符

Python（其他编程语言也差不多）有好多类操作符，我们这里介绍最常用的四类：算术操作符、大小比较操作符、赋值操作符和逻辑运算操作符。

### 算术操作符

算术操作符就是加减乘除之类的，专门针对整数和小数。

```python
a = 6
b = 30
f = 3.14
g = 0.7
```

```python
a + b
```

    36

```python
a - b
```

    -24

```python
a * b
```

    180

```python
b / a
```

    5.0

```python
a * g
```

    4.199999999999999

```python
g / f
```

    0.22292993630573246

我们可以看出，整数之间的加减乘结果还是整数，而除法结果是浮点数（即使除尽了也是）；而浮点数无论是和整数还是浮点数运算，结果都是浮点数。

除了加减乘除，还有一个乘方操作符 `x ** y`，做幂运算，就是 $x^y$（x 的 y 次方），其中 x 和 y 都可以是整数或者浮点数，如果都是整数结果也是整数，否则结果是浮点数：

```python
b ** 2
```

    900

```python
b ** a
```

    729000000

```python
f ** a
```

    958.4685972127362

```python
g ** f
```

    0.3262930666604556

最后两个算术运算符是 `//` 和 `%`，是整除运算符，分别用来计算 x 除以 y 的商和余数：

```python
# 计算 30 除以 6 的商
b // a
```

    5

```python
# 计算 30 除以 6 的余数
b % a
```

    0

```python
# 计算 30 除以 4 的商
b // 4
```

    7

```python
# 计算 30 除以 4 的余数
b % 4
```

    2

```python
# 浮点数也可以的
f // 2
```

    1.0

```python
f % 2
```

    1.1400000000000001

```python
g // f # 浮点数除浮点数也没问题
```

    0.0

```python
g % f
```

    0.7

上面算 `f`（值为 `3.14`）除以 2 的余数 `f % 2` 出现了奇怪的结果，这是计算机浮点误差导致的，下面会解释。

### 插播：代码注释

上面的代码中井号 `#` 开始的部分叫做“注释”（井号这个名字是我们中国人才懂的，在英语世界里这个符号有好些个名字，比如 *number*、*pound*、*hash*），注释是源代码里被编译器和解释器忽略的部分，也就是说这部分写了机器也不看，是写给我们人类的。程序复杂起来之后，不管编程语言本身多么易懂或者多么接近自然语言，还是会有看不明白的可能，所以作为一种好习惯我们应该在需要时写下一些重要的注释，方便别人阅读和理解我们写的代码，也方便自己，因为难保过些时自己都忘了为啥这么写！

有不少编程语言还提供配套的工具，可以自动分析源代码里的注释来生成程序文档，比如 Python 的 [pydoc](https://docs.python.org/2/library/pydoc.html)。

注释并非越多越好，注释一定是“光看源代码难以知晓的重要信息”，我个人的风格是程序尽量写得不需要注释，然后再写少量必须注释的内容。比如写了一个经典算法的实现，参考的算法说明可以作为一个链接附上，方便不了解这个算法原理的人参考；再比如某处代码用到了一个临时假定，是未来有可能变化的，也很适合记录下来，并且标记一个 TODO 之类的标签，这样以后你只要全文检索 TODO 就能找出所有这类假定，检查是不是情况已经变化或者有了更好的解决方案，而做出相应修改。

`#` 既可以用在行首也可以在中间，总之 `#` 之后直到行尾的部分就是注释。

因为对编译器和解释器来说，注释无非就是“人类自己看而我不需要理”的意思，所以有时候我们想暂时移除某行代码又不想删掉（因为等下可能还要用），就可以临时在这行代码前加个 `#`，想加回来的时候去掉 `#` 就好了，这也是个常用的手法。

### 大小比较操作符

比较大小的一组操作符，`> < >= <= == !=` 分别是大于、小于、大于或等于、小于或等于、等于和不等于关系。这几个操作符比较它左右的数据的大小关系，如果符合操作符的含义就返回布尔值真（`True`），否则返回布尔值（`False`）。在 Python 里这些操作符不仅可以比较数字，还可以比较其他很多类型的数据，下面有一些简单的例子。

```python
a < b
```

    True

```python
a >= a
```

    True

```python
a <= f
```

    False

```python
a + b == 36
```

    True

```python
a + b != 36
```

    False

```python
'aaa' < 'aab'
```

    True

```python
a * g == 4.2
```

    False

上面的比较有没有什么问题？前面都没啥，但是注意最后一个，a 的值是 6，而 g 的值是 0.7，他们乘起来就是 4.2 啊，为啥最后一个比较返回是 `False` 呢？我们来看看计算机算出来的 `a * g` 是多少：

```python
a * g
```

    4.199999999999999

这是什么鬼啊？不要惊慌，这叫浮点误差，下面来简单解释下。

### 插播：浮点误差

目前我们用的计算机叫“电子计算机”，用电平的高低表示数，电平高的是 1，低的是 0，只有这两种状态，也就是说计算机只认识 0 和 1。要用 0 和 1 表示我们要用到的各种数字，就要采用[二进制](https://zh.wikipedia.org/zh-cn/%E4%BA%8C%E8%BF%9B%E5%88%B6)计数法。

二进制计数法对于整数来说没啥问题，每一个十进制整数都对应唯一的二进制表示，但对于小数来说问题就来了，**绝大部分十进制小数是没法用有限位数表示为二进制小数的**，如果想知道为什么，可以看看[这篇文章](https://www.taowong.com/blog/2018/07/10/principle-of-computer-float-num.html)，如果看不明白也没关系，反正记住这个结论就行了。这个结论决定了在计算机里小数是以一种“近似值”的方式表示的，0.7 在计算机里是个近似等于 0.7 的二进制小数，这个小数乘上 6 之后的结果也是有微小的误差的，转换回十进制小数给我们看结果时就会出现上面那样的奇怪现象。

那么我们怎么解决这个问题呢？分两种情况：
1. 如果需要绝对精确的结果，那么不要用小数，只用整数进行运算，然后自己计算小数点的位置，绝大部分计算器就是这么做的；
2. 如果不需要绝对精确，那么只要结果的误差足够小就可以了，这时候可以用浮点数进行运算，但比较时不要直接比较结果是不是相等（`==`），而用误差范围来比较：

```python
s = 4.2 # 这是预期结果
r = 0.001 # 这是可容忍误差

abs(a * g - s) < r # 如果 (a * g - s) 的绝对值比 r 小，就算结果合格
```

    True

上面的代码里又出现了新东西，`abs(...)` 是什么呢？这是个函数，函数也是一个值，`abs` 函数的值等于后面括号里的值的绝对值。关于函数我们下面会讲到，这是个提前引用（*forward reference*）的例子。

### 赋值操作符

赋值操作符我们都见过和用过了，就是上一章赋值语句里的等号 `=`，正是为了避免和这个操作符冲突，大小比较中的“等于”才会采用双等号 `==` 表示。

赋值操作符本身很简单，就是**把右边的值赋给左边的变量（无论右边的东西多复杂，都先算出它的值）**。比如：

```python
c = a + b # 先计算赋值等号右边的部分，a + b 得到值为 36，把 36 赋给左边的变量 c
c
```

    36

赋值操作符有一些变体，比如下面这个:

```python
c += 2
c
```

    38

上面的 `c += 2` 等价于 `c = c + 2`，你可能觉得 `c = c + 2` 有点怪，赋值操作符左右都有 `c`，但回顾一下赋值操作符的定义，就能理解了：永远要先算出右边的值，`c` 这时的值是 36，那么 `c + 2` 的值就是 38，把 38 赋给左边的变量 `c`，于是 `c` 的值成为 38。而 `c += 2` 没啥特别，就是 `c = c + 2` 的一种简化写法。

类似的还有 `-= *= /= %= **= //=` 这一组赋值操作符的变体，与 `+=` 同理。

### 逻辑运算操作符

逻辑运算操作符是布尔值之间的运算，结果也是布尔值。我们知道布尔值是逻辑上的真和假，而逻辑运算操作符就对应了逻辑代数（也叫布尔代数）里的各种运算。

顺便说一句，逻辑代数可是很有来头的东西，它是数理逻辑的基础，由乔治·布尔（George Boole）创立，所以布尔代数和布尔值都是因为这位奠基人得名；这些理论是逻辑电路的基础，逻辑电路又是电子计算机的基础，所以可以说我们能坐在这里学编程，完全是因为有这些东西存在！

另外逻辑运算也是我们下一章介绍程序中的逻辑判断和分支的基础，所以真的是非常重要的部分。

基本的逻辑运算就三个：`and`、`or`、`not`，即 **与**、**或**、**否**。
* 两个布尔值如果都是“真”，他们的 **与** 运算结果也为“真”，其他情况下结果都是“假”；
* 两个布尔值中只要有一个是“真”，他们的 **或** 运算结果就为“真”，如果两个都是“假”，那么 **或** 运算结果为“假”；
* 布尔值“真”的 **否** 运算结果为“假”，布尔值“假”的 **否** 运算结果为“真”。

通过这三个操作符我们可以组合复杂的逻辑运算：

```python
t = True
f = False
```

```python
t and f
```

    False

```python
t or f
```

    True

```python
not t
```

    False

```python
t and (not f)
```

    True

```python
(not t) or f
```

    False

请务必搞清楚上面每一个式子的含义以及为什么是这个结果。

### 操作符小结

* 操作符是对数据进行操作的一些符号，最常用的有算术运算、大小比较、赋值、逻辑运算等；
* 通过例子学习和掌握常用的这几类操作符；
* 了解代码注释的语法和意义；
* 了解浮点表示误差和规避方法。

## 函数

程序里的函数概念完全来自数学里的函数。我们先来看看数学里的函数。

### 函数的基本概念

在数学里，我们可以定义一个函数，函数有一些*自变量*，也就是一些**输入**，**函数定义**就是定义如何通过这些输入计算出*函数的值*，也就是**输出**。下面是个典型的函数定义的例子：

$
\begin{align}
f(a, b) = \sqrt{a^2 + b^2}
\end{align}
$

函数定义好之后，随时可以用具体的值送进去算出一个具体的函数值，比如：

$
\begin{align}
f(1, 2) = \sqrt{5}
\end{align}
$

$
\begin{align}
f(3, 4) = 5
\end{align}
$

$
\begin{align}
f(2, 6) = 2\sqrt{10}
\end{align}
$

程序里的函数也是这样，我们先要定义函数，也就是定义它的名字（叫做 **函数名** *function name*）、若干输入变量（叫做 **参数** *argument*）以及如何通过输入变量算出函数的值（叫做 **返回值** *return value*）；一旦定义好，就可以在程序的其他地方随时使用这个函数，给它具体的参数值，它就给出具体的返回值。

### 调用现成的函数

我们先看看怎么用函数，术语叫做函数**调用**（*call*），因为 Python 本身自带了很多已经定义好的函数，我们可以直接拿来用。其中有一些我们之前已经用过了：
* `print(a, b,...)`：在命令行输出界面打印输入的参数（可以是各种类型的数据）；
* `type(x)`：返回参数 x 的数据类型；
* `abs(x)`：返回参数 x（整数或者浮点数）的绝对值。

这几个都是 Python 的内置函数，可以直接用。我们下面来看两个其他的例子。

第一个例子是另一个 Python 内置函数，叫做 `isinstance(x, c)`，它有两个输入参数，第一个是一个数据，第二个是一个数据类型，如果第一个数据是第二个数据类型，`isinstance` 函数返回 `True`，否则就返回 `False`。所以 `isinstance` 可以用来判断某个值或者变量是不是某种类型，下面是例子：

```python
isinstance(a, int)
```

    True

```python
isinstance(a, float)
```

    False

```python
isinstance(g, float)
```

    True

```python
isinstance(True, str)
```

    False

```python
isinstance(True, bool)
```

    True

```python
isinstance('abracadabra', str)
```

    True

```python
isinstance(True, bool) and not isinstance('abracadabra', float)
```

    True

同样的，请务必搞清楚上面每一个式子的含义以及为什么是这个结果。

第二个例子是 Python 的平方根函数 `math.sqrt(x)`，这个函数前面有个 `math`，这表示函数 `sqrt(x)` 不在 Python 的内置环境中，而是放在一个叫 `math` 的函数包（学名叫 **模块** *module*）里，关于模块我们以后会专门介绍。要使用这种函数，第一种方法是：
* 先 **引入**（*import*）对应的函数包；
* 使用时函数名前面要带上模块的名字，中间加个 `.`。

就像下面这样：

```python
import math
math.sqrt(16)
```

    4.0

另外一种方法是，从对应的模块里指明引入这一个函数，使用时就不用带上模块名了：

```python
from math import sqrt
sqrt(40)
```

    6.324555320336759

我们可以看到 `sqrt(x)` 函数可以接受整数或者浮点数输入，返回值是浮点数。

### 定义函数

有了 `sqrt(x)` 这个函数，我们可以定义一个函数来实现前面举例的那个数学里的函数 `f(a, b)` 了。在 Python 里函数的定义以关键字 `def` 开始，后面依次是函数名、小括号括起来的参数列表和一个冒号，之后的代码就是函数的算法定义，直到关键字 `return` 开始的 **返回语句**，这个返回语句会终止函数的运行，并把 `return` 后面的值作为函数的返回值，回到调用函数的地方。看起来有点抽象，我们看看例子：

```python
from math import sqrt

def f(a, b):
    return sqrt(a**2 + b**2)

result = f(3, 4)
print(result)
```

    5.0

上面的代码很短，但是很重要，我们务必要搞懂这里的每一个细节。

首先第一行是引入 `sqrt(x)` 函数，因为我们下面定义的函数要用到它。

然后是函数定义 `def f(a, b):`，关键字 `def` 打头，后面是函数名 `f`、参数列表 `(a, b)` 和一个冒号，这表示函数 `f` 接受两个参数，一个叫 `a` 一个叫 `b`。下面一行就是**函数体**（*function body*），里面可以写很多东西，只要最后有一个 `return` 指明函数返回值就行，注意 Python 的函数可以没有返回值（`return` 后面什么都不写就行了），也可以返回多个值（`return` 后面写多个值，用逗号隔开）。

我们这个例子因为很简单，就一句话就能搞定：函数返回值等于 a 平方加 b 平方再开方。注意函数体的代码缩进了一些，和其他的行都是顶着行首书写不一样，这是 Python 的缩进规则要求的，下面的插播会做出解释。

然后是调用函数的例子，它是这么进行的：
1. 首先用 3 和 4 作为参数来调用 `f()` 函数；
2. 函数 `f()` 开始执行，把值 3 赋给参数变量 a，4 赋给 b，然后执行函数体；
3. 函数体计算出返回值 5.0 并返回；
4. 用返回值 5.0 替换掉 `f()` 的位置，相当于执行赋值 `result = 5.0`
5. 用 5.0 作为参数值调用内置的 `print()` 函数，打印出 5.0（`print()` 没有返回值）。

顺便说一句，熟练之后我们可以把后两行简化成一句 `print(f(3, 4))`，效果是完全一样的。

### 插播：代码缩进

所谓代码缩进就是一行代码不顶格写，而是在行首空若干格再写。缩进可以很多层，比如 Python 中第一层缩进空四格，第二层缩进空八格，第三层缩进空十二格，依此类推。程序员都喜欢用“**等宽字体**（*fix-width font*）”，就是因为等宽字体在缩进时非常整齐美观。

缩进是为了让代码层次更加鲜明地呈现出来。比如上面函数定义的代码，函数体的部分是“隶属于”函数定义 `def` 语句的，是它的组成部分，所以缩进一层表示这个从属关系；而后面的函数调用则不是，所以最后两行不缩进，和 `def` 语句平齐，代表是同一层级的代码。

在 Python 中缩进是强制的，如果不进行正确的缩进，比如如果函数体没有正确的缩进，解释器会因为找不到必须的部分而报错。

目前主流的程序编辑器都提供了自动缩进、自动排版的功能，在 VSCode 和 Jupyter Notebook 中书写代码时大部分时候都会自动正确的缩进，所以很方便。

对于初学者来说，知道有这么回事，然后多看，慢慢就会习惯成自然，和多听多看学语言是差不多的道理。

### 函数的意义

函数是编程中最重要的概念之一。实现它有助于我们实现程序的“**模块化**（*modulization*）”，我们可以把特定的工作用一个函数来实现，然后在需要的地方反复地调用它。这是一种代码重用的方法，一个函数就好像乐高积木里的一个组件，我们可以通过组合各种不同功能的函数来实现复杂的目标。

函数还是协作编程的基础。如果我们实现了一个很有用的函数，我们可以写一个文档说明函数的输入参数和返回值是什么，这样的文档叫做**调用接口**（*interface*），别人都可以照此使用我们写的函数，甚至不需要了解它是怎么实现的（比如我们就直接拿了 `math.sqrt()` 来用而不需要自己写一个）。函数天生就把自己的接口和实现分离开，让协作和复用成果更容易。

**模块化** 和 **接口独立** 是编程时最重要的两个思维模型（没有之一），我们在后面还会深入介绍。

### 函数小结

* 我们可以通过声明函数名、函数参数列表以及计算函数返回值的算法来定义一个函数；在 Python 中通过 `def` 和 `return` 两个关键字来做这件事；
* 定义好的函数可以随时被调用，送入具体的参数值，得到函数返回值，返回值可以用于赋值也可以用作参数值来调用其他函数；
* 函数是编程中重要的概念，我们在第四部分还会深入讲解；
* 了解 Python 的缩进规则。


```
from sympy import I, Matrix, symbols, Symbol, eye
from datetime import datetime
import numpy as np
import pandas as pd

```

```
'''
Rotas
'''
R1 = Matrix([[0,"L1_p1",0,0,0,0,0],[0,0,"L1_p3",0,0,0,0],[0,0,0,"L1_v1",0,0,0],[0,0,0,0,"L1_p4",0,0],[0,0,0,0,0,"L1_v3",0],[0,0,0,0,0,0,"L1_v4"],[0,0,0,0,0,0,0]])
R2 = Matrix([[0,"L2_p2",0,0,0,0,0],[0,0,"L2_p3",0,0,0,0],[0,0,0,"L2_v2",0,0,0],[0,0,0,0,"L2_p5",0,0],[0,0,0,0,0,"L2_v3",0],[0,0,0,0,0,0,"L2_v5"],[0,0,0,0,0,0,0]])
R3 = Matrix([[0,"L3_p5",0,0,0,0,0],[0,0,"L3_p3",0,0,0,0],[0,0,0,"L3_v5",0,0,0],[0,0,0,0,"L3_p1",0,0],[0,0,0,0,0,"L3_v3",0],[0,0,0,0,0,0,"L3_v1"],[0,0,0,0,0,0,0]])
```

```
'''
Seções de bloqueio
'''
T1 = Matrix([[0, "p1"],["v1", 0]])
T2 = Matrix([[0, "p2"],["v2", 0]])
T3 = Matrix([[0, "p3"],["v3", 0]])
T4 = Matrix([[0, "p4"],["v4", 0]])
T5 = Matrix([[0, "p5"],["v5", 0]])
```

```
def kronSum(A,B):
  m = np.size(A,1)
  n = np.size(B,1)
  A = np.kron(A, np.eye(n))
  B = np.kron(np.eye(m),B)
  return A + B
```

```
momento_inicio = datetime.now()
'''
Algebra de rotas
'''
rotas = kronSum(R1,R2)
rotas = kronSum(rotas,R3)

'''
Algebra de seções
'''
secoes = kronSum(T1,T2)
secoes = kronSum(secoes,T3)
secoes = kronSum(secoes,T4)
secoes = kronSum(secoes,T5)
'''
Algebra de sistema
'''
sistema = np.kron(rotas, secoes)
# calcula tempo de processamento
tempo_processamento = datetime.now() - momento_inicio
```

```
sistema = pd.DataFrame(data=sistema,index=list(range(1,np.size(sistema,0)+1)), columns=list(range(1,np.size(sistema,1)+1)))
```

```
sistema.shape
```

    (10976, 10976)

```
print(tempo_processamento)
```

    0:03:04.477018

```
sistema
```

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      ...
      10937
      10938
      10939
      10940
      10941
      10942
      10943
      10944
      10945
      10946
      10947
      10948
      10949
      10950
      10951
      10952
      10953
      10954
      10955
      10956
      10957
      10958
      10959
      10960
      10961
      10962
      10963
      10964
      10965
      10966
      10967
      10968
      10969
      10970
      10971
      10972
      10973
      10974
      10975
      10976

      1
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      1.0*L3_p5*p5
      1.0*L3_p5*p4
      0
      1.0*L3_p5*p3
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      2
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      1.0*L3_p5*v5
      0
      0
      1.0*L3_p5*p4
      0
      1.0*L3_p5*p3
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      3
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      1.0*L3_p5*v4
      0
      0
      1.0*L3_p5*p5
      0
      0
      1.0*L3_p5*p3
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      4
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      1.0*L3_p5*v4
      1.0*L3_p5*v5
      0
      0
      0
      0
      1.0*L3_p5*p3
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      5
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      1.0*L3_p5*v3
      0
      0
      0
      0
      1.0*L3_p5*p5
      1.0*L3_p5*p4
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      10972
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10973
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10974
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10975
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10976
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

10976 rows × 10976 columns

```
sistema.loc[4740,4788]
```

    1.0*L3_p3*p1

```
momento_inicio = datetime.now()
colunas = ['denode', 'paranode', 'aresta']
grafo = pd.DataFrame(columns=colunas)
r = 1
c = 1
for j in range(np.size(sistema,0)):
  for i in range(np.size(sistema,0)):
    if sistema.loc[r,c]==0 and c < np.size(sistema,0):
      c += 1
    elif c < np.size(sistema,0):
      grafo.loc[len(grafo)+1] = (r, c, sistema.loc[r,c])
      c += 1
    else:
      c = 1
      r += 1
tempo_processamento = datetime.now() - momento_inicio
print(tempo_processamento)
```

    0:49:20.053598

```
grafo
```

      denode
      paranode
      aresta

      1
      1
      34
      1.0*L3_p5*p5

      2
      1
      35
      1.0*L3_p5*p4

      3
      1
      37
      1.0*L3_p5*p3

      4
      1
      41
      1.0*L3_p5*p2

      5
      1
      49
      1.0*L3_p5*p1

      ...
      ...
      ...
      ...

      141101
      10944
      10960
      1.0*L3_v1*v1

      141102
      10944
      10968
      1.0*L3_v1*v2

      141103
      10944
      10972
      1.0*L3_v1*v3

      141104
      10944
      10974
      1.0*L3_v1*v4

      141105
      10944
      10975
      1.0*L3_v1*v5

141105 rows × 3 columns

```
grafo['aresta'] = grafo['aresta'].astype('str')
grafo
```

      denode
      paranode
      aresta

      1
      1
      34
      1.0*L3_p5*p5

      2
      1
      35
      1.0*L3_p5*p4

      3
      1
      37
      1.0*L3_p5*p3

      4
      1
      41
      1.0*L3_p5*p2

      5
      1
      49
      1.0*L3_p5*p1

      ...
      ...
      ...
      ...

      141101
      10944
      10960
      1.0*L3_v1*v1

      141102
      10944
      10968
      1.0*L3_v1*v2

      141103
      10944
      10972
      1.0*L3_v1*v3

      141104
      10944
      10974
      1.0*L3_v1*v4

      141105
      10944
      10975
      1.0*L3_v1*v5

141105 rows × 3 columns

```
new = grafo["aresta"].str.split("*", n = -1, expand = True)
grafo["aresta"]=new[1]
grafo["semaforo_secao"]=new[2]
new = grafo["aresta"].str.split("_", n = -1, expand = True)
grafo["semaforo_trem"]=new[1]
grafo['coincide'] = np.where(grafo['semaforo_secao']==grafo['semaforo_trem'], True, False)
grafo
```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      35
      L3_p5
      p4
      p5
      False

      3
      1
      37
      L3_p5
      p3
      p5
      False

      4
      1
      41
      L3_p5
      p2
      p5
      False

      5
      1
      49
      L3_p5
      p1
      p5
      False

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      141101
      10944
      10960
      L3_v1
      v1
      v1
      True

      141102
      10944
      10968
      L3_v1
      v2
      v1
      False

      141103
      10944
      10972
      L3_v1
      v3
      v1
      False

      141104
      10944
      10974
      L3_v1
      v4
      v1
      False

      141105
      10944
      10975
      L3_v1
      v5
      v1
      False

141105 rows × 6 columns

```
grafo = pd.DataFrame(data=grafo)
```

```
grafo.drop(grafo[grafo.coincide == False].index, inplace=True)
grafo.reset_index(drop = True)
grafo.index = np.arange(1, len(grafo) + 1)
grafo
```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      233
      L2_p2
      p2
      p2
      True

      3
      1
      1585
      L1_p1
      p1
      p1
      True

      4
      2
      234
      L2_p2
      p2
      p2
      True

      5
      2
      1586
      L1_p1
      p1
      p1
      True

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      14108
      10940
      10956
      L3_v1
      v1
      v1
      True

      14109
      10941
      10957
      L3_v1
      v1
      v1
      True

      14110
      10942
      10958
      L3_v1
      v1
      v1
      True

      14111
      10943
      10959
      L3_v1
      v1
      v1
      True

      14112
      10944
      10960
      L3_v1
      v1
      v1
      True

14112 rows × 6 columns

```
# Passo 1:
alcancavel = [1]
r = 1
for i in range(np.size(grafo,0)):
  de = grafo.loc[r]['denode']
  para = grafo.loc[r]['paranode']
  if (de in alcancavel):
    alcancavel.append(para)
    r = r + 1
  else:
    r = r + 1
```

```
alcancavel.sort()
```

```
grafo = grafo[grafo.denode.isin(alcancavel)]
grafo
```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      233
      L2_p2
      p2
      p2
      True

      3
      1
      1585
      L1_p1
      p1
      p1
      True

      52
      34
      70
      L3_p3
      p3
      p3
      True

      53
      34
      266
      L2_p2
      p2
      p2
      True

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      14034
      10786
      10822
      L3_p3
      p3
      p3
      True

      14051
      10822
      10853
      L3_v5
      v5
      v5
      True

      14069
      10853
      10901
      L3_p1
      p1
      p1
      True

      14089
      10901
      10929
      L3_v3
      v3
      v3
      True

      14097
      10929
      10945
      L3_v1
      v1
      v1
      True

300 rows × 6 columns

```
grafo.reset_index(drop = True)
grafo.index = np.arange(1, len(grafo) + 1)
grafo
```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      233
      L2_p2
      p2
      p2
      True

      3
      1
      1585
      L1_p1
      p1
      p1
      True

      4
      34
      70
      L3_p3
      p3
      p3
      True

      5
      34
      266
      L2_p2
      p2
      p2
      True

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      296
      10786
      10822
      L3_p3
      p3
      p3
      True

      297
      10822
      10853
      L3_v5
      v5
      v5
      True

      298
      10853
      10901
      L3_p1
      p1
      p1
      True

      299
      10901
      10929
      L3_v3
      v3
      v3
      True

      300
      10929
      10945
      L3_v1
      v1
      v1
      True

300 rows × 6 columns

```
# Passo 2:
ultimo = alcancavel[-1]
alcancavel = [ultimo]
r = np.size(grafo,0)
for i in range(np.size(grafo,0),1,-1):
  para = grafo.loc[r]['paranode']
  de = grafo.loc[r]['denode']
  if (para in alcancavel):
    alcancavel.append(de)
    r = r - 1
  else:
    r = r - 1

```

```
grafo = grafo[grafo.paranode.isin(alcancavel)]
grafo
```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      233
      L2_p2
      p2
      p2
      True

      3
      1
      1585
      L1_p1
      p1
      p1
      True

      4
      34
      70
      L3_p3
      p3
      p3
      True

      5
      34
      266
      L2_p2
      p2
      p2
      True

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      296
      10786
      10822
      L3_p3
      p3
      p3
      True

      297
      10822
      10853
      L3_v5
      v5
      v5
      True

      298
      10853
      10901
      L3_p1
      p1
      p1
      True

      299
      10901
      10929
      L3_v3
      v3
      v3
      True

      300
      10929
      10945
      L3_v1
      v1
      v1
      True

266 rows × 6 columns

```
grafo.reset_index(drop = True)
grafo.index = np.arange(1, len(grafo) + 1)
grafo

```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      233
      L2_p2
      p2
      p2
      True

      3
      1
      1585
      L1_p1
      p1
      p1
      True

      4
      34
      70
      L3_p3
      p3
      p3
      True

      5
      34
      266
      L2_p2
      p2
      p2
      True

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      262
      10786
      10822
      L3_p3
      p3
      p3
      True

      263
      10822
      10853
      L3_v5
      v5
      v5
      True

      264
      10853
      10901
      L3_p1
      p1
      p1
      True

      265
      10901
      10929
      L3_v3
      v3
      v3
      True

      266
      10929
      10945
      L3_v1
      v1
      v1
      True

266 rows × 6 columns

```
grafo.to_csv('grafo.csv', sep=";")
from google.colab import files
files.download('grafo.csv')
```

    <IPython.core.display.Javascript object>

    <IPython.core.display.Javascript object>

```
grafo
```

      denode
      paranode
      aresta
      semaforo_secao
      semaforo_trem
      coincide

      1
      1
      34
      L3_p5
      p5
      p5
      True

      2
      1
      233
      L2_p2
      p2
      p2
      True

      3
      1
      1585
      L1_p1
      p1
      p1
      True

      4
      34
      70
      L3_p3
      p3
      p3
      True

      5
      34
      266
      L2_p2
      p2
      p2
      True

      ...
      ...
      ...
      ...
      ...
      ...
      ...

      262
      10786
      10822
      L3_p3
      p3
      p3
      True

      263
      10822
      10853
      L3_v5
      v5
      v5
      True

      264
      10853
      10901
      L3_p1
      p1
      p1
      True

      265
      10901
      10929
      L3_v3
      v3
      v3
      True

      266
      10929
      10945
      L3_v1
      v1
      v1
      True

266 rows × 6 columns


# Notation: Converter Numeric

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

```python
np.set_printoptions(
    linewidth=140,
    precision=3,
    # suppress=False,
)
converter = mechkit.notation.Converter()
```

```python
# ### Mandel6: symmetric, Mandel9: Mandel6 + asymmetric parts
basics = mechkit.tensors.Basic()
```

```python
# Symmetric identity
print(converter.to_mandel6(basics.I4s))
print(converter.to_mandel9(basics.I4s))
```

    [[1. 0. 0. 0. 0. 0.]
     [0. 1. 0. 0. 0. 0.]
     [0. 0. 1. 0. 0. 0.]
     [0. 0. 0. 1. 0. 0.]
     [0. 0. 0. 0. 1. 0.]
     [0. 0. 0. 0. 0. 1.]]
    [[ 1.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  1.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  1.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  1.000e+00  0.000e+00  0.000e+00 -4.266e-17  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  1.000e+00  0.000e+00  0.000e+00  4.266e-17  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  1.000e+00  0.000e+00  0.000e+00 -4.266e-17]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]]

```python
# Asymmetric identity
print(converter.to_mandel6(basics.I4a))
print(converter.to_mandel9(basics.I4a))
```

    [[0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0.]]
    [[ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00 -4.266e-17  0.000e+00  0.000e+00  1.000e+00  0.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  4.266e-17  0.000e+00  0.000e+00  1.000e+00  0.000e+00]
     [ 0.000e+00  0.000e+00  0.000e+00  0.000e+00  0.000e+00 -4.266e-17  0.000e+00  0.000e+00  1.000e+00]]

### Convenient autodetection of the notation: Specify only target

```python
tensor = np.ones((3, 3, 3, 3))
mandel6 = converter.to_mandel6(tensor)
# Pass through
print(converter.to_tensor(mandel6))
print(converter.to_mandel6(mandel6))
print(converter.to_mandel9(mandel6))
```

    [[[[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]

      [[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]

      [[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]]

     [[[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]

      [[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]

      [[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]]

     [[[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]

      [[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]

      [[1. 1. 1.]
       [1. 1. 1.]
       [1. 1. 1.]]]]
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]
    [[1.    1.    1.    1.414 1.414 1.414 0.    0.    0.   ]
     [1.    1.    1.    1.414 1.414 1.414 0.    0.    0.   ]
     [1.    1.    1.    1.414 1.414 1.414 0.    0.    0.   ]
     [1.414 1.414 1.414 2.    2.    2.    0.    0.    0.   ]
     [1.414 1.414 1.414 2.    2.    2.    0.    0.    0.   ]
     [1.414 1.414 1.414 2.    2.    2.    0.    0.    0.   ]
     [0.    0.    0.    0.    0.    0.    0.    0.    0.   ]
     [0.    0.    0.    0.    0.    0.    0.    0.    0.   ]
     [0.    0.    0.    0.    0.    0.    0.    0.    0.   ]]

### Vectorized explicit converter

```python
expl_converter = mechkit.notation.ExplicitConverter()
```

```python
tensors = np.ones((2, 5, 3, 3, 3, 3))  # We have 5 times 2 tensors of fourth order
```

```python
mandel6s = expl_converter.convert(
    inp=tensors, source="tensor", target="mandel6", quantity="stiffness"
)
```

```python
for i in range(2):
    for j in range(5):
        print(f"Tensor at position {i}, {j} in Mandel6 notation")
        print(mandel6s[i, j, ...])
        print()
```

    Tensor at position 0, 0 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 0, 1 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 0, 2 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 0, 3 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 0, 4 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 1, 0 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 1, 1 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 1, 2 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 1, 3 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]

    Tensor at position 1, 4 in Mandel6 notation
    [[1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.    1.    1.    1.414 1.414 1.414]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]
     [1.414 1.414 1.414 2.    2.    2.   ]]