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

1 from typing import Optional, Tuple

2

3 import numpy as np

4

5 from imod.logging import init_log_decorator

6 from imod.mf6.boundary_condition import BoundaryCondition

7 from imod.mf6.interfaces.iregridpackage import IRegridPackage

8 from imod.mf6.utilities.regrid import RegridderType

9 from imod.mf6.validation import BOUNDARY_DIMS_SCHEMA, CONC_DIMS_SCHEMA

10 from imod.schemata import (

11 AllInsideNoDataSchema,

12 AllNoDataSchema,

13 AllValueSchema,

14 CoordsSchema,

15 DimsSchema,

16 DTypeSchema,

17 IdentityNoDataSchema,

18 IndexesSchema,

19 OtherCoordsSchema,

20)

21 from imod.util.spatial import unstack_dim_into_variable

22

23 SEGMENT_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

37 class 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