def computeRjb(self, lon, lat, depth, var=False): """ Method for computing Joyner-Boore distance. Args: lon (array): Numpy array of longitudes. lat (array): Numpy array of latitudes. depth (array): Numpy array of depths (km; positive down). var (bool): Also return variance of prediction. Unused, and will raise an exception if not False. Returns: array: Joyner-Boore distance (km). """ if var is True: raise ValueError('var must be False for EdgeRupture') mesh_dx = self._mesh_dx # --------------------------------------------------------------------- # Sort out sites # --------------------------------------------------------------------- oldshape = lon.shape if len(oldshape) == 2: newshape = (oldshape[0] * oldshape[1], 1) else: newshape = (oldshape[0], 1) x, y, z = latlon2ecef(lat, lon, depth) x.shape = newshape y.shape = newshape z.shape = newshape sites_ecef = np.hstack((x, y, z)) # --------------------------------------------------------------------- # Get mesh # --------------------------------------------------------------------- mx = [] my = [] mz = [] u_groups = np.unique(self._group_index) n_groups = len(u_groups) for j in range(n_groups): g_ind = np.where(u_groups[j] == self._group_index)[0] nq = len(self._toplats[g_ind]) - 1 for i in range(nq): q = [ Point(self._toplons[g_ind[i]], self._toplats[g_ind[i]], 0), Point(self._toplons[g_ind[i + 1]], self._toplats[g_ind[i + 1]], 0), Point(self._botlons[g_ind[i + 1]], self._botlats[g_ind[i + 1]], 0), Point(self._botlons[g_ind[i]], self._botlats[g_ind[i]], 0) ] mesh = utils.get_quad_mesh(q, dx=mesh_dx) mx.extend(list(np.reshape(mesh['x'], (-1, )))) my.extend(list(np.reshape(mesh['y'], (-1, )))) mz.extend(list(np.reshape(mesh['z'], (-1, )))) mesh_mat = np.array([np.array(mx), np.array(my), np.array(mz)]) # --------------------------------------------------------------------- # Compute distance # --------------------------------------------------------------------- dist = np.zeros_like(x) for i in range(len(x)): sitecol = sites_ecef[i, :].reshape([3, 1]) dif = sitecol - mesh_mat distarray = np.sqrt(np.sum(dif * dif, axis=0)) dist[i] = np.min(distarray) / 1000.0 # convert to km dist = np.reshape(dist, oldshape) return dist
def __computeXiPrime(self): """ Computes the xi' value. """ hypo_ecef = Vector.fromPoint( geo.point.Point(self._hyp.longitude, self._hyp.latitude, self._hyp.depth)) slat = self._lat slon = self._lon # Convert site to ECEF: site_ecef_x = np.ones_like(slat) site_ecef_y = np.ones_like(slat) site_ecef_z = np.ones_like(slat) # Make a 3x(#number of sites) matrix of site locations # (rows are x, y, z) in ECEF site_ecef_x, site_ecef_y, site_ecef_z = latlon2ecef( slat, slon, np.zeros(slon.shape)) site_mat = np.array([ np.reshape(site_ecef_x, (-1, )), np.reshape(site_ecef_y, (-1, )), np.reshape(site_ecef_z, (-1, )) ]) xi_prime_unscaled = np.zeros_like(slat) # Normalize by total number of subruptures. For mtype == 1, the number # of subruptures will vary with site and be different for xi_s and # xi_p, so keep two variables and sum them for each quad. nsubs = np.zeros(np.product(slat.shape)) nsubp = np.zeros(np.product(slat.shape)) xi_prime_s = np.zeros(np.product(slat.shape)) xi_prime_p = np.zeros(np.product(slat.shape)) for k in range(len(self._rup.getQuadrilaterals())): # Select a quad q = self._rup.getQuadrilaterals()[k] # Quad mesh (ECEF coords) mesh = utils.get_quad_mesh(q, self._dx) # Rupture plane normal vector (ECEF coords) rpnv = utils.get_quad_normal(q) rpnvcol = np.array([[rpnv.x], [rpnv.y], [rpnv.z]]) cp_mat = np.array([ np.reshape(mesh['cpx'], (-1, )), np.reshape(mesh['cpy'], (-1, )), np.reshape(mesh['cpz'], (-1, )) ]) # Compute matrix of p vectors hypcol = np.array([[hypo_ecef.x], [hypo_ecef.y], [hypo_ecef.z]]) pmat = cp_mat - hypcol # Project pmat onto quad ndotp = np.sum(pmat * rpnvcol, axis=0) pmat = pmat - ndotp * rpnvcol mag = np.sqrt(np.sum(pmat * pmat, axis=0)) pmatnorm = pmat / mag # like r1 # According to Rowshandel: # "The choice of the +/- sign in the above equations # depends on the (along-the-strike and across-the-dip) # location of the rupturing sub-fault relative to the # location of the hypocenter." # and: # "for the along the strike component of the slip unit # vector, the choice of the sign should result in the # slip unit vector (s) being exactly the same as the # rupture unit vector (p) for a pure strike-slip case" # Strike slip and dip slip components of unit slip vector # (ECEF coords) ds_mat, ss_mat = _get_quad_slip_ds_ss(q, self._rake, cp_mat, pmatnorm) slpmat = (ds_mat + ss_mat) mag = np.sqrt(np.sum(slpmat * slpmat, axis=0)) slpmatnorm = slpmat / mag # Loop over sites for i in range(site_mat.shape[1]): sitecol = np.array([[site_mat[0, i]], [site_mat[1, i]], [site_mat[2, i]]]) qmat = sitecol - cp_mat # 3x(ni*nj), like r2 mag = np.sqrt(np.sum(qmat * qmat, axis=0)) qmatnorm = qmat / mag # Propagation dot product pdotqraw = np.sum(pmatnorm * qmatnorm, axis=0) # Slip vector dot product sdotqraw = np.sum(slpmatnorm * qmatnorm, axis=0) if self._mtype == 1: # Only sum over (+) directivity effect subruptures # xi_p_prime pdotq = pdotqraw.clip(min=0) nsubp[i] = nsubp[i] + np.sum(pdotq > 0) # xi_s_prime sdotq = sdotqraw.clip(min=0) nsubs[i] = nsubs[i] + np.sum(sdotq > 0) elif self._mtype == 2: # Sum over contributing subruptures # xi_p_prime pdotq = pdotqraw nsubp[i] = nsubp[i] + cp_mat.shape[1] # xi_s_prime sdotq = sdotqraw nsubs[i] = nsubs[i] + cp_mat.shape[1] # Normalize by n sub ruptures later xi_prime_s[i] = xi_prime_s[i] + np.sum(sdotq) xi_prime_p[i] = xi_prime_p[i] + np.sum(pdotq) # Apply a water level to nsubp and nsubs to avoid division by # zero. This should only occur when the numerator is also zero # and so the resulting value should be zero. nsubs = np.maximum(nsubs, 1) nsubp = np.maximum(nsubp, 1) # We are outside the 'k' loop over nquads. # o Normalize xi_prime_s and xi_prime_p # o Reshape them # o Add them together with the 'a' weights xi_prime_tmp = (self._a_weight) * (xi_prime_s / nsubs) + \ (1 - self._a_weight) * (xi_prime_p / nsubp) xi_prime_unscaled = xi_prime_unscaled + \ np.reshape(xi_prime_tmp, slat.shape) # Scale so that xi_prime has range (0, 1) if self._mtype == 1: xi_prime = xi_prime_unscaled elif self._mtype == 2: xi_prime = 0.5 * (xi_prime_unscaled + 1) self._xi_prime = xi_prime
def computeRrup(self, lon, lat, depth): """ Method for computing rupture distance. Args: lon (array): Numpy array of longitudes. lat (array): Numpy array of latitudes. depth (array): Numpy array of depths (km; positive down). Returns: tuple: A tuple of an array of rupture distance (km), and None. """ mesh_dx = self._mesh_dx # --------------------------------------------------------------------- # Sort out sites # --------------------------------------------------------------------- oldshape = lon.shape if len(oldshape) == 2: newshape = (oldshape[0] * oldshape[1], 1) else: newshape = (oldshape[0], 1) x, y, z = latlon2ecef(lat, lon, depth) x.shape = newshape y.shape = newshape z.shape = newshape sites_ecef = np.hstack((x, y, z)) # --------------------------------------------------------------------- # Get mesh # --------------------------------------------------------------------- mx = [] my = [] mz = [] u_groups = np.unique(self._group_index) n_groups = len(u_groups) for j in range(n_groups): g_ind = np.where(u_groups[j] == self._group_index)[0] nq = len(self._toplats[g_ind]) - 1 for i in range(nq): q = [ Point(self._toplons[g_ind[i]], self._toplats[g_ind[i]], self._topdeps[g_ind[i]]), Point(self._toplons[g_ind[i + 1]], self._toplats[g_ind[i + 1]], self._topdeps[g_ind[i + 1]]), Point(self._botlons[g_ind[i + 1]], self._botlats[g_ind[i + 1]], self._botdeps[g_ind[i + 1]]), Point(self._botlons[g_ind[i]], self._botlats[g_ind[i]], self._botdeps[g_ind[i]]) ] mesh = utils.get_quad_mesh(q, dx=mesh_dx) mx.extend(list(np.reshape(mesh['x'], (-1, )))) my.extend(list(np.reshape(mesh['y'], (-1, )))) mz.extend(list(np.reshape(mesh['z'], (-1, )))) mesh_mat = np.array([np.array(mx), np.array(my), np.array(mz)]) # --------------------------------------------------------------------- # Compute distance # --------------------------------------------------------------------- dist = np.zeros_like(x) for i in range(len(x)): sitecol = sites_ecef[i, :].reshape([3, 1]) dif = sitecol - mesh_mat distarray = np.sqrt(np.sum(dif * dif, axis=0)) dist[i] = np.min(distarray) / 1000.0 # convert to km dist = np.reshape(dist, oldshape) return dist, None
def computeRjb(self, lon, lat, depth): """ Method for computing Joyner-Boore distance. Args: lon (array): Numpy array of longitudes. lat (array): Numpy array of latitudes. depth (array): Numpy array of depths (km; positive down). Returns: array: Joyner-Boore distance (km). """ mesh_dx = self._mesh_dx # --------------------------------------------------------------------- # Sort out sites # --------------------------------------------------------------------- oldshape = lon.shape if len(oldshape) == 2: newshape = (oldshape[0] * oldshape[1], 1) else: newshape = (oldshape[0], 1) x, y, z = latlon2ecef(lat, lon, depth) x.shape = newshape y.shape = newshape z.shape = newshape sites_ecef = np.hstack((x, y, z)) # --------------------------------------------------------------------- # Get mesh # --------------------------------------------------------------------- mx = [] my = [] mz = [] u_groups = np.unique(self._group_index) n_groups = len(u_groups) for j in range(n_groups): g_ind = np.where(u_groups[j] == self._group_index)[0] nq = len(self._toplats[g_ind]) - 1 for i in range(nq): q = [Point(self._toplons[g_ind[i]], self._toplats[g_ind[i]], 0), Point(self._toplons[g_ind[i + 1]], self._toplats[g_ind[i + 1]], 0), Point(self._botlons[g_ind[i + 1]], self._botlats[g_ind[i + 1]], 0), Point(self._botlons[g_ind[i]], self._botlats[g_ind[i]], 0) ] mesh = utils.get_quad_mesh(q, dx=mesh_dx) mx.extend(list(np.reshape(mesh['x'], (-1,)))) my.extend(list(np.reshape(mesh['y'], (-1,)))) mz.extend(list(np.reshape(mesh['z'], (-1,)))) mesh_mat = np.array([np.array(mx), np.array(my), np.array(mz)]) # --------------------------------------------------------------------- # Compute distance # --------------------------------------------------------------------- dist = np.zeros_like(x) for i in range(len(x)): sitecol = sites_ecef[i, :].reshape([3, 1]) dif = sitecol - mesh_mat distarray = np.sqrt(np.sum(dif * dif, axis=0)) dist[i] = np.min(distarray) / 1000.0 # convert to km dist = np.reshape(dist, oldshape) return dist
def __computeXiPrime(self): """ Computes the xi' value. """ hypo_ecef = Vector.fromPoint(geo.point.Point( self._hyp.longitude, self._hyp.latitude, self._hyp.depth)) slat = self._lat slon = self._lon # Convert site to ECEF: site_ecef_x = np.ones_like(slat) site_ecef_y = np.ones_like(slat) site_ecef_z = np.ones_like(slat) # Make a 3x(#number of sites) matrix of site locations # (rows are x, y, z) in ECEF site_ecef_x, site_ecef_y, site_ecef_z = latlon2ecef( slat, slon, np.zeros(slon.shape)) site_mat = np.array([np.reshape(site_ecef_x, (-1,)), np.reshape(site_ecef_y, (-1,)), np.reshape(site_ecef_z, (-1,))]) xi_prime_unscaled = np.zeros_like(slat) # Normalize by total number of subruptures. For mtype == 1, the number # of subruptures will vary with site and be different for xi_s and # xi_p, so keep two variables and sum them for each quad. nsubs = np.zeros(np.product(slat.shape)) nsubp = np.zeros(np.product(slat.shape)) xi_prime_s = np.zeros(np.product(slat.shape)) xi_prime_p = np.zeros(np.product(slat.shape)) for k in range(len(self._rup.getQuadrilaterals())): # Select a quad q = self._rup.getQuadrilaterals()[k] # Quad mesh (ECEF coords) mesh = utils.get_quad_mesh(q, self._dx) # Rupture plane normal vector (ECEF coords) rpnv = utils.get_quad_normal(q) rpnvcol = np.array([[rpnv.x], [rpnv.y], [rpnv.z]]) cp_mat = np.array([np.reshape(mesh['cpx'], (-1,)), np.reshape(mesh['cpy'], (-1,)), np.reshape(mesh['cpz'], (-1,))]) # Compute matrix of p vectors hypcol = np.array([[hypo_ecef.x], [hypo_ecef.y], [hypo_ecef.z]]) pmat = cp_mat - hypcol # Project pmat onto quad ndotp = np.sum(pmat * rpnvcol, axis=0) pmat = pmat - ndotp * rpnvcol mag = np.sqrt(np.sum(pmat * pmat, axis=0)) pmatnorm = pmat / mag # like r1 # According to Rowshandel: # "The choice of the +/- sign in the above equations # depends on the (along-the-strike and across-the-dip) # location of the rupturing sub-fault relative to the # location of the hypocenter." # and: # "for the along the strike component of the slip unit # vector, the choice of the sign should result in the # slip unit vector (s) being exactly the same as the # rupture unit vector (p) for a pure strike-slip case" # Strike slip and dip slip components of unit slip vector # (ECEF coords) ds_mat, ss_mat = _get_quad_slip_ds_ss( q, self._rake, cp_mat, pmatnorm) slpmat = (ds_mat + ss_mat) mag = np.sqrt(np.sum(slpmat * slpmat, axis=0)) slpmatnorm = slpmat / mag # Loop over sites for i in range(site_mat.shape[1]): sitecol = np.array([[site_mat[0, i]], [site_mat[1, i]], [site_mat[2, i]]]) qmat = sitecol - cp_mat # 3x(ni*nj), like r2 mag = np.sqrt(np.sum(qmat * qmat, axis=0)) qmatnorm = qmat / mag # Propagation dot product pdotqraw = np.sum(pmatnorm * qmatnorm, axis=0) # Slip vector dot product sdotqraw = np.sum(slpmatnorm * qmatnorm, axis=0) if self._mtype == 1: # Only sum over (+) directivity effect subruptures # xi_p_prime pdotq = pdotqraw.clip(min=0) nsubp[i] = nsubp[i] + np.sum(pdotq > 0) # xi_s_prime sdotq = sdotqraw.clip(min=0) nsubs[i] = nsubs[i] + np.sum(sdotq > 0) elif self._mtype == 2: # Sum over contributing subruptures # xi_p_prime pdotq = pdotqraw nsubp[i] = nsubp[i] + cp_mat.shape[1] # xi_s_prime sdotq = sdotqraw nsubs[i] = nsubs[i] + cp_mat.shape[1] # Normalize by n sub ruptures later xi_prime_s[i] = xi_prime_s[i] + np.sum(sdotq) xi_prime_p[i] = xi_prime_p[i] + np.sum(pdotq) # Apply a water level to nsubp and nsubs to avoid division by # zero. This should only occur when the numerator is also zero # and so the resulting value should be zero. nsubs = np.maximum(nsubs, 1) nsubp = np.maximum(nsubp, 1) # We are outside the 'k' loop over nquads. # o Normalize xi_prime_s and xi_prime_p # o Reshape them # o Add them together with the 'a' weights xi_prime_tmp = (self._a_weight) * (xi_prime_s / nsubs) + \ (1 - self._a_weight) * (xi_prime_p / nsubp) xi_prime_unscaled = xi_prime_unscaled + \ np.reshape(xi_prime_tmp, slat.shape) # Scale so that xi_prime has range (0, 1) if self._mtype == 1: xi_prime = xi_prime_unscaled elif self._mtype == 2: xi_prime = 0.5 * (xi_prime_unscaled + 1) self._xi_prime = xi_prime