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

42 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-08 14:15 +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.interfaces.iregridpackage import IRegridPackage 

8from imod.mf6.utilities.regrid import RegridderType 

9from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA 

10from imod.schemata import ( 

11 AllInsideNoDataSchema, 

12 AllNoDataSchema, 

13 AllValueSchema, 

14 CoordsSchema, 

15 DimsSchema, 

16 DTypeSchema, 

17 IdentityNoDataSchema, 

18 IndexesSchema, 

19 OtherCoordsSchema, 

20) 

21from imod.util.spatial import unstack_dim_into_variable 

22 

23SEGMENT_BOUNDARY_DIMS_SCHEMA = ( 

24 BOUNDARY_DIMS_SCHEMA 

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

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

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

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

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

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

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

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

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

34) 

35 

36 

37class Evapotranspiration(BoundaryCondition, IRegridPackage): 

38 """ 

39 Evapotranspiration (EVT) Package. 

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

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

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

43 

44 Parameters 

45 ---------- 

46 surface: array of floats (xr.DataArray) 

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

48 specified. 

49 rate: array of floats (xr.DataArray) 

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

51 specified. 

52 depth: array of floats (xr.DataArray) 

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

54 proportion_rate: array of floats (xr.DataArray) 

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

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

57 proportion_depth: array of floats (xr.DataArray) 

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

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

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

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

62 as the concentration for inflow over this boundary. 

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

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

65 how outflow over this boundary is computed. 

66 fixed_cell: array of floats (xr.DataArray) 

67 indicates that evapotranspiration will not be reassigned to a cell 

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

69 inactive. 

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

71 keyword to indicate that the list of evapotranspiration information will 

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

73 Default is False. 

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

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

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

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

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

79 last time step of each stress period. 

80 Default is False. 

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

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

83 specified with "BUDGET FILEOUT" in Output Control. 

84 Default is False. 

85 observations: [Not yet supported.] 

86 Default is None. 

87 validate: {True, False} 

88 Flag to indicate whether the package should be validated upon 

89 initialization. This raises a ValidationError if package input is 

90 provided in the wrong manner. Defaults to True. 

91 repeat_stress: Optional[xr.DataArray] of datetimes 

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

93 seasonality without duplicating the values. The DataArray should have 

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

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

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

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

98 ``set_repeat_stress`` method. 

99 """ 

100 

101 _pkg_id = "evt" 

102 _init_schemata = { 

103 "surface": [ 

104 DTypeSchema(np.floating), 

105 IndexesSchema(), 

106 CoordsSchema(("layer",)), 

107 BOUNDARY_DIMS_SCHEMA, 

108 ], 

109 "rate": [ 

110 DTypeSchema(np.floating), 

111 IndexesSchema(), 

112 CoordsSchema(("layer",)), 

113 BOUNDARY_DIMS_SCHEMA, 

114 ], 

115 "depth": [ 

116 DTypeSchema(np.floating), 

117 IndexesSchema(), 

118 CoordsSchema(("layer",)), 

119 BOUNDARY_DIMS_SCHEMA, 

120 ], 

121 "proportion_rate": [ 

122 DTypeSchema(np.floating), 

123 IndexesSchema(), 

124 CoordsSchema(("layer",)), 

125 SEGMENT_BOUNDARY_DIMS_SCHEMA, 

126 ], 

127 "proportion_depth": [ 

128 DTypeSchema(np.floating), 

129 IndexesSchema(), 

130 CoordsSchema(("layer",)), 

131 SEGMENT_BOUNDARY_DIMS_SCHEMA, 

132 ], 

133 "concentration": [ 

134 DTypeSchema(np.floating), 

135 IndexesSchema(), 

136 CoordsSchema( 

137 ( 

138 "species", 

139 "layer", 

140 ) 

141 ), 

142 CONC_DIMS_SCHEMA, 

143 ], 

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

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

146 } 

147 _write_schemata = { 

148 "surface": [ 

149 OtherCoordsSchema("idomain"), 

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

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

152 ], 

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

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

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

156 "proportion_depth": [ 

157 IdentityNoDataSchema("surface"), 

158 AllValueSchema(">=", 0.0), 

159 AllValueSchema("<=", 1.0), 

160 ], 

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

162 } 

163 

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

165 _keyword_map = {} 

166 _template = BoundaryCondition._initialize_template(_pkg_id) 

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

168 

169 _regrid_method = { 

170 "surface": ( 

171 RegridderType.OVERLAP, 

172 "mean", 

173 ), 

174 "rate": ( 

175 RegridderType.OVERLAP, 

176 "mean", 

177 ), 

178 "depth": ( 

179 RegridderType.OVERLAP, 

180 "mean", 

181 ), 

182 "proportion_rate": ( 

183 RegridderType.OVERLAP, 

184 "mean", 

185 ), 

186 "proportion_depth": ( 

187 RegridderType.OVERLAP, 

188 "mean", 

189 ), 

190 } 

191 

192 @init_log_decorator() 

193 def __init__( 

194 self, 

195 surface, 

196 rate, 

197 depth, 

198 proportion_rate, 

199 proportion_depth, 

200 concentration=None, 

201 concentration_boundary_type="auxmixed", 

202 fixed_cell=False, 

203 print_input=False, 

204 print_flows=False, 

205 save_flows=False, 

206 observations=None, 

207 validate: bool = True, 

208 repeat_stress=None, 

209 ): 

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

211 raise ValueError( 

212 "Segment must be provided for both proportion_rate and" 

213 " proportion_depth, or for none at all." 

214 ) 

215 dict_dataset = { 

216 "surface": surface, 

217 "rate": rate, 

218 "depth": depth, 

219 "proportion_rate": proportion_rate, 

220 "proportion_depth": proportion_depth, 

221 "concentration": concentration, 

222 "concentration_boundary_type": concentration_boundary_type, 

223 "fixed_cell": fixed_cell, 

224 "print_input": print_input, 

225 "print_flows": print_flows, 

226 "save_flows": save_flows, 

227 "observations": observations, 

228 "repeat_stress": repeat_stress, 

229 } 

230 super().__init__(dict_dataset) 

231 self._validate_init_schemata(validate) 

232 

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

234 # Insert additional kwargs 

235 kwargs["surface"] = self["surface"] 

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

237 

238 return errors 

239 

240 def _get_options( 

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

242 ): 

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

244 # Add amount of segments 

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

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

247 else: 

248 options["nseg"] = 1 

249 return options 

250 

251 def _get_bin_ds(self): 

252 bin_ds = super()._get_bin_ds() 

253 

254 # Unstack "segment" dimension into different variables 

255 bin_ds = unstack_dim_into_variable(bin_ds, "segment") 

256 

257 return bin_ds 

258 

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

260 return self._regrid_method