resqpy 4.14.3__tar.gz → 4.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {resqpy-4.14.3 → resqpy-4.15.0}/PKG-INFO +1 -1
- {resqpy-4.14.3 → resqpy-4.15.0}/pyproject.toml +1 -1
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/__init__.py +1 -1
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/__init__.py +3 -2
- resqpy-4.15.0/resqpy/property/attribute_property_set.py +264 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_surface.py +117 -55
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/_any_time_series.py +3 -1
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/_geologic_time_series.py +1 -1
- {resqpy-4.14.3 → resqpy-4.15.0}/LICENSE +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/README.md +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/crs.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_edges_per_column_property_array.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_faults.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_one_blocked_well_property.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_one_grid_property_array.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_single_cell_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_wells_from_ascii_file.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_zone_by_layer_property.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_coarsened_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_common.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_copy_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_drape_to_surface.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_extract_box.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_extract_box_for_well.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_fault_throw_scaling.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_gather_ensemble.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_interpolated_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_local_depth_adjustment.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_refined_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_tilted_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_unsplit_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_zonal_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_zone_layer_ranges_from_array.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/fault/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/fault/_gcs_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/fault/_grid_connection_set.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_cell_properties.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_connection_sets.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_create_grid_xml.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_defined_geometry.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_extract_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_face_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_faults.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_grid_types.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_intervals_info.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_moved_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_pillars.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_pixel_maps.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_points_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_regular_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_transmissibility.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_write_hdf5_from_caches.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_write_nexus_corp.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid/_xyz.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/_blocked_well_populate.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/_find_faces.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/_grid_skin.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/_grid_surface.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/_trajectory_intersects.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/grid_surface/grid_surface_cuda.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/lines/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/lines/_common.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/lines/_polyline.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/lines/_polyline_set.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_catalogue.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_context.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_forestry.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_grids.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_hdf5.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_model.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/model/_xml.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/multi_processing/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/multi_processing/_multiprocessing.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/multi_processing/wrappers/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/multi_processing/wrappers/blocked_well_mp.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/multi_processing/wrappers/grid_surface_mp.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/multi_processing/wrappers/mesh_mp.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/ab_toolbox.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/base.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/box_utilities.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/class_dict.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/consolidation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/data/build.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/data/properties.json +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/dataframe.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/exceptions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/factors.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/fine_coarse.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/grid_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/intersection.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/keyword_files.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/load_data.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/point_inclusion.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/random_seed.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/read_nexus_fault.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/relperm.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/simple_lines.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/time.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/trademark.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/transmission.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/triangulation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/uuid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/vdb.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/vector_utilities.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/volume.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/wellspec_keywords.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/write_data.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/write_hdf5.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/xml_et.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/xml_namespaces.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/olio/zmap_reader.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/_utils.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/boundary_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/boundary_feature_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/earth_model_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/fault_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/fluid_boundary_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/frontier_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/generic_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/genetic_boundary_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/geobody_boundary_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/geobody_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/geobody_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/geologic_unit_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/horizon_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/organization_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/rock_fluid_unit_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/structural_organization_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/tectonic_boundary_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/wellbore_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/organize/wellbore_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/_collection_add_part.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/_collection_create_xml.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/_collection_get_attributes.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/_collection_support.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/_property.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/grid_property_collection.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/property_collection.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/property_common.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/property_kind.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/string_lookup.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/well_interval_property.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/well_interval_property_collection.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/well_log.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/property/well_log_collection.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/_add_ab_properties.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/_add_surfaces.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/_grid_from_cp.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/_import_nexus.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/_import_vdb_all_grids.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/rq_import/_import_vdb_ensemble.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_binary_contact_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_geologic_unit_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_strata_common.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_stratigraphic_column.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_stratigraphic_column_rank.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_stratigraphic_unit_feature.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/strata/_stratigraphic_unit_interpretation.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_base_surface.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_combined_surface.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_mesh.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_pointset.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_tri_mesh.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_tri_mesh_stencil.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/surface/_triangulated_patch.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/_from_nexus_summary.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/_functions.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/_time_duration.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/time_series/_time_series.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/unstructured/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/unstructured/_hexa_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/unstructured/_prism_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/unstructured/_pyramid_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/unstructured/_tetra_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/unstructured/_unstructured_grid.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/weights_and_measures/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/weights_and_measures/nexus_units.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/weights_and_measures/weights_and_measures.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/__init__.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_blocked_well.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_deviation_survey.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_md_datum.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_trajectory.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_wellbore_frame.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_wellbore_marker.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/_wellbore_marker_frame.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/blocked_well_frame.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/well_object_funcs.py +0 -0
- {resqpy-4.14.3 → resqpy-4.15.0}/resqpy/well/well_utils.py +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
"""Collections of properties for grids, wellbore frames, grid connection sets etc."""
|
2
2
|
|
3
3
|
__all__ = [
|
4
|
-
'PropertyCollection', 'Property', '
|
5
|
-
'WellLogCollection', 'StringLookup', 'PropertyKind', 'GridPropertyCollection',
|
4
|
+
'PropertyCollection', 'Property', 'AttributePropertySet', 'ApsProperty', 'WellLog', 'WellIntervalProperty',
|
5
|
+
'WellIntervalPropertyCollection', 'WellLogCollection', 'StringLookup', 'PropertyKind', 'GridPropertyCollection',
|
6
6
|
'property_over_time_series_from_collection', 'property_collection_for_keyword', 'infer_property_kind',
|
7
7
|
'write_hdf5_and_create_xml_for_active_property', 'reformat_column_edges_to_resqml_format',
|
8
8
|
'reformat_column_edges_from_resqml_format', 'same_property_kind', 'selective_version_of_collection',
|
@@ -30,6 +30,7 @@ from .property_common import property_collection_for_keyword, \
|
|
30
30
|
from .property_kind import PropertyKind, create_transmisibility_multiplier_property_kind
|
31
31
|
from .string_lookup import StringLookup
|
32
32
|
from .property_collection import PropertyCollection
|
33
|
+
from .attribute_property_set import AttributePropertySet, ApsProperty
|
33
34
|
from .grid_property_collection import GridPropertyCollection
|
34
35
|
from ._property import Property
|
35
36
|
from .well_interval_property import WellIntervalProperty
|
@@ -0,0 +1,264 @@
|
|
1
|
+
"""Class handling set of RESQML properties using attribute syntax for properties and their metadata."""
|
2
|
+
|
3
|
+
# Nexus is a registered trademark of the Halliburton Company
|
4
|
+
|
5
|
+
import logging
|
6
|
+
|
7
|
+
log = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
import numpy as np
|
10
|
+
import numpy.ma as ma
|
11
|
+
|
12
|
+
import resqpy.property as rqp
|
13
|
+
|
14
|
+
|
15
|
+
class ApsProperty:
|
16
|
+
"""Class holding a single property with attribute style read access to metadata items."""
|
17
|
+
|
18
|
+
# note: this class could be private to the AttributePropertySet class
|
19
|
+
|
20
|
+
def __init__(self, aps, part):
|
21
|
+
"""Initialise a single property from a property set with attribute style read access to metadata items."""
|
22
|
+
self.aps = aps
|
23
|
+
self._part = part
|
24
|
+
self.key = aps._key(part)
|
25
|
+
|
26
|
+
# NB. the following are read-only attributes
|
27
|
+
|
28
|
+
@property
|
29
|
+
def part(self):
|
30
|
+
"""The part (string) identifier for this property."""
|
31
|
+
return self._part
|
32
|
+
|
33
|
+
@part.setter
|
34
|
+
def part(self, value):
|
35
|
+
self._part = value
|
36
|
+
|
37
|
+
@property
|
38
|
+
def node(self):
|
39
|
+
"""The xml root node for this property."""
|
40
|
+
return self.aps.node_for_part(self.part)
|
41
|
+
|
42
|
+
@property
|
43
|
+
def uuid(self):
|
44
|
+
"""The uuid for this property."""
|
45
|
+
return self.aps.uuid_for_part(self.part)
|
46
|
+
|
47
|
+
@property
|
48
|
+
def array_ref(self):
|
49
|
+
"""The cached numpy array of values for this property."""
|
50
|
+
return self.aps.cached_part_array_ref(self.part)
|
51
|
+
|
52
|
+
@property
|
53
|
+
def values(self):
|
54
|
+
"""A copy of the numpy array of values for this property."""
|
55
|
+
return self.array_ref.copy()
|
56
|
+
|
57
|
+
@property
|
58
|
+
def property_kind(self):
|
59
|
+
"""The property kind of this property."""
|
60
|
+
return self.aps.property_kind_for_part(self.part)
|
61
|
+
|
62
|
+
@property
|
63
|
+
def facet_type(self):
|
64
|
+
"""The facet type for this property (may be None)."""
|
65
|
+
return self.aps.facet_type_for_part(self.part)
|
66
|
+
|
67
|
+
@property
|
68
|
+
def facet(self):
|
69
|
+
"""The facet value for this property (may be None)."""
|
70
|
+
return self.aps.facet_for_part(self.part)
|
71
|
+
|
72
|
+
@property
|
73
|
+
def indexable(self):
|
74
|
+
"""The indexable element for this property (synonymous with indexable_element)."""
|
75
|
+
return self.aps.indexable_for_part(self.part)
|
76
|
+
|
77
|
+
@property
|
78
|
+
def indexable_element(self):
|
79
|
+
"""The indexable element for this property (synonymous with indexable)."""
|
80
|
+
return self.indexable
|
81
|
+
|
82
|
+
@property
|
83
|
+
def is_continuous(self):
|
84
|
+
"""Boolean indicating whether this property is continuous."""
|
85
|
+
return self.aps.continuous_for_part(self.part)
|
86
|
+
|
87
|
+
@property
|
88
|
+
def is_categorical(self):
|
89
|
+
"""Boolean indicating whether this property is categorical."""
|
90
|
+
return self.aps.part_is_categorical(self.part)
|
91
|
+
|
92
|
+
@property
|
93
|
+
def is_discrete(self):
|
94
|
+
"""Boolean indicating whether this property is discrete (False for categorical properties)."""
|
95
|
+
return not (self.is_continuous or self.is_categorical)
|
96
|
+
|
97
|
+
@property
|
98
|
+
def is_points(self):
|
99
|
+
"""Boolean indicating whether this is a points property."""
|
100
|
+
return self.aps.points_for_part(self.part)
|
101
|
+
|
102
|
+
@property
|
103
|
+
def count(self):
|
104
|
+
"""The count (number of sub-elements per element, usually 1) for this property."""
|
105
|
+
return self.aps.count_for_part(self.part)
|
106
|
+
|
107
|
+
@property
|
108
|
+
def uom(self):
|
109
|
+
"""The unit of measure for this property (will be None for discrete or categorical properties)."""
|
110
|
+
return self.aps.uom_for_part(self.part)
|
111
|
+
|
112
|
+
@property
|
113
|
+
def null_value(self):
|
114
|
+
"""The null value for this property (will be None for continuous properties, for which NaN is always the null value)."""
|
115
|
+
return self.aps.null_value_for_part(self.part)
|
116
|
+
|
117
|
+
@property
|
118
|
+
def realization(self):
|
119
|
+
"""The realisation number for this property (may be None)."""
|
120
|
+
return self.aps.realization_for_part(self.part)
|
121
|
+
|
122
|
+
@property
|
123
|
+
def time_index(self):
|
124
|
+
"""The time index for this property (may be None)."""
|
125
|
+
return self.aps.time_index_for_part(self.part)
|
126
|
+
|
127
|
+
@property
|
128
|
+
def title(self):
|
129
|
+
"""The citation title for this property (synonymous with citation_title)."""
|
130
|
+
return self.aps.citation_title_for_part(self.part)
|
131
|
+
|
132
|
+
@property
|
133
|
+
def citation_title(self):
|
134
|
+
"""The citation title for this property (synonymous with title)."""
|
135
|
+
return self.title
|
136
|
+
|
137
|
+
@property
|
138
|
+
def min_value(self):
|
139
|
+
"""The minimum value for this property, as stored in xml metadata."""
|
140
|
+
return self.aps.minimum_value_for_part(self.part)
|
141
|
+
|
142
|
+
@property
|
143
|
+
def max_value(self):
|
144
|
+
"""The maximum value for this property, as stored in xml metadata."""
|
145
|
+
return self.aps.maximum_value_for_part(self.part)
|
146
|
+
|
147
|
+
@property
|
148
|
+
def constant_value(self):
|
149
|
+
"""The constant value for this property, as stored in xml metadata (usually None)."""
|
150
|
+
return self.aps.constant_value_for_part(self.part)
|
151
|
+
|
152
|
+
@property
|
153
|
+
def extra(self):
|
154
|
+
"""The extra metadata for this property (synonymous with extra_metadata)."""
|
155
|
+
return self.aps.extra_metadata_for_part(self.part)
|
156
|
+
|
157
|
+
@property
|
158
|
+
def extra_metadata(self):
|
159
|
+
"""The extra metadata for this property (synonymous with extra)."""
|
160
|
+
return self.extra
|
161
|
+
|
162
|
+
@property
|
163
|
+
def support_uuid(self):
|
164
|
+
"""The uuid of the supporting representation for this property."""
|
165
|
+
return self.aps.support_uuid_for_part(self.part)
|
166
|
+
|
167
|
+
@property
|
168
|
+
def string_lookup_uuid(self):
|
169
|
+
"""The uuid of the string lookup table for a categorical property (otherwise None)."""
|
170
|
+
return self.aps.string_lookup_uuid_for_part(self.part)
|
171
|
+
|
172
|
+
@property
|
173
|
+
def time_series_uuid(self):
|
174
|
+
"""The uuid of the time series for this property (may be None)."""
|
175
|
+
return self.aps.time_series_uuid_for_part(self.part)
|
176
|
+
|
177
|
+
@property
|
178
|
+
def local_property_kind_uuid(self):
|
179
|
+
"""The uuid of the local property kind for this property (may be None)."""
|
180
|
+
return self.aps.local_property_kind_uuid(self.part)
|
181
|
+
|
182
|
+
|
183
|
+
class AttributePropertySet(rqp.PropertyCollection):
|
184
|
+
"""Class for set of RESQML properties for any supporting representation, using attribute syntax."""
|
185
|
+
|
186
|
+
def __init__(self, model = None, support = None, property_set_uuid = None, realization = None, key_mode = 'pk'):
|
187
|
+
"""Initialise an empty property set, optionally populate properties from a supporting representation.
|
188
|
+
|
189
|
+
arguments:
|
190
|
+
model (Model, optional): required if property_set_uuid is not None
|
191
|
+
support (optional): a grid.Grid object, or a well.BlockedWell, or a well.WellboreFrame object which belongs to a
|
192
|
+
resqpy.Model which includes associated properties; if this argument is given, and property_set_root is None,
|
193
|
+
the properties in the support's parent model which are for this representation (ie. have this object as the
|
194
|
+
supporting representation) are added to this collection as part of the initialisation
|
195
|
+
property_set_uuid (optional): if present, the collection is populated with the properties defined in the xml tree
|
196
|
+
of the property set
|
197
|
+
realization (integer, optional): if present, the single realisation (within an ensemble) that this collection is for;
|
198
|
+
if None, then the collection is either covering a whole ensemble (individual properties can each be flagged with a
|
199
|
+
realisation number), or is for properties that do not have multiple realizations
|
200
|
+
key_mode (str, default 'pk'): either 'pk' (for property kind) or 'title', identifying the basis of property attribute keys
|
201
|
+
|
202
|
+
note:
|
203
|
+
at present, if the collection is being initialised from a property set, the support argument must also be specified;
|
204
|
+
also for now, if not initialising from a property set, all properties related to the support are included, whether
|
205
|
+
the relationship is supporting representation or some other relationship;
|
206
|
+
|
207
|
+
:meta common:
|
208
|
+
"""
|
209
|
+
|
210
|
+
assert key_mode in ['pk', 'title']
|
211
|
+
assert property_set_uuid is None or model is not None
|
212
|
+
assert support is None or model is None or support.model is model
|
213
|
+
if property_set_uuid is None:
|
214
|
+
property_set_root = None
|
215
|
+
else:
|
216
|
+
property_set_root = model.root_for_uuid(property_set_uuid)
|
217
|
+
|
218
|
+
super().__init__(support = support, property_set_root = property_set_root, realization = realization)
|
219
|
+
self.key_mode = key_mode
|
220
|
+
self._make_attributes()
|
221
|
+
|
222
|
+
def keys(self):
|
223
|
+
"""Iterator over property keys within the set."""
|
224
|
+
for p in self.parts():
|
225
|
+
yield self._key(p)
|
226
|
+
|
227
|
+
def properties(self):
|
228
|
+
"""Iterator over ApsProperty members of the set."""
|
229
|
+
for k in self.keys():
|
230
|
+
yield getattr(self, k)
|
231
|
+
|
232
|
+
def items(self):
|
233
|
+
"""Iterator over (key, ApsProperty) members of the set."""
|
234
|
+
for k in self.keys():
|
235
|
+
yield (k, getattr(self, k))
|
236
|
+
|
237
|
+
def _key(self, part):
|
238
|
+
"""Returns the key (attribute name) for a given part."""
|
239
|
+
if self.key_mode == 'pk':
|
240
|
+
key = self.property_kind_for_part(part)
|
241
|
+
facet = self.facet_for_part(part)
|
242
|
+
if facet is not None:
|
243
|
+
key += f'_{facet}'
|
244
|
+
else:
|
245
|
+
key = self.citation_title_for_part(part)
|
246
|
+
key = key.replace(' ', '_')
|
247
|
+
ti = self.time_index_for_part(part)
|
248
|
+
if ti is not None:
|
249
|
+
key += f'_t{ti}'
|
250
|
+
r = self.realization_for_part(part)
|
251
|
+
if r is not None:
|
252
|
+
key += f'_r{r}'
|
253
|
+
return key
|
254
|
+
|
255
|
+
def _make_attributes(self):
|
256
|
+
"""Setup individual properties with attribute style read access to metadata."""
|
257
|
+
for part in self.parts():
|
258
|
+
key = self._key(part)
|
259
|
+
aps_property = ApsProperty(self, part)
|
260
|
+
setattr(self, key, aps_property)
|
261
|
+
|
262
|
+
def __len__(self):
|
263
|
+
"""Returns the number of properties in the set."""
|
264
|
+
return self.number_of_parts()
|
@@ -272,48 +272,86 @@ class Surface(rqsb.BaseSurface):
|
|
272
272
|
|
273
273
|
self.model = parent_model
|
274
274
|
|
275
|
-
def
|
276
|
-
"""Returns
|
275
|
+
def number_of_patches(self):
|
276
|
+
"""Returns the number of patches present in the surface."""
|
277
|
+
|
278
|
+
self.extract_patches(self.root)
|
279
|
+
return len(self.patch_list)
|
280
|
+
|
281
|
+
def triangles_and_points(self, patch = None):
|
282
|
+
"""Returns arrays representing one patch or a combination of all the patches in the surface.
|
283
|
+
|
284
|
+
arguments:
|
285
|
+
patch (int, optional): patch index; if None, combined arrays for all patches are returned
|
277
286
|
|
278
287
|
returns:
|
279
288
|
tuple (triangles, points):
|
280
289
|
triangles (int array of shape[:, 3]): integer indices into points array,
|
281
|
-
|
290
|
+
being the nodes of the corners of the triangles;
|
282
291
|
points (float array of shape[:, 3]): flat array of xyz points, indexed by triangles
|
283
292
|
|
284
293
|
:meta common:
|
285
294
|
"""
|
286
295
|
|
287
|
-
if self.triangles is not None:
|
288
|
-
return (self.triangles, self.points)
|
289
296
|
self.extract_patches(self.root)
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
297
|
+
if patch is None:
|
298
|
+
if self.triangles is None:
|
299
|
+
points_offset = 0
|
300
|
+
for triangulated_patch in self.patch_list:
|
301
|
+
(t, p) = triangulated_patch.triangles_and_points()
|
302
|
+
if points_offset == 0:
|
303
|
+
self.triangles = t.copy()
|
304
|
+
self.points = p.copy()
|
305
|
+
else:
|
306
|
+
self.triangles = np.concatenate((self.triangles, t.copy() + points_offset))
|
307
|
+
self.points = np.concatenate((self.points, p.copy()))
|
308
|
+
points_offset += p.shape[0]
|
309
|
+
return (self.triangles, self.points)
|
310
|
+
assert 0 <= patch < len(self.patch_list), \
|
311
|
+
ValueError(f'patch index {patch} out of range for surface with {len(self.patch_list)} patches')
|
312
|
+
return self.patch_list[patch].triangles_and_points()
|
301
313
|
|
302
|
-
def triangle_count(self):
|
303
|
-
"""Return the numner of triangles in this surface.
|
314
|
+
def triangle_count(self, patch = None):
|
315
|
+
"""Return the numner of triangles in this surface, or in one patch.
|
316
|
+
|
317
|
+
arguments:
|
318
|
+
patch (int, optional): patch index; if None, a combined triangle count for all patches is returned
|
319
|
+
|
320
|
+
returns:
|
321
|
+
int being the number of trianges in the patch (if specified) or in all the patches
|
322
|
+
"""
|
304
323
|
|
305
324
|
self.extract_patches(self.root)
|
306
|
-
if
|
307
|
-
|
308
|
-
|
325
|
+
if patch is None:
|
326
|
+
if not self.patch_list:
|
327
|
+
return 0
|
328
|
+
return np.sum([tp.triangle_count for tp in self.patch_list])
|
329
|
+
assert 0 <= patch < len(self.patch_list), \
|
330
|
+
ValueError(f'patch index {patch} out of range for surface with {len(self.patch_list)} patches in triangle_count')
|
331
|
+
return self.patch_list[patch].triangle_count
|
332
|
+
|
333
|
+
def node_count(self, patch = None):
|
334
|
+
"""Return the number of nodes (points) used in this surface, or in one patch.
|
335
|
+
|
336
|
+
arguments:
|
337
|
+
patch (int, optional): patch index; if None, a combined node count for all patches is returned
|
338
|
+
|
339
|
+
returns:
|
340
|
+
int being the number of nodes in the patch (if specified) or in all the patches
|
309
341
|
|
310
|
-
|
311
|
-
|
342
|
+
note:
|
343
|
+
a multi patch surface might have more than one node colocated; this method will treat such coincident nodes
|
344
|
+
as separate nodes
|
345
|
+
"""
|
312
346
|
|
313
347
|
self.extract_patches(self.root)
|
314
|
-
if
|
315
|
-
|
316
|
-
|
348
|
+
if patch is None:
|
349
|
+
if not self.patch_list:
|
350
|
+
return 0
|
351
|
+
return np.sum([tp.node_count for tp in self.patch_list])
|
352
|
+
assert 0 <= patch < len(self.patch_list), \
|
353
|
+
ValueError(f'patch index {patch} out of range for surface with {len(self.patch_list)} patches in node_count')
|
354
|
+
return self.patch_list[patch].node_count
|
317
355
|
|
318
356
|
def change_crs(self, required_crs):
|
319
357
|
"""Changes the crs of the surface, also sets a new uuid if crs changed.
|
@@ -381,7 +419,7 @@ class Surface(rqsb.BaseSurface):
|
|
381
419
|
self.uuid = bu.new_uuid()
|
382
420
|
|
383
421
|
def set_to_split_surface(self, large_surface, line, delta_xyz):
|
384
|
-
"""Populate this (empty) surface with a version of a larger surface split by
|
422
|
+
"""Populate this (empty) surface with a version of a larger surface split by a straight xy line.
|
385
423
|
|
386
424
|
arguments:
|
387
425
|
large_surface (Surface): the larger surface, a copy of which is to be split
|
@@ -404,8 +442,8 @@ class Surface(rqsb.BaseSurface):
|
|
404
442
|
tp[:, 1] = len(p) + 1
|
405
443
|
tp[:, 2] = np.arange(len(p), dtype = int)
|
406
444
|
cw = vec.clockwise_triangles(pp, tp)
|
407
|
-
pai =
|
408
|
-
pbi =
|
445
|
+
pai = (cw >= 0.0) # bool mask over p
|
446
|
+
pbi = (cw <= 0.0) # bool mask over p
|
409
447
|
tap = pai[t]
|
410
448
|
tbp = pbi[t]
|
411
449
|
ta = np.any(tap, axis = 1) # bool array over t
|
@@ -435,17 +473,24 @@ class Surface(rqsb.BaseSurface):
|
|
435
473
|
|
436
474
|
self.set_from_triangles_and_points(t_combo, p_combo)
|
437
475
|
|
438
|
-
def distinct_edges(self):
|
439
|
-
"""Returns a numpy int array of shape (N, 2) being the ordered node pairs of distinct edges of triangles.
|
476
|
+
def distinct_edges(self, patch = None):
|
477
|
+
"""Returns a numpy int array of shape (N, 2) being the ordered node pairs of distinct edges of triangles.
|
478
|
+
|
479
|
+
arguments:
|
480
|
+
patch (int, optional): patch index; if None, a combination of edges for all patches is returned
|
481
|
+
"""
|
440
482
|
|
441
|
-
triangles, _ = self.triangles_and_points()
|
483
|
+
triangles, _ = self.triangles_and_points(patch = patch)
|
442
484
|
assert triangles is not None
|
443
485
|
unique_edges, _ = triangulate.edges(triangles)
|
444
486
|
return unique_edges
|
445
487
|
|
446
|
-
def distinct_edges_and_counts(self):
|
488
|
+
def distinct_edges_and_counts(self, patch = None):
|
447
489
|
"""Returns unique edges as pairs of point indices, and a count of uses of each edge.
|
448
490
|
|
491
|
+
arguments:
|
492
|
+
patch (int, optional): patch index; if None, combined results for all patches are returned
|
493
|
+
|
449
494
|
returns:
|
450
495
|
numpy int array of shape (N, 2), numpy int array of shape (N,)
|
451
496
|
where 2D array is list-like sorted points index pairs for unique edges
|
@@ -453,25 +498,26 @@ class Surface(rqsb.BaseSurface):
|
|
453
498
|
|
454
499
|
notes:
|
455
500
|
first entry in each pair is always the lower of the two point indices;
|
456
|
-
for well formed surfaces, the count should everywhere be
|
501
|
+
for well formed surfaces, the count should everywhere be one or two;
|
457
502
|
the function does not attempt to detect coincident points
|
458
503
|
"""
|
459
504
|
|
460
|
-
triangles, _ = self.triangles_and_points()
|
505
|
+
triangles, _ = self.triangles_and_points(patch = patch)
|
461
506
|
assert triangles is not None
|
462
507
|
return triangulate.edges(triangles)
|
463
508
|
|
464
|
-
def edge_lengths(self, required_uom = None):
|
509
|
+
def edge_lengths(self, required_uom = None, patch = None):
|
465
510
|
"""Returns float array of shape (N, 3) being triangle edge lengths.
|
466
511
|
|
467
512
|
arguments:
|
468
513
|
required_uom (str, optional): the required length uom for the resulting edge lengths; default is crs xy units
|
514
|
+
patch (int, optional): patch index; if None, edge lengths for all patches are returned
|
469
515
|
|
470
516
|
returns:
|
471
517
|
numpy float array of shape (N, 3) where N is the number of triangles
|
472
518
|
"""
|
473
519
|
|
474
|
-
t, p = self.triangles_and_points()
|
520
|
+
t, p = self.triangles_and_points(patch = patch)
|
475
521
|
crs = rqc.Crs(self.model, uuid = self.crs_uuid)
|
476
522
|
if required_uom is None:
|
477
523
|
required_uom = crs.xy_units
|
@@ -495,6 +541,20 @@ class Surface(rqsb.BaseSurface):
|
|
495
541
|
self.triangles = triangles.copy()
|
496
542
|
self.points = points.copy()
|
497
543
|
|
544
|
+
def set_multi_patch_from_triangles_and_points(self, triangles_and_points_list):
|
545
|
+
"""Populate this (empty) Surface object from a list of paits: array of triangle corner indices, array of points."""
|
546
|
+
|
547
|
+
self.patch_list = []
|
548
|
+
self.trianges = None
|
549
|
+
self.points = None
|
550
|
+
for patch, entry in enumerate(triangles_and_points_list):
|
551
|
+
assert len(entry) == 2, 'expecting pair of arrays (triangles, points) for each patch'
|
552
|
+
triangles, points = entry
|
553
|
+
tri_patch = rqstp.TriangulatedPatch(self.model, patch_index = patch, crs_uuid = self.crs_uuid)
|
554
|
+
tri_patch.set_from_triangles_and_points(triangles, points)
|
555
|
+
self.patch_list.append(tri_patch)
|
556
|
+
self.uuid = bu.new_uuid()
|
557
|
+
|
498
558
|
def set_from_point_set(self,
|
499
559
|
point_set,
|
500
560
|
convexity_parameter = 5.0,
|
@@ -927,10 +987,10 @@ class Surface(rqsb.BaseSurface):
|
|
927
987
|
for patch in self.patch_list:
|
928
988
|
patch.vertical_rescale_points(ref_depth, scaling_factor)
|
929
989
|
|
930
|
-
def line_intersection(self, line_p, line_v, line_segment = False):
|
990
|
+
def line_intersection(self, line_p, line_v, line_segment = False, patch = None):
|
931
991
|
"""Returns x,y,z of an intersection point of straight line with the surface, or None if no intersection found."""
|
932
992
|
|
933
|
-
t, p = self.triangles_and_points()
|
993
|
+
t, p = self.triangles_and_points(patch = patch)
|
934
994
|
tp = p[t]
|
935
995
|
intersects = meet.line_triangles_intersects(line_p, line_v, tp, line_segment = line_segment)
|
936
996
|
indices = meet.intersects_indices(intersects)
|
@@ -938,21 +998,22 @@ class Surface(rqsb.BaseSurface):
|
|
938
998
|
return None
|
939
999
|
return intersects[indices[0]]
|
940
1000
|
|
941
|
-
def sample_z_at_xy_points(self, points, multiple_handling = 'any'):
|
1001
|
+
def sample_z_at_xy_points(self, points, multiple_handling = 'any', patch = None):
|
942
1002
|
"""Returns interpolated z values for an array of xy values.
|
943
1003
|
|
944
1004
|
arguments:
|
945
1005
|
points (numpy float array of shape (..., 2 or 3)): xy points to sample surface at (z values ignored)
|
946
1006
|
multiple_handling (str, default 'any'): one of 'any', 'minimum', 'maximum', 'exception'
|
1007
|
+
patch (int, optional): patch index; if None, results are for the full surface
|
947
1008
|
|
948
1009
|
returns:
|
949
1010
|
numpy float array of shape points.shape[:-1] being z values interpolated from the surface z values
|
950
1011
|
|
951
1012
|
notes:
|
952
1013
|
points must be in the same crs as the surface;
|
953
|
-
NaN will be set for any points that do not intersect with the surface in the xy projection;
|
954
|
-
multiple_handling argument controls behaviour when one sample point intersects
|
955
|
-
|
1014
|
+
NaN will be set for any points that do not intersect with the patch or surface in the xy projection;
|
1015
|
+
multiple_handling argument controls behaviour when one sample point intersects more than once:
|
1016
|
+
'any' a random one of the intersection z values is returned; 'minimum' or 'maximum': the
|
956
1017
|
numerical min or max of the z values is returned; 'exception': a ValueError is raised
|
957
1018
|
"""
|
958
1019
|
|
@@ -964,7 +1025,7 @@ class Surface(rqsb.BaseSurface):
|
|
964
1025
|
else:
|
965
1026
|
sample_xy = np.zeros((points.size // 2, 3), dtype = float)
|
966
1027
|
sample_xy[:, :2] = points.reshape((-1, 2))
|
967
|
-
t, p = self.triangles_and_points()
|
1028
|
+
t, p = self.triangles_and_points(patch = patch)
|
968
1029
|
p_list = vec.points_in_triangles_njit(sample_xy, p[t], 1)
|
969
1030
|
vertical = np.array((0.0, 0.0, 1.0), dtype = float)
|
970
1031
|
z = np.full(sample_xy.shape[0], np.NaN, dtype = float)
|
@@ -983,17 +1044,18 @@ class Surface(rqsb.BaseSurface):
|
|
983
1044
|
raise ValueError(f'multiple {self.title} surface intersections at xy: {sample_xy[p_index]}')
|
984
1045
|
return z.reshape(points.shape[:-1])
|
985
1046
|
|
986
|
-
def normal_vectors(self, add_as_property: bool = False) -> np.ndarray:
|
987
|
-
"""Returns the normal vectors for each triangle in the surface.
|
988
|
-
|
1047
|
+
def normal_vectors(self, add_as_property: bool = False, patch = None) -> np.ndarray:
|
1048
|
+
"""Returns the normal vectors for each triangle in the patch or surface.
|
1049
|
+
|
989
1050
|
arguments:
|
990
|
-
add_as_property (bool): if True, face_surface_normal_vectors_array is added as a property to the model
|
1051
|
+
add_as_property (bool): if True, face_surface_normal_vectors_array is added as a property to the model
|
1052
|
+
patch (int, optional): patch index; if None, normal vectors for triangles in all patches are returned
|
991
1053
|
|
992
1054
|
returns:
|
993
|
-
normal_vectors_array (np.ndarray): the normal vectors corresponding to each triangle in the surface
|
1055
|
+
normal_vectors_array (np.ndarray): the normal vectors corresponding to each triangle in the surface
|
994
1056
|
"""
|
995
1057
|
crs = rqc.Crs(self.model, uuid = self.crs_uuid)
|
996
|
-
triangles, points = self.triangles_and_points()
|
1058
|
+
triangles, points = self.triangles_and_points(patch = patch)
|
997
1059
|
if crs.xy_units != crs.z_units:
|
998
1060
|
points = points.copy()
|
999
1061
|
wam.convert_lengths(points[:, 2], crs.z_units, crs.xy_units)
|
@@ -1053,13 +1115,13 @@ class Surface(rqsb.BaseSurface):
|
|
1053
1115
|
return ep
|
1054
1116
|
|
1055
1117
|
def resampled_surface(self, title = None):
|
1056
|
-
"""Creates a new
|
1057
|
-
|
1118
|
+
"""Creates a new surface which is a refined version of this surface; each triangle is divided equally into 4 new triangles.
|
1119
|
+
|
1058
1120
|
arguments:
|
1059
|
-
title (str):
|
1060
|
-
|
1121
|
+
title (str): title for the output triangulated set, if None the title will be inherited from the input surface
|
1122
|
+
|
1061
1123
|
returns:
|
1062
|
-
resqpy.surface.Surface object, with extra_metadata ('resampled from surface':
|
1124
|
+
resqpy.surface.Surface object, with extra_metadata ('resampled from surface': uuid), where uuid is for the original surface uuid
|
1063
1125
|
"""
|
1064
1126
|
rt, rp = self.triangles_and_points()
|
1065
1127
|
edge1 = np.mean(rp[rt[:]][:, ::2, :], axis = 1)
|
@@ -34,8 +34,10 @@ class AnyTimeSeries(BaseResqpy):
|
|
34
34
|
dt_text = rqet.find_tag_text(child, 'DateTime')
|
35
35
|
assert dt_text, 'missing DateTime field in xml for time series'
|
36
36
|
year_offset = rqet.find_tag_int(child, 'YearOffset')
|
37
|
-
if year_offset:
|
37
|
+
if year_offset is not None:
|
38
38
|
assert self.timeframe == 'geologic'
|
39
|
+
if year_offset > 0:
|
40
|
+
log.warning(f'positive year offset in xml indicates future geological time: {year_offset}')
|
39
41
|
self.timestamps.append(year_offset) # todo: trim and check timestamp
|
40
42
|
else:
|
41
43
|
assert self.timeframe == 'human'
|
@@ -29,7 +29,7 @@ class GeologicTimeSeries(ats.AnyTimeSeries):
|
|
29
29
|
|
30
30
|
note:
|
31
31
|
if instantiating from an existing RESQML time series, its Time entries must all have YearOffset data
|
32
|
-
which should be large negative integers
|
32
|
+
which should be large negative integers (or zero if reaching the current era)
|
33
33
|
|
34
34
|
:meta common:
|
35
35
|
"""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{resqpy-4.14.3 → resqpy-4.15.0}/resqpy/derived_model/_add_edges_per_column_property_array.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|