Coverage for C:\src\imod-python\imod\wq\lpf.py: 100%

1 import jinja2

2

3 from imod.wq.pkgbase import Package

4

5

6 class LayerPropertyFlow(Package):

7 """

8 The Layer-Property Flow (LPF) package is used to specify properties

9 controlling flow between cells.

10

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 """

100

101 _pkg_id = "lpf"

102

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 )

113

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 )

125

126 _keywords = {

127 "save_budget": {False: 0, True: 1},

128 "method_wet": {"wetfactor": 0, "bottom": 1},

129 }

130

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

159

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 = {}

168

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

180

181 return self._template.render(d)

182

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)