def initialize(blk, liquid_state_args=None, vapor_state_args=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None): ''' Initialization routine for solvent condenser unit model. Keyword Arguments: liquid_state_args : a dict of arguments to be passed to the liquid property package to provide an initial state for initialization (see documentation of the specific property package) (default = none). vapor_state_args : a dict of arguments to be passed to the vapor property package to provide an initial state for initialization (see documentation of the specific property package) (default = none). 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 ''' if optarg is None: optarg = {} # Check DOF if degrees_of_freedom(blk) != 0: raise InitializationError( f"{blk.name} degrees of freedom were not 0 at the beginning " f"of initialization. DoF = {degrees_of_freedom(blk)}") # Set solver options init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") solverobj = get_solver(solver, optarg) # --------------------------------------------------------------------- # Initialize liquid phase control volume block flags = blk.vapor_phase.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=vapor_state_args, hold_state=True ) init_log.info_high('Initialization Step 1 Complete.') # --------------------------------------------------------------------- # Initialize liquid phase state block if liquid_state_args is None: t_init = blk.flowsheet().time.first() liquid_state_args = {} liq_state_vars = blk.liquid_phase[t_init].define_state_vars() vap_state = blk.vapor_phase.properties_out[t_init] # Check for unindexed state variables for sv in liq_state_vars: if "flow" in sv: # Flow varaible, assume 10% condensation if "phase_comp" in sv: # Flow is indexed by phase and component liquid_state_args[sv] = {} for p, j in liq_state_vars[sv]: if j in vap_state.component_list: liquid_state_args[sv][p, j] = 0.1*value( getattr(vap_state, sv)[p, j]) else: liquid_state_args[sv][p, j] = 1e-8 elif "comp" in sv: # Flow is indexed by component liquid_state_args[sv] = {} for j in liq_state_vars[sv]: if j in vap_state.component_list: liquid_state_args[sv][j] = 0.1*value( getattr(vap_state, sv)[j]) else: liquid_state_args[sv][j] = 1e-8 elif "phase" in sv: # Flow is indexed by phase liquid_state_args[sv] = {} for p in liq_state_vars[sv]: liquid_state_args[sv][p] = 0.1*value( getattr(vap_state, sv)["Vap"]) else: liquid_state_args[sv] = 0.1*value( getattr(vap_state, sv)) elif "mole_frac" in sv: liquid_state_args[sv] = {} if "phase" in sv: # Variable is indexed by phase and component for p, j in liq_state_vars[sv].keys(): if j in vap_state.component_list: liquid_state_args[sv][p, j] = value( vap_state.fug_phase_comp["Vap", j] / vap_state.pressure) else: liquid_state_args[sv][p, j] = 1e-8 else: for j in liq_state_vars[sv].keys(): if j in vap_state.component_list: liquid_state_args[sv][j] = value( vap_state.fug_phase_comp["Vap", j] / vap_state.pressure) else: liquid_state_args[sv][j] = 1e-8 else: liquid_state_args[sv] = value( getattr(vap_state, sv)) blk.liquid_phase.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=liquid_state_args, hold_state=False ) init_log.info_high('Initialization Step 2 Complete.') # --------------------------------------------------------------------- # Solve unit model with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: results = solverobj.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 3 {}.".format(idaeslog.condition(results)) ) # --------------------------------------------------------------------- # Release Inlet state blk.vapor_phase.release_state(flags, outlvl) # TODO : This fails in the current model # if not check_optimal_termination(results): # raise InitializationError( # f"{blk.name} failed to initialize successfully. Please check " # f"the output logs for more information.") init_log.info('Initialization Complete: {}' .format(idaeslog.condition(results)))
StateBlockData, PhysicalParameterBlock from idaes.core.util.initialization import fix_state_vars, revert_state_vars from idaes.core.util.initialization import solve_indexed_blocks from idaes.core.util.model_statistics import degrees_of_freedom from idaes.core.base.phases import LiquidPhase from idaes.core.base.components import Component from idaes.core.solvers import get_solver import idaes.logger as idaeslog # Some more inforation about this module _author_ = "Naresh Susarla" __version__ = "0.0.1" # Logger _log = logging.getLogger(__name__) init_log = idaeslog.getInitLogger(__name__) # **** Requires Temperature in K # Ref: (2015) Chang et al, Energy Procedia 69, 779 - 789 # Specific Heat Capacity as a function of Temperature, J/kg/K # def specific_heat_cp(model): # return model.specific_heat_cp # == 1443 + 0.172 * model.temperature # # Thermal Conductivity as a function of Temperature, W/m/K # def thermal_conductivity(model): # return model.thermal_conductivity # == 0.443 + 0.00019 * model.temperature # # ****************************************************************************
def initialize( self, state_args_1=None, state_args_2=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None, duty=None, ): """ Heat exchanger initialization method. Args: state_args_1 : a dict of arguments to be passed to the property initialization for the hot side (see documentation of the specific property package) (default = {}). state_args_2 : a dict of arguments to be passed to the property initialization for the cold side (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 solver) duty : an initial guess for the amount of heat transfered. This should be a tuple in the form (value, units), (default = (1000 J/s)) Returns: None """ # Set solver options init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") hot_side = getattr(self, self.config.hot_side_name) cold_side = getattr(self, self.config.cold_side_name) # Create solver opt = get_solver(solver, optarg) flags1 = hot_side.initialize(outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_1) init_log.info_high("Initialization Step 1a (hot side) Complete.") flags2 = cold_side.initialize(outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_2) init_log.info_high("Initialization Step 1b (cold side) Complete.") # --------------------------------------------------------------------- # Solve unit without heat transfer equation # if costing block exists, deactivate if hasattr(self, "costing"): self.costing.deactivate() self.heat_transfer_equation.deactivate() # Get side 1 and side 2 heat units, and convert duty as needed s1_units = hot_side.heat.get_units() s2_units = cold_side.heat.get_units() if duty is None: # Assume 1000 J/s and check for unitless properties if s1_units is None and s2_units is None: # Backwards compatability for unitless properties s1_duty = -1000 s2_duty = 1000 else: s1_duty = pyunits.convert_value(-1000, from_units=pyunits.W, to_units=s1_units) s2_duty = pyunits.convert_value(1000, from_units=pyunits.W, to_units=s2_units) else: # Duty provided with explicit units s1_duty = -pyunits.convert_value( duty[0], from_units=duty[1], to_units=s1_units) s2_duty = pyunits.convert_value(duty[0], from_units=duty[1], to_units=s2_units) cold_side.heat.fix(s2_duty) for i in hot_side.heat: hot_side.heat[i].value = s1_duty with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(self, tee=slc.tee) init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(res))) cold_side.heat.unfix() self.heat_transfer_equation.activate() # --------------------------------------------------------------------- # Solve unit with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(self, tee=slc.tee) init_log.info_high("Initialization Step 3 {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Release Inlet state hot_side.release_state(flags1, outlvl=outlvl) cold_side.release_state(flags2, outlvl=outlvl) init_log.info("Initialization Completed, {}".format( idaeslog.condition(res))) # if costing block exists, activate and initialize if hasattr(self, "costing"): self.costing.activate() costing.initialize(self.costing)
def initialize(self, outlvl=idaeslog.NOTSET, solver=None, flow_iterate=2, optarg=None, copy_disconneted_flow=True, copy_disconneted_pressure=True, calculate_outlet_cf=False, calculate_inlet_cf=False): """ Initialize Args: outlvl: logging level default is NOTSET, which inherits from the parent logger solver: the NL solver flow_iterate: If not calculating flow coefficients, this is the number of times to update the flow and repeat initialization (1 to 5 where 1 does not update the flow guess) optarg: solver arguments, default is None copy_disconneted_flow: Copy the flow through the disconnected stages default is True copy_disconneted_pressure: Copy the pressure through the disconnected stages default is True calculate_outlet_cf: Use the flow initial flow guess to calculate the outlet stage flow coefficient, default is False, calculate_inlet_cf: Use the inlet stage ratioP to calculate the flow coefficent for the inlet stage default is False Returns: None """ # Setup loggers init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") # Store initial model specs, restored at the end of initializtion, so # the problem is not altered. This can restore fixed/free vars, # active/inactive constraints, and fixed variable values. sp = StoreSpec.value_isfixed_isactive(only_fixed=True) istate = to_json(self, return_dict=True, wts=sp) # Assume the flow into the turbine is a reasonable guess for # initializtion flow_guess = self.inlet_split.inlet.flow_mol[0].value for it_count in range(flow_iterate): self.inlet_split.initialize(outlvl=outlvl, solver=solver, optarg=optarg) # Initialize valves for i in self.inlet_stage_idx: u = self.throttle_valve[i] propagate_state( u.inlet, getattr(self.inlet_split, "outlet_{}".format(i))) u.initialize(outlvl=outlvl, solver=solver, optarg=optarg) # Initialize turbine for i in self.inlet_stage_idx: u = self.inlet_stage[i] propagate_state(u.inlet, self.throttle_valve[i].outlet) u.initialize(outlvl=outlvl, solver=solver, optarg=optarg, calculate_cf=calculate_inlet_cf) # Initialize Mixer self.inlet_mix.use_minimum_inlet_pressure_constraint() for i in self.inlet_stage_idx: propagate_state( getattr(self.inlet_mix, "inlet_{}".format(i)), self.inlet_stage[i].outlet, ) getattr(self.inlet_mix, "inlet_{}".format(i)).fix() self.inlet_mix.initialize(outlvl=outlvl, solver=solver, optarg=optarg) for i in self.inlet_stage_idx: getattr(self.inlet_mix, "inlet_{}".format(i)).unfix() self.inlet_mix.use_equal_pressure_constraint() prev_port = self.inlet_mix.outlet prev_port = self._init_section( self.hp_stages, self.hp_split, self.config.hp_disconnect, prev_port, outlvl, solver, optarg, copy_disconneted_flow=copy_disconneted_flow, copy_disconneted_pressure=copy_disconneted_pressure, ) if len(self.hp_stages) in self.config.hp_disconnect: self.config.ip_disconnect.append(0) prev_port = self._init_section( self.ip_stages, self.ip_split, self.config.ip_disconnect, prev_port, outlvl, solver, optarg, copy_disconneted_flow=copy_disconneted_flow, copy_disconneted_pressure=copy_disconneted_pressure, ) if len(self.ip_stages) in self.config.ip_disconnect: self.config.lp_disconnect.append(0) prev_port = self._init_section( self.lp_stages, self.lp_split, self.config.lp_disconnect, prev_port, outlvl, solver, optarg, copy_disconneted_flow=copy_disconneted_flow, copy_disconneted_pressure=copy_disconneted_pressure, ) propagate_state(self.outlet_stage.inlet, prev_port) self.outlet_stage.initialize(outlvl=outlvl, solver=solver, optarg=optarg, calculate_cf=calculate_outlet_cf) if calculate_outlet_cf: break if it_count < flow_iterate - 1: for t in self.inlet_split.inlet.flow_mol: self.inlet_split.inlet.flow_mol[t].value = \ self.outlet_stage.inlet.flow_mol[t].value for s in self.hp_split.values(): for i, o in enumerate(s.outlet_list): if i == 0: continue o = getattr(s, o) self.inlet_split.inlet.flow_mol[t].value += \ o.flow_mol[t].value for s in self.ip_split.values(): for i, o in enumerate(s.outlet_list): if i == 0: continue o = getattr(s, o) self.inlet_split.inlet.flow_mol[t].value += \ o.flow_mol[t].value for s in self.lp_split.values(): for i, o in enumerate(s.outlet_list): if i == 0: continue o = getattr(s, o) self.inlet_split.inlet.flow_mol[t].value += \ o.flow_mol[t].value if calculate_inlet_cf: # cf was probably fixed, so will have to set the value agian here # if you ask for it to be calculated. icf = {} for i in self.inlet_stage: for t in self.inlet_stage[i].flow_coeff: icf[i, t] = pyo.value(self.inlet_stage[i].flow_coeff[t]) if calculate_outlet_cf: ocf = pyo.value(self.outlet_stage.flow_coeff) from_json(self, sd=istate, wts=sp) if calculate_inlet_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.inlet_stage[i].flow_coeff: for i in self.inlet_stage: self.inlet_stage[i].flow_coeff[t] = icf[i, t] if calculate_outlet_cf: self.outlet_stage.flow_coeff = ocf
def initialize(self, outlvl=idaeslog.NOTSET, optarg=None, solver=None): """ Initialization routine for splitter Keyword Arguments: 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 solver) 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="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") # Create solver opt = get_solver(solver, optarg) # 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) # check for fixed outlet flows and use them to calculate fixed split # fractions for t in self.flowsheet().time: for o in self.outlet_list: if self.outlet_blocks[o][t].flow_mol.fixed: self.split_fraction[t, o].fix( value(self.mixed_state[t] / self.outlet_blocks[o][t].flow_mol)) # fix or unfix split fractions so n - 1 are fixed for t in self.flowsheet().time: # see how many split fractions are fixed n = sum(1 for o in self.outlet_list if self.split_fraction[t, o].fixed) # if number of outlets - 1 we're good if n == len(self.outlet_list) - 1: continue # if too mant are fixed un fix the first, generally assume that is # the main flow, and is the calculated split fraction if n == len(self.outlet_list): self.split_fraction[t, self.outlet_list[0]].unfix() # if not enough fixed, start fixing from the back until there are # are enough for o in reversed(self.outlet_list): if not self.split_fraction[t, o].fixed: self.split_fraction[t, o].fix() n += 1 if n == len(self.outlet_list) - 1: break # This model is really simple so it should easily solve without much # effort to initialize self.inlet.fix() for o, p in self.outlet_ports.items(): p.unfix() assert degrees_of_freedom(self) == 0 with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(self, tee=slc.tee) init_log.info("Initialization Complete: {}".format( idaeslog.condition(res))) from_json(self, sd=istate, wts=sp)
def initialize(blk, state_args=None, hold_state=False, state_vars_fixed=False, outlvl=idaeslog.NOTSET, solver="ipopt", optarg={"tol": 1e-8}): """ Initialization routine for property package. Keyword Arguments: state_args : Dictionary with initial guesses for the state vars chosen. Note that if this method is triggered through the control volume, and if initial guesses were not provided at the unit model level, the control volume passes the inlet values as initial guess. Keys for the state_args dictionary are: flow_mol, temperature, pressure and mole_frac_comp outlvl : sets output level of initialization routine optarg : solver options dictionary object (default=None) 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 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(blk.name, outlvl, tag="properties") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="properties") init_log.info_high('Starting initialization') # Deactivate the constraints specific for non-inlet blocks i.e. # when defined state is False for k in blk.keys(): if blk[k].config.defined_state is False: blk[k].sum_component_eqn.deactivate() # Fix state variables if not already fixed if state_vars_fixed is False: flags = fix_state_vars(blk, state_args) else: # Check when the state vars are fixed already result in dof 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.") # --------------------------------------------------------------------- # Initialise values for k in blk.keys(): if hasattr(blk[k], "mw_eqn"): calculate_variable_from_constraint(blk[k].mw, blk[k].mw_eqn) if hasattr(blk[k], "ideal_gas"): calculate_variable_from_constraint(blk[k].dens_mol, blk[k].ideal_gas) if hasattr(blk[k], "dens_mass_basis"): calculate_variable_from_constraint(blk[k].dens_mass, blk[k].dens_mass_basis) if hasattr(blk[k], "mixture_heat_capacity_eqn"): calculate_variable_from_constraint( blk[k].cp_mol, blk[k].mixture_heat_capacity_eqn) if hasattr(blk[k], "cp_mass_basis"): calculate_variable_from_constraint(blk[k].cp_mass, blk[k].cp_mass_basis) if hasattr(blk[k], "visc_d_constraint"): calculate_variable_from_constraint(blk[k].visc_d, blk[k].visc_d_constraint) if hasattr(blk[k], "therm_cond_constraint"): calculate_variable_from_constraint( blk[k].therm_cond, blk[k].therm_cond_constraint) if hasattr(blk[k], "mixture_enthalpy_eqn"): calculate_variable_from_constraint(blk[k].enth_mol, blk[k].mixture_enthalpy_eqn) for j in blk[k]._params.component_list: if hasattr(blk[k], "comp_conc_eqn"): calculate_variable_from_constraint(blk[k].dens_mol_comp[j], blk[k].comp_conc_eqn[j]) if hasattr(blk[k], "diffusion_comp_constraint"): calculate_variable_from_constraint( blk[k].diffusion_comp[j], blk[k].diffusion_comp_constraint[j]) if hasattr(blk[k], "cp_shomate_eqn"): calculate_variable_from_constraint( blk[k].cp_mol_comp[j], blk[k].cp_shomate_eqn[j]) if hasattr(blk[k], "enthalpy_shomate_eqn"): calculate_variable_from_constraint( blk[k].enth_mol_comp[j], blk[k].enthalpy_shomate_eqn[j]) # Solve property block if non-empty free_vars = 0 for k in blk.keys(): free_vars += number_unfixed_variables_in_activated_equalities( blk[k]) if free_vars > 0: # Create solver opt = get_solver(solver, optarg) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solve_indexed_blocks(opt, [blk], tee=slc.tee) else: res = "" init_log.info_high("Initialization complete {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- if state_vars_fixed is False: if hold_state is True: return flags else: blk.release_state(flags)
def copy_non_time_indexed_values( fs_tgt, fs_src, copy_fixed=True, outlvl=idaeslog.NOTSET, ): """ Function to set the values of all variables that are not (implicitly or explicitly) indexed by time to their values in a different flowsheet. Args: fs_tgt : Flowsheet into which values will be copied. fs_src : Flowsheet from which values will be copied. copy_fixed : Bool marking whether or not to copy over fixed variables in the target flowsheet. outlvl : Outlevel for the IDAES logger. Returns: None """ time_tgt = fs_tgt.time var_visited = set() for var_tgt in fs_tgt.component_objects(Var, descend_into=False): if id(var_tgt) in var_visited: continue var_visited.add(id(var_tgt)) if is_explicitly_indexed_by(var_tgt, time_tgt): continue var_src = fs_src.find_component(var_tgt.local_name) # ^ this find_component is fine because var_tgt is a Var not VarData # and its local_name is used. Assumes that there are no other decimal # indices in between fs_src and var_src if var_src is None: # Log a warning msg = ('Warning copying values: ' + varname + ' does not exist in source block ' + fs_src.name) init_log = idaeslog.getInitLogger(__name__, outlvl) init_log.warning(msg) continue for index in var_tgt: if not copy_fixed and var_tgt[index].fixed: continue var_tgt[index].set_value(var_src.value) blk_visited = set() for blk_tgt in fs_tgt.component_objects(Block): if id(blk_tgt) in blk_visited: continue blk_visited.add(id(blk_tgt)) if (is_in_block_indexed_by(blk_tgt, time_tgt) or is_explicitly_indexed_by(blk_tgt, time_tgt)): continue # block is not even implicitly indexed by time for b_index in blk_tgt: var_visited = set() for var_tgt in blk_tgt[b_index].component_objects( Var, descend_into=False): if id(var_tgt) in var_visited: continue var_visited.add(id(var_tgt)) if is_explicitly_indexed_by(var_tgt, time_tgt): continue # can't used find_component(local_name) here because I might # have decimal indices try: local_parent = fs_src for r in path_from_block(var_tgt, fs_tgt): local_parent = getattr(local_parent, r[0])[r[1]] except AttributeError: # log warning msg = ('Warning copying values: ' + r[0] + ' does not exist in source' + local_parent.name) init_log = idaeslog.getInitLogger(__name__, outlvl) init_log.warning(msg) continue except KeyError: msg = ('Warning copying values: ' + str(r[1]) + ' is not a valid index for' + getattr(local_parent, r[0]).name) init_log = idaeslog.getInitLogger(__name__, outlvl) init_log.warning(msg) continue var_src = getattr(local_parent, var_tgt.local_name) for index in var_tgt: if not copy_fixed and var_tgt[index].fixed: continue var_tgt[index].set_value(var_src[index].value)
def initialize( blk, state_args_in=None, state_args_out=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None, ): """ This method calls the initialization method of the state blocks. Keyword Arguments: state_args_in : a dict of arguments to be passed to the inlet property package (to provide an initial state for initialization (see documentation of the specific property package) (default = None). state_args_out : a dict of arguments to be passed to the outlet property package (to provide an initial state for initialization (see documentation of the specific property package) (default = None). 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 solver) Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") # Create solver opt = get_solver(solver, optarg) # --------------------------------------------------------------------- # Initialize state block flags = blk.properties_in.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_in, hold_state=True, ) blk.properties_out_turbine.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_out, ) blk.properties_out_pipeline.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_out, ) if degrees_of_freedom(blk) != 0: raise AssertionError(f"Initialization of {blk.name} is unsuccessful. " f"Degrees of freedom was not zero. Please provide " f"sufficient number of constraints linking the state " f"variables between the two state blocks.") with idaeslog.solver_log(init_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info("Initialization Complete {}." .format(idaeslog.condition(res))) blk.properties_in.release_state(flags=flags, outlvl=outlvl)
def initialize_by_time_element(fs, time, **kwargs): """ Function to initialize Flowsheet fs element-by-element along ContinuousSet time. Assumes sufficient initialization/correct degrees of freedom such that the first finite element can be solved immediately and each subsequent finite element can be solved by fixing differential and derivative variables at the initial time point of that finite element. Args: fs : Flowsheet to initialize time : Set whose elements will be solved for individually solver : Pyomo solver object initialized with user's desired options outlvl : IDAES logger outlvl ignore_dof : Bool. If True, checks for square problems will be skipped. Returns: None """ if not isinstance(fs, FlowsheetBlock): raise TypeError('First arg must be a FlowsheetBlock') if not isinstance(time, ContinuousSet): raise TypeError('Second arg must be a ContinuousSet') if time.get_discretization_info() == {}: raise ValueError('ContinuousSet must be discretized') scheme = time.get_discretization_info()['scheme'] fep_list = time.get_finite_elements() nfe = time.get_discretization_info()['nfe'] if scheme == 'LAGRANGE-RADAU': ncp = time.get_discretization_info()['ncp'] elif scheme == 'LAGRANGE-LEGENDRE': msg = 'Initialization does not support collocation with Legendre roots' raise NotImplementedError(msg) elif scheme == 'BACKWARD Difference': ncp = 1 elif scheme == 'FORWARD Difference': ncp = 1 msg = 'Forward initialization (explicit Euler) has not yet been implemented' raise NotImplementedError(msg) elif scheme == 'CENTRAL Difference': msg = 'Initialization does not support central finite difference' raise NotImplementedError(msg) else: msg = 'Unrecognized discretization scheme. ' 'Has the model been discretized along the provided ContinuousSet?' raise ValueError(msg) # Disallow Central/Legendre discretizations. # Neither of these seem to be square by default for multi-finite element # initial value problems. # Create logger objects outlvl = kwargs.pop('outlvl', idaeslog.NOTSET) init_log = idaeslog.getInitLogger(__name__, level=outlvl) solver_log = idaeslog.getSolveLogger(__name__, level=outlvl) ignore_dof = kwargs.pop('ignore_dof', False) solver = kwargs.pop('solver', get_solver()) fix_diff_only = kwargs.pop('fix_diff_only', True) # This option makes the assumption that the only variables that # link constraints to previous points in time (which must be fixed) # are the derivatives and differential variables. Not true if a controller # is being present, but should be a good assumption otherwise, and is # significantly faster than searching each constraint for time-linking # variables. if not ignore_dof: if degrees_of_freedom(fs) != 0: msg = ('Original model has nonzero degrees of freedom. This was ' 'unexpected. Use keyword arg igore_dof=True to skip this ' 'check.') init_log.error(msg) raise ValueError('Nonzero degrees of freedom.') # Get dict telling which constraints/blocks are already inactive: # dict: id(compdata) -> bool (is active?) was_originally_active = get_activity_dict(fs) # Deactivate flowsheet except at t0, solve to ensure consistency # of initial conditions. non_initial_time = [t for t in time] non_initial_time.remove(time.first()) deactivated = deactivate_model_at(fs, time, non_initial_time, outlvl=idaeslog.ERROR) if not ignore_dof: if degrees_of_freedom(fs) != 0: msg = ('Model has nonzero degrees of freedom at initial conditions.' ' This was unexpected. Use keyword arg igore_dof=True to skip' ' this check.') init_log.error(msg) raise ValueError('Nonzero degrees of freedom.') init_log.info( 'Model is inactive except at t=0. Solving for consistent initial conditions.') with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc: results = solver.solve(fs, tee=slc.tee) if check_optimal_termination(results): init_log.info('Successfully solved for consistent initial conditions') else: init_log.error('Failed to solve for consistent initial conditions') raise ValueError('Solver failed in initialization') deactivated[time.first()] = deactivate_model_at(fs, time, time.first(), outlvl=idaeslog.ERROR)[time.first()] # Here, deactivate non-time-indexed components. Do this after solve # for initial conditions in case these were used to specify initial # conditions con_unindexed_by_time = deactivate_constraints_unindexed_by(fs, time) var_unindexed_by_time = fix_vars_unindexed_by(fs, time) # Now model is completely inactive # For each timestep, we need to # 1. Activate model at points we're solving for # 2. Fix initial conditions (differential variables at previous timestep) # of finite element # 3. Solve the (now) square system # 4. Revert the model to its prior state # This will make use of the following dictionaries mapping # time points -> time derivatives and time-differential variables derivs_at_time = get_derivatives_at(fs, time, [t for t in time]) dvars_at_time = {t: [d.parent_component().get_state_var()[d.index()] for d in derivs_at_time[t]] for t in time} # Perform a solve for 1 -> nfe; i is the index of the finite element init_log.info('Flowsheet has been deactivated. Beginning element-wise initialization') for i in range(1, nfe+1): t_prev = time[(i-1)*ncp+1] # Non-initial time points in the finite element: fe = [time[k] for k in range((i-1)*ncp+2, i*ncp+2)] init_log.info(f'Entering step {i}/{nfe} of initialization') # Activate components of model that were active in the presumably # square original system for t in fe: for comp in deactivated[t]: if was_originally_active[id(comp)]: comp.activate() # Get lists of derivative and differential variables # at initial time point of finite element init_deriv_list = derivs_at_time[t_prev] init_dvar_list = dvars_at_time[t_prev] # Variables that were originally fixed fixed_vars = [] if fix_diff_only: for drv in init_deriv_list: # Cannot fix variables with value None. # Any variable with value None was not solved for # (either stale or not included in previous solve) # and we don't want to fix it. if not drv.fixed: fixed_vars.append(drv) if not drv.value is None: drv.fix() for dv in init_dvar_list: if not dv.fixed: fixed_vars.append(dv) if not dv.value is None: dv.fix() else: for con in fs.component_data_objects(Constraint, active=True): for var in identify_variables(con.expr, include_fixed=False): t_idx = get_implicit_index_of_set(var, time) if t_idx is None: continue if t_idx <= t_prev: fixed_vars.append(var) var.fix() # Initialize finite element from its initial conditions for t in fe: copy_values_at_time(fs, fs, t, t_prev, copy_fixed=False, outlvl=idaeslog.ERROR) # Log that we are solving finite element {i} init_log.info(f'Solving finite element {i}') if not ignore_dof: if degrees_of_freedom(fs) != 0: msg = (f'Model has nonzero degrees of freedom at finite element' ' {i}. This was unexpected. ' 'Use keyword arg igore_dof=True to skip this check.') init_log.error(msg) raise ValueError('Nonzero degrees of freedom') with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc: results = solver.solve(fs, tee=slc.tee) if check_optimal_termination(results): init_log.info(f'Successfully solved finite element {i}') else: init_log.error(f'Failed to solve finite element {i}') raise ValueError('Failure in initialization solve') # Deactivate components that may have been activated for t in fe: for comp in deactivated[t]: comp.deactivate() # Unfix variables that have been fixed for var in fixed_vars: var.unfix() # Log that initialization step {i} has been finished init_log.info(f'Initialization step {i} complete') # Reactivate components of the model that were originally active for t in time: for comp in deactivated[t]: if was_originally_active[id(comp)]: comp.activate() for con in con_unindexed_by_time: con.activate() for var in var_unindexed_by_time: var.unfix() # Logger message that initialization is finished init_log.info('Initialization completed. Model has been reactivated')
def initialize(blk, state_args=None, outlvl=idaeslog.NOTSET, solver='ipopt', optarg={'tol': 1e-6}): ''' Downcomer initialization routine. Keyword Arguments: state_args : a dict of arguments to be passed to the property package(s) for the control_volume of the model to provide an initial state for initialization (see documentation of the specific property package) (default = None). outlvl : sets output level of initialisation routine optarg : solver options dictionary object (default={'tol': 1e-6}) solver : str indicating whcih solver to use during initialization (default = 'ipopt') Returns: None ''' init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") opt = SolverFactory(solver) opt.options = optarg init_log.info_low("Starting initialization...") flags = blk.control_volume.initialize( outlvl=outlvl+1, optarg=optarg, solver=solver, state_args=state_args, ) init_log.info_high("Initialization Step 1 Complete.") # make sure 0 DoF if degrees_of_freedom(blk) != 0: raise ConfigurationError( "Incorrect degrees of freedom when initializing {}: dof = {}".format( blk.name, degrees_of_freedom(blk))) # Fix outlet pressure for t in blk.flowsheet().config.time: blk.control_volume.properties_out[t].pressure.fix( value(blk.control_volume.properties_in[t].pressure) ) blk.pressure_change_total_eqn.deactivate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 2 {}.".format(idaeslog.condition(res)) ) # Unfix outlet enthalpy and pressure for t in blk.flowsheet().config.time: blk.control_volume.properties_out[t].pressure.unfix() blk.pressure_change_total_eqn.activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 3 {}.".format(idaeslog.condition(res)) ) blk.control_volume.release_state(flags, outlvl+1) init_log.info("Initialization Complete.")
def initialize(blk, state_args_feedwater={}, state_args_water_steam={}, outlvl=idaeslog.NOTSET, solver=None, optarg={}): ''' Drum initialization routine. Keyword Arguments: state_args : a dict of arguments to be passed to the property package(s) for the control_volume of the model to provide an initial state for initialization (see documentation of the specific property package) (default = None). outlvl : sets output level of initialisation routine * 0 = no output (default) * 1 = return solver state for each step in routine * 2 = return solver state for each step in subroutines * 3 = include solver output infomation (tee=True) optarg : solver options dictionary object (default={}) solver : str indicating whcih solver to use during initialization (default = None, use default solver) Returns: None ''' init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Create solver opt = get_solver(solver, optarg) init_log.info_low("Starting initialization...") # fix FeedWater Inlet flags_fw = fix_state_vars(blk.mixer.FeedWater_state, state_args_feedwater) # expecting 2 DOF due to pressure driven constraint if degrees_of_freedom(blk) != 2: raise Exception(degrees_of_freedom(blk)) blk.flash.initialize(state_args_water_steam=state_args_water_steam, outlvl=outlvl, optarg=optarg, solver=solver) init_log.info("Initialization Step 1 Complete.") blk.mixer.SaturatedWater.flow_mol[:].fix( blk.flash.liq_outlet.flow_mol[0].value) blk.mixer.SaturatedWater.pressure[:].fix( blk.flash.liq_outlet.pressure[0].value) blk.mixer.SaturatedWater.enth_mol[:].fix( blk.flash.liq_outlet.enth_mol[0].value) blk.mixer.initialize(outlvl=outlvl, optarg=optarg, solver=solver) init_log.info("Initialization Step 2 Complete.") blk.control_volume.initialize(outlvl=outlvl, optarg=optarg, solver=solver, hold_state=False) init_log.info("Initialization Step 3 Complete.") # fix flash Inlet flags_steam = fix_state_vars(blk.flash.mixed_state, state_args_water_steam) # unfix inlets (connected with arc) blk.mixer.SaturatedWater.flow_mol[:].unfix() blk.mixer.SaturatedWater.enth_mol[:].unfix() blk.mixer.SaturatedWater.pressure[:].unfix() blk.mixer.FeedWater.pressure[0].unfix() # solve model with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 4 {}.".format( idaeslog.condition(res))) revert_state_vars(blk.mixer.FeedWater_state, flags_fw) revert_state_vars(blk.flash.mixed_state, flags_steam) init_log.info("Initialization Complete.")
def initialize(m, fileinput=None, outlvl=idaeslog.NOTSET): """ Initialize a mode from create_model(), set model inputs before initializing. Args: m (ConcreteModel): A Pyomo model from create_model() fileinput (str|None): File to load initialized model state from. If a file is supplied skip initialization routine. If None, initialize. Returns: solver: A Pyomo solver object, that can be used to solve the model. """ init_log = idaeslog.getInitLogger(m.name, outlvl, tag="flowsheet") solve_log = idaeslog.getSolveLogger(m.name, outlvl, tag="flowsheet") iscale.calculate_scaling_factors(m) solver = pyo.SolverFactory("ipopt") solver.options = { "tol": 1e-7, "linear_solver": "ma27", "max_iter": 40, } if fileinput is not None: init_log.info("Loading initial values from file: {}".format(fileinput)) ms.from_json(m, fname=fileinput) return solver init_log.info("Starting initialization") ############################################################################ # Initialize turbine # ############################################################################ # Extraction rates are calculated from the feedwater heater models, so to # initialize the turbine fix some initial guesses. They get unfixed after # solving the turbine m.fs.turb.outlet_stage.control_volume.properties_out[:].pressure.fix(3500) m.fs.turb.lp_split[11].split_fraction[:, "outlet_2"].fix(0.04403) m.fs.turb.lp_split[10].split_fraction[:, "outlet_2"].fix(0.04025) m.fs.turb.lp_split[8].split_fraction[:, "outlet_2"].fix(0.04362) m.fs.turb.lp_split[4].split_fraction[:, "outlet_2"].fix(0.08025) m.fs.turb.ip_split[10].split_fraction[:, "outlet_2"].fix(0.045) m.fs.turb.ip_split[10].split_fraction[:, "outlet_3"].fix(0.04) m.fs.turb.ip_split[5].split_fraction[:, "outlet_2"].fix(0.05557) m.fs.turb.hp_split[7].split_fraction[:, "outlet_2"].fix(0.09741) m.fs.turb.hp_split[4].split_fraction[:, "outlet_2"].fix(0.0740) # Put in a rough initial guess for the IP section inlet, since it is # disconnected from the HP section for the reheater. ip1_pin = 5.35e6 ip1_hin = iapws95.htpx(T=866, P=ip1_pin) ip1_fin = pyo.value(m.fs.turb.inlet_split.inlet.flow_mol[0]) m.fs.turb.ip_stages[1].inlet.enth_mol[:].value = ip1_hin m.fs.turb.ip_stages[1].inlet.flow_mol[:].value = ip1_fin m.fs.turb.ip_stages[1].inlet.pressure[:].value = ip1_pin # initialize turbine assert degrees_of_freedom(m.fs.turb) == 0 m.fs.turb.initialize(outlvl=outlvl, optarg=solver.options) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solver.solve(m.fs.turb, tee=slc.tee) init_log.info("Full turbine solve complete: {}".format( idaeslog.condition(res))) # The turbine outlet pressure is determined by the condenser once hooked up m.fs.turb.outlet_stage.control_volume.properties_out[:].pressure.unfix() # Extraction rates are calculated once the feedwater heater models are # added so unfix the splits. m.fs.turb.lp_split[11].split_fraction[:, "outlet_2"].unfix() m.fs.turb.lp_split[10].split_fraction[:, "outlet_2"].unfix() m.fs.turb.lp_split[8].split_fraction[:, "outlet_2"].unfix() m.fs.turb.lp_split[4].split_fraction[:, "outlet_2"].unfix() m.fs.turb.ip_split[10].split_fraction[:, "outlet_3"].unfix() m.fs.turb.ip_split[5].split_fraction[:, "outlet_2"].unfix() m.fs.turb.hp_split[7].split_fraction[:, "outlet_2"].unfix() m.fs.turb.hp_split[4].split_fraction[:, "outlet_2"].unfix() # Initialize the boiler feed pump turbine. _set_port(m.fs.bfpt.inlet, m.fs.turb.ip_split[10].outlet_3) m.fs.bfpt.control_volume.properties_out[:].pressure.fix(10000) m.fs.bfpt.efficiency_isentropic.fix() m.fs.bfpt.initialize(outlvl=idaeslog.DEBUG, optarg=solver.options) m.fs.bfpt.control_volume.properties_out[:].pressure.unfix() m.fs.bfpt.efficiency_isentropic.unfix() ############################################################################ # Condenser section # ############################################################################ # initialize condenser mixer _set_port(m.fs.condenser_mix.main, m.fs.turb.outlet_stage.outlet) _set_port(m.fs.condenser_mix.bfpt, m.fs.bfpt.outlet) m.fs.condenser_mix.initialize(outlvl=outlvl, optarg=solver.options) # initialize condenser hx _set_port(m.fs.condenser.inlet_1, m.fs.condenser_mix.outlet_tpx) _set_port(m.fs.condenser.outlet_2, m.fs.condenser.inlet_2) # This still has the outlet vapor fraction fixed at 0, so if that isn't # true for the initial pressure guess this could fail to initialize m.fs.condenser.inlet_1.fix() m.fs.condenser.inlet_1.pressure.unfix() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solver.solve(m.fs.condenser, tee=slc.tee) init_log.info("Condenser initialized: {}".format(idaeslog.condition(res))) init_log.info_high("Init condenser pressure: {}".format( m.fs.condenser.shell.properties_in[0].pressure.value)) m.fs.condenser.inlet_1.unfix() # initialize hotwell _set_port(m.fs.hotwell.condensate, m.fs.condenser.outlet_1_ph) m.fs.hotwell.initialize(outlvl=outlvl, optarg=solver.options) m.fs.hotwell.condensate.unfix() # initialize condensate pump _set_port(m.fs.cond_pump.inlet, m.fs.hotwell.outlet) m.fs.cond_pump.initialize(outlvl=outlvl, optarg=solver.options) ############################################################################ # Low-pressure FWH section # ############################################################################ # fwh1 m.fs.fwh1.drain_mix.drain.flow_mol[:] = 1000 m.fs.fwh1.drain_mix.drain.pressure[:] = 1e5 m.fs.fwh1.drain_mix.drain.enth_mol[:] = 6117 _set_port(m.fs.fwh1.condense.inlet_2, m.fs.cond_pump.outlet) _set_port(m.fs.fwh1.drain_mix.steam, m.fs.turb.lp_split[11].outlet_2) m.fs.fwh1.initialize(outlvl=outlvl, optarg=solver.options) # initialize fwh1 drain pump _set_port(m.fs.fwh1_pump.inlet, m.fs.fwh1.condense.outlet_1) m.fs.fwh1_pump.initialize(outlvl=5, optarg=solver.options) # initialize mixer to add fwh1 drain to feedwater _set_port(m.fs.fwh1_return.feedwater, m.fs.fwh1.condense.outlet_2) _set_port(m.fs.fwh1_return.fwh1_drain, m.fs.fwh1.condense.outlet_1) m.fs.fwh1_return.initialize(outlvl=outlvl, optarg=solver.options) m.fs.fwh1_return.feedwater.unfix() m.fs.fwh1_return.fwh1_drain.unfix() # fwh2 m.fs.fwh2.drain_mix.drain.flow_mol[:] = 100 m.fs.fwh2.drain_mix.drain.pressure[:] = 1.5e5 m.fs.fwh2.drain_mix.drain.enth_mol[:] = 7000 _set_port(m.fs.fwh2.cooling.inlet_2, m.fs.fwh1_return.outlet) _set_port(m.fs.fwh2.desuperheat.inlet_1, m.fs.turb.lp_split[10].outlet_2) m.fs.fwh2.initialize(outlvl=outlvl, optarg=solver.options) # fwh3 m.fs.fwh3.drain_mix.drain.flow_mol[:] = 100 m.fs.fwh3.drain_mix.drain.pressure[:] = 2.5e5 m.fs.fwh3.drain_mix.drain.enth_mol[:] = 8000 _set_port(m.fs.fwh3.cooling.inlet_2, m.fs.fwh2.desuperheat.outlet_2) _set_port(m.fs.fwh3.desuperheat.inlet_1, m.fs.turb.lp_split[8].outlet_2) m.fs.fwh3.initialize(outlvl=outlvl, optarg=solver.options) # fwh4 _set_port(m.fs.fwh4.cooling.inlet_2, m.fs.fwh3.desuperheat.outlet_2) _set_port(m.fs.fwh4.desuperheat.inlet_1, m.fs.turb.lp_split[4].outlet_2) m.fs.fwh4.initialize(outlvl=outlvl, optarg=solver.options) ############################################################################ # boiler feed pump and deaerator # ############################################################################ _set_port(m.fs.fwh5_da.feedwater, m.fs.fwh4.desuperheat.outlet_2) _set_port(m.fs.fwh5_da.steam, m.fs.turb.ip_split[10].outlet_2) m.fs.fwh5_da.drain.flow_mol[:] = 2000 m.fs.fwh5_da.drain.pressure[:] = 3e6 m.fs.fwh5_da.drain.enth_mol[:] = 9000 m.fs.fwh5_da.initialize(outlvl=outlvl, optarg=solver.options) _set_port(m.fs.bfp.inlet, m.fs.fwh5_da.outlet) m.fs.bfp.control_volume.properties_out[:].pressure.fix() m.fs.bfp.initialize(outlvl=outlvl, optarg=solver.options) m.fs.bfp.control_volume.properties_out[:].pressure.unfix() ############################################################################ # High-pressure feedwater heaters # ############################################################################ # fwh6 m.fs.fwh6.drain_mix.drain.flow_mol[:] = 1000 m.fs.fwh6.drain_mix.drain.pressure[:] = 1e7 m.fs.fwh6.drain_mix.drain.enth_mol[:] = 9500 _set_port(m.fs.fwh6.cooling.inlet_2, m.fs.bfp.outlet) _set_port(m.fs.fwh6.desuperheat.inlet_1, m.fs.turb.ip_split[5].outlet_2) m.fs.fwh6.initialize(outlvl=outlvl, optarg=solver.options) # fwh7 m.fs.fwh7.drain_mix.drain.flow_mol[:] = 2000 m.fs.fwh7.drain_mix.drain.pressure[:] = 1e7 m.fs.fwh7.drain_mix.drain.enth_mol[:] = 9500 _set_port(m.fs.fwh7.cooling.inlet_2, m.fs.fwh6.desuperheat.outlet_2) _set_port(m.fs.fwh7.desuperheat.inlet_1, m.fs.turb.hp_split[7].outlet_2) m.fs.fwh7.initialize(outlvl=outlvl, optarg=solver.options) # fwh8 _set_port(m.fs.fwh8.cooling.inlet_2, m.fs.fwh7.desuperheat.outlet_2) _set_port(m.fs.fwh8.desuperheat.inlet_1, m.fs.turb.hp_split[4].outlet_2) m.fs.fwh8.initialize(outlvl=outlvl, optarg=solver.options) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solver.solve(m, tee=slc.tee) init_log.info("Initialization Complete: {}".format( idaeslog.condition(res))) return solver
def initialize(self, state_args=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None): """ Initialize the inlet compressor stage model. This deactivates the specialized constraints, then does the isentropic compressor initialization, then reactivates the constraints and solves. Args: state_args (dict): Initial state for property initialization outlvl: Initialization logger level 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") # Create solver opt = get_solver(solver, optarg) unfix_ratioP = {} for t in self.flowsheet().config.time: # if there is not a good guess for efficiency or outlet pressure # provide something reasonable. eff = self.efficiency_isentropic[t] eff.fix( eff.value if pyo.value(eff) > 0.3 and pyo.value( eff) < 1.0 else 0.85) # check for alternate pressure specs if self.outlet.pressure[t].fixed: self.ratioP[t] = pyo.value( self.outlet.pressure[t] / self.inlet.pressure[t]) elif self.control_volume.deltaP[t].fixed: self.ratioP[t] = pyo.value( (self.control_volume.deltaP[t] + self.inlet.pressure[t] ) / self.inlet.pressure[t]) elif self.ratioP[t].fixed: self.outlet.pressure[t] = pyo.value( self.ratioP[t] * self.inlet.pressure[t]) else: if value(self.ratioP[t]) is None or value( self.ratioP[t]) < 1.01: self.ratioP[t].fix(1.5) else: self.ratioP[t].fix() unfix_ratioP[t] = True # set list of constraints constraint_list = [ "impeller_work_coeff_eqn", "polytropic_head_coeff_vaned_diffuser_eqn", "polytropic_head_coeff_eqn", "polytropic_efficiency_eqn", "Ma_con", "rspeed_con", "eff_p_v_cons", "polytropic_correlation", "delta_enth_polytropic_con", "mass_flow_coeff_eqn", "psi_3_eqn", "psi_s_eqn"] # deactivate unit model level constraints for c in constraint_list: self.component(c).deactivate() super().initialize( state_args=state_args, outlvl=outlvl, solver=solver, optarg=optarg) self.efficiency_isentropic.unfix() for t in self.flowsheet().config.time: if unfix_ratioP.get(t, False): self.ratioP[t].unfix() # Activate special constraints for c in constraint_list: getattr(self, c).activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(self, tee=slc.tee) init_log.info( "Initialization Complete: {}".format(idaeslog.condition(res)) )
def initialize( self, state_args={}, outlvl=idaeslog.NOTSET, solver="ipopt", optarg={ "tol": 1e-6, "max_iter": 30 }, ): """ 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 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 sp = StoreSpec.value_isfixed_isactive(only_fixed=True) istate = to_json(self, return_dict=True, wts=sp) # Deactivate special constraints self.stodola_equation.deactivate() self.efficiency_correlation.deactivate() self.deltaP.unfix() self.ratioP.unfix() # Fix turbine parameters + eff_isen self.eff_dry.fix() self.design_exhaust_flow_vol.fix() self.flow_coeff.fix() # fix inlet and free outlet for t in self.flowsheet().config.time: for k, v in self.inlet.vars.items(): v[t].fix() for k, v in self.outlet.vars.items(): v[t].unfix() # If there isn't a good guess for efficiency or outlet pressure # provide something reasonable. eff = self.efficiency_isentropic[t] eff.fix( eff.value if value(eff) > 0.3 and value(eff) < 1.0 else 0.8) # for outlet pressure try outlet pressure, pressure ratio, delta P, # then if none of those look reasonable use a pressure ratio of 0.8 # to calculate outlet pressure Pout = self.outlet.pressure[t] Pin = self.inlet.pressure[t] prdp = value((self.deltaP[t] - Pin) / Pin) if value(Pout / Pin) > 0.95 or value(Pout / Pin) < 0.003: if value(self.ratioP[t]) < 0.9 and value( self.ratioP[t]) > 0.01: Pout.fix(value(Pin * self.ratioP)) elif prdp < 0.9 and prdp > 0.01: Pout.fix(value(prdp * Pin)) else: Pout.fix(value(Pin * 0.3)) else: Pout.fix() self.deltaP[:] = value(Pout - Pin) self.ratioP[:] = value(Pout / Pin) for t in self.flowsheet().config.time: self.properties_isentropic[t].pressure.value = value( self.outlet.pressure[t]) self.properties_isentropic[t].flow_mol.value = value( self.inlet.flow_mol[t]) self.properties_isentropic[t].enth_mol.value = value( self.inlet.enth_mol[t] * 0.95) self.outlet.flow_mol[t].value = value(self.inlet.flow_mol[t]) self.outlet.enth_mol[t].value = value(self.inlet.enth_mol[t] * 0.95) # Make sure the initialization problem has no degrees of freedom # This shouldn't happen here unless there is a bug in this dof = degrees_of_freedom(self) try: assert dof == 0 except AssertionError: init_log.error("Degrees of freedom not 0, ({})".format(dof)) raise 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] cf = self.flow_coeff self.inlet.flow_mol.fix( value(cf * Pin * sqrt(1 - Pr**2) / mw / sqrt(Tin - 273.15))) # one bad thing about reusing this is that the log messages aren't # really compatible with being nested inside another initialization super().initialize(state_args=state_args, outlvl=outlvl, solver=solver, optarg=optarg) # Free eff_isen and activate sepcial constarints self.efficiency_isentropic.unfix() self.outlet.pressure.fix() self.inlet.flow_mol.unfix() self.stodola_equation.activate() self.efficiency_correlation.activate() 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 (Outlet Stage): {}".format( idaeslog.condition(res))) # reload original spec from_json(self, sd=istate, wts=sp)
def initialize( self, outlvl=idaeslog.NOTSET, solver="ipopt", optarg={"tol": 1e-6, "max_iter": 35}, copy_disconneted_flow=True, ): """ Initialize """ # 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 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) ni = self.config.num_parallel_inlet_stages flow_guess = self.inlet_split.inlet.flow_mol[0].value def init_section(stages, splits, disconnects, prev_port): if 0 in splits: _set_port(splits[0].inlet, prev_port) splits[0].initialize(outlvl=outlvl, solver=solver, optarg=optarg) prev_port = splits[0].outlet_1 for i in stages: if i - 1 not in disconnects: _set_port(stages[i].inlet, prev_port) else: if copy_disconneted_flow: for t in stages[i].stages[i].inlet.flow_mol[t]: stages[i].inlet.flow_mol[t] = prev_port.flow_mol[t] stages[i].initialize(outlvl=outlvl, solver=solver, optarg=optarg) prev_port = stages[i].outlet if i in splits: _set_port(splits[i].inlet, prev_port) splits[i].initialize(outlvl=outlvl, solver=solver, optarg=optarg) prev_port = splits[i].outlet_1 return prev_port for k in [1, 2]: # Initialize Splitter # Fix n - 1 split fractions self.inlet_split.split_fraction[0, "outlet_1"].value = 1.0 / ni for i in self.inlet_stage_idx: if i == 1: # fix rest of splits at leaving first one free continue self.inlet_split.split_fraction[0, "outlet_{}".format(i)].fix(1.0 / ni) # fix inlet and free outlet self.inlet_split.inlet.fix() for i in self.inlet_stage_idx: ol = getattr(self.inlet_split, "outlet_{}".format(i)) ol.unfix() self.inlet_split.initialize(outlvl=outlvl, solver=solver, optarg=optarg) # free split fractions for i in self.inlet_stage_idx: self.inlet_split.split_fraction[0, "outlet_{}".format(i)].unfix() # Initialize valves for i in self.inlet_stage_idx: _set_port( self.throttle_valve[i].inlet, getattr(self.inlet_split, "outlet_{}".format(i)), ) self.throttle_valve[i].initialize( outlvl=outlvl, solver=solver, optarg=optarg ) # Initialize turbine for i in self.inlet_stage_idx: _set_port(self.inlet_stage[i].inlet, self.throttle_valve[i].outlet) self.inlet_stage[i].initialize( outlvl=outlvl, solver=solver, optarg=optarg ) # Initialize Mixer self.inlet_mix.use_minimum_inlet_pressure_constraint() for i in self.inlet_stage_idx: _set_port( getattr(self.inlet_mix, "inlet_{}".format(i)), self.inlet_stage[i].outlet, ) getattr(self.inlet_mix, "inlet_{}".format(i)).fix() self.inlet_mix.initialize(outlvl=outlvl, solver=solver, optarg=optarg) for i in self.inlet_stage_idx: getattr(self.inlet_mix, "inlet_{}".format(i)).unfix() self.inlet_mix.use_equal_pressure_constraint() prev_port = self.inlet_mix.outlet prev_port = init_section( self.hp_stages, self.hp_split, self.config.hp_disconnect, prev_port ) if len(self.hp_stages) in self.config.hp_disconnect: prev_port = self.ip_stages[1].inlet prev_port = init_section( self.ip_stages, self.ip_split, self.config.ip_disconnect, prev_port ) if len(self.ip_stages) in self.config.ip_disconnect: prev_port = self.lp_stages[1].inlet prev_port = init_section( self.lp_stages, self.lp_split, self.config.lp_disconnect, prev_port ) _set_port(self.outlet_stage.inlet, prev_port) self.outlet_stage.initialize(outlvl=outlvl, solver=solver, optarg=optarg) for t in self.flowsheet().time: self.inlet_split.inlet.flow_mol[ t ].value = self.outlet_stage.inlet.flow_mol[t].value from_json(self, sd=istate, wts=sp)
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 - 273.15) / (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 initialize( blk, shell_state_args=None, tube_state_args=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None, ): """ Initialization routine for the unit. 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 solver) Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Create solver opt = get_solver(solver, optarg) # --------------------------------------------------------------------- # Initialize shell block flags_shell = blk.shell.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=shell_state_args, ) flags_tube = blk.tube.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=tube_state_args, ) init_log.info_high("Initialization Step 1 Complete.") # --------------------------------------------------------------------- # Solve unit # Wall 0D if blk.config.has_wall_conduction == WallConductionType.zero_dimensional: shell_units = \ blk.config.shell_side.property_package.get_metadata().get_derived_units for t in blk.flowsheet().config.time: for z in blk.shell.length_domain: blk.temperature_wall[t, z].fix( value( 0.5 * ( blk.shell.properties[t, 0].temperature + pyunits.convert( blk.tube.properties[t, 0].temperature, to_units=shell_units('temperature')) ) ) ) blk.tube.deactivate() blk.tube_heat_transfer_eq.deactivate() blk.wall_0D_model.deactivate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 2 {}.".format(idaeslog.condition(res)) ) blk.tube.activate() blk.tube_heat_transfer_eq.activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 3 {}.".format(idaeslog.condition(res)) ) blk.wall_0D_model.activate() blk.temperature_wall.unfix() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 4 {}.".format(idaeslog.condition(res)) ) blk.shell.release_state(flags_shell) blk.tube.release_state(flags_tube) init_log.info("Initialization Complete.")
def initialize(blk, state_args={}, outlvl=idaeslog.NOTSET, solver='ipopt', optarg={'tol': 1e-6}): ''' water tank initialization routine. Keyword Arguments: state_args : a dict of arguments to be passed to the property package(s) for the control_volume of the model to provide an initial state for initialization (see documentation of the specific property package) (default = None). outlvl : sets output level of initialisation routine * 0 = no output (default) * 1 = return solver state for each step in routine * 2 = return solver state for each step in subroutines * 3 = include solver output infomation (tee=True) optarg : solver options dictionary object (default={'tol': 1e-6}) solver : str indicating whcih solver to use during initialization (default = 'ipopt') Returns: None ''' init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") opt = SolverFactory(solver) opt.options = optarg init_log.info_low("Starting initialization...") flags = blk.control_volume.initialize(state_args=state_args, outlvl=outlvl, optarg=optarg, solver=solver) init_log.info_high("Initialization Step 1 Complete.") # Fix outlet pressure for t in blk.flowsheet().config.time: blk.control_volume.properties_out[t].pressure.\ fix(value(blk.control_volume.properties_in[t].pressure)) blk.pressure_change_eqn.deactivate() # solve model with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 2 {}.".format(idaeslog.condition(res)) ) # Unfix outlet pressure for t in blk.flowsheet().config.time: blk.control_volume.properties_out[t].pressure.unfix() blk.pressure_change_eqn.activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( "Initialization Step 3 {}.".format(idaeslog.condition(res)) ) blk.control_volume.release_state(flags, outlvl) init_log.info("Initialization Complete.")
def initialize( self, outlvl=idaeslog.NOTSET, solver=None, optarg=None, 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: 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 special 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() # Create solver slvr = get_solver(solver, optarg) 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 initialize(self, state_args={}, state_vars_fixed=False, hold_state=False, outlvl=idaeslog.NOTSET, temperature_bounds=(260, 616), solver='ipopt', optarg={'tol': 1e-8}): ''' Initialization routine for property package. Keyword Arguments: state_args : Dictionary with initial guesses for the state vars chosen. Note that if this method is triggered through the control volume, and if initial guesses were not provied at the unit model level, the control volume passes the inlet values as initial guess.The keys for the state_args dictionary are: flow_mol_comp : value at which to initialize component flows (default=None) pressure : value at which to initialize pressure (default=None) temperature : value at which to initialize temperature (default=None) outlvl : sets output level of initialization routine state_vars_fixed: Flag to denote if state vars have already been fixed. - True - states have already been fixed and initialization does not need to worry about fixing and unfixing variables. - 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") if state_vars_fixed is False: # Fix state variables if not already fixed flags = fix_state_vars(self, state_args) else: # Check when the state vars are fixed already result in dof 0 for k in self.keys(): if degrees_of_freedom(self[k]) != 0: raise Exception("State vars fixed but degrees of freedom " "for state block is not zero during " "initialization.") if optarg is None: sopt = {"tol": 1e-8} else: sopt = optarg opt = SolverFactory(solver) opt.options = sopt with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solve_indexed_blocks(opt, [self], tee=slc.tee) init_log.info("Initialization Step 1 {}.".format( idaeslog.condition(res))) if state_vars_fixed is False: if hold_state is True: return flags else: self.release_state(flags) init_log.info('Initialization Complete.')
def copy_values_at_time(fs_tgt, fs_src, t_target, t_source, copy_fixed=True, outlvl=idaeslog.NOTSET): """ Function to set the values of all (explicitly or implicitly) time-indexed variables in a flowsheet to similar values (with the same name) but at different points in time and (potentially) in different flowsheets. Args: fs_tgt : Target flowsheet, whose variables' values will get set fs_src : Source flowsheet, whose variables' values will be used to set those of the target flowsheet. Could be the target flowsheet t_target : Target time point t_source : Source time point copy_fixed : Bool of whether or not to copy over fixed variables in target model outlvl : IDAES logger output level Returns: None """ time_target = fs_tgt.time var_visited = set() for var_target in fs_tgt.component_objects(Var): if id(var_target) in var_visited: continue var_visited.add(id(var_target)) if not is_explicitly_indexed_by(var_target, time_target): continue n = var_target.index_set().dimen local_parent = fs_src varname = var_target.getname(fully_qualified=True, relative_to=fs_tgt) # Calling find_component here makes the assumption that varname does not # contain decimal indices. var_source = fs_src.find_component(varname) if var_source is None: # Log a warning msg = ('Warning copying values: ' + varname + ' does not exist in source block ' + fs_src.name) init_log = idaeslog.getInitLogger(__name__, outlvl) init_log.warning(msg) continue if n == 1: if not copy_fixed and var_target[t_target].fixed: continue var_target[t_target].set_value(var_source[t_source].value) elif n >= 2: index_info = get_index_set_except(var_target, time_target) non_time_index_set = index_info['set_except'] index_getter = index_info['index_getter'] for non_time_index in non_time_index_set: source_index = index_getter(non_time_index, t_source) target_index = index_getter(non_time_index, t_target) if not copy_fixed and var_target[target_index].fixed: continue var_target[target_index].set_value( var_source[source_index].value) blk_visited = set() for blk_target in fs_tgt.component_objects(Block): if id(blk_target) in blk_visited: continue blk_visited.add(id(blk_target)) if not is_explicitly_indexed_by(blk_target, time_target): continue n = blk_target.index_set().dimen blkname = blk_target.getname(fully_qualified=True, relative_to=fs_tgt) blk_source = fs_src.find_component(blkname) if blk_source is None: # log warning msg = ('Warning copying values: ' + blkname + ' does not exist in source' + fs_src.name) init_log = idaeslog.getInitLogger(__name__, outlvl) init_log.warning(msg) continue if n == 1: target_index = t_target source_index = t_source var_visited = set() for var_target in blk_target[target_index].component_data_objects( Var): if id(var_target) in var_visited: continue var_visited.add(id(var_target)) if not copy_fixed and var_target.fixed: continue # Here, find_component will not work from BlockData object local_parent = blk_source[source_index] for r in path_from_block(var_target, blk_target[target_index]): local_parent = getattr(local_parent, r[0])[r[1]] var_source = getattr(local_parent, var_target.parent_component().local_name)[ var_target.index()] var_target.set_value(var_source.value) elif n >= 2: index_info = get_index_set_except(blk_target, time_target) non_time_index_set = index_info['set_except'] index_getter = index_info['index_getter'] for non_time_index in non_time_index_set: source_index = index_getter(non_time_index, t_source) target_index = index_getter(non_time_index, t_target) var_visited = set() for var_target in blk_target[ target_index].component_data_objects(Var): if id(var_target) in var_visited: continue var_visited.add(id(var_target)) if not copy_fixed and var_target.fixed: continue local_parent = blk_source[source_index] for r in path_from_block(var_target, blk_target[target_index]): local_parent = getattr(local_parent, r[0])[r[1]] var_source = getattr( local_parent, var_target.parent_component().local_name)[ var_target.index()] var_target.set_value(var_source.value)
def init_isentropic(blk, state_args, outlvl, solver, optarg): """ Initialization routine for unit (default solver ipopt) 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={'tol': 1e-6}) solver : str indicating whcih solver to use during initialization (default = 'ipopt') Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Set solver options opt = SolverFactory(solver) opt.options = optarg # --------------------------------------------------------------------- # Initialize holdup block flags = blk.control_volume.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args, ) init_log.info_high("Initialization Step 1 Complete.") # --------------------------------------------------------------------- # Initialize Isentropic block # Set state_args from inlet state if state_args is None: state_args = {} state_dict = blk.control_volume.properties_in[ blk.flowsheet().config.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 blk.properties_isentropic.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args, ) init_log.info_high("Initialization Step 2 Complete.") # --------------------------------------------------------------------- # Solve for isothermal conditions if isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Var, ): blk.properties_isentropic[:].temperature.fix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].enth_mol, Var, ): blk.properties_isentropic[:].enth_mol.fix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Expression, ): def tmp_rule(b, t): return blk.properties_isentropic[t].temperature == \ blk.control_volume.properties_in[t].temperature blk.tmp_init_constraint = Constraint(blk.flowsheet().config.time, rule=tmp_rule) blk.isentropic.deactivate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 3 {}.".format( idaeslog.condition(res))) if isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Var, ): blk.properties_isentropic[:].temperature.unfix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].enth_mol, Var, ): blk.properties_isentropic[:].enth_mol.unfix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Expression, ): blk.del_component(blk.tmp_init_constraint) blk.isentropic.activate() # --------------------------------------------------------------------- # Solve unit with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 4 {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Release Inlet state blk.control_volume.release_state(flags, outlvl + 1) init_log.info("Initialization Complete: {}".format( idaeslog.condition(res)))
def initialize(self, state_args_feed=None, state_args_liq=None, state_args_vap=None, solver=None, optarg={}, outlvl=idaeslog.NOTSET): init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") init_log.info("Begin initialization.") solverobj = get_solver(solver, optarg) feed_flags = self.feed_tray.initialize(solver=None, optarg={}, outlvl=idaeslog.NOTSET) self.propagate_stream_state(source=self.feed_tray.vap_out, destination=self.condenser.inlet) self.condenser.initialize(solver=None, optarg={}, outlvl=idaeslog.NOTSET) self.propagate_stream_state(source=self.feed_tray.liq_out, destination=self.reboiler.inlet) self.reboiler.initialize(solver=None, optarg={}, outlvl=idaeslog.NOTSET) # initialize the rectification section for i in self._rectification_index: self.propagate_stream_state( source=self.condenser.reflux, destination=self.rectification_section[i].liq_in) self.propagate_stream_state( source=self.feed_tray.vap_out, destination=self.rectification_section[i].vap_in) if i == 1: rect_liq_flags = self.rectification_section[i]. \ initialize(hold_state_liq=True, hold_state_vap=False, solver=None, optarg={}, outlvl=idaeslog.NOTSET) elif i == len(self._rectification_index): rect_vap_flags = \ self.rectification_section[i]. \ initialize(hold_state_liq=False, hold_state_vap=True, solver=None, optarg={}, outlvl=idaeslog.NOTSET) else: self.rectification_section[i].initialize( solver=None, optarg={}, outlvl=idaeslog.NOTSET) # initialize the stripping section for i in self._stripping_index: self.propagate_stream_state( source=self.feed_tray.liq_out, destination=self.stripping_section[i].liq_in) self.propagate_stream_state( source=self.reboiler.vapor_reboil, destination=self.stripping_section[i].vap_in) if i == self.config.feed_tray_location + 1: strip_liq_flags = self.stripping_section[i]. \ initialize(hold_state_liq=True, hold_state_vap=False, solver=None, optarg={}, outlvl=idaeslog.NOTSET) elif i == self.config.number_of_trays: strip_vap_flags = self.stripping_section[i]. \ initialize(hold_state_liq=False, hold_state_vap=True, solver=None, optarg={}, outlvl=idaeslog.NOTSET) else: self.stripping_section[i].initialize(solver=None, optarg={}, outlvl=idaeslog.NOTSET) # For initialization purposes and to enable solving individual sections # creating a temp block. Note that this temp block is a reference to # the rectification, stripping, and feed sections. Also, expanded arcs # are added to the temp block as the initialization solve proceeds. self._temp_block = Block() self._temp_block.rectification = Block() # adding reference to the rectification section and the expanded # vapor and liquid arcs self._temp_block.rectification.trays = Reference( self.rectification_section) self._temp_block.rectification.expanded_liq_stream = Reference( self.rectification_liq_stream[:].expanded_block) self._temp_block.rectification.expanded_vap_stream = Reference( self.rectification_vap_stream[:].expanded_block) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solverobj.solve(self._temp_block.rectification, tee=slc.tee) init_log.info("Rectification section initialization status {}.".format( idaeslog.condition(res))) self._temp_block.stripping = Block() # adding reference to the stripping section and the expanded # vapor and liquid arcs self._temp_block.stripping.trays = Reference(self.stripping_section) self._temp_block.stripping.expanded_liq_stream = Reference( self.stripping_liq_stream[:].expanded_block) self._temp_block.stripping.expanded_vap_stream = Reference( self.stripping_vap_stream[:].expanded_block) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solverobj.solve(self._temp_block.stripping, tee=slc.tee) init_log.info("Stripping section initialization status {}.".format( idaeslog.condition(res))) # releasing the fixed inlets for the vap in to the rectification # to enable connection with the feed tray vap out self.rectification_section[len(self._rectification_index)]. \ properties_in_vap. \ release_state(flags=rect_vap_flags, outlvl=outlvl) # releasing the fixed inlets for the liq in to the stripping # to enable connection with the feed tray liq out self.stripping_section[self.config.feed_tray_location + 1]. \ properties_in_liq. \ release_state(flags=strip_liq_flags, outlvl=outlvl) # Adding the feed tray to temp block solve self._temp_block.feed_tray = Reference(self.feed_tray) self._temp_block.expanded_feed_liq_stream_in = Reference( self.feed_liq_in.expanded_block) self._temp_block.expanded_feed_liq_stream_out = Reference( self.feed_liq_out.expanded_block) self._temp_block.expanded_feed_vap_stream_in = Reference( self.feed_vap_in.expanded_block) self._temp_block.expanded_feed_vap_stream_out = Reference( self.feed_vap_out.expanded_block) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solverobj.solve(self._temp_block, tee=slc.tee) init_log.info("Column section initialization status {}.".format( idaeslog.condition(res))) self.rectification_section[1]. \ properties_in_liq. \ release_state(flags=rect_liq_flags, outlvl=outlvl) # Adding the condenser to the temp block solve self._temp_block.condenser = Reference(self.condenser) self._temp_block.expanded_condenser_vap_in = Reference( self.condenser_vap_in.expanded_block) self._temp_block.expanded_condenser_reflux_out = Reference( self.condenser_reflux_out.expanded_block) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solverobj.solve(self._temp_block, tee=slc.tee) init_log.info( "Column section + Condenser initialization status {}.".format( idaeslog.condition(res))) self.stripping_section[self.config.number_of_trays]. \ properties_in_vap. \ release_state(flags=strip_vap_flags, outlvl=outlvl) # Delete the _temp_block as next solve is solving the entire column. # If we add the reboiler to the temp block, it will be similar to # solving the original block. This ensures that if # initialize is triggered twice, there is no implicit replacing # component error. self.del_component(self._temp_block) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solverobj.solve(self, tee=slc.tee) init_log.info( "Column section + Condenser + Reboiler initialization status {}.". format(idaeslog.condition(res))) # release feed tray state once initialization is complete self.feed_tray.properties_in_feed.\ release_state(flags=feed_flags, outlvl=outlvl)
def init_adiabatic(blk, state_args, outlvl, solver, optarg): """ Initialization routine for adiabatic pressure changers. 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={}) solver : str indicating whcih solver to use during initialization (default = None) Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Create solver opt = get_solver(solver, optarg) cv = blk.control_volume t0 = blk.flowsheet().config.time.first() state_args_out = {} if state_args is None: state_args = {} state_dict = (cv.properties_in[t0].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 # Get initialisation guesses for outlet and isentropic states for k in state_args: if k == "pressure" and k not in state_args_out: # Work out how to estimate outlet pressure if cv.properties_out[t0].pressure.fixed: # Fixed outlet pressure, use this value state_args_out[k] = value(cv.properties_out[t0].pressure) elif blk.deltaP[t0].fixed: state_args_out[k] = value(state_args[k] + blk.deltaP[t0]) elif blk.ratioP[t0].fixed: state_args_out[k] = value(state_args[k] * blk.ratioP[t0]) else: # Not obvious what to do, use inlet state state_args_out[k] = state_args[k] elif k not in state_args_out: state_args_out[k] = state_args[k] # Initialize state blocks flags = cv.properties_in.initialize( outlvl=outlvl, optarg=optarg, solver=solver, hold_state=True, state_args=state_args, ) cv.properties_out.initialize( outlvl=outlvl, optarg=optarg, solver=solver, hold_state=False, state_args=state_args_out, ) init_log.info_high("Initialization Step 1 Complete.") with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 2 {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Solve unit with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 3 {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Release Inlet state blk.control_volume.release_state(flags, outlvl) init_log.info(f"Initialization Complete: {idaeslog.condition(res)}")
def initialize(blk, state_args=None, outlvl=idaeslog.NOTSET, solver=None, optarg=None): """ General wrapper for pressure changer initialization routines 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) solver : str indicating which solver to use during initialization (default = None) Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") opt = get_solver(solver, optarg) # --------------------------------------------------------------------- # Initialize holdup block flags = blk.properties_in.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args, hold_state=True, ) init_log.info_high("Initialization Step 1 Complete.") # --------------------------------------------------------------------- # Initialize other state blocks # Set state_args from inlet state if state_args is None: state_args = {} state_dict = blk.properties_in[ blk.flowsheet().config.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 blk.properties_out.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args, ) state_args_solids = deepcopy(state_args) for p, j in blk.properties_solids.phase_component_set: if p == "Sol": state_args_solids["flow_mass_phase_comp"][ p, j] = state_args["flow_mass_phase_comp"]["Liq", j] elif p == "Liq" or p == "Vap": state_args_solids["flow_mass_phase_comp"][p, j] = 1e-8 blk.properties_solids.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_solids, ) state_args_vapor = deepcopy(state_args) for p, j in blk.properties_vapor.phase_component_set: if p == "Vap": state_args_vapor["flow_mass_phase_comp"][ p, j] = state_args["flow_mass_phase_comp"]["Liq", j] elif p == "Liq" or p == "Sol": state_args_vapor["flow_mass_phase_comp"][p, j] = 1e-8 blk.properties_vapor.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_vapor, ) init_log.info_high("Initialization Step 2 Complete.") # --------------------------------------------------------------------- # Solve unit with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 3 {}.".format( idaeslog.condition(res))) # --------------------------------------------------------------------- # Release Inlet state blk.properties_in.release_state(flags, outlvl=outlvl) init_log.info("Initialization Complete: {}".format( idaeslog.condition(res)))
def init_isentropic(blk, state_args, outlvl, solver, optarg): """ Initialization routine for isentropic pressure changers. 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={}) solver : str indicating whcih solver to use during initialization (default = None) Returns: None """ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Create solver opt = get_solver(solver, optarg) cv = blk.control_volume t0 = blk.flowsheet().config.time.first() state_args_out = {} # performance curves exist and are active so initialize with them activate_performance_curves = (hasattr(blk, "performance_curve") and blk.performance_curve.has_constraints() and blk.performance_curve.active) if activate_performance_curves: blk.performance_curve.deactivate() # The performance curves will provide (maybe indirectly) efficency # and/or pressure ratio. To get through the standard isentropic # pressure changer init, we'll see if the user provided a guess for # pressure ratio or isentropic efficency and fix them if need. If # not fixed and no guess provided, fill in something reasonable # until the performance curves are turned on. unfix_eff = {} unfix_ratioP = {} for t in blk.flowsheet().config.time: if not (blk.ratioP[t].fixed or blk.deltaP[t].fixed or cv.properties_out[t].pressure.fixed): if blk.config.compressor: if not (value(blk.ratioP[t]) >= 1.01 and value(blk.ratioP[t]) <= 50): blk.ratioP[t] = 1.8 else: if not (value(blk.ratioP[t]) >= 0.01 and value(blk.ratioP[t]) <= 0.999): blk.ratioP[t] = 0.7 blk.ratioP[t].fix() unfix_ratioP[t] = True if not blk.efficiency_isentropic[t].fixed: if not (value(blk.efficiency_isentropic[t]) >= 0.05 and value(blk.efficiency_isentropic[t]) <= 1.0): blk.efficiency_isentropic[t] = 0.8 blk.efficiency_isentropic[t].fix() unfix_eff[t] = True if state_args is None: state_args = {} state_dict = (cv.properties_in[t0].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 # Get initialisation guesses for outlet and isentropic states for k in state_args: if k == "pressure" and k not in state_args_out: # Work out how to estimate outlet pressure if cv.properties_out[t0].pressure.fixed: # Fixed outlet pressure, use this value state_args_out[k] = value(cv.properties_out[t0].pressure) elif blk.deltaP[t0].fixed: state_args_out[k] = value(state_args[k] + blk.deltaP[t0]) elif blk.ratioP[t0].fixed: state_args_out[k] = value(state_args[k] * blk.ratioP[t0]) else: # Not obvious what to do, use inlet state state_args_out[k] = state_args[k] elif k not in state_args_out: state_args_out[k] = state_args[k] # Initialize state blocks flags = cv.properties_in.initialize( outlvl=outlvl, optarg=optarg, solver=solver, hold_state=True, state_args=state_args, ) cv.properties_out.initialize( outlvl=outlvl, optarg=optarg, solver=solver, hold_state=False, state_args=state_args_out, ) init_log.info_high("Initialization Step 1 Complete.") # --------------------------------------------------------------------- # Initialize Isentropic block blk.properties_isentropic.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_out, ) init_log.info_high("Initialization Step 2 Complete.") # --------------------------------------------------------------------- # Solve for isothermal conditions if isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Var, ): blk.properties_isentropic[:].temperature.fix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].enth_mol, Var, ): blk.properties_isentropic[:].enth_mol.fix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Expression, ): def tmp_rule(b, t): return blk.properties_isentropic[t].temperature == \ blk.control_volume.properties_in[t].temperature blk.tmp_init_constraint = Constraint(blk.flowsheet().config.time, rule=tmp_rule) blk.isentropic.deactivate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 3 {}.".format( idaeslog.condition(res))) if isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Var, ): blk.properties_isentropic[:].temperature.unfix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].enth_mol, Var, ): blk.properties_isentropic[:].enth_mol.unfix() elif isinstance( blk.properties_isentropic[ blk.flowsheet().config.time.first()].temperature, Expression, ): blk.del_component(blk.tmp_init_constraint) blk.isentropic.activate() # --------------------------------------------------------------------- # Solve unit with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high("Initialization Step 4 {}.".format( idaeslog.condition(res))) if activate_performance_curves: blk.performance_curve.activate() for t, v in unfix_eff.items(): if v: blk.efficiency_isentropic[t].unfix() for t, v in unfix_ratioP.items(): if v: blk.ratioP[t].unfix() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info_high( f"Initialization Step 5 {idaeslog.condition(res)}.") # --------------------------------------------------------------------- # Release Inlet state blk.control_volume.release_state(flags, outlvl) init_log.info(f"Initialization Complete: {idaeslog.condition(res)}")
def initialize(self, state_args=None, solver=None, optarg=None, outlvl=idaeslog.NOTSET): init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit") solverobj = get_solver(solver, optarg) # Initialize the inlet and outlet state blocks. Calling the state # blocks initialize methods directly so that custom set of state args # can be passed to the inlet and outlet state blocks as control_volume # initialize method initializes the state blocks with the same # state conditions. flags = self.control_volume.properties_in. \ initialize(state_args=state_args, solver=solver, optarg=optarg, outlvl=outlvl, hold_state=True) # Initialize outlet state block at same conditions of inlet except # the temperature. Set the temperature to a temperature guess based # on the desired boilup_ratio. # Get index for bubble point temperature and and assume it # will have only a single phase equilibrium pair. This is to # support the generic property framework where the T_bubble # is indexed by the phases_in_equilibrium. In distillation, # the assumption is that there will only be a single pair # i.e. vap-liq. idx = next(iter(self.control_volume.properties_in[0]. temperature_bubble)) temp_guess = 0.5 * ( value(self.control_volume.properties_in[0].temperature_dew[idx]) - value(self.control_volume.properties_in[0]. temperature_bubble[idx])) + \ value(self.control_volume.properties_in[0].temperature_bubble[idx]) state_args_outlet = {} state_dict_outlet = ( self.control_volume.properties_in[ self.flowsheet().time.first()] .define_port_members()) for k in state_dict_outlet.keys(): if state_dict_outlet[k].is_indexed(): state_args_outlet[k] = {} for m in state_dict_outlet[k].keys(): state_args_outlet[k][m] = value(state_dict_outlet[k][m]) else: if k != "temperature": state_args_outlet[k] = value(state_dict_outlet[k]) else: state_args_outlet[k] = temp_guess self.control_volume.properties_out.initialize( state_args=state_args_outlet, solver=solver, optarg=optarg, outlvl=outlvl, hold_state=False) if degrees_of_freedom(self) == 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solverobj.solve(self, tee=slc.tee) init_log.info( "Initialization Complete, {}.".format(idaeslog.condition(res)) ) else: raise ConfigurationError( "State vars fixed but degrees of freedom " "for reboiler is not zero during " "initialization. Please ensure that the boilup_ratio " "or the outlet temperature is fixed.") self.control_volume.properties_in.\ release_state(flags=flags, outlvl=outlvl)
def initialize(blk, state_args_1={}, state_args_2={}, outlvl=0, solver='ipopt', optarg={'tol': 1e-6}): ''' General Heat Exchanger initialisation routine. Keyword Arguments: state_args_1 : a dict of arguments to be passed to the property package(s) for side 1 of the heat exchanger to provide an initial state for initialization (see documentation of the specific property package) (default = {}). state_args_2 : a dict of arguments to be passed to the property package(s) for side 2 of the heat exchanger to provide an initial state for initialization (see documentation of the specific property package) (default = {}). outlvl : sets output level of initialisation routine * 0 = no output (default) * 1 = return solver state for each step in routine * 2 = return solver state for each step in subroutines * 3 = include solver output infomation (tee=True) optarg : solver options dictionary object (default={'tol': 1e-6}) solver : str indicating whcih solver to use during initialization (default = 'ipopt') Returns: None ''' init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") opt = SolverFactory(solver) opt.options = optarg # --------------------------------------------------------------------- # Initialize inlet property blocks flags1 = blk.side_1.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_1, ) flags2 = blk.side_2.initialize( outlvl=outlvl, optarg=optarg, solver=solver, state_args=state_args_2, ) init_log.info('Initialisation Step 1 Complete.') # --------------------------------------------------------------------- # Initialize temperature differentials for t in blk.flowsheet().time: blk.side_1.properties_out[t].pressure.fix( value(blk.side_1.properties_in[t].pressure)-100.0) blk.side_2.properties_out[t].pressure.fix( value(blk.side_2.properties_in[t].pressure)-100.0) blk.side_1.properties_out[t].enth_mol.fix( value(blk.side_1.properties_in[t].enth_mol)+100.0) blk.side_2.properties_out[t].temperature.fix( value(blk.side_2.properties_in[t].temperature)-1.0) # Deactivate Constraints blk.heat_transfer_correlation.deactivate() blk.LMTD.deactivate() blk.energy_balance.deactivate() blk.deltaP_tube_eqn.deactivate() blk.deltaP_shell_eqn.deactivate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info("Initialization Step 2 Complete: {}".format( idaeslog.condition(res) ) ) # Activate energy balance and driving force for t in blk.flowsheet().time: blk.side_1.properties_out[t].pressure.unfix() blk.side_2.properties_out[t].pressure.unfix() blk.side_1.properties_out[t].enth_mol.unfix() blk.side_2.properties_out[t].temperature.unfix() blk.heat_transfer_correlation.activate() blk.LMTD.activate() blk.energy_balance.activate() blk.deltaP_tube_eqn.activate() blk.deltaP_shell_eqn.activate() with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = opt.solve(blk, tee=slc.tee) init_log.info("Initialization Step 3 Complete: {}".format( idaeslog.condition(res) ) ) # --------------------------------------------------------------------- # Release Inlet state blk.side_1.release_state(flags1, outlvl+1) blk.side_2.release_state(flags2, outlvl+1) init_log.info_low("Initialization Complete: {}".format( idaeslog.condition(res) ) )
def initialize_by_element_in_range(model, time, t_start, t_end, time_linking_vars=[], dae_vars=[], max_linking_range=0, **kwargs): """Function for solving a square model, time element-by-time element, between specified start and end times. Args: model : Flowsheet model to solve t_start : Beginning of timespan over which to solve t_end : End of timespan over which to solve Kwargs: solver : Solver option used to solve portions of the square model outlvl : idaes.logger output level """ # TODO: How to handle config arguments here? Should this function # be moved to be a method of NMPC? Have a module-level config? # CONFIG, KWARGS: handle these kwargs through config solver = kwargs.pop('solver', SolverFactory('ipopt')) outlvl = kwargs.pop('outlvl', idaeslog.NOTSET) init_log = idaeslog.getInitLogger('nmpc', outlvl) solver_log = idaeslog.getSolveLogger('nmpc', outlvl) solve_initial_conditions = kwargs.pop('solve_initial_conditions', False) #TODO: Move to docstring # Variables that will be fixed for time points outside the finite element # when constraints for a finite element are activated. # For a "normal" process, these should just be differential variables # (and maybe derivative variables). For a process with a (PID) controller, # these should also include variables used by the controller. # If these variables are not specified, # Timespan over which these variables will be fixed, counting backwards # from the first time point in the finite element (which will always be # fixed) # Should I specify max_linking_range as an integer number of finite # elements, an integer number of time points, or a float in actual time # units? Go with latter for now. # TODO: Should I fix scalar vars? Intuition is that they should already # be fixed. #time = model.time assert t_start in time.get_finite_elements() assert t_end in time.get_finite_elements() assert degrees_of_freedom(model) == 0 #dae_vars = kwargs.pop('dae_vars', []) if not dae_vars: scalar_vars, dae_vars = flatten_dae_components(model, time, Var) for var in scalar_vars: var.fix() deactivate_constraints_unindexed_by(model, time) ncp = time.get_discretization_info()['ncp'] fe_in_range = [i for i, fe in enumerate(time.get_finite_elements()) if fe >= t_start and fe <= t_end] t_in_range = [t for t in time if t >= t_start and t <= t_end] fe_in_range.pop(0) n_fe_in_range = len(fe_in_range) was_originally_active = get_activity_dict(model) was_originally_fixed = get_fixed_dict(model) # Deactivate model if not solve_initial_conditions: time_list = [t for t in time] deactivated = deactivate_model_at(model, time, time_list, outlvl=idaeslog.ERROR) else: time_list = [t for t in time if t != time.first()] deactivated = deactivate_model_at(model, time, time_list, outlvl=idaeslog.ERROR) assert degrees_of_freedom(model) == 0 with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc: results = solver.solve(model, tee=slc.tee) if results.solver.termination_condition == TerminationCondition.optimal: pass else: raise ValueError deactivated[time.first()] = deactivate_model_at(model, time, time.first(), outlvl=idaeslog.ERROR)[time.first()] # "Integration" loop for i in fe_in_range: t_prev = time[(i-1)*ncp+1] fe = [time[k] for k in range((i-1)*ncp+2, i*ncp+2)] con_list = [] for t in fe: # These will be fixed vars in constraints at t # Probably not necessary to record at what t # they occur for comp in deactivated[t]: if was_originally_active[id(comp)]: comp.activate() if not time_linking_vars: if isinstance(comp, _ConstraintData): con_list.append(comp) elif isinstance(comp, _BlockData): # Active here should be independent of whether block # was active con_list.extend( list(comp.component_data_objects(Constraint, active=True))) if not time_linking_vars: fixed_vars = [] for con in con_list: for var in identify_variables(con.expr, include_fixed=False): # use var_locator/ComponentMap to get index somehow t_idx = get_implicit_index_of_set(var, time) if t_idx is None: assert not is_in_block_indexed_by(var, time) continue if t_idx <= t_prev: fixed_vars.append(var) var.fix() else: fixed_vars = [] time_range = [t for t in time if t_prev - t <= max_linking_range and t <= t_prev] time_range = [t_prev] for _slice in time_linking_vars: for t in time_range: #if not _slice[t].fixed: _slice[t].fix() fixed_vars.append(_slice[t]) # Here I assume that the only variables that can appear in # constraints at a different (later) time index are derivatives # and differential variables (they do so in the discretization # equations) and that they only participate at t_prev. # # This is not the case for, say, PID controllers, in which case # I should pass in a list of "complicating variables," then fix # them at all time points outside the finite element. # # Alternative solution is to identify_variables in each constraint # that is activated and fix those belonging to a previous finite # element. (Should not encounter variables belonging to a future # finite element.) # ^ This option is easier, less efficient # # In either case need to record whether variable was previously fixed # so I know if I should unfix it or not. for t in fe: for _slice in dae_vars: if not _slice[t].fixed: # Fixed DAE variables are time-dependent disturbances, # whose values should not be altered by this function. _slice[t].set_value(_slice[t_prev].value) assert degrees_of_freedom(model) == 0 with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc: results = solver.solve(model, tee=slc.tee) if results.solver.termination_condition == TerminationCondition.optimal: pass else: raise ValueError for t in fe: for comp in deactivated[t]: comp.deactivate() for var in fixed_vars: if not was_originally_fixed[id(var)]: var.unfix() for t in time: for comp in deactivated[t]: if was_originally_active[id(comp)]: comp.activate() assert degrees_of_freedom(model) == 0
def initialize(blk, state_args=None, state_vars_fixed=False, hold_state=False, outlvl=idaeslog.NOTSET, solver=None, optarg=None): """ Initialization routine for property package. Keyword Arguments: state_args : Dictionary with initial guesses for the state vars chosen. Note that if this method is triggered through the control volume, and if initial guesses were not provided at the unit model level, the control volume passes the inlet values as initial guess.Keys for the state_args dictionary are: flow_mol, temperature, pressure and mole_frac_comp. 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) hold_state : flag indicating whether the initialization routine should unfix any state variables fixed during initialization (default=False). valid options: 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(blk.name, outlvl, tag="properties") solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="properties") init_log.info('Starting Vapor phase properties initialization') # Deactivate the constraints specific for non-inlet blocks i.e. # when defined state is False for k in blk.keys(): if blk[k].config.defined_state is False: blk[k].sum_component_eqn.deactivate() # Fix state variables if not already fixed if state_vars_fixed is False: flags = fix_state_vars(blk, state_args) for k in blk.keys(): dof = degrees_of_freedom(blk[k]) if dof != 0: raise RuntimeError( "{} - degrees of freedom for state block is not zero " "during initialization. DoF = {}".format(blk.name, dof)) # Create solver opt = get_solver(solver, optarg) # --------------------------------------------------------------------- # Initialise values for k in blk.keys(): for j in blk[k].component_list: if hasattr(blk[k], "cp_mol_comp_eqn"): calculate_variable_from_constraint(blk[k].cp_mol_comp[j], blk[k].cp_mol_comp_eqn[j]) if hasattr(blk[k], "flow_mol_comp_eqn"): calculate_variable_from_constraint(blk[k].flow_mol_comp[j], blk[k].flow_mol_comp_eqn[j]) if hasattr(blk[k], "cp_mol_comp_mean_eqn"): calculate_variable_from_constraint(blk[k].cp_mol_comp_mean[j], blk[k].cp_mol_comp_mean_eqn[j]) if hasattr(blk[k], "cp_mol_eqn"): calculate_variable_from_constraint(blk[k].cp_mol, blk[k].cp_mol_eqn) if hasattr(blk[k], "cp_mol_mean_eqn"): calculate_variable_from_constraint(blk[k].cp_mol_mean, blk[k].cp_mol_mean_eqn) if hasattr(blk[k], "enth_mean_eqn"): calculate_variable_from_constraint(blk[k].enth_mean, blk[k].enth_mean_eqn) # Solve property block if non-empty free_vars = 0 for k in blk.keys(): free_vars += number_unfixed_variables(blk[k]) if free_vars > 0: with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: res = solve_indexed_blocks(opt, [blk], tee=slc.tee) else: res = "" init_log.info("Vapor properties initialization complete {}.".format( idaeslog.condition(res))) # ---------------------------------------------------------------------- if state_vars_fixed is False: if hold_state is True: return flags else: blk.release_state(flags)