|
43 | 43 | from prediction import wrapped_run_prediction_single |
44 | 44 | from utils import calc_percent_limit, remove_intersecting_windows, find_charge_rate, dp2, dp4 |
45 | 45 | from futurerate import FutureRate |
46 | | -from config import MINUTE_WATT, INVERTER_MAX_RETRY_REST |
| 46 | +from config import MINUTE_WATT, INVERTER_MAX_RETRY_REST, PREDICT_STEP |
47 | 47 | from inverter import Inverter |
48 | 48 | from compare import Compare |
49 | 49 | from gecloud import GECloudDirect |
@@ -9134,6 +9134,163 @@ def run_test_units(my_predbat): |
9134 | 9134 | return failed |
9135 | 9135 |
|
9136 | 9136 |
|
| 9137 | +def add_incrementing_sensor_total(data): |
| 9138 | + max_entry = max(data.keys()) if data else 0 |
| 9139 | + total = 0 |
| 9140 | + for minute in range(PREDICT_STEP, max_entry, PREDICT_STEP): |
| 9141 | + increment = max(data.get(minute, 0) - data.get(minute + PREDICT_STEP, 0), 0) |
| 9142 | + total += increment |
| 9143 | + return total |
| 9144 | + |
| 9145 | + |
| 9146 | +def test_previous_days_modal_filter(my_predbat): |
| 9147 | + """ |
| 9148 | + Test the previous_days_modal_filter function |
| 9149 | + """ |
| 9150 | + print("**** Running previous_days_modal_filter tests ****") |
| 9151 | + failed = False |
| 9152 | + |
| 9153 | + # Set up test environment |
| 9154 | + my_predbat.load_minutes_age = 7 # 7 days of data |
| 9155 | + my_predbat.days_previous = [1, 2] # Test with 3 days |
| 9156 | + my_predbat.days_previous_weight = [1.0, 1.0] # Equal weighting |
| 9157 | + number_of_days = 2 |
| 9158 | + my_predbat.load_filter_modal = True # Enable modal filtering |
| 9159 | + my_predbat.car_charging_hold = False |
| 9160 | + my_predbat.car_charging_energy = None |
| 9161 | + my_predbat.iboost_energy_subtract = False |
| 9162 | + my_predbat.iboost_energy_today = None |
| 9163 | + my_predbat.base_load = 0.0 |
| 9164 | + |
| 9165 | + # Mock the get_arg method |
| 9166 | + original_get_arg = my_predbat.get_arg |
| 9167 | + |
| 9168 | + def mock_get_arg(key, default=None): |
| 9169 | + if key == "load_filter_threshold": |
| 9170 | + return 30 |
| 9171 | + return original_get_arg(key, default) |
| 9172 | + |
| 9173 | + my_predbat.get_arg = mock_get_arg |
| 9174 | + |
| 9175 | + # Test 1: Empty data set - should be filled with gap filling logic |
| 9176 | + print("Test 1: Empty data set") |
| 9177 | + test_data = {} |
| 9178 | + |
| 9179 | + # Call the function |
| 9180 | + my_predbat.previous_days_modal_filter(test_data) |
| 9181 | + data_length = len(test_data) |
| 9182 | + data_length_days = data_length / (24 * 60) |
| 9183 | + print("Data length after processing: {} minutes ({} days)".format(data_length, data_length_days)) |
| 9184 | + |
| 9185 | + # After gap filling, data should have been populated for the empty gaps |
| 9186 | + # Check that data has been filled for at least some minutes |
| 9187 | + total_filled_data = dp2(add_incrementing_sensor_total(test_data)) |
| 9188 | + expected_total_per_day = 24.0 # 24 kWh per day as default when no data |
| 9189 | + |
| 9190 | + print("Total filled data: {} kWh".format(dp2(total_filled_data))) |
| 9191 | + |
| 9192 | + # With 2 days and complete gaps, should use 24kWh default for each day |
| 9193 | + if total_filled_data != expected_total_per_day * number_of_days: |
| 9194 | + print("ERROR: Expected gap filling to add approximately {} kWh, got {} kWh".format(expected_total_per_day * number_of_days, total_filled_data)) |
| 9195 | + for minute in range(0, data_length, 30): |
| 9196 | + print(" Minute {}: {}".format(minute, test_data.get(minute, 0))) |
| 9197 | + failed = True |
| 9198 | + |
| 9199 | + # Test 2: Data with gaps - should be filled to create 1 kWh per hour pattern |
| 9200 | + print("Test 2: Data with some gaps") |
| 9201 | + test_data = {} |
| 9202 | + |
| 9203 | + # Create partial data for 1 day (1 kWh per hour = incrementing total) |
| 9204 | + # PREDICT_STEP is 5 minutes, so 12 steps per hour |
| 9205 | + # Each step should increment by 1/12 kWh to get 1 kWh per hour |
| 9206 | + step_increment = 1.0 / 60 |
| 9207 | + running_total = 0 |
| 9208 | + |
| 9209 | + # Fill first half of day with proper incremental data |
| 9210 | + for minute in range(0, 12 * 60): # 12 hours worth |
| 9211 | + running_total += step_increment |
| 9212 | + test_data[24 * 60 - minute] = running_total # Backwards indexing as used in function |
| 9213 | + |
| 9214 | + # Leave second half empty to test gap filling |
| 9215 | + |
| 9216 | + # Set up days_previous for this test |
| 9217 | + my_predbat.days_previous = [1] # Only test with 1 day |
| 9218 | + my_predbat.days_previous_weight = [1.0] |
| 9219 | + |
| 9220 | + initial_data_sum = dp2(add_incrementing_sensor_total(test_data)) |
| 9221 | + print("Initial partial data sum: {} kWh".format(dp2(initial_data_sum))) |
| 9222 | + |
| 9223 | + # Call the function |
| 9224 | + my_predbat.previous_days_modal_filter(test_data) |
| 9225 | + |
| 9226 | + final_data_sum = dp2(add_incrementing_sensor_total(test_data)) |
| 9227 | + print("Final data sum after gap filling: {} kWh".format(dp2(final_data_sum))) |
| 9228 | + |
| 9229 | + # Should now have approximately 24 kWh total (1 kWh per hour for 24 hours) |
| 9230 | + expected_final_total = 24.0 |
| 9231 | + if abs(final_data_sum - expected_final_total) > 2.0: # Allow 2 kWh tolerance |
| 9232 | + print("ERROR: Expected final total around {} kWh, got {} kWh".format(expected_final_total, final_data_sum)) |
| 9233 | + failed = True |
| 9234 | + else: |
| 9235 | + print("Gap filling successful: filled from {} kWh to {} kWh".format(dp2(initial_data_sum), dp2(final_data_sum))) |
| 9236 | + |
| 9237 | + # Test 3: Modal filtering - remove lowest consumption day |
| 9238 | + print("Test 3: Modal filtering removes lowest day") |
| 9239 | + |
| 9240 | + # Reset for modal filter test |
| 9241 | + my_predbat.days_previous = [1, 2, 3] |
| 9242 | + my_predbat.days_previous_weight = [1.0, 1.0, 1.0] |
| 9243 | + original_days_count = len(my_predbat.days_previous) |
| 9244 | + |
| 9245 | + # Create test data with different consumption per day |
| 9246 | + test_data = {} |
| 9247 | + |
| 9248 | + # Day 1: Low consumption (10 kWh total) |
| 9249 | + day1_total = 10.0 |
| 9250 | + step_increment_day1 = day1_total / (24 * 60) |
| 9251 | + running_total = 0 |
| 9252 | + for minute in range(0, 24 * 60): |
| 9253 | + running_total += step_increment_day1 |
| 9254 | + test_data[24 * 60 - minute] = running_total |
| 9255 | + |
| 9256 | + # Day 2: Medium consumption (20 kWh total) |
| 9257 | + day2_total = 20.0 |
| 9258 | + step_increment_day2 = day2_total / (24 * 60) |
| 9259 | + running_total = 0 |
| 9260 | + for minute in range(0, 24 * 60): |
| 9261 | + running_total += step_increment_day2 |
| 9262 | + test_data[2 * 24 * 60 - minute] = running_total |
| 9263 | + |
| 9264 | + # Day 3: High consumption (30 kWh total) |
| 9265 | + day3_total = 30.0 |
| 9266 | + step_increment_day3 = day3_total / (24 * 60) |
| 9267 | + running_total = 0 |
| 9268 | + for minute in range(0, 24 * 60): |
| 9269 | + running_total += step_increment_day3 |
| 9270 | + test_data[3 * 24 * 60 - minute] = running_total |
| 9271 | + |
| 9272 | + print("Created test data with day totals: {} kWh, {} kWh, {} kWh".format(day1_total, day2_total, day3_total)) |
| 9273 | + |
| 9274 | + # Call the function - should remove day 1 (lowest consumption) |
| 9275 | + my_predbat.previous_days_modal_filter(test_data) |
| 9276 | + |
| 9277 | + # Check that one day was removed |
| 9278 | + final_days_count = len(my_predbat.days_previous) |
| 9279 | + if final_days_count != original_days_count - 1: |
| 9280 | + print("ERROR: Expected modal filter to remove 1 day, had {} days, now have {} days".format(original_days_count, final_days_count)) |
| 9281 | + failed = True |
| 9282 | + elif 1 in my_predbat.days_previous: |
| 9283 | + print("ERROR: Expected modal filter to remove day 1 (lowest consumption), but it's still present") |
| 9284 | + failed = True |
| 9285 | + else: |
| 9286 | + print("Modal filter correctly removed lowest consumption day") |
| 9287 | + |
| 9288 | + # Restore original get_arg method |
| 9289 | + my_predbat.get_arg = original_get_arg |
| 9290 | + |
| 9291 | + return failed |
| 9292 | + |
| 9293 | + |
9137 | 9294 | def test_download_octopus_url_wrapper(my_predbat): |
9138 | 9295 | """ |
9139 | 9296 | Wrapper to run the async test function |
@@ -9223,11 +9380,12 @@ def main(): |
9223 | 9380 | if args.perf_only: |
9224 | 9381 | return failed |
9225 | 9382 |
|
| 9383 | + if not failed: |
| 9384 | + failed |= test_previous_days_modal_filter(my_predbat) |
9226 | 9385 | if not failed: |
9227 | 9386 | failed |= test_download_octopus_url_wrapper(my_predbat) |
9228 | 9387 | if not failed: |
9229 | 9388 | failed |= test_plugin_startup_order(my_predbat) |
9230 | | - |
9231 | 9389 | if not failed: |
9232 | 9390 | failed |= test_minute_data(my_predbat) |
9233 | 9391 | if not failed: |
|
0 commit comments