def okada_at_origin(strike, dip, rake, depth, L, W, alpha, strike_slip, dip_slip, x, y): # Mechanical part. # Assumes top back corner of fault plane is located at 0,0 # Given strike, dip, rake, depth, length, width, alpha, strike_slip, and dip_slip... # Given vectors of positions x and y... # Returns vectors of displacements u, v, w. theta=strike-90 theta=np.deg2rad(theta) R=np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]]) R2=np.array([[np.cos(-theta),-np.sin(-theta)],[np.sin(-theta),np.cos(-theta)]]) ux=np.zeros(np.shape(x)); uy=np.zeros(np.shape(x)); uz=np.zeros(np.shape(x)); for k in range(len(x)): #Calculate on rotated position xy=R.dot(np.array([[x[k]], [y[k]]])); success, u, grad_u = dc3dwrapper(alpha, [xy[0], xy[1], 0.0], depth, dip, [0, L], [0, W], [strike_slip, dip_slip, 0.0]) urot=R2.dot(np.array([[u[0]], [u[1]]])) ux[k]=urot[0] uy[k]=urot[1] uz[k]=u[2] # vertical doesn't rotate return ux, uy, uz;
def okada_pt(pt): disp = np.zeros(3) trac = np.zeros(3) D = 100000 pt_copy = pt.copy() pt_copy[2] += -D for j in range(OKADAN): X1 = X_vals[j] X2 = X_vals[j + 1] midX = (X1 + X2) / 2.0 for k in range(OKADAN): Z1 = Z_vals[k] Z2 = Z_vals[k + 1] midZ = (Z1 + Z2) / 2.0 slip = -gauss_slip_fnc(np.array([midX]), np.array([midZ]))[0] [suc, uv, grad_uv] = okada_wrapper.dc3dwrapper(alpha, pt_copy, D, 90.0, [X1, X2], [Z1, Z2], [slip, 0.0, 0.0]) disp += uv strain = (grad_uv + grad_uv.T) / 2.0 strain_trace = sum([strain[d, d] for d in [0, 1, 2]]) kronecker = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) stress = lam * strain_trace * kronecker + 2 * sm * strain trac += stress.dot(obs_ns[0]) assert (suc == 0) if which == 'T': return disp elif which == 'A': assert (False) elif which == 'H': return trac
def compute_disp(self): for plain in self.plains: sub_stk_dim = plain.strike_length / plain.strike_sub sub_dip_dim = plain.dip_length / plain.dip_sub rstrike = np.deg2rad(plain.STK) plain_disp = np.zeros((self.size, self.size, 3)) sub_plains = np.array(plain.sub_plains) sub_plains = sub_plains.reshape(plain.strike_sub, plain.dip_sub, 3) for k in range(plain.strike_sub): for l in range(plain.dip_sub): for i in range(self.size): for j in range(self.size): ec = j - int(plain.XO / self.pixel) nc = i - int(plain.YO / self.pixel) x = np.cos(rstrike) * nc + np.sin(rstrike) * ec y = np.sin(rstrike) * nc - np.cos(rstrike) * ec plain_disp[self.img.shape[0] - 1 - i, j] += \ dc3dwrapper(self.alpha, [x * self.pixel, y * self.pixel, 0], np.abs(plain.ZO), plain.DIP , [k * sub_stk_dim, (k + 1) * sub_stk_dim], [l * sub_dip_dim, (l + 1) * sub_dip_dim], sub_plains[k, l, :])[1] self.img[:, :, 0] += np.sin(rstrike) * plain_disp[:, :, 0] - np.cos( rstrike) * plain_disp[:, :, 1] self.img[:, :, 1] += np.cos(rstrike) * plain_disp[:, :, 0] + np.sin( rstrike) * plain_disp[:, :, 1] self.img[:, :, 2] += plain_disp[:, :, 2]
def test_dc3d(): source_depth, obs_depth, poisson_ratio, mu, dip, alpha = get_params() n = (100, 100) x = linspace(-1, 1, n[0]) y = linspace(-1, 1, n[1]) ux = zeros((n[0], n[1])) for i in range(n[0]): for j in range(n[1]): success, u, grad_u = dc3dwrapper(alpha, [x[i], y[j], -obs_depth], source_depth, dip, [-0.6, 0.6], [-0.6, 0.6], [1.0, 0.0, 0.0]) assert (success == 0) ux[i, j] = u[0] levels = linspace(-0.5, 0.5, 21) cntrf = contourf(x, y, ux.T, levels=levels) contour(x, y, ux.T, colors='k', levels=levels, linestyles='solid') xlabel('x') ylabel('y') cbar = colorbar(cntrf) tick_locator = matplotlib.ticker.MaxNLocator(nbins=5) cbar.locator = tick_locator cbar.update_ticks() cbar.set_label('$u_{\\textrm{x}}$') savefig("strike_slip.png") show()
def test_dc3d(): source_depth, obs_depth, poisson_ratio, mu, dip, alpha = get_params() n = (100, 100) x = linspace(-1, 1, n[0]) y = linspace(-1, 1, n[1]) ux = zeros((n[0], n[1])) for i in range(n[0]): for j in range(n[1]): success, u, grad_u = dc3dwrapper(alpha, [x[i], y[j], -obs_depth], source_depth, dip, [-0.6, 0.6], [-0.6, 0.6], [1.0, 0.0, 0.0]) assert(success == 0) ux[i, j] = u[0] levels = linspace(-0.5, 0.5, 21) cntrf = contourf(x, y, ux.T, levels = levels) contour(x, y, ux.T, colors = 'k', levels = levels, linestyles = 'solid') xlabel('x') ylabel('y') cbar = colorbar(cntrf) tick_locator = matplotlib.ticker.MaxNLocator(nbins=5) cbar.locator = tick_locator cbar.update_ticks() cbar.set_label('$u_{\\textrm{x}}$') savefig("strike_slip.png") show()
def calc_deformation(alpha, strike, depth, dip, strike_width, dip_width, dislocation, x, y): theta = strike - 90 theta = np.deg2rad(theta) R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) R2 = np.array([[np.cos(-theta), -np.sin(-theta)], [np.sin(-theta), np.cos(theta)]]) xrot, yrot = R.dot([x, y]) success, u, grad_u = dc3dwrapper(alpha, [xrot, yrot, 0.0], depth, dip, strike_width, dip_width, dislocation) urot = R2.dot(np.array([[u[0]], [u[1]]])) u[0] = urot[0] u[1] = urot[1] ux = u[0] uy = u[1] uz = u[2] #returns ux, uy, ux, at every location from xmin, xmax, ymin, ymax return ux, uy, uz
def test_success(): # should fail because z is positive success, u, grad_u = dc3d0wrapper(1.0, [0.0, 0.0, 1.0], 1.0, 90, [1.0, 0.0, 0.0, 0.0]) assert (success == 2) success, u, grad_u = dc3dwrapper(1.0, [0.0, 0.0, 1.0], 0.0, 90, [-0.7, 0.7], [-0.7, 0.7], [1.0, 0.0, 0.0]) assert (success == 2)
def benchmark(): n = 1000000 start = time.time() for i in range(n): success, u, grad_u = dc3dwrapper(1.0, [0.0, 0.0, 1.0], 0.0, 90, [-0.7, 0.7], [-0.7, 0.7], [1.0, 0.0, 0.0]) end = time.time() T = end - start print(str(n) + " DC3D evaluations took: " + str(T) + " seconds") print("Giving a time of " + str(T / n) + " seconds per evaluation.")
def test_success(): # should fail because z is positive success, u, grad_u = dc3d0wrapper(1.0, [0.0, 0.0, 1.0], 1.0, 90, [1.0, 0.0, 0.0, 0.0]); assert(success == 2) success, u, grad_u = dc3dwrapper(1.0, [0.0, 0.0, 1.0], 0.0, 90, [-0.7, 0.7], [-0.7, 0.7], [1.0, 0.0, 0.0]) assert(success == 2)
def benchmark(): n = 1000000 start = time.time() for i in range(n): success, u, grad_u = dc3dwrapper(1.0, [0.0, 0.0, 1.0], 0.0, 90, [-0.7, 0.7], [-0.7, 0.7], [1.0, 0.0, 0.0]) end = time.time() T = end - start print(str(n) + " DC3D evaluations took: " + str(T) + " seconds") print("Giving a time of " + str(T / n) + " seconds per evaluation.")
def compute_surface_disp_point(params, inputs, x, y): # A major compute loop for each source object at an x/y point. # x/y in the same coordinate system as the fault object. u_disp = 0 v_disp = 0 w_disp = 0 for fault in inputs.source_object: # Fault parameters L = conversion_math.get_strike_length(fault.xstart, fault.xfinish, fault.ystart, fault.yfinish) W = conversion_math.get_downdip_width(fault.top, fault.bottom, fault.dipangle) depth = fault.top dip = fault.dipangle strike_slip = fault.rtlat * -1 # The dc3d coordinate system has left-lateral positive. dip_slip = fault.reverse # Preparing to rotate to a fault-oriented coordinate system. theta = fault.strike - 90 theta = np.deg2rad(theta) R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) R2 = np.array([[np.cos(-theta), -np.sin(-theta)], [np.sin(-theta), np.cos(-theta)]]) # Compute the position relative to the translated, rotated fault. translated_pos = np.array([[x - fault.xstart], [y - fault.ystart]]) xy = R.dot(translated_pos) # Solve for displacements at the surface if fault.potency != []: success, u, grad_u = dc3d0wrapper( params.alpha, [xy[0], xy[1], 0.0], depth, dip, [ fault.potency[0], fault.potency[1], fault.potency[2], fault.potency[3] ]) u = u * 1e-6 # Unit correction: potency from N-m results in displacements in microns. else: success, u, grad_u = dc3dwrapper(params.alpha, [xy[0], xy[1], 0.0], depth, dip, [0, L], [-W, 0], [strike_slip, dip_slip, 0.0]) urot = R2.dot(np.array([[u[0]], [u[1]]])) # Update the displacements from all sources u_disp = u_disp + urot[0] v_disp = v_disp + urot[1] w_disp = w_disp + u[2] # vertical return u_disp, v_disp, w_disp
def compute_strains_stresses_from_one_fault(source, x, y, z, alpha): """ The main math of DC3D Operates on a source object (e.g., fault), and an xyz position in the same cartesian reference frame. """ L = fault_vector_functions.get_strike_length(source.xstart, source.xfinish, source.ystart, source.yfinish) W = fault_vector_functions.get_downdip_width(source.top, source.bottom, source.dipangle) depth = source.top dip = source.dipangle strike_slip = source.rtlat * -1 # The dc3d coordinate system has left-lateral positive. dip_slip = source.reverse # Preparing to rotate to a fault-oriented coordinate system. theta = source.strike - 90 theta = np.deg2rad(theta) R = np.array([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]) # horizontal rotation into strike-aligned coordinates. R2 = np.array([[np.cos(-theta), -np.sin(-theta), 0], [np.sin(-theta), np.cos(-theta), 0], [0, 0, 1]]) # Compute the position relative to the translated, rotated fault. translated_pos = np.array([[x - source.xstart], [y - source.ystart], [-z]]) xyz = R.dot(translated_pos) if source.potency: success, u, grad_u = dc3d0wrapper( alpha, [xyz[0], xyz[1], xyz[2]], depth, dip, [ source.potency[0], source.potency[1], source.potency[2], source.potency[3] ]) grad_u = grad_u * 1e-9 # DC3D0 Unit correction: potency from N-m results in strain in nanostrain u = u * 1e-6 # Unit correction: potency from N-m results in displacements in microns. else: success, u, grad_u = dc3dwrapper( alpha, [xyz[0], xyz[1], xyz[2]], depth, dip, [0, L], [-W, 0], [strike_slip, dip_slip, source.tensile]) grad_u = grad_u * 1e-3 # DC3D Unit correction. # Solve for displacement gradients at certain xyz position # Rotate grad_u back into the unprimed coordinates. desired_coords_grad_u = np.dot(R2, np.dot(grad_u, R2.T)) desired_coords_u = R2.dot(np.array([[u[0]], [u[1]], [u[2]]])) return desired_coords_grad_u, desired_coords_u
def CalcOkada(x, y, z, event_srcmod, lambda_lame, mu_lame): """Calculate strains and stresses from SRCMOD event with Okada (1992). Calculate nine element symmetric elastic strain and stress tensors at observation coordinates using Okada (1992). Dislocation parameters are given an event_srcmod dictionary which contains the geometry and slip distribution for a given SRCMOD event. Args: x: List of x-coordinates of observation points (meters) y: List of y-coordinates of observation points (meters) z: List of z-coordinates of observation points (meters, negative down) event_srcmod: Dictionary with SRCMOD event parameters for one event lambda_lame: Lame's first parameter (Pascals) mu_lame: Lame's second parameter, shear modulus (Pascals) Returns: strains, stresses: Lists of 3x3 numpy arrays with full strain and stress tensors at each 3d set of obervation coordinates """ strains = [] stresses = [] alpha = (lambda_lame + mu_lame) / (lambda_lame + 2 * mu_lame) for j in range(len(x)): strain = np.zeros((3, 3)) stress = np.zeros((3, 3)) for i in range(len(event_srcmod["x1"])): x_rot, y_rot = RotateCoords( x[j], y[j], event_srcmod["x1Utm"][i], event_srcmod["y1Utm"][i], -1.0 * event_srcmod["angle"][i] ) _, _, gradient_tensor = dc3dwrapper( alpha, [x_rot, y_rot, z[j]], event_srcmod["z3"][i], event_srcmod["dip"][i], [0.0, event_srcmod["length"][i]], [0.0, event_srcmod["width"][i]], [event_srcmod["slipStrike"][i], event_srcmod["slipDip"][i], 0.0], ) # Tensor algebra definition of strain cur_strain = 0.5 * (gradient_tensor.T + gradient_tensor) strain += cur_strain # Tensor algebra constituitive relationship for elasticity stress += lambda_lame * np.eye(cur_strain.shape[0]) * np.trace(cur_strain) + 2 * mu_lame * cur_strain strains.append(strain) stresses.append(stress) return strains, stresses
def CalcOkada(x, y, z, event_srcmod, lambda_lame, mu_lame): """Calculate strains and stresses from SRCMOD event with Okada (1992). Calculate nine element symmetric elastic strain and stress tensors at observation coordinates using Okada (1992). Dislocation parameters are given an event_srcmod dictionary which contains the geometry and slip distribution for a given SRCMOD event. Args: x: List of x-coordinates of observation points (meters) y: List of y-coordinates of observation points (meters) z: List of z-coordinates of observation points (meters, negative down) event_srcmod: Dictionary with SRCMOD event parameters for one event lambda_lame: Lame's first parameter (Pascals) mu_lame: Lame's second parameter, shear modulus (Pascals) Returns: strains, stresses: Lists of 3x3 numpy arrays with full strain and stress tensors at each 3d set of obervation coordinates """ strains = [] stresses = [] alpha = (lambda_lame + mu_lame) / (lambda_lame + 2 * mu_lame) for j in range(len(x)): strain = np.zeros((3, 3)) stress = np.zeros((3, 3)) for i in range(len(event_srcmod['x1'])): x_rot, y_rot = RotateCoords(x[j], y[j], event_srcmod['x1Utm'][i], event_srcmod['y1Utm'][i], -1.0 * event_srcmod['angle'][i]) _, _, gradient_tensor = dc3dwrapper( alpha, [x_rot, y_rot, z[j]], event_srcmod['z3'][i], event_srcmod['dip'][i], [0.0, event_srcmod['length'][i]], [0.0, event_srcmod['width'][i]], [ event_srcmod['slipStrike'][i], event_srcmod['slipDip'][i], 0.0 ]) # Tensor algebra definition of strain cur_strain = 0.5 * (gradient_tensor.T + gradient_tensor) strain += cur_strain # Tensor algebra constituitive relationship for elasticity stress += (lambda_lame * np.eye(cur_strain.shape[0]) * np.trace(cur_strain) + 2 * mu_lame * cur_strain) strains.append(strain) stresses.append(stress) return strains, stresses
def compute_okada(mu, pr, vertices): n_pts = vertices.shape[0] disp = np.empty((n_pts, 3)) for i in range(n_pts): m = 30e9 pr = 0.25 l = (2 * m * pr) / (1 - 2 * pr); alpha = (l+m) / (l + 2 * m) v = vertices[i,:] success, u, grad_u = dc3dwrapper(alpha, v, 2.0, 90, [-1.0, 1.0], [-1.0, 2.0], [-1.0, 0.0, 0.0]) if success != 0: pass disp[i, :] = u return disp
def okada_synthetics(strike, dip, rake, length, width, lon_source, lat_source, depth_source, lon_obs, lat_obs, mu): ''' Calculate neu synthetics for a subfault using Okada analytical solutions ''' from okada_wrapper import dc3dwrapper from numpy import array, cos, sin, deg2rad, zeros from pyproj import Geod theta = strike - 90 theta = deg2rad(theta) #Rotaion matrices since okada_wrapper is only for east striking fault R = array([[cos(theta), -sin(theta)], [sin(theta), cos(theta)]]) R2 = array([[cos(-theta), -sin(-theta)], [sin(-theta), cos(-theta)]]) #position of point from lon/lat to x/y assuming subfault center is origin P = Geod(ellps='WGS84') az, baz, dist = P.inv(lon_source, lat_source, lon_obs, lat_obs) dist = dist / 1000. x_obs = dist * sin(deg2rad(az)) y_obs = dist * cos(deg2rad(az)) #Calculate on rotated position xy = R.dot(array([x_obs, y_obs])) #Get Okada displacements lamb = mu alpha = (lamb + mu) / (lamb + 2 * mu) ss_in_m = 1.0 * cos(deg2rad(rake)) ds_in_m = 1.0 * sin(deg2rad(rake)) success, u, grad_u = dc3dwrapper(alpha, [xy[0], xy[1], 0.0], depth_source, dip, [-length / 2., length / 2.], [-width / 2., width / 2], [ss_in_m, ds_in_m, 0.0]) #Rotate output urot = R2.dot(array([[u[0]], [u[1]]])) u[0] = urot[0] u[1] = urot[1] #output n = u[1] e = u[0] z = u[2] return n, e, z
def okada_synthetics(strike,dip,rake,length,width,lon_source,lat_source, depth_source,lon_obs,lat_obs,mu): ''' Calculate neu synthetics for a subfault using Okada analytical solutions ''' from okada_wrapper import dc3dwrapper from numpy import array,cos,sin,deg2rad,zeros from pyproj import Geod theta=strike-90 theta=deg2rad(theta) #Rotaion matrices since okada_wrapper is only for east striking fault R=array([[cos(theta),-sin(theta)],[sin(theta),cos(theta)]]) R2=array([[cos(-theta),-sin(-theta)],[sin(-theta),cos(-theta)]]) #position of point from lon/lat to x/y assuming subfault center is origin P=Geod(ellps='WGS84') az,baz,dist=P.inv(lon_source,lat_source,lon_obs,lat_obs) dist=dist/1000. x_obs=dist*sin(deg2rad(az)) y_obs=dist*cos(deg2rad(az)) #Calculate on rotated position xy=R.dot(array([x_obs, y_obs])) #Get Okada displacements lamb=mu alpha = (lamb + mu) / (lamb + 2 * mu) ss_in_m=1.0*cos(deg2rad(rake)) ds_in_m=1.0*sin(deg2rad(rake)) success, u, grad_u = dc3dwrapper(alpha, [xy[0], xy[1], 0.0],depth_source,dip, [-length/2., length/2.], [-width/2., width/2], [ss_in_m, ds_in_m, 0.0]) #Rotate output urot=R2.dot(array([[u[0]], [u[1]]])) u[0]=urot[0] u[1]=urot[1] #output n=u[1] e=u[0] z=u[2] return n,e,z
def calc_A(self): self.A = np.zeros((self.sub_plain_num, len(self.station), 3)) plain_index = 0 rstrike = 0 for plain in self.plains: sub_stk_dim = float(plain.strike_length) / plain.strike_sub sub_dip_dim = float(plain.dip_length) / plain.dip_sub rstrike = np.deg2rad(plain.STK) for k in range(plain.strike_sub): for l in range(plain.dip_sub): for s, i in zip(self.station, range(len(self.station))): #need to take out of the loop the calculation of the station cordination in the fualt cord east = int(s.east / self.pixel) * self.pixel north = int(s.north / self.pixel) * self.pixel # possible that for real data i dont need the round up to pixel ec = east - int(plain.XO / self.pixel) * self.pixel nc = north - int(plain.YO / self.pixel) * self.pixel x = np.cos(rstrike) * nc + np.sin(rstrike) * ec y = np.sin(rstrike) * nc - np.cos(rstrike) * ec self.A[plain_index, i] += \ dc3dwrapper(self.alpha, [x, y, 0], np.abs(plain.ZO), plain.DIP, [k * sub_stk_dim, (k + 1) * sub_stk_dim], [l * sub_dip_dim, (l + 1) * sub_dip_dim], [1, 0, 0])[1] plain_index += 1 if self.cord != 'fault': temp = np.zeros(self.A.shape) temp[:, :, 0] += np.sin(rstrike) * self.A[:, :, 0] - np.cos( rstrike) * self.A[:, :, 1] temp[:, :, 1] += np.cos(rstrike) * self.A[:, :, 0] + np.sin( rstrike) * self.A[:, :, 1] temp[:, :, 2] = self.A[:, :, 2] self.A = temp if self.cord != 'cartesian': # nadir = np.array([s.east for s in self.station])/self.im_size*(self.nadir[1]-self.nadir[0])+self.nadir[0] nadir = np.array([ self.nadir[int(s.east / self.pixel)] for s in self.station ]) self.A = (np.cos(self.azimuth) * self.A[:, :, 0] - np.sin(self.azimuth) * self.A[:, :, 1]) *\ np.sin(nadir) + self.A[:, :, 2] * np.cos(nadir) # self.A = (np.sin(nadir) * (np.cos(self.azimuth) * self.A[:, :, 0] - # np.sin(self.azimuth) * self.A[:, :, 1]).T).T + (np.cos( # nadir)* self.A[:, :, 2].T).T self.A = self.A.T
def test_dc3d(): for ii,jj in [(0,1), (2,1)]: source_depth, obs_depth, poisson_ratio, mu, dip, alpha = get_params() n = (107, 107) x = linspace(-2, 2, n[0]) y = linspace(0, 4, n[1]) ux = zeros((n[0], n[1])) for i in range(n[0]): for j in range(n[1]): success, u, grad_u = dc3dwrapper(alpha, [x[i], 0.0, -y[j]], source_depth, dip, [-1, 1], [-1, 1], [1.0, 0.0, 0.0]) strain = (grad_u + grad_u.T) / 2.0 # stress = assert(success == 0) import numpy as np lame_lambda = (2 * mu * poisson_ratio) / (1 - 2 * poisson_ratio) strain_trace = sum([strain[d,d] for d in [0,1,2]]) kronecker = np.array([[1,0,0],[0,1,0],[0,0,1]]) stress = lame_lambda * strain_trace * kronecker + 2 * mu * strain ux[i, j] = stress[ii,jj] figure() cntrf = contourf(x, y, np.log10(np.abs(ux.T)), cmap = 'seismic') contour(x, y, ux.T, colors = 'k', linestyles = 'solid') xlabel('x') ylabel('y') cbar = colorbar(cntrf) tick_locator = matplotlib.ticker.MaxNLocator(nbins=5) cbar.locator = tick_locator cbar.update_ticks() cbar.set_label('$u_{\\textrm{x}}$') savefig("strike_slip.png") show()
def okada_exact(self): obs_pts = self.all_mesh[0] sm, pr = self.k_params lam = 2 * sm * pr / (1 - 2 * pr) alpha = (lam + sm) / (lam + 2 * sm) print(lam, sm, pr, alpha) n_pts = obs_pts.shape[0] u = np.zeros((n_pts, 3)) NX = 20 NY = 20 X_vals = np.linspace(-self.fault_L, self.fault_L, NX + 1) Y_vals = np.linspace(-1.0, 0.0, NX + 1) for i in range(n_pts): pt = obs_pts[i, :] for j in range(NX): X1 = X_vals[j] X2 = X_vals[j + 1] midX = (X1 + X2) / 2.0 for k in range(NY): Y1 = Y_vals[k] Y2 = Y_vals[k + 1] midY = (Y1 + Y2) / 2.0 slip = gauss_slip_fnc(midX, midY + self.top_depth, self.gauss_z) [suc, uv, grad_uv ] = okada_wrapper.dc3dwrapper(alpha, pt, -self.top_depth, 90.0, [X1, X2], [Y1, Y2], [slip, 0.0, 0.0]) if suc != 0: u[i, :] = 0 else: u[i, :] += uv return u
def calcOkadaDisplacementStress(x, y, z, event_srcmod, lambda_lame, mu_lame): """Calculate strains and stresses from SRCMOD event with Okada (1992). Calculate nine element symmetric elastic strain and stress tensors at observation coordinates using Okada (1992). Dislocation parameters are given an event_srcmod dictionary which contains the geometry and slip distribution for a given SRCMOD event. Inputs: x: List of x-coordinates of observation points (meters) y: List of y-coordinates of observation points (meters) z: List of z-coordinates of observation points (meters, negative down) event_srcmod: Dictionary with SRCMOD event parameters for one event lambda_lame: Lame's first parameter (Pascals) mu_lame: Lame's second parameter, shear modulus (Pascals) Returns: strains, stresses: Lists of 3x3 numpy arrays with full strain and stress tensors at each 3d set of obervation coordinates """ strains = [] stresses = [] displacements = [] mindistance = [] # Define the material parameter that Okada's Greens functions is sensitive too alpha = (lambda_lame + mu_lame) / (lambda_lame + 2 * mu_lame) for j in range(len(x)): strain = np.zeros((3, 3)) stress = np.zeros((3, 3)) displacement = np.zeros([3]) distance = [] for i in range(len(event_srcmod['x1'])): # Translate and (un)rotate observation coordinates to get a local reference frame in which top edge of fault is aligned with x-axis x_rot, y_rot = RotateCoords(x[j], y[j], event_srcmod['x1Utm'][i], event_srcmod['y1Utm'][i], -1.0 * event_srcmod['angle'][i]) # get rotated fault coordinates (otherwise fault patch might be offset on x-axis) x2_f, y2_f = RotateCoords(event_srcmod['x2Utm'][i], event_srcmod['y2Utm'][i], event_srcmod['x1Utm'][i], event_srcmod['y1Utm'][i], -1.0 * event_srcmod['angle'][i]) x_fault1 = np.min([0., x2_f]) x_fault2 = np.max([0., x2_f]) assert (x_fault2 - x_fault1) - event_srcmod['length'][i] < 100 # Calculate elastic deformation using Okada 1992 (BSSA) # Seven arguments to DC3DWrapper are required: # alpha = (lambda + mu) / (lambda + 2 * mu) # xo = 3-vector representing the observation point (x, y, z in the original) # depth = the depth of the fault origin # dip = the dip-angle of the rectangular dislocation surface # strike_width = the along-strike range of the surface (al1,al2 in the original) # dip_width = the along-dip range of the surface (aw1, aw2 in the original) # dislocation = 3-vector representing the direction of motion on the surface (DISL1, DISL2, DISL3) success, uvec, gradient_tensor = dc3dwrapper( alpha, [x_rot[0], y_rot[0], z[j] ], #observation depth has to be negative event_srcmod['z1'][i], event_srcmod['dip'][i], [x_fault1, x_fault2], [-1.0 * event_srcmod['width'][i], 0], [ event_srcmod['slipStrike'][i], event_srcmod['slipDip'][i], 0.0 ]) # Tensor algebra definition of strain cur_straintmp = 0.5 * (gradient_tensor.T + gradient_tensor) # cur_strain = RotateTensor(cur_straintmp, 1.0 * event_srcmod['angle'][i]) strain += cur_strain # Tensor algebra constituitive relationship for elasticity stress += (lambda_lame * np.eye(cur_strain.shape[0]) * np.trace(cur_strain) + 2. * mu_lame * cur_strain) displacement += RotateDisplacements(uvec, 1.0 * event_srcmod['angle'][i]) distance.append( np.sqrt(np.power(x_rot[0], 2.) + np.power(y_rot[0], 2.))) mindistance.append(np.min(np.array(distance))) strains.append(strain) stresses.append(stress) displacements.append(displacement) return displacements, strains, stresses, mindistance
def compute_surface_disp(params, inputs): x = np.linspace(inputs.start_gridx, inputs.finish_gridx, (inputs.finish_gridx - inputs.start_gridx) / inputs.xinc) y = np.linspace(inputs.start_gridy, inputs.finish_gridy, (inputs.finish_gridy - inputs.start_gridy) / inputs.yinc) [x2d, y2d] = np.meshgrid(x, y) u_displacements = np.zeros((len(y), len(x))) v_displacements = np.zeros((len(y), len(x))) w_displacements = np.zeros((len(y), len(x))) numrows = np.shape(u_displacements)[0] numcols = np.shape(u_displacements)[1] # A major compute loop for each source object. for i in range(len(inputs.source_object.xstart)): # Fault parameters L = conversion_math.get_strike_length(inputs.source_object.xstart[i], inputs.source_object.xfinish[i], inputs.source_object.ystart[i], inputs.source_object.yfinish[i]) W = conversion_math.get_downdip_width(inputs.source_object.top[i], inputs.source_object.bottom[i], inputs.source_object.dipangle[i]) depth = inputs.source_object.top[i] strike = inputs.source_object.strike[i] dip = inputs.source_object.dipangle[i] strike_slip = inputs.source_object.rtlat[i] * -1 # The dc3d coordinate system has left-lateral positive. dip_slip = inputs.source_object.reverse[i] # Preparing to rotate to a fault-oriented coordinate system. theta = inputs.source_object.strike[i] - 90 theta = np.deg2rad(theta) R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) R2 = np.array([[np.cos(-theta), -np.sin(-theta)], [np.sin(-theta), np.cos(-theta)]]) for ky in range(numrows): for kx in range(numcols): # Compute the position relative to the translated, rotated fault. # print("%d %d " %(kx, ky)); translated_pos = np.array( [[x2d[ky][kx] - inputs.source_object.xstart[i]], [y2d[ky][kx] - inputs.source_object.ystart[i]]]) xy = R.dot(translated_pos) success, u, grad_u = dc3dwrapper(params.alpha, [xy[0], xy[1], 0.0], depth, dip, [0, L], [-W, 0], [strike_slip, dip_slip, 0.0]) # solve for displacements at the surface urot = R2.dot(np.array([[u[0]], [u[1]]])) # Update the displacements from all sources u_displacements[ky][kx] = u_displacements[ky][kx] + urot[0] v_displacements[ky][kx] = v_displacements[ky][kx] + urot[1] w_displacements[ky][kx] = w_displacements[ky][kx] + u[2] # vertical # OUTPUT GRIDS AND DISPLACEMENTS return [x, y, x2d, y2d, u_displacements, v_displacements, w_displacements]
yout = zeros(len(x) * len(y)) ux = zeros(len(x) * len(y)) uy = zeros(len(x) * len(y)) uz = zeros(len(x) * len(y)) k = 0 for kx in range(len(x)): print kx for ky in range(len(y)): xout[k] = x[kx] yout[k] = y[ky] #Calculate on rotated position xy = R.dot(array([[x[kx]], [y[ky]]])) success, u, grad_u = dc3dwrapper(alpha, [xy[0], xy[1], 0.0], depth, dip, [-L / 2, L / 2], [-W / 2, W / 2], [0.0, slip, 0.0]) urot = R2.dot(array([[u[0]], [u[1]]])) u[0] = urot[0] u[1] = urot[1] ux[k] = u[0] uy[k] = u[1] uz[k] = u[2] k += 1 plt.figure(figsize=(16, 16)) h = (ux**2 + uy**2)**0.5 hmax = h.max() plt.scatter(xout, yout, c=h, lw=0, s=200, vmin=0.0, vmax=hmax)
def compute_strains_stresses(params, inputs): # Pseudocode: # For each receiver, at the center point, sum up the strain and stress for each source. # Return : source object, receiver object, shear stress, normal stress, and coulomb stress on each receiver. number_of_receivers = len(inputs.receiver_object.xstart) # Where do we calculate the stress tensor? receiver_center_x = [] receiver_center_y = [] receiver_center_z = [] # The values we're actually going to output. receiver_shear = [] receiver_normal = [] receiver_coulomb = [] for m in range(number_of_receivers): centercoords = conversion_math.get_fault_center( inputs.receiver_object, m) receiver_center_x.append(centercoords[0]) receiver_center_y.append(centercoords[1]) receiver_center_z.append(centercoords[2]) receiver_strike = inputs.receiver_object.strike[m] receiver_dip = inputs.receiver_object.dipangle[m] receiver_rake = inputs.receiver_object.rake[m] normal_sum = 0 shear_sum = 0 coulomb_sum = 0 for i in range(len(inputs.source_object.xstart)): # A major compute loop for each source object. L = conversion_math.get_strike_length( inputs.source_object.xstart[i], inputs.source_object.xfinish[i], inputs.source_object.ystart[i], inputs.source_object.yfinish[i]) W = conversion_math.get_downdip_width( inputs.source_object.top[i], inputs.source_object.bottom[i], inputs.source_object.dipangle[i]) depth = inputs.source_object.top[i] strike = inputs.source_object.strike[i] dip = inputs.source_object.dipangle[i] strike_slip = inputs.source_object.rtlat[i] * -1 # The dc3d coordinate system has left-lateral positive. dip_slip = inputs.source_object.reverse[i] # Preparing to rotate to a fault-oriented coordinate system. theta = inputs.source_object.strike[i] - 90 theta = np.deg2rad(theta) R = np.array([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]) # horizontal rotation into strike-aligned coordinates. R2 = np.array([[np.cos(-theta), -np.sin(-theta), 0], [np.sin(-theta), np.cos(-theta), 0], [0, 0, 1]]) # Compute the position relative to the translated, rotated fault. translated_pos = np.array( [[centercoords[0] - inputs.source_object.xstart[i]], [centercoords[1] - inputs.source_object.ystart[i]], [-centercoords[2]]]) xyz = R.dot(translated_pos) success, u, grad_u = dc3dwrapper(params.alpha, [xyz[0], xyz[1], xyz[2]], depth, dip, [0, L], [-W, 0], [strike_slip, dip_slip, 0.0]) # Solve for displacement gradients at center of receiver fault # Rotate grad_u back into the unprimed coordinates. Divide by 1000 because coordinate units (km) and slip units (m) are different by 1000. desired_coords_grad_u = np.dot(R2, np.dot(grad_u, R2.T)) desired_coords_grad_u = [ k / 1000.0 for k in desired_coords_grad_u ] # Then rotate again into receiver coordinates. strain_tensor = conversion_math.get_strain_tensor( desired_coords_grad_u) stress_tensor = conversion_math.get_stress_tensor( strain_tensor, params.lame1, params.mu) # Then compute shear, normal, and coulomb stresses. [normal, shear, coulomb] = conversion_math.get_coulomb_stresses( stress_tensor, receiver_strike, receiver_rake, receiver_dip, inputs.FRIC) normal_sum = normal_sum + normal shear_sum = shear_sum + shear coulomb_sum = coulomb_sum + coulomb receiver_normal.append(normal_sum) receiver_shear.append(shear_sum) receiver_coulomb.append(coulomb_sum) # Maybe return a source_object, receiver_object, and lists of normal, shear, coulomb values. return [ inputs.source_object, inputs.receiver_object, receiver_normal, receiver_shear, receiver_coulomb ]
disp_full_space_quadratic = d1_quadratic @ fault_slip_quadratic disp_free_surface_quadratic = np.linalg.inv(t2_quadratic) @ ( t1_quadratic @ fault_slip_quadratic) # Okada solution for 45 degree dipping fault x_okada = np.linspace(-5, 5, 1000) disp_okada_x = np.zeros(x_okada.shape) disp_okada_y = np.zeros(x_okada.shape) for i in range(0, x_okada.size): # Fault dipping at 45 degrees _, u, _ = dc3dwrapper( 2.0 / 3.0, [0, x_okada[i] + 0.5, 0], 0.5, 45, # 135 [-1000, 1000], [-np.sqrt(2) / 2, np.sqrt(2) / 2], [0.0, 1.0, 0.0], ) disp_okada_x[i] = u[1] disp_okada_y[i] = u[2] plt.figure(figsize=(6, 8)) plt.subplot(2, 1, 1) plt.plot(x_okada, disp_okada_x, "-k", linewidth=0.5, label="Okada") plt.plot( x_center, disp_free_surface[0::2], "bo", markerfacecolor="None",
def compute_strains_stresses(params, inputs): # Pseudocode: # For each receiver, at the center point, sum up the strain and stress for each source. # Return : source object, receiver object, shear stress, normal stress, and coulomb stress on each receiver. # The values we're actually going to output. receiver_shear = [] receiver_normal = [] receiver_coulomb = [] for receiver in inputs.receiver_object: centercoords = conversion_math.get_fault_center(receiver) normal_sum = 0 shear_sum = 0 coulomb_sum = 0 for source in inputs.source_object: # A major compute loop for each source object. L = conversion_math.get_strike_length(source.xstart, source.xfinish, source.ystart, source.yfinish) W = conversion_math.get_downdip_width(source.top, source.bottom, source.dipangle) depth = source.top dip = source.dipangle strike_slip = source.rtlat * -1 # The dc3d coordinate system has left-lateral positive. dip_slip = source.reverse # Preparing to rotate to a fault-oriented coordinate system. theta = source.strike - 90 theta = np.deg2rad(theta) R = np.array([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1]]) # horizontal rotation into strike-aligned coordinates. R2 = np.array([[np.cos(-theta), -np.sin(-theta), 0], [np.sin(-theta), np.cos(-theta), 0], [0, 0, 1]]) # Compute the position relative to the translated, rotated fault. translated_pos = np.array([[centercoords[0] - source.xstart], [centercoords[1] - source.ystart], [-centercoords[2]]]) xyz = R.dot(translated_pos) if source.potency != []: success, u, grad_u = dc3d0wrapper( params.alpha, [xyz[0], xyz[1], xyz[2]], depth, dip, [ source.potency[0], source.potency[1], source.potency[2], source.potency[3] ]) grad_u = grad_u * 1e-9 # DC3D0 Unit correction: potency from N-m results in displacements in nanostrain else: success, u, grad_u = dc3dwrapper(params.alpha, [xyz[0], xyz[1], xyz[2]], depth, dip, [0, L], [-W, 0], [strike_slip, dip_slip, 0.0]) grad_u = grad_u * 1e-3 # DC3D Unit correction. # Solve for displacement gradients at center of receiver fault # Rotate grad_u back into the unprimed coordinates. Divide by 1000 because coordinate units (km) and slip units (m) are different by 1000. desired_coords_grad_u = np.dot(R2, np.dot(grad_u, R2.T)) # Then rotate again into receiver coordinates. strain_tensor = conversion_math.get_strain_tensor( desired_coords_grad_u) stress_tensor = conversion_math.get_stress_tensor( strain_tensor, params.lame1, params.mu) # Then compute shear, normal, and coulomb stresses. [normal, shear, coulomb] = conversion_math.get_coulomb_stresses( stress_tensor, receiver.strike, receiver.rake, receiver.dipangle, inputs.FRIC) normal_sum = normal_sum + normal shear_sum = shear_sum + shear coulomb_sum = coulomb_sum + coulomb receiver_normal.append(normal_sum) receiver_shear.append(shear_sum) receiver_coulomb.append(coulomb_sum) # return lists of normal, shear, coulomb values for each receiver. return [receiver_normal, receiver_shear, receiver_coulomb]
"BEM: flat fault", ) # Okada solution for 0 degree dipping fault disp_okada_x = np.zeros(x.shape) disp_okada_y = np.zeros(y.shape) stress_okada_xx = np.zeros(x.shape) stress_okada_yy = np.zeros(y.shape) stress_okada_xy = np.zeros(y.shape) big_deep = 1e6 for i in range(0, x.size): _, u, s = dc3dwrapper( 2.0 / 3.0, [0, x[i], y[i] - big_deep], big_deep, 0, [-1e10, 1e10], [-L, L], [0.0, 1.0, 0.0], ) disp_okada_x[i] = u[1] disp_okada_y[i] = u[2] dgt_xx = s[1, 1] dgt_yy = s[2, 2] dgt_xy = s[1, 2] dgt_yx = s[2, 1] e_xx = dgt_xx e_yy = dgt_yy e_xy = 0.5 * (dgt_yx + dgt_xy) s_xx = mu * (e_xx + e_yy) + 2 * mu * e_xx
def rect_tensile_fault(fparams, eparams, data): "Attempt at a functional Okada dyke and sill model - wish me luck!" # fparams is a vector of fault parameter input # 8 numbers: strike, dip, opening, x, y, depth, length, width # angles in degrees, positions/dimensions in meters # x, y and depth are the centroid of the dislocation # eparams is a vector of Lame elastic parameters # 2 numbers: lambda and mu (rigidity) # values in N m # data is an array of input x and y coordinates, displacements and LOS vectors # minimum 6 numbers: x_pos, y_pos, displacement, x_los, y_los, z_los # positions, displacement in meters, rest are unit los vector components # fault parameters strike = fparams[0] dip = fparams[1] opening = fparams[2] xc = fparams[3] yc = fparams[4] zc = fparams[5] as_length = fparams[6] dd_width = fparams[7] # elastic parameters lmda = eparams[0] mu = eparams[1] alpha = (lmda + mu) / (lmda + 2 * mu) # elastic constant used by Okada # make a rotation matrix to account for strike R=np.array([[cos(radians(strike-90)), -sin(radians(strike-90))], [sin(radians(strike-90)), cos(radians(strike-90))]]) # how many data points are we dealing with? n = len(data) UX = np.zeros(n) UY = np.zeros(n) UZ = np.zeros(n) for i in range(n): # shift and rotate the coordinates into Okada geometry P=np.array([[data[i,0]-xc],[data[i,1]-yc]]); # observation point wrt centroid in map coordinates Q=R.dot(P) # observation point rotated into Okada geometry # run the Okada dc3d function on the rotated coordinates success, u, grad_u = dc3dwrapper(alpha, [Q[0], Q[1], 0], zc, dip, [-as_length/2, as_length/2], [-dd_width/2, dd_width/2], [0.0, 0.0, opening]) assert(success == 0) # here u[0] is strike-parallel displacement and u[1] is strike-normal displacement UX[i] = u[0]*sin(radians(strike))-u[1]*cos(radians(strike)) # x displacement UY[i] = u[0]*cos(radians(strike))-u[1]*sin(radians(strike)) # y displacement UZ[i] = u[2] # z displacement ULOS = np.multiply(UX,data[:,3]) + np.multiply(UY,data[:,4]) + np.multiply(UZ,data[:,5]) return ULOS
def rect_shear_fault(fparams, eparams, data): "Attempt at a functional Okada dislocation model - wish me luck!" # fparams is a vector of fault parameter input # 9 numbers: strike, dip, rake, slip, x, y, length, top, bottom # angles in degrees, positions/dimensions in meters # eparams is a vector of Lame elastic parameters # 2 numbers: lambda and mu (rigidity) # values in N m # data is an array of input x and y coordinates, displacements and LOS vectors # minimum 6 numbers: x_pos, y_pos, displacement, x_los, y_los, z_los # positions, displacement in meters, rest are unit los vector components # fault parameters strike = fparams[0] dip = fparams[1] rake = fparams[2] slip = fparams[3] xs = fparams[4] ys = fparams[5] as_length = fparams[6] dd_width = (fparams[8]-fparams[7])/sin(radians(dip)) cd_depth = (fparams[7]+fparams[8])/2 # elastic parameters lmda = eparams[0] mu = eparams[1] alpha = (lmda + mu) / (lmda + 2 * mu) # elastic constant used by Okada # calculate centroid coordinates rc = cd_depth/tan(radians(dip)) # radial surface distance from (xs,ys) to centroid rcx = rc*sin(radians(strike+90)) # coordinate shift in x from xs to centroid rcy = rc*cos(radians(strike+90)) # coordinate shift in y from ys to centroid xc = xs+rcx # x coordinate of centroid yc = ys+rcy # y coordinate of centroid # make a rotation matrix to account for strike R=np.array([[cos(radians(strike-90)), -sin(radians(strike-90))], [sin(radians(strike-90)), cos(radians(strike-90))]]) # convert slip and rake to strike-slip and dip-slip ss=slip*cos(radians(rake)) ds=slip*sin(radians(rake)) # how many data points are we dealing with? n = len(data) UX = np.zeros(n) UY = np.zeros(n) UZ = np.zeros(n) for i in range(n): # shift and rotate the coordinates into Okada geometry P=np.array([[data[i,0]-xc],[data[i,1]-yc]]); # observation point wrt centroid in map coordinates Q=R.dot(P) # observation point rotated into Okada geometry # run the Okada dc3d function on the rotated coordinates success, u, grad_u = dc3dwrapper(alpha, [Q[0], Q[1], 0], cd_depth, dip, [-as_length/2, as_length/2], [-dd_width/2, dd_width/2], [ss, ds, 0.0]) assert(success == 0) # here u[0] is strike-parallel displacement and u[1] is strike-normal displacement UX[i] = u[0]*sin(radians(strike))-u[1]*cos(radians(strike)) # x displacement UY[i] = u[0]*cos(radians(strike))-u[1]*sin(radians(strike)) # y displacement UZ[i] = u[2] # z displacement ULOS = np.multiply(UX,data[:,3]) + np.multiply(UY,data[:,4]) + np.multiply(UZ,data[:,5]) return ULOS