From a4b3fb3747706b5efc8def62f203f09509890eb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20L=2E=20Magalh=C3=A3es?= <pmlpm@posteo.de>
Date: Fri, 26 Jul 2024 01:01:23 +0200
Subject: [PATCH] Implemented the price functions using a block component.

---
 src/topupopt/problems/esipp/blocks/prices.py | 626 +++++--------------
 src/topupopt/problems/esipp/model.py         |  11 +-
 src/topupopt/problems/esipp/problem.py       |  12 +-
 tests/test_esipp_prices.py                   |   2 +-
 4 files changed, 177 insertions(+), 474 deletions(-)

diff --git a/src/topupopt/problems/esipp/blocks/prices.py b/src/topupopt/problems/esipp/blocks/prices.py
index 404c852..870a71e 100644
--- a/src/topupopt/problems/esipp/blocks/prices.py
+++ b/src/topupopt/problems/esipp/blocks/prices.py
@@ -1,10 +1,12 @@
 # imports
 
 import pyomo.environ as pyo
+import math
+
 # *****************************************************************************
 # *****************************************************************************
 
-def add_prices_block(
+def add_price_functions(
     model: pyo.AbstractModel,
     **kwargs
 ):  
@@ -12,18 +14,18 @@ def add_prices_block(
     # *************************************************************************
     # *************************************************************************
     
-    # model.node_price_block = pyo.Block(model.set_QPK)
-
-    price_other(model, **kwargs)
-    # price_block_other(model, **kwargs)
+    # price_other(model, **kwargs)
+    price_other_block(model, **kwargs)
+        
+    # *************************************************************************
+    # *************************************************************************
 
 # *****************************************************************************
 # *****************************************************************************
 
-# TODO: try to implement it as a block (might make things look cleaner)
-def price_block_other(
+def price_other_block(
     model: pyo.AbstractModel,
-    convex_price_function: bool = True,
+    # convex_price_function: bool = True,
     enable_default_values: bool = True,
     enable_validation: bool = True,
     enable_initialisation: bool = True
@@ -31,485 +33,177 @@ def price_block_other(
     
     # auxiliary set for pyomo
     model.set_GLQPK = model.set_GL_exp_imp*model.set_QPK
+    
+    # create a block for a transshipment node during a given time interval
+    def rule_block_prices(b, g, l, q, p, k):
+        
+        # *********************************************************************
+        # *********************************************************************
+        
+        # sets
+        
+        # set of price segments
+        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)
+    
+        # *********************************************************************
+        # *********************************************************************
+        
+        # parameters
 
-    # set of price segments
-    model.set_S = pyo.Set(model.set_GLQPK)
-
-    # set of GLQKS tuples
-    def init_set_GLQPKS(m):
-        return (
-            (g, l, q, p, k, s)
-            # for (g,l) in m.set_GL_exp_imp
-            # for (q,k) in m.set_QK
-            for (g, l, q, p, k) in m.set_S
-            for s in m.set_S[(g, l, q, p, k)]
-        )
-
-    model.set_GLQPKS = pyo.Set(
-        dimen=6, initialize=(init_set_GLQPKS if enable_initialisation else None)
-    )
-
-    # *************************************************************************
-    # *************************************************************************
-
-    # parameters
-
-    # resource prices
+        # resource prices
+        b.param_p_s = pyo.Param(b.set_S, within=pyo.NonNegativeReals)
+        
+        # price function convexity
+        b.param_price_function_is_convex = pyo.Param(within=pyo.Boolean)
 
-    model.param_p_glqpks = pyo.Param(model.set_GLQPKS, within=pyo.NonNegativeReals)
+        # maximum resource volumes for each prices
+        b.param_v_max_s = pyo.Param(b.set_S, within=pyo.NonNegativeReals)
     
-    # price function convexity
-
-    model.param_price_function_is_convex = pyo.Param(
-        model.set_GLQPK, 
-        within=pyo.Boolean
+        # *********************************************************************
+        # *********************************************************************
+    
+        # variables
+    
+        # *********************************************************************
+        # *********************************************************************
+        
+        # TODO: consider replacing None in the bounds with inf in the parameter
+    
+        # import and export flows
+        def bounds_var_trans_flows_s(b, s):
+            if s in b.param_v_max_s:
+                # predefined finite capacity
+                return (0, b.param_v_max_s[s])
+            else:
+                # infinite capacity
+                return (0, None)
+        b.var_trans_flows_s = pyo.Var(
+            b.set_S, 
+            within=pyo.NonNegativeReals, 
+            bounds=bounds_var_trans_flows_s
         )
-
-    # maximum resource volumes for each prices
-
-    model.param_v_max_glqpks = pyo.Param(
-        model.set_GLQPKS, 
-        within=pyo.NonNegativeReals
+        
+        # *********************************************************************
+        # *********************************************************************
+    
+        # import flow costs and export flow revenues
+        def rule_constr_trans_monetary_flows(b):
+            if (g,l) in b.parent_block().set_GL_imp:
+                return (
+                    sum(b.var_trans_flows_s[s]*b.param_p_s[s]
+                        for s in b.set_S)
+                    == b.parent_block().var_ifc_glqpk[(g,l,q,p,k)]
+                )
+            else:
+                return (
+                    sum(b.var_trans_flows_s[s]*b.param_p_s[s]
+                        for s in b.set_S)
+                    == b.parent_block().var_efr_glqpk[(g,l,q,p,k)]
+                )
+        b.constr_trans_monetary_flows = pyo.Constraint(
+            rule=rule_constr_trans_monetary_flows
         )
-    \
-    # price block
-    model.price_block = pyo.Block(model.set_GLQPK)
-
-    # *************************************************************************
-    # *************************************************************************
-
-    # variables
-
-    # *************************************************************************
-    # *************************************************************************
-
-    # import and export flows
-    def bounds_var_trans_flows_glqpks(m, g, l, q, p, k, s):
-        if (g, l, q, p, k, s) in m.param_v_max_glqpks:
-            # predefined finite capacity
-            return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)])
-        else:
-            # infinite capacity
-            return (0, None)
-    model.var_trans_flows_glqpks = pyo.Var(
-        model.set_GLQPKS, within=pyo.NonNegativeReals, bounds=bounds_var_trans_flows_glqpks
-    )
-
-    # *************************************************************************
-    # *************************************************************************
-
-    # import flow costs and export flow revenues
-    def rule_constr_trans_monetary_flows(m, g, l, q, p, k):
-        if (g,l) in m.set_GL_imp:
+        
+        # imported and exported flows
+        def rule_constr_trans_flows(b):
+            if (g,l) in b.parent_block().set_GL_imp:
+                return sum(
+                    b.parent_block().var_v_glljqk[(g,l,l_star,j,q,k)]
+                    for l_star in b.parent_block().set_L[g]
+                    if l_star not in b.parent_block().set_L_imp[g]
+                    for j in b.parent_block().set_J[(g,l,l_star)]  # only directed arcs
+                ) == sum(b.var_trans_flows_s[s] for s in b.set_S)
+            else:
+                return sum(
+                    b.parent_block().var_v_glljqk[(g,l_star,l,j,q,k)]
+                    * b.parent_block().param_eta_glljqk[(g,l_star,l,j,q,k)]
+                    for l_star in b.parent_block().set_L[g]
+                    if l_star not in b.parent_block().set_L_exp[g]
+                    for j in b.parent_block().set_J[(g,l_star,l)]  # only directed arcs
+                ) == sum(b.var_trans_flows_s[s] for s in b.set_S)
+        b.constr_trans_flows = pyo.Constraint(rule=rule_constr_trans_flows)
+    
+        # *********************************************************************
+        # *********************************************************************
+        
+        # non-convex price functions
+        
+        # delta variables
+        b.var_active_segment_s = pyo.Var(b.set_S, within=pyo.Binary)
+        
+        # segments must be empty if the respective delta variable is zero
+        def rule_constr_empty_segment_if_delta_zero(b, s):
+            if len(b.set_S) == 1 or b.param_price_function_is_convex:
+                # single segment, skip
+                # convex, skip
+                return pyo.Constraint.Skip
             return (
-                sum(
-                    m.var_trans_flows_glqpks[(g, l, q, p, k, s)]
-                    * m.param_p_glqpks[(g, l, q, p, k, s)]
-                    for s in m.set_S[(g, l, q, p, k)]
+                b.var_trans_flows_s[s] <= 
+                b.param_v_max_s[s]*b.var_active_segment_s[s]
                 )
-                == m.var_ifc_glqpk[(g, l, q, p, k)]
+        b.constr_empty_segment_if_delta_zero = pyo.Constraint(
+            b.set_S, rule=rule_constr_empty_segment_if_delta_zero
             )
-        else:
+        
+        # if delta var is one, previous ones must be one too
+        # if delta var is zero, the next ones must also be zero
+        def rule_constr_delta_summing_logic(b, s):
+            if s == len(b.set_S)-1 or b.param_price_function_is_convex:
+                # last segment, skip
+                # convex, skip
+                return pyo.Constraint.Skip
             return (
-                sum(
-                    m.var_trans_flows_glqpks[(g, l, q, p, k, s)]
-                    * m.param_p_glqpks[(g, l, q, p, k, s)]
-                    for s in m.set_S[(g, l, q, p, k)]
+                b.var_active_segment_s[s] >= 
+                b.var_active_segment_s[s+1]
                 )
-                == m.var_efr_glqpk[(g, l, q, p, k)]
+        b.constr_delta_summing_logic = pyo.Constraint(
+            b.set_S, rule=rule_constr_delta_summing_logic
             )
-    model.constr_trans_monetary_flows = pyo.Constraint(
-        model.set_GLQPK, rule=rule_constr_trans_monetary_flows
-    )
-
-    # imported and exported flows
-    def rule_constr_trans_flows(m, g, l, q, p, k):
-        if (g,l) in m.set_GL_imp:
-            return sum(
-                m.var_v_glljqk[(g, l, l_star, j, q, k)]
-                for l_star in m.set_L[g]
-                if l_star not in m.set_L_imp[g]
-                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)])
-        else:
-            return sum(
-                m.var_v_glljqk[(g, l_star, l, j, q, k)]
-                * m.param_eta_glljqk[(g, l_star, l, j, q, k)]
-                for l_star in m.set_L[g]
-                if l_star not in m.set_L_exp[g]
-                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)])
-
-    model.constr_trans_flows = pyo.Constraint(
-        model.set_GLQPK, rule=rule_constr_trans_flows
-    )
-
-    # *************************************************************************
-    # *************************************************************************
-    
-    # non-convex price functions
         
-    # delta variables
-    model.var_active_segment_glqpks = pyo.Var(
-        model.set_GLQPKS, within=pyo.Binary
-    )
-    
-    # segments must be empty if the respective delta variable is zero
-    def rule_constr_empty_segment_if_delta_zero(m, g, l, q, p, k, s):
-        if len(m.set_S[(g,l,q,p,k)]) == 1 or m.param_price_function_is_convex[(g,l,q,p,k)]:
-            # single segment, skip
-            # convex, skip
-            return pyo.Constraint.Skip
-        return (
-            m.var_trans_flows_glqpks[(g,l,q,p,k,s)] <= 
-            m.param_v_max_glqpks[(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.set_GLQPKS, rule=rule_constr_empty_segment_if_delta_zero
-        )
-    
-    # if delta var is one, previous ones must be one too
-    # if delta var is zero, the next ones must also be zero
-    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
-            # convex, skip
-            return pyo.Constraint.Skip
-        return (
-            m.var_active_segment_glqpks[(g,l,q,p,k,s)] >= 
-            m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]
-            )
-    model.constr_delta_summing_logic = pyo.Constraint(
-        model.set_GLQPKS, rule=rule_constr_delta_summing_logic
-        )
-    
-    # if a segment is not completely used, the next ones must remain empty
-    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 or m.param_price_function_is_convex[(g,l,q,p,k)]:
-            # last segment, skip
-            # convex, skip
-            return pyo.Constraint.Skip
-        return (
-            m.var_trans_flows_glqpks[(g,l,q,p,k,s)] >= 
-            m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]*
-            m.param_v_max_glqpks[(g,l,q,p,k,s)]
+        # if a segment is not completely used, the next ones must remain empty
+        def rule_constr_fill_up_segment_before_next(b, s):
+            if s == len(b.set_S)-1 or b.param_price_function_is_convex:
+                # last segment, skip
+                # convex, skip
+                return pyo.Constraint.Skip
+            return (
+                b.var_trans_flows_s[s] >= 
+                b.var_active_segment_s[s+1]*
+                b.param_v_max_s[s]
+                )
+        b.constr_fill_up_segment_before_next = pyo.Constraint(
+            b.set_S, rule=rule_constr_fill_up_segment_before_next
             )
-        # return (
-        #     m.var_if_glqpks[(g,l,q,p,k,s)]/m.param_v_max_glqpks[(g,l,q,p,k,s)] >= 
-        #     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
-        )
-    # *************************************************************************
-    # *************************************************************************
-    
-    # # non-convex price functions
-    
-    # if not convex_price_function:
         
-    #     # delta variables
-    #     model.var_active_segment_glqpks = pyo.Var(
-    #         model.set_GLQPKS, within=pyo.Binary
-    #     )
+        # *********************************************************************
+        # ********************************************************************* 
         
-    #     # segments must be empty if the respective delta variable is zero
-    #     def rule_constr_empty_segment_if_delta_zero_imp(m, g, l, q, p, k, s):
-    #         return (
-    #             m.var_if_glqpks[(g,l,q,p,k,s)] <= 
-    #             m.param_v_max_glqpks[(g,l,q,p,k,s)]*
-    #             m.var_active_segment_glqpks[(g,l,q,p,k,s)]
-    #             )
-    #     model.constr_empty_segment_if_delta_zero_imp = pyo.Constraint(
-    #         model.set_GLQPKS_imp, rule=rule_constr_empty_segment_if_delta_zero_imp
-    #         )
-            
-    #     # segments must be empty if the respective delta variable is zero
-    #     def rule_constr_empty_segment_if_delta_zero_exp(m, g, l, q, p, k, s):
-    #         return (
-    #             m.var_ef_glqpks[(g,l,q,p,k,s)] <= 
-    #             m.param_v_max_glqpks[(g,l,q,p,k,s)]*
-    #             m.var_active_segment_glqpks[(g,l,q,p,k,s)]
-    #             )
-    #     model.constr_empty_segment_if_delta_zero_exp = pyo.Constraint(
-    #         model.set_GLQPKS_exp, rule=rule_constr_empty_segment_if_delta_zero_exp
-    #         )
-        
-    #     # if delta var is one, previous ones must be one too
-    #     def rule_constr_delta_summing_logic(m, g, l, q, p, k, s):
-    #         if s == len(m.set_S)-1:
-    #             return pyo.Constraint.Skip
-    #         return (
-    #             m.var_active_segment_glqpks[(g,l,q,p,k,s)] >= 
-    #             m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]
-    #             )
-    #     model.constr_delta_summing_logic = pyo.Constraint(
-    #         model.set_GLQPKS, rule=rule_constr_delta_summing_logic
-    #         )
-    #     # if delta var is zero, subsequent ones must also be zero
-    #     def rule_constr_delta_next_zeros(m, g, l, q, p, k, s):
-    #         if s == len(m.set_S)-1:
-    #             return pyo.Constraint.Skip
-    #         return (
-    #             1-m.var_active_segment_glqpks[(g,l,q,p,k,s)] >= 
-    #             m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]
-    #             )
-    #     model.constr_delta_next_zeros = pyo.Constraint(
-    #         model.set_GLQPKS, rule=rule_constr_delta_next_zeros
-    #         )
+    model.block_prices = pyo.Block(model.set_GLQPK, rule=rule_block_prices)
 
     # *************************************************************************
     # *************************************************************************
-    
-    
+
+# *****************************************************************************
+# *****************************************************************************
+# *****************************************************************************
+# *****************************************************************************
+# *****************************************************************************
+# *****************************************************************************
+# *****************************************************************************
+# *****************************************************************************
 # *****************************************************************************
 # *****************************************************************************
-
-# def price_other2(
-#     model: pyo.AbstractModel,
-#     convex_price_function: bool = False,
-#     enable_default_values: bool = True,
-#     enable_validation: bool = True,
-#     enable_initialisation: bool = True
-#     ):
-
-#     # set of price segments
-#     model.set_S = pyo.Set(model.set_GL_exp_imp, model.set_QPK)
-
-#     # set of GLQKS tuples
-#     def init_set_GLQPKS(m):
-#         return (
-#             (g, l, q, p, k, s)
-#             # for (g,l) in m.set_GL_exp_imp
-#             # for (q,k) in m.set_QK
-#             for (g, l, q, p, k) in m.set_S
-#             for s in m.set_S[(g, l, q, p, k)]
-#         )
-
-#     model.set_GLQPKS = pyo.Set(
-#         dimen=6, initialize=(init_set_GLQPKS if enable_initialisation else None)
-#     )
-
-#     def init_set_GLQPKS_exp(m):
-#         return (
-#             glqpks for glqpks in m.set_GLQPKS if glqpks[1] in m.set_L_exp[glqpks[0]]
-#         )
-
-#     model.set_GLQPKS_exp = pyo.Set(
-#         dimen=6, initialize=(init_set_GLQPKS_exp if enable_initialisation else None)
-#     )
-
-#     def init_set_GLQPKS_imp(m):
-#         return (
-#             glqpks for glqpks in m.set_GLQPKS if glqpks[1] in m.set_L_imp[glqpks[0]]
-#         )
-
-#     model.set_GLQPKS_imp = pyo.Set(
-#         dimen=6, initialize=(init_set_GLQPKS_imp if enable_initialisation else None)
-#     )
-
-#     # *************************************************************************
-#     # *************************************************************************
-
-#     # parameters
-
-#     # resource prices
-
-#     model.param_p_glqpks = pyo.Param(model.set_GLQPKS, within=pyo.NonNegativeReals)
-
-#     # maximum resource volumes for each prices
-
-#     model.param_v_max_glqpks = pyo.Param(
-#         model.set_GLQPKS, 
-#         within=pyo.NonNegativeReals
-#         )
-
-#     # *************************************************************************
-#     # *************************************************************************
-
-#     # variables
-
-#     # *************************************************************************
-#     # *************************************************************************
-
-#     # exported flow
-
-#     # TODO: validate the bounds by ensuring inf. cap. only exists in last segm.
-
-#     def bounds_var_ef_glqpks(m, g, l, q, p, k, s):
-#         if (g, l, q, p, k, s) in m.param_v_max_glqpks:
-#             # predefined finite capacity
-#             return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)])
-#         else:
-#             # infinite capacity
-#             return (0, None)
-
-#     model.var_ef_glqpks = pyo.Var(
-#         model.set_GLQPKS_exp, within=pyo.NonNegativeReals, bounds=bounds_var_ef_glqpks
-#     )
-
-#     # imported flow
-
-#     def bounds_var_if_glqpks(m, g, l, q, p, k, s):
-#         if (g, l, q, p, k, s) in m.param_v_max_glqpks:
-#             # predefined finite capacity
-#             return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)])
-#         else:
-#             # infinite capacity
-#             return (0, None)
-
-#     model.var_if_glqpks = pyo.Var(
-#         model.set_GLQPKS_imp, within=pyo.NonNegativeReals, bounds=bounds_var_if_glqpks
-#     )
-
-#     # *************************************************************************
-#     # *************************************************************************
-
-#     # exported flow revenue
-#     def rule_constr_exp_flow_revenue(m, g, l, q, p, k):
-#         return (
-#             sum(
-#                 m.var_ef_glqpks[(g, l, q, p, k, s)]
-#                 * m.param_p_glqpks[(g, l, q, p, k, s)]
-#                 for s in m.set_S[(g, l, q, p, k)]
-#             )
-#             == m.var_efr_glqpk[(g, l, q, p, k)]
-#         )
-
-#     model.constr_exp_flow_revenue = pyo.Constraint(
-#         model.set_GL_exp, model.set_QPK, rule=rule_constr_exp_flow_revenue
-#     )
-
-#     # imported flow cost
-#     def rule_constr_imp_flow_cost(m, g, l, q, p, k):
-#         return (
-#             sum(
-#                 m.var_if_glqpks[(g, l, q, p, k, s)]
-#                 * m.param_p_glqpks[(g, l, q, p, k, s)]
-#                 for s in m.set_S[(g, l, q, p, k)]
-#             )
-#             == m.var_ifc_glqpk[(g, l, q, p, k)]
-#         )
-
-#     model.constr_imp_flow_cost = pyo.Constraint(
-#         model.set_GL_imp, model.set_QPK, rule=rule_constr_imp_flow_cost
-#     )
-
-#     # exported flows
-#     def rule_constr_exp_flows(m, g, l, q, p, k):
-#         return sum(
-#             m.var_v_glljqk[(g, l_star, l, j, q, k)]
-#             * m.param_eta_glljqk[(g, l_star, l, j, q, k)]
-#             for l_star in m.set_L[g]
-#             if l_star not in m.set_L_exp[g]
-#             for j in m.set_J[(g, l_star, l)]  # only directed arcs
-#         ) == sum(m.var_ef_glqpks[(g, l, q, p, k, s)] for s in m.set_S[(g, l, q, p, k)])
-
-#     model.constr_exp_flows = pyo.Constraint(
-#         model.set_GL_exp, model.set_QPK, rule=rule_constr_exp_flows
-#     )
-
-#     # imported flows
-#     def rule_constr_imp_flows(m, g, l, q, p, k):
-#         return sum(
-#             m.var_v_glljqk[(g, l, l_star, j, q, k)]
-#             for l_star in m.set_L[g]
-#             if l_star not in m.set_L_imp[g]
-#             for j in m.set_J[(g, l, l_star)]  # only directed arcs
-#         ) == sum(m.var_if_glqpks[(g, l, q, p, k, s)] for s in m.set_S[(g, l, q, p, k)])
-
-#     model.constr_imp_flows = pyo.Constraint(
-#         model.set_GL_imp, model.set_QPK, rule=rule_constr_imp_flows
-#     )
-
-#     # *************************************************************************
-#     # *************************************************************************
-    
-#     # non-convex price functions
-    
-#     if not convex_price_function:
-        
-#         # delta variables
-#         model.var_active_segment_glqpks = pyo.Var(
-#             model.set_GLQPKS, within=pyo.Binary
-#         )
-        
-#         # segments must be empty if the respective delta variable is zero
-#         def rule_constr_empty_segment_if_delta_zero_imp(m, g, l, q, p, k, s):
-#             return (
-#                 m.var_if_glqpks[(g,l,q,p,k,s)] <= 
-#                 m.param_v_max_glqpks[(g,l,q,p,k,s)]*
-#                 m.var_active_segment_glqpks[(g,l,q,p,k,s)]
-#                 )
-#         model.constr_empty_segment_if_delta_zero_imp = pyo.Constraint(
-#             model.set_GLQPKS_imp, rule=rule_constr_empty_segment_if_delta_zero_imp
-#             )
-            
-#         # segments must be empty if the respective delta variable is zero
-#         def rule_constr_empty_segment_if_delta_zero_exp(m, g, l, q, p, k, s):
-#             return (
-#                 m.var_ef_glqpks[(g,l,q,p,k,s)] <= 
-#                 m.param_v_max_glqpks[(g,l,q,p,k,s)]*
-#                 m.var_active_segment_glqpks[(g,l,q,p,k,s)]
-#                 )
-#         model.constr_empty_segment_if_delta_zero_exp = pyo.Constraint(
-#             model.set_GLQPKS_exp, rule=rule_constr_empty_segment_if_delta_zero_exp
-#             )
-        
-#         # if delta var is one, previous ones must be one too
-#         # if delta var is zero, the next ones must also be zero
-#         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:
-#                 # last segment, skip
-#                 return pyo.Constraint.Skip
-#             return (
-#                 m.var_active_segment_glqpks[(g,l,q,p,k,s)] >= 
-#                 m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]
-#                 )
-#         model.constr_delta_summing_logic = pyo.Constraint(
-#             model.set_GLQPKS, rule=rule_constr_delta_summing_logic
-#             )
-        
-#         # if a segment is not completely used, the next ones must remain empty
-#         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:
-#                 # last segment, skip
-#                 return pyo.Constraint.Skip
-#             if (g,l) in m.set_GL_imp:
-#                 return (
-#                     m.var_if_glqpks[(g,l,q,p,k,s)] >= 
-#                     m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]*
-#                     m.param_v_max_glqpks[(g,l,q,p,k,s)]
-#                     )
-#             else:
-#                 return (
-#                     m.var_ef_glqpks[(g,l,q,p,k,s)] >= 
-#                     m.var_active_segment_glqpks[(g,l,q,p,k,s+1)]*
-#                     m.param_v_max_glqpks[(g,l,q,p,k,s)]
-#                     )
-#             # return (
-#             #     m.var_if_glqpks[(g,l,q,p,k,s)]/m.param_v_max_glqpks[(g,l,q,p,k,s)] >= 
-#             #     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
-#             )
-
 # *****************************************************************************
 # *****************************************************************************
 
 def price_other(
     model: pyo.AbstractModel,
-    convex_price_function: bool = True,
+    # convex_price_function: bool = True,
     enable_default_values: bool = True,
     enable_validation: bool = True,
     enable_initialisation: bool = True
@@ -555,7 +249,8 @@ def price_other(
 
     model.param_v_max_glqpks = pyo.Param(
         model.set_GLQPKS, 
-        within=pyo.NonNegativeReals
+        within=pyo.NonNegativeReals,
+        # default=math.inf
         )
 
     # *************************************************************************
@@ -568,6 +263,7 @@ def price_other(
 
     # import and export flows
     def bounds_var_trans_flows_glqpks(m, g, l, q, p, k, s):
+        # return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)])
         if (g, l, q, p, k, s) in m.param_v_max_glqpks:
             # predefined finite capacity
             return (0, m.param_v_max_glqpks[(g, l, q, p, k, s)])
@@ -693,6 +389,8 @@ def price_other(
 # *****************************************************************************
 # *****************************************************************************
 
+# TODO: implement the lambda model
+
 def price_block_lambda(model: pyo.AbstractModel, **kwargs):
     
     raise NotImplementedError
@@ -700,6 +398,8 @@ def price_block_lambda(model: pyo.AbstractModel, **kwargs):
 # *****************************************************************************
 # *****************************************************************************
 
+# TODO: implement the delta model
+
 def price_block_delta(model: pyo.AbstractModel, **kwargs):
     
     raise NotImplementedError
diff --git a/src/topupopt/problems/esipp/model.py b/src/topupopt/problems/esipp/model.py
index 62e387c..ade66bb 100644
--- a/src/topupopt/problems/esipp/model.py
+++ b/src/topupopt/problems/esipp/model.py
@@ -3,7 +3,7 @@
 import pyomo.environ as pyo
 
 from .blocks.networks import add_network_restrictions
-from .blocks.prices import add_prices_block
+from .blocks.prices import add_price_functions
 
 # *****************************************************************************
 # *****************************************************************************
@@ -57,33 +57,26 @@ def create_model(
     # *************************************************************************
 
     # set of assessments
-
     model.set_Q = pyo.Set()
 
     # TODO: use rangesets for time-related sets
 
     # set of time step intervals for each assessment
-
     model.set_K_q = pyo.Set(model.set_Q)
 
     # set of representative evaluation periods for each assessment
-
     model.set_P_q = pyo.Set(model.set_Q)
 
     # set of networks
-
     model.set_G = pyo.Set()
 
     # set of nodes on each network
-
     model.set_L = pyo.Set(model.set_G)
 
     # set of importing nodes on each network
-
     model.set_L_imp = pyo.Set(model.set_G, within=model.set_L)
 
     # set of exporting nodes on each network
-
     model.set_L_exp = pyo.Set(model.set_G, within=model.set_L)
     
     # *************************************************************************
@@ -2163,7 +2156,7 @@ def create_model(
     )
     
     # prices
-    add_prices_block(model)
+    add_price_functions(model)
 
     # *************************************************************************
     # *************************************************************************
diff --git a/src/topupopt/problems/esipp/problem.py b/src/topupopt/problems/esipp/problem.py
index 8b0dd6b..c05b664 100644
--- a/src/topupopt/problems/esipp/problem.py
+++ b/src/topupopt/problems/esipp/problem.py
@@ -2548,7 +2548,7 @@ class InfrastructurePlanningProblem(EnergySystem):
         }
 
         # maximum resource volume per segment (infinity is the default)
-
+        
         param_v_max_glqpks = {
             (g, l, q, p, k, s): self.networks[g]
             .nodes[l][Network.KEY_NODE_PRICES][(q, p, k)]
@@ -3511,6 +3511,16 @@ class InfrastructurePlanningProblem(EnergySystem):
                 "param_arc_sns_sos1_weights_glljqk": param_arc_sns_sos1_weights_glljqk,
                 # *****************************************************************
                 # *****************************************************************
+                "block_prices": {
+                    (*gl,*qpk): {
+                        'param_price_function_is_convex': {None: param_price_function_is_convex[(*gl,*qpk)]}, 
+                        'set_S': {None: set_S[(*gl,*qpk)]}, 
+                        'param_p_s': {s: param_p_glqpks[(*gl,*qpk,s)] for s in set_S[(*gl,*qpk)]}, 
+                        'param_v_max_s': {s: param_v_max_glqpks[(*gl,*qpk,s)] for s in set_S[(*gl,*qpk)] if (*gl,*qpk,s) in param_v_max_glqpks},
+                        }
+                    for gl in set_GL_exp_imp
+                    for qpk in set_QPK
+                    }
             }
         }
 
diff --git a/tests/test_esipp_prices.py b/tests/test_esipp_prices.py
index 0bccfd4..9f3bf25 100644
--- a/tests/test_esipp_prices.py
+++ b/tests/test_esipp_prices.py
@@ -681,7 +681,7 @@ class TestESIPPProblem:
             max_number_parallel_arcs={},
             simplify_problem=False,
         )
-
+        ipp.instance.pprint()
         assert not ipp.has_peak_total_assessments()
         assert ipp.results["Problem"][0]["Number of constraints"] == 14 # 10 before nonconvex block
         assert ipp.results["Problem"][0]["Number of variables"] == 13 # 11 before nonconvex block
-- 
GitLab