def report(self, index=(0), true_state=False, dof=False, ostream=None, prefix=""): """ Default report method for StateBlocks. Returns a Block report populated with either the display or state variables defined in the StateBlockData class. Args: index : tuple of Block indices indicating which point in time (and space if applicable) to report state at. true_state : whether to report the display variables (False default) or the actual state variables (True) dof : whether to show local degrees of freedom in the report (default=False) ostream : output stream to write report to prefix : string to append to the beginning of all output lines Returns: Printed output to ostream """ if ostream is None: ostream = sys.stdout # Get DoF and model stats if dof: dof_stat = degrees_of_freedom(self[index]) nv = number_variables(self[index]) nc = number_activated_constraints(self[index]) nb = number_activated_blocks(self[index]) # Create stream table if true_state: disp_dict = self[index].define_state_vars() else: disp_dict = self[index].define_display_vars() stream_attributes = {} for k in disp_dict: for i in disp_dict[k]: if i is None: stream_attributes[k] = disp_dict[k][i] else: stream_attributes[k + " " + i] = disp_dict[k][i] # Write output max_str_length = 84 tab = " " * 4 ostream.write("\n" + "=" * max_str_length + "\n") lead_str = f"{prefix}State : {self.name}" trail_str = f"Index: {index}" mid_str = " " * (max_str_length - len(lead_str) - len(trail_str)) ostream.write(lead_str + mid_str + trail_str) if dof: ostream.write("\n" + "=" * max_str_length + "\n") ostream.write(f"{prefix}{tab}Local Degrees of Freedom: {dof_stat}") ostream.write('\n') ostream.write(f"{prefix}{tab}Total Variables: {nv}{tab}" f"Activated Constraints: {nc}{tab}" f"Activated Blocks: {nb}") ostream.write("\n" + "-" * max_str_length + "\n") ostream.write(f"{prefix}{tab}State Report") if any(isinstance(v, _VarData) for k, v in stream_attributes.items()): ostream.write("\n" * 2) ostream.write(f"{prefix}{tab}Variables: \n\n") tabular_writer( ostream, prefix + tab, ((k, v) for k, v in stream_attributes.items() if isinstance(v, _VarData)), ("Value", "Fixed", "Bounds"), lambda k, v: ["{:#.5g}".format(value(v)), v.fixed, v.bounds]) if any( isinstance(v, _ExpressionData) for k, v in stream_attributes.items()): ostream.write("\n" * 2) ostream.write(f"{prefix}{tab}Expressions: \n\n") tabular_writer(ostream, prefix + tab, ((k, v) for k, v in stream_attributes.items() if isinstance(v, _ExpressionData)), ("Value", ), lambda k, v: ["{:#.5g}".format(value(v))]) ostream.write("\n" + "=" * max_str_length + "\n")
def report(self, time_point=0, dof=False, ostream=None, prefix=""): time_point = float(time_point) if ostream is None: ostream = sys.stdout # Get DoF and model stats if dof: dof_stat = degrees_of_freedom(self) nv = number_variables(self) nc = number_activated_constraints(self) nb = number_activated_blocks(self) # Get components to report in performance section performance = self._get_performance_contents(time_point=time_point) # Get stream table stream_table = self._get_stream_table_contents(time_point=time_point) # Set model type output if hasattr(self, "is_flowsheet") and self.is_flowsheet: model_type = "Flowsheet" else: model_type = "Unit" # Write output max_str_length = 84 tab = " " * 4 ostream.write("\n" + "=" * max_str_length + "\n") lead_str = f"{prefix}{model_type} : {self.name}" trail_str = f"Time: {time_point}" mid_str = " " * (max_str_length - len(lead_str) - len(trail_str)) ostream.write(lead_str + mid_str + trail_str) if dof: ostream.write("\n" + "=" * max_str_length + "\n") ostream.write(f"{prefix}{tab}Local Degrees of Freedom: {dof_stat}") ostream.write('\n') ostream.write(f"{prefix}{tab}Total Variables: {nv}{tab}" f"Activated Constraints: {nc}{tab}" f"Activated Blocks: {nb}") if performance is not None: ostream.write("\n" + "-" * max_str_length + "\n") ostream.write(f"{prefix}{tab}Unit Performance") ostream.write("\n" * 2) if "vars" in performance.keys() and len(performance["vars"]) > 0: ostream.write(f"{prefix}{tab}Variables: \n\n") tabular_writer( ostream, prefix + tab, ((k, v) for k, v in performance["vars"].items()), ("Value", "Fixed", "Bounds"), lambda k, v: ["{:#.5g}".format(value(v)), v.fixed, v.bounds]) if "exprs" in performance.keys() and len(performance["exprs"]) > 0: ostream.write("\n") ostream.write(f"{prefix}{tab}Expressions: \n\n") tabular_writer(ostream, prefix + tab, ((k, v) for k, v in performance["exprs"].items()), ("Value", ), lambda k, v: ["{:#.5g}".format(value(v))]) if ("params" in performance.keys() and len(performance["params"]) > 0): ostream.write("\n") ostream.write(f"{prefix}{tab}Parameters: \n\n") tabular_writer(ostream, prefix + tab, ((k, v) for k, v in performance["params"].items()), ("Value", "Mutable"), lambda k, v: [value(v), not v.is_constant()]) if stream_table is not None: ostream.write("\n" + "-" * max_str_length + "\n") ostream.write(f"{prefix}{tab}Stream Table") ostream.write('\n') ostream.write( textwrap.indent(stream_table_dataframe_to_string(stream_table), prefix + tab)) ostream.write("\n" + "=" * max_str_length + "\n")
def initialize(blk, state_args={}, state_vars_fixed=False, hold_state=False, outlvl=idaeslog.NOTSET, solver='ipopt', optarg={'tol': 1e-8}): """ Initialization routine for property package. Keyword Arguments: state_args : a dict of initial values for the state variables defined by the property package. outlvl : sets output level of initialization routine optarg : solver options dictionary object (default=None) state_vars_fixed: Flag to denote if state vars have already been fixed. - True - states have already been fixed by the control volume 1D. Control volume 0D does not fix the state vars, so will be False if this state block is used with 0D blocks. - False - states have not been fixed. The state block will deal with fixing/unfixing. solver : str indicating which solver to use during initialization (default = 'ipopt') hold_state : flag indicating whether the initialization routine should unfix any state variables fixed during initialization (default=False). - True - states variables are not unfixed, and a dict of returned containing flags for which states were fixed during initialization. - False - state variables are unfixed after initialization by calling the relase_state method Returns: If hold_states is True, returns a dict containing flags for which states were fixed during initialization. """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="properties") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="properties") init_log.info('Starting initialization') for k in blk.keys(): # Deactivate the constraints specific for outlet block i.e. # when defined state is False if blk[k].config.defined_state is False: try: blk[k].sum_mole_frac_out.deactivate() except AttributeError: pass # Fix state variables if not already fixed if state_vars_fixed is False: flag_dict = fix_state_vars(blk, state_args) # Confirm DoF for sanity for k in blk.keys(): if degrees_of_freedom(blk[k]) != 0: raise BurntToast("Degrees of freedom were not zero " "after trying to fix state variables. " "Something broke in the generic property " "package code - please inform the IDAES " "developers.") else: # When state vars are fixed, check that DoF is 0 for k in blk.keys(): if degrees_of_freedom(blk[k]) != 0: raise Exception("State vars fixed but degrees of " "freedom for state block is not zero " "during initialization.") # Set solver options if optarg is None: sopt = {'tol': 1e-8} else: sopt = optarg opt = SolverFactory('ipopt') opt.options = sopt # --------------------------------------------------------------------- # If present, initialize bubble and dew point calculations for k in blk.keys(): # Bubble temperature initialization if hasattr(blk[k], "_mole_frac_tbub"): # Use lowest component critical temperature as starting point # Starting high and moving down generally works better, # as it under-predicts next step due to exponential form of # Psat. # Subtract 1 to avoid potential singularities at Tcrit Tbub0 = min(blk[k]._params.temperature_crit_comp[j] for j in blk[k]._params.component_list) - 1 err = 1 counter = 0 # Newton solver with step limiter to prevent overshoot # Tolerance only needs to be ~1e-1 # Iteration limit of 30 while err > 1e-1 and counter < 30: f = value( sum(blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, Tbub0) * blk[k].mole_frac_comp[j] for j in blk[k]._params.component_list) - blk[k].pressure) df = value( sum(blk[k].mole_frac_comp[j] * blk[k]._params.config.pressure_sat_comp. pressure_sat_comp_dT(blk[k], j, Tbub0) for j in blk[k]._params.component_list)) # Limit temperature step to avoid excessive overshoot # Only limit positive steps due to non-linearity if f / df < -50: Tbub1 = Tbub0 + 50 else: Tbub1 = Tbub0 - f / df err = abs(Tbub1 - Tbub0) Tbub0 = Tbub1 counter += 1 blk[k].temperature_bubble.value = Tbub0 for j in blk[k]._params.component_list: blk[k]._mole_frac_tbub[j].value = value( blk[k].mole_frac_comp[j] * blk[k].pressure / blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, Tbub0)) # Bubble temperature initialization if hasattr(blk[k], "_mole_frac_tdew"): if hasattr(blk[k], "_mole_frac_tbub"): # If Tbub has been calculated above, use this as the # starting point Tdew0 = blk[k].temperature_bubble.value else: # Otherwise, use lowest component critical temperature as # starting point # Subtract 1 to avoid potential singularities at Tcrit Tdew0 = min(blk[k]._params.temperature_crit_comp[j] for j in blk[k]._params.component_list) - 1 err = 1 counter = 0 # Newton solver with step limiter to prevent overshoot # Tolerance only needs to be ~1e-1 # Iteration limit of 30 while err > 1e-1 and counter < 30: f = value(blk[k].pressure * sum( blk[k].mole_frac_comp[j] / blk[k]._params.config. pressure_sat_comp.pressure_sat_comp(blk[k], j, Tdew0) for j in blk[k]._params.component_list) - 1) df = -value(blk[k].pressure * sum(blk[k].mole_frac_comp[j] / blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, Tdew0)**2 * blk[k]._params.config.pressure_sat_comp. pressure_sat_comp_dT(blk[k], j, Tdew0) for j in blk[k]._params.component_list)) # Limit temperature step to avoid excessive overshoot if f / df < -50: Tdew1 = Tdew0 + 50 else: Tdew1 = Tdew0 - f / df err = abs(Tdew1 - Tdew0) Tdew0 = Tdew1 counter += 1 blk[k].temperature_dew.value = Tdew0 for j in blk[k]._params.component_list: blk[k]._mole_frac_tdew[j].value = value( blk[k].mole_frac_comp[j] * blk[k].pressure / blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, Tdew0)) # Bubble pressure initialization if hasattr(blk[k], "_mole_frac_pbub"): blk[k].pressure_bubble.value = value( sum(blk[k].mole_frac_comp[j] * blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, blk[k].temperature) for j in blk[k]._params.component_list)) for j in blk[k]._params.component_list: blk[k]._mole_frac_pbub[j].value = value( blk[k].mole_frac_comp[j] * blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, blk[k].temperature) / blk[k].pressure_bubble) # Dew pressure initialization if hasattr(blk[k], "_mole_frac_pdew"): blk[k].pressure_dew.value = value( sum(1 / (blk[k].mole_frac_comp[j] / blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, blk[k].temperature)) for j in blk[k]._params.component_list)) for j in blk[k]._params.component_list: blk[k]._mole_frac_pdew[j].value = value( blk[k].mole_frac_comp[j] * blk[k].pressure_bubble / blk[k]._params.config.pressure_sat_comp. pressure_sat_comp(blk[k], j, blk[k].temperature)) # Solve bubble and dew point constraints for c in blk[k].component_objects(Constraint): # Deactivate all constraints not associated wtih bubble and dew # points if c.local_name not in ("eq_pressure_dew", "eq_pressure_bubble", "eq_temperature_dew", "eq_temperature_bubble", "_sum_mole_frac_tbub", "_sum_mole_frac_tdew", "_sum_mole_frac_pbub", "_sum_mole_frac_pdew"): c.deactivate() # If StateBlock has active constraints (i.e. has bubble and/or dew # point calculations), solve the block to converge these n_cons = 0 for k in blk: n_cons += number_activated_constraints(blk[k]) if n_cons > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solve_indexed_blocks(opt, [blk], tee=slc.tee) init_log.info("Dew and bubble point initialization: {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # If StateBlock is using a smooth VLE, calculate _T1 and _Teq eq_check = 0 for k in blk.keys(): if hasattr(blk[k], "_t1"): blk[k]._t1.value = max(blk[k].temperature.value, blk[k].temperature_bubble.value) blk[k]._teq.value = min(blk[k]._t1.value, blk[k].temperature_dew.value) eq_check += 1 if eq_check > 0: init_log.info("Equilibrium temperature initialization completed.") # --------------------------------------------------------------------- # Initialize flow rates and compositions for k in blk.keys(): blk[k]._params.config.state_definition.state_initialization(blk[k]) if outlvl > 0: init_log.info("State variable initialization completed.") # --------------------------------------------------------------------- if (blk[k]._params.config.phase_equilibrium_formulation is not None and (not blk[k].config.defined_state or blk[k].always_flash)): blk[k]._params.config.phase_equilibrium_formulation \ .phase_equil_initialization(blk[k]) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solve_indexed_blocks(opt, [blk], tee=slc.tee) init_log.info("Phase equilibrium initialization: {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Initialize other properties for k in blk.keys(): for c in blk[k].component_objects(Constraint): # Activate all constraints except flagged do_not_initialize if c.local_name not in (blk[k]._params.config.state_definition. do_not_initialize): c.activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solve_indexed_blocks(opt, [blk], tee=slc.tee) init_log.info("Property initialization: {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Return constraints to initial state for k in blk.keys(): for c in blk[k].component_objects(Constraint): if c.local_name in (blk[k]._params.config.state_definition. do_not_initialize): c.activate() if state_vars_fixed is False: if hold_state is True: return flag_dict else: blk.release_state(flag_dict) init_log.info("Property package initialization: {}.".format( idaeslog.condition(res)))
def initialize( self, state_args={ "flow_mol_comp": { "N2": 1.0, "CO2": 1.0, "NO": 1.0, "O2": 1.0, "H2O": 1.0, "SO2": 1.0 }, "pressure": 1e5, "temperature": 495.0 }, hold_state=False, state_vars_fixed=False, outlvl=0, solver='ipopt', optarg={'tol': 1e-8}): """Initialisation routine for property package. Key values for the state_args dict: flow_mol_comp : value at which to initialize component flows (default=27.5e3 mol/s) pressure : value at which to initialize pressure (default=2.97e7 Pa) temperature : value at which to initialize temperature (default=866.5 K) Args: outlvl: sets logging level state_vars_fixed: Flag to denote state vars have been fixed. - True - states have been fixed by the control volume 1D. Control volume 0D does not fix the state vars, so will be False if this state block is used with 0D blocks. - False - states have not been fixed. The state block will deal with fixing/unfixing. optarg: solver options dictionary object (default=None) solver: str indicating whcih solver to use during initialization (default = 'ipopt') hold_state: flag indicating whether the initialization routine should unfix any state variables fixed during initialization (default=False). - True - states varaibles are not unfixed, and a dict of returned containing flags for which states were fixed during initialization. - False - state variables are unfixed after initialization by calling the relase_state method Returns: If hold_states is True, returns a dict containing flags for which states were fixed during initialization. """ init_log = idaeslog.getInitLogger(self.name, outlvl, tag="properties") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="properties") opt = SolverFactory(solver) opt.options = optarg if state_vars_fixed is False: flags = fix_state_vars(self, state_args) # Check when the state vars are fixed already result in dof 0 for b in self.values(): if degrees_of_freedom(b) != 0: raise Exception(f"{self.name} initializtion error: State vars " "fixed but degrees of freedom not equal to 0") # --------------------------------------------------------------------- # Solve 1st stage for k, b in self.items(): if b.is_property_constructed("pressure_sat"): b.pressure_sat.value = value( exp(b.vapor_pressure_coeff[1] + b.vapor_pressure_coeff[2] / b.temperature + b.vapor_pressure_coeff[3] * blk.temperature + b.vapor_pressure_coeff[4] * log(blk.temperature) + b.vapor_pressure_coeff[5].value * blk.temperature**2)) deactivate_list = [] if hasattr(b, 'enthalpy_correlation'): deactivate_list.append(b.enthalpy_correlation) if hasattr(b, "volumetric_flow_calculation"): deactivate_list.append(b.volumetric_flow_calculation) if hasattr(b, "entropy_correlation"): deactivate_list.append(b.entropy_correlation) for c in deactivate_list: c.deactivate() if number_activated_constraints(b) > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(b, tee=slc.tee) else: res = "skipped" init_log.info_high("Initialization Step 1 {}.".format( idaeslog.condition(res))) for c in deactivate_list: c.activate() if number_activated_constraints(b) > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(b, tee=slc.tee) else: res = "skipped" init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(res))) init_log.info('Initialisation Complete, {}.'.format( idaeslog.condition(res))) # --------------------------------------------------------------------- # If input block, return flags, else release state if state_vars_fixed is False: if hold_state is True: return flags else: self.release_state(flags)
def initialize( self, state_args=None, hold_state=False, state_vars_fixed=False, outlvl=idaeslog.NOTSET, solver=None, optarg=None, ): """Initialisation routine for property package. Key values for the state_args dict: flow_mol_comp : value at which to initialize component flows (default=27.5e3 mol/s) pressure : value at which to initialize pressure (default=2.97e7 Pa) temperature : value at which to initialize temperature (default=866.5 K) Args: outlvl: sets logging level state_vars_fixed: Flag to denote state vars have been fixed. - True - states have been fixed by the control volume 1D. Control volume 0D does not fix the state vars, so will be False if this state block is used with 0D blocks. - False - states have not been fixed. The state block will deal with fixing/unfixing. optarg: solver options dictionary object (default=None, use default solver options) solver: str indicating which solver to use during initialization (default = None, use default solver) hold_state: flag indicating whether the initialization routine should unfix any state variables fixed during initialization (default=False). - True - states varaibles are not unfixed, and a dict of returned containing flags for which states were fixed during initialization. - False - state variables are unfixed after initialization by calling the relase_state method Returns: If hold_states is True, returns a dict containing flags for which states were fixed during initialization. """ init_log = idaeslog.getInitLogger(self.name, outlvl, tag="properties") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="properties") # Create solver opt = get_solver(solver, optarg) if state_args is None: state_args = { "flow_mol_comp": { "N2": 1.0, "CO2": 1.0, "NO": 1.0, "O2": 1.0, "H2O": 1.0, "SO2": 1.0, }, "pressure": 1e5, "temperature": 495.0, } if state_vars_fixed is False: flags = fix_state_vars(self, state_args) # Check when the state vars are fixed already result in dof 0 for b in self.values(): if degrees_of_freedom(b) != 0: raise InitializationError( f"{self.name} State vars fixed but degrees of freedom not 0" ) # --------------------------------------------------------------------- # Solve 1st stage for k, b in self.items(): deactivate_list = [] if hasattr(b, "enthalpy_correlation"): deactivate_list.append(b.enthalpy_correlation) if hasattr(b, "volumetric_flow_calculation"): deactivate_list.append(b.volumetric_flow_calculation) if hasattr(b, "entropy_correlation"): deactivate_list.append(b.entropy_correlation) for c in deactivate_list: c.deactivate() if number_activated_constraints(b) > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(b, tee=slc.tee) else: res = "skipped" init_log.info_high("Initialization Step 1 {}.".format( idaeslog.condition(res))) for c in deactivate_list: c.activate() if number_activated_constraints(b) > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(b, tee=slc.tee) else: res = "skipped" init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(res))) if not res == "skipped" and not check_optimal_termination(res): raise InitializationError(f"Solve failed {res}.") init_log.info(f"Initialisation Complete, {idaeslog.condition(res)}.") # --------------------------------------------------------------------- # If input block, return flags, else release state if state_vars_fixed is False: if hold_state is True: return flags else: self.release_state(flags)
def initialize_pt(blk, state_args=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None): """ Initialization routine for pass-through unit models. Keyword Arguments: state_args : a dict of arguments to be passed to the property package(s) to provide an initial state for initialization (see documentation of the specific property package) (default = {}). outlvl : sets output level of initialization routine optarg : solver options dictionary object (default=None, use default solver options) solver : str indicating which solver to use during initialization (default = None, use default IDAES solver) Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") solver_obj = get_solver(solver, optarg) # Get initial guesses for inlet if none provided if state_args is None: state_args = {} state_dict = blk.properties[ blk.flowsheet().time.first()].define_port_members() for k in state_dict.keys(): if state_dict[k].is_indexed(): state_args[k] = {} for m in state_dict[k].keys(): state_args[k][m] = state_dict[k][m].value else: state_args[k] = state_dict[k].value # --------------------------------------------------------------------- # Initialize control volume block flags = blk.properties.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args, hold_state=True, ) init_log.info_high("Initialization Step 1 Complete.") # --------------------------------------------------------------------- # Solve unit if number_activated_constraints(blk) > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: results = solver_obj.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(results))) # --------------------------------------------------------------------- # Release Inlet state blk.properties.release_state(flags, outlvl) init_log.info("Initialization Complete: {}".format( idaeslog.condition(results))) if number_activated_constraints(blk) > 0 and not check_optimal_termination( results): raise InitializationError( f"{blk.name} failed to initialize successfully. Please check " f"the output logs for more information.")