New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lateral drag / Shape factors #429
Conversation
…nservation inversion. Shape factors are used as given in Adhikari 2012. A new cfg-param "use_shape_factor" is introduced. Needs further tests and eventually adaption in forward model.
Changed one comment/TODO in inversion routine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much! This already looks very good. I have a few minor comments only.
For the rest (and for future reference) it would be super nice to have some plots pasted in this PR:
- curves for various zetas and for both methods
- maybe some application test at HEF?
This we could talk bout it or I'll do it in a subsequent PR
oggm/cfg.py
Outdated
@@ -274,6 +274,7 @@ def initialize(file=None): | |||
PARAMS['invert_with_sliding'] = cp.as_bool('invert_with_sliding') | |||
_k = 'optimize_inversion_params' | |||
PARAMS[_k] = cp.as_bool(_k) | |||
PARAMS['use_shape_factor'] = cp.as_bool('use_shape_factor') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should rename it to use_shape_factor_for_inversion
. It is likely that we will want to separate between inversion and dynamical model's factors.
For testing between the factors it might be useful to have this option as a str: 'Huss'
(or the original paper name), "Adhikari"
or empty for no shape factor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I already also thought about an option like this, but just did not spend more time on it. Will do so.
oggm/core/inversion.py
Outdated
according to Huss and Farinotti (2012) | ||
|
||
Discouraged to use, as this does not make any difference | ||
between parabolic and rectangular beds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure to understand: wouldn't it be better to return a factor of 1 when the bed is rectangular in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could not really find any information on the assumed bed form in the paper. Therefore I did not go into too much detail yet. But you're right, I will adapt it this way.
oggm/core/inversion.py
Outdated
Adhikari (2012) | ||
|
||
TODO: should we expand this here to also include | ||
the factors suggested for sliding? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not yet I would say
oggm/core/inversion.py
Outdated
factors_parabolic = np.array([0.251, 0.448, 0.653, 0.748, | ||
0.803, 0.839, 0.917]) | ||
f_parab = interp1d(tabular_zetas, factors_parabolic, | ||
fill_value='extrapolate') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire block could live at the module scope instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
oggm/core/inversion.py
Outdated
is_rectangular = is_rectangular.astype(bool) | ||
|
||
# TODO: could check for division by 0, but at the moment | ||
# this is covered by interpolation and clip, resulting in a factor of 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just a problem if it throws numerical warnings. If you want you can put this statement in a catch as done here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case is_rectangular is of type integer, these integers (0/1) are directly interpreted as indices, which is not what is intended here. In case of floats I have to admit that I did not check.
oggm/core/inversion.py
Outdated
------- | ||
|
||
""" | ||
a0s_shaped = a0s / (shape_factor ** 3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can safely overwrite a0s
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then I cannot exploit the precomputed a0s in subsequent loops
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, my mistake. Did not realize I was inside the function.
oggm/core/inversion.py
Outdated
@@ -161,6 +273,11 @@ def mass_conservation_inversion(gdir, glen_a=cfg.A, fs=0., write=True, | |||
fd = 2. / (cfg.N+2) * glen_a | |||
a3 = fs / fd | |||
|
|||
# Shape factor params | |||
sf_func = shape_factor_adhikari | |||
sf_tol = 1e-2 # TODO: better as params in cfg? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is too specific it's OK to leave it here
oggm/core/inversion.py
Outdated
# Start iteration for shape factor with guess of 1 | ||
i = 0 | ||
sf = np.ones(slope.shape) | ||
sf_diff = np.ones(slope.shape) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i = 0
and sf_diff
could be defined in the if
block below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely right
oggm/core/inversion.py
Outdated
|
||
shape_factors = np.ones(widths.shape) | ||
|
||
# TODO: higher order interpolation? (e.g. via InterpolatedUnivariateSpline) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe? Depends on how the curve looks like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @alexjarosch for info |
oggm/core/inversion.py
Outdated
|
||
# TODO: how to handle zeta > 10? at the moment extrapolation | ||
# Table 1 from Adhikari (2012) and corresponding interpolation functions | ||
_ADHIKARI_TABLE_ZETAS = np.array([0.5, 1, 2, 3, 4, 5, 10]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to top of file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure there is a pep rule for this, but yes I think it's slightly better
…ion of Nye-shape factor.
…ion of Nye-shape factor. Updated test to match change in shape factor calculation
oggm/params.cfg
Outdated
@@ -189,6 +189,9 @@ optimize_inversion_params = False | |||
# If false, tell OGGM which should be used | |||
inversion_glen_a = 2.4e-24 | |||
inversion_fs = 0. | |||
# Do you want to use shape factors to account for lateral drag? | |||
# Allowed is empty, "Adhikari", "Nye" equal to "Adhikari" or "Huss" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Nye" (equivalent to "Adhikari")
oggm/core/inversion.py
Outdated
@@ -161,6 +287,16 @@ def mass_conservation_inversion(gdir, glen_a=cfg.A, fs=0., write=True, | |||
fd = 2. / (cfg.N+2) * glen_a | |||
a3 = fs / fd | |||
|
|||
# Shape factor params | |||
sf_func = None | |||
if cfg.PARAMS['use_shape_factor_for_inversion'] == 'Adhikari' or \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create a new variable, like use_shape_factor = cfg.PARAMS['use_shape_factor_for_inversion']
and use it throughout the function to shorten the code a little bit
@phigre should we merge this as is and add the tests at a later stage or are you still working on this? |
…shape factor in FluxbasedModel.step Currently, the shape factor methods of the inversion module are used for the calculation of shape factors.
…he shape factors for sections with height = 0 are no longer np.nan but are set to 1. (so shape factor is actually not influencing this section)
Ah, and don't forget to acknowledge your work in |
…riables used for calculation of velocity
oggm/core/flowline.py
Outdated
sf = sf_func(fl.widths_m, thick, fl.is_rectangular) | ||
# TODO: avoid unnecessary new array initialization by adding | ||
# sf_stag to self._stags | ||
sf_stag = np.zeros(thick_stag.shape) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Staggered shape factors need to be computed to match with thick_stag
and u_stag
.
I decided to use shape factors from the unstaggered grid and combine upstream and downstream shape factors to obtain staggered values.
Directly computing shape factors on the staggered grid would make an additional width_stag
necessary and the question arises what is_rectangular_stag
would look like.
oggm/core/flowline.py
Outdated
@@ -21,6 +21,7 @@ | |||
from oggm import entity_task | |||
import oggm.core.massbalance as mbmods | |||
from oggm.core.centerlines import Centerline, line_order | |||
from oggm.core.inversion import shape_factor_adhikari, shape_factor_huss |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I referenced the shape factor functions from the inversion module. IMHO, having them in a separate dedicated module and referencing this module from inversion and flowline would make more sense in regards of code structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, probably. It could be added to utils
for now (and the test moved to test_utils
for now, even if the utils module is definitely overcrowded and could need a split-up one day. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. move to oggm.utils?
Introduced use_sf in flowline and inversion to shorten lines and avoid multiple calls to cfg.PARAMS[use_shape_...']
Using a proxy variable `use_sf` instead of `cfg.PARAMS['use_shape_...']`. This variable is set to None, if the acquired key is not found in PARAMS.
There are still some issues like the storage of Otherwise ready to merge now? |
…s. Moved shape factor function choice to init of class. Added tests with shape factors for inversion and FluxBasedModel doing a roundtrip.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stiil a lot of testing necessary and some easy performance possible, but getting better
d = np.zeros(nx-1) | ||
e = np.zeros(nx) | ||
self._stags.append((a, b, c, d, e)) | ||
d = np.ones(nx+1) # shape factor default is 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could set to 1.0
in case shape factors are not wanted to make the array-array mutliplication an array-scalar mutliplication afterwards. May give some perfomance advantages (especially if numpy could identify multiplication by 1 as skippable)
oggm/core/flowline.py
Outdated
rhogh = (RHO*G*slope_stag*sf_stag)**N | ||
u_stag = (thick_stag**(N+1)) * self._fd * rhogh + \ | ||
rhogh = (RHO*G*slope_stag)**N | ||
u_stag = (thick_stag**(N+1)) * self._fd * rhogh * sf_stag**N + \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shape factor as we use it only makes sense for the non-sliding part
@@ -1377,6 +1377,96 @@ def test_inversion_parabolic(self): | |||
plt.plot(bed_shape_gl[:-3]) | |||
plt.show() | |||
|
|||
def test_inversion_parabolic_sf_adhikari(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests for each case would probably be much better with one single callable function and write wrappers, which are actually called for testing and set the shape factor mode before calling the function.
@@ -1377,6 +1377,96 @@ def test_inversion_parabolic(self): | |||
plt.plot(bed_shape_gl[:-3]) | |||
plt.show() | |||
|
|||
def test_inversion_parabolic_sf_adhikari(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better to define one single function and then write wrappers for different shape factor modes, which are actually called for testing and do nothing else than setting shape factor cfg and call the function doing the work. Would avoid a lot of ugly copied code
d = np.zeros(nx-1) | ||
e = np.zeros(nx) | ||
self._stags.append((a, b, c, d, e)) | ||
d = np.ones(nx+1) # shape factor default is 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set to 1.0
as a scalar if scalararray is faster than arrayarray in numpy. Would be cool, if numpy could skip multiplication by 1 automatically
oggm/utils.py
Outdated
@@ -3161,10 +3161,10 @@ def shape_factor_huss(widths, heights, is_rectangular): | |||
|
|||
# Ensure bool (for masking) | |||
is_rectangular = is_rectangular.astype(bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid typecast necessary? Did not look into the implementation and cannot say anything about perfomance impacts
oggm/utils.py
Outdated
@@ -3161,10 +3161,10 @@ def shape_factor_huss(widths, heights, is_rectangular): | |||
|
|||
# Ensure bool (for masking) | |||
is_rectangular = is_rectangular.astype(bool) | |||
|
|||
shape_factors = np.ones(widths.shape) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
always repeating initialization could be substituted by storing shape factor array in the flowmodel and passing the reference to the shape factor functions
oggm/utils.py
Outdated
shape_factors = np.ones(widths.shape) | ||
shape_factors[~is_rectangular] = \ | ||
(widths / (2 * heights + widths))[~is_rectangular] | ||
shape_factors[heights <= 0.] = 1. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to avoid nans for areas without ice
This is a brilliant piece of work, thanks a lot! I will merge this now, we can always go back to some details later. Experience shows that the global applications will require some tuning, but I'm eager to test this out. Note that this probably should become the default some day. |
whats-new.rst
for all changes (remove if this change is small enough to be undocumented)not yet to a sufficient amount
For first review (@fmaussion)