Skip to content

Commit

Permalink
Merge pull request #736 from bp/polyline_properties
Browse files Browse the repository at this point in the history
adding support for properties on Polyline, PolylineSet, and PointSet
  • Loading branch information
andy-beer committed Jun 28, 2023
2 parents 64faed0 + 90e6745 commit 525090c
Show file tree
Hide file tree
Showing 7 changed files with 483 additions and 11 deletions.
23 changes: 23 additions & 0 deletions resqpy/lines/_polyline.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import resqpy.olio.intersection as meet
import resqpy.lines
import resqpy.property as rqp
import resqpy.olio.point_inclusion as pip
import resqpy.olio.uuid as bu
import resqpy.olio.vector_utilities as vu
Expand Down Expand Up @@ -74,6 +75,7 @@ def __init__(
self.coordinates = None
self.centre = None
self.rep_int_root = rep_int_root # Optional represented interpretation xml root node
self.property_collection = None
super().__init__(model = parent_model,
uuid = uuid,
title = title,
Expand Down Expand Up @@ -817,6 +819,27 @@ def area(self):
a = d_xy[0] * d_xy[1] * float(np.count_nonzero(inside)) / float(inside.size)
return a

def extract_property_collection(self, refresh = False):
"""Returns a property collection for the polyline.
arguments:
refresh (bool, default False): if True, the property collection is refreshed
from the current state of the model; if False and the property collection
has already been cached for the polyline, then the cached copy is used
returns:
a PropertyCollection holding those properties in the model where this polyline
is the supporting representation
notes:
polyline properties may have indexable element of 'nodes' or 'intervals';
for interval properties, the number of elements depends on whether the
polyline is closed or not
"""
if self.property_collection is None or refresh:
self.property_collection = rqp.PropertyCollection(support = self)
return self.property_collection

def create_xml(self,
ext_uuid = None,
add_as_part = True,
Expand Down
28 changes: 27 additions & 1 deletion resqpy/lines/_polyline_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import resqpy.crs as rcrs
import resqpy.lines
import resqpy.property as rqp
import resqpy.olio.simple_lines as rsl
import resqpy.olio.uuid as bu
import resqpy.olio.vector_utilities as vu
Expand Down Expand Up @@ -71,6 +72,7 @@ def __init__(self,
self.closed_array = None
self.crs_uuid = crs_uuid
self.indices = None
self.property_collection = None

super().__init__(model = parent_model,
uuid = uuid,
Expand Down Expand Up @@ -99,7 +101,7 @@ def __init__(self,
self.title = f"{polylines[0].title} + {len(polylines)-1} polylines"
else:
self.title = polylines[0].title

self.combine_polylines(polylines)
elif irap_file is not None: # Create from an input IRAP file
if crs_uuid is None:
log.warning(f'applying generic model CRS when loading polylines from IRAP file: {irap_file}')
Expand Down Expand Up @@ -603,3 +605,27 @@ def convert_to_charisma(self, file_name):
with open(file_name, 'w') as f:
for item in lines:
f.write(item)

def extract_property_collection(self, refresh = False):
"""Returns a property collection for the polyline set.
arguments:
refresh (bool, default False): if True, the property collection is refreshed
from the current state of the model; if False and the property collection
has already been cached for the polyline set, then the cached copy is used
returns:
a PropertyCollection holding those properties in the model where this polyline set
is the supporting representation
notes:
polyline set properties may have indexable element of 'nodes' or 'intervals';
for interval properties, the number of elements per polyline depends on whether the
polyline is closed or not;
it is currently left to calling code build the composite property array from data
for individual polylines, or to find the subset of the combined property data that
is applicable to an individual polyline
"""
if self.property_collection is None or refresh:
self.property_collection = rqp.PropertyCollection(support = self)
return self.property_collection
52 changes: 48 additions & 4 deletions resqpy/property/_collection_get_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ def _normalized_part_array_fix_zero_at(min_value, max_value, n_prop, fix_zero_at


def _supporting_shape_grid(support, indexable_element, direction):
shape_list = None
if indexable_element is None or indexable_element == 'cells':
shape_list = [support.nk, support.nj, support.ni]
elif indexable_element == 'columns':
Expand Down Expand Up @@ -442,6 +443,7 @@ def _supporting_shape_grid_faces(direction, support):


def _supporting_shape_grid_nodes(support):
shape_list = None
assert not support.k_gaps, 'indexable element of nodes not currently supported for grids with K gaps'
if support.has_split_coordinate_lines:
pillar_count = (support.nj + 1) * (support.ni + 1) + support.split_pillars_count
Expand All @@ -452,6 +454,7 @@ def _supporting_shape_grid_nodes(support):


def _supporting_shape_wellboreframe(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'nodes':
shape_list = [support.node_count]
elif indexable_element == 'intervals':
Expand All @@ -460,6 +463,7 @@ def _supporting_shape_wellboreframe(support, indexable_element):


def _supporting_shape_wellboremarkerframe(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'nodes':
shape_list = [support.node_count]
elif indexable_element == 'intervals':
Expand All @@ -468,6 +472,7 @@ def _supporting_shape_wellboremarkerframe(support, indexable_element):


def _supporting_shape_blockedwell(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'intervals':
shape_list = [support.node_count - 1] # all intervals, including unblocked
elif indexable_element == 'nodes':
Expand All @@ -478,6 +483,7 @@ def _supporting_shape_blockedwell(support, indexable_element):


def _supporting_shape_mesh(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'cells' or indexable_element == 'columns':
shape_list = [support.nj - 1, support.ni - 1]
elif indexable_element == 'nodes':
Expand All @@ -486,19 +492,50 @@ def _supporting_shape_mesh(support, indexable_element):


def _supporting_shape_surface(support, indexable_element):
if indexable_element is None or indexable_element == 'faces':
shape_list = None
if indexable_element is None or indexable_element in ['triangles', 'faces']:
shape_list = [support.triangle_count()]
elif indexable_element == 'nodes':
shape_list = [support.node_count()]
return shape_list


def _supporting_shape_gridconnectionset(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'faces':
shape_list = [support.count]
return shape_list


def _supporting_shape_polyline(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'intervals':
shape_list = [len(support.coordinates) - (0 if support.isclosed else 1)]
elif indexable_element == 'nodes':
shape_list = [len(support.coordinates)]
return shape_list


def _supporting_shape_polylineset(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'intervals':
if support.boolnotconstant:
reduction = len(support.closed_array) - np.count_nonzero(support.closed_array)
else:
reduction = 0 if support.boolvalue else len(support.closed_array)
shape_list = [len(support.coordinates) - reduction]
elif indexable_element == 'nodes':
shape_list = [len(support.coordinates)]
return shape_list


def _supporting_shape_pointset(support, indexable_element):
shape_list = None
if indexable_element is None or indexable_element == 'nodes':
shape_list = [len(support.full_array_ref())]
return shape_list


def _supporting_shape_other(support, indexable_element):
if indexable_element is None or indexable_element == 'cells':
shape_list = [support.cell_count]
Expand All @@ -519,16 +556,23 @@ def _realizations_array_ref_get_r_extent(fill_missing, r_list):


def _get_indexable_element(indexable_element, support_type):
# returns a default indexable element depending on the type of supporting representation
if indexable_element is None:
if support_type in [
'obj_IjkGridRepresentation', 'obj_BlockedWellboreRepresentation', 'obj_Grid2dRepresentation',
'obj_UnstructuredGridRepresentation'
]:
indexable_element = 'cells'
elif support_type in ['obj_WellboreFrameRepresentation', 'obj_WellboreMarkerFrameRepresentation']:
indexable_element = 'nodes' # note: could be 'intervals'
elif support_type in ['obj_GridConnectionSetRepresentation', 'obj_TriangulatedSetRepresentation']:
elif support_type in [
'obj_WellboreFrameRepresentation', 'obj_WellboreMarkerFrameRepresentation', 'obj_PointSetRepresentation'
]:
indexable_element = 'nodes' # note: could be 'intervals' (except for PointSet properties)
elif support_type == 'obj_GridConnectionSetRepresentation':
indexable_element = 'faces'
elif support_type == 'obj_TriangulatedSetRepresentation':
indexable_element = 'triangles'
elif support_type in ['obj_PolylineRepresentation', 'obj_PolylineSetRepresentation']:
indexable_element = 'intervals' # could also be 'nodes'
else:
raise Exception('indexable element unknown for unsupported supporting representation object')
return indexable_element
Expand Down
10 changes: 9 additions & 1 deletion resqpy/property/_collection_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def _set_support_uuid_notnone(collection, support, support_uuid, model, modify_p
import resqpy.surface as rqs
import resqpy.unstructured as rug
import resqpy.well as rqw
import resqpy.lines as rql

assert model is not None, 'model not established when setting support for property collection'
if collection.support_uuid is not None and not bu.matching_uuids(support_uuid, collection.support_uuid):
Expand All @@ -46,7 +47,7 @@ def _set_support_uuid_notnone(collection, support, support_uuid, model, modify_p
if type(collection.support) in [
grr.Grid, grr.RegularGrid, rqw.WellboreFrame, rqw.BlockedWell, rqs.Mesh, rqf.GridConnectionSet,
rug.UnstructuredGrid, rug.HexaGrid, rug.TetraGrid, rug.PrismGrid, rug.VerticalPrismGrid,
rug.PyramidGrid, rqw.WellboreMarkerFrame, rqs.Surface
rug.PyramidGrid, rqw.WellboreMarkerFrame, rqs.Surface, rql.Polyline, rql.PolylineSet, rqs.PointSet
]:
collection.support_root = collection.support.root
else:
Expand All @@ -65,6 +66,7 @@ def _set_support_uuid_notnone_supportnone(collection, support_uuid, model):
import resqpy.surface as rqs
import resqpy.unstructured as rug
import resqpy.well as rqw
import resqpy.lines as rql

support_part = model.part_for_uuid(support_uuid)
assert support_part is not None, 'supporting representation part missing in model'
Expand All @@ -90,6 +92,12 @@ def _set_support_uuid_notnone_supportnone(collection, support_uuid, model):
find_properties = False)
elif support_type == 'obj_WellboreMarkerFrameRepresentation':
collection.support = rqw.WellboreMarkerFrame(model, uuid = collection.support_uuid)
elif support_type == 'obj_PolylineRepresentation':
collection.support = rql.Polyline(model, uuid = collection.support_uuid)
elif support_type == 'obj_PolylineSetRepresentation':
collection.support = rql.PolylineSet(model, uuid = collection.support_uuid)
elif support_type == 'obj_PointSetRepresentation':
collection.support = rqs.PointSet(model, uuid = collection.support_uuid)
else:
raise TypeError('unsupported property supporting representation class: ' + str(support_type))

Expand Down
15 changes: 12 additions & 3 deletions resqpy/property/property_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def supporting_shape(self, indexable_element = None, direction = None):
import resqpy.surface as rqs
import resqpy.unstructured as rug
import resqpy.well as rqw
import resqpy.lines as rql

support = self.support

Expand All @@ -184,14 +185,22 @@ def supporting_shape(self, indexable_element = None, direction = None):
elif isinstance(support, rqs.Surface):
shape_list = pcga._supporting_shape_surface(support, indexable_element)

elif type(support) in [
rug.UnstructuredGrid, rug.HexaGrid, rug.TetraGrid, rug.PrismGrid, rug.VerticalPrismGrid, rug.PyramidGrid
]:
elif type(support) in \
[rug.UnstructuredGrid, rug.HexaGrid, rug.TetraGrid, rug.PrismGrid, rug.VerticalPrismGrid, rug.PyramidGrid]:
shape_list, support = pcga._supporting_shape_other(support, indexable_element)

elif isinstance(support, rqw.WellboreMarkerFrame):
shape_list = pcga._supporting_shape_wellboremarkerframe(support, indexable_element)

elif isinstance(support, rql.Polyline):
shape_list = pcga._supporting_shape_polyline(support, indexable_element)

elif isinstance(support, rql.PolylineSet):
shape_list = pcga._supporting_shape_polylineset(support, indexable_element)

elif isinstance(support, rqs.PointSet):
shape_list = pcga._supporting_shape_pointset(support, indexable_element)

else:
raise Exception(f'unsupported support class {type(support)} for property')

Expand Down
21 changes: 21 additions & 0 deletions resqpy/surface/_pointset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import resqpy.olio.vector_utilities as vec
import resqpy.olio.write_hdf5 as rwh5
import resqpy.olio.xml_et as rqet
import resqpy.property as rqp
import resqpy.surface
import resqpy.surface._base_surface as rqsb
from resqpy.olio.xml_namespaces import curly_namespace as ns
Expand Down Expand Up @@ -78,6 +79,7 @@ def __init__(self,
self.patch_array_list = [] # ordered list of numpy float arrays (or None before loading), each of shape (N, 3)
self.full_array = None # composite points (all patches)
self.represented_interpretation_root = None
self.property_collection = None
super().__init__(model = parent_model,
uuid = uuid,
title = title,
Expand Down Expand Up @@ -402,6 +404,25 @@ def add_patch(self, points_array):
self.patch_count = 0
self.patch_count += 1

def extract_property_collection(self, refresh = False):
"""Returns a property collection for the point set.
arguments:
refresh (bool, default False): if True, the property collection is refreshed
from the current state of the model; if False and the property collection
has already been cached for the point set, then the cached copy is used
returns:
a PropertyCollection holding those properties in the model where this point set
is the supporting representation
note:
point set properties have indexable element of 'nodes'
"""
if self.property_collection is None or refresh:
self.property_collection = rqp.PropertyCollection(support = self)
return self.property_collection

def write_hdf5(self, file_name = None, mode = 'a'):
"""Create or append to an hdf5 file, writing datasets for the point set patches after caching arrays.
Expand Down
Loading

0 comments on commit 525090c

Please sign in to comment.