Coverage for C:\src\imod-python\imod\mf6\evt.py: 95%

42 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-08 13:27 +0200

1from typing import Optional, Tuple 

2 

3import numpy as np 

4 

5from imod.logging import init_log_decorator 

6from imod.mf6.boundary_condition import BoundaryCondition 

7from imod.mf6.utilities.regrid import RegridderType 

8from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA 

9from imod.schemata import ( 

10 AllInsideNoDataSchema, 

11 AllNoDataSchema, 

12 AllValueSchema, 

13 CoordsSchema, 

14 DimsSchema, 

15 DTypeSchema, 

16 IdentityNoDataSchema, 

17 IndexesSchema, 

18 OtherCoordsSchema, 

19) 

20from imod.util.spatial import unstack_dim_into_variable 

21 

22SEGMENT_BOUNDARY_DIMS_SCHEMA = ( 

23 BOUNDARY_DIMS_SCHEMA 

24 | DimsSchema("segment", "time", "layer", "y", "x") 

25 | DimsSchema("segment", "layer", "y", "x") 

26 | DimsSchema("segment", "time", "layer", "{face_dim}") 

27 | DimsSchema("segment", "layer", "{face_dim}") 

28 # Layer dim not necessary, as long as there is a layer coordinate present. 

29 | DimsSchema("segment", "time", "y", "x") 

30 | DimsSchema("segment", "y", "x") 

31 | DimsSchema("segment", "time", "{face_dim}") 

32 | DimsSchema("segment", "{face_dim}") 

33) 

34 

35from imod.mf6.interfaces.iregridpackage import IRegridPackage 

36 

37 

38class Evapotranspiration(BoundaryCondition, IRegridPackage): 

39 """ 

40 Evapotranspiration (EVT) Package. 

41 Any number of EVT Packages can be specified for a single groundwater flow 

42 model. All single-valued variables are free format. 

43 https://water.usgs.gov/water-resources/software/MODFLOW-6/mf6io_6.0.4.pdf#page=86 

44 

45 Parameters 

46 ---------- 

47 surface: array of floats (xr.DataArray) 

48 is the elevation of the ET surface (L). A time-series name may be 

49 specified. 

50 rate: array of floats (xr.DataArray) 

51 is the maximum ET flux rate (LT −1). A time-series name may be 

52 specified. 

53 depth: array of floats (xr.DataArray) 

54 is the ET extinction depth (L). A time-series name may be specified. 

55 proportion_rate: array of floats (xr.DataArray) 

56 is the proportion of the maximum ET flux rate at the bottom of a segment 

57 (dimensionless). A time-series name may be specified. (petm) 

58 proportion_depth: array of floats (xr.DataArray) 

59 is the proportion of the ET extinction depth at the bottom of a segment 

60 (dimensionless). A timeseries name may be specified. (pxdp) 

61 concentration: array of floats (xr.DataArray, optional) 

62 if this flow package is used in simulations also involving transport, then this array is used 

63 as the concentration for inflow over this boundary. 

64 concentration_boundary_type: ({"AUX", "AUXMIXED"}, optional) 

65 if this flow package is used in simulations also involving transport, then this keyword specifies 

66 how outflow over this boundary is computed. 

67 fixed_cell: array of floats (xr.DataArray) 

68 indicates that evapotranspiration will not be reassigned to a cell 

69 underlying the cell specified in the list if the specified cell is 

70 inactive. 

71 print_input: ({True, False}, optional) 

72 keyword to indicate that the list of evapotranspiration information will 

73 be written to the listing file immediately after it is read. 

74 Default is False. 

75 print_flows: ({True, False}, optional) 

76 Indicates that the list of evapotranspiration flow rates will be printed 

77 to the listing file for every stress period time step in which "BUDGET 

78 PRINT" is specified in Output Control. If there is no Output Control 

79 option and PRINT FLOWS is specified, then flow rates are printed for the 

80 last time step of each stress period. 

81 Default is False. 

82 save_flows: ({True, False}, optional) 

83 Indicates that evapotranspiration flow terms will be written to the file 

84 specified with "BUDGET FILEOUT" in Output Control. 

85 Default is False. 

86 observations: [Not yet supported.] 

87 Default is None. 

88 validate: {True, False} 

89 Flag to indicate whether the package should be validated upon 

90 initialization. This raises a ValidationError if package input is 

91 provided in the wrong manner. Defaults to True. 

92 repeat_stress: Optional[xr.DataArray] of datetimes 

93 Used to repeat data for e.g. repeating stress periods such as 

94 seasonality without duplicating the values. The DataArray should have 

95 dimensions ``("repeat", "repeat_items")``. The ``repeat_items`` 

96 dimension should have size 2: the first value is the "key", the second 

97 value is the "value". For the "key" datetime, the data of the "value" 

98 datetime will be used. Can also be set with a dictionary using the 

99 ``set_repeat_stress`` method. 

100 """ 

101 

102 _pkg_id = "evt" 

103 _init_schemata = { 

104 "surface": [ 

105 DTypeSchema(np.floating), 

106 IndexesSchema(), 

107 CoordsSchema(("layer",)), 

108 BOUNDARY_DIMS_SCHEMA, 

109 ], 

110 "rate": [ 

111 DTypeSchema(np.floating), 

112 IndexesSchema(), 

113 CoordsSchema(("layer",)), 

114 BOUNDARY_DIMS_SCHEMA, 

115 ], 

116 "depth": [ 

117 DTypeSchema(np.floating), 

118 IndexesSchema(), 

119 CoordsSchema(("layer",)), 

120 BOUNDARY_DIMS_SCHEMA, 

121 ], 

122 "proportion_rate": [ 

123 DTypeSchema(np.floating), 

124 IndexesSchema(), 

125 CoordsSchema(("layer",)), 

126 SEGMENT_BOUNDARY_DIMS_SCHEMA, 

127 ], 

128 "proportion_depth": [ 

129 DTypeSchema(np.floating), 

130 IndexesSchema(), 

131 CoordsSchema(("layer",)), 

132 SEGMENT_BOUNDARY_DIMS_SCHEMA, 

133 ], 

134 "concentration": [ 

135 DTypeSchema(np.floating), 

136 IndexesSchema(), 

137 CoordsSchema( 

138 ( 

139 "species", 

140 "layer", 

141 ) 

142 ), 

143 CONC_DIMS_SCHEMA, 

144 ], 

145 "print_flows": [DTypeSchema(np.bool_), DimsSchema()], 

146 "save_flows": [DTypeSchema(np.bool_), DimsSchema()], 

147 } 

148 _write_schemata = { 

149 "surface": [ 

150 OtherCoordsSchema("idomain"), 

151 AllNoDataSchema(), # Check for all nan, can occur while clipping 

152 AllInsideNoDataSchema(other="idomain", is_other_notnull=(">", 0)), 

153 ], 

154 "rate": [IdentityNoDataSchema("surface")], 

155 "depth": [IdentityNoDataSchema("surface")], 

156 "proportion_rate": [IdentityNoDataSchema("surface")], 

157 "proportion_depth": [ 

158 IdentityNoDataSchema("surface"), 

159 AllValueSchema(">=", 0.0), 

160 AllValueSchema("<=", 1.0), 

161 ], 

162 "concentration": [IdentityNoDataSchema("surface"), AllValueSchema(">=", 0.0)], 

163 } 

164 

165 _period_data = ("surface", "rate", "depth", "proportion_depth", "proportion_rate") 

166 _keyword_map = {} 

167 _template = BoundaryCondition._initialize_template(_pkg_id) 

168 _auxiliary_data = {"concentration": "species"} 

169 

170 _regrid_method = { 

171 "surface": ( 

172 RegridderType.OVERLAP, 

173 "mean", 

174 ), 

175 "rate": ( 

176 RegridderType.OVERLAP, 

177 "mean", 

178 ), 

179 "depth": ( 

180 RegridderType.OVERLAP, 

181 "mean", 

182 ), 

183 "proportion_rate": ( 

184 RegridderType.OVERLAP, 

185 "mean", 

186 ), 

187 "proportion_depth": ( 

188 RegridderType.OVERLAP, 

189 "mean", 

190 ), 

191 } 

192 

193 @init_log_decorator() 

194 def __init__( 

195 self, 

196 surface, 

197 rate, 

198 depth, 

199 proportion_rate, 

200 proportion_depth, 

201 concentration=None, 

202 concentration_boundary_type="auxmixed", 

203 fixed_cell=False, 

204 print_input=False, 

205 print_flows=False, 

206 save_flows=False, 

207 observations=None, 

208 validate: bool = True, 

209 repeat_stress=None, 

210 ): 

211 if ("segment" in proportion_rate.dims) ^ ("segment" in proportion_depth.dims): 

212 raise ValueError( 

213 "Segment must be provided for both proportion_rate and" 

214 " proportion_depth, or for none at all." 

215 ) 

216 dict_dataset = { 

217 "surface": surface, 

218 "rate": rate, 

219 "depth": depth, 

220 "proportion_rate": proportion_rate, 

221 "proportion_depth": proportion_depth, 

222 "concentration": concentration, 

223 "concentration_boundary_type": concentration_boundary_type, 

224 "fixed_cell": fixed_cell, 

225 "print_input": print_input, 

226 "print_flows": print_flows, 

227 "save_flows": save_flows, 

228 "observations": observations, 

229 "repeat_stress": repeat_stress, 

230 } 

231 super().__init__(dict_dataset) 

232 self._validate_init_schemata(validate) 

233 

234 def _validate(self, schemata, **kwargs): 

235 # Insert additional kwargs 

236 kwargs["surface"] = self["surface"] 

237 errors = super()._validate(schemata, **kwargs) 

238 

239 return errors 

240 

241 def _get_options( 

242 self, predefined_options: dict, not_options: Optional[list] = None 

243 ): 

244 options = super()._get_options(predefined_options, not_options=not_options) 

245 # Add amount of segments 

246 if "segment" in self.dataset.dims: 

247 options["nseg"] = self.dataset.dims["segment"] + 1 

248 else: 

249 options["nseg"] = 1 

250 return options 

251 

252 def _get_bin_ds(self): 

253 bin_ds = super()._get_bin_ds() 

254 

255 # Unstack "segment" dimension into different variables 

256 bin_ds = unstack_dim_into_variable(bin_ds, "segment") 

257 

258 return bin_ds 

259 

260 def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]: 

261 return self._regrid_method