Coverage for fixtures\mf6_circle_fixture.py: 52%

181 statements  

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

1import copy 

2 

3import numpy as np 

4import pandas as pd 

5import pytest 

6import xarray as xr 

7import xugrid as xu 

8 

9import imod 

10 

11 

12def make_circle_model(): 

13 grid = imod.data.circle() 

14 nface = grid.n_face 

15 

16 nlayer = 2 

17 

18 idomain = xu.UgridDataArray( 

19 xr.DataArray( 

20 np.ones((nlayer, nface), dtype=np.int32), 

21 coords={"layer": [1, 2]}, 

22 dims=["layer", grid.face_dimension], 

23 ), 

24 grid=grid, 

25 ) 

26 icelltype = xu.full_like(idomain, 0) 

27 k = xu.full_like(idomain, 1.0, dtype=np.float64) 

28 k33 = k.copy() 

29 rch_rate = xu.full_like(k.sel(layer=1), 0.001, dtype=float) 

30 

31 bottom = k * xr.DataArray([5.0, 0.0], dims=["layer"]) 

32 chd_location = xu.zeros_like(k.sel(layer=2), dtype=bool).ugrid.binary_dilation( 

33 border_value=True 

34 ) 

35 constant_head = xu.full_like(k.sel(layer=2), 1.0).where(chd_location) 

36 

37 gwf_model = imod.mf6.GroundwaterFlowModel() 

38 gwf_model["disv"] = imod.mf6.VerticesDiscretization( 

39 top=10.0, bottom=bottom, idomain=idomain 

40 ) 

41 gwf_model["chd"] = imod.mf6.ConstantHead( 

42 constant_head, print_input=True, print_flows=True, save_flows=True 

43 ) 

44 gwf_model["ic"] = imod.mf6.InitialConditions(start=0.0) 

45 gwf_model["npf"] = imod.mf6.NodePropertyFlow( 

46 icelltype=icelltype, 

47 k=k, 

48 k33=k33, 

49 save_flows=True, 

50 ) 

51 gwf_model["sto"] = imod.mf6.SpecificStorage( 

52 specific_storage=1.0e-5, 

53 specific_yield=0.15, 

54 transient=False, 

55 convertible=0, 

56 save_flows=False, 

57 ) 

58 gwf_model["oc"] = imod.mf6.OutputControl(save_head="all", save_budget="all") 

59 gwf_model["rch"] = imod.mf6.Recharge(rch_rate) 

60 

61 simulation = imod.mf6.Modflow6Simulation("circle") 

62 simulation["GWF_1"] = gwf_model 

63 simulation["solver"] = imod.mf6.Solution( 

64 modelnames=["GWF_1"], 

65 print_option="summary", 

66 csv_output=False, 

67 no_ptc=True, 

68 outer_dvclose=1.0e-4, 

69 outer_maximum=500, 

70 under_relaxation=None, 

71 inner_dvclose=1.0e-4, 

72 inner_rclose=0.001, 

73 inner_maximum=100, 

74 linear_acceleration="cg", 

75 scaling_method=None, 

76 reordering_method=None, 

77 relaxation_factor=0.97, 

78 ) 

79 simulation.create_time_discretization(additional_times=["2000-01-01", "2000-01-02"]) 

80 return simulation 

81 

82 

83def make_circle_model_flow_with_transport_data(species: list[str]): 

84 grid = imod.data.circle() 

85 max_concentration = 35.0 

86 min_concentration = 0.0 

87 nface = grid.n_face 

88 

89 nlayer = 2 

90 

91 idomain = xu.UgridDataArray( 

92 xr.DataArray( 

93 np.ones((nlayer, nface), dtype=np.int32), 

94 coords={"layer": [1, 2]}, 

95 dims=["layer", grid.face_dimension], 

96 ), 

97 grid=grid, 

98 ) 

99 icelltype = xu.full_like(idomain, 0) 

100 k = xu.full_like(idomain, 1.0, dtype=np.float64) 

101 k33 = k.copy() 

102 rch_rate = xu.full_like(k.sel(layer=1), 0.001, dtype=float) 

103 rch_concentration = xu.full_like(rch_rate, min_concentration) 

104 rch_concentration = rch_concentration.expand_dims(species=species) 

105 bottom = k * xr.DataArray([5.0, 0.0], dims=["layer"]) 

106 chd_location = xu.zeros_like(k.sel(layer=2), dtype=bool).ugrid.binary_dilation( 

107 border_value=True 

108 ) 

109 constant_head = xu.full_like(k.sel(layer=2), 1.0).where(chd_location) 

110 constant_concentration = xu.full_like(constant_head, max_concentration).where( 

111 chd_location 

112 ) 

113 constant_concentration = constant_concentration.expand_dims(species=species) 

114 

115 gwf_model = imod.mf6.GroundwaterFlowModel(save_flows=True) 

116 gwf_model["disv"] = imod.mf6.VerticesDiscretization( 

117 top=10.0, bottom=bottom, idomain=idomain 

118 ) 

119 gwf_model["chd"] = imod.mf6.ConstantHead( 

120 constant_head, 

121 concentration=constant_concentration, 

122 print_input=True, 

123 print_flows=True, 

124 save_flows=True, 

125 ) 

126 gwf_model["ic"] = imod.mf6.InitialConditions(start=0.0) 

127 gwf_model["npf"] = imod.mf6.NodePropertyFlow( 

128 icelltype=icelltype, 

129 k=k, 

130 k33=k33, 

131 save_flows=True, 

132 ) 

133 gwf_model["sto"] = imod.mf6.SpecificStorage( 

134 specific_storage=1.0e-5, 

135 specific_yield=0.15, 

136 transient=False, 

137 convertible=0, 

138 save_flows=False, 

139 ) 

140 gwf_model["oc"] = imod.mf6.OutputControl(save_head="all", save_budget="all") 

141 gwf_model["rch"] = imod.mf6.Recharge( 

142 rch_rate, save_flows=True, concentration=rch_concentration 

143 ) 

144 

145 simulation = imod.mf6.Modflow6Simulation("circle") 

146 simulation["GWF_1"] = gwf_model 

147 simulation["solver"] = imod.mf6.Solution( 

148 modelnames=["GWF_1"], 

149 print_option="summary", 

150 csv_output=False, 

151 no_ptc=True, 

152 outer_dvclose=1.0e-4, 

153 outer_maximum=500, 

154 under_relaxation=None, 

155 inner_dvclose=1.0e-4, 

156 inner_rclose=0.001, 

157 inner_maximum=100, 

158 linear_acceleration="cg", 

159 scaling_method=None, 

160 reordering_method=None, 

161 relaxation_factor=0.97, 

162 ) 

163 simulation.create_time_discretization(additional_times=["2000-01-01", "2000-01-02"]) 

164 return simulation 

165 

166 

167@pytest.fixture(scope="function") 

168def circle_model(): 

169 return make_circle_model() 

170 

171 

172@pytest.mark.usefixtures("circle_model") 

173@pytest.fixture(scope="session") 

174def circle_result(tmpdir_factory): 

175 # Using a tmpdir_factory is the canonical way of sharing a tempory pytest 

176 # directory between different testing modules. 

177 modeldir = tmpdir_factory.mktemp("circle") 

178 simulation = make_circle_model() 

179 simulation.write(modeldir) 

180 simulation.run() 

181 return modeldir 

182 

183 

184def make_circle_model_evt(): 

185 simulation = make_circle_model() 

186 gwf_model = simulation["GWF_1"] 

187 

188 idomain = gwf_model["disv"].dataset["idomain"] 

189 like = idomain.sel(layer=1).astype(np.float64) 

190 face_dim = idomain.ugrid.grid.face_dimension 

191 

192 rate = xu.full_like(like, 0.001) 

193 # Lay surface on chd level 

194 surface = xu.full_like(like, 1.0) 

195 depth = xu.full_like(like, 2.0) 

196 

197 segments = xr.DataArray( 

198 data=[1, 2, 3], coords={"segment": [1, 2, 3]}, dims=("segment",) 

199 ) 

200 segments_reversed = segments.copy() 

201 segments_reversed.values = [3, 2, 1] 

202 

203 proportion_depth = xu.full_like(like, 0.3) * segments 

204 proportion_rate = xu.full_like(like, 0.3) * segments_reversed 

205 

206 proportion_depth = proportion_depth.transpose("segment", face_dim) 

207 proportion_rate = proportion_rate.transpose("segment", face_dim) 

208 

209 gwf_model["evt"] = imod.mf6.Evapotranspiration( 

210 surface, rate, depth, proportion_rate, proportion_depth 

211 ) 

212 

213 simulation["GWF_1"] = gwf_model 

214 

215 return simulation 

216 

217 

218@pytest.fixture(scope="session") 

219def circle_model_evt(): 

220 return make_circle_model_evt() 

221 

222 

223@pytest.mark.usefixtures("circle_model_evt") 

224@pytest.fixture(scope="session") 

225def circle_result_evt(tmpdir_factory): 

226 # Using a tmpdir_factory is the canonical way of sharing a tempory pytest 

227 # directory between different testing modules. 

228 modeldir = tmpdir_factory.mktemp("circle_evt") 

229 simulation = make_circle_model_evt() 

230 simulation.write(modeldir) 

231 simulation.run() 

232 return modeldir 

233 

234 

235def make_circle_model_save_sto(): 

236 simulation = make_circle_model() 

237 gwf_model = simulation["GWF_1"] 

238 

239 gwf_model["sto"].dataset["save_flows"] = True 

240 return simulation 

241 

242 

243@pytest.fixture(scope="session") 

244def circle_result_sto(tmpdir_factory): 

245 """ 

246 Circle result with storage fluxes, which are saved as METH1 instead of METH6 

247 """ 

248 # Using a tmpdir_factory is the canonical way of sharing a tempory pytest 

249 # directory between different testing modules. 

250 modeldir = tmpdir_factory.mktemp("circle_sto") 

251 simulation = make_circle_model_save_sto() 

252 simulation.write(modeldir) 

253 simulation.run() 

254 return modeldir 

255 

256 

257@pytest.mark.usefixtures("circle_model_evt") 

258@pytest.fixture(scope="function") 

259def circle_partitioned(): 

260 simulation = make_circle_model_evt() 

261 

262 idomain = simulation["GWF_1"]["disv"].dataset["idomain"] 

263 submodel_labels = copy.deepcopy(idomain.sel({"layer": 1})) 

264 

265 submodel_labels.values[:67] = 0 

266 submodel_labels.values[67:118] = 1 

267 submodel_labels.values[118:] = 2 

268 

269 return simulation.split(submodel_labels) 

270 

271 

272@pytest.fixture(scope="function") 

273def circle_model_transport(): 

274 al = 0.001 

275 porosity = 0.3 

276 max_concentration = 35.0 

277 min_concentration = 0.0 

278 max_density = 1025.0 

279 min_density = 1000.0 

280 

281 simulation = make_circle_model_flow_with_transport_data(["salinity"]) 

282 gwf_model = simulation["GWF_1"] 

283 

284 slope = (max_density - min_density) / (max_concentration - min_concentration) 

285 gwf_model["buoyancy"] = imod.mf6.Buoyancy( 

286 reference_density=min_density, 

287 modelname=["transport"], 

288 reference_concentration=[min_concentration], 

289 density_concentration_slope=[slope], 

290 species=["salinity"], 

291 ) 

292 transport_model = imod.mf6.GroundwaterTransportModel(save_flows=True) 

293 transport_model["ssm"] = imod.mf6.SourceSinkMixing.from_flow_model( 

294 gwf_model, "salinity", save_flows=True 

295 ) 

296 transport_model["disv"] = gwf_model["disv"] 

297 

298 # %% 

299 # Now we define some transport packages for simulating the physical processes 

300 # of advection, mechanical dispersion, and molecular diffusion dispersion. This 

301 # example is transient, and the volume available for storage is the porosity, 

302 # in this case 0.10. 

303 

304 transport_model["dsp"] = imod.mf6.Dispersion( 

305 diffusion_coefficient=1e-4, 

306 longitudinal_horizontal=al, 

307 transversal_horizontal1=al * 0.1, 

308 transversal_vertical=al * 0.01, 

309 xt3d_off=False, 

310 xt3d_rhs=False, 

311 ) 

312 transport_model["adv"] = imod.mf6.AdvectionUpstream() 

313 transport_model["mst"] = imod.mf6.MobileStorageTransfer(porosity, save_flows=True) 

314 

315 # %% 

316 # Define the maximum concentration as the initial conditions, also output 

317 # options for the transport model, and assign the transport model to the 

318 # simulation as well. 

319 max_concentration = 35.0 

320 min_concentration = 0.0 

321 transport_model["ic"] = imod.mf6.InitialConditions(start=max_concentration) 

322 transport_model["oc"] = imod.mf6.OutputControl( 

323 save_concentration="last", save_budget="last" 

324 ) 

325 

326 simulation["transport"] = transport_model 

327 simulation["transport_solver"] = imod.mf6.Solution( 

328 modelnames=["transport"], 

329 print_option="summary", 

330 csv_output=False, 

331 no_ptc=True, 

332 outer_dvclose=1.0e-4, 

333 outer_maximum=500, 

334 under_relaxation=None, 

335 inner_dvclose=1.0e-4, 

336 inner_rclose=0.001, 

337 inner_maximum=100, 

338 linear_acceleration="bicgstab", 

339 scaling_method=None, 

340 reordering_method=None, 

341 relaxation_factor=0.97, 

342 ) 

343 simtimes = pd.date_range(start="2000-01-01", end="2001-01-01", freq="W") 

344 simulation.create_time_discretization(additional_times=simtimes) 

345 return simulation 

346 

347 

348@pytest.fixture(scope="function") 

349def circle_model_transport_multispecies_variable_density(): 

350 al = 0.001 

351 porosity = 0.3 

352 max_concentration = 35.0 

353 min_concentration = 0.0 

354 max_density = 1025.0 

355 min_density = 1000.0 

356 species = ["salt", "temp"] 

357 

358 simulation = make_circle_model_flow_with_transport_data(species) 

359 gwf_model = simulation["GWF_1"] 

360 

361 for specie in species: 

362 transport_model = imod.mf6.GroundwaterTransportModel(save_flows=True) 

363 transport_model["ssm"] = imod.mf6.SourceSinkMixing.from_flow_model( 

364 gwf_model, specie, save_flows=True 

365 ) 

366 transport_model["disv"] = gwf_model["disv"] 

367 

368 # %% 

369 # Now we define some transport packages for simulating the physical processes 

370 # of advection, mechanical dispersion, and molecular diffusion dispersion. This 

371 # example is transient, and the volume available for storage is the porosity, 

372 # in this case 0.10. 

373 

374 transport_model["dsp"] = imod.mf6.Dispersion( 

375 diffusion_coefficient=1e-4, 

376 longitudinal_horizontal=al, 

377 transversal_horizontal1=al * 0.1, 

378 transversal_vertical=al * 0.01, 

379 xt3d_off=False, 

380 xt3d_rhs=False, 

381 ) 

382 transport_model["adv"] = imod.mf6.AdvectionUpstream() 

383 transport_model["mst"] = imod.mf6.MobileStorageTransfer( 

384 porosity, save_flows=True 

385 ) 

386 

387 # %% Define the maximum concentration as the initial conditions, also 

388 # output options for the transport model, and assign the transport model 

389 # to the simulation as well. 

390 transport_model["ic"] = imod.mf6.InitialConditions(start=max_concentration) 

391 transport_model["oc"] = imod.mf6.OutputControl( 

392 save_concentration="all", save_budget="all" 

393 ) 

394 

395 simulation[f"tpt_{specie}"] = transport_model 

396 slope = (max_density - min_density) / (max_concentration - min_concentration) 

397 modelnames = [f"tpt_{specie}" for specie in species] 

398 gwf_model["buoyancy"] = imod.mf6.Buoyancy( 

399 reference_density=min_density, 

400 modelname=modelnames, 

401 reference_concentration=[min_concentration, min_concentration], 

402 density_concentration_slope=[slope, slope], 

403 species=species, 

404 ) 

405 

406 simulation["transport_solver"] = imod.mf6.Solution( 

407 modelnames=modelnames, 

408 print_option="summary", 

409 csv_output=False, 

410 no_ptc=True, 

411 outer_dvclose=1.0e-4, 

412 outer_maximum=500, 

413 under_relaxation=None, 

414 inner_dvclose=1.0e-4, 

415 inner_rclose=0.001, 

416 inner_maximum=100, 

417 linear_acceleration="bicgstab", 

418 scaling_method=None, 

419 reordering_method=None, 

420 relaxation_factor=0.97, 

421 ) 

422 simtimes = pd.date_range(start="2000-01-01", end="2001-01-01", freq="W") 

423 simulation.create_time_discretization(additional_times=simtimes) 

424 return simulation