1- """test_variable_func.py"""
1+ """test_variable_func.py: Unit tests for variable_func.py. """
22
33import unittest
44from unittest .mock import MagicMock , patch , call
55import pandas as pd
66
7+ # Assuming variable_func is in pownet.optim_model directory
8+ # Adjust the import path if your directory structure is different.
9+ # For example, if pownet is in your PYTHONPATH:
710from pownet .optim_model import variable_func
811
12+ # If variable_func.py is in the same directory as the test for local testing,
13+ # you might use:
14+ # import variable_func
15+
916
1017class TestVariableFunctions (unittest .TestCase ):
1118
12- @classmethod
13- def setUpClass (cls ):
14- """Set up class-level resources or patches before any tests in the class run."""
15- variable_func .VAR_PREFIX_THERMAL_GENERATION = "thermal_generation"
19+ # Removed setUpClass as VAR_PREFIX_THERMAL_GENERATION is no longer used
20+ # in variable_func.py
1621
1722 def setUp (self ):
1823 """Set up common test data and mocks."""
1924
20- self .VAR_PREFIX_THERMAL_GENERATION = "thermal_generation"
21-
22- self .timesteps = range (
23- 3
24- ) # Global timestep as per user request (implicitly via usage)
25+ self .timesteps = range (3 )
2526 self .units = ["gen_A" , "gen_B" ]
2627 self .edges = [("node1" , "node2" ), ("node2" , "node3" )]
27- self .step_k = 1 # Default step_k, can be overridden in specific tests
28+ self .step_k = 1 # Default step_k, can be overridden
2829
2930 # Mock Gurobi model
3031 self .mock_model = MagicMock ()
@@ -37,9 +38,6 @@ def setUp(self):
3738 self .dummy_capacity_df = pd .DataFrame ({"dummy_col" : [1 , 2 , 3 ]})
3839
3940 # More structured capacity DataFrame for update_flow_vars
40- # Index needs to cover t + (step_k - 1) * 24
41- # For step_k=1, t=0,1,2 -> index 0,1,2
42- # For step_k=2, t=0,1,2 -> index 24,25,26
4341 idx = pd .RangeIndex (start = 0 , stop = 50 , step = 1 ) # Sufficient for a few steps
4442 data_for_flow_df = {
4543 self .edges [0 ]: [100 + i for i in range (50 )],
@@ -54,12 +52,12 @@ def test_add_var_with_variable_ub(self, mock_get_capacity_value, mock_grb_module
5452 var_name = "test_var"
5553 step_k_test = 2
5654
55+ # Mock the GRB constant
5756 mock_grb_module .CONTINUOUS = "MOCK_GRB_CONTINUOUS_TYPE"
5857
59- # Define a side effect for mock_get_capacity_value to simulate different capacities
58+ # Define a side effect for mock_get_capacity_value
6059 def capacity_side_effect (t , unit , sk , df ):
61- # sk is step_k, df is capacity_df
62- # Simple unique value based on inputs for verification
60+ self .assertIs (df , self .dummy_capacity_df ) # Ensure correct df is passed
6361 if unit == self .units [0 ]:
6462 return 100 + t + sk
6563 elif unit == self .units [1 ]:
@@ -68,7 +66,7 @@ def capacity_side_effect(t, unit, sk, df):
6866
6967 mock_get_capacity_value .side_effect = capacity_side_effect
7068
71- # Expected upper bounds dictionary
69+ # Expected upper bounds dictionary based on the side effect
7270 expected_ub_dict = {}
7371 for t_val in self .timesteps :
7472 for unit_val in self .units :
@@ -92,7 +90,7 @@ def capacity_side_effect(t, unit, sk, df):
9290 self .timesteps ,
9391 lb = 0 ,
9492 ub = expected_ub_dict ,
95- vtype = "MOCK_GRB_CONTINUOUS_TYPE" , # Check if the mocked GRB type is used
93+ vtype = "MOCK_GRB_CONTINUOUS_TYPE" ,
9694 name = var_name ,
9795 )
9896
@@ -114,151 +112,125 @@ def capacity_side_effect(t, unit, sk, df):
114112 )
115113
116114 @patch ("pownet.optim_model.variable_func.get_capacity_value" )
117- @patch ("pownet.optim_model.variable_func.get_unit_hour_from_varname" )
118- def test_update_var_with_variable_ub (
119- self , mock_get_unit_hour_from_varname , mock_get_capacity_value
120- ):
115+ def test_update_var_with_variable_ub (self , mock_get_capacity_value ):
121116 """Test the update_var_with_variable_ub function."""
122117 step_k_test = 1
123118
124- # Create mock Gurobi variables
119+ # Create mock Gurobi variables (VarName is not used by the new function logic)
125120 mock_gvar1 = MagicMock ()
126- mock_gvar1 .VarName = (
127- f"{ variable_func .VAR_PREFIX_THERMAL_GENERATION } _{ self .units [0 ]} [0]"
128- )
129121 mock_gvar1 .ub = 0 # Initial ub
130122
131123 mock_gvar2 = MagicMock ()
132- mock_gvar2 .VarName = (
133- f"{ variable_func .VAR_PREFIX_THERMAL_GENERATION } _{ self .units [1 ]} [1]"
134- )
135124 mock_gvar2 .ub = 0 # Initial ub
136125
137- # Simulate a gp.tupledict by using a dictionary of these mocks
138- # The function iterates over .values()
126+ mock_gvar3 = MagicMock () # For a different timestep
127+ mock_gvar3 .ub = 0
128+
129+ # Simulate a gp.tupledict by using a Python dictionary of these mocks
130+ # Keys are (unit, t) as expected by the function's iteration
139131 mock_variables_dict = {
140- (self .units [0 ], 0 ): mock_gvar1 ,
132+ (self .units [0 ], 0 ): mock_gvar1 , # (unit, t)
141133 (self .units [1 ], 1 ): mock_gvar2 ,
134+ (self .units [0 ], 2 ): mock_gvar3 ,
142135 }
143136
144- # Configure side effect for get_unit_hour_from_varname
145- def unit_hour_side_effect (var_name ):
146- if var_name == mock_gvar1 .VarName :
147- return self .units [0 ], 0
148- elif var_name == mock_gvar2 .VarName :
149- return self .units [1 ], 1
150- return None , None # Should not happen with controlled inputs
151-
152- mock_get_unit_hour_from_varname .side_effect = unit_hour_side_effect
153-
154137 # Configure side effect for get_capacity_value
155- # Capacity depends on unit, t, and step_k
156138 expected_capacity_gvar1 = 150
157139 expected_capacity_gvar2 = 250
140+ expected_capacity_gvar3 = 175
158141
159- def capacity_side_effect (t , unit , sk , df ):
160- self .assertEqual (sk , step_k_test ) # Check step_k is passed correctly
161- self .assertIs (df , self .dummy_capacity_df ) # Check df is passed correctly
162- if unit == self .units [0 ] and t == 0 :
142+ def capacity_side_effect (t_arg , unit_arg , sk_arg , df_arg ):
143+ self .assertEqual (sk_arg , step_k_test )
144+ self .assertIs (df_arg , self .dummy_capacity_df )
145+ if unit_arg == self .units [0 ] and t_arg == 0 :
163146 return expected_capacity_gvar1
164- elif unit == self .units [1 ] and t == 1 :
147+ elif unit_arg == self .units [1 ] and t_arg == 1 :
165148 return expected_capacity_gvar2
166- return 0 # Default, should not be hit with specific var names
149+ elif unit_arg == self .units [0 ] and t_arg == 2 :
150+ return expected_capacity_gvar3
151+ return 0 # Default, should not be hit
167152
168153 mock_get_capacity_value .side_effect = capacity_side_effect
169154
170155 # Call the function
171- # Pass the .values() if the function expects an iterable of Gurobi variables
172- # The type hint is gp.tupledict, so we pass the dict itself.
173156 variable_func .update_var_with_variable_ub (
174157 variables = mock_variables_dict ,
175158 step_k = step_k_test ,
176159 capacity_df = self .dummy_capacity_df ,
177160 )
178161
179162 # Assertions
180- # Check get_unit_hour_from_varname calls
181- mock_get_unit_hour_from_varname .assert_any_call (mock_gvar1 .VarName )
182- mock_get_unit_hour_from_varname .assert_any_call (mock_gvar2 .VarName )
183- self .assertEqual (mock_get_unit_hour_from_varname .call_count , 2 )
184-
185163 # Check get_capacity_value calls
186- mock_get_capacity_value .assert_any_call (
187- 0 , self .units [0 ], step_k_test , self .dummy_capacity_df
188- )
189- mock_get_capacity_value .assert_any_call (
190- 1 , self .units [1 ], step_k_test , self .dummy_capacity_df
191- )
192- self .assertEqual (mock_get_capacity_value .call_count , 2 )
164+ # The calls are made based on the keys of mock_variables_dict
165+ expected_calls = [
166+ call (0 , self .units [0 ], step_k_test , self .dummy_capacity_df ),
167+ call (1 , self .units [1 ], step_k_test , self .dummy_capacity_df ),
168+ call (2 , self .units [0 ], step_k_test , self .dummy_capacity_df ),
169+ ]
170+ mock_get_capacity_value .assert_has_calls (expected_calls , any_order = True )
171+ self .assertEqual (mock_get_capacity_value .call_count , len (mock_variables_dict ))
193172
194173 # Check if variable upper bounds were updated
195174 self .assertEqual (mock_gvar1 .ub , expected_capacity_gvar1 )
196175 self .assertEqual (mock_gvar2 .ub , expected_capacity_gvar2 )
176+ self .assertEqual (mock_gvar3 .ub , expected_capacity_gvar3 )
197177
198- @patch ("pownet.optim_model.variable_func.get_edge_hour_from_varname" )
199- def test_update_flow_vars (self , mock_get_edge_hour_from_varname ):
178+ def test_update_flow_vars (
179+ self ,
180+ ): # No external calls to mock within update_flow_vars directly
200181 """Test the update_flow_vars function."""
201- step_k_test = 2 # Using a different step_k to test the time indexing
182+ step_k_test = 2
202183 line_capacity_factor = 0.9
203184 hours_per_step = 24 # As defined in the source function
204185
205186 # Create mock Gurobi flow variables
206187 mock_flow_var1 = MagicMock ()
207- # Example VarName format, assuming some prefix like "flow_"
208- mock_flow_var1 .VarName = f"flow_{ self .edges [0 ][0 ]} _{ self .edges [0 ][1 ]} [0]"
209188 mock_flow_var1 .ub = 0 # Initial ub
210189
211190 mock_flow_var2 = MagicMock ()
212- mock_flow_var2 .VarName = (
213- f"flow_{ self .edges [1 ][0 ]} _{ self .edges [1 ][1 ]} [2]" # t=2 for this var
214- )
215191 mock_flow_var2 .ub = 0
216192
193+ # Keys are (node1, node2, t) as expected by the function's iteration
217194 mock_flow_variables_dict = {
218- (self .edges [0 ], 0 ): mock_flow_var1 ,
219- (self .edges [1 ], 2 ): mock_flow_var2 ,
195+ (
196+ self .edges [0 ][0 ],
197+ self .edges [0 ][1 ],
198+ 0 ,
199+ ): mock_flow_var1 , # ("node1", "node2", 0)
200+ (
201+ self .edges [1 ][0 ],
202+ self .edges [1 ][1 ],
203+ 2 ,
204+ ): mock_flow_var2 , # ("node2", "node3", 2)
220205 }
221206
222- # Configure side effect for get_edge_hour_from_varname
223- def edge_hour_side_effect (var_name ):
224- if var_name == mock_flow_var1 .VarName :
225- return self .edges [0 ], 0 # (edge_tuple, time_in_step)
226- elif var_name == mock_flow_var2 .VarName :
227- return self .edges [1 ], 2
228- return None , None
229-
230- mock_get_edge_hour_from_varname .side_effect = edge_hour_side_effect
231-
232207 # Call the function
233208 variable_func .update_flow_vars (
234- flow_variables = mock_flow_variables_dict , # Pass dict, function iterates .values()
209+ flow_variables = mock_flow_variables_dict ,
235210 step_k = step_k_test ,
236211 capacity_df = self .flow_capacity_df ,
237212 line_capacity_factor = line_capacity_factor ,
238213 )
239214
240- # Assertions
241- # Check get_edge_hour_from_varname calls
242- mock_get_edge_hour_from_varname .assert_any_call (mock_flow_var1 .VarName )
243- mock_get_edge_hour_from_varname .assert_any_call (mock_flow_var2 .VarName )
244- self .assertEqual (mock_get_edge_hour_from_varname .call_count , 2 )
245-
246215 # Calculate expected capacities and UBs
247- # For flow_var1: edge=self.edges[0], t=0
248- time_idx1 = 0 + (step_k_test - 1 ) * hours_per_step # 0 + (2-1)*24 = 24
249- expected_capacity1 = self .flow_capacity_df .loc [time_idx1 , self .edges [0 ]]
216+ # For flow_var1: edge=self.edges[0] ("node1", "node2"), t=0
217+ key1_node1 , key1_node2 , key1_t = self .edges [0 ][0 ], self .edges [0 ][1 ], 0
218+ edge1 = (key1_node1 , key1_node2 )
219+ time_idx1 = key1_t + (step_k_test - 1 ) * hours_per_step # 0 + (2-1)*24 = 24
220+ expected_capacity1 = self .flow_capacity_df .loc [time_idx1 , edge1 ]
250221 expected_ub1 = expected_capacity1 * line_capacity_factor
251222
252- # For flow_var2: edge=self.edges[1], t=2
253- time_idx2 = 2 + (step_k_test - 1 ) * hours_per_step # 2 + (2-1)*24 = 26
254- expected_capacity2 = self .flow_capacity_df .loc [time_idx2 , self .edges [1 ]]
223+ # For flow_var2: edge=self.edges[1] ("node2", "node3"), t=2
224+ key2_node1 , key2_node2 , key2_t = self .edges [1 ][0 ], self .edges [1 ][1 ], 2
225+ edge2 = (key2_node1 , key2_node2 )
226+ time_idx2 = key2_t + (step_k_test - 1 ) * hours_per_step # 2 + (2-1)*24 = 26
227+ expected_capacity2 = self .flow_capacity_df .loc [time_idx2 , edge2 ]
255228 expected_ub2 = expected_capacity2 * line_capacity_factor
256229
257230 # Check if flow variable upper bounds were updated
258231 self .assertEqual (mock_flow_var1 .ub , expected_ub1 )
259232 self .assertEqual (mock_flow_var2 .ub , expected_ub2 )
260233
261234
262- # This allows running the tests directly from the script
263235if __name__ == "__main__" :
264236 unittest .main ()
0 commit comments