Esempio n. 1
0
def main():
    m_ss = get_model(dynamic=False)
    m_dyn = get_model(dynamic=True)
    copy_non_time_indexed_values(m_dyn.fs, m_ss.fs, copy_fixed=True)
    for t in m_dyn.fs.config.time:
        copy_values_at_time(m_dyn.fs, m_ss.fs, t, 0.0, copy_fixed=True)
    m_dyn.fs.controller.mv_ref.value = m_dyn.fs.valve.valve_opening[0].value
    # calculate integral error assuming error is zero
    m_dyn.fs.controller.integral_of_error[:].value = 0

    dof = degrees_of_freedom(m_dyn)
    assert dof == 0
    run_dynamic(m_dyn)
    return m_dyn
Esempio n. 2
0
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 results.solver.termination_condition == TerminationCondition.optimal:
        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 results.solver.termination_condition == TerminationCondition.optimal:
            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')
Esempio n. 3
0
def main_dyn():
    start_time = wall_clock.time()
    # declare dictionary for the data to plot
    plot_data = {}
    plot_data['time'] = []
    plot_data['coal_flow'] = []
    plot_data['bfpt_opening'] = []
    plot_data['gross_power'] = []
    plot_data['ww_heat'] = []
    plot_data['fegt'] = []
    plot_data['drum_level'] = []
    plot_data['feed_water_flow_sp'] = []
    plot_data['drum_master_ctrl_op'] = []
    plot_data['feed_water_flow'] = []
    plot_data['spray_flow'] = []
    plot_data['main_steam_flow'] = []
    plot_data['rh_steam_flow'] = []
    plot_data['bfpt_flow'] = []
    plot_data['main_steam_temp'] = []
    plot_data['rh_steam_temp'] = []
    plot_data['fw_pres'] = []
    plot_data['drum_pres'] = []
    plot_data['main_steam_pres'] = []
    plot_data['rh_steam_pres'] = []
    plot_data['hw_tank_level'] = []
    plot_data['da_tank_level'] = []
    plot_data['fwh2_level'] = []
    plot_data['fwh3_level'] = []
    plot_data['fwh5_level'] = []
    plot_data['fwh6_level'] = []
    plot_data['makeup_valve_opening'] = []
    plot_data['cond_valve_opening'] = []
    plot_data['fwh2_valve_opening'] = []
    plot_data['fwh3_valve_opening'] = []
    plot_data['fwh5_valve_opening'] = []
    plot_data['fwh6_valve_opening'] = []
    plot_data['spray_valve_opening'] = []
    plot_data['tube_temp_rh2'] = []
    plot_data['t_fg_econ_exit'] = []
    plot_data['t_fg_aph_exit'] = []
    plot_data['throttle_opening'] = []
    plot_data['load_demand'] = []
    plot_data['sliding_pressure'] = []
    plot_data['steam_pressure_sp'] = []
    plot_data['gross_heat_rate'] = []
    plot_data['deaerator_pressure'] = []
    plot_data['SR'] = []
    plot_data['temp_econ_in'] = []
    plot_data['temp_econ_out'] = []
    plot_data['temp_econ_out_sat'] = []
    #plot_data['boiler_efficiency_heat'] = []
    #plot_data['boiler_efficiency_steam'] = []

    # steady-state model
    m_ss = get_model(dynamic=False)
    num_step = [2, 2, 2]
    step_size = [30, 60, 180]
    # 1st dynamic model with smallest time step size
    m_dyn0 = get_model(dynamic=True, time_set=[0, num_step[0]*step_size[0]], nstep=num_step[0])
    # 2nd dynamic model with intermediate time step size
    m_dyn1 = get_model(dynamic=True, time_set=[0, num_step[1]*step_size[1]], nstep=num_step[1])
    # 3rd dynamic model with largest time size
    m_dyn2 = get_model(dynamic=True, time_set=[0, num_step[2]*step_size[2]], nstep=num_step[2])
    # model type list, user input to specify the time duration
    itype_list = []
    for i in range(31): # to 1860 sec
        itype_list.append(0)
    # number of periods
    for i in range(20): # to 4260 sec
        itype_list.append(1)
    for i in range(30): # to 6060 sec
        itype_list.append(0)
    nperiod = len(itype_list)
    tstart = []
    model_list = []
    t = 0
    for i in range(nperiod):
        tstart.append(t)
        t += step_size[itype_list[i]]*num_step[itype_list[i]]
        if itype_list[i]==0:
            model_list.append(m_dyn0)
        elif itype_list[i]==1:
            model_list.append(m_dyn1)
        else:
            model_list.append(m_dyn2)
    # start first model
    m_dyn = model_list[0]
    copy_non_time_indexed_values(m_dyn.fs_main, m_ss.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
    for t in m_dyn.fs_main.config.time:
        copy_values_at_time(m_dyn.fs_main, m_ss.fs_main, t, 0.0, copy_fixed=True, outlvl=idaeslog.ERROR)
    t0 = m_dyn.fs_main.config.time.first()
    # reset bias of controller to current steady-state value, this makes both error and integral error to zero
    m_dyn.fs_main.fs_stc.fwh2_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.fwh2_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.fwh3_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.fwh3_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.fwh5_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.fwh5_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.fwh6_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.fwh6_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.makeup_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.makeup_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.da_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.cond_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.spray_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.spray_valve.valve_opening[t0].value
    opening = m_dyn.fs_main.fs_stc.spray_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.spray_ctrl.integral_of_error[:].value = pyo.value(m_dyn.fs_main.fs_stc.spray_ctrl.integral_of_error_ref[t0])
    opening = m_dyn.fs_main.fs_stc.makeup_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_stc.makeup_ctrl.integral_of_error[:].value = pyo.value(m_dyn.fs_main.fs_stc.makeup_ctrl.integral_of_error_ref[t0])

    m_dyn.fs_main.fs_blr.aDrum.level[t0].value = 0.889
    m_dyn.fs_main.flow_level_ctrl_output[t0].value = m_dyn.fs_main.fs_stc.bfp.outlet.flow_mol[t0].value

    m_dyn.fs_main.drum_slave_ctrl.mv_ref.value = m_dyn.fs_main.fs_stc.bfp_turb_valve.valve_opening[t0].value
    m_dyn.fs_main.fs_blr.aBoiler.flowrate_coal_raw[:].value = m_ss.fs_main.fs_blr.aBoiler.flowrate_coal_raw[t0].value

    m_dyn.fs_main.main_steam_pressure[:].value = m_ss.fs_main.fs_blr.aPlaten.outlet.pressure[t0].value/1e6
    m_dyn.fs_main.turbine_master_ctrl.mv_ref.value = m_ss.fs_main.fs_stc.turb.throttle_valve[1].valve_opening[t0].value
    m_dyn.fs_main.turbine_master_ctrl.setpoint[:].value = m_ss.fs_main.fs_stc.power_output[t0].value
    m_dyn.fs_main.boiler_master_ctrl.mv_ref.value = m_ss.fs_main.fs_blr.aBoiler.flowrate_coal_raw[t0].value
    m_dyn.fs_main.boiler_master_ctrl.setpoint[:].value = m_ss.fs_main.fs_blr.aPlaten.outlet.pressure[t0].value/1e6

    print('boiler_master_setpoint=', m_dyn.fs_main.boiler_master_ctrl.setpoint[0].value)
    print('sliding pressure=', pyo.value(m_dyn.fs_main.sliding_pressure[0]))
    solver = pyo.SolverFactory("ipopt")
    solver.options = {
            "tol": 1e-7,
            "linear_solver": "ma27",
            "max_iter": 20,
    }
    # copy non-time-indexed variables to all dynamic models
    if itype_list[0]==0:
        copy_non_time_indexed_values(m_dyn1.fs_main, m_dyn.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
        copy_non_time_indexed_values(m_dyn2.fs_main, m_dyn.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
    elif itype_list[0]==1:
        copy_non_time_indexed_values(m_dyn0.fs_main, m_dyn.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
        copy_non_time_indexed_values(m_dyn2.fs_main, m_dyn.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
    else:
        copy_non_time_indexed_values(m_dyn0.fs_main, m_dyn.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
        copy_non_time_indexed_values(m_dyn1.fs_main, m_dyn.fs_main, copy_fixed=True, outlvl=idaeslog.ERROR)
    dof = degrees_of_freedom(m_dyn)
    print('dof of full model', dof)
    # solving dynamic model at steady-state, this step could be skipped if all setup is good
    print('Solving dynamic model at steady-state...')
    results = solver.solve(m_dyn, tee=True)
    # start 1st simulation
    print("Solving for period number 0 from ", tstart[0], " sec.")
    ss_value = m_ss.fs_main.fs_stc.power_output[0].value
    m_dyn = run_dynamic(m_dyn, ss_value, tstart[0], plot_data, solver)

    # loop for remaining periods
    tlast = m_dyn.fs_main.config.time.last()
    m_prev = m_dyn
    for i in range(1,nperiod):
        m_dyn = model_list[i]
        for t in m_dyn.fs_main.config.time:
            if itype_list[i]!=itype_list[i-1] or t!=tlast:
                copy_values_at_time(m_dyn.fs_main, m_prev.fs_main, t, tlast, copy_fixed=True, outlvl=idaeslog.ERROR)
            print('windup=', m_dyn.fs_main.fs_stc.spray_ctrl.integral_of_error[t].value)
            p_only_with_ref = pyo.value(m_dyn.fs_main.fs_stc.spray_ctrl.mv_p_only_with_ref[t])
            if p_only_with_ref<pyo.value(m_dyn.fs_main.fs_stc.spray_ctrl.mv_lb):
                m_dyn.fs_main.fs_stc.spray_ctrl.integral_of_error[t].value = 0
                print('reachig lower bound. windup reset to 0')
            if p_only_with_ref>pyo.value(m_dyn.fs_main.fs_stc.spray_ctrl.mv_ub):
                m_dyn.fs_main.fs_stc.spray_ctrl.integral_of_error[t].value = 0
                print('reachig upper bound. windup reset to 0')
        print("Solving for period number ", i, "from ", tstart[i], " sec.")
        m_dyn = run_dynamic(m_dyn, ss_value, tstart[i], plot_data, solver)
        tlast = m_dyn.fs_main.config.time.last()
        m_prev = m_dyn
    end_time = wall_clock.time()
    time_used = end_time - start_time
    print("simulation time=", time_used)
    write_data_to_txt_file(plot_data)
    plot_results(plot_data)
    return m_dyn