rapid2 2.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rapid2/__init__.py +79 -0
- rapid2/cli/__init__.py +0 -0
- rapid2/cli/_cmpncf.py +315 -0
- rapid2/cli/_cpllsm.py +278 -0
- rapid2/cli/_dgldas2.py +290 -0
- rapid2/cli/_m3rivtoqext.py +161 -0
- rapid2/cli/_rapid2.py +238 -0
- rapid2/cli/_sandboxqext.py +172 -0
- rapid2/cli/_zeroqinit.py +151 -0
- rapid2/core/__init__.py +0 -0
- rapid2/core/chck_bas.py +139 -0
- rapid2/core/chck_cpl.py +85 -0
- rapid2/core/make_0bi_tbl.py +88 -0
- rapid2/core/make_CCC_mat.py +102 -0
- rapid2/core/make_Mus_mat.py +108 -0
- rapid2/core/make_Net_mat.py +92 -0
- rapid2/core/make_Wdw_mat.py +166 -0
- rapid2/core/prep_Qex_ncf.py +169 -0
- rapid2/core/prep_Qfi_ncf.py +108 -0
- rapid2/core/prep_Qou_ncf.py +169 -0
- rapid2/core/prep_skl_ncf.py +144 -0
- rapid2/core/read_bas_vec.py +77 -0
- rapid2/core/read_con_vec.py +84 -0
- rapid2/core/read_cpl_vec.py +99 -0
- rapid2/core/read_crd_vec.py +91 -0
- rapid2/core/read_kpr_vec.py +83 -0
- rapid2/core/read_nml_tbl.py +109 -0
- rapid2/core/read_std_vec.py +156 -0
- rapid2/core/read_xpr_vec.py +83 -0
- rapid2/core/updt_Mus_Qou.py +131 -0
- rapid2-2.0.0b1.dist-info/METADATA +266 -0
- rapid2-2.0.0b1.dist-info/RECORD +36 -0
- rapid2-2.0.0b1.dist-info/WHEEL +5 -0
- rapid2-2.0.0b1.dist-info/entry_points.txt +8 -0
- rapid2-2.0.0b1.dist-info/licenses/LICENSE +24 -0
- rapid2-2.0.0b1.dist-info/top_level.txt +1 -0
rapid2/__init__.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# *****************************************************************************
|
|
2
|
+
# __init__.py
|
|
3
|
+
# *****************************************************************************
|
|
4
|
+
|
|
5
|
+
# Purpose:
|
|
6
|
+
# This file used in Python to define packages and initialize their namespaces.
|
|
7
|
+
# Author:
|
|
8
|
+
# Cedric H. David, 2025-2025
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# *****************************************************************************
|
|
12
|
+
# Initialization
|
|
13
|
+
# *****************************************************************************
|
|
14
|
+
|
|
15
|
+
# -----------------------------------------------------------------------------
|
|
16
|
+
# Dynamic Package Versioning
|
|
17
|
+
# -----------------------------------------------------------------------------
|
|
18
|
+
import importlib.metadata
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
__version__ = importlib.metadata.version("rapid2")
|
|
22
|
+
except importlib.metadata.PackageNotFoundError:
|
|
23
|
+
__version__ = "unknown"
|
|
24
|
+
|
|
25
|
+
# -----------------------------------------------------------------------------
|
|
26
|
+
# Top-Level API Facade
|
|
27
|
+
# -----------------------------------------------------------------------------
|
|
28
|
+
from .core.chck_bas import chck_bas
|
|
29
|
+
from .core.chck_cpl import chck_cpl
|
|
30
|
+
from .core.make_0bi_tbl import make_0bi_tbl
|
|
31
|
+
from .core.make_CCC_mat import make_CCC_mat
|
|
32
|
+
from .core.make_Mus_mat import make_Mus_mat
|
|
33
|
+
from .core.make_Net_mat import make_Net_mat
|
|
34
|
+
from .core.make_Wdw_mat import make_Wdw_mat
|
|
35
|
+
from .core.prep_Qex_ncf import prep_Qex_ncf
|
|
36
|
+
from .core.prep_Qfi_ncf import prep_Qfi_ncf
|
|
37
|
+
from .core.prep_Qou_ncf import prep_Qou_ncf
|
|
38
|
+
from .core.prep_skl_ncf import prep_skl_ncf
|
|
39
|
+
from .core.read_bas_vec import read_bas_vec
|
|
40
|
+
from .core.read_con_vec import read_con_vec
|
|
41
|
+
from .core.read_cpl_vec import read_cpl_vec
|
|
42
|
+
from .core.read_crd_vec import read_crd_vec
|
|
43
|
+
from .core.read_kpr_vec import read_kpr_vec
|
|
44
|
+
from .core.read_nml_tbl import read_nml_tbl
|
|
45
|
+
from .core.read_std_vec import read_std_vec
|
|
46
|
+
from .core.read_xpr_vec import read_xpr_vec
|
|
47
|
+
from .core.updt_Mus_Qou import updt_Mus_Qou
|
|
48
|
+
|
|
49
|
+
# -----------------------------------------------------------------------------
|
|
50
|
+
# Explicit Public Interface
|
|
51
|
+
# -----------------------------------------------------------------------------
|
|
52
|
+
__all__ = [
|
|
53
|
+
"__version__",
|
|
54
|
+
"chck_bas",
|
|
55
|
+
"chck_cpl",
|
|
56
|
+
"make_0bi_tbl",
|
|
57
|
+
"make_CCC_mat",
|
|
58
|
+
"make_Mus_mat",
|
|
59
|
+
"make_Net_mat",
|
|
60
|
+
"make_Wdw_mat",
|
|
61
|
+
"prep_Qex_ncf",
|
|
62
|
+
"prep_Qfi_ncf",
|
|
63
|
+
"prep_Qou_ncf",
|
|
64
|
+
"prep_skl_ncf",
|
|
65
|
+
"read_bas_vec",
|
|
66
|
+
"read_con_vec",
|
|
67
|
+
"read_cpl_vec",
|
|
68
|
+
"read_crd_vec",
|
|
69
|
+
"read_kpr_vec",
|
|
70
|
+
"read_nml_tbl",
|
|
71
|
+
"read_std_vec",
|
|
72
|
+
"read_xpr_vec",
|
|
73
|
+
"updt_Mus_Qou",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# *****************************************************************************
|
|
78
|
+
# End
|
|
79
|
+
# *****************************************************************************
|
rapid2/cli/__init__.py
ADDED
|
File without changes
|
rapid2/cli/_cmpncf.py
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# *****************************************************************************
|
|
3
|
+
# _cmpncf.py
|
|
4
|
+
# *****************************************************************************
|
|
5
|
+
|
|
6
|
+
# Author:
|
|
7
|
+
# Cedric H. David, 2016-2026
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# *****************************************************************************
|
|
11
|
+
# Import Python modules
|
|
12
|
+
# *****************************************************************************
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
import netCDF4
|
|
17
|
+
import numpy as np
|
|
18
|
+
from numpy.ma import MaskedArray
|
|
19
|
+
|
|
20
|
+
from rapid2 import (
|
|
21
|
+
__version__,
|
|
22
|
+
make_0bi_tbl,
|
|
23
|
+
read_std_vec,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# *****************************************************************************
|
|
28
|
+
# Main
|
|
29
|
+
# *****************************************************************************
|
|
30
|
+
def main() -> None:
|
|
31
|
+
|
|
32
|
+
# -------------------------------------------------------------------------
|
|
33
|
+
# Initialize the argument parser and add valid arguments
|
|
34
|
+
# -------------------------------------------------------------------------
|
|
35
|
+
parser = argparse.ArgumentParser(
|
|
36
|
+
description=(
|
|
37
|
+
"Compare RAPID input/output netCDF files for numerical regression "
|
|
38
|
+
"testing."
|
|
39
|
+
),
|
|
40
|
+
epilog=(
|
|
41
|
+
"examples:\n"
|
|
42
|
+
" cmpncf "
|
|
43
|
+
"--previous "
|
|
44
|
+
"input/Tutorial/Qinit_GLDAS_2.1_VIC_2010-01_GOLD.nc4 "
|
|
45
|
+
"--now "
|
|
46
|
+
"input/Tutorial/Qinit_GLDAS_2.1_VIC_2010-01.nc4 "
|
|
47
|
+
"--relative_tolerance 1e-6 "
|
|
48
|
+
"--absolute_tolerance 1e-3\n"
|
|
49
|
+
" cmpncf "
|
|
50
|
+
"--previous "
|
|
51
|
+
"input/Tutorial/Qext_GLDAS_2.1_VIC_2010-01_GOLD.nc4 "
|
|
52
|
+
"--now "
|
|
53
|
+
"input/Tutorial/Qext_GLDAS_2.1_VIC_2010-01.nc4 "
|
|
54
|
+
"--relative_tolerance 1e-6 "
|
|
55
|
+
"--absolute_tolerance 1e-3"
|
|
56
|
+
),
|
|
57
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--version", action="version", version=f"rapid2 {__version__}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-prv",
|
|
66
|
+
"--previous",
|
|
67
|
+
dest="prv",
|
|
68
|
+
metavar="PREVIOUS",
|
|
69
|
+
type=str,
|
|
70
|
+
required=True,
|
|
71
|
+
help="specify the old netCDF file",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"-now",
|
|
76
|
+
"--now",
|
|
77
|
+
dest="now",
|
|
78
|
+
metavar="NOW",
|
|
79
|
+
type=str,
|
|
80
|
+
required=True,
|
|
81
|
+
help="specify the new netCDF file",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"-rtl",
|
|
86
|
+
"--relative_tolerance",
|
|
87
|
+
dest="rtl",
|
|
88
|
+
metavar="RELATIVE_TOLERANCE",
|
|
89
|
+
type=str,
|
|
90
|
+
required=False,
|
|
91
|
+
default="0",
|
|
92
|
+
help="specify the relative tolerance",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
parser.add_argument(
|
|
96
|
+
"-atl",
|
|
97
|
+
"--absolute_tolerance",
|
|
98
|
+
dest="atl",
|
|
99
|
+
metavar="ABSOLUTE_TOLERANCE",
|
|
100
|
+
type=str,
|
|
101
|
+
required=False,
|
|
102
|
+
default="0",
|
|
103
|
+
help="specify the absolute tolerance",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# -------------------------------------------------------------------------
|
|
107
|
+
# Parse arguments and assign to variables
|
|
108
|
+
# -------------------------------------------------------------------------
|
|
109
|
+
args = parser.parse_args()
|
|
110
|
+
|
|
111
|
+
prv_ncf = args.prv
|
|
112
|
+
now_ncf = args.now
|
|
113
|
+
YS_rtl = args.rtl
|
|
114
|
+
YS_atl = args.atl
|
|
115
|
+
|
|
116
|
+
print(
|
|
117
|
+
f"Comparing {prv_ncf} "
|
|
118
|
+
f"with {now_ncf} "
|
|
119
|
+
f"relative tolerance {YS_rtl} "
|
|
120
|
+
f"absolute tolerance {YS_atl}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
ZS_rtl = np.float64(YS_rtl)
|
|
124
|
+
ZS_atl = np.float64(YS_atl)
|
|
125
|
+
|
|
126
|
+
# -------------------------------------------------------------------------
|
|
127
|
+
# Get metadata in netCDF files
|
|
128
|
+
# -------------------------------------------------------------------------
|
|
129
|
+
(
|
|
130
|
+
IV_riv_prv,
|
|
131
|
+
ZV_lon_prv,
|
|
132
|
+
ZV_lat_prv,
|
|
133
|
+
IV_tim_prv,
|
|
134
|
+
IM_tim_prv,
|
|
135
|
+
) = read_std_vec(prv_ncf)
|
|
136
|
+
|
|
137
|
+
(
|
|
138
|
+
IV_riv_now,
|
|
139
|
+
ZV_lon_now,
|
|
140
|
+
ZV_lat_now,
|
|
141
|
+
IV_tim_now,
|
|
142
|
+
IM_tim_now,
|
|
143
|
+
) = read_std_vec(now_ncf)
|
|
144
|
+
|
|
145
|
+
# -------------------------------------------------------------------------
|
|
146
|
+
# Compare dimension sizes
|
|
147
|
+
# -------------------------------------------------------------------------
|
|
148
|
+
if len(IV_riv_prv) == len(IV_riv_now):
|
|
149
|
+
IS_riv_tot = len(IV_riv_prv)
|
|
150
|
+
print(f"Common number of river reaches: {IS_riv_tot}")
|
|
151
|
+
else:
|
|
152
|
+
print(
|
|
153
|
+
f"ERROR - The number of river reaches differs: "
|
|
154
|
+
f"{len(IV_riv_prv)} <> {len(IV_riv_now)}"
|
|
155
|
+
)
|
|
156
|
+
sys.exit(1)
|
|
157
|
+
|
|
158
|
+
if len(IV_tim_prv) == len(IV_tim_now):
|
|
159
|
+
IS_tim = len(IV_tim_prv)
|
|
160
|
+
print(f"Common number of time steps : {IS_tim}")
|
|
161
|
+
else:
|
|
162
|
+
print(
|
|
163
|
+
f"ERROR - The number of time steps differs: "
|
|
164
|
+
f"{len(IV_tim_prv)} <> {len(IV_tim_now)}"
|
|
165
|
+
)
|
|
166
|
+
sys.exit(1)
|
|
167
|
+
|
|
168
|
+
# -------------------------------------------------------------------------
|
|
169
|
+
# Compare rivid values
|
|
170
|
+
# -------------------------------------------------------------------------
|
|
171
|
+
if np.array_equal(IV_riv_prv, IV_riv_now):
|
|
172
|
+
print("The rivids and their sort are both the same")
|
|
173
|
+
else:
|
|
174
|
+
if np.array_equal(np.sort(IV_riv_prv), np.sort(IV_riv_now)):
|
|
175
|
+
print("WARNING - The rivids are the same, but sorted differently")
|
|
176
|
+
_, _, IV_0bi_prv = make_0bi_tbl(IV_riv_now, IV_riv_prv)
|
|
177
|
+
else:
|
|
178
|
+
print("ERROR - The rivids differ")
|
|
179
|
+
sys.exit(1)
|
|
180
|
+
|
|
181
|
+
# -------------------------------------------------------------------------
|
|
182
|
+
# Compare other metadata values
|
|
183
|
+
# -------------------------------------------------------------------------
|
|
184
|
+
if np.array_equal(ZV_lon_prv, ZV_lon_now):
|
|
185
|
+
print("The longitude values are the same")
|
|
186
|
+
else:
|
|
187
|
+
print("ERROR - The longitude values differ")
|
|
188
|
+
sys.exit(1)
|
|
189
|
+
|
|
190
|
+
if np.array_equal(ZV_lat_prv, ZV_lat_now):
|
|
191
|
+
print("The latitude values are the same")
|
|
192
|
+
else:
|
|
193
|
+
print("ERROR - The latitude values differ")
|
|
194
|
+
sys.exit(1)
|
|
195
|
+
|
|
196
|
+
if np.array_equal(IV_tim_prv, IV_tim_now):
|
|
197
|
+
print("The time values are the same")
|
|
198
|
+
else:
|
|
199
|
+
print("ERROR - The time values differ")
|
|
200
|
+
sys.exit(1)
|
|
201
|
+
|
|
202
|
+
if (IM_tim_prv is None) != (IM_tim_now is None):
|
|
203
|
+
print("ERROR - time_bnds present in only one file")
|
|
204
|
+
sys.exit(1)
|
|
205
|
+
|
|
206
|
+
if (IM_tim_prv is not None) and (IM_tim_now is not None):
|
|
207
|
+
if np.array_equal(IM_tim_prv, IM_tim_now):
|
|
208
|
+
print("The time_bnds values are the same")
|
|
209
|
+
else:
|
|
210
|
+
print("ERROR - The time_bnds values differ")
|
|
211
|
+
sys.exit(1)
|
|
212
|
+
else:
|
|
213
|
+
print("WARNING - time_bnds variable missing: skipping comparison")
|
|
214
|
+
|
|
215
|
+
# -------------------------------------------------------------------------
|
|
216
|
+
# Get main variable in netCDF files
|
|
217
|
+
# -------------------------------------------------------------------------
|
|
218
|
+
p = netCDF4.Dataset(prv_ncf, "r")
|
|
219
|
+
n = netCDF4.Dataset(now_ncf, "r")
|
|
220
|
+
|
|
221
|
+
if "Qext" in p.variables and "Qext" in n.variables:
|
|
222
|
+
YS_val_tmp = "Qext"
|
|
223
|
+
elif "Qout" in p.variables and "Qout" in n.variables:
|
|
224
|
+
YS_val_tmp = "Qout"
|
|
225
|
+
else:
|
|
226
|
+
print("ERROR - Neither Qext nor Qout is common variable")
|
|
227
|
+
sys.exit(1)
|
|
228
|
+
print(f"The main variable names are the same: {YS_val_tmp}")
|
|
229
|
+
|
|
230
|
+
# -------------------------------------------------------------------------
|
|
231
|
+
# Compute differences
|
|
232
|
+
# -------------------------------------------------------------------------
|
|
233
|
+
ZS_rdf_max = 0
|
|
234
|
+
ZS_adf_max = 0
|
|
235
|
+
BS_fll_prv = False
|
|
236
|
+
BS_fll_now = False
|
|
237
|
+
|
|
238
|
+
for JS_tim in range(IS_tim):
|
|
239
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
240
|
+
# Initializing
|
|
241
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
242
|
+
ZS_rdf = 0
|
|
243
|
+
ZS_adf = 0
|
|
244
|
+
|
|
245
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
246
|
+
# Getting values
|
|
247
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
248
|
+
ZV_val_prv = p.variables[YS_val_tmp][JS_tim, :]
|
|
249
|
+
ZV_val_now = n.variables[YS_val_tmp][JS_tim, :]
|
|
250
|
+
if "IV_0bi_prv" in locals():
|
|
251
|
+
ZV_val_now = ZV_val_now[IV_0bi_prv]
|
|
252
|
+
|
|
253
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
254
|
+
# Converting masked values to -9999
|
|
255
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
256
|
+
if isinstance(ZV_val_prv, MaskedArray) and np.any(ZV_val_prv.mask):
|
|
257
|
+
ZV_val_prv = ZV_val_prv.filled(fill_value=-9999)
|
|
258
|
+
BS_fll_prv = True
|
|
259
|
+
if isinstance(ZV_val_now, MaskedArray) and np.any(ZV_val_now.mask):
|
|
260
|
+
ZV_val_now = ZV_val_now.filled(fill_value=-9999)
|
|
261
|
+
BS_fll_now = True
|
|
262
|
+
|
|
263
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
264
|
+
# Comparing difference values
|
|
265
|
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
266
|
+
# Tried computations with regular Python lists but they are very slow.
|
|
267
|
+
# Also tried using map(operator.sub,V,W) or [x-y for x,y in zip(V,W)],
|
|
268
|
+
# but this still results in slow computations.
|
|
269
|
+
# The best performance seems to be with Numpy.
|
|
270
|
+
ZV_adf_tmp = np.absolute(ZV_val_prv - ZV_val_now)
|
|
271
|
+
ZS_adf_max = max(np.max(ZV_adf_tmp), ZS_adf_max)
|
|
272
|
+
|
|
273
|
+
ZS_rdf = np.sqrt(
|
|
274
|
+
np.sum(ZV_adf_tmp * ZV_adf_tmp) / np.sum(ZV_val_prv * ZV_val_prv)
|
|
275
|
+
)
|
|
276
|
+
ZS_rdf_max = max(ZS_rdf, ZS_rdf_max)
|
|
277
|
+
|
|
278
|
+
# ------------------------------------------------------------------------
|
|
279
|
+
# Print difference values and compare to tolerances
|
|
280
|
+
# ------------------------------------------------------------------------
|
|
281
|
+
if BS_fll_prv:
|
|
282
|
+
print(f"WARNING - masked values replaced by -9999 in {prv_ncf}")
|
|
283
|
+
if BS_fll_now:
|
|
284
|
+
print(f"WARNING - masked values replaced by -9999 in {now_ncf}")
|
|
285
|
+
if BS_fll_prv or BS_fll_now:
|
|
286
|
+
print("-------------------------------")
|
|
287
|
+
|
|
288
|
+
print("Max relative difference :" + "{0:.2e}".format(ZS_rdf_max))
|
|
289
|
+
print("Max absolute difference :" + "{0:.2e}".format(ZS_adf_max))
|
|
290
|
+
print("-------------------------------")
|
|
291
|
+
|
|
292
|
+
if ZS_rdf_max > ZS_rtl:
|
|
293
|
+
print("Unacceptable rel. difference!!!")
|
|
294
|
+
print("-------------------------------")
|
|
295
|
+
sys.exit(1)
|
|
296
|
+
|
|
297
|
+
if ZS_adf_max > ZS_atl:
|
|
298
|
+
print("Unacceptable abs. difference!!!")
|
|
299
|
+
print("-------------------------------")
|
|
300
|
+
sys.exit(1)
|
|
301
|
+
|
|
302
|
+
print("netCDF files similar!!!")
|
|
303
|
+
print("-------------------------------")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# *****************************************************************************
|
|
307
|
+
# If executed as a script
|
|
308
|
+
# *****************************************************************************
|
|
309
|
+
if __name__ == "__main__":
|
|
310
|
+
main()
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# *****************************************************************************
|
|
314
|
+
# End
|
|
315
|
+
# *****************************************************************************
|
rapid2/cli/_cpllsm.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# *****************************************************************************
|
|
3
|
+
# _cpllsm.py
|
|
4
|
+
# *****************************************************************************
|
|
5
|
+
|
|
6
|
+
# Author:
|
|
7
|
+
# Cedric H. David, 2025-2025
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# *****************************************************************************
|
|
11
|
+
# Import Python modules
|
|
12
|
+
# *****************************************************************************
|
|
13
|
+
import argparse
|
|
14
|
+
import os.path
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
import netCDF4
|
|
18
|
+
import numpy as np
|
|
19
|
+
from tqdm import tqdm
|
|
20
|
+
|
|
21
|
+
from rapid2 import (
|
|
22
|
+
__version__,
|
|
23
|
+
chck_cpl,
|
|
24
|
+
prep_Qex_ncf,
|
|
25
|
+
read_con_vec,
|
|
26
|
+
read_cpl_vec,
|
|
27
|
+
read_crd_vec,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# *****************************************************************************
|
|
32
|
+
# Main
|
|
33
|
+
# *****************************************************************************
|
|
34
|
+
def main() -> None:
|
|
35
|
+
|
|
36
|
+
# -------------------------------------------------------------------------
|
|
37
|
+
# Initialize the argument parser and add valid arguments
|
|
38
|
+
# -------------------------------------------------------------------------
|
|
39
|
+
parser = argparse.ArgumentParser(
|
|
40
|
+
description=(
|
|
41
|
+
"Transform Land Surface Model data into RAPID external inflow "
|
|
42
|
+
"input."
|
|
43
|
+
),
|
|
44
|
+
epilog=(
|
|
45
|
+
"examples:\n"
|
|
46
|
+
" cpllsm "
|
|
47
|
+
"--land_surface_model "
|
|
48
|
+
"input/Tutorial/GLDAS_2.1_VIC_2010-01.nc4 "
|
|
49
|
+
"--connectivity "
|
|
50
|
+
"input/Tutorial/rapid_connect_pfaf_74.csv "
|
|
51
|
+
"--coordinates "
|
|
52
|
+
"input/Tutorial/coords_pfaf_74.csv "
|
|
53
|
+
"--coupling "
|
|
54
|
+
"input/Tutorial/rapid_coupling_pfaf_74_GLDAS.csv "
|
|
55
|
+
"--external_inflow "
|
|
56
|
+
"input/Tutorial/Qext_GLDAS_2.1_VIC_2010-01.nc4"
|
|
57
|
+
),
|
|
58
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"--version", action="version", version=f"rapid2 {__version__}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"-lsm",
|
|
67
|
+
"--land_surface_model",
|
|
68
|
+
dest="lsm",
|
|
69
|
+
metavar="LAND_SURFACE_MODEL",
|
|
70
|
+
type=str,
|
|
71
|
+
required=True,
|
|
72
|
+
help="specify the LSM file",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
"-con",
|
|
77
|
+
"--connectivity",
|
|
78
|
+
dest="con",
|
|
79
|
+
metavar="CONNECTIVITY",
|
|
80
|
+
type=str,
|
|
81
|
+
required=True,
|
|
82
|
+
help="specify the connectivity file",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"-crd",
|
|
87
|
+
"--coordinates",
|
|
88
|
+
dest="crd",
|
|
89
|
+
metavar="COORDINATES",
|
|
90
|
+
type=str,
|
|
91
|
+
required=True,
|
|
92
|
+
help="specify the coordinates",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
parser.add_argument(
|
|
96
|
+
"-cpl",
|
|
97
|
+
"--coupling",
|
|
98
|
+
dest="cpl",
|
|
99
|
+
metavar="COUPLING",
|
|
100
|
+
type=str,
|
|
101
|
+
required=True,
|
|
102
|
+
help="specify the coupling file",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
parser.add_argument(
|
|
106
|
+
"-Qex",
|
|
107
|
+
"--external_inflow",
|
|
108
|
+
dest="Qex",
|
|
109
|
+
metavar="EXTERNAL_INFLOW",
|
|
110
|
+
type=str,
|
|
111
|
+
required=True,
|
|
112
|
+
help="specify the file name",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# -------------------------------------------------------------------------
|
|
116
|
+
# Parse arguments and assign to variables
|
|
117
|
+
# -------------------------------------------------------------------------
|
|
118
|
+
args = parser.parse_args()
|
|
119
|
+
|
|
120
|
+
lsm_ncf = args.lsm
|
|
121
|
+
con_csv = args.con
|
|
122
|
+
crd_csv = args.crd
|
|
123
|
+
cpl_csv = args.cpl
|
|
124
|
+
Qex_ncf = args.Qex
|
|
125
|
+
|
|
126
|
+
print(
|
|
127
|
+
f"Transforming data from {lsm_ncf} "
|
|
128
|
+
f"for {con_csv} "
|
|
129
|
+
f"with {crd_csv} "
|
|
130
|
+
f"and {cpl_csv} "
|
|
131
|
+
f"as {Qex_ncf}"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# -------------------------------------------------------------------------
|
|
135
|
+
# Skip if file already exists
|
|
136
|
+
# -------------------------------------------------------------------------
|
|
137
|
+
if os.path.exists(Qex_ncf):
|
|
138
|
+
print(f"WARNING - File already exists {Qex_ncf}. Exit without error")
|
|
139
|
+
sys.exit(0)
|
|
140
|
+
|
|
141
|
+
# -------------------------------------------------------------------------
|
|
142
|
+
# Check if files exist
|
|
143
|
+
# -------------------------------------------------------------------------
|
|
144
|
+
try:
|
|
145
|
+
with open(lsm_ncf):
|
|
146
|
+
pass
|
|
147
|
+
except IOError:
|
|
148
|
+
print(f"ERROR - Unable to open {lsm_ncf}")
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
# -------------------------------------------------------------------------
|
|
152
|
+
# Read connectivity file
|
|
153
|
+
# -------------------------------------------------------------------------
|
|
154
|
+
print("- Read connectivity file")
|
|
155
|
+
|
|
156
|
+
IV_riv_tot, IV_dwn_tot = read_con_vec(con_csv)
|
|
157
|
+
IS_riv_tot = len(IV_riv_tot)
|
|
158
|
+
print(
|
|
159
|
+
" . The number of river reaches in connectivity file is: "
|
|
160
|
+
f"{IS_riv_tot}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# -------------------------------------------------------------------------
|
|
164
|
+
# Read coordinate file
|
|
165
|
+
# -------------------------------------------------------------------------
|
|
166
|
+
print("- Read coordinate file")
|
|
167
|
+
|
|
168
|
+
IV_riv_tmp, ZV_lon_tot, ZV_lat_tot = read_crd_vec(crd_csv)
|
|
169
|
+
np.testing.assert_array_equal(IV_riv_tot, IV_riv_tmp)
|
|
170
|
+
print(" . The river reaches are the same as in connectivity file")
|
|
171
|
+
|
|
172
|
+
# -------------------------------------------------------------------------
|
|
173
|
+
# Read coupling file
|
|
174
|
+
# -------------------------------------------------------------------------
|
|
175
|
+
print("- Read coupling file")
|
|
176
|
+
|
|
177
|
+
IV_riv_tmp, ZV_skm_tot, IV_1bi_tot, IV_1bj_tot = read_cpl_vec(cpl_csv)
|
|
178
|
+
np.testing.assert_array_equal(IV_riv_tot, IV_riv_tmp)
|
|
179
|
+
print(" . The river reaches are the same as in connectivity file")
|
|
180
|
+
|
|
181
|
+
# -------------------------------------------------------------------------
|
|
182
|
+
# Check consistency of coupling file
|
|
183
|
+
# -------------------------------------------------------------------------
|
|
184
|
+
print("- Check consisitency of coupling file")
|
|
185
|
+
|
|
186
|
+
chck_cpl(ZV_skm_tot, IV_1bi_tot, IV_1bj_tot)
|
|
187
|
+
print(" . OK")
|
|
188
|
+
|
|
189
|
+
# -------------------------------------------------------------------------
|
|
190
|
+
# Read LSM metadata
|
|
191
|
+
# -------------------------------------------------------------------------
|
|
192
|
+
print("- Read LSM metadata")
|
|
193
|
+
|
|
194
|
+
c = netCDF4.Dataset(lsm_ncf, "r")
|
|
195
|
+
|
|
196
|
+
IS_lon_lsm = len(c.dimensions["lon"])
|
|
197
|
+
print(f" . The number of longitudes is: {IS_lon_lsm}")
|
|
198
|
+
|
|
199
|
+
IS_lat_lsm = len(c.dimensions["lat"])
|
|
200
|
+
print(f" . The number of latitudes is: {IS_lat_lsm}")
|
|
201
|
+
|
|
202
|
+
IS_tim_all = len(c.dimensions["time"])
|
|
203
|
+
print(f" . The number of time steps is: {IS_tim_all}")
|
|
204
|
+
|
|
205
|
+
if "Qs_acc" in c.variables:
|
|
206
|
+
if "_FillValue" in c.variables["Qs_acc"].ncattrs():
|
|
207
|
+
ZS_fll = c.variables["Qs_acc"]._FillValue
|
|
208
|
+
print(f" . The fill value for Qs_acc is: {ZS_fll}")
|
|
209
|
+
else:
|
|
210
|
+
raise ValueError("Qs_acc variable missing")
|
|
211
|
+
|
|
212
|
+
if "Qsb_acc" in c.variables:
|
|
213
|
+
if "_FillValue" in c.variables["Qsb_acc"].ncattrs():
|
|
214
|
+
ZS_fll = c.variables["Qsb_acc"]._FillValue
|
|
215
|
+
print(f" . The fill value for Qsb_acc is: {ZS_fll}")
|
|
216
|
+
else:
|
|
217
|
+
raise ValueError("Qsb_acc variable missing")
|
|
218
|
+
|
|
219
|
+
# -------------------------------------------------------------------------
|
|
220
|
+
# Create Qext file
|
|
221
|
+
# -------------------------------------------------------------------------
|
|
222
|
+
print("- Create Qext file")
|
|
223
|
+
|
|
224
|
+
prep_Qex_ncf(IV_riv_tot, ZV_lon_tot, ZV_lat_tot, Qex_ncf)
|
|
225
|
+
|
|
226
|
+
f = netCDF4.Dataset(Qex_ncf, "a")
|
|
227
|
+
|
|
228
|
+
# -------------------------------------------------------------------------
|
|
229
|
+
# Populate dynamic data
|
|
230
|
+
# -------------------------------------------------------------------------
|
|
231
|
+
print("- Populate dynamic data")
|
|
232
|
+
|
|
233
|
+
ZV_scl_tot = 1000 * ZV_skm_tot
|
|
234
|
+
# Scale by 1000: the multiplication of 0.001 m/mm and 1,000,000 m^2/km^2
|
|
235
|
+
# This directly converts an input flux of mm/s (or kg*m^-2*s-1) and an
|
|
236
|
+
# input area of km^2 into an output flow rate of m^3/s.
|
|
237
|
+
|
|
238
|
+
IV_0bi_tot = IV_1bi_tot - 1
|
|
239
|
+
IV_0bj_tot = IV_1bj_tot - 1
|
|
240
|
+
# Shift to 0-based indexing; entries becoming −1 have 0 area (chck_cpl.py).
|
|
241
|
+
|
|
242
|
+
for JS_tim_all in tqdm(range(IS_tim_all), desc="Processing LSM data"):
|
|
243
|
+
ZM_rsf_lsm = c.variables["Qs_acc"][JS_tim_all][:][:]
|
|
244
|
+
ZM_rsb_lsm = c.variables["Qsb_acc"][JS_tim_all][:][:]
|
|
245
|
+
# netCDF data are stored following: c.variables[var][time][lat][lon]
|
|
246
|
+
ZM_run_lsm = ZM_rsf_lsm + ZM_rsb_lsm
|
|
247
|
+
# ZM_run_lsm is of type 'np.ma.core.MaskedArray' or 'np.ndarray'
|
|
248
|
+
# We here assume that runoff data inputs are in kg/m^2/s.
|
|
249
|
+
|
|
250
|
+
ZV_Qex_tot = ZM_run_lsm[IV_0bj_tot, IV_0bi_tot]
|
|
251
|
+
# This uses the multidimensional list-of-locations indexing capability.
|
|
252
|
+
# All values at given i and j indices can be obtained by giving two
|
|
253
|
+
# lists of j and i indices.
|
|
254
|
+
ZV_Qex_tot = ZV_Qex_tot * ZV_scl_tot
|
|
255
|
+
# Result is now a true flow rate (m3/s) because input was a rate.
|
|
256
|
+
|
|
257
|
+
if isinstance(ZV_Qex_tot, np.ma.MaskedArray):
|
|
258
|
+
ZV_Qex_tot = np.where(ZV_Qex_tot.mask, 0, ZV_Qex_tot.data)
|
|
259
|
+
# Make sure the masked values are replaced by 0
|
|
260
|
+
f.variables["Qext"][JS_tim_all, :] = ZV_Qex_tot[:]
|
|
261
|
+
# netCDF data are stored following: f.variables[Qext][time][rivid]
|
|
262
|
+
|
|
263
|
+
f.variables["time"][:] = c.variables["time"][:]
|
|
264
|
+
f.variables["time_bnds"][:] = c.variables["time_bnds"][:]
|
|
265
|
+
# From the LSM netCDF file
|
|
266
|
+
c.close()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# *****************************************************************************
|
|
270
|
+
# If executed as a script
|
|
271
|
+
# *****************************************************************************
|
|
272
|
+
if __name__ == "__main__":
|
|
273
|
+
main()
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# *****************************************************************************
|
|
277
|
+
# End
|
|
278
|
+
# *****************************************************************************
|