Calculate thermodynamic properties and mixture behavior for pump applications
The thermo library is a comprehensive Python package for chemical and mechanical engineers working with thermodynamic properties and phase equilibria. It provides validated correlations for:
The library includes extensive chemical databases and implements industry-standard correlations from DIPPR, NIST, and literature sources. Essential for pump applications involving temperature-dependent properties, mixtures, and multiphase flow.
pip install thermo chemicals
The chemicals package is a required dependency that provides the chemical database and pure component property correlations.
Interface for pure component properties including:
Mixture property calculations:
Process stream calculations:
Equation of state implementations:
Critical for pump performance across operating temperature ranges.
from thermo.chemical import Chemical
# Water viscosity variation with temperature
water = Chemical('water', T=298.15, P=101325) # 25°C, 1 atm
# At different temperatures
temps = [20, 40, 60, 80, 100] # °C
for T_c in temps:
water.T = T_c + 273.15
print(f"T = {T_c}°C: μ = {water.mu*1000:.4f} cP, ρ = {water.rho:.1f} kg/m³")
# Output demonstrates viscosity decreasing with temperature:
# T = 20°C: μ = 1.0016 cP, ρ = 998.2 kg/m³
# T = 40°C: μ = 0.6529 cP, ρ = 992.2 kg/m³
# T = 60°C: μ = 0.4665 cP, ρ = 983.2 kg/m³
Account for viscosity and density changes when pumping at different temperatures.
from thermo.chemical import Chemical
def pump_power_correction(fluid_name, T_design, T_actual, Q, H, eta=0.75):
"""
Calculate pump power at actual temperature vs design temperature.
Parameters:
-----------
fluid_name : str, chemical name
T_design : float, design temperature (K)
T_actual : float, actual temperature (K)
Q : float, flow rate (m³/s)
H : float, head (m)
eta : float, pump efficiency
Returns:
--------
Power correction factor and actual power required
"""
# Properties at design temperature
fluid_design = Chemical(fluid_name, T=T_design, P=101325)
rho_design = fluid_design.rho
mu_design = fluid_design.mu
# Properties at actual temperature
fluid_actual = Chemical(fluid_name, T=T_actual, P=101325)
rho_actual = fluid_actual.rho
mu_actual = fluid_actual.mu
# Power calculation: P = ρ*g*Q*H/η
g = 9.81 # m/s²
P_design = rho_design * g * Q * H / eta
P_actual = rho_actual * g * Q * H / eta
# Viscosity affects efficiency (simplified)
# Higher viscosity reduces efficiency
eta_correction = (mu_design / mu_actual) ** 0.1 # Empirical
eta_actual = eta * eta_correction
P_actual_corrected = rho_actual * g * Q * H / eta_actual
return {
'P_design': P_design / 1000, # kW
'P_actual': P_actual / 1000, # kW
'P_corrected': P_actual_corrected / 1000, # kW
'rho_ratio': rho_actual / rho_design,
'mu_ratio': mu_actual / mu_design,
'eta_correction': eta_correction
}
# Example: Pumping water at elevated temperature
result = pump_power_correction('water', T_design=298.15, T_actual=353.15,
Q=0.05, H=50, eta=0.75)
print(f"Design power: {result['P_design']:.2f} kW")
print(f"Actual power (density only): {result['P_actual']:.2f} kW")
print(f"Actual power (with efficiency): {result['P_corrected']:.2f} kW")
Essential for pumps handling mixed fluids or process streams.
from thermo.mixture import Mixture
# Ethanol-water mixture (common in chemical processing)
mixture = Mixture(['ethanol', 'water'], ws=[0.3, 0.7], T=298.15, P=101325)
print(f"Mixture density: {mixture.rho:.1f} kg/m³")
print(f"Mixture viscosity: {mixture.mu*1000:.3f} cP")
print(f"Mixture heat capacity: {mixture.Cp:.1f} J/kg·K")
# Compare to pure components
from thermo.chemical import Chemical
ethanol = Chemical('ethanol', T=298.15, P=101325)
water = Chemical('water', T=298.15, P=101325)
print(f"\nPure ethanol: ρ = {ethanol.rho:.1f} kg/m³, μ = {ethanol.mu*1000:.3f} cP")
print(f"Pure water: ρ = {water.rho:.1f} kg/m³, μ = {water.mu*1000:.3f} cP")
Determine available NPSH and cavitation risk.
from thermo.chemical import Chemical
def calculate_NPSHa(fluid_name, T, P_suction, z_suction, v_suction):
"""
Calculate Net Positive Suction Head Available.
Parameters:
-----------
fluid_name : str, chemical name
T : float, fluid temperature (K)
P_suction : float, suction pressure (Pa absolute)
z_suction : float, suction elevation relative to pump (m)
v_suction : float, velocity at suction (m/s)
Returns:
--------
NPSHa in meters
"""
fluid = Chemical(fluid_name, T=T, P=P_suction)
rho = fluid.rho # kg/m³
Psat = fluid.Psat # Vapor pressure, Pa
g = 9.81 # m/s²
# NPSHa = (P_suction - Psat)/(ρ*g) + v²/(2g) - z_suction
pressure_head = (P_suction - Psat) / (rho * g)
velocity_head = v_suction**2 / (2 * g)
NPSHa = pressure_head + velocity_head - z_suction
return {
'NPSHa': NPSHa,
'P_suction': P_suction / 1000, # kPa
'Psat': Psat / 1000, # kPa
'rho': rho,
'margin': (P_suction - Psat) / 1000 # kPa
}
# Example: Water at 80°C (elevated temperature)
result = calculate_NPSHa('water', T=353.15, P_suction=150000,
z_suction=2.0, v_suction=2.5)
print(f"NPSHa = {result['NPSHa']:.2f} m")
print(f"Suction pressure = {result['P_suction']:.1f} kPa")
print(f"Vapor pressure = {result['Psat']:.1f} kPa")
print(f"Pressure margin = {result['margin']:.1f} kPa")
# Warning if NPSHa is low
if result['NPSHa'] < 3.0:
print("⚠ WARNING: Low NPSHa - high cavitation risk!")
Calculate enthalpy rise and temperature increase in multistage pumps.
from thermo.chemical import Chemical
def pump_temperature_rise(fluid_name, T_inlet, P_inlet, P_outlet, eta):
"""
Calculate temperature rise due to pump inefficiency.
Energy not converted to useful work appears as heat in the fluid:
ΔT = (1-η) * ΔP / (ρ * Cp)
Parameters:
-----------
fluid_name : str
T_inlet : float, inlet temperature (K)
P_inlet : float, inlet pressure (Pa)
P_outlet : float, outlet pressure (Pa)
eta : float, pump efficiency
"""
fluid = Chemical(fluid_name, T=T_inlet, P=P_inlet)
rho = fluid.rho # kg/m³
Cp = fluid.Cp # J/kg·K
delta_P = P_outlet - P_inlet # Pa
# Temperature rise from inefficiency
delta_T = (1 - eta) * delta_P / (rho * Cp)
# Recalculate properties at outlet
T_outlet = T_inlet + delta_T
fluid_out = Chemical(fluid_name, T=T_outlet, P=P_outlet)
# Enthalpy change
H_inlet = fluid.H # J/kg
H_outlet = fluid_out.H # J/kg
delta_H = H_outlet - H_inlet
return {
'T_inlet': T_inlet - 273.15, # °C
'T_outlet': T_outlet - 273.15, # °C
'delta_T': delta_T, # K
'delta_H': delta_H / 1000, # kJ/kg
'delta_P': delta_P / 1e6 # MPa
}
# Example: Multistage boiler feed pump
result = pump_temperature_rise('water', T_inlet=333.15, P_inlet=500000,
P_outlet=15000000, eta=0.82)
print(f"Inlet temperature: {result['T_inlet']:.1f} °C")
print(f"Outlet temperature: {result['T_outlet']:.1f} °C")
print(f"Temperature rise: {result['delta_T']:.2f} K")
print(f"Enthalpy rise: {result['delta_H']:.1f} kJ/kg")
print(f"Pressure rise: {result['delta_P']:.1f} MPa")
Determine if two-phase flow will occur in pump.
from thermo.chemical import Chemical
def check_phase_state(fluid_name, T, P):
"""
Check if fluid is single-phase or will flash to vapor.
"""
fluid = Chemical(fluid_name, T=T, P=P)
Psat = fluid.Psat # Vapor pressure
Tsat = fluid.Tb # Boiling point at 1 atm
# Determine phase
if P > Psat:
phase = "Liquid (subcooled)"
margin = (P - Psat) / Psat * 100 # % margin
elif P < Psat:
phase = "Two-phase or Vapor (FLASHING!)"
margin = (Psat - P) / P * 100 # % above operating pressure
else:
phase = "Saturated liquid"
margin = 0
return {
'phase': phase,
'T': T - 273.15,
'P': P / 1000, # kPa
'Psat': Psat / 1000, # kPa
'margin': margin
}
# Example: Check if water will flash at pump suction
temps = [40, 60, 80, 100] # °C
P_suction = 120000 # Pa (1.2 bar absolute)
for T_c in temps:
result = check_phase_state('water', T=T_c+273.15, P=P_suction)
print(f"T = {result['T']:.0f}°C, P = {result['P']:.1f} kPa: {result['phase']}")
if 'subcooled' in result['phase']:
print(f" Margin = {result['margin']:.1f}%")
print()
# Output will show flashing occurs above ~105°C at 1.2 bar
from thermo import ChemicalConstantsPackage, PRMIX, CEOSGas, CEOSLiquid, FlashVL
from thermo.interaction_parameters import IPDB
# High-pressure natural gas mixture
constants = ChemicalConstantsPackage.constants_from_IDs(['methane', 'ethane', 'propane'])
# Operating conditions
T = 298.15 # K
P = 10e6 # Pa (100 bar)
zs = [0.85, 0.10, 0.05] # Mole fractions
# Peng-Robinson EOS
kijs = IPDB.get_ip_asymmetric_matrix('ChemSep PR', constants.CASs, 'kij')
eos_kwargs = {'Tcs': constants.Tcs, 'Pcs': constants.Pcs, 'omegas': constants.omegas, 'kijs': kijs}
gas = CEOSGas(PRMIX, eos_kwargs, HeatCapacityGases=constants.HeatCapacityGases, T=T, P=P, zs=zs)
liquid = CEOSLiquid(PRMIX, eos_kwargs, HeatCapacityGases=constants.HeatCapacityGases, T=T, P=P, zs=zs)
# Flash calculation
flasher = FlashVL(constants, HeatCapacityGases=constants.HeatCapacityGases, gas=gas, liquids=[liquid])
res = flasher.flash(T=T, P=P, zs=zs)
print(f"Temperature: {T-273.15:.1f} °C")
print(f"Pressure: {P/1e6:.1f} MPa")
print(f"Phase: {res.phase}")
if res.phase == 'L':
print(f"Liquid density: {res.rho_mass():.1f} kg/m³")
print(f"Liquid viscosity: {res.mu()*1e6:.2f} μPa·s")
See examples.py for verified, production-ready examples including:
The thermo package complements fluids for complete pump analysis:
from thermo.chemical import Chemical
from fluids.core import Reynolds
from fluids.friction import friction_factor
# Get properties from thermo
water = Chemical('water', T=333.15, P=101325) # 60°C
rho = water.rho
mu = water.mu
# Use in fluids calculations
Re = Reynolds(V=2.0, D=0.1, rho=rho, mu=mu)
f = friction_factor(Re=Re, eD=0.0001)
print(f"At 60°C: Re = {Re:.0f}, f = {f:.5f}")
Access to 20,000+ chemicals via CAS number or name:
See reference.md for database details and common pump fluids.
All properties in SI units: