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
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 14:15 +0200
1from typing import Optional, Tuple
3import numpy as np
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
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)
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
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 """
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 }
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"}
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 }
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)
233 def _validate(self, schemata, **kwargs):
234 # Insert additional kwargs
235 kwargs["surface"] = self["surface"]
236 errors = super()._validate(schemata, **kwargs)
238 return errors
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
251 def _get_bin_ds(self):
252 bin_ds = super()._get_bin_ds()
254 # Unstack "segment" dimension into different variables
255 bin_ds = unstack_dim_into_variable(bin_ds, "segment")
257 return bin_ds
259 def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
260 return self._regrid_method