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
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
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
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
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]
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)
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
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
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
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()
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))
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
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
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
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))
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))