Exemple #1
0
    def __computeD(self, i):
        """Compute d for the i-th quad/segment.

        Y = d/W, where d is the portion (in km) of the width of the fault which
        ruptures up-dip from the hypocenter to the top of the fault.

        :param i:
            index of segment for which d is to be computed.
        """
        hyp_ecef = self.phyp[i]  # already in ECEF
        hyp_col = np.array([[hyp_ecef.x], [hyp_ecef.y], [hyp_ecef.z]])

        # First compute "updip" vector
        P0, P1, P2 = self._rup.getQuadrilaterals()[i][0:3]
        p1 = Vector.fromPoint(P1)  # convert to ECEF
        p2 = Vector.fromPoint(P2)
        e21 = p1 - p2
        e21norm = e21.norm()
        hp1 = p1 - hyp_ecef
        # convert to km (used as max later)
        udip_len = Vector.dot(hp1, e21norm) / 1000.0
        udip_col = np.array([[e21norm.x], [e21norm.y],
                             [e21norm.z]])  # ECEF coords

        # Sites
        slat = self._lat
        slon = self._lon

        # Convert sites to ECEF:
        site_ecef_x = np.ones_like(slat)
        site_ecef_y = np.ones_like(slat)
        site_ecef_z = np.ones_like(slat)

        # Make a 3x(#number of sites) matrix of site locations
        # (rows are x, y, z) in ECEF
        site_ecef_x, site_ecef_y, site_ecef_z = ecef.latlon2ecef(
            slat, slon, np.zeros(slon.shape))
        site_mat = np.array([
            np.reshape(site_ecef_x, (-1, )),
            np.reshape(site_ecef_y, (-1, )),
            np.reshape(site_ecef_z, (-1, ))
        ])

        # Hypocenter-to-site matrix
        h2s_mat = site_mat - hyp_col  # in ECEF

        # Dot hypocenter-to-site with updip vector
        d_raw = np.abs(np.sum(h2s_mat * udip_col, axis=0)) / \
            1000.0  # convert to km
        d_raw = np.reshape(d_raw, self._lat.shape)
        self.d = d_raw.clip(min=1.0, max=udip_len)
Exemple #2
0
    def __computeD(self, i):
        """Compute d for the i-th quad/segment.

        Y = d/W, where d is the portion (in km) of the width of the fault which
        ruptures up-dip from the hypocenter to the top of the fault.

        :param i:
            index of segment for which d is to be computed.
        """
        hyp_ecef = self.phyp[i]  # already in ECEF
        hyp_col = np.array([[hyp_ecef.x], [hyp_ecef.y], [hyp_ecef.z]])

        # First compute "updip" vector
        P0, P1, P2, P3 = self._flt.getQuadrilaterals()[i]
        p1 = Vector.fromPoint(P1)  # convert to ECEF
        p2 = Vector.fromPoint(P2)
        e21 = p1 - p2
        e21norm = e21.norm()
        hp1 = p1 - hyp_ecef
        # convert to km (used as max later)
        udip_len = Vector.dot(hp1, e21norm) / 1000.0
        udip_col = np.array([[e21norm.x], [e21norm.y],
                             [e21norm.z]])  # ECEF coords

        # Sites
        slat = self._lat
        slon = self._lon

        # Convert sites to ECEF:
        site_ecef_x = np.ones_like(slat)
        site_ecef_y = np.ones_like(slat)
        site_ecef_z = np.ones_like(slat)

        # Make a 3x(#number of sites) matrix of site locations
        # (rows are x, y, z) in ECEF
        site_ecef_x, site_ecef_y, site_ecef_z = ecef.latlon2ecef(
            slat, slon, np.zeros(slon.shape))
        site_mat = np.array([
            np.reshape(site_ecef_x, (-1, )),
            np.reshape(site_ecef_y, (-1, )),
            np.reshape(site_ecef_z, (-1, ))
        ])

        # Hypocenter-to-site matrix
        h2s_mat = site_mat - hyp_col  # in ECEF

        # Dot hypocenter-to-site with updip vector
        d_raw = np.abs(np.sum(h2s_mat * udip_col, axis=0)) / \
            1000.0  # convert to km
        d_raw = np.reshape(d_raw, self._lat.shape)
        self.d = d_raw.clip(min=1.0, max=udip_len)
Exemple #3
0
def __calc_t_i(P0, P1, lat, lon, proj):
    """
    Calculate t_i distance. See Spudich and Chiou OFR 2015-1028. This is the
    distance measured normal to strike from the i-th segment. Values on the
    hanging-wall are positive and those on the foot-wall are negative.

    :param P0:
        Point object, representing the first top-edge vertex of a rupture
        quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a rupture
        quadrilateral.
    :param lat:
        A numpy array of latitudes.
    :param lon:
        A numpy array of longitudes.
    :param proj:
        An orthographic projection from 
        openquake.hazardlib.geo.utils.get_orthographic_projection. 
    :returns:
        Array of size N of distances (in km) from input points to rupture
        surface.
    """

    # projected coordinates are in km
    p0x, p0y = proj(P0.x, P0.y)
    p1x, p1y = proj(P1.x, P1.y)

    # Unit vector pointing normal to strike
    t_i_hat = Vector(p1y - p0y, -(p1x - p0x), 0).norm()

    # Convert sites to Cartesian
    sx, sy = proj(lon, lat)
    sx1d = np.reshape(sx, (-1,))
    sy1d = np.reshape(sy, (-1,))

    # Vectors from P0 to sites
    r = np.zeros([len(sx1d), 2])
    r[:, 0] = sx1d - p0x
    r[:, 1] = sy1d - p0y

    # Dot product gives t_i
    t_i = np.sum(t_i_hat.getArray()[0:2] * r, axis=1)
    shp = t_i.shape
    if len(shp) == 1:
        t_i.shape = (shp[0], 1)
    t_i = np.fliplr(t_i)
    return t_i
Exemple #4
0
def __calc_u_i(P0, P1, lat, lon, proj):
    """
    Calculate u_i distance. See Spudich and Chiou OFR 2015-1028. This is the 
    distance along strike from the first vertex (P0) of the i-th segment.

    :param P0:
        Point object, representing the first top-edge vertex of a rupture 
        quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a rupture 
        quadrilateral.
    :param lat:
        A numpy array of latitude.
    :param lon:
        A numpy array of longitude.
    :param proj:
        An orthographic projection from 
        openquake.hazardlib.geo.utils.get_orthographic_projection. 
    :returns:
        Array of size lat.shape of distances (in km).
    """

    # projected coordinates are in km
    p0x, p0y = proj(P0.x, P0.y)
    p1x, p1y = proj(P1.x, P1.y)

    # Unit vector pointing along strike
    u_i_hat = Vector(p1x - p0x, p1y - p0y, 0).norm()

    # Convert sites to Cartesian
    sx, sy = proj(lon, lat)
    sx1d = np.reshape(sx, (-1,))
    sy1d = np.reshape(sy, (-1,))

    # Vectors from P0 to sites
    r = np.zeros([len(sx1d), 2])
    r[:, 0] = sx1d - p0x
    r[:, 1] = sy1d - p0y

    # Dot product gives t_i
    u_i = np.sum(u_i_hat.getArray()[0:2] * r, axis=1)
    shp = u_i.shape
    if len(shp) == 1:
        u_i.shape = (shp[0], 1)
    u_i = np.fliplr(u_i)

    return u_i
Exemple #5
0
def __calc_u_i(P0, P1, lat, lon, proj):
    """
    Calculate u_i distance. See Spudich and Chiou OFR 2015-1028. This is the distance
    along strike from the first vertex (P0) of the i-th segment.

    :param P0:
        Point object, representing the first top-edge vertex of a fault quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a fault quadrilateral.
    :param lat:
        A numpy array of latitude.
    :param lon:
        A numpy array of longitude.
    :param proj:
        An orthographic projection from 
        openquake.hazardlib.geo.utils.get_orthographic_projection. 
    :returns:
        Array of size lat.shape of distances (in km).
    """

    # projected coordinates are in km
    p0x, p0y = proj(P0.x, P0.y)
    p1x, p1y = proj(P1.x, P1.y)

    # Unit vector pointing along strike
    u_i_hat = Vector(p1x - p0x, p1y - p0y, 0).norm()

    # Convert sites to Cartesian
    sx, sy = proj(lon, lat)
    sx1d = np.reshape(sx, (-1, ))
    sy1d = np.reshape(sy, (-1, ))

    # Vectors from P0 to sites
    r = np.zeros([len(sx1d), 2])
    r[:, 0] = sx1d - p0x
    r[:, 1] = sy1d - p0y

    # Dot product gives t_i
    u_i = np.sum(u_i_hat.getArray()[0:2] * r, axis=1)
    shp = u_i.shape
    if len(shp) == 1:
        u_i.shape = (shp[0], 1)
    u_i = np.fliplr(u_i)

    return u_i
Exemple #6
0
def __calc_t_i(P0, P1, lat, lon, proj):
    """
    Calculate t_i distance. See Spudich and Chiou OFR 2015-1028. This is the distance
    measured normal to strike from the i-th segment. Values on the hanging-wall are
    positive and those on the foot-wall are negative.

    :param P0:
        Point object, representing the first top-edge vertex of a fault quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a fault quadrilateral.
    :param lat:
        A numpy array of latitudes.
    :param lon:
        A numpy array of longitudes.
    :param proj:
        An orthographic projection from 
        openquake.hazardlib.geo.utils.get_orthographic_projection. 
    :returns:
        Array of size N of distances (in km) from input points to rupture surface.
    """

    # projected coordinates are in km
    p0x, p0y = proj(P0.x, P0.y)
    p1x, p1y = proj(P1.x, P1.y)

    # Unit vector pointing normal to strike
    t_i_hat = Vector(p1y - p0y, -(p1x - p0x), 0).norm()

    # Convert sites to Cartesian
    sx, sy = proj(lon, lat)
    sx1d = np.reshape(sx, (-1, ))
    sy1d = np.reshape(sy, (-1, ))

    # Vectors from P0 to sites
    r = np.zeros([len(sx1d), 2])
    r[:, 0] = sx1d - p0x
    r[:, 1] = sy1d - p0y

    # Dot product gives t_i
    t_i = np.sum(t_i_hat.getArray()[0:2] * r, axis=1)
    shp = t_i.shape
    if len(shp) == 1:
        t_i.shape = (shp[0], 1)
    t_i = np.fliplr(t_i)
    return t_i
Exemple #7
0
    def __computeWrup(self):
        """
        Compute the the portion (in km) of the width of the fault which ruptures
        up-dip from the hypocenter to the top of the fault.

        Wrup is the portion (in km) of the width of the fault which
        ruptures up-dip from the hypocenter to the top of the fault.

        * This is ambiguous for faults with varible top of rupture (not
          allowed in NGA). For now, lets just compute this for the
          quad where the hypocenter is located.
        * Alternative is to compute max Wrup for the different quads.

        """
        nquad = len(self._flt._quadrilaterals)

        #-------------------------------------------
        # First find which quad the hypocenter is on
        #-------------------------------------------

        x, y, z = latlon2ecef(
            self._hyp.latitude, self._hyp.longitude, self._hyp.depth)
        hyp_ecef = np.array([[x, y, z]])
        qdist = np.zeros(nquad)
        for i in range(0, nquad):
            P0, P1, P2, P3 = self._flt.getQuadrilaterals()[i]
            qdist[i] = _calc_rupture_distance(P0, P1, P2, P3, hyp_ecef)
        ind = int(np.where(qdist == np.min(qdist))[0])
        # *** check that this doesn't break with more than one quad
        q = self._flt.getQuadrilaterals()[ind]

        #--------------------------
        # Compute Wrup on that quad
        #--------------------------

        pp0 = Vector.fromPoint(geo.point.Point(
            q[0].longitude, q[0].latitude, q[0].depth))
        hyp_ecef = Vector.fromPoint(geo.point.Point(
            self._hyp.longitude, self._hyp.latitude, self._hyp.depth))
        hp0 = hyp_ecef - pp0
        ddv = fault.get_quad_down_dip_vector(q)
        self._Wrup = Vector.dot(ddv, hp0) / 1000
Exemple #8
0
def test():
    print('Testing Vector class...')
    a = Vector(1, 1, 1)
    b = Vector(2, 2, 2)
    c = Vector(1, 1, 1)
    np.testing.assert_almost_equal(a.getArray(), np.array([1, 1, 1]))
    assert a == c
    alen = a.mag()
    np.testing.assert_almost_equal(alen, 1.73205, decimal=5)
    anorm = a.norm()
    bnorm = b.norm()
    assert anorm == bnorm
    acrossb = a.cross(b)
    assert acrossb == Vector(0, 0, 0)
    adotb = a.dot(b)
    assert adotb == 6
    aplusb = a + b
    print('Passed Vector class tests.')
Exemple #9
0
def _calc_rupture_distance(P0, P1, P2, P3, points):
    """
    Calculate the shortest distance from a set of points to a rupture surface.

    :param P0:
        Point object, representing the first top-edge vertex of a fault quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a fault quadrilateral.
    :param P2:
        Point object, representing the first bottom-edge vertex of a fault quadrilateral.
    :param P3:
        Point object, representing the second bottom-edge vertex of a fault quadrilateral.
    :param points:
        Numpy array Nx3 of points (ECEF) to calculate distance from.
    :returns:
        Array of size N of distances (in km) from input points to rupture surface.
    """
    # Convert to ecef
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Make a unit vector normal to the plane
    normalVector = (p1 - p0).cross(p2 - p0).norm()

    dist = np.ones_like(points[:, 0]) * np.nan

    p0d = p0.getArray() - points
    p1d = p1.getArray() - points
    p2d = p2.getArray() - points
    p3d = p3.getArray() - points

    # Create 4 planes with normals pointing outside rectangle
    n0 = (p1 - p0).cross(normalVector).getArray()
    n1 = (p2 - p1).cross(normalVector).getArray()
    n2 = (p3 - p2).cross(normalVector).getArray()
    n3 = (p0 - p3).cross(normalVector).getArray()

    sgn0 = np.signbit(np.sum(n0 * p0d, axis=1))
    sgn1 = np.signbit(np.sum(n1 * p1d, axis=1))
    sgn2 = np.signbit(np.sum(n2 * p2d, axis=1))
    sgn3 = np.signbit(np.sum(n3 * p3d, axis=1))

    inside_idx = (sgn0 == sgn1) & (sgn1 == sgn2) & (sgn2 == sgn3)
    dist[inside_idx] = np.power(
        np.abs(np.sum(p0d[inside_idx, :] * normalVector.getArray(), axis=1)),
        2)

    outside_idx = np.logical_not(inside_idx)
    s0 = _distance_sq_to_segment(p0d, p1d)
    s1 = _distance_sq_to_segment(p1d, p2d)
    s2 = _distance_sq_to_segment(p2d, p3d)
    s3 = _distance_sq_to_segment(p3d, p0d)

    smin = np.minimum(np.minimum(s0, s1), np.minimum(s2, s3))
    dist[outside_idx] = smin[outside_idx]
    dist = np.sqrt(dist) / 1000.0
    shp = dist.shape
    if len(shp) == 1:
        dist.shape = (shp[0], 1)
    if np.any(np.isnan(dist)):
        raise ShakeMapException("Could not calculate some distances!")
    dist = np.fliplr(dist)
    return dist
Exemple #10
0
def get_distance(methods, lat, lon, dep, source, use_median_distance=True):
    """
    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.                                        |
    +--------+----------------------------------------------------------+

    :param methods:
        List of strings (or just a string) of distances to compute.
    :param lat:
       A numpy array of latitudes.
    :param lon:
       A numpy array of longidues.
    :param dep:
       A numpy array of depths (km).
    :param source:
       source instance.
    :param use_median_distance:
        Boolean; only used if GMPE requests fault distances and not fault is
        availalbe. Default is True, meaning that point-source distances are
        adjusted based on magnitude to get the median fault distance.
    :returns:
       dictionary of numpy array of distances, size of lon.shape
    """
    fault = source.getFault()
    hypo = source.getHypo()
    if fault is not None:
        quadlist = fault.getQuadrilaterals()
    else:
        quadlist = None

    # Dictionary for holding the distances
    distdict = dict()

    if not isinstance(methods, list):
        methods = [methods]

    methods_available = set(
        ['repi', 'rhypo', 'rjb', 'rrup', 'rx', 'ry', 'ry0', 'U', 'T'])
    if not set(methods).issubset(methods_available):
        raise NotImplementedError(
            'One or more requested distance method is not '
            'valid or is not implemented yet')

    if (lat.shape == lon.shape) and (lat.shape == dep.shape):
        pass
    else:
        raise ShakeMapException('lat, lon, and dep must have the same shape.')

    oldshape = lon.shape

    if len(oldshape) == 2:
        newshape = (oldshape[0] * oldshape[1], 1)
    else:
        newshape = (oldshape[0], 1)

    if ('rrup' in methods) or ('rjb' in methods):
        x, y, z = latlon2ecef(lat, lon, dep)
        x.shape = newshape
        y.shape = newshape
        z.shape = newshape
        sites_ecef = np.hstack((x, y, z))

    # Define a projection that spands sites and fault
    if fault is None:
        all_lat = lat
        all_lon = lon
    else:
        all_lat = np.append(lat, fault.getLats())
        all_lon = np.append(lon, fault.getLons())

    west = np.nanmin(all_lon)
    east = np.nanmax(all_lon)
    south = np.nanmin(all_lat)
    north = np.nanmax(all_lat)
    proj = get_orthographic_projection(west, east, north, south)

    # ---------------------------------------------
    # Distances that do not require loop over quads
    # ---------------------------------------------

    if ('repi' in methods) or \
       (('rjb' in methods) and (quadlist is None)) or \
       (('rrup' in methods) and (quadlist is None)) or \
       (('ry0' in methods) and (quadlist is None)) or \
       (('rx' in methods) and (quadlist is None)) or \
       (('T' in methods) and (quadlist is None)) or \
       (('U' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        repidist = geodetic.distance(hypo.longitude, hypo.latitude, 0.0, lon,
                                     lat, dep)
        repidist = repidist.reshape(oldshape)
        distdict['repi'] = repidist

    if ('rhypo' in methods) or \
       (('rrup' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        rhypodist = geodetic.distance(hypo.longitude, hypo.latitude,
                                      hypo.depth, lon, lat, dep)
        rhypodist = rhypodist.reshape(oldshape)
        distdict['rhypo'] = rhypodist

    # --------------------------------------------------------
    # Loop over quadlist for those distances that require loop
    # --------------------------------------------------------
    if 'rrup' in methods:
        minrrup = np.ones(newshape, dtype=lon.dtype) * 1e16
    if 'rjb' in methods:
        minrjb = np.ones(newshape, dtype=lon.dtype) * 1e16
    if ('rx' in methods) or ('ry' in methods) or \
       ('ry0' in methods) or ('U' in methods) or ('T' in methods):
        totweight = np.zeros(newshape, dtype=lon.dtype)
        GC2T = np.zeros(newshape, dtype=lon.dtype)
        GC2U = np.zeros(newshape, dtype=lon.dtype)

        if quadlist is not None:
            #-----------------------------------------------------------------
            # For these distances, we need to sort out strike discordance and
            # nominal strike prior to starting the loop if there is more than
            # one segment.
            #-----------------------------------------------------------------

            segind = fault._getSegmentIndex()
            segindnp = np.array(segind)
            uind = np.unique(segind)
            nseg = len(uind)

            #-------------------------------------------------------------------
            # The first thing we need to worry about is finding the coordinate
            # shift. U's origin is " selected from the two endpoints most
            # distant from each other."
            #-------------------------------------------------------------------

            if nseg > 1:
                # Need to get index of first and last quad
                # for each segment
                iq0 = np.zeros(nseg, dtype='int16')
                iq1 = np.zeros(nseg, dtype='int16')
                for k in uind:
                    ii = [i for i, j in enumerate(segind) if j == uind[k]]
                    iq0[k] = int(np.min(ii))
                    iq1[k] = int(np.max(ii))

                #---------------------------------------------------------------
                # This is an iterator for each possible combination of segments
                # including segment orientations (i.e., flipped).
                #---------------------------------------------------------------

                it_seg = it.product(it.combinations(uind, 2),
                                    it.product([0, 1], [0, 1]))

                # Placeholder for the segment pair/orientation that gives the
                # largest distance.
                dist_save = 0

                for k in it_seg:
                    s0ind = k[0][0]
                    s1ind = k[0][1]
                    p0ind = k[1][0]
                    p1ind = k[1][1]
                    if p0ind == 0:
                        P0 = quadlist[iq0[s0ind]][0]
                    else:
                        P0 = quadlist[iq1[s0ind]][1]
                    if p1ind == 0:
                        P1 = quadlist[iq1[s1ind]][0]
                    else:
                        P1 = quadlist[iq0[s1ind]][1]

                    dist = geodetic.distance(P0.longitude, P0.latitude, 0.0,
                                             P1.longitude, P1.latitude, 0.0)
                    if dist > dist_save:
                        dist_save = dist
                        A0 = P0
                        A1 = P1

                #---------------------------------------------------------------
                # A0 and A1 are the furthest two segment endpoints, but we still
                # need to sort out which one is the "origin".
                #---------------------------------------------------------------

                # Primate fixes the trend of the trial a vector.
                primate = -1
                while primate < 0:
                    A0.depth = 0
                    A1.depth = 0
                    p_origin = Vector.fromPoint(A0)
                    a0 = Vector.fromPoint(A0)
                    a1 = Vector.fromPoint(A1)
                    ahat = (a1 - a0).norm()

                    # Loop over traces
                    e_j = np.zeros(nseg)
                    b_prime = [None] * nseg
                    for j in range(nseg):
                        P0 = quadlist[iq0[j]][0]
                        P1 = quadlist[iq1[j]][1]
                        P0.depth = 0
                        P1.depth = 0
                        p0 = Vector.fromPoint(P0)
                        p1 = Vector.fromPoint(P1)
                        b_prime[j] = p1 - p0
                        e_j[j] = ahat.dot(b_prime[j])
                    E = np.sum(e_j)

                    # List of discordancy
                    dc = [np.sign(a) * np.sign(E) for a in e_j]
                    b = Vector(0, 0, 0)
                    for j in range(nseg):
                        b.x = b.x + b_prime[j].x * dc[j]
                        b.y = b.y + b_prime[j].y * dc[j]
                        b.z = b.z + b_prime[j].z * dc[j]
                    bhat = b.norm()
                    primate = bhat.dot(ahat)
                    if primate < 0:
                        tmpA0 = copy.copy(A0)
                        tmpA1 = copy.copy(A1)
                        A0 = tmpA1
                        A1 = tmpA0

    if quadlist is not None:
        # Length of prior segments
        s_i = 0.0
        l_i = np.zeros(len(quadlist))
        for i in range(len(quadlist)):
            P0, P1, P2, P3 = quadlist[i]

            if 'rrup' in methods:
                rrupdist = _calc_rupture_distance(P0, P1, P2, P3, sites_ecef)
                minrrup = np.minimum(minrrup, rrupdist)

            if 'rjb' in methods:
                S0 = copy.deepcopy(P0)
                S1 = copy.deepcopy(P1)
                S2 = copy.deepcopy(P2)
                S3 = copy.deepcopy(P3)
                S0.depth = 0.0
                S1.depth = 0.0
                S2.depth = 0.0
                S3.depth = 0.0
                rjbdist = _calc_rupture_distance(S0, S1, S2, S3, sites_ecef)
                minrjb = np.minimum(minrjb, rjbdist)

            if ('rx' in methods) or ('ry' in methods) or \
               ('ry0' in methods) or ('U' in methods) or ('T' in methods):
                # Rx, Ry, and Ry0 are all computed if one is requested since
                # they all require similar information for the weights. This
                # isn't necessary for a single segment fault though.
                # Note, we are basing these calculations on GC2 coordinates U
                # and T as described in:
                # Spudich and Chiou (2015)
                # http://dx.doi.org/10.3133/ofr20151028.

                # Compute u_i and t_i for this segment
                t_i = __calc_t_i(P0, P1, lat, lon, proj)
                u_i = __calc_u_i(P0, P1, lat, lon, proj)

                # Quad length
                l_i[i] = get_quad_length(quadlist[i])

                # Weight of segment, three cases
                # Case 3: t_i == 0 and 0 <= u_i <= l_i
                w_i = np.zeros_like(t_i)
                # Case 1:
                ix = t_i != 0
                w_i[ix] = (1.0 / t_i[ix]) * (np.arctan(
                    (l_i[i] - u_i[ix]) / t_i[ix]) -
                                             np.arctan(-u_i[ix] / t_i[ix]))
                # Case 2:
                ix = (t_i == 0) & ((u_i < 0) | (u_i > l_i[i]))
                w_i[ix] = 1 / (u_i[ix] - l_i[i]) - 1 / u_i[ix]

                totweight = totweight + w_i
                GC2T = GC2T + w_i * t_i
                if nseg == 1:
                    GC2U = GC2U + w_i * (u_i + s_i)
                else:
                    if i == 0:
                        qind = np.array(range(len(quadlist)))
                        l_kj = 0
                        s_ij_1 = 0
                    else:
                        l_kj = l_i[(segindnp == segindnp[i]) & (qind < i)]
                        s_ij_1 = np.sum(l_kj)

                    p1 = Vector.fromPoint(quadlist[iq0[segind[i]]][0])
                    s_ij_2 = (
                        (p1 - p_origin) * dc[segind[i]]).dot(ahat) / 1000.0
                    # This is implemented with GC2N, for GC2T use:
                    #                    s_ij_2 = (p1 - p_origin).dot(bhat) / 1000.0

                    s_ij = s_ij_1 + s_ij_2
                    GC2U = GC2U + w_i * (u_i + s_ij)
                s_i = s_i + l_i[i]

        # Collect distances from loop into the distance dict
        if 'rjb' in methods:
            minrjb = minrjb.reshape(oldshape)
            distdict['rjb'] = minrjb

        if ('rx' in methods) or ('ry' in methods) or \
           ('ry0' in methods) or ('U' in methods) or ('T' in methods):
            # Normalize by sum of quad weights
            GC2T = GC2T / totweight
            GC2U = GC2U / totweight
            distdict['T'] = copy.deepcopy(GC2T).reshape(oldshape)
            distdict['U'] = copy.deepcopy(GC2U).reshape(oldshape)

            # Take care of Rx
            Rx = copy.deepcopy(GC2T)  # preserve sign (no absolute value)
            Rx = Rx.reshape(oldshape)
            distdict['rx'] = Rx

            # Ry
            Ry = GC2U - s_i / 2.0
            Ry = Ry.reshape(oldshape)
            distdict['ry'] = Ry

            # Ry0
            Ry0 = np.zeros_like(GC2U)
            ix = GC2U < 0
            Ry0[ix] = np.abs(GC2U[ix])
            if nseg > 1:
                s_i = s_ij + l_i[-1]
            ix = GC2U > s_i
            Ry0[ix] = GC2U[ix] - s_i
            Ry0 = Ry0.reshape(oldshape)
            distdict['ry0'] = Ry0

        if 'rrup' in methods:
            minrrup = minrrup.reshape(oldshape)
            distdict['rrup'] = minrrup

    else:
        if 'rjb' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rjb with median rjb given M and repi.'
                )
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                mech = source.getEventParam('mech')
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff",
                                      "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechA_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechA_ar1p7_seis0_20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechR_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechR_ar1p7_seis0_20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechN_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechN_ar1p7_seis0_20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechSS_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechSS_ar1p7_seis0_20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechA_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechA_ar1p0_seis0_15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechR_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechR_ar1p0_seis0_15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechN_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechN_ar1p0_seis0_15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechSS_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechSS_ar1p0_seis0_15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff",
                                      "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rjb_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rjb_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(
                            re.findall('R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rjb_ratios_tbl['Repi_km']))
                repi2rjb_grid = repi2rjb_ratios_tbl.values[:, 1:]
                repi2rjb_obj = spint.RectBivariateSpline(dist_list,
                                                         mag_list,
                                                         repi2rjb_grid,
                                                         kx=1,
                                                         ky=1)

                def repi2rjb_tbl(repi, M):
                    ratio = repi2rjb_obj.ev(np.log(repi), M)
                    rjb = repi * ratio
                    return rjb

                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rjb_hat = repi2rjb_tbl(repis, mags)
                distdict['rjb'] = rjb_hat
                # -------------------
                # Additional Variance
                # -------------------
                repi2rjbvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rjbvar_grid = repi2rjbvar_ratios_tbl.values[:, 1:]
                repi2rjbvar_obj = spint.RectBivariateSpline(dist_list,
                                                            mag_list,
                                                            repi2rjbvar_grid,
                                                            kx=1,
                                                            ky=1)
                rjbvar = repi2rjbvar_obj.ev(np.log(repis), mags)
                distdict['rjbvar'] = rjbvar
            else:
                warnings.warn('No fault; Replacing rjb with repi')
                distdict['rjb'] = distdict['repi']
        if 'rrup' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rrup with median rrup given M and repi.'
                )
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                rake = source._event_dict.get('rake')
                mech = rake_to_mech(rake)
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechA_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechA_ar1p7_seis0-20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechR_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechR_ar1p7_seis0-20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechN_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechN_ar1p7_seis0-20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechSS_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechSS_ar1p7_seis0-20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechA_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechA_ar1p0_seis0-15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechR_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechR_ar1p0_seis0-15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechN_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechN_ar1p0_seis0-15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechSS_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechSS_ar1p0_seis0-15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rrup_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rrup_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(
                            re.findall('R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rrup_ratios_tbl['Repi_km']))
                repi2rrup_grid = repi2rrup_ratios_tbl.values[:, 1:]
                repi2rrup_obj = spint.RectBivariateSpline(dist_list,
                                                          mag_list,
                                                          repi2rrup_grid,
                                                          kx=1,
                                                          ky=1)

                def repi2rrup_tbl(repi, M):
                    ratio = repi2rrup_obj.ev(np.log(repi), M)
                    rrup = repi * ratio
                    return rrup

                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rrup_hat = repi2rrup_tbl(repis, mags)
                distdict['rrup'] = rrup_hat

                # -------------------
                # Additional Variance
                # -------------------
                repi2rrupvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rrupvar_grid = repi2rrupvar_ratios_tbl.values[:, 1:]
                repi2rrupvar_obj = spint.RectBivariateSpline(dist_list,
                                                             mag_list,
                                                             repi2rrupvar_grid,
                                                             kx=1,
                                                             ky=1)
                rrupvar = repi2rrupvar_obj.ev(np.log(repis), mags)
                distdict['rrupvar'] = rrupvar
            else:
                warnings.warn('No fault; Replacing rrup with rhypo')
                distdict['rrup'] = distdict['rhypo']
        if 'rx' in methods:
            warnings.warn('No fault; Setting Rx to zero.')
            distdict['rx'] = np.zeros_like(distdict['repi'])
        if 'ry0' in methods:
            warnings.warn('No fault; Replacing ry0 with repi')
            distdict['ry0'] = distdict['repi']
        if 'ry' in methods:
            warnings.warn('No fault; Replacing ry with repi')
            distdict['ry'] = distdict['repi']

    return distdict
Exemple #11
0
def test_so6():
    event_name = 'so6'
    magnitude = 7.2
    dip = np.array([70])
    rake = 135
    width = np.array([15])
    L = 80
    rupx = np.array([0, 0])
    rupy = np.array([0, L])
    zp = np.array([0])

    # Convert to lat/lon
    proj = geo.utils.get_orthographic_projection(-122, -120, 39, 37)
    tlon,tlat = proj(rupx, rupy, reverse = True)

    # Dummy origin
    origin = Origin({'lat':0,
                     'lon':0,
                     'depth':0,
                     'mag':0,
                     'id':'so6',
                     'rake':rake})

    # Rupture
    rup = rupture.QuadRupture.fromTrace(
        np.array([tlon[0]]), np.array([tlat[0]]), 
        np.array([tlon[1]]), np.array([tlat[1]]),
        zp, width, dip, origin, reference = 'rv4')

    # Sites
    x = np.linspace(-80, 80, 21)
    y = np.linspace(-50, 130, 21)
    site_x,site_y = np.meshgrid(x, y)
    slon,slat = proj(site_x, site_y, reverse = True)
    sdepth = np.zeros_like(slon)

    # Fix origin
    tmp = rup.getQuadrilaterals()[0]
    pp0 = Vector.fromPoint(point.Point(tmp[0].longitude, tmp[0].latitude, tmp[0].depth))
    pp1 = Vector.fromPoint(point.Point(tmp[1].longitude, tmp[1].latitude, tmp[1].depth))
    pp2 = Vector.fromPoint(point.Point(tmp[2].longitude, tmp[2].latitude, tmp[2].depth))
    pp3 = Vector.fromPoint(point.Point(tmp[3].longitude, tmp[3].latitude, tmp[3].depth))
    dxp = 10/L
    dyp = (width-5)/width
    mp0 = pp0 + (pp1 - pp0)*dxp
    mp1 = pp3 + (pp2 - pp3)*dxp
    rp = mp0 + (mp1 - mp0)*dyp
    epilat,epilon,epidepth = ecef2latlon(rp.x, rp.y, rp.z)
    epix,epiy = proj(epilon, epilat, reverse = False)

    origin = Origin({'lat':epilat,
                     'lon':epilon,
                     'depth':epidepth,
                     'mag':magnitude,
                     'id':'so6',
                     'rake':rake})

    ruplat = [a.latitude for a in rup.getQuadrilaterals()[0]]
    ruplon = [a.longitude for a in rup.getQuadrilaterals()[0]]
    ruplat = np.append(ruplat, ruplat[0])
    ruplon = np.append(ruplon, ruplon[0])
    rupx,rupy = proj(ruplon, ruplat, reverse = False)

    test1 = Bayless2013(origin, rup, slat, slon, sdepth, T = 5)
    fd = test1.getFd()
    fd_test = np.array(
      [[  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
         -8.92879772e-03,  -1.74526918e-02,  -2.22981746e-02,
         -2.34350450e-02,  -2.13620062e-02,  -1.72712346e-02,
         -1.29509613e-02,  -1.02545064e-02,  -1.03010185e-02,
         -1.28847597e-02,  -1.66274727e-02,  -1.96984070e-02,
         -2.05377743e-02,  -1.81831337e-02,  -1.21881814e-02,
         -2.64862879e-03,   0.00000000e+00,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00,  -8.73221519e-03,
         -2.21421374e-02,  -3.18438939e-02,  -3.71488270e-02,
         -3.76239913e-02,  -3.35015951e-02,  -2.61748968e-02,
         -1.83864728e-02,  -1.34793002e-02,  -1.36687799e-02,
         -1.85727143e-02,  -2.55527671e-02,  -3.14227568e-02,
         -3.38933995e-02,  -3.19289607e-02,  -2.53396980e-02,
         -1.45943649e-02,  -3.71405488e-04,   0.00000000e+00],
       [  0.00000000e+00,  -2.54621422e-03,  -2.11428566e-02,
         -3.68609103e-02,  -4.87464747e-02,  -5.56539037e-02,
         -5.64419387e-02,  -5.05331157e-02,  -3.52919381e-02,
         -2.18782050e-02,  -1.40858125e-02,  -1.47354546e-02,
         -2.35727189e-02,  -3.74838465e-02,  -4.75915414e-02,
         -5.13000399e-02,  -4.87882409e-02,  -4.05716321e-02,
         -2.77368254e-02,  -1.13542729e-02,   0.00000000e+00],
       [  0.00000000e+00,  -1.21642958e-02,  -3.33747360e-02,
         -5.21661817e-02,  -6.74724509e-02,  -7.77628842e-02,
         -8.00243748e-02,  -6.42496853e-02,  -4.38124530e-02,
         -1.97027426e-02,  -1.45897731e-02,  -1.07427056e-02,
         -3.08235222e-02,  -4.82656988e-02,  -6.67692677e-02,
         -7.35152908e-02,  -6.85574283e-02,  -5.71811573e-02,
         -4.12138780e-02,  -2.20396726e-02,  -6.24121310e-04],
       [  0.00000000e+00,  -2.00643401e-02,  -4.39827328e-02,
         -6.62722434e-02,  -8.60268414e-02,  -1.01730306e-01,
         -9.86277741e-02,  -9.82914922e-02,  -5.22335876e-02,
         -1.54622435e-02,  -1.57487554e-02,  -3.06190808e-03,
         -4.81481586e-02,  -8.92480491e-02,  -8.63776477e-02,
         -9.98130440e-02,  -8.95491230e-02,  -7.33553695e-02,
         -5.34401725e-02,  -3.11601812e-02,  -7.33715103e-03],
       [  0.00000000e+00,  -2.50053614e-02,  -5.11695772e-02,
         -7.65997026e-02,  -1.00809054e-01,  -1.22877573e-01,
         -1.18738178e-01,  -1.55236782e-01,  -7.45388001e-02,
          1.92779182e-03,  -1.94380016e-02,   1.94922939e-02,
         -7.66669920e-02,  -1.53909722e-01,  -1.10846875e-01,
         -1.19746768e-01,  -1.07680300e-01,  -8.59905101e-02,
         -6.22042294e-02,  -3.71802472e-02,  -1.13867485e-02],
       [  0.00000000e+00,  -2.63645827e-02,  -5.37984901e-02,
         -8.11337022e-02,  -1.08298371e-01,  -1.35146441e-01,
         -1.34825430e-01,  -1.85836050e-01,  -1.10730875e-01,
         -3.18861095e-02,   4.14395701e-02,  -1.52711946e-02,
         -1.31840763e-01,  -1.96794707e-01,  -1.33453212e-01,
         -1.34989129e-01,  -1.17922385e-01,  -9.21637323e-02,
         -6.58369237e-02,  -3.91646838e-02,  -1.22685698e-02],
       [  0.00000000e+00,  -2.64622244e-02,  -5.40483999e-02,
         -8.16190336e-02,  -1.09162854e-01,  -1.36656677e-01,
         -1.37081504e-01,  -1.89522811e-01,  -1.17723634e-01,
         -4.88765748e-02,  -5.04529015e-03,  -5.76414497e-02,
         -1.45712183e-01,  -2.03062804e-01,  -1.36859828e-01,
         -1.37107390e-01,  -1.19124650e-01,  -9.28263279e-02,
         -6.61800709e-02,  -3.93088682e-02,  -1.22842049e-02],
       [  0.00000000e+00,  -2.58466495e-02,  -5.24858827e-02,
         -7.86086164e-02,  -1.03856343e-01,  -1.27529509e-01,
         -1.23794779e-01,  -1.68810613e-01,  -8.22602627e-02,
          1.74236964e-02,   9.38708725e-02,   4.23208284e-02,
         -8.46343723e-02,  -1.70476759e-01,  -1.17547884e-01,
         -1.24569752e-01,  -1.11518670e-01,  -8.84736806e-02,
         -6.38037151e-02,  -3.81874381e-02,  -1.19867610e-02],
       [  0.00000000e+00,  -2.42186547e-02,  -4.84175525e-02,
         -7.09428614e-02,  -9.07754575e-02,  -1.06117824e-01,
         -9.50228292e-02,  -1.29781980e-01,  -3.08573454e-02,
          7.39058739e-02,   1.30478117e-01,   8.28181149e-02,
         -2.70389535e-02,  -1.20837502e-01,  -8.02081725e-02,
         -9.70274506e-02,  -9.35853383e-02,  -7.77422806e-02,
         -5.77817530e-02,  -3.53067886e-02,  -1.12414659e-02],
       [  0.00000000e+00,  -2.16818717e-02,  -4.22363856e-02,
         -5.96909893e-02,  -7.24805224e-02,  -7.81867829e-02,
         -6.11838569e-02,  -9.05679744e-02,   9.95934969e-03,
          1.07503875e-01,   1.52073917e-01,   1.05894634e-01,
          8.68652263e-03,  -7.98571818e-02,  -4.16548658e-02,
         -6.40511838e-02,  -6.99337160e-02,  -6.26305633e-02,
         -4.89098800e-02,  -3.09284566e-02,  -1.00919381e-02],
       [  0.00000000e+00,  -1.84940182e-02,  -3.47054606e-02,
         -4.65278129e-02,  -5.22037664e-02,  -4.93977115e-02,
         -2.95395230e-02,  -5.82421092e-02,   3.91025654e-02,
          1.29337956e-01,   1.67436703e-01,   1.21969296e-01,
          3.20823547e-02,  -5.00287386e-02,  -9.22993907e-03,
         -3.27186625e-02,  -4.52706958e-02,  -4.57409325e-02,
         -3.84701291e-02,  -2.55751405e-02,  -8.64950254e-03],
       [  0.00000000e+00,  -1.49431380e-02,  -2.65887341e-02,
         -3.29162158e-02,  -3.22994323e-02,  -2.29081781e-02,
         -2.60259636e-03,  -3.29856530e-02,   6.02631314e-02,
          1.45003704e-01,   1.79361264e-01,   1.34292814e-01,
          4.88007115e-02,  -2.82328554e-02,   1.64212421e-02,
         -5.72391847e-03,  -2.23438861e-02,  -2.90246794e-02,
         -2.76054402e-02,  -1.97779758e-02,  -7.03945406e-03],
       [  0.00000000e+00,  -1.12771143e-02,  -1.84737590e-02,
         -1.98228664e-02,  -1.40092305e-02,   1.84580818e-04,
          1.95817303e-02,  -1.32608487e-02,   7.62783168e-02,
          1.57076433e-01,   1.89083905e-01,   1.44259188e-01,
          6.15722813e-02,  -1.17505212e-02,   3.65938109e-02,
          1.66937711e-02,  -2.18970818e-03,  -1.35507683e-02,
         -1.70890527e-02,  -1.39519424e-02,  -5.37036892e-03],
       [  0.00000000e+00,  -7.67615215e-03,  -1.07348257e-02,
         -7.75276739e-03,   2.22351695e-03,   1.98662250e-02,
          3.77611177e-02,   2.42018661e-03,   8.89036172e-02,
          1.66855206e-01,   1.97260700e-01,   1.52590263e-01,
          7.17981256e-02,   1.18005972e-03,   5.26852303e-02,
          3.51638855e-02,   1.51012176e-02,   2.69654076e-04,
         -7.33815554e-03,  -8.36639665e-03,  -3.72176313e-03],
       [  0.00000000e+00,  -4.50552324e-03,  -4.32262850e-03,
          1.73559158e-03,   1.42670366e-02,   3.35040699e-02,
          4.97279358e-02,   1.85410528e-02,   9.39950666e-02,
          1.46646579e-01,   9.13474746e-02,   1.37004651e-01,
          7.74648339e-02,   1.59777072e-02,   6.25334939e-02,
          4.74577418e-02,   2.72155518e-02,   1.06174952e-02,
          3.94103899e-04,  -3.68465400e-03,  -2.19830733e-03],
       [  0.00000000e+00,  -1.74629916e-03,   5.44471813e-04,
          8.22933499e-03,   2.15699287e-02,   4.04232250e-02,
          5.69678048e-02,   5.52408259e-02,   9.04381272e-02,
          1.08204635e-01,   9.14439984e-02,   1.06884511e-01,
          8.17241884e-02,   5.55282924e-02,   6.78528399e-02,
          5.47188925e-02,   3.35251483e-02,   1.69615982e-02,
          5.72048628e-03,  -8.81437278e-05,  -7.36518436e-04],
       [  0.00000000e+00,   4.07838765e-05,   3.63933766e-03,
          1.20080876e-02,   2.51274691e-02,   4.25687176e-02,
          6.25685606e-02,   7.33480475e-02,   8.37515545e-02,
          9.52500287e-02,   9.15135660e-02,   9.66442834e-02,
          8.66659913e-02,   8.10325633e-02,   7.18836713e-02,
          5.45548434e-02,   3.55884875e-02,   2.00142359e-02,
          8.71200201e-03,   2.04407846e-03,  -6.53680674e-06],
       [  0.00000000e+00,   2.40054729e-04,   4.44975227e-03,
          1.27572519e-02,   2.49362989e-02,   4.03831326e-02,
          5.80039988e-02,   7.61280192e-02,   8.37404162e-02,
          8.89634569e-02,   9.15651607e-02,   9.13586235e-02,
          8.83589144e-02,   8.27804032e-02,   6.75666471e-02,
          5.00483249e-02,   3.36733366e-02,   1.96758691e-02,
          9.00603204e-03,   2.18370401e-03,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00,   2.78776980e-03,
          1.05086036e-02,   2.13238822e-02,   3.45577738e-02,
          4.91570145e-02,   6.36787133e-02,   7.63710088e-02,
          8.54072310e-02,   8.92960200e-02,   8.75702197e-02,
          8.07095447e-02,   6.97999389e-02,   5.63787286e-02,
          4.20734776e-02,   2.83073312e-02,   1.61614525e-02,
          6.56194125e-03,   1.00721924e-04,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
          5.49667845e-03,   1.47563319e-02,   2.57955743e-02,
          3.76689418e-02,   4.91861917e-02,   5.90108907e-02,
          6.58478416e-02,   6.87018515e-02,   6.73174642e-02,
          6.20270643e-02,   5.35456385e-02,   4.29400416e-02,
          3.14129728e-02,   2.00795162e-02,   9.84001885e-03,
          1.53992995e-03,   0.00000000e+00,   0.00000000e+00]]
    )
    np.testing.assert_allclose(fd, fd_test, rtol=1e-4)
def test_rv4():
    magnitude = 7.0
    rake = 90.0
    width = np.array([28])
    fltx = np.array([0, 0])
    flty = np.array([0, 32])
    zp = np.array([0])
    dip = np.array([30])

    # Convert to lat/lon
    proj = geo.utils.get_orthographic_projection(-122, -120, 39, 37)
    tlon, tlat = proj(fltx, flty, reverse=True)

    flt = fault.Fault.fromTrace(np.array([tlon[0]]),
                                np.array([tlat[0]]),
                                np.array([tlon[1]]),
                                np.array([tlat[1]]),
                                zp,
                                width,
                                dip,
                                reference='')
    L = flt.getFaultLength()

    # Try to figure out epicenter
    tmp = flt.getQuadrilaterals()[0]
    pp0 = Vector.fromPoint(
        point.Point(tmp[0].longitude, tmp[0].latitude, tmp[0].depth))
    pp1 = Vector.fromPoint(
        point.Point(tmp[1].longitude, tmp[1].latitude, tmp[1].depth))
    pp2 = Vector.fromPoint(
        point.Point(tmp[2].longitude, tmp[2].latitude, tmp[2].depth))
    pp3 = Vector.fromPoint(
        point.Point(tmp[3].longitude, tmp[3].latitude, tmp[3].depth))
    dxp = 6 / L
    dyp = (width - 8) / width
    mp0 = pp0 + (pp1 - pp0) * dxp
    mp1 = pp3 + (pp2 - pp3) * dxp
    rp = mp0 + (mp1 - mp0) * dyp
    epilat, epilon, epidepth = ecef2latlon(rp.x, rp.y, rp.z)

    event = {
        'lat': epilat,
        'lon': epilon,
        'depth': epidepth,
        'mag': magnitude,
        'id': 'test',
        'locstring': 'rv4',
        'type': 'DS',
        'timezone': 'UTC'
    }
    event['time'] = ShakeDateTime.utcfromtimestamp(int(time.time()))
    event['created'] = ShakeDateTime.utcfromtimestamp(int(time.time()))

    x = np.linspace(-50, 50, 11)
    y = np.linspace(-50, 50, 11)
    site_x, site_y = np.meshgrid(x, y)
    slon, slat = proj(site_x, site_y, reverse=True)
    deps = np.zeros_like(slon)
    source = Source(event, flt)
    source.setEventParam('rake', rake)

    test1 = Bayless2013(source, slat, slon, deps, T=2.0)

    # Test fd
    fd = test1.getFd()
    fd_test = np.array(
        [[
            0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.72143257e-03,
            1.34977260e-03, 4.33616224e-15, 1.24446253e-03, 1.16142357e-03,
            2.25464716e-03, 7.05281751e-04, 0.00000000e+00
        ],
         [
             0.00000000e+00, 0.00000000e+00, 7.62610242e-03, 1.25133844e-02,
             5.61896104e-03, 7.63126014e-15, 4.52266194e-03, 4.67970900e-03,
             1.02820316e-02, 5.13160096e-03, -6.13926251e-03
         ],
         [
             0.00000000e+00, 4.00495234e-03, 2.37608386e-02, 2.37139333e-02,
             9.55224050e-03, 5.66364910e-15, 7.70344813e-03, 7.36466362e-03,
             1.48239704e-02, 8.40388145e-03, -1.58592485e-02
         ],
         [
             8.08385547e-19, 9.38150101e-03, 3.38610620e-02, 3.85351492e-02,
             1.91044918e-02, 3.98697802e-15, 1.54321666e-02, 1.21913760e-02,
             2.04435166e-02, 1.04931859e-02, -1.85935894e-02
         ],
         [
             2.12025421e-18, 1.37316085e-02, 4.40193799e-02, 6.16562477e-02,
             4.77612496e-02, 2.60257085e-15, 3.86322888e-02, 1.97965887e-02,
             2.64882038e-02, 1.23335908e-02, -2.07389932e-02
         ],
         [
             2.64338576e-18, 1.45898292e-02, 4.89104213e-02, 7.70703166e-02,
             9.55225258e-02, 1.01875104e-01, 7.73459329e-02, 2.50275508e-02,
             2.93537540e-02, 1.30949577e-02, -2.15685454e-02
         ],
         [
             2.64330042e-18, 1.45898262e-02, 4.89104186e-02, 7.70703146e-02,
             9.55225248e-02, 1.01910945e-01, 7.74050835e-02, 2.52307946e-02,
             2.92970736e-02, 1.30880504e-02, -2.15685424e-02
         ],
         [
             2.64318867e-18, 1.45898259e-02, 4.89104184e-02, 7.70703144e-02,
             9.55225247e-02, 1.01933432e-01, 7.74421258e-02, 2.53572923e-02,
             2.92615130e-02, 1.30837284e-02, -2.15685422e-02
         ],
         [
             2.64305117e-18, 1.45898284e-02, 4.89104206e-02, 7.70703161e-02,
             9.55225256e-02, 1.01942593e-01, 7.74571359e-02, 2.54081640e-02,
             2.92472117e-02, 1.30819985e-02, -2.15685446e-02
         ],
         [
             2.30141673e-18, 1.40210825e-02, 4.56205547e-02, 6.63109661e-02,
             5.79266964e-02, 2.33044622e-15, 4.69672564e-02, 2.18401553e-02,
             2.72864925e-02, 1.25728575e-02, -2.10227772e-02
         ],
         [
             1.10672535e-18, 1.04777076e-02, 3.59041065e-02, 4.24614318e-02,
             2.24217216e-02, 3.66914762e-15, 1.81728517e-02, 1.39301504e-02,
             2.14956836e-02, 1.08711460e-02, -1.90802849e-02
         ]])
    np.testing.assert_allclose(fd, fd_test, rtol=2e-4)
Exemple #13
0
def test():
    print('Testing Vector class...')
    a = Vector(1, 1, 1)
    b = Vector(2, 2, 2)
    c = Vector(1, 1, 1)
    np.testing.assert_almost_equal(a.getArray(), np.array([1, 1, 1]))
    assert a == c
    alen = a.mag()
    np.testing.assert_almost_equal(alen, 1.73205, decimal=5)
    anorm = a.norm()
    bnorm = b.norm()
    assert anorm == bnorm
    acrossb = a.cross(b)
    assert acrossb == Vector(0, 0, 0)
    adotb = a.dot(b)
    assert adotb == 6
    aplusb = a + b
    print('Passed Vector class tests.')
Exemple #14
0
    def __computeThetaAndS(self, i):
        """
        :param i:
            Compute d for the i-th quad/segment.
        """
        # self.phyp is in ECEF
        tmp = ecef.ecef2latlon(self.phyp[i].x, self.phyp[i].y, self.phyp[i].z)
        epi_ecef = Vector.fromPoint(geo.point.Point(tmp[1], tmp[0], 0.0))
        epi_col = np.array([[epi_ecef.x], [epi_ecef.y], [epi_ecef.z]])

        # First compute along strike vector
        P0, P1, P2, P3 = self._flt.getQuadrilaterals()[i]
        p0 = Vector.fromPoint(P0)  # convert to ECEF
        p1 = Vector.fromPoint(P1)
        e01 = p1 - p0
        e01norm = e01.norm()
        hp0 = p0 - epi_ecef
        hp1 = p1 - epi_ecef
        strike_min = Vector.dot(hp0, e01norm) / 1000.0  # convert to km
        strike_max = Vector.dot(hp1, e01norm) / 1000.0  # convert to km
        strike_col = np.array([[e01norm.x], [e01norm.y],
                               [e01norm.z]])  # ECEF coords

        # Sites
        slat = self._lat
        slon = self._lon

        # Convert sites to ECEF:
        site_ecef_x = np.ones_like(slat)
        site_ecef_y = np.ones_like(slat)
        site_ecef_z = np.ones_like(slat)

        # Make a 3x(#number of sites) matrix of site locations
        # (rows are x, y, z) in ECEF
        site_ecef_x, site_ecef_y, site_ecef_z = ecef.latlon2ecef(
            slat, slon, np.zeros(slon.shape))
        site_mat = np.array([
            np.reshape(site_ecef_x, (-1, )),
            np.reshape(site_ecef_y, (-1, )),
            np.reshape(site_ecef_z, (-1, ))
        ])

        # Epicenter-to-site matrix
        e2s_mat = site_mat - epi_col  # in ECEF
        mag = np.sqrt(np.sum(e2s_mat * e2s_mat, axis=0))

        # Avoid division by zero
        mag[mag == 0] = 1e-12
        e2s_norm = e2s_mat / mag

        # Dot epicenter-to-site with along-strike vector
        s_raw = np.sum(e2s_mat * strike_col, axis=0) / 1000.0  # conver to km

        # Put back into a 2d array
        s_raw = np.reshape(s_raw, self._lat.shape)
        self.s = np.abs(s_raw.clip(min=strike_min,
                                   max=strike_max)).clip(min=np.exp(1))

        # Compute theta
        sdots = np.sum(e2s_norm * strike_col, axis=0)
        theta_raw = np.arccos(sdots)

        # But theta is defined to be the reference angle
        # (i.e., the equivalent angle between 0 and 90 deg)
        sintheta = np.abs(np.sin(theta_raw))
        costheta = np.abs(np.cos(theta_raw))
        theta = np.arctan2(sintheta, costheta)
        self.theta = np.reshape(theta, self._lat.shape)
Exemple #15
0
    def __setPseudoHypocenters(self):
        """ Set a pseudo-hypocenter.

        Adapted from ShakeMap 3.5 src/contour/directivity.c

        From Bayless and Somerville:

        "Define the pseudo-hypocenter for rupture of successive segments as
        the point on the side edge of the fault segment that is closest to
        the side edge of the previous segment, and that lies half way
        between the top and bottom of the fault. We assume that the fault is
        segmented along strike, not updip. All geometric parameters are
        computed relative to the pseudo-hypocenter."
        """
        hyp_ecef = Vector.fromPoint(
            geo.point.Point(self._hyp.longitude, self._hyp.latitude,
                            self._hyp.depth))
        # Loop over each quad
        self.phyp = [None] * self._nq
        for i in range(self._nq):
            P0, P1, P2, P3 = self._flt.getQuadrilaterals()[i]
            p0 = Vector.fromPoint(P0)  # convert to ECEF
            p1 = Vector.fromPoint(P1)
            p2 = Vector.fromPoint(P2)
            p3 = Vector.fromPoint(P3)

            # Create 4 planes with normals pointing outside rectangle
            hpnp = Vector.cross(p1 - p0, p2 - p0).norm()
            hpp = -hpnp.x * p0.x - hpnp.y * p0.y - hpnp.z * p0.z
            n0 = Vector.cross(p1 - p0, hpnp)
            n1 = Vector.cross(p2 - p1, hpnp)
            n2 = Vector.cross(p3 - p2, hpnp)
            n3 = Vector.cross(p0 - p3, hpnp)

            # Is the hypocenter inside the projected rectangle?
            # Dot products show which side the origin is on.
            # If origin is on same side of all the planes, then it is 'inside'

            sgn0 = np.signbit(Vector.dot(n0, p0 - hyp_ecef))
            sgn1 = np.signbit(Vector.dot(n1, p1 - hyp_ecef))
            sgn2 = np.signbit(Vector.dot(n2, p2 - hyp_ecef))
            sgn3 = np.signbit(Vector.dot(n3, p3 - hyp_ecef))

            if (sgn0 == sgn1) and (sgn1 == sgn2) and (sgn2 == sgn3):
                # Origin is inside. Use distance-to-plane formula.
                d = Vector.dot(hpnp, hyp_ecef) + hpp
                d = d * d

                # Put the pseudo hypocenter on the plane
                D = Vector.dot(hpnp, hyp_ecef) + hpp
                self.phyp[i] = hyp_ecef - hpnp * D

            else:
                # Origin is outside. Find distance to edges
                p0p = np.reshape(p0.getArray() - hyp_ecef.getArray(), [1, 3])
                p1p = np.reshape(p1.getArray() - hyp_ecef.getArray(), [1, 3])
                p2p = np.reshape(p2.getArray() - hyp_ecef.getArray(), [1, 3])
                p3p = np.reshape(p3.getArray() - hyp_ecef.getArray(), [1, 3])
                s1 = dist2_to_segment(p1p, p2p)
                s2 = dist2_to_segment(p2p, p3p)
                s3 = dist2_to_segment(p3p, p0p)

                # Assuming that the fault is segmented along strike and not
                # updip (as described by Bayless and somerville), we only
                # need to consider s1 and s3:
                if s1 > s3:
                    e30 = p0 - p3
                    e30norm = e30.norm()
                    mag = e30.mag()
                    self.phyp[i] = p3 + e30norm * (0.5 * mag)
                else:
                    e21 = p1 - p2
                    e21norm = e21.norm()
                    mag = e21.mag()
                    self.phyp[i] = p2 + e21norm * (0.5 * mag)
Exemple #16
0
def test_rv4():
    magnitude = 7.0
    rake = 90.0
    width = np.array([28])
    rupx = np.array([0, 0])
    rupy = np.array([0, 32])
    zp = np.array([0])
    dip = np.array([30])

    # Convert to lat/lon
    proj = geo.utils.get_orthographic_projection(-122, -120, 39, 37)
    tlon, tlat = proj(rupx, rupy, reverse=True)

    # Dummy Origin
    origin = Origin({'lat':0,
                     'lon':0,
                     'depth':0,
                     'mag':0,
                     'id':'rv4',
                     'rake':rake})

    # Rupture
    rup = rupture.QuadRupture.fromTrace(
        np.array([tlon[0]]), np.array([tlat[0]]),
        np.array([tlon[1]]), np.array([tlat[1]]),
        zp, width, dip, origin, reference='')
    L = rup.getLength()

    # Figure out epicenter
    tmp = rup.getQuadrilaterals()[0]
    pp0 = Vector.fromPoint(point.Point(
        tmp[0].longitude, tmp[0].latitude, tmp[0].depth))
    pp1 = Vector.fromPoint(point.Point(
        tmp[1].longitude, tmp[1].latitude, tmp[1].depth))
    pp2 = Vector.fromPoint(point.Point(
        tmp[2].longitude, tmp[2].latitude, tmp[2].depth))
    pp3 = Vector.fromPoint(point.Point(
        tmp[3].longitude, tmp[3].latitude, tmp[3].depth))
    dxp = 6/L
    dyp = (width-8)/width
    mp0 = pp0 + (pp1 - pp0)*dxp
    mp1 = pp3 + (pp2 - pp3)*dxp
    rp = mp0 + (mp1 - mp0)*dyp
    epilat,epilon,epidepth = ecef2latlon(rp.x, rp.y, rp.z)

    # Fix Origin:
    origin = Origin({'lat':epilat,
                     'lon':epilon,
                     'depth':epidepth,
                     'mag':magnitude,
                     'id':'rv4',
                     'rake':rake})

    x = np.linspace(-50, 50, 11)
    y = np.linspace(-50, 50, 11)
    site_x, site_y = np.meshgrid(x, y)
    slon, slat = proj(site_x, site_y, reverse=True)
    deps = np.zeros_like(slon)

    test1 = Bayless2013(origin, rup, slat, slon, deps, T=2.0)

    # Test fd
    fd = test1.getFd()
    fd_test = np.array(
      [[  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
          1.72143257e-03,   1.34977260e-03,   4.33616224e-15,
          1.24446253e-03,   1.16142357e-03,   2.25464716e-03,
          7.05281751e-04,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00,   7.62610242e-03,
          1.25133844e-02,   5.61896104e-03,   7.63126014e-15,
          4.52266194e-03,   4.67970900e-03,   1.02820316e-02,
          5.13160096e-03,  -6.13926251e-03],
       [  0.00000000e+00,   4.00495234e-03,   2.37608386e-02,
          2.37139333e-02,   9.55224050e-03,   5.66364910e-15,
          7.70344813e-03,   7.36466362e-03,   1.48239704e-02,
          8.40388145e-03,  -1.58592485e-02],
       [  8.08385547e-19,   9.38150101e-03,   3.38610620e-02,
          3.85351492e-02,   1.91044918e-02,   3.98697802e-15,
          1.54321666e-02,   1.21913760e-02,   2.04435166e-02,
          1.04931859e-02,  -1.85935894e-02],
       [  2.12025421e-18,   1.37316085e-02,   4.40193799e-02,
          6.16562477e-02,   4.77612496e-02,   2.60257085e-15,
          3.86322888e-02,   1.97965887e-02,   2.64882038e-02,
          1.23335908e-02,  -2.07389932e-02],
       [  2.64338576e-18,   1.45898292e-02,   4.89104213e-02,
          7.70703166e-02,   9.55225258e-02,   1.01875104e-01,
          7.73459329e-02,   2.50275508e-02,   2.93537540e-02,
          1.30949577e-02,  -2.15685454e-02],
       [  2.64330042e-18,   1.45898262e-02,   4.89104186e-02,
          7.70703146e-02,   9.55225248e-02,   1.01910945e-01,
          7.74050835e-02,   2.52307946e-02,   2.92970736e-02,
          1.30880504e-02,  -2.15685424e-02],
       [  2.64318867e-18,   1.45898259e-02,   4.89104184e-02,
          7.70703144e-02,   9.55225247e-02,   1.01933432e-01,
          7.74421258e-02,   2.53572923e-02,   2.92615130e-02,
          1.30837284e-02,  -2.15685422e-02],
       [  2.64305117e-18,   1.45898284e-02,   4.89104206e-02,
          7.70703161e-02,   9.55225256e-02,   1.01942593e-01,
          7.74571359e-02,   2.54081640e-02,   2.92472117e-02,
          1.30819985e-02,  -2.15685446e-02],
       [  2.30141673e-18,   1.40210825e-02,   4.56205547e-02,
          6.63109661e-02,   5.79266964e-02,   2.33044622e-15,
          4.69672564e-02,   2.18401553e-02,   2.72864925e-02,
          1.25728575e-02,  -2.10227772e-02],
       [  1.10672535e-18,   1.04777076e-02,   3.59041065e-02,
          4.24614318e-02,   2.24217216e-02,   3.66914762e-15,
          1.81728517e-02,   1.39301504e-02,   2.14956836e-02,
          1.08711460e-02,  -1.90802849e-02]]
    )
    np.testing.assert_allclose(fd, fd_test, rtol=2e-4)
def test_so6():
    event_name = 'so6'
    magnitude = 7.2
    dip = np.array([70])
    rake = 135
    width = np.array([15])
    L = 80
    fltx = np.array([0, 0])
    flty = np.array([0, L])
    zp = np.array([0])
    # Convert to lat/lon
    proj = geo.utils.get_orthographic_projection(-122, -120, 39, 37)
    tlon, tlat = proj(fltx, flty, reverse=True)
    flt = fault.Fault.fromTrace(np.array([tlon[0]]),
                                np.array([tlat[0]]),
                                np.array([tlon[1]]),
                                np.array([tlat[1]]),
                                zp,
                                width,
                                dip,
                                reference='rv4')
    x = np.linspace(-80, 80, 21)
    y = np.linspace(-50, 130, 21)
    site_x, site_y = np.meshgrid(x, y)
    slon, slat = proj(site_x, site_y, reverse=True)
    sdepth = np.zeros_like(slon)
    tmp = flt.getQuadrilaterals()[0]
    pp0 = Vector.fromPoint(
        point.Point(tmp[0].longitude, tmp[0].latitude, tmp[0].depth))
    pp1 = Vector.fromPoint(
        point.Point(tmp[1].longitude, tmp[1].latitude, tmp[1].depth))
    pp2 = Vector.fromPoint(
        point.Point(tmp[2].longitude, tmp[2].latitude, tmp[2].depth))
    pp3 = Vector.fromPoint(
        point.Point(tmp[3].longitude, tmp[3].latitude, tmp[3].depth))
    dxp = 10 / L
    dyp = (width - 5) / width
    mp0 = pp0 + (pp1 - pp0) * dxp
    mp1 = pp3 + (pp2 - pp3) * dxp
    rp = mp0 + (mp1 - mp0) * dyp
    epilat, epilon, epidepth = ecef2latlon(rp.x, rp.y, rp.z)
    epix, epiy = proj(epilon, epilat, reverse=False)
    event = {
        'lat': epilat,
        'lon': epilon,
        'depth': epidepth,
        'mag': magnitude,
        'id': 'so6',
        'locstring': 'so6',
        'type': 'RV',
        'timezone': 'UTC'
    }
    event['time'] = ShakeDateTime.utcfromtimestamp(int(time.time()))
    event['created'] = ShakeDateTime.utcfromtimestamp(int(time.time()))
    fltlat = [a.latitude for a in flt.getQuadrilaterals()[0]]
    fltlon = [a.longitude for a in flt.getQuadrilaterals()[0]]
    fltlat = np.append(fltlat, fltlat[0])
    fltlon = np.append(fltlon, fltlon[0])
    fltx, flty = proj(fltlon, fltlat, reverse=False)
    source = Source(event, flt)
    source.setEventParam('rake', rake)
    test1 = Bayless2013(source, slat, slon, sdepth, T=5)
    fd = test1.getFd()
    fd_test = np.array([
        [
            0.00000000e+00, 0.00000000e+00, 0.00000000e+00, -8.92879772e-03,
            -1.74526918e-02, -2.22981746e-02, -2.34350450e-02, -2.13620062e-02,
            -1.72712346e-02, -1.29509613e-02, -1.02545064e-02, -1.03010185e-02,
            -1.28847597e-02, -1.66274727e-02, -1.96984070e-02, -2.05377743e-02,
            -1.81831337e-02, -1.21881814e-02, -2.64862879e-03, 0.00000000e+00,
            0.00000000e+00
        ],
        [
            0.00000000e+00, 0.00000000e+00, -8.73221519e-03, -2.21421374e-02,
            -3.18438939e-02, -3.71488270e-02, -3.76239913e-02, -3.35015951e-02,
            -2.61748968e-02, -1.83864728e-02, -1.34793002e-02, -1.36687799e-02,
            -1.85727143e-02, -2.55527671e-02, -3.14227568e-02, -3.38933995e-02,
            -3.19289607e-02, -2.53396980e-02, -1.45943649e-02, -3.71405488e-04,
            0.00000000e+00
        ],
        [
            0.00000000e+00, -2.54621422e-03, -2.11428566e-02, -3.68609103e-02,
            -4.87464747e-02, -5.56539037e-02, -5.64419387e-02, -5.05331157e-02,
            -3.52919381e-02, -2.18782050e-02, -1.40858125e-02, -1.47354546e-02,
            -2.35727189e-02, -3.74838465e-02, -4.75915414e-02, -5.13000399e-02,
            -4.87882409e-02, -4.05716321e-02, -2.77368254e-02, -1.13542729e-02,
            0.00000000e+00
        ],
        [
            0.00000000e+00, -1.21642958e-02, -3.33747360e-02, -5.21661817e-02,
            -6.74724509e-02, -7.77628842e-02, -8.00243748e-02, -6.42496853e-02,
            -4.38124530e-02, -1.97027426e-02, -1.45897731e-02, -1.07427056e-02,
            -3.08235222e-02, -4.82656988e-02, -6.67692677e-02, -7.35152908e-02,
            -6.85574283e-02, -5.71811573e-02, -4.12138780e-02, -2.20396726e-02,
            -6.24121310e-04
        ],
        [
            0.00000000e+00, -2.00643401e-02, -4.39827328e-02, -6.62722434e-02,
            -8.60268414e-02, -1.01730306e-01, -9.86277741e-02, -9.82914922e-02,
            -5.22335876e-02, -1.54622435e-02, -1.57487554e-02, -3.06190808e-03,
            -4.81481586e-02, -8.92480491e-02, -8.63776477e-02, -9.98130440e-02,
            -8.95491230e-02, -7.33553695e-02, -5.34401725e-02, -3.11601812e-02,
            -7.33715103e-03
        ],
        [
            0.00000000e+00, -2.50053614e-02, -5.11695772e-02, -7.65997026e-02,
            -1.00809054e-01, -1.22877573e-01, -1.18738178e-01, -1.55236782e-01,
            -7.45388001e-02, 1.92779182e-03, -1.94380016e-02, 1.94922939e-02,
            -7.66669920e-02, -1.53909722e-01, -1.10846875e-01, -1.19746768e-01,
            -1.07680300e-01, -8.59905101e-02, -6.22042294e-02, -3.71802472e-02,
            -1.13867485e-02
        ],
        [
            0.00000000e+00, -2.63645827e-02, -5.37984901e-02, -8.11337022e-02,
            -1.08298371e-01, -1.35146441e-01, -1.34825430e-01, -1.85836050e-01,
            -1.10730875e-01, -3.18861095e-02, 4.14395701e-02, -1.52711946e-02,
            -1.31840763e-01, -1.96794707e-01, -1.33453212e-01, -1.34989129e-01,
            -1.17922385e-01, -9.21637323e-02, -6.58369237e-02, -3.91646838e-02,
            -1.22685698e-02
        ],
        [
            0.00000000e+00, -2.64622244e-02, -5.40483999e-02, -8.16190336e-02,
            -1.09162854e-01, -1.36656677e-01, -1.37081504e-01, -1.89522811e-01,
            -1.17723634e-01, -4.88765748e-02, -5.04529015e-03, -5.76414497e-02,
            -1.45712183e-01, -2.03062804e-01, -1.36859828e-01, -1.37107390e-01,
            -1.19124650e-01, -9.28263279e-02, -6.61800709e-02, -3.93088682e-02,
            -1.22842049e-02
        ],
        [
            0.00000000e+00, -2.58466495e-02, -5.24858827e-02, -7.86086164e-02,
            -1.03856343e-01, -1.27529509e-01, -1.23794779e-01, -1.68810613e-01,
            -8.22602627e-02, 1.74236964e-02, 9.38708725e-02, 4.23208284e-02,
            -8.46343723e-02, -1.70476759e-01, -1.17547884e-01, -1.24569752e-01,
            -1.11518670e-01, -8.84736806e-02, -6.38037151e-02, -3.81874381e-02,
            -1.19867610e-02
        ],
        [
            0.00000000e+00, -2.42186547e-02, -4.84175525e-02, -7.09428614e-02,
            -9.07754575e-02, -1.06117824e-01, -9.50228292e-02, -1.29781980e-01,
            -3.08573454e-02, 7.39058739e-02, 1.30478117e-01, 8.28181149e-02,
            -2.70389535e-02, -1.20837502e-01, -8.02081725e-02, -9.70274506e-02,
            -9.35853383e-02, -7.77422806e-02, -5.77817530e-02, -3.53067886e-02,
            -1.12414659e-02
        ],
        [
            0.00000000e+00, -2.16818717e-02, -4.22363856e-02, -5.96909893e-02,
            -7.24805224e-02, -7.81867829e-02, -6.11838569e-02, -9.05679744e-02,
            9.95934969e-03, 1.07503875e-01, 1.52073917e-01, 1.05894634e-01,
            8.68652263e-03, -7.98571818e-02, -4.16548658e-02, -6.40511838e-02,
            -6.99337160e-02, -6.26305633e-02, -4.89098800e-02, -3.09284566e-02,
            -1.00919381e-02
        ],
        [
            0.00000000e+00, -1.84940182e-02, -3.47054606e-02, -4.65278129e-02,
            -5.22037664e-02, -4.93977115e-02, -2.95395230e-02, -5.82421092e-02,
            3.91025654e-02, 1.29337956e-01, 1.67436703e-01, 1.21969296e-01,
            3.20823547e-02, -5.00287386e-02, -9.22993907e-03, -3.27186625e-02,
            -4.52706958e-02, -4.57409325e-02, -3.84701291e-02, -2.55751405e-02,
            -8.64950254e-03
        ],
        [
            0.00000000e+00, -1.49431380e-02, -2.65887341e-02, -3.29162158e-02,
            -3.22994323e-02, -2.29081781e-02, -2.60259636e-03, -3.29856530e-02,
            6.02631314e-02, 1.45003704e-01, 1.79361264e-01, 1.34292814e-01,
            4.88007115e-02, -2.82328554e-02, 1.64212421e-02, -5.72391847e-03,
            -2.23438861e-02, -2.90246794e-02, -2.76054402e-02, -1.97779758e-02,
            -7.03945406e-03
        ],
        [
            0.00000000e+00, -1.12771143e-02, -1.84737590e-02, -1.98228664e-02,
            -1.40092305e-02, 1.84580818e-04, 1.95817303e-02, -1.32608487e-02,
            7.62783168e-02, 1.57076433e-01, 1.89083905e-01, 1.44259188e-01,
            6.15722813e-02, -1.17505212e-02, 3.65938109e-02, 1.66937711e-02,
            -2.18970818e-03, -1.35507683e-02, -1.70890527e-02, -1.39519424e-02,
            -5.37036892e-03
        ],
        [
            0.00000000e+00, -7.67615215e-03, -1.07348257e-02, -7.75276739e-03,
            2.22351695e-03, 1.98662250e-02, 3.77611177e-02, 2.42018661e-03,
            8.89036172e-02, 1.66855206e-01, 1.97260700e-01, 1.52590263e-01,
            7.17981256e-02, 1.18005972e-03, 5.26852303e-02, 3.51638855e-02,
            1.51012176e-02, 2.69654076e-04, -7.33815554e-03, -8.36639665e-03,
            -3.72176313e-03
        ],
        [
            0.00000000e+00, -4.50552324e-03, -4.32262850e-03, 1.73559158e-03,
            1.42670366e-02, 3.35040699e-02, 4.97279358e-02, 1.85410528e-02,
            9.39950666e-02, 1.46646579e-01, 9.13474746e-02, 1.37004651e-01,
            7.74648339e-02, 1.59777072e-02, 6.25334939e-02, 4.74577418e-02,
            2.72155518e-02, 1.06174952e-02, 3.94103899e-04, -3.68465400e-03,
            -2.19830733e-03
        ],
        [
            0.00000000e+00, -1.74629916e-03, 5.44471813e-04, 8.22933499e-03,
            2.15699287e-02, 4.04232250e-02, 5.69678048e-02, 5.52408259e-02,
            9.04381272e-02, 1.08204635e-01, 9.14439984e-02, 1.06884511e-01,
            8.17241884e-02, 5.55282924e-02, 6.78528399e-02, 5.47188925e-02,
            3.35251483e-02, 1.69615982e-02, 5.72048628e-03, -8.81437278e-05,
            -7.36518436e-04
        ],
        [
            0.00000000e+00, 4.07838765e-05, 3.63933766e-03, 1.20080876e-02,
            2.51274691e-02, 4.25687176e-02, 6.25685606e-02, 7.33480475e-02,
            8.37515545e-02, 9.52500287e-02, 9.15135660e-02, 9.66442834e-02,
            8.66659913e-02, 8.10325633e-02, 7.18836713e-02, 5.45548434e-02,
            3.55884875e-02, 2.00142359e-02, 8.71200201e-03, 2.04407846e-03,
            -6.53680674e-06
        ],
        [
            0.00000000e+00, 2.40054729e-04, 4.44975227e-03, 1.27572519e-02,
            2.49362989e-02, 4.03831326e-02, 5.80039988e-02, 7.61280192e-02,
            8.37404162e-02, 8.89634569e-02, 9.15651607e-02, 9.13586235e-02,
            8.83589144e-02, 8.27804032e-02, 6.75666471e-02, 5.00483249e-02,
            3.36733366e-02, 1.96758691e-02, 9.00603204e-03, 2.18370401e-03,
            0.00000000e+00
        ],
        [
            0.00000000e+00, 0.00000000e+00, 2.78776980e-03, 1.05086036e-02,
            2.13238822e-02, 3.45577738e-02, 4.91570145e-02, 6.36787133e-02,
            7.63710088e-02, 8.54072310e-02, 8.92960200e-02, 8.75702197e-02,
            8.07095447e-02, 6.97999389e-02, 5.63787286e-02, 4.20734776e-02,
            2.83073312e-02, 1.61614525e-02, 6.56194125e-03, 1.00721924e-04,
            0.00000000e+00
        ],
        [
            0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 5.49667845e-03,
            1.47563319e-02, 2.57955743e-02, 3.76689418e-02, 4.91861917e-02,
            5.90108907e-02, 6.58478416e-02, 6.87018515e-02, 6.73174642e-02,
            6.20270643e-02, 5.35456385e-02, 4.29400416e-02, 3.14129728e-02,
            2.00795162e-02, 9.84001885e-03, 1.53992995e-03, 0.00000000e+00,
            0.00000000e+00
        ]
    ])
    np.testing.assert_allclose(fd, fd_test, rtol=1e-4)
Exemple #18
0
    def __setPseudoHypocenters(self):
        """ Set a pseudo-hypocenter.

        Adapted from ShakeMap 3.5 src/contour/directivity.c

        From Bayless and Somerville:

        "Define the pseudo-hypocenter for rupture of successive segments as
        the point on the side edge of the rupture segment that is closest to
        the side edge of the previous segment, and that lies half way
        between the top and bottom of the rupture. We assume that the rupture
        is segmented along strike, not updip. All geometric parameters are
        computed relative to the pseudo-hypocenter."
        """
        hyp_ecef = Vector.fromPoint(
            geo.point.Point(self._hyp.longitude, self._hyp.latitude,
                            self._hyp.depth))
        # Loop over each quad
        self.phyp = [None] * self._nq
        for i in range(self._nq):
            P0, P1, P2, P3 = self._rup.getQuadrilaterals()[i]
            p0 = Vector.fromPoint(P0)  # convert to ECEF
            p1 = Vector.fromPoint(P1)
            p2 = Vector.fromPoint(P2)
            p3 = Vector.fromPoint(P3)

            # Create 4 planes with normals pointing outside rectangle
            hpnp = Vector.cross(p1 - p0, p2 - p0).norm()
            hpp = -hpnp.x * p0.x - hpnp.y * p0.y - hpnp.z * p0.z
            n0 = Vector.cross(p1 - p0, hpnp)
            n1 = Vector.cross(p2 - p1, hpnp)
            n2 = Vector.cross(p3 - p2, hpnp)
            n3 = Vector.cross(p0 - p3, hpnp)

            #-------------------------------------------------------------------
            # Is the hypocenter inside the projected rectangle?
            # Dot products show which side the origin is on.
            # If origin is on same side of all the planes, then it is 'inside'
            #-------------------------------------------------------------------

            sgn0 = np.signbit(Vector.dot(n0, p0 - hyp_ecef))
            sgn1 = np.signbit(Vector.dot(n1, p1 - hyp_ecef))
            sgn2 = np.signbit(Vector.dot(n2, p2 - hyp_ecef))
            sgn3 = np.signbit(Vector.dot(n3, p3 - hyp_ecef))

            if (sgn0 == sgn1) and (sgn1 == sgn2) and (sgn2 == sgn3):
                # Origin is inside. Use distance-to-plane formula.
                d = Vector.dot(hpnp, hyp_ecef) + hpp
                d = d * d

                # Put the pseudo hypocenter on the plane
                D = Vector.dot(hpnp, hyp_ecef) + hpp
                self.phyp[i] = hyp_ecef - hpnp * D

            else:
                # Origin is outside. Find distance to edges
                p0p = np.reshape(p0.getArray() - hyp_ecef.getArray(), [1, 3])
                p1p = np.reshape(p1.getArray() - hyp_ecef.getArray(), [1, 3])
                p2p = np.reshape(p2.getArray() - hyp_ecef.getArray(), [1, 3])
                p3p = np.reshape(p3.getArray() - hyp_ecef.getArray(), [1, 3])
                s1 = _distance_sq_to_segment(p1p, p2p)
                s3 = _distance_sq_to_segment(p3p, p0p)

                # Assuming that the rupture is segmented along strike and not
                # updip (as described by Bayless and somerville), we only
                # need to consider s1 and s3:
                if s1 > s3:
                    e30 = p0 - p3
                    e30norm = e30.norm()
                    mag = e30.mag()
                    self.phyp[i] = p3 + e30norm * (0.5 * mag)
                else:
                    e21 = p1 - p2
                    e21norm = e21.norm()
                    mag = e21.mag()
                    self.phyp[i] = p2 + e21norm * (0.5 * mag)
Exemple #19
0
def test_rv4():
    magnitude = 7.0
    rake = 90.0
    width = np.array([28])
    fltx = np.array([0, 0])
    flty = np.array([0, 32])
    zp = np.array([0])
    dip = np.array([30])

    # Convert to lat/lon
    proj = geo.utils.get_orthographic_projection(-122, -120, 39, 37)
    tlon, tlat = proj(fltx, flty, reverse=True)

    flt = fault.Fault.fromTrace(np.array([tlon[0]]), np.array([tlat[0]]),
                                np.array([tlon[1]]), np.array([tlat[1]]),
                                zp, width, dip, reference='')
    L = flt.getFaultLength()

    # Try to figure out epicenter
    tmp = flt.getQuadrilaterals()[0]
    pp0 = Vector.fromPoint(point.Point(tmp[0].longitude, tmp[0].latitude,
                                       tmp[0].depth))
    pp1 = Vector.fromPoint(point.Point(tmp[1].longitude, tmp[1].latitude,
                                       tmp[1].depth))
    pp2 = Vector.fromPoint(point.Point(tmp[2].longitude, tmp[2].latitude,
                                       tmp[2].depth))
    pp3 = Vector.fromPoint(point.Point(tmp[3].longitude, tmp[3].latitude,
                                       tmp[3].depth))
    dxp = 6/L
    dyp = (width-8)/width
    mp0 = pp0 + (pp1 - pp0)*dxp
    mp1 = pp3 + (pp2 - pp3)*dxp
    rp = mp0 + (mp1 - mp0)*dyp
    epilat,epilon,epidepth = ecef2latlon(rp.x, rp.y, rp.z)

    event = {'lat': epilat,
             'lon': epilon,
             'depth': epidepth,
             'mag': magnitude,
             'id': 'test',
             'locstring': 'rv4',
             'type': 'DS',
             'timezone': 'UTC'}
    event['time'] = ShakeDateTime.utcfromtimestamp(int(time.time()))
    event['created'] = ShakeDateTime.utcfromtimestamp(int(time.time()))

    x = np.linspace(-50, 50, 11)
    y = np.linspace(-50, 50, 11)
    site_x, site_y = np.meshgrid(x, y)
    slon, slat = proj(site_x, site_y, reverse=True)
    deps = np.zeros_like(slon)
    source = Source(event, flt)
    source.setEventParam('rake', rake)

    test1 = Bayless2013(source, slat, slon, deps, T=2.0)

    # Test fd
    fd = test1.getFd()
    fd_test = np.array(
      [[  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
          1.72147747e-03,   1.34981119e-03,   8.95673480e-29,
          1.24449087e-03,   1.16145147e-03,   2.25470229e-03,
          7.05301515e-04,   0.00000000e+00],
       [  0.00000000e+00,   0.00000000e+00,   7.62625126e-03,
          1.25136528e-02,   5.61909403e-03,   3.18694606e-28,
          4.52275052e-03,   4.67980272e-03,   1.02822365e-02,
          5.13171639e-03,  -6.13935060e-03],
       [  0.00000000e+00,   4.00499692e-03,   2.37611880e-02,
          2.37143264e-02,   9.55241972e-03,   5.65693221e-28,
          7.70357238e-03,   7.36477919e-03,   1.48241947e-02,
          8.40402367e-03,  -1.58594139e-02],
       [  8.08392720e-19,   9.38156493e-03,   3.38613859e-02,
          3.85355818e-02,   1.91047521e-02,   1.27066310e-27,
          1.54323543e-02,   1.21915074e-02,   2.04437211e-02,
          1.04933053e-02,  -1.85937074e-02],
       [  2.12026318e-18,   1.37316424e-02,   4.40195705e-02,
          6.16565712e-02,   4.77616016e-02,   5.07336347e-27,
          3.86325509e-02,   1.97966900e-02,   2.64883302e-02,
          1.23336661e-02,  -2.07390404e-02],
       [  2.64338576e-18,   1.45898071e-02,   4.89104103e-02,
          7.70703129e-02,   9.55225254e-02,   1.01875104e-01,
          7.73459333e-02,   2.50275520e-02,   2.93537605e-02,
          1.30949772e-02,  -2.15685118e-02],
       [  2.64330042e-18,   1.45898071e-02,   4.89104103e-02,
          7.70703129e-02,   9.55225254e-02,   1.01910945e-01,
          7.74050830e-02,   2.52307951e-02,   2.92970785e-02,
          1.30880672e-02,  -2.15685118e-02],
       [  2.64318867e-18,   1.45898071e-02,   4.89104103e-02,
          7.70703129e-02,   9.55225254e-02,   1.01933432e-01,
          7.74421253e-02,   2.53572928e-02,   2.92615177e-02,
          1.30837449e-02,  -2.15685118e-02],
       [  2.64305117e-18,   1.45898071e-02,   4.89104103e-02,
          7.70703129e-02,   9.55225254e-02,   1.01942593e-01,
          7.74571361e-02,   2.54081650e-02,   2.92472178e-02,
          1.30820173e-02,  -2.15685118e-02],
       [  2.30140686e-18,   1.40209885e-02,   4.56202616e-02,
          6.63103459e-02,   5.79255225e-02,   7.72925496e-27,
          4.69663059e-02,   2.18399567e-02,   2.72863359e-02,
          1.25728195e-02,  -2.10226512e-02],
       [  1.10671369e-18,   1.04775558e-02,   3.59035524e-02,
          4.24605614e-02,   2.24210618e-02,   1.53459722e-27,
          1.81723013e-02,   1.39298662e-02,   2.14953705e-02,
          1.08710398e-02,  -1.90800441e-02]]
    )
    np.testing.assert_allclose(fd, fd_test, rtol=1e-5)
Exemple #20
0
    def __computeThetaAndS(self, i):
        """
        :param i:
            Compute d for the i-th quad/segment.
        """
        # self.phyp is in ECEF
        tmp = ecef.ecef2latlon(self.phyp[i].x, self.phyp[i].y, self.phyp[i].z)
        epi_ecef = Vector.fromPoint(geo.point.Point(tmp[1], tmp[0], 0.0))
        epi_col = np.array([[epi_ecef.x], [epi_ecef.y], [epi_ecef.z]])

        # First compute along strike vector
        P0, P1, P2, P3 = self._rup.getQuadrilaterals()[i]
        p0 = Vector.fromPoint(P0)  # convert to ECEF
        p1 = Vector.fromPoint(P1)
        e01 = p1 - p0
        e01norm = e01.norm()
        hp0 = p0 - epi_ecef
        hp1 = p1 - epi_ecef
        strike_min = Vector.dot(hp0, e01norm) / 1000.0  # convert to km
        strike_max = Vector.dot(hp1, e01norm) / 1000.0  # convert to km
        strike_col = np.array([[e01norm.x], [e01norm.y],
                               [e01norm.z]])  # ECEF coords

        # Sites
        slat = self._lat
        slon = self._lon

        # Convert sites to ECEF:
        site_ecef_x = np.ones_like(slat)
        site_ecef_y = np.ones_like(slat)
        site_ecef_z = np.ones_like(slat)

        # Make a 3x(#number of sites) matrix of site locations
        # (rows are x, y, z) in ECEF
        site_ecef_x, site_ecef_y, site_ecef_z = ecef.latlon2ecef(
            slat, slon, np.zeros(slon.shape))
        site_mat = np.array([
            np.reshape(site_ecef_x, (-1, )),
            np.reshape(site_ecef_y, (-1, )),
            np.reshape(site_ecef_z, (-1, ))
        ])

        # Epicenter-to-site matrix
        e2s_mat = site_mat - epi_col  # in ECEF
        mag = np.sqrt(np.sum(e2s_mat * e2s_mat, axis=0))

        # Avoid division by zero
        mag[mag == 0] = 1e-12
        e2s_norm = e2s_mat / mag

        # Dot epicenter-to-site with along-strike vector
        s_raw = np.sum(e2s_mat * strike_col, axis=0) / 1000.0  # conver to km

        # Put back into a 2d array
        s_raw = np.reshape(s_raw, self._lat.shape)
        self.s = np.abs(s_raw.clip(min=strike_min,
                                   max=strike_max)).clip(min=np.exp(1))

        # Compute theta
        sdots = np.sum(e2s_norm * strike_col, axis=0)
        theta_raw = np.arccos(sdots)

        # But theta is defined to be the reference angle
        # (i.e., the equivalent angle between 0 and 90 deg)
        sintheta = np.abs(np.sin(theta_raw))
        costheta = np.abs(np.cos(theta_raw))
        theta = np.arctan2(sintheta, costheta)
        self.theta = np.reshape(theta, self._lat.shape)
Exemple #21
0
def get_distance(methods, lat, lon, dep, source,
                 use_median_distance=True):
    """
    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.                                        |
    +--------+----------------------------------------------------------+

    :param methods:
        List of strings (or just a string) of distances to compute.
    :param lat:
       A numpy array of latitudes.
    :param lon:
       A numpy array of longidues.
    :param dep:
       A numpy array of depths (km).
    :param source:
       source instance.
    :param use_median_distance:
        Boolean; only used if GMPE requests fault distances and not fault is
        availalbe. Default is True, meaning that point-source distances are
        adjusted based on magnitude to get the median fault distance.
    :returns:
       dictionary of numpy arrays of distances, size of lon.shape
       IMPORTANT: If a finite fault is not supplied, and the distance
       measures requested include rx, ry, ry0, U, or T, then zeros will
       be returned; if rjb is requested, repi will be returned; if rrup
       is requested, rhypo will be returned.
    """
    fault = source.getFault()
    hypo = source.getHypo()
    if fault is not None:
        quadlist = fault.getQuadrilaterals()
        # Need a copy for GC2 since order of verticies/quads needs to be modivied.
        quadgc2 = copy.deepcopy(quadlist)
    else:
        quadlist = None

    # Dictionary for holding the distances
    distdict = dict()

    if not isinstance(methods, list):
        methods = [methods]

    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')

    if (lat.shape == lon.shape) and (lat.shape == dep.shape):
        pass
    else:
        raise ShakeMapException('lat, lon, and dep must have the same shape.')

    oldshape = lon.shape

    if len(oldshape) == 2:
        newshape = (oldshape[0] * oldshape[1], 1)
    else:
        newshape = (oldshape[0], 1)

    if ('rrup' in methods) or ('rjb' in methods):
        x, y, z = latlon2ecef(lat, lon, dep)
        x.shape = newshape
        y.shape = newshape
        z.shape = newshape
        sites_ecef = np.hstack((x, y, z))

    # Define a projection that spands sites and fault
    if fault is None:
        all_lat = lat
        all_lon = lon
    else:
        all_lat = np.append(lat, fault.getLats())
        all_lon = np.append(lon, fault.getLons())

    west = np.nanmin(all_lon)
    east = np.nanmax(all_lon)
    south = np.nanmin(all_lat)
    north = np.nanmax(all_lat)
    proj = get_orthographic_projection(west, east, north, south)

    # ---------------------------------------------
    # Distances that do not require loop over quads
    # ---------------------------------------------

    if ('repi' in methods) or \
       (('rjb' in methods) and (quadlist is None)) or \
       (('rrup' in methods) and (quadlist is None)) or \
       (('ry0' in methods) and (quadlist is None)) or \
       (('rx' in methods) and (quadlist is None)) or \
       (('T' in methods) and (quadlist is None)) or \
       (('U' in methods) and (quadlist is None)):
        # I don't think this error check makes sense any more because hypo
        # is assigned above with source.getHypo() that constructs it from
        # source._event_dict entries. 
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        repidist = geodetic.distance(hypo.longitude, hypo.latitude, 0.0,
                                     lon, lat, dep)
        repidist = repidist.reshape(oldshape)
        distdict['repi'] = repidist

    if ('rhypo' in methods) or \
       (('rrup' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        rhypodist = geodetic.distance(
            hypo.longitude, hypo.latitude, hypo.depth, lon, lat, dep)
        rhypodist = rhypodist.reshape(oldshape)
        distdict['rhypo'] = rhypodist

    # --------------------------------------------------------
    # Loop over quadlist for those distances that require loop
    # --------------------------------------------------------
    if 'rrup' in methods:
        minrrup = np.ones(newshape, dtype=lon.dtype) * 1e16
    if 'rjb' in methods:
        minrjb = np.ones(newshape, dtype=lon.dtype) * 1e16
    if ('rx' in methods) or ('ry' in methods) or \
       ('ry0' in methods) or ('U' in methods) or ('T' in methods):
        totweight = np.zeros(newshape, dtype=lon.dtype)
        GC2T = np.zeros(newshape, dtype=lon.dtype)
        GC2U = np.zeros(newshape, dtype=lon.dtype)

        if quadlist is not None:
            #-----------------------------------------------------------------
            # For these distances, we need to sort out strike discordance and
            # nominal strike prior to starting the loop if there is more than
            # one segment.
            #-----------------------------------------------------------------

            segind = fault._getSegmentIndex()
            segindnp = np.array(segind)
            uind = np.unique(segind)
            nseg = len(uind)

            #-------------------------------------------------------------------
            # The first thing we need to worry about is finding the coordinate
            # shift. U's origin is " selected from the two endpoints most
            # distant from each other." 
            #-------------------------------------------------------------------

            if nseg > 1:
                # Need to get index of first and last quad
                # for each segment
                iq0 = np.zeros(nseg, dtype='int16')
                iq1 = np.zeros(nseg, dtype='int16')
                for k in uind:
                    ii = [i for i, j in enumerate(segind) if j == uind[k]]
                    iq0[k] = int(np.min(ii))
                    iq1[k] = int(np.max(ii))

                #---------------------------------------------------------------
                # This is an iterator for each possible combination of segments
                # including segment orientations (i.e., flipped). 
                #---------------------------------------------------------------

                it_seg = it.product(it.combinations(uind, 2),
                                    it.product([0, 1], [0, 1]))

                # Placeholder for the segment pair/orientation that gives the
                # largest distance. 
                dist_save = 0

                for k in it_seg:
                    s0ind = k[0][0]
                    s1ind = k[0][1]
                    p0ind = k[1][0]
                    p1ind = k[1][1]
                    if p0ind == 0:
                        P0 = quadlist[iq0[s0ind]][0]
                    else:
                        P0 = quadlist[iq1[s0ind]][1]
                    if p1ind == 0:
                        P1 = quadlist[iq1[s1ind]][0]
                    else:
                        P1 = quadlist[iq0[s1ind]][1]

                    dist = geodetic.distance(P0.longitude, P0.latitude, 0.0,
                                             P1.longitude, P1.latitude, 0.0)
                    if dist > dist_save:
                        dist_save = dist
                        A0 = P0
                        A1 = P1

                #---------------------------------------------------------------
                # A0 and A1 are the furthest two segment endpoints, but we still
                # need to sort out which one is the "origin".
                #---------------------------------------------------------------

                # Goofy while-loop is to adjust the side of the fault where the
                # origin is located
                dummy = -1
                while dummy < 0:
                    A0.depth = 0
                    A1.depth = 0
                    p_origin = Vector.fromPoint(A0)
                    a0 = Vector.fromPoint(A0)
                    a1 = Vector.fromPoint(A1)
                    ahat = (a1 - a0).norm()

                    # Loop over traces
                    e_j = np.zeros(nseg)
                    b_prime = [None] * nseg
                    for j in range(nseg):
                        P0 = quadlist[iq0[j]][0]
                        P1 = quadlist[iq1[j]][1]
                        P0.depth = 0
                        P1.depth = 0
                        p0 = Vector.fromPoint(P0)
                        p1 = Vector.fromPoint(P1)
                        b_prime[j] = p1 - p0
                        e_j[j] = ahat.dot(b_prime[j])
                    E = np.sum(e_j)

                    # List of discordancy
                    dc = [np.sign(a) * np.sign(E) for a in e_j]
                    b = Vector(0, 0, 0)
                    for j in range(nseg):
                        b.x = b.x + b_prime[j].x * dc[j]
                        b.y = b.y + b_prime[j].y * dc[j]
                        b.z = b.z + b_prime[j].z * dc[j]
                    bhat = b.norm()
                    dummy = bhat.dot(ahat)
                    if dummy < 0:
                        tmpA0 = copy.copy(A0)
                        tmpA1 = copy.copy(A1)
                        A0 = tmpA1
                        A1 = tmpA0

                # To fix discordancy, need to flip quads and rearrange
                # the order of quadgc2

                # 1) flip quads
                for i in range(len(quadgc2)):
                    if dc[segind[i]] < 0: #***************U*UUUUUUUSDFUSDfkjjhakjsdhfljkhn
                        quadgc2[i] = reverse_quad(quadgc2[i])

                # 2) rearrange quadlist to remove discordancy
                qind = np.arange(len(quadgc2))
                segnp = np.array(segind)
                for i in range(nseg):
                    qsel = qind[segnp == uind[i]]
                    if dc[i] < 0:
                        qrev = qsel[::-1]
                        qind[segnp == uind[i]] = qrev

                quadgc2old = copy.deepcopy(quadgc2)
                for i in range(len(qind)):
                    quadgc2[i] = quadgc2old[qind[i]]


    if quadlist is not None:
        # Length of prior segments
        s_i = 0.0
        l_i = np.zeros(len(quadlist))
        for i in range(len(quadlist)):
            P0, P1, P2, P3 = quadlist[i]
            G0, G1, G2, G3 = quadgc2[i]

            if 'rrup' in methods:
                rrupdist = _calc_rupture_distance(P0, P1, P2, P3, sites_ecef)
                minrrup = np.minimum(minrrup, rrupdist)

            if 'rjb' in methods:
                S0 = copy.deepcopy(P0)
                S1 = copy.deepcopy(P1)
                S2 = copy.deepcopy(P2)
                S3 = copy.deepcopy(P3)
                S0.depth = 0.0
                S1.depth = 0.0
                S2.depth = 0.0
                S3.depth = 0.0
                rjbdist = _calc_rupture_distance(S0, S1, S2, S3, sites_ecef)
                minrjb = np.minimum(minrjb, rjbdist)

            if ('rx' in methods) or ('ry' in methods) or \
               ('ry0' in methods) or ('U' in methods) or ('T' in methods):
                # Rx, Ry, and Ry0 are all computed if one is requested since
                # they all require similar information for the weights. This
                # isn't necessary for a single segment fault though.
                # Note, we are basing these calculations on GC2 coordinates U
                # and T as described in:
                # Spudich and Chiou (2015)
                # http://dx.doi.org/10.3133/ofr20151028.

                # Compute u_i and t_i for this quad
                t_i = __calc_t_i(G0, G1, lat, lon, proj)
                u_i = __calc_u_i(G0, G1, lat, lon, proj)

                # Quad length
                l_i[i] = get_quad_length(quadlist[i])

                # Weight of segment, three cases
                # Case 3: t_i == 0 and 0 <= u_i <= l_i
                w_i = np.zeros_like(t_i)
                # Case 1:
                ix = t_i != 0
                w_i[ix] = (1.0 / t_i[ix]) * (np.arctan((l_i[i] -
                    u_i[ix]) / t_i[ix]) - np.arctan(-u_i[ix] / t_i[ix]))
                # Case 2:
                ix = (t_i == 0) & ((u_i < 0) | (u_i > l_i[i]))
                w_i[ix] = 1 / (u_i[ix] - l_i[i]) - 1 / u_i[ix]

                totweight = totweight + w_i
                GC2T = GC2T + w_i * t_i
                if nseg == 1:
                    GC2U = GC2U + w_i * (u_i + s_i)
                else:
                    if i == 0:
                        qind = np.array(range(len(quadlist)))
                        l_kj = 0
                        s_ij_1 = 0
                    else:
                        l_kj = l_i[(segindnp == segindnp[i]) & (qind < i)]
                        s_ij_1 = np.sum(l_kj)

                    p1 = Vector.fromPoint(quadgc2[iq0[segind[i]]][0])
                    s_ij_2 = (p1 - p_origin).dot(np.sign(E)*ahat) / 1000.0
                    # Above is GC2N, for GC2T use:
#                    s_ij_2 = (p1 - p_origin).dot(bhat) / 1000.0


                    s_ij = s_ij_1 + s_ij_2
                    GC2U = GC2U + w_i * (u_i + s_ij)
                s_i = s_i + l_i[i]

        # Collect distances from loop into the distance dict
        if 'rjb' in methods:
            minrjb = minrjb.reshape(oldshape)
            distdict['rjb'] = minrjb

        if ('rx' in methods) or ('ry' in methods) or \
           ('ry0' in methods) or ('U' in methods) or ('T' in methods):
            # Normalize by sum of quad weights
            GC2T = GC2T / totweight
            GC2U = GC2U / totweight
            distdict['T'] = copy.deepcopy(GC2T).reshape(oldshape)
            distdict['U'] = copy.deepcopy(GC2U).reshape(oldshape)

            # Take care of Rx
            Rx = copy.deepcopy(GC2T)  # preserve sign (no absolute value)
            Rx = Rx.reshape(oldshape)
            distdict['rx'] = Rx

            # Ry
            Ry = GC2U - s_i / 2.0
            Ry = Ry.reshape(oldshape)
            distdict['ry'] = Ry

            # Ry0
            Ry0 = np.zeros_like(GC2U)
            ix = GC2U < 0
            Ry0[ix] = np.abs(GC2U[ix])
            if nseg > 1:
                s_i = s_ij + l_i[-1]
            ix = GC2U > s_i
            Ry0[ix] = GC2U[ix] - s_i
            Ry0 = Ry0.reshape(oldshape)
            distdict['ry0'] = Ry0

        if 'rrup' in methods:
            minrrup = minrrup.reshape(oldshape)
            distdict['rrup'] = minrrup

    else:
        if 'rjb' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rjb with median rjb given M and repi.')
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                mech = source.getEventParam('mech')
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p7_seis0_20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechR_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechR_ar1p7_seis0_20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechN_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechN_ar1p7_seis0_20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechSS_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_WC94_mechSS_ar1p7_seis0_20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechA_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechA_ar1p0_seis0_15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechR_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechR_ar1p0_seis0_15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechN_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechN_ar1p0_seis0_15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechSS_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rjb_S14_mechSS_ar1p0_seis0_15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rjb_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rjb_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(re.findall(
                            'R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rjb_ratios_tbl['Repi_km']))
                repi2rjb_grid = repi2rjb_ratios_tbl.values[:, 1:]
                repi2rjb_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rjb_grid, kx=1, ky=1)

                def repi2rjb_tbl(repi, M):
                    ratio = repi2rjb_obj.ev(np.log(repi), M)
                    rjb = repi * ratio
                    return rjb
                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rjb_hat = repi2rjb_tbl(repis, mags)
                distdict['rjb'] = rjb_hat
                # -------------------
                # Additional Variance
                # -------------------
                repi2rjbvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rjbvar_grid = repi2rjbvar_ratios_tbl.values[:, 1:]
                repi2rjbvar_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rjbvar_grid, kx=1, ky=1)
                rjbvar = repi2rjbvar_obj.ev(np.log(repis), mags)
                distdict['rjbvar'] = rjbvar
            else:
                warnings.warn('No fault; Replacing rjb with repi')
                distdict['rjb'] = distdict['repi'].copy()
        if 'rrup' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rrup with median rrup given M and repi.')
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                rake = source._event_dict.get('rake')
                mech = rake_to_mech(rake)
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p7_seis0-20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechR_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechR_ar1p7_seis0-20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechN_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechN_ar1p7_seis0-20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechSS_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_WC94_mechSS_ar1p7_seis0-20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechA_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechA_ar1p0_seis0-15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechR_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechR_ar1p0_seis0-15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechN_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechN_ar1p0_seis0-15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechSS_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff", "Rrup_S14_mechSS_ar1p0_seis0-15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff", "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rrup_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rrup_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(re.findall(
                            'R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rrup_ratios_tbl['Repi_km']))
                repi2rrup_grid = repi2rrup_ratios_tbl.values[:, 1:]
                repi2rrup_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rrup_grid, kx=1, ky=1)

                def repi2rrup_tbl(repi, M):
                    ratio = repi2rrup_obj.ev(np.log(repi), M)
                    rrup = repi * ratio
                    return rrup
                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rrup_hat = repi2rrup_tbl(repis, mags)
                distdict['rrup'] = rrup_hat

                # -------------------
                # Additional Variance
                # -------------------
                repi2rrupvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rrupvar_grid = repi2rrupvar_ratios_tbl.values[:, 1:]
                repi2rrupvar_obj = spint.RectBivariateSpline(
                    dist_list, mag_list, repi2rrupvar_grid, kx=1, ky=1)
                rrupvar = repi2rrupvar_obj.ev(np.log(repis), mags)
                distdict['rrupvar'] = rrupvar
            else:
                warnings.warn('No fault; Replacing rrup with rhypo')
                distdict['rrup'] = distdict['rhypo'].copy()
        if 'rx' in methods:
            warnings.warn('No fault; Setting Rx to zero.')
            distdict['rx'] = np.zeros_like(distdict['repi'])
        if 'ry0' in methods:
            warnings.warn('No fault; Setting ry0 to zero')
            distdict['ry0'] = np.zeros_like(distdict['repi'])
        if 'ry' in methods:
            warnings.warn('No fault; Setting ry to zero')
            distdict['ry'] = np.zeros_like(distdict['repi'])
        if 'U' in methods:
            warnings.warn('No fault; Setting U to zero')
            distdict['U'] = np.zeros_like(distdict['repi'])
        if 'T' in methods:
            warnings.warn('No fault; Setting T to zero')
            distdict['T'] = np.zeros_like(distdict['repi'])

    return distdict
Exemple #22
0
    def __computeXiPrime(self):
        """
        Computes the xi' value.
        """
        hypo_ecef = Vector.fromPoint(geo.point.Point(
            self._hyp.longitude, self._hyp.latitude, self._hyp.depth))
        epi_ll = Vector(self._hyp.longitude, self._hyp.latitude, 0)
        epi_ecef = Vector.fromPoint(geo.point.Point(
            epi_ll.x, epi_ll.y, 0))

        slat = self._lat
        slon = self._lon

        # Convert site to ECEF:
        site_ecef_x = np.ones_like(slat)
        site_ecef_y = np.ones_like(slat)
        site_ecef_z = np.ones_like(slat)

        # Make a 3x(#number of sites) matrix of site locations
        # (rows are x, y, z) in ECEF
        site_ecef_x, site_ecef_y, site_ecef_z = latlon2ecef(
            slat, slon, np.zeros(slon.shape))
        site_mat = np.array([np.reshape(site_ecef_x, (-1,)),
                             np.reshape(site_ecef_y, (-1,)),
                             np.reshape(site_ecef_z, (-1,))])

        xi_prime_unscaled = np.zeros_like(slat)

        # Normalize by total number of subfaults. For mtype == 1, the number
        # of subfaults will vary with site and be different for xi_s and
        # xi_p, so keep two variables and sum them for each quad.
        nsubs = np.zeros(np.product(slat.shape))
        nsubp = np.zeros(np.product(slat.shape))

        xi_prime_s = np.zeros(np.product(slat.shape))
        xi_prime_p = np.zeros(np.product(slat.shape))

        for k in range(len(self._flt._quadrilaterals)):
            # Select a quad
            q = self._flt.getQuadrilaterals()[k]

            # Quad mesh (ECEF coords)
            mesh = fault.get_quad_mesh(q, self._dx)

            # Rupture plane normal vector (ECEF coords)
            rpnv = fault.get_quad_normal(q)
            rpnvcol = np.array([[rpnv.x],
                                [rpnv.y],
                                [rpnv.z]])

            # Strike vector (ECEF coords)
            strike_vec = fault.get_quad_strike_vector(q)
            strike_vec_col = np.array([[strike_vec.x], [strike_vec.y], [
                                      strike_vec.z]])  # convert to column vector

            # Make 3x(i*j) matrix of cp
            ni, nj = mesh['llx'].shape

            cp_mat = np.array([np.reshape(mesh['cpx'], (-1,)),
                               np.reshape(mesh['cpy'], (-1,)),
                               np.reshape(mesh['cpz'], (-1,))])

            # Compute matrix of p vectors
            hypcol = np.array([[hypo_ecef.x],
                               [hypo_ecef.y],
                               [hypo_ecef.z]])
            pmat = cp_mat - hypcol

            # Project pmat onto quad
            ndotp = np.sum(pmat * rpnvcol, axis=0)
            pmat = pmat - ndotp * rpnvcol

            mag = np.sqrt(np.sum(pmat * pmat, axis=0))
            pmatnorm = pmat / mag  # like r1

            # According to Rowshandel:
            #   "The choice of the +/- sign in the above equations
            #    depends on the (along-the-strike and across-the-dip)
            #    location of the rupturing sub-fault relative to the
            #    location of the hypocenter."
            # and:
            #   "for the along the strike component of the slip unit
            #    vector, the choice of the sign should result in the
            #    slip unit vector (s) being exactly the same as  the
            #    rupture unit vector (p) for a pure strike-slip case"

            # Strike slip and dip slip components of unit slip vector
            # (ECEF coords)
            ds_mat, ss_mat = _get_quad_slip_ds_ss(
                q, self._rake, cp_mat, pmatnorm)

            slpmat = (ds_mat + ss_mat)
            mag = np.sqrt(np.sum(slpmat * slpmat, axis=0))
            slpmatnorm = slpmat / mag

            # Loop over sites
            for i in range(site_mat.shape[1]):
                sitecol = np.array([[site_mat[0, i]],
                                    [site_mat[1, i]],
                                    [site_mat[2, i]]])

                qmat = sitecol - cp_mat  # 3x(ni*nj), like r2
                mag = np.sqrt(np.sum(qmat * qmat, axis=0))
                qmatnorm = qmat / mag

                # Propagation dot product
                pdotqraw = np.sum(pmatnorm * qmatnorm, axis=0)

                # Slip vector dot product
                sdotqraw = np.sum(slpmatnorm * qmatnorm, axis=0)

                if self._mtype == 1:
                    # Only sum over (+) directivity effect subfaults

                    # xi_p_prime
                    pdotq = pdotqraw.clip(min=0)
                    nsubp[i] = nsubp[i] + np.sum(pdotq > 0)

                    # xi_s_prime
                    sdotq = sdotqraw.clip(min=0)
                    nsubs[i] = nsubs[i] + np.sum(sdotq > 0)

                elif self._mtype == 2:
                    # Sum over contributing subfaults

                    # xi_p_prime
                    pdotq = pdotqraw
                    nsubp[i] = nsubp[i] + cp_mat.shape[1]

                    # xi_s_prime
                    sdotq = sdotqraw
                    nsubs[i] = nsubs[i] + cp_mat.shape[1]

                # Normalize by n sub faults later
                xi_prime_s[i] = xi_prime_s[i] + np.sum(sdotq)
                xi_prime_p[i] = xi_prime_p[i] + np.sum(pdotq)

        # Apply a water level to nsubp and nsubs to avoid division by
        # zero. This should only occur when the numerator is also zero
        # and so the resulting value should be zero.
        nsubs = np.maximum(nsubs, 1)
        nsubp = np.maximum(nsubp, 1)

        # We are outside the 'k' loop over nquads.
        # o Normalize xi_prime_s and xi_prime_p
        # o Reshape them
        # o Add them together with the 'a' weights
        xi_prime_tmp = (self._a_weight) * (xi_prime_s / nsubs) + \
                       (1 - self._a_weight) * (xi_prime_p / nsubp)
        xi_prime_unscaled = xi_prime_unscaled + \
            np.reshape(xi_prime_tmp, slat.shape)

        # Scale so that xi_prime has range (0, 1)
        if self._mtype == 1:
            xi_prime = xi_prime_unscaled
        elif self._mtype == 2:
            xi_prime = 0.5 * (xi_prime_unscaled + 1)

        self._xi_prime = xi_prime
Exemple #23
0
def _calc_rupture_distance(P0, P1, P2, P3, points):
    """
    Calculate the shortest distance from a set of points to a rupture surface.

    :param P0:
        Point object, representing the first top-edge vertex of a fault quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a fault quadrilateral.
    :param P2:
        Point object, representing the first bottom-edge vertex of a fault quadrilateral.
    :param P3:
        Point object, representing the second bottom-edge vertex of a fault quadrilateral.
    :param points:
        Numpy array Nx3 of points (ECEF) to calculate distance from.
    :returns:
        Array of size N of distances (in km) from input points to rupture surface.
    """
    # Convert to ecef
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Make a unit vector normal to the plane
    normalVector = (p1 - p0).cross(p2 - p0).norm()

    dist = np.ones_like(points[:, 0]) * np.nan

    p0d = p0.getArray() - points
    p1d = p1.getArray() - points
    p2d = p2.getArray() - points
    p3d = p3.getArray() - points

    # Create 4 planes with normals pointing outside rectangle
    n0 = (p1 - p0).cross(normalVector).getArray()
    n1 = (p2 - p1).cross(normalVector).getArray()
    n2 = (p3 - p2).cross(normalVector).getArray()
    n3 = (p0 - p3).cross(normalVector).getArray()

    sgn0 = np.signbit(np.sum(n0 * p0d, axis=1))
    sgn1 = np.signbit(np.sum(n1 * p1d, axis=1))
    sgn2 = np.signbit(np.sum(n2 * p2d, axis=1))
    sgn3 = np.signbit(np.sum(n3 * p3d, axis=1))

    inside_idx = (sgn0 == sgn1) & (sgn1 == sgn2) & (sgn2 == sgn3)
    dist[inside_idx] = np.power(np.abs(
        np.sum(p0d[inside_idx, :] * normalVector.getArray(), axis=1)), 2)

    outside_idx = np.logical_not(inside_idx)
    s0 = _distance_sq_to_segment(p0d, p1d)
    s1 = _distance_sq_to_segment(p1d, p2d)
    s2 = _distance_sq_to_segment(p2d, p3d)
    s3 = _distance_sq_to_segment(p3d, p0d)

    smin = np.minimum(np.minimum(s0, s1), np.minimum(s2, s3))
    dist[outside_idx] = smin[outside_idx]
    dist = np.sqrt(dist) / 1000.0
    shp = dist.shape
    if len(shp) == 1:
        dist.shape = (shp[0], 1)
    if np.any(np.isnan(dist)):
        raise ShakeMapException("Could not calculate some distances!")
    dist = np.fliplr(dist)
    return dist