resqpy 4.14.2__py3-none-any.whl → 5.1.5__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/_gcs_functions.py +10 -10
- resqpy/fault/_grid_connection_set.py +277 -113
- resqpy/grid/__init__.py +2 -3
- resqpy/grid/_defined_geometry.py +3 -3
- resqpy/grid/_extract_functions.py +2 -1
- resqpy/grid/_grid.py +95 -12
- resqpy/grid/_grid_types.py +22 -7
- resqpy/grid/_points_functions.py +1 -1
- resqpy/grid/_regular_grid.py +6 -2
- resqpy/grid_surface/__init__.py +17 -38
- resqpy/grid_surface/_blocked_well_populate.py +5 -5
- resqpy/grid_surface/_find_faces.py +1349 -253
- resqpy/lines/_polyline.py +24 -33
- resqpy/model/_catalogue.py +9 -0
- resqpy/model/_forestry.py +18 -14
- resqpy/model/_hdf5.py +11 -3
- resqpy/model/_model.py +85 -10
- resqpy/model/_xml.py +38 -13
- resqpy/multi_processing/wrappers/grid_surface_mp.py +92 -37
- resqpy/olio/read_nexus_fault.py +8 -2
- resqpy/olio/relperm.py +1 -1
- resqpy/olio/transmission.py +8 -8
- resqpy/olio/triangulation.py +36 -30
- resqpy/olio/vector_utilities.py +340 -6
- resqpy/olio/volume.py +0 -20
- resqpy/olio/wellspec_keywords.py +19 -13
- resqpy/olio/write_hdf5.py +1 -1
- resqpy/olio/xml_et.py +12 -0
- resqpy/property/__init__.py +6 -4
- resqpy/property/_collection_add_part.py +4 -3
- resqpy/property/_collection_create_xml.py +4 -2
- resqpy/property/_collection_get_attributes.py +4 -0
- resqpy/property/attribute_property_set.py +311 -0
- resqpy/property/grid_property_collection.py +11 -11
- resqpy/property/property_collection.py +79 -31
- resqpy/property/property_common.py +3 -8
- resqpy/rq_import/_add_surfaces.py +34 -14
- resqpy/rq_import/_grid_from_cp.py +2 -2
- resqpy/rq_import/_import_nexus.py +75 -48
- resqpy/rq_import/_import_vdb_all_grids.py +64 -52
- resqpy/rq_import/_import_vdb_ensemble.py +12 -13
- resqpy/surface/_mesh.py +4 -0
- resqpy/surface/_surface.py +593 -118
- resqpy/surface/_tri_mesh.py +13 -10
- resqpy/surface/_tri_mesh_stencil.py +4 -4
- resqpy/surface/_triangulated_patch.py +71 -51
- resqpy/time_series/_any_time_series.py +7 -4
- resqpy/time_series/_geologic_time_series.py +1 -1
- resqpy/unstructured/_hexa_grid.py +6 -2
- resqpy/unstructured/_prism_grid.py +13 -5
- resqpy/unstructured/_pyramid_grid.py +6 -2
- resqpy/unstructured/_tetra_grid.py +6 -2
- resqpy/unstructured/_unstructured_grid.py +6 -2
- resqpy/well/_blocked_well.py +1986 -1946
- resqpy/well/_deviation_survey.py +3 -3
- resqpy/well/_md_datum.py +11 -21
- resqpy/well/_trajectory.py +10 -5
- resqpy/well/_wellbore_frame.py +10 -2
- resqpy/well/blocked_well_frame.py +3 -3
- resqpy/well/well_object_funcs.py +7 -9
- resqpy/well/well_utils.py +33 -0
- {resqpy-4.14.2.dist-info → resqpy-5.1.5.dist-info}/METADATA +8 -9
- {resqpy-4.14.2.dist-info → resqpy-5.1.5.dist-info}/RECORD +66 -66
- {resqpy-4.14.2.dist-info → resqpy-5.1.5.dist-info}/WHEEL +1 -1
- resqpy/grid/_moved_functions.py +0 -15
- {resqpy-4.14.2.dist-info → resqpy-5.1.5.dist-info}/LICENSE +0 -0
| @@ -85,10 +85,10 @@ class PropertyCollection(): | |
| 85 85 | 
             
                    self.realization = realization  # model realization number within an ensemble
         | 
| 86 86 | 
             
                    self.null_value = None
         | 
| 87 87 | 
             
                    self.imported_list = []
         | 
| 88 | 
            -
                    # above is list of (uuid,  | 
| 88 | 
            +
                    # above is list of (uuid, source, keyword, cached_name, discrete, uom, time_index, null_value,
         | 
| 89 89 | 
             
                    #                   min_value, max_value, property_kind, facet_type, facet, realization,
         | 
| 90 90 | 
             
                    #                   indexable_element, count, local_property_kind_uuid, const_value, points,
         | 
| 91 | 
            -
                    #                   time_series_uuid, string_lookup_uuid)
         | 
| 91 | 
            +
                    #                   time_series_uuid, string_lookup_uuid, pre_packed)
         | 
| 92 92 | 
             
                    self.guess_warning = False
         | 
| 93 93 | 
             
                    if support is not None:
         | 
| 94 94 | 
             
                        self.model = support.model
         | 
| @@ -141,20 +141,28 @@ class PropertyCollection(): | |
| 141 141 | 
             
                    else:
         | 
| 142 142 | 
             
                        pcs._set_support_uuid_notnone(self, support, support_uuid, model, modify_parts)
         | 
| 143 143 |  | 
| 144 | 
            -
                def supporting_shape(self, | 
| 144 | 
            +
                def supporting_shape(self,
         | 
| 145 | 
            +
                                     indexable_element = None,
         | 
| 146 | 
            +
                                     direction = None,
         | 
| 147 | 
            +
                                     count = 1,
         | 
| 148 | 
            +
                                     points = False,
         | 
| 149 | 
            +
                                     pre_packed = False):
         | 
| 145 150 | 
             
                    """Return the shape of the supporting representation with respect to the given indexable element
         | 
| 146 151 |  | 
| 147 152 | 
             
                    arguments:
         | 
| 148 153 | 
             
                       indexable_element (string, optional): if None, a hard-coded default depending on the supporting representation class
         | 
| 149 154 | 
             
                          will be used
         | 
| 150 155 | 
             
                       direction (string, optional): must be passed if required for the combination of support class and indexable element;
         | 
| 151 | 
            -
                          currently only used for Grid faces | 
| 156 | 
            +
                          currently only used for Grid faces
         | 
| 157 | 
            +
                       count (int, default 1): the count parameter for the property
         | 
| 158 | 
            +
                       points (bool, default False): set True if the property is a points property
         | 
| 159 | 
            +
                       pre_packed (bool, default False): set True if the required shape is for a pre-packed boolean property
         | 
| 152 160 |  | 
| 153 161 | 
             
                    returns:
         | 
| 154 162 | 
             
                       list of int, being required shape of numpy array, or None if not coded for
         | 
| 155 163 |  | 
| 156 164 | 
             
                    note:
         | 
| 157 | 
            -
                       individual property arrays will only match this shape if they have the same indexable element and  | 
| 165 | 
            +
                       individual property arrays will only match this shape if they have the same indexable element and matching count etc.
         | 
| 158 166 | 
             
                    """
         | 
| 159 167 |  | 
| 160 168 | 
             
                    # when at global level was causing circular reference loading issues as grid imports this module
         | 
| @@ -204,6 +212,14 @@ class PropertyCollection(): | |
| 204 212 | 
             
                    else:
         | 
| 205 213 | 
             
                        raise Exception(f'unsupported support class {type(support)} for property')
         | 
| 206 214 |  | 
| 215 | 
            +
                    if pre_packed:
         | 
| 216 | 
            +
                        shape_list[-1] = (shape_list[-1] - 1) // 8 + 1
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                    if shape_list is not None:
         | 
| 219 | 
            +
                        if count > 1:
         | 
| 220 | 
            +
                            shape_list.append(count)
         | 
| 221 | 
            +
                        if points:
         | 
| 222 | 
            +
                            shape_list.append(3)
         | 
| 207 223 | 
             
                    return shape_list
         | 
| 208 224 |  | 
| 209 225 | 
             
                def populate_from_property_set(self, property_set_root):
         | 
| @@ -404,7 +420,8 @@ class PropertyCollection(): | |
| 404 420 | 
             
                      call this method once for each group of differently sized properties; for very large collections
         | 
| 405 421 | 
             
                      it might also be necessary to divide the work into smaller groups to reduce memory usage;
         | 
| 406 422 | 
             
                      this method does not write to hdf5 nor create xml – use the usual methods for further processing
         | 
| 407 | 
            -
                      of the imported list
         | 
| 423 | 
            +
                      of the imported list;
         | 
| 424 | 
            +
                      does not currently support packed arrays
         | 
| 408 425 | 
             
                    """
         | 
| 409 426 |  | 
| 410 427 | 
             
                    source = 'sampled'
         | 
| @@ -743,7 +760,8 @@ class PropertyCollection(): | |
| 743 760 | 
             
                              title = None,
         | 
| 744 761 | 
             
                              title_mode = None,
         | 
| 745 762 | 
             
                              related_uuid = None,
         | 
| 746 | 
            -
                              const_value = None | 
| 763 | 
            +
                              const_value = None,
         | 
| 764 | 
            +
                              extra = None):
         | 
| 747 765 | 
             
                    """Returns a single part selected by those arguments which are not None.
         | 
| 748 766 |  | 
| 749 767 | 
             
                       multiple_handling (string, default 'exception'): one of 'exception', 'none', 'first', 'oldest', 'newest'
         | 
| @@ -782,7 +800,8 @@ class PropertyCollection(): | |
| 782 800 | 
             
                                                                            title = title,
         | 
| 783 801 | 
             
                                                                            title_mode = title_mode,
         | 
| 784 802 | 
             
                                                                            related_uuid = related_uuid,
         | 
| 785 | 
            -
                                                                            const_value = const_value | 
| 803 | 
            +
                                                                            const_value = const_value,
         | 
| 804 | 
            +
                                                                            extra = extra)
         | 
| 786 805 | 
             
                    parts_list = temp_collection.parts()
         | 
| 787 806 | 
             
                    if len(parts_list) == 0:
         | 
| 788 807 | 
             
                        return None
         | 
| @@ -815,7 +834,8 @@ class PropertyCollection(): | |
| 815 834 | 
             
                                     title = None,
         | 
| 816 835 | 
             
                                     title_mode = None,
         | 
| 817 836 | 
             
                                     related_uuid = None,
         | 
| 818 | 
            -
                                     use_pack = True | 
| 837 | 
            +
                                     use_pack = True,
         | 
| 838 | 
            +
                                     extra = None):
         | 
| 819 839 | 
             
                    """Returns the array of data for a single part selected by those arguments which are not None.
         | 
| 820 840 |  | 
| 821 841 | 
             
                    arguments:
         | 
| @@ -832,7 +852,7 @@ class PropertyCollection(): | |
| 832 852 |  | 
| 833 853 | 
             
                    Other optional arguments:
         | 
| 834 854 | 
             
                    realization, support_uuid, continuous, points, count, indexable, property_kind, facet_type, facet,
         | 
| 835 | 
            -
                    citation_title, time_series_uuid, time_index, uom, string_lookup_id, categorical, related_uuid:
         | 
| 855 | 
            +
                    citation_title, time_series_uuid, time_index, uom, string_lookup_id, categorical, related_uuid, extra:
         | 
| 836 856 |  | 
| 837 857 | 
             
                    For each of these arguments: if None, then all members of collection pass this filter;
         | 
| 838 858 | 
             
                    if not None then only those members with the given value pass this filter;
         | 
| @@ -868,7 +888,8 @@ class PropertyCollection(): | |
| 868 888 | 
             
                                          multiple_handling = multiple_handling,
         | 
| 869 889 | 
             
                                          title = title,
         | 
| 870 890 | 
             
                                          title_mode = title_mode,
         | 
| 871 | 
            -
                                          related_uuid = related_uuid | 
| 891 | 
            +
                                          related_uuid = related_uuid,
         | 
| 892 | 
            +
                                          extra = extra)
         | 
| 872 893 | 
             
                    if part is None:
         | 
| 873 894 | 
             
                        return None
         | 
| 874 895 | 
             
                    return self.cached_part_array_ref(part,
         | 
| @@ -1085,17 +1106,17 @@ class PropertyCollection(): | |
| 1085 1106 | 
             
                    return meta
         | 
| 1086 1107 |  | 
| 1087 1108 | 
             
                def null_value_for_part(self, part):
         | 
| 1088 | 
            -
                    """Returns the null value for the (discrete) property part; np. | 
| 1109 | 
            +
                    """Returns the null value for the (discrete) property part; np.nan for continuous parts.
         | 
| 1089 1110 |  | 
| 1090 1111 | 
             
                    arguments:
         | 
| 1091 1112 | 
             
                       part (string): the part name for which the null value is required
         | 
| 1092 1113 |  | 
| 1093 1114 | 
             
                    returns:
         | 
| 1094 | 
            -
                       int or np. | 
| 1115 | 
            +
                       int or np.nan
         | 
| 1095 1116 | 
             
                    """
         | 
| 1096 1117 |  | 
| 1097 1118 | 
             
                    if self.continuous_for_part(part):
         | 
| 1098 | 
            -
                        return np. | 
| 1119 | 
            +
                        return np.nan
         | 
| 1099 1120 | 
             
                    return self.element_for_part(part, 19)
         | 
| 1100 1121 |  | 
| 1101 1122 | 
             
                def continuous_for_part(self, part):
         | 
| @@ -1300,6 +1321,12 @@ class PropertyCollection(): | |
| 1300 1321 |  | 
| 1301 1322 | 
             
                    return [self.citation_title_for_part(p) for p in self.parts()]
         | 
| 1302 1323 |  | 
| 1324 | 
            +
                def source_for_part(self, part):
         | 
| 1325 | 
            +
                    """Returns the source string from the part's extra metadata, if present, else None."""
         | 
| 1326 | 
            +
             | 
| 1327 | 
            +
                    assert self.model is not None
         | 
| 1328 | 
            +
                    return self.model.source_for_part(part)
         | 
| 1329 | 
            +
             | 
| 1303 1330 | 
             
                def time_series_uuid_for_part(self, part):
         | 
| 1304 1331 | 
             
                    """If the property has an associated time series (is not static), returns the uuid for the time series.
         | 
| 1305 1332 |  | 
| @@ -1677,7 +1704,7 @@ class PropertyCollection(): | |
| 1677 1704 | 
             
                       exclude_inactive (boolean, default True): elements which are flagged as inactive in the supporting representation
         | 
| 1678 1705 | 
             
                          are masked out if this argument is True
         | 
| 1679 1706 | 
             
                       exclude_value (float or int, optional): if present, elements which match this value are masked out; if not None
         | 
| 1680 | 
            -
                          then usually set to np. | 
| 1707 | 
            +
                          then usually set to np.nan for continuous data or null_value_for_part() for discrete data
         | 
| 1681 1708 | 
             
                       points (boolean, default False): if True, the simple array is expected to have an extra dimension of extent 3,
         | 
| 1682 1709 | 
             
                          relative to the inactive attribute of the support
         | 
| 1683 1710 |  | 
| @@ -1765,7 +1792,7 @@ class PropertyCollection(): | |
| 1765 1792 | 
             
                       representation object with the attribute name 'inactive', to multiple properties (this will only work
         | 
| 1766 1793 | 
             
                       if the indexable element is set to the typical value for the class of supporting representation, eg.
         | 
| 1767 1794 | 
             
                       'cells' for grid objects); if exclude_null is set True then null value elements will also be masked out
         | 
| 1768 | 
            -
                       (as long as masked is True); however, it is recommended simply to use np. | 
| 1795 | 
            +
                       (as long as masked is True); however, it is recommended simply to use np.nan values in floating point
         | 
| 1769 1796 | 
             
                       property arrays if the commonality is not needed;
         | 
| 1770 1797 | 
             
                       set use_pack True if the hdf5 data may have been written with a similar setting
         | 
| 1771 1798 |  | 
| @@ -1783,8 +1810,10 @@ class PropertyCollection(): | |
| 1783 1810 | 
             
                    if masked:
         | 
| 1784 1811 | 
             
                        exclude_value = self.null_value_for_part(part) if exclude_null else None
         | 
| 1785 1812 | 
             
                        return self.masked_array(self.__dict__[cached_array_name], exclude_value = exclude_value)
         | 
| 1786 | 
            -
                     | 
| 1813 | 
            +
                    elif dtype is None:
         | 
| 1787 1814 | 
             
                        return self.__dict__[cached_array_name]
         | 
| 1815 | 
            +
                    else:
         | 
| 1816 | 
            +
                        return self.__dict__[cached_array_name].astype(dtype)
         | 
| 1788 1817 |  | 
| 1789 1818 | 
             
                def h5_slice(self, part, slice_tuple):
         | 
| 1790 1819 | 
             
                    """Returns a subset of the array for part, without loading the whole array.
         | 
| @@ -1860,7 +1889,7 @@ class PropertyCollection(): | |
| 1860 1889 | 
             
                        shape = self.supporting_shape(indexable_element = self.indexable_for_part(part),
         | 
| 1861 1890 | 
             
                                                      direction = pcga._part_direction(self, part))
         | 
| 1862 1891 | 
             
                        assert shape is not None
         | 
| 1863 | 
            -
                        return shape, (float if self.continuous_for_part(part) else int)
         | 
| 1892 | 
            +
                        return tuple(shape), (float if self.continuous_for_part(part) else int)
         | 
| 1864 1893 |  | 
| 1865 1894 | 
             
                    h5_key_pair = self._shape_and_type_of_part_get_h5keypair(part, part_node, model)
         | 
| 1866 1895 | 
             
                    if h5_key_pair is None:
         | 
| @@ -1942,7 +1971,7 @@ class PropertyCollection(): | |
| 1942 1971 | 
             
                          the maximum realization number present and slices for any missing realizations will be filled with fill_value;
         | 
| 1943 1972 | 
             
                          if False, the extent of the first axis will only cpver the number pf realizations actually present (see also notes)
         | 
| 1944 1973 | 
             
                       fill_value (int or float, optional): the value to use for missing realization slices; if None, will default to
         | 
| 1945 | 
            -
                          np. | 
| 1974 | 
            +
                          np.nan if data is continuous, -1 otherwise; irrelevant if fill_missing is False
         | 
| 1946 1975 | 
             
                       indexable_element (string, optional): the indexable element for the properties in the collection; if None, will
         | 
| 1947 1976 | 
             
                          be determined from the data
         | 
| 1948 1977 |  | 
| @@ -1962,7 +1991,7 @@ class PropertyCollection(): | |
| 1962 1991 | 
             
                    r_list, continuous = pcga._realizations_array_ref_initial_checks(self)
         | 
| 1963 1992 |  | 
| 1964 1993 | 
             
                    if fill_value is None:
         | 
| 1965 | 
            -
                        fill_value = np. | 
| 1994 | 
            +
                        fill_value = np.nan if continuous else -1
         | 
| 1966 1995 | 
             
                    if indexable_element is None:
         | 
| 1967 1996 | 
             
                        indexable_element = self.indexable_for_part(self.parts()[0])
         | 
| 1968 1997 |  | 
| @@ -1991,7 +2020,7 @@ class PropertyCollection(): | |
| 1991 2020 | 
             
                          the maximum time index present and slices for any missing indices will be filled with fill_value; if False,
         | 
| 1992 2021 | 
             
                          the extent of the first axis will only cpver the number pf time indices actually present (see also notes)
         | 
| 1993 2022 | 
             
                       fill_value (int or float, optional): the value to use for missing time index slices; if None, will default to
         | 
| 1994 | 
            -
                          np. | 
| 2023 | 
            +
                          np.nan if data is continuous, -1 otherwise; irrelevant if fill_missing is False
         | 
| 1995 2024 | 
             
                       indexable_element (string, optional): the indexable element for the properties in the collection; if None, will
         | 
| 1996 2025 | 
             
                          be determined from the data
         | 
| 1997 2026 |  | 
| @@ -2012,7 +2041,7 @@ class PropertyCollection(): | |
| 2012 2041 | 
             
                    ti_list, continuous = pcga._time_array_ref_initial_checks(self)
         | 
| 2013 2042 |  | 
| 2014 2043 | 
             
                    if fill_value is None:
         | 
| 2015 | 
            -
                        fill_value = np. | 
| 2044 | 
            +
                        fill_value = np.nan if continuous else -1
         | 
| 2016 2045 |  | 
| 2017 2046 | 
             
                    if indexable_element is None:
         | 
| 2018 2047 | 
             
                        indexable_element = self.indexable_for_part(self.parts()[0])
         | 
| @@ -2213,7 +2242,8 @@ class PropertyCollection(): | |
| 2213 2242 | 
             
                                                      const_value = None,
         | 
| 2214 2243 | 
             
                                                      points = False,
         | 
| 2215 2244 | 
             
                                                      time_series_uuid = None,
         | 
| 2216 | 
            -
                                                      string_lookup_uuid = None | 
| 2245 | 
            +
                                                      string_lookup_uuid = None,
         | 
| 2246 | 
            +
                                                      pre_packed = False):
         | 
| 2217 2247 | 
             
                    """Caches array and adds to the list of imported properties (but not to the collection dict).
         | 
| 2218 2248 |  | 
| 2219 2249 | 
             
                    arguments:
         | 
| @@ -2244,6 +2274,7 @@ class PropertyCollection(): | |
| 2244 2274 | 
             
                          be provided when writing hdf5 and creating xml for the imported list
         | 
| 2245 2275 | 
             
                       string_lookup_uuid (UUID, optional): should be provided for categorical properties, though can alternatively
         | 
| 2246 2276 | 
             
                          be specified when creating xml
         | 
| 2277 | 
            +
                       pre_packed (bool, default False): set to True if the property is boolean and the array is already packed
         | 
| 2247 2278 |  | 
| 2248 2279 | 
             
                    returns:
         | 
| 2249 2280 | 
             
                       uuid of nascent property object
         | 
| @@ -2265,6 +2296,7 @@ class PropertyCollection(): | |
| 2265 2296 | 
             
                    assert (cached_array is not None and const_value is None) or (cached_array is None and const_value is not None)
         | 
| 2266 2297 | 
             
                    assert not points or not discrete
         | 
| 2267 2298 | 
             
                    assert count > 0
         | 
| 2299 | 
            +
                    assert (not pre_packed) or ((cached_array is not None) and (cached_array.dtype == np.uint8))
         | 
| 2268 2300 | 
             
                    rqp_c.check_and_warn_property_kind(property_kind, 'adding property to imported list')
         | 
| 2269 2301 |  | 
| 2270 2302 | 
             
                    if self.imported_list is None:
         | 
| @@ -2273,16 +2305,25 @@ class PropertyCollection(): | |
| 2273 2305 | 
             
                    uuid = bu.new_uuid()
         | 
| 2274 2306 | 
             
                    cached_name = rqp_c._cache_name_for_uuid(uuid)
         | 
| 2275 2307 | 
             
                    if cached_array is not None:
         | 
| 2308 | 
            +
                        direction = facet if facet_type == 'direction' else None
         | 
| 2309 | 
            +
                        shape = self.supporting_shape(indexable_element = indexable_element,
         | 
| 2310 | 
            +
                                                      direction = direction,
         | 
| 2311 | 
            +
                                                      count = count,
         | 
| 2312 | 
            +
                                                      points = points,
         | 
| 2313 | 
            +
                                                      pre_packed = pre_packed)
         | 
| 2314 | 
            +
                        assert shape is not None, f'unsupported indexable element {indexable_element} for supporting representation'
         | 
| 2315 | 
            +
                        assert cached_array.shape == tuple(
         | 
| 2316 | 
            +
                            shape), f'property array has shape {cached_array.shape} when expecting {tuple(shape)}'
         | 
| 2276 2317 | 
             
                        min_value, max_value = pcga._min_max_of_cached_array(self, cached_name, cached_array, null_value, discrete)
         | 
| 2277 2318 | 
             
                    else:
         | 
| 2278 | 
            -
                        if const_value == null_value or (not discrete and np.isnan(const_value)):
         | 
| 2319 | 
            +
                        if const_value == null_value or isinstance(const_value, bool) or (not discrete and np.isnan(const_value)):
         | 
| 2279 2320 | 
             
                            min_value = max_value = None
         | 
| 2280 2321 | 
             
                        else:
         | 
| 2281 2322 | 
             
                            min_value = max_value = const_value
         | 
| 2282 2323 | 
             
                    self.imported_list.append(
         | 
| 2283 2324 | 
             
                        (uuid, source_info, keyword, cached_name, discrete, uom, time_index, null_value, min_value, max_value,
         | 
| 2284 2325 | 
             
                         property_kind, facet_type, facet, realization, indexable_element, count, local_property_kind_uuid,
         | 
| 2285 | 
            -
                         const_value, points, time_series_uuid, string_lookup_uuid))
         | 
| 2326 | 
            +
                         const_value, points, time_series_uuid, string_lookup_uuid, pre_packed))
         | 
| 2286 2327 | 
             
                    return uuid
         | 
| 2287 2328 |  | 
| 2288 2329 | 
             
                def add_similar_to_imported_list(self,
         | 
| @@ -2305,6 +2346,7 @@ class PropertyCollection(): | |
| 2305 2346 | 
             
                                                 points = None,
         | 
| 2306 2347 | 
             
                                                 time_series_uuid = None,
         | 
| 2307 2348 | 
             
                                                 string_lookup_uuid = None,
         | 
| 2349 | 
            +
                                                 pre_packed = False,
         | 
| 2308 2350 | 
             
                                                 similar_model = None,
         | 
| 2309 2351 | 
             
                                                 title = None):
         | 
| 2310 2352 | 
             
                    """Caches array and adds to the list of imported properties using default metadata from a similar property.
         | 
| @@ -2336,6 +2378,7 @@ class PropertyCollection(): | |
| 2336 2378 | 
             
                          be provided when writing hdf5 and creating xml for the imported list
         | 
| 2337 2379 | 
             
                       string_lookup_uuid (UUID, optional): should be provided for categorical properties, though can alternatively
         | 
| 2338 2380 | 
             
                          be specified when creating xml
         | 
| 2381 | 
            +
                       pre_packed (bool, default False): set to True if the property is boolean and the cached array is packed
         | 
| 2339 2382 | 
             
                       similar_model (Model, optional): the model where the similar property resides, if not the same as this
         | 
| 2340 2383 | 
             
                          property collection
         | 
| 2341 2384 | 
             
                       title (str, optional): synonym for keyword argument
         | 
| @@ -2392,6 +2435,7 @@ class PropertyCollection(): | |
| 2392 2435 | 
             
                    args['string_lookup_uuid'] = get_arg(time_series_uuid, similar.string_lookup_uuid())
         | 
| 2393 2436 | 
             
                    em = similar.extra_metadata if hasattr(similar, 'extra_metadata') else {}
         | 
| 2394 2437 | 
             
                    args['source_info'] = get_arg(source_info, em.get('source'))
         | 
| 2438 | 
            +
                    args['pre_packed'] = pre_packed
         | 
| 2395 2439 |  | 
| 2396 2440 | 
             
                    return self.add_cached_array_to_imported_list(cached_array, **args)
         | 
| 2397 2441 |  | 
| @@ -2439,7 +2483,8 @@ class PropertyCollection(): | |
| 2439 2483 | 
             
                          as 32 bit; if None, the system default is to write as 32 bit; if True, 32 bit is used; if
         | 
| 2440 2484 | 
             
                          False, 64 bit data is written; ignored if dtype is not None
         | 
| 2441 2485 | 
             
                       use_pack (bool, default False): if True, bool arrays will be packed along their last axis; this
         | 
| 2442 | 
            -
                          will generally result in hdf5 data that is not readable by non-resqpy applications
         | 
| 2486 | 
            +
                          will generally result in hdf5 data that is not readable by non-resqpy applications; leave
         | 
| 2487 | 
            +
                          as False for already packed arrays
         | 
| 2443 2488 | 
             
                       chunks (str, optional): if not None, one of 'auto', 'all', or 'slice', controlling hdf5 chunks
         | 
| 2444 2489 | 
             
                       compression (str, optional): if not None, one of 'gzip' or 'lzf' being the hdf5 compression
         | 
| 2445 2490 | 
             
                          algorithm to be used; gzip gives better compression ratio but is slower
         | 
| @@ -2467,8 +2512,8 @@ class PropertyCollection(): | |
| 2467 2512 | 
             
                            uuid = entry[0]
         | 
| 2468 2513 | 
             
                            cached_name = entry[3]
         | 
| 2469 2514 | 
             
                        tail = 'points_patch0' if entry[18] else 'values_patch0'
         | 
| 2470 | 
            -
                        if use_pack and (str(dtype) | 
| 2471 | 
            -
                                         (dtype is None and str(self.__dict__[cached_name].dtype) | 
| 2515 | 
            +
                        if use_pack and ('bool' in str(dtype) or
         | 
| 2516 | 
            +
                                         (dtype is None and 'bool' in str(self.__dict__[cached_name].dtype))):
         | 
| 2472 2517 | 
             
                            dtype = 'pack'
         | 
| 2473 2518 | 
             
                        h5_reg.register_dataset(uuid, tail, self.__dict__[cached_name], dtype = dtype)
         | 
| 2474 2519 | 
             
                    h5_reg.write(file = file_name, mode = mode, use_int32 = use_int32)
         | 
| @@ -2595,7 +2640,8 @@ class PropertyCollection(): | |
| 2595 2640 | 
             
                               points = False,
         | 
| 2596 2641 | 
             
                               extra_metadata = {},
         | 
| 2597 2642 | 
             
                               const_value = None,
         | 
| 2598 | 
            -
                               expand_const_arrays = False | 
| 2643 | 
            +
                               expand_const_arrays = False,
         | 
| 2644 | 
            +
                               pre_packed = False):
         | 
| 2599 2645 | 
             
                    """Create a property xml node for a single property related to a given supporting representation node.
         | 
| 2600 2646 |  | 
| 2601 2647 | 
             
                    arguments:
         | 
| @@ -2652,9 +2698,11 @@ class PropertyCollection(): | |
| 2652 2698 | 
             
                          must cycle fastest in the array, ie. be the last index
         | 
| 2653 2699 | 
             
                       points (bool, default False): if True, this is a points property
         | 
| 2654 2700 | 
             
                       extra_metadata (dictionary, optional): if present, adds extra metadata in the xml
         | 
| 2655 | 
            -
                       const_value (float or  | 
| 2701 | 
            +
                       const_value (float, int or bool, optional): if present, create xml for a constant array filled with this value
         | 
| 2656 2702 | 
             
                       expand_const_arrays (boolean, default False): if True, the hdf5 write must also have been called with the
         | 
| 2657 2703 | 
             
                          same argument and the xml will treat a constant array as a normal array
         | 
| 2704 | 
            +
                       pre_packed (boolean, default False): if True, the property is a boolean property and the array has already
         | 
| 2705 | 
            +
                          been packed into bits
         | 
| 2658 2706 |  | 
| 2659 2707 | 
             
                    returns:
         | 
| 2660 2708 | 
             
                       the newly created property xml node
         | 
| @@ -2682,7 +2730,7 @@ class PropertyCollection(): | |
| 2682 2730 | 
             
                    direction = None if facet_type is None or facet_type != 'direction' else facet
         | 
| 2683 2731 |  | 
| 2684 2732 | 
             
                    if self.support is not None:
         | 
| 2685 | 
            -
                        pcxml._check_shape_list(self, indexable_element, direction, property_array, points, count)
         | 
| 2733 | 
            +
                        pcxml._check_shape_list(self, indexable_element, direction, property_array, points, count, pre_packed)
         | 
| 2686 2734 |  | 
| 2687 2735 | 
             
                    # todo: assertions:
         | 
| 2688 2736 | 
             
                    #    numpy data type matches discrete flag (and assumptions about precision)
         | 
| @@ -12,18 +12,13 @@ import numpy as np | |
| 12 12 | 
             
            import resqpy.property as rqp
         | 
| 13 13 | 
             
            import resqpy.olio.uuid as bu
         | 
| 14 14 | 
             
            import resqpy.olio.xml_et as rqet
         | 
| 15 | 
            -
            import resqpy.weights_and_measures as  | 
| 15 | 
            +
            import resqpy.weights_and_measures as wam
         | 
| 16 16 |  | 
| 17 17 | 
             
            # the following resqml property kinds and facet types are 'known about' by this module in relation to nexus
         | 
| 18 18 | 
             
            # other property kinds should be handled okay but without any special treatment
         | 
| 19 19 | 
             
            # see property_kind_and_facet_from_keyword() for simulator keyword to property kind and facet mapping
         | 
| 20 20 |  | 
| 21 | 
            -
            supported_property_kind_list =  | 
| 22 | 
            -
                'continuous', 'discrete', 'categorical', 'code', 'index', 'depth', 'rock volume', 'pore volume', 'volume',
         | 
| 23 | 
            -
                'thickness', 'length', 'cell length', 'area', 'net to gross ratio', 'porosity', 'permeability thickness',
         | 
| 24 | 
            -
                'permeability length', 'permeability rock', 'rock permeability', 'fluid volume', 'transmissibility', 'pressure',
         | 
| 25 | 
            -
                'saturation', 'solution gas-oil ratio', 'vapor oil-gas ratio', 'property multiplier', 'thermodynamic temperature'
         | 
| 26 | 
            -
            ]
         | 
| 21 | 
            +
            supported_property_kind_list = list(wam.valid_property_kinds())
         | 
| 27 22 |  | 
| 28 23 | 
             
            supported_local_property_kind_list = [
         | 
| 29 24 | 
             
                'active', 'transmissibility multiplier', 'fault transmissibility', 'mat transmissibility'
         | 
| @@ -326,7 +321,7 @@ def infer_property_kind(name, unit): | |
| 326 321 |  | 
| 327 322 | 
             
                # Currently unit is ignored
         | 
| 328 323 |  | 
| 329 | 
            -
                valid_kinds =  | 
| 324 | 
            +
                valid_kinds = wam.valid_property_kinds()
         | 
| 330 325 |  | 
| 331 326 | 
             
                if name in valid_kinds:
         | 
| 332 327 | 
             
                    kind = name
         | 
| @@ -15,14 +15,16 @@ import resqpy.surface as rqs | |
| 15 15 |  | 
| 16 16 |  | 
| 17 17 | 
             
            def add_surfaces(
         | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 18 | 
            +
                    epc_file,  # existing resqml model
         | 
| 19 | 
            +
                    crs_uuid = None,  # optional crs uuid, defaults to crs associated with model (usually main grid crs)
         | 
| 20 | 
            +
                    surface_file_format = 'zmap',  # zmap, rms (roxar) or GOCAD-Tsurf only formats currently supported
         | 
| 21 | 
            +
                    rq_class = 'surface',  # 'surface' or 'mesh': the class of object to be created
         | 
| 22 | 
            +
                    surface_role = 'map',  # 'map' or 'pick'
         | 
| 23 | 
            +
                    quad_triangles = False,  # if True, 4 triangles per quadrangle will be used for mesh formats, otherwise 2
         | 
| 24 | 
            +
                    surface_file_list = None,  # list of full file names (paths), each holding one surface
         | 
| 25 | 
            +
                    make_horizon_interpretations_and_features = True,  # if True, feature and interpretation objects are created
         | 
| 26 | 
            +
                    interpretation_type = 'horizon',
         | 
| 27 | 
            +
                    fault_is_normal = True):
         | 
| 26 28 | 
             
                """Process a list of surface files, adding each surface as a new part in the resqml model.
         | 
| 27 29 |  | 
| 28 30 | 
             
                Arguments:
         | 
| @@ -34,6 +36,8 @@ def add_surfaces( | |
| 34 36 | 
             
                    quad_triangles (bool, default False): if True, 4 triangles per quadrangle will be used for mesh formats, otherwise 2
         | 
| 35 37 | 
             
                    surface_file_list (list, default None): list of full file names (paths), each holding one surface
         | 
| 36 38 | 
             
                    make_horizon_interpretations_and_features (bool, default True): if True, feature and interpretation objects are created
         | 
| 39 | 
            +
                    interpretation_type (str, default 'horizon'): if 'make_horizon_interpretations_and_features' is True, feature and interpretation objects are added. Default is 'horizon', other options are 'fault' and 'geobody'
         | 
| 40 | 
            +
                    fault_is_normal (bool, default True): if 'interpretation_type' is 'fault', define if the fault is a normal fault. Default True
         | 
| 37 41 |  | 
| 38 42 | 
             
                Returns:
         | 
| 39 43 | 
             
                    resqml model object with added surfaces
         | 
| @@ -42,13 +46,15 @@ def add_surfaces( | |
| 42 46 | 
             
                assert surface_file_list, 'surface file list is empty or missing'
         | 
| 43 47 | 
             
                assert surface_file_format in ['zmap', 'rms', 'roxar',
         | 
| 44 48 | 
             
                                               'GOCAD-Tsurf'], 'unsupported surface file format: ' + str(surface_file_format)
         | 
| 49 | 
            +
                assert interpretation_type in ['horizon', 'fault', 'geobody']
         | 
| 45 50 | 
             
                rq_class = _get_rq_class(rq_class)
         | 
| 46 51 |  | 
| 47 52 | 
             
                model, crs_uuid = _get_model_details(epc_file, crs_uuid)
         | 
| 48 53 |  | 
| 49 54 | 
             
                for surf_file in surface_file_list:
         | 
| 50 55 | 
             
                    model = _add_single_surface(model, surf_file, surface_file_format, surface_role, quad_triangles, crs_uuid,
         | 
| 51 | 
            -
                                                rq_class, make_horizon_interpretations_and_features | 
| 56 | 
            +
                                                rq_class, make_horizon_interpretations_and_features, interpretation_type,
         | 
| 57 | 
            +
                                                fault_is_normal)
         | 
| 52 58 |  | 
| 53 59 | 
             
                # mark model as modified
         | 
| 54 60 | 
             
                model.set_modified()
         | 
| @@ -61,7 +67,7 @@ def add_surfaces( | |
| 61 67 |  | 
| 62 68 |  | 
| 63 69 | 
             
            def _add_single_surface(model, surf_file, surface_file_format, surface_role, quad_triangles, crs_uuid, rq_class,
         | 
| 64 | 
            -
                                    make_horizon_interpretations_and_features):
         | 
| 70 | 
            +
                                    make_horizon_interpretations_and_features, interpretation_type, fault_is_normal):
         | 
| 65 71 | 
             
                _, short_name = os.path.split(surf_file)
         | 
| 66 72 | 
             
                dot = short_name.rfind('.')
         | 
| 67 73 | 
             
                if dot > 0:
         | 
| @@ -106,10 +112,24 @@ def _add_single_surface(model, surf_file, surface_file_format, surface_role, qua | |
| 106 112 | 
             
                surface.write_hdf5()
         | 
| 107 113 |  | 
| 108 114 | 
             
                if make_horizon_interpretations_and_features:
         | 
| 109 | 
            -
                     | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 115 | 
            +
                    if interpretation_type == 'horizon':
         | 
| 116 | 
            +
                        feature = rqo.GeneticBoundaryFeature(model, kind = 'horizon', feature_name = short_name)
         | 
| 117 | 
            +
                        feature.create_xml()
         | 
| 118 | 
            +
                        interp = rqo.HorizonInterpretation(model, genetic_boundary_feature = feature, domain = 'depth')
         | 
| 119 | 
            +
                        interp_root = interp.create_xml()
         | 
| 120 | 
            +
                    elif interpretation_type == 'fault':
         | 
| 121 | 
            +
                        feature = rqo.TectonicBoundaryFeature(model, kind = 'fault', feature_name = short_name)
         | 
| 122 | 
            +
                        feature.create_xml()
         | 
| 123 | 
            +
                        interp = rqo.FaultInterpretation(model,
         | 
| 124 | 
            +
                                                         tectonic_boundary_feature = feature,
         | 
| 125 | 
            +
                                                         domain = 'depth',
         | 
| 126 | 
            +
                                                         is_normal = fault_is_normal)
         | 
| 127 | 
            +
                        interp_root = interp.create_xml()
         | 
| 128 | 
            +
                    else:
         | 
| 129 | 
            +
                        feature = rqo.GeobodyFeature(model, feature_name = short_name)
         | 
| 130 | 
            +
                        feature.create_xml()
         | 
| 131 | 
            +
                        interp = rqo.GeobodyInterpretation(model, geobody_feature = feature, domain = 'depth')
         | 
| 132 | 
            +
                        interp_root = interp.create_xml()
         | 
| 113 133 | 
             
                    surface.set_represented_interpretation_root(interp_root)
         | 
| 114 134 |  | 
| 115 135 | 
             
                surface.create_xml(add_as_part = True, add_relationships = True, title = short_name, originator = None)
         | 
| @@ -511,8 +511,8 @@ class _GridFromCp: | |
| 511 511 | 
             
                            assert len(where_defined) == 3 and len(where_defined[0]) > 0, 'no extant cell geometries'
         | 
| 512 512 | 
             
                            sample_kji0 = (where_defined[0][0], where_defined[1][0], where_defined[2][0])
         | 
| 513 513 | 
             
                        sample_cp = self.__cp_array[sample_kji0]
         | 
| 514 | 
            -
                        self.__cell_ijk_lefthanded = | 
| 515 | 
            -
             | 
| 514 | 
            +
                        self.__cell_ijk_lefthanded =  \
         | 
| 515 | 
            +
                            (vec.clockwise(sample_cp[0, 0, 0], sample_cp[0, 1, 0], sample_cp[0, 0, 1]) >= 0.0)
         | 
| 516 516 | 
             
                        if not self.grid.k_direction_is_down:
         | 
| 517 517 | 
             
                            self.__cell_ijk_lefthanded = not self.__cell_ijk_lefthanded
         | 
| 518 518 | 
             
                        if self.__crs.is_right_handed_xyz():
         |