Files

296 lines
10 KiB
Python

# -*- coding: utf-8 -*-
"""Cycle definitions and efficiency calculations."""
from .components import (
Compressor,
Concentrator,
Condenser,
Heater,
Recuperator,
Turbine,
)
from .properties import CO2PropertyCalculator
class BraytonCycle:
"""CO2 Brayton cycle calculator."""
def __init__(self, name, refprop_path="C:/Program Files (x86)/REFPROP 10.0+/REFPROP"):
self.name = name
self.compressor = None
self.turbine = None
self.recuperator = None
self.heater = None
self.condenser = None
self.concentrator = None
self.component_masses = None
self.total_mass = None
self.refprop_path = refprop_path
self.property_calculator = CO2PropertyCalculator(self.refprop_path)
def cycle_eff_calculator(self):
if isinstance(self.compressor, list):
Wc = sum(compressor.variables["Wc"] for compressor in self.compressor)
else:
Wc = self.compressor.variables["Wc"]
Wt = self.turbine.variables["Wt"]
Q_input = self.heater.variables["Q_in"]
return (Wt - Wc) / Q_input
def iter_components(self):
"""Yield initialized component objects in the current cycle."""
for value in (
self.compressor,
self.turbine,
self.recuperator,
self.heater,
self.condenser,
self.concentrator,
):
if value is None:
continue
if isinstance(value, list):
for component in value:
if component is not None:
yield component
else:
yield value
def calculate_component_masses(self, mass_model=None, **kwargs):
"""Calculate mass for each initialized component."""
component_masses = {}
for component in self.iter_components():
if not hasattr(component, "calculate_mass"):
continue
mass = component.calculate_mass(mass_model=mass_model, **kwargs)
key = component.variables.get("name", component.name)
component_masses[key] = {
"component_type": component.component_type,
"mass": mass,
"mass_variables": component.mass_variables,
}
self.component_masses = component_masses
return self.component_masses
def cycle_mass_calculator(self, mass_model=None, **kwargs):
"""Calculate and return total cycle component mass."""
component_masses = self.calculate_component_masses(
mass_model=mass_model,
**kwargs,
)
self.total_mass = sum(item["mass"] for item in component_masses.values())
return self.total_mass
def calculate_total_mass(self, mass_model=None, **kwargs):
"""Compatibility alias for cycle_mass_calculator."""
return self.cycle_mass_calculator(mass_model=mass_model, **kwargs)
def SC(self, T_low, T_high, p_low, p_high, param=None):
"""Simple Brayton cycle."""
defaults = {
"compressor_eff": 0.98,
"turbine_eff": 0.95,
}
cycle_params = defaults if param is None else {**defaults, **param}
self.heater = Heater(name="Main heater")
self.condenser = Condenser(name="Main condenser")
self.compressor = Compressor(
name="Main compressor",
eff=cycle_params["compressor_eff"],
)
self.turbine = Turbine(name="Turbine", eff=cycle_params["turbine_eff"])
self.compressor.calculator(p_low, T_low, p_high, self.property_calculator)
self.turbine.calculator(p_high, T_high, p_low, self.property_calculator)
self.heater.calculator(
self.compressor.variables["outlet_state"],
self.turbine.variables["inlet_state"],
)
self.condenser.calculator(
self.turbine.variables["outlet_state"],
self.compressor.variables["inlet_state"],
)
return self.cycle_eff_calculator()
def SRC(self, T_low, T_high, p_low, p_high, param=None, ploss=0.0):
"""Simple recuperated Brayton cycle."""
defaults = {
"compressor_eff": 0.98,
"turbine_eff": 0.95,
"recuperator_eff": 0.85,
}
cycle_params = defaults if param is None else {**defaults, **param}
self.heater = Heater(name="Main heater")
self.condenser = Condenser(name="Main condenser")
self.compressor = Compressor(
name="Main compressor",
eff=cycle_params["compressor_eff"],
)
self.turbine = Turbine(name="Turbine", eff=cycle_params["turbine_eff"])
self.recuperator = Recuperator(
name="Recuperator",
eff=cycle_params["recuperator_eff"],
)
self.compressor.calculator(p_low, T_low, p_high, self.property_calculator)
self.turbine.calculator(p_high, T_high, p_low, self.property_calculator)
self.recuperator.calculator(
self.compressor.variables["outlet_state"],
self.turbine.variables["outlet_state"],
0,
ploss,
self.property_calculator,
)
self.condenser.calculator(
self.recuperator.variables["hot_outlet_state"],
self.compressor.variables["inlet_state"],
)
self.heater.calculator(
self.recuperator.variables["cold_outlet_state"],
self.turbine.variables["inlet_state"],
)
return self.cycle_eff_calculator()
def RC(self, T_low, T_high, p_low, p_high, ploss, param=None):
"""Recompression Brayton cycle."""
defaults = {
"compressor_eff": 0.98,
"turbine_eff": 0.95,
"recuperator_eff": 0.85,
"recompressor_eff": 0.98,
"highT_recuperator_eff": 0.85,
"x": 0.9,
}
cycle_params = defaults if param is None else {**defaults, **param}
self.x = cycle_params["x"]
self.heater = Heater(name="Main heater")
self.condenser = Condenser(name="Main_condenser")
self.turbine = Turbine(
name="Main Turbine",
eff=cycle_params["turbine_eff"],
)
main_compressor = Compressor(
name="Main compressor",
eff=cycle_params["compressor_eff"],
)
recompressor = Compressor(
name="Recompressor",
eff=cycle_params["recompressor_eff"],
)
lrecuperator = Recuperator(
name="Low Temperature recuprerator",
eff=cycle_params["recuperator_eff"],
x=self.x,
)
hrecuperator = Recuperator(
name="High Temperature recuperator",
eff=cycle_params["highT_recuperator_eff"],
)
self.concentrator = Concentrator(name="Concentrator")
self.recuperator = []
self.compressor = []
main_compressor.calculator(p_low, T_low, p_high, self.property_calculator)
self.turbine.calculator(
p_high * (1 - ploss) ** 3,
T_high,
p_low * (1 - ploss) ** (-3),
self.property_calculator,
)
mw = self.property_calculator.mw
T_hr_inlet = (
self.turbine.variables["outlet_state"]["T"]
+ main_compressor.variables["outlet_state"]["T"]
) / 2
max_iter = 300
relax_fac = 0.4
for i in range(max_iter):
hr_inlet_state_hot_mol = self.property_calculator.calculate_properties(
P=p_high * (1 - ploss),
T=T_hr_inlet,
)
hr_inlet_state_hot = {
"P": hr_inlet_state_hot_mol["P"],
"T": hr_inlet_state_hot_mol["T"],
"h": hr_inlet_state_hot_mol["h"] / mw,
"s": hr_inlet_state_hot_mol["s"] / mw,
}
hrecuperator.calculator(
hr_inlet_state_hot,
self.turbine.variables["outlet_state"],
0,
ploss,
self.property_calculator,
)
lrecuperator.calculator(
main_compressor.variables["outlet_state"],
hrecuperator.variables["hot_outlet_state"],
1,
ploss,
self.property_calculator,
)
recompressor.calculator(
p_in=p_low / (1 - ploss),
p_out=p_high * (1 - ploss),
T_in=lrecuperator.variables["hot_outlet_state"]["T"],
property_calculator=self.property_calculator,
)
self.concentrator.calculator(
recompressor.variables["outlet_state"],
lrecuperator.variables["cold_outlet_state"],
self.x,
self.property_calculator,
)
Tc_outlet = self.concentrator.variables["outlet_state"]["T"]
err = abs(Tc_outlet - T_hr_inlet)
if err <= 1e-5:
break
if i == max_iter - 1:
raise ValueError(f"Iteration limit exceeded; current error={err:.4f}")
T_hr_inlet = relax_fac * Tc_outlet + (1 - relax_fac) * T_hr_inlet
hrecuperator.calculator(
self.concentrator.variables["outlet_state"],
self.turbine.variables["outlet_state"],
0,
ploss,
self.property_calculator,
)
lrecuperator.calculator(
main_compressor.variables["outlet_state"],
hrecuperator.variables["hot_outlet_state"],
1,
ploss,
self.property_calculator,
)
main_compressor.variables["Wc"] *= 1 - self.x
recompressor.variables["Wc"] *= self.x
self.recuperator.append(lrecuperator)
self.recuperator.append(hrecuperator)
self.compressor.append(main_compressor)
self.compressor.append(recompressor)
self.condenser.calculator(
lrecuperator.variables["hot_outlet_state"],
main_compressor.variables["inlet_state"],
)
self.condenser.variables["Q_out"] *= 1 - self.x
self.heater.calculator(
hrecuperator.variables["cold_outlet_state"],
self.turbine.variables["inlet_state"],
)
return self.cycle_eff_calculator()