Coverage for fixtures\mf6_small_models_fixture.py: 100%

1 from copy import deepcopy

2 from typing import Callable, Union

3

4 import numpy as np

5 import pytest

6 import xarray as xr

7 import xugrid as xu

8

9 import imod

10

11

12 def grid_data_structured(

13 dtype: type, value: Union[int, float], cellsize: float

14) -> xr.DataArray:

15 """

16 This function creates a dataarray with scalar values for a grid of configurable cell size.

17 """

18 horizontal_range = 10

19 y = np.arange(horizontal_range, -cellsize, -cellsize)

20 x = np.arange(0, horizontal_range + cellsize, cellsize)

21

22 nlayer = 3

23

24 shape = nlayer, len(x), len(y)

25 dims = ("layer", "y", "x")

26 layer = np.arange(1, nlayer + 1)

27

28 coords = {"layer": layer, "y": y, "x": x, "dx": cellsize, "dy": cellsize}

29

30 structured_grid_data = xr.DataArray(

31 np.ones(shape, dtype=dtype) * value, coords=coords, dims=dims

32 )

33

34 return structured_grid_data

35

36

37 def grid_data_structured_layered(

38 dtype: type, value: Union[int, float], cellsize: float

39) -> xr.DataArray:

40 """

41 This function creates a dataarray with scalar values for a grid of configurable cell size. The values are

42 multiplied with the layer index.

43 """

44 unstructured_grid_data = grid_data_structured(dtype, value, cellsize)

45 nlayer = unstructured_grid_data.coords["layer"].max().values[()]

46 for ilayer in range(1, nlayer + 1):

47 layer_value = ilayer * value

48 unstructured_grid_data.loc[dict(layer=ilayer)] = layer_value

49 return unstructured_grid_data

50

51

52 def grid_data_unstructured(

53 dtype: type, value: Union[int, float], cellsize: float

54) -> xu.UgridDataArray:

55 """

56 This function creates a dataarray with scalar values for a grid of configurable cell size.

57 First a regular grid is constructed and then this is converted to an ugrid dataarray.

58 """

59 return xu.UgridDataArray.from_structured(

60 grid_data_structured(dtype, value, cellsize)

61 )

62

63

64 def grid_data_unstructured_layered(

65 dtype: type, value: Union[int, float], cellsize: float

66) -> xu.UgridDataArray:

67 """

68 This function creates a dataarray with scalar values for a grid of configurable cell size. The values are

69 multiplied with the layer index. First a regular grid is constructed and then this is converted to an ugrid dataarray.

70 """

71 return xu.UgridDataArray.from_structured(

72 grid_data_structured_layered(dtype, value, cellsize)

73 )

74

75

76 def _make_model(

77 grid_data_function: (

78 Callable[[type, Union[int, float], float], xr.DataArray]

79 | Callable[[type, Union[int, float], float], xu.UgridDataArray]

80 ),

81 cellsize: float,

82) -> imod.mf6.GroundwaterFlowModel:

83 gwf_model = imod.mf6.GroundwaterFlowModel()

84

85 grid_data_function(np.float64, 1, cellsize)

86

87 # Create a constant head field. Some cells should not be constant head in order to have a meaningfull simulation

88 constant_head = grid_data_function(np.float64, 1, cellsize)

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

90 constant_head, print_input=True, print_flows=True, save_flows=True

91 )

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

93

94 icelltype = grid_data_function(np.int32, 1, cellsize)

95 k = grid_data_function(np.float64, 1.23, cellsize)

96 k33 = k.copy()

97

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

99 icelltype=icelltype,

100 k=k,

101 k33=k33,

102 save_flows=True,

103 )

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

105 specific_storage=grid_data_function(np.float64, 0.002, cellsize),

106 specific_yield=0.15,

107 transient=False,

108 convertible=0,

109 )

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

111 rch_rate_all = grid_data_function(np.float64, 0.002, cellsize)

112 rch_rate = rch_rate_all.sel(layer=[1])

113

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

115

116 return gwf_model

117

118

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

120 def structured_flow_model() -> imod.mf6.GroundwaterFlowModel:

121 cellsize = 2.0

122

123 gwf_model = _make_model(grid_data_structured, cellsize)

124

125 bottom = grid_data_structured_layered(np.float64, -1.0, cellsize)

126 idomain = grid_data_structured(np.int32, 1, cellsize)

127

128 # Unassign the constant-head package from some cells to have a more meaningful simulation

129 constant_head = gwf_model["chd"].dataset["head"]

130 constant_head.loc[{"x": cellsize, "y": 2 * cellsize, "layer": 1}] = np.nan

131 constant_head.loc[{"x": 2 * cellsize, "y": cellsize, "layer": 1}] = np.nan

132 constant_head.loc[{"x": cellsize, "y": 2 * cellsize, "layer": 1}] = np.nan

133 constant_head.loc[{"x": 2 * cellsize, "y": 2 * cellsize, "layer": 1}] = np.nan

134

135 gwf_model["dis"] = imod.mf6.StructuredDiscretization(

136 top=10.0, bottom=bottom, idomain=idomain

137 )

138 return gwf_model

139

140

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

142 def unstructured_flow_model() -> imod.mf6.GroundwaterFlowModel:

143 cellsize = 2.0

144

145 gwf_model = _make_model(grid_data_unstructured, cellsize)

146

147 # Unassign the constant-head package from some cells to have a more meaningful simulation

148 constant_head = gwf_model["chd"].dataset["head"]

149 constant_head.loc[{"mesh2d_nFaces": 12, "layer": 1}] = np.nan

150 constant_head.loc[{"mesh2d_nFaces": 13, "layer": 1}] = np.nan

151 constant_head.loc[{"mesh2d_nFaces": 12, "layer": 2}] = np.nan

152 constant_head.loc[{"mesh2d_nFaces": 13, "layer": 2}] = np.nan

153

154 bottom = grid_data_unstructured_layered(np.float64, -1.0, cellsize)

155 idomain = grid_data_unstructured(np.int32, 1, cellsize)

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

157 top=10.0, bottom=bottom, idomain=idomain

158 )

159 return gwf_model

160

161

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

163 def solution_settings() -> imod.mf6.Solution:

164 return imod.mf6.Solution(

165 modelnames=["flow"],

166 print_option="summary",

167 csv_output=False,

168 no_ptc=True,

169 outer_dvclose=1.0e-4,

170 outer_maximum=500,

171 under_relaxation=None,

172 inner_dvclose=1.0e-4,

173 inner_rclose=0.001,

174 inner_maximum=100,

175 linear_acceleration="cg",

176 scaling_method=None,

177 reordering_method=None,

178 relaxation_factor=0.97,

179 )

180

181

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

183 def structured_flow_simulation(

184 structured_flow_model: imod.mf6.GroundwaterFlowModel,

185 solution_settings: imod.mf6.Solution,

186) -> imod.mf6.Modflow6Simulation:

187 simulation = imod.mf6.Modflow6Simulation("original_simulation")

188 simulation["flow"] = structured_flow_model

189 simulation["solution"] = solution_settings

190 simulation.create_time_discretization(

191 additional_times=["2000-01-01T00:00", "2020-01-02T00:00", "2020-01-03T00:00"]

192 )

193 return simulation

194

195

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

197 def structured_flow_simulation_2_flow_models(

198 structured_flow_simulation: imod.mf6.Modflow6Simulation,

199) -> imod.mf6.Modflow6Simulation:

200 """Returns transient confined model."""

201 other_flow_model = deepcopy(structured_flow_simulation["flow"])

202 structured_flow_simulation["flow_copy"] = other_flow_model

203 structured_flow_simulation["solution"].add_model_to_solution("flow_copy")

204

205 return structured_flow_simulation

206

207

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

209 def unstructured_flow_simulation(

210 unstructured_flow_model: imod.mf6.GroundwaterFlowModel,

211 solution_settings: imod.mf6.Solution,

212) -> imod.mf6.Modflow6Simulation:

213 simulation = imod.mf6.Modflow6Simulation("original_simulation")

214 simulation["flow"] = unstructured_flow_model

215 simulation["solution"] = solution_settings

216 simulation.create_time_discretization(

217 additional_times=["2000-01-01T00:00", "2020-01-02T00:00", "2020-01-03T00:00"]

218 )

219 return simulation