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
« 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
7import imod.prepare.wells as prepwel
8from imod.testing import assert_frame_equal
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]))
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)
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
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
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
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 )
116 return wells, top, bottom, k
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 )
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
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 )
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]])
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)
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)
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)
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)
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)
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()
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))
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))
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()
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))
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))
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 )