Summary of Issue
|
increment = 0.00001 * (1 if diffweights < 0 else -1) |
|
for i in range(1000000): |
|
iweights = (rweights * (1 + increment*i)).round(0).astype(int) |
|
if iweights.sum() == numdays: |
|
break |
|
|
|
iweights = iweights.replace(0,np.nan).dropna().astype(int) |
The above lines of code in hourly_repperiods.py are responsible for incrementally adjusting the weights of the selected representative periods until the sum of weights exactly equal the target number of total days in the list of years defined by GSw_HourlyWeatherYears
The code works as intended when using default hourly load and resource profiles, but use of non-default data has shown potential for the incremental adjustment to mathematically "overshoot" the target. Below is the gamslog.txt output of the incremental procedure above from an example run using non-default profile data and GSw_HourlyWeatherYears == {10 years}
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | sumweights: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | diffweights: 3
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | increment: -1e-05
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 0: iweights sum: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 1: iweights sum: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 2: iweights sum: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 3: iweights sum: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 4: iweights sum: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 5: iweights sum: 3653 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 6: iweights sum: 3652 and numdays: 3650
...
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 43: iweights sum: 3651 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 44: iweights sum: 3651 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 45: iweights sum: 3649 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:02:37 | INFO | Iteration 46: iweights sum: 3649 and numdays: 3650
...
hourly_repperiods.py | 2026-05-20 10:05:16 | INFO | Iteration 999997: iweights sum: -32850 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:05:16 | INFO | Iteration 999998: iweights sum: -32850 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:05:16 | INFO | Iteration 999999: iweights sum: -32850 and numdays: 3650
hourly_repperiods.py | 2026-05-20 10:05:16 | INFO | Max increments reached: iweights sum of -32850 != 3650
Note how between Iteration 44 and 45 the procedure ends up overshooting the target, resulting in the loop to continue to its maximum iteration limit and ending with a number far beyond the target.
Proposed Fix
Include procedures that would identify if the iterations "overshoot" the target value and, if so, adjust iteration step size and direction.
increment = 0.00001 * (1 if diffweights < 0 else -1)
+ best_iweights = iweights.copy()
+ best_diff = abs(sumweights - numdays)
+ prev_sum = sumweights.copy()
for i in range(1000000):
iweights = (rweights * (1 + increment*i)).round(0).astype(int)
+ # Track iweights over iteration
+ new_diff = iweights.sum() - numdays
+ if abs(new_diff) < best_diff:
+ best_diff = abs(new_diff)
+ best_iweights = iweights.copy()
if iweights.sum() == numdays:
break
+ # Detect overshoot:
+ if ((prev_sum - numdays) * new_diff < 0):
+ print(f'Incremental adjustment overshot target: change increment to {increment * 0.5} and continue')
+ increment *= 0.5
+ prev_sum = iweights.sum().copy()
iweights = iweights.replace(0,np.nan).dropna().astype(int)
Summary of Issue
ReEDS/reeds/input_processing/hourly_repperiods.py
Lines 160 to 166 in b3bbeef
The above lines of code in hourly_repperiods.py are responsible for incrementally adjusting the weights of the selected representative periods until the sum of weights exactly equal the target number of total days in the list of years defined by
GSw_HourlyWeatherYearsThe code works as intended when using default hourly load and resource profiles, but use of non-default data has shown potential for the incremental adjustment to mathematically "overshoot" the target. Below is the gamslog.txt output of the incremental procedure above from an example run using non-default profile data and
GSw_HourlyWeatherYears == {10 years}Note how between Iteration 44 and 45 the procedure ends up overshooting the target, resulting in the loop to continue to its maximum iteration limit and ending with a number far beyond the target.
Proposed Fix
Include procedures that would identify if the iterations "overshoot" the target value and, if so, adjust iteration step size and direction.