Skip to content
Snippets Groups Projects
Commit 9d5156f4 authored by Pedro L. Magalhães's avatar Pedro L. Magalhães
Browse files

Added support for convex and nonconvex price functions using the delta...

Added support for convex and nonconvex price functions using the delta formulation, with and without blocks. Revised the tests too.
parent cc6501fd
No related branches found
No related tags found
1 merge request!7Added support for convex price functions using the delta formulation and...
...@@ -26,11 +26,6 @@ def price_delta_block( ...@@ -26,11 +26,6 @@ def price_delta_block(
# set of price segments # set of price segments
b.set_S = pyo.Set() b.set_S = pyo.Set()
# TODO: introduce a set of price segments for non-convex tariffs
# def init_set_S_nonconvex(b, s):
# return (s for s in b.set_S if b.param_price_function_is_convex)
# b.set_S_nonconvex = pyo.Set(within=b.set_S, initialize=init_set_S_nonconvex)
# ********************************************************************* # *********************************************************************
# ********************************************************************* # *********************************************************************
...@@ -66,28 +61,21 @@ def price_delta_block( ...@@ -66,28 +61,21 @@ def price_delta_block(
bounds=bounds_var_trans_flows bounds=bounds_var_trans_flows
) )
# import and export flows # segment usage variables
def rule_var_segment_usage_s(b, s):
if b.param_price_function_is_convex:
return pyo.UnitInterval
else:
return pyo.Binary
b.var_segment_usage_s = pyo.Var( b.var_segment_usage_s = pyo.Var(
b.set_S, b.set_S,
within=rule_var_segment_usage_s, within=pyo.UnitInterval,
) )
# ********************************************************************* # *********************************************************************
# ********************************************************************* # *********************************************************************
# import flow costs and export flow revenues # import flow costs and export flow revenues (y function)
def rule_constr_trans_monetary_flows(b): def rule_constr_trans_monetary_flows(b):
if (g,l) in b.parent_block().set_GL_imp: if (g,l) in b.parent_block().set_GL_imp:
return ( return (
sum( sum(
(b.var_segment_usage_s[s]*b.param_p_s[s]*b.param_v_max_s[s] b.var_segment_usage_s[s]*b.param_p_s[s]*b.param_v_max_s[s]
if s == 0 else
b.var_segment_usage_s[s]*(b.param_p_s[s]*b.param_v_max_s[s]-b.param_p_s[s-1]*b.param_v_max_s[s-1]))
for s in b.set_S for s in b.set_S
) )
== b.parent_block().var_ifc_glqpk[(g,l,q,p,k)] == b.parent_block().var_ifc_glqpk[(g,l,q,p,k)]
...@@ -95,9 +83,7 @@ def price_delta_block( ...@@ -95,9 +83,7 @@ def price_delta_block(
else: else:
return ( return (
sum( sum(
(b.var_segment_usage_s[s]*b.param_p_s[s]*b.param_v_max_s[s] b.var_segment_usage_s[s]*b.param_p_s[s]*b.param_v_max_s[s]
if s == 0 else
b.var_segment_usage_s[s]*(b.param_p_s[s]*b.param_v_max_s[s]-b.param_p_s[s-1]*b.param_v_max_s[s-1]))
for s in b.set_S for s in b.set_S
) )
== b.parent_block().var_efr_glqpk[(g,l,q,p,k)] == b.parent_block().var_efr_glqpk[(g,l,q,p,k)]
...@@ -106,7 +92,7 @@ def price_delta_block( ...@@ -106,7 +92,7 @@ def price_delta_block(
rule=rule_constr_trans_monetary_flows rule=rule_constr_trans_monetary_flows
) )
# imported and exported flows: volumes # imported and exported flows (x function)
def rule_constr_trans_flows_seg(b): def rule_constr_trans_flows_seg(b):
return sum( return sum(
b.var_segment_usage_s[s]*b.param_v_max_s[s] b.var_segment_usage_s[s]*b.param_v_max_s[s]
...@@ -114,7 +100,7 @@ def price_delta_block( ...@@ -114,7 +100,7 @@ def price_delta_block(
) == b.var_trans_flows ) == b.var_trans_flows
b.constr_trans_flows_seg = pyo.Constraint(rule=rule_constr_trans_flows_seg) b.constr_trans_flows_seg = pyo.Constraint(rule=rule_constr_trans_flows_seg)
# imported and exported flows: energy system # imported and exported flows: energy system defines the x value
def rule_constr_trans_flows_sys(b): def rule_constr_trans_flows_sys(b):
if (g,l) in b.parent_block().set_GL_imp: if (g,l) in b.parent_block().set_GL_imp:
return sum( return sum(
...@@ -140,22 +126,64 @@ def price_delta_block( ...@@ -140,22 +126,64 @@ def price_delta_block(
if not b.param_price_function_is_convex: if not b.param_price_function_is_convex:
# this means the segment usage variables are binary # this means the segment usage variables are binary
raise NotImplementedError # variables to indicate if the segments are active
b.var_segment_is_full = pyo.Var(
# # if delta var is one, previous ones must be one too b.set_S,
# # if delta var is zero, the next ones must also be zero within=pyo.Binary
# def rule_constr_delta_summing_logic(b, s): )
# if s == len(b.set_S)-1:
# # last segment, skip # if next level is active, bin. var. for current level must be one
# # convex, skip # if bin. var. for current level is one, segment must be used fully
# return pyo.Constraint.Skip # if previous level is not full, bin. var for current level must be zero
# return ( # if bin. var. for current level is zero,
# b.var_segment_usage_s[s] >= def rule_constr_nonconvex_p1(b, s):
# b.var_segment_usage_s[s+1] if s == len(b.set_S)-1:
# ) # last segment, skip
# b.constr_delta_summing_logic = pyo.Constraint( return pyo.Constraint.Skip
# b.set_S, rule=rule_constr_delta_summing_logic return (
# ) b.var_segment_usage_s[s+1] <=
b.var_segment_is_full[s]
)
b.constr_nonconvex_p1 = pyo.Constraint(
b.set_S, rule=rule_constr_nonconvex_p1
)
def rule_constr_nonconvex_p2(b, s):
if s == len(b.set_S)-1:
# last segment, skip
return pyo.Constraint.Skip
return (
b.var_segment_usage_s[s] >=
b.var_segment_is_full[s]
)
b.constr_nonconvex_p2 = pyo.Constraint(
b.set_S, rule=rule_constr_nonconvex_p2
)
def rule_constr_nonconvex_p3(b, s):
if s == len(b.set_S)-1:
# last segment, skip
return pyo.Constraint.Skip
return (
b.var_segment_usage_s[s] >=
b.var_segment_is_full[s+1]
)
b.constr_nonconvex_p3 = pyo.Constraint(
b.set_S, rule=rule_constr_nonconvex_p3
)
def rule_constr_nonconvex_p4(b, s):
if s == len(b.set_S)-1:
# last segment, skip
return pyo.Constraint.Skip
return (
b.var_segment_usage_s[s+1] <=
b.var_segment_is_full[s+1]
)
b.constr_nonconvex_p4 = pyo.Constraint(
b.set_S, rule=rule_constr_nonconvex_p4
)
# ********************************************************************* # *********************************************************************
# ********************************************************************* # *********************************************************************
...@@ -186,8 +214,6 @@ def price_delta_no_block( ...@@ -186,8 +214,6 @@ def price_delta_no_block(
enable_initialisation: bool = True enable_initialisation: bool = True
): ):
raise NotImplementedError
# auxiliary set for pyomo # auxiliary set for pyomo
model.set_GLQPK = model.set_GL_exp_imp*model.set_QPK model.set_GLQPK = model.set_GL_exp_imp*model.set_QPK
...@@ -241,28 +267,33 @@ def price_delta_no_block( ...@@ -241,28 +267,33 @@ def price_delta_no_block(
# ************************************************************************* # *************************************************************************
# import and export flows # import and export flows
def bounds_var_trans_flows_glqpks(m, g, l, q, p, k, s): def bounds_var_trans_flows_glqpk(m, g, l, q, p, k):
# return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)]) try:
if (g, l, q, p, k, s) in m.param_v_max_glqpks: return (0, sum(m.param_v_max_glqpks[s] for s in m.set_S[(g, l, q, p, k)]))
# predefined finite capacity except Exception:
return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)])
else:
# infinite capacity
return (0, None) return (0, None)
model.var_trans_flows_glqpks = pyo.Var( model.var_trans_flows_glqpk = pyo.Var(
model.set_GLQPKS, within=pyo.NonNegativeReals, bounds=bounds_var_trans_flows_glqpks model.set_GLQPK, within=pyo.NonNegativeReals, bounds=bounds_var_trans_flows_glqpk
)
# segment usage variables
model.var_segment_usage_glqpks = pyo.Var(
model.set_GLQPKS,
within=pyo.UnitInterval,
) )
# ************************************************************************* # *************************************************************************
# ************************************************************************* # *************************************************************************
# import flow costs and export flow revenues
# import flow costs and export flow revenues (y function)
def rule_constr_trans_monetary_flows(m, g, l, q, p, k): def rule_constr_trans_monetary_flows(m, g, l, q, p, k):
if (g,l) in m.set_GL_imp: if (g,l) in m.set_GL_imp:
return ( return (
sum( sum(
m.var_trans_flows_glqpks[(g, l, q, p, k, s)] m.var_segment_usage_glqpks[(g, l, q, p, k, s)]*
* m.param_p_glqpks[(g, l, q, p, k, s)] m.param_p_glqpks[(g, l, q, p, k, s)]*
m.param_v_max_glqpks[(g, l, q, p, k, s)]
for s in m.set_S[(g, l, q, p, k)] for s in m.set_S[(g, l, q, p, k)]
) )
== m.var_ifc_glqpk[(g,l,q,p,k)] == m.var_ifc_glqpk[(g,l,q,p,k)]
...@@ -270,25 +301,38 @@ def price_delta_no_block( ...@@ -270,25 +301,38 @@ def price_delta_no_block(
else: else:
return ( return (
sum( sum(
m.var_trans_flows_glqpks[(g, l, q, p, k, s)] m.var_segment_usage_glqpks[(g, l, q, p, k, s)]*
* m.param_p_glqpks[(g, l, q, p, k, s)] m.param_p_glqpks[(g, l, q, p, k, s)]*
m.param_v_max_glqpks[(g, l, q, p, k, s)]
for s in m.set_S[(g, l, q, p, k)] for s in m.set_S[(g, l, q, p, k)]
) )
== m.var_efr_glqpk[(g,l,q,p,k)] == m.var_efr_glqpk[(g,l,q,p,k)]
) )
model.constr_trans_monetary_flows = pyo.Constraint( model.constr_trans_monetary_flows = pyo.Constraint(
model.set_GLQPK, rule=rule_constr_trans_monetary_flows model.set_GLQPK,
rule=rule_constr_trans_monetary_flows
) )
# imported and exported flows # imported and exported flows (x function)
def rule_constr_trans_flows(m, g, l, q, p, k): def rule_constr_trans_flows_seg(m, g, l, q, p, k):
return sum(
m.var_segment_usage_glqpks[(g, l, q, p, k, s)]*m.param_v_max_glqpks[(g, l, q, p, k, s)]
for s in m.set_S[(g, l, q, p, k)]
) == m.var_trans_flows_glqpk[(g, l, q, p, k)]
model.constr_trans_flows_seg = pyo.Constraint(
model.set_GLQPK,
rule=rule_constr_trans_flows_seg
)
# imported and exported flows: energy system defines the x value
def rule_constr_trans_flows_sys(m, g, l, q, p, k):
if (g,l) in m.set_GL_imp: if (g,l) in m.set_GL_imp:
return sum( return sum(
m.var_v_glljqk[(g,l,l_star,j,q,k)] m.var_v_glljqk[(g,l,l_star,j,q,k)]
for l_star in m.set_L[g] for l_star in m.set_L[g]
if l_star not in m.set_L_imp[g] if l_star not in m.set_L_imp[g]
for j in m.set_J[(g,l,l_star)] # only directed arcs for j in m.set_J[(g,l,l_star)] # only directed arcs
) == sum(m.var_trans_flows_glqpks[(g, l, q, p, k, s)] for s in m.set_S[(g, l, q, p, k)]) ) == m.var_trans_flows_glqpk[(g, l, q, p, k)]
else: else:
return sum( return sum(
m.var_v_glljqk[(g,l_star,l,j,q,k)] m.var_v_glljqk[(g,l_star,l,j,q,k)]
...@@ -296,73 +340,73 @@ def price_delta_no_block( ...@@ -296,73 +340,73 @@ def price_delta_no_block(
for l_star in m.set_L[g] for l_star in m.set_L[g]
if l_star not in m.set_L_exp[g] if l_star not in m.set_L_exp[g]
for j in m.set_J[(g,l_star,l)] # only directed arcs for j in m.set_J[(g,l_star,l)] # only directed arcs
) == sum(m.var_trans_flows_glqpks[(g, l, q, p, k, s)] for s in m.set_S[(g, l, q, p, k)]) ) == m.var_trans_flows_glqpk[(g, l, q, p, k)]
model.constr_trans_flows_sys = pyo.Constraint(
model.constr_trans_flows = pyo.Constraint( model.set_GLQPK,
model.set_GLQPK, rule=rule_constr_trans_flows rule=rule_constr_trans_flows_sys
) )
# ************************************************************************* # *************************************************************************
# ************************************************************************* # *************************************************************************
# non-convex price functions # non-convex price functions
# TODO: remove these variables from the model if they are not needed
# delta variables # variables to indicate if the segments are active
model.var_active_segment_glqpks = pyo.Var( model.var_segment_is_full = pyo.Var(
model.set_GLQPKS, within=pyo.Binary model.set_GLQPKS,
within=pyo.Binary
)
# if next level is active, bin. var. for current level must be one
# if bin. var. for current level is one, segment must be used fully
# if previous level is not full, bin. var for current level must be zero
# if bin. var. for current level is zero,
def rule_constr_nonconvex_p1(m, g, l, q, p, k, s):
if s == len(m.set_S[(g, l, q, p, k)])-1:
# last segment, skip
return pyo.Constraint.Skip
return (
m.var_segment_usage_glqpks[(g,l,q,p,k,s+1)] <=
m.var_segment_is_full[(g,l,q,p,k,s)]
)
model.constr_nonconvex_p1 = pyo.Constraint(
model.set_GLQPKS, rule=rule_constr_nonconvex_p1
) )
# segments must be empty if the respective delta variable is zero def rule_constr_nonconvex_p2(m, g, l, q, p, k, s):
def rule_constr_empty_segment_if_delta_zero(m, g, l, q, p, k, s): if s == len(m.set_S[(g, l, q, p, k)])-1:
if len(m.set_S[(g,l,q,p,k)]) == 1 or m.param_price_function_is_convex[(g,l,q,p,k)]: # last segment, skip
# single segment, skip
# convex, skip
return pyo.Constraint.Skip return pyo.Constraint.Skip
return ( return (
m.var_trans_flows_glqpks[(g,l,q,p,k,s)] <= m.var_segment_usage_glqpks[(g,l,q,p,k,s)] >=
m.param_v_max_glqpks[(g,l,q,p,k,s)]* m.var_segment_is_full[(g,l,q,p,k,s)]
m.var_active_segment_glqpks[(g,l,q,p,k,s)]
) )
model.constr_empty_segment_if_delta_zero = pyo.Constraint( model.constr_nonconvex_p2 = pyo.Constraint(
model.set_GLQPKS, rule=rule_constr_empty_segment_if_delta_zero model.set_GLQPKS, rule=rule_constr_nonconvex_p2
) )
# if delta var is one, previous ones must be one too def rule_constr_nonconvex_p3(m, g, l, q, p, k, s):
# if delta var is zero, the next ones must also be zero if s == len(m.set_S[(g, l, q, p, k)])-1:
def rule_constr_delta_summing_logic(m, g, l, q, p, k, s):
if s == len(m.set_S[(g,l,q,p,k)])-1 or m.param_price_function_is_convex[(g,l,q,p,k)]:
# last segment, skip # last segment, skip
# convex, skip
return pyo.Constraint.Skip return pyo.Constraint.Skip
return ( return (
m.var_active_segment_glqpks[(g,l,q,p,k,s)] >= m.var_segment_usage_glqpks[(g,l,q,p,k,s)] >=
m.var_active_segment_glqpks[(g,l,q,p,k,s+1)] m.var_segment_is_full[(g,l,q,p,k,s+1)]
) )
model.constr_delta_summing_logic = pyo.Constraint( model.constr_nonconvex_p3 = pyo.Constraint(
model.set_GLQPKS, rule=rule_constr_delta_summing_logic model.set_GLQPKS, rule=rule_constr_nonconvex_p3
) )
# if a segment is not completely used, the next ones must remain empty def rule_constr_nonconvex_p4(m, g, l, q, p, k, s):
def rule_constr_fill_up_segment_before_next(m, g, l, q, p, k, s): if s == len(m.set_S[(g, l, q, p, k)])-1:
if s == len(m.set_S[(g,l,q,p,k)])-1 or m.param_price_function_is_convex[(g,l,q,p,k)]:
# last segment, skip # last segment, skip
# convex, skip
return pyo.Constraint.Skip return pyo.Constraint.Skip
return ( return (
m.var_trans_flows_glqpks[(g,l,q,p,k,s)] >= m.var_segment_usage_glqpks[(g,l,q,p,k,s+1)] <=
m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]* m.var_segment_is_full[(g,l,q,p,k,s+1)]
m.param_v_max_glqpks[(g,l,q,p,k,s)]
) )
# return ( model.constr_nonconvex_p4 = pyo.Constraint(
# m.var_if_glqpks[(g,l,q,p,k,s)]/m.param_v_max_glqpks[(g,l,q,p,k,s)] >= model.set_GLQPKS, rule=rule_constr_nonconvex_p4
# m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]
# )
# return (
# m.param_v_max_glqpks[(g,l,q,p,k,s)]-m.var_if_glqpks[(g,l,q,p,k,s)] <=
# m.param_v_max_glqpks[(g,l,q,p,k,s)]*(1- m.var_active_segment_glqpks[(g,l,q,p,k,s+1)])
# )
model.constr_fill_up_segment_before_next = pyo.Constraint(
model.set_GLQPKS, rule=rule_constr_fill_up_segment_before_next
) )
# ***************************************************************************** # *****************************************************************************
......
...@@ -39,7 +39,7 @@ def add_price_functions( ...@@ -39,7 +39,7 @@ def add_price_functions(
if node_price_model == NODE_PRICE_LAMBDA: if node_price_model == NODE_PRICE_LAMBDA:
raise NotImplementedError raise NotImplementedError
elif node_price_model == NODE_PRICE_DELTA: elif node_price_model == NODE_PRICE_DELTA:
raise NotImplementedError price_delta_no_block(model, **kwargs)
else: else:
price_other_no_block(model, **kwargs) price_other_no_block(model, **kwargs)
......
...@@ -152,6 +152,31 @@ def statistics(ipp: InfrastructurePlanningProblem, ...@@ -152,6 +152,31 @@ def statistics(ipp: InfrastructurePlanningProblem,
} }
else: else:
# not in a block # not in a block
if ipp.node_price_model == NODE_PRICE_DELTA:
# imports
imports_qpk = {
qpk: pyo.value(
sum(
ipp.instance.var_trans_flows_glqpk[(g,l_imp,*qpk)]
for g, l_imp in import_node_keys
)
*ipp.instance.param_c_time_qpk[qpk]
)
for qpk in ipp.time_frame.qpk()
}
# exports
exports_qpk = {
qpk: pyo.value(
sum(
ipp.instance.var_trans_flows_glqpk[(g,l_exp,*qpk)]
for g, l_exp in export_node_keys
)
*ipp.instance.param_c_time_qpk[qpk]
)
for qpk in ipp.time_frame.qpk()
}
else:
# imports # imports
imports_qpk = { imports_qpk = {
qpk: pyo.value( qpk: pyo.value(
......
...@@ -25,6 +25,9 @@ from src.topupopt.problems.esipp.blocks.prices import NODE_PRICE_OTHER, NODE_PRI ...@@ -25,6 +25,9 @@ from src.topupopt.problems.esipp.blocks.prices import NODE_PRICE_OTHER, NODE_PRI
# ***************************************************************************** # *****************************************************************************
# ***************************************************************************** # *****************************************************************************
# TODO: test time-varying tariffs (convex/non-convex)
# TODO: check problem sizes
class TestESIPPProblem: class TestESIPPProblem:
# ************************************************************************* # *************************************************************************
...@@ -102,14 +105,25 @@ class TestESIPPProblem: ...@@ -102,14 +105,25 @@ class TestESIPPProblem:
) )
assert not ipp.has_peak_total_assessments() assert not ipp.has_peak_total_assessments()
# print('hey')
print('hey') # print((use_prices_block, node_price_model))
print(ipp.results["Problem"][0]) # print(ipp.results["Problem"][0])
# if node_price_model != NODE_PRICE_OTHER: if (use_prices_block, node_price_model) == (True, NODE_PRICE_OTHER):
# ipp.instance.pprint() assert ipp.results["Problem"][0]["Number of constraints"] == 10
# assert ipp.results["Problem"][0]["Number of constraints"] == 24 assert ipp.results["Problem"][0]["Number of variables"] == 11
# assert ipp.results["Problem"][0]["Number of variables"] == 22 assert ipp.results["Problem"][0]["Number of nonzeros"] == 20
# assert ipp.results["Problem"][0]["Number of nonzeros"] == 49 elif (use_prices_block, node_price_model) == (False, NODE_PRICE_OTHER):
assert ipp.results["Problem"][0]["Number of constraints"] == 10
assert ipp.results["Problem"][0]["Number of variables"] == 11
assert ipp.results["Problem"][0]["Number of nonzeros"] == 20
elif (use_prices_block, node_price_model) == (True, NODE_PRICE_DELTA):
assert ipp.results["Problem"][0]["Number of constraints"] == 11
assert ipp.results["Problem"][0]["Number of variables"] == 12
assert ipp.results["Problem"][0]["Number of nonzeros"] == 22
elif (use_prices_block, node_price_model) == (False, NODE_PRICE_DELTA):
assert ipp.results["Problem"][0]["Number of constraints"] == 15
assert ipp.results["Problem"][0]["Number of variables"] == 14
assert ipp.results["Problem"][0]["Number of nonzeros"] == 30
# ********************************************************************* # *********************************************************************
# ********************************************************************* # *********************************************************************
...@@ -149,7 +163,8 @@ class TestESIPPProblem: ...@@ -149,7 +163,8 @@ class TestESIPPProblem:
# ************************************************************************* # *************************************************************************
# ************************************************************************* # *************************************************************************
# TODO: test a nonconvex price function in which only the first segment is used
# e.g. using a flow 0.5 in the following example
@pytest.mark.parametrize( @pytest.mark.parametrize(
"use_prices_block, node_price_model", "use_prices_block, node_price_model",
[(True, NODE_PRICE_OTHER), [(True, NODE_PRICE_OTHER),
...@@ -157,7 +172,8 @@ class TestESIPPProblem: ...@@ -157,7 +172,8 @@ class TestESIPPProblem:
(True, NODE_PRICE_LAMBDA), (True, NODE_PRICE_LAMBDA),
(False, NODE_PRICE_OTHER), (False, NODE_PRICE_OTHER),
(False, NODE_PRICE_DELTA), (False, NODE_PRICE_DELTA),
(False, NODE_PRICE_LAMBDA)] (False, NODE_PRICE_LAMBDA)
]
) )
def test_problem_decreasing_imp_prices(self, use_prices_block, node_price_model): def test_problem_decreasing_imp_prices(self, use_prices_block, node_price_model):
...@@ -220,6 +236,25 @@ class TestESIPPProblem: ...@@ -220,6 +236,25 @@ class TestESIPPProblem:
) )
assert not ipp.has_peak_total_assessments() assert not ipp.has_peak_total_assessments()
# print('hey')
# print((use_prices_block, node_price_model))
# print(ipp.results["Problem"][0])
if (use_prices_block, node_price_model) == (True, NODE_PRICE_OTHER):
assert ipp.results["Problem"][0]["Number of constraints"] == 14
assert ipp.results["Problem"][0]["Number of variables"] == 13
assert ipp.results["Problem"][0]["Number of nonzeros"] == 28
elif (use_prices_block, node_price_model) == (False, NODE_PRICE_OTHER):
assert ipp.results["Problem"][0]["Number of constraints"] == 14
assert ipp.results["Problem"][0]["Number of variables"] == 13
assert ipp.results["Problem"][0]["Number of nonzeros"] == 28
elif (use_prices_block, node_price_model) == (True, NODE_PRICE_DELTA):
assert ipp.results["Problem"][0]["Number of constraints"] == 15
assert ipp.results["Problem"][0]["Number of variables"] == 14
assert ipp.results["Problem"][0]["Number of nonzeros"] == 30
elif (use_prices_block, node_price_model) == (False, NODE_PRICE_DELTA):
assert ipp.results["Problem"][0]["Number of constraints"] == 15
assert ipp.results["Problem"][0]["Number of variables"] == 14
assert ipp.results["Problem"][0]["Number of nonzeros"] == 30
# ********************************************************************* # *********************************************************************
# ********************************************************************* # *********************************************************************
...@@ -260,6 +295,140 @@ class TestESIPPProblem: ...@@ -260,6 +295,140 @@ class TestESIPPProblem:
# ************************************************************************* # *************************************************************************
# ************************************************************************* # *************************************************************************
@pytest.mark.parametrize(
"use_prices_block, node_price_model",
[(True, NODE_PRICE_OTHER),
(True, NODE_PRICE_DELTA),
(True, NODE_PRICE_LAMBDA),
(False, NODE_PRICE_OTHER),
(False, NODE_PRICE_DELTA),
(False, NODE_PRICE_LAMBDA)
]
)
def test_problem_decreasing_imp_prices2(self, use_prices_block, node_price_model):
# assessment
q = 0
tf = EconomicTimeFrame(
discount_rate=0.0,
reporting_periods={q: (0,)},
reporting_period_durations={q: (365 * 24 * 3600,)},
time_intervals={q: (0,)},
time_interval_durations={q: (1,)},
)
# 2 nodes: one import, one regular
mynet = Network()
# import node
node_IMP = 'I'
mynet.add_import_node(
node_key=node_IMP,
prices={
qpk: ResourcePrice(prices=[2.0, 1.0], volumes=[0.5, 3.0])
for qpk in tf.qpk()
},
)
# other nodes
node_A = 'A'
mynet.add_source_sink_node(node_key=node_A, base_flow={(q, 0): 0.25})
# arc IA
arc_tech_IA = Arcs(
name="any",
efficiency={(q, 0): 0.5},
efficiency_reverse=None,
static_loss=None,
capacity=[3],
minimum_cost=[2],
specific_capacity_cost=1,
capacity_is_instantaneous=False,
validate=False,
)
mynet.add_directed_arc(node_key_a=node_IMP, node_key_b=node_A, arcs=arc_tech_IA)
# no sos, regular time intervals
ipp = build_solve_ipp(
solver_options={},
perform_analysis=False,
plot_results=False, # True,
print_solver_output=False,
time_frame=tf,
networks={"mynet": mynet},
static_losses_mode=True, # just to reach a line,
mandatory_arcs=[],
max_number_parallel_arcs={},
simplify_problem=False,
use_prices_block=use_prices_block,
node_price_model=node_price_model
)
assert not ipp.has_peak_total_assessments()
# print('hey')
# # print((use_prices_block, node_price_model))
# print(ipp.results["Problem"][0])
# # capex should be four
# print(pyo.value(ipp.instance.var_capex))
# print(pyo.value(ipp.instance.var_sdncf_q[q]))
# print(pyo.value(ipp.instance.obj_f))
if (use_prices_block, node_price_model) == (True, NODE_PRICE_OTHER):
assert ipp.results["Problem"][0]["Number of constraints"] == 14
assert ipp.results["Problem"][0]["Number of variables"] == 13
assert ipp.results["Problem"][0]["Number of nonzeros"] == 28
elif (use_prices_block, node_price_model) == (False, NODE_PRICE_OTHER):
assert ipp.results["Problem"][0]["Number of constraints"] == 14
assert ipp.results["Problem"][0]["Number of variables"] == 13
assert ipp.results["Problem"][0]["Number of nonzeros"] == 28
elif (use_prices_block, node_price_model) == (True, NODE_PRICE_DELTA):
assert ipp.results["Problem"][0]["Number of constraints"] == 15
assert ipp.results["Problem"][0]["Number of variables"] == 14
assert ipp.results["Problem"][0]["Number of nonzeros"] == 30
elif (use_prices_block, node_price_model) == (False, NODE_PRICE_DELTA):
assert ipp.results["Problem"][0]["Number of constraints"] == 15
assert ipp.results["Problem"][0]["Number of variables"] == 14
assert ipp.results["Problem"][0]["Number of nonzeros"] == 30
# *********************************************************************
# *********************************************************************
# validation
# the arc should be installed since it is required for feasibility
assert (
True
in ipp.networks["mynet"]
.edges[(node_IMP, node_A, 0)][Network.KEY_ARC_TECH]
.options_selected
)
# the flows should be 0.5
assert math.isclose(
pyo.value(ipp.instance.var_v_glljqk[("mynet", node_IMP, node_A, 0, q, 0)]),
0.5,
abs_tol=1e-6,
)
# arc amplitude should be 0.5
assert math.isclose(
pyo.value(ipp.instance.var_v_amp_gllj[("mynet", node_IMP, node_A, 0)]),
0.5,
abs_tol=0.01,
)
# capex should be four
assert math.isclose(pyo.value(ipp.instance.var_capex), 2.5, abs_tol=1e-3)
# sdncf should be -2.5
assert math.isclose(pyo.value(ipp.instance.var_sdncf_q[q]), -1.0, abs_tol=1e-3)
# the objective function should be -7.5
assert math.isclose(pyo.value(ipp.instance.obj_f), -3.5, abs_tol=1e-3)
# *************************************************************************
# *************************************************************************
@pytest.mark.parametrize( @pytest.mark.parametrize(
"use_prices_block, node_price_model", "use_prices_block, node_price_model",
[(True, NODE_PRICE_OTHER), [(True, NODE_PRICE_OTHER),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment