class HelmTurbineStageData(HelmIsentropicTurbineData): CONFIG = HelmIsentropicTurbineData.CONFIG() def build(self): super().build() self.efficiency_mech = Var(initialize=1.0, doc="Turbine mechanical efficiency") self.efficiency_mech.fix() time_set = self.flowsheet().config.time self.shaft_speed = Var(time_set, doc="Shaft speed [1/s]", initialize=60.0) self.shaft_speed.fix() @self.Expression(time_set, doc="Specific speed [dimensionless]") def specific_speed(b, t): s = b.shaft_speed[t] # 1/s v = b.control_volume.properties_out[t].flow_vol # m3/s his_rate = b.work_isentropic[t] # J/s m = b.control_volume.properties_out[t].flow_mass # kg/s return s * v**0.5 * (his_rate / m)**(-0.75) # dimensionless @self.Expression(time_set, doc="Thermodynamic power [J/s]") def power_thermo(b, t): return b.control_volume.work[t] @self.Expression(self.flowsheet().config.time, doc="Shaft power [J/s]") def power_shaft(b, t): return b.power_thermo[t] * b.efficiency_mech def initialize( self, outlvl=idaeslog.NOTSET, solver="ipopt", optarg={ "tol": 1e-6, "max_iter": 30 }, ): """ Initialize the turbine stage model. This deactivates the specialized constraints, then does the isentropic turbine initialization, then reactivates the constraints and solves. Args: outlvl : sets output level of initialization routine solver (str): Solver to use for initialization optarg (dict): Solver arguments dictionary """ super().initialize(outlvl=outlvl, solver=solver, optarg=optarg) def calculate_scaling_factors(self): super().calculate_scaling_factors()
class TurbineOutletStageData(HelmIsentropicTurbineData): # Same settings as the default pressure changer, but force to expander with # isentropic efficiency CONFIG = HelmIsentropicTurbineData.CONFIG() def build(self): super().build() self.flow_coeff = Var(initialize=0.0333, doc="Turbine flow coefficient [kg*C^0.5/s/Pa]") self.eff_dry = Var(initialize=0.87, doc="Turbine dry isentropic efficiency") self.design_exhaust_flow_vol = Var( initialize=6000.0, doc="Design exit volumetirc flowrate [m^3/s]") self.efficiency_mech = Var(initialize=1.0, doc="Turbine mechanical efficiency") self.efficiency_isentropic.unfix() self.eff_dry.fix() self.design_exhaust_flow_vol.fix() self.flow_coeff.fix() self.efficiency_mech.fix() @self.Expression(self.flowsheet().config.time, doc="Eff. fact. correlation") def tel(b, t): f = b.control_volume.properties_out[ t].flow_vol / b.design_exhaust_flow_vol return 1e6 * (-0.0035 * f**5 + 0.022 * f**4 - 0.0542 * f**3 + 0.0638 * f**2 - 0.0328 * f + 0.0064) @self.Constraint(self.flowsheet().config.time, doc="Stodola eq. choked flow") def stodola_equation(b, t): flow = b.control_volume.properties_in[t].flow_mol mw = b.control_volume.properties_in[t].mw Tin = b.control_volume.properties_in[t].temperature Pin = b.control_volume.properties_in[t].pressure Pr = b.ratioP[t] cf = b.flow_coeff return flow**2 * mw**2 * (Tin) == (cf**2 * Pin**2 * (1 - Pr**2)) @self.Constraint(self.flowsheet().config.time, doc="Efficiency correlation") def efficiency_correlation(b, t): x = b.control_volume.properties_out[t].vapor_frac eff = b.efficiency_isentropic[t] dh_isen = b.delta_enth_isentropic[t] tel = b.tel[t] return eff == b.eff_dry * x * (1 - 0.65 * (1 - x)) * (1 + tel / dh_isen) @self.Expression(self.flowsheet().config.time, doc="Thermodynamic power [J/s]") def power_thermo(b, t): return b.control_volume.work[t] @self.Expression(self.flowsheet().config.time, doc="Shaft power [J/s]") def power_shaft(b, t): return b.power_thermo[t] * b.efficiency_mech def initialize( self, state_args={}, outlvl=idaeslog.NOTSET, solver="ipopt", optarg={ "tol": 1e-6, "max_iter": 30 }, calculate_cf=True, ): """ Initialize the outlet turbine stage model. This deactivates the specialized constraints, then does the isentropic turbine initialization, then reactivates the constraints and solves. Args: state_args (dict): Initial state for property initialization outlvl : sets output level of initialization routine solver (str): Solver to use for initialization optarg (dict): Solver arguments dictionary """ init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") sp = StoreSpec.value_isfixed_isactive(only_fixed=True) istate = to_json(self, return_dict=True, wts=sp) # sp is what to save to make sure state after init is same as the start # saves value, fixed, and active state, doesn't load originally free # values, this makes sure original problem spec is same but initializes # the values of free vars for t in self.flowsheet().config.time: if self.outlet.pressure[t].fixed: self.ratioP[t] = value(self.outlet.pressure[t] / self.inlet.pressure[t]) self.deltaP[t] = value(self.outlet.pressure[t] - self.inlet.pressure[t]) # Deactivate special constraints self.stodola_equation.deactivate() self.efficiency_correlation.deactivate() self.efficiency_isentropic.fix() self.deltaP.unfix() self.ratioP.unfix() self.inlet.fix() self.outlet.unfix() super().initialize(outlvl=outlvl, solver=solver, optarg=optarg) for t in self.flowsheet().config.time: mw = self.control_volume.properties_in[t].mw Tin = self.control_volume.properties_in[t].temperature Pin = self.control_volume.properties_in[t].pressure Pr = self.ratioP[t] if not calculate_cf: cf = self.flow_coeff self.inlet.flow_mol[t].fix( value(cf * Pin * sqrt(1 - Pr**2) / mw / sqrt(Tin))) super().initialize(outlvl=outlvl, solver=solver, optarg=optarg) self.control_volume.properties_out[:].pressure.fix() # Free eff_isen and activate sepcial constarints self.efficiency_isentropic.unfix() self.outlet.pressure.fix() if calculate_cf: self.flow_coeff.unfix() self.inlet.flow_mol.unfix() self.inlet.flow_mol[0].fix() flow = self.control_volume.properties_in[0].flow_mol mw = self.control_volume.properties_in[0].mw Tin = self.control_volume.properties_in[0].temperature Pin = self.control_volume.properties_in[0].pressure Pr = self.ratioP[0] self.flow_coeff.value = value(flow * mw * sqrt(Tin / (1 - Pr**2)) / Pin) else: self.inlet.flow_mol.unfix() self.stodola_equation.activate() self.efficiency_correlation.activate() slvr = SolverFactory(solver) slvr.options = optarg self.display() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = slvr.solve(self, tee=slc.tee) init_log.info("Initialization Complete (Outlet Stage): {}".format( idaeslog.condition(res))) # reload original spec if calculate_cf: cf = value(self.flow_coeff) from_json(self, sd=istate, wts=sp) if calculate_cf: # cf was probably fixed, so will have to set the value agian here # if you ask for it to be calculated. self.flow_coeff = cf def calculate_scaling_factors(self): super().calculate_scaling_factors() for t, c in self.stodola_equation.items(): s = iscale.get_scaling_factor( self.control_volume.properties_in[t].flow_mol, default=1, warning=True)**2 iscale.constraint_scaling_transform(c, s)
class HelmTurbineInletStageData(HelmIsentropicTurbineData): CONFIG = HelmIsentropicTurbineData.CONFIG() def build(self): super().build() self.flow_coeff = Var( self.flowsheet().config.time, initialize=1.053 / 3600.0, doc="Turbine flow coefficient [kg*C^0.5/Pa/s]", ) self.blade_reaction = Var( initialize=0.9, doc="Blade reaction parameter" ) self.blade_velocity = Var( initialize=110.0, doc="Design blade velocity [m/s]" ) self.eff_nozzle = Var( initialize=0.95, bounds=(0.0, 1.0), doc="Nozzel efficiency (typically 0.90 to 0.95)", ) self.efficiency_mech = Var( initialize=1.0, doc="Turbine mechanical efficiency" ) self.eff_nozzle.fix() self.blade_reaction.fix() self.flow_coeff.fix() self.blade_velocity.fix() self.efficiency_mech.fix() self.efficiency_isentropic.unfix() self.ratioP[:] = 0.9 # make sure these have a number value self.deltaP[:] = 0 # to avoid an error later in initialize @self.Expression( self.flowsheet().config.time, doc="Entering steam velocity calculation [m/s]", ) def steam_entering_velocity(b, t): # 1.414 = 44.72/sqrt(1000) for SI if comparing to Liese (2014), # b.delta_enth_isentropic[t] = -(hin - hiesn), the mw converts # enthalpy to a mass basis return 1.414 * sqrt( (b.blade_reaction - 1)*b.delta_enth_isentropic[t]*self.eff_nozzle / b.control_volume.properties_in[t].mw ) @self.Expression(self.flowsheet().config.time, doc="Efficiency expression") def efficiency_isentropic_expr(b, t): Vr = b.blade_velocity / b.steam_entering_velocity[t] R = b.blade_reaction return 2*Vr*((sqrt(1 - R) - Vr) + sqrt((sqrt(1 - R) - Vr)**2 + R)) @self.Constraint( self.flowsheet().config.time, doc="Equation: Turbine inlet flow") def inlet_flow_constraint(b, t): # Some local vars to make the equation more readable g = b.control_volume.properties_in[t].heat_capacity_ratio mw = b.control_volume.properties_in[t].mw flow = b.control_volume.properties_in[t].flow_mol Tin = b.control_volume.properties_in[t].temperature cf = b.flow_coeff[t] Pin = b.control_volume.properties_in[t].pressure Pratio = b.ratioP[t] return flow ** 2 * mw ** 2 * Tin == ( cf ** 2 * Pin ** 2 * g / (g - 1) * (Pratio ** (2.0 / g) - Pratio ** ((g + 1) / g))) @self.Constraint(self.flowsheet().config.time, doc="Equation: Efficiency") def efficiency_correlation(b, t): return b.efficiency_isentropic[t] == b.efficiency_isentropic_expr[t] @self.Expression(self.flowsheet().config.time, doc="Thermodynamic power [J/s]") def power_thermo(b, t): return b.control_volume.work[t] @self.Expression(self.flowsheet().config.time, doc="Shaft power [J/s]") def power_shaft(b, t): return b.power_thermo[t] * b.efficiency_mech def initialize( self, state_args={}, outlvl=idaeslog.NOTSET, solver="ipopt", optarg={"tol": 1e-6, "max_iter": 30}, calculate_cf=False, ): """ Initialize the inlet turbine stage model. This deactivates the specialized constraints, then does the isentropic turbine initialization, then reactivates the constraints and solves. This initializtion uses a flow value guess, so some reasonable flow guess should be sepecified prior to initializtion. Args: state_args (dict): Initial state for property initialization outlvl (int): Amount of output (0 to 3) 0 is lowest solver (str): Solver to use for initialization optarg (dict): Solver arguments dictionary calculate_cf (bool): If True, use the flow and pressure ratio to calculate the flow coefficient. """ init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") # sp is what to save to make sure state after init is same as the start sp = StoreSpec.value_isfixed_isactive(only_fixed=True) istate = to_json(self, return_dict=True, wts=sp) # Setup for initializtion step 1 self.inlet_flow_constraint.deactivate() self.efficiency_correlation.deactivate() self.eff_nozzle.fix() self.blade_reaction.fix() self.flow_coeff.fix() self.blade_velocity.fix() self.inlet.fix() self.outlet.unfix() for t in self.flowsheet().config.time: self.efficiency_isentropic[t] = 0.9 super().initialize(outlvl=outlvl, solver=solver, optarg=optarg) # Free eff_isen and activate sepcial constarints self.inlet_flow_constraint.activate() self.efficiency_correlation.activate() if calculate_cf: self.ratioP.fix() self.flow_coeff.unfix() for t in self.flowsheet().config.time: g = self.control_volume.properties_in[t].heat_capacity_ratio mw = self.control_volume.properties_in[t].mw flow = self.control_volume.properties_in[t].flow_mol Tin = self.control_volume.properties_in[t].temperature Pin = self.control_volume.properties_in[t].pressure Pratio = self.ratioP[t] self.flow_coeff[t].value = value( flow * mw * sqrt( Tin/(g/(g - 1) *(Pratio**(2.0/g) - Pratio**((g + 1)/g))) )/Pin ) slvr = SolverFactory(solver) slvr.options = optarg with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = slvr.solve(self, tee=slc.tee) init_log.info("Initialization Complete: {}".format(idaeslog.condition(res))) # reload original spec if calculate_cf: cf = {} for t in self.flowsheet().config.time: cf[t] = value(self.flow_coeff[t]) from_json(self, sd=istate, wts=sp) if calculate_cf: # cf was probably fixed, so will have to set the value agian here # if you ask for it to be calculated. for t in self.flowsheet().config.time: self.flow_coeff[t] = cf[t] def calculate_scaling_factors(self): super().calculate_scaling_factors() for t, c in self.inlet_flow_constraint.items(): s = iscale.get_scaling_factor( self.control_volume.properties_in[t].flow_mol)**2 iscale.constraint_scaling_transform(c, s)