Example #1
0
def test_json_load(m):
    fname = os.path.join(this_file_dir(), 'NGFC_flowsheet_init.json')
    ms.from_json(m, fname=fname)

    assert (pyo.value(m.fs.cathode.ion_outlet.flow_mol[0]) == pytest.approx(
        1670.093, 1e-5))
    assert (pyo.value(m.fs.reformer_recuperator.area) == pytest.approx(
        4512.56, 1e-5))
    assert (pyo.value(m.fs.anode.heat_duty[0]) == pytest.approx(
        -672918626, 1e-5))
    assert (pyo.value(m.fs.CO2_emissions) == pytest.approx(291.169, 1e-5))
    assert (pyo.value(m.fs.net_power) == pytest.approx(659.879, 1e-5))
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 = pyo.value(
        iapws95.htpx(T=866 * pyo.units.K, P=ip1_pin * pyo.units.Pa))
    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
Example #3
0
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
    ]
    for tag in m.data_param:
        col.append(tag + "_data")
        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
Example #5
0
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
Example #6
0
import idaes.logger as idaeslog
import idaes.core.util.model_serializer as ms
from idaes.core.util.model_statistics import large_residuals_set

meth = "normal" #normal, slide, or fixed

_log = idaeslog.getLogger(__name__)

if __name__ == "__main__":
    solver = pyo.SolverFactory("ipopt")
    m = main_steady(load_state="initial_steady.json.gz")
    strip_bounds = pyo.TransformationFactory("contrib.strip_var_bounds")
    strip_bounds.apply_to(m, reversible=False)
    # Start from the highest output case, this should already have the
    # estimated parameters loaded in from the validation run.
    ms.from_json(m, fname=f"results/state4_26.json.gz")

    _log.info("Make sure initial model setup is okay.")
    solver.solve(m, tee=True)

    # Add constraints for caol flow dependent operating variables, PA, O2, fheat_ww
    pa = m.fs_main.fs_blr.flow_pa_kg_per_s
    o2pct = m.fs_main.fs_blr.aBoiler.fluegas_o2_pct_dry
    fww = m.fs_main.fs_blr.aBoiler.fheat_ww
    coal_flow = m.fs_main.fs_blr.aBoiler.flowrate_coal_raw

    @m.fs_main.Constraint(m.fs_main.time)
    def pa_eqn(b, t):
        return pa[t] == 1.6569*coal_flow[t] + 16.581
    pa.unfix()
Example #7
0
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