Coverage for fixtures\flow_transport_simulation_fixture.py: 100%
73 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-16 11:25 +0200
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-16 11:25 +0200
1# %%
3import numpy as np
4import pandas as pd
5import pytest
6import xarray as xr
8import imod
11def create_transport_model(flow_model, species_name, dispersivity, retardation, decay):
12 """
13 Function to create a transport model, as we intend to create four similar
14 transport models.
16 Parameters
17 ----------
18 flow_model: GroundwaterFlowModel
19 species_name: str
20 dispersivity: float
21 retardation: float
22 decay: float
24 Returns
25 -------
26 transportmodel: GroundwaterTransportModel
27 """
29 rhobulk = 1150.0
30 porosity = 0.25
32 transport_model = imod.mf6.GroundwaterTransportModel()
33 transport_model["ssm"] = imod.mf6.SourceSinkMixing.from_flow_model(
34 flow_model, species_name, save_flows=True
35 )
36 transport_model["adv"] = imod.mf6.AdvectionUpstream()
37 transport_model["dsp"] = imod.mf6.Dispersion(
38 diffusion_coefficient=0.0,
39 longitudinal_horizontal=dispersivity,
40 transversal_horizontal1=0.0,
41 xt3d_off=False,
42 xt3d_rhs=False,
43 )
45 # Compute the sorption coefficient based on the desired retardation factor
46 # and the bulk density. Because of this, the exact value of bulk density
47 # does not matter for the solution.
48 if retardation != 1.0:
49 sorption = "linear"
50 kd = (retardation - 1.0) * porosity / rhobulk
51 else:
52 sorption = None
53 kd = 1.0
55 transport_model["mst"] = imod.mf6.MobileStorageTransfer(
56 porosity=porosity,
57 decay=decay,
58 decay_sorbed=decay,
59 bulk_density=rhobulk,
60 distcoef=kd,
61 first_order_decay=True,
62 sorption=sorption,
63 )
65 transport_model["ic"] = imod.mf6.InitialConditions(start=0.0)
66 transport_model["oc"] = imod.mf6.OutputControl(
67 save_concentration="all", save_budget="last"
68 )
69 transport_model["dis"] = flow_model["dis"]
70 return transport_model
73# %%
74@pytest.fixture(scope="function")
75def flow_transport_simulation():
76 """
77 This fixture is a variation on the model also present in
78 examples/mf6/example_1d_transport.py. To make that model more useful for
79 testing eg partitioning or regridding, some boundary conditions were added
80 (2 wells, one extractor and one injector which injects with a nonzero
81 concentration) as well as a recharge zone with a nonzero concentration.
82 """
83 nlay = 1
84 nrow = 2
85 ncol = 101
86 dx = 10.0
87 xmin = 0.0
88 xmax = dx * ncol
89 layer = [1]
90 y = [1.5, 0.5]
91 x = np.arange(xmin, xmax, dx) + 0.5 * dx
93 grid_dims = ("layer", "y", "x")
94 grid_coords = {"layer": layer, "y": y, "x": x, "dx": dx, "dy": 1.0}
95 grid_shape = (nlay, nrow, ncol)
96 grid = xr.DataArray(
97 np.ones(grid_shape, dtype=int), coords=grid_coords, dims=grid_dims
98 )
99 bottom = xr.full_like(grid, -1.0, dtype=float)
101 gwf_model = imod.mf6.GroundwaterFlowModel()
102 gwf_model["ic"] = imod.mf6.InitialConditions(0.0)
104 # %%
105 # Create the input for a constant head boundary and its associated concentration.
106 constant_head = xr.full_like(grid, np.nan, dtype=float)
107 constant_head[..., 0] = 60.0
108 constant_head[..., 100] = 0.0
110 constant_conc = xr.full_like(grid, np.nan, dtype=float)
111 constant_conc[..., 0] = 1.0
112 constant_conc[..., 100] = 0.0
113 constant_conc = constant_conc.expand_dims(
114 species=["species_a", "species_b", "species_c", "species_d"]
115 )
117 gwf_model["chd"] = imod.mf6.ConstantHead(constant_head, constant_conc)
119 # %%
120 # Add other flow packages.
122 gwf_model["npf"] = imod.mf6.NodePropertyFlow(
123 icelltype=1,
124 k=xr.full_like(grid, 1.0, dtype=float),
125 )
126 gwf_model["dis"] = imod.mf6.StructuredDiscretization(
127 top=0.0,
128 bottom=bottom,
129 idomain=grid,
130 )
131 gwf_model["oc"] = imod.mf6.OutputControl(save_head="all", save_budget="all")
132 gwf_model["sto"] = imod.mf6.SpecificStorage(
133 specific_storage=1.0e-5,
134 specific_yield=0.15,
135 transient=False,
136 convertible=0,
137 )
138 recharge_conc = xr.full_like(grid, np.nan, dtype=float)
139 recharge_conc[..., 20:60] = 0.001
140 recharge_conc = recharge_conc.expand_dims(
141 species=["species_a", "species_b", "species_c", "species_d"]
142 )
143 recharge_rate = xr.full_like(grid, np.nan, dtype=float)
144 recharge_rate[..., 20:60] = 0.0001
145 gwf_model["rch"] = imod.mf6.Recharge(recharge_rate, recharge_conc, "AUX")
146 # %%
147 # Create the simulation.
148 injection_concentration = xr.DataArray(
149 [[0.2, 0.23], [0.5, 0.2], [0.2, 0.23], [0.5, 0.2]],
150 coords={
151 "species": ["species_a", "species_b", "species_c", "species_d"],
152 "index": [0, 1],
153 },
154 dims=("species", "index"),
155 )
157 gwf_model["well"] = imod.mf6.Well(
158 x=[20.0, 580.0],
159 y=[0.6, 1.2],
160 concentration_boundary_type="Aux",
161 screen_top=[0.0, 0.0],
162 screen_bottom=[-1.0, -1.0],
163 rate=[0.1, -0.2],
164 minimum_k=0.0001,
165 concentration=injection_concentration,
166 )
168 simulation = imod.mf6.Modflow6Simulation("1d_tpt_benchmark")
169 simulation["flow"] = gwf_model
171 # %%
172 # Add four transport simulations, and setup the solver flow and transport.
174 simulation["tpt_a"] = create_transport_model(gwf_model, "species_a", 0.0, 1.0, 0.0)
175 simulation["tpt_b"] = create_transport_model(gwf_model, "species_b", 10.0, 1.0, 0.0)
176 simulation["tpt_c"] = create_transport_model(gwf_model, "species_c", 10.0, 5.0, 0.0)
177 simulation["tpt_d"] = create_transport_model(
178 gwf_model, "species_d", 10.0, 5.0, 0.002
179 )
181 simulation["solver"] = imod.mf6.Solution(
182 modelnames=["flow"],
183 print_option="summary",
184 outer_dvclose=1.0e-4,
185 outer_maximum=500,
186 under_relaxation=None,
187 inner_dvclose=1.0e-4,
188 inner_rclose=0.001,
189 inner_maximum=100,
190 linear_acceleration="bicgstab",
191 scaling_method=None,
192 reordering_method=None,
193 relaxation_factor=0.97,
194 )
195 simulation["transport_solver"] = imod.mf6.Solution(
196 modelnames=["tpt_a", "tpt_b", "tpt_c", "tpt_d"],
197 print_option="summary",
198 outer_dvclose=1.0e-6,
199 outer_maximum=500,
200 under_relaxation=None,
201 inner_dvclose=1.0e-6,
202 inner_rclose=0.0001,
203 inner_maximum=200,
204 linear_acceleration="bicgstab",
205 scaling_method=None,
206 reordering_method=None,
207 relaxation_factor=0.9,
208 )
210 duration = pd.to_timedelta("2000d")
211 start = pd.to_datetime("2000-01-01")
212 simulation.create_time_discretization(additional_times=[start, start + duration])
213 simulation["time_discretization"]["n_timesteps"] = 50
215 return simulation
216 # %%