seiscode-models 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- seiscode_models/__init__.py +42 -0
- seiscode_models/models/__init__.py +82 -0
- seiscode_models/models/geological.py +92 -0
- seiscode_models/models/primitives.py +82 -0
- seiscode_models/utils/__init__.py +0 -0
- seiscode_models/utils/dtypes.py +45 -0
- seiscode_models/utils/io.py +68 -0
- seiscode_models/utils/validation.py +32 -0
- seiscode_models/utils/visualization.py +39 -0
- seiscode_models-0.1.0.dist-info/METADATA +31 -0
- seiscode_models-0.1.0.dist-info/RECORD +14 -0
- seiscode_models-0.1.0.dist-info/WHEEL +5 -0
- seiscode_models-0.1.0.dist-info/licenses/LICENSE +21 -0
- seiscode_models-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""SeisCode Models: Synthetic subsurface velocity model generators."""
|
|
2
|
+
from .models.primitives import (
|
|
3
|
+
gradient_background,
|
|
4
|
+
add_gaussian_anomaly,
|
|
5
|
+
add_fault_offset,
|
|
6
|
+
)
|
|
7
|
+
from .models.geological import (
|
|
8
|
+
dipping_layers,
|
|
9
|
+
sinusoidal_interface,
|
|
10
|
+
faulted_layers,
|
|
11
|
+
checkerboard,
|
|
12
|
+
salt_dome,
|
|
13
|
+
low_velocity_layer,
|
|
14
|
+
heterogeneous_anomaly_model
|
|
15
|
+
)
|
|
16
|
+
from .utils.dtypes import parse_dtype
|
|
17
|
+
from .utils.validation import validate_model
|
|
18
|
+
from .utils.io import (
|
|
19
|
+
export_model,
|
|
20
|
+
load_model
|
|
21
|
+
)
|
|
22
|
+
from .utils.visualization import quick_view
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"gradient_background",
|
|
28
|
+
"add_gaussian_anomaly",
|
|
29
|
+
"add_fault_offset",
|
|
30
|
+
"parse_dtype",
|
|
31
|
+
"validate_model",
|
|
32
|
+
"dipping_layers",
|
|
33
|
+
"sinusoidal_interface",
|
|
34
|
+
"faulted_layers",
|
|
35
|
+
"checkerboard",
|
|
36
|
+
"salt_dome",
|
|
37
|
+
"low_velocity_layer",
|
|
38
|
+
"heterogeneous_anomaly_model",
|
|
39
|
+
"export_model",
|
|
40
|
+
"load_model",
|
|
41
|
+
"quick_view"
|
|
42
|
+
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Primitive building blocks for subsurface velocity models with dtype support."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from ..utils.dtypes import parse_dtype
|
|
4
|
+
|
|
5
|
+
def gradient_background(nx: int, nz: int, v0: float = 1500.0, v1: float = 4500.0, dtype=np.float32) -> np.ndarray:
|
|
6
|
+
"""Generate a linear velocity gradient with depth.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
nx : int
|
|
11
|
+
Number of cells along x (horizontal direction).
|
|
12
|
+
nz : int
|
|
13
|
+
Number of cells along z (vertical direction, positive downward).
|
|
14
|
+
v0 : float, optional
|
|
15
|
+
Velocity at the surface, in m/s. Default is 1500.0.
|
|
16
|
+
v1 : float, optional
|
|
17
|
+
Velocity at the bottom of the model, in m/s. Default is 4500.0.
|
|
18
|
+
dtype : str or np.dtype, optional
|
|
19
|
+
Data type precision ('f32', 'f64', etc.). Default is np.float32.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
ndarray (nx, nz)
|
|
24
|
+
Velocity field in m/s with the requested dtype.
|
|
25
|
+
"""
|
|
26
|
+
dt = parse_dtype(dtype)
|
|
27
|
+
z = np.linspace(0.0, 1.0, nz, dtype=dt)
|
|
28
|
+
v_col = v0 + (v1 - v0) * z
|
|
29
|
+
return np.broadcast_to(v_col, (nx, nz)).copy()
|
|
30
|
+
|
|
31
|
+
def add_gaussian_anomaly(v: np.ndarray, cx: float, cz: float, sigma_x: float, sigma_z: float, dv: float, vmin: float = 200.0, vmax: float = 8000.0) -> np.ndarray:
|
|
32
|
+
"""Add a Gaussian velocity anomaly to an existing model.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
v : ndarray (nx, nz)
|
|
37
|
+
Velocity field to modify (not modified in place).
|
|
38
|
+
cx, cz : float
|
|
39
|
+
Center position along x and z, in cells.
|
|
40
|
+
sigma_x, sigma_z : float
|
|
41
|
+
Gaussian standard deviation along x and z, in cells.
|
|
42
|
+
dv : float
|
|
43
|
+
Amplitude of the anomaly at the center, in m/s.
|
|
44
|
+
vmin, vmax : float, optional
|
|
45
|
+
Clipping velocity bounds, in m/s.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
ndarray (nx, nz)
|
|
50
|
+
Copy of v with the anomaly added, preserving dtype and clipped.
|
|
51
|
+
"""
|
|
52
|
+
nx, nz = v.shape
|
|
53
|
+
dt = v.dtype
|
|
54
|
+
ii = np.arange(nx, dtype=dt)[:, None]
|
|
55
|
+
jj = np.arange(nz, dtype=dt)[None, :]
|
|
56
|
+
g = dv * np.exp(-((ii - cx) ** 2 / (2 * sigma_x ** 2) + (jj - cz) ** 2 / (2 * sigma_z ** 2)))
|
|
57
|
+
return np.clip(v + g, vmin, vmax).astype(dt)
|
|
58
|
+
|
|
59
|
+
def add_fault_offset(v: np.ndarray, ix_fault: int, throw: int) -> np.ndarray:
|
|
60
|
+
"""Shift layers on either side of a vertical fault.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
v : ndarray (nx, nz)
|
|
65
|
+
Stratified velocity field to shift.
|
|
66
|
+
ix_fault : int
|
|
67
|
+
Column index of the fault.
|
|
68
|
+
throw : int
|
|
69
|
+
Fault throw in cells along z.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
ndarray (nx, nz)
|
|
74
|
+
Shifted field preserving original dtype.
|
|
75
|
+
"""
|
|
76
|
+
out = v.copy()
|
|
77
|
+
left, right = slice(None, ix_fault), slice(ix_fault, None)
|
|
78
|
+
if throw >= 0:
|
|
79
|
+
out[left, throw:] = v[left, :out.shape[1] - throw]
|
|
80
|
+
else:
|
|
81
|
+
out[right, :throw] = v[right, -throw:]
|
|
82
|
+
return out
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Geological velocity models supporting f32/f64 precision."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from .primitives import gradient_background, add_gaussian_anomaly, add_fault_offset
|
|
4
|
+
from ..utils.dtypes import parse_dtype
|
|
5
|
+
|
|
6
|
+
def dipping_layers(nx: int = 201, nz: int = 51, dx: float = 1.0, dz: float = 1.0,
|
|
7
|
+
v: tuple = (1800.0, 2400.0, 3200.0, 4200.0), dip_deg: float = 8.0, dtype=np.float32) -> np.ndarray:
|
|
8
|
+
"""Generate dipping planar layers with the same dip angle."""
|
|
9
|
+
dt = parse_dtype(dtype)
|
|
10
|
+
v_arr = np.asarray(v, dtype=dt)
|
|
11
|
+
x = np.arange(nx, dtype=dt) * dx
|
|
12
|
+
dip = np.deg2rad(dip_deg)
|
|
13
|
+
v_out = np.full((nx, nz), v_arr[-1], dtype=dt)
|
|
14
|
+
depths = [0.0]
|
|
15
|
+
for k in range(1, len(v_arr)):
|
|
16
|
+
depths.append(depths[-1] + 10.0 * k)
|
|
17
|
+
for k in range(len(v_arr) - 2, -1, -1):
|
|
18
|
+
z_int = depths[k] + (x - x.mean()) * np.tan(dip)
|
|
19
|
+
k_int = np.clip((z_int / dz).astype(int), 0, nz - 1)
|
|
20
|
+
for i in range(nx):
|
|
21
|
+
v_out[i, :k_int[i]] = v_arr[k]
|
|
22
|
+
return v_out
|
|
23
|
+
|
|
24
|
+
def sinusoidal_interface(nx: int = 201, nz: int = 51, period: float = 100.0, amplitude: float = 8.0,
|
|
25
|
+
v_top: float = 2000.0, v_bottom: float = 3500.0, dtype=np.float32) -> np.ndarray:
|
|
26
|
+
"""Generate two layers separated by a sinusoidal interface."""
|
|
27
|
+
dt = parse_dtype(dtype)
|
|
28
|
+
x = np.arange(nx, dtype=dt)
|
|
29
|
+
z_int = nz * 0.45 + amplitude * np.sin(2 * np.pi * x / period)
|
|
30
|
+
k_int = z_int.astype(int)
|
|
31
|
+
v = np.full((nx, nz), v_bottom, dtype=dt)
|
|
32
|
+
for i in range(nx):
|
|
33
|
+
v[i, :k_int[i]] = v_top
|
|
34
|
+
return v
|
|
35
|
+
|
|
36
|
+
def faulted_layers(nx: int = 201, nz: int = 51, ix_fault: int = 120, throw: int = 8,
|
|
37
|
+
v: tuple = (1800.0, 2400.0, 3200.0, 4200.0), dtype=np.float32) -> np.ndarray:
|
|
38
|
+
"""Generate stratigraphic layers with a normal fault."""
|
|
39
|
+
dt = parse_dtype(dtype)
|
|
40
|
+
base = np.zeros((nx, nz), dtype=dt)
|
|
41
|
+
bounds = [0, 12, 28, 40, nz]
|
|
42
|
+
vs = np.asarray(v, dtype=dt)
|
|
43
|
+
for k in range(4):
|
|
44
|
+
base[:, bounds[k]:bounds[k + 1]] = vs[k]
|
|
45
|
+
return add_fault_offset(base, ix_fault, throw).astype(dt)
|
|
46
|
+
|
|
47
|
+
def checkerboard(nx: int = 201, nz: int = 51, cell: int = 20, v0: float = 3000.0,
|
|
48
|
+
dv: float = 600.0, dtype=np.float32) -> np.ndarray:
|
|
49
|
+
"""Generate a checkerboard pattern for resolution testing."""
|
|
50
|
+
dt = parse_dtype(dtype)
|
|
51
|
+
ii = np.arange(nx, dtype=dt)[:, None]
|
|
52
|
+
jj = np.arange(nz, dtype=dt)[None, :]
|
|
53
|
+
pat = ((ii // cell) + (jj // cell)) % 2
|
|
54
|
+
return np.where(pat == 0, v0 + dv, v0 - dv).astype(dt)
|
|
55
|
+
|
|
56
|
+
def salt_dome(nx: int = 201, nz: int = 51, cx: int = 140, width: float = 40.0,
|
|
57
|
+
v_salt: float = 4500.0, v_sed: float = 2500.0, dtype=np.float32) -> np.ndarray:
|
|
58
|
+
"""Generate a salt dome model with semi-elliptical shape."""
|
|
59
|
+
dt = parse_dtype(dtype)
|
|
60
|
+
v = gradient_background(nx, nz, v_sed, 4000.0, dtype=dt)
|
|
61
|
+
ii = np.arange(nx, dtype=dt)[:, None]
|
|
62
|
+
jj = np.arange(nz, dtype=dt)[None, :]
|
|
63
|
+
r = ((ii - cx) / width) ** 2 + np.clip((nz * 0.9 - jj) / (nz * 0.9), 0, None) ** 2
|
|
64
|
+
v[r < 1.0] = v_salt
|
|
65
|
+
return v
|
|
66
|
+
|
|
67
|
+
def low_velocity_layer(nx: int = 201, nz: int = 51, k0: int = 20, thickness: int = 8,
|
|
68
|
+
v_slow: float = 1200.0, dtype=np.float32) -> np.ndarray:
|
|
69
|
+
"""Generate a model with a buried low-velocity channel."""
|
|
70
|
+
dt = parse_dtype(dtype)
|
|
71
|
+
v = gradient_background(nx, nz, 1800.0, 4500.0, dtype=dt)
|
|
72
|
+
v[:, k0:k0 + thickness] = v_slow
|
|
73
|
+
return v
|
|
74
|
+
|
|
75
|
+
def heterogeneous_anomaly_model(nx: int = 384, nz: int = 122, seed: int = 7, dtype=np.float32) -> np.ndarray:
|
|
76
|
+
"""Generate a complex synthetic model."""
|
|
77
|
+
dt = parse_dtype(dtype)
|
|
78
|
+
rng = np.random.default_rng(seed)
|
|
79
|
+
v = gradient_background(nx, nz, 1500.0, 4800.0, dtype=dt)
|
|
80
|
+
v = add_gaussian_anomaly(v, nx * 0.72, nz * 0.35, 30.0, 12.0, 1200.0)
|
|
81
|
+
v = add_gaussian_anomaly(v, nx * 0.25, nz * 0.55, 25.0, 10.0, -700.0)
|
|
82
|
+
v = add_gaussian_anomaly(v, nx * 0.5, nz * 0.75, 40.0, 15.0, 900.0)
|
|
83
|
+
noise = rng.normal(0.0, 1.0, (nx, nz)).astype(dt)
|
|
84
|
+
k = 7
|
|
85
|
+
pad = k // 2
|
|
86
|
+
padded = np.pad(noise, pad, mode="edge")
|
|
87
|
+
smooth = np.zeros_like(noise)
|
|
88
|
+
for di in range(k):
|
|
89
|
+
for dj in range(k):
|
|
90
|
+
smooth += padded[di:di + nx, dj:dj + nz]
|
|
91
|
+
smooth /= (k * k)
|
|
92
|
+
return np.clip(v * (1.0 + 0.05 * smooth / smooth.std()), 500.0, 6000.0).astype(dt)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Primitive building blocks for subsurface velocity models with dtype support."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from ..utils.dtypes import parse_dtype
|
|
4
|
+
|
|
5
|
+
def gradient_background(nx: int, nz: int, v0: float = 1500.0, v1: float = 4500.0, dtype=np.float32) -> np.ndarray:
|
|
6
|
+
"""Generate a linear velocity gradient with depth.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
nx : int
|
|
11
|
+
Number of cells along x (horizontal direction).
|
|
12
|
+
nz : int
|
|
13
|
+
Number of cells along z (vertical direction, positive downward).
|
|
14
|
+
v0 : float, optional
|
|
15
|
+
Velocity at the surface, in m/s. Default is 1500.0.
|
|
16
|
+
v1 : float, optional
|
|
17
|
+
Velocity at the bottom of the model, in m/s. Default is 4500.0.
|
|
18
|
+
dtype : str or np.dtype, optional
|
|
19
|
+
Data type precision ('f32', 'f64', etc.). Default is np.float32.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
ndarray (nx, nz)
|
|
24
|
+
Velocity field in m/s with the requested dtype.
|
|
25
|
+
"""
|
|
26
|
+
dt = parse_dtype(dtype)
|
|
27
|
+
z = np.linspace(0.0, 1.0, nz, dtype=dt)
|
|
28
|
+
v_col = v0 + (v1 - v0) * z
|
|
29
|
+
return np.broadcast_to(v_col, (nx, nz)).copy()
|
|
30
|
+
|
|
31
|
+
def add_gaussian_anomaly(v: np.ndarray, cx: float, cz: float, sigma_x: float, sigma_z: float, dv: float, vmin: float = 200.0, vmax: float = 8000.0) -> np.ndarray:
|
|
32
|
+
"""Add a Gaussian velocity anomaly to an existing model.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
v : ndarray (nx, nz)
|
|
37
|
+
Velocity field to modify (not modified in place).
|
|
38
|
+
cx, cz : float
|
|
39
|
+
Center position along x and z, in cells.
|
|
40
|
+
sigma_x, sigma_z : float
|
|
41
|
+
Gaussian standard deviation along x and z, in cells.
|
|
42
|
+
dv : float
|
|
43
|
+
Amplitude of the anomaly at the center, in m/s.
|
|
44
|
+
vmin, vmax : float, optional
|
|
45
|
+
Clipping velocity bounds, in m/s.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
ndarray (nx, nz)
|
|
50
|
+
Copy of v with the anomaly added, preserving dtype and clipped.
|
|
51
|
+
"""
|
|
52
|
+
nx, nz = v.shape
|
|
53
|
+
dt = v.dtype
|
|
54
|
+
ii = np.arange(nx, dtype=dt)[:, None]
|
|
55
|
+
jj = np.arange(nz, dtype=dt)[None, :]
|
|
56
|
+
g = dv * np.exp(-((ii - cx) ** 2 / (2 * sigma_x ** 2) + (jj - cz) ** 2 / (2 * sigma_z ** 2)))
|
|
57
|
+
return np.clip(v + g, vmin, vmax).astype(dt)
|
|
58
|
+
|
|
59
|
+
def add_fault_offset(v: np.ndarray, ix_fault: int, throw: int) -> np.ndarray:
|
|
60
|
+
"""Shift layers on either side of a vertical fault.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
v : ndarray (nx, nz)
|
|
65
|
+
Stratified velocity field to shift.
|
|
66
|
+
ix_fault : int
|
|
67
|
+
Column index of the fault.
|
|
68
|
+
throw : int
|
|
69
|
+
Fault throw in cells along z.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
ndarray (nx, nz)
|
|
74
|
+
Shifted field preserving original dtype.
|
|
75
|
+
"""
|
|
76
|
+
out = v.copy()
|
|
77
|
+
left, right = slice(None, ix_fault), slice(ix_fault, None)
|
|
78
|
+
if throw >= 0:
|
|
79
|
+
out[left, throw:] = v[left, :out.shape[1] - throw]
|
|
80
|
+
else:
|
|
81
|
+
out[right, :throw] = v[right, -throw:]
|
|
82
|
+
return out
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Precision management utilities for velocity models."""
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def parse_dtype(dtype: Union[str, np.dtype] = np.float32) -> np.dtype:
|
|
8
|
+
"""Parse and validate the requested floating-point precision dtype.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
dtype : str or np.dtype, optional
|
|
13
|
+
Target precision, e.g., 'f32', 'float32', 'f64', 'float64',
|
|
14
|
+
or np.float32, np.float64. Default is np.float32.
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
-------
|
|
18
|
+
np.dtype
|
|
19
|
+
Normalized NumPy floating-point dtype.
|
|
20
|
+
|
|
21
|
+
Raises
|
|
22
|
+
------
|
|
23
|
+
ValueError
|
|
24
|
+
If the dtype is not supported or not a floating-point type.
|
|
25
|
+
"""
|
|
26
|
+
if isinstance(dtype, str):
|
|
27
|
+
dtype_lower = dtype.lower()
|
|
28
|
+
|
|
29
|
+
if dtype_lower in ("f32", "float32"):
|
|
30
|
+
return np.dtype(np.float32)
|
|
31
|
+
elif dtype_lower in ("f64", "float64"):
|
|
32
|
+
return np.dtype(np.float64)
|
|
33
|
+
else:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"Unsupported string dtype: '{dtype}'. Use 'f32' or 'f64'."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
dt = np.dtype(dtype)
|
|
39
|
+
|
|
40
|
+
if dt == np.dtype(np.float32) or dt == np.dtype(np.float64):
|
|
41
|
+
return dt
|
|
42
|
+
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"Unsupported dtype: {dt}. Only float32 (f32) and float64 (f64) are allowed."
|
|
45
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Input/Output utilities for exporting velocity models to seismic solvers."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import numpy as np
|
|
4
|
+
from typing import Union, Tuple
|
|
5
|
+
from .validation import validate_model
|
|
6
|
+
|
|
7
|
+
def export_model(
|
|
8
|
+
v: np.ndarray,
|
|
9
|
+
filepath: Union[str, Path],
|
|
10
|
+
format: str = "bin",
|
|
11
|
+
dx: float = 1.0,
|
|
12
|
+
dz: float = 1.0
|
|
13
|
+
) -> None:
|
|
14
|
+
"""Export a velocity model to a file compatible with seismic solvers.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
v : ndarray (nx, nz)
|
|
19
|
+
Velocity model array.
|
|
20
|
+
filepath : str or Path
|
|
21
|
+
Destination file path.
|
|
22
|
+
format : str, optional
|
|
23
|
+
Export format:
|
|
24
|
+
- 'bin' or 'raw': Flat binary file (C-contiguous or Fortran-contiguous depending on solver needs).
|
|
25
|
+
- 'npy': Standard NumPy binary format preserving dtype and shape.
|
|
26
|
+
Default is 'bin'.
|
|
27
|
+
dx, dz : float, optional
|
|
28
|
+
Grid spacing (validated before export).
|
|
29
|
+
"""
|
|
30
|
+
validate_model(v, dx=dx, dz=dz)
|
|
31
|
+
filepath = Path(filepath)
|
|
32
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
fmt = format.lower()
|
|
35
|
+
if fmt in ("bin", "raw"):
|
|
36
|
+
# Enregistrement en binaire brut (souvent attendu en float32/float64 pur par les solveurs C/Fortran)
|
|
37
|
+
v.tofile(filepath)
|
|
38
|
+
elif fmt == "npy":
|
|
39
|
+
np.save(filepath, v)
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError(f"Unsupported export format: '{format}'. Use 'bin', 'raw', or 'npy'.")
|
|
42
|
+
|
|
43
|
+
def load_model(filepath: Union[str, Path], shape: Tuple[int, int], dtype=np.float32) -> np.ndarray:
|
|
44
|
+
"""Load a velocity model from a raw binary file.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
filepath : str or Path
|
|
49
|
+
Path to the binary file.
|
|
50
|
+
shape : tuple of int
|
|
51
|
+
Expected shape (nx, nz) of the model.
|
|
52
|
+
dtype : str or np.dtype, optional
|
|
53
|
+
Data type of the binary file. Default is np.float32.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
ndarray (nx, nz)
|
|
58
|
+
Loaded velocity model.
|
|
59
|
+
"""
|
|
60
|
+
filepath = Path(filepath)
|
|
61
|
+
if filepath.suffix == ".npy":
|
|
62
|
+
return np.load(filepath)
|
|
63
|
+
|
|
64
|
+
v = np.fromfile(filepath, dtype=dtype)
|
|
65
|
+
expected_size = shape[0] * shape[1]
|
|
66
|
+
if v.size != expected_size:
|
|
67
|
+
raise ValueError(f"File size mismatch: expected {expected_size} elements for shape {shape}, got {v.size}.")
|
|
68
|
+
return v.reshape(shape)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Model validation utilities."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
def validate_model(v: np.ndarray, dx: float = 1.0, dz: float = 1.0, v_min: float = 200.0, v_max: float = 8000.0) -> None:
|
|
5
|
+
"""Validate a velocity model array and its parameters.
|
|
6
|
+
|
|
7
|
+
Parameters
|
|
8
|
+
----------
|
|
9
|
+
v : ndarray
|
|
10
|
+
Velocity model array (nx, nz).
|
|
11
|
+
dx, dz : float
|
|
12
|
+
Grid spacing in meters.
|
|
13
|
+
v_min, v_max : float
|
|
14
|
+
Acceptable velocity limits in m/s.
|
|
15
|
+
|
|
16
|
+
Raises
|
|
17
|
+
------
|
|
18
|
+
ValueError
|
|
19
|
+
If the model structure, dtype, or bounds are invalid.
|
|
20
|
+
"""
|
|
21
|
+
if not isinstance(v, np.ndarray):
|
|
22
|
+
raise ValueError("Model must be a NumPy array.")
|
|
23
|
+
if v.ndim != 2:
|
|
24
|
+
raise ValueError(f"Model must be 2D, got {v.ndim}D.")
|
|
25
|
+
if v.dtype not in (np.dtype(np.float32), np.dtype(np.float64)):
|
|
26
|
+
raise ValueError(f"Model dtype must be float32 or float64, got {v.dtype}.")
|
|
27
|
+
if not np.all(np.isfinite(v)):
|
|
28
|
+
raise ValueError("Model contains non-finite values (NaN or Inf).")
|
|
29
|
+
if np.any(v < v_min) or np.any(v > v_max):
|
|
30
|
+
raise ValueError(f"Velocities out of range [{v_min}, {v_max}] m/s.")
|
|
31
|
+
if dx <= 0 or dz <= 0:
|
|
32
|
+
raise ValueError("Grid spacing dx and dz must be positive.")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Visualization utilities compatible with Python 3.8+."""
|
|
2
|
+
import numpy as np
|
|
3
|
+
from .validation import validate_model
|
|
4
|
+
|
|
5
|
+
def quick_view(
|
|
6
|
+
velocities: np.ndarray,
|
|
7
|
+
dx: float = 1.0,
|
|
8
|
+
dz: float = 1.0,
|
|
9
|
+
title: str = "Velocity model"
|
|
10
|
+
) -> None:
|
|
11
|
+
"""Display a velocity field with depth positive downward.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
velocities : ndarray (nx, nz)
|
|
16
|
+
Velocity field to display, in m/s.
|
|
17
|
+
dx : float, optional
|
|
18
|
+
Grid spacing along x, in meters. Default is 1.0.
|
|
19
|
+
dz : float, optional
|
|
20
|
+
Grid spacing along z, in meters. Default is 1.0.
|
|
21
|
+
title : str, optional
|
|
22
|
+
Figure title. Default is "Velocity model".
|
|
23
|
+
"""
|
|
24
|
+
import matplotlib.pyplot as plt
|
|
25
|
+
validate_model(velocities, dx=dx, dz=dz)
|
|
26
|
+
|
|
27
|
+
nx, nz = velocities.shape
|
|
28
|
+
fig, ax = plt.subplots(figsize=(12, 6))
|
|
29
|
+
im = ax.imshow(
|
|
30
|
+
velocities.T,
|
|
31
|
+
extent=[0, (nx - 1) * dx, (nz - 1) * dz, 0],
|
|
32
|
+
aspect="auto",
|
|
33
|
+
cmap="viridis"
|
|
34
|
+
)
|
|
35
|
+
ax.set_xlabel("x (m)")
|
|
36
|
+
ax.set_ylabel("Depth z (m)")
|
|
37
|
+
ax.set_title(title)
|
|
38
|
+
fig.colorbar(im, ax=ax, label="Velocity (m/s)")
|
|
39
|
+
plt.show()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seiscode-models
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Synthetic subsurface velocity model generators for seismic testing
|
|
5
|
+
Author-email: Damien Pageot <damien.pageot@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/PageotD/seiscode-models
|
|
8
|
+
Project-URL: Repository, https://github.com/PageotD/seiscode-models
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: numpy>=1.21.0
|
|
22
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Requires-Dist: build; extra == "dev"
|
|
28
|
+
Requires-Dist: twine; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# SeisCode Models
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
seiscode_models/__init__.py,sha256=4J8B9idUJGVrIPCx_2Bjy5mLjQRq4B4Bq74O3RtcGWQ,937
|
|
2
|
+
seiscode_models/models/__init__.py,sha256=kjmCkZ2QldahaopnNbM_a2Ksq1uld3Ujn_oJnuV0AI0,2770
|
|
3
|
+
seiscode_models/models/geological.py,sha256=gDvfw__0XeYsj3dNNuSui6woVQRrSu6oDcIQcivVre4,4281
|
|
4
|
+
seiscode_models/models/primitives.py,sha256=kjmCkZ2QldahaopnNbM_a2Ksq1uld3Ujn_oJnuV0AI0,2770
|
|
5
|
+
seiscode_models/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
seiscode_models/utils/dtypes.py,sha256=ykk7dnOnj961-vwi1Tzfif6MReoz5yftMPM8vBh86Rw,1242
|
|
7
|
+
seiscode_models/utils/io.py,sha256=Vg9TwwKpPK6C72zKc5iudPHrfOhk8TQmVWh13mD-iCk,2236
|
|
8
|
+
seiscode_models/utils/validation.py,sha256=OCGNRvOPam0EMtNJd9opFPgLvv6Im9ukd1zOI6127FQ,1206
|
|
9
|
+
seiscode_models/utils/visualization.py,sha256=apOR9kQHH4SHmYpiT9EtcP0sPukwtv_AZXfRjQu76Co,1128
|
|
10
|
+
seiscode_models-0.1.0.dist-info/licenses/LICENSE,sha256=nWmkmlLKRYqs-P25ka5_7FH30mxPfdX8V3vROLVlqZw,1070
|
|
11
|
+
seiscode_models-0.1.0.dist-info/METADATA,sha256=3zDgmUSrORII6qyqS88plpS3luWfk1K8EAC06UnQA_8,1189
|
|
12
|
+
seiscode_models-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
seiscode_models-0.1.0.dist-info/top_level.txt,sha256=-mTq28wC7E8Y7_B7zGDQ805k6oggIRGO4sUJPuVZiTc,16
|
|
14
|
+
seiscode_models-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Damien Pageot
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
seiscode_models
|