DeepGPR 0.0.20__tar.gz → 0.1.0__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.1.0/CHANGELOG.md +285 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/MANIFEST.in +7 -4
- deepgpr-0.1.0/PKG-INFO +260 -0
- deepgpr-0.1.0/README.md +224 -0
- deepgpr-0.1.0/pyproject.toml +108 -0
- deepgpr-0.1.0/requirements.txt +5 -0
- deepgpr-0.1.0/src/DeepGPR/__init__.py +133 -0
- deepgpr-0.1.0/src/DeepGPR/_version.py +3 -0
- deepgpr-0.1.0/src/DeepGPR/common.py +51 -0
- deepgpr-0.1.0/src/DeepGPR/compute2.py +89 -0
- deepgpr-0.1.0/src/DeepGPR/config/__init__.py +11 -0
- deepgpr-0.1.0/src/DeepGPR/config/constants.py +235 -0
- deepgpr-0.1.0/src/DeepGPR/config/defaults.py +75 -0
- deepgpr-0.1.0/src/DeepGPR/inversion/__init__.py +5 -0
- deepgpr-0.1.0/src/DeepGPR/inversion/regularization.py +148 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR/lib/deepgpr.cu +76 -23
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR/lib/deepgpr.dll +0 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR/lib/deepgpr.h +36 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR/lib/deepgpr.so +0 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR/lib/deepgpr_cpu.c +594 -432
- deepgpr-0.1.0/src/DeepGPR/lib/deepgpr_cpu.dll +0 -0
- deepgpr-0.1.0/src/DeepGPR/lib/deepgpr_cpu.dylib +0 -0
- deepgpr-0.1.0/src/DeepGPR/lib/deepgpr_cpu.so +0 -0
- deepgpr-0.1.0/src/DeepGPR/lib/libomp.dylib +0 -0
- deepgpr-0.1.0/src/DeepGPR/multiscale.py +7 -0
- deepgpr-0.1.0/src/DeepGPR/native/__init__.py +27 -0
- deepgpr-0.1.0/src/DeepGPR/native/abi.py +222 -0
- deepgpr-0.1.0/src/DeepGPR/native/bridge.py +268 -0
- deepgpr-0.1.0/src/DeepGPR/native/loader.py +291 -0
- deepgpr-0.1.0/src/DeepGPR/postprocessing/__init__.py +12 -0
- deepgpr-0.1.0/src/DeepGPR/postprocessing/filters.py +158 -0
- deepgpr-0.1.0/src/DeepGPR/postprocessing/io.py +92 -0
- deepgpr-0.1.0/src/DeepGPR/preprocessing/__init__.py +29 -0
- deepgpr-0.1.0/src/DeepGPR/preprocessing/grid.py +119 -0
- deepgpr-0.1.0/src/DeepGPR/preprocessing/model_setup.py +426 -0
- deepgpr-0.1.0/src/DeepGPR/preprocessing/stability.py +117 -0
- deepgpr-0.1.0/src/DeepGPR/preprocessing/wavelets.py +173 -0
- deepgpr-0.1.0/src/DeepGPR/py.typed +0 -0
- deepgpr-0.1.0/src/DeepGPR/solver/__init__.py +44 -0
- deepgpr-0.1.0/src/DeepGPR/solver/autograd.py +650 -0
- deepgpr-0.1.0/src/DeepGPR/solver/fields.py +151 -0
- deepgpr-0.1.0/src/DeepGPR/solver/memory.py +403 -0
- deepgpr-0.1.0/src/DeepGPR/solver/modeling.py +495 -0
- deepgpr-0.1.0/src/DeepGPR/solver/pml.py +441 -0
- deepgpr-0.1.0/src/DeepGPR/solver/storage.py +329 -0
- deepgpr-0.1.0/src/DeepGPR/utils/__init__.py +38 -0
- deepgpr-0.1.0/src/DeepGPR/utils/devices.py +23 -0
- deepgpr-0.1.0/src/DeepGPR/utils/exceptions.py +37 -0
- deepgpr-0.1.0/src/DeepGPR/utils/formatting.py +25 -0
- deepgpr-0.1.0/src/DeepGPR/utils/logger.py +67 -0
- deepgpr-0.1.0/src/DeepGPR/utils/tensor_checks.py +90 -0
- deepgpr-0.1.0/src/DeepGPR/utils/validators.py +98 -0
- deepgpr-0.1.0/src/DeepGPR/visualization/__init__.py +5 -0
- deepgpr-0.1.0/src/DeepGPR/visualization/plotting.py +209 -0
- deepgpr-0.1.0/src/DeepGPR/wavelet.py +21 -0
- deepgpr-0.1.0/src/DeepGPR.egg-info/PKG-INFO +260 -0
- deepgpr-0.1.0/src/DeepGPR.egg-info/SOURCES.txt +60 -0
- deepgpr-0.1.0/src/DeepGPR.egg-info/requires.txt +15 -0
- deepgpr-0.0.20/PKG-INFO +0 -411
- deepgpr-0.0.20/README.md +0 -388
- deepgpr-0.0.20/pyproject.toml +0 -63
- deepgpr-0.0.20/src/DeepGPR/__init__.py +0 -376
- deepgpr-0.0.20/src/DeepGPR/common.py +0 -1002
- deepgpr-0.0.20/src/DeepGPR/compute2.py +0 -1566
- deepgpr-0.0.20/src/DeepGPR/lib/deepgpr_cpu.dll +0 -0
- deepgpr-0.0.20/src/DeepGPR/lib/deepgpr_cpu.dylib +0 -0
- deepgpr-0.0.20/src/DeepGPR/lib/deepgpr_cpu.so +0 -0
- deepgpr-0.0.20/src/DeepGPR/lib/libomp.dylib +0 -0
- deepgpr-0.0.20/src/DeepGPR/multiscale.py +0 -78
- deepgpr-0.0.20/src/DeepGPR/requirements.txt +0 -1
- deepgpr-0.0.20/src/DeepGPR/wavelet.py +0 -140
- deepgpr-0.0.20/src/DeepGPR.egg-info/PKG-INFO +0 -411
- deepgpr-0.0.20/src/DeepGPR.egg-info/SOURCES.txt +0 -24
- deepgpr-0.0.20/src/DeepGPR.egg-info/requires.txt +0 -4
- {deepgpr-0.0.20 → deepgpr-0.1.0}/license +0 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/setup.cfg +0 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR.egg-info/dependency_links.txt +0 -0
- {deepgpr-0.0.20 → deepgpr-0.1.0}/src/DeepGPR.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — Structural refactor and numerical fixes
|
|
8
|
+
|
|
9
|
+
This release reorganises the Python package into a layered, typed and
|
|
10
|
+
documented code base and fixes the defects found in the review
|
|
11
|
+
([`docs/AUDIT.md`](docs/AUDIT.md)).
|
|
12
|
+
|
|
13
|
+
**What changes numerically** (all deliberate, see [Numerical fixes](#numerical-fixes)):
|
|
14
|
+
|
|
15
|
+
| Area | Change | Size |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Forward modelling | none, except ε0/µ0 in the CPML profiles now CODATA 2018 | ~5·10⁻¹⁰ relative in the coefficients; no forward output changed in the verification runs |
|
|
18
|
+
| CPU gradients | fixed summation order (deterministic) instead of OpenMP atomics | rounding level (≤ ~7·10⁻⁶ relative on random test problems, same size as the old run-to-run noise) |
|
|
19
|
+
| `design_fir_filter` / `apply_filter` | correct taps for an even filter length | filtered data changes when `int(fs/cutoff)` is even |
|
|
20
|
+
| `hilbert_transform` | correct analytic signal for an odd trace length | envelopes change for odd `nt`; even `nt` unchanged |
|
|
21
|
+
| `TVRegularization(method="isotropic")` | standard isotropic TV | different penalty; `"anisotropic"` (default) unchanged |
|
|
22
|
+
|
|
23
|
+
`legacy=True` (filters, envelope) and `method="legacy_isotropic"` (TV)
|
|
24
|
+
reproduce DeepGPR 0.0.20 bitwise. The refactor itself was first verified to be
|
|
25
|
+
bitwise identical to 0.0.20 before any of these fixes were applied — see
|
|
26
|
+
[Verification](#verification).
|
|
27
|
+
|
|
28
|
+
### Architecture
|
|
29
|
+
|
|
30
|
+
| Before | After | Responsibility |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `__init__.py` (library loading, 81/86-argument `argtypes` lists) | `native/loader.py`, `native/abi.py`, `native/bridge.py` | locate/load/configure the CPU and CUDA libraries; one declarative ABI table; argument marshalling |
|
|
33
|
+
| `common.py` (1026 lines, mixed concerns) | `preprocessing/{grid,stability,model_setup}.py`, `solver/{pml,fields}.py`, `inversion/regularization.py`, `utils/tensor_checks.py` | validation & PML extension, CFL, CPML, field state, TV, diagnostics |
|
|
34
|
+
| `compute2.py` (1585 lines) | `solver/{modeling,autograd,storage,memory}.py`, `postprocessing/io.py` | `compute()`, autograd bridge, history storage & INT8, memory report, file output |
|
|
35
|
+
| `wavelet.py` | `preprocessing/wavelets.py` | source wavelets |
|
|
36
|
+
| `multiscale.py` | `postprocessing/filters.py` | FIR low-pass, envelope |
|
|
37
|
+
| inline literals | `config/constants.py`, `config/defaults.py` | physical constants, ABI codes, defaults |
|
|
38
|
+
| — | `utils/{logger,exceptions,validators,formatting,devices}.py` | logging, errors, validation |
|
|
39
|
+
| — | `visualization/plotting.py` | model / radargram / trace plots |
|
|
40
|
+
|
|
41
|
+
`DeepGPR.common`, `DeepGPR.compute2`, `DeepGPR.multiscale` and `DeepGPR.wavelet`
|
|
42
|
+
remain importable as thin compatibility modules, and every name previously
|
|
43
|
+
reachable as `DeepGPR.<name>` for the public API is still exported.
|
|
44
|
+
|
|
45
|
+
### Key refactoring decisions
|
|
46
|
+
|
|
47
|
+
- **Native changes kept minimal.** The FDTD, CPML and material-gradient
|
|
48
|
+
kernels are unchanged. The native sources changed only for the fixes below:
|
|
49
|
+
error reporting (CPU and CUDA), shared constants in `deepgpr.h`, and the
|
|
50
|
+
deterministic CPU adjoint. The C ABI stays at version 6; new functions are
|
|
51
|
+
additive and probed at run time, so binaries built from older sources still
|
|
52
|
+
load (without the new guarantees).
|
|
53
|
+
- **One source of truth for the C ABI.** `native/abi.py` lists every exported
|
|
54
|
+
function once, parameter by parameter, mirroring `deepgpr.h`. `argtypes`
|
|
55
|
+
and the call-argument order are both generated from it, replacing two
|
|
56
|
+
hand-maintained 80+-item positional lists. `tests/test_native_abi.py` parses
|
|
57
|
+
the header and fails on any drift (runs without PyTorch).
|
|
58
|
+
- **Named native arguments.** `solver/autograd.py` builds a
|
|
59
|
+
`{c_parameter_name: value}` mapping (`build_forward_arguments`,
|
|
60
|
+
`build_backward_arguments`); a missing or misspelled parameter raises
|
|
61
|
+
instead of silently shifting every following argument.
|
|
62
|
+
- **Typed solver state.** The 77 positional arguments of the autograd
|
|
63
|
+
`Function` became a frozen `SolverConfig` dataclass, a `SolverTensors`
|
|
64
|
+
dataclass and the 30 differentiable state tensors.
|
|
65
|
+
- **Named tuple for model preparation.** `initialization()` returns a
|
|
66
|
+
`PreparedModel` `NamedTuple`: identical tuple behaviour, readable fields.
|
|
67
|
+
- **Constants, not magic numbers.** Physical constants, CPML profile
|
|
68
|
+
parameters, stencil coefficient sums, ABI bit fields, INT8 limits, memory
|
|
69
|
+
margins, Hamming coefficients and API defaults live in `config/`.
|
|
70
|
+
- **Duplicate code removed.** Six copy-pasted CPML face blocks, six
|
|
71
|
+
CPML-phi allocation blocks, 30 `.contiguous()` lines, two 60-line `ctypes`
|
|
72
|
+
call sites and the per-face capability checks were replaced by loops/tables.
|
|
73
|
+
|
|
74
|
+
### Numerical fixes
|
|
75
|
+
|
|
76
|
+
- **Deterministic CPU adjoint.** The CPU backward scattered the transposed
|
|
77
|
+
curl stencils with `#pragma omp atomic`, so gradients differed in the last
|
|
78
|
+
bits from run to run whenever `OMP_NUM_THREADS > 1`. It now runs in two
|
|
79
|
+
passes: every source point stores its stencil weight per (target field,
|
|
80
|
+
axis) channel, then every target point gathers the transposed stencil in a
|
|
81
|
+
fixed order. No OpenMP atomics remain in `deepgpr_cpu.c`; source injection
|
|
82
|
+
and receiver adjoints run shots in parallel and sources/receivers
|
|
83
|
+
sequentially. Results are **bitwise identical for any thread count**, and
|
|
84
|
+
the backend reports this through `deepgpr_deterministic_adjoint()`. The
|
|
85
|
+
gradient is still the exact discrete transpose (dot-product test below).
|
|
86
|
+
Cost: six field-sized buffers per shot during backward (included in the
|
|
87
|
+
memory estimate as "CPU adjoint stencil weights"). The CUDA adjoint still
|
|
88
|
+
uses `atomicAdd` and is not bitwise reproducible.
|
|
89
|
+
- **Native errors reach Python.** CUDA API and launch errors used to be
|
|
90
|
+
printed to stderr while the call returned normally. Both backends now keep a
|
|
91
|
+
per-thread error message (`deepgpr_last_error`, `deepgpr_clear_last_error`,
|
|
92
|
+
`deepgpr_supports_error_reporting`); the bridge clears it before every native
|
|
93
|
+
call and raises `NativeLibraryError` with the message afterwards. The CPU
|
|
94
|
+
backend also reports allocation failures (it used to fall back silently to
|
|
95
|
+
a less accurate R history) and size overflows. INT8 parameter errors in the
|
|
96
|
+
CUDA backend are reported the same way.
|
|
97
|
+
- **One set of physical constants.** ε0, µ0 and the 100 S/m PEC threshold are
|
|
98
|
+
defined once in `lib/deepgpr.h` (CODATA 2018) and used by the CPU and CUDA
|
|
99
|
+
backends; `DeepGPR.config.constants` uses the same values (previously
|
|
100
|
+
µ0 = 4π·10⁻⁷ in Python). `tests/test_native_abi.py` fails if they drift.
|
|
101
|
+
- **FIR design.** `design_fir_filter` replaced tap `(numtaps-1)//2` with the
|
|
102
|
+
sinc peak `2 fc/fs` also for an even number of taps, where no sample lies
|
|
103
|
+
at the centre. The override is now applied only for odd lengths.
|
|
104
|
+
- **Analytic-signal envelope.** For an odd trace length `hilbert_transform`
|
|
105
|
+
did not double bin `nt//2`, which is a positive frequency there. The
|
|
106
|
+
highest positive bin is now doubled (as `scipy.signal.hilbert`). Removing
|
|
107
|
+
the DC bin stays the default (`remove_dc=True`).
|
|
108
|
+
- **Isotropic TV.** `method="isotropic"` (or `"iso"`) now computes the
|
|
109
|
+
standard `Σ_cells sqrt(|∇m|² + ε)` with forward differences and a Neumann
|
|
110
|
+
far boundary; the smoothing `epsilon` is a constructor argument. The old
|
|
111
|
+
formula `sqrt(Lx² + Ly² + Lz² + ε)` is available as
|
|
112
|
+
`method="legacy_isotropic"`. Unknown method names now raise `ValueError`
|
|
113
|
+
(they used to select the old formula silently).
|
|
114
|
+
|
|
115
|
+
**Prebuilt binaries.** The Linux CPU library in `src/DeepGPR/lib` is built
|
|
116
|
+
from the 0.1.0 sources. The CUDA (`deepgpr.so`, `deepgpr.dll`) and the Windows /
|
|
117
|
+
macOS CPU binaries are still the 0.0.20 builds until the *Build Native
|
|
118
|
+
Libraries* workflow runs; they load and compute as before but without error
|
|
119
|
+
reporting (and, for the CPU ones, without the deterministic adjoint).
|
|
120
|
+
`tools/run_local_validation.sh` rebuilds the CPU and CUDA libraries locally
|
|
121
|
+
and runs the full validation.
|
|
122
|
+
|
|
123
|
+
### Fixed
|
|
124
|
+
|
|
125
|
+
- **Caller gradients were overwritten.** The native adjoint advances the
|
|
126
|
+
incoming state cotangents in place; the old code passed autograd's buffers
|
|
127
|
+
straight through (`.contiguous()` is a no-op on contiguous tensors), so
|
|
128
|
+
user-supplied `grad_outputs`, retained gradients and hook arguments were
|
|
129
|
+
silently modified. They are now copied first (only cotangents that autograd
|
|
130
|
+
actually supplies; absent ones are allocated as zeros, as before). Returned
|
|
131
|
+
gradients are unchanged; the cost is one transient copy of the E/H/CPML state
|
|
132
|
+
size during backward, small next to the saved wavefield history.
|
|
133
|
+
- **Second backward crashed** with `AttributeError: 'NoneType' object has no
|
|
134
|
+
attribute 'contiguous'` (e.g. `retain_graph=True`). It now raises a clear
|
|
135
|
+
`RuntimeError` explaining that the history was released.
|
|
136
|
+
- **FDTD-order race.** `set_fdtd_order` sets process-global native state; two
|
|
137
|
+
threads using different orders could run with the wrong stencil. Calls now
|
|
138
|
+
go through an `FdtdOrderGate`: same-order calls stay concurrent, different
|
|
139
|
+
orders are serialised.
|
|
140
|
+
- **CUDA device not restored on error.** Stream/device bookkeeping around the
|
|
141
|
+
native call is now a `try/finally` context manager.
|
|
142
|
+
- **Library loading was not thread-safe** on first use; it is now locked.
|
|
143
|
+
- **`apply_filter` returned NaNs silently** when `cutoff >= fs` (a 0- or
|
|
144
|
+
1-tap FIR divides by zero); it now raises `ValueError`, as it does when the
|
|
145
|
+
filter is longer than the trace.
|
|
146
|
+
- **`TVRegularization()(None, None)`** raised `AttributeError`; now `ValueError`.
|
|
147
|
+
- **Forward-wavefield file naming race.** Two processes saving in the same
|
|
148
|
+
minute could pick the same filename; names are now reserved atomically
|
|
149
|
+
(same `forward_wavefield_HH-MM[_NN].pt` scheme). Partially written files are
|
|
150
|
+
removed on failure.
|
|
151
|
+
- **Invalid requirement shipped in the wheel.** `src/DeepGPR/requirements.txt`
|
|
152
|
+
contained `torch+cu` and was packaged via `MANIFEST.in`; removed.
|
|
153
|
+
- Error message of `apply_filter` used full-width punctuation (`。`).
|
|
154
|
+
|
|
155
|
+
### Changed
|
|
156
|
+
|
|
157
|
+
- `print()` diagnostics replaced by the `DeepGPR` logger: the waveform
|
|
158
|
+
broadcast "Tips" message (was printed on every call) is `INFO`, the saved
|
|
159
|
+
wavefield path is `INFO`, NaN/Inf reports are `ERROR`. Call
|
|
160
|
+
`DeepGPR.configure_logging("INFO")` to see them. `print_parameters=True`
|
|
161
|
+
still prints its report to stdout, byte-for-byte as before.
|
|
162
|
+
- Errors raised by DeepGPR now use a small hierarchy
|
|
163
|
+
(`DeepGPRError`, `NativeLibraryError`, `NativeLibraryNotFoundError`,
|
|
164
|
+
`CFLConditionError`, `NonFiniteTensorError`). Each class also derives from
|
|
165
|
+
the built-in type raised before (`RuntimeError`, `FileNotFoundError`,
|
|
166
|
+
`ValueError`, `FloatingPointError`), so existing `except` clauses still match.
|
|
167
|
+
All messages are unchanged.
|
|
168
|
+
- `pmlthick` accepts NumPy integer scalars (`np.int64(10)`), which previously
|
|
169
|
+
raised `TypeError`.
|
|
170
|
+
- `model_gradient_sampling_interval=True` is rejected (it was silently
|
|
171
|
+
treated as `1`).
|
|
172
|
+
- A warning is logged when conductivity exceeds 100 S/m: the native solvers
|
|
173
|
+
treat such cells as perfect conductors (no update, no gradient).
|
|
174
|
+
- `scipy` moved from a required to an optional (`examples`) dependency; it was
|
|
175
|
+
never imported.
|
|
176
|
+
- Version is single-sourced from `DeepGPR/_version.py`; `py.typed` marker added.
|
|
177
|
+
|
|
178
|
+
### Performance
|
|
179
|
+
|
|
180
|
+
The deterministic CPU adjoint is also faster, because it replaces contended
|
|
181
|
+
atomic updates with plain gathers. Forward + backward wall time, prebuilt
|
|
182
|
+
0.0.20 library vs 0.1.0 (2-core test machine, 150 time steps, 10-cell CPML):
|
|
183
|
+
|
|
184
|
+
| Problem | 1 thread | 2 threads |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| 2D 320×320, order 2 | 3.81 s → 3.16 s | 2.30 s → 1.70 s |
|
|
187
|
+
| 2D 320×320, order 8 | 6.50 s → 5.01 s | 3.79 s → 2.81 s |
|
|
188
|
+
| 3D 68³, order 2 | 19.41 s → 13.26 s | 11.91 s → 8.00 s |
|
|
189
|
+
| 3D 68³, order 4 | 35.93 s → 20.54 s | 22.17 s → 14.16 s |
|
|
190
|
+
|
|
191
|
+
The CUDA kernels are unchanged. Python-side overhead was reduced where it was
|
|
192
|
+
measurable:
|
|
193
|
+
|
|
194
|
+
- `debug=True` NaN/Inf validation scans each tensor once (`isfinite`) instead
|
|
195
|
+
of twice (`isnan` + `isinf`), halving device→host synchronisations; the
|
|
196
|
+
detailed breakdown is only computed for failing tensors.
|
|
197
|
+
- `checkpoint_initial_field` no longer computes (and discards) the full CPML
|
|
198
|
+
coefficient profiles; it only needs the face geometry.
|
|
199
|
+
- The memory estimate uses `dtype.itemsize` instead of allocating three
|
|
200
|
+
temporary tensors per call.
|
|
201
|
+
- All validation scalars in `initialization` are still gathered in a single
|
|
202
|
+
device→host transfer (the new PEC check is part of the same `torch.stack`).
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
|
|
206
|
+
- `DeepGPR.max_stable_time_step(dx, nx, ny, nz, fdtd_order=, min_er_mr=)`.
|
|
207
|
+
- `DeepGPR.estimate_compute_memory(...)` (previously private).
|
|
208
|
+
- `DeepGPR.visualization`: `plot_model`, `plot_bscan`, `plot_trace`,
|
|
209
|
+
`plot_model_comparison`.
|
|
210
|
+
- `DeepGPR.configure_logging()` / `DeepGPR.get_logger()`.
|
|
211
|
+
- `DeepGPR.__version__`.
|
|
212
|
+
- `tests/test_native_abi.py` (header ↔ ABI table and constants, marshalling,
|
|
213
|
+
order gate, native error reporting, library configuration; no PyTorch
|
|
214
|
+
required).
|
|
215
|
+
- `tests/test_signal_and_regularization.py` (FIR, envelope and TV against
|
|
216
|
+
independent NumPy implementations; legacy switches).
|
|
217
|
+
- `tests/test_cpu_determinism.py` (bitwise equality for 1, 2 and 5 threads).
|
|
218
|
+
- `tools/verify_equivalence.py` (comparison with a reference release: forward
|
|
219
|
+
and gradient tolerances, legacy reproduction, determinism, error reporting).
|
|
220
|
+
- `legacy=` keyword for `design_fir_filter`, `apply_filter`,
|
|
221
|
+
`hilbert_transform`; `remove_dc=` for `hilbert_transform`; `epsilon=` and
|
|
222
|
+
`method="legacy_isotropic"` for `TVRegularization`.
|
|
223
|
+
- Native functions `deepgpr_supports_error_reporting`, `deepgpr_last_error`,
|
|
224
|
+
`deepgpr_clear_last_error`, `deepgpr_deterministic_adjoint` (ABI 6, additive).
|
|
225
|
+
- `.github/workflows/quality.yml` (ruff, ABI test, CPU unit tests, equivalence).
|
|
226
|
+
- `requirements.txt`, `requirements-dev.txt`, ruff/mypy/pytest configuration.
|
|
227
|
+
|
|
228
|
+
### Removed / internal changes
|
|
229
|
+
|
|
230
|
+
- `from DeepGPR import *` no longer leaks `torch`, `nn`, `F`, `math`, `ctypes`,
|
|
231
|
+
`datetime`, `Path`, `warnings` and `numbers` (they were accidentally part of
|
|
232
|
+
`__all__`).
|
|
233
|
+
- The autograd `Function` `DeepGPR.DeepGPR` has a new internal call signature
|
|
234
|
+
(`apply(config, tensors, eps_r, sigma, source, *states)`). It was never a
|
|
235
|
+
documented entry point; use `DeepGPR.compute`.
|
|
236
|
+
- Monkey-patching `DeepGPR.compute2.<name>` no longer affects `compute()`;
|
|
237
|
+
patch `DeepGPR.solver.modeling` / `DeepGPR.solver.autograd` (the test-suite
|
|
238
|
+
was updated accordingly).
|
|
239
|
+
|
|
240
|
+
### Verification
|
|
241
|
+
|
|
242
|
+
**Numerical fixes** (CPU library built from the 0.1.0 sources):
|
|
243
|
+
|
|
244
|
+
| Check | Scope | Result |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| Thread determinism | 21 native cases (2D/3D, order 2/4/8, fp32/fp16/bf16, coincident sources and receivers), `OMP_NUM_THREADS` = 1, 2, 3, 4, 7, 8; plus 144 full `compute` + backward cases at 1 vs 2 threads | **bitwise identical** for every thread count (the 0.0.20 library differed in 662 of 1,386 buffers between 1 and 4 threads) |
|
|
247
|
+
| Exact transpose | adjoint dot-product test `<A x, y> = <x, Aᵀ y>` for the full state/receiver map, 12 configurations (2D/3D, order 2/4/8, all-face and partial CPML) | relative mismatch ≤ 8.3·10⁻⁶ (0.0.20: ≤ 1.2·10⁻⁵), i.e. float32 round-off |
|
|
248
|
+
| Agreement with 0.0.20 | same 144 end-to-end cases | forward outputs bitwise identical; gradients within 7·10⁻⁶ relative |
|
|
249
|
+
| Error reporting | failing native backward through the Python bridge | `NativeLibraryError` with the native message; message cleared afterwards |
|
|
250
|
+
|
|
251
|
+
**Refactor** (before the numerical fixes), against commit `73b2b50`:
|
|
252
|
+
|
|
253
|
+
| Check | Scope | Result |
|
|
254
|
+
|---|---|---|
|
|
255
|
+
| End-to-end `compute` + adjoint, real CPU library | 155 cases: 2D/3D × order 2/4/8 × fp32/fp16/bf16 history × sampling 1/2 × int/list PML × shared/per-source waveforms, plus `mu_r`, forward-only, checkpoint continuation (2D/3D), 2D mode 3, no/one-sided PML, float64 inputs, 2D waveform arrays, single shot | 10,065 outputs & gradients **bitwise identical**; `print_parameters` report identical |
|
|
256
|
+
| Native marshalling (original `ctypes` binding vs ABI table) | 56 cases incl. no-PML, partial PML, all storage types | 3,808 buffers **bitwise identical**; injected argument swaps detected |
|
|
257
|
+
| Python numerics | CPML coefficients/descriptors, CPML state allocation & validation, CFL, PML parsing, wavelets, FIR design, INT8 layout/encoding, storage codes, memory estimate, TV, `initialization`, `checkpoint_initial_field` (incl. error types & messages) | 52,910 comparisons identical |
|
|
258
|
+
| ABI table vs `deepgpr.h` | all 9 exported functions | identical names, kinds and order |
|
|
259
|
+
| `compute()` option resolution (incl. CUDA-only paths) | 25,613 combinations: CPU/CUDA × 2D/3D × history dtype × compression × INT8 block × conversion backend × INT8 reduction × async offload × history on/off, plus argument validation | identical exceptions and messages; the 544 accepted combinations resolve to identical native storage settings |
|
|
260
|
+
|
|
261
|
+
The end-to-end and numerics checks above used a NumPy implementation of the
|
|
262
|
+
PyTorch tensor API. Both package versions used this compatibility layer and
|
|
263
|
+
the same native library, with `OMP_NUM_THREADS=1`.
|
|
264
|
+
|
|
265
|
+
**Real PyTorch and CUDA** (PyTorch 1.11 / CUDA 11.5 on an RTX 4090, CPU and
|
|
266
|
+
CUDA libraries rebuilt from the 0.1.0 sources, reference = commit `73b2b50`,
|
|
267
|
+
which is byte-identical to the 0.0.21 PyPI release):
|
|
268
|
+
|
|
269
|
+
| Check | Scope | Result |
|
|
270
|
+
|---|---|---|
|
|
271
|
+
| `tools/verify_equivalence.py` | CPU and CUDA: 2D/3D × order 2/4/8 × fp32/fp16/bf16 × sampling 1/3, no/asymmetric PML, forward-only, mode 3, checkpoint segments; CUDA INT8 (2D/3D), async offload, fp16 conversion backends; legacy switches; error reporting; CPU determinism for 1/2/4/8 threads | all passed; forward outputs **bitwise identical**, gradients within 1.9·10⁻⁶ (CPU) / 4.7·10⁻⁷ (CUDA) relative |
|
|
272
|
+
| Unit tests | 105 tests, `OMP_NUM_THREADS` = 1, 2, 4, 8 | all passed (1 skip: needs two GPUs) |
|
|
273
|
+
| Verification notebooks `tests/00`–`09`, `99` | `DEEPGPR_REQUIRE_CUDA=1` | 108 checks passed, CUDA parity evidence present |
|
|
274
|
+
| CI simulation | Python 3.12, PyTorch 2.x CPU: unit tests and `verify_equivalence.py --device cpu --quick`; `ruff check src/DeepGPR tools` | all passed |
|
|
275
|
+
|
|
276
|
+
The isotropic-TV comparisons use a float32 tolerance because
|
|
277
|
+
`TVRegularization` accumulates its penalty in float32, as in 0.0.20.
|
|
278
|
+
The checkpoint-vs-full-history gradient check in
|
|
279
|
+
`test_external_pml.py` scales its absolute tolerance with the gradient
|
|
280
|
+
magnitude, because the deterministic CPU adjoint sums in a different (fixed)
|
|
281
|
+
order than the OpenMP-atomic 0.0.20 adjoint.
|
|
282
|
+
|
|
283
|
+
## [0.0.20] and earlier
|
|
284
|
+
|
|
285
|
+
See the Git history.
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
include README.md
|
|
2
|
+
include CHANGELOG.md
|
|
2
3
|
include license
|
|
3
4
|
include pyproject.toml
|
|
5
|
+
include requirements.txt
|
|
4
6
|
|
|
5
|
-
recursive-include src/DeepGPR *.py *.
|
|
7
|
+
recursive-include src/DeepGPR *.py *.typed *.cu *.h *.c *.dll *.so *.dylib
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
prune
|
|
9
|
-
prune
|
|
9
|
+
# Exclude local state and repository-only resources.
|
|
10
|
+
prune .*
|
|
11
|
+
prune validation
|
|
10
12
|
prune Fig
|
|
11
13
|
prune docs
|
|
12
14
|
prune examples
|
|
13
15
|
prune scripts
|
|
14
16
|
prune tests
|
|
17
|
+
prune tools
|
|
15
18
|
prune website
|
|
16
19
|
|
|
17
20
|
global-exclude .DS_Store
|
deepgpr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DeepGPR
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PyTorch and CUDA for GPR FWI
|
|
5
|
+
Author-email: Lei Liu <liulei990222@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/songc0a/DeepGPR
|
|
8
|
+
Project-URL: Repository, https://github.com/songc0a/DeepGPR
|
|
9
|
+
Project-URL: Documentation, https://songc0a.github.io/DeepGPR/
|
|
10
|
+
Project-URL: Changelog, https://github.com/songc0a/DeepGPR/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: ground penetrating radar,GPR,FDTD,full waveform inversion,PyTorch,CUDA
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: license
|
|
22
|
+
Requires-Dist: numpy
|
|
23
|
+
Requires-Dist: matplotlib
|
|
24
|
+
Requires-Dist: torch
|
|
25
|
+
Provides-Extra: examples
|
|
26
|
+
Requires-Dist: scipy; extra == "examples"
|
|
27
|
+
Requires-Dist: scikit-image; extra == "examples"
|
|
28
|
+
Requires-Dist: jupyter; extra == "examples"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
33
|
+
Requires-Dist: nbformat; extra == "dev"
|
|
34
|
+
Requires-Dist: nbclient; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# DeepGPR
|
|
38
|
+
|
|
39
|
+
**Official website / documentation:** [https://songc0a.github.io/DeepGPR/](https://songc0a.github.io/DeepGPR/)
|
|
40
|
+
|
|
41
|
+
DeepGPR provides a wave propagation module for PyTorch, designed for applications such as Ground Penetrating Radar (GPR) imaging and inversion. Its core concepts are derived from Deepwave. You can use it to perform both forward modeling and backpropagation—thereby enabling the simulation of wave propagation to generate synthetic data—as well as for Full Waveform Inversion (FWI). Furthermore, you can integrate this wave propagation functionality into a larger operational pipeline—incorporating various wavelets, loss functions, and other components—to achieve end-to-end forward and reverse propagation, powered by automatic differentiation and our high-performance operators.
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- 2D and 3D forward modeling of Maxwell's equations with the Finite-Difference Time-Domain (FDTD) method, for single and multiple excitations.
|
|
46
|
+
- Gradients of the receiver data with respect to relative permittivity, conductivity, the initial wavefield and the source amplitudes, computed by an exact discrete adjoint.
|
|
47
|
+
- Automatic extension of the physical model into a CPML with an independent thickness on every face. Supply only air and the target region; material gradients keep the input model shape.
|
|
48
|
+
- CUDA GPU backend and a C/OpenMP CPU backend (selected automatically with `device='cpu'`).
|
|
49
|
+
- Spatial FDTD order 2, 4 or 8 (`fdtd_order`), and gradient mode 2 (Ez only) or 3 (Ex, Ey, Ez).
|
|
50
|
+
- Large models: checkpointing, DDP, temporal sub-sampling, FP16/BF16 or GPU-native INT8 wavefield histories and asynchronous offload to host memory.
|
|
51
|
+
|
|
52
|
+
## System requirements
|
|
53
|
+
|
|
54
|
+
- **OS**: Linux, Windows and macOS for CPU execution; Linux and Windows for CUDA execution
|
|
55
|
+
- **Python**: 3.8+
|
|
56
|
+
- **Libraries**: `torch`, `numpy`, `matplotlib` (the examples additionally use `scikit-image`)
|
|
57
|
+
- **Hardware**: an NVIDIA GPU with sufficient memory for CUDA execution; CPU execution works without a GPU
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
Install a PyTorch build for your platform first ([pytorch.org](https://pytorch.org/get-started/locally/)): a CUDA-enabled build for GPU use, or a CPU build. Then
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install DeepGPR # runtime
|
|
65
|
+
pip install "DeepGPR[examples]" # + scipy, scikit-image, jupyter for the notebooks
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For development from a clone:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install -r requirements-dev.txt
|
|
72
|
+
pip install -e .
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The prebuilt native libraries ship in `src/DeepGPR/lib`. To rebuild them see [`docs/BUILDING.md`](docs/BUILDING.md).
|
|
76
|
+
|
|
77
|
+
## Quick start: a small forward-modeling test
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import torch
|
|
81
|
+
import matplotlib.pyplot as plt
|
|
82
|
+
import DeepGPR
|
|
83
|
+
|
|
84
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
85
|
+
dx = 0.02 # or [dx, dy, dz], for example [0.02, 0.015, 0.01]
|
|
86
|
+
dt = 3e-11
|
|
87
|
+
nt = 2000
|
|
88
|
+
|
|
89
|
+
eps_r = torch.ones(100, 100, 1) * 2
|
|
90
|
+
eps_r[50:, :] = 5
|
|
91
|
+
sigma = torch.zeros_like(eps_r)
|
|
92
|
+
eps_r.requires_grad_()
|
|
93
|
+
|
|
94
|
+
source_location = torch.tensor([[[10, 10, 0]]], device=device, dtype=torch.int)
|
|
95
|
+
receiver_location = torch.tensor([[[10, 90, 0]]], device=device, dtype=torch.int)
|
|
96
|
+
freq = 2e8
|
|
97
|
+
source_amplitudes = torch.zeros((1, nt, 1), device=device)
|
|
98
|
+
source_amplitudes[0, :, 0] = DeepGPR.wavelet.ricker(freq, nt, dt, 1 / freq, device=device)
|
|
99
|
+
|
|
100
|
+
E_saved, E, H, pml_state, receivers = DeepGPR.compute(
|
|
101
|
+
device=device, dx=dx, dt=dt,
|
|
102
|
+
source_amplitudes=source_amplitudes,
|
|
103
|
+
source_location=source_location,
|
|
104
|
+
receiver_location=receiver_location,
|
|
105
|
+
eps_r=eps_r, sigma=sigma,
|
|
106
|
+
fdtd_order=2,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
receivers.square().sum().backward()
|
|
110
|
+
|
|
111
|
+
fig, ax = plt.subplots(1, 2, figsize=(10, 3))
|
|
112
|
+
DeepGPR.visualization.plot_trace(receivers[0, :, 0], dt=dt, ax=ax[0], title="Receiver data")
|
|
113
|
+
DeepGPR.visualization.plot_model(eps_r.grad, ax=ax[1], title="Gradient", cmap="seismic")
|
|
114
|
+
plt.show()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+

|
|
118
|
+
|
|
119
|
+
More examples are in [`examples/`](examples) (forward modeling, 2D FWI, 3D FWI).
|
|
120
|
+
|
|
121
|
+
## Source wavelets
|
|
122
|
+
|
|
123
|
+
Wavelets live in `DeepGPR.wavelet`. Every function returns a one-dimensional
|
|
124
|
+
tensor and accepts optional `dtype` and `device` arguments.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
ricker = DeepGPR.wavelet.ricker(freq, nt, dt, peak_time, device=device)
|
|
128
|
+
gaussian = DeepGPR.wavelet.gaussian(freq, nt, dt, peak_time, device=device)
|
|
129
|
+
derivative = DeepGPR.wavelet.gaussian_derivative(freq, nt, dt, peak_time, device=device)
|
|
130
|
+
morlet = DeepGPR.wavelet.morlet(freq, nt, dt, peak_time, cycles=3.0, device=device)
|
|
131
|
+
burst = DeepGPR.wavelet.sine_burst(freq, nt, dt, peak_time, cycles=3.0, device=device)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`DeepGPR.ricker(...)` remains available as a backward-compatible alias.
|
|
135
|
+
|
|
136
|
+
## Full-waveform inversion results
|
|
137
|
+
|
|
138
|
+
The figures show the true model, the initial model and the inverted result.
|
|
139
|
+
|
|
140
|
+
### 2D FWI
|
|
141
|
+
|
|
142
|
+
| True model | Initial model | Inverted result |
|
|
143
|
+
| --- | --- | --- |
|
|
144
|
+
|  |  |  |
|
|
145
|
+
|
|
146
|
+
### 3D FWI (central slice)
|
|
147
|
+
|
|
148
|
+
| Model | Central slice |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| True model |  |
|
|
151
|
+
| Initial model |  |
|
|
152
|
+
| Inverted result |  |
|
|
153
|
+
|
|
154
|
+
## API overview
|
|
155
|
+
|
|
156
|
+
| Function | Purpose |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| `DeepGPR.compute(...)` | FDTD forward modeling with autograd (full reference: [`docs/API.md`](docs/API.md)) |
|
|
159
|
+
| `DeepGPR.checkpoint_initial_field(...)` | zero E/H/CPML states for segment-wise (checkpointed) runs |
|
|
160
|
+
| `DeepGPR.wavelet.*` | source wavelets |
|
|
161
|
+
| `DeepGPR.TVRegularization` | total-variation penalty for εr and σ (`"anisotropic"`, `"isotropic"`) |
|
|
162
|
+
| `DeepGPR.apply_filter`, `DeepGPR.hilbert_transform` | low-pass FIR and envelope for multi-scale FWI |
|
|
163
|
+
| `DeepGPR.max_stable_time_step(...)` | largest CFL-stable `dt` for a grid, order and material |
|
|
164
|
+
| `DeepGPR.estimate_compute_memory(...)` | memory estimate (also printed by `print_parameters=True`) |
|
|
165
|
+
| `DeepGPR.decompress_wavefield_history(...)` | decode a packed INT8 history for diagnostics |
|
|
166
|
+
| `DeepGPR.visualization.plot_*` | models, radargrams, traces, true/initial/inverted comparison |
|
|
167
|
+
| `DeepGPR.configure_logging(level)` | enable DeepGPR log output |
|
|
168
|
+
|
|
169
|
+
### Logging and errors
|
|
170
|
+
|
|
171
|
+
DeepGPR logs through the standard `logging` module under the `DeepGPR`
|
|
172
|
+
logger and never configures the root logger. Enable console output with
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
DeepGPR.configure_logging("INFO") # or "DEBUG" for per-call solver details
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Errors raised by the package derive from `DeepGPR.DeepGPRError` **and** from the
|
|
179
|
+
built-in type used before 0.1.0, e.g. `DeepGPR.CFLConditionError` is also a
|
|
180
|
+
`ValueError` and `DeepGPR.NativeLibraryError` is also a `RuntimeError`.
|
|
181
|
+
Failures inside the native CPU/CUDA code (allocation, CUDA API or launch
|
|
182
|
+
errors) are raised as `NativeLibraryError` with the native message.
|
|
183
|
+
|
|
184
|
+
## Project structure
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
src/DeepGPR/
|
|
188
|
+
├── __init__.py public API
|
|
189
|
+
├── _version.py package version
|
|
190
|
+
├── config/ physical constants, native ABI codes, API defaults
|
|
191
|
+
├── native/ library loader, declarative C ABI table, ctypes bridge
|
|
192
|
+
├── preprocessing/ input validation, grid/PML normalisation, CFL, wavelets
|
|
193
|
+
├── solver/ CPML, field state, history storage, autograd bridge, compute()
|
|
194
|
+
├── postprocessing/ FIR filter, envelope, forward-wavefield files
|
|
195
|
+
├── inversion/ TV regularisation
|
|
196
|
+
├── visualization/ matplotlib helpers
|
|
197
|
+
├── utils/ logging, exceptions, validators, diagnostics
|
|
198
|
+
├── lib/ deepgpr.cu, deepgpr_cpu.c, deepgpr.h and prebuilt binaries
|
|
199
|
+
├── common.py, compute2.py, multiscale.py, wavelet.py compatibility modules
|
|
200
|
+
docs/ API reference, build guide, code audit
|
|
201
|
+
examples/ forward, 2D FWI and 3D FWI notebooks
|
|
202
|
+
tests/ unit tests, verification notebooks, benchmarks
|
|
203
|
+
tools/ verify_equivalence.py
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Data flow of one `compute` call:
|
|
207
|
+
`preprocessing.model_setup.initialization` (validate, CFL, extend into CPML) →
|
|
208
|
+
`solver.pml` (CPML coefficients and state) → `solver.autograd.DeepGPR`
|
|
209
|
+
(native forward; native adjoint on `backward`) → receiver data, final states
|
|
210
|
+
and the saved history.
|
|
211
|
+
|
|
212
|
+
## Tests and verification
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
python -m unittest discover -s tests -p "test_*.py" # unit tests
|
|
216
|
+
python -m unittest tests/test_native_abi.py # ABI check, no PyTorch needed
|
|
217
|
+
python tests/run_notebook.py tests/00_local_backend_and_contracts.ipynb
|
|
218
|
+
python tools/verify_equivalence.py --reference-src /path/to/DeepGPR-0.0.20/src/DeepGPR
|
|
219
|
+
bash tools/run_local_validation.sh /path/to/DeepGPR-0.0.20 # rebuild CPU+CUDA libs, run everything
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Run the notebooks `00` to `09` in numeric order, followed by
|
|
223
|
+
`99_verification_summary.ipynb`. See [`tests/README.md`](tests/README.md) for
|
|
224
|
+
the complete matrix and CUDA options.
|
|
225
|
+
|
|
226
|
+
**Reproducibility.** The CPU backend gives bitwise identical forward results
|
|
227
|
+
and gradients for any `OMP_NUM_THREADS`. The CUDA adjoint accumulates with
|
|
228
|
+
atomics and can differ in the last bits between runs.
|
|
229
|
+
|
|
230
|
+
**Reproducing results from DeepGPR ≤ 0.0.20.** 0.1.0 corrects the FIR design
|
|
231
|
+
for even filter lengths, the envelope for odd trace lengths and the isotropic
|
|
232
|
+
TV (see [`CHANGELOG.md`](CHANGELOG.md)). Use `apply_filter(..., legacy=True)`,
|
|
233
|
+
`design_fir_filter(..., legacy=True)`, `hilbert_transform(..., legacy=True)`
|
|
234
|
+
and `TVRegularization(method="legacy_isotropic")` to reproduce earlier numbers.
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
- Code style: PEP 8, enforced with `ruff check src/DeepGPR tools` (configuration in `pyproject.toml`).
|
|
239
|
+
- Type hints throughout; `mypy` configuration in `pyproject.toml`.
|
|
240
|
+
- Changes are recorded in [`CHANGELOG.md`](CHANGELOG.md); known issues and recommended native work in [`docs/AUDIT.md`](docs/AUDIT.md).
|
|
241
|
+
- Changes to `lib/deepgpr.h` must be mirrored in `src/DeepGPR/native/abi.py`; `tests/test_native_abi.py` enforces it.
|
|
242
|
+
|
|
243
|
+
## Citation
|
|
244
|
+
|
|
245
|
+
If you find our code useful, please cite:
|
|
246
|
+
|
|
247
|
+
```bibtex
|
|
248
|
+
@article{liu2026fast,
|
|
249
|
+
title = {Fast ground penetrating radar dual-parameter full waveform inversion method accelerated by hybrid compilation of CUDA kernel function and PyTorch},
|
|
250
|
+
author = {Liu, Lei and Song, Chao and He, Liangsheng and Wang, Silin and Feng, Xuan and Liu, Cai},
|
|
251
|
+
journal = {Computers \& Geosciences},
|
|
252
|
+
pages = {106101},
|
|
253
|
+
year = {2026},
|
|
254
|
+
publisher = {Elsevier}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT — see [`license`](license).
|