Coverage for C:\src\imod-python\imod\wq\lpf.py: 100%
39 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 jinja2
3from imod.wq.pkgbase import Package
6class LayerPropertyFlow(Package):
7 """
8 The Layer-Property Flow (LPF) package is used to specify properties
9 controlling flow between cells.
11 Parameters
12 ----------
13 k_horizontal: float or xr.DataArray of floats
14 is the hydraulic conductivity along rows (HK). HK is multiplied by
15 horizontal anisotropy (see horizontal_anisotropy) to obtain hydraulic
16 conductivity along columns.
17 k_vertical: float or xr.DataArray of floats
18 is the vertical hydraulic conductivity (VKA).
19 horizontal_anisotropy: float or xr.DataArray of floats
20 contains a value for each layer that is the horizontal anisotropy
21 (CHANI). Use as many records as needed to enter a value of CHANI for
22 each layer. The horizontal anisotropy is the ratio of the hydraulic
23 conductivity along columns (the Y direction) to the hydraulic
24 conductivity along rows (the X direction).
25 interblock: int
26 contains a flag for each layer that defines the method of calculating
27 interblock transmissivity (LAYAVG). Use as many records needed to enter
28 a value for each layer.
29 0 = harmonic mean (This is most appropriate for confined and unconfined
30 aquifers with abrupt boundaries in transmissivity at the cell boundaries
31 or for confined aquifers with uniform hydraulic conductivity).
32 1 = logarithmic mean (This is most appropriate for confined aquifers
33 with gradually varying transmissivities).
34 2 = arithmetic mean of saturated thickness and logarithmic-mean
35 hydraulic conductivity. (This is most appropriate for unconfined
36 aquifers with gradually varying transmissivities).
37 layer_type: int
38 contains a flag for each layer that specifies the layer type (LAYTYP).
39 Use as many records needed to enter a value for each layer.
40 0 = confined
41 not 0 = convertible
42 specific_storage: float or xr.DataArray of floats
43 is specific storage (SS). Read only for a transient simulation (at least
44 one transient stress period). Include only if at least one stress period
45 is transient.
46 Specific storage is the amount of water released when the head in an aquifer
47 drops by 1 m, in one meter of the aquifer (or model layer).
48 The unit is: ((m3 / m2) / m head change) / m aquifer = m-1
49 specific_yield: float or xr.DataArray of floats
50 is specific yield (SY). Read only for a transient simulation (at least
51 one transient stress period) and if the layer is convertible (layer_type
52 is not 0). Include only if at least one stress period is transient.
53 The specific yield is the volume of water released from (or added to) the
54 pore matrix for one meter of head change.
55 The unit is: (m3 / m2) / m head change = dimensionless
56 save_budget: int
57 is a flag and a unit number (ILPFCB).
58 If save_budget > 0, it is the unit number to which cell-by-cell flow
59 terms will be written when "SAVE BUDGET" or a non-zero value for
60 save_budget is specified in Output Control. The terms that are saved are
61 storage, constant-head flow, and flow between adjacent cells.
62 If save_budget = 0, cell-by-cell flow terms will not be written.
63 If save_budget < 0, cell-by-cell flow for constant-head cells will be
64 written in the listing file when "SAVE BUDGET" or a non-zero value for
65 ICBCFL is specified in Output Control. Cell-by-cell flow to storage and
66 between adjacent cells will not be written to any file. The flow terms
67 that will be saved are the flows through the right, front, and lower
68 cell face. Positive values represent flows toward higher column, row, or
69 layer numbers.
70 layer_wet: int
71 contains a flag for each layer that indicates if wetting is active. Use
72 as many records as needed to enter a value for each layer.
73 0 = wetting is inactive
74 not 0 = wetting is active
75 interval_wet: int
76 is the iteration interval for attempting to wet cells. Wetting is
77 attempted every interval_wet iteration (IWETIT). If using the PCG solver
78 (Hill, 1990), this applies to outer iterations, not inner iterations. If
79 interval_wet less than or equal to 0, it is changed to 1.
80 method_wet: int
81 is a flag that determines which equation is used to define the initial
82 head at cells that become wet (IHDWET).
83 If method_wet = 0, this equation is used:
84 h = BOT + WETFCT (hn - BOT).
85 (hn is the head in the neighboring cell that is causing the dry cell to
86 convert to an active cell.)
87 If method_wet is not 0, this equation is used:
88 h = BOT + WETFCT(THRESH).
89 WETFCT is a factor that is included in the calculation of the head that
90 is initially established at a cell when it is converted from dry to wet.
91 head_dry: float, optional
92 is the head that is assigned to cells that are converted to dry during a
93 simulation (HDRY). Although this value plays no role in the model calculations,
94 it is useful as an indicator when looking at the resulting heads that
95 are output from the model. HDRY is thus similar to HNOFLO in the Basic
96 Package, which is the value assigned to cells that are no-flow cells at
97 the start of a model simulation.
98 Default value: 1.0e20.
99 """
101 _pkg_id = "lpf"
103 _mapping = (
104 ("laytyp", "layer_type"),
105 ("layavg", "interblock"),
106 ("chani", "horizontal_anisotropy"),
107 ("hk", "k_horizontal"),
108 ("vka", "k_vertical"),
109 ("ss", "specific_storage"),
110 ("sy", "specific_yield"),
111 ("laywet", "layer_wet"),
112 )
114 _template = jinja2.Template(
115 "[lpf]\n"
116 " ilpfcb = {{save_budget}}\n"
117 " hdry = {{head_dry}}\n"
118 " layvka_l? = 0\n"
119 " {%- for name, dictname in mapping -%}\n"
120 " {%- for layer, value in dicts[dictname].items() %}\n"
121 " {{name}}_l{{layer}} = {{value}}\n"
122 " {%- endfor -%}\n"
123 " {%- endfor -%}\n"
124 )
126 _keywords = {
127 "save_budget": {False: 0, True: 1},
128 "method_wet": {"wetfactor": 0, "bottom": 1},
129 }
131 def __init__(
132 self,
133 k_horizontal,
134 k_vertical,
135 horizontal_anisotropy=1.0,
136 interblock=0,
137 layer_type=0,
138 specific_storage=0.0001,
139 specific_yield=0.15,
140 save_budget=False,
141 layer_wet=0,
142 interval_wet=0.001,
143 method_wet="wetfactor",
144 head_dry=1.0e20,
145 ):
146 super().__init__()
147 self["k_horizontal"] = k_horizontal
148 self["k_vertical"] = k_vertical
149 self["horizontal_anisotropy"] = horizontal_anisotropy
150 self["interblock"] = interblock
151 self["layer_type"] = layer_type
152 self["specific_storage"] = specific_storage
153 self["specific_yield"] = specific_yield
154 self["save_budget"] = save_budget
155 self["layer_wet"] = layer_wet
156 self["interval_wet"] = interval_wet
157 self["method_wet"] = method_wet
158 self["head_dry"] = head_dry
160 def _render(self, directory, nlayer, *args, **kwargs):
161 d = {}
162 # Don't include absentee members
163 mapping = tuple(
164 [(k, v) for k, v in self._mapping if v in self.dataset.data_vars]
165 )
166 d["mapping"] = mapping
167 dicts = {}
169 da_vars = [t[1] for t in self._mapping]
170 for varname in self.dataset.data_vars.keys():
171 if varname in da_vars:
172 dicts[varname] = self._compose_values_layer(
173 varname, directory, nlayer=nlayer
174 )
175 else:
176 d[varname] = self.dataset[varname].values
177 if varname == "save_budget" or varname == "method_wet":
178 self._replace_keyword(d, varname)
179 d["dicts"] = dicts
181 return self._template.render(d)
183 def _pkgcheck(self, ibound=None):
184 to_check = [
185 "k_horizontal",
186 "k_vertical",
187 "horizontal_anisotropy",
188 "specific_storage",
189 "specific_yield",
190 ]
191 self._check_positive(to_check)
192 self._check_location_consistent(to_check)