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