def apparent_mb_from_linear_mb(gdir, mb_gradient=3.): """Compute apparent mb from a linear mass-balance assumption (for testing). This is for testing currently, but could be used as alternative method for the inversion quite easily. Parameters ---------- gdir : oggm.GlacierDirectory """ # Do we have a calving glacier? cmb = calving_mb(gdir) # Get the height and widths along the fls h, w = gdir.get_inversion_flowline_hw() # Now find the ELA till the integrated mb is zero from oggm.core.massbalance import LinearMassBalance def to_minimize(ela_h): mbmod = LinearMassBalance(ela_h[0], grad=mb_gradient) smb = mbmod.get_specific_mb(h, w) return (smb - cmb)**2 ela_h = optimization.minimize(to_minimize, [0.], bounds=((0, 10000), )) ela_h = ela_h['x'][0] mbmod = LinearMassBalance(ela_h, grad=mb_gradient) # For each flowline compute the apparent MB fls = gdir.read_pickle('inversion_flowlines') # Reset flux for fl in fls: fl.flux = np.zeros(len(fl.surface_h)) # Flowlines in order to be sure rho = cfg.PARAMS['ice_density'] for fl in fls: mbz = mbmod.get_annual_mb(fl.surface_h) * cfg.SEC_IN_YEAR * rho fl.set_apparent_mb(mbz) # Check and write aflux = fls[-1].flux[-1] * 1e-9 / rho * gdir.grid.dx**2 # If not marine and a bit far from zero, warning if cmb == 0 and not np.allclose(fls[-1].flux[-1], 0., atol=0.01): log.warning('(%s) flux should be zero, but is: ' '%.4f km3 ice yr-1', gdir.rgi_id, aflux) # If not marine and quite far from zero, error if cmb == 0 and not np.allclose(fls[-1].flux[-1], 0., atol=1): msg = ('({}) flux should be zero, but is: {:.4f} km3 ice yr-1'.format( gdir.rgi_id, aflux)) raise RuntimeError(msg) gdir.write_pickle(fls, 'inversion_flowlines') gdir.write_pickle({ 'ela_h': ela_h, 'grad': mb_gradient }, 'linear_mb_params')
def test_water_massbalance(): mb_mod = LinearMassBalance(ela_h=100, grad=1) to_test = mb_mod.get_annual_mb([200, 100, 0, -1, -100]) # Convert units to_test *= cfg.SEC_IN_YEAR * cfg.PARAMS['ice_density'] # Test assert_allclose(to_test, [100, 0, -100, -101, -200]) mb_mod = WaterMassBalance(ela_h=100, grad=1) to_test = mb_mod.get_annual_mb([200, 100, 0, -1, -100]) # Convert units to_test *= cfg.SEC_IN_YEAR * cfg.PARAMS['ice_density'] # Test assert_allclose(to_test, [100, 0, -100, 0, 0]) mb_mod = WaterMassBalance(ela_h=100, grad=1, underwater_melt=-1000) to_test = mb_mod.get_annual_mb([200, 100, 0, -1, -100]) # Convert units to_test *= cfg.SEC_IN_YEAR * cfg.PARAMS['ice_density'] # Test assert_allclose(to_test, [100, 0, -100, -1000, -1000])
def test_staggered_diagnostics(self): mb = LinearMassBalance(2600.) fls = dummy_constant_bed() model = FluxBasedModel(fls, mb_model=mb, y0=0.) model.run_until(700) assert_allclose(mb.get_specific_mb(fls=fls), 0, atol=10) # Check the flux just for fun fl = model.flux_stag[0] assert fl[0] == 0 # Now check the diags df = model.get_diagnostics() fl = model.fls[0] df['my_flux'] = np.cumsum(mb.get_annual_mb(fl.surface_h) * fl.widths_m * fl.dx_meter * cfg.SEC_IN_YEAR).clip(0) df = df.loc[df['ice_thick'] > 0] # Also convert ours df['ice_flux'] *= cfg.SEC_IN_YEAR df['ice_velocity'] *= cfg.SEC_IN_YEAR df['tributary_flux'] *= cfg.SEC_IN_YEAR assert_allclose(np.abs(df['ice_flux'] - df['my_flux']), 0, atol=35e3) assert df['ice_velocity'].max() > 25 assert df['tributary_flux'].max() == 0 fls = dummy_width_bed_tributary() model = FluxBasedModel(fls, mb_model=mb, y0=0.) model.run_until(500) df = model.get_diagnostics() df['ice_velocity'] *= cfg.SEC_IN_YEAR df['tributary_flux'] *= cfg.SEC_IN_YEAR df = df.loc[df['ice_thick'] > 0] assert df['ice_velocity'].max() > 50 assert df['tributary_flux'].max() > 30e4 df = model.get_diagnostics(fl_id=0) df = df.loc[df['ice_thick'] > 0] df['ice_velocity'] *= cfg.SEC_IN_YEAR df['tributary_flux'] *= cfg.SEC_IN_YEAR assert df['ice_velocity'].max() > 10 assert df['tributary_flux'].max() == 0
def to_minimize(ela_h): mbmod = LinearMassBalance(ela_h[0]) smb = mbmod.get_annual_mb(heights=topo) return np.sum(smb)**2
def gridded_mb_attributes(gdir): """Adds mass-balance related attributes to the gridded data file. 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 """ from oggm.core.massbalance import LinearMassBalance, ConstantMassBalance from oggm.core.centerlines import line_inflows # Get the input data with ncDataset(gdir.get_filepath('gridded_data')) as nc: topo_2d = nc.variables['topo_smoothed'][:] glacier_mask_2d = nc.variables['glacier_mask'][:] glacier_mask_2d = glacier_mask_2d == 1 catchment_mask_2d = glacier_mask_2d * np.NaN cls = gdir.read_pickle('centerlines') # Catchment areas cis = gdir.read_pickle('geometries')['catchment_indices'] for j, ci in enumerate(cis): catchment_mask_2d[tuple(ci.T)] = j # Make everything we need flat catchment_mask = catchment_mask_2d[glacier_mask_2d].astype(int) topo = topo_2d[glacier_mask_2d] # Prepare the distributed mass-balance data rho = cfg.PARAMS['ice_density'] dx2 = gdir.grid.dx ** 2 # Linear def to_minimize(ela_h): mbmod = LinearMassBalance(ela_h[0]) smb = mbmod.get_annual_mb(heights=topo) return np.sum(smb)**2 ela_h = optimization.minimize(to_minimize, [0.], method='Powell') mbmod = LinearMassBalance(float(ela_h['x'])) lin_mb_on_z = mbmod.get_annual_mb(heights=topo) * cfg.SEC_IN_YEAR * rho if not np.isclose(np.sum(lin_mb_on_z), 0, atol=10): raise RuntimeError('Spec mass-balance should be zero but is: {}' .format(np.sum(lin_mb_on_z))) # Normal OGGM (a bit tweaked) df = gdir.read_json('local_mustar') def to_minimize(mu_star): mbmod = ConstantMassBalance(gdir, mu_star=mu_star, bias=0, check_calib_params=False, y0=df['t_star']) smb = mbmod.get_annual_mb(heights=topo) return np.sum(smb)**2 mu_star = optimization.minimize(to_minimize, [0.], method='Powell') mbmod = ConstantMassBalance(gdir, mu_star=float(mu_star['x']), bias=0, check_calib_params=False, y0=df['t_star']) oggm_mb_on_z = mbmod.get_annual_mb(heights=topo) * cfg.SEC_IN_YEAR * rho if not np.isclose(np.sum(oggm_mb_on_z), 0, atol=10): raise RuntimeError('Spec mass-balance should be zero but is: {}' .format(np.sum(oggm_mb_on_z))) # Altitude based mass balance catch_area_above_z = topo * np.NaN lin_mb_above_z = topo * np.NaN oggm_mb_above_z = topo * np.NaN for i, h in enumerate(topo): catch_area_above_z[i] = np.sum(topo >= h) * dx2 lin_mb_above_z[i] = np.sum(lin_mb_on_z[topo >= h]) * dx2 oggm_mb_above_z[i] = np.sum(oggm_mb_on_z[topo >= h]) * dx2 # Hardest part - MB per catchment catchment_area = topo * np.NaN lin_mb_above_z_on_catch = topo * np.NaN oggm_mb_above_z_on_catch = topo * np.NaN # First, find all inflows indices and min altitude per catchment inflows = [] lowest_h = [] for i, cl in enumerate(cls): lowest_h.append(np.min(topo[catchment_mask == i])) inflows.append([cls.index(l) for l in line_inflows(cl, keep=False)]) for i, (catch_id, h) in enumerate(zip(catchment_mask, topo)): if h == np.min(topo): t = 1 # Find the catchment area of the point itself by eliminating points # below the point altitude. We assume we keep all of them first, # then remove those we don't want sel_catchs = inflows[catch_id].copy() for catch in inflows[catch_id]: if h >= lowest_h[catch]: for cc in np.append(inflows[catch], catch): try: sel_catchs.remove(cc) except ValueError: pass # At the very least we need or own catchment sel_catchs.append(catch_id) # Then select all the catchment points sel_points = np.isin(catchment_mask, sel_catchs) # And keep the ones above our altitude sel_points = sel_points & (topo >= h) # Compute lin_mb_above_z_on_catch[i] = np.sum(lin_mb_on_z[sel_points]) * dx2 oggm_mb_above_z_on_catch[i] = np.sum(oggm_mb_on_z[sel_points]) * dx2 catchment_area[i] = np.sum(sel_points) * dx2 # Make 2D again def _fill_2d_like(data): out = topo_2d * np.NaN out[glacier_mask_2d] = data return out catchment_area = _fill_2d_like(catchment_area) catch_area_above_z = _fill_2d_like(catch_area_above_z) lin_mb_above_z = _fill_2d_like(lin_mb_above_z) oggm_mb_above_z = _fill_2d_like(oggm_mb_above_z) lin_mb_above_z_on_catch = _fill_2d_like(lin_mb_above_z_on_catch) oggm_mb_above_z_on_catch = _fill_2d_like(oggm_mb_above_z_on_catch) # Save to file with ncDataset(gdir.get_filepath('gridded_data'), 'a') as nc: vn = 'catchment_area' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'm^2' v.long_name = 'Catchment area above point' v.description = ('This is a very crude method: just the area above ' 'the points elevation on glacier.') v[:] = catch_area_above_z vn = 'catchment_area_on_catch' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x',)) v.units = 'm^2' v.long_name = 'Catchment area above point on flowline catchments' v.description = ('Uses the catchments masks of the flowlines to ' 'compute the area above the altitude of the given ' 'point.') v[:] = catchment_area vn = 'lin_mb_above_z' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'kg/year' v.long_name = 'MB above point from linear MB model, without catchments' v.description = ('Mass-balance cumulated above the altitude of the' 'point, hence in unit of flux. Note that it is ' 'a coarse approximation of the real flux. ' 'The mass-balance model is a simple linear function' 'of altitude.') v[:] = lin_mb_above_z vn = 'lin_mb_above_z_on_catch' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'kg/year' v.long_name = 'MB above point from linear MB model, with catchments' v.description = ('Mass-balance cumulated above the altitude of the' 'point in a flowline catchment, hence in unit of ' 'flux. Note that it is a coarse approximation of the ' 'real flux. The mass-balance model is a simple ' 'linear function of altitude.') v[:] = lin_mb_above_z_on_catch vn = 'oggm_mb_above_z' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'kg/year' v.long_name = 'MB above point from OGGM MB model, without catchments' v.description = ('Mass-balance cumulated above the altitude of the' 'point, hence in unit of flux. Note that it is ' 'a coarse approximation of the real flux. ' 'The mass-balance model is a calibrated temperature ' 'index model like OGGM.') v[:] = oggm_mb_above_z vn = 'oggm_mb_above_z_on_catch' if vn in nc.variables: v = nc.variables[vn] else: v = nc.createVariable(vn, 'f4', ('y', 'x', )) v.units = 'kg/year' v.long_name = 'MB above point from OGGM MB model, with catchments' v.description = ('Mass-balance cumulated above the altitude of the' 'point in a flowline catchment, hence in unit of ' 'flux. Note that it is a coarse approximation of the ' 'real flux. The mass-balance model is a calibrated ' 'temperature index model like OGGM.') v[:] = oggm_mb_above_z_on_catch