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_params_for_slip_source(strike, dip, rake, depth, L, W, fault_lon, fault_lat, slip, zerolon, zerolat): [xcorner, ycorner] = fault_vector_functions.latlon2xy(fault_lon, fault_lat, zerolon, zerolat); # if hypocenter is really the center of the rupture: # xstart, ystart = fault_vector_functions.add_vector_to_point(xcorner, ycorner, -L/2, strike); # xfinish, yfinish = fault_vector_functions.add_vector_to_point(xcorner, ycorner, L/2, strike); # top, bottom = fault_vector_functions.get_top_bottom_from_center(depth, W, dip); # if hypocenter is on one side of rupture: xstart, ystart = fault_vector_functions.add_vector_to_point(xcorner, ycorner, 0, strike); xfinish, yfinish = fault_vector_functions.add_vector_to_point(xcorner, ycorner, L, strike); top, bottom = fault_vector_functions.get_top_bottom_from_top(depth, W, dip); rtlat, reverse = fault_vector_functions.get_rtlat_dip_slip(slip, rake); comment = ''; return [xstart, xfinish, ystart, yfinish, rtlat, reverse, top, bottom, comment]
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 write_stress_results_slippy_format(faults_list, shear, normal, coulomb, outfile): """ :param faults_list: a list of fault dictionaries :param outfile: name of output file. :param shear: list of shear stress change on each element, kpa :param normal: list of normal stress, kpa :param coulomb: list of coulomb stress, kpa Caveat: This is ALMOST slippy format. You will lose the fault segment number, and we add the rake column. """ print("Writing file %s " % outfile) ofile = open(outfile, 'w') ofile.write( "# lon[degrees] lat[degrees] depth[m] strike[degrees] dip[degrees] rake[degrees] length[m] width[m] " "shear[KPa] normal[KPa] coulomb[KPa]\n") for i, item in enumerate(faults_list): x_center, y_center = fault_vector_functions.add_vector_to_point( 0, 0, item["length"] / 2, item["strike"]) center_lon, center_lat = fault_vector_functions.xy2lonlat( x_center, y_center, item["lon"], item["lat"]) ofile.write("%f %f %f " % (center_lon, center_lat, item["depth"] * -1000)) ofile.write( "%f %f %f %f %f %f %f %f \n" % (item["strike"], item["dip"], item["rake"], item["length"] * 1000, item["width"] * 1000, shear[i], normal[i], coulomb[i])) ofile.close() return
def write_slippy_distribution(faults_list, outfile): """ :param faults_list: a list of fault dictionaries :param outfile: name of output file. Caveat: can only do one fault segment right now. That part of the read/write cycle is lossy. """ print("Writing file %s " % outfile) ofile = open(outfile, 'w') ofile.write( "# lon[degrees] lat[degrees] depth[m] strike[degrees] dip[degrees] length[m] width[m] left-lateral[m] " "thrust[m] tensile[m] segment_num\n") for item in faults_list: x_center, y_center = fault_vector_functions.add_vector_to_point( 0, 0, item["length"] / 2, item["strike"]) center_lon, center_lat = fault_vector_functions.xy2lonlat( x_center, y_center, item["lon"], item["lat"]) rtlat_slip, dip_slip = fault_vector_functions.get_rtlat_dip_slip( item["slip"], item["rake"]) tensile_slip = 0 ofile.write("%f %f %f " % (center_lon, center_lat, item["depth"] * -1000)) ofile.write( "%f %f %f %f %f %f %f 0 \n" % (item["strike"], item["dip"], item["length"] * 1000, item["width"] * 1000, -1 * rtlat_slip, dip_slip, tensile_slip)) ofile.close() return
def compute_params_for_WC_source(strike, dip, rake, depth, mag, faulting_type, fault_lon, fault_lat, zerolon, zerolat): [xcenter, ycenter] = fault_vector_functions.latlon2xy(fault_lon, fault_lat, zerolon, zerolat); L = wells_and_coppersmith.RLD_from_M(mag, faulting_type); # rupture length W = wells_and_coppersmith.RW_from_M(mag, faulting_type); # rupture width slip = wells_and_coppersmith.rectangular_slip(L * 1000, W * 1000, mag); # must input in meters # if hypocenter is really the center of the rupture: # xstart, ystart = fault_vector_functions.add_vector_to_point(xcenter, ycenter, -L/2, strike); # xfinish, yfinish = fault_vector_functions.add_vector_to_point(xcenter, ycenter, L/2, strike); # top, bottom = fault_vector_functions.get_top_bottom_from_center(depth, W, dip); # if hypocenter is on one side of rupture: xstart, ystart = fault_vector_functions.add_vector_to_point(xcenter, ycenter, 0, strike); xfinish, yfinish = fault_vector_functions.add_vector_to_point(xcenter, ycenter, L, strike); rtlat, reverse = fault_vector_functions.get_rtlat_dip_slip(slip, rake); top, bottom = fault_vector_functions.get_top_bottom_from_top(depth, W, dip); comment = ''; return [xstart, xfinish, ystart, yfinish, rtlat, reverse, top, bottom, comment];
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 read_slippy_distribution(infile, desired_segment=-1): """ Read a file from the Slippy inversion outputs (lon[degrees] lat[degrees] depth[m] strike[degrees] dip[degrees] length[m] width[m] left-lateral[m] thrust[m] tensile[m] segment_num) into a list of fault dictionaries. Lon/lat usually refer to the center top of the fault, so it must convert the lon/lat to the top left corner. :param infile: name of input slip distribution file :type infile: string :param desired_segment: starting at 0, which fault segment do we want to return? default of -1 means all. :type desired_segment: int :returns: list of fault dictionaries :rtype: list """ print("Reading slippy distribution %s " % infile) fault_list = [] [ lon, lat, depth, strike, dip, length, width, ll_slip, thrust_slip, _, num ] = np.loadtxt(infile, skiprows=1, unpack=True, dtype={ "names": ('lon', 'lat', 'depth', 'strike', 'dip', 'length', 'width', 'ss', 'ds', 'tensile', 'num'), "formats": (float, float, float, float, float, float, float, float, float, float, float) }) for i in range(len(lon)): if desired_segment == -1 or desired_segment == num[i]: one_fault = { "strike": strike[i], "dip": dip[i], "length": length[i] / 1000, "width": width[i] / 1000, "depth": -depth[i] / 1000 } center_lon = lon[i] center_lat = lat[i] x_start, y_start = fault_vector_functions.add_vector_to_point( 0, 0, one_fault["length"] / 2, one_fault["strike"] - 180) # in km corner_lon, corner_lat = fault_vector_functions.xy2lonlat( x_start, y_start, center_lon, center_lat) one_fault["lon"] = corner_lon one_fault["lat"] = corner_lat one_fault["rake"] = fault_vector_functions.get_rake( rtlat_strike_slip=-ll_slip[i], dip_slip=thrust_slip[i]) one_fault["slip"] = fault_vector_functions.get_total_slip( ll_slip[i], thrust_slip[i]) one_fault["tensile"] = 0 fault_list.append(one_fault) return fault_list
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_stress_slippy_format(infile): """ Read stress results from CFS calculation :param infile: text file in full-stress format :returns: fault_list (internal dictionary format). shear, normal, and coulomb are matching lists in KPa """ print("Reading file %s " % infile) fault_list = [] [ lon, lat, depth, strike, dip, rake, length, width, shear, normal, coulomb ] = np.loadtxt(infile, skiprows=1, unpack=True, dtype={ "names": ('lon', 'lat', 'depth', 'strike', 'dip', 'rake', 'length', 'width', 'shear', 'normal', 'coulomb'), "formats": (float, float, float, float, float, float, float, float, float, float, float, float) }) for i in range(len(lon)): one_fault = { "strike": strike[i], "dip": dip[i], "length": length[i] / 1000, "width": width[i] / 1000, "depth": -depth[i] / 1000 } center_lon = lon[i] center_lat = lat[i] x_start, y_start = fault_vector_functions.add_vector_to_point( 0, 0, one_fault["length"] / 2, one_fault["strike"] - 180) # in km corner_lon, corner_lat = fault_vector_functions.xy2lonlat( x_start, y_start, center_lon, center_lat) one_fault["lon"] = corner_lon one_fault["lat"] = corner_lat one_fault["rake"] = rake[i] one_fault["slip"] = 0 one_fault["tensile"] = 0 fault_list.append(one_fault) return fault_list, shear, normal, coulomb
def fault_dict_to_coulomb_fault(fault_dict_list, zerolon_system=None, zerolat_system=None): """ Convert a list of internal dictionary objects into a list of source objects for Elastic_stresses_py By default, the bottom corner of the fault is the center of the coordinate system, but Parameters zerolon_system and zerolat_system can be passed in for system with 1+ faults. """ source_object = [] for onefault in fault_dict_list: zerolon = onefault['lon'] if not zerolon_system else zerolon_system zerolat = onefault['lat'] if not zerolat_system else zerolat_system _top, bottom = fault_vector_functions.get_top_bottom_from_top( onefault['depth'], onefault['width'], onefault['dip']) [startx, starty ] = fault_vector_functions.latlon2xy_single(onefault['lon'], onefault['lat'], zerolon, zerolat) rtlat, reverse = fault_vector_functions.get_rtlat_dip_slip( onefault['slip'], onefault['rake']) xfinish, yfinish = fault_vector_functions.add_vector_to_point( startx, starty, onefault['length'], onefault['strike']) one_source = cc.Faults_object(xstart=startx, xfinish=xfinish, ystart=starty, yfinish=yfinish, Kode=100, rtlat=rtlat, reverse=reverse, tensile=onefault['tensile'], potency=[], strike=onefault['strike'], dipangle=onefault['dip'], zerolon=zerolon, zerolat=zerolat, rake=onefault['rake'], top=onefault['depth'], bottom=bottom, comment='') source_object.append(one_source) return source_object
def write_faults_json(faults_list, outfile): """ Writes faults to json as receivers with zero slip position is lon/lat/depth in meters (negative means below surface) :param faults_list: list of fault dictionaries :type faults_list: list :param outfile: name of output json file :type outfile: string """ output = {} for k, fault in enumerate(faults_list): # Convert the fault (which has top left corner) into a fault with top center coordinate label = "fault" + str(k) newfault = {} corner_lon = fault["lon"] corner_lat = fault["lat"] x_center, y_center = fault_vector_functions.add_vector_to_point( 0, 0, fault["length"] / 2, fault["strike"]) center_lon, center_lat = fault_vector_functions.xy2lonlat( x_center, y_center, corner_lon, corner_lat) newfault["strike"] = fault["strike"] newfault["dip"] = fault["dip"] newfault["length"] = fault["length"] * 1000 newfault["width"] = fault["width"] * 1000 newfault["basis1"] = [1, 0, 0] newfault["basis2"] = None newfault["Nlength"] = 1 newfault["Nwidth"] = 1 newfault["penalty"] = 1 newfault["position"] = [ center_lon, center_lat, -fault["depth"] * 1000 ] output[label] = newfault with open(outfile, 'w') as ofile: json.dump(output, ofile, indent=4) return
def read_faults_json(infile): """ Read all faults from a json file (just geometry; no slip or rake) into a list of fault dictionaries. It has to convert from fault center to fault corner. Faults read from JSON have zero slip. :param infile: name of input json file :type infile: string :returns: list of fault dictionaries :rtype: list """ fault_list = [] config_file = open(infile, 'r') config = json.load(config_file) for key in config.keys(): one_fault = { "strike": config[key]["strike"], "dip": config[key]["dip"], "length": config[key]["length"] / 1000.0, "width": config[key]["width"] / 1000.0 } center_lon = config[key]["position"][0] center_lat = config[key]["position"][1] x_start, y_start = fault_vector_functions.add_vector_to_point( 0, 0, one_fault["length"] / 2, one_fault["strike"] - 180) # in km corner_lon, corner_lat = fault_vector_functions.xy2lonlat( x_start, y_start, center_lon, center_lat) # one_fault["lon"] = corner_lon one_fault["lat"] = corner_lat one_fault["depth"] = -config[key]["position"][2] / 1000 one_fault["rake"] = 0 one_fault["slip"] = 0 one_fault["tensile"] = 0 fault_list.append(one_fault) config_file.close() return fault_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_srcmod_distribution(infile): """ Let's assume that the lon/lat/depth given in the SRCMOD .fsp file is for the top center of the fault patch. This function doesn't have a unit test yet. :param infile: name of input slip distribution file, defined to be the '.fsp' file :type infile: string :returns: list of fault dictionaries :rtype: list """ print("Reading SRCMOD distribution %s " % infile) fault_list = [] overall_strike, overall_dip, total_len_km, nx, total_width_km, nz = 0, 90, 10, 10, 10, 10 # defaults. ifile = open(infile, 'r') for line in ifile: temp = line.split() if len(temp) > 3: if temp[0] == '%' and temp[1] == 'Mech': overall_strike = float(temp[5]) overall_dip = float(temp[8]) if temp[0] == '%' and temp[1] == 'Size': total_len_km = float(temp[5]) total_width_km = float(temp[9]) if temp[0] == '%' and temp[3] == 'Nx': nx = int(temp[5]) nz = int(temp[8]) if temp[0] != '%': lon_top_center = float(temp[1]) lat_top_center = float(temp[0]) depth_top_center = float(temp[4]) depth_top, _ = fault_vector_functions.get_top_bottom_from_center( depth_top_center, total_width_km / nz, overall_dip) slip_m = float(temp[5]) rake = float(temp[6]) one_fault = { "strike": overall_strike, "dip": overall_dip, "length": total_len_km / nx, "width": total_width_km / nz, "depth": depth_top_center, "rake": rake, "slip": slip_m, "tensile": 0 } x_start, y_start = fault_vector_functions.add_vector_to_point( 0, 0, one_fault["length"] / 2, one_fault["strike"] - 180) # in km _downdip_width_proj = one_fault["width"] * np.cos( np.deg2rad(overall_dip)) # x_start, y_start = fault_vector_functions.add_vector_to_point(x_start, y_start, downdip_width_proj/2, # one_fault["strike"] - 90); # ^^ offset the fault location for center. Optional/unknown. corner_lon, corner_lat = fault_vector_functions.xy2lonlat( x_start, y_start, lon_top_center, lat_top_center) one_fault["lon"] = corner_lon one_fault["lat"] = corner_lat fault_list.append(one_fault) ifile.close() print(" -->Returning %d fault segments" % len(fault_list)) return fault_list