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_RGI5.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_RGI5.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.read_pickle('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_run_until_equilibrium(self): """""" # 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_RGI5.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 ref_df = cfg.PARAMS['vas_ref_tstars_rgi5_histalp'] 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-4) # equilibrium should be reached after a couple of 100 years assert model.year <= 300 # 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
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 # TODO: does this even make any sense?! assert np.abs(rel_err(past_mb.mean(), vas_mb.mean())) <= 0.36 # 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 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_RGI5.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.read_json('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.read_json('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.96, 0.90, 0.93]) rmsds = np.array([0.43e3, 0.14e6, 0.03e9]) # 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
# -------------------- # compute local t* and the corresponding mu* vascaling.local_t_star(gdir) # instance the mass balance models mbmod = vascaling.VAScalingMassBalance(gdir) # ---------------- # DYNAMICAL PART # ---------------- # get reference area (from RGI entry) a0 = gdir.rgi_area_m2 # get reference year (start of climate records) y0 = gdir.read_pickle('climate_info')['baseline_hydro_yr_0'] # get min and max glacier surface elevation (based on RGI outline) h0, h1 = vascaling.get_min_max_elevation(gdir) # initialize iteration counter variable k = 1 # start iteration process of "finding the start area" while True: # specify path to *.mat file and see if file exists mat_file = '../data/start_area_results/{:s}_iteration_{:02d}.mat'.format(rgi_id, k) if not os.path.isfile(mat_file): # break loop if no file found break # read mat file mat_file = loadmat(mat_file) # increment counter
def compare(rgi_id, glacier_name): """ :param rgi_id: :param glacier_name: :return: """ # --------------------- # PREPROCESSING TASKS # --------------------- # create test directory wdir = os.path.join(os.path.abspath('.'), 'comparison_wdir') if not os.path.exists(wdir): os.makedirs(wdir) shutil.rmtree(wdir) os.makedirs(wdir) # load default parameter file cfg.initialize() # RGI entity # get/downlaod the rgi entity including the outline shapefile rgi_df = utils.get_rgi_glacier_entities([rgi_id]) # set name, since 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'] = glacier_name # select single entry rgi_entity = rgi_df.iloc[0] # GlacierDirectory # specify the working directory and define the glacier directory cfg.PATHS['working_dir'] = wdir gdir = oggm.GlacierDirectory(rgi_entity) # DEM and GIS tasks # get the path to the DEM file (will download if necessary) dem = utils.get_topo_file(gdir.cenlon, gdir.cenlat) # 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, entity=rgi_entity) gis.glacier_masks(gdir) # Climate data # using HistAlp cfg.PARAMS['baseline_climate'] = 'HISTALP' # climate records before 1850 are hardly reliable, which is not so drastic for # qualitative experiments (could be driven with random climate anyway) # cfg.PARAMS['baseline_y0'] = 1850 # change hyper parameters for HistAlp cfg.PARAMS['prcp_scaling_factor'] = 1.75 cfg.PARAMS['temp_melt'] = -1.75 # run climate task climate.process_histalp_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) # -------------------- # SCALING MODEL # -------------------- # compute local t* and the corresponding mu* vascaling.local_t_star(gdir) # instance the mass balance models vas_mb_mod = vascaling.VAScalingMassBalance(gdir) # get reference area a0 = gdir.rgi_area_m2 # get reference year y0 = gdir.read_pickle('climate_info')['baseline_hydro_yr_0'] y1 = gdir.read_pickle('climate_info')['baseline_hydro_yr_1'] # get min and max glacier surface elevation h0, h1 = vascaling.get_min_max_elevation(gdir) # instance VAS model vas_model = vascaling.VAScalingModel(year_0=y0, area_m2_0=a0, min_hgt=h0, max_hgt=h1, mb_model=vas_mb_mod) # run model over all HistAlp climate period vas_df = vas_model.run_and_store(y1, reset=True) # get relevant parameters years_vas = vas_df.index.values length_m_vas = vas_df.length_m.values area_m2_vas = vas_df.area_m2.values volume_m3_vas = vas_df.volume_m3.values # ------ # OGGM # ------ # compute local t* and the corresponding mu* climate.local_t_star(gdir) climate.mu_star_calibration(gdir) # instance the mass balance models mb_mod = massbalance.PastMassBalance(gdir) # run inversion tasks 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.read_pickle('climate_info')['baseline_hydro_yr_0'] y1 = gdir.read_pickle('climate_info')['baseline_hydro_yr_1'] 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(y1) years_oggm = oggm_ds.hydro_year.values # annual index must be equal np.testing.assert_array_equal(years_oggm, years_vas) length_m_oggm = oggm_ds.length_m.values area_m2_oggm = oggm_ds.area_m2.values volume_m3_oggm = oggm_ds.volume_m3.values # define column names for DataFrame names = ['length_vas', 'length_oggm', 'area_vas', 'area_oggm', 'volume_vas', 'volume_oggm'] # combine glacier geometries into DataFrame df = pd.DataFrame(np.array([length_m_vas, length_m_oggm, area_m2_vas, area_m2_oggm, volume_m3_vas, volume_m3_oggm]).T, index=years_vas, columns=names) # save to file store = True if store: # define path and file names folder = '/Users/oberrauch/work/master/data/' df.to_csv(folder+'run_comparison.csv') def plot_both(vas_df, oggm_df, ref=None, correct_bias=False, title='', ylabel='', file_path='', exp=0): """ Plot geometric parameters of both models. If a `file_path` is given, the figure will be saved. :param vas_df: (pandas.Series) geometric glacier parameter of the VAS model :param oggm_df: (pandas.Series) geometric glacier parameter of the OGGM :param ref: (pandas.Series) measured glacier parameter, optional :param title: (string) figure title, optional :param ylabel: (string) label for y-axis, optional :param file_path: (string) where to store the figure, optional :param exp: (int) exponent for labels in scientific notation, optional """ beamer = True if beamer: mpl.rc('axes', titlesize=18) mpl.rc('axes', labelsize=14) mpl.rc('xtick', labelsize=14) mpl.rc('ytick', labelsize=14) mpl.rc('legend', fontsize=10) # create figure and first axes fig = plt.figure(figsize=[6, 4]) ax = fig.add_axes([0.15, 0.1, 0.8, 0.8]) # define colors c1 = 'C0' c2 = 'C1' c3 = 'C3' # plot vas and OGGM parameters ax.plot(oggm_df.index, oggm_df.values, c=c2, label='OGGM') ax.plot(vas_df.index, vas_df.values, c=c1, label='VAS') if ref: # plot reference parameter if given ax.plot(ref.index, ref.values, c=c3, label='measurements') if correct_bias: # plot bias corrected vas df_ = pd.DataFrame([oggm_df, vas_df]).T bias = vas_df.values - df_.mean().diff().iloc[1] ax.plot(vas_df.index, bias, c=c1, ls='--', label='VAS, bias corrected') # add RMSD as text ax.text(0.05, 0.05, 'RMSD: {:.1e}'.format(utils.rmsd(oggm_df, bias)), transform=plt.gca().transAxes) # add correlation coefficient as text ax.text(0.05, 0.11, 'Corr. Coef.: {:.2f}'.format( utils.corrcoef(oggm_df, vas_df)), transform=plt.gca().transAxes) # add title, labels, legend ax.set_title(title) ax.set_ylabel(ylabel) ax.legend() import matplotlib.ticker class OOMFormatter(matplotlib.ticker.ScalarFormatter): def __init__(self, order=0, fformat="%1.1f", offset=False, mathText=False): self.oom = order self.fformat = fformat matplotlib.ticker.ScalarFormatter.__init__(self, useOffset=offset, useMathText=mathText) def _set_orderOfMagnitude(self, nothing): self.orderOfMagnitude = self.oom def _set_format(self, vmin, vmax): self.format = self.fformat if self._useMathText: self.format = '$%s$' % matplotlib.ticker._mathdefault(self.format) # use scientific notation with fixed exponent according ax.yaxis.set_major_formatter(OOMFormatter(exp, "%1.2f")) # store to file if file_path: plt.savefig(file_path, bbox_inches='tight', format=file_path.split('.')[-1]) # specify plot directory folder = '/Users/oberrauch/work/master/plots/' # plot length plot_both(df.length_vas, df.length_oggm, correct_bias=True, title='Glacier length - {}'.format(glacier_name), ylabel=r'Length [m]', file_path=os.path.join(folder, '{}_length.pdf'.format(rgi_id)), exp=3) # plot area plot_both(df.area_vas, df.area_oggm, correct_bias=True, title='Surface area - {}'.format(glacier_name), ylabel=r'Area [m$^2$]', file_path=os.path.join(folder, '{}_area.pdf'.format(rgi_id)), exp=6) # plot volume plot_both(df.volume_vas, df.volume_oggm, correct_bias=True, title='Glacier volume - {}'.format(glacier_name), ylabel=r'Volume [m$^3$]', file_path=os.path.join(folder, '{}_volume.pdf'.format(rgi_id)), exp=9)