def plot_raster(gdirs, var_name=None, cmap='viridis', ax=None, smap=None): """Plot any raster from the gridded_data file.""" # Files gdir = gdirs[0] with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc: var = nc.variables[var_name] data = var[:] description = var.long_name description += ' [{}]'.format(var.units) smap.set_data(data) smap.set_cmap(cmap) for gdir in gdirs: crs = gdir.grid.center_grid try: geom = gdir.read_pickle('geometries') # Plot boundaries poly_pix = geom['polygon_pix'] smap.set_geometry(poly_pix, crs=crs, fc='none', alpha=0.3, zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) except FileNotFoundError: smap.set_shapefile(gdir.read_shapefile('outlines')) smap.plot(ax) return dict(cbar_label='\n'.join(textwrap.wrap(description, 30)))
def merge_glacier_tasks(gdirs, main_rgi_ids, glcdf=None, filename='climate_monthly', input_filesuffix=''): """Shortcut function: run all tasks to merge tributaries to a main glacier TODO - Automatic search for tributary glaciers TODO - Every tributary should only be used once Parameters ---------- gdirs : list of :py:class:`oggm.GlacierDirectory` all glaciers, main and tributary. Preprocessed and initialised main_rgi_ids: list of str RGI IDs of the main glaciers of interest glcdf: geopandas.GeoDataFrame which contains the main glaciers, will be downloaded if None filename: str Baseline climate file input_filesuffix: str Filesuffix to the climate file Returns ------- merged_gdirs: list of merged GlacierDirectories """ # make sure rgi_ids are iteratable main_rgi_ids = utils.tolist(main_rgi_ids) # split main glaciers from candidates gdirs_main = [gd for gd in gdirs if gd.rgi_id in main_rgi_ids] # find true tributary glaciers tributaries = execute_entity_task( centerlines.intersect_downstream_lines, gdirs_main, candidates=gdirs) # make one dictionary and a list of all gdirs for further preprocessing tribs_dict = {} gdirs_tribs = [] for trb in tributaries: tribs_dict.update(trb) for gd in trb.values(): gdirs_tribs += gd # check if all tributaries are only used once rgiids = [gd.rgi_id for gd in gdirs_tribs] if not len(np.unique(rgiids)) == len(rgiids): raise RuntimeError('Every tributary glacier should only be used once!') # create merged glacier directories gdirs_merged = execute_entity_task( utils.initialize_merged_gdir, gdirs_main, tribs=tribs_dict, glcdf=glcdf, filename=filename, input_filesuffix=input_filesuffix) # Merge the Tributary glacier flowlines to the main glacier one execute_entity_task(flowline.merge_tributary_flowlines, gdirs_merged, tribs=tribs_dict, filename=filename, input_filesuffix=input_filesuffix) return gdirs_merged
def test_thick_elev_bands(): fig, ax = plt.subplots() gdir = init_columbia_eb(dir_name='test_thick_eb') workflow.inversion_tasks(utils.tolist(gdir)) inversion.distribute_thickness_per_altitude(gdir) graphics.plot_distributed_thickness(gdir, ax=ax) fig.tight_layout() return fig
def plot_domain(gdirs, ax=None, smap=None, use_netcdf=False): """Plot the glacier directory. Parameters ---------- gdirs ax smap use_netcdf : bool use output of glacier_masks instead of geotiff DEM """ # Files gdir = gdirs[0] if use_netcdf: with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc: topo = nc.variables['topo'][:] else: topo = gis.read_geotiff_dem(gdir) try: smap.set_data(topo) except ValueError: pass cm = truncate_colormap(OGGM_CMAPS['terrain'], minval=0.25, maxval=1.0) smap.set_cmap(cm) smap.set_plot_params(nlevels=256) for gdir in gdirs: crs = gdir.grid.center_grid try: geom = gdir.read_pickle('geometries') # Plot boundaries poly_pix = geom['polygon_pix'] smap.set_geometry(poly_pix, crs=crs, fc='white', alpha=0.3, zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) except FileNotFoundError: smap.set_shapefile(gdir.read_shapefile('outlines')) smap.plot(ax) return dict(cbar_label='Alt. [m]')
def __init__(self, gdir, fls=None, mu_star=None, mb_model_class=PastMassBalance, **kwargs): """Initialize. Parameters ---------- gdir : GlacierDirectory the glacier directory mu_star : float or list of floats, optional set to the alternative value of mu* you want to use (the default is to use the calibrated value). Give a list of values for flowline-specific mu* fls : list, optional list of flowline objects to use (defaults to 'model_flowlines', and if not available, to 'inversion_flowlines') mb_model_class : class, optional the mass-balance model to use (e.g. PastMassBalance, ConstantMassBalance...) kwargs : kwargs to pass to mb_model_class """ # Read in the flowlines if fls is None: for fn in ['model_flowlines', 'inversion_flowlines']: try: fls = gdir.read_pickle(fn) except FileNotFoundError: pass if fls is None: raise RuntimeError('Need a valid `model_flowlines` or ' '`inversion_flowlines` to continue!') self.fls = fls # User mu*? if mu_star is not None: mu_star = tolist(mu_star, length=len(fls)) for fl, mu in zip(self.fls, mu_star): fl.mu_star = mu # Initialise the mb models self.flowline_mb_models = [ mb_model_class(gdir, mu_star=fl.mu_star, **kwargs) for fl in self.fls ] self.valid_bounds = self.flowline_mb_models[-1].valid_bounds
def _check_duplicates(rgidf=None): """Complain if the input has duplicates.""" if rgidf is None: return # Check if dataframe or list of strs try: rgidf = rgidf.RGIId except AttributeError: rgidf = utils.tolist(rgidf) u, c = np.unique(rgidf, return_counts=True) if len(u) < len(rgidf): raise InvalidWorkflowError('Found duplicates in the list of ' 'RGI IDs: {}'.format(u[c > 1]))
def execute_entity_task(task, gdirs, **kwargs): """Execute a task on gdirs. If you asked for multiprocessing, it will do it. If ``task`` has more arguments than `gdir` they have to be keyword arguments. Parameters ---------- task : function the entity task to apply gdirs : list of :py:class:`oggm.GlacierDirectory` objects the glacier directories to process """ if task.__dict__.get('is_global_task', False): raise InvalidWorkflowError('execute_entity_task cannot be used on ' 'global tasks.') # Should be iterable gdirs = utils.tolist(gdirs) ng = len(gdirs) if ng == 0: log.workflow('Called entity task %s on 0 glaciers. Returning...', task.__name__) return log.workflow('Execute entity task %s on %d glaciers', task.__name__, ng) pc = _pickle_copier(task, kwargs) if _have_ogmpi: if ogmpi.OGGM_MPI_COMM is not None: return ogmpi.mpi_master_spin_tasks(pc, gdirs) if cfg.PARAMS['use_multiprocessing'] and ng > 1: mppool = init_mp_pool(cfg.CONFIG_MODIFIED) out = mppool.map(pc, gdirs, chunksize=1) else: if ng > 3: log.workflow( 'WARNING: you are trying to run an entity task on ' '%d glaciers with multiprocessing turned off. OGGM ' 'will run faster with multiprocessing turned on.', ng) out = [pc(gdir) for gdir in gdirs] return out
def _check_rgi_input(rgidf=None): """Complain if the input has duplicates.""" if rgidf is None: return # Check if dataframe or list of strs try: rgi_ids = rgidf.RGIId # if dataframe we can also check for connectivity if 'Connect' in rgidf and np.any(rgidf['Connect'] == 2): log.workflow('WARNING! You have glaciers with connectivity level ' '2 in your list. OGGM does not provide pre-processed ' 'directories for these.') except AttributeError: rgi_ids = utils.tolist(rgidf) u, c = np.unique(rgi_ids, return_counts=True) if len(u) < len(rgi_ids): raise InvalidWorkflowError('Found duplicates in the list of ' 'RGI IDs: {}'.format(u[c > 1]))
def execute_entity_task(task, gdirs, **kwargs): """Execute a task on gdirs. If you asked for multiprocessing, it will do it. If ``task`` has more arguments than `gdir` they have to be keyword arguments. Parameters ---------- task : function the entity task to apply gdirs : list of :py:class:`oggm.GlacierDirectory` objects the glacier directories to process """ # Should be iterable gdirs = utils.tolist(gdirs) if len(gdirs) == 0: return log.workflow('Execute entity task %s on %d glaciers', task.__name__, len(gdirs)) if task.__dict__.get('global_task', False): return task(gdirs, **kwargs) pc = _pickle_copier(task, kwargs) if _have_ogmpi: if ogmpi.OGGM_MPI_COMM is not None: return ogmpi.mpi_master_spin_tasks(pc, gdirs) if cfg.PARAMS['use_multiprocessing']: mppool = init_mp_pool(cfg.CONFIG_MODIFIED) out = mppool.map(pc, gdirs, chunksize=1) else: out = [pc(gdir) for gdir in gdirs] return out
def plot_domain(gdirs, ax=None, smap=None): """Plot the glacier directory.""" # Files gdir = gdirs[0] topo = salem.GeoTiff(gdir.get_filepath('dem')).get_vardata() try: smap.set_data(topo) except ValueError: pass cm = truncate_colormap(ALTITUDE_CMAP, minval=0.25, maxval=1.0, n=256) smap.set_cmap(cm) smap.set_plot_params(nlevels=256) for gdir in gdirs: crs = gdir.grid.center_grid try: geom = gdir.read_pickle('geometries') # Plot boundaries poly_pix = geom['polygon_pix'] smap.set_geometry(poly_pix, crs=crs, fc='white', alpha=0.3, zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) except FileNotFoundError: smap.set_shapefile(gdir.read_shapefile('outlines')) smap.plot(ax) return dict(cbar_label='Alt. [m]')
def plot_googlemap(gdirs, ax=None, figsize=None): """Plots the glacier(s) over a googlemap.""" dofig = False if ax is None: fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) dofig = True gdirs = utils.tolist(gdirs) xx, yy = [], [] for gdir in gdirs: xx.extend(gdir.extent_ll[0]) yy.extend(gdir.extent_ll[1]) gm = salem.GoogleVisibleMap(xx, yy, key='AIzaSyDWG_aTgfU7CeErtIzWfdGxpStTlvDXV_o') img = gm.get_vardata() cmap = salem.Map(gm.grid, countries=False, nx=gm.grid.nx) cmap.set_rgb(img) for gdir in gdirs: cmap.set_shapefile(gdir.read_shapefile('outlines')) cmap.plot(ax) title = '' if len(gdirs) == 1: title = gdir.rgi_id if gdir.name is not None and gdir.name != '': title += ': ' + gdir.name ax.set_title(title) if dofig: plt.tight_layout()
def plot_domain(gdirs, ax=None, smap=None): """Plot the glacier directory.""" # Files gdir = gdirs[0] topo = salem.GeoTiff(gdir.get_filepath('dem')).get_vardata() try: smap.set_data(topo) except ValueError: pass cm = truncate_colormap(ALTITUDE_CMAP, minval=0.25, maxval=1.0, n=256) smap.set_cmap(cm) smap.set_plot_params(nlevels=256) for gdir in gdirs: crs = gdir.grid.center_grid try: geom = gdir.read_pickle('geometries') # Plot boundaries poly_pix = geom['polygon_pix'] smap.set_geometry(poly_pix, crs=crs, fc='white', alpha=0.3, zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) except FileNotFoundError: smap.set_shapefile(gdir.read_shapefile('outlines')) smap.plot(ax) return dict(cbar_label='Alt. [m]')
def plot_googlemap(gdirs, ax=None, figsize=None): """Plots the glacier(s) over a googlemap.""" dofig = False if ax is None: fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) dofig = True gdirs = utils.tolist(gdirs) xx, yy = [], [] for gdir in gdirs: xx.extend(gdir.extent_ll[0]) yy.extend(gdir.extent_ll[1]) gm = salem.GoogleVisibleMap(xx, yy, key='AIzaSyDWG_aTgfU7CeErtIzWfdGxpStTlvDXV_o') img = gm.get_vardata() cmap = salem.Map(gm.grid, countries=False, nx=gm.grid.nx) cmap.set_rgb(img) for gdir in gdirs: cmap.set_shapefile(gdir.read_shapefile('outlines')) cmap.plot(ax) title = '' if len(gdirs) == 1: title = gdir.rgi_id if gdir.name is not None and gdir.name != '': title += ': ' + gdir.name ax.set_title(title) if dofig: plt.tight_layout()
def __init__(self, gdir, fls=None, mu_star=None, mb_model_class=PastMassBalance, use_inversion_flowlines=False, input_filesuffix='', bias=None, **kwargs): """Initialize. Parameters ---------- gdir : GlacierDirectory the glacier directory mu_star : float or list of floats, optional set to the alternative value of mu* you want to use (the default is to use the calibrated value). Give a list of values for flowline-specific mu* fls : list, optional list of flowline objects to use (defaults to 'model_flowlines', and if not available, to 'inversion_flowlines') mb_model_class : class, optional the mass-balance model to use (e.g. PastMassBalance, ConstantMassBalance...) use_inversion_flowlines: bool, optional if True 'inversion_flowlines' instead of 'model_flowlines' will be used. input_filesuffix : str the file suffix of the input climate file bias : float, optional set to the alternative value of the calibration bias [mm we yr-1] you want to use (the default is to use the calibrated value) Note that this bias is *substracted* from the computed MB. Indeed: BIAS = MODEL_MB - REFERENCE_MB. kwargs : kwargs to pass to mb_model_class """ # Read in the flowlines if use_inversion_flowlines: fls = gdir.read_pickle('inversion_flowlines') if fls is None: try: fls = gdir.read_pickle('model_flowlines') except FileNotFoundError: raise RuntimeError('Need a valid `model_flowlines` file. ' 'If you explicitly want to use ' '`inversion_flowlines`, set ' 'use_inversion_flowlines=True.') from None self.fls = fls _y0 = kwargs.get('y0', None) # User mu*? if mu_star is not None: mu_star = tolist(mu_star, length=len(fls)) for fl, mu in zip(self.fls, mu_star): fl.mu_star = mu # Initialise the mb models self.flowline_mb_models = [] for fl in self.fls: # Merged glaciers will need different climate files, use filesuffix if (fl.rgi_id is not None) and (fl.rgi_id != gdir.rgi_id): rgi_filesuffix = '_' + fl.rgi_id + input_filesuffix else: rgi_filesuffix = input_filesuffix # merged glaciers also have a different MB bias from calibration if ((bias is None) and cfg.PARAMS['use_bias_for_run'] and (fl.rgi_id != gdir.rgi_id)): df = gdir.read_json('local_mustar', filesuffix='_' + fl.rgi_id) fl_bias = df['bias'] else: fl_bias = bias # Constant and RandomMassBalance need y0 if not provided if (issubclass(mb_model_class, RandomMassBalance) or issubclass(mb_model_class, ConstantMassBalance)) and ( fl.rgi_id != gdir.rgi_id) and (_y0 is None): df = gdir.read_json('local_mustar', filesuffix='_' + fl.rgi_id) kwargs['y0'] = df['t_star'] self.flowline_mb_models.append( mb_model_class(gdir, mu_star=fl.mu_star, bias=fl_bias, input_filesuffix=rgi_filesuffix, **kwargs)) self.valid_bounds = self.flowline_mb_models[-1].valid_bounds
def plot_modeloutput_map(gdirs, ax=None, smap=None, model=None, vmax=None, linewidth=3, filesuffix='', modelyr=None): """Plots the result of the model output.""" gdir = gdirs[0] with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc: topo = nc.variables['topo'][:] # Dirty optim try: smap.set_topography(topo) except ValueError: pass toplot_th = np.array([]) toplot_lines = [] toplot_crs = [] if model is None: models = [] for gdir in gdirs: model = FileModel( gdir.get_filepath('model_run', filesuffix=filesuffix)) model.run_until(modelyr) models.append(model) else: models = utils.tolist(model) for gdir, model in zip(gdirs, models): geom = gdir.read_pickle('geometries') poly_pix = geom['polygon_pix'] crs = gdir.grid.center_grid smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) # plot Centerlines cls = model.fls for l in cls: smap.set_geometry(l.line, crs=crs, color='gray', linewidth=1.2, zorder=50) toplot_th = np.append(toplot_th, l.thick) widths = l.widths.copy() widths = np.where(l.thick > 0, widths, 0.) for wi, cur, (n1, n2) in zip(widths, l.line.coords, l.normals): line = shpg.LineString([ shpg.Point(cur + wi / 2. * n1), shpg.Point(cur + wi / 2. * n2) ]) toplot_lines.append(line) toplot_crs.append(crs) dl = salem.DataLevels(cmap=OGGM_CMAPS['section_thickness'], data=toplot_th, vmin=0, vmax=vmax) colors = dl.to_rgb() for l, c, crs in zip(toplot_lines, colors, toplot_crs): smap.set_geometry(l, crs=crs, color=c, linewidth=linewidth, zorder=50) smap.plot(ax) return dict(cbar_label='Section thickness [m]', cbar_primitive=dl, title_comment=' -- year: {:d}'.format(np.int64(model.yr)))
def plot_centerlines(gdirs, ax=None, smap=None, use_flowlines=False, add_downstream=False, lines_cmap='Set1', add_line_index=False, use_model_flowlines=False): """Plots the centerlines of a glacier directory.""" if add_downstream and not use_flowlines: raise ValueError('Downstream lines can be plotted with flowlines only') # Files filename = 'centerlines' if use_model_flowlines: filename = 'model_flowlines' elif use_flowlines: filename = 'inversion_flowlines' gdir = gdirs[0] with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc: topo = nc.variables['topo'][:] cm = truncate_colormap(OGGM_CMAPS['terrain'], minval=0.25, maxval=1.0) smap.set_plot_params(cmap=cm) smap.set_data(topo) for gdir in gdirs: crs = gdir.grid.center_grid geom = gdir.read_pickle('geometries') # Plot boundaries poly_pix = geom['polygon_pix'] smap.set_geometry(poly_pix, crs=crs, fc='white', alpha=0.3, zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) # plot Centerlines cls = gdir.read_pickle(filename) # Go in reverse order for red always being the longuest cls = cls[::-1] nl = len(cls) color = gencolor(len(cls) + 1, cmap=lines_cmap) for i, (l, c) in enumerate(zip(cls, color)): if add_downstream and not gdir.is_tidewater and l is cls[0]: line = gdir.read_pickle('downstream_line')['full_line'] else: line = l.line smap.set_geometry(line, crs=crs, color=c, linewidth=2.5, zorder=50) text = '{}'.format(nl - i - 1) if add_line_index else None smap.set_geometry(l.head, crs=gdir.grid, marker='o', markersize=60, alpha=0.8, color=c, zorder=99, text=text) for j in l.inflow_points: smap.set_geometry(j, crs=crs, marker='o', markersize=40, edgecolor='k', alpha=0.8, zorder=99, facecolor='none') smap.plot(ax) return dict(cbar_label='Alt. [m]')
def execute_entity_task(task, gdirs, **kwargs): """Execute a task on gdirs. If you asked for multiprocessing, it will do it. If ``task`` has more arguments than `gdir` they have to be keyword arguments. Parameters ---------- task : function or sequence of functions The entity task(s) to apply. Can be None, in which case each gdir is expected to be a tuple of (task, gdir). When passing a sequence, each item can also optionally be a tuple of (task, dictionary). In this case the dictionary items will be passed to the task as kwargs. gdirs : list of :py:class:`oggm.GlacierDirectory` objects The glacier directories to process. Each individual gdir can optionally be a tuple of (gdir, dictionary). In this case, the values in the dictionary will be passed to the task as keyword arguments for that specific gdir. Returns ------- List of results from task. Last task if a list of tasks was given. """ # Normalize task into list of tuples for simplicity if not isinstance(task, Sequence): task = [task] tasks = [] for t in task: if isinstance(t, tuple): tasks.append(t) else: tasks.append((t, {})) # Reject global tasks for t in tasks: if t[0].__dict__.get('is_global_task', False): raise InvalidWorkflowError('execute_entity_task cannot be used on ' 'global tasks.') # Should be iterable gdirs = utils.tolist(gdirs) ng = len(gdirs) if ng == 0: log.workflow('Called execute_entity_task on 0 glaciers. Returning...') return log.workflow('Execute entity tasks [%s] on %d glaciers', ', '.join([t[0].__name__ for t in tasks]), ng) pc = _pickle_copier(tasks, kwargs) if _have_ogmpi: if ogmpi.OGGM_MPI_COMM is not None: return ogmpi.mpi_master_spin_tasks(pc, gdirs) if cfg.PARAMS['use_multiprocessing'] and ng > 1: mppool = init_mp_pool(cfg.CONFIG_MODIFIED) out = mppool.map(pc, gdirs, chunksize=1) else: if ng > 3: log.workflow( 'WARNING: you are trying to run an entity task on ' '%d glaciers with multiprocessing turned off. OGGM ' 'will run faster with multiprocessing turned on.', ng) out = [pc(gdir) for gdir in gdirs] return out
def newplotfunc(gdirs, ax=None, smap=None, add_colorbar=True, title=None, title_comment=None, horizontal_colorbar=False, lonlat_contours_kwargs=None, cbar_ax=None, autosave=False, add_scalebar=True, figsize=None, savefig=None, savefig_kwargs=None, **kwargs): dofig = False if ax is None: fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) dofig = True # Cast to list gdirs = utils.tolist(gdirs) if smap is None: mp = salem.Map(gdirs[0].grid, countries=False, nx=gdirs[0].grid.nx) else: mp = smap if lonlat_contours_kwargs is not None: mp.set_lonlat_contours(**lonlat_contours_kwargs) if add_scalebar: mp.set_scale_bar() out = plotfunc(gdirs, ax=ax, smap=mp, **kwargs) if add_colorbar and 'cbar_label' in out: cbprim = out.get('cbar_primitive', mp) if cbar_ax: cb = cbprim.colorbarbase(cbar_ax) else: if horizontal_colorbar: cb = cbprim.append_colorbar(ax, "bottom", size="5%", pad=0.4) else: cb = cbprim.append_colorbar(ax, "right", size="5%", pad=0.2) cb.set_label(out['cbar_label']) if title is None: if 'title' not in out: # Make a defaut one title = '' if len(gdirs) == 1: gdir = gdirs[0] title = gdir.rgi_id if gdir.name is not None and gdir.name != '': title += ': ' + gdir.name out['title'] = title if title_comment is None: title_comment = out.get('title_comment', '') out['title'] += title_comment ax.set_title(out['title']) else: ax.set_title(title) if dofig: plt.tight_layout() if autosave: savefig = os.path.join(cfg.PATHS['working_dir'], 'plots') utils.mkdir(savefig) savefig = os.path.join( savefig, plotfunc.__name__ + '_' + gdirs[0].rgi_id + '.png') if savefig is not None: plt.savefig(savefig, **savefig_kwargs) plt.close()
def newplotfunc(gdirs, ax=None, smap=None, add_colorbar=True, title=None, title_comment=None, horizontal_colorbar=False, lonlat_contours_kwargs=None, cbar_ax=None, autosave=False, add_scalebar=True, figsize=None, savefig=None, savefig_kwargs=None, **kwargs): dofig = False if ax is None: fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) dofig = True # Cast to list gdirs = utils.tolist(gdirs) if smap is None: mp = salem.Map(gdirs[0].grid, countries=False, nx=gdirs[0].grid.nx) else: mp = smap if lonlat_contours_kwargs is not None: mp.set_lonlat_contours(**lonlat_contours_kwargs) if add_scalebar: mp.set_scale_bar() out = plotfunc(gdirs, ax=ax, smap=mp, **kwargs) if add_colorbar and 'cbar_label' in out: cbprim = out.get('cbar_primitive', mp) if cbar_ax: cb = cbprim.colorbarbase(cbar_ax) else: if horizontal_colorbar: cb = cbprim.append_colorbar(ax, "bottom", size="5%", pad=0.4) else: cb = cbprim.append_colorbar(ax, "right", size="5%", pad=0.2) cb.set_label(out['cbar_label']) if title is None: if 'title' not in out: # Make a defaut one title = '' if len(gdirs) == 1: gdir = gdirs[0] title = gdir.rgi_id if gdir.name is not None and gdir.name != '': title += ': ' + gdir.name out['title'] = title if title_comment is None: title_comment = out.get('title_comment', '') out['title'] += title_comment ax.set_title(out['title']) else: ax.set_title(title) if dofig: plt.tight_layout() if autosave: savefig = os.path.join(cfg.PATHS['working_dir'], 'plots') utils.mkdir(savefig) savefig = os.path.join(savefig, plotfunc.__name__ + '_' + gdirs[0].rgi_id + '.png') if savefig is not None: plt.savefig(savefig, **savefig_kwargs) plt.close()
def __init__(self, gdir, fls=None, mu_star=None, mb_model_class=PastMassBalance, use_inversion_flowlines=False, input_filesuffix='', bias=None, **kwargs): """Initialize. Parameters ---------- gdir : GlacierDirectory the glacier directory mu_star : float or list of floats, optional set to the alternative value of mu* you want to use (the default is to use the calibrated value). Give a list of values for flowline-specific mu* fls : list, optional list of flowline objects to use (defaults to 'model_flowlines', and if not available, to 'inversion_flowlines') mb_model_class : class, optional the mass-balance model to use (e.g. PastMassBalance, ConstantMassBalance...) use_inversion_flowlines: bool, optional if True 'inversion_flowlines' instead of 'model_flowlines' will be used. input_filesuffix : str the file suffix of the input climate file bias : float, optional set to the alternative value of the calibration bias [mm we yr-1] you want to use (the default is to use the calibrated value) Note that this bias is *substracted* from the computed MB. Indeed: BIAS = MODEL_MB - REFERENCE_MB. kwargs : kwargs to pass to mb_model_class """ # Read in the flowlines if use_inversion_flowlines: fls = gdir.read_pickle('inversion_flowlines') if fls is None: try: fls = gdir.read_pickle('model_flowlines') except FileNotFoundError: raise RuntimeError('Need a valid `model_flowlines` file. ' 'If you explicitly want to use ' '`inversion_flowlines`, set ' 'use_inversion_flowlines=True.') from None self.fls = fls _y0 = kwargs.get('y0', None) # User mu*? if mu_star is not None: mu_star = tolist(mu_star, length=len(fls)) for fl, mu in zip(self.fls, mu_star): fl.mu_star = mu # Initialise the mb models self.flowline_mb_models = [] for fl in self.fls: # Merged glaciers will need different climate files, use filesuffix if (fl.rgi_id is not None) and (fl.rgi_id != gdir.rgi_id): rgi_filesuffix = '_' + fl.rgi_id + input_filesuffix else: rgi_filesuffix = input_filesuffix # merged glaciers also have a different MB bias from calibration if ((bias is None) and cfg.PARAMS['use_bias_for_run'] and (fl.rgi_id != gdir.rgi_id)): df = gdir.read_json('local_mustar', filesuffix='_' + fl.rgi_id) fl_bias = df['bias'] else: fl_bias = bias # Constant and RandomMassBalance need y0 if not provided if (issubclass(mb_model_class, RandomMassBalance) or issubclass(mb_model_class, ConstantMassBalance)) and ( fl.rgi_id != gdir.rgi_id) and (_y0 is None): df = gdir.read_json('local_mustar', filesuffix='_' + fl.rgi_id) kwargs['y0'] = df['t_star'] self.flowline_mb_models.append( mb_model_class(gdir, mu_star=fl.mu_star, bias=fl_bias, input_filesuffix=rgi_filesuffix, **kwargs)) self.valid_bounds = self.flowline_mb_models[-1].valid_bounds
def plot_centerlines(gdirs, ax=None, smap=None, use_flowlines=False, add_downstream=False, lines_cmap='Set1', add_line_index=False, use_model_flowlines=False): """Plots the centerlines of a glacier directory.""" if add_downstream and not use_flowlines: raise ValueError('Downstream lines can be plotted with flowlines only') # Files filename = 'centerlines' if use_model_flowlines: filename = 'model_flowlines' elif use_flowlines: filename = 'inversion_flowlines' gdir = gdirs[0] with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc: topo = nc.variables['topo'][:] cm = truncate_colormap(ALTITUDE_CMAP, minval=0.25, maxval=1.0, n=256) smap.set_cmap(cm) smap.set_plot_params(nlevels=256) smap.set_data(topo) for gdir in gdirs: crs = gdir.grid.center_grid geom = gdir.read_pickle('geometries') # Plot boundaries poly_pix = geom['polygon_pix'] smap.set_geometry(poly_pix, crs=crs, fc='white', alpha=0.3, zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) # plot Centerlines cls = gdir.read_pickle(filename) # Go in reverse order for red always being the longuest cls = cls[::-1] nl = len(cls) color = gencolor(len(cls) + 1, cmap=lines_cmap) for i, (l, c) in enumerate(zip(cls, color)): if add_downstream and not gdir.is_tidewater and l is cls[0]: line = gdir.read_pickle('downstream_line')['full_line'] else: line = l.line smap.set_geometry(line, crs=crs, color=c, linewidth=2.5, zorder=50) text = '{}'.format(nl - i - 1) if add_line_index else None smap.set_geometry(l.head, crs=gdir.grid, marker='o', markersize=60, alpha=0.8, color=c, zorder=99, text=text) for j in l.inflow_points: smap.set_geometry(j, crs=crs, marker='o', markersize=40, edgecolor='k', alpha=0.8, zorder=99, facecolor='none') smap.plot(ax) return dict(cbar_label='Alt. [m]')
def init_glacier_regions(rgidf=None, *, reset=False, force=False, from_prepro_level=None, prepro_border=None, prepro_rgi_version=None, from_tar=False, delete_tar=False, use_demo_glaciers=None): """Initializes the list of Glacier Directories for this run. This is the very first task to do (always). If the directories are already available in the working directory, use them. If not, create new ones. Parameters ---------- rgidf : GeoDataFrame or list of ids, optional for pre-computed runs the RGI glacier outlines. If unavailable, OGGM will parse the information from the glacier directories found in the working directory. It is required for new runs. reset : bool delete the existing glacier directories if found. force : bool setting `reset=True` will trigger a yes/no question to the user. Set `force=True` to avoid this. from_prepro_level : int get the gdir data from the official pre-processed pool. See the documentation for more information prepro_border : int for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['border']` prepro_rgi_version : str for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['rgi_version']` use_demo_glaciers : bool whether to check the demo glaciers for download (faster than the standard prepro downloads). The default is to decide whether or not to check based on simple crietria such as glacier list size. from_tar : bool, default=False extract the gdir data from a tar file. If set to `True`, will check for a tar file at the expected location in `base_dir`. delete_tar : bool, default=False delete the original tar file after extraction. delete_tar : bool, default=False delete the original tar file after extraction. Returns ------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the initialised glacier directories """ if reset and not force: reset = utils.query_yes_no('Delete all glacier directories?') # if reset delete also the log directory if reset: fpath = os.path.join(cfg.PATHS['working_dir'], 'log') if os.path.exists(fpath): rmtree(fpath) gdirs = [] new_gdirs = [] if rgidf is None: if reset: raise ValueError('Cannot use reset without setting rgidf') log.workflow('init_glacier_regions by parsing available folders ' '(can be slow).') # The dirs should be there already gl_dir = os.path.join(cfg.PATHS['working_dir'], 'per_glacier') for root, _, files in os.walk(gl_dir): if files and ('dem.tif' in files): gdirs.append(oggm.GlacierDirectory(os.path.basename(root))) else: # Check if dataframe or list of strs try: entities = [] for _, entity in rgidf.iterrows(): entities.append(entity) except AttributeError: entities = utils.tolist(rgidf) # Check demo if use_demo_glaciers is None: use_demo_glaciers = len(entities) < 100 if from_prepro_level is not None: log.workflow('init_glacier_regions from prepro level {} on ' '{} glaciers.'.format(from_prepro_level, len(entities))) gdirs = execute_entity_task(gdir_from_prepro, entities, from_prepro_level=from_prepro_level, prepro_border=prepro_border, prepro_rgi_version=prepro_rgi_version, check_demo_glacier=use_demo_glaciers) else: # TODO: if necessary this could use multiprocessing as well for entity in entities: gdir = oggm.GlacierDirectory(entity, reset=reset, from_tar=from_tar, delete_tar=delete_tar) if not os.path.exists(gdir.get_filepath('dem')): new_gdirs.append((gdir, dict(entity=entity))) gdirs.append(gdir) # We can set the intersects file automatically here if (cfg.PARAMS['use_intersects'] and new_gdirs and (len(cfg.PARAMS['intersects_gdf']) == 0)): rgi_ids = np.unique(np.sort([t[0].rgi_id for t in new_gdirs])) rgi_version = new_gdirs[0][0].rgi_version fp = utils.get_rgi_intersects_entities(rgi_ids, version=rgi_version) cfg.set_intersects_db(fp) # If not initialized, run the task in parallel execute_entity_task(tasks.define_glacier_region, new_gdirs) return gdirs
def merge_glacier_tasks(gdirs, main_rgi_ids, glcdf=None, filename='climate_monthly', input_filesuffix=''): """Shortcut function: run all tasks to merge tributaries to a main glacier TODO - Automatic search for tributary glaciers TODO - Every tributary should only be used once Parameters ---------- gdirs : list of :py:class:`oggm.GlacierDirectory` all glaciers, main and tributary. Preprocessed and initialised main_rgi_ids: list of str RGI IDs of the main glaciers of interest glcdf: geopandas.GeoDataFrame which contains the main glaciers, will be downloaded if None filename: str Baseline climate file input_filesuffix: str Filesuffix to the climate file Returns ------- merged_gdirs: list of merged GlacierDirectories """ # make sure rgi_ids are iteratable main_rgi_ids = utils.tolist(main_rgi_ids) # split main glaciers from candidates gdirs_main = [gd for gd in gdirs if gd.rgi_id in main_rgi_ids] # find true tributary glaciers tributaries = execute_entity_task(centerlines.intersect_downstream_lines, gdirs_main, candidates=gdirs) # make one dictionary and a list of all gdirs for further preprocessing tribs_dict = {} gdirs_tribs = [] for trb in tributaries: tribs_dict.update(trb) for gd in trb.values(): gdirs_tribs += gd # check if all tributaries are only used once rgiids = [gd.rgi_id for gd in gdirs_tribs] if not len(np.unique(rgiids)) == len(rgiids): raise RuntimeError('Every tributary glacier should only be used once!') # create merged glacier directories gdirs_merged = execute_entity_task(utils.initialize_merged_gdir, gdirs_main, tribs=tribs_dict, glcdf=glcdf, filename=filename, input_filesuffix=input_filesuffix) # Merge the Tributary glacier flowlines to the main glacier one execute_entity_task(flowline.merge_tributary_flowlines, gdirs_merged, tribs=tribs_dict, filename=filename, input_filesuffix=input_filesuffix) return gdirs_merged
def plot_modeloutput_map(gdirs, ax=None, smap=None, model=None, vmax=None, linewidth=3, filesuffix='', modelyr=None): """Plots the result of the model output.""" gdir = gdirs[0] with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc: topo = nc.variables['topo'][:] # Dirty optim try: smap.set_topography(topo) except ValueError: pass toplot_th = np.array([]) toplot_lines = [] toplot_crs = [] if model is None: models = [] for gdir in gdirs: model = FileModel(gdir.get_filepath('model_run', filesuffix=filesuffix)) model.run_until(modelyr) models.append(model) else: models = utils.tolist(model) for gdir, model in zip(gdirs, models): geom = gdir.read_pickle('geometries') poly_pix = geom['polygon_pix'] crs = gdir.grid.center_grid smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2, linewidth=.2) poly_pix = utils.tolist(poly_pix) for _poly in poly_pix: for l in _poly.interiors: smap.set_geometry(l, crs=crs, color='black', linewidth=0.5) # plot Centerlines cls = model.fls for l in cls: smap.set_geometry(l.line, crs=crs, color='gray', linewidth=1.2, zorder=50) toplot_th = np.append(toplot_th, l.thick) widths = l.widths.copy() widths = np.where(l.thick > 0, widths, 0.) for wi, cur, (n1, n2) in zip(widths, l.line.coords, l.normals): line = shpg.LineString([shpg.Point(cur + wi/2. * n1), shpg.Point(cur + wi/2. * n2)]) toplot_lines.append(line) toplot_crs.append(crs) dl = salem.DataLevels(cmap=SECTION_THICKNESS_CMAP, nlevels=256, data=toplot_th, vmin=0, vmax=vmax) colors = dl.to_rgb() for l, c, crs in zip(toplot_lines, colors, toplot_crs): smap.set_geometry(l, crs=crs, color=c, linewidth=linewidth, zorder=50) smap.plot(ax) return dict(cbar_label='Section thickness [m]', cbar_primitive=dl, title_comment=' -- year: {:d}'.format(np.int64(model.yr)))
def init_glacier_directories(rgidf=None, *, reset=False, force=False, from_prepro_level=None, prepro_border=None, prepro_rgi_version=None, prepro_base_url=None, from_tar=False, delete_tar=False, use_demo_glaciers=None): """Initializes the list of Glacier Directories for this run. This is the very first task to do (always). If the directories are already available in the working directory, use them. If not, create new ones. Parameters ---------- rgidf : GeoDataFrame or list of ids, optional for pre-computed runs the RGI glacier outlines. If unavailable, OGGM will parse the information from the glacier directories found in the working directory. It is required for new runs. reset : bool delete the existing glacier directories if found. force : bool setting `reset=True` will trigger a yes/no question to the user. Set `force=True` to avoid this. from_prepro_level : int get the gdir data from the official pre-processed pool. See the documentation for more information prepro_border : int for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['border']` prepro_rgi_version : str for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['rgi_version']` prepro_base_url : str for `from_prepro_level` only: if you want to override the default URL from which to download the gdirs. Default currently is https://cluster.klima.uni-bremen.de/~fmaussion/gdirs/oggm_v1.1/ use_demo_glaciers : bool whether to check the demo glaciers for download (faster than the standard prepro downloads). The default is to decide whether or not to check based on simple criteria such as glacier list size. from_tar : bool, default=False extract the gdir data from a tar file. If set to `True`, will check for a tar file at the expected location in `base_dir`. delete_tar : bool, default=False delete the original tar file after extraction. Returns ------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the initialised glacier directories Notes ----- This task is very similar to init_glacier_regions, with one main difference: it does not process the DEMs for this glacier. Eventually, init_glacier_regions will be deprecated and removed from the codebase. """ _check_duplicates(rgidf) if reset and not force: reset = utils.query_yes_no('Delete all glacier directories?') if from_prepro_level: url = utils.get_prepro_base_url(base_url=prepro_base_url, border=prepro_border, prepro_level=from_prepro_level, rgi_version=prepro_rgi_version) if cfg.PARAMS['has_internet'] and not utils.url_exists(url): raise InvalidParamsError("base url seems unreachable with these " "parameters: {}".format(url)) # if reset delete also the log directory if reset: fpath = os.path.join(cfg.PATHS['working_dir'], 'log') if os.path.exists(fpath): rmtree(fpath) if rgidf is None: # Infer the glacier directories from folders available in working dir if reset: raise ValueError('Cannot use reset without setting rgidf') log.workflow('init_glacier_directories by parsing all available ' 'folders (this takes time: if possible, provide rgidf ' 'instead).') # The dirs should be there already gl_dir = os.path.join(cfg.PATHS['working_dir'], 'per_glacier') gdirs = [] for root, _, files in os.walk(gl_dir): if files and ('outlines.shp' in files or 'outlines.tar.gz' in files): gdirs.append(oggm.GlacierDirectory(os.path.basename(root))) else: # Create glacier directories from input # Check if dataframe or list of str try: entities = [] for _, entity in rgidf.iterrows(): entities.append(entity) except AttributeError: entities = utils.tolist(rgidf) # Check demo if use_demo_glaciers is None: use_demo_glaciers = len(entities) < 100 if from_prepro_level is not None: log.workflow('init_glacier_directories from prepro level {} on ' '{} glaciers.'.format(from_prepro_level, len(entities))) # Read the hash dictionary before we use multiproc if cfg.PARAMS['dl_verify']: utils.get_dl_verify_data('cluster.klima.uni-bremen.de') gdirs = execute_entity_task(gdir_from_prepro, entities, from_prepro_level=from_prepro_level, prepro_border=prepro_border, prepro_rgi_version=prepro_rgi_version, check_demo_glacier=use_demo_glaciers, base_url=prepro_base_url) else: # We can set the intersects file automatically here if (cfg.PARAMS['use_intersects'] and len(cfg.PARAMS['intersects_gdf']) == 0): try: rgi_ids = np.unique(np.sort([entity.RGIId for entity in entities])) rgi_version = rgi_ids[0].split('-')[0][-2:] fp = utils.get_rgi_intersects_entities(rgi_ids, version=rgi_version) cfg.set_intersects_db(fp) except AttributeError: # List of str pass gdirs = execute_entity_task(utils.GlacierDirectory, entities, reset=reset, from_tar=from_tar, delete_tar=delete_tar) return gdirs
def merged_glacier_masks(gdir, geometry): """Makes a gridded mask of a merged glacier outlines. This is a simplified version of glacier_masks. We don't need fancy corrections or smoothing here: The flowlines for the actual model run are based on a proper call of glacier_masks. This task is only to get outlines etc. for visualization! Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data geometry: shapely.geometry.multipolygon.MultiPolygon united outlines of the merged glaciers """ # open srtm tif-file: dem = read_geotiff_dem(gdir) if np.min(dem) == np.max(dem): raise RuntimeError('({}) min equal max in the DEM.' .format(gdir.rgi_id)) # Clip topography to 0 m a.s.l. utils.clip_min(dem, 0, out=dem) # Interpolate shape to a regular path glacier_poly_hr = tolist(geometry) for nr, poly in enumerate(glacier_poly_hr): # transform geometry to map _geometry = salem.transform_geometry(poly, to_crs=gdir.grid.proj) glacier_poly_hr[nr] = _interp_polygon(_geometry, gdir.grid.dx) glacier_poly_hr = shpg.MultiPolygon(glacier_poly_hr) # Transform geometry into grid coordinates # It has to be in pix center coordinates because of how skimage works def proj(x, y): grid = gdir.grid.center_grid return grid.transform(x, y, crs=grid.proj) glacier_poly_hr = shapely.ops.transform(proj, glacier_poly_hr) # simple trick to correct invalid polys: # http://stackoverflow.com/questions/20833344/ # fix-invalid-polygon-python-shapely glacier_poly_hr = glacier_poly_hr.buffer(0) if not glacier_poly_hr.is_valid: raise RuntimeError('This glacier geometry is not valid.') # Rounded geometry to nearest nearest pix # I can not use _polyg # glacier_poly_pix = _polygon_to_pix(glacier_poly_hr) def project(x, y): return np.rint(x).astype(np.int64), np.rint(y).astype(np.int64) glacier_poly_pix = shapely.ops.transform(project, glacier_poly_hr) glacier_poly_pix_iter = tolist(glacier_poly_pix) # Compute the glacier mask (currently: center pixels + touched) nx, ny = gdir.grid.nx, gdir.grid.ny glacier_mask = np.zeros((ny, nx), dtype=np.uint8) glacier_ext = np.zeros((ny, nx), dtype=np.uint8) for poly in glacier_poly_pix_iter: (x, y) = poly.exterior.xy glacier_mask[skdraw.polygon(np.array(y), np.array(x))] = 1 for gint in poly.interiors: x, y = tuple2int(gint.xy) glacier_mask[skdraw.polygon(y, x)] = 0 glacier_mask[y, x] = 0 # on the nunataks, no x, y = tuple2int(poly.exterior.xy) glacier_mask[y, x] = 1 glacier_ext[y, x] = 1 # Last sanity check based on the masked dem tmp_max = np.max(dem[np.where(glacier_mask == 1)]) tmp_min = np.min(dem[np.where(glacier_mask == 1)]) if tmp_max < (tmp_min + 1): raise RuntimeError('({}) min equal max in the masked DEM.' .format(gdir.rgi_id)) # write out the grids in the netcdf file with GriddedNcdfFile(gdir, reset=True) as nc: v = nc.createVariable('topo', 'f4', ('y', 'x', ), zlib=True) v.units = 'm' v.long_name = 'DEM topography' v[:] = dem v = nc.createVariable('glacier_mask', 'i1', ('y', 'x', ), zlib=True) v.units = '-' v.long_name = 'Glacier mask' v[:] = glacier_mask v = nc.createVariable('glacier_ext', 'i1', ('y', 'x', ), zlib=True) v.units = '-' v.long_name = 'Glacier external boundaries' v[:] = glacier_ext # add some meta stats and close nc.max_h_dem = np.max(dem) nc.min_h_dem = np.min(dem) dem_on_g = dem[np.where(glacier_mask)] nc.max_h_glacier = np.max(dem_on_g) nc.min_h_glacier = np.min(dem_on_g) geometries = dict() geometries['polygon_hr'] = glacier_poly_hr geometries['polygon_pix'] = glacier_poly_pix geometries['polygon_area'] = geometry.area gdir.write_pickle(geometries, 'geometries')
def sia_thickness_via_optim(slope, width, flux, shape='rectangular', glen_a=None, fs=None, t_lambda=None): """Compute the thickness numerically instead of analytically. It's the only way that works for trapezoid shapes. Parameters ---------- slope : -np.gradient(hgt, dx) width : section width in m flux : mass flux in m3 s-1 shape : 'rectangular', 'trapezoid' or 'parabolic' glen_a : Glen A, defaults to PARAMS fs : sliding, defaults to PARAMS t_lambda: the trapezoid lambda, defaults to PARAMS Returns ------- the ice thickness (in m) """ if len(np.atleast_1d(slope)) > 1: shape = utils.tolist(shape, len(slope)) t_lambda = utils.tolist(t_lambda, len(slope)) out = [] for sl, w, f, s, t in zip(slope, width, flux, shape, t_lambda): out.append( sia_thickness_via_optim(sl, w, f, shape=s, glen_a=glen_a, fs=fs, t_lambda=t)) return np.asarray(out) # Sanity if flux <= 0: return 0 if width <= MIN_WIDTH_FOR_INV: return 0 if glen_a is None: glen_a = cfg.PARAMS['inversion_glen_a'] if fs is None: fs = cfg.PARAMS['inversion_fs'] if t_lambda is None: t_lambda = cfg.PARAMS['trapezoid_lambdas'] if shape not in ['parabolic', 'rectangular', 'trapezoid']: raise InvalidParamsError('shape must be `parabolic`, `trapezoid` ' 'or `rectangular`, not: {}'.format(shape)) # Ice flow params n = cfg.PARAMS['glen_n'] fd = 2 / (n + 2) * glen_a rho = cfg.PARAMS['ice_density'] rhogh = (rho * cfg.G * slope)**n # To avoid geometrical inconsistencies max_h = width / t_lambda if shape == 'trapezoid' else 1e4 def to_minimize(h): u = (h**(n + 1)) * fd * rhogh + (h**(n - 1)) * fs * rhogh if shape == 'parabolic': sect = 2. / 3. * width * h elif shape == 'trapezoid': w0m = width - t_lambda * h sect = (width + w0m) / 2 * h else: sect = width * h return sect * u - flux out_h, r = optimize.brentq(to_minimize, 0, max_h, full_output=True) return out_h
def init_glacier_regions(rgidf=None, *, reset=False, force=False, from_prepro_level=None, prepro_border=None, prepro_rgi_version=None, prepro_base_url=None, from_tar=False, delete_tar=False, use_demo_glaciers=None): """DEPRECATED: Initializes the list of Glacier Directories for this run. This is the very first task to do (always). If the directories are already available in the working directory, use them. If not, create new ones. Parameters ---------- rgidf : GeoDataFrame or list of ids, optional for pre-computed runs the RGI glacier outlines. If unavailable, OGGM will parse the information from the glacier directories found in the working directory. It is required for new runs. reset : bool delete the existing glacier directories if found. force : bool setting `reset=True` will trigger a yes/no question to the user. Set `force=True` to avoid this. from_prepro_level : int get the gdir data from the official pre-processed pool. See the documentation for more information prepro_border : int for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['border']` prepro_rgi_version : str for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['rgi_version']` prepro_base_url : str for `from_prepro_level` only: if you want to override the default URL from which to download the gdirs. Default currently is https://cluster.klima.uni-bremen.de/~oggm/gdirs/oggm_v1.1/ use_demo_glaciers : bool whether to check the demo glaciers for download (faster than the standard prepro downloads). The default is to decide whether or not to check based on simple criteria such as glacier list size. from_tar : bool, default=False extract the gdir data from a tar file. If set to `True`, will check for a tar file at the expected location in `base_dir`. delete_tar : bool, default=False delete the original tar file after extraction. delete_tar : bool, default=False delete the original tar file after extraction. Returns ------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the initialised glacier directories Notes ----- This task is deprecated in favor of the more explicit init_glacier_directories. Indeed, init_glacier_directories is very similar to init_glacier_regions, but it does not process the DEMs: a glacier directory is valid also without DEM. """ _check_duplicates(rgidf) if reset and not force: reset = utils.query_yes_no('Delete all glacier directories?') if prepro_border is None: prepro_border = int(cfg.PARAMS['border']) if from_prepro_level and prepro_border not in [10, 80, 160, 250]: if 'test' not in utils._downloads.GDIR_URL: raise InvalidParamsError("prepro_border or cfg.PARAMS['border'] " "should be one of: 10, 80, 160, 250.") # if reset delete also the log directory if reset: fpath = os.path.join(cfg.PATHS['working_dir'], 'log') if os.path.exists(fpath): rmtree(fpath) gdirs = [] new_gdirs = [] if rgidf is None: if reset: raise ValueError('Cannot use reset without setting rgidf') log.workflow('init_glacier_regions by parsing available folders ' '(can be slow).') # The dirs should be there already gl_dir = os.path.join(cfg.PATHS['working_dir'], 'per_glacier') for root, _, files in os.walk(gl_dir): if files and ('dem.tif' in files): gdirs.append(oggm.GlacierDirectory(os.path.basename(root))) else: # Check if dataframe or list of strs try: entities = [] for _, entity in rgidf.iterrows(): entities.append(entity) except AttributeError: entities = utils.tolist(rgidf) # Check demo if use_demo_glaciers is None: use_demo_glaciers = len(entities) < 100 if from_prepro_level is not None: log.workflow('init_glacier_regions from prepro level {} on ' '{} glaciers.'.format(from_prepro_level, len(entities))) # Read the hash dictionary before we use multiproc if cfg.PARAMS['dl_verify']: utils.get_dl_verify_data('cluster.klima.uni-bremen.de') gdirs = execute_entity_task(gdir_from_prepro, entities, from_prepro_level=from_prepro_level, prepro_border=prepro_border, prepro_rgi_version=prepro_rgi_version, check_demo_glacier=use_demo_glaciers, base_url=prepro_base_url) else: # We can set the intersects file automatically here if (cfg.PARAMS['use_intersects'] and len(cfg.PARAMS['intersects_gdf']) == 0): rgi_ids = np.unique(np.sort([entity.RGIId for entity in entities])) rgi_version = rgi_ids[0].split('-')[0][-2:] fp = utils.get_rgi_intersects_entities(rgi_ids, version=rgi_version) cfg.set_intersects_db(fp) gdirs = execute_entity_task(utils.GlacierDirectory, entities, reset=reset, from_tar=from_tar, delete_tar=delete_tar) for gdir in gdirs: if not os.path.exists(gdir.get_filepath('dem')): new_gdirs.append(gdir) if len(new_gdirs) > 0: # If not initialized, run the task in parallel execute_entity_task(tasks.define_glacier_region, new_gdirs) return gdirs
def calibrate_inversion_from_consensus_estimate(gdirs, ignore_missing=False): """Fit the total volume of the glaciers to the 2019 consensus estimate. This method finds the "best Glen A" to match all glaciers in gdirs with a valid inverted volume. Parameters ---------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the glacier directories to process ignore_missing : bool set this to true to silence the error if some glaciers could not be found in the consensus estimate. Returns ------- a dataframe with the individual glacier volumes """ gdirs = utils.tolist(gdirs) # Get the ref data for the glaciers we have df = pd.read_hdf(utils.get_demo_file('rgi62_itmix_df.h5')) rids = [gdir.rgi_id for gdir in gdirs] found_ids = df.index.intersection(rids) if not ignore_missing and (len(found_ids) != len(rids)): raise InvalidWorkflowError('Could not find matching indices in the ' 'consensus estimate for all provided ' 'glaciers. Set ignore_missing=True to ' 'ignore this error.') df = df.reindex(rids) def_a = cfg.PARAMS['inversion_glen_a'] a_bounds = [0.1, 10] # Optimize the diff to ref def to_minimize(x): cfg.PARAMS['inversion_glen_a'] = x * def_a vols = execute_entity_task(tasks.mass_conservation_inversion, gdirs) _df = df.copy() _df['oggm'] = vols _df = _df.dropna() return _df.vol_itmix_m3.sum() - _df.oggm.sum() out_fac, r = optimization.brentq(to_minimize, *a_bounds, rtol=1e-2, full_output=True) if r.converged: log.workflow('calibrate_inversion_from_consensus_estimate ' 'converged after {} iterations. The resulting Glen A ' 'factor is {}.'.format(r.iterations, out_fac)) else: raise RuntimeError('Unexpected error') # Compute the final volume with the correct A cfg.PARAMS['inversion_glen_a'] = out_fac * def_a vols = execute_entity_task(tasks.mass_conservation_inversion, gdirs) df['vol_oggm_m3'] = vols return df
def calibrate_inversion_from_consensus(gdirs, ignore_missing=True, fs=0, a_bounds=(0.1, 10), apply_fs_on_mismatch=False, error_on_mismatch=True): """Fit the total volume of the glaciers to the 2019 consensus estimate. This method finds the "best Glen A" to match all glaciers in gdirs with a valid inverted volume. Parameters ---------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the glacier directories to process ignore_missing : bool set this to true to silence the error if some glaciers could not be found in the consensus estimate. fs : float invert with sliding (default: no) a_bounds: tuple factor to apply to default A apply_fs_on_mismatch: false on mismatch, try to apply an arbitrary value of fs (fs = 5.7e-20 from Oerlemans) and try to otpimize A again. error_on_mismatch: bool sometimes the given bounds do not allow to find a zero mismatch: this will normally raise an error, but you can switch this off, use the closest value instead and move on. Returns ------- a dataframe with the individual glacier volumes """ gdirs = utils.tolist(gdirs) # Get the ref data for the glaciers we have df = pd.read_hdf(utils.get_demo_file('rgi62_itmix_df.h5')) rids = [gdir.rgi_id for gdir in gdirs] found_ids = df.index.intersection(rids) if not ignore_missing and (len(found_ids) != len(rids)): raise InvalidWorkflowError('Could not find matching indices in the ' 'consensus estimate for all provided ' 'glaciers. Set ignore_missing=True to ' 'ignore this error.') df = df.reindex(rids) # Optimize the diff to ref def_a = cfg.PARAMS['inversion_glen_a'] def compute_vol(x): inversion_tasks(gdirs, glen_a=x*def_a, fs=fs) odf = df.copy() odf['oggm'] = execute_entity_task(tasks.get_inversion_volume, gdirs) return odf.dropna() def to_minimize(x): log.workflow('Consensus estimate optimisation with ' 'A factor: {} and fs: {}'.format(x, fs)) odf = compute_vol(x) return odf.vol_itmix_m3.sum() - odf.oggm.sum() try: out_fac, r = optimization.brentq(to_minimize, *a_bounds, rtol=1e-2, full_output=True) if r.converged: log.workflow('calibrate_inversion_from_consensus ' 'converged after {} iterations and fs={}. The ' 'resulting Glen A factor is {}.' ''.format(r.iterations, fs, out_fac)) else: raise ValueError('Unexpected error in optimization.brentq') except ValueError: # Ok can't find an A. Log for debug: odf1 = compute_vol(a_bounds[0]).sum() * 1e-9 odf2 = compute_vol(a_bounds[1]).sum() * 1e-9 msg = ('calibration fom consensus estimate CANT converge with fs={}.\n' 'Bound values (km3):\nRef={:.3f} OGGM={:.3f} for A factor {}\n' 'Ref={:.3f} OGGM={:.3f} for A factor {}' ''.format(fs, odf1.vol_itmix_m3, odf1.oggm, a_bounds[0], odf2.vol_itmix_m3, odf2.oggm, a_bounds[1])) if apply_fs_on_mismatch and fs == 0 and odf2.oggm > odf2.vol_itmix_m3: return calibrate_inversion_from_consensus(gdirs, ignore_missing=ignore_missing, fs=5.7e-20, a_bounds=a_bounds, apply_fs_on_mismatch=False, error_on_mismatch=error_on_mismatch) if error_on_mismatch: raise ValueError(msg) out_fac = a_bounds[int(abs(odf1.vol_itmix_m3 - odf1.oggm) > abs(odf2.vol_itmix_m3 - odf2.oggm))] log.workflow(msg) log.workflow('We use A factor = {} and fs = {} and move on.' ''.format(out_fac, fs)) # Compute the final volume with the correct A inversion_tasks(gdirs, glen_a=out_fac*def_a, fs=fs) df['vol_oggm_m3'] = execute_entity_task(tasks.get_inversion_volume, gdirs) return df
def init_glacier_regions(rgidf=None, *, reset=False, force=False, from_prepro_level=None, prepro_border=None, prepro_rgi_version=None, from_tar=False, delete_tar=False, use_demo_glaciers=None): """Initializes the list of Glacier Directories for this run. This is the very first task to do (always). If the directories are already available in the working directory, use them. If not, create new ones. Parameters ---------- rgidf : GeoDataFrame or list of ids, optional for pre-computed runs the RGI glacier outlines. If unavailable, OGGM will parse the information from the glacier directories found in the working directory. It is required for new runs. reset : bool delete the existing glacier directories if found. force : bool setting `reset=True` will trigger a yes/no question to the user. Set `force=True` to avoid this. from_prepro_level : int get the gdir data from the official pre-processed pool. See the documentation for more information prepro_border : int for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['border']` prepro_rgi_version : str for `from_prepro_level` only: if you want to override the default behavior which is to use `cfg.PARAMS['rgi_version']` use_demo_glaciers : bool whether to check the demo glaciers for download (faster than the standard prepro downloads). The default is to decide whether or not to check based on simple crietria such as glacier list size. from_tar : bool, default=False extract the gdir data from a tar file. If set to `True`, will check for a tar file at the expected location in `base_dir`. delete_tar : bool, default=False delete the original tar file after extraction. delete_tar : bool, default=False delete the original tar file after extraction. Returns ------- gdirs : list of :py:class:`oggm.GlacierDirectory` objects the initialised glacier directories """ if reset and not force: reset = utils.query_yes_no('Delete all glacier directories?') if prepro_border is None: prepro_border = int(cfg.PARAMS['border']) if from_prepro_level and prepro_border not in [10, 80, 160, 250]: if 'test' not in utils._downloads.GDIR_URL: raise InvalidParamsError("prepro_border or cfg.PARAMS['border'] " "should be one of: 10, 80, 160, 250.") # if reset delete also the log directory if reset: fpath = os.path.join(cfg.PATHS['working_dir'], 'log') if os.path.exists(fpath): rmtree(fpath) gdirs = [] new_gdirs = [] if rgidf is None: if reset: raise ValueError('Cannot use reset without setting rgidf') log.workflow('init_glacier_regions by parsing available folders ' '(can be slow).') # The dirs should be there already gl_dir = os.path.join(cfg.PATHS['working_dir'], 'per_glacier') for root, _, files in os.walk(gl_dir): if files and ('dem.tif' in files): gdirs.append(oggm.GlacierDirectory(os.path.basename(root))) else: # Check if dataframe or list of strs try: entities = [] for _, entity in rgidf.iterrows(): entities.append(entity) except AttributeError: entities = utils.tolist(rgidf) # Check demo if use_demo_glaciers is None: use_demo_glaciers = len(entities) < 100 if from_prepro_level is not None: log.workflow('init_glacier_regions from prepro level {} on ' '{} glaciers.'.format(from_prepro_level, len(entities))) gdirs = execute_entity_task(gdir_from_prepro, entities, from_prepro_level=from_prepro_level, prepro_border=prepro_border, prepro_rgi_version=prepro_rgi_version, check_demo_glacier=use_demo_glaciers) else: # TODO: if necessary this could use multiprocessing as well for entity in entities: gdir = oggm.GlacierDirectory(entity, reset=reset, from_tar=from_tar, delete_tar=delete_tar) if not os.path.exists(gdir.get_filepath('dem')): new_gdirs.append((gdir, dict(entity=entity))) gdirs.append(gdir) # We can set the intersects file automatically here if (cfg.PARAMS['use_intersects'] and new_gdirs and (len(cfg.PARAMS['intersects_gdf']) == 0)): rgi_ids = np.unique(np.sort([t[0].rgi_id for t in new_gdirs])) rgi_version = new_gdirs[0][0].rgi_version fp = utils.get_rgi_intersects_entities(rgi_ids, version=rgi_version) cfg.set_intersects_db(fp) # If not initialized, run the task in parallel execute_entity_task(tasks.define_glacier_region, new_gdirs) return gdirs