Scalar propagator reference

The scalar and scalar_born propagators, in both their Module class and functional forms. Usage covers the conventions they share with every other propagator, and Scalar Wave Equation and Born Modelling with the Scalar Wave Equation cover the physics.

Scalar Class

class deepwave.Scalar(v: Tensor, grid_spacing: float | Sequence[float], *, requires_grad: bool | Sequence[str] | None = None, v_requires_grad: bool | None = None, storage: str | Storage | None = None, storage_mode: Literal['device', 'cpu', 'disk', 'none'] | None = None, storage_path: str | PathLike[str] | None = None, storage_compression: bool | None = None)

Convenience nn.Module wrapper for scalar wave propagation.

Stability: stable (see Stability policy).

Stores v and grid_spacing once, so that each forward() call needs only the arguments that vary between calls.

Deepwave orders spatial dimensions from slowest-varying to fastest-varying and attaches no physical meaning to any of them; the component suffixes count from the fastest axis, so a 2-D run uses _y and _x and a 3-D run uses _z, _y and _x. See Dimension order and the axis convention.

Parameters:
  • v – The wavespeed model, of shape [*model_shape] or, for a batched model, [n_shots, *model_shape], where model_shape has ndim entries. See Batched models.

  • grid_spacing – The spatial grid cell size. It can be a single number that will be used for all dimensions, or a number for each dimension.

  • requires_grad – Which of this module’s models require a gradient. None (the default) defers entirely to the individual <model>_requires_grad flags below, each of which is unset (None) by default and then means False. True selects every model this module owns, False selects none, and a sequence of model names selects exactly those. Passing it together with an individual flag that was given explicitly raises ValueError rather than letting one silently win – whichever way the two point, and even when they agree, so that there is one rule rather than a direction-dependent one.

  • v_requires_grad – A bool specifying whether gradients will be computed for v. Unset by default, which means False. Cannot be combined with requires_grad.

  • storage – The storage policy for the wavefield history that the model gradient is computed from, as one object: either a mode string (“device”, “cpu”, “disk” or “none”) or a deepwave.Storage, which also carries the path and the lossy-compression flag. Defaults to “device”. This is an alternative spelling for the four individual storage_* arguments below, which remain permanently supported and are not deprecated; passing storage together with any of them raises DeepwaveValueError, because there is no honest way to merge two policies.

  • storage_mode – A string specifying the storage mode for intermediate data. One of “device”, “cpu”, “disk”, or “none”. Defaults to “device”. A permanent alias for Storage.mode; not deprecated.

  • storage_path – The directory that storage_mode=”disk” writes to. A string or any os.PathLike (such as a pathlib.Path). Defaults to the system temporary directory, resolved when you call rather than when Deepwave is imported, so TMPDIR set at any point before the call is honoured. Using the default with storage_mode=”disk” emits a one-time warning naming the directory, because on many systems it is a RAM-backed tmpfs far smaller than the disk. A permanent alias for Storage.path; not deprecated.

  • storage_compression – Lossy: it changes the model gradient, by about 0.2-4% in 2-D and 0.2-8% in 3-D in relative L2 error. The name does not say so, which is why this entry does. If True, the sampled wavefield history kept for the model gradient is stored as 8-bit block max-abs quantisation (8 cells per spatial dimension per block, one full-precision scale per block). Measured relative L2 error against an uncompressed run: about 0.2-4% for 2-D problems and 0.2-8% for 3-D. For a matched geometry 3-D is consistently two to five times the 2-D error – a 3-D block shares one scale across 512 cells against 64 in 2-D – but the spread across geometries and loss functions is wide enough that the two ranges overlap. The error does not grow with the size of the problem: with the survey and the model anomaly held at fixed cell indices it is constant to five significant figures from 32x32 to 400x400 and from 32^3 to 120^3, and constant in nt once the record is long enough for the wavefield to reach the receivers. It does depend on the survey geometry and on the loss function – one fixed 3-D problem gives 3.4% under a sum of squared receiver amplitudes and 1.9% under a data residual – so treat these as ranges rather than a number to budget against. A barely illuminated gradient is the exception: a record too short for the wavefield to reach the receivers at all can give tens of percent, because there the gradient itself is nearly empty. The relative maximum error, defined as max|g_comp - g_exact| / max|g_exact|, is comparable to the relative L2 error rather than a fixed multiple of it – typically between 0.3 and 3 times it, and in 3-D more often below the L2 error than above. Do not use a per-cell relative error as a figure of merit: it is unbounded near the gradient’s zero crossings. The error is essentially independent of dtype (float32 and float64 agree to three or four significant figures) and exactly independent of storage_mode. The worst-affected values are the outermost cells of the model: a cell’s gradient there sums large, largely cancelling contributions from the absorbing layer, so the quantisation error does not cancel with them. Measured on the gradient of scalar_born’s dv, normalised by the gradient’s own maximum, the largest edge-cell error is 0.03-0.07 in 1-D, about 1e-4 in 2-D and about 1e-5 in 3-D, against below 1e-3, 2e-5 and 1e-5 respectively in the interior. Disabling compression removes quantisation error; it does not remove model-gradient sampling error. Exact discrete model derivatives also require an effective history stride of one and fixed setup parameters (see What Deepwave guarantees about gradients). Unaffected, bit for bit: the returned wavefields and auxiliary state, the receiver amplitudes, and the gradients with respect to the source amplitudes and the initial wavefields – compression touches only the stored history that forms the model gradient. Do not use it for `gradcheck`, adjoint or dot-product tests, or publication-quality gradients. In exchange it shrinks the offloaded host buffer by close to the size of the dtype in bytes – each cell becomes one byte, plus one full-precision scale per block – which is about 3.8x in 2-D and 4.0x in 3-D for float32, and about 7.1x and 7.9x for float64. That ratio is set by the dtype and the number of spatial dimensions, not by the size of the problem. Composed with storage_mode=”cpu” it runs roughly 2 to 3 times faster than uncompressed offload, because it reduces the PCIe traffic that offload is bottlenecked on. Defaults to False.

Model ownership and gradients

Every model tensor given to the constructor is wrapped in a new torch.nn.Parameter that this module owns. The Parameter is a distinct leaf tensor that shares storage with the tensor you passed in: it is not a copy, so an in-place edit of one is visible in the other, but it is not the same object. Gradients therefore accumulate on the module’s own attribute (module.<model>.grad) and never on the tensor you passed in. A model requires a gradient only if you ask for one, through the constructor’s requires_grad argument or by calling module.<model>.requires_grad_(True) afterwards; hand module.parameters() to a torch.optim optimiser as usual. An optional model left as None is not a Parameter at all, so it appears in neither parameters() nor state_dict(); assigning a tensor to that attribute later stores a plain tensor rather than registering a Parameter, so it stays out of parameters() and state_dict() (and out of any checkpoint) – give the model to the constructor, or wrap it yourself in torch.nn.Parameter. Because the module owns a leaf Parameter, it cannot carry an upstream autograd graph: if the model is itself produced by another operation – a network mapping a latent vector to a wavespeed, a reparameterisation, a smoothing filter – wrapping it in a module severs that graph and no gradient reaches the producer. Use the functional interface for anything graph-producing; it takes the model as an ordinary tensor argument and backpropagates through it.

Initialises the Module.

forward(dt: float, *, sources: Mapping[str, tuple[Tensor, Tensor | Hicks] | tuple[Tensor, Tensor | Hicks, Tensor | None]] | None = None, receivers: Mapping[str, Tensor | Hicks | tuple[Tensor | Hicks, Tensor | None]] | None = None, source_amplitudes: Tensor | None = None, source_locations: Tensor | Hicks | None = None, source_mask: Tensor | None = None, receiver_locations: Tensor | Hicks | None = None, receiver_mask: Tensor | None = None, wavefield_0: Tensor | None = None, wavefield_m1: Tensor | None = None, initial_state: PropagatorOutput | Mapping[str, Tensor | AuxiliaryState] | None = None, accuracy: Literal[2, 4, 6, 8] = 4, pml_width: int | Sequence[int] = 20, boundary: str | Sequence[str] | None = None, pml_freq: float | None = None, max_vel: float | None = None, courant_number: float | None = None, survey_pad: int | Sequence[int | None] | None = None, origin: Sequence[int] | None = None, nt: int | None = None, model_gradient_sampling_interval: int = 1, checkpoint_segments: int | None = None, freq_taper_frac: float = 0.0, time_pad_frac: float = 0.0, time_taper: bool = False, check_output_finite: bool = True, forward_callback: Callback | None = None, backward_callback: Callback | None = None, callback_frequency: int = 1, backward_callback_needs_forward_wavefield: bool = False, backend: str = 'auto', python_backend: Literal['eager', 'jit', 'compile'] | bool | None = None, storage: str | Storage | None = None, storage_mode: Literal['device', 'cpu', 'disk', 'none'] | None = None, storage_path: str | PathLike[str] | None = None, storage_compression: bool | None = None, location_interpolation: Literal['auto', 'hicks', 'none'] = 'auto', location_interpolation_halfwidth: int = 4) → PropagatorOutput

Perform forward propagation/modelling.

See scalar() for details (the constructor-owned v/ grid_spacing and the storage settings are supplied from the stored module state).

Parameters:
  • dt – A float specifying the time step interval of the input and output (internally a smaller interval may be used in propagation to obey the CFL (Courant-Friedrichs-Lewy) condition for stability). Deepwave is unit-agnostic: choose any single consistent system of units for v, grid_spacing, and dt; see Units and source conventions.

  • sources – This simulation’s sources, keyed by channel. This is the canonical way to specify a survey. Each value is (amplitudes, locations), or (amplitudes, locations, mask) to include an activity mask. This propagator has one channel, u (the scalar wavefield), in every dimensionality, spelled that way however this propagator’s own flat keywords are spelled (they carry no channel suffix at all). Exactly equivalent to the flat source_amplitudes/source_locations/source_mask keywords, which remain permanently supported; giving the channel in both forms raises. Optional.

  • receivers – This simulation’s receivers, keyed by channel, as the canonical alternative to the flat receiver_locations/receiver_mask keywords. Each value is a locations object, or (locations, mask) to include an activity mask. The one channel is u. Giving the channel in both forms raises. Optional.

  • source_amplitudes – A torch.Tensor with dimensions [shot, source, time], holding the right-hand side of the scalar wave equation; its units set the units of the receiver data, and Units and source conventions gives the calibration. For example, if two shots are propagated simultaneously, each containing three sources of one hundred time samples, the shape would be [2, 3, 100]. The time dimension length determines the number of time steps in the simulation. Optional. If provided, source_locations must also be specified. If not provided, nt must be specified.

  • source_locations – A torch.Tensor with dimensions [shot, source, ndim], containing the index in the ndim spatial dimensions of the cell that each source is located in, relative to the origin of the model. Optional. Must be provided if source_amplitudes is. It usually has torch.long (int64) datatype, giving integer grid cell indices; a floating-point tensor may instead be provided to place sources at off-grid positions, handled according to the location_interpolation argument. For full control (including dipole sources and free surfaces) a deepwave.Hicks object may be passed here instead of a tensor, in which case the propagator performs the interpolation spread and gather for you (provide the source amplitudes at the original, un-interpolated locations). The location of each source must be unique within the same shot (you cannot have two sources in the same shot that both have location [1, 2], for example). A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active. True means active; False means that point injects nothing, and its coordinates in source_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of source_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • receiver_locations – A torch.Tensor with dimensions [shot, receiver, ndim], containing the coordinates of the cell containing each receiver. Optional. It usually has torch.long (int64) datatype; a floating-point tensor may instead be provided (see source_locations and location_interpolation). If not provided, the output receiver_amplitudes torch.Tensor will be empty. If backpropagation will be performed, the location of each receiver must be unique within the same shot. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active. True means active; False means that point records zeros, and its coordinates in receiver_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of receiver_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • wavefield_0 – A torch.Tensor specifying the initial wavefield at time step 0. It should have (ndim + 1) dimensions, with the first dimension being shot and the subsequent ones corresponding to the spatial dimensions. Two spatial shapes are accepted. The first is the simulation domain, which is the extracted model region plus the PML: if two shots are being propagated simultaneously in a region of size [20, 30] extracted from the model for the simulation, and pml_width=[1, 2, 3, 4], then wavefield_0 would be of shape [2, 23, 37]. This is the shape a previous call returns, so it is the shape to use when continuing a simulation. The second is the extracted model region itself ([2, 20, 30] in that example), which Deepwave zero-pads for you by pml_width on each face; this is usually the convenient shape when you are specifying an initial condition rather than continuing a run, such as the initial pressure of a photoacoustic simulation. Because a wavefield determines the region that is extracted when survey_pad is not given, a shape that could be read either way is read as the simulation domain, which is what it has always meant, and a warning names both readings and the region each would select. Providing origin does not remove that ambiguity – origin says where the region begins, not whether the shape includes the PML – so the warning is issued whether or not you provide one. Passing initial_state from a previous call is never ambiguous, since the auxiliary state it carries records the origin and PML width it was produced with. Optional, default all zeros.

  • wavefield_m1 – A torch.Tensor specifying the initial wavefield at time step -1 (using Deepwave’s internal time step interval, which may be smaller than the user provided one to obey the CFL condition). See the entry for wavefield_0 for more details.

  • initial_state –

    An optional bundle of initial wavefields, supplied as either a deepwave.PropagatorOutput (the return of a previous call), that output’s final_state mapping, or any mapping whose keys are the public state field names (wavefield_0, wavefield_m1) and auxiliary_state (a deepwave.AuxiliaryState from a previous call). Each entry fills the correspondingly-named initial wavefield that was not also passed explicitly. Because the output wavefields are named identically to these arguments, passing a previous output back in as initial_state continues the simulation seamlessly (its receiver_amplitudes field is ignored), and it is the unambiguous route: the auxiliary state records the propagator, accuracy, PML width, boundary conditions, number of relaxation mechanisms, simulation-domain shape and internal time step it was produced with, so a continuation that does not match them raises rather than producing quiet nonsense, and one whose pml_freq or max_vel differs – which reshapes the absorbing boundary without changing what the tensors are – warns. The survey origin is recorded and reused: see the continuation rule below.

    Continuation rule. When initial_state carries a region – a PropagatorOutput from a previous call, whose RunInfo.origin and RunInfo.extent record the region that run used, or a AuxiliaryState carrying the same – and you pass neither survey_pad nor origin, that stored region is reused: the continuation propagates exactly the same part of the model, and the new call’s sources and receivers must lie inside it (they are checked; a location outside it raises). If you pass survey_pad or origin explicitly, your value wins and the stored wavefields must match the region it implies; a disagreement raises rather than silently re-windowing the simulation. Passing the previous call’s output (initial_state=out) is the unambiguous route. A mapping of bare tensors records no region at all, so it warns and assumes origin zero unless you pass origin.

    The individual absorbing-boundary field names (psiz_m1, …, zetax_m1) are still accepted as keys, with a FutureWarning; they will stop being accepted in Deepwave 0.4.0. Providing a wavefield both here and as an explicit argument raises an error. For the continuation to exactly reproduce a single run over the combined time range, the same internal time step and the same absorbing boundary must be used throughout. The first holds when the user dt already satisfies the CFL condition (no internal sub-stepping); otherwise the segment boundary introduces a small discretisation difference. The second is automatic: when pml_freq or max_vel is omitted, the value recorded in initial_state is used, rather than one inferred from this call’s own portion of the source record or from its model, so the continuation builds the same absorbing boundary as the run it continues. Passing either explicitly overrides that, and a mismatch with the recorded value then warns; a mismatched internal time step raises. If initial_state is a PropagatorOutput and neither origin nor survey_pad is also passed, the survey origin used to produce it is recovered automatically, so the continuation extracts the same region of the model rather than assuming an origin of zero.

    The number of shots (the leading dimension of every per-shot tensor) is taken from the source locations if any are given, otherwise from the initial wavefields. So a call with nt and no sources takes its shot count from the wavefields given here; with neither sources nor initial wavefields the call raises. Optional, default None.

  • accuracy – An int specifying the finite difference order of accuracy. Possible values are 2, 4, 6, and 8, with larger numbers resulting in more accurate results but greater computational cost. Optional, with a default of 4.

  • pml_width –

    A single number, or two numbers for each dimension, specifying the width (in number of cells) of the PML that prevents reflections from the edges of the model. If a single value is provided, it will be used for all edges. If a sequence is provided, it should contain values for the edges in the following order:

    • beginning of first (slowest) dimension

    • end of first dimension

    • beginning of second dimension

    • end of second dimension

    • …

    Larger values result in smaller reflections, with values of 10 to 20 being typical. To make an edge reflective instead of absorbing, set the value for that edge to be zero; for the scalar propagator such an edge is a free surface (it reflects with inverted polarity, a reflection coefficient of approximately -1). For example, if your 2D model is oriented so that the surface that you want to be reflective is the beginning of the second dimension, then you could specify pml_width=[20, 20, 0, 20]. The wavespeed in the PML region is obtained by replicating the values on the edge of the model. Optional, default 20. A face whose pml_width is zero does not absorb: it becomes an implicit reflecting boundary whose type depends on the propagator and the field component. See Reflecting edges for the per-propagator reflection coefficient and surface location, and prefer naming the condition with boundary (or deepwave.boundaries()), which also uses the exact image rather than this approximate one.

  • boundary – An optional single string, applying to every face, or a list of 2 * ndim strings selecting a first-class boundary condition per face, in the same order as pml_width. Each entry is one of “pml” (absorbing; boundary=”pml” is the spelled-out default, and behaves in every respect – including the warnings it emits – as omitting boundary does), “free” (a pressure-release surface: the field is held to zero on the boundary and reflects with inverted polarity, reflection coefficient -1), “rigid” (a rigid wall: the field reflects with preserved polarity, +1), or “periodic” (the face wraps around to the opposite face). A “free”, “rigid”, or “periodic” face requires pml_width to be zero on that face, and “periodic” must be set on both faces of a dimension. Unlike a bare zero-pml_width face (which is only an approximate reflector at accuracy above 2), a “free”/”rigid” face fills the exterior with the exact odd/even image at every accuracy order, so the analytic reflection coefficient is met to better than 1%. See the boundary-conditions documentation for the precise ghost-plane conventions. Optional, default None. Availability: this propagator accepts all four names, on every face, at every accuracy.

  • pml_freq –

    The frequency to use when constructing the PML, usually the dominant frequency of the source wavelet: the PML absorbs best near this frequency, and choosing it poorly makes the edges more reflective. Optional. If not specified, it is inferred from the peak of the source amplitudes’ spectrum (which returns the wavelet’s own centre frequency for a Ricker), or, if the simulation has no sources at all, from 0.5 / dt / 5, a fifth of the Nyquist frequency. Nothing is inferred when the call continues an earlier one: an omitted pml_freq then takes the value recorded in initial_state. The value actually used is reported as out.info.pml_freq. Pin it explicitly for any published result: leaving it inferred makes the absorbing boundary depend on the wavelet, so changing the wavelet also changes the boundary. The source-amplitude gradient does not follow that path, so pin pml_freq when you differentiate with respect to the source. See Choosing pml_freq.

    Note

    Deepwave 0.0.27 and earlier used a fixed 25 Hz here. A seismic call with a 25 Hz wavelet therefore infers exactly 25.0 and gets the same numbers as before; a call whose inferred value is anything else warns once, naming pml_freq=25.0 as the line that restores the old behaviour. That warning is a migration aid and is removed in 0.4.0.

  • max_vel – A float specifying the maximum velocity, which is used when applying the CFL condition and when constructing the PML. If not specified, the actual maximum absolute wavespeed in the model (or portion of it that is used) will be used. The option to specify this is provided to allow you to ensure consistency between runs even if there are changes in the wavespeed. Optional, default None. Leaving this None makes the discretisation a property of the current model: the internal time step (and therefore RunInfo.step_ratio and the quadrature of the model gradient) changes as the model updates, so an inversion’s objective is not exactly comparable between iterations. Pass an explicit max_vel – the maximum wavespeed you expect over the whole inversion – for a reproducible objective. Nothing is read from the model when the call continues an earlier one: an omitted max_vel then takes the value recorded in initial_state. The value actually used is reported as RunInfo.max_vel. A value below the model’s actual maximum warns and risks an unstable (diverging or NaN) simulation, since the CFL condition is computed from max_vel. It has to be given when v is batched by torch.func.vmap(), which hides the values the maximum is otherwise read from. Every other torch.func transform (torch.func.grad(), torch.func.jacrev(), torch.func.jvp()) leaves them readable, so nothing has to be supplied under those. The maximum arrives as a Python number, so the absorbing boundary and the internal time step it chooses are constants of the call rather than differentiable functions of the model. When it is inferred, the model gradient at the cell the maximum is read from omits the term that acts through those two; every other cell’s gradient is unaffected. Passing max_vel removes that path. An exact model gradient of the fixed discrete call also requires internal-step sampling and lossless storage; see What Deepwave Calculates. Capability matrix lists, for each propagator, the models the maximum is read from.

  • courant_number – A float specifying the Courant number to use for the CFL time-step calculation. If None (the default), the conservative deepwave.DEFAULT_COURANT_NUMBER is used for all accuracy orders (the historical behaviour, reproduced bit-for-bit). If given, it is the actual dimensionless Courant number that the internal time step will target; a larger value permits a larger internal time step (fewer internal steps, faster) but reduces the stability margin. It is validated against the critical Courant number for the chosen accuracy order and errors if the value would be unstable. See the discretisation documentation for the critical Courant numbers. Optional, default None.

  • survey_pad –

    A single value or list of two values for each dimension, all of which are either an int or None, specifying whether the simulation domain should be restricted to a region surrounding the sources and receivers. If you have a large model, but the sources and receivers of individual shots only cover a small portion of it (such as in a towed streamer survey), then it may be wasteful to simulate wave propagation over the whole model. This parameter allows you to specify what distance around sources and receivers you would like to extract from the model to use for the simulation. A value of None means that no restriction of the model should be performed in that direction, while an integer value specifies the minimum number of cells that the edge of the simulation domain should be from any source or receiver in that direction (if possible). If a single value is provided, it applies to all directions, so specifying None will use the whole model for every simulation, while specifying 10 will cause the simulation domain to contain at least 10 cells of padding in each direction around sources and receivers if possible. The padding will end if the edge of the model is encountered. Specifying a list, in the following order, allows the padding in each direction to be controlled:

    • beginning of first (slowest) dimension

    • end of first dimension

    • beginning of second dimension

    • end of second dimension

    • …

    Ints and None may be mixed, so a survey_pad of [5, None, None, 10] means that there should be at least 5 cells of padding towards the beginning of the first dimension, no restriction of the simulation domain towards the end of the first dimension or beginning of the second, and 10 cells of padding towards the end of the second dimension. If the simulation contains one source at [20, 15], one receiver at [10, 15], and the model is of shape [40, 50], then the extracted simulation domain with this value of survey_pad would cover the region [5:40, 0:25]. The same simulation domain will be used for all shots propagated simultaneously, so if this option is used then it is advisable to propagate shots that cover similar regions of the model simultaneously so that it provides a benefit (if the shots cover opposite ends of a dimension, then that whole dimension will be used regardless of what survey_pad value is used). Optional, default None. Cannot be specified if origin is also specified. Passing it alongside an initial_state that records a different region raises, rather than silently re-windowing the simulation: see the continuation rule under initial_state.

  • origin – A list of ints specifying the origin of the provided initial wavefields relative to the origin of the model. Only relevant if initial wavefields are provided. The origin of a wavefield is the cell where the extracted model used for the simulation domain began. It does not include the PML. So if a simulation is performed using the model region [10:20, 15:30], the origin is [10, 15]. Optional, default all zero (or, if initial_state records a region and survey_pad is not specified, the origin recorded by that previous call – see the continuation rule under initial_state). Passing an origin that disagrees with the one initial_state records raises. Cannot be specified if survey_pad is also specified.

  • nt –

    The number of time steps to run the simulation for. Optional: if it is not given, the length of the source amplitudes is used, so a call with sources need not repeat it.

    When both are given, nt is the length of the run and the source amplitudes are fitted to it in your own time samples, before any internal resampling: a record shorter than nt is zero-padded at the end, so the sources fall silent and the propagation continues; a longer one is truncated, so the samples beyond nt are never injected and no gradient reaches them. The result is bit-for-bit what passing a hand-padded or hand-truncated record would have produced. out.info.nt reports the length the run actually used.

    The number of shots (the leading dimension of every per-shot tensor) is taken from the source locations if any are given, otherwise from the initial wavefields. So with nt and no sources, the initial wavefields set it; with neither sources nor initial wavefields the call raises.

  • model_gradient_sampling_interval –

    An int specifying the number of time steps between contributions to the model gradient. The gradient with respect to the model is an integral over the backpropagation time steps. Under band-limited forward and adjoint fields, a sampling rate greater than twice their highest frequency suffices for the full-time correlation integral. If the signal bandwidth is well below the data Nyquist frequency, a larger interval can reduce computational (especially memory) costs. Finite-record endpoints and arbitrary initial states can violate the ideal assumptions; see docs/what_deepwave_calculates.rst. Optional, default 1 (integral is sampled every time step interval dt).

    Two things to know. First, the CFL step_ratio multiplies this: the quadrature stride the kernel actually uses is step_ratio * model_gradient_sampling_interval, so a dt above the CFL limit already coarsens the gradient integral before this argument is applied (see docs/what_deepwave_calculates.rst). Second, it applies only to the compiled backend: the pure-Python backends (backend=”eager”/”torch_compile”) step every internal time step and rely on native autograd, so they return the exact discrete adjoint and ignore this argument entirely. The two can therefore differ for the same arguments when the effective stride exceeds 1. Band-limited wavefields can justify this sampling for the time integral, but finite-record transients and energy near the sampling limit can cause material differences; source bandwidth alone does not provide a universal error bound.

    It must divide nt whenever a model requires a gradient, since the stride is also the engine’s outer time step; a remainder would leave the final nt % model_gradient_sampling_interval steps unpropagated, so it raises instead. When no model requires a gradient (including any run under torch.no_grad()) it is reset to 1 and ignored. It does not alter callback observation times; callback_frequency always counts user time steps.

  • checkpoint_segments –

    If given, split the time axis into this many segments and run each of them under torch.utils.checkpoint, which is the memory lever for a run whose wavefield history does not fit: peak backward memory falls to roughly one segment’s history plus the states held at the segment boundaries, at the cost of running the forward pass of each segment a second time during the backward pass. None (the default) runs the whole time axis in one piece, exactly as before. It is the same thing as calling deepwave.checkpointing.run_segments() with n_segments= and checkpoint=True, and returns the same combined output – the same fields, the same info and the same auxiliary_state a direct call gives. initial_state, the storage arguments and every physics option are supported; nt may pad or truncate supplied source amplitudes for the whole run. Source-free nt calls, individual initial-wavefield arguments and per-step callbacks are not supported, and say so.

    Take the gradient with loss.backward(). The checkpointing is reentrant, and torch.utils.checkpoint does not support torch.autograd.grad() (or an inputs= argument to .backward()) in that mode: it raises. If you need torch.autograd.grad, call deepwave.checkpointing.run_segments() directly with use_reentrant=False.

  • freq_taper_frac – A float specifying the fraction of the end of the source and receiver amplitudes (if present) in the frequency domain to cosine taper if they are resampled due to the CFL condition. This might be useful to reduce ringing. A value of 0.1 means that the top 10% of frequencies will be tapered. Default 0.0 (no tapering).

  • time_pad_frac – A float specifying the amount of zero padding that will be added to the source and receiver amplitudes (if present) before resampling and removed afterwards, if they are resampled due to the CFL condition, as a fraction of their length. This might be useful to reduce wraparound artifacts. A value of 0.1 means that zero padding of 10% of the number of time samples will be used. Default 0.0.

  • time_taper – A bool specifying whether to apply a Hann window in time to resampled source and receiver amplitudes. Upsampling tapers its output after the inverse FFT; downsampling tapers its input before the FFT. The FFT-based resampling treats the time axis as periodic, so amplitudes that do not decay to (near) zero at the edges – e.g. field data cropped from a longer recording – can produce wraparound ringing. Tapering can reduce this ringing at the cost of attenuating genuine signal near the edges. Leave as False if amplitudes already taper to (near) zero. Default False.

  • check_output_finite – Whether to check, before returning, that the final wavefields and receiver amplitudes contain no NaN or Inf, raising deepwave.DeepwaveFloatingPointError if any do. Default True: a diverged run otherwise completes silently and only surfaces later, far from its cause. Each output is reduced on the device and the answer read back with a single synchronisation, so the cost is one pipeline drain per call – a fixed cost, whose relative size is therefore largest on the smallest calls. Measured over loops of 100 calls on two workstation GPUs: about +1.2% to +1.3% for a 100x100 grid with nt=300, +4% to +7% for a 40x40 grid with nt=60, and +0.5% to +1.9% for 300x300 with nt=1000 (see Troubleshooting). Pass False for a tight loop of very small calls, or where the outputs feed something that detects a NaN itself. The check is skipped under vmap and the other torch.func transforms, which do not permit reading a value back from a batched tensor.

  • forward_callback – A function that will be called during the forward pass. See deepwave.CallbackState for the state that will be provided to the function. A callback can alter the running simulation, not only inspect it: deepwave.CallbackState.set_wavefield() writes into the simulation state, and deepwave.CallbackState.get_gradient() returns a live view into the gradient accumulator whose in-place mutation is supported (see Custom imaging condition). Mutating what deepwave.CallbackState.get_wavefield() or deepwave.CallbackState.get_model() returns is not: see that class’s docstring for the full contract.

  • backward_callback – A function that will be called during the backward pass. See deepwave.CallbackState for the state that will be provided to the function (the same mutation contract applies). Uniquely among the propagators, the callback state’s get_wavefield(..., role="forward") returns the reconstructed forward wavefield time-coincident with the adjoint field (on both this propagator’s eager backend and its default compiled backend), for building custom imaging conditions; see deepwave.CallbackState.get_wavefield(). If the output being differentiated turns out to be unused, so that autograd has only an undefined (identically zero) gradient to propagate, the callback still fires on its usual schedule and is given an adjoint wavefield of zeros, never None. Retaining that forward field costs a second full sampled wavefield history alongside the usual model-gradient one, so it is opt-in: pass backward_callback_needs_forward_wavefield=True.

  • backward_callback_needs_forward_wavefield –

    Whether backward_callback needs get_wavefield(..., role="forward"). Default False.

    Reconstructing the forward wavefield during the backward pass requires the forward pass to keep a second full sampled wavefield history alongside the model-gradient one. On the default compiled backend that is roughly a doubling of deepwave.estimate_memory()’s backward_storage_bytes (which does not itself account for this argument). On backend="eager" it costs more than that, not less: the retained snapshot covers every wavefield, including the absorbing boundary’s memory variables, which the autograd graph would otherwise be free to release. Most backward callbacks – a NaN check, a norm monitor, a progress bar – never ask for the forward field, and should not pay for it; a 3-D run is exactly where the callback is most useful and the extra history least affordable.

    With False (the default) the history is not allocated and get_wavefield(..., role="forward") raises NotImplementedError naming this argument. deepwave.CallbackState.has_forward_wavefield reports which case you are in without a try/except.

    The second history uses the same storage_mode/storage_compression settings as the primary one, so storage_mode="cpu"/"disk" and storage_compression=True reduce it exactly as they do the model-gradient storage (see Troubleshooting). The argument itself changes no number, but storage_compression=True does change what the callback sees: the field role="forward" returns is then dequantised from the same lossy 8-bit history, so it is an approximation of the forward field (of order 0.1% of the local block maximum), not the field the forward pass actually propagated. An imaging condition built on it inherits that error; leave compression off where you need the exact field. This argument is provisional (see Stability policy): a future backend that can reconstruct the forward field for free would make it unnecessary.

  • callback_frequency –

    The number of user time steps between callback observations. Forward callbacks see steps 0, f, 2*f, ... < nt; backward callbacks see the same steps in reverse. CallbackState.step and n_steps use user steps, dt is the user time step, internal_dt names the CFL step, and time is step * dt from this call’s start. The final state at nt is returned normally, without an extra callback. Native execution chunks meet these boundaries independently of the model-gradient history stride, so requesting model gradients does not change the observation schedule.

    Each chunk carries a fixed cost that does not shrink with the chunk’s length: a separate entry into the backend for that chunk (on the compiled backend, its own set of kernel launches), the construction of a CallbackState, and the call to your callback. A backward pass split into more than one chunk also re-zeroes the per-shot model-gradient scratch buffers before every chunk after the first. Splitting a run more finely therefore pays those costs more times, so prefer the largest callback_frequency that still samples often enough for what you are doing, and keep small values for debugging and for animations that need every step.

  • backend – Which execution backend to use: "auto" (the default), "compiled", "eager" or "torch_compile". See Backends for what each value selects, what "auto" resolves to, and how the backends agree numerically. All four values are implemented for this propagator, and "auto" resolves to the compiled C/CUDA kernel.

  • python_backend – Deprecated alias for backend, retained for backward compatibility. Removed in Deepwave 0.4.0. Providing both backend and python_backend raises ValueError. Accepts a bool or one of "eager", "jit", "compile"; None (the default) means the alias was not given, leaving backend in force. Backends gives the full mapping to backend values.

  • storage – The storage policy for the wavefield history that the model gradient is computed from, as one object: either a mode string (“device”, “cpu”, “disk” or “none”) or a deepwave.Storage, which also carries the path and the lossy-compression flag. Defaults to “device”. This is an alternative spelling for the four individual storage_* arguments below, which remain permanently supported and are not deprecated; passing storage together with any of them raises DeepwaveValueError, because there is no honest way to merge two policies.

  • storage_mode – A string specifying the storage mode for intermediate data. One of “device”, “cpu”, “disk”, or “none”. Defaults to “device”. A permanent alias for Storage.mode; not deprecated.

  • storage_path – The directory that storage_mode=”disk” writes to. A string or any os.PathLike (such as a pathlib.Path). Defaults to the system temporary directory, resolved when you call rather than when Deepwave is imported, so TMPDIR set at any point before the call is honoured. Using the default with storage_mode=”disk” emits a one-time warning naming the directory, because on many systems it is a RAM-backed tmpfs far smaller than the disk. A permanent alias for Storage.path; not deprecated.

  • storage_compression – Lossy: it changes the model gradient, by about 0.2-4% in 2-D and 0.2-8% in 3-D in relative L2 error. The name does not say so, which is why this entry does. If True, the sampled wavefield history kept for the model gradient is stored as 8-bit block max-abs quantisation (8 cells per spatial dimension per block, one full-precision scale per block). Measured relative L2 error against an uncompressed run: about 0.2-4% for 2-D problems and 0.2-8% for 3-D. For a matched geometry 3-D is consistently two to five times the 2-D error – a 3-D block shares one scale across 512 cells against 64 in 2-D – but the spread across geometries and loss functions is wide enough that the two ranges overlap. The error does not grow with the size of the problem: with the survey and the model anomaly held at fixed cell indices it is constant to five significant figures from 32x32 to 400x400 and from 32^3 to 120^3, and constant in nt once the record is long enough for the wavefield to reach the receivers. It does depend on the survey geometry and on the loss function – one fixed 3-D problem gives 3.4% under a sum of squared receiver amplitudes and 1.9% under a data residual – so treat these as ranges rather than a number to budget against. A barely illuminated gradient is the exception: a record too short for the wavefield to reach the receivers at all can give tens of percent, because there the gradient itself is nearly empty. The relative maximum error, defined as max|g_comp - g_exact| / max|g_exact|, is comparable to the relative L2 error rather than a fixed multiple of it – typically between 0.3 and 3 times it, and in 3-D more often below the L2 error than above. Do not use a per-cell relative error as a figure of merit: it is unbounded near the gradient’s zero crossings. The error is essentially independent of dtype (float32 and float64 agree to three or four significant figures) and exactly independent of storage_mode. The worst-affected values are the outermost cells of the model: a cell’s gradient there sums large, largely cancelling contributions from the absorbing layer, so the quantisation error does not cancel with them. Measured on the gradient of scalar_born’s dv, normalised by the gradient’s own maximum, the largest edge-cell error is 0.03-0.07 in 1-D, about 1e-4 in 2-D and about 1e-5 in 3-D, against below 1e-3, 2e-5 and 1e-5 respectively in the interior. Disabling compression removes quantisation error; it does not remove model-gradient sampling error. Exact discrete model derivatives also require an effective history stride of one and fixed setup parameters (see What Deepwave guarantees about gradients). Unaffected, bit for bit: the returned wavefields and auxiliary state, the receiver amplitudes, and the gradients with respect to the source amplitudes and the initial wavefields – compression touches only the stored history that forms the model gradient. Do not use it for `gradcheck`, adjoint or dot-product tests, or publication-quality gradients. In exchange it shrinks the offloaded host buffer by close to the size of the dtype in bytes – each cell becomes one byte, plus one full-precision scale per block – which is about 3.8x in 2-D and 4.0x in 3-D for float32, and about 7.1x and 7.9x for float64. That ratio is set by the dtype and the number of spatial dimensions, not by the size of the problem. Composed with storage_mode=”cpu” it runs roughly 2 to 3 times faster than uncompressed offload, because it reduces the PCIe traffic that offload is bottlenecked on. Defaults to False.

  • location_interpolation –

    A string controlling how non-integer (float) source/receiver coordinates are handled. One of:

    • ”auto” (default): float coordinates that do not lie on grid cells are interpolated onto the grid using the method of Hicks (Kaiser-windowed sinc); float coordinates that do lie on grid cells (within a small tolerance), and integer (torch.long) coordinates, take the exact path and are bit-for-bit identical to providing integer cell indices.

    • ”hicks”: always use Hicks interpolation.

    • ”none”: never interpolate; only the exact path is accepted (integer (torch.long) coordinates, or float coordinates that lie on grid cells within a small tolerance). An off-cell float coordinate raises a ValueError rather than being silently truncated to the cell below it, as v0.0.27 did.

    With genuinely off-grid coordinates the propagator performs band-limited injection/recording, an approximation. The manual deepwave.Hicks interface remains available for full control. Defaults to “auto”. "auto" interpolates a floating-point locations Tensor only when at least one non-ignored coordinate lies more than deepwave.ON_NODE_TOLERANCE cells off its component’s grid node – except that a floating-point Tensor with requires_grad=True is always interpolated, even when every coordinate is on-node, because only the interpolated path carries gradients back to the positions. That changes the forward values for an on-node requires_grad locations Tensor relative to the same Tensor without requires_grad. Pass location_interpolation="none" for the exact path (positions are then not differentiable), or accept the band-limited values. RunInfo.location_interpolation_used records which path was taken: "none" or "hicks", or "mixed" if this propagator has several channels of one kind (several source components, say) and they did not all take the same path.

  • location_interpolation_halfwidth – The halfwidth of the Hicks interpolation window (see deepwave.Hicks) used when float coordinates are interpolated. Defaults to 4.

Scalar Function

deepwave.scalar(v: Tensor, grid_spacing: float | Sequence[float], dt: float, *, sources: Mapping[str, tuple[Tensor, Tensor | Hicks] | tuple[Tensor, Tensor | Hicks, Tensor | None]] | None = None, receivers: Mapping[str, Tensor | Hicks | tuple[Tensor | Hicks, Tensor | None]] | None = None, source_amplitudes: Tensor | None = None, source_locations: Tensor | Hicks | None = None, source_mask: Tensor | None = None, receiver_locations: Tensor | Hicks | None = None, receiver_mask: Tensor | None = None, wavefield_0: Tensor | None = None, wavefield_m1: Tensor | None = None, initial_state: PropagatorOutput | Mapping[str, Tensor | AuxiliaryState] | None = None, accuracy: Literal[2, 4, 6, 8] = 4, pml_width: int | Sequence[int] = 20, boundary: str | Sequence[str] | None = None, pml_freq: float | None = None, max_vel: float | None = None, courant_number: float | None = None, survey_pad: int | Sequence[int | None] | None = None, origin: Sequence[int] | None = None, nt: int | None = None, model_gradient_sampling_interval: int = 1, checkpoint_segments: int | None = None, freq_taper_frac: float = 0.0, time_pad_frac: float = 0.0, time_taper: bool = False, check_output_finite: bool = True, forward_callback: Callback | None = None, backward_callback: Callback | None = None, callback_frequency: int = 1, backward_callback_needs_forward_wavefield: bool = False, backend: str = 'auto', python_backend: Literal['eager', 'jit', 'compile'] | bool | None = None, storage: str | Storage | None = None, storage_mode: Literal['device', 'cpu', 'disk', 'none'] | None = None, storage_path: str | PathLike[str] | None = None, storage_compression: bool | None = None, location_interpolation: Literal['auto', 'hicks', 'none'] = 'auto', location_interpolation_halfwidth: int = 4) → PropagatorOutput

Scalar wave propagation (functional interface).

Stability: stable (see Stability policy).

This function performs forward modelling with the scalar wave equation. The outputs are differentiable with respect to the wavespeed, the source amplitudes, and the initial wavefields.

For computational performance, multiple shots may be propagated simultaneously.

The function returns the final wavefields and the data recorded at the specified receiver locations. All returned torch.Tensors are differentiable with respect to the float torch.Tensors in the input.

Deepwave orders spatial dimensions from slowest-varying to fastest-varying and attaches no physical meaning to any of them; the component suffixes count from the fastest axis, so a 2-D run uses _y and _x and a 3-D run uses _z, _y and _x. See Dimension order and the axis convention.

Parameters:
  • v – The wavespeed model, of shape [*model_shape] or, for a batched model, [n_shots, *model_shape], where model_shape has ndim entries. See Batched models.

  • grid_spacing – The spatial grid cell size. It can be a single number that will be used for all dimensions, or a number for each dimension.

  • dt – A float specifying the time step interval of the input and output (internally a smaller interval may be used in propagation to obey the CFL (Courant-Friedrichs-Lewy) condition for stability). Deepwave is unit-agnostic: choose any single consistent system of units for v, grid_spacing, and dt; see Units and source conventions.

  • sources – This simulation’s sources, keyed by channel. This is the canonical way to specify a survey. Each value is (amplitudes, locations), or (amplitudes, locations, mask) to include an activity mask. This propagator has one channel, u (the scalar wavefield), in every dimensionality, spelled that way however this propagator’s own flat keywords are spelled (they carry no channel suffix at all). Exactly equivalent to the flat source_amplitudes/source_locations/source_mask keywords, which remain permanently supported; giving the channel in both forms raises. Optional.

  • receivers – This simulation’s receivers, keyed by channel, as the canonical alternative to the flat receiver_locations/receiver_mask keywords. Each value is a locations object, or (locations, mask) to include an activity mask. The one channel is u. Giving the channel in both forms raises. Optional.

  • source_amplitudes – A torch.Tensor with dimensions [shot, source, time], holding the right-hand side of the scalar wave equation; its units set the units of the receiver data, and Units and source conventions gives the calibration. For example, if two shots are propagated simultaneously, each containing three sources of one hundred time samples, the shape would be [2, 3, 100]. The time dimension length determines the number of time steps in the simulation. Optional. If provided, source_locations must also be specified. If not provided, nt must be specified.

  • source_locations – A torch.Tensor with dimensions [shot, source, ndim], containing the index in the ndim spatial dimensions of the cell that each source is located in, relative to the origin of the model. Optional. Must be provided if source_amplitudes is. It usually has torch.long (int64) datatype, giving integer grid cell indices; a floating-point tensor may instead be provided to place sources at off-grid positions, handled according to the location_interpolation argument. For full control (including dipole sources and free surfaces) a deepwave.Hicks object may be passed here instead of a tensor, in which case the propagator performs the interpolation spread and gather for you (provide the source amplitudes at the original, un-interpolated locations). The location of each source must be unique within the same shot (you cannot have two sources in the same shot that both have location [1, 2], for example). A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active. True means active; False means that point injects nothing, and its coordinates in source_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of source_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • receiver_locations – A torch.Tensor with dimensions [shot, receiver, ndim], containing the coordinates of the cell containing each receiver. Optional. It usually has torch.long (int64) datatype; a floating-point tensor may instead be provided (see source_locations and location_interpolation). If not provided, the output receiver_amplitudes torch.Tensor will be empty. If backpropagation will be performed, the location of each receiver must be unique within the same shot. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active. True means active; False means that point records zeros, and its coordinates in receiver_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of receiver_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • wavefield_0 – A torch.Tensor specifying the initial wavefield at time step 0. It should have (ndim + 1) dimensions, with the first dimension being shot and the subsequent ones corresponding to the spatial dimensions. Two spatial shapes are accepted. The first is the simulation domain, which is the extracted model region plus the PML: if two shots are being propagated simultaneously in a region of size [20, 30] extracted from the model for the simulation, and pml_width=[1, 2, 3, 4], then wavefield_0 would be of shape [2, 23, 37]. This is the shape a previous call returns, so it is the shape to use when continuing a simulation. The second is the extracted model region itself ([2, 20, 30] in that example), which Deepwave zero-pads for you by pml_width on each face; this is usually the convenient shape when you are specifying an initial condition rather than continuing a run, such as the initial pressure of a photoacoustic simulation. Because a wavefield determines the region that is extracted when survey_pad is not given, a shape that could be read either way is read as the simulation domain, which is what it has always meant, and a warning names both readings and the region each would select. Providing origin does not remove that ambiguity – origin says where the region begins, not whether the shape includes the PML – so the warning is issued whether or not you provide one. Passing initial_state from a previous call is never ambiguous, since the auxiliary state it carries records the origin and PML width it was produced with. Optional, default all zeros.

  • wavefield_m1 – A torch.Tensor specifying the initial wavefield at time step -1 (using Deepwave’s internal time step interval, which may be smaller than the user provided one to obey the CFL condition). See the entry for wavefield_0 for more details.

  • initial_state –

    An optional bundle of initial wavefields, supplied as either a deepwave.PropagatorOutput (the return of a previous call), that output’s final_state mapping, or any mapping whose keys are the public state field names (wavefield_0, wavefield_m1) and auxiliary_state (a deepwave.AuxiliaryState from a previous call). Each entry fills the correspondingly-named initial wavefield that was not also passed explicitly. Because the output wavefields are named identically to these arguments, passing a previous output back in as initial_state continues the simulation seamlessly (its receiver_amplitudes field is ignored), and it is the unambiguous route: the auxiliary state records the propagator, accuracy, PML width, boundary conditions, number of relaxation mechanisms, simulation-domain shape and internal time step it was produced with, so a continuation that does not match them raises rather than producing quiet nonsense, and one whose pml_freq or max_vel differs – which reshapes the absorbing boundary without changing what the tensors are – warns. The survey origin is recorded and reused: see the continuation rule below.

    Continuation rule. When initial_state carries a region – a PropagatorOutput from a previous call, whose RunInfo.origin and RunInfo.extent record the region that run used, or a AuxiliaryState carrying the same – and you pass neither survey_pad nor origin, that stored region is reused: the continuation propagates exactly the same part of the model, and the new call’s sources and receivers must lie inside it (they are checked; a location outside it raises). If you pass survey_pad or origin explicitly, your value wins and the stored wavefields must match the region it implies; a disagreement raises rather than silently re-windowing the simulation. Passing the previous call’s output (initial_state=out) is the unambiguous route. A mapping of bare tensors records no region at all, so it warns and assumes origin zero unless you pass origin.

    The individual absorbing-boundary field names (psiz_m1, …, zetax_m1) are still accepted as keys, with a FutureWarning; they will stop being accepted in Deepwave 0.4.0. Providing a wavefield both here and as an explicit argument raises an error. For the continuation to exactly reproduce a single run over the combined time range, the same internal time step and the same absorbing boundary must be used throughout. The first holds when the user dt already satisfies the CFL condition (no internal sub-stepping); otherwise the segment boundary introduces a small discretisation difference. The second is automatic: when pml_freq or max_vel is omitted, the value recorded in initial_state is used, rather than one inferred from this call’s own portion of the source record or from its model, so the continuation builds the same absorbing boundary as the run it continues. Passing either explicitly overrides that, and a mismatch with the recorded value then warns; a mismatched internal time step raises. If initial_state is a PropagatorOutput and neither origin nor survey_pad is also passed, the survey origin used to produce it is recovered automatically, so the continuation extracts the same region of the model rather than assuming an origin of zero.

    The number of shots (the leading dimension of every per-shot tensor) is taken from the source locations if any are given, otherwise from the initial wavefields. So a call with nt and no sources takes its shot count from the wavefields given here; with neither sources nor initial wavefields the call raises. Optional, default None.

  • accuracy – An int specifying the finite difference order of accuracy. Possible values are 2, 4, 6, and 8, with larger numbers resulting in more accurate results but greater computational cost. Optional, with a default of 4.

  • pml_width –

    A single number, or two numbers for each dimension, specifying the width (in number of cells) of the PML that prevents reflections from the edges of the model. If a single value is provided, it will be used for all edges. If a sequence is provided, it should contain values for the edges in the following order:

    • beginning of first (slowest) dimension

    • end of first dimension

    • beginning of second dimension

    • end of second dimension

    • …

    Larger values result in smaller reflections, with values of 10 to 20 being typical. To make an edge reflective instead of absorbing, set the value for that edge to be zero; for the scalar propagator such an edge is a free surface (it reflects with inverted polarity, a reflection coefficient of approximately -1). For example, if your 2D model is oriented so that the surface that you want to be reflective is the beginning of the second dimension, then you could specify pml_width=[20, 20, 0, 20]. The wavespeed in the PML region is obtained by replicating the values on the edge of the model. Optional, default 20. A face whose pml_width is zero does not absorb: it becomes an implicit reflecting boundary whose type depends on the propagator and the field component. See Reflecting edges for the per-propagator reflection coefficient and surface location, and prefer naming the condition with boundary (or deepwave.boundaries()), which also uses the exact image rather than this approximate one.

  • boundary – An optional single string, applying to every face, or a list of 2 * ndim strings selecting a first-class boundary condition per face, in the same order as pml_width. Each entry is one of “pml” (absorbing; boundary=”pml” is the spelled-out default, and behaves in every respect – including the warnings it emits – as omitting boundary does), “free” (a pressure-release surface: the field is held to zero on the boundary and reflects with inverted polarity, reflection coefficient -1), “rigid” (a rigid wall: the field reflects with preserved polarity, +1), or “periodic” (the face wraps around to the opposite face). A “free”, “rigid”, or “periodic” face requires pml_width to be zero on that face, and “periodic” must be set on both faces of a dimension. Unlike a bare zero-pml_width face (which is only an approximate reflector at accuracy above 2), a “free”/”rigid” face fills the exterior with the exact odd/even image at every accuracy order, so the analytic reflection coefficient is met to better than 1%. See the boundary-conditions documentation for the precise ghost-plane conventions. Optional, default None. Availability: this propagator accepts all four names, on every face, at every accuracy.

  • pml_freq –

    The frequency to use when constructing the PML, usually the dominant frequency of the source wavelet: the PML absorbs best near this frequency, and choosing it poorly makes the edges more reflective. Optional. If not specified, it is inferred from the peak of the source amplitudes’ spectrum (which returns the wavelet’s own centre frequency for a Ricker), or, if the simulation has no sources at all, from 0.5 / dt / 5, a fifth of the Nyquist frequency. Nothing is inferred when the call continues an earlier one: an omitted pml_freq then takes the value recorded in initial_state. The value actually used is reported as out.info.pml_freq. Pin it explicitly for any published result: leaving it inferred makes the absorbing boundary depend on the wavelet, so changing the wavelet also changes the boundary. The source-amplitude gradient does not follow that path, so pin pml_freq when you differentiate with respect to the source. See Choosing pml_freq.

    Note

    Deepwave 0.0.27 and earlier used a fixed 25 Hz here. A seismic call with a 25 Hz wavelet therefore infers exactly 25.0 and gets the same numbers as before; a call whose inferred value is anything else warns once, naming pml_freq=25.0 as the line that restores the old behaviour. That warning is a migration aid and is removed in 0.4.0.

  • max_vel – A float specifying the maximum velocity, which is used when applying the CFL condition and when constructing the PML. If not specified, the actual maximum absolute wavespeed in the model (or portion of it that is used) will be used. The option to specify this is provided to allow you to ensure consistency between runs even if there are changes in the wavespeed. Optional, default None. Leaving this None makes the discretisation a property of the current model: the internal time step (and therefore RunInfo.step_ratio and the quadrature of the model gradient) changes as the model updates, so an inversion’s objective is not exactly comparable between iterations. Pass an explicit max_vel – the maximum wavespeed you expect over the whole inversion – for a reproducible objective. Nothing is read from the model when the call continues an earlier one: an omitted max_vel then takes the value recorded in initial_state. The value actually used is reported as RunInfo.max_vel. A value below the model’s actual maximum warns and risks an unstable (diverging or NaN) simulation, since the CFL condition is computed from max_vel. It has to be given when v is batched by torch.func.vmap(), which hides the values the maximum is otherwise read from. Every other torch.func transform (torch.func.grad(), torch.func.jacrev(), torch.func.jvp()) leaves them readable, so nothing has to be supplied under those. The maximum arrives as a Python number, so the absorbing boundary and the internal time step it chooses are constants of the call rather than differentiable functions of the model. When it is inferred, the model gradient at the cell the maximum is read from omits the term that acts through those two; every other cell’s gradient is unaffected. Passing max_vel removes that path. An exact model gradient of the fixed discrete call also requires internal-step sampling and lossless storage; see What Deepwave Calculates. Capability matrix lists, for each propagator, the models the maximum is read from.

  • courant_number – A float specifying the Courant number to use for the CFL time-step calculation. If None (the default), the conservative deepwave.DEFAULT_COURANT_NUMBER is used for all accuracy orders (the historical behaviour, reproduced bit-for-bit). If given, it is the actual dimensionless Courant number that the internal time step will target; a larger value permits a larger internal time step (fewer internal steps, faster) but reduces the stability margin. It is validated against the critical Courant number for the chosen accuracy order and errors if the value would be unstable. See the discretisation documentation for the critical Courant numbers. Optional, default None.

  • survey_pad –

    A single value or list of two values for each dimension, all of which are either an int or None, specifying whether the simulation domain should be restricted to a region surrounding the sources and receivers. If you have a large model, but the sources and receivers of individual shots only cover a small portion of it (such as in a towed streamer survey), then it may be wasteful to simulate wave propagation over the whole model. This parameter allows you to specify what distance around sources and receivers you would like to extract from the model to use for the simulation. A value of None means that no restriction of the model should be performed in that direction, while an integer value specifies the minimum number of cells that the edge of the simulation domain should be from any source or receiver in that direction (if possible). If a single value is provided, it applies to all directions, so specifying None will use the whole model for every simulation, while specifying 10 will cause the simulation domain to contain at least 10 cells of padding in each direction around sources and receivers if possible. The padding will end if the edge of the model is encountered. Specifying a list, in the following order, allows the padding in each direction to be controlled:

    • beginning of first (slowest) dimension

    • end of first dimension

    • beginning of second dimension

    • end of second dimension

    • …

    Ints and None may be mixed, so a survey_pad of [5, None, None, 10] means that there should be at least 5 cells of padding towards the beginning of the first dimension, no restriction of the simulation domain towards the end of the first dimension or beginning of the second, and 10 cells of padding towards the end of the second dimension. If the simulation contains one source at [20, 15], one receiver at [10, 15], and the model is of shape [40, 50], then the extracted simulation domain with this value of survey_pad would cover the region [5:40, 0:25]. The same simulation domain will be used for all shots propagated simultaneously, so if this option is used then it is advisable to propagate shots that cover similar regions of the model simultaneously so that it provides a benefit (if the shots cover opposite ends of a dimension, then that whole dimension will be used regardless of what survey_pad value is used). Optional, default None. Cannot be specified if origin is also specified. Passing it alongside an initial_state that records a different region raises, rather than silently re-windowing the simulation: see the continuation rule under initial_state.

  • origin – A list of ints specifying the origin of the provided initial wavefields relative to the origin of the model. Only relevant if initial wavefields are provided. The origin of a wavefield is the cell where the extracted model used for the simulation domain began. It does not include the PML. So if a simulation is performed using the model region [10:20, 15:30], the origin is [10, 15]. Optional, default all zero (or, if initial_state records a region and survey_pad is not specified, the origin recorded by that previous call – see the continuation rule under initial_state). Passing an origin that disagrees with the one initial_state records raises. Cannot be specified if survey_pad is also specified.

  • nt –

    The number of time steps to run the simulation for. Optional: if it is not given, the length of the source amplitudes is used, so a call with sources need not repeat it.

    When both are given, nt is the length of the run and the source amplitudes are fitted to it in your own time samples, before any internal resampling: a record shorter than nt is zero-padded at the end, so the sources fall silent and the propagation continues; a longer one is truncated, so the samples beyond nt are never injected and no gradient reaches them. The result is bit-for-bit what passing a hand-padded or hand-truncated record would have produced. out.info.nt reports the length the run actually used.

    The number of shots (the leading dimension of every per-shot tensor) is taken from the source locations if any are given, otherwise from the initial wavefields. So with nt and no sources, the initial wavefields set it; with neither sources nor initial wavefields the call raises.

  • model_gradient_sampling_interval –

    An int specifying the number of time steps between contributions to the model gradient. The gradient with respect to the model is an integral over the backpropagation time steps. Under band-limited forward and adjoint fields, a sampling rate greater than twice their highest frequency suffices for the full-time correlation integral. If the signal bandwidth is well below the data Nyquist frequency, a larger interval can reduce computational (especially memory) costs. Finite-record endpoints and arbitrary initial states can violate the ideal assumptions; see docs/what_deepwave_calculates.rst. Optional, default 1 (integral is sampled every time step interval dt).

    Two things to know. First, the CFL step_ratio multiplies this: the quadrature stride the kernel actually uses is step_ratio * model_gradient_sampling_interval, so a dt above the CFL limit already coarsens the gradient integral before this argument is applied (see docs/what_deepwave_calculates.rst). Second, it applies only to the compiled backend: the pure-Python backends (backend=”eager”/”torch_compile”) step every internal time step and rely on native autograd, so they return the exact discrete adjoint and ignore this argument entirely. The two can therefore differ for the same arguments when the effective stride exceeds 1. Band-limited wavefields can justify this sampling for the time integral, but finite-record transients and energy near the sampling limit can cause material differences; source bandwidth alone does not provide a universal error bound.

    It must divide nt whenever a model requires a gradient, since the stride is also the engine’s outer time step; a remainder would leave the final nt % model_gradient_sampling_interval steps unpropagated, so it raises instead. When no model requires a gradient (including any run under torch.no_grad()) it is reset to 1 and ignored. It does not alter callback observation times; callback_frequency always counts user time steps.

  • checkpoint_segments –

    If given, split the time axis into this many segments and run each of them under torch.utils.checkpoint, which is the memory lever for a run whose wavefield history does not fit: peak backward memory falls to roughly one segment’s history plus the states held at the segment boundaries, at the cost of running the forward pass of each segment a second time during the backward pass. None (the default) runs the whole time axis in one piece, exactly as before. It is the same thing as calling deepwave.checkpointing.run_segments() with n_segments= and checkpoint=True, and returns the same combined output – the same fields, the same info and the same auxiliary_state a direct call gives. initial_state, the storage arguments and every physics option are supported; nt may pad or truncate supplied source amplitudes for the whole run. Source-free nt calls, individual initial-wavefield arguments and per-step callbacks are not supported, and say so.

    Take the gradient with loss.backward(). The checkpointing is reentrant, and torch.utils.checkpoint does not support torch.autograd.grad() (or an inputs= argument to .backward()) in that mode: it raises. If you need torch.autograd.grad, call deepwave.checkpointing.run_segments() directly with use_reentrant=False.

  • freq_taper_frac – A float specifying the fraction of the end of the source and receiver amplitudes (if present) in the frequency domain to cosine taper if they are resampled due to the CFL condition. This might be useful to reduce ringing. A value of 0.1 means that the top 10% of frequencies will be tapered. Default 0.0 (no tapering).

  • time_pad_frac – A float specifying the amount of zero padding that will be added to the source and receiver amplitudes (if present) before resampling and removed afterwards, if they are resampled due to the CFL condition, as a fraction of their length. This might be useful to reduce wraparound artifacts. A value of 0.1 means that zero padding of 10% of the number of time samples will be used. Default 0.0.

  • time_taper – A bool specifying whether to apply a Hann window in time to resampled source and receiver amplitudes. Upsampling tapers its output after the inverse FFT; downsampling tapers its input before the FFT. The FFT-based resampling treats the time axis as periodic, so amplitudes that do not decay to (near) zero at the edges – e.g. field data cropped from a longer recording – can produce wraparound ringing. Tapering can reduce this ringing at the cost of attenuating genuine signal near the edges. Leave as False if amplitudes already taper to (near) zero. Default False.

  • check_output_finite – Whether to check, before returning, that the final wavefields and receiver amplitudes contain no NaN or Inf, raising deepwave.DeepwaveFloatingPointError if any do. Default True: a diverged run otherwise completes silently and only surfaces later, far from its cause. Each output is reduced on the device and the answer read back with a single synchronisation, so the cost is one pipeline drain per call – a fixed cost, whose relative size is therefore largest on the smallest calls. Measured over loops of 100 calls on two workstation GPUs: about +1.2% to +1.3% for a 100x100 grid with nt=300, +4% to +7% for a 40x40 grid with nt=60, and +0.5% to +1.9% for 300x300 with nt=1000 (see Troubleshooting). Pass False for a tight loop of very small calls, or where the outputs feed something that detects a NaN itself. The check is skipped under vmap and the other torch.func transforms, which do not permit reading a value back from a batched tensor.

  • forward_callback – A function that will be called during the forward pass. See deepwave.CallbackState for the state that will be provided to the function. A callback can alter the running simulation, not only inspect it: deepwave.CallbackState.set_wavefield() writes into the simulation state, and deepwave.CallbackState.get_gradient() returns a live view into the gradient accumulator whose in-place mutation is supported (see Custom imaging condition). Mutating what deepwave.CallbackState.get_wavefield() or deepwave.CallbackState.get_model() returns is not: see that class’s docstring for the full contract.

  • backward_callback – A function that will be called during the backward pass. See deepwave.CallbackState for the state that will be provided to the function (the same mutation contract applies). Uniquely among the propagators, the callback state’s get_wavefield(..., role="forward") returns the reconstructed forward wavefield time-coincident with the adjoint field (on both this propagator’s eager backend and its default compiled backend), for building custom imaging conditions; see deepwave.CallbackState.get_wavefield(). If the output being differentiated turns out to be unused, so that autograd has only an undefined (identically zero) gradient to propagate, the callback still fires on its usual schedule and is given an adjoint wavefield of zeros, never None. Retaining that forward field costs a second full sampled wavefield history alongside the usual model-gradient one, so it is opt-in: pass backward_callback_needs_forward_wavefield=True.

  • backward_callback_needs_forward_wavefield –

    Whether backward_callback needs get_wavefield(..., role="forward"). Default False.

    Reconstructing the forward wavefield during the backward pass requires the forward pass to keep a second full sampled wavefield history alongside the model-gradient one. On the default compiled backend that is roughly a doubling of deepwave.estimate_memory()’s backward_storage_bytes (which does not itself account for this argument). On backend="eager" it costs more than that, not less: the retained snapshot covers every wavefield, including the absorbing boundary’s memory variables, which the autograd graph would otherwise be free to release. Most backward callbacks – a NaN check, a norm monitor, a progress bar – never ask for the forward field, and should not pay for it; a 3-D run is exactly where the callback is most useful and the extra history least affordable.

    With False (the default) the history is not allocated and get_wavefield(..., role="forward") raises NotImplementedError naming this argument. deepwave.CallbackState.has_forward_wavefield reports which case you are in without a try/except.

    The second history uses the same storage_mode/storage_compression settings as the primary one, so storage_mode="cpu"/"disk" and storage_compression=True reduce it exactly as they do the model-gradient storage (see Troubleshooting). The argument itself changes no number, but storage_compression=True does change what the callback sees: the field role="forward" returns is then dequantised from the same lossy 8-bit history, so it is an approximation of the forward field (of order 0.1% of the local block maximum), not the field the forward pass actually propagated. An imaging condition built on it inherits that error; leave compression off where you need the exact field. This argument is provisional (see Stability policy): a future backend that can reconstruct the forward field for free would make it unnecessary.

  • callback_frequency –

    The number of user time steps between callback observations. Forward callbacks see steps 0, f, 2*f, ... < nt; backward callbacks see the same steps in reverse. CallbackState.step and n_steps use user steps, dt is the user time step, internal_dt names the CFL step, and time is step * dt from this call’s start. The final state at nt is returned normally, without an extra callback. Native execution chunks meet these boundaries independently of the model-gradient history stride, so requesting model gradients does not change the observation schedule.

    Each chunk carries a fixed cost that does not shrink with the chunk’s length: a separate entry into the backend for that chunk (on the compiled backend, its own set of kernel launches), the construction of a CallbackState, and the call to your callback. A backward pass split into more than one chunk also re-zeroes the per-shot model-gradient scratch buffers before every chunk after the first. Splitting a run more finely therefore pays those costs more times, so prefer the largest callback_frequency that still samples often enough for what you are doing, and keep small values for debugging and for animations that need every step.

  • backend – Which execution backend to use: "auto" (the default), "compiled", "eager" or "torch_compile". See Backends for what each value selects, what "auto" resolves to, and how the backends agree numerically. All four values are implemented for this propagator, and "auto" resolves to the compiled C/CUDA kernel.

  • python_backend – Deprecated alias for backend, retained for backward compatibility. Removed in Deepwave 0.4.0. Providing both backend and python_backend raises ValueError. Accepts a bool or one of "eager", "jit", "compile"; None (the default) means the alias was not given, leaving backend in force. Backends gives the full mapping to backend values.

  • storage – The storage policy for the wavefield history that the model gradient is computed from, as one object: either a mode string (“device”, “cpu”, “disk” or “none”) or a deepwave.Storage, which also carries the path and the lossy-compression flag. Defaults to “device”. This is an alternative spelling for the four individual storage_* arguments below, which remain permanently supported and are not deprecated; passing storage together with any of them raises DeepwaveValueError, because there is no honest way to merge two policies.

  • storage_mode – A string specifying the storage mode for intermediate data. One of “device”, “cpu”, “disk”, or “none”. Defaults to “device”. A permanent alias for Storage.mode; not deprecated.

  • storage_path – The directory that storage_mode=”disk” writes to. A string or any os.PathLike (such as a pathlib.Path). Defaults to the system temporary directory, resolved when you call rather than when Deepwave is imported, so TMPDIR set at any point before the call is honoured. Using the default with storage_mode=”disk” emits a one-time warning naming the directory, because on many systems it is a RAM-backed tmpfs far smaller than the disk. A permanent alias for Storage.path; not deprecated.

  • storage_compression – Lossy: it changes the model gradient, by about 0.2-4% in 2-D and 0.2-8% in 3-D in relative L2 error. The name does not say so, which is why this entry does. If True, the sampled wavefield history kept for the model gradient is stored as 8-bit block max-abs quantisation (8 cells per spatial dimension per block, one full-precision scale per block). Measured relative L2 error against an uncompressed run: about 0.2-4% for 2-D problems and 0.2-8% for 3-D. For a matched geometry 3-D is consistently two to five times the 2-D error – a 3-D block shares one scale across 512 cells against 64 in 2-D – but the spread across geometries and loss functions is wide enough that the two ranges overlap. The error does not grow with the size of the problem: with the survey and the model anomaly held at fixed cell indices it is constant to five significant figures from 32x32 to 400x400 and from 32^3 to 120^3, and constant in nt once the record is long enough for the wavefield to reach the receivers. It does depend on the survey geometry and on the loss function – one fixed 3-D problem gives 3.4% under a sum of squared receiver amplitudes and 1.9% under a data residual – so treat these as ranges rather than a number to budget against. A barely illuminated gradient is the exception: a record too short for the wavefield to reach the receivers at all can give tens of percent, because there the gradient itself is nearly empty. The relative maximum error, defined as max|g_comp - g_exact| / max|g_exact|, is comparable to the relative L2 error rather than a fixed multiple of it – typically between 0.3 and 3 times it, and in 3-D more often below the L2 error than above. Do not use a per-cell relative error as a figure of merit: it is unbounded near the gradient’s zero crossings. The error is essentially independent of dtype (float32 and float64 agree to three or four significant figures) and exactly independent of storage_mode. The worst-affected values are the outermost cells of the model: a cell’s gradient there sums large, largely cancelling contributions from the absorbing layer, so the quantisation error does not cancel with them. Measured on the gradient of scalar_born’s dv, normalised by the gradient’s own maximum, the largest edge-cell error is 0.03-0.07 in 1-D, about 1e-4 in 2-D and about 1e-5 in 3-D, against below 1e-3, 2e-5 and 1e-5 respectively in the interior. Disabling compression removes quantisation error; it does not remove model-gradient sampling error. Exact discrete model derivatives also require an effective history stride of one and fixed setup parameters (see What Deepwave guarantees about gradients). Unaffected, bit for bit: the returned wavefields and auxiliary state, the receiver amplitudes, and the gradients with respect to the source amplitudes and the initial wavefields – compression touches only the stored history that forms the model gradient. Do not use it for `gradcheck`, adjoint or dot-product tests, or publication-quality gradients. In exchange it shrinks the offloaded host buffer by close to the size of the dtype in bytes – each cell becomes one byte, plus one full-precision scale per block – which is about 3.8x in 2-D and 4.0x in 3-D for float32, and about 7.1x and 7.9x for float64. That ratio is set by the dtype and the number of spatial dimensions, not by the size of the problem. Composed with storage_mode=”cpu” it runs roughly 2 to 3 times faster than uncompressed offload, because it reduces the PCIe traffic that offload is bottlenecked on. Defaults to False.

  • location_interpolation –

    A string controlling how non-integer (float) source/receiver coordinates are handled. One of:

    • ”auto” (default): float coordinates that do not lie on grid cells are interpolated onto the grid using the method of Hicks (Kaiser-windowed sinc); float coordinates that do lie on grid cells (within a small tolerance), and integer (torch.long) coordinates, take the exact path and are bit-for-bit identical to providing integer cell indices.

    • ”hicks”: always use Hicks interpolation.

    • ”none”: never interpolate; only the exact path is accepted (integer (torch.long) coordinates, or float coordinates that lie on grid cells within a small tolerance). An off-cell float coordinate raises a ValueError rather than being silently truncated to the cell below it, as v0.0.27 did.

    With genuinely off-grid coordinates the propagator performs band-limited injection/recording, an approximation. The manual deepwave.Hicks interface remains available for full control. Defaults to “auto”. "auto" interpolates a floating-point locations Tensor only when at least one non-ignored coordinate lies more than deepwave.ON_NODE_TOLERANCE cells off its component’s grid node – except that a floating-point Tensor with requires_grad=True is always interpolated, even when every coordinate is on-node, because only the interpolated path carries gradients back to the positions. That changes the forward values for an on-node requires_grad locations Tensor relative to the same Tensor without requires_grad. Pass location_interpolation="none" for the exact path (positions are then not differentiable), or accept the band-limited values. RunInfo.location_interpolation_used records which path was taken: "none" or "hicks", or "mixed" if this propagator has several channels of one kind (several source components, say) and they did not all take the same path.

  • location_interpolation_halfwidth – The halfwidth of the Hicks interpolation window (see deepwave.Hicks) used when float coordinates are interpolated. Defaults to 4.

Returns:

a tuple subclass whose fields, in order, are:

  • wavefield_0: The wavefield at the final time step.

  • wavefield_m1: The wavefield at the second-to-last time step.

  • auxiliary_state: A deepwave.AuxiliaryState holding the absorbing boundary’s memory variables. Opaque: pass it back (as part of initial_state) to continue the simulation; its contents are implementation state rather than physics.

  • receiver_amplitudes: The receiver amplitudes. Empty if no receivers were specified.

Return type:

deepwave.PropagatorOutput

ScalarBorn Class

class deepwave.ScalarBorn(v: Tensor, grid_spacing: float | Sequence[float], *, dv: Tensor | None = None, scatter: Tensor | None = None, requires_grad: bool | Sequence[str] | None = None, v_requires_grad: bool | None = None, dv_requires_grad: bool | None = None, scatter_requires_grad: bool | None = None, storage: str | Storage | None = None, storage_mode: Literal['device', 'cpu', 'disk', 'none'] | None = None, storage_path: str | PathLike[str] | None = None, storage_compression: bool | None = None)

A Module wrapper around scalar_born().

Stability: stable (see Stability policy).

This is a convenience module that allows you to only specify v, dv, and grid_spacing once. They will then be added to the list of arguments passed to scalar_born() when you call the forward method.

The scatter attribute is a deprecated read-only alias for dv. Removed in Deepwave 0.4.0 (see the stability policy documentation page).

Deepwave orders spatial dimensions from slowest-varying to fastest-varying and attaches no physical meaning to any of them; the component suffixes count from the fastest axis, so a 2-D run uses _y and _x and a 3-D run uses _z, _y and _x. See Dimension order and the axis convention.

Parameters:
  • v – An initial guess of the wavespeed model, of shape [*model_shape] or, for a batched model, [n_shots, *model_shape], where model_shape has ndim entries. See Batched models.

  • grid_spacing – The spatial grid cell size. It can be a single number that will be used for all dimensions, or a number for each dimension.

  • dv – An initial guess of the wavespeed scattering perturbation, of shape [*model_shape] or, for a batched model, [n_shots, *model_shape], where model_shape has ndim entries. See Batched models. At least one of dv/scatter must be provided.

  • scatter – Deprecated alias for dv, retained for backward compatibility. Removed in Deepwave 0.4.0 (see the stability policy documentation page). It takes exactly the shapes dv takes, batched or not. Providing both dv and scatter raises ValueError.

  • requires_grad – Which of this module’s models require a gradient. None (the default) defers entirely to the individual <model>_requires_grad flags below, each of which is unset (None) by default and then means False. True selects every model this module owns, False selects none, and a sequence of model names selects exactly those. Passing it together with an individual flag that was given explicitly raises ValueError rather than letting one silently win – whichever way the two point, and even when they agree, so that there is one rule rather than a direction-dependent one.

  • v_requires_grad – A bool specifying whether the requires_grad attribute of the wavespeed should be set. Unset by default, which means False. Cannot be combined with requires_grad.

  • dv_requires_grad – A bool specifying whether the requires_grad attribute of the wavespeed perturbation should be set. Unset by default, which means False. Cannot be combined with requires_grad.

  • scatter_requires_grad – Deprecated alias for dv_requires_grad. Removed in Deepwave 0.4.0 (see the stability policy documentation page). Providing it without scatter, or together with dv_requires_grad, raises ValueError.

  • storage – The storage policy for the wavefield history that the model gradient is computed from, as one object: either a mode string (“device”, “cpu”, “disk” or “none”) or a deepwave.Storage, which also carries the path and the lossy-compression flag. Defaults to “device”. This is an alternative spelling for the four individual storage_* arguments below, which remain permanently supported and are not deprecated; passing storage together with any of them raises DeepwaveValueError, because there is no honest way to merge two policies.

  • storage_mode – A string specifying the storage mode for intermediate data. One of “device”, “cpu”, “disk”, or “none”. Defaults to “device”. A permanent alias for Storage.mode; not deprecated.

  • storage_path – The directory that storage_mode=”disk” writes to. A string or any os.PathLike (such as a pathlib.Path). Defaults to the system temporary directory, resolved when you call rather than when Deepwave is imported, so TMPDIR set at any point before the call is honoured. Using the default with storage_mode=”disk” emits a one-time warning naming the directory, because on many systems it is a RAM-backed tmpfs far smaller than the disk. A permanent alias for Storage.path; not deprecated.

  • storage_compression – Lossy – changes the model gradient by about 0.2-4% in 2-D and 0.2-8% in 3-D. See deepwave.elastic().

Model ownership and gradients

Every model tensor given to the constructor is wrapped in a new torch.nn.Parameter that this module owns. The Parameter is a distinct leaf tensor that shares storage with the tensor you passed in: it is not a copy, so an in-place edit of one is visible in the other, but it is not the same object. Gradients therefore accumulate on the module’s own attribute (module.<model>.grad) and never on the tensor you passed in. A model requires a gradient only if you ask for one, through the constructor’s requires_grad argument or by calling module.<model>.requires_grad_(True) afterwards; hand module.parameters() to a torch.optim optimiser as usual. An optional model left as None is not a Parameter at all, so it appears in neither parameters() nor state_dict(); assigning a tensor to that attribute later stores a plain tensor rather than registering a Parameter, so it stays out of parameters() and state_dict() (and out of any checkpoint) – give the model to the constructor, or wrap it yourself in torch.nn.Parameter. Because the module owns a leaf Parameter, it cannot carry an upstream autograd graph: if the model is itself produced by another operation – a network mapping a latent vector to a wavespeed, a reparameterisation, a smoothing filter – wrapping it in a module severs that graph and no gradient reaches the producer. Use the functional interface for anything graph-producing; it takes the model as an ordinary tensor argument and backpropagates through it.

Initialises the Module.

forward(dt: float, *, sources: Mapping[str, tuple[Tensor, Tensor | Hicks] | tuple[Tensor, Tensor | Hicks, Tensor | None]] | None = None, receivers: Mapping[str, Tensor | Hicks | tuple[Tensor | Hicks, Tensor | None]] | None = None, bg_receivers: Mapping[str, Tensor | Hicks | tuple[Tensor | Hicks, Tensor | None]] | None = None, source_amplitudes: Tensor | None = None, source_locations: Tensor | Hicks | None = None, source_mask: Tensor | None = None, bg_receiver_locations: Tensor | Hicks | None = None, bg_receiver_mask: Tensor | None = None, sc_receiver_locations: Tensor | Hicks | None = None, receiver_locations: Tensor | Hicks | None = None, sc_receiver_mask: Tensor | None = None, wavefield_0: Tensor | None = None, wavefield_m1: Tensor | None = None, wavefield_sc_0: Tensor | None = None, wavefield_sc_m1: Tensor | None = None, initial_state: PropagatorOutput | Mapping[str, Tensor | AuxiliaryState] | None = None, accuracy: Literal[2, 4, 6, 8] = 4, pml_width: int | Sequence[int] = 20, boundary: str | Sequence[str] | None = None, pml_freq: float | None = None, max_vel: float | None = None, courant_number: float | None = None, survey_pad: int | Sequence[int | None] | None = None, origin: Sequence[int] | None = None, nt: int | None = None, model_gradient_sampling_interval: int = 1, checkpoint_segments: int | None = None, freq_taper_frac: float = 0.0, time_pad_frac: float = 0.0, time_taper: bool = False, check_output_finite: bool = True, forward_callback: Callback | None = None, backward_callback: Callback | None = None, callback_frequency: int = 1, backend: str = 'auto', python_backend: Literal['eager', 'jit', 'compile'] | bool | None = None, storage: str | Storage | None = None, storage_mode: Literal['device', 'cpu', 'disk', 'none'] | None = None, storage_path: str | PathLike[str] | None = None, storage_compression: bool | None = None, location_interpolation: Literal['auto', 'hicks', 'none'] = 'auto', location_interpolation_halfwidth: int = 4) → PropagatorOutput

Perform forward propagation/modelling.

See scalar_born() for details (the constructor-owned v/dv/ grid_spacing and the storage settings are supplied from the stored module state).

Parameters:
  • dt – The temporal grid cell size. See scalar() for the unit-consistency requirement across v/grid_spacing/dt.

  • sources – This simulation’s sources, keyed by channel. This is the canonical way to specify a survey. Each value is (amplitudes, locations), or (amplitudes, locations, mask) to include an activity mask. This propagator has one channel, u (the scalar wavefield), in every dimensionality, spelled that way however this propagator’s own flat keywords are spelled (they carry no channel suffix at all). Exactly equivalent to the flat source_amplitudes/source_locations/source_mask keywords, which remain permanently supported; giving the channel in both forms raises. Optional.

  • receivers – The receivers that record the scattered wavefield, keyed by channel, as the canonical alternative to the flat sc_receiver_locations/sc_receiver_mask keywords (of which receiver_locations is a deprecated spelling). Each value is a locations object, or (locations, mask) to include an activity mask. The one channel is u. Giving the channel in both forms raises. Optional.

  • bg_receivers – The receivers that record the background wavefield, keyed by channel, as the canonical alternative to the flat bg_receiver_locations/bg_receiver_mask keywords. Values take the same forms, and use the same one channel, as receivers’. Giving the channel in both forms raises. Optional.

  • source_amplitudes – A torch.Tensor with dimensions [shot, source, time] holding the background source amplitudes, which are the same quantity, with the same units, as deepwave.scalar()’s; see Units and source conventions.

  • source_locations – A torch.Tensor containing the source locations. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active. True means active; False means that point injects nothing, and its coordinates in source_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of source_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • sc_receiver_locations – A torch.Tensor containing the locations of the receivers that record the scattered wavefield. The sc_ spelling makes the name match the order of the returned tuple, in which the background data comes first and the scattered data second; the same spelling is used by deepwave.acoustic_born() and deepwave.elastic_born(). A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • sc_receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active. True means active; False means that point records zeros, and its coordinates in sc_receiver_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of sc_receiver_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • receiver_locations – Deprecated spelling of sc_receiver_locations, which means exactly the same thing. It is the spelling v0.0.27 shipped; it warns deepwave.DeepwaveFutureWarning and will be removed in Deepwave 0.4.0. Passing both raises. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • bg_receiver_locations – A torch.Tensor with dimensions [shot, receiver, ndim], containing the coordinates of the cell containing each receiver of the background wavefield. Optional. It should have torch.long (int64) datatype. If not provided, the output bg_receiver_amplitudes torch.Tensor will be empty. If backpropagation will be performed, the location of each background receiver must be unique within the same shot. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • bg_receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers recording the background wavefield are active. True means active; False means that point records zeros, and its coordinates in bg_receiver_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of bg_receiver_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • accuracy – See deepwave.scalar().

  • pml_width – See deepwave.scalar(). A face whose pml_width is zero does not absorb: it becomes an implicit reflecting boundary whose type depends on the propagator and the field component. See Reflecting edges for the per-propagator reflection coefficient and surface location, and prefer naming the condition with boundary (or deepwave.boundaries()), which also uses the exact image rather than this approximate one.

  • boundary – An optional single string, applying to every face, or a list of 2 * ndim strings selecting a first-class boundary condition per face, in the same order as pml_width. Each entry is one of “pml” (absorbing; boundary=”pml” is the spelled-out default, and behaves in every respect – including the warnings it emits – as omitting boundary does), “free” (a pressure-release surface: the field is held to zero on the boundary and reflects with inverted polarity, reflection coefficient -1), “rigid” (a rigid wall: the field reflects with preserved polarity, +1), or “periodic” (the face wraps around to the opposite face). Both the background and the scattered Born wavefields obey the chosen faces (mirroring is linear, so the scattered field uses the same image rules). A “free”, “rigid”, or “periodic” face requires pml_width to be zero on that face, and “periodic” must be set on both faces of a dimension. Unlike a bare zero-pml_width face (only an approximate reflector above accuracy 2), a “free”/”rigid” face fills the exterior with the exact odd/even image at every accuracy order, meeting the analytic reflection coefficient to better than 1%. See the boundary-conditions documentation for the precise ghost-plane conventions. Optional, default None. Availability: this propagator accepts all four names, on every face, at every accuracy.

  • pml_freq – See deepwave.scalar().

  • max_vel – See deepwave.scalar().

  • courant_number – See deepwave.scalar().

  • survey_pad – See deepwave.scalar().

  • wavefield_0 – The wavefield at time step 0. Like every initial wavefield, it may be provided either at the shape of the simulation domain (the extracted model region plus the PML), which is the shape a previous call returns, or at the shape of the extracted model region itself, which Deepwave zero-pads for you; see the wavefield_0 entry of deepwave.scalar().

  • wavefield_m1 – The wavefield at time step -1.

  • wavefield_sc_0 – The scattered wavefield at time step 0.

  • wavefield_sc_m1 – The scattered wavefield at time step -1.

  • initial_state – See deepwave.scalar(). The mapping keys are this propagator’s own public state field names (wavefield_0, wavefield_m1, wavefield_sc_0, wavefield_sc_m1) and auxiliary_state; its bg_receiver_amplitudes and sc_receiver_amplitudes fields are ignored. The individual absorbing-boundary field names (psiz_m1, …, zetax_sc_m1) are still accepted as keys, with a FutureWarning; they will stop being accepted in Deepwave 0.4.0.

  • origin – See deepwave.scalar().

  • nt – See deepwave.scalar().

  • model_gradient_sampling_interval – See deepwave.scalar().

  • checkpoint_segments –

    If given, split the time axis into this many segments and run each of them under torch.utils.checkpoint, which is the memory lever for a run whose wavefield history does not fit: peak backward memory falls to roughly one segment’s history plus the states held at the segment boundaries, at the cost of running the forward pass of each segment a second time during the backward pass. None (the default) runs the whole time axis in one piece, exactly as before. It is the same thing as calling deepwave.checkpointing.run_segments() with n_segments= and checkpoint=True, and returns the same combined output – the same fields, the same info and the same auxiliary_state a direct call gives. initial_state, the storage arguments and every physics option are supported; nt may pad or truncate supplied source amplitudes for the whole run. Source-free nt calls, individual initial-wavefield arguments and per-step callbacks are not supported, and say so.

    Take the gradient with loss.backward(). The checkpointing is reentrant, and torch.utils.checkpoint does not support torch.autograd.grad() (or an inputs= argument to .backward()) in that mode: it raises. If you need torch.autograd.grad, call deepwave.checkpointing.run_segments() directly with use_reentrant=False.

  • freq_taper_frac – See deepwave.scalar().

  • time_pad_frac – See deepwave.scalar().

  • time_taper – See deepwave.scalar().

  • check_output_finite – See deepwave.scalar().

  • forward_callback – See deepwave.scalar().

  • backward_callback – See deepwave.scalar().

  • callback_frequency – See deepwave.scalar().

  • backend – Which execution backend to use: "auto" (the default), "compiled", "eager" or "torch_compile". See Backends for what each value selects, what "auto" resolves to, and how the backends agree numerically. "auto" resolves to the compiled C/CUDA kernel.

  • python_backend – See deepwave.scalar().

  • storage – The storage policy for the wavefield history that the model gradient is computed from, as one object: either a mode string (“device”, “cpu”, “disk” or “none”) or a deepwave.Storage, which also carries the path and the lossy-compression flag. Defaults to “device”. This is an alternative spelling for the four individual storage_* arguments below, which remain permanently supported and are not deprecated; passing storage together with any of them raises DeepwaveValueError, because there is no honest way to merge two policies.

  • storage_mode – A string specifying the storage mode for intermediate data. One of “device”, “cpu”, “disk”, or “none”. Defaults to “device”. A permanent alias for Storage.mode; not deprecated.

  • storage_path – The directory that storage_mode=”disk” writes to. A string or any os.PathLike (such as a pathlib.Path). Defaults to the system temporary directory, resolved when you call rather than when Deepwave is imported, so TMPDIR set at any point before the call is honoured. Using the default with storage_mode=”disk” emits a one-time warning naming the directory, because on many systems it is a RAM-backed tmpfs far smaller than the disk. A permanent alias for Storage.path; not deprecated.

  • storage_compression – Lossy – changes the model gradient by about 0.2-4% in 2-D and 0.2-8% in 3-D. See deepwave.elastic().

  • location_interpolation – See deepwave.scalar(). The single source interpolator is shared by the background and scattered source inputs, while the background and scattered receivers are gathered independently. "auto" interpolates a floating-point locations Tensor only when at least one non-ignored coordinate lies more than deepwave.ON_NODE_TOLERANCE cells off its component’s grid node – except that a floating-point Tensor with requires_grad=True is always interpolated, even when every coordinate is on-node, because only the interpolated path carries gradients back to the positions. That changes the forward values for an on-node requires_grad locations Tensor relative to the same Tensor without requires_grad. Pass location_interpolation="none" for the exact path (positions are then not differentiable), or accept the band-limited values. RunInfo.location_interpolation_used records which path was taken: "none" or "hicks", or "mixed" if this propagator has several channels of one kind (several source components, say) and they did not all take the same path.

  • location_interpolation_halfwidth – See deepwave.scalar().

property scatter: Tensor

Deprecated alias for the dv attribute, retained for compatibility.

Removed in Deepwave 0.4.0 (see the stability policy documentation page). Read-only: assign to dv instead.

ScalarBorn Function

deepwave.scalar_born(v: Tensor, grid_spacing: float | Sequence[float], dt: float, *, dv: Tensor | None = None, scatter: Tensor | None = None, sources: Mapping[str, tuple[Tensor, Tensor | Hicks] | tuple[Tensor, Tensor | Hicks, Tensor | None]] | None = None, receivers: Mapping[str, Tensor | Hicks | tuple[Tensor | Hicks, Tensor | None]] | None = None, bg_receivers: Mapping[str, Tensor | Hicks | tuple[Tensor | Hicks, Tensor | None]] | None = None, source_amplitudes: Tensor | None = None, source_locations: Tensor | Hicks | None = None, source_mask: Tensor | None = None, bg_receiver_locations: Tensor | Hicks | None = None, bg_receiver_mask: Tensor | None = None, sc_receiver_locations: Tensor | Hicks | None = None, receiver_locations: Tensor | Hicks | None = None, sc_receiver_mask: Tensor | None = None, wavefield_0: Tensor | None = None, wavefield_m1: Tensor | None = None, wavefield_sc_0: Tensor | None = None, wavefield_sc_m1: Tensor | None = None, initial_state: PropagatorOutput | Mapping[str, Tensor | AuxiliaryState] | None = None, accuracy: Literal[2, 4, 6, 8] = 4, pml_width: int | Sequence[int] = 20, boundary: str | Sequence[str] | None = None, pml_freq: float | None = None, max_vel: float | None = None, courant_number: float | None = None, survey_pad: int | Sequence[int | None] | None = None, origin: Sequence[int] | None = None, nt: int | None = None, model_gradient_sampling_interval: int = 1, checkpoint_segments: int | None = None, freq_taper_frac: float = 0.0, time_pad_frac: float = 0.0, time_taper: bool = False, check_output_finite: bool = True, forward_callback: Callback | None = None, backward_callback: Callback | None = None, callback_frequency: int = 1, backend: str = 'auto', python_backend: Literal['eager', 'jit', 'compile'] | bool | None = None, storage: str | Storage | None = None, storage_mode: Literal['device', 'cpu', 'disk', 'none'] | None = None, storage_path: str | PathLike[str] | None = None, storage_compression: bool | None = None, location_interpolation: Literal['auto', 'hicks', 'none'] = 'auto', location_interpolation_halfwidth: int = 4) → PropagatorOutput

Scalar Born wave propagation (functional interface).

Stability: stable (see Stability policy).

This function performs Born forward modelling with the scalar wave equation. The outputs are differentiable with respect to the wavespeed, the wavespeed perturbation, the source amplitudes, and the initial wavefields.

For computational performance, multiple shots may be propagated simultaneously.

Most arguments and returns are the same as those of scalar(), so only those that are different will be described here.

Deepwave orders spatial dimensions from slowest-varying to fastest-varying and attaches no physical meaning to any of them; the component suffixes count from the fastest axis, so a 2-D run uses _y and _x and a 3-D run uses _z, _y and _x. See Dimension order and the axis convention.

Parameters:
  • v – The wavespeed model, of shape [*model_shape] or, for a batched model, [n_shots, *model_shape], where model_shape has ndim entries. See Batched models.

  • grid_spacing – The spatial grid cell size.

  • dt – The temporal grid cell size. See scalar() for the unit-consistency requirement across v/grid_spacing/dt.

  • dv – The wavespeed scattering perturbation model, of shape [*model_shape] or, for a batched model, [n_shots, *model_shape], where model_shape has ndim entries. See Batched models. It is the same quantity as deepwave.acoustic_born()’s dv, and the direction in which the Born forward linearises v. The scattered field is exactly the directional derivative of scalar() with respect to v in this direction (the kernel’s coupling term is 2 v dt^2 dv w, the first-order term of d(v^2) = 2 v dv), and Deepwave’s test suite pins the scattered field against a finite difference of scalar() in that direction. At least one of dv/scatter must be provided.

  • scatter – Deprecated alias for dv, retained for backward compatibility. Removed in Deepwave 0.4.0 (see the stability policy documentation page). It takes exactly the shapes dv takes, batched or not. Providing both dv and scatter raises ValueError.

  • sources – This simulation’s sources, keyed by channel. This is the canonical way to specify a survey. Each value is (amplitudes, locations), or (amplitudes, locations, mask) to include an activity mask. This propagator has one channel, u (the scalar wavefield), in every dimensionality, spelled that way however this propagator’s own flat keywords are spelled (they carry no channel suffix at all). Exactly equivalent to the flat source_amplitudes/source_locations/source_mask keywords, which remain permanently supported; giving the channel in both forms raises. Optional.

  • receivers – The receivers that record the scattered wavefield, keyed by channel, as the canonical alternative to the flat sc_receiver_locations/sc_receiver_mask keywords (of which receiver_locations is a deprecated spelling). Each value is a locations object, or (locations, mask) to include an activity mask. The one channel is u. Giving the channel in both forms raises. Optional.

  • bg_receivers – The receivers that record the background wavefield, keyed by channel, as the canonical alternative to the flat bg_receiver_locations/bg_receiver_mask keywords. Values take the same forms, and use the same one channel, as receivers’. Giving the channel in both forms raises. Optional.

  • source_amplitudes – A torch.Tensor with dimensions [shot, source, time] holding the background source amplitudes, which are the same quantity, with the same units, as deepwave.scalar()’s; see Units and source conventions.

  • source_locations – A torch.Tensor containing the source locations. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active. True means active; False means that point injects nothing, and its coordinates in source_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of source_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • sc_receiver_locations – A torch.Tensor containing the locations of the receivers that record the scattered wavefield. The sc_ spelling makes the name match the order of the returned tuple, in which the background data comes first and the scattered data second; the same spelling is used by deepwave.acoustic_born() and deepwave.elastic_born(). A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • sc_receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active. True means active; False means that point records zeros, and its coordinates in sc_receiver_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of sc_receiver_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • receiver_locations – Deprecated spelling of sc_receiver_locations, which means exactly the same thing. It is the spelling v0.0.27 shipped; it warns deepwave.DeepwaveFutureWarning and will be removed in Deepwave 0.4.0. Passing both raises. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • bg_receiver_locations – A torch.Tensor with dimensions [shot, receiver, ndim], containing the coordinates of the cell containing each receiver of the background wavefield. Optional. It should have torch.long (int64) datatype. If not provided, the output bg_receiver_amplitudes torch.Tensor will be empty. If backpropagation will be performed, the location of each background receiver must be unique within the same shot. A point whose coordinates are all deepwave.IGNORE_LOCATION is ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare against deepwave.IGNORE_LOCATION; do not hard-code its value.

  • bg_receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers recording the background wavefield are active. True means active; False means that point records zeros, and its coordinates in bg_receiver_locations are then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions of bg_receiver_locations, which must be given. This is the modern spelling of the deepwave.IGNORE_LOCATION sentinel and produces bit-identical results; supplying both a mask and a sentinel-containing locations tensor for the same component raises, as does combining a mask with a deepwave.Hicks object: a Hicks carries its own already-resolved points, so disable points inside it by setting all of their coordinates to deepwave.IGNORE_LOCATION in the locations it was built from. Optional.

  • accuracy – See deepwave.scalar().

  • pml_width – See deepwave.scalar(). A face whose pml_width is zero does not absorb: it becomes an implicit reflecting boundary whose type depends on the propagator and the field component. See Reflecting edges for the per-propagator reflection coefficient and surface location, and prefer naming the condition with boundary (or deepwave.boundaries()), which also uses the exact image rather than this approximate one.

  • boundary – An optional single string, applying to every face, or a list of 2 * ndim strings selecting a first-class boundary condition per face, in the same order as pml_width. Each entry is one of “pml” (absorbing; boundary=”pml” is the spelled-out default, and behaves in every respect – including the warnings it emits – as omitting boundary does), “free” (a pressure-release surface: the field is held to zero on the boundary and reflects with inverted polarity, reflection coefficient -1), “rigid” (a rigid wall: the field reflects with preserved polarity, +1), or “periodic” (the face wraps around to the opposite face). Both the background and the scattered Born wavefields obey the chosen faces (mirroring is linear, so the scattered field uses the same image rules). A “free”, “rigid”, or “periodic” face requires pml_width to be zero on that face, and “periodic” must be set on both faces of a dimension. Unlike a bare zero-pml_width face (only an approximate reflector above accuracy 2), a “free”/”rigid” face fills the exterior with the exact odd/even image at every accuracy order, meeting the analytic reflection coefficient to better than 1%. See the boundary-conditions documentation for the precise ghost-plane conventions. Optional, default None. Availability: this propagator accepts all four names, on every face, at every accuracy.

  • pml_freq – See deepwave.scalar().

  • max_vel – See deepwave.scalar().

  • courant_number – See deepwave.scalar().

  • survey_pad – See deepwave.scalar().

  • wavefield_0 – The wavefield at time step 0. Like every initial wavefield, it may be provided either at the shape of the simulation domain (the extracted model region plus the PML), which is the shape a previous call returns, or at the shape of the extracted model region itself, which Deepwave zero-pads for you; see the wavefield_0 entry of deepwave.scalar().

  • wavefield_m1 – The wavefield at time step -1.

  • wavefield_sc_0 – The scattered wavefield at time step 0.

  • wavefield_sc_m1 – The scattered wavefield at time step -1.

  • initial_state – See deepwave.scalar(). The mapping keys are this propagator’s own public state field names (wavefield_0, wavefield_m1, wavefield_sc_0, wavefield_sc_m1) and auxiliary_state; its bg_receiver_amplitudes and sc_receiver_amplitudes fields are ignored. The individual absorbing-boundary field names (psiz_m1, …, zetax_sc_m1) are still accepted as keys, with a FutureWarning; they will stop being accepted in Deepwave 0.4.0.

  • origin – See deepwave.scalar().

  • nt – See deepwave.scalar().

  • model_gradient_sampling_interval – See deepwave.scalar().

  • checkpoint_segments –

    If given, split the time axis into this many segments and run each of them under torch.utils.checkpoint, which is the memory lever for a run whose wavefield history does not fit: peak backward memory falls to roughly one segment’s history plus the states held at the segment boundaries, at the cost of running the forward pass of each segment a second time during the backward pass. None (the default) runs the whole time axis in one piece, exactly as before. It is the same thing as calling deepwave.checkpointing.run_segments() with n_segments= and checkpoint=True, and returns the same combined output – the same fields, the same info and the same auxiliary_state a direct call gives. initial_state, the storage arguments and every physics option are supported; nt may pad or truncate supplied source amplitudes for the whole run. Source-free nt calls, individual initial-wavefield arguments and per-step callbacks are not supported, and say so.

    Take the gradient with loss.backward(). The checkpointing is reentrant, and torch.utils.checkpoint does not support torch.autograd.grad() (or an inputs= argument to .backward()) in that mode: it raises. If you need torch.autograd.grad, call deepwave.checkpointing.run_segments() directly with use_reentrant=False.

  • freq_taper_frac – See deepwave.scalar().

  • time_pad_frac – See deepwave.scalar().

  • time_taper – See deepwave.scalar().

  • check_output_finite – See deepwave.scalar().

  • forward_callback – See deepwave.scalar().

  • backward_callback – See deepwave.scalar().

  • callback_frequency – See deepwave.scalar().

  • backend – Which execution backend to use: "auto" (the default), "compiled", "eager" or "torch_compile". See Backends for what each value selects, what "auto" resolves to, and how the backends agree numerically. "auto" resolves to the compiled C/CUDA kernel.

  • python_backend – See deepwave.scalar().

  • storage – The storage policy for the wavefield history that the model gradient is computed from, as one object: either a mode string (“device”, “cpu”, “disk” or “none”) or a deepwave.Storage, which also carries the path and the lossy-compression flag. Defaults to “device”. This is an alternative spelling for the four individual storage_* arguments below, which remain permanently supported and are not deprecated; passing storage together with any of them raises DeepwaveValueError, because there is no honest way to merge two policies.

  • storage_mode – A string specifying the storage mode for intermediate data. One of “device”, “cpu”, “disk”, or “none”. Defaults to “device”. A permanent alias for Storage.mode; not deprecated.

  • storage_path – The directory that storage_mode=”disk” writes to. A string or any os.PathLike (such as a pathlib.Path). Defaults to the system temporary directory, resolved when you call rather than when Deepwave is imported, so TMPDIR set at any point before the call is honoured. Using the default with storage_mode=”disk” emits a one-time warning naming the directory, because on many systems it is a RAM-backed tmpfs far smaller than the disk. A permanent alias for Storage.path; not deprecated.

  • storage_compression – Lossy – changes the model gradient by about 0.2-4% in 2-D and 0.2-8% in 3-D. See deepwave.elastic().

  • location_interpolation – See deepwave.scalar(). The single source interpolator is shared by the background and scattered source inputs, while the background and scattered receivers are gathered independently. "auto" interpolates a floating-point locations Tensor only when at least one non-ignored coordinate lies more than deepwave.ON_NODE_TOLERANCE cells off its component’s grid node – except that a floating-point Tensor with requires_grad=True is always interpolated, even when every coordinate is on-node, because only the interpolated path carries gradients back to the positions. That changes the forward values for an on-node requires_grad locations Tensor relative to the same Tensor without requires_grad. Pass location_interpolation="none" for the exact path (positions are then not differentiable), or accept the band-limited values. RunInfo.location_interpolation_used records which path was taken: "none" or "hicks", or "mixed" if this propagator has several channels of one kind (several source components, say) and they did not all take the same path.

  • location_interpolation_halfwidth – See deepwave.scalar().

Returns:

a tuple subclass whose fields, in order, are:

  • wavefield_0: The non-scattered wavefield at the final time step.

  • wavefield_m1: The non-scattered wavefield at the second-to-last time step.

  • auxiliary_state: A deepwave.AuxiliaryState holding the absorbing boundary’s memory variables, for both the background and the scattered wavefield. Opaque: pass it back (as part of initial_state) to continue the simulation; its contents are implementation state rather than physics.

  • wavefield_sc_0: The scattered wavefield.

  • wavefield_sc_m1: The scattered wavefield.

  • bg_receiver_amplitudes: The receiver amplitudes recorded at the provided receiver locations, extracted from the background (non-scattered) wavefield.

  • sc_receiver_amplitudes: The receiver amplitudes recorded at the provided receiver locations, extracted from the scattered wavefield. out.receiver_amplitudes is a permanent alias of this field – the spelling v0.0.27 shipped, kept for good and never warning – but out.names and out.as_dict() report the sc_ spelling.

The names match the order: the background receiver amplitudes (bg_receiver_amplitudes) come first and the scattered ones (sc_receiver_amplitudes) second, so out[-2] is the background data and out[-1] is the scattered data. The same bg_/sc_ pairing is used by deepwave.acoustic_born() and deepwave.elastic_born(), and by the bg_receivers=/receivers= arguments: note that receivers= and deepwave.PropagatorOutput.receivers both mean the scattered data.

The fields are also accessible by name, which removes the need to remember the positional order: the wavefields use their initial-state names (wavefield_0, wavefield_m1, …, wavefield_sc_0, …) and the receiver data is exposed as bg_receiver_amplitudes (background) and sc_receiver_amplitudes (scattered).

Return type:

deepwave.PropagatorOutput