main_glac_hyps[main_glac_icethickness == 0] = 0 # Width [km], average main_glac_width = modelsetup.import_Husstable(main_glac_rgi, pygem_prms.width_filepath, pygem_prms.width_filedict, pygem_prms.width_colsdrop) # Add volume [km**3] and mean elevation [m a.s.l.] to the main glaciers table main_glac_rgi['Volume'], main_glac_rgi['Zmean'] = modelsetup.hypsometrystats( main_glac_hyps, main_glac_icethickness) # Model time frame dates_table = modelsetup.datesmodelrun(startyear, endyear, spinupyears) # Quality control - if ice thickness = 0, glacier area = 0 (problem identified by glacier RGIV6-15.00016 03/06/2018) main_glac_hyps[main_glac_icethickness == 0] = 0 #%% ===== LOAD CLIMATE DATA ===== gcm = class_climate.GCM(name=gcm_name) if option_gcm_downscale == 1: # Air Temperature [degC] and GCM dates gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table) # Precipitation [m] and GCM dates gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table) # Elevation [m a.s.l] associated with air temperature and precipitation data gcm_elev = gcm.importGCMfxnearestneighbor_xarray(gcm.elev_fn, gcm.elev_vn, main_glac_rgi) # Air temperature standard deviation if pygem_prms.option_ablation != 2: gcm_tempstd = np.zeros(gcm_temp.shape) elif gcm_name in ['ERA5']: gcm_tempstd, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(
def main(list_packed_vars): """ Climate data bias adjustment Parameters ---------- list_packed_vars : list list of packed variables that enable the use of parallels Returns ------- csv files of bias adjustment output The bias adjustment parameters are output instead of the actual temperature and precipitation to reduce file sizes. Additionally, using the bias adjustment will cause the GCM climate data to use the reference elevation since the adjustments were made from the GCM climate data to be consistent with the reference dataset. """ # Unpack variables count = list_packed_vars[0] chunk = list_packed_vars[1] main_glac_rgi_all = list_packed_vars[2] chunk_size = list_packed_vars[3] gcm_name = list_packed_vars[4] time_start = time.time() parser = getparser() args = parser.parse_args() if (gcm_name != pygem_prms.ref_gcm_name) and (args.rcp is None): rcp_scenario = os.path.basename(args.gcm_list_fn).split('_')[1] elif args.rcp is not None: rcp_scenario = args.rcp # rcp_scenario = os.path.basename(args.gcm_file).split('_')[1] # ===== LOAD OTHER GLACIER DATA ===== main_glac_rgi = main_glac_rgi_all.iloc[chunk:chunk + chunk_size, :] # Glacier hypsometry [km**2], total area main_glac_hyps = modelsetup.import_Husstable(main_glac_rgi, pygem_prms.hyps_filepath, pygem_prms.hyps_filedict, pygem_prms.hyps_colsdrop) # Ice thickness [m], average main_glac_icethickness = modelsetup.import_Husstable(main_glac_rgi, pygem_prms.thickness_filepath, pygem_prms.thickness_filedict, pygem_prms.thickness_colsdrop) main_glac_hyps[main_glac_icethickness == 0] = 0 # Width [km], average main_glac_width = modelsetup.import_Husstable(main_glac_rgi, pygem_prms.width_filepath, pygem_prms.width_filedict, pygem_prms.width_colsdrop) elev_bins = main_glac_hyps.columns.values.astype(int) # Select dates including future projections # If reference climate data starts or ends before or after the GCM data, then adjust reference climate data such # that the reference and GCM span the same period of time. if pygem_prms.startyear >= pygem_prms.gcm_startyear: ref_startyear = pygem_prms.startyear else: ref_startyear = pygem_prms.gcm_startyear if pygem_prms.endyear <= pygem_prms.gcm_endyear: ref_endyear = pygem_prms.endyear else: ref_endyear = pygem_prms.gcm_endyear dates_table_ref = modelsetup.datesmodelrun(startyear=ref_startyear, endyear=ref_endyear, spinupyears=pygem_prms.ref_spinupyears, option_wateryear=pygem_prms.ref_wateryear) dates_table = modelsetup.datesmodelrun(startyear=pygem_prms.gcm_startyear, endyear=pygem_prms.gcm_endyear, spinupyears=pygem_prms.gcm_spinupyears, option_wateryear=pygem_prms.gcm_wateryear) # ===== LOAD CLIMATE DATA ===== # Reference climate data ref_gcm = class_climate.GCM(name=pygem_prms.ref_gcm_name) # Air temperature [degC], Precipitation [m], Elevation [masl], Lapse rate [K m-1] ref_temp, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray(ref_gcm.temp_fn, ref_gcm.temp_vn, main_glac_rgi, dates_table_ref) ref_prec, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray(ref_gcm.prec_fn, ref_gcm.prec_vn, main_glac_rgi, dates_table_ref) ref_elev = ref_gcm.importGCMfxnearestneighbor_xarray(ref_gcm.elev_fn, ref_gcm.elev_vn, main_glac_rgi) ref_lr, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray(ref_gcm.lr_fn, ref_gcm.lr_vn, main_glac_rgi, dates_table_ref) ref_lr_monthly_avg = (ref_lr.reshape(-1,12).transpose().reshape(-1,int(ref_temp.shape[1]/12)).mean(1) .reshape(12,-1).transpose()) # GCM climate data if gcm_name == 'ERA-Interim' or gcm_name == 'COAWST': gcm = class_climate.GCM(name=gcm_name) else: gcm = class_climate.GCM(name=gcm_name, rcp_scenario=rcp_scenario) # Air temperature [degC], Precipitation [m], Elevation [masl], Lapse rate [K m-1] gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table) gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table) gcm_elev = gcm.importGCMfxnearestneighbor_xarray(gcm.elev_fn, gcm.elev_vn, main_glac_rgi) if gcm_name == 'ERA-Interim': gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table) else: gcm_lr = monthly_avg_array_rolled(ref_lr, dates_table_ref, dates_table) # COAWST data has two domains, so need to merge the two domains if gcm_name == 'COAWST': gcm_temp_d01, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.temp_fn_d01, gcm.temp_vn, main_glac_rgi, dates_table) gcm_prec_d01, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.prec_fn_d01, gcm.prec_vn, main_glac_rgi, dates_table) gcm_elev_d01 = gcm.importGCMfxnearestneighbor_xarray(gcm.elev_fn_d01, gcm.elev_vn, main_glac_rgi) # Check if glacier outside of high-res (d02) domain for glac in range(main_glac_rgi.shape[0]): glac_lat = main_glac_rgi.loc[glac,pygem_prms.rgi_lat_colname] glac_lon = main_glac_rgi.loc[glac,pygem_prms.rgi_lon_colname] if (~(pygem_prms.coawst_d02_lat_min <= glac_lat <= pygem_prms.coawst_d02_lat_max) or ~(pygem_prms.coawst_d02_lon_min <= glac_lon <= pygem_prms.coawst_d02_lon_max)): gcm_prec[glac,:] = gcm_prec_d01[glac,:] gcm_temp[glac,:] = gcm_temp_d01[glac,:] gcm_elev[glac] = gcm_elev_d01[glac] #%% ===== BIAS CORRECTIONS ===== # OPTION 1: Adjust temp and prec similar to Huss and Hock (2015) but limit maximum precipitation # - temperature accounts for means and interannual variability # - precipitation corrects if pygem_prms.option_bias_adjustment == 1: # Temperature bias correction gcm_temp_biasadj, gcm_elev_biasadj = temp_biasadj_HH2015(ref_temp, ref_elev, gcm_temp, dates_table_ref, dates_table) # Precipitation bias correction gcm_prec_biasadj, gcm_elev_biasadj = prec_biasadj_opt1(ref_prec, ref_elev, gcm_prec, dates_table_ref, dates_table) # OPTION 2: Adjust temp and prec according to Huss and Hock (2015) accounts for means and interannual variability elif pygem_prms.option_bias_adjustment == 2: # Temperature bias correction gcm_temp_biasadj, gcm_elev_biasadj = temp_biasadj_HH2015(ref_temp, ref_elev, gcm_temp, dates_table_ref, dates_table) # Precipitation bias correction gcm_prec_biasadj, gcm_elev_biasadj = prec_biasadj_HH2015(ref_prec, ref_elev, gcm_prec, dates_table_ref, dates_table) if gcm_prec_biasadj.max() > 10: print('precipitation bias too high, needs to be modified') print(np.where(gcm_prec_biasadj > 10)) elif gcm_prec_biasadj.min() < 0: print('Negative precipitation value') print(np.where(gcm_prec_biasadj < 0)) #%% PLOT BIAS ADJUSTED DATA if option_plot_adj: print('plotting') plot_biasadj(ref_temp, gcm_temp_biasadj, ref_prec, gcm_prec, gcm_prec_biasadj, dates_table_ref, dates_table) #%% Export variables as global to view in variable explorer if args.option_parallels == 0: global main_vars main_vars = inspect.currentframe().f_locals print('\nProcessing time of', gcm_name, 'for', count,':',time.time()-time_start, 's')
#%% #gcm_list = ['CanESM2', 'CCSM4', 'CSIRO-Mk3-6-0', 'CNRM-CM5', 'GFDL-CM3', 'GFDL-ESM2M', 'GISS-E2-R', 'IPSL-CM5A-LR', # 'MPI-ESM-LR', 'NorESM1-M'] gcm_list = ['CSIRO-Mk3-6-0', 'CNRM-CM5', 'GISS-E2-R', 'GFDL-ESM2M', 'CCSM4', 'MPI-ESM-LR', 'NorESM1-M', 'CanESM2', 'GFDL-CM3', 'IPSL-CM5A-LR'] #gcm_list = ['CanESM2'] rcp_list = ['rcp26', 'rcp45', 'rcp85'] RCP_list = ['RCP 2.6', 'RCP 4.5', 'RCP 8.5'] fig, ax = plt.subplots(2, 3, squeeze=False, sharex='col', sharey='row', gridspec_kw = {'wspace':0.1, 'hspace':0.15}) for j in range(len(rcp_list)): for i in range(len(gcm_list)): gcm = class_climate.GCM(name=gcm_list[i], rcp_scenario=rcp_list[j]) main_glac_rgi = modelsetup.selectglaciersrgitable(rgi_regionsO1=input.rgi_regionsO1, rgi_regionsO2 = 'all', rgi_glac_number = 'all') dates_table = modelsetup.datesmodelrun(startyear=2000, endyear=2100, spinupyears=0) time = np.linspace(2000, 2100, 101, dtype=int) gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table) gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray(gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table) gcm_temp_annual = pygemfxns.annual_avg_2darray(gcm_temp) gcm_prec_annual = pygemfxns.annual_sum_2darray(gcm_prec) x_values = time y_values = gcm_temp_annual[0] y2_values = gcm_prec_annual[0]
def main(list_packed_vars): """ Model calibration Parameters ---------- list_packed_vars : list list of packed variables that enable the use of parallels Returns ------- netcdf files of the calibration output Depending on the calibration scheme additional output may be exported as well """ #%% # Unpack variables main_glac_rgi = list_packed_vars[0] gcm_name = list_packed_vars[1] time_start = time.time() parser = getparser() args = parser.parse_args() if args.debug == 1: debug = True else: debug = False # ===== MASS BALANCE DATA AND DATES TABLE ===== data_source = 'regional' # data_source = 'individual_glaciers' if data_source in ['individual_glaciers']: dates_table = modelsetup.datesmodelrun(startyear=2000, endyear=2018, spinupyears=0) mb_shean_fullfn = pygem_prms.shean_fp + pygem_prms.shean_fn mb_shean_df = pd.read_csv(mb_shean_fullfn) mb_shean_df['RGIId'] = [ 'RGI60-' + str(int(x)) + '.' + str(int(np.round((x - int(x)) * 1e5, 0))).zfill(5) for x in mb_shean_df.RGIId.values ] elif data_source in ['regional']: dates_table = modelsetup.datesmodelrun(startyear=2006, endyear=2015, spinupyears=0) roi_mbobs_dict = { '01': [-0.70, 0.18], '02': [-0.50, 0.91], '03': [-0.38, 0.80], '04': [-0.80, 0.22], '05': [-0.57, 0.20], '06': [-0.69, 0.26], '07': [-0.27, 0.17], '08': [-0.66, 0.27], '09': [-0.30, 0.27], '10': [-0.40, 0.31], '11': [-0.91, 0.70], '12': [-0.88, 0.57], '13': [-0.19, 0.15], '14': [-0.11, 0.15], '15': [-0.44, 0.15], '16': [-0.59, 0.58], '17': [-0.86, 0.17], '18': [-0.59, 1.14] } # ===== LOAD CLIMATE DATA ===== gcm = class_climate.GCM(name=gcm_name) # Air temperature [degC], Air temperature Std [K], Precipitation [m], Elevation [masl], Lapse rate [K m-1] gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table) gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table) gcm_elev = gcm.importGCMfxnearestneighbor_xarray(gcm.elev_fn, gcm.elev_vn, main_glac_rgi) # Air temperature standard deviation [K] if pygem_prms.option_ablation != 2 or gcm_name not in ['ERA5']: gcm_tempstd = np.zeros(gcm_temp.shape) elif gcm_name in ['ERA5']: gcm_tempstd, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table) # Lapse rate [K m-1] if gcm_name in ['ERA-Interim', 'ERA5']: gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table) else: # Mean monthly lapse rate ref_lr_monthly_avg = np.genfromtxt(gcm.lr_fp + gcm.lr_fn, delimiter=',') gcm_lr = np.tile(ref_lr_monthly_avg, int(gcm_temp.shape[1] / 12)) # Huss and Hock (2015) parameters and bounds tempchange_init = 0 tempchange_bndlow = -10 tempchange_bndhigh = 10 precfactor_init = 1.5 precfactor_bndlow = 0.8 precfactor_bndhigh = 2 ddfsnow_init = 0.003 ddfsnow_bndlow = 0.00175 ddfsnow_bndhigh = 0.0045 ddfsnow_iceratio = 0.5 # Area change rate dictionary to account for clean ice retreat scenario area_chg_rate_dict = { '01': [-0.42, 0.23], '02': [-0.54, 0.24], '03': [-0.07, 0.03], '04': [-0.08, 0.05], '05': [-0.18, 0.23], '06': [-0.58, 0.23], '07': [-0.26, 0.23], '08': [-0.18, 0.11], '09': [-0.18, 0.23], '10': [-0.52, 0.67], '11': [-0.93, 0.47], '12': [-0.18, 0.23], '13': [-0.18, 0.17], '14': [-0.36, 0.08], '15': [-0.47, 0.13], '16': [-1.19, 0.54], '17': [-0.20, 0.08], '18': [-0.69, 0.23] } for nglac, rgiid in enumerate(main_glac_rgi.rgino_str): if debug: print(nglac, rgiid) # ===== LOAD GLACIER DATA ===== binnedcsv = pd.read_csv(main_glac_rgi.loc[nglac, 'binned_fullfn']) if prescribe_retreat: roi = rgiid.split('.')[0].zfill(2) dc_perc_min = area_chg_rate_dict[roi][0] * ( main_glac_rgi.loc[nglac, 'RefYear'] - 2015) binnedcsv['area_cumsum_%'] = ( np.cumsum(binnedcsv.z1_bin_area_valid_km2) / binnedcsv.z1_bin_area_valid_km2.sum() * 100) binnedcsv = binnedcsv[ binnedcsv['area_cumsum_%'] > dc_perc_min].copy() binnedcsv.reset_index(inplace=True, drop=True) # Elevation bin statistics elev_bins = binnedcsv['bin_center_elev_m'].values bins_area = binnedcsv['z1_bin_area_valid_km2'].values main_glac_rgi['Zmed'] = weighted_percentile(elev_bins, bins_area, 0.5) main_glac_rgi['Zmin'] = elev_bins.min() main_glac_rgi['Zmax'] = elev_bins.max() # Set model parameters modelparameters = [ pygem_prms.lrgcm, pygem_prms.lrglac, precfactor_init, pygem_prms.precgrad, ddfsnow_init, ddfsnow_init / ddfsnow_iceratio, pygem_prms.tempsnow, tempchange_init ] # Select subsets of data glacier_rgi_table = main_glac_rgi.loc[ main_glac_rgi.index.values[nglac], :] glacier_gcm_elev = gcm_elev[nglac] glacier_gcm_prec = gcm_prec[nglac, :] glacier_gcm_temp = gcm_temp[nglac, :] glacier_gcm_tempstd = gcm_tempstd[nglac, :] glacier_gcm_lrgcm = gcm_lr[nglac, :] glacier_gcm_lrglac = glacier_gcm_lrgcm.copy() glacier_area_initial = binnedcsv['z1_bin_area_valid_km2'].values icethickness_initial = binnedcsv['H_mean'] glacier_debrismf = binnedcsv[mf_cn].values # Glacier widths (from OGGM) oggm_widths_fp = (pygem_prms.main_directory + '/../oggm_widths/' + main_glac_rgi.loc[nglac, 'RGIId'].split('.')[0] + '/') widths_fn = main_glac_rgi.loc[nglac, 'RGIId'] + '_widths_m.csv' try: # Add width (km) to each elevation bin widths_df = pd.read_csv(oggm_widths_fp + widths_fn) elev_nearidx = (np.abs( np.array(elev_bins)[:, np.newaxis] - widths_df['elev'].values).argmin(axis=1)) width_initial = widths_df.loc[elev_nearidx, 'width_m'].values / 1000 except: width_initial = np.zeros(glacier_area_initial.shape[0]) # Mass balance data if data_source in ['individual_glaciers']: assert glacier_rgi_table.O1Region in [ 13, 14, 15 ], 'Individual mb data not available' mb_shean_idx = np.where( glacier_rgi_table.RGIId == mb_shean_df.RGIId.values)[0][0] observed_massbal = mb_shean_df.loc[mb_shean_idx, 'mb_mwea'] observed_massbal_std = mb_shean_df.loc[mb_shean_idx, 'mb_mwea_sigma'] elif data_source in ['regional']: observed_massbal = roi_mbobs_dict[str( glacier_rgi_table.O1Region).zfill(2)][0] observed_massbal_std = roi_mbobs_dict[str( glacier_rgi_table.O1Region).zfill(2)][1] t1_idx = 0 t2_idx = dates_table.shape[0] if debug: print('obs_mwea:', np.round(observed_massbal, 2), '+/-', np.round(observed_massbal_std, 2)) # #%% ## dates_table = dates_table.loc[0:11,:] ## glacier_gcm_temp = glacier_gcm_temp[0:12] ## glacier_gcm_tempstd = glacier_gcm_tempstd[0:12] ## glacier_gcm_prec = glacier_gcm_prec[0:12] ## glacier_gcm_lrgcm = glacier_gcm_lrgcm[0:12] ## glacier_gcm_lrglac = glacier_gcm_lrglac[0:12] # option_areaconstant = 1 # (glac_bin_temp, glac_bin_prec, glac_bin_acc, glac_bin_refreeze, glac_bin_snowpack, glac_bin_melt, # glac_bin_frontalablation, glac_bin_massbalclim, glac_bin_massbalclim_annual, glac_bin_area_annual, # glac_bin_icethickness_annual, glac_bin_width_annual, glac_bin_surfacetype_annual, # glac_wide_massbaltotal, glac_wide_runoff, glac_wide_snowline, glac_wide_snowpack, # glac_wide_area_annual, glac_wide_volume_annual, glac_wide_ELA_annual, offglac_wide_prec, # offglac_wide_refreeze, offglac_wide_melt, offglac_wide_snowpack, offglac_wide_runoff) = ( # massbalance.runmassbalance(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, # width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, # glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, # option_areaconstant=option_areaconstant, glacier_debrismf=glacier_debrismf, # debug=True)) # # # Compute glacier volume change for every time step and use this to compute mass balance # glac_wide_area = glac_wide_area_annual[:-1].repeat(12) # # Mass change [km3 mwe] # # mb [mwea] * (1 km / 1000 m) * area [km2] # glac_wide_masschange = glac_wide_massbaltotal[t1_idx:t2_idx+1] / 1000 * glac_wide_area[t1_idx:t2_idx+1] # # Mean annual mass balance [mwea] # mb_mwea = (glac_wide_masschange.sum() / glac_wide_area[0] * 1000 / (glac_wide_masschange.shape[0] / 12)) # ## mb_mwea = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, ## width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, ## glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, ## option_areaconstant=1, glacier_debrismf=glacier_debrismf) # print(np.round(mb_mwea,2)) #%% # Huss and Hock (2015) model calibration steps if pygem_prms.option_calibration == 3: def objective(modelparameters_subset): """ Objective function for mass balance data. Parameters ---------- modelparameters_subset : list of model parameters to calibrate [precipitation factor, precipitation gradient, degree-day factor of snow, temperature bias] Returns ------- mb_dif_mwea : difference in modeled vs observed mass balance [mwea] """ # Use a subset of model parameters to reduce number of constraints required modelparameters[2] = modelparameters_subset[0] modelparameters[3] = modelparameters_subset[1] modelparameters[4] = modelparameters_subset[2] modelparameters[5] = modelparameters[4] / ddfsnow_iceratio modelparameters[7] = modelparameters_subset[3] mb_mwea = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) # Differnece [mwea] = Observed mass balance [mwea] - mb_mwea mb_dif_mwea_abs = abs(observed_massbal - mb_mwea) return mb_dif_mwea_abs def run_objective(modelparameters_init, observed_massbal, precfactor_bnds=(0.33, 3), tempchange_bnds=(-10, 10), ddfsnow_bnds=(0.0026, 0.0056), precgrad_bnds=(0.0001, 0.0001), run_opt=True, ftol_opt=pygem_prms.ftol_opt): """ Run the optimization for the single glacier objective function. Parameters ---------- modelparams_init : list List of model parameters to calibrate [precipitation factor, precipitation gradient, degree day factor of snow, temperature change] precfactor_bnds, tempchange_bnds, ddfsnow_bnds, precgrad_bnds : tuples Lower and upper bounds for various model parameters run_opt : boolean Boolean statement allowing one to bypass the optimization and run through with initial parameters (default is True - run the optimization) Returns ------- modelparams : list of model parameters mb_mwea : optimized modeled mass balance (mwea) """ # Bounds modelparameters_bnds = (precfactor_bnds, precgrad_bnds, ddfsnow_bnds, tempchange_bnds) # Run the optimization # 'L-BFGS-B' - much slower # 'SLSQP' did not work for some geodetic measurements using the sum_abs_zscore. One work around was to # divide the sum_abs_zscore by 1000, which made it work in all cases. However, methods were switched # to 'L-BFGS-B', which may be slower, but is still effective. # note: switch enables running through with given parameters if run_opt: modelparameters_opt = minimize(objective, modelparameters_init, method=pygem_prms.method_opt, bounds=modelparameters_bnds, options={'ftol': ftol_opt}) # Record the optimized parameters modelparameters_subset = modelparameters_opt.x else: modelparameters_subset = modelparameters_init.copy() modelparams = ([ modelparameters[0], modelparameters[1], modelparameters_subset[0], modelparameters_subset[1], modelparameters_subset[2], modelparameters_subset[2] / ddfsnow_iceratio, modelparameters[6], modelparameters_subset[3] ]) # Re-run the optimized parameters in order to see the mass balance mb_mwea = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) return modelparams, mb_mwea continue_param_search = True # ===== ROUND 1: PRECIPITATION FACTOR ====== if debug: print('Round 1:') # Lower bound modelparameters[2] = precfactor_bndlow mb_mwea_kp_low = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) # Upper bound modelparameters[2] = precfactor_bndhigh mb_mwea_kp_high = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) # Optimimum precipitation factor if observed_massbal < mb_mwea_kp_low: precfactor_opt = precfactor_bndlow mb_mwea = mb_mwea_kp_low elif observed_massbal > mb_mwea_kp_high: precfactor_opt = precfactor_bndhigh mb_mwea = mb_mwea_kp_high else: modelparameters[2] = precfactor_init modelparameters_subset = [ modelparameters[2], modelparameters[3], modelparameters[4], modelparameters[7] ] precfactor_bnds = (precfactor_bndlow, precfactor_bndhigh) ddfsnow_bnds = (ddfsnow_init, ddfsnow_init) tempchange_bnds = (tempchange_init, tempchange_init) modelparams, mb_mwea = run_objective( modelparameters_subset, observed_massbal, precfactor_bnds=precfactor_bnds, tempchange_bnds=tempchange_bnds, ddfsnow_bnds=ddfsnow_bnds, ftol_opt=1e-3) precfactor_opt = modelparams[2] continue_param_search = False # Update parameter values modelparameters[2] = precfactor_opt if debug: print(' kp:', np.round(precfactor_opt, 2), 'mb_mwea:', np.round(mb_mwea, 2)) # ===== ROUND 2: DEGREE-DAY FACTOR OF SNOW ====== if continue_param_search: if debug: print('Round 2:') # Lower bound modelparameters[4] = ddfsnow_bndlow modelparameters[5] = modelparameters[4] / ddfsnow_iceratio mb_mwea_ddflow = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) # Upper bound modelparameters[4] = ddfsnow_bndhigh modelparameters[5] = modelparameters[4] / ddfsnow_iceratio mb_mwea_ddfhigh = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) # Optimimum degree-day factor of snow if observed_massbal < mb_mwea_ddfhigh: ddfsnow_opt = ddfsnow_bndhigh mb_mwea = mb_mwea_ddfhigh elif observed_massbal > mb_mwea_ddflow: ddfsnow_opt = ddfsnow_bndlow mb_mwea = mb_mwea_ddflow else: modelparameters_subset = [ precfactor_opt, modelparameters[3], modelparameters[4], modelparameters[7] ] precfactor_bnds = (precfactor_opt, precfactor_opt) ddfsnow_bnds = (ddfsnow_bndlow, ddfsnow_bndhigh) tempchange_bnds = (tempchange_init, tempchange_init) modelparams, mb_mwea = run_objective( modelparameters_subset, observed_massbal, precfactor_bnds=precfactor_bnds, tempchange_bnds=tempchange_bnds, ddfsnow_bnds=ddfsnow_bnds, ftol_opt=1e-5) ddfsnow_opt = modelparams[4] continue_param_search = False # Update parameter values modelparameters[4] = ddfsnow_opt modelparameters[5] = modelparameters[4] / ddfsnow_iceratio if debug: print(' ddfsnow:', np.round(ddfsnow_opt, 4), 'mb_mwea:', np.round(mb_mwea, 2)) else: ddfsnow_opt = modelparams[4] # ===== ROUND 3: TEMPERATURE BIAS ====== if continue_param_search: if debug: print('Round 3:') tc_step = 0.5 # ----- TEMPBIAS: max accumulation ----- # Lower temperature bound based on no positive temperatures # Temperature at the lowest bin # T_bin = T_gcm + lr_gcm * (z_ref - z_gcm) + lr_glac * (z_bin - z_ref) + tempchange lowest_bin = np.where(glacier_area_initial > 0)[0][0] tempchange_max_acc = ( -1 * (glacier_gcm_temp + glacier_gcm_lrgcm * (elev_bins[lowest_bin] - glacier_gcm_elev)).max()) tempchange_bndlow = tempchange_max_acc modelparameters[7] = tempchange_bndlow mb_mwea = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) if debug: print(' tc_bndlow:', np.round(tempchange_bndlow, 2), 'mb_mwea:', np.round(mb_mwea, 2)) while mb_mwea > observed_massbal and modelparameters[7] < 20: modelparameters[7] = modelparameters[7] + tc_step mb_mwea = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) if debug: print(' tc:', np.round(modelparameters[7], 2), 'mb_mwea:', np.round(mb_mwea, 2)) tempchange_bndhigh = modelparameters[7] modelparameters_subset = [ precfactor_opt, modelparameters[3], ddfsnow_opt, modelparameters[7] - tc_step / 2 ] precfactor_bnds = (precfactor_opt, precfactor_opt) ddfsnow_bnds = (ddfsnow_opt, ddfsnow_opt) tempchange_bnds = (tempchange_bndlow, tempchange_bndhigh) modelparams, mb_mwea = run_objective( modelparameters_subset, observed_massbal, precfactor_bnds=precfactor_bnds, tempchange_bnds=tempchange_bnds, ddfsnow_bnds=ddfsnow_bnds, ftol_opt=1e-3) # Update parameter values tc_opt = modelparams[7] modelparameters[7] = tc_opt if debug: print(' tc:', np.round(tc_opt, 3), 'mb_mwea:', np.round(mb_mwea, 2)) else: tc_opt = modelparams[7] #%% mb_mwea = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=glacier_debrismf) mb_mwea_nodebris = mb_mwea_calc(modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, t1_idx, t2_idx, option_areaconstant=1, glacier_debrismf=None) if debug: print('mod_mwea:', np.round(mb_mwea, 2), 'no debris:', np.round(mb_mwea_nodebris, 2)) if abs(mb_mwea - observed_massbal) > observed_massbal_std: print(rgiid, ' check as observed mass balance not close') # Skip and replace with observation for both to not skew results # ex. 3.02860 - frontal ablation appears too much causing far too negative mass balance troubleshoot_fp = pygem_prms.output_fp_cal + '_4debrispaper/' + data_source + '/errors/' if not os.path.exists(troubleshoot_fp): os.makedirs(troubleshoot_fp) txt_fn = rgiid + "-mb_agreement_notfound.txt" with open(troubleshoot_fp + txt_fn, "w") as text_file: text_file.write(rgiid + ' mass balance not close to observation (' + str(np.round(mb_mwea, 2)) + ' vs. ' + str(np.round(observed_massbal, 2)) + '), so replaced with observed mb') mb_mwea = observed_massbal mb_mwea_nodebris = observed_massbal # ===== EXPORT RESULTS ===== output_cns = [ 'RGIId', 'area_km2', 'kp', 'ddfsnow', 'ddfice', 'tc', 'tsnow', 'obs_mwea', 'obs_mwea_std', 'mod_mwea', 'mod_mwea_nodebris' ] output_df = pd.DataFrame(np.zeros((1, len(output_cns))), columns=output_cns) output_df['RGIId'] = rgiid output_df['area_km2'] = binnedcsv.z1_bin_area_valid_km2.sum() print(binnedcsv.z1_bin_area_valid_km2.sum(), bins_area.sum(), main_glac_rgi.loc[nglac, 'Area']) output_df['kp'] = precfactor_opt output_df['ddfsnow'] = ddfsnow_opt output_df['ddfice'] = ddfsnow_opt / ddfsnow_iceratio output_df['tc'] = tc_opt output_df['tsnow'] = pygem_prms.tempsnow output_df['obs_mwea'] = observed_massbal output_df['obs_mwea_std'] = observed_massbal_std output_df['mod_mwea'] = mb_mwea output_df['mod_mwea_nodebris'] = mb_mwea_nodebris # EXPORT TO NETCDF if mf_cn == 'mf_ts_mean_bndlow': mf_str = 'bndlow_' elif mf_cn == 'mf_ts_mean_bndhigh': mf_str = 'bndhigh_' else: mf_str = '' output_fp = (pygem_prms.output_fp_cal + '_4debrispaper/' + mf_str + data_source + '/' + str(glacier_rgi_table.O1Region).zfill(2) + '/') if not os.path.exists(output_fp): os.makedirs(output_fp) output_fn = rgiid + '_HH2015_wdebris.csv' output_df.to_csv(output_fp + output_fn, index=False) #%% ===== CALCULATE MASS BALANCE WITH AND WITHOUT DEBRIS ===== # modelparameters = [-0.0065, -0.0065, 0.8, 0.0001, 0.0045, 0.009, 1.0, 1.067748599396429] # print(modelparameters) (glac_bin_temp, glac_bin_prec, glac_bin_acc, glac_bin_refreeze, glac_bin_snowpack, glac_bin_melt, glac_bin_frontalablation, glac_bin_massbalclim, glac_bin_massbalclim_annual, glac_bin_area_annual, glac_bin_icethickness_annual, glac_bin_width_annual, glac_bin_surfacetype_annual, glac_wide_massbaltotal, glac_wide_runoff, glac_wide_snowline, glac_wide_snowpack, glac_wide_area_annual, glac_wide_volume_annual, glac_wide_ELA_annual, offglac_wide_prec, offglac_wide_refreeze, offglac_wide_melt, offglac_wide_snowpack, offglac_wide_runoff) = (massbalance.runmassbalance( modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, option_areaconstant=1, glacier_debrismf=glacier_debrismf, debug=False)) glac_bin_massbalclim_annual_mean_wdebris = glac_bin_massbalclim_annual.mean( axis=1) (glac_bin_temp, glac_bin_prec, glac_bin_acc, glac_bin_refreeze, glac_bin_snowpack, glac_bin_melt, glac_bin_frontalablation, glac_bin_massbalclim, glac_bin_massbalclim_annual, glac_bin_area_annual, glac_bin_icethickness_annual, glac_bin_width_annual, glac_bin_surfacetype_annual, glac_wide_massbaltotal, glac_wide_runoff, glac_wide_snowline, glac_wide_snowpack, glac_wide_area_annual, glac_wide_volume_annual, glac_wide_ELA_annual, offglac_wide_prec, offglac_wide_refreeze, offglac_wide_melt, offglac_wide_snowpack, offglac_wide_runoff) = (massbalance.runmassbalance( modelparameters, glacier_rgi_table, glacier_area_initial, icethickness_initial, width_initial, elev_bins, glacier_gcm_temp, glacier_gcm_tempstd, glacier_gcm_prec, glacier_gcm_elev, glacier_gcm_lrgcm, glacier_gcm_lrglac, dates_table, option_areaconstant=1, glacier_debrismf=None, debug=False)) glac_bin_massbalclim_annual_mean_nodebris = glac_bin_massbalclim_annual.mean( axis=1) mbclim_output_cns = [ 'elev', 'area', 'mf', 'mbclim_mwea_wdebris', 'mbclim_mwea_nodebris', 'frontalablation' ] mbclim_output_df = pd.DataFrame(np.zeros( (len(elev_bins), len(mbclim_output_cns))), columns=mbclim_output_cns) mbclim_output_df['elev'] = elev_bins mbclim_output_df['area'] = bins_area mbclim_output_df['mf'] = glacier_debrismf mbclim_output_df[ 'mbclim_mwea_wdebris'] = glac_bin_massbalclim_annual_mean_wdebris mbclim_output_df[ 'mbclim_mwea_nodebris'] = glac_bin_massbalclim_annual_mean_nodebris mbclim_output_df['frontalablation'] = glac_bin_frontalablation.mean(1) mbclim_output_fn = rgiid + '_mbclim_data.csv' mbclim_output_fp = (pygem_prms.output_fp_cal + '_4debrispaper/' + mf_str + data_source + '-mbclim' + '/' + str(glacier_rgi_table.O1Region).zfill(2) + '/') if not os.path.exists(mbclim_output_fp): os.makedirs(mbclim_output_fp) mbclim_output_df.to_csv(mbclim_output_fp + mbclim_output_fn, index=False) if debug: return main_glac_rgi # # Export variables as global to view in variable explorers # if args.option_parallels == 0: # global main_vars # main_vars = inspect.currentframe().f_locals print('\nProcessing time of', gcm_name, ':', time.time() - time_start, 's')