#!/usr/bin/env python
# stdlib imports
import copy
# third party imports
import numpy as np
from openquake.hazardlib.gsim.base import GMPE
from openquake.hazardlib.gsim import base
# local imports
from shakelib.utils.exception import ShakeLibException
from shakelib.rupture.edge_rupture import EdgeRupture
[docs]class Distance(object):
"""
Class for distance calculations. Primary method is 'get_distance'. To
handle multiple segment ruptures, many of the distances are based on the
Spudich and Chiou (2015) GC2 coordinate system.
References:
Spudich, Paul and Chiou, Brian, 2015, Strike-parallel and strike-normal
coordinate system around geometrically complicated rupture traces—Use
by NGA-West2 and further improvements: U.S. Geological Survey Open-File
Report 2015-1028, 20 p., http://dx.doi.org/10.3133/ofr20151028.
"""
def __init__(self, gmpe, lon, lat, dep, rupture=None):
"""
Constructor for Distance class.
Args:
gmpe (GMPE): Concrete subclass of GMPE
`[link] <http://docs.openquake.org/oq-hazardlib/master/gsim/index.html#built-in-gsims>`__;
can be individual instance or list of instances.
lon (array): A numpy array of site longitudes.
lat (array): A numpy array of site latitudes.
dep (array): A numpy array of site depths (km); down is positive.
rupture (Rupture): A Shakemap Rupture instance.
Returns:
Distance object.
""" # noqa
self._rupture = rupture
self._distance_context = self._calcDistanceContext(
gmpe, lat, lon, dep)
[docs] @classmethod
def fromSites(cls, gmpe, sites, rup):
"""
Convenience class method to construct a Distance object from a sites
object.
Args:
gmpe (GMPE): Concrete subclass of GMPE
`[link] <http://docs.openquake.org/oq-hazardlib/master/gsim/index.html#built-in-gsims>`__;
can be individual instance or list of instances.
sites (Sites): A Shakemap Sites object.
rup (Rupture): A Shakemap Rupture object.
Returns:
Distance object.
""" # noqa
sm_dict = sites._GeoDict
west = sm_dict.xmin
east = sm_dict.xmax
south = sm_dict.ymin
north = sm_dict.ymax
nx = sm_dict.nx
ny = sm_dict.ny
lats = np.linspace(north, south, ny)
lons = np.linspace(west, east, nx)
lon, lat = np.meshgrid(lons, lats)
dep = np.zeros_like(lon)
return cls(gmpe, lon, lat, dep, rup)
[docs] def getDistanceContext(self):
"""
Returns:
Openquake distance context
`[link] <http://docs.openquake.org/oq-hazardlib/master/gsim/index.html?highlight=distancescontext#openquake.hazardlib.gsim.base.DistancesContext>`__.
""" # noqa
return copy.deepcopy(self._distance_context)
def _calcDistanceContext(self, gmpe, lat, lon, dep):
"""
Create a DistancesContext object.
Args:
gmpe (GMPE): Concrete subclass of GMPE
(https://github.com/gem/oq-hazardlib/blob/master/openquake/hazardlib/gsim/base.py)
can be individual instance or list of instances.
lat (array): Numpy array of latitudes.
lon (array): Numpy array of longitudes.
dep (array): Numpy array of depths (km).
Returns:
DistancesContext object with distance grids required by input
gmpe(s).
Raises:
TypeError: If gmpe is not a subclass of GMPE.
"""
if not isinstance(gmpe, list):
gmpe = [gmpe]
# require rhypo always
requires = set(['rhypo','rrup','rjb'])
for ig in gmpe:
if not isinstance(ig, GMPE):
raise TypeError(
'getDistanceContext() cannot work with objects of '
'type "%s"' % type(ig))
requires = requires | ig.REQUIRES_DISTANCES
context = base.DistancesContext()
if isinstance(self._rupture, EdgeRupture):
ddict = get_distance(list(requires), lat, lon, dep, self._rupture,
dx=self._rupture._mesh_dx)
else:
ddict = get_distance(list(requires), lat, lon, dep, self._rupture)
for method in requires:
(context.__dict__)[method] = ddict[method]
return context
[docs]def get_distance_measures():
"""
Returns a list of strings specifying the distance measure types
(e.g. "repi", "rhypo") available for the "methods" argument to
get_diistance().
Returns:
A list of strings.
"""
return ['repi', 'rhypo', 'rjb', 'rrup', 'rx', 'ry', 'ry0', 'U', 'T']
[docs]def get_distance(methods, lat, lon, dep, rupture, dx=0.5):
"""
Calculate distance using any one of a number of distance measures.
One of quadlist OR hypo must be specified. The following table gives
the allowed distance strings and a description of each.
+--------+----------------------------------------------------------+
| String | Description |
+========+==========================================================+
| repi | Distance to epicenter. |
+--------+----------------------------------------------------------+
| rhypo | Distance to hypocenter. |
+--------+----------------------------------------------------------+
| rjb | Joyner-Boore distance; this is closest distance to the |
| | surface projection of the rupture plane. |
+--------+----------------------------------------------------------+
| rrup | Rupture distance; closest distance to the rupture plane. |
+--------+----------------------------------------------------------+
| rx | Strike-normal distance; same as GC2 coordiante T. |
+--------+----------------------------------------------------------+
| ry | Strike-parallel distance; same as GC2 coordiante U, but |
| | with a shift in origin definition. See Spudich and Chiou |
| | (2015) http://dx.doi.org/10.3133/ofr20151028. |
+--------+----------------------------------------------------------+
| ry0 | Horizontal distance off the end of the rupture measured |
| | parallel to strike. Can only be zero or positive. We |
| | compute this as a function of GC2 coordinate U. |
+--------+----------------------------------------------------------+
| U | GC2 coordinate U. |
+--------+----------------------------------------------------------+
| T | GC2 coordinate T. |
+--------+----------------------------------------------------------+
Args:
methods (list): List of strings (or just a string) of distances to
compute.
lat (array): A numpy array of latitudes.
lon (array): A numpy array of longidues.
dep (array): A numpy array of depths (km).
rupture (Rupture): A ShakeMap Rupture instance.
dx (float): Mesh spacing for rupture; only used if rupture is an
EdgeRupture subclass.
Returns:
dict: dictionary of numpy arrays of distances, size of lon.shape.
"""
rupture._mesh_dx = dx
# Dictionary for holding/returning the requested distances
distdict = dict()
# Coerce methods into list if it isn't
if not isinstance(methods, list):
methods = [methods]
# Check that all requested distances are available
methods_available = set(get_distance_measures())
if not set(methods).issubset(methods_available):
raise NotImplementedError(
'One or more requested distance method is not '
'valid or is not implemented yet')
# Check dimensions of site coordinates
if (lat.shape != lon.shape) or (lat.shape != dep.shape):
raise ShakeLibException('lat, lon, and dep must have the same shape.')
# -------------------------------------------------------------------------
# Point distances
# -------------------------------------------------------------------------
if 'rhypo' in methods:
distdict['rhypo'] = rupture.computeRhyp(lon, lat, dep)
if 'repi' in methods:
distdict['repi'] = rupture.computeRepi(lon, lat, dep)
# -------------------------------------------------------------------------
# Rupture distances
# -------------------------------------------------------------------------
gc2_distances = set(['rx', 'ry', 'ry0', 'U', 'T'])
if 'rrup' in methods:
distdict['rrup'] = rupture.computeRrup(lon, lat, dep)
if 'rjb' in methods:
distdict['rjb'] = rupture.computeRjb(lon, lat, dep)
# If any of the GC2-related distances are requested, may as well do all
if len(set(methods).intersection(gc2_distances)) > 0:
distdict.update(rupture.computeGC2(lon, lat, dep))
return distdict