Coverage for C:\src\imod-python\imod\mf6\dis.py: 93%
55 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
1import pathlib
2from typing import Optional, Tuple
4import numpy as np
6import imod
7from imod.logging import init_log_decorator
8from imod.mf6.interfaces.iregridpackage import IRegridPackage
9from imod.mf6.package import Package
10from imod.mf6.utilities.regrid import RegridderType
11from imod.mf6.validation import DisBottomSchema
12from imod.schemata import (
13 ActiveCellsConnectedSchema,
14 AllValueSchema,
15 AnyValueSchema,
16 DimsSchema,
17 DTypeSchema,
18 IdentityNoDataSchema,
19 IndexesSchema,
20)
23class StructuredDiscretization(Package, IRegridPackage):
24 """
25 Discretization information for structered grids is specified using the file.
26 (DIS6) Only one discretization input file (DISU6, DISV6 or DIS6) can be
27 specified for a model.
28 https://water.usgs.gov/water-resources/software/MODFLOW-6/mf6io_6.0.4.pdf#page=35
30 Parameters
31 ----------
32 top: array of floats (xr.DataArray)
33 is the top elevation for each cell in the top model layer.
34 bottom: array of floats (xr.DataArray)
35 is the bottom elevation for each cell.
36 idomain: array of integers (xr.DataArray)
37 Indicates the existence status of a cell. Horizontal discretization
38 information will be derived from the x and y coordinates of the
39 DataArray. If the idomain value for a cell is 0, the cell does not exist
40 in the simulation. Input and output values will be read and written for
41 the cell, but internal to the program, the cell is excluded from the
42 solution. If the idomain value for a cell is 1, the cell exists in the
43 simulation. if the idomain value for a cell is -1, the cell does not
44 exist in the simulation. Furthermore, the first existing cell above will
45 be connected to the first existing cell below. This type of cell is
46 referred to as a "vertical pass through" cell.
47 validate: {True, False}
48 Flag to indicate whether the package should be validated upon
49 initialization. This raises a ValidationError if package input is
50 provided in the wrong manner. Defaults to True.
51 """
53 _pkg_id = "dis"
54 _init_schemata = {
55 "top": [
56 DTypeSchema(np.floating),
57 DimsSchema("y", "x") | DimsSchema(),
58 IndexesSchema(),
59 ],
60 "bottom": [
61 DTypeSchema(np.floating),
62 DimsSchema("layer", "y", "x") | DimsSchema("layer"),
63 IndexesSchema(),
64 ],
65 "idomain": [
66 DTypeSchema(np.integer),
67 DimsSchema("layer", "y", "x"),
68 IndexesSchema(),
69 ],
70 }
71 _write_schemata = {
72 "idomain": (
73 ActiveCellsConnectedSchema(is_notnull=("!=", 0)),
74 AnyValueSchema(">", 0),
75 ),
76 "top": (
77 AllValueSchema(">", "bottom", ignore=("idomain", "==", -1)),
78 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)),
79 # No need to check coords: dataset ensures they align with idomain.
80 ),
81 "bottom": (DisBottomSchema(other="idomain", is_other_notnull=(">", 0)),),
82 }
84 _grid_data = {"top": np.float64, "bottom": np.float64, "idomain": np.int32}
85 _keyword_map = {"bottom": "botm"}
86 _template = Package._initialize_template(_pkg_id)
88 _regrid_method = {
89 "top": (RegridderType.OVERLAP, "mean"),
90 "bottom": (RegridderType.OVERLAP, "mean"),
91 "idomain": (RegridderType.OVERLAP, "mode"),
92 }
94 _skip_mask_arrays = ["bottom"]
96 @init_log_decorator()
97 def __init__(self, top, bottom, idomain, validate: bool = True):
98 dict_dataset = {
99 "idomain": idomain,
100 "top": top,
101 "bottom": bottom,
102 }
103 super().__init__(dict_dataset)
104 self._validate_init_schemata(validate)
106 def _delrc(self, dx):
107 """
108 dx means dx or dy
109 """
110 if isinstance(dx, (int, float)):
111 return f"constant {dx}"
112 elif isinstance(dx, np.ndarray):
113 arrstr = str(dx)[1:-1]
114 return f"internal\n {arrstr}"
115 else:
116 raise ValueError(f"Unhandled type of {dx}")
118 def render(self, directory, pkgname, globaltimes, binary):
119 disdirectory = pathlib.Path(directory) / pkgname
120 d = {}
121 x = self.dataset["idomain"].coords["x"]
122 y = self.dataset["idomain"].coords["y"]
123 dx, xmin, _ = imod.util.spatial.coord_reference(x)
124 dy, ymin, _ = imod.util.spatial.coord_reference(y)
126 d["xorigin"] = xmin
127 d["yorigin"] = ymin
128 d["nlay"] = self.dataset["idomain"].coords["layer"].size
129 d["nrow"] = y.size
130 d["ncol"] = x.size
131 d["delr"] = self._delrc(np.abs(dx))
132 d["delc"] = self._delrc(np.abs(dy))
133 _, d["top"] = self._compose_values(
134 self["top"], disdirectory, "top", binary=binary
135 )
136 d["botm_layered"], d["botm"] = self._compose_values(
137 self["bottom"], disdirectory, "botm", binary=binary
138 )
139 d["idomain_layered"], d["idomain"] = self._compose_values(
140 self["idomain"], disdirectory, "idomain", binary=binary
141 )
143 return self._template.render(d)
145 def _validate(self, schemata, **kwargs):
146 # Insert additional kwargs
147 kwargs["bottom"] = self["bottom"]
148 errors = super()._validate(schemata, **kwargs)
150 return errors
152 def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:
153 return self._regrid_method