def write_intxt(input_object, output_file, label=None): ofile = open(output_file, 'w'); if label: ofile.write(label); ofile.write("# General: poissons_ratio friction_coef lon_min lon_max lon_zero lat_min lat_max lat_zero\n"); ofile.write("# Source_Patch: strike rake dip length_km width_km lon lat depth_km slip_m\n"); ofile.write("# Receiver: strike rake dip length_km width_km lon lat depth_km\n\n"); ofile.write("General: %f %f %f %f %f %f %f %f \n" % (input_object.PR1, input_object.FRIC, input_object.minlon, input_object.maxlon, input_object.zerolon, input_object.minlat, input_object.maxlat, input_object.zerolat) ); for src in input_object.source_object: if not src.potency: # write a finite source L = fault_vector_functions.get_strike_length(src.xstart, src.xfinish, src.ystart, src.yfinish); # in km W = fault_vector_functions.get_downdip_width(src.top, src.bottom, src.dipangle); # in km fault_lon, fault_lat = fault_vector_functions.xy2lonlat(src.xstart, src.ystart, src.zerolon, src.zerolat); slip = fault_vector_functions.get_vector_magnitude([src.rtlat, src.reverse]); # in m ofile.write("Source_Patch: %f %f %f %f %f %f %f %f %f\n" % (src.strike, src.rake, src.dipangle, L, W, fault_lon, fault_lat, src.top, slip)); if src.potency: # write a focal mechanism source continue; # still working on this. for rec in input_object.receiver_object: L = fault_vector_functions.get_strike_length(rec.xstart, rec.xfinish, rec.ystart, rec.yfinish); # in km W = fault_vector_functions.get_downdip_width(rec.top, rec.bottom, rec.dipangle); # in km fault_lon, fault_lat = fault_vector_functions.xy2lonlat(rec.xstart, rec.ystart, rec.zerolon, rec.zerolat); ofile.write("Receiver: %f %f %f %f %f %f %f %f \n" % (rec.strike, rec.rake, rec.dipangle, L, W, fault_lon, fault_lat, rec.top) ); ofile.close(); print("Writing file %s " % output_file); return;
def read_stat2C_geometry(infile): """ Reading a fault geometry file used for stat2c.f, specifically for the Cascadia subduction zone. Returns a list of fault dictionaries in the internal format. """ faultlist = [] with open(infile, 'r') as ifile: for line in ifile: temp = line.split() if len(temp) == 2: upper_depth, lower_depth = float(temp[0]), float(temp[1]) elif len(temp) == 7: lower_lat_corner = float(temp[0]) # in degrees lower_lon_corner = float(temp[1]) # in degrees length = float(temp[2]) # in km strike = float(temp[3]) # in degrees rake = float(temp[4]) # in degrees dip = float(temp[6]) # in degrees slip = float(temp[5]) / 100 # from cm/yr into m/yr downdip_width = fault_vector_functions.get_downdip_width( upper_depth, lower_depth, dip) vector_mag = downdip_width * np.cos(np.deg2rad(dip)) # how far the bottom edge is displaced upper_corner_along_strike = fault_vector_functions.add_vector_to_point( 0, 0, vector_mag, strike - 90) upper_corner_back_edge = fault_vector_functions.add_vector_to_point( upper_corner_along_strike[0], upper_corner_along_strike[1], length, strike + 180) fault_lon, fault_lat = fault_vector_functions.xy2lonlat_single( upper_corner_back_edge[0], upper_corner_back_edge[1], lower_lon_corner, lower_lat_corner) new_fault = { "strike": strike, "dip": dip, "length": length, "rake": rake, "slip": slip, "tensile": 0, "depth": upper_depth, "width": downdip_width, "lon": fault_lon, "lat": fault_lat } faultlist.append(new_fault) return faultlist
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 get_fault_four_corners(fault_object, coords="cartesian"): """ Get the four corners of the object, including updip and downdip. depth is fault_object.top dip is fault_object.dipangle (in case you need it) coords can be "cartesian" or "geographic" for lon/lat """ W = fault_vector_functions.get_downdip_width(fault_object.top, fault_object.bottom, fault_object.dipangle) strike = fault_object.strike updip_point0 = [fault_object.xstart, fault_object.ystart] updip_point1 = [fault_object.xfinish, fault_object.yfinish] vector_mag = W * np.cos(np.deg2rad(fault_object.dipangle)) # how far the bottom edge is displaced # downdip from map-view downdip_point0 = fault_vector_functions.add_vector_to_point( fault_object.xstart, fault_object.ystart, vector_mag, strike + 90) # strike+90 = downdip direction. downdip_point1 = fault_vector_functions.add_vector_to_point( fault_object.xfinish, fault_object.yfinish, vector_mag, strike + 90) if coords == 'geographic': updip_point0 = fault_vector_functions.xy2lonlat_single( updip_point0[0], updip_point0[1], fault_object.zerolon, fault_object.zerolat) updip_point1 = fault_vector_functions.xy2lonlat_single( updip_point1[0], updip_point1[1], fault_object.zerolon, fault_object.zerolat) downdip_point0 = fault_vector_functions.xy2lonlat_single( downdip_point0[0], downdip_point0[1], fault_object.zerolon, fault_object.zerolat) downdip_point1 = fault_vector_functions.xy2lonlat_single( downdip_point1[0], downdip_point1[1], fault_object.zerolon, fault_object.zerolat) x_total = [ updip_point0[0], updip_point1[0], downdip_point1[0], downdip_point0[0], updip_point0[0] ] y_total = [ updip_point0[1], updip_point1[1], downdip_point1[1], downdip_point0[1], updip_point0[1] ] x_updip = [updip_point0[0], updip_point1[0]] y_updip = [updip_point0[1], updip_point1[1]] return [x_total, y_total, x_updip, y_updip]
def get_fault_center(fault_object): """ Compute the x-y-z coordinates of the center of a PyCoulomb fault patch (a namedtuple) """ W = fault_vector_functions.get_downdip_width(fault_object.top, fault_object.bottom, fault_object.dipangle) center_z = (fault_object.top + fault_object.bottom) / 2.0 updip_center_x = (fault_object.xstart + fault_object.xfinish) / 2.0 updip_center_y = (fault_object.ystart + fault_object.yfinish) / 2.0 vector_mag = W * np.cos(np.deg2rad(fault_object.dipangle)) / 2.0 # how far the middle is displaced # downdip from map-view center_point = fault_vector_functions.add_vector_to_point( updip_center_x, updip_center_y, vector_mag, fault_object.strike + 90) # strike+90 = downdip direction. center = [center_point[0], center_point[1], center_z] return center
def read_fault_slip_line_static1d_visco1d(line, upper_depth, lower_depth, dip): """ read a line from fred's format of faults into my format of faults for Visco1D, the slip field is pretty meaningless """ lower_lat_corner = float(line.split()[0]) # in degrees lower_lon_corner = float(line.split()[1]) # in degrees length = float(line.split()[2]) # in km strike = float(line.split()[3]) # in degrees rake = float(line.split()[4]) # in degrees slip = float(line.split()[5]) # in cm downdip_width = fault_vector_functions.get_downdip_width( upper_depth, lower_depth, dip) vector_mag = downdip_width * np.cos(np.deg2rad(dip)) # how far bottom edge is displaced from top edge upper_corner_along_strike = fault_vector_functions.add_vector_to_point( 0, 0, vector_mag, strike - 90) upper_corner_back_edge = fault_vector_functions.add_vector_to_point( upper_corner_along_strike[0], upper_corner_along_strike[1], length, strike + 180) fault_lon, fault_lat = fault_vector_functions.xy2lonlat_single( upper_corner_back_edge[0], upper_corner_back_edge[1], lower_lon_corner, lower_lat_corner) new_fault = { "strike": strike, "dip": dip, "length": length, "rake": rake, "slip": slip / 100, "tensile": 0, "depth": upper_depth, "width": downdip_width, "lon": fault_lon, "lat": fault_lat } return new_fault
def convert_2d_segments_to_internal_fault_dictionary(fault_segments, dip, dip_direction, top_depth, bottom_depth, slip_cm, rake): """ fault_segment includes: (starting lon, starting_lat, strike, length, ending_lon, ending_lat) where starting_lon is probably south of ending_lon. We are converting to the internal fault_dictionary format from Elastic_stresses_py """ fault_dict_list = []; for item in fault_segments: # Flip strike/dip if necessary if dip_direction == 'south': strike = item[2] - 180; # Do I flip the lon/lat to the other corner here too? Not totally sure. Should test this. else: strike = item[2]; downdip_width = fault_vector_functions.get_downdip_width(top_depth, bottom_depth, dip); new_fault = {"strike": strike, "dip": dip, "length": item[3], "rake": rake, "slip": slip_cm / 100, "tensile": 0, "depth": top_depth, "width": downdip_width, "lon": item[0], "lat": item[1]}; fault_dict_list.append(new_fault); return fault_dict_list;
def get_fault_slip_moment(fault_object, mu): """ From a source fault object, calculate the seismic moment. Must be a finite fault, not a point source. Not really used yet, but could be useful in the future. """ if fault_object.potency: # for the case of point source, we can't do the moment calculation return None, None W = fault_vector_functions.get_downdip_width(fault_object.top, fault_object.bottom, fault_object.dipangle) L = fault_vector_functions.get_strike_length(fault_object.xstart, fault_object.xfinish, fault_object.ystart, fault_object.yfinish) area = L * W * 1000 * 1000 slip = fault_vector_functions.get_vector_magnitude( [fault_object.rtlat, fault_object.reverse]) seismic_moment = mu * area * slip moment_magnitude = moment_calculations.mw_from_moment(seismic_moment) return seismic_moment, moment_magnitude
def coulomb_fault_to_fault_dict(source_object): """Convert a list of fault objects from Elastic_stresses_py into a list of internal dictionary objects""" fault_dict_list = [] for src in source_object: if src.potency: print( "ERROR! Cannot convert a point source into a rectangular source. Skipping..." ) continue one_fault = { "strike": src.strike, "dip": src.dipangle, "depth": src.top, "rake": fault_vector_functions.get_rake(rtlat_strike_slip=src.rtlat, dip_slip=src.reverse), "slip": fault_vector_functions.get_total_slip(src.rtlat, src.reverse), "tensile": src.tensile, "length": fault_vector_functions.get_strike_length(src.xstart, src.xfinish, src.ystart, src.yfinish), "width": fault_vector_functions.get_downdip_width(src.top, src.bottom, src.dipangle) } lon, lat = fault_vector_functions.xy2lonlat(src.xstart, src.ystart, src.zerolon, src.zerolat) one_fault["lon"] = lon one_fault["lat"] = lat fault_dict_list.append(one_fault) return fault_dict_list
def split_subfault_receivers(params, inputs): strike_split = params.strike_num_receivers dip_split = params.dip_num_receivers if strike_split == 1 and dip_split == 1: # If we're not splitting the subfaults... subfaulted_receivers = inputs.receiver_object print("Not subdividing receiver faults further.") else: subfaulted_receivers = [] print("Splitting %d receiver faults into %d subfaults each." % (len(inputs.receiver_object), strike_split * dip_split)) for fault in inputs.receiver_object: # for each receiver... # We find the depths corresponding to the tops and bottoms of our new sub-faults zsplit_array = get_split_z_array(fault.top, fault.bottom, dip_split) for j in range(dip_split): # First we split it up by dip. # Get the new coordinates of the top of the fault plane. W = fault_vector_functions.get_downdip_width( fault.top, zsplit_array[j], fault.dipangle) vector_mag = W * np.cos(np.deg2rad(fault.dipangle)) # how far the bottom edge is displaced downdip from map-view # Get the starting points for the next row of fault subpatches. [start_x_top, start_y_top] = fault_vector_functions.add_vector_to_point( fault.xstart, fault.ystart, vector_mag, fault.strike + 90) [finish_x_top, finish_y_top] = fault_vector_functions.add_vector_to_point( fault.xfinish, fault.yfinish, vector_mag, fault.strike + 90) [xsplit_array, ysplit_array ] = get_split_x_y_arrays(start_x_top, finish_x_top, start_y_top, finish_y_top, strike_split) for k in range(strike_split): single_subfaulted_receiver = cc.Faults_object( xstart=xsplit_array[k], xfinish=xsplit_array[k + 1], ystart=ysplit_array[k], yfinish=ysplit_array[k + 1], Kode=fault.Kode, rtlat=0, reverse=0, tensile=0, potency=[], strike=fault.strike, dipangle=fault.dipangle, zerolon=inputs.zerolon, zerolat=inputs.zerolat, rake=fault.rake, top=zsplit_array[j], bottom=zsplit_array[j + 1], comment=fault.comment) subfaulted_receivers.append(single_subfaulted_receiver) subfaulted_objects = cc.Input_object( PR1=inputs.PR1, FRIC=inputs.FRIC, depth=inputs.depth, start_gridx=inputs.start_gridx, finish_gridx=inputs.finish_gridx, start_gridy=inputs.start_gridy, finish_gridy=inputs.finish_gridy, xinc=inputs.xinc, yinc=inputs.yinc, minlon=inputs.minlon, maxlon=inputs.maxlon, zerolon=inputs.zerolon, minlat=inputs.minlat, maxlat=inputs.maxlat, zerolat=inputs.zerolat, source_object=inputs.source_object, receiver_object=subfaulted_receivers, receiver_horiz_profile=inputs.receiver_horiz_profile) return subfaulted_objects
def read_four_corners_fault_file(filename): """ Read fault file from Shengji Wei, EPSL, 2015. Provided: lat/lon/depth of each corner. Read into an internal fault dictionary """ print("Reading file %s" % filename); lons, lats, depths = [], [], []; ifile = open(filename, 'r'); for line in ifile: temp = line.split(); if len(temp) == 0: continue; if temp[0][0] == "#": continue; else: lons.append(float(temp[0])) lats.append(float(temp[1])) depths.append(float(temp[2])) ifile.close(); # Get parameters of fault patch; assuming four vertices are given, and top depth is same between two top vertices. x, y = fault_vector_functions.latlon2xy(lons, lats, lons[0], lats[0]); # Find the top of the fault plane shallow_depth = np.min(depths); deep_depth = np.max(depths); shallow_depth_idx = np.where(depths == shallow_depth) idx0 = shallow_depth_idx[0][0]; idx1 = shallow_depth_idx[0][1]; deltax = x[idx1] - x[idx0]; deltay = y[idx1] - y[idx0]; strike_initial = fault_vector_functions.get_strike(deltax, deltay) # STRIKE MIGHT BE 180 DEGREES AWAY dip = fault_vector_functions.get_dip_degrees(x[-2], y[-2], depths[-2], x[-1], y[-1], depths[-1]); # Assuming the last two entries go from the bottom to the top. # MIGHT REVERSE THE STRIKE HERE. strike_vector = fault_vector_functions.get_strike_vector(strike_initial); dip_vector = fault_vector_functions.get_dip_vector(strike_initial, dip); xp = np.cross(dip_vector, strike_vector); # dip x strike for outward facing normal, by right hand rule. if xp[2] < 0: print("WARNING: STRIKE AND DIP DON'T OBEY RIGHT HAND RULE. FLIPPING STRIKE."); strike = strike_initial+180; else: strike = strike_initial; if strike > 360: strike = strike-360; length = fault_vector_functions.get_strike_length(x[idx0], x[idx1], y[idx0], y[idx1]); width = fault_vector_functions.get_downdip_width(shallow_depth, deep_depth, dip); # GET THE TOP CORNER OF THE FAULT PLANE (ONE OF TWO OPTIONS) print(" ", strike, "degrees strike"); print(" ", dip, "degrees dip"); print(" ", length, "km length"); print(" ", width, "km width"); if strike_initial == strike: updip_corner_lon = lons[idx0]; updip_corner_lat = lats[idx0]; updip_corner_depth = depths[idx0]; else: updip_corner_lon = lons[idx1]; updip_corner_lat = lats[idx1]; updip_corner_depth = depths[idx1]; fault_object = {'strike': strike, 'slip': 0, 'tensile': 0, 'rake': 0, 'length': length, 'width': width, 'dip': dip, 'lon': updip_corner_lon, 'lat': updip_corner_lat, 'depth': updip_corner_depth}; return fault_object;