Air in a tank#

A spherical metal tank stores hot air, initially at 200°C and 350 psi. The tank is made out of stainless steel, with specific heat capacity 471 J/kg-K and density 7902 kg/m\(^3\). The tank wall is 0.35 mm thick and initially at 20°C; the inner radius is 10 cm and the outer wall is insulated.

Treat the air in the tank as an ideal gas and the stainless steel tank wall as an incompressible substance.

Eventually the air reaches thermal equilibrium with the tank wall. Find the final temperature of the air and the tank, and the final pressure of the air.

import numpy as np
from scipy import optimize
import cantera as ct

from pint import UnitRegistry
ureg = UnitRegistry()
Q_ = ureg.Quantity
# input data
tank_specific_heat = Q_(471, 'J/(kg*K)')
tank_density = Q_(7902, 'kg/m^3')
tank_radius = Q_(10, 'cm')
tank_thickness = Q_(0.35, 'mm')

tank_temp1 = Q_(20, 'degC')

air_temp1 = Q_(200, 'degC')
air_pres1 = Q_(350, 'psi')

# use an ideal gas mixture to represent air
air1 = ct.Solution('air.cti')
air1.TPX = (
    air_temp1.to('K').magnitude, 
    air_pres1.to('Pa').magnitude, 
    'O2:1.0, N2:3.76'
    )
/tmp/ipykernel_4646/2407163354.py:13: DeprecationWarning: XML_Node::build: 
The CTI and XML input file formats are deprecated and will be removed in
Cantera 3.0. Use 'cti2yaml.py' or 'ctml2yaml.py' to convert CTI or XML input
files to the YAML format. See https://cantera.org/tutorials/legacy2yaml.html
for more information.
  air1 = ct.Solution('air.cti')
air_volume = 4.0 * np.pi * tank_radius**3 / 3.0
air_mass = air_volume / Q_(air1.v, 'm^3/kg')

tank_volume = (4. * np.pi / 3.) * (
    (tank_radius + tank_thickness)**3 - tank_radius**3
    )
tank_mass = tank_volume * tank_density

During the equilibration process, the specific volume of the air does not change:

(18)#\[\begin{equation} v_{a,2} = v_{a,1} \;. \end{equation}\]

We can perform an energy balance on the system that contains both the tank and air:

\[\begin{split} 0 = m_t ( u_{t,2} - u_{t,1} ) + m_a ( u_{a,2} - u_{a,1} ) \\ 0 = m_t c_{\text{steel}} (T_{t,2} - T_{t,1}) - m_a ( u_{a,2} - u_{a,1} ) \end{split}\]

and we know that at equilibrium the temperatures are the same: \(T_{t,2} = T_{a,2}\).

So, we fundamentally have one unknown, the final temperature, which we can solve using the final version of the energy balance:

def tank_energy(T2, air_mass, air1, tank_mass, tank_specific_heat, tank_temp1):
    '''Returns value of energy balance equation based on input T2
    
    This equation should return zero, if the correct T2 is input.
    '''
    # the temperatures are the same
    tank_temp2 = Q_(T2, 'K')
    air_temp2 = Q_(T2, 'K')
    
    air_specific_volume2 = Q_(air1.v, 'm^3/kg')
    air_density2 = 1.0 / air_specific_volume2

    air2 = ct.Solution('air.cti')
    air2.TDX = (
        air_temp2.to('K').magnitude,
        air_density2.to('kg/m^3').magnitude,
        'O2:1.0, N2:3.76'
        )
    
    return (
        tank_mass * tank_specific_heat * (tank_temp2 - tank_temp1) +
        air_mass * (Q_(air2.u, 'J/kg') - Q_(air1.u, 'J/kg'))
        ).to('J').magnitude
# find the root of the equation, with an initial guess of 300 K
sol = optimize.root(
    tank_energy, 300.0, 
    args=(air_mass, air1, tank_mass, tank_specific_heat, tank_temp1)
    )

# the solution is the first element of the sol.x list
temp2 = Q_(sol.x[0], 'K')
print(f'Final temperature: {temp2.to("degC"): .2f}')

air2 = ct.Solution('air.cti')
air2.TDX = (
    temp2.to('K').magnitude,
    air1.density,
    'O2:1.0, N2:3.76'
    )
air_pres2 = Q_(air2.P, 'Pa')
print(f'Final air pressure: {air_pres2.to("psi"): .2f}')
Final temperature: 64.83 degree_Celsius
Final air pressure: 250.01 pound_force_per_square_inch

Alternate solution#

If we wanted, we could also solve this problem with a larger number of equations and unknowns, although as seen above this is not necessary. For example, we can treat \(v_{a,2}\), \(T_{a,2}\), \(T_{t,2}\), and \(u_{a,2}\) as the four unknowns, with the equations

\[\begin{split} v_{a,2} = v_{a,1} \\ T_{a,2} = T_{t,2} \\ u_{a,2} = f \left( T_{a,2}, v_{a,2} \right) \end{split}\]

being added to the one used above.

def tank_energy2(x, air_mass, air1, tank_mass, tank_specific_heat, tank_temp1):
    '''Returns value of energy balance equation based on input T2
    
    This equation should return zero, if the correct values are input.
    
    x: T_t2, v_a2, T_a2, u_a2
    '''
    tank_temp2 = Q_(x[0], 'K')
    air_specific_volume2 = Q_(x[1], 'm^3/kg')
    air_temp2 = Q_(x[2], 'K')
    air_internal_energy2 = Q_(x[3], 'J/kg')
    
    air_density2 = 1.0 / air_specific_volume2

    air2 = ct.Solution('air.cti')
    air2.TDX = (
        air_temp2.to('K').magnitude,
        air_density2.to('kg/m^3').magnitude,
        'O2:1.0, N2:3.76'
        )
    
    return [
        (tank_mass * tank_specific_heat * (tank_temp2 - tank_temp1) +
         air_mass * (Q_(air2.u, 'J/kg') - Q_(air1.u, 'J/kg'))
         ).to('J').magnitude,
        (air_specific_volume2 - Q_(air1.v, 'm^3/kg')).to('m^3/kg').magnitude,
        (air_temp2 - tank_temp2).to('K').magnitude,
        (air_internal_energy2 - Q_(air2.u, 'J/kg')).to('J/kg').magnitude
        ]

# now we have to give guesses for all four variables
sol = optimize.root(
    tank_energy2, [300.0, air1.v, 300.0, air1.u], 
    args=(air_mass, air1, tank_mass, tank_specific_heat, tank_temp1)
    )

# the solution is the first element of the sol.x list
tank_temp2 = Q_(sol.x[0], 'K')
print(f'Final temperature of tank: {tank_temp2.to("degC"): .2f}')
air_temp2 = Q_(sol.x[2], 'K')
print(f'Final temperature of air: {air_temp2.to("degC"): .2f}')

air2 = ct.Solution('air.cti')
air2.TDX = (
    air_temp2.to('K').magnitude,
    1.0 / sol.x[1],
    'O2:1.0, N2:3.76'
    )
air_pres2 = Q_(air2.P, 'Pa')
print(f'Final air pressure: {air_pres2.to("psi"): .2f}')
Final temperature of tank: 64.83 degree_Celsius
Final temperature of air: 64.83 degree_Celsius
Final air pressure: 250.01 pound_force_per_square_inch

As expected, we get the same answer as before, without any real benefits. In fact, this is a bit more work, since we had to specify four guess values instead of one.