beamax.solvers.kwave_solver
Two- and three-dimensional k-wave-python integration. Install with
pip install "beamax[kwave]".
beamax.solvers.kwave_solver
KWaveSolver(simulation_options=None, execution_options=None, **kwargs)
:mod:k-wave-python wrapper (2D/3D) for forward, time-reversal, and adjoint solves.
Used as the reference solver in examples and as the low-frequency leg of
:class:HybridSolver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulation_options
|
SimulationOptions
|
Legacy simulation-options object. It cannot be mixed with unified keyword options. |
None
|
execution_options
|
SimulationExecutionOptions
|
Legacy execution-options object. It cannot be mixed with unified keyword options. |
None
|
**kwargs
|
Unified k-Wave arguments forwarded to
:func: |
{}
|
Notes
- Requires the
[kwave]extra (pip install 'beamax[kwave]'). - Boundary handling uses a PML; :attr:
Domain.periodicflags control PML-inside vs. PML-outside placement via the k-Wave options. - Forward solves run on the C++ backend by default. Time-reversal falls
back to the pure-Python backend until the upstream
CppSimulationpath ships source preprocessing for time-varying pressure sources. The adjoint also uses the Python backend and applies the Appendix-B source and terminal-field scalings needed for the discrete transpose. binary_pathcan be supplied directly or viaBEAMAX_KWAVE_BINARY_PATH. Direct kwargs take precedence.
Examples:
>>> import jax.numpy as jnp
>>> from beamax import Domain, Sensor
>>> from beamax.solvers import KWaveSolver
>>> domain = Domain(N=(64, 64), dx=(1e-3, 1e-3), c=1500.0, periodic=(False, False))
>>> solver = KWaveSolver(pml_size=10, device="cpu")
Initialize k-Wave solver options.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulation_options
|
SimulationOptions
|
Legacy simulation options object. |
None
|
execution_options
|
SimulationExecutionOptions
|
Legacy execution options object. |
None
|
**kwargs
|
Unified keyword options forwarded to
:func: |
{}
|
forward(p0: Union[np.ndarray, jnp.ndarray], domain: Domain, sensors: Union[np.ndarray, jnp.ndarray], ts: Union[np.ndarray, jnp.ndarray], *, record: str = 'p') -> np.ndarray
Forward k-Wave simulation for linear wave equation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p0
|
array
|
Initial pressure. |
required |
domain
|
Domain
|
|
required |
sensors
|
array
|
Sensor mask or positions (solver expects mask). |
required |
ts
|
(array, shape(Nt))
|
|
required |
record
|
('p', ...)
|
Quantity to record. |
"p"
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Sensor time series |
time_reversal(data: Union[np.ndarray, jnp.ndarray], domain, sensors, sources, ts: Union[np.ndarray, jnp.ndarray], *, record: str = 'p_final', data_layout: str = 'auto') -> np.ndarray
Run classic k-Wave time reversal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
(ndarray or ndarray, shape(Ns, Nt) or (Nt, Ns))
|
Sensor measurements. |
required |
domain
|
Domain
|
Reconstruction domain. |
required |
sensors
|
array - like
|
Sensor mask. |
required |
sources
|
array - like
|
Source mask where the reversed data are injected. |
required |
ts
|
(ndarray or ndarray, shape(Nt))
|
Time grid. |
required |
record
|
str
|
k-Wave field to return. |
"p_final"
|
data_layout
|
('auto', 'ns_nt', 'nt_ns')
|
Sensor-data layout interpretation. |
"auto"
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Requested k-Wave record. |
Notes
Enforces \(p(\mathbf{x}_s,t)=d(t,\mathbf{x}_s)\) as a Dirichlet source,
where \(d\) denotes sensor_data.
adjoint(data: Union[np.ndarray, jnp.ndarray], domain, sensors, sources, ts: Union[np.ndarray, jnp.ndarray], *, record: str = 'p_final', data_layout: str = 'auto') -> np.ndarray
Discrete k-Wave adjoint following Arridge et al., Appendix B.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
(array, shape(Ns, Nt) or (Nt, Ns))
|
Sensor measurements. |
required |
domain
|
Domain
|
|
required |
sensors
|
array
|
Sensor mask. |
required |
sources
|
array
|
Source mask. |
required |
ts
|
(array, shape(Nt))
|
|
required |
record
|
'p_final'
|
Terminal pressure field. Other records cannot be converted to the initial-pressure transpose and are therefore rejected. |
"p_final"
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Euclidean discrete adjoint image \(A^{\mathsf T}d\)
( |
Notes
The returned field is the algebraic transpose under unweighted discrete sums. To represent a one-cell planar detector under the continuous surface and volume rectangle rules of Arridge et al. (2016), multiply this result by \(\Delta t/\Delta x_{\mathrm{normal}}\).
The source is Eq. (B.2) of Arridge et al. (2016). k-Wave's additive pressure-source preprocessing multiplies a user source by \(2\Delta t/(d\,c\,\Delta x)\) before adding it to each split density field. Consequently the user source must be
and the terminal pressure must be divided pointwise by
\(c^2\rho\). additive-no-correction is intentional: the optional
cosine k-space filter on the injected source is not part of Eq. (B.2).
This mode still applies k-Wave's additive-source amplitude scaling and
does not disable the sinc k-space correction used by the propagation
operators.
The current k-wave-python Python backend uses a different pressure scaling for each spatial axis on anisotropic grids. A single scalar pressure signal cannot then create the equal split-density increments required by Eq. (B.2), so this implementation requires isotropic grid spacing.