def _setup_mb_test(self): """Avoiding a chunk of code duplicate. Performs needed prepo tasks and returns the oggm.GlacierDirectory. """ # 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 the 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 centerline prepro 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 = vascaling.t_star_from_refmb(gdir, mbdf=mbdf['ANNUAL_BALANCE']) t_star, bias = res['t_star'], res['bias'] # compute local t* and the corresponding mu* vascaling.local_t_star(gdir, tstar=t_star, bias=bias) # run OGGM mu* calibration climate.local_t_star(gdir, tstar=t_star, bias=bias) climate.mu_star_calibration(gdir) # pass the GlacierDirectory return gdir
tasks.define_glacier_region(gdir) tasks.glacier_masks(gdir) tasks.compute_centerlines(gdir) tasks.initialize_flowlines(gdir) tasks.compute_downstream_line(gdir) tasks.catchment_area(gdir) tasks.catchment_width_geom(gdir) tasks.catchment_width_correction(gdir) cfg.PARAMS['baseline_climate'] = 'HISTALP' cfg.PARAMS['baseline_y0'] = 1850 tasks.process_histalp_data(gdir) mu_yr_clim = tasks.glacier_mu_candidates(gdir) mbdf = gdir.get_ref_mb_data() res = t_star_from_refmb(gdir, mbdf=mbdf.ANNUAL_BALANCE) local_t_star(gdir, tstar=res['t_star'], bias=res['bias'], reset=True) mu_star_calibration(gdir, reset=True) # For flux plot tasks.prepare_for_inversion(gdir, add_debug_var=True) # For plots years, temp_yr, prcp_yr = mb_yearly_climate_on_glacier(gdir) # which years to look at selind = np.searchsorted(years, mbdf.index) temp_yr = np.mean(temp_yr[selind]) prcp_yr = np.mean(prcp_yr[selind]) # Average oberved mass-balance ref_mb = mbdf.ANNUAL_BALANCE.mean()
def init_hef(reset=False, border=40, logging_level='INFO'): from oggm.core import gis, inversion, climate, centerlines, flowline import geopandas as gpd # test directory testdir = os.path.join(get_test_dir(), 'tmp_border{}'.format(border)) if not os.path.exists(testdir): os.makedirs(testdir) reset = True # Init cfg.initialize(logging_level=logging_level) cfg.set_intersects_db(get_demo_file('rgi_intersect_oetztal.shp')) cfg.PATHS['dem_file'] = get_demo_file('hef_srtm.tif') cfg.PATHS['climate_file'] = get_demo_file('histalp_merged_hef.nc') cfg.PARAMS['baseline_climate'] = '' cfg.PATHS['working_dir'] = testdir cfg.PARAMS['border'] = border hef_file = get_demo_file('Hintereisferner_RGI5.shp') entity = gpd.read_file(hef_file).iloc[0] gdir = oggm.GlacierDirectory(entity, reset=reset) if not gdir.has_file('inversion_params'): reset = True gdir = oggm.GlacierDirectory(entity, reset=reset) if not reset: return gdir gis.define_glacier_region(gdir) execute_entity_task(gis.glacier_masks, [gdir]) execute_entity_task(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) climate.process_custom_climate_data(gdir) mbdf = gdir.get_ref_mb_data()['ANNUAL_BALANCE'] res = climate.t_star_from_refmb(gdir, mbdf=mbdf) climate.local_t_star(gdir, tstar=res['t_star'], bias=res['bias']) climate.mu_star_calibration(gdir) inversion.prepare_for_inversion(gdir, add_debug_var=True) ref_v = 0.573 * 1e9 glen_n = cfg.PARAMS['glen_n'] def to_optimize(x): # For backwards compat _fd = 1.9e-24 * x[0] glen_a = (glen_n + 2) * _fd / 2. fs = 5.7e-20 * x[1] v, _ = inversion.mass_conservation_inversion(gdir, fs=fs, glen_a=glen_a) return (v - ref_v)**2 out = optimization.minimize(to_optimize, [1, 1], bounds=((0.01, 10), (0.01, 10)), tol=1e-4)['x'] _fd = 1.9e-24 * out[0] glen_a = (glen_n + 2) * _fd / 2. fs = 5.7e-20 * out[1] v, _ = inversion.mass_conservation_inversion(gdir, fs=fs, glen_a=glen_a, write=True) d = dict(fs=fs, glen_a=glen_a) d['factor_glen_a'] = out[0] d['factor_fs'] = out[1] gdir.write_pickle(d, 'inversion_params') # filter inversion.filter_inversion_output(gdir) inversion.distribute_thickness_interp(gdir, varname_suffix='_interp') inversion.distribute_thickness_per_altitude(gdir, varname_suffix='_alt') flowline.init_present_time_glacier(gdir) return gdir
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
def find_inversion_calving_loop(gdir, initial_water_depth=None, max_ite=30, stop_after_convergence=True, fixed_water_depth=False): """Iterative search for a calving flux compatible with the bed inversion. See Recinos et al 2019 for details. Parameters ---------- initial_water_depth : float the initial water depth starting the loop (for sensitivity experiments or to fix it to an observed value). The default is to use 1/3 of the terminus elevation if > 10 m, and 10 m otherwise max_ite : int the maximal number of iterations allowed before raising an error stop_after_convergence : bool continue to loop after convergence is reached (for sensitivity experiments) fixed_water_depth : bool fix the water depth and let the frontal altitude vary instead """ # Shortcuts from oggm.core import climate, inversion from oggm.exceptions import MassBalanceCalibrationError # Input if initial_water_depth is None: fl = gdir.read_pickle('inversion_flowlines')[-1] initial_water_depth = utils.clip_min(fl.surface_h[-1] / 3, 10) rho = cfg.PARAMS['ice_density'] # We accept values down to zero before stopping cfg.PARAMS['min_mu_star'] = 0 # Start iteration i = 0 cfg.PARAMS['clip_mu_star'] = False odf = pd.DataFrame() mu_is_zero = False while i < max_ite: # Calculates a calving flux from model output if i == 0: # First call we set to zero (it's just to be sure we start # from a non-calving glacier) f_calving = 0 elif i == 1: # Second call, we set a small positive calving to start with # Default is to get the thickness from free board and # initial water depth thick = None if fixed_water_depth: # This leaves the free board open for change thick = initial_water_depth + 1 out = calving_flux_from_depth(gdir, water_depth=initial_water_depth, thick=thick, fixed_water_depth=fixed_water_depth) f_calving = out['flux'] elif cfg.PARAMS['clip_mu_star']: # If we had to clip mu, the inversion calving becomes the real # flux, i.e. not compatible with calving law but with the # inversion fl = gdir.read_pickle('inversion_flowlines')[-1] f_calving = fl.flux[-1] * (gdir.grid.dx**2) * 1e-9 / rho mu_is_zero = True else: # Otherwise it is parameterized by the calving law if fixed_water_depth: out = calving_flux_from_depth(gdir, water_depth=initial_water_depth, fixed_water_depth=True) f_calving = out['flux'] else: f_calving = calving_flux_from_depth(gdir)['flux'] # Give it back to the inversion and recompute gdir.inversion_calving_rate = f_calving # 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 and store the # bad value (just for plotting) cfg.PARAMS['clip_mu_star'] = True df = gdir.read_json('local_mustar') df['mu_star_glacierwide'] = float(str(e).split(':')[-1]) climate.local_t_star(gdir) climate.mu_star_calibration(gdir) inversion.prepare_for_inversion(gdir, add_debug_var=True) v_inv, _ = inversion.mass_conservation_inversion(gdir) if fixed_water_depth: out = calving_flux_from_depth(gdir, water_depth=initial_water_depth, fixed_water_depth=True) else: out = calving_flux_from_depth(gdir) # Store the data odf.loc[i, 'calving_flux'] = f_calving odf.loc[i, 'mu_star'] = df['mu_star_glacierwide'] odf.loc[i, 'calving_law_flux'] = out['flux'] odf.loc[i, 'width'] = out['width'] odf.loc[i, 'thick'] = out['thick'] odf.loc[i, 'water_depth'] = out['water_depth'] odf.loc[i, 'free_board'] = out['free_board'] # Do we have to do another_loop? Start testing at 5th iteration calving_flux = odf.calving_flux.values if stop_after_convergence and i > 4: # We want to make sure that we don't converge by chance # so we test on last two iterations conv = (np.allclose(calving_flux[[-1, -2]], [out['flux'], out['flux']], rtol=0.01)) if mu_is_zero or conv: break i += 1 # Write output odf.index.name = 'iterations' odf.to_csv(gdir.get_filepath('calving_loop')) # Restore defaults cfg.PARAMS['min_mu_star'] = 1. cfg.PARAMS['clip_mu_star'] = False return odf
centerlines.catchment_width_geom(gdir) centerlines.catchment_width_correction(gdir) # -------------------- # MASS BALANCE TASKS # -------------------- # 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'] # compute local t* and the corresponding mu* climate.local_t_star(gdir, tstar=t_star, bias=bias) climate.mu_star_calibration(gdir) from oggm.core import massbalance # instance the mass balance models mb_mod = massbalance.PastMassBalance(gdir) # ----------- # INVERSION # ----------- from oggm.core import inversion inversion.prepare_for_inversion(gdir) inversion.mass_conservation_inversion(gdir) inversion.filter_inversion_output(gdir) # ----------------
def find_inversion_calving(gdir, fixed_water_depth=None): """Optimized search for a calving flux compatible with the bed inversion. See Recinos et al 2019 for details. Parameters ---------- fixed_water_depth : float fix the water depth to an observed value and let the free board vary instead. """ from oggm.core import climate, inversion from oggm.exceptions import MassBalanceCalibrationError # Let's start from a fresh state gdir.inversion_calving_rate = 0 climate.local_t_star(gdir) climate.mu_star_calibration(gdir) inversion.prepare_for_inversion(gdir, add_debug_var=True) inversion.mass_conservation_inversion(gdir) # Get the relevant variables cls = gdir.read_pickle('inversion_input')[-1] slope = cls['slope_angle'][-1] width = cls['width'][-1] # 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_depth=fixed_water_depth, fixed_water_depth=True) else: fl = calving_flux_from_depth(gdir, 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 # See e.g. RGI60-01.23642 df = gdir.read_json('local_mustar') out = calving_flux_from_depth(gdir) odf = dict() odf['calving_flux'] = 0 odf['calving_mu_star'] = df['mu_star_glacierwide'] odf['calving_law_flux'] = out['flux'] odf['calving_slope'] = slope odf['calving_thick'] = out['thick'] odf['calving_water_depth'] = out['water_depth'] odf['calving_free_board'] = out['free_board'] 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, thick=opt, water_depth=fixed_water_depth, fixed_water_depth=True) f_calving = out['flux'] else: out = calving_flux_from_depth(gdir, water_depth=opt) f_calving = out['flux'] gdir.inversion_calving_rate = f_calving # 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) inversion.prepare_for_inversion(gdir, add_debug_var=True) inversion.mass_conservation_inversion(gdir) if fixed_water_depth is not None: out = calving_flux_from_depth(gdir, water_depth=fixed_water_depth, fixed_water_depth=True) else: out = calving_flux_from_depth(gdir) 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_slope'] = slope odf['calving_thick'] = out['thick'] odf['calving_water_depth'] = out['water_depth'] odf['calving_free_board'] = out['free_board'] odf['calving_front_width'] = out['width'] for k, v in odf.items(): gdir.add_to_diagnostics(k, v) # Restore defaults cfg.PARAMS['min_mu_star'] = 1. cfg.PARAMS['clip_mu_star'] = False return odf
def find_inversion_calving(gdir, initial_water_depth=None, max_ite=30, stop_after_convergence=True, fixed_water_depth=False): """Iterative search for a calving flux compatible with the bed inversion. See Recinos et al 2019 for details. Parameters ---------- initial_water_depth : float the initial water depth starting the loop (for sensitivity experiments or to fix it to an observed value). The default is to use 1/3 of the terminus elevation if > 10 m, and 10 m otherwise max_ite : int the maximal number of iterations allowed before raising an error stop_after_convergence : bool continue to loop after convergence is reached (for sensitivity experiments) fixed_water_depth : bool fix the water depth and let the frontal altitude vary instead """ # Shortcuts from oggm.core import climate, inversion from oggm.exceptions import MassBalanceCalibrationError # Input if initial_water_depth is None: fl = gdir.read_pickle('inversion_flowlines')[-1] initial_water_depth = np.clip(fl.surface_h[-1] / 3, 10, None) rho = cfg.PARAMS['ice_density'] # We accept values down to zero before stopping cfg.PARAMS['min_mu_star'] = 0 # Start iteration i = 0 cfg.PARAMS['clip_mu_star'] = False odf = pd.DataFrame() mu_is_zero = False while i < max_ite: # Calculates a calving flux from model output if i == 0: # First call we set to zero (it's just to be sure we start # from a non-calving glacier) f_calving = 0 elif i == 1: # Second call, we set a small positive calving to start with # Default is to get the thickness from free board and # initial water depth thick = None if fixed_water_depth: # This leaves the free board open for change thick = initial_water_depth + 1 out = calving_flux_from_depth(gdir, water_depth=initial_water_depth, thick=thick, fixed_water_depth=fixed_water_depth) f_calving = out['flux'] elif cfg.PARAMS['clip_mu_star']: # If we had to clip mu, the inversion calving becomes the real # flux, i.e. not compatible with calving law but with the # inversion fl = gdir.read_pickle('inversion_flowlines')[-1] f_calving = fl.flux[-1] * (gdir.grid.dx ** 2) * 1e-9 / rho mu_is_zero = True else: # Otherwise it is parameterized by the calving law if fixed_water_depth: out = calving_flux_from_depth(gdir, water_depth=initial_water_depth, fixed_water_depth=True) f_calving = out['flux'] else: f_calving = calving_flux_from_depth(gdir)['flux'] # Give it back to the inversion and recompute gdir.inversion_calving_rate = f_calving # 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 and store the # bad value (just for plotting) cfg.PARAMS['clip_mu_star'] = True df = gdir.read_json('local_mustar') df['mu_star_glacierwide'] = float(str(e).split(':')[-1]) climate.local_t_star(gdir) climate.mu_star_calibration(gdir) inversion.prepare_for_inversion(gdir, add_debug_var=True) v_inv, _ = inversion.mass_conservation_inversion(gdir) if fixed_water_depth: out = calving_flux_from_depth(gdir, water_depth=initial_water_depth, fixed_water_depth=True) else: out = calving_flux_from_depth(gdir) # Store the data odf.loc[i, 'calving_flux'] = f_calving odf.loc[i, 'mu_star'] = df['mu_star_glacierwide'] odf.loc[i, 'calving_law_flux'] = out['flux'] odf.loc[i, 'width'] = out['width'] odf.loc[i, 'thick'] = out['thick'] odf.loc[i, 'water_depth'] = out['water_depth'] odf.loc[i, 'free_board'] = out['free_board'] # Do we have to do another_loop? Start testing at 5th iteration calving_flux = odf.calving_flux.values if stop_after_convergence and i > 4: # We want to make sure that we don't converge by chance # so we test on last two iterations conv = (np.allclose(calving_flux[[-1, -2]], [out['flux'], out['flux']], rtol=0.01)) if mu_is_zero or conv: break i += 1 # Write output odf.index.name = 'iterations' odf.to_csv(gdir.get_filepath('calving_loop')) # Restore defaults cfg.PARAMS['min_mu_star'] = 1. cfg.PARAMS['clip_mu_star'] = False return odf
def setup_cache(self): setattr(full_workflow.setup_cache, "timeout", 360) utils.mkdir(self.testdir, reset=True) self.cfg_init() entity = gpd.read_file(get_demo_file('01_rgi60_Columbia.shp')).iloc[0] gdir = oggm.GlacierDirectory(entity, base_dir=self.testdir) tasks.define_glacier_region(gdir, entity=entity) tasks.glacier_masks(gdir) tasks.compute_centerlines(gdir) tasks.initialize_flowlines(gdir) tasks.compute_downstream_line(gdir) tasks.compute_downstream_bedshape(gdir) tasks.catchment_area(gdir) tasks.catchment_intersections(gdir) tasks.catchment_width_geom(gdir) tasks.catchment_width_correction(gdir) climate.process_dummy_cru_file(gdir, seed=0) rho = cfg.PARAMS['ice_density'] i = 0 calving_flux = [] mu_star = [] ite = [] cfg.PARAMS['clip_mu_star'] = False cfg.PARAMS['min_mu_star'] = 0 # default is now 1 while i < 12: # Calculates a calving flux from model output if i == 0: # First call we set to zero (not very necessary, # this first loop could be removed) f_calving = 0 elif i == 1: # Second call we set a very small positive calving f_calving = utils.calving_flux_from_depth(gdir, water_depth=1) elif cfg.PARAMS['clip_mu_star']: # If we have to clip mu the calving becomes the real flux fl = gdir.read_pickle('inversion_flowlines')[-1] f_calving = fl.flux[-1] * (gdir.grid.dx**2) * 1e-9 / rho else: # Otherwise it is parameterized f_calving = utils.calving_flux_from_depth(gdir) # Give it back to the inversion and recompute gdir.inversion_calving_rate = f_calving # At this step we might raise a MassBalanceCalibrationError mu_is_zero = False 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 and store the # bad value (just for plotting) cfg.PARAMS['clip_mu_star'] = True df = gdir.read_json('local_mustar') df['mu_star_glacierwide'] = float(str(e).split(':')[-1]) climate.local_t_star(gdir) climate.mu_star_calibration(gdir) tasks.prepare_for_inversion(gdir, add_debug_var=True) v_inv, _ = tasks.mass_conservation_inversion(gdir) # Store the data calving_flux = np.append(calving_flux, f_calving) mu_star = np.append(mu_star, df['mu_star_glacierwide']) ite = np.append(ite, i) # Do we have to do another_loop? if i > 0: avg_one = np.mean(calving_flux[-4:]) avg_two = np.mean(calving_flux[-5:-1]) difference = abs(avg_two - avg_one) conv = (difference < 0.05 * avg_two or calving_flux[-1] == 0 or calving_flux[-1] == calving_flux[-2]) if mu_is_zero or conv: break i += 1 assert i < 8 assert calving_flux[-1] < np.max(calving_flux) assert calving_flux[-1] > 2 assert mu_star[-1] == 0 mbmod = massbalance.MultipleFlowlineMassBalance mb = mbmod(gdir, use_inversion_flowlines=True, mb_model_class=massbalance.ConstantMassBalance, bias=0) flux_mb = (mb.get_specific_mb() * gdir.rgi_area_m2) * 1e-9 / rho np.testing.assert_allclose(flux_mb, calving_flux[-1], atol=0.001) return calving_flux, mu_star
def init_hef(reset=False, border=40): # test directory testdir = os.path.join(get_test_dir(), 'tmp_border{}'.format(border)) if not os.path.exists(testdir): os.makedirs(testdir) reset = True # Init cfg.initialize() cfg.set_intersects_db(get_demo_file('rgi_intersect_oetztal.shp')) cfg.PATHS['dem_file'] = get_demo_file('hef_srtm.tif') cfg.PATHS['climate_file'] = get_demo_file('histalp_merged_hef.nc') cfg.PARAMS['baseline_climate'] = '' cfg.PATHS['working_dir'] = testdir cfg.PARAMS['border'] = border hef_file = get_demo_file('Hintereisferner_RGI5.shp') entity = gpd.read_file(hef_file).iloc[0] gdir = oggm.GlacierDirectory(entity, reset=reset) if not gdir.has_file('inversion_params'): reset = True gdir = oggm.GlacierDirectory(entity, reset=reset) if not reset: return gdir gis.define_glacier_region(gdir, entity=entity) execute_entity_task(gis.glacier_masks, [gdir]) execute_entity_task(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) climate.process_custom_climate_data(gdir) mbdf = gdir.get_ref_mb_data()['ANNUAL_BALANCE'] res = climate.t_star_from_refmb(gdir, mbdf=mbdf) climate.local_t_star(gdir, tstar=res['t_star'], bias=res['bias']) climate.mu_star_calibration(gdir) inversion.prepare_for_inversion(gdir, add_debug_var=True) ref_v = 0.573 * 1e9 glen_n = cfg.PARAMS['glen_n'] def to_optimize(x): # For backwards compat _fd = 1.9e-24 * x[0] glen_a = (glen_n+2) * _fd / 2. fs = 5.7e-20 * x[1] v, _ = inversion.mass_conservation_inversion(gdir, fs=fs, glen_a=glen_a) return (v - ref_v)**2 out = optimization.minimize(to_optimize, [1, 1], bounds=((0.01, 10), (0.01, 10)), tol=1e-4)['x'] _fd = 1.9e-24 * out[0] glen_a = (glen_n+2) * _fd / 2. fs = 5.7e-20 * out[1] v, _ = inversion.mass_conservation_inversion(gdir, fs=fs, glen_a=glen_a, write=True) d = dict(fs=fs, glen_a=glen_a) d['factor_glen_a'] = out[0] d['factor_fs'] = out[1] gdir.write_pickle(d, 'inversion_params') # filter inversion.filter_inversion_output(gdir) inversion.distribute_thickness_interp(gdir, varname_suffix='_interp') inversion.distribute_thickness_per_altitude(gdir, varname_suffix='_alt') flowline.init_present_time_glacier(gdir) return gdir
def find_inversion_calving(gdir, water_depth=1, max_ite=30, stop_after_convergence=True): """Iterative search for a calving flux compatible with the bed inversion. See Recinos et al 2019 for details. Parameters ---------- water_depth : float the initial water depth starting the loop (for sensitivity experiments) """ # Shortcuts from oggm.core import climate, inversion from oggm.exceptions import MassBalanceCalibrationError rho = cfg.PARAMS['ice_density'] # We accept values down to zero before stopping cfg.PARAMS['min_mu_star'] = 0 # Start iteration i = 0 cfg.PARAMS['clip_mu_star'] = False odf = pd.DataFrame() mu_is_zero = False while i < max_ite: # Calculates a calving flux from model output if i == 0: # First call we set to zero (it's just to be sure we start # from a non-calving glacier) f_calving = 0 elif i == 1: # Second call we set a small positive calving to start with out = calving_flux_from_depth(gdir, water_depth=water_depth) f_calving = out['flux'] elif cfg.PARAMS['clip_mu_star']: # If we had to clip mu, the inversion calving becomes the real # flux, i.e. not compatible with calving law but with the # inversion fl = gdir.read_pickle('inversion_flowlines')[-1] f_calving = fl.flux[-1] * (gdir.grid.dx**2) * 1e-9 / rho mu_is_zero = True else: # Otherwise it is parameterized by the calving law f_calving = calving_flux_from_depth(gdir)['flux'] # Give it back to the inversion and recompute gdir.inversion_calving_rate = f_calving # 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 and store the # bad value (just for plotting) cfg.PARAMS['clip_mu_star'] = True df = gdir.read_json('local_mustar') df['mu_star_glacierwide'] = float(str(e).split(':')[-1]) climate.local_t_star(gdir) climate.mu_star_calibration(gdir) inversion.prepare_for_inversion(gdir, add_debug_var=True) v_inv, _ = inversion.mass_conservation_inversion(gdir) out = calving_flux_from_depth(gdir) # Store the data odf.loc[i, 'calving_flux'] = f_calving odf.loc[i, 'mu_star'] = df['mu_star_glacierwide'] odf.loc[i, 'calving_law_flux'] = out['flux'] odf.loc[i, 'width'] = out['width'] odf.loc[i, 'thick'] = out['thick'] odf.loc[i, 'water_depth'] = out['water_depth'] odf.loc[i, 'free_board'] = out['free_board'] # Do we have to do another_loop? calving_flux = odf.calving_flux.values if stop_after_convergence and i > 0: conv = np.allclose(calving_flux[-1], out['flux'], rtol=0.001) if mu_is_zero or conv: break i += 1 odf.index.name = 'iterations' return odf
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
tasks.compute_centerlines(gdir) tasks.initialize_flowlines(gdir) tasks.compute_downstream_line(gdir) tasks.catchment_area(gdir) tasks.catchment_width_geom(gdir) tasks.catchment_width_correction(gdir) data_dir = get_demo_file('HISTALP_precipitation_all_abs_1801-2014.nc') cfg.PATHS['cru_dir'] = os.path.dirname(data_dir) cfg.PARAMS['baseline_climate'] = 'HISTALP' cfg.PARAMS['baseline_y0'] = 1850 tasks.process_histalp_data(gdir) tasks.glacier_mu_candidates(gdir) mbdf = gdir.get_ref_mb_data() res = t_star_from_refmb(gdir, mbdf=mbdf.ANNUAL_BALANCE) local_t_star(gdir, tstar=res['t_star'], bias=res['bias'], reset=True) mu_star_calibration(gdir, reset=True) # For flux plot tasks.prepare_for_inversion(gdir, add_debug_var=True) # For plots mu_yr_clim = gdir.read_pickle('climate_info')['mu_candidates_glacierwide'] years, temp_yr, prcp_yr = mb_yearly_climate_on_glacier(gdir) # which years to look at selind = np.searchsorted(years, mbdf.index) temp_yr = np.mean(temp_yr[selind]) prcp_yr = np.mean(prcp_yr[selind]) # Average oberved mass-balance
mkdir(base_dir, reset=True) entity = get_rgi_glacier_entities(['RGI60-11.00897']).iloc[0] gdir = oggm.GlacierDirectory(entity, base_dir=base_dir) cfg.set_intersects_db(get_rgi_intersects_entities(['RGI60-11.00897'])) tasks.define_glacier_region(gdir, entity=entity) tasks.glacier_masks(gdir) tasks.compute_centerlines(gdir) tasks.initialize_flowlines(gdir) tasks.catchment_area(gdir) tasks.catchment_intersections(gdir) tasks.catchment_width_geom(gdir) tasks.catchment_width_correction(gdir) tasks.process_cru_data(gdir) local_t_star(gdir) mu_star_calibration(gdir) tasks.prepare_for_inversion(gdir) facs = np.append((np.arange(9) + 1) * 0.1, (np.arange(19) + 2) * 0.5) vols1 = facs * 0. vols2 = facs * 0. vols3 = facs * 0. vols4 = facs * 0. vols5 = facs * 0. vols6 = facs * 0. vols7 = facs * 0. vols8 = facs * 0. glen_a = cfg.PARAMS['glen_a']
vas_model = vascaling.VAScalingModel(year_0=y0, area_m2_0=a0, min_hgt=h0, max_hgt=h1, mb_model=vas_mb_mod) years_vas, length_m_vas, area_m2_vas, volume_m3_vas, min_hgt_vas, spec_mb_vas \ = vas_model.run_until(y1) # ------ # OGGM # ------ # compute local t* and the corresponding mu* climate.local_t_star(gdir) climate.mu_star_calibration(gdir) from oggm.core import massbalance # instance the mass balance models mb_mod = massbalance.PastMassBalance(gdir) from oggm.core import inversion inversion.prepare_for_inversion(gdir) inversion.mass_conservation_inversion(gdir) inversion.filter_inversion_output(gdir) from oggm.core import flowline # initialize present time glacier flowline.init_present_time_glacier(gdir)
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)