resqpy 4.16.11__py3-none-any.whl → 4.17.1__py3-none-any.whl
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.
- resqpy/__init__.py +1 -1
- resqpy/fault/_grid_connection_set.py +224 -62
- resqpy/grid/_grid.py +4 -0
- resqpy/grid_surface/__init__.py +4 -0
- resqpy/grid_surface/_blocked_well_populate.py +5 -5
- resqpy/grid_surface/_find_faces.py +731 -212
- resqpy/model/_hdf5.py +3 -3
- resqpy/olio/triangulation.py +17 -13
- resqpy/olio/vector_utilities.py +175 -1
- resqpy/olio/wellspec_keywords.py +16 -10
- resqpy/property/grid_property_collection.py +10 -10
- resqpy/rq_import/_import_vdb_ensemble.py +12 -13
- resqpy/surface/_mesh.py +4 -0
- resqpy/surface/_surface.py +40 -24
- resqpy/surface/_tri_mesh.py +8 -7
- resqpy/surface/_triangulated_patch.py +71 -51
- resqpy/well/_blocked_well.py +28 -25
- resqpy/well/_trajectory.py +2 -2
- resqpy/well/blocked_well_frame.py +1 -1
- resqpy/well/well_object_funcs.py +5 -5
- {resqpy-4.16.11.dist-info → resqpy-4.17.1.dist-info}/METADATA +1 -1
- {resqpy-4.16.11.dist-info → resqpy-4.17.1.dist-info}/RECORD +24 -24
- {resqpy-4.16.11.dist-info → resqpy-4.17.1.dist-info}/LICENSE +0 -0
- {resqpy-4.16.11.dist-info → resqpy-4.17.1.dist-info}/WHEEL +0 -0
resqpy/model/_hdf5.py
CHANGED
@@ -262,11 +262,11 @@ def _h5_array_element(model,
|
|
262
262
|
if dtype is None:
|
263
263
|
return result
|
264
264
|
if result.size == 1:
|
265
|
-
if dtype is float or (isinstance(dtype, str) and
|
265
|
+
if dtype is float or (isinstance(dtype, str) and ('float' in dtype)):
|
266
266
|
return float(result)
|
267
|
-
elif dtype is int or (isinstance(dtype, str) and
|
267
|
+
elif dtype is int or (isinstance(dtype, str) and ('int' in dtype)):
|
268
268
|
return int(result)
|
269
|
-
elif dtype is bool or (isinstance(dtype, str) and
|
269
|
+
elif dtype is bool or (isinstance(dtype, str) and ('bool' in dtype)):
|
270
270
|
return bool(result)
|
271
271
|
return np.array(result, dtype = dtype)
|
272
272
|
|
resqpy/olio/triangulation.py
CHANGED
@@ -115,7 +115,7 @@ def _dt_simple(po, plot_fn = None, progress_fn = None, container_size_factor = N
|
|
115
115
|
if n_p < 3:
|
116
116
|
return None, None # not enough points
|
117
117
|
elif n_p == 3:
|
118
|
-
return np.array([0, 1, 2], dtype =
|
118
|
+
return np.array([0, 1, 2], dtype = np.int32).reshape((1, 3)), np.array([0, 1, 2], dtype = np.int32)
|
119
119
|
|
120
120
|
if progress_fn is not None:
|
121
121
|
progress_fn(0.0)
|
@@ -133,19 +133,20 @@ def _dt_simple(po, plot_fn = None, progress_fn = None, container_size_factor = N
|
|
133
133
|
p[-1] = (min_xy[0] + 0.5 * csf * dxy[0], max_xy[1] + 0.8 * csf * dxy[1])
|
134
134
|
|
135
135
|
# triangle vertex indices
|
136
|
-
|
136
|
+
t_type = np.int32 if n_p < 2_147_483_648 else np.int64
|
137
|
+
t = np.empty((2 * n_p + 2, 3), dtype = t_type) # empty space for triangle vertex indices
|
137
138
|
t[0] = (n_p, n_p + 1, n_p + 2) # initial set of one containing triangle
|
138
139
|
nt = 1 # number of triangles so far populated
|
139
140
|
|
140
141
|
# edges: list of indices of triangles and edge within triangle; -1 indicates no triangle using edge
|
141
|
-
e = np.full((3 * n_p + 6, 2, 2), fill_value = -1, dtype =
|
142
|
+
e = np.full((3 * n_p + 6, 2, 2), fill_value = -1, dtype = t_type)
|
142
143
|
e[:3, 0, 0] = 0
|
143
144
|
for edge in range(3):
|
144
145
|
e[edge, 0, 1] = edge
|
145
146
|
ne = 3 # number of edges so far in use
|
146
147
|
|
147
148
|
# edge indices (in e) for each triangle, first axis indexed in sync with t
|
148
|
-
te = np.empty((2 * n_p + 2, 3), dtype =
|
149
|
+
te = np.empty((2 * n_p + 2, 3), dtype = t_type) # empty space for triangle edge indices
|
149
150
|
te[0] = (0, 1, 2)
|
150
151
|
|
151
152
|
# mask tracking which edges have been flipped
|
@@ -501,7 +502,7 @@ def voronoi(p, t, b, aoi: rql.Polyline):
|
|
501
502
|
return trimmed_ci
|
502
503
|
|
503
504
|
def __veroni_cells(aoi_count, aoi_intersect_segments, b, c, c_count, ca_count, cah_count, caho_count, cahon_count,
|
504
|
-
hull_count, out_pair_intersect_segments, p, t, tc_outwith_aoi):
|
505
|
+
hull_count, out_pair_intersect_segments, p, t, tc_outwith_aoi, t_type):
|
505
506
|
# list of voronoi cells (each a numpy list of node indices into c extended with aoi points etc)
|
506
507
|
v = []
|
507
508
|
# for each seed point build the voronoi cell
|
@@ -550,7 +551,7 @@ def voronoi(p, t, b, aoi: rql.Polyline):
|
|
550
551
|
ci_for_p, out_pair_intersect_segments)
|
551
552
|
|
552
553
|
# remove circumcircle centres that are outwith area of interest
|
553
|
-
ci_for_p = np.array([ti for ti in ci_for_p if ti >= c_count or ti not in tc_outwith_aoi], dtype =
|
554
|
+
ci_for_p = np.array([ti for ti in ci_for_p if ti >= c_count or ti not in tc_outwith_aoi], dtype = t_type)
|
554
555
|
|
555
556
|
# find azimuths of vectors from seed point to circumcircle centres and aoi boundary points
|
556
557
|
azi = [vec.azimuth(centre - p[p_i, :2]) for centre in c[ci_for_p, :2]]
|
@@ -612,11 +613,12 @@ def voronoi(p, t, b, aoi: rql.Polyline):
|
|
612
613
|
caho_count = cah_count + 2 * o_count
|
613
614
|
cahon_count = caho_count + hull_count
|
614
615
|
assert cahon_count + hull_count == len(c)
|
616
|
+
t_type = np.int32 if len(c) < 2_147_483_648 else np.int64
|
615
617
|
|
616
618
|
# compute intersection points between hull edge normals and aoi polyline
|
617
619
|
# also extended virtual centres for hull edges
|
618
620
|
extension_scaling = 1000.0 * np.sum((np.max(aoi.coordinates, axis = 0) - np.min(aoi.coordinates, axis = 0))[:2])
|
619
|
-
aoi_intersect_segments = np.empty((hull_count,), dtype =
|
621
|
+
aoi_intersect_segments = np.empty((hull_count,), dtype = t_type)
|
620
622
|
for ei in range(hull_count):
|
621
623
|
# use segment midpoint and normal methods of hull to project out
|
622
624
|
m = hull.segment_midpoint(ei)[:2] # midpoint
|
@@ -638,8 +640,8 @@ def voronoi(p, t, b, aoi: rql.Polyline):
|
|
638
640
|
c[cahon_count + ei] = hull.coordinates[ei, :2] + extension_scaling * vector
|
639
641
|
|
640
642
|
# where cicrumcircle centres are outwith aoi, compute intersections of normals of wing edges with aoi
|
641
|
-
out_pair_intersect_segments = np.empty((o_count, 2), dtype =
|
642
|
-
wing_hull_segments = np.empty((o_count, 2), dtype =
|
643
|
+
out_pair_intersect_segments = np.empty((o_count, 2), dtype = t_type)
|
644
|
+
wing_hull_segments = np.empty((o_count, 2), dtype = t_type)
|
643
645
|
for oi, ti in enumerate(tc_outwith_aoi):
|
644
646
|
tpi = __shorter_sides_p_i(p[t[ti]])
|
645
647
|
for wing in range(2):
|
@@ -658,7 +660,7 @@ def voronoi(p, t, b, aoi: rql.Polyline):
|
|
658
660
|
tpi = (tpi + 1) % 3
|
659
661
|
|
660
662
|
v = __veroni_cells(aoi_count, aoi_intersect_segments, b, c, c_count, ca_count, cah_count, caho_count, cahon_count,
|
661
|
-
hull_count, out_pair_intersect_segments, p, t, tc_outwith_aoi)
|
663
|
+
hull_count, out_pair_intersect_segments, p, t, tc_outwith_aoi, t_type)
|
662
664
|
|
663
665
|
return c[:caho_count], v
|
664
666
|
|
@@ -703,7 +705,8 @@ def triangulated_polygons(p, v, centres = None):
|
|
703
705
|
points[len(p):] = centres
|
704
706
|
|
705
707
|
t_count = sum([len(x) for x in v])
|
706
|
-
|
708
|
+
t_type = np.int32 if len(points) < 2_147_483_648 else np.int64
|
709
|
+
triangles = np.empty((t_count, 3), dtype = t_type)
|
707
710
|
t_index = 0
|
708
711
|
|
709
712
|
for cell, poly_vertices in enumerate(v):
|
@@ -711,7 +714,7 @@ def triangulated_polygons(p, v, centres = None):
|
|
711
714
|
centre_i = len(p) + cell
|
712
715
|
if centres is None:
|
713
716
|
polygon = rql.Polyline(model,
|
714
|
-
set_coord = p[np.array(poly_vertices, dtype =
|
717
|
+
set_coord = p[np.array(poly_vertices, dtype = t_type)],
|
715
718
|
is_closed = True,
|
716
719
|
set_crs = crs.uuid,
|
717
720
|
title = 'v cell')
|
@@ -917,7 +920,7 @@ def edges(t):
|
|
917
920
|
"""
|
918
921
|
|
919
922
|
assert t.ndim == 2 and t.shape[1] == 3
|
920
|
-
all_edges = np.empty((len(t), 3, 2), dtype =
|
923
|
+
all_edges = np.empty((len(t), 3, 2), dtype = t.dtype)
|
921
924
|
all_edges[:, :, 0] = t
|
922
925
|
all_edges[:, :2, 1] = t[:, 1:]
|
923
926
|
all_edges[:, 2, 1] = t[:, 0]
|
@@ -946,6 +949,7 @@ def triangles_using_edges(t, edges):
|
|
946
949
|
"""Returns int array of shape (len(edges), 2) with indices of upto 2 triangles using each edge (-1 for unused)."""
|
947
950
|
|
948
951
|
assert t.ndim == 2 and t.shape[1] == 3 and edges.ndim == 2 and edges.shape[1] == 2
|
952
|
+
t_type = np.int32 if len(t) < 2_147_483_648 else np.int64
|
949
953
|
ti = np.full((len(edges), 2), -1, dtype = int)
|
950
954
|
for i in range(len(edges)):
|
951
955
|
te = triangles_using_edge(t, edges[i, 0], edges[i, 1])
|
resqpy/olio/vector_utilities.py
CHANGED
@@ -14,7 +14,7 @@ import math as maths
|
|
14
14
|
import numpy as np
|
15
15
|
import numba # type: ignore
|
16
16
|
from numba import njit, prange # type: ignore
|
17
|
-
from typing import Tuple, Optional
|
17
|
+
from typing import Tuple, Optional, List
|
18
18
|
|
19
19
|
|
20
20
|
def radians_from_degrees(deg):
|
@@ -1013,6 +1013,47 @@ def vertical_intercept_nan(x: float, x_value_0: float, x_value_1: float, y_value
|
|
1013
1013
|
return y
|
1014
1014
|
|
1015
1015
|
|
1016
|
+
@njit
|
1017
|
+
def vertical_intercept_nan_yz(x: float, x_0: float, x_1: float, y_0: float, y_1: float, z_0: float,
|
1018
|
+
z_1: float) -> Tuple[float, float]: # pragma: no cover
|
1019
|
+
"""Finds the y & z values of a straight line between two points at a given x.
|
1020
|
+
|
1021
|
+
If the x value given is not within the x values of the points, returns NaN.
|
1022
|
+
|
1023
|
+
arguments:
|
1024
|
+
x (float): x value at which to determine the y value
|
1025
|
+
x_0 (float): the x coordinate of point a
|
1026
|
+
x_1 (float): the x coordinate of point b
|
1027
|
+
y_0 (float): the y coordinate of point a
|
1028
|
+
y_1 (float): the y coordinate of point b
|
1029
|
+
z_0 (float): the z coordinate of point a
|
1030
|
+
z_1 (float): the z coordinate of point b
|
1031
|
+
|
1032
|
+
returns:
|
1033
|
+
y, z (float, float): y & z values of the straight line segment between point a and point b,
|
1034
|
+
evaluated at x; if x is outside the x values range, y & z are NaN
|
1035
|
+
"""
|
1036
|
+
y = np.nan
|
1037
|
+
z = np.nan
|
1038
|
+
if x_1 < x_0:
|
1039
|
+
x_0, x_1 = x_1, x_0
|
1040
|
+
y_0, y_1 = y_1, y_0
|
1041
|
+
z_0, z_1 = z_1, z_0
|
1042
|
+
if x >= x_0 and x <= x_1:
|
1043
|
+
if x_0 == x_1:
|
1044
|
+
y = y_0
|
1045
|
+
z = z_0
|
1046
|
+
else:
|
1047
|
+
xr = x_1 - x_0
|
1048
|
+
m = (y_1 - y_0) / xr
|
1049
|
+
c = y_1 - m * x_1
|
1050
|
+
y = m * x + c
|
1051
|
+
m = (z_1 - z_0) / xr
|
1052
|
+
c = z_1 - m * x_1
|
1053
|
+
z = m * x + c
|
1054
|
+
return (y, z)
|
1055
|
+
|
1056
|
+
|
1016
1057
|
@njit
|
1017
1058
|
def points_in_triangles_aligned_optimised_old(nx: int, ny: int, dx: float, dy: float,
|
1018
1059
|
triangles: np.ndarray) -> np.ndarray: # pragma: no cover
|
@@ -1138,6 +1179,56 @@ def points_in_triangles_aligned_optimised(nx: int,
|
|
1138
1179
|
return collated
|
1139
1180
|
|
1140
1181
|
|
1182
|
+
@njit(parallel = True)
|
1183
|
+
def points_in_triangles_aligned_unified(nx: int,
|
1184
|
+
ny: int,
|
1185
|
+
ax: int,
|
1186
|
+
ay: int,
|
1187
|
+
az: int,
|
1188
|
+
triangles: np.ndarray,
|
1189
|
+
n_batches: int = 20) -> Tuple[np.ndarray, np.ndarray]: # pragma: no cover
|
1190
|
+
"""Calculates which points are within which triangles in 2D for a regular mesh of aligned points.
|
1191
|
+
|
1192
|
+
arguments:
|
1193
|
+
- nx (int): number of points in x axis
|
1194
|
+
- ny (int): number of points in y axis
|
1195
|
+
- ax (int): 'x' axis selection (0, 1, or 2)
|
1196
|
+
- ay (int): 'y' axis selection (0, 1, or 2)
|
1197
|
+
- az (int): 'z' axis selection (0, 1, or 2)
|
1198
|
+
- triangles (np.ndarray): float array of each triangles' normalised vertices in 3D, shape (N, 3, 3)
|
1199
|
+
- n_batches (int, default 20): number of parallel batches
|
1200
|
+
|
1201
|
+
returns:
|
1202
|
+
list like int array with each row being (tri number, axis 'y' int, axis 'x' int), and
|
1203
|
+
corresponding list like float array being axis 'z' sampled at point on triangle
|
1204
|
+
|
1205
|
+
notes:
|
1206
|
+
- actual axes to use for x, y, & z are determined by the ax, ay, & az arguments
|
1207
|
+
- 0, 1, & 2 must appear once each amongst the ax, ay, & az arguments
|
1208
|
+
- triangles points have already been normalised to a unit grid spacing and offset by half a cell
|
1209
|
+
- returned 'z' values are in normalised form
|
1210
|
+
- to denormalize 'z' values, add 0.5 and multiply by the actual cell length in the corresponding axis
|
1211
|
+
"""
|
1212
|
+
n_triangles = len(triangles)
|
1213
|
+
if n_triangles == 0:
|
1214
|
+
return np.empty((0, 3), dtype = np.int32), np.empty((0,), dtype = np.float64)
|
1215
|
+
n_batches = min(n_triangles, n_batches)
|
1216
|
+
batch_size = (n_triangles - 1) // n_batches + 1
|
1217
|
+
tp = [np.empty((0, 3), dtype = np.int32)] * n_batches
|
1218
|
+
tz = [np.empty((0,), dtype = np.float64)] * n_batches
|
1219
|
+
for batch in prange(n_batches):
|
1220
|
+
base = batch * batch_size
|
1221
|
+
tp[batch], tz[batch] = _points_in_triangles_aligned_unified_batch(nx, ny, base,
|
1222
|
+
triangles[base:(batch + 1) * batch_size], ax,
|
1223
|
+
ay, az)
|
1224
|
+
collated = np.empty((0, 3), dtype = np.int32)
|
1225
|
+
collated_z = np.empty((0,), dtype = np.float64)
|
1226
|
+
for batch in range(n_batches):
|
1227
|
+
collated = np.concatenate((collated, tp[batch]), axis = 0)
|
1228
|
+
collated_z = np.concatenate((collated_z, tz[batch]), axis = 0)
|
1229
|
+
return collated, collated_z
|
1230
|
+
|
1231
|
+
|
1141
1232
|
@njit
|
1142
1233
|
def _points_in_triangles_aligned_optimised_batch(nx: int, ny: int, base_triangle: int,
|
1143
1234
|
triangles: np.ndarray) -> np.ndarray: # pragma: no cover
|
@@ -1165,6 +1256,89 @@ def _points_in_triangles_aligned_optimised_batch(nx: int, ny: int, base_triangle
|
|
1165
1256
|
return triangles_points
|
1166
1257
|
|
1167
1258
|
|
1259
|
+
@njit
|
1260
|
+
def _points_in_triangles_aligned_unified_batch(nx: int, ny: int, base_triangle: int, tp: np.ndarray, ax: int, ay: int,
|
1261
|
+
az: int) -> Tuple[np.ndarray, np.ndarray]: # pragma: no cover
|
1262
|
+
# returns list like int array with each row being (tri number, axis y int, axis x int), and
|
1263
|
+
# corresponding list like float array being axis z sampled at point on triangle
|
1264
|
+
# todo: add type subscripting once support for python 3.8 is dropped
|
1265
|
+
int_list = []
|
1266
|
+
sampled_z_list = []
|
1267
|
+
|
1268
|
+
for triangle_num in range(len(tp)):
|
1269
|
+
tri = tp[triangle_num]
|
1270
|
+
min_x = np.min(tri[:, ax])
|
1271
|
+
max_x = np.max(tri[:, ax])
|
1272
|
+
min_y = np.min(tri[:, ay])
|
1273
|
+
max_y = np.max(tri[:, ay])
|
1274
|
+
for xi in range(max(maths.ceil(min_x), 0), min(maths.floor(max_x) + 1, nx)):
|
1275
|
+
x = float(xi)
|
1276
|
+
e0_y, e0_z = vertical_intercept_nan_yz(x, tri[1, ax], tri[2, ax], tri[1, ay], tri[2, ay], tri[1, az],
|
1277
|
+
tri[2, az])
|
1278
|
+
e1_y, e1_z = vertical_intercept_nan_yz(x, tri[0, ax], tri[1, ax], tri[0, ay], tri[1, ay], tri[0, az],
|
1279
|
+
tri[1, az])
|
1280
|
+
e2_y, e2_z = vertical_intercept_nan_yz(x, tri[0, ax], tri[2, ax], tri[0, ay], tri[2, ay], tri[0, az],
|
1281
|
+
tri[2, az])
|
1282
|
+
floor_y = np.nan
|
1283
|
+
ceil_y = np.nan
|
1284
|
+
floor_z = np.nan
|
1285
|
+
ceil_z = np.nan
|
1286
|
+
if not np.isnan(e0_y):
|
1287
|
+
floor_y = e0_y
|
1288
|
+
ceil_y = e0_y
|
1289
|
+
floor_z = e0_z
|
1290
|
+
ceil_z = e0_z
|
1291
|
+
if not np.isnan(e1_y):
|
1292
|
+
if np.isnan(floor_y):
|
1293
|
+
floor_y = e1_y
|
1294
|
+
ceil_y = e1_y
|
1295
|
+
floor_z = e1_z
|
1296
|
+
ceil_z = e1_z
|
1297
|
+
else:
|
1298
|
+
if e1_y < floor_y:
|
1299
|
+
floor_y = e1_y
|
1300
|
+
floor_z = e1_z
|
1301
|
+
else:
|
1302
|
+
ceil_y = e1_y
|
1303
|
+
ceil_z = e1_z
|
1304
|
+
if not np.isnan(e2_y):
|
1305
|
+
if np.isnan(floor_y):
|
1306
|
+
floor_y = e2_y
|
1307
|
+
ceil_y = e2_y
|
1308
|
+
floor_z = e2_z
|
1309
|
+
ceil_z = e2_z
|
1310
|
+
else:
|
1311
|
+
if e2_y < floor_y:
|
1312
|
+
floor_y = e2_y
|
1313
|
+
floor_z = e2_z
|
1314
|
+
elif e2_y > ceil_y:
|
1315
|
+
ceil_y = e2_y
|
1316
|
+
ceil_z = e2_z
|
1317
|
+
y_range = ceil_y - floor_y
|
1318
|
+
z_range = ceil_z - floor_z
|
1319
|
+
t = triangle_num + base_triangle
|
1320
|
+
extend_int_list = []
|
1321
|
+
extend_z_list = []
|
1322
|
+
for y in range(max(maths.ceil(floor_y), 0), min(maths.floor(ceil_y) + 1, ny)):
|
1323
|
+
yf = float(y) - floor_y
|
1324
|
+
if y_range > 0.0:
|
1325
|
+
yf /= y_range
|
1326
|
+
z = floor_z + yf * z_range
|
1327
|
+
extend_int_list.append([triangle_num + base_triangle, y, xi])
|
1328
|
+
extend_z_list.append(z)
|
1329
|
+
int_list.extend(extend_int_list)
|
1330
|
+
sampled_z_list.extend(extend_z_list)
|
1331
|
+
|
1332
|
+
if len(int_list) == 0:
|
1333
|
+
int_array = np.empty((0, 3), dtype = np.int32)
|
1334
|
+
z_array = np.empty((0,), dtype = np.float64)
|
1335
|
+
else:
|
1336
|
+
int_array = np.array(int_list, dtype = np.int32)
|
1337
|
+
z_array = np.array(sampled_z_list, dtype = np.float64)
|
1338
|
+
|
1339
|
+
return (int_array, z_array)
|
1340
|
+
|
1341
|
+
|
1168
1342
|
def triangle_normal_vector(p3):
|
1169
1343
|
"""For a triangle in 3D space, defined by 3 vertex points, returns a unit vector normal to the plane of the triangle.
|
1170
1344
|
|
resqpy/olio/wellspec_keywords.py
CHANGED
@@ -75,9 +75,9 @@ wellspec_dict['DZ'] = (0, wk_preferably_not, wk_okay, None, True) # no
|
|
75
75
|
|
76
76
|
wellspec_dtype: Dict[str, Type] = { } # mapping wellspec column key to expected data type
|
77
77
|
|
78
|
-
wellspec_dtype['IW'] =
|
79
|
-
wellspec_dtype['JW'] =
|
80
|
-
wellspec_dtype['L'] =
|
78
|
+
wellspec_dtype['IW'] = np.int32
|
79
|
+
wellspec_dtype['JW'] = np.int32
|
80
|
+
wellspec_dtype['L'] = np.int32
|
81
81
|
wellspec_dtype['GRID'] = str
|
82
82
|
wellspec_dtype['RADW'] = float
|
83
83
|
wellspec_dtype['KHMULT'] = float
|
@@ -89,11 +89,11 @@ wellspec_dtype['KH'] = float
|
|
89
89
|
wellspec_dtype['SKIN'] = float
|
90
90
|
wellspec_dtype['PPERF'] = float
|
91
91
|
wellspec_dtype['ANGLE'] = float
|
92
|
-
wellspec_dtype['IRELPM'] =
|
92
|
+
wellspec_dtype['IRELPM'] = np.int32
|
93
93
|
wellspec_dtype['RADB'] = float
|
94
94
|
wellspec_dtype['WI'] = float
|
95
95
|
wellspec_dtype['K'] = float
|
96
|
-
wellspec_dtype['LAYER'] =
|
96
|
+
wellspec_dtype['LAYER'] = np.int32
|
97
97
|
wellspec_dtype['DEPTH'] = float # '#' causes nexus to use cell depth
|
98
98
|
wellspec_dtype['X'] = float # use cell X for i.p. perf
|
99
99
|
wellspec_dtype['Y'] = float # use cell Y for i.p. perf
|
@@ -109,10 +109,10 @@ wellspec_dtype['PARENT'] = str
|
|
109
109
|
wellspec_dtype['MDCON'] = float
|
110
110
|
wellspec_dtype['SECT'] = str # todo: need to check type
|
111
111
|
wellspec_dtype['FLOWSECT'] = str # todo: need to check type
|
112
|
-
wellspec_dtype['ZONE'] =
|
112
|
+
wellspec_dtype['ZONE'] = np.int32
|
113
113
|
wellspec_dtype['GROUP'] = str
|
114
114
|
wellspec_dtype['TEMP'] = float
|
115
|
-
wellspec_dtype['IPTN'] =
|
115
|
+
wellspec_dtype['IPTN'] = np.int32
|
116
116
|
wellspec_dtype['D'] = float
|
117
117
|
wellspec_dtype['ND'] = str
|
118
118
|
wellspec_dtype['DZ'] = float
|
@@ -456,7 +456,7 @@ def get_well_data(
|
|
456
456
|
line = kf.strip_trailing_comment(file.readline()).upper()
|
457
457
|
columns_present = line.split()
|
458
458
|
if selecting:
|
459
|
-
column_map = np.full((len(column_list),), -1, dtype =
|
459
|
+
column_map = np.full((len(column_list),), -1, dtype = np.int32)
|
460
460
|
for i in range(len(column_list)):
|
461
461
|
column = column_list[i].upper()
|
462
462
|
if column in columns_present:
|
@@ -519,13 +519,19 @@ def get_well_data(
|
|
519
519
|
|
520
520
|
def stat_tranformation(row):
|
521
521
|
if row["STAT"] == "ON":
|
522
|
-
return 1
|
522
|
+
return np.int8(1)
|
523
523
|
else:
|
524
|
-
return 0
|
524
|
+
return np.int8(0)
|
525
525
|
|
526
526
|
if "STAT" in df.columns:
|
527
527
|
df["STAT"] = df.apply(lambda row: stat_tranformation(row), axis = 1)
|
528
528
|
|
529
|
+
int_col_dict = {}
|
530
|
+
for col in ["IW", "JW", "L", "LAYER", "STAT"]:
|
531
|
+
if col in df.columns:
|
532
|
+
int_col_dict[col] = (np.int8 if col == "STAT" else np.int32)
|
533
|
+
df = df.astype(int_col_dict)
|
534
|
+
|
529
535
|
return df
|
530
536
|
|
531
537
|
|
@@ -51,11 +51,11 @@ class GridPropertyCollection(rqp.PropertyCollection):
|
|
51
51
|
|
52
52
|
# NB: RESQML documentation is not clear which order is correct; should be kept consistent with same data in fault.py
|
53
53
|
# face_index_map maps from (axis, p01) to face index value in range 0..5
|
54
|
-
#
|
55
|
-
self.face_index_map = np.array([[0, 1], [2, 4], [5, 3]], dtype =
|
54
|
+
# self.face_index_map = np.array([[0, 1], [4, 2], [5, 3]], dtype = np.int8)
|
55
|
+
self.face_index_map = np.array([[0, 1], [2, 4], [5, 3]], dtype = np.int8) # order: top, base, J-, I+, J+, I-
|
56
56
|
# and the inverse, maps from 0..5 to (axis, p01)
|
57
|
-
#
|
58
|
-
self.face_index_inverse_map = np.array([[0, 0], [0, 1], [1, 0], [2, 1], [1, 1], [2, 0]], dtype =
|
57
|
+
# self.face_index_inverse_map = np.array([[0, 0], [0, 1], [1, 1], [2, 1], [1, 0], [2, 0]], dtype = np.int8)
|
58
|
+
self.face_index_inverse_map = np.array([[0, 0], [0, 1], [1, 0], [2, 1], [1, 1], [2, 0]], dtype = np.int8)
|
59
59
|
|
60
60
|
def _copy_support_to_grid_attributes(self):
|
61
61
|
# following three pseudonyms are for backward compatibility
|
@@ -266,11 +266,11 @@ class GridPropertyCollection(rqp.PropertyCollection):
|
|
266
266
|
dtype = None
|
267
267
|
if keyword[0].upper() == 'I' or keyword in ['KID', 'UID', 'UNPACK', 'DAD']:
|
268
268
|
# coerce to integer values (vdb stores integer data as reals!)
|
269
|
-
dtype =
|
269
|
+
dtype = np.int32
|
270
270
|
elif keyword in ['DEADCELL', 'LIVECELL']:
|
271
|
-
dtype =
|
271
|
+
dtype = bool # could use the default dtype of 64 bit integer
|
272
272
|
else:
|
273
|
-
dtype =
|
273
|
+
dtype = np.float32
|
274
274
|
discrete = False
|
275
275
|
import_array = vdbase.grid_static_property(grid_name, keyword, dtype = dtype)
|
276
276
|
assert import_array is not None
|
@@ -335,7 +335,7 @@ class GridPropertyCollection(rqp.PropertyCollection):
|
|
335
335
|
time_index = timestep
|
336
336
|
keyword = keyword.upper()
|
337
337
|
try:
|
338
|
-
import_array = vdbase.grid_recurrent_property_for_timestep(grid_name, keyword, timestep, dtype =
|
338
|
+
import_array = vdbase.grid_recurrent_property_for_timestep(grid_name, keyword, timestep, dtype = np.float32)
|
339
339
|
assert import_array is not None
|
340
340
|
except Exception:
|
341
341
|
# could raise an exception (as for static properties)
|
@@ -489,9 +489,9 @@ class GridPropertyCollection(rqp.PropertyCollection):
|
|
489
489
|
for k0 in range(self.grid.nk):
|
490
490
|
for j0 in range(self.grid.nj):
|
491
491
|
for i0 in range(self.grid.ni):
|
492
|
-
#
|
492
|
+
# if decoarsen_array[k0, j0, i0] < 0:
|
493
493
|
if kid[k0, j0, i0] == 0:
|
494
|
-
#
|
494
|
+
# assert not kid_mask[k0, j0, i0]
|
495
495
|
ke = k0 + 1
|
496
496
|
while ke < self.grid.nk and kid_mask[ke, j0, i0]:
|
497
497
|
ke += 1
|
@@ -225,11 +225,11 @@ def import_vdb_ensemble(
|
|
225
225
|
vdb_file = ensemble_list[realisation]
|
226
226
|
log.info('processing realisation ' + str(realisation) + ' from: ' + str(vdb_file))
|
227
227
|
vdbase = vdb.VDB(vdb_file)
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
228
|
+
# case_list = vdbase.cases()
|
229
|
+
# assert len(case_list) > 0, 'no cases found in vdb: ' + str(vdb_file)
|
230
|
+
# if len(case_list) > 1: log.warning('more than one case found in vdb (using first): ' + str(vdb_file))
|
231
|
+
# vdb_case = case_list[0]
|
232
|
+
# vdbase.set_use_case(vdb_case)
|
233
233
|
vdbase.set_extent_kji(grid.extent_kji)
|
234
234
|
|
235
235
|
prop_import_collection = rp.GridPropertyCollection(realization = realisation)
|
@@ -243,9 +243,8 @@ def import_vdb_ensemble(
|
|
243
243
|
if keyword_list is not None and keyword not in keyword_list:
|
244
244
|
continue
|
245
245
|
prop_kind, facet_type, facet = rp.property_kind_and_facet_from_keyword(keyword)
|
246
|
-
if property_kind_list is not None and prop_kind not in property_kind_list and
|
247
|
-
|
248
|
-
]:
|
246
|
+
if property_kind_list is not None and prop_kind not in property_kind_list and \
|
247
|
+
prop_kind not in ['active', 'region initialization']:
|
249
248
|
continue
|
250
249
|
prop_import_collection.import_vdb_static_property_to_cache(vdbase,
|
251
250
|
keyword,
|
@@ -312,9 +311,9 @@ def import_vdb_ensemble(
|
|
312
311
|
if decoarsen_array is not None:
|
313
312
|
step_import_collection.decoarsen_imported_list(decoarsen_array = decoarsen_array)
|
314
313
|
# extend hdf5 with cached arrays for this timestep
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
314
|
+
# log.info('number of recurrent grid property arrays for timestep: ' + str(timestep_number) +
|
315
|
+
# ' is: ' + str(step_import_collection.number_of_imports()))
|
316
|
+
# log.info('extending hdf5 file with recurrent properties for timestep: ' + str(timestep_number))
|
318
317
|
grid.write_hdf5_from_caches(hdf5_file,
|
319
318
|
mode = 'a',
|
320
319
|
geometry = False,
|
@@ -322,8 +321,8 @@ def import_vdb_ensemble(
|
|
322
321
|
write_active = False)
|
323
322
|
# add imported list for this timestep to full imported list
|
324
323
|
prop_import_collection.inherit_imported_list_from_other_collection(step_import_collection)
|
325
|
-
#
|
326
|
-
#
|
324
|
+
# log.debug('total number of property arrays after timestep: ' + str(timestep_number) +
|
325
|
+
# ' is: ' + str(prop_import_collection.number_of_imports()))
|
327
326
|
# remove cached copies of arrays
|
328
327
|
step_import_collection.remove_all_cached_arrays()
|
329
328
|
|
resqpy/surface/_mesh.py
CHANGED
@@ -207,6 +207,10 @@ class Mesh(rqsb.BaseSurface):
|
|
207
207
|
extra_metadata = em)
|
208
208
|
return mesh
|
209
209
|
|
210
|
+
def is_big(self):
|
211
|
+
"""Returns True if the number of nodes exceeds 2^31 - 1, False otherwise."""
|
212
|
+
return (self.ni * self.nj >= 2_147_483_648)
|
213
|
+
|
210
214
|
def set_represented_interpretation_root(self, interp_root):
|
211
215
|
"""Makes a note of the xml root of the represented interpretation."""
|
212
216
|
|
resqpy/surface/_surface.py
CHANGED
@@ -279,11 +279,13 @@ class Surface(rqsb.BaseSurface):
|
|
279
279
|
self.extract_patches(self.root)
|
280
280
|
return len(self.patch_list)
|
281
281
|
|
282
|
-
def triangles_and_points(self, patch = None):
|
282
|
+
def triangles_and_points(self, patch = None, copy = False):
|
283
283
|
"""Returns arrays representing one patch or a combination of all the patches in the surface.
|
284
284
|
|
285
285
|
arguments:
|
286
286
|
patch (int, optional): patch index; if None, combined arrays for all patches are returned
|
287
|
+
copy (bool, default False): if True, a copy of the arrays is returned; if False, the cached
|
288
|
+
arrays are returned
|
287
289
|
|
288
290
|
returns:
|
289
291
|
tuple (triangles, points):
|
@@ -296,21 +298,30 @@ class Surface(rqsb.BaseSurface):
|
|
296
298
|
|
297
299
|
self.extract_patches(self.root)
|
298
300
|
if patch is None:
|
299
|
-
if self.triangles is None:
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
301
|
+
if self.triangles is None or self.points is None:
|
302
|
+
if self.triangles is None:
|
303
|
+
points_offset = 0
|
304
|
+
for triangulated_patch in self.patch_list:
|
305
|
+
(t, p) = triangulated_patch.triangles_and_points()
|
306
|
+
if points_offset == 0:
|
307
|
+
self.triangles = t
|
308
|
+
self.points = p
|
309
|
+
else:
|
310
|
+
self.triangles = np.concatenate((self.triangles, t.copy() + points_offset))
|
311
|
+
self.points = np.concatenate((self.points, p))
|
312
|
+
points_offset += p.shape[0]
|
313
|
+
if copy:
|
314
|
+
return (self.triangles.copy(), self.points.copy())
|
315
|
+
else:
|
316
|
+
return (self.triangles, self.points)
|
311
317
|
assert 0 <= patch < len(self.patch_list), \
|
312
318
|
ValueError(f'patch index {patch} out of range for surface with {len(self.patch_list)} patches')
|
313
|
-
return self.patch_list[patch].triangles_and_points()
|
319
|
+
return self.patch_list[patch].triangles_and_points(copy = copy)
|
320
|
+
|
321
|
+
def decache_triangles_and_points(self):
|
322
|
+
"""Removes the cached composite triangles and points arrays."""
|
323
|
+
self.points = None
|
324
|
+
self.triangles = None
|
314
325
|
|
315
326
|
def triangle_count(self, patch = None):
|
316
327
|
"""Return the numner of triangles in this surface, or in one patch.
|
@@ -438,10 +449,11 @@ class Surface(rqsb.BaseSurface):
|
|
438
449
|
t, p = large_surface.triangles_and_points()
|
439
450
|
assert p.ndim == 2 and p.shape[1] == 3
|
440
451
|
pp = np.concatenate((p, line), axis = 0)
|
441
|
-
|
452
|
+
t_type = np.int32 if len(pp) <= 2_147_483_647 else np.int64
|
453
|
+
tp = np.empty(p.shape, dtype = t_type)
|
442
454
|
tp[:, 0] = len(p)
|
443
455
|
tp[:, 1] = len(p) + 1
|
444
|
-
tp[:, 2] = np.arange(len(p), dtype =
|
456
|
+
tp[:, 2] = np.arange(len(p), dtype = t_type)
|
445
457
|
cw = vec.clockwise_triangles(pp, tp)
|
446
458
|
pai = (cw >= 0.0) # bool mask over p
|
447
459
|
pbi = (cw <= 0.0) # bool mask over p
|
@@ -453,11 +465,11 @@ class Surface(rqsb.BaseSurface):
|
|
453
465
|
# here we stick the two halves together into a single patch
|
454
466
|
# todo: keep as two patches as required by RESQML business rules
|
455
467
|
p_combo = np.empty((0, 3))
|
456
|
-
t_combo = np.empty((0, 3), dtype =
|
468
|
+
t_combo = np.empty((0, 3), dtype = t_type)
|
457
469
|
for i, tab in enumerate((ta, tb)):
|
458
470
|
p_keep = np.unique(t[tab])
|
459
471
|
# note new point index for each old point that is being kept
|
460
|
-
p_map = np.full(len(p), -1, dtype =
|
472
|
+
p_map = np.full(len(p), -1, dtype = t_type)
|
461
473
|
p_map[p_keep] = np.arange(len(p_keep))
|
462
474
|
# copy those unique points into a trimmed points array
|
463
475
|
points_trimmed = p[p_keep].copy()
|
@@ -950,7 +962,8 @@ class Surface(rqsb.BaseSurface):
|
|
950
962
|
triangles.append(line.rstrip().split(" ")[1:4])
|
951
963
|
assert len(vertices) >= 3, 'vertices missing'
|
952
964
|
assert len(triangles) > 0, 'triangles missing'
|
953
|
-
|
965
|
+
t_type = np.int32 if len(vertices) <= 2_147_483_647 else np.int64
|
966
|
+
triangles = np.array(triangles, dtype = t_type) - index_offset
|
954
967
|
vertices = np.array(vertices, dtype = float)
|
955
968
|
assert np.all(triangles >= 0) and np.all(triangles < len(vertices)), 'triangle vertex indices out of range'
|
956
969
|
self.set_from_triangles_and_points(triangles = triangles, points = vertices)
|
@@ -1139,8 +1152,9 @@ class Surface(rqsb.BaseSurface):
|
|
1139
1152
|
|
1140
1153
|
# TODO: implement alternate solution using edge functions in olio triangulation to optimise
|
1141
1154
|
points_unique, inverse = np.unique(allpoints, axis = 0, return_inverse = True)
|
1142
|
-
|
1143
|
-
|
1155
|
+
t_type = np.int32 if len(allpoints) <= 2_147_483_647 else np.int64
|
1156
|
+
tris = np.array(tris, dtype = t_type)
|
1157
|
+
tris_unique = np.empty(shape = tris.shape, dtype = t_type)
|
1144
1158
|
tris_unique[:, 0] = inverse[tris[:, 0]]
|
1145
1159
|
tris_unique[:, 1] = inverse[tris[:, 1]]
|
1146
1160
|
tris_unique[:, 2] = inverse[tris[:, 2]]
|
@@ -1333,7 +1347,8 @@ def distill_triangle_points(t, p):
|
|
1333
1347
|
# find unique points used by triangles
|
1334
1348
|
p_keep = np.unique(t)
|
1335
1349
|
# note new point index for each old point that is being kept
|
1336
|
-
|
1350
|
+
t_type = np.int32 if len(p) <= 2_147_483_647 else np.int64
|
1351
|
+
p_map = np.full(len(p), -1, dtype = t_type)
|
1337
1352
|
p_map[p_keep] = np.arange(len(p_keep))
|
1338
1353
|
# copy those unique points into a trimmed points array
|
1339
1354
|
points_distilled = p[p_keep]
|
@@ -1360,8 +1375,9 @@ def nan_removed_triangles_and_points(t, p):
|
|
1360
1375
|
expanded_mask[:] = np.expand_dims(np.logical_not(t_nan_mask), axis = -1)
|
1361
1376
|
t_filtered = t[expanded_mask].reshape((-1, 3))
|
1362
1377
|
# modified the filtered t values to adjust for the compression of filtered p
|
1363
|
-
|
1364
|
-
p_map
|
1378
|
+
t_type = np.int32 if len(p) <= 2_147_483_647 else np.int64
|
1379
|
+
p_map = np.full(len(p), -1, dtype = t_type)
|
1380
|
+
p_map[p_non_nan_mask] = np.arange(len(p_filtered), dtype = t_type)
|
1365
1381
|
t_filtered = p_map[t_filtered]
|
1366
1382
|
assert t_filtered.ndim == 2 and t_filtered.shape[1] == 3
|
1367
1383
|
assert not np.any(t_filtered < 0) and not np.any(t_filtered >= len(p_filtered))
|