# **Modelagem**

## **Instalação e importação**

```python
%reset -f
```

```python
#Instalar e importar bibliotecas:

try:
  import CoolProp
except ImportError:
  !pip install CoolProp

import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
import CoolProp.CoolProp as cp
from CoolProp.CoolProp import PropsSI as ps
from CoolProp.CoolProp import State as st
from tabulate import tabulate as tab
import numpy as np
from sympy import *
import pandas as pd

'''
from IPython.core.display import HTML
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
""")
'''
```

    '\nfrom IPython.core.display import HTML\nHTML("""\n<style>\n.output_png {\n    display: table-cell;\n    text-align: center;\n    vertical-align: middle;\n}\n</style>\n""")\n'

## **Unidade de medida**

```python
# Selecionar escala de temperatura:
Temperatura = 'c' #input('Temperatura em °C ou em K? (Digite c ou k): ')
```

## **Declaração de variáveis**

```python
# Declarar estados:

n = 12

fld = "Water"
cp.set_reference_state(fld,'DEF')
if Temperatura == 'k':
  Tnn = "Temperatura [K]"
else:
  Tnn = "Temperatura [°C]"

mnn = "Vazão mássica [kg/s]"
hnn = "Entalpia [kJ/kg]"
rhonn = "Massa específica [m³/kg]"
Pnn = "Pressão [kPa]"
snn = "Entropia [kJ/kg*K]"
Xnn = "Título"

estado = np.arange(1, n+1, 1)

T = np.linspace(0,0,n)
m = np.linspace(0,0,n)
h = np.linspace(0,0,n)
rho = np.linspace(0,0,n)
P = np.linspace(0,0,n)
s = np.linspace(0,0,n)
X = np.linspace(0,0,n)
```

## **Caldeira**

```python
# Combustível:

CH4 = np.array([1,4,0,0])
C2H6 = np.array([2,6,0,0])
C3H8 = np.array([3,8,0,0])
C4H10 = np.array([4,10,0,0])
CO2 = np.array([1,0,2,0])
N2 = np.array([0,0,0,2])
O2 = np.array([0,0,2,0])
H2O = np.array([0,2,1,0])

P_CH4 = 0.8897
P_C2H6 = 0.0592
P_C3H8 = 0.0191
P_C4H10 = 0.0109
P_C02 = 0.0121
P_N2 = 0.0089
P_O2 = 0.0001
P_N2_Ar = 0.79
P_O2_Ar = 0.21
```

```python
#Cálculos:

MM = np.array([12.011,1.008,15.999,14.007])
P_GN = [P_CH4,P_C2H6,P_C3H8,P_C4H10,P_C02,P_N2,P_O2]

M_CH4 = np.sum(CH4*MM)
M_C2H6 = np.sum(C2H6*MM)
M_C3H8 = np.sum(C3H8*MM)
M_C4H10 = np.sum(C4H10*MM)
M_CO2 = np.sum(CO2*MM)
M_N2 = np.sum(N2*MM)
M_O2 = np.sum(O2*MM)
M_H2O = np.sum(H2O*MM)
M_Air = M_N2*P_N2_Ar+M_O2*P_O2_Ar

M = [M_CH4,M_C2H6,M_C3H8,M_C4H10,M_CO2,M_N2,M_O2,M_N2,M_H2O]

F_M = []
for i in range(0,4):
    F_M.append(P_GN)

np.array(F_M)
quant_atomos_comb = np.array([CH4,C2H6,C3H8,C4H10,CO2,N2,O2])
m_comp_comb = np.sum(F_M*quant_atomos_comb.T,axis=1)
m_comp_comb[2] = m_comp_comb[2]*(-1)

P_GN.append(P_N2_Ar/P_O2_Ar) #acrecentar partes de O2
P_GN.append(P_O2_Ar/P_O2_Ar) ##acrecentar partes de N2

b = m_comp_comb[0]
c = m_comp_comb[1]/2
a = (m_comp_comb[2] + 2*b + c)/(2*P_GN[8])
d = (m_comp_comb[3] + 2*a*P_GN[7])/2

BLNC = [1,1,1,1,1,1,1,a,a]

resposta = np.zeros((9,4))
quant_atomos_molecula = np.array([CH4,C2H6,C3H8,C4H10,CO2,N2,O2,N2,O2])

for i in range(0,9):
    for j in range(0,4):
        resposta[i,j] = quant_atomos_molecula[i,j] * MM[j] * P_GN[i] * BLNC[i]

fuel = resposta[0:7,0:4]
air = resposta[7:9,0:4]
soma = np.sum(resposta)
M_fuel = np.sum(fuel) #Massa total de combustível
soma_air = np.sum(air) #Massa total de ar
total_elem = np.sum(fuel, axis=0,keepdims=True) #C_tot, H_tot, O_tot, N_tot

F_tot = total_elem/M_fuel

PCS = 33900*F_tot[0,0] + 141800*(F_tot[0,1] - (F_tot[0,2]/8))

w = PCS/2440 - 9*F_tot[0,1]
F_w = w/M_fuel

PCI = PCS - 2440*(9*F_tot[0,1] - F_w)

ȦḞ = a*(1+79/21)
AF = ȦḞ*(M_Air/M_fuel)

AF
```

    16.14939716388906

## **Estados**

```python
# Estado 1 - Entrada da turbina:

m[0] = 27.9        #kg/s
P[0] = 6495        #kPa
T[0] = 485 + 273.15       #K
st1 = st(fld, {'P':P[0],'T':T[0]})
h[0] = st1.h         #entalpia
s[0] = st1.s         #entropia
X[0] = st1.Q
rho[0] = st1.rho
```

```python
# Estado 2 - Primeira extração da turbina:

P[1] = 900      #kPa
st_isen2 = st(fld,{'P':P[1],'S':s[0]})     #turbina/bomba isentrópica ideal
h_isen2 = st_isen2.h
η_turb = 0.85       #eficiência da turbina
h[1] = h[0] - (h[0] - h_isen2) * η_turb
st2 = st(fld,{'P':P[1],'H':h[1]})
s[1] = st2.s
T[1] = st2.T
X[1] = st2.Q
rho[1] = st2.rho
```

```python
# Estado 3 - Segunda extração da turbina:

P[2] = 250     #kPa
st_isen3 = st(fld,{'P':P[2],'S':s[1]})
h_isen3 = st_isen3.h
h[2] = h[1] - (h[1] - h_isen3) * η_turb
st3 = st(fld,{'P':P[2],'H':h[2]})
s[2] = st3.s
rho[2] = st3.rho
T[2] = st3.T
X[2] = st3.Q
```

```python
# Estado 4 - Saída final da turbina / entrada do condensador:

T[3] = 51 + 273.15 #K
#P[3] = psi('P','T',T[3],'Q', 1, fld)/1000  #divide por 1000 pra sair em kPa
st4 = st(fld, {'T': T[3], 'Q': 1})
P[3] = st4.p
st_isen4 = st(fld,{'P':P[3],'S':s[2]})
h_isen4 = st_isen4.h
h[3] = h[2] - (h[2] - h_isen4) * η_turb
st4 = st(fld,{'P':P[3],'H':h[3]})
X[3] = st4.Q      #título
s[3]=st4.s
rho[3] = st4.rho
```

```python
# Estado 5 - Saída do condensador / entrada da bomba:

X[4] = 0
P[4] = P[3]
st5 = st(fld, {'P': P[4], 'Q': 0})
h[4] = st5.h
s[4] = st5.s
T[4] = st5.T
rho[4] = st5.rho
```

```python
# Estado 6 - Entrada do desaerador / saída da bomba:

P[5] = P[2]
st_isen6 = st(fld, {'P': P[5], 'S': s[4]})
h_isen6 = st_isen6.h
η_pump = 0.85       #eficiência das bombas
h[5] = (h_isen6 - h[4]) / η_pump + h[4]
st6 = st(fld, {'P':P[5], 'H': h[5]})
s[5] = st6.s
T[5] = st6.T
X[5] = st6.Q
rho[5] = st6.rho
```

```python
# Estado 7 - Saída do desaerador:

P[6] = P[2]
T[6] = 110 + 273.15
st7 = st(fld, {'P': P[6], 'T': T[6]})
h[6] = st7.h
s[6] = st7.s
X[6] = st7.Q
rho[6] = st7.rho
```

```python
# Estado 8 - Entrada da caldeira:

P[7] = P[0]
st_isen8 = st(fld, {'P': P[7], 'S': s[6]})
h_isen8 = st_isen8.h
h[7] = (h_isen8 - h[6]) / η_pump + h[6]
st8 = st(fld, {'P': P[7], 'H': h[7]})
T[7] = st8.T - 273.15
s[7] = st8.s
X[7] = st8.Q
rho[7] = st8.rho
```

```python
# Estado 9 - Entrada da bomba:

st9 = st7
h[8] = st9.h
s[8] = st9.s
P[8] = st9.p
T[8] = st9.T
X[8] = st9.Q
rho[8] = st9.rho
```

```python
# Estado 10 - Saída da bomba / entrada do trocador de calor:

P[9] = P[1]
st_isen10 = st(fld, {'P': P[9], 'S': s[8]})
h_isen10 = st_isen10.h
h[9] = (h_isen10 - h[8]) / η_pump + h[8]
st10 = st(fld, {'P': P[9], 'H': h[9]})
s[9] = st10.s
T[9] = st10.T
X[9] = st10.Q
rho[9] = st10.rho
```

```python
# Estado 11 - Entrada do processo industrial vizinho:

X[10] = 1
P[10] = P[9]
st11 = st(fld, {'P': P[10], 'Q': X[10]})
h[10] = st11.h
s[10] = st11.s
T[10] = st11.T
rho[10] = st11.rho
```

```python
# Estado 12 - Saída do processo industrial vizinho:

X[11] = 0
P[11] = P[9]
st12 = st(fld, {'P':P[11], 'Q': X[11]})
h[11] = st12.h
s[11] = st12.s
T[11] = st12.T
rho[11] = st12.rho
```

## **Tabela - Estados**

```python
# Tabela 1:
headers=["Estado", Tnn, Pnn, Xnn, hnn, snn, rhonn]
table = list(range(n))

for i in table:
  if Temperatura == 'k':
    T[i] = round(float(T[i]),2)
  else:
    T[i] = round(float(T[i]-273.15),2)
  P[i] = round(float(P[i]),2)
  X[i] = round(float(X[i]),2)
  h[i] = round(float(h[i]),2)
  s[i] = round(float(s[i]),4)
  rho[i] = round(float(rho[i]),2)
  table[i] = [estado[i], T[i], P[i], X[i], h[i], s[i], rho[i]]

table = np.array(list(table))

print(tab(table, headers, tablefmt="rst", stralign="center", numalign="left"))
print('\nOBS: Tomar título "-1" como líquido sub-resfriado')

for i in range(0, n):
  if Temperatura == 'k':
    T[i] = round(float(T[i]),2)
  else:
    T[i] = round(float(T[i]+273.15),2)

st_antes = [st1, st2, st3, st4, st5, st6, st7, st8, st9, st10, st11, st12]
```

    ========  ==================  ===============  ========  ==================  ====================  ==========================
    Estado    Temperatura [°C]    Pressão [kPa]    Título    Entalpia [kJ/kg]    Entropia [kJ/kg*K]    Massa específica [m³/kg]
    ========  ==================  ===============  ========  ==================  ====================  ==========================
    1         485                 6495             -1        3381.2              6.7932                19.65
    2         243.27              900              -1        2932.08             6.9522                3.91
    3         127.41              250              1         2714.68             7.0479                1.39
    4         51                  12.98            0.89      2333.12             7.2557                0.1
    5         51                  12.98            0         213.52              0.7167                987.54
    6         51.02               250              -1        213.81              0.7169                987.64
    7         110                 250              -1        461.49              1.4187                951
    8         -162.4              6495             -1        469.21              1.4218                953.43
    9         110                 250              -1        461.49              1.4187                951
    10        110.08              900              -1        462.3               1.4191                951.25
    11        175.35              900              1         2773.03             6.6213                4.65
    12        175.35              900              0         742.56              2.094                 891.92
    ========  ==================  ===============  ========  ==================  ====================  ==========================

    OBS: Tomar título "-1" como líquido sub-resfriado

## **Cálculos**

```python
# Cálculos:
m1 = m[0]
h1 = h[0]
h2 = h[1]
h3 = h[2]
h4 = h[3]
h5 = h[4]
h6 = h[5]
h7 = h[6]
h8 = h[7]
h9 = h[8]
h10 = h[9]
h11 = h[10]
h12 = h[11]

# Quantidade de iterações:
quant = 10

variacao = np.zeros(quant + 1, dtype = float)
resultados = np.zeros((n+1)*(quant + 1), dtype = float).reshape((n+1, quant + 1))

T_hout_cond = []
η_sys = []
W_liq =[]

STT_T = []
STT_P = []
STT_X = []
STT_h = []
STT_s = []
STT_v = []
Ti = np.linspace(0,0,n)

# Vazões mássicas:
for i in range(quant + 1):
  processo = i/10

  m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12 = var('m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12')
  EqMassa = [m8-m1, m4-m5, m5-m6, m9-m10, m11-m12, m2+m10-m11, m2+m3+m4-m1, m12+m3+m6-m7, m2*h2+m10*h10-m11*h11, m12*h12+m3*h3+m6*h6-m7*h7, m12-6.94*processo]
  massa = linsolve(EqMassa, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12)
  (m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12) =  next(iter(massa))

  massa = np.array(list(massa))

  for j in range(quant + 1):
    massa[0,j] = round(float(massa[0,j]),2)

  variacao[i] = i*10
  resultados[0, i] = variacao[i]
  resultados[1, i] = m[0]
  resultados[2:(n+1), i] = massa

  # Saídas de energia:
  W_p1 = -m5*(h5 - h6) #(Perde energia "-")
  W_p2 = -m8*(h7 - h8) #(Perde energia "-")
  W_p3 = -m9*(h9 - h10) #(Perde energia "-")
  W_pump = W_p1 + W_p2 + W_p3 #(Somatório dos trabalhos da bombas)

  # Eficiência da caldeira:
  η_cald = 0.85
  #PCI = 10000 # kJ/kg

  # Encontra o trabalho gerado pela turbina e a vazão de combustível para a demanda:
  W_t = m1*h1 - m2*h2 - m3*h3 - m4*h4
  m_fuel = m1*(h1-h8)/(PCI*η_cald) #η_cald = m1*(h1-h8)/(m_fuel*PCI)

  # Entrada de energia:
  Q_in = m_fuel * PCI #(Ganha energia "+")

  η_sys.append(round((W_t - W_pump)/Q_in, 4))

  W_liq.append(round(η_sys[i]*Q_in,2))

  # Condensador:
  T_cout = 39.5
  T_cin = 29
  T_hin = T[3] - 273.15

  ϵ = (T_cout - T_cin)/(T_hin - T_cin)
  NUT = -ln(1 - ϵ)

  CT_in = st(fld, {'P': 101.325, 'T': T_cin + 273.15})
  CT_out = st(fld, {'P': 101.325, 'T': T_cout + 273.15})

  C_min = 4500/3600*(CT_in.rho + CT_out.rho)/2*(CT_in.cp + CT_out.cp)/2

  UA = NUT * C_min # Coeficiente global de tranferência de calor * Área

  # Sistema não-linear:
  ΔT_0, ΔT_L, T_hout, ΔT_ml, Q_out = var('ΔT_0, ΔT_L, T_hout, ΔT_ml, Q_out')
  EqCond = [ΔT_0-(T_hout-T_cin), ΔT_L-(T_hout-T_cout), ΔT_ml-(ΔT_0-ΔT_L)/ln(ΔT_0/ΔT_L), Q_out-UA*ΔT_ml, Q_out-m4*(h4-h5)]
  EqCond_solve = nonlinsolve(EqCond, ΔT_0, ΔT_L, T_hout, ΔT_ml, Q_out)
  (ΔT_0, ΔT_L, T_hout, ΔT_ml, Q_out) = next(iter(EqCond_solve))

  # Redefinindo estados:
  T[3] = T_hout + 273.15 #K
  #P[3] = psi('P','T',T[3],'Q', 1, fld)/1000  #divide por 1000 pra sair em kPa
  st4 = st(fld, {'T': T[3], 'Q': 1})
  P[3] = st4.p
  st_isen4 = st(fld,{'P':P[3],'S':s[2]})
  h_isen4 = st_isen4.h
  h[3] = h[2] - (h[2] - h_isen4) * η_turb
  st4 = st(fld,{'P':P[3],'H':h[3]})
  X[3] = st4.Q      #título
  s[3]=st4.s
  rho[3] = st4.rho

  X[4] = 0
  P[4] = P[3]
  st5 = st(fld, {'P': P[4], 'Q': X[4]})
  h[4] = st5.h
  s[4] = st5.s
  T[4] = st5.T
  rho[4] = st5.rho

  P[5] = P[2]
  st_isen6 = st(fld, {'P': P[5], 'S': s[4]})
  h_isen6 = st_isen6.h
  η_pump = 0.85       #eficiência das bombas
  h[5] = (h_isen6 - h[4]) / η_pump + h[4]
  st6 = st(fld, {'P':P[5], 'H': h[5]})
  s[5] = st6.s
  T[5] = st6.T
  X[5] = st6.Q
  rho[5] = st6.rho

  P[6] = P[2]
  T[6] = 110 + 273.15
  st7 = st(fld, {'P': P[6], 'T': T[6]})
  h[6] = st7.h
  s[6] = st7.s
  X[6] = st7.Q
  rho[6] = st7.rho

  P[7] = P[0]
  st_isen8 = st(fld, {'P': P[7], 'S': s[6]})
  h_isen8 = st_isen8.h
  h[7] = (h_isen8 - h[6]) / η_pump + h[6]
  st8 = st(fld, {'P': P[7], 'H': h[7]})
  T[7] = st8.T - 273.15
  s[7] = st8.s
  X[7] = st8.Q
  rho[7] = st8.rho

  st9 = st7
  h[8] = st9.h
  s[8] = st9.s
  P[8] = st9.p
  T[8] = st9.T
  X[8] = st9.Q
  rho[8] = st9.rho

  P[9] = P[1]
  st_isen10 = st(fld, {'P': P[9], 'S': s[8]})
  h_isen10 = st_isen10.h
  h[9] = (h_isen10 - h[8]) / η_pump + h[8]
  st10 = st(fld, {'P': P[9], 'H': h[9]})
  s[9] = st10.s
  T[9] = st10.T
  X[9] = st10.Q
  rho[9] = st10.rho

  X[10] = 1
  P[10] = P[9]
  st11 = st(fld, {'P': P[10], 'Q': X[10]})
  h[10] = st11.h
  s[10] = st11.s
  T[10] = st11.T
  rho[10] = st11.rho

  X[11] = 0
  P[11] = P[9]
  st12 = st(fld, {'P':P[11], 'Q': X[11]})
  h[11] = st12.h
  s[11] = st12.s
  T[11] = st12.T
  rho[11] = st12.rho

  for i in range(0,n):
    if Temperatura == 'k':
      Ti[i] = round(T[i],2)
    else:
      Ti[i] = round(T[i]-273.15,2)
    P[i] = round(P[i],2)
    X[i] = round(X[i],2)
    h[i] = round(h[i],2)
    s[i] = round(s[i],2)
    rho[i] = round(rho[i],2)

  STT_T.append([Tnn,processo*100,Ti[0],Ti[1],Ti[2],Ti[3],Ti[4],Ti[5],Ti[6],Ti[7],Ti[8],Ti[9],Ti[10],Ti[11]])
  STT_P.append([Pnn,processo*100,P[0],P[1],P[2],P[3],P[4],P[5],P[6],P[7],P[8],P[9],P[10],P[11]])
  STT_X.append([Xnn,processo*100,X[0],X[1],X[2],X[3],X[4],X[5],X[6],X[7],X[8],X[9],X[10],X[11]])
  STT_h.append([hnn,processo*100,h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8],h[9],h[10],h[11]])
  STT_s.append([snn,processo*100,s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9],s[10],s[11]])
  STT_v.append([rhonn,processo*100,rho[0],rho[1],rho[2],rho[3],rho[4],rho[5],rho[6],rho[7],rho[8],rho[9],rho[10],rho[11]])

  T_hout = round(T_hout, 2)
  T_hout_cond.append(T_hout)

# Adiciona linhas vazias entre as propriedades na tabela:
STT_T.append(["","","","","","","","","","","","","",""])
STT_P.append(["","","","","","","","","","","","","",""])
STT_X.append(["","","","","","","","","","","","","",""])
STT_h.append(["","","","","","","","","","","","","",""])
STT_s.append(["","","","","","","","","","","","","",""])
STT_v.append(["","","","","","","","","","","","","",""])

# Incluir na tabela e gráfico:
np.reshape(variacao, (1,n-1))
np.reshape(η_sys, (1,n-1))
np.reshape(T_hout_cond, (1,n-1))
np.reshape(W_liq, (1,n-1))

for i in range(0, n):
    T[i] = round(T[i],2)
    P[i] = round(P[i],2)
    X[i] = round(X[i],2)
    h[i] = round(h[i],2)
    s[i] = round(s[i],2)
    rho[i] = round(rho[i],2)

x = []
y = []
z = []
w = []

for i in range(0, quant+1):
  x.append(variacao[i])
  y.append(T_hout_cond[i])
  z.append(η_sys[i])
  w.append(W_liq[i])

table2 = resultados.T

resultados2 = np.zeros((n+4)*(quant + 1), dtype = float).reshape((n+4, quant + 1))

for i in range(0, quant+1):
  variacao[i] = i*10
  resultados2[0, i] = resultados[0, i]
  resultados2[1, i] = resultados[1, i]
  resultados2[2:(n+1), i] = resultados[2:(n+1), i]
  resultados2[n+2,i] = round(float(w[i]),2)
  resultados2[n+3,i] = round(float(z[i]),4)
table2 = resultados2.T

st_depois = [st1, st2, st3, st4, st5, st6, st7, st8, st9, st10, st11, st12]
```

```python
# Balanço de energia da caldeira:

m_air = AF*m_fuel
m_gas = m_air + m_fuel
m13 = m8
m14 = m_fuel
m15 = 0.1*m_air
m16 = 0.9*m_air
m17 = m16
m18 = m_gas
m19 = m18
m20 = m19

fld2 = 'Air'

T_amb = 20 + 273.15 #K
P_amb = 101.325 #kPa

T15 = T_amb
T16 = T_amb
T8 = T[7]
T20 = 200 + 273.15 #
P20 = P_amb

#Economizador:
st13 = st(fld, {'P': P[7], 'Q': 0})
T13 = st13.T - 30

st13_2 = st(fld, {'P': P[7], 'T': T13})
h13 = st13_2.h

q13 = m13*(h13-h8)

st20_1 = st('Water', {'P':P_amb, 'T':T20})
st20_2 = st('CO2', {'P':P_amb, 'T':T20})
st20_3 = st('Nitrogen', {'P':P_amb, 'T':T20})

ΔT19 = q13/(m_gas*(0.0997*st20_1.cp+0.1771*st20_2.cp+0.7232*st20_3.cp))

#Pré-Ar:
T19 = T20 + ΔT19
T17 = T16 + 200

st16 = st(fld2, {'P':P_amb, 'T':T_amb})
st17 = st(fld2, {'P':P_amb, 'T':T17})
h16 = st16.h
h17 = st17.h

q17 = m17*(h17-h16)

st19_1 = st('Water', {'P':P_amb, 'T':T19})
st19_2 = st('CO2', {'P':P_amb, 'T':T19})
st19_3 = st('Nitrogen', {'P':P_amb, 'T':T19})

ΔT18 = q17/(m_gas*(0.0997*st19_1.cp+0.1771*st19_2.cp+0.7232*st19_3.cp))

T18 = T19 + ΔT18

#st17 = st(fld2, {'P':P16, 'T':T17})

#Fornalha:

print(T18)

'''
m19*CP_gas19amb*(T19-T_amb) + m8*h8 = h13*m13 + m20*CP_gas20amb*(T20-T_amb)
m18*CP_gas18amb*(T18-T_amb) = m17*cp_ar17amb*(T17-T_amb) + m19*CP_gas19amb*(T19-T_amb)
m_fuel*PCI + m17*cp_ar17amb*(T17-T_amb)  + h13*m13 = m1*h1 + m18*CP_gas18amb*(T18-T_amb)

'''
m20
```

    1117.98115571945

    29.838965867806223

## **Tabela - Vazões mássicas**

```python
# Tabela 2:
for i in range(0, quant+1):
  if Temperatura == 'k':
    headers2 = ["%", "m1 [kg/s]", "m2 [kg/s]", "m3 [kg/s]", "m4 [kg/s]", "m5 [kg/s]", "m6 [kg/s]", "m7 [kg/s]", "m8 [kg/s]", "m9 [kg/s]", "m10 [kg/s]", "m11 [kg/s]", "m12 [kg/s]", "T_hout [K]", "W_liq [kJ/s]", "η"]
    resultados2[n+1,i] = round(float(y[i]+273.15),2) #converter para K
    y[i] = resultados2[n+1,i]
  else:
    headers2 = ["%", "m1 [kg/s]", "m2 [kg/s]", "m3 [kg/s]", "m4 [kg/s]", "m5 [kg/s]", "m6 [kg/s]", "m7 [kg/s]", "m8 [kg/s]", "m9 [kg/s]", "m10 [kg/s]", "m11 [kg/s]", "m12 [kg/s]", "T_hout [°C]", "W_liq [kJ/s]", "η"]
    resultados2[n+1,i] = round(float(y[i]),2)
print(tab(table2, headers2, tablefmt="rst", stralign="center", numalign="left"))
print('\nA vazão mássica de combustível é de %.2f kg/s' %m_fuel)
print('A vazão mássica de ar é de %.2f kg/s\n' %m_air)

# Gráficos:
escala1 = 1.5
plt.figure(dpi=100, figsize=[escala1*2*6.4, escala1*4.8])
plt.subplot(1,2,1)
plt.xlabel('Processo [%]', fontsize=14)
if Temperatura == 'k':
  plt.ylabel('Temperatura de saída do condensador [K]', fontsize=14)
  plt.plot(x, y, color='r', marker = 'o', linestyle = 'solid')
else:
  plt.ylabel('Temperatura de saída do condensador [°C]', fontsize=14)
  plt.plot(x, y, color='r', marker = 'o', linestyle = 'solid')
plt.grid()
plt.subplot(1,2,2)
plt.xlabel('Processo [%]', fontsize=14)
plt.ylabel('$η_{sistema}$', fontsize=14)
plt.plot(x, z, color='b', marker = 'o', linestyle = 'solid')
plt.grid()
plt.show()
```

## **Gráfico completo Ciclo Rankine**

```python
# Gráfico do Ciclo Rankine:
escala2 = 1.2
plt.figure(dpi=150, figsize=[escala2*6.4, escala2*4.8])
y1 = 273.15
y2 = 850
x1 = -0.5
x2 = 10
plt.ylim(y1,y2)
plt.xlim(x1,x2)
plt.title('Ciclo Rankine',fontsize=20)
plt.xlabel('Entropia, s[kJ/kg$\cdot$K]')
plt.ylabel('Temperatura, T[K]')

Tmin = ps('Tmin',fld )
Tcrit = ps('Tcrit',fld)
Pcrit = ps('Pcrit', fld)

Larg_isolines = .25
Larg_cycle = 1.5
Larg_scatters = 15

#Linhas de entalpia:
T = np.linspace(Tmin,Tcrit,1000)
Q = np.arange(0.1,1,0.1)
for i in Q:
  s = ps('S','T',T,'Q',i,'Water')/1000
  plt.plot(s,T,'#6ca8fa',alpha=0.7,lw=Larg_isolines)

#Domo:
T = np.linspace(Tmin,Tcrit,1000)
s = ps('S','T',T,'Q',0,fld)/1000
plt.plot(s,T,'black',lw=2*Larg_isolines)

T = np.linspace(Tcrit,Tmin,1000)
s = ps('S','T',T,'Q',1,fld)/1000
plt.plot(s,T,'black',lw=2*Larg_isolines)

#Linhas de pressão constante:
T = np.linspace(Tmin,1000,1000)
P = [st_antes[0].p,st_antes[1].p,st_antes[2].p,st_antes[3].p,st_depois[3].p] #P1, P2, P3, P4 e P4*
for i in P:
  s = ps('S','T',T,'P',i*1000,'Water')/1000
  plt.plot(s,T,'#cc00ff',alpha=.7,lw=Larg_isolines)

#Ciclo:
#(antes)
T = np.linspace(st_antes[7].T,st_antes[0].T,1000)
s = ps('S','P',st_antes[0].p * 1000,'T',T,'Water')/1000
plt.plot(s,T,'r',lw=Larg_cycle)

T = np.linspace(st_antes[9].T,st_antes[1].T,1000)
s = ps('S','T',T,'P',st_antes[9].p*1000,'Water')/1000
plt.plot(s,T,'r',lw=Larg_cycle)
plt.plot([st_antes[3].s,st_antes[4].s],[st_antes[3].T,st_antes[4].T],'r',lw=Larg_cycle)

plt.plot([st_antes[0].s,st_antes[1].s],[st_antes[0].T,st_antes[1].T],color='r',linestyle='dashed',lw=Larg_cycle)
plt.plot([st_antes[1].s,st_antes[2].s],[st_antes[1].T,st_antes[2].T],color='r',linestyle='dashed',lw=Larg_cycle)

s = np.linspace(st_antes[2].s,st_antes[6].s,1000)
T = ps('T','S',s*1000,'P',st_antes[2].p*1000,'Water')
plt.plot(s,T,'r',lw=Larg_cycle)

T = np.linspace(st_antes[5].T,st_antes[6].T,1000)
s = ps('S','P',st_antes[5].p*1000,'T',T,'Water')/1000
plt.plot(s,T,'r',lw=Larg_cycle)

s = np.linspace(st_antes[2].s,st_antes[3].s,1000)
plt.plot([st_antes[3].s,st_antes[2].s],[st_antes[3].T,st_antes[2].T],color='r',linestyle='dashed',lw=Larg_cycle)

#(depois)
T = np.linspace(st_depois[7].T,st_depois[0].T,1000)
s = ps('S','P',st_depois[0].p * 1000,'T',T,'Water')/1000
plt.plot(s,T,'lime',lw=Larg_cycle)

T = np.linspace(st_depois[9].T,st_depois[1].T,1000)
s = ps('S','T',T,'P',st_depois[9].p*1000,'Water')/1000
plt.plot(s,T,'lime',lw=Larg_cycle)
plt.plot([st_depois[3].s,st_depois[4].s],[st_depois[3].T,st_depois[4].T],'lime',lw=Larg_cycle)

plt.plot([st_depois[0].s,st_depois[1].s],[st_depois[0].T,st_depois[1].T],color='lime',linestyle='dashed',lw=Larg_cycle)
plt.plot([st_depois[1].s,st_depois[2].s],[st_depois[1].T,st_depois[2].T],color='lime',linestyle='dashed',lw=Larg_cycle)

s = np.linspace(st_depois[2].s,st_depois[6].s,1000)
T = ps('T','S',s*1000,'P',st_depois[2].p*1000,'Water')
plt.plot(s,T,'lime',lw=Larg_cycle)

T = np.linspace(st_depois[5].T,st_depois[6].T,1000)
s = ps('S','P',st_depois[5].p*1000,'T',T,'Water')/1000
plt.plot(s,T,'lime',lw=Larg_cycle)

s = np.linspace(st_depois[2].s,st_depois[3].s,1000)
plt.plot([st_depois[3].s,st_depois[2].s],[st_depois[3].T,st_depois[2].T],color='lime',linestyle='dashed',lw=Larg_cycle)

#Pontuar e nomear:
for i in range(0, n):
  plt.scatter(st_antes[i].s,st_antes[i].T,zorder=5,color='k',s=Larg_scatters)
  plt.scatter(st_depois[i].s,st_depois[i].T,zorder=5,color='k',s=Larg_scatters)

plt.text(st_antes[0].s+.1,st_antes[0].T,'1',ha='left')
plt.text(st_antes[1].s+.1,st_antes[1].T,'2',ha='left')
plt.text(st_antes[2].s+.1,st_antes[2].T,'3',ha='left')
plt.text(st_antes[3].s+.1,st_antes[3].T,'4',ha='left')
plt.text(st_antes[4].s-.1,st_antes[4].T,'5, 6',ha='right')
plt.text(st_antes[6].s-.1,st_antes[6].T,'7, 8, 9, 10',ha='right')
plt.text(st_antes[10].s-.1,st_antes[10].T,'11',va='bottom',ha='right')
plt.text(st_antes[11].s-.1,st_antes[11].T+.1,'12',ha='right')
plt.text(st_depois[3].s+.1,st_depois[3].T,'4*',va='top',ha='left')
plt.text(st_depois[4].s-.1,st_depois[4].T,'5*, 6*',va='top',ha='right')

#plt.grid(lw=Larg_isolines)
plt.twinx()
plt.ylim(y1-273.15,y2-273.15)
plt.ylabel('Temperatura, T[°C]')
#plt.grid(lw=Larg_isolines)

plt.savefig('Diagrama T-s.png')
#files.download('Diagrama T-s.png')
```

## **Tabela relatório completo Ciclo Rankine**

```python
# Tabela 3:
table3 = STT_T + STT_P + STT_X + STT_h + STT_s + STT_v
headers3=["Propriedade", "%", "Estado 1", "Estado 2", "Estado 3", "Estado 4", "Estado 5", "Estado 6", "Estado 7", "Estado 8", "Estado 9", "Estado 10", "Estado 11", "Estado 12"]

print(tab(table3, headers3, tablefmt="rst", stralign="center", numalign="center"))
```

    ========================  =====  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ===========  ===========  ===========
          Propriedade           %     Estado 1    Estado 2    Estado 3    Estado 4    Estado 5    Estado 6    Estado 7    Estado 8    Estado 9    Estado 10    Estado 11    Estado 12
    ========================  =====  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ===========  ===========  ===========
        Temperatura [°C]       0.0     485.0       243.27      127.41      50.64       50.64       50.66       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      10.0     485.0       243.27      127.41      49.99       49.99       50.01       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      20.0     485.0       243.27      127.41      49.08       49.08        49.1       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      30.0     485.0       243.27      127.41      47.97       47.97       47.99       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      40.0     485.0       243.27      127.41      46.72       46.72       46.74       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      50.0     485.0       243.27      127.41       45.4        45.4       45.42       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      60.0     485.0       243.27      127.41      44.09       44.09       44.11       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      70.0     485.0       243.27      127.41      42.85       42.85       42.87       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      80.0     485.0       243.27      127.41      41.76       41.76       41.78       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      90.0     485.0       243.27      127.41      40.86       40.86       40.88       110.0       -162.4      110.0       110.08       175.35       175.35
        Temperatura [°C]      100.0    485.0       243.27      127.41       40.2        40.2       40.21       110.0       -162.4      110.0       110.08       175.35       175.35
               ..
         Pressão [kPa]         0.0     6495.0      900.0       250.0       12.75       12.75       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        10.0     6495.0      900.0       250.0       12.34       12.34       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        20.0     6495.0      900.0       250.0        11.8        11.8       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        30.0     6495.0      900.0       250.0       11.16       11.16       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        40.0     6495.0      900.0       250.0       10.48       10.48       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        50.0     6495.0      900.0       250.0        9.8         9.8        250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        60.0     6495.0      900.0       250.0        9.15        9.15       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        70.0     6495.0      900.0       250.0        8.58        8.58       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        80.0     6495.0      900.0       250.0        8.11        8.11       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        90.0     6495.0      900.0       250.0        7.73        7.73       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
         Pressão [kPa]        100.0    6495.0      900.0       250.0        7.46        7.46       250.0       250.0       6495.0      250.0        900.0        900.0        900.0
               ..
             Título            0.0      -1.0        -1.0        1.0         0.89        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           10.0      -1.0        -1.0        1.0         0.89        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           20.0      -1.0        -1.0        1.0         0.89        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           30.0      -1.0        -1.0        1.0         0.89        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           40.0      -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           50.0      -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           60.0      -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           70.0      -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           80.0      -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           90.0      -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
             Título           100.0     -1.0        -1.0        1.0         0.88        0.0         -1.0        -1.0        -1.0        -1.0        -1.0          1.0          0.0
               ..
        Entalpia [kJ/kg]       0.0     3381.2     2932.08     2714.68     2331.17      212.02      212.3       461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      10.0     3381.2     2932.08     2714.68     2328.23      209.29      209.58      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      20.0     3381.2     2932.08     2714.68     2323.34      205.5       205.79      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      30.0     3381.2     2932.08     2714.68     2317.33      200.86      201.14      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      40.0     3381.2     2932.08     2714.68     2310.55      195.63      195.92      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      50.0     3381.2     2932.08     2714.68     2303.38      190.12      190.41      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      60.0     3381.2     2932.08     2714.68     2296.22      184.63      184.92      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      70.0     3381.2     2932.08     2714.68     2289.46      179.46      179.75      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      80.0     3381.2     2932.08     2714.68     2283.46      174.88      175.17      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      90.0     3381.2     2932.08     2714.68     2278.53      171.13      171.42      461.49      469.21      461.49       462.3       2773.03      742.56
        Entalpia [kJ/kg]      100.0    3381.2     2932.08     2714.68     2274.88      168.36      168.64      461.49      469.21      461.49       462.3       2773.03      742.56
               ..
       Entropia [kJ/kg*K]      0.0      6.79        6.95        7.05        7.26        0.71        0.71        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     10.0      6.79        6.95        7.05        7.26        0.7         0.7         1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     20.0      6.79        6.95        7.05        7.26        0.69        0.69        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     30.0      6.79        6.95        7.05        7.27        0.68        0.68        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     40.0      6.79        6.95        7.05        7.27        0.66        0.66        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     50.0      6.79        6.95        7.05        7.28        0.64        0.64        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     60.0      6.79        6.95        7.05        7.28        0.63        0.63        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     70.0      6.79        6.95        7.05        7.29        0.61        0.61        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     80.0      6.79        6.95        7.05        7.29        0.6         0.6         1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     90.0      6.79        6.95        7.05        7.3         0.58        0.58        1.42        1.42        1.42        1.42         6.62         2.09
       Entropia [kJ/kg*K]     100.0     6.79        6.95        7.05        7.3         0.58        0.58        1.42        1.42        1.42        1.42         6.62         2.09
               ..
    Massa específica [m³/kg]   0.0     19.65        3.91        1.39        0.1        987.71      987.8       951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  10.0     19.65        3.91        1.39        0.09       988.0       988.1       951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  20.0     19.65        3.91        1.39        0.09       988.41      988.5       951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  30.0     19.65        3.91        1.39        0.09       988.9       989.0       951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  40.0     19.65        3.91        1.39        0.08       989.44      989.54      951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  50.0     19.65        3.91        1.39        0.08       990.0       990.1       951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  60.0     19.65        3.91        1.39        0.07       990.55      990.65      951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  70.0     19.65        3.91        1.39        0.07       991.05      991.15      951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  80.0     19.65        3.91        1.39        0.06       991.49      991.59      951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  90.0     19.65        3.91        1.39        0.06       991.84      991.94      951.0       953.43      951.0       951.25        4.65        891.92
    Massa específica [m³/kg]  100.0    19.65        3.91        1.39        0.06       992.1       992.2       951.0       953.43      951.0       951.25        4.65        891.92
               ..
    ========================  =====  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ==========  ===========  ===========  ===========


# Math

## 数据分析师需要哪些技能
前期
- 一、统计概率基础
- 二、工具使用（Excel，SPSS，SAS等）
- 三、行业背景知识（业务知识）

中期
- 一、分析方法模型
- 二、数据挖掘算法
- 三、IT编程能力

后期：
- 一、业务能力
- 二、管理能力
- 三、管理业务能力+1

## AI工程师需要哪些技能

- 微积分（求导，极限）和线性代数（矩阵表示和运算）是基础中的基础；
- 数据处理当然需要编程了，因此C/C++/Python任选一门建议再上一门算法课。
- 概率论+统计（很多数据分析建模基于统计模型），线性规划+凸优化（或者只学一门叫numerical optimization，统计到最后也还是求解一个优化问题，当然也有纯优化模型不用统计模型的），Machine Learning学到这里，你基本就会发现，学这一门课中间会穿插着很多其他课的内容。恩，知识总是相通的嘛，特别是这些跨专业的新兴学科，都是在以往学科的基础上由社会需求发展而来。
- 专业导向：计算机视觉，模式识别，视频追踪，医学图像处理，增强学习，深度学习, 神经网络，自然语言处理，网络信息安全，等等等等。
- 针对数据建模能力

## AI工程师是算法工程师的超集

## 总结起来数学重点关注
- 矩阵
- 求导
- 概率

## Libs required in this lesson
- numpy
- scipy
- sympy
- matplotlib

```python
import numpy as np
import scipy
import sympy as sym
import matplotlib
import pyecharts
sym.init_printing()

print("NumPy version:", np.__version__)
print("SciPy version:", scipy.__version__)
print("SymPy version:", sym.__version__)
print("Matplotlib version:", matplotlib.__version__)
print("Echarts version:", pyecharts.__version__)
```

    NumPy version: 1.13.1
    SciPy version: 0.19.1
    SymPy version: 1.1.1
    Matplotlib version: 2.0.2
    Echarts version: 0.2.6

## 函数库介绍

### numpy
Numpy（Numerical Python的简称）是高性能科学计算和数据分析的基础包。其部分功能如下：
- ndarray，一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。
- 用于对整组数据进行快速运算的标准数学函数（无需编写循环）。
- 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
- 线性代数、随机数生成以及傅里叶变换功能。
- 用于集成由C、C++、Fortran等语言编写的代码的工具。

**BSD license**

### scipy
SciPy是世界上著名的Python开源科学计算库，建立在Numpy之上。它增加的功能包括数值积分、最优化、统计和一些专用函数。
SciPy函数库在NumPy库的基础上增加了众多的数学、科学以及工程计算中常用的库函数。例如线性代数、常微分方程数值求解、信号处理、图像处理、稀疏矩阵等等。

**SciPy license**

### sympy
SymPy是一个符号计算的Python库。它的目标是成为一个全功能的计算机代数系统，同时保持代码简 洁、易于理解和扩展。它完全由Python写成，不依赖于外部库。SymPy支持符号计算、高精度计算、模式匹配、绘图、解方程、微积分、组合数学、离散 数学、几何学、概率与统计、物理学等方面的功能。

**BSD license**

### matplotlib
matplotlib是一个开源的用于创建出版质量图表的python 2D绘图库，该项目在2002年由John Hunter启动， matplotlib支持在python脚本和ipython交互式命令行中使用。我们可以使用matplotlib来生成各种高质量的图片，比如矢量图，光栅图。

**PSF license**

### 补充-开源许可证之间关系
节选自阮一峰博文

注：PSF为许可证与BSD许可证类似

# 前置

## numpy使用需求

```python
import numpy as np
```

创建矩阵

```python
lst = [[1, 2], [2, 2]]
```

```python
a1 = np.array(lst)
a1
```

    array([[1, 2],
           [2, 2]])

```python
a2 = np.ones([2,2])
a2
```

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

```python
a3 = np.zeros_like(a2)
a3
```

    array([[ 0.,  0.],
           [ 0.,  0.]])

```python
np.linspace(0,1,100)
```

    array([ 0.        ,  0.01010101,  0.02020202,  0.03030303,  0.04040404,
            0.05050505,  0.06060606,  0.07070707,  0.08080808,  0.09090909,
            0.1010101 ,  0.11111111,  0.12121212,  0.13131313,  0.14141414,
            0.15151515,  0.16161616,  0.17171717,  0.18181818,  0.19191919,
            0.2020202 ,  0.21212121,  0.22222222,  0.23232323,  0.24242424,
            0.25252525,  0.26262626,  0.27272727,  0.28282828,  0.29292929,
            0.3030303 ,  0.31313131,  0.32323232,  0.33333333,  0.34343434,
            0.35353535,  0.36363636,  0.37373737,  0.38383838,  0.39393939,
            0.4040404 ,  0.41414141,  0.42424242,  0.43434343,  0.44444444,
            0.45454545,  0.46464646,  0.47474747,  0.48484848,  0.49494949,
            0.50505051,  0.51515152,  0.52525253,  0.53535354,  0.54545455,
            0.55555556,  0.56565657,  0.57575758,  0.58585859,  0.5959596 ,
            0.60606061,  0.61616162,  0.62626263,  0.63636364,  0.64646465,
            0.65656566,  0.66666667,  0.67676768,  0.68686869,  0.6969697 ,
            0.70707071,  0.71717172,  0.72727273,  0.73737374,  0.74747475,
            0.75757576,  0.76767677,  0.77777778,  0.78787879,  0.7979798 ,
            0.80808081,  0.81818182,  0.82828283,  0.83838384,  0.84848485,
            0.85858586,  0.86868687,  0.87878788,  0.88888889,  0.8989899 ,
            0.90909091,  0.91919192,  0.92929293,  0.93939394,  0.94949495,
            0.95959596,  0.96969697,  0.97979798,  0.98989899,  1.        ])

```python
np.random.random([3, 3])
```

    array([[ 0.5423611 ,  0.31013511,  0.30294498],
           [ 0.32739092,  0.8069292 ,  0.88014048],
           [ 0.37054806,  0.91892312,  0.35880515]])

```python
np.random.normal(loc=0.0, scale=1, size=[3, 3])
```

    array([[ 0.05908995,  1.47634459,  0.80876312],
           [-0.11909987,  0.89629714,  1.16387389],
           [ 0.24506491,  0.83042124,  0.29133585]])

保存矩阵

```python
np.savez("save.npz", array1=a1, array2=a2)
```

```python
data = np.load("save.npz")
```

```python
data["array1"]
```

    array([[1, 2],
           [2, 2]])

## 矩阵索引

```python
import numpy as np
array=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
```

```python
array
```

    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])

```python
array[1]
```

    array([4, 5, 6])

```python
array[1,1]
```

    5

```python
array[:,0]
```

    array([1, 4, 7])

```python
array[1:2,1:]
```

    array([[5, 6]])

矩阵运算

```python
import numpy as np
array=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
```

```python
np.dot(array, array)
```

    array([[ 30,  36,  42],
           [ 66,  81,  96],
           [102, 126, 150]])

```python
array*array
```

    array([[ 1,  4,  9],
           [16, 25, 36],
           [49, 64, 81]])

## sympy使用需求

定义变量

```python
import sympy as sym
sym.init_printing()
```

```python
x = sym.symbols('x')
func = sym.Function('func')(x)
```

```python
x
```

函数与求导

```python
func
```

```python
func.diff(x)
```

```python
y = x**2+4*x+3
y
```

```python
y.diff(x)
```

## scipy 使用需求

```python
from scipy import integrate
result, error = integrate.quad(lambda x: x, 0, 6)
result
```

## Matplotlib 使用需求

```python
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
mpl.style.use('seaborn-darkgrid')
```

```python
x = np.linspace(-5,5,100)
```

```python
y = x**2+4*x+1
plt.plot(x, y, color="#000099", lw=2, alpha=0.6)
plt.show()
```

```python
z = 1/(1+np.exp(-x))
plt.plot(x, z, color="#000099", lw=2, alpha=0.6)
plt.show()
```

```python
mat = np.random.random([6, 6])
plt.matshow(mat)
plt.show()
```

## Echarts
- 开源软件，并且我们提供了非常炫酷的图形界面，特色是地图，另外还提供了柱状图、折线图、饼图、气泡图及四象限图等；
- 使用简单，在官网中为我们封装了js，只要会引用就会得到完美的展示效果；
- 种类多，echarts实现简单，各类图形都有；相应的模板，还有丰富的API及文档说明，非常详细；
- 兼容性好，基于html5，有着良好的动画渲染效果。

```python
from pyecharts import Bar

attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
v1 = [5, 20, 36, 10, 75, 90]
v2 = [10, 25, 8, 60, 20, 80]
bar = Bar("柱状图数据堆叠示例")
bar.add("商家A", attr, v1, is_stack=True)
bar.add("商家B", attr, v2, is_stack=True)
bar.render("echarts/demo1.html")
bar
```

```python
import random
attr = ["{}天".format(i) for i in range(30)]
v1 = [random.randint(1, 30) for _ in range(30)]
bar = Bar("Bar - datazoom - slider 示例")
bar.add("", attr, v1, is_label_show=True, is_datazoom_show=True)
bar.render("echarts/demo2.html")
bar
```

```python
from pyecharts import Bar, Line, Overlap

attr = ['A', 'B', 'C', 'D', 'E', 'F']
v1 = [10, 20, 30, 40, 50, 60]
v2 = [38, 28, 58, 48, 78, 68]
bar = Bar("Line - Bar 示例")
bar.add("bar", attr, v1)
line = Line()
line.add("line", attr, v2)

overlap = Overlap()
overlap.add(bar)
overlap.add(line)
overlap.render("echarts/demo3.1.html")
overlap
```

```python
from pyecharts import Boxplot

boxplot = Boxplot("箱形图")
x_axis = ['expr1', 'expr2', 'expr3', 'expr4', 'expr5']
y_axis = [
    [850, 740, 900, 1070, 930, 850, 950, 980, 980, 880,
    1000, 980, 930, 650, 760, 810, 1000, 1000, 960, 960],
    [960, 940, 960, 940, 880, 800, 850, 880, 900, 840,
    830, 790, 810, 880, 880, 830, 800, 790, 760, 800],
    [880, 880, 880, 860, 720, 720, 620, 860, 970, 950,
    880, 910, 850, 870, 840, 840, 850, 840, 840, 840],
    [890, 810, 810, 820, 800, 770, 760, 740, 750, 760,
    910, 920, 890, 860, 880, 720, 840, 850, 850, 780],
    [890, 840, 780, 810, 760, 810, 790, 810, 820, 850,
    870, 870, 810, 740, 810, 940, 950, 800, 810, 870]
]
_yaxis = boxplot.prepare_data(y_axis)       # 转换数据
boxplot.add("boxplot", x_axis, _yaxis)
boxplot.render("echarts/demo3.html")
boxplot
```

```python
from pyecharts import Funnel

attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
value = [20, 40, 60, 80, 100, 120]
funnel = Funnel("漏斗图示例")
funnel.add("商品", attr, value, is_label_show=True, label_pos="inside", label_text_color="#fff")
funnel.render("echarts/demo4.html")
funnel
```

```python
from pyecharts import Graph

nodes = [{"name": "结点1", "symbolSize": 10},
         {"name": "结点2", "symbolSize": 20},
         {"name": "结点3", "symbolSize": 30},
         {"name": "结点4", "symbolSize": 40},
         {"name": "结点5", "symbolSize": 50},
         {"name": "结点6", "symbolSize": 40},
         {"name": "结点7", "symbolSize": 30},
         {"name": "结点8", "symbolSize": 20}]
links = []
for i in nodes:
    for j in nodes:
        links.append({"source": i.get('name'), "target": j.get('name')})
graph = Graph("关系图-力引导布局示例")
graph.add("", nodes, links, repulsion=8000)
graph.render("echarts/demo5.html")
graph
```

```python
from pyecharts import Kline

v1 = [[2320.26, 2320.26, 2287.3, 2362.94], [2300, 2291.3, 2288.26, 2308.38],
      [2295.35, 2346.5, 2295.35, 2345.92], [2347.22, 2358.98, 2337.35, 2363.8],
      [2360.75, 2382.48, 2347.89, 2383.76], [2383.43, 2385.42, 2371.23, 2391.82],
      [2377.41, 2419.02, 2369.57, 2421.15], [2425.92, 2428.15, 2417.58, 2440.38],
      [2411, 2433.13, 2403.3, 2437.42], [2432.68, 2334.48, 2427.7, 2441.73],
      [2430.69, 2418.53, 2394.22, 2433.89], [2416.62, 2432.4, 2414.4, 2443.03],
      [2441.91, 2421.56, 2418.43, 2444.8], [2420.26, 2382.91, 2373.53, 2427.07],
      [2383.49, 2397.18, 2370.61, 2397.94], [2378.82, 2325.95, 2309.17, 2378.82],
      [2322.94, 2314.16, 2308.76, 2330.88], [2320.62, 2325.82, 2315.01, 2338.78],
      [2313.74, 2293.34, 2289.89, 2340.71], [2297.77, 2313.22, 2292.03, 2324.63],
      [2322.32, 2365.59, 2308.92, 2366.16], [2364.54, 2359.51, 2330.86, 2369.65],
      [2332.08, 2273.4, 2259.25, 2333.54], [2274.81, 2326.31, 2270.1, 2328.14],
      [2333.61, 2347.18, 2321.6, 2351.44], [2340.44, 2324.29, 2304.27, 2352.02],
      [2326.42, 2318.61, 2314.59, 2333.67], [2314.68, 2310.59, 2296.58, 2320.96],
      [2309.16, 2286.6, 2264.83, 2333.29], [2282.17, 2263.97, 2253.25, 2286.33],
      [2255.77, 2270.28, 2253.31, 2276.22]]
kline = Kline("K 线图示例")
kline.add("日K", ["2017/7/{}".format(i + 1) for i in range(31)], v1)
kline.render("echarts/demo6.html")
kline
```

```python
from pyecharts import Geo

data = [("海门", 9), ("鄂尔多斯", 12), ("招远", 12), ("舟山", 12), ("齐齐哈尔", 14), ("盐城", 15)]
geo = Geo("全国主要城市空气质量", "data from pm2.5", title_color="#fff", title_pos="center",
          width=1200, height=600, background_color='#404a59')
attr, value = geo.cast(data)
geo.add("", attr, value, type="effectScatter", is_random=True, effect_scale=5)
geo.render("echarts/geo.html")
geo
```

```python

```

math_slides/Chapter 1 - Introduction.ipynb


```python
from resources.workspace import *
```

$
% START OF MACRO DEF
% DO NOT EDIT IN INDIVIDUAL NOTEBOOKS, BUT IN macros.py
%
\newcommand{\Reals}{\mathbb{R}}
\newcommand{\Expect}[0]{\mathbb{E}}
\newcommand{\NormDist}{\mathcal{N}}
%
\newcommand{\DynMod}[0]{\mathscr{M}}
\newcommand{\ObsMod}[0]{\mathscr{H}}
%
\newcommand{\mat}[1]{{\mathbf{{#1}}}}
%\newcommand{\mat}[1]{{\pmb{\mathsf{#1}}}}
\newcommand{\bvec}[1]{{\mathbf{#1}}}
%
\newcommand{\trsign}{{\mathsf{T}}}
\newcommand{\tr}{^{\trsign}}
\newcommand{\tn}[1]{#1}
\newcommand{\ceq}[0]{\mathrel{≔}}
%
\newcommand{\I}[0]{\mat{I}}
\newcommand{\K}[0]{\mat{K}}
\newcommand{\bP}[0]{\mat{P}}
\newcommand{\bH}[0]{\mat{H}}
\newcommand{\bF}[0]{\mat{F}}
\newcommand{\R}[0]{\mat{R}}
\newcommand{\Q}[0]{\mat{Q}}
\newcommand{\B}[0]{\mat{B}}
\newcommand{\C}[0]{\mat{C}}
\newcommand{\Ri}[0]{\R^{-1}}
\newcommand{\Bi}[0]{\B^{-1}}
\newcommand{\X}[0]{\mat{X}}
\newcommand{\A}[0]{\mat{A}}
\newcommand{\Y}[0]{\mat{Y}}
\newcommand{\E}[0]{\mat{E}}
\newcommand{\U}[0]{\mat{U}}
\newcommand{\V}[0]{\mat{V}}
%
\newcommand{\x}[0]{\bvec{x}}
\newcommand{\y}[0]{\bvec{y}}
\newcommand{\z}[0]{\bvec{z}}
\newcommand{\q}[0]{\bvec{q}}
\newcommand{\br}[0]{\bvec{r}}
\newcommand{\bb}[0]{\bvec{b}}
%
\newcommand{\bx}[0]{\bvec{\bar{x}}}
\newcommand{\by}[0]{\bvec{\bar{y}}}
\newcommand{\barB}[0]{\mat{\bar{B}}}
\newcommand{\barP}[0]{\mat{\bar{P}}}
\newcommand{\barC}[0]{\mat{\bar{C}}}
\newcommand{\barK}[0]{\mat{\bar{K}}}
%
\newcommand{\D}[0]{\mat{D}}
\newcommand{\Dobs}[0]{\mat{D}_{\text{obs}}}
\newcommand{\Dmod}[0]{\mat{D}_{\text{obs}}}
%
\newcommand{\ones}[0]{\bvec{1}}
\newcommand{\AN}[0]{\big( \I_N - \ones \ones\tr / N \big)}
%
% END OF MACRO DEF
$
In tutorial [T3](T3%20-%20Univariate%20Kalman%20filtering.ipynb) we saw that the Kalman filter (KF) produces reasonable results for straight lines (in so far as linear regression does!).
What about more intricate time series?

### The model
The straight line example (of the tutorial T3) could result from discretizing the model:
\begin{align*}
\frac{d^2 x}{dt^2} &= 0 \, .
\end{align*}
Here we're going to consider the M-th order model:
$$ \frac{d^M x}{dt^M} = 0 \, .$$

This can be rewritten as a 1-st order vector (i.e. coupled system of) ODE:
$$ \begin{align}
\frac{d x_M}{dt} &= 0 \, , \\
\frac{d x_m}{dt} &= x_{m+1} \, ,
\end{align} $$
where the subscript $1,\ldots,M$ is the *index* of the state vector element.

To make it more interesting, we'll add two terms to this evolution model:
 - damping: $\beta x_m$, with $\beta < 0$;
 - noise: $\frac{d q_m}{dt}$.

Thus,
$$ \frac{d x_m}{dt} = \beta x_m + x_{m+1} + \frac{d q_m}{dt} \, ,$$
where $q_m$ is the noise process, and $\beta = \log(0.9)$.

Discretized by explicit-Euler, with a time step `dt=1`, this yields
$$ x_{k+1,m} = 0.9 x_{k,m} + x_{k,m+1} + q_{k,m}\, ,$$

In summary, $\x_{k+1} = \DynMod \x_k + \q_k$, with $\DynMod$ as below.

```python
M = 4 # model order (and also ndim)
M_matrix = 0.9*eye(M) + diag(ones(M-1),1)
print(M_matrix)
```

### Estimation by the Kalman filter (and smoother) with DAPPER

Note that this is an $M$-dimensional time series.
However, we'll only observe the first (0th) component.

We shall not write the code for the multivariate Kalman filter,
because it already exists in DAPPER in `da_methods.py` and is called `ExtKF()`.

The following code configures an experiment based on the above model. Don't worry about the specifics. We'll get back to how to use DAPPER later.

```python
# Forecast dynamics
Dyn = linear_model_setup(M_matrix,dt0=1)
Dyn['noise'] = 0.0001*(1+arange(M))

# Initial conditions
X0 = GaussRV(M=M,C=0.02*arange(M))

# Observe 0th component only
Obs = partial_direct_Obs(M,[0])
Obs['noise'] = 1000

# Time settings
t = Chronology(dt=1,dtObs=5,K=250)

# Wrap-up
HMM = HiddenMarkovModel(Dyn,Obs,t,X0)
```

This generates (simulates) a synthetic truth (xx) and observations (yy)

```python
xx,yy = simulate(HMM)
for m,x in enumerate(xx.T):
    plt.plot(x,label="x^%d"%m)
plt.legend();
```

Now we'll run assimilation methods on the data. Firstly, the KF, available as `ExtKF` in DAPPER:

```python
stats_KF = ExtKF(store_u=1).assimilate(HMM,xx,yy)
```

We'll also run the "Kalman smoother" available as `ExtRTS`.
Without going into details, this method is based on the Kalman *filter* but,
being a *smoother*,
it also goes backwards and updates previous estimates with future (relatively speaking) observations.

```python
stats_KS = ExtRTS(store_u=1).assimilate(HMM,xx,yy)
```

### Estimation by "time series analysis"
The following methods perform time series analysis of the observations, and are mainly derived from signal processing theory.
Considering that derivatives can be approximated by differentials, it is plausible that the above model could also be written as an AR(M) process. Thus these methods should perform quite well.

```python
# Tools
import scipy.signal as sp_sp
normalize = lambda x: x / x.sum()
truncate  = lambda x,n: np.hstack([x[:n],zeros(len(x)-n)])

# We only estimate the 0-th component.
signal = yy[:,0]

# Estimated signals
ESig = {}
ESig['Gaussian']       = sp_sp.convolve(signal, normalize(sp.signal.gaussian(30,3)),'same')
ESig['Wiener']         = sp_sp.wiener(signal)
ESig['Butter']         = sp_sp.filtfilt(*sp_sp.butter(10, 0.12), signal, padlen=len(signal)//10)
ESig['Spline']         = sp.interpolate.UnivariateSpline(t.kkObs,signal,s=1e4)(t.kkObs)
ESig['Trunc. Fourier'] = np.fft.irfft(truncate(np.fft.rfft(signal),len(signal)//14))
```

### Comparison
The following code plots the results.

```python
@interact(Visible=SelectMultiple(options=['Truth','Kalman smoother','Kalman filter','My Method']+list(ESig)))
def plot_results(Visible):
    plt.figure(figsize=(9,5))
    plt.plot(t.kkObs,yy,'k.',alpha=0.4,label="Obs")
    if 'Truth'           in Visible: plt.plot(t.kk   ,xx[:,0]           ,'k',label="Truth")
    if 'Kalman smoother' in Visible: plt.plot(t.kk   ,stats_KS.mu.u[:,0],'m',label="K. smoother")
    #if'Kalman filter u' in Visible: plt.plot(t.kk   ,stats_KF.mu.u[:,0],'b',label="K. filter (u)")
    #if'Kalman filter f' in Visible: plt.plot(t.kkObs,stats_KF.mu.f[:,0],'b',label="K. filter (f)")
    #if'Kalman filter a' in Visible: plt.plot(t.kkObs,stats_KF.mu.a[:,0],'b',label="K. filter (a)")
    if 'Kalman filter'   in Visible:
        pw_xxf, pw_xxa = weave_fa(stats_KF.mu.f[:,0],stats_KF.mu.a[:,0])
        pw_kkf, pw_kka = weave_fa(t.kkObs)
        plt.plot(pw_kkf,pw_xxf,'b',label="KF. forecast")
        plt.plot(pw_kka,pw_xxa,'c',label="KF. analysis")

    if 'My Method' in Visible and 'stats_MM' in locals():
        pw_xxf, pw_xxa = weave_fa(stats_MM.mu.f[:,0],stats_MM.mu.a[:,0])
        pw_kkf, pw_kka = weave_fa(t.kkObs)
        plt.plot(pw_kkf,pw_xxf,'y',label=stats_MM.config.da_method.__name__+" forecast")
        plt.plot(pw_kka,pw_xxa,'g',label=stats_MM.config.da_method.__name__+" analysis")

    for method, estimate in ESig.items():
        if method in Visible: plt.plot(t.kkObs, estimate,label=method)

    plt.ylabel('$x^0$, $y$, and $\hat{x}^0$')
    plt.xlabel('Time index ($k$)')
    plt.legend()
    plt.show()
```

Visually, it's hard to imagine better performance than from the Kalman smoother.
However, recall the advantage of the Kalman filter (and smoother): *they know the forecast model that generated the truth*.

Since the noise levels Q and R are given to the DA methods (but they don't know the actual outcomes/realizations of the random noises), they also do not need any *tuning*, compared to signal processing filters, or choosing between the myriad of signal processing filters [out there](https://docs.scipy.org/doc/scipy/reference/signal.html#module-scipy.signal).

```python
def average_error(estimate_at_obs_times):
    return np.mean(np.abs(xx[t.kkObs,0] - estimate_at_obs_times))

for method, estimate in ESig.items():
    print(method   , average_error(estimate))
print('K. smoother', average_error(stats_KS.mu.u[t.kkObs,0]))
print('K. filter'  , average_error(stats_KF.mu.a[:,0]))
# print('My Method', average_error(stats_MM.mu.a[:,0])) # uncomment after Exc 8
```

**Exc 2:** Theoretically, in the long run, the Kalman smoother should yield the optimal result. Verify this by increasing the experiment length to `K=10**4`.

**Exc 4:** Re-run the experiment with different paramters, for example the observation noise strength or `dkObs`.
[Results will differ even if you changed nothing because the truth noises (and obs) are stochastic.]

**Exc 6:** Right before executing the assimilations (but after simulating the truth and obs), change $R$ by inserting:

    HMM.Obs.noise = GaussRV(C=0.01*eye(1))

What happens to the estimates of the Kalman filter and smoother?

**Exc 8*:** Try out different methods from DAPPER by replacing `MyMethod` below with one of the following:
 - Climatology
 - Var3D
 - OptInterp
 - EnKF
 - EnKS
 - PartFilt

You typically also need to set (and possibly tune) some method parameters. Otherwise you will get an error (or possibly the method will perform very badly). You may find (some) documentation for each method in its source code...

```python
stats_MM = MyMethod(param1=val1,...).assimilate(HMM,xx,yy)
```

### Summary
Like linear regression, time series analysis is also a subset of state estimation and DA [(much of time series analysis can be formulated as state estimation)](https://www.google.com/search?q="We+now+demonstrate+how+to+put+these+models+into+state+space+form"). Moreover, DA methods produce uncertainty quantification, something which is usually more obscure with time series analysis methods. Still, the best is yet to come: DA methods should have the capacity to handle inhomogeneous, multivariate, sparsely observed, chaotic systems (which is more fun than stochastically-driven signals such as the above example).

### Next: [Dynamical systems, chaos, Lorenz](T6%20-%20Dynamical%20systems,%20chaos,%20Lorenz.ipynb)

notebooks/T5 - Time series analysis.ipynb


```python
%run beam_simulator.py
%run visibility_simulator.py
%matplotlib inline
%run antpos
import numpy as np
import matplotlib.pyplot as plt
from astropy.io import fits
import pandas as pd
import astropy.coordinates as coord
import astropy.units as u
from astropy import time
from matplotlib.colors import LogNorm
import scipy.constants as CNST1
from scipy import signal
from matplotlib.colors import LogNorm
```

```python
#loadinng GLEAM Calague 70-200 MHz
datafile = pd.read_csv('GLEAM_Catalogue_flux_above_15Jy_70_237MHz.csv')

df = pd.DataFrame(data=datafile)

#Flux and spectral indeces best fit

data_bestfit = np.load('GLEAM_Catalogue_flux_above_15Jy_100_200MHz_best_fit_S_0_alpha.npy')

RA = np.deg2rad(df['RAJ2000'])
DEC =np.deg2rad(df['DEJ2000'])

RA_0,DEC_0 = 21.41100,-30.72100
```

$\textbf{GLEAM Bright Point Source Spartial Distribution}$

```python
ra = coord.Angle(df['RAJ2000']*u.degree)
ra = ra.wrap_at(180*u.degree)
dec = coord.Angle(df['DEJ2000']*u.degree)

RA_0, DEC_0 = 21.41100,-30.72100 #degrees

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111, projection="mollweide")
ax.scatter(ra.radian, dec.radian,label='GLEAM Bright Point Sources')
ax.plot(np.deg2rad(RA_0),np.deg2rad(DEC_0),'*r',label='HERA Phase Center')
ax.set_xticklabels(['14h','16h','18h','20h','22h','0h','2h','4h','6h','8h','10h'])
ax.legend(loc='best')
ax.grid(True)

```

$\textbf{HERA-19 Antenna Layout in ENU System}$

```python
antposs = hex_array(5,split_core= True, outriggers=0)

hera_hex_pos = np.array([list(pos) for pos in antposs.values()])

print 'number of Antennas', len(hera_hex_pos[:,0])

plt.figure(figsize=(5,4), dpi=100)
plt.scatter(hera_hex_pos[:,0],hera_hex_pos[:,1])
plt.xlabel("East-West [meters]")
plt.ylabel("North-South [meters]")
plt.axis('equal')
plt.tight_layout()

```

```python

```

```python

```

$\textbf{Transform from ENU to XYZ}$

```python
lat = -30.722

XYZ_array = np.array([XYZ_trans_mat(hera_hex_pos[i],lat)[0] for i in range(len(hera_hex_pos))])

plt.figure(figsize=(5,4), dpi=100)
plt.scatter(XYZ_array[:,0],XYZ_array[:,1])
plt.xlabel("CEW[meters]")
plt.ylabel("CNS [meters]")
plt.axis('equal')
plt.tight_layout()

```

$\textbf{Computing Baseline Vectors}$

```python

ants_indices = np.arange(len(hera_hex_pos))

ublDict = InterferometricArray(hera_hex_pos,ants_indices).CalculateUBLs()[4] #computing Unique Baselines

B_xyz = np.array(ublDict.keys())

plt.figure(figsize=(5,4), dpi=100)
plt.scatter(B_xyz[:,0],B_xyz[:,1])
plt.xlabel(r"$B_X$ [meters]")
plt.ylabel(r"$B_Y$ [meters]")
plt.axis('equal')
plt.tight_layout()

```

$\textbf{Simulating Visibility with Gaussian Primary Beam}$

```python
RA_0,DEC_0 = 21.41100,-30.72100
lsts = np.array([np.deg2rad(0)]) #sources closer to the Zenith
freqs = np.linspace(140.0,160.0,100)

dish_diam =14.0
d_block = 0.0
a_theta_ant1= a_phi_ant1 = a_theta_ant2 =a_phi_ant2 =6.0 #meters
vis_gb={}
vis_ad={}

for ubl_i,redblock in ublDict.iteritems():

    vis_redblock_gb =np.zeros((len(redblock),freqs.size),dtype='complex')
    vis_redblock_ad =np.zeros((len(redblock),freqs.size),dtype='complex')
    for antspairs in range(len(redblock)):

        ant1, ant2 = hera_hex_pos[redblock[antspairs][1]],hera_hex_pos[redblock[antspairs][0]]

        vis_redblock_gb[antspairs] = vis_sim_gaussian_beam_bl(ant1,ant2,dish_diam,RA_0,DEC_0,RA,DEC,data_bestfit,freqs,lsts,0.0,0.0,0.0,0.0,0.0,0.0)

        vis_redblock_ad[antspairs] = vis_sim_airydisk_beam_bl(ant1,ant2,dish_diam,d_block,RA_0,DEC_0,RA,DEC,data_bestfit,freqs,lsts,a_theta_ant1,a_phi_ant1,0.0,0.0,a_theta_ant2,a_phi_ant2,0.0,0.0)

    vis_gb[ubl_i] =vis_redblock_gb
    vis_ad[ubl_i] =vis_redblock_ad

```

```python

```

```python

```

$\textbf{Simulating Visibility Noise}$

\begin{equation}
    \textit{T}_{sys}(\nu,\textit{t})= \eta_{\textrm{L}}\textit{T}_{\textrm{A}}(\nu,\textit{t})  + \textit{T}_{\textrm{R}}(\nu,\textit{t})
\end{equation}
with antenna temperature given by
\begin{equation}
    \textit{T}_{\textrm{A}}(\nu,\textit{t}) = \frac{\int \int \textit{P}(\theta,\phi,\nu) \textit{T}_{\textrm{sky}}(\theta,\phi,\nu,\textit{t})\sin(\theta)d\theta d\phi}{\int\int \textit{P}(\theta,\phi,\nu)\sin(\theta)d\theta d\phi}
\end{equation}
  where $\eta_{\textrm{L}}$ is the signal losses in the receiver.  Here $\textit{P}(\theta,\phi,\nu)$ is the antenna power pattern and $\textit{T}_{\textrm{sky}}$ is the brightness temperature in the sky during the observation time $\textit{t}$ seen by an antenna, which have the contribution from the cosmic background radiation, galactic emission, bright radio sources and ground temperature,

\begin{equation}
    \textit{T}_{\textrm{sky}}(\theta,\phi,\nu,\textit{t}) = \textit{T}_{\textrm{CMB}} + \textit{T}_{\textrm{gal}}(\theta,\phi,\nu,\textit{t}) + \textit{T}_{\textrm{radio}}(\theta,\phi,\nu,\textit{t})
\end{equation}

where $\textit{T}_{\textit{CMB}} =  2.725 $ $\rm{K}$, $\textit{T}_{\textrm{ground}} = 300$ $\textrm{K}$ the temperature of the surface (ignore scattering effect from neighboring antennas). In the case of sky with radio point sources only, then the antenna temperature becomes,

\begin{equation}
    \textit{T}_{\textrm{A}}(\nu,\textit{t}) = \frac{\sum_{\textit{q}}^{\textit{N}_{\textrm{src}}} \textit{P}(\theta_{\textit{q}},\phi_{\textit{q}},\nu) \textit{T}_{\textit{q}}(\nu)\sin(\theta_{\textit{q}})}{\sum_{\textit{q}}^{\textit{N}_{\textrm{src}} }\textit{P}(\theta_{\textit{q}},\phi_{\textit{q}},\nu)\sin(\theta_{\textit{q}})}
\end{equation}

The antenna noise is defined as
\begin{equation}\label{eq:radm}
    \sigma(\nu,\textit{t}) = \frac{\textit{T}_{\textrm{sys}}(\nu,\textit{t})}{\sqrt{\Delta \nu \Delta \textit{t}}}
\end{equation}
where $\Delta \nu$ and $\Delta \textit{t}$ are frequency bandwidth and time integration per observation.
Although the system noise temperature plays an important role on the determining interferometer sensitivity, however, Radio Interference Frequency Interference (RFI) can lead to deletion of the entire observation session if it not properly monitored. The following subsection is dedicated in discussing RFI impact on the interferometer sensitivity, and RFI mitigation techniques.

```python
frq_mid = 150 # Central frequency in MHz
c=3e8
jy2K = 1e-26*(c/(frq_mid*1e6))**2/(2*CNST1.k)
def get_antenna_noise_pt_src(RA_0,DEC_0,RA,DEC,flux_cat,freq_array,freq_res,time_int,dish_diam=14.6,T_receiver =100,antenna_loss=0.8,ant1_theta_error=0.0,ant1_phi_error=0.0,Ant1_beam_size_error=0.0):

    T_sys = np.zeros(freq_array.size)

    for freq_i in range(freq_array.size):
        sigma_fwhm = sigma_func(dish_diam,freqs[freq_i]*10**6)

        T_sys[freq_i] = antenna_loss*(np.sum([np.power(gaussian_beam(np.deg2rad(RA_0),np.deg2rad(DEC_0),DEC[src_i],RA[src_i],sigma_fwhm,ant1_theta_error,ant1_phi_error,Ant1_beam_size_error),2)*flux_func(freqs[freq_i],flux_cat[src_i][0],flux_cat[src_i][1])*jy2K*np.sin(RA[src_i]) for src_i in range(len(RA))])/np.sum([np.power(gaussian_beam(np.deg2rad(RA_0),np.deg2rad(DEC_0),DEC[src_i],RA[src_i],sigma_fwhm,ant1_theta_error,ant1_phi_error,Ant1_beam_size_error),2)*np.sin(RA[src_i])for src_i in range(len(RA))])) + T_receiver

    noise = T_sys/np.sqrt(freq_res*time_int)

    return noise

freq_res = (freqs[1] -freqs[0])*10**6
time_int =112 #seconds

ant_noise =get_antenna_noise_pt_src(RA_0,DEC_0,RA,DEC,data_bestfit,freqs,freq_res,time_int)

```

```python

#add thermal noise
vis_add_noise_gb = {}
vis_add_noise_ad = {}
for ubl_i,redblock in vis_gb.iteritems():
    vis_redblock_gb_noise_add =np.zeros((len(redblock),freqs.size),dtype='complex')
    vis_redblock_ad_noise_add =np.zeros((len(redblock),freqs.size),dtype='complex')
    for bl_i in range(len(redblock)):
        vis_redblock_gb_noise_add[bl_i] = [vis_gb[ubl_i][bl_i][freq_i] + ant_noise[freq_i]*ant_noise[freq_i] for freq_i in range(freqs.size)]
        vis_redblock_ad_noise_add[bl_i] = [vis_ad[ubl_i][bl_i][freq_i] + ant_noise[freq_i]*ant_noise[freq_i] for freq_i in range(freqs.size)]
    vis_add_noise_gb[ubl_i] = vis_redblock_gb_noise_add
    vis_add_noise_ad[ubl_i] = vis_redblock_ad_noise_add

```

```python
ublDict
```

    {(-5110, 1264, 0): [(3, 11), (7, 15)],
     (-4380, 2529, 0): [(0, 11), (3, 15), (7, 18)],
     (-3650, 1264, 0): [(0, 6), (3, 10), (4, 11), (7, 14), (8, 15), (12, 18)],
     (-3650, 3793, 0): [(0, 15), (3, 18)],
     (-2920, 2529, 0): [(0, 10), (1, 11), (3, 14), (4, 15), (7, 17), (8, 18)],
     (-2920, 5058, 0): [(0, 18)],
     (-2190, 1264, 0): [(0, 5),
      (1, 6),
      (3, 9),
      (4, 10),
      (5, 11),
      (7, 13),
      (8, 14),
      (9, 15),
      (12, 17),
      (13, 18)],
     (-2190, 3793, 0): [(0, 14), (1, 15), (3, 17), (4, 18)],
     (-1460, 2529, 0): [(0, 9),
      (1, 10),
      (2, 11),
      (3, 13),
      (4, 14),
      (5, 15),
      (7, 16),
      (8, 17),
      (9, 18)],
     (-1460, 5058, 0): [(0, 17), (1, 18)],
     (-730, 1264, 0): [(0, 4),
      (1, 5),
      (2, 6),
      (3, 8),
      (4, 9),
      (5, 10),
      (6, 11),
      (7, 12),
      (8, 13),
      (9, 14),
      (10, 15),
      (12, 16),
      (13, 17),
      (14, 18)],
     (-730, 3793, 0): [(0, 13), (1, 14), (2, 15), (3, 16), (4, 17), (5, 18)],
     (0, 2529, 0): [(0, 8),
      (1, 9),
      (2, 10),
      (3, 12),
      (4, 13),
      (5, 14),
      (6, 15),
      (8, 16),
      (9, 17),
      (10, 18)],
     (0, 5058, 0): [(0, 16), (1, 17), (2, 18)],
     (730, 1264, 0): [(0, 3),
      (1, 4),
      (2, 5),
      (3, 7),
      (4, 8),
      (5, 9),
      (6, 10),
      (8, 12),
      (9, 13),
      (10, 14),
      (11, 15),
      (13, 16),
      (14, 17),
      (15, 18)],
     (730, 3793, 0): [(0, 12), (1, 13), (2, 14), (4, 16), (5, 17), (6, 18)],
     (1460, 0, 0): [(1, 0),
      (2, 1),
      (4, 3),
      (5, 4),
      (6, 5),
      (8, 7),
      (9, 8),
      (10, 9),
      (11, 10),
      (13, 12),
      (14, 13),
      (15, 14),
      (17, 16),
      (18, 17)],
     (1460, 2529, 0): [(0, 7),
      (1, 8),
      (2, 9),
      (4, 12),
      (5, 13),
      (6, 14),
      (9, 16),
      (10, 17),
      (11, 18)],
     (1460, 5058, 0): [(1, 16), (2, 17)],
     (2190, 1264, 0): [(1, 3),
      (2, 4),
      (4, 7),
      (5, 8),
      (6, 9),
      (9, 12),
      (10, 13),
      (11, 14),
      (14, 16),
      (15, 17)],
     (2190, 3793, 0): [(1, 12), (2, 13), (5, 16), (6, 17)],
     (2920, 0, 0): [(2, 0),
      (5, 3),
      (6, 4),
      (9, 7),
      (10, 8),
      (11, 9),
      (14, 12),
      (15, 13),
      (18, 16)],
     (2920, 2529, 0): [(1, 7), (2, 8), (5, 12), (6, 13), (10, 16), (11, 17)],
     (2920, 5058, 0): [(2, 16)],
     (3650, 1264, 0): [(2, 3), (5, 7), (6, 8), (10, 12), (11, 13), (15, 16)],
     (3650, 3793, 0): [(2, 12), (6, 16)],
     (4380, 0, 0): [(6, 3), (10, 7), (11, 8), (15, 12)],
     (4380, 2529, 0): [(2, 7), (6, 12), (11, 16)],
     (5110, 1264, 0): [(6, 7), (11, 12)],
     (5840, 0, 0): [(11, 7)]}

```python
bl_ew = (5840, 0, 0)
vis_gb_ew ={}
vis_ad_ew ={}

vis_gb_ns ={}
vis_ad_ns ={}
for bl_i in ublDict.keys():
    if np.rad2deg(np.arccos(np.dot(bl_i,bl_ew)/(np.linalg.norm(bl_i)*np.linalg.norm(bl_ew)))) == 0.0 :
        vis_gb_ew[bl_i] = vis_gb[bl_i]
        vis_ad_ew[bl_i] = vis_ad[bl_i]

    elif np.rad2deg(np.arccos(np.dot(bl_i,bl_ew)/(np.linalg.norm(bl_i)*np.linalg.norm(bl_ew)))) == 90.0:
        vis_gb_ns[bl_i] = vis_gb[bl_i]
        vis_ad_ns[bl_i] = vis_ad[bl_i]

    else:

        pass

```

```python
blvecs = vis_gb_ew.keys()
blvecs_sorted = sorted(blvecs)

vis_gb_add_noise_sorted_ew = np.array([vis_gb_ew[blvecs_sorted[i]][0] for i in range(len(blvecs_sorted))])
vis_ad_add_noise_sorted_ew = np.array([vis_ad_ew[blvecs_sorted[i]][0] for i in range(len(blvecs_sorted))])
```

$\textbf{Delay Power Spectrum}$

The delay power spectrum $\textit{P}(\textit{k}_{\parallel},\textit{k}_{\perp})$ is related to the diagonal of covariance matrix of the Fourier domain visibility $\textit{u}(\mathbf{u},\tau)$ as follows

\begin{equation}
\textit{P}(\textit{k}_{\parallel},\textit{k}_{\perp}) = \Big(\frac{\lambda^2}{2\textit{k}_{\textrm{B}}}\Big)^2\Big(\frac{\textit{D}^2\Delta \textit{D}}{\Delta\textit{B}}\Big)\Big(\frac{1}{\Omega\Delta \textit{B}}\Big) \langle |\textit{u}(\mathbf{u},\tau)|^2\rangle
\end{equation}
where

\begin{equation}
\textit{k}_{\perp} = \frac{2\pi\mathbf{b}}{\lambda\textit{D}(\textit{z})}
\end{equation}
and
\begin{equation}
\textit{k}_{\parallel} \approx\frac{2\pi\tau}{\Delta \textit{D}(\textit{z})}
\end{equation}
are wavenumber in the line-of-sight and transeverse directions. Here $\textit{D}(\textit{z})$ is the transeverse comoving distance at redshift $\textit{z}$, and $\Delta \textit{D}(\textit{z})$ is the line-of-sight comoving width of the   observation bandwidth at redshift $\textit{z}$, if $\textit{B}$ is set to be bandwidth;

\begin{equation}
\Delta \textit{D}(\textit{z}) \approx\frac{\textit{c}(1+\textit{z})^2}{\textit{H}_0\textit{f}_{21}\textit{E}(\textit{z})}\Delta \textit{B}
\end{equation}
where $\textit{c}$ is the speed of light in vaccum, and $\textit{H}_0$ and $E(z)=[\Omega _{\rm M}(1+z)^3+\Omega _k(1+z)^2+\Omega _\Lambda ]^{1/2}$ are standard terms in cosmology. The effective bandwidth is given by
\begin{equation}
\Delta\textit{B} = \int_{-\textit{B}/2}^{\textit{B}/2} \textit{W}(\textit{f})\textit{d}\textit{f}
\end{equation}
and
\begin{equation}
\Omega = \int_{4\pi}\textit{A}(\hat{s},\textit{f})\textit{d}\Omega
\end{equation}

where $\textit{W}(\textit{f})$  and $\textit{A}(\hat{s},\textit{f})$ is window function at frequency $\textit{f}$ and antenna angular response at function positon $\hat{s}$ in celestial sphere, respectively. The delay visibility are given by

\begin{equation}
\textit{u}(\mathbf{u},\tau) = \int \textit{u}(\mathbf{u},\textit{f})\textit{e}^{-j2\pi\textit{f}\tau} \textit{d}\textit{f}
\end{equation}
where $\tau$ is a Fourier conjugate of $\textit{f}$. Here $j=\sqrt{-1}$, $\lambda$ is the observing wavelenght and $\textit{k}_{\textrm{B}}$.

reference

https://iopscience.iop.org/article/10.1088/0004-637X/776/1/6

```python
from astropy.cosmology import FlatLambdaCDM
import scipy.integrate  as intg
from scipy.integrate import dblquad

```

$\textbf{Comoving Transverse Distance $ \textit{D}$}$

```python
cosm = FlatLambdaCDM(70.0,0.3) #standard cosmology

get_redshift = lambda f_0,f: (f_0/f) -1.0
f_0 = 1420
z_values = [get_redshift(f_0,freqs[j]) for j in range(freqs.size)]

comoving_distance= cosm.comoving_transverse_distance(z_values)

plt.plot(z_values,comoving_distance)
plt.xlabel('Redshift')
plt.ylabel('Comoving Distance [Mpc]')
```

$\textbf{Bandwidth $\Delta \textit{B}$}$

```python
def get_bandwidth(window_func,frqs,dfreq =1.0):
    "This function compute the bandwidth given the window function as an array, and freqs array where it is sampled"

    bandwidth = intg.simps(window_func,frqs,dfreq)

    return bandwidth

window = signal.nuttall(freqs.size)

plt.plot(freqs,window)
plt.title("Nuttall window")
plt.ylabel("Amplitude")
plt.xlabel("Frequnecy Samples")
plt.show()

print 'Bandwidth is', get_bandwidth(window,freqs),'MHz'
```

$\textbf{Line-of-sight Comoving Width $\Delta \textit{D}$}$

```python
def get_comoving_width(z_values,bandwidth,f_21=1420):

    c = 3e8 # speed of light in vaccum
    comv_width = ((c*(np.ones(len(z_values))+z_values)**2)/(f_21*cosm.H(z_values).value*1000))*bandwidth

    return comv_width

print cosm.H0.value
plt.plot(z_values,get_comoving_width(z_values,get_bandwidth(window,freqs)))
plt.xlabel('Redshift')
plt.ylabel('Comoving Width [Mpc]')
```

$\textbf{Total Solid Angle $\Omega$}$

```python
def omega_gaussian_beam(dish_diam,frqs,dfrqs=1.0):
    omeg_frq = np.zeros(len(frqs))
    for freq_i in range(frqs.size):
        sigma_value = sigma_func(dish_diam,frqs[freq_i])
        omeg_frq[freq_i] = dblquad(lambda theta,phi,theta_c, phi_c,sigma_fwhm,e_theta,e_phi,e_sigma : gaussian_beam(theta_c, phi_c,theta,phi,sigma_fwhm,e_theta,e_phi,e_sigma),-np.pi,np.pi,-np.pi/2.0,np.pi/2.0, args=(0.0,0.0,sigma_value,0.0,0.0,0.0))[0]
    Omega = intg.simps(omeg_frq,frqs,dfrqs)

    return Omega

omega_gaussian_beam(14.0,freqs)
```

    394.78417583781857

$\textbf{$\textit{k}_{\perp}$ and $\textit{k}_{\parallel}$ }$

\begin{equation}
\textit{k}_{\perp} = \frac{2\pi\mathbf{b}}{\lambda\textit{D}(\textit{z})}
\end{equation}
and
\begin{equation}
\textit{k}_{\parallel} \approx\frac{2\pi\tau}{\Delta \textit{D}(\textit{z})}
\end{equation}

```python
def get_k_perp(blvec,freq_i,f_0 = 1420,c=3e8):

    "Compute the k-perpducular"

    wavelen = c/(freq_i*10**6)

    redshift = get_redshift(f_0,freq_i)

    comoving_distance_transv = cosm.comoving_transverse_distance(redshift).value

    k_x,k_y = 2.0*np.pi*blvec[0]/(wavelen*comoving_distance_transv), 2.0*np.pi*blvec[1]/(wavelen*comoving_distance_transv)
    k_perp = np.sqrt(k_x**2 + k_y**2)
    return k_perp

def get_k_parall(freq_array,f_0=1420):

    "Compute k-parallel"
    window_func = signal.nuttall(freq_array.size)

    delays = np.fft.fftfreq(freq_array.size,freq_array[1]-freq_array[0])
    delays = np.fft.fftshift(delays)

    pos_delays = delays[delays.size/2:]
    neg_delays = delays[:delays.size/2]
    redshift = [get_redshift(f_0,freq_array[j]) for j in range(freq_array.size)]

    comoving_dist_lof = get_comoving_width(redshift,get_bandwidth(window_func,freq_array))

    k_par=  2.0*np.pi*delays/comoving_dist_lof

    return k_par

def get_wedge(k_perp,freq_i,f_0 = 1420,c=3e8):

    redshift = get_redshift(f_0,freq_i)

    k_par = (cosm.H0.value*cosm.H(redshift).value*1e6)/(c*(1 +redshift))*k_perp

    return k_par

k_perp =np.array([get_k_perp(blvecs[j],150.0) for j in range(len(blvecs))])

k_par = get_k_parall(freqs)

```

```python
plt.plot(np.abs(vis_gb_add_noise_sorted_ew[0]),label='GB')
plt.plot(np.abs(vis_ad_add_noise_sorted_ew[0]),label='AD')
plt.legend(loc='best')
```

$\textbf{Delay Transform Visibility}$

```python

def get_delay_vis(data,window,frqs):
    delay_data=np.fft.ifft(data*window)
    delaydata_shift=np.fft.fftshift(delay_data)
    delays = np.fft.fftfreq(frqs.size,frqs[1]-frqs[0])
    delays = np.fft.fftshift(delays)

    return [delays,delaydata_shift]

frq_mid = 150 # Central frequency in MHz
c=3e8
jy2mK = 1e-26*(c/(frq_mid*1e6))**2/((2*CNST1.k)*1e3)

blvecs=[[np.power(4,2),0.0,0.0],[np.power(4,3),0.0,0.0],[np.power(4,4),0.0,0.0],[np.power(4,5),0.0,0.0]]

def geometric_delay(bl):
    return bl/c

bl= 14.6
g_dl_vecs = [geometric_delay(np.linalg.norm(blvecs_sorted[bl_i])) for bl_i in range(len(blvecs_sorted))]

delays = np.fft.fftfreq(freqs.size,freqs[1]*10**6-freqs[0]*10**6)
delays = np.fft.fftshift(delays)
ddata_gb = np.array([get_delay_vis(vis_gb_add_noise_sorted_ew[bl_i]*jy2mK,window,freqs)[1] for bl_i in range(vis_gb_add_noise_sorted_ew.shape[0])])
ddata_ad = np.array([get_delay_vis(vis_ad_add_noise_sorted_ew[bl_i]*jy2mK,window,freqs)[1] for bl_i in range(vis_ad_add_noise_sorted_ew.shape[0])])

```

```python

```

```python
bllen = [np.linalg.norm(blvecs_sorted[i]) for i in range(len(blvecs_sorted))]

k_perp =np.array([get_k_perp(blvecs_sorted[j],150.0) for j in range(len(blvecs_sorted))])

k_par = get_k_parall(freqs)

print k_par
```

    [-0.12199816 -0.11964501 -0.11728825 -0.1149279  -0.11256395 -0.11019641
     -0.10782529 -0.10545058 -0.10307229 -0.10069043 -0.098305   -0.095916
     -0.09352343 -0.0911273  -0.08872762 -0.08632438 -0.0839176  -0.08150726
     -0.07909339 -0.07667598 -0.07425503 -0.07183055 -0.06940254 -0.066971
     -0.06453595 -0.06209738 -0.05965529 -0.05720969 -0.05476059 -0.05230798
     -0.04985187 -0.04739227 -0.04492917 -0.04246259 -0.03999251 -0.03751896
     -0.03504192 -0.03256141 -0.03007743 -0.02758998 -0.02509906 -0.02260468
     -0.02010684 -0.01760555 -0.0151008  -0.0125926  -0.01008096 -0.00756588
     -0.00504735 -0.00252539  0.          0.00252882  0.00506107  0.00759674
      0.01013583  0.01267833  0.01522425  0.01777358  0.02032632  0.02288246
      0.025442    0.02800493  0.03057126  0.03314098  0.03571409  0.03829058
      0.04087046  0.04345371  0.04604034  0.04863034  0.05122371  0.05382044
      0.05642054  0.059024    0.06163081  0.06424098  0.0668545   0.06947136
      0.07209157  0.07471513  0.07734202  0.07997224  0.0826058   0.08524269
      0.0878829   0.09052644  0.0931733   0.09582348  0.09847697  0.10113377
      0.10379389  0.1064573   0.10912403  0.11179405  0.11446737  0.11714398
      0.11982389  0.12250708  0.12519356  0.12788333]

```python
extent =[k_perp[0],k_perp[-1],k_par[-1],k_par[0]]

plt.figure(figsize=(5,20))
plt.imshow(np.abs(ddata_gb.T),extent=extent,norm=LogNorm())
plt.xlabel(r'$k_{\perp}[Mpc^-1]$')
plt.ylabel(r'$k_{\parallel}[Mpc^-1]$')

```

```python

```

$\textbf{Delay Power Spectrum}$

\begin{equation}
\textit{P}(\textit{k}_{\parallel},\textit{k}_{\perp}) = \Big(\frac{\lambda^2}{2\textit{k}_{\textrm{B}}}\Big)^2\Big(\frac{\textit{D}^2\Delta \textit{D}}{\Delta\textit{B}}\Big)\Big(\frac{1}{\Omega\Delta \textit{B}}\Big) \langle |\textit{u}(\mathbf{u},\tau)|^2\rangle
\end{equation}

```python
def get_delay_ps(freq_array,bl_dtvis,f_0=1420,c=3e8,k=CNST1.k):

    "Compute Delay Power Spectrum"

    window_func = signal.nuttall(freq_array.size)

    redshift = [get_redshift(f_0,freq_array[j]) for j in range(freq_array.size)]

    comvoing_dist_trans = cosm.comoving_transverse_distance(redshift).value
    comoving_width_lof =get_comoving_width(redshift,get_bandwidth(window_func,freq_array))

    bandwidth = get_bandwidth(window_func,freq_array)
    omega = omega_gaussian_beam(14.0,freq_array)

    cf = [(np.power(((c/(freq_array[freq_j]*10**6))**2)/(2*k*1e3),2)*(comvoing_dist_trans[freq_j]*comoving_width_lof[freq_j])/(np.power(bandwidth,2)*10**6))*1.0/(omega) for freq_j in range(freq_array.size)]

    ps = cf*np.power(np.abs(bl_dtvis),2)

    #print cf

    return ps

ddata_gb[0].size

dps = np.array([get_delay_ps(freqs,ddata_ad[j]) for j in range(ddata_gb.shape[0])])

```

```python

extent =[k_perp[0],k_perp[-1],k_par[-1],k_par[0]]

plt.figure(figsize=(10,10))
plt.imshow(dps.T,extent=extent,norm=LogNorm())
plt.xlabel(r'$k_{\perp}[Mpc^-1]$')
plt.ylabel(r'$k_{\parallel}[Mpc^-1]$')
plt.colorbar()
```

Simulations/Simulating Multi-baseline Visibility and Delay Power Spectrum.ipynb


##### Copyright 2020 The TensorFlow Authors.

```
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
```

# 勾配の計算

   TensorFlow.orgで表示
   Google Colab で実行
  GitHub でソースを表示{
  ノートブックをダウンロード

このチュートリアルでは、量子回路の期待値の勾配計算アルゴリズムについて説明します。

量子回路で特定のオブザーバブルの期待値の勾配を計算することは、複雑なプロセスです。行列の乗算やベクトルの加算などの従来の機械学習変換では簡単に使用できる解析的勾配式がありますが、オブザーバブルの期待値には、このような解析的勾配式は必ずしもありません。そのため、シナリオに適したさまざまな量子勾配計算方法を使用する必要があります。このチュートリアルでは、2 つの異なる微分スキームを比較対照します。

## セットアップ

```
!pip install tensorflow==2.1.0
```

TensorFlow Quantum をインストールします。

```
!pip install tensorflow-quantum
```

次に、TensorFlow とモジュールの依存関係をインポートします。

```
import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
```

## 1. 予備

量子回路の勾配計算の概念をもう少し具体的に見てみましょう。次のようなパラメータ化された回路があるとします。

```
qubit = cirq.GridQubit(0, 0)
my_circuit = cirq.Circuit(cirq.Y(qubit)**sympy.Symbol('alpha'))
SVGCircuit(my_circuit)
```

オブザーバブルは以下のとおりです。

```
pauli_x = cirq.X(qubit)
pauli_x
```

この演算子を見ると、$⟨Y(\alpha)| X | Y(\alpha)⟩ = \sin(\pi \alpha)$ であることが分かります。

```
def my_expectation(op, alpha):
    """Compute ⟨Y(alpha)| `op` | Y(alpha)⟩"""
    params = {'alpha': alpha}
    sim = cirq.Simulator()
    final_state = sim.simulate(my_circuit, params).final_state
    return op.expectation_from_wavefunction(final_state, {qubit: 0}).real

my_alpha = 0.3
print("Expectation=", my_expectation(pauli_x, my_alpha))
print("Sin Formula=", np.sin(np.pi * my_alpha))
```

$f_{1}(\alpha) = ⟨Y(\alpha)| X | Y(\alpha)⟩$ と定義すると $f_{1}^{'}(\alpha) = \pi \cos(\pi \alpha)$ になります。確認しましょう。

```
def my_grad(obs, alpha, eps=0.01):
    grad = 0
    f_x = my_expectation(obs, alpha)
    f_x_prime = my_expectation(obs, alpha + eps)
    return ((f_x_prime - f_x) / eps).real

print('Finite difference:', my_grad(pauli_x, my_alpha))
print('Cosine formula:   ', np.pi * np.cos(np.pi * my_alpha))
```

## 2. 微分器の必要性

より大きな回路では、与えられた量子回路の勾配を正確に計算する式は必ずしもありません。単純な式では勾配を計算できない場合、`tfq.differentiators.Differentiator`クラスを使用すると、回路の勾配を計算するためのアルゴリズムを定義できます。たとえば、TensorFlow Quantum（TFQ）で上記の例を次のように再現できます。

```
expectation_calculation = tfq.layers.Expectation(
    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))

expectation_calculation(my_circuit,
                        operators=pauli_x,
                        symbol_names=['alpha'],
                        symbol_values=[[my_alpha]])
```

ただし、サンプリングに基づいて期待値を推定するように切り替えると（実際のデバイスで何が起こるか）、値が少し変わる可能性があります。これは、期待値が不正確になることを意味します。

```
sampled_expectation_calculation = tfq.layers.SampledExpectation(
    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))

sampled_expectation_calculation(my_circuit,
                                operators=pauli_x,
                                repetitions=500,
                                symbol_names=['alpha'],
                                symbol_values=[[my_alpha]])
```

これは、勾配における深刻な精度の問題につながる可能性があります。

```
# Make input_points = [batch_size, 1] array.
input_points = np.linspace(0, 5, 200)[:, np.newaxis].astype(np.float32)
exact_outputs = expectation_calculation(my_circuit,
                                        operators=pauli_x,
                                        symbol_names=['alpha'],
                                        symbol_values=input_points)
imperfect_outputs = sampled_expectation_calculation(my_circuit,
                                                    operators=pauli_x,
                                                    repetitions=500,
                                                    symbol_names=['alpha'],
                                                    symbol_values=input_points)
plt.title('Forward Pass Values')
plt.xlabel('$x$')
plt.ylabel('$f(x)$')
plt.plot(input_points, exact_outputs, label='Analytic')
plt.plot(input_points, imperfect_outputs, label='Sampled')
plt.legend()
```

```
# Gradients are a much different story.
values_tensor = tf.convert_to_tensor(input_points)

with tf.GradientTape() as g:
    g.watch(values_tensor)
    exact_outputs = expectation_calculation(my_circuit,
                                            operators=pauli_x,
                                            symbol_names=['alpha'],
                                            symbol_values=values_tensor)
analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)

with tf.GradientTape() as g:
    g.watch(values_tensor)
    imperfect_outputs = sampled_expectation_calculation(
        my_circuit,
        operators=pauli_x,
        repetitions=500,
        symbol_names=['alpha'],
        symbol_values=values_tensor)
sampled_finite_diff_gradients = g.gradient(imperfect_outputs, values_tensor)

plt.title('Gradient Values')
plt.xlabel('$x$')
plt.ylabel('$f^{\'}(x)$')
plt.plot(input_points, analytic_finite_diff_gradients, label='Analytic')
plt.plot(input_points, sampled_finite_diff_gradients, label='Sampled')
plt.legend()
```

ここでは、解析の場合は有限差分式は勾配自体を高速に計算できますが、サンプリングベースの方法の場合ではノイズが多すぎることが分かります。適切な勾配を計算するには、より注意深い手法を使用する必要があります。次に、解析的期待値の勾配計算にはあまり適していませんが、実際のサンプルベースの方法の場合ではより優れたパフォーマンスを発揮する、大幅に低速な手法を見ていきます。

```
# A smarter differentiation scheme.
gradient_safe_sampled_expectation = tfq.layers.SampledExpectation(
    differentiator=tfq.differentiators.ParameterShift())

with tf.GradientTape() as g:
    g.watch(values_tensor)
    imperfect_outputs = gradient_safe_sampled_expectation(
        my_circuit,
        operators=pauli_x,
        repetitions=500,
        symbol_names=['alpha'],
        symbol_values=values_tensor)

sampled_param_shift_gradients = g.gradient(imperfect_outputs, values_tensor)

plt.title('Gradient Values')
plt.xlabel('$x$')
plt.ylabel('$f^{\'}(x)$')
plt.plot(input_points, analytic_finite_diff_gradients, label='Analytic')
plt.plot(input_points, sampled_param_shift_gradients, label='Sampled')
plt.legend()
```

上記から、特定の微分器が特定の研究シナリオに最適であることがわかります。一般に、デバイスノイズなどに対して堅牢な、低速のサンプルベースの方法は、より「現実的」設定でアルゴリズムをテストまたは実装する場合に適した微分器です。有限差分のようなより高速な方法はアルゴリズムのデバイスにおける実行可能性にはまだ関心がなく、解析的計算やより高いスループットが必要な場合に最適です。

## 3. 複数のオブザーバブル

2 番目のオブザーバブルを使用し、TensorFlow Quantum が 1 つの回路に対して複数のオブザーバブルをサポートする方法を見てみましょう。

```
pauli_z = cirq.Z(qubit)
pauli_z
```

このオブザーバブルが以前と同じ回路で使用されている場合、$f_{2}(\alpha) = ⟨Y(\alpha)| Z | Y(\alpha)⟩ = \cos(\pi \alpha)$ および$f_{2}^{'}(\alpha) = -\pi \sin(\pi \alpha)$になります。確認します。

```
test_value = 0.

print('Finite difference:', my_grad(pauli_z, test_value))
print('Sin formula:      ', -np.pi * np.sin(np.pi * test_value))
```

（ほぼ）一致します。

次に、$g(\alpha) = f_{1}(\alpha) + f_{2}(\alpha)$ を定義すると、$g'(\alpha) = f_{1}^{'}(\alpha) + f^{'}_{2}(\alpha)$になります。TensorFlow Quantum で複数のオブザーバブルを定義して回路と共に使用するには、$g$ にさらに項を追加します。

これは、回路内の特定のシンボルの勾配が、その回路に適用されたそのシンボルの各オブザーバブルに関する勾配の合計に等しいことを意味します。これは、TensorFlow の勾配取得およびバックプロパゲーションと互換性があります（特定のシンボルの勾配として、すべてのオブザーバブルの勾配の合計を指定します）。

```
sum_of_outputs = tfq.layers.Expectation(
    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))

sum_of_outputs(my_circuit,
               operators=[pauli_x, pauli_z],
               symbol_names=['alpha'],
               symbol_values=[[test_value]])
```

ここで、最初のエントリは期待値 w.r.t Pauli X であり、2 番目のエントリは期待値 w.r.t Pauli Z です。勾配は以下のとおりです。

```
test_value_tensor = tf.convert_to_tensor([[test_value]])

with tf.GradientTape() as g:
    g.watch(test_value_tensor)
    outputs = sum_of_outputs(my_circuit,
                             operators=[pauli_x, pauli_z],
                             symbol_names=['alpha'],
                             symbol_values=test_value_tensor)

sum_of_gradients = g.gradient(outputs, test_value_tensor)

print(my_grad(pauli_x, test_value) + my_grad(pauli_z, test_value))
print(sum_of_gradients.numpy())
```

ここで、各オブザーバブルの勾配の合計が実際に $\alpha$ の勾配であることを確認しました。この動作は、すべての TensorFlow Quantum 微分器によってサポートされており、TensorFlow の他の部分との互換性において重要な役割を果たします。

## 4. 高度な使用

ここでは、量子回路用に独自のカスタム微分ルーチンを定義する方法を紹介します。TensorFlowQuantum サブクラス`tfq.differentiators.Differentiator`内に存在するすべての微分器。微分器は`differentiate_analytic`と`differentiate_sampled`を実装する必要があります。

以下では、TensorFlow Quantum 構造を使用して、このチュートリアルの最初の部分の閉形式の解を実装します。

```
class MyDifferentiator(tfq.differentiators.Differentiator):
    """A Toy differentiator for <Y^alpha | X |Y^alpha>."""

    def __init__(self):
        pass

    @tf.function
    def _compute_gradient(self, symbol_values):
        """Compute the gradient based on symbol_values."""

        # f(x) = sin(pi * x)
        # f'(x) = pi * cos(pi * x)
        return tf.cast(tf.cos(symbol_values * np.pi) * np.pi, tf.float32)

    @tf.function
    def differentiate_analytic(self, programs, symbol_names, symbol_values,
                               pauli_sums, forward_pass_vals, grad):
        """Specify how to differentiate a circuit with analytical expectation.

        This is called at graph runtime by TensorFlow. `differentiate_analytic`
        should calculate the gradient of a batch of circuits and return it
        formatted as indicated below. See
        `tfq.differentiators.ForwardDifference` for an example.

        Args:
            programs: `tf.Tensor` of strings with shape [batch_size] containing
                the string representations of the circuits to be executed.
            symbol_names: `tf.Tensor` of strings with shape [n_params], which
                is used to specify the order in which the values in
                `symbol_values` should be placed inside of the circuits in
                `programs`.
            symbol_values: `tf.Tensor` of real numbers with shape
                [batch_size, n_params] specifying parameter values to resolve
                into the circuits specified by programs, following the ordering
                dictated by `symbol_names`.
            pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]
                containing the string representation of the operators that will
                be used on all of the circuits in the expectation calculations.
            forward_pass_vals: `tf.Tensor` of real numbers with shape
                [batch_size, n_ops] containing the output of the forward pass
                through the op you are differentiating.
            grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]
                representing the gradient backpropagated to the output of the
                op you are differentiating through.

        Returns:
            A `tf.Tensor` with the same shape as `symbol_values` representing
            the gradient backpropagated to the `symbol_values` input of the op
            you are differentiating through.
        """

        # Computing gradients just based off of symbol_values.
        return self._compute_gradient(symbol_values) * grad

    @tf.function
    def differentiate_sampled(self, programs, symbol_names, symbol_values,
                              pauli_sums, num_samples, forward_pass_vals, grad):
        """Specify how to differentiate a circuit with sampled expectation.

        This is called at graph runtime by TensorFlow. `differentiate_sampled`
        should calculate the gradient of a batch of circuits and return it
        formatted as indicated below. See
        `tfq.differentiators.ForwardDifference` for an example.

        Args:
            programs: `tf.Tensor` of strings with shape [batch_size] containing
                the string representations of the circuits to be executed.
            symbol_names: `tf.Tensor` of strings with shape [n_params], which
                is used to specify the order in which the values in
                `symbol_values` should be placed inside of the circuits in
                `programs`.
            symbol_values: `tf.Tensor` of real numbers with shape
                [batch_size, n_params] specifying parameter values to resolve
                into the circuits specified by programs, following the ordering
                dictated by `symbol_names`.
            pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]
                containing the string representation of the operators that will
                be used on all of the circuits in the expectation calculations.
            num_samples: `tf.Tensor` of positive integers representing the
                number of samples per term in each term of pauli_sums used
                during the forward pass.
            forward_pass_vals: `tf.Tensor` of real numbers with shape
                [batch_size, n_ops] containing the output of the forward pass
                through the op you are differentiating.
            grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]
                representing the gradient backpropagated to the output of the
                op you are differentiating through.

        Returns:
            A `tf.Tensor` with the same shape as `symbol_values` representing
            the gradient backpropagated to the `symbol_values` input of the op
            you are differentiating through.
        """
        return self._compute_gradient(symbol_values) * grad
```

この新しい微分器は、既存の`tfq.layer`オブジェクトで使用できるようになりました。

```
custom_dif = MyDifferentiator()
custom_grad_expectation = tfq.layers.Expectation(differentiator=custom_dif)

# Now let's get the gradients with finite diff.
with tf.GradientTape() as g:
    g.watch(values_tensor)
    exact_outputs = expectation_calculation(my_circuit,
                                            operators=[pauli_x],
                                            symbol_names=['alpha'],
                                            symbol_values=values_tensor)

analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)

# Now let's get the gradients with custom diff.
with tf.GradientTape() as g:
    g.watch(values_tensor)
    my_outputs = custom_grad_expectation(my_circuit,
                                         operators=[pauli_x],
                                         symbol_names=['alpha'],
                                         symbol_values=values_tensor)

my_gradients = g.gradient(my_outputs, values_tensor)

plt.subplot(1, 2, 1)
plt.title('Exact Gradient')
plt.plot(input_points, analytic_finite_diff_gradients.numpy())
plt.xlabel('x')
plt.ylabel('f(x)')
plt.subplot(1, 2, 2)
plt.title('My Gradient')
plt.plot(input_points, my_gradients.numpy())
plt.xlabel('x')
```

この新しい微分器を使用して、微分可能な演算を生成できるようになりました。

重要点：微分器は一度に 1 つの演算にしか接続できないため、以前に演算に接続されていた微分器は、新しい演算に接続する前に更新する必要があります。

```
# Create a noisy sample based expectation op.
expectation_sampled = tfq.get_sampled_expectation_op(
    cirq.DensityMatrixSimulator(noise=cirq.depolarize(0.01)))

# Make it differentiable with your differentiator:
# Remember to refresh the differentiator before attaching the new op
custom_dif.refresh()
differentiable_op = custom_dif.generate_differentiable_op(
    sampled_op=expectation_sampled)

# Prep op inputs.
circuit_tensor = tfq.convert_to_tensor([my_circuit])
op_tensor = tfq.convert_to_tensor([[pauli_x]])
single_value = tf.convert_to_tensor([[my_alpha]])
num_samples_tensor = tf.convert_to_tensor([[1000]])

with tf.GradientTape() as g:
    g.watch(single_value)
    forward_output = differentiable_op(circuit_tensor, ['alpha'], single_value,
                                       op_tensor, num_samples_tensor)

my_gradients = g.gradient(forward_output, single_value)

print('---TFQ---')
print('Foward:  ', forward_output.numpy())
print('Gradient:', my_gradients.numpy())
print('---Original---')
print('Forward: ', my_expectation(pauli_x, my_alpha))
print('Gradient:', my_grad(pauli_x, my_alpha))
```

成功：TensorFlow Quantum が提供するすべての微分器を使用して、独自の微分器を定義できるようになりました。