Пример #1
0
def get_quad_slip(q, rake):
    """
    Compute the unit slip vector in ECEF space for a quad and rake angle.

    Args:
        q (list): A quadrilateral; list of four points.
        rake (float): Direction of motion of the hanging wall relative to
        the foot wall, as measured by the angle (deg) from the strike vector.

    Returns:
        Vector: Unit slip vector in ECEF space.

    """
    P0, P1, P2 = q[0:3]
    strike = P0.azimuth(P1)
    dip = get_quad_dip(q)
    s1_local = get_local_unit_slip_vector(strike, dip, rake)
    s0_local = Vector(0, 0, 0)
    qlats = [a.latitude for a in q]
    qlons = [a.longitude for a in q]
    proj = OrthographicProjection(
        np.min(qlons), np.max(qlons), np.min(qlats), np.max(qlats))
    s1_ll = proj(np.array([s1_local.x]), np.array([s1_local.y]), reverse=True)
    s0_ll = proj(np.array([s0_local.x]), np.array([s0_local.y]), reverse=True)
    s1_ecef = Vector.fromTuple(latlon2ecef(s1_ll[1], s1_ll[0], s1_local.z))
    s0_ecef = Vector.fromTuple(latlon2ecef(s0_ll[1], s0_ll[0], s0_local.z))
    slp_ecef = (s1_ecef - s0_ecef).norm()
    return slp_ecef
Пример #2
0
def get_quad_slip(q, rake):
    """
    Compute the unit slip vector in ECEF space for a quad and rake angle.

    Args:
        q (list): A quadrilateral; list of four points.
        rake (float): Direction of motion of the hanging wall relative to
        the foot wall, as measured by the angle (deg) from the strike vector.

    Returns:
        Vector: Unit slip vector in ECEF space.

    """
    P0, P1, P2 = q[0:3]
    strike = P0.azimuth(P1)
    dip = get_quad_dip(q)
    s1_local = get_local_unit_slip_vector(strike, dip, rake)
    s0_local = Vector(0, 0, 0)
    qlats = [a.latitude for a in q]
    qlons = [a.longitude for a in q]
    proj = get_orthographic_projection(
        np.min(qlons), np.max(qlons), np.min(qlats), np.max(qlats))
    s1_ll = proj(np.array([s1_local.x]), np.array([s1_local.y]), reverse=True)
    s0_ll = proj(np.array([s0_local.x]), np.array([s0_local.y]), reverse=True)
    s1_ecef = Vector.fromTuple(latlon2ecef(s1_ll[1], s1_ll[0], s1_local.z))
    s0_ecef = Vector.fromTuple(latlon2ecef(s0_ll[1], s0_ll[0], s0_local.z))
    slp_ecef = (s1_ecef - s0_ecef).norm()
    return slp_ecef
Пример #3
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.

        Args:
            i (int): 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)
Пример #4
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.

        Args:
            i (int): 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)
Пример #5
0
    def getIndividualTopLengths(self):
        """
        Return an array of rupture lengths along top edge (km),
        one for each quadrilateral defined for the rupture.

        :returns:
            Array of lengths in km of top edge of quadrilaterals.
        """
        nquad = self.getNumQuads()
        lengths = np.zeros(nquad)
        for i in range(nquad):
            P0, P1, P2, P3 = self._quadrilaterals[i]
            p0 = Vector.fromPoint(P0)
            p1 = Vector.fromPoint(P1)
            lengths[i] = (p1 - p0).mag() / 1000.0
        return lengths
Пример #6
0
def get_quad_length(q):
    """
    Length of top eduge of a quadrilateral.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        float: Length of quadrilateral in km.

    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    qlength = (p1 - p0).mag() / 1000
    return qlength
Пример #7
0
def get_local_unit_slip_vector(strike, dip, rake):
    """
    Compute the components of a unit slip vector.

    Args:
        strike (float): Clockwise angle (deg) from north of the line at the
            intersection of the rupture plane and the horizontal plane.
        dip (float): Angle (deg) between rupture plane and the horizontal plane
            normal to the strike (0-90 using right hand rule).
        rake (float): Direction of motion of the hanging wall relative to the
            foot wall, as measured by the angle (deg) from the strike vector.

    Returns:
        Vector: Unit slip vector in 'local' N-S, E-W, U-D coordinates.

    """
    strike = np.radians(strike)
    dip = np.radians(dip)
    rake = np.radians(rake)
    sx = np.sin(rake) * np.cos(dip) * np.cos(strike) + \
        np.cos(rake) * np.sin(strike)
    sy = np.sin(rake) * np.cos(dip) * np.sin(strike) + \
        np.cos(rake) * np.cos(strike)
    sz = np.sin(rake) * np.sin(dip)
    return Vector(sx, sy, sz)
Пример #8
0
def get_quad_strike_vector(q):
    """
    Compute the unit vector pointing in the direction of strike for a
    quadrilateral in ECEF coordinates. Top edge assumed to be horizontal.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: The unit vector pointing in strike direction in ECEF coords.
    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    v1 = (p1 - p0).norm()
    return v1
Пример #9
0
def get_quad_strike_vector(q):
    """
    Compute the unit vector pointing in the direction of strike for a
    quadrilateral in ECEF coordinates. Top edge assumed to be horizontal.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: The unit vector pointing in strike direction in ECEF coords.
    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    v1 = (p1 - p0).norm()
    return v1
Пример #10
0
def get_quad_length(q):
    """
    Length of top eduge of a quadrilateral.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        float: Length of quadrilateral in km.

    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    qlength = (p1 - p0).mag() / 1000
    return qlength
Пример #11
0
def is_quad(q):
    """
    Checks that an individual quad is coplanar.

    Args:
        q (list): A quadrilateral; list of four OQ Points.

    Returns:
        tuple: Bool for whether or not the points are planar within tolerance;
            and also the corrected quad where p2 is adjusted to be on the same
            plane as the other points.
    """
    P0, P1, P2, P3 = q

    # Convert points to ECEF
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Unit vector along top edge
    v0 = (p1 - p0).norm()

    # Distance along bottom edge
    d = (p3 - p2).mag()

    # New location for p2 by extending from p3 the same distance and
    # direction that p1 is from p0:
    new_p2 = p3 + v0 * d

    # How far off of the plane is the origin p2?
    planepoints = [p0, p1, p2]
    dist = get_distance_to_plane(planepoints, p2)

    # Is it close enough?
    if dist / d > constants.OFFPLANE_TOLERANCE:
        on_plane = False
    else:
        on_plane = True

    # Fixed quad
    fquad = [p0.toPoint(),
             p1.toPoint(),
             new_p2.toPoint(),
             p3.toPoint()]

    return (on_plane, fquad)
Пример #12
0
def is_quad(q):
    """
    Checks that an individual quad is coplanar.

    Args:
        q (list): A quadrilateral; list of four OQ Points.

    Returns:
        tuple: Bool for whether or not the points are planar within tolerance;
            and also the corrected quad where p2 is adjusted to be on the same
            plane as the other points.
    """
    P0, P1, P2, P3 = q

    # Convert points to ECEF
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Unit vector along top edge
    v0 = (p1 - p0).norm()

    # Distance along bottom edge
    d = (p3 - p2).mag()

    # New location for p2 by extending from p3 the same distance and
    # direction that p1 is from p0:
    new_p2 = p3 + v0 * d

    # How far off of the plane is the origin p2?
    planepoints = [p0, p1, p2]
    dist = get_distance_to_plane(planepoints, p2)

    # Is it close enough?
    if dist / d > constants.OFFPLANE_TOLERANCE:
        on_plane = False
    else:
        on_plane = True

    # Fixed quad
    fquad = [p0.toPoint(),
             p1.toPoint(),
             new_p2.toPoint(),
             p3.toPoint()]

    return (on_plane, fquad)
Пример #13
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.

    Args:
        P0 (point): OQ Point object, representing the first top-edge vertex of
            a rupture quadrilateral.
        P1 (point): OQ Point object, representing the second top-edge vertex of
            a rupture quadrilateral.
        lat (array): A numpy array of latitudes.
        lon (array): A numpy array of longitudes.
        proj (object): An orthographic projection from
            openquake.hazardlib.geo.utils.get_orthographic_projection.

    Returns:
        array: 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 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 u_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
Пример #14
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.

    Args:
        P0 (point): OQ Point object, representing the first top-edge vertex of
            a rupture quadrilateral.
        P1 (point): OQ Point object, representing the second top-edge vertex of
            a rupture quadrilateral.
        lat (array): A numpy array of latitudes.
        lon (array): A numpy array of longitudes.
        proj (object): An orthographic projection from
            openquake.hazardlib.geo.utils.OrthographicProjection.

    Returns:
        array: 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 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 u_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
Пример #15
0
def get_quad_down_dip_vector(q):
    """
    Compute the unit vector pointing down dip for a quadrilateral in
    ECEF coordinates.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: The unit vector pointing down dip in ECEF coords.

    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    p0p1 = p1 - p0
    qnv = get_quad_normal(q)
    ddv = Vector.cross(p0p1, qnv).norm()
    return ddv
Пример #16
0
def get_quad_normal(q):
    """
    Compute the unit normal vector for a quadrilateral in
    ECEF coordinates.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: Normalized normal vector for the quadrilateral in ECEF coords.
    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    p3 = Vector.fromPoint(P3)
    v1 = p1 - p0
    v2 = p3 - p0
    vn = Vector.cross(v2, v1).norm()
    return vn
Пример #17
0
def get_vertical_vector(q):
    """
    Compute the vertical unit vector for a quadrilateral
    in ECEF coordinates.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: Normalized vertical vector for the quadrilateral in ECEF
                coords.
    """
    P0, P1, P2, P3 = q
    P0_up = copy.deepcopy(P0)
    P0_up.depth = P0_up.depth - 1.0
    p0 = Vector.fromPoint(P0)   # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P0_up)
    v1 = (p1 - p0).norm()
    return v1
Пример #18
0
def get_quad_normal(q):
    """
    Compute the unit normal vector for a quadrilateral in
    ECEF coordinates.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: Normalized normal vector for the quadrilateral in ECEF coords.
    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    p3 = Vector.fromPoint(P3)
    v1 = p1 - p0
    v2 = p3 - p0
    vn = Vector.cross(v2, v1).norm()
    return vn
Пример #19
0
def get_vertical_vector(q):
    """
    Compute the vertical unit vector for a quadrilateral
    in ECEF coordinates.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: Normalized vertical vector for the quadrilateral in ECEF
                coords.
    """
    P0, P1, P2, P3 = q
    P0_up = copy.deepcopy(P0)
    P0_up.depth = P0_up.depth - 1.0
    p0 = Vector.fromPoint(P0)   # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P0_up)
    v1 = (p1 - p0).norm()
    return v1
Пример #20
0
def get_quad_down_dip_vector(q):
    """
    Compute the unit vector pointing down dip for a quadrilateral in
    ECEF coordinates.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        Vector: The unit vector pointing down dip in ECEF coords.

    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    p0p1 = p1 - p0
    qnv = get_quad_normal(q)
    ddv = Vector.cross(p0p1, qnv).norm()
    return ddv
Пример #21
0
    def __computeWrup(self):
        """
        Compute the the portion (in km) of the width of the rupture which
        ruptures up-dip from the hypocenter to the top of the rupture.

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

        * This is ambiguous for ruptures 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._rup.getQuadrilaterals())

        # ---------------------------------------------------------------------
        # 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):
            qdist[i] = utils._quad_distance(self._rup.getQuadrilaterals()[i],
                                            hyp_ecef)
        ind = int(np.where(qdist == np.min(qdist))[0][0])
        # *** check that this doesn't break with more than one quad
        q = self._rup.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 = utils.get_quad_down_dip_vector(q)
        self._Wrup = Vector.dot(ddv, hp0) / 1000
Пример #22
0
    def __computeWrup(self):
        """
        Compute the the portion (in km) of the width of the rupture which
        ruptures up-dip from the hypocenter to the top of the rupture.

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

        * This is ambiguous for ruptures 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._rup.getQuadrilaterals())

        # ---------------------------------------------------------------------
        # 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):
            qdist[i] = utils._quad_distance(
                self._rup.getQuadrilaterals()[i], hyp_ecef)
        ind = int(np.where(qdist == np.min(qdist))[0][0])
        # *** check that this doesn't break with more than one quad
        q = self._rup.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 = utils.get_quad_down_dip_vector(q)
        self._Wrup = Vector.dot(ddv, hp0) / 1000
Пример #23
0
 def _fixStrikeDirection(quad):
     P0, P1, P2, P3 = quad
     eps = 1e-6
     p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
     p1 = Vector.fromPoint(P1)
     p2 = Vector.fromPoint(P2)
     p1p0 = p1 - p0
     p2p0 = p2 - p0
     qnv = Vector.cross(p2p0, p1p0).norm()
     tmp = p0 + qnv
     tmplat, tmplon, tmpz = ecef2latlon(tmp.x, tmp.y, tmp.z)
     if (tmpz - P0.depth) < eps:  # If True then do nothing
         fixed = quad
     else:
         newP0 = copy.deepcopy(P1)
         newP1 = copy.deepcopy(P0)
         newP2 = copy.deepcopy(P3)
         newP3 = copy.deepcopy(P2)
         fixed = [newP0, newP1, newP2, newP3]
     return fixed
Пример #24
0
def get_quad_width(q):
    """
    Return width of an individual planar trapezoid, where the p0-p1 distance
    represents the long side.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        float: Width of planar trapezoid in km.
    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p3 = Vector.fromPoint(P3)
    AB = p0 - p1
    AC = p0 - p3
    t1 = (AB.cross(AC).cross(AB)).norm()
    width = t1.dot(AC) / 1000.0

    return width
Пример #25
0
def get_quad_width(q):
    """
    Return width of an individual planar trapezoid, where the p0-p1 distance
    represents the long side.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        float: Width of planar trapezoid.
    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p3 = Vector.fromPoint(P3)
    AB = p0 - p1
    AC = p0 - p3
    t1 = (AB.cross(AC).cross(AB)).norm()
    width = t1.dot(AC)

    return width
Пример #26
0
def get_quad_dip(q):
    """
    Dip of a quadrilateral.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        float: Dip in degrees.

    """
    N = get_quad_normal(q)
    V = get_vertical_vector(q)
    dip = np.degrees(np.arccos(Vector.dot(N, V)))
    return dip
Пример #27
0
def get_quad_dip(q):
    """
    Dip of a quadrilateral.

    Args:
        q (list): A quadrilateral; list of four points.

    Returns:
        float: Dip in degrees.

    """
    N = get_quad_normal(q)
    V = get_vertical_vector(q)
    dip = np.degrees(np.arccos(Vector.dot(N, V)))
    return dip
Пример #28
0
    def getDip(self):
        """
        Return average dip of all quadrilaterals in the rupture.

        Returns:
           float: Average dip in degrees.

        """
        dipsum = 0.0
        for quad in self._quadrilaterals:
            N = utils.get_quad_normal(quad)
            V = utils.get_vertical_vector(quad)
            dipsum = dipsum + np.degrees(np.arccos(Vector.dot(N, V)))
        dip = dipsum / len(self._quadrilaterals)
        return dip
Пример #29
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)
Пример #30
0
def _quad_distance(q, points, horizontal=False):
    """
    Calculate the shortest distance from a set of points to a rupture surface.

    Args:
        q (list): A quadrilateral; list of four points.
        points (array): Numpy array Nx3 of points (ECEF) to calculate distance
            from.
        horizontal:  Boolean indicating whether to treat points inside quad as
            0 distance.

    Returns:
        float: Array of size N of distances (in km) from input points to
            rupture surface.
    """
    P0, P1, P2, P3 = q

    if horizontal:
        # project this quad to the surface
        P0 = Point(P0.x, P0.y, 0)
        P1 = Point(P1.x, P1.y, 0)
        P2 = Point(P2.x, P2.y, 0)
        P3 = Point(P3.x, P3.y, 0)

    # 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)
    if horizontal:
        dist[inside_idx] = 0.0
    else:
        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 Exception("Could not calculate some distances!")
    dist = np.fliplr(dist)
    return dist
Пример #31
0
def _computeGC2(rupture, lon, lat, depth):
    """
    Method for computing GC2 from a ShakeMap Rupture instance.

    Args:
        rupture (Rupture): ShakeMap rupture object.
        lon (array): Numpy array of site longitudes.
        lat (array): Numpy array of site latitudes.
        depth (array): Numpy array of site depths.

    Returns:
        dict: Dictionary of GC2 distances. Keys include "T", "U", "rx"
            "ry", "ry0".
    """

    quadlist = rupture.getQuadrilaterals()
    quadgc2 = copy.deepcopy(quadlist)

    oldshape = lon.shape

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

    # -------------------------------------------------------------------------
    # Define a projection that spans sites and rupture
    # -------------------------------------------------------------------------

    all_lat = np.append(lat, rupture.lats)
    all_lon = np.append(lon, rupture.lons)

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

    totweight = np.zeros(newshape, dtype=lon.dtype)
    GC2T = np.zeros(newshape, dtype=lon.dtype)
    GC2U = np.zeros(newshape, dtype=lon.dtype)

    # -------------------------------------------------------------------------
    # First sort out strike discordance and nominal strike prior to
    # starting the loop if there is more than one group/trace.
    # -------------------------------------------------------------------------
    group_ind = rupture._getGroupIndex()

    # Need group_ind as numpy array for sensible indexing...
    group_ind_np = np.array(group_ind)
    uind = np.unique(group_ind_np)
    n_groups = len(uind)

    if n_groups > 1:
        # ---------------------------------------------------------------------
        # 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."
        # ---------------------------------------------------------------------

        # Need to get index of first and last quad
        # for each segment
        iq0 = np.zeros(n_groups, dtype='int16')
        iq1 = np.zeros(n_groups, dtype='int16')
        for k in uind:
            ii = [i for i, j in enumerate(group_ind) 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 traces
        # including trace orientations (i.e., flipped).
        # ---------------------------------------------------------------------

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

        # Placeholder for the trace 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".
        # ---------------------------------------------------------------------

        # This goofy while-loop is to adjust the side of the rupture 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(n_groups)
            b_prime = [None] * n_groups
            for j in range(n_groups):
                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(n_groups):
                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.deepcopy(A0)
                A0 = copy.deepcopy(A1)
                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[group_ind[i]] < 0:
                quadgc2[i] = reverse_quad(quadgc2[i])

        # 2) rearrange quadlist order
        qind = np.arange(len(quadgc2))
        for i in range(n_groups):
            qsel = qind[group_ind_np == uind[i]]
            if dc[i] < 0:
                qrev = qsel[::-1]
                qind[group_ind_np == uind[i]] = qrev

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

        # End of if-statement for adjusting group discordancy

    s_i = 0.0
    l_i = np.zeros(len(quadgc2))

    for i in range(len(quadgc2)):
        G0, G1, G2, G3 = quadgc2[i]

        # 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 (top edge)
        l_i[i] = get_quad_length(quadgc2[i])

        # ---------------------------------------------------------------------
        # Weight of segment, three cases
        # ---------------------------------------------------------------------

        # Case 3: t_i == 0 and 0 <= u_i <= l_i
        w_i = np.zeros_like(t_i)

        # To avoid division by zero in totweight later on:
        ix = (t_i == 0) & (0 <= u_i) & (u_i <= l_i[i])
        totweight[ix] = 1.0

        # 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 n_groups == 1:
            GC2U = GC2U + w_i * (u_i + s_i)
        else:
            if i == 0:
                qind = np.array(range(len(quadgc2)))
                l_kj = 0
                s_ij_1 = 0
            else:
                l_kj = l_i[(group_ind_np == group_ind_np[i]) & (qind < i)]
                s_ij_1 = np.sum(l_kj)

            # First endpoint in the current 'group' (or 'trace' in GC2 terms)
            p1 = Vector.fromPoint(quadgc2[iq0[group_ind[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]

    GC2T = GC2T / totweight
    GC2U = GC2U / totweight

    # Dictionary for holding the distances
    distdict = dict()

    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 n_groups > 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

    return distdict
Пример #32
0
def get_hypo(edges, args):
    """
    Args:
        edges (list): A list of two lists of points; the first list corresponds
            to the top edge and the second is the bottom edge.
        args (ArgumentParser): argparse object.

    Returns:
        tuple: Hypocenter (lat, lon depth).

    """
    top = copy.deepcopy(edges[0])
    bot = copy.deepcopy(edges[1])

    # Along strike and along dip distance (0-1)
    # NOTE: This could also be made a function of mechanism
    if args.dirind == -1:
        # no directivity
        dxp = 0.5  # strike
        dyp = 0.6  # dip
    elif args.dirind == 0:
        # first unilateral
        dxp = 0.05  # strike
        dyp = 0.6  # dip
    elif args.dirind == 2:
        # second unilateral
        dxp = 0.95  # strike
        dyp = 0.6  # dip
    elif args.dirind == 1:
        # bilateral
        dxp = 0.5  # strike
        dyp = 0.6  # dip

    # Convert to ECEF
    topxy = [
        Vector.fromPoint(geo.point.Point(p.longitude, p.latitude, p.depth))
        for p in top
    ]
    botxy = [
        Vector.fromPoint(geo.point.Point(p.longitude, p.latitude, p.depth))
        for p in bot
    ]

    # Compute distances along edges for each vertex
    t0 = topxy[0]
    b0 = botxy[0]
    topdist = np.array([t0.distance(p) for p in topxy])
    botdist = np.array([b0.distance(p) for p in botxy])

    # Normalize distance from 0 to 1
    topdist = topdist / np.max(topdist)
    botdist = botdist / np.max(botdist)

    #---------------------------------------------------------------------------
    # Find points of surrounding quad
    #---------------------------------------------------------------------------
    tix0 = np.amax(np.where(topdist < dxp))
    tix1 = np.amin(np.where(topdist > dxp))
    bix0 = np.amax(np.where(botdist < dxp))
    bix1 = np.amin(np.where(botdist > dxp))

    # top left
    pp0 = topxy[tix0]

    # top right
    pp1 = topxy[tix1]

    # bottom right
    pp2 = botxy[bix0]

    # bottom left
    pp3 = botxy[bix1]

    # How far from pp0 to pp1, and pp2 to pp3?
    dxt = (dxp - topdist[tix0]) / (topdist[tix1] - topdist[tix0])
    dxb = (dxp - botdist[bix0]) / (botdist[bix1] - botdist[bix0])

    mp0 = pp0 + (pp1 - pp0) * dxt
    mp1 = pp3 + (pp2 - pp3) * dxb
    rp = mp0 + (mp1 - mp0) * dyp
    hlat, hlon, hdepth = ecef2latlon(rp.x, rp.y, rp.z)

    return hlat, hlon, hdepth
Пример #33
0
    def getDepthAtPoint(self, lat, lon):
        SMALL_DISTANCE = 2e-03  # 2 meters
        depth = np.nan

        tmp, _ = self.computeRjb(np.array([lon]), np.array([lat]),
                                 np.array([0]))
        if tmp > SMALL_DISTANCE:
            return depth

        i = 0
        imin = -1
        dmin = 9999999999999999
        for quad in self.getQuadrilaterals():
            pX = Vector.fromPoint(Point(lon, lat, 0))
            points = np.reshape(np.array([pX.x, pX.y, pX.z]), (1, 3))
            rjb = utils._quad_distance(quad, points, horizontal=True)
            if rjb[0][0] < dmin:
                dmin = rjb[0][0]
                imin = i
            i += 1

        quad = self._quadrilaterals[imin]
        P0, P1, P2, P3 = quad
        # project the quad and the point in question to orthographic defined by
        # quad
        xmin = np.min([P0.x, P1.x, P2.x, P3.x])
        xmax = np.max([P0.x, P1.x, P2.x, P3.x])
        ymin = np.min([P0.y, P1.y, P2.y, P3.y])
        ymax = np.max([P0.y, P1.y, P2.y, P3.y])
        proj = OrthographicProjection(xmin, xmax, ymax, ymin)

        # project each vertex of quad (at 0 depth)
        s0x, s0y = proj(P0.x, P0.y)
        s1x, s1y = proj(P1.x, P1.y)
        s2x, s2y = proj(P2.x, P2.y)
        s3x, s3y = proj(P3.x, P3.y)
        sxx, sxy = proj(lon, lat)

        # turn these to vectors
        s0 = Vector(s0x, s0y, 0)
        s1 = Vector(s1x, s1y, 0)
        s3 = Vector(s3x, s3y, 0)
        sx = Vector(sxx, sxy, 0)

        # Compute vector from s0 to s1
        s0s1 = s1 - s0
        # Compute the vector from s0 to s3
        s0s3 = s3 - s0
        # Compute the vector from s0 to sx
        s0sx = sx - s0

        # cross products
        s0normal = s0s3.cross(s0s1)
        dd = s0s1.cross(s0normal)
        # normalize dd (down dip direction)
        ddn = dd.norm()
        # dot product
        sxdd = ddn.dot(s0sx)

        # get width of quad (convert from km to m)
        w = utils.get_quad_width(quad) * 1000

        # Get weights for top and bottom edge depths
        N = utils.get_quad_normal(quad)
        V = utils.get_vertical_vector(quad)
        dip = np.degrees(np.arccos(Vector.dot(N, V)))
        ws = (w * np.cos(np.radians(dip)))
        wtt = (ws - sxdd) / ws
        wtb = sxdd / ws

        # Compute the depth of of the plane at Px:
        depth = wtt * P0.z + wtb * P3.z * 1000

        return depth
Пример #34
0
def test_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,
        'eventsourcecode': 'so6',
        'rake': rake
    })

    # Rupture
    rup = 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,
        'eventsourcecode': '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)
Пример #35
0
    def _computeStikeDip(self):
        """
        Loop over all triangles and get the average normal, north, and up
        vectors in ECEF. Use these to compute a representative strike and dip.
        """
        seg = self._group_index
        groups = np.unique(seg)
        ng = len(groups)
        norm_vec = Vector(0, 0, 0)
        north_vec = Vector(0, 0, 0)
        up_vec = Vector(0, 0, 0)
        for i in range(ng):
            group_segments = np.where(groups[i] == seg)[0]
            nseg = len(group_segments) - 1
            for j in range(nseg):
                ind = group_segments[j]
                P0 = Point(self._toplons[ind],
                           self._toplats[ind],
                           self._topdeps[ind])
                P1 = Point(self._toplons[ind + 1],
                           self._toplats[ind + 1],
                           self._topdeps[ind + 1])
                P2 = Point(self._botlons[ind + 1],
                           self._botlats[ind + 1],
                           self._botdeps[ind + 1])
                P3 = Point(self._botlons[ind],
                           self._botlats[ind],
                           self._botdeps[ind])
                P1up = Point(self._toplons[ind + 1],
                             self._toplats[ind + 1],
                             self._topdeps[ind + 1] - 1.0)
                P1N = Point(self._toplons[ind + 1],
                            self._toplats[ind + 1] + 0.001,
                            self._topdeps[ind + 1])
                P3up = Point(self._botlons[ind],
                             self._botlats[ind],
                             self._botdeps[ind] - 1.0)
                P3N = Point(self._botlons[ind],
                            self._botlats[ind] + 0.001,
                            self._botdeps[ind])
                p0 = Vector.fromPoint(P0)
                p1 = Vector.fromPoint(P1)
                p2 = Vector.fromPoint(P2)
                p3 = Vector.fromPoint(P3)
                p1up = Vector.fromPoint(P1up)
                p1N = Vector.fromPoint(P1N)
                p3up = Vector.fromPoint(P3up)
                p3N = Vector.fromPoint(P3N)

                # Sides
                s01 = p1 - p0
                s02 = p2 - p0
                s03 = p3 - p0
                s21 = p1 - p2
                s23 = p3 - p2

                # First triangle
                t1norm = (s02.cross(s01)).norm()
                a = s01.mag()
                b = s02.mag()
                c = s21.mag()
                s = (a + b + c) / 2
                A1 = np.sqrt(s * (s - a) * (s - b) * (s - c)) / 1000

                # Second triangle
                t2norm = (s03.cross(s02)).norm()
                a = s03.mag()
                b = s23.mag()
                c = s02.mag()
                s = (a + b + c) / 2
                A2 = np.sqrt(s * (s - a) * (s - b) * (s - c)) / 1000

                # Up and North
                p1up = (p1up - p1).norm()
                p3up = (p3up - p3).norm()
                p1N = (p1N - p1).norm()
                p3N = (p3N - p3).norm()

                # Combine
                norm_vec = norm_vec + A1 * t1norm + A2 * t2norm
                north_vec = north_vec + A1 * p1N + A2 * p3N
                up_vec = up_vec + A1 * p1up + A2 * p3up

        norm_vec = norm_vec.norm()
        north_vec = north_vec.norm()
        up_vec = up_vec.norm()

        # Do I need to flip the vector because it is pointing down (i.e.,
        # right-hand rule is violated)?
        flip = np.sign(up_vec.dot(norm_vec))
        norm_vec = flip * norm_vec

        # Angle between up_vec and norm_vec is dip
        self._dip = np.arcsin(up_vec.cross(norm_vec).mag()) * 180 / np.pi

        # Normal vector projected to horizontal plane
        nvph = (norm_vec - up_vec.dot(norm_vec) * up_vec).norm()

        # Dip direction is angle between nvph and north; strike is orthogonal.
        cp = nvph.cross(north_vec)
        sign = np.sign(cp.dot(up_vec))
        dp = nvph.dot(north_vec)
        strike = np.arctan2(sign * cp.mag(), dp) * 180 / np.pi - 90
        if strike < -180:
            strike = strike + 360
        self._strike = strike
Пример #36
0
def test_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,
                     'eventsourcecode': 'so6',
                     'rake': rake})

    # Rupture
    rup = 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,
                     'eventsourcecode': '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)
Пример #37
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,
                     'eventsourcecode': 'rv4',
                     'rake': rake})

    # Rupture
    rup = 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,
                     'eventsourcecode': '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)
Пример #38
0
def get_quad_mesh(q, dx):
    """
    Create a mesh from a quadrilateal.

    Args:
        q (list): A quadrilateral; list of four points.
        dx (float):  Target dx in km; used to get nx and ny of mesh, but mesh
            snaps to edges of rupture so actual dx/dy will not actually equal
            this value in general.

    Returns:
        dict: Mesh dictionary, which includes numpy arrays:

        - llx: lower left x coordinate in ECEF coords.
        - lly: lower left y coordinate in ECEF coords.
        - llz: lower left z coordinate in ECEF coords.
        - ulx: upper left x coordinate in ECEF coords.
        - etc.

    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Get nx based on length of top edge, minimum allowed is 2
    toplen_km = get_quad_length(q)
    nx = int(np.max([round(toplen_km / dx, 0) + 1, 2]))

    # Get array of points along top and bottom edges
    xfac = np.linspace(0, 1, nx)
    topp = [p0 + (p1 - p0) * a for a in xfac]
    botp = [p3 + (p2 - p3) * a for a in xfac]

    # Get ny based on mean length of vectors connecting top and bottom points
    ylen_km = np.ones(nx)
    for i in range(nx):
        ylen_km[i] = (topp[i] - botp[i]).mag() / 1000
    ny = int(np.max([round(np.mean(ylen_km) / dx, 0) + 1, 2]))
    yfac = np.linspace(0, 1, ny)

    # Build mesh: dict of ny by nx arrays (x, y, z):
    mesh = {'x': np.zeros([ny, nx]), 'y': np.zeros(
        [ny, nx]), 'z': np.zeros([ny, nx])}
    for i in range(nx):
        mpts = [topp[i] + (botp[i] - topp[i]) * a for a in yfac]
        mesh['x'][:, i] = [a.x for a in mpts]
        mesh['y'][:, i] = [a.y for a in mpts]
        mesh['z'][:, i] = [a.z for a in mpts]

    # Make arrays of pixel corners
    mesh['llx'] = mesh['x'][1:, 0:-1]
    mesh['lrx'] = mesh['x'][1:, 1:]
    mesh['ulx'] = mesh['x'][0:-1, 0:-1]
    mesh['urx'] = mesh['x'][0:-1, 1:]
    mesh['lly'] = mesh['y'][1:, 0:-1]
    mesh['lry'] = mesh['y'][1:, 1:]
    mesh['uly'] = mesh['y'][0:-1, 0:-1]
    mesh['ury'] = mesh['y'][0:-1, 1:]
    mesh['llz'] = mesh['z'][1:, 0:-1]
    mesh['lrz'] = mesh['z'][1:, 1:]
    mesh['ulz'] = mesh['z'][0:-1, 0:-1]
    mesh['urz'] = mesh['z'][0:-1, 1:]
    mesh['cpx'] = np.zeros_like(mesh['llx'])
    mesh['cpy'] = np.zeros_like(mesh['llx'])
    mesh['cpz'] = np.zeros_like(mesh['llx'])

    # i and j are indices over subruptures
    ni, nj = mesh['llx'].shape
    for i in range(0, ni):
        for j in range(0, nj):
            # Rupture corner points
            pp0 = Vector(
                mesh['ulx'][i, j], mesh['uly'][i, j], mesh['ulz'][i, j])
            pp1 = Vector(
                mesh['urx'][i, j], mesh['ury'][i, j], mesh['urz'][i, j])
            pp2 = Vector(
                mesh['lrx'][i, j], mesh['lry'][i, j], mesh['lrz'][i, j])
            pp3 = Vector(
                mesh['llx'][i, j], mesh['lly'][i, j], mesh['llz'][i, j])
            # Find center of quad
            mp0 = pp0 + (pp1 - pp0) * 0.5
            mp1 = pp3 + (pp2 - pp3) * 0.5
            cp = mp0 + (mp1 - mp0) * 0.5
            mesh['cpx'][i, j] = cp.x
            mesh['cpy'][i, j] = cp.y
            mesh['cpz'][i, j] = cp.z
    return mesh
Пример #39
0
def _computeGC2(rupture, lon, lat, depth):
    """
    Method for computing GC2 from a ShakeMap Rupture instance.

    Args:
        rupture (Rupture): ShakeMap rupture object.
        lon (array): Numpy array of site longitudes.
        lat (array): Numpy array of site latitudes.
        depth (array): Numpy array of site depths.

    Returns:
        dict: Dictionary of GC2 distances. Keys include "T", "U", "rx"
            "ry", "ry0".
    """

    quadlist = rupture.getQuadrilaterals()
    quadgc2 = copy.deepcopy(quadlist)

    oldshape = lon.shape

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

    # -------------------------------------------------------------------------
    # Define a projection that spans sites and rupture
    # -------------------------------------------------------------------------

    all_lat = np.append(lat, rupture.lats)
    all_lon = np.append(lon, rupture.lons)

    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)

    totweight = np.zeros(newshape, dtype=lon.dtype)
    GC2T = np.zeros(newshape, dtype=lon.dtype)
    GC2U = np.zeros(newshape, dtype=lon.dtype)

    # -------------------------------------------------------------------------
    # First sort out strike discordance and nominal strike prior to
    # starting the loop if there is more than one group/trace.
    # -------------------------------------------------------------------------
    group_ind = rupture._getGroupIndex()

    # Need group_ind as numpy array for sensible indexing...
    group_ind_np = np.array(group_ind)
    uind = np.unique(group_ind_np)
    n_groups = len(uind)

    if n_groups > 1:
        # ---------------------------------------------------------------------
        # 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."
        # ---------------------------------------------------------------------

        # Need to get index of first and last quad
        # for each segment
        iq0 = np.zeros(n_groups, dtype='int16')
        iq1 = np.zeros(n_groups, dtype='int16')
        for k in uind:
            ii = [i for i, j in enumerate(group_ind) 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 traces
        # including trace orientations (i.e., flipped).
        # ---------------------------------------------------------------------

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

        # Placeholder for the trace 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".
        # ---------------------------------------------------------------------

        # This goofy while-loop is to adjust the side of the rupture 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(n_groups)
            b_prime = [None] * n_groups
            for j in range(n_groups):
                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(n_groups):
                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.deepcopy(A0)
                tmpA1 = copy.deepcopy(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[group_ind[i]] < 0:
                quadgc2[i] = reverse_quad(quadgc2[i])

        # 2) rearrange quadlist order
        qind = np.arange(len(quadgc2))
        for i in range(n_groups):
            qsel = qind[group_ind_np == uind[i]]
            if dc[i] < 0:
                qrev = qsel[::-1]
                qind[group_ind_np == uind[i]] = qrev

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

        # End of if-statement for adjusting group discordancy

    s_i = 0.0
    l_i = np.zeros(len(quadgc2))

    for i in range(len(quadgc2)):
        G0, G1, G2, G3 = quadgc2[i]

        # 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 (top edge)
        l_i[i] = get_quad_length(quadgc2[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 n_groups == 1:
            GC2U = GC2U + w_i * (u_i + s_i)
        else:
            if i == 0:
                qind = np.array(range(len(quadgc2)))
                l_kj = 0
                s_ij_1 = 0
            else:
                l_kj = l_i[(group_ind_np == group_ind_np[i]) & (qind < i)]
                s_ij_1 = np.sum(l_kj)

            # First endpoint in the current 'group' (or 'trace' in GC2 terms)
            p1 = Vector.fromPoint(quadgc2[iq0[group_ind[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]

    GC2T = GC2T / totweight
    GC2U = GC2U / totweight

    # Dictionary for holding the distances
    distdict = dict()

    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 n_groups > 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

    return distdict
Пример #40
0
    def __computeXiPrime(self):
        """
        Computes the xi' value.
        """
        hypo_ecef = Vector.fromPoint(geo.point.Point(
            self._hyp.longitude, self._hyp.latitude, self._hyp.depth))

        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 subruptures. For mtype == 1, the number
        # of subruptures 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._rup.getQuadrilaterals())):
            # Select a quad
            q = self._rup.getQuadrilaterals()[k]

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

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

            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 subruptures

                    # 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 subruptures

                    # 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 ruptures 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
Пример #41
0
    def __computeThetaAndS(self, i):
        """
        Args:
            i (int): 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)
Пример #42
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)
Пример #43
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,
        'eventsourcecode': 'rv4',
        'rake': rake
    })

    # Rupture
    rup = 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,
        'eventsourcecode': '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)
Пример #44
0
    def __computeXiPrime(self):
        """
        Computes the xi' value.
        """
        hypo_ecef = Vector.fromPoint(
            geo.point.Point(self._hyp.longitude, self._hyp.latitude,
                            self._hyp.depth))

        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 subruptures. For mtype == 1, the number
        # of subruptures 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._rup.getQuadrilaterals())):
            # Select a quad
            q = self._rup.getQuadrilaterals()[k]

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

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

            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 subruptures

                    # 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 subruptures

                    # 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 ruptures 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
Пример #45
0
def _quad_distance(q, points, horizontal=False):
    """
    Calculate the shortest distance from a set of points to a rupture surface.

    Args:
        q (list): A quadrilateral; list of four points.
        points (array): Numpy array Nx3 of points (ECEF) to calculate distance
            from.
        horizontal:  Boolean indicating whether to treat points inside quad as
            0 distance.

    Returns:
        float: Array of size N of distances (in km) from input points to
            rupture surface.
    """
    P0, P1, P2, P3 = q

    if horizontal:
        # project this quad to the surface
        P0 = Point(P0.x, P0.y, 0)
        P1 = Point(P1.x, P1.y, 0)
        P2 = Point(P2.x, P2.y, 0)
        P3 = Point(P3.x, P3.y, 0)

    # 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)
    if horizontal:
        dist[inside_idx] = 0.0
    else:
        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 ShakeLibException("Could not calculate some distances!")
    dist = np.fliplr(dist)
    return dist
Пример #46
0
    def __computeThetaAndS(self, i):
        """
        Args:
            i (int): 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)
Пример #47
0
    def _computeStrikeDip(self):
        """
        Loop over all triangles and get the average normal, north, and up
        vectors in ECEF. Use these to compute a representative strike and dip.
        """
        seg = self._group_index
        groups = np.unique(seg)
        ng = len(groups)
        norm_vec = Vector(0, 0, 0)
        north_vec = Vector(0, 0, 0)
        up_vec = Vector(0, 0, 0)
        for i in range(ng):
            group_segments = np.where(groups[i] == seg)[0]
            nseg = len(group_segments) - 1
            for j in range(nseg):
                ind = group_segments[j]
                P0 = Point(self._toplons[ind], self._toplats[ind],
                           self._topdeps[ind])
                P1 = Point(self._toplons[ind + 1], self._toplats[ind + 1],
                           self._topdeps[ind + 1])
                P2 = Point(self._botlons[ind + 1], self._botlats[ind + 1],
                           self._botdeps[ind + 1])
                P3 = Point(self._botlons[ind], self._botlats[ind],
                           self._botdeps[ind])
                P1up = Point(self._toplons[ind + 1], self._toplats[ind + 1],
                             self._topdeps[ind + 1] - 1.0)
                P1N = Point(self._toplons[ind + 1],
                            self._toplats[ind + 1] + 0.001,
                            self._topdeps[ind + 1])
                P3up = Point(self._botlons[ind], self._botlats[ind],
                             self._botdeps[ind] - 1.0)
                P3N = Point(self._botlons[ind], self._botlats[ind] + 0.001,
                            self._botdeps[ind])
                p0 = Vector.fromPoint(P0)
                p1 = Vector.fromPoint(P1)
                p2 = Vector.fromPoint(P2)
                p3 = Vector.fromPoint(P3)
                p1up = Vector.fromPoint(P1up)
                p1N = Vector.fromPoint(P1N)
                p3up = Vector.fromPoint(P3up)
                p3N = Vector.fromPoint(P3N)

                # Sides
                s01 = p1 - p0
                s02 = p2 - p0
                s03 = p3 - p0
                s21 = p1 - p2
                s23 = p3 - p2

                # First triangle
                t1norm = (s02.cross(s01)).norm()
                a = s01.mag()
                b = s02.mag()
                c = s21.mag()
                s = (a + b + c) / 2
                A1 = np.sqrt(s * (s - a) * (s - b) * (s - c)) / 1000

                # Second triangle
                t2norm = (s03.cross(s02)).norm()
                a = s03.mag()
                b = s23.mag()
                c = s02.mag()
                s = (a + b + c) / 2
                A2 = np.sqrt(s * (s - a) * (s - b) * (s - c)) / 1000

                # Up and North
                p1up = (p1up - p1).norm()
                p3up = (p3up - p3).norm()
                p1N = (p1N - p1).norm()
                p3N = (p3N - p3).norm()

                # Combine
                norm_vec = norm_vec + A1 * t1norm + A2 * t2norm
                north_vec = north_vec + A1 * p1N + A2 * p3N
                up_vec = up_vec + A1 * p1up + A2 * p3up

        norm_vec = norm_vec.norm()
        north_vec = north_vec.norm()
        up_vec = up_vec.norm()

        # Do I need to flip the vector because it is pointing down (i.e.,
        # right-hand rule is violated)?
        flip = np.sign(up_vec.dot(norm_vec))
        norm_vec = flip * norm_vec

        # Angle between up_vec and norm_vec is dip
        self._dip = np.arcsin(up_vec.cross(norm_vec).mag()) * 180 / np.pi

        # Normal vector projected to horizontal plane
        nvph = (norm_vec - up_vec.dot(norm_vec) * up_vec).norm()

        # Dip direction is angle between nvph and north; strike is orthogonal.
        cp = nvph.cross(north_vec)
        sign = np.sign(cp.dot(up_vec))
        dp = nvph.dot(north_vec)
        strike = np.arctan2(sign * cp.mag(), dp) * 180 / np.pi - 90
        if strike < -180:
            strike = strike + 360
        self._strike = strike
Пример #48
0
def get_quad_mesh(q, dx):
    """
    Create a mesh from a quadrilateal.

    Args:
        q (list): A quadrilateral; list of four points.
        dx (float):  Target dx in km; used to get nx and ny of mesh, but mesh
            snaps to edges of rupture so actual dx/dy will not actually equal
            this value in general.

    Returns:
        dict: Mesh dictionary, which includes numpy arrays:

        - llx: lower left x coordinate in ECEF coords.
        - lly: lower left y coordinate in ECEF coords.
        - llz: lower left z coordinate in ECEF coords.
        - ulx: upper left x coordinate in ECEF coords.
        - etc.

    """
    P0, P1, P2, P3 = q
    p0 = Vector.fromPoint(P0)  # fromPoint converts to ECEF
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Get nx based on length of top edge, minimum allowed is 2
    toplen_km = get_quad_length(q)
    nx = int(np.max([round(toplen_km / dx, 0) + 1, 2]))

    # Get array of points along top and bottom edges
    xfac = np.linspace(0, 1, nx)
    topp = [p0 + (p1 - p0) * a for a in xfac]
    botp = [p3 + (p2 - p3) * a for a in xfac]

    # Get ny based on mean length of vectors connecting top and bottom points
    ylen_km = np.ones(nx)
    for i in range(nx):
        ylen_km[i] = (topp[i] - botp[i]).mag() / 1000
    ny = int(np.max([round(np.mean(ylen_km) / dx, 0) + 1, 2]))
    yfac = np.linspace(0, 1, ny)

    # Build mesh: dict of ny by nx arrays (x, y, z):
    mesh = {'x': np.zeros([ny, nx]), 'y': np.zeros(
        [ny, nx]), 'z': np.zeros([ny, nx])}
    for i in range(nx):
        mpts = [topp[i] + (botp[i] - topp[i]) * a for a in yfac]
        mesh['x'][:, i] = [a.x for a in mpts]
        mesh['y'][:, i] = [a.y for a in mpts]
        mesh['z'][:, i] = [a.z for a in mpts]

    # Make arrays of pixel corners
    mesh['llx'] = mesh['x'][1:, 0:-1]
    mesh['lrx'] = mesh['x'][1:, 1:]
    mesh['ulx'] = mesh['x'][0:-1, 0:-1]
    mesh['urx'] = mesh['x'][0:-1, 1:]
    mesh['lly'] = mesh['y'][1:, 0:-1]
    mesh['lry'] = mesh['y'][1:, 1:]
    mesh['uly'] = mesh['y'][0:-1, 0:-1]
    mesh['ury'] = mesh['y'][0:-1, 1:]
    mesh['llz'] = mesh['z'][1:, 0:-1]
    mesh['lrz'] = mesh['z'][1:, 1:]
    mesh['ulz'] = mesh['z'][0:-1, 0:-1]
    mesh['urz'] = mesh['z'][0:-1, 1:]
    mesh['cpx'] = np.zeros_like(mesh['llx'])
    mesh['cpy'] = np.zeros_like(mesh['llx'])
    mesh['cpz'] = np.zeros_like(mesh['llx'])

    # i and j are indices over subruptures
    ni, nj = mesh['llx'].shape
    for i in range(0, ni):
        for j in range(0, nj):
            # Rupture corner points
            pp0 = Vector(
                mesh['ulx'][i, j], mesh['uly'][i, j], mesh['ulz'][i, j])
            pp1 = Vector(
                mesh['urx'][i, j], mesh['ury'][i, j], mesh['urz'][i, j])
            pp2 = Vector(
                mesh['lrx'][i, j], mesh['lry'][i, j], mesh['lrz'][i, j])
            pp3 = Vector(
                mesh['llx'][i, j], mesh['lly'][i, j], mesh['llz'][i, j])
            # Find center of quad
            mp0 = pp0 + (pp1 - pp0) * 0.5
            mp1 = pp3 + (pp2 - pp3) * 0.5
            cp = mp0 + (mp1 - mp0) * 0.5
            mesh['cpx'][i, j] = cp.x
            mesh['cpy'][i, j] = cp.y
            mesh['cpz'][i, j] = cp.z
    return mesh