def build(cls, m, wn, updater, index_over=None): """ Adds a mass balance to the model for the specified junctions. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of pipe names; default is all pipes in wn """ if not hasattr(m, 'approx_hazen_williams_headloss'): m.approx_hazen_williams_headloss = aml.ConstraintDict() if index_over is None: index_over = wn.pipe_name_list for link_name in index_over: if link_name in m.approx_hazen_williams_headloss: del m.approx_hazen_williams_headloss[link_name] link = wn.get_link(link_name) f = m.flow[link_name] status = link.status if status == LinkStatus.Closed or link._is_isolated: con = aml.Constraint(f) else: eps = 1e-5 # Need to provide an options for this start_node_name = link.start_node_name end_node_name = link.end_node_name start_node = wn.get_node(start_node_name) end_node = wn.get_node(end_node_name) if isinstance(start_node, wntr.network.Junction): start_h = m.head[start_node_name] else: start_h = m.source_head[start_node_name] if isinstance(end_node, wntr.network.Junction): end_h = m.head[end_node_name] else: end_h = m.source_head[end_node_name] k = m.hw_resistance[link_name] minor_k = m.minor_loss[link_name] con = aml.Constraint( expr=-aml.sign(f) * k * aml.abs(f)**m.hw_exp - eps * k**0.5 * f - aml.sign(f) * minor_k * f**m.hw_minor_exp + start_h - end_h) m.approx_hazen_williams_headloss[link_name] = con updater.add(link, 'status', approx_hazen_williams_headloss_constraint.update) updater.add(link, '_is_isolated', approx_hazen_williams_headloss_constraint.update)
def build(cls, m, wn, updater, index_over=None): """ Adds a headloss constraint to the model for the power pumps. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of powerPump names; default is all powerPumps in wn """ if not hasattr(m, 'fcv_headloss'): m.fcv_headloss = aml.ConstraintDict() if index_over is None: index_over = wn.fcv_name_list for link_name in index_over: if link_name in m.fcv_headloss: del m.fcv_headloss[link_name] link = wn.get_link(link_name) f = m.flow[link_name] status = link.status if status == LinkStatus.Closed or link._is_isolated: con = aml.Constraint(f) else: start_node_name = link.start_node_name end_node_name = link.end_node_name start_node = wn.get_node(start_node_name) end_node = wn.get_node(end_node_name) if isinstance(start_node, wntr.network.Junction): start_h = m.head[start_node_name] else: start_h = m.source_head[start_node_name] if isinstance(end_node, wntr.network.Junction): end_h = m.head[end_node_name] else: end_h = m.source_head[end_node_name] if status == LinkStatus.Active: con = aml.Constraint(f - m.valve_setting[link_name]) else: assert status == LinkStatus.Open con = aml.ConditionalExpression() con.add_condition( aml.inequality(body=f, ub=0), -m.minor_loss[link_name] * f**2 - start_h + end_h) con.add_final_expr(m.minor_loss[link_name] * f**2 - start_h + end_h) con = aml.Constraint(con) m.fcv_headloss[link_name] = con updater.add(link, 'status', fcv_headloss_constraint.update) updater.add(link, '_is_isolated', fcv_headloss_constraint.update)
def build(cls, m, wn, updater, index_over=None): """ Adds a headloss constraint to the model for the power pumps. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of powerPump names; default is all powerPumps in wn """ if not hasattr(m, 'power_pump_headloss'): m.power_pump_headloss = aml.ConstraintDict() if index_over is None: index_over = wn.power_pump_name_list for link_name in index_over: if link_name in m.power_pump_headloss: del m.power_pump_headloss[link_name] link = wn.get_link(link_name) f = m.flow[link_name] status = link.status if status == LinkStatus.Closed or link._is_isolated: con = aml.Constraint(f) else: start_node_name = link.start_node_name end_node_name = link.end_node_name start_node = wn.get_node(start_node_name) end_node = wn.get_node(end_node_name) if isinstance(start_node, wntr.network.Junction): start_h = m.head[start_node_name] else: start_h = m.source_head[start_node_name] if isinstance(end_node, wntr.network.Junction): end_h = m.head[end_node_name] else: end_h = m.source_head[end_node_name] con = aml.Constraint(m.pump_power[link_name] + (start_h - end_h) * f * (9.81 * 1000.0)) m.power_pump_headloss[link_name] = con updater.add(link, 'status', power_pump_headloss_constraint.update) updater.add(link, '_is_isolated', power_pump_headloss_constraint.update)
def test_structure_exception(self): m = aml.Model() m.x = aml.Var() m.c = aml.Constraint(m.x - 1) with self.assertRaises(RuntimeError): m.get_x() m.set_structure() m.get_x() m.y = aml.Var() m.c2 = aml.Constraint(m.x + m.y) with self.assertRaises(RuntimeError): m.get_x() m.set_structure() x = m.get_x() self.assertEqual(len(x), 2)
def build(cls, m, wn, updater, index_over=None): """ Adds a leak constraint to the model for the specified junctions. Parameters ---------- m: wntr.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of junction/tank names """ if not hasattr(m, 'leak_con'): m.leak_con = aml.ConstraintDict() if index_over is None: index_over = wn.junction_name_list + wn.tank_name_list for node_name in index_over: if node_name in m.leak_con: del m.leak_con[node_name] node = wn.get_node(node_name) if node.leak_status and not node._is_isolated: leak_rate = m.leak_rate[node_name] h = m.head[node_name] elev = m.elevation[node_name] delta = m.leak_delta slope = m.leak_slope a = m.leak_poly_coeffs_a[node_name] b = m.leak_poly_coeffs_b[node_name] c = m.leak_poly_coeffs_c[node_name] d = m.leak_poly_coeffs_d[node_name] area = m.leak_area[node_name] Cd = m.leak_coeff[node_name] con = aml.ConditionalExpression() con.add_condition(aml.inequality(h, ub=elev), leak_rate - slope * (h - elev)) con.add_condition( aml.inequality(h - elev, ub=delta), leak_rate - (a * (h - elev)**3 + b * (h - elev)**2 + c * (h - elev) + d)) con.add_final_expr(leak_rate - Cd * area * (2.0 * 9.81 * (h - elev))**0.5) con = aml.Constraint(con) m.leak_con[node_name] = con updater.add(node, 'leak_status', leak_constraint.update) updater.add(node, '_is_isolated', leak_constraint.update)
def test_basic_constraints(self): m = aml.Model() x = 2.5 y = -3.7 c = 1.5 m.x = aml.Var(x) m.y = aml.Var(y) m.c = aml.Param(c) m.con1 = aml.Constraint(m.x + 2.0 * m.y + m.c) m.con2 = aml.Constraint(m.x ** 2 - m.y ** 2 + 10) true_con_values = OrderedDict() true_con_values[m.con1] = x + 2 * y + c true_con_values[m.con2] = x ** 2 - y ** 2 + 10 true_jac = OrderedDict() true_jac[m.con1] = OrderedDict() true_jac[m.con2] = OrderedDict() true_jac[m.con1][m.x] = 1 true_jac[m.con1][m.y] = 2 true_jac[m.con2][m.x] = 2 * x true_jac[m.con2][m.y] = -2 * y compare_evaluation(self, m, true_con_values, true_jac) del true_con_values[m.con2] del true_jac[m.con2] del m.con2 m.con3 = aml.Constraint(m.x * m.y) true_con_values[m.con3] = x * y true_jac[m.con3] = OrderedDict() true_jac[m.con3][m.x] = y true_jac[m.con3][m.y] = x compare_evaluation(self, m, true_con_values, true_jac)
def build(cls, m, wn, updater, index_over=None): """ Adds a mass balance to the model for the specified junctions. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of junction names; default is all junctions in wn """ if not hasattr(m, 'pdd_mass_balance'): m.pdd_mass_balance = aml.ConstraintDict() if index_over is None: index_over = wn.junction_name_list for node_name in index_over: if node_name in m.pdd_mass_balance: del m.pdd_mass_balance[node_name] node = wn.get_node(node_name) if not node._is_isolated: expr = m.demand[node_name] for link_name in wn.get_links_for_node(node_name, flag='INLET'): expr -= m.flow[link_name] for link_name in wn.get_links_for_node(node_name, flag='OUTLET'): expr += m.flow[link_name] if node.leak_status: expr += m.leak_rate[node_name] m.pdd_mass_balance[node_name] = aml.Constraint(expr) updater.add(node, 'leak_status', pdd_mass_balance_constraint.update) updater.add(node, '_is_isolated', pdd_mass_balance_constraint.update)
def build(cls, m, wn, updater, index_over=None): """ Adds a mass balance to the model for the specified junctions. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of pipe names; default is all pipes in wn """ if not hasattr(m, 'piecewise_hazen_williams_headloss'): m.piecewise_hazen_williams_headloss = aml.ConstraintDict() if index_over is None: index_over = wn.pipe_name_list for link_name in index_over: if link_name in m.piecewise_hazen_williams_headloss: del m.piecewise_hazen_williams_headloss[link_name] link = wn.get_link(link_name) f = m.flow[link_name] status = link.status if status == LinkStatus.Closed or link._is_isolated: con = aml.Constraint(f) else: start_node_name = link.start_node_name end_node_name = link.end_node_name start_node = wn.get_node(start_node_name) end_node = wn.get_node(end_node_name) if isinstance(start_node, wntr.network.Junction): start_h = m.head[start_node_name] else: start_h = m.source_head[start_node_name] if isinstance(end_node, wntr.network.Junction): end_h = m.head[end_node_name] else: end_h = m.source_head[end_node_name] k = m.hw_resistance[link_name] minor_k = m.minor_loss[link_name] a = m.hw_a b = m.hw_b c = m.hw_c d = m.hw_d con = aml.ConditionalExpression() con.add_condition( aml.inequality(body=aml.abs(f), ub=m.hw_q1), -k * m.hw_m * f - aml.sign(f) * minor_k * f**m.hw_minor_exp + start_h - end_h) con.add_condition( aml.inequality(body=aml.abs(f), ub=m.hw_q2), -k * (a * f**3 + aml.sign(f) * b * f**2 + c * f + aml.sign(f) * d) - aml.sign(f) * minor_k * f**m.hw_minor_exp + start_h - end_h) con.add_final_expr(-aml.sign(f) * k * aml.abs(f)**m.hw_exp - aml.sign(f) * minor_k * f**m.hw_minor_exp + start_h - end_h) con = aml.Constraint(con) m.piecewise_hazen_williams_headloss[link_name] = con updater.add(link, 'status', piecewise_hazen_williams_headloss_constraint.update) updater.add(link, '_is_isolated', piecewise_hazen_williams_headloss_constraint.update)
def build(cls, m, wn, updater, index_over=None): """ Adds a headloss constraint to the model for the head curve pumps. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of HeadPump names; default is all HeadPumps in wn """ if not hasattr(m, 'head_pump_headloss'): m.head_pump_headloss = aml.ConstraintDict() if index_over is None: index_over = wn.head_pump_name_list for link_name in index_over: if link_name in m.head_pump_headloss: del m.head_pump_headloss[link_name] link = wn.get_link(link_name) f = m.flow[link_name] status = link.status if status == LinkStatus.Closed or link._is_isolated: con = aml.Constraint(f) else: start_node_name = link.start_node_name end_node_name = link.end_node_name start_node = wn.get_node(start_node_name) end_node = wn.get_node(end_node_name) if isinstance(start_node, wntr.network.Junction): start_h = m.head[start_node_name] else: start_h = m.source_head[start_node_name] if isinstance(end_node, wntr.network.Junction): end_h = m.head[end_node_name] else: end_h = m.source_head[end_node_name] A, B, C = link.get_head_curve_coefficients() if C <= 1: a, b, c, d = get_pump_poly_coefficients(A, B, C, m) con = aml.ConditionalExpression() con.add_condition(aml.inequality(body=f, ub=m.pump_q1), m.pump_slope * f + A - end_h + start_h) con.add_condition( aml.inequality(body=f, ub=m.pump_q2), a * f**3 + b * f**2 + c * f + d - end_h + start_h) con.add_final_expr(A - B * f**C - end_h + start_h) con = aml.Constraint(con) else: q_bar, h_bar = get_pump_line_params(A, B, C, m) con = aml.ConditionalExpression() con.add_condition( aml.inequality(body=f, ub=q_bar), m.pump_slope * (f - q_bar) + h_bar - end_h + start_h) con.add_final_expr(A - B * f**C - end_h + start_h) con = aml.Constraint(con) m.head_pump_headloss[link_name] = con updater.add(link, 'status', head_pump_headloss_constraint.update) updater.add(link, '_is_isolated', head_pump_headloss_constraint.update) updater.add(link, 'pump_curve_name', head_pump_headloss_constraint.update)
def build(cls, m, wn, updater, index_over=None): """ Adds a pdd constraint to the model for the specified junctions. Parameters ---------- m: wntr.aml.aml.aml.Model wn: wntr.network.model.WaterNetworkModel updater: ModelUpdater index_over: list of str list of junction names; default is all junctions in wn """ if not hasattr(m, 'pdd'): m.pdd = aml.ConstraintDict() if index_over is None: index_over = wn.junction_name_list for node_name in index_over: if node_name in m.pdd: del m.pdd[node_name] node = wn.get_node(node_name) h = m.head[node_name] d = m.demand[node_name] d_expected = m.expected_demand[node_name] if not node._is_isolated: pmin = m.pmin[node_name] pnom = m.pnom[node_name] elev = m.elevation[node_name] delta = m.pdd_smoothing_delta slope = m.pdd_slope a1 = m.pdd_poly1_coeffs_a[node_name] b1 = m.pdd_poly1_coeffs_b[node_name] c1 = m.pdd_poly1_coeffs_c[node_name] d1 = m.pdd_poly1_coeffs_d[node_name] a2 = m.pdd_poly2_coeffs_a[node_name] b2 = m.pdd_poly2_coeffs_b[node_name] c2 = m.pdd_poly2_coeffs_c[node_name] d2 = m.pdd_poly2_coeffs_d[node_name] con = aml.ConditionalExpression() con.add_condition(aml.inequality(body=h - elev - pmin, ub=0), d - d_expected * slope * (h - elev - pmin)) con.add_condition( aml.inequality(body=h - elev - pmin - delta, ub=0), d - d_expected * (a1 * (h - elev)**3 + b1 * (h - elev)**2 + c1 * (h - elev) + d1)) con.add_condition( aml.inequality(body=h - elev - pnom + delta, ub=0), d - d_expected * ((h - elev - pmin) / (pnom - pmin))**0.5) con.add_condition( aml.inequality(body=h - elev - pnom, ub=0), d - d_expected * (a2 * (h - elev)**3 + b2 * (h - elev)**2 + c2 * (h - elev) + d2)) con.add_final_expr(d - d_expected * (slope * (h - elev - pnom) + 1.0)) con = aml.Constraint(con) m.pdd[node_name] = con updater.add(node, '_is_isolated', pdd_constraint.update)
def test_register_and_remove_constraint(self): m = aml.Model() m.x = aml.Var(2.0) m.y = aml.Var(3.0) m.z = aml.Var(4.0) m.v = aml.Var(10.0) m.c1 = aml.Constraint(m.x + m.y) m.c2 = aml.Constraint(m.x * m.y * m.v) m.c3 = aml.Constraint(m.z ** 3.0) m.c4 = aml.Constraint(m.x + 1.0 / m.v) m.set_structure() con_values = m.evaluate_residuals() A = m.evaluate_jacobian() true_con_values = OrderedDict() true_con_values[m.c1] = 5.0 true_con_values[m.c2] = 60.0 true_con_values[m.c3] = 64.0 true_con_values[m.c4] = 2.1 for c in m.cons(): self.assertTrue(true_con_values[c] == con_values[c.index]) true_jac = OrderedDict() true_jac[m.c1] = OrderedDict() true_jac[m.c2] = OrderedDict() true_jac[m.c3] = OrderedDict() true_jac[m.c4] = OrderedDict() true_jac[m.c1][m.x] = 1.0 true_jac[m.c1][m.y] = 1.0 true_jac[m.c1][m.z] = 0.0 true_jac[m.c1][m.v] = 0.0 true_jac[m.c2][m.x] = 30.0 true_jac[m.c2][m.y] = 20.0 true_jac[m.c2][m.z] = 0.0 true_jac[m.c2][m.v] = 6.0 true_jac[m.c3][m.x] = 0.0 true_jac[m.c3][m.y] = 0.0 true_jac[m.c3][m.z] = 48.0 true_jac[m.c3][m.v] = 0.0 true_jac[m.c4][m.x] = 1.0 true_jac[m.c4][m.y] = 0.0 true_jac[m.c4][m.z] = 0.0 true_jac[m.c4][m.v] = -0.01 for c in m.cons(): for v in m.vars(): self.assertTrue(true_jac[c][v] == A[c.index, v.index]) del m.c3 m.c3 = aml.Constraint(m.z) m.set_structure() con_values = m.evaluate_residuals() A = m.evaluate_jacobian() true_con_values[m.c1] = 5.0 true_con_values[m.c2] = 60.0 true_con_values[m.c3] = 4.0 true_con_values[m.c4] = 2.1 for c in m.cons(): self.assertTrue(true_con_values[c] == con_values[c.index]) true_jac[m.c3] = OrderedDict() true_jac[m.c3][m.x] = 0.0 true_jac[m.c3][m.y] = 0.0 true_jac[m.c3][m.z] = 1.0 true_jac[m.c3][m.v] = 0.0 for c in m.cons(): for v in m.vars(): self.assertTrue(true_jac[c][v] == A[c.index, v.index])
def test_if_then_constraints(self): m = aml.Model() x = -4.5 y = -3.7 m.x = aml.Var(x) m.y = aml.Var(y) e = aml.ConditionalExpression() e.add_condition( aml.inequality(body=m.x, ub=-1), -((-m.x) ** 1.852) - (-m.x) ** 2 - m.y ) e.add_condition(aml.inequality(body=m.x, ub=1), m.x) e.add_final_expr(m.x ** 1.852 + m.x ** 2 - m.y) m.con1 = aml.Constraint(e) e = aml.ConditionalExpression() e.add_condition( aml.inequality(body=m.y, ub=-1), -((-m.y) ** (1.852)) - (-m.y) ** (2) - m.x ) e.add_condition(aml.inequality(body=m.y, ub=1), m.y) e.add_final_expr(m.y ** (1.852) + m.y ** (2) - m.x) m.con2 = aml.Constraint(e) true_con_values = OrderedDict() true_jac = OrderedDict() true_con_values[m.con1] = -(abs(x) ** 1.852 + abs(x) ** 2) - y true_con_values[m.con2] = -(abs(y) ** 1.852 + abs(y) ** 2) - x true_jac[m.con1] = OrderedDict() true_jac[m.con2] = OrderedDict() true_jac[m.con1][m.x] = 1.852 * abs(x) ** 0.852 + 2 * abs(x) true_jac[m.con1][m.y] = -1 true_jac[m.con2][m.x] = -1 true_jac[m.con2][m.y] = 1.852 * abs(y) ** 0.852 + 2 * abs(y) compare_evaluation(self, m, true_con_values, true_jac) x = 0.5 y = 0.3 m.x.value = x m.y.value = y true_con_values[m.con1] = x true_con_values[m.con2] = y true_jac[m.con1][m.x] = 1 true_jac[m.con1][m.y] = 0 true_jac[m.con2][m.x] = 0 true_jac[m.con2][m.y] = 1 compare_evaluation(self, m, true_con_values, true_jac) x = 4.5 y = 3.7 m.x.value = x m.y.value = y true_con_values[m.con1] = x ** 1.852 + x ** 2 - y true_con_values[m.con2] = y ** 1.852 + y ** 2 - x true_jac[m.con1][m.x] = 1.852 * x ** 0.852 + 2 * x true_jac[m.con1][m.y] = -1 true_jac[m.con2][m.x] = -1 true_jac[m.con2][m.y] = 1.852 * y ** 0.852 + 2 * y compare_evaluation(self, m, true_con_values, true_jac) del true_con_values[m.con2] del true_jac[m.con2] del m.con2 e = aml.ConditionalExpression() e.add_condition( aml.inequality(body=m.y, ub=-1), -((-m.y) ** 2.852) - (-m.y) ** 3 - m.x ) e.add_condition(aml.inequality(body=m.y, ub=1), m.y ** 2) e.add_final_expr(m.y ** 2.852 + m.y ** 3 - m.x) m.con2 = aml.Constraint(e) true_jac[m.con2] = OrderedDict() x = -4.5 y = -3.7 m.x.value = x m.y.value = y true_con_values[m.con1] = -(abs(x) ** 1.852 + abs(x) ** 2) - y true_con_values[m.con2] = -(abs(y) ** 2.852 + abs(y) ** 3) - x true_jac[m.con1][m.x] = 1.852 * abs(x) ** 0.852 + 2 * abs(x) true_jac[m.con1][m.y] = -1 true_jac[m.con2][m.x] = -1 true_jac[m.con2][m.y] = 2.852 * abs(y) ** 1.852 + 3 * abs(y) ** 2 compare_evaluation(self, m, true_con_values, true_jac) x = 0.5 y = 0.3 m.x.value = x m.y.value = y true_con_values[m.con1] = x true_con_values[m.con2] = y ** 2 true_jac[m.con1][m.x] = 1 true_jac[m.con1][m.y] = 0 true_jac[m.con2][m.x] = 0 true_jac[m.con2][m.y] = 2 * y compare_evaluation(self, m, true_con_values, true_jac) x = 4.5 y = 3.7 m.x.value = x m.y.value = y true_con_values[m.con1] = x ** 1.852 + x ** 2 - y true_con_values[m.con2] = y ** 2.852 + y ** 3 - x true_jac[m.con1][m.x] = 1.852 * x ** 0.852 + 2 * x true_jac[m.con1][m.y] = -1 true_jac[m.con2][m.x] = -1 true_jac[m.con2][m.y] = 2.852 * y ** 1.852 + 3 * y ** 2 compare_evaluation(self, m, true_con_values, true_jac)