Coverage for C:\src\imod-python\imod\mf6\ist.py: 89%

28 statements  

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

1from typing import Optional 

2 

3import numpy as np 

4import xarray as xr 

5 

6from imod.logging import init_log_decorator 

7from imod.mf6.package import Package 

8from imod.mf6.validation import PKG_DIMS_SCHEMA 

9from imod.schemata import ( 

10 AllValueSchema, 

11 DTypeSchema, 

12 IdentityNoDataSchema, 

13 IndexesSchema, 

14) 

15 

16 

17class ImmobileStorageTransfer(Package): 

18 """ 

19 The Immobile Storage and Transfer (IST) package represents an immobile 

20 fraction of groundwater. Any number of IST Packages can be specified for a 

21 single GWT model. This allows the user to specify triple porosity systems, 

22 or systems with as many immobile domains as necessary. 

23 

24 Parameters 

25 ---------- 

26 initial_immmobile_concentration : array of floats (xr.DataArray) 

27 initial concentration of the immobile domain in mass per length cubed 

28 (cim). 

29 immobile_porosity : array of floats (xr.DataArray) 

30 porosity of the immobile domain specified as the volume of immobile 

31 pore space per total volume (dimensionless) (thetaim). 

32 mobile_immobile_mass_transfer_rate: array of floats (xr.DataArray) 

33 mass transfer rate coefficient between the mobile and immobile domains, 

34 in dimensions of per time (zetaim). 

35 decay: array of floats (xr.DataArray). 

36 is the rate coefficient for first or zero-order decay for the aqueous 

37 phase of the immobile domain. A negative value indicates solute 

38 production. The dimensions of decay for first-order decay is one over 

39 time. The dimensions of decay for zero-order decay is mass per length 

40 cubed per time. Decay will have no effect on simulation results unless 

41 either first- or zero-order decay is specified in the options block. 

42 decay_sorbed: array of floats (xr.DataArray) 

43 is the rate coefficient for first or zero-order decay for the sorbed 

44 phase of the immobile domain. A negative value indicates solute 

45 production. The dimensions of decay_sorbed for first-order decay is one 

46 over time. The dimensions of decay_sorbed for zero-order decay is mass 

47 of solute per mass of aquifer per time. If decay_sorbed is not 

48 specified and both decay and sorption are active, then the program will 

49 terminate with an error. decay_sorbed will have no effect on simulation 

50 results unless the SORPTION keyword and either first- or zero-order 

51 decay are specified in the options block. 

52 bulk_density: array of floats (xr.DataArray) 

53 is the bulk density of the aquifer in mass per length cubed. 

54 bulk_density will have no effect on simulation results unless the 

55 SORPTION keyword is specified in the options block. 

56 distribution_coefficient: array of floats (xr.DataArray) 

57 is the distribution coefficient for the equilibrium-controlled linear 

58 sorption isotherm in dimensions of length cubed per mass. distcoef will 

59 have no effect on simulation results unless the SORPTION keyword is 

60 specified in the options block. 

61 save_flows: ({True, False}, optional) 

62 Indicates that drain flow terms will be written to the file specified 

63 with "BUDGET FILEOUT" in Output Control. Default is False. 

64 budgetbinfile: 

65 name of the binary output file to write budget information. 

66 budgetcsvfile: 

67 name of the comma-separated value (CSV) output file to write budget 

68 summary information. A budget summary record will be written to this 

69 file for each time step of the simulation. 

70 sorption: ({True, False}, optional) 

71 is a text keyword to indicate that sorption will be activated. Use of 

72 this keyword requires that BULK_DENSITY and DISTCOEF are specified in 

73 the GRIDDATA block. The linear sorption isotherm is the only isotherm 

74 presently supported in the IST Package. 

75 first_order_decay: ({True, False}, optional) 

76 is a text keyword to indicate that first-order decay will occur. Use of 

77 this keyword requires that DECAY and DECAY_SORBED (if sorption is 

78 active) are specified in the GRIDDATA block. 

79 zero_order_decay: ({True, False}, optional) 

80 is a text keyword to indicate that zero-order decay will occur. Use of 

81 this keyword requires that DECAY and DECAY_SORBED (if sorption is 

82 active) are specified in the GRIDDATA block. 

83 cimfile: (str) 

84 name of the output file to write immobile concentrations. This file is 

85 a binary file that has the same format and structure as a binary head 

86 and concentration file. The value for the text variable written to the 

87 file is CIM. Immobile domain concentrations will be written to this 

88 file at the same interval as mobile domain concentrations are saved, as 

89 specified in the GWT Model Output Control file. 

90 columns: (int, optional), default is 10 

91 number of columns for writing data. 

92 width: (int, optional), default is 10 

93 width for writing each number. 

94 digits: (int, optional), default is 7 

95 number of digits to use for writing a number. 

96 format: (str, optional) default exponential 

97 One of {"exponential", "fixed", "general", "scientific"}. 

98 validate: {True, False} 

99 Flag to indicate whether the package should be validated upon 

100 initialization. This raises a ValidationError if package input is 

101 provided in the wrong manner. Defaults to True. 

102 """ 

103 

104 _pkg_id = "ist" 

105 _template = Package._initialize_template(_pkg_id) 

106 _grid_data = { 

107 "initial_immobile_concentration": np.float64, 

108 "immobile_porosity": np.float64, 

109 "mobile_immobile_mass_transfer_rate": np.float64, 

110 "decay": np.float64, 

111 "decay_sorbed": np.float64, 

112 "bulk_density": np.float64, 

113 "distribution_coefficient": np.float64, 

114 } 

115 

116 _keyword_map = { 

117 "initial_immobile_concentration": "cim", 

118 "immobile_porosity": "thetaim", 

119 "mobile_immobile_mass_transfer_rate": "zetaim", 

120 "decay": "decay", 

121 "decay_sorbed": "decay_sorbed", 

122 "bulk_density": "bulk_density", 

123 "distribution_coefficient": "distcoef", 

124 } 

125 

126 _init_schemata = { 

127 "initial_immobile_concentration": [ 

128 DTypeSchema(np.floating), 

129 IndexesSchema(), 

130 PKG_DIMS_SCHEMA, 

131 ], 

132 "immobile_porosity": [ 

133 DTypeSchema(np.floating), 

134 IndexesSchema(), 

135 PKG_DIMS_SCHEMA, 

136 ], 

137 "mobile_immobile_mass_transfer_rate": [ 

138 DTypeSchema(np.floating), 

139 IndexesSchema(), 

140 PKG_DIMS_SCHEMA, 

141 ], 

142 "decay": [ 

143 DTypeSchema(np.floating), 

144 IndexesSchema(), 

145 PKG_DIMS_SCHEMA, 

146 ], 

147 "decay_sorbed": [ 

148 DTypeSchema(np.floating), 

149 IndexesSchema(), 

150 PKG_DIMS_SCHEMA, 

151 ], 

152 "bulk_density": [ 

153 DTypeSchema(np.floating), 

154 IndexesSchema(), 

155 PKG_DIMS_SCHEMA, 

156 ], 

157 "distribution_coefficient": [ 

158 DTypeSchema(np.floating), 

159 IndexesSchema(), 

160 PKG_DIMS_SCHEMA, 

161 ], 

162 } 

163 

164 _write_schemata = { 

165 "initial_immobile_concentration": [ 

166 AllValueSchema(">", 0.0), 

167 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)), 

168 ], 

169 "immobile_porosity": [ 

170 AllValueSchema(">=", 0.0), 

171 AllValueSchema("<", 1.0), 

172 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)), 

173 ], 

174 "mobile_immobile_mass_transfer_rate": [ 

175 AllValueSchema(">=", 0.0), 

176 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)), 

177 ], 

178 "decay": [IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0))], 

179 "decay_sorbed": [ 

180 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)) 

181 ], 

182 "bulk_density": [ 

183 AllValueSchema(">", 0.0), 

184 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)), 

185 ], 

186 "distribution_coefficient": [ 

187 IdentityNoDataSchema(other="idomain", is_other_notnull=(">", 0)) 

188 ], 

189 } 

190 

191 @init_log_decorator() 

192 def __init__( 

193 self, 

194 initial_immobile_concentration, 

195 immobile_porosity, 

196 mobile_immobile_mass_transfer_rate, 

197 decay=None, 

198 decay_sorbed=None, 

199 bulk_density: Optional[xr.DataArray] = None, 

200 distribution_coefficient=None, 

201 save_flows: Optional[bool] = None, 

202 budgetbinfile: Optional[str] = None, 

203 budgetcsvfile: Optional[str] = None, 

204 sorption: bool = False, 

205 first_order_decay: bool = False, 

206 zero_order_decay: bool = False, 

207 cimfile: str = "cim.dat", 

208 columns: int = 7, 

209 width: int = 10, 

210 digits: int = 7, 

211 format: str = "EXPONENTIAL", 

212 validate: bool = True, 

213 ): 

214 # is True fails on a np.bool_ True. 

215 if sorption: 

216 if bulk_density is None or distribution_coefficient is None: 

217 raise ValueError( 

218 "if sorption is active, a bulk density and distribution " 

219 "coefficient must be provided.", 

220 ) 

221 if first_order_decay or zero_order_decay: 

222 if decay is None: 

223 raise ValueError( 

224 "if first_order_decay is active or if zero_order_decay is " 

225 "active, decay must be provided.", 

226 ) 

227 if sorption: 

228 if decay_sorbed is None: 

229 raise ValueError( 

230 "if first_order_decay is active or if zero_order_decay " 

231 "is active, and sorption is active, decay_sorbed must be " 

232 "provided.", 

233 ) 

234 

235 dict_dataset = { 

236 "initial_immobile_concentration": initial_immobile_concentration, 

237 "mobile_immobile_mass_transfer_rate": mobile_immobile_mass_transfer_rate, 

238 "immobile_porosity": immobile_porosity, 

239 "decay": decay, 

240 "decay_sorbed": decay_sorbed, 

241 "bulk_density": bulk_density, 

242 "distribution_coefficient": distribution_coefficient, 

243 "save_flows": save_flows, 

244 "budgetfile": budgetbinfile, 

245 "budgetcsvfile": budgetcsvfile, 

246 "sorption": sorption, 

247 "first_order_decay": first_order_decay, 

248 "zero_order_decay": zero_order_decay, 

249 "cimfile": cimfile, 

250 "columns ": columns, 

251 "width": width, 

252 "digits": digits, 

253 "format": format, 

254 } 

255 super().__init__(dict_dataset) 

256 self._validate_init_schemata(validate)