Source code for oggm.shop.cook23

import logging
import os
import warnings

import numpy as np
import pandas as pd
import xarray as xr
import shapely.geometry as shpg

try:
    import salem
except ImportError:
    pass

try:
    import geopandas as gpd
except ImportError:
    pass

from oggm import utils, cfg
from oggm.exceptions import InvalidWorkflowError

# Module logger
log = logging.getLogger(__name__)

default_base_url = 'https://cluster.klima.uni-bremen.de/~oggm/ice_thickness/cook23/'

_lookup_thickness = None


def _get_lookup_thickness():
    global _lookup_thickness
    if _lookup_thickness is None:
        fname = default_base_url + 'cook23_thickness_lookup_shp_20240815.zip'
        _lookup_thickness = gpd.read_file('zip://' + utils.file_downloader(fname))
    return _lookup_thickness


[docs] @utils.entity_task(log, writes=['gridded_data']) def cook23_to_gdir(gdir, vars=['thk', 'divflux']): """Add the Cook 23 thickness data (and others if wanted) to this glacier directory. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` the glacier directory to process vars : list of str the list of variables to add to gdir. Must be available in the netcdf files. """ # Very easy case: no cook file for this glacier if gdir.rgi_region != '11' and gdir.rgi_subregion != '01': raise InvalidWorkflowError(f'Cook23 data is only for the Alps: ' f'{gdir.rgi_id}') # Find out which file(s) we need gdf = _get_lookup_thickness() cp = shpg.Point(gdir.cenlon, gdir.cenlat) sel = gdf.loc[gdf.contains(cp)] if len(sel) == 0: raise InvalidWorkflowError(f'There seems to be no Cook23 file for this ' f'glacier: {gdir.rgi_id}') if len(sel) > 1: # We have multiple files for this glacier other = gdir.read_shapefile('outlines').to_crs(gdf.crs) sel = gdf.loc[gdf.contains_properly(other)] if len(sel) != 1: raise InvalidWorkflowError(f'Weird double file situation for ' f'glacier: {gdir.rgi_id}') sel = sel.iloc[0] url = default_base_url + sel['thickness'] input_file = utils.file_downloader(url) # Read and reproject with xr.open_dataset(input_file) as ds: for var in vars: if var not in ds: raise InvalidWorkflowError(f'Variable {var} not found in the ' f'Cook file for glacier {gdir.rgi_id}') ds[var].attrs['pyproj_srs'] = 'EPSG:32632' ds.attrs['pyproj_srs'] = 'EPSG:32632' ds = ds[vars].load() # Reproject and write with utils.ncDataset(gdir.get_filepath('gridded_data'), 'a') as nc: for var in vars: # Reproject data = gdir.grid.map_gridded_data(ds[var].data, ds.salem.grid, interp='linear') # Write vn = 'cook23_' + var if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', ), zlib=True, fill_value=np.nan) if var == 'thk': v.units = 'm' v.long_name = 'Ice thickness from Cook et al. 2023' elif var == 'divflux': v.units = 'm yr-1' v.long_name = 'Divergence of ice flux from Cook et al. 2023' v.data_source = url v[:] = data.astype(np.float32)
@utils.entity_task(log) def cook23_statistics(gdir): """Gather statistics about the Cook23 data interpolated to this glacier. """ d = dict() # Easy stats - this should always be possible d['rgi_id'] = gdir.rgi_id d['rgi_region'] = gdir.rgi_region d['rgi_subregion'] = gdir.rgi_subregion d['rgi_area_km2'] = gdir.rgi_area_km2 d['cook23_vol_km3'] = 0 d['cook23_area_km2'] = 0 d['cook23_perc_cov'] = 0 try: with xr.open_dataset(gdir.get_filepath('gridded_data')) as ds: thick = ds['cook23_thk'].where(ds['glacier_mask'], np.nan).load() gridded_area = ds['glacier_mask'].sum() * gdir.grid.dx ** 2 * 1e-6 with warnings.catch_warnings(): # For operational runs we ignore the warnings warnings.filterwarnings('ignore', category=RuntimeWarning) d['cook23_vol_km3'] = float(thick.sum() * gdir.grid.dx ** 2 * 1e-9) d['cook23_area_km2'] = float((~thick.isnull()).sum() * gdir.grid.dx ** 2 * 1e-6) d['cook23_perc_cov'] = float(d['cook23_area_km2'] / gridded_area) except (FileNotFoundError, AttributeError, KeyError): pass return d
[docs] @utils.global_task(log) def compile_cook23_statistics(gdirs, filesuffix='', path=True): """Gather as much statistics as possible about a list of glaciers. It can be used to do result diagnostics and other stuffs. If the data necessary for a statistic is not available (e.g.: flowlines length) it will simply be ignored. Parameters ---------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the glacier directories to process filesuffix : str add suffix to output file path : str, bool Set to "True" in order to store the info in the working directory Set to a path to store the file to your chosen location """ from oggm.workflow import execute_entity_task out_df = execute_entity_task(cook23_statistics, gdirs) out = pd.DataFrame(out_df).set_index('rgi_id') if path: if path is True: out.to_csv(os.path.join(cfg.PATHS['working_dir'], ('cook23_statistics' + filesuffix + '.csv'))) else: out.to_csv(path) return out