def test_min_max_elevation(self): """Test the helper method which computes the minimal and maximal glacier surface elevation in meters asl, from the given DEM and glacier outline. """ # read the Hintereisferner DEM hef_file = get_demo_file('Hintereisferner_RGI6.shp') entity = gpd.read_file(hef_file).iloc[0] # initialize the GlacierDirectory gdir = oggm.GlacierDirectory(entity, base_dir=self.testdir) # define the local grid and glacier mask gis.define_glacier_region(gdir, entity=entity) gis.glacier_masks(gdir) # set targets from RGI min_target = 2430.0 max_target = 3674.0 # get values from method min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # test with one percentage relative tolerance np.testing.assert_allclose(min_hgt, min_target, rtol=1e-2) np.testing.assert_allclose(max_hgt, max_target, rtol=1e-2)
def test_monthly_specific_mb(self): """Test the monthly specific mass balance against the corresponding yearly mass balance. """ # run all needed prepro tasks gdir = self._setup_mb_test() # instance mb models vas_mbmod = vascaling.VAScalingMassBalance(gdir) # get relevant glacier surface elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # get all month of that year in the # floating (hydrological) year convention year = 1803 months = np.linspace(year, year + 1, num=12, endpoint=False) # compute monthly specific mass balance for # all month of given year and store in array spec_mb_month = np.empty(months.size) for i, month in enumerate(months): spec_mb_month[i] = vas_mbmod.get_monthly_specific_mb( min_hgt, max_hgt, month) # compute yearly specific mass balance spec_mb_year = vas_mbmod.get_specific_mb(min_hgt, max_hgt, year) # compare np.testing.assert_allclose(spec_mb_month.sum(), spec_mb_year, rtol=1e-3)
def _set_up_VAS_model(self): """Avoiding a chunk of code duplicate. Set's up a running volume/area scaling model, including all needed prepo tasks. """ # read the Hintereisferner DEM hef_file = get_demo_file('Hintereisferner_RGI6.shp') entity = gpd.read_file(hef_file).iloc[0] # initialize the GlacierDirectory gdir = oggm.GlacierDirectory(entity, base_dir=self.testdir) # define the local grid and glacier mask gis.define_glacier_region(gdir, entity=entity) gis.glacier_masks(gdir) # process the given climate file climate.process_custom_climate_data(gdir) # run center line preprocessing tasks centerlines.compute_centerlines(gdir) centerlines.initialize_flowlines(gdir) centerlines.catchment_area(gdir) centerlines.catchment_intersections(gdir) centerlines.catchment_width_geom(gdir) centerlines.catchment_width_correction(gdir) # read reference glacier mass balance data mbdf = gdir.get_ref_mb_data() # compute the reference t* for the glacier # given the reference of mass balance measurements res = climate.t_star_from_refmb(gdir, mbdf=mbdf['ANNUAL_BALANCE']) t_star, bias = res['t_star'], res['bias'] # -------------------- # MASS BALANCE TASKS # -------------------- # compute local t* and the corresponding mu* vascaling.local_t_star(gdir, tstar=t_star, bias=bias) # instance the mass balance models mbmod = vascaling.VAScalingMassBalance(gdir) # ---------------- # DYNAMICAL PART # ---------------- # get reference area a0 = gdir.rgi_area_m2 # get reference year y0 = gdir.get_climate_info()['baseline_hydro_yr_0'] # get min and max glacier surface elevation h0, h1 = vascaling.get_min_max_elevation(gdir) model = vascaling.VAScalingModel(year_0=y0, area_m2_0=a0, min_hgt=h0, max_hgt=h1, mb_model=mbmod) return gdir, model
def test_specific_mb(self): """Compare the specific mass balance to the one computed using the OGGM function of the PastMassBalance model. """ # run all needed prepro tasks gdir = self._setup_mb_test() # instance mb models vas_mbmod = vascaling.VAScalingMassBalance(gdir) past_mbmod = massbalance.PastMassBalance(gdir) # get relevant glacier surface elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # define temporal range ys = 1802 ye = 2003 years = np.arange(ys, ye + 1) # get flow lines fls = gdir.read_pickle('inversion_flowlines') # create empty container past_mb = np.empty(years.size) vas_mb = np.empty(years.size) # get specific mass balance for all years for i, year in enumerate(years): past_mb[i] = past_mbmod.get_specific_mb(fls=fls, year=year) vas_mb[i] = vas_mbmod.get_specific_mb(min_hgt, max_hgt, year) # compute and check correlation assert corrcoef(past_mb, vas_mb) >= 0.94 # relative error of average spec mb assert np.abs(rel_err(past_mb.mean(), vas_mb.mean())) <= 0.38 # check correlation of positive and negative mb years assert corrcoef(np.sign(past_mb), np.sign(vas_mb)) >= 0.72 # compare to reference mb measurements mbs = gdir.get_ref_mb_data()['ANNUAL_BALANCE'] assert corrcoef(vas_mb[np.in1d(years, mbs.index)], mbs) >= 0.79
def test_annual_climate(self): """Test my routine against the corresponding OGGM routine from the `PastMassBalance()` model. """ # run all needed prepro tasks gdir = self._setup_mb_test() # instance the mass balance models vas_mbmod = vascaling.VAScalingMassBalance(gdir) past_mbmod = massbalance.PastMassBalance(gdir) # get relevant glacier surface elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) heights = np.array([min_hgt, (min_hgt + max_hgt) / 2, max_hgt]) # specify an (arbitray) year year = 1975 # get mass balance relevant climate information temp_for_melt_vas, prcp_solid_vas = \ vas_mbmod.get_annual_climate(min_hgt, max_hgt, year) _, temp_for_melt_oggm, _, prcp_solid_oggm = \ past_mbmod.get_annual_climate(heights, year) # prepare my (monthly) values for comparison temp_for_melt_vas = temp_for_melt_vas.sum() prcp_solid_vas = prcp_solid_vas.sum() # computed positive terminus melting temperature must be equal for both # used methods, i.e. temp_VAS == temp_OGGM np.testing.assert_allclose(temp_for_melt_vas, temp_for_melt_oggm[0], rtol=1e-3) # glacier averaged solid precipitation amount must be greater than (or # equal to) solid precipitation amount at glacier terminus elevation assert md(prcp_solid_oggm[0], prcp_solid_vas) >= 0 # glacier averaged solid precipitation amount must be comparable to the # solid precipitation amount at average glacier surface elevation assert rel_err(prcp_solid_oggm[1], prcp_solid_vas) <= 0.15 # glacier averaged solid precipitation amount must be less than (or # equal to) solid precipitation amount at maximum glacier elevation assert md(prcp_solid_oggm[2], prcp_solid_vas) <= 0
def test_annual_mb(self): """Test the routine computing the annual mass balance.""" # run all needed prepro tasks gdir = self._setup_mb_test() # get relevant glacier surface elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # define temporal range year = 1975 years = np.array([year, year]) # get mass balance relevant climate data _, temp, prcp = vascaling.get_yearly_mb_temp_prcp(gdir, year_range=years) temp = temp[0] prcp = prcp[0] # read mu* and bias from vascaling_mustar vascaling_mustar = gdir.read_json('vascaling_mustar') mu_star = vascaling_mustar['mu_star'] bias = vascaling_mustar['bias'] # specify scaling factor for SI units [kg s-1] fac_SI = cfg.SEC_IN_YEAR * cfg.PARAMS['ice_density'] # compute mass balance 'by hand' mb_ref = (prcp - mu_star * temp - bias) / fac_SI # compute mb using the VAS mass balance model mb_mod = vascaling.VAScalingMassBalance(gdir).get_annual_mb( min_hgt, max_hgt, year) # compare mass balances with bias np.testing.assert_allclose(mb_ref, mb_mod, rtol=1e-3) # compute mass balance 'by hand' mb_ref = (prcp - mu_star * temp) / fac_SI # compute mb 'by model' mb_mod = vascaling.VAScalingMassBalance(gdir, bias=0). \ get_annual_mb(min_hgt, max_hgt, year) # compare mass balances without bias np.testing.assert_allclose(mb_ref, mb_mod, rtol=1e-3)
def test_monthly_climate(self): """Test the routine getting the monthly climate against the routine getting annual climate. """ # run all needed prepro tasks gdir = self._setup_mb_test() # instance the mass balance models mbmod = vascaling.VAScalingMassBalance(gdir) # get relevant glacier surface elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # get all month of the year in the # floating (hydrological) year convention year = 1975 months = np.linspace(year, year + 1, num=12, endpoint=False) # create containers temp_month = np.empty(12) prcp_month = np.empty(12) # get mb relevant climate data for every month for i, month in enumerate(months): _temp, _prcp = mbmod.get_monthly_climate(min_hgt, max_hgt, month) temp_month[i] = _temp prcp_month[i] = _prcp # melting temperature and precipitation amount cannot be negative assert temp_month.all() >= 0. assert prcp_month.all() >= 0. # get climate data for the whole year temp_year, prcp_year = mbmod.get_annual_climate(min_hgt, max_hgt, year) # compare np.testing.assert_array_almost_equal(temp_month, temp_year, decimal=2) np.testing.assert_array_almost_equal(prcp_month, prcp_year, decimal=2)
def seek_start_area(rgi_id, name, show=False, path='', ref=np.NaN, adjust_term_elev=False, legend=True, instant_geometry_change=False): """ Set up an VAS model from scratch and run/test the start area seeking tasks. The result is a plot showing the modeled glacier area evolution for different start values. The plots can be displayed and/or stored to file. Parameters ---------- rgi_id: string RGI ID denoting the glacier on which to perform the tasks name: string Name og glacier, since it is not always given (or correct) in RGI show: bool, optional, default=False Flag deciding whether or not to show the created plots. path: string, optional, default='' Path under which the modeled area plot should be stored. ref: float, optional, default=np.NaN Historic (1851) reference area with which a reference model run is performed. """ # Initialization and load default parameter file vascaling.initialize() # compute RGI region and version from RGI IDs # assuming they all are all the same rgi_region = (rgi_id.split('-')[-1]).split('.')[0] rgi_version = (rgi_id.split('-')[0])[-2:-1] # specify working directory and output directory working_dir = os.path.abspath('../working_directories/start_area/') # output_dir = os.path.abspath('./vas_run_output') output_dir = os.path.abspath('../data/vas_run_output') # create working directory utils.mkdir(working_dir, reset=False) utils.mkdir(output_dir) # set path to working directory cfg.PATHS['working_dir'] = working_dir # set RGI version and region cfg.PARAMS['rgi_version'] = rgi_version # define how many grid points to use around the glacier, # if you expect the glacier to grow large use a larger border cfg.PARAMS['border'] = 20 # we use HistAlp climate data cfg.PARAMS['baseline_climate'] = 'HISTALP' # set the mb hyper parameters accordingly cfg.PARAMS['prcp_scaling_factor'] = 1.75 cfg.PARAMS['temp_melt'] = -1.75 cfg.PARAMS['run_mb_calibration'] = False # the bias is defined to be zero during the calibration process, # which is why we don't use it here to reproduce the results cfg.PARAMS['use_bias_for_run'] = True # get/downlaod the rgi entity including the outline shapefile rgi_df = utils.get_rgi_glacier_entities([rgi_id]) # set name, if not delivered with RGI if rgi_df.loc[int(rgi_id[-5:]) - 1, 'Name'] is None: rgi_df.loc[int(rgi_id[-5:]) - 1, 'Name'] = name # get and set path to intersect shapefile intersects_db = utils.get_rgi_intersects_region_file(region=rgi_region) cfg.set_intersects_db(intersects_db) # initialize the GlacierDirectory gdir = workflow.init_glacier_directories(rgi_df)[0] # # DEM and GIS tasks # # get the path to the DEM file (will download if necessary) # dem = utils.get_topo_file(gdir.cenlon, gdir.cenlat) # print('DEM source: {}, path to DEM file: {}'.format(dem[1], dem[0][0])) # # set path in config file # cfg.PATHS['dem_file'] = dem[0][0] # cfg.PARAMS['border'] = 10 # cfg.PARAMS['use_intersects'] = False # run GIS tasks gis.define_glacier_region(gdir) gis.glacier_masks(gdir) # process climate data climate.process_climate_data(gdir) # compute local t* and the corresponding mu* vascaling.local_t_star(gdir) # create mass balance model mb_mod = vascaling.VAScalingMassBalance(gdir) # look at specific mass balance over climate data period min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) y0 = 1851 y1 = 2014 # run scalar minimization minimize_res = vascaling.find_start_area( gdir, adjust_term_elev=adjust_term_elev, instant_geometry_change=instant_geometry_change) # print(minimize_res) # stop script if minimization was not successful if minimize_res.status and False: sys.exit(minimize_res.status) # instance glacier with today's values model_ref = vascaling.VAScalingModel(year_0=gdir.rgi_date, area_m2_0=gdir.rgi_area_m2, min_hgt=min_hgt, max_hgt=max_hgt, mb_model=mb_mod) # instance guessed starting areas num = 9 area_guess = np.linspace(1e6, np.floor(gdir.rgi_area_m2 * 2), num, endpoint=True) # create empty containers area_list = list() volume_list = list() spec_mb_list = list() # iterate over all starting areas for area_ in area_guess: # instance iteration model model_guess = vascaling.VAScalingModel(year_0=gdir.rgi_date, area_m2_0=gdir.rgi_area_m2, min_hgt=min_hgt, max_hgt=max_hgt, mb_model=mb_mod) # set new starting values model_guess.create_start_glacier(area_, y0, adjust_term_elev=adjust_term_elev) # run model and save years and area best_guess_ds = model_guess.run_until_and_store( year_end=model_ref.year, instant_geometry_change=instant_geometry_change) # create series and store in container area_list.append(best_guess_ds.area_m2.to_dataframe()['area_m2']) volume_list.append(best_guess_ds.volume_m3.to_dataframe()['volume_m3']) spec_mb_list.append(best_guess_ds.spec_mb.to_dataframe()['spec_mb']) # create DataFrame area_df = pd.DataFrame( area_list, index=['{:.2f}'.format(a / 1e6) for a in area_guess]) area_df.index.name = 'Start Area [km$^2$]' volume_df = pd.DataFrame( volume_list, index=['{:.2f}'.format(a / 1e6) for a in area_guess]) volume_df.index.name = 'Start Area [km$^2$]' # set up model with resulted starting area model = vascaling.VAScalingModel(year_0=model_ref.year_0, area_m2_0=model_ref.area_m2_0, min_hgt=model_ref.min_hgt_0, max_hgt=model_ref.max_hgt, mb_model=model_ref.mb_model) model.create_start_glacier(minimize_res.x, y0, adjust_term_elev=adjust_term_elev) # run model with best guess initial area best_guess_ds = model.run_until_and_store( year_end=model_ref.year, instant_geometry_change=instant_geometry_change) # run model with historic reference area if ref: model.reset() model.create_start_glacier(ref * 1e6, y0, adjust_term_elev=adjust_term_elev) ref_ds = model.run_until_and_store( year_end=model_ref.year, instant_geometry_change=instant_geometry_change) # create figure and add axes fig = plt.figure(figsize=[5, 5]) ax = fig.add_axes([0.125, 0.075, 0.85, 0.9]) # plot model output ax = (area_df / 1e6).T.plot(legend=False, colormap='Spectral', ax=ax) # plot best guess ax.plot( best_guess_ds.time, best_guess_ds.area_m2 / 1e6, color='k', ls='--', lw=1.2, label= f'{best_guess_ds.area_m2.isel(time=0).values/1e6:.2f} km$^2$ (best result)' ) # plot reference if ref: ax.plot( ref_ds.time, ref_ds.area_m2 / 1e6, color='k', ls='-.', lw=1.2, label= f'{ref_ds.area_m2.isel(time=0).values/1e6:.2f} km$^2$ (1850 ref.)') # plot 2003 reference line ax.axhline( model_ref.area_m2_0 / 1e6, c='k', ls=':', label=f'{model_ref.area_m2_0/1e6:.2f} km$^2$ ({gdir.rgi_date} obs.)') # add legend if legend: handels, labels = ax.get_legend_handles_labels() labels[:-3] = [r'{} km$^2$'.format(l) for l in labels[:-3]] leg = ax.legend(handels, labels, loc='upper right', ncol=2) # leg.set_title('Start area $A_0$', prop={'size': 12}) # replot best guess estimate and reference (in case it lies below another # guess) ax.plot(best_guess_ds.time, best_guess_ds.area_m2 / 1e6, color='k', ls='--', lw=1.2) if ref: ax.plot(ref_ds.time, ref_ds.area_m2 / 1e6, color='k', ls='-.', lw=1.2) # labels, title ax.set_xlim([best_guess_ds.time.values[0], best_guess_ds.time.values[-1]]) ax.set_xlabel('') ax.set_ylabel('Glacier area [km$^2$]') # save figure to file if path: fig.savefig(path) # show plot if show: plt.show() plt.clf() # plot and store volume evolution (volume_df / 1e9).T.plot(legend=False, colormap='viridis') plt.gcf().savefig(path[:-4] + '_volume.pdf')
def climate_run_vas(rgi_ids, path=True, temp_biases=[0, +0.5, -0.5], use_bias_for_run=False, suffixes=['_bias_zero', '_bias_p', '_bias_n'], tstar=None, nyears=None, **kwargs): """Computes 'only' the massbalance in analogy to the `equilibrium_run_...` routines, without running the evolution (volume/area scaling) model. Dataset containing yearly values of specific mass balance is returned. Parameters ---------- rgi_ids: array-like List of RGI IDs for which the equilibrium experiments are performed. path: bool or str, optional, default=True If a path is given (or True), the resulting dataset is stored to file. temp_biases: array-like, optional, default=(0, +0.5, -0.5) List of temperature biases (float, in degC) for the mass balance model. suffixes: array-like, optional, default=['_normal', '_bias_p', '_bias_n'] Descriptive suffixes corresponding to the given temperature biases. tstar: float 'Equilibrium year' used for the mass balance calibration. nyears: int, optional, default=None Number of years for which to compute the random mass balance kwargs: Additional key word arguments for massbalance model. Returns ------- Dataset containing yearly values of specific massbalance. """ # assert correct output file suffixes for temp biases if len(temp_biases) != len(suffixes): raise RuntimeError("Each given temperature bias must have its " "corresponding suffix") # compute RGI region and version from RGI IDs # assuming all they are all the same rgi_region = (rgi_ids[0].split('-')[-1]).split('.')[0] rgi_version = (rgi_ids[0].split('-')[0])[-2:] # load default parameter file cfg.initialize() # create working directory wdir = '/Users/oberrauch/work/master/working_directories/' wdir += 'test_cluster' if not os.path.exists(wdir): os.makedirs(wdir) # shutil.rmtree(wdir) # os.makedirs(wdir) # set path to working directory cfg.PATHS['working_dir'] = wdir # set RGI verion and region cfg.PARAMS['rgi_version'] = rgi_version # define how many grid points to use around the glacier, # if you expect the glacier to grow large use a larger border cfg.PARAMS['border'] = 120 # we use HistAlp climate data cfg.PARAMS['baseline_climate'] = 'HISTALP' # set the mb hyper parameters accordingly cfg.PARAMS['prcp_scaling_factor'] = 1.75 cfg.PARAMS['temp_melt'] = -1.75 # the bias is defined to be zero during the calibration process, # which is why we don't use it here to reproduce the results cfg.PARAMS['use_bias_for_run'] = use_bias_for_run # operational run, all glaciers should run cfg.PARAMS['continue_on_error'] = True # read RGI entry for the glaciers as DataFrame # containing the outline area as shapefile rgidf = utils.get_rgi_glacier_entities(rgi_ids) # get and set path to intersect shapefile intersects_db = utils.get_rgi_intersects_region_file(region=rgi_region) cfg.set_intersects_db(intersects_db) # initialize the GlacierDirectory gdirs = workflow.init_glacier_directories(rgidf, reset=False, force=True) # define the local grid and glacier mask workflow.execute_entity_task(gis.define_glacier_region, gdirs) workflow.execute_entity_task(gis.glacier_masks, gdirs) # process the given climate file workflow.execute_entity_task(climate.process_climate_data, gdirs) # compute local t* and the corresponding mu* workflow.execute_entity_task(vascaling.local_t_star, gdirs, tstar=tstar, bias=0) # use t* as center year, even if specified differently kwargs['y0'] = tstar # run for 10'000 years if not specified otherwise if nyears is None: nyears = 1e4 years = np.arange(0, nyears + 1) # create dataset ds = list() # run RandomMassBalance model centered around t*, once without # temperature bias and once with positive and negative temperature bias # of 0.5 °C each. for gdir in gdirs: # set random seed to get reproducible results kwargs.setdefault('seed', 12) kwargs.setdefault('halfsize', 15) kwargs.setdefault('filename', 'climate_historical') kwargs.setdefault('input_filesuffix', '') kwargs.setdefault('unique_samples', False) ds_ = list() for suffix, temp_bias in zip(suffixes, temp_biases): # instance mass balance model mb_mod = vascaling.RandomVASMassBalance(gdir, **kwargs) if temp_bias is not None: # add given temperature bias to mass balance model mb_mod.temp_bias = temp_bias # where to store the model output diag_path = gdir.get_filepath('model_diagnostics', filesuffix='_vas', delete=True) # get minimum and maximum glacier elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # create empty container spec_mb = list() # iterate over all years for yr in years: spec_mb.append(mb_mod.get_specific_mb(min_hgt, max_hgt, yr)) # add to dataset da = xr.DataArray(spec_mb, dims=('year'), coords={'year': years}) ds_.append(xr.Dataset({'spec_mb': da})) ds_ = xr.concat(ds_, pd.Index(temp_biases, name='temp_bias')) ds_.coords['rgi_id'] = gdir.rgi_id ds.append(ds_) ds = xr.concat(ds, 'rgi_id') # store datasets if path: if path is True: path = os.path.join(cfg.PATHS['working_dir'], 'mb_output_vas.nc') ds.to_netcdf(path) # ds_normal.to_netcdf(path[1]) # return ds, ds_normal return ds
def climate_run_vas(rgi_ids, path=True, temp_biases=[0, +0.5, -0.5], suffixes=['_bias_zero', '_bias_p', '_bias_n'], use_bias_for_run=False, use_default_tstar=True, tstar=None, nyears=None, **kwargs): """Computes 'only' the massbalance in analogy to the `equilibrium_run_...` routines, without running the (volume/area scaling) evolution model. Dataset containing yearly values of specific mass balance is returned. Note: the task is not parallelized, hence it can take long if many glaciers are given. TODO: could/should be fixed sometime... TODO: add logging information Parameters ---------- rgi_ids: array-like List of RGI IDs for which the equilibrium experiments are performed. path: bool or str, optional, default=True If a path is given (or True), the resulting dataset is stored to file. temp_biases: array-like, optional, default=(0, +0.5, -0.5) List of temperature biases (float, in degC) for the mass balance model. suffixes: array-like, optional, default=['_normal', '_bias_p', '_bias_n'] Descriptive suffixes corresponding to the given temperature biases. use_bias_for_run: bool, optional, default=False Flag deciding whether or not the mass balance residual is used tstar: float, optional, default=None 'Equilibrium year' used for the mass balance calibration. Using the `ref_tstars.csv` table if not supplied. use_default_tstar : bool, optional, default=True Flag deciding whether or not to use the default ref_tstar.csv list. If `False`the `oggm_ref_tstars_rgi6_histalp.csv` reference table is used. nyears: int, optional, default=None Number of years for which to compute the random mass balance kwargs: Additional key word arguments for massbalance model. Returns ------- Dataset containing yearly values of specific massbalance. """ # assert correct output file suffixes for temp biases if len(temp_biases) != len(suffixes): raise RuntimeError("Each given temperature bias must have its " "corresponding suffix") # compute RGI region and version from RGI IDs # assuming all they are all the same rgi_region = (rgi_ids[0].split('-')[-1]).split('.')[0] rgi_version = (rgi_ids[0].split('-')[0])[-2:-1] # load default parameter file cfg.initialize() # get environmental variables for working and output directories WORKING_DIR = os.environ["WORKDIR"] OUTPUT_DIR = os.environ["OUTDIR"] # create working directory utils.mkdir(WORKING_DIR) # set path to working directory cfg.PATHS['working_dir'] = WORKING_DIR # set RGI version and region cfg.PARAMS['rgi_version'] = rgi_version # define how many grid points to use around the glacier, # if you expect the glacier to grow large use a larger border cfg.PARAMS['border'] = 120 # we use HistAlp climate data cfg.PARAMS['baseline_climate'] = 'HISTALP' # set the mb hyper parameters accordingly cfg.PARAMS['prcp_scaling_factor'] = 2.5 cfg.PARAMS['temp_melt'] = -0.5 # the bias is defined to be zero during the calibration process, # which is why we don't use it here to reproduce the results cfg.PARAMS['use_bias_for_run'] = use_bias_for_run # operational run, all glaciers should run cfg.PARAMS['continue_on_error'] = False # read RGI entry for the glaciers as DataFrame # containing the outline area as shapefile rgidf = utils.get_rgi_glacier_entities(rgi_ids) # get and set path to intersect shapefile intersects_db = utils.get_rgi_intersects_region_file(region=rgi_region) cfg.set_intersects_db(intersects_db) # initialize the GlacierDirectory gdirs = workflow.init_glacier_directories(rgidf, reset=False, force=True) # define the local grid and glacier mask workflow.execute_entity_task(gis.define_glacier_region, gdirs) workflow.execute_entity_task(gis.glacier_masks, gdirs) # process the given climate file workflow.execute_entity_task(climate.process_climate_data, gdirs) # compute local t* and the corresponding mu* if tstar or use_default_tstar: # compute mustar from given tstar workflow.execute_entity_task(vascaling.local_t_star, gdirs, tstar=tstar, bias=0) else: # compute mustar from the reference table for the flowline model # RGI v6 and HISTALP baseline climate ref_df = pd.read_csv(utils.get_demo_file('oggm_ref_tstars_rgi6_histalp.csv')) workflow.execute_entity_task(vascaling.local_t_star, gdirs, ref_df=ref_df) # use t* as center year, even if specified differently kwargs['y0'] = tstar # run for 10'000 years if not specified otherwise if nyears is None: nyears = 1e4 years = np.arange(0, nyears + 1) # create dataset ds = list() # run RandomMassBalance model centered around t*, once without # temperature bias and once with positive and negative temperature bias # of 0.5 deg C each. for gdir in gdirs: # set random seed to get reproducible results kwargs.setdefault('halfsize', 15) kwargs.setdefault('filename', 'climate_historical') kwargs.setdefault('input_filesuffix', '') ds_ = list() for suffix, temp_bias in zip(suffixes, temp_biases): # instance mass balance model try: mb_mod = vascaling.ConstantVASMassBalance(gdir, **kwargs) except: # continue with the next glacier or raise exception # depending on `continue_on_error` flag if cfg.PARAMS['continue_on_error']: continue else: raise if temp_bias is not None: # add given temperature bias to mass balance model mb_mod.temp_bias = temp_bias # where to store the model output diag_path = gdir.get_filepath('model_diagnostics', filesuffix='_vas', delete=True) # get minimum and maximum glacier elevation min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) # create empty container spec_mb = list() # iterate over all years for yr in years: spec_mb.append(mb_mod.get_specific_mb(min_hgt, max_hgt, yr)) # add to dataset da = xr.DataArray(spec_mb, dims=('year'), coords={'year': years}) ds_.append(xr.Dataset({'spec_mb': da})) if ds_: ds_ = xr.concat(ds_, pd.Index(temp_biases, name='temp_bias')) ds_.coords['rgi_id'] = gdir.rgi_id ds.append(ds_) if ds: # combine output from single glaciers into one dataset ds = xr.concat(ds, 'rgi_id') # store datasets if path: if path is True: path = os.path.join(OUTPUT_DIR, 'mb_output_vas.nc') ds.to_netcdf(path) # return ds, ds_normal return ds
def test_run_until_and_store(self): """Test the volume/area scaling model against the oggm.FluxBasedModel. Both models run the Hintereisferner over the entire HistAlp climate period, initialized with the 2003 RGI outline without spin up. The following two parameters for length, area and volume are tested: - correlation coefficient - relative RMSE, i.e. RMSE/mean(OGGM). Whereby the results from the VAS model are offset with the average differences to the OGGM results. """ # read the Hintereisferner DEM hef_file = get_demo_file('Hintereisferner_RGI6.shp') entity = gpd.read_file(hef_file).iloc[0] # initialize the GlacierDirectory gdir = oggm.GlacierDirectory(entity, base_dir=self.testdir) # define the local grid and glacier mask gis.define_glacier_region(gdir, entity=entity) gis.glacier_masks(gdir) # process the given climate file climate.process_custom_climate_data(gdir) # run center line preprocessing tasks centerlines.compute_centerlines(gdir) centerlines.initialize_flowlines(gdir) centerlines.compute_downstream_line(gdir) centerlines.compute_downstream_bedshape(gdir) centerlines.catchment_area(gdir) centerlines.catchment_intersections(gdir) centerlines.catchment_width_geom(gdir) centerlines.catchment_width_correction(gdir) # read reference glacier mass balance data mbdf = gdir.get_ref_mb_data() # compute the reference t* for the glacier # given the reference of mass balance measurements res = climate.t_star_from_refmb(gdir, mbdf=mbdf['ANNUAL_BALANCE']) t_star, bias = res['t_star'], res['bias'] # -------------------- # SCALING MODEL # -------------------- # compute local t* and the corresponding mu* vascaling.local_t_star(gdir, tstar=t_star, bias=bias) # instance the mass balance models vas_mbmod = vascaling.VAScalingMassBalance(gdir) # get reference area a0 = gdir.rgi_area_m2 # get reference year y0 = gdir.get_climate_info()['baseline_hydro_yr_0'] # get min and max glacier surface elevation h0, h1 = vascaling.get_min_max_elevation(gdir) vas_model = vascaling.VAScalingModel(year_0=y0, area_m2_0=a0, min_hgt=h0, max_hgt=h1, mb_model=vas_mbmod) # let model run over entire HistAlp climate period vas_ds = vas_model.run_until_and_store(2003) # ------ # OGGM # ------ # compute local t* and the corresponding mu* climate.local_t_star(gdir, tstar=t_star, bias=bias) climate.mu_star_calibration(gdir) # instance the mass balance models mb_mod = massbalance.PastMassBalance(gdir) # perform ice thickness inversion inversion.prepare_for_inversion(gdir) inversion.mass_conservation_inversion(gdir) inversion.filter_inversion_output(gdir) # initialize present time glacier flowline.init_present_time_glacier(gdir) # instance flowline model fls = gdir.read_pickle('model_flowlines') y0 = gdir.get_climate_info()['baseline_hydro_yr_0'] fl_mod = flowline.FluxBasedModel(flowlines=fls, mb_model=mb_mod, y0=y0) # run model and store output as xarray data set _, oggm_ds = fl_mod.run_until_and_store(2003) # temporal indices must be equal assert (vas_ds.time == oggm_ds.time).all() # specify which parameters to compare and their respective correlation # coefficients and rmsd values params = ['length_m', 'area_m2', 'volume_m3'] corr_coeffs = np.array([0.7, 0.7, 0.7]) rmsds = np.array([0.43e3, 0.25e6, 0.05e9]) # compare given parameters for param, cc, rmsd in zip(params, corr_coeffs, rmsds): # correlation coefficient assert corrcoef(oggm_ds[param].values, vas_ds[param].values) >= cc # root mean squared deviation rmsd_an = rmsd_bc(oggm_ds[param].values, vas_ds[param].values) assert rmsd_an <= rmsd
def test_run_until_equilibrium(self): """ Note: the oscillating behavior makes this test almost meaningless Returns ------- """ # let's not use the mass balance bias since we want to reproduce # results from mass balance calibration cfg.PARAMS['use_bias_for_run'] = False # read the Hintereisferner DEM hef_file = get_demo_file('Hintereisferner_RGI6.shp') entity = gpd.read_file(hef_file).iloc[0] # initialize the GlacierDirectory gdir = oggm.GlacierDirectory(entity, base_dir=self.testdir) # define the local grid and glacier mask gis.define_glacier_region(gdir, entity=entity) gis.glacier_masks(gdir) # process the given climate file climate.process_custom_climate_data(gdir) # compute mass balance parameters fn = 'vas_ref_tstars_rgi6_histalp.csv' fp = vascaling.get_ref_tstars_filepath(fn) ref_df = pd.read_csv(fp) vascaling.local_t_star(gdir, ref_df=ref_df) # instance a constant mass balance model, centred around t* mb_model = vascaling.ConstantVASMassBalance(gdir) # add a positive temperature bias mb_model.temp_bias = 0.5 # create a VAS model: start with year 0 since we are using a constant # massbalance model, other values are read from RGI min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) model = vascaling.VAScalingModel(year_0=0, area_m2_0=gdir.rgi_area_m2, min_hgt=min_hgt, max_hgt=max_hgt, mb_model=mb_model) # run glacier with new mass balance model model.run_until_equilibrium(rate=1e-5) # equilibrium should be reached after a couple of 100 years assert model.year <= 600 # new equilibrium glacier should be smaller (positive temperature bias) assert model.volume_m3 < model.volume_m3_0 # run glacier for another 100 years and check volume again v_eq = model.volume_m3 model.run_until(model.year + 100) assert abs(1 - (model.volume_m3 / v_eq)) < 0.01 # instance a random mass balance model, centred around t* mb_model = vascaling.RandomVASMassBalance(gdir) min_hgt, max_hgt = vascaling.get_min_max_elevation(gdir) model = vascaling.VAScalingModel(year_0=0, area_m2_0=gdir.rgi_area_m2, min_hgt=min_hgt, max_hgt=max_hgt, mb_model=mb_model) # run glacier with random mass balance model with self.assertRaises(TypeError): model.run_until_equilibrium(rate=1e-4)
# -------------------- # compute local t* and the corresponding mu* log.info('Initialize the model') gdir = gdirs[0] # instance the mass balance models mb_mod = vascaling.VAScalingMassBalance(gdir) # get reference area a0 = gdir.rgi_area_m2 # get reference year y0 = int(rgidf.BgnDate.values[0][:4]) # get min and max glacier surface elevation h0, h1 = vascaling.get_min_max_elevation(gdir) # set up the glacier model with the values of 2003 hef_ref = vascaling.VAScalingModel(year_0=1851, area_m2_0=a0, min_hgt=h0, max_hgt=h1, mb_model=mb_mod) # specify path where to store model diagnostics diag_path = gdir.get_filepath('model_diagnostics', delete=True) hef_ref.run_until_and_store(2000, diag_path=diag_path) ds = utils.compile_run_output([gdir]) ds.to_netcdf(os.path.join(output_dir, 'run_output.nc'))