示例#1
0
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 test_load_veg_parms():
  fname = resource_filename('conductor', 'tests/input/veg.txt')
  cells = load_veg_parms(fname)
  assert len(cells) == 6
  assert len(cells['368470']) == 16
示例#3
0
def test_load_veg_parms():
    fname = resource_filename('conductor', 'tests/input/veg.txt')
    cells = load_veg_parms(fname)
    assert len(cells) == 6
    assert len(cells['368470']) == 16
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
示例#5
0
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