Пример #1
0
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;	
Пример #2
0
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
Пример #3
0
 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]
Пример #4
0
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()
Пример #5
0
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
Пример #7
0
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)
Пример #8
0
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.")
Пример #9
0
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)
Пример #10
0
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.")
Пример #11
0
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
Пример #12
0
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
Пример #13
0
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
Пример #14
0
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
Пример #15
0
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
Пример #16
0
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
Пример #17
0
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
Пример #18
0
    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
Пример #19
0
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()
Пример #20
0
    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
Пример #21
0
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
Пример #22
0
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]
Пример #23
0
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)
Пример #24
0
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
    ]
Пример #25
0
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",
Пример #26
0
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]
Пример #27
0
        "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
Пример #28
0
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
Пример #29
0
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