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
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-08 13:27 +0200
1import copy
3import numpy as np
4import pandas as pd
5import pytest
6import xarray as xr
7import xugrid as xu
9import imod
12def make_circle_model():
13 grid = imod.data.circle()
14 nface = grid.n_face
16 nlayer = 2
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)
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)
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)
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
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
89 nlayer = 2
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)
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 )
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
167@pytest.fixture(scope="function")
168def circle_model():
169 return make_circle_model()
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
184def make_circle_model_evt():
185 simulation = make_circle_model()
186 gwf_model = simulation["GWF_1"]
188 idomain = gwf_model["disv"].dataset["idomain"]
189 like = idomain.sel(layer=1).astype(np.float64)
190 face_dim = idomain.ugrid.grid.face_dimension
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)
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]
203 proportion_depth = xu.full_like(like, 0.3) * segments
204 proportion_rate = xu.full_like(like, 0.3) * segments_reversed
206 proportion_depth = proportion_depth.transpose("segment", face_dim)
207 proportion_rate = proportion_rate.transpose("segment", face_dim)
209 gwf_model["evt"] = imod.mf6.Evapotranspiration(
210 surface, rate, depth, proportion_rate, proportion_depth
211 )
213 simulation["GWF_1"] = gwf_model
215 return simulation
218@pytest.fixture(scope="session")
219def circle_model_evt():
220 return make_circle_model_evt()
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
235def make_circle_model_save_sto():
236 simulation = make_circle_model()
237 gwf_model = simulation["GWF_1"]
239 gwf_model["sto"].dataset["save_flows"] = True
240 return simulation
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
257@pytest.mark.usefixtures("circle_model_evt")
258@pytest.fixture(scope="function")
259def circle_partitioned():
260 simulation = make_circle_model_evt()
262 idomain = simulation["GWF_1"]["disv"].dataset["idomain"]
263 submodel_labels = copy.deepcopy(idomain.sel({"layer": 1}))
265 submodel_labels.values[:67] = 0
266 submodel_labels.values[67:118] = 1
267 submodel_labels.values[118:] = 2
269 return simulation.split(submodel_labels)
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
281 simulation = make_circle_model_flow_with_transport_data(["salinity"])
282 gwf_model = simulation["GWF_1"]
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"]
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.
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)
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 )
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
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"]
358 simulation = make_circle_model_flow_with_transport_data(species)
359 gwf_model = simulation["GWF_1"]
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"]
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.
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 )
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 )
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 )
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