Closed Pheenixm closed 3 years ago
Hello Rob, Ejectors? Very neat stuff! The loop in Chemical to seek the PS values you specified failed to converge. You might want to make a PS plot for the chemical and check the value you're seeking is able to be obtained.
The method Calculate_PS is a very basic newton wrapper of the TP calculation routine; if it works great, but I never had high hopes for it. In particular, newton likes to guess negative temperatures and that obviously goes poorly. Quality is also not part of the API for chemicals at the moment, so you might be running into issues there if it's changing phase. The mixture API may be more helpful there.
Not knowing the chemical you're working with or the PS values you were seeking, I can't speculate further.
Sincerely, Caleb
Here's my entire file, it's for a class, so I have no compunctions about making it publicly available.
from thermo import Chemical
from thermo import utils
import numpy as np
import math
class Ejector:
# Geometric Parameters
nozzle_throat_area = 8.04e-6
nozzle_outlet_area = 3.914e-5
mixing_area = 7.088e-5
diffuser_outlet_area = 4.524e-4
# Values from Boiler
primary_flow_temperature = 45 + 273
# Is pressure temperature dependent?
primary_flow_pressure = 0.3e6
# Values from Evaporator
secondary_flow_temperature = 10 + 273
secondary_flow_pressure = 0.5e3
# Estimates
isoentropic_nozzle_efficiency = 0.95
isoentropic_suction_efficiency = 0.85
# Specify fluid
medium = 'Ammonia'
ref = "NIST Refprop 9.1"
entrainment_ratio = 0.5 #secondary mass flow rate / primary mass flow rate
'''
Allows you to set all variables manually
'''
def __init__(self,
primary_flow_temperature=primary_flow_temperature,
primary_flow_pressure=primary_flow_pressure,
secondary_flow_temperature=secondary_flow_temperature,
secondary_flow_pressure=secondary_flow_pressure,
nozzle_throat_area=nozzle_throat_area,
nozzle_outlet_area=nozzle_outlet_area,
mixing_area=mixing_area,
diffuser_outlet_area=diffuser_outlet_area,
medium=medium,
entrainment_ratio=entrainment_ratio
):
self.primary_flow_temperature = primary_flow_temperature
self.primary_flow_pressure = primary_flow_pressure
self.secondary_flow_temperature = secondary_flow_temperature
self.secondary_flow_pressure = secondary_flow_pressure
self.nozzle_throat_area = nozzle_throat_area
self.nozzle_outlet_area = nozzle_outlet_area
self.mixing_area = mixing_area
self.diffuser_outlet_area = diffuser_outlet_area
self.medium = medium
self.entrainment_ratio = entrainment_ratio
'''
Takes initial conditions and calculates the nozzle pressure from this
Returns nozzle flow as a Chemical object
'''
def calculate_nozzle_flow(self):
# CALCULATE Pressure in the Nozzle (Pt)
assumed_nozzle_pressure = 0.3e6
# Calculate Primary Flow Characteristics
primary_flow = Chemical(self.medium, T=self.primary_flow_temperature, P=self.primary_flow_pressure)
primary_flow_entropy = primary_flow.S
primary_flow_enthalpy = primary_flow.H
iterator = 0
nozzle_flow = None
while True:
# Calculate isoentropic enthalpies
isoentropic_enthalpy_throat = Chemical(self.medium, P=assumed_nozzle_pressure, T=Chemical(self.medium).calculate_PS(assumed_nozzle_pressure, primary_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_nozzle = primary_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (primary_flow_enthalpy - isoentropic_enthalpy_throat))
# Calculate estimated speed of sound
calculated_mach_velocity = math.fabs((primary_flow_enthalpy - real_enthalpy_nozzle) * 2) ** 0.5
# Calculate speed of sound at the nozzle, using assumed pressure; will have to iterate over this
primary_nozzle_temp = Chemical(self.medium).calculate_PH(assumed_nozzle_pressure, real_enthalpy_nozzle)
nozzle_flow = Chemical(self.medium, T=primary_nozzle_temp, P=assumed_nozzle_pressure)
pnf = nozzle_flow
dP_dV = pnf.eos.derivatives_and_departures(pnf.T, pnf.P, pnf.Vm, pnf.eos.b, pnf.eos.delta, pnf.eos.epsilon, pnf.eos.a_alpha, pnf.eos.da_alpha_dT, pnf.eos.d2a_alpha_dT2)[0][1]
mach_velocity = utils.speed_of_sound(pnf.Vm, dP_dV, pnf.Cp, pnf.Cvg, pnf.MW)
if math.fabs(mach_velocity - calculated_mach_velocity) <= 0.5:
print("Nozzle temp is %d, Nozzle pressure is %s, Nozzle entropy is %a" % (primary_nozzle_temp, assumed_nozzle_pressure, pnf.S))
return (nozzle_flow, mach_velocity)
break
else:
assumed_nozzle_pressure = assumed_nozzle_pressure - 1000
iterator += 1
# del pnf, primary_nozzle_fluid
def calculate_nozzle_outlet_flow(self, nozzle_flow, velocity_throat):
assumed_outlet_pressure = .2e6
# Calculate Primary Flow Characteristics
nozzle_flow_entropy = nozzle_flow.S
nozzle_flow_enthalpy = nozzle_flow.H
#Calculate flow rate
mass_flow_nozzle = nozzle_flow.rho * self.nozzle_throat_area * velocity_throat
iterator = 0
primary_outlet_flow = None
while True:
# Calculate isoentropic enthalpies
print("Pressure is %d, Entropy is %s" % (assumed_outlet_pressure,nozzle_flow_entropy))
isoentropic_enthalpy_outlet = Chemical(self.medium, P=assumed_outlet_pressure, T=Chemical(self.medium).calculate_PS(assumed_outlet_pressure, nozzle_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_outlet = nozzle_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (nozzle_flow_enthalpy - isoentropic_enthalpy_outlet))
# Calculate estimated speed of sound
calculated_mach_velocity = (2 * (nozzle_flow_enthalpy - real_enthalpy_outlet + (velocity_throat ** 2) / 2)) ** 0.5
# Calculate speed at outlet of nozzle based on known mass flow rate
primary_outlet_temp = Chemical(self.medium).calculate_PH(assumed_outlet_pressure, real_enthalpy_outlet)
primary_outlet_flow = Chemical(self.medium, T=primary_outlet_temp, P=assumed_outlet_pressure)
pof = primary_outlet_flow
assumed_outlet_rho = pof.rho
thermo_velocity = mass_flow_nozzle / (self.nozzle_outlet_area * assumed_outlet_rho)
print('Outlet thermodynamic speed is ', thermo_velocity)
print('Outlet calculated speed is ', calculated_mach_velocity)
if math.fabs(thermo_velocity - calculated_mach_velocity) <= 0.1:
return primary_outlet_flow,thermo_velocity
break
else:
assumed_outlet_pressure = assumed_outlet_pressure - 1000
iterator += 1
print(assumed_outlet_pressure)
#Flow1
def calculate_secondary_pressure(self, flow_outlet, flow_outlet_velocity):
secondary_flow = Chemical(self.medium, T=self.secondary_flow_temperature, P=self.secondary_flow_pressure)
secondary_flow_entropy = secondary_flow.S
secondary_flow_enthalpy = secondary_flow.H
assumed_suction_pressure = self.secondary_flow_pressure
while True:
#First, the S2 Flow#
isoentropic_enthalpy_suction = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, secondary_flow_entropy)).H
real_enthalpy_suction = secondary_flow_enthalpy - (self.isoentropic_suction_efficiency * (secondary_flow_enthalpy - isoentropic_enthalpy_suction))
suction_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_suction)
suction_flow = Chemical(self.medium, T=suction_temp, P=assumed_suction_pressure)
sf = suction_flow
dP_dV = sf.eos.derivatives_and_departures(sf.T, sf.P, sf.Vm, sf.eos.b, sf.eos.delta, sf.eos.epsilon, sf.eos.a_alpha, sf.eos.da_alpha_dT, sf.eos.d2a_alpha_dT2)[0][1]
suction_velocity = utils.speed_of_sound(sf.Vm, dP_dV, sf.Cp, sf.Cvg, sf.MW)
suction_density= sf.rho
suction_mass_flow = self.entrainment_ratio * (flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity)
thermo_suction_area = suction_mass_flow / (suction_velocity * suction_density)
#Now, onto the P2 flow#
isoentropic_enthalpy_primary = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, flow_outlet.S)).H
real_enthalpy_primary = flow_outlet.H - (self.isoentropic_nozzle_efficiency * (flow_outlet.H - isoentropic_enthalpy_primary))
primary_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_primary)
primary_flow_2 = Chemical(self.medium, T=primary_temp, P=assumed_suction_pressure)
pf = primary_flow_2
primary_density = pf.rho
thermo_primary_area = self.mixing_area - thermo_suction_area
primary_mass_flow = flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity
primary_velocity = primary_mass_flow / (thermo_primary_area * primary_density)
right_hand = primary_mass_flow * (flow_outlet.H + (flow_outlet_velocity ** 2) * 0.5) + suction_mass_flow * secondary_flow_enthalpy
left_hand = primary_mass_flow * (pf.H + (primary_velocity ** 2) * 0.5) + suction_mass_flow * (sf.H + (suction_velocity ** 2) * 0.5)
print("Right hand", right_hand)
print("Left hand" , left_hand)
if math.fabs(right_hand - left_hand) <= 1000:
return pf #Primary flow contains all relevant information
break
else:
assumed_suction_pressure = assumed_suction_pressure - 100
print('Assumed Pressure ', assumed_suction_pressure)
ejector = Ejector()
flow_throat, velocity_throat = ejector.calculate_nozzle_flow()
print('Pressure in Nozzle Throat is ', flow_throat.P)
flow1, velocity_outlet = ejector.calculate_nozzle_outlet_flow(flow_throat, velocity_throat)
print('Calling first')
print('Pressure in Nozzle Outlet is ', flow1.P)
print('Mass flow rate is ', flow_throat.rho * ejector.nozzle_throat_area * velocity_throat)
mixing_flow = ejector.calculate_secondary_pressure(flow1, velocity_outlet)
from thermo import Chemical
from thermo import utils
import numpy as np
import math
class Ejector:
# Geometric Parameters
nozzle_throat_area = 8.04e-6
nozzle_outlet_area = 3.914e-5
mixing_area = 7.088e-5
diffuser_outlet_area = 4.524e-4
# Values from Boiler
primary_flow_temperature = 45 + 273
# Is pressure temperature dependent?
primary_flow_pressure = 0.3e6
# Values from Evaporator
secondary_flow_temperature = 10 + 273
secondary_flow_pressure = 0.5e3
# Estimates
isoentropic_nozzle_efficiency = 0.95
isoentropic_suction_efficiency = 0.85
# Specify fluid
medium = 'Ammonia'
ref = "NIST Refprop 9.1"
entrainment_ratio = 0.5 #secondary mass flow rate / primary mass flow rate
'''
Allows you to set all variables manually
'''
def __init__(self,
primary_flow_temperature=primary_flow_temperature,
primary_flow_pressure=primary_flow_pressure,
secondary_flow_temperature=secondary_flow_temperature,
secondary_flow_pressure=secondary_flow_pressure,
nozzle_throat_area=nozzle_throat_area,
nozzle_outlet_area=nozzle_outlet_area,
mixing_area=mixing_area,
diffuser_outlet_area=diffuser_outlet_area,
medium=medium,
entrainment_ratio=entrainment_ratio
):
self.primary_flow_temperature = primary_flow_temperature
self.primary_flow_pressure = primary_flow_pressure
self.secondary_flow_temperature = secondary_flow_temperature
self.secondary_flow_pressure = secondary_flow_pressure
self.nozzle_throat_area = nozzle_throat_area
self.nozzle_outlet_area = nozzle_outlet_area
self.mixing_area = mixing_area
self.diffuser_outlet_area = diffuser_outlet_area
self.medium = medium
self.entrainment_ratio = entrainment_ratio
'''
Takes initial conditions and calculates the nozzle pressure from this
Returns nozzle flow as a Chemical object
'''
def calculate_nozzle_flow(self):
# CALCULATE Pressure in the Nozzle (Pt)
assumed_nozzle_pressure = 0.3e6
# Calculate Primary Flow Characteristics
primary_flow = Chemical(self.medium, T=self.primary_flow_temperature, P=self.primary_flow_pressure)
primary_flow_entropy = primary_flow.S
primary_flow_enthalpy = primary_flow.H
iterator = 0
nozzle_flow = None
while True:
# Calculate isoentropic enthalpies
isoentropic_enthalpy_throat = Chemical(self.medium, P=assumed_nozzle_pressure, T=Chemical(self.medium).calculate_PS(assumed_nozzle_pressure, primary_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_nozzle = primary_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (primary_flow_enthalpy - isoentropic_enthalpy_throat))
# Calculate estimated speed of sound
calculated_mach_velocity = math.fabs((primary_flow_enthalpy - real_enthalpy_nozzle) * 2) ** 0.5
# Calculate speed of sound at the nozzle, using assumed pressure; will have to iterate over this
primary_nozzle_temp = Chemical(self.medium).calculate_PH(assumed_nozzle_pressure, real_enthalpy_nozzle)
nozzle_flow = Chemical(self.medium, T=primary_nozzle_temp, P=assumed_nozzle_pressure)
pnf = nozzle_flow
dP_dV = pnf.eos.derivatives_and_departures(pnf.T, pnf.P, pnf.Vm, pnf.eos.b, pnf.eos.delta, pnf.eos.epsilon, pnf.eos.a_alpha, pnf.eos.da_alpha_dT, pnf.eos.d2a_alpha_dT2)[0][1]
mach_velocity = utils.speed_of_sound(pnf.Vm, dP_dV, pnf.Cp, pnf.Cvg, pnf.MW)
if math.fabs(mach_velocity - calculated_mach_velocity) <= 0.5:
print("Nozzle temp is %d, Nozzle pressure is %s, Nozzle entropy is %a" % (primary_nozzle_temp, assumed_nozzle_pressure, pnf.S))
return (nozzle_flow, mach_velocity)
break
else:
assumed_nozzle_pressure = assumed_nozzle_pressure - 1000
iterator += 1
# del pnf, primary_nozzle_fluid
def calculate_nozzle_outlet_flow(self, nozzle_flow, velocity_throat):
assumed_outlet_pressure = .2e6
# Calculate Primary Flow Characteristics
nozzle_flow_entropy = nozzle_flow.S
nozzle_flow_enthalpy = nozzle_flow.H
#Calculate flow rate
mass_flow_nozzle = nozzle_flow.rho * self.nozzle_throat_area * velocity_throat
iterator = 0
primary_outlet_flow = None
while True:
# Calculate isoentropic enthalpies
print("Pressure is %d, Entropy is %s" % (assumed_outlet_pressure,nozzle_flow_entropy))
isoentropic_enthalpy_outlet = Chemical(self.medium, P=assumed_outlet_pressure, T=Chemical(self.medium).calculate_PS(assumed_outlet_pressure, nozzle_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_outlet = nozzle_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (nozzle_flow_enthalpy - isoentropic_enthalpy_outlet))
# Calculate estimated speed of sound
calculated_mach_velocity = (2 * (nozzle_flow_enthalpy - real_enthalpy_outlet + (velocity_throat ** 2) / 2)) ** 0.5
# Calculate speed at outlet of nozzle based on known mass flow rate
primary_outlet_temp = Chemical(self.medium).calculate_PH(assumed_outlet_pressure, real_enthalpy_outlet)
primary_outlet_flow = Chemical(self.medium, T=primary_outlet_temp, P=assumed_outlet_pressure)
pof = primary_outlet_flow
assumed_outlet_rho = pof.rho
thermo_velocity = mass_flow_nozzle / (self.nozzle_outlet_area * assumed_outlet_rho)
print('Outlet thermodynamic speed is ', thermo_velocity)
print('Outlet calculated speed is ', calculated_mach_velocity)
if math.fabs(thermo_velocity - calculated_mach_velocity) <= 0.1:
return primary_outlet_flow,thermo_velocity
break
else:
assumed_outlet_pressure = assumed_outlet_pressure - 1000
iterator += 1
print(assumed_outlet_pressure)
#Flow1
def calculate_secondary_pressure(self, flow_outlet, flow_outlet_velocity):
secondary_flow = Chemical(self.medium, T=self.secondary_flow_temperature, P=self.secondary_flow_pressure)
secondary_flow_entropy = secondary_flow.S
secondary_flow_enthalpy = secondary_flow.H
assumed_suction_pressure = self.secondary_flow_pressure
while True:
#First, the S2 Flow#
isoentropic_enthalpy_suction = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, secondary_flow_entropy)).H
real_enthalpy_suction = secondary_flow_enthalpy - (self.isoentropic_suction_efficiency * (secondary_flow_enthalpy - isoentropic_enthalpy_suction))
suction_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_suction)
suction_flow = Chemical(self.medium, T=suction_temp, P=assumed_suction_pressure)
sf = suction_flow
dP_dV = sf.eos.derivatives_and_departures(sf.T, sf.P, sf.Vm, sf.eos.b, sf.eos.delta, sf.eos.epsilon, sf.eos.a_alpha, sf.eos.da_alpha_dT, sf.eos.d2a_alpha_dT2)[0][1]
suction_velocity = utils.speed_of_sound(sf.Vm, dP_dV, sf.Cp, sf.Cvg, sf.MW)
suction_density= sf.rho
suction_mass_flow = self.entrainment_ratio * (flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity)
thermo_suction_area = suction_mass_flow / (suction_velocity * suction_density)
#Now, onto the P2 flow#
isoentropic_enthalpy_primary = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, flow_outlet.S)).H
real_enthalpy_primary = flow_outlet.H - (self.isoentropic_nozzle_efficiency * (flow_outlet.H - isoentropic_enthalpy_primary))
primary_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_primary)
primary_flow_2 = Chemical(self.medium, T=primary_temp, P=assumed_suction_pressure)
pf = primary_flow_2
primary_density = pf.rho
thermo_primary_area = self.mixing_area - thermo_suction_area
primary_mass_flow = flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity
primary_velocity = primary_mass_flow / (thermo_primary_area * primary_density)
right_hand = primary_mass_flow * (flow_outlet.H + (flow_outlet_velocity ** 2) * 0.5) + suction_mass_flow * secondary_flow_enthalpy
left_hand = primary_mass_flow * (pf.H + (primary_velocity ** 2) * 0.5) + suction_mass_flow * (sf.H + (suction_velocity ** 2) * 0.5)
print("Right hand", right_hand)
print("Left hand" , left_hand)
if math.fabs(right_hand - left_hand) <= 1000:
return pf #Primary flow contains all relevant information
break
else:
assumed_suction_pressure = assumed_suction_pressure - 100
print('Assumed Pressure ', assumed_suction_pressure)
ejector = Ejector()
flow_throat, velocity_throat = ejector.calculate_nozzle_flow()
print('Pressure in Nozzle Throat is ', flow_throat.P)
flow1, velocity_outlet = ejector.calculate_nozzle_outlet_flow(flow_throat, velocity_throat)
print('Calling first')
print('Pressure in Nozzle Outlet is ', flow1.P)
print('Mass flow rate is ', flow_throat.rho * ejector.nozzle_throat_area * velocity_throat)
mixing_flow = ejector.calculate_secondary_pressure(flow1, velocity_outlet)
from thermo import Chemical
from thermo import utils
import numpy as np
import math
class Ejector:
# Geometric Parameters
nozzle_throat_area = 8.04e-6
nozzle_outlet_area = 3.914e-5
mixing_area = 7.088e-5
diffuser_outlet_area = 4.524e-4
# Values from Boiler
primary_flow_temperature = 45 + 273
# Is pressure temperature dependent?
primary_flow_pressure = 0.3e6
# Values from Evaporator
secondary_flow_temperature = 10 + 273
secondary_flow_pressure = 0.5e3
# Estimates
isoentropic_nozzle_efficiency = 0.95
isoentropic_suction_efficiency = 0.85
# Specify fluid
medium = 'Ammonia'
ref = "NIST Refprop 9.1"
entrainment_ratio = 0.5 #secondary mass flow rate / primary mass flow rate
'''
Allows you to set all variables manually
'''
def __init__(self,
primary_flow_temperature=primary_flow_temperature,
primary_flow_pressure=primary_flow_pressure,
secondary_flow_temperature=secondary_flow_temperature,
secondary_flow_pressure=secondary_flow_pressure,
nozzle_throat_area=nozzle_throat_area,
nozzle_outlet_area=nozzle_outlet_area,
mixing_area=mixing_area,
diffuser_outlet_area=diffuser_outlet_area,
medium=medium,
entrainment_ratio=entrainment_ratio
):
self.primary_flow_temperature = primary_flow_temperature
self.primary_flow_pressure = primary_flow_pressure
self.secondary_flow_temperature = secondary_flow_temperature
self.secondary_flow_pressure = secondary_flow_pressure
self.nozzle_throat_area = nozzle_throat_area
self.nozzle_outlet_area = nozzle_outlet_area
self.mixing_area = mixing_area
self.diffuser_outlet_area = diffuser_outlet_area
self.medium = medium
self.entrainment_ratio = entrainment_ratio
'''
Takes initial conditions and calculates the nozzle pressure from this
Returns nozzle flow as a Chemical object
'''
def calculate_nozzle_flow(self):
# CALCULATE Pressure in the Nozzle (Pt)
assumed_nozzle_pressure = 0.3e6
# Calculate Primary Flow Characteristics
primary_flow = Chemical(self.medium, T=self.primary_flow_temperature, P=self.primary_flow_pressure)
primary_flow_entropy = primary_flow.S
primary_flow_enthalpy = primary_flow.H
iterator = 0
nozzle_flow = None
while True:
# Calculate isoentropic enthalpies
isoentropic_enthalpy_throat = Chemical(self.medium, P=assumed_nozzle_pressure, T=Chemical(self.medium).calculate_PS(assumed_nozzle_pressure, primary_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_nozzle = primary_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (primary_flow_enthalpy - isoentropic_enthalpy_throat))
# Calculate estimated speed of sound
calculated_mach_velocity = math.fabs((primary_flow_enthalpy - real_enthalpy_nozzle) * 2) ** 0.5
# Calculate speed of sound at the nozzle, using assumed pressure; will have to iterate over this
primary_nozzle_temp = Chemical(self.medium).calculate_PH(assumed_nozzle_pressure, real_enthalpy_nozzle)
nozzle_flow = Chemical(self.medium, T=primary_nozzle_temp, P=assumed_nozzle_pressure)
pnf = nozzle_flow
dP_dV = pnf.eos.derivatives_and_departures(pnf.T, pnf.P, pnf.Vm, pnf.eos.b, pnf.eos.delta, pnf.eos.epsilon, pnf.eos.a_alpha, pnf.eos.da_alpha_dT, pnf.eos.d2a_alpha_dT2)[0][1]
mach_velocity = utils.speed_of_sound(pnf.Vm, dP_dV, pnf.Cp, pnf.Cvg, pnf.MW)
if math.fabs(mach_velocity - calculated_mach_velocity) <= 0.5:
print("Nozzle temp is %d, Nozzle pressure is %s, Nozzle entropy is %a" % (primary_nozzle_temp, assumed_nozzle_pressure, pnf.S))
return (nozzle_flow, mach_velocity)
break
else:
assumed_nozzle_pressure = assumed_nozzle_pressure - 1000
iterator += 1
# del pnf, primary_nozzle_fluid
def calculate_nozzle_outlet_flow(self, nozzle_flow, velocity_throat):
assumed_outlet_pressure = .2e6
# Calculate Primary Flow Characteristics
nozzle_flow_entropy = nozzle_flow.S
nozzle_flow_enthalpy = nozzle_flow.H
#Calculate flow rate
mass_flow_nozzle = nozzle_flow.rho * self.nozzle_throat_area * velocity_throat
iterator = 0
primary_outlet_flow = None
while True:
# Calculate isoentropic enthalpies
print("Pressure is %d, Entropy is %s" % (assumed_outlet_pressure,nozzle_flow_entropy))
isoentropic_enthalpy_outlet = Chemical(self.medium, P=assumed_outlet_pressure, T=Chemical(self.medium).calculate_PS(assumed_outlet_pressure, nozzle_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_outlet = nozzle_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (nozzle_flow_enthalpy - isoentropic_enthalpy_outlet))
# Calculate estimated speed of sound
calculated_mach_velocity = (2 * (nozzle_flow_enthalpy - real_enthalpy_outlet + (velocity_throat ** 2) / 2)) ** 0.5
# Calculate speed at outlet of nozzle based on known mass flow rate
primary_outlet_temp = Chemical(self.medium).calculate_PH(assumed_outlet_pressure, real_enthalpy_outlet)
primary_outlet_flow = Chemical(self.medium, T=primary_outlet_temp, P=assumed_outlet_pressure)
pof = primary_outlet_flow
assumed_outlet_rho = pof.rho
thermo_velocity = mass_flow_nozzle / (self.nozzle_outlet_area * assumed_outlet_rho)
print('Outlet thermodynamic speed is ', thermo_velocity)
print('Outlet calculated speed is ', calculated_mach_velocity)
if math.fabs(thermo_velocity - calculated_mach_velocity) <= 0.1:
return primary_outlet_flow,thermo_velocity
break
else:
assumed_outlet_pressure = assumed_outlet_pressure - 1000
iterator += 1
print(assumed_outlet_pressure)
#Flow1
def calculate_secondary_pressure(self, flow_outlet, flow_outlet_velocity):
secondary_flow = Chemical(self.medium, T=self.secondary_flow_temperature, P=self.secondary_flow_pressure)
secondary_flow_entropy = secondary_flow.S
secondary_flow_enthalpy = secondary_flow.H
assumed_suction_pressure = self.secondary_flow_pressure
while True:
#First, the S2 Flow#
isoentropic_enthalpy_suction = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, secondary_flow_entropy)).H
real_enthalpy_suction = secondary_flow_enthalpy - (self.isoentropic_suction_efficiency * (secondary_flow_enthalpy - isoentropic_enthalpy_suction))
suction_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_suction)
suction_flow = Chemical(self.medium, T=suction_temp, P=assumed_suction_pressure)
sf = suction_flow
dP_dV = sf.eos.derivatives_and_departures(sf.T, sf.P, sf.Vm, sf.eos.b, sf.eos.delta, sf.eos.epsilon, sf.eos.a_alpha, sf.eos.da_alpha_dT, sf.eos.d2a_alpha_dT2)[0][1]
suction_velocity = utils.speed_of_sound(sf.Vm, dP_dV, sf.Cp, sf.Cvg, sf.MW)
suction_density= sf.rho
suction_mass_flow = self.entrainment_ratio * (flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity)
thermo_suction_area = suction_mass_flow / (suction_velocity * suction_density)
#Now, onto the P2 flow#
isoentropic_enthalpy_primary = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, flow_outlet.S)).H
real_enthalpy_primary = flow_outlet.H - (self.isoentropic_nozzle_efficiency * (flow_outlet.H - isoentropic_enthalpy_primary))
primary_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_primary)
primary_flow_2 = Chemical(self.medium, T=primary_temp, P=assumed_suction_pressure)
pf = primary_flow_2
primary_density = pf.rho
thermo_primary_area = self.mixing_area - thermo_suction_area
primary_mass_flow = flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity
primary_velocity = primary_mass_flow / (thermo_primary_area * primary_density)
right_hand = primary_mass_flow * (flow_outlet.H + (flow_outlet_velocity ** 2) * 0.5) + suction_mass_flow * secondary_flow_enthalpy
left_hand = primary_mass_flow * (pf.H + (primary_velocity ** 2) * 0.5) + suction_mass_flow * (sf.H + (suction_velocity ** 2) * 0.5)
print("Right hand", right_hand)
print("Left hand" , left_hand)
if math.fabs(right_hand - left_hand) <= 1000:
return pf #Primary flow contains all relevant information
break
else:
assumed_suction_pressure = assumed_suction_pressure - 100
print('Assumed Pressure ', assumed_suction_pressure)
ejector = Ejector()
flow_throat, velocity_throat = ejector.calculate_nozzle_flow()
print('Pressure in Nozzle Throat is ', flow_throat.P)
flow1, velocity_outlet = ejector.calculate_nozzle_outlet_flow(flow_throat, velocity_throat)
print('Calling first')
print('Pressure in Nozzle Outlet is ', flow1.P)
print('Mass flow rate is ', flow_throat.rho * ejector.nozzle_throat_area * velocity_throat)
mixing_flow = ejector.calculate_secondary_pressure(flow1, velocity_outlet)
from thermo import Chemical
from thermo import utils
import numpy as np
import math
class Ejector:
# Geometric Parameters
nozzle_throat_area = 8.04e-6
nozzle_outlet_area = 3.914e-5
mixing_area = 7.088e-5
diffuser_outlet_area = 4.524e-4
# Values from Boiler
primary_flow_temperature = 45 + 273
# Is pressure temperature dependent?
primary_flow_pressure = 0.3e6
# Values from Evaporator
secondary_flow_temperature = 10 + 273
secondary_flow_pressure = 0.5e3
# Estimates
isoentropic_nozzle_efficiency = 0.95
isoentropic_suction_efficiency = 0.85
# Specify fluid
medium = 'Ammonia'
ref = "NIST Refprop 9.1"
entrainment_ratio = 0.5 #secondary mass flow rate / primary mass flow rate
'''
Allows you to set all variables manually
'''
def __init__(self,
primary_flow_temperature=primary_flow_temperature,
primary_flow_pressure=primary_flow_pressure,
secondary_flow_temperature=secondary_flow_temperature,
secondary_flow_pressure=secondary_flow_pressure,
nozzle_throat_area=nozzle_throat_area,
nozzle_outlet_area=nozzle_outlet_area,
mixing_area=mixing_area,
diffuser_outlet_area=diffuser_outlet_area,
medium=medium,
entrainment_ratio=entrainment_ratio
):
self.primary_flow_temperature = primary_flow_temperature
self.primary_flow_pressure = primary_flow_pressure
self.secondary_flow_temperature = secondary_flow_temperature
self.secondary_flow_pressure = secondary_flow_pressure
self.nozzle_throat_area = nozzle_throat_area
self.nozzle_outlet_area = nozzle_outlet_area
self.mixing_area = mixing_area
self.diffuser_outlet_area = diffuser_outlet_area
self.medium = medium
self.entrainment_ratio = entrainment_ratio
'''
Takes initial conditions and calculates the nozzle pressure from this
Returns nozzle flow as a Chemical object
'''
def calculate_nozzle_flow(self):
# CALCULATE Pressure in the Nozzle (Pt)
assumed_nozzle_pressure = 0.3e6
# Calculate Primary Flow Characteristics
primary_flow = Chemical(self.medium, T=self.primary_flow_temperature, P=self.primary_flow_pressure)
primary_flow_entropy = primary_flow.S
primary_flow_enthalpy = primary_flow.H
iterator = 0
nozzle_flow = None
while True:
# Calculate isoentropic enthalpies
isoentropic_enthalpy_throat = Chemical(self.medium, P=assumed_nozzle_pressure, T=Chemical(self.medium).calculate_PS(assumed_nozzle_pressure, primary_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_nozzle = primary_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (primary_flow_enthalpy - isoentropic_enthalpy_throat))
# Calculate estimated speed of sound
calculated_mach_velocity = math.fabs((primary_flow_enthalpy - real_enthalpy_nozzle) * 2) ** 0.5
# Calculate speed of sound at the nozzle, using assumed pressure; will have to iterate over this
primary_nozzle_temp = Chemical(self.medium).calculate_PH(assumed_nozzle_pressure, real_enthalpy_nozzle)
nozzle_flow = Chemical(self.medium, T=primary_nozzle_temp, P=assumed_nozzle_pressure)
pnf = nozzle_flow
dP_dV = pnf.eos.derivatives_and_departures(pnf.T, pnf.P, pnf.Vm, pnf.eos.b, pnf.eos.delta, pnf.eos.epsilon, pnf.eos.a_alpha, pnf.eos.da_alpha_dT, pnf.eos.d2a_alpha_dT2)[0][1]
mach_velocity = utils.speed_of_sound(pnf.Vm, dP_dV, pnf.Cp, pnf.Cvg, pnf.MW)
if math.fabs(mach_velocity - calculated_mach_velocity) <= 0.5:
print("Nozzle temp is %d, Nozzle pressure is %s, Nozzle entropy is %a" % (primary_nozzle_temp, assumed_nozzle_pressure, pnf.S))
return (nozzle_flow, mach_velocity)
break
else:
assumed_nozzle_pressure = assumed_nozzle_pressure - 1000
iterator += 1
# del pnf, primary_nozzle_fluid
def calculate_nozzle_outlet_flow(self, nozzle_flow, velocity_throat):
assumed_outlet_pressure = .2e6
# Calculate Primary Flow Characteristics
nozzle_flow_entropy = nozzle_flow.S
nozzle_flow_enthalpy = nozzle_flow.H
#Calculate flow rate
mass_flow_nozzle = nozzle_flow.rho * self.nozzle_throat_area * velocity_throat
iterator = 0
primary_outlet_flow = None
while True:
# Calculate isoentropic enthalpies
print("Pressure is %d, Entropy is %s" % (assumed_outlet_pressure,nozzle_flow_entropy))
isoentropic_enthalpy_outlet = Chemical(self.medium, P=assumed_outlet_pressure, T=Chemical(self.medium).calculate_PS(assumed_outlet_pressure, nozzle_flow_entropy)).H
# Determine real enthropy at nozzle
real_enthalpy_outlet = nozzle_flow_enthalpy - (self.isoentropic_nozzle_efficiency * (nozzle_flow_enthalpy - isoentropic_enthalpy_outlet))
# Calculate estimated speed of sound
calculated_mach_velocity = (2 * (nozzle_flow_enthalpy - real_enthalpy_outlet + (velocity_throat ** 2) / 2)) ** 0.5
# Calculate speed at outlet of nozzle based on known mass flow rate
primary_outlet_temp = Chemical(self.medium).calculate_PH(assumed_outlet_pressure, real_enthalpy_outlet)
primary_outlet_flow = Chemical(self.medium, T=primary_outlet_temp, P=assumed_outlet_pressure)
pof = primary_outlet_flow
assumed_outlet_rho = pof.rho
thermo_velocity = mass_flow_nozzle / (self.nozzle_outlet_area * assumed_outlet_rho)
print('Outlet thermodynamic speed is ', thermo_velocity)
print('Outlet calculated speed is ', calculated_mach_velocity)
if math.fabs(thermo_velocity - calculated_mach_velocity) <= 0.1:
return primary_outlet_flow,thermo_velocity
break
else:
assumed_outlet_pressure = assumed_outlet_pressure - 1000
iterator += 1
print(assumed_outlet_pressure)
#Flow1
def calculate_secondary_pressure(self, flow_outlet, flow_outlet_velocity):
secondary_flow = Chemical(self.medium, T=self.secondary_flow_temperature, P=self.secondary_flow_pressure)
secondary_flow_entropy = secondary_flow.S
secondary_flow_enthalpy = secondary_flow.H
assumed_suction_pressure = self.secondary_flow_pressure
while True:
#First, the S2 Flow#
isoentropic_enthalpy_suction = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, secondary_flow_entropy)).H
real_enthalpy_suction = secondary_flow_enthalpy - (self.isoentropic_suction_efficiency * (secondary_flow_enthalpy - isoentropic_enthalpy_suction))
suction_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_suction)
suction_flow = Chemical(self.medium, T=suction_temp, P=assumed_suction_pressure)
sf = suction_flow
dP_dV = sf.eos.derivatives_and_departures(sf.T, sf.P, sf.Vm, sf.eos.b, sf.eos.delta, sf.eos.epsilon, sf.eos.a_alpha, sf.eos.da_alpha_dT, sf.eos.d2a_alpha_dT2)[0][1]
suction_velocity = utils.speed_of_sound(sf.Vm, dP_dV, sf.Cp, sf.Cvg, sf.MW)
suction_density= sf.rho
suction_mass_flow = self.entrainment_ratio * (flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity)
thermo_suction_area = suction_mass_flow / (suction_velocity * suction_density)
#Now, onto the P2 flow#
isoentropic_enthalpy_primary = Chemical(self.medium, P=assumed_suction_pressure, T=Chemical(self.medium).calculate_PS(assumed_suction_pressure, flow_outlet.S)).H
real_enthalpy_primary = flow_outlet.H - (self.isoentropic_nozzle_efficiency * (flow_outlet.H - isoentropic_enthalpy_primary))
primary_temp = Chemical(self.medium).calculate_PH(assumed_suction_pressure, real_enthalpy_primary)
primary_flow_2 = Chemical(self.medium, T=primary_temp, P=assumed_suction_pressure)
pf = primary_flow_2
primary_density = pf.rho
thermo_primary_area = self.mixing_area - thermo_suction_area
primary_mass_flow = flow_outlet.rho * self.nozzle_outlet_area * flow_outlet_velocity
primary_velocity = primary_mass_flow / (thermo_primary_area * primary_density)
right_hand = primary_mass_flow * (flow_outlet.H + (flow_outlet_velocity ** 2) * 0.5) + suction_mass_flow * secondary_flow_enthalpy
left_hand = primary_mass_flow * (pf.H + (primary_velocity ** 2) * 0.5) + suction_mass_flow * (sf.H + (suction_velocity ** 2) * 0.5)
print("Right hand", right_hand)
print("Left hand" , left_hand)
if math.fabs(right_hand - left_hand) <= 1000:
return pf #Primary flow contains all relevant information
break
else:
assumed_suction_pressure = assumed_suction_pressure - 100
print('Assumed Pressure ', assumed_suction_pressure)
ejector = Ejector()
flow_throat, velocity_throat = ejector.calculate_nozzle_flow()
print('Pressure in Nozzle Throat is ', flow_throat.P)
flow1, velocity_outlet = ejector.calculate_nozzle_outlet_flow(flow_throat, velocity_throat)
print('Calling first')
print('Pressure in Nozzle Outlet is ', flow1.P)
print('Mass flow rate is ', flow_throat.rho * ejector.nozzle_throat_area * velocity_throat)
mixing_flow = ejector.calculate_secondary_pressure(flow1, velocity_outlet)
Thanks so much for the quick reply! I'm not entirely sure what's going on, I've been examining the entropy values more closely, and they're negative.
For reference:
Nozzle temp is 275, Nozzle pressure is 159000.0, Nozzle entropy is -389.13764658464726
Actual value should be 6.38 KJ/kgK, can you explain why I'm seeing these values? Am I not calculating entropy correctly?
Hi Rob, Entropy and enthalpy are always calculated with respect to a reference point. To compare values from different sources, one must always use only changes in values, not absolute values. REFPROP has much more accurate values of enthalpy than thermo; thermo has only a cubic EOS behind it, whereas theirs is a custom EOS just for ammonia. I'd hope it wouldn't be too much of a difference though.
The issue with the solver is indeed a phase change you are observing; the entropy is discontinuous at the phase change, as shown in this crude plot.
You seem comfortable with the library; I'd encourage you to use the HeatCapacityLiquid and HeatCapacityGas (use the T_dependent_property_integral and T_dependent_property_integral_over_T methods) along with the eos departure enthalpy/entropies, and build your own entropy/enthalpy reference frame you can fully understand and be confident in from the pieces. It's a great learning experience! Feel free to ask more questions.
Sincerely,
Caleb
This functionality should be quite reliable now with the new flash interface: https://thermo.readthedocs.io/thermo.flash.html#thermo.flash.FlashPureVLS A number of components are flashed with all sorts of different specifications to a very low tolerance. The implemented phases include cubic equations of state as well as ideal and activity coefficient based models: https://thermo.readthedocs.io/thermo.phases.html Hopefully no one else will need to go through your pain, Pheenixm! Sincerely, Caleb
The code
Any clue what's going on here? I'm trying to perform an iterative conversion for use in a thermodynamic cycle. Thanks.