def step(self, dt): """Advance one step.""" div_q, dt_cfl = self.diffusion_upstream_2d() dt_use = utils.clip_scalar(np.min([dt_cfl, dt]), 0, self.max_dt) self.ice_thick = utils.clip_min( self.surface_h + (self.get_mb() + div_q) * dt_use - self.bed_topo, 0) # Next step self.t += dt_use return dt
def step(self, dt): """Advance one step.""" # Just a check to avoid useless computations if dt <= 0: raise InvalidParamsError('dt needs to be strictly positive') # Guarantee a precise arrival on a specific date if asked min_dt = dt if dt < self.min_dt else self.min_dt dt = utils.clip_scalar(dt, min_dt, self.max_dt) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=RuntimeWarning) fl = self.fls[0] dx = fl.dx_meter # Switch to the notation from the MUSCL_1D example # This is useful to ensure that the MUSCL-SuperBee code # is working as it has been benchmarked many times # mass balance m_dot = self.get_mb(fl.surface_h, self.yr, fl_id=id(fl)) # get in the surface elevation S = fl.surface_h # get the bed B = fl.bed_h # define Glen's law here N = self.glen_n # this is the correct Gamma !! Gamma = 2. * self.glen_a * (self.rho * G)**N / (N + 2.) # time stepping c_stab = 0.165 # define the finite difference indices k = np.arange(0, fl.nx) kp = np.hstack([np.arange(1, fl.nx), fl.nx - 1]) kpp = np.hstack([np.arange(2, fl.nx), fl.nx - 1, fl.nx - 1]) km = np.hstack([0, np.arange(0, fl.nx - 1)]) kmm = np.hstack([0, 0, np.arange(0, fl.nx - 2)]) # I'm gonna introduce another level of adaptive time stepping here, # which is probably not necessary. However I keep it to be # consistent with my benchmarked and tested code. # If the OGGM time stepping is correctly working, this loop # should never run more than once # Fabi: actually no, it is your job to choose the right time step # but you can let OGGM decide whether a new time step is needed # or not -> to be meliorated one day stab_t = 0. while stab_t < dt: H = S - B # MUSCL scheme up. "up" denotes here the k+1/2 flux boundary r_up_m = (H[k] - H[km]) / (H[kp] - H[k]) # Eq. 27 H_up_m = H[k] + 0.5 * self.phi(r_up_m) * (H[kp] - H[k] ) # Eq. 23 # Eq. 27, k+1 is used instead of k r_up_p = (H[kp] - H[k]) / (H[kpp] - H[kp]) # Eq. 24 H_up_p = H[kp] - 0.5 * self.phi(r_up_p) * (H[kpp] - H[kp]) # surface slope gradient s_grad_up = ((S[kp] - S[k])**2. / dx**2.)**((N - 1.) / 2.) # like Eq. 30, now using Eq. 23 instead of Eq. 24 D_up_m = Gamma * H_up_m**(N + 2.) * s_grad_up D_up_p = Gamma * H_up_p**(N + 2.) * s_grad_up # Eq. 30 # Eq. 31 D_up_min = np.minimum(D_up_m, D_up_p) # Eq. 32 D_up_max = np.maximum(D_up_m, D_up_p) D_up = np.zeros(fl.nx) # Eq. 33 cond = (S[kp] <= S[k]) & (H_up_m <= H_up_p) D_up[cond] = D_up_min[cond] cond = (S[kp] <= S[k]) & (H_up_m > H_up_p) D_up[cond] = D_up_max[cond] cond = (S[kp] > S[k]) & (H_up_m <= H_up_p) D_up[cond] = D_up_max[cond] cond = (S[kp] > S[k]) & (H_up_m > H_up_p) D_up[cond] = D_up_min[cond] # MUSCL scheme down. "down" denotes the k-1/2 flux boundary r_dn_m = (H[km] - H[kmm]) / (H[k] - H[km]) H_dn_m = H[km] + 0.5 * self.phi(r_dn_m) * (H[k] - H[km]) r_dn_p = (H[k] - H[km]) / (H[kp] - H[k]) H_dn_p = H[k] - 0.5 * self.phi(r_dn_p) * (H[kp] - H[k]) # calculate the slope gradient s_grad_dn = ((S[k] - S[km])**2. / dx**2.)**((N - 1.) / 2.) D_dn_m = Gamma * H_dn_m**(N + 2.) * s_grad_dn D_dn_p = Gamma * H_dn_p**(N + 2.) * s_grad_dn D_dn_min = np.minimum(D_dn_m, D_dn_p) D_dn_max = np.maximum(D_dn_m, D_dn_p) D_dn = np.zeros(fl.nx) cond = (S[k] <= S[km]) & (H_dn_m <= H_dn_p) D_dn[cond] = D_dn_min[cond] cond = (S[k] <= S[km]) & (H_dn_m > H_dn_p) D_dn[cond] = D_dn_max[cond] cond = (S[k] > S[km]) & (H_dn_m <= H_dn_p) D_dn[cond] = D_dn_max[cond] cond = (S[k] > S[km]) & (H_dn_m > H_dn_p) D_dn[cond] = D_dn_min[cond] # Eq. 37 dt_stab = c_stab * dx**2. / max(max(abs(D_up)), max(abs(D_dn))) dt_use = min(dt_stab, dt - stab_t) stab_t = stab_t + dt_use # explicit time stepping scheme, Eq. 36 div_q = (D_up * (S[kp] - S[k]) / dx - D_dn * (S[k] - S[km]) / dx) / dx # Eq. 35 S = S[k] + (m_dot + div_q) * dt_use # Eq. 7 S = np.maximum(S, B) # Done with the loop, prepare output fl.thick = S - B # Next step self.t += dt
def calibrate_calving_k_single_wconsensus(gdir, calving_data_fn=pygem_prms.calving_data_fullfn): """Calibrate calving parameter k with frontal ablation data and export to the given glacier directory Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data """ assert os.path.exists(calving_data_fn), 'Error: ' + calving_data_fn + ' does not exist.' #%% # Load calving data calving_data = pd.read_csv(pygem_prms.calving_data_fullfn) if gdir.rgi_id in list(calving_data.RGIId): # Load glacier data gidx = np.where(gdir.rgi_id == calving_data.RGIId)[0][0] calving_flux_obs_gta = calving_data.loc[gidx,'frontal_ablation_Gta'] calving_flux_obs_km3a = calving_flux_obs_gta * pygem_prms.density_water / pygem_prms.density_ice fls = gdir.read_pickle('inversion_flowlines') # Ice thickness at calving front from consensus ice thickness h_calving = fls[-1].consensus_h[-1] # Water level (max based on 50 m freeboard) th = fls[-1].surface_h[-1] vmin, vmax = cfg.PARAMS['free_board_marine_terminating'] # Constrain water level such that the freeboard is somewhere between 10 and 50 m water_level = clip_scalar(0, th - vmax, th - vmin) # ----- Optimize calving_k ------ def objective(calving_k_obj): """ Objective function for calving data (mimize difference between model and observation). Parameters ---------- calving_k : calving parameter """ # Calving flux (km3 yr-1; positive value refers to amount of calving, need to subtract from mb) calving_output = calving_flux_from_depth(gdir, water_level=water_level, thick=h_calving, k=calving_k_obj) calving_flux_km3a = calving_output['flux'] # Difference with observation (km3 yr-1) calving_flux_dif_abs = abs(calving_flux_km3a - calving_flux_obs_km3a) return calving_flux_dif_abs # Run objective calving_k_init = 1 calving_k_bnds = ((0,50),) calving_k_opt_all = minimize(objective, calving_k_init, method=pygem_prms.method_opt, bounds=calving_k_bnds, # options={'ftol':ftol_opt, 'eps':eps_opt} ) # Record the optimized parameters calving_k_opt = calving_k_opt_all.x[0] calving_k = calving_k_opt # Calving flux (km3 yr-1; positive value refers to amount of calving, need to subtract from mb) calving_output = calving_flux_from_depth(gdir, water_level=water_level, thick=h_calving, k=calving_k) calving_flux = calving_output['flux'] print('\n', gdir.rgi_id) print(' calving_k:', np.round(calving_k_opt,2)) print(' calving flux (km3 yr-1):', np.round(calving_flux,3)) print(' calving flux obs (km3 yr-1):', np.round(calving_flux_obs_km3a,3)) print(' freeboard:', np.round(calving_output['free_board'],1)) print(' water_level:', np.round(calving_output['water_level'],1)) print(' h_calving (m):', np.round(h_calving,1)) print(' w_calving (m):', np.round(fls[-1].widths[-1] * gdir.grid.dx, 1),'\n') # Pickle data output_fn = gdir.get_filepath('calving_k_opt') with open(output_fn, 'wb') as f: pickle.dump(calving_k_opt, f)
def find_inversion_calving(gdir, water_level=None, fixed_water_depth=None): """Optimized search for a calving flux compatible with the bed inversion. See Recinos et al 2019 for details. Parameters ---------- water_level : float the water level. It should be zero m a.s.l, but: - sometimes the frontal elevation is unrealistically high (or low). - lake terminating glaciers - other uncertainties With this parameter, you can produce more realistic values. The default is to infer the water level from PARAMS['free_board_lake_terminating'] and PARAMS['free_board_marine_terminating'] fixed_water_depth : float fix the water depth to an observed value and let the free board vary instead. """ from oggm.core import climate from oggm.exceptions import MassBalanceCalibrationError if not gdir.is_tidewater or not cfg.PARAMS['use_kcalving_for_inversion']: # Do nothing return # Let's start from a fresh state gdir.inversion_calving_rate = 0 with utils.DisableLogger(): climate.local_t_star(gdir) climate.mu_star_calibration(gdir) prepare_for_inversion(gdir) v_ref = mass_conservation_inversion(gdir, water_level=water_level) # Store for statistics gdir.add_to_diagnostics('volume_before_calving', v_ref) # Get the relevant variables cls = gdir.read_pickle('inversion_input')[-1] slope = cls['slope_angle'][-1] width = cls['width'][-1] # Check that water level is within given bounds if water_level is None: th = cls['hgt'][-1] if gdir.is_lake_terminating: water_level = th - cfg.PARAMS['free_board_lake_terminating'] else: vmin, vmax = cfg.PARAMS['free_board_marine_terminating'] water_level = utils.clip_scalar(0, th - vmax, th - vmin) # The functions all have the same shape: they decrease, then increase # We seek the absolute minimum first def to_minimize(h): if fixed_water_depth is not None: fl = calving_flux_from_depth(gdir, thick=h, water_level=water_level, water_depth=fixed_water_depth, fixed_water_depth=True) else: fl = calving_flux_from_depth(gdir, water_level=water_level, water_depth=h) flux = fl['flux'] * 1e9 / cfg.SEC_IN_YEAR sia_thick = sia_thickness(slope, width, flux) return fl['thick'] - sia_thick abs_min = optimize.minimize(to_minimize, [1], bounds=((1e-4, 1e4), ), tol=1e-1) if not abs_min['success']: raise RuntimeError('Could not find the absolute minimum in calving ' 'flux optimization: {}'.format(abs_min)) if abs_min['fun'] > 0: # This happens, and means that this glacier simply can't calve # This is an indicator for physics not matching, often a unrealistic # slope of free-board df = gdir.read_json('local_mustar') out = calving_flux_from_depth(gdir, water_level=water_level) odf = dict() odf['calving_flux'] = 0 odf['calving_mu_star'] = df['mu_star_glacierwide'] odf['calving_law_flux'] = out['flux'] odf['calving_water_level'] = out['water_level'] odf['calving_inversion_k'] = out['inversion_calving_k'] odf['calving_front_slope'] = slope odf['calving_front_water_depth'] = out['water_depth'] odf['calving_front_free_board'] = out['free_board'] odf['calving_front_thick'] = out['thick'] odf['calving_front_width'] = out['width'] for k, v in odf.items(): gdir.add_to_diagnostics(k, v) return # OK, we now find the zero between abs min and an arbitrary high front abs_min = abs_min['x'][0] opt = optimize.brentq(to_minimize, abs_min, 1e4) # This is the thick guaranteeing OGGM Flux = Calving Law Flux # Let's see if it results in a meaningful mu_star # Give the flux to the inversion and recompute if fixed_water_depth is not None: out = calving_flux_from_depth(gdir, water_level=water_level, thick=opt, water_depth=fixed_water_depth, fixed_water_depth=True) f_calving = out['flux'] else: out = calving_flux_from_depth(gdir, water_level=water_level, water_depth=opt) f_calving = out['flux'] gdir.inversion_calving_rate = f_calving with utils.DisableLogger(): # We accept values down to zero before stopping cfg.PARAMS['min_mu_star'] = 0 cfg.PARAMS['clip_mu_star'] = False # At this step we might raise a MassBalanceCalibrationError try: climate.local_t_star(gdir) df = gdir.read_json('local_mustar') except MassBalanceCalibrationError as e: assert 'mu* out of specified bounds' in str(e) # When this happens we clip mu* to zero cfg.PARAMS['clip_mu_star'] = True climate.local_t_star(gdir) df = gdir.read_json('local_mustar') climate.mu_star_calibration(gdir) prepare_for_inversion(gdir) mass_conservation_inversion(gdir, water_level=water_level) if fixed_water_depth is not None: out = calving_flux_from_depth(gdir, water_level=water_level, water_depth=fixed_water_depth, fixed_water_depth=True) else: out = calving_flux_from_depth(gdir, water_level=water_level) fl = gdir.read_pickle('inversion_flowlines')[-1] f_calving = (fl.flux[-1] * (gdir.grid.dx**2) * 1e-9 / cfg.PARAMS['ice_density']) # Store results odf = dict() odf['calving_flux'] = f_calving odf['calving_mu_star'] = df['mu_star_glacierwide'] odf['calving_law_flux'] = out['flux'] odf['calving_water_level'] = out['water_level'] odf['calving_inversion_k'] = out['inversion_calving_k'] odf['calving_front_slope'] = slope odf['calving_front_water_depth'] = out['water_depth'] odf['calving_front_free_board'] = out['free_board'] odf['calving_front_thick'] = out['thick'] odf['calving_front_width'] = out['width'] for k, v in odf.items(): gdir.add_to_diagnostics(k, v) # Restore defaults with utils.DisableLogger(): cfg.PARAMS['min_mu_star'] = 1. cfg.PARAMS['clip_mu_star'] = False return odf
def gridded_attributes(gdir): """Adds attributes to the gridded file, useful for thickness interpolation. This could be useful for distributed ice thickness models. The raster data are added to the gridded_data file. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data """ # Variables grids_file = gdir.get_filepath('gridded_data') with ncDataset(grids_file) as nc: topo_smoothed = nc.variables['topo_smoothed'][:] glacier_mask = nc.variables['glacier_mask'][:] # Glacier exterior including nunataks erode = binary_erosion(glacier_mask) glacier_ext = glacier_mask ^ erode glacier_ext = np.where(glacier_mask == 1, glacier_ext, 0) # Intersects between glaciers gdfi = gpd.GeoDataFrame(columns=['geometry']) if gdir.has_file('intersects'): # read and transform to grid gdf = gdir.read_shapefile('intersects') salem.transform_geopandas(gdf, gdir.grid, inplace=True) gdfi = pd.concat([gdfi, gdf[['geometry']]]) # Ice divide mask # Probably not the fastest way to do this, but it works dist = np.array([]) jj, ii = np.where(glacier_ext) for j, i in zip(jj, ii): dist = np.append(dist, np.min(gdfi.distance(shpg.Point(i, j)))) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=RuntimeWarning) pok = np.where(dist <= 1) glacier_ext_intersect = glacier_ext * 0 glacier_ext_intersect[jj[pok], ii[pok]] = 1 # Distance from border mask - Scipy does the job dx = gdir.grid.dx dis_from_border = 1 + glacier_ext_intersect - glacier_ext dis_from_border = distance_transform_edt(dis_from_border) * dx # Slope glen_n = cfg.PARAMS['glen_n'] sy, sx = np.gradient(topo_smoothed, dx, dx) slope = np.arctan(np.sqrt(sy**2 + sx**2)) slope_factor = utils.clip_scalar(slope, np.deg2rad(cfg.PARAMS['min_slope']*4), np.pi/2) slope_factor = 1 / slope_factor**(glen_n / (glen_n+2)) aspect = np.arctan2(-sx, sy) aspect[aspect < 0] += 2 * np.pi with ncDataset(grids_file, 'a') as nc: vn = 'glacier_ext_erosion' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'i1', ('y', 'x', )) v.units = '-' v.long_name = 'Glacier exterior with binary erosion method' v[:] = glacier_ext vn = 'ice_divides' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'i1', ('y', 'x', )) v.units = '-' v.long_name = 'Glacier ice divides' v[:] = glacier_ext_intersect vn = 'slope' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'rad' v.long_name = 'Local slope based on smoothed topography' v[:] = slope vn = 'aspect' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'rad' v.long_name = 'Local aspect based on smoothed topography' v[:] = aspect vn = 'slope_factor' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = '-' v.long_name = 'Slope factor as defined in Farinotti et al 2009' v[:] = slope_factor vn = 'dis_from_border' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'm' v.long_name = 'Distance from glacier boundaries' v[:] = dis_from_border
def define_glacier_region(gdir, entity=None): """Very first task: define the glacier's local grid. Defines the local projection (Transverse Mercator), centered on the glacier. There is some options to set the resolution of the local grid. It can be adapted depending on the size of the glacier with:: dx (m) = d1 * AREA (km) + d2 ; clipped to dmax or be set to a fixed value. See ``params.cfg`` for setting these options. Default values of the adapted mode lead to a resolution of 50 m for Hintereisferner, which is approx. 8 km2 large. After defining the grid, the topography and the outlines of the glacier are transformed into the local projection. The default interpolation for the topography is `cubic`. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data entity : geopandas.GeoSeries the glacier geometry to process """ # Make a local glacier map proj_params = dict(name='tmerc', lat_0=0., lon_0=gdir.cenlon, k=0.9996, x_0=0, y_0=0, datum='WGS84') proj4_str = "+proj={name} +lat_0={lat_0} +lon_0={lon_0} +k={k} " \ "+x_0={x_0} +y_0={y_0} +datum={datum}".format(**proj_params) proj_in = pyproj.Proj("+init=EPSG:4326", preserve_units=True) proj_out = pyproj.Proj(proj4_str, preserve_units=True) project = partial(transform_proj, proj_in, proj_out) # transform geometry to map geometry = shapely.ops.transform(project, entity['geometry']) geometry = multi_to_poly(geometry, gdir=gdir) xx, yy = geometry.exterior.xy # Save transformed geometry to disk entity = entity.copy() entity['geometry'] = geometry # Avoid fiona bug: https://github.com/Toblerity/Fiona/issues/365 for k, s in entity.iteritems(): if type(s) in [np.int32, np.int64]: entity[k] = int(s) towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str # Delete the source before writing if 'DEM_SOURCE' in towrite: del towrite['DEM_SOURCE'] # Define glacier area to use area = entity['Area'] # Do we want to use the RGI area or ours? if not cfg.PARAMS['use_rgi_area']: # Update Area area = geometry.area * 1e-6 entity['Area'] = area towrite['Area'] = area # Write shapefile gdir.write_shapefile(towrite, 'outlines') # Also transform the intersects if necessary gdf = cfg.PARAMS['intersects_gdf'] if len(gdf) > 0: gdf = gdf.loc[((gdf.RGIId_1 == gdir.rgi_id) | (gdf.RGIId_2 == gdir.rgi_id))] if len(gdf) > 0: gdf = salem.transform_geopandas(gdf, to_crs=proj_out) if hasattr(gdf.crs, 'srs'): # salem uses pyproj gdf.crs = gdf.crs.srs gdir.write_shapefile(gdf, 'intersects') else: # Sanity check if cfg.PARAMS['use_intersects']: raise InvalidParamsError('You seem to have forgotten to set the ' 'intersects file for this run. OGGM ' 'works better with such a file. If you ' 'know what your are doing, set ' "cfg.PARAMS['use_intersects'] = False to " "suppress this error.") # 6. choose a spatial resolution with respect to the glacier area dxmethod = cfg.PARAMS['grid_dx_method'] if dxmethod == 'linear': dx = np.rint(cfg.PARAMS['d1'] * area + cfg.PARAMS['d2']) elif dxmethod == 'square': dx = np.rint(cfg.PARAMS['d1'] * np.sqrt(area) + cfg.PARAMS['d2']) elif dxmethod == 'fixed': dx = np.rint(cfg.PARAMS['fixed_dx']) else: raise InvalidParamsError('grid_dx_method not supported: {}' .format(dxmethod)) # Additional trick for varying dx if dxmethod in ['linear', 'square']: dx = utils.clip_scalar(dx, cfg.PARAMS['d2'], cfg.PARAMS['dmax']) log.debug('(%s) area %.2f km, dx=%.1f', gdir.rgi_id, area, dx) # Safety check border = cfg.PARAMS['border'] if border > 1000: raise InvalidParamsError("You have set a cfg.PARAMS['border'] value " "of {}. ".format(cfg.PARAMS['border']) + 'This a very large value, which is ' 'currently not supported in OGGM.') # For tidewater glaciers we force border to 10 if gdir.is_tidewater and cfg.PARAMS['clip_tidewater_border']: border = 10 # Corners, incl. a buffer of N pix ulx = np.min(xx) - border * dx lrx = np.max(xx) + border * dx uly = np.max(yy) + border * dx lry = np.min(yy) - border * dx # n pixels nx = np.int((lrx - ulx) / dx) ny = np.int((uly - lry) / dx) # Back to lon, lat for DEM download/preparation tmp_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), x0y0=(ulx, uly), dxdy=(dx, -dx), pixel_ref='corner') minlon, maxlon, minlat, maxlat = tmp_grid.extent_in_crs(crs=salem.wgs84) # Open DEM source = entity.DEM_SOURCE if hasattr(entity, 'DEM_SOURCE') else None if not is_dem_source_available(source, (minlon, maxlon), (minlat, maxlat)): raise InvalidDEMError('Source: {} not available for glacier {}' .format(source, gdir.rgi_id)) dem_list, dem_source = get_topo_file((minlon, maxlon), (minlat, maxlat), rgi_region=gdir.rgi_region, rgi_subregion=gdir.rgi_subregion, dx_meter=dx, source=source) log.debug('(%s) DEM source: %s', gdir.rgi_id, dem_source) log.debug('(%s) N DEM Files: %s', gdir.rgi_id, len(dem_list)) # Decide how to tag nodata def _get_nodata(rio_ds): nodata = rio_ds[0].meta.get('nodata', None) if nodata is None: # badly tagged geotiffs, let's do it ourselves nodata = -32767 if source == 'TANDEM' else -9999 return nodata # A glacier area can cover more than one tile: if len(dem_list) == 1: dem_dss = [rasterio.open(dem_list[0])] # if one tile, just open it dem_data = rasterio.band(dem_dss[0], 1) if LooseVersion(rasterio.__version__) >= LooseVersion('1.0'): src_transform = dem_dss[0].transform else: src_transform = dem_dss[0].affine nodata = _get_nodata(dem_dss) else: dem_dss = [rasterio.open(s) for s in dem_list] # list of rasters nodata = _get_nodata(dem_dss) dem_data, src_transform = merge_tool(dem_dss, nodata=nodata) # merge # Use Grid properties to create a transform (see rasterio cookbook) dst_transform = rasterio.transform.from_origin( ulx, uly, dx, dx # sign change (2nd dx) is done by rasterio.transform ) # Set up profile for writing output profile = dem_dss[0].profile profile.update({ 'crs': proj4_str, 'transform': dst_transform, 'nodata': nodata, 'width': nx, 'height': ny }) # Could be extended so that the cfg file takes all Resampling.* methods if cfg.PARAMS['topo_interp'] == 'bilinear': resampling = Resampling.bilinear elif cfg.PARAMS['topo_interp'] == 'cubic': resampling = Resampling.cubic else: raise InvalidParamsError('{} interpolation not understood' .format(cfg.PARAMS['topo_interp'])) dem_reproj = gdir.get_filepath('dem') profile.pop('blockxsize', None) profile.pop('blockysize', None) profile.pop('compress', None) with rasterio.open(dem_reproj, 'w', **profile) as dest: dst_array = np.empty((ny, nx), dtype=dem_dss[0].dtypes[0]) reproject( # Source parameters source=dem_data, src_crs=dem_dss[0].crs, src_transform=src_transform, src_nodata=nodata, # Destination parameters destination=dst_array, dst_transform=dst_transform, dst_crs=proj4_str, dst_nodata=nodata, # Configuration resampling=resampling) dest.write(dst_array, 1) for dem_ds in dem_dss: dem_ds.close() # Glacier grid x0y0 = (ulx+dx/2, uly-dx/2) # To pixel center coordinates glacier_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), dxdy=(dx, -dx), x0y0=x0y0) glacier_grid.to_json(gdir.get_filepath('glacier_grid')) # Write DEM source info gdir.add_to_diagnostics('dem_source', dem_source) source_txt = DEM_SOURCE_INFO.get(dem_source, dem_source) with open(gdir.get_filepath('dem_source'), 'w') as fw: fw.write(source_txt) fw.write('\n\n') fw.write('# Data files\n\n') for fname in dem_list: fw.write('{}\n'.format(os.path.basename(fname)))