From 1a00462220f970614e9e045af4a6b3198f47dc8b Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 19 Nov 2020 14:47:50 -0500 Subject: [PATCH 01/13] ERA island effect weight changes --- RAPIDpy/gis/weight.py | 64 ++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index f17c020..bc83836 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -80,7 +80,7 @@ def find_nearest(array, value): return (np.abs(array-value)).argmin() -def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, +def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_mask, in_catchment_shapefile, river_id, in_rapid_connect, out_weight_table, file_geodatabase=None, area_id=None): @@ -103,7 +103,6 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, else: ogr_catchment_shapefile = ogr.Open(in_catchment_shapefile) ogr_catchment_shapefile_lyr = ogr_catchment_shapefile.GetLayer() - ogr_catchment_shapefile_lyr_proj = \ ogr_catchment_shapefile_lyr.GetSpatialRef() original_catchment_proj = \ @@ -196,9 +195,11 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, connectwriter.writerow([rapid_connect_rivid] + dummy_row_end) continue + get_catchment_feature = \ ogr_catchment_shapefile_lyr.GetFeature(catchment_pos) feat_geom = get_catchment_feature.GetGeometryRef() + # make sure coordinates are geographic if proj_transform: feat_geom.Transform(proj_transform) @@ -242,22 +243,40 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_lon, lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], lsm_grid_feature_list[sub_lsm_grid_pos]['lon']) - intersect_grid_info_list.append({ - 'rivid': rapid_connect_rivid, - 'area': poly_area, - 'lsm_grid_lat': - lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], - 'lsm_grid_lon': - lsm_grid_feature_list[sub_lsm_grid_pos]['lon'], - 'index_lsm_grid_lon': index_lsm_grid_lon, - 'index_lsm_grid_lat': index_lsm_grid_lat - }) + # If ocean/water point, ECMWF sets ro to very negative number. + # This resulted at times in "island effect", i.e., large + # grid cell overlaying rivid and thus weird results. So, + # use land mask field to address these ECMWF points. + # There are arguments for and against division by land fraction. E.g., + # it's arguable that division by mask might overemphasis some + # pixels that contain fine grained islands with runoff. + if lsm_grid_mask[int(index_lsm_grid_lat), int(index_lsm_grid_lon)] > 0: + intersect_grid_info_list.append({ + 'rivid': rapid_connect_rivid, + 'area': poly_area/lsm_grid_mask[int(index_lsm_grid_lat), int(index_lsm_grid_lon)], + 'lsm_grid_lat': + lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], + 'lsm_grid_lon': + lsm_grid_feature_list[sub_lsm_grid_pos]['lon'], + 'index_lsm_grid_lon': index_lsm_grid_lon, + 'index_lsm_grid_lat': index_lsm_grid_lat + }) +# Prior weight table write without addressing land mask. +# intersect_grid_info_list.append({ +# 'rivid': rapid_connect_rivid, +# 'area': poly_area, +# 'lsm_grid_lat': +# lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], +# 'lsm_grid_lon': +# lsm_grid_feature_list[sub_lsm_grid_pos]['lon'], +# 'index_lsm_grid_lon': index_lsm_grid_lon, +# 'index_lsm_grid_lat': index_lsm_grid_lat +# }) npoints = len(intersect_grid_info_list) # If no intersection found, add dummy row if npoints <= 0: connectwriter.writerow([rapid_connect_rivid] + dummy_row_end) - for intersect_grid_info in intersect_grid_info_list: connectwriter.writerow([ intersect_grid_info['rivid'], @@ -268,7 +287,6 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, intersect_grid_info['lsm_grid_lon'], intersect_grid_info['lsm_grid_lat'] ]) - time_end_all = datetime.utcnow() log(time_end_all - time_end_lsm_grid_rtree) log("TOTAL TIME: {0}".format(time_end_all - time_start_all)) @@ -332,15 +350,22 @@ def CreateWeightTableECMWF(in_ecmwf_nc, in_ecmwf_lon_var = 'lon' if 'longitude' in variables_list: in_ecmwf_lon_var = 'longitude' + in_ecmwf_mask_var = 'mask' +# in_ecmwf_mask_var = 'LAND_P0_L1_GLL0' + if 'mask' in variables_list: + in_ecmwf_mask_var = 'mask' + if 'lsm' in variables_list: + in_ecmwf_mask_var = 'lsm' # convert [0, 360] to [-180, 180] - ecmwf_lon = \ - (data_ecmwf_nc.variables[in_ecmwf_lon_var][:] + 180) % 360 - 180 + ecmwf_lon = data_ecmwf_nc.variables[in_ecmwf_lon_var][:] # assume [-90, 90] ecmwf_lat = data_ecmwf_nc.variables[in_ecmwf_lat_var][:] + ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] + data_ecmwf_nc.close() - rtree_create_weight_table(ecmwf_lat, ecmwf_lon, + rtree_create_weight_table(ecmwf_lat, ecmwf_lon, ecmwf_mask, in_catchment_shapefile, river_id, in_connectivity_file, out_weight_table, file_geodatabase, area_id) @@ -373,6 +398,8 @@ def CreateWeightTableLDAS(in_ldas_nc, The name of the field with the river ID (Ex. 'DrainLnID' or 'LINKNO'). in_connectivity_file: str The path to the RAPID connectivity file. + is the name of the stream network feature class. + (WARNING: Not always stable with GDAL.) out_weight_table: str The path to the output weight table file. area_id: str, optional @@ -383,7 +410,6 @@ def CreateWeightTableLDAS(in_ldas_nc, is the name of the stream network feature class. (WARNING: Not always stable with GDAL.) - Example: .. code:: python @@ -398,7 +424,7 @@ def CreateWeightTableLDAS(in_ldas_nc, river_id='LINKNO', in_connectivity_file='/path/to/rapid_connect.csv', out_weight_table='/path/to/ldas_weight.csv', - ) + ) """ # extract LDAS GRID data_ldas_nc = Dataset(in_ldas_nc) From 7bfd2d066014515b6fc8c0c4477e4819e2a3a7b3 Mon Sep 17 00:00:00 2001 From: mjshaw1 <22522597+mjshaw1@users.noreply.github.com> Date: Thu, 19 Nov 2020 15:03:21 -0500 Subject: [PATCH 02/13] Update weight.py Reverted to mask from 0-360 (so, shifting by 180 degrees). --- RAPIDpy/gis/weight.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index bc83836..3cd55c6 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -358,7 +358,8 @@ def CreateWeightTableECMWF(in_ecmwf_nc, in_ecmwf_mask_var = 'lsm' # convert [0, 360] to [-180, 180] - ecmwf_lon = data_ecmwf_nc.variables[in_ecmwf_lon_var][:] + ecmwf_long = \ + (data_ecmwf_nc.variables[in_ecmwf_lon_var][:] + 180) % 360 - 180 # assume [-90, 90] ecmwf_lat = data_ecmwf_nc.variables[in_ecmwf_lat_var][:] ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] From 6528184a422fcff4722db6b889dbcd3edefaad3f Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 20 Nov 2020 06:50:21 -0600 Subject: [PATCH 03/13] Modifies island_effect code to allow weight table generation routines to be called without specifying a land-mask file. --- RAPIDpy/gis/weight.py | 85 ++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index 3cd55c6..9e87b13 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -80,10 +80,11 @@ def find_nearest(array, value): return (np.abs(array-value)).argmin() -def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_mask, +def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, in_catchment_shapefile, river_id, in_rapid_connect, out_weight_table, - file_geodatabase=None, area_id=None): + file_geodatabase=None, area_id=None, + lsm_grid_mask=None): """ Create Weight Table for Land Surface Model Grids """ @@ -103,6 +104,7 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_mask, else: ogr_catchment_shapefile = ogr.Open(in_catchment_shapefile) ogr_catchment_shapefile_lyr = ogr_catchment_shapefile.GetLayer() + ogr_catchment_shapefile_lyr_proj = \ ogr_catchment_shapefile_lyr.GetSpatialRef() original_catchment_proj = \ @@ -195,11 +197,9 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_mask, connectwriter.writerow([rapid_connect_rivid] + dummy_row_end) continue - get_catchment_feature = \ ogr_catchment_shapefile_lyr.GetFeature(catchment_pos) feat_geom = get_catchment_feature.GetGeometryRef() - # make sure coordinates are geographic if proj_transform: feat_geom.Transform(proj_transform) @@ -243,35 +243,21 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_mask, lsm_grid_lon, lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], lsm_grid_feature_list[sub_lsm_grid_pos]['lon']) - # If ocean/water point, ECMWF sets ro to very negative number. - # This resulted at times in "island effect", i.e., large - # grid cell overlaying rivid and thus weird results. So, - # use land mask field to address these ECMWF points. - # There are arguments for and against division by land fraction. E.g., - # it's arguable that division by mask might overemphasis some - # pixels that contain fine grained islands with runoff. - if lsm_grid_mask[int(index_lsm_grid_lat), int(index_lsm_grid_lon)] > 0: - intersect_grid_info_list.append({ - 'rivid': rapid_connect_rivid, - 'area': poly_area/lsm_grid_mask[int(index_lsm_grid_lat), int(index_lsm_grid_lon)], - 'lsm_grid_lat': - lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], - 'lsm_grid_lon': - lsm_grid_feature_list[sub_lsm_grid_pos]['lon'], - 'index_lsm_grid_lon': index_lsm_grid_lon, - 'index_lsm_grid_lat': index_lsm_grid_lat - }) -# Prior weight table write without addressing land mask. -# intersect_grid_info_list.append({ -# 'rivid': rapid_connect_rivid, -# 'area': poly_area, -# 'lsm_grid_lat': -# lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], -# 'lsm_grid_lon': -# lsm_grid_feature_list[sub_lsm_grid_pos]['lon'], -# 'index_lsm_grid_lon': index_lsm_grid_lon, -# 'index_lsm_grid_lat': index_lsm_grid_lat -# }) + + if lsm_grid_mask is not None: + if lsm_grid_mask[int(index_lsm_grid_lat), int(index_lsm_grid_lon)] > 0: + poly_area /= lsm_grid_mask[int(index_lsm_grid_lat), int(index_lsm_grid_lon)] + + intersect_grid_info_list.append({ + 'rivid': rapid_connect_rivid, + 'area': poly_area, + 'lsm_grid_lat': + lsm_grid_feature_list[sub_lsm_grid_pos]['lat'], + 'lsm_grid_lon': + lsm_grid_feature_list[sub_lsm_grid_pos]['lon'], + 'index_lsm_grid_lon': index_lsm_grid_lon, + 'index_lsm_grid_lat': index_lsm_grid_lat + }) npoints = len(intersect_grid_info_list) # If no intersection found, add dummy row @@ -287,6 +273,7 @@ def rtree_create_weight_table(lsm_grid_lat, lsm_grid_lon, lsm_grid_mask, intersect_grid_info['lsm_grid_lon'], intersect_grid_info['lsm_grid_lat'] ]) + time_end_all = datetime.utcnow() log(time_end_all - time_end_lsm_grid_rtree) log("TOTAL TIME: {0}".format(time_end_all - time_start_all)) @@ -350,26 +337,34 @@ def CreateWeightTableECMWF(in_ecmwf_nc, in_ecmwf_lon_var = 'lon' if 'longitude' in variables_list: in_ecmwf_lon_var = 'longitude' - in_ecmwf_mask_var = 'mask' -# in_ecmwf_mask_var = 'LAND_P0_L1_GLL0' - if 'mask' in variables_list: - in_ecmwf_mask_var = 'mask' - if 'lsm' in variables_list: - in_ecmwf_mask_var = 'lsm' - + # convert [0, 360] to [-180, 180] - ecmwf_long = \ + ecmwf_lon = \ (data_ecmwf_nc.variables[in_ecmwf_lon_var][:] + 180) % 360 - 180 # assume [-90, 90] ecmwf_lat = data_ecmwf_nc.variables[in_ecmwf_lat_var][:] - ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] + mask_var_names = ['mask', 'lsm'] + mask_var_intersect_variable_list = ( + set(variables_list) & set(mask_var_names)) + + try: + in_ecmwf_mask_var = mask_var_intersect_variable_list.pop() + except KeyError: + in_ecmwf_mask_var = None + + if in_ecmwf_mask_var is None: + ecmwf_mask = None + else: + ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] + data_ecmwf_nc.close() - rtree_create_weight_table(ecmwf_lat, ecmwf_lon, ecmwf_mask, + rtree_create_weight_table(ecmwf_lat, ecmwf_lon, in_catchment_shapefile, river_id, in_connectivity_file, out_weight_table, - file_geodatabase, area_id) + file_geodatabase, area_id, + lsm_grid_mask=ecmwf_mask) def CreateWeightTableLDAS(in_ldas_nc, @@ -425,7 +420,7 @@ def CreateWeightTableLDAS(in_ldas_nc, river_id='LINKNO', in_connectivity_file='/path/to/rapid_connect.csv', out_weight_table='/path/to/ldas_weight.csv', - ) + ) """ # extract LDAS GRID data_ldas_nc = Dataset(in_ldas_nc) From 799fdf9c679af9c3b1d0fefffdcb7bdd32d18f60 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 23 Nov 2020 13:08:31 -0600 Subject: [PATCH 04/13] Simplifies handling of land mask variable. --- RAPIDpy/gis/weight.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index 9e87b13..44bd126 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -285,7 +285,8 @@ def CreateWeightTableECMWF(in_ecmwf_nc, in_connectivity_file, out_weight_table, area_id=None, - file_geodatabase=None): + file_geodatabase=None, + in_ecmwf_mask_var=None): """ Create Weight Table for ECMWF Grids @@ -344,20 +345,11 @@ def CreateWeightTableECMWF(in_ecmwf_nc, # assume [-90, 90] ecmwf_lat = data_ecmwf_nc.variables[in_ecmwf_lat_var][:] - mask_var_names = ['mask', 'lsm'] - mask_var_intersect_variable_list = ( - set(variables_list) & set(mask_var_names)) - - try: - in_ecmwf_mask_var = mask_var_intersect_variable_list.pop() - except KeyError: - in_ecmwf_mask_var = None - - if in_ecmwf_mask_var is None: - ecmwf_mask = None - else: + if in_ecmwf_mask_var is not None and mask_var in variables_list: ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] - + else: + ecmwf_mask = None + data_ecmwf_nc.close() rtree_create_weight_table(ecmwf_lat, ecmwf_lon, From bb2c69d6f3722b1fa1a2239d63e113db5639f6f4 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 23 Nov 2020 18:40:41 -0600 Subject: [PATCH 05/13] Adds statement to notify user if specified land-mask variable is not found in input LSM file. --- RAPIDpy/gis/weight.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index 44bd126..5f664ba 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -345,10 +345,14 @@ def CreateWeightTableECMWF(in_ecmwf_nc, # assume [-90, 90] ecmwf_lat = data_ecmwf_nc.variables[in_ecmwf_lat_var][:] - if in_ecmwf_mask_var is not None and mask_var in variables_list: - ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] - else: - ecmwf_mask = None + if in_ecmwf_mask_var is not None: + if mask_var in variables_list: + ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] + else: + print('Variable {} not found in {}.'.format(in_ecmwf_mask_var, + in_ecmwf_nc)) + print('Continuing with no land mask.') + ecmwf_mask = None data_ecmwf_nc.close() From 5f0c0ec3c66494c655e8dec7ace670df7eaf9885 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 23 Nov 2020 19:22:35 -0600 Subject: [PATCH 06/13] Adds ECMWF land-mask file test. --- tests/test_gis.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_gis.py b/tests/test_gis.py index cbe7285..9ae3d54 100644 --- a/tests/test_gis.py +++ b/tests/test_gis.py @@ -765,3 +765,34 @@ def test_weight_table_with_area_id(): generated_weight_table_file_solution)) remove_files(generated_weight_table_file) + +def test_gen_weight_table_era5_land_mask(): + """ + Checks generating weight table for ERA5 grid with land mask. + """ + print("TEST 18: TEST GENERATE WEIGHT TABLE FOR ERA5 GRID WITH LAND MASK.") + generated_weight_table_file = os.path.join( + OUTPUT_DATA_PATH, "mendocino_nhdplus_catchment", + "weight_mendocino_sample_era5.csv") + + #rapid_connect + rapid_connect_file = os.path.join(COMPARE_DATA_PATH, + "mendocino_nhdplus_catchment", + "rapid_connectivity_mendocino_sample.csv") + + lsm_grid = os.path.join(LSM_INPUT_DATA_PATH, "era5", "era5_land-sea_mask_mendocino_subset.nc") + + CreateWeightTableECMWF(in_ecmwf_nc=lsm_grid, + in_catchment_shapefile=os.path.join(GIS_INPUT_DATA_PATH, 'mendocino_nhdplus_catchment', 'NHDCat_mendocino_watershed_hopland_sample.shp'), + river_id="FEATUREID", + in_connectivity_file=rapid_connect_file, + out_weight_table=generated_weight_table_file) + + generated_weight_table_file_solution = os.path.join( + COMPARE_DATA_PATH, 'mendocino_nhdplus_catchment', + 'weight_mendocino_sample_era5.csv') + + assert (compare_csv_decimal_files(generated_weight_table_file, + generated_weight_table_file_solution)) + + remove_files(generated_weight_table_file) From 682d1ba9d2d16b03159a93fbb8b5370c54c042d5 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 23 Nov 2020 14:34:55 -0600 Subject: [PATCH 07/13] Corrects path names in land-mask test. --- tests/test_gis.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_gis.py b/tests/test_gis.py index 9ae3d54..41e7cb8 100644 --- a/tests/test_gis.py +++ b/tests/test_gis.py @@ -772,8 +772,7 @@ def test_gen_weight_table_era5_land_mask(): """ print("TEST 18: TEST GENERATE WEIGHT TABLE FOR ERA5 GRID WITH LAND MASK.") generated_weight_table_file = os.path.join( - OUTPUT_DATA_PATH, "mendocino_nhdplus_catchment", - "weight_mendocino_sample_era5.csv") + OUTPUT_DATA_PATH, "weight_mendocino_era5_land_mask.csv") #rapid_connect rapid_connect_file = os.path.join(COMPARE_DATA_PATH, @@ -786,11 +785,12 @@ def test_gen_weight_table_era5_land_mask(): in_catchment_shapefile=os.path.join(GIS_INPUT_DATA_PATH, 'mendocino_nhdplus_catchment', 'NHDCat_mendocino_watershed_hopland_sample.shp'), river_id="FEATUREID", in_connectivity_file=rapid_connect_file, - out_weight_table=generated_weight_table_file) + out_weight_table=generated_weight_table_file, + in_ecmwf_mask_var='lsm') generated_weight_table_file_solution = os.path.join( COMPARE_DATA_PATH, 'mendocino_nhdplus_catchment', - 'weight_mendocino_sample_era5.csv') + 'weight_mendocino_era5_land_mask.csv') assert (compare_csv_decimal_files(generated_weight_table_file, generated_weight_table_file_solution)) From d98caacfed9f8770ff885f3b3f3754c561cd65b0 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 23 Nov 2020 14:36:11 -0600 Subject: [PATCH 08/13] Corrects variable name in CreateWeightTableECMWF. --- RAPIDpy/gis/weight.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index 5f664ba..db64f43 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -346,13 +346,15 @@ def CreateWeightTableECMWF(in_ecmwf_nc, ecmwf_lat = data_ecmwf_nc.variables[in_ecmwf_lat_var][:] if in_ecmwf_mask_var is not None: - if mask_var in variables_list: + if in_ecmwf_mask_var in variables_list: ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] else: - print('Variable {} not found in {}.'.format(in_ecmwf_mask_var, + print('Variable "{}" not found in {}.'.format(in_ecmwf_mask_var, in_ecmwf_nc)) print('Continuing with no land mask.') ecmwf_mask = None + else: + ecmwf_mask = None data_ecmwf_nc.close() From 9ccd0a915127c429f82ef7f736e8db5dfabf3325 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 23 Nov 2020 14:46:14 -0600 Subject: [PATCH 09/13] Adds Mendocino land-mask test data files. --- .../rapid_connectivity_mendocino_sample.csv | 6 ++++++ .../weight_mendocino_era5_land_mask.csv | 9 +++++++++ ...HDCat_mendocino_watershed_hopland_sample.cpg | 1 + ...HDCat_mendocino_watershed_hopland_sample.dbf | Bin 0 -> 2382 bytes ...HDCat_mendocino_watershed_hopland_sample.prj | 1 + ...HDCat_mendocino_watershed_hopland_sample.sbn | Bin 0 -> 188 bytes ...HDCat_mendocino_watershed_hopland_sample.sbx | Bin 0 -> 124 bytes ...HDCat_mendocino_watershed_hopland_sample.shp | Bin 0 -> 14852 bytes ...HDCat_mendocino_watershed_hopland_sample.shx | Bin 0 -> 148 bytes .../era5/era5_land-sea_mask_mendocino_subset.nc | Bin 0 -> 2976 bytes 10 files changed, 17 insertions(+) create mode 100755 tests/compare/gis/mendocino_nhdplus_catchment/rapid_connectivity_mendocino_sample.csv create mode 100644 tests/compare/gis/mendocino_nhdplus_catchment/weight_mendocino_era5_land_mask.csv create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.cpg create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.dbf create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.prj create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.sbn create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.sbx create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.shp create mode 100755 tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.shx create mode 100755 tests/data/lsm_grids/era5/era5_land-sea_mask_mendocino_subset.nc diff --git a/tests/compare/gis/mendocino_nhdplus_catchment/rapid_connectivity_mendocino_sample.csv b/tests/compare/gis/mendocino_nhdplus_catchment/rapid_connectivity_mendocino_sample.csv new file mode 100755 index 0000000..26d9523 --- /dev/null +++ b/tests/compare/gis/mendocino_nhdplus_catchment/rapid_connectivity_mendocino_sample.csv @@ -0,0 +1,6 @@ +8267669,8267695,0,0,0,0,0 +8267671,8267695,0,0,0,0,0 +8267697,8267725,0,0,0,0,0 +8267723,8267745,0,0,0,0,0 +8267695,8267725,2,8267669,8267671,0,0 +8267725,8267745,2,8267695,8267697,0,0 diff --git a/tests/compare/gis/mendocino_nhdplus_catchment/weight_mendocino_era5_land_mask.csv b/tests/compare/gis/mendocino_nhdplus_catchment/weight_mendocino_era5_land_mask.csv new file mode 100644 index 0000000..f453973 --- /dev/null +++ b/tests/compare/gis/mendocino_nhdplus_catchment/weight_mendocino_era5_land_mask.csv @@ -0,0 +1,9 @@ +rivid,area_sqm,lon_index,lat_index,npoints,lsm_grid_lon,lsm_grid_lat +8267669,1022424.4461225494,7,2,1,-123.25,39.5 +8267671,678361.3599336375,7,2,2,-123.25,39.5 +8267671,313143.12121458724,7,3,2,-123.25,39.25 +8267697,887055.8385729626,7,3,1,-123.25,39.25 +8267723,1539436.066144096,7,3,1,-123.25,39.25 +8267695,710034.1467391531,7,2,2,-123.25,39.5 +8267695,4245512.985625456,7,3,2,-123.25,39.25 +8267725,2136545.1570262704,7,3,1,-123.25,39.25 diff --git a/tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.cpg b/tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.cpg new file mode 100755 index 0000000..3ad133c --- /dev/null +++ b/tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.dbf b/tests/data/gis/mendocino_nhdplus_catchment/NHDCat_mendocino_watershed_hopland_sample.dbf new file mode 100755 index 0000000000000000000000000000000000000000..bda85fac9f9c39aefdaaab387769695d7181d102 GIT binary patch literal 2382 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 o0OS}fsJ)8zrDhKVTv&*4mnCO*O#`yM Date: Mon, 23 Nov 2020 20:15:04 -0600 Subject: [PATCH 10/13] Fixes typesetting. --- RAPIDpy/gis/weight.py | 9 ++++----- tests/test_gis.py | 11 +++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/RAPIDpy/gis/weight.py b/RAPIDpy/gis/weight.py index db64f43..f2eee8d 100644 --- a/RAPIDpy/gis/weight.py +++ b/RAPIDpy/gis/weight.py @@ -338,7 +338,7 @@ def CreateWeightTableECMWF(in_ecmwf_nc, in_ecmwf_lon_var = 'lon' if 'longitude' in variables_list: in_ecmwf_lon_var = 'longitude' - + # convert [0, 360] to [-180, 180] ecmwf_lon = \ (data_ecmwf_nc.variables[in_ecmwf_lon_var][:] + 180) % 360 - 180 @@ -349,8 +349,8 @@ def CreateWeightTableECMWF(in_ecmwf_nc, if in_ecmwf_mask_var in variables_list: ecmwf_mask = data_ecmwf_nc.variables[in_ecmwf_mask_var][0,:,:] else: - print('Variable "{}" not found in {}.'.format(in_ecmwf_mask_var, - in_ecmwf_nc)) + print('Variable "{}" not found in {}.'.format( + in_ecmwf_mask_var, in_ecmwf_nc)) print('Continuing with no land mask.') ecmwf_mask = None else: @@ -392,8 +392,6 @@ def CreateWeightTableLDAS(in_ldas_nc, The name of the field with the river ID (Ex. 'DrainLnID' or 'LINKNO'). in_connectivity_file: str The path to the RAPID connectivity file. - is the name of the stream network feature class. - (WARNING: Not always stable with GDAL.) out_weight_table: str The path to the output weight table file. area_id: str, optional @@ -404,6 +402,7 @@ def CreateWeightTableLDAS(in_ldas_nc, is the name of the stream network feature class. (WARNING: Not always stable with GDAL.) + Example: .. code:: python diff --git a/tests/test_gis.py b/tests/test_gis.py index 41e7cb8..933bff7 100644 --- a/tests/test_gis.py +++ b/tests/test_gis.py @@ -775,14 +775,17 @@ def test_gen_weight_table_era5_land_mask(): OUTPUT_DATA_PATH, "weight_mendocino_era5_land_mask.csv") #rapid_connect - rapid_connect_file = os.path.join(COMPARE_DATA_PATH, - "mendocino_nhdplus_catchment", - "rapid_connectivity_mendocino_sample.csv") + rapid_connect_file = os.path.join( + COMPARE_DATA_PATH, "mendocino_nhdplus_catchment", + "rapid_connectivity_mendocino_sample.csv") lsm_grid = os.path.join(LSM_INPUT_DATA_PATH, "era5", "era5_land-sea_mask_mendocino_subset.nc") CreateWeightTableECMWF(in_ecmwf_nc=lsm_grid, - in_catchment_shapefile=os.path.join(GIS_INPUT_DATA_PATH, 'mendocino_nhdplus_catchment', 'NHDCat_mendocino_watershed_hopland_sample.shp'), + in_catchment_shapefile=os.path.join( + GIS_INPUT_DATA_PATH, + 'mendocino_nhdplus_catchment', + 'NHDCat_mendocino_watershed_hopland_sample.shp'), river_id="FEATUREID", in_connectivity_file=rapid_connect_file, out_weight_table=generated_weight_table_file, From 7573313677650ef6670cc98efb6811ec248f3e4a Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 25 Nov 2020 11:41:43 -0500 Subject: [PATCH 11/13] ERA5 and ERA Interim t1279 changes for inflow processing. --- .../inflow/CreateInflowFileFromERARunoff.py | 88 ++++++++++++++++++ .../CreateInflowFileFromGriddedRunoff.py | 20 ++++ RAPIDpy/inflow/lsm_rapid_process.py | 93 ++++++++++++++----- 3 files changed, 179 insertions(+), 22 deletions(-) create mode 100644 RAPIDpy/inflow/CreateInflowFileFromERARunoff.py diff --git a/RAPIDpy/inflow/CreateInflowFileFromERARunoff.py b/RAPIDpy/inflow/CreateInflowFileFromERARunoff.py new file mode 100644 index 0000000..50a2e8b --- /dev/null +++ b/RAPIDpy/inflow/CreateInflowFileFromERARunoff.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +""" + CreateInflowFileFromERARunoff.py + RAPIDpy + + Created by Alan D. Snow, 2015 + Adapted from CreateInflowFileFromECMWFRunoff.py. + License: BSD-3-Clause +""" +from netCDF4 import Dataset + +from .CreateInflowFileFromGriddedRunoff import \ + CreateInflowFileFromGriddedRunoff + + +class CreateInflowFileFromERARunoff(CreateInflowFileFromGriddedRunoff): + """Create Inflow File From ERA Runoff + + Creates RAPID NetCDF input of water inflow based on + ERA runoff and previously created weight table. + """ + land_surface_model_name = "ERA" + header_wt = ['rivid', 'area_sqm', 'lon_index', 'lat_index', 'npoints'] + dims_oi = [['lon', 'lat', 'time'], + ['longitude', 'latitude', 'time'], + ['time','lon','lat'], + ['time','longitude','latitude'], + ['latitude','longitude','time'], + ['time','latitude','longitude']] + vars_oi = [['lon', 'lat', 'time', 'RO'],['lon','lat','time','ro'], + ['time','lon','lat','RO'],['time','lon','lat','ro'], + ['time','longitude','latitude','RO'],['time','longitude','latitude','ro'], + ['longitude', 'latitude', 'time', 'RO'],['longitude', 'latitude', 'time', 'ro'], + ['latitude','longitude','time','RO'],['latitude','longitude','time','ro'], + ['latitude','longitude','RO','time'],['latitude','longitude','ro','time'], + ['time','latitude','longitude','RO'],['time','latitude','longitude','ro']] + length_time = {"Daily": 1, "3-Hourly": 8, "1-Hourly":24} + + def __init__(self): + """Define the attributes to look for""" + self.runoff_vars = ['ro'] + super(CreateInflowFileFromERARunoff, self).__init__() + + def data_validation(self, in_nc): + """Check the necessary dimensions and variables in the input + netcdf data""" + data_nc = Dataset(in_nc) + + dims = list(data_nc.dimensions) + + if dims not in self.dims_oi: + data_nc.close() + raise Exception("{0} {1}".format(self.error_messages[1], dims)) + + nc_vars = list(data_nc.variables) + + if nc_vars == self.vars_oi[0]: + self.runoff_vars = [self.vars_oi[0][-1]] + elif nc_vars == self.vars_oi[1]: + self.runoff_vars = [self.vars_oi[1][-1]] + elif nc_vars == self.vars_oi[2]: + self.runoff_vars = [self.vars_oi[2][-1]] + elif nc_vars == self.vars_oi[3]: + self.runoff_vars = [self.vars_oi[3][-1]] + elif nc_vars == self.vars_oi[4]: + self.runoff_vars = [self.vars_oi[4][-1]] + elif nc_vars == self.vars_oi[5]: + self.runoff_vars = [self.vars_oi[5][-1]] + elif nc_vars == self.vars_oi[6]: + self.runoff_vars = [self.vars_oi[6][-1]] + elif nc_vars == self.vars_oi[7]: + self.runoff_vars = [self.vars_oi[7][-1]] + elif nc_vars == self.vars_oi[8]: + self.runoff_vars = [self.vars_oi[8][-1]] + elif nc_vars == self.vars_oi[9]: + self.runoff_vars = [self.vars_oi[9][-1]] + elif nc_vars == self.vars_oi[10]: + self.runoff_vars = [self.vars_oi[10][-2]] + elif nc_vars == self.vars_oi[11]: + self.runoff_vars = [self.vars_oi[11][-2]] + elif nc_vars == self.vars_oi[12]: + self.runoff_vars = [self.vars_oi[12][-1]] + elif nc_vars == self.vars_oi[13]: + self.runoff_vars = [self.vars_oi[13][-1]] + else: + data_nc.close() + raise Exception("{0} {1}".format(self.error_messages[2], nc_vars)) + data_nc.close() diff --git a/RAPIDpy/inflow/CreateInflowFileFromGriddedRunoff.py b/RAPIDpy/inflow/CreateInflowFileFromGriddedRunoff.py index b299ab7..4b62011 100644 --- a/RAPIDpy/inflow/CreateInflowFileFromGriddedRunoff.py +++ b/RAPIDpy/inflow/CreateInflowFileFromGriddedRunoff.py @@ -481,6 +481,26 @@ def execute(self, nc_file_list, index_list, in_weight_table, np.concatenate([ro_first_half, ro_second_half]), area_sqm_npoints) + elif grid_type == 't1279': + # A) ERA Interim Low Res (T1279) - data is cumulative + # from time 6/12 + # 0 1 2 3 4 + # (time zero not included, so assumed to be zero) + ro_first_half = \ + np.concatenate([data_goal[0:1, ], + np.subtract(data_goal[1:2, ], + data_goal[0:1, ])]) + # from time 15/18/21/24 + # (time restarts at time 12, assumed to be zero) + ro_second_half = \ + np.concatenate([data_goal[2:3, ], + np.subtract(data_goal[3:, ], + data_goal[2:3, ])]) + ro_stream = \ + np.multiply( + np.concatenate([ro_first_half, ro_second_half]), + area_sqm_npoints) + else: ro_stream = data_goal * area_sqm_npoints * \ conversion_factor diff --git a/RAPIDpy/inflow/lsm_rapid_process.py b/RAPIDpy/inflow/lsm_rapid_process.py index 6088f7a..6ef861d 100644 --- a/RAPIDpy/inflow/lsm_rapid_process.py +++ b/RAPIDpy/inflow/lsm_rapid_process.py @@ -20,8 +20,8 @@ # local imports from ..rapid import RAPID -from .CreateInflowFileFromERAInterimRunoff import \ - CreateInflowFileFromERAInterimRunoff +from .CreateInflowFileFromERARunoff import \ + CreateInflowFileFromERARunoff from .CreateInflowFileFromLDASRunoff import CreateInflowFileFromLDASRunoff from .CreateInflowFileFromWRFHydroRunoff import \ CreateInflowFileFromWRFHydroRunoff @@ -31,9 +31,6 @@ get_valid_directory_list, partition) -from ..pangaea.read import open_mfdataset - - # ----------------------------------------------------------------------------- # MULTIPROCESSING FUNCTION @@ -66,12 +63,10 @@ def generate_inflows_from_runoff(args): index_string = "Index: {0}".format(file_index_list[0]) if len(file_index_list) > 1: index_string += " to {0}".format(file_index_list[-1]) - print(index_string) runoff_string = "File(s): {0}".format(runoff_file_list[0]) if len(runoff_file_list) > 1: runoff_string += " to {0}".format(runoff_file_list[-1]) - print(runoff_string) - print("Converting inflow ...") + print("Converting inflow in generate inflow from runoff...") try: rapid_inflow_tool.execute(nc_file_list=runoff_file_list, index_list=file_index_list, @@ -94,6 +89,14 @@ def generate_inflows_from_runoff(args): # UTILITY FUNCTIONS # ----------------------------------------------------------------------------- DEFAULT_LSM_INPUTS = { + 'llera5': { + 'file_datetime_re_pattern': r'\d{8}', + 'file_datetime_pattern': "%Y%m%d", + }, + 't1279': { + 'file_datetime_re_pattern': r'\d{8}', + 'file_datetime_pattern': "%Y%m%d", + }, 't255': { 'file_datetime_re_pattern': r'\d{8}', 'file_datetime_pattern': "%Y%m%d", @@ -232,7 +235,6 @@ def identify_lsm_grid(lsm_grid_path): elif 'X' in var_list: # FLDAS longitude_var = 'X' - time_var = None if 'time' in var_list: time_var = 'time' @@ -280,6 +282,9 @@ def identify_lsm_grid(lsm_grid_path): elif var.lower() == "ro": # ERA Interim total_runoff_var = var + elif var.lower() == "RO": + # ERA5 + total_runoff_var = var elif var == "total runoff": # CMIP5 data total_runoff_var = var @@ -312,9 +317,11 @@ def identify_lsm_grid(lsm_grid_path): runoff_vars = [surface_runoff_var, subsurface_runoff_var] + print('Checking grid type.',total_runoff_var.lower(),institution) if institution == "European Centre for Medium-Range Weather Forecasts" \ - or total_runoff_var.lower() == "ro": + or total_runoff_var.lower() == "ro" or total_runoff_var.lower() == "RO": # these are the ECMWF models + print('This is an ECMWF model') if lat_dim_size == 361 and lon_dim_size == 720: print("Runoff file identified as ERA Interim Low Res (T255) GRID") # A) ERA Interim Low Res (T255) @@ -343,17 +350,53 @@ def identify_lsm_grid(lsm_grid_path): # Downloaded as 1.125 degree grid # dimensions: # longitude = 320 ; + # Downloaded as 1.125 degree grid + # dimensions: + # longitude = 320 ; + lsm_file_data["grid_type"] = 't511' + elif lat_dim_size == 161 and lon_dim_size == 320: + print("Runoff file identified as ERA 20CM (T159) GRID") + # C) ERA 20CM (T159) - 3hr - 10 ensembles + # Downloaded as 1.125 degree grid + # dimensions: + # longitude = 320 ; + # C) ERA 20CM (T159) - 3hr - 10 ensembles + # Downloaded as 1.125 degree grid + # dimensions: + # longitude = 320 ; # latitude = 161 ; lsm_file_data["description"] = "ERA 20CM (T159 Grid)" lsm_file_data["weight_file_name"] = r'weight_era_t159\.csv' lsm_file_data["model_name"] = "era_20cm" lsm_file_data["grid_type"] = 't159' + elif lat_dim_size == 721 and lon_dim_size == 1440: + print("Runoff file identified as ERA5 lat lon .25 degree GRID") + # C) ERA 20CM (lat lon quarter degree) - 1hr + # Downloaded as .25 degree grid + # dimensions: + # longitude = 1440 ; + # latitude = 721 ; + lsm_file_data["description"] = "ERA5 (LL Grid)" + lsm_file_data["weight_file_name"] = r'weight_era5_ll\.csv' + lsm_file_data["model_name"] = "era5" + lsm_file_data["grid_type"] = 'llera5' + elif lat_dim_size == 1280 and lon_dim_size == 2576: + print("Runoff file identified as ERAI Gaussian GRID") + # C) ERA INTERIM (Gaussian) - Daily + # Downloaded as Gaussian grid + # dimensions: + # longitude = 2576 ; + # latitude = 1280 ; + lsm_file_data["description"] = "ERAI (Gaussian Grid)" + lsm_file_data["weight_file_name"] = r'weight_erai_1279\.csv' + lsm_file_data["model_name"] = "erai_1279" + lsm_file_data["grid_type"] = 't1279' else: lsm_example_file.close() raise Exception("Unsupported ECMWF grid.") lsm_file_data["rapid_inflow_tool"] = \ - CreateInflowFileFromERAInterimRunoff() + CreateInflowFileFromERARunoff() elif institution == "NASA GSFC": if title == "GLDAS2.0 LIS land surface model output": @@ -530,7 +573,8 @@ def determine_start_end_timestep(lsm_file_list, file_datetime_pattern) \ + timedelta(seconds=(file_size_time-1) * time_step) else: - with open_mfdataset(lsm_file_list, #pangaea.open_mfdataset +# with pangaea.open_mfdataset(lsm_file_list, + with open_mfdataset(lsm_file_list, lat_var=lsm_grid_info['latitude_var'], lon_var=lsm_grid_info['longitude_var'], time_var=lsm_grid_info['time_var'], @@ -592,7 +636,13 @@ def run_lsm_rapid_process(rapid_executable_location, modeling_institution="US Army Engineer Research " "and Development Center", convert_one_hour_to_three=False, - expected_time_step=None): + expected_time_step=None, + BS_opt_dam='', + IS_dam_tot=0, + IS_dam_use=0, + dam_tot_id_file='', + dam_use_id_file='', + dam_file=''): # pylint: disable=anomalous-backslash-in-string """ This is the main process to generate inflow for RAPID and to run RAPID. @@ -802,7 +852,6 @@ def run_lsm_rapid_process(rapid_executable_location, lsm_file_list.append( os.path.join(walkdir_info[0], lsm_file)) lsm_file_list = sorted(lsm_file_list) - # IDENTIFY THE GRID lsm_file_data = identify_lsm_grid(lsm_file_list[0]) @@ -815,7 +864,6 @@ def run_lsm_rapid_process(rapid_executable_location, DEFAULT_LSM_INPUTS[lsm_file_data['grid_type']][ 'file_datetime_pattern'] file_re_match = re.compile(file_datetime_re_pattern) - # get subset based on time bounds if simulation_start_datetime is not None: print("Filtering files by datetime ...") @@ -828,9 +876,7 @@ def run_lsm_rapid_process(rapid_executable_location, break if file_date >= simulation_start_datetime: lsm_file_list_subset.append(lsm_file) - lsm_file_list = sorted(lsm_file_list_subset) - print("Running from {0} to {1}".format(lsm_file_list[0], lsm_file_list[-1])) @@ -843,7 +889,6 @@ def run_lsm_rapid_process(rapid_executable_location, file_datetime_pattern=file_datetime_pattern, expected_time_step=expected_time_step, lsm_grid_info=lsm_file_data) - # VALIDATING INPUT IF DIVIDING BY 3 if (lsm_file_data['grid_type'] in ('nldas', 'lis', 'joules')) \ and convert_one_hour_to_three: @@ -863,7 +908,6 @@ def run_lsm_rapid_process(rapid_executable_location, actual_simulation_start_datetime, actual_simulation_end_datetime, ensemble_file_ending) - # run LSM processes for master_watershed_input_directory, \ master_watershed_output_directory in rapid_directories: @@ -939,7 +983,7 @@ def run_lsm_rapid_process(rapid_executable_location, # generate_inflows_from_runoff(( # cpu_grouped_file_list, # partition_index_list[loop_index], -# lsm_file_data['weight_table_file'], +# weight_table_file, #m_file_data['weight_table_file'], # lsm_file_data['grid_type'], # master_rapid_runoff_file, # lsm_file_data['rapid_inflow_tool'], @@ -959,7 +1003,13 @@ def run_lsm_rapid_process(rapid_executable_location, ZS_TauR=time_step, ZS_dtR=15 * 60, ZS_TauM=total_num_time_steps * time_step, - ZS_dtM=time_step) + ZS_dtM=time_step, + BS_opt_dam=BS_opt_dam, + IS_dam_tot=IS_dam_tot, + IS_dam_use=IS_dam_use, + dam_tot_id_file=dam_tot_id_file, + dam_use_id_file=dam_use_id_file, + dam_file=dam_file) if initial_flows_file and os.path.exists(initial_flows_file): rapid_manager.update_parameters( @@ -1027,7 +1077,6 @@ def run_lsm_rapid_process(rapid_executable_location, num_cpus=num_cpus, storm_duration_days=storm_length_days, method=return_period_method) - # generate seasonal averages file if generate_seasonal_averages_file and \ os.path.exists(lsm_rapid_output_file) and \ From e0ddff54cc592367c012e41fbe3373c27073459c Mon Sep 17 00:00:00 2001 From: Chase Hamilton Date: Thu, 11 Aug 2022 13:41:23 -0500 Subject: [PATCH 12/13] 'all references to "erdc-cm" changed to "erdc"' --- README.md | 6 +++--- docs/gis_stream_network.rst | 2 +- docs/index.rst | 4 ++-- docs/installation.rst | 6 +++--- setup.py | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index effd289..e4cd38f 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ More information about installation and the input parameters for RAPID can be fo The source code for RAPID is located at https://github.com/c-h-david/rapid/. -[![DOI](https://zenodo.org/badge/19918/erdc-cm/RAPIDpy.svg)](https://zenodo.org/badge/latestdoi/19918/erdc-cm/RAPIDpy) +[![DOI](https://zenodo.org/badge/19918/erdc/RAPIDpy.svg)](https://zenodo.org/badge/latestdoi/19918/erdc-cm/RAPIDpy) -[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-yellow.svg)](https://github.com/erdc-cm/RAPIDpy/blob/master/LICENSE) +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-yellow.svg)](https://github.com/erdc/RAPIDpy/blob/master/LICENSE) [![PyPI version](https://badge.fury.io/py/RAPIDpy.svg)](https://badge.fury.io/py/RAPIDpy) @@ -60,5 +60,5 @@ Ahmad A Tavakoly. (2017). RAPID input files corresponding to the Mississippi Riv ## Other tools to prepare input for RAPID - For ESRI users: https://github.com/Esri/python-toolbox-for-rapid -- Modified version of the ESRI RAPID Toolbox: https://github.com/erdc-cm/python-toolbox-for-rapid +- Modified version of the ESRI RAPID Toolbox: https://github.com/erdc/python-toolbox-for-rapid - For the NHDPlus dataset: https://github.com/c-h-david/RRR diff --git a/docs/gis_stream_network.rst b/docs/gis_stream_network.rst index 45857ef..0d5b9b4 100644 --- a/docs/gis_stream_network.rst +++ b/docs/gis_stream_network.rst @@ -7,7 +7,7 @@ Using ArcHydro to Generate Stream Network See: - https://github.com/Esri/python-toolbox-for-rapid -- https://github.com/erdc-cm/python-toolbox-for-rapid +- https://github.com/erdc/python-toolbox-for-rapid Using TauDEM to Generate Stream Network --------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index 782a2e3..5aa9c4d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,7 +26,7 @@ https://github.com/c-h-david/rapid. .. |Coverage Status| image:: https://coveralls.io/repos/github/erdc/RAPIDpy/badge.svg?branch=master :target: https://coveralls.io/github/erdc/RAPIDpy .. |License (3-Clause BSD)| image:: https://img.shields.io/badge/license-BSD%203--Clause-yellow.svg - :target: https://github.com/erdc-cm/RAPIDpy/blob/master/LICENSE + :target: https://github.com/erdc/RAPIDpy/blob/master/LICENSE Contents: @@ -90,7 +90,7 @@ Other tools to prepare input for RAPID --------------------------------------- - For ESRI users: https://github.com/Esri/python-toolbox-for-rapid -- Modified version of the ESRI RAPID Toolbox: https://github.com/erdc-cm/python-toolbox-for-rapid +- Modified version of the ESRI RAPID Toolbox: https://github.com/erdc/python-toolbox-for-rapid - For the NHDPlus dataset: https://github.com/c-h-david/RRR diff --git a/docs/installation.rst b/docs/installation.rst index 563cfa6..364ff74 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -79,11 +79,11 @@ or from https://conda.io/miniconda.html. This is how you get the most up-to-date version of the code. -.. note:: If you don't have git, you can download the code from https://github.com/erdc-cm/RAPIDpy +.. note:: If you don't have git, you can download the code from https://github.com/erdc/RAPIDpy :: - $ git clone https://github.com/erdc-cm/RAPIDpy.git + $ git clone https://github.com/erdc/RAPIDpy.git $ cd RAPIDpy $ conda env create -f rapidpy_env.yml $ conda activate rapidpy_env @@ -93,7 +93,7 @@ To develop on the latest version: :: - $ git clone https://github.com/erdc-cm/RAPIDpy.git + $ git clone https://github.com/erdc/RAPIDpy.git $ cd RAPIDpy $ conda env create -f rapidpy_env.yml $ conda activate rapidpy_env diff --git a/setup.py b/setup.py index 3bff51f..ef0b48f 100644 --- a/setup.py +++ b/setup.py @@ -11,12 +11,12 @@ 'parameters for RAPID can be found at http://rapid-hub.org.' ' The source code for RAPID is located at ' 'https://github.com/c-h-david/rapid/. \n\n' - '.. image:: https://zenodo.org/badge/19918/erdc-cm/RAPIDpy.svg \n' - ' :target: https://zenodo.org/badge/latestdoi/19918/erdc-cm/RAPIDpy', + '.. image:: https://zenodo.org/badge/19918/erdc/RAPIDpy.svg \n' + ' :target: https://zenodo.org/badge/latestdoi/19918/erdc/RAPIDpy', keywords='RAPID', author='Alan Dee Snow', author_email='alan.d.snow@usace.army.mil', - url='https://github.com/erdc-cm/RAPIDpy', + url='https://github.com/erdc/RAPIDpy', license='BSD 3-Clause', packages=find_packages(), package_data={'': ['gis/lsm_grids/*.nc']}, From ca4d593c8c48e856d4aa30d3ce5ecbbc0cf5c0be Mon Sep 17 00:00:00 2001 From: Chase Hamilton Date: Thu, 11 Aug 2022 14:36:54 -0500 Subject: [PATCH 13/13] 'all references to "erdc-cm" changed to "erdc"' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4cd38f..30f2eff 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ More information about installation and the input parameters for RAPID can be fo The source code for RAPID is located at https://github.com/c-h-david/rapid/. -[![DOI](https://zenodo.org/badge/19918/erdc/RAPIDpy.svg)](https://zenodo.org/badge/latestdoi/19918/erdc-cm/RAPIDpy) +[![DOI](https://zenodo.org/badge/19918/erdc/RAPIDpy.svg)](https://zenodo.org/badge/latestdoi/19918/erdc/RAPIDpy) [![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-yellow.svg)](https://github.com/erdc/RAPIDpy/blob/master/LICENSE)