```python
from estado import *
from sympy import *
import copy

```

```python
# palta = 3000
# talta = 400
# pbaixa = 800
# cond = 10
#
# estado_antes_alta = estado('water','gas',palta,talta)
# a = turbina(estado_antes_alta,1,pbaixa)
#
# estado_pos_alta = a[0]
#
# estado_pre_baixa = estado('water','gas',pbaixa,talta)
#
# a = turbina(estado_pre_baixa,1,cond)
# estado_pos_baixa = a[0]
```

```python

#estado3 = estado_antes_alta
#estado4 = estado_pos_alta
#estado5 = estado_pre_baixa
#estado6 = estado_pos_baixa
#estado1 = copy.copy(estado6)

```

```python
#estado1.propriedade_dado_titulo('specific_enthalpy', 0)
#estado1.propriedade_dado_titulo('specific_entropy', 0)

```

```python
#estado1_ = estado('water','liq', estado3._p, 45)
#estado2 = busca_estado('specific_entropy', estado1.specific_entropy,'T',estado1_)

```

```python
#print(estado6.titulo)
#a = (estado3.specific_enthalpy - estado4.specific_enthalpy + estado1.specific_enthalpy - estado2.specific_enthalpy + estado5.specific_enthalpy - estado6.specific_enthalpy)/(estado3.specific_enthalpy - estado2.specific_enthalpy)
#print(a)
```

```python

```

```python
# palta = 10000
# talta = 520
# pcond = 6.5
# potliq = 150000
#
# estado3 = estado('water','gas',palta,talta)
# estado4 = turbina(estado3,1,pcond)[0]
# estado1 = copy.copy(estado4)
# estado1.propriedade_dado_titulo('specific_enthalpy', 0)
# estado1.propriedade_dado_titulo('specific_entropy', 0)
# estado1.propriedade_dado_titulo('specific_volume', 0)
# potturb_spec = estado3.specific_enthalpy - estado4.specific_enthalpy
# potbomb_spec = estado1.specific_volume*(estado3._p - estado1.pressure*100)
```

```python
# h2 = estado1.specific_enthalpy + potbomb_spec
# h3 = estado3.specific_enthalpy
# h1 = estado1.specific_enthalpy
# h4 = estado4.specific_enthalpy
# potliq_spec = -(h4 - h3 + h1 - h2)
# m = potliq/potliq_spec
# print((h3-h2)*m)
# print((h3-h4+h1-h2)/(h3-h2))
```

```python
# temp = 750
# palta = 2500
# pbaixa = 600
# tcond = 50
# qout = 10500
```

```python
# estado6 = estado('water','saturado',T=tcond)
# estado6.propriedade_dado_titulo('specific_enthalpy', 1)
# estado6.propriedade_dado_titulo('specific_entropy', 1)
#
# estado1 = copy.copy(estado6)
# estado1.propriedade_dado_titulo('specific_enthalpy', 0)
# estado1.propriedade_dado_titulo('specific_entropy', 0)
# estado1.propriedade_dado_titulo('specific_volume', 0)
#
#
# pcond = estado6.pressure*100
#
# estado3 = estado('water','gas',palta,temp)
# a = turbina(estado3,1,pbaixa)
#
# estado4 = a[0]
#
# estado5_ = estado('water','gas',p=estado4.pressure*100,T=500)
#
# estado5 = busca_estado('specific_entropy',estado6.specific_entropy,'T',estado5_)
```

```python
# wbomb = estado1.specific_volume * (estado3.pressure*100 - estado1.pressure*100)
# h2 = estado1.specific_enthalpy + wbomb
# #
#
```

```python
# estado5.temperature
#
#
```

```python
# h4 = estado4.specific_enthalpy
# h3 = estado3.specific_enthalpy
# h5 = estado5.specific_enthalpy
# h6 = estado6.specific_enthalpy
# h1 = estado1.specific_enthalpy
#
```

```python
# qout_spec = h6 - h1
# m = qout/qout_spec
#
# (h3-h4+h5-h6)*m
#
#
```

```python
# estado1_ = estado('water','liq', estado3._p, 45)
# estado2 = busca_estado('specific_entropy', estado1.specific_entropy,'T',estado1_)
# #
#
```

```python
# a = (estado3.specific_enthalpy - estado4.specific_enthalpy + estado1.specific_enthalpy - estado2.specific_enthalpy + estado5.specific_enthalpy - estado6.specific_enthalpy)/(estado3.specific_enthalpy - estado2.specific_enthalpy)
```

```python
# potturb = 8700
# pmax = 3000
# Tmax = 430
# pcond = 60
# nturb = 0.77
#
# estado3 = estado('water','gas',pmax,Tmax)
#
# a = turbina(estado3,nturb,pcond)
#
# estado4 = a[1]
#
# estado1 = copy.copy(estado4)
# estado1.propriedade_dado_titulo('specific_enthalpy', 0)
# estado1.propriedade_dado_titulo('specific_entropy', 0)
# estado1.propriedade_dado_titulo('specific_volume', 0)
```

```python
# wbomb = estado1.specific_volume * (estado3.pressure*100 - estado1.pressure*100)
# h2 = estado1.specific_enthalpy + wbomb
```

```python
# h1 = estado1.specific_enthalpy
# h3 = estado3.specific_enthalpy
# h4 = estado4.specific_enthalpy
```

```python
# potturb_spec = (h3 - h4)*nturb
# m = potturb/potturb_spec
```

```python
# nciclo = ((h3-h4)*nturb+h1-h2)/(h3-h2)
```

```python
# print(m)
# print(nciclo)
```

```python
kgs = 18
palta = 2000
talta = 580
tcond = 37
pbaixa = 300
tbaixa = 390

estado3 = estado('water','gas',palta,talta)
estado4 = turbina(estado3,1,pbaixa)[0]

estado5 = estado('water', 'gas', pbaixa, tbaixa)

estado6_ = estado('water','saturado',T=tcond)

estado6 = copy.copy(estado6_)
estado6.titulo_dada_propriedade('specific_entropy', estado5.specific_entropy)
estado6.propriedade_dado_titulo('specific_enthalpy',estado6.titulo)

estado1 = copy.copy(estado6_)
estado1.propriedade_dado_titulo('specific_entropy',0)
estado1.propriedade_dado_titulo('specific_enthalpy',0)

estado2_ = estado('water', 'liq', palta, 50)
estado2 = busca_estado('specific_entropy',estado1.specific_entropy,'T',estado2_)
```

```python
h4 = estado4.specific_enthalpy
h3 = estado3.specific_enthalpy
h5 = estado5.specific_enthalpy
h6 = estado6.specific_enthalpy
h1 = estado1.specific_enthalpy
h2 = estado2.specific_enthalpy
```

```python
efic = (h3 - h4 + h5 - h6 + h1 - h2)/(h3 - h2 + h5 - h4)
```

```python
trab_total = (h3 - h2 + h5 - h4)*kgs
```

```python
print(efic)
print(trab_total)
```

    0.3742314885413028
    66653.75048281379


# kilojoule Library
We will be using a custom python library, `kilojoule`, written specifically for this course.  The main advantage to this approach is the nomenclature for the functions you will be using in Python will be consistent with the nomenclature from your textbook.  The disadvantage to this approach is there will be limited sources for external tech support (you won't find example code using this library outside of this course).

Prior to using this library, it needs to be installed to a location where the Python interpreter can find it.  If you are using Coclac, this will have already been done for you.  I you are using a local installation of Python you can install the library from the Python Package Index (PyPi) using the command `pip install kilojoule`.

After installing the library, you will need to import it into each notebook where you intend to use it.  If you are referring back to this document and want a quick import template for starting a new file, you can use the following code.  If this is your first time reading this document, the code in the following block will be explained in detail below.

```python
from kilojoule.templates.kSI_C import *

# Initialize an interface to evaluate fluid properties
# You can create multiple interfaces as long as they are stored in different variables
air = idealgas.Properties('Air', unit_system='kSI_C') # Treat air as an ideal gas
water = realfluid.Properties('Water', unit_system='kSI_C') # Treat water as a real fluid
```

## Units
The `kilojoule` library is designed to make use of dimensional "quantities" rather than simply doing calculations with numbers.  In engineering, all your calculations use numbers to represent physical quantities and your calculations have no meaning without connection to appropriate units.  By including the physical units as an integral part of the calculation process, we keep the physical significance of our calculation in focus and will avoid costly mistakes from unit conversion errors.  To do this we will make use of the third-party library `pint` for managing units.  By executing the code in the block before this one, you have already loaded this library in the background; it is accessible through the `units` and `Quantity` objects.

We will first define a few property values, i.e. temperature and pressure

```python
# The Quantity(value,units) notation defines a physical quantity with a magnitude associated with a type of unit
T = Quantity(300.0,'degK')
print(T)
print(f'T = {T} = {T.to("degC")} = {T.to("degF")} = {T.to("degR")}')

p = Quantity(1.0,'atm')
print(p)
print(f'p = {p} = {p.to("kPa")} = {p.to("Pa")} = {p.to("psi")} = {p.to("bar")}')

p.ito('kPa')
print(p)
```

    300.0 K
    T = 300.0 K = 26.85 °C = 80.33 °F = 540.0 °R
    1.0 atm
    p = 1.0 atm = 101.33 kPa = 1.0132×10⁵ Pa = 14.696 psi = 1.0133 bar
    101.33 kPa

We were able to access the quantities stored in the variables `T` and `p` in any unit system by using the notation `var.to("desired units")`, which temporarily convertes the units to the specified form, or we can permanently convert a variable to a different unit system using the notation `var.ito("desired units")`. We defined temperature in metric units, then displayed it in both alternate metric and English units.  Whereas we defined pressure in English units, then displayed it in both alternate English and metric units.  This system allows us to quickly switch back and forth between unit systems as needed.

The real benefit of this system is most evident when we start performing calculations with combined units.  In the following code we will calculate the change in energy of a mass that is changing temperature, velocity, and elevation.
\begin{align}\Delta E_{CV} &= m(\Delta u + \Delta ke + \Delta pe) \\&= m\left(u_2-u_1 + \frac{V_2^2}{2}-\frac{V_1^2}{2} + g(z_2-z_1)\right)\end{align}

```python
m = Quantity(10.0,'kg')       # metric
u_1 = Quantity(300.0,'kJ/kg') # metric
u_2 = Quantity(200.0,'kJ/kg') # metric
Vel_1 = Quantity(20.0,'mph')  # English
Vel_2 = Quantity(30.5,'m/s')  # metric
g = Quantity(9.8,'m/s^2')     # metric
z_2 = Quantity(30.1,'ft')     # English
z_1 = Quantity(1.2,'m')       # metric

Delta_u = u_2-u_1
print(f"Delta u = {u_2} - {u_1} = {Delta_u}")

Delta_ke = (Vel_2**2-Vel_1**2)/2
print(f"Delta ke = {Vel_2**2/2} - {Vel_1**2/2} = {Delta_ke}")

Delta_pe = g*(z_2-z_1)
print(f"Delta pe = {g}({z_2}-{z_1}) = {Delta_pe}")

Delta_E_CV = m*(u_2-u_1 + (Vel_2**2-Vel_1**2)/2 + g*(z_2-z_1))
print(f"Delta E = {m}({Delta_u} + {Delta_ke} + {Delta_pe}) = {Delta_E_CV}")

Calculations();
```

    Delta u = 200.0 kJ/kg - 300.0 kJ/kg = -100.0 kJ/kg
    Delta ke = 465.12 m²/s² - 200.0 mph² = 425.16 m²/s²
    Delta pe = 9.8 m/s²(30.1 ft-1.2 m) = 256.4 ft·m/s²
    Delta E = 10.0 kg(-100.0 kJ/kg + 425.16 m²/s² + 256.4 ft·m/s²) = -994.97 kJ

\[
  \begin{aligned}
    { m }&={ \left( 10.0\,\mathrm{kg} \right) } = { 10.0\ \mathrm{kg}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { u_{1} }&={ \left( 300.0\,\frac{\mathrm{kJ}}{\mathrm{kg}} \right) } = { 300.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { u_{2} }&={ \left( 200.0\,\frac{\mathrm{kJ}}{\mathrm{kg}} \right) } = { 200.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { Vel_{1} }&={ \left( 20.0\,\mathrm{mph} \right) } = { 20.0\ \mathrm{mph}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { Vel_{2} }&={ \left( 30.5\,\frac{\mathrm{m}}{\mathrm{s}} \right) } = { 30.5\ \frac{\mathrm{m}}{\mathrm{s}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { g }&={ \left( 9.8\,\frac{\mathrm{m}}{\mathrm{s}^{2}} \right) } = { 9.8\ \frac{\mathrm{m}}{\mathrm{s}^{2}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { z_{2} }&={ \left( 30.1\,\mathrm{ft} \right) } = { 30.1\ \mathrm{ft}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { z_{1} }&={ \left( 1.2\,\mathrm{m} \right) } = { 1.2\ \mathrm{m}  }
  \end{aligned}
\]

\[
  \begin{aligned}{ \Delta{}u }&={ u_{2} - u_{1} }\\
    &={ \left( 200.0\ \frac{\mathrm{kJ}}{\mathrm{kg}} \right) - \left( 300.0\ \frac{\mathrm{kJ}}{\mathrm{kg}} \right) }\\
    &={  -100.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}{ \Delta{}ke }&={ \frac{Vel_{2}^{2} - Vel_{1}^{2}}{2} }\\
    &={ \frac{\left( 30.5\ \frac{\mathrm{m}}{\mathrm{s}} \right)^{ 2 } - \left( 20.0\ \mathrm{mph} \right)^{ 2 }}{ 2 } }\\
    &={  425.16\ \frac{\mathrm{m}^{2}}{\mathrm{s}^{2}}  }
  \end{aligned}
\]

\[
  \begin{aligned}{ \Delta{}pe }&={ g \left(z_{2} - z_{1}\right) }\\
    &={ \left( 9.8\ \frac{\mathrm{m}}{\mathrm{s}^{2}} \right) \left(\left( 30.1\ \mathrm{ft} \right) - \left( 1.2\ \mathrm{m} \right)\right) }\\
    &={  256.4\ \frac{\mathrm{ft} \cdot \mathrm{m}}{\mathrm{s}^{2}}  }
  \end{aligned}
\]

\[
  \begin{aligned}{ \Delta{}E_{CV} }&={ m \left(g \left(z_{2} - z_{1}\right) + u_{2} - u_{1} + \frac{Vel_{2}^{2} - Vel_{1}^{2}}{2}\right) }\\
    &={ \left( 10.0\ \mathrm{kg} \right) \left(\left( 9.8\ \frac{\mathrm{m}}{\mathrm{s}^{2}} \right) \left(\left( 30.1\ \mathrm{ft} \right) - \left( 1.2\ \mathrm{m} \right)\right) + \left( 200.0\ \frac{\mathrm{kJ}}{\mathrm{kg}} \right) - \left( 300.0\ \frac{\mathrm{kJ}}{\mathrm{kg}} \right) + \frac{\left( 30.5\ \frac{\mathrm{m}}{\mathrm{s}} \right)^{ 2 } - \left( 20.0\ \mathrm{mph} \right)^{ 2 }}{ 2 }\right) }\\
    &={  -994.97\ \mathrm{kJ}  }
  \end{aligned}
\]

Notice that in the above example, the units for each of the terms were in different systems until they were combined.

## States Database
Many of the systems we will be analyzing will have many states with multiple properties of interest at each state.  Keeping track of these states and properties in a consistent, organized manner will make your code cleaner and easier to maintain.  To aid in this, the `kilojoule` library provides a data structure designed specifically for this purpose.  The `QuantityTable` behaves similarly to a nested Python dictionary.  You can view the data structure as a table with columns representing properties and rows representing states.  Each property column has a defined unit that will apply to all it's values, i.e. all temperatures stored in $^\circ\text{C}$.  We first need to import the `QuantityTable` class from the `kilojoule.organization` module. *(Note: this will already be loaded if you recently executed the first code block in this notebook)*

```python
from kilojoule.organization import QuantityTable
```

We can now initialize our states database (`QuantityTable(...)`) and store it in a variable where we can easily access it (`states = ...`).  There are a few ways to fill out the table columns with properties and units, but the straight forward way is to make a dictionary with the desired properties as keys associated with the appropriate units (`properties_dict = {'property symbol':'units', ...}`).  Note: a few templates, such as the one you imported at the beginning of this notebook, provide pre-built tables for common variables used in this course to make this process easier.

```python
# Make a dictionary with the types of properties you want to track and units for each property
properties_dict = {
    'T':'degC',         # Temperature: unit options ('K','degC','degF','degR')
    'p':'kPa',          # pressure: unit options ('kPa','bar','psi','atm',etc.)
    'v':'m^3/kg',       # specific volume
    'u':'kJ/kg',        # specific internal energy
    'h':'kJ/kg',        # specific enthalpy
    's':'kJ/kg/K',      # specific entropy
    'x':'',             # quality: dimensionless units enter as an empty string
}

# Make a database to hold the property values for each state and store in the variable name `states`
states = QuantityTable(properties=properties_dict)

# The states container is initially empty
print(states)
```

    Empty DataFrame
    Columns: []
    Index: []

The table will initially be empty, be we can add property values for different states to it on the fly.  Recall that we defined preferred units for each of the property columns.  In the example below we will define some temperatures and pressures in consistent units, inconsistent units, and with missing units.

```python
states[1,'T'] = Quantity(30,'degC') # consistent units
states[2,'p'] = Quantity(1,'atm')   # inconsistent units (will be converted kPa)
states[3,'T'] = 100                 # missing units (assumed to be degC)
states[3,'p'] = 200                 # missing units (assumed to be kPa)

print(states)
```

            T        p
    unit   °C      kPa
    1      30        -
    2       -  101.325
    3     100      200

Notice that we originally defined the temperature column to have units of $^\circ\text{C}$, then we explicitly defined a temperature quantity with units of $^\circ\text{C}$ and placed it in state 1 in the temperature column (`states[state, property] = value`).  We then defined a pressure for state 2, but we used an inconsistent unit, i.e. we defined it in $\text{atm}$ when the column expected $\text{kPa}$.  When we view the contents of the states database (`print(states)`) we see that the pressure value at state 2 was automatically converted to $\text{kPa}$.  Finally we defined a temperature and pressure for state 3 without explicitly stating the units.  When this happens, it will be assumed that the values are already in the preferred units.  While this makes the syntax shorter, it is not a good practice since changes in other parts of the code could have unexpected consequences.

An alternate (shorter) syntax for working with the values in the table can be enabled by assigning each column in the table to a variable in the local namespace.  After executing the code below, we will be able to set the quality at state 2 to 50% with the code `x[2] = 0.5` rather than needing to type `states[1,'x'] = 0.5`. Note: this step will also be performed for you if you import one of the pre-built templates.

```python
# The following lines will define (overwrite) convenience variables in the local name space for each of the properties in the states container
# This allows you to add values to (or pull values from) the database using the nomenclature T[1], T[2], p[3], etc.
for property in states.properties:
    globals()[property] = states.dict[property]

x[2] = 0.5
T['inlet'] = Quantity(25,'degC')
print(states)
```

             T        p    x
    unit    °C      kPa
    1       30        -    -
    2        -  101.325  0.5
    3      100      200    -
    inlet   25        -    -

The preferred units for each property column can be changed at any time using the `.set_units()` method and all values in that column will be automatically converted to the new units

```python
T.set_units('degF')
p.set_units('psi')
states.display()

T.set_units('K')
p.set_units('Pa')
states.display()

T.set_units('degC')
p.set_units('kPa')
states.display()
```

      T
      p
      x

      unit
      °F
      psi

      1
      86
      -
      -

      2
      -
      14.6959
      0.5

      3
      212
      29.0075
      -

      inlet
      77
      -
      -

      T
      p
      x

      unit
      K
      Pa

      1
      303.15
      -
      -

      2
      -
      101325
      0.5

      3
      373.15
      200000
      -

      inlet
      298.15
      -
      -

      T
      p
      x

      unit
      °C
      kPa

      1
      30
      -
      -

      2
      -
      101.325
      0.5

      3
      100
      200
      -

      inlet
      25
      -
      -

## Properties
During our calculations for this course, we will often need to evaluate material/fluid properties at various states.  The traditional method for doing this is to read values from a table that will often involve interpolation (sometimes even double interpolation).  You will still be expected to know how to use the property tables for this course (especially during exams), but you will also be expected to use tools that automate this procedure so you can investigate more complex problems that are not easily solved through hand calculations.

This will be achieved using third-party Libraries: `CoolProp` for real-fluid properties and `PYroMat` for ideal-gas properties.  Each of these libraries can be used directly in Python without loading the `kilojoule` package.  However, we will be primarily using a wrapper for theses libraries provided by the `kilojoule` package, which incorporates the `pint` package to handle a wider range of units and also renames a number of parameters to be consistent with the nomenclature we will be using in this course.

### Water Properties
In the first code block at the top of this notebook, you imported the `realfluid` class from the `kilojoule` library.  This module contains a `Properties` class that can be used to evaluate the properties of a number of real (*pure/pseudopure*) fluids.  You are already familiar with looking up properties for water from the tables from your Thermo I course.  Recall that for a pure substance you need two independent, intensive properties to fix a state, i.e. if you know two independent properties you can find any other property that you need for the state *(Note: there is a little more to the story here, but we will get to that later in the course)*.  For now, let's say we have water at $T_1=300^\circ\text{C}$ and $p_1=750\,\text{kPa}$ and we would like to find the specific volume, $v$, specific internal energy, $u$, specific enthalpy, $h$, and  specific entropy, $s$.  For each of these cases, we could say the desired (dependent) property is a function of the two known (independent) properties:
$$v_1 = v(T=300^\circ\text{C}, p=750\,\text{kPa})$$
$$u_1 = u(T=300^\circ\text{C}, p=750\,\text{kPa})$$
$$h_1 = h(T=300^\circ\text{C}, p=750\,\text{kPa})$$
$$s_1 = s(T=300^\circ\text{C}, p=750\,\text{kPa})$$
In order to use the `kilojoule.realfluid.Properties` class, we first need to instantiate it (Python-speak for initialize the class and store in a variable).  The following code block will import the class (if needed), set the target fluid to be water, and set the default unit system to be metric with temperatures in $^\circ\text{C}$

```python
from kilojoule.templates.kSI_C import *
from kilojoule import realfluid

water = realfluid.Properties('Water', unit_system='kSI_C') # the default unit_system is 'kSI_C' other options are 'SI', 'SI_C', kSI', 'USCS_F', and 'USCS_R'
```

The `water` object now has sub-functions (or methods) that can be used to evaluate (look up) dependent properties.

```python
# Define known values (independent properties)
T[1] = Quantity(300.0,'degC')
p[1] = Quantity(750.0,'kPa')

# Look up dependent properties correspondign to $T_1$ and $p_1$
# specific volume
v[1] = water.v(T=T[1], p=p[1])

# specific internal energy
u[1] = water.u(T=T[1], p=p[1])

# specific enthalpy
h[1] = water.h(T=T[1], p=p[1])

# specific entropy
s[1] = water.s(T=T[1], p=p[1])

# quality
x[1] = water.x(T=T[1], p=p[1])

# phase
phase[1] = water.phase(T=T[1], p=p[1])

Calculations()
states.display()
```

 Define known values (independent properties)

\[
  \begin{aligned}
    { T_{1} }&={ \left( 300.0\,\mathrm{°C} \right) } = { 300.0\ \mathrm{°C}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { p_{1} }&={ \left( 750.0\,\mathrm{kPa} \right) } = { 750.0\ \mathrm{kPa}  }
  \end{aligned}
\]

 Look up dependent properties correspondign to $T_1$ and $p_1$

 specific volume

\[
  \begin{aligned}
    { v_{1} }&={ \mathrm{\underset{water}{v}}\left(\mathrm{T}=T_{1} , \mathrm{p}=p_{1}\right) } = { 0.34621\ \frac{\mathrm{m}^{3}}{\mathrm{kg}}  }
  \end{aligned}
\]

 specific internal energy

\[
  \begin{aligned}
    { u_{1} }&={ \mathrm{\underset{water}{u}}\left(\mathrm{T}=T_{1} , \mathrm{p}=p_{1}\right) } = { 2798.5\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

 specific enthalpy

\[
  \begin{aligned}
    { h_{1} }&={ \mathrm{\underset{water}{h}}\left(\mathrm{T}=T_{1} , \mathrm{p}=p_{1}\right) } = { 3058.2\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

 specific entropy

\[
  \begin{aligned}
    { s_{1} }&={ \mathrm{\underset{water}{s}}\left(\mathrm{T}=T_{1} , \mathrm{p}=p_{1}\right) } = { 7.2659\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }
  \end{aligned}
\]

 quality

\[
  \begin{aligned}
    { x_{1} }&={ \mathrm{\underset{water}{x}}\left(\mathrm{T}=T_{1} , \mathrm{p}=p_{1}\right) } = { N/A\  }
  \end{aligned}
\]

 phase

\[
  \begin{aligned}
    { phase_{1} }&={ \mathrm{\underset{water}{phase}}\left(\mathrm{T}=T_{1} , \mathrm{p}=p_{1}\right) } = { gas\  }
  \end{aligned}
\]

      T
      p
      v
      u
      h
      s
      x
      phase

      unit
      °C
      kPa
      m³/kg
      kJ/kg
      kJ/kg
      kJ/K/kg

      1
      300.0
      750.0
      0.346214
      2798.496371
      3058.156633
      7.26594
      N/A
      gas

Notice the quality, $x_1$, was reported to be `N/A` because the substance is a single-phase, superheated vapor at state 1, so the quality is not defined for this state.

We can also use the same functions for evaluating the properties of saturated fluids.  Let's assume the fluid from state 1 in the above example is cooled at a constant pressure until it is entirely in liquid form, i.e. $x_2=0$.  We could then find all the remaining properties at state 2 as well.
$$ p_2 = p_1 $$
$$ T_2 = T(p=p_2, x=x_2) $$
$$ v_2 = v(p=p_2, x=x_2) $$
$$ u_2 = u(p=p_2, x=x_2) $$
$$ h_2 = h(p=p_2, x=x_2) $$
$$ s_2 = x(p=p_2, x=x_2) $$

```python
# Independent properties that fix state 2
p[2] = p[1]
x[2] = 0

# Dependent properties corresponding to $p[2]$ and $x[2]$
T[2] = water.T(p=p[2], x=x[2])
v[2] = water.v(p=p[2], x=x[2])
u[2] = water.u(p=p[2], x=x[2])
h[2] = water.h(p=p[2], x=x[2])
s[2] = water.s(p=p[2], x=x[2])
phase[2] = water.phase(p=p[2],x=x[2])

Calculations()
states.display()
```

 Independent properties that fix state 2

\[
  \begin{aligned}
    { p_{2} }&={ p_{1} } = { 750.0\ \mathrm{kPa}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { x_{2} }&={ 0 } = { 0\  }
  \end{aligned}
\]

 Dependent properties corresponding to $p[2]$ and $x[2]$

\[
  \begin{aligned}
    { T_{2} }&={ \mathrm{\underset{water}{T}}\left(\mathrm{p}=p_{2} , \mathrm{x}=x_{2}\right) } = { 167.75\ \mathrm{°C}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { v_{2} }&={ \mathrm{\underset{water}{v}}\left(\mathrm{p}=p_{2} , \mathrm{x}=x_{2}\right) } = { 0.0011114\ \frac{\mathrm{m}^{3}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { u_{2} }&={ \mathrm{\underset{water}{u}}\left(\mathrm{p}=p_{2} , \mathrm{x}=x_{2}\right) } = { 708.4\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { h_{2} }&={ \mathrm{\underset{water}{h}}\left(\mathrm{p}=p_{2} , \mathrm{x}=x_{2}\right) } = { 709.24\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { s_{2} }&={ \mathrm{\underset{water}{s}}\left(\mathrm{p}=p_{2} , \mathrm{x}=x_{2}\right) } = { 2.0195\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { phase_{2} }&={ \mathrm{\underset{water}{phase}}\left(\mathrm{p}=p_{2} , \mathrm{x}=x_{2}\right) } = { twoph\  }
  \end{aligned}
\]

      T
      p
      v
      u
      h
      s
      x
      phase

      unit
      °C
      kPa
      m³/kg
      kJ/kg
      kJ/kg
      kJ/K/kg

      1
      300.000000
      750.0
      0.346214
      2798.496371
      3058.156633
      7.265940
      N/A
      gas

      2
      167.748559
      750.0
      0.001111
      708.401761
      709.235334
      2.019482
      0
      twophase

Notice the phase for state 2 is reported as `twophase`, even though we know it is entirely liquid because the quality is 0.  This state would be more accurately described as a saturated-liquid, but the `CoolProp` library reports all saturated states (saturate liquid, saturated mixture, and saturated vapor) as `twophase`.

Let's now calculate a third state that would be obtained from an isenthalpic expansion to $p_3=100\,\text{kPa}$ resulting in a saturated mixture.
$$ h_3 = h_2 $$
$$ p_3 = 100\,\text{kPa} $$
$$ T_3 = T(p=p_3, h=h_3) $$
$$ v_3 = v(p=p_3, h=h_3) $$
$$ u_3 = u(p=p_3, h=h_3) $$
$$ x_3 = x(p=p_3, h=h_3) $$
$$ s_3 = x(p=p_3, h=h_3) $$

```python
# Independent properties that fix the state
h[3] = h[2]
p[3] = Quantity(100.0,'kPa')

# Dependent properties corresponding to $p_3$ and $h_3$
T[3] = water.T(p=p[3], h=h[3])
v[3] = water.v(p=p[3], h=h[3])
u[3] = water.u(p=p[3], h=h[3])
x[3] = water.x(p=p[3], h=h[3])
s[3] = water.s(p=p[3], h=h[3])
phase[3] = water.phase(p=p[3], h=h[3])

Calculations()
states.display()
```

 Independent properties that fix the state

\[
  \begin{aligned}
    { h_{3} }&={ h_{2} } = { 709.24\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { p_{3} }&={ \left( 100.0\,\mathrm{kPa} \right) } = { 100.0\ \mathrm{kPa}  }
  \end{aligned}
\]

 Dependent properties corresponding to $p_3$ and $h_3$

\[
  \begin{aligned}
    { T_{3} }&={ \mathrm{\underset{water}{T}}\left(\mathrm{p}=p_{3} , \mathrm{h}=h_{3}\right) } = { 99.606\ \mathrm{°C}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { v_{3} }&={ \mathrm{\underset{water}{v}}\left(\mathrm{p}=p_{3} , \mathrm{h}=h_{3}\right) } = { 0.21982\ \frac{\mathrm{m}^{3}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { u_{3} }&={ \mathrm{\underset{water}{u}}\left(\mathrm{p}=p_{3} , \mathrm{h}=h_{3}\right) } = { 687.25\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { x_{3} }&={ \mathrm{\underset{water}{x}}\left(\mathrm{p}=p_{3} , \mathrm{h}=h_{3}\right) } = { 0.12923\  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { s_{3} }&={ \mathrm{\underset{water}{s}}\left(\mathrm{p}=p_{3} , \mathrm{h}=h_{3}\right) } = { 2.0854\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { phase_{3} }&={ \mathrm{\underset{water}{phase}}\left(\mathrm{p}=p_{3} , \mathrm{h}=h_{3}\right) } = { twoph\  }
  \end{aligned}
\]

      T
      p
      v
      u
      h
      s
      x
      phase

      unit
      °C
      kPa
      m³/kg
      kJ/kg
      kJ/kg
      kJ/K/kg

      1
      300.000000
      750.0
      0.346214
      2798.496371
      3058.156633
      7.265940
      N/A
      gas

      2
      167.748559
      750.0
      0.001111
      708.401761
      709.235334
      2.019482
      0
      twophase

      3
      99.605929
      100.0
      0.219816
      687.253725
      709.235334
      2.085392
      0.129231
      twophase

#### Short-form Notation for Property Evaluation
The procedure illustrated above is somewhat repetitive and can be shortened in a few ways.  For each call to the `water.property()` object, we explicitly told the function both the type and value of each of the argments passed to it (or in Python lingo, we gave both a keyword and an argument).  However, we can take advantage of the units associated with each of the variables to infer the appropriate property type, i.e. if the argument has units of $kPa$ we can safely assume the appropriate keyword should be `p` for pressure.  Therefore, we could shorten the line `T[3] = water.T(p=p[3], x=x[3])` to `T[3] = water.T(p[3], x[3])`.

**Note: the approach will not work when passing an passing an internal energy or enthalpy argument because they share the same units and you must use the long-from notation, i.e. (...,u=u[3]) or (..., h=h[3]).**

Also, since we are using the same two independent properties for each of the dependent property calls, we could use a loop to automate the process.  The command `states.fix(3, water)` will attempt to use the specified `water` property table to evaluate all missing properties at state `3` using the information already in the table as independent properties.

To illustrate this, let's calculate a fourth state that would be obtained from an isobaric expansion to a temperature of $T_4=150^\circ \mathrm{C}$.
$$ p_4 = p_3 $$
$$ T_4 = 150^\circ\mathrm{C} $$

```python
# Independent properties that fix the state
p[4] = p[3]
T[4] = Quantity(150.0,'degC')

# Dependent properties corresponding to $T_4$ and $p_4$
# using short-form notation
v[4] = water.v(T[4], p[4])

# or using `states.fix(4,water)` fill in the rest of the table
states.fix(4, water)

Calculations()
states.display()
```

 Independent properties that fix the state

\[
  \begin{aligned}
    { p_{4} }&={ p_{3} } = { 100.0\ \mathrm{kPa}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { T_{4} }&={ \left( 150.0\,\mathrm{°C} \right) } = { 150.0\ \mathrm{°C}  }
  \end{aligned}
\]

 Dependent properties corresponding to $T_4$ and $p_4$

 using short-form notation

\[
  \begin{aligned}
    { v_{4} }&={ \mathrm{\underset{water}{v}}\left(T_{4} , p_{4}\right) } = { 1.9367\ \frac{\mathrm{m}^{3}}{\mathrm{kg}}  }
  \end{aligned}
\]

 or using `states.fix(4,water)` fill in the rest of the table

      T
      p
      v
      u
      h
      s
      x
      phase

      unit
      °C
      kPa
      m³/kg
      kJ/kg
      kJ/kg
      kJ/K/kg

      1
      300.000000
      750.0
      0.346214
      2798.496371
      3058.156633
      7.265940
      N/A
      gas

      2
      167.748559
      750.0
      0.001111
      708.401761
      709.235334
      2.019482
      0
      twophase

      3
      99.605929
      100.0
      0.219816
      687.253725
      709.235334
      2.085392
      0.129231
      twophase

      4
      150.000000
      100.0
      1.936650
      2582.937555
      2776.602582
      7.614753
      N/A
      gas

#### Plotting Property Diagrams
It is often helpful to visualize processes by plotting the states on property diagrams, i.e. $T$-$s$, $p$-$v$, $p$-$h$, etc.  The `kilojoule` library provides a `.property_diagram()` method for each of the fluid property tables that can create common property diagrams used in thermodynamics.  This class uses the popular `matplotlib` library.  You first instantiate the class by telling it which properties you want represented on the $x$ and $y$ axes and the unit system (if not using the default/same units as the table).  You can also use the `saturation` parameter to specify whether or not to draw the saturation curves (the default is `True` for real fluids).

In the following code, we will store a plot object (instance of the `PropertyPlot` class) in the variable `Ts`.
> `Ts = water.property_diagram(x='s', y='T', unit_system='USCS_F', saturation=True)`

The `Ts` object contains a `matplotlib` figure and axis stored as attributes accessible at `Ts.fig` and `Ts.ax`.  We can use any `matplotlib` functions on these objects to add features to the diagram (many examples are available on the internet).  However, there are a few custom `matplotlib` routines built into the `PropertyPlot` class, which will make it easier to show visualize the process we will be analyzing.

The simplest built-in construct is the `.plot_point(x, y, label='label', label_loc='north')` method, which places a dot at the $x$, $y$ coordinates with an optional label placed at the relative location provided (default is north)
> `Ts.plot_point(x=s[1], y=T[1], label='1', label_loc='north')`

An alternate interface is also available if your data is stored in the `QuantityTable()` class described above.
> `Ts.plot_state(states[2])`
> `Ts.plot_state(states[3])`

We also like to draw lines connecting states to illustrate processes.  However, we do not want to simply draw straight lines connecting the points.  Rather, we would like the path of the line to represent the process properties at all points.  We can do this if we know something that was constant during the process, ie. pressure was constant from 1 to 2 and enthalpy was constant from 2 to 3 in our earlier example.  The `.plot_process()` method accepts two states and a path description to achieve this:
>`Ts.plot_process(states[1], states[2], path='isobaric')`
>`Ts.plot_process(states[2], states[3], path='isenthalpic')`

```python
# Create Ts_diagram instance
Ts = water.property_diagram(x='s', y='T', unit_system='English_F', saturation=True)

# Plot Critial and Triple Points
Ts.plot_triple_point(label='TP',label_loc='northwest')
Ts.plot_critical_point(label_loc='south')

# Plot State 1 using the .plot_point() method
Ts.plot_point(x=s[1], y=T[1], label='1', label_loc='north')

# Plot States 2 and 3 using the .plot_state() method
Ts.plot_state(states[2])
Ts.plot_state(states[3], label_loc='south west')
Ts.plot_state(states[4])

# Connect the states with lines that illustrate the process paths
Ts.plot_process(states[1], states[2], path='isobaric')
Ts.plot_process(states[2], states[3], path='isenthalpic')
Ts.plot_process(states[3], states[4], path='isobaric');
```

We can use this same process to also create additional plots for our system by changing the parameters when we call the `.property_diagram()` method, or we can shorten the syntax if we use one of the buit-in property combinations `pv_diagram, Ts_diagram, Tv_diagram, hs_diagram, ph_diagram, pT_diagram`

```python
# Create pv_diagram instance
diag = water.pv_diagram(unit_system='SI_K') # Note: this is the only line that will be changed for the next few examples

# Plot Critial and Triple Points
diag.plot_triple_point(label_loc='northwest')
diag.plot_critical_point(label_loc='south')
# Plot States 1-3 using the .plot_state() method
diag.plot_state(states[1])
diag.plot_state(states[2])
diag.plot_state(states[3], label_loc='south west')
diag.plot_state(states[4])
# Connect the states with lines that illustrate the process paths
diag.plot_process(states[1], states[2], path='isobaric')
diag.plot_process(states[2], states[3], path='isenthalpic')
diag.plot_process(states[3], states[4], path='isobaric');
```

```python
# Create ph_diagram instance
diag = water.ph_diagram(unit_system='SI_C') # Note: this is the only line that will be changed for the next few examples

# Plot Critial and Triple Points
diag.plot_triple_point(label_loc='northwest')
diag.plot_critical_point(label_loc='south')
# Plot States 1-3 using the .plot_state() method
diag.plot_state(states[1])
diag.plot_state(states[2])
diag.plot_state(states[3], label_loc='south west')
diag.plot_state(states[4])
# Connect the states with lines that illustrate the process paths
diag.plot_process(states[1], states[2], path='isobaric')
diag.plot_process(states[2], states[3], path='isenthalpic')
diag.plot_process(states[3], states[4], path='isobaric');
```

```python
diag = water.Tv_diagram() # Note: this is the only line that will be changed for the next few examples

# Plot Critial and Triple Points
diag.plot_triple_point(label_loc='northwest')
diag.plot_critical_point(label_loc='south')
# Plot States 1-3 using the .plot_state() method
diag.plot_state(states[1])
diag.plot_state(states[2])
diag.plot_state(states[3], label_loc='south west')
diag.plot_state(states[4])
# Connect the states with lines that illustrate the process paths
diag.plot_process(states[1], states[2], path='isobaric')
diag.plot_process(states[2], states[3], path='isenthalpic')
diag.plot_process(states[3], states[4], path='isobaric');
```

```python
diag = water.hs_diagram() # Note: this is the only line that will be changed for the next few examples

# Plot Critial and Triple Points
diag.plot_triple_point(label_loc='northwest')
diag.plot_critical_point(label_loc='south')
# Plot States 1-3 using the .plot_state() method
diag.plot_state(states[1])
diag.plot_state(states[2])
diag.plot_state(states[3], label_loc='south west')
diag.plot_state(states[4])
# Connect the states with lines that illustrate the process paths
diag.plot_process(states[1], states[2], path='isobaric')
diag.plot_process(states[2], states[3], path='isenthalpic')
diag.plot_process(states[3], states[4], path='isobaric');
```

The ability to generate the previous 5 diagrams using the same set of commands (with only minor changes to the first line) provides a excellent opportunity to write a loop to decrease the amount of code you need to write and maintain.

### Refrigerant Properties
All the commands demonstrated above will also work for any of the other pure/pseudopure substances, such as R-134a, supported by the underlying `CoolProp` library, a list of which can be obtained with the following code.

```python
from kilojoule import realfluid
realfluid.fluids()
```

    ['1-Butene',
     'Acetone',
     'Air',
     'Ammonia',
     'Argon',
     'Benzene',
     'CarbonDioxide',
     'CarbonMonoxide',
     'CarbonylSulfide',
     'cis-2-Butene',
     'CycloHexane',
     'Cyclopentane',
     'CycloPropane',
     'D4',
     'D5',
     'D6',
     'Deuterium',
     'Dichloroethane',
     'DiethylEther',
     'DimethylCarbonate',
     'DimethylEther',
     'Ethane',
     'Ethanol',
     'EthylBenzene',
     'Ethylene',
     'EthyleneOxide',
     'Fluorine',
     'HeavyWater',
     'Helium',
     'HFE143m',
     'Hydrogen',
     'HydrogenChloride',
     'HydrogenSulfide',
     'IsoButane',
     'IsoButene',
     'Isohexane',
     'Isopentane',
     'Krypton',
     'm-Xylene',
     'MD2M',
     'MD3M',
     'MD4M',
     'MDM',
     'Methane',
     'Methanol',
     'MethylLinoleate',
     'MethylLinolenate',
     'MethylOleate',
     'MethylPalmitate',
     'MethylStearate',
     'MM',
     'n-Butane',
     'n-Decane',
     'n-Dodecane',
     'n-Heptane',
     'n-Hexane',
     'n-Nonane',
     'n-Octane',
     'n-Pentane',
     'n-Propane',
     'n-Undecane',
     'Neon',
     'Neopentane',
     'Nitrogen',
     'NitrousOxide',
     'Novec649',
     'o-Xylene',
     'OrthoDeuterium',
     'OrthoHydrogen',
     'Oxygen',
     'p-Xylene',
     'ParaDeuterium',
     'ParaHydrogen',
     'Propylene',
     'Propyne',
     'R11',
     'R113',
     'R114',
     'R115',
     'R116',
     'R12',
     'R123',
     'R1233zd(E)',
     'R1234yf',
     'R1234ze(E)',
     'R1234ze(Z)',
     'R124',
     'R1243zf',
     'R125',
     'R13',
     'R134a',
     'R13I1',
     'R14',
     'R141b',
     'R142b',
     'R143a',
     'R152A',
     'R161',
     'R21',
     'R218',
     'R22',
     'R227EA',
     'R23',
     'R236EA',
     'R236FA',
     'R245ca',
     'R245fa',
     'R32',
     'R365MFC',
     'R40',
     'R404A',
     'R407C',
     'R41',
     'R410A',
     'R507A',
     'RC318',
     'SES36',
     'SulfurDioxide',
     'SulfurHexafluoride',
     'Toluene',
     'trans-2-Butene',
     'Water',
     'Xenon']

To obtain properties for any of the supported fluids, simply supply the appropriate name when you instantiate the `realfluid.Properties()` classe, i.e.

```python
r134a = realfluid.Properties('R134a')
T_ref = Quantity(30,'degC')
x_ref = Quantity(0.25,'')              # Note: quality is dimensionless, so we define its units as an empty string

h_ref = r134a.h(T=T_ref, x=x_ref)
print(f'h_ref = {h_ref}')

v_ref = r134a.v(T=T_ref, x=x_ref)
print(f'v_ref = {v_ref}')

s_ref = r134a.s(T=T_ref, x=x_ref)
print(f's_ref = {s_ref}')

Ts_diagram = r134a.Ts_diagram()
Ts_diagram.plot_triple_point()
Ts_diagram.plot_critical_point()
Ts_diagram.plot_point(x=s_ref, y=T_ref, label='1');
```

### Air Properties
#### Ideal Gas Air Properties
Your textbook treats air as an ideal gas.  As a result, the internal energy and enthalpy values from the tables in the back of the book are only a function of temperature.  Therefore, you only need one independent, intensive property, temperature, to find the enthalpy at a state, since the ideal gas law is used to fix the other degree of freedom (therefore removing the need for a second independent property), i.e.
$$h=h(T)\qquad\text{for an ideal gas only}$$
The entropy, however, is still dependent on the pressure (even with the ideal gas assumption applied).  Since the ideal gas air tables are only tabulated by temperature, it is not possible to look up the entropy directly with the pressure information also being accounted for.  To workaround this problem, your textbook tabulates $s^o$ rather than $s$.  Where the $^o$ is provided to remind you that it is only the temperature dependent portion of the change in entropy.  To get the full change in entropy between two states using the information from the tables, you can use
$$ \Delta s_{1\to2} = s_2^o-s_1^o - R\ln\frac{p_2}{p_1} $$
where $s_2^o$ and $s_1^o$ are from the tables, $R$ is the specific gas constant, and $p_2$ and $p_1$ are the pressures of the fluid in absolute units (i.e. *absolute pressure* not gauge pressure).

Ideal gas properties for air (and many other gases) can be obtained from the `PYroMat` library using the Burcat equations.  The `kilojoule` library provides a wrapper to access this library using the same syntax as we used for the real-fluid library above.  This wrapper is provided by the `idealgas` module.
> `from kilojoule import idealgas`
> `air = idealgas.Properties('Air')`

```python
from kilojoule import idealgas
air = idealgas.Properties('Air', unit_system='kSI_C')

T_air = Quantity(450,'K')
p_air = Quantity(1.0,'atm')
h_air = air.h(T=T_air)
s_air = air.s(T=T_air,p=p_air)
print(f'h_air = {h_air}')
print(f's_air = {s_air}')
Calculations();
```

    h_air = 149.37 kJ/kg
    s_air = 7.114 kJ/kg/Δ°C

\[
  \begin{aligned}
    { T_{air} }&={ \left( 450\,\mathrm{K} \right) } = { 450\ \mathrm{K}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { p_{air} }&={ \left( 1.0\,\mathrm{atm} \right) } = { 1.0\ \mathrm{atm}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { h_{air} }&={ \mathrm{\underset{air}{h}}\left(\mathrm{T}=T_{air}\right) } = { 149.37\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { s_{air} }&={ \mathrm{\underset{air}{s}}\left(\mathrm{T}=T_{air} , \mathrm{p}=p_{air}\right) } = { 7.114\ \frac{\mathrm{kJ}}{\left(\mathrm{kg} \cdot \mathrm{Δ°C}\right)}  }
  \end{aligned}
\]

#### Pseudopure Real Fluid Air Properties
While we can obtain reasonably accurate answer for many engineering systems that involve air using the ideal gas assumption, there are cases when we need to treat air as a real fluid instead. The `CoolProp` library used in `kilojoule` does not treat air as an ideal gas, rather it treats air as a *pseudopure fluid*.  In this context we call the air a *pseudopure fluid* because it is really a mixture (approximately $79\%\text{N}_2$ and $21\%\text{O}_2$) but we treat it as if it were a pure fluid with known properties.  As a result, you still need to provide two independent, intensive properties when using the `kilojoule.realfluid.Properties` class with air.

```python
air = realfluid.Properties(fluid='Air')

T_air = Quantity(450,'K')
p_air = Quantity(1.0,'atm')
h_air = air.h(T=T_air, p=p_air)
print(f'h_air = {h_air}')

Calculations();
```

    h_air = 578.13 kJ/kg

\[
  \begin{aligned}
    { T_{air} }&={ \left( 450\,\mathrm{K} \right) } = { 450\ \mathrm{K}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { p_{air} }&={ \left( 1.0\,\mathrm{atm} \right) } = { 1.0\ \mathrm{atm}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { h_{air} }&={ \mathrm{\underset{air}{h}}\left(\mathrm{T}=T_{air} , \mathrm{p}=p_{air}\right) } = { 578.13\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

At this point it is worth pointing out that the ideal gas tables and the real fluid tables gave significantly different answers for the enthalpy in the previous example (neither of which are in agreement with your textbook).  This is because enthalpy is an integrated property and the libraries used to evaluate these properties use different reference states (or starting points for the integration).  While this may seem like a major problem, it is not.  The vast majority of our thermodynamics calcuations will look at changes in intergrated properties, such as internal energy, enthalpy, entropy, Gibbs function, etc., rather than their absoute values.  So as long as you pull all your properties from the same table your final results will be (nearly) the same regardless of which set of tables you used.  However, you cannot mix and match between different property sources.

#### Humid Air
Later in this course we will study mixtures, rather than just pure substances.  One common mixture encountered in many engineering applications is humid air (a mixture of air and water vapor).  Because we will be treating humid air as a mixture of two substances (with air still being treated as a pseudopure fluid), we will need three independent intensive properties to fix the state.  The fluid properties for humid air can be reached in the same way as the pure/pseudopure substance, with the exception that you need to provide three independent properties to fix the state instead of two and you need to use the `humidair.Properties` class instead of the `realfluid.Properties` class.

```python
from kilojoule.templates.humidair_default import *

# Start with air at 30 C, 50% relative humidity, at 1 atmosphere
T[1] = Quantity(30,'degC')
rel_hum[1] = 0.5
p[1] = Quantity(1,'atm')

T_wb[1] = humidair.T_wb(T[1],p[1],rel_hum[1])
h[1] = humidair.h(T[1],p[1],rel_hum[1])
v[1] = humidair.v(T[1],p[1],rel_hum[1])
s[1] = humidair.s(T[1],p[1],rel_hum[1])
omega[1] = humidair.omega(T[1],p[1],rel_hum[1])

# Use a simple cooling process to lower the temperature and dehumidify by cooling to 10 C
T[2] = Quantity(10,'degC')
rel_hum[2] = 1
p[2] = p[1]

states.fix(2,humidair)

states.display()
```

      T
      p
      v
      h
      h_w
      s
      T_wb
      T_dp
      rel_hum
      omega

      unit
      °C
      kPa
      m³/kg_a
      kJ/kg_a
      Btu/lb_water
      kJ/K/kg_a
      °C
      °C

      kg_water/kg_a

      1
      30
      101.325
      0.876956
      64.355677
      -
      0.228260
      22.000878
      -
      0.5
      0.013373

      2
      10
      101.325
      0.811596
      29.354502
      18.1079
      0.107866
      10.000000
      10
      1.0
      0.007663

The `kilojoule` library provides a routine for drawing psychrometric charts to visualize humid air systems.  *Note: this can be used to generate psychrometric charts for non-standard pressures and unit systems*

```python
psych = humidair.psychrometric_chart()
psych.plot_state(states[1])
psych.plot_state(states[2],label_loc='south east')
psych.plot_process(states[1],states[2],path='simple cooling');
```

```python
from kilojoule.templates.humidair_default import *

p[1] = Quantity(85,'kPa')
humidair.p = Quantity(p[1])

# Start with air at 30 C, 50% relative humidity, at 0.85 atmosphere
T[1] = Quantity(30,'degC')
rel_hum[1] = 0.5

T_wb[1] = humidair.T_wb(T[1],p[1],rel_hum[1])
h[1] = humidair.h(T[1],p[1],rel_hum[1])
v[1] = humidair.v(T[1],p[1],rel_hum[1])
s[1] = humidair.s(T[1],p[1],rel_hum[1])
omega[1] = humidair.omega(T[1],p[1],rel_hum[1])

# Use a simple cooling process to lower the temperature and dehumidify by cooling to 10 C
T[2] = Quantity(10,'degC')
rel_hum[2] = 1
p[2] = p[1]

states.fix(2,humidair)

states.display()

psych = humidair.psychrometric_chart()
psych.plot_state(states[1])
psych.plot_state(states[2],label_loc='south east')
psych.plot_process(states[1],states[2],path='simple cooling');
```

## Equation Formatting
Simply arriving at the correct answer for a problem is only half the battle.  You then need to be able to communicate your methods and results to a range of audiences (in this class your instructor).  This should be done following technical writing conventions with a narrative discussion of your process including properly formatted equations and sample calculations.  It is not sufficient to simply submit your code and a final numerical answer or a long list of equations without any explanation.

Throughout your academic career you have learned many different conventions (shorthand) for writing down mathematical concepts, i.e. to show a variable is being raised to a power we put that power in the superscript $x^2$.  However, there is no key on your keyboard to make that 2 shrink in size and move above the variable.  You'll also notice that the $x$ was not written in the same font as the rest of the text.  It is convention for variables to be written in italics rather than normal font because it helps the reader quickly distinguish them from regular text (you probably already do this in your head without realizing it).

There are a few ways to create properly formatted equations.  While the Microsoft equation editor has improved greatly in recent years, the most powerful tool is the formatting language $\LaTeX$.  $\LaTeX$ has been around for many decades and it was developed to represent complex mathematical expressions using plain text (just the keys on a regular keyboard).  While there is a bit of a learning curve if you choose to start using $\LaTeX$, your efforts will pay off many times over as you will find that most scientific/mathematical software has $\LaTeX$ support built in.  In fact, learning $\LaTeX$ will even make you faster when you do need to use Microsoft Equations editor because it includes support for many $\LaTeX$ symbol names.

The Jupyter notebook this document is being created in has built-in $\LaTeX$ support.  In some of the earlier examples you may have noticed special symbols in some of the output, such as $\Delta^\circ\text{C}$.  Those were created using $\LaTeX$ formatting and the special symbols in this explanation were also created using $\LaTeX$ formatting (if you are reading this in a live notebook, double-click on this cell to see the source code written in Markdown syntax).  You can include inline math, $f(x)=5x^2-3x+2$, or you can include "display" math
$$f(x) = \int_0^\infty\frac{3}{2}x\ dx$$

To help you convert your calculations into technical writing format, the `kilojoule` library provides a few convenience functions in its `display` module to automate the $\LaTeX$ creation process.  The `display.Calculations()` class will trigger the a process that attempts to convert the code in the current cell to $\LaTeX$ and show the progression of the calculations from symbolic to final numerical form.

To demonstrate the use of `display.Calculations()` we'll step through the evaluation and display of the function $\psi=ax-cx^2+\frac{b}{x}$.  We'll start by defining values for `a`, `b`, and `x`

The `kilojoule` library provides a routine for drawing psychrometric charts to visualize humid air systems.  *Note: this can be used to generate psychrometric charts for non-standard pressures and unit systems*

```python
from kilojoule.display import Calculations
from kilojoule.units import Quantity
```

```python
a = Quantity(3.2,'psi')
b = Quantity(1,'kPa')
x = 2

Calculations();
```

\[
  \begin{aligned}
    { a }&={ \left( 3.2\,\mathrm{psi} \right) } = { 3.2\ \mathrm{psi}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { b }&={ \left( 1\,\mathrm{kPa} \right) } = { 1\ \mathrm{kPa}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { x }&={  2  }
  \end{aligned}
\]

In this example, the lines defining `a`, `b`, and `x` were simple definitions involving no mathematical operations, so they are shown in simple form.  By placing the line `display.Calculations();` at the end of the cell, we trigger a sequence where the code in the cell is parsed for strings of letters resembling equations and displays them with $\LaTeX$ formatting.

In the next cell we will define `c` as being equal to `a`.

```python
c = a

Calculations();
```

\[
  \begin{aligned}
    { c }&={ a } = { 3.2\ \mathrm{psi}  }
  \end{aligned}
\]

In this example, we see 3 terms rather than two.  This line still has no mathematical operations, but there is a train of logic where we are setting $c$ equal to $a$.  While it is important to show the numerical value of $c$ as being $3.2\ \mathrm{psi}$, it is also important (possibly more important) to show the process that led to $c$ having that value, so we show the symbolic form of the expression $c=a$ as well.

Let's now evaluate a full equation with mathematical operations.

```python
psi = a*x - c*x**2 + b/x

Calculations();
```

\[
  \begin{aligned}{ \psi }&={ a x - c x^{2} + \frac{b}{x} }\\
    &={ \left( 3.2\ \mathrm{psi} \right)  2  - \left( 3.2\ \mathrm{psi} \right)  2 ^{ 2 } + \frac{\left( 1\ \mathrm{kPa} \right)}{ 2 } }\\
    &={  -6.3275\ \mathrm{psi}  }
  \end{aligned}
\]

In this example the equation is expressed in 3 lines.  The first line shows the symbolic form of the equation, which shows the reader the process or logic that is being applied.  The second line shows numerical values in place of each symbol, which shows the propagation of information from earlier calculations.  Finally the third line shows the numerical value resulting from the calculation.  *Note: this is the form you should use when writing out calculations by hand as well.*  Also, notice that the variable name `psi` was recognized as being a Greek letter and converted to the $\LaTeX$ equivalent of `\psi`.  This will work for most cases if you define your variable names carefully.

Using the `display.Calculations()` command will allow you to properly format your calculations, but you will still need to provide a narrative discussion to describe your process to the reader.  You can do this in a Jupyter notebook by interspersing `Markdown` cells like this one between your equations, or you can place your narrative in your code as comments that will be shown in your output using the `comments=True` (by default) option for the `display.Calculations()` class.

```python
# You can place comments in your code to describe your process.
# The comments will be processed as `Markdown` so you can apply **formatting** if desired
# For instance, let's calculate the amount of heat transfer required to decrease the temperature of air from 400 K to 300 K in a constant pressure process assuming constant specific heat.  We can start by defining some known parameters,
T_1 = Quantity(400,'K')
T_2 = Quantity(300,'K')
c_p = Quantity(1.005,'kJ/kg/K')

# We can then solve the first law for $Q$ and substitute $c_p\Delta T$ for $\Delta h$
Q_1_to_2 = c_p*(T_2-T_1)

calcs = Calculations(comments=True);
```

 You can place comments in your code to describe your process.

 The comments will be processed as `Markdown` so you can apply **formatting** if desired

 For instance, let's calculate the amount of heat transfer required to decrease the temperature of air from 400 K to 300 K in a constant pressure process assuming constant specific heat.  We can start by defining some known parameters,

\[
  \begin{aligned}
    { T_{1} }&={ \left( 400\,\mathrm{K} \right) } = { 400\ \mathrm{K}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { T_{2} }&={ \left( 300\,\mathrm{K} \right) } = { 300\ \mathrm{K}  }
  \end{aligned}
\]

\[
  \begin{aligned}
    { c_{p} }&={ \left( 1.005\,\frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)} \right) } = { 1.005\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }
  \end{aligned}
\]

 We can then solve the first law for $Q$ and substitute $c_p\Delta T$ for $\Delta h$

\[
  \begin{aligned}{ Q_{1\to{}2} }&={ c_{p} \left(T_{2} - T_{1}\right) }\\
    &={ \left( 1.005\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)} \right) \left(\left( 300\ \mathrm{K} \right) - \left( 400\ \mathrm{K} \right)\right) }\\
    &={  -100.5\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
  \end{aligned}
\]

You may have noticed that in the example above, we stored the result of `Calculations()` in the variable `calcs`.  This gives us access to the $\LaTeX$ used to generate the output, which can be accessed at `calcs.output`.  This can be useful if you are learning $\LaTeX$ and want to see how an equation was created or if you want export the $\LaTeX$ code for inclusion in another document.

```python
print(calcs.output)
```

     You can place comments in your code to describe your process.
 The comments will be processed as `Markdown` so you can apply **formatting** if desired
 For instance, let's calculate the amount of heat transfer required to decrease the temperature of air from 400 K to 300 K in a constant pressure process assuming constant specific heat.  We can start by defining some known parameters,
\[
      \begin{aligned}
        { T_{1} }&={ \left( 400\,\mathrm{K} \right) } = { 400\ \mathrm{K}  }
      \end{aligned}
    \]
    \[
      \begin{aligned}
        { T_{2} }&={ \left( 300\,\mathrm{K} \right) } = { 300\ \mathrm{K}  }
      \end{aligned}
    \]
    \[
      \begin{aligned}
        { c_{p} }&={ \left( 1.005\,\frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)} \right) } = { 1.005\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }
      \end{aligned}
    \]
     We can then solve the first law for $Q$ and substitute $c_p\Delta T$ for $\Delta h$
\[
      \begin{aligned}{ Q_{1\to{}2} }&={ c_{p} \left(T_{2} - T_{1}\right) }\\
        &={ \left( 1.005\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)} \right) \left(\left( 300\ \mathrm{K} \right) - \left( 400\ \mathrm{K} \right)\right) }\\
        &={  -100.5\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }
      \end{aligned}
    \]

The `kilojoule` library also provides a quick way to show the current value of all the quantities and property tables defined in the local namespace using the `display.Summary()` class, just the quantities using `display.Quantities()`, or just the property tables using `display.QuantityTables()`

```python
import kilojoule as kj

kj.display.Summary(n_col=4);
```

\[\begin{aligned}{ \Delta{}E_{CV} }&={  -994.97\ \mathrm{kJ}  }&{ \Delta{}ke }&={  425.16\ \frac{\mathrm{m}^{2}}{\mathrm{s}^{2}}  }&{ \Delta{}pe }&={  256.4\ \frac{\mathrm{ft} \cdot \mathrm{m}}{\mathrm{s}^{2}}  }&{ \Delta{}u }&={  -100.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }\\{ Q_{1\to{}2} }&={  -100.5\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ T_{1} }&={  400\ \mathrm{K}  }&{ T_{2} }&={  300\ \mathrm{K}  }&{ T_{air} }&={  450\ \mathrm{K}  }\\{ T_{ref} }&={  30\ \mathrm{°C}  }&{ Vel_{1} }&={  20.0\ \mathrm{mph}  }&{ Vel_{2} }&={  30.5\ \frac{\mathrm{m}}{\mathrm{s}}  }&{ a }&={  3.2\ \mathrm{psi}  }\\{ b }&={  1\ \mathrm{kPa}  }&{ c }&={  3.2\ \mathrm{psi}  }&{ c_{p} }&={  1.005\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }&{ g }&={  9.8\ \frac{\mathrm{m}}{\mathrm{s}^{2}}  }\\{ h_{air} }&={  578.13\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ h_{ref} }&={  285.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ p_{air} }&={  1.0\ \mathrm{atm}  }&{ \psi }&={  -6.3275\ \mathrm{psi}  }\\{ s_{air} }&={  7.114\ \frac{\mathrm{kJ}}{\left(\mathrm{kg} \cdot \mathrm{Δ°C}\right)}  }&{ s_{ref} }&={  1.2862\ \frac{\mathrm{kJ}}{\left(\mathrm{kg} \cdot \mathrm{Δ°C}\right)}  }&{ u_{1} }&={  300.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ u_{2} }&={  200.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }\\{ v_{ref} }&={  0.007292\ \frac{\mathrm{m}^{3}}{\mathrm{kg}}  }&{ x_{ref} }&={  0.25\  }&{ z_{1} }&={  1.2\ \mathrm{m}  }&{ z_{2} }&={  30.1\ \mathrm{ft}  }\\{  }\end{aligned}\]

      T
      p
      v
      h
      h_w
      s
      T_wb
      T_dp
      rel_hum
      omega

      unit
      °C
      kPa
      m³/kg_a
      kJ/kg_a
      Btu/lb_water
      kJ/K/kg_a
      °C
      °C

      kg_water/kg_a

      1
      30
      85
      1.049749
      71.107451
      -
      0.303031
      21.600711
      -
      0.5
      0.015999

      2
      10
      85
      0.811596
      29.354502
      18.1079
      0.107866
      10.000000
      10
      1.0
      0.007663

```python
kj.display.Quantities(n_col=6);
```

\[\begin{aligned}{ \Delta{}E_{CV} }&={  -994.97\ \mathrm{kJ}  }&{ \Delta{}ke }&={  425.16\ \frac{\mathrm{m}^{2}}{\mathrm{s}^{2}}  }&{ \Delta{}pe }&={  256.4\ \frac{\mathrm{ft} \cdot \mathrm{m}}{\mathrm{s}^{2}}  }&{ \Delta{}u }&={  -100.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ Q_{1\to{}2} }&={  -100.5\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ T_{1} }&={  400\ \mathrm{K}  }\\{ T_{2} }&={  300\ \mathrm{K}  }&{ T_{air} }&={  450\ \mathrm{K}  }&{ T_{ref} }&={  30\ \mathrm{°C}  }&{ Vel_{1} }&={  20.0\ \mathrm{mph}  }&{ Vel_{2} }&={  30.5\ \frac{\mathrm{m}}{\mathrm{s}}  }&{ a }&={  3.2\ \mathrm{psi}  }\\{ b }&={  1\ \mathrm{kPa}  }&{ c }&={  3.2\ \mathrm{psi}  }&{ c_{p} }&={  1.005\ \frac{\mathrm{kJ}}{\left(\mathrm{K} \cdot \mathrm{kg}\right)}  }&{ g }&={  9.8\ \frac{\mathrm{m}}{\mathrm{s}^{2}}  }&{ h_{air} }&={  578.13\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ h_{ref} }&={  285.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }\\{ p_{air} }&={  1.0\ \mathrm{atm}  }&{ \psi }&={  -6.3275\ \mathrm{psi}  }&{ s_{air} }&={  7.114\ \frac{\mathrm{kJ}}{\left(\mathrm{kg} \cdot \mathrm{Δ°C}\right)}  }&{ s_{ref} }&={  1.2862\ \frac{\mathrm{kJ}}{\left(\mathrm{kg} \cdot \mathrm{Δ°C}\right)}  }&{ u_{1} }&={  300.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }&{ u_{2} }&={  200.0\ \frac{\mathrm{kJ}}{\mathrm{kg}}  }\\{ v_{ref} }&={  0.007292\ \frac{\mathrm{m}^{3}}{\mathrm{kg}}  }&{ x_{ref} }&={  0.25\  }&{ z_{1} }&={  1.2\ \mathrm{m}  }&{ z_{2} }&={  30.1\ \mathrm{ft}  }&{  }\end{aligned}\]

```python
kj.display.QuantityTables();
```

      T
      p
      v
      h
      h_w
      s
      T_wb
      T_dp
      rel_hum
      omega

      unit
      °C
      kPa
      m³/kg_a
      kJ/kg_a
      Btu/lb_water
      kJ/K/kg_a
      °C
      °C

      kg_water/kg_a

      1
      30
      85
      1.049749
      71.107451
      -
      0.303031
      21.600711
      -
      0.5
      0.015999

      2
      10
      85
      0.811596
      29.354502
      18.1079
      0.107866
      10.000000
      10
      1.0
      0.007663

```python

```

```python

```