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