Skip to content

Commit da9a8fe

Browse files
authored
Merge pull request #70 from NikosMastrantonas/make_mct_rivers_mask
Make mct rivers mask
2 parents 0025ef2 + 86547aa commit da9a8fe

File tree

6 files changed

+31
-24
lines changed

6 files changed

+31
-24
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Created by .ignore support plugin (hsz.mobi)
2+
.ipynb*
23
.idea
34
*.nc
45
**/__pycache__/
@@ -57,4 +58,4 @@ tests/data/gridding/meteo_out/**/*.tiff
5758
.project
5859
.pydevproject
5960

60-
.ipynb_checkpoints/
61+
.ipynb_checkpoints/

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ The tool can take the following additional input arguments:
778778
- `-U`, `--minuparea`: Minimum upstream drainage area for a pixel to be included in the MCT rivers mask (uses the same units as in the -u file) (default: 0)
779779
- `-E`, `--coordsnames`: Coordinates names for lat, lon (in this order with space!) used in the netcdf files. The function checks for 3 commonly used names (x, lon, rlon for longitudes, and y, lat, rlat for latitudes). Therefere, it is recommended to keep the default value.
780780

781-
The tool generates the following outputs:
781+
The tool generates the following outputs (when called from command line as main script):
782782

783783
- `-O`, `--outputfilename`: Output file containing the rivers mask where LISFLOOD can use the MCT diffusive wave routing (default: chanmct.nc)
784784

@@ -791,8 +791,9 @@ mctrivers -i changrad.nc -l ldd.nc -u upArea.nc -O chanmct.nc -U 500
791791
```
792792

793793
```python
794+
# no data are saved when called inside python
794795
from lisfloodutilities.mctrivers.mctrivers import mct_mask
795-
mct_mask(channels_slope_file='changrad.nc', ldd_file='ldd.nc', uparea_file='upArea.nc', minuparea=500, outputfile='chanmct.nc')
796+
mct_mask_ds = mct_mask(channels_slope_file='changrad.nc', ldd_file='ldd.nc', uparea_file='upArea.nc', minuparea=500)
796797
```
797798

798799
Example of command that will generate an MCT rivers mask with pixels where riverbed slope < 0.0005, drainage area > 0 (same as default) kms and at least 3 downstream pixels meet the same two conditions. Also a mask (mask.nc) will be used, and the coords names in the nc files are "Lat1", "Lon1" for lat, lon respectively:
@@ -803,7 +804,7 @@ mctrivers -i changrad.nc -l ldd.nc -u upArea.nc -O chanmct.nc -m mask.nc -E Lat1
803804

804805
```python
805806
from lisfloodutilities.mctrivers.mctrivers import mct_mask
806-
mct_mask(channels_slope_file='changrad.nc', ldd_file='ldd.nc', uparea_file='upArea.nc', mask_file='mask.nc', slp_threshold=0.0005, nloops=3, coords=["Lat1", "Lon1"], outputfile='chanmct.nc')
807+
mct_mask_ds = mct_mask(channels_slope_file='changrad.nc', ldd_file='ldd.nc', uparea_file='upArea.nc', mask_file='mask.nc', slp_threshold=0.0005, nloops=3, coords=["Lat1", "Lon1"])
807808
```
808809

809810

src/lisfloodutilities/mctrivers/mctrivers.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ def getarg():
4343

4444

4545
def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
46-
slp_threshold=0.001, nloops=5, minuparea=0, coords_names='None',
47-
outputfile='chanmct.nc'):
46+
slp_threshold=0.001, nloops=5, minuparea=0, coords_names='None'):
4847
"""
4948
5049
Builds a mask of mild sloping rivers for use in LISFLOOD with MCT diffusive river routing. It takes LISFLOOD channels slope map (changrad.nc), the LDD (ldd.nc),
@@ -57,7 +56,7 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
5756
5857
channels_slope_file: LISFLOOD channels gradient map (changrad.nc)
5958
ldd_file: LISFLOOD local drain direction file (ldd.nc)
60-
uparea_file: LISFLOOD Uustream area file (upArea.nc)
59+
uparea_file: LISFLOOD Upstream area file (upArea.nc)
6160
mask_file: a mask nc file; if not given (default) all cells are considered valid.
6261
slp_threshold: Riverbed slope threshold to use MCT diffusive wave routing (default: 0.001)
6362
nloops: Number of consecutive downstream grid cells that also need to comply with the slope requirement for including a grid cell in the MCT rivers mask (default: 5)
@@ -69,8 +68,7 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
6968
two conditions, considering the units of the upArea.nc file are given in kms:
7069
7170
mct_mask(channels_slope_file='changrad.nc', ldd_file='ldd.nc', uparea_file='upArea.nc', mask_file='mask.nc',
72-
slp_threshold=0.001, nloops=5, minuparea=500, coords_names=['y' , 'x'],
73-
outputfile='chanmct.nc')
71+
slp_threshold=0.001, nloops=5, minuparea=500, coords_names=['y' , 'x'])
7472
"""
7573
# ---------------- Read LDD (Note that for EFAS5 there is small shift of values for CH)
7674
LD = xr.open_dataset(ldd_file)
@@ -98,7 +96,7 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
9896
# proprocess CH dataset for having correct format
9997
CH = xr.open_dataset(channels_slope_file)
10098
old_name = [i for i in list(CH.data_vars) if sorted(CH[i].dims)==sorted([x_proj, y_proj])]
101-
CH = CH.rename({old_name[0]: "changrad"}) # only 1 variable complies with above check
99+
CH = CH.rename({old_name[0]: "changrad"}) # only 1 variable complies with the above check
102100
CH['changrad'] = CH['changrad'].transpose(y_proj, x_proj) # make sure dims order is as pcraster needs
103101

104102
# ---------------- Set clone map for pcraster
@@ -149,7 +147,7 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
149147
# ---------------- Read upstream area
150148
UA = xr.open_dataset(uparea_file)
151149
old_name = [i for i in list(UA.data_vars) if sorted(UA[i].dims)==sorted([x_proj, y_proj])]
152-
UA = UA.rename({old_name[0]: "domain"})['domain'] # only 1 variable complies with above if
150+
UA = UA.rename({old_name[0]: "domain"})['domain'] # only 1 variable complies with the above if statement
153151

154152
# convert the xarray to pcraster
155153
UA = UA>=minuparea # check that the area is over the minimum
@@ -160,11 +158,10 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
160158
try:
161159
MX = xr.open_dataset(mask_file)
162160
old_name = [i for i in list(MX.data_vars) if sorted(MX[i].dims)==sorted([x_proj, y_proj])]
163-
MX = MX.rename({old_name[0]: "domain"})['domain'] # only 1 variable complies with above if
161+
MX = MX.rename({old_name[0]: "domain"})['domain'] # only 1 variable complies with the above if statement
164162
except:
165163
print(f'The given mask path {mask_file} is not a valid path. All domain read from LDD file {ldd_file} is considered vaid.')
166164
MX = LD.copy(deep=True)
167-
MX = MX.fillna(0)*0+1
168165

169166
# use the exact same coords from channel slope file, just in case there are precision differences
170167
MX = MX.assign_coords({x_proj: x_all, y_proj: y_all})
@@ -180,11 +177,19 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
180177
# Loop nloops times and use downstream function to find out if each cell has nloop MCT cells downstream
181178
# Downstream function gives the value in the downstream pixel in a map:
182179
# here it gives 1 if the downstream pixel is Muskingum, zero otherwise.
180+
# Note that for pits it always gives its own value so we need to mask out all pits as ann intermidiate step
181+
182+
# modify data, so that the most downstream point is masked out (otherwise the below loop gives for all points the same value)
183+
downstream_actual_mask_pcr = pcr.downstreamdist(ldd_pcr)
184+
downstream_actual_mask_pcr = pcr.ifthenelse(downstream_actual_mask_pcr == 0, pcr.boolean(0), pcr.boolean(1))
185+
downstream_actual_mask_pcr = pcr.scalar(downstream_actual_mask_pcr)
186+
183187
# The loop is used to count how many pixels are MCT downstream, as at each loop we move the values 1 pixel upstream
184188
# At the end of the loop, each element of the array has the number of downstream MCT pixels for that pixel
185189
for loops in range(0, nloops):
186190
# get the value on the downstream cell and put it in a mask
187191
downstream_cells_pcr = pcr.downstream(ldd_pcr, downstream_cells_pcr)
192+
downstream_cells_pcr = downstream_cells_pcr*downstream_actual_mask_pcr
188193
sum_rivers_pcr = sum_rivers_pcr + downstream_cells_pcr
189194

190195
# ---------------- Generate a new MCT rivers mask
@@ -207,16 +212,14 @@ def mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file='',
207212
MCT.name = 'mct_mask'
208213

209214
# mask final data with the mask_file
210-
MCT = MCT.where(MX==1)
215+
MCT = MCT.where(MX.notnull())
211216

212-
# lisflood does not read NaNs so the data are saved as boolean 0-1, with 0 being flagged as NaN for python reading
213-
MCT.to_netcdf(outputfile, encoding={"mct_mask": {'_FillValue': 0, 'dtype': 'int8'}})
214217
return MCT
215218

216219

217220
def main():
218-
'function for runnign from command line'
219-
# ---------------- Read settings
221+
'function for running from command line'
222+
# ---------------- Read settingss
220223
args = getarg()
221224
channels_slope_file_arg = args.changradfile
222225
ldd_file_arg = args.LDDfile
@@ -228,10 +231,11 @@ def main():
228231
coords_names_arg = args.coordsnames
229232
outputfile_arg = args.outputfilename
230233

231-
mct_mask(channels_slope_file=channels_slope_file_arg, ldd_file=ldd_file_arg, uparea_file=uparea_file_arg, mask_file=mask_file_arg,
232-
slp_threshold=slp_threshold_arg, nloops=nloops_arg, minuparea=minuparea_arg, coords_names=coords_names_arg,
233-
outputfile=outputfile_arg)
234-
234+
mct_final = mct_mask(channels_slope_file=channels_slope_file_arg, ldd_file=ldd_file_arg, uparea_file=uparea_file_arg, mask_file=mask_file_arg,
235+
slp_threshold=slp_threshold_arg, nloops=nloops_arg, minuparea=minuparea_arg, coords_names=coords_names_arg)
236+
# lisflood does not read NaNs so the data are saved as boolean 0-1, with -1 being flagged as NaN for python reading
237+
mct_final.to_netcdf(outputfile_arg, encoding={"mct_mask": {'_FillValue': -1, 'dtype': 'int8'}})
238+
235239

236240
if __name__ == "__main__":
237241
main()
Binary file not shown.
Binary file not shown.

tests/test_mctrivers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ def run(self, slp_threshold, nloops, minuparea, coords_names, case_name):
3131
outputfile = os.path.join(self.out_path_run, 'mctmask.nc')
3232

3333
# generate the mct river mask
34-
mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file, slp_threshold, nloops, minuparea, coords_names, outputfile)
35-
34+
mct_final = mct_mask(channels_slope_file, ldd_file, uparea_file, mask_file, slp_threshold, nloops, minuparea, coords_names)
35+
mct_final.to_netcdf(outputfile, encoding={"mct_mask": {'_FillValue': 0, 'dtype': 'int8'}})
36+
3637
# compare the generated mask with the reference one
3738
ref_file = self.out_path_ref+'/mctmask.nc'
3839
reference = xr.open_dataset(ref_file)

0 commit comments

Comments
 (0)