def test_constructor_4():
    m_plant = make_model(horizon=6, ntfe=60, ntcp=2)
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2)
    sample_time = 0.5
    # Six samples per horizon, five elements per sample

    initial_plant_inputs = [
        m_plant.fs.mixer.S_inlet.flow_vol[0],
        m_plant.fs.mixer.E_inlet.flow_vol[0]
    ]

    # Fix some derivative vars, as in pseudo-steady state
    # Controller model only
    for t in m_controller.fs.time:
        m_controller.fs.cstr.control_volume.\
                energy_accumulation[t, 'aq'].fix(0)
        m_controller.fs.cstr.control_volume.\
                material_accumulation[t, 'aq', 'E'].fix(0)
    m_controller.fs.cstr.control_volume.\
            energy_holdup[0, 'aq'].unfix()
    m_controller.fs.cstr.control_volume.\
            material_holdup[0, 'aq', 'E'].unfix()
    m_controller.fs.cstr.control_volume.\
            energy_accumulation_disc_eq.deactivate()
    m_controller.fs.cstr.control_volume.\
            material_accumulation_disc_eq.deactivate()

    nmpc = NMPCSim(m_plant.fs,
                   m_plant.fs.time,
                   m_controller.fs,
                   m_controller.fs.time,
                   inputs_at_t0=initial_plant_inputs,
                   solver=solver,
                   outlvl=idaeslog.DEBUG,
                   sample_time=sample_time)
def test_constructor_3():
    m_plant = make_model(horizon=6, ntfe=60, ntcp=2)
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2)
    sample_time = 0.5
    # Six samples per horizon, five elements per sample

    initial_plant_inputs = [
        m_plant.fs.mixer.S_inlet.flow_vol[0],
        m_plant.fs.mixer.E_inlet.flow_vol[0]
    ]

    # energy_holdup should now be a fixed variable
    # (only in plant model...)
    m_plant.fs.cstr.control_volume.energy_holdup.fix(300)
    m_plant.fs.cstr.control_volume.energy_accumulation_disc_eq.deactivate()

    nmpc = NMPCSim(m_plant.fs,
                   m_plant.fs.time,
                   m_controller.fs,
                   m_controller.fs.time,
                   inputs_at_t0=initial_plant_inputs,
                   solver=solver,
                   outlvl=idaeslog.DEBUG,
                   sample_time=sample_time)

    assert_categorization(m_plant.fs)
示例#3
0
def test_constructor_1():
    m_plant = make_model(horizon=6, ntfe=60, ntcp=2)
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2)
    sample_time = 0.5
    # Six samples per horizon, five elements per sample

    initial_plant_inputs = [
        m_plant.fs.mixer.S_inlet.flow_vol[0],
        m_plant.fs.mixer.E_inlet.flow_vol[0]
    ]

    nmpc = NMPCSim(m_plant.fs,
                   m_plant.fs.time,
                   m_controller.fs,
                   m_controller.fs.time,
                   initial_plant_inputs,
                   solver=solver,
                   outlvl=idaeslog.DEBUG,
                   sample_time=sample_time)

    assert hasattr(nmpc, 'plant')
    assert hasattr(nmpc, 'controller')
    assert hasattr(nmpc, 'sample_time')

    plant = nmpc.plant
    controller = nmpc.controller

    assert hasattr(plant._NMPC_NAMESPACE, 'diff_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'deriv_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'alg_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'input_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'fixed_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'scalar_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'ic_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_diff_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_input_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_alg_vars')

    assert hasattr(controller._NMPC_NAMESPACE, 'diff_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'deriv_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'alg_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'input_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'fixed_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'scalar_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'ic_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_diff_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_input_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_alg_vars')
    assert_categorization(controller)
    assert_categorization(plant)
def test_categorize_error():
    model = make_model(horizon=1, ntfe=5, ntcp=2)
    time = model.fs.time

    scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)

    # Add a dummy var to treat as an input.
    # This var is not in `dae_vars`, so it will not be located during
    # categorization, which should fail.
    model.dummy_var = Var(time)

    init_input_list = [
        model.fs.mixer.S_inlet.flow_vol[0],
        model.fs.mixer.E_inlet.flow_vol[0],
        model.dummy_var[0],
    ]

    with pytest.raises(RuntimeError, match=r'Not all inputs could be found'):
        category_dict = categorize_dae_variables(
            dae_vars,
            time,
            init_input_list,
        )

    # Re-run flattener. Now `dummy_var` should be included in `dae_vars`.
    scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)
    category_dict = categorize_dae_variables(
        dae_vars,
        time,
        init_input_list,
    )
示例#5
0
def test_constructor_2():
    m_plant = make_model(horizon=6, ntfe=60, ntcp=2)
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2)
    sample_time = 0.5
    # Six samples per horizon, five elements per sample

    initial_plant_inputs = [
        m_plant.fs.mixer.S_inlet.flow_vol[0],
        m_plant.fs.mixer.E_inlet.flow_vol[0]
    ]

    # Change some initial conditions - in controller model only

    # Specify temperature instead of energy holdup
    m_controller.fs.cstr.control_volume.energy_holdup\
            [m_controller.fs.time.first(), 'aq'].unfix()
    m_controller.fs.cstr.outlet.temperature[0].fix(300)

    # Specify C_P instead of holdup
    m_controller.fs.cstr.control_volume.material_holdup\
            [m_controller.fs.time.first(), 'aq', 'P'].unfix()
    m_controller.fs.cstr.outlet.conc_mol[m_controller.fs.time.first(),
                                         'P'].fix(0)

    # specify \dot M_C insteady of M_C
    m_controller.fs.cstr.control_volume.material_holdup\
            [m_controller.fs.time.first(), 'aq', 'C'].unfix()
    m_controller.fs.cstr.control_volume.material_accumulation\
            [m_controller.fs.time.first(), 'aq', 'C'].fix(0)

    nmpc = NMPCSim(m_plant.fs,
                   m_plant.fs.time,
                   m_controller.fs,
                   m_controller.fs.time,
                   inputs_at_t0=initial_plant_inputs,
                   solver=solver,
                   outlvl=idaeslog.DEBUG,
                   sample_time=sample_time)
    # IPOPT output looks a little weird solving for initial conditions here...
    # has non-zero dual infeasibility, iteration 1 has a non-zero
    # regularization coefficient. (Would love to debug this with a
    # transparent NLP solver...)

    # Check that variables have been categorized properly
    assert_categorization(m_controller.fs)
示例#6
0
def test_initialize_by_element_in_range():
    mod = make_model(horizon=2, ntfe=20)
    assert degrees_of_freedom(mod) == 0

    scalar_vars, dae_vars = flatten_dae_variables(mod.fs, mod.fs.time)
    diff_vars = [
        Reference(mod.fs.cstr.control_volume.energy_holdup[:, 'aq']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'S']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'E']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'C']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'P'])
    ]

    initialize_by_element_in_range(mod.fs,
                                   mod.fs.time,
                                   0,
                                   1,
                                   solver=solver,
                                   dae_vars=dae_vars,
                                   time_linking_variables=diff_vars,
                                   outlvl=idaeslog.DEBUG,
                                   solve_initial_conditions=True)

    assert degrees_of_freedom(mod.fs) == 0

    assert mod.fs.cstr.outlet.conc_mol[1, 'S'].value == approx(10.189,
                                                               abs=1e-3)
    assert mod.fs.cstr.outlet.conc_mol[1, 'C'].value == approx(0.4275,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[1, 'E'].value == approx(0.0541,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[1, 'P'].value == approx(0.3503,
                                                               abs=1e-4)

    initialize_by_element_in_range(mod.fs,
                                   mod.fs.time,
                                   1,
                                   2,
                                   solver=solver,
                                   dae_vars=dae_vars,
                                   outlvl=idaeslog.DEBUG)

    assert degrees_of_freedom(mod.fs) == 0
    for con in activated_equalities_generator(mod.fs):
        assert value(con.body) - value(con.upper) < 1e-5

    assert mod.fs.cstr.outlet.conc_mol[2, 'S'].value == approx(11.263,
                                                               abs=1e-3)
    assert mod.fs.cstr.outlet.conc_mol[2, 'C'].value == approx(0.4809,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[2, 'E'].value == approx(0.0538,
                                                               abs=1e-4)
    assert mod.fs.cstr.outlet.conc_mol[2, 'P'].value == approx(0.4372,
                                                               abs=1e-4)
示例#7
0
def steady_cstr_model():
    return make_model(steady=True).fs
示例#8
0
def nmpc():
    # This tests the same model constructed in the test_nmpc_constructor_1 file
    m_plant = make_model(horizon=6, ntfe=60, ntcp=2)
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2, bounds=True)
    sample_time = 0.5
    # Six samples per horizon, five elements per sample

    initial_plant_inputs = [m_plant.fs.mixer.S_inlet.flow_vol[0],
                            m_plant.fs.mixer.E_inlet.flow_vol[0]]

    nmpc = NMPCSim(m_plant.fs, m_plant.fs.time,
            m_controller.fs, m_controller.fs.time,
            inputs_at_t0=initial_plant_inputs,
            solver=solver, outlvl=idaeslog.DEBUG,
            sample_time=sample_time)
    # IPOPT output looks a little weird solving for initial conditions here...
    # has non-zero dual infeasibility, iteration 1 has a non-zero
    # regularization coefficient. (Would love to debug this with a
    # transparent NLP solver...)

    assert hasattr(nmpc, 'plant')
    assert hasattr(nmpc, 'plant_time')
    assert hasattr(nmpc, 'controller')
    assert hasattr(nmpc, 'controller_time')
    assert hasattr(nmpc, 'sample_time')

    plant = nmpc.plant
    p_time = nmpc.plant_time
    controller = nmpc.controller
    c_time = nmpc.controller_time

    assert hasattr(plant, '_NMPC_NAMESPACE')
    assert hasattr(controller, '_NMPC_NAMESPACE')

    assert hasattr(plant._NMPC_NAMESPACE, 'diff_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'deriv_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'alg_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'input_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'fixed_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'scalar_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'ic_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_diff_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_deriv_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_input_vars')
    assert hasattr(plant._NMPC_NAMESPACE, 'n_alg_vars')

    assert hasattr(controller._NMPC_NAMESPACE, 'diff_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'deriv_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'alg_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'input_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'fixed_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'scalar_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'ic_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_diff_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_deriv_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_input_vars')
    assert hasattr(controller._NMPC_NAMESPACE, 'n_alg_vars')

    # Check that variables have been categorized properly
    assert_categorization(controller)
    assert_categorization(plant)

    return nmpc
def test_categorize_4():
    """
    This tests categorization when a psuedo-steady state
    approximation is used. Energy accumulation and accumulation of E
    are fixed, the corresponding initial conditions unfixed, and
    the corresponding discretization equations deactivated.
    """
    model = make_model(horizon=1, ntfe=5, ntcp=2)
    time = model.fs.time

    CV = model.fs.cstr.control_volume
    CV.energy_accumulation[:, 'aq'].fix(0.)
    CV.material_accumulation[:, 'aq', 'E'].fix(0.)
    CV.energy_holdup[0, 'aq'].unfix()
    CV.material_holdup[0, 'aq', 'E'].unfix()
    CV.energy_accumulation_disc_eq.deactivate()
    CV.material_accumulation_disc_eq.deactivate()

    init_input_list = [
        model.fs.mixer.S_inlet.flow_vol[0],
        model.fs.mixer.E_inlet.flow_vol[0],
    ]
    init_input_set = ComponentSet(init_input_list)

    init_deriv_list = [
        CV.material_accumulation[0, 'aq', 'C'],
        CV.material_accumulation[0, 'aq', 'S'],
        CV.material_accumulation[0, 'aq', 'P'],
        CV.material_accumulation[0, 'aq', 'Solvent'],
    ]
    init_deriv_set = ComponentSet(init_deriv_list)

    init_diff_list = [
        CV.material_holdup[0, 'aq', 'C'],
        CV.material_holdup[0, 'aq', 'S'],
        CV.material_holdup[0, 'aq', 'P'],
        CV.material_holdup[0, 'aq', 'Solvent'],
    ]
    init_diff_set = ComponentSet(init_diff_list)

    init_fixed_list = [
        model.fs.cstr.control_volume.energy_accumulation[0, 'aq'],
        CV.material_accumulation[0, 'aq', 'E'],
        model.fs.mixer.E_inlet.temperature[0],
        model.fs.mixer.S_inlet.temperature[0],
        *list(model.fs.mixer.E_inlet.conc_mol[0, :]),
        *list(model.fs.mixer.S_inlet.conc_mol[0, :]),
        model.fs.cstr.outlet.conc_mol[0, 'Solvent'],
        model.fs.mixer.outlet.conc_mol[0, 'Solvent'],
    ]
    init_fixed_set = ComponentSet(init_fixed_list)

    init_meas_list = [
        model.fs.cstr.control_volume.volume[0],
        CV.material_holdup[0, 'aq', 'P'],
        CV.material_holdup[0, 'aq', 'C'],
        CV.material_holdup[0, 'aq', 'S'],
    ]
    init_meas_set = ComponentSet(init_meas_list)

    init_alg_list = [
        model.fs.cstr.control_volume.energy_holdup[0, 'aq'],
        CV.material_holdup[0, 'aq', 'E'],
        model.fs.cstr.outlet.flow_vol[0],
        model.fs.cstr.outlet.temperature[0],
        model.fs.cstr.inlet.flow_vol[0],
        model.fs.cstr.inlet.temperature[0],
        model.fs.mixer.outlet.flow_vol[0],
        model.fs.mixer.outlet.temperature[0],
        model.fs.cstr.control_volume.volume[0],
        *list(model.fs.cstr.control_volume.properties_out[0].flow_mol_comp[:]),
        *list(model.fs.cstr.inlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.properties_in[0].flow_mol_comp[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_generation[0,
                                                                    'aq', :]),
        *list(model.fs.mixer.mixed_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.E_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.S_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.cstr.outlet.conc_mol[0, :]),
        *list(model.fs.mixer.outlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_coef[:]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_rate[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_extent[0, :]),
    ]
    init_alg_set = ComponentSet(init_alg_list)
    # solvent outlet concentrations are not algebraic variables as
    # it is fixed.
    init_alg_set.remove(model.fs.cstr.outlet.conc_mol[0, 'Solvent'])
    init_alg_set.remove(model.fs.mixer.outlet.conc_mol[0, 'Solvent'])

    scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)
    category_dict = categorize_dae_variables(dae_vars, time, init_input_list)

    input_vars = category_dict[VC.INPUT]
    diff_vars = category_dict[VC.DIFFERENTIAL]
    deriv_vars = category_dict[VC.DERIVATIVE]
    fixed_vars = category_dict[VC.FIXED]
    alg_vars = category_dict[VC.ALGEBRAIC]
    meas_vars = category_dict[VC.MEASUREMENT]

    assert len(input_vars) == len(init_input_set)
    for v in input_vars:
        assert v[0] in init_input_set

    assert len(deriv_vars) == len(init_deriv_set)
    for v in deriv_vars:
        assert v[0] in init_deriv_set

    assert len(diff_vars) == len(init_deriv_set)
    for v in diff_vars:
        assert v[0] in init_diff_set

    assert len(fixed_vars) == len(init_fixed_set)
    for v in fixed_vars:
        assert v[0] in init_fixed_set

    assert len(alg_vars) == len(init_alg_set)
    for v in alg_vars:
        assert v[0] in init_alg_set

    assert len(meas_vars) == len(init_meas_set)
    for v in meas_vars:
        assert v[0] in init_meas_set

    assert len(scalar_vars) == 0
示例#10
0
def main(plot_switch=False):

    # This tests the same model constructed in the test_nmpc_constructor_1 file
    m_plant = make_model(horizon=6, ntfe=60, ntcp=2)
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2, bounds=True)
    sample_time = 0.5
    time_plant = m_plant.fs.time

    n_plant_samples = (time_plant.last() - time_plant.first()) / sample_time
    assert n_plant_samples == int(n_plant_samples)
    n_plant_samples = int(n_plant_samples)

    plant_sample_points = [
        time_plant.first() + i * sample_time
        for i in range(1, n_plant_samples)
    ]
    for t in plant_sample_points:
        assert t in time_plant
    # Six samples per horizon, five elements per sample

    initial_plant_inputs = [
        m_plant.fs.mixer.S_inlet.flow_vol[0],
        m_plant.fs.mixer.E_inlet.flow_vol[0]
    ]

    nmpc = NMPCSim(plant_model=m_plant.fs,
                   plant_time_set=m_plant.fs.time,
                   controller_model=m_controller.fs,
                   controller_time_set=m_controller.fs.time,
                   inputs_at_t0=initial_plant_inputs,
                   solver=solver,
                   outlvl=idaeslog.DEBUG,
                   sample_time=sample_time)

    plant = nmpc.plant
    controller = nmpc.controller

    nmpc.solve_consistent_initial_conditions(plant)
    nmpc.solve_consistent_initial_conditions(controller)

    set_point = [(controller.cstr.outlet.conc_mol[0, 'P'], 0.4),
                 (controller.cstr.outlet.conc_mol[0, 'S'], 0.0),
                 (controller.cstr.control_volume.energy_holdup[0, 'aq'], 300),
                 (controller.mixer.E_inlet.flow_vol[0], 0.1),
                 (controller.mixer.S_inlet.flow_vol[0], 2.0)]
    # Interestingly, this (st.st. set point) solve converges infeasible
    # if energy_holdup set point is not 300. (Needs higher weight?)

    weight_tolerance = 5e-7

    # Weight overwrite expects a list of (VarData, value) tuples
    # in the STEADY MODEL
    weight_override = [(controller.mixer.E_inlet.flow_vol[0], 20.0)]

    nmpc.calculate_full_state_setpoint(
        set_point,
        objective_weight_override=weight_override,
        objective_weight_tolerance=weight_tolerance,
        outlvl=idaeslog.DEBUG,
        allow_inconsistent=False,
        tolerance=1e-6)

    nmpc.add_setpoint_to_controller()

    nmpc.constrain_control_inputs_piecewise_constant()

    nmpc.initialize_control_problem(
        control_init_option=ControlInitOption.FROM_INITIAL_CONDITIONS)

    nmpc.solve_control_problem()

    nmpc.inject_control_inputs_into_plant(time_plant.first(),
                                          add_input_noise=True)

    nmpc.simulate_plant(time_plant.first())

    for t in plant_sample_points:
        nmpc.transfer_current_plant_state_to_controller(t,
                                                        add_plant_noise=True)

        nmpc.initialize_control_problem(
            control_init_option=ControlInitOption.FROM_PREVIOUS)

        nmpc.solve_control_problem()

        nmpc.inject_control_inputs_into_plant(t, add_input_noise=True)

        nmpc.simulate_plant(t)

    # TODO: add option for specifying "user-interest variables"

    if plot_switch:
        temp_info = plant._NMPC_NAMESPACE.var_locator[
            plant.cstr.outlet.temperature[0.]]
        temp_location = temp_info.location
        temp_group = temp_info.group
        temperature_data = PlotData(temp_group,
                                    temp_location,
                                    name='Temperature')
        fig, ax = temperature_data.plot()
        fig.savefig(temperature_data.name)

        P_info = plant._NMPC_NAMESPACE.var_locator[plant.cstr.outlet.conc_mol[
            0., 'P']]
        P_location = P_info.location
        P_group = P_info.group
        P_data = PlotData(P_group, P_location, name='P_conc')
        fig, ax = P_data.plot()
        fig.savefig(P_data.name)

        S_info = plant._NMPC_NAMESPACE.var_locator[plant.cstr.outlet.conc_mol[
            0., 'S']]
        S_location = S_info.location
        S_group = S_info.group
        S_data = PlotData(S_group, S_location, name='S_conc')
        fig, ax = S_data.plot()
        fig.savefig(S_data.name)
示例#11
0
def test_categorize_3():
    """
    In this test we fix one of the differential variables.
    This is the case where somebody wants to run an isothermal
    CSTR.
    """
    model = make_model(horizon=1, ntfe=5, ntcp=2)
    time = model.fs.time

    CV = model.fs.cstr.control_volume
    CV.energy_holdup.fix(300)
    CV.energy_accumulation_disc_eq.deactivate()

    init_input_list = [
        model.fs.mixer.S_inlet.flow_vol[0],
        model.fs.mixer.E_inlet.flow_vol[0],
    ]
    init_input_set = ComponentSet(init_input_list)

    init_deriv_list = [
        *list(model.fs.cstr.control_volume.material_accumulation[0, 'aq', :]),
    ]
    init_deriv_set = ComponentSet(init_deriv_list)

    init_diff_list = [
        *list(model.fs.cstr.control_volume.material_holdup[0, 'aq', :]),
    ]
    init_diff_set = ComponentSet(init_diff_list)

    init_fixed_list = [
        # Energy holdup has been fixed
        model.fs.cstr.control_volume.energy_holdup[0, 'aq'],
        model.fs.mixer.E_inlet.temperature[0],
        model.fs.mixer.S_inlet.temperature[0],
        *list(model.fs.mixer.E_inlet.conc_mol[0, :]),
        *list(model.fs.mixer.S_inlet.conc_mol[0, :]),
        model.fs.cstr.outlet.conc_mol[0, 'Solvent'],
        model.fs.mixer.outlet.conc_mol[0, 'Solvent'],
    ]
    init_fixed_set = ComponentSet(init_fixed_list)

    init_meas_list = [
        model.fs.cstr.control_volume.volume[0],
        *list(model.fs.cstr.control_volume.material_holdup[0, 'aq', :]),
    ]
    init_meas_set = ComponentSet(init_meas_list)
    # Solvent holdup is not a measurement; we measure volume instead
    init_meas_set.remove(
        model.fs.cstr.control_volume.material_holdup[0, 'aq', 'Solvent'])

    init_alg_list = [
        # Since energy_holdup is fixed, energy_accumulation is not
        # considered a derivative. Instead it is algebraic.
        model.fs.cstr.control_volume.energy_accumulation[0, 'aq'],
        model.fs.cstr.outlet.flow_vol[0],
        model.fs.cstr.outlet.temperature[0],
        model.fs.cstr.inlet.flow_vol[0],
        model.fs.cstr.inlet.temperature[0],
        model.fs.mixer.outlet.flow_vol[0],
        model.fs.mixer.outlet.temperature[0],
        model.fs.cstr.control_volume.volume[0],
        *list(model.fs.cstr.control_volume.properties_out[0].flow_mol_comp[:]),
        *list(model.fs.cstr.inlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.properties_in[0].flow_mol_comp[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_generation[0,
                                                                    'aq', :]),
        *list(model.fs.mixer.mixed_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.E_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.S_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.cstr.outlet.conc_mol[0, :]),
        *list(model.fs.mixer.outlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_coef[:]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_rate[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_extent[0, :]),
    ]
    init_alg_set = ComponentSet(init_alg_list)
    # solvent outlet concentrations are not algebraic variables as
    # it is fixed.
    init_alg_set.remove(model.fs.cstr.outlet.conc_mol[0, 'Solvent'])
    init_alg_set.remove(model.fs.mixer.outlet.conc_mol[0, 'Solvent'])

    scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)
    category_dict = categorize_dae_variables(dae_vars, time, init_input_list)

    input_vars = category_dict[VC.INPUT]
    diff_vars = category_dict[VC.DIFFERENTIAL]
    deriv_vars = category_dict[VC.DERIVATIVE]
    fixed_vars = category_dict[VC.FIXED]
    alg_vars = category_dict[VC.ALGEBRAIC]
    meas_vars = category_dict[VC.MEASUREMENT]

    assert len(input_vars) == len(init_input_set)
    for v in input_vars:
        assert v[0] in init_input_set

    assert len(deriv_vars) == len(init_deriv_set)
    for v in deriv_vars:
        assert v[0] in init_deriv_set

    assert len(diff_vars) == len(init_deriv_set)
    for v in diff_vars:
        assert v[0] in init_diff_set

    assert len(fixed_vars) == len(init_fixed_set)
    for v in fixed_vars:
        assert v[0] in init_fixed_set

    assert len(alg_vars) == len(init_alg_set)
    for v in alg_vars:
        assert v[0] in init_alg_set

    assert len(meas_vars) == len(init_meas_set)
    for v in meas_vars:
        assert v[0] in init_meas_set

    assert len(scalar_vars) == 0
示例#12
0
def test_categorize_1():
    """
    This test categorizes the enzyme cstr "as intended."
    Volume is used as a measurement, and solvent flow rates are
    fixed, but otherwise equations are as expected.
    """
    model = make_model(horizon=1, ntfe=5, ntcp=2)
    time = model.fs.time

    init_input_list = [
        model.fs.mixer.S_inlet.flow_vol[0],
        model.fs.mixer.E_inlet.flow_vol[0],
    ]
    init_input_set = ComponentSet(init_input_list)

    init_deriv_list = [
        model.fs.cstr.control_volume.energy_accumulation[0, 'aq'],
        *list(model.fs.cstr.control_volume.material_accumulation[0, 'aq', :]),
    ]
    init_deriv_set = ComponentSet(init_deriv_list)

    init_diff_list = [
        model.fs.cstr.control_volume.energy_holdup[0, 'aq'],
        *list(model.fs.cstr.control_volume.material_holdup[0, 'aq', :]),
    ]
    init_diff_set = ComponentSet(init_diff_list)

    init_fixed_list = [
        model.fs.mixer.E_inlet.temperature[0],
        model.fs.mixer.S_inlet.temperature[0],
        *list(model.fs.mixer.E_inlet.conc_mol[0, :]),
        *list(model.fs.mixer.S_inlet.conc_mol[0, :]),
        model.fs.cstr.outlet.conc_mol[0, 'Solvent'],
        model.fs.mixer.outlet.conc_mol[0, 'Solvent'],
    ]
    init_fixed_set = ComponentSet(init_fixed_list)

    init_meas_list = [
        model.fs.cstr.control_volume.energy_holdup[0, 'aq'],
        model.fs.cstr.control_volume.volume[0],
        *list(model.fs.cstr.control_volume.material_holdup[0, 'aq', :]),
    ]
    init_meas_set = ComponentSet(init_meas_list)
    # Solvent holdup is not a measurement; we measure volume instead
    init_meas_set.remove(
        model.fs.cstr.control_volume.material_holdup[0, 'aq', 'Solvent'])

    init_alg_list = [
        model.fs.cstr.outlet.flow_vol[0],
        model.fs.cstr.outlet.temperature[0],
        model.fs.cstr.inlet.flow_vol[0],
        model.fs.cstr.inlet.temperature[0],
        model.fs.mixer.outlet.flow_vol[0],
        model.fs.mixer.outlet.temperature[0],
        model.fs.cstr.control_volume.volume[0],
        *list(model.fs.cstr.control_volume.properties_out[0].flow_mol_comp[:]),
        *list(model.fs.cstr.inlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.properties_in[0].flow_mol_comp[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_generation[0,
                                                                    'aq', :]),
        *list(model.fs.mixer.mixed_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.E_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.S_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.cstr.outlet.conc_mol[0, :]),
        *list(model.fs.mixer.outlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_coef[:]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_rate[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_extent[0, :]),
    ]
    init_alg_set = ComponentSet(init_alg_list)
    # solvent outlet concentrations are not algebraic variables as
    # it is fixed.
    init_alg_set.remove(model.fs.cstr.outlet.conc_mol[0, 'Solvent'])
    init_alg_set.remove(model.fs.mixer.outlet.conc_mol[0, 'Solvent'])

    scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)
    category_dict = categorize_dae_variables(dae_vars, time, init_input_list)

    input_vars = category_dict[VC.INPUT]
    diff_vars = category_dict[VC.DIFFERENTIAL]
    deriv_vars = category_dict[VC.DERIVATIVE]
    fixed_vars = category_dict[VC.FIXED]
    alg_vars = category_dict[VC.ALGEBRAIC]
    meas_vars = category_dict[VC.MEASUREMENT]

    assert len(input_vars) == len(init_input_set)
    for v in input_vars:
        assert v[0] in init_input_set

    assert len(deriv_vars) == len(init_deriv_set)
    for v in deriv_vars:
        assert v[0] in init_deriv_set

    assert len(diff_vars) == len(init_deriv_set)
    for v in diff_vars:
        assert v[0] in init_diff_set

    assert len(fixed_vars) == len(init_fixed_set)
    for v in fixed_vars:
        assert v[0] in init_fixed_set

    assert len(alg_vars) == len(init_alg_set)
    for v in alg_vars:
        assert v[0] in init_alg_set

    assert len(meas_vars) == len(init_meas_set)
    for v in meas_vars:
        assert v[0] in init_meas_set

    assert len(scalar_vars) == 0
示例#13
0
def test_categorize_2():
    """
    In this instance, temperature is "measured" (used as an initial condition)
    instead of energy_holdup, conc[P] is measured instead of holdup[P],
    and accumulation[C] is measured instead of holdup[C].
    """
    model = make_model(horizon=1, ntfe=5, ntcp=2)
    time = model.fs.time
    t0 = time.first()

    material_holdup = model.fs.cstr.control_volume.material_holdup
    material_accumulation = model.fs.cstr.control_volume.material_accumulation
    energy_holdup = model.fs.cstr.control_volume.energy_holdup
    energy_accumulation = model.fs.cstr.control_volume.energy_accumulation
    conc_mol = model.fs.cstr.outlet.conc_mol

    # Specify temperature instead of energy holdup
    energy_holdup[t0, 'aq'].unfix()
    model.fs.cstr.outlet.temperature[t0].fix(300)

    # Specify C_P instead of holdup
    material_holdup[t0, 'aq', 'P'].unfix()
    conc_mol[t0, 'P'].fix(0.)

    # Specify accumulation of C instead of holdup
    # You might want to do this if you want to start at steady state,
    # but don't know the value of every variable at the steady state
    # you want to start at...
    material_holdup[t0, 'aq', 'C'].unfix()
    material_accumulation[t0, 'aq', 'C'].fix(0.)

    init_input_list = [
        model.fs.mixer.S_inlet.flow_vol[0],
        model.fs.mixer.E_inlet.flow_vol[0],
    ]
    init_input_set = ComponentSet(init_input_list)

    init_deriv_list = [
        model.fs.cstr.control_volume.energy_accumulation[0, 'aq'],
        *list(model.fs.cstr.control_volume.material_accumulation[0, 'aq', :]),
    ]
    init_deriv_set = ComponentSet(init_deriv_list)

    init_diff_list = [
        model.fs.cstr.control_volume.energy_holdup[0, 'aq'],
        *list(model.fs.cstr.control_volume.material_holdup[0, 'aq', :]),
    ]
    init_diff_set = ComponentSet(init_diff_list)

    init_fixed_list = [
        model.fs.mixer.E_inlet.temperature[0],
        model.fs.mixer.S_inlet.temperature[0],
        *list(model.fs.mixer.E_inlet.conc_mol[0, :]),
        *list(model.fs.mixer.S_inlet.conc_mol[0, :]),
        model.fs.cstr.outlet.conc_mol[0, 'Solvent'],
        model.fs.mixer.outlet.conc_mol[0, 'Solvent'],
    ]
    init_fixed_set = ComponentSet(init_fixed_list)

    init_meas_list = [
        model.fs.cstr.outlet.temperature[0],
        model.fs.cstr.control_volume.volume[0],
        model.fs.cstr.control_volume.material_holdup[0, 'aq', 'S'],
        model.fs.cstr.control_volume.material_holdup[0, 'aq', 'E'],
        model.fs.cstr.control_volume.material_holdup[0, 'aq', 'S'],
        model.fs.cstr.control_volume.material_accumulation[0, 'aq', 'C'],
        model.fs.cstr.outlet.conc_mol[0, 'P'],
    ]
    init_meas_set = ComponentSet(init_meas_list)
    # No need to remove solvent holdup here as it was not added to this list.

    init_alg_list = [
        model.fs.cstr.outlet.flow_vol[0],
        model.fs.cstr.outlet.temperature[0],
        model.fs.cstr.inlet.flow_vol[0],
        model.fs.cstr.inlet.temperature[0],
        model.fs.mixer.outlet.flow_vol[0],
        model.fs.mixer.outlet.temperature[0],
        model.fs.cstr.control_volume.volume[0],
        *list(model.fs.cstr.control_volume.properties_out[0].flow_mol_comp[:]),
        *list(model.fs.cstr.inlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.properties_in[0].flow_mol_comp[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_generation[0,
                                                                    'aq', :]),
        *list(model.fs.mixer.mixed_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.E_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.mixer.S_inlet_state[0].flow_mol_comp[:]),
        *list(model.fs.cstr.outlet.conc_mol[0, :]),
        *list(model.fs.mixer.outlet.conc_mol[0, :]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_coef[:]),
        *list(model.fs.cstr.control_volume.reactions[0].reaction_rate[:]),
        *list(model.fs.cstr.control_volume.rate_reaction_extent[0, :]),
    ]
    init_alg_set = ComponentSet(init_alg_list)
    # solvent outlet concentrations are not algebraic variables as
    # it is fixed.
    init_alg_set.remove(model.fs.cstr.outlet.conc_mol[0, 'Solvent'])
    init_alg_set.remove(model.fs.mixer.outlet.conc_mol[0, 'Solvent'])

    scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)
    category_dict = categorize_dae_variables(dae_vars, time, init_input_list)

    input_vars = category_dict[VC.INPUT]
    diff_vars = category_dict[VC.DIFFERENTIAL]
    deriv_vars = category_dict[VC.DERIVATIVE]
    fixed_vars = category_dict[VC.FIXED]
    alg_vars = category_dict[VC.ALGEBRAIC]
    meas_vars = category_dict[VC.MEASUREMENT]

    assert len(input_vars) == len(init_input_set)
    for v in input_vars:
        assert v[0] in init_input_set

    assert len(deriv_vars) == len(init_deriv_set)
    for v in deriv_vars:
        assert v[0] in init_deriv_set

    assert len(diff_vars) == len(init_deriv_set)
    for v in diff_vars:
        assert v[0] in init_diff_set

    assert len(fixed_vars) == len(init_fixed_set)
    for v in fixed_vars:
        assert v[0] in init_fixed_set

    assert len(alg_vars) == len(init_alg_set)
    for v in alg_vars:
        assert v[0] in init_alg_set

    assert len(meas_vars) == len(init_meas_set)
    for v in meas_vars:
        assert v[0] in init_meas_set

    assert len(scalar_vars) == 0
示例#14
0
def test_add_noise_at_time():
    mod = make_model(horizon=2, ntfe=20)
    time = mod.fs.time
    t0 = time.first()
    assert degrees_of_freedom(mod) == 0

    scalar_vars, dae_vars = flatten_dae_variables(mod.fs, time)
    diff_vars = [
        Reference(mod.fs.cstr.control_volume.energy_holdup[:, 'aq']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'S']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'E']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'C']),
        Reference(mod.fs.cstr.control_volume.material_holdup[:, 'aq', 'P'])
    ]

    for t in time:
        diff_vars[0][t].setlb(290)
        diff_vars[0][t].setub(310)
        for i in range(1, 5):
            diff_vars[i][t].setlb(0)
            diff_vars[i][t].setub(1)
            # Pretend this is mole fraction...

    assert diff_vars[0][0].value == 300
    for i in range(1, 5):
        assert diff_vars[i][0].value == 0.001

    copy_values_at_time(diff_vars, diff_vars, [t for t in time if t != t0], t0)

    for seed in [4, 8, 15, 16, 23, 42]:
        random.seed(seed)
        weights = [10, 0.001, 0.001, 0.001, 0.001]
        nom_vals = add_noise_at_time(diff_vars, 0, weights=weights)

        assert nom_vals[0][0] == 300
        assert diff_vars[0][0].value != 300
        assert diff_vars[0][0].value == approx(300, abs=2)
        for i in range(1, 5):
            assert nom_vals[0][i] == 0.001
            # ^ nom_vals indexed by time, then var-index. This is confusing,
            # might need to change (or only accept one time point at a time)
            assert diff_vars[i][0].value != 0.001
            assert diff_vars[i][0].value == approx(0.001, abs=2e-4)
            # Within four standard deviations should be a safe check

        for i in range(0, 5):
            diff_vars[i][0].set_value(nom_vals[0][i])
        # Reset and try again with new seed

    # Try providing function for uniform random
    rand_fcn = random.uniform

    random_arg_dict = {
        'range_list': [(295, 305), (0.001, 0.01), (0.001, 0.01), (0.001, 0.01),
                       (0.001, 0.01)]
    }

    def args_fcn(i, val, **kwargs):
        # args_fcn expects arguments like this
        range_list = kwargs.pop('range_list', None)
        return range_list[i]

    nom_vals = add_noise_at_time(diff_vars,
                                 0.5,
                                 random_function=rand_fcn,
                                 args_function=args_fcn,
                                 random_arg_dict=random_arg_dict)

    assert nom_vals[0.5][0] == 300
    assert diff_vars[0][0.5].value != 300
    assert 295 <= diff_vars[0][0.5].value <= 305
    for i in range(1, 5):
        assert nom_vals[0.5][i] == 0.001
        assert diff_vars[i][0.5].value != 0.001
        assert 0.001 <= diff_vars[i][0.5].value <= 0.01

    # Try to get some bound violations
    random_arg_dict = {
        'range_list': [(295, 305), (1, 2), (1, 2), (1, 2), (1, 2)]
    }

    nom_vals = add_noise_at_time(diff_vars,
                                 1,
                                 random_function=rand_fcn,
                                 args_function=args_fcn,
                                 random_arg_dict=random_arg_dict,
                                 bound_strategy='push',
                                 bound_push=0.01)

    for i in range(1, 5):
        assert diff_vars[i][1].value == 0.99

    random.seed(123)
    with pytest.raises(ValueError) as exc_test:
        # Large weights - one of these lower bounds should fail...
        nom_vals = add_noise_at_time(diff_vars,
                                     1.5,
                                     bound_strategy='discard',
                                     discard_limit=0,
                                     weights=[1, 1, 1, 1, 1],
                                     sig_0=0.05)

    @pytest.mark.unit
    def test_get_violated_bounds_at_time():
        m = ConcreteModel()
        m.time = Set(initialize=[1, 2, 3])
        m.v = Var(m.time, ['a', 'b', 'c'], initialize=5)

        varlist = [
            Reference(m.v[:, 'a']),
            Reference(m.v[:, 'b']),
            Reference(m.v[:, 'c'])
        ]
        group = NMPCVarGroup(varlist, m.time)
        group.set_lb(0, 0)
        group.set_lb(1, 6)
        group.set_lb(2, 0)
        group.set_ub(0, 4)
        group.set_ub(1, 10)
        group.set_ub(2, 10)
        violated = get_violated_bounds_at_time(group, [1, 2, 3],
                                               tolerance=1e-8)
        violated_set = ComponentSet(violated)
        for t in m.time:
            assert m.v[t, 'a'] in violated_set
            assert m.v[t, 'b'] in violated_set

        violated = get_violated_bounds_at_time(group, 2, tolerance=1e-8)
        violated_set = ComponentSet(violated)
        assert m.v[2, 'a'] in violated_set
        assert m.v[2, 'b'] in violated_set
示例#15
0
def dynamic_cstr_model():
    return make_model().fs
示例#16
0
def main(plot_switch=False):

    # This tests the same model constructed in the test_nmpc_constructor_1 file
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2, bounds=True)
    sample_time = 0.5
    m_plant = make_model(horizon=sample_time, ntfe=5, ntcp=2)
    time_plant = m_plant.fs.time

    solve_consistent_initial_conditions(m_plant, time_plant, solver)

    #####
    # Flatten and categorize controller model
    #####
    model = m_controller
    time = model.fs.time
    t0 = time.first()
    t1 = time[2]
    scalar_vars, dae_vars = flatten_dae_components(
        model,
        time,
        pyo.Var,
    )
    scalar_cons, dae_cons = flatten_dae_components(
        model,
        time,
        pyo.Constraint,
    )
    inputs = [
        model.fs.mixer.S_inlet.flow_vol,
        model.fs.mixer.E_inlet.flow_vol,
    ]
    measurements = [
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'C']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'E']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'S']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'P']),
        model.fs.cstr.outlet.temperature,
    ]
    model.fs.cstr.control_volume.material_holdup[:, 'aq', 'Solvent'].fix()
    model.fs.cstr.total_flow_balance.deactivate()
    var_partition, con_partition = categorize_dae_variables_and_constraints(
        model,
        dae_vars,
        dae_cons,
        time,
        input_vars=inputs,
    )
    controller = ControllerBlock(
        model=model,
        time=time,
        measurements=measurements,
        category_dict={None: var_partition},
    )
    controller.construct()

    solve_consistent_initial_conditions(m_controller, time, solver)
    controller.initialize_to_initial_conditions()

    m_controller._dummy_obj = pyo.Objective(expr=0)
    nlp = PyomoNLP(m_controller)
    igraph = IncidenceGraphInterface(nlp)
    m_controller.del_component(m_controller._dummy_obj)
    diff_vars = [var[t1] for var in var_partition[VC.DIFFERENTIAL]]
    alg_vars = [var[t1] for var in var_partition[VC.ALGEBRAIC]]
    deriv_vars = [var[t1] for var in var_partition[VC.DERIVATIVE]]
    diff_eqns = [con[t1] for con in con_partition[CC.DIFFERENTIAL]]
    alg_eqns = [con[t1] for con in con_partition[CC.ALGEBRAIC]]

    # Assemble and factorize "derivative Jacobian"
    dfdz = nlp.extract_submatrix_jacobian(diff_vars, diff_eqns)
    dfdy = nlp.extract_submatrix_jacobian(alg_vars, diff_eqns)
    dgdz = nlp.extract_submatrix_jacobian(diff_vars, alg_eqns)
    dgdy = nlp.extract_submatrix_jacobian(alg_vars, alg_eqns)
    dfdzdot = nlp.extract_submatrix_jacobian(deriv_vars, diff_eqns)
    fact = sps.linalg.splu(dgdy.tocsc())
    dydz = fact.solve(dgdz.toarray())
    deriv_jac = dfdz - dfdy.dot(dydz)
    fact = sps.linalg.splu(dfdzdot.tocsc())
    dzdotdz = -fact.solve(deriv_jac)

    # Use some heuristic on the eigenvalues of the derivative Jacobian
    # to identify fast states.
    w, V = np.linalg.eig(dzdotdz)
    w_max = np.max(np.abs(w))
    fast_modes, = np.where(np.abs(w) > w_max / 2)
    fast_states = []
    for idx in fast_modes:
        evec = V[:, idx]
        _fast_states, _ = np.where(np.abs(evec) > 0.5)
        fast_states.extend(_fast_states)
    fast_states = set(fast_states)

    # Store components necessary for model reduction in a model-
    # independent form.
    fast_state_derivs = [
        pyo.ComponentUID(var_partition[VC.DERIVATIVE][idx].referent,
                         context=model) for idx in fast_states
    ]
    fast_state_diffs = [
        pyo.ComponentUID(var_partition[VC.DIFFERENTIAL][idx].referent,
                         context=model) for idx in fast_states
    ]
    fast_state_discs = [
        pyo.ComponentUID(con_partition[CC.DISCRETIZATION][idx].referent,
                         context=model) for idx in fast_states
    ]

    # Perform pseudo-steady state model reduction on the fast states
    # and re-categorize
    for cuid in fast_state_derivs:
        var = cuid.find_component_on(m_controller)
        var.fix(0.0)
    for cuid in fast_state_diffs:
        var = cuid.find_component_on(m_controller)
        var[t0].unfix()
    for cuid in fast_state_discs:
        con = cuid.find_component_on(m_controller)
        con.deactivate()

    var_partition, con_partition = categorize_dae_variables_and_constraints(
        model,
        dae_vars,
        dae_cons,
        time,
        input_vars=inputs,
    )
    controller.del_component(model)

    # Re-construct controller block with new categorization
    measurements = [
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'C']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'E']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'S']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'P']),
    ]
    controller = ControllerBlock(
        model=model,
        time=time,
        measurements=measurements,
        category_dict={None: var_partition},
    )
    controller.construct()

    #####
    # Construct dynamic block for plant
    #####
    model = m_plant
    time = model.fs.time
    t0 = time.first()
    t1 = time[2]
    scalar_vars, dae_vars = flatten_dae_components(
        model,
        time,
        pyo.Var,
    )
    scalar_cons, dae_cons = flatten_dae_components(
        model,
        time,
        pyo.Constraint,
    )
    inputs = [
        model.fs.mixer.S_inlet.flow_vol,
        model.fs.mixer.E_inlet.flow_vol,
    ]
    measurements = [
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'C']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'E']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'S']),
        pyo.Reference(model.fs.cstr.outlet.conc_mol[:, 'P']),
    ]
    model.fs.cstr.control_volume.material_holdup[:, 'aq', 'Solvent'].fix()
    model.fs.cstr.total_flow_balance.deactivate()

    var_partition, con_partition = categorize_dae_variables_and_constraints(
        model,
        dae_vars,
        dae_cons,
        time,
        input_vars=inputs,
    )
    plant = DynamicBlock(
        model=model,
        time=time,
        measurements=measurements,
        category_dict={None: var_partition},
    )
    plant.construct()

    p_t0 = plant.time.first()
    c_t0 = controller.time.first()
    p_ts = plant.sample_points[1]
    c_ts = controller.sample_points[1]

    controller.set_sample_time(sample_time)
    plant.set_sample_time(sample_time)

    # We now perform the "RTO" calculation: Find the optimal steady state
    # to achieve the following setpoint
    setpoint = [
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'P'], 0.4),
        #(controller.mod.fs.cstr.outlet.conc_mol[0, 'S'], 0.01),
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'S'], 0.1),
        (controller.mod.fs.cstr.control_volume.energy_holdup[0, 'aq'], 300),
        (controller.mod.fs.mixer.E_inlet.flow_vol[0], 0.1),
        (controller.mod.fs.mixer.S_inlet.flow_vol[0], 2.0),
        (controller.mod.fs.cstr.volume[0], 1.0),
    ]
    setpoint_weights = [
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'P'], 1.),
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'S'], 1.),
        (controller.mod.fs.cstr.control_volume.energy_holdup[0, 'aq'], 1.),
        (controller.mod.fs.mixer.E_inlet.flow_vol[0], 1.),
        (controller.mod.fs.mixer.S_inlet.flow_vol[0], 1.),
        (controller.mod.fs.cstr.volume[0], 1.),
    ]

    # Some of the "differential variables" that have been fixed in the
    # model file are different from the measurements listed above. We
    # unfix them here so the RTO solve is not overconstrained.
    # (The RTO solve will only automatically unfix inputs and measurements.)
    controller.mod.fs.cstr.control_volume.material_holdup[0, ...].unfix()
    controller.mod.fs.cstr.control_volume.energy_holdup[0, ...].unfix()
    #controller.mod.fs.cstr.volume[0].unfix()
    controller.mod.fs.cstr.control_volume.material_holdup[0, 'aq',
                                                          'Solvent'].fix()

    controller.add_setpoint_objective(setpoint, setpoint_weights)
    controller.solve_setpoint(solver)

    # Now we are ready to construct the tracking NMPC problem
    tracking_weights = [
        *((v, 1.) for v in controller.vectors.differential[:, 0]),
        *((v, 1.) for v in controller.vectors.input[:, 0]),
    ]

    controller.add_tracking_objective(tracking_weights)

    controller.constrain_control_inputs_piecewise_constant()

    controller.initialize_to_initial_conditions()

    # Solve the first control problem
    controller.vectors.input[...].unfix()
    controller.vectors.input[:, 0].fix()
    solver.solve(controller, tee=True)

    # For a proper NMPC simulation, we must have noise.
    # We do this by treating inputs and measurements as Gaussian random
    # variables with the following variances (and bounds).
    cstr = controller.mod.fs.cstr
    variance = [
        (cstr.outlet.conc_mol[0.0, 'S'], 0.01),
        (cstr.outlet.conc_mol[0.0, 'E'], 0.005),
        (cstr.outlet.conc_mol[0.0, 'C'], 0.01),
        (cstr.outlet.conc_mol[0.0, 'P'], 0.005),
        (cstr.outlet.temperature[0.0], 1.),
        (cstr.volume[0.0], 0.05),
    ]
    controller.set_variance(variance)
    measurement_variance = [
        v.variance for v in controller.MEASUREMENT_BLOCK[:].var
    ]
    measurement_noise_bounds = [(0.0, var[c_t0].ub)
                                for var in controller.MEASUREMENT_BLOCK[:].var]

    mx = plant.mod.fs.mixer
    variance = [
        (mx.S_inlet_state[0.0].flow_vol, 0.02),
        (mx.E_inlet_state[0.0].flow_vol, 0.001),
    ]
    plant.set_variance(variance)
    input_variance = [v.variance for v in plant.INPUT_BLOCK[:].var]
    input_noise_bounds = [(0.0, var[p_t0].ub)
                          for var in plant.INPUT_BLOCK[:].var]

    random.seed(100)

    # Extract inputs from controller and inject them into plant
    inputs = controller.generate_inputs_at_time(c_ts)
    plant.inject_inputs(inputs)

    # This "initialization" really simulates the plant with the new inputs.
    plant.vectors.input[:, :].fix()
    plant.initialize_by_solving_elements(solver)
    plant.vectors.input[:, :].fix()
    solver.solve(plant, tee=True)

    for i in range(1, 11):
        print('\nENTERING NMPC LOOP ITERATION %s\n' % i)
        measured = plant.generate_measurements_at_time(p_ts)
        plant.advance_one_sample()
        plant.initialize_to_initial_conditions()
        measured = apply_noise_with_bounds(
            measured,
            measurement_variance,
            random.gauss,
            measurement_noise_bounds,
        )

        controller.advance_one_sample()
        controller.load_measurements(measured)

        solver.solve(controller, tee=True)

        inputs = controller.generate_inputs_at_time(c_ts)
        inputs = apply_noise_with_bounds(
            inputs,
            input_variance,
            random.gauss,
            input_noise_bounds,
        )
        plant.inject_inputs(inputs)

        plant.initialize_by_solving_elements(solver)
        solver.solve(plant)

    import pdb
    pdb.set_trace()
示例#17
0
def main(plot_switch=False):

    # This tests the same model constructed in the test_nmpc_constructor_1 file
    m_controller = make_model(horizon=3, ntfe=30, ntcp=2, bounds=True)
    sample_time = 0.5
    m_plant = make_model(horizon=sample_time, ntfe=5, ntcp=2)
    time_plant = m_plant.fs.time

    simulation_horizon = 60
    n_samples_to_simulate = round(simulation_horizon / sample_time)

    samples_to_simulate = [
        time_plant.first() + i * sample_time
        for i in range(1, n_samples_to_simulate)
    ]

    # We must identify for the controller which variables are our
    # inputs and measurements.
    inputs = [
        m_plant.fs.mixer.S_inlet.flow_vol[0],
        m_plant.fs.mixer.E_inlet.flow_vol[0],
    ]
    measurements = [
        m_controller.fs.cstr.outlet.conc_mol[0, 'C'],
        m_controller.fs.cstr.outlet.conc_mol[0, 'E'],
        m_controller.fs.cstr.outlet.conc_mol[0, 'S'],
        m_controller.fs.cstr.outlet.conc_mol[0, 'P'],
        m_controller.fs.cstr.outlet.temperature[0],
        m_controller.fs.cstr.volume[0],
    ]

    # Construct the "NMPC simulator" object
    nmpc = NMPCSim(
        plant_model=m_plant,
        plant_time_set=m_plant.fs.time,
        controller_model=m_controller,
        controller_time_set=m_controller.fs.time,
        inputs_at_t0=inputs,
        measurements=measurements,
        sample_time=sample_time,
    )

    plant = nmpc.plant
    controller = nmpc.controller

    p_t0 = nmpc.plant.time.first()
    c_t0 = nmpc.controller.time.first()
    p_ts = nmpc.plant.sample_points[1]
    c_ts = nmpc.controller.sample_points[1]

    solve_consistent_initial_conditions(plant, plant.time, solver)
    solve_consistent_initial_conditions(controller, controller.time, solver)

    # We now perform the "RTO" calculation: Find the optimal steady state
    # to achieve the following setpoint
    setpoint = [
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'P'], 0.4),
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'S'], 0.0),
        (controller.mod.fs.cstr.control_volume.energy_holdup[0, 'aq'], 300),
        (controller.mod.fs.mixer.E_inlet.flow_vol[0], 0.1),
        (controller.mod.fs.mixer.S_inlet.flow_vol[0], 2.0),
        (controller.mod.fs.cstr.volume[0], 1.0),
    ]
    setpoint_weights = [
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'P'], 1.),
        (controller.mod.fs.cstr.outlet.conc_mol[0, 'S'], 1.),
        (controller.mod.fs.cstr.control_volume.energy_holdup[0, 'aq'], 1.),
        (controller.mod.fs.mixer.E_inlet.flow_vol[0], 1.),
        (controller.mod.fs.mixer.S_inlet.flow_vol[0], 1.),
        (controller.mod.fs.cstr.volume[0], 1.),
    ]

    # Some of the "differential variables" that have been fixed in the
    # model file are different from the measurements listed above. We
    # unfix them here so the RTO solve is not overconstrained.
    # (The RTO solve will only automatically unfix inputs and measurements.)
    nmpc.controller.mod.fs.cstr.control_volume.material_holdup[0, ...].unfix()
    nmpc.controller.mod.fs.cstr.control_volume.energy_holdup[0, ...].unfix()
    nmpc.controller.mod.fs.cstr.volume[0].unfix()

    nmpc.controller.add_setpoint_objective(setpoint, setpoint_weights)
    nmpc.controller.solve_setpoint(solver)

    # Now we are ready to construct the tracking NMPC problem
    tracking_weights = [
        *((v, 1.) for v in nmpc.controller.vectors.differential[:, 0]),
        *((v, 1.) for v in nmpc.controller.vectors.input[:, 0]),
    ]

    nmpc.controller.add_tracking_objective(tracking_weights)

    nmpc.controller.constrain_control_inputs_piecewise_constant()

    nmpc.controller.initialize_to_initial_conditions()

    # Solve the first control problem
    nmpc.controller.vectors.input[...].unfix()
    nmpc.controller.vectors.input[:, 0].fix()
    solver.solve(nmpc.controller, tee=True)

    # For a proper NMPC simulation, we must have noise.
    # We do this by treating inputs and measurements as Gaussian random
    # variables with the following variances (and bounds).
    cstr = nmpc.controller.mod.fs.cstr
    variance = [
        (cstr.outlet.conc_mol[0.0, 'S'], 0.2),
        (cstr.outlet.conc_mol[0.0, 'E'], 0.05),
        (cstr.outlet.conc_mol[0.0, 'C'], 0.1),
        (cstr.outlet.conc_mol[0.0, 'P'], 0.05),
        (cstr.outlet.temperature[0.0], 5.),
        (cstr.volume[0.0], 0.05),
    ]
    nmpc.controller.set_variance(variance)
    measurement_variance = [v.variance for v in controller.measurement_vars]
    measurement_noise_bounds = [(0.0, var[c_t0].ub)
                                for var in controller.measurement_vars]

    mx = plant.mod.fs.mixer
    variance = [
        (mx.S_inlet_state[0.0].flow_vol, 0.02),
        (mx.E_inlet_state[0.0].flow_vol, 0.001),
    ]
    nmpc.plant.set_variance(variance)
    input_variance = [v.variance for v in plant.input_vars]
    input_noise_bounds = [(0.0, var[p_t0].ub) for var in plant.input_vars]

    random.seed(246)

    # Extract inputs from controller and inject them into plant
    inputs = controller.generate_inputs_at_time(c_ts)
    plant.inject_inputs(inputs)

    # This "initialization" really simulates the plant with the new inputs.
    nmpc.plant.initialize_by_solving_elements(solver)
    solver.solve(nmpc.plant)

    for i in range(1, 11):
        print('\nENTERING NMPC LOOP ITERATION %s\n' % i)
        measured = nmpc.plant.generate_measurements_at_time(p_ts)
        nmpc.plant.advance_one_sample()
        nmpc.plant.initialize_to_initial_conditions()
        measured = apply_noise_with_bounds(
            measured,
            measurement_variance,
            random.gauss,
            measurement_noise_bounds,
        )

        nmpc.controller.advance_one_sample()
        nmpc.controller.load_measurements(measured)

        solver.solve(nmpc.controller, tee=True)

        inputs = controller.generate_inputs_at_time(c_ts)
        inputs = apply_noise_with_bounds(
            inputs,
            input_variance,
            random.gauss,
            input_noise_bounds,
        )
        plant.inject_inputs(inputs)

        nmpc.plant.initialize_by_solving_elements(solver)
        solver.solve(nmpc.plant)