Coverage for fixtures\flow_transport_simulation_fixture.py: 100%

73 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-13 11:15 +0200

1# %% 

2 

3import numpy as np 

4import pandas as pd 

5import pytest 

6import xarray as xr 

7 

8import imod 

9 

10 

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. 

15 

16 Parameters 

17 ---------- 

18 flow_model: GroundwaterFlowModel 

19 species_name: str 

20 dispersivity: float 

21 retardation: float 

22 decay: float 

23 

24 Returns 

25 ------- 

26 transportmodel: GroundwaterTransportModel 

27 """ 

28 

29 rhobulk = 1150.0 

30 porosity = 0.25 

31 

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 ) 

44 

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 

54 

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 ) 

64 

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 

71 

72 

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 

92 

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) 

100 

101 gwf_model = imod.mf6.GroundwaterFlowModel() 

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

103 

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 

109 

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 ) 

116 

117 gwf_model["chd"] = imod.mf6.ConstantHead(constant_head, constant_conc) 

118 

119 # %% 

120 # Add other flow packages. 

121 

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 ) 

156 

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 ) 

167 

168 simulation = imod.mf6.Modflow6Simulation("1d_tpt_benchmark") 

169 simulation["flow"] = gwf_model 

170 

171 # %% 

172 # Add four transport simulations, and setup the solver flow and transport. 

173 

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 ) 

180 

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 ) 

209 

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"] = 100 

214 

215 return simulation 

216 # %%