resfo-utilities 0.0.3__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of resfo-utilities might be problematic. Click here for more details.
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/PKG-INFO +1 -2
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/pyproject.toml +1 -1
- resfo_utilities-0.1.0/src/resfo_utilities/__init__.py +8 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/src/resfo_utilities/_cornerpoint_grid.py +81 -31
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/src/resfo_utilities.egg-info/PKG-INFO +1 -2
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/src/resfo_utilities.egg-info/requires.txt +0 -1
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/unit/test_cornerpoint_grid.py +215 -59
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/uv.lock +337 -647
- resfo_utilities-0.0.3/src/resfo_utilities/__init__.py +0 -3
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.github/dependabot.yml +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.github/mypy-matcher.json +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.github/workflows/publish_to_pypi.yml +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.github/workflows/testing.yml +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.github/workflows/typing.yml +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.gitignore +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.mypy.ini +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.pre-commit-config.yaml +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/.readthedocs.yaml +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/CONTRIBUTING.md +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/LICENSE.md +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/README.md +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/SECURITY.md +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/docs/requirements.txt +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/docs/source/api_reference.rst +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/docs/source/conf.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/docs/source/glossary.rst +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/docs/source/index.rst +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/setup.cfg +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/src/resfo_utilities.egg-info/SOURCES.txt +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/src/resfo_utilities.egg-info/dependency_links.txt +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/src/resfo_utilities.egg-info/top_level.txt +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/__init__.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/conftest.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/integration/__init__.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/integration/conftest.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/integration/test_grid.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/performance/__init__.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/performance/test_find_cell_performance.py +0 -0
- {resfo_utilities-0.0.3 → resfo_utilities-0.1.0}/tests/unit/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: resfo-utilities
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: A utility library for working with the output of reservoir simulators.
|
|
5
5
|
Author-email: Equinor <fg_sib-scout@equinor.com>
|
|
6
6
|
Maintainer-email: Eivind Jahren <ejah@equinor.com>, Håkon Steinkopf Søhoel <hsoho@equinor.com>
|
|
@@ -19,7 +19,6 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE.md
|
|
20
20
|
Requires-Dist: numpy
|
|
21
21
|
Requires-Dist: resfo
|
|
22
|
-
Requires-Dist: matplotlib
|
|
23
22
|
Requires-Dist: scipy
|
|
24
23
|
Provides-Extra: doc
|
|
25
24
|
Requires-Dist: sphinx; extra == "doc"
|
|
@@ -7,7 +7,6 @@ import numpy as np
|
|
|
7
7
|
import resfo
|
|
8
8
|
import scipy.optimize
|
|
9
9
|
import warnings
|
|
10
|
-
from matplotlib.path import Path
|
|
11
10
|
import heapq
|
|
12
11
|
from functools import cached_property
|
|
13
12
|
|
|
@@ -16,6 +15,10 @@ class InvalidEgridFileError(ValueError):
|
|
|
16
15
|
pass
|
|
17
16
|
|
|
18
17
|
|
|
18
|
+
class InvalidGridError(ValueError):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
19
22
|
@dataclass
|
|
20
23
|
class MapAxes:
|
|
21
24
|
"""The axes of the map coordinate system.
|
|
@@ -108,6 +111,19 @@ class CornerpointGrid:
|
|
|
108
111
|
zcorn: npt.NDArray[np.float32]
|
|
109
112
|
map_axes: MapAxes | None = None
|
|
110
113
|
|
|
114
|
+
def __post_init__(self) -> None:
|
|
115
|
+
if len(self.coord.shape) != 4 or self.coord.shape[2:4] != (2, 3):
|
|
116
|
+
raise InvalidGridError(f"coord had invalid dimensions {self.coord.shape}")
|
|
117
|
+
if len(self.zcorn.shape) != 4 or self.zcorn.shape[-1] != 8:
|
|
118
|
+
raise InvalidGridError(f"zcorn had invalid dimensions {self.zcorn.shape}")
|
|
119
|
+
ni = self.coord.shape[0] - 1
|
|
120
|
+
nj = self.coord.shape[1] - 1
|
|
121
|
+
if self.zcorn.shape[0] != ni or self.zcorn.shape[1] != nj:
|
|
122
|
+
raise InvalidGridError(
|
|
123
|
+
"zcorn and coord dimensions do not match:"
|
|
124
|
+
f" {self.zcorn.shape} vs {self.coord.shape}"
|
|
125
|
+
)
|
|
126
|
+
|
|
111
127
|
@classmethod
|
|
112
128
|
def read_egrid(cls, file_like: str | os.PathLike[str] | IO[Any]) -> Self:
|
|
113
129
|
"""Read the global grid from an .EGRID or .FEGRID file.
|
|
@@ -272,6 +288,10 @@ class CornerpointGrid:
|
|
|
272
288
|
if map_coordinates and self.map_axes is not None:
|
|
273
289
|
points = self.map_axes.transform_map_points(points)
|
|
274
290
|
|
|
291
|
+
dims = self.zcorn.shape[0:3]
|
|
292
|
+
top = self._pillars_z_plane_intersection(self.zcorn.min())
|
|
293
|
+
bot = self._pillars_z_plane_intersection(self.zcorn.max())
|
|
294
|
+
|
|
275
295
|
# This algorithm will for each point p calculate the mesh surface that
|
|
276
296
|
# is the intersection of the pillars with the plane z=p[2]. Then it searches
|
|
277
297
|
# through the quad with a heuristical search that orders each neighbour by
|
|
@@ -285,19 +305,24 @@ class CornerpointGrid:
|
|
|
285
305
|
class Quad:
|
|
286
306
|
"""The quad at index i,j"""
|
|
287
307
|
|
|
288
|
-
mesh: npt.NDArray[np.float32]
|
|
289
308
|
i: int
|
|
290
309
|
j: int
|
|
291
310
|
p: npt.NDArray[np.float32]
|
|
311
|
+
i_neighbourhood: int
|
|
312
|
+
j_neighbourhood: int
|
|
292
313
|
|
|
293
314
|
@cached_property
|
|
294
315
|
def vertices(self) -> npt.NDArray[np.float32]:
|
|
295
316
|
return np.array(
|
|
296
317
|
[
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
318
|
+
top[self.i, self.j],
|
|
319
|
+
top[self.i + 1, self.j],
|
|
320
|
+
top[self.i + 1, self.j + 1],
|
|
321
|
+
top[self.i, self.j + 1],
|
|
322
|
+
bot[self.i, self.j],
|
|
323
|
+
bot[self.i + 1, self.j],
|
|
324
|
+
bot[self.i + 1, self.j + 1],
|
|
325
|
+
bot[self.i, self.j + 1],
|
|
301
326
|
],
|
|
302
327
|
dtype=np.float32,
|
|
303
328
|
)
|
|
@@ -334,53 +359,63 @@ class CornerpointGrid:
|
|
|
334
359
|
return False
|
|
335
360
|
return bool(self.distance_from_bounds < other.distance_from_bounds)
|
|
336
361
|
|
|
337
|
-
if
|
|
362
|
+
if dims[0] <= 0 or dims[1] <= 0:
|
|
338
363
|
return [None] * len(points)
|
|
339
364
|
|
|
340
365
|
for p in points:
|
|
341
366
|
found = False
|
|
342
|
-
mesh = self._pillars_z_plane_intersection(p[2])
|
|
343
367
|
if prev_ij is None:
|
|
344
|
-
queue = [
|
|
368
|
+
queue = [
|
|
369
|
+
Quad(dims[0] // 2, dims[1] // 2, p, dims[0] // 2, dims[1] // 2)
|
|
370
|
+
]
|
|
345
371
|
else:
|
|
346
|
-
queue = [Quad(
|
|
372
|
+
queue = [Quad(*prev_ij, p, 1, 1)]
|
|
347
373
|
visited = set([(queue[0].i, queue[0].j)])
|
|
348
374
|
while queue:
|
|
349
375
|
node = heapq.heappop(queue)
|
|
350
|
-
vertices = node.vertices
|
|
351
376
|
i = node.i
|
|
352
377
|
j = node.j
|
|
353
378
|
|
|
354
379
|
# If the quad contains the point then search through each k index
|
|
355
380
|
# for that quad
|
|
356
|
-
if node.distance_from_bounds <=
|
|
357
|
-
|
|
358
|
-
).contains_points([p[0:2]], radius=tolerance):
|
|
359
|
-
for k in range(self.zcorn.shape[2]):
|
|
381
|
+
if node.distance_from_bounds <= 2 * tolerance:
|
|
382
|
+
for k in range(dims[2]):
|
|
360
383
|
zcorn = self.zcorn[i, j, k]
|
|
361
384
|
z = p[2]
|
|
362
385
|
# Prune by bounding box first then check whether point_in_cell
|
|
363
386
|
if (
|
|
364
|
-
zcorn.min() -
|
|
365
|
-
|
|
387
|
+
zcorn.min() - 2 * tolerance
|
|
388
|
+
<= z
|
|
389
|
+
<= zcorn.max() + 2 * tolerance
|
|
390
|
+
and self.point_in_cell(
|
|
391
|
+
p, i, j, k, tolerance=tolerance, map_coordinates=False
|
|
392
|
+
)
|
|
366
393
|
):
|
|
367
394
|
prev_ij = (i, j)
|
|
368
395
|
result.append((i, j, k))
|
|
369
396
|
found = True
|
|
370
397
|
break
|
|
398
|
+
if found:
|
|
371
399
|
break
|
|
372
400
|
|
|
373
401
|
# Add each neighbour to the queue if not visited
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
for dj in (-1, 0,
|
|
379
|
-
nj = j + dj
|
|
380
|
-
if nj < 0 or nj >= self.zcorn.shape[1]:
|
|
381
|
-
continue
|
|
402
|
+
size_i = node.i_neighbourhood
|
|
403
|
+
for di in (-1 * size_i, 0, size_i):
|
|
404
|
+
ni = np.clip(i + di, 0, dims[0] - 1)
|
|
405
|
+
size_j = node.j_neighbourhood
|
|
406
|
+
for dj in (-1 * size_j, 0, size_j):
|
|
407
|
+
nj = np.clip(j + dj, 0, dims[1] - 1)
|
|
382
408
|
if (ni, nj) not in visited:
|
|
383
|
-
heapq.heappush(
|
|
409
|
+
heapq.heappush(
|
|
410
|
+
queue,
|
|
411
|
+
Quad(
|
|
412
|
+
ni,
|
|
413
|
+
nj,
|
|
414
|
+
p,
|
|
415
|
+
max(size_i // 2, 1),
|
|
416
|
+
max(size_j // 2, 1),
|
|
417
|
+
),
|
|
418
|
+
)
|
|
384
419
|
visited.add((ni, nj))
|
|
385
420
|
if not found:
|
|
386
421
|
result.append(None)
|
|
@@ -408,8 +443,23 @@ class CornerpointGrid:
|
|
|
408
443
|
def twice(a: npt.NDArray[Any]) -> npt.NDArray[Any]:
|
|
409
444
|
return np.concatenate([a, a])
|
|
410
445
|
|
|
411
|
-
|
|
412
|
-
|
|
446
|
+
height_diff = twice(bot_z - top_z)
|
|
447
|
+
|
|
448
|
+
if np.any(height_diff == 0):
|
|
449
|
+
raise InvalidGridError(
|
|
450
|
+
f"Grid contains zero height pillars with different for cell {i, j, k}"
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
t = (self.zcorn[i, j, k] - twice(top_z)) / height_diff
|
|
454
|
+
|
|
455
|
+
result = twice(top) + t[:, np.newaxis] * twice(bot - top)
|
|
456
|
+
|
|
457
|
+
if not np.all(np.isfinite(result)):
|
|
458
|
+
raise InvalidGridError(
|
|
459
|
+
f"The corners of the cell at {i, j, k} is not well defined"
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
return result
|
|
413
463
|
|
|
414
464
|
def point_in_cell(
|
|
415
465
|
self,
|
|
@@ -444,7 +494,7 @@ class CornerpointGrid:
|
|
|
444
494
|
if map_coordinates and self.map_axes is not None:
|
|
445
495
|
points = self.map_axes.transform_map_points(points)
|
|
446
496
|
|
|
447
|
-
vertices = self.cell_corners(i, j, k)
|
|
497
|
+
vertices = self.cell_corners(i, j, k)
|
|
448
498
|
|
|
449
499
|
corner_signs = np.array(
|
|
450
500
|
[
|
|
@@ -480,7 +530,7 @@ class CornerpointGrid:
|
|
|
480
530
|
|
|
481
531
|
solutions = []
|
|
482
532
|
for point in points:
|
|
483
|
-
point = point
|
|
533
|
+
point = point
|
|
484
534
|
initial_guess = 2 * (point - vertices[0]) / (vertices[6] - vertices[0]) - 1
|
|
485
535
|
initial_guess = np.clip(initial_guess, -1, 1)
|
|
486
536
|
np.nan_to_num(initial_guess, copy=False)
|
|
@@ -498,7 +548,7 @@ class CornerpointGrid:
|
|
|
498
548
|
solutions.append(
|
|
499
549
|
bool(
|
|
500
550
|
np.all(np.abs(sol.x) <= 1.0 + tolerance)
|
|
501
|
-
and np.linalg.norm(residual(point)(sol.x)) <=
|
|
551
|
+
and np.linalg.norm(residual(point)(sol.x)) <= tolerance
|
|
502
552
|
)
|
|
503
553
|
)
|
|
504
554
|
return np.array(solutions, dtype=np.bool_)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: resfo-utilities
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: A utility library for working with the output of reservoir simulators.
|
|
5
5
|
Author-email: Equinor <fg_sib-scout@equinor.com>
|
|
6
6
|
Maintainer-email: Eivind Jahren <ejah@equinor.com>, Håkon Steinkopf Søhoel <hsoho@equinor.com>
|
|
@@ -19,7 +19,6 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE.md
|
|
20
20
|
Requires-Dist: numpy
|
|
21
21
|
Requires-Dist: resfo
|
|
22
|
-
Requires-Dist: matplotlib
|
|
23
22
|
Requires-Dist: scipy
|
|
24
23
|
Provides-Extra: doc
|
|
25
24
|
Requires-Dist: sphinx; extra == "doc"
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
from resfo_utilities import
|
|
1
|
+
from resfo_utilities import (
|
|
2
|
+
CornerpointGrid,
|
|
3
|
+
InvalidEgridFileError,
|
|
4
|
+
MapAxes,
|
|
5
|
+
InvalidGridError,
|
|
6
|
+
)
|
|
2
7
|
import resfo
|
|
3
8
|
import pytest
|
|
4
9
|
from io import BytesIO
|
|
5
10
|
import numpy as np
|
|
6
11
|
from numpy.testing import assert_allclose
|
|
7
|
-
from hypothesis import given, assume
|
|
12
|
+
from hypothesis import given, assume, example
|
|
8
13
|
import hypothesis.strategies as st
|
|
14
|
+
from hypothesis.extra.numpy import arrays, from_dtype
|
|
9
15
|
from itertools import product
|
|
10
16
|
|
|
11
17
|
|
|
@@ -80,6 +86,47 @@ def test_that_read_egrid_raises_invalid_egrid_file_when_mapaxes_has_too_many_val
|
|
|
80
86
|
CornerpointGrid.read_egrid(write_to_buffer([("MAPAXES ", [1.0])]))
|
|
81
87
|
|
|
82
88
|
|
|
89
|
+
def test_that_grid_with_invalid_coord_shape_raises():
|
|
90
|
+
with pytest.raises(InvalidGridError, match="coord had invalid dimensions"):
|
|
91
|
+
CornerpointGrid(
|
|
92
|
+
coord=np.array([], dtype=np.float32),
|
|
93
|
+
zcorn=np.array([[[[0, 0, 0, 0, 1, 1, 1, 1]]]], dtype=np.float32),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_that_grid_with_invalid_zcorn_shape_raises():
|
|
98
|
+
with pytest.raises(InvalidGridError, match="zcorn had invalid dimensions"):
|
|
99
|
+
CornerpointGrid(
|
|
100
|
+
coord=np.array(
|
|
101
|
+
[
|
|
102
|
+
[[[0, 0, 0], [0, 0, 1]], [[0, 1, 0], [0, 1, 1]]],
|
|
103
|
+
[[[1, 0, 0], [1, 0, 1]], [[1, 1, 0], [1, 1, 1]]],
|
|
104
|
+
],
|
|
105
|
+
dtype=np.float32,
|
|
106
|
+
),
|
|
107
|
+
zcorn=np.array([], dtype=np.float32),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_that_grid_with_zcorn_and_coord_shape_mismatch_raises():
|
|
112
|
+
with pytest.raises(
|
|
113
|
+
InvalidGridError, match="zcorn and coord dimensions do not match"
|
|
114
|
+
):
|
|
115
|
+
CornerpointGrid(
|
|
116
|
+
coord=np.array(
|
|
117
|
+
[
|
|
118
|
+
[[[0, 0, 0], [0, 0, 1]], [[0, 1, 0], [0, 1, 1]]],
|
|
119
|
+
[[[1, 0, 0], [1, 0, 1]], [[1, 1, 0], [1, 1, 1]]],
|
|
120
|
+
],
|
|
121
|
+
dtype=np.float32,
|
|
122
|
+
),
|
|
123
|
+
zcorn=np.array(
|
|
124
|
+
[[[[0, 0, 0, 0, 1, 1, 1, 1]]], [[[0, 0, 0, 0, 1, 1, 1, 1]]]],
|
|
125
|
+
dtype=np.float32,
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
83
130
|
@pytest.mark.parametrize(
|
|
84
131
|
"contents_after_global_grid",
|
|
85
132
|
[
|
|
@@ -193,61 +240,6 @@ def test_that_read_egrid_fetches_the_geometry_from_the_global_grid_in_the_file(
|
|
|
193
240
|
]
|
|
194
241
|
|
|
195
242
|
|
|
196
|
-
def test_that_pillars_z_plane_intersection_returns_meshgrid():
|
|
197
|
-
coord = np.array(
|
|
198
|
-
[
|
|
199
|
-
[
|
|
200
|
-
[[0.0, 0.0, 0.0], [1.0, 10.0, 100.0]],
|
|
201
|
-
[[10.0, 20.0, 0.0], [20.0, 30.0, 100.0]],
|
|
202
|
-
]
|
|
203
|
-
]
|
|
204
|
-
)
|
|
205
|
-
grid = CornerpointGrid(coord, None, None)
|
|
206
|
-
assert grid._pillars_z_plane_intersection(50.0).tolist() == [
|
|
207
|
-
[[0.5, 5.0], [15.0, 25.0]]
|
|
208
|
-
]
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def test_that_pillars_z_plane_intersection_keeps_same_shape_as_coord_in_i_j_dimensions():
|
|
212
|
-
coord = np.array(
|
|
213
|
-
[
|
|
214
|
-
[
|
|
215
|
-
[[0.0, 1.0, 100.0], [0.0, 1.0, 200.0]],
|
|
216
|
-
[[2.0, 3.0, 101.0], [2.0, 3.0, 201.0]],
|
|
217
|
-
[[4.0, 5.0, 102.0], [4.0, 5.0, 202.0]],
|
|
218
|
-
],
|
|
219
|
-
[
|
|
220
|
-
[[6.0, 7.0, 103.0], [6.0, 7.0, 203.0]],
|
|
221
|
-
[[8.0, 9.0, 104.0], [8.0, 9.0, 204.0]],
|
|
222
|
-
[[10.0, 11.0, 105.0], [10.0, 11.0, 205.0]],
|
|
223
|
-
],
|
|
224
|
-
[
|
|
225
|
-
[[12.0, 13.0, 106.0], [12.0, 13.0, 206.0]],
|
|
226
|
-
[[14.0, 15.0, 107.0], [14.0, 15.0, 207.0]],
|
|
227
|
-
[[16.0, 17.0, 108.0], [16.0, 17.0, 208.0]],
|
|
228
|
-
],
|
|
229
|
-
]
|
|
230
|
-
)
|
|
231
|
-
grid = CornerpointGrid(coord, None, None)
|
|
232
|
-
assert grid._pillars_z_plane_intersection(50.0).tolist() == [
|
|
233
|
-
[
|
|
234
|
-
[0.0, 1.0],
|
|
235
|
-
[2.0, 3.0],
|
|
236
|
-
[4.0, 5.0],
|
|
237
|
-
],
|
|
238
|
-
[
|
|
239
|
-
[6.0, 7.0],
|
|
240
|
-
[8.0, 9.0],
|
|
241
|
-
[10.0, 11.0],
|
|
242
|
-
],
|
|
243
|
-
[
|
|
244
|
-
[12.0, 13.0],
|
|
245
|
-
[14.0, 15.0],
|
|
246
|
-
[16.0, 17.0],
|
|
247
|
-
],
|
|
248
|
-
]
|
|
249
|
-
|
|
250
|
-
|
|
251
243
|
@pytest.fixture
|
|
252
244
|
def unit_cell_grid():
|
|
253
245
|
"""A Corner point grid which just contains the unit cube as a cell"""
|
|
@@ -362,9 +354,12 @@ def regular_grids(draw):
|
|
|
362
354
|
return CornerpointGrid(coord, zcorn)
|
|
363
355
|
|
|
364
356
|
|
|
357
|
+
points = st.tuples(coordinates, coordinates, coordinates)
|
|
358
|
+
|
|
359
|
+
|
|
365
360
|
@given(
|
|
366
361
|
grid=regular_grids(),
|
|
367
|
-
point=
|
|
362
|
+
point=points,
|
|
368
363
|
data=st.data(),
|
|
369
364
|
)
|
|
370
365
|
def test_that_found_cell_contains_point(grid, point, data):
|
|
@@ -386,7 +381,7 @@ def test_that_found_cell_contains_point(grid, point, data):
|
|
|
386
381
|
|
|
387
382
|
@given(
|
|
388
383
|
grid=regular_grids(),
|
|
389
|
-
point=
|
|
384
|
+
point=points,
|
|
390
385
|
data=st.data(),
|
|
391
386
|
)
|
|
392
387
|
def test_that_on_regular_grids_point_in_cell_is_the_same_as_in_bounding_box(
|
|
@@ -560,3 +555,164 @@ def test_point_in_cell_considers_cells_as_trilinear_shapes(bottom_heights, x, y,
|
|
|
560
555
|
assert grid.point_in_cell([x, y, z], 0, 0, 0, tolerance) == (
|
|
561
556
|
z <= bottom_face_depth(x, y) and in_bounding_box((x, y, z))
|
|
562
557
|
)
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
def test_that_zero_height_pillar_is_invalid():
|
|
561
|
+
grid = CornerpointGrid(
|
|
562
|
+
coord=np.array(
|
|
563
|
+
[
|
|
564
|
+
[
|
|
565
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
|
|
566
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
|
|
567
|
+
],
|
|
568
|
+
[
|
|
569
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
|
|
570
|
+
[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
|
|
571
|
+
],
|
|
572
|
+
],
|
|
573
|
+
dtype=np.float32,
|
|
574
|
+
),
|
|
575
|
+
zcorn=np.array(
|
|
576
|
+
[[[[0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]]]], dtype=np.float32
|
|
577
|
+
),
|
|
578
|
+
map_axes=None,
|
|
579
|
+
)
|
|
580
|
+
with pytest.raises(InvalidGridError, match="Grid contains zero height pillars"):
|
|
581
|
+
grid.cell_corners(0, 0, 0)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def test_that_cells_with_infinite_pillars_are_invalid():
|
|
585
|
+
grid = CornerpointGrid(
|
|
586
|
+
coord=np.array(
|
|
587
|
+
[
|
|
588
|
+
[
|
|
589
|
+
[[np.inf, np.inf, np.inf], [np.inf, np.inf, np.inf]],
|
|
590
|
+
[[np.inf, np.inf, np.inf], [np.inf, np.inf, np.inf]],
|
|
591
|
+
],
|
|
592
|
+
[
|
|
593
|
+
[[np.inf, np.inf, np.inf], [np.inf, np.inf, np.inf]],
|
|
594
|
+
[[np.inf, np.inf, np.inf], [np.inf, np.inf, np.inf]],
|
|
595
|
+
],
|
|
596
|
+
],
|
|
597
|
+
dtype=np.float32,
|
|
598
|
+
),
|
|
599
|
+
zcorn=np.array(
|
|
600
|
+
[[[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]]], dtype=np.float32
|
|
601
|
+
),
|
|
602
|
+
map_axes=None,
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
with pytest.raises(InvalidGridError, match="The corners of the cell"):
|
|
606
|
+
grid.cell_corners(0, 0, 0)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
@st.composite
|
|
610
|
+
def single_cell_grids(draw):
|
|
611
|
+
nice_elements = dict(
|
|
612
|
+
allow_nan=False, allow_infinity=False, max_value=2**12, min_value=-(2**12)
|
|
613
|
+
)
|
|
614
|
+
coord = draw(
|
|
615
|
+
arrays(
|
|
616
|
+
np.float32,
|
|
617
|
+
(2, 2, 2, 3),
|
|
618
|
+
elements=nice_elements,
|
|
619
|
+
)
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
for i in range(coord.shape[0]):
|
|
623
|
+
for j in range(coord.shape[1]):
|
|
624
|
+
bot_pillar_z = np.float32(coord[i, j, 0, 2] + 0.1)
|
|
625
|
+
coord[i, j, 1] = draw(
|
|
626
|
+
from_dtype(
|
|
627
|
+
np.dtype(np.float32),
|
|
628
|
+
**{
|
|
629
|
+
**nice_elements,
|
|
630
|
+
**dict(min_value=bot_pillar_z, max_value=2**14),
|
|
631
|
+
},
|
|
632
|
+
)
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
grid = CornerpointGrid(
|
|
636
|
+
coord=coord,
|
|
637
|
+
zcorn=draw(arrays(np.float32, (1, 1, 1, 8), elements=nice_elements)),
|
|
638
|
+
)
|
|
639
|
+
try:
|
|
640
|
+
grid.cell_corners(0, 0, 0)
|
|
641
|
+
except InvalidGridError:
|
|
642
|
+
assume(False)
|
|
643
|
+
return grid
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
@given(single_cell_grids(), points)
|
|
647
|
+
@example(
|
|
648
|
+
grid=CornerpointGrid(
|
|
649
|
+
coord=np.array(
|
|
650
|
+
[
|
|
651
|
+
[
|
|
652
|
+
[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]],
|
|
653
|
+
[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]],
|
|
654
|
+
],
|
|
655
|
+
[
|
|
656
|
+
[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]],
|
|
657
|
+
[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]],
|
|
658
|
+
],
|
|
659
|
+
],
|
|
660
|
+
dtype=np.float32,
|
|
661
|
+
),
|
|
662
|
+
zcorn=np.array(
|
|
663
|
+
[[[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]]], dtype=np.float32
|
|
664
|
+
),
|
|
665
|
+
map_axes=None,
|
|
666
|
+
),
|
|
667
|
+
point=(0.0, 0.0, 0.0),
|
|
668
|
+
).via("discovered failure")
|
|
669
|
+
@example(
|
|
670
|
+
grid=CornerpointGrid(
|
|
671
|
+
coord=np.array(
|
|
672
|
+
[
|
|
673
|
+
[
|
|
674
|
+
[[0.0, 0.0, -1.0], [0.0, 0.0, 1.0]],
|
|
675
|
+
[[0.0, 1.0, 0.0], [2.0, 1.0, 2.0]],
|
|
676
|
+
],
|
|
677
|
+
[
|
|
678
|
+
[[1.0, 0.0, -1.0], [1.0, 0.0, 1.0]],
|
|
679
|
+
[[0.0, 1.0, -1.0], [0.0, 1.0, 1.0]],
|
|
680
|
+
],
|
|
681
|
+
],
|
|
682
|
+
dtype=np.float32,
|
|
683
|
+
),
|
|
684
|
+
zcorn=np.array(
|
|
685
|
+
[[[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]]], dtype=np.float32
|
|
686
|
+
),
|
|
687
|
+
map_axes=None,
|
|
688
|
+
),
|
|
689
|
+
point=(0.0, 0.0, 0.0),
|
|
690
|
+
).via("constructed grid with bulging face")
|
|
691
|
+
@example(
|
|
692
|
+
grid=CornerpointGrid(
|
|
693
|
+
coord=np.array(
|
|
694
|
+
[
|
|
695
|
+
[
|
|
696
|
+
[[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]],
|
|
697
|
+
[[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]],
|
|
698
|
+
],
|
|
699
|
+
[
|
|
700
|
+
[[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]],
|
|
701
|
+
[[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]],
|
|
702
|
+
],
|
|
703
|
+
],
|
|
704
|
+
dtype=np.float32,
|
|
705
|
+
),
|
|
706
|
+
zcorn=np.array(
|
|
707
|
+
[[[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]]], dtype=np.float32
|
|
708
|
+
),
|
|
709
|
+
map_axes=None,
|
|
710
|
+
),
|
|
711
|
+
point=(0.0, 0.0, 9.999999747378752e-06),
|
|
712
|
+
).via("discovered failure")
|
|
713
|
+
def test_that_in_single_cell_grids_found_and_contains_are_the_same(
|
|
714
|
+
grid: CornerpointGrid, point: tuple[float, float, float]
|
|
715
|
+
):
|
|
716
|
+
assert bool(grid.find_cell_containing_point([point])[0]) == grid.point_in_cell(
|
|
717
|
+
point, 0, 0, 0
|
|
718
|
+
)
|