def _load_from_xml(self): """Loads class specific attributes from xml for an existing RESQML object; called from BaseResqpy.""" root_node = self.root assert root_node is not None assert rqet.find_tag_text(root_node, 'OrderingCriteria') == 'age', \ 'stratigraphic column rank interpretation ordering criterion must be age' self.domain = rqet.find_tag_text(root_node, 'Domain') self.feature_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(root_node, ['InterpretedFeature', 'UUID'])) self.has_occurred_during = rqo.extract_has_occurred_during(root_node) self.index = rqet.find_tag_int(root_node, 'Index') self.units = [] for su_node in rqet.list_of_tag(root_node, 'StratigraphicUnits'): index = rqet.find_tag_int(su_node, 'Index') unit_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(su_node, ['Unit', 'UUID'])) assert index is not None and unit_uuid is not None assert self.model.type_of_uuid( unit_uuid, strip_obj=True) == 'StratigraphicUnitInterpretation' self.units.append( (index, StratigraphicUnitInterpretation(self.model, uuid=unit_uuid))) self._sort_units() self.contacts = [] for contact_node in rqet.list_of_tag(root_node, 'ContactInterpretation'): self.contacts.append( BinaryContactInterpretation(self.model, existing_xml_node=contact_node)) self._sort_contacts()
def _load_from_xml(self): root_node = self.root self.domain = rqet.find_tag_text(root_node, 'Domain') interp_feature_ref_node = rqet.find_tag(root_node, 'InterpretedFeature') assert interp_feature_ref_node is not None self.feature_root = self.model.referenced_node(interp_feature_ref_node) if self.feature_root is not None: self.tectonic_boundary_feature = TectonicBoundaryFeature(self.model, uuid = self.feature_root.attrib['uuid'], feature_name = self.model.title_for_root( self.feature_root)) self.main_has_occurred_during = extract_has_occurred_during(root_node) self.is_listric = rqet.find_tag_bool(root_node, 'IsListric') self.is_normal = (self.is_listric is None) self.maximum_throw = rqet.find_tag_float(root_node, 'MaximumThrow') # todo: check that type="eml:LengthMeasure" is simple float self.mean_azimuth = rqet.find_tag_float(root_node, 'MeanAzimuth') self.mean_dip = rqet.find_tag_float(root_node, 'MeanDip') throw_interpretation_nodes = rqet.list_of_tag(root_node, 'ThrowInterpretation') if throw_interpretation_nodes is not None and len(throw_interpretation_nodes): self.throw_interpretation_list = [] for ti_node in throw_interpretation_nodes: hod_pair = extract_has_occurred_during(ti_node) throw_kind_list = rqet.list_of_tag(ti_node, 'Throw') for tk_node in throw_kind_list: self.throw_interpretation_list.append((tk_node.text, hod_pair))
def _process_complex_types(schema, complex_types): for completon in rqet.list_of_tag(schema, 'complexType'): name = completon.attrib['name'] doc = rqet.find_nested_tags_text(completon, ['annotation', 'documentation']) content = rqet.find_tag(completon, 'complexContent') extension = rqet.find_tag(content, 'extension') if extension is None: e_base = None else: e_base = extension.attrib['base'] content = extension if content is None: content = completon sequence = rqet.find_tag(content, 'sequence') if sequence is None: sequence = content element_list = [] e_nodes = rqet.list_of_tag(sequence, 'element') if e_nodes is not None: for element in e_nodes: e_name = element.attrib['name'] flavour = element.attrib[ 'type'] # could strip prefix here? min_occurs = element.get('minOccurs') max_occurs = element.get('maxOccurs') element_list.append( (e_name, flavour, min_occurs, max_occurs)) complex_types[name] = (e_base, element_list, doc)
def cases(self): """Returns a list of simulation case strings as found in the main xml file.""" if self.case_list is not None: return self.case_list try: xml_file = os.path.join(self.path, 'main.xml') if self.zipped: with zf.ZipFile(self.zip_file) as zfp: with zfp.open(xml_file) as fp: tree = rqet.parse(fp) root = tree.getroot() else: assert os.path.exists( xml_file), 'could not find vdb main xml file: ' + xml_file with open(xml_file, 'r') as fp: tree = rqet.parse(fp) root = tree.getroot() caselist = rqet.list_of_tag(rqet.find_tag(root, 'CASELIST'), 'CASE') self.case_list = [] for case in caselist: self.case_list.append(str(case.attrib['Name']).strip()) if self.use_case is None and len(self.case_list): self.use_case = self.case_list[0] except Exception: log.exception('failed to extract case list') return self.case_list
def __load_from_xml_refz(self, support_geom_node): self.flavour = 'ref&z' # assert rqet.node_type(support_geom_node) == 'Point3dFromRepresentationLatticeArray' # only this supported for now self.ref_uuid = rqet.find_nested_tags_text( support_geom_node, ['SupportingRepresentation', 'UUID']) assert self.ref_uuid, 'missing supporting representation info in xml for z-value mesh' self.ref_mesh = Mesh(self.model, uuid=self.ref_uuid) assert self.nj == self.ref_mesh.nj and self.ni == self.ref_mesh.ni # only this supported for now niosr_node = rqet.find_tag(support_geom_node, 'NodeIndicesOnSupportingRepresentation') start_value = rqet.find_tag_int(niosr_node, 'StartValue') assert start_value == 0, 'only full use of supporting mesh catered for at present' offset_nodes = rqet.list_of_tag( niosr_node, 'Offset') # first occurrence for FastestAxis, ie. I; 2nd for J assert len( offset_nodes ) == 2, 'missing (or too many) offset nodes in xml for regular mesh (lattice)' for j_or_i in range(2): # 0 = J, 1 = I assert rqet.node_type( offset_nodes[j_or_i] ) == 'IntegerConstantArray', 'variable step not catered for' assert rqet.find_tag_int( offset_nodes[j_or_i], 'Value') == 1, 'step other than 1 not catered for' count = rqet.find_tag_int(offset_nodes[j_or_i], 'Count') assert count == (self.nj, self.ni)[j_or_i] - 1, \ 'unexpected value for count in xml spacing info for regular mesh (lattice)'
def _load_from_xml(self): """Loads class specific attributes from xml for an existing RESQML object; called from BaseResqpy.""" rank_node_list = rqet.list_of_tag(self.root, 'Ranks') assert rank_node_list is not None, 'no stratigraphic column ranks in xml for stratigraphic column' for rank_node in rank_node_list: rank = StratigraphicColumnRank(self.model, uuid=rqet.find_tag_text( rank_node, 'UUID')) self.add_rank(rank)
def _process_simple_types(schema, simple_types): for simpleton in rqet.list_of_tag(schema, 'simpleType'): name = simpleton.attrib['name'] doc = rqet.find_nested_tags_text(simpleton, ['annotation', 'documentation']) restriction = rqet.find_tag(simpleton, 'restriction') restrict = None if restriction is None else rqet.stripped_of_prefix( restriction.attrib['base']) value_list = [] enum_list = rqet.list_of_tag(restriction, 'enumeration') if enum_list is not None: for enumeration in enum_list: value = enumeration.attrib['value'] v_doc = rqet.find_nested_tags_text( enumeration, ['annotation', 'documentation']) if v_doc is not None: v_doc = str(v_doc).replace('\n', ' ') value_list.append((value, v_doc)) simple_types[name] = (restrict, value_list, doc)
def _load_from_xml(self): root_node = self.root for v_node in rqet.list_of_tag(root_node, 'Value'): key = rqet.find_tag_int(v_node, 'Key') value = rqet.find_tag_text(v_node, 'Value') assert key not in self.str_dict, 'key value ' + str( key) + ' occurs more than once in string lookup table xml' self.str_dict[key] = value if self.min_index is None or key < self.min_index: self.min_index = key if self.max_index is None or key > self.max_index: self.max_index = key
def _cached_part_array_ref_get_node_points(part_node, dtype): patch_list = rqet.list_of_tag(part_node, 'PatchOfPoints') assert len(patch_list) == 1 # todo: handle more than one patch of points first_values_node = rqet.find_tag(patch_list[0], 'Points') if first_values_node is None: return None # could treat as fatal error if dtype is None: dtype = 'float' else: assert dtype in ['float', float, np.float32, np.float64] tag = 'Coordinates' return first_values_node, tag, dtype
def _load_from_xml(self): time_series_root = self.root assert time_series_root is not None # for human timeframe series, timestamps is an ordered list of timestamp strings in resqml/iso format # for geological timeframe series, timestamps is an ordered list of ints being the year offsets from present self.timestamps = [] for child in rqet.list_of_tag(time_series_root, 'Time'): dt_text = rqet.find_tag_text(child, 'DateTime') assert dt_text, 'missing DateTime field in xml for time series' year_offset = rqet.find_tag_int(child, 'YearOffset') if year_offset: assert self.timeframe == 'geologic' self.timestamps.append(year_offset) # todo: trim and check timestamp else: assert self.timeframe == 'human' self.timestamps.append(dt_text) # todo: trim and check timestamp self.timestamps.sort()
def _load_from_xml(self): assert self.root is not None # polyline set xml node specified root = self.root self.rep_int_root = self.model.referenced_node(rqet.find_tag(root, 'RepresentedInterpretation')) for patch_node in rqet.list_of_tag(root, 'LinePatch'): # Loop over all LinePatches - likely just the one assert patch_node is not None # Required field geometry_node = rqet.find_tag(patch_node, 'Geometry') assert geometry_node is not None # Required field crs_root = self.model.referenced_node(rqet.find_tag(geometry_node, 'LocalCrs')) assert crs_root is not None # Required field self.crs_uuid = rqet.uuid_for_part_root(crs_root) assert self.crs_uuid is not None # Required field closed_node = rqet.find_tag(patch_node, 'ClosedPolylines') assert closed_node is not None # Required field # The ClosedPolylines could be a BooleanConstantArray, or a BooleanArrayFromIndexArray closed_array = self.get_bool_array(closed_node) count_node = rqet.find_tag(patch_node, 'NodeCountPerPolyline') load_hdf5_array(self, count_node, 'count_perpol', tag = 'Values') points_node = rqet.find_tag(geometry_node, 'Points') assert points_node is not None # Required field load_hdf5_array(self, points_node, 'coordinates', tag = 'Coordinates') # Check that the number of bools aligns with the number of count_perpoly # Check that the total of the count_perpoly aligns with the number of coordinates assert len(self.count_perpol) == len(closed_array) assert np.sum(self.count_perpol) == len(self.coordinates) subpolys = self.convert_to_polylines(closed_array, self.count_perpol, self.coordinates, self.crs_uuid, self.rep_int_root) # Check we have the right number of polygons assert len(subpolys) == len(self.count_perpol) # Remove duplicate coordinates and count arrays (exist in polylines now) # delattr(self,'coordinates') # delattr(self,'count_perpol') self.polys.extend(subpolys)
def _load_from_xml(self): root_node = self.root self.domain = rqet.find_tag_text(root_node, 'Domain') interp_feature_ref_node = rqet.find_tag(root_node, 'InterpretedFeature') assert interp_feature_ref_node is not None self.feature_root = self.model.referenced_node(interp_feature_ref_node) if self.feature_root is not None: self.genetic_boundary_feature = GeneticBoundaryFeature(self.model, kind = 'geobody boundary', uuid = self.feature_root.attrib['uuid'], feature_name = self.model.title_for_root( self.feature_root)) self.has_occurred_during = extract_has_occurred_during(root_node) br_node_list = rqet.list_of_tag(root_node, 'BoundaryRelation') if br_node_list is not None and len(br_node_list) > 0: self.boundary_relation_list = [] for br_node in br_node_list: self.boundary_relation_list.append(br_node.text)
def _cached_part_array_ref_get_node_values(part_node, dtype): patch_list = rqet.list_of_tag(part_node, 'PatchOfValues') assert len(patch_list) == 1 # todo: handle more than one patch of values first_values_node = rqet.find_tag(patch_list[0], 'Values') if first_values_node is None: return None # could treat as fatal error if dtype is None: array_type = rqet.node_type(first_values_node) assert array_type is not None if array_type == 'DoubleHdf5Array': dtype = 'float' elif array_type == 'IntegerHdf5Array': dtype = 'int' elif array_type == 'BooleanHdf5Array': dtype = 'bool' else: raise ValueError('array type not catered for: ' + str(array_type)) tag = 'Values' return first_values_node, tag, dtype
def __load_from_xml_regz(self, support_geom_node): self.flavour = 'reg&z' orig_node = rqet.find_tag(support_geom_node, 'Origin') self.regular_origin = (rqet.find_tag_float(orig_node, 'Coordinate1'), rqet.find_tag_float(orig_node, 'Coordinate2'), rqet.find_tag_float(orig_node, 'Coordinate3')) assert self.regular_origin is not None, 'origin missing in xml for reg&z mesh (lattice)' offset_nodes = rqet.list_of_tag( support_geom_node, 'Offset') # first occurrence for FastestAxis, ie. I; 2nd for J assert len( offset_nodes ) == 2, 'missing (or too many) offset nodes in xml for reg&z mesh (lattice)' self.regular_dxyz_dij = np.empty((2, 3)) for j_or_i in range(2): # 0 = J, 1 = I axial_offset_node = rqet.find_tag(offset_nodes[j_or_i], 'Offset') assert axial_offset_node is not None, 'missing offset offset node in xml' self.regular_dxyz_dij[1 - j_or_i] = (rqet.find_tag_float( axial_offset_node, 'Coordinate1'), rqet.find_tag_float( axial_offset_node, 'Coordinate2'), rqet.find_tag_float( axial_offset_node, 'Coordinate3')) if not maths.isclose( vec.dot_product(self.regular_dxyz_dij[1 - j_or_i], self.regular_dxyz_dij[1 - j_or_i]), 1.0): log.warning( 'non-orthogonal axes and/or scaling in xml for regular mesh (lattice)' ) spacing_node = rqet.find_tag(offset_nodes[j_or_i], 'Spacing') stride = rqet.find_tag_float(spacing_node, 'Value') count = rqet.find_tag_int(spacing_node, 'Count') assert stride is not None and count is not None, 'missing spacing info in xml' assert count == (self.nj, self.ni)[j_or_i] - 1, \ 'unexpected value for count in xml spacing info for regular mesh (lattice)' assert stride > 0.0, 'spacing distance is not positive in xml for regular mesh (lattice)' self.regular_dxyz_dij[1 - j_or_i] *= stride
def _load_from_xml(self): """Loads the wellbore marker frame object from an xml node (and associated hdf5 data). note: this method is not usually called directly """ wellbore_marker_frame_root = self.root assert wellbore_marker_frame_root is not None self.trajectory_uuid = bu.uuid_from_string( rqet.find_nested_tags_text(wellbore_marker_frame_root, ['Trajectory', 'UUID'])) self.trajectory = Trajectory(parent_model=self.model, uuid=self.trajectory_uuid) # list of Wellbore markers self.marker_list = [] for i, tag in enumerate( rqet.list_of_tag(wellbore_marker_frame_root, 'WellboreMarker')): marker_obj = WellboreMarker(parent_model=self.model, parent_frame=self, marker_index=i, marker_node=tag) self.marker_list.append(marker_obj) self.node_count = rqet.find_tag_int(wellbore_marker_frame_root, 'NodeCount') load_hdf5_array(self, rqet.find_tag(wellbore_marker_frame_root, 'NodeMd'), "node_mds", tag='Values') assert self.node_count == len( self.node_mds), 'node count does not match hdf5 array' assert len( self.marker_list ) == self.node_count, 'wellbore marker list does not contain correct node count'
def _load_from_xml(self): assert self.root is not None self.patch_count = rqet.count_tag(self.root, 'NodePatch') assert self.patch_count, 'no patches found in xml for point set' self.patch_array_list = [None for _ in range(self.patch_count)] patch_index = 0 for child in rqet.list_of_tag(self.root, 'NodePatch'): point_count = rqet.find_tag_int(child, 'Count') geom_node = rqet.find_tag(child, 'Geometry') assert geom_node is not None, 'geometry missing in xml for point set patch' crs_uuid = rqet.find_nested_tags_text(geom_node, ['LocalCrs', 'UUID']) assert crs_uuid, 'crs uuid missing in geometry xml for point set patch' self.check_crs_match(crs_uuid) ext_uuid = rqet.find_nested_tags_text(geom_node, ['Points', 'Coordinates', 'HdfProxy', 'UUID']) assert ext_uuid, 'missing hdf5 uuid in geometry xml for point set patch' hdf5_path = rqet.find_nested_tags_text(geom_node, ['Points', 'Coordinates', 'PathInHdfFile']) assert hdf5_path, 'missing internal hdf5 path in geometry xml for point set patch' self.patch_ref_list.append((ext_uuid, hdf5_path, point_count)) patch_index += 1 ref_node = rqet.find_tag(self.root, 'RepresentedInterpretation') if ref_node is not None: interp_root = self.model.referenced_node(ref_node) self.set_represented_interpretation_root(interp_root)
def _create_reciprocal_relationship(model, node_a, rel_type_a, node_b, rel_type_b, avoid_duplicates=True): """Adds a node to each of a pair of trees in the rels forest, to represent a two-way relationship.""" def id_str(uuid): stringy = str(uuid) if not (rqet.pretend_to_be_fesapi or rqet.use_fesapi_quirks) or not stringy[0].isdigit(): return stringy return '_' + stringy assert (model.rels_present) if node_a is None or node_b is None: log.error('attempt to create relationship with missing object') return uuid_a = node_a.attrib['uuid'] obj_type_a = rqet.stripped_of_prefix( rqet.content_type(node_a.attrib[ns['xsi'] + 'type'])) part_name_a = rqet.part_name_for_object(obj_type_a, uuid_a) rel_part_name_a = rqet.rels_part_name_for_part(part_name_a) (rel_uuid_a, rel_tree_a) = model.rels_forest[rel_part_name_a] rel_root_a = rel_tree_a.getroot() uuid_b = node_b.attrib['uuid'] obj_type_b = rqet.stripped_of_prefix( rqet.content_type(node_b.attrib[ns['xsi'] + 'type'])) part_name_b = rqet.part_name_for_object(obj_type_b, uuid_b) rel_part_name_b = rqet.rels_part_name_for_part(part_name_b) (rel_uuid_b, rel_tree_b) = model.rels_forest[rel_part_name_b] rel_root_b = rel_tree_b.getroot() create_a = True if avoid_duplicates: existing_rel_nodes = rqet.list_of_tag(rel_root_a, 'Relationship') for existing in existing_rel_nodes: if (rqet.stripped_of_prefix(existing.attrib['Type']) == rel_type_a and existing.attrib['Target'] == part_name_b): create_a = False break if create_a: rel_a = rqet.SubElement(rel_root_a, ns['rels'] + 'Relationship') rel_a.set( 'Id', id_str(uuid_b) ) # NB: fesapi prefixes uuid with _ for some rels only (where uuid starts with a digit) rel_a.set('Type', ns_url['rels_ext'] + rel_type_a) rel_a.set('Target', part_name_b) create_b = True if avoid_duplicates: existing_rel_nodes = rqet.list_of_tag(rel_root_b, 'Relationship') for existing in existing_rel_nodes: if (rqet.stripped_of_prefix(existing.attrib['Type']) == rel_type_b and existing.attrib['Target'] == part_name_a): create_b = False break if create_b: rel_b = rqet.SubElement(rel_root_b, ns['rels'] + 'Relationship') rel_b.set( 'Id', id_str(uuid_a) ) # NB: fesapi prefixes uuid with _ for some rels only (where uuid starts with a digit) rel_b.set('Type', ns_url['rels_ext'] + rel_type_b) rel_b.set('Target', part_name_a)