### Install swprocess Source: https://github.com/jpvantassel/swprocess/blob/main/docs/install.md Install the swprocess package using pip. ```bash pip install swprocess ``` ```bash pip install swprocess --upgrade ``` -------------------------------- ### Create miniseed files Source: https://github.com/jpvantassel/swprocess/blob/main/test/data/utils/create_mseed.ipynb Create example miniseed files for testing. ```python start_master = obspy.UTCDateTime(2020, 12, 31, 0) for d in range(2): for n in range(24): delta = datetime.timedelta(days=d, hours=n).total_seconds() starttime = start_master + delta start_seconds = 3600*n + 3600*24*d trace = obspy.Trace(np.arange(3600) + start_seconds, header=dict(starttime=starttime, network="NW", station="STN01", channel="BHZ")) obspy.Stream([trace]).write(f"NW.STN01_{starttime.year}{str(starttime.month).zfill(2)}{str(starttime.day).zfill(2)}_{str(n).zfill(2)}0000.mseed") ``` -------------------------------- ### Install swprocess Source: https://github.com/jpvantassel/swprocess/blob/main/README.md Command to install the swprocess package using pip. ```bash pip install swprocess ``` -------------------------------- ### MASW Workflow Selection Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Selects the MASW workflow. 'time-domain' is recommended. ```python # Masw workflow {"time-domain", "frequency-domain", "single"}, time-domain is recommended workflow = "time-domain" ``` -------------------------------- ### Import Libraries Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Imports necessary Python libraries for data processing and visualization. ```python import json import time import pathlib import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.gridspec import GridSpec import numpy as np import swprocess ``` -------------------------------- ### MASW Settings and Execution Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This code snippet sets up the parameters for the MASW analysis and then runs the analysis for a given set of filenames. It also times the execution. ```python # This cell may take several seconds to run. %matplotlib qt5 settings = swprocess.Masw.create_settings_dict(workflow=workflow, trim=trim, trim_begin=trim_begin, trim_end=trim_end, mute=mute, method=method, window_kwargs=window_kwargs, transform=transform, fmin=fmin, fmax=fmax, pad=pad, df=df, vmin=vmin, vmax=vmax, nvel=nvel, vspace=vspace, weighting=fdbf_weighting, steering=fdbf_steering, snr=snr, noise_begin=noise_begin, noise_end=noise_end, signal_begin=signal_begin, signal_end=signal_end, pad_snr = pad_snr, df_snr=df_snr) start = time.perf_counter() wavefieldtransforms = [] for fnames in fnames_set: wavefieldtransforms.append(swprocess.Masw.run(fnames=fnames, settings=settings)) end = time.perf_counter() print(f"Elapsed Time (s): {round(end-start,2)}") ``` -------------------------------- ### Image Normalization and Display Settings Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Configuration for image normalization and display options for the MASW results. ```python # Image normalization {"none", "absolute-maximum" "frequency-maximum"} -> "frequency-maximum" is recommended. wavefield_normalization = "frequency-maximum" # Display the wavelength resolution limit. display_lambda_res = True # Display Yoon and Rix (2009) near-field criteria display_nearfield = False number_of_array_center_distances = 1 ``` -------------------------------- ### Output of MASW Execution Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This shows the elapsed time for the MASW execution. ```text Elapsed Time (s): 6.27 ``` -------------------------------- ### Time Domain Padding Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Configures zero padding for the time-domain record to achieve a desired frequency step. Padding with df=0.5 is recommended. ```python # Zero pad the time-domain record to achieve a desired frequency step. Padding with df=0.5 is recommended. pad, df = True, 0.5 ``` -------------------------------- ### Set Names Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Defines names for each set of files. If set to None, files will be named based on their source position. ```python # Name for each fnames_set, if None, sets will be named according to the source position. names = None ``` -------------------------------- ### Create SU from CSV Data (Example 2) Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_3/construction/create_su.ipynb This script reads CSV data and saves it in SU format, using a predefined list of desired x-coordinates for sensor placement. It iterates through multiple CSV files to generate the sensor data. ```python dt = 0.001 desired_time = np.arange(0, 2, dt) # desired_xs = [5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 55, 60, 65] desired_xs = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 55, 60, 65, 70] desired_xs = [ x + 0.05 for x in desired_xs] desired_x_index = 0 current_x, dx = 0.0, 0.05 current_desired_x = desired_xs[desired_x_index] source = swprocess.Source(x=0.05, y=0, z=0) sensors = [] for setnumber in range(6): fname = f"set{str(setnumber).zfill(2)}.csv" with open(fname, "r") as f: ncols = f.readlines()[1].count(",") df = pd.read_csv(fname, usecols=np.arange(0, ncols), names=["time", *[x for x in np.arange(ncols)]], skiprows=2, skipinitialspace=True) xs = np.arange(ncols-1)*dx + current_x current_x = float(xs[-1]) + dx for col, _x in enumerate(xs): if np.abs(_x - current_desired_x) > dx/2: continue interpolator = interp1d(x=df["time"], y=df[col], kind="cubic") sensor = swprocess.Sensor1C(amplitude=np.array(interpolator(desired_time), dtype=np.float32), dt=dt, x=_x, y=0, z=0) sensors.append(sensor) desired_x_index += 1 if desired_x_index == len(desired_xs): break current_desired_x = desired_xs[desired_x_index] array = swprocess.Array1D(sensors, source) # array.to_file("60m_Xm_-5m.su") array.to_file("60m_Xm_-10m.su") ``` -------------------------------- ### Define Data File Paths Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Specifies the paths to the data files, grouped by source offset. It includes a check to ensure all files exist. ```python # Path (relative or full) to a folder containing the data files. Data files must be in either the SEG2 and/or SU data format. path_to_folder = "data/wghs/" # Files grouped by source offset. Each set may contain the location a single or multiple files. # By default the notebook assumes that multiple source offsets have been used (6 in this examples, set0 to set5) # with multiple shots per source offset (5 in this example). The files of a set will be stacked according # to the selected MASW workflow (selected next). set0 = [f"{path_to_folder}{x}.dat" for x in range(6, 11)] set1 = [f"{path_to_folder}{x}.dat" for x in range(11, 16)] set2 = [f"{path_to_folder}{x}.dat" for x in range(16, 21)] set3 = [f"{path_to_folder}{x}.dat" for x in range(26, 31)] set4 = [f"{path_to_folder}{x}.dat" for x in range(31, 36)] set5 = [f"{path_to_folder}{x}.dat" for x in range(36, 41)] fnames_set = [set0, set1, set2, set3, set4, set5] print("Summary:") for set_number, fnames in enumerate(fnames_set): print(f" set{set_number} includes {len(fnames)} files from {fnames[0]} to {fnames[-1]}") for fname in fnames: if not pathlib.Path(fname).exists(): raise FileNotFoundError(f" File {fname} in set{set_number} cannot be located. Check path.") ``` -------------------------------- ### Create SU from CSV Data (Example 1) Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_3/construction/create_su.ipynb This script reads CSV data, interpolates it, and saves it in SU format. It defines a range of desired x-coordinates and processes multiple CSV files to create sensor data. ```python dt = 0.001 desired_time = np.arange(0, 2, dt) # desired_xmin, desired_xmax, desired_dx = 5.05, 51.05, 2. # desired_xmin, desired_xmax, desired_dx = 10.05, 56.05, 2. desired_xmin, desired_xmax, desired_dx = 20.05, 66.05, 2. source = swprocess.Source(x=0.05, y=0, z=0) current_x, dx = 0.0, 0.05 current_desired_x = desired_xmin sensors = [] for setnumber in range(6): fname = f"set{str(setnumber).zfill(2)}.csv" with open(fname, "r") as f: ncols = f.readlines()[1].count(",") df = pd.read_csv(fname, usecols=np.arange(0, ncols), names=["time", *[x for x in np.arange(ncols)]], skiprows=2, skipinitialspace=True) xs = np.arange(ncols-1)*dx + current_x current_x = float(xs[-1]) + dx for col, _x in enumerate(xs): if np.abs(_x - current_desired_x) > dx/2: continue interpolator = interp1d(x=df["time"], y=df[col], kind="cubic") sensor = swprocess.Sensor1C(amplitude=np.array(interpolator(desired_time), dtype=np.float32), dt=dt, x=_x, y=0, z=0) sensors.append(sensor) current_desired_x += desired_dx if current_desired_x > desired_xmax: break if current_desired_x > desired_xmax: break array = swprocess.Array1D(sensors, source) # array.to_file("46m_2m_-5m.su") # array.to_file("46m_2m_-10m.su") array.to_file("46m_2m_-20m.su") ``` -------------------------------- ### Time Domain Trimming and Muting Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Configures trimming and muting for the time-domain records. Trimming is recommended, while muting is generally not advised. ```python # Trim record between the specified begin and end times (time in seconds). Trimming is recommended, however # it must be done carefully to avoid accidentally trimming signal, particularly for far offsets. mute, trim_begin, trim_end = True, 0, 0.5 # Mute portions of the time-domain record to isolate surface wave energy. No muting is recommended. # Mute method {"interactive"} and window_kwargs (see documenation for details). mute, method, window_kwargs = False, "interactive", {} ``` -------------------------------- ### Wavefield Transform Parameters Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Sets parameters for the wavefield transform, including the transform type, frequency range, velocity range, and weighting/steering for the 'fdbf' method. ```python # Wavefield transform {"fk", "slantstack", "phaseshift", "fdbf"}, "fdbf" is recommended. transform = "fdbf" # Minimum and maximum frequencies of interest (frequency in Hertz). fmin, fmax = 3, 100 # Selection of trial velocities (velocity in m/s) with minimum, maximum, number of steps, and space {"linear", "log"}. vmin, vmax, nvel, vspace = 100, 500, 400, "linear" # Weighting for "fdbf" {"sqrt", "invamp", "none"} (ignored for all other wavefield transforms). "sqrt" is recommended. fdbf_weighting = "sqrt" # Steering vector for "fdbf" {"cylindrical", "plane"} (ignored for all other wavefield transforms). "cylindrical" is recommended. fdbf_steering = "cylindrical" ``` -------------------------------- ### Execute MASW Workflow Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This cell is intended to perform the selected MASW workflow. No changes are typically required here, but the `settings_fname` variable can be modified for specific naming. ```python # Perform the selcted MASW workflow. No changes to this cell are required, however you may # wish to change the variable `settings_fname` to a more specific name for later reference. ``` -------------------------------- ### Import necessary libraries Source: https://github.com/jpvantassel/swprocess/blob/main/test/data/utils/create_mseed.ipynb Import datetime, numpy, and obspy. ```python import datetime import numpy as np import obspy ``` -------------------------------- ### Import necessary libraries Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_0/vis_model_0.ipynb Imports common libraries for numerical operations, plotting, data manipulation, and specific modules from swprepost, swprocess, and sigpropy. ```python import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import pandas as pd import swprepost import swprocess import sigpropy ``` -------------------------------- ### Plotting Result Placeholder Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This indicates that a figure object is generated by the plotting code. ```text
``` -------------------------------- ### Import necessary libraries Source: https://github.com/jpvantassel/swprocess/blob/main/examples/mam/mam_fk.ipynb Imports the required libraries for the script, including pathlib, matplotlib.pyplot, numpy, and swprocess. ```python import pathlib import matplotlib.pyplot as plt import numpy as np import swprocess ``` -------------------------------- ### Define input file paths Source: https://github.com/jpvantassel/swprocess/blob/main/examples/mam/mam_fk.ipynb Specifies a list of file paths for .max files to be processed. It includes checks to ensure each file exists. ```python # File(s) to import in the .max format produced using Geopsy and fk-style processing. fnames_set = [["data/fk/nz_wghs_c50_rtbf-3c.max"], # rayleigh & love ["data/fk/nz_wghs_c50_fk-vt.max"], # rayleigh only ["data/fk/nz_wghs_c50_hfk-vt.max"], # rayleigh only ["data/fk/nz_wghs_bigx_rtbf-3c.max"], # rayleigh & love ["data/fk/nz_wghs_bigx_fk-vt.max"], # rayleigh only ["data/fk/nz_wghs_bigx_hfk-vt.max"], # rayleigh only ] for set_number, fnames in enumerate(fnames_set): for fname in fnames: if not pathlib.Path(fname).exists(): raise FileNotFoundError(f"File {fname} in set{set_number} cannot be located. Check path.") ``` -------------------------------- ### Signal-to-Noise Ratio (SNR) Calculation Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Configures the calculation of the signal-to-noise ratio, including defining noise and signal windows and optional zero padding. ```python # Compute the records signal-to-noise ratio. snr = True # Define noise and signal windows being and end times (time in seconds). Negative time refers to pre-trigger record. noise_begin, noise_end = -0.5, 0. signal_begin, signal_end = 0., 0.5 # Zero pad the noise and signal records to achieve a specified frequency step. Padding with df=1 is recommended. pad_snr, df_snr = True, 1 ``` -------------------------------- ### Define plotting domains, colors, and labels Source: https://github.com/jpvantassel/swprocess/blob/main/examples/mam/mam_fk.ipynb Configures the domains for plotting dispersion data (frequency/velocity, wavelength/velocity), assigns colors to each dataset, and defines custom labels for them. ```python # Domains in which to plot the experimental dispersion data. domains = [ ["frequency", "velocity"], ["wavelength", "velocity"], ] # Colors one per `fnames_set` entry. Examples include: "dodgerblue", "darkblue", "tomato", "pink", "limegreen", "darkorange". # Colors may also be listed in hexidecimal. colors = ["dodgerblue", "darkblue", "tomato", "pink", "limegreen", "darkorange"] # Custom labels one per `fanems_set` entry. Note these will also be used for the output file suffixs. labels = ["c50-rtbf", "c50-fk", "c50-hfk", "bigx-rtbf", "bigx-fk", "bigx-hfk"] ``` -------------------------------- ### Write Experimental Dispersion Curve to .txt Source: https://github.com/jpvantassel/swprocess/blob/main/examples/stats/stats.ipynb Prepares and writes the experimental dispersion curve data to a .txt file. It handles different domain configurations (frequency-velocity or wavelength-velocity), calculates new mean and standard deviation values, and plots the statistics. It also creates a 'Target' object, sets a minimum covariance, and saves the data using `to_txt_dinver`. ```python fname = "nz_wghs_rayleigh.txt" version = "3.4.2" minimum_cov = 0.05 if xdomain == "frequency" and ydomain == "velocity": new_xx = np.array(xx) new_mean = np.array(mean) new_stddev = np.array(stddev) elif xdomain == "wavelength" and ydomain == "velocity": new_xx = mean/xx new_mean = np.array(mean) upper = swprepost.Curve(x=(mean+stddev)/xx, y=mean+stddev) lower = swprepost.Curve(x=(mean-stddev)/xx, y=mean-stddev) new_stddev = ((upper.resample(xx=new_xx, interp1d_kwargs=dict(fill_value="extrapolate"))[1] - new_mean) + (new_mean - lower.resample(xx=new_xx, interp1d_kwargs=dict(fill_value="extrapolate"))[1])) /2 else: raise NotImplementedError() %matplotlib inline fig, axs = main_suite.plot(xtype=xtype, ytype=ytype, plot_kwargs=dict(color=_colors, label=_labels)) for ax, _xtype, _ytype in zip(axs, xtype, ytype): if _xtype == "frequency" and _ytype == "velocity": main_suite.plot_statistics(ax=ax, xx=new_xx, mean=new_mean, stddev=new_stddev, errorbar_kwargs=dict(label="Exp. Disp. Data")) for index, _stddev in enumerate(new_stddev): if np.isnan(_stddev): new_stddev[index] = 0 target = swprepost.Target(frequency=new_xx, velocity=new_mean, velstd=new_stddev) target.setmincov(minimum_cov) ylabel = axs[0].get_ylabel() target.plot(ax=axs[0], errorbarkwargs=dict(color="r", label=f"Exp. Disp. Data (min COV={minimum_cov})")) axs[0].set_ylabel(ylabel) target.to_txt_dinver(fname=fname, version=version) axs[0].legend(bbox_to_anchor = (2.4, 0.5), loc="center left") # # manually set limits for first panel (frequency in this case) # axs[0].set_xlim(0.1, 50) # axs[0].set_ylim(0, 1000) # # manually set limits for second panel (wavelength in this case) # axs[1].set_xlim(0.1, 1000) # axs[1].set_ylim(0, 1000) plt.show() ``` -------------------------------- ### Create SU Array (Method 1) Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_0/construction/create_su.ipynb Generates an SU array by selecting sensors based on a defined range and interval, then writes it to a file. ```python dt = 0.001 desired_time = np.arange(0, 1.5, dt) # desired_xmin, desired_xmax, desired_dx = 5.05, 51.05, 2. # desired_xmin, desired_xmax, desired_dx = 10.05, 56.05, 2. desired_xmin, desired_xmax, desired_dx = 20.05, 66.05, 2. source = swprocess.Source(x=0.05, y=0, z=0) current_x, dx = 0.0, 0.05 current_desired_x = desired_xmin sensors = [] for setnumber in range(14): fname = f"set{str(setnumber).zfill(2)}.csv" with open(fname, "r") as f: ncols = f.readlines()[1].count(",") df = pd.read_csv(fname, usecols=np.arange(0, ncols), names=["time", *[x for x in np.arange(ncols)]], skiprows=2, skipinitialspace=True) xs = np.arange(ncols-1)*dx + current_x current_x = float(xs[-1]) + dx for col, _x in enumerate(xs): if np.abs(_x - current_desired_x) > dx/2: continue interpolator = interp1d(x=df["time"], y=df[col], kind="cubic") sensor = swprocess.Sensor1C(amplitude=np.array(interpolator(desired_time), dtype=np.float32), dt=dt, x=_x, y=0, z=0) sensors.append(sensor) current_desired_x += desired_dx if current_desired_x > desired_xmax: break if current_desired_x > desired_xmax: break array = swprocess.Array1D(sensors, source) # array.to_file("46m_2m_-5m.su") # array.to_file("46m_2m_-10m.su") array.to_file("46m_2m_-20m.su") ``` -------------------------------- ### Extract miniseed data Source: https://github.com/jpvantassel/swprocess/blob/main/examples/extract/extract.ipynb Extracts miniseed data based on a CSV file containing start and end times, specifying network, data directory, and file extension. ```python utils.extract_mseed("extract_startandend.csv", network="NW", data_dir="passive/", extension="miniseed") ``` -------------------------------- ### SNR Threshold Setting Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb Sets the minimum Signal-to-Noise Ratio (SNR) threshold. Frequencies with SNR below this value should be used cautiously. A minimum threshold of 10 dB (SNR of ~3.2) is recommended by Wood and Cox (2012). ```python # SNR threshold, frequencies with SNR below this value should be used cautiously. # Wood and Cox (2012) recommended a minimum threshold of 10 dB (or SNR of ~3.2). minimum_snr = 3.2 ``` -------------------------------- ### Create SU Array (Method 2) Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_0/construction/create_su.ipynb Generates an SU array by selecting sensors based on a predefined list of desired x-coordinates, then writes it to a file. ```python dt = 0.001 desired_time = np.arange(0, 1.5, dt) # desired_xs = [5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 55, 60, 65] desired_xs = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 55, 60, 65, 70] desired_xs = [ x + 0.05 for x in desired_xs] desired_x_index = 0 current_x, dx = 0.0, 0.05 current_desired_x = desired_xs[desired_x_index] source = swprocess.Source(x=0.05, y=0, z=0) sensors = [] for setnumber in range(14): fname = f"set{str(setnumber).zfill(2)}.csv" with open(fname, "r") as f: ncols = f.readlines()[1].count(",") df = pd.read_csv(fname, usecols=np.arange(0, ncols), names=["time", *[x for x in np.arange(ncols)]], skiprows=2, skipinitialspace=True) xs = np.arange(ncols-1)*dx + current_x current_x = float(xs[-1]) + dx for col, _x in enumerate(xs): if np.abs(_x - current_desired_x) > dx/2: continue interpolator = interp1d(x=df["time"], y=df[col], kind="cubic") sensor = swprocess.Sensor1C(amplitude=np.array(interpolator(desired_time), dtype=np.float32), dt=dt, x=_x, y=0, z=0) sensors.append(sensor) desired_x_index += 1 if desired_x_index == len(desired_xs): break current_desired_x = desired_xs[desired_x_index] array = swprocess.Array1D(sensors, source) # array.to_file("60m_Xm_-5m.su") array.to_file("60m_Xm_-10m.su") ``` -------------------------------- ### Save Peaks to File Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This code snippet saves the identified peaks to JSON files. It allows for saving all peaks to a single file or individual files based on the 'one_file_per_fname_set' flag. It also handles saving figures to PNG files. ```python prefix = "nz_wghs_rayleigh" one_file_per_fname_set = False names = names if names is not None else [f"{x.array.source.x}" for x in wavefieldtransforms] if len(names) != len(wavefieldtransforms): raise ValueError(f"len(fnames_set)={len(fnames_set)} must equal len(names)={len(names)}.") append = False for name, wavefieldtransform in zip(names, wavefieldtransforms): peak = swprocess.peaks.Peaks(wavefieldtransform.frequencies, wavefieldtransform.find_peak_power(by="frequency-maximum"), identifier=name) fname = f"{prefix}_{name}m.json" if one_file_per_fname_set else f"{prefix}_masw.json" peak.to_json(fname=fname, append=append) append = False if one_file_per_fname_set else True ``` ```python for name, wavefieldtransform, figure in zip(names, wavefieldtransforms, figures): figure.savefig(f"{prefix}_{name}m.png", dpi=300, bbox_inches="tight") ``` -------------------------------- ### Import Libraries Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_1/construction/create_su.ipynb Imports necessary libraries for data manipulation and processing. ```python import numpy as np import pandas as pd from scipy.interpolate import interp1d import swprocess ``` -------------------------------- ### MASW Result Visualization Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This code snippet iterates through the computed wavefield transforms and generates plots for each, including the array, waterfall timeseries, SNR, and dispersion image. It also handles display options like wavelength resolution limit and near-field criteria. ```python %matplotlib inline figures = [] for wavefieldtransform in wavefieldtransforms: fig = plt.figure(figsize=(6,6), dpi=150) gs = GridSpec(nrows=4, ncols=4, height_ratios=(1.7, 0.5, 1.5, 4), width_ratios=(1, 0.3, 1, 0.05), hspace=0.2, wspace=0.1) ax0 = fig.add_subplot(gs[0, :]) ax1 = fig.add_subplot(gs[2:4, 0]) ax2 = fig.add_subplot(gs[2, 2]) ax3 = fig.add_subplot(gs[3, 2]) ax4 = fig.add_subplot(gs[3, 3]) # Array wavefieldtransform.array.plot(ax=ax0) ax0.set_yticks([]) ax0.legend(ncol=2) # Timeseries wavefieldtransform.array.waterfall(ax=ax1, amplitude_detrend=False, amplitude_normalization="each") if trim: ax1.set_ylim((trim_end, trim_begin)) # Signal-to-Noise Ratio wavefieldtransform.plot_snr(ax=ax2, plot_kwargs=dict(color="black", label="SNR")) xlim = ax2.get_xlim() ax2.plot(xlim, [minimum_snr]*2, lw=2, color="red", label="10 dB") ax2.set_xlim(xlim) ax2.set_xticklabels([]) ax2.set_xlabel("") ax2.set_ylabel("SNR") ax2.set_yscale("log") ax2.legend(loc="upper left") # Dispersion Image nearfield = number_of_array_center_distances if display_nearfield else None wavefieldtransform.plot(fig=fig, ax=ax3, cax=ax4, normalization=wavefield_normalization, nearfield=nearfield) xlim = ax3.get_xlim() ylim = ax3.get_ylim() if display_lambda_res: kres_format = dict(linewidth=1.5, color="#000000", linestyle="--") kres = wavefieldtransform.array.kres kvelocity = 2*np.pi*wavefieldtransform.frequencies / kres ax3.plot(wavefieldtransform.frequencies, kvelocity, label=r"$\lambda_{a,min}$" + f"={np.round(2*np.pi/kres,2)} m", **kres_format) ax3.legend(loc="upper right") ax3.set_xlim(xlim) ax2.set_xlim(xlim) ax3.set_ylim(ylim) ax2.set_xscale("log") ax2.set_xticks([]) ax3.set_xscale("log") figures.append(fig) plt.show() print("\n\n\n") ``` -------------------------------- ### Plot Experimental Dispersion Data Source: https://github.com/jpvantassel/swprocess/blob/main/examples/masw/masw.ipynb This code snippet plots the experimental dispersion data for different domains (frequency vs. velocity, wavelength vs. velocity). It generates a plot with labels and colors corresponding to the wavefield transforms and saves the plot to a file. ```python %matplotlib inline xtype = [x for x, _ in domains] ytype = [y for _, y in domains] labels = names if names is not None else [f"{x.array.source.x:.1f} m" for x in wavefieldtransforms] cmap = plt.get_cmap(name="Paired", lut=len(labels)) colors = [mpl.colors.to_hex(cmap.colors[x]) for x in range(len(labels))] if len(fnames_set) != len(colors) or len(fnames_set) != len(labels): raise IndexError(f"fnames_set, colors, and labels must be the same length.") fig, axs = plt.subplots(ncols=len(xtype), figsize=(6,3), dpi=150, gridspec_kw=dict(wspace=0.4)) for wavefieldtransform, color, label in zip(wavefieldtransforms, colors, labels): peak = swprocess.peaks.Peaks(wavefieldtransform.frequencies, wavefieldtransform.find_peak_power(by="frequency-maximum"), identifier=label) peaksuite = swprocess.PeaksSuite.from_peaks([peak]) peaksuite.plot(xtype=xtype, ax=axs, ytype=ytype, plot_kwargs=dict(color=color, label=label)) axs[-1].legend(bbox_to_anchor = (1.1, 0.5), loc="center left") plt.show() ``` -------------------------------- ### Create SU Array (Method 2) Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_2/construction/create_su.ipynb Generates an SU array by selecting sensors based on a predefined list of desired x-coordinates. ```python dt = 0.001 desired_time = np.arange(0, 1.5, dt) # desired_xs = [5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 55, 60, 65] desired_xs = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 55, 60, 65, 70] desired_xs = [ x + 0.05 for x in desired_xs] desired_x_index = 0 current_x, dx = 0.0, 0.05 current_desired_x = desired_xs[desired_x_index] source = swprocess.Source(x=0.05, y=0, z=0) sensors = [] for setnumber in range(8): fname = f"set{str(setnumber).zfill(2)}.csv" with open(fname, "r") as f: ncols = f.readlines()[1].count(",") df = pd.read_csv(fname, usecols=np.arange(0, ncols), names=["time", *[x for x in np.arange(ncols)]], skiprows=2, skipinitialspace=True) xs = np.arange(ncols-1)*dx + current_x current_x = float(xs[-1]) + dx for col, _x in enumerate(xs): if np.abs(_x - current_desired_x) > dx/2: continue interpolator = interp1d(x=df["time"], y=df[col], kind="cubic") sensor = swprocess.Sensor1C(amplitude=np.array(interpolator(desired_time), dtype=np.float32), dt=dt, x=_x, y=0, z=0) sensors.append(sensor) desired_x_index += 1 if desired_x_index == len(desired_xs): break current_desired_x = desired_xs[desired_x_index] array = swprocess.Array1D(sensors, source) # array.to_file("60m_Xm_-5m.su") array.to_file("60m_Xm_-10m.su") ``` -------------------------------- ### Create SU Array (Method 1) Source: https://github.com/jpvantassel/swprocess/blob/main/benchmarks/model_2/construction/create_su.ipynb Generates an SU array by selecting sensors within a specified x-range and spacing. ```python dt = 0.001 desired_time = np.arange(0, 1.5, dt) # desired_xmin, desired_xmax, desired_dx = 5.05, 51.05, 2. # desired_xmin, desired_xmax, desired_dx = 10.05, 56.05, 2. desired_xmin, desired_xmax, desired_dx = 20.05, 66.05, 2. source = swprocess.Source(x=0.05, y=0, z=0) current_x, dx = 0.0, 0.05 current_desired_x = desired_xmin sensors = [] for setnumber in range(8): fname = f"set{str(setnumber).zfill(2)}.csv" with open(fname, "r") as f: ncols = f.readlines()[1].count(",") df = pd.read_csv(fname, usecols=np.arange(0, ncols), names=["time", *[x for x in np.arange(ncols)]], skiprows=2, skipinitialspace=True) xs = np.arange(ncols-1)*dx + current_x current_x = float(xs[-1]) + dx for col, _x in enumerate(xs): if np.abs(_x - current_desired_x) > dx/2: continue interpolator = interp1d(x=df["time"], y=df[col], kind="cubic") sensor = swprocess.Sensor1C(amplitude=np.array(interpolator(desired_time), dtype=np.float32), dt=dt, x=_x, y=0, z=0) sensors.append(sensor) current_desired_x += desired_dx if current_desired_x > desired_xmax: break if current_desired_x > desired_xmax: break array = swprocess.Array1D(sensors, source) # array.to_file("46m_2m_-5m.su") # array.to_file("46m_2m_-10m.su") array.to_file("46m_2m_-20m.su") ```