|
| 1 | +import unittest |
| 2 | +from pownet.optim_model import objfunc |
| 3 | + |
| 4 | + |
| 5 | +class TestObjectiveFunctions(unittest.TestCase): |
| 6 | + |
| 7 | + def setUp(self): |
| 8 | + """Set up common test data.""" |
| 9 | + self.timesteps = range(3) # As per user request |
| 10 | + self.thermal_units = ["coal_unit_1", "gas_unit_1"] |
| 11 | + self.nondispatch_units = ["hydro_1", "solar_1"] |
| 12 | + |
| 13 | + def test_get_thermal_fixed_coeff(self): |
| 14 | + thermal_rated_capacity = {"coal_unit_1": 300, "gas_unit_1": 150} |
| 15 | + thermal_fixed_cost = {"coal_unit_1": 10, "gas_unit_1": 12} |
| 16 | + |
| 17 | + expected_coeffs = { |
| 18 | + ("coal_unit_1", 0): 300 * 10, |
| 19 | + ("coal_unit_1", 1): 300 * 10, |
| 20 | + ("coal_unit_1", 2): 300 * 10, |
| 21 | + ("gas_unit_1", 0): 150 * 12, |
| 22 | + ("gas_unit_1", 1): 150 * 12, |
| 23 | + ("gas_unit_1", 2): 150 * 12, |
| 24 | + } |
| 25 | + |
| 26 | + actual_coeffs = objfunc.get_thermal_fixed_coeff( |
| 27 | + timesteps=self.timesteps, |
| 28 | + thermal_units=self.thermal_units, |
| 29 | + thermal_fixed_cost=thermal_fixed_cost, |
| 30 | + thermal_rated_capacity=thermal_rated_capacity, |
| 31 | + ) |
| 32 | + self.assertEqual(actual_coeffs, expected_coeffs) |
| 33 | + |
| 34 | + def test_get_thermal_opex_coeff_step_1(self): |
| 35 | + step_k = 1 |
| 36 | + thermal_opex = {"coal_unit_1": 5, "gas_unit_1": 3} |
| 37 | + fuel_contracts = {"coal_unit_1": "coal_A", "gas_unit_1": "gas_B"} |
| 38 | + # contract_costs: (fuel_type, time_index) -> cost |
| 39 | + contract_costs = { |
| 40 | + ("coal_A", 0): 20, |
| 41 | + ("coal_A", 1): 21, |
| 42 | + ("coal_A", 2): 20, |
| 43 | + ("coal_A", 24): 25, |
| 44 | + ("gas_B", 0): 30, |
| 45 | + ("gas_B", 1): 32, |
| 46 | + ("gas_B", 2): 31, |
| 47 | + ("gas_B", 24): 35, |
| 48 | + } |
| 49 | + thermal_heat_rate = {"coal_unit_1": 1.5, "gas_unit_1": 1.2} |
| 50 | + |
| 51 | + expected_coeffs = { |
| 52 | + # (unit, t): (contract_costs[(fuel_contracts[unit], t + (step_k - 1) * 24)] * thermal_heat_rate[unit]) + thermal_opex[unit] |
| 53 | + ("coal_unit_1", 0): (contract_costs[("coal_A", 0 + (1 - 1) * 24)] * 1.5) |
| 54 | + + 5, # (20 * 1.5) + 5 = 30 + 5 = 35 |
| 55 | + ("coal_unit_1", 1): (contract_costs[("coal_A", 1 + (1 - 1) * 24)] * 1.5) |
| 56 | + + 5, # (21 * 1.5) + 5 = 31.5 + 5 = 36.5 |
| 57 | + ("coal_unit_1", 2): (contract_costs[("coal_A", 2 + (1 - 1) * 24)] * 1.5) |
| 58 | + + 5, # (20 * 1.5) + 5 = 30 + 5 = 35 |
| 59 | + ("gas_unit_1", 0): (contract_costs[("gas_B", 0 + (1 - 1) * 24)] * 1.2) |
| 60 | + + 3, # (30 * 1.2) + 3 = 36 + 3 = 39 |
| 61 | + ("gas_unit_1", 1): (contract_costs[("gas_B", 1 + (1 - 1) * 24)] * 1.2) |
| 62 | + + 3, # (32 * 1.2) + 3 = 38.4 + 3 = 41.4 |
| 63 | + ("gas_unit_1", 2): (contract_costs[("gas_B", 2 + (1 - 1) * 24)] * 1.2) |
| 64 | + + 3, # (31 * 1.2) + 3 = 37.2 + 3 = 40.2 |
| 65 | + } |
| 66 | + |
| 67 | + actual_coeffs = objfunc.get_thermal_opex_coeff( |
| 68 | + step_k=step_k, |
| 69 | + timesteps=self.timesteps, |
| 70 | + thermal_units=self.thermal_units, |
| 71 | + thermal_opex=thermal_opex, |
| 72 | + fuel_contracts=fuel_contracts, |
| 73 | + contract_costs=contract_costs, |
| 74 | + thermal_heat_rate=thermal_heat_rate, |
| 75 | + ) |
| 76 | + self.assertEqual(actual_coeffs, expected_coeffs) |
| 77 | + |
| 78 | + def test_get_thermal_opex_coeff_step_2(self): |
| 79 | + step_k = 2 # Test with a different step_k |
| 80 | + thermal_opex = {"coal_unit_1": 5, "gas_unit_1": 3} |
| 81 | + fuel_contracts = {"coal_unit_1": "coal_A", "gas_unit_1": "gas_B"} |
| 82 | + contract_costs = { |
| 83 | + # Need costs for t + (2-1)*24 => t + 24 |
| 84 | + ("coal_A", 24): 25, |
| 85 | + ("coal_A", 25): 26, |
| 86 | + ("coal_A", 26): 24, |
| 87 | + ("gas_B", 24): 35, |
| 88 | + ("gas_B", 25): 36, |
| 89 | + ("gas_B", 26): 34, |
| 90 | + } |
| 91 | + thermal_heat_rate = {"coal_unit_1": 1.5, "gas_unit_1": 1.2} |
| 92 | + |
| 93 | + expected_coeffs = { |
| 94 | + ("coal_unit_1", 0): (contract_costs[("coal_A", 0 + (2 - 1) * 24)] * 1.5) |
| 95 | + + 5, # (25 * 1.5) + 5 = 37.5 + 5 = 42.5 |
| 96 | + ("coal_unit_1", 1): (contract_costs[("coal_A", 1 + (2 - 1) * 24)] * 1.5) |
| 97 | + + 5, # (26 * 1.5) + 5 = 39 + 5 = 44 |
| 98 | + ("coal_unit_1", 2): (contract_costs[("coal_A", 2 + (2 - 1) * 24)] * 1.5) |
| 99 | + + 5, # (24 * 1.5) + 5 = 36 + 5 = 41 |
| 100 | + ("gas_unit_1", 0): (contract_costs[("gas_B", 0 + (2 - 1) * 24)] * 1.2) |
| 101 | + + 3, # (35 * 1.2) + 3 = 42 + 3 = 45 |
| 102 | + ("gas_unit_1", 1): (contract_costs[("gas_B", 1 + (2 - 1) * 24)] * 1.2) |
| 103 | + + 3, # (36 * 1.2) + 3 = 43.2 + 3 = 46.2 |
| 104 | + ("gas_unit_1", 2): (contract_costs[("gas_B", 2 + (2 - 1) * 24)] * 1.2) |
| 105 | + + 3, # (34 * 1.2) + 3 = 40.8 + 3 = 43.8 |
| 106 | + } |
| 107 | + |
| 108 | + actual_coeffs = objfunc.get_thermal_opex_coeff( |
| 109 | + step_k=step_k, |
| 110 | + timesteps=self.timesteps, |
| 111 | + thermal_units=self.thermal_units, |
| 112 | + thermal_opex=thermal_opex, |
| 113 | + fuel_contracts=fuel_contracts, |
| 114 | + contract_costs=contract_costs, |
| 115 | + thermal_heat_rate=thermal_heat_rate, |
| 116 | + ) |
| 117 | + self.assertEqual(actual_coeffs, expected_coeffs) |
| 118 | + |
| 119 | + def test_get_thermal_startup_coeff(self): |
| 120 | + thermal_rated_capacity = {"coal_unit_1": 300, "gas_unit_1": 150} |
| 121 | + thermal_startup_cost = { |
| 122 | + "coal_unit_1": 500, |
| 123 | + "gas_unit_1": 250, |
| 124 | + } # Cost per unit of capacity |
| 125 | + |
| 126 | + expected_coeffs = { |
| 127 | + ("coal_unit_1", 0): 300 * 500, |
| 128 | + ("coal_unit_1", 1): 300 * 500, |
| 129 | + ("coal_unit_1", 2): 300 * 500, |
| 130 | + ("gas_unit_1", 0): 150 * 250, |
| 131 | + ("gas_unit_1", 1): 150 * 250, |
| 132 | + ("gas_unit_1", 2): 150 * 250, |
| 133 | + } |
| 134 | + actual_coeffs = objfunc.get_thermal_startup_coeff( |
| 135 | + timesteps=self.timesteps, |
| 136 | + thermal_units=self.thermal_units, |
| 137 | + thermal_startup_cost=thermal_startup_cost, |
| 138 | + thermal_rated_capacity=thermal_rated_capacity, |
| 139 | + ) |
| 140 | + self.assertEqual(actual_coeffs, expected_coeffs) |
| 141 | + |
| 142 | + def test_get_marginal_cost_coeff_step_1(self): |
| 143 | + step_k = 1 |
| 144 | + nondispatch_contracts = {"hydro_1": "hydro_A", "solar_1": "solar_B"} |
| 145 | + # contract_costs: (contract_name, time_index) -> cost |
| 146 | + contract_costs = { |
| 147 | + ("hydro_A", 0): 5, |
| 148 | + ("hydro_A", 1): 5.5, |
| 149 | + ("hydro_A", 2): 5.2, |
| 150 | + ("hydro_A", 24): 6, |
| 151 | + ("solar_B", 0): 2, |
| 152 | + ("solar_B", 1): 2.1, |
| 153 | + ("solar_B", 2): 2.0, |
| 154 | + ("solar_B", 24): 2.5, |
| 155 | + } |
| 156 | + expected_coeffs = { |
| 157 | + # (unit, t): contract_costs[(nondispatch_contracts[unit], t + (step_k - 1) * 24)] |
| 158 | + ("hydro_1", 0): contract_costs[("hydro_A", 0 + (1 - 1) * 24)], # 5 |
| 159 | + ("hydro_1", 1): contract_costs[("hydro_A", 1 + (1 - 1) * 24)], # 5.5 |
| 160 | + ("hydro_1", 2): contract_costs[("hydro_A", 2 + (1 - 1) * 24)], # 5.2 |
| 161 | + ("solar_1", 0): contract_costs[("solar_B", 0 + (1 - 1) * 24)], # 2 |
| 162 | + ("solar_1", 1): contract_costs[("solar_B", 1 + (1 - 1) * 24)], # 2.1 |
| 163 | + ("solar_1", 2): contract_costs[("solar_B", 2 + (1 - 1) * 24)], # 2.0 |
| 164 | + } |
| 165 | + actual_coeffs = objfunc.get_marginal_cost_coeff( |
| 166 | + step_k=step_k, |
| 167 | + timesteps=self.timesteps, |
| 168 | + units=self.nondispatch_units, |
| 169 | + nondispatch_contracts=nondispatch_contracts, |
| 170 | + contract_costs=contract_costs, |
| 171 | + ) |
| 172 | + self.assertEqual(actual_coeffs, expected_coeffs) |
| 173 | + |
| 174 | + def test_get_marginal_cost_coeff_step_2(self): |
| 175 | + step_k = 2 # Test with a different step_k |
| 176 | + nondispatch_contracts = {"hydro_1": "hydro_A", "solar_1": "solar_B"} |
| 177 | + contract_costs = { |
| 178 | + # Need costs for t + (2-1)*24 => t + 24 |
| 179 | + ("hydro_A", 24): 6, |
| 180 | + ("hydro_A", 25): 6.5, |
| 181 | + ("hydro_A", 26): 6.2, |
| 182 | + ("solar_B", 24): 3, |
| 183 | + ("solar_B", 25): 3.1, |
| 184 | + ("solar_B", 26): 3.0, |
| 185 | + } |
| 186 | + expected_coeffs = { |
| 187 | + ("hydro_1", 0): contract_costs[("hydro_A", 0 + (2 - 1) * 24)], # 6 |
| 188 | + ("hydro_1", 1): contract_costs[("hydro_A", 1 + (2 - 1) * 24)], # 6.5 |
| 189 | + ("hydro_1", 2): contract_costs[("hydro_A", 2 + (2 - 1) * 24)], # 6.2 |
| 190 | + ("solar_1", 0): contract_costs[("solar_B", 0 + (2 - 1) * 24)], # 3 |
| 191 | + ("solar_1", 1): contract_costs[("solar_B", 1 + (2 - 1) * 24)], # 3.1 |
| 192 | + ("solar_1", 2): contract_costs[("solar_B", 2 + (2 - 1) * 24)], # 3.0 |
| 193 | + } |
| 194 | + actual_coeffs = objfunc.get_marginal_cost_coeff( |
| 195 | + step_k=step_k, |
| 196 | + timesteps=self.timesteps, |
| 197 | + units=self.nondispatch_units, |
| 198 | + nondispatch_contracts=nondispatch_contracts, |
| 199 | + contract_costs=contract_costs, |
| 200 | + ) |
| 201 | + self.assertEqual(actual_coeffs, expected_coeffs) |
| 202 | + |
| 203 | + |
| 204 | +if __name__ == "__main__": |
| 205 | + unittest.main() |
0 commit comments