Coverage for test_assign_wells.py: 100%

122 statements  

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

1import numpy as np 

2import pandas as pd 

3import pytest 

4import xarray as xr 

5from pytest_cases import parametrize_with_cases 

6 

7import imod.prepare.wells as prepwel 

8from imod.testing import assert_frame_equal 

9 

10 

11def 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 

28def 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 

55class 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 

119class 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 )