From e8753c6ee8bb6b33c9a21059a6c4d5150f9fbe4e Mon Sep 17 00:00:00 2001 From: Patrick Schmitt <50843444+pat-schmitt@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:03:10 +0100 Subject: [PATCH] adapted for daily melt_f in dynamic calibration (#4) --- oggm/core/dynamic_spinup.py | 23 ++++++++++++++++------- oggm/tests/test_models.py | 3 ++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/oggm/core/dynamic_spinup.py b/oggm/core/dynamic_spinup.py index 4357cb78d..386209af4 100644 --- a/oggm/core/dynamic_spinup.py +++ b/oggm/core/dynamic_spinup.py @@ -1597,7 +1597,7 @@ def dynamic_melt_f_run_fallback( def run_dynamic_melt_f_calibration( gdir, ref_dmdtda=None, err_ref_dmdtda=None, err_dmdtda_scaling_factor=1, ref_period='', melt_f_min=None, - melt_f_max=None, melt_f_max_step_length=5, maxiter=20, + melt_f_max=None, melt_f_max_step_length_minimum=0.1, maxiter=20, ignore_errors=False, output_filesuffix='_dynamic_melt_f', ys=None, ye=None, run_function=dynamic_melt_f_run_with_dynamic_spinup, @@ -1659,10 +1659,11 @@ def run_dynamic_melt_f_calibration( melt_f_max : float or None Upper absolute limit for melt_f. Default is None (-> cfg.PARAMS['melt_f_max']) - melt_f_max_step_length : float - Defines the maximum allowed change of melt_f between two iterations. - Is needed to avoid to large changes. - Default is 5 + melt_f_max_step_length_minimum : float + Defines a minimum maximal change of melt_f between two iterations. The + maximum step length is needed to avoid too large steps, which likely + lead to an error. + Default is 0.1 maxiter : int Maximum number of minimisation iterations of minimising mismatch to dmdtda by changing melt_f. Each of this iterations conduct a complete @@ -1821,6 +1822,14 @@ def run_dynamic_melt_f_calibration( # (using the fallback function) if an error occurs melt_f_initial = gdir.read_json('mb_calib')['melt_f'] + # define maximum allowed change of melt_f between two iterations. Is needed + # to avoid to large changes (=likely lead to an error). It is defined in a + # way that in maxiter steps the further away limit can be reached + melt_f_max_step_length = np.max( + [np.max(np.abs(np.array([melt_f_min, melt_f_min]) - melt_f_initial)) / + maxiter, + melt_f_max_step_length_minimum]) + # only used to check performance of minimisation dynamic_melt_f_calibration_runs = [0] @@ -2103,12 +2112,12 @@ def get_mismatch(melt_f): # mismatch = 2 * err_ref_dmdtda this corresponds to 100%; for 100% or # 150% the next step is (-1) * melt_f_max_step_length; if mismatch # -40%, next step is 0.4 * melt_f_max_step_length; but always at least - # an absolute change of 0.5 is imposed to prevent too close guesses). + # an absolute change of 0.02 is imposed to prevent too close guesses). # (-1) as if mismatch is negative we need a larger melt_f to get closer # to 0. step = (-1) * np.sign(mismatch[-1]) * \ max((np.abs(mismatch[-1]) - err_ref_dmdtda) / err_ref_dmdtda * - melt_f_max_step_length, 0.5) + melt_f_max_step_length, 0.02) new_mismatch, new_melt_f = get_mismatch(melt_f_guess[0] + step) melt_f_guess.append(new_melt_f) mismatch.append(new_mismatch) diff --git a/oggm/tests/test_models.py b/oggm/tests/test_models.py index 1c0a93853..b4bca27d4 100644 --- a/oggm/tests/test_models.py +++ b/oggm/tests/test_models.py @@ -5050,7 +5050,7 @@ def test_hydro_dynamical_spinup(self, hef_gdir, inversion_params): @pytest.mark.parametrize('do_inversion', [True, False]) @pytest.mark.slow def test_hydro_dynamic_melt_f_with_dynamic_spinup(self, inversion_params, - do_inversion): + do_inversion): # reset kcalving for this test and set it back to the previous value # after the test @@ -5077,6 +5077,7 @@ def test_hydro_dynamic_melt_f_with_dynamic_spinup(self, inversion_params, # Needed for this to run cfg.PARAMS['store_model_geometry'] = True + melt_f_max = 1000 * 12 / 365 tasks.run_with_hydro( gdir, run_task=tasks.run_dynamic_melt_f_calibration, store_monthly_hydro=True, ref_area_from_y0=True,