Пример #1
0
 def computeD(self, i):
     """
     :param 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.
     """
     hyp_ecef = self.phyp[i] # already in ECEF
     hyp_col = np.array([[hyp_ecef.x], [hyp_ecef.y], [hyp_ecef.z]])
     
     # First compute "updip" vector
     P0,P1,P2,P3 = self.flt.Quadrilaterals[i]
     p1 = Vector.fromPoint(P1) # convert to ECEF
     p2 = Vector.fromPoint(P2)
     e21 = p1 - p2
     e21norm = e21.norm()
     hp1 = p1 - hyp_ecef
     udip_len = Vector.dot(hp1, e21norm)/1000.0 # convert to km (used as max later)
     udip_col = np.array([[e21norm.x], [e21norm.y], [e21norm.z]]) # ECEF coords
     
     # Sites
     slat = self.sites[1]
     slon = self.sites[0]
     
     # 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.sites[0].shape)
     self.d = d_raw.clip(min = 1.0, max = udip_len)
Пример #2
0
    def computeWrup(self):
        """
        Wrup is the portion (in km) of the width of the fault which 
        ruptures up-dip from the hypocenter to the top of the fault.
          * This is ambiguous for faults with varible top of rupture (not 
            allowed in NGA). For now, lets just compute this for the 
            quad where the hypocenter is located.
          * Alternative is to compute max Wrup for the different quads. 
        """
        nquad = len(self._flt._quadrilaterals)

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

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

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

        pp0 = Vector.fromPoint(
            geo.point.Point(q[0].longitude, q[0].latitude, q[0].depth))
        hyp_ecef = Vector.fromPoint(
            geo.point.Point(self._hyp.longitude, self._hyp.latitude,
                            self._hyp.depth))
        hp0 = hyp_ecef - pp0
        ddv = fault.get_quad_down_dip_vector(q)
        self._Wrup = Vector.dot(ddv, hp0) / 1000
Пример #3
0
def test():
    print('Testing Vector class...')
    a = Vector(1,1,1)
    b = Vector(2,2,2)
    c = Vector(1,1,1)
    np.testing.assert_almost_equal(a.getArray(),np.array([1,1,1]))
    assert a == c
    alen = a.mag()
    np.testing.assert_almost_equal(alen,1.73205,decimal=5)
    anorm = a.norm()
    bnorm = b.norm()
    assert anorm == bnorm
    acrossb = a.cross(b)
    assert acrossb == Vector(0,0,0)
    adotb = a.dot(b)
    assert adotb == 6
    aplusb = a + b
    print('Passed Vector class tests.')
Пример #4
0
def test_rrup():
    print('Testing low level calc_rrup() method...')
    quads = []
    quad = (Point(-118.598000, 34.387000,
                  5.0000), Point(-118.431819, 34.301105, 5.0000),
            Point(-118.537983, 34.160984,
                  20.4270), Point(-118.703995, 34.246737, 20.4269))
    quads.append(quad)
    P0, P1, P2, P3 = quad
    # this is the coordinate of a station used in Northridge
    slat = np.array([34.0700])
    slon = np.array([-118.1500])
    sdep = np.array([0.0])
    p0 = Vector.fromPoint(Point(slon, slat, sdep))
    points = np.array([p0.x, p0.y, p0.z]).reshape(1, 3)
    rrup = calc_rupture_distance(P0, P1, P2, P3, points)
    output = 36.77
    np.testing.assert_almost_equal(rrup, output, decimal=0)
    print('Passed low level calc_rrup() method...')

    print('Testing high level get_distance() method with Rrup...')
    rrupd = get_distance('rrup', slat, slon, sdep, quadlist=quads)['rrup']
    np.testing.assert_almost_equal(rrupd, output, decimal=0)
    print('Passed high level get_distance() method with Rrup...')
Пример #5
0
def test_rrup():
    print('Testing low level calc_rrup() method...')
    quads = []
    quad = (Point(-118.598000, 34.387000, 5.0000),
            Point(-118.431819, 34.301105, 5.0000),
            Point(-118.537983, 34.160984, 20.4270),
            Point(-118.703995, 34.246737, 20.4269))
    quads.append(quad)
    P0, P1, P2, P3 = quad
    # this is the coordinate of a station used in Northridge
    slat = np.array([34.0700])
    slon = np.array([-118.1500])
    sdep = np.array([0.0])
    p0 = Vector.fromPoint(Point(slon, slat, sdep))
    points = np.array([p0.x, p0.y, p0.z]).reshape(1, 3)
    rrup = calc_rupture_distance(P0, P1, P2, P3, points)
    output = 36.77
    np.testing.assert_almost_equal(rrup, output, decimal = 0)
    print('Passed low level calc_rrup() method...')

    print('Testing high level get_distance() method with Rrup...')
    rrupd = get_distance('rrup', slat, slon, sdep, quadlist = quads)['rrup']
    np.testing.assert_almost_equal(rrupd,output,decimal = 0)
    print('Passed high level get_distance() method with Rrup...')
Пример #6
0
def test():
    print('Testing Vector class...')
    a = Vector(1, 1, 1)
    b = Vector(2, 2, 2)
    c = Vector(1, 1, 1)
    np.testing.assert_almost_equal(a.getArray(), np.array([1, 1, 1]))
    assert a == c
    alen = a.mag()
    np.testing.assert_almost_equal(alen, 1.73205, decimal=5)
    anorm = a.norm()
    bnorm = b.norm()
    assert anorm == bnorm
    acrossb = a.cross(b)
    assert acrossb == Vector(0, 0, 0)
    adotb = a.dot(b)
    assert adotb == 6
    aplusb = a + b
    print('Passed Vector class tests.')
Пример #7
0
    def computeXiPrime(self):
        """
        Computes the xi' value. 
        """
        hypo_ecef = Vector.fromPoint(
            geo.point.Point(self._hyp.longitude, self._hyp.latitude,
                            self._hyp.depth))
        epi_ll = Vector(self._hyp.longitude, self._hyp.latitude, 0)
        epi_ecef = Vector.fromPoint(geo.point.Point(epi_ll.x, epi_ll.y, 0))

        slat = self._lat
        slon = self._lon

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

        # Make a 3x(#number of sites) matrix of site locations
        # (rows are x, y, z) in ECEF
        site_ecef_x, site_ecef_y, site_ecef_z = 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, ))
        ])

        xi_prime_unscaled = np.zeros_like(slat)

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

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

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

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

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

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

            # Down dip vector (ECEF coords)
            ddip_vec = fault.get_quad_down_dip_vector(q)
            ddip_vec_col = np.array([[ddip_vec.x], [ddip_vec.y],
                                     [ddip_vec.z]])  # convert to column vector

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self._xi_prime = xi_prime
Пример #8
0
 def computeThetaAndS(self, i):
     """
     :param i:
         Compute d for the i-th quad/segment. 
     """
     # self.phyp is in ECEF
     tmp = ecef.ecef2latlon(self.phyp[i].x, self.phyp[i].y, self.phyp[i].z)
     epi_ecef = Vector.fromPoint(geo.point.Point(tmp[1], tmp[0], 0.0))
     epi_col = np.array([[epi_ecef.x], [epi_ecef.y], [epi_ecef.z]])
     
     # First compute along strike vector
     P0,P1,P2,P3 = self.flt.Quadrilaterals[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.sites[1]
     slon = self.sites[0]
     
     # 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.sites[0].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.sites[0].shape)
Пример #9
0
 def setPseudoHypocenters(self):
     """
     Adapted from ShakeMap 3.5 src/contour/directivity.c 
     From Bayless and Somerville:
         "Define the pseudo-hypocenter for rupture of successive segments as 
          the point on the side edge of the fault segment that is closest to 
          the side edge of the previous segment, and that lies half way 
          between the top and bottom of the fault. We assume that the fault is
          segmented along strike, not updip. All geometric parameters are 
          computed relative to the pseudo-hypocenter."
     """
     hyp_ecef = Vector.fromPoint(geo.point.Point(
         self.hyp[0], self.hyp[1], self.hyp[2]))
     # Loop over each quad
     self.phyp = [None]*self.nq
     for i in range(self.nq):
         P0,P1,P2,P3 = self.flt.Quadrilaterals[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])
             s0 = dist2_to_segment(p0p, p1p)
             s1 = dist2_to_segment(p1p, p2p)
             s2 = dist2_to_segment(p2p, p3p)
             s3 = dist2_to_segment(p3p, p0p)
             
             # Assuming that the fault is segmented along strike and not
             # updip (as described by Bayless and somerville), we only
             # need to consider s1 and s3:
             if s1 > s3:
                 e30 = p0 - p3
                 e30norm = e30.norm()
                 mag = e30.mag()
                 self.phyp[i] = p3 + e30norm*(0.5*mag)
             else:
                 e21 = p1 - p2
                 e21norm = e21.norm()
                 mag = e21.mag()
                 self.phyp[i] = p2 + e21norm*(0.5*mag)