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
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-08 13:27 +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.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
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)
35from imod.mf6.interfaces.iregridpackage import IRegridPackage
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
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 """
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 }
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"}
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 }
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)
234 def _validate(self, schemata, **kwargs):
235 # Insert additional kwargs
236 kwargs["surface"] = self["surface"]
237 errors = super()._validate(schemata, **kwargs)
239 return errors
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
252 def _get_bin_ds(self):
253 bin_ds = super()._get_bin_ds()
255 # Unstack "segment" dimension into different variables
256 bin_ds = unstack_dim_into_variable(bin_ds, "segment")
258 return bin_ds
260 def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
261 return self._regrid_method