def test_load_snb_parms(): fname = resource_filename('conductor', 'tests/input/snow_band.txt') cells = load_snb_parms(fname, 15) assert len(cells) == 6 assert len(cells['369560']) == 15 expected_zs = [ 2076, 2159, 2264, 2354, 2451, 2550, 2620, 2714, 2802,\ 2900, 3000, 3100, 3200, 3300, 3400 ] zs = [ band.median_elev for band in cells['368470'] ] assert zs == expected_zs
def test_load_snb_parms(): fname = resource_filename('conductor', 'tests/input/snow_band.txt') cells = load_snb_parms(fname, 15) assert len(cells) == 6 assert len(cells['369560']) == 15 expected_zs = [ 2076, 2159, 2264, 2354, 2451, 2550, 2620, 2714, 2802,\ 2900, 3000, 3100, 3200, 3300, 3400 ] zs = [band.median_elev for band in cells['368470']] assert zs == expected_zs
def large_merge_cells_unit_test_parms(): """ Uses input from a large real-world set of VIC snow band and vegetation parameter files to test merge_cells() """ fname = resource_filename('conductor', 'tests/input/snow_band.txt') elevation_cells = load_snb_parms(fname, 15) fname = resource_filename('conductor', 'tests/input/veg.txt') hru_cells = load_veg_parms(fname) expected_zs = [ 2076, 2159, 2264, 2354, 2451, 2550, 2620, 2714, 2802, 2900,\ 3000, 3100, 3200, 3300, 3400 ] expected_afs = { 0.000765462339, 0.000873527611, 0.009125511809,\ 0.009314626034, 0.004426673711, 0.004558753487, 0.001388838859, 0.000737445417 } return elevation_cells, hru_cells, expected_zs, expected_afs
def main(): print('\n\nVIC + RGM ... together at last!') # Parse command line parameters vic_global_file, rgm_params_file, surf_dem_in_file, bed_dem_file, \ pixel_cell_map_file, init_glacier_mask_file, output_trace_files, \ glacier_root_zone_parms, open_ground_root_zone_parms, band_size = parse_input_parms() # Get all initial VIC global parameters from the global parameter file with open(vic_global_file, 'r') as f: global_parms = Global(f) assert global_parms.state_format == 'NETCDF', \ "{} only supports NetCDF input statefile input as opposed "\ "to the specified {}. Please change change this in your "\ "global file {}".format(__name__, global_parms.state_format, vic_global_file) # Initial VIC output state filename prefix is determined by STATENAME in the global file state_filename_prefix = global_parms.statename # Apply custom glacier_id and open_ground_id Band attributes, if provided if global_parms.glacier_id is None: print( 'No value for GLACIER_ID was provided in the VIC global file. Assuming default value of {}.' .format(Band.glacier_id)) else: Band.glacier_id = global_parms.glacier_id # FIXME: reinstate the following commented-out code once OPEN_GROUND_ID is supported in VIC # Numeric code indicating an open ground vegetation tile (HRU) # if global_parms.open_ground_id is None: # print('No value for OPEN_GROUND_ID was provided in the VIC global file. Assuming default value of {}.'.format(Band.open_ground_id)) # else: # Band.open_ground_id = global_parms.open_ground_id if band_size: Band.band_size = band_size # Load parameters from Snow Band Parameters File num_snow_bands, snb_file = global_parms.snow_band.split() num_snow_bands = int(num_snow_bands) elevation_cell_dict = load_snb_parms(snb_file, num_snow_bands) # Load vegetation parameters from initial Vegetation Parameter File hru_cell_dict = vegparams.load_veg_parms(global_parms.vegparam) # Apply custom HRU root_zone_parms attributes, if provided if glacier_root_zone_parms or open_ground_root_zone_parms: cells.apply_custom_root_zone_parms(hru_cell_dict, glacier_root_zone_parms, open_ground_root_zone_parms) Band.glacier_root_zone_parms = glacier_root_zone_parms Band.open_ground_root_zone_parms = open_ground_root_zone_parms # Merge all VIC cells info gathered from Snow Band and Vegetation Parameter files and custom parameters cells = merge_cell_input(hru_cell_dict, elevation_cell_dict) # TODO: Do a sanity check to make sure band area fractions in Snow Band Parameters file add up to sum of HRU # area fractions in Vegetation Parameter File for each cell? #assert (area_fracs == [sums of HRU area fracs for all bands]) # The RGM will always output a DEM file of the same name (if running RGM for a single year at a time) rgm_surf_dem_out_file = temp_files_path + 's_out_00001.grd' # Open and read VIC-grid-to-RGM-pixel mapping file cellid_map, elevation_map, cell_areas, num_cols_dem, num_rows_dem = get_rgm_pixel_mapping( pixel_cell_map_file) # Get DEM xmin, xmax, ymin, ymax metadata of Bed DEM and check file header validity dem_xmin, dem_xmax, dem_ymin, dem_ymax, num_rows, num_cols = read_gsa_headers( bed_dem_file) # Verify number of columns & rows agree with what's stated in the pixel_to_cell_map_file assert (num_cols == num_cols_dem) and ( num_rows == num_rows_dem), 'Mismatch of stated dimension(s) \ between Bed DEM in {} (num rows: {}, num columns: {}) and the VIC-grid-to-RGM-pixel map in {} \ (num rows: {}, num columns: {}). Exiting.\n'.format( bed_dem_file, num_rows, num_cols, pixel_cell_map_file, num_rows_dem, num_cols_dem) # Read in the provided Bed Digital Elevation Map (BDEM) file to 2D bed_dem array bed_dem = np.loadtxt(bed_dem_file, skiprows=5) # Check header validity of Surface DEM file _, _, _, _, num_rows, num_cols = read_gsa_headers(surf_dem_in_file) # Verify number of columns & rows agree with what's stated in the pixel_to_cell_map_file assert (num_cols == num_cols_dem) and ( num_rows == num_rows_dem), 'Mismatch of stated dimension(s) \ between Surface DEM in {} (num rows: {}, num columns: {}) and the VIC-grid-to-RGM-pixel map in {} \ (num rows: {}, num columns: {}). Exiting.\n'.format( surf_dem_in_file, num_rows, num_cols, pixel_cell_map_file, num_rows_dem, num_cols_dem) # Read in the provided Surface Digital Elevation Map (SDEM) file to 2D surf_dem array surf_dem_initial = np.loadtxt(surf_dem_in_file, skiprows=5) # Check agreement between elevation map from VIC-grid-to-RGM-pixel file and the initial Surface DEM assert (np.equal(elevation_map, surf_dem_initial)), 'Values mismatch between provided initial Surface DEM \ file (num rows:{}, num columns:{}) and VIC-grid-to-RGM-pixel file (num rows:{}, num columns:{}). Exiting.\n'\ .format(num_rows, num_cols, num_rows_dem, num_cols_dem) # Check header validity of initial Glacier Mask file _, _, _, _, num_rows, num_cols = read_gsa_headers(init_glacier_mask_file) # Verify number of columns & rows agree with what's stated in the pixel_to_cell_map_file assert (num_cols == num_cols_dem) and ( num_rows == num_rows_dem), 'Mismatch of stated dimension(s) \ between Glacier Mask in {} (num rows: {}, num columns: {}) and the VIC-grid-to-RGM-pixel map in {} \ (num rows: {}, num columns: {}). Exiting.\n'.format( init_glacier_mask_file, num_rows, num_cols, pixel_cell_map_file, num_rows_dem, num_cols_dem) # Read in the provided initial glacier mask file to 2D glacier_mask array glacier_mask = np.loadtxt(init_glacier_mask_file, skiprows=5) # Apply the initial glacier mask and modify the band and glacier area fractions accordingly update_area_fracs(cells, cell_areas, cellid_map, num_snow_bands, surf_dem_initial, num_rows_dem, num_cols_dem, glacier_mask) temp_snb = temp_files_path + 'snb_temp_' + global_parms.startdate.isoformat( ) + '.txt' snbparams.save_snb_parms(cells, temp_snb, band_map) temp_vpf = temp_files_path + 'vpf_temp_' + global_parms.startdate.isoformat( ) + '.txt' vegparams.save_veg_parms(cells, temp_vpf) # Run the coupled VIC-RGM model for the time range specified in the VIC global parameters file time_iterator = run_ranges(global_parms.startdate, global_parms.enddate, global_parms.glacier_accum_startdate) for start, end in time_iterator: print('\nRunning VIC from {} to {}'.format(start, end)) # 1. Write / Update temporary Global Parameters File, temp_gpf temp_gpf = temp_files_path + 'gpf_temp_{}.txt'.format( start.isoformat()) # set global parameters for this VIC run global_parms.vegparm = temp_vpf global_parms.snow_band = '{} {}'.format(num_snow_bands, temp_snb) global_parms.startdate = start global_parms.enddate = end global_parms.statedate = end global_parms.write(temp_gpf) print('invoking VIC with global parameter file {}'.format(temp_gpf)) # 2. Run VIC for a year. This will save VIC model state at the end of the year, along with a Glacier Mass Balance (GMB) polynomial for each cell subprocess.check_call([vic_full_path, "-g", temp_gpf], shell=False, stderr=subprocess.STDOUT) # 3. Open VIC NetCDF state file and get the most recent GMB polynomial for each grid cell being modeled state_file = state_filename_prefix + "_" + start.isoformat() ## FIXME print('opening VIC state file {}'.format(state_file)) state = h5py.File(state_file, 'r+') gmb_polys = get_mass_balance_polynomials(state, state_file, cell_ids) # 4. Translate mass balances using grid cell GMB polynomials and current veg_parm_file into a 2D RGM mass balance grid (MBG) mass_balance_grid = mass_balances_to_rgm_grid(gmb_polys, pixel_to_cell_map, num_rows_dem, num_cols_dem, cell_ids) # write Mass Balance Grid to ASCII file to direct the RGM to use as input mbg_file = temp_files_path + 'mass_balance_grid_' + start.isoformat( ) + '.gsa' write_grid_to_gsa_file(mass_balance_grid, mbg_file, num_cols_dem, num_rows_dem, dem_xmin, dem_xmax, dem_ymin, dem_ymax) # 5. Run RGM for one year, passing MBG, BDEM, SDEM subprocess.check_call([ rgm_full_path, "-p", rgm_params_file, "-b", bed_dem_file, "-d", surf_dem_in_file, "-m", mbg_file, "-o", temp_files_path, "-s", "0", "-e", "0" ], shell=False, stderr=subprocess.STDOUT) # remove temporary files if not saving for offline inspection if not output_trace_files: os.remove(mbg_file) os.remove(rgm_surf_dem_file) # 6. Read in new Surface DEM file from RGM output rgm_surf_dem_out = np.loadtxt(rgm_surf_dem_out_file, skiprows=5) temp_surf_dem_file = temp_files_path + 'rgm_surf_dem_out_' + start.isoformat( ) + '.gsa' os.rename(rgm_surf_dem_out_file, temp_surf_dem_file) # this will be fed back into RGM on next time step surf_dem_in_file = temp_surf_dem_file # 7. Update glacier mask glacier_mask = update_glacier_mask(rgm_surf_dem_out, bed_dem, num_rows_dem, num_cols_dem) if output_trace_files: glacier_mask_file = temp_files_path + 'glacier_mask_' + start.isoformat( ) + '.gsa' write_grid_to_gsa_file(glacier_mask, glacier_mask_file) # 8. Update areas of each elevation band in each VIC grid cell, and update snow band and vegetation parameters update_area_fracs(cells, cell_areas, cellid_map, num_snow_bands, \ rgm_surf_dem_out, num_rows_dem, num_cols_dem, glacier_mask) temp_snb = temp_files_path + 'snb_temp_' + start.isoformat() + '.txt' snbparams.save_snb_parms(cells, temp_snb, band_map) temp_vpf = temp_files_path + 'vpf_temp_' + start.isoformat() + '.txt' vegparams.save_veg_parms(cells, temp_vpf) # 11 Update HRUs in VIC state file # don't forget to close the state file # Get ready for the next loop global_parms.init_state = "{}_{}.txt".format(global_parms.statename, end.strftime("%Y%m%d")) global_parms.statedate = end
def main(): print('\n\nVIC + RGM Hydro-Conductor starting...') # Parse command line parameters vic_path, rgm_path, output_path, vic_global_file, rgm_params_file, \ surf_dem_in_file, bed_dem_file, pixel_cell_map_file, \ init_glacier_mask_file, glacier_thickness_threshold, output_trace_files,\ glacier_root_zone_parms, open_ground_root_zone_parms, band_size,\ loglevel, output_plots\ = parse_input_parms() # Set up logging numeric_loglevel = getattr(logging, loglevel.upper()) logging.basicConfig(filename=output_path+'/hydrocon.log.'+\ strftime("%d-%m-%Y_%H:%M"), level=numeric_loglevel,\ format='%(levelname)s %(asctime)s %(message)s') logging.info('------- VIC-RGM Hydro-Conductor Startup -------') logging.info('VIC executable at {} '.format(vic_path)) logging.info('RGM executable at {} '.format(rgm_path)) # Get all initial VIC global parameters from the global parameter file logging.info('Loading initial VIC global parameters from %s', vic_global_file) with open(vic_global_file, 'r') as f: global_parms = Global(f) assert global_parms.state_format == 'NETCDF',\ 'VIC only supports NETCDF input statefile input format.'\ '(STATE_FORMAT in global file is currently set as {})'\ .format(global_parms.state_format) # Apply custom glacier_id and open_ground_id Band attributes, if provided if global_parms.glacier_id is None: logging.info('No value for GLACIER_ID was provided in the VIC global file. \ Assuming default value of {}.'.format(Band.glacier_id)) else: Band.glacier_id = global_parms.glacier_id # FIXME: reinstate the following commented-out code once OPEN_GROUND_ID # is supported in VIC # Numeric code indicating an open ground vegetation tile (HRU) # if global_parms.open_ground_id is None: # print('No value for OPEN_GROUND_ID was provided in the VIC global file. # Assuming default value of {}.'.format(Band.open_ground_id)) # else: # Band.open_ground_id = global_parms.open_ground_id # Set custom elevation band size, if provided if band_size: Band.band_size = band_size logging.info('Elevation band size set to {} meters.'.format(Band.band_size)) # Create temp_files_path, if it doesn't already exist temp_files_path = output_path + '/hydrocon_temp/' os.makedirs(temp_files_path, exist_ok=True) logging.info('Temporary output files will be written to {}.'.format(temp_files_path)) # Load parameters from Snow Band Parameters File num_snow_bands, snb_file = global_parms.snow_band.split() num_snow_bands = int(num_snow_bands) logging.info('Loading initial VIC snow band parameters from %s', snb_file) elevation_cell_dict = load_snb_parms(snb_file, num_snow_bands) # Load vegetation parameters from initial Vegetation Parameter File logging.info('Loading initial VIC vegetation parameters from %s',\ global_parms.vegparam) hru_cell_dict = load_veg_parms(global_parms.vegparam) # Apply custom HRU root_zone_parms attributes, if provided if glacier_root_zone_parms or open_ground_root_zone_parms: cells.apply_custom_root_zone_parms(hru_cell_dict, glacier_root_zone_parms,\ open_ground_root_zone_parms) Band.glacier_root_zone_parms = glacier_root_zone_parms Band.open_ground_root_zone_parms = open_ground_root_zone_parms # TODO: Do a sanity check to make sure band area fractions in Snow Band # Parameters file add up to sum of HRU area fractions in Vegetation # Parameter File for each cell? #assert (area_fracs == [sums of HRU area fracs for all bands]) # Create Ordered dictionary of Cell objects by merging info gathered from # Snow Band and Vegetation Parameter files and custom parameters cells = merge_cell_input(hru_cell_dict, elevation_cell_dict) # Open and read VIC-grid-to-RGM-pixel mapping file. logging.info('Loading VIC-grid-to-RGM-pixel mapping from %s',\ pixel_cell_map_file) vic_cell_mask, cell_areas, num_cols_dem, num_rows_dem\ = get_rgm_pixel_mapping(pixel_cell_map_file) # Get DEM xmin, xmax, ymin, ymax metadata of Bed DEM and check file header # validity dem_xmin, dem_xmax, dem_ymin, dem_ymax, num_rows, num_cols\ = read_gsa_headers(bed_dem_file) # Verify that number of columns & rows agree with what's stated in the # pixel_cell_map_file assert (num_cols == num_cols_dem) and (num_rows == num_rows_dem),\ 'Mismatch of stated dimension(s) between Bed DEM in {} (num rows: {}, ' 'num columns: {}) and the VIC-grid-to-RGM-pixel map in {} (num rows: {}, ' 'num columns: {}). Exiting.\n'.format(bed_dem_file, num_rows, num_cols, pixel_cell_map_file, num_rows_dem, num_cols_dem) # Read in the provided Bed Digital Elevation Map (BDEM) file to 2D bed_dem # array logging.info('Loading Bed Digital Elevation Map (BDEM) from %s', bed_dem_file) bed_dem = np.loadtxt(bed_dem_file, skiprows=5) # Check header validity of Surface DEM file _, _, _, _, num_rows, num_cols = read_gsa_headers(surf_dem_in_file) # Verify number of columns & rows agree with what's stated in the # pixel_to_cell_map_file assert (num_cols == num_cols_dem) and (num_rows == num_rows_dem),\ 'Mismatch of stated dimension(s) between Surface DEM in {} (num rows: {}, ' 'num columns: {}) and the VIC-grid-to-RGM-pixel map in {} (num rows: {}, ' 'num columns: {}). Exiting.\n'.format(surf_dem_in_file, num_rows, num_cols,\ pixel_cell_map_file, num_rows_dem, num_cols_dem) # Read in the provided Surface Digital Elevation Map (SDEM) file to 2D # surf_dem array logging.info('Loading Surface Digital Elevation Map (SDEM) from %s',\ surf_dem_in_file) current_surf_dem = np.loadtxt(surf_dem_in_file, skiprows=5) # Check if Bed DEM has any points that are higher than the Surface DEM # in the same location. If so, set these Bed DEM points to equal the # Surface DEM values, thus avoiding producing negative values when the # two are subtracted during glacier mask update. This reconciliation is # necessary because the two DEMs come from different sources, and could # have some overlapping elevation points. dem_diffs = current_surf_dem - bed_dem neg_val_inds = np.where(dem_diffs < 0) num_neg_vals = len(neg_val_inds[0]) if num_neg_vals > 0: bed_dem[neg_val_inds] = current_surf_dem[neg_val_inds] new_bed_dem_file = bed_dem_file[0:-4] + '_adjusted.gsa' logging.warning('The provided Bed DEM (%s) has %s elevation points \ (out of a total of %s elevation points in the domain) higher than those \ in the provided Surface DEM (%s), probably because they come from different \ data sources. The Bed DEM has been adjusted to equal the Surface DEM elevation \ at these points and written out to the file %s.',\ bed_dem_file, num_neg_vals, len(bed_dem), surf_dem_in_file, new_bed_dem_file) bed_dem_file = new_bed_dem_file write_grid_to_gsa_file(bed_dem, bed_dem_file, num_cols_dem, num_rows_dem,\ dem_xmin, dem_xmax, dem_ymin, dem_ymax) # Check header validity of initial Glacier Mask file _, _, _, _, num_rows, num_cols = read_gsa_headers(init_glacier_mask_file) # Verify number of columns & rows agree with what's stated in the # pixel_to_cell_map_file assert (num_cols == num_cols_dem) and (num_rows == num_rows_dem),\ 'Mismatch of stated dimension(s) between Glacier Mask in {} (num rows: {}, ' 'num columns: {}) and the VIC-grid-to-RGM-pixel map in {} (num rows: {}, num ' 'columns: {}). Exiting.\n'.format(init_glacier_mask_file, num_rows, num_cols,\ pixel_cell_map_file, num_rows_dem, num_cols_dem) # Read in the provided initial glacier mask file to 2D glacier_mask array logging.info('Loading initial Glacier Mask from %s', init_glacier_mask_file) glacier_mask = np.loadtxt(init_glacier_mask_file, skiprows=5) # Apply the initial glacier mask and modify the band and HRU area # fractions according to their digitized fractions of the DEM logging.debug('Applying initial band and HRU area fraction digitization.') band_areas, glacier_areas = bin_bands_and_glaciers(cells, cell_areas, vic_cell_mask, num_snow_bands, current_surf_dem, glacier_mask) digitize_domain(cells, cell_areas, band_areas, glacier_areas) # Set the VIC output state file name prefix (to be written to STATENAME # in the global file) state_filename_prefix = temp_files_path + 'vic_hydrocon_state' # Set the VIC results output file name prefix to NETCDF_OUTPUT_FILENAME given # in the original global file. netcdf_output_filename_prefix = global_parms.netcdf_output_filename # The RGM will always output a DEM file of the same name (if running RGM for # a single year at a time) rgm_surf_dem_out_file = temp_files_path + 's_out_00001.grd' # (initialisation done) # Display initial surface DEM and glacier mask if output_plots: figure = GlacierPlotter(current_surf_dem, glacier_mask, bed_dem, global_parms.startdate.isoformat(), output_trace_files, temp_files_path, glacier_thickness_threshold) #### Run the coupled VIC-RGM model for the time range specified in the VIC # global parameters file time_step = 0 time_iterator = run_ranges(global_parms.startdate, global_parms.enddate, global_parms.glacier_accum_startdate) for start, end in time_iterator: # Write temporary VIC parameter files temp_snb = temp_files_path + 'snb_temp_' + start.isoformat() + '.txt' logging.debug('Writing temporary snow band parameter file %s', temp_snb) save_snb_parms(cells, temp_snb) temp_vpf = temp_files_path + 'vpf_temp_' + start.isoformat() + '.txt' logging.debug('Writing temporary vegetation parameter file %s', temp_vpf) save_veg_parms(cells, temp_vpf) temp_gpf = temp_files_path + 'gpf_temp_{}.txt'.format(start.isoformat()) logging.debug('Writing temporary global parameter file %s', temp_gpf) global_parms.vegparam = temp_vpf global_parms.snow_band = '{} {}'.format(num_snow_bands, temp_snb) global_parms.startdate = start global_parms.enddate = end global_parms.statedate = end global_parms.statename = state_filename_prefix global_parms.netcdf_output_filename = netcdf_output_filename_prefix \ + start.isoformat() + '-' + end.isoformat() + '.nc' if time_step > 0: global_parms.glacier_accum_start_year = start.year global_parms.glacier_accum_start_month = start.month global_parms.glacier_accum_start_day = start.day global_parms.write(temp_gpf) # Run VIC for a year, saving model state at the end print('\nRunning VIC from {} to {}'.format(start, end)) logging.info('\nRunning VIC from %s to %s using global parameter file %s',\ start, end, temp_gpf) try: subprocess.check_call([vic_path, "-g", temp_gpf], shell=False,\ stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: logging.error('Subprocess invocation of VIC failed with the following \ error: %s', e) sys.exit(0) # Open VIC NetCDF state file and load the most recent set of state # variable values for all grid cells being modeled state_file = state_filename_prefix + '_' + end.isoformat() logging.info('Reading saved VIC state file %s', state_file) # leave the state file open for modification later state_dataset = netCDF4.Dataset(state_file, 'r+') state_dataset.set_auto_mask(False) # these should never change within a run of the Hydro-Conductor: Cell.Nlayers = state_dataset.state_nlayer Cell.Nnodes = state_dataset.state_nnode # drop unused fit error term from glacier mass balance polynomial Cell.NglacMassBalanceEqnTerms = state_dataset.state_nglac_mass_balance_eqn_terms - 1 state = state_dataset.variables # read new states of all cells read_state(state, cells) # optionally leave the last VIC state file on disk if not output_trace_files: os.remove(state_file) gmb_polys = {} cell_ids = [] for cell_id in cells: # Make sure VIC cell IDs in the state file agree with those in the # vic_cell_mask, which is derived from the pixel_cell_map_file if int(cell_id) not in vic_cell_mask: print('Cell ID {} read from the VIC state file {} was not found in ' 'the VIC cell mask derived from the given RGM-Pixel-to-VIC-Cell map ' 'file (option --pixel_map) {}. Exiting.' .format(cell_id, state_file, pixel_cell_map_file)) logging.error('Cell ID {} read from the VIC state file {} was not ' 'found in the VIC cell mask derived from the given ' 'RGM-Pixel-to-VIC-Cell map file (option --pixel_map) {}') sys.exit(0) cell_ids.append(cell_id) # Read Glacier Mass Balance polynomial terms from cell states; # leave off 4th the "fit error" term at the end of the GMB polynomial. gmb_polys[cell_id] = cells[cell_id].cell_state.variables\ ['GLAC_MASS_BALANCE_EQN_TERMS'][0:Cell.NglacMassBalanceEqnTerms] # Translate mass balances using grid cell GMB polynomials and current # surface DEM into a 2D RGM mass balance grid (MBG) and write the # MBG to an ASCII file to give as input to the RGM mbg_file = temp_files_path + 'mass_balance_grid_' + end.isoformat()\ + '.gsa' logging.debug('Converting glacier mass balance polynomials to 2D grid \ and writing to file %s', mbg_file) mass_balance_grid = mass_balances_to_rgm_grid(gmb_polys, vic_cell_mask,\ current_surf_dem, bed_dem, num_rows_dem, num_cols_dem) write_grid_to_gsa_file(mass_balance_grid, mbg_file, num_cols_dem,\ num_rows_dem, dem_xmin, dem_xmax, dem_ymin, dem_ymax) # Write modified surface DEM with all pixels lying outside of VIC # domain set equal to the bed DEM rgm_surf_dem_in_file = temp_files_path + 'rgm_surf_dem_in_'\ + end.isoformat() + '.gsa' write_grid_to_gsa_file(current_surf_dem, rgm_surf_dem_in_file, num_cols_dem,\ num_rows_dem, dem_xmin, dem_xmax, dem_ymin, dem_ymax) # Run RGM for one year, passing it the MBG, BDEM, SDEM logging.info('Running RGM for current year with parameter file %s, \ Bed DEM file %s, Surface DEM file %s, Mass Balance Grid file %s',\ rgm_params_file, bed_dem_file, rgm_surf_dem_in_file, mbg_file) try: subprocess.check_call([rgm_path, "-p", rgm_params_file, "-b",\ bed_dem_file, "-d", rgm_surf_dem_in_file, "-m", mbg_file, "-o",\ temp_files_path, "-s", "0", "-e", "0" ], shell=False,\ stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: logging.error('Subprocess invocation of RGM failed with the following \ error: %s', e) sys.exit(0) # Read in new Surface DEM file from RGM output logging.debug('Reading Surface DEM file from RGM output %s',\ rgm_surf_dem_out_file) current_surf_dem = np.loadtxt(rgm_surf_dem_out_file, skiprows=5) temp_surf_dem_file = temp_files_path + 'rgm_surf_dem_out_'\ + end.isoformat() + '.gsa' os.rename(rgm_surf_dem_out_file, temp_surf_dem_file) # this will be fed back into RGM on next time step: rgm_surf_dem_in_file = temp_surf_dem_file # remove temporary files if not saving for offline inspection if not output_trace_files: os.remove(mbg_file) os.remove(rgm_surf_dem_in_file) os.remove(rgm_surf_dem_out_file) # Update glacier mask logging.debug('Updating Glacier Mask') glacier_mask = update_glacier_mask(current_surf_dem, bed_dem, num_rows_dem, num_cols_dem, glacier_thickness_threshold) if output_trace_files: glacier_mask_file = temp_files_path + 'glacier_mask_'\ + end.isoformat() + '.gsa' logging.debug('Writing Glacier Mask to file %s', glacier_mask_file) write_grid_to_gsa_file(glacier_mask, glacier_mask_file, num_cols_dem, num_rows_dem, dem_xmin, dem_xmax, dem_ymin, dem_ymax) if output_plots: figure.update_plots(current_surf_dem, glacier_mask, glacier_thickness_threshold, bed_dem, end.isoformat()) # Update HRU and band area fractions and state for all VIC grid cells logging.debug('Updating VIC grid cell area fractions and states') update_area_fracs(cells, cell_areas, vic_cell_mask, num_snow_bands, current_surf_dem, glacier_mask) # Update the VIC state file with new state information new_state_date = end + one_day new_state_file = state_filename_prefix + '_' + new_state_date.isoformat() logging.debug('Writing updated VIC state file %s', new_state_file) # Set the new state file name VIC will have to read in on next iteration global_parms.init_state = new_state_file new_state_dataset = netCDF4.Dataset(new_state_file, 'w') write_state(cells, state_dataset, new_state_dataset, new_state_date) logging.debug('Closing old and updated NetCDF state files.') state_dataset.close() new_state_dataset.close() time_step = time_step + 1
def toy_domain_64px_cells(): # NOT USED BUT USEFUL INFO: initial band map of lower elevation bounds for # all existing (valid) bands # test_band_map = { # allows for glacier growth at top: # '12345': [2000, 2100, 2200, 2300, 0], # allows for glacier growth at top, and revelation of lower band at bottom: # '23456': [0, 1900, 2000, 2100, 0]} fname = resource_filename('conductor', 'tests/input/snb_toy_64px.txt') elevation_cells = load_snb_parms(fname, 5) fname = resource_filename('conductor', 'tests/input/vpf_toy_64px.txt') hru_cells = load_veg_parms(fname) cells = merge_cell_input(hru_cells, elevation_cells) cell_ids = list(cells.keys()) # We have a total allowable number of snow bands of 5, with 100m spacing num_snow_bands = 5 band_size = 100 # Spatial DEM layouts bed_dem_by_cells = { cell_ids[0]: np.array([ [2065, 2055, 2045, 2035, 2025, 2015, 2005, 2000], [2075, 2085, 2100, 2100, 2100, 2100, 2100, 2005], [2085, 2100, 2210, 2230, 2220, 2200, 2110, 2010], [2090, 2100, 2240, 2377, 2310, 2230, 2125, 2015], [2070, 2110, 2230, 2340, 2320, 2230, 2130, 2020], [2090, 2105, 2200, 2210, 2220, 2220, 2120, 2015], [2090, 2100, 2105, 2110, 2140, 2150, 2130, 2010], [2080, 2075, 2065, 2055, 2045, 2035, 2020, 2000] ]), # note: bed elev 2085 at position [1,1] above will be used to demonstrate # glacier receding to reveal more band area fraction for Band 0 cell_ids[1]: np.array([ [1970, 1975, 1850, 1799, 1975, 1965, 1960, 1960], [1970, 2000, 2025, 2035, 2005, 2005, 2000, 1965], [1975, 2000, 2100, 2125, 2130, 2110, 2000, 1970], [1985, 2005, 2105, 2130, 2150, 2100, 2000, 1975], [1990, 2010, 2110, 2120, 2110, 2105, 2005, 1980], [1980, 2005, 2105, 2105, 2110, 2100, 2000, 1980], [1970, 2000, 2000, 2020, 2035, 2025, 2000, 1970], [1965, 1965, 1970, 1970, 1975, 1960, 1950, 1960] ]) } initial_surf_dem_by_cells = { cell_ids[0]: np.array([ [2065, 2055, 2045, 2035, 2025, 2015, 2005, 2000], [2075, 2100, 2120, 2140, 2130, 2120, 2100, 2005], [2085, 2110, 2250, 2270, 2260, 2240, 2110, 2010], [2090, 2120, 2260, 2377, 2310, 2250, 2125, 2015], [2070, 2120, 2250, 2340, 2320, 2250, 2130, 2020], [2090, 2105, 2200, 2210, 2220, 2220, 2120, 2015], [2090, 2100, 2105, 2110, 2140, 2150, 2130, 2010], [2080, 2075, 2065, 2055, 2045, 2035, 2020, 2000] ]), cell_ids[1]: np.array([ [1970, 1975, 1995, 1995, 1975, 1965, 1960, 1960], [1970, 2000, 2045, 2055, 2005, 2005, 2000, 1965], [1975, 2000, 2100, 2155, 2160, 2140, 2000, 1970], [1985, 2005, 2105, 2160, 2180, 2130, 2000, 1975], [1990, 2010, 2110, 2150, 2140, 2105, 2005, 1980], [1980, 2005, 2105, 2105, 2110, 2100, 2000, 1980], [1970, 2000, 2000, 2020, 2035, 2025, 2000, 1970], [1965, 1965, 1970, 1970, 1975, 1960, 1950, 1960] ]) } initial_glacier_mask_by_cells = { cell_ids[0]: np.array([ [ 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 1, 1, 1, 1, 1, 0, 0 ], [ 0, 1, 1, 1, 1, 1, 0, 0 ], [ 0, 1, 1, 0, 0, 1, 0, 0 ], [ 0, 1, 1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ] ]), cell_ids[1]: np.array([ [ 0, 0, 1, 1, 0, 0, 0, 0 ], [ 0, 0, 1, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 1, 1, 0, 0 ], [ 0, 0, 0, 1, 1, 1, 0, 0 ], [ 0, 0, 0, 1, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0 ] ]) } def build_padded_dem_aligned_map(array_1, array_2, padding_thickness,\ fill_value): """ Helper function to create a map that is aligned with a DEM made up of two adjacent rectangular pixel arrays (representing 2 VIC cells), with NaN padding around the edges (simulating the output from get_rgm_pixel_mapping(), where some pixels that are read in do not belong to either VIC cell). This can be used to generate test instances of surf_dem, bed_dem, cellid_map, glacier_mask """ vertical_pad = np.empty((padding_thickness, 2*padding_thickness+len(array_1[0])+len(array_2[0]))) vertical_pad.fill(fill_value) horizontal_pad = np.empty((len(array_1), padding_thickness)) horizontal_pad.fill(fill_value) padded_map = np.concatenate((array_1, array_2), axis=1) padded_map = np.concatenate((horizontal_pad, padded_map), axis=1) padded_map = np.concatenate((padded_map, horizontal_pad), axis=1) padded_map = np.concatenate((vertical_pad, padded_map), axis=0) padded_map = np.concatenate((padded_map, vertical_pad), axis=0) return padded_map # Create cellid_map that is padded by 2 rows & columns of np.NaNs (to simulate mapping # of valid pixels to VIC cells, as read in by get_rgm_pixel_mapping()) cellid_map_cell_0 = np.empty((8,8)) cellid_map_cell_0.fill(cell_ids[0]) cellid_map_cell_1 = np.empty((8,8)) cellid_map_cell_1.fill(cell_ids[1]) cellid_map = build_padded_dem_aligned_map(cellid_map_cell_0,\ cellid_map_cell_1, 2, 9999) # Create bed_dem with padding of 2 bed_dem = build_padded_dem_aligned_map(bed_dem_by_cells[cell_ids[0]],\ bed_dem_by_cells[cell_ids[1]], 2, 9999) # Create initial surf_dem with padding of 2 surf_dem = build_padded_dem_aligned_map(initial_surf_dem_by_cells[cell_ids[0]],\ initial_surf_dem_by_cells[cell_ids[1]], 2, 9999) # Non-padded version of surf_dem: #surf_dem = np.concatenate((initial_surf_dem_by_cells[cell_ids[0]], \ # initial_surf_dem_by_cells[cell_ids[1]]), axis=1) # Create initial glacier mask with padding of 2 glacier_mask = build_padded_dem_aligned_map(initial_glacier_mask_by_cells[cell_ids[0]],\ initial_glacier_mask_by_cells[cell_ids[1]], 2, 9999) # The toy surface DEM above, broken down by elevation bands. Useful for checking median elevations cell_band_surf_pixel_elevations = { cell_ids[0]: [ # band 0, median: 2040 [2065, 2055, 2045, 2035, 2025, 2015, 2005, 2000, 2075, 2005, 2085, 2010, 2090, 2015, 2070, 2020, 2090, 2015, 2090, 2010, 2080, 2075, 2065, 2055, 2045, 2035, 2020, 2000], # band 1, median: 2120 [2100, 2120, 2140, 2130, 2120, 2100, 2110, 2110, 2120, 2125, 2110, 2130, 2105, 2120, 2100, 2105, 2110, 2140, 2150, 2130], # band 2, median: 2250 [2250, 2270, 2260, 2240, 2260, 2250, 2250, 2250, 2200, 2210, 2220, 2220], # band 3, median: 2330 [2377, 2310, 2340, 2320] ], cell_ids[1]: [ # band 0, median: 1970 [1970, 1975, 1995, 1995, 1975, 1965, 1960, 1960, 1970, 1965, 1975, 1970, 1985, 1975, 1990, 1980, 1980, 1980, 1970, 1970, 1965, 1965, 1970, 1970, 1975, 1960, 1950, 1960], # band 1, median: 2005 [2000, 2045, 2055, 2005, 2005, 2000, 2000, 2000, 2005, 2000, 2010, 2005, 2005, 2000, 2000, 2000, 2020, 2035, 2025, 2000], # band 2, median: 2120 [2100, 2155, 2160, 2140, 2105, 2160, 2180, 2130, 2110, 2150, 2140, 2105, 2105, 2105, 2110, 2100], ] } return cells, cell_ids, num_snow_bands, band_size, cellid_map, bed_dem,\ surf_dem, glacier_mask, cell_band_surf_pixel_elevations