Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions oggm/core/dynamic_spinup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2626,8 +2626,7 @@ def get_mismatch(melt_f):
# step away from the limits until we are at the initial guess or we
# found an error free run
tmp_mismatch = None
while ((current_min_error | current_max_error | iteration == 0) &
(iteration < max_iterations)):
while tmp_mismatch is None and iteration < max_iterations:
try:
tmp_mismatch = fct_to_minimise(melt_f)
except RuntimeError as e:
Expand Down Expand Up @@ -2686,7 +2685,7 @@ def get_mismatch(melt_f):

iteration += 1

if iteration >= max_iterations:
if tmp_mismatch is None:
# ok we were not able to find an mismatch without error
if current_min_error:
raise RuntimeError('Not able to find new lower limit for '
Expand Down
57 changes: 57 additions & 0 deletions oggm/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
dynamic_melt_f_run_with_dynamic_spinup_fallback,
dynamic_melt_f_run,
dynamic_melt_f_run_fallback,
define_new_melt_f_in_gdir,
_get_spinup_periods_to_run)

FluxBasedModel = partial(FluxBasedModel, inplace=True)
Expand Down Expand Up @@ -7285,6 +7286,62 @@ def reset_melt_f():
run_function=dynamic_melt_f_run,
fallback_function=dynamic_melt_f_run_fallback)

def test_run_dynamic_melt_f_calibration_recovers_from_error_at_limit(
self, hef_gdir):
# if run_function fails with melt_f at a limit, calibration
# should retry with a different limit instead of falling back.
gdir = hef_gdir
melt_f_initial = gdir.settings['melt_f']
ref_mb = -500.
ref_mb_err = 100.

calls = []

def fake_run_function(gdir, melt_f=None, set_local_variables=False,
**kwargs):
if set_local_variables:
return
define_new_melt_f_in_gdir(gdir, melt_f)
calls.append(melt_f)
if len(calls) == 1:
# large negative mismatch pushes the second guess above the
# upper melt_f limit
return None, ref_mb - 10 * ref_mb_err
elif len(calls) == 2:
# fail while melt_f sits at the upper limit
raise RuntimeError('planned error at melt_f limit')
return None, ref_mb

def fake_fallback_function(*args, **kwargs):
raise AssertionError('fallback_function must not be called')

try:
run_dynamic_melt_f_calibration(
gdir, observations_filesuffix='_recovery_test',
ref_mb=ref_mb, ref_mb_err=ref_mb_err,
ref_mb_period='1980-01-01_2000-01-01',
ys=1980, ye=2000,
melt_f_min=1, melt_f_max=1000 * 12 / 365,
melt_f_max_step_length_minimum=1.,
run_function=fake_run_function,
fallback_function=fake_fallback_function,
output_filesuffix='_dyn_melt_f_recovery')

# first guess, failing run at the upper limit, successful retry
assert len(calls) == 3
assert np.isclose(calls[0], melt_f_initial)
# second guess was clipped to the upper limit
# (melt_f_max_step_length is pinned to 1 in this setup)
assert np.isclose(calls[1], melt_f_initial + 1.)
# retry failing threshold in steps of melt_f_max_step_length / 10
assert np.isclose(calls[2],
np.round(melt_f_initial + 1. - 0.1, decimals=1))
assert gdir.settings['used_spinup_option'] == \
'dynamic melt_f calibration (full success)'
finally:
# restore for other tests sharing the fixture
gdir.settings['melt_f'] = melt_f_initial


@pytest.mark.usefixtures('with_class_wd')
@pytest.mark.test_env("models_dynamics")
Expand Down