Example #1
0
    def test_add_references(self):
        m = make_small_model()
        time = m.time
        t0 = time.first()
        blk = DynamicBlock(
                model=m,
                time=time,
                inputs=[m.flow_in[0]],
                measurements=[m.conc[0,'A'], m.conc[0,'B']],
                )
        blk.construct()

        attrs = [
                'differential',
                'algebraic',
                'derivative',
                'input',
                'fixed',
                'measurement',
                ]

        for attr in attrs:
            # Make sure we've added the expected attribute
            assert hasattr(blk.vectors, attr)
            vec = getattr(blk.vectors, attr)

            # These "vectors" should be two-dimensional;
            # indexed by a coordinate (index into a list) and by time.
            assert vec.dim() == 2
            assert list(vec.index_set().subsets())[1] is time

            # Make sure we can use the set/get setpoint methods.
            # This should be tested more extensively elsewhere.
            setpoints = list(range(len(list(vec[:,t0]))))
            vec.set_setpoint(setpoints)
            assert list(vec.get_setpoint()) == setpoints

            # Make sure the underlying var has the ctype we expect
            # (check using the attribute/component name)
            for var in vec._generate_referenced_vars():
                assert var.ctype._attr == attr
Example #2
0
 def make_block(self, sample_time=0.5, horizon=1, nfe=2):
     model = make_model(horizon=horizon, nfe=nfe)
     time = model.time
     t0 = time.first()
     inputs = [model.flow_in[t0]]
     measurements = [model.conc[t0, 'A'], model.conc[t0, 'B']]
     scalar_vars, dae_vars = flatten_dae_components(
         model,
         time,
         pyo.Var,
     )
     category_dict = categorize_dae_variables(dae_vars,
                                              time,
                                              inputs,
                                              measurements=measurements)
     dyn_block = DynamicBlock(
         model=model,
         time=time,
         category_dict={None: category_dict},
         #inputs=inputs,
         #measurements=measurements,
     )
     dyn_block.construct()
     dyn_block.set_sample_time(sample_time)
     return dyn_block
Example #3
0
    def test_empty_category(self):
        model = make_model(horizon=1, nfe=2)
        time = model.time
        t0 = time.first()
        inputs = [model.flow_in]
        measurements = [
            pyo.Reference(model.conc[:, 'A']),
            pyo.Reference(model.conc[:, 'B']),
        ]
        category_dict = {
            VC.INPUT: inputs,
            VC.MEASUREMENT: measurements,
            VC.ALGEBRAIC: [],
        }
        db = DynamicBlock(
            model=model,
            time=time,
            category_dict={None: category_dict},
        )
        db.construct()
        db.set_sample_time(0.5)

        # Categories with no variables are removed.
        # If they were retained, trying to iterate
        # over, e.g., vectors.algebraic[:, :] would
        # fail due to inconsistent dimension.
        assert VC.ALGEBRAIC not in db.category_dict
Example #4
0
    def test_extra_category(self):
        model = make_model(horizon=1, nfe=2)
        time = model.time
        t0 = time.first()
        inputs = [model.flow_in]
        measurements = [
            pyo.Reference(model.conc[:, 'A']),
            pyo.Reference(model.conc[:, 'B']),
        ]
        disturbances = [
            pyo.Reference(model.conc_in[:, 'A']),
            pyo.Reference(model.conc_in[:, 'B']),
        ]
        category_dict = {
            VC.INPUT: inputs,
            VC.MEASUREMENT: measurements,
            VC.DISTURBANCE: disturbances,
        }
        db = DynamicBlock(
            model=model,
            time=time,
            category_dict={None: category_dict},
        )
        db.construct()
        db.set_sample_time(0.5)

        db.vectors.input[:, t0].set_value(1.1)
        db.vectors.measurement[:, t0].set_value(2.2)
        db.vectors.disturbance[:, t0].set_value(3.3)
        assert model.flow_in[t0].value == 1.1
        assert model.conc[t0, 'A'].value == 2.2
        assert model.conc[t0, 'B'].value == 2.2
        assert model.conc_in[t0, 'A'].value == 3.3
        assert model.conc_in[t0, 'B'].value == 3.3
Example #5
0
    def test_set_sample_time(self):
        model = make_model(horizon=1, nfe=2)
        time = model.time
        t0 = time.first()
        inputs = [model.flow_in[t0]]
        measurements = [model.conc[t0,'A'], model.conc[t0,'B']]
        blk = DynamicBlock(
                model=model,
                time=time,
                inputs=inputs,
                measurements=measurements,
                )
        blk.construct()

        blk.set_sample_time(1.0)
        assert blk.sample_points == [0.0, 1.0]

        blk.set_sample_time(0.5)
        assert blk.sample_points == [0.0, 0.5, 1.0]
Example #6
0
    def test_initialize_only_measurement_input(self):
        model = make_model(horizon=1, nfe=2)
        time = model.time
        t0 = time.first()
        inputs = [model.flow_in]
        measurements = [
            pyo.Reference(model.conc[:, 'A']),
            pyo.Reference(model.conc[:, 'B']),
        ]
        category_dict = {
            VC.INPUT: inputs,
            VC.MEASUREMENT: measurements,
        }
        db = DynamicBlock(
            model=model,
            time=time,
            category_dict={None: category_dict},
        )
        db.construct()
        db.set_sample_time(0.5)

        db.mod.flow_in[:].set_value(3.0)
        initialize_t0(db.mod)
        copy_values_forward(db.mod)
        db.mod.flow_in[:].set_value(2.0)

        # Don't need to know any of the special categories to initialize
        # by element. This is only because we have an implicit discretization.
        db.initialize_by_solving_elements(solver)

        t0 = time.first()
        tl = time.last()
        vectors = db.vectors
        assert vectors.input[0, tl].value == 2.0
        assert vectors.measurement[0, tl].value == pytest.approx(
            3.185595567867036)
        assert vectors.measurement[1, tl].value == pytest.approx(
            1.1532474073395755)
        assert model.dcdt[tl, 'A'].value == pytest.approx(0.44321329639889284)
        assert model.dcdt[tl, 'B'].value == pytest.approx(0.8791007531878847)
Example #7
0
 def make_block(self, sample_time=0.5, horizon=1, nfe=2):
     model = make_model(horizon=horizon, nfe=nfe)
     time = model.time
     t0 = time.first()
     inputs = [model.flow_in[t0]]
     measurements = [model.conc[t0,'A'], model.conc[t0,'B']]
     dyn_block = DynamicBlock(
             model=model,
             time=time,
             inputs=inputs,
             measurements=measurements,
             )
     dyn_block.construct()
     dyn_block.set_sample_time(sample_time)
     return dyn_block
Example #8
0
    def __init__(self,
                 plant_model=None,
                 plant_time_set=None,
                 controller_model=None,
                 controller_time_set=None,
                 inputs_at_t0=None,
                 measurements=None,
                 sample_time=None,
                 **kwargs):
        """
        Measurements must be defined in the controller model.
        Inputs must be defined in the plant model.
        """
        # To find components in a model given a name,
        # modulo the index of some set:
        # i.   slice the component along the set
        # ii.  create a cuid from that slice
        # iii. get a reference to the slice from the cuid on the new model
        # iv.  access the reference at the index you want (optional)
        self.measurement_cuids = [
            ComponentUID(
                slice_component_along_sets(comp, (controller_time_set, )))
            for comp in measurements
        ]
        self.input_cuids = [
            ComponentUID(slice_component_along_sets(comp, (plant_time_set, )))
            for comp in inputs_at_t0
        ]

        p_t0 = plant_time_set.first()
        init_plant_measurements = [
            cuid.find_component_on(plant_model)[p_t0]
            for cuid in self.measurement_cuids
        ]

        self.plant = DynamicBlock(
            model=plant_model,
            time=plant_time_set,
            inputs=inputs_at_t0,
            measurements=init_plant_measurements,
        )
        self.plant.construct()

        # Here we repeat essentially the same "find component"
        # procedure as above.
        c_t0 = controller_time_set.first()
        init_controller_inputs = [
            cuid.find_component_on(controller_model)[c_t0]
            for cuid in self.input_cuids
        ]
        self.controller = ControllerBlock(
            model=controller_model,
            time=controller_time_set,
            inputs=init_controller_inputs,
            measurements=measurements,
        )
        self.controller.construct()

        if sample_time is not None:
            self.controller.set_sample_time(sample_time)
            self.plant.set_sample_time(sample_time)
            self.sample_time = sample_time
Example #9
0
class NMPCSim(object):
    """
    This is a user-facing class to perform NMPC simulations with Pyomo
    models for both plant and controller. The user must provide the
    models to use for each, along with sets to treat as "time,"
    inputs in the plant model, and measurements in the controller
    model. Its functionality is primarily to ensure that these components
    (as defined by the names relative to the corresponding provided models)
    exist on both models.
    """

    # TODO: pyomo.common.config.add_docstring_list

    def __init__(self,
                 plant_model=None,
                 plant_time_set=None,
                 controller_model=None,
                 controller_time_set=None,
                 inputs_at_t0=None,
                 measurements=None,
                 sample_time=None,
                 **kwargs):
        """
        Measurements must be defined in the controller model.
        Inputs must be defined in the plant model.
        """
        # To find components in a model given a name,
        # modulo the index of some set:
        # i.   slice the component along the set
        # ii.  create a cuid from that slice
        # iii. get a reference to the slice from the cuid on the new model
        # iv.  access the reference at the index you want (optional)
        self.measurement_cuids = [
            ComponentUID(
                slice_component_along_sets(comp, (controller_time_set, )))
            for comp in measurements
        ]
        self.input_cuids = [
            ComponentUID(slice_component_along_sets(comp, (plant_time_set, )))
            for comp in inputs_at_t0
        ]

        p_t0 = plant_time_set.first()
        init_plant_measurements = [
            cuid.find_component_on(plant_model)[p_t0]
            for cuid in self.measurement_cuids
        ]

        self.plant = DynamicBlock(
            model=plant_model,
            time=plant_time_set,
            inputs=inputs_at_t0,
            measurements=init_plant_measurements,
        )
        self.plant.construct()

        # Here we repeat essentially the same "find component"
        # procedure as above.
        c_t0 = controller_time_set.first()
        init_controller_inputs = [
            cuid.find_component_on(controller_model)[c_t0]
            for cuid in self.input_cuids
        ]
        self.controller = ControllerBlock(
            model=controller_model,
            time=controller_time_set,
            inputs=init_controller_inputs,
            measurements=measurements,
        )
        self.controller.construct()

        if sample_time is not None:
            self.controller.set_sample_time(sample_time)
            self.plant.set_sample_time(sample_time)
            self.sample_time = sample_time
Example #10
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()
Example #11
0
    def test_init_simple(self):
        model = make_model(horizon=1, nfe=2)
        time = model.time
        t0 = time.first()
        inputs = [model.flow_in[t0]]
        measurements = [model.conc[0,'A'], model.conc[0,'B']]
        block = DynamicBlock(
                model=model,
                time=time,
                inputs=inputs,
                measurements=measurements,
                )
        # Assert that we have the correct type
        assert type(block) is SimpleDynamicBlock
        assert isinstance(block, DynamicBlock)
        assert isinstance(block, _DynamicBlockData)

        block.construct()
        # Assert that we behave like a simple block
        assert block[None] is block
        assert all(b is block for b in block[:])

        # Assert that input attributes have been processed correctly
        assert block.mod is model
        assert block.time is time
        assert all(i1 is i2 for i1, i2 in zip(block._inputs, inputs))
        assert all(i1 is i2 for i1, i2 in zip(block._measurements, measurements))

        # Assert that utility attributes have been added
        assert hasattr(block, 'category_dict')
        assert hasattr(block, 'vardata_map')
        assert hasattr(block, 'measurement_vars')
        assert hasattr(block, 'differential_vars')
        assert hasattr(block, 'algebraic_vars')
        assert hasattr(block, 'derivative_vars')
        assert hasattr(block, 'input_vars')
        assert hasattr(block, 'fixed_vars')

        subblocks = [
                block.mod,
                block.vectors,
                block.DIFFERENTIAL_BLOCK,
                block.ALGEBRAIC_BLOCK,
                block.INPUT_BLOCK,
                block.FIXED_BLOCK,
                block.DERIVATIVE_BLOCK,
                block.MEASUREMENT_BLOCK,
                ]

        block_objects = ComponentSet(
                block.component_objects(pyo.Block, descend_into=False))
        # Assert that subblocks have been added
        assert len(subblocks) == len(block_objects)
        for b in subblocks:
            assert b in block_objects

        # Assert that we can add variables and constraints to the block
        block.v = pyo.Var(initialize=3)
        block.c = pyo.Constraint(expr=block.v==5)
        assert block.v.value == 3
        assert block.v in ComponentSet(identify_variables(block.c.expr))
Example #12
0
    def test_validate_sample_time(self):
        model = make_model(horizon=1, nfe=2)
        time = model.time
        t0 = time.first()
        inputs = [model.flow_in[t0]]
        measurements = [model.conc[t0,'A'], model.conc[t0,'B']]
        blk = DynamicBlock(
                model=model,
                time=time,
                inputs=inputs,
                measurements=measurements,
                )
        blk.construct()
        blk.validate_sample_time(0.5)
        assert hasattr(blk, 'sample_points')
        assert hasattr(blk, 'fe_per_sample')
        assert hasattr(blk, 'sample_point_indices')
        
        sample_point_set = set(blk.sample_points)
        sample_point_indices = set(blk.sample_point_indices)
        for p in [0.0, 0.5, 1.0]:
            assert p in sample_point_set
        for i in [1, 3, 5]:
            assert i in sample_point_indices
        assert len(sample_point_set) == 3
        assert len(sample_point_indices) == 3

        with pytest.raises(ValueError, match=r".*integer divider.*"):
            blk.validate_sample_time(0.6)

        with pytest.raises(ValueError, match=r"Could not find a time point.*"):
            blk.validate_sample_time(1/3.)

        with pytest.raises(ValueError, 
                match=r".*tolerance is larger than.*not.*unique.*"):
            blk.validate_sample_time(0.5, tolerance=0.09)
            # min spacing in continuous set: 0.166667

        blk.validate_sample_time(0.5, tolerance=0.08)
        sample_point_set = set(blk.sample_points)
        for p in [0.0, 0.5, 1.0]:
            assert p in sample_point_set
        for i in [1, 3, 5]:
            assert i in sample_point_indices
        assert len(sample_point_set) == 3
        assert len(sample_point_indices) == 3
Example #13
0
    def test_category_blocks(self):
        m = make_small_model()
        time = m.time
        t0 = time.first()
        helper = DynamicBlock(
                model=m,
                time=time,
                inputs=[m.flow_in[0]],
                measurements=[m.conc[0,'A'], m.conc[0,'B']],
                )
        helper.construct()

        # Test that NmpcVectors and category blocks behave
        # as we expect
        diff_vars = helper.vectors.differential
        diff_var_set = helper.DIFFERENTIAL_SET*m.time
        assert diff_vars.index_set() == diff_var_set
        # And that they contain the same variables as the corresponding
        # lists on our helper block.
        for b, v in zip(helper.DIFFERENTIAL_BLOCK.values(),
                helper.differential_vars):
            assert b.var is v

        helper.vectors.derivative.set_setpoint(0.0)
        for var in helper.DERIVATIVE_BLOCK[:].var:
            assert var.setpoint == 0.
        for b, v in zip(helper.DERIVATIVE_BLOCK.values(),
                helper.derivative_vars):
            assert b.var is v

        sp = [1,2,3]
        helper.vectors.algebraic.set_setpoint(sp)
        for i,j in zip(helper.ALGEBRAIC_SET, sp):
            assert helper.ALGEBRAIC_BLOCK[i].var.setpoint == j

        alg_vars = list(helper.component_objects(AlgVar))
        pred_alg_vars = ComponentSet((
                m.flow_out[t0],
                m.rate[t0,'A'],
                m.rate[t0,'B'],
                ))
        assert len(pred_alg_vars) == len(alg_vars)
        for var in alg_vars:
            assert var[t0] in pred_alg_vars
        for b, v in zip(helper.ALGEBRAIC_BLOCK.values(),
                helper.algebraic_vars):
            assert b.var is v

        assert list(helper.vectors.algebraic.get_setpoint()) == sp

        vals = (10, 11) 
        diff_vars.values = vals
        for var, val in zip(helper.DIFFERENTIAL_BLOCK[:].var, vals):
            for v in var[:]:
                assert v.value == val 
    
        _slice = helper.DIFFERENTIAL_BLOCK[:].var
        diff_var_vals = diff_vars.values
        for var, vals in zip(_slice, diff_var_vals):
            for v, vl in zip(var[:], vals):
                assert v.value == vl
Example #14
0
    def test_construct(self):
        m = make_small_model()
        time = m.time
        t0 = time.first()
        helper = DynamicBlock(
                model=m,
                time=time,
                inputs=[m.flow_in[0]],
                measurements=[m.conc[0,'A'], m.conc[0,'B']])
        helper.construct()
        assert hasattr(helper, 'category_dict')
        assert hasattr(helper, 'vardata_map')
        assert hasattr(helper, 'measurement_vars')
        assert hasattr(helper, 'differential_vars')
        assert hasattr(helper, 'algebraic_vars')
        assert hasattr(helper, 'derivative_vars')
        assert hasattr(helper, 'input_vars')
        assert hasattr(helper, 'fixed_vars')

        # Make sure category dict contains variables we expect
        assert VariableCategory.DIFFERENTIAL in helper.category_dict
        assert VariableCategory.ALGEBRAIC in helper.category_dict
        assert VariableCategory.DERIVATIVE in helper.category_dict
        assert VariableCategory.INPUT in helper.category_dict
        assert VariableCategory.FIXED in helper.category_dict
    
        pred_diff_vars = ComponentSet((
                m.conc[t0,'A'],
                m.conc[t0,'B'],
                ))
        diff_vars = list(helper.component_objects(DiffVar))
        assert len(pred_diff_vars) == len(diff_vars)
        for var in diff_vars:
            assert var[t0] in pred_diff_vars

        pred_alg_vars = ComponentSet((
                m.flow_out[t0],
                m.rate[t0,'A'],
                m.rate[t0,'B'],
                ))
        alg_vars = list(helper.component_objects(AlgVar))
        assert len(pred_alg_vars) == len(alg_vars)
        for var in alg_vars:
            assert var[t0] in pred_alg_vars

        pred_input_vars = ComponentSet((
                m.flow_in[t0],
                ))
        input_vars = list(helper.component_objects(InputVar))
        assert len(pred_input_vars) == len(input_vars)
        for var in input_vars:
            assert var[t0] in pred_input_vars

        pred_fixed_vars = ComponentSet((
                m.conc_in[t0,'A'],
                m.conc_in[t0,'B'],
                ))
        fixed_vars = list(helper.component_objects(FixedVar))
        assert len(pred_fixed_vars) == len(fixed_vars)
        for var in fixed_vars:
            assert var[t0] in pred_fixed_vars

        pred_deriv_vars = ComponentSet((
                m.dcdt[t0,'A'],
                m.dcdt[t0,'B'],
                ))
        deriv_vars = list(helper.component_objects(DerivVar))
        assert len(pred_deriv_vars) == len(deriv_vars)
        for var in deriv_vars:
            assert var[t0] in pred_deriv_vars
Example #15
0
    def test_init_rule(self):
        block_set = pyo.Set(initialize=range(3))
        block_set.construct()
        # Create same maps as before
        horizon_map = {0: 1, 1: 3, 2: 5}
        nfe_map = {0: 2, 1: 6, 2: 10}
        model_map = {
                i: make_model(horizon=horizon_map[i], nfe=nfe_map[i])
                for i in block_set
                }

        # Create rule to construct DynamicBlock with
        def dynamic_block_rule(b, i):
            model = model_map[i]
            time = model.time
            t0 = time.first()
            inputs = [model.flow_in[t0]]
            measurements = [model.conc[0,'A'], model.conc[0,'B']]

            # Won't be obvious that these attrs need to be set if
            # constructing from a rule
            b.mod = model
            super(_BlockData, b).__setattr__('time', time)
            b._inputs = inputs
            b._measurements = measurements

        # Create DynamicBlock from a rule
        block = DynamicBlock(block_set, rule=dynamic_block_rule)
        assert type(block) is IndexedDynamicBlock
        assert isinstance(block, DynamicBlock)

        block.construct()

        # Make sure iterating over block.values works as expected
        assert all(b.parent_component() is block for b in block.values())

        # Make sure __contains__ works
        for i in block_set:
            assert i in block

        # Assert correct attributes and subblocks
        for i, b in block.items():
            assert b.mod is model_map[i]
            assert b.time is model_map[i].time
            t0 = b.time.first()
            assert all(i1 is i2 for i1, i2 in zip(b._inputs, 
                [model_map[i].flow_in[t0]]))
            assert all(i1 is i2 for i1, i2 in zip(b._measurements, 
                [model_map[i].conc[t0,'A'], model_map[i].conc[t0,'B']]))

            assert hasattr(b, 'category_dict')
            assert hasattr(b, 'vardata_map')
            assert hasattr(b, 'measurement_vars')
            assert hasattr(b, 'differential_vars')
            assert hasattr(b, 'algebraic_vars')
            assert hasattr(b, 'derivative_vars')
            assert hasattr(b, 'input_vars')
            assert hasattr(b, 'fixed_vars')

            subblocks = [
                    b.mod,
                    b.vectors,
                    b.DIFFERENTIAL_BLOCK,
                    b.ALGEBRAIC_BLOCK,
                    b.INPUT_BLOCK,
                    b.FIXED_BLOCK,
                    b.DERIVATIVE_BLOCK,
                    b.MEASUREMENT_BLOCK,
                    ]

            block_objects = ComponentSet(
                    b.component_objects(pyo.Block, descend_into=False))
            assert len(subblocks) == len(block_objects)
            for sb in subblocks:
                assert sb in block_objects

            b.v = pyo.Var(initialize=3)
            b.c = pyo.Constraint(expr=b.v==5)
            assert b.v.value == 3
            assert b.v in ComponentSet(identify_variables(b.c.expr))
Example #16
0
    def test_init_indexed(self):
        block_set = pyo.Set(initialize=[0,1,2])
        block_set.construct()
        horizon_map = {0: 1., 1: 3., 2: 5.}
        nfe_map = {0: 2, 1: 6, 2: 10}
        model_map = {i: make_model(horizon_map[i], nfe_map[i])
                for i in block_set}
        time_map = {i: model_map[i].time for i in block_set}
        inputs_map = {i: [model_map[i].flow_in[0]] for i in block_set}
        measurements_map = {
                i: [model_map[i].conc[0,'A'], model_map[i].conc[0,'B']]
                for i in block_set
                }
        # Construct block with a dict for each of its arguments
        block = DynamicBlock(
                block_set,
                model=model_map,
                time=time_map,
                inputs=inputs_map,
                measurements=measurements_map,
                )
        # Make sure we have the right type
        assert type(block) is IndexedDynamicBlock
        assert isinstance(block, DynamicBlock)

        block.construct()
        assert all(b.parent_component() is block for b in block.values())

        # Check __contains__
        for i in block_set:
            assert i in block

        # Check attributes and subblocks of each data object
        for i, b in block.items():
            assert b.mod is model_map[i]
            assert b.time is time_map[i]
            assert all(i1 is i2 for i1, i2 in zip(b._inputs, inputs_map[i]))
            assert all(i1 is i2 for i1, i2 in 
                    zip(b._measurements, measurements_map[i]))

            assert hasattr(b, 'category_dict')
            assert hasattr(b, 'vardata_map')
            assert hasattr(b, 'measurement_vars')
            assert hasattr(b, 'differential_vars')
            assert hasattr(b, 'algebraic_vars')
            assert hasattr(b, 'derivative_vars')
            assert hasattr(b, 'input_vars')
            assert hasattr(b, 'fixed_vars')

            subblocks = [
                    b.mod,
                    b.vectors,
                    b.DIFFERENTIAL_BLOCK,
                    b.ALGEBRAIC_BLOCK,
                    b.INPUT_BLOCK,
                    b.FIXED_BLOCK,
                    b.DERIVATIVE_BLOCK,
                    b.MEASUREMENT_BLOCK,
                    ]

            block_objects = ComponentSet(
                    b.component_objects(pyo.Block, descend_into=False))
            assert len(subblocks) == len(block_objects)
            for sb in subblocks:
                assert sb in block_objects

            b.v = pyo.Var(initialize=3)
            b.c = pyo.Constraint(expr=b.v==5)
            assert b.v.value == 3
            assert b.v in ComponentSet(identify_variables(b.c.expr))