def test_perfect_matching(self): model = make_gas_expansion_model() # These are the variables and constraints of the square, # nonsingular subsystem variables = [] variables.extend(model.P.values()) variables.extend(model.T[i] for i in model.streams if i != model.streams.first()) variables.extend(model.rho[i] for i in model.streams if i != model.streams.first()) variables.extend(model.F[i] for i in model.streams if i != model.streams.first()) constraints = list(model.component_data_objects(pyo.Constraint)) imat = get_numeric_incidence_matrix(variables, constraints) con_idx_map = ComponentMap((c, i) for i, c in enumerate(constraints)) n_var = len(variables) matching = maximum_matching(imat) matching = ComponentMap( (c, variables[matching[con_idx_map[c]]]) for c in constraints) values = ComponentSet(matching.values()) self.assertEqual(len(matching), n_var) self.assertEqual(len(values), n_var) # The subset of variables and equations we have identified # do not have a unique perfect matching. But we at least know # this much. self.assertIs(matching[model.ideal_gas[0]], model.P[0])
def test_imperfect_matching(self): model = make_gas_expansion_model() all_vars = list(model.component_data_objects(pyo.Var)) all_cons = list(model.component_data_objects(pyo.Constraint)) imat = get_numeric_incidence_matrix(all_vars, all_cons) n_eqn = len(all_cons) matching = maximum_matching(imat) values = set(matching.values()) self.assertEqual(len(matching), n_eqn) self.assertEqual(len(values), n_eqn)
def test_triangularize(self): N = 5 model = make_gas_expansion_model(N) # These are the variables and constraints of the square, # nonsingular subsystem variables = [] variables.extend(model.P.values()) variables.extend(model.T[i] for i in model.streams if i != model.streams.first()) variables.extend(model.rho[i] for i in model.streams if i != model.streams.first()) variables.extend(model.F[i] for i in model.streams if i != model.streams.first()) constraints = list(model.component_data_objects(pyo.Constraint)) imat = get_numeric_incidence_matrix(variables, constraints) con_idx_map = ComponentMap((c, i) for i, c in enumerate(constraints)) var_idx_map = ComponentMap((v, i) for i, v in enumerate(variables)) row_block_map, col_block_map = block_triangularize(imat) var_block_map = ComponentMap( (v, col_block_map[var_idx_map[v]]) for v in variables) con_block_map = ComponentMap( (c, row_block_map[con_idx_map[c]]) for c in constraints) var_values = set(var_block_map.values()) con_values = set(con_block_map.values()) self.assertEqual(len(var_values), N + 1) self.assertEqual(len(con_values), N + 1) self.assertEqual(var_block_map[model.P[0]], 0) for i in model.streams: if i != model.streams.first(): self.assertEqual(var_block_map[model.rho[i]], i) self.assertEqual(var_block_map[model.T[i]], i) self.assertEqual(var_block_map[model.P[i]], i) self.assertEqual(var_block_map[model.F[i]], i) self.assertEqual(con_block_map[model.ideal_gas[i]], i) self.assertEqual(con_block_map[model.expansion[i]], i) self.assertEqual(con_block_map[model.mbal[i]], i) self.assertEqual(con_block_map[model.ebal[i]], i)
def test_incidence_matrix(self): N = 5 model = make_gas_expansion_model(N) all_vars = list(model.component_data_objects(pyo.Var)) all_cons = list(model.component_data_objects(pyo.Constraint)) imat = get_numeric_incidence_matrix(all_vars, all_cons) n_var = 4 * (N + 1) n_con = 4 * N + 1 self.assertEqual(imat.shape, (n_con, n_var)) var_idx_map = ComponentMap((v, i) for i, v in enumerate(all_vars)) con_idx_map = ComponentMap((c, i) for i, c in enumerate(all_cons)) # Map constraints to the variables they contain. csr_map = ComponentMap() csr_map.update((model.mbal[i], ComponentSet([ model.F[i], model.F[i - 1], model.rho[i], model.rho[i - 1], ])) for i in model.streams if i != model.streams.first()) csr_map.update((model.ebal[i], ComponentSet([ model.F[i], model.F[i - 1], model.rho[i], model.rho[i - 1], model.T[i], model.T[i - 1], ])) for i in model.streams if i != model.streams.first()) csr_map.update((model.expansion[i], ComponentSet([ model.rho[i], model.rho[i - 1], model.P[i], model.P[i - 1], ])) for i in model.streams if i != model.streams.first()) csr_map.update((model.ideal_gas[i], ComponentSet([ model.P[i], model.rho[i], model.T[i], ])) for i in model.streams) # Map constraint and variable indices to the values of the derivatives # Note that the derivative values calculated here depend on the model's # canonical form. deriv_lookup = {} m = model # for convenience for s in model.streams: # Ideal gas: i = con_idx_map[model.ideal_gas[s]] j = var_idx_map[model.P[s]] deriv_lookup[i, j] = 1.0 j = var_idx_map[model.rho[s]] deriv_lookup[i, j] = -model.R.value * model.T[s].value j = var_idx_map[model.T[s]] deriv_lookup[i, j] = -model.R.value * model.rho[s].value if s != model.streams.first(): # Expansion: i = con_idx_map[model.expansion[s]] j = var_idx_map[model.P[s]] deriv_lookup[i, j] = 1 / model.P[s - 1].value j = var_idx_map[model.P[s - 1]] deriv_lookup[i, j] = -model.P[s].value / model.P[s - 1]**2 j = var_idx_map[model.rho[s]] deriv_lookup[i, j] = pyo.value( -m.gamma * (m.rho[s] / m.rho[s - 1])**(m.gamma - 1) / m.rho[s - 1]) j = var_idx_map[model.rho[s - 1]] deriv_lookup[i, j] = pyo.value( -m.gamma * (m.rho[s] / m.rho[s - 1])**(m.gamma - 1) * (-m.rho[s] / m.rho[s - 1]**2)) # Energy balance: i = con_idx_map[m.ebal[s]] j = var_idx_map[m.rho[s - 1]] deriv_lookup[i, j] = pyo.value(m.F[s - 1] * m.T[s - 1]) j = var_idx_map[m.F[s - 1]] deriv_lookup[i, j] = pyo.value(m.rho[s - 1] * m.T[s - 1]) j = var_idx_map[m.T[s - 1]] deriv_lookup[i, j] = pyo.value(m.F[s - 1] * m.rho[s - 1]) j = var_idx_map[m.rho[s]] deriv_lookup[i, j] = pyo.value(-m.F[s] * m.T[s]) j = var_idx_map[m.F[s]] deriv_lookup[i, j] = pyo.value(-m.rho[s] * m.T[s]) j = var_idx_map[m.T[s]] deriv_lookup[i, j] = pyo.value(-m.F[s] * m.rho[s]) # Mass balance: i = con_idx_map[m.mbal[s]] j = var_idx_map[m.rho[s - 1]] deriv_lookup[i, j] = pyo.value(m.F[s - 1]) j = var_idx_map[m.F[s - 1]] deriv_lookup[i, j] = pyo.value(m.rho[s - 1]) j = var_idx_map[m.rho[s]] deriv_lookup[i, j] = pyo.value(-m.F[s]) j = var_idx_map[m.F[s]] deriv_lookup[i, j] = pyo.value(-m.rho[s]) # Want to test that the columns have the rows we expect. i = model.streams.first() for i, j, e in zip(imat.row, imat.col, imat.data): con = all_cons[i] var = all_vars[j] self.assertIn(var, csr_map[con]) csr_map[con].remove(var) self.assertAlmostEqual(deriv_lookup[i, j], e, 8) # And no additional rows for con in csr_map: self.assertEqual(len(csr_map[con]), 0)