320 lines
11 KiB
Python
320 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Cycle definitions and efficiency calculations."""
|
|
|
|
from .components import (
|
|
Compressor,
|
|
Concentrator,
|
|
Condenser,
|
|
Heater,
|
|
Recuperator,
|
|
Turbine,
|
|
)
|
|
from .mass_models import apply_component_mass, has_default_mass_model
|
|
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, strict=False, **kwargs):
|
|
"""Calculate mass for each initialized component."""
|
|
component_masses = {}
|
|
for component in self.iter_components():
|
|
if not hasattr(component, "set_mass_result"):
|
|
continue
|
|
if mass_model is None and not has_default_mass_model(component):
|
|
if strict:
|
|
raise NotImplementedError(
|
|
f"No default mass model for component type "
|
|
f"{component.component_type!r}."
|
|
)
|
|
continue
|
|
|
|
try:
|
|
mass = apply_component_mass(
|
|
component,
|
|
mass_model=mass_model,
|
|
**kwargs,
|
|
)
|
|
except NotImplementedError:
|
|
if strict:
|
|
raise
|
|
continue
|
|
|
|
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, strict=False, **kwargs):
|
|
"""Calculate and return total cycle component mass."""
|
|
component_masses = self.calculate_component_masses(
|
|
mass_model=mass_model,
|
|
strict=strict,
|
|
**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, strict=False, **kwargs):
|
|
"""Compatibility alias for cycle_mass_calculator."""
|
|
return self.cycle_mass_calculator(
|
|
mass_model=mass_model,
|
|
strict=strict,
|
|
**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()
|