DeepGPR 0.0.14__tar.gz → 0.0.16__tar.gz
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.
- {deepgpr-0.0.14 → deepgpr-0.0.16}/PKG-INFO +86 -18
- {deepgpr-0.0.14 → deepgpr-0.0.16}/README.md +85 -17
- {deepgpr-0.0.14 → deepgpr-0.0.16}/pyproject.toml +1 -1
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR/__init__.py +24 -8
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR/common.py +327 -190
- deepgpr-0.0.16/src/DeepGPR/compute2.py +1082 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr.cu +2072 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr.dll +0 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr.h +92 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr.so +0 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr_cpu.c +1809 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr_cpu.dll +0 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr_cpu.dylib +0 -0
- deepgpr-0.0.16/src/DeepGPR/lib/deepgpr_cpu.so +0 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR/lib/libomp.dylib +0 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR/multiscale.py +15 -8
- deepgpr-0.0.16/src/DeepGPR/wavelet.py +140 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR.egg-info/PKG-INFO +86 -18
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR.egg-info/SOURCES.txt +5 -1
- deepgpr-0.0.16/tests/test_discrete_adjoint.py +383 -0
- deepgpr-0.0.16/tests/test_numerics.py +544 -0
- deepgpr-0.0.16/tests/test_wavelet.py +135 -0
- deepgpr-0.0.14/src/DeepGPR/compute2.py +0 -491
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr.cu +0 -1390
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr.dll +0 -0
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr.so +0 -0
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr_cpu.c +0 -1161
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr_cpu.dll +0 -0
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr_cpu.dylib +0 -0
- deepgpr-0.0.14/src/DeepGPR/lib/deepgpr_cpu.so +0 -0
- deepgpr-0.0.14/src/DeepGPR/wavelet.py +0 -34
- {deepgpr-0.0.14 → deepgpr-0.0.16}/license +0 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/setup.cfg +0 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR.egg-info/dependency_links.txt +0 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR.egg-info/requires.txt +0 -0
- {deepgpr-0.0.14 → deepgpr-0.0.16}/src/DeepGPR.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DeepGPR
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.16
|
|
4
4
|
Summary: PyTorch and CUDA for GPR FWI
|
|
5
5
|
Author-email: Lei Liu <liulei990222@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -72,7 +72,7 @@ import matplotlib.pyplot as plt
|
|
|
72
72
|
|
|
73
73
|
# Set up the parameters and models
|
|
74
74
|
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
75
|
-
dx=0.02
|
|
75
|
+
dx=0.02 # Or [dx, dy, dz], for example [0.02, 0.015, 0.01]
|
|
76
76
|
dt=3e-11
|
|
77
77
|
nt=2000
|
|
78
78
|
er = torch.ones(100, 100,1) * 2
|
|
@@ -84,7 +84,9 @@ receiver_location=torch.tensor([[[10,90,0]]],device=device,dtype=torch.int)
|
|
|
84
84
|
freq=2e8
|
|
85
85
|
peak_time = 1 / freq
|
|
86
86
|
source_amplitudes = torch.zeros((1,nt,1),device=device)
|
|
87
|
-
source_amplitudes[0,:,0]=DeepGPR.ricker(
|
|
87
|
+
source_amplitudes[0,:,0]=DeepGPR.wavelet.ricker(
|
|
88
|
+
freq, nt, dt, peak_time, device=device
|
|
89
|
+
)
|
|
88
90
|
|
|
89
91
|
|
|
90
92
|
#forward modeling
|
|
@@ -111,6 +113,28 @@ plt.show()
|
|
|
111
113
|
|
|
112
114
|
There are more examples in the ./examples.
|
|
113
115
|
|
|
116
|
+
## Source Wavelets
|
|
117
|
+
|
|
118
|
+
Wavelets are available from the `DeepGPR.wavelet` module. Every function
|
|
119
|
+
returns a one-dimensional PyTorch tensor and accepts optional `dtype` and
|
|
120
|
+
`device` arguments.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
ricker = DeepGPR.wavelet.ricker(freq, nt, dt, peak_time, device=device)
|
|
124
|
+
gaussian = DeepGPR.wavelet.gaussian(freq, nt, dt, peak_time, device=device)
|
|
125
|
+
derivative = DeepGPR.wavelet.gaussian_derivative(
|
|
126
|
+
freq, nt, dt, peak_time, device=device
|
|
127
|
+
)
|
|
128
|
+
morlet = DeepGPR.wavelet.morlet(
|
|
129
|
+
freq, nt, dt, peak_time, cycles=3.0, device=device
|
|
130
|
+
)
|
|
131
|
+
burst = DeepGPR.wavelet.sine_burst(
|
|
132
|
+
freq, nt, dt, peak_time, cycles=3.0, device=device
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`DeepGPR.ricker(...)` remains available as a backward-compatible alias.
|
|
137
|
+
|
|
114
138
|
|
|
115
139
|
|
|
116
140
|
The following figures present representative 2D and 3D full-waveform inversion (FWI) examples. For each case, the true model, initial model, and inverted result are shown to evaluate the reconstruction performance of the proposed method.
|
|
@@ -148,31 +172,48 @@ def compute(device, dx=None, dt=None,
|
|
|
148
172
|
E=None, H=None, PML=None,
|
|
149
173
|
pmlthick=10, source_direction=2, reciever_direction=2,
|
|
150
174
|
model_gradient_sampling_interval=1,
|
|
175
|
+
wavefield_storage_dtype=torch.float32,
|
|
151
176
|
use_async_offload=False,
|
|
152
177
|
fdtd_order=2,
|
|
153
178
|
mode=2,
|
|
154
|
-
debug=False
|
|
179
|
+
debug=False,
|
|
180
|
+
print_parameters=False,
|
|
181
|
+
save_forward_wavefield_path=None):
|
|
155
182
|
```
|
|
183
|
+
|
|
184
|
+
Set `print_parameters=True` on an ordinary call to print the normalized
|
|
185
|
+
configuration and memory estimate immediately before the native solver starts:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
result = DeepGPR.compute(
|
|
189
|
+
device="cuda:0",
|
|
190
|
+
# Other model, source, and acquisition arguments...
|
|
191
|
+
print_parameters=True,
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
156
195
|
## 📥 Input Parameters
|
|
157
196
|
### 1. Basic Physics & Grid Parameters
|
|
158
197
|
|
|
159
198
|
| Parameter | Data Type | Description |
|
|
160
199
|
| :--- | :--- | :--- |
|
|
161
200
|
| **`device`** | `torch.device` / `str` | PyTorch computation device, e.g., `'cuda:0'` or `'cpu'`. CUDA loads `deepgpr.so/.dll`; CPU loads `deepgpr_cpu.so/.dll/.dylib`. |
|
|
162
|
-
| **`dx`** | `float` |
|
|
163
|
-
| **`dt`** | `float` | Time step size.
|
|
201
|
+
| **`dx`** | `float` / 3-value `list`, `tuple`, or `Tensor` | Grid spacing in meters. A scalar uses an isotropic grid ($dx = dy = dz$). Three values specify independent $(dx, dy, dz)$ spacings for the finite differences, CFL condition, CPML coefficients, and source scaling. |
|
|
202
|
+
| **`dt`** | `float` | Time step size. It is checked against a material-aware CFL limit that includes the selected 2/4/8-order stencil. Typically in seconds (s). |
|
|
164
203
|
| **`fdtd_order`** | `int` | Spatial finite-difference order used by the FDTD field updates. Supported values are `2`, `4`, and `8`; default is `2` for compatibility with earlier versions. |
|
|
165
204
|
| **`mode`** | `int` | FWI gradient mode. `2` keeps the previous Ez-only model-gradient calculation. `3` uses Ex, Ey, and Ez electric-field contributions for relative permittivity and conductivity gradients. |
|
|
166
|
-
| **`debug`** | `bool` | Runs expensive NaN/Inf and zero-field validation checks when `True`. The default `False` keeps these checks disabled for faster production runs. |
|
|
205
|
+
| **`debug`** | `bool` | Runs expensive NaN/Inf and zero-field validation checks when `True`. Backward validation covers material, source, and initial-state gradients actually requested by autograd. The default `False` keeps these checks disabled for faster production runs. |
|
|
206
|
+
| **`print_parameters`** | `bool` | Prints a preflight summary before native FDTD execution. The summary includes all simulation options and a tensor-payload memory estimate for model/state tensors, CPML, saved `E_saved`/`R_saved` wavefields, receiver buffers, gradients, low-precision snapshots, and CUDA offload buffers. CPU and CUDA estimates are reported separately with a 20% capacity margin. |
|
|
207
|
+
| **`save_forward_wavefield_path`** | `str` / path-like / `None` | Directory used to save `E_saved` after a successful forward run. The default `None` performs no file I/O. Files use the local 24-hour start time, for example `forward_wavefield_14-35.pt`; a numeric suffix prevents overwriting when multiple runs start in the same minute. |
|
|
167
208
|
### 2. Medium Model Parameters
|
|
168
209
|
|
|
169
210
|
This section defines the electromagnetic properties of the simulation space. For 2D simulations, set `nz=1`.
|
|
170
211
|
|
|
171
212
|
| Parameter | Data Type | Shape | Description |
|
|
172
213
|
| :--- | :--- | :--- | :--- |
|
|
173
|
-
| **`
|
|
174
|
-
| **`
|
|
175
|
-
| **`
|
|
214
|
+
| **`eps_r`** (`er`) | `Tensor` (float) | `(nx, ny, nz)` or `(nx, ny)` | Relative permittivity ($\epsilon_r$). Values must be $\ge 1$. `er` is the deprecated alias. |
|
|
215
|
+
| **`sigma`** (`se`) | `Tensor` (float) | `(nx, ny, nz)` or `(nx, ny)` | Electrical conductivity ($\sigma$). Values must be non-negative. `se` is the deprecated alias. |
|
|
216
|
+
| **`mu_r`** (`mr`) | `Tensor` (float) | `(nx, ny, nz)` or `(nx, ny)` | Relative permeability ($\mu_r$). Optional; defaults to 1 for the entire space. `mr` is the deprecated alias. |
|
|
176
217
|
|
|
177
218
|
> **Dimension Key**: `nx`, `ny`, and `nz` represent the number of grid cells along the X, Y, and Z axes, respectively.
|
|
178
219
|
|
|
@@ -186,7 +227,7 @@ This section defines the geometric observation system (coordinates) and the exci
|
|
|
186
227
|
| **`source_location`** | `Tensor` (int) | `(nstep, nsr, 3)` | Grid coordinate indices of the sources.<br>The last dimension corresponds to `[x_idx, y_idx, z_idx]`. |
|
|
187
228
|
| **`receiver_location`** | `Tensor` (int) | `(nstep, nrx, 3)` | Grid coordinate indices of the receivers.<br>The last dimension corresponds to `[x_idx, y_idx, z_idx]`. |
|
|
188
229
|
| **`source_direction`** | `int` | Scalar | Polarization direction/component of the source excitation.<br>`0` = X, `1` = Y, `2` = Z (e.g., exciting $E_z$). |
|
|
189
|
-
| **`
|
|
230
|
+
| **`receiver_component`** (`reciever_direction`) | `int` | Scalar | The component recorded by the receivers.<br>`0` = $E_x$, `1` = $E_y$, `2` = $E_z$. The misspelled name remains as a deprecated alias. |
|
|
190
231
|
|
|
191
232
|
> **Core Shape Definitions**:
|
|
192
233
|
> * `nstep`: Number of shots/batches (independent simulation tasks running in parallel).
|
|
@@ -199,15 +240,26 @@ This section defines the geometric observation system (coordinates) and the exci
|
|
|
199
240
|
| Parameter | Data Type | Format | Description |
|
|
200
241
|
| :--- | :--- | :--- | :--- |
|
|
201
242
|
| **`pmlthick`** | `int` / `list` / `Tensor`| Scalar or list of 6 | Thickness (in grid layers) of the PML (Perfectly Matched Layer) absorbing boundaries.<br>- Integer `p`: All six boundaries have thickness `p` (Z-boundaries are ignored in 2D).<br>- List `[x0, xm, y0, ym, z0, zm]`: Specific thicknesses for the 6 boundaries. |
|
|
202
|
-
| **`model_gradient_sampling_interval`**| `int` | Scalar | Wavefield sampling interval during forward propagation (Default: 1).<br>A larger integer reduces
|
|
203
|
-
| **`
|
|
243
|
+
| **`model_gradient_sampling_interval`**| `int` | Scalar | Wavefield sampling interval during forward propagation (Default: 1).<br>A larger integer reduces VRAM use for `E_saved` and `R_saved`, but uses an explicitly approximate model gradient. The last incomplete sampling block is weighted by its actual length. |
|
|
244
|
+
| **`wavefield_storage_dtype`** | `torch.dtype` / `str` | `float32`, `float16`, or `bfloat16` | Storage format for saved `E_saved` and `R_saved` model-gradient wavefields. FDTD propagation remains float32. `float16` and `bfloat16` halve saved-wavefield memory at the cost of gradient accuracy; `bfloat16` has the safer dynamic range. String aliases such as `"fp16"` and `"bf16"` are accepted. |
|
|
245
|
+
| **`use_async_offload`** | `bool` | Scalar | CUDA-only VRAM optimization flag (Default: `False`).<br>If `True`, `E_saved` and `R_saved` are asynchronously offloaded to page-locked host memory (`pin_memory` CPU RAM). This reduces GPU VRAM consumption at the cost of PCIe transfers. On CPU this option is ignored. |
|
|
204
246
|
|
|
205
247
|
### 4.1 FWI Gradient Mode
|
|
206
248
|
|
|
207
249
|
`mode` only changes how the model gradients are accumulated during backpropagation:
|
|
208
250
|
|
|
209
|
-
- `mode=2` (default): Saves Ez in `
|
|
210
|
-
- `mode=3`: Saves Ex, Ey, and Ez in `
|
|
251
|
+
- `mode=2` (default): Saves Ez in `E_saved` and computes relative permittivity/conductivity gradients from Ez only.
|
|
252
|
+
- `mode=3`: Saves Ex, Ey, and Ez in `E_saved` and computes relative permittivity/conductivity gradients from all three electric-field components. This is intended for complete 3D Maxwell FWI. The receiver component is not changed by this option.
|
|
253
|
+
|
|
254
|
+
When `eps_r` or `sigma` requires gradients, `mode=2` is restricted to 2D Ez-TM modeling; use `mode=3` for 3D gradients. Source-waveform gradients are supported. Gradients with respect to `mu_r` are not currently implemented and are rejected explicitly.
|
|
255
|
+
|
|
256
|
+
### 4.2 Discrete Adjoint Gradient
|
|
257
|
+
|
|
258
|
+
The backward solver applies the exact reverse-mode transpose of each executed operation in reverse order: receiver sampling, source injection, electric CPML, electric update, magnetic CPML, and magnetic update. The derivative transpose is applied to the material-weighted field cotangent, so heterogeneous update coefficients and anisotropic grid spacing are handled by the executed discrete operator. Every electric and magnetic CPML auxiliary state has a separate cotangent recurrence on all six faces.
|
|
259
|
+
|
|
260
|
+
CPML is treated as a fixed numerical boundary. Its boundary material averages are explicitly detached, and CPML cells are excluded from the returned material gradients. This separation must be retained when optimizing a model.
|
|
261
|
+
|
|
262
|
+
Use `model_gradient_sampling_interval=1` and `wavefield_storage_dtype=torch.float32` for a directional derivative check in the physical model region. Temporal subsampling and lower-precision storage deliberately approximate the gradient. Run [the gradient-check notebook](examples/4.GradientCheck.ipynb) after rebuilding the native ABI 6 libraries.
|
|
211
263
|
|
|
212
264
|
## CPU Backend Build
|
|
213
265
|
|
|
@@ -253,20 +305,36 @@ For starting a forward simulation from scratch ($t=0$), these three parameters s
|
|
|
253
305
|
The function returns a tuple of 5 elements. These are used to extract synthetic data, initiate the gradient flow for backpropagation, or serve as initial parameters (`E`, `H`, `PML`) for subsequent time-stepped calculations.
|
|
254
306
|
|
|
255
307
|
```python
|
|
256
|
-
return
|
|
308
|
+
return E_saved, (Ex, Ey, Ez), (Hx, Hy, Hz), (x0EPhi1...zmHPhi2), receiver_amplitudes
|
|
257
309
|
```
|
|
258
310
|
|
|
259
|
-
1. **`
|
|
311
|
+
1. **`E_saved`**: The pre-update electric field history `E^n` saved for gradient calculation and diagnostics. An internal `R_saved` tensor stores the corresponding discrete right-hand side `R^n`.
|
|
260
312
|
* **Shape when `mode=2`**: `(nt_saved, nstep, nx, ny, nz)`, storing Ez only.
|
|
261
313
|
* **Shape when `mode=3`**: `(3, nt_saved, nstep, nx, ny, nz)`, storing components in `[Ex, Ey, Ez]` order.
|
|
262
314
|
* `nt_saved` depends on `nt` and `model_gradient_sampling_interval`.
|
|
315
|
+
* Dtype is selected by `wavefield_storage_dtype`.
|
|
316
|
+
* Set `save_forward_wavefield_path="/path/to/output"` to save this tensor as a CPU-loadable `.pt` file. Load it with `torch.load(path, map_location="cpu")`.
|
|
263
317
|
2. **`(Ex, Ey, Ez)`**: The 3D electric field state at the final time step.
|
|
264
318
|
3. **`(Hx, Hy, Hz)`**: The 3D magnetic field state at the final time step.
|
|
265
319
|
4. **`(PML_Tuple)`**: A tuple of 24 Tensors recording the final time step state of the PML auxiliary $\Phi$ variables.
|
|
266
320
|
5. **`receiver_amplitudes`**: **The core output.** The waveform signals recorded by the receivers over the entire simulation time.
|
|
267
321
|
* **Shape**: `(nstep, nt, nrx)`
|
|
268
|
-
* **Meaning**: `[Shot Index, Time Step, Receiver Index]`.
|
|
322
|
+
* **Meaning**: `[Shot Index, Time Step, Receiver Index]`. This output is sliced to the component specified by `receiver_component` (or its deprecated alias `reciever_direction`).
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
## Tests and Verification
|
|
326
|
+
|
|
327
|
+
Fast unit tests and the complete numerical verification notebooks are kept in
|
|
328
|
+
the single [`tests`](tests) directory:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
python -m unittest discover -s tests -p "test_*.py"
|
|
332
|
+
python tests/run_notebook.py tests/00_local_backend_and_contracts.ipynb
|
|
333
|
+
```
|
|
269
334
|
|
|
335
|
+
Run notebooks `00` through `09` in numeric order, followed by
|
|
336
|
+
`99_verification_summary.ipynb`. See [`tests/README.md`](tests/README.md) for
|
|
337
|
+
the complete matrix and CUDA verification options.
|
|
270
338
|
|
|
271
339
|
# Cite information
|
|
272
340
|
If you find our codes useful, please kindly cite this article. Thanks.
|
|
@@ -49,7 +49,7 @@ import matplotlib.pyplot as plt
|
|
|
49
49
|
|
|
50
50
|
# Set up the parameters and models
|
|
51
51
|
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
52
|
-
dx=0.02
|
|
52
|
+
dx=0.02 # Or [dx, dy, dz], for example [0.02, 0.015, 0.01]
|
|
53
53
|
dt=3e-11
|
|
54
54
|
nt=2000
|
|
55
55
|
er = torch.ones(100, 100,1) * 2
|
|
@@ -61,7 +61,9 @@ receiver_location=torch.tensor([[[10,90,0]]],device=device,dtype=torch.int)
|
|
|
61
61
|
freq=2e8
|
|
62
62
|
peak_time = 1 / freq
|
|
63
63
|
source_amplitudes = torch.zeros((1,nt,1),device=device)
|
|
64
|
-
source_amplitudes[0,:,0]=DeepGPR.ricker(
|
|
64
|
+
source_amplitudes[0,:,0]=DeepGPR.wavelet.ricker(
|
|
65
|
+
freq, nt, dt, peak_time, device=device
|
|
66
|
+
)
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
#forward modeling
|
|
@@ -88,6 +90,28 @@ plt.show()
|
|
|
88
90
|
|
|
89
91
|
There are more examples in the ./examples.
|
|
90
92
|
|
|
93
|
+
## Source Wavelets
|
|
94
|
+
|
|
95
|
+
Wavelets are available from the `DeepGPR.wavelet` module. Every function
|
|
96
|
+
returns a one-dimensional PyTorch tensor and accepts optional `dtype` and
|
|
97
|
+
`device` arguments.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
ricker = DeepGPR.wavelet.ricker(freq, nt, dt, peak_time, device=device)
|
|
101
|
+
gaussian = DeepGPR.wavelet.gaussian(freq, nt, dt, peak_time, device=device)
|
|
102
|
+
derivative = DeepGPR.wavelet.gaussian_derivative(
|
|
103
|
+
freq, nt, dt, peak_time, device=device
|
|
104
|
+
)
|
|
105
|
+
morlet = DeepGPR.wavelet.morlet(
|
|
106
|
+
freq, nt, dt, peak_time, cycles=3.0, device=device
|
|
107
|
+
)
|
|
108
|
+
burst = DeepGPR.wavelet.sine_burst(
|
|
109
|
+
freq, nt, dt, peak_time, cycles=3.0, device=device
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`DeepGPR.ricker(...)` remains available as a backward-compatible alias.
|
|
114
|
+
|
|
91
115
|
|
|
92
116
|
|
|
93
117
|
The following figures present representative 2D and 3D full-waveform inversion (FWI) examples. For each case, the true model, initial model, and inverted result are shown to evaluate the reconstruction performance of the proposed method.
|
|
@@ -125,31 +149,48 @@ def compute(device, dx=None, dt=None,
|
|
|
125
149
|
E=None, H=None, PML=None,
|
|
126
150
|
pmlthick=10, source_direction=2, reciever_direction=2,
|
|
127
151
|
model_gradient_sampling_interval=1,
|
|
152
|
+
wavefield_storage_dtype=torch.float32,
|
|
128
153
|
use_async_offload=False,
|
|
129
154
|
fdtd_order=2,
|
|
130
155
|
mode=2,
|
|
131
|
-
debug=False
|
|
156
|
+
debug=False,
|
|
157
|
+
print_parameters=False,
|
|
158
|
+
save_forward_wavefield_path=None):
|
|
132
159
|
```
|
|
160
|
+
|
|
161
|
+
Set `print_parameters=True` on an ordinary call to print the normalized
|
|
162
|
+
configuration and memory estimate immediately before the native solver starts:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
result = DeepGPR.compute(
|
|
166
|
+
device="cuda:0",
|
|
167
|
+
# Other model, source, and acquisition arguments...
|
|
168
|
+
print_parameters=True,
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
133
172
|
## 📥 Input Parameters
|
|
134
173
|
### 1. Basic Physics & Grid Parameters
|
|
135
174
|
|
|
136
175
|
| Parameter | Data Type | Description |
|
|
137
176
|
| :--- | :--- | :--- |
|
|
138
177
|
| **`device`** | `torch.device` / `str` | PyTorch computation device, e.g., `'cuda:0'` or `'cpu'`. CUDA loads `deepgpr.so/.dll`; CPU loads `deepgpr_cpu.so/.dll/.dylib`. |
|
|
139
|
-
| **`dx`** | `float` |
|
|
140
|
-
| **`dt`** | `float` | Time step size.
|
|
178
|
+
| **`dx`** | `float` / 3-value `list`, `tuple`, or `Tensor` | Grid spacing in meters. A scalar uses an isotropic grid ($dx = dy = dz$). Three values specify independent $(dx, dy, dz)$ spacings for the finite differences, CFL condition, CPML coefficients, and source scaling. |
|
|
179
|
+
| **`dt`** | `float` | Time step size. It is checked against a material-aware CFL limit that includes the selected 2/4/8-order stencil. Typically in seconds (s). |
|
|
141
180
|
| **`fdtd_order`** | `int` | Spatial finite-difference order used by the FDTD field updates. Supported values are `2`, `4`, and `8`; default is `2` for compatibility with earlier versions. |
|
|
142
181
|
| **`mode`** | `int` | FWI gradient mode. `2` keeps the previous Ez-only model-gradient calculation. `3` uses Ex, Ey, and Ez electric-field contributions for relative permittivity and conductivity gradients. |
|
|
143
|
-
| **`debug`** | `bool` | Runs expensive NaN/Inf and zero-field validation checks when `True`. The default `False` keeps these checks disabled for faster production runs. |
|
|
182
|
+
| **`debug`** | `bool` | Runs expensive NaN/Inf and zero-field validation checks when `True`. Backward validation covers material, source, and initial-state gradients actually requested by autograd. The default `False` keeps these checks disabled for faster production runs. |
|
|
183
|
+
| **`print_parameters`** | `bool` | Prints a preflight summary before native FDTD execution. The summary includes all simulation options and a tensor-payload memory estimate for model/state tensors, CPML, saved `E_saved`/`R_saved` wavefields, receiver buffers, gradients, low-precision snapshots, and CUDA offload buffers. CPU and CUDA estimates are reported separately with a 20% capacity margin. |
|
|
184
|
+
| **`save_forward_wavefield_path`** | `str` / path-like / `None` | Directory used to save `E_saved` after a successful forward run. The default `None` performs no file I/O. Files use the local 24-hour start time, for example `forward_wavefield_14-35.pt`; a numeric suffix prevents overwriting when multiple runs start in the same minute. |
|
|
144
185
|
### 2. Medium Model Parameters
|
|
145
186
|
|
|
146
187
|
This section defines the electromagnetic properties of the simulation space. For 2D simulations, set `nz=1`.
|
|
147
188
|
|
|
148
189
|
| Parameter | Data Type | Shape | Description |
|
|
149
190
|
| :--- | :--- | :--- | :--- |
|
|
150
|
-
| **`
|
|
151
|
-
| **`
|
|
152
|
-
| **`
|
|
191
|
+
| **`eps_r`** (`er`) | `Tensor` (float) | `(nx, ny, nz)` or `(nx, ny)` | Relative permittivity ($\epsilon_r$). Values must be $\ge 1$. `er` is the deprecated alias. |
|
|
192
|
+
| **`sigma`** (`se`) | `Tensor` (float) | `(nx, ny, nz)` or `(nx, ny)` | Electrical conductivity ($\sigma$). Values must be non-negative. `se` is the deprecated alias. |
|
|
193
|
+
| **`mu_r`** (`mr`) | `Tensor` (float) | `(nx, ny, nz)` or `(nx, ny)` | Relative permeability ($\mu_r$). Optional; defaults to 1 for the entire space. `mr` is the deprecated alias. |
|
|
153
194
|
|
|
154
195
|
> **Dimension Key**: `nx`, `ny`, and `nz` represent the number of grid cells along the X, Y, and Z axes, respectively.
|
|
155
196
|
|
|
@@ -163,7 +204,7 @@ This section defines the geometric observation system (coordinates) and the exci
|
|
|
163
204
|
| **`source_location`** | `Tensor` (int) | `(nstep, nsr, 3)` | Grid coordinate indices of the sources.<br>The last dimension corresponds to `[x_idx, y_idx, z_idx]`. |
|
|
164
205
|
| **`receiver_location`** | `Tensor` (int) | `(nstep, nrx, 3)` | Grid coordinate indices of the receivers.<br>The last dimension corresponds to `[x_idx, y_idx, z_idx]`. |
|
|
165
206
|
| **`source_direction`** | `int` | Scalar | Polarization direction/component of the source excitation.<br>`0` = X, `1` = Y, `2` = Z (e.g., exciting $E_z$). |
|
|
166
|
-
| **`
|
|
207
|
+
| **`receiver_component`** (`reciever_direction`) | `int` | Scalar | The component recorded by the receivers.<br>`0` = $E_x$, `1` = $E_y$, `2` = $E_z$. The misspelled name remains as a deprecated alias. |
|
|
167
208
|
|
|
168
209
|
> **Core Shape Definitions**:
|
|
169
210
|
> * `nstep`: Number of shots/batches (independent simulation tasks running in parallel).
|
|
@@ -176,15 +217,26 @@ This section defines the geometric observation system (coordinates) and the exci
|
|
|
176
217
|
| Parameter | Data Type | Format | Description |
|
|
177
218
|
| :--- | :--- | :--- | :--- |
|
|
178
219
|
| **`pmlthick`** | `int` / `list` / `Tensor`| Scalar or list of 6 | Thickness (in grid layers) of the PML (Perfectly Matched Layer) absorbing boundaries.<br>- Integer `p`: All six boundaries have thickness `p` (Z-boundaries are ignored in 2D).<br>- List `[x0, xm, y0, ym, z0, zm]`: Specific thicknesses for the 6 boundaries. |
|
|
179
|
-
| **`model_gradient_sampling_interval`**| `int` | Scalar | Wavefield sampling interval during forward propagation (Default: 1).<br>A larger integer reduces
|
|
180
|
-
| **`
|
|
220
|
+
| **`model_gradient_sampling_interval`**| `int` | Scalar | Wavefield sampling interval during forward propagation (Default: 1).<br>A larger integer reduces VRAM use for `E_saved` and `R_saved`, but uses an explicitly approximate model gradient. The last incomplete sampling block is weighted by its actual length. |
|
|
221
|
+
| **`wavefield_storage_dtype`** | `torch.dtype` / `str` | `float32`, `float16`, or `bfloat16` | Storage format for saved `E_saved` and `R_saved` model-gradient wavefields. FDTD propagation remains float32. `float16` and `bfloat16` halve saved-wavefield memory at the cost of gradient accuracy; `bfloat16` has the safer dynamic range. String aliases such as `"fp16"` and `"bf16"` are accepted. |
|
|
222
|
+
| **`use_async_offload`** | `bool` | Scalar | CUDA-only VRAM optimization flag (Default: `False`).<br>If `True`, `E_saved` and `R_saved` are asynchronously offloaded to page-locked host memory (`pin_memory` CPU RAM). This reduces GPU VRAM consumption at the cost of PCIe transfers. On CPU this option is ignored. |
|
|
181
223
|
|
|
182
224
|
### 4.1 FWI Gradient Mode
|
|
183
225
|
|
|
184
226
|
`mode` only changes how the model gradients are accumulated during backpropagation:
|
|
185
227
|
|
|
186
|
-
- `mode=2` (default): Saves Ez in `
|
|
187
|
-
- `mode=3`: Saves Ex, Ey, and Ez in `
|
|
228
|
+
- `mode=2` (default): Saves Ez in `E_saved` and computes relative permittivity/conductivity gradients from Ez only.
|
|
229
|
+
- `mode=3`: Saves Ex, Ey, and Ez in `E_saved` and computes relative permittivity/conductivity gradients from all three electric-field components. This is intended for complete 3D Maxwell FWI. The receiver component is not changed by this option.
|
|
230
|
+
|
|
231
|
+
When `eps_r` or `sigma` requires gradients, `mode=2` is restricted to 2D Ez-TM modeling; use `mode=3` for 3D gradients. Source-waveform gradients are supported. Gradients with respect to `mu_r` are not currently implemented and are rejected explicitly.
|
|
232
|
+
|
|
233
|
+
### 4.2 Discrete Adjoint Gradient
|
|
234
|
+
|
|
235
|
+
The backward solver applies the exact reverse-mode transpose of each executed operation in reverse order: receiver sampling, source injection, electric CPML, electric update, magnetic CPML, and magnetic update. The derivative transpose is applied to the material-weighted field cotangent, so heterogeneous update coefficients and anisotropic grid spacing are handled by the executed discrete operator. Every electric and magnetic CPML auxiliary state has a separate cotangent recurrence on all six faces.
|
|
236
|
+
|
|
237
|
+
CPML is treated as a fixed numerical boundary. Its boundary material averages are explicitly detached, and CPML cells are excluded from the returned material gradients. This separation must be retained when optimizing a model.
|
|
238
|
+
|
|
239
|
+
Use `model_gradient_sampling_interval=1` and `wavefield_storage_dtype=torch.float32` for a directional derivative check in the physical model region. Temporal subsampling and lower-precision storage deliberately approximate the gradient. Run [the gradient-check notebook](examples/4.GradientCheck.ipynb) after rebuilding the native ABI 6 libraries.
|
|
188
240
|
|
|
189
241
|
## CPU Backend Build
|
|
190
242
|
|
|
@@ -230,20 +282,36 @@ For starting a forward simulation from scratch ($t=0$), these three parameters s
|
|
|
230
282
|
The function returns a tuple of 5 elements. These are used to extract synthetic data, initiate the gradient flow for backpropagation, or serve as initial parameters (`E`, `H`, `PML`) for subsequent time-stepped calculations.
|
|
231
283
|
|
|
232
284
|
```python
|
|
233
|
-
return
|
|
285
|
+
return E_saved, (Ex, Ey, Ez), (Hx, Hy, Hz), (x0EPhi1...zmHPhi2), receiver_amplitudes
|
|
234
286
|
```
|
|
235
287
|
|
|
236
|
-
1. **`
|
|
288
|
+
1. **`E_saved`**: The pre-update electric field history `E^n` saved for gradient calculation and diagnostics. An internal `R_saved` tensor stores the corresponding discrete right-hand side `R^n`.
|
|
237
289
|
* **Shape when `mode=2`**: `(nt_saved, nstep, nx, ny, nz)`, storing Ez only.
|
|
238
290
|
* **Shape when `mode=3`**: `(3, nt_saved, nstep, nx, ny, nz)`, storing components in `[Ex, Ey, Ez]` order.
|
|
239
291
|
* `nt_saved` depends on `nt` and `model_gradient_sampling_interval`.
|
|
292
|
+
* Dtype is selected by `wavefield_storage_dtype`.
|
|
293
|
+
* Set `save_forward_wavefield_path="/path/to/output"` to save this tensor as a CPU-loadable `.pt` file. Load it with `torch.load(path, map_location="cpu")`.
|
|
240
294
|
2. **`(Ex, Ey, Ez)`**: The 3D electric field state at the final time step.
|
|
241
295
|
3. **`(Hx, Hy, Hz)`**: The 3D magnetic field state at the final time step.
|
|
242
296
|
4. **`(PML_Tuple)`**: A tuple of 24 Tensors recording the final time step state of the PML auxiliary $\Phi$ variables.
|
|
243
297
|
5. **`receiver_amplitudes`**: **The core output.** The waveform signals recorded by the receivers over the entire simulation time.
|
|
244
298
|
* **Shape**: `(nstep, nt, nrx)`
|
|
245
|
-
* **Meaning**: `[Shot Index, Time Step, Receiver Index]`.
|
|
299
|
+
* **Meaning**: `[Shot Index, Time Step, Receiver Index]`. This output is sliced to the component specified by `receiver_component` (or its deprecated alias `reciever_direction`).
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
## Tests and Verification
|
|
303
|
+
|
|
304
|
+
Fast unit tests and the complete numerical verification notebooks are kept in
|
|
305
|
+
the single [`tests`](tests) directory:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
python -m unittest discover -s tests -p "test_*.py"
|
|
309
|
+
python tests/run_notebook.py tests/00_local_backend_and_contracts.ipynb
|
|
310
|
+
```
|
|
246
311
|
|
|
312
|
+
Run notebooks `00` through `09` in numeric order, followed by
|
|
313
|
+
`99_verification_summary.ipynb`. See [`tests/README.md`](tests/README.md) for
|
|
314
|
+
the complete matrix and CUDA verification options.
|
|
247
315
|
|
|
248
316
|
# Cite information
|
|
249
317
|
If you find our codes useful, please kindly cite this article. Thanks.
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
|
|
9
9
|
_FLOAT_P = ctypes.POINTER(ctypes.c_float)
|
|
10
10
|
_INT_P = ctypes.POINTER(ctypes.c_int)
|
|
11
|
+
_VOID_P = ctypes.c_void_p
|
|
11
12
|
|
|
12
13
|
_PACKAGE_DIR = Path(__file__).resolve().parent
|
|
13
14
|
_LIB_DIR = _PACKAGE_DIR / "lib"
|
|
@@ -154,8 +155,17 @@ def _configure_deepgpr_library(lib: ctypes.CDLL) -> None:
|
|
|
154
155
|
if getattr(lib, "_deepgpr_argtypes_configured", False):
|
|
155
156
|
return
|
|
156
157
|
|
|
158
|
+
lib.deepgpr_abi_version.argtypes = []
|
|
159
|
+
lib.deepgpr_abi_version.restype = ctypes.c_int
|
|
160
|
+
abi_version = int(lib.deepgpr_abi_version())
|
|
161
|
+
if abi_version != 6:
|
|
162
|
+
raise RuntimeError(
|
|
163
|
+
f"Incompatible DeepGPR native ABI {abi_version}; expected ABI 6. "
|
|
164
|
+
"Rebuild the CPU/CUDA shared libraries from the current sources."
|
|
165
|
+
)
|
|
166
|
+
|
|
157
167
|
lib.forward.argtypes = [
|
|
158
|
-
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
168
|
+
_FLOAT_P, _FLOAT_P, _FLOAT_P, _VOID_P, _VOID_P,
|
|
159
169
|
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
160
170
|
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
161
171
|
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
@@ -175,16 +185,18 @@ def _configure_deepgpr_library(lib: ctypes.CDLL) -> None:
|
|
|
175
185
|
_FLOAT_P, _FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
176
186
|
_FLOAT_P, _FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
177
187
|
|
|
178
|
-
ctypes.c_float, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
179
|
-
|
|
188
|
+
ctypes.c_float, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
189
|
+
ctypes.c_float, ctypes.c_float, ctypes.c_float,
|
|
190
|
+
_INT_P, _FLOAT_P, ctypes.c_int,
|
|
180
191
|
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
181
192
|
_INT_P, _FLOAT_P,
|
|
182
|
-
ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
193
|
+
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
194
|
+
ctypes.c_int,
|
|
183
195
|
]
|
|
184
196
|
lib.forward.restype = None
|
|
185
197
|
|
|
186
198
|
lib.backward.argtypes = [
|
|
187
|
-
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
199
|
+
_FLOAT_P, _FLOAT_P, _FLOAT_P, _VOID_P, _VOID_P,
|
|
188
200
|
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
189
201
|
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
190
202
|
_FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
@@ -204,12 +216,15 @@ def _configure_deepgpr_library(lib: ctypes.CDLL) -> None:
|
|
|
204
216
|
_FLOAT_P, _FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
205
217
|
_FLOAT_P, _FLOAT_P, _FLOAT_P, _FLOAT_P,
|
|
206
218
|
|
|
207
|
-
ctypes.c_float, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
219
|
+
ctypes.c_float, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
220
|
+
ctypes.c_float, ctypes.c_float, ctypes.c_float,
|
|
208
221
|
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
209
222
|
_INT_P, _FLOAT_P,
|
|
210
223
|
ctypes.c_int,
|
|
224
|
+
ctypes.c_int, _INT_P, ctypes.c_int, _FLOAT_P, ctypes.c_int,
|
|
211
225
|
_FLOAT_P, _FLOAT_P,
|
|
212
|
-
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
226
|
+
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
227
|
+
ctypes.c_int,
|
|
213
228
|
]
|
|
214
229
|
lib.backward.restype = None
|
|
215
230
|
|
|
@@ -245,7 +260,7 @@ def _load_deepgpr_library(kind: str) -> ctypes.CDLL:
|
|
|
245
260
|
load_errors.append(f"{path}: {exc}")
|
|
246
261
|
continue
|
|
247
262
|
|
|
248
|
-
_require_exported_symbols(lib, ("forward", "backward"), path)
|
|
263
|
+
_require_exported_symbols(lib, ("forward", "backward", "deepgpr_abi_version"), path)
|
|
249
264
|
_configure_deepgpr_library(lib)
|
|
250
265
|
lib._deepgpr_path = str(path.resolve())
|
|
251
266
|
_LOADED_LIBS[kind] = lib
|
|
@@ -321,6 +336,7 @@ def set_library_fdtd_order(lib: ctypes.CDLL, fdtd_order: int) -> None:
|
|
|
321
336
|
from .common import *
|
|
322
337
|
from .compute2 import *
|
|
323
338
|
from .multiscale import *
|
|
339
|
+
from . import wavelet as wavelet
|
|
324
340
|
from .wavelet import *
|
|
325
341
|
|
|
326
342
|
|