Source code for openquake.hazardlib.source.non_parametric
# The Hazard Library# Copyright (C) 2013-2023 GEM Foundation## This program 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.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU Affero General Public License for more details.## You should have received a copy of the GNU Affero General Public License# along with this program. If not, see <http://www.gnu.org/licenses/>."""Module :mod:`openquake.hazardlib.source.non_parametric` defines:class:`NonParametricSeismicSource`"""importnumpyfromopenquake.baselib.generalimportblock_splitterfromopenquake.hazardlib.source.baseimportBaseSeismicSourcefromopenquake.hazardlib.geo.surface.griddedimportGriddedSurfacefromopenquake.hazardlib.geo.surface.multiimportMultiSurfacefromopenquake.hazardlib.source.ruptureimport \
NonParametricProbabilisticRupturefromopenquake.hazardlib.geo.utilsimport(angular_distance,KM_TO_DEGREES,get_spherical_bounding_box)fromopenquake.hazardlib.geo.meshimportMeshfromopenquake.hazardlib.geo.pointimportPointfromopenquake.hazardlib.pmfimportPMFF32=numpy.float32U32=numpy.uint32BLOCKSIZE=100
[docs]classNonParametricSeismicSource(BaseSeismicSource):""" Non Parametric Seismic Source explicitly defines earthquake ruptures in the constructor. That is earthquake ruptures are not generated algorithmically from a set of source parameters. Ruptures' rectonic region types are overwritten by source tectonic region type. :param data: List of tuples. Each tuple must contain two items. The first item must be an instance of :class:`openquake.hazardlib.source.rupture.Rupture`. The second item must be an instance of :class:`openquake.hazardlib.pmf.PMF` describing the probability of the rupture to occur N times (the PMF must be defined from a minimum number of occurrences equal to 0) """code=b'N'MODIFICATIONS=set()def__init__(self,source_id,name,tectonic_region_type,data,weights=None):super().__init__(source_id,name,tectonic_region_type)self.data=dataifweightsisnotNone:assertlen(weights)==len(data),(len(weights),len(data))for(rup,pmf),weightinzip(data,weights):rup.weight=weight@propertydefrup_weights(self):return[rup.weightforrup,pmfinself.data]
[docs]defiter_ruptures(self,**kwargs):""" Get a generator object that yields probabilistic ruptures the source consists of. :returns: Generator of instances of :class:`openquake.hazardlib.source. rupture.NonParametricProbabilisticRupture`. """step=kwargs.get('step',1)forrup,pmfinself.data[::step**2]:yieldNonParametricProbabilisticRupture(rup.mag,rup.rake,self.tectonic_region_type,rup.hypocenter,rup.surface,pmf,weight=getattr(rup,'weight',0.))
def__iter__(self):iflen(self.data)==1:# there is nothing to splityieldselfreturnfori,blockinenumerate(block_splitter(self.data,BLOCKSIZE)):source_id='%s:%d'%(self.source_id,i)src=self.__class__(source_id,self.name,self.tectonic_region_type,block)src.num_ruptures=len(block)src.trt_smr=self.trt_smryieldsrc
[docs]defcount_ruptures(self):""" See :meth: `openquake.hazardlib.source.base.BaseSeismicSource.count_ruptures`. """returnlen(self.data)
[docs]defget_min_max_mag(self):""" Return the minimum and maximum magnitudes of the ruptures generated by the source """min_mag=min(rup.magforrup,pmfinself.data)max_mag=max(rup.magforrup,pmfinself.data)returnmin_mag,max_mag
[docs]defget_bounding_box(self,maxdist):""" Bounding box containing the surfaces, enlarged by the maximum distance """surfaces=[]forrup,_inself.data:ifisinstance(rup.surface,MultiSurface):surfaces.extend(rup.surface.surfaces)else:surfaces.append(rup.surface)S=len(surfaces)lons=numpy.zeros(2*S)lats=numpy.zeros(2*S)fori,surfinenumerate(surfaces):lo1,lo2,la1,la2=surf.get_bounding_box()lons[2*i]=lo1lons[2*i+1]=lo2lats[2*i]=la1lats[2*i+1]=la2west,east,north,south=get_spherical_bounding_box(lons,lats)a1=maxdist*KM_TO_DEGREESa2=angular_distance(maxdist,north,south)returnwest-a2,south-a1,east+a2,north+a1
[docs]defis_gridded(self):""" :returns: True if containing only GriddedRuptures, False otherwise """forrup,_inself.data:ifnotisinstance(rup.surface,GriddedSurface):returnFalsereturnTrue
[docs]deftodict(self):""" Convert a GriddedSource into a dictionary of arrays """assertself.is_gridded(),'%s is not gridded'%selfn=len(self.data)m=sum(len(rup.surface.mesh)forrup,pmfinself.data)p=len(self.data[0][1].data)dic={'probs_occur':numpy.zeros((n,p)),'magnitude':numpy.zeros(n),'rake':numpy.zeros(n),'hypocenter':numpy.zeros((n,3),F32),'mesh3d':numpy.zeros((m,3),F32),'slice':numpy.zeros((n,2),U32)}start=0fori,(rup,pmf)inenumerate(self.data):dic['probs_occur'][i]=[probfor(prob,_)inpmf.data]dic['magnitude'][i]=rup.magdic['rake'][i]=rup.rakedic['hypocenter'][i]=(rup.hypocenter.x,rup.hypocenter.y,rup.hypocenter.z)mesh=rup.surface.mesh.array.T# shape (npoints, 3)dic['mesh3d'][start:start+len(mesh)]=meshdic['slice'][i]=start,start+len(mesh)start+=len(mesh)returndic
[docs]deffromdict(self,dic,weights=None):""" Populate a GriddedSource with ruptures """assertnotself.data,'%s is not empty'%selfi=0formag,rake,hp,probs,(start,stop)inzip(dic['magnitude'],dic['rake'],dic['hypocenter'],dic['probs_occur'],dic['slice']):mesh=Mesh(dic['mesh3d'][start:stop,0],dic['mesh3d'][start:stop,1],dic['mesh3d'][start:stop,2])surface=GriddedSurface(mesh)pmf=PMF([(prob,i)fori,probinenumerate(probs)])hypocenter=Point(hp[0],hp[1],hp[2])rup=NonParametricProbabilisticRupture(mag,rake,self.tectonic_region_type,hypocenter,surface,pmf,weight=NoneifweightsisNoneelseweights[i])self.data.append((rup,pmf))i+=1
def__repr__(self):return'<%s%s gridded=%s>'%(self.__class__.__name__,self.source_id,self.is_gridded())@propertydefmesh_size(self):""" :returns: the number of points in the underlying meshes (reduced) """n=0forrupinself.iter_ruptures(step=50):# reducedifisinstance(rup.surface,MultiSurface):forsfcinrup.surface.surfaces:n+=len(sfc.mesh)else:n+=len(rup.surface.mesh)returnn@propertydefpolygon(self):""" The convex hull of a few subsurfaces """lons,lats=[],[]forrupinself.iter_ruptures(step=50):# reducedifisinstance(rup.surface,MultiSurface):forsfcinrup.surface.surfaces:lons.extend(sfc.mesh.lons.flat)lats.extend(sfc.mesh.lats.flat)else:lons.extend(rup.surface.mesh.lons.flat)lats.extend(rup.surface.mesh.lats.flat)condition=numpy.isfinite(lons).astype(int)lons=numpy.extract(condition,lons)lats=numpy.extract(condition,lats)points=numpy.zeros(len(lons),[('lon',float),('lat',float)])points['lon']=numpy.round(lons,5)points['lat']=numpy.round(lats,5)points=numpy.unique(points)mesh=Mesh(points['lon'],points['lat'])returnmesh.get_convex_hull()
[docs]defwkt(self):""" :returns: the geometry as a WKT string """returnself.polygon.wkt