def _set_mesh_from_df(self): """Creates Mesh object; called before writing to hdf5 or creating xml.""" # note: actual data is stored in related Property if realization number is present, directly in Mesh otherwise assert self.n_rows == len(self.df) assert self.n_cols == len(self.df.columns) if self.mesh is None: origin = (0.0, 0.0, 0.0) dxyz_dij = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]) crs_uuids = self.model.uuids(obj_type='LocalDepth3dCrs') if len(crs_uuids) == 0: crs = rqc.Crs(self.model) crs.create_xml() crs_uuid = crs.uuid else: # use any available crs crs_uuid = crs_uuids[0] if self.realization is None: self.mesh = rqs.Mesh(self.model, mesh_flavour='reg&z', ni=self.n_cols, nj=self.n_rows, dxyz_dij=dxyz_dij, origin=origin, z_values=np.array(self.df), crs_uuid=crs_uuid) else: self.mesh = rqs.Mesh(self.model, mesh_flavour='regular', ni=self.n_cols, nj=self.n_rows, dxyz_dij=dxyz_dij, origin=origin, crs_uuid=crs_uuid) self.mesh.write_hdf5() mesh_root = self.mesh.create_xml(title=self.title) rqet.create_metadata_xml(mesh_root, {'dataframe': 'true'}) if self.realization is not None: self.pc = rqp.PropertyCollection() self.pc.set_support(support=self.mesh) dataframe_pk_uuid = self.model.uuid(obj_type='PropertyKind', title='dataframe') if dataframe_pk_uuid is None: dataframe_pk = rqp.PropertyKind(self.model, title='dataframe', example_uom='Euc') dataframe_pk.create_xml() dataframe_pk_uuid = dataframe_pk.uuid self.pc.add_cached_array_to_imported_list( np.array(self.df), 'dataframe', self.title, uom='Euc', property_kind='dataframe', local_property_kind_uuid=dataframe_pk_uuid, realization=self.realization, indexable_element='nodes') self.pc.write_hdf5_for_imported_list() self.pc.create_xml_for_imported_list_and_add_parts_to_model()
def _add_single_surface(model, surf_file, surface_file_format, surface_role, quad_triangles, crs_uuid, rq_class, hdf5_file, h5_mode, make_horizon_interpretations_and_features, ext_uuid): _, short_name = os.path.split(surf_file) dot = short_name.rfind('.') if dot > 0: short_name = short_name[:dot] log.info('surface ' + short_name + ' processing file: ' + surf_file + ' using format: ' + surface_file_format) if rq_class == 'surface': if surface_file_format == 'GOCAD-Tsurf': surface = rqs.Surface(model, tsurf_file = surf_file, surface_role = surface_role, quad_triangles = quad_triangles) else: surface = rqs.Surface(model, mesh_file = surf_file, mesh_format = surface_file_format, surface_role = surface_role, quad_triangles = quad_triangles) elif rq_class == 'mesh': if surface_file_format == 'GOCAD-Tsurf': log.info(f"Cannot convert a GOCAD-Tsurf to mesh, only to TriangulatedSurface - skipping file {surf_file}") return model else: surface = rqs.Mesh(model, mesh_file = surf_file, mesh_format = surface_file_format, mesh_flavour = 'reg&z', surface_role = surface_role, crs_uuid = crs_uuid) else: log.critical('this is impossible') # NB. surface may be either a Surface object or a Mesh object log.debug('appending to hdf5 file for surface file: ' + surf_file) surface.write_hdf5(hdf5_file, mode = h5_mode) if make_horizon_interpretations_and_features: feature = rqo.GeneticBoundaryFeature(model, kind = 'horizon', feature_name = short_name) feature.create_xml() interp = rqo.HorizonInterpretation(model, genetic_boundary_feature = feature, domain = 'depth') interp_root = interp.create_xml() surface.set_represented_interpretation_root(interp_root) surface.create_xml(ext_uuid, add_as_part = True, add_relationships = True, title = short_name + ' sourced from ' + surf_file, originator = None) return model
def _set_support_uuid_notnone_supportnone(collection, support_uuid, model): import resqpy.fault as rqf import resqpy.grid as grr import resqpy.surface as rqs import resqpy.unstructured as rug import resqpy.well as rqw support_part = model.part_for_uuid(support_uuid) assert support_part is not None, 'supporting representation part missing in model' collection.support_root = model.root_for_part(support_part) support_type = model.type_of_part(support_part) assert support_type is not None if support_type == 'obj_IjkGridRepresentation': collection.support = grr.any_grid(model, uuid=collection.support_uuid, find_properties=False) elif support_type == 'obj_WellboreFrameRepresentation': collection.support = rqw.WellboreFrame(model, uuid=collection.support_uuid) elif support_type == 'obj_BlockedWellboreRepresentation': collection.support = rqw.BlockedWell(model, uuid=collection.support_uuid) elif support_type == 'obj_Grid2dRepresentation': collection.support = rqs.Mesh(model, uuid=collection.support_uuid) elif support_type == 'obj_GridConnectionSetRepresentation': collection.support = rqf.GridConnectionSet( model, uuid=collection.support_uuid) elif support_type == 'obj_TriangulatedSetRepresentation': collection.support = rqs.Surface(model, uuid=collection.support_uuid) elif support_type == 'obj_UnstructuredGridRepresentation': collection.support = rug.UnstructuredGrid(model, uuid=collection.support_uuid, geometry_required=False, find_properties=False) elif support_type == 'obj_WellboreMarkerFrameRepresentation': collection.support = rqw.WellboreMarkerFrame( model, uuid=collection.support_uuid) else: raise TypeError( 'unsupported property supporting representation class: ' + str(support_type))
def test_vertical_prism_grid_from_seed_points_and_surfaces(tmp_path): seed(23487656) # to ensure test reproducibility epc = os.path.join(tmp_path, 'voronoi_prism_grid.epc') model = rq.new_model(epc) crs = rqc.Crs(model) crs.create_xml() # define a boundary polyline: b_count = 7 boundary_points = np.empty((b_count, 3)) radius = 1000.0 for i in range(b_count): theta = -vec.radians_from_degrees(i * 360.0 / b_count) boundary_points[i] = (2.0 * radius * maths.cos(theta), radius * maths.sin(theta), 0.0) boundary = rql.Polyline(model, set_coord=boundary_points, set_bool=True, set_crs=crs.uuid, title='rough ellipse') boundary.write_hdf5() boundary.create_xml() # derive a larger area of interest aoi = rql.Polyline.from_scaled_polyline(boundary, 1.1, title='area of interest') aoi.write_hdf5() aoi.create_xml() min_xy = np.min(aoi.coordinates[:, :2], axis=0) - 50.0 max_xy = np.max(aoi.coordinates[:, :2], axis=0) + 50.0 print(f'***** min max xy aoi+ : {min_xy} {max_xy}') # debug # create some seed points within boundary seed_count = 5 seeds = rqs.PointSet(model, crs_uuid=crs.uuid, polyline=boundary, random_point_count=seed_count, title='seeds') seeds.write_hdf5() seeds.create_xml() seeds_xy = seeds.single_patch_array_ref(0) for seed_xy in seeds_xy: assert aoi.point_is_inside_xy( seed_xy), f'seed point {seed_xy} outwith aoi' print( f'***** min max xy seeds : {np.min(seeds_xy, axis = 0)} {np.max(seeds_xy, axis = 0)}' ) # debug # create some horizon surfaces ni, nj = 21, 11 lattice = rqs.Mesh(model, crs_uuid=crs.uuid, mesh_flavour='regular', ni=ni, nj=nj, origin=(min_xy[0], min_xy[1], 0.0), dxyz_dij=np.array([[ (max_xy[0] - min_xy[0]) / (ni - 1), 0.0, 0.0 ], [0.0, (max_xy[1] - min_xy[1]) / (nj - 1), 0.0]])) lattice.write_hdf5() lattice.create_xml() horizons = [] for i in range(4): horizon_depths = 1000.0 + 100.0 * i + 20.0 * (np.random.random( (nj, ni)) - 0.5) horizon_mesh = rqs.Mesh(model, crs_uuid=crs.uuid, mesh_flavour='ref&z', ni=ni, nj=nj, z_values=horizon_depths, z_supporting_mesh_uuid=lattice.uuid, title='h' + str(i)) horizon_mesh.write_hdf5() horizon_mesh.create_xml() horizon_surface = rqs.Surface(model, crs_uuid=crs.uuid, mesh=horizon_mesh, quad_triangles=True, title=horizon_mesh.title) horizon_surface.write_hdf5() horizon_surface.create_xml() horizons.append(horizon_surface) # create a re-triangulated Voronoi vertical prism grid grid = rug.VerticalPrismGrid.from_seed_points_and_surfaces( model, seeds_xy, horizons, aoi, title="giant's causeway") assert grid is not None grid.write_hdf5() grid.create_xml() # check cell thicknesses are in expected range thick = grid.thickness() assert np.all(thick >= 80.0) assert np.all(thick <= 120.0) model.store_epc()
def __init__( self, model, support_root=None, # deprecated uuid=None, df=None, uom_list=None, realization=None, title='dataframe', column_lookup_uuid=None, uom_lookup_uuid=None, extra_metadata=None): """Create a new Dataframe object from either a previously stored property or a pandas dataframe. arguments: model (model.Model): the model to which the new Dataframe will be attached support_root (lxml.Element, DEPRECATED): use uuid instead uuid (uuid.UUID, optional): the uuid of an existing Grid2dRepresentation object acting as support for a dataframe property (or holding the dataframe as z values) df (pandas.DataFrame, optional): a dataframe from which the new Dataframe is to be created; if both uuid (or support_root) and df are supplied, realization must not be None and a new realization property will be created uom_list (list of str, optional): a list holding the units of measure for each column; if present, length of list must match number of columns in df; ignored if uuid or support_root is not None realization (int, optional): if present, the realization number of the RESQML property holding the dataframe title (str, default 'dataframe'): used as the citation title for the Mesh (and property); ignored if uuid or support_root is not None column_lookup_uuid (uuid, optional): if present, the uuid of a string lookup table holding the column names; if present, the contents and order of the table must match the columns in the dataframe; if absent, a new lookup table will be created; ignored if support_root is not None uom_lookup_uuid (uuid, optional): if present, the uuid of a string lookup table holding the units of measure for each column; if None and uom_list is present, a new table will be created; if both uom_list and uom_lookup_uuid are present, their contents must match; ignored if support_root is not None extra_metadata (dict, optional): if present, a dictionary of extra metadata items, str: str; ignored if uuid (or support_root) is not None returns: a newly created Dataframe object notes: when initialising from an existing RESQML object, the supporting mesh and its property should have been originally created using this class; when working with ensembles, each object of this class will only handle the data for one realization, though they may share a common support_root """ assert uuid is not None or support_root is not None or df is not None assert (uuid is None and support_root is None) or df is None or realization is not None if uuid is None: if support_root is not None: warnings.warn( "support_root parameter is deprecated, use uuid instead", DeprecationWarning) uuid = rqet.uuid_for_part_root(support_root) else: support_root = model.root_for_uuid(uuid) self.model = model self.df = None self.n_rows = self.n_cols = 0 self.uom_list = None self.realization = realization self.title = title self.mesh = None # only generated when needed for write_hdf5(), create_xml() self.pc = None # property collection; only generated when needed for write_hdf5(), create_xml() self.column_lookup_uuid = column_lookup_uuid self.column_lookup = None # string lookup table mapping column index (0 based) to column name self.uom_lookup_uuid = uom_lookup_uuid self.uom_lookup = None # string lookup table mapping column index (0 based) to uom self.extra_metadata = extra_metadata if uuid is not None: assert rqet.node_type(support_root) == 'obj_Grid2dRepresentation' self.mesh = rqs.Mesh(self.model, uuid=uuid) self.extra_metadata = self.mesh.extra_metadata assert 'dataframe' in self.extra_metadata and self.extra_metadata[ 'dataframe'] == 'true' self.title = self.mesh.title self.n_rows, self.n_cols = self.mesh.nj, self.mesh.ni cl_uuid = self.model.uuid(obj_type='StringTableLookup', related_uuid=uuid, title='dataframe columns') assert cl_uuid is not None, 'column name lookup table not found for dataframe' self.column_lookup = rqp.StringLookup(self.model, uuid=cl_uuid) self.column_lookup_uuid = self.column_lookup.uuid assert self.column_lookup.length() == self.n_cols ul_uuid = self.model.uuid(obj_type='StringTableLookup', related_uuid=uuid, title='dataframe units') if ul_uuid is not None: self.uom_lookup = rqp.StringLookup(self.model, uuid=ul_uuid) self.uom_lookup_uuid = self.uom_lookup.uuid self.uom_list = self.uom_lookup.get_list() da = self.mesh.full_array_ref( )[..., 2] # dataframe data as 2D numpy array, defaulting to z values in mesh existing_pc = rqp.PropertyCollection(support=self.mesh) existing_count = 0 if existing_pc is None else existing_pc.number_of_parts( ) if df is None: # existing dara, either in mesh or property if existing_count > 0: # use property data instead of z values if existing_count == 1: if self.realization is not None: assert existing_pc.realization_for_part( existing_pc.singleton()) == self.realization else: assert self.realization is not None, 'no realization specified when accessing ensemble dataframe' da = existing_pc.single_array_ref( realization=self.realization) assert da is not None and da.ndim == 2 and da.shape == ( self.n_rows, self.n_cols) else: assert realization is None self.df = pd.DataFrame(da, columns=self.column_lookup.get_list()) else: # both support_root and df supplied: add a new realisation if existing_count > 0: assert existing_pc.singleton( realization=self.realization ) is None, 'dataframe realization already exists' self.df = df.copy() assert len(self.df) == self.n_rows assert len(self.df.columns) == self.n_rows else: assert df is not None, 'no dataframe (or support root) provided when instantiating DataFrame object' self.df = df.copy() # todo: check data type of columns – restrict to numerical data self.n_rows = len(self.df) self.n_cols = len(self.df.columns) if column_lookup_uuid is not None: self.column_lookup = rqp.StringLookup(self.model, uuid=column_lookup_uuid) assert self.column_lookup is not None assert self.column_lookup.length() == self.n_cols assert all(self.df.columns == self.column_lookup.get_list() ) # exact match of column names required! if uom_lookup_uuid is not None: self.uom_lookup = rqp.StringLookup(self.model, uuid=uom_lookup_uuid) assert self.uom_lookup is not None if uom_list is not None: assert len(uom_list) == self.n_cols self.uom_list = uom_list.copy() if self.uom_lookup is not None: assert self.uom_list == self.uom_lookup.get_list() elif self.uom_lookup is not None: self.uom_list = self.uom_lookup.get_list()