GeoFWI is a large 2D velocity model dataset for deep-learning-based FWI benchmarking. This project is inspired by the OpenFWI project (https://sites.google.com/site/youzuolin044/openfwi) but includes more realistic geological models (e.g., folding layers, faults, and salt bodies).
Li, C., Shen, Y., Fomel, S., Waheed, U.B., Savvaidis, A. and Chen, Y., 2026. GeoFWI: A large velocity model data set for benchmarking full waveform inversion using deep learning. Journal of Geophysical Research: Machine Learning and Computation, 3(2), p.e2025JH001037.
BibTeX:
@Article{geofwi,
author={Chao Li and Yiran Shen and Sergey Fomel and Umair Bin Waheed and Alexandros Savvaidis and Yangkang Chen},
title = {GeoFWI: A large velocity model dataset for benchmarking full waveform inversion using deep learning},
journal={Journal of Geophysical Research: Machine Learning and Computation},
year=2026,
volume=3,
number=1,
issue=1,
pages={e2025JH001037},
doi={10.1029/2025JH001037},
}
GeoFWI provides a simple Python interface for automatically downloading, loading, exploring, and visualizing the benchmark velocity models.
Install GeoFWI directly from GitHub:
pip install git+https://github.com/aaspip/geofwi.gitor using Pypi
pip install geofwi
Install the optional visualization and download-progress dependencies:
pip install numpy matplotlib tqdmDownload the GeoFWI dataset from the official Zenodo repository:
import geofwi
# Automatically download and extract the GeoFWI dataset
path = geofwi.download(progress=True)
print("GeoFWI dataset location:", path)The downloader automatically:
- Selects a local storage directory (
~/Datasets/GeoFWIby default on macOS/Linux). - Downloads the dataset from Zenodo with a live progress bar.
- Verifies downloaded files using checksums.
- Skips downloading files that already exist and pass verification.
- Extracts ZIP archives automatically.
You can also specify a custom storage directory:
path = geofwi.download(
path="~/Datasets/GeoFWI",
progress=True,
extract=True,
)For large datasets, an external SSD can be used:
path = geofwi.download(
path="/Volumes/ExternalSSD/GeoFWI",
progress=True,
)Inspect the downloaded dataset:
import geofwi
# Download the dataset if necessary
path = geofwi.download()
# Display dataset information
geofwi.info(path)
# Find available NumPy arrays
arrays = geofwi.find_arrays(path)
print("Available arrays:", arrays)Load a velocity-model array:
# Select an available NumPy array
velocity = geofwi.load(arrays[1])
print("Velocity array shape:", velocity.shape)GeoFWI supports memory-efficient loading of large NumPy arrays, avoiding unnecessary memory consumption.
Visualize representative velocity models:
import geofwi
# Automatically download the dataset
path = geofwi.download()
# Visualize the dataset
geofwi.visualize(path)Alternatively, load the data and visualize it using Matplotlib:
import matplotlib.pyplot as plt
import geofwi
path = geofwi.download()
arrays = geofwi.find_arrays(path)
velocity = geofwi.load(arrays[1])
# Display the first velocity model
plt.figure(figsize=(6, 5))
plt.imshow(velocity[0], cmap="jet", aspect="auto")
plt.colorbar(label="Velocity")
plt.title("GeoFWI Velocity Model")
plt.xlabel("Horizontal grid index")
plt.ylabel("Depth grid index")
plt.tight_layout()
plt.show()The following example demonstrates the complete workflow:
import geofwi
# Step 1: Download the dataset (skips existing verified files)
path = geofwi.download(progress=True)
# Step 2: Inspect dataset information
geofwi.info(path)
# Step 3: Find and load a velocity-model array
arrays = geofwi.find_arrays(path)
velocity = geofwi.load(arrays[1])
# Step 4: Visualize representative models
geofwi.visualize(path)# List available files on Zenodo
files = geofwi.available_files()
# Download a specific file
path = geofwi.download(filename=files[0]["name"])
# Download without extracting the ZIP archive
archive = geofwi.download(extract=False)
# Force a fresh download
path = geofwi.download(force=True)
# Disable the download progress bar
path = geofwi.download(progress=False)
# Show the default data directory
print(geofwi.data_dir())For additional examples, see the examples directory.
For dataset details, visit the GeoFWI Zenodo record.
Here is a sneak peek of some of the structures and initial results of the GeoFWI project.
Figure below shows a few good/bad predictions using the data-driven DL-based method (SeisInvNet) detailed in Li et al., 2020, Deep learning Inversion of Seismic Data, IEEE TGRS.
An example below shows a few shot gathers simulated from the velocity models on top.
Read the GeoFWI data and check its dimension
import geofwi
path = geofwi.download()
arrays = geofwi.find_arrays(path)
sizes=geofwi.load(arrays[0]) #load geofwi-size-layer-fault-salt-1-10.npy
geofwi=geofwi.load(arrays[1])
print('geofwi.shape',geofwi.shape)
print('NO of samples in GeoFWI is ',geofwi.shape[0])
print('Each sample has a dimension of %d * %d '%(geofwi.shape[1],geofwi.shape[2]))
The output will be like
geofwi.shape (49476, 100, 100)
NO of samples in GeoFWI is 49476
Each sample has a dimension of 100 * 100
Get some statistics from GeoFWI
import geofwi
path = geofwi.download()
sizes = geofwi.load(geofwi.find_arrays(path)[0])
print('length of sizes:',len(sizes))
print('Two-layer folding model: %d samples'%sizes[0])
print('Two-layer fault model: %d samples'%sizes[1])
print('Two-layer salt model: %d samples'%sizes[2])
print('Three-layer folding model: %d samples'%sizes[3])
print('Three-layer fault model: %d samples'%sizes[4])
print('Three-layer salt model: %d samples'%sizes[5])
print('Four-layer folding model: %d samples'%sizes[6])
print('Four-layer fault model: %d samples'%sizes[7])
print('Four-layer salt model: %d samples'%sizes[8])
print('Five-layer folding model: %d samples'%sizes[9])
print('Five-layer fault model: %d samples'%sizes[10])
print('Five-layer salt model: %d samples'%sizes[11])
print('Six-layer folding model: %d samples'%sizes[12])
print('Six-layer fault model: %d samples'%sizes[13])
print('Six-layer salt model: %d samples'%sizes[14])
print('Seven-layer folding model: %d samples'%sizes[15])
print('Seven-layer fault model: %d samples'%sizes[16])
print('Seven-layer salt model: %d samples'%sizes[17])
print('Eight-layer folding model: %d samples'%sizes[18])
print('Eight-layer fault model: %d samples'%sizes[19])
print('Eight-layer salt model: %d samples'%sizes[20])
print('Nine-layer folding model: %d samples'%sizes[21])
print('Nine-layer fault model: %d samples'%sizes[22])
print('Nine-layer salt model: %d samples'%sizes[23])
print('Ten-layer folding model: %d samples'%sizes[24])
print('Ten-layer fault model: %d samples'%sizes[25])
print('Ten-layer salt model: %d samples'%sizes[26])
print('Eleven-layer folding model: %d samples'%sizes[27])
print('Eleven-layer fault model: %d samples'%sizes[28])
print('Eleven-layer salt model: %d samples'%sizes[29])
The output will be like
Two-layer folding model: 2826 samples
Two-layer fault model: 2754 samples
Two-layer salt model: 1888 samples
Three-layer folding model: 2649 samples
Three-layer fault model: 2473 samples
Three-layer salt model: 1758 samples
Four-layer folding model: 2491 samples
Four-layer fault model: 2161 samples
Four-layer salt model: 1674 samples
Five-layer folding model: 2245 samples
Five-layer fault model: 1948 samples
Five-layer salt model: 1508 samples
Six-layer folding model: 1974 samples
Six-layer fault model: 1653 samples
Six-layer salt model: 1340 samples
Seven-layer folding model: 1797 samples
Seven-layer fault model: 1478 samples
Seven-layer salt model: 1226 samples
Eight-layer folding model: 1635 samples
Eight-layer fault model: 1325 samples
Eight-layer salt model: 1076 samples
Nine-layer folding model: 1453 samples
Nine-layer fault model: 1117 samples
Nine-layer salt model: 976 samples
Ten-layer folding model: 1309 samples
Ten-layer fault model: 1000 samples
Ten-layer salt model: 881 samples
Eleven-layer folding model: 1200 samples
Eleven-layer fault model: 829 samples
Eleven-layer salt model: 832 samples
Extract a few five-layer fault models from GeoFWI
import geofwi
path = geofwi.download()
sizes=geofwi.load(geofwi.find_arrays(path)[0])
geofwi=geofwi.load(geofwi.find_arrays(path)[1])
#first index for five-layer fault models,
ind=sum(sizes[0:10])+1 #refer to last example for checking the index for each type of model
no=8 #make it 4,8,16
import matplotlib.pyplot as plt
fig=plt.figure(figsize=(16, 8))
for ii in range(no):
plt.subplot(2,int(no/2),ii+1)
plt.imshow(geofwi[ind+ii,:,:],clim=[1500,4000]);
plt.colorbar(orientation='horizontal',cax=fig.add_axes([0.37,0.07,0.3,0.01]),shrink=1,label='Velocity (m/s)');
plt.show()
Extract a few five-layer salt models from GeoFWI
import numpy as np
import geofwi
path = geofwi.download()
sizes=np.load(str(path)+'/geofwi'+'/geofwi-size-layer-fault-salt-1-10.npy')
geofwi=np.load(str(path)+'/geofwi'+'/geofwi.npy')
#first index for five-layer salt models,
ind=sum(sizes[0:11])+1 #refer to last example for checking the index for each type of model
no=8 #make it 4,8,16
import matplotlib.pyplot as plt
fig=plt.figure(figsize=(16, 8))
for ii in range(no):
plt.subplot(2,int(no/2),ii+1)
plt.imshow(geofwi[ind+ii,:,:],clim=[1500,4000]);
plt.colorbar(orientation='horizontal',cax=fig.add_axes([0.37,0.07,0.3,0.01]),shrink=1,label='Velocity (m/s)');
plt.show()
Extract one velocity with a certain number of layers and a specific type of fold/fault/salt.
import geofwi
path = geofwi.download()
from geofwi import get_vel
lay=5;mod='salt';
vel=get_vel(layer=lay,mode=mod,datapath=str(path)+'/geofwi',indx=0) #indx=0 means the first sample of this type
import matplotlib.pyplot as plt
plt.imshow(vel,cmap=plt.jet());plt.colorbar(label='Velocity (m/s)');
plt.xlabel('X sample'); plt.ylabel('Z sample');
plt.title("%d-layer %s model"%(lay,mod))
plt.show()
Here is a benchmarking test using different loss functions for the data-driven FWI (we use SeisInvNet, Li et al., 2020).
Here is a benchmarking test using different diffusion model scenarios. Generated by benchmark/DiffusionModel/test_compare_three_DMs.py
The development team welcomes voluntary contributions from any open-source enthusiast.
If you want to make contribution to this project, feel free to contact the development team.
Regarding any questions, bugs, developments, collaborations, please contact
Yangkang Chen
chenyk2016@gmail.com
The gallery figures of the geofwi project can be found at https://github.com/aaspip/gallery/tree/main/geofwi All figures in the gallery directory can be reproduced by their corresponding DEMO scripts in the "demo" directory. These gallery figures are also presented below.
Generated by demos/test_quick_visualize.py
First scenario: randomly chosen 25 samples from the whole GeoFWI dataset

Second scenario: the first 25 samples from the whole GeoFWI dataset

Third scenario: the last 25 samples from the whole GeoFWI dataset

Fourth scenario: the last 25 samples in the "11-layer fault category" (e.g., sizes=np.load('data/geofwi-size-layer-fault-salt-1-10.npy'); index is chosen from 49476-sizes[-1]-1 to sizes[-1]-25) from the whole GeoFWI dataset

Below is an example for a simple FWI example using GeoFWI velocity samples. It requires the pywave package (https://github.com/chenyk1990/pywave) for modeling and inversion.
Generated by demos/test_fwi_pywave.py
Below are two examples for applying the diffusion model trained from the GeoFWI dataset to a completely independent complex model (Marmousi or Overthrust). It can be observed that 1) L2-only FWI suffers from cycle skipping and artifacts; 2) L2 + TV FWI improves continuity but over-smooths the structure; 3) L2 + SGDS (Split Gibbs Diffusion Sampling) FWI recovers high-resolution features like salt bodies and faults, closely matching the ground truth.
Below is a benchmark example comparing SeisInvNet with another data-driven FWI approach (InversionNet, Wu and Lin, 2019). The benchmark is conducted by using 2000 samples from the GeoFWI dataset for training and 500 samples for independent testing. It is clear that SeisInvNet performs better than InversionNet on the GeoFWI dataset, e.g., fewer spurious structures (like the fake salts on the third row), and more accurately recovered layered structures.
GeoFWI-Augmented extends the original GeoFWI dataset with 20,000 procedurally generated salt-bearing velocity models, comprising 10,000 examples from each of two new structural families. The first family contains snaking salt diapirs constructed from connected ellipsoidal segments with curved stems and multiple salt heads, while the second contains broad, irregular salt bodies formed by combining three to six randomly positioned, scaled, and rotated ellipsoids. Each salt body is embedded in a randomized folded sedimentary background containing one to seven layers. Three dimensional 100*100*100 models are generated first, and two dimensional 100*100 slices are then extracted along both horizontal directions. Only slices containing a meaningful high velocity salt feature are retained, producing a final augmented dataset of 69,476 velocity models.
The above two figures can be reproduced using
import geofwi;path = geofwi.download()
from geofwi import get_vels, plot_four
vels=get_vels([49680,50388,51482,51729],datapath=str(path)+'/geofwi')
plot_four(vels, title="Four Samples in Augmented GeoFWI", figname="SaltAug.png")
import geofwi;path = geofwi.download()
from geofwi import get_vels, plot_four
vels=get_vels([54895,55047,55390,57247],datapath=str(path)+'/geofwi')
plot_four(vels, title="Four Samples in Augmented GeoFWI", figname="Salt2Aug.png")
GEOFWI developing team, 2024-present
MIT License












