Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ docs/_images
docs/.doctrees
oggm/version.py
oggm/ignore
ignore/
7 changes: 7 additions & 0 deletions docs/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Enhancements
``workflow.merge_gridded_data``. If no grid is provided, the default is to
merge all grids of the provided gdirs (:pull:`1779`).
By `Patrick Schmitt <https://github.com/pat-schmitt>`_
- `min_area_h` is now a default diagnostic variable output: this is
glacier area trying to avoid spikes due to interannual variability
in snowfall (:pull:`1791`).
By `Fabien Maussion <https://github.com/fmaussion>`_ and
`Patrick Schmitt <https://github.com/pat-schmitt>`_.
- Flowlines shapefiles output now have more attributes and are easier to
use (:pull:`1786`).
By `Fabien Maussion <https://github.com/fmaussion>`_
Expand All @@ -62,6 +67,7 @@ Enhancements
``flux_divergence`` (:pull:`1792`).
By `Patrick Schmitt <https://github.com/pat-schmitt>`_


Bug fixes
~~~~~~~~~

Expand All @@ -72,6 +78,7 @@ Bug fixes
By `Dan Goldberg <https://github.com/dngoldberg>`_ and
`Fabien Maussion <https://github.com/fmaussion>`_.


v1.6.2 (August 25, 2024)
------------------------

Expand Down
34 changes: 33 additions & 1 deletion oggm/cli/prepro_levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import time
import logging
import json
import importlib
import pandas as pd
import numpy as np
import geopandas as gpd
Expand Down Expand Up @@ -86,6 +87,8 @@ def run_prepro_levels(rgi_version=None, rgi_reg=None, border=None,
add_millan_thickness=False, add_millan_velocity=False,
add_hugonnet_dhdt=False, add_bedmachine=False,
add_glathida=False,
custom_climate_task=None,
custom_climate_task_kwargs=None,
start_level=None, start_base_url=None, max_level=5,
logging_level='WORKFLOW',
dynamic_spinup=False, err_dmdtda_scaling_factor=0.2,
Expand Down Expand Up @@ -169,6 +172,12 @@ def run_prepro_levels(rgi_version=None, rgi_reg=None, border=None,
add_glathida : bool
adds (reprojects) the glathida thickness data to the glacier
directories. Data points are stored as csv files.
custom_climate_task : str
optional import path to a custom climate task in the form
"module_path:function_name". If provided, it will be called instead of
the default process_climate_data.
custom_climate_task_kwargs : dict
optional kwargs passed to the custom climate task when it is executed.
start_level : int
the pre-processed level to start from (default is to start from
scratch). If set, you'll need to indicate start_base_url as well.
Expand Down Expand Up @@ -629,7 +638,23 @@ def _time_log():
utils.mkdir(sum_dir)

# Climate
workflow.execute_entity_task(tasks.process_climate_data, gdirs)
climate_kwargs = custom_climate_task_kwargs or {}
if custom_climate_task:
try:
mod_path, func_name = custom_climate_task.rsplit(':', 1)
except ValueError:
raise InvalidParamsError('custom_climate_task must be of the form "module:function"')
try:
mod = importlib.import_module(mod_path)
except ModuleNotFoundError as err:
raise InvalidParamsError(f'Cannot import module {mod_path}') from err
try:
custom_task_func = getattr(mod, func_name)
except AttributeError as err:
raise InvalidParamsError(f'Module {mod_path} has no attribute {func_name}') from err
workflow.execute_entity_task(custom_task_func, gdirs, **climate_kwargs)
else:
workflow.execute_entity_task(tasks.process_climate_data, gdirs)

# Small optim to avoid concurrency
utils.get_geodetic_mb_dataframe()
Expand Down Expand Up @@ -946,6 +971,11 @@ def parse_args(args):
help='adds (reprojects) the glathida point thickness '
'observations to the glacier directories. '
'The data points are stored as csv.')
parser.add_argument('--custom-climate-task', type=str, default=None,
help='Custom climate task import path in the form module:function. '
'If provided, it replaces the default process_climate_data.')
parser.add_argument('--custom-climate-task-kwargs', type=json.loads, default=None,
help='JSON dict of kwargs passed to the custom climate task.')
parser.add_argument('--demo', nargs='?', const=True, default=False,
help='if you want to run the prepro for the '
'list of demo glaciers.')
Expand Down Expand Up @@ -1036,6 +1066,8 @@ def parse_args(args):
add_hugonnet_dhdt=args.add_hugonnet_dhdt,
add_bedmachine=args.add_bedmachine,
add_glathida=args.add_glathida,
custom_climate_task=args.custom_climate_task,
custom_climate_task_kwargs=args.custom_climate_task_kwargs,
dynamic_spinup=dynamic_spinup,
err_dmdtda_scaling_factor=args.err_dmdtda_scaling_factor,
dynamic_spinup_start_year=args.dynamic_spinup_start_year,
Expand Down
14 changes: 7 additions & 7 deletions oggm/core/dynamic_spinup.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def run_dynamic_spinup(gdir, init_model_filesuffix=None, init_model_yr=None,
the forward model run. Therefore you could see quite fast changes
(spikes) in the time-evolution (especially visible in length and area).
If you set this value to 0 the filtering can be switched off.
Default is cfg.PARAMS['dynamic_spinup_min_ice_thick'].
Default is cfg.PARAMS['min_ice_thick_for_area'].
first_guess_t_spinup : float
The initial guess for the temperature bias for the spinup
MassBalanceModel in °C.
Expand Down Expand Up @@ -231,7 +231,7 @@ def run_dynamic_spinup(gdir, init_model_filesuffix=None, init_model_yr=None,
yr_min = gdir.get_climate_info()['baseline_yr_0']

if min_ice_thickness is None:
min_ice_thickness = cfg.PARAMS['dynamic_spinup_min_ice_thick']
min_ice_thickness = cfg.PARAMS['min_ice_thick_for_area']

# check provided maximum start year here, and change min_spinup_period
if spinup_start_yr_max is not None:
Expand Down Expand Up @@ -443,7 +443,7 @@ def run_model_with_spinup_to_target_year(t_spinup):
geom_path=geom_path,
diag_path=diag_path,
fl_diag_path=fl_diag_path,
dynamic_spinup_min_ice_thick=min_ice_thickness,
min_ice_thick_for_area=min_ice_thickness,
fixed_geometry_spinup_yr=fixed_geometry_spinup_yr,
store_monthly_step=store_monthly_step,
)
Expand All @@ -453,9 +453,9 @@ def run_model_with_spinup_to_target_year(t_spinup):
if delete_area_min_h:
ovars.remove('area_min_h')

if type(ds) == tuple:
if isinstance(ds, tuple):
ds = ds[0]
model_area_km2 = ds.area_m2_min_h.loc[target_yr].values * 1e-6
model_area_km2 = ds.area_min_h_m2.loc[target_yr].values * 1e-6
model_volume_km3 = ds.volume_m3.loc[target_yr].values * 1e-9
else:
# only run to rgi date and extract values
Expand Down Expand Up @@ -1047,7 +1047,7 @@ def dynamic_melt_f_run_with_dynamic_spinup(
the forward model run. Therefore you could see quite fast changes
(spikes) in the time-evolution (especially visible in length and area).
If you set this value to 0 the filtering can be switched off.
Default is cfg.PARAMS['dynamic_spinup_min_ice_thick'].
Default is cfg.PARAMS['min_ice_thick_for_area'].
first_guess_t_spinup : float
The initial guess for the temperature bias for the spinup
MassBalanceModel in °C.
Expand Down Expand Up @@ -1332,7 +1332,7 @@ def dynamic_melt_f_run_with_dynamic_spinup_fallback(
the forward model run. Therefore you could see quite fast changes
(spikes) in the time-evolution (especially visible in length and area).
If you set this value to 0 the filtering can be switched off.
Default is cfg.PARAMS['dynamic_spinup_min_ice_thick'].
Default is cfg.PARAMS['min_ice_thick_for_area'].
first_guess_t_spinup : float
The initial guess for the temperature bias for the spinup
MassBalanceModel in °C.
Expand Down
26 changes: 13 additions & 13 deletions oggm/core/flowline.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ def run_until_and_store(self, y1,
store_monthly_step=None,
stop_criterion=None,
fixed_geometry_spinup_yr=None,
dynamic_spinup_min_ice_thick=None,
min_ice_thick_for_area=None,
):
"""Runs the model and returns intermediate steps in xarray datasets.

Expand Down Expand Up @@ -978,13 +978,13 @@ def run_until_and_store(self, y1,
starting from the chosen year. The only output affected are the
glacier wide diagnostic files - all other outputs are set
to constants during "spinup"
dynamic_spinup_min_ice_thick : float or None
min_ice_thick_for_area : float or None
if set to a float, additional variables are saved which are useful
in combination with the dynamic spinup. In particular only grid
points with a minimum ice thickness are considered for the total
area or the total volume. This is useful to smooth out yearly
fluctuations when matching to observations. The names of this new
variables include the suffix _min_h (e.g. 'area_m2_min_h')
variables include the suffix _min_h (e.g. 'area_min_h_m2')

Returns
-------
Expand Down Expand Up @@ -1106,16 +1106,16 @@ def run_until_and_store(self, y1,
diag_ds['area_m2'].attrs['description'] = 'Total glacier area'
diag_ds['area_m2'].attrs['unit'] = 'm 2'

if dynamic_spinup_min_ice_thick is None:
dynamic_spinup_min_ice_thick = cfg.PARAMS['dynamic_spinup_min_ice_thick']
if min_ice_thick_for_area is None:
min_ice_thick_for_area = cfg.PARAMS['min_ice_thick_for_area']

if 'area_min_h' in ovars:
# filled with a value if dynamic_spinup_min_ice_thick is not None
diag_ds['area_m2_min_h'] = ('time', np.zeros(nm) * np.nan)
diag_ds['area_m2_min_h'].attrs['description'] = \
f'Total glacier area of gridpoints with a minimum ice' \
f'thickness of {dynamic_spinup_min_ice_thick} m'
diag_ds['area_m2_min_h'].attrs['unit'] = 'm 2'
# filled with a value if min_ice_thick_for_area is not None
diag_ds['area_min_h_m2'] = ('time', np.zeros(nm) * np.nan)
diag_ds['area_min_h_m2'].attrs['description'] = \
f'Total glacier area of gridpoints with a minimum ice ' \
f'thickness of {min_ice_thick_for_area} m'
diag_ds['area_min_h_m2'].attrs['unit'] = 'm 2'

if 'length' in ovars:
diag_ds['length_m'] = ('time', np.zeros(nm) * np.nan)
Expand Down Expand Up @@ -1381,8 +1381,8 @@ def run_until_and_store(self, y1,
if 'volume_bwl' in ovars:
diag_ds['volume_bwl_m3'].data[i] = self.volume_bwl_m3
if 'area_min_h' in ovars:
diag_ds['area_m2_min_h'].data[i] = np.sum([np.sum(
fl.bin_area_m2[fl.thick > dynamic_spinup_min_ice_thick])
diag_ds['area_min_h_m2'].data[i] = np.sum([np.sum(
fl.bin_area_m2[fl.thick > min_ice_thick_for_area])
for fl in self.fls])
# Terminus thick is a bit more logic
ti = None
Expand Down
12 changes: 8 additions & 4 deletions oggm/core/massbalance.py
Original file line number Diff line number Diff line change
Expand Up @@ -1630,10 +1630,14 @@ def mb_calibration_from_geodetic_mb(gdir, *,
temp_bias = 0
if cfg.PARAMS['use_temp_bias_from_file']:
climinfo = gdir.get_climate_info()
if 'w5e5' not in climinfo['baseline_climate_source'].lower():
raise InvalidWorkflowError('use_temp_bias_from_file currently '
'only available for W5E5 data.')
bias_df = get_temp_bias_dataframe()
# if 'w5e5' not in climinfo['baseline_climate_source'].lower():
# raise InvalidWorkflowError('use_temp_bias_from_file currently '
# 'only available for W5E5 data.')
if 'w5e5' in climinfo['baseline_climate_source']:
bias_df = get_temp_bias_dataframe()
elif 'lmr_mira' in climinfo['baseline_climate_source']:
bias_df = get_temp_bias_dataframe(dataset='lmr_mira')

ref_lon = climinfo['baseline_climate_ref_pix_lon']
ref_lat = climinfo['baseline_climate_ref_pix_lat']
# Take nearest
Expand Down
9 changes: 5 additions & 4 deletions oggm/params.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ error_when_glacier_reaches_boundaries = True
# some options to the user.
# This option sets an arbitrary limit on how thick (m) a glacier should be
# to be defined as "glacier" (https://github.com/OGGM/oggm/issues/914)
min_ice_thick_for_length = 0
min_ice_thick_for_length = 2
# How to calculate the length of a glacier?
# - 'naive' (the default) computes the length by summing the number of
# grid points with an ice thickness above min_ice_thick_for_length
Expand All @@ -361,8 +361,9 @@ use_inversion_params_for_run = True
# Defines the minimum ice thickness which is used during the dynamic spinup to
# match area or volume. Only grid points with a larger thickness are considered
# to the total area. This is needed to filter out area changes due to climate
# variability around the rgi year (spikes).
dynamic_spinup_min_ice_thick = 2.
# variability (spikes). The resulting diagnostic variable is stored in
# in 'area_min_h_m2' - the recommended area variable for most applications.
min_ice_thick_for_area = 2.

### Tidewater glaciers options

Expand Down Expand Up @@ -430,7 +431,7 @@ store_model_geometry = False
# melt_residual_off_glacier, melt_residual_on_glacier
# model_mb, residual_mb, snow_bucket,
# You need to keep all variables in one line unfortunately
store_diagnostic_variables = volume, volume_bsl, volume_bwl, area, length, calving, calving_rate, off_area, on_area, melt_off_glacier, melt_on_glacier, liq_prcp_off_glacier, liq_prcp_on_glacier, snowfall_off_glacier, snowfall_on_glacier
store_diagnostic_variables = volume, volume_bsl, volume_bwl, area, area_min_h, length, calving, calving_rate, off_area, on_area, melt_off_glacier, melt_on_glacier, liq_prcp_off_glacier, liq_prcp_on_glacier, snowfall_off_glacier, snowfall_on_glacier

# Whether to store the model flowline diagnostic files during operational runs
# This can be useful for advanced diagnostics along the flowlines but is
Expand Down
Loading
Loading