def main(initialize_from_file=None, store_initialization=None): """ Create and initalize a model and solver Args: None Returns: A tuple of a model and solver """ m = create_model() _stream_dict(m) set_model_input(m) if initialize_from_file is None: solver = initialize(m) else: solver = initialize(m, fileinput=initialize_from_file) solver.solve(m, tee=True) if store_initialization is not None: ms.to_json(m, fname=store_initialization) return m, solver
def homotopy(model, variables, targets, max_solver_iterations=50, max_solver_time=10, step_init=0.1, step_cut=0.5, iter_target=4, step_accel=0.5, max_step=1, min_step=0.05, max_eval=200): """ Homotopy meta-solver routine using Ipopt as the non-linear solver. This routine takes a model along with a list of fixed variables in that model and a list of target values for those variables. The routine then tries to iteratively move the values of the fixed variables to their target values using an adaptive step size. Args: model : model to be solved variables : list of Pyomo Var objects to be varied using homotopy. Variables must be fixed. targets : list of target values for each variable max_solver_iterations : maximum number of solver iterations per homotopy step (default=50) max_solver_time : maximum cpu time for the solver per homotopy step (default=10) step_init : initial homotopy step size (default=0.1) step_cut : factor by which to reduce step size on failed step (default=0.5) step_accel : acceleration factor for adjusting step size on successful step (default=0.5) iter_target : target number of solver iterations per homotopy step (default=4) max_step : maximum homotopy step size (default=1) min_step : minimum homotopy step size (default=0.05) max_eval : maximum number of homotopy evaluations (both successful and unsuccessful) (default=200) Returns: Termination Condition : A Pyomo TerminationCondition Enum indicating how the meta-solver terminated (see documentation) Solver Progress : a fraction indication how far the solver progressed from the initial values to the target values Number of Iterations : number of homotopy evaluations before solver terminated """ eps = 1e-3 # Tolerance for homotopy step convergence to 1 # Get model logger _log = logging.getLogger(__name__) # Validate model is an instance of Block if not isinstance(model, Block): raise TypeError("Model provided was not a valid Pyomo model object " "(instance of Block). Please provide a valid model.") if degrees_of_freedom(model) != 0: raise ConfigurationError( "Degrees of freedom in model are not equal to zero. Homotopy " "should not be used on probelms which are not well-defined.") # Validate variables and targets if len(variables) != len(targets): raise ConfigurationError( "Number of variables and targets do not match.") for i in range(len(variables)): v = variables[i] t = targets[i] if not isinstance(v, _VarData): raise TypeError("Variable provided ({}) was not a valid Pyomo Var " "component.".format(v)) # Check that v is part of model parent = v.parent_block() while parent != model: if parent is None: raise ConfigurationError( "Variable {} is not part of model".format(v)) parent = parent.parent_block() # Check that v is fixed if not v.fixed: raise ConfigurationError( "Homotopy metasolver provided with unfixed variable {}." "All variables must be fixed.".format(v.name)) # Check bounds on v (they don't really matter, but check for sanity) if v.ub is not None: if v.value > v.ub: raise ConfigurationError( "Current value for variable {} is greater than the " "upper bound for that variable. Please correct this " "before continuing.".format(v.name)) if t > v.ub: raise ConfigurationError( "Target value for variable {} is greater than the " "upper bound for that variable. Please correct this " "before continuing.".format(v.name)) if v.lb is not None: if v.value < v.lb: raise ConfigurationError( "Current value for variable {} is less than the " "lower bound for that variable. Please correct this " "before continuing.".format(v.name)) if t < v.lb: raise ConfigurationError( "Target value for variable {} is less than the " "lower bound for that variable. Please correct this " "before continuing.".format(v.name)) # TODO : Should we be more restrictive on these values to avoid users # TODO : picking numbers that are less likely to solve (but still valid)? # Validate homotopy parameter selections if not 0.05 <= step_init <= 0.8: raise ConfigurationError("Invalid value for step_init ({}). Must lie " "between 0.05 and 0.8.".format(step_init)) if not 0.1 <= step_cut <= 0.9: raise ConfigurationError("Invalid value for step_cut ({}). Must lie " "between 0.1 and 0.9.".format(step_cut)) if step_accel < 0: raise ConfigurationError( "Invalid value for step_accel ({}). Must be " "greater than or equal to 0.".format(step_accel)) if iter_target < 1: raise ConfigurationError( "Invalid value for iter_target ({}). Must be " "greater than or equal to 1.".format(iter_target)) if not isinstance(iter_target, int): raise ConfigurationError("Invalid value for iter_target ({}). Must be " "an an integer.".format(iter_target)) if not 0.05 <= max_step <= 1: raise ConfigurationError("Invalid value for max_step ({}). Must lie " "between 0.05 and 1.".format(max_step)) if not 0.01 <= min_step <= 0.1: raise ConfigurationError("Invalid value for min_step ({}). Must lie " "between 0.01 and 0.1.".format(min_step)) if not min_step <= max_step: raise ConfigurationError("Invalid argumnets: step_min must be less " "or equal to step_max.") if not min_step <= step_init <= max_step: raise ConfigurationError("Invalid arguments: step_init must lie " "between min_step and max_step.") if max_eval < 1: raise ConfigurationError( "Invalid value for max_eval ({}). Must be " "greater than or equal to 1.".format(step_accel)) if not isinstance(max_eval, int): raise ConfigurationError("Invalid value for max_eval ({}). Must be " "an an integer.".format(iter_target)) # Create solver object solver_obj = SolverFactory('ipopt') # Perform initial solve of model to confirm feasible initial solution results, solved, sol_iter, sol_time, sol_reg = ipopt_solve_with_stats( model, solver_obj, max_solver_iterations, max_solver_time) if not solved: _log.exception("Homotopy Failed - initial solution infeasible.") return TerminationCondition.infeasible, 0, 0 elif sol_reg != "-": _log.warning( "Homotopy - initial solution converged with regularization.") return TerminationCondition.other, 0, 0 else: _log.info("Homotopy - initial point converged") # Set up homotopy variables # Get initial values and deltas for all variables v_init = [] for i in range(len(variables)): v_init.append(variables[i].value) n_0 = 0.0 # Homotopy progress variable s = step_init # Set step size to step_init iter_count = 0 # Counter for homotopy iterations # Save model state to dict # TODO : for very large models, it may be necessary to dump this to a file current_state = to_json(model, return_dict=True) while n_0 < 1.0: iter_count += 1 # Increase iter_count regardless of success or failure # Calculate next n value given current step size if n_0 + s >= 1.0 - eps: n_1 = 1.0 else: n_1 = n_0 + s _log.info("Homotopy Iteration {}. Next Step: {} (Current: {})".format( iter_count, n_1, n_0)) # Update values for all variables using n_1 for i in range(len(variables)): variables[i].fix(targets[i] * n_1 + v_init[i] * (1 - n_1)) # Solve model at new state results, solved, sol_iter, sol_time, sol_reg = ipopt_solve_with_stats( model, solver_obj, max_solver_iterations, max_solver_time) # Check solver output for convergence if solved: # Step succeeded - accept current state current_state = to_json(model, return_dict=True) # Update n_0 to accept current step n_0 = n_1 # Check solver iterations and calculate next step size s_proposed = s * (1 + step_accel * (iter_target / sol_iter - 1)) if s_proposed > max_step: s = max_step elif s_proposed < min_step: s = min_step else: s = s_proposed else: # Step failed - reload old state from_json(model, current_state) # Try to cut back step size if s > min_step: # Step size can be cut s = max(min_step, s * step_cut) else: # Step is already at minimum size, terminate homotopy _log.exception( "Homotopy failed - could not converge at minimum step " "size. Current progress is {}".format(n_0)) return TerminationCondition.minStepLength, n_0, iter_count if iter_count >= max_eval: # Use greater than or equal to to be safe _log.exception("Homotopy failed - maximum homotopy iterations " "exceeded. Current progress is {}".format(n_0)) return TerminationCondition.maxEvaluations, n_0, iter_count if sol_reg == "-": _log.info("Homotopy successful - converged at target values in {} " "iterations.".format(iter_count)) return TerminationCondition.optimal, n_0, iter_count else: _log.exception("Homotopy failed - converged at target values with " "regularization in {} iterations.".format(iter_count)) return TerminationCondition.other, n_0, iter_count
def initialize( m, outlvl=idaeslog.NOTSET, optarg={ "tol": 1e-6, "max_iter": 40, }, ): """Initialize unit models""" init_log = idaeslog.getInitLogger(m.name, outlvl, tag="flowsheet") solve_log = idaeslog.getSolveLogger(m.name, outlvl, tag="flowsheet") solver = get_solver() solver.options = optarg init_log.info_low("Starting initialization...") if not os.path.exists("subcritical_boiler_init.json.gz"): # 10 Waterwalls, initial guess for specific simulation m.fs.Waterwalls[1].heat_fireside[:].fix(2.3e7) m.fs.Waterwalls[2].heat_fireside[:].fix(1.5e7) m.fs.Waterwalls[3].heat_fireside[:].fix(6.8e6) m.fs.Waterwalls[4].heat_fireside[:].fix(1.2e7) m.fs.Waterwalls[5].heat_fireside[:].fix(1.2e7) m.fs.Waterwalls[6].heat_fireside[:].fix(1.2e7) m.fs.Waterwalls[7].heat_fireside[:].fix(1.0e7) m.fs.Waterwalls[8].heat_fireside[:].fix(9.9e6) m.fs.Waterwalls[9].heat_fireside[:].fix(2.1) m.fs.Waterwalls[10].heat_fireside[:].fix(2.0e7) state_args_water_steam = { 'flow_mol': 199470.7831, # mol/s 'pressure': 10903981.9107, # Pa 'enth_mol': 26585.3483 } # j/mol state_args_feedwater = { 'flow_mol': 4630.6098, # mol/s 'pressure': 10903981.9107, # Pa 'enth_mol': 22723.907 } # j/mol m.fs.drum.initialize( outlvl=outlvl, optarg=solver.options, state_args_water_steam=state_args_water_steam, state_args_feedwater=state_args_feedwater, ) m.fs.downcomer.inlet.flow_mol[:].fix( m.fs.drum.liquid_outlet.flow_mol[0].value) m.fs.downcomer.inlet.pressure[:].fix( m.fs.drum.liquid_outlet.pressure[0].value) m.fs.downcomer.inlet.enth_mol[:].fix( m.fs.drum.liquid_outlet.enth_mol[0].value) m.fs.downcomer.initialize( state_args={ "flow_mol": m.fs.drum.liquid_outlet.flow_mol[0].value, "pressure": m.fs.drum.liquid_outlet.pressure[0].value, "enth_mol": m.fs.drum.liquid_outlet.enth_mol[0].value, }, outlvl=outlvl, optarg=solver.options, ) m.fs.Waterwalls[1].inlet.flow_mol[:].fix( m.fs.downcomer.outlet.flow_mol[0].value) m.fs.Waterwalls[1].inlet.pressure[:].fix( m.fs.downcomer.outlet.pressure[0].value) m.fs.Waterwalls[1].inlet.enth_mol[:].fix( m.fs.downcomer.outlet.enth_mol[0].value) m.fs.Waterwalls[1].initialize( state_args={ "flow_mol": m.fs.Waterwalls[1].inlet.flow_mol[0].value, "pressure": m.fs.Waterwalls[1].inlet.pressure[0].value, "enth_mol": m.fs.Waterwalls[1].inlet.enth_mol[0].value, }, outlvl=6, optarg=solver.options, ) for i in range(2, 11): m.fs.Waterwalls[i].inlet.flow_mol[:].fix( m.fs.Waterwalls[i - 1].outlet.flow_mol[0].value) m.fs.Waterwalls[i].inlet.pressure[:].fix( m.fs.Waterwalls[i - 1].outlet.pressure[0].value) m.fs.Waterwalls[i].inlet.enth_mol[:].fix( m.fs.Waterwalls[i - 1].outlet.enth_mol[0].value) m.fs.Waterwalls[i].initialize( state_args={ "flow_mol": m.fs.Waterwalls[i - 1].outlet.flow_mol[0].value, "pressure": m.fs.Waterwalls[i - 1].outlet.pressure[0].value, "enth_mol": m.fs.Waterwalls[i - 1].outlet.enth_mol[0].value, }, outlvl=6, optarg=solver.options, ) m.fs.drum.feedwater_inlet.flow_mol[:].fix() m.fs.drum.feedwater_inlet.pressure[:].unfix() m.fs.drum.feedwater_inlet.enth_mol[:].fix() m.fs.downcomer.inlet.flow_mol[:].unfix() m.fs.downcomer.inlet.pressure[:].unfix() m.fs.downcomer.inlet.enth_mol[:].unfix() print('solving full-space problem') for i in m.fs.ww_zones: m.fs.Waterwalls[i].inlet.flow_mol[:].unfix() m.fs.Waterwalls[i].inlet.pressure[:].unfix() m.fs.Waterwalls[i].inlet.enth_mol[:].unfix() df = degrees_of_freedom(m) if df != 0: raise ValueError("Check degrees of freedom: {}".format(df)) with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc: print('solving full-space problem') res = solver.solve(m, tee=slc.tee) init_log.info_low("Initialization Complete: {}".format( idaeslog.condition(res))) ms.to_json(m, fname="subcritical_boiler_init.json.gz") else: print('\n\nInitializing from json file') ms.from_json(m, fname="subcritical_boiler_init.json.gz") return m
col.append(tag + "_model") val_cases = range(len(df.index)) df_val = pd.DataFrame(columns=col, index=val_cases) data_module.set_data_params(m, df, index_index=0) set_input_from_data(m) set_params(m, 0) strip_bounds = pyo.TransformationFactory("contrib.strip_var_bounds") strip_bounds.apply_to(m, reversible=False) n = 0 ms.from_json(m, fname=f"results/state4_{n}.json.gz") set_params(m, n) solver.solve(m, tee=True, linear_eliminate=False) sd_latest = ms.to_json(m, return_dict=True) for i in val_cases: data_module.set_data_params(m, df, index_index=i) set_input_from_data(m) set_params(m, i) print(f"Starting run for {i}, {df.iloc[i]['1LDC-GROSS-MW']/1e6} MW") for q in m.kq: for j in m.kq[q]: print(f"{q}[{j}] {pyo.value(m.kq[q][j])}") solver.options["max_iter"] = 100 try: res = solver.solve(m, tee=True, linear_eliminate=False) res = idaeslog.condition(res)
def get_model(dynamic=True, load_state=None, save_state=None, time_set=None, nstep=None): if load_state is not None and not os.path.exists(load_state): # Want to load state but file doesn't exist, so warn and reinitialize _log.warning(f"Warning cannot load saved state {load_state}") load_state = None m = pyo.ConcreteModel() m.dynamic = dynamic m.init_dyn = False if time_set is None: time_set = [0,20,200] if nstep is None: nstep = 5 if m.dynamic: m.fs_main = FlowsheetBlock(default={"dynamic": True, "time_set": time_set}) else: m.fs_main = FlowsheetBlock(default={"dynamic": False}) # Add property packages to flowsheet library m.fs_main.prop_water = iapws95.Iapws95ParameterBlock() m.fs_main.prop_gas = FlueGasParameterBlock() m.fs_main.fs_blr = FlowsheetBlock() m.fs_main.fs_stc = FlowsheetBlock() m = blr.add_unit_models(m) m = stc.add_unit_models(m) if m.dynamic: # extra variables required by controllers # master level control output, desired feed water flow rate at bfp outlet m.fs_main.flow_level_ctrl_output = pyo.Var( m.fs_main.config.time, initialize=10000, doc="mole flow rate of feed water demand from drum level master controller" ) # boiler master pv main steam pressure in MPa m.fs_main.main_steam_pressure = pyo.Var( m.fs_main.config.time, initialize=13, doc="main steam pressure in MPa for boiler master controller" ) # PID controllers # master of cascading level controller m.fs_main.drum_master_ctrl = PIDController(default={"pv":m.fs_main.fs_blr.aDrum.level, "mv":m.fs_main.flow_level_ctrl_output, "type": 'PI'}) # slave of cascading level controller m.fs_main.drum_slave_ctrl = PIDController(default={"pv":m.fs_main.fs_stc.bfp.outlet.flow_mol, "mv":m.fs_main.fs_stc.bfp_turb_valve.valve_opening, "type": 'PI', "bounded_output": False}) # turbine master PID controller to control power output in MW by manipulating throttling valve m.fs_main.turbine_master_ctrl = PIDController(default={"pv":m.fs_main.fs_stc.power_output, "mv":m.fs_main.fs_stc.turb.throttle_valve[1].valve_opening, "type": 'PI'}) # boiler master PID controller to control main steam pressure in MPa by manipulating coal feed rate m.fs_main.boiler_master_ctrl = PIDController(default={"pv":m.fs_main.main_steam_pressure, "mv":m.fs_main.fs_blr.aBoiler.flowrate_coal_raw, "type": 'PI'}) m.discretizer = pyo.TransformationFactory('dae.finite_difference') m.discretizer.apply_to(m, nfe=nstep, wrt=m.fs_main.config.time, scheme="BACKWARD") # desired sliding pressure in MPa as a function of power demand in MW @m.fs_main.Expression(m.fs_main.config.time, doc="Sliding pressure as a function of power output") def sliding_pressure(b, t): return 12.511952+(12.511952-9.396)/(249.234-96.8)*(b.turbine_master_ctrl.setpoint[t]-249.234) # main steam pressure in MPa @m.fs_main.Constraint(m.fs_main.config.time, doc="main steam pressure in MPa") def main_steam_pressure_eqn(b, t): return b.main_steam_pressure[t] == 1e-6*b.fs_blr.aPlaten.outlet.pressure[t] # Constraint for setpoint of the slave controller of the three-element drum level controller @m.fs_main.Constraint(m.fs_main.config.time, doc="Set point of drum level slave control") def drum_level_control_setpoint_eqn(b, t): return b.drum_slave_ctrl.setpoint[t] == b.flow_level_ctrl_output[t] + \ b.fs_blr.aPlaten.outlet.flow_mol[t] + \ b.fs_blr.blowdown_split.FW_Blowdown.flow_mol[t] #revised to add steam flow only # Constraint for setpoint of boiler master @m.fs_main.Constraint(m.fs_main.config.time, doc="Set point of boiler master") def boiler_master_setpoint_eqn(b, t): return b.boiler_master_ctrl.setpoint[t] == 0.02*(b.turbine_master_ctrl.setpoint[t] - b.fs_stc.power_output[t]) + b.sliding_pressure[t] # Constraint for setpoint of boiler master @m.fs_main.Constraint(m.fs_main.config.time, doc="dry O2 in flue gas in dynamic mode") def dry_o2_in_flue_gas_dyn_eqn(b, t): return b.fs_blr.aBoiler.fluegas_o2_pct_dry[t] == 0.05*(b.fs_stc.spray_ctrl.setpoint[t] - b.fs_stc.temperature_main_steam[t]) - \ 0.0007652*b.fs_blr.aBoiler.flowrate_coal_raw[t]**3 + \ 0.06744*b.fs_blr.aBoiler.flowrate_coal_raw[t]**2 - 1.9815*b.fs_blr.aBoiler.flowrate_coal_raw[t] + 22.275 # controller inputs # for master level controller m.fs_main.drum_master_ctrl.gain_p.fix(40000) #increased from 5000 m.fs_main.drum_master_ctrl.gain_i.fix(100) m.fs_main.drum_master_ctrl.setpoint.fix(0.889) m.fs_main.drum_master_ctrl.mv_ref.fix(0) #revised to 0 # for slave level controller, note the setpoint is defined by the constraint m.fs_main.drum_slave_ctrl.gain_p.fix(2e-2) # increased from 1e-2 m.fs_main.drum_slave_ctrl.gain_i.fix(2e-4) # increased from 1e-4 m.fs_main.drum_slave_ctrl.mv_ref.fix(0.5) # for turbine master controller, note the setpoint is the power demand m.fs_main.turbine_master_ctrl.gain_p.fix(5e-4) #changed from 2e-3 m.fs_main.turbine_master_ctrl.gain_i.fix(5e-4) #changed from 2e-3 m.fs_main.turbine_master_ctrl.mv_ref.fix(0.6) # for boiler master controller, note the setpoint is specified by the constraint m.fs_main.boiler_master_ctrl.gain_p.fix(10) m.fs_main.boiler_master_ctrl.gain_i.fix(0.25) m.fs_main.boiler_master_ctrl.mv_ref.fix(29.0) t0 = m.fs_main.config.time.first() m.fs_main.drum_master_ctrl.integral_of_error[t0].fix(0) m.fs_main.drum_slave_ctrl.integral_of_error[t0].fix(0) m.fs_main.turbine_master_ctrl.integral_of_error[t0].fix(0) m.fs_main.boiler_master_ctrl.integral_of_error[t0].fix(0) blr.set_arcs_and_constraints(m) blr.set_inputs(m) stc.set_arcs_and_constraints(m) stc.set_inputs(m) # Now that the mole is discreteized set and calculate scaling factors set_scaling_factors(m) add_overall_performance_expressions(m) # Add performance measures if load_state is None: blr.initialize(m) stc.initialize(m) optarg={"tol":5e-7,"linear_solver":"ma27","max_iter":50} solver = pyo.SolverFactory("ipopt") solver.options = optarg _log.info("Bring models closer together...") m.fs_main.fs_blr.flow_mol_steam_rh_eqn.deactivate() # Hook the boiler to the steam cycle. m.fs_main.S001 = Arc( source=m.fs_main.fs_blr.aPlaten.outlet, destination=m.fs_main.fs_stc.turb.inlet_split.inlet ) m.fs_main.S005 = Arc( source=m.fs_main.fs_stc.turb.hp_split[14].outlet_1, destination=m.fs_main.fs_blr.aRH1.tube_inlet ) m.fs_main.S009 = Arc( source=m.fs_main.fs_blr.aRH2.tube_outlet, destination=m.fs_main.fs_stc.turb.ip_stages[1].inlet ) m.fs_main.S042 = Arc( source=m.fs_main.fs_stc.fwh6.desuperheat.outlet_2, destination=m.fs_main.fs_blr.aECON.tube_inlet ) m.fs_main.B006 = Arc( source=m.fs_main.fs_stc.spray_valve.outlet, destination=m.fs_main.fs_blr.Attemp.Water_inlet ) pyo.TransformationFactory('network.expand_arcs').apply_to(m.fs_main) # unfix all connected streams m.fs_main.fs_stc.turb.inlet_split.inlet.unfix() m.fs_main.fs_stc.turb.hp_split[14].outlet_1.unfix() m.fs_main.fs_blr.aRH1.tube_inlet.unfix() m.fs_main.fs_stc.turb.ip_stages[1].inlet.unfix() m.fs_main.fs_blr.aECON.tube_inlet.unfix() m.fs_main.fs_blr.Attemp.Water_inlet.unfix() m.fs_main.fs_stc.spray_valve.outlet.unfix() #outlet pressure fixed on steam cycle sub-flowsheet # deactivate constraints on steam cycle flowsheet m.fs_main.fs_stc.fw_flow_constraint.deactivate() m.fs_main.fs_stc.turb.constraint_reheat_flow.deactivate() m.fs_main.fs_blr.aBoiler.flowrate_coal_raw.unfix() # steam circulation and coal flow are linked if m.dynamic==False: if load_state is None: m.fs_main.fs_stc.spray_valve.valve_opening.unfix() m.fs_main.fs_stc.temperature_main_steam.fix(810) _log.info("Solve connected models...") print("Degrees of freedom = {}".format(degrees_of_freedom(m))) assert degrees_of_freedom(m) == 0 res = solver.solve(m, tee=True) _log.info("Solved: {}".format(idaeslog.condition(res))) # increase load to around 250 MW _log.info("Increase coal feed rate to 32.5...") m.fs_main.fs_stc.bfp.outlet.pressure.unfix() m.fs_main.fs_stc.turb.throttle_valve[1].valve_opening.fix(0.9) m.fs_main.fs_blr.aBoiler.flowrate_coal_raw.fix(32.5) res = solver.solve(m, tee=True) if not load_state is None: # the fwh heat transfer coefficient correlations are added in the # initialization, so if we are skipping the init, we have to add # them here. stc._add_heat_transfer_correlation(m.fs_main.fs_stc) ms.from_json(m, fname=load_state) if save_state is not None: ms.to_json(m, fname=save_state) else: m.fs_main.fs_blr.dry_o2_in_flue_gas_eqn.deactivate() t0 = m.fs_main.config.time.first() m.fs_main.fs_stc.fwh2.condense.level[t0].fix() m.fs_main.fs_stc.fwh3.condense.level[t0].fix() m.fs_main.fs_stc.fwh5.condense.level[t0].fix() m.fs_main.fs_stc.fwh6.condense.level[t0].fix() m.fs_main.fs_stc.hotwell_tank.level[t0].fix() m.fs_main.fs_stc.da_tank.level[t0].fix() m.fs_main.fs_stc.temperature_main_steam[t0].unfix() m.fs_main.fs_stc.spray_valve.valve_opening[t0].fix() m.fs_main.fs_blr.aDrum.level.unfix() m.fs_main.fs_blr.aDrum.level[t0].fix() m.fs_main.flow_level_ctrl_output.unfix() m.fs_main.flow_level_ctrl_output[t0].fix() m.fs_main.fs_stc.bfp.outlet.pressure.unfix() m.fs_main.fs_blr.aBoiler.flowrate_coal_raw.unfix() m.fs_main.fs_blr.aBoiler.flowrate_coal_raw[t0].fix() m.fs_main.fs_stc.turb.throttle_valve[1].valve_opening.unfix() m.fs_main.fs_stc.turb.throttle_valve[1].valve_opening[t0].fix() m.fs_main.turbine_master_ctrl.setpoint.fix() return m