コード例 #1
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.')
コード例 #2
0
ファイル: distance.py プロジェクト: kallstadt-usgs/shakemap
def __calc_t_i(P0, P1, lat, lon, proj):
    """
    Calculate t_i distance. See Spudich and Chiou OFR 2015-1028. This is the distance
    measured normal to strike from the i-th segment. Values on the hanging-wall are
    positive and those on the foot-wall are negative.

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

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

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

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

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

    # Dot product gives t_i
    t_i = np.sum(t_i_hat.getArray()[0:2] * r, axis=1)
    shp = t_i.shape
    if len(shp) == 1:
        t_i.shape = (shp[0], 1)
    t_i = np.fliplr(t_i)
    return t_i
コード例 #3
0
ファイル: distance.py プロジェクト: kallstadt-usgs/shakemap
def __calc_u_i(P0, P1, lat, lon, proj):
    """
    Calculate u_i distance. See Spudich and Chiou OFR 2015-1028. This is the distance
    along strike from the first vertex (P0) of the i-th segment.

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

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

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

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

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

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

    return u_i
コード例 #4
0
    def __computeXiPrime(self):
        """
        Computes the xi' value.
        """
        hypo_ecef = Vector.fromPoint(geo.point.Point(
            self._hyp.longitude, self._hyp.latitude, self._hyp.depth))
        epi_ll = Vector(self._hyp.longitude, self._hyp.latitude, 0)
        epi_ecef = Vector.fromPoint(geo.point.Point(
            epi_ll.x, epi_ll.y, 0))

        slat = self._lat
        slon = self._lon

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

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

        xi_prime_unscaled = np.zeros_like(slat)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self._xi_prime = xi_prime
コード例 #5
0
ファイル: distance.py プロジェクト: kallstadt-usgs/shakemap
def get_distance(methods, lat, lon, dep, source, use_median_distance=True):
    """
    Calculate distance using any one of a number of distance measures.
    One of quadlist OR hypo must be specified. The following table gives
    the allowed distance strings and a description of each. 

    +--------+----------------------------------------------------------+
    | String | Description                                              |
    +========+==========================================================+
    | repi   | Distance to epicenter.                                   |
    +--------+----------------------------------------------------------+
    | rhypo  | Distance to hypocenter.                                  |
    +--------+----------------------------------------------------------+
    | rjb    | Joyner-Boore distance; this is closest distance to the   |
    |        | surface projection of the rupture plane.                 |
    +--------+----------------------------------------------------------+
    | rrup   | Rupture distance; closest distance to the rupture plane. |
    +--------+----------------------------------------------------------+
    | rx     | Strike-normal distance; same as GC2 coordiante T.        |
    +--------+----------------------------------------------------------+
    | ry     | Strike-parallel distance; same as GC2 coordiante U, but  |
    |        | with a shift in origin definition. See Spudich and Chiou |
    |        | (2015) http://dx.doi.org/10.3133/ofr20151028.            |
    +--------+----------------------------------------------------------+
    | ry0    | Horizontal distance off the end of the rupture measured  |
    |        | parallel to strike. Can only be zero or positive. We     |
    |        | compute this as a function of GC2 coordinate U.          |
    +--------+----------------------------------------------------------+
    | U      | GC2 coordinate U.                                        |
    +--------+----------------------------------------------------------+
    | T      | GC2 coordinate T.                                        |
    +--------+----------------------------------------------------------+

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

    # Dictionary for holding the distances
    distdict = dict()

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

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

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

    oldshape = lon.shape

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return distdict