Coverage for test_assign_wells.py: 100%

1 import numpy as np

2 import pandas as pd

3 import pytest

4 import xarray as xr

5 from pytest_cases import parametrize_with_cases

6

7 import imod.prepare.wells as prepwel

8 from imod.testing import assert_frame_equal

9

10

11 def test_vectorized_overlap():

12 bounds_a = np.array(

13 [

14 [0.0, 3.0],

15 [0.0, 3.0],

16 ]

17 )

18 bounds_b = np.array(

19 [

20 [1.0, 2.0],

21 [1.0, 2.0],

22 ]

23 )

24 actual = prepwel.vectorized_overlap(bounds_a, bounds_b)

25 assert np.array_equal(actual, np.array([1.0, 1.0]))

26

27

28 def test_compute_overlap():

29 # Three wells

30 wells = pd.DataFrame(

31 {

32 "top": [5.0, 4.0, 3.0],

33 "bottom": [4.0, 2.0, -1.0],

34 }

35 )

36 top = xr.DataArray(

37 data=[

38 [10.0, 10.0, 10.0],

39 [0.0, 0.0, 0.0],

40 ],

41 dims=["layer", "index"],

42 )

43 bottom = xr.DataArray(

44 data=[

45 [0.0, 0.0, 0.0],

46 [-10.0, -10.0, -10.0],

47 ],

48 dims=["layer", "index"],

49 )

50 actual = prepwel.compute_overlap(wells, top, bottom)

51 expected = np.array([1.0, 2.0, 3.0, 0.0, 0.0, 1.0])

52 assert np.allclose(actual, expected)

53

54

55 class AssignWellCases:

56 def case_mix_wells(self):

57 # This is a testcase where NOT all the wells are in the domain, but most

58 # are. It can be used to verify that validation erros will not occurr if

59 # validation is off

60 ones = xr.DataArray(

61 data=np.ones((2, 3, 3)),

62 coords={"layer": [1, 2], "y": [2.5, 1.5, 0.5], "x": [0.5, 1.5, 2.5]},

63 dims=["layer", "y", "x"],

64 )

65 top = ones.copy()

66 top[0] = 10.0

67 top[1] = 0.0

68 bottom = ones.copy()

69 bottom[0] = 0.0

70 bottom[1] = -10.0

71 k = ones.copy()

72 k[0] = 10.0

73 k[1] = 20.0

74

75 wells = pd.DataFrame(

76 {

77 "x": [0.6, 1.1, 2.3, 5.0],

78 "y": [0.6, 1.1, 2.3, 5.0],

79 "id": [1, 2, 3, 4],

80 "top": [5.0, 4.0, 3.0, 0.0],

81 "bottom": [4.0, 2.0, -1.0, 0.0],

82 "rate": [1.0, 10.0, 100.0, 0.0],

83 }

84 )

85 return wells, top, bottom, k

86

87 def case_all_in_domain(self):

88 # This is a testcase where all where all the wells are in the domain and

89 # have valid tops and bottoms

90 ones = xr.DataArray(

91 data=np.ones((2, 3, 3)),

92 coords={"layer": [1, 2], "y": [2.5, 1.5, 0.5], "x": [0.5, 1.5, 2.5]},

93 dims=["layer", "y", "x"],

94 )

95 top = ones.copy()

96 top[0] = 10.0

97 top[1] = 0.0

98 bottom = ones.copy()

99 bottom[0] = 0.0

100 bottom[1] = -10.0

101 k = ones.copy()

102 k[0] = 10.0

103 k[1] = 20.0

104

105 wells = pd.DataFrame(

106 {

107 "x": [0.6, 1.1, 2.3, 2.6],

108 "y": [0.6, 1.1, 2.3, 2.6],

109 "id": [1, 2, 3, 4],

110 "top": [5.0, 4.0, 3.0, 0.0],

111 "bottom": [4.0, 2.0, -1.0, 0.0],

112 "rate": [1.0, 10.0, 100.0, 0.0],

113 }

114 )

115

116 return wells, top, bottom, k

117

118

119 class TestAssignWell:

120 @parametrize_with_cases(

121 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

122 )

123 def test_locate_wells__no_kh(self, wells, top, bottom, k):

124 id_in_bounds, xy_top, xy_bottom, xy_kh = prepwel.locate_wells(

125 wells=wells,

126 top=top,

127 bottom=bottom,

128 k=None,

129 )

130

131 assert np.array_equal(id_in_bounds, [1, 2, 3, 4])

132 assert np.allclose(xy_top, [[10.0, 10.0, 10.0, 10], [0.0, 0.0, 0.0, 0.0]])

133 assert np.allclose(

134 xy_bottom, [[0.0, 0.0, 0.0, 0.0], [-10.0, -10.0, -10.0, -10.0]]

135 )

136 assert xy_kh == 1.0

137

138 @parametrize_with_cases(

139 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

140 )

141 def test_locate_wells(self, wells, top, bottom, k):

142 id_in_bounds, xy_top, xy_bottom, xy_kh = prepwel.locate_wells(

143 wells=wells,

144 top=top,

145 bottom=bottom,

146 k=k,

147 )

148

149 assert np.array_equal(id_in_bounds, [1, 2, 3, 4])

150 assert np.allclose(xy_top, [[10.0, 10.0, 10.0, 10], [0.0, 0.0, 0.0, 0.0]])

151 assert np.allclose(

152 xy_bottom, [[0.0, 0.0, 0.0, 0.0], [-10.0, -10.0, -10.0, -10.0]]

153 )

154 assert np.allclose(xy_kh, [[10.0, 10.0, 10.0, 10.0], [20.0, 20.0, 20.0, 20.0]])

155

156 @parametrize_with_cases(

157 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

158 )

159 def test_locate_wells_errors(self, wells, top, bottom, k):

160 with pytest.raises(TypeError, match="top and bottom"):

161 prepwel.locate_wells(wells, top.values, bottom, None)

162 with pytest.raises(ValueError, match="bottom grid does not match"):

163 small_bottom = bottom.sel(y=slice(2.0, 0.0))

164 prepwel.locate_wells(wells, top, small_bottom, None)

165 with pytest.raises(ValueError, match="k grid does not match"):

166 small_kh = k.sel(y=slice(2.0, 0.0))

167 prepwel.locate_wells(wells, top, bottom, small_kh)

168

169 @parametrize_with_cases(

170 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

171 )

172 def test_assign_wells_errors(self, wells, top, bottom, k):

173 with pytest.raises(ValueError, match="Columns are missing"):

174 faulty_wells = pd.DataFrame({"id": [1], "x": [1.0], "y": [1.0]})

175 prepwel.assign_wells(faulty_wells, top, bottom, k)

176 with pytest.raises(TypeError, match="top, bottom, and optionally"):

177 prepwel.assign_wells(wells, top, bottom.values)

178 with pytest.raises(TypeError, match="top, bottom, and optionally"):

179 prepwel.assign_wells(wells, top.values, bottom, k)

180

181 @parametrize_with_cases(

182 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

183 )

184 def test_assign_wells__no_kh(self, wells, top, bottom, k):

185 actual = prepwel.assign_wells(

186 wells=wells,

187 top=top,

188 bottom=bottom,

189 )

190 assert isinstance(actual, pd.DataFrame)

191 expected = pd.DataFrame(

192 {

193 "index": [0, 1, 2, 3],

194 "id": [1, 2, 3, 3],

195 "layer": [1, 1, 1, 2],

196 "bottom": [4.0, 2.0, -1.0, -1.0],

197 "overlap": [1.0, 2.0, 3.0, 1.0],

198 "rate": [1.0, 10.0, 75.0, 25.0],

199 "top": [5.0, 4.0, 3.0, 3.0],

200 "k": [1.0, 1.0, 1.0, 1.0],

201 "transmissivity": [1.0, 2.0, 3.0, 1.0],

202 "x": [0.6, 1.1, 2.3, 2.3],

203 "y": [0.6, 1.1, 2.3, 2.3],

204 }

205 )

206 assert_frame_equal(actual, expected, check_like=True)

207

208 @parametrize_with_cases(

209 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

210 )

211 def test_assign_wells(self, wells, top, bottom, k):

212 actual = prepwel.assign_wells(

213 wells=wells,

214 top=top,

215 bottom=bottom,

216 k=k,

217 )

218 assert isinstance(actual, pd.DataFrame)

219 expected = pd.DataFrame(

220 {

221 "index": [0, 1, 2, 3],

222 "id": [1, 2, 3, 3],

223 "layer": [1, 1, 1, 2],

224 "bottom": [4.0, 2.0, -1.0, -1.0],

225 "overlap": [1.0, 2.0, 3.0, 1.0],

226 "rate": [1.0, 10.0, 60.0, 40.0],

227 "top": [5.0, 4.0, 3.0, 3.0],

228 "k": [10.0, 10.0, 10.0, 20.0],

229 "transmissivity": [10.0, 20.0, 30.0, 20.0],

230 "x": [0.6, 1.1, 2.3, 2.3],

231 "y": [0.6, 1.1, 2.3, 2.3],

232 }

233 )

234 assert_frame_equal(actual, expected, check_like=True)

235

236 @parametrize_with_cases(

237 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

238 )

239 def test_assign_wells_minimum_thickness(self, wells, top, bottom, k):

240 actual = prepwel.assign_wells(

241 wells=wells,

242 top=top,

243 bottom=bottom,

244 k=k,

245 minimum_thickness=1.01,

246 )

247 assert isinstance(actual, pd.DataFrame)

248 expected = pd.DataFrame(

249 {

250 "index": [0, 1],

251 "id": [2, 3],

252 "layer": [1, 1],

253 "bottom": [2.0, -1.0],

254 "overlap": [2.0, 3.0],

255 "rate": [10.0, 100.0],

256 "top": [4.0, 3.0],

257 "k": [10.0, 10.0],

258 "transmissivity": [20.0, 30.0],

259 "x": [1.1, 2.3],

260 "y": [1.1, 2.3],

261 }

262 )

263 assert_frame_equal(actual, expected, check_like=True)

264

265 @parametrize_with_cases(

266 "wells, top, bottom, k", cases=AssignWellCases.case_all_in_domain

267 )

268 def test_assign_wells_transient_rate(self, wells, top, bottom, k):

269 wells_xr = wells.to_xarray()

270 multiplier = xr.DataArray(

271 data=np.arange(1.0, 6.0),

272 coords={"time": pd.date_range("2000-01-01", "2000-01-05")},

273 dims=["time"],

274 )

275 wells_xr["rate"] = multiplier * wells_xr["rate"]

276 transient_wells = wells_xr.to_dataframe().reset_index()

277

278 actual = prepwel.assign_wells(

279 wells=transient_wells,

280 top=top,

281 bottom=bottom,

282 k=k,

283 )

284 assert np.array_equal(actual["id"], np.repeat([1, 2, 3, 3], 5))

285

286 actual = prepwel.assign_wells(

287 wells=transient_wells,

288 top=top,

289 bottom=bottom,

290 k=k,

291 minimum_thickness=1.01,

292 )

293 assert np.array_equal(actual["id"], np.repeat([2, 3], 5))

294

295 @parametrize_with_cases(

296 "wells, top, bottom, k", cases=AssignWellCases.case_mix_wells

297 )

298 def test_assign_wells_out_of_domain(self, wells, top, bottom, k):

299 wells_xr = wells.to_xarray()

300 multiplier = xr.DataArray(

301 data=np.arange(1.0, 6.0),

302 coords={"time": pd.date_range("2000-01-01", "2000-01-05")},

303 dims=["time"],

304 )

305 wells_xr["rate"] = multiplier * wells_xr["rate"]

306 transient_wells = wells_xr.to_dataframe().reset_index()

307

308 actual = prepwel.assign_wells(

309 wells=transient_wells, top=top, bottom=bottom, k=k, validate=False

310 )

311 assert np.array_equal(actual["id"], np.repeat([1, 2, 3, 3], 5))

312

313 actual = prepwel.assign_wells(

314 wells=transient_wells,

315 top=top,

316 bottom=bottom,

317 k=k,

318 minimum_thickness=1.01,

319 validate=False,

320 )

321 assert np.array_equal(actual["id"], np.repeat([2, 3], 5))

322

323 @parametrize_with_cases(

324 "wells, top, bottom, k", cases=AssignWellCases.case_mix_wells

325 )

326 def test_assign_wells_out_of_domain_invalid(self, wells, top, bottom, k):

327 with pytest.raises(ValueError, match="could not be mapped on the grid"):

328 _ = prepwel.assign_wells(

329 wells=wells,

330 top=top,

331 bottom=bottom,

332 k=k,

333 )