resqpy 4.5.0__py3-none-any.whl → 4.6.3__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/grid/_defined_geometry.py +15 -9
- resqpy/grid_surface/__init__.py +61 -40
- resqpy/grid_surface/_find_faces.py +351 -243
- resqpy/grid_surface/grid_surface_cuda.py +172 -125
- resqpy/lines/_common.py +10 -7
- resqpy/lines/_polyline.py +20 -0
- resqpy/lines/_polyline_set.py +64 -34
- resqpy/model/_hdf5.py +17 -7
- resqpy/model/_model.py +2 -1
- resqpy/model/_xml.py +4 -4
- resqpy/multi_processing/_multiprocessing.py +1 -0
- resqpy/multi_processing/wrappers/grid_surface_mp.py +12 -3
- resqpy/olio/intersection.py +2 -3
- resqpy/olio/read_nexus_fault.py +71 -67
- resqpy/olio/triangulation.py +66 -22
- resqpy/olio/vector_utilities.py +175 -71
- resqpy/olio/wellspec_keywords.py +5 -4
- resqpy/olio/write_hdf5.py +16 -8
- resqpy/olio/xml_et.py +3 -3
- resqpy/property/_collection_get_attributes.py +11 -5
- resqpy/property/_property.py +16 -5
- resqpy/property/property_collection.py +36 -11
- resqpy/surface/__init__.py +2 -2
- resqpy/surface/_surface.py +69 -6
- resqpy/time_series/__init__.py +3 -2
- resqpy/time_series/_time_series.py +10 -0
- {resqpy-4.5.0.dist-info → resqpy-4.6.3.dist-info}/METADATA +3 -3
- {resqpy-4.5.0.dist-info → resqpy-4.6.3.dist-info}/RECORD +31 -31
- {resqpy-4.5.0.dist-info → resqpy-4.6.3.dist-info}/WHEEL +1 -1
- {resqpy-4.5.0.dist-info → resqpy-4.6.3.dist-info}/LICENSE +0 -0
resqpy/lines/_polyline_set.py
CHANGED
@@ -68,7 +68,9 @@ class PolylineSet(rql_c._BasePolyline):
|
|
68
68
|
self.save_polys = False
|
69
69
|
self.boolnotconstant = None
|
70
70
|
self.boolvalue = None
|
71
|
+
self.closed_array = None
|
71
72
|
self.crs_uuid = crs_uuid
|
73
|
+
self.indices = None
|
72
74
|
|
73
75
|
super().__init__(model = parent_model,
|
74
76
|
uuid = uuid,
|
@@ -92,10 +94,11 @@ class PolylineSet(rql_c._BasePolyline):
|
|
92
94
|
break
|
93
95
|
self.polys = polylines
|
94
96
|
# Setting the title of the first polyline given as the PolylineSet title
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
|
97
|
+
if not self.title:
|
98
|
+
if len(polylines) > 1:
|
99
|
+
self.title = f"{polylines[0].title} + {len(polylines)-1} polylines"
|
100
|
+
else:
|
101
|
+
self.title = polylines[0].title
|
99
102
|
|
100
103
|
elif irap_file is not None: # Create from an input IRAP file
|
101
104
|
if crs_uuid is None:
|
@@ -113,6 +116,7 @@ class PolylineSet(rql_c._BasePolyline):
|
|
113
116
|
root = self.root
|
114
117
|
|
115
118
|
self.rep_int_root = self.model.referenced_node(rqet.find_tag(root, 'RepresentedInterpretation'))
|
119
|
+
self.closed_array = np.empty((0,), dtype = bool)
|
116
120
|
|
117
121
|
for patch_node in rqet.list_of_tag(root, 'LinePatch'): # Loop over all LinePatches - likely just the one
|
118
122
|
assert patch_node is not None # Required field
|
@@ -134,7 +138,7 @@ class PolylineSet(rql_c._BasePolyline):
|
|
134
138
|
closed_array = self.get_bool_array(closed_node)
|
135
139
|
|
136
140
|
count_node = rqet.find_tag(patch_node, 'NodeCountPerPolyline')
|
137
|
-
rql_c.load_hdf5_array(self, count_node, 'count_perpol', tag = 'Values')
|
141
|
+
rql_c.load_hdf5_array(self, count_node, 'count_perpol', tag = 'Values', dtype = 'int')
|
138
142
|
|
139
143
|
points_node = rqet.find_tag(geometry_node, 'Points')
|
140
144
|
assert points_node is not None # Required field
|
@@ -155,12 +159,16 @@ class PolylineSet(rql_c._BasePolyline):
|
|
155
159
|
# delattr(self,'count_perpol')
|
156
160
|
|
157
161
|
self.polys.extend(subpolys)
|
162
|
+
self.closed_array = np.concatenate((self.closed_array, closed_array))
|
163
|
+
|
164
|
+
self.bool_array_format()
|
158
165
|
|
159
166
|
def _set_from_irap(self, irap_file):
|
160
167
|
inpoints = rsl.read_lines(irap_file)
|
161
168
|
self.count_perpol = []
|
162
169
|
closed_array = []
|
163
|
-
self.title
|
170
|
+
if not self.title:
|
171
|
+
self.title = os.path.basename(irap_file).split(".")[0]
|
164
172
|
for i, poly in enumerate(inpoints):
|
165
173
|
if len(poly) > 1: # Polylines must have at least 2 points
|
166
174
|
self.count_perpol.append(len(poly))
|
@@ -183,7 +191,8 @@ class PolylineSet(rql_c._BasePolyline):
|
|
183
191
|
inpoints = f.readlines()
|
184
192
|
self.count_perpol = []
|
185
193
|
closed_array = []
|
186
|
-
self.title
|
194
|
+
if not self.title:
|
195
|
+
self.title = os.path.basename(charisma_file).split(".")[0]
|
187
196
|
for i, line in enumerate(inpoints):
|
188
197
|
line = line.split()
|
189
198
|
coord_entry = np.array([[float(line[3]), float(line[4]), float(line[5])]])
|
@@ -278,32 +287,45 @@ class PolylineSet(rql_c._BasePolyline):
|
|
278
287
|
pindex.text = '0'
|
279
288
|
|
280
289
|
if self.boolnotconstant:
|
290
|
+
|
281
291
|
# We have mixed data - use a BooleanArrayFromIndexArray
|
282
292
|
closed = rqet.SubElement(patch, ns['resqml2'] + 'ClosedPolylines')
|
283
293
|
closed.set(ns['xsi'] + 'type', ns['xsd'] + 'BooleanArrayFromIndexArray')
|
284
294
|
closed.text = '\n'
|
285
295
|
|
286
|
-
bool_val = rqet.SubElement(closed, ns['resqml2'] + '
|
296
|
+
bool_val = rqet.SubElement(closed, ns['resqml2'] + 'IndexIsTrue')
|
287
297
|
bool_val.set(ns['xsi'] + 'type', ns['xsd'] + 'boolean')
|
288
|
-
bool_val.text = str(self.boolvalue).lower()
|
289
|
-
|
290
|
-
ind_val = rqet.SubElement(closed, ns['resqml2'] + 'Indices')
|
291
|
-
ind_val.set(ns['xsi'] + 'type', ns['eml'] + 'Hdf5Dataset')
|
292
|
-
ind_val.text = '\n'
|
298
|
+
bool_val.text = str(not self.boolvalue).lower()
|
293
299
|
|
294
300
|
count = rqet.SubElement(closed, ns['resqml2'] + 'Count')
|
295
301
|
count.set(ns['xsi'] + 'type', ns['xsd'] + 'positiveInteger')
|
296
302
|
count.text = str(len(self.count_perpol))
|
297
303
|
|
298
|
-
|
304
|
+
ind_val = rqet.SubElement(closed, ns['resqml2'] + 'Indices')
|
305
|
+
ind_val.set(ns['xsi'] + 'type', ns['resqml2'] + 'IntegerHdf5Array')
|
306
|
+
ind_val.text = '\n'
|
307
|
+
|
308
|
+
iv_null = rqet.SubElement(ind_val, ns['resqml2'] + 'NullValue')
|
309
|
+
iv_null.set(ns['xsi'] + 'type', ns['xsd'] + 'integer')
|
310
|
+
iv_null.text = '-1'
|
311
|
+
|
312
|
+
iv_values = rqet.SubElement(ind_val, ns['resqml2'] + 'Values')
|
313
|
+
iv_values.set(ns['xsi'] + 'type', ns['eml'] + 'Hdf5Dataset')
|
314
|
+
iv_values.text = '\n'
|
315
|
+
|
316
|
+
self.model.create_hdf5_dataset_ref(ext_uuid, self.uuid, 'indices_patch0', root = iv_values)
|
317
|
+
|
299
318
|
else:
|
319
|
+
|
300
320
|
# All bools are the same - use a BooleanConstantArray
|
301
321
|
closed = rqet.SubElement(patch, ns['resqml2'] + 'ClosedPolylines')
|
302
322
|
closed.set(ns['xsi'] + 'type', ns['resqml2'] + 'BooleanConstantArray')
|
303
323
|
closed.text = '\n'
|
324
|
+
|
304
325
|
bool_val = rqet.SubElement(closed, ns['resqml2'] + 'Value')
|
305
326
|
bool_val.set(ns['xsi'] + 'type', ns['xsd'] + 'boolean')
|
306
327
|
bool_val.text = str(self.boolvalue).lower()
|
328
|
+
|
307
329
|
count = rqet.SubElement(closed, ns['resqml2'] + 'Count')
|
308
330
|
count.set(ns['xsi'] + 'type', ns['xsd'] + 'positiveInteger')
|
309
331
|
count.text = str(len(self.count_perpol))
|
@@ -367,7 +389,7 @@ class PolylineSet(rql_c._BasePolyline):
|
|
367
389
|
if self.uuid is None:
|
368
390
|
self.uuid = bu.new_uuid()
|
369
391
|
self.combine_polylines(self.polys)
|
370
|
-
self.bool_array_format(
|
392
|
+
self.bool_array_format()
|
371
393
|
self.save_polys = save_polylines
|
372
394
|
if self.save_polys:
|
373
395
|
for poly in self.polys:
|
@@ -377,29 +399,37 @@ class PolylineSet(rql_c._BasePolyline):
|
|
377
399
|
h5_reg.register_dataset(self.uuid, 'points_patch0', self.coordinates)
|
378
400
|
h5_reg.register_dataset(self.uuid, 'NodeCountPerPolyline_patch0', self.count_perpol.astype(np.int32))
|
379
401
|
if self.boolnotconstant:
|
380
|
-
h5_reg.register_dataset(self.uuid, 'indices_patch0', self.indices)
|
402
|
+
h5_reg.register_dataset(self.uuid, 'indices_patch0', np.array(self.indices, dtype = np.int32))
|
381
403
|
h5_reg.write(file_name, mode = mode)
|
382
404
|
|
383
405
|
def get_bool_array(self, closed_node):
|
384
|
-
# TODO:
|
385
|
-
"""Returns a boolean array using details in the node location.
|
406
|
+
# TODO: check for other permissible forms of the abstract boolean array
|
407
|
+
"""Returns a boolean array using details in the xml node location.
|
386
408
|
|
387
|
-
|
409
|
+
arguments:
|
410
|
+
closed_node (xml node): the node under which the boolean array information sits
|
388
411
|
|
389
|
-
|
390
|
-
|
412
|
+
returns:
|
413
|
+
1D numpy bool array set True for those pplylines within the set which are marked as closed
|
391
414
|
"""
|
392
|
-
if
|
415
|
+
# if type of boolean array is BooleanConstantArray, uses the array value and count to generate
|
416
|
+
# the array; if type of boolean array is BooleanArrayFromIndexArray, finds the "other" value
|
417
|
+
# bool and indices of the "other" values, and inserts these into an array opposite to the main bool
|
418
|
+
flavour = rqet.node_type(closed_node)
|
419
|
+
if flavour == 'BooleanConstantArray':
|
393
420
|
count = rqet.find_tag_int(closed_node, 'Count')
|
394
421
|
value = rqet.bool_from_text(rqet.node_text(rqet.find_tag(closed_node, 'Value')))
|
395
|
-
return np.full((count), value)
|
396
|
-
elif
|
422
|
+
return np.full((count,), value, dtype = bool)
|
423
|
+
elif flavour == 'BooleanArrayFromIndexArray':
|
397
424
|
count = rqet.find_tag_int(closed_node, 'Count')
|
398
|
-
|
425
|
+
indices_node = rqet.find_tag(closed_node, 'Indices')
|
426
|
+
assert indices_node is not None
|
427
|
+
indices_arr = rql_c.load_hdf5_array(self, indices_node, 'indices_arr', tag = 'Values', dtype = 'int')
|
399
428
|
istrue = rqet.bool_from_text(rqet.node_text(rqet.find_tag(closed_node, 'IndexIsTrue')))
|
400
|
-
out = np.full((count), not istrue)
|
429
|
+
out = np.full((count,), not istrue, dtype = bool)
|
401
430
|
out[indices_arr] = istrue
|
402
431
|
return out
|
432
|
+
raise ValueError(f'unrecognised closed array xml node type: {flavour}')
|
403
433
|
|
404
434
|
def convert_to_polylines(self,
|
405
435
|
closed_array = None,
|
@@ -496,32 +526,32 @@ class PolylineSet(rql_c._BasePolyline):
|
|
496
526
|
|
497
527
|
self.polys = polylines
|
498
528
|
|
499
|
-
def bool_array_format(self
|
500
|
-
"""Determines an appropriate output boolean array format
|
529
|
+
def bool_array_format(self):
|
530
|
+
"""Determines an appropriate output boolean array format depending on the closed_array bools.
|
501
531
|
|
502
532
|
self.boolnotconstant - set to True if all are not open or all closed
|
503
533
|
self.boolvalue - value of isclosed for all polylines, or for the majority of polylines if mixed
|
504
534
|
self.indices - array of indices where the values are not self.boolvalue, if the polylines are mixed
|
505
535
|
"""
|
506
536
|
|
537
|
+
assert self.closed_array is not None
|
507
538
|
self.indices = []
|
508
539
|
self.boolnotconstant = False
|
509
|
-
if all(closed_array):
|
540
|
+
if all(self.closed_array):
|
510
541
|
self.boolvalue = True
|
511
|
-
elif not
|
542
|
+
elif not any(self.closed_array):
|
512
543
|
self.boolvalue = False
|
513
544
|
else:
|
514
|
-
if np.count_nonzero(closed_array) > (len(closed_array)
|
545
|
+
if np.count_nonzero(self.closed_array) > (len(self.closed_array) // 2):
|
515
546
|
self.boolvalue = True
|
516
|
-
for i, val in enumerate(closed_array):
|
547
|
+
for i, val in enumerate(self.closed_array):
|
517
548
|
if not val:
|
518
549
|
self.indices.append(i)
|
519
550
|
else:
|
520
551
|
self.boolvalue = False
|
521
|
-
for i, val in enumerate(closed_array):
|
552
|
+
for i, val in enumerate(self.closed_array):
|
522
553
|
if val:
|
523
554
|
self.indices.append(i)
|
524
|
-
if len(self.indices) > 0:
|
525
555
|
self.boolnotconstant = True
|
526
556
|
|
527
557
|
def set_interpretation_root(self, rep_int_root, recursive = True):
|
resqpy/model/_hdf5.py
CHANGED
@@ -208,12 +208,15 @@ def _h5_array_element(model,
|
|
208
208
|
if object is None:
|
209
209
|
object = model
|
210
210
|
|
211
|
-
|
211
|
+
if isinstance(dtype, str) and dtype == 'pack':
|
212
|
+
dtype = bool
|
213
|
+
|
214
|
+
# check if attribute has already been cached
|
212
215
|
if array_attribute is not None:
|
213
216
|
existing_value = getattr(object, array_attribute, None)
|
214
217
|
|
215
218
|
# Watch out for np.array(None): check existing_value has a valid "shape"
|
216
|
-
if existing_value is not None and getattr(existing_value,
|
219
|
+
if existing_value is not None and getattr(existing_value, 'shape', False):
|
217
220
|
if index is None:
|
218
221
|
return None # this option allows caching of array without actually referring to any element
|
219
222
|
return existing_value[tuple(index)]
|
@@ -222,14 +225,20 @@ def _h5_array_element(model,
|
|
222
225
|
if h5_root is None:
|
223
226
|
return None
|
224
227
|
if cache_array:
|
228
|
+
str_dtype = str(dtype)
|
225
229
|
shape_tuple = tuple(h5_root[h5_key_pair[1]].shape)
|
226
|
-
if required_shape is None
|
227
|
-
|
230
|
+
if required_shape is None:
|
231
|
+
required_shape = shape_tuple
|
232
|
+
object.__dict__[array_attribute] = np.zeros(required_shape, dtype = dtype)
|
233
|
+
if shape_tuple == required_shape:
|
228
234
|
object.__dict__[array_attribute][:] = h5_root[h5_key_pair[1]]
|
235
|
+
elif (len(shape_tuple) == len(required_shape) and ('bool' in str_dtype or 'int8' in str_dtype) and
|
236
|
+
8 * (shape_tuple[-1] - 1) < required_shape[-1] <= 8 * shape_tuple[-1]):
|
237
|
+
a = np.unpackbits(h5_root[h5_key_pair[1]], axis = -1).astype(bool)
|
238
|
+
object.__dict__[array_attribute][:] = a[..., :required_shape[-1]]
|
229
239
|
else:
|
230
|
-
object.__dict__[array_attribute] =
|
231
|
-
|
232
|
-
dtype = dtype).reshape(required_shape)
|
240
|
+
object.__dict__[array_attribute][:] = \
|
241
|
+
np.array(h5_root[h5_key_pair[1]], dtype = dtype).reshape(required_shape)
|
233
242
|
_h5_release(model)
|
234
243
|
if index is None:
|
235
244
|
return None
|
@@ -241,6 +250,7 @@ def _h5_array_element(model,
|
|
241
250
|
result = h5_root[h5_key_pair[1]][tuple(index)]
|
242
251
|
else:
|
243
252
|
shape_tuple = tuple(h5_root[h5_key_pair[1]].shape)
|
253
|
+
# todo: handle unpacking of a single bit into a bool?
|
244
254
|
if shape_tuple == required_shape:
|
245
255
|
result = h5_root[h5_key_pair[1]][tuple(index)]
|
246
256
|
else:
|
resqpy/model/_model.py
CHANGED
@@ -1344,7 +1344,8 @@ class Model():
|
|
1344
1344
|
required to cache or access cached array
|
1345
1345
|
dtype (string or data type): the data type of the elements of the array (need not match hdf5 array in precision)
|
1346
1346
|
required_shape (tuple of ints, optional): if not None, the hdf5 array will be reshaped to this shape; if index
|
1347
|
-
is not None, it is taken to be applicable to the required shape
|
1347
|
+
is not None, it is taken to be applicable to the required shape; required if the array is bool data that was
|
1348
|
+
written with resqpy specific dtype of 'pack'
|
1348
1349
|
|
1349
1350
|
returns:
|
1350
1351
|
if index is None, then None;
|
resqpy/model/_xml.py
CHANGED
@@ -153,9 +153,9 @@ def _create_ref_node(model, flavour, title, uuid, content_type = None, root = No
|
|
153
153
|
else:
|
154
154
|
ct_node.text = 'application/x-resqml+xml;version=2.0;type=' + content_type
|
155
155
|
|
156
|
-
if
|
156
|
+
if title is None or len(title) == 0:
|
157
157
|
title = model.title(uuid = uuid)
|
158
|
-
if title is None:
|
158
|
+
if title is None or len(title) == 0:
|
159
159
|
title = 'untitled'
|
160
160
|
title_node = rqet.SubElement(ref_node, ns['eml'] + 'Title')
|
161
161
|
title_node.set(ns['xsi'] + 'type', ns['eml'] + 'DescriptionString')
|
@@ -204,8 +204,8 @@ def _create_rels_part(model):
|
|
204
204
|
def _create_citation(root = None, title = '', originator = None):
|
205
205
|
"""Creates a citation xml node and optionally appends as a child of root."""
|
206
206
|
|
207
|
-
if title is None:
|
208
|
-
title = ''
|
207
|
+
if title is None or len(title) == 0:
|
208
|
+
title = 'untitled'
|
209
209
|
|
210
210
|
citation = rqet.Element(ns['eml'] + 'Citation')
|
211
211
|
citation.set(ns['xsi'] + 'type', ns['eml'] + 'Citation')
|
@@ -80,6 +80,7 @@ def function_multiprocessing(function: Callable,
|
|
80
80
|
if tmp_dir_path is None:
|
81
81
|
tmp_dir_path = '.'
|
82
82
|
tmp_dir = Path(tmp_dir_path) / f'tmp_{uuid.uuid4()}'
|
83
|
+
os.makedirs(tmp_dir)
|
83
84
|
for i, kwargs in enumerate(kwargs_list):
|
84
85
|
kwargs["index"] = i
|
85
86
|
kwargs["parent_tmp_dir"] = str(tmp_dir)
|
@@ -33,12 +33,15 @@ def find_faces_to_represent_surface_regular_wrapper(
|
|
33
33
|
trimmed: bool = False,
|
34
34
|
is_curtain = False,
|
35
35
|
extend_fault_representation: bool = False,
|
36
|
+
flange_inner_ring = False,
|
37
|
+
saucer_parameter = None,
|
36
38
|
retriangulate: bool = False,
|
37
39
|
related_uuid = None,
|
38
40
|
progress_fn: Optional[Callable] = None,
|
39
41
|
extra_metadata = None,
|
40
42
|
return_properties: Optional[List[str]] = None,
|
41
|
-
raw_bisector: bool = False
|
43
|
+
raw_bisector: bool = False,
|
44
|
+
use_pack: bool = False) -> Tuple[int, bool, str, List[Union[UUID, str]]]:
|
42
45
|
"""Multiprocessing wrapper function of find_faces_to_represent_surface_regular_optimised.
|
43
46
|
|
44
47
|
arguments:
|
@@ -79,6 +82,8 @@ def find_faces_to_represent_surface_regular_wrapper(
|
|
79
82
|
the returned dictionary has the passed strings as keys and numpy arrays as values
|
80
83
|
raw_bisector (bool, default False): if True and grid bisector is requested then it is left in a raw
|
81
84
|
form without assessing which side is shallower (True values indicate same side as origin cell)
|
85
|
+
use_pack (bool, default False): if True, boolean properties will be stored in numpy packed format,
|
86
|
+
which will only be readable by resqpy based applications
|
82
87
|
|
83
88
|
returns:
|
84
89
|
Tuple containing:
|
@@ -150,6 +155,8 @@ def find_faces_to_represent_surface_regular_wrapper(
|
|
150
155
|
convexity_parameter = 2.0,
|
151
156
|
reorient = True,
|
152
157
|
extend_with_flange = extend_fault_representation,
|
158
|
+
flange_inner_ring = flange_inner_ring,
|
159
|
+
saucer_parameter = saucer_parameter,
|
153
160
|
flange_radial_distance = flange_radius,
|
154
161
|
make_clockwise = False)
|
155
162
|
extended = extend_fault_representation
|
@@ -181,6 +188,8 @@ def find_faces_to_represent_surface_regular_wrapper(
|
|
181
188
|
convexity_parameter = 2.0,
|
182
189
|
reorient = True,
|
183
190
|
extend_with_flange = extend_fault_representation,
|
191
|
+
flange_inner_ring = flange_inner_ring,
|
192
|
+
saucer_parameter = saucer_parameter,
|
184
193
|
flange_radial_distance = flange_radius,
|
185
194
|
make_clockwise = False)
|
186
195
|
del pset
|
@@ -345,13 +354,13 @@ def find_faces_to_represent_surface_regular_wrapper(
|
|
345
354
|
raise ValueError(f'unrecognised property name {p_name}')
|
346
355
|
if property_collection.number_of_imports() > 0:
|
347
356
|
# log.debug('writing gcs property hdf5 data')
|
348
|
-
property_collection.write_hdf5_for_imported_list()
|
357
|
+
property_collection.write_hdf5_for_imported_list(use_pack = use_pack)
|
349
358
|
uuids_properties = property_collection.create_xml_for_imported_list_and_add_parts_to_model(
|
350
359
|
find_local_property_kinds = True)
|
351
360
|
uuid_list.extend(uuids_properties)
|
352
361
|
if grid_pc is not None and grid_pc.number_of_imports() > 0:
|
353
362
|
# log.debug('writing grid property (bisector) hdf5 data')
|
354
|
-
grid_pc.write_hdf5_for_imported_list()
|
363
|
+
grid_pc.write_hdf5_for_imported_list(use_pack = use_pack)
|
355
364
|
# log.debug('creating xml for grid property (bisector)')
|
356
365
|
uuids_properties = grid_pc.create_xml_for_imported_list_and_add_parts_to_model(
|
357
366
|
find_local_property_kinds = True)
|
resqpy/olio/intersection.py
CHANGED
@@ -76,7 +76,7 @@ def line_triangle_intersect(line_p, line_v, triangle, line_segment = False, l_to
|
|
76
76
|
return line_p + t * line_v
|
77
77
|
|
78
78
|
|
79
|
-
@njit
|
79
|
+
@njit # pragma: no cover
|
80
80
|
def line_triangle_intersect_numba(
|
81
81
|
line_p: np.ndarray,
|
82
82
|
line_v: np.ndarray,
|
@@ -164,8 +164,7 @@ def line_triangles_intersects(line_p, line_v, triangles, line_segment = False):
|
|
164
164
|
ts[:] = np.where(np.logical_or(ts < 0.0, ts > 1.0), np.nan, ts)
|
165
165
|
np.divide(np.sum(np.cross(p02s, line_rv) * lp_t0s, axis = 1), denoms, out = us, where = nz)
|
166
166
|
np.divide(np.sum(np.cross(line_rv, p01s) * lp_t0s, axis = 1), denoms, out = vs, where = nz)
|
167
|
-
ts[
|
168
|
-
ts)
|
167
|
+
ts[np.where(np.logical_or(np.logical_or(us < 0.0, us > 1.0), np.logical_or(vs < 0.0, us + vs > 1.0)))] = np.nan
|
169
168
|
|
170
169
|
intersects = np.empty((n, 3))
|
171
170
|
intersects[:] = line_v * np.repeat(ts, 3).reshape((n, 3)) + line_p
|
resqpy/olio/read_nexus_fault.py
CHANGED
@@ -13,8 +13,8 @@ import numpy as np
|
|
13
13
|
import pandas as pd
|
14
14
|
|
15
15
|
|
16
|
-
def
|
17
|
-
"""Reads a Nexus (!) format file containing one or more MULT keywords and returns a dataframe with the MULT rows."""
|
16
|
+
def load_nexus_fault_mult_table_from_list(file_as_list):
|
17
|
+
"""Reads a Nexus (!) format list of file contents containing one or more MULT keywords and returns a dataframe with the MULT rows."""
|
18
18
|
|
19
19
|
def is_number(s):
|
20
20
|
try:
|
@@ -41,70 +41,18 @@ def load_nexus_fault_mult_table(file_name):
|
|
41
41
|
|
42
42
|
face_dict = {'TX': 'I', 'TY': 'J', 'TZ': 'K', 'TI': 'I', 'TJ': 'J', 'TK': 'K'}
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# line = line.partition('C')[0] # removing trailing comments
|
52
|
-
tokens = line.split()
|
53
|
-
if ISTABLE:
|
54
|
-
if is_number(tokens[0]):
|
55
|
-
ISRECORD = True
|
56
|
-
|
57
|
-
if ISRECORD and (not is_number(tokens[0])):
|
58
|
-
data = chunks[0:]
|
59
|
-
d_elems = np.array([np.array(data[i].split()) for i in range(len(data))])
|
60
|
-
# fill empty elements with zero
|
61
|
-
lens = np.array([len(i) for i in d_elems])
|
62
|
-
# Mask of valid places in each row
|
63
|
-
mask = np.arange(lens.max()) < lens[:, None]
|
64
|
-
# Setup output array and put elements from data into masked positions
|
65
|
-
outdata = np.zeros(mask.shape, dtype = d_elems.dtype)
|
66
|
-
outdata[mask] = np.concatenate(d_elems)
|
67
|
-
df = pd.DataFrame(outdata)
|
68
|
-
for column in df.columns:
|
69
|
-
df[column] = pd.to_numeric(df[column], errors = 'ignore')
|
70
|
-
df.columns = ['i1', 'i2', 'j1', 'j2', 'k1', 'k2', 'mult']
|
71
|
-
df['grid'] = grid
|
72
|
-
df['name'] = name
|
73
|
-
df['face'] = face
|
74
|
-
dfs.append(df)
|
75
|
-
num_tables += 1
|
76
|
-
|
77
|
-
ISTABLE = False
|
78
|
-
ISRECORD = False
|
79
|
-
chunks = []
|
80
|
-
|
81
|
-
if ISTABLE:
|
82
|
-
if re.match("(.*)GRID(.*)", tokens[0]):
|
83
|
-
if len(tokens) > 0:
|
84
|
-
grid = tokens[1]
|
85
|
-
elif re.match("(.*)FNAME(.*)", tokens[0]):
|
86
|
-
if len(tokens) > 0:
|
87
|
-
name = tokens[1]
|
88
|
-
else:
|
89
|
-
if re.match(r"^MULT$", tokens[0]):
|
90
|
-
ISTABLE = False
|
91
|
-
ISRECORD = False
|
92
|
-
chunks = []
|
93
|
-
else:
|
94
|
-
chunks.append(line.strip())
|
95
|
-
|
96
|
-
if re.match(r"^MULT$", tokens[0]):
|
97
|
-
if len(tokens) > 0:
|
98
|
-
face = face_dict[tokens[1]]
|
99
|
-
if 'MINUS' in tokens:
|
100
|
-
face += '-' # indicates data apply to 'negative' faces of specified cells
|
101
|
-
grid = 'ROOT' # nexus default
|
102
|
-
name = 'NONE'
|
103
|
-
ISTABLE = True
|
104
|
-
|
105
|
-
else:
|
44
|
+
chunks = []
|
45
|
+
for line in file_as_list:
|
46
|
+
if len(line.strip()):
|
47
|
+
if (not line.strip()[0] == '!') & (not line.strip()[0] == 'C'):
|
48
|
+
line = line.partition('!')[0] # removing trailing comments
|
49
|
+
# line = line.partition('C')[0] # removing trailing comments
|
50
|
+
tokens = line.split()
|
106
51
|
if ISTABLE:
|
107
|
-
if
|
52
|
+
if is_number(tokens[0]):
|
53
|
+
ISRECORD = True
|
54
|
+
|
55
|
+
if ISRECORD and (not is_number(tokens[0])):
|
108
56
|
data = chunks[0:]
|
109
57
|
d_elems = np.array([np.array(data[i].split()) for i in range(len(data))])
|
110
58
|
# fill empty elements with zero
|
@@ -128,6 +76,59 @@ def load_nexus_fault_mult_table(file_name):
|
|
128
76
|
ISRECORD = False
|
129
77
|
chunks = []
|
130
78
|
|
79
|
+
if ISTABLE:
|
80
|
+
if re.match("(.*)GRID(.*)", tokens[0]):
|
81
|
+
if len(tokens) > 0:
|
82
|
+
grid = tokens[1]
|
83
|
+
elif re.match("(.*)FNAME(.*)", tokens[0]):
|
84
|
+
if len(tokens) > 0:
|
85
|
+
name = tokens[1]
|
86
|
+
else:
|
87
|
+
if re.match(r"^MULT$", tokens[0]):
|
88
|
+
ISTABLE = False
|
89
|
+
ISRECORD = False
|
90
|
+
chunks = []
|
91
|
+
else:
|
92
|
+
chunks.append(line.strip())
|
93
|
+
|
94
|
+
if re.match(r"^MULT$", tokens[0]):
|
95
|
+
if len(tokens) > 0:
|
96
|
+
face = face_dict[tokens[1]]
|
97
|
+
if 'MINUS' in tokens:
|
98
|
+
face += '-' # indicates data apply to 'negative' faces of specified cells
|
99
|
+
grid = 'ROOT' # nexus default
|
100
|
+
name = 'NONE'
|
101
|
+
ISTABLE = True
|
102
|
+
|
103
|
+
else:
|
104
|
+
if ISTABLE:
|
105
|
+
if ISRECORD:
|
106
|
+
data = chunks[0:]
|
107
|
+
d_elems = np.array([np.array(data[i].split()) for i in range(len(data))])
|
108
|
+
# fill empty elements with zero
|
109
|
+
lens = np.array([len(i) for i in d_elems])
|
110
|
+
# Mask of valid places in each row
|
111
|
+
mask = np.arange(lens.max()) < lens[:, None]
|
112
|
+
# Setup output array and put elements from data into masked positions
|
113
|
+
outdata = np.zeros(mask.shape, dtype = d_elems.dtype)
|
114
|
+
outdata[mask] = np.concatenate(d_elems)
|
115
|
+
df = pd.DataFrame(outdata)
|
116
|
+
for column in df.columns:
|
117
|
+
df[column] = pd.to_numeric(df[column], errors = 'ignore')
|
118
|
+
df.columns = ['i1', 'i2', 'j1', 'j2', 'k1', 'k2', 'mult']
|
119
|
+
df['grid'] = grid
|
120
|
+
df['name'] = name
|
121
|
+
df['face'] = face
|
122
|
+
dfs.append(df)
|
123
|
+
num_tables += 1
|
124
|
+
|
125
|
+
ISTABLE = False
|
126
|
+
ISRECORD = False
|
127
|
+
chunks = []
|
128
|
+
|
129
|
+
if not dfs:
|
130
|
+
return pd.DataFrame()
|
131
|
+
|
131
132
|
fault_df = pd.concat(dfs).reset_index(drop = True)
|
132
133
|
|
133
134
|
convert_dict = {'i1': int, 'i2': int, 'j1': int, 'j2': int, 'k1': int, 'k2': int, 'mult': float}
|
@@ -136,7 +137,10 @@ def load_nexus_fault_mult_table(file_name):
|
|
136
137
|
return fault_df
|
137
138
|
|
138
139
|
|
139
|
-
def
|
140
|
+
def load_nexus_fault_mult_table(file_name):
|
140
141
|
"""Reads a Nexus (!) format file containing one or more MULT keywords and returns a dataframe with the MULT rows."""
|
141
142
|
|
142
|
-
|
143
|
+
with open(file_name) as f:
|
144
|
+
file_as_list = f.readlines()
|
145
|
+
|
146
|
+
return load_nexus_fault_mult_table_from_list(file_as_list)
|