Source code for openquake.hmtk.parsers.source_model.nrml04_parser
# -*- coding: utf-8 -*-# vim: tabstop=4 shiftwidth=4 softtabstop=4## LICENSE## Copyright (C) 2015-2023 GEM Foundation## The Hazard Modeller's Toolkit is free software: you can redistribute# it and/or modify it under the terms of the GNU Affero General Public# License as published by the Free Software Foundation, either version# 3 of the License, or (at your option) any later version.## You should have received a copy of the GNU Affero General Public License# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>## DISCLAIMER## The software Hazard Modeller's Toolkit (openquake.hmtk) provided herein# is released as a prototype implementation on behalf of# scientists and engineers working within the GEM Foundation (Global# Earthquake Model).## It is distributed for the purpose of open collaboration and in the# hope that it will be useful to the scientific, engineering, disaster# risk and software design communities.## The software is NOT distributed as part of GEM's OpenQuake suite# (https://www.globalquakemodel.org/tools-products) and must be considered as a# separate entity. The software provided herein is designed and implemented# by scientific staff. It is not developed to the design standards, nor# subject to same level of critical review by professional software# developers, as GEM's OpenQuake software suite.## Feedback and contribution to the software is welcome, and can be# directed to the hazard scientific staff of the GEM Model Facility# (hazard@globalquakemodel.org).## The Hazard Modeller's Toolkit (openquake.hmtk) is therefore distributed# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License# for more details.## The GEM Foundation, and the authors of the software, assume no# liability for use of the software."""Parser for input in a NRML format, with partial validation"""importrefromcopyimportcopyfromopenquake.baselib.nodeimportnode_from_xmlfromopenquake.baselib.generalimportdeprecatedfromopenquake.hazardlib.geo.pointimportPointfromopenquake.hazardlib.geo.lineimportLinefromopenquake.hazardlib.geo.polygonimportPolygonfromopenquake.hazardlibimportmfd,validfromopenquake.hazardlib.pmfimportPMFfromopenquake.hazardlib.geo.nodalplaneimportNodalPlanefromopenquake.hmtk.sources.source_modelimportmtkSourceModelfromopenquake.hmtk.sources.point_sourceimportmtkPointSourcefromopenquake.hmtk.sources.area_sourceimportmtkAreaSourcefromopenquake.hmtk.sources.simple_fault_sourceimportmtkSimpleFaultSourcefromopenquake.hmtk.sources.complex_fault_sourceimportmtkComplexFaultSourcefromopenquake.hmtk.parsers.source_model.baseimportBaseSourceModelParser
[docs]defstring_(string):""" Returns string or None """ifstring:returnstringelse:returnNone
[docs]deffloat_(value):""" Returns float of a value, or None """ifvalue:returnfloat(value)else:returnNone
[docs]defint_(value):""" Returns int or None """ifvalue:returnfloat(value)else:returnNone
[docs]defget_taglist(node):""" Return a list of tags (with NRML namespace removed) representing the order of the nodes within a node """return[re.sub(r"\{[^}]*\}","",copy(subnode.tag))forsubnodeinnode.nodes]
[docs]deflinestring_node_to_line(node,with_depth=False):""" Returns an instance of a Linestring node to :class: openquake.hazardlib.geo.line.Line """assert"LineString"innode.tagcrds=[float(x)forxinnode.nodes[0].text.split()]ifwith_depth:returnLine([Point(crds[iloc],crds[iloc+1],crds[iloc+2])forilocinrange(0,len(crds),3)])else:returnLine([Point(crds[iloc],crds[iloc+1])forilocinrange(0,len(crds),2)])
[docs]defnode_to_point_geometry(node):""" Reads the node and returns the point geometry, upper depth and lower depth """assert"pointGeometry"innode.tagforsubnodeinnode.nodes:if"Point"insubnode.tag:# Positionlon,lat=map(float,subnode.nodes[0].text.split())point=Point(lon,lat)elif"upperSeismoDepth"insubnode.tag:upper_depth=float_(subnode.text)elif"lowerSeismoDepth"insubnode.tag:lower_depth=float_(subnode.text)else:# Redundentpassassertlower_depth>upper_depthreturnpoint,upper_depth,lower_depth
[docs]defnode_to_area_geometry(node):""" Reads an area geometry node and returns the polygon, upper depth and lower depth """assert"areaGeometry"innode.tagforsubnodeinnode.nodes:if"Polygon"insubnode.tag:crds=[float(x)forxinsubnode.nodes[0].nodes[0].nodes[0].text.split()]polygon=Polygon([Point(crds[iloc],crds[iloc+1])forilocinrange(0,len(crds),2)])elif"upperSeismoDepth"insubnode.tag:upper_depth=float_(subnode.text)elif"lowerSeismoDepth"insubnode.tag:lower_depth=float_(subnode.text)else:# Redundentpassassertlower_depth>upper_depthreturnpolygon,upper_depth,lower_depth
[docs]defnode_to_simple_fault_geometry(node):""" Reads a simple fault geometry node and returns an OpenQuake representation :returns: trace - Trace of fault as instance """assert"simpleFaultGeometry"innode.tagforsubnodeinnode.nodes:if"LineString"insubnode.tag:trace=linestring_node_to_line(subnode,with_depth=False)elif"dip"insubnode.tag:dip=float(subnode.text)elif"upperSeismoDepth"insubnode.tag:upper_depth=float(subnode.text)elif"lowerSeismoDepth"insubnode.tag:lower_depth=float(subnode.text)else:# Redundentpassassertlower_depth>upper_depthreturntrace,dip,upper_depth,lower_depth
[docs]defnode_to_complex_fault_geometry(node):""" Reads a complex fault geometry node and returns an """assert"complexFaultGeometry"innode.tagintermediate_edges=[]forsubnodeinnode.nodes:if"faultTopEdge"insubnode.tag:top_edge=linestring_node_to_line(subnode.nodes[0],with_depth=True)elif"intermediateEdge"insubnode.tag:int_edge=linestring_node_to_line(subnode.nodes[0],with_depth=True)intermediate_edges.append(int_edge)elif"faultBottomEdge"insubnode.tag:bottom_edge=linestring_node_to_line(subnode.nodes[0],with_depth=True)else:# Redundentpassreturn[top_edge]+intermediate_edges+[bottom_edge]
[docs]defnode_to_scalerel(node):""" Parses a node to an instance of a supported scaling relation class """ifnotnode.text:returnNonereturnvalid.mag_scale_rel(node.text)
[docs]defnode_to_truncated_gr(node,bin_width=0.1):""" Parses truncated GR node to an instance of the :class: openquake.hazardlib.mfd.truncated_gr.TruncatedGRMFD """# Parse to float dictionaryifnotall([node.attrib[key]forkeyin["minMag","maxMag","aValue","bValue"]]):returnNonetgr=dict((key,float_(node.attrib[key]))forkeyinnode.attrib)returnmfd.truncated_gr.TruncatedGRMFD(min_mag=tgr["minMag"],max_mag=tgr["maxMag"],bin_width=bin_width,a_val=tgr["aValue"],b_val=tgr["bValue"],)
[docs]defnode_to_evenly_discretized(node):""" Parses the evenly discretized mfd node to an instance of the :class: openquake.hazardlib.mfd.evenly_discretized.EvenlyDiscretizedMFD, or to None if not all parameters are available """ifnotall([node.attrib["minMag"],node.attrib["binWidth"],node.nodes[0].text]):returnNone# Text to floatrates=[float(x)forxinnode.nodes[0].text.split()]returnmfd.evenly_discretized.EvenlyDiscretizedMFD(float(node.attrib["minMag"]),float(node.attrib["binWidth"]),rates)
[docs]defnode_to_mfd(node,taglist):""" Reads the node to return a magnitude frequency distribution """if"incrementalMFD"intaglist:mfd=node_to_evenly_discretized(node.nodes[taglist.index("incrementalMFD")])elif"truncGutenbergRichterMFD"intaglist:mfd=node_to_truncated_gr(node.nodes[taglist.index("truncGutenbergRichterMFD")])else:mfd=Nonereturnmfd
[docs]defnode_to_nodal_planes(node):""" Parses the nodal plane distribution to a PMF """ifnotlen(node):returnNonenpd_pmf=[]forplaneinnode.nodes:ifnotall(plane.attrib[key]forkeyinplane.attrib):# One plane fails - return NonereturnNonenpd=NodalPlane(float(plane.attrib["strike"]),float(plane.attrib["dip"]),float(plane.attrib["rake"]),)npd_pmf.append((float(plane.attrib["probability"]),npd))returnPMF(npd_pmf)
[docs]defnode_to_hdd(node):""" Parses the node to a hpyocentral depth distribution PMF """ifnotlen(node):returnNonehdds=[]forsubnodeinnode.nodes:ifnotall([subnode.attrib[key]forkeyin["depth","probability"]]):returnNonehdds.append((float(subnode.attrib["probability"]),float(subnode.attrib["depth"]),))returnPMF(hdds)
[docs]defparse_point_source_node(node,mfd_spacing=0.1):""" Returns an "areaSource" node into an instance of the :class: openquake.hmtk.sources.area.mtkAreaSource """assert"pointSource"innode.tagpnt_taglist=get_taglist(node)# Get metadatapoint_id,name,trt=(node.attrib["id"],node.attrib["name"],node.attrib["tectonicRegion"],)assertpoint_id# Defensive validation!# Process geometrylocation,upper_depth,lower_depth=node_to_point_geometry(node.nodes[pnt_taglist.index("pointGeometry")])# Process scaling relationmsr=node_to_scalerel(node.nodes[pnt_taglist.index("magScaleRel")])# Process aspect ratioaspect=float_(node.nodes[pnt_taglist.index("ruptAspectRatio")].text)# Process MFDmfd=node_to_mfd(node,pnt_taglist)# Process nodal planesnpds=node_to_nodal_planes(node.nodes[pnt_taglist.index("nodalPlaneDist")])# Process hypocentral depthshdds=node_to_hdd(node.nodes[pnt_taglist.index("hypoDepthDist")])returnmtkPointSource(point_id,name,trt,geometry=location,upper_depth=upper_depth,lower_depth=lower_depth,mag_scale_rel=msr,rupt_aspect_ratio=aspect,mfd=mfd,nodal_plane_dist=npds,hypo_depth_dist=hdds,)
[docs]defparse_area_source_node(node,mfd_spacing=0.1):""" Returns an "areaSource" node into an instance of the :class: openquake.hmtk.sources.area.mtkAreaSource """assert"areaSource"innode.tagarea_taglist=get_taglist(node)# Get metadataarea_id,name,trt=(node.attrib["id"],node.attrib["name"],node.attrib["tectonicRegion"],)assertarea_id# Defensive validation!# Process geometrypolygon,upper_depth,lower_depth=node_to_area_geometry(node.nodes[area_taglist.index("areaGeometry")])# Process scaling relationmsr=node_to_scalerel(node.nodes[area_taglist.index("magScaleRel")])# Process aspect ratioaspect=float_(node.nodes[area_taglist.index("ruptAspectRatio")].text)# Process MFDmfd=node_to_mfd(node,area_taglist)# Process nodal planesnpds=node_to_nodal_planes(node.nodes[area_taglist.index("nodalPlaneDist")])# Process hypocentral depthshdds=node_to_hdd(node.nodes[area_taglist.index("hypoDepthDist")])returnmtkAreaSource(area_id,name,trt,geometry=polygon,upper_depth=upper_depth,lower_depth=lower_depth,mag_scale_rel=msr,rupt_aspect_ratio=aspect,mfd=mfd,nodal_plane_dist=npds,hypo_depth_dist=hdds,)
[docs]defparse_simple_fault_node(node,mfd_spacing=0.1,mesh_spacing=1.0):""" Parses a "simpleFaultSource" node and returns an instance of the :class: openquake.hmtk.sources.simple_fault.mtkSimpleFaultSource """assert"simpleFaultSource"innode.tagsf_taglist=get_taglist(node)# Get metadatasf_id,name,trt=(node.attrib["id"],node.attrib["name"],node.attrib["tectonicRegion"],)# Process geometrytrace,dip,upper_depth,lower_depth=node_to_simple_fault_geometry(node.nodes[sf_taglist.index("simpleFaultGeometry")])# Process scaling relationmsr=node_to_scalerel(node.nodes[sf_taglist.index("magScaleRel")])# Process aspect ratioaspect=float_(node.nodes[sf_taglist.index("ruptAspectRatio")].text)# Process MFDmfd=node_to_mfd(node,sf_taglist)# Process rakerake=float_(node.nodes[sf_taglist.index("rake")].text)simple_fault=mtkSimpleFaultSource(sf_id,name,trt,geometry=None,dip=dip,upper_depth=upper_depth,lower_depth=lower_depth,mag_scale_rel=msr,rupt_aspect_ratio=aspect,mfd=mfd,rake=rake,)simple_fault.create_geometry(trace,dip,upper_depth,lower_depth,mesh_spacing)returnsimple_fault
[docs]defparse_complex_fault_node(node,mfd_spacing=0.1,mesh_spacing=4.0):""" Parses a "complexFaultSource" node and returns an instance of the :class: openquake.hmtk.sources.complex_fault.mtkComplexFaultSource """assert"complexFaultSource"innode.tagsf_taglist=get_taglist(node)# Get metadatasf_id,name,trt=(node.attrib["id"],node.attrib["name"],node.attrib["tectonicRegion"],)# Process geometryedges=node_to_complex_fault_geometry(node.nodes[sf_taglist.index("complexFaultGeometry")])# Process scaling relationmsr=node_to_scalerel(node.nodes[sf_taglist.index("magScaleRel")])# Process aspect ratioaspect=float_(node.nodes[sf_taglist.index("ruptAspectRatio")].text)# Process MFDmfd=node_to_mfd(node,sf_taglist)# Process rakerake=float_(node.nodes[sf_taglist.index("rake")].text)complex_fault=mtkComplexFaultSource(sf_id,name,trt,geometry=None,mag_scale_rel=msr,rupt_aspect_ratio=aspect,mfd=mfd,rake=rake,)complex_fault.create_geometry(edges,mesh_spacing)returncomplex_fault
[docs]classnrmlSourceModelParser(BaseSourceModelParser):""" Parser for a source model in NRML format, permitting partial validation such that not all fields need to be specified for the file to be parsed """
[docs]@deprecated(msg="Use openquake.hazardlib.nrml.to_python instead")defread_file(self,identifier,mfd_spacing=0.1,simple_mesh_spacing=1.0,complex_mesh_spacing=4.0,area_discretization=10.0,):""" Reads in the source model in returns an instance of the :class: openquake.hmtk.sourcs.source_model.mtkSourceModel """sm_node=node_from_xml(self.input_file)[0]nrml04="xmlns/nrml/0.4"insm_node[0].tagifnrml04:node_sets=[sm_node]sm_name=sm_node.get("name","")else:# format NRML 0.5+node_sets=sm_nodesm_name=sm_node["name"]source_model=mtkSourceModel(identifier,name=sm_name)fornode_setinnode_sets:fornodeinnode_set:ifnotnrml04:# get the TRT from the sourceGroupnode.attrib["tectonicRegion"]=node_set["tectonicRegion"]if"pointSource"innode.tag:source_model.sources.append(parse_point_source_node(node,mfd_spacing))elif"areaSource"innode.tag:source_model.sources.append(parse_area_source_node(node,mfd_spacing))elif"simpleFaultSource"innode.tag:source_model.sources.append(parse_simple_fault_node(node,mfd_spacing,simple_mesh_spacing))elif"complexFaultSource"innode.tag:source_model.sources.append(parse_complex_fault_node(node,mfd_spacing,complex_mesh_spacing))# TODO: multiPointSource are not supportedelse:print("Source typology %s not recognised - skipping!"%node.tag)returnsource_model