# -*- 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(): 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()