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

39 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-08 10:26 +0200

1import jinja2 

2 

3from imod.wq.pkgbase import Package 

4 

5 

6class 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)