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
Exemplo n.º 3
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 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
Exemplo n.º 6
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