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
_yand_xand a 3-D run uses_z,_yand_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], wheremodel_shapehasndimentries. 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_gradflags below, each of which is unset (None) by default and then meansFalse.Trueselects every model this module owns,Falseselects none, and a sequence of model names selects exactly those. Passing it together with an individual flag that was given explicitly raisesValueErrorrather 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.Parameterthat 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’srequires_gradargument or by callingmodule.<model>.requires_grad_(True)afterwards; handmodule.parameters()to atorch.optimoptimiser as usual. An optional model left asNoneis not a Parameter at all, so it appears in neitherparameters()norstate_dict(); assigning a tensor to that attribute later stores a plain tensor rather than registering a Parameter, so it stays out ofparameters()andstate_dict()(and out of any checkpoint) – give the model to the constructor, or wrap it yourself intorch.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, anddt; 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 flatsource_amplitudes/source_locations/source_maskkeywords, 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_maskkeywords. Each value is a locations object, or(locations, mask)to include an activity mask. The one channel isu. 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.Hicksobject 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 alldeepwave.IGNORE_LOCATIONis ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active.
Truemeans active;Falsemeans that point injects nothing, and its coordinates insource_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofsource_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin 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_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active.
Truemeans active;Falsemeans that point records zeros, and its coordinates inreceiver_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofreceiver_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin 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 (adeepwave.AuxiliaryStatefrom 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_widthis 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 withboundary(ordeepwave.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 omittedpml_freqthen takes the value recorded ininitial_state. The value actually used is reported asout.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 pinpml_freqwhen 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.0as 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_NUMBERis 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 chosenaccuracyorder 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_frequencyalways 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 callingdeepwave.checkpointing.run_segments()withn_segments=andcheckpoint=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, andtorch.utils.checkpointdoes not supporttorch.autograd.grad()(or aninputs=argument to.backward()) in that mode: it raises. If you needtorch.autograd.grad, calldeepwave.checkpointing.run_segments()directly withuse_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
NaNorInf, raisingdeepwave.DeepwaveFloatingPointErrorif 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 aNaNitself. The check is skipped undervmapand the othertorch.functransforms, 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.CallbackStatefor 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, anddeepwave.CallbackState.get_gradient()returns a live view into the gradient accumulator whose in-place mutation is supported (see Custom imaging condition). Mutating whatdeepwave.CallbackState.get_wavefield()ordeepwave.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.CallbackStatefor the state that will be provided to the function (the same mutation contract applies). Uniquely among the propagators, the callback state’sget_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; seedeepwave.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, neverNone. Retaining that forward field costs a second full sampled wavefield history alongside the usual model-gradient one, so it is opt-in: passbackward_callback_needs_forward_wavefield=True.backward_callback_needs_forward_wavefield –
Whether
backward_callbackneedsget_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()’sbackward_storage_bytes(which does not itself account for this argument). Onbackend="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")raisesNotImplementedErrornaming this argument.deepwave.CallbackState.has_forward_wavefieldreports which case you are in without atry/except.The second history uses the same
storage_mode/storage_compressionsettings as the primary one, sostorage_mode="cpu"/"disk"andstorage_compression=Truereduce it exactly as they do the model-gradient storage (see Troubleshooting). The argument itself changes no number, butstorage_compression=Truedoes change what the callback sees: the fieldrole="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.stepandn_stepsuse user steps,dtis the user time step,internal_dtnames the CFL step, andtimeisstep * dtfrom this call’s start. The final state atntis 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
ValueErrorrather than being silently truncated to the cell below it, asv0.0.27did.
With genuinely off-grid coordinates the propagator performs band-limited injection/recording, an approximation. The manual
deepwave.Hicksinterface 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 thandeepwave.ON_NODE_TOLERANCEcells off its component’s grid node – except that a floating-point Tensor withrequires_grad=Trueis 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-noderequires_gradlocations Tensor relative to the same Tensor withoutrequires_grad. Passlocation_interpolation="none"for the exact path (positions are then not differentiable), or accept the band-limited values.RunInfo.location_interpolation_usedrecords 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
_yand_xand a 3-D run uses_z,_yand_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], wheremodel_shapehasndimentries. 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, anddt; 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 flatsource_amplitudes/source_locations/source_maskkeywords, 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_maskkeywords. Each value is a locations object, or(locations, mask)to include an activity mask. The one channel isu. 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.Hicksobject 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 alldeepwave.IGNORE_LOCATIONis ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active.
Truemeans active;Falsemeans that point injects nothing, and its coordinates insource_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofsource_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin 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_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active.
Truemeans active;Falsemeans that point records zeros, and its coordinates inreceiver_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofreceiver_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin 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 (adeepwave.AuxiliaryStatefrom 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_widthis 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 withboundary(ordeepwave.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 omittedpml_freqthen takes the value recorded ininitial_state. The value actually used is reported asout.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 pinpml_freqwhen 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.0as 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_NUMBERis 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 chosenaccuracyorder 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_frequencyalways 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 callingdeepwave.checkpointing.run_segments()withn_segments=andcheckpoint=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, andtorch.utils.checkpointdoes not supporttorch.autograd.grad()(or aninputs=argument to.backward()) in that mode: it raises. If you needtorch.autograd.grad, calldeepwave.checkpointing.run_segments()directly withuse_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
NaNorInf, raisingdeepwave.DeepwaveFloatingPointErrorif 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 aNaNitself. The check is skipped undervmapand the othertorch.functransforms, 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.CallbackStatefor 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, anddeepwave.CallbackState.get_gradient()returns a live view into the gradient accumulator whose in-place mutation is supported (see Custom imaging condition). Mutating whatdeepwave.CallbackState.get_wavefield()ordeepwave.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.CallbackStatefor the state that will be provided to the function (the same mutation contract applies). Uniquely among the propagators, the callback state’sget_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; seedeepwave.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, neverNone. Retaining that forward field costs a second full sampled wavefield history alongside the usual model-gradient one, so it is opt-in: passbackward_callback_needs_forward_wavefield=True.backward_callback_needs_forward_wavefield –
Whether
backward_callbackneedsget_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()’sbackward_storage_bytes(which does not itself account for this argument). Onbackend="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")raisesNotImplementedErrornaming this argument.deepwave.CallbackState.has_forward_wavefieldreports which case you are in without atry/except.The second history uses the same
storage_mode/storage_compressionsettings as the primary one, sostorage_mode="cpu"/"disk"andstorage_compression=Truereduce it exactly as they do the model-gradient storage (see Troubleshooting). The argument itself changes no number, butstorage_compression=Truedoes change what the callback sees: the fieldrole="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.stepandn_stepsuse user steps,dtis the user time step,internal_dtnames the CFL step, andtimeisstep * dtfrom this call’s start. The final state atntis 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
ValueErrorrather than being silently truncated to the cell below it, asv0.0.27did.
With genuinely off-grid coordinates the propagator performs band-limited injection/recording, an approximation. The manual
deepwave.Hicksinterface 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 thandeepwave.ON_NODE_TOLERANCEcells off its component’s grid node – except that a floating-point Tensor withrequires_grad=Trueis 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-noderequires_gradlocations Tensor relative to the same Tensor withoutrequires_grad. Passlocation_interpolation="none"for the exact path (positions are then not differentiable), or accept the band-limited values.RunInfo.location_interpolation_usedrecords 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.AuxiliaryStateholding 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:
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
_yand_xand a 3-D run uses_z,_yand_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], wheremodel_shapehasndimentries. 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], wheremodel_shapehasndimentries. 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_gradflags below, each of which is unset (None) by default and then meansFalse.Trueselects every model this module owns,Falseselects none, and a sequence of model names selects exactly those. Passing it together with an individual flag that was given explicitly raisesValueErrorrather 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.Parameterthat 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’srequires_gradargument or by callingmodule.<model>.requires_grad_(True)afterwards; handmodule.parameters()to atorch.optimoptimiser as usual. An optional model left asNoneis not a Parameter at all, so it appears in neitherparameters()norstate_dict(); assigning a tensor to that attribute later stores a plain tensor rather than registering a Parameter, so it stays out ofparameters()andstate_dict()(and out of any checkpoint) – give the model to the constructor, or wrap it yourself intorch.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 acrossv/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 flatsource_amplitudes/source_locations/source_maskkeywords, 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_maskkeywords (of whichreceiver_locationsis a deprecated spelling). Each value is a locations object, or(locations, mask)to include an activity mask. The one channel isu. 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_maskkeywords. Values take the same forms, and use the same one channel, asreceivers’. 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_LOCATIONis ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active.
Truemeans active;Falsemeans that point injects nothing, and its coordinates insource_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofsource_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin 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 bydeepwave.acoustic_born()anddeepwave.elastic_born(). A point whose coordinates are alldeepwave.IGNORE_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.sc_receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active.
Truemeans active;Falsemeans that point records zeros, and its coordinates insc_receiver_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofsc_receiver_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin the locations it was built from. Optional.receiver_locations – Deprecated spelling of
sc_receiver_locations, which means exactly the same thing. It is the spellingv0.0.27shipped; it warnsdeepwave.DeepwaveFutureWarningand will be removed in Deepwave 0.4.0. Passing both raises. A point whose coordinates are alldeepwave.IGNORE_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.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_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.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.
Truemeans active;Falsemeans that point records zeros, and its coordinates inbg_receiver_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofbg_receiver_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin the locations it was built from. Optional.accuracy – See
deepwave.scalar().pml_width – See
deepwave.scalar(). A face whosepml_widthis 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 withboundary(ordeepwave.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 callingdeepwave.checkpointing.run_segments()withn_segments=andcheckpoint=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, andtorch.utils.checkpointdoes not supporttorch.autograd.grad()(or aninputs=argument to.backward()) in that mode: it raises. If you needtorch.autograd.grad, calldeepwave.checkpointing.run_segments()directly withuse_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 thandeepwave.ON_NODE_TOLERANCEcells off its component’s grid node – except that a floating-point Tensor withrequires_grad=Trueis 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-noderequires_gradlocations Tensor relative to the same Tensor withoutrequires_grad. Passlocation_interpolation="none"for the exact path (positions are then not differentiable), or accept the band-limited values.RunInfo.location_interpolation_usedrecords 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().
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
_yand_xand a 3-D run uses_z,_yand_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], wheremodel_shapehasndimentries. See Batched models.grid_spacing – The spatial grid cell size.
dt – The temporal grid cell size. See
scalar()for the unit-consistency requirement acrossv/grid_spacing/dt.dv – The wavespeed scattering perturbation model, of shape
[*model_shape]or, for a batched model,[n_shots, *model_shape], wheremodel_shapehasndimentries. See Batched models. It is the same quantity asdeepwave.acoustic_born()’sdv, and the direction in which the Born forward linearisesv. The scattered field is exactly the directional derivative ofscalar()with respect tovin this direction (the kernel’s coupling term is2 v dt^2 dv w, the first-order term ofd(v^2) = 2 v dv), and Deepwave’s test suite pins the scattered field against a finite difference ofscalar()in that direction. At least one ofdv/scattermust 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 shapesdvtakes, batched or not. Providing bothdvandscatterraisesValueError.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 flatsource_amplitudes/source_locations/source_maskkeywords, 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_maskkeywords (of whichreceiver_locationsis a deprecated spelling). Each value is a locations object, or(locations, mask)to include an activity mask. The one channel isu. 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_maskkeywords. Values take the same forms, and use the same one channel, asreceivers’. 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_LOCATIONis ignored: it injects nothing. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.source_mask – A boolean torch.Tensor with dimensions [shot, source] selecting which sources are active.
Truemeans active;Falsemeans that point injects nothing, and its coordinates insource_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofsource_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin 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 bydeepwave.acoustic_born()anddeepwave.elastic_born(). A point whose coordinates are alldeepwave.IGNORE_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.IGNORE_LOCATION; do not hard-code its value.sc_receiver_mask – A boolean torch.Tensor with dimensions [shot, receiver] selecting which receivers are active.
Truemeans active;Falsemeans that point records zeros, and its coordinates insc_receiver_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofsc_receiver_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin the locations it was built from. Optional.receiver_locations – Deprecated spelling of
sc_receiver_locations, which means exactly the same thing. It is the spellingv0.0.27shipped; it warnsdeepwave.DeepwaveFutureWarningand will be removed in Deepwave 0.4.0. Passing both raises. A point whose coordinates are alldeepwave.IGNORE_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.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_LOCATIONis ignored: it records zeros. A point with only some coordinates set to the sentinel raises. Compare againstdeepwave.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.
Truemeans active;Falsemeans that point records zeros, and its coordinates inbg_receiver_locationsare then ignored entirely (they need not be valid, in range, or unique). The shape must match the leading dimensions ofbg_receiver_locations, which must be given. This is the modern spelling of thedeepwave.IGNORE_LOCATIONsentinel 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 adeepwave.Hicksobject: aHickscarries its own already-resolved points, so disable points inside it by setting all of their coordinates todeepwave.IGNORE_LOCATIONin the locations it was built from. Optional.accuracy – See
deepwave.scalar().pml_width – See
deepwave.scalar(). A face whosepml_widthis 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 withboundary(ordeepwave.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 callingdeepwave.checkpointing.run_segments()withn_segments=andcheckpoint=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, andtorch.utils.checkpointdoes not supporttorch.autograd.grad()(or aninputs=argument to.backward()) in that mode: it raises. If you needtorch.autograd.grad, calldeepwave.checkpointing.run_segments()directly withuse_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 thandeepwave.ON_NODE_TOLERANCEcells off its component’s grid node – except that a floating-point Tensor withrequires_grad=Trueis 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-noderequires_gradlocations Tensor relative to the same Tensor withoutrequires_grad. Passlocation_interpolation="none"for the exact path (positions are then not differentiable), or accept the band-limited values.RunInfo.location_interpolation_usedrecords 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.AuxiliaryStateholding 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_amplitudesis a permanent alias of this field – the spellingv0.0.27shipped, kept for good and never warning – butout.namesandout.as_dict()report thesc_spelling.
The names match the order: the background receiver amplitudes (
bg_receiver_amplitudes) come first and the scattered ones (sc_receiver_amplitudes) second, soout[-2]is the background data andout[-1]is the scattered data. The samebg_/sc_pairing is used bydeepwave.acoustic_born()anddeepwave.elastic_born(), and by thebg_receivers=/receivers=arguments: note thatreceivers=anddeepwave.PropagatorOutput.receiversboth 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 asbg_receiver_amplitudes(background) andsc_receiver_amplitudes(scattered).- Return type: