Example #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)
Example #2
0
def test():
    print('Testing ECEF conversion code...')
    lat = 32.1
    lon = 118.5
    dep = 10.0

    x,y,z = latlon2ecef(lat,lon,dep)

    TESTX = -2576515.419
    TESTY = 4745351.087
    TESTZ = 3364516.124
    
    np.testing.assert_almost_equal(x,TESTX,decimal=2)
    np.testing.assert_almost_equal(y,TESTY,decimal=2)
    np.testing.assert_almost_equal(z,TESTZ,decimal=2)

    lat2,lon2,dep2 = ecef2latlon(x,y,z)
    np.testing.assert_almost_equal(lat2,lat,decimal=2)
    np.testing.assert_almost_equal(lon2,lon,decimal=2)
    np.testing.assert_almost_equal(dep2,dep,decimal=2)
    print('Passed tests of ECEF conversion code.')
Example #3
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
Example #4
0
def _get_quad_slip_ds_ss(q, rake, cp, p):
    """
    Compute the DIP SLIP and STRIKE SLIP components of the unit slip vector in 
    ECEF coords for a quad and rake angle. 
    :param q:
        A quadrilateral. 
    :param rake:
        Direction of motion of the hanging wall relative to the
        foot wall, as measured by the angle (deg) from the strike vector.
    :param cp:
        A 3x(n sub fault) array giving center points of each sub fault
        in ECEF coords. 
    :param p:
        A 3x(n sub fault) array giving the unit vectors of the propagation
        vector on each sub fault in ECEF coords. 
    :returns:
        List of dip slip and strike slip components (each is a matrix)
        of the unit slip vector in ECEF space. 
    """
    # Get quad vertices, strike, dip
    P0, P1, P2, P3 = q
    strike = P0.azimuth(P1)
    dip = fault.get_quad_dip(q)

    # Slip unit vectors in 'local' (i.e., z-up, x-east) coordinates
    d1_local = fault.get_local_unit_slip_vector_DS(strike, dip, rake)
    s1_local = fault.get_local_unit_slip_vector_SS(strike, dip, rake)

    # Convert to a column array
    d1_col = np.array([[d1_local.x], [d1_local.y], [d1_local.z]])
    s1_col = np.array([[s1_local.x], [s1_local.y], [s1_local.z]])

    # Define 'local' coordinate system
    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))

    # Convert p and cp to geographic coords
    p0lat, p0lon, p0z = ecef2latlon(cp[0, ], cp[1, ], cp[2, ])
    p1lat, p1lon, p1z = ecef2latlon(cp[0, ] + p[0, ], cp[1, ] + p[1, ],
                                    cp[2, ] + p[2, ])

    # Convert p to 'local' coords
    p0x, p0y = proj(p0lon, p0lat)
    p1x, p1y = proj(p1lon, p1lat)
    px = p1x - p0x
    py = p1y - p0y
    pz = p1z - p0z

    # Apply sign changes in 'local' coords
    s1mat = np.array([[np.abs(s1_col[0]) * np.sign(px)],
                      [np.abs(s1_col[1]) * np.sign(py)],
                      [np.abs(s1_col[2]) * np.sign(pz)]])
    #                      [np.abs(s1_col[2])*np.ones_like(pz)]])

    dipsign = -np.sign(np.sin(np.radians(rake)))
    d1mat = np.array([[d1_col[0] * np.ones_like(px) * dipsign],
                      [d1_col[1] * np.ones_like(py) * dipsign],
                      [d1_col[2] * np.ones_like(pz) * dipsign]])

    # Need to track 'origin'
    s0 = np.array([[0], [0], [0]])

    # Convert from 'local' to geographic coords
    s1_ll = proj(s1mat[0, ], s1mat[1, ], reverse=True)
    d1_ll = proj(d1mat[0, ], d1mat[1, ], reverse=True)
    s0_ll = proj(s0[0], s0[1], reverse=True)

    # And then back to ECEF:
    s1_ecef = latlon2ecef(s1_ll[1], s1_ll[0], s1mat[2, ])
    d1_ecef = latlon2ecef(d1_ll[1], d1_ll[0], d1mat[2, ])
    s0_ecef = latlon2ecef(s0_ll[1], s0_ll[0], s0[2])
    s00 = s0_ecef[0].reshape(-1)
    s01 = s0_ecef[1].reshape(-1)
    s02 = s0_ecef[2].reshape(-1)
    d_mat = np.array([
        d1_ecef[0].reshape(-1) - s00, d1_ecef[1].reshape(-1) - s01,
        d1_ecef[2].reshape(-1) - s02
    ])
    s_mat = np.array([
        s1_ecef[0].reshape(-1) - s00, s1_ecef[1].reshape(-1) - s01,
        s1_ecef[2].reshape(-1) - s02
    ])
    return d_mat, s_mat
Example #5
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
Example #6
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)