|
| 1 | +Unified API (v0.6.0) |
| 2 | +==================== |
| 3 | + |
| 4 | +Starting with v0.6.0, ``kspaceFirstOrder()`` is the preferred way to run |
| 5 | +simulations. It replaces the legacy ``kspaceFirstOrder2D``, |
| 6 | +``kspaceFirstOrder3D``, and their GPU variants with a single function that |
| 7 | +auto-detects dimensionality from the grid. |
| 8 | + |
| 9 | +.. contents:: On this page |
| 10 | + :local: |
| 11 | + :depth: 2 |
| 12 | + |
| 13 | +Quick Start |
| 14 | +----------- |
| 15 | + |
| 16 | +.. code-block:: python |
| 17 | +
|
| 18 | + from kwave.kgrid import kWaveGrid |
| 19 | + from kwave.kmedium import kWaveMedium |
| 20 | + from kwave.ksource import kSource |
| 21 | + from kwave.ksensor import kSensor |
| 22 | + from kwave.kspaceFirstOrder import kspaceFirstOrder |
| 23 | + import numpy as np |
| 24 | +
|
| 25 | + # Setup (works for 1D, 2D, or 3D — just change grid dimensions) |
| 26 | + kgrid = kWaveGrid([128, 128], [0.1e-3, 0.1e-3]) |
| 27 | + kgrid.makeTime(1500) |
| 28 | +
|
| 29 | + medium = kWaveMedium(sound_speed=1500, density=1000) |
| 30 | +
|
| 31 | + source = kSource() |
| 32 | + source.p0 = np.zeros((128, 128)) |
| 33 | + source.p0[64, 64] = 1.0 |
| 34 | +
|
| 35 | + sensor = kSensor(mask=np.ones((128, 128), dtype=bool)) |
| 36 | +
|
| 37 | + # Run with the NumPy/CuPy solver in Python |
| 38 | + result = kspaceFirstOrder(kgrid, medium, source, sensor) |
| 39 | +
|
| 40 | + print(result["p"].shape) # (16384, Nt) — time series at each sensor |
| 41 | + print(result["p_final"].shape) # (128, 128) — final pressure field |
| 42 | +
|
| 43 | +Backend and Device |
| 44 | +------------------ |
| 45 | + |
| 46 | +Two independent choices control how the simulation runs: |
| 47 | + |
| 48 | +- **backend** selects the simulation engine: |
| 49 | + |
| 50 | + - ``"python"`` (default) — pure Python solver using NumPy or CuPy. |
| 51 | + No external dependencies beyond NumPy. |
| 52 | + - ``"cpp"`` — serializes to HDF5 and invokes the pre-compiled C++ binary. |
| 53 | + Requires FFTW (``brew install fftw`` on macOS). |
| 54 | + |
| 55 | +- **device** selects the hardware: |
| 56 | + |
| 57 | + - ``"cpu"`` (default) — NumPy for ``backend="python"``, OMP binary for |
| 58 | + ``backend="cpp"``. |
| 59 | + - ``"gpu"`` — CuPy for ``backend="python"`` (requires CuPy + CUDA), |
| 60 | + CUDA binary for ``backend="cpp"``. |
| 61 | + |
| 62 | +.. code-block:: python |
| 63 | +
|
| 64 | + # NumPy on CPU (default) |
| 65 | + result = kspaceFirstOrder(kgrid, medium, source, sensor) |
| 66 | +
|
| 67 | + # CuPy on GPU (requires CuPy + CUDA) |
| 68 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, device="gpu") |
| 69 | +
|
| 70 | + # C++ binary on CPU (requires FFTW) |
| 71 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, backend="cpp") |
| 72 | +
|
| 73 | + # C++ CUDA binary on GPU |
| 74 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, backend="cpp", device="gpu") |
| 75 | +
|
| 76 | +PML Options |
| 77 | +----------- |
| 78 | + |
| 79 | +The perfectly-matched layer (PML) absorbs outgoing waves at the domain |
| 80 | +boundary. Three controls: |
| 81 | + |
| 82 | +.. code-block:: python |
| 83 | +
|
| 84 | + # Fixed size (default: 20 grid points on all sides) |
| 85 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, pml_size=20) |
| 86 | +
|
| 87 | + # Per-dimension sizes |
| 88 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, pml_size=(10, 15)) |
| 89 | +
|
| 90 | + # Auto-optimal size (computed from grid via FFT analysis) |
| 91 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, pml_size="auto") |
| 92 | +
|
| 93 | + # PML inside the user domain (saves memory, but PML overlaps your grid) |
| 94 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, pml_inside=True) |
| 95 | +
|
| 96 | +By default (``pml_inside=False``), the grid is automatically expanded so |
| 97 | +the PML sits outside your domain. Full-grid output fields (``_final``, |
| 98 | +``_max``, etc.) are cropped back to the original size. |
| 99 | + |
| 100 | +Save-Only Mode (Cluster Submission) |
| 101 | +------------------------------------ |
| 102 | + |
| 103 | +Generate HDF5 input files for the C++ binary without running it: |
| 104 | + |
| 105 | +.. code-block:: python |
| 106 | +
|
| 107 | + result = kspaceFirstOrder( |
| 108 | + kgrid, medium, source, sensor, |
| 109 | + backend="cpp", |
| 110 | + save_only=True, |
| 111 | + data_path="/path/to/output", |
| 112 | + ) |
| 113 | + print(result["input_file"]) # path to HDF5 input |
| 114 | + print(result["output_file"]) # path where C++ will write results |
| 115 | +
|
| 116 | +Full Parameter Reference |
| 117 | +------------------------ |
| 118 | + |
| 119 | +.. autofunction:: kwave.kspaceFirstOrder.kspaceFirstOrder |
| 120 | + |
| 121 | +Migrating from Legacy API |
| 122 | +-------------------------- |
| 123 | + |
| 124 | +The ``kwave.compat`` module provides ``options_to_kwargs()`` to convert |
| 125 | +old ``SimulationOptions`` / ``SimulationExecutionOptions`` to keyword |
| 126 | +arguments: |
| 127 | + |
| 128 | +.. code-block:: python |
| 129 | +
|
| 130 | + from kwave.compat import options_to_kwargs |
| 131 | + from kwave.options.simulation_options import SimulationOptions |
| 132 | + from kwave.options.simulation_execution_options import SimulationExecutionOptions |
| 133 | +
|
| 134 | + sim_opts = SimulationOptions(smooth_p0=False, pml_inside=True) |
| 135 | + exec_opts = SimulationExecutionOptions(is_gpu_simulation=False) |
| 136 | +
|
| 137 | + kwargs = options_to_kwargs(simulation_options=sim_opts, execution_options=exec_opts) |
| 138 | + result = kspaceFirstOrder(kgrid, medium, source, sensor, **kwargs) |
| 139 | +
|
| 140 | +The legacy ``kspaceFirstOrder2D`` and ``kspaceFirstOrder3D`` functions |
| 141 | +continue to work but emit ``DeprecationWarning``. |
0 commit comments