class WaterStateBlockData(StateBlockData): """ General purpose StateBlock for Zero-Order unit models. """ def build(self): super().build() # Create state variables self.flow_mass_comp = Var(self.component_list, initialize=1, domain=PositiveReals, doc='Mass flowrate of each component', units=pyunits.kg / pyunits.s) # ------------------------------------------------------------------------- # Other properties def _conc_mass_comp(self): def rule_cmc(blk, j): return (blk.flow_mass_comp[j] / sum(self.flow_mass_comp[k] for k in self.component_list) * blk.dens_mass) self.conc_mass_comp = Expression(self.component_list, rule=rule_cmc) def _dens_mass(self): self.dens_mass = Param(initialize=self.params.dens_mass_default, units=pyunits.kg / pyunits.m**3, mutable=True, doc="Mass density of flow") def _flow_vol(self): self.flow_vol = Expression(expr=sum(self.flow_mass_comp[j] for j in self.component_list) / self.dens_mass) def _visc_d(self): self.visc_d = Param(initialize=self.params.visc_d_default, units=pyunits.kg / pyunits.m / pyunits.s, mutable=True, doc="Dynamic viscosity of solution") def get_material_flow_terms(blk, p, j): return blk.flow_mass_comp[j] def get_enthalpy_flow_terms(blk, p): raise NotImplementedError def get_material_density_terms(blk, p, j): return blk.conc_mass_comp[j] def get_energy_density_terms(blk, p): raise NotImplementedError def default_material_balance_type(self): return MaterialBalanceType.componentTotal def default_energy_balance_type(self): return EnergyBalanceType.none def define_state_vars(blk): return {"flow_mass_comp": blk.flow_mass_comp} def define_display_vars(blk): return { "Volumetric Flowrate": blk.flow_vol, "Mass Concentration": blk.conc_mass_comp } def get_material_flow_basis(blk): return MaterialFlowBasis.mass def calculate_scaling_factors(self): # Get default scale factors and do calculations from base classes super().calculate_scaling_factors() d_sf_Q = self.params.default_scaling_factor["flow_vol"] d_sf_c = self.params.default_scaling_factor["conc_mass_comp"] for j, v in self.flow_mass_comp.items(): if iscale.get_scaling_factor(v) is None: iscale.set_scaling_factor(v, d_sf_Q * d_sf_c) if self.is_property_constructed("flow_vol"): if iscale.get_scaling_factor(self.flow_vol) is None: iscale.set_scaling_factor(self.flow_vol, d_sf_Q) if self.is_property_constructed("conc_mass_comp"): for j, v in self.conc_mass_comp.items(): sf_c = iscale.get_scaling_factor(self.conc_mass_comp[j]) if sf_c is None: try: sf_c = self.params.default_scaling_factor[( "conc_mass_comp", j)] except KeyError: sf_c = d_sf_c iscale.set_scaling_factor(self.conc_mass_comp[j], sf_c)
class FlueGasStateBlockData(StateBlockData): """ This is an example of a property package for calculating the thermophysical properties of flue gas using the ideal gas assumption. """ def build(self): """ Callable method for Block construction """ super(FlueGasStateBlockData, self).build() comps = self.params.component_list # Add state variables self.flow_mol_comp = Var(comps, domain=Reals, initialize=1.0, bounds=(0, 1e6), doc='Component molar flowrate [mol/s]', units=pyunits.mol / pyunits.s) self.pressure = Var(domain=Reals, initialize=1.01325e5, bounds=(1, 5e7), doc='State pressure [Pa]', units=pyunits.Pa) self.temperature = Var(domain=Reals, initialize=500, bounds=(200, 1500), doc='State temperature [K]', units=pyunits.K) # Add expressions for some basic oft-used quantiies self.flow_mol = Expression(expr=sum(self.flow_mol_comp[j] for j in comps)) def rule_mole_frac(b, c): return b.flow_mol_comp[c] / b.flow_mol self.mole_frac_comp = Expression(comps, rule=rule_mole_frac, doc='mole fraction of component i') self.flow_mass = Expression(expr=sum( self.flow_mol_comp[j] * self.params.mw_comp[j] for j in comps), doc='total mass flow') def rule_mw_comp(b, j): return b.params.mw_comp[j] self.mw_comp = Expression(comps, rule=rule_mw_comp) def rule_mw(b): return sum(b.mw_comp[j] * b.mole_frac_comp[j] for j in comps) self.mw = Expression(rule=rule_mw) self.pressure_crit = Expression(expr=sum(self.params.pressure_crit[j] * self.mole_frac_comp[j] for j in comps)) self.temperature_crit = Expression(expr=sum( self.params.temperature_crit[j] * self.mole_frac_comp[j] for j in comps)) self.pressure_red = Expression(expr=self.pressure / self.pressure_crit) self.temperature_red = Expression(expr=self.temperature / self.temperature_crit) self.compress_fact = Expression(expr=1.0, doc='Vapor Compressibility Factor') def rule_dens_mol_phase(b, p): return b.pressure / b.compress_fact / \ constants.Constants.gas_constant / b.temperature self.dens_mol_phase = Expression(self.params.phase_list, rule=rule_dens_mol_phase, doc='Molar Density') self.flow_vol = Expression(doc='Volumetric Flowrate', expr=self.flow_mol / self.dens_mol_phase["Vap"]) def _heat_cap_calc(self): # heat capacity J/mol-K self.cp_mol = Var(initialize=1000, doc='heat capacity [J/mol-K]', units=pyunits.J / pyunits.mol / pyunits.K) def rule_cp_phase(b, p): # This property module only has one phase return self.cp_mol self.cp_mol_phase = Expression(self.params.phase_list, rule=rule_cp_phase) try: ft = sum(self.flow_mol_comp[j] for j in self.params.component_list) t = pyunits.convert(self.temperature, to_units=pyunits.kK) self.heat_cap_correlation = Constraint( expr=(self.cp_mol * ft == sum(self.flow_mol_comp[j] * (self.params.cp_mol_ig_comp_coeff_A[j] + self.params.cp_mol_ig_comp_coeff_B[j] * t + self.params.cp_mol_ig_comp_coeff_C[j] * t**2 + self.params.cp_mol_ig_comp_coeff_D[j] * t**3 + self.params.cp_mol_ig_comp_coeff_E[j] / t**2) for j in self.params.component_list))) except AttributeError: self.del_component(self.cp_mol) self.del_component(self.heat_cap_correlation) def _enthalpy_calc(self): self.enth_mol = Var(doc='Specific Enthalpy [J/mol]', units=pyunits.J / pyunits.mol) def rule_enth_phase(b, p): # This property module only has one phase return self.enth_mol self.enth_mol_phase = Expression(self.params.phase_list, rule=rule_enth_phase) def enthalpy_correlation(b): ft = sum(self.flow_mol_comp[j] for j in self.params.component_list) t = pyunits.convert(self.temperature, to_units=pyunits.kK) return self.enth_mol * ft == sum( self.flow_mol_comp[j] * pyunits.convert( self.params.cp_mol_ig_comp_coeff_A[j] * t + self.params.cp_mol_ig_comp_coeff_B[j] * t**2 / 2 + self.params.cp_mol_ig_comp_coeff_C[j] * t**3 / 3 + self.params.cp_mol_ig_comp_coeff_D[j] * t**4 / 4 - self.params.cp_mol_ig_comp_coeff_E[j] / t + self.params.cp_mol_ig_comp_coeff_F[j], to_units=pyunits.J / pyunits.mol) for j in self.params.component_list) # NOTE: the H term (from the Shomate Equation) is not # included here so that the reference state enthalpy is the # enthalpy of formation (not 0). try: self.enthalpy_correlation = Constraint(rule=enthalpy_correlation) except AttributeError: self.del_component(self.enth_mol_phase) self.del_component(self.enth_mol) self.del_component(self.enthalpy_correlation) def _entropy_calc(self): self.entr_mol = Var(doc='Specific Entropy [J/mol/K]', units=pyunits.J / pyunits.mol / pyunits.K) # Specific Entropy def rule_entr_phase(b, p): # This property module only has one phase return self.entr_mol self.entr_mol_phase = Expression(self.params.phase_list, rule=rule_entr_phase) def entropy_correlation(b): ft = sum(self.flow_mol_comp[j] for j in self.params.component_list) t = pyunits.convert(self.temperature, to_units=pyunits.kK) n = self.flow_mol_comp x = self.mole_frac_comp p = self.pressure r_gas = constants.Constants.gas_constant return (self.entr_mol + r_gas * log(p/1e5)) * ft == \ sum(n[j] * ( self.params.cp_mol_ig_comp_coeff_A[j]*log(t) + self.params.cp_mol_ig_comp_coeff_B[j]*t + self.params.cp_mol_ig_comp_coeff_C[j]*t**2 / 2 + self.params.cp_mol_ig_comp_coeff_D[j]*t**3 / 3 - self.params.cp_mol_ig_comp_coeff_E[j]/t**2 / 2 + self.params.cp_mol_ig_comp_coeff_G[j] + r_gas * log(x[j])) for j in self.params.component_list) try: self.entropy_correlation = Constraint(rule=entropy_correlation) except AttributeError: self.del_component(self.entr_mol_phase) self.del_component(self.entropy_correlation) def _therm_cond(self): comps = self.params.component_list self.therm_cond_comp = Var(comps, initialize=0.05, doc='thermal conductivity J/m-K-s', units=pyunits.J / pyunits.m / pyunits.K / pyunits.s) self.therm_cond = Var( initialize=0.05, doc='thermal conductivity of gas mixture J/m-K-s', units=pyunits.J / pyunits.m / pyunits.K / pyunits.s) self.visc_d_comp = Var(comps, initialize=2e-5, doc='dynamic viscocity of pure gas species', units=pyunits.kg / pyunits.m / pyunits.s) self.visc_d = Var(initialize=2e-5, doc='viscosity of gas mixture kg/m-s', units=pyunits.kg / pyunits.m / pyunits.s) try: def rule_therm_cond(b, c): t = pyunits.convert(b.temperature, to_units=pyunits.kK) return b.therm_cond_comp[c] == ( ((b.params.cp_mol_ig_comp_coeff_A[c] + b.params.cp_mol_ig_comp_coeff_B[c] * t + b.params.cp_mol_ig_comp_coeff_C[c] * t**2 + b.params.cp_mol_ig_comp_coeff_D[c] * t**3 + b.params.cp_mol_ig_comp_coeff_E[c] / t**2) / b.params.mw_comp[c]) + 1.25 * (constants.Constants.gas_constant / b.params.mw_comp[c]) ) * b.visc_d_comp[c] self.therm_cond_con = Constraint(comps, rule=rule_therm_cond) def rule_theta(b, c): return b.temperature / b.params.ep_Kappa[c] self.theta = Expression(comps, rule=rule_theta) def rule_omega(b, c): return (1.5794145 + 0.00635771 * b.theta[c] - 0.7314 * log(b.theta[c]) + 0.2417357 * log(b.theta[c])**2 - 0.0347045 * log(b.theta[c])**3) self.omega = Expression(comps, rule=rule_omega) # Pure gas viscocity - from Chapman-Enskog theory def rule_visc_d(b, c): return (pyunits.convert(b.visc_d_comp[c], to_units=pyunits.g / pyunits.cm / pyunits.s) * b.params.sigma[c]**2 * b.omega[c] == b.params.ce_param * sqrt( pyunits.convert(b.params.mw_comp[c], to_units=pyunits.g / pyunits.mol) * b.temperature)) self.visc_d_con = Constraint(comps, rule=rule_visc_d) # section to calculate viscosity of gas mixture def rule_phi(b, i, j): return (1 / 2.8284 * (1 + (b.params.mw_comp[i] / b.params.mw_comp[j]))**(-0.5) * (1 + sqrt(b.visc_d_comp[i] / b.visc_d_comp[j]) * (b.params.mw_comp[j] / b.params.mw_comp[i])**0.25)**2) self.phi_ij = Expression(comps, comps, rule=rule_phi) # viscosity of Gas mixture kg/m-s def rule_visc_d_mix(b): return b.visc_d == sum( (b.mole_frac_comp[i] * b.visc_d_comp[i]) / sum(b.mole_frac_comp[j] * b.phi_ij[i, j] for j in comps) for i in comps) self.vis_d_mix_con = Constraint(rule=rule_visc_d_mix) # thermal conductivity of gas mixture in kg/m-s def rule_therm_mix(b): return b.therm_cond == sum( (b.mole_frac_comp[i] * b.therm_cond_comp[i]) / sum(b.mole_frac_comp[j] * b.phi_ij[i, j] for j in comps) for i in comps) self.therm_mix_con = Constraint(rule=rule_therm_mix) except AttributeError: self.del_component(self.therm_cond_comp) self.del_component(self.therm_cond) self.del_component(self.visc_d_comp) self.del_component(self.visc_d) self.del_component(self.omega) self.del_component(self.theta) self.del_component(self.phi_ij) self.del_component(self.sigma) self.del_component(self.ep_Kappa) self.del_component(self.therm_cond_con) self.del_component(self.theta_con) self.del_component(self.omega_con) self.del_component(self.visc_d_con) self.del_component(self.phi_con) def default_material_balance_type(self): return MaterialBalanceType.componentTotal def default_energy_balance_type(self): return EnergyBalanceType.enthalpyTotal def get_material_flow_terms(self, p, j): return self.flow_mol_comp[j] def get_material_flow_basis(self): return MaterialFlowBasis.molar def get_enthalpy_flow_terms(self, p): if not self.is_property_constructed("enthalpy_flow_terms"): try: def rule_enthalpy_flow_terms(b, p): return self.enth_mol_phase[p] * self.flow_mol self.enthalpy_flow_terms = Expression( self.params.phase_list, rule=rule_enthalpy_flow_terms) except AttributeError: self.del_component(self.enthalpy_flow_terms) return self.enthalpy_flow_terms[p] def get_material_density_terms(self, p, j): return self.dens_mol_phase[p] def get_energy_density_terms(self, p): if not self.is_property_constructed("energy_density_terms"): try: def rule_energy_density_terms(b, p): return self.enth_mol_phase[p] * \ self.dens_mol_phase[p] - self.pressure self.energy_density_terms = Expression( self.params.phase_list, rule=rule_energy_density_terms) except AttributeError: self.del_component(self.energy_density_terms) return self.energy_density_terms[p] def define_state_vars(self): return { "flow_mol_comp": self.flow_mol_comp, "temperature": self.temperature, "pressure": self.pressure } def model_check(self): """ Model checks for property block """ # Check temperature bounds for v in self.compoent_object_data(Var, descend_into=True): if value(v) < v.lb: _log.error(f"{v} is below lower bound in {self.name}") if value(v) > v.ub: _log.error(f"{v} is above upper bound in {self.name}") def calculate_scaling_factors(self): super().calculate_scaling_factors() # Get some scale factors that are frequently used to calculate others sf_flow = iscale.get_scaling_factor(self.flow_mol) sf_mol_fraction = {} comps = self.params.component_list for i in comps: sf_mol_fraction[i] = iscale.get_scaling_factor( self.mole_frac_comp[i]) # calculate flow_mol_comp scale factors for i, c in self.flow_mol_comp.items(): iscale.set_scaling_factor(c, sf_flow * sf_mol_fraction[i]) if self.is_property_constructed("energy_density_terms"): for i, c in self.energy_density_terms.items(): sf1 = iscale.get_scaling_factor(self.enth_mol_phase[i]) sf2 = iscale.get_scaling_factor(self.dens_mol_phase[i]) iscale.set_scaling_factor(c, sf1 * sf2) if self.is_property_constructed("enthalpy_flow_terms"): for i, c in self.enthalpy_flow_terms.items(): sf1 = iscale.get_scaling_factor(self.enth_mol_phase[i]) sf2 = iscale.get_scaling_factor(self.flow_mol) iscale.set_scaling_factor(c, sf1 * sf2) if self.is_property_constructed("heat_cap_correlation"): iscale.constraint_scaling_transform( self.heat_cap_correlation, iscale.get_scaling_factor(self.cp_mol) * iscale.get_scaling_factor(self.flow_mol)) if self.is_property_constructed("enthalpy_correlation"): for p, c in self.enthalpy_correlation.items(): iscale.constraint_scaling_transform( c, iscale.get_scaling_factor(self.enth_mol) * iscale.get_scaling_factor(self.flow_mol)) if self.is_property_constructed("entropy_correlation"): iscale.constraint_scaling_transform( self.entropy_correlation, iscale.get_scaling_factor(self.entr_mol) * iscale.get_scaling_factor(self.flow_mol)) if self.is_property_constructed("vapor_pressure_correlation"): iscale.constraint_scaling_transform( self.vapor_pressure_correlation, log(iscale.get_scaling_factor(self.pressure_sat)) * iscale.get_scaling_factor(self.flow_mol)) if self.is_property_constructed("therm_cond_con"): for i, c in self.therm_cond_con.items(): iscale.constraint_scaling_transform( c, iscale.get_scaling_factor(self.therm_cond_comp[i])) if self.is_property_constructed("therm_mix_con"): iscale.constraint_scaling_transform( self.therm_mix_con, iscale.get_scaling_factor(self.therm_cond)) if self.is_property_constructed("visc_d_con"): for i, c in self.visc_d_con.items(): iscale.constraint_scaling_transform( c, iscale.get_scaling_factor(self.visc_d_comp[i])) if self.is_property_constructed("visc_d_mix_con"): iscale.constraint_scaling_transform( self.visc_d_mix_con, iscale.get_scaling_factor(self.visc_d))