From 475e332bf5dc9f3ff4ebdef334dd9336548ad430 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Thu, 25 Jan 2024 19:17:30 +0000 Subject: [PATCH] build based on 1412cf7 --- dev/about/index.html | 2 +- dev/abstract_vectors/index.html | 8 ++++---- dev/basics/index.html | 2 +- dev/data_structures/index.html | 10 +++++----- dev/helper/index.html | 6 +++--- dev/index.html | 2 +- dev/installation/index.html | 2 +- dev/inversion/index.html | 12 ++++++------ dev/io/index.html | 2 +- dev/linear_operators/index.html | 10 +++++----- dev/preconditioners/index.html | 12 ++++++------ dev/pysource/index.html | 2 +- dev/search/index.html | 2 +- dev/search_index.js | 2 +- dev/tutorials/01_intro/index.html | 2 +- dev/tutorials/02_fwi_example_NLopt/index.html | 2 +- dev/tutorials/03_constrained_fwi/index.html | 2 +- .../04_judi_leading_edge_tutorial/index.html | 2 +- dev/tutorials/05_custom_misfit/index.html | 2 +- .../06_automatic_differentiation/index.html | 2 +- dev/tutorials/07_preconditionners/index.html | 2 +- dev/tutorials/imaging_conditions/index.html | 2 +- dev/tutorials/quickstart/index.html | 2 +- 23 files changed, 46 insertions(+), 46 deletions(-) diff --git a/dev/about/index.html b/dev/about/index.html index b45054801..b292e6a17 100644 --- a/dev/about/index.html +++ b/dev/about/index.html @@ -10,4 +10,4 @@ doi = {10.1190/geo2018-0174.1}, URL = {https://doi.org/10.1190/geo2018-0174.1}, eprint = {https://doi.org/10.1190/geo2018-0174.1} -}

Also visit the Devito homepage at https://www.devitoproject.org/publications for more information and references. If you need to cite a specific version of JUDI, you can find our citeable archives on Zenodo.

Contribution and community

We gladly welcome and encorage contributions from the community to improve our software and its usability. Feel free to:

Field examples

An example of using JUDI to invert field data is provided for the Viking Graben Line 12 which includes data preprocessing steps using Madagascar.

+}

Also visit the Devito homepage at https://www.devitoproject.org/publications for more information and references. If you need to cite a specific version of JUDI, you can find our citeable archives on Zenodo.

Contribution and community

We gladly welcome and encorage contributions from the community to improve our software and its usability. Feel free to:

Field examples

An example of using JUDI to invert field data is provided for the Viking Graben Line 12 which includes data preprocessing steps using Madagascar.

diff --git a/dev/abstract_vectors/index.html b/dev/abstract_vectors/index.html index 36c38424c..77893408b 100644 --- a/dev/abstract_vectors/index.html +++ b/dev/abstract_vectors/index.html @@ -5,7 +5,7 @@ seis_block = segy_read("test_file.segy") dobs = judiVector(seis_block; segy_depth_key="RecGroupElevation")

(4) Construct out-of-core data vector from SegyIO.SeisCon object (for large SEG-Y files):

using SegyIO
 seis_container = segy_scan("/path/to/data/directory","filenames",["GroupX","GroupY","RecGroupElevation","SourceDepth","dt"])
-dobs = judiVector(seis_container; segy_depth_key="RecGroupElevation")
source

Access fields (in-core data containers):

# Access i-th shot record
+dobs = judiVector(seis_container; segy_depth_key="RecGroupElevation")
source

Access fields (in-core data containers):

# Access i-th shot record
 x.data[i]
 
 # Extract judiVector for i-th shot
@@ -42,7 +42,7 @@
 y = x * α
 
 # Concatenate
-y = vcat(x, x)

judiWavefield

Abstract vector class for wavefields.

Construction:

JUDI.judiWavefieldType

judiWavefield nsrc::Integer dt::AbstractFloat data

Abstract vector for seismic wavefields.

Constructors

Construct wavefield vector from an info structure, a cell array of wavefields and the computational
time step dt:

judiWavefield(nsrc, dt, data)
source

Access fields:

# Access wavefield from i-th shot location
+y = vcat(x, x)

judiWavefield

Abstract vector class for wavefields.

Construction:

JUDI.judiWavefieldType

judiWavefield nsrc::Integer dt::AbstractFloat data

Abstract vector for seismic wavefields.

Constructors

Construct wavefield vector from an info structure, a cell array of wavefields and the computational
time step dt:

judiWavefield(nsrc, dt, data)
source

Access fields:

# Access wavefield from i-th shot location
 u.data[i]

Operations:

Supports some basic arithmetric operations:

# Size 
 size(u)
 
@@ -70,5 +70,5 @@
 # Info structure
 rhs.info

judiWeights

Abstract vector class for extended source weights. The weights for each shot location have the dimensions of the model (namely model.n).

Construction:

JUDI.judiWeightsMethod
judiWeights
     nsrc
-    weights

Abstract vector for weighting an extended source, which is injected at every grid point, as weighted by this vector. Constructors ============ Construct vector cell array of weights. The weights keyword
can also be a single (non-cell) array, in which case the weights are the same for all source positions: judiWeights(weights; nsrc=1)

source

Parameters:

Access fields:

# Access weights of i-th shot locatoin
-w.weights[i]

Operations:

Supports the same arithmetric operations as a judiVector.

+ weights

Abstract vector for weighting an extended source, which is injected at every grid point, as weighted by this vector. Constructors ============ Construct vector cell array of weights. The weights keyword
can also be a single (non-cell) array, in which case the weights are the same for all source positions: judiWeights(weights; nsrc=1)

source

Parameters:

Access fields:

# Access weights of i-th shot locatoin
+w.weights[i]

Operations:

Supports the same arithmetric operations as a judiVector.

diff --git a/dev/basics/index.html b/dev/basics/index.html index ef41c3d9f..90a4b6f11 100644 --- a/dev/basics/index.html +++ b/dev/basics/index.html @@ -320,4 +320,4 @@ for j=1:nsrc J.options.frequencies[j] = select_frequencies(q_dist; fmin=0.003, fmax=0.04, nf=nfreq) end

Once the options.frequencies field is set, on-the-fly DFTs are used for both born modeling and RTM. To save computational cost, we can limit the number of DFTs that are performed. Rather than computing the DFT at every time step, we can define a subsampling factor as follows:

# Compute DFT every 4 time steps
-J.options.dft_subsampling_factor=4
+J.options.dft_subsampling_factor=4 diff --git a/dev/data_structures/index.html b/dev/data_structures/index.html index 044396b05..24d5fb8f6 100644 --- a/dev/data_structures/index.html +++ b/dev/data_structures/index.html @@ -11,8 +11,8 @@ PhysicalParameter(v::Array{T, N}, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) where `v` is a vector or nd-array that is reshaped into shape `n` -PhysicalParameter(v::T, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) Creates a constant (single number) PhyicalParametersource

Unless specified otherwise with the return_array option in Options, the result of a migration/FWIgradient(judiJacobian, fwi_objective, lsrtm_objective) will be wrapped into a PhysicalParameter. THis allow better handling of different model parts and a better representation of the dimensional array.

Model structure

Data structure for velocity models in JUDI.

JUDI.ModelFunction
Model(n, d, o, m; epsilon=nothing, delta=nothing, theta=nothing,
-        phi=nothing, rho=nothing, qp=nothing, vs=nothing, nb=40)

The parameters n, d, o and m are mandatory, whith nb and other physical parameters being optional input arguments.

where

m: velocity model in slowness squared (s^2/km^2)

epsilon: Epsilon thomsen parameter ( between -1 and 1)

delta: Delta thomsen parameter ( between -1 and 1 and delta < epsilon)

theta: Anisotopy dip in radian

phi: Anisotropy asymuth in radian

rho: density (g / m^3)

qp: P-wave attenuation for visco-acoustic models

vs: S-wave velocity for elastic models.

nb: Number of ABC points

source

Accessible fields include all of the above parameters p.n, p.d, p.o, p.data. Additionaly, arithmetic operation are all impemented such as addition, multiplication, broadcasting and indexing. Linear algebra operation are implemented as well but will return a standard Julia vector if the matrix used is external to JUDI.

Access fields:

Accessible fields include all of the above parameters, which can be accessed as follows:

# Access model
+PhysicalParameter(v::T, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) Creates a constant (single number) PhyicalParameter
source

Unless specified otherwise with the return_array option in Options, the result of a migration/FWIgradient(judiJacobian, fwi_objective, lsrtm_objective) will be wrapped into a PhysicalParameter. THis allow better handling of different model parts and a better representation of the dimensional array.

Model structure

Data structure for velocity models in JUDI.

JUDI.ModelFunction
Model(n, d, o, m; epsilon=nothing, delta=nothing, theta=nothing,
+        phi=nothing, rho=nothing, qp=nothing, vs=nothing, nb=40)

The parameters n, d, o and m are mandatory, whith nb and other physical parameters being optional input arguments.

where

m: velocity model in slowness squared (s^2/km^2)

epsilon: Epsilon thomsen parameter ( between -1 and 1)

delta: Delta thomsen parameter ( between -1 and 1 and delta < epsilon)

theta: Anisotopy dip in radian

phi: Anisotropy asymuth in radian

rho: density (g / m^3)

qp: P-wave attenuation for visco-acoustic models

vs: S-wave velocity for elastic models.

nb: Number of ABC points

source

Accessible fields include all of the above parameters p.n, p.d, p.o, p.data. Additionaly, arithmetic operation are all impemented such as addition, multiplication, broadcasting and indexing. Linear algebra operation are implemented as well but will return a standard Julia vector if the matrix used is external to JUDI.

Access fields:

Accessible fields include all of the above parameters, which can be accessed as follows:

# Access model
 model.m
 
 # Access number of grid points
@@ -38,14 +38,14 @@
 src_geometry = Geometry(seis_block; key="source", segy_depth_key="SourceDepth")

Check the seis_block's header entries to findall out which keywords contain the depth coordinates. The source depth keyword is either SourceDepth or SourceSurfaceElevation. The receiver depth keyword is typically RecGroupElevation.

(4) Read source and receiver geometries from out-of-core SEG-Y files (for large data sets). Returns an out-of-core geometry object GeometryOOC without the source/receiver coordinates, but a lookup table instead:

using SegyIO
 seis_container = segy_scan("/path/to/data/directory","filenames",["GroupX","GroupY","RecGroupElevation","SourceDepth","dt"])
 rec_geometry = Geometry(seis_container; key="receiver", segy_depth_key="RecGroupElevation")
-src_geometry = Geometry(seis_container; key="source", segy_depth_key="SourceDepth")
source

From the optional arguments, you have to pass (at least) two of dt, nt and t. The third value is automatically determined and set from the two other values. a Geometry can be constructed in a number of different ways for in-core and out-of-core cases. Check our examples and the source for additional details while the documentation is being extended.

Access fields:

Accessible fields include all of the above parameters, which can be accessed as follows:

# Access cell arrays of x coordinates:
+src_geometry = Geometry(seis_container; key="source", segy_depth_key="SourceDepth")
source

From the optional arguments, you have to pass (at least) two of dt, nt and t. The third value is automatically determined and set from the two other values. a Geometry can be constructed in a number of different ways for in-core and out-of-core cases. Check our examples and the source for additional details while the documentation is being extended.

Access fields:

Accessible fields include all of the above parameters, which can be accessed as follows:

# Access cell arrays of x coordinates:
 geometry.xloc
 
 # Access x coordinates of the i-th source location
 geometry.xloc[i]
 
 # Access j-th receiver location (in x) of the i-th source location
-geometry.xloc[i][j]

Geometry utilities

A few utilities to manipulates geometries are provided as well.

JUDI.super_shot_geometryFunction
super_shot_geometry(Geometry)

Merge all the sub-geometries 1:get_nsrc(Geometry) into a single supershot geometry

source
JUDI.reciprocal_geomFunction
reciprocal_geom(sourceGeom, recGeom)

Applies reciprocity to the par of geometries sourceGeom and recGeom where each source becomes a receiver and each receiver becomes a source.

This method expects:

  • Both geometries to be In Core. If the geometries are OOC they will be converted to in core geometries
  • The metadata to be compatible. In details all the time sampling rates (dt) and recording times (t) must be the same
  • The source to be single point sources. This method will error if a simultaneous sources (multiple poisiton for a single source) are used.
source

Options structure

The options structure allows setting several modeling parameters.

JUDI.OptionsFunction
JUDIOptions
+geometry.xloc[i][j]

Geometry utilities

A few utilities to manipulates geometries are provided as well.

JUDI.super_shot_geometryFunction
super_shot_geometry(Geometry)

Merge all the sub-geometries 1:get_nsrc(Geometry) into a single supershot geometry

source
JUDI.reciprocal_geomFunction
reciprocal_geom(sourceGeom, recGeom)

Applies reciprocity to the par of geometries sourceGeom and recGeom where each source becomes a receiver and each receiver becomes a source.

This method expects:

  • Both geometries to be In Core. If the geometries are OOC they will be converted to in core geometries
  • The metadata to be compatible. In details all the time sampling rates (dt) and recording times (t) must be the same
  • The source to be single point sources. This method will error if a simultaneous sources (multiple poisiton for a single source) are used.
source

Options structure

The options structure allows setting several modeling parameters.

JUDI.OptionsFunction
JUDIOptions
     space_order::Integer
     free_surface::Bool
     limit_m::Bool
@@ -70,4 +70,4 @@
         num_checkpoints=nothing, checkpoints_maxmem=nothing,
         frequencies=[], isic=false,
         subsampling_factor=1, dft_subsampling_factor=1, return_array=false,
-        dt_comp=nothing, f0=0.015f0)
source

notes

Option has been renamed JUDIOptions as of JUDI version 4.0 to avoid potential (and exisiting) conflicts wiht other packages exporting an Options structure.

+ dt_comp=nothing, f0=0.015f0)source

notes

Option has been renamed JUDIOptions as of JUDI version 4.0 to avoid potential (and exisiting) conflicts wiht other packages exporting an Options structure.

diff --git a/dev/helper/index.html b/dev/helper/index.html index 22624ca8d..ee761a38a 100644 --- a/dev/helper/index.html +++ b/dev/helper/index.html @@ -1,9 +1,9 @@ -Helper Functions · JUDI documentation

Helper functions

JUDI provides numerous helper and utility functions need for seismic modeling and inversion.

Ricker wavelet

Create a 1D Ricker wavelet:

Missing docstring.

Missing docstring for ricker_wavelet(tmax, dt, f0; t0=nothing). Check Documenter's build log for details.

Compute CFL time stepping interval

Calculate the time stepping interval based on the CFL condition

calculate_dt

Compute number of computational time steps

Estimate the number of computational time steps. Required for calculating the dimensions of the matrix-free linear modeling operators:

JUDI.get_computational_ntFunction
get_computational_nt(srcGeometry, recGeoemtry, model; dt=nothing)

Estimate the number of computational time steps. Required for calculating the dimensions
of the matrix-free linear modeling operators. srcGeometry and recGeometry are source
and receiver geometries of type Geometry and model is the model structure of type
Model.

source
get_computational_nt(Geoemtry, model; dt=nothing)

Estimate the number of computational time steps. Required for calculating the dimensions
of the matrix-free linear modeling operators. srcGeometry and recGeometry are source
and receiver geometries of type Geometry and model is the model structure of type
Model.

source

Set up 3D acquisition grid

Helper function to create a regular acquisition grid for a 3D survey.

setup_3D_grid

Data interpolation

Time interpolation for source/receiver data using splines. For modeling, the data is interpolated automatically onto the computational time axis, so generally, these functions are not needed for users.

JUDI.time_resampleFunction
time_resample(data, geometry_in, dt_new)

Resample the input data with sinc interpolation from the current time sampling (geometrtyin) to the new time sampling `dtnew`.

Parameters

  • data: Data to be reampled. If data is a matrix, resamples each column.
  • geometry_in: Geometry on which data is defined.
  • dt_new: New time sampling rate to interpolate onto.
source
time_resample(data, dt_in, dt_new)

Resample the input data with sinc interpolation from the current time sampling dtin to the new time sampling `dtnew`.

Parameters

  • data: Data to be reampled. If data is a matrix, resamples each column.
  • dt_in: Time sampling of input
  • dt_new: New time sampling rate to interpolate onto.
source
time_resample(data, dt_in, geometry_in)

Resample the input data with sinc interpolation from the current time sampling (dtin) to the new time sampling `geometryout`.

Parameters

  • data: Data to be reampled. If data is a matrix, resamples each column.
  • geometry_out: Geometry on which data is to be interpolated.
  • dt_in: Time sampling rate of the data.
source

Generate and sample from frequency distribution

Create a probability distribution with the shape of the source spectrum from which we can draw random frequencies.

JUDI.generate_distributionFunction
generate_distribution(x; src_no=1)

Generates a probability distribution for the discrete input judiVector x.

Parameters

  • x: judiVector. Usualy a source with a single trace per source position.
  • src_no: Index of the source to select out of x
source
JUDI.select_frequenciesFunction
select_frequencies(q_dist; fmin=0f0, fmax=Inf, nf=1)

Selects nf frequencies based on the source distribution q_dist computed with generate_distribution.

Parameters

  • q_dist: Distribution to sample from.
  • f_min: Minimum acceptable frequency to sample (defaults to 0).
  • f_max: Maximum acceptable frequency to sample (defaults to Inf).
  • fd: Number of frequnecies to sample (defaults to 1).
source

We can draw random samples from dist by passing it values between 0 and 1:

# Draw a single random frequency
+Helper Functions · JUDI documentation

Helper functions

JUDI provides numerous helper and utility functions need for seismic modeling and inversion.

Ricker wavelet

Create a 1D Ricker wavelet:

Missing docstring.

Missing docstring for ricker_wavelet(tmax, dt, f0; t0=nothing). Check Documenter's build log for details.

Compute CFL time stepping interval

Calculate the time stepping interval based on the CFL condition

calculate_dt

Compute number of computational time steps

Estimate the number of computational time steps. Required for calculating the dimensions of the matrix-free linear modeling operators:

JUDI.get_computational_ntFunction
get_computational_nt(srcGeometry, recGeoemtry, model; dt=nothing)

Estimate the number of computational time steps. Required for calculating the dimensions
of the matrix-free linear modeling operators. srcGeometry and recGeometry are source
and receiver geometries of type Geometry and model is the model structure of type
Model.

source
get_computational_nt(Geoemtry, model; dt=nothing)

Estimate the number of computational time steps. Required for calculating the dimensions
of the matrix-free linear modeling operators. srcGeometry and recGeometry are source
and receiver geometries of type Geometry and model is the model structure of type
Model.

source

Set up 3D acquisition grid

Helper function to create a regular acquisition grid for a 3D survey.

setup_3D_grid

Data interpolation

Time interpolation for source/receiver data using splines. For modeling, the data is interpolated automatically onto the computational time axis, so generally, these functions are not needed for users.

JUDI.time_resampleFunction
time_resample(data, geometry_in, dt_new)

Resample the input data with sinc interpolation from the current time sampling (geometrtyin) to the new time sampling `dtnew`.

Parameters

  • data: Data to be reampled. If data is a matrix, resamples each column.
  • geometry_in: Geometry on which data is defined.
  • dt_new: New time sampling rate to interpolate onto.
source
time_resample(data, dt_in, dt_new)

Resample the input data with sinc interpolation from the current time sampling dtin to the new time sampling `dtnew`.

Parameters

  • data: Data to be reampled. If data is a matrix, resamples each column.
  • dt_in: Time sampling of input
  • dt_new: New time sampling rate to interpolate onto.
source
time_resample(data, dt_in, geometry_in)

Resample the input data with sinc interpolation from the current time sampling (dtin) to the new time sampling `geometryout`.

Parameters

  • data: Data to be reampled. If data is a matrix, resamples each column.
  • geometry_out: Geometry on which data is to be interpolated.
  • dt_in: Time sampling rate of the data.
source

Generate and sample from frequency distribution

Create a probability distribution with the shape of the source spectrum from which we can draw random frequencies.

JUDI.generate_distributionFunction
generate_distribution(x; src_no=1)

Generates a probability distribution for the discrete input judiVector x.

Parameters

  • x: judiVector. Usualy a source with a single trace per source position.
  • src_no: Index of the source to select out of x
source
JUDI.select_frequenciesFunction
select_frequencies(q_dist; fmin=0f0, fmax=Inf, nf=1)

Selects nf frequencies based on the source distribution q_dist computed with generate_distribution.

Parameters

  • q_dist: Distribution to sample from.
  • f_min: Minimum acceptable frequency to sample (defaults to 0).
  • f_max: Maximum acceptable frequency to sample (defaults to Inf).
  • fd: Number of frequnecies to sample (defaults to 1).
source

We can draw random samples from dist by passing it values between 0 and 1:

# Draw a single random frequency
 f = dist(rand(1))
 
 # Draw 10 random frequencies
-f = dist(rand(10))

Alternatively, we can use the function:

f = select_frequencies(dist; fmin=0f0, fmax=Inf, nf=1)

to draw nf number of frequencies for a given distribution dist in the frequency range of fmin to fmax (both in kHz).

Read data from out of core container

In the case where a judiVector is out of core (points to a segy file) it is possible to convert it or part of it into an in core judiVecor with the get_data function.

d_ic = get_data(d_ooc, inds)

where inds is either a single index, a list of index or a range of index.

Restrict model to acquisition

In practice, and in particular for marine data, the aperture of a single shot is much smaller than the full model size. We provide a function (automatically used when the option limit_m is set in Options) that limits the model to the acquisition area.

JUDI.limit_model_to_receiver_areaFunction
limit_model_to_receiver_area(srcGeometry, recGeometry, model, buffer; pert=nothing)

Crops the model to the area of the source an receiver with an extra buffer. This reduces the size of the problem when the model si large and the source and receiver located in a small part of the domain.

In the cartoon below, the full model will be cropped to the center area containg the source (o) receivers (x) and buffer area (*)

  • o Source position
  • x receiver positions
    • Extra buffer (grid spacing in that simple case)

| . . . . . . . . . . . . . . . . . . . . . |

| . . . . . . . . . . . . . . . . . . . . . |

| . . . . * * * * * * * * * * * * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x o x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * * * * * * * * * * * * . . . . . |

| . . . . . . . . . . . . . . . . . . . . . |

| . . . . . . . . . . . . . . . . . . . . . |


Parameters

  • srcGeometry: Geometry of the source.
  • recGeometry: Geometry of the receivers.
  • model: Model to be croped.
  • buffer: Size of the buffer on each side.
  • pert: Model perturbation (optional) to be cropped as well.
source

We also provide it's complement that removes receivers outside of the computational domain if the dataset contains locations that are not wanted

JUDI.remove_out_of_bounds_receiversFunction
remove_out_of_bounds_receivers(recGeometry, model)

Removes receivers that are positionned outside the computational domain defined by the model.

Parameters

  • recGeometry: Geometry of receivers in which out of bounds will be removed.
  • model: Model defining the computational domain.
source
remove_out_of_bounds_receivers(recGeometry, recData, model)

Removes receivers that are positionned outside the computational domain defined by the model.

Parameters

  • recGeometry: Geometry of receivers in which out of bounds will be removed.
  • recData: Shot record for that geometry in which traces will be removed.
  • model: Model defining the computational domain.
source

Additional miscellanous utilities

JUDI.devito_modelFunction
devito_model(model, options;dm=nothing)

Creates a python side model strucutre for devito.

Parameters

  • model: JUDI Model structure.
  • options: JUDI Options structure.
  • dm: Squared slowness perturbation (optional), Array or PhysicalParameter.
source
JUDI.setup_gridFunction
setup_grid(geometry, n)

Sets up the coordinate arrays for Devito.

Parameters:

  • geometry: Geometry containing the coordinates
  • n: Domain size
source
JUDI.pad_sizesFunction
pad_sizes(model, options; so=nothing)

Computes ABC padding sizes according to the model's numbr of abc points and spatial order

Parameters

  • model: JUDI or Python side Model.
  • options: JUDI Options structure.
  • so: Space order (optional) defaults to options.space_order.
source
JUDI.pad_arrayFunction
pad_array(m, nb; mode=:border)

Pads to the input array with either copying the edge value (:border) or zeros (:zeros)

Parameters

  • m: Array to be padded.
  • nb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.
  • mode: Padding mode (optional), defaults to :border.
source
JUDI.remove_paddingFunction
remove_padding(m, nb; true_adjoint=False)

Removes the padding from array m. This is the adjoint of pad_array.

Parameters

  • m: Array to remvove padding from.
  • nb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.
  • true_adjoint: Unpadding mode, defaults to False. Will sum the padding to the edge point with true_adjoint=true

and should only be used this way for adjoint testing purpose.

source
JUDI.convertToCellFunction
convertToCell(x)

Convert an array x to a cell array (Array{Any,1}) with length(x) entries,
where the i-th cell contains the i-th entry of x.

Parameters

  • x: Array to be converted into and array of array
source
JUDI.process_input_dataFunction
process_input_data(input, geometry, nsrc)

Preprocesses input Array into an Array of Array for modeling

Parameters:

  • input: Input to preprocess.
  • geometry: Geometry containing physical parameters.
  • nsrc: Number of sources
source
process_input_data(input, model, nsrc)

Preprocesses input Array into an Array of Array for modeling

Parameters:

  • input: Input to preprocess.
  • model: Model containing physical parameters.
  • nsrc: Number of sources
source
Base.reshapeFunction
reshape(A, dims...) -> AbstractArray
+f = dist(rand(10))

Alternatively, we can use the function:

f = select_frequencies(dist; fmin=0f0, fmax=Inf, nf=1)

to draw nf number of frequencies for a given distribution dist in the frequency range of fmin to fmax (both in kHz).

Read data from out of core container

In the case where a judiVector is out of core (points to a segy file) it is possible to convert it or part of it into an in core judiVecor with the get_data function.

d_ic = get_data(d_ooc, inds)

where inds is either a single index, a list of index or a range of index.

Restrict model to acquisition

In practice, and in particular for marine data, the aperture of a single shot is much smaller than the full model size. We provide a function (automatically used when the option limit_m is set in Options) that limits the model to the acquisition area.

JUDI.limit_model_to_receiver_areaFunction
limit_model_to_receiver_area(srcGeometry, recGeometry, model, buffer; pert=nothing)

Crops the model to the area of the source an receiver with an extra buffer. This reduces the size of the problem when the model si large and the source and receiver located in a small part of the domain.

In the cartoon below, the full model will be cropped to the center area containg the source (o) receivers (x) and buffer area (*)

  • o Source position
  • x receiver positions
    • Extra buffer (grid spacing in that simple case)

| . . . . . . . . . . . . . . . . . . . . . |

| . . . . . . . . . . . . . . . . . . . . . |

| . . . . * * * * * * * * * * * * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x o x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * x x x x x x x x x x * . . . . . |

| . . . . * * * * * * * * * * * * . . . . . |

| . . . . . . . . . . . . . . . . . . . . . |

| . . . . . . . . . . . . . . . . . . . . . |


Parameters

  • srcGeometry: Geometry of the source.
  • recGeometry: Geometry of the receivers.
  • model: Model to be croped.
  • buffer: Size of the buffer on each side.
  • pert: Model perturbation (optional) to be cropped as well.
source

We also provide it's complement that removes receivers outside of the computational domain if the dataset contains locations that are not wanted

JUDI.remove_out_of_bounds_receiversFunction
remove_out_of_bounds_receivers(recGeometry, model)

Removes receivers that are positionned outside the computational domain defined by the model.

Parameters

  • recGeometry: Geometry of receivers in which out of bounds will be removed.
  • model: Model defining the computational domain.
source
remove_out_of_bounds_receivers(recGeometry, recData, model)

Removes receivers that are positionned outside the computational domain defined by the model.

Parameters

  • recGeometry: Geometry of receivers in which out of bounds will be removed.
  • recData: Shot record for that geometry in which traces will be removed.
  • model: Model defining the computational domain.
source

Additional miscellanous utilities

JUDI.devito_modelFunction
devito_model(model, options;dm=nothing)

Creates a python side model strucutre for devito.

Parameters

  • model: JUDI Model structure.
  • options: JUDI Options structure.
  • dm: Squared slowness perturbation (optional), Array or PhysicalParameter.
source
JUDI.setup_gridFunction
setup_grid(geometry, n)

Sets up the coordinate arrays for Devito.

Parameters:

  • geometry: Geometry containing the coordinates
  • n: Domain size
source
JUDI.pad_sizesFunction
pad_sizes(model, options; so=nothing)

Computes ABC padding sizes according to the model's numbr of abc points and spatial order

Parameters

  • model: JUDI or Python side Model.
  • options: JUDI Options structure.
  • so: Space order (optional) defaults to options.space_order.
source
JUDI.pad_arrayFunction
pad_array(m, nb; mode=:border)

Pads to the input array with either copying the edge value (:border) or zeros (:zeros)

Parameters

  • m: Array to be padded.
  • nb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.
  • mode: Padding mode (optional), defaults to :border.
source
JUDI.remove_paddingFunction
remove_padding(m, nb; true_adjoint=False)

Removes the padding from array m. This is the adjoint of pad_array.

Parameters

  • m: Array to remvove padding from.
  • nb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.
  • true_adjoint: Unpadding mode, defaults to False. Will sum the padding to the edge point with true_adjoint=true

and should only be used this way for adjoint testing purpose.

source
JUDI.convertToCellFunction
convertToCell(x)

Convert an array x to a cell array (Array{Any,1}) with length(x) entries,
where the i-th cell contains the i-th entry of x.

Parameters

  • x: Array to be converted into and array of array
source
JUDI.process_input_dataFunction
process_input_data(input, geometry, nsrc)

Preprocesses input Array into an Array of Array for modeling

Parameters:

  • input: Input to preprocess.
  • geometry: Geometry containing physical parameters.
  • nsrc: Number of sources
source
process_input_data(input, model, nsrc)

Preprocesses input Array into an Array of Array for modeling

Parameters:

  • input: Input to preprocess.
  • model: Model containing physical parameters.
  • nsrc: Number of sources
source
Base.reshapeFunction
reshape(A, dims...) -> AbstractArray
 reshape(A, dims) -> AbstractArray

Return an array with the same data as A, but with different dimension sizes or number of dimensions. The two arrays share the same underlying data, so that the result is mutable if and only if A is mutable, and setting elements of one alters the values of the other.

The new dimensions may be specified either as a list of arguments or as a shape tuple. At most one dimension may be specified with a :, in which case its length is computed such that its product with all the specified dimensions is equal to the length of the original array A. The total number of elements must not change.

Examples

julia> A = Vector(1:16)
 16-element Vector{Int64}:
   1
@@ -38,4 +38,4 @@
 julia> reshape(1:6, 2, 3)
 2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:
  1  3  5
- 2  4  6
source
JUDI.transducerFunction
transducer(q, d, r, theta)

Create the JUDI soure for a circular transducer Theta=0 points downward:

. . . . - - - . . . . . .

. . . . + + + . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

Theta=pi/2 points right:

. . . . - + . . . . . . .

. . . . - + . . . . . . .

. . . . - + . . . . . . .

. . . . . . . . . . . . .

2D only, to extend to 3D

source
JUDI.as_vecFunction
as_vec(x, ::Val{Bool})

Vectorizes output when return_array is set to true.

source
+ 2 4 6
source
JUDI.transducerFunction
transducer(q, d, r, theta)

Create the JUDI soure for a circular transducer Theta=0 points downward:

. . . . - - - . . . . . .

. . . . + + + . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

Theta=pi/2 points right:

. . . . - + . . . . . . .

. . . . - + . . . . . . .

. . . . - + . . . . . . .

. . . . . . . . . . . . .

2D only, to extend to 3D

source
JUDI.as_vecFunction
as_vec(x, ::Val{Bool})

Vectorizes output when return_array is set to true.

source
diff --git a/dev/index.html b/dev/index.html index 72e56540f..ed4d3449e 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Home · JUDI documentation

The Julia Devito Inversion framework (JUDI.jl)

JUDI is a framework for large-scale seismic modeling and inversion and designed to enable rapid translations of algorithms to fast and efficient code that scales to industry-size 3D problems. Wave equations in JUDI are solved with Devito, a Python domain-specific language for automated finite-difference (FD) computations.

Docs overview

This documentation provides an overview over JUDI's basic data structures and abstract operators:

  • Installation: Install guidlines for JUDI and compilers.

  • Getting Started: A few simple guided examples to get familiar with JUDI.

  • Data structures: Explains the Model, Geometry and Options data structures and how to set up acquisition geometries.

  • Abstract Vectors: Documents JUDI's abstract vector classes judiVector, judiWavefield, judiRHS, judiWeights and judiExtendedSource.

  • Linear Operators: Lists and explains JUDI's abstract linear operators judiModeling, judiJacobian, judiProjection and judiLRWF.

  • Input/Output: Read SEG-Y data and set up judiVectors for shot records and sources. Read velocity models.

  • Helper functions: API of functions that make your life easier.

  • Seismic Inversion: Inversion utility functions to avoid recomputation and memry overhead.

  • Seismic Preconditioners: Basic preconditioners for seismic imaging.

  • pysource package: API reference for the propagators implementation with Devito in python. The API is the backend of JUDI handled with PyCall.

+Home · JUDI documentation

The Julia Devito Inversion framework (JUDI.jl)

JUDI is a framework for large-scale seismic modeling and inversion and designed to enable rapid translations of algorithms to fast and efficient code that scales to industry-size 3D problems. Wave equations in JUDI are solved with Devito, a Python domain-specific language for automated finite-difference (FD) computations.

Docs overview

This documentation provides an overview over JUDI's basic data structures and abstract operators:

  • Installation: Install guidlines for JUDI and compilers.

  • Getting Started: A few simple guided examples to get familiar with JUDI.

  • Data structures: Explains the Model, Geometry and Options data structures and how to set up acquisition geometries.

  • Abstract Vectors: Documents JUDI's abstract vector classes judiVector, judiWavefield, judiRHS, judiWeights and judiExtendedSource.

  • Linear Operators: Lists and explains JUDI's abstract linear operators judiModeling, judiJacobian, judiProjection and judiLRWF.

  • Input/Output: Read SEG-Y data and set up judiVectors for shot records and sources. Read velocity models.

  • Helper functions: API of functions that make your life easier.

  • Seismic Inversion: Inversion utility functions to avoid recomputation and memry overhead.

  • Seismic Preconditioners: Basic preconditioners for seismic imaging.

  • pysource package: API reference for the propagators implementation with Devito in python. The API is the backend of JUDI handled with PyCall.

diff --git a/dev/installation/index.html b/dev/installation/index.html index cbb393836..7e7faf7e1 100644 --- a/dev/installation/index.html +++ b/dev/installation/index.html @@ -2,4 +2,4 @@ Installation · JUDI documentation

Installation

JUDI is a linear algebra abstraction built on top of Devito. Because Devito is a just-in-time compiler, you will need to have a standard C compiler installed. by default most system come with a gcc compiler (except Windows where we recommend to use docker or WSL) which unfortunately isnt' very reliable. It is therefore recommended to install a proper compiler (gcc>=7, icc). For GPU offloading, you will then need to install a proper offloading compiler such as Nvidia's nvc or the latest version of clang (not Apple clang).

Standard installation

JUDI is registered and can be installed directly in julia REPL

] add JUDI

This will install JUDI, and the build will install the necessary dependencies including Devito.

Custom installation

In some case you may want to have your own installation of Devito you want JUDI to use in which case you should foloow these steps.

You can find installation instruction in our Wiki at Installation.

JUDI is a registered package and can therefore be easily installed from the General registry with ]add/dev JUDI

GPU

JUDI supports the computation of the wave equation on GPU via Devito's GPU offloading support.

NOTE: Only the wave equation part will be computed on GPU, the julia arrays will still be CPU arrays and CUDA.jl is not supported.

Compiler installation

To enable gpu support in JUDI, you will need to install one of Devito's supported offloading compilers. We strongly recommend checking the Wiki for installation steps and to reach out to the Devito community for GPU compiler related issues.

  • [x] nvc/pgcc. This is recommended and the simplest installation. You can install the compiler following Nvidia's installation instruction at HPC-sdk
  • [ ] aompcc. This is the AMD compiler that is necessary for running on AMD GPUs. This installation is not tested with JUDI and we recommend to reach out to Devito's team for installation guidelines.
  • [ ] openmp5/clang. This installation requires the compilation from source openmp, clang and llvm to install the latest version of openmp5 enabling gpu offloading. You can find instructions on this installation in Devito's Wiki

Setup

The only required setup for GPU support are the environment variables for Devito. For the currently supported nvc+openacc setup these are:

export DEVITO_LANGUAGE=openacc
 export DEVITO_ARCH=nvc
 export DEVITO_PLATFORM=nvidiaX

Running with Docker

If you do not want to install JUDI, you can run JUDI as a docker image. The first possibility is to run the docker container as a Jupyter notebook. JUDI provides two docker images for the latest JUDI release for Julia versions 1.6 (LTS) and 1.7 (latest stable version). The images names are mloubout/judi:JVER-latest where JVER is the Julia version. This docker images contain pre-installed compilers for CPUs (gcc-10) and Nvidia GPUs (nvc) via the nvidia HPC sdk. The environment is automatically set for [Devito] based on the hardware available.

Note: If you wish to use your gpu, you will need to install nvidia-docker and run docker run --gpus all in order to make the GPUs available at runtime from within the image.

To run JUDI via docker execute the following command in your terminal:

docker run -p 8888:8888 mloubout/judi:1.7-latest

This command downloads the image and launches a container. You will see a link that you can copy-paste to your browser to access the notebooks. Alternatively, you can run a bash session, in which you can start a regular interactive Julia session and run the example scripts. Download/start the container as a bash session with:

docker run -it mloubout/judi:1.7-latest /bin/bash

Inside the container, all examples are located in the directory /app/judi/examples/scripts.

Previous versions: As of version v2.6.7 of JUDI, we also ship version-tagged images as mloubout/judi:JVER-ver where ver is the version of JUDI wanted, for example the current JUDI version with Julia 1.7 is mloubout/judi:1.7-v2.6.7

Development version: Additionally, we provide two images corresponding to the latest development version of JUDI (latest state of the master branch). These images are called mloubout/judi:JVER-dev and can be used in a similar way.

Testing

A complete test suite is included with JUDI and is tested via GitHub Actions. You can also run the test locally via:

    julia --project -e 'using Pkg;Pkg.test(coverage=false)'

By default, only the JUDI base API will be tested, however the testing suite supports other modes controlled via the environemnt variable GROUP such as:

	GROUP=JUDI julia --project -e 'using Pkg;Pkg.test(coverage=false)'

The supported modes are:

  • JUDI : Only the base API (linear operators, vectors, ...)
  • ISO_OP : Isotropic acoustic operators
  • ISOOPFS : Isotropic acoustic operators with free surface
  • TTI_OP : Transverse tilted isotropic operators
  • TTIOPFS : Transverse tilted isotropic operators with free surface
  • filename : you can also provide just a filename (i.e GROUP=test_judiVector.jl) and only this one test file will be run. Single files with TTI or free surface are not currently supported as it relies on Base.ARGS for the setup.

Configure compiler and OpenMP

Devito uses just-in-time compilation for the underlying wave equation solves. The default compiler is intel, but can be changed to any other specified compiler such as gnu. Either run the following command from the command line or add it to your ~/.bashrc file:

export DEVITO_ARCH=gnu

Devito uses shared memory OpenMP parallelism for solving PDEs. OpenMP is disabled by default, but you can enable OpenMP and define the number of threads (per PDE solve) as follows:

export DEVITO_LANGUAGE=openmp  # Enable OpenMP. 
-export OMP_NUM_THREADS=4    # Number of OpenMP threads
+export OMP_NUM_THREADS=4 # Number of OpenMP threads diff --git a/dev/inversion/index.html b/dev/inversion/index.html index 7668d778f..905eb8c23 100644 --- a/dev/inversion/index.html +++ b/dev/inversion/index.html @@ -3,7 +3,7 @@ d_syn = F*q r = judiJacobian(F, q)' * (d_syn - d_obs)

In this two lines, the forward modeling is performed twice: once to compute d_syn then once again to compute the Jacobian adjoint. In order to avoid this overhead for practical inversion, we provide utility function that directly comput the gradient and objective function (L2- misfit) of FWI, LSRTM and TWRI with minimum overhead.

FWI

JUDI.fwi_objectiveFunction
fwi_objective(model, source, dobs; options=Options())
 
-Evaluate the full-waveform-inversion (reduced state) objective function. Returns a tuple with function value and vectorized \

gradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and
observed data of type judiVector.

Example

function_value, gradient = fwi_objective(model, source, dobs)
source

Example

JUDI is designed to let you set up objective functions that can be passed to standard packages for (gradient-based) optimization. The following example demonstrates how to perform FWI on the 2D Overthrust model using a spectral projected gradient algorithm from the minConf library, which is included in the software. A small test dataset (62 MB) and the model can be downloaded from this FTP server:

run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D.segy`)
+Evaluate the full-waveform-inversion (reduced state) objective function. Returns a tuple with function value and vectorized \

gradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and
observed data of type judiVector.

Example

function_value, gradient = fwi_objective(model, source, dobs)
source

Example

JUDI is designed to let you set up objective functions that can be passed to standard packages for (gradient-based) optimization. The following example demonstrates how to perform FWI on the 2D Overthrust model using a spectral projected gradient algorithm from the minConf library, which is included in the software. A small test dataset (62 MB) and the model can be downloaded from this FTP server:

run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D.segy`)
 run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D_initial_model.h5`)

The first step is to load the velocity model and the observed data into Julia, as well as setting up bound constraints for the inversion, which prevent too high or low velocities in the final result. Furthermore, we define an 8 Hertz Ricker wavelet as the source function:

using PyPlot, HDF5, SegyIO, JUDI, SlimOptim, Statistics, Random
 
 # Load starting model
@@ -48,9 +48,9 @@
 # FWI with SPG
 ProjBound(x) = median([mmin x mmax], dims=2)	# Bound projection
 options = spg_options(verbose=3, maxIter=fevals, memory=3)
-x, fsave, funEvals= minConf_SPG(objective_function, vec(m0), ProjBound, options)

This example script can be run in parallel and requires roughly 220 MB of memory per source location. Execute the following code to generate figures of the initial model and the result, as well as the function values:

figure(); imshow(sqrt.(1./adjoint(m0))); title("Initial model")
+res = spg(objective_function, vec(m0), ProjBound, options)

This example script can be run in parallel and requires roughly 220 MB of memory per source location. Execute the following code to generate figures of the initial model and the result, as well as the function values:

figure(); imshow(sqrt.(1./adjoint(m0))); title("Initial model")
 figure(); imshow(sqrt.(1./adjoint(reshape(x, model0.n)))); title("FWI")
-figure(); plot(fvals); title("Function value")

fwi

LSRTM

JUDI.lsrtm_objectiveFunction
lsrtm_objective(model, source, dobs, dm; options=Options(), nlind=false)

Evaluate the least-square migration objective function. Returns a tuple with function value and
gradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and
observed data of type judiVector.

Example

function_value, gradient = lsrtm_objective(model, source, dobs, dm)
source

Example

JUDI includes matrix-free linear operators for modeling and linearized (Born) modeling, that let you write algorithms for migration that follow the mathematical notation of standard least squares problems. This example demonstrates how to use Julia Devito to perform least-squares reverse-time migration on the 2D Marmousi model. Start by downloading the test data set (1.1 GB) and the model:

run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_2D.segy`)
+figure(); plot(fvals); title("Function value")

fwi

LSRTM

JUDI.lsrtm_objectiveFunction
lsrtm_objective(model, source, dobs, dm; options=Options(), nlind=false)

Evaluate the least-square migration objective function. Returns a tuple with function value and
gradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and
observed data of type judiVector.

Example

function_value, gradient = lsrtm_objective(model, source, dobs, dm)
source

Example

JUDI includes matrix-free linear operators for modeling and linearized (Born) modeling, that let you write algorithms for migration that follow the mathematical notation of standard least squares problems. This example demonstrates how to use Julia Devito to perform least-squares reverse-time migration on the 2D Marmousi model. Start by downloading the test data set (1.1 GB) and the model:

run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_2D.segy`)
 run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_migration_velocity.h5`)

Once again, load the starting model and the data and set up the source wavelet. For this example, we use a Ricker wavelet with 30 Hertz peak frequency.

using PyPlot, HDF5, JUDI, SegyIO, Random
 
 # Load smooth migration velocity model
@@ -98,13 +98,13 @@
 	fval[j] = .5f0*norm(r)^2
 	t = norm(r)^2/norm(g)^2
 	global x -= t*g
-end

lsrtm

TWRI

JUDI.twri_objectiveFunction
twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())

Evaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and gradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and observed data of type judiVector.

Example

function_value, gradient_m, gradient_y = twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())
source
twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())

Evaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and
gradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and
observed data of type judiVector. Example ======= functionvalue, gradient = fwiobjective(model, source, dobs)

source

and related TWRI options

JUDI.TWRIOptionsType
TWRIOptions
+end

lsrtm

TWRI

JUDI.twri_objectiveFunction
twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())

Evaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and gradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and observed data of type judiVector.

Example

function_value, gradient_m, gradient_y = twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())
source
twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())

Evaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and
gradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and
observed data of type judiVector. Example ======= functionvalue, gradient = fwiobjective(model, source, dobs)

source

and related TWRI options

JUDI.TWRIOptionsType
TWRIOptions
     grad_corr::Bool
     comp_alpha::Bool
     weight_fun
     eps
     params::Symbol
-    Invq::String

Options structure for TWRI.

grad_corr: Whether to add the gradient correction J'(m0, q)*∇_y

comp_alpha: Whether to compute optimal alpha (alpha=1 if not)

weight_fun: Whether to apply focusing/weighting function to F(m0)'*y and its norm

eps: Epsilon (noise level) value (default=0)

Invq: How to compute F'Y, either as full field or as a rank 1 approximation w(t)*q(x) using the source wavelet for w

param: Which gradient to compute. Choices are nothing (objective only), :m, :y or :all

Constructor

All arguments are optional keyword arguments with the following default values:

TWRIOptions(;gradcorr=false, compalpha=true, weight_fun=nothing, eps=0, params=:m)

source

Machine Learning

ChainRules.jl allows integrating JUDI modeling operators into convolutional neural networks for deep learning. For example, the following code snippet shows how to create a shallow CNN consisting of two convolutional layers with a nonlinear forward modeling layer in-between them. The integration of ChainRules and JUDI enables backpropagation through Flux' automatic differentiation tool, but calls the corresponding adjoint JUDI operators under the hood. For more details, please check out this tutorial.

# Jacobian
+    Invq::String

Options structure for TWRI.

grad_corr: Whether to add the gradient correction J'(m0, q)*∇_y

comp_alpha: Whether to compute optimal alpha (alpha=1 if not)

weight_fun: Whether to apply focusing/weighting function to F(m0)'*y and its norm

eps: Epsilon (noise level) value (default=0)

Invq: How to compute F'Y, either as full field or as a rank 1 approximation w(t)*q(x) using the source wavelet for w

param: Which gradient to compute. Choices are nothing (objective only), :m, :y or :all

Constructor

All arguments are optional keyword arguments with the following default values:

TWRIOptions(;gradcorr=false, compalpha=true, weight_fun=nothing, eps=0, params=:m)

source

Machine Learning

ChainRules.jl allows integrating JUDI modeling operators into convolutional neural networks for deep learning. For example, the following code snippet shows how to create a shallow CNN consisting of two convolutional layers with a nonlinear forward modeling layer in-between them. The integration of ChainRules and JUDI enables backpropagation through Flux' automatic differentiation tool, but calls the corresponding adjoint JUDI operators under the hood. For more details, please check out this tutorial.

# Jacobian
 W1 = judiJacobian(F0, q)
 b1 = randn(Float32, num_samples)
 
@@ -119,4 +119,4 @@
 # Compute gradient w/ Flux
 p = params(x, y, W1, b1, b2)
 gs = Tracker.gradient(() -> loss(x, y), p)
-gs[x]	# gradient w.r.t. to x

Integration with ChainRules allows implementing physics-augmented neural networks for seismic inversion, such as loop-unrolled seismic imaging algorithms. For example, the following results are a conventional RTM image, an LS-RTM image and a loop-unrolled LS-RTM image for a single simultaneous shot record.

flux

+gs[x] # gradient w.r.t. to x

Integration with ChainRules allows implementing physics-augmented neural networks for seismic inversion, such as loop-unrolled seismic imaging algorithms. For example, the following results are a conventional RTM image, an LS-RTM image and a loop-unrolled LS-RTM image for a single simultaneous shot record.

flux

diff --git a/dev/io/index.html b/dev/io/index.html index 8fc2bbe87..ff9e0a3a6 100644 --- a/dev/io/index.html +++ b/dev/io/index.html @@ -29,4 +29,4 @@ m = M["m"] # Set up a Model object -model = Model(n, d, o, m) +model = Model(n, d, o, m) diff --git a/dev/linear_operators/index.html b/dev/linear_operators/index.html index bdd8e8d57..da888f04f 100644 --- a/dev/linear_operators/index.html +++ b/dev/linear_operators/index.html @@ -1,6 +1,6 @@ Linear Operators · JUDI documentation

Linear Operators

JUDI is building on JOLI.jl to implement matrix-free linear operators. These operators represent the discretized wave-equations and sensitivit (Jacobian) for different acquisition schemes.

judiModeling

Seismic modeling operator for solving a wave equation for a given right-hand-side.

JUDI.judiModelingType
judiModeling(model; options=Options())
-judiModeling(model, src_geometry, rec_geometry; options=Options())

Create seismic modeling operator for a velocity model given as a Model structure. The function also takes the source and receiver geometries as additional input arguments, which creates a combined operator judiProjection*judiModeling*judiProjection'.

Example

Pr and Ps are projection operatos of type judiProjection and q is a data vector of type judiVector: F = judiModeling(model) dobs = PrFPs'q F = judiModeling(model, q.geometry, rec_geometry) dobs = Fq

source

Construction:

  • Construct a modeling operator without source/receiver projections:
F = judiModeling(model; options=opt)
  • Construct a modeling operator with source/receiver projections:
F = judiModeling(model, src_geometry, rec_geometry)
  • Construct a modeling operator from an existing operator without geometries and projection operators:
F = Pr*F*Ps'

where Ps and Pr are source/receiver projection operators of type judiProjection.

  • Construct a modeling operator for extended source modeling:
F = Pr*F*Pw'

where Pw is a judiLRWF (low-rank-wavefield) projection operator.

Accessible fields:

# Model structure
+judiModeling(model, src_geometry, rec_geometry; options=Options())

Create seismic modeling operator for a velocity model given as a Model structure. The function also takes the source and receiver geometries as additional input arguments, which creates a combined operator judiProjection*judiModeling*judiProjection'.

Example

Pr and Ps are projection operatos of type judiProjection and q is a data vector of type judiVector: F = judiModeling(model) dobs = PrFPs'q F = judiModeling(model, q.geometry, rec_geometry) dobs = Fq

source

Construction:

F = judiModeling(model; options=opt)
F = judiModeling(model, src_geometry, rec_geometry)
F = Pr*F*Ps'

where Ps and Pr are source/receiver projection operators of type judiProjection.

F = Pr*F*Pw'

where Pw is a judiLRWF (low-rank-wavefield) projection operator.

Accessible fields:

# Model structure
 F.model
 
 # Source injection (if available) and geometry
@@ -40,7 +40,7 @@
 d_obs = Pr*F*u
 
 # Adjoint modeling with full wavefield as source (F w/o geometries)
-q_ad = Ps*F*v

judiJacobian

Jacobian of a non-linear forward modeling operator. Corresponds to linearized Born modeling (forward mode) and reverse-time migration (adjoint mode).

JUDI.judiJacobianType
judiJacobian(F,q)

Create a linearized modeling operator from the non-linear modeling operator F and the source q. q can be either a judiVector (point source Jacobian) or a judiWeight for extended source modeling. F is a full modeling operator including source/receiver projections.

Examples

  1. F is a modeling operator without source/receiver projections: J = judiJacobian(PrFPs',q)
  2. F is the combined operator Pr*F*Ps': J = judiJacobian(F,q)
source

Construction:

J = judiJacobian(F, q)  # F w/ geometries
J = judiJacobian(Pr*F*Ps', q)   # F w/o geometries

where Ps and Pr are source/receiver projection operators of type judiProjection.

J = judiJacobian(Pr*F*Pw', w)

where Pw is a judiLRWF operator and w is a judiWeights vector (or 2D/3D Julia array).

Accessible fields::

# Model structure
+q_ad = Ps*F*v

judiJacobian

Jacobian of a non-linear forward modeling operator. Corresponds to linearized Born modeling (forward mode) and reverse-time migration (adjoint mode).

JUDI.judiJacobianType
judiJacobian(F,q)

Create a linearized modeling operator from the non-linear modeling operator F and the source q. q can be either a judiVector (point source Jacobian) or a judiWeight for extended source modeling. F is a full modeling operator including source/receiver projections.

Examples

  1. F is a modeling operator without source/receiver projections: J = judiJacobian(PrFPs',q)
  2. F is the combined operator Pr*F*Ps': J = judiJacobian(F,q)
source

Construction:

J = judiJacobian(F, q)  # F w/ geometries
J = judiJacobian(Pr*F*Ps', q)   # F w/o geometries

where Ps and Pr are source/receiver projection operators of type judiProjection.

J = judiJacobian(Pr*F*Pw', w)

where Pw is a judiLRWF operator and w is a judiWeights vector (or 2D/3D Julia array).

Accessible fields::

# Model structure
 J.model
 
 # Underlying propagator
@@ -65,16 +65,16 @@
 rtm = J'*d_lin
 
 # Matrix-free normal operator
-H = J'*J

judiProjection

Abstract linear operator for source/receiver projections. A (transposed) judiProjection operator symbolically injects the data with which it is multiplied during modeling. If multiplied with a forward modeling operator, it samples the wavefield at the specified source/receiver locations.

JUDI.judiProjectionType
judiProjection(geometry)

Projection operator for sources/receivers to restrict or inject data at specified locations.

Examples

F is a modeling operator of type judiModeling and q is a seismic source of type judiVector: Pr = judiProjection(rec_geometry) Ps = judiProjection(q.geometry) dobs = PrFPs'q qad = PsF'Pr'dobs

source

Accessible fields:

# Source/receiver geometry
+H = J'*J

judiProjection

Abstract linear operator for source/receiver projections. A (transposed) judiProjection operator symbolically injects the data with which it is multiplied during modeling. If multiplied with a forward modeling operator, it samples the wavefield at the specified source/receiver locations.

JUDI.judiProjectionType
judiProjection(geometry)

Projection operator for sources/receivers to restrict or inject data at specified locations.

Examples

F is a modeling operator of type judiModeling and q is a seismic source of type judiVector: Pr = judiProjection(rec_geometry) Ps = judiProjection(q.geometry) dobs = PrFPs'q qad = PsF'Pr'dobs

source

Accessible fields:

# Source/receiver geometry
 P.geometry

Usage:

# Multiply with judiVector to create a judiRHS
 rhs1 = Pr'*d_obs
 rhs2 = Ps'*q
 
 # Sample wavefield at source/receiver location during modeling
 d_obs = Pr*F*Ps'*q
-q_ad = Ps*F*Pr'*d_obs

judiLRWF

Abstract linear operator for sampling a seismic wavefield as a sum over all time steps, weighted by a time-varying wavelet. Its transpose injects a time-varying wavelet at every grid point in the model.

JUDI.judiWaveletType
judiWavelet(dt, wavelet)

Low-rank wavefield operator which injects a wavelet q at every point of the subsurface.

Examples

F is a modeling operator of type judiModeling and w is a weighting matrix of type judiWeights: Pr = judiProjection(recgeometry) Pw = judiWavelet(recgeometry.dt, q.data) # or judiLRWF(rec_geometry.dt, q.data) dobs = PrFPw'w dw = PwF'Pr'dobs

source

Accessible fields:

# Wavelet of i-th source location
+q_ad = Ps*F*Pr'*d_obs

judiLRWF

Abstract linear operator for sampling a seismic wavefield as a sum over all time steps, weighted by a time-varying wavelet. Its transpose injects a time-varying wavelet at every grid point in the model.

JUDI.judiWaveletType
judiWavelet(dt, wavelet)

Low-rank wavefield operator which injects a wavelet q at every point of the subsurface.

Examples

F is a modeling operator of type judiModeling and w is a weighting matrix of type judiWeights: Pr = judiProjection(recgeometry) Pw = judiWavelet(recgeometry.dt, q.data) # or judiLRWF(rec_geometry.dt, q.data) dobs = PrFPw'w dw = PwF'Pr'dobs

source

Accessible fields:

# Wavelet of i-th source location
 P.wavelet[i]

Usage:

# Multiply with a judiWeight vector to create a judiExtendedSource
 ex_src = Pw'*w
 
 # Sample wavefield as a sum over time, weighted by the source
-u_ex = Pw*F'*Pr'*d_obs
+u_ex = Pw*F'*Pr'*d_obs diff --git a/dev/preconditioners/index.html b/dev/preconditioners/index.html index cc893fd1f..e700cf3a7 100644 --- a/dev/preconditioners/index.html +++ b/dev/preconditioners/index.html @@ -1,14 +1,14 @@ Preconditioners · JUDI documentation

Seismic Preconditioners

JUDI provides a selected number of preconditioners known to be beneficial to FWI and RTM. We welcome additional preconditionners from the community. Additionnaly, any JOLI operator can be used as a preconditiner in conbination with JUDI operator thanks to the fundamental interface between JUDI and JOLI.

Model domain preconditioners

Model space preconditioners acts on model size arrays such as the velocity model or the FWI/RTM gradient. These preconditioners are indepenedent of the number of sources and therefore should not be indexed.

Water column muting (top mute)

Create a linear operator for a 2D model topmute, i.e. for muting the water column:

JUDI.TopMuteType
TopMute{T, N, Nw}

Mute top of the model in N dimensions

Constructor

judiTopmute(model; taperwidht=10)
-judiTopmute(n, wb, taperwidth)   # Legacy
source

Usage:

# Forward
+judiTopmute(n, wb, taperwidth)   # Legacy
source

Usage:

# Forward
 m_mute = Mr*vec(m)
 
 # Adjoint
-m_mute = Mr'*vec(m)

As Mr is self adjoint, Mr is equal to Mr'.

legacy:

The legacy constructor judiTopmute(n, wb, taperwidth) is still available to construct a muting operator with user specified muting depth.

Model depth scaling

Create a 2D model depth scaling. This preconditionenr is the most simple form of inverse Hessain approximation compensating for illumination in the subsurface. We also describe below a more accurate diagonal approximation of the Hessian with the illlumination operator. Additionnaly, as a simple diagonal approximation, this operator is invertible and can be inverted with the standard julia inv function.

JUDI.DepthScalingType
DepthScaling{T, N, K}

Depth scaling operator in N dimensions scaling by depth^K.

Constructor

judiDepthScaling(model::AbstractModel; K=.5)
source

Usage:

# Forward
+m_mute = Mr'*vec(m)

As Mr is self adjoint, Mr is equal to Mr'.

legacy:

The legacy constructor judiTopmute(n, wb, taperwidth) is still available to construct a muting operator with user specified muting depth.

Model depth scaling

Create a 2D model depth scaling. This preconditionenr is the most simple form of inverse Hessain approximation compensating for illumination in the subsurface. We also describe below a more accurate diagonal approximation of the Hessian with the illlumination operator. Additionnaly, as a simple diagonal approximation, this operator is invertible and can be inverted with the standard julia inv function.

JUDI.DepthScalingType
DepthScaling{T, N, K}

Depth scaling operator in N dimensions scaling by depth^K.

Constructor

judiDepthScaling(model::AbstractModel; K=.5)
source

Usage:

# Forward
 m_mute = Mr*vec(m)
 
 # Adjoint
-m_mute = Mr'*vec(m)

Illumination

The illumination computed the energy of the wavefield along time for each grid point. This provides a first order diagonal approximation of the Hessian of FWI/LSRTM helping the ocnvergence and quality of an update.

JUDI.judiIlluminationType
judiIllumination(model; mode="u", k=1, recompute=true)

Arguments

  • model: JUDI Model structure

  • mode: Type of ilumination, choicees of ("u", "v", "uv")

  • k: Power of the illumination, real number

  • recompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)

    judiIllumination(F; mode="u", k=1, recompute=true)

Arguments

  • F: JUDI propagator
  • mode: Type of ilumination, choicees of ("u", "v", "uv")
  • k: Power of the illumination, real positive number
  • recompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)

Diagonal approximation of the FWI Hessian as the energy of the wavefield. The diagonal contains the sum over time of the wavefield chosen as mode.

Options for the mode are "u" for the forward wavefield illumination, "v" for the adjoint wavefield illumination, and "uv" for the pointwise product of the forward and adjoint wavefields illuminations. Additionally, the parameter "k" provides control on the scaling of the daiagonal raising it to the power k.

Example

I = judiIllumination(model)

Construct the diagonal operator such that I*x = x ./ |||u||_2^2

source

Usage:

# Forward
+m_mute = Mr'*vec(m)

Illumination

The illumination computed the energy of the wavefield along time for each grid point. This provides a first order diagonal approximation of the Hessian of FWI/LSRTM helping the ocnvergence and quality of an update.

JUDI.judiIlluminationType
judiIllumination(model; mode="u", k=1, recompute=true)

Arguments

  • model: JUDI Model structure

  • mode: Type of ilumination, choicees of ("u", "v", "uv")

  • k: Power of the illumination, real number

  • recompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)

    judiIllumination(F; mode="u", k=1, recompute=true)

Arguments

  • F: JUDI propagator
  • mode: Type of ilumination, choicees of ("u", "v", "uv")
  • k: Power of the illumination, real positive number
  • recompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)

Diagonal approximation of the FWI Hessian as the energy of the wavefield. The diagonal contains the sum over time of the wavefield chosen as mode.

Options for the mode are "u" for the forward wavefield illumination, "v" for the adjoint wavefield illumination, and "uv" for the pointwise product of the forward and adjoint wavefields illuminations. Additionally, the parameter "k" provides control on the scaling of the daiagonal raising it to the power k.

Example

I = judiIllumination(model)

Construct the diagonal operator such that I*x = x ./ |||u||_2^2

source

Usage:

# Forward
 m_mute = I*vec(m)
 
 # Adjoint
@@ -18,11 +18,11 @@
     vp::Vector{T}
     t0::Vector{T}
     taperwidth::Vector{Int64}
-end

Data mute linear operator a where {T, N}sociated with source srcGeom and receiver recGeom geometries used to compute the distance to the source for each trace in the data. Data mute preconditionner. Supports two modes (:reflection, :turning) that mute either the turning waves (standard direct wave mute) or mutes the reflections. A cosine tapr is applied with width taperwidth to avoid abrupt change and infinite frequency jumps in the data.

Constructors

judiDataMute(srcGeom, recGeom; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))

Construct the data mute operator from the source srcGeom and receiver recGeom geometries.

judiDataMute(q, d; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))

Construct the data mute operator from the judivector source q and judivector data d.

Parameters

The following optional paramet where {T, N}rs control the muting operator

source

Band pass filter

While not purely a preconditioner, because this operator acts on the data and is traditionally used fro frequency continuation in FWI, we implemented this operator as a source indexable linear operator as well. Additionally, the filtering function is available as a standalone julia function for general usage

JUDI.FrequencyFilterType
struct FrequencyFilter
+end

Data mute linear operator a where {T, N}sociated with source srcGeom and receiver recGeom geometries used to compute the distance to the source for each trace in the data. Data mute preconditionner. Supports two modes (:reflection, :turning) that mute either the turning waves (standard direct wave mute) or mutes the reflections. A cosine tapr is applied with width taperwidth to avoid abrupt change and infinite frequency jumps in the data.

Constructors

judiDataMute(srcGeom, recGeom; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))

Construct the data mute operator from the source srcGeom and receiver recGeom geometries.

judiDataMute(q, d; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))

Construct the data mute operator from the judivector source q and judivector data d.

Parameters

The following optional paramet where {T, N}rs control the muting operator

  • vp: P wave velocity of the direct wave (usually water velocity). Can be a constant or a Vector with one value per source position. Devfaults to 1500m/s
  • t0: Time shift in seconds (usually width of the wavelet). Defaults to $.1 sec$
  • mode: :reflection to keep the reflections and mute above the direct wave (i.e for RTM) :turning to keep the turning waves and mute below the direct wave (i.e for FWI)
  • taperwidth: Width of the cosine taper in number of samples. Defaults to 2 / t0
source

Band pass filter

While not purely a preconditioner, because this operator acts on the data and is traditionally used fro frequency continuation in FWI, we implemented this operator as a source indexable linear operator as well. Additionally, the filtering function is available as a standalone julia function for general usage

JUDI.FrequencyFilterType
struct FrequencyFilter
     recGeom

Bandpass filter linear operator. Filters the input judiVector or Vector

Constructor

judiFilter(geometry, fmin, fmax) 
-judiFilter(judiVector, fmin, fmax)
source
JUDI.filter_dataFunction
filter(Din, dt_in; fmin=0, fmax=25)

Performs a causal filtering [fmin, fmax] on the input data bases on its sampling rate dt. Automatically perfroms a lowpass if fmin=0 (default)

source

Data time derivative/intergraton

A TimeDifferential{K} is a linear operator that implements a time derivative (K>0) or time integration (K<0) of order K for any real K including fractional values.

JUDI.TimeDifferentialType
struct TimeDifferential
+judiFilter(judiVector, fmin, fmax)
source
JUDI.filter_dataFunction
filter(Din, dt_in; fmin=0, fmax=25)

Performs a causal filtering [fmin, fmax] on the input data bases on its sampling rate dt. Automatically perfroms a lowpass if fmin=0 (default)

source

Data time derivative/intergraton

A TimeDifferential{K} is a linear operator that implements a time derivative (K>0) or time integration (K<0) of order K for any real K including fractional values.

JUDI.TimeDifferentialType
struct TimeDifferential
     recGeom

Differential operator of order K to be applied along the time dimension. Applies the ilter w^k where k is the order. For example, the tinme derivative is TimeDifferential{1} and the time integration is TimeDifferential{-1}

Constructor

judiTimeIntegration(recGeom, order)
 judiTimeIntegration(judiVector, order)
 
 judiTimeDerivative(recGeom, order)
-judiTimeDerivative(judiVector, order)
source
+judiTimeDerivative(judiVector, order)source diff --git a/dev/pysource/index.html b/dev/pysource/index.html index f17cde4e2..2810b8538 100644 --- a/dev/pysource/index.html +++ b/dev/pysource/index.html @@ -1,2 +1,2 @@ -Devito backend reference · JUDI documentation

pysource package

Submodules

FD_utils module

FDutils.Rmat(model)

Rotation matrix according to tilt and asymut.

  • Parameters: model (Model) – Model structure

FDutils.divs(func, sofact=1, side=-1)

GrDivergenceadient shifted by half a grid point, only to be used in combination with grads.

FDutils.grads(func, sofact=1, side=1)

Gradient shifted by half a grid point, only to be used in combination with divs.

FD_utils.laplacian(v, irho)

Laplacian with density div( 1/rho grad) (u)

FDutils.satti(u, v, model)

Tensor factorized SSA TTI wave equation spatial derivatives.

  • Parameters:
    • u (TimeFunction) – first TTI field
    • v (TimeFunction) – second TTI field
    • model (Model) – Model structure

FDutils.thomsenmat(model)

Diagonal Matrices with Thomsen parameters for vectorial temporaries computation.

  • Parameters: model (Model) – Model structure

checkpoint module

class checkpoint.CheckpointOperator(op, **kwargs)

Devito’s concrete implementation of the ABC pyrevolve.Operator. This class wraps devito.Operator so it conforms to the pyRevolve API. pyRevolve will call apply with arguments tstart and tend. Devito calls these arguments ts and te so the following dict is used to perform the translations between different names.

  • Parameters:
    • op (Operator) – devito.Operator object that this object will wrap.
    • args (dict) – If devito.Operator.apply() expects any arguments, they can be provided here to be cached. Any calls to CheckpointOperator.apply() will automatically include these cached arguments in the call to the underlying devito.Operator.apply().

apply(tstart, tend)

If the devito operator requires some extra arguments in the call to apply they can be stored in the args property of this object so pyRevolve calls pyRevolve.Operator.apply() without caring about these extra arguments while this method passes them on correctly to devito.Operator

targnames = {'tend': 'timeM', 'tstart': 'timem'}

class checkpoint.DevitoCheckpoint(objects)

Devito’s concrete implementation of the Checkpoint abstract base class provided by pyRevolve. Holds a list of symbol objects that hold data.

property dtype

data type

get_data(timestep)

returns the data (wavefield) for the time-step timestep

getdatalocation(timestep)

returns the data (wavefield) for the time-step timestep

load()

NotImplementedError

save()

NotImplementedError

property size

The memory consumption of the data contained in a checkpoint.

checkpoint.getsymboldata(symbol, timestep)

Return the symbol corresponding to the data at time-step timestep

geom_utils module

geomutils.geomexpr(model, u, srccoords=None, reccoords=None, wavelet=None, fw=True, nt=None)

Generates the source injection and receiver interpolation. This function is fully abstracted and does not care whether this is a forward or adjoint wave-equation. The source is the source term of the equation The receiver is the measurment term

Therefore, for the adjoint, this function has to be called as: srcrec(model, v, srccoords=rec_coords, …) because the data is the sources

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or tuple) – Wavefield to inject into and read from
    • src_coords (Array) – Physical coordinates of the sources
    • rec_coords (Array) – Physical coordinates of the receivers
    • wavelet (Array) – Data for the source
    • fw=True – Whether the direction is forward or backward in time
    • nt (int) – Number of time steps

geomutils.srcrec(model, u, srccoords=None, reccoords=None, wavelet=None, nt=None)

interface module

interface.Jadjoint(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, checkpointing=False, ncheckpoints=None, tsub=1, returnobj=False, freqlist=[], dftsub=None, ic='as', illum=False, ws=None, f0=0.015, born_fwd=False, nlind=False, misfit=None, fw=True)

Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Supports three modes: * Checkpinting * Frequency compression (on-the-fly DFT) * Standard zero lag cross correlation over time

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • checkpointing (Bool) – Whether or not to use checkpointing
    • n_checkpoints (Int) – Number of checkpoints for checkpointing
    • maxmem (Float) – Maximum memory to use for checkpointing
    • freq_list (List) – List of frequencies for on-the-fly DFT
    • dft_sub (Int) – Subsampling factor for on-the-fly DFT
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.Jadjointcheckpointing(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, ncheckpoints=None, bornfwd=False, return_obj=False, ic='as', ws=None, nlind=False, f0=0.015, misfit=None, illum=False, fw=True)

Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Checkpointing.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • checkpointing (Bool) – Whether or not to use checkpointing
    • n_checkpoints (Int) – Number of checkpoints for checkpointing
    • maxmem (Float) – Maximum memory to use for checkpointing
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • is_residual (Bool) – Whether to treat the input as the residual or as the observed data
    • born_fwd (Bool) – Whether to use the forward or linearized forward modeling operator
    • nlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.Jadjointfreq(model, srccoords, wavelet, reccoords, recin, spaceorder=8, freqlist=[], isresidual=False, returnobj=False, nlind=False, dftsub=None, ic='as', ws=None, bornfwd=False, f0=0.015, misfit=None, illum=False, fw=True)

Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Frequency compression (on-the-fly DFT).

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • freq_list (List) – List of frequencies for on-the-fly DFT
    • dft_sub (Int) – Subsampling factor for on-the-fly DFT
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • is_residual (Bool) – Whether to treat the input as the residual or as the observed data
    • born_fwd (Bool) – Whether to use the forward or linearized forward modeling operator
    • nlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.Jadjointstandard(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, returnobj=False, bornfwd=False, illum=False, ic='as', ws=None, t_sub=1, nlind=False, f0=0.015, misfit=None, fw=True)

Adjoint Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with standard zero lag cross correlation over time.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • is_residual (Bool) – Whether to treat the input as the residual or as the observed data
    • born_fwd (Bool) – Whether to use the forward or linearized forward modeling operator
    • nlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.adjointw(model, reccoords, data, wavelet, space_order=8, f0=0.015, illum=False, fw=True)

Adjoint/backward modeling of a shot record (receivers as source) for an extended source setup Pw*F^T*Pr^T*d_obs.

  • Parameters:
    • model (Model) – Physical model
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • data (Array) – Shot gather
    • wavelet (Array) – Time signature of the forward source for stacking along time
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: spatial distribution
  • Return type: Array

interface.bornrec(model, srccoords, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)

Linearized (Born) modeling of a point source for a model perturbation (square slowness) dm.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.bornrecw(model, weight, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)

Linearized (Born) modeling of an extended source for a model perturbation (square slowness) dm with an extended source

  • Parameters:
    • model (Model) – Physical model
    • weight (Array) – Spatial distriubtion of the extended source
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardnorec(model, srccoords, wavelet, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of a point source without receiver.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Wavefield
  • Return type: Array

interface.forwardrec(model, srccoords, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)

Modeling of a point source with receivers Pr*F*Ps^T*q.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardrecw(model, weight, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of an extended source with receivers Pr*F*Pw^T*w

  • Parameters:
    • model (Model) – Physical model
    • weights (Array) – Spatial distribution of the extended source.
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardwfsrc(model, u, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of a full wavefield source Pr*F*u.

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or Array) – Time-space dependent wavefield
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardwfsrcnorec(model, u, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of a full wavefield source without receiver F*u.

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or Array) – Time-space dependent wavefield
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Wavefield
  • Return type: Array

interface.wrifunc(model, srccoords, wavelet, reccoords, recin, yin, spaceorder=8, ic='as', ws=None, tsub=1, grad='m', gradcorr=False, alphaop=False, wfun=None, eps=0, freq_list=[], wfilt=None, f0=0.015)

Time domain wavefield reconstruction inversion wrapper

kernels module

kernels.SLS2ndorder(model, p, fw=True, q=None, f0=0.015)

Viscoacoustic 2nd SLS wave equation. https://library.seg.org/doi/10.1190/geo2013-0030.1

Bulk modulus moved to rhs. The adjoint equation is directly derived as the discrete adjoint of the forward PDE which leads to a slightly different formulation than in the paper.

  • Parameters:
    • model (Model) – Physical model
    • u1 (TimeFunction) – Pressure field
    • u2 (TimeFunction) – Attenuation Memory variable
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)
    • f0 (Peak frequency) –

kernels.acoustic_kernel(model, u, fw=True, q=None)

Acoustic wave equation time stepper

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or tuple) – wavefield (tuple if TTI)
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source

kernels.elastic_kernel(model, v, tau, fw=True, q=None)

Elastic wave equation time stepper

  • Parameters:
    • model (Model) – Physical model
    • v (VectorTimeFunction) – Particle Velocity
    • tau (TensorTimeFunction) – Stress tensor
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)

kernels.tti_kernel(model, u1, u2, fw=True, q=None)

TTI wave equation time stepper

  • Parameters:
    • model (Model) – Physical model
    • u1 (TimeFunction) – First component (pseudo-P) of the wavefield
    • u2 (TimeFunction) – First component (pseudo-P) of the wavefield
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)

kernels.wave_kernel(model, u, fw=True, q=None, f0=0.015)

Pde kernel corresponding the the model for the input wavefield

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or tuple) – wavefield (tuple if TTI or Viscoacoustic)
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source
    • f0 (Peak frequency) –

models module

class models.Model(origin, spacing, shape, space_order=8, nbl=40, dtype=<class 'numpy.float32'>, m=None, epsilon=None, delta=None, theta=None, phi=None, rho=None, b=None, qp=None, lam=None, mu=None, dm=None, fs=False, **kwargs)

The physical model used in seismic inversion : shape_pml = np.array(shape) + 2 * self.nbl processes.

  • Parameters:
    • origin (tuple of floats) – Origin of the model in m as a tuple in (x,y,z) order.
    • spacing (tuple of floats) – Grid size in m as a Tuple in (x,y,z) order.
    • shape (tuple of int) – Number of grid points size in (x,y,z) order.
    • space_order (int) – Order of the spatial stencil discretisation.
    • m (array_like or float) – Squared slownes in s^2/km^2
    • nbl (int , optional) – The number of absorbin layers for boundary damping.
    • dtype (np.float32 or np.float64) – Defaults to 32.
    • epsilon (array_like or float , optional) – Thomsen epsilon parameter (0<epsilon<1).
    • delta (array_like or float) – Thomsen delta parameter (0<delta<1), delta<epsilon.
    • theta (array_like or float) – Tilt angle in radian.
    • phi (array_like or float) – Asymuth angle in radian.
    • dt (Float) – User provided computational time-step

property critical_dt

Critical computational time step value from the CFL condition.

property dim

Spatial dimension of the problem and model domain.

property dm

Model perturbation for linearized modeling

property domain_size

Physical size of the domain as determined by shape and spacing

property dt

User provided dt

property dtype

Data type for all assocaited data objects.

property is_elastic

Whether the model is TTI or isotopic

property is_tti

Whether the model is TTI or isotopic

property is_viscoacoustic

Whether the model is TTI or isotopic

property m

Function holding the squared slowness in s^2/km^2.

property padsizes

property physical_parameters

List of physical parameteres

physical_params(**kwargs)

Return all set physical parameters and update to input values if provided

property space_dimensions

Spatial dimensions of the grid

property space_order

Spatial discretization order

property spacing

Grid spacing for all fields in the physical model.

property spacing_map

Map between spacing symbols and their values for each SpaceDimension.

property vp

Symbolic representation of the velocity vp = sqrt(1 / m)

property zero_thomsen

propagators module

propagators.adjoint(*args, **kwargs)

propagators.born(model, srccoords, rcvcoords, wavelet, spaceorder=8, save=False, qwf=None, returnop=False, ic='as', freqlist=None, dftsub=None, ws=None, t_sub=1, nlind=False, f0=0.015, illum=False, fw=True)

Low level propagator, to be used through interface.py Compute linearized wavefield U = J(m)* δ m and related quantities.

propagators.forward(model, srccoords, rcvcoords, wavelet, spaceorder=8, save=False, qwf=None, returnop=False, freqlist=None, dftsub=None, normwf=False, wfun=None, ws=None, wr=None, t_sub=1, f0=0.015, illum=False, fw=True, **kwargs)

Low level propagator, to be used through interface.py Compute forward wavefield u = A(m)^{-1}*f and related quantities (u(xrcv))

propagators.forwardgrad(model, srccoords, rcvcoords, wavelet, v, spaceorder=8, q=None, ws=None, ic='as', w=None, freq=None, f0=0.015, **kwargs)

Low level propagator, to be used through interface.py Compute forward wavefield u = A(m)^{-1}*f and related quantities (u(xrcv))

propagators.gradient(model, residual, rcvcoords, u, returnop=False, spaceorder=8, fw=True, w=None, freq=None, dftsub=None, ic='as', f0=0.015, save=True, illum=False)

Low level propagator, to be used through interface.py Compute the action of the adjoint Jacobian onto a residual J’* δ d.

sensitivity module

sensitivity.Loss(dsyn, dobs, dt, is_residual=False, misfit=None)

L2 loss and residual between the synthetic data dsyn and observed data dobs

  • Parameters:
    • dsyn (SparseTimeFunction or tuple) – Synthetic data or tuple (background, linearized) data
    • dobs (SparseTimeFunction) – Observed data
    • dt (float) – Time sampling rate
    • is_residual (bool) – Whether input dobs is already the data residual
    • misfit (function) – User provided function of the form: misifit(dsyn, dob) -> obj, adjoint_source

sensitivity.basic_src(model, u, **kwargs)

Basic source for linearized modeling

  • Parameters:
    • model (Model) – Model containing the perturbation dm
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)

sensitivity.crosscorrfreq(u, v, model, freq=None, dftsub=None, **kwargs)

Standard cross-correlation imaging condition with on-th-fly-dft

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure
    • freq (Array) – Array of frequencies for on-the-fly DFT
    • factor (int) – Subsampling factor for DFT

sensitivity.crosscorr_time(u, v, model, **kwargs)

Cross correlation of forward and adjoint wavefield

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure

sensitivity.func_name(freq=None, ic='as')

Get key for imaging condition/linearized source function

sensitivity.fwi_freq(*ar, **kw)

sensitivity.fwi_src(*ar, **kw)

sensitivity.fwi_time(*ar, **kw)

sensitivity.gradexpr(gradm, u, v, model, w=None, freq=None, dftsub=None, ic='as')

Gradient update stencil

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure
    • w (Float or Expr (**optional )) – Weight for the gradient expression (default=1)
    • freq (Array) – Array of frequencies for on-the-fly DFT
    • factor (int) – Subsampling factor for DFT
    • isic (Bool) – Whether or not to use inverse scattering imaging condition (not supported yet)

sensitivity.inner_grad(u, v)

Inner product of the gradient of two Function.

  • Parameters:
    • u (TimeFunction or Function) – First wavefield
    • v (TimeFunction or Function) – Second wavefield

sensitivity.isic_freq(u, v, model, **kwargs)

Inverse scattering imaging condition

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure

sensitivity.isic_src(model, u, **kwargs)

ISIC source for linearized modeling

  • Parameters:
    • model (Model) – Model containing the perturbation dm
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)

sensitivity.isic_time(u, v, model, **kwargs)

Inverse scattering imaging condition

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure

sensitivity.lin_src(model, u, ic='as')

Source for linearized modeling

  • Parameters:
    • model (Model) – Model containing the perturbation dm
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • ic (String) – Imaging condition of which we compute the linearized source

sources module

class sources.PointSource(*args)

Symbolic data object for a set of sparse point sources

  • Parameters:
    • name (String) – Name of the symbol representing this source
    • grid (Grid) – Grid object defining the computational domain.
    • coordinates (Array) – Point coordinates for this source
    • data ( (**Optional ) Data) – values to initialise point data
    • ntime (Int (**Optional )) – Number of timesteps for which to allocate data
    • npoint (Int (**Optional )) –
    • source (Number of sparse points represented by this) –
    • dimension (Dimension (**Optional )) – object for representing the number of points in this source
    • Note
    • fully (either the dimensions ntime and npoint or the) –
    • provided. (initialised data array need to be) –

defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*

is_commutative : bool | None = True

is_complex = True

isextendedreal : bool | None = True

is_finite = True

is_hermitian = True

is_imaginary = False

is_infinite = False

is_real : bool | None = True

sources.Receiver

alias of PointSource

class sources.RickerSource(*args)

Symbolic object that encapsulate a set of sources with a pre-defined Ricker wavelet: http://subsurfwiki.org/wiki/Ricker_wavelet name: Name for the resulting symbol grid: Grid object defining the computational domain. f0: Peak frequency for Ricker wavelet in kHz time: Discretized values of time in ms

defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*

is_commutative : bool | None = True

is_complex = True

isextendedreal : bool | None = True

is_finite = True

is_hermitian = True

is_imaginary = False

is_infinite = False

is_real : bool | None = True

wavelet(timev)

class sources.TimeAxis(start=None, step=None, num=None, stop=None)

Data object to store the TimeAxis. Exactly three of the four key arguments must be prescribed. Because of remainder values it is not possible to create a TimeAxis that exactly adhears to the inputs therefore start, stop, step and num values should be taken from the TimeAxis object rather than relying upon the input values. The four possible cases are: * start is None: start = step*(1 - num) + stop * step is None: step = (stop - start)/(num - 1) * num is None: num = ceil((stop - start + step)/step) and because of remainder stop = step*(num - 1) + start * stop is None: stop = step*(num - 1) + start

  • Parameters:
    • start (float , optional) – Start of time axis.
    • step (float , optional) – Time interval.
    • num (int , optional) – Number of values (Note: this is the number of intervals + 1). Stop value is reset to correct for remainder.
    • stop (float , optional) – End time.

time_values

wave_utils module

Module contents

+Devito backend reference · JUDI documentation

pysource package

Submodules

FD_utils module

FDutils.Rmat(model)

Rotation matrix according to tilt and asymut.

  • Parameters: model (Model) – Model structure

FDutils.divs(func, sofact=1, side=-1)

GrDivergenceadient shifted by half a grid point, only to be used in combination with grads.

FDutils.grads(func, sofact=1, side=1)

Gradient shifted by half a grid point, only to be used in combination with divs.

FD_utils.laplacian(v, irho)

Laplacian with density div( 1/rho grad) (u)

FDutils.satti(u, v, model)

Tensor factorized SSA TTI wave equation spatial derivatives.

  • Parameters:
    • u (TimeFunction) – first TTI field
    • v (TimeFunction) – second TTI field
    • model (Model) – Model structure

FDutils.thomsenmat(model)

Diagonal Matrices with Thomsen parameters for vectorial temporaries computation.

  • Parameters: model (Model) – Model structure

checkpoint module

class checkpoint.CheckpointOperator(op, **kwargs)

Devito’s concrete implementation of the ABC pyrevolve.Operator. This class wraps devito.Operator so it conforms to the pyRevolve API. pyRevolve will call apply with arguments tstart and tend. Devito calls these arguments ts and te so the following dict is used to perform the translations between different names.

  • Parameters:
    • op (Operator) – devito.Operator object that this object will wrap.
    • args (dict) – If devito.Operator.apply() expects any arguments, they can be provided here to be cached. Any calls to CheckpointOperator.apply() will automatically include these cached arguments in the call to the underlying devito.Operator.apply().

apply(tstart, tend)

If the devito operator requires some extra arguments in the call to apply they can be stored in the args property of this object so pyRevolve calls pyRevolve.Operator.apply() without caring about these extra arguments while this method passes them on correctly to devito.Operator

targnames = {'tend': 'timeM', 'tstart': 'timem'}

class checkpoint.DevitoCheckpoint(objects)

Devito’s concrete implementation of the Checkpoint abstract base class provided by pyRevolve. Holds a list of symbol objects that hold data.

property dtype

data type

get_data(timestep)

returns the data (wavefield) for the time-step timestep

getdatalocation(timestep)

returns the data (wavefield) for the time-step timestep

load()

NotImplementedError

save()

NotImplementedError

property size

The memory consumption of the data contained in a checkpoint.

checkpoint.getsymboldata(symbol, timestep)

Return the symbol corresponding to the data at time-step timestep

geom_utils module

geomutils.geomexpr(model, u, srccoords=None, reccoords=None, wavelet=None, fw=True, nt=None)

Generates the source injection and receiver interpolation. This function is fully abstracted and does not care whether this is a forward or adjoint wave-equation. The source is the source term of the equation The receiver is the measurment term

Therefore, for the adjoint, this function has to be called as: srcrec(model, v, srccoords=rec_coords, …) because the data is the sources

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or tuple) – Wavefield to inject into and read from
    • src_coords (Array) – Physical coordinates of the sources
    • rec_coords (Array) – Physical coordinates of the receivers
    • wavelet (Array) – Data for the source
    • fw=True – Whether the direction is forward or backward in time
    • nt (int) – Number of time steps

geomutils.srcrec(model, u, srccoords=None, reccoords=None, wavelet=None, nt=None)

interface module

interface.Jadjoint(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, checkpointing=False, ncheckpoints=None, tsub=1, returnobj=False, freqlist=[], dftsub=None, ic='as', illum=False, ws=None, f0=0.015, born_fwd=False, nlind=False, misfit=None, fw=True)

Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Supports three modes: * Checkpinting * Frequency compression (on-the-fly DFT) * Standard zero lag cross correlation over time

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • checkpointing (Bool) – Whether or not to use checkpointing
    • n_checkpoints (Int) – Number of checkpoints for checkpointing
    • maxmem (Float) – Maximum memory to use for checkpointing
    • freq_list (List) – List of frequencies for on-the-fly DFT
    • dft_sub (Int) – Subsampling factor for on-the-fly DFT
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.Jadjointcheckpointing(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, ncheckpoints=None, bornfwd=False, return_obj=False, ic='as', ws=None, nlind=False, f0=0.015, misfit=None, illum=False, fw=True)

Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Checkpointing.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • checkpointing (Bool) – Whether or not to use checkpointing
    • n_checkpoints (Int) – Number of checkpoints for checkpointing
    • maxmem (Float) – Maximum memory to use for checkpointing
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • is_residual (Bool) – Whether to treat the input as the residual or as the observed data
    • born_fwd (Bool) – Whether to use the forward or linearized forward modeling operator
    • nlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.Jadjointfreq(model, srccoords, wavelet, reccoords, recin, spaceorder=8, freqlist=[], isresidual=False, returnobj=False, nlind=False, dftsub=None, ic='as', ws=None, bornfwd=False, f0=0.015, misfit=None, illum=False, fw=True)

Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Frequency compression (on-the-fly DFT).

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • freq_list (List) – List of frequencies for on-the-fly DFT
    • dft_sub (Int) – Subsampling factor for on-the-fly DFT
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • is_residual (Bool) – Whether to treat the input as the residual or as the observed data
    • born_fwd (Bool) – Whether to use the forward or linearized forward modeling operator
    • nlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.Jadjointstandard(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, returnobj=False, bornfwd=False, illum=False, ic='as', ws=None, t_sub=1, nlind=False, f0=0.015, misfit=None, fw=True)

Adjoint Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with standard zero lag cross correlation over time.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • recin (Array) – Receiver data
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • ws (Array) – Extended source spatial distribution
    • is_residual (Bool) – Whether to treat the input as the residual or as the observed data
    • born_fwd (Bool) – Whether to use the forward or linearized forward modeling operator
    • nlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation as base propagator
  • Returns: Adjoint jacobian on the input data (gradient)
  • Return type: Array

interface.adjointw(model, reccoords, data, wavelet, space_order=8, f0=0.015, illum=False, fw=True)

Adjoint/backward modeling of a shot record (receivers as source) for an extended source setup Pw*F^T*Pr^T*d_obs.

  • Parameters:
    • model (Model) – Physical model
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • data (Array) – Shot gather
    • wavelet (Array) – Time signature of the forward source for stacking along time
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: spatial distribution
  • Return type: Array

interface.bornrec(model, srccoords, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)

Linearized (Born) modeling of a point source for a model perturbation (square slowness) dm.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.bornrecw(model, weight, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)

Linearized (Born) modeling of an extended source for a model perturbation (square slowness) dm with an extended source

  • Parameters:
    • model (Model) – Physical model
    • weight (Array) – Spatial distriubtion of the extended source
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • ic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardnorec(model, srccoords, wavelet, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of a point source without receiver.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Wavefield
  • Return type: Array

interface.forwardrec(model, srccoords, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)

Modeling of a point source with receivers Pr*F*Ps^T*q.

  • Parameters:
    • model (Model) – Physical model
    • src_coords (Array) – Coordiantes of the source(s)
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardrecw(model, weight, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of an extended source with receivers Pr*F*Pw^T*w

  • Parameters:
    • model (Model) – Physical model
    • weights (Array) – Spatial distribution of the extended source.
    • wavelet (Array) – Source signature
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardwfsrc(model, u, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of a full wavefield source Pr*F*u.

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or Array) – Time-space dependent wavefield
    • rec_coords (Array) – Coordiantes of the receiver(s)
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Shot record
  • Return type: Array

interface.forwardwfsrcnorec(model, u, spaceorder=8, f0=0.015, illum=False, fw=True)

Forward modeling of a full wavefield source without receiver F*u.

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or Array) – Time-space dependent wavefield
    • space_order (Int (**optional )) – Spatial discretization order, defaults to 8
    • f0 (float) – peak frequency
    • illum (bool) – Whether to compute illumination during propagation
    • fw (bool) – Whether it is forward or adjoint propagation
  • Returns: Wavefield
  • Return type: Array

interface.wrifunc(model, srccoords, wavelet, reccoords, recin, yin, spaceorder=8, ic='as', ws=None, tsub=1, grad='m', gradcorr=False, alphaop=False, wfun=None, eps=0, freq_list=[], wfilt=None, f0=0.015)

Time domain wavefield reconstruction inversion wrapper

kernels module

kernels.SLS2ndorder(model, p, fw=True, q=None, f0=0.015)

Viscoacoustic 2nd SLS wave equation. https://library.seg.org/doi/10.1190/geo2013-0030.1

Bulk modulus moved to rhs. The adjoint equation is directly derived as the discrete adjoint of the forward PDE which leads to a slightly different formulation than in the paper.

  • Parameters:
    • model (Model) – Physical model
    • u1 (TimeFunction) – Pressure field
    • u2 (TimeFunction) – Attenuation Memory variable
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)
    • f0 (Peak frequency) –

kernels.acoustic_kernel(model, u, fw=True, q=None)

Acoustic wave equation time stepper

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or tuple) – wavefield (tuple if TTI)
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source

kernels.elastic_kernel(model, v, tau, fw=True, q=None)

Elastic wave equation time stepper

  • Parameters:
    • model (Model) – Physical model
    • v (VectorTimeFunction) – Particle Velocity
    • tau (TensorTimeFunction) – Stress tensor
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)

kernels.tti_kernel(model, u1, u2, fw=True, q=None)

TTI wave equation time stepper

  • Parameters:
    • model (Model) – Physical model
    • u1 (TimeFunction) – First component (pseudo-P) of the wavefield
    • u2 (TimeFunction) – First component (pseudo-P) of the wavefield
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)

kernels.wave_kernel(model, u, fw=True, q=None, f0=0.015)

Pde kernel corresponding the the model for the input wavefield

  • Parameters:
    • model (Model) – Physical model
    • u (TimeFunction or tuple) – wavefield (tuple if TTI or Viscoacoustic)
    • fw (Bool) – Whether forward or backward in time propagation
    • q (TimeFunction or Expr) – Full time-space source
    • f0 (Peak frequency) –

models module

class models.Model(origin, spacing, shape, space_order=8, nbl=40, dtype=<class 'numpy.float32'>, m=None, epsilon=None, delta=None, theta=None, phi=None, rho=None, b=None, qp=None, lam=None, mu=None, dm=None, fs=False, **kwargs)

The physical model used in seismic inversion : shape_pml = np.array(shape) + 2 * self.nbl processes.

  • Parameters:
    • origin (tuple of floats) – Origin of the model in m as a tuple in (x,y,z) order.
    • spacing (tuple of floats) – Grid size in m as a Tuple in (x,y,z) order.
    • shape (tuple of int) – Number of grid points size in (x,y,z) order.
    • space_order (int) – Order of the spatial stencil discretisation.
    • m (array_like or float) – Squared slownes in s^2/km^2
    • nbl (int , optional) – The number of absorbin layers for boundary damping.
    • dtype (np.float32 or np.float64) – Defaults to 32.
    • epsilon (array_like or float , optional) – Thomsen epsilon parameter (0<epsilon<1).
    • delta (array_like or float) – Thomsen delta parameter (0<delta<1), delta<epsilon.
    • theta (array_like or float) – Tilt angle in radian.
    • phi (array_like or float) – Asymuth angle in radian.
    • dt (Float) – User provided computational time-step

property critical_dt

Critical computational time step value from the CFL condition.

property dim

Spatial dimension of the problem and model domain.

property dm

Model perturbation for linearized modeling

property domain_size

Physical size of the domain as determined by shape and spacing

property dt

User provided dt

property dtype

Data type for all assocaited data objects.

property is_elastic

Whether the model is TTI or isotopic

property is_tti

Whether the model is TTI or isotopic

property is_viscoacoustic

Whether the model is TTI or isotopic

property m

Function holding the squared slowness in s^2/km^2.

property padsizes

property physical_parameters

List of physical parameteres

physical_params(**kwargs)

Return all set physical parameters and update to input values if provided

property space_dimensions

Spatial dimensions of the grid

property space_order

Spatial discretization order

property spacing

Grid spacing for all fields in the physical model.

property spacing_map

Map between spacing symbols and their values for each SpaceDimension.

property vp

Symbolic representation of the velocity vp = sqrt(1 / m)

property zero_thomsen

propagators module

propagators.adjoint(*args, **kwargs)

propagators.born(model, srccoords, rcvcoords, wavelet, spaceorder=8, save=False, qwf=None, returnop=False, ic='as', freqlist=None, dftsub=None, ws=None, t_sub=1, nlind=False, f0=0.015, illum=False, fw=True)

Low level propagator, to be used through interface.py Compute linearized wavefield U = J(m)* δ m and related quantities.

propagators.forward(model, srccoords, rcvcoords, wavelet, spaceorder=8, save=False, qwf=None, returnop=False, freqlist=None, dftsub=None, normwf=False, wfun=None, ws=None, wr=None, t_sub=1, f0=0.015, illum=False, fw=True, **kwargs)

Low level propagator, to be used through interface.py Compute forward wavefield u = A(m)^{-1}*f and related quantities (u(xrcv))

propagators.forwardgrad(model, srccoords, rcvcoords, wavelet, v, spaceorder=8, q=None, ws=None, ic='as', w=None, freq=None, f0=0.015, **kwargs)

Low level propagator, to be used through interface.py Compute forward wavefield u = A(m)^{-1}*f and related quantities (u(xrcv))

propagators.gradient(model, residual, rcvcoords, u, returnop=False, spaceorder=8, fw=True, w=None, freq=None, dftsub=None, ic='as', f0=0.015, save=True, illum=False)

Low level propagator, to be used through interface.py Compute the action of the adjoint Jacobian onto a residual J’* δ d.

sensitivity module

sensitivity.Loss(dsyn, dobs, dt, is_residual=False, misfit=None)

L2 loss and residual between the synthetic data dsyn and observed data dobs

  • Parameters:
    • dsyn (SparseTimeFunction or tuple) – Synthetic data or tuple (background, linearized) data
    • dobs (SparseTimeFunction) – Observed data
    • dt (float) – Time sampling rate
    • is_residual (bool) – Whether input dobs is already the data residual
    • misfit (function) – User provided function of the form: misifit(dsyn, dob) -> obj, adjoint_source

sensitivity.basic_src(model, u, **kwargs)

Basic source for linearized modeling

  • Parameters:
    • model (Model) – Model containing the perturbation dm
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)

sensitivity.crosscorrfreq(u, v, model, freq=None, dftsub=None, **kwargs)

Standard cross-correlation imaging condition with on-th-fly-dft

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure
    • freq (Array) – Array of frequencies for on-the-fly DFT
    • factor (int) – Subsampling factor for DFT

sensitivity.crosscorr_time(u, v, model, **kwargs)

Cross correlation of forward and adjoint wavefield

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure

sensitivity.func_name(freq=None, ic='as')

Get key for imaging condition/linearized source function

sensitivity.fwi_freq(*ar, **kw)

sensitivity.fwi_src(*ar, **kw)

sensitivity.fwi_time(*ar, **kw)

sensitivity.gradexpr(gradm, u, v, model, w=None, freq=None, dftsub=None, ic='as')

Gradient update stencil

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure
    • w (Float or Expr (**optional )) – Weight for the gradient expression (default=1)
    • freq (Array) – Array of frequencies for on-the-fly DFT
    • factor (int) – Subsampling factor for DFT
    • isic (Bool) – Whether or not to use inverse scattering imaging condition (not supported yet)

sensitivity.inner_grad(u, v)

Inner product of the gradient of two Function.

  • Parameters:
    • u (TimeFunction or Function) – First wavefield
    • v (TimeFunction or Function) – Second wavefield

sensitivity.isic_freq(u, v, model, **kwargs)

Inverse scattering imaging condition

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure

sensitivity.isic_src(model, u, **kwargs)

ISIC source for linearized modeling

  • Parameters:
    • model (Model) – Model containing the perturbation dm
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)

sensitivity.isic_time(u, v, model, **kwargs)

Inverse scattering imaging condition

  • Parameters:
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • v (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)
    • model (Model) – Model structure

sensitivity.lin_src(model, u, ic='as')

Source for linearized modeling

  • Parameters:
    • model (Model) – Model containing the perturbation dm
    • u (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)
    • ic (String) – Imaging condition of which we compute the linearized source

sources module

class sources.PointSource(*args)

Symbolic data object for a set of sparse point sources

  • Parameters:
    • name (String) – Name of the symbol representing this source
    • grid (Grid) – Grid object defining the computational domain.
    • coordinates (Array) – Point coordinates for this source
    • data ( (**Optional ) Data) – values to initialise point data
    • ntime (Int (**Optional )) – Number of timesteps for which to allocate data
    • npoint (Int (**Optional )) –
    • source (Number of sparse points represented by this) –
    • dimension (Dimension (**Optional )) – object for representing the number of points in this source
    • Note
    • fully (either the dimensions ntime and npoint or the) –
    • provided. (initialised data array need to be) –

defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*

is_commutative : bool | None = True

is_complex = True

isextendedreal : bool | None = True

is_finite = True

is_hermitian = True

is_imaginary = False

is_infinite = False

is_real : bool | None = True

sources.Receiver

alias of PointSource

class sources.RickerSource(*args)

Symbolic object that encapsulate a set of sources with a pre-defined Ricker wavelet: http://subsurfwiki.org/wiki/Ricker_wavelet name: Name for the resulting symbol grid: Grid object defining the computational domain. f0: Peak frequency for Ricker wavelet in kHz time: Discretized values of time in ms

defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*

is_commutative : bool | None = True

is_complex = True

isextendedreal : bool | None = True

is_finite = True

is_hermitian = True

is_imaginary = False

is_infinite = False

is_real : bool | None = True

wavelet(timev)

class sources.TimeAxis(start=None, step=None, num=None, stop=None)

Data object to store the TimeAxis. Exactly three of the four key arguments must be prescribed. Because of remainder values it is not possible to create a TimeAxis that exactly adhears to the inputs therefore start, stop, step and num values should be taken from the TimeAxis object rather than relying upon the input values. The four possible cases are: * start is None: start = step*(1 - num) + stop * step is None: step = (stop - start)/(num - 1) * num is None: num = ceil((stop - start + step)/step) and because of remainder stop = step*(num - 1) + start * stop is None: stop = step*(num - 1) + start

  • Parameters:
    • start (float , optional) – Start of time axis.
    • step (float , optional) – Time interval.
    • num (int , optional) – Number of values (Note: this is the number of intervals + 1). Stop value is reset to correct for remainder.
    • stop (float , optional) – End time.

time_values

wave_utils module

Module contents

diff --git a/dev/search/index.html b/dev/search/index.html index 7c5b48e92..22d46c212 100644 --- a/dev/search/index.html +++ b/dev/search/index.html @@ -1,2 +1,2 @@ -Search · JUDI documentation

Loading search...

    +Search · JUDI documentation

    Loading search...

      diff --git a/dev/search_index.js b/dev/search_index.js index 1042865e3..e53e81ab2 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"tutorials/01_intro/#Introduction-to-JUDI","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI is a framework for large-scale seismic modeling and inversion and designed to enable rapid translations of algorithms to fast and efficient code that scales to industry-size 3D problems. The focus of the package lies on seismic modeling as well as PDE-constrained optimization such as full-waveform inversion (FWI) and imaging (LS-RTM). Wave equations in JUDI are solved with Devito, a Python domain-specific language for automated finite-difference (FD) computations. JUDI's modeling operators can also be used as layers in (convolutional) neural networks to implement physics-augmented deep learning algorithms. For this, check out JUDI's deep learning extension via ChainRules.jl in this tutorial.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"using JUDI, PyPlot, LinearAlgebra","category":"page"},{"location":"tutorials/01_intro/#Physical-problem-setup","page":"Introduction to JUDI","title":"Physical problem setup","text":"","category":"section"},{"location":"tutorials/01_intro/#Grid","page":"Introduction to JUDI","title":"Grid","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI relies on a cartesian grid for modeling and inversion. We start by defining the parameters needed for a cartesian grid:","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"A shape\nA grid spacing in each direction\nAn origin","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"shape = (201, 201) # Number of gridpoints nx, nz\nspacing = (10.0, 10.0) # #n meters here\norigin = (0.0, 0.0) # In meters as well","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(0.0, 0.0)","category":"page"},{"location":"tutorials/01_intro/#Physical-object","page":"Introduction to JUDI","title":"Physical object","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI defines a few basic types to handle physical object such as the velocity model. The type PhyisicalParameter is an abstract vector and behaves as a standard vector. A PhysicalParameter can be constructed in various ways but always require the origin o and grid spacing d that cannot be infered from the array.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"PhysicalParameter(v::Array{vDT}, d, o) where `v` is an n-dimensional array and n=size(v)\n\nPhysicalParameter(n, d, o; vDT=Float32) Creates a zero PhysicalParameter\n\nPhysicalParameter(v::Array{vDT}, A::PhysicalParameter) Creates a PhysicalParameter from the Array `v` with n, d, o from `A`\n\nPhysicalParameter(v::Array{vDT, N}, n::Tuple, d::Tuple, o::Tuple) where `v` is a vector or nd-array that is reshaped into shape `n`\n\nPhysicalParameter(v::vDT, n::Tuple, d::Tuple, o::Tuple) Creates a constant (single number) PhyicalParameter","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Let's make a simple three layer velocity model","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"# Define the velocity (in km/sec=m/ms)\nvp = 1.5f0 * ones(Float32, shape)\nvp[:, 66:end] .= 2.0f0\nvp[:, 134:end] .= 2.5f0\n# Create a physical parameter\nVP = PhysicalParameter(vp, spacing, origin);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Let's plot the velocities. Because we adopt a standad cartesian dimension ordering for generality (X, Z) in 2D and (X, Y, Z) in 3D, we plot the transpose of the velocity for proper visualization.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"figure()\nsubplot(121)\nimshow(vp', cmap=\"jet\")\ntitle(\"vp\")\nsubplot(122)\nimshow(VP', cmap=\"jet\")\ntitle(\"vp as a physical parameter\")","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"PyObject Text(0.5, 1.0, 'vp as a physical parameter')","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Because the physical parameter behaves as vector, we can easily perform standard operations on it.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"norm(VP), extrema(VP), 2f0 .* VP, VP .^ 2","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(411.6956f0, (1.5f0, 2.5f0), PhysicalParameter{Float32} of size (201, 201) with origin (0.0, 0.0) and spacing (10.0, 10.0), PhysicalParameter{Float32} of size (201, 201) with origin (0.0, 0.0) and spacing (10.0, 10.0))","category":"page"},{"location":"tutorials/01_intro/#Model","page":"Introduction to JUDI","title":"Model","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI then provide a Model structure that wraps multiple physical parameters toghether. A Model accept currently only accept standard Array as an input (to be fixed #1)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"model = Model(shape, spacing, origin, 1f0./vp.^2f0)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Model (n=(201, 201), d=(10.0f0, 10.0f0), o=(0.0f0, 0.0f0)) with parameters [:m]","category":"page"},{"location":"tutorials/01_intro/#Modeling","page":"Introduction to JUDI","title":"Modeling","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now that we have a seismic model, we will generate a few shot records.","category":"page"},{"location":"tutorials/01_intro/#Acquisition-Geometry","page":"Introduction to JUDI","title":"Acquisition Geometry","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"The first thing we need is an acquisiton geometry. In JUDI there is two ways to create a Geometry.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"By hand, as we will show here\nFrom a SEGY file, as we will show in a follow-up tutorial","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"We create a split-spread geomtry with sources at the top and receivers at the ocean bottom (top of second layer).","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Note:","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI currently expects all three coordinates to be inputed to setup a Geometry in 2D as well. This will be fixed in a later version of JUDI.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"# Sources position\nnsrc = 11\nxsrc = range(0f0, (shape[1] -1)*spacing[1], length=nsrc)\nysrc = 0f0 .* xsrc # this a 2D case so we set y to zero\nzsrc = 12.5f0*ones(Float32, nsrc);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now this definition creates a single Array of position, which would correspond to a single Simultenous source. Since we are interested in single source experiments here, we convert these position into an Array of Array of size nsrc where each sub-array is a single source position","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"xsrc, ysrc, zsrc = convertToCell.([xsrc, ysrc, zsrc]);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"# OBN position\nnrec = 101\nxrec = range(0f0, (shape[1] -1)*spacing[1], length=nrec)\nyrec = 0f0 # this a 2D case so we set y to zero. This can be a single number for receivers\nzrec = (66*spacing[1])*ones(Float32, nrec);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"The last step to be able to create and acquisiton geometry is to define a recording time and sampling rate","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"record_time = 4000f0 # Recording time in ms (since we have m/ms for the velocity)\nsampling_rate = 4f0; # Let's use a standard 4ms sampling rate","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now we can create the source and receivers geometry","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"src_geom = Geometry(xsrc, ysrc, zsrc; dt=sampling_rate, t=record_time)\n# For the receiver geometry, we specify the number of source to tell JUDI to use the same receiver position for all sources\nrec_geom = Geometry(xrec, yrec, zrec, dt=sampling_rate, t=record_time, nsrc=nsrc);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Let's visualize the geometry onto the model","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"figure();\nimshow(vp', cmap=\"jet\", extent=[0, (shape[1]-1)*spacing[1], (shape[2]-1)*spacing[2], 0])\nscatter(xsrc, zsrc, label=:sources)\nscatter(xrec, zrec, label=\"OBN\")\nlegend()","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"PyObject ","category":"page"},{"location":"tutorials/01_intro/#Source-wavelet","page":"Introduction to JUDI","title":"Source wavelet","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"For the source wavelet, we will use a standard Ricker wavelet at 10Hz for this tutorial.In practice this wavelet would be read from a file or estimated during inversion. ","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"f0 = 0.010 # Since we use ms, the frequency is in KHz\nwavelet = ricker_wavelet(record_time, sampling_rate, f0);\nplot(wavelet)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"1-element Vector{PyCall.PyObject}:\n PyObject ","category":"page"},{"location":"tutorials/01_intro/#judiVector","page":"Introduction to JUDI","title":"judiVector","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"In order to represent seismic data, JUDI provide the judiVector type. This type wraps a geometry with the seismic data corresponding to it. Let's cretae one for the source","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"q = judiVector(src_geom, wavelet)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"judiVector{Float32, Matrix{Float32}} with 11 sources","category":"page"},{"location":"tutorials/01_intro/#Linear-operator","page":"Introduction to JUDI","title":"Linear operator","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"The last step to model our seismic data os to create the linear operator representing the discretized wave equation on the Model we defined. We also need to define the linear operator corresponding to the source injection and the receiver interpolation.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Pr = judiProjection(rec_geom) # receiver interpolation\nPs = judiProjection(src_geom) # Source interpolation\nAinv = judiModeling(model) # Inverse of the disrete ewave equation.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI forward{Float32} propagator (x * z * time) -> (x * z * time)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"WARNING While these three operator are well defined in JUDI, judiProjection is a no-op operator and cannot be used by itself but only in combination with a judiModeling operator","category":"page"},{"location":"tutorials/01_intro/#Seismic-data","page":"Introduction to JUDI","title":"Seismic data","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now that we have all our operators setup we can finally generate synthetic data wit ha simple mat-vec product thanks to the abstraction","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"d_obs = Pr * Ainv * Ps' * q","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Building forward operator\nOperator `forward` ran in 0.48 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.69 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.69 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.66 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.66 s\nOperator `forward` ran in 0.68 s\n\n\n\n\n\njudiVector{Float32, Matrix{Float32}} with 11 sources","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"data_extent = [xrec[1], xrec[end], 1f-3*record_time, 0]\nfigure(figsize=(20, 5))\nfor i=1:5\n subplot(1, 5, i)\n imshow(d_obs.data[2*i], vmin=-1, vmax=1, cmap=\"Greys\", extent=data_extent, aspect=\"auto\")\n xlabel(\"Receiver position (m)\")\n ylabel(\"Recording time (s)\")\n title(\"xsrc=$(1f-3xsrc[2*i][1])km\")\nend\ntight_layout()","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Geophysics-tutorial","page":"Full-Waveform Inversion - Part 3: optimization","title":"Geophysics tutorial","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Full-Waveform-Inversion-Part-3:-optimization","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Philipp Witte1*, Mathias Louboutin1, Keegan Lensink1, Michael Lange2, Navjot Kukreja2, Fabio Luporini2, Gerard Gorman2, and Felix J. Herrmann1,3","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"1 Seismic Laboratory for Imaging and Modeling (SLIM), The University of British Columbia ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"2 Imperial College London, London, UK","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"3 now at Georgia Institute of Technology, USA ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Corresponding author: pwitte.slim@gmail.com","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Introduction","page":"Full-Waveform Inversion - Part 3: optimization","title":"Introduction","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This tutorial is the third part of a full-waveform inversion (FWI) tutorial series with a step-by-step walkthrough of setting up forward and adjoint wave equations and building a basic FWI inversion framework. For discretizing and solving wave equations, we use [Devito], a Python-based domain-specific language for automated generation of finite-difference code (Lange et al., 2016). The first two parts of this tutorial (Louboutin et al., 2017, 2018) demonstrated how to solve the acoustic wave equation for modeling seismic shot records and how to compute the gradient of the FWI objective function using the adjoint-state method. With these two key ingredients, we will now build an inversion framework that can be used to minimize the FWI least-squares objective function.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"[Devito]:http://www.opesci.org/devito-public [Julia]:https://julialang.org [JUDI]:https://github.com/slimgroup/JUDI.jl/tree/tletutorialjan_2018","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"FWI is a computationally and mathematically challenging problem. The computational complexity comes from the fact that an already expensive solution procedure for the wave equation needs to be repeated for a large number of source positions for each iteration of the optimization algorithm. The mathematical complexity comes from the fact that the FWI objective is known to have many local minima due to cycle skipping. ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Furthermore, since the solution is not unique it is generally not possible to unambiguously recover the parameterization of the subsurface from the given data alone, making FWI an active field of research (e.g. Leeuwen et al., 2013; Warner and Guasch, 2014; Peters and Herrmann, 2017).

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This tutorial demonstrates how we can set up a basic FWI framework with two alternative gradient-based optimization algorithms: stochastic gradient descent, and the Gauss–Newton method (Nocedal and Wright, 2009). ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"We implement our inversion framework with the Julia Devito Inversion framework (JUDI), a parallel software package for seismic modeling and inversion in the Julia (Bezanson et al., 2012) programming language. JUDI provides abstractions and function wrappers that allow the implementation of wave-equation-based inversion problems such as FWI using code that closely follows the mathematical notation, while using Devito’s automatic code generation for solving the underlying wave equations.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"All the code to run the algorithms and generate the figures in this paper can be found at http://github.com/SEG. ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Optimizing-the-FWI-objective-function","page":"Full-Waveform Inversion - Part 3: optimization","title":"Optimizing the FWI objective function","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"The goal of this tutorial series is to optimize the FWI objective function with the ell_2-misfit:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"\\mathop{\\hbox{minimize}}_{\\mathbf{m}} \\hspace{.2cm} f(\\mathbf{m})= \\sum_{i=1}^{n_s} \\frac{1}{2} \\left\\lVert \\mathbf{d}^\\mathrm{pred}_i (\\mathbf{m}, \\mathbf{q}_i) - \\mathbf{d}_i^\\mathrm{obs} \\right\\rVert_2^2,","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"where mathbfd^mathrmpred_i and mathbfd^mathrmobs_i are the predicted and observed seismic shot records of the i^textth source location and mathbfm is the velocity model (expressed as squared slowness). In part one, we demonstrated how to implement a forward modeling operator to generate the predicted shot records, which we will denote as mathbfd^mathrmpred_i = mathbfF(mathbfmmathbfq_i). In the second tutorial, we then showed how we can compute the gradient nabla f(mathbfm) of the objective function and update our initial model using gradient descent.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"There is a snag, however. This first-order optimization algorithm has a linear convergence rate at best, and typically requires many iterations to converge. Second-order optimization methods converge considerably faster. To implement them, we first approximate the objective with a second-order Taylor expansion:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"f(\\mathbf{m}) = f(\\mathbf{m}_0) + \\nabla f(\\mathbf{m}_0) \\delta \\mathbf{m} + \\delta \\mathbf{m}^\\top \\nabla^2 f(\\mathbf{m}_0) \\delta \\mathbf{m} + \\mathcal{O}(\\delta \\mathbf{m}^3),","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"where mathcalO(delta mathbfm^3) represents the error term, nabla f(mathbfm_0) is the gradient as implemented in part two, and nabla^2 f(mathbfm_0) is the Hessian of the objective function, which we will refer to as mathbfH. Rather than using the negative gradient to incrementally update our model, as in gradient descent, we directly calculate a model update delta mathbfm that leads us to the minimum. This is called Newton's method:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"\\delta \\mathbf{m} = - \\mathbf{H}(\\mathbf{m}_0)^{-1} \\nabla f(\\mathbf{m}_0).","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Although the method converges to the minimum of the FWI objective function quickly, it comes at the cost of having to compute and invert the Hessian matrix (Nocedal and Wright, 2009). Fortunately, for least squares problems, such as FWI, the Hessian can be approximated by the Gauss-Newton (GN) Hessian mathbfJ^top mathbfJ, where mathbfJ is the Jacobian matrix. This is the partial derivative of the forward modeling operator mathbfF(mathbfmmathbfq) with respect to mathbfm — something we can easily compute. Furthermore, the Jacobian can also be used to express the gradient of the FWI objective function as nabla f(mathbfm_0) = mathbfJ^top (mathbfd^mathrmpred_i - mathbfd_i^mathrmobs), where mathbfJ^top is the adjoint (transposed) Jacobian. This is useful, because we now have a set of operators mathbfF mathbfJ and mathbfH_GN=mathbfJ^topmathbfJ, through which we can express both first and second order optimization algorithms for FWI.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Although forming these matrices explicitly is not possible, since they can become extremely large, we only need the action of these operators on vectors. This allows us to implement these operators matrix-free. In the following section we will demonstrate how to set up these operators in our JUDI software framework and to how to use them to implement FWI algorithms.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      [SegyIO]:https://github.com/slimgroup/SegyIO.jl

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      The Julia Devito Inversion framework is a parallel matrix-free linear operator library for seismic modeling and inversion based on Devito and [SeisIO], a performant Julia package for reading and writing large data volumes in SEG-Y format. JUDI allows implementing seismic inversion algorithms as linear algebra operations, enabling rapid translations of FWI algorithms to executable Julia code. The underlying wave equations are set up and solved using Devito, as described in the first two tutorials, and are interfaced from Julia using the PyCall package (Johnson, 2017).

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      For reading and writing SEG-Y data, JUDI uses the SeisIO package, a sophisticated SEG-Y reader that allows us to scan large 3D data sets for creating look-up tables with header summaries. However, since our data set is relatively small, we will directly load the full file into memory. The segy_read command takes the file name as an input and returns a dense data block. This is our observed data and we store it as a JUDI vector.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"# NOT FOR MANUSCRIPT\n# using Distributed\n# addprocs(4);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using JUDI.TimeModeling, JUDI.SLIM_optim, SegyIO, Random, Statistics","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Implementing-FWI-in-JUDI","page":"Full-Waveform Inversion - Part 3: optimization","title":"Implementing FWI in JUDI","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"We start our demonstration by reading our data set, which consists of 16 shot records and was generated with an excerpt from the SEG/EAGE Overthrust model (Aminzadeh et al. 1997). We store it as a judiVector:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"block = segy_read(\"overthrust_shot_records.segy\")\nd_obs = judiVector(block);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using PyPlot\n\ni = 8\nfigure(figsize=(15,6))\nimshow(d_obs.data[i], cmap=\"seismic\", extent=[0,size(d_obs.data[i],2),2,0], aspect=\"auto\", vmin=-3, vmax=3)\nxlabel(\"Receiver number\", size=12)\nylabel(\"Time [s]\", size=12)\ntext(3, 0.13, \"Shot record $i\", size=16)\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Figure 1: Observed shot record number 8.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"JUDI vectors such as d_obs can be used like a regular Julia vectors, so we can compute norms via norm(d_obs) or the inner product via dot(d_obs, d_obs), but they contain the shot records in their original dimension. Shot records can be accessed via their respective shot number with d_obs.data[shot_no], while the header information can be accessed with d_obs.geometry. We extract the source geometry from our SEG-Y file and then manually set up a source vector q with an 8 Hz Ricker wavelet:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"f = 0.008 # kHz\nsrc_geom = Geometry(block; key=\"source\")\nsrc_data = ricker_wavelet(src_geom.t[1], src_geom.dt[1], f)\nq = judiVector(src_geom, src_data);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"plot(q.data[1])\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Since our data set consists of 16 shot records, both d_obs and q contain the data and geometries for all source positions. We can check the number of source positions with d_obs.nsrc and q.nsrc and we can extract the part of the vector that corresponds to one or multiple shots with d_obs[shot_no], q[shot_no].

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"We will now set up the forward modeling operator mathbfF(mathbfmmathbfq) as a matrix-free operator for the inverse wave equation mathbfA(mathbfm)^-1, where mathbfm is the current model, and source/receiver injection and sampling operators mathbfP_mathrms and mathbfP_mathrmr.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Since the dimensions of the inverse wave equation operator depend on the number of computational time steps, we calculate this number using the get_computational_nt function and set up an info object that contains some dimensionality information required by all operators.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Then we can define Pr and Ps as matrix-free operators implementing Devito sparse point injection and interpolation (Louboutin et al., 2017). Multiplications with Ps and Pr represent sampling the wavefield at source/receiver locations, while their adjoints Ps', Pr' denote injecting either source wavelets or shot records into the computational grid.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"These projection and modelling operators can then be set up in Julia in the following way:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using HDF5\n\nm0, n, d, o = read(h5open(\"overthrust_model.h5\",\"r\"),\"m0\",\"n\",\"d\",\"o\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"\n\nPr = judiProjection(d_obs.geometry)\nPs = judiProjection(q.geometry)\nAinv = judiModeling(model0);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"The forward modeling step can be expressed mathematically as","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"F(\\mathbf{m};\\mathbf{q})=\\mathbf{P}r\\mathbf{A}^{-1}(\\mathbf{m})\\mathbf{P}s^\\top\\mathbf{q} $","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"which is expressed in Julia as","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"d_pred = Pr * Ainv * Ps' * q","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This forward models all 16 predicted shot records in parallel. Notice that, in instantiating Ainv, we made the wave equation solver implicitly dependent on model0.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Finally, we set up the matrix-free Jacobian operator J and the Gauss–Newton Hessian J' * J. As mentioned in the introduction, J is the partial derivative of the forward modeling operator mathbfF(mathbfm mathbfq) with respect to the model m and is therefore directly constructed from our modeling operator Pr * Ainv * Ps' and a specified source vector q:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"J = judiJacobian(Pr * Ainv * Ps', q);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Note that, if we started our Julia session with multiple CPU cores or nodes (julia -p n, with n being the number of workers), the wave equation solves are automatically parallelized over source locations and all shots are collected in the d_pred vector.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      We can also model a single or subset of shots by indexing the operators with the respective shot numbers. E.g. if we want to model the first two shots, we define i=[1,2] and then run d_sub = Pr[i]*Ainv[i]*Ps[i]'*q[i].

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      The FWI gradient would then be given by:

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
       g = J[i]' * (d_pred - d_obs[i]) 
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      If we want to solve an adjoint wave equation with the observed data as the adjoint source and restrictions of the wavefields back to the source locations, we can simply run qad = Ps * Ainv' * Pr' * d_obs, exemplifying the advantages of casting FWI in a proper computational linear algebra framework.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Once we have J we can also form the Gauss–Newton Hessian:

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
       H_GN = J' * J 
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"In the context of seismic inversion, the Jacobian is also called the linearized modeling or demigration operator and its adjoint J' is the migration operator. One drawback of this notation is that the forward wavefields for the gradient calculation have to be recomputed, since the forward modeling operator only returns the shot records and not the complete wavefields. For this reason, JUDI has an additional function for computing the gradients of the FWI objective function f,g = fwi_objective(model0,q[i],d_obs[i]), which takes the current model, source and data vectors as an input and computes the objective value and gradient in parallel without having to recompute the forward wavefields.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#FWI-via-gradient-descent","page":"Full-Waveform Inversion - Part 3: optimization","title":"FWI via gradient descent","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"With expressions for modeling operators, Jacobians and gradients of the FWI objective, we can now implement different FWI algorithms in a few lines of code. We will start with a basic gradient descent example with a line search. To reduce the computational cost of full gradient descent, we will use a stochastic approach in which we only compute the gradient and function value for a randomized subset of source locations. In JUDI, this is accomplished by choosing a random vector of integers between 1 and 16 and indexing the data vectors as described earlier. Furthermore, we will apply a projection operator proj(x), which prevent velocities (or squared slownesses) becoming negative or too large by clipping values outside the allowed range.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"A few extra variables are defined in the notebook, but the full algorithm for FWI with stochastic gradient descent and box constraints is implemented as follows:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"# Set up bound constraints.\nv0 = sqrt.(1f0./model0.m)\nvmin = ones(Float32, model0.n) .* 1.3f0\nvmax = ones(Float32, model0.n) .* 6.5f0\n\n# Fix water column.\nvmin[:,1:21] = v0[:,1:21]\nvmax[:,1:21] = v0[:,1:21]\n\n# Convert to squared slowness.\nmmin = vec((1f0./vmax).^2)\nmmax = vec((1f0./vmin).^2);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"maxiter = 10\nbatchsize = 8 # Number of shots for each iteration.\nproj(x) = reshape(median([vec(mmin) vec(x) vec(mmax)], dims=2), model0.n)\nfhistory_SGD = zeros(Float32, maxiter);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"for j=1:maxiter\n \n # FWI objective function value and gradient.\n i = randperm(d_obs.nsrc)[1:batchsize]\n fval, grad = fwi_objective(model0, q[i], d_obs[i])\n fhistory_SGD[j] = fval\n\n # Line search and update model.\n update = backtracking_linesearch(model0, q[i], d_obs[i], fval, grad, proj; alpha=1f0)\n model0.m += reshape(update, model0.n)\n\n # Apply box constraints.\n model0.m = proj(model0.m)\nend","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"model_SGD = copy(model0.m);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"JUDI's backtracking_linesearch function performs an approximate line search and returns a model update that leads to a decrease of the objective function value (Armijo condition; Nocedal and Wright, 2009). The result after 10 iterations of SGD with box constraints is shown in Figure 2. In practice, where starting models are typically less accurate than in our example, FWI is often performed from low to high frequencies, since the objective function has less local minima for lower frequencies (Bunks et al., 1995). In this multi-scale FWI approach, a low-pass filtered version of the data is used to invert for a low resolution velocity model first and higher frequencies are added in subsequent iterations.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#FWI-via-the-Gauss–Newton-method","page":"Full-Waveform Inversion - Part 3: optimization","title":"FWI via the Gauss–Newton method","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"As discussed earlier, the convergence rate of GD depends on the objective function, but requires many FWI iterations necessary to reach an acceptable solution. Using our matrix-free operator for the Jacobian J, we can modify the above code to implement the Gauss–Newton method (Equation 3) to improve the convergence rate. In practice, directly inverting the Gauss–Newton Hessian J'* J should be avoided, because the matrix is badly conditioned and takes many iterations to invert. Instead, we perform a few iterations of a least-squares solver, lsqr(), to approximately solve J * p = d_pred - d_obs and obtain the update direction p. lsqr, from the Julia IterativeSolvers package, is a conjugate-gradient type algorithm for solving least squares problems and is mathematically equivalent to inverting J' * J, but has better numerical properties (Paige and Saunders, 1982). We implement the Gauss-Newton method as follows:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"model0.m = m0 # Reset velocity model.\nfhistory_GN = zeros(Float32,maxiter);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using IterativeSolvers\n\n# Return data as Julia array\nAinv.options.return_array = true\nd_vec = convert_to_array(d_obs)\n\nfor j=1:maxiter\n \n # Model predicted data.\n d_pred = Pr * Ainv * Ps' * q\n\n # GN update direction.\n p = lsqr(J, d_pred - d_obs; maxiter=6)\n fhistory_GN[j] = .5f0 * norm(d_pred - d_vec)^2 # OMIT LINE FROM MANUSCRIPT\n\n # update model and box constraints.\n model0.m = model0.m - reshape(p, model0.n)\nend","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      An important benefit of matrix-free operators is that we do not need to implement our own least-squares solver, but can pass J and the data residual d_pred - d_obs to a third-party optimization library. The operator J does not need to be an explicit matrix, since lsqr only uses matrix-vector products.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"In contrast to our SGD algorithm, we use all shot records in every iteration, since stochastic methods for second order algorithms are less well understood, making this approach considerably more expensive than our previous algorithm. However, as shown in figures 2 and 3, it achieves a superior result, with a considerably lower misfit compared to the known model. Furthermore, figure 3 shows that it achieves the improved result in relatively few iterations.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"figure(figsize=(15, 12))\n\nsubplot(311)\nimshow(sqrt.(1f0./m0)', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4)\ntext(0.15, 0.3, \"Starting model\", size=14, color=\"black\")\nylabel(\"Depth [km]\", size=12)\ncolorbar()\ntext(10.35, 1.5, \"Velocity [km/s]\", va=\"center\", size=12, rotation=90)\n\nsubplot(312)\nimshow(sqrt.(1f0./model_SGD)', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4)\nmisfit = round(fhistory_SGD[end])\ntext(0.15, 0.3, \"Gradient descent, misfit = $misfit\", size=14, color=\"black\")\nylabel(\"Depth [km]\", size=12)\ncolorbar()\ntext(10.35, 1.5, \"Velocity [km/s]\", va=\"center\", size=12, rotation=90)\n\nsubplot(313)\nimshow(sqrt.(1f0./model0.m)', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4)\nmisfit = round(fhistory_GN[end])\ntext(0.15, 0.3, \"Gauss–Newton, misfit = $misfit\", size=14, color=\"black\")\nxlabel(\"Lateral position [km]\", size=12)\nylabel(\"Depth [km]\", size=12)\ncolorbar()\ntext(10.35, 1.5, \"Velocity [km/s]\", va=\"center\", size=12, rotation=90)\n\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"fhistory_SGD = fhistory_SGD/norm(fhistory_SGD, Inf)\nfhistory_GN = fhistory_GN/norm(fhistory_GN, Inf);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"figure(figsize=(8,3))\nplot(1:10, fhistory_SGD, label=\"Gradient descent\")\nplot(1:10, fhistory_GN, label=\"Gauss–Newton\")\ntext(9.8, 0.28, \"Gradient descent\", ha=\"right\", color=\"steelblue\")\ntext(9.8, 0.09, \"Gauss–Newton\", ha=\"right\", color=\"chocolate\")\nxlabel(\"Iteration number\", size=12)\nylabel(\"Misfit\", size=12)\nxlim(1, 10)\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Figure 3: Normalized function values for the FWI inversion example with stochastic gradient descent and the Gauss-Newton method.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"An alternative to (Gauss–)Newton methods are quasi-Newton methods, which build up an approximation of the Hessian from previous gradients only and require no additional PDE solves or matrix inversions. Implementing an efficient and correct version of this method, such as the L-BFGS algorithm, exceeds a few lines of code and we therefore leave this exercise to the reader. Instead of implementing more complicated algorithms by hand, it is also possible to interface third-party Julia optimization libraries and an example for this is given in the notebook fwiexampleNLopt.ipynb.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Even though all examples shown here are two-dimensional, in order to make them reproducible on a laptop or desktop PC, JUDI can be used for 3D modeling and inversion without having to change the code, since the number of dimensions are automatically inferred from the velocity model and data dimensions.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Conclusions","page":"Full-Waveform Inversion - Part 3: optimization","title":"Conclusions","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"In this final part of our FWI tutorial series, we demonstrated how to set up basic optimization algorithms for waveform inversion using JUDI. The methods shown here are all gradient based and differ in the way how update directions for the velocity model are computed. Our numerical examples can serve for the reader as a basis for developing more advanced FWI workflows, which usually include additional data preprocessing, frequency continuation techniques or further model constraints.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Acknowledgments","page":"Full-Waveform Inversion - Part 3: optimization","title":"Acknowledgments","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This research was carried out as part of the SINBAD II project with the support of the member organizations of the SINBAD Consortium. This work was financially supported in part by EPSRC grant EP/L000407/1 and the Imperial College London Intel Parallel Computing Centre.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#References","page":"Full-Waveform Inversion - Part 3: optimization","title":"References","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Aminzadeh, F., Brac, J., and Kunz, T., 1997. 3D Salt and Overthrust models. SEG/EAGE Modeling Series, No. 1: Distribution CD of Salt and Overthrust models, SEG Book Series Tulsa, Oklahoma.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Bezanson, J., Karpinski, S., Shah, V. B., and Edelman, A., 2012, Julia: A fast dynamic language for technical computing: CoRR. Retrieved from http://arxiv.org/abs/1209.5145","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Bunks, C., Saleck, F. M., Zaleski, S., and Chavent, G., 1995, Multiscale seismic waveform inversion: GEOPHYSICS, 60, 1457–1473. doi:10.1190/1.1443880","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Johnson, S., 2017, Calling python functions from the julia language: GitHub repository. https://github.com/JuliaPy/PyCall.jl; GitHub.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Lange, M., Kukreja, N., Louboutin, M., Luporini, F., Zacarias, F. V., Pandolfo, V., … Gorman, G., 2016, Devito: Towards a generic finite difference DSL using symbolic python: 6th workshop on python for high-performance and scientific computing. doi:10.1109/PyHPC.2016.9","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Leeuwen, T. van, Aravkin, A. Y., Calandra, H., and Herrmann, F. J., 2013, In which domain should we measure the misfit for robust full waveform inversion? EAGE annual conference proceedings. doi:10.3997/2214-4609.20130839","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2017, Full-waveform inversion - part 1: Forward modeling: Retrieved from https://www.slim.eos.ubc.ca/Publications/Private/Submitted/2017/louboutin2017fwi/louboutin2017fwi.html","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2018, Full-waveform inversion - part 2: Adjoint modeling:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Nocedal, J., and Wright, S., 2009, Numerical optimization: (2nd ed.). Springer.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Paige, C. C., and Saunders, M. A., 1982, LSQR: An algorithm for sparse linear equations and sparse least squares: ACM Trans. Math. Softw., 8, 43–71. doi:10.1145/355984.355989","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Peters, B., and Herrmann, F. J., 2017, Constraints versus penalties for edge-preserving full-waveform inversion: The Leading Edge, 36, 94–100. doi:10.1190/tle36010094.1","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Warner, M., and Guasch, L., 2014, Adaptive waveform inversion: Theory: In SEG technical program expanded abstracts 2014 (pp. 1089–1093). doi:10.1190/segam2014-0371.1","category":"page"},{"location":"pysource/#pysource-package","page":"Devito backend reference","title":"pysource package","text":"","category":"section"},{"location":"pysource/#Submodules","page":"Devito backend reference","title":"Submodules","text":"","category":"section"},{"location":"pysource/#FD_utils-module","page":"Devito backend reference","title":"FD_utils module","text":"","category":"section"},{"location":"pysource/#FD*utils.R*mat(model)","page":"Devito backend reference","title":"FDutils.Rmat(model)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Rotation matrix according to tilt and asymut.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters: model (Model) – Model structure","category":"page"},{"location":"pysource/#FD*utils.divs(func,-so*fact1,-side-1)","page":"Devito backend reference","title":"FDutils.divs(func, sofact=1, side=-1)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"GrDivergenceadient shifted by half a grid point, only to be used in combination with grads.","category":"page"},{"location":"pysource/#FD*utils.grads(func,-so*fact1,-side1)","page":"Devito backend reference","title":"FDutils.grads(func, sofact=1, side=1)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Gradient shifted by half a grid point, only to be used in combination with divs.","category":"page"},{"location":"pysource/#FD_utils.laplacian(v,-irho)","page":"Devito backend reference","title":"FD_utils.laplacian(v, irho)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Laplacian with density div( 1/rho grad) (u)","category":"page"},{"location":"pysource/#FD*utils.sa*tti(u,-v,-model)","page":"Devito backend reference","title":"FDutils.satti(u, v, model)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Tensor factorized SSA TTI wave equation spatial derivatives.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction) – first TTI field\nv (TimeFunction) – second TTI field\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#FD*utils.thomsen*mat(model)","page":"Devito backend reference","title":"FDutils.thomsenmat(model)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Diagonal Matrices with Thomsen parameters for vectorial temporaries computation.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters: model (Model) – Model structure","category":"page"},{"location":"pysource/#checkpoint-module","page":"Devito backend reference","title":"checkpoint module","text":"","category":"section"},{"location":"pysource/#*class*-checkpoint.CheckpointOperator(op,-**kwargs)","page":"Devito backend reference","title":"class checkpoint.CheckpointOperator(op, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Devito’s concrete implementation of the ABC pyrevolve.Operator. This class wraps devito.Operator so it conforms to the pyRevolve API. pyRevolve will call apply with arguments tstart and tend. Devito calls these arguments ts and te so the following dict is used to perform the translations between different names.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nop (Operator) – devito.Operator object that this object will wrap.\nargs (dict) – If devito.Operator.apply() expects any arguments, they can be provided here to be cached. Any calls to CheckpointOperator.apply() will automatically include these cached arguments in the call to the underlying devito.Operator.apply().","category":"page"},{"location":"pysource/#apply(t*start,-t*end)","page":"Devito backend reference","title":"apply(tstart, tend)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"If the devito operator requires some extra arguments in the call to apply they can be stored in the args property of this object so pyRevolve calls pyRevolve.Operator.apply() without caring about these extra arguments while this method passes them on correctly to devito.Operator","category":"page"},{"location":"pysource/#t*arg*names-*-{'t*end':-'time*M',-'t*start':-'time*m'}*","page":"Devito backend reference","title":"targnames = {'tend': 'timeM', 'tstart': 'timem'}","text":"","category":"section"},{"location":"pysource/#*class*-checkpoint.DevitoCheckpoint(objects)","page":"Devito backend reference","title":"class checkpoint.DevitoCheckpoint(objects)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Devito’s concrete implementation of the Checkpoint abstract base class provided by pyRevolve. Holds a list of symbol objects that hold data.","category":"page"},{"location":"pysource/#*property*-dtype","page":"Devito backend reference","title":"property dtype","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"data type","category":"page"},{"location":"pysource/#get_data(timestep)","page":"Devito backend reference","title":"get_data(timestep)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"returns the data (wavefield) for the time-step timestep","category":"page"},{"location":"pysource/#get*data*location(timestep)","page":"Devito backend reference","title":"getdatalocation(timestep)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"returns the data (wavefield) for the time-step timestep","category":"page"},{"location":"pysource/#load()","page":"Devito backend reference","title":"load()","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"NotImplementedError","category":"page"},{"location":"pysource/#save()","page":"Devito backend reference","title":"save()","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"NotImplementedError","category":"page"},{"location":"pysource/#*property*-size","page":"Devito backend reference","title":"property size","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"The memory consumption of the data contained in a checkpoint.","category":"page"},{"location":"pysource/#checkpoint.get*symbol*data(symbol,-timestep)","page":"Devito backend reference","title":"checkpoint.getsymboldata(symbol, timestep)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Return the symbol corresponding to the data at time-step timestep","category":"page"},{"location":"pysource/#geom_utils-module","page":"Devito backend reference","title":"geom_utils module","text":"","category":"section"},{"location":"pysource/#geom*utils.geom*expr(model,-u,-src*coordsNone,-rec*coordsNone,-waveletNone,-fwTrue,-ntNone)","page":"Devito backend reference","title":"geomutils.geomexpr(model, u, srccoords=None, reccoords=None, wavelet=None, fw=True, nt=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Generates the source injection and receiver interpolation. This function is fully abstracted and does not care whether this is a forward or adjoint wave-equation. The source is the source term of the equation The receiver is the measurment term","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Therefore, for the adjoint, this function has to be called as: srcrec(model, v, srccoords=rec_coords, …) because the data is the sources","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or tuple) – Wavefield to inject into and read from\nsrc_coords (Array) – Physical coordinates of the sources\nrec_coords (Array) – Physical coordinates of the receivers\nwavelet (Array) – Data for the source\nfw=True – Whether the direction is forward or backward in time\nnt (int) – Number of time steps","category":"page"},{"location":"pysource/#geom*utils.src*rec(model,-u,-src*coordsNone,-rec*coordsNone,-waveletNone,-ntNone)","page":"Devito backend reference","title":"geomutils.srcrec(model, u, srccoords=None, reccoords=None, wavelet=None, nt=None)","text":"","category":"section"},{"location":"pysource/#interface-module","page":"Devito backend reference","title":"interface module","text":"","category":"section"},{"location":"pysource/#interface.J*adjoint(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-is*residualFalse,-checkpointingFalse,-n*checkpointsNone,-t*sub1,-return*objFalse,-freq*list[],-dft*subNone,-ic'as',-illumFalse,-wsNone,-f00.015,-born_fwdFalse,-nlindFalse,-misfitNone,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjoint(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, checkpointing=False, ncheckpoints=None, tsub=1, returnobj=False, freqlist=[], dftsub=None, ic='as', illum=False, ws=None, f0=0.015, born_fwd=False, nlind=False, misfit=None, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Supports three modes: * Checkpinting * Frequency compression (on-the-fly DFT) * Standard zero lag cross correlation over time","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\ncheckpointing (Bool) – Whether or not to use checkpointing\nn_checkpoints (Int) – Number of checkpoints for checkpointing\nmaxmem (Float) – Maximum memory to use for checkpointing\nfreq_list (List) – List of frequencies for on-the-fly DFT\ndft_sub (Int) – Subsampling factor for on-the-fly DFT\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.J*adjoint*checkpointing(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-is*residualFalse,-n*checkpointsNone,-born*fwdFalse,-return_objFalse,-ic'as',-wsNone,-nlindFalse,-f00.015,-misfitNone,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjointcheckpointing(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, ncheckpoints=None, bornfwd=False, return_obj=False, ic='as', ws=None, nlind=False, f0=0.015, misfit=None, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Checkpointing.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\ncheckpointing (Bool) – Whether or not to use checkpointing\nn_checkpoints (Int) – Number of checkpoints for checkpointing\nmaxmem (Float) – Maximum memory to use for checkpointing\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nis_residual (Bool) – Whether to treat the input as the residual or as the observed data\nborn_fwd (Bool) – Whether to use the forward or linearized forward modeling operator\nnlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.J*adjoint*freq(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-freq*list[],-is*residualFalse,-return*objFalse,-nlindFalse,-dft*subNone,-ic'as',-wsNone,-born*fwdFalse,-f00.015,-misfitNone,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjointfreq(model, srccoords, wavelet, reccoords, recin, spaceorder=8, freqlist=[], isresidual=False, returnobj=False, nlind=False, dftsub=None, ic='as', ws=None, bornfwd=False, f0=0.015, misfit=None, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Frequency compression (on-the-fly DFT).","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nfreq_list (List) – List of frequencies for on-the-fly DFT\ndft_sub (Int) – Subsampling factor for on-the-fly DFT\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nis_residual (Bool) – Whether to treat the input as the residual or as the observed data\nborn_fwd (Bool) – Whether to use the forward or linearized forward modeling operator\nnlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.J*adjoint*standard(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-is*residualFalse,-return*objFalse,-born*fwdFalse,-illumFalse,-ic'as',-wsNone,-t_sub1,-nlindFalse,-f00.015,-misfitNone,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjointstandard(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, returnobj=False, bornfwd=False, illum=False, ic='as', ws=None, t_sub=1, nlind=False, f0=0.015, misfit=None, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Adjoint Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with standard zero lag cross correlation over time.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nis_residual (Bool) – Whether to treat the input as the residual or as the observed data\nborn_fwd (Bool) – Whether to use the forward or linearized forward modeling operator\nnlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.adjoint*w(model,-rec*coords,-data,-wavelet,-space_order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.adjointw(model, reccoords, data, wavelet, space_order=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Adjoint/backward modeling of a shot record (receivers as source) for an extended source setup Pw*F^T*Pr^T*d_obs.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nrec_coords (Array) – Coordiantes of the receiver(s)\ndata (Array) – Shot gather\nwavelet (Array) – Time signature of the forward source for stacking along time\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: spatial distribution\nReturn type: Array","category":"page"},{"location":"pysource/#interface.born*rec(model,-src*coords,-wavelet,-rec*coords,-space*order8,-ic'as',-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.bornrec(model, srccoords, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Linearized (Born) modeling of a point source for a model perturbation (square slowness) dm.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.born*rec*w(model,-weight,-wavelet,-rec*coords,-space*order8,-ic'as',-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.bornrecw(model, weight, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Linearized (Born) modeling of an extended source for a model perturbation (square slowness) dm with an extended source","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nweight (Array) – Spatial distriubtion of the extended source\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*no*rec(model,-src*coords,-wavelet,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardnorec(model, srccoords, wavelet, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of a point source without receiver.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Wavefield\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*rec(model,-src*coords,-wavelet,-rec*coords,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardrec(model, srccoords, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Modeling of a point source with receivers Pr*F*Ps^T*q.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*rec*w(model,-weight,-wavelet,-rec*coords,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardrecw(model, weight, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of an extended source with receivers Pr*F*Pw^T*w","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nweights (Array) – Spatial distribution of the extended source.\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*wf*src(model,-u,-rec*coords,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardwfsrc(model, u, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of a full wavefield source Pr*F*u.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or Array) – Time-space dependent wavefield\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*wf*src*norec(model,-u,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardwfsrcnorec(model, u, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of a full wavefield source without receiver F*u.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or Array) – Time-space dependent wavefield\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Wavefield\nReturn type: Array","category":"page"},{"location":"pysource/#interface.wri*func(model,-src*coords,-wavelet,-rec*coords,-recin,-yin,-space*order8,-ic'as',-wsNone,-t*sub1,-grad'm',-grad*corrFalse,-alpha*opFalse,-w*funNone,-eps0,-freq_list[],-wfiltNone,-f00.015)","page":"Devito backend reference","title":"interface.wrifunc(model, srccoords, wavelet, reccoords, recin, yin, spaceorder=8, ic='as', ws=None, tsub=1, grad='m', gradcorr=False, alphaop=False, wfun=None, eps=0, freq_list=[], wfilt=None, f0=0.015)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Time domain wavefield reconstruction inversion wrapper","category":"page"},{"location":"pysource/#kernels-module","page":"Devito backend reference","title":"kernels module","text":"","category":"section"},{"location":"pysource/#kernels.SLS*2nd*order(model,-p,-fwTrue,-qNone,-f00.015)","page":"Devito backend reference","title":"kernels.SLS2ndorder(model, p, fw=True, q=None, f0=0.015)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Viscoacoustic 2nd SLS wave equation. https://library.seg.org/doi/10.1190/geo2013-0030.1","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Bulk modulus moved to rhs. The adjoint equation is directly derived as the discrete adjoint of the forward PDE which leads to a slightly different formulation than in the paper.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu1 (TimeFunction) – Pressure field\nu2 (TimeFunction) – Attenuation Memory variable\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)\nf0 (Peak frequency) – ","category":"page"},{"location":"pysource/#kernels.acoustic_kernel(model,-u,-fwTrue,-qNone)","page":"Devito backend reference","title":"kernels.acoustic_kernel(model, u, fw=True, q=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Acoustic wave equation time stepper","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or tuple) – wavefield (tuple if TTI)\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source","category":"page"},{"location":"pysource/#kernels.elastic_kernel(model,-v,-tau,-fwTrue,-qNone)","page":"Devito backend reference","title":"kernels.elastic_kernel(model, v, tau, fw=True, q=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Elastic wave equation time stepper","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nv (VectorTimeFunction) – Particle Velocity\ntau (TensorTimeFunction) – Stress tensor\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)","category":"page"},{"location":"pysource/#kernels.tti_kernel(model,-u1,-u2,-fwTrue,-qNone)","page":"Devito backend reference","title":"kernels.tti_kernel(model, u1, u2, fw=True, q=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"TTI wave equation time stepper","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu1 (TimeFunction) – First component (pseudo-P) of the wavefield\nu2 (TimeFunction) – First component (pseudo-P) of the wavefield\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)","category":"page"},{"location":"pysource/#kernels.wave_kernel(model,-u,-fwTrue,-qNone,-f00.015)","page":"Devito backend reference","title":"kernels.wave_kernel(model, u, fw=True, q=None, f0=0.015)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Pde kernel corresponding the the model for the input wavefield","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or tuple) – wavefield (tuple if TTI or Viscoacoustic)\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source\nf0 (Peak frequency) – ","category":"page"},{"location":"pysource/#models-module","page":"Devito backend reference","title":"models module","text":"","category":"section"},{"location":"pysource/#*class*-models.Model(origin,-spacing,-shape,-space_order8,-nbl40,-dtypeclass-'numpy.float32',-mNone,-epsilonNone,-deltaNone,-thetaNone,-phiNone,-rhoNone,-bNone,-qpNone,-lamNone,-muNone,-dmNone,-fsFalse,-**kwargs)","page":"Devito backend reference","title":"class models.Model(origin, spacing, shape, space_order=8, nbl=40, dtype=, m=None, epsilon=None, delta=None, theta=None, phi=None, rho=None, b=None, qp=None, lam=None, mu=None, dm=None, fs=False, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"The physical model used in seismic inversion : shape_pml = np.array(shape) + 2 * self.nbl processes.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\norigin (tuple of floats) – Origin of the model in m as a tuple in (x,y,z) order.\nspacing (tuple of floats) – Grid size in m as a Tuple in (x,y,z) order.\nshape (tuple of int) – Number of grid points size in (x,y,z) order.\nspace_order (int) – Order of the spatial stencil discretisation.\nm (array_like or float) – Squared slownes in s^2/km^2\nnbl (int , optional) – The number of absorbin layers for boundary damping.\ndtype (np.float32 or np.float64) – Defaults to 32.\nepsilon (array_like or float , optional) – Thomsen epsilon parameter (0 obj, adjoint_source","category":"page"},{"location":"pysource/#sensitivity.basic_src(model,-u,-**kwargs)","page":"Devito backend reference","title":"sensitivity.basic_src(model, u, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Basic source for linearized modeling","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Model containing the perturbation dm\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)","category":"page"},{"location":"pysource/#sensitivity.crosscorr*freq(u,-v,-model,-freqNone,-dft*subNone,-**kwargs)","page":"Devito backend reference","title":"sensitivity.crosscorrfreq(u, v, model, freq=None, dftsub=None, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Standard cross-correlation imaging condition with on-th-fly-dft","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure\nfreq (Array) – Array of frequencies for on-the-fly DFT\nfactor (int) – Subsampling factor for DFT","category":"page"},{"location":"pysource/#sensitivity.crosscorr_time(u,-v,-model,-**kwargs)","page":"Devito backend reference","title":"sensitivity.crosscorr_time(u, v, model, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Cross correlation of forward and adjoint wavefield","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#sensitivity.func_name(freqNone,-ic'as')","page":"Devito backend reference","title":"sensitivity.func_name(freq=None, ic='as')","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Get key for imaging condition/linearized source function","category":"page"},{"location":"pysource/#sensitivity.fwi_freq(*ar,-**kw)","page":"Devito backend reference","title":"sensitivity.fwi_freq(*ar, **kw)","text":"","category":"section"},{"location":"pysource/#sensitivity.fwi_src(*ar,-**kw)","page":"Devito backend reference","title":"sensitivity.fwi_src(*ar, **kw)","text":"","category":"section"},{"location":"pysource/#sensitivity.fwi_time(*ar,-**kw)","page":"Devito backend reference","title":"sensitivity.fwi_time(*ar, **kw)","text":"","category":"section"},{"location":"pysource/#sensitivity.grad*expr(gradm,-u,-v,-model,-wNone,-freqNone,-dft*subNone,-ic'as')","page":"Devito backend reference","title":"sensitivity.gradexpr(gradm, u, v, model, w=None, freq=None, dftsub=None, ic='as')","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Gradient update stencil","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure\nw (Float or Expr (**optional )) – Weight for the gradient expression (default=1)\nfreq (Array) – Array of frequencies for on-the-fly DFT\nfactor (int) – Subsampling factor for DFT\nisic (Bool) – Whether or not to use inverse scattering imaging condition (not supported yet)","category":"page"},{"location":"pysource/#sensitivity.inner_grad(u,-v)","page":"Devito backend reference","title":"sensitivity.inner_grad(u, v)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Inner product of the gradient of two Function.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Function) – First wavefield\nv (TimeFunction or Function) – Second wavefield","category":"page"},{"location":"pysource/#sensitivity.isic_freq(u,-v,-model,-**kwargs)","page":"Devito backend reference","title":"sensitivity.isic_freq(u, v, model, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Inverse scattering imaging condition","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#sensitivity.isic_src(model,-u,-**kwargs)","page":"Devito backend reference","title":"sensitivity.isic_src(model, u, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"ISIC source for linearized modeling","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Model containing the perturbation dm\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)","category":"page"},{"location":"pysource/#sensitivity.isic_time(u,-v,-model,-**kwargs)","page":"Devito backend reference","title":"sensitivity.isic_time(u, v, model, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Inverse scattering imaging condition","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#sensitivity.lin_src(model,-u,-ic'as')","page":"Devito backend reference","title":"sensitivity.lin_src(model, u, ic='as')","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Source for linearized modeling","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Model containing the perturbation dm\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nic (String) – Imaging condition of which we compute the linearized source","category":"page"},{"location":"pysource/#sources-module","page":"Devito backend reference","title":"sources module","text":"","category":"section"},{"location":"pysource/#*class*-sources.PointSource(*args)","page":"Devito backend reference","title":"class sources.PointSource(*args)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Symbolic data object for a set of sparse point sources","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nname (String) – Name of the symbol representing this source\ngrid (Grid) – Grid object defining the computational domain.\ncoordinates (Array) – Point coordinates for this source\ndata ( (**Optional ) Data) – values to initialise point data\nntime (Int (**Optional )) – Number of timesteps for which to allocate data\nnpoint (Int (**Optional )) – \nsource (Number of sparse points represented by this) – \ndimension (Dimension (**Optional )) – object for representing the number of points in this source\nNote – \nfully (either the dimensions ntime and npoint or the) – \nprovided. (initialised data array need to be) – ","category":"page"},{"location":"pysource/#default*assumptions-*-{'commutative':-True,-'complex':-True,-'extended*real':-True,-'finite':-True,-'hermitian':-True,-'imaginary':-False,-'infinite':-False,-'real':-True}*","page":"Devito backend reference","title":"defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*","text":"","category":"section"},{"location":"pysource/#is_commutative-*:-bool-None*-*-True*","page":"Devito backend reference","title":"is_commutative : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_complex-*-True*","page":"Devito backend reference","title":"is_complex = True","text":"","category":"section"},{"location":"pysource/#is*extended*real-*:-bool-None*-*-True*","page":"Devito backend reference","title":"isextendedreal : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_finite-*-True*","page":"Devito backend reference","title":"is_finite = True","text":"","category":"section"},{"location":"pysource/#is_hermitian-*-True*","page":"Devito backend reference","title":"is_hermitian = True","text":"","category":"section"},{"location":"pysource/#is_imaginary-*-False*","page":"Devito backend reference","title":"is_imaginary = False","text":"","category":"section"},{"location":"pysource/#is_infinite-*-False*","page":"Devito backend reference","title":"is_infinite = False","text":"","category":"section"},{"location":"pysource/#is_real-*:-bool-None*-*-True*","page":"Devito backend reference","title":"is_real : bool | None = True","text":"","category":"section"},{"location":"pysource/#sources.Receiver","page":"Devito backend reference","title":"sources.Receiver","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"alias of PointSource","category":"page"},{"location":"pysource/#*class*-sources.RickerSource(*args)","page":"Devito backend reference","title":"class sources.RickerSource(*args)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Symbolic object that encapsulate a set of sources with a pre-defined Ricker wavelet: http://subsurfwiki.org/wiki/Ricker_wavelet name: Name for the resulting symbol grid: Grid object defining the computational domain. f0: Peak frequency for Ricker wavelet in kHz time: Discretized values of time in ms","category":"page"},{"location":"pysource/#default*assumptions-*-{'commutative':-True,-'complex':-True,-'extended*real':-True,-'finite':-True,-'hermitian':-True,-'imaginary':-False,-'infinite':-False,-'real':-True}*-2","page":"Devito backend reference","title":"defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*","text":"","category":"section"},{"location":"pysource/#is_commutative-*:-bool-None*-*-True*-2","page":"Devito backend reference","title":"is_commutative : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_complex-*-True*-2","page":"Devito backend reference","title":"is_complex = True","text":"","category":"section"},{"location":"pysource/#is*extended*real-*:-bool-None*-*-True*-2","page":"Devito backend reference","title":"isextendedreal : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_finite-*-True*-2","page":"Devito backend reference","title":"is_finite = True","text":"","category":"section"},{"location":"pysource/#is_hermitian-*-True*-2","page":"Devito backend reference","title":"is_hermitian = True","text":"","category":"section"},{"location":"pysource/#is_imaginary-*-False*-2","page":"Devito backend reference","title":"is_imaginary = False","text":"","category":"section"},{"location":"pysource/#is_infinite-*-False*-2","page":"Devito backend reference","title":"is_infinite = False","text":"","category":"section"},{"location":"pysource/#is_real-*:-bool-None*-*-True*-2","page":"Devito backend reference","title":"is_real : bool | None = True","text":"","category":"section"},{"location":"pysource/#wavelet(timev)","page":"Devito backend reference","title":"wavelet(timev)","text":"","category":"section"},{"location":"pysource/#*class*-sources.TimeAxis(startNone,-stepNone,-numNone,-stopNone)","page":"Devito backend reference","title":"class sources.TimeAxis(start=None, step=None, num=None, stop=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Data object to store the TimeAxis. Exactly three of the four key arguments must be prescribed. Because of remainder values it is not possible to create a TimeAxis that exactly adhears to the inputs therefore start, stop, step and num values should be taken from the TimeAxis object rather than relying upon the input values. The four possible cases are: * start is None: start = step*(1 - num) + stop * step is None: step = (stop - start)/(num - 1) * num is None: num = ceil((stop - start + step)/step) and because of remainder stop = step*(num - 1) + start * stop is None: stop = step*(num - 1) + start","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nstart (float , optional) – Start of time axis.\nstep (float , optional) – Time interval.\nnum (int , optional) – Number of values (Note: this is the number of intervals + 1). Stop value is reset to correct for remainder.\nstop (float , optional) – End time.","category":"page"},{"location":"pysource/#time_values","page":"Devito backend reference","title":"time_values","text":"","category":"section"},{"location":"pysource/#wave_utils-module","page":"Devito backend reference","title":"wave_utils module","text":"","category":"section"},{"location":"pysource/#Module-contents","page":"Devito backend reference","title":"Module contents","text":"","category":"section"},{"location":"linear_operators/#Linear-Operators","page":"Linear Operators","title":"Linear Operators","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"JUDI is building on JOLI.jl to implement matrix-free linear operators. These operators represent the discretized wave-equations and sensitivit (Jacobian) for different acquisition schemes. ","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Pages = [\"linear_operators.md\"]","category":"page"},{"location":"linear_operators/#judiModeling","page":"Linear Operators","title":"judiModeling","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Seismic modeling operator for solving a wave equation for a given right-hand-side.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiModeling{DDT, RDT}","category":"page"},{"location":"linear_operators/#JUDI.judiModeling","page":"Linear Operators","title":"JUDI.judiModeling","text":"judiModeling(model; options=Options())\njudiModeling(model, src_geometry, rec_geometry; options=Options())\n\nCreate seismic modeling operator for a velocity model given as a Model structure. The function also takes the source and receiver geometries as additional input arguments, which creates a combined operator judiProjection*judiModeling*judiProjection'.\n\nExample\n\nPr and Ps are projection operatos of type judiProjection and q is a data vector of type judiVector: F = judiModeling(model) dobs = PrFPs'q F = judiModeling(model, q.geometry, rec_geometry) dobs = Fq\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construction:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator without source/receiver projections:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = judiModeling(model; options=opt)","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator with source/receiver projections:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = judiModeling(model, src_geometry, rec_geometry)","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator from an existing operator without geometries and projection operators:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = Pr*F*Ps'","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Ps and Pr are source/receiver projection operators of type judiProjection.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator for extended source modeling:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = Pr*F*Pw'","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Pw is a judiLRWF (low-rank-wavefield) projection operator.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Model structure\nF.model\n\n# Source injection (if available) and geometry\nF.qInjection\nF.qInjection.geometry\n\n# Receiver interpolation (if available) and geometry\nF.rInterpolation\nF.rInterpolation.geometry\n\n# Options structure\nF.options","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Forward modeling (F w/ geometries)\nd_obs = F*q\n\n# Adjoint modeling (F w/ geometries)\nq_ad = F'*d_obs\n\n# Forward modeling (F w/o geometries)\nd_obs = Pr*F*Ps'*q\n\n# Adjoint modelng (F w/o geometries)\nq_ad = Ps*F'*Pr'*d_obs\n\n# Extended source modeling (F w/o geometries)\nd_obs = Pr*F*Pw'*w\n\n# Adjoint extended source modeling (F w/o geometries)\nw_ad = Pw*F'*Pr'*d_obs\n\n# Forward modeling and return full wavefield (F w/o geometries)\nu = F*Ps'*q\n\n# Adjoint modelnig and return wavefield (F w/o geometries)\nv = F'*Pr'*d_obs\n\n# Forward modeling with full wavefield as source (F w/o geometries)\nd_obs = Pr*F*u\n\n# Adjoint modeling with full wavefield as source (F w/o geometries)\nq_ad = Ps*F*v","category":"page"},{"location":"linear_operators/#judiJacobian","page":"Linear Operators","title":"judiJacobian","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Jacobian of a non-linear forward modeling operator. Corresponds to linearized Born modeling (forward mode) and reverse-time migration (adjoint mode).","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiJacobian","category":"page"},{"location":"linear_operators/#JUDI.judiJacobian","page":"Linear Operators","title":"JUDI.judiJacobian","text":"judiJacobian(F,q)\n\nCreate a linearized modeling operator from the non-linear modeling operator F and the source q. q can be either a judiVector (point source Jacobian) or a judiWeight for extended source modeling. F is a full modeling operator including source/receiver projections.\n\nExamples\n\nF is a modeling operator without source/receiver projections: J = judiJacobian(PrFPs',q)\nF is the combined operator Pr*F*Ps': J = judiJacobian(F,q)\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construction:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"A judiJacobian operator can be create from an exisiting forward modeling operator and a source vector:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"J = judiJacobian(F, q) # F w/ geometries","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"J = judiJacobian(Pr*F*Ps', q) # F w/o geometries","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Ps and Pr are source/receiver projection operators of type judiProjection.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"A Jacobian can also be created for an extended source modeling operator:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"J = judiJacobian(Pr*F*Pw', w)","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Pw is a judiLRWF operator and w is a judiWeights vector (or 2D/3D Julia array).","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields::","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Model structure\nJ.model\n\n# Underlying propagator\nJ.F\n\n# Source injection (if available) and geometry throughpropagator\nJ.F.qInjection\nJ.F.qInjection.geometry\n\n# Receiver interpolation (if available) and geometry through propagator\nJ.F.rInterpolation\nJ.F.rInterpolation.geometry\n\n# Source term, can be a judiWeights, judiWavefield, or a judiVector\nJ.q\n\n# Options structure\nJ.options","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Linearized modeilng\nd_lin = J*dm\n\n# RTM\nrtm = J'*d_lin\n\n# Matrix-free normal operator\nH = J'*J","category":"page"},{"location":"linear_operators/#judiProjection","page":"Linear Operators","title":"judiProjection","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Abstract linear operator for source/receiver projections. A (transposed) judiProjection operator symbolically injects the data with which it is multiplied during modeling. If multiplied with a forward modeling operator, it samples the wavefield at the specified source/receiver locations.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiProjection","category":"page"},{"location":"linear_operators/#JUDI.judiProjection","page":"Linear Operators","title":"JUDI.judiProjection","text":"judiProjection(geometry)\n\nProjection operator for sources/receivers to restrict or inject data at specified locations.\n\nExamples\n\nF is a modeling operator of type judiModeling and q is a seismic source of type judiVector: Pr = judiProjection(rec_geometry) Ps = judiProjection(q.geometry) dobs = PrFPs'q qad = PsF'Pr'dobs\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Source/receiver geometry\nP.geometry","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Multiply with judiVector to create a judiRHS\nrhs1 = Pr'*d_obs\nrhs2 = Ps'*q\n\n# Sample wavefield at source/receiver location during modeling\nd_obs = Pr*F*Ps'*q\nq_ad = Ps*F*Pr'*d_obs","category":"page"},{"location":"linear_operators/#judiLRWF","page":"Linear Operators","title":"judiLRWF","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Abstract linear operator for sampling a seismic wavefield as a sum over all time steps, weighted by a time-varying wavelet. Its transpose injects a time-varying wavelet at every grid point in the model.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiWavelet","category":"page"},{"location":"linear_operators/#JUDI.judiWavelet","page":"Linear Operators","title":"JUDI.judiWavelet","text":"judiWavelet(dt, wavelet)\n\nLow-rank wavefield operator which injects a wavelet q at every point of the subsurface.\n\nExamples\n\nF is a modeling operator of type judiModeling and w is a weighting matrix of type judiWeights: Pr = judiProjection(recgeometry) Pw = judiWavelet(recgeometry.dt, q.data) # or judiLRWF(rec_geometry.dt, q.data) dobs = PrFPw'w dw = PwF'Pr'dobs\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Wavelet of i-th source location\nP.wavelet[i]","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Multiply with a judiWeight vector to create a judiExtendedSource\nex_src = Pw'*w\n\n# Sample wavefield as a sum over time, weighted by the source\nu_ex = Pw*F'*Pr'*d_obs","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Automatic-differentiation-with-JUDI","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"In this tutorial, we will look at the automatic differentiation in julia, and in particular how ChainRules.jl supports addition of your own differentiation rules into Julia's Automatic Differentiation system. This allows for seamless integration between your code, including its handcoded derivatives, and Julia's native AD systems, e.g. those used by Flux Julia's machine learning platform. We use ChainRules.jl to automatically differentiate codes that involve complex operations implemented by JUDI.jl (this example) and JOLI.jl.","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Introduction-to-chain-rules","page":"Automatic differentiation with JUDI","title":"Introduction to chain rules","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We first provide a brief introduction to automatic differentiation and to rrule, the ChainRules.jl interface used to define custom AD rules. With this rules defined, we show how we can use generic AD frameworks (Zygote/Flux in this tutorial) to compute gradients of complicated expression swaping in our own rule for part of the expression.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using Flux, ChainRulesCore, LinearAlgebra, JOLI","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Simple-example","page":"Automatic differentiation with JUDI","title":"Simple example","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Let's consider a simple example with a basic differentiable function x - cos(x) + 1","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"mycos(x) = cos(x) + 1","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"mycos (generic function with 1 method)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Now we know that the derivative of this function is x - -sin(x) from standard functional analysis.e can therefore define through chainrules a new rule for our function f","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"function ChainRulesCore.rrule(::typeof(mycos), x)\n println(\"Using custom AD rule for mycos\")\n y = mycos(x)\n pullback(Δy) = (NoTangent(), -sin(x)*Δy)\n return y, pullback\nend","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We now have the rule to compute the directional derivative of our function mycos . Let's check the gradient","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"x0 = randn()\n# Standard AD of cos\ng1 = gradient(x->norm(cos(x)+1)^2, x0);\n# Our definition\ng2 = gradient(x->norm(mycos(x))^2, x0);\n# Analytical gradient\ng3 = -2*sin(x0)*(mycos(x0));\nprintln(\"True gradient: $(g3) \\nStandard AD : $(g1[1]) \\nCustom AD : $(g2[1])\")","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Using custom AD rule for mycos\nTrue gradient: -1.1650215811256202 \nStandard AD : -1.1650215811256202 \nCustom AD : -1.1650215811256202","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"And we see that we get the correct gradient. Now this is an extremely simple case, now let's look at a more complicated case where we define the AD rule for matrix-free operators defined in JOLI.","category":"page"},{"location":"tutorials/06_automatic_differentiation/#JOLI","page":"Automatic differentiation with JUDI","title":"JOLI","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We look at how we define automatic differentiation rules involding matrix-free linear operator. We consider operations of the form A*x where A is a JOLI matrix-free linear operator and we differentiate with respect to the input x","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"In JOLI, the base type of our linear operator is joAbstractLinearOperator. If we define the rule for this abstract type, all linear operator should follow. Now in this case the acual operation to be differentiated is the multiplication * with two inputs (A, x). Because we consider A to be fixed,its derivative will be defined as NoTangent that is ChainRules's way to state there is no derivative for this input.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"
      NOTE These rules are implemented inside JOLI and are merely implemented here as an illustration. JOLI operators are usable with FLux/Zygote by default and with any Julia ML framework implemented AD through ChainRules.jl
      ","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using JOLI","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"function ChainRulesCore.rrule(::typeof(*), A::T, x) where {T<:joAbstractLinearOperator}\n y = A*x\n pullback(Δy) = (NoTangent(), NoTangent(), A'*Δy)\n return y, pullback\nend","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"With this rule defined we can now use a JOLI operator. Let's solve a simple data fitting problem with a restricted Fourier measuerment","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using Random\nN = 128\n# Fourier transform as a linear operator\nF = joDFT(N)\n# Restriction\nR = joRomberg(N; DDT=Complex{Float64}, RDT=Complex{Float64})\n# Combine the operators\nA = R*F;","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Make data\nx = randn(128)\nb = A*x;","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Let's create a loss function","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"loss(x) = .5f0*norm(A*x - b)^2 + .5f0*norm(x, 2)^2","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"loss (generic function with 1 method)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We can now easily obtain the gradient at any given x since the only undefined part would have been the JOLI operator that now has its own differentiation rule","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"x0 = randn(128)\ng_ad = gradient(loss, x0)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"([3.202332642059349, 3.3474396077773836, 1.7192577704407273, 1.091738893925904, 2.99497941149576, 0.033555000810577607, 2.1856165385946493, -1.9623327339375567, 1.6305386659659917, 0.49094575011054564 … 1.498782430471393, 3.979368223594536, 2.37224960018878, -1.7065306486883383, 0.2043370108057428, -1.1826425144324078, 1.76641366139114, -3.0095898127121723, 3.064337151574949, 5.76008767012525],)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Once again, we can compare to the know analytical gradient","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"g_hand = A'*(A*x0 - b) + x0;\nerr = norm(g_hand - g_ad[1])","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"6.845562607034591e-15","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"And we get the exact gradient without the AD system needing to know what A computes but using the prededined rule for A*x","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Optimization","page":"Automatic differentiation with JUDI","title":"Optimization","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Let's now slve the problem above with standard gradient descent","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using Optim","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"δloss!(g, x) = begin g.=gradient(loss, x)[1]; return loss(x) end;","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"summary = optimize(loss, δloss!, randn(N), ConjugateGradient(),\n Optim.Options(g_tol = 1e-12, iterations = 200, store_trace = true, show_trace = true, show_every=1))","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Iter Function value Gradient norm \n 0 2.078016e+02 7.110069e+00\n * time: 0.008176088333129883\n 1 2.996845e+01 3.191891e-15\n * time: 0.32667112350463867\n\n\n\n\n\n * Status: success\n\n * Candidate solution\n Final objective value: 2.996845e+01\n\n * Found with\n Algorithm: Conjugate Gradient\n\n * Convergence measures\n |x - x'| = 3.56e+00 ≰ 0.0e+00\n |x - x'|/|x'| = 2.52e+00 ≰ 0.0e+00\n |f(x) - f(x')| = 1.78e+02 ≰ 0.0e+00\n |f(x) - f(x')|/|f(x')| = 5.93e+00 ≰ 0.0e+00\n |g(x)| = 3.19e-15 ≤ 1.0e-12\n\n * Work counters\n Seconds run: 0 (vs limit Inf)\n Iterations: 1\n f(x) calls: 3\n ∇f(x) calls: 2","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using PyPlot\nplot(x, label=\"true\")\nplot(summary.minimizer, label=\"Recovered\")\nlegend()","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"PyObject ","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Automatic-differentiation-for-JUDI","page":"Automatic differentiation with JUDI","title":"Automatic differentiation for JUDI","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"With this introductory example, we have seen how to define simple derivative reverse rules. However, seismic inversion tend to rely and much more complicated operator such as the discrete wave-equation and its non-linear dependence to the velocity. While implementing a pure native-julia propagator using simple artithmetic operations easy to differentiate would be possible, this would limit the control user have on crtitical pieces such as the imaging condition and the memory management for the forward wavefield. Consequently, most seismic inversion framework a very carefully implemented but do not necessarly allow for plug-and-play with external framework. This incompatibility makes the integration of modern machine learning algorithms extremely complciated, if feasible at all, with these legacy software.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"In JUDI, we made design choice from the beginning of high level abstractions and separation of concern that allow easy extension. In the following, we will demonstrate how JUDI can be integrated with machine learning algorithm trivially thanks to the definition of the core rules for adjoint state problem. More specifically, JUDI implements the rule for the following derivatives:","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"fracd mathbfF * mathbfqd mathbfq\nwhere mathbfF = mathbfP_r mathbfA^s mathbfP_s^T is a forward (s=-1) or adjoint (s=-*) propagator. JUDI supports numerous cases including full wavefield modelling (mathbfP_s=mathbfP_r=mathcalI, stanrad point source and point receivers, and extendend source modeling. \nfracd mathbfF * mathbfqd mathbfm\nwhere mathbfF is a forward (s=-1) or adjoint (s=-*) propagator. This effectively allow for FWI with any chosen misfit function rho_mathbfm(mathbfF * mathbfq mathbfq)\nfracd mathbfJ * mathbfdmd mathbfdm\nwhere mathbfJ is the standard FWI/RTM jacobian of the forward operator mathbfF\nfracd mathbfJ(mathbfq) * mathbfdmd mathbfq\nwhere once again mathbfJ is the standard FWI/RTM jacobian of the forward operator and mathbfq is the source of the forward modeling operator","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"With all these derivatives predefine, we can easily let the implementation of the propagators and Jacobian handle high performance kernels (via Devito), advanced imaging condition and efficient memory mamangement. From these rules, the AD framework will only call the propagation kernels implemented in JUDI and integrate it as part of the chain of differentiation. ","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We now illustrate these capabilities on a few trivial example that show the flexibiluty of our inversion framework.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using JUDI, Flux","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using SlimPlotting\n# Set up model structure\nn = (120, 100) # (x,y,z) or (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32,n) .+ 0.4f0\nv0 = ones(Float32,n) .+ 0.4f0\nv[:, Int(round(end/2)):end] .= 4f0\n\n# Slowness squared [s^2/km^2]\nm = (1f0 ./ v).^2\nm0 = (1f0 ./ v0).^2\ndm = vec(m - m0);# Lets get some simple default parameter","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"plot_velocity(v', d; cbar=true)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Setup model structure\nnsrc = 1\t# number of sources\nmodel0 = Model(n, d, o, m0)\n\n# Set up receiver geometry\nnxrec = 120\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = 0f0\nzrec = range(50f0, stop=50f0, length=nxrec)\n\n# receiver sampling and recording time\ntime = 1000f0 # receiver recording time [ms]\ndt = 1f0 # receiver sampling interval [ms]\n\n# Set up receiver structure\nrecGeometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)\n\n## Set up source geometry (cell array with source locations for each shot)\nxsrc = convertToCell([600f0])\nysrc = convertToCell([0f0])\nzsrc = convertToCell([20f0])\n\n# Set up source structure\nsrcGeometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"GeometryIC{Float32} wiht 1 sources","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"\n# setup wavelet\nf0 = 0.01f0 # MHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(srcGeometry, wavelet)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 1 sources","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Return-type","page":"Automatic differentiation with JUDI","title":"Return type","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Whule JUDI defines its own dimensional types, it is recommended to drop the metadata and return pure array/tensors for ML. This can be done with a simple option passed to the propagators","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"opt = Options(return_array=true)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"JUDIOptions(8, false, false, 1000.0f0, false, \"\", \"shot\", false, false, Any[], \"as\", 1, 1, true, nothing, 0.015f0)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"F0 = judiModeling(model0, srcGeometry, recGeometry; options=opt)\nnum_samples = recGeometry.nt[1] * nxrec # Number of value","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"120120","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"##################################################################################\n# Fully connected neural network with linearized modeling operator\nn_in = 100\nn_out = 10\n\nW1 = randn(Float32, prod(model0.n), n_in)\nb1 = randn(Float32, prod(model0.n))\n\nW2 = judiJacobian(F0, q)\nb2 = randn(Float32, num_samples)\n\nW3 = randn(Float32, n_out, num_samples)\nb3 = randn(Float32, n_out);\n","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.n, use size(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"function network(x)\n x = W1*x .+ b1\n x = vec(W2*x) .+ b2\n x = W3*x .+ b3\n return x\nend","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"network (generic function with 1 method)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Inputs and target\nx = zeros(Float32, n_in)\ny = randn(Float32, n_out);","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Evaluate MSE loss\nloss(x, y) = Flux.mse(network(x), y)\n","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"loss (generic function with 2 methods)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Compute gradient w.r.t. x and y\nΔx, Δy = gradient(loss, x, y)\n","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Building born operator\nOperator `born` ran in 0.04 s\nBuilding forward operator\nOperator `forward` ran in 0.03 s\nBuilding adjoint born operator\nOperator `gradient` ran in 0.03 s\nOperator `forward` ran in 0.26 s\nOperator `gradient` ran in 0.03 s\n\n\n\n\n\n(Float32[537865.2, -881569.94, 479238.75, 193671.75, 785871.56, 170005.88, -387432.75, -84344.25, -662277.6, 475783.38 … 910177.75, 988473.4, 249698.53, -737089.44, 211155.11, -397048.3, 421428.7, 94724.03, -94981.22, -508586.4], Float32[102.665306, 173.1151, -374.61276, -112.29274, -36.785393, 124.619865, -138.68846, -12.379652, 76.64807, -25.363287])","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"And we can see that the underlying JUDI propagators were called propetly.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Compute gradient for x, y and weights (except for W2)\np = Flux.params(x, y, W1, b1, b2, W3, b3)\ngs = gradient(() -> loss(x, y), p)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Operator `born` ran in 0.28 s\nOperator `forward` ran in 0.26 s\nOperator `gradient` ran in 0.03 s\nOperator `forward` ran in 0.04 s\nOperator `gradient` ran in 0.22 s\n\n\n\n\n\nGrads(...)","category":"page"},{"location":"basics/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"These tutorials provide instructions of how to set up various modeling or inversion scenarios with JUDI. For a list of runnable Julia scripts and reproducable research, please also check out the examples:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The examples scripts contain simple modeling and inversion examples such as FWI, LSRTM, and medical modeling.\nThe machine-learning scripts contain examples of machine learning using Flux.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Pages = [\"tutorials.md\"]","category":"page"},{"location":"basics/#D-Modeling-Quickstart","page":"Getting Started","title":"2D Modeling Quickstart","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To set up a simple 2D modeling experiment with JUDI with an OBN-type acquisition (receivers everywhere), we start by loading the module and building a two layer model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100) # (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, 50:end] .= 5f0\n\n# Squared slowness\nm = (1f0 ./ v).^2","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For working with JUDI operators, we need to set up a model structure, which contains the grid information, as well as the slowness. Optionally, we can provide an array of the density in g/cm^3 (by default a density of 1 is used):","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Density (optional)\nrho = ones(Float32, n)\n\n# Model structure:\nmodel = Model(n, d, o, m; rho=rho)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we define our source acquisition geometry, which needs to be defined as a Geometry structure. The Geometry function requires the x-, y- and z-coordinates of the source locations as input, as well as the modeling time and samping interval of the wavelet. In general, each parameter can be passed as a cell array, where each cell entry provides the information for the respective source location. The helper function convertToCell converts a Julia range to a cell array, which makes defining the source geometry easier:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up source geometry\nnsrc = 4 # no. of sources\nxsrc = convertToCell(range(400f0, stop=800f0, length=nsrc))\nysrc = convertToCell(range(0f0, stop=0f0, length=nsrc))\nzsrc = convertToCell(range(20f0, stop=20f0, length=nsrc))\n\n# Modeling time and sampling interval\ntime = 1000f0 # ms\ndt = 2f0 # ms\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Now we can define our source wavelet. The source must be defined as a judiVector, which takes the source geometry, as well as the source data (i.e. the wavelet) as an input argument:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"In general, wavelet can be a cell array with a different wavelet in each cell, i.e. for every source location. Here, we want to use the same wavelet for all 4 source experiments, so we can simply pass a single vector. As we already specified in our src_geometry object that we want to have 4 source locations, judiVector will automaticallty copy the wavelet for every experiment.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we set up the receiver acquisition geometry. Here, we define an OBN acquisition, where the receivers are spread out over the entire domain and each source experiment uses the same set of receivers. Again, we can in principle pass the coordinates as cell arrays, with one cell per source location. Since we want to use the same geometry for every source, we can use a short cut and define the coordinates as Julia ranges and pass nsrc=nsrc as an optional argument to the Geometry function. This tells the function that we want to use our receiver set up for nsrc distinct source experiments:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up receiver geometry (for 2D, set yrec to zero)\nnxrec = 120\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = 0f0\nzrec = range(50f0, stop=50f0, length=nxrec)\n\n# Set up receiver structure\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we can define separate operators for source/receiver projections and a forward modeling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nPr = judiProjection(rec_geometry)\nA_inv = judiModeling(model)\nPs = judiProjection(src_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can see, that from JUDI's perspective, source and receivers are treated equally and are represented by the same operators (judiProjection) and vectors (judiVector).","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We also could've skipped setting up the projection operators and directly created:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"F = judiModeling(model, src_geometry, rec_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"which is equivalent to creating the combined operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"F = Pr*A_inv*Ps'","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Finally, to model our seismic data, we run:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"d_obs = Pr*A_inv*Ps'*q\n# or\nd_obs = F*q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can plot a 2D shot record by accessing the .data field of the judiVector, which contains the data in the original (non-vectorized) dimensions:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using PyPlot\nimshow(d_obs.data[1], vmin=-5, vmax=5, cmap=\"seismic\", aspect=\"auto\")","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can also set up a Jacobian operator for Born modeling and reverse-time migration. First we set up a (constant) migration velocity model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"v0 = ones(Float32, n) .* 1.4f0\nm0 = (1f0 ./ v0).^2\ndm = m - m0 # model perturbation/image\n\n# Model structure\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can create the Jacobian directly from a (non-linear) modeling operator and a source vector:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"A0_inv = judiModeling(model0) # modeling operator for migration velocity\nJ = judiJacobian(Pr*A0_inv*Ps', q)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can use this operator to model single scattered data, as well as for migration our previous data:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"d_lin = J*vec(dm)\n\n# RTM\nrtm = J'*d_obs","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To plot, first reshape the image:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"rtm = reshape(rtm, model0.n)\nimshow(rtm', cmap=\"gray\", vmin=-1e3, vmax=1e3)","category":"page"},{"location":"basics/#D-Modeling-Quickstart-2","page":"Getting Started","title":"3D Modeling Quickstart","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Setting up a 3D experiment largely follows the instructions for the 2D example. Instead of a 2D model, we define our velocity model as:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100, 80) # (x,y,z)\nd = (10., 10., 10.)\no = (0., 0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, :, 40:end] .= 5f0\n\n# Squared slowness and model structure\nm = (1f0 ./ v).^2\nmodel = Model(n, d, o, m)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Our source coordinates now also need to have the y-coordinate defined:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up source geometry\nnsrc = 4 # no. of sources\nxsrc = convertToCell(range(400f0, stop=800f0, length=nsrc))\nysrc = convertToCell(range(200f0, stop=1000f0, length=nsrc))\nzsrc = convertToCell(range(20f0, stop=20f0, length=nsrc))\n\n# Modeling time and sampling interval\ntime = 1000f0 # ms\ndt = 2f0 # ms\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Our source wavelet, is set up as in the 2D case:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For the receivers, we generally need to define each coordinate (x, y, z) for every receiver. I.e. xrec, yrec and zrec each have the length of the total number of receivers. However, oftentimes we are interested in a regular receiver grid, which can be defined by two basis vectors and a constant depth value for all receivers. We can then use the setup_3D_grid helper function to create the full set of coordinates:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Receiver geometry\nnxrec = 120\nnyrec = 100\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = range(100f0, stop=900f0, length=nyrec)\nzrec = 50f0\n\n# Construct 3D grid from basis vectors\n(xrec, yrec, zrec) = setup_3D_grid(xrec, yrec, zrec)\n\n# Set up receiver structure\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Setting up the modeling operators is done as in the previous 2D case:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nPr = judiProjection(rec_geometry)\nA_inv = judiModeling(model)\nPs = judiProjection(src_geometry)\n\n# Model data\nd_obs = Pr*A_inv*Ps'*q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The 3D shot records are still saved as 2D arrays of dimensions time x (nxrec*nyrec):","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using PyPlot\nimshow(d_obs.data[1], vmin=-.4, vmax=.4, cmap=\"seismic\", aspect=\"auto\")","category":"page"},{"location":"basics/#Vertical-and-tilted-transverse-isotropic-modeling-(VTI,-TTI)","page":"Getting Started","title":"Vertical and tilted-transverse isotropic modeling (VTI, TTI)","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports both VTI and TTI modeling based on a coupled pseudo-acoustic wave equation. To enable VTI/TTI modeling, simply pass Thomsen parameters as well as the tilt angles to the Model structure as optional keyword arguments:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Grid and model\nn = (120, 100, 80)\nd = (10., 10., 10)\no = (0., 0., 0.)\n\n# Velocity\nv = ones(Float32, n) .* 1.5f0\nm = 1f0 ./ v.^2\n\n# Thomsen parameters\nepsilon = ones(Float32, n) .* 0.2f0\ndelta = ones(Float32, n) .* 0.1f0\n\n# Tile angles for TTI\ntheta = ones(Float32, n) .* pi/2f0\nphi = ones(Float32, n) .* pi/3f0 # 3D only\n\n# Set up model structure with Thomsen parameters\nmodel = Model(n, d, o, m; rho=rho, epsilon=epsilon, delta=delta, theta=theta, delta=delta)","category":"page"},{"location":"basics/#Modeling-with-density","page":"Getting Started","title":"Modeling with density","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To use density, pass rho in the units of [g/cm^3] as an optional keyword argument to the Model structure. The default density is rho=1f0 (i.e. density of water):","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Grid and model\nn = (120, 100)\nd = (10., 10.)\no = (0., 0.)\nv = ones(Float32, n) .* 1.5f0\nm = 1f0 ./ v.^2\nrho = ones(Float32, n) .* 1.1f0\n\n# Set up model structure with density\nmodel = Model(n, d, o, m; rho=rho)","category":"page"},{"location":"basics/#D-Marine-streamer-acquisition","page":"Getting Started","title":"2D Marine streamer acquisition","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For a marine streamer acquisition, we need to define a moving set of receivers representing a streamer that is towed behind a seismic source vessel. In JUDI, this is easily done by defining a different set of receivers for each source location. Here, we explain how to set up the Geometry objects for a 2D marine streamer acquisition.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"If we define that our streamer is to the right side of the source vessel, this has the effect that part of the streamer is outside the grid while our vessel is in the right side of the model. To circumvent this, we can say that our streamer is on the right side of the source while the vessel is in the left-hand side of the model and vice versa. This way, we get the full maximum offset coverage for every source location (assuming that the maximum offset is less or equal than half the domain size). ","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"First, we have to specify our domain size (the physical extent of our model), as well as the number of receivers and the minimum and maximum offset:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"domain_x = (model.n[1] - 1)*model.d[1] # horizontal extent of model\nnrec = 120 # no. of receivers\nxmin = 50f0 # leave buffer zone w/o source and receivers of this size\nxmax = domain_x - 50f0\nmin_offset = 10f0 # distance between source and first receiver\nmax_offset = 400f0 # distance between source and last\nxmid = domain_x / 2 # midpoint of model\nsource_spacing = 25f0 # source interval [m]","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For the JUDI Geometry objects, we need to create cell arrays for the source and receiver coordinates, with one cell entry per source location:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source/receivers\nnsrc = 20 # number of shot locations\n\n# Receiver coordinates\nxrec = Array{Any}(undef, nsrc)\nyrec = Array{Any}(undef, nsrc)\nzrec = Array{Any}(undef, nsrc)\n\n# Source coordinates\nxsrc = Array{Any}(undef, nsrc)\nysrc = Array{Any}(undef, nsrc)\nzsrc = Array{Any}(undef, nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we compute the source and receiver coordinates for when the vessel moves from left to right in the right-hand side of the model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Vessel goes from left to right in right-hand side of model\nnsrc_half = Int(nsrc/2)\nfor j=1:nsrc_half\n xloc = xmid + (j-1)*source_spacing\n\n # Current receiver locations\n xrec[j] = range(xloc - max_offset, xloc - min_offset, length=nrec)\n yrec[j] = 0.\n zrec[j] = range(50f0, 50f0, length=nrec)\n \n # Current source\n xsrc[j] = xloc\n ysrc[j] = 0f0\n zsrc[j] = 20f0\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Then, we repeat this for the case where the vessel goes from right to left in the left-hand model side:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Vessel goes from right to left in left-hand side of model\nfor j=1:nsrc_half\n xloc = xmid - (j-1)*source_spacing\n \n # Current receiver locations\n xrec[nsrc_half + j] = range(xloc + min_offset, xloc + max_offset, length=nrec)\n yrec[nsrc_half + j] = 0f0\n zrec[nsrc_half + j] = range(50f0, 50f0, length=nrec)\n \n # Current source\n xsrc[nsrc_half + j] = xloc\n ysrc[nsrc_half + j] = 0f0\n zsrc[nsrc_half + j] = 20f0\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Finally, we can set the modeling time and sampling interval and create the Geometry objects:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# receiver sampling and recording time\ntime = 10000f0 # receiver recording time [ms]\ndt = 4f0 # receiver sampling interval\n\n# Set geometry objects\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time)\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"You can find a full (reproducable) example for generating a marine streamer data set for the Sigsbee 2A model here.","category":"page"},{"location":"basics/#Simultaneous-sources","page":"Getting Started","title":"Simultaneous sources","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To set up a simultaneous source with JUDI, we first create a cell array with nsrc cells, where nsrc is the number of separate experiments (here nsrc=1). For a simultaneous source, we create an array of source coordinates for each cell entry. In fact, this is exactly like setting up the receiver geometry, in which case we define multiple receivers per shot location. Here, we define a single experiment with a simultaneous source consisting of four sources:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"nsrc = 1 # single simultaneous source\nxsrc = Vector{Float32}(undef, nsrc)\nysrc = Vector{Float32}(undef, nsrc)\nzsrc = Vector{Float32}(undef, nsrc)\n\n# Set up source geometry\nxsrc[1] = [250f0, 500f0, 750f0, 1000f0] # four simultaneous sources\nysrc[1] = 0f0\nzsrc[1] = [50f0, 50f0, 50f0, 50f0]\t\n\n# Source sampling and number of time steps\ntime = 2000f0\ndt = 4f0\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"With the simultaneous source geometry in place, we can now create our simultaneous data. As we have four sources per sim. source, we create an array of dimensions 4 x src_geometry.nt[1] and fill it with wavelets of different time shifts:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Create wavelet\nf0 = 0.01\t# source peak frequencies\nq = ricker_wavelet(500f0, dt, f0) # 500 ms wavelet\n\n# Create array with different time shifts of the wavelet\nwavelet = zeros(Float32, 4, src_geometry.nt[1])\nwavelet[1, 1:1+length(q)-1] = q\nwavelet[2, 41:41+length(q)-1] = q\nwavelet[3, 121:121+length(q)-1] = q\nwavelet[4, 201:201+length(q)-1] = q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Finally, we create our simultaneous source as a judiVector:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/#Computational-simultaneous-sources-(super-shots)","page":"Getting Started","title":"Computational simultaneous sources (super shots)","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The computational simultaneous sources refer to superposition of sequentially-fired sources and shot records from the field. These computational simultaneous shot records are not acquired in the field simultaneously, but computational simultaneous sources introduce randomness to the optimization problems like FWI and LS-RTM, which takes advantage of the knowledge in randomized linear algebra to make optimization faster and more robust.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The implementation of computational simultaneous sources follows the journal article Fast randomized full-waveform inversion with compressive sensing The simultaneous sources experiments are generated by superposition of shot records with random weights drawn from Gaussian distribution.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# assume dobs is generated by sequentially fired point sources q\nnsimsrc = 8\n# Set up random weights\nweights = randn(Float32,nsimsrc,q.nsrc)\n# Create SimSource\nq_sim = weights * q\ndata_sim = weights * dobs\n# We can also apply the weights to the operator and check the equivalence\nd_sim = (weights * F) * q_sim\nisapprox(data_sim, d_sim)","category":"page"},{"location":"basics/#Working-with-wavefields","page":"Getting Started","title":"Working with wavefields","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI allows computing full time domain wavefields and using them as right-hand sides for wave equations solves. This tutorial shows how. We start by setting up a basic 2D experiment:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100) # (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, 50:end] .= 5f0\n\n# Squared slowness\nm = (1f0 ./ v).^2\n\n# Model structure:\nmodel = Model(n, d, o, m)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we set up the source geometry for a single source experiment:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up source geometry\nnsrc = 1 # no. of sources\nxsrc = convertToCell([600f0])\nysrc = convertToCell([0f0])\nzsrc = convertToCell([20f0])\n\n# Modeling time and sampling interval\ntime = 600f0 # ms\ndt = 4f0 # ms\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)\n\n# Source wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As in the 2D quick start tutorial, we create our modeling operator and source projection operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nA_inv = judiModeling(model)\nPs = judiProjection(src_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To model a wavefield, we simply omit the receiver sampling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"u = A_inv*Ps'*q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"This return an abstract data vector called judiWavefield. Similar to judiVectors, we can access the data for each source number i via u.data[i]. The data is a 3D array of size (nt, nx, nz) for 2D and a 4D array of size (nt, nx, ny, nz) for 3D. We can plot the wavefield of the 600th time step with:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using PyPlot\nimshow(u.data[1][600, :, :]', vmin=-5, vmax=5, cmap=\"seismic\", aspect=\"auto\")","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can also use the computed wavefield u as a right-hand side for forward and adjoint wave equation solves:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"v = A_inv*u\nw = A_inv'*u","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Similarly, by setting up a receiver projection operator, we can use wavefields as right-hand sides, but restrict the output to the receiver locations.","category":"page"},{"location":"basics/#Extended-source-modeling","page":"Getting Started","title":"Extended source modeling","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports extened source modeling, which injects a 1D wavelet q at every point in the subsurface weighted by a spatially varying extended source. To demonstrate extended source modeling, we first set up a runnable 2D experiment with JUDI. We start with defining the model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100) # (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, 50:end] .= 5f0\n\n# Squared slowness\nm = (1f0 ./ v).^2\n\n# Model structure:\nmodel = Model(n, d, o, m)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we set up the receiver geometry:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Number of experiments\nnsrc = 2\n\n# Set up receiver geometry\nnxrec = 120\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = 0f0\nzrec = range(50f0, stop=50f0, length=nxrec)\n\n# Modeling time and receiver sampling interval\ntime = 2000\ndt = 4\n\n# Set up receiver structure\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For the extended source, we do not need to set up a source geometry object, but we need to define a wavelet function:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nf0 = 0.01f0 # MHz\nwavelet = ricker_wavelet(time, dt, f0)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As before, we set up a modeling operator and a receiver sampling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nA_inv = judiModeling(model)\nPr = judiProjection(rec_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We define our extended source as a so called judiWeights vector. Similar to a judiVector, the data of this abstract vector is stored as a cell array, where each cell corresponds to one source experiment. We create a cell array of length two and create a random array of the size of the model as our extended source:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"weights = Array{Array}(undef, nsrc)\nfor j=1:nsrc\n weights[j] = randn(Float32, model.n)\nend\nw = judiWeights(weights)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To inject the extended source into the model and weight it by the wavelet, we create a special projection operator called judiLRWF (for JUDI low-rank wavefield). This operator needs to know the wavelet we defined earlier. We can then create our full modeling operator, by combining Pw with A_inv and the receiver sampling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Create operator for injecting the weights, multiplied by the provided wavelet(s)\nPw = judiLRWF(wavelet)\n\n# Model observed data w/ extended source\nF = Pr*A_inv*adjoint(Pw)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Extended source modeling supports both forward and adjoint modeling:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Simultaneous observed data\nd_sim = F*w\ndw = adjoint(F)*d_sim","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As for regular modeling, we can create a Jacobian for linearized modeling and migration. First we define a migration velocity model and the corresponding modeling operator A0_inv:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Migration velocity and squared slowness\nv0 = ones(Float32, n) .* 1.4f0\nm0 = (1f0 ./ v0).^2\n\n# Model structure and modeling operator for migration velocity\nmodel0 = Model(n, d, o, m0)\nA0_inv = judiModeling(model0)\n\n# Jacobian and RTM\nJ = judiJacobian(Pr*A0_inv*adjoint(Pw), w)\nrtm = adjoint(J)*d_sim","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As before, we can plot the image after reshaping it into its original dimensions:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"rtm = reshape(rtm, model.n)\nimshow(rtm', cmap=\"gray\", vmin=-3e6, vmax=3e6)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Please also refer to the reproducable example on github for 2D and 3D extended modeling.","category":"page"},{"location":"basics/#Impedance-imaging-(inverse-scattering)","page":"Getting Started","title":"Impedance imaging (inverse scattering)","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports imaging (RTM) and demigration (linearized modeling) using the linearized inverse scattering imaging condition (ISIC) and its corresponding adjoint. ISIC can be enabled via the Options class. You can set this options when you initially create the modeling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Options strucuture\nopt = Options(isic=true)\n\n# Set up modeling operator\nA0_inv = judiModeling(model0; options=opt)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"When you create a Jacobian from a forward modeling operator, the Jacobian inherits the options from A0_inv:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J = judiJacobian(Pr*A0_inv*Ps', q)\nJ.options.isic\n# -> true","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Alternatively, you can directly set the option in your Jacobian:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J.options.isic = true # enable isic\nJ.options.isic = false # disable isic","category":"page"},{"location":"basics/#Optimal-checkpointing","page":"Getting Started","title":"Optimal checkpointing","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports optimal checkpointing via Devito's interface to the Revolve library. To enable checkpointing, use the Options class:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Options strucuture\nopt = Options(optimal_checkpointing=true)\n\n# Set up modeling operator\nA0_inv = judiModeling(model0; options=opt)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"When you create a Jacobian from a forward modeling operator, the Jacobian inherits the options from A0_inv:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J = judiJacobian(Pr*A0_inv*Ps', q)\nJ.options.optimal_checkpointing\n# -> true","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Alternatively, you can directly set the option in your Jacobian:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J.options.optimal_checkpointing = true # enable checkpointing\nJ.options.optimal_checkpointing = false # disable checkpointing","category":"page"},{"location":"basics/#On-the-fly-Fourier-transforms","page":"Getting Started","title":"On-the-fly Fourier transforms","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports seismic imaging in the frequency domain using on-the-fly discrete Fourier transforms (DFTs). To compute an RTM image in the frequency domain for a given set of frequencies, we first create a cell array for the frequencies of each source experiment:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"nsrc = 4 # assume 4 source experiments\nfrequencies = Array{Any}(undef, nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Now we can define single or multiple frequencies for each shot location for which the RTM image will be computed:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# For every source location, compute RTM image for 10 and 20 Hz\nfor j=1:nsrc\n frequencies[j] = [0.001, 0.002]\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The frequencies are passed to the Jacobian via the options field. Assuming we already have a Jacobian set up, we set the frequencies via:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J.options.frequencies = frequencies","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Instead of the same two frequencies for each source experiment, we could have chosen different random sets of frequencies, which creates an RTM with incoherent noise. We can also draw random frequencies using the frequency spectrum of the true source as the probability density function. To create a distribution for a given source q (judiVector) from which we can draw frequency samples, use:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"q_dist = generate_distribution(q)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Then we can assigne a random set of frequencies in a specified range as follows:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"nfreq = 10 # no. of frequencies per source location\nfor j=1:nsrc\n J.options.frequencies[j] = select_frequencies(q_dist; fmin=0.003, fmax=0.04, nf=nfreq)\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Once the options.frequencies field is set, on-the-fly DFTs are used for both born modeling and RTM. To save computational cost, we can limit the number of DFTs that are performed. Rather than computing the DFT at every time step, we can define a subsampling factor as follows:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Compute DFT every 4 time steps\nJ.options.dft_subsampling_factor=4","category":"page"},{"location":"installation/#Installation","page":"Installation","title":"Installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI is a linear algebra abstraction built on top of Devito. Because Devito is a just-in-time compiler, you will need to have a standard C compiler installed. by default most system come with a gcc compiler (except Windows where we recommend to use docker or WSL) which unfortunately isnt' very reliable. It is therefore recommended to install a proper compiler (gcc>=7, icc). For GPU offloading, you will then need to install a proper offloading compiler such as Nvidia's nvc or the latest version of clang (not Apple clang).","category":"page"},{"location":"installation/#Standard-installation","page":"Installation","title":"Standard installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI is registered and can be installed directly in julia REPL","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"] add JUDI","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"This will install JUDI, and the build will install the necessary dependencies including Devito.","category":"page"},{"location":"installation/#Custom-installation","page":"Installation","title":"Custom installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"In some case you may want to have your own installation of Devito you want JUDI to use in which case you should foloow these steps.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"You can find installation instruction in our Wiki at Installation.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI is a registered package and can therefore be easily installed from the General registry with ]add/dev JUDI","category":"page"},{"location":"installation/#GPU","page":"Installation","title":"GPU","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI supports the computation of the wave equation on GPU via Devito's GPU offloading support.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"NOTE: Only the wave equation part will be computed on GPU, the julia arrays will still be CPU arrays and CUDA.jl is not supported.","category":"page"},{"location":"installation/#Compiler-installation","page":"Installation","title":"Compiler installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"To enable gpu support in JUDI, you will need to install one of Devito's supported offloading compilers. We strongly recommend checking the Wiki for installation steps and to reach out to the Devito community for GPU compiler related issues.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"[x] nvc/pgcc. This is recommended and the simplest installation. You can install the compiler following Nvidia's installation instruction at HPC-sdk\n[ ] aompcc. This is the AMD compiler that is necessary for running on AMD GPUs. This installation is not tested with JUDI and we recommend to reach out to Devito's team for installation guidelines.\n[ ] openmp5/clang. This installation requires the compilation from source openmp, clang and llvm to install the latest version of openmp5 enabling gpu offloading. You can find instructions on this installation in Devito's Wiki","category":"page"},{"location":"installation/#Setup","page":"Installation","title":"Setup","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"The only required setup for GPU support are the environment variables for Devito. For the currently supported nvc+openacc setup these are:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"export DEVITO_LANGUAGE=openacc\nexport DEVITO_ARCH=nvc\nexport DEVITO_PLATFORM=nvidiaX","category":"page"},{"location":"installation/#Running-with-Docker","page":"Installation","title":"Running with Docker","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"If you do not want to install JUDI, you can run JUDI as a docker image. The first possibility is to run the docker container as a Jupyter notebook. JUDI provides two docker images for the latest JUDI release for Julia versions 1.6 (LTS) and 1.7 (latest stable version). The images names are mloubout/judi:JVER-latest where JVER is the Julia version. This docker images contain pre-installed compilers for CPUs (gcc-10) and Nvidia GPUs (nvc) via the nvidia HPC sdk. The environment is automatically set for [Devito] based on the hardware available. ","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Note: If you wish to use your gpu, you will need to install nvidia-docker and run docker run --gpus all in order to make the GPUs available at runtime from within the image.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"To run JUDI via docker execute the following command in your terminal:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"docker run -p 8888:8888 mloubout/judi:1.7-latest","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"This command downloads the image and launches a container. You will see a link that you can copy-paste to your browser to access the notebooks. Alternatively, you can run a bash session, in which you can start a regular interactive Julia session and run the example scripts. Download/start the container as a bash session with:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"docker run -it mloubout/judi:1.7-latest /bin/bash","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Inside the container, all examples are located in the directory /app/judi/examples/scripts.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Previous versions: As of version v2.6.7 of JUDI, we also ship version-tagged images as mloubout/judi:JVER-ver where ver is the version of JUDI wanted, for example the current JUDI version with Julia 1.7 is mloubout/judi:1.7-v2.6.7","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Development version: Additionally, we provide two images corresponding to the latest development version of JUDI (latest state of the master branch). These images are called mloubout/judi:JVER-dev and can be used in a similar way.","category":"page"},{"location":"installation/#Testing","page":"Installation","title":"Testing","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"A complete test suite is included with JUDI and is tested via GitHub Actions. You can also run the test locally via:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":" julia --project -e 'using Pkg;Pkg.test(coverage=false)'","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"By default, only the JUDI base API will be tested, however the testing suite supports other modes controlled via the environemnt variable GROUP such as:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"\tGROUP=JUDI julia --project -e 'using Pkg;Pkg.test(coverage=false)'","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"The supported modes are:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI : Only the base API (linear operators, vectors, ...)\nISO_OP : Isotropic acoustic operators\nISOOPFS : Isotropic acoustic operators with free surface\nTTI_OP : Transverse tilted isotropic operators\nTTIOPFS : Transverse tilted isotropic operators with free surface\nfilename : you can also provide just a filename (i.e GROUP=test_judiVector.jl) and only this one test file will be run. Single files with TTI or free surface are not currently supported as it relies on Base.ARGS for the setup.","category":"page"},{"location":"installation/#Configure-compiler-and-OpenMP","page":"Installation","title":"Configure compiler and OpenMP","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"Devito uses just-in-time compilation for the underlying wave equation solves. The default compiler is intel, but can be changed to any other specified compiler such as gnu. Either run the following command from the command line or add it to your ~/.bashrc file:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"export DEVITO_ARCH=gnu","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Devito uses shared memory OpenMP parallelism for solving PDEs. OpenMP is disabled by default, but you can enable OpenMP and define the number of threads (per PDE solve) as follows:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"export DEVITO_LANGUAGE=openmp # Enable OpenMP. \nexport OMP_NUM_THREADS=4 # Number of OpenMP threads","category":"page"},{"location":"inversion/#Seismic-Inversion","page":"Inversion","title":"Seismic Inversion","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"Pages = [\"inversion.md\"]","category":"page"},{"location":"inversion/#Introduction","page":"Inversion","title":"Introduction","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"We currently introduced the linear operators that allow to write seismic modeling and inversion in a high-level, linear algebra way. These linear operator allow the script to closely follow the mathematics and to be readable and understandable.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"However, these come with overhead. In particular, consider the following compuation on the FWI gradient:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"\nd_syn = F*q\nr = judiJacobian(F, q)' * (d_syn - d_obs)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"In this two lines, the forward modeling is performed twice: once to compute d_syn then once again to compute the Jacobian adjoint. In order to avoid this overhead for practical inversion, we provide utility function that directly comput the gradient and objective function (L2- misfit) of FWI, LSRTM and TWRI with minimum overhead.","category":"page"},{"location":"inversion/#FWI","page":"Inversion","title":"FWI","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"fwi_objective","category":"page"},{"location":"inversion/#JUDI.fwi_objective","page":"Inversion","title":"JUDI.fwi_objective","text":"fwi_objective(model, source, dobs; options=Options())\n\nEvaluate the full-waveform-inversion (reduced state) objective function. Returns a tuple with function value and vectorized \\\n\ngradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and \nobserved data of type judiVector.\n\nExample\n\nfunction_value, gradient = fwi_objective(model, source, dobs)\n\n\n\n\n\n","category":"function"},{"location":"inversion/#Example","page":"Inversion","title":"Example","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"JUDI is designed to let you set up objective functions that can be passed to standard packages for (gradient-based) optimization. The following example demonstrates how to perform FWI on the 2D Overthrust model using a spectral projected gradient algorithm from the minConf library, which is included in the software. A small test dataset (62 MB) and the model can be downloaded from this FTP server:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D.segy`)\nrun(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D_initial_model.h5`)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"The first step is to load the velocity model and the observed data into Julia, as well as setting up bound constraints for the inversion, which prevent too high or low velocities in the final result. Furthermore, we define an 8 Hertz Ricker wavelet as the source function:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"using PyPlot, HDF5, SegyIO, JUDI, SlimOptim, Statistics, Random\n\n# Load starting model\nn, d, o, m0 = read(h5open(\"overthrust_2D_initial_model.h5\", \"r\"), \"n\", \"d\", \"o\", \"m0\")\nmodel0 = Model((n[1], n[2]), (d[1], d[2]), (o[1], o[2]), m0)\t# need n, d, o as tuples and m0 as array\n\n# Bound constraints\nvmin = ones(Float32, model0.n) .+ 0.3f0\nvmax = ones(Float32, model0.n) .+ 5.5f0\nmmin = vec((1f0 ./ vmax).^2)\t# convert to slowness squared [s^2/km^2]\nmmax = vec((1f0 ./ vmin).^2)\n\n# Load segy data\nblock = segy_read(\"overthrust_2D.segy\")\ndobs = judiVector(block)\n\n# Set up wavelet\nsrc_geometry = Geometry(block; key=\"source\", segy_depth_key=\"SourceDepth\")\t# read source position geometry\nwavelet = ricker_wavelet(src_geometry.t[1], src_geometry.dt[1], 0.008f0)\t# 8 Hz wavelet\nq = judiVector(src_geometry, wavelet)\n","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"For this FWI example, we define an objective function that can be passed to the minConf optimization library, which is included in the Julia Devito software package. We allow a maximum of 20 function evaluations using a spectral-projected gradient (SPG) algorithm. To save computational cost, each function evaluation uses a randomized subset of 20 shot records, instead of all 97 shots:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"# Optimization parameters\nfevals = 20\t# number of function evaluations\nbatchsize = 20\t# number of sources per iteration\nfvals = zeros(21)\nopt = Options(optimal_checkpointing = false) # set to true to enable checkpointing\n\n# Objective function for minConf library\ncount = 0\nfunction objective_function(x)\n\tmodel0.m = reshape(x, model0.n);\n\n\t# fwi function value and gradient\n\ti = randperm(dobs.nsrc)[1:batchsize]\n\tfval, grad = fwi_objective(model0, q[i], dobs[i]; options=opt)\n\tgrad = reshape(grad, model0.n); grad[:, 1:21] .= 0f0\t# reset gradient in water column to 0.\n\tgrad = .1f0*grad/maximum(abs.(grad))\t# scale gradient for line search\n\n\tglobal count; count += 1; fvals[count] = fval\n return fval, vec(grad.data)\nend\n\n# FWI with SPG\nProjBound(x) = median([mmin x mmax], dims=2)\t# Bound projection\noptions = spg_options(verbose=3, maxIter=fevals, memory=3)\nx, fsave, funEvals= minConf_SPG(objective_function, vec(m0), ProjBound, options)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"This example script can be run in parallel and requires roughly 220 MB of memory per source location. Execute the following code to generate figures of the initial model and the result, as well as the function values:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"figure(); imshow(sqrt.(1./adjoint(m0))); title(\"Initial model\")\nfigure(); imshow(sqrt.(1./adjoint(reshape(x, model0.n)))); title(\"FWI\")\nfigure(); plot(fvals); title(\"Function value\")","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"(Image: fwi)","category":"page"},{"location":"inversion/#LSRTM","page":"Inversion","title":"LSRTM","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"lsrtm_objective","category":"page"},{"location":"inversion/#JUDI.lsrtm_objective","page":"Inversion","title":"JUDI.lsrtm_objective","text":"lsrtm_objective(model, source, dobs, dm; options=Options(), nlind=false)\n\nEvaluate the least-square migration objective function. Returns a tuple with function value and \ngradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and \nobserved data of type judiVector.\n\nExample\n\nfunction_value, gradient = lsrtm_objective(model, source, dobs, dm)\n\n\n\n\n\n","category":"function"},{"location":"inversion/#Example-2","page":"Inversion","title":"Example","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"JUDI includes matrix-free linear operators for modeling and linearized (Born) modeling, that let you write algorithms for migration that follow the mathematical notation of standard least squares problems. This example demonstrates how to use Julia Devito to perform least-squares reverse-time migration on the 2D Marmousi model. Start by downloading the test data set (1.1 GB) and the model:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_2D.segy`)\nrun(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_migration_velocity.h5`)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"Once again, load the starting model and the data and set up the source wavelet. For this example, we use a Ricker wavelet with 30 Hertz peak frequency.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"using PyPlot, HDF5, JUDI, SegyIO, Random\n\n# Load smooth migration velocity model\nn,d,o,m0 = read(h5open(\"marmousi_migration_velocity.h5\",\"r\"), \"n\", \"d\", \"o\", \"m0\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0)\n\n# Load data\nblock = segy_read(\"marmousi_2D.segy\")\ndD = judiVector(block)\n\n# Set up wavelet\nsrc_geometry = Geometry(block; key=\"source\", segy_depth_key=\"SourceDepth\")\nwavelet = ricker_wavelet(src_geometry.t[1],src_geometry.dt[1],0.03)\t# 30 Hz wavelet\nq = judiVector(src_geometry,wavelet)\n\n# Set up info structure\nntComp = get_computational_nt(q.geometry,dD.geometry,model0)\t# no. of computational time steps\ninfo = Info(prod(model0.n),dD.nsrc,ntComp)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"To speed up the convergence of our imaging example, we set up a basic preconditioner for each the model- and the data space, consisting of mutes to suppress the ocean-bottom reflection in the data and the source/receiver imprint in the image. The operator J represents the linearized modeling operator and its adjoint J' corresponds to the migration (RTM) operator. The forward and adjoint pair can be used for a basic LS-RTM example with (stochastic) gradient descent:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"# Set up matrix-free linear operators\nopt = Options(optimal_checkpointing = true) # set to false to disable optimal checkpointing\nF = judiModeling(model0, q.geometry, dD.geometry; options=opt)\nJ = judiJacobian(F, q)\n\n# Right-hand preconditioners (model topmute)\nMr = judiTopmute(model0; taperwidth=10)\t# mute up to grid point 52, with 10 point taper\n# Left-hand side preconditioners\nMl = judiDatMute(q.geometry, dD.geometry; t0=.120)\t# data topmute starting at time 120ms\n\n# Stochastic gradient\nx = zeros(Float32, info.n)\t# zero initial guess\nbatchsize = 10\t# use subset of 10 shots per iteration\nniter = 32\nfval = zeros(Float32, niter)\n\nfor j=1:niter\n\tprintln(\"Iteration: \", j)\n\n\t# Select batch and set up left-hand preconditioner\n\ti = randperm(dD.nsrc)[1:batchsize]\n\n\t# Compute residual and gradient\n\tr = Ml[i]*J[i]*Mr*x - Ml[i]*dD[i]\n\tg = adjoint(Mr)*adjoint(J[i])*adjoint(Ml[i])*r\n\n\t# Step size and update variable\n\tfval[j] = .5f0*norm(r)^2\n\tt = norm(r)^2/norm(g)^2\n\tglobal x -= t*g\nend","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"(Image: lsrtm)","category":"page"},{"location":"inversion/#TWRI","page":"Inversion","title":"TWRI","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"twri_objective","category":"page"},{"location":"inversion/#JUDI.twri_objective","page":"Inversion","title":"JUDI.twri_objective","text":"twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())\n\nEvaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and gradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and observed data of type judiVector.\n\nExample\n\nfunction_value, gradient_m, gradient_y = twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())\n\n\n\n\n\ntwri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())\n\nEvaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and \ngradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and \nobserved data of type judiVector. Example ======= functionvalue, gradient = fwiobjective(model, source, dobs)\n\n\n\n\n\n","category":"function"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"and related TWRI options","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"TWRIOptions","category":"page"},{"location":"inversion/#JUDI.TWRIOptions","page":"Inversion","title":"JUDI.TWRIOptions","text":"TWRIOptions\n grad_corr::Bool\n comp_alpha::Bool\n weight_fun\n eps\n params::Symbol\n Invq::String\n\nOptions structure for TWRI.\n\ngrad_corr: Whether to add the gradient correction J'(m0, q)*∇_y\n\ncomp_alpha: Whether to compute optimal alpha (alpha=1 if not)\n\nweight_fun: Whether to apply focusing/weighting function to F(m0)'*y and its norm\n\neps: Epsilon (noise level) value (default=0)\n\nInvq: How to compute F'Y, either as full field or as a rank 1 approximation w(t)*q(x) using the source wavelet for w\n\nparam: Which gradient to compute. Choices are nothing (objective only), :m, :y or :all\n\nConstructor\n\nAll arguments are optional keyword arguments with the following default values:\n\nTWRIOptions(;gradcorr=false, compalpha=true, weight_fun=nothing, eps=0, params=:m)\n\n\n\n\n\n","category":"type"},{"location":"inversion/#Machine-Learning","page":"Inversion","title":"Machine Learning","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"ChainRules.jl allows integrating JUDI modeling operators into convolutional neural networks for deep learning. For example, the following code snippet shows how to create a shallow CNN consisting of two convolutional layers with a nonlinear forward modeling layer in-between them. The integration of ChainRules and JUDI enables backpropagation through Flux' automatic differentiation tool, but calls the corresponding adjoint JUDI operators under the hood. For more details, please check out this tutorial.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"# Jacobian\nW1 = judiJacobian(F0, q)\nb1 = randn(Float32, num_samples)\n\n# Fully connected layer\nW2 = randn(Float32, n_out, num_samples)\nb2 = randn(Float32, n_out)\n\n# Network and loss\nnetwork(x) = W2*(W1*x .+ b1) .+ b2\nloss(x, y) = Flux.mse(network(x), y)\n\n# Compute gradient w/ Flux\np = params(x, y, W1, b1, b2)\ngs = Tracker.gradient(() -> loss(x, y), p)\ngs[x]\t# gradient w.r.t. to x","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"Integration with ChainRules allows implementing physics-augmented neural networks for seismic inversion, such as loop-unrolled seismic imaging algorithms. For example, the following results are a conventional RTM image, an LS-RTM image and a loop-unrolled LS-RTM image for a single simultaneous shot record.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"(Image: flux)","category":"page"},{"location":"io/#Input/Output","page":"Input/Output","title":"Input/Output","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"For reading and writing SEG-Y data, JUDI uses the SegyIO.jl package. JUDI supports reading SEG-Y from disk into memory, as well as working with out-of-core (OOC) data containers. In the latter case, judiVectors contain look-up tables that allow accessing the underlying data in constant time.","category":"page"},{"location":"io/#Reading-SEG-Y-files-into-memory","page":"Input/Output","title":"Reading SEG-Y files into memory","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"To read a single SEG-Y file into memory, use the segy_read function:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"using SegyIO\n\nblock = segy_read(\"data.segy\")","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"From a SegyIO data block, you can create an in-core judiVector, as well as a Geometry object for the source:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# judiVector for observed data\nd_obs = judiVector(block; segy_depth_key=\"RecGroupElevation\")\n\n# Source geometry\nsrc_geometry = Geometry(block; key=\"source\", segy_depth_key=\"SourceDepth\")","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"The optional keyword segy_depth_key specifies which SEG-Y header stores the depth coordinate. After reading a block, you can check block.traceheaders to see which trace headers are set and where to find the depth coordinates for sources or receivers.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"The d_obs vector constains the receiver geometry in d_obs.geometry, so there is no need to set up a separate geometry object manually. However, in principle we can set up a receiver Geometry object as follows:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"rec_geometry = Geometry(block; key=\"receiver\", segy_depth_key=\"RecGroupElevation\")","category":"page"},{"location":"io/#Writing-SEG-Y-files","page":"Input/Output","title":"Writing SEG-Y files","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"To write a judiVector as a SEG-Y file, we need a judiVector containing the receiver data and geometry, as well as a judiVector with the source coordinates. From the judiVectors, we first create a SegyIO block:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"block = judiVector_to_SeisBlock(d_obs, q)","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"where d_obs and q are judiVectors for receiver and source data respectively. To save only the source q, we can do","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"block = src_to_SeisBlock(q)","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Next, we can write a SEG-Y file from a SegyIO block:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"segy_write(\"new_file.segy\", block) # writes a SEG-Y file called new_file.segy","category":"page"},{"location":"io/#Reading-out-of-core-SEG-Y-files","page":"Input/Output","title":"Reading out-of-core SEG-Y files","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"For SEG-Y files that do not fit into memory, JUDI provides the possibility to work with OOC data containers. First, SegyIO scans also available files and then creates a lookup table, including a summary of the most important SEG-Y header values. See SegyIO's documentation for more information.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"First we provide the path to the directory that we want to scan, as well as a string that appears in all the files we want to scan. For example, here we want to scan all files that contain the string \"bp_observed_data\". The third argument is a list of SEG-Y headers for which we create a summary. For creating OOC judiVectors, always include the \"GroupX\", \"GroupY\" and \"dt\" keyworkds, as well as the keywords that carry the source and receiver depth coordinates:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# Specify direcotry to scan\npath_to_data = \"/home/username/data_directory/\"\n\n# Scan files in given directory and create OOC data container\ncontainer = segy_scan(path_to_data, \"bp_observed_data\", [\"GroupX\", \"GroupY\", \n \"RecGroupElevation\", \"SourceDepth\", \"dt\"])","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Depending of the number and size of the underlying files, this process can take multiple hours, but it only has to be executed once! Furthermore, parallel scanning is supported as well. ","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Once we have scanned all files in the directory, we can create an OOC judiVector and source Geometry object as follows:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# Create OOC judiVector\nd_obs = judiVector(container; segy_depth_key=\"RecGroupElevation\")\n\n# Create OOC source geometry object\nsrc_geometry = Geometry(container; key=\"source\", segy_depth_key=\"SourceDepth\")","category":"page"},{"location":"io/#Reading-and-writing-velocity-models","page":"Input/Output","title":"Reading and writing velocity models","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"JUDI does not require velocity models to be read or saved in any specific format. Any file format that allows reading the velocity model as a two or three-dimensional Julia array will work.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"In our examples, we often use the JLD or HDF5 packages to read/write velocity models and the corresponing meta data (i.e. grid spacings and origins). If your model is a SEG-Y file, use the segy_read function from SegyIO as shown above.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Create an example model to write and read:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"n = (120, 100)\nd = (10.0, 10.0)\no = (0.0, 0.0)\nv = ones(Float32, n) .* 1.5f0\nm = 1f0 ./ v.^2","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Write a model as a .jld file:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"using JLD\n\nsave(\"my_model.jld\", \"n\", n, \"d\", d, \"o\", o, \"m\", m)","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Read a model from a .jld file:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# Returns a Julia dictionary\nM = load(\"my_model.jld\")\n\nn = M[\"n\"]\nd = M[\"d\"]\no = M[\"o\"]\nm = M[\"m\"]\n\n# Set up a Model object\nmodel = Model(n, d, o, m)","category":"page"},{"location":"tutorials/quickstart/#Modeling-and-inversion-with-JUDI","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"title: Overview of JUDI modeling and inversion usage author: Mathias Louboutin, Philipp Witte date: April 2022 –-","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"This example script is written using Weave.jl and can be converted to different format for documentation and usage This example is converted to a markdown file for the documentation.","category":"page"},{"location":"tutorials/quickstart/#Import-JUDI,-Linear-algebra-utilities-and-Plotting","page":"Modeling and inversion with JUDI","title":"Import JUDI, Linear algebra utilities and Plotting","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"using JUDI, PyPlot, LinearAlgebra","category":"page"},{"location":"tutorials/quickstart/#Create-a-JUDI-model-structure","page":"Modeling and inversion with JUDI","title":"Create a JUDI model structure","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"In JUDI, a Model structure contains the grid information (origin, spacing, number of gridpoints) and the physical parameters. The squared slowness is always required as the base physical parameter for propagation. In addition, JUDI supports additional physical representations. First we accept density that can either be a direct input Model(n, d, o, m, rho) or an optional keyword argument Model(n,d,o,m;rho=rho). Second, we also provide VTI/TTI kernels parametrized by the THomsen parameters that can be input as keyword arguments Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta,theta=theta,phi=phi). Because the thomsen parameters are optional the propagator wil lonloy use the ones provided. For example Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta) will infer a VTI propagation","category":"page"},{"location":"tutorials/quickstart/#Create-discrete-parameters","page":"Modeling and inversion with JUDI","title":"Create discrete parameters","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Set up model structure\nn = (120, 100) # (x,y,z) or (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32,n) .+ 0.5f0\nv0 = ones(Float32,n) .+ 0.5f0\nv[:,Int(round(end/2)):end] .= 3.5f0\nrho = (v0 .+ .5f0) ./ 2\n\n# Slowness squared [s^2/km^2]\nm = (1f0 ./ v).^2\nm0 = (1f0 ./ v0).^2\ndm = vec(m0 - m)\n\n# Setup model structure\nnsrc = 2\t# number of sources\nmodel = Model(n, d, o, m)\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Model (n=(120, 100), d=(10.0f0, 10.0f0), o=(0.0f0, 0.0f0)) with parameters \n(:m, :rho)","category":"page"},{"location":"tutorials/quickstart/#Create-acquisition-geometry","page":"Modeling and inversion with JUDI","title":"Create acquisition geometry","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"In this simple usage example, we create a simple acquisiton by hand. In practice the acquisition geometry will be defined by the dataset beeing inverted. We show in a spearate tutorial how to use SegyIO.jl to handle SEGY seismic datasets in JUDI.","category":"page"},{"location":"tutorials/quickstart/#Create-source-and-receivers-positions-at-the-surface","page":"Modeling and inversion with JUDI","title":"Create source and receivers positions at the surface","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Set up receiver geometry\nnxrec = 120\nxrec = range(0f0, stop=(n[1]-1)*d[1], length=nxrec)\nyrec = 0f0 # WE have to set the y coordiante to zero (or any number) for 2D modeling\nzrec = range(d[1], stop=d[1], length=nxrec)\n\n# receiver sampling and recording time\ntimeD = 1250f0 # receiver recording time [ms]\ndtD = 2f0 # receiver sampling interval [ms]\n\n# Set up receiver structure\nrecGeometry = Geometry(xrec, yrec, zrec; dt=dtD, t=timeD, nsrc=nsrc)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"GeometryIC{Float32} wiht 2 sources","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"The source geometry is a but different. Because we want to create a survey with nsrc shot records, we need to convert the vector of sources postions [s0, s1, ... sn] into an array of array [[s0], [s1], ...] so that JUDI understands that this is a set of indepednet nsrc","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"xsrc = convertToCell(range(0f0, stop=(n[1]-1)*d[1], length=nsrc))\nysrc = convertToCell(range(0f0, stop=0f0, length=nsrc))\nzsrc = convertToCell(range(d[1], stop=d[1], length=nsrc))\n\n# Set up source structure\nsrcGeometry = Geometry(xsrc, ysrc, zsrc; dt=dtD, t=timeD)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"GeometryIC{Float32} wiht 2 sources","category":"page"},{"location":"tutorials/quickstart/#Source-judiVector","page":"Modeling and inversion with JUDI","title":"Source judiVector","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Finally, with the geometry defined, we can create a source wavelet (a simple Ricker wavelet here) a our first judiVector In JUDI, a judiVector is the core structure that represent a acquisition-geometry based dataset. This structure encapsulate the physical locations (trace coordinates) and corrsponding data trace in a source-based structure. for a given judiVector d then d[1] will be the shot record for the first source, or in the case of the source term, the first source wavelet and its positon.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# setup wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(timeD, dtD, f0)\nq = judiVector(srcGeometry, wavelet)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 2 sources","category":"page"},{"location":"tutorials/quickstart/#Modeling","page":"Modeling and inversion with JUDI","title":"Modeling","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"With our survey and subsurface model setup, we can now model and image seismic data. We first define a few options. In this tutorial we will choose to compute gradients/images subsampling the forward wavefield every two time steps subsampling_factor=2 and we fix the computational time step to be 1ms wiuth dt_comp=1.0 know to satisfy the CFL condition for this simple example. In practice, when dt_comp isn't provided, JUDI will compute the CFL condition for the propagation.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Setup options\nopt = Options(subsampling_factor=2, space_order=32)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"JUDIOptions(32, false, false, 1000.0f0, false, \"\", \"shot\", false, false, An\ny[], \"as\", 2, 1, false, nothing, 0.015f0)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Linear Operators The core idea behind JUDI is to abstract seismic inverse problems in term of linear algebra. In its simplest form, seismic inversion can be formulated as","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"undersetmathbfmtextargmin phi(mathbfm) = frac12 mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd _2^2 \ntext \nnabla_mathbfm phi(mathbfm) = mathbfJ(mathbfm mathbfq)^top (mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"where mathbfP_r is the receiver projection (measurment operator) and mathbfP_s^top is the source injection operator (adjoint of measurment at the source location). Therefore, we bastracted these operation to be able to define these operators","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Setup operators\nPr = judiProjection(recGeometry)\nF = judiModeling(model; options=opt)\nF0 = judiModeling(model0; options=opt)\nPs = judiProjection(srcGeometry)\nJ = judiJacobian(Pr*F0*adjoint(Ps), q)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"JUDI born{Float32} propagator (z * x) -> (src * rec * time)","category":"page"},{"location":"tutorials/quickstart/#Model-and-image-data","page":"Modeling and inversion with JUDI","title":"Model and image data","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We first model synthetic data using our defined source and true model ","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Nonlinear modeling\ndobs = Pr*F*adjoint(Ps)*q","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 2 sources","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot the shot record","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(dobs.data[1], vmin=-1, vmax=1, cmap=\"PuOr\", extent=[xrec[1], xrec[end], timeD/1000, 0], aspect=\"auto\")\nxlabel(\"Receiver position (m)\")\nylabel(\"Time (s)\")\ntitle(\"Synthetic data\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Because we have abstracted the linear algebra, we can solve the adjoint wave-equation as well where the data becomes the source. This adjoint solve will be part of the imaging procedure.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# # Adjoint\nqad = Ps*adjoint(F)*adjoint(Pr)*dobs","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 2 sources","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We can easily now test the adjointness of our operator with the standard dot test. Because we intend to conserve our linear algebra abstraction, judiVector implements all the necessary linear algebra functions such as dot product or norm to be used directly.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# \ndot1 = dot(q, qad)\n# \ndot2 = dot(dobs, dobs)\n# Compare\n@show dot1, dot2, (dot2 - dot2)/(dot1 + dot2)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(dot1, dot2, (dot2 - dot2) / (dot1 + dot2)) = (467806.06f0, 467806.28f0, 0.\n0f0)\n(467806.06f0, 467806.28f0, 0.0f0)","category":"page"},{"location":"tutorials/quickstart/#Inversion","page":"Modeling and inversion with JUDI","title":"Inversion","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Our main goal is to provide an inversion framework for seismic inversion. To this end, as shown earlier, users can easily define the Jacobian operator and compute an RTM image (i.e FWI gradient) with a simple matrix-vector product. Once again, we provide both the Jacobian and its adjoint and we can compute Born linearized data.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Linearized modeling J*dm\ndD = J*dm\n# Adjoint jacobian, RTM image\nrtm = adjoint(J)*dD","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"PhysicalParameter{Float32, 2} of size (120, 100) with origin (0.0f0, 0.0f0)\n and spacing (10.0f0, 10.0f0)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We show the linearized data.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(dD.data[1], vmin=-1, vmax=1, cmap=\"PuOr\", extent=[xrec[1], xrec[end], timeD/1000, 0], aspect=\"auto\")\nxlabel(\"Receiver position (m)\")\nylabel(\"Time (s)\")\ntitle(\"Linearized data\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"And the RTM image","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(rtm', vmin=-1e2, vmax=1e2, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"RTM image\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/#Inversion-utility-functions","page":"Modeling and inversion with JUDI","title":"Inversion utility functions","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We currently introduced the lineaar operators that allow to write seismic modeling and inversion in a high-level, linear algebra way. These linear operators allow the script to closely follow the mathematics and to be readable and understandable.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"However, these come with overhead. In particular, consider the following compuation on the FWI gradient:","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"d_syn = F*q\nr = judiJacobian(F, q)' * (d_syn - d_obs)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"In this two lines, the forward modeling is performed twice: once to compute d_syn then once again to compute the Jacobian adjoint. In order to avoid this overhead for practical inversion, we provide utility function that directly comput the gradient and objective function (L2- misfit) of FWI, LSRTM and TWRI with minimum overhead.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"FWI misfit and gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# evaluate FWI objective function\nf, g = fwi_objective(model0, q, dobs; options=opt)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(21226.521f0, PhysicalParameter{Float32, 2} of size (120, 100) with origin \n(0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(g', vmin=-1e2, vmax=1e2, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"FWI gradient\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"LSRTM misfit and gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# evaluate LSRTM objective function\nfj, gj = lsrtm_objective(model0, q, dD, dm; options=opt)\nfjn, gjn = lsrtm_objective(model0, q, dobs, dm; nlind=true, options=opt)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(35903.93f0, PhysicalParameter{Float32, 2} of size (120, 100) with origin (\n0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradients","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(gj', vmin=-1, vmax=1, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"LSRTM gradient\")\ndisplay(fig)\n\nfig = figure()\nimshow(gjn', vmin=-1, vmax=1, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"LSRTM gradient with background data substracted\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: ) (Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"By extension, lsrtmobjective is the same as fwiobjecive when dm is zero And with computing of the residual. Small noise can be seen in the difference due to floating point roundoff errors with openMP, but running with OMPNUMTHREADS=1 (no parllelism) produces the exact (difference == 0) same result gjn2 == g","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fjn2, gjn2 = lsrtm_objective(model0, q, dobs, 0f0.*dm; nlind=true, options=opt)\nfig = figure()","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"PyPlot.Figure(PyObject
      )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"imshow(gjn2', vmin=-1e2, vmax=1e2, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"LSRTM gradient with zero perturbation\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/#TWRI","page":"Modeling and inversion with JUDI","title":"TWRI","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Finally, JUDI implements TWRI, an augmented method to tackle cycle skipping. Once again we provide a computationnally efficient wrapper function that returns the objective value and necessary gradients","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"f, gm, gy = twri_objective(model0, q, dobs, nothing; options=opt, optionswri=TWRIOptions(params=:all))\n# With on-the-fly DFT, experimental\nf, gmf = twri_objective(model0, q, dobs, nothing; options=Options(frequencies=[[.009, .011], [.008, .012]]), optionswri=TWRIOptions(params=:m))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(-2244.6035f0, PhysicalParameter{Float32, 2} of size (120, 100) with origin\n (0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradients","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(gm', vmin=-1, vmax=1, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"TWRI gradient w.r.t m\")\ndisplay(fig)\n\nfig = figure()\nimshow(gy.data[1], vmin=-1e2, vmax=1e2, cmap=\"PuOr\", extent=[xrec[1], xrec[end], timeD/1000, 0], aspect=\"auto\")\nxlabel(\"Receiver position (m)\")\nylabel(\"Time (s)\")\ntitle(\"TWRI gradient w.r.t y\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: ) (Image: )","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/#FWI-with-Quasi-Newton-methods-from-the-NLopt-library","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"","category":"section"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"In this notebook, we demonstrate how to interface the NLopt optimization library for full-waveform inversion with a limited-memory Quasi-Newton (L-BFGS) algorithm. Once again, we start by adding additional workers for parallel computing and by loading all necessary modules:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"using SegyIO, HDF5, PyPlot, JUDI, NLopt, Random, LinearAlgebra, Printf","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"We load the FWI starting model from the HDF5 model file and set up the JUDI model structure:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"m0, n, d, o = read(h5open(\"overthrust_model.h5\",\"r\"),\"m0\",\"n\",\"d\",\"o\"); title(\"Starting model\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0);\nimshow(sqrt.(1f0./m0)', cmap=\"GnBu\", extent=(0,10,3,0));\nxlabel(\"Lateral position [km]\");\nylabel(\"Depth [km]\");","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Then we read the SEG-Y file containing our test data set. The data was generated with a 2D excerpt from the Overthrust velocity model and consists of 31 shot records with 2 seconds recording time. We load the data and set up a JUDI seismic data vector:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"block = segy_read(\"overthrust_shot_records.segy\");\nd_obs = judiVector(block);","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mFixed length trace flag set in stream: IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=7076688, maxsize=Inf, ptr=3601, mark=-1)\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ SegyIO ~/.julia/dev/SegyIO/src/read/read_file.jl:36\u001b[39m","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"extent = [0, 10, 2, 0]\nfigure(figsize=(7, 7))\nsubplot(221)\nimshow(d_obs.data[1], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\nsubplot(222)\nimshow(d_obs.data[6], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\nsubplot(223)\nimshow(d_obs.data[11], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\nsubplot(224)\nimshow(d_obs.data[16], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\ntight_layout()\n","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Since the SEG-Y file contains the source coordinates, but not the wavelet itself, we create a JUDI Geometry structure for the source and then manually set up an 8 Hz Ricker wavelet. As for the observed data, we set up a JUDI seismic data vector q with the source geometry and wavelet:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"src_geometry = Geometry(block; key=\"source\");\nsrc_data = ricker_wavelet(src_geometry.t[1], src_geometry.dt[1], 0.008f0);\nq = judiVector(src_geometry, src_data);","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/#Optimization","page":"FWI with Quasi-Newton methods from the NLopt library","title":"Optimization","text":"","category":"section"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Rather than implementing the L-BFGS algorithms in Julia ourselves, we interface the NLopt optimization library. This library requires objective functions with the current variable and gradient as input arguments and the function value as the only output argument. For this reason, we build a wrapper that is customized for the NLopt library around our fwi_objective function. The function f! takes a vectorized estimate of the current model as well as the (vectorized) gradient as input arguments. NLopt uses double precision for floating point variables, so the first step inside f! is to reshape and convert the model to single precision. Then we choose a randomized subset of sources and shot records and compute the function value fval and gradient of the FWI objective function. We then set the gradient in the water layer to zero and overwrite the input gradient grad with the new gradient. Furthermore, we keep track of the number of function evaluations through increasing the count variable, which will serve as the termination criterion for the algorithm. In Julia, we set up f! in the following way: ","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"batchsize = 8;\ncount = 0;\n\n# NLopt objective function\nfunction objf!(x, grad)\n if count == 0\n @printf(\"%10s %15s %15s\\n\",\"Iteration\",\"Function Val\",\"norm(g)\")\n end\n # Update model\n model0.m .= Float32.(reshape(x, model0.n))\n\n # Seclect batch and calculate gradient\n i = randperm(d_obs.nsrc)[1:batchsize]\n fval, gradient = fwi_objective(model0, q[i], d_obs[i])\n\n # Reset gradient in water column to zero\n gradient = reshape(gradient, model0.n)\n gradient[:,1:21] .= 0f0\n if length(grad) > 0\n grad[1:end] = vec(gradient)\n end\n global count += 1\n @printf(\"%10d %15.5e %15.5e\\n\",count, fval, norm(g))\n return convert(Float64, fval)\nend","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"objf! (generic function with 1 method)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"g = zeros(prod(model0.n))\nf0 = objf!(vec(model0.m), g)\n# Reset count\nglobal count = 0;","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.n, use size(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m\n\n\n Iteration Function Val norm(g)\n 1 2.61794e+05 2.83025e+05","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"imshow(reshape(g, model0.n)', vmin=-1e3, vmax=1e3, extent=(0,10,3,0), cmap=\"jet\")\ntitle(\"FWI first gradient\")\nxlabel(\"Lateral position [km]\");\nylabel(\"Depth [km]\");","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"As in our gradient descent and Gauss-Newton example, we define bound constraints for the squared slowness to prevent velocities from becoming negative or too large:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"# Squared slowness\nmmax = (1.3f0).^(-2)\nmmin = (6.5f0).^(-2)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"0.023668641f0","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"The NLopt library offers a range of different optimization algorithms, from which we choose the L-BFGS method. We create an optimization object called opt by specifying the algorithm we want to use and the dimenions of the unknown model vector. We then set the upper and lower bounds of the variable, define f! as the objective function and set the termination criterion to be a maximum of 15 function evaluations:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"opt = Opt(:LD_LBFGS, prod(model0.n))\nopt.lower_bounds = mmin\nopt.upper_bounds = mmax\n# min_objective!(opt, f!)\nopt.min_objective = objf!\nopt.maxeval = 15","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"15","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Remark: Subsampling the number of sources should in practice never be used for second order methods such as L-BFGS. Specialized stochastic second order methods exist, but differ from standard Quasi-Newton methods. We only use source subsampling to reduce the computational cost of our example. Having set up the objective function, bound constraints and termination criterion, we can now run the inversion:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"** This example requires ~200 MB of memory per gradient, i.e. 800 MB with four parallel workers. It runs for approximately 1 minutes. **","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"@time (minf, minx, ret) = optimize(opt, model0.m[:])","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":" Iteration Function Val norm(g)\n 1 2.46220e+05 2.83025e+05\n 2 2.36730e+05 2.83025e+05\n 3 1.67086e+05 2.83025e+05\n 4 1.21062e+05 2.83025e+05\n 5 9.66964e+04 2.83025e+05\n 6 7.97872e+04 2.83025e+05\n 7 6.54899e+04 2.83025e+05\n 8 5.23806e+04 2.83025e+05\n 9 4.46780e+04 2.83025e+05\n 10 4.05689e+04 2.83025e+05\n 11 3.14446e+04 2.83025e+05\n 12 3.06919e+04 2.83025e+05\n 13 2.52438e+04 2.83025e+05\n 14 2.52550e+04 2.83025e+05\n 15 2.24706e+04 2.83025e+05\n 65.695496 seconds (444.24 k allocations: 1.269 GiB, 0.10% gc time, 0.20% compilation time)\n\n\n\n\n\n(22470.578125, [0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136 … 0.05060886426348713, 0.05044609939336615, 0.050292761170062046, 0.05017357884927398, 0.05011064413011435, 0.05011993876926194, 0.05020965433880526, 0.05038047472521424, 0.05062740429803987, 0.05094217839179781], :MAXEVAL_REACHED)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"We plot the final velocity model after 15 function evaluations:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"imshow(sqrt.(1f0./reshape(minx, model0.n))', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4); title(\"FWI with L-BFGS\")\nxlabel(\"Lateral position [km]\");\nylabel(\"Depth [km]\");","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"about/#Authors","page":"About","title":"Authors","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"This package was written by Philipp Witte and Mathias Louboutin from the Seismic Laboratory for Imaging and Modeling (SLIM) at the Georgia Institute of Technology. People involved in the development of JUDI include:","category":"page"},{"location":"about/","page":"About","title":"About","text":"Philipp A. Witte^* (Now MSFT)\nMathias Louboutin (Georgia Institute of Technology)\nHenryk Modzelewski (The Univeristy of British Columbia)\nFelix J. Herrmann (Georgia Institute of Technology)","category":"page"},{"location":"about/","page":"About","title":"About","text":"And you can find the full list of collaborators on github at Contributors.","category":"page"},{"location":"about/#Cite-us","page":"About","title":"Cite us","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"If you use our software for your research, please cite our Geophysics paper:","category":"page"},{"location":"about/","page":"About","title":"About","text":"@article{witteJUDI2019,\nauthor = {Philipp A. Witte and Mathias Louboutin and Navjot Kukreja and Fabio Luporini and Michael Lange and Gerard J. Gorman and Felix J. Herrmann},\ntitle = {A large-scale framework for symbolic implementations of seismic inversion algorithms in Julia},\njournal = {GEOPHYSICS},\nvolume = {84},\nnumber = {3},\npages = {F57-F71},\nyear = {2019},\ndoi = {10.1190/geo2018-0174.1},\nURL = {https://doi.org/10.1190/geo2018-0174.1},\neprint = {https://doi.org/10.1190/geo2018-0174.1}\n}","category":"page"},{"location":"about/","page":"About","title":"About","text":"Also visit the Devito homepage at https://www.devitoproject.org/publications for more information and references. If you need to cite a specific version of JUDI, you can find our citeable archives on Zenodo.","category":"page"},{"location":"about/#Contribution-and-community","page":"About","title":"Contribution and community","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"We gladly welcome and encorage contributions from the community to improve our software and its usability. Feel free to:","category":"page"},{"location":"about/","page":"About","title":"About","text":"Open issues for bugs\nStart discussions to interat with the developper and ask any questions\nOpen PR for bug fixes and improvements","category":"page"},{"location":"about/#Field-examples","page":"About","title":"Field examples","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"An example of using JUDI to invert field data is provided for the Viking Graben Line 12 which includes data preprocessing steps using Madagascar. ","category":"page"},{"location":"tutorials/05_custom_misfit/#FWI-with-user-provided-misfit-function","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In this notebook, we will introduce how the loss (data misifit) can be modifed to a user chosen function for seismic inversion. We will use one of JUDI's FWI example as a skeleton and illustrate the losses available in the package and how to input a custom data misfit function.","category":"page"},{"location":"tutorials/05_custom_misfit/#Single-source-loss","page":"FWI with user provided misfit function","title":"Single source loss","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In JUDI, the data misfit function is defined on a single shot record basis (single source experiment) and will be automatically reduced by julia's distribution for multiple sources. Therefore, the misfit function is extremly simple and should have the form:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"\nfunction misfit(dsyn, dobs)\n fval = ...\n adjoint_source = ...\n return fval, adjoint_source\nend\n","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Where fval is the misfit value for a given pair of synthetic dsyn and observed dobs shot records and adjoint_source is the residual to be backpropagated (adjoint wave-equation source) for the computation of the adjoint state gradient. For example the standard ell_2 loss (default misfit in JUDI) is defined as","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"\nfunction mse(dsyn, dobs)\n fval = .5f0 * norm(dsyn - dobs)^2\n adjoint_source = syn - dobs\n return fval, adjoint_source\nend\n","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With this convention defined, we will now perform FWI with a few misfit functions and highlight the flexibility of this interface, including the possibility to use Julia's automatic differentiation frameworks to compute the adjoint souce of misfit functions difficult to differentiate by hand.","category":"page"},{"location":"tutorials/05_custom_misfit/#Inversion-setup","page":"FWI with user provided misfit function","title":"Inversion setup","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"using SegyIO, HDF5, PyPlot, JUDI, Random, LinearAlgebra, Printf, SlimPlotting, SlimOptim, Statistics","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Define some utilities for plotting\nsx(d::judiVector) = d.geometry.xloc[1][1]","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"sx (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"We load the FWI starting model from the HDF5 model file and set up the JUDI model structure:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"m0, n, d, o = read(h5open(\"overthrust_model.h5\",\"r\"),\"m0\",\"n\",\"d\",\"o\"); title(\"Starting model\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Model (n=(401, 121), d=(25.0f0, 25.0f0), o=(0.0f0, 0.0f0)) with parameters [:m]","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"plot_velocity(m0'.^(-.5), d)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Then we read the SEG-Y file containing our test data set. The data was generated with a 2D excerpt from the Overthrust velocity model and consists of 31 shot records with 2 seconds recording time. We load the data and set up a JUDI seismic data vector:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"block = segy_read(\"overthrust_shot_records.segy\");\nd_obs = judiVector(block);","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"┌ Warning: Fixed length trace flag set in stream: IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=7076688, maxsize=Inf, ptr=3601, mark=-1)\n└ @ SegyIO /Users/mathiaslouboutin/.julia/packages/SegyIO/qkvUT/src/read/read_file.jl:26","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Since the SEG-Y file contains the source coordinates, but not the wavelet itself, we create a JUDI Geometry structure for the source and then manually set up an 8 Hz Ricker wavelet. As for the observed data, we set up a JUDI seismic data vector q with the source geometry and wavelet:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"src_geometry = Geometry(block; key=\"source\");\nsrc_data = ricker_wavelet(src_geometry.t[1], src_geometry.dt[1], 0.008f0);\nq = judiVector(src_geometry, src_data);","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure(figsize=(7, 7))\nsuptitle(\"Observed data\")\nsubplot(221)\nplot_sdata(d_obs[1]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[1]))m\")\nsubplot(222)\nplot_sdata(d_obs[6]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[6]))m\")\nsubplot(223)\nplot_sdata(d_obs[11]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[11]))m\")\nsubplot(224)\nplot_sdata(d_obs[16]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[16]))m\")\ntight_layout()","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/#Misfit-functions","page":"FWI with user provided misfit function","title":"Misfit functions","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With the data and model defined, we can now define the misfit function we will be working with. For some of the considered functions, we will modify the observed data to highlight properties of a given misfit.","category":"page"},{"location":"tutorials/05_custom_misfit/#Predefined-misfit","page":"FWI with user provided misfit function","title":"Predefined misfit","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"JUDI provides two predefined misfits: the standard mse ell_2 loss and the studentst misfit that correspond to the Student's T Loss that has been shown to increase robustness against outliers in the data. The Student's T loss is defined as ","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"\nfunction studentst(dsyn, dobs; k=2)\n fval = sum(.5 * (k + 1) * log.(1 .+ (dsyn - dobs).^2 ./ k)\n adjoint_source = (k + 1) .* (dsyn - dobs) ./ (k .+ (dsyn - dobs).^2)\n return fval, adjoint_source\nend\n","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"where k is the number of degree of freedom (usually 1 or 2). We can see that in this case the adjoint source is pointwise normalized (in time and receiver position) which allows to mitigate outlier in the data such as incorrect amplitudes due to geophone mis-functionment. To illustrate this property, wee create an aritifical dataset with outliers to showcase the added robustness by artificially rescaling a few traces picked at random in every shot record of the dataset","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"d_outlier = deepcopy(d_obs)\n\n## Add outliers to the data\nfor s=1:d_outlier.nsrc\n # wrongly scale 10 traces\n nrec = d_outlier[s].geometry.nrec[1]\n inds = rand(1:nrec, 10)\n d_outlier.data[s][:, inds] .*= 20\nend","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure(figsize=(7, 7))\nsuptitle(\"Observed data with outliers\")\nsubplot(221)\nplot_sdata(d_outlier[1]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[1]))m\")\nsubplot(222)\nplot_sdata(d_outlier[6]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[6]))m\")\nsubplot(223)\nplot_sdata(d_outlier[11]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[11]))m\")\nsubplot(224)\nplot_sdata(d_outlier[16]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[16]))m\")\ntight_layout()","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/#Setup-inversion","page":"FWI with user provided misfit function","title":"Setup inversion","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With the data and misfit functions defined, we can now run the inversion. For this example, we will use SlimOptim's spectral projected gradient, a simple gradient base optimization method were we impose bound constraints on the model. For practical considerations, we will also work with a stochastic objective function that only computes the gradient on a random subset of shots at each iteration. This method has been show to be efficient for seismic inversion in particualr with advanced algorithms with constraints or on simpler illustrative examples such as this one. We will use 8 shots per iteration (50% of the dataset) and run 20 iterations of the algorithm.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"g_const = 0\nfunction objective_function(x, d_obs, misfit=mse)\n model0.m .= reshape(x,model0.n);\n\n # fwi function value and gradient\n i = randperm(d_obs.nsrc)[1:batchsize]\n fval, grad = fwi_objective(model0, q[i], d_obs[i]; misfit=misfit)\n # Normalize for nicer convergence\n g = grad.data\n # Mute water\n g[:, 1:20] .= 0f0\n g_const == 0 && (global g_const = 1/norm(g, Inf))\n return fval, g_const .* g\nend","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"objective_function (generic function with 2 methods)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Bound constraints based on initial velocity\nmmin = .95f0 * minimum(m0) * ones(Float32, n...)\nmmax = 1.1f0 * maximum(m0) * ones(Float32, n...)\n\n# Fix water layer\nmmin[:, 1:20] .= m0[1,1]\nmmax[:, 1:20] .= m0[1,1]\n\n# Bound projection\nproj(x) = reshape(median([vec(mmin) vec(x) vec(mmax)]; dims=2), model0.n)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"proj (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"batchsize = 8\nniter = 20\n\n# Setup SPG\noptions = spg_options(verbose=3, maxIter=niter, memory=3)\n\n# Compare l2 with students t on ideal data\nϕmse_ideal = x->objective_function(x, d_obs)\nϕst_ideal = x->objective_function(x, d_obs, studentst)\n\n# Compare l2 with students t on the data with outliers\nϕmse_out = x->objective_function(x, d_outlier)\nϕst_out = x->objective_function(x, d_outlier, studentst)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"#7 (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Perform the inversion\ng_const = 0\nsolmse_ideal = spg(ϕmse_ideal, m0, proj, options)\ng_const = 0\nsolst_ideal = spg(ϕst_ideal, m0, proj, options)\ng_const = 0\nsolmse_out = spg(ϕmse_out, m0, proj, options)\ng_const = 0\nsolst_out = spg(ϕst_out, m0, proj, options)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Running SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 2.66752e+05 4.24512e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 1.83654e+05 3.97694e-01\n 2 4 4 9 1.00000e+00 3.86448e-02 1.68483e+05 4.46593e-01\n 3 6 6 12 1.00000e+00 2.37916e-02 9.82918e+04 4.21356e-01\n 4 8 8 15 1.00000e+00 2.00663e-02 7.17603e+04 4.30578e-01\n 5 10 10 18 1.00000e+00 1.80122e-02 5.64357e+04 1.52451e-01\n 6 12 12 21 1.00000e+00 1.59804e-02 4.64412e+04 3.24789e-01\n 7 14 14 24 1.00000e+00 1.49305e-02 4.02634e+04 2.00507e-01\n 8 16 16 27 1.00000e+00 1.84277e-02 3.24419e+04 3.97697e-01\n 9 18 18 30 1.00000e+00 1.88020e-02 3.26324e+04 1.51282e-01\n 10 20 20 33 1.00000e+00 1.44802e-02 3.01499e+04 3.31139e-01\n 11 22 22 36 1.00000e+00 1.05926e-02 2.65702e+04 1.77554e-01\n 12 24 24 39 1.00000e+00 8.83888e-03 1.99885e+04 1.48811e-01\n 13 26 26 42 1.00000e+00 1.04110e-02 2.36893e+04 1.68137e-01\n 14 28 28 45 1.00000e+00 1.58912e-02 1.87623e+04 1.55777e-01\n 15 30 30 48 1.00000e+00 1.88643e-02 1.78986e+04 8.86404e-02\n 16 32 32 51 1.00000e+00 1.56725e-02 1.71706e+04 2.11131e-01\n 17 34 34 54 1.00000e+00 1.29173e-02 1.53461e+04 7.06666e-02\n 18 36 36 57 1.00000e+00 1.32459e-02 1.62717e+04 6.97212e-02\n 19 38 38 60 1.00000e+00 2.35474e-02 1.33874e+04 2.31529e-01\n 20 41 41 63 1.00000e-01 2.34768e-02 1.57433e+04 1.03599e-01\nRunning SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 2.65220e+05 4.33497e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 2.63573e+05 3.96120e-01\n 2 5 5 9 1.00000e-01 2.87833e-02 2.04122e+05 4.08234e-01\n 3 8 8 12 1.00000e-01 1.14303e-01 1.46186e+05 4.51432e-01\n 4 10 10 15 1.00000e+00 6.83298e-03 1.14790e+05 4.35991e-01\n 5 12 12 18 1.00000e+00 7.10424e-03 1.09469e+05 4.49633e-01\n 6 14 14 21 1.00000e+00 6.74102e-03 8.16012e+04 4.49592e-01\n 7 16 16 24 1.00000e+00 5.73175e-03 7.39588e+04 4.15352e-01\n 8 18 18 27 1.00000e+00 6.79955e-03 6.74752e+04 4.01064e-01\n 9 20 20 30 1.00000e+00 8.90642e-03 6.10524e+04 4.09543e-01\n 10 22 22 33 1.00000e+00 6.13604e-03 6.80915e+04 4.43890e-01\n 11 24 24 36 1.00000e+00 4.03840e-03 4.76625e+04 3.86592e-01\n 12 26 26 39 1.00000e+00 6.17972e-03 4.52775e+04 4.45348e-01\n 13 28 28 42 1.00000e+00 4.58222e-03 3.83993e+04 3.40792e-01\n 14 30 30 45 1.00000e+00 4.33826e-03 3.45115e+04 3.76462e-01\n 15 32 32 48 1.00000e+00 7.44435e-03 3.33236e+04 4.13865e-01\n 16 34 34 51 1.00000e+00 8.23616e-03 3.66736e+04 4.48623e-01\n 17 36 36 54 1.00000e+00 3.59586e-03 3.03236e+04 3.60418e-01\n 18 38 38 57 1.00000e+00 2.77100e-03 2.72704e+04 3.36501e-01\n 19 40 40 60 1.00000e+00 3.27021e-03 2.44597e+04 3.00182e-01\n 20 42 42 63 1.00000e+00 5.67399e-03 2.58941e+04 2.96022e-01\nRunning SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 1.44941e+08 3.99644e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 1.43838e+08 3.93633e-01\n 2 4 4 9 1.00000e+00 1.16882e-01 1.20535e+08 3.92942e-01\n 3 6 6 12 1.00000e+00 1.56330e-01 1.00449e+08 3.48297e-01\n 4 8 8 15 1.00000e+00 3.67119e-01 7.55863e+07 3.48005e-01\n 5 10 10 18 1.00000e+00 3.19012e-01 1.08780e+08 3.90391e-01\n 6 13 13 21 1.00000e-01 3.74207e-01 9.95976e+07 4.11056e-01\n 7 15 15 24 1.00000e+00 1.00220e-01 8.43423e+07 3.17958e-01\n 8 24 24 27 1.00000e-07 1.14440e-01 1.01919e+08 3.01010e-01\n 9 26 26 30 1.00000e+00 1.56984e-08 8.62008e+07 3.17958e-01\n 10 36 36 33 1.00000e-08 3.33508e-08 8.25621e+07 3.50130e-01\nStep size: 2.98e-16 below progTol: 1.00e-10\nRunning SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 5.46581e+05 4.47720e-01\n 1 9 9 6 1.00000e-07 4.44444e-02 5.44028e+05 4.34094e-01\n 2 12 12 9 1.00000e-01 9.44714e-08 5.32362e+05 4.26830e-01\n 3 14 14 12 1.00000e+00 2.12196e-08 5.42095e+05 4.26773e-01\n 4 16 16 15 1.00000e+00 1.94585e-07 5.42074e+05 4.32777e-01\n 5 21 21 18 1.00000e-03 6.28857e-07 5.29497e+05 4.29520e-01\n 6 25 25 21 1.00000e-02 1.00000e+00 5.06206e+05 4.23433e-01\n 7 27 27 24 1.00000e+00 1.54459e-02 4.13344e+05 4.08260e-01\n 8 29 29 27 1.00000e+00 2.72312e-02 4.15654e+05 4.51937e-01\n 9 31 31 30 1.00000e+00 1.83427e-02 5.03455e+05 4.41503e-01\n 10 40 40 33 1.00000e-07 1.39864e-02 4.97702e+05 3.91456e-01\n 11 48 48 36 1.00000e-06 9.34530e-09 4.96147e+05 4.39331e-01\nStep size: 1.49e-14 below progTol: 1.00e-10\n\n\n\n\n\nresult{Float32}(Float32[0.44444445 0.44444445 … 0.055672385 0.054975696; 0.44444445 0.44444445 … 0.05563018 0.05495146; … ; 0.44444445 0.44444445 … 0.05432263 0.05296257; 0.44444445 0.44444445 … 0.054288656 0.05309218], Float32[0.0 0.0 … 0.03932687 0.051932618; 0.0 0.0 … 0.03822797 0.05325304; … ; 0.0 0.0 … 0.006757668 0.0029647883; 0.0 0.0 … 0.0061980807 0.0022367856], 413343.9f0, Float32[546580.56, 544027.9, 532361.8, 542095.3, 542073.7, 529497.06, 506206.3, 413343.9, 415653.62, 503455.06, 497702.1, 496147.38], Matrix{Float32}[[0.44444445 0.44444445 … 0.05585606 0.05512348; 0.44444445 0.44444445 … 0.05584115 0.055107832; … ; 0.44444445 0.44444445 … 0.055858355 0.05516676; 0.44444445 0.44444445 … 0.05585274 0.05516145]], 36, 48, 48)","category":"page"},{"location":"tutorials/05_custom_misfit/#Results","page":"FWI with user provided misfit function","title":"Results","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"We can now look at the inversion results for these two datasets and two misfit functions. We show all four results below. We can see that in the case of ideal data, we invert for the velocity fairly easily in both cases as expected and obtain an accurate reconstruction.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"We obsereved on the second row that, as expected from the litterature, the student's t misift provides a very robust inversion in the presence of outlier and that the inverted velocity is as good as with ideal data. On the other hand, the standard mse misfits fails to lead to an acceptable inverted velocity in the presence of outliers as these outliers will lead to the main contribution to the gradient. These artifacts, once present in the velocity model after the few first iteration, cannot be recovered from.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure(figsize=(14, 7))\nsuptitle(\"FWI result\")\nsubplot(221)\nplot_velocity(reshape(solmse_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Ideal data, mse\")\nsubplot(222)\nplot_velocity(reshape(solst_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Ideal data, student's t\")\nsubplot(223)\nplot_velocity(reshape(solmse_out.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Data with outliers, mse\")\nsubplot(224)\nplot_velocity(reshape(solst_out.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Data with outliers, student's t\")\ntight_layout()","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure()\nplot(solmse_ideal.ϕ_trace ./ solmse_ideal.ϕ_trace[1], label=\"Ideal mse\")\nplot(solst_ideal.ϕ_trace ./ solst_ideal.ϕ_trace[1], label=\"Ideal student's t\")\nplot(solmse_out.ϕ_trace ./ solmse_out.ϕ_trace[1], label=\"Outlier mse\")\nplot(solst_out.ϕ_trace ./ solst_out.ϕ_trace[1], label=\"Outlier student's t\")\nxlabel(\"iteration\")\nylabel(\"Normalized misfit\")\ntitle(\"Convergence\")","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"PyObject Text(0.5, 1.0, 'Convergence')","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"## Custom misfits","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Finally, we now introduce how to use custom misfit for inversion. As illustrated above, the misfit is a simple keyword argument to the fwi_objective/lsrtm_objective function and consequently, any function returning the misfit value and adjoint source can be provided. In particular, users can take advantage of the extensive automatic differentiation ecosystem in Julia to define complicated misfit functions and automatically derive the corresponding adjoint source. ","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In the following, as an illustrative example, we define the misfit for Envelope FWI and let Zygote computes its derivative with respect to the synthetic data. We add a little bit of complexity to the misfit function by normalizing the data by its ell_2 norm as well to demonstrate how we can layer complexities in the misfit function and let the automatic differentiation infer the adjoint source.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"using FFTW, Zygote\n\nn2(x) = x / norm(x, 2)\n\nfunction Hilbert(x)\n n = size(x, 1)\n σ = sign.(-n/2+1:n/2)\n y = imag(ifft(fftshift(σ.*fftshift(fft(x, 1), 1), 1), 1))\n return y\nend\n\nHLoss(dsyn, dobs) = sum(abs2.((dsyn - dobs) .+ 1im .* Hilbert(dsyn - dobs)))\n\nfunction envelope(dsyn, dobs)\n ϕ = HLoss(n2(dsyn), n2(dobs))\n g = gradient(xs -> HLoss(n2(xs), n2(dobs)), dsyn)\n # Zygote always returns a tuple, in this case (g,)\n return ϕ, real.(g[1])\nend","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"┌ Info: Precompiling Zygote [e88e6eb3-aa80-5325-afca-941959d7151f]\n└ @ Base loading.jl:1662\n\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mModule DiffRules with build ID 773862253559751 is missing from the cache.\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39mThis may mean DiffRules [b552c78f-8df3-52c6-915a-8e097449b14b] does not support precompilation but is imported by a module that does.\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Base loading.jl:1325\u001b[39m\n┌ Info: Skipping precompilation since __precompile__(false). Importing Zygote [e88e6eb3-aa80-5325-afca-941959d7151f].\n└ @ Base loading.jl:1341\n\n\n\n\n\nenvelope (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With our loss defined we can now rerun FWI with it","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Compare l2 with students t on ideal data\nϕh_ideal = x->objective_function(x, d_obs, envelope)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"#11 (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"g_const = 0\nsolh_ideal = spg(ϕh_ideal, m0, proj, options)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Running SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 9.89023e-01 4.26088e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 5.96211e-01 3.97723e-01\n 2 4 4 9 1.00000e+00 3.22405e-02 5.85686e-01 4.51937e-01\n 3 6 6 12 1.00000e+00 2.09030e-02 3.81984e-01 4.51005e-01\n 4 8 8 15 1.00000e+00 1.60157e-02 2.25568e-01 3.97775e-01\n 5 10 10 18 1.00000e+00 1.59596e-02 1.75072e-01 3.59442e-01\n 6 12 12 21 1.00000e+00 2.15618e-02 1.56823e-01 2.05124e-01\n 7 14 14 24 1.00000e+00 2.46165e-02 1.42123e-01 3.35674e-01\n 8 16 16 27 1.00000e+00 2.11885e-02 1.17001e-01 1.94726e-01\n 9 18 18 30 1.00000e+00 1.20896e-02 1.11277e-01 4.14274e-01\n 10 20 20 33 1.00000e+00 7.33151e-03 9.26926e-02 2.81033e-01\n 11 22 22 36 1.00000e+00 6.84483e-03 7.99434e-02 3.38496e-01\n 12 24 24 39 1.00000e+00 9.87291e-03 7.37838e-02 2.56689e-01\n 13 26 26 42 1.00000e+00 1.70487e-02 7.32610e-02 9.64325e-02\n 14 28 28 45 1.00000e+00 1.53908e-02 7.61458e-02 2.71101e-01\n 15 30 30 48 1.00000e+00 1.21645e-02 5.53087e-02 1.33696e-01\n 16 32 32 51 1.00000e+00 9.74719e-03 5.92018e-02 1.71851e-01\n 17 34 34 54 1.00000e+00 8.90081e-03 5.74107e-02 9.27853e-02\n 18 36 36 57 1.00000e+00 7.97771e-03 4.89929e-02 1.21926e-01\n 19 38 38 60 1.00000e+00 8.40343e-03 5.05762e-02 8.61898e-02\n 20 40 40 63 1.00000e+00 9.53746e-03 4.93289e-02 1.69757e-01\n\n\n\n\n\nresult{Float32}(Float32[0.44444445 0.44444445 … 0.053637963 0.052456174; 0.44444445 0.44444445 … 0.053673286 0.052389987; … ; 0.44444445 0.44444445 … 0.05293595 0.05135409; 0.44444445 0.44444445 … 0.052959688 0.051626943], Float32[0.0 0.0 … 0.0014253161 0.0031370732; 0.0 0.0 … 0.0007196072 0.0023543844; … ; 0.0 0.0 … 0.014447195 0.01871568; 0.0 0.0 … 0.013791027 0.017101452], 0.04899293f0, Float32[0.98902273, 0.5962111, 0.5856857, 0.38198355, 0.22556786, 0.17507246, 0.15682286, 0.14212339, 0.11700058, 0.111277334 … 0.079943426, 0.073783785, 0.073261, 0.07614578, 0.05530872, 0.059201777, 0.057410687, 0.04899293, 0.050576232, 0.04932886], Matrix{Float32}[[0.44444445 0.44444445 … 0.05585606 0.05512348; 0.44444445 0.44444445 … 0.05584115 0.055107832; … ; 0.44444445 0.44444445 … 0.055858355 0.05516676; 0.44444445 0.44444445 … 0.05585274 0.05516145]], 63, 40, 40)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"suptitle(\"FWI result\")\nplot_velocity(reshape(solh_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Ideal data, Envelope\")","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In this tutorial, we have seen how misfit functions can impact the inversion result and how JUDI provides multiple misfit functions and a flexible interface for custom user inputs. This interface will allow for better application to real worl dataset since, as show in our example suite, JUDI already provide an interface for handling large SegY datasets and interfaces tricially with optimization frameworks","category":"page"},{"location":"helper/#Helper-functions","page":"Helper Functions","title":"Helper functions","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"JUDI provides numerous helper and utility functions need for seismic modeling and inversion.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Pages = [\"helper.md\"]","category":"page"},{"location":"helper/#Ricker-wavelet","page":"Helper Functions","title":"Ricker wavelet","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Create a 1D Ricker wavelet:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"ricker_wavelet(tmax, dt, f0; t0=nothing)","category":"page"},{"location":"helper/#Compute-CFL-time-stepping-interval","page":"Helper Functions","title":"Compute CFL time stepping interval","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Calculate the time stepping interval based on the CFL condition","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"calculate_dt","category":"page"},{"location":"helper/#Compute-number-of-computational-time-steps","page":"Helper Functions","title":"Compute number of computational time steps","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Estimate the number of computational time steps. Required for calculating the dimensions of the matrix-free linear modeling operators:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"get_computational_nt","category":"page"},{"location":"helper/#JUDI.get_computational_nt","page":"Helper Functions","title":"JUDI.get_computational_nt","text":"get_computational_nt(srcGeometry, recGeoemtry, model; dt=nothing)\n\nEstimate the number of computational time steps. Required for calculating the dimensions\nof the matrix-free linear modeling operators. srcGeometry and recGeometry are source\nand receiver geometries of type Geometry and model is the model structure of type \nModel.\n\n\n\n\n\nget_computational_nt(Geoemtry, model; dt=nothing)\n\nEstimate the number of computational time steps. Required for calculating the dimensions\nof the matrix-free linear modeling operators. srcGeometry and recGeometry are source\nand receiver geometries of type Geometry and model is the model structure of type \nModel.\n\n\n\n\n\n","category":"function"},{"location":"helper/#Set-up-3D-acquisition-grid","page":"Helper Functions","title":"Set up 3D acquisition grid","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Helper function to create a regular acquisition grid for a 3D survey.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"setup_3D_grid","category":"page"},{"location":"helper/#Data-interpolation","page":"Helper Functions","title":"Data interpolation","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Time interpolation for source/receiver data using splines. For modeling, the data is interpolated automatically onto the computational time axis, so generally, these functions are not needed for users.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"time_resample","category":"page"},{"location":"helper/#JUDI.time_resample","page":"Helper Functions","title":"JUDI.time_resample","text":"time_resample(data, geometry_in, dt_new)\n\nResample the input data with sinc interpolation from the current time sampling (geometrtyin) to the new time sampling `dtnew`.\n\nParameters\n\ndata: Data to be reampled. If data is a matrix, resamples each column.\ngeometry_in: Geometry on which data is defined.\ndt_new: New time sampling rate to interpolate onto.\n\n\n\n\n\ntime_resample(data, dt_in, dt_new)\n\nResample the input data with sinc interpolation from the current time sampling dtin to the new time sampling `dtnew`.\n\nParameters\n\ndata: Data to be reampled. If data is a matrix, resamples each column.\ndt_in: Time sampling of input\ndt_new: New time sampling rate to interpolate onto.\n\n\n\n\n\ntime_resample(data, dt_in, geometry_in)\n\nResample the input data with sinc interpolation from the current time sampling (dtin) to the new time sampling `geometryout`.\n\nParameters\n\ndata: Data to be reampled. If data is a matrix, resamples each column.\ngeometry_out: Geometry on which data is to be interpolated.\ndt_in: Time sampling rate of the data.\n\n\n\n\n\n","category":"function"},{"location":"helper/#Generate-and-sample-from-frequency-distribution","page":"Helper Functions","title":"Generate and sample from frequency distribution","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Create a probability distribution with the shape of the source spectrum from which we can draw random frequencies.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"generate_distribution\nselect_frequencies","category":"page"},{"location":"helper/#JUDI.generate_distribution","page":"Helper Functions","title":"JUDI.generate_distribution","text":"generate_distribution(x; src_no=1)\n\nGenerates a probability distribution for the discrete input judiVector x.\n\nParameters\n\nx: judiVector. Usualy a source with a single trace per source position.\nsrc_no: Index of the source to select out of x\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.select_frequencies","page":"Helper Functions","title":"JUDI.select_frequencies","text":"select_frequencies(q_dist; fmin=0f0, fmax=Inf, nf=1)\n\nSelects nf frequencies based on the source distribution q_dist computed with generate_distribution.\n\nParameters\n\nq_dist: Distribution to sample from.\nf_min: Minimum acceptable frequency to sample (defaults to 0).\nf_max: Maximum acceptable frequency to sample (defaults to Inf).\nfd: Number of frequnecies to sample (defaults to 1).\n\n\n\n\n\n","category":"function"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"We can draw random samples from dist by passing it values between 0 and 1:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"# Draw a single random frequency\nf = dist(rand(1))\n\n# Draw 10 random frequencies\nf = dist(rand(10))","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Alternatively, we can use the function:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"f = select_frequencies(dist; fmin=0f0, fmax=Inf, nf=1)","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"to draw nf number of frequencies for a given distribution dist in the frequency range of fmin to fmax (both in kHz).","category":"page"},{"location":"helper/#Read-data-from-out-of-core-container","page":"Helper Functions","title":"Read data from out of core container","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"In the case where a judiVector is out of core (points to a segy file) it is possible to convert it or part of it into an in core judiVecor with the get_data function.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"d_ic = get_data(d_ooc, inds)","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"where inds is either a single index, a list of index or a range of index.","category":"page"},{"location":"helper/#Restrict-model-to-acquisition","page":"Helper Functions","title":"Restrict model to acquisition","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"In practice, and in particular for marine data, the aperture of a single shot is much smaller than the full model size. We provide a function (automatically used when the option limit_m is set in Options) that limits the model to the acquisition area.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"limit_model_to_receiver_area","category":"page"},{"location":"helper/#JUDI.limit_model_to_receiver_area","page":"Helper Functions","title":"JUDI.limit_model_to_receiver_area","text":"limit_model_to_receiver_area(srcGeometry, recGeometry, model, buffer; pert=nothing)\n\nCrops the model to the area of the source an receiver with an extra buffer. This reduces the size of the problem when the model si large and the source and receiver located in a small part of the domain.\n\nIn the cartoon below, the full model will be cropped to the center area containg the source (o) receivers (x) and buffer area (*)\n\no Source position\nx receiver positions\nExtra buffer (grid spacing in that simple case)\n\n\n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n| . . . . * * * * * * * * * * * * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x o x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * * * * * * * * * * * * . . . . . | \n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n\n\nParameters\n\nsrcGeometry: Geometry of the source.\nrecGeometry: Geometry of the receivers.\nmodel: Model to be croped.\nbuffer: Size of the buffer on each side.\npert: Model perturbation (optional) to be cropped as well.\n\n\n\n\n\n","category":"function"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"We also provide it's complement that removes receivers outside of the computational domain if the dataset contains locations that are not wanted","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"remove_out_of_bounds_receivers","category":"page"},{"location":"helper/#JUDI.remove_out_of_bounds_receivers","page":"Helper Functions","title":"JUDI.remove_out_of_bounds_receivers","text":"remove_out_of_bounds_receivers(recGeometry, model)\n\nRemoves receivers that are positionned outside the computational domain defined by the model.\n\nParameters\n\nrecGeometry: Geometry of receivers in which out of bounds will be removed.\nmodel: Model defining the computational domain.\n\n\n\n\n\nremove_out_of_bounds_receivers(recGeometry, recData, model)\n\nRemoves receivers that are positionned outside the computational domain defined by the model.\n\nParameters\n\nrecGeometry: Geometry of receivers in which out of bounds will be removed.\nrecData: Shot record for that geometry in which traces will be removed.\nmodel: Model defining the computational domain.\n\n\n\n\n\n","category":"function"},{"location":"helper/#Additional-miscellanous-utilities","page":"Helper Functions","title":"Additional miscellanous utilities","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"devito_model\nsetup_grid\npad_sizes\npad_array\nremove_padding\nconvertToCell\nprocess_input_data\nreshape\ntransducer\nas_vec","category":"page"},{"location":"helper/#JUDI.devito_model","page":"Helper Functions","title":"JUDI.devito_model","text":"devito_model(model, options;dm=nothing)\n\nCreates a python side model strucutre for devito.\n\nParameters\n\nmodel: JUDI Model structure.\noptions: JUDI Options structure.\ndm: Squared slowness perturbation (optional), Array or PhysicalParameter.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.setup_grid","page":"Helper Functions","title":"JUDI.setup_grid","text":"setup_grid(geometry, n)\n\nSets up the coordinate arrays for Devito.\n\nParameters:\n\ngeometry: Geometry containing the coordinates\nn: Domain size\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.pad_sizes","page":"Helper Functions","title":"JUDI.pad_sizes","text":"pad_sizes(model, options; so=nothing)\n\nComputes ABC padding sizes according to the model's numbr of abc points and spatial order\n\nParameters\n\nmodel: JUDI or Python side Model.\noptions: JUDI Options structure.\nso: Space order (optional) defaults to options.space_order.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.pad_array","page":"Helper Functions","title":"JUDI.pad_array","text":"pad_array(m, nb; mode=:border)\n\nPads to the input array with either copying the edge value (:border) or zeros (:zeros)\n\nParameters\n\nm: Array to be padded.\nnb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.\nmode: Padding mode (optional), defaults to :border.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.remove_padding","page":"Helper Functions","title":"JUDI.remove_padding","text":"remove_padding(m, nb; true_adjoint=False)\n\nRemoves the padding from array m. This is the adjoint of pad_array.\n\nParameters\n\nm: Array to remvove padding from.\nnb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.\ntrue_adjoint: Unpadding mode, defaults to False. Will sum the padding to the edge point with true_adjoint=true\n\nand should only be used this way for adjoint testing purpose.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.convertToCell","page":"Helper Functions","title":"JUDI.convertToCell","text":"convertToCell(x)\n\nConvert an array x to a cell array (Array{Any,1}) with length(x) entries,\nwhere the i-th cell contains the i-th entry of x.\n\nParameters\n\nx: Array to be converted into and array of array\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.process_input_data","page":"Helper Functions","title":"JUDI.process_input_data","text":"process_input_data(input, geometry, nsrc)\n\nPreprocesses input Array into an Array of Array for modeling\n\nParameters:\n\ninput: Input to preprocess.\ngeometry: Geometry containing physical parameters.\nnsrc: Number of sources\n\n\n\n\n\nprocess_input_data(input, model, nsrc)\n\nPreprocesses input Array into an Array of Array for modeling\n\nParameters:\n\ninput: Input to preprocess.\nmodel: Model containing physical parameters.\nnsrc: Number of sources\n\n\n\n\n\n","category":"function"},{"location":"helper/#Base.reshape","page":"Helper Functions","title":"Base.reshape","text":"reshape(A, dims...) -> AbstractArray\nreshape(A, dims) -> AbstractArray\n\nReturn an array with the same data as A, but with different dimension sizes or number of dimensions. The two arrays share the same underlying data, so that the result is mutable if and only if A is mutable, and setting elements of one alters the values of the other.\n\nThe new dimensions may be specified either as a list of arguments or as a shape tuple. At most one dimension may be specified with a :, in which case its length is computed such that its product with all the specified dimensions is equal to the length of the original array A. The total number of elements must not change.\n\nExamples\n\njulia> A = Vector(1:16)\n16-element Vector{Int64}:\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n 10\n 11\n 12\n 13\n 14\n 15\n 16\n\njulia> reshape(A, (4, 4))\n4×4 Matrix{Int64}:\n 1 5 9 13\n 2 6 10 14\n 3 7 11 15\n 4 8 12 16\n\njulia> reshape(A, 2, :)\n2×8 Matrix{Int64}:\n 1 3 5 7 9 11 13 15\n 2 4 6 8 10 12 14 16\n\njulia> reshape(1:6, 2, 3)\n2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:\n 1 3 5\n 2 4 6\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.transducer","page":"Helper Functions","title":"JUDI.transducer","text":"transducer(q, d, r, theta)\n\nCreate the JUDI soure for a circular transducer Theta=0 points downward:\n\n. . . . - - - . . . . . .\n\n. . . . + + + . . . . . .\n\n. . . . . . . . . . . . .\n\n. . . . . . . . . . . . .\n\nTheta=pi/2 points right:\n\n. . . . - + . . . . . . .\n\n. . . . - + . . . . . . .\n\n. . . . - + . . . . . . .\n\n. . . . . . . . . . . . .\n\n2D only, to extend to 3D\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.as_vec","page":"Helper Functions","title":"JUDI.as_vec","text":"as_vec(x, ::Val{Bool})\n\nVectorizes output when return_array is set to true.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/imaging_conditions/#Imaging-conditions-in-JUDI","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"title: Overview of JUDI imaging conditions for inversion author: Mathias Louboutin date: October 2022 –-","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"This example script is written using Weave.jl and can be converted to different format for documentation and usage This example is converted to a markdown file for the documentation.","category":"page"},{"location":"tutorials/imaging_conditions/#Import-JUDI,-Linear-algebra-utilities-and-Plotting","page":"Imaging conditions in JUDI","title":"Import JUDI, Linear algebra utilities and Plotting","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"using JUDI, LinearAlgebra, PyPlot","category":"page"},{"location":"tutorials/imaging_conditions/#Create-a-JUDI-model-structure","page":"Imaging conditions in JUDI","title":"Create a JUDI model structure","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"In JUDI, a Model structure contains the grid information (origin, spacing, number of gridpoints) and the physical parameters. The squared slowness is always required as the base physical parameter for propagation. In addition, JUDI supports additional physical representations. First we accept density that can either be a direct input Model(n, d, o, m, rho) or an optional keyword argument Model(n,d,o,m;rho=rho). Second, we also provide VTI/TTI kernels parametrized by the THomsen parameters that can be input as keyword arguments Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta,theta=theta,phi=phi). Because the thomsen parameters are optional the propagator wil lonloy use the ones provided. For example Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta) will infer a VTI propagation","category":"page"},{"location":"tutorials/imaging_conditions/#Create-discrete-parameters","page":"Imaging conditions in JUDI","title":"Create discrete parameters","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Set up model structure\nn = (601, 151) # (x,y,z) or (x,z)\nd = (10f0, 10f0)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32,n) .+ 0.5f0\nv0 = ones(Float32,n) .+ 0.51f0\nv[:, 101:end] .= 2f0\nv0[:, 101:end] .= 2f0\n\n# Slowness squared [s^2/km^2]\nm = (1f0 ./ v).^2\nm0 = (1f0 ./ v0).^2\n\n# Setup model structure\nnsrc = 1\t# number of sources\nmodel = Model(n, d, o, m)\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"Model (n=(601, 151), d=(10.0f0, 10.0f0), o=(0.0f0, 0.0f0)) with parameters \n(:m, :rho)","category":"page"},{"location":"tutorials/imaging_conditions/#Create-acquisition-geometry","page":"Imaging conditions in JUDI","title":"Create acquisition geometry","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"In this simple usage example, we create a simple acquisiton by hand. In practice the acquisition geometry will be defined by the dataset beeing inverted. We show in a spearate tutorial how to use SegyIO.jl to handle SEGY seismic datasets in JUDI.","category":"page"},{"location":"tutorials/imaging_conditions/#Create-source-and-receivers-positions-at-the-surface","page":"Imaging conditions in JUDI","title":"Create source and receivers positions at the surface","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Set up receiver geometry\nxrec = [1 * (n[1] - 1) * d[1] / 4]\nyrec = [0f0] # WE have to set the y coordiante to zero (or any number) for 2D modeling\nzrec = [d[1]]\n\n# receiver sampling and recording time\ntimeD = 2500f0 # receiver recording time [ms]\ndtD = 4f0 # receiver sampling interval [ms]\n\n# Set up receiver structure\nrecGeometry = Geometry(xrec, yrec, zrec; dt=dtD, t=timeD, nsrc=nsrc)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"GeometryIC{Float32} wiht 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"The source geometry is a but different. Because we want to create a survey with nsrc shot records, we need to convert the vector of sources postions [s0, s1, ... sn] into an array of array [[s0], [s1], ...] so that JUDI understands that this is a set of indepednet nsrc","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"xsrc = 3 * (n[1] - 1) * d[1] / 4\nysrc = 0f0\nzsrc = d[1]\n\n# Set up source structure\nsrcGeometry = Geometry(xsrc, ysrc, zsrc; dt=dtD, t=timeD)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"GeometryIC{Float32} wiht 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/#Source-judiVector","page":"Imaging conditions in JUDI","title":"Source judiVector","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"Finally, with the geometry defined, we can create a source wavelet (a simple Ricker wavelet here) a our first judiVector In JUDI, a judiVector is the core structure that represent a acquisition-geometry based dataset. This structure encapsulate the physical locations (trace coordinates) and corrsponding data trace in a source-based structure. for a given judiVector d then d[1] will be the shot record for the first source, or in the case of the source term, the first source wavelet and its positon.","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# setup wavelet\nf0 = 0.015f0 # kHz\nwavelet = ricker_wavelet(timeD, dtD, f0)\nq = judiVector(srcGeometry, wavelet)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"judiVector{Float32, Matrix{Float32}} with 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/#Modeling","page":"Imaging conditions in JUDI","title":"Modeling","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"With our survey and subsurface model setup, we can now model and image seismic data. Linear Operators The core idea behind JUDI is to abstract seismic inverse problems in term of linear algebra. In its simplest form, seismic inversion can be formulated as","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"undersetmathbfmtextargmin phi(mathbfm) = frac12 mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd _2^2 \ntext \nnabla_mathbfm phi(mathbfm) = mathbfJ(mathbfm mathbfq)^top (mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"where mathbfP_r is the receiver projection (measurment operator) and mathbfP_s^top is the source injection operator (adjoint of measurment at the source location). Therefore, we bastracted these operation to be able to define these operators","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Setup operators\nF = judiModeling(model, srcGeometry, recGeometry)\nF0 = judiModeling(model0, srcGeometry, recGeometry)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"JUDI forward{Float32} propagator (src * rec * time) -> (src * rec * time)","category":"page"},{"location":"tutorials/imaging_conditions/#Model-and-image-data","page":"Imaging conditions in JUDI","title":"Model and image data","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"We first model synthetic data using our defined source and true model ","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Nonlinear modeling\ndobs = F*q","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"judiVector{Float32, Matrix{Float32}} with 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/#Inversion","page":"Imaging conditions in JUDI","title":"Inversion","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"Our main goal is to provide an inversion framework for seismic inversion. In this tutorial, we highlight the different imaging conditions available in JUDI. These imaging conditions are designed to enhanced properties in the gradient beneficial to the inversion problem such as the frequency content. Because these imaging conditions are intended tho be used in potential least-square problems, we also implemented (and test as part of our CI) their adjoint such that we can model linearized data with the JAcobian that correspond to the adjoint of the modified adjoint state crosscorelation imaging condition.","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"We compute now the FWI gradient with three different imaging conditions:","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"\"as\" adjoint state imaging condition. This is the conventional cross-correlation adjoint state gradient.\n\"isic\" that computes the inverse scattering imaging condition designed to provide reflections for LSRTM (high frequency content)\n\"FWI\" that computes the complement of \"isic\" and brings up the low frequency content for velocity inversion","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"These can be specified in the Option structure via IC=\"as/isic/fwi","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"fas, g_as = fwi_objective(model0, q, 0*dobs; options=Options(IC=\"as\", space_order=12))\nfisic, g_isic = fwi_objective(model0, q, 0*dobs; options=Options(IC=\"isic\", space_order=12))\nffwi, g_fwi = fwi_objective(model0, q, 0*dobs; options=Options(IC=\"fwi\", space_order=12))","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"(32.991665f0, PhysicalParameter{Float32, 2} of size (601, 151) with origin \n(0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"We show below the sensitivity kernels for a single source-receiver pair highlighting the inversion properties of these imaging conditions","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"ni(x) = 10 * x ./ norm(x, Inf)\n\nfig = figure(figsize=(8, 12))\nsubplot(311)\nimshow(ni(g_isic'), cmap=\"seismic\", aspect=\"auto\", vmin=-1, vmax=1)\ntitle(\"ISIC\")\nsubplot(312)\nimshow(ni(g_fwi'), cmap=\"seismic\", aspect=\"auto\", vmin=-1, vmax=1)\ntitle(\"FWI\")\nsubplot(313)\nimshow(ni(g_as'), cmap=\"seismic\", aspect=\"auto\", vmin=-1, vmax=1)\ntitle(\"Adjoint State\")\ntight_layout()\ndisplay(fig)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"(Image: )","category":"page"},{"location":"preconditioners/#Seismic-Preconditioners","page":"Preconditioners","title":"Seismic Preconditioners","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"JUDI provides a selected number of preconditioners known to be beneficial to FWI and RTM. We welcome additional preconditionners from the community. Additionnaly, any JOLI operator can be used as a preconditiner in conbination with JUDI operator thanks to the fundamental interface between JUDI and JOLI.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Pages = [\"preconditioners.md\"]","category":"page"},{"location":"preconditioners/#Model-domain-preconditioners","page":"Preconditioners","title":"Model domain preconditioners","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Model space preconditioners acts on model size arrays such as the velocity model or the FWI/RTM gradient. These preconditioners are indepenedent of the number of sources and therefore should not be indexed.","category":"page"},{"location":"preconditioners/#Water-column-muting-(top-mute)","page":"Preconditioners","title":"Water column muting (top mute)","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Create a linear operator for a 2D model topmute, i.e. for muting the water column:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"TopMute","category":"page"},{"location":"preconditioners/#JUDI.TopMute","page":"Preconditioners","title":"JUDI.TopMute","text":"TopMute{T, N, Nw}\n\nMute top of the model in N dimensions\n\nConstructor\n\njudiTopmute(model; taperwidht=10)\njudiTopmute(n, wb, taperwidth) # Legacy\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Usage:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"# Forward\nm_mute = Mr*vec(m)\n\n# Adjoint\nm_mute = Mr'*vec(m)","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"As Mr is self adjoint, Mr is equal to Mr'.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"legacy:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"The legacy constructor judiTopmute(n, wb, taperwidth) is still available to construct a muting operator with user specified muting depth.","category":"page"},{"location":"preconditioners/#Model-depth-scaling","page":"Preconditioners","title":"Model depth scaling","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Create a 2D model depth scaling. This preconditionenr is the most simple form of inverse Hessain approximation compensating for illumination in the subsurface. We also describe below a more accurate diagonal approximation of the Hessian with the illlumination operator. Additionnaly, as a simple diagonal approximation, this operator is invertible and can be inverted with the standard julia inv function.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"DepthScaling","category":"page"},{"location":"preconditioners/#JUDI.DepthScaling","page":"Preconditioners","title":"JUDI.DepthScaling","text":"DepthScaling{T, N, K}\n\nDepth scaling operator in N dimensions scaling by depth^K.\n\nConstructor\n\njudiDepthScaling(model::AbstractModel; K=.5)\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Usage:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"# Forward\nm_mute = Mr*vec(m)\n\n# Adjoint\nm_mute = Mr'*vec(m)","category":"page"},{"location":"preconditioners/#Illumination","page":"Preconditioners","title":"Illumination","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"The illumination computed the energy of the wavefield along time for each grid point. This provides a first order diagonal approximation of the Hessian of FWI/LSRTM helping the ocnvergence and quality of an update.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"judiIllumination","category":"page"},{"location":"preconditioners/#JUDI.judiIllumination","page":"Preconditioners","title":"JUDI.judiIllumination","text":"judiIllumination(model; mode=\"u\", k=1, recompute=true)\n\nArguments\n\nmodel: JUDI Model structure\nmode: Type of ilumination, choicees of (\"u\", \"v\", \"uv\")\nk: Power of the illumination, real number\nrecompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)\njudiIllumination(F; mode=\"u\", k=1, recompute=true)\n\nArguments\n\nF: JUDI propagator\nmode: Type of ilumination, choicees of (\"u\", \"v\", \"uv\")\nk: Power of the illumination, real positive number\nrecompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)\n\nDiagonal approximation of the FWI Hessian as the energy of the wavefield. The diagonal contains the sum over time of the wavefield chosen as mode.\n\nOptions for the mode are \"u\" for the forward wavefield illumination, \"v\" for the adjoint wavefield illumination, and \"uv\" for the pointwise product of the forward and adjoint wavefields illuminations. Additionally, the parameter \"k\" provides control on the scaling of the daiagonal raising it to the power k. \n\nExample\n\nI = judiIllumination(model) \n\nConstruct the diagonal operator such that I*x = x ./ |||u||_2^2\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Usage:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"# Forward\nm_mute = I*vec(m)\n\n# Adjoint\nm_mute = I'*vec(m)","category":"page"},{"location":"preconditioners/#Data-preconditioners","page":"Preconditioners","title":"Data preconditioners","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"These preconditioners are design to act on the shot records (data). These preconditioners are indexable by source number so that working with a subset of shot is trivial to implement.","category":"page"},{"location":"preconditioners/#Data-topmute","page":"Preconditioners","title":"Data topmute","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Create a data topmute for the data based on the source and receiver geometry (i.e based on the offsets between each souurce-receiver pair). THis operator allows two modes, :reflection for the standard \"top-mute\" direct wave muting and :turning for its opposite muting the reflection to compute gradients purely based on the turning waves. The muting operators uses a cosine taper at the mask limit to allow for a smooth transition.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"DataMute","category":"page"},{"location":"preconditioners/#JUDI.DataMute","page":"Preconditioners","title":"JUDI.DataMute","text":"struct DataMute{T, mode} <: DataPreconditioner{T, T}\n srcGeom::Geometry\n recGeom::Geometry\n vp::Vector{T}\n t0::Vector{T}\n taperwidth::Vector{Int64}\nend\n\nData mute linear operator a where {T, N}sociated with source srcGeom and receiver recGeom geometries used to compute the distance to the source for each trace in the data. Data mute preconditionner. Supports two modes (:reflection, :turning) that mute either the turning waves (standard direct wave mute) or mutes the reflections. A cosine tapr is applied with width taperwidth to avoid abrupt change and infinite frequency jumps in the data.\n\nConstructors\n\njudiDataMute(srcGeom, recGeom; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))\n\nConstruct the data mute operator from the source srcGeom and receiver recGeom geometries.\n\njudiDataMute(q, d; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))\n\nConstruct the data mute operator from the judivector source q and judivector data d.\n\nParameters\n\nThe following optional paramet where {T, N}rs control the muting operator\n\nvp: P wave velocity of the direct wave (usually water velocity). Can be a constant or a Vector with one value per source position. Devfaults to 1500m/s\nt0: Time shift in seconds (usually width of the wavelet). Defaults to 1 sec\nmode: :reflection to keep the reflections and mute above the direct wave (i.e for RTM) :turning to keep the turning waves and mute below the direct wave (i.e for FWI)\ntaperwidth: Width of the cosine taper in number of samples. Defaults to 2 / t0\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/#Band-pass-filter","page":"Preconditioners","title":"Band pass filter","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"While not purely a preconditioner, because this operator acts on the data and is traditionally used fro frequency continuation in FWI, we implemented this operator as a source indexable linear operator as well. Additionally, the filtering function is available as a standalone julia function for general usage","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"FrequencyFilter\nfilter_data","category":"page"},{"location":"preconditioners/#JUDI.FrequencyFilter","page":"Preconditioners","title":"JUDI.FrequencyFilter","text":"struct FrequencyFilter\n recGeom\n\nBandpass filter linear operator. Filters the input judiVector or Vector\n\nConstructor\n\njudiFilter(geometry, fmin, fmax) \njudiFilter(judiVector, fmin, fmax)\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/#JUDI.filter_data","page":"Preconditioners","title":"JUDI.filter_data","text":"filter(Din, dt_in; fmin=0, fmax=25)\n\nPerforms a causal filtering [fmin, fmax] on the input data bases on its sampling rate dt. Automatically perfroms a lowpass if fmin=0 (default)\n\n\n\n\n\n","category":"function"},{"location":"preconditioners/#Data-time-derivative/intergraton","page":"Preconditioners","title":"Data time derivative/intergraton","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"A TimeDifferential{K} is a linear operator that implements a time derivative (K>0) or time integration (K<0) of order K for any real K including fractional values.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"TimeDifferential","category":"page"},{"location":"preconditioners/#JUDI.TimeDifferential","page":"Preconditioners","title":"JUDI.TimeDifferential","text":"struct TimeDifferential\n recGeom\n\nDifferential operator of order K to be applied along the time dimension. Applies the ilter w^k where k is the order. For example, the tinme derivative is TimeDifferential{1} and the time integration is TimeDifferential{-1}\n\nConstructor\n\njudiTimeIntegration(recGeom, order)\njudiTimeIntegration(judiVector, order)\n\njudiTimeDerivative(recGeom, order)\njudiTimeDerivative(judiVector, order)\n\n\n\n\n\n","category":"type"},{"location":"tutorials/07_preconditionners/#Seismic-preconditionners","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"This tutorials provide an overview of the preconditionners available in JUDI. THese examples are useful, if not necessary, for FWI, RTM and LSRTM to improve convergence and the quality of the result. Preconditionners fall in two categories:","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Model preconditioners: these are linear operators that act on model domain vectors such as the velcoity or seismic image. \nData preconditionners: there are linear operators that act on the data, i.e the shot records.","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"We will show in the following example for both those categories. By design, the JUDI precontionners are designed to work on JUDI types such as judiVector or PhysicalParameters but can also be used directly on standard nion dimensional vectors.","category":"page"},{"location":"tutorials/07_preconditionners/#Setup","page":"Seismic preconditionners","title":"Setup","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"We setup this tutorial by creating a few objects we will be using. We consider ","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"d_textobs\na seismic dataset consiting of nsrc sources\nm\na squared slowness model\ndm\na model perturbation. For simplicity we derive this image directly for the squared slowness to avoid costly computation ","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"using JUDI, SlimPlotting, JLD2, HDF5, LinearAlgebra, SegyIO, Images\ndata_path = JUDI.JUDI_DATA","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"\"/Users/mathiaslouboutin/.julia/dev/JUDI/src/../data\"","category":"page"},{"location":"tutorials/07_preconditionners/#Model","page":"Seismic preconditionners","title":"Model","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"n, d, o, m0, m = read(h5open(\"$(data_path)/overthrust_model.h5\",\"r\"), \"n\", \"d\", \"o\", \"m0\", \"m\");","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(10, 5))\nplot_velocity(m'.^(-.5), d; aspect=1, new_fig=false, cbar=true)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(10, 5))\nplot_velocity(m0'.^(-.5), d; aspect=1, new_fig=false, cbar=true)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"dm = m0 - m\nfigure(figsize=(10, 5))\nplot_simage(dm', d; aspect=1, new_fig=false, cbar=true, name=\"dm\")","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"model = Model(n, d, o, m)\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Model (n=(401, 121), d=(25.0f0, 25.0f0), o=(0.0f0, 0.0f0)) with parameters (:m, :rho)","category":"page"},{"location":"tutorials/07_preconditionners/#Data","page":"Seismic preconditionners","title":"Data","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Now that we have a model, let's load and look at the data","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"block = segy_read(\"$(data_path)/overthrust_shot_records.segy\")\nd_obs = judiVector(block); # linearized observed data\n# Source\nsrc_geometry = Geometry(block; key = \"source\")\nwavelet = ricker_wavelet(src_geometry.t[1],src_geometry.dt[1],0.008f0) # 8 Hz wavelet\nq = diff(judiVector(src_geometry, wavelet), dims=1)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mFixed length trace flag set in stream: IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=7076688, maxsize=Inf, ptr=3601, mark=-1)\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ SegyIO ~/.julia/dev/SegyIO/src/read/read_file.jl:36\u001b[39m\n\n\n\n\n\njudiVector{Float32, Matrix{Float32}} with 16 sources","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(10, 10))\nplot_sdata(get_data(d_obs[1]); new_fig=false)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"since some of the preconditionners are designed for inversion, let's create the data forr the background model and the propagators needed for imaging and inversion","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"F = judiModeling(model, q.geometry, d_obs.geometry; options=Options(space_order=16))\nJ = judiJacobian(F(model0), q)\nd0 = F(model0)*q","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Building forward operator\nOperator `forward` ran in 0.11 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.32 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.42 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.34 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\n\n\n\n\n\njudiVector{Float32, Matrix{Float32}} with 16 sources","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 10))\nsubplot(121)\nplot_sdata(get_data(d_obs[1]); new_fig=false)\nsubplot(122)\nplot_sdata(d0[1]; new_fig=false)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Model-preconditionners","page":"Seismic preconditionners","title":"Model preconditionners","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Dm = judiDepthScaling(model0)\nTm = judiTopmute(model0; taperwidth=0)\nIl = inv(judiIllumination(model0))\nmcases = [(Dm, \"Depth scaling\"), (Tm, \"Water layer mute\"), (Il, \"Illumination\")]","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"3-element Vector{Tuple{JUDI.ModelPreconditioner{Float32, Float32}, String}}:\n (DepthScaling{Float32, 2, 0.5f0}(48521, Float32[0.0 25.0 … 2975.0 3000.0]), \"Depth scaling\")\n (TopMute{Float32, 2, 1}(48521, [21, 21, 21, 21, 21, 21, 21, 21, 21, 21 … 21, 21, 21, 21, 21, 21, 21, 21, 21, 21], 0), \"Water layer mute\")\n (judiIllumination{Float32, :u, -1, true}(\"judiIllumination{Float32, :u, 1, true}\", Dict{SubString{String}, PhysicalParameter{Float32, 2}}(\"u\" => PhysicalParameter{Float32, 2} of size (401, 121) with origin (0.0f0, 0.0f0) and spacing (25.0f0, 25.0f0)), 48521), \"Illumination\")","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"rtm = J'*(d0 - d_obs)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Building forward operator\nOperator `forward` ran in 0.17 s\nBuilding adjoint born operator\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.28 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.27 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.46 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.35 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.40 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.30 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.24 s\nOperator `gradient` ran in 0.18 s\nOperator `forward` ran in 0.36 s\nOperator `gradient` ran in 0.16 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.17 s\nOperator `forward` ran in 0.35 s\nOperator `gradient` ran in 0.17 s\n\n\n\n\n\nPhysicalParameter{Float32, 2} of size (401, 121) with origin (0.0f0, 0.0f0) and spacing (25.0f0, 25.0f0)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"plot_simage(Il.illums[\"u\"]'; cbar=true, name=\"Illumination\")","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Model-top-mute","page":"Seismic preconditionners","title":"Model top mute","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"This preconditionner is one of the simplest one and mutes the water layer, or more generally sets to zeros the top of the model.","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 12))\nfor (i, (pr, name)) in enumerate(mcases)\n subplot(3,1,i)\n plot_simage((pr*rtm)'; new_fig=false, name=name)\nend\ntight_layout()","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Data-Preconditionners","page":"Seismic preconditionners","title":"Data Preconditionners","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Dt = judiTimeDerivative(d_obs, .5)\nIt = judiTimeIntegration(d_obs, .5)\nDmr = judiDataMute(q, d_obs; mode=:reflection, taperwidth=2)\nDmt = judiDataMute(q, d_obs; mode=:turning)\nDf = judiFilter(d_obs, .1, 5.0)\ndcases = [(Dt, \"Fractional (.5) time derivative\"),\n (It, \"Fractional (.5) time integration\"),\n (Dmr, \"Turning waves muting\"),\n (Dmt, \"Reflection muting\"),\n (Df, \"bandpass filter [.1, 5]Hz\")];","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 12))\nfor (i, (pr, name)) in enumerate(dcases)\n subplot(3,2,i)\n dloc = (pr*d_obs)[1]\n plot_sdata(dloc; new_fig=false, name=name)\nend\ntight_layout()","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Putting-it-together","page":"Seismic preconditionners","title":"Putting it together","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Of course, in practice numerous preconditionners would be needed for the best result. Since our implementation relies on linear algebra abstractions, those preconditionners can be used with each other and in combination with propagators as well. We show a few examples below","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"# Low frequency gradient with turing waves only for FWI\nrtmlow = Il*Tm*J'* Dmt * Df * (d0 - d_obs);","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Operator `forward` ran in 0.25 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.32 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.26 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.29 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.27 s\nOperator `gradient` ran in 0.16 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.16 s\nOperator `forward` ran in 0.37 s\nOperator `gradient` ran in 0.21 s\nOperator `forward` ran in 0.27 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.24 s\nOperator `gradient` ran in 0.15 s\n\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mJOLI linear operator, returning julia Array\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ JUDI ~/.julia/dev/JUDI/src/TimeModeling/Types/ModelStructure.jl:217\u001b[39m","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 4))\nplot_simage(reshape(rtmlow, model.n)', model.d; new_fig=false, name=\"Low frequency FWI gradient\", cmap=seiscm(:bwr))","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.n, use size(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m\n\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.d, use spacing(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#FWI-Example","page":"FWI Example","title":"FWI Example","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We will peform FWI using the following steps:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Prepare models\nSetup Constraints with SetIntersectionProjection\nBuild a small local compute cluster (2 workers)\nTake care of some HPC details related to thread affinity\nCreate source and receivers geometries\nBuild F, the JUDI modeling operator\nUse F to create data for both models\nVisualize data\nAssess if data is cycle skipped at the farthest offsets\nBuild the objective function\nPerform the FWI using minConf_PQN from JUDI\nVisualize velocity models and objective function\nVisualize data match\nRemove workers","category":"page"},{"location":"tutorials/03_constrained_fwi/#Note-on-runtime","page":"FWI Example","title":"Note on runtime","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Warning: this notebook takes more than 1 hour to run for 16 shots with two workers on an Intel 8168.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"lscpu CPU information: Intel(R) Xeon(R) Platinum 8168 CPU @ 2.70GHz","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# Add packages needed\n# PyPlot is already installed in the environment\n# using Pkg\n# Pkg.add([\"Distributed\", \"SlimOptim\", \"JUDI\", \"SetIntersectionProjection\"])","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"using Distributed, JUDI, SlimOptim, LinearAlgebra, PyPlot, SetIntersectionProjection, Printf","category":"page"},{"location":"tutorials/03_constrained_fwi/#Prepare-models","page":"FWI Example","title":"Prepare models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"n = (251, 251) # nx, nz\nd = (15., 15.) # hx, hz\no = (0., 0.); # ox, oz","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# Squared slowness\nm = 1.5f0^(-2) * ones(Float32, n)\nm[101:150, 101:150] .= 1.7f0^(-2)\nm0 = 1/1.5f0^2 * ones(Float32, n);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"model = Model(n,d,o,m)\nmodel0 = Model(n,d,o,m0);","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize","page":"FWI Example","title":"Visualize","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,9))\nvmin,vmax = extrema(m)\ndmin,dmax = -.1,.1\n\nsubplot(3,1,1); imshow(m,aspect=\"auto\",cmap=\"jet\"); \ncolorbar(); clim(vmin,vmax); title(\"True squared slowness (m)\")\n\nsubplot(3,1,2); imshow(m0,aspect=\"auto\",cmap=\"jet\");\ncolorbar(); clim(vmin,vmax); title(\"Initial squared slowness (m0)\");\n\nsubplot(3,1,3); imshow(m.-m0,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(); clim(dmin,dmax); title(\"Difference (m-m0)\");\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Setup-Constraints-with-[SetIntersectionProjection](https://github.com/slimgroup/SetIntersectionProjection.jl)","page":"FWI Example","title":"Setup Constraints with SetIntersectionProjection","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"options=PARSDMM_options()\noptions.FL=Float32\noptions=default_PARSDMM_options(options,options.FL)\nconstraint = Vector{SetIntersectionProjection.set_definitions}()\nconstraint2 = Vector{SetIntersectionProjection.set_definitions}()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"set_definitions[]","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We setup two constaints:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Bounds that limit maximum and minimum velocity\nTV, that limits variation and force a piece-wise constant structure","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#bounds:\nm_min = 0 .* m .+ minimum(m).*.5\nm_max = 0 .* m .+ maximum(m)\nset_type = \"bounds\"\nTD_OP = \"identity\"\napp_mode = (\"matrix\",\"\")\ncustom_TD_OP = ([],false)\npush!(constraint, set_definitions(set_type,TD_OP,vec(m_min),vec(m_max),app_mode,custom_TD_OP));\npush!(constraint2, set_definitions(set_type,TD_OP,vec(m_min),vec(m_max),app_mode,custom_TD_OP));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#TV\n(TV,dummy1,dummy2,dummy3) = get_TD_operator(model0,\"TV\",options.FL)\nm_min = 0.0\nm_max = norm(TV*vec(m),1) * .5\nset_type = \"l1\"\nTD_OP = \"TV\"\napp_mode = (\"matrix\",\"\")\ncustom_TD_OP = ([],false)\npush!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#set up constraints with bounds only, precompute some things and define projector\n(P_sub2,TD_OP2,set_Prop2) = setup_constraints(constraint2, model0,options.FL)\n(TD_OP2,AtA2,l2,y2) = PARSDMM_precompute_distribute(TD_OP2,set_Prop2,model0,options)\noptions2 = deepcopy(options)\noptions2.rho_ini = ones(length(TD_OP2))*10.0\n\nproj_intersection2 = x-> PARSDMM(x, AtA2, TD_OP2, set_Prop2, P_sub2, model0, options2) \n\n# Projection function\nfunction prj2(input)\n input = Float32.(input)\n (x,dummy1,dummy2,dymmy3) = proj_intersection2(vec(input))\n return x\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"prj2 (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#set up constraints with bounds and TV\n(P_sub,TD_OP,set_Prop) = setup_constraints(constraint, model0,options.FL)\n(TD_OP,AtA,l,y) = PARSDMM_precompute_distribute(TD_OP,set_Prop,model0,options)\noptions.rho_ini = ones(length(TD_OP))*10.0\n\nproj_intersection = x-> PARSDMM(x, AtA, TD_OP, set_Prop, P_sub, model0, options)\n\n# Projection function\nfunction prj(input)\n input = Float32.(input)\n (x,dummy1,dummy2,dymmy3) = proj_intersection(vec(input))\n return x\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"prj (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-a-small-local-compute-cluster","page":"FWI Example","title":"Build a small local compute cluster","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/#Setup-OMP-environment-variables-for-the-cluster","page":"FWI Example","title":"Setup OMP environment variables for the cluster","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"In the distributed compute case the workers that we add would be on different hardware, and we might add tens of workers in 2D and hundreds in 3D. Here we run on a single machine with only 2 workers, and so we need to be careful with details related to high performance computing. If we did not specify thread affinity, the two workers would compete for the same physical cores and the modeling would be incredibly slow.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We spin up the small 2-worker cluster by calling addprocs(2), and because we set the environment variable ENV[\"OMP_DISPLAY_ENV\"] = \"true\" we will see the OMP environment printed out on each worker. In that output (below) we can verify that half of the total threads (44/2 = 22) are assigned to each socket on this 2 socket system. You can obtain more details about the hardware with the shell command lscpu.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We set four environment variables related to OpenMP:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"OMP_DISPLAY_ENV prints out the OpenMP environment on each worker\nOMP_PROC_BIND specifies that threads should be bound to physical cores\nOMP_NUM_THREADS specifies the number of threads per workers is 1/2 the number of physical cores\nGOMP_CPU_AFFINITY specifies which physical cores the threads run on for each worker","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"If you run the shell command top during execution, you will see 3 julia processes: the main process and two workers. The two workers should generally have about 50% of the system, and load average should tend towards the physical number of cores.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"nthread = Sys.CPU_THREADS\nnw = 2\n\nENV[\"OMP_DISPLAY_ENV\"] = \"true\"\nENV[\"OMP_PROC_BIND\"] = \"close\"\nENV[\"OMP_NUM_THREADS\"] = \"$(div(nthread, nw))\" \naddprocs(nw)\n@show workers()\nfor k in 1:nworkers()\n place1 = (k - 1) * div(nthread,nworkers())\n place2 = (k + 0) * div(nthread,nworkers()) - 1\n @show place1, place2, div(nthread, nw)\n @spawnat workers()[k] ENV[\"GOMP_CPU_AFFINITY\"] = \"$(place1)-$(place2)\";\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"workers() = [2, 3]\n(place1, place2, div(nthread, nw)) = (0, 1, 2)\n(place1, place2, div(nthread, nw)) = (2, 3, 2)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"@everywhere using Distributed, JUDI, LinearAlgebra","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 3:\t\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 3:\t _OPENMP = '201511'\n From worker 3:\t OMP_DYNAMIC = 'FALSE'\n From worker 3:\t OMP_NESTED = 'FALSE'\n From worker 3:\t OMP_NUM_THREADS = '2'\n From worker 3:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 3:\t OMP_PROC_BIND = 'CLOSE'\n From worker 3:\t OMP_PLACES = ''\n From worker 3:\t OMP_STACKSIZE = '2097152'\n From worker 3:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 3:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 3:\t OMP_MAX_ACTIVE_LEVELS = '1'\n From worker 3:\t OMP_CANCELLATION = 'FALSE'\n From worker 3:\t OMP_DEFAULT_DEVICE = '0'\n From worker 3:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 3:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 3:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 3:\t OMP_ALLOCATOR = 'omp_default_mem_alloc'\n From worker 3:\t OMP_TARGET_OFFLOAD = 'DEFAULT'\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT END\n From worker 2:\t\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 2:\t _OPENMP = '201511'\n From worker 2:\t OMP_DYNAMIC = 'FALSE'\n From worker 2:\t OMP_NESTED = 'FALSE'\n From worker 2:\t OMP_NUM_THREADS = '2'\n From worker 2:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 2:\t OMP_PROC_BIND = 'CLOSE'\n From worker 2:\t OMP_PLACES = ''\n From worker 2:\t OMP_STACKSIZE = '2097152'\n From worker 2:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 2:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 2:\t OMP_MAX_ACTIVE_LEVELS = '1'\n From worker 2:\t OMP_CANCELLATION = 'FALSE'\n From worker 2:\t OMP_DEFAULT_DEVICE = '0'\n From worker 2:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 2:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 2:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 2:\t OMP_ALLOCATOR = 'omp_default_mem_alloc'\n From worker 2:\t OMP_TARGET_OFFLOAD = 'DEFAULT'\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT END","category":"page"},{"location":"tutorials/03_constrained_fwi/#Create-source-and-receivers-geometries","page":"FWI Example","title":"Create source and receivers geometries","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We use 8 shot locations evenly distributed across the left of the model.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"tn = 4000 # Recording time in ms\ndt = 2f0 # Shot record sampling rate in ms\nf0 = 0.005 # Peak frquency in kHz","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"0.005","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"nsrc = 8\nxsrc = convertToCell(d[1].*ones(Float32, nsrc))\nysrc = convertToCell(range(0f0, stop = 0f0, length = nsrc))\nzsrc = convertToCell(range(0f0, (n[2] - 1)*d[2], length=nsrc))\nsrc_geom = Geometry(xsrc, ysrc, zsrc; dt=dt, t=tn);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"nrec = 251\nxrec = (n[1] - 2)*d[1] .* ones(Float32, nrec)\nyrec = 0f0\nzrec = range(0f0, (n[2] - 1)*d[2], length=nrec)\nrec_geom = Geometry(xrec, yrec, zrec; dt=dt, t=tn, nsrc=nsrc);","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-geometry","page":"FWI Example","title":"Visualize geometry","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure()\nvmin,vmax = extrema(m)\ndmin,dmax = -.1,.1\n\nimshow(m,aspect=\"auto\",cmap=\"jet\", extent=[0, 3750, 3750, 0]); \ncolorbar(); clim(vmin,vmax); title(\"True squared slowness (m)\")\nscatter(xsrc, zsrc, c=\"g\", label=\"Sources\")\nscatter(xrec[1:4:end], zrec[1:4:end], c=\"c\", label=\"Receiver\")\nlegend()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"PyObject ","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-F,-the-JUDI-modeling-operator","page":"FWI Example","title":"Build F, the JUDI modeling operator","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# True model operator\nF = judiModeling(model, src_geom, rec_geom)\n\n# Intial model operator\nF0 = judiModeling(model0, src_geom, rec_geom);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# Source function\nfsrc = judiVector(src_geom, ricker_wavelet(tn, dt, f0));","category":"page"},{"location":"tutorials/03_constrained_fwi/#Use-F-to-create-the-data-in-both-models","page":"FWI Example","title":"Use F to create the data in both models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"t1 = @elapsed begin\n dobs = F*fsrc;\nend\n@info @sprintf(\"Time in true model; %.2f seconds\\n\", t1);\n\nt2 = @elapsed begin\n d0 = F0*fsrc;\nend\n@info @sprintf(\"Time in init model; %.2f seconds\\n\", t2);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 3:\t\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 3:\t _OPENMP = '201511'\n From worker 3:\t OMP_DYNAMIC = 'FALSE'\n From worker 3:\t OMP_NESTED = 'FALSE'\n From worker 3:\t OMP_NUM_THREADS = '2'\n From worker 3:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 3:\t OMP_PROC_BIND = 'CLOSE'\n From worker 3:\t OMP_PLACES = ''\n From worker 3:\t OMP_STACKSIZE = '2097152'\n From worker 3:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 3:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 3:\t OMP_MAX_ACTIVE_LEVELS = '2147483647'\n From worker 3:\t OMP_CANCELLATION = 'FALSE'\n From worker 3:\t OMP_DEFAULT_DEVICE = '0'\n From worker 3:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 3:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 3:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT END\n From worker 2:\t\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 2:\t _OPENMP = '201511'\n From worker 2:\t OMP_DYNAMIC = 'FALSE'\n From worker 2:\t OMP_NESTED = 'FALSE'\n From worker 2:\t OMP_NUM_THREADS = '2'\n From worker 2:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 2:\t OMP_PROC_BIND = 'CLOSE'\n From worker 2:\t OMP_PLACES = ''\n From worker 2:\t OMP_STACKSIZE = '2097152'\n From worker 2:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 2:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 2:\t OMP_MAX_ACTIVE_LEVELS = '2147483647'\n From worker 2:\t OMP_CANCELLATION = 'FALSE'\n From worker 2:\t OMP_DEFAULT_DEVICE = '0'\n From worker 2:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 2:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 2:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT END\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n\n\n┌ Info: Time in true model; 27.31 seconds\n└ @ Main In[20]:4\n\n\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.29 s\n\n\n┌ Info: Time in init model; 10.21 seconds\n└ @ Main In[20]:9","category":"page"},{"location":"tutorials/03_constrained_fwi/#Compute-the-residual-data","page":"FWI Example","title":"Compute the residual data","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"r = d0 - dobs;","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-data","page":"FWI Example","title":"Visualize data","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"shots = [1,4,8]","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"3-element Vector{Int64}:\n 1\n 4\n 8","category":"page"},{"location":"tutorials/03_constrained_fwi/#Plot-shot-gathers-for-true-model,-initial-model,-and-residual","page":"FWI Example","title":"Plot shot gathers for true model, initial model, and residual","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"The table below describes the data images below. We flip the direction of the residual and modeled data in order to help display the match with the true data.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"
      Initial Residual Data
      (flipped)
      True Data Initial Data
      (flipped)
      ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Note that the data modeled in the initial model lacks a lot of reflectivity that is evident in the data modeled in the true model. We expect to recover this missing reflectivity with the FWI.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"scale = 10.0 / sqrt(norm(dobs)^2 / length(dobs.data))\n@show scale\n\nnzero = 5\npad = ones(Float32,2001,nzero)\n\nfigure(figsize=(8,9)); clf()\nfor (iplot,ishot) in enumerate(shots)\n cat2 = hcat(reverse(r.data[ishot],dims=2), pad, dobs.data[ishot], pad, reverse(d0.data[ishot],dims=2))\n subplot(3,1,iplot);\n imshow(cat2,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\" Initial Residual sz=$(zsrc[ishot]) | True sz=$(zsrc[ishot]) | Initial sz=$(zsrc[ishot]) (flipped)\");\nend\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"scale = 0.018069575143305386","category":"page"},{"location":"tutorials/03_constrained_fwi/#Assess-if-data-is-cycle-skipped-at-the-farthest-offsets","page":"FWI Example","title":"Assess if data is cycle skipped at the farthest offsets","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Next we plot the far offset traces for these three shots in order to assess if the data is cycle skipped. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"You can ovbserve in the plots below that the refraction waveforms (first arrivals) in the initial model are not cycle skipped with respect to the true model, so we can proceed. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"A very significant part of the residual wavefield is actually reflections in this example.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"scale = 10.0 / sqrt(norm(dobs)^2 / length(dobs.data))\nt = [0.0:dt:tn;]\n\nfigure(figsize=(8,9)); clf()\nfor (iplot,ishot) in enumerate(shots)\n subplot(3,1,iplot);\n plot(t,dobs.data[ishot][:,end],label=\"True Model $(ishot) at z=$(zsrc[ishot])\");\n plot(t,d0.data[ishot][:,end],label=\"Initial Model $(ishot) at z=$(zsrc[ishot])\");\n xlim([4.5,t[end]])\n legend()\nend\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-the-objective-functions","page":"FWI Example","title":"Build the objective functions","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/#Build-src/rec-positions-mask","page":"FWI Example","title":"Build src/rec positions mask","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We use this mask to remove the imprint in gradients of proximity to source locations. The mask is set to 0 wherever a source or receiver is close, and is set to 1 otherwise. Without this mask most of the gradient updates would be concentrated close to sources where the model is correct. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"wb_mask = ones(Float32,size(m))\nwb_mask[1:5, :] .= 0;\nwb_mask[end-5:end, :] .= 0;\n\nfigure(figsize=(8,3))\nimshow(wb_mask', aspect=\"auto\",cmap=\"gray_r\",clim=[0,+2]);\ncolorbar();\ntitle(\"Water Bottom Mask\");\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-the-objective-function","page":"FWI Example","title":"Build the objective function","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"This method is called by the solver whenever the gradient is required. Steps in computing the gradient are as follows:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Apply the adjoint of the Jacobian to the current residual J' * [F*v - d] \nApply simple scaling based on the size of the first gradient, and save to apply to future gradients","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# build Jacoian\nJ = judiJacobian(F0, fsrc)\n\nfunction objective(F0, G, m, dobs, wb_mask)\n F0.model.m .= m\n t = @elapsed begin\n d0 = F0*fsrc\n if isnothing(G)\n return .5f0*norm(d0 .- dobs)^2\n end\n G .= J' * (d0 .- dobs)\n end\n G .*= vec(wb_mask)\n ϕ = .5f0*norm(d0 .- dobs)^2\n if gscale == 0.0\n # compute scalar from first gradient, apply to future gradients\n global gscale = .25f0 ./ maximum(G) \n @show gscale\n end\n G .*= gscale\n return ϕ\nend\n\n# struct to save the first gradient scalar\ngscale = 0f0\nf(x) = objective(F0, nothing, x, dobs, wb_mask)\ng!(G, x) = objective(F0, G, x, dobs, wb_mask)\nfg!(G, x) = objective(F0, G, x, dobs, wb_mask)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"fg! (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Compute-gradient","page":"FWI Example","title":"Compute gradient","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"grad1 = 0f0 .* vec(m0)\ntgrad1 = @elapsed begin\n g!(grad1, vec(m0))\n gscale = 0\nend\n@show tgrad1;","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.26 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.59 s\n From worker 2:\tOperator `gradient` ran in 0.46 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.63 s\n From worker 2:\tOperator `gradient` ran in 0.49 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `gradient` ran in 0.54 s\n From worker 2:\tOperator `gradient` ran in 0.34 s\ngscale = 3.15937f-5\ntgrad1 = 33.235375243","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"dm = m0 .- m\ngrad1 = reshape(grad1, n)\nmg2 = reshape(m0 .- grad1, n)\nfigure(figsize=(8,9))\n\nsubplot(5,1,1)\nimshow(grad1' ./ maximum(abs,grad1),aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(-1,1);\ntitle(\"Initial Gradient without Illumination Compensation\");\n\nsubplot(5,1,2)\nimshow(dm ./ maximum(abs,dm),aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(-1,1);\ntitle(\"Squared slowness Difference: (m0 - m)\");\n\nsubplot(5,1,3)\nimshow(mg2',aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax)\ntitle(\"Updated squared slowness: (m0 - grad1)\");\n\nsubplot(5,1,4)\nimshow(reshape(prj(mg2), n)',aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax)\ntitle(\"Updated projected (bounds + TV) squared slowness: prj(m0 - grad1)\");\n\nsubplot(5,1,5)\nimshow(reshape(prj2(mg2), n)',aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax)\ntitle(\"Updated projected (bounds) squared slowness: prj(m0 - grad1)\");\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"relative evolution to small, exiting PARSDMM (iteration 37)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"input to PARSDMM is feasible, returning","category":"page"},{"location":"tutorials/03_constrained_fwi/#Perform-the-FWI-using-minConf_PQN","page":"FWI Example","title":"Perform the FWI using minConf_PQN","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We will do 10 functions evaluation cost of projected quasi-Newton with two setup:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Bounds constraints only\nBounds + tv constrains","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# FWI with PQN\nniter = 10\ngscale = 0f0\noptions_pqn = pqn_options(progTol=0, store_trace=true, verbose=3, maxIter=niter)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"SlimOptim.PQN_params(3, 1.0f-5, 0, 10, 0.0001f0, 10, false, true, true, 1.0f-6, 1.0f-7, 100, false, 20, 1, 1)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"sol = pqn(f, g!, fg!, vec(m0), prj, options_pqn);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Running PQN...\nNumber of L-BFGS Corrections to store: 10\nSpectral initialization of SPG: 1\nMaximum number of SPG iterations: 100\nSPG optimality tolerance: 1.00e-06\nSPG progress tolerance: 1.00e-07\nPQN optimality tolerance: 1.00e-05\nPQN progress tolerance: 0.00e+00\nQuadratic initialization of line search: 0\nMaximum number of iterations: 10\nLine search: BackTracking{Float32, Int64}\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.56 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `gradient` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.57 s\n From worker 3:\tOperator `gradient` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.38 s\ngscale = 3.159375f-5\n Iteration FunEvals GradEvals Projections Step Length Function Val Opt Cond\nrelative evolution to small, exiting PARSDMM (iteration 37)\n 0 0 0 0 0.00000e+00 8.04513e+05 6.74951e-02\nrelative evolution to small, exiting PARSDMM (iteration 37)\nrelative evolution to small, exiting PARSDMM (iteration 37)\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.28 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.54 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.23 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.33 s\n From worker 3:\tOperator `gradient` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.55 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.52 s\n From worker 3:\tOperator `gradient` ran in 0.32 s\nrelative evolution to small, exiting PARSDMM (iteration 38)\n 1 3 2 6 1.00000e-01 5.95413e+05 5.50441e-02\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 29)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.25 s\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.22 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.69 s\n From worker 3:\tOperator `gradient` ran in 0.71 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.73 s\n From worker 3:\tOperator `gradient` ran in 0.72 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `forward` ran in 0.54 s\n From worker 2:\tOperator `gradient` ran in 0.72 s\n From worker 3:\tOperator `gradient` ran in 0.72 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.66 s\n From worker 3:\tOperator `gradient` ran in 0.63 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n 2 5 3 36 1.00000e-01 4.97818e+05 1.70875e-01\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 30)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.62 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.66 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `gradient` ran in 0.66 s\n From worker 2:\tOperator `gradient` ran in 0.68 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.71 s\n From worker 2:\tOperator `gradient` ran in 0.68 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n 3 7 4 64 1.00000e-01 3.82668e+05 1.56781e-01\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 29)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.24 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.33 s\n From worker 2:\tOperator `gradient` ran in 0.52 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `gradient` ran in 0.53 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `gradient` ran in 0.54 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `gradient` ran in 0.49 s\n From worker 2:\tOperator `gradient` ran in 0.32 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n 4 8 5 92 1.00000e+00 3.50573e+05 6.73204e-02\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 38)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.24 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.30 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.67 s\n From worker 2:\tOperator `gradient` ran in 0.85 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.67 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `gradient` ran in 0.65 s\n From worker 2:\tOperator `gradient` ran in 0.66 s\nrelative evolution to small, exiting PARSDMM (iteration 26)\n 5 9 6 116 1.00000e+00 1.17713e+05 2.24156e-02\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.22 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `gradient` ran in 0.70 s\n From worker 2:\tOperator `gradient` ran in 0.72 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.74 s\n From worker 2:\tOperator `gradient` ran in 0.74 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.55 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `gradient` ran in 0.32 s\nrelative evolution to small, exiting PARSDMM (iteration 26)\n 6 10 7 130 1.00000e+00 8.91832e+04 2.10390e-02\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 28)\nrelative evolution to small, exiting PARSDMM (iteration 28)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.30 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `gradient` ran in 0.59 s\n From worker 3:\tOperator `gradient` ran in 0.49 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.40 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 7 11 8 146 1.00000e+00 3.31746e+04 2.72466e-02\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 27)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.26 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.45 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.75 s\n From worker 2:\tOperator `gradient` ran in 0.77 s\n From worker 3:\tOperator `forward` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.61 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.45 s\n From worker 3:\tOperator `gradient` ran in 0.76 s\n From worker 2:\tOperator `gradient` ran in 0.77 s\n From worker 3:\tOperator `forward` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.82 s\n From worker 2:\tOperator `gradient` ran in 0.80 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 8 12 9 164 1.00000e+00 1.77415e+04 2.05675e-02\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 27)\nrelative evolution to small, exiting PARSDMM (iteration 20)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.25 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.53 s\n From worker 3:\tOperator `gradient` ran in 0.96 s\n From worker 2:\tOperator `gradient` ran in 0.97 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `forward` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.78 s\n From worker 2:\tOperator `gradient` ran in 0.75 s\n From worker 3:\tOperator `forward` ran in 0.53 s\n From worker 2:\tOperator `forward` ran in 0.57 s\n From worker 3:\tOperator `gradient` ran in 0.77 s\n From worker 2:\tOperator `gradient` ran in 0.78 s\n From worker 3:\tOperator `forward` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.70 s\n From worker 2:\tOperator `gradient` ran in 0.74 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 9 13 10 184 1.00000e+00 1.05269e+04 1.38740e-02\nrelative evolution to small, exiting PARSDMM (iteration 25)\nrelative evolution to small, exiting PARSDMM (iteration 41)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.27 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.81 s\n From worker 2:\tOperator `gradient` ran in 0.81 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.58 s\n From worker 2:\tOperator `forward` ran in 0.54 s\n From worker 3:\tOperator `forward` ran in 0.52 s\n From worker 2:\tOperator `gradient` ran in 0.67 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 10 14 11 216 1.00000e+00 5.58767e+03 9.81355e-03","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"sol2 = pqn(f, g!, fg!, vec(m0), prj2, options_pqn);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Running PQN...\nNumber of L-BFGS Corrections to store: 10\nSpectral initialization of SPG: 1\nMaximum number of SPG iterations: 100\nSPG optimality tolerance: 1.00e-06\nSPG progress tolerance: 1.00e-07\nPQN optimality tolerance: 1.00e-05\nPQN progress tolerance: 0.00e+00\nQuadratic initialization of line search: 0\nMaximum number of iterations: 10\nLine search: BackTracking{Float32, Int64}\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.31 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `gradient` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.29 s\n Iteration FunEvals GradEvals Projections Step Length Function Val Opt Cond\ninput to PARSDMM is feasible, returning\n 0 0 0 0 0.00000e+00 8.04514e+05 2.50000e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.52 s\n From worker 3:\tOperator `forward` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.50 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.29 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.22 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `gradient` ran in 0.73 s\n From worker 3:\tOperator `gradient` ran in 0.77 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `gradient` ran in 0.59 s\n From worker 3:\tOperator `gradient` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.56 s\n From worker 3:\tOperator `gradient` ran in 0.33 s\ninput to PARSDMM is feasible, returning\n 1 3 2 6 1.00000e-01 4.12124e+05 1.59019e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.64 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.22 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.23 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.24 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.75 s\n From worker 2:\tOperator `gradient` ran in 0.75 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `gradient` ran in 0.61 s\n From worker 3:\tOperator `gradient` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.49 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.31 s\ninput to PARSDMM is feasible, returning\n 2 5 3 42 1.00000e-01 2.75734e+05 1.35675e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `gradient` ran in 0.77 s\n From worker 2:\tOperator `gradient` ran in 0.79 s\n From worker 3:\tOperator `forward` ran in 0.67 s\n From worker 2:\tOperator `forward` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.99 s\n From worker 2:\tOperator `gradient` ran in 0.99 s\n From worker 3:\tOperator `forward` ran in 0.54 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.73 s\n From worker 2:\tOperator `gradient` ran in 0.93 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.67 s\n From worker 2:\tOperator `gradient` ran in 0.42 s\ninput to PARSDMM is feasible, returning\n 3 6 4 70 1.00000e+00 1.12580e+05 1.14484e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.53 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.52 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.84 s\n From worker 3:\tOperator `gradient` ran in 0.84 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `gradient` ran in 0.64 s\n From worker 3:\tOperator `gradient` ran in 0.69 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `gradient` ran in 0.78 s\n From worker 3:\tOperator `gradient` ran in 0.79 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.41 s\ninput to PARSDMM is feasible, returning\n 4 7 5 94 1.00000e+00 3.94970e+04 4.63609e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.56 s\n From worker 2:\tOperator `gradient` ran in 0.43 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `gradient` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\ninput to PARSDMM is feasible, returning\n 5 8 6 118 1.00000e+00 2.78628e+04 2.88242e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.29 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `gradient` ran in 0.62 s\n From worker 3:\tOperator `gradient` ran in 0.58 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `gradient` ran in 0.59 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.56 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\ninput to PARSDMM is feasible, returning\n 6 9 7 150 1.00000e+00 1.45960e+04 2.03700e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.50 s\n From worker 2:\tOperator `gradient` ran in 0.64 s\n From worker 3:\tOperator `gradient` ran in 0.56 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.49 s\n From worker 2:\tOperator `gradient` ran in 0.64 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.34 s\ninput to PARSDMM is feasible, returning\n 7 10 8 190 1.00000e+00 7.63842e+03 1.36674e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.26 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 2:\tOperator `forward` ran in 0.26 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.80 s\n From worker 3:\tOperator `gradient` ran in 0.80 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `gradient` ran in 0.53 s\n From worker 3:\tOperator `gradient` ran in 0.53 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.53 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.38 s\ninput to PARSDMM is feasible, returning\n 8 11 9 238 1.00000e+00 3.18718e+03 1.09845e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `gradient` ran in 0.61 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `gradient` ran in 0.65 s\n From worker 2:\tOperator `gradient` ran in 0.52 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `gradient` ran in 0.34 s\ninput to PARSDMM is feasible, returning\n 9 12 10 292 1.00000e+00 2.62946e+03 1.24063e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.59 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.74 s\n From worker 2:\tOperator `gradient` ran in 0.76 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `gradient` ran in 0.78 s\n From worker 2:\tOperator `gradient` ran in 0.82 s\n From worker 3:\tOperator `forward` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.78 s\n From worker 2:\tOperator `gradient` ran in 0.78 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.73 s\n From worker 2:\tOperator `gradient` ran in 0.71 s\ninput to PARSDMM is feasible, returning\n 10 13 11 338 1.00000e+00 1.73191e+03 5.47692e-03","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"mf = reshape(prj(sol.x), n) # optimal solution\nϕ = sol.ϕ_trace # cost vs iteration\nm1 = sol.x_trace # model vs iteration\ncollect(m1[i] = reshape(m1[i], n) for i=1:length(ϕ));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"relative evolution to small, exiting PARSDMM (iteration 15)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"mf2 = reshape(prj(sol2.x), n) # optimal solution\nϕ2 = sol2.ϕ_trace # cost vs iteration\nm2 = sol2.x_trace # model vs iteration\ncollect(m2[i] = reshape(m2[i], n) for i=1:length(ϕ2));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"relative evolution to small, exiting PARSDMM (iteration 15)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-velocity-models-and-objective-function","page":"FWI Example","title":"Visualize velocity models and objective function","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,9)); clf()\n\nsubplot(4,1,1);imshow(m0',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"Initial Velocity\");\n\nsubplot(4,1,2);imshow(mf2',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"FWI Velocity\");\n\nsubplot(4,1,3);imshow(mf',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"FWI Velocity with TV\");\n\nsubplot(4,1,4);imshow(m',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"True Velocity\")\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Display-the-velocity-difference-models","page":"FWI Example","title":"Display the velocity difference models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"rms_v2 = @sprintf(\"%.1f m/s\", sqrt(norm(m .- m0)^2 / length(m)))\nrms_vf = @sprintf(\"%.1f m/s\", sqrt(norm(m .- mf)^2 / length(m)))\nrms_vf2 = @sprintf(\"%.1f m/s\", sqrt(norm(m .- mf2)^2 / length(m)))\n\nfigure(figsize=(8,6)); clf()\n\nsubplot(3,1,1);imshow(m .- m0,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(dmin,dmax);\ntitle(\"Vtrue - Vinit difference, rms=$(rms_v2)\");\n\nsubplot(3,1,2);imshow(m .- mf,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(dmin,dmax);\ntitle(\"Vtrue - Vfwi difference, rms=$(rms_vf)\");\n\nsubplot(3,1,3);imshow(m .- mf2,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(dmin,dmax);\ntitle(\"Vtrue - Vfwi_TV difference, rms=$(rms_vf2)\");\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Display-the-cost-function","page":"FWI Example","title":"Display the cost function","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,4)); clf()\niters = [0:1:niter;]\nplot(ϕ[2:end] ./ ϕ[2], marker=\"o\", label=\"FWI_TV\")\nplot(ϕ2[2:end] ./ ϕ2[2], marker=\"o\", label=\"FWI\")\nylim([0,1.05])\nxlabel(\"Nonlinear Iteration\")\nylabel(\"Normalized cost ||f(v) - d||\")\ntitle(@sprintf(\"FWI Objective Function reduced %.1f percent and %.1f percent with TV\",\n 100 * (ϕ[2] - ϕ[end]) / ϕ[2], 100 * (ϕ2[2] - ϕ2[end]) / ϕ2[2]));\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Display-data-misfit-vs-model-misfit","page":"FWI Example","title":"Display data misfit vs model misfit","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,4)); clf()\n\nc = [norm(m1[i] .- m, 2) for i in 1:length(m1)]\nc2 = [norm(m2[i] .- m, 2) for i in 1:length(m2)]\nloglog(c[2:end], ϕ[2:end], label=\"FWI_TV\", marker=\"s\", linewidth=1)\nloglog(c2[2:end], ϕ2[2:end], label=\"FWI\", marker=\"s\", linewidth=1)\nlegend()\nxlabel(\"Log Model residual\")\nylabel(\"Log Data residual\")\ntitle(\"Misfit Trajectory, LOOK AT THAT TV MODEL ERROR\");\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-data-match","page":"FWI Example","title":"Visualize data match","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/#Generate-data-in-the-FWI-velocity-model","page":"FWI Example","title":"Generate data in the FWI velocity model","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"tf = @elapsed begin\n F0.model.m .= vec(mf)\n df = F0*fsrc;\nend\n@show tf;\n\ntf2 = @elapsed begin\n F0.model.m .= vec(mf2)\n df2 = F0*fsrc;\nend\n@show tf2;","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.24 s\ntf = 10.834594565\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.25 s\ntf2 = 10.631615719","category":"page"},{"location":"tutorials/03_constrained_fwi/#Compute-residuals","page":"FWI Example","title":"Compute residuals","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"rf = df - dobs;\nrf2 = df2 - dobs;","category":"page"},{"location":"tutorials/03_constrained_fwi/#Plot-shot-gathers-for-true,-initial-model,-and-fwi-models","page":"FWI Example","title":"Plot shot gathers for true, initial model, and fwi models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"The table below describes the data images below. We will flip the direction of the residual and modeled data in order to help display the match with the true data. We include the initial data as shown above for easier comparison. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"
      Initial Residual Data
      (flipped)
      True Data Initial Data
      (flipped)
      FWI Residual Data
      (flipped)
      True Data FWI Data
      (flipped)
      ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We first make a function to create the plots that we can re-use for the selected shots.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"zsrc = trunc.([zsrc[i][1] for i=1:nsrc]; digits=6)\nfunction make_plot(index)\n figure(figsize=(8,6)); clf()\n cat2 = hcat(reverse(r.data[index],dims=2), pad, dobs.data[index], pad, reverse(d0.data[index],dims=2))\n catf = hcat(reverse(rf.data[index],dims=2), pad, dobs.data[index], pad, reverse(df.data[index],dims=2))\n catf2 = hcat(reverse(rf2.data[index],dims=2), pad, dobs.data[index], pad, reverse(df.data[index],dims=2))\n subplot(3,1,1);\n imshow(cat2,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\" Initial Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || Initial sz=$(zsrc[index]) (flipped)\");\n subplot(3,1,2);\n imshow(catf2,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\" FWI Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || FWI sz=$(zsrc[index]) (flipped)\");\n tight_layout()\n subplot(3,1,3);\n imshow(catf,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\"TV FWI Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || FWI sz=$(zsrc[index]) (flipped)\");\n tight_layout()\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Data-for-the-1st-shot,-generated-in-the-initial-and-FWI-models","page":"FWI Example","title":"Data for the 1st shot, generated in the initial and FWI models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot(1)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Data-for-the-4th-shot,-generated-in-the-initial-and-FWI-models","page":"FWI Example","title":"Data for the 4th shot, generated in the initial and FWI models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot(4)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Data-for-the-8th-shot,-generated-in-the-initial-and-FWI-models","page":"FWI Example","title":"Data for the 8th shot, generated in the initial and FWI models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot(8)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Remove-workers","page":"FWI Example","title":"Remove workers","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"rmprocs(workers());","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"┌ Warning: rmprocs: process 1 not removed\n└ @ Distributed /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.6/Distributed/src/cluster.jl:1038","category":"page"},{"location":"data_structures/#Data-structures","page":"Data Structures","title":"Data structures","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Pages = [\"data_structures.md\"]","category":"page"},{"location":"data_structures/#Physical-Parameter","page":"Data Structures","title":"Physical Parameter","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Data structure for physical parameter array in JUDI. A PhysicalParameter inherits from julia AbstractVector","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"PhysicalParameter","category":"page"},{"location":"data_structures/#JUDI.PhysicalParameter","page":"Data Structures","title":"JUDI.PhysicalParameter","text":"PhysicalParameter\n n::NTuple{N, T}\n d::NTuple{N, Tf}\n o::NTuple{N, Tf}\n data::Union{Array, Number}\n\nPhysicalParameter structure for physical space parameter.\n\nn: number of gridpoints in (x,y,z) for 3D or (x,z) for 2D\n\nd: grid spacing in (x,y,z) or (x,z) (in meters)\n\no: origin of coordinate system in (x,y,z) or (x,z) (in meters)\n\ndata: the array of the parameter values of size n\n\nConstructor\n\nA PhysicalParameter can be constructed in various ways but always require the origin o and grid spacing d that cannot be infered from the array.\n\nPhysicalParameter(v::Array{T}, d, o) where `v` is an n-dimensional array and n=size(v)\n\nPhysicalParameter(n, d, o; T=Float32) Creates a zero PhysicalParameter\n\nPhysicalParameter(v::Array{T}, A::PhysicalParameter{T, N}) where {T<:Real, N} Creates a PhysicalParameter from the Array `v` with n, d, o from `A`\n\nPhysicalParameter(v::Array{T, N}, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) where `v` is a vector or nd-array that is reshaped into shape `n`\n\nPhysicalParameter(v::T, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) Creates a constant (single number) PhyicalParameter\n\n\n\n\n\n","category":"type"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Unless specified otherwise with the return_array option in Options, the result of a migration/FWIgradient(judiJacobian, fwi_objective, lsrtm_objective) will be wrapped into a PhysicalParameter. THis allow better handling of different model parts and a better representation of the dimensional array.","category":"page"},{"location":"data_structures/#Model-structure","page":"Data Structures","title":"Model structure","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Data structure for velocity models in JUDI.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Model","category":"page"},{"location":"data_structures/#JUDI.Model","page":"Data Structures","title":"JUDI.Model","text":"Model(n, d, o, m; epsilon=nothing, delta=nothing, theta=nothing,\n phi=nothing, rho=nothing, qp=nothing, vs=nothing, nb=40)\n\nThe parameters n, d, o and m are mandatory, whith nb and other physical parameters being optional input arguments.\n\nwhere\n\nm: velocity model in slowness squared (s^2/km^2)\n\nepsilon: Epsilon thomsen parameter ( between -1 and 1)\n\ndelta: Delta thomsen parameter ( between -1 and 1 and delta < epsilon)\n\ntheta: Anisotopy dip in radian\n\nphi: Anisotropy asymuth in radian\n\nrho: density (g / m^3)\n\nqp: P-wave attenuation for visco-acoustic models\n\nvs: S-wave velocity for elastic models.\n\nnb: Number of ABC points\n\n\n\n\n\n","category":"function"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Accessible fields include all of the above parameters p.n, p.d, p.o, p.data. Additionaly, arithmetic operation are all impemented such as addition, multiplication, broadcasting and indexing. Linear algebra operation are implemented as well but will return a standard Julia vector if the matrix used is external to JUDI.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Access fields:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Accessible fields include all of the above parameters, which can be accessed as follows:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"# Access model\nmodel.m\n\n# Access number of grid points\nmodel.n","category":"page"},{"location":"data_structures/#Geometry-structure","page":"Data Structures","title":"Geometry structure","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"JUDI's geometry structure contains the information of either the source or the receiver geometry. Construct an (in-core) geometry object for either a source or receiver set up:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":" Geometry","category":"page"},{"location":"data_structures/#JUDI.Geometry","page":"Data Structures","title":"JUDI.Geometry","text":"GeometryIC\n xloc::Array{Array{T, 1},1}\n yloc::Array{Array{T, 1},1}\n zloc::Array{Array{T, 1},1}\n dt::Array{T,1}\n nt::Array{Integer,1}\n t::Array{T,1}\n\nGeometry structure for seismic sources or receivers. Each field is a cell array, where individual cell entries contain values or arrays with coordinates and sampling information for the corresponding shot position. The first three entries are in meters and the last three entries in milliseconds.\n\nGeometryOOC{T} <: Geometry{T} container::Array{SegyIO.SeisCon,1} dt::Array{T,1} nt::Array{Integer,1} t::Array{T,1} nrec::Array{Integer,1} key::String segydepthkey::String\n\nConstructors\n\nOnly pass dt and n and automatically set t:\n\nGeometry(xloc, yloc, zloc; dt=[], nt=[])\n\nPass single array as coordinates/parameters for all nsrc experiments:\n\nGeometry(xloc, yloc, zloc, dt=[], nt=[], nsrc=1)\n\nCreate geometry structure for either source or receivers from a SegyIO.SeisBlock object. segy_depth_key is the SegyIO keyword that contains the depth coordinate and key is set to either source for source geometry or receiver for receiver geometry:\n\nGeometry(SeisBlock; key=\"source\", segy_depth_key=\"\")\n\nCreate geometry structure for from a SegyIO.SeisCon object (seismic data container):\n\nGeometry(SeisCon; key=\"source\", segy_depth_key=\"\")\n\nExamples\n\n(1) Set up receiver geometry for 2D experiment with 4 source locations and 80 fixed receivers:\n\nxrec = range(100,stop=900,length=80)\nyrec = range(0,stop=0,length=80)\nzrec = range(50,stop=50,length=80)\ndt = 4f0\nt = 1000f0\n\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=t, nsrc=4)\n\n(2) Set up corresponding source geometry (coordinates can be of type linspace or regular arrays):\n\nxsrc = [200,400,600,800]\nysrc = [0,0,0,0]\nzsrc = [50,50,50,50]\n\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=t, nsrc=4)\n\n(3) Read source and receiver geometries from SEG-Y file:\n\nusing SegyIO\nseis_block = segy_read(\"test_file.segy\")\nrec_geometry = Geometry(seis_block; key=\"receiver\", segy_depth_key=\"RecGroupElevation\")\nsrc_geometry = Geometry(seis_block; key=\"source\", segy_depth_key=\"SourceDepth\")\n\nCheck the seis_block's header entries to findall out which keywords contain the depth coordinates. The source depth keyword is either SourceDepth or SourceSurfaceElevation. The receiver depth keyword is typically RecGroupElevation.\n\n(4) Read source and receiver geometries from out-of-core SEG-Y files (for large data sets). Returns an out-of-core geometry object GeometryOOC without the source/receiver coordinates, but a lookup table instead:\n\nusing SegyIO\nseis_container = segy_scan(\"/path/to/data/directory\",\"filenames\",[\"GroupX\",\"GroupY\",\"RecGroupElevation\",\"SourceDepth\",\"dt\"])\nrec_geometry = Geometry(seis_container; key=\"receiver\", segy_depth_key=\"RecGroupElevation\")\nsrc_geometry = Geometry(seis_container; key=\"source\", segy_depth_key=\"SourceDepth\")\n\n\n\n\n\n","category":"type"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"From the optional arguments, you have to pass (at least) two of dt, nt and t. The third value is automatically determined and set from the two other values. a Geometry can be constructed in a number of different ways for in-core and out-of-core cases. Check our examples and the source for additional details while the documentation is being extended.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Access fields:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Accessible fields include all of the above parameters, which can be accessed as follows:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"# Access cell arrays of x coordinates:\ngeometry.xloc\n\n# Access x coordinates of the i-th source location\ngeometry.xloc[i]\n\n# Access j-th receiver location (in x) of the i-th source location\ngeometry.xloc[i][j]","category":"page"},{"location":"data_structures/#Geometry-utilities","page":"Data Structures","title":"Geometry utilities","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"A few utilities to manipulates geometries are provided as well.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"super_shot_geometry\nreciprocal_geom","category":"page"},{"location":"data_structures/#JUDI.super_shot_geometry","page":"Data Structures","title":"JUDI.super_shot_geometry","text":"super_shot_geometry(Geometry)\n\nMerge all the sub-geometries 1:get_nsrc(Geometry) into a single supershot geometry\n\n\n\n\n\n","category":"function"},{"location":"data_structures/#JUDI.reciprocal_geom","page":"Data Structures","title":"JUDI.reciprocal_geom","text":"reciprocal_geom(sourceGeom, recGeom)\n\nApplies reciprocity to the par of geometries sourceGeom and recGeom where each source becomes a receiver and each receiver becomes a source.\n\nThis method expects:\n\nBoth geometries to be In Core. If the geometries are OOC they will be converted to in core geometries\nThe metadata to be compatible. In details all the time sampling rates (dt) and recording times (t) must be the same\nThe source to be single point sources. This method will error if a simultaneous sources (multiple poisiton for a single source) are used.\n\n\n\n\n\n","category":"function"},{"location":"data_structures/#Options-structure","page":"Data Structures","title":"Options structure","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"The options structure allows setting several modeling parameters.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Options","category":"page"},{"location":"data_structures/#JUDI.Options","page":"Data Structures","title":"JUDI.Options","text":"JUDIOptions\n space_order::Integer\n free_surface::Bool\n limit_m::Bool\n buffer_size::AbstractFloat\n save_rate::AbstractFloat\n save_data_to_disk::Bool\n file_path::String\n file_name::String\n sum_padding::Bool\n optimal_checkpointing::Bool\n frequencies::Array\n IC::String\n subsampling_factor::Integer\n dft_subsampling_factor::Integer\n return_array::Bool\n dt_comp::Real\n f0::Real\n\nOptions structure for seismic modeling.\n\nspace_order: finite difference space order for wave equation (default is 8, needs to be multiple of 4)\n\nfree_surface: set to true to enable a free surface boundary condition.\n\nlimit_m: for 3D modeling, limit modeling domain to area with receivers (saves memory)\n\nbuffer_size: if limit_m=true, define buffer area on each side of modeling domain (in meters)\n\nsave_data_to_disk: if true, saves shot records as separate SEG-Y files\n\nfile_path: path to directory where data is saved\n\nfile_name: shot records will be saved as specified file name plus its source coordinates\n\nsum_padding: when removing the padding area of the gradient, sum into boundary rows/columns for true adjoints\n\noptimal_checkpointing: instead of saving the forward wavefield, recompute it using optimal checkpointing\n\nfrequencies: calculate the FWI/LS-RTM gradient in the frequency domain for a given set of frequencies\n\nsubsampling_factor: compute forward wavefield on a time axis that is reduced by a given factor (default is 1)\n\ndft_subsampling_factor: compute on-the-fly DFTs on a time axis that is reduced by a given factor (default is 1)\n\nIC: Imaging condition. Options are 'as, isic, fwi' with \"as\" for adjoint state, isic for the inverse scattering imaging condition and FWI for the complement of isic (i.e isic + fwi = as)\n\nisic: Inverse scattering imaging condition. Deprecated, use IC=\"isic\" instead.\n\nreturn_array: return data from nonlinear/linear modeling as a plain Julia array.\n\ndt_comp: overwrite automatically computed computational time step with this value.\n\nf0: define peak frequency.\n\nConstructor\n\nAll arguments are optional keyword arguments with the following default values:\n\nOptions(;space_order=8, free_surface=false,\n limit_m=false, buffer_size=1e3,\n save_data_to_disk=false, file_path=\"\",\n file_name=\"shot\", sum_padding=false,\n optimal_checkpointing=false,\n num_checkpoints=nothing, checkpoints_maxmem=nothing,\n frequencies=[], isic=false,\n subsampling_factor=1, dft_subsampling_factor=1, return_array=false,\n dt_comp=nothing, f0=0.015f0)\n\n\n\n\n\n","category":"function"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"notes","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Option has been renamed JUDIOptions as of JUDI version 4.0 to avoid potential (and exisiting) conflicts wiht other packages exporting an Options structure.","category":"page"},{"location":"abstract_vectors/#Abstract-Vectors","page":"Abstract vectors","title":"Abstract Vectors","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"JUDI provides abstract vector types that encapsulate seismic related objects. In particula, JUDI defines thre main types for seismic data judiVector, full time-space wavefields judiWavefield and extended source weights judiWeights.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Pages = [\"abstract_vectors.md\"]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"At the core of JUDI's vector types is the abstract type judiMultiSourceVector that represent a dimensionalized Vector{Array} where each sub-array correspond to a single source. All JUDI vector types inhert from this abstract type that implements most of the arithmetic and julia core utilities. As an abstract types, judiMultiSourceVector should not be instantiated but new concrete types based on it should be created if one desire to create its own JUDI multi-source vector type.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"All sub-type of judiMultiSourceVector must implement the following methods to be compatible with JUDI. The following JUDI core types are examples of sub-types.","category":"page"},{"location":"abstract_vectors/#judiVector","page":"Abstract vectors","title":"judiVector","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"The class judiVector is the basic data structure for seismic shot records or seismic sources. From JUDI's perspective, both are treated the same and can be multiplied with modeling operators.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"In the most basic way, judiVectors are contstructed from a Geometry object (containing either source or receiver geometry) and a cell array of data:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"judiVector(geometry::Geometry, data::Array{T, N}) where {T, N}","category":"page"},{"location":"abstract_vectors/#JUDI.judiVector-Union{Tuple{N}, Tuple{T}, Tuple{Geometry, Array{T, N}}} where {T, N}","page":"Abstract vectors","title":"JUDI.judiVector","text":"judiVector\n geometry::Geometry\n data\n\nAbstract vector for seismic data. This vector-like structure contains the geometry and data for either\nreceiver data (shot records) or source data (wavelets).\n\nConstructors\n\nConstruct vector from Geometry structure and cell array of shot records or wavelets. The data keyword\ncan also be a single (non-cell) array, in which case the data is the same for all source positions:\n\njudiVector(geometry, data)\n\nConstruct vector for observed data from SegyIO.SeisBlock. segy_depth_key is the SegyIO keyword \nthat contains the receiver depth coordinate:\n\njudiVector(SegyIO.SeisBlock; segy_depth_key=\"RecGroupElevation\")\n\nConstruct vector for observed data from out-of-core data container of type SegyIO.SeisCon:\n\njudiVector(SegyIO.SeisCon; segy_depth_key=\"RecGroupElevation\")\n\nExamples\n\n(1) Construct data vector from Geometry structure and a cell array of shot records:\n\ndobs = judiVector(rec_geometry, shot_records)\n\n(2) Construct data vector for a seismic wavelet (can be either cell arrays of individual\nwavelets or a single wavelet as an array):\n\nq = judiVector(src_geometry, wavelet)\n\n(3) Construct data vector from SegyIO.SeisBlock object:\n\nusing SegyIO\nseis_block = segy_read(\"test_file.segy\")\ndobs = judiVector(seis_block; segy_depth_key=\"RecGroupElevation\")\n\n(4) Construct out-of-core data vector from SegyIO.SeisCon object (for large SEG-Y files):\n\nusing SegyIO\nseis_container = segy_scan(\"/path/to/data/directory\",\"filenames\",[\"GroupX\",\"GroupY\",\"RecGroupElevation\",\"SourceDepth\",\"dt\"])\ndobs = judiVector(seis_container; segy_depth_key=\"RecGroupElevation\")\n\n\n\n\n\n","category":"method"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields (in-core data containers):","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access i-th shot record\nx.data[i]\n\n# Extract judiVector for i-th shot\nx1 = x[i]\n\n# Access j-th receiver location of i-th shot\nx.geometry.xloc[i][j]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields (out-of-core data containers):","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access data container of i-th shot\nx.data[i]\n\n# Read data from i-th shot into memory\nx.data[i][1].data\n\n# Access out-of-core geometry\nx.geometry\n\n# Load OOC geometry into memory\nGeometry(x.geometry)","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"In-core judiVectors can be used like regular Julia arrays and support common operations such as:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"x = judiVector(geometry, data)\n\n# Size (as if all data was vectorized)\nsize(x)\n\n# Norms\nnorm(x)\n\n# Inner product\ndot(x, x)\n\n# Addition, subtraction (geometries must match)\ny = x + x\nz = x - y\n\n# Scaling\nα = 2f0\ny = x * α\n\n# Concatenate\ny = vcat(x, x)","category":"page"},{"location":"abstract_vectors/#judiWavefield","page":"Abstract vectors","title":"judiWavefield","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Abstract vector class for wavefields. ","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":" judiWavefield","category":"page"},{"location":"abstract_vectors/#JUDI.judiWavefield","page":"Abstract vectors","title":"JUDI.judiWavefield","text":"judiWavefield nsrc::Integer dt::AbstractFloat data\n\nAbstract vector for seismic wavefields.\n\nConstructors\n\nConstruct wavefield vector from an info structure, a cell array of wavefields and the computational \ntime step dt:\n\njudiWavefield(nsrc, dt, data)\n\n\n\n\n\n","category":"type"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access wavefield from i-th shot location\nu.data[i]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Supports some basic arithmetric operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Size \nsize(u)\n\n# Norms\nnorm(u)\n\n# Inner product \ndot(u, y)\n\n# Addition, subtraction\nv = u + u\nz = u - v\n\n# Absolute value\nabs(u)\n\n# Concatenation\nv = vcat(u, u)","category":"page"},{"location":"abstract_vectors/#judiRHS","page":"Abstract vectors","title":"judiRHS","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Abstract vector class for a right-hand-side (RHS). A RHS has the size of a full wavefield, but only contains the data of the source wavelet of shot records in memory, as well as the geometry information of where the data is injected during modeling.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"rhs = judiRHS(geometry, data)","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"A JUDI RHS can also be constructed by multplying a judiVector and the corresponding transpose of a judiProjection operator:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"rhs1 = Ps'*q\nrhs2 = Pr'*d_obs","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"where Ps and Pr are judiProjection operators for sources and receivers respectively and q and d_obs are judiVectors with the source and receiver data.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Accessible fields include:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Source/receiver data\nrhs.data\n\n# Source/receiver geometry\nrhs.geometry\n\n# Info structure\nrhs.info","category":"page"},{"location":"abstract_vectors/#judiWeights","page":"Abstract vectors","title":"judiWeights","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Abstract vector class for extended source weights. The weights for each shot location have the dimensions of the model (namely model.n).","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"judiWeights(weights::Array{T, N}; nsrc=1, vDT::DataType=Float32) where {T<:Real, N}","category":"page"},{"location":"abstract_vectors/#JUDI.judiWeights-Union{Tuple{Array{T, N}}, Tuple{N}, Tuple{T}} where {T<:Real, N}","page":"Abstract vectors","title":"JUDI.judiWeights","text":"judiWeights\n nsrc\n weights\n\nAbstract vector for weighting an extended source, which is injected at every grid point, as weighted by this vector. Constructors ============ Construct vector cell array of weights. The weights keyword\ncan also be a single (non-cell) array, in which case the weights are the same for all source positions: judiWeights(weights; nsrc=1)\n\n\n\n\n\n","category":"method"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Parameters:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"weights: Cell array with one cell per shot location. Each cell contains a 2D/3D Julia array with the weights for the spatially extended source. Alternatively: pass a single Julia array which will be used for all source locations.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access weights of i-th shot locatoin\nw.weights[i]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Supports the same arithmetric operations as a judiVector.","category":"page"},{"location":"#The-Julia-Devito-Inversion-framework-(JUDI.jl)","page":"Home","title":"The Julia Devito Inversion framework (JUDI.jl)","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"JUDI is a framework for large-scale seismic modeling and inversion and designed to enable rapid translations of algorithms to fast and efficient code that scales to industry-size 3D problems. Wave equations in JUDI are solved with Devito, a Python domain-specific language for automated finite-difference (FD) computations. ","category":"page"},{"location":"#Docs-overview","page":"Home","title":"Docs overview","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"This documentation provides an overview over JUDI's basic data structures and abstract operators:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Installation: Install guidlines for JUDI and compilers.\nGetting Started: A few simple guided examples to get familiar with JUDI.\nData structures: Explains the Model, Geometry and Options data structures and how to set up acquisition geometries.\nAbstract Vectors: Documents JUDI's abstract vector classes judiVector, judiWavefield, judiRHS, judiWeights and judiExtendedSource.\nLinear Operators: Lists and explains JUDI's abstract linear operators judiModeling, judiJacobian, judiProjection and judiLRWF.\nInput/Output: Read SEG-Y data and set up judiVectors for shot records and sources. Read velocity models.\nHelper functions: API of functions that make your life easier.\nSeismic Inversion: Inversion utility functions to avoid recomputation and memry overhead.\nSeismic Preconditioners: Basic preconditioners for seismic imaging.\npysource package: API reference for the propagators implementation with Devito in python. The API is the backend of JUDI handled with PyCall.","category":"page"}] +[{"location":"tutorials/01_intro/#Introduction-to-JUDI","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI is a framework for large-scale seismic modeling and inversion and designed to enable rapid translations of algorithms to fast and efficient code that scales to industry-size 3D problems. The focus of the package lies on seismic modeling as well as PDE-constrained optimization such as full-waveform inversion (FWI) and imaging (LS-RTM). Wave equations in JUDI are solved with Devito, a Python domain-specific language for automated finite-difference (FD) computations. JUDI's modeling operators can also be used as layers in (convolutional) neural networks to implement physics-augmented deep learning algorithms. For this, check out JUDI's deep learning extension via ChainRules.jl in this tutorial.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"using JUDI, PyPlot, LinearAlgebra","category":"page"},{"location":"tutorials/01_intro/#Physical-problem-setup","page":"Introduction to JUDI","title":"Physical problem setup","text":"","category":"section"},{"location":"tutorials/01_intro/#Grid","page":"Introduction to JUDI","title":"Grid","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI relies on a cartesian grid for modeling and inversion. We start by defining the parameters needed for a cartesian grid:","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"A shape\nA grid spacing in each direction\nAn origin","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"shape = (201, 201) # Number of gridpoints nx, nz\nspacing = (10.0, 10.0) # #n meters here\norigin = (0.0, 0.0) # In meters as well","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(0.0, 0.0)","category":"page"},{"location":"tutorials/01_intro/#Physical-object","page":"Introduction to JUDI","title":"Physical object","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI defines a few basic types to handle physical object such as the velocity model. The type PhyisicalParameter is an abstract vector and behaves as a standard vector. A PhysicalParameter can be constructed in various ways but always require the origin o and grid spacing d that cannot be infered from the array.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"PhysicalParameter(v::Array{vDT}, d, o) where `v` is an n-dimensional array and n=size(v)\n\nPhysicalParameter(n, d, o; vDT=Float32) Creates a zero PhysicalParameter\n\nPhysicalParameter(v::Array{vDT}, A::PhysicalParameter) Creates a PhysicalParameter from the Array `v` with n, d, o from `A`\n\nPhysicalParameter(v::Array{vDT, N}, n::Tuple, d::Tuple, o::Tuple) where `v` is a vector or nd-array that is reshaped into shape `n`\n\nPhysicalParameter(v::vDT, n::Tuple, d::Tuple, o::Tuple) Creates a constant (single number) PhyicalParameter","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Let's make a simple three layer velocity model","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"# Define the velocity (in km/sec=m/ms)\nvp = 1.5f0 * ones(Float32, shape)\nvp[:, 66:end] .= 2.0f0\nvp[:, 134:end] .= 2.5f0\n# Create a physical parameter\nVP = PhysicalParameter(vp, spacing, origin);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Let's plot the velocities. Because we adopt a standad cartesian dimension ordering for generality (X, Z) in 2D and (X, Y, Z) in 3D, we plot the transpose of the velocity for proper visualization.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"figure()\nsubplot(121)\nimshow(vp', cmap=\"jet\")\ntitle(\"vp\")\nsubplot(122)\nimshow(VP', cmap=\"jet\")\ntitle(\"vp as a physical parameter\")","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"PyObject Text(0.5, 1.0, 'vp as a physical parameter')","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Because the physical parameter behaves as vector, we can easily perform standard operations on it.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"norm(VP), extrema(VP), 2f0 .* VP, VP .^ 2","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(411.6956f0, (1.5f0, 2.5f0), PhysicalParameter{Float32} of size (201, 201) with origin (0.0, 0.0) and spacing (10.0, 10.0), PhysicalParameter{Float32} of size (201, 201) with origin (0.0, 0.0) and spacing (10.0, 10.0))","category":"page"},{"location":"tutorials/01_intro/#Model","page":"Introduction to JUDI","title":"Model","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI then provide a Model structure that wraps multiple physical parameters toghether. A Model accept currently only accept standard Array as an input (to be fixed #1)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"model = Model(shape, spacing, origin, 1f0./vp.^2f0)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Model (n=(201, 201), d=(10.0f0, 10.0f0), o=(0.0f0, 0.0f0)) with parameters [:m]","category":"page"},{"location":"tutorials/01_intro/#Modeling","page":"Introduction to JUDI","title":"Modeling","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now that we have a seismic model, we will generate a few shot records.","category":"page"},{"location":"tutorials/01_intro/#Acquisition-Geometry","page":"Introduction to JUDI","title":"Acquisition Geometry","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"The first thing we need is an acquisiton geometry. In JUDI there is two ways to create a Geometry.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"By hand, as we will show here\nFrom a SEGY file, as we will show in a follow-up tutorial","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"We create a split-spread geomtry with sources at the top and receivers at the ocean bottom (top of second layer).","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Note:","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI currently expects all three coordinates to be inputed to setup a Geometry in 2D as well. This will be fixed in a later version of JUDI.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"# Sources position\nnsrc = 11\nxsrc = range(0f0, (shape[1] -1)*spacing[1], length=nsrc)\nysrc = 0f0 .* xsrc # this a 2D case so we set y to zero\nzsrc = 12.5f0*ones(Float32, nsrc);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now this definition creates a single Array of position, which would correspond to a single Simultenous source. Since we are interested in single source experiments here, we convert these position into an Array of Array of size nsrc where each sub-array is a single source position","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"xsrc, ysrc, zsrc = convertToCell.([xsrc, ysrc, zsrc]);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"# OBN position\nnrec = 101\nxrec = range(0f0, (shape[1] -1)*spacing[1], length=nrec)\nyrec = 0f0 # this a 2D case so we set y to zero. This can be a single number for receivers\nzrec = (66*spacing[1])*ones(Float32, nrec);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"The last step to be able to create and acquisiton geometry is to define a recording time and sampling rate","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"record_time = 4000f0 # Recording time in ms (since we have m/ms for the velocity)\nsampling_rate = 4f0; # Let's use a standard 4ms sampling rate","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now we can create the source and receivers geometry","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"src_geom = Geometry(xsrc, ysrc, zsrc; dt=sampling_rate, t=record_time)\n# For the receiver geometry, we specify the number of source to tell JUDI to use the same receiver position for all sources\nrec_geom = Geometry(xrec, yrec, zrec, dt=sampling_rate, t=record_time, nsrc=nsrc);","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Let's visualize the geometry onto the model","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"figure();\nimshow(vp', cmap=\"jet\", extent=[0, (shape[1]-1)*spacing[1], (shape[2]-1)*spacing[2], 0])\nscatter(xsrc, zsrc, label=:sources)\nscatter(xrec, zrec, label=\"OBN\")\nlegend()","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"PyObject ","category":"page"},{"location":"tutorials/01_intro/#Source-wavelet","page":"Introduction to JUDI","title":"Source wavelet","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"For the source wavelet, we will use a standard Ricker wavelet at 10Hz for this tutorial.In practice this wavelet would be read from a file or estimated during inversion. ","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"f0 = 0.010 # Since we use ms, the frequency is in KHz\nwavelet = ricker_wavelet(record_time, sampling_rate, f0);\nplot(wavelet)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"1-element Vector{PyCall.PyObject}:\n PyObject ","category":"page"},{"location":"tutorials/01_intro/#judiVector","page":"Introduction to JUDI","title":"judiVector","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"In order to represent seismic data, JUDI provide the judiVector type. This type wraps a geometry with the seismic data corresponding to it. Let's cretae one for the source","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"q = judiVector(src_geom, wavelet)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"judiVector{Float32, Matrix{Float32}} with 11 sources","category":"page"},{"location":"tutorials/01_intro/#Linear-operator","page":"Introduction to JUDI","title":"Linear operator","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"The last step to model our seismic data os to create the linear operator representing the discretized wave equation on the Model we defined. We also need to define the linear operator corresponding to the source injection and the receiver interpolation.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Pr = judiProjection(rec_geom) # receiver interpolation\nPs = judiProjection(src_geom) # Source interpolation\nAinv = judiModeling(model) # Inverse of the disrete ewave equation.","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"JUDI forward{Float32} propagator (x * z * time) -> (x * z * time)","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"WARNING While these three operator are well defined in JUDI, judiProjection is a no-op operator and cannot be used by itself but only in combination with a judiModeling operator","category":"page"},{"location":"tutorials/01_intro/#Seismic-data","page":"Introduction to JUDI","title":"Seismic data","text":"","category":"section"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Now that we have all our operators setup we can finally generate synthetic data wit ha simple mat-vec product thanks to the abstraction","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"d_obs = Pr * Ainv * Ps' * q","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"Building forward operator\nOperator `forward` ran in 0.48 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.69 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.69 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.66 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.68 s\nOperator `forward` ran in 0.66 s\nOperator `forward` ran in 0.68 s\n\n\n\n\n\njudiVector{Float32, Matrix{Float32}} with 11 sources","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"data_extent = [xrec[1], xrec[end], 1f-3*record_time, 0]\nfigure(figsize=(20, 5))\nfor i=1:5\n subplot(1, 5, i)\n imshow(d_obs.data[2*i], vmin=-1, vmax=1, cmap=\"Greys\", extent=data_extent, aspect=\"auto\")\n xlabel(\"Receiver position (m)\")\n ylabel(\"Recording time (s)\")\n title(\"xsrc=$(1f-3xsrc[2*i][1])km\")\nend\ntight_layout()","category":"page"},{"location":"tutorials/01_intro/","page":"Introduction to JUDI","title":"Introduction to JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Geophysics-tutorial","page":"Full-Waveform Inversion - Part 3: optimization","title":"Geophysics tutorial","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Full-Waveform-Inversion-Part-3:-optimization","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Philipp Witte1*, Mathias Louboutin1, Keegan Lensink1, Michael Lange2, Navjot Kukreja2, Fabio Luporini2, Gerard Gorman2, and Felix J. Herrmann1,3","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"1 Seismic Laboratory for Imaging and Modeling (SLIM), The University of British Columbia ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"2 Imperial College London, London, UK","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"3 now at Georgia Institute of Technology, USA ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Corresponding author: pwitte.slim@gmail.com","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Introduction","page":"Full-Waveform Inversion - Part 3: optimization","title":"Introduction","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This tutorial is the third part of a full-waveform inversion (FWI) tutorial series with a step-by-step walkthrough of setting up forward and adjoint wave equations and building a basic FWI inversion framework. For discretizing and solving wave equations, we use [Devito], a Python-based domain-specific language for automated generation of finite-difference code (Lange et al., 2016). The first two parts of this tutorial (Louboutin et al., 2017, 2018) demonstrated how to solve the acoustic wave equation for modeling seismic shot records and how to compute the gradient of the FWI objective function using the adjoint-state method. With these two key ingredients, we will now build an inversion framework that can be used to minimize the FWI least-squares objective function.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"[Devito]:http://www.opesci.org/devito-public [Julia]:https://julialang.org [JUDI]:https://github.com/slimgroup/JUDI.jl/tree/tletutorialjan_2018","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"FWI is a computationally and mathematically challenging problem. The computational complexity comes from the fact that an already expensive solution procedure for the wave equation needs to be repeated for a large number of source positions for each iteration of the optimization algorithm. The mathematical complexity comes from the fact that the FWI objective is known to have many local minima due to cycle skipping. ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Furthermore, since the solution is not unique it is generally not possible to unambiguously recover the parameterization of the subsurface from the given data alone, making FWI an active field of research (e.g. Leeuwen et al., 2013; Warner and Guasch, 2014; Peters and Herrmann, 2017).

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This tutorial demonstrates how we can set up a basic FWI framework with two alternative gradient-based optimization algorithms: stochastic gradient descent, and the Gauss–Newton method (Nocedal and Wright, 2009). ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"We implement our inversion framework with the Julia Devito Inversion framework (JUDI), a parallel software package for seismic modeling and inversion in the Julia (Bezanson et al., 2012) programming language. JUDI provides abstractions and function wrappers that allow the implementation of wave-equation-based inversion problems such as FWI using code that closely follows the mathematical notation, while using Devito’s automatic code generation for solving the underlying wave equations.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"All the code to run the algorithms and generate the figures in this paper can be found at http://github.com/SEG. ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Optimizing-the-FWI-objective-function","page":"Full-Waveform Inversion - Part 3: optimization","title":"Optimizing the FWI objective function","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"The goal of this tutorial series is to optimize the FWI objective function with the ell_2-misfit:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"\\mathop{\\hbox{minimize}}_{\\mathbf{m}} \\hspace{.2cm} f(\\mathbf{m})= \\sum_{i=1}^{n_s} \\frac{1}{2} \\left\\lVert \\mathbf{d}^\\mathrm{pred}_i (\\mathbf{m}, \\mathbf{q}_i) - \\mathbf{d}_i^\\mathrm{obs} \\right\\rVert_2^2,","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"where mathbfd^mathrmpred_i and mathbfd^mathrmobs_i are the predicted and observed seismic shot records of the i^textth source location and mathbfm is the velocity model (expressed as squared slowness). In part one, we demonstrated how to implement a forward modeling operator to generate the predicted shot records, which we will denote as mathbfd^mathrmpred_i = mathbfF(mathbfmmathbfq_i). In the second tutorial, we then showed how we can compute the gradient nabla f(mathbfm) of the objective function and update our initial model using gradient descent.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"There is a snag, however. This first-order optimization algorithm has a linear convergence rate at best, and typically requires many iterations to converge. Second-order optimization methods converge considerably faster. To implement them, we first approximate the objective with a second-order Taylor expansion:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"f(\\mathbf{m}) = f(\\mathbf{m}_0) + \\nabla f(\\mathbf{m}_0) \\delta \\mathbf{m} + \\delta \\mathbf{m}^\\top \\nabla^2 f(\\mathbf{m}_0) \\delta \\mathbf{m} + \\mathcal{O}(\\delta \\mathbf{m}^3),","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"where mathcalO(delta mathbfm^3) represents the error term, nabla f(mathbfm_0) is the gradient as implemented in part two, and nabla^2 f(mathbfm_0) is the Hessian of the objective function, which we will refer to as mathbfH. Rather than using the negative gradient to incrementally update our model, as in gradient descent, we directly calculate a model update delta mathbfm that leads us to the minimum. This is called Newton's method:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"\\delta \\mathbf{m} = - \\mathbf{H}(\\mathbf{m}_0)^{-1} \\nabla f(\\mathbf{m}_0).","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Although the method converges to the minimum of the FWI objective function quickly, it comes at the cost of having to compute and invert the Hessian matrix (Nocedal and Wright, 2009). Fortunately, for least squares problems, such as FWI, the Hessian can be approximated by the Gauss-Newton (GN) Hessian mathbfJ^top mathbfJ, where mathbfJ is the Jacobian matrix. This is the partial derivative of the forward modeling operator mathbfF(mathbfmmathbfq) with respect to mathbfm — something we can easily compute. Furthermore, the Jacobian can also be used to express the gradient of the FWI objective function as nabla f(mathbfm_0) = mathbfJ^top (mathbfd^mathrmpred_i - mathbfd_i^mathrmobs), where mathbfJ^top is the adjoint (transposed) Jacobian. This is useful, because we now have a set of operators mathbfF mathbfJ and mathbfH_GN=mathbfJ^topmathbfJ, through which we can express both first and second order optimization algorithms for FWI.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Although forming these matrices explicitly is not possible, since they can become extremely large, we only need the action of these operators on vectors. This allows us to implement these operators matrix-free. In the following section we will demonstrate how to set up these operators in our JUDI software framework and to how to use them to implement FWI algorithms.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      [SegyIO]:https://github.com/slimgroup/SegyIO.jl

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      The Julia Devito Inversion framework is a parallel matrix-free linear operator library for seismic modeling and inversion based on Devito and [SeisIO], a performant Julia package for reading and writing large data volumes in SEG-Y format. JUDI allows implementing seismic inversion algorithms as linear algebra operations, enabling rapid translations of FWI algorithms to executable Julia code. The underlying wave equations are set up and solved using Devito, as described in the first two tutorials, and are interfaced from Julia using the PyCall package (Johnson, 2017).

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      For reading and writing SEG-Y data, JUDI uses the SeisIO package, a sophisticated SEG-Y reader that allows us to scan large 3D data sets for creating look-up tables with header summaries. However, since our data set is relatively small, we will directly load the full file into memory. The segy_read command takes the file name as an input and returns a dense data block. This is our observed data and we store it as a JUDI vector.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"# NOT FOR MANUSCRIPT\n# using Distributed\n# addprocs(4);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using JUDI.TimeModeling, JUDI.SLIM_optim, SegyIO, Random, Statistics","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Implementing-FWI-in-JUDI","page":"Full-Waveform Inversion - Part 3: optimization","title":"Implementing FWI in JUDI","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"We start our demonstration by reading our data set, which consists of 16 shot records and was generated with an excerpt from the SEG/EAGE Overthrust model (Aminzadeh et al. 1997). We store it as a judiVector:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"block = segy_read(\"overthrust_shot_records.segy\")\nd_obs = judiVector(block);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using PyPlot\n\ni = 8\nfigure(figsize=(15,6))\nimshow(d_obs.data[i], cmap=\"seismic\", extent=[0,size(d_obs.data[i],2),2,0], aspect=\"auto\", vmin=-3, vmax=3)\nxlabel(\"Receiver number\", size=12)\nylabel(\"Time [s]\", size=12)\ntext(3, 0.13, \"Shot record $i\", size=16)\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Figure 1: Observed shot record number 8.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"JUDI vectors such as d_obs can be used like a regular Julia vectors, so we can compute norms via norm(d_obs) or the inner product via dot(d_obs, d_obs), but they contain the shot records in their original dimension. Shot records can be accessed via their respective shot number with d_obs.data[shot_no], while the header information can be accessed with d_obs.geometry. We extract the source geometry from our SEG-Y file and then manually set up a source vector q with an 8 Hz Ricker wavelet:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"f = 0.008 # kHz\nsrc_geom = Geometry(block; key=\"source\")\nsrc_data = ricker_wavelet(src_geom.t[1], src_geom.dt[1], f)\nq = judiVector(src_geom, src_data);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"plot(q.data[1])\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Since our data set consists of 16 shot records, both d_obs and q contain the data and geometries for all source positions. We can check the number of source positions with d_obs.nsrc and q.nsrc and we can extract the part of the vector that corresponds to one or multiple shots with d_obs[shot_no], q[shot_no].

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"We will now set up the forward modeling operator mathbfF(mathbfmmathbfq) as a matrix-free operator for the inverse wave equation mathbfA(mathbfm)^-1, where mathbfm is the current model, and source/receiver injection and sampling operators mathbfP_mathrms and mathbfP_mathrmr.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Since the dimensions of the inverse wave equation operator depend on the number of computational time steps, we calculate this number using the get_computational_nt function and set up an info object that contains some dimensionality information required by all operators.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Then we can define Pr and Ps as matrix-free operators implementing Devito sparse point injection and interpolation (Louboutin et al., 2017). Multiplications with Ps and Pr represent sampling the wavefield at source/receiver locations, while their adjoints Ps', Pr' denote injecting either source wavelets or shot records into the computational grid.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"These projection and modelling operators can then be set up in Julia in the following way:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using HDF5\n\nm0, n, d, o = read(h5open(\"overthrust_model.h5\",\"r\"),\"m0\",\"n\",\"d\",\"o\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"\n\nPr = judiProjection(d_obs.geometry)\nPs = judiProjection(q.geometry)\nAinv = judiModeling(model0);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"The forward modeling step can be expressed mathematically as","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"$","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"F(\\mathbf{m};\\mathbf{q})=\\mathbf{P}r\\mathbf{A}^{-1}(\\mathbf{m})\\mathbf{P}s^\\top\\mathbf{q} $","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"which is expressed in Julia as","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"d_pred = Pr * Ainv * Ps' * q","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This forward models all 16 predicted shot records in parallel. Notice that, in instantiating Ainv, we made the wave equation solver implicitly dependent on model0.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Finally, we set up the matrix-free Jacobian operator J and the Gauss–Newton Hessian J' * J. As mentioned in the introduction, J is the partial derivative of the forward modeling operator mathbfF(mathbfm mathbfq) with respect to the model m and is therefore directly constructed from our modeling operator Pr * Ainv * Ps' and a specified source vector q:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"J = judiJacobian(Pr * Ainv * Ps', q);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Note that, if we started our Julia session with multiple CPU cores or nodes (julia -p n, with n being the number of workers), the wave equation solves are automatically parallelized over source locations and all shots are collected in the d_pred vector.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      We can also model a single or subset of shots by indexing the operators with the respective shot numbers. E.g. if we want to model the first two shots, we define i=[1,2] and then run d_sub = Pr[i]*Ainv[i]*Ps[i]'*q[i].

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      The FWI gradient would then be given by:

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
       g = J[i]' * (d_pred - d_obs[i]) 
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      If we want to solve an adjoint wave equation with the observed data as the adjoint source and restrictions of the wavefields back to the source locations, we can simply run qad = Ps * Ainv' * Pr' * d_obs, exemplifying the advantages of casting FWI in a proper computational linear algebra framework.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      Once we have J we can also form the Gauss–Newton Hessian:

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
       H_GN = J' * J 
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"In the context of seismic inversion, the Jacobian is also called the linearized modeling or demigration operator and its adjoint J' is the migration operator. One drawback of this notation is that the forward wavefields for the gradient calculation have to be recomputed, since the forward modeling operator only returns the shot records and not the complete wavefields. For this reason, JUDI has an additional function for computing the gradients of the FWI objective function f,g = fwi_objective(model0,q[i],d_obs[i]), which takes the current model, source and data vectors as an input and computes the objective value and gradient in parallel without having to recompute the forward wavefields.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#FWI-via-gradient-descent","page":"Full-Waveform Inversion - Part 3: optimization","title":"FWI via gradient descent","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"With expressions for modeling operators, Jacobians and gradients of the FWI objective, we can now implement different FWI algorithms in a few lines of code. We will start with a basic gradient descent example with a line search. To reduce the computational cost of full gradient descent, we will use a stochastic approach in which we only compute the gradient and function value for a randomized subset of source locations. In JUDI, this is accomplished by choosing a random vector of integers between 1 and 16 and indexing the data vectors as described earlier. Furthermore, we will apply a projection operator proj(x), which prevent velocities (or squared slownesses) becoming negative or too large by clipping values outside the allowed range.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"A few extra variables are defined in the notebook, but the full algorithm for FWI with stochastic gradient descent and box constraints is implemented as follows:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"# Set up bound constraints.\nv0 = sqrt.(1f0./model0.m)\nvmin = ones(Float32, model0.n) .* 1.3f0\nvmax = ones(Float32, model0.n) .* 6.5f0\n\n# Fix water column.\nvmin[:,1:21] = v0[:,1:21]\nvmax[:,1:21] = v0[:,1:21]\n\n# Convert to squared slowness.\nmmin = vec((1f0./vmax).^2)\nmmax = vec((1f0./vmin).^2);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"maxiter = 10\nbatchsize = 8 # Number of shots for each iteration.\nproj(x) = reshape(median([vec(mmin) vec(x) vec(mmax)], dims=2), model0.n)\nfhistory_SGD = zeros(Float32, maxiter);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"for j=1:maxiter\n \n # FWI objective function value and gradient.\n i = randperm(d_obs.nsrc)[1:batchsize]\n fval, grad = fwi_objective(model0, q[i], d_obs[i])\n fhistory_SGD[j] = fval\n\n # Line search and update model.\n update = backtracking_linesearch(model0, q[i], d_obs[i], fval, grad, proj; alpha=1f0)\n model0.m += reshape(update, model0.n)\n\n # Apply box constraints.\n model0.m = proj(model0.m)\nend","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"model_SGD = copy(model0.m);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"JUDI's backtracking_linesearch function performs an approximate line search and returns a model update that leads to a decrease of the objective function value (Armijo condition; Nocedal and Wright, 2009). The result after 10 iterations of SGD with box constraints is shown in Figure 2. In practice, where starting models are typically less accurate than in our example, FWI is often performed from low to high frequencies, since the objective function has less local minima for lower frequencies (Bunks et al., 1995). In this multi-scale FWI approach, a low-pass filtered version of the data is used to invert for a low resolution velocity model first and higher frequencies are added in subsequent iterations.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#FWI-via-the-Gauss–Newton-method","page":"Full-Waveform Inversion - Part 3: optimization","title":"FWI via the Gauss–Newton method","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"As discussed earlier, the convergence rate of GD depends on the objective function, but requires many FWI iterations necessary to reach an acceptable solution. Using our matrix-free operator for the Jacobian J, we can modify the above code to implement the Gauss–Newton method (Equation 3) to improve the convergence rate. In practice, directly inverting the Gauss–Newton Hessian J'* J should be avoided, because the matrix is badly conditioned and takes many iterations to invert. Instead, we perform a few iterations of a least-squares solver, lsqr(), to approximately solve J * p = d_pred - d_obs and obtain the update direction p. lsqr, from the Julia IterativeSolvers package, is a conjugate-gradient type algorithm for solving least squares problems and is mathematically equivalent to inverting J' * J, but has better numerical properties (Paige and Saunders, 1982). We implement the Gauss-Newton method as follows:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"model0.m = m0 # Reset velocity model.\nfhistory_GN = zeros(Float32,maxiter);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"using IterativeSolvers\n\n# Return data as Julia array\nAinv.options.return_array = true\nd_vec = convert_to_array(d_obs)\n\nfor j=1:maxiter\n \n # Model predicted data.\n d_pred = Pr * Ainv * Ps' * q\n\n # GN update direction.\n p = lsqr(J, d_pred - d_obs; maxiter=6)\n fhistory_GN[j] = .5f0 * norm(d_pred - d_vec)^2 # OMIT LINE FROM MANUSCRIPT\n\n # update model and box constraints.\n model0.m = model0.m - reshape(p, model0.n)\nend","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      NOT FOR MANUSCRIPT

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"

      An important benefit of matrix-free operators is that we do not need to implement our own least-squares solver, but can pass J and the data residual d_pred - d_obs to a third-party optimization library. The operator J does not need to be an explicit matrix, since lsqr only uses matrix-vector products.

      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"
      ","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"In contrast to our SGD algorithm, we use all shot records in every iteration, since stochastic methods for second order algorithms are less well understood, making this approach considerably more expensive than our previous algorithm. However, as shown in figures 2 and 3, it achieves a superior result, with a considerably lower misfit compared to the known model. Furthermore, figure 3 shows that it achieves the improved result in relatively few iterations.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"figure(figsize=(15, 12))\n\nsubplot(311)\nimshow(sqrt.(1f0./m0)', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4)\ntext(0.15, 0.3, \"Starting model\", size=14, color=\"black\")\nylabel(\"Depth [km]\", size=12)\ncolorbar()\ntext(10.35, 1.5, \"Velocity [km/s]\", va=\"center\", size=12, rotation=90)\n\nsubplot(312)\nimshow(sqrt.(1f0./model_SGD)', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4)\nmisfit = round(fhistory_SGD[end])\ntext(0.15, 0.3, \"Gradient descent, misfit = $misfit\", size=14, color=\"black\")\nylabel(\"Depth [km]\", size=12)\ncolorbar()\ntext(10.35, 1.5, \"Velocity [km/s]\", va=\"center\", size=12, rotation=90)\n\nsubplot(313)\nimshow(sqrt.(1f0./model0.m)', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4)\nmisfit = round(fhistory_GN[end])\ntext(0.15, 0.3, \"Gauss–Newton, misfit = $misfit\", size=14, color=\"black\")\nxlabel(\"Lateral position [km]\", size=12)\nylabel(\"Depth [km]\", size=12)\ncolorbar()\ntext(10.35, 1.5, \"Velocity [km/s]\", va=\"center\", size=12, rotation=90)\n\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"fhistory_SGD = fhistory_SGD/norm(fhistory_SGD, Inf)\nfhistory_GN = fhistory_GN/norm(fhistory_GN, Inf);","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"figure(figsize=(8,3))\nplot(1:10, fhistory_SGD, label=\"Gradient descent\")\nplot(1:10, fhistory_GN, label=\"Gauss–Newton\")\ntext(9.8, 0.28, \"Gradient descent\", ha=\"right\", color=\"steelblue\")\ntext(9.8, 0.09, \"Gauss–Newton\", ha=\"right\", color=\"chocolate\")\nxlabel(\"Iteration number\", size=12)\nylabel(\"Misfit\", size=12)\nxlim(1, 10)\nshow()","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Figure 3: Normalized function values for the FWI inversion example with stochastic gradient descent and the Gauss-Newton method.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"An alternative to (Gauss–)Newton methods are quasi-Newton methods, which build up an approximation of the Hessian from previous gradients only and require no additional PDE solves or matrix inversions. Implementing an efficient and correct version of this method, such as the L-BFGS algorithm, exceeds a few lines of code and we therefore leave this exercise to the reader. Instead of implementing more complicated algorithms by hand, it is also possible to interface third-party Julia optimization libraries and an example for this is given in the notebook fwiexampleNLopt.ipynb.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Even though all examples shown here are two-dimensional, in order to make them reproducible on a laptop or desktop PC, JUDI can be used for 3D modeling and inversion without having to change the code, since the number of dimensions are automatically inferred from the velocity model and data dimensions.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Conclusions","page":"Full-Waveform Inversion - Part 3: optimization","title":"Conclusions","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"In this final part of our FWI tutorial series, we demonstrated how to set up basic optimization algorithms for waveform inversion using JUDI. The methods shown here are all gradient based and differ in the way how update directions for the velocity model are computed. Our numerical examples can serve for the reader as a basis for developing more advanced FWI workflows, which usually include additional data preprocessing, frequency continuation techniques or further model constraints.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#Acknowledgments","page":"Full-Waveform Inversion - Part 3: optimization","title":"Acknowledgments","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"This research was carried out as part of the SINBAD II project with the support of the member organizations of the SINBAD Consortium. This work was financially supported in part by EPSRC grant EP/L000407/1 and the Imperial College London Intel Parallel Computing Centre.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/#References","page":"Full-Waveform Inversion - Part 3: optimization","title":"References","text":"","category":"section"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Aminzadeh, F., Brac, J., and Kunz, T., 1997. 3D Salt and Overthrust models. SEG/EAGE Modeling Series, No. 1: Distribution CD of Salt and Overthrust models, SEG Book Series Tulsa, Oklahoma.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Bezanson, J., Karpinski, S., Shah, V. B., and Edelman, A., 2012, Julia: A fast dynamic language for technical computing: CoRR. Retrieved from http://arxiv.org/abs/1209.5145","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Bunks, C., Saleck, F. M., Zaleski, S., and Chavent, G., 1995, Multiscale seismic waveform inversion: GEOPHYSICS, 60, 1457–1473. doi:10.1190/1.1443880","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Johnson, S., 2017, Calling python functions from the julia language: GitHub repository. https://github.com/JuliaPy/PyCall.jl; GitHub.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Lange, M., Kukreja, N., Louboutin, M., Luporini, F., Zacarias, F. V., Pandolfo, V., … Gorman, G., 2016, Devito: Towards a generic finite difference DSL using symbolic python: 6th workshop on python for high-performance and scientific computing. doi:10.1109/PyHPC.2016.9","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Leeuwen, T. van, Aravkin, A. Y., Calandra, H., and Herrmann, F. J., 2013, In which domain should we measure the misfit for robust full waveform inversion? EAGE annual conference proceedings. doi:10.3997/2214-4609.20130839","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2017, Full-waveform inversion - part 1: Forward modeling: Retrieved from https://www.slim.eos.ubc.ca/Publications/Private/Submitted/2017/louboutin2017fwi/louboutin2017fwi.html","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2018, Full-waveform inversion - part 2: Adjoint modeling:","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Nocedal, J., and Wright, S., 2009, Numerical optimization: (2nd ed.). Springer.","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Paige, C. C., and Saunders, M. A., 1982, LSQR: An algorithm for sparse linear equations and sparse least squares: ACM Trans. Math. Softw., 8, 43–71. doi:10.1145/355984.355989","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Peters, B., and Herrmann, F. J., 2017, Constraints versus penalties for edge-preserving full-waveform inversion: The Leading Edge, 36, 94–100. doi:10.1190/tle36010094.1","category":"page"},{"location":"tutorials/04_judi_leading_edge_tutorial/","page":"Full-Waveform Inversion - Part 3: optimization","title":"Full-Waveform Inversion - Part 3: optimization","text":"Warner, M., and Guasch, L., 2014, Adaptive waveform inversion: Theory: In SEG technical program expanded abstracts 2014 (pp. 1089–1093). doi:10.1190/segam2014-0371.1","category":"page"},{"location":"pysource/#pysource-package","page":"Devito backend reference","title":"pysource package","text":"","category":"section"},{"location":"pysource/#Submodules","page":"Devito backend reference","title":"Submodules","text":"","category":"section"},{"location":"pysource/#FD_utils-module","page":"Devito backend reference","title":"FD_utils module","text":"","category":"section"},{"location":"pysource/#FD*utils.R*mat(model)","page":"Devito backend reference","title":"FDutils.Rmat(model)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Rotation matrix according to tilt and asymut.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters: model (Model) – Model structure","category":"page"},{"location":"pysource/#FD*utils.divs(func,-so*fact1,-side-1)","page":"Devito backend reference","title":"FDutils.divs(func, sofact=1, side=-1)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"GrDivergenceadient shifted by half a grid point, only to be used in combination with grads.","category":"page"},{"location":"pysource/#FD*utils.grads(func,-so*fact1,-side1)","page":"Devito backend reference","title":"FDutils.grads(func, sofact=1, side=1)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Gradient shifted by half a grid point, only to be used in combination with divs.","category":"page"},{"location":"pysource/#FD_utils.laplacian(v,-irho)","page":"Devito backend reference","title":"FD_utils.laplacian(v, irho)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Laplacian with density div( 1/rho grad) (u)","category":"page"},{"location":"pysource/#FD*utils.sa*tti(u,-v,-model)","page":"Devito backend reference","title":"FDutils.satti(u, v, model)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Tensor factorized SSA TTI wave equation spatial derivatives.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction) – first TTI field\nv (TimeFunction) – second TTI field\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#FD*utils.thomsen*mat(model)","page":"Devito backend reference","title":"FDutils.thomsenmat(model)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Diagonal Matrices with Thomsen parameters for vectorial temporaries computation.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters: model (Model) – Model structure","category":"page"},{"location":"pysource/#checkpoint-module","page":"Devito backend reference","title":"checkpoint module","text":"","category":"section"},{"location":"pysource/#*class*-checkpoint.CheckpointOperator(op,-**kwargs)","page":"Devito backend reference","title":"class checkpoint.CheckpointOperator(op, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Devito’s concrete implementation of the ABC pyrevolve.Operator. This class wraps devito.Operator so it conforms to the pyRevolve API. pyRevolve will call apply with arguments tstart and tend. Devito calls these arguments ts and te so the following dict is used to perform the translations between different names.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nop (Operator) – devito.Operator object that this object will wrap.\nargs (dict) – If devito.Operator.apply() expects any arguments, they can be provided here to be cached. Any calls to CheckpointOperator.apply() will automatically include these cached arguments in the call to the underlying devito.Operator.apply().","category":"page"},{"location":"pysource/#apply(t*start,-t*end)","page":"Devito backend reference","title":"apply(tstart, tend)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"If the devito operator requires some extra arguments in the call to apply they can be stored in the args property of this object so pyRevolve calls pyRevolve.Operator.apply() without caring about these extra arguments while this method passes them on correctly to devito.Operator","category":"page"},{"location":"pysource/#t*arg*names-*-{'t*end':-'time*M',-'t*start':-'time*m'}*","page":"Devito backend reference","title":"targnames = {'tend': 'timeM', 'tstart': 'timem'}","text":"","category":"section"},{"location":"pysource/#*class*-checkpoint.DevitoCheckpoint(objects)","page":"Devito backend reference","title":"class checkpoint.DevitoCheckpoint(objects)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Devito’s concrete implementation of the Checkpoint abstract base class provided by pyRevolve. Holds a list of symbol objects that hold data.","category":"page"},{"location":"pysource/#*property*-dtype","page":"Devito backend reference","title":"property dtype","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"data type","category":"page"},{"location":"pysource/#get_data(timestep)","page":"Devito backend reference","title":"get_data(timestep)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"returns the data (wavefield) for the time-step timestep","category":"page"},{"location":"pysource/#get*data*location(timestep)","page":"Devito backend reference","title":"getdatalocation(timestep)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"returns the data (wavefield) for the time-step timestep","category":"page"},{"location":"pysource/#load()","page":"Devito backend reference","title":"load()","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"NotImplementedError","category":"page"},{"location":"pysource/#save()","page":"Devito backend reference","title":"save()","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"NotImplementedError","category":"page"},{"location":"pysource/#*property*-size","page":"Devito backend reference","title":"property size","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"The memory consumption of the data contained in a checkpoint.","category":"page"},{"location":"pysource/#checkpoint.get*symbol*data(symbol,-timestep)","page":"Devito backend reference","title":"checkpoint.getsymboldata(symbol, timestep)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Return the symbol corresponding to the data at time-step timestep","category":"page"},{"location":"pysource/#geom_utils-module","page":"Devito backend reference","title":"geom_utils module","text":"","category":"section"},{"location":"pysource/#geom*utils.geom*expr(model,-u,-src*coordsNone,-rec*coordsNone,-waveletNone,-fwTrue,-ntNone)","page":"Devito backend reference","title":"geomutils.geomexpr(model, u, srccoords=None, reccoords=None, wavelet=None, fw=True, nt=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Generates the source injection and receiver interpolation. This function is fully abstracted and does not care whether this is a forward or adjoint wave-equation. The source is the source term of the equation The receiver is the measurment term","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Therefore, for the adjoint, this function has to be called as: srcrec(model, v, srccoords=rec_coords, …) because the data is the sources","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or tuple) – Wavefield to inject into and read from\nsrc_coords (Array) – Physical coordinates of the sources\nrec_coords (Array) – Physical coordinates of the receivers\nwavelet (Array) – Data for the source\nfw=True – Whether the direction is forward or backward in time\nnt (int) – Number of time steps","category":"page"},{"location":"pysource/#geom*utils.src*rec(model,-u,-src*coordsNone,-rec*coordsNone,-waveletNone,-ntNone)","page":"Devito backend reference","title":"geomutils.srcrec(model, u, srccoords=None, reccoords=None, wavelet=None, nt=None)","text":"","category":"section"},{"location":"pysource/#interface-module","page":"Devito backend reference","title":"interface module","text":"","category":"section"},{"location":"pysource/#interface.J*adjoint(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-is*residualFalse,-checkpointingFalse,-n*checkpointsNone,-t*sub1,-return*objFalse,-freq*list[],-dft*subNone,-ic'as',-illumFalse,-wsNone,-f00.015,-born_fwdFalse,-nlindFalse,-misfitNone,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjoint(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, checkpointing=False, ncheckpoints=None, tsub=1, returnobj=False, freqlist=[], dftsub=None, ic='as', illum=False, ws=None, f0=0.015, born_fwd=False, nlind=False, misfit=None, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Supports three modes: * Checkpinting * Frequency compression (on-the-fly DFT) * Standard zero lag cross correlation over time","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\ncheckpointing (Bool) – Whether or not to use checkpointing\nn_checkpoints (Int) – Number of checkpoints for checkpointing\nmaxmem (Float) – Maximum memory to use for checkpointing\nfreq_list (List) – List of frequencies for on-the-fly DFT\ndft_sub (Int) – Subsampling factor for on-the-fly DFT\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.J*adjoint*checkpointing(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-is*residualFalse,-n*checkpointsNone,-born*fwdFalse,-return_objFalse,-ic'as',-wsNone,-nlindFalse,-f00.015,-misfitNone,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjointcheckpointing(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, ncheckpoints=None, bornfwd=False, return_obj=False, ic='as', ws=None, nlind=False, f0=0.015, misfit=None, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Checkpointing.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\ncheckpointing (Bool) – Whether or not to use checkpointing\nn_checkpoints (Int) – Number of checkpoints for checkpointing\nmaxmem (Float) – Maximum memory to use for checkpointing\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nis_residual (Bool) – Whether to treat the input as the residual or as the observed data\nborn_fwd (Bool) – Whether to use the forward or linearized forward modeling operator\nnlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.J*adjoint*freq(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-freq*list[],-is*residualFalse,-return*objFalse,-nlindFalse,-dft*subNone,-ic'as',-wsNone,-born*fwdFalse,-f00.015,-misfitNone,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjointfreq(model, srccoords, wavelet, reccoords, recin, spaceorder=8, freqlist=[], isresidual=False, returnobj=False, nlind=False, dftsub=None, ic='as', ws=None, bornfwd=False, f0=0.015, misfit=None, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with Frequency compression (on-the-fly DFT).","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nfreq_list (List) – List of frequencies for on-the-fly DFT\ndft_sub (Int) – Subsampling factor for on-the-fly DFT\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nis_residual (Bool) – Whether to treat the input as the residual or as the observed data\nborn_fwd (Bool) – Whether to use the forward or linearized forward modeling operator\nnlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.J*adjoint*standard(model,-src*coords,-wavelet,-rec*coords,-recin,-space*order8,-is*residualFalse,-return*objFalse,-born*fwdFalse,-illumFalse,-ic'as',-wsNone,-t_sub1,-nlindFalse,-f00.015,-misfitNone,-fwTrue)","page":"Devito backend reference","title":"interface.Jadjointstandard(model, srccoords, wavelet, reccoords, recin, spaceorder=8, isresidual=False, returnobj=False, bornfwd=False, illum=False, ic='as', ws=None, t_sub=1, nlind=False, f0=0.015, misfit=None, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Adjoint Jacobian (adjoint fo born modeling operator) operator on a shot record as a source (i.e data residual). Outputs the gradient with standard zero lag cross correlation over time.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nrecin (Array) – Receiver data\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nws (Array) – Extended source spatial distribution\nis_residual (Bool) – Whether to treat the input as the residual or as the observed data\nborn_fwd (Bool) – Whether to use the forward or linearized forward modeling operator\nnlind (Bool) – Whether to remove the non linear data from the input data. This option is only available in combination with born_fwd\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation as base propagator\nReturns: Adjoint jacobian on the input data (gradient)\nReturn type: Array","category":"page"},{"location":"pysource/#interface.adjoint*w(model,-rec*coords,-data,-wavelet,-space_order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.adjointw(model, reccoords, data, wavelet, space_order=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Adjoint/backward modeling of a shot record (receivers as source) for an extended source setup Pw*F^T*Pr^T*d_obs.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nrec_coords (Array) – Coordiantes of the receiver(s)\ndata (Array) – Shot gather\nwavelet (Array) – Time signature of the forward source for stacking along time\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: spatial distribution\nReturn type: Array","category":"page"},{"location":"pysource/#interface.born*rec(model,-src*coords,-wavelet,-rec*coords,-space*order8,-ic'as',-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.bornrec(model, srccoords, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Linearized (Born) modeling of a point source for a model perturbation (square slowness) dm.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.born*rec*w(model,-weight,-wavelet,-rec*coords,-space*order8,-ic'as',-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.bornrecw(model, weight, wavelet, reccoords, spaceorder=8, ic='as', f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Linearized (Born) modeling of an extended source for a model perturbation (square slowness) dm with an extended source","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nweight (Array) – Spatial distriubtion of the extended source\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nic (String) – Imaging conditions (“as”, “isic” or “fwi”), defaults to “as”\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*no*rec(model,-src*coords,-wavelet,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardnorec(model, srccoords, wavelet, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of a point source without receiver.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Wavefield\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*rec(model,-src*coords,-wavelet,-rec*coords,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardrec(model, srccoords, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Modeling of a point source with receivers Pr*F*Ps^T*q.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nsrc_coords (Array) – Coordiantes of the source(s)\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*rec*w(model,-weight,-wavelet,-rec*coords,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardrecw(model, weight, wavelet, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of an extended source with receivers Pr*F*Pw^T*w","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nweights (Array) – Spatial distribution of the extended source.\nwavelet (Array) – Source signature\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*wf*src(model,-u,-rec*coords,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardwfsrc(model, u, reccoords, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of a full wavefield source Pr*F*u.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or Array) – Time-space dependent wavefield\nrec_coords (Array) – Coordiantes of the receiver(s)\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Shot record\nReturn type: Array","category":"page"},{"location":"pysource/#interface.forward*wf*src*norec(model,-u,-space*order8,-f00.015,-illumFalse,-fwTrue)","page":"Devito backend reference","title":"interface.forwardwfsrcnorec(model, u, spaceorder=8, f0=0.015, illum=False, fw=True)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Forward modeling of a full wavefield source without receiver F*u.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or Array) – Time-space dependent wavefield\nspace_order (Int (**optional )) – Spatial discretization order, defaults to 8\nf0 (float) – peak frequency\nillum (bool) – Whether to compute illumination during propagation\nfw (bool) – Whether it is forward or adjoint propagation\nReturns: Wavefield\nReturn type: Array","category":"page"},{"location":"pysource/#interface.wri*func(model,-src*coords,-wavelet,-rec*coords,-recin,-yin,-space*order8,-ic'as',-wsNone,-t*sub1,-grad'm',-grad*corrFalse,-alpha*opFalse,-w*funNone,-eps0,-freq_list[],-wfiltNone,-f00.015)","page":"Devito backend reference","title":"interface.wrifunc(model, srccoords, wavelet, reccoords, recin, yin, spaceorder=8, ic='as', ws=None, tsub=1, grad='m', gradcorr=False, alphaop=False, wfun=None, eps=0, freq_list=[], wfilt=None, f0=0.015)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Time domain wavefield reconstruction inversion wrapper","category":"page"},{"location":"pysource/#kernels-module","page":"Devito backend reference","title":"kernels module","text":"","category":"section"},{"location":"pysource/#kernels.SLS*2nd*order(model,-p,-fwTrue,-qNone,-f00.015)","page":"Devito backend reference","title":"kernels.SLS2ndorder(model, p, fw=True, q=None, f0=0.015)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Viscoacoustic 2nd SLS wave equation. https://library.seg.org/doi/10.1190/geo2013-0030.1","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Bulk modulus moved to rhs. The adjoint equation is directly derived as the discrete adjoint of the forward PDE which leads to a slightly different formulation than in the paper.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu1 (TimeFunction) – Pressure field\nu2 (TimeFunction) – Attenuation Memory variable\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)\nf0 (Peak frequency) – ","category":"page"},{"location":"pysource/#kernels.acoustic_kernel(model,-u,-fwTrue,-qNone)","page":"Devito backend reference","title":"kernels.acoustic_kernel(model, u, fw=True, q=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Acoustic wave equation time stepper","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or tuple) – wavefield (tuple if TTI)\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source","category":"page"},{"location":"pysource/#kernels.elastic_kernel(model,-v,-tau,-fwTrue,-qNone)","page":"Devito backend reference","title":"kernels.elastic_kernel(model, v, tau, fw=True, q=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Elastic wave equation time stepper","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nv (VectorTimeFunction) – Particle Velocity\ntau (TensorTimeFunction) – Stress tensor\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)","category":"page"},{"location":"pysource/#kernels.tti_kernel(model,-u1,-u2,-fwTrue,-qNone)","page":"Devito backend reference","title":"kernels.tti_kernel(model, u1, u2, fw=True, q=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"TTI wave equation time stepper","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu1 (TimeFunction) – First component (pseudo-P) of the wavefield\nu2 (TimeFunction) – First component (pseudo-P) of the wavefield\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source as a tuple (one value for each component)","category":"page"},{"location":"pysource/#kernels.wave_kernel(model,-u,-fwTrue,-qNone,-f00.015)","page":"Devito backend reference","title":"kernels.wave_kernel(model, u, fw=True, q=None, f0=0.015)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Pde kernel corresponding the the model for the input wavefield","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Physical model\nu (TimeFunction or tuple) – wavefield (tuple if TTI or Viscoacoustic)\nfw (Bool) – Whether forward or backward in time propagation\nq (TimeFunction or Expr) – Full time-space source\nf0 (Peak frequency) – ","category":"page"},{"location":"pysource/#models-module","page":"Devito backend reference","title":"models module","text":"","category":"section"},{"location":"pysource/#*class*-models.Model(origin,-spacing,-shape,-space_order8,-nbl40,-dtypeclass-'numpy.float32',-mNone,-epsilonNone,-deltaNone,-thetaNone,-phiNone,-rhoNone,-bNone,-qpNone,-lamNone,-muNone,-dmNone,-fsFalse,-**kwargs)","page":"Devito backend reference","title":"class models.Model(origin, spacing, shape, space_order=8, nbl=40, dtype=, m=None, epsilon=None, delta=None, theta=None, phi=None, rho=None, b=None, qp=None, lam=None, mu=None, dm=None, fs=False, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"The physical model used in seismic inversion : shape_pml = np.array(shape) + 2 * self.nbl processes.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\norigin (tuple of floats) – Origin of the model in m as a tuple in (x,y,z) order.\nspacing (tuple of floats) – Grid size in m as a Tuple in (x,y,z) order.\nshape (tuple of int) – Number of grid points size in (x,y,z) order.\nspace_order (int) – Order of the spatial stencil discretisation.\nm (array_like or float) – Squared slownes in s^2/km^2\nnbl (int , optional) – The number of absorbin layers for boundary damping.\ndtype (np.float32 or np.float64) – Defaults to 32.\nepsilon (array_like or float , optional) – Thomsen epsilon parameter (0 obj, adjoint_source","category":"page"},{"location":"pysource/#sensitivity.basic_src(model,-u,-**kwargs)","page":"Devito backend reference","title":"sensitivity.basic_src(model, u, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Basic source for linearized modeling","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Model containing the perturbation dm\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)","category":"page"},{"location":"pysource/#sensitivity.crosscorr*freq(u,-v,-model,-freqNone,-dft*subNone,-**kwargs)","page":"Devito backend reference","title":"sensitivity.crosscorrfreq(u, v, model, freq=None, dftsub=None, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Standard cross-correlation imaging condition with on-th-fly-dft","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure\nfreq (Array) – Array of frequencies for on-the-fly DFT\nfactor (int) – Subsampling factor for DFT","category":"page"},{"location":"pysource/#sensitivity.crosscorr_time(u,-v,-model,-**kwargs)","page":"Devito backend reference","title":"sensitivity.crosscorr_time(u, v, model, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Cross correlation of forward and adjoint wavefield","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#sensitivity.func_name(freqNone,-ic'as')","page":"Devito backend reference","title":"sensitivity.func_name(freq=None, ic='as')","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Get key for imaging condition/linearized source function","category":"page"},{"location":"pysource/#sensitivity.fwi_freq(*ar,-**kw)","page":"Devito backend reference","title":"sensitivity.fwi_freq(*ar, **kw)","text":"","category":"section"},{"location":"pysource/#sensitivity.fwi_src(*ar,-**kw)","page":"Devito backend reference","title":"sensitivity.fwi_src(*ar, **kw)","text":"","category":"section"},{"location":"pysource/#sensitivity.fwi_time(*ar,-**kw)","page":"Devito backend reference","title":"sensitivity.fwi_time(*ar, **kw)","text":"","category":"section"},{"location":"pysource/#sensitivity.grad*expr(gradm,-u,-v,-model,-wNone,-freqNone,-dft*subNone,-ic'as')","page":"Devito backend reference","title":"sensitivity.gradexpr(gradm, u, v, model, w=None, freq=None, dftsub=None, ic='as')","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Gradient update stencil","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure\nw (Float or Expr (**optional )) – Weight for the gradient expression (default=1)\nfreq (Array) – Array of frequencies for on-the-fly DFT\nfactor (int) – Subsampling factor for DFT\nisic (Bool) – Whether or not to use inverse scattering imaging condition (not supported yet)","category":"page"},{"location":"pysource/#sensitivity.inner_grad(u,-v)","page":"Devito backend reference","title":"sensitivity.inner_grad(u, v)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Inner product of the gradient of two Function.","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Function) – First wavefield\nv (TimeFunction or Function) – Second wavefield","category":"page"},{"location":"pysource/#sensitivity.isic_freq(u,-v,-model,-**kwargs)","page":"Devito backend reference","title":"sensitivity.isic_freq(u, v, model, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Inverse scattering imaging condition","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#sensitivity.isic_src(model,-u,-**kwargs)","page":"Devito backend reference","title":"sensitivity.isic_src(model, u, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"ISIC source for linearized modeling","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Model containing the perturbation dm\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)","category":"page"},{"location":"pysource/#sensitivity.isic_time(u,-v,-model,-**kwargs)","page":"Devito backend reference","title":"sensitivity.isic_time(u, v, model, **kwargs)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Inverse scattering imaging condition","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nv (TimeFunction or Tuple) – Adjoint wavefield (tuple of fields for TTI)\nmodel (Model) – Model structure","category":"page"},{"location":"pysource/#sensitivity.lin_src(model,-u,-ic'as')","page":"Devito backend reference","title":"sensitivity.lin_src(model, u, ic='as')","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Source for linearized modeling","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nmodel (Model) – Model containing the perturbation dm\nu (TimeFunction or Tuple) – Forward wavefield (tuple of fields for TTI or dft)\nic (String) – Imaging condition of which we compute the linearized source","category":"page"},{"location":"pysource/#sources-module","page":"Devito backend reference","title":"sources module","text":"","category":"section"},{"location":"pysource/#*class*-sources.PointSource(*args)","page":"Devito backend reference","title":"class sources.PointSource(*args)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Symbolic data object for a set of sparse point sources","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nname (String) – Name of the symbol representing this source\ngrid (Grid) – Grid object defining the computational domain.\ncoordinates (Array) – Point coordinates for this source\ndata ( (**Optional ) Data) – values to initialise point data\nntime (Int (**Optional )) – Number of timesteps for which to allocate data\nnpoint (Int (**Optional )) – \nsource (Number of sparse points represented by this) – \ndimension (Dimension (**Optional )) – object for representing the number of points in this source\nNote – \nfully (either the dimensions ntime and npoint or the) – \nprovided. (initialised data array need to be) – ","category":"page"},{"location":"pysource/#default*assumptions-*-{'commutative':-True,-'complex':-True,-'extended*real':-True,-'finite':-True,-'hermitian':-True,-'imaginary':-False,-'infinite':-False,-'real':-True}*","page":"Devito backend reference","title":"defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*","text":"","category":"section"},{"location":"pysource/#is_commutative-*:-bool-None*-*-True*","page":"Devito backend reference","title":"is_commutative : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_complex-*-True*","page":"Devito backend reference","title":"is_complex = True","text":"","category":"section"},{"location":"pysource/#is*extended*real-*:-bool-None*-*-True*","page":"Devito backend reference","title":"isextendedreal : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_finite-*-True*","page":"Devito backend reference","title":"is_finite = True","text":"","category":"section"},{"location":"pysource/#is_hermitian-*-True*","page":"Devito backend reference","title":"is_hermitian = True","text":"","category":"section"},{"location":"pysource/#is_imaginary-*-False*","page":"Devito backend reference","title":"is_imaginary = False","text":"","category":"section"},{"location":"pysource/#is_infinite-*-False*","page":"Devito backend reference","title":"is_infinite = False","text":"","category":"section"},{"location":"pysource/#is_real-*:-bool-None*-*-True*","page":"Devito backend reference","title":"is_real : bool | None = True","text":"","category":"section"},{"location":"pysource/#sources.Receiver","page":"Devito backend reference","title":"sources.Receiver","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"alias of PointSource","category":"page"},{"location":"pysource/#*class*-sources.RickerSource(*args)","page":"Devito backend reference","title":"class sources.RickerSource(*args)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Symbolic object that encapsulate a set of sources with a pre-defined Ricker wavelet: http://subsurfwiki.org/wiki/Ricker_wavelet name: Name for the resulting symbol grid: Grid object defining the computational domain. f0: Peak frequency for Ricker wavelet in kHz time: Discretized values of time in ms","category":"page"},{"location":"pysource/#default*assumptions-*-{'commutative':-True,-'complex':-True,-'extended*real':-True,-'finite':-True,-'hermitian':-True,-'imaginary':-False,-'infinite':-False,-'real':-True}*-2","page":"Devito backend reference","title":"defaultassumptions *= {'commutative': True, 'complex': True, 'extendedreal': True, 'finite': True, 'hermitian': True, 'imaginary': False, 'infinite': False, 'real': True}*","text":"","category":"section"},{"location":"pysource/#is_commutative-*:-bool-None*-*-True*-2","page":"Devito backend reference","title":"is_commutative : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_complex-*-True*-2","page":"Devito backend reference","title":"is_complex = True","text":"","category":"section"},{"location":"pysource/#is*extended*real-*:-bool-None*-*-True*-2","page":"Devito backend reference","title":"isextendedreal : bool | None = True","text":"","category":"section"},{"location":"pysource/#is_finite-*-True*-2","page":"Devito backend reference","title":"is_finite = True","text":"","category":"section"},{"location":"pysource/#is_hermitian-*-True*-2","page":"Devito backend reference","title":"is_hermitian = True","text":"","category":"section"},{"location":"pysource/#is_imaginary-*-False*-2","page":"Devito backend reference","title":"is_imaginary = False","text":"","category":"section"},{"location":"pysource/#is_infinite-*-False*-2","page":"Devito backend reference","title":"is_infinite = False","text":"","category":"section"},{"location":"pysource/#is_real-*:-bool-None*-*-True*-2","page":"Devito backend reference","title":"is_real : bool | None = True","text":"","category":"section"},{"location":"pysource/#wavelet(timev)","page":"Devito backend reference","title":"wavelet(timev)","text":"","category":"section"},{"location":"pysource/#*class*-sources.TimeAxis(startNone,-stepNone,-numNone,-stopNone)","page":"Devito backend reference","title":"class sources.TimeAxis(start=None, step=None, num=None, stop=None)","text":"","category":"section"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Data object to store the TimeAxis. Exactly three of the four key arguments must be prescribed. Because of remainder values it is not possible to create a TimeAxis that exactly adhears to the inputs therefore start, stop, step and num values should be taken from the TimeAxis object rather than relying upon the input values. The four possible cases are: * start is None: start = step*(1 - num) + stop * step is None: step = (stop - start)/(num - 1) * num is None: num = ceil((stop - start + step)/step) and because of remainder stop = step*(num - 1) + start * stop is None: stop = step*(num - 1) + start","category":"page"},{"location":"pysource/","page":"Devito backend reference","title":"Devito backend reference","text":"Parameters:\nstart (float , optional) – Start of time axis.\nstep (float , optional) – Time interval.\nnum (int , optional) – Number of values (Note: this is the number of intervals + 1). Stop value is reset to correct for remainder.\nstop (float , optional) – End time.","category":"page"},{"location":"pysource/#time_values","page":"Devito backend reference","title":"time_values","text":"","category":"section"},{"location":"pysource/#wave_utils-module","page":"Devito backend reference","title":"wave_utils module","text":"","category":"section"},{"location":"pysource/#Module-contents","page":"Devito backend reference","title":"Module contents","text":"","category":"section"},{"location":"linear_operators/#Linear-Operators","page":"Linear Operators","title":"Linear Operators","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"JUDI is building on JOLI.jl to implement matrix-free linear operators. These operators represent the discretized wave-equations and sensitivit (Jacobian) for different acquisition schemes. ","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Pages = [\"linear_operators.md\"]","category":"page"},{"location":"linear_operators/#judiModeling","page":"Linear Operators","title":"judiModeling","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Seismic modeling operator for solving a wave equation for a given right-hand-side.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiModeling{DDT, RDT}","category":"page"},{"location":"linear_operators/#JUDI.judiModeling","page":"Linear Operators","title":"JUDI.judiModeling","text":"judiModeling(model; options=Options())\njudiModeling(model, src_geometry, rec_geometry; options=Options())\n\nCreate seismic modeling operator for a velocity model given as a Model structure. The function also takes the source and receiver geometries as additional input arguments, which creates a combined operator judiProjection*judiModeling*judiProjection'.\n\nExample\n\nPr and Ps are projection operatos of type judiProjection and q is a data vector of type judiVector: F = judiModeling(model) dobs = PrFPs'q F = judiModeling(model, q.geometry, rec_geometry) dobs = Fq\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construction:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator without source/receiver projections:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = judiModeling(model; options=opt)","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator with source/receiver projections:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = judiModeling(model, src_geometry, rec_geometry)","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator from an existing operator without geometries and projection operators:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = Pr*F*Ps'","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Ps and Pr are source/receiver projection operators of type judiProjection.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construct a modeling operator for extended source modeling:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"F = Pr*F*Pw'","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Pw is a judiLRWF (low-rank-wavefield) projection operator.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Model structure\nF.model\n\n# Source injection (if available) and geometry\nF.qInjection\nF.qInjection.geometry\n\n# Receiver interpolation (if available) and geometry\nF.rInterpolation\nF.rInterpolation.geometry\n\n# Options structure\nF.options","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Forward modeling (F w/ geometries)\nd_obs = F*q\n\n# Adjoint modeling (F w/ geometries)\nq_ad = F'*d_obs\n\n# Forward modeling (F w/o geometries)\nd_obs = Pr*F*Ps'*q\n\n# Adjoint modelng (F w/o geometries)\nq_ad = Ps*F'*Pr'*d_obs\n\n# Extended source modeling (F w/o geometries)\nd_obs = Pr*F*Pw'*w\n\n# Adjoint extended source modeling (F w/o geometries)\nw_ad = Pw*F'*Pr'*d_obs\n\n# Forward modeling and return full wavefield (F w/o geometries)\nu = F*Ps'*q\n\n# Adjoint modelnig and return wavefield (F w/o geometries)\nv = F'*Pr'*d_obs\n\n# Forward modeling with full wavefield as source (F w/o geometries)\nd_obs = Pr*F*u\n\n# Adjoint modeling with full wavefield as source (F w/o geometries)\nq_ad = Ps*F*v","category":"page"},{"location":"linear_operators/#judiJacobian","page":"Linear Operators","title":"judiJacobian","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Jacobian of a non-linear forward modeling operator. Corresponds to linearized Born modeling (forward mode) and reverse-time migration (adjoint mode).","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiJacobian","category":"page"},{"location":"linear_operators/#JUDI.judiJacobian","page":"Linear Operators","title":"JUDI.judiJacobian","text":"judiJacobian(F,q)\n\nCreate a linearized modeling operator from the non-linear modeling operator F and the source q. q can be either a judiVector (point source Jacobian) or a judiWeight for extended source modeling. F is a full modeling operator including source/receiver projections.\n\nExamples\n\nF is a modeling operator without source/receiver projections: J = judiJacobian(PrFPs',q)\nF is the combined operator Pr*F*Ps': J = judiJacobian(F,q)\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Construction:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"A judiJacobian operator can be create from an exisiting forward modeling operator and a source vector:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"J = judiJacobian(F, q) # F w/ geometries","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"J = judiJacobian(Pr*F*Ps', q) # F w/o geometries","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Ps and Pr are source/receiver projection operators of type judiProjection.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"A Jacobian can also be created for an extended source modeling operator:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"J = judiJacobian(Pr*F*Pw', w)","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"where Pw is a judiLRWF operator and w is a judiWeights vector (or 2D/3D Julia array).","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields::","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Model structure\nJ.model\n\n# Underlying propagator\nJ.F\n\n# Source injection (if available) and geometry throughpropagator\nJ.F.qInjection\nJ.F.qInjection.geometry\n\n# Receiver interpolation (if available) and geometry through propagator\nJ.F.rInterpolation\nJ.F.rInterpolation.geometry\n\n# Source term, can be a judiWeights, judiWavefield, or a judiVector\nJ.q\n\n# Options structure\nJ.options","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Linearized modeilng\nd_lin = J*dm\n\n# RTM\nrtm = J'*d_lin\n\n# Matrix-free normal operator\nH = J'*J","category":"page"},{"location":"linear_operators/#judiProjection","page":"Linear Operators","title":"judiProjection","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Abstract linear operator for source/receiver projections. A (transposed) judiProjection operator symbolically injects the data with which it is multiplied during modeling. If multiplied with a forward modeling operator, it samples the wavefield at the specified source/receiver locations.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiProjection","category":"page"},{"location":"linear_operators/#JUDI.judiProjection","page":"Linear Operators","title":"JUDI.judiProjection","text":"judiProjection(geometry)\n\nProjection operator for sources/receivers to restrict or inject data at specified locations.\n\nExamples\n\nF is a modeling operator of type judiModeling and q is a seismic source of type judiVector: Pr = judiProjection(rec_geometry) Ps = judiProjection(q.geometry) dobs = PrFPs'q qad = PsF'Pr'dobs\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Source/receiver geometry\nP.geometry","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Multiply with judiVector to create a judiRHS\nrhs1 = Pr'*d_obs\nrhs2 = Ps'*q\n\n# Sample wavefield at source/receiver location during modeling\nd_obs = Pr*F*Ps'*q\nq_ad = Ps*F*Pr'*d_obs","category":"page"},{"location":"linear_operators/#judiLRWF","page":"Linear Operators","title":"judiLRWF","text":"","category":"section"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Abstract linear operator for sampling a seismic wavefield as a sum over all time steps, weighted by a time-varying wavelet. Its transpose injects a time-varying wavelet at every grid point in the model.","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"judiWavelet","category":"page"},{"location":"linear_operators/#JUDI.judiWavelet","page":"Linear Operators","title":"JUDI.judiWavelet","text":"judiWavelet(dt, wavelet)\n\nLow-rank wavefield operator which injects a wavelet q at every point of the subsurface.\n\nExamples\n\nF is a modeling operator of type judiModeling and w is a weighting matrix of type judiWeights: Pr = judiProjection(recgeometry) Pw = judiWavelet(recgeometry.dt, q.data) # or judiLRWF(rec_geometry.dt, q.data) dobs = PrFPw'w dw = PwF'Pr'dobs\n\n\n\n\n\n","category":"type"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Accessible fields:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Wavelet of i-th source location\nP.wavelet[i]","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"Usage:","category":"page"},{"location":"linear_operators/","page":"Linear Operators","title":"Linear Operators","text":"# Multiply with a judiWeight vector to create a judiExtendedSource\nex_src = Pw'*w\n\n# Sample wavefield as a sum over time, weighted by the source\nu_ex = Pw*F'*Pr'*d_obs","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Automatic-differentiation-with-JUDI","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"In this tutorial, we will look at the automatic differentiation in julia, and in particular how ChainRules.jl supports addition of your own differentiation rules into Julia's Automatic Differentiation system. This allows for seamless integration between your code, including its handcoded derivatives, and Julia's native AD systems, e.g. those used by Flux Julia's machine learning platform. We use ChainRules.jl to automatically differentiate codes that involve complex operations implemented by JUDI.jl (this example) and JOLI.jl.","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Introduction-to-chain-rules","page":"Automatic differentiation with JUDI","title":"Introduction to chain rules","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We first provide a brief introduction to automatic differentiation and to rrule, the ChainRules.jl interface used to define custom AD rules. With this rules defined, we show how we can use generic AD frameworks (Zygote/Flux in this tutorial) to compute gradients of complicated expression swaping in our own rule for part of the expression.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using Flux, ChainRulesCore, LinearAlgebra, JOLI","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Simple-example","page":"Automatic differentiation with JUDI","title":"Simple example","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Let's consider a simple example with a basic differentiable function x - cos(x) + 1","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"mycos(x) = cos(x) + 1","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"mycos (generic function with 1 method)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Now we know that the derivative of this function is x - -sin(x) from standard functional analysis.e can therefore define through chainrules a new rule for our function f","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"function ChainRulesCore.rrule(::typeof(mycos), x)\n println(\"Using custom AD rule for mycos\")\n y = mycos(x)\n pullback(Δy) = (NoTangent(), -sin(x)*Δy)\n return y, pullback\nend","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We now have the rule to compute the directional derivative of our function mycos . Let's check the gradient","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"x0 = randn()\n# Standard AD of cos\ng1 = gradient(x->norm(cos(x)+1)^2, x0);\n# Our definition\ng2 = gradient(x->norm(mycos(x))^2, x0);\n# Analytical gradient\ng3 = -2*sin(x0)*(mycos(x0));\nprintln(\"True gradient: $(g3) \\nStandard AD : $(g1[1]) \\nCustom AD : $(g2[1])\")","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Using custom AD rule for mycos\nTrue gradient: -1.1650215811256202 \nStandard AD : -1.1650215811256202 \nCustom AD : -1.1650215811256202","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"And we see that we get the correct gradient. Now this is an extremely simple case, now let's look at a more complicated case where we define the AD rule for matrix-free operators defined in JOLI.","category":"page"},{"location":"tutorials/06_automatic_differentiation/#JOLI","page":"Automatic differentiation with JUDI","title":"JOLI","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We look at how we define automatic differentiation rules involding matrix-free linear operator. We consider operations of the form A*x where A is a JOLI matrix-free linear operator and we differentiate with respect to the input x","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"In JOLI, the base type of our linear operator is joAbstractLinearOperator. If we define the rule for this abstract type, all linear operator should follow. Now in this case the acual operation to be differentiated is the multiplication * with two inputs (A, x). Because we consider A to be fixed,its derivative will be defined as NoTangent that is ChainRules's way to state there is no derivative for this input.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"
      NOTE These rules are implemented inside JOLI and are merely implemented here as an illustration. JOLI operators are usable with FLux/Zygote by default and with any Julia ML framework implemented AD through ChainRules.jl
      ","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using JOLI","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"function ChainRulesCore.rrule(::typeof(*), A::T, x) where {T<:joAbstractLinearOperator}\n y = A*x\n pullback(Δy) = (NoTangent(), NoTangent(), A'*Δy)\n return y, pullback\nend","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"With this rule defined we can now use a JOLI operator. Let's solve a simple data fitting problem with a restricted Fourier measuerment","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using Random\nN = 128\n# Fourier transform as a linear operator\nF = joDFT(N)\n# Restriction\nR = joRomberg(N; DDT=Complex{Float64}, RDT=Complex{Float64})\n# Combine the operators\nA = R*F;","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Make data\nx = randn(128)\nb = A*x;","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Let's create a loss function","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"loss(x) = .5f0*norm(A*x - b)^2 + .5f0*norm(x, 2)^2","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"loss (generic function with 1 method)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We can now easily obtain the gradient at any given x since the only undefined part would have been the JOLI operator that now has its own differentiation rule","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"x0 = randn(128)\ng_ad = gradient(loss, x0)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"([3.202332642059349, 3.3474396077773836, 1.7192577704407273, 1.091738893925904, 2.99497941149576, 0.033555000810577607, 2.1856165385946493, -1.9623327339375567, 1.6305386659659917, 0.49094575011054564 … 1.498782430471393, 3.979368223594536, 2.37224960018878, -1.7065306486883383, 0.2043370108057428, -1.1826425144324078, 1.76641366139114, -3.0095898127121723, 3.064337151574949, 5.76008767012525],)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Once again, we can compare to the know analytical gradient","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"g_hand = A'*(A*x0 - b) + x0;\nerr = norm(g_hand - g_ad[1])","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"6.845562607034591e-15","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"And we get the exact gradient without the AD system needing to know what A computes but using the prededined rule for A*x","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Optimization","page":"Automatic differentiation with JUDI","title":"Optimization","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Let's now slve the problem above with standard gradient descent","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using Optim","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"δloss!(g, x) = begin g.=gradient(loss, x)[1]; return loss(x) end;","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"summary = optimize(loss, δloss!, randn(N), ConjugateGradient(),\n Optim.Options(g_tol = 1e-12, iterations = 200, store_trace = true, show_trace = true, show_every=1))","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Iter Function value Gradient norm \n 0 2.078016e+02 7.110069e+00\n * time: 0.008176088333129883\n 1 2.996845e+01 3.191891e-15\n * time: 0.32667112350463867\n\n\n\n\n\n * Status: success\n\n * Candidate solution\n Final objective value: 2.996845e+01\n\n * Found with\n Algorithm: Conjugate Gradient\n\n * Convergence measures\n |x - x'| = 3.56e+00 ≰ 0.0e+00\n |x - x'|/|x'| = 2.52e+00 ≰ 0.0e+00\n |f(x) - f(x')| = 1.78e+02 ≰ 0.0e+00\n |f(x) - f(x')|/|f(x')| = 5.93e+00 ≰ 0.0e+00\n |g(x)| = 3.19e-15 ≤ 1.0e-12\n\n * Work counters\n Seconds run: 0 (vs limit Inf)\n Iterations: 1\n f(x) calls: 3\n ∇f(x) calls: 2","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using PyPlot\nplot(x, label=\"true\")\nplot(summary.minimizer, label=\"Recovered\")\nlegend()","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"PyObject ","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Automatic-differentiation-for-JUDI","page":"Automatic differentiation with JUDI","title":"Automatic differentiation for JUDI","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"With this introductory example, we have seen how to define simple derivative reverse rules. However, seismic inversion tend to rely and much more complicated operator such as the discrete wave-equation and its non-linear dependence to the velocity. While implementing a pure native-julia propagator using simple artithmetic operations easy to differentiate would be possible, this would limit the control user have on crtitical pieces such as the imaging condition and the memory management for the forward wavefield. Consequently, most seismic inversion framework a very carefully implemented but do not necessarly allow for plug-and-play with external framework. This incompatibility makes the integration of modern machine learning algorithms extremely complciated, if feasible at all, with these legacy software.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"In JUDI, we made design choice from the beginning of high level abstractions and separation of concern that allow easy extension. In the following, we will demonstrate how JUDI can be integrated with machine learning algorithm trivially thanks to the definition of the core rules for adjoint state problem. More specifically, JUDI implements the rule for the following derivatives:","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"fracd mathbfF * mathbfqd mathbfq\nwhere mathbfF = mathbfP_r mathbfA^s mathbfP_s^T is a forward (s=-1) or adjoint (s=-*) propagator. JUDI supports numerous cases including full wavefield modelling (mathbfP_s=mathbfP_r=mathcalI, stanrad point source and point receivers, and extendend source modeling. \nfracd mathbfF * mathbfqd mathbfm\nwhere mathbfF is a forward (s=-1) or adjoint (s=-*) propagator. This effectively allow for FWI with any chosen misfit function rho_mathbfm(mathbfF * mathbfq mathbfq)\nfracd mathbfJ * mathbfdmd mathbfdm\nwhere mathbfJ is the standard FWI/RTM jacobian of the forward operator mathbfF\nfracd mathbfJ(mathbfq) * mathbfdmd mathbfq\nwhere once again mathbfJ is the standard FWI/RTM jacobian of the forward operator and mathbfq is the source of the forward modeling operator","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"With all these derivatives predefine, we can easily let the implementation of the propagators and Jacobian handle high performance kernels (via Devito), advanced imaging condition and efficient memory mamangement. From these rules, the AD framework will only call the propagation kernels implemented in JUDI and integrate it as part of the chain of differentiation. ","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"We now illustrate these capabilities on a few trivial example that show the flexibiluty of our inversion framework.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using JUDI, Flux","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"using SlimPlotting\n# Set up model structure\nn = (120, 100) # (x,y,z) or (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32,n) .+ 0.4f0\nv0 = ones(Float32,n) .+ 0.4f0\nv[:, Int(round(end/2)):end] .= 4f0\n\n# Slowness squared [s^2/km^2]\nm = (1f0 ./ v).^2\nm0 = (1f0 ./ v0).^2\ndm = vec(m - m0);# Lets get some simple default parameter","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"plot_velocity(v', d; cbar=true)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"(Image: png)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Setup model structure\nnsrc = 1\t# number of sources\nmodel0 = Model(n, d, o, m0)\n\n# Set up receiver geometry\nnxrec = 120\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = 0f0\nzrec = range(50f0, stop=50f0, length=nxrec)\n\n# receiver sampling and recording time\ntime = 1000f0 # receiver recording time [ms]\ndt = 1f0 # receiver sampling interval [ms]\n\n# Set up receiver structure\nrecGeometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)\n\n## Set up source geometry (cell array with source locations for each shot)\nxsrc = convertToCell([600f0])\nysrc = convertToCell([0f0])\nzsrc = convertToCell([20f0])\n\n# Set up source structure\nsrcGeometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"GeometryIC{Float32} wiht 1 sources","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"\n# setup wavelet\nf0 = 0.01f0 # MHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(srcGeometry, wavelet)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 1 sources","category":"page"},{"location":"tutorials/06_automatic_differentiation/#Return-type","page":"Automatic differentiation with JUDI","title":"Return type","text":"","category":"section"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Whule JUDI defines its own dimensional types, it is recommended to drop the metadata and return pure array/tensors for ML. This can be done with a simple option passed to the propagators","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"opt = Options(return_array=true)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"JUDIOptions(8, false, false, 1000.0f0, false, \"\", \"shot\", false, false, Any[], \"as\", 1, 1, true, nothing, 0.015f0)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"F0 = judiModeling(model0, srcGeometry, recGeometry; options=opt)\nnum_samples = recGeometry.nt[1] * nxrec # Number of value","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"120120","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"##################################################################################\n# Fully connected neural network with linearized modeling operator\nn_in = 100\nn_out = 10\n\nW1 = randn(Float32, prod(model0.n), n_in)\nb1 = randn(Float32, prod(model0.n))\n\nW2 = judiJacobian(F0, q)\nb2 = randn(Float32, num_samples)\n\nW3 = randn(Float32, n_out, num_samples)\nb3 = randn(Float32, n_out);\n","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.n, use size(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"function network(x)\n x = W1*x .+ b1\n x = vec(W2*x) .+ b2\n x = W3*x .+ b3\n return x\nend","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"network (generic function with 1 method)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Inputs and target\nx = zeros(Float32, n_in)\ny = randn(Float32, n_out);","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Evaluate MSE loss\nloss(x, y) = Flux.mse(network(x), y)\n","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"loss (generic function with 2 methods)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Compute gradient w.r.t. x and y\nΔx, Δy = gradient(loss, x, y)\n","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Building born operator\nOperator `born` ran in 0.04 s\nBuilding forward operator\nOperator `forward` ran in 0.03 s\nBuilding adjoint born operator\nOperator `gradient` ran in 0.03 s\nOperator `forward` ran in 0.26 s\nOperator `gradient` ran in 0.03 s\n\n\n\n\n\n(Float32[537865.2, -881569.94, 479238.75, 193671.75, 785871.56, 170005.88, -387432.75, -84344.25, -662277.6, 475783.38 … 910177.75, 988473.4, 249698.53, -737089.44, 211155.11, -397048.3, 421428.7, 94724.03, -94981.22, -508586.4], Float32[102.665306, 173.1151, -374.61276, -112.29274, -36.785393, 124.619865, -138.68846, -12.379652, 76.64807, -25.363287])","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"And we can see that the underlying JUDI propagators were called propetly.","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"# Compute gradient for x, y and weights (except for W2)\np = Flux.params(x, y, W1, b1, b2, W3, b3)\ngs = gradient(() -> loss(x, y), p)","category":"page"},{"location":"tutorials/06_automatic_differentiation/","page":"Automatic differentiation with JUDI","title":"Automatic differentiation with JUDI","text":"Operator `born` ran in 0.28 s\nOperator `forward` ran in 0.26 s\nOperator `gradient` ran in 0.03 s\nOperator `forward` ran in 0.04 s\nOperator `gradient` ran in 0.22 s\n\n\n\n\n\nGrads(...)","category":"page"},{"location":"basics/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"These tutorials provide instructions of how to set up various modeling or inversion scenarios with JUDI. For a list of runnable Julia scripts and reproducable research, please also check out the examples:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The examples scripts contain simple modeling and inversion examples such as FWI, LSRTM, and medical modeling.\nThe machine-learning scripts contain examples of machine learning using Flux.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Pages = [\"tutorials.md\"]","category":"page"},{"location":"basics/#D-Modeling-Quickstart","page":"Getting Started","title":"2D Modeling Quickstart","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To set up a simple 2D modeling experiment with JUDI with an OBN-type acquisition (receivers everywhere), we start by loading the module and building a two layer model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100) # (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, 50:end] .= 5f0\n\n# Squared slowness\nm = (1f0 ./ v).^2","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For working with JUDI operators, we need to set up a model structure, which contains the grid information, as well as the slowness. Optionally, we can provide an array of the density in g/cm^3 (by default a density of 1 is used):","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Density (optional)\nrho = ones(Float32, n)\n\n# Model structure:\nmodel = Model(n, d, o, m; rho=rho)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we define our source acquisition geometry, which needs to be defined as a Geometry structure. The Geometry function requires the x-, y- and z-coordinates of the source locations as input, as well as the modeling time and samping interval of the wavelet. In general, each parameter can be passed as a cell array, where each cell entry provides the information for the respective source location. The helper function convertToCell converts a Julia range to a cell array, which makes defining the source geometry easier:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up source geometry\nnsrc = 4 # no. of sources\nxsrc = convertToCell(range(400f0, stop=800f0, length=nsrc))\nysrc = convertToCell(range(0f0, stop=0f0, length=nsrc))\nzsrc = convertToCell(range(20f0, stop=20f0, length=nsrc))\n\n# Modeling time and sampling interval\ntime = 1000f0 # ms\ndt = 2f0 # ms\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Now we can define our source wavelet. The source must be defined as a judiVector, which takes the source geometry, as well as the source data (i.e. the wavelet) as an input argument:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"In general, wavelet can be a cell array with a different wavelet in each cell, i.e. for every source location. Here, we want to use the same wavelet for all 4 source experiments, so we can simply pass a single vector. As we already specified in our src_geometry object that we want to have 4 source locations, judiVector will automaticallty copy the wavelet for every experiment.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we set up the receiver acquisition geometry. Here, we define an OBN acquisition, where the receivers are spread out over the entire domain and each source experiment uses the same set of receivers. Again, we can in principle pass the coordinates as cell arrays, with one cell per source location. Since we want to use the same geometry for every source, we can use a short cut and define the coordinates as Julia ranges and pass nsrc=nsrc as an optional argument to the Geometry function. This tells the function that we want to use our receiver set up for nsrc distinct source experiments:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up receiver geometry (for 2D, set yrec to zero)\nnxrec = 120\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = 0f0\nzrec = range(50f0, stop=50f0, length=nxrec)\n\n# Set up receiver structure\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we can define separate operators for source/receiver projections and a forward modeling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nPr = judiProjection(rec_geometry)\nA_inv = judiModeling(model)\nPs = judiProjection(src_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can see, that from JUDI's perspective, source and receivers are treated equally and are represented by the same operators (judiProjection) and vectors (judiVector).","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We also could've skipped setting up the projection operators and directly created:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"F = judiModeling(model, src_geometry, rec_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"which is equivalent to creating the combined operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"F = Pr*A_inv*Ps'","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Finally, to model our seismic data, we run:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"d_obs = Pr*A_inv*Ps'*q\n# or\nd_obs = F*q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can plot a 2D shot record by accessing the .data field of the judiVector, which contains the data in the original (non-vectorized) dimensions:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using PyPlot\nimshow(d_obs.data[1], vmin=-5, vmax=5, cmap=\"seismic\", aspect=\"auto\")","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can also set up a Jacobian operator for Born modeling and reverse-time migration. First we set up a (constant) migration velocity model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"v0 = ones(Float32, n) .* 1.4f0\nm0 = (1f0 ./ v0).^2\ndm = m - m0 # model perturbation/image\n\n# Model structure\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can create the Jacobian directly from a (non-linear) modeling operator and a source vector:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"A0_inv = judiModeling(model0) # modeling operator for migration velocity\nJ = judiJacobian(Pr*A0_inv*Ps', q)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can use this operator to model single scattered data, as well as for migration our previous data:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"d_lin = J*vec(dm)\n\n# RTM\nrtm = J'*d_obs","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To plot, first reshape the image:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"rtm = reshape(rtm, model0.n)\nimshow(rtm', cmap=\"gray\", vmin=-1e3, vmax=1e3)","category":"page"},{"location":"basics/#D-Modeling-Quickstart-2","page":"Getting Started","title":"3D Modeling Quickstart","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Setting up a 3D experiment largely follows the instructions for the 2D example. Instead of a 2D model, we define our velocity model as:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100, 80) # (x,y,z)\nd = (10., 10., 10.)\no = (0., 0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, :, 40:end] .= 5f0\n\n# Squared slowness and model structure\nm = (1f0 ./ v).^2\nmodel = Model(n, d, o, m)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Our source coordinates now also need to have the y-coordinate defined:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up source geometry\nnsrc = 4 # no. of sources\nxsrc = convertToCell(range(400f0, stop=800f0, length=nsrc))\nysrc = convertToCell(range(200f0, stop=1000f0, length=nsrc))\nzsrc = convertToCell(range(20f0, stop=20f0, length=nsrc))\n\n# Modeling time and sampling interval\ntime = 1000f0 # ms\ndt = 2f0 # ms\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Our source wavelet, is set up as in the 2D case:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For the receivers, we generally need to define each coordinate (x, y, z) for every receiver. I.e. xrec, yrec and zrec each have the length of the total number of receivers. However, oftentimes we are interested in a regular receiver grid, which can be defined by two basis vectors and a constant depth value for all receivers. We can then use the setup_3D_grid helper function to create the full set of coordinates:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Receiver geometry\nnxrec = 120\nnyrec = 100\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = range(100f0, stop=900f0, length=nyrec)\nzrec = 50f0\n\n# Construct 3D grid from basis vectors\n(xrec, yrec, zrec) = setup_3D_grid(xrec, yrec, zrec)\n\n# Set up receiver structure\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Setting up the modeling operators is done as in the previous 2D case:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nPr = judiProjection(rec_geometry)\nA_inv = judiModeling(model)\nPs = judiProjection(src_geometry)\n\n# Model data\nd_obs = Pr*A_inv*Ps'*q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The 3D shot records are still saved as 2D arrays of dimensions time x (nxrec*nyrec):","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using PyPlot\nimshow(d_obs.data[1], vmin=-.4, vmax=.4, cmap=\"seismic\", aspect=\"auto\")","category":"page"},{"location":"basics/#Vertical-and-tilted-transverse-isotropic-modeling-(VTI,-TTI)","page":"Getting Started","title":"Vertical and tilted-transverse isotropic modeling (VTI, TTI)","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports both VTI and TTI modeling based on a coupled pseudo-acoustic wave equation. To enable VTI/TTI modeling, simply pass Thomsen parameters as well as the tilt angles to the Model structure as optional keyword arguments:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Grid and model\nn = (120, 100, 80)\nd = (10., 10., 10)\no = (0., 0., 0.)\n\n# Velocity\nv = ones(Float32, n) .* 1.5f0\nm = 1f0 ./ v.^2\n\n# Thomsen parameters\nepsilon = ones(Float32, n) .* 0.2f0\ndelta = ones(Float32, n) .* 0.1f0\n\n# Tile angles for TTI\ntheta = ones(Float32, n) .* pi/2f0\nphi = ones(Float32, n) .* pi/3f0 # 3D only\n\n# Set up model structure with Thomsen parameters\nmodel = Model(n, d, o, m; rho=rho, epsilon=epsilon, delta=delta, theta=theta, delta=delta)","category":"page"},{"location":"basics/#Modeling-with-density","page":"Getting Started","title":"Modeling with density","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To use density, pass rho in the units of [g/cm^3] as an optional keyword argument to the Model structure. The default density is rho=1f0 (i.e. density of water):","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Grid and model\nn = (120, 100)\nd = (10., 10.)\no = (0., 0.)\nv = ones(Float32, n) .* 1.5f0\nm = 1f0 ./ v.^2\nrho = ones(Float32, n) .* 1.1f0\n\n# Set up model structure with density\nmodel = Model(n, d, o, m; rho=rho)","category":"page"},{"location":"basics/#D-Marine-streamer-acquisition","page":"Getting Started","title":"2D Marine streamer acquisition","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For a marine streamer acquisition, we need to define a moving set of receivers representing a streamer that is towed behind a seismic source vessel. In JUDI, this is easily done by defining a different set of receivers for each source location. Here, we explain how to set up the Geometry objects for a 2D marine streamer acquisition.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"If we define that our streamer is to the right side of the source vessel, this has the effect that part of the streamer is outside the grid while our vessel is in the right side of the model. To circumvent this, we can say that our streamer is on the right side of the source while the vessel is in the left-hand side of the model and vice versa. This way, we get the full maximum offset coverage for every source location (assuming that the maximum offset is less or equal than half the domain size). ","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"First, we have to specify our domain size (the physical extent of our model), as well as the number of receivers and the minimum and maximum offset:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"domain_x = (model.n[1] - 1)*model.d[1] # horizontal extent of model\nnrec = 120 # no. of receivers\nxmin = 50f0 # leave buffer zone w/o source and receivers of this size\nxmax = domain_x - 50f0\nmin_offset = 10f0 # distance between source and first receiver\nmax_offset = 400f0 # distance between source and last\nxmid = domain_x / 2 # midpoint of model\nsource_spacing = 25f0 # source interval [m]","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For the JUDI Geometry objects, we need to create cell arrays for the source and receiver coordinates, with one cell entry per source location:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source/receivers\nnsrc = 20 # number of shot locations\n\n# Receiver coordinates\nxrec = Array{Any}(undef, nsrc)\nyrec = Array{Any}(undef, nsrc)\nzrec = Array{Any}(undef, nsrc)\n\n# Source coordinates\nxsrc = Array{Any}(undef, nsrc)\nysrc = Array{Any}(undef, nsrc)\nzsrc = Array{Any}(undef, nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we compute the source and receiver coordinates for when the vessel moves from left to right in the right-hand side of the model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Vessel goes from left to right in right-hand side of model\nnsrc_half = Int(nsrc/2)\nfor j=1:nsrc_half\n xloc = xmid + (j-1)*source_spacing\n\n # Current receiver locations\n xrec[j] = range(xloc - max_offset, xloc - min_offset, length=nrec)\n yrec[j] = 0.\n zrec[j] = range(50f0, 50f0, length=nrec)\n \n # Current source\n xsrc[j] = xloc\n ysrc[j] = 0f0\n zsrc[j] = 20f0\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Then, we repeat this for the case where the vessel goes from right to left in the left-hand model side:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Vessel goes from right to left in left-hand side of model\nfor j=1:nsrc_half\n xloc = xmid - (j-1)*source_spacing\n \n # Current receiver locations\n xrec[nsrc_half + j] = range(xloc + min_offset, xloc + max_offset, length=nrec)\n yrec[nsrc_half + j] = 0f0\n zrec[nsrc_half + j] = range(50f0, 50f0, length=nrec)\n \n # Current source\n xsrc[nsrc_half + j] = xloc\n ysrc[nsrc_half + j] = 0f0\n zsrc[nsrc_half + j] = 20f0\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Finally, we can set the modeling time and sampling interval and create the Geometry objects:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# receiver sampling and recording time\ntime = 10000f0 # receiver recording time [ms]\ndt = 4f0 # receiver sampling interval\n\n# Set geometry objects\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time)\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"You can find a full (reproducable) example for generating a marine streamer data set for the Sigsbee 2A model here.","category":"page"},{"location":"basics/#Simultaneous-sources","page":"Getting Started","title":"Simultaneous sources","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To set up a simultaneous source with JUDI, we first create a cell array with nsrc cells, where nsrc is the number of separate experiments (here nsrc=1). For a simultaneous source, we create an array of source coordinates for each cell entry. In fact, this is exactly like setting up the receiver geometry, in which case we define multiple receivers per shot location. Here, we define a single experiment with a simultaneous source consisting of four sources:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"nsrc = 1 # single simultaneous source\nxsrc = Vector{Float32}(undef, nsrc)\nysrc = Vector{Float32}(undef, nsrc)\nzsrc = Vector{Float32}(undef, nsrc)\n\n# Set up source geometry\nxsrc[1] = [250f0, 500f0, 750f0, 1000f0] # four simultaneous sources\nysrc[1] = 0f0\nzsrc[1] = [50f0, 50f0, 50f0, 50f0]\t\n\n# Source sampling and number of time steps\ntime = 2000f0\ndt = 4f0\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"With the simultaneous source geometry in place, we can now create our simultaneous data. As we have four sources per sim. source, we create an array of dimensions 4 x src_geometry.nt[1] and fill it with wavelets of different time shifts:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Create wavelet\nf0 = 0.01\t# source peak frequencies\nq = ricker_wavelet(500f0, dt, f0) # 500 ms wavelet\n\n# Create array with different time shifts of the wavelet\nwavelet = zeros(Float32, 4, src_geometry.nt[1])\nwavelet[1, 1:1+length(q)-1] = q\nwavelet[2, 41:41+length(q)-1] = q\nwavelet[3, 121:121+length(q)-1] = q\nwavelet[4, 201:201+length(q)-1] = q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Finally, we create our simultaneous source as a judiVector:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/#Computational-simultaneous-sources-(super-shots)","page":"Getting Started","title":"Computational simultaneous sources (super shots)","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The computational simultaneous sources refer to superposition of sequentially-fired sources and shot records from the field. These computational simultaneous shot records are not acquired in the field simultaneously, but computational simultaneous sources introduce randomness to the optimization problems like FWI and LS-RTM, which takes advantage of the knowledge in randomized linear algebra to make optimization faster and more robust.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The implementation of computational simultaneous sources follows the journal article Fast randomized full-waveform inversion with compressive sensing The simultaneous sources experiments are generated by superposition of shot records with random weights drawn from Gaussian distribution.","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# assume dobs is generated by sequentially fired point sources q\nnsimsrc = 8\n# Set up random weights\nweights = randn(Float32,nsimsrc,q.nsrc)\n# Create SimSource\nq_sim = weights * q\ndata_sim = weights * dobs\n# We can also apply the weights to the operator and check the equivalence\nd_sim = (weights * F) * q_sim\nisapprox(data_sim, d_sim)","category":"page"},{"location":"basics/#Working-with-wavefields","page":"Getting Started","title":"Working with wavefields","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI allows computing full time domain wavefields and using them as right-hand sides for wave equations solves. This tutorial shows how. We start by setting up a basic 2D experiment:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100) # (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, 50:end] .= 5f0\n\n# Squared slowness\nm = (1f0 ./ v).^2\n\n# Model structure:\nmodel = Model(n, d, o, m)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we set up the source geometry for a single source experiment:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Set up source geometry\nnsrc = 1 # no. of sources\nxsrc = convertToCell([600f0])\nysrc = convertToCell([0f0])\nzsrc = convertToCell([20f0])\n\n# Modeling time and sampling interval\ntime = 600f0 # ms\ndt = 4f0 # ms\n\n# Set up source structure\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=time)\n\n# Source wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(time, dt, f0)\nq = judiVector(src_geometry, wavelet)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As in the 2D quick start tutorial, we create our modeling operator and source projection operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nA_inv = judiModeling(model)\nPs = judiProjection(src_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To model a wavefield, we simply omit the receiver sampling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"u = A_inv*Ps'*q","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"This return an abstract data vector called judiWavefield. Similar to judiVectors, we can access the data for each source number i via u.data[i]. The data is a 3D array of size (nt, nx, nz) for 2D and a 4D array of size (nt, nx, ny, nz) for 3D. We can plot the wavefield of the 600th time step with:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using PyPlot\nimshow(u.data[1][600, :, :]', vmin=-5, vmax=5, cmap=\"seismic\", aspect=\"auto\")","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We can also use the computed wavefield u as a right-hand side for forward and adjoint wave equation solves:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"v = A_inv*u\nw = A_inv'*u","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Similarly, by setting up a receiver projection operator, we can use wavefields as right-hand sides, but restrict the output to the receiver locations.","category":"page"},{"location":"basics/#Extended-source-modeling","page":"Getting Started","title":"Extended source modeling","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports extened source modeling, which injects a 1D wavelet q at every point in the subsurface weighted by a spatially varying extended source. To demonstrate extended source modeling, we first set up a runnable 2D experiment with JUDI. We start with defining the model:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"using JUDI\n\n# Grid\nn = (120, 100) # (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32, n) .* 1.4f0\nv[:, 50:end] .= 5f0\n\n# Squared slowness\nm = (1f0 ./ v).^2\n\n# Model structure:\nmodel = Model(n, d, o, m)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Next, we set up the receiver geometry:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Number of experiments\nnsrc = 2\n\n# Set up receiver geometry\nnxrec = 120\nxrec = range(50f0, stop=1150f0, length=nxrec)\nyrec = 0f0\nzrec = range(50f0, stop=50f0, length=nxrec)\n\n# Modeling time and receiver sampling interval\ntime = 2000\ndt = 4\n\n# Set up receiver structure\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=time, nsrc=nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"For the extended source, we do not need to set up a source geometry object, but we need to define a wavelet function:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Source wavelet\nf0 = 0.01f0 # MHz\nwavelet = ricker_wavelet(time, dt, f0)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As before, we set up a modeling operator and a receiver sampling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Setup operators\nA_inv = judiModeling(model)\nPr = judiProjection(rec_geometry)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"We define our extended source as a so called judiWeights vector. Similar to a judiVector, the data of this abstract vector is stored as a cell array, where each cell corresponds to one source experiment. We create a cell array of length two and create a random array of the size of the model as our extended source:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"weights = Array{Array}(undef, nsrc)\nfor j=1:nsrc\n weights[j] = randn(Float32, model.n)\nend\nw = judiWeights(weights)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"To inject the extended source into the model and weight it by the wavelet, we create a special projection operator called judiLRWF (for JUDI low-rank wavefield). This operator needs to know the wavelet we defined earlier. We can then create our full modeling operator, by combining Pw with A_inv and the receiver sampling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Create operator for injecting the weights, multiplied by the provided wavelet(s)\nPw = judiLRWF(wavelet)\n\n# Model observed data w/ extended source\nF = Pr*A_inv*adjoint(Pw)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Extended source modeling supports both forward and adjoint modeling:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Simultaneous observed data\nd_sim = F*w\ndw = adjoint(F)*d_sim","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As for regular modeling, we can create a Jacobian for linearized modeling and migration. First we define a migration velocity model and the corresponding modeling operator A0_inv:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Migration velocity and squared slowness\nv0 = ones(Float32, n) .* 1.4f0\nm0 = (1f0 ./ v0).^2\n\n# Model structure and modeling operator for migration velocity\nmodel0 = Model(n, d, o, m0)\nA0_inv = judiModeling(model0)\n\n# Jacobian and RTM\nJ = judiJacobian(Pr*A0_inv*adjoint(Pw), w)\nrtm = adjoint(J)*d_sim","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"As before, we can plot the image after reshaping it into its original dimensions:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"rtm = reshape(rtm, model.n)\nimshow(rtm', cmap=\"gray\", vmin=-3e6, vmax=3e6)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Please also refer to the reproducable example on github for 2D and 3D extended modeling.","category":"page"},{"location":"basics/#Impedance-imaging-(inverse-scattering)","page":"Getting Started","title":"Impedance imaging (inverse scattering)","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports imaging (RTM) and demigration (linearized modeling) using the linearized inverse scattering imaging condition (ISIC) and its corresponding adjoint. ISIC can be enabled via the Options class. You can set this options when you initially create the modeling operator:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Options strucuture\nopt = Options(isic=true)\n\n# Set up modeling operator\nA0_inv = judiModeling(model0; options=opt)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"When you create a Jacobian from a forward modeling operator, the Jacobian inherits the options from A0_inv:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J = judiJacobian(Pr*A0_inv*Ps', q)\nJ.options.isic\n# -> true","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Alternatively, you can directly set the option in your Jacobian:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J.options.isic = true # enable isic\nJ.options.isic = false # disable isic","category":"page"},{"location":"basics/#Optimal-checkpointing","page":"Getting Started","title":"Optimal checkpointing","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports optimal checkpointing via Devito's interface to the Revolve library. To enable checkpointing, use the Options class:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Options strucuture\nopt = Options(optimal_checkpointing=true)\n\n# Set up modeling operator\nA0_inv = judiModeling(model0; options=opt)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"When you create a Jacobian from a forward modeling operator, the Jacobian inherits the options from A0_inv:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J = judiJacobian(Pr*A0_inv*Ps', q)\nJ.options.optimal_checkpointing\n# -> true","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Alternatively, you can directly set the option in your Jacobian:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J.options.optimal_checkpointing = true # enable checkpointing\nJ.options.optimal_checkpointing = false # disable checkpointing","category":"page"},{"location":"basics/#On-the-fly-Fourier-transforms","page":"Getting Started","title":"On-the-fly Fourier transforms","text":"","category":"section"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"JUDI supports seismic imaging in the frequency domain using on-the-fly discrete Fourier transforms (DFTs). To compute an RTM image in the frequency domain for a given set of frequencies, we first create a cell array for the frequencies of each source experiment:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"nsrc = 4 # assume 4 source experiments\nfrequencies = Array{Any}(undef, nsrc)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Now we can define single or multiple frequencies for each shot location for which the RTM image will be computed:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# For every source location, compute RTM image for 10 and 20 Hz\nfor j=1:nsrc\n frequencies[j] = [0.001, 0.002]\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"The frequencies are passed to the Jacobian via the options field. Assuming we already have a Jacobian set up, we set the frequencies via:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"J.options.frequencies = frequencies","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Instead of the same two frequencies for each source experiment, we could have chosen different random sets of frequencies, which creates an RTM with incoherent noise. We can also draw random frequencies using the frequency spectrum of the true source as the probability density function. To create a distribution for a given source q (judiVector) from which we can draw frequency samples, use:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"q_dist = generate_distribution(q)","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Then we can assigne a random set of frequencies in a specified range as follows:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"nfreq = 10 # no. of frequencies per source location\nfor j=1:nsrc\n J.options.frequencies[j] = select_frequencies(q_dist; fmin=0.003, fmax=0.04, nf=nfreq)\nend","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"Once the options.frequencies field is set, on-the-fly DFTs are used for both born modeling and RTM. To save computational cost, we can limit the number of DFTs that are performed. Rather than computing the DFT at every time step, we can define a subsampling factor as follows:","category":"page"},{"location":"basics/","page":"Getting Started","title":"Getting Started","text":"# Compute DFT every 4 time steps\nJ.options.dft_subsampling_factor=4","category":"page"},{"location":"installation/#Installation","page":"Installation","title":"Installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI is a linear algebra abstraction built on top of Devito. Because Devito is a just-in-time compiler, you will need to have a standard C compiler installed. by default most system come with a gcc compiler (except Windows where we recommend to use docker or WSL) which unfortunately isnt' very reliable. It is therefore recommended to install a proper compiler (gcc>=7, icc). For GPU offloading, you will then need to install a proper offloading compiler such as Nvidia's nvc or the latest version of clang (not Apple clang).","category":"page"},{"location":"installation/#Standard-installation","page":"Installation","title":"Standard installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI is registered and can be installed directly in julia REPL","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"] add JUDI","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"This will install JUDI, and the build will install the necessary dependencies including Devito.","category":"page"},{"location":"installation/#Custom-installation","page":"Installation","title":"Custom installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"In some case you may want to have your own installation of Devito you want JUDI to use in which case you should foloow these steps.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"You can find installation instruction in our Wiki at Installation.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI is a registered package and can therefore be easily installed from the General registry with ]add/dev JUDI","category":"page"},{"location":"installation/#GPU","page":"Installation","title":"GPU","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI supports the computation of the wave equation on GPU via Devito's GPU offloading support.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"NOTE: Only the wave equation part will be computed on GPU, the julia arrays will still be CPU arrays and CUDA.jl is not supported.","category":"page"},{"location":"installation/#Compiler-installation","page":"Installation","title":"Compiler installation","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"To enable gpu support in JUDI, you will need to install one of Devito's supported offloading compilers. We strongly recommend checking the Wiki for installation steps and to reach out to the Devito community for GPU compiler related issues.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"[x] nvc/pgcc. This is recommended and the simplest installation. You can install the compiler following Nvidia's installation instruction at HPC-sdk\n[ ] aompcc. This is the AMD compiler that is necessary for running on AMD GPUs. This installation is not tested with JUDI and we recommend to reach out to Devito's team for installation guidelines.\n[ ] openmp5/clang. This installation requires the compilation from source openmp, clang and llvm to install the latest version of openmp5 enabling gpu offloading. You can find instructions on this installation in Devito's Wiki","category":"page"},{"location":"installation/#Setup","page":"Installation","title":"Setup","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"The only required setup for GPU support are the environment variables for Devito. For the currently supported nvc+openacc setup these are:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"export DEVITO_LANGUAGE=openacc\nexport DEVITO_ARCH=nvc\nexport DEVITO_PLATFORM=nvidiaX","category":"page"},{"location":"installation/#Running-with-Docker","page":"Installation","title":"Running with Docker","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"If you do not want to install JUDI, you can run JUDI as a docker image. The first possibility is to run the docker container as a Jupyter notebook. JUDI provides two docker images for the latest JUDI release for Julia versions 1.6 (LTS) and 1.7 (latest stable version). The images names are mloubout/judi:JVER-latest where JVER is the Julia version. This docker images contain pre-installed compilers for CPUs (gcc-10) and Nvidia GPUs (nvc) via the nvidia HPC sdk. The environment is automatically set for [Devito] based on the hardware available. ","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Note: If you wish to use your gpu, you will need to install nvidia-docker and run docker run --gpus all in order to make the GPUs available at runtime from within the image.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"To run JUDI via docker execute the following command in your terminal:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"docker run -p 8888:8888 mloubout/judi:1.7-latest","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"This command downloads the image and launches a container. You will see a link that you can copy-paste to your browser to access the notebooks. Alternatively, you can run a bash session, in which you can start a regular interactive Julia session and run the example scripts. Download/start the container as a bash session with:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"docker run -it mloubout/judi:1.7-latest /bin/bash","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Inside the container, all examples are located in the directory /app/judi/examples/scripts.","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Previous versions: As of version v2.6.7 of JUDI, we also ship version-tagged images as mloubout/judi:JVER-ver where ver is the version of JUDI wanted, for example the current JUDI version with Julia 1.7 is mloubout/judi:1.7-v2.6.7","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Development version: Additionally, we provide two images corresponding to the latest development version of JUDI (latest state of the master branch). These images are called mloubout/judi:JVER-dev and can be used in a similar way.","category":"page"},{"location":"installation/#Testing","page":"Installation","title":"Testing","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"A complete test suite is included with JUDI and is tested via GitHub Actions. You can also run the test locally via:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":" julia --project -e 'using Pkg;Pkg.test(coverage=false)'","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"By default, only the JUDI base API will be tested, however the testing suite supports other modes controlled via the environemnt variable GROUP such as:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"\tGROUP=JUDI julia --project -e 'using Pkg;Pkg.test(coverage=false)'","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"The supported modes are:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"JUDI : Only the base API (linear operators, vectors, ...)\nISO_OP : Isotropic acoustic operators\nISOOPFS : Isotropic acoustic operators with free surface\nTTI_OP : Transverse tilted isotropic operators\nTTIOPFS : Transverse tilted isotropic operators with free surface\nfilename : you can also provide just a filename (i.e GROUP=test_judiVector.jl) and only this one test file will be run. Single files with TTI or free surface are not currently supported as it relies on Base.ARGS for the setup.","category":"page"},{"location":"installation/#Configure-compiler-and-OpenMP","page":"Installation","title":"Configure compiler and OpenMP","text":"","category":"section"},{"location":"installation/","page":"Installation","title":"Installation","text":"Devito uses just-in-time compilation for the underlying wave equation solves. The default compiler is intel, but can be changed to any other specified compiler such as gnu. Either run the following command from the command line or add it to your ~/.bashrc file:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"export DEVITO_ARCH=gnu","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"Devito uses shared memory OpenMP parallelism for solving PDEs. OpenMP is disabled by default, but you can enable OpenMP and define the number of threads (per PDE solve) as follows:","category":"page"},{"location":"installation/","page":"Installation","title":"Installation","text":"export DEVITO_LANGUAGE=openmp # Enable OpenMP. \nexport OMP_NUM_THREADS=4 # Number of OpenMP threads","category":"page"},{"location":"inversion/#Seismic-Inversion","page":"Inversion","title":"Seismic Inversion","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"Pages = [\"inversion.md\"]","category":"page"},{"location":"inversion/#Introduction","page":"Inversion","title":"Introduction","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"We currently introduced the linear operators that allow to write seismic modeling and inversion in a high-level, linear algebra way. These linear operator allow the script to closely follow the mathematics and to be readable and understandable.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"However, these come with overhead. In particular, consider the following compuation on the FWI gradient:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"\nd_syn = F*q\nr = judiJacobian(F, q)' * (d_syn - d_obs)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"In this two lines, the forward modeling is performed twice: once to compute d_syn then once again to compute the Jacobian adjoint. In order to avoid this overhead for practical inversion, we provide utility function that directly comput the gradient and objective function (L2- misfit) of FWI, LSRTM and TWRI with minimum overhead.","category":"page"},{"location":"inversion/#FWI","page":"Inversion","title":"FWI","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"fwi_objective","category":"page"},{"location":"inversion/#JUDI.fwi_objective","page":"Inversion","title":"JUDI.fwi_objective","text":"fwi_objective(model, source, dobs; options=Options())\n\nEvaluate the full-waveform-inversion (reduced state) objective function. Returns a tuple with function value and vectorized \\\n\ngradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and \nobserved data of type judiVector.\n\nExample\n\nfunction_value, gradient = fwi_objective(model, source, dobs)\n\n\n\n\n\n","category":"function"},{"location":"inversion/#Example","page":"Inversion","title":"Example","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"JUDI is designed to let you set up objective functions that can be passed to standard packages for (gradient-based) optimization. The following example demonstrates how to perform FWI on the 2D Overthrust model using a spectral projected gradient algorithm from the minConf library, which is included in the software. A small test dataset (62 MB) and the model can be downloaded from this FTP server:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D.segy`)\nrun(`wget ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_2D_initial_model.h5`)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"The first step is to load the velocity model and the observed data into Julia, as well as setting up bound constraints for the inversion, which prevent too high or low velocities in the final result. Furthermore, we define an 8 Hertz Ricker wavelet as the source function:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"using PyPlot, HDF5, SegyIO, JUDI, SlimOptim, Statistics, Random\n\n# Load starting model\nn, d, o, m0 = read(h5open(\"overthrust_2D_initial_model.h5\", \"r\"), \"n\", \"d\", \"o\", \"m0\")\nmodel0 = Model((n[1], n[2]), (d[1], d[2]), (o[1], o[2]), m0)\t# need n, d, o as tuples and m0 as array\n\n# Bound constraints\nvmin = ones(Float32, model0.n) .+ 0.3f0\nvmax = ones(Float32, model0.n) .+ 5.5f0\nmmin = vec((1f0 ./ vmax).^2)\t# convert to slowness squared [s^2/km^2]\nmmax = vec((1f0 ./ vmin).^2)\n\n# Load segy data\nblock = segy_read(\"overthrust_2D.segy\")\ndobs = judiVector(block)\n\n# Set up wavelet\nsrc_geometry = Geometry(block; key=\"source\", segy_depth_key=\"SourceDepth\")\t# read source position geometry\nwavelet = ricker_wavelet(src_geometry.t[1], src_geometry.dt[1], 0.008f0)\t# 8 Hz wavelet\nq = judiVector(src_geometry, wavelet)\n","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"For this FWI example, we define an objective function that can be passed to the minConf optimization library, which is included in the Julia Devito software package. We allow a maximum of 20 function evaluations using a spectral-projected gradient (SPG) algorithm. To save computational cost, each function evaluation uses a randomized subset of 20 shot records, instead of all 97 shots:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"# Optimization parameters\nfevals = 20\t# number of function evaluations\nbatchsize = 20\t# number of sources per iteration\nfvals = zeros(21)\nopt = Options(optimal_checkpointing = false) # set to true to enable checkpointing\n\n# Objective function for minConf library\ncount = 0\nfunction objective_function(x)\n\tmodel0.m = reshape(x, model0.n);\n\n\t# fwi function value and gradient\n\ti = randperm(dobs.nsrc)[1:batchsize]\n\tfval, grad = fwi_objective(model0, q[i], dobs[i]; options=opt)\n\tgrad = reshape(grad, model0.n); grad[:, 1:21] .= 0f0\t# reset gradient in water column to 0.\n\tgrad = .1f0*grad/maximum(abs.(grad))\t# scale gradient for line search\n\n\tglobal count; count += 1; fvals[count] = fval\n return fval, vec(grad.data)\nend\n\n# FWI with SPG\nProjBound(x) = median([mmin x mmax], dims=2)\t# Bound projection\noptions = spg_options(verbose=3, maxIter=fevals, memory=3)\nres = spg(objective_function, vec(m0), ProjBound, options)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"This example script can be run in parallel and requires roughly 220 MB of memory per source location. Execute the following code to generate figures of the initial model and the result, as well as the function values:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"figure(); imshow(sqrt.(1./adjoint(m0))); title(\"Initial model\")\nfigure(); imshow(sqrt.(1./adjoint(reshape(x, model0.n)))); title(\"FWI\")\nfigure(); plot(fvals); title(\"Function value\")","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"(Image: fwi)","category":"page"},{"location":"inversion/#LSRTM","page":"Inversion","title":"LSRTM","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"lsrtm_objective","category":"page"},{"location":"inversion/#JUDI.lsrtm_objective","page":"Inversion","title":"JUDI.lsrtm_objective","text":"lsrtm_objective(model, source, dobs, dm; options=Options(), nlind=false)\n\nEvaluate the least-square migration objective function. Returns a tuple with function value and \ngradient. model is a Model structure with the current velocity model and source and dobs are the wavelets and \nobserved data of type judiVector.\n\nExample\n\nfunction_value, gradient = lsrtm_objective(model, source, dobs, dm)\n\n\n\n\n\n","category":"function"},{"location":"inversion/#Example-2","page":"Inversion","title":"Example","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"JUDI includes matrix-free linear operators for modeling and linearized (Born) modeling, that let you write algorithms for migration that follow the mathematical notation of standard least squares problems. This example demonstrates how to use Julia Devito to perform least-squares reverse-time migration on the 2D Marmousi model. Start by downloading the test data set (1.1 GB) and the model:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"run(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_2D.segy`)\nrun(`wget ftp://slim.gatech.edu/data/SoftwareRelease/Imaging.jl/2DLSRTM/marmousi_migration_velocity.h5`)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"Once again, load the starting model and the data and set up the source wavelet. For this example, we use a Ricker wavelet with 30 Hertz peak frequency.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"using PyPlot, HDF5, JUDI, SegyIO, Random\n\n# Load smooth migration velocity model\nn,d,o,m0 = read(h5open(\"marmousi_migration_velocity.h5\",\"r\"), \"n\", \"d\", \"o\", \"m0\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0)\n\n# Load data\nblock = segy_read(\"marmousi_2D.segy\")\ndD = judiVector(block)\n\n# Set up wavelet\nsrc_geometry = Geometry(block; key=\"source\", segy_depth_key=\"SourceDepth\")\nwavelet = ricker_wavelet(src_geometry.t[1],src_geometry.dt[1],0.03)\t# 30 Hz wavelet\nq = judiVector(src_geometry,wavelet)\n\n# Set up info structure\nntComp = get_computational_nt(q.geometry,dD.geometry,model0)\t# no. of computational time steps\ninfo = Info(prod(model0.n),dD.nsrc,ntComp)","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"To speed up the convergence of our imaging example, we set up a basic preconditioner for each the model- and the data space, consisting of mutes to suppress the ocean-bottom reflection in the data and the source/receiver imprint in the image. The operator J represents the linearized modeling operator and its adjoint J' corresponds to the migration (RTM) operator. The forward and adjoint pair can be used for a basic LS-RTM example with (stochastic) gradient descent:","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"# Set up matrix-free linear operators\nopt = Options(optimal_checkpointing = true) # set to false to disable optimal checkpointing\nF = judiModeling(model0, q.geometry, dD.geometry; options=opt)\nJ = judiJacobian(F, q)\n\n# Right-hand preconditioners (model topmute)\nMr = judiTopmute(model0; taperwidth=10)\t# mute up to grid point 52, with 10 point taper\n# Left-hand side preconditioners\nMl = judiDatMute(q.geometry, dD.geometry; t0=.120)\t# data topmute starting at time 120ms\n\n# Stochastic gradient\nx = zeros(Float32, info.n)\t# zero initial guess\nbatchsize = 10\t# use subset of 10 shots per iteration\nniter = 32\nfval = zeros(Float32, niter)\n\nfor j=1:niter\n\tprintln(\"Iteration: \", j)\n\n\t# Select batch and set up left-hand preconditioner\n\ti = randperm(dD.nsrc)[1:batchsize]\n\n\t# Compute residual and gradient\n\tr = Ml[i]*J[i]*Mr*x - Ml[i]*dD[i]\n\tg = adjoint(Mr)*adjoint(J[i])*adjoint(Ml[i])*r\n\n\t# Step size and update variable\n\tfval[j] = .5f0*norm(r)^2\n\tt = norm(r)^2/norm(g)^2\n\tglobal x -= t*g\nend","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"(Image: lsrtm)","category":"page"},{"location":"inversion/#TWRI","page":"Inversion","title":"TWRI","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"twri_objective","category":"page"},{"location":"inversion/#JUDI.twri_objective","page":"Inversion","title":"JUDI.twri_objective","text":"twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())\n\nEvaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and gradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and observed data of type judiVector.\n\nExample\n\nfunction_value, gradient_m, gradient_y = twri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())\n\n\n\n\n\ntwri_objective(model, source, dobs; options=Options(), optionswri=TWRIOptions())\n\nEvaluate the time domain Wavefield reconstruction inversion objective function. Returns a tuple with function value and \ngradient(s) w.r.t to m and/or y. model is a Model structure with the current velocity model and source and dobs are the wavelets and \nobserved data of type judiVector. Example ======= functionvalue, gradient = fwiobjective(model, source, dobs)\n\n\n\n\n\n","category":"function"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"and related TWRI options","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"TWRIOptions","category":"page"},{"location":"inversion/#JUDI.TWRIOptions","page":"Inversion","title":"JUDI.TWRIOptions","text":"TWRIOptions\n grad_corr::Bool\n comp_alpha::Bool\n weight_fun\n eps\n params::Symbol\n Invq::String\n\nOptions structure for TWRI.\n\ngrad_corr: Whether to add the gradient correction J'(m0, q)*∇_y\n\ncomp_alpha: Whether to compute optimal alpha (alpha=1 if not)\n\nweight_fun: Whether to apply focusing/weighting function to F(m0)'*y and its norm\n\neps: Epsilon (noise level) value (default=0)\n\nInvq: How to compute F'Y, either as full field or as a rank 1 approximation w(t)*q(x) using the source wavelet for w\n\nparam: Which gradient to compute. Choices are nothing (objective only), :m, :y or :all\n\nConstructor\n\nAll arguments are optional keyword arguments with the following default values:\n\nTWRIOptions(;gradcorr=false, compalpha=true, weight_fun=nothing, eps=0, params=:m)\n\n\n\n\n\n","category":"type"},{"location":"inversion/#Machine-Learning","page":"Inversion","title":"Machine Learning","text":"","category":"section"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"ChainRules.jl allows integrating JUDI modeling operators into convolutional neural networks for deep learning. For example, the following code snippet shows how to create a shallow CNN consisting of two convolutional layers with a nonlinear forward modeling layer in-between them. The integration of ChainRules and JUDI enables backpropagation through Flux' automatic differentiation tool, but calls the corresponding adjoint JUDI operators under the hood. For more details, please check out this tutorial.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"# Jacobian\nW1 = judiJacobian(F0, q)\nb1 = randn(Float32, num_samples)\n\n# Fully connected layer\nW2 = randn(Float32, n_out, num_samples)\nb2 = randn(Float32, n_out)\n\n# Network and loss\nnetwork(x) = W2*(W1*x .+ b1) .+ b2\nloss(x, y) = Flux.mse(network(x), y)\n\n# Compute gradient w/ Flux\np = params(x, y, W1, b1, b2)\ngs = Tracker.gradient(() -> loss(x, y), p)\ngs[x]\t# gradient w.r.t. to x","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"Integration with ChainRules allows implementing physics-augmented neural networks for seismic inversion, such as loop-unrolled seismic imaging algorithms. For example, the following results are a conventional RTM image, an LS-RTM image and a loop-unrolled LS-RTM image for a single simultaneous shot record.","category":"page"},{"location":"inversion/","page":"Inversion","title":"Inversion","text":"(Image: flux)","category":"page"},{"location":"io/#Input/Output","page":"Input/Output","title":"Input/Output","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"For reading and writing SEG-Y data, JUDI uses the SegyIO.jl package. JUDI supports reading SEG-Y from disk into memory, as well as working with out-of-core (OOC) data containers. In the latter case, judiVectors contain look-up tables that allow accessing the underlying data in constant time.","category":"page"},{"location":"io/#Reading-SEG-Y-files-into-memory","page":"Input/Output","title":"Reading SEG-Y files into memory","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"To read a single SEG-Y file into memory, use the segy_read function:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"using SegyIO\n\nblock = segy_read(\"data.segy\")","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"From a SegyIO data block, you can create an in-core judiVector, as well as a Geometry object for the source:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# judiVector for observed data\nd_obs = judiVector(block; segy_depth_key=\"RecGroupElevation\")\n\n# Source geometry\nsrc_geometry = Geometry(block; key=\"source\", segy_depth_key=\"SourceDepth\")","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"The optional keyword segy_depth_key specifies which SEG-Y header stores the depth coordinate. After reading a block, you can check block.traceheaders to see which trace headers are set and where to find the depth coordinates for sources or receivers.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"The d_obs vector constains the receiver geometry in d_obs.geometry, so there is no need to set up a separate geometry object manually. However, in principle we can set up a receiver Geometry object as follows:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"rec_geometry = Geometry(block; key=\"receiver\", segy_depth_key=\"RecGroupElevation\")","category":"page"},{"location":"io/#Writing-SEG-Y-files","page":"Input/Output","title":"Writing SEG-Y files","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"To write a judiVector as a SEG-Y file, we need a judiVector containing the receiver data and geometry, as well as a judiVector with the source coordinates. From the judiVectors, we first create a SegyIO block:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"block = judiVector_to_SeisBlock(d_obs, q)","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"where d_obs and q are judiVectors for receiver and source data respectively. To save only the source q, we can do","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"block = src_to_SeisBlock(q)","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Next, we can write a SEG-Y file from a SegyIO block:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"segy_write(\"new_file.segy\", block) # writes a SEG-Y file called new_file.segy","category":"page"},{"location":"io/#Reading-out-of-core-SEG-Y-files","page":"Input/Output","title":"Reading out-of-core SEG-Y files","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"For SEG-Y files that do not fit into memory, JUDI provides the possibility to work with OOC data containers. First, SegyIO scans also available files and then creates a lookup table, including a summary of the most important SEG-Y header values. See SegyIO's documentation for more information.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"First we provide the path to the directory that we want to scan, as well as a string that appears in all the files we want to scan. For example, here we want to scan all files that contain the string \"bp_observed_data\". The third argument is a list of SEG-Y headers for which we create a summary. For creating OOC judiVectors, always include the \"GroupX\", \"GroupY\" and \"dt\" keyworkds, as well as the keywords that carry the source and receiver depth coordinates:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# Specify direcotry to scan\npath_to_data = \"/home/username/data_directory/\"\n\n# Scan files in given directory and create OOC data container\ncontainer = segy_scan(path_to_data, \"bp_observed_data\", [\"GroupX\", \"GroupY\", \n \"RecGroupElevation\", \"SourceDepth\", \"dt\"])","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Depending of the number and size of the underlying files, this process can take multiple hours, but it only has to be executed once! Furthermore, parallel scanning is supported as well. ","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Once we have scanned all files in the directory, we can create an OOC judiVector and source Geometry object as follows:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# Create OOC judiVector\nd_obs = judiVector(container; segy_depth_key=\"RecGroupElevation\")\n\n# Create OOC source geometry object\nsrc_geometry = Geometry(container; key=\"source\", segy_depth_key=\"SourceDepth\")","category":"page"},{"location":"io/#Reading-and-writing-velocity-models","page":"Input/Output","title":"Reading and writing velocity models","text":"","category":"section"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"JUDI does not require velocity models to be read or saved in any specific format. Any file format that allows reading the velocity model as a two or three-dimensional Julia array will work.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"In our examples, we often use the JLD or HDF5 packages to read/write velocity models and the corresponing meta data (i.e. grid spacings and origins). If your model is a SEG-Y file, use the segy_read function from SegyIO as shown above.","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Create an example model to write and read:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"n = (120, 100)\nd = (10.0, 10.0)\no = (0.0, 0.0)\nv = ones(Float32, n) .* 1.5f0\nm = 1f0 ./ v.^2","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Write a model as a .jld file:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"using JLD\n\nsave(\"my_model.jld\", \"n\", n, \"d\", d, \"o\", o, \"m\", m)","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"Read a model from a .jld file:","category":"page"},{"location":"io/","page":"Input/Output","title":"Input/Output","text":"# Returns a Julia dictionary\nM = load(\"my_model.jld\")\n\nn = M[\"n\"]\nd = M[\"d\"]\no = M[\"o\"]\nm = M[\"m\"]\n\n# Set up a Model object\nmodel = Model(n, d, o, m)","category":"page"},{"location":"tutorials/quickstart/#Modeling-and-inversion-with-JUDI","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"title: Overview of JUDI modeling and inversion usage author: Mathias Louboutin, Philipp Witte date: April 2022 –-","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"This example script is written using Weave.jl and can be converted to different format for documentation and usage This example is converted to a markdown file for the documentation.","category":"page"},{"location":"tutorials/quickstart/#Import-JUDI,-Linear-algebra-utilities-and-Plotting","page":"Modeling and inversion with JUDI","title":"Import JUDI, Linear algebra utilities and Plotting","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"using JUDI, PyPlot, LinearAlgebra","category":"page"},{"location":"tutorials/quickstart/#Create-a-JUDI-model-structure","page":"Modeling and inversion with JUDI","title":"Create a JUDI model structure","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"In JUDI, a Model structure contains the grid information (origin, spacing, number of gridpoints) and the physical parameters. The squared slowness is always required as the base physical parameter for propagation. In addition, JUDI supports additional physical representations. First we accept density that can either be a direct input Model(n, d, o, m, rho) or an optional keyword argument Model(n,d,o,m;rho=rho). Second, we also provide VTI/TTI kernels parametrized by the THomsen parameters that can be input as keyword arguments Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta,theta=theta,phi=phi). Because the thomsen parameters are optional the propagator wil lonloy use the ones provided. For example Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta) will infer a VTI propagation","category":"page"},{"location":"tutorials/quickstart/#Create-discrete-parameters","page":"Modeling and inversion with JUDI","title":"Create discrete parameters","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Set up model structure\nn = (120, 100) # (x,y,z) or (x,z)\nd = (10., 10.)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32,n) .+ 0.5f0\nv0 = ones(Float32,n) .+ 0.5f0\nv[:,Int(round(end/2)):end] .= 3.5f0\nrho = (v0 .+ .5f0) ./ 2\n\n# Slowness squared [s^2/km^2]\nm = (1f0 ./ v).^2\nm0 = (1f0 ./ v0).^2\ndm = vec(m0 - m)\n\n# Setup model structure\nnsrc = 2\t# number of sources\nmodel = Model(n, d, o, m)\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Model (n=(120, 100), d=(10.0f0, 10.0f0), o=(0.0f0, 0.0f0)) with parameters \n(:m, :rho)","category":"page"},{"location":"tutorials/quickstart/#Create-acquisition-geometry","page":"Modeling and inversion with JUDI","title":"Create acquisition geometry","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"In this simple usage example, we create a simple acquisiton by hand. In practice the acquisition geometry will be defined by the dataset beeing inverted. We show in a spearate tutorial how to use SegyIO.jl to handle SEGY seismic datasets in JUDI.","category":"page"},{"location":"tutorials/quickstart/#Create-source-and-receivers-positions-at-the-surface","page":"Modeling and inversion with JUDI","title":"Create source and receivers positions at the surface","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Set up receiver geometry\nnxrec = 120\nxrec = range(0f0, stop=(n[1]-1)*d[1], length=nxrec)\nyrec = 0f0 # WE have to set the y coordiante to zero (or any number) for 2D modeling\nzrec = range(d[1], stop=d[1], length=nxrec)\n\n# receiver sampling and recording time\ntimeD = 1250f0 # receiver recording time [ms]\ndtD = 2f0 # receiver sampling interval [ms]\n\n# Set up receiver structure\nrecGeometry = Geometry(xrec, yrec, zrec; dt=dtD, t=timeD, nsrc=nsrc)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"GeometryIC{Float32} wiht 2 sources","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"The source geometry is a but different. Because we want to create a survey with nsrc shot records, we need to convert the vector of sources postions [s0, s1, ... sn] into an array of array [[s0], [s1], ...] so that JUDI understands that this is a set of indepednet nsrc","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"xsrc = convertToCell(range(0f0, stop=(n[1]-1)*d[1], length=nsrc))\nysrc = convertToCell(range(0f0, stop=0f0, length=nsrc))\nzsrc = convertToCell(range(d[1], stop=d[1], length=nsrc))\n\n# Set up source structure\nsrcGeometry = Geometry(xsrc, ysrc, zsrc; dt=dtD, t=timeD)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"GeometryIC{Float32} wiht 2 sources","category":"page"},{"location":"tutorials/quickstart/#Source-judiVector","page":"Modeling and inversion with JUDI","title":"Source judiVector","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Finally, with the geometry defined, we can create a source wavelet (a simple Ricker wavelet here) a our first judiVector In JUDI, a judiVector is the core structure that represent a acquisition-geometry based dataset. This structure encapsulate the physical locations (trace coordinates) and corrsponding data trace in a source-based structure. for a given judiVector d then d[1] will be the shot record for the first source, or in the case of the source term, the first source wavelet and its positon.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# setup wavelet\nf0 = 0.01f0 # kHz\nwavelet = ricker_wavelet(timeD, dtD, f0)\nq = judiVector(srcGeometry, wavelet)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 2 sources","category":"page"},{"location":"tutorials/quickstart/#Modeling","page":"Modeling and inversion with JUDI","title":"Modeling","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"With our survey and subsurface model setup, we can now model and image seismic data. We first define a few options. In this tutorial we will choose to compute gradients/images subsampling the forward wavefield every two time steps subsampling_factor=2 and we fix the computational time step to be 1ms wiuth dt_comp=1.0 know to satisfy the CFL condition for this simple example. In practice, when dt_comp isn't provided, JUDI will compute the CFL condition for the propagation.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Setup options\nopt = Options(subsampling_factor=2, space_order=32)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"JUDIOptions(32, false, false, 1000.0f0, false, \"\", \"shot\", false, false, An\ny[], \"as\", 2, 1, false, nothing, 0.015f0)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Linear Operators The core idea behind JUDI is to abstract seismic inverse problems in term of linear algebra. In its simplest form, seismic inversion can be formulated as","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"undersetmathbfmtextargmin phi(mathbfm) = frac12 mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd _2^2 \ntext \nnabla_mathbfm phi(mathbfm) = mathbfJ(mathbfm mathbfq)^top (mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"where mathbfP_r is the receiver projection (measurment operator) and mathbfP_s^top is the source injection operator (adjoint of measurment at the source location). Therefore, we bastracted these operation to be able to define these operators","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Setup operators\nPr = judiProjection(recGeometry)\nF = judiModeling(model; options=opt)\nF0 = judiModeling(model0; options=opt)\nPs = judiProjection(srcGeometry)\nJ = judiJacobian(Pr*F0*adjoint(Ps), q)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"JUDI born{Float32} propagator (z * x) -> (src * rec * time)","category":"page"},{"location":"tutorials/quickstart/#Model-and-image-data","page":"Modeling and inversion with JUDI","title":"Model and image data","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We first model synthetic data using our defined source and true model ","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Nonlinear modeling\ndobs = Pr*F*adjoint(Ps)*q","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 2 sources","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot the shot record","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(dobs.data[1], vmin=-1, vmax=1, cmap=\"PuOr\", extent=[xrec[1], xrec[end], timeD/1000, 0], aspect=\"auto\")\nxlabel(\"Receiver position (m)\")\nylabel(\"Time (s)\")\ntitle(\"Synthetic data\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Because we have abstracted the linear algebra, we can solve the adjoint wave-equation as well where the data becomes the source. This adjoint solve will be part of the imaging procedure.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# # Adjoint\nqad = Ps*adjoint(F)*adjoint(Pr)*dobs","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"judiVector{Float32, Matrix{Float32}} with 2 sources","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We can easily now test the adjointness of our operator with the standard dot test. Because we intend to conserve our linear algebra abstraction, judiVector implements all the necessary linear algebra functions such as dot product or norm to be used directly.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# \ndot1 = dot(q, qad)\n# \ndot2 = dot(dobs, dobs)\n# Compare\n@show dot1, dot2, (dot2 - dot2)/(dot1 + dot2)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(dot1, dot2, (dot2 - dot2) / (dot1 + dot2)) = (467806.06f0, 467806.28f0, 0.\n0f0)\n(467806.06f0, 467806.28f0, 0.0f0)","category":"page"},{"location":"tutorials/quickstart/#Inversion","page":"Modeling and inversion with JUDI","title":"Inversion","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Our main goal is to provide an inversion framework for seismic inversion. To this end, as shown earlier, users can easily define the Jacobian operator and compute an RTM image (i.e FWI gradient) with a simple matrix-vector product. Once again, we provide both the Jacobian and its adjoint and we can compute Born linearized data.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# Linearized modeling J*dm\ndD = J*dm\n# Adjoint jacobian, RTM image\nrtm = adjoint(J)*dD","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"PhysicalParameter{Float32, 2} of size (120, 100) with origin (0.0f0, 0.0f0)\n and spacing (10.0f0, 10.0f0)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We show the linearized data.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(dD.data[1], vmin=-1, vmax=1, cmap=\"PuOr\", extent=[xrec[1], xrec[end], timeD/1000, 0], aspect=\"auto\")\nxlabel(\"Receiver position (m)\")\nylabel(\"Time (s)\")\ntitle(\"Linearized data\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"And the RTM image","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(rtm', vmin=-1e2, vmax=1e2, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"RTM image\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/#Inversion-utility-functions","page":"Modeling and inversion with JUDI","title":"Inversion utility functions","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"We currently introduced the lineaar operators that allow to write seismic modeling and inversion in a high-level, linear algebra way. These linear operators allow the script to closely follow the mathematics and to be readable and understandable.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"However, these come with overhead. In particular, consider the following compuation on the FWI gradient:","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"d_syn = F*q\nr = judiJacobian(F, q)' * (d_syn - d_obs)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"In this two lines, the forward modeling is performed twice: once to compute d_syn then once again to compute the Jacobian adjoint. In order to avoid this overhead for practical inversion, we provide utility function that directly comput the gradient and objective function (L2- misfit) of FWI, LSRTM and TWRI with minimum overhead.","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"FWI misfit and gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# evaluate FWI objective function\nf, g = fwi_objective(model0, q, dobs; options=opt)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(21226.521f0, PhysicalParameter{Float32, 2} of size (120, 100) with origin \n(0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(g', vmin=-1e2, vmax=1e2, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"FWI gradient\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"LSRTM misfit and gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"# evaluate LSRTM objective function\nfj, gj = lsrtm_objective(model0, q, dD, dm; options=opt)\nfjn, gjn = lsrtm_objective(model0, q, dobs, dm; nlind=true, options=opt)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(35903.93f0, PhysicalParameter{Float32, 2} of size (120, 100) with origin (\n0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradients","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(gj', vmin=-1, vmax=1, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"LSRTM gradient\")\ndisplay(fig)\n\nfig = figure()\nimshow(gjn', vmin=-1, vmax=1, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"LSRTM gradient with background data substracted\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: ) (Image: )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"By extension, lsrtmobjective is the same as fwiobjecive when dm is zero And with computing of the residual. Small noise can be seen in the difference due to floating point roundoff errors with openMP, but running with OMPNUMTHREADS=1 (no parllelism) produces the exact (difference == 0) same result gjn2 == g","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fjn2, gjn2 = lsrtm_objective(model0, q, dobs, 0f0.*dm; nlind=true, options=opt)\nfig = figure()","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"PyPlot.Figure(PyObject
      )","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradient","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"imshow(gjn2', vmin=-1e2, vmax=1e2, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"LSRTM gradient with zero perturbation\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: )","category":"page"},{"location":"tutorials/quickstart/#TWRI","page":"Modeling and inversion with JUDI","title":"TWRI","text":"","category":"section"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Finally, JUDI implements TWRI, an augmented method to tackle cycle skipping. Once again we provide a computationnally efficient wrapper function that returns the objective value and necessary gradients","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"f, gm, gy = twri_objective(model0, q, dobs, nothing; options=opt, optionswri=TWRIOptions(params=:all))\n# With on-the-fly DFT, experimental\nf, gmf = twri_objective(model0, q, dobs, nothing; options=Options(frequencies=[[.009, .011], [.008, .012]]), optionswri=TWRIOptions(params=:m))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(-2244.6035f0, PhysicalParameter{Float32, 2} of size (120, 100) with origin\n (0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"Plot gradients","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"fig = figure()\nimshow(gm', vmin=-1, vmax=1, cmap=\"Greys\", extent=[0, (n[1]-1)*d[1], (n[2]-1)*d[2], 0 ], aspect=\"auto\")\nxlabel(\"Lateral position(m)\")\nylabel(\"Depth (m)\")\ntitle(\"TWRI gradient w.r.t m\")\ndisplay(fig)\n\nfig = figure()\nimshow(gy.data[1], vmin=-1e2, vmax=1e2, cmap=\"PuOr\", extent=[xrec[1], xrec[end], timeD/1000, 0], aspect=\"auto\")\nxlabel(\"Receiver position (m)\")\nylabel(\"Time (s)\")\ntitle(\"TWRI gradient w.r.t y\")\ndisplay(fig)","category":"page"},{"location":"tutorials/quickstart/","page":"Modeling and inversion with JUDI","title":"Modeling and inversion with JUDI","text":"(Image: ) (Image: )","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/#FWI-with-Quasi-Newton-methods-from-the-NLopt-library","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"","category":"section"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"In this notebook, we demonstrate how to interface the NLopt optimization library for full-waveform inversion with a limited-memory Quasi-Newton (L-BFGS) algorithm. Once again, we start by adding additional workers for parallel computing and by loading all necessary modules:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"using SegyIO, HDF5, PyPlot, JUDI, NLopt, Random, LinearAlgebra, Printf","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"We load the FWI starting model from the HDF5 model file and set up the JUDI model structure:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"m0, n, d, o = read(h5open(\"overthrust_model.h5\",\"r\"),\"m0\",\"n\",\"d\",\"o\"); title(\"Starting model\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0);\nimshow(sqrt.(1f0./m0)', cmap=\"GnBu\", extent=(0,10,3,0));\nxlabel(\"Lateral position [km]\");\nylabel(\"Depth [km]\");","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Then we read the SEG-Y file containing our test data set. The data was generated with a 2D excerpt from the Overthrust velocity model and consists of 31 shot records with 2 seconds recording time. We load the data and set up a JUDI seismic data vector:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"block = segy_read(\"overthrust_shot_records.segy\");\nd_obs = judiVector(block);","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mFixed length trace flag set in stream: IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=7076688, maxsize=Inf, ptr=3601, mark=-1)\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ SegyIO ~/.julia/dev/SegyIO/src/read/read_file.jl:36\u001b[39m","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"extent = [0, 10, 2, 0]\nfigure(figsize=(7, 7))\nsubplot(221)\nimshow(d_obs.data[1], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\nsubplot(222)\nimshow(d_obs.data[6], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\nsubplot(223)\nimshow(d_obs.data[11], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\nsubplot(224)\nimshow(d_obs.data[16], vmin=-1, vmax=1, cmap=\"PuOr\", extent=extent, aspect=4, interpolation=\"hamming\")\nxlabel(\"Receiver position(km)\")\nylabel(\"Time(s)\")\ntight_layout()\n","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Since the SEG-Y file contains the source coordinates, but not the wavelet itself, we create a JUDI Geometry structure for the source and then manually set up an 8 Hz Ricker wavelet. As for the observed data, we set up a JUDI seismic data vector q with the source geometry and wavelet:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"src_geometry = Geometry(block; key=\"source\");\nsrc_data = ricker_wavelet(src_geometry.t[1], src_geometry.dt[1], 0.008f0);\nq = judiVector(src_geometry, src_data);","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/#Optimization","page":"FWI with Quasi-Newton methods from the NLopt library","title":"Optimization","text":"","category":"section"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Rather than implementing the L-BFGS algorithms in Julia ourselves, we interface the NLopt optimization library. This library requires objective functions with the current variable and gradient as input arguments and the function value as the only output argument. For this reason, we build a wrapper that is customized for the NLopt library around our fwi_objective function. The function f! takes a vectorized estimate of the current model as well as the (vectorized) gradient as input arguments. NLopt uses double precision for floating point variables, so the first step inside f! is to reshape and convert the model to single precision. Then we choose a randomized subset of sources and shot records and compute the function value fval and gradient of the FWI objective function. We then set the gradient in the water layer to zero and overwrite the input gradient grad with the new gradient. Furthermore, we keep track of the number of function evaluations through increasing the count variable, which will serve as the termination criterion for the algorithm. In Julia, we set up f! in the following way: ","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"batchsize = 8;\ncount = 0;\n\n# NLopt objective function\nfunction objf!(x, grad)\n if count == 0\n @printf(\"%10s %15s %15s\\n\",\"Iteration\",\"Function Val\",\"norm(g)\")\n end\n # Update model\n model0.m .= Float32.(reshape(x, model0.n))\n\n # Seclect batch and calculate gradient\n i = randperm(d_obs.nsrc)[1:batchsize]\n fval, gradient = fwi_objective(model0, q[i], d_obs[i])\n\n # Reset gradient in water column to zero\n gradient = reshape(gradient, model0.n)\n gradient[:,1:21] .= 0f0\n if length(grad) > 0\n grad[1:end] = vec(gradient)\n end\n global count += 1\n @printf(\"%10d %15.5e %15.5e\\n\",count, fval, norm(g))\n return convert(Float64, fval)\nend","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"objf! (generic function with 1 method)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"g = zeros(prod(model0.n))\nf0 = objf!(vec(model0.m), g)\n# Reset count\nglobal count = 0;","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.n, use size(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m\n\n\n Iteration Function Val norm(g)\n 1 2.61794e+05 2.83025e+05","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"imshow(reshape(g, model0.n)', vmin=-1e3, vmax=1e3, extent=(0,10,3,0), cmap=\"jet\")\ntitle(\"FWI first gradient\")\nxlabel(\"Lateral position [km]\");\nylabel(\"Depth [km]\");","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"As in our gradient descent and Gauss-Newton example, we define bound constraints for the squared slowness to prevent velocities from becoming negative or too large:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"# Squared slowness\nmmax = (1.3f0).^(-2)\nmmin = (6.5f0).^(-2)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"0.023668641f0","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"The NLopt library offers a range of different optimization algorithms, from which we choose the L-BFGS method. We create an optimization object called opt by specifying the algorithm we want to use and the dimenions of the unknown model vector. We then set the upper and lower bounds of the variable, define f! as the objective function and set the termination criterion to be a maximum of 15 function evaluations:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"opt = Opt(:LD_LBFGS, prod(model0.n))\nopt.lower_bounds = mmin\nopt.upper_bounds = mmax\n# min_objective!(opt, f!)\nopt.min_objective = objf!\nopt.maxeval = 15","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"15","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"Remark: Subsampling the number of sources should in practice never be used for second order methods such as L-BFGS. Specialized stochastic second order methods exist, but differ from standard Quasi-Newton methods. We only use source subsampling to reduce the computational cost of our example. Having set up the objective function, bound constraints and termination criterion, we can now run the inversion:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"** This example requires ~200 MB of memory per gradient, i.e. 800 MB with four parallel workers. It runs for approximately 1 minutes. **","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"@time (minf, minx, ret) = optimize(opt, model0.m[:])","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":" Iteration Function Val norm(g)\n 1 2.46220e+05 2.83025e+05\n 2 2.36730e+05 2.83025e+05\n 3 1.67086e+05 2.83025e+05\n 4 1.21062e+05 2.83025e+05\n 5 9.66964e+04 2.83025e+05\n 6 7.97872e+04 2.83025e+05\n 7 6.54899e+04 2.83025e+05\n 8 5.23806e+04 2.83025e+05\n 9 4.46780e+04 2.83025e+05\n 10 4.05689e+04 2.83025e+05\n 11 3.14446e+04 2.83025e+05\n 12 3.06919e+04 2.83025e+05\n 13 2.52438e+04 2.83025e+05\n 14 2.52550e+04 2.83025e+05\n 15 2.24706e+04 2.83025e+05\n 65.695496 seconds (444.24 k allocations: 1.269 GiB, 0.10% gc time, 0.20% compilation time)\n\n\n\n\n\n(22470.578125, [0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136 … 0.05060886426348713, 0.05044609939336615, 0.050292761170062046, 0.05017357884927398, 0.05011064413011435, 0.05011993876926194, 0.05020965433880526, 0.05038047472521424, 0.05062740429803987, 0.05094217839179781], :MAXEVAL_REACHED)","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"We plot the final velocity model after 15 function evaluations:","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"imshow(sqrt.(1f0./reshape(minx, model0.n))', cmap=\"GnBu\", extent=(0,10,3,0), vmin=1.5, vmax=5.4); title(\"FWI with L-BFGS\")\nxlabel(\"Lateral position [km]\");\nylabel(\"Depth [km]\");","category":"page"},{"location":"tutorials/02_fwi_example_NLopt/","page":"FWI with Quasi-Newton methods from the NLopt library","title":"FWI with Quasi-Newton methods from the NLopt library","text":"(Image: png)","category":"page"},{"location":"about/#Authors","page":"About","title":"Authors","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"This package was written by Philipp Witte and Mathias Louboutin from the Seismic Laboratory for Imaging and Modeling (SLIM) at the Georgia Institute of Technology. People involved in the development of JUDI include:","category":"page"},{"location":"about/","page":"About","title":"About","text":"Philipp A. Witte^* (Now MSFT)\nMathias Louboutin (Georgia Institute of Technology)\nHenryk Modzelewski (The Univeristy of British Columbia)\nFelix J. Herrmann (Georgia Institute of Technology)","category":"page"},{"location":"about/","page":"About","title":"About","text":"And you can find the full list of collaborators on github at Contributors.","category":"page"},{"location":"about/#Cite-us","page":"About","title":"Cite us","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"If you use our software for your research, please cite our Geophysics paper:","category":"page"},{"location":"about/","page":"About","title":"About","text":"@article{witteJUDI2019,\nauthor = {Philipp A. Witte and Mathias Louboutin and Navjot Kukreja and Fabio Luporini and Michael Lange and Gerard J. Gorman and Felix J. Herrmann},\ntitle = {A large-scale framework for symbolic implementations of seismic inversion algorithms in Julia},\njournal = {GEOPHYSICS},\nvolume = {84},\nnumber = {3},\npages = {F57-F71},\nyear = {2019},\ndoi = {10.1190/geo2018-0174.1},\nURL = {https://doi.org/10.1190/geo2018-0174.1},\neprint = {https://doi.org/10.1190/geo2018-0174.1}\n}","category":"page"},{"location":"about/","page":"About","title":"About","text":"Also visit the Devito homepage at https://www.devitoproject.org/publications for more information and references. If you need to cite a specific version of JUDI, you can find our citeable archives on Zenodo.","category":"page"},{"location":"about/#Contribution-and-community","page":"About","title":"Contribution and community","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"We gladly welcome and encorage contributions from the community to improve our software and its usability. Feel free to:","category":"page"},{"location":"about/","page":"About","title":"About","text":"Open issues for bugs\nStart discussions to interat with the developper and ask any questions\nOpen PR for bug fixes and improvements","category":"page"},{"location":"about/#Field-examples","page":"About","title":"Field examples","text":"","category":"section"},{"location":"about/","page":"About","title":"About","text":"An example of using JUDI to invert field data is provided for the Viking Graben Line 12 which includes data preprocessing steps using Madagascar. ","category":"page"},{"location":"tutorials/05_custom_misfit/#FWI-with-user-provided-misfit-function","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In this notebook, we will introduce how the loss (data misifit) can be modifed to a user chosen function for seismic inversion. We will use one of JUDI's FWI example as a skeleton and illustrate the losses available in the package and how to input a custom data misfit function.","category":"page"},{"location":"tutorials/05_custom_misfit/#Single-source-loss","page":"FWI with user provided misfit function","title":"Single source loss","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In JUDI, the data misfit function is defined on a single shot record basis (single source experiment) and will be automatically reduced by julia's distribution for multiple sources. Therefore, the misfit function is extremly simple and should have the form:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"\nfunction misfit(dsyn, dobs)\n fval = ...\n adjoint_source = ...\n return fval, adjoint_source\nend\n","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Where fval is the misfit value for a given pair of synthetic dsyn and observed dobs shot records and adjoint_source is the residual to be backpropagated (adjoint wave-equation source) for the computation of the adjoint state gradient. For example the standard ell_2 loss (default misfit in JUDI) is defined as","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"\nfunction mse(dsyn, dobs)\n fval = .5f0 * norm(dsyn - dobs)^2\n adjoint_source = syn - dobs\n return fval, adjoint_source\nend\n","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With this convention defined, we will now perform FWI with a few misfit functions and highlight the flexibility of this interface, including the possibility to use Julia's automatic differentiation frameworks to compute the adjoint souce of misfit functions difficult to differentiate by hand.","category":"page"},{"location":"tutorials/05_custom_misfit/#Inversion-setup","page":"FWI with user provided misfit function","title":"Inversion setup","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"using SegyIO, HDF5, PyPlot, JUDI, Random, LinearAlgebra, Printf, SlimPlotting, SlimOptim, Statistics","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Define some utilities for plotting\nsx(d::judiVector) = d.geometry.xloc[1][1]","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"sx (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"We load the FWI starting model from the HDF5 model file and set up the JUDI model structure:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"m0, n, d, o = read(h5open(\"overthrust_model.h5\",\"r\"),\"m0\",\"n\",\"d\",\"o\"); title(\"Starting model\")\nmodel0 = Model((n[1],n[2]), (d[1],d[2]), (o[1],o[2]), m0)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Model (n=(401, 121), d=(25.0f0, 25.0f0), o=(0.0f0, 0.0f0)) with parameters [:m]","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"plot_velocity(m0'.^(-.5), d)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Then we read the SEG-Y file containing our test data set. The data was generated with a 2D excerpt from the Overthrust velocity model and consists of 31 shot records with 2 seconds recording time. We load the data and set up a JUDI seismic data vector:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"block = segy_read(\"overthrust_shot_records.segy\");\nd_obs = judiVector(block);","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"┌ Warning: Fixed length trace flag set in stream: IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=7076688, maxsize=Inf, ptr=3601, mark=-1)\n└ @ SegyIO /Users/mathiaslouboutin/.julia/packages/SegyIO/qkvUT/src/read/read_file.jl:26","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Since the SEG-Y file contains the source coordinates, but not the wavelet itself, we create a JUDI Geometry structure for the source and then manually set up an 8 Hz Ricker wavelet. As for the observed data, we set up a JUDI seismic data vector q with the source geometry and wavelet:","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"src_geometry = Geometry(block; key=\"source\");\nsrc_data = ricker_wavelet(src_geometry.t[1], src_geometry.dt[1], 0.008f0);\nq = judiVector(src_geometry, src_data);","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure(figsize=(7, 7))\nsuptitle(\"Observed data\")\nsubplot(221)\nplot_sdata(d_obs[1]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[1]))m\")\nsubplot(222)\nplot_sdata(d_obs[6]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[6]))m\")\nsubplot(223)\nplot_sdata(d_obs[11]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[11]))m\")\nsubplot(224)\nplot_sdata(d_obs[16]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[16]))m\")\ntight_layout()","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/#Misfit-functions","page":"FWI with user provided misfit function","title":"Misfit functions","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With the data and model defined, we can now define the misfit function we will be working with. For some of the considered functions, we will modify the observed data to highlight properties of a given misfit.","category":"page"},{"location":"tutorials/05_custom_misfit/#Predefined-misfit","page":"FWI with user provided misfit function","title":"Predefined misfit","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"JUDI provides two predefined misfits: the standard mse ell_2 loss and the studentst misfit that correspond to the Student's T Loss that has been shown to increase robustness against outliers in the data. The Student's T loss is defined as ","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"\nfunction studentst(dsyn, dobs; k=2)\n fval = sum(.5 * (k + 1) * log.(1 .+ (dsyn - dobs).^2 ./ k)\n adjoint_source = (k + 1) .* (dsyn - dobs) ./ (k .+ (dsyn - dobs).^2)\n return fval, adjoint_source\nend\n","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"where k is the number of degree of freedom (usually 1 or 2). We can see that in this case the adjoint source is pointwise normalized (in time and receiver position) which allows to mitigate outlier in the data such as incorrect amplitudes due to geophone mis-functionment. To illustrate this property, wee create an aritifical dataset with outliers to showcase the added robustness by artificially rescaling a few traces picked at random in every shot record of the dataset","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"d_outlier = deepcopy(d_obs)\n\n## Add outliers to the data\nfor s=1:d_outlier.nsrc\n # wrongly scale 10 traces\n nrec = d_outlier[s].geometry.nrec[1]\n inds = rand(1:nrec, 10)\n d_outlier.data[s][:, inds] .*= 20\nend","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure(figsize=(7, 7))\nsuptitle(\"Observed data with outliers\")\nsubplot(221)\nplot_sdata(d_outlier[1]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[1]))m\")\nsubplot(222)\nplot_sdata(d_outlier[6]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[6]))m\")\nsubplot(223)\nplot_sdata(d_outlier[11]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[11]))m\")\nsubplot(224)\nplot_sdata(d_outlier[16]; new_fig=false, cmap=\"PuOr\", name=\"sx=$(sx(q[16]))m\")\ntight_layout()","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/#Setup-inversion","page":"FWI with user provided misfit function","title":"Setup inversion","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With the data and misfit functions defined, we can now run the inversion. For this example, we will use SlimOptim's spectral projected gradient, a simple gradient base optimization method were we impose bound constraints on the model. For practical considerations, we will also work with a stochastic objective function that only computes the gradient on a random subset of shots at each iteration. This method has been show to be efficient for seismic inversion in particualr with advanced algorithms with constraints or on simpler illustrative examples such as this one. We will use 8 shots per iteration (50% of the dataset) and run 20 iterations of the algorithm.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"g_const = 0\nfunction objective_function(x, d_obs, misfit=mse)\n model0.m .= reshape(x,model0.n);\n\n # fwi function value and gradient\n i = randperm(d_obs.nsrc)[1:batchsize]\n fval, grad = fwi_objective(model0, q[i], d_obs[i]; misfit=misfit)\n # Normalize for nicer convergence\n g = grad.data\n # Mute water\n g[:, 1:20] .= 0f0\n g_const == 0 && (global g_const = 1/norm(g, Inf))\n return fval, g_const .* g\nend","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"objective_function (generic function with 2 methods)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Bound constraints based on initial velocity\nmmin = .95f0 * minimum(m0) * ones(Float32, n...)\nmmax = 1.1f0 * maximum(m0) * ones(Float32, n...)\n\n# Fix water layer\nmmin[:, 1:20] .= m0[1,1]\nmmax[:, 1:20] .= m0[1,1]\n\n# Bound projection\nproj(x) = reshape(median([vec(mmin) vec(x) vec(mmax)]; dims=2), model0.n)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"proj (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"batchsize = 8\nniter = 20\n\n# Setup SPG\noptions = spg_options(verbose=3, maxIter=niter, memory=3)\n\n# Compare l2 with students t on ideal data\nϕmse_ideal = x->objective_function(x, d_obs)\nϕst_ideal = x->objective_function(x, d_obs, studentst)\n\n# Compare l2 with students t on the data with outliers\nϕmse_out = x->objective_function(x, d_outlier)\nϕst_out = x->objective_function(x, d_outlier, studentst)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"#7 (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Perform the inversion\ng_const = 0\nsolmse_ideal = spg(ϕmse_ideal, m0, proj, options)\ng_const = 0\nsolst_ideal = spg(ϕst_ideal, m0, proj, options)\ng_const = 0\nsolmse_out = spg(ϕmse_out, m0, proj, options)\ng_const = 0\nsolst_out = spg(ϕst_out, m0, proj, options)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Running SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 2.66752e+05 4.24512e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 1.83654e+05 3.97694e-01\n 2 4 4 9 1.00000e+00 3.86448e-02 1.68483e+05 4.46593e-01\n 3 6 6 12 1.00000e+00 2.37916e-02 9.82918e+04 4.21356e-01\n 4 8 8 15 1.00000e+00 2.00663e-02 7.17603e+04 4.30578e-01\n 5 10 10 18 1.00000e+00 1.80122e-02 5.64357e+04 1.52451e-01\n 6 12 12 21 1.00000e+00 1.59804e-02 4.64412e+04 3.24789e-01\n 7 14 14 24 1.00000e+00 1.49305e-02 4.02634e+04 2.00507e-01\n 8 16 16 27 1.00000e+00 1.84277e-02 3.24419e+04 3.97697e-01\n 9 18 18 30 1.00000e+00 1.88020e-02 3.26324e+04 1.51282e-01\n 10 20 20 33 1.00000e+00 1.44802e-02 3.01499e+04 3.31139e-01\n 11 22 22 36 1.00000e+00 1.05926e-02 2.65702e+04 1.77554e-01\n 12 24 24 39 1.00000e+00 8.83888e-03 1.99885e+04 1.48811e-01\n 13 26 26 42 1.00000e+00 1.04110e-02 2.36893e+04 1.68137e-01\n 14 28 28 45 1.00000e+00 1.58912e-02 1.87623e+04 1.55777e-01\n 15 30 30 48 1.00000e+00 1.88643e-02 1.78986e+04 8.86404e-02\n 16 32 32 51 1.00000e+00 1.56725e-02 1.71706e+04 2.11131e-01\n 17 34 34 54 1.00000e+00 1.29173e-02 1.53461e+04 7.06666e-02\n 18 36 36 57 1.00000e+00 1.32459e-02 1.62717e+04 6.97212e-02\n 19 38 38 60 1.00000e+00 2.35474e-02 1.33874e+04 2.31529e-01\n 20 41 41 63 1.00000e-01 2.34768e-02 1.57433e+04 1.03599e-01\nRunning SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 2.65220e+05 4.33497e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 2.63573e+05 3.96120e-01\n 2 5 5 9 1.00000e-01 2.87833e-02 2.04122e+05 4.08234e-01\n 3 8 8 12 1.00000e-01 1.14303e-01 1.46186e+05 4.51432e-01\n 4 10 10 15 1.00000e+00 6.83298e-03 1.14790e+05 4.35991e-01\n 5 12 12 18 1.00000e+00 7.10424e-03 1.09469e+05 4.49633e-01\n 6 14 14 21 1.00000e+00 6.74102e-03 8.16012e+04 4.49592e-01\n 7 16 16 24 1.00000e+00 5.73175e-03 7.39588e+04 4.15352e-01\n 8 18 18 27 1.00000e+00 6.79955e-03 6.74752e+04 4.01064e-01\n 9 20 20 30 1.00000e+00 8.90642e-03 6.10524e+04 4.09543e-01\n 10 22 22 33 1.00000e+00 6.13604e-03 6.80915e+04 4.43890e-01\n 11 24 24 36 1.00000e+00 4.03840e-03 4.76625e+04 3.86592e-01\n 12 26 26 39 1.00000e+00 6.17972e-03 4.52775e+04 4.45348e-01\n 13 28 28 42 1.00000e+00 4.58222e-03 3.83993e+04 3.40792e-01\n 14 30 30 45 1.00000e+00 4.33826e-03 3.45115e+04 3.76462e-01\n 15 32 32 48 1.00000e+00 7.44435e-03 3.33236e+04 4.13865e-01\n 16 34 34 51 1.00000e+00 8.23616e-03 3.66736e+04 4.48623e-01\n 17 36 36 54 1.00000e+00 3.59586e-03 3.03236e+04 3.60418e-01\n 18 38 38 57 1.00000e+00 2.77100e-03 2.72704e+04 3.36501e-01\n 19 40 40 60 1.00000e+00 3.27021e-03 2.44597e+04 3.00182e-01\n 20 42 42 63 1.00000e+00 5.67399e-03 2.58941e+04 2.96022e-01\nRunning SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 1.44941e+08 3.99644e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 1.43838e+08 3.93633e-01\n 2 4 4 9 1.00000e+00 1.16882e-01 1.20535e+08 3.92942e-01\n 3 6 6 12 1.00000e+00 1.56330e-01 1.00449e+08 3.48297e-01\n 4 8 8 15 1.00000e+00 3.67119e-01 7.55863e+07 3.48005e-01\n 5 10 10 18 1.00000e+00 3.19012e-01 1.08780e+08 3.90391e-01\n 6 13 13 21 1.00000e-01 3.74207e-01 9.95976e+07 4.11056e-01\n 7 15 15 24 1.00000e+00 1.00220e-01 8.43423e+07 3.17958e-01\n 8 24 24 27 1.00000e-07 1.14440e-01 1.01919e+08 3.01010e-01\n 9 26 26 30 1.00000e+00 1.56984e-08 8.62008e+07 3.17958e-01\n 10 36 36 33 1.00000e-08 3.33508e-08 8.25621e+07 3.50130e-01\nStep size: 2.98e-16 below progTol: 1.00e-10\nRunning SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 5.46581e+05 4.47720e-01\n 1 9 9 6 1.00000e-07 4.44444e-02 5.44028e+05 4.34094e-01\n 2 12 12 9 1.00000e-01 9.44714e-08 5.32362e+05 4.26830e-01\n 3 14 14 12 1.00000e+00 2.12196e-08 5.42095e+05 4.26773e-01\n 4 16 16 15 1.00000e+00 1.94585e-07 5.42074e+05 4.32777e-01\n 5 21 21 18 1.00000e-03 6.28857e-07 5.29497e+05 4.29520e-01\n 6 25 25 21 1.00000e-02 1.00000e+00 5.06206e+05 4.23433e-01\n 7 27 27 24 1.00000e+00 1.54459e-02 4.13344e+05 4.08260e-01\n 8 29 29 27 1.00000e+00 2.72312e-02 4.15654e+05 4.51937e-01\n 9 31 31 30 1.00000e+00 1.83427e-02 5.03455e+05 4.41503e-01\n 10 40 40 33 1.00000e-07 1.39864e-02 4.97702e+05 3.91456e-01\n 11 48 48 36 1.00000e-06 9.34530e-09 4.96147e+05 4.39331e-01\nStep size: 1.49e-14 below progTol: 1.00e-10\n\n\n\n\n\nresult{Float32}(Float32[0.44444445 0.44444445 … 0.055672385 0.054975696; 0.44444445 0.44444445 … 0.05563018 0.05495146; … ; 0.44444445 0.44444445 … 0.05432263 0.05296257; 0.44444445 0.44444445 … 0.054288656 0.05309218], Float32[0.0 0.0 … 0.03932687 0.051932618; 0.0 0.0 … 0.03822797 0.05325304; … ; 0.0 0.0 … 0.006757668 0.0029647883; 0.0 0.0 … 0.0061980807 0.0022367856], 413343.9f0, Float32[546580.56, 544027.9, 532361.8, 542095.3, 542073.7, 529497.06, 506206.3, 413343.9, 415653.62, 503455.06, 497702.1, 496147.38], Matrix{Float32}[[0.44444445 0.44444445 … 0.05585606 0.05512348; 0.44444445 0.44444445 … 0.05584115 0.055107832; … ; 0.44444445 0.44444445 … 0.055858355 0.05516676; 0.44444445 0.44444445 … 0.05585274 0.05516145]], 36, 48, 48)","category":"page"},{"location":"tutorials/05_custom_misfit/#Results","page":"FWI with user provided misfit function","title":"Results","text":"","category":"section"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"We can now look at the inversion results for these two datasets and two misfit functions. We show all four results below. We can see that in the case of ideal data, we invert for the velocity fairly easily in both cases as expected and obtain an accurate reconstruction.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"We obsereved on the second row that, as expected from the litterature, the student's t misift provides a very robust inversion in the presence of outlier and that the inverted velocity is as good as with ideal data. On the other hand, the standard mse misfits fails to lead to an acceptable inverted velocity in the presence of outliers as these outliers will lead to the main contribution to the gradient. These artifacts, once present in the velocity model after the few first iteration, cannot be recovered from.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure(figsize=(14, 7))\nsuptitle(\"FWI result\")\nsubplot(221)\nplot_velocity(reshape(solmse_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Ideal data, mse\")\nsubplot(222)\nplot_velocity(reshape(solst_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Ideal data, student's t\")\nsubplot(223)\nplot_velocity(reshape(solmse_out.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Data with outliers, mse\")\nsubplot(224)\nplot_velocity(reshape(solst_out.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Data with outliers, student's t\")\ntight_layout()","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"figure()\nplot(solmse_ideal.ϕ_trace ./ solmse_ideal.ϕ_trace[1], label=\"Ideal mse\")\nplot(solst_ideal.ϕ_trace ./ solst_ideal.ϕ_trace[1], label=\"Ideal student's t\")\nplot(solmse_out.ϕ_trace ./ solmse_out.ϕ_trace[1], label=\"Outlier mse\")\nplot(solst_out.ϕ_trace ./ solst_out.ϕ_trace[1], label=\"Outlier student's t\")\nxlabel(\"iteration\")\nylabel(\"Normalized misfit\")\ntitle(\"Convergence\")","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"PyObject Text(0.5, 1.0, 'Convergence')","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"## Custom misfits","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Finally, we now introduce how to use custom misfit for inversion. As illustrated above, the misfit is a simple keyword argument to the fwi_objective/lsrtm_objective function and consequently, any function returning the misfit value and adjoint source can be provided. In particular, users can take advantage of the extensive automatic differentiation ecosystem in Julia to define complicated misfit functions and automatically derive the corresponding adjoint source. ","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In the following, as an illustrative example, we define the misfit for Envelope FWI and let Zygote computes its derivative with respect to the synthetic data. We add a little bit of complexity to the misfit function by normalizing the data by its ell_2 norm as well to demonstrate how we can layer complexities in the misfit function and let the automatic differentiation infer the adjoint source.","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"using FFTW, Zygote\n\nn2(x) = x / norm(x, 2)\n\nfunction Hilbert(x)\n n = size(x, 1)\n σ = sign.(-n/2+1:n/2)\n y = imag(ifft(fftshift(σ.*fftshift(fft(x, 1), 1), 1), 1))\n return y\nend\n\nHLoss(dsyn, dobs) = sum(abs2.((dsyn - dobs) .+ 1im .* Hilbert(dsyn - dobs)))\n\nfunction envelope(dsyn, dobs)\n ϕ = HLoss(n2(dsyn), n2(dobs))\n g = gradient(xs -> HLoss(n2(xs), n2(dobs)), dsyn)\n # Zygote always returns a tuple, in this case (g,)\n return ϕ, real.(g[1])\nend","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"┌ Info: Precompiling Zygote [e88e6eb3-aa80-5325-afca-941959d7151f]\n└ @ Base loading.jl:1662\n\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mModule DiffRules with build ID 773862253559751 is missing from the cache.\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39mThis may mean DiffRules [b552c78f-8df3-52c6-915a-8e097449b14b] does not support precompilation but is imported by a module that does.\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Base loading.jl:1325\u001b[39m\n┌ Info: Skipping precompilation since __precompile__(false). Importing Zygote [e88e6eb3-aa80-5325-afca-941959d7151f].\n└ @ Base loading.jl:1341\n\n\n\n\n\nenvelope (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"With our loss defined we can now rerun FWI with it","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"# Compare l2 with students t on ideal data\nϕh_ideal = x->objective_function(x, d_obs, envelope)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"#11 (generic function with 1 method)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"g_const = 0\nsolh_ideal = spg(ϕh_ideal, m0, proj, options)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"Running SPG...\nNumber of objective function to store: 3\nUsing spectral projection : true\nMaximum number of iterations: 20\nSPG optimality tolerance: 1.00e-10\nSPG progress tolerance: 1.00e-10\nLine search: BackTracking{Float32, Int64}\n Iteration FunEvals GradEvals Projections Step Length alpha Function Val Opt Cond\n 0 0 0 0 0.00000e+00 0.00000e+00 9.89023e-01 4.26088e-01\n 1 2 2 6 1.00000e+00 4.44444e-02 5.96211e-01 3.97723e-01\n 2 4 4 9 1.00000e+00 3.22405e-02 5.85686e-01 4.51937e-01\n 3 6 6 12 1.00000e+00 2.09030e-02 3.81984e-01 4.51005e-01\n 4 8 8 15 1.00000e+00 1.60157e-02 2.25568e-01 3.97775e-01\n 5 10 10 18 1.00000e+00 1.59596e-02 1.75072e-01 3.59442e-01\n 6 12 12 21 1.00000e+00 2.15618e-02 1.56823e-01 2.05124e-01\n 7 14 14 24 1.00000e+00 2.46165e-02 1.42123e-01 3.35674e-01\n 8 16 16 27 1.00000e+00 2.11885e-02 1.17001e-01 1.94726e-01\n 9 18 18 30 1.00000e+00 1.20896e-02 1.11277e-01 4.14274e-01\n 10 20 20 33 1.00000e+00 7.33151e-03 9.26926e-02 2.81033e-01\n 11 22 22 36 1.00000e+00 6.84483e-03 7.99434e-02 3.38496e-01\n 12 24 24 39 1.00000e+00 9.87291e-03 7.37838e-02 2.56689e-01\n 13 26 26 42 1.00000e+00 1.70487e-02 7.32610e-02 9.64325e-02\n 14 28 28 45 1.00000e+00 1.53908e-02 7.61458e-02 2.71101e-01\n 15 30 30 48 1.00000e+00 1.21645e-02 5.53087e-02 1.33696e-01\n 16 32 32 51 1.00000e+00 9.74719e-03 5.92018e-02 1.71851e-01\n 17 34 34 54 1.00000e+00 8.90081e-03 5.74107e-02 9.27853e-02\n 18 36 36 57 1.00000e+00 7.97771e-03 4.89929e-02 1.21926e-01\n 19 38 38 60 1.00000e+00 8.40343e-03 5.05762e-02 8.61898e-02\n 20 40 40 63 1.00000e+00 9.53746e-03 4.93289e-02 1.69757e-01\n\n\n\n\n\nresult{Float32}(Float32[0.44444445 0.44444445 … 0.053637963 0.052456174; 0.44444445 0.44444445 … 0.053673286 0.052389987; … ; 0.44444445 0.44444445 … 0.05293595 0.05135409; 0.44444445 0.44444445 … 0.052959688 0.051626943], Float32[0.0 0.0 … 0.0014253161 0.0031370732; 0.0 0.0 … 0.0007196072 0.0023543844; … ; 0.0 0.0 … 0.014447195 0.01871568; 0.0 0.0 … 0.013791027 0.017101452], 0.04899293f0, Float32[0.98902273, 0.5962111, 0.5856857, 0.38198355, 0.22556786, 0.17507246, 0.15682286, 0.14212339, 0.11700058, 0.111277334 … 0.079943426, 0.073783785, 0.073261, 0.07614578, 0.05530872, 0.059201777, 0.057410687, 0.04899293, 0.050576232, 0.04932886], Matrix{Float32}[[0.44444445 0.44444445 … 0.05585606 0.05512348; 0.44444445 0.44444445 … 0.05584115 0.055107832; … ; 0.44444445 0.44444445 … 0.055858355 0.05516676; 0.44444445 0.44444445 … 0.05585274 0.05516145]], 63, 40, 40)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"suptitle(\"FWI result\")\nplot_velocity(reshape(solh_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name=\"Ideal data, Envelope\")","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"(Image: png)","category":"page"},{"location":"tutorials/05_custom_misfit/","page":"FWI with user provided misfit function","title":"FWI with user provided misfit function","text":"In this tutorial, we have seen how misfit functions can impact the inversion result and how JUDI provides multiple misfit functions and a flexible interface for custom user inputs. This interface will allow for better application to real worl dataset since, as show in our example suite, JUDI already provide an interface for handling large SegY datasets and interfaces tricially with optimization frameworks","category":"page"},{"location":"helper/#Helper-functions","page":"Helper Functions","title":"Helper functions","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"JUDI provides numerous helper and utility functions need for seismic modeling and inversion.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Pages = [\"helper.md\"]","category":"page"},{"location":"helper/#Ricker-wavelet","page":"Helper Functions","title":"Ricker wavelet","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Create a 1D Ricker wavelet:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"ricker_wavelet(tmax, dt, f0; t0=nothing)","category":"page"},{"location":"helper/#Compute-CFL-time-stepping-interval","page":"Helper Functions","title":"Compute CFL time stepping interval","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Calculate the time stepping interval based on the CFL condition","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"calculate_dt","category":"page"},{"location":"helper/#Compute-number-of-computational-time-steps","page":"Helper Functions","title":"Compute number of computational time steps","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Estimate the number of computational time steps. Required for calculating the dimensions of the matrix-free linear modeling operators:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"get_computational_nt","category":"page"},{"location":"helper/#JUDI.get_computational_nt","page":"Helper Functions","title":"JUDI.get_computational_nt","text":"get_computational_nt(srcGeometry, recGeoemtry, model; dt=nothing)\n\nEstimate the number of computational time steps. Required for calculating the dimensions\nof the matrix-free linear modeling operators. srcGeometry and recGeometry are source\nand receiver geometries of type Geometry and model is the model structure of type \nModel.\n\n\n\n\n\nget_computational_nt(Geoemtry, model; dt=nothing)\n\nEstimate the number of computational time steps. Required for calculating the dimensions\nof the matrix-free linear modeling operators. srcGeometry and recGeometry are source\nand receiver geometries of type Geometry and model is the model structure of type \nModel.\n\n\n\n\n\n","category":"function"},{"location":"helper/#Set-up-3D-acquisition-grid","page":"Helper Functions","title":"Set up 3D acquisition grid","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Helper function to create a regular acquisition grid for a 3D survey.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"setup_3D_grid","category":"page"},{"location":"helper/#Data-interpolation","page":"Helper Functions","title":"Data interpolation","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Time interpolation for source/receiver data using splines. For modeling, the data is interpolated automatically onto the computational time axis, so generally, these functions are not needed for users.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"time_resample","category":"page"},{"location":"helper/#JUDI.time_resample","page":"Helper Functions","title":"JUDI.time_resample","text":"time_resample(data, geometry_in, dt_new)\n\nResample the input data with sinc interpolation from the current time sampling (geometrtyin) to the new time sampling `dtnew`.\n\nParameters\n\ndata: Data to be reampled. If data is a matrix, resamples each column.\ngeometry_in: Geometry on which data is defined.\ndt_new: New time sampling rate to interpolate onto.\n\n\n\n\n\ntime_resample(data, dt_in, dt_new)\n\nResample the input data with sinc interpolation from the current time sampling dtin to the new time sampling `dtnew`.\n\nParameters\n\ndata: Data to be reampled. If data is a matrix, resamples each column.\ndt_in: Time sampling of input\ndt_new: New time sampling rate to interpolate onto.\n\n\n\n\n\ntime_resample(data, dt_in, geometry_in)\n\nResample the input data with sinc interpolation from the current time sampling (dtin) to the new time sampling `geometryout`.\n\nParameters\n\ndata: Data to be reampled. If data is a matrix, resamples each column.\ngeometry_out: Geometry on which data is to be interpolated.\ndt_in: Time sampling rate of the data.\n\n\n\n\n\n","category":"function"},{"location":"helper/#Generate-and-sample-from-frequency-distribution","page":"Helper Functions","title":"Generate and sample from frequency distribution","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Create a probability distribution with the shape of the source spectrum from which we can draw random frequencies.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"generate_distribution\nselect_frequencies","category":"page"},{"location":"helper/#JUDI.generate_distribution","page":"Helper Functions","title":"JUDI.generate_distribution","text":"generate_distribution(x; src_no=1)\n\nGenerates a probability distribution for the discrete input judiVector x.\n\nParameters\n\nx: judiVector. Usualy a source with a single trace per source position.\nsrc_no: Index of the source to select out of x\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.select_frequencies","page":"Helper Functions","title":"JUDI.select_frequencies","text":"select_frequencies(q_dist; fmin=0f0, fmax=Inf, nf=1)\n\nSelects nf frequencies based on the source distribution q_dist computed with generate_distribution.\n\nParameters\n\nq_dist: Distribution to sample from.\nf_min: Minimum acceptable frequency to sample (defaults to 0).\nf_max: Maximum acceptable frequency to sample (defaults to Inf).\nfd: Number of frequnecies to sample (defaults to 1).\n\n\n\n\n\n","category":"function"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"We can draw random samples from dist by passing it values between 0 and 1:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"# Draw a single random frequency\nf = dist(rand(1))\n\n# Draw 10 random frequencies\nf = dist(rand(10))","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"Alternatively, we can use the function:","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"f = select_frequencies(dist; fmin=0f0, fmax=Inf, nf=1)","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"to draw nf number of frequencies for a given distribution dist in the frequency range of fmin to fmax (both in kHz).","category":"page"},{"location":"helper/#Read-data-from-out-of-core-container","page":"Helper Functions","title":"Read data from out of core container","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"In the case where a judiVector is out of core (points to a segy file) it is possible to convert it or part of it into an in core judiVecor with the get_data function.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"d_ic = get_data(d_ooc, inds)","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"where inds is either a single index, a list of index or a range of index.","category":"page"},{"location":"helper/#Restrict-model-to-acquisition","page":"Helper Functions","title":"Restrict model to acquisition","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"In practice, and in particular for marine data, the aperture of a single shot is much smaller than the full model size. We provide a function (automatically used when the option limit_m is set in Options) that limits the model to the acquisition area.","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"limit_model_to_receiver_area","category":"page"},{"location":"helper/#JUDI.limit_model_to_receiver_area","page":"Helper Functions","title":"JUDI.limit_model_to_receiver_area","text":"limit_model_to_receiver_area(srcGeometry, recGeometry, model, buffer; pert=nothing)\n\nCrops the model to the area of the source an receiver with an extra buffer. This reduces the size of the problem when the model si large and the source and receiver located in a small part of the domain.\n\nIn the cartoon below, the full model will be cropped to the center area containg the source (o) receivers (x) and buffer area (*)\n\no Source position\nx receiver positions\nExtra buffer (grid spacing in that simple case)\n\n\n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n| . . . . * * * * * * * * * * * * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x o x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * x x x x x x x x x x * . . . . . | \n\n| . . . . * * * * * * * * * * * * . . . . . | \n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n| . . . . . . . . . . . . . . . . . . . . . | \n\n\n\nParameters\n\nsrcGeometry: Geometry of the source.\nrecGeometry: Geometry of the receivers.\nmodel: Model to be croped.\nbuffer: Size of the buffer on each side.\npert: Model perturbation (optional) to be cropped as well.\n\n\n\n\n\n","category":"function"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"We also provide it's complement that removes receivers outside of the computational domain if the dataset contains locations that are not wanted","category":"page"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"remove_out_of_bounds_receivers","category":"page"},{"location":"helper/#JUDI.remove_out_of_bounds_receivers","page":"Helper Functions","title":"JUDI.remove_out_of_bounds_receivers","text":"remove_out_of_bounds_receivers(recGeometry, model)\n\nRemoves receivers that are positionned outside the computational domain defined by the model.\n\nParameters\n\nrecGeometry: Geometry of receivers in which out of bounds will be removed.\nmodel: Model defining the computational domain.\n\n\n\n\n\nremove_out_of_bounds_receivers(recGeometry, recData, model)\n\nRemoves receivers that are positionned outside the computational domain defined by the model.\n\nParameters\n\nrecGeometry: Geometry of receivers in which out of bounds will be removed.\nrecData: Shot record for that geometry in which traces will be removed.\nmodel: Model defining the computational domain.\n\n\n\n\n\n","category":"function"},{"location":"helper/#Additional-miscellanous-utilities","page":"Helper Functions","title":"Additional miscellanous utilities","text":"","category":"section"},{"location":"helper/","page":"Helper Functions","title":"Helper Functions","text":"devito_model\nsetup_grid\npad_sizes\npad_array\nremove_padding\nconvertToCell\nprocess_input_data\nreshape\ntransducer\nas_vec","category":"page"},{"location":"helper/#JUDI.devito_model","page":"Helper Functions","title":"JUDI.devito_model","text":"devito_model(model, options;dm=nothing)\n\nCreates a python side model strucutre for devito.\n\nParameters\n\nmodel: JUDI Model structure.\noptions: JUDI Options structure.\ndm: Squared slowness perturbation (optional), Array or PhysicalParameter.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.setup_grid","page":"Helper Functions","title":"JUDI.setup_grid","text":"setup_grid(geometry, n)\n\nSets up the coordinate arrays for Devito.\n\nParameters:\n\ngeometry: Geometry containing the coordinates\nn: Domain size\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.pad_sizes","page":"Helper Functions","title":"JUDI.pad_sizes","text":"pad_sizes(model, options; so=nothing)\n\nComputes ABC padding sizes according to the model's numbr of abc points and spatial order\n\nParameters\n\nmodel: JUDI or Python side Model.\noptions: JUDI Options structure.\nso: Space order (optional) defaults to options.space_order.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.pad_array","page":"Helper Functions","title":"JUDI.pad_array","text":"pad_array(m, nb; mode=:border)\n\nPads to the input array with either copying the edge value (:border) or zeros (:zeros)\n\nParameters\n\nm: Array to be padded.\nnb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.\nmode: Padding mode (optional), defaults to :border.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.remove_padding","page":"Helper Functions","title":"JUDI.remove_padding","text":"remove_padding(m, nb; true_adjoint=False)\n\nRemoves the padding from array m. This is the adjoint of pad_array.\n\nParameters\n\nm: Array to remvove padding from.\nnb: Size of padding. Array of tuple with one (nbleft, nbright) tuple per dimension.\ntrue_adjoint: Unpadding mode, defaults to False. Will sum the padding to the edge point with true_adjoint=true\n\nand should only be used this way for adjoint testing purpose.\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.convertToCell","page":"Helper Functions","title":"JUDI.convertToCell","text":"convertToCell(x)\n\nConvert an array x to a cell array (Array{Any,1}) with length(x) entries,\nwhere the i-th cell contains the i-th entry of x.\n\nParameters\n\nx: Array to be converted into and array of array\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.process_input_data","page":"Helper Functions","title":"JUDI.process_input_data","text":"process_input_data(input, geometry, nsrc)\n\nPreprocesses input Array into an Array of Array for modeling\n\nParameters:\n\ninput: Input to preprocess.\ngeometry: Geometry containing physical parameters.\nnsrc: Number of sources\n\n\n\n\n\nprocess_input_data(input, model, nsrc)\n\nPreprocesses input Array into an Array of Array for modeling\n\nParameters:\n\ninput: Input to preprocess.\nmodel: Model containing physical parameters.\nnsrc: Number of sources\n\n\n\n\n\n","category":"function"},{"location":"helper/#Base.reshape","page":"Helper Functions","title":"Base.reshape","text":"reshape(A, dims...) -> AbstractArray\nreshape(A, dims) -> AbstractArray\n\nReturn an array with the same data as A, but with different dimension sizes or number of dimensions. The two arrays share the same underlying data, so that the result is mutable if and only if A is mutable, and setting elements of one alters the values of the other.\n\nThe new dimensions may be specified either as a list of arguments or as a shape tuple. At most one dimension may be specified with a :, in which case its length is computed such that its product with all the specified dimensions is equal to the length of the original array A. The total number of elements must not change.\n\nExamples\n\njulia> A = Vector(1:16)\n16-element Vector{Int64}:\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n 10\n 11\n 12\n 13\n 14\n 15\n 16\n\njulia> reshape(A, (4, 4))\n4×4 Matrix{Int64}:\n 1 5 9 13\n 2 6 10 14\n 3 7 11 15\n 4 8 12 16\n\njulia> reshape(A, 2, :)\n2×8 Matrix{Int64}:\n 1 3 5 7 9 11 13 15\n 2 4 6 8 10 12 14 16\n\njulia> reshape(1:6, 2, 3)\n2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64:\n 1 3 5\n 2 4 6\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.transducer","page":"Helper Functions","title":"JUDI.transducer","text":"transducer(q, d, r, theta)\n\nCreate the JUDI soure for a circular transducer Theta=0 points downward:\n\n. . . . - - - . . . . . .\n\n. . . . + + + . . . . . .\n\n. . . . . . . . . . . . .\n\n. . . . . . . . . . . . .\n\nTheta=pi/2 points right:\n\n. . . . - + . . . . . . .\n\n. . . . - + . . . . . . .\n\n. . . . - + . . . . . . .\n\n. . . . . . . . . . . . .\n\n2D only, to extend to 3D\n\n\n\n\n\n","category":"function"},{"location":"helper/#JUDI.as_vec","page":"Helper Functions","title":"JUDI.as_vec","text":"as_vec(x, ::Val{Bool})\n\nVectorizes output when return_array is set to true.\n\n\n\n\n\n","category":"function"},{"location":"tutorials/imaging_conditions/#Imaging-conditions-in-JUDI","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"title: Overview of JUDI imaging conditions for inversion author: Mathias Louboutin date: October 2022 –-","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"This example script is written using Weave.jl and can be converted to different format for documentation and usage This example is converted to a markdown file for the documentation.","category":"page"},{"location":"tutorials/imaging_conditions/#Import-JUDI,-Linear-algebra-utilities-and-Plotting","page":"Imaging conditions in JUDI","title":"Import JUDI, Linear algebra utilities and Plotting","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"using JUDI, LinearAlgebra, PyPlot","category":"page"},{"location":"tutorials/imaging_conditions/#Create-a-JUDI-model-structure","page":"Imaging conditions in JUDI","title":"Create a JUDI model structure","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"In JUDI, a Model structure contains the grid information (origin, spacing, number of gridpoints) and the physical parameters. The squared slowness is always required as the base physical parameter for propagation. In addition, JUDI supports additional physical representations. First we accept density that can either be a direct input Model(n, d, o, m, rho) or an optional keyword argument Model(n,d,o,m;rho=rho). Second, we also provide VTI/TTI kernels parametrized by the THomsen parameters that can be input as keyword arguments Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta,theta=theta,phi=phi). Because the thomsen parameters are optional the propagator wil lonloy use the ones provided. For example Model(n,d,o,m; rho=rho, epsilon=epsilon;delta=delta) will infer a VTI propagation","category":"page"},{"location":"tutorials/imaging_conditions/#Create-discrete-parameters","page":"Imaging conditions in JUDI","title":"Create discrete parameters","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Set up model structure\nn = (601, 151) # (x,y,z) or (x,z)\nd = (10f0, 10f0)\no = (0., 0.)\n\n# Velocity [km/s]\nv = ones(Float32,n) .+ 0.5f0\nv0 = ones(Float32,n) .+ 0.51f0\nv[:, 101:end] .= 2f0\nv0[:, 101:end] .= 2f0\n\n# Slowness squared [s^2/km^2]\nm = (1f0 ./ v).^2\nm0 = (1f0 ./ v0).^2\n\n# Setup model structure\nnsrc = 1\t# number of sources\nmodel = Model(n, d, o, m)\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"Model (n=(601, 151), d=(10.0f0, 10.0f0), o=(0.0f0, 0.0f0)) with parameters \n(:m, :rho)","category":"page"},{"location":"tutorials/imaging_conditions/#Create-acquisition-geometry","page":"Imaging conditions in JUDI","title":"Create acquisition geometry","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"In this simple usage example, we create a simple acquisiton by hand. In practice the acquisition geometry will be defined by the dataset beeing inverted. We show in a spearate tutorial how to use SegyIO.jl to handle SEGY seismic datasets in JUDI.","category":"page"},{"location":"tutorials/imaging_conditions/#Create-source-and-receivers-positions-at-the-surface","page":"Imaging conditions in JUDI","title":"Create source and receivers positions at the surface","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Set up receiver geometry\nxrec = [1 * (n[1] - 1) * d[1] / 4]\nyrec = [0f0] # WE have to set the y coordiante to zero (or any number) for 2D modeling\nzrec = [d[1]]\n\n# receiver sampling and recording time\ntimeD = 2500f0 # receiver recording time [ms]\ndtD = 4f0 # receiver sampling interval [ms]\n\n# Set up receiver structure\nrecGeometry = Geometry(xrec, yrec, zrec; dt=dtD, t=timeD, nsrc=nsrc)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"GeometryIC{Float32} wiht 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"The source geometry is a but different. Because we want to create a survey with nsrc shot records, we need to convert the vector of sources postions [s0, s1, ... sn] into an array of array [[s0], [s1], ...] so that JUDI understands that this is a set of indepednet nsrc","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"xsrc = 3 * (n[1] - 1) * d[1] / 4\nysrc = 0f0\nzsrc = d[1]\n\n# Set up source structure\nsrcGeometry = Geometry(xsrc, ysrc, zsrc; dt=dtD, t=timeD)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"GeometryIC{Float32} wiht 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/#Source-judiVector","page":"Imaging conditions in JUDI","title":"Source judiVector","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"Finally, with the geometry defined, we can create a source wavelet (a simple Ricker wavelet here) a our first judiVector In JUDI, a judiVector is the core structure that represent a acquisition-geometry based dataset. This structure encapsulate the physical locations (trace coordinates) and corrsponding data trace in a source-based structure. for a given judiVector d then d[1] will be the shot record for the first source, or in the case of the source term, the first source wavelet and its positon.","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# setup wavelet\nf0 = 0.015f0 # kHz\nwavelet = ricker_wavelet(timeD, dtD, f0)\nq = judiVector(srcGeometry, wavelet)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"judiVector{Float32, Matrix{Float32}} with 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/#Modeling","page":"Imaging conditions in JUDI","title":"Modeling","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"With our survey and subsurface model setup, we can now model and image seismic data. Linear Operators The core idea behind JUDI is to abstract seismic inverse problems in term of linear algebra. In its simplest form, seismic inversion can be formulated as","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"undersetmathbfmtextargmin phi(mathbfm) = frac12 mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd _2^2 \ntext \nnabla_mathbfm phi(mathbfm) = mathbfJ(mathbfm mathbfq)^top (mathbfP_r mathbfF(mathbfm) mathbfP_s^top mathbfq - mathbfd)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"where mathbfP_r is the receiver projection (measurment operator) and mathbfP_s^top is the source injection operator (adjoint of measurment at the source location). Therefore, we bastracted these operation to be able to define these operators","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Setup operators\nF = judiModeling(model, srcGeometry, recGeometry)\nF0 = judiModeling(model0, srcGeometry, recGeometry)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"JUDI forward{Float32} propagator (src * rec * time) -> (src * rec * time)","category":"page"},{"location":"tutorials/imaging_conditions/#Model-and-image-data","page":"Imaging conditions in JUDI","title":"Model and image data","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"We first model synthetic data using our defined source and true model ","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"# Nonlinear modeling\ndobs = F*q","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"judiVector{Float32, Matrix{Float32}} with 1 sources","category":"page"},{"location":"tutorials/imaging_conditions/#Inversion","page":"Imaging conditions in JUDI","title":"Inversion","text":"","category":"section"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"Our main goal is to provide an inversion framework for seismic inversion. In this tutorial, we highlight the different imaging conditions available in JUDI. These imaging conditions are designed to enhanced properties in the gradient beneficial to the inversion problem such as the frequency content. Because these imaging conditions are intended tho be used in potential least-square problems, we also implemented (and test as part of our CI) their adjoint such that we can model linearized data with the JAcobian that correspond to the adjoint of the modified adjoint state crosscorelation imaging condition.","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"We compute now the FWI gradient with three different imaging conditions:","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"\"as\" adjoint state imaging condition. This is the conventional cross-correlation adjoint state gradient.\n\"isic\" that computes the inverse scattering imaging condition designed to provide reflections for LSRTM (high frequency content)\n\"FWI\" that computes the complement of \"isic\" and brings up the low frequency content for velocity inversion","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"These can be specified in the Option structure via IC=\"as/isic/fwi","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"fas, g_as = fwi_objective(model0, q, 0*dobs; options=Options(IC=\"as\", space_order=12))\nfisic, g_isic = fwi_objective(model0, q, 0*dobs; options=Options(IC=\"isic\", space_order=12))\nffwi, g_fwi = fwi_objective(model0, q, 0*dobs; options=Options(IC=\"fwi\", space_order=12))","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"(32.991665f0, PhysicalParameter{Float32, 2} of size (601, 151) with origin \n(0.0f0, 0.0f0) and spacing (10.0f0, 10.0f0))","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"We show below the sensitivity kernels for a single source-receiver pair highlighting the inversion properties of these imaging conditions","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"ni(x) = 10 * x ./ norm(x, Inf)\n\nfig = figure(figsize=(8, 12))\nsubplot(311)\nimshow(ni(g_isic'), cmap=\"seismic\", aspect=\"auto\", vmin=-1, vmax=1)\ntitle(\"ISIC\")\nsubplot(312)\nimshow(ni(g_fwi'), cmap=\"seismic\", aspect=\"auto\", vmin=-1, vmax=1)\ntitle(\"FWI\")\nsubplot(313)\nimshow(ni(g_as'), cmap=\"seismic\", aspect=\"auto\", vmin=-1, vmax=1)\ntitle(\"Adjoint State\")\ntight_layout()\ndisplay(fig)","category":"page"},{"location":"tutorials/imaging_conditions/","page":"Imaging conditions in JUDI","title":"Imaging conditions in JUDI","text":"(Image: )","category":"page"},{"location":"preconditioners/#Seismic-Preconditioners","page":"Preconditioners","title":"Seismic Preconditioners","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"JUDI provides a selected number of preconditioners known to be beneficial to FWI and RTM. We welcome additional preconditionners from the community. Additionnaly, any JOLI operator can be used as a preconditiner in conbination with JUDI operator thanks to the fundamental interface between JUDI and JOLI.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Pages = [\"preconditioners.md\"]","category":"page"},{"location":"preconditioners/#Model-domain-preconditioners","page":"Preconditioners","title":"Model domain preconditioners","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Model space preconditioners acts on model size arrays such as the velocity model or the FWI/RTM gradient. These preconditioners are indepenedent of the number of sources and therefore should not be indexed.","category":"page"},{"location":"preconditioners/#Water-column-muting-(top-mute)","page":"Preconditioners","title":"Water column muting (top mute)","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Create a linear operator for a 2D model topmute, i.e. for muting the water column:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"TopMute","category":"page"},{"location":"preconditioners/#JUDI.TopMute","page":"Preconditioners","title":"JUDI.TopMute","text":"TopMute{T, N, Nw}\n\nMute top of the model in N dimensions\n\nConstructor\n\njudiTopmute(model; taperwidht=10)\njudiTopmute(n, wb, taperwidth) # Legacy\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Usage:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"# Forward\nm_mute = Mr*vec(m)\n\n# Adjoint\nm_mute = Mr'*vec(m)","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"As Mr is self adjoint, Mr is equal to Mr'.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"legacy:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"The legacy constructor judiTopmute(n, wb, taperwidth) is still available to construct a muting operator with user specified muting depth.","category":"page"},{"location":"preconditioners/#Model-depth-scaling","page":"Preconditioners","title":"Model depth scaling","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Create a 2D model depth scaling. This preconditionenr is the most simple form of inverse Hessain approximation compensating for illumination in the subsurface. We also describe below a more accurate diagonal approximation of the Hessian with the illlumination operator. Additionnaly, as a simple diagonal approximation, this operator is invertible and can be inverted with the standard julia inv function.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"DepthScaling","category":"page"},{"location":"preconditioners/#JUDI.DepthScaling","page":"Preconditioners","title":"JUDI.DepthScaling","text":"DepthScaling{T, N, K}\n\nDepth scaling operator in N dimensions scaling by depth^K.\n\nConstructor\n\njudiDepthScaling(model::AbstractModel; K=.5)\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Usage:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"# Forward\nm_mute = Mr*vec(m)\n\n# Adjoint\nm_mute = Mr'*vec(m)","category":"page"},{"location":"preconditioners/#Illumination","page":"Preconditioners","title":"Illumination","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"The illumination computed the energy of the wavefield along time for each grid point. This provides a first order diagonal approximation of the Hessian of FWI/LSRTM helping the ocnvergence and quality of an update.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"judiIllumination","category":"page"},{"location":"preconditioners/#JUDI.judiIllumination","page":"Preconditioners","title":"JUDI.judiIllumination","text":"judiIllumination(model; mode=\"u\", k=1, recompute=true)\n\nArguments\n\nmodel: JUDI Model structure\nmode: Type of ilumination, choicees of (\"u\", \"v\", \"uv\")\nk: Power of the illumination, real number\nrecompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)\njudiIllumination(F; mode=\"u\", k=1, recompute=true)\n\nArguments\n\nF: JUDI propagator\nmode: Type of ilumination, choicees of (\"u\", \"v\", \"uv\")\nk: Power of the illumination, real positive number\nrecompute: Flag whether to recompute the illumination at each new propagation (Defaults to true)\n\nDiagonal approximation of the FWI Hessian as the energy of the wavefield. The diagonal contains the sum over time of the wavefield chosen as mode.\n\nOptions for the mode are \"u\" for the forward wavefield illumination, \"v\" for the adjoint wavefield illumination, and \"uv\" for the pointwise product of the forward and adjoint wavefields illuminations. Additionally, the parameter \"k\" provides control on the scaling of the daiagonal raising it to the power k. \n\nExample\n\nI = judiIllumination(model) \n\nConstruct the diagonal operator such that I*x = x ./ |||u||_2^2\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Usage:","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"# Forward\nm_mute = I*vec(m)\n\n# Adjoint\nm_mute = I'*vec(m)","category":"page"},{"location":"preconditioners/#Data-preconditioners","page":"Preconditioners","title":"Data preconditioners","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"These preconditioners are design to act on the shot records (data). These preconditioners are indexable by source number so that working with a subset of shot is trivial to implement.","category":"page"},{"location":"preconditioners/#Data-topmute","page":"Preconditioners","title":"Data topmute","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"Create a data topmute for the data based on the source and receiver geometry (i.e based on the offsets between each souurce-receiver pair). THis operator allows two modes, :reflection for the standard \"top-mute\" direct wave muting and :turning for its opposite muting the reflection to compute gradients purely based on the turning waves. The muting operators uses a cosine taper at the mask limit to allow for a smooth transition.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"DataMute","category":"page"},{"location":"preconditioners/#JUDI.DataMute","page":"Preconditioners","title":"JUDI.DataMute","text":"struct DataMute{T, mode} <: DataPreconditioner{T, T}\n srcGeom::Geometry\n recGeom::Geometry\n vp::Vector{T}\n t0::Vector{T}\n taperwidth::Vector{Int64}\nend\n\nData mute linear operator a where {T, N}sociated with source srcGeom and receiver recGeom geometries used to compute the distance to the source for each trace in the data. Data mute preconditionner. Supports two modes (:reflection, :turning) that mute either the turning waves (standard direct wave mute) or mutes the reflections. A cosine tapr is applied with width taperwidth to avoid abrupt change and infinite frequency jumps in the data.\n\nConstructors\n\njudiDataMute(srcGeom, recGeom; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))\n\nConstruct the data mute operator from the source srcGeom and receiver recGeom geometries.\n\njudiDataMute(q, d; vp=1500, t0=.1, mode=:reflection, taperwidth=floor(Int, 2/t0))\n\nConstruct the data mute operator from the judivector source q and judivector data d.\n\nParameters\n\nThe following optional paramet where {T, N}rs control the muting operator\n\nvp: P wave velocity of the direct wave (usually water velocity). Can be a constant or a Vector with one value per source position. Devfaults to 1500m/s\nt0: Time shift in seconds (usually width of the wavelet). Defaults to 1 sec\nmode: :reflection to keep the reflections and mute above the direct wave (i.e for RTM) :turning to keep the turning waves and mute below the direct wave (i.e for FWI)\ntaperwidth: Width of the cosine taper in number of samples. Defaults to 2 / t0\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/#Band-pass-filter","page":"Preconditioners","title":"Band pass filter","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"While not purely a preconditioner, because this operator acts on the data and is traditionally used fro frequency continuation in FWI, we implemented this operator as a source indexable linear operator as well. Additionally, the filtering function is available as a standalone julia function for general usage","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"FrequencyFilter\nfilter_data","category":"page"},{"location":"preconditioners/#JUDI.FrequencyFilter","page":"Preconditioners","title":"JUDI.FrequencyFilter","text":"struct FrequencyFilter\n recGeom\n\nBandpass filter linear operator. Filters the input judiVector or Vector\n\nConstructor\n\njudiFilter(geometry, fmin, fmax) \njudiFilter(judiVector, fmin, fmax)\n\n\n\n\n\n","category":"type"},{"location":"preconditioners/#JUDI.filter_data","page":"Preconditioners","title":"JUDI.filter_data","text":"filter(Din, dt_in; fmin=0, fmax=25)\n\nPerforms a causal filtering [fmin, fmax] on the input data bases on its sampling rate dt. Automatically perfroms a lowpass if fmin=0 (default)\n\n\n\n\n\n","category":"function"},{"location":"preconditioners/#Data-time-derivative/intergraton","page":"Preconditioners","title":"Data time derivative/intergraton","text":"","category":"section"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"A TimeDifferential{K} is a linear operator that implements a time derivative (K>0) or time integration (K<0) of order K for any real K including fractional values.","category":"page"},{"location":"preconditioners/","page":"Preconditioners","title":"Preconditioners","text":"TimeDifferential","category":"page"},{"location":"preconditioners/#JUDI.TimeDifferential","page":"Preconditioners","title":"JUDI.TimeDifferential","text":"struct TimeDifferential\n recGeom\n\nDifferential operator of order K to be applied along the time dimension. Applies the ilter w^k where k is the order. For example, the tinme derivative is TimeDifferential{1} and the time integration is TimeDifferential{-1}\n\nConstructor\n\njudiTimeIntegration(recGeom, order)\njudiTimeIntegration(judiVector, order)\n\njudiTimeDerivative(recGeom, order)\njudiTimeDerivative(judiVector, order)\n\n\n\n\n\n","category":"type"},{"location":"tutorials/07_preconditionners/#Seismic-preconditionners","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"This tutorials provide an overview of the preconditionners available in JUDI. THese examples are useful, if not necessary, for FWI, RTM and LSRTM to improve convergence and the quality of the result. Preconditionners fall in two categories:","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Model preconditioners: these are linear operators that act on model domain vectors such as the velcoity or seismic image. \nData preconditionners: there are linear operators that act on the data, i.e the shot records.","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"We will show in the following example for both those categories. By design, the JUDI precontionners are designed to work on JUDI types such as judiVector or PhysicalParameters but can also be used directly on standard nion dimensional vectors.","category":"page"},{"location":"tutorials/07_preconditionners/#Setup","page":"Seismic preconditionners","title":"Setup","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"We setup this tutorial by creating a few objects we will be using. We consider ","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"d_textobs\na seismic dataset consiting of nsrc sources\nm\na squared slowness model\ndm\na model perturbation. For simplicity we derive this image directly for the squared slowness to avoid costly computation ","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"using JUDI, SlimPlotting, JLD2, HDF5, LinearAlgebra, SegyIO, Images\ndata_path = JUDI.JUDI_DATA","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"\"/Users/mathiaslouboutin/.julia/dev/JUDI/src/../data\"","category":"page"},{"location":"tutorials/07_preconditionners/#Model","page":"Seismic preconditionners","title":"Model","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"n, d, o, m0, m = read(h5open(\"$(data_path)/overthrust_model.h5\",\"r\"), \"n\", \"d\", \"o\", \"m0\", \"m\");","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(10, 5))\nplot_velocity(m'.^(-.5), d; aspect=1, new_fig=false, cbar=true)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(10, 5))\nplot_velocity(m0'.^(-.5), d; aspect=1, new_fig=false, cbar=true)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"dm = m0 - m\nfigure(figsize=(10, 5))\nplot_simage(dm', d; aspect=1, new_fig=false, cbar=true, name=\"dm\")","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"model = Model(n, d, o, m)\nmodel0 = Model(n, d, o, m0)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Model (n=(401, 121), d=(25.0f0, 25.0f0), o=(0.0f0, 0.0f0)) with parameters (:m, :rho)","category":"page"},{"location":"tutorials/07_preconditionners/#Data","page":"Seismic preconditionners","title":"Data","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Now that we have a model, let's load and look at the data","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"block = segy_read(\"$(data_path)/overthrust_shot_records.segy\")\nd_obs = judiVector(block); # linearized observed data\n# Source\nsrc_geometry = Geometry(block; key = \"source\")\nwavelet = ricker_wavelet(src_geometry.t[1],src_geometry.dt[1],0.008f0) # 8 Hz wavelet\nq = diff(judiVector(src_geometry, wavelet), dims=1)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mFixed length trace flag set in stream: IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=7076688, maxsize=Inf, ptr=3601, mark=-1)\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ SegyIO ~/.julia/dev/SegyIO/src/read/read_file.jl:36\u001b[39m\n\n\n\n\n\njudiVector{Float32, Matrix{Float32}} with 16 sources","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(10, 10))\nplot_sdata(get_data(d_obs[1]); new_fig=false)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"since some of the preconditionners are designed for inversion, let's create the data forr the background model and the propagators needed for imaging and inversion","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"F = judiModeling(model, q.geometry, d_obs.geometry; options=Options(space_order=16))\nJ = judiJacobian(F(model0), q)\nd0 = F(model0)*q","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Building forward operator\nOperator `forward` ran in 0.11 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.32 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.42 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.34 s\nOperator `forward` ran in 0.35 s\nOperator `forward` ran in 0.35 s\n\n\n\n\n\njudiVector{Float32, Matrix{Float32}} with 16 sources","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 10))\nsubplot(121)\nplot_sdata(get_data(d_obs[1]); new_fig=false)\nsubplot(122)\nplot_sdata(d0[1]; new_fig=false)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Model-preconditionners","page":"Seismic preconditionners","title":"Model preconditionners","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Dm = judiDepthScaling(model0)\nTm = judiTopmute(model0; taperwidth=0)\nIl = inv(judiIllumination(model0))\nmcases = [(Dm, \"Depth scaling\"), (Tm, \"Water layer mute\"), (Il, \"Illumination\")]","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"3-element Vector{Tuple{JUDI.ModelPreconditioner{Float32, Float32}, String}}:\n (DepthScaling{Float32, 2, 0.5f0}(48521, Float32[0.0 25.0 … 2975.0 3000.0]), \"Depth scaling\")\n (TopMute{Float32, 2, 1}(48521, [21, 21, 21, 21, 21, 21, 21, 21, 21, 21 … 21, 21, 21, 21, 21, 21, 21, 21, 21, 21], 0), \"Water layer mute\")\n (judiIllumination{Float32, :u, -1, true}(\"judiIllumination{Float32, :u, 1, true}\", Dict{SubString{String}, PhysicalParameter{Float32, 2}}(\"u\" => PhysicalParameter{Float32, 2} of size (401, 121) with origin (0.0f0, 0.0f0) and spacing (25.0f0, 25.0f0)), 48521), \"Illumination\")","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"rtm = J'*(d0 - d_obs)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Building forward operator\nOperator `forward` ran in 0.17 s\nBuilding adjoint born operator\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.28 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.27 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.46 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.35 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.40 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.30 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.24 s\nOperator `gradient` ran in 0.18 s\nOperator `forward` ran in 0.36 s\nOperator `gradient` ran in 0.16 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.17 s\nOperator `forward` ran in 0.35 s\nOperator `gradient` ran in 0.17 s\n\n\n\n\n\nPhysicalParameter{Float32, 2} of size (401, 121) with origin (0.0f0, 0.0f0) and spacing (25.0f0, 25.0f0)","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"plot_simage(Il.illums[\"u\"]'; cbar=true, name=\"Illumination\")","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Model-top-mute","page":"Seismic preconditionners","title":"Model top mute","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"This preconditionner is one of the simplest one and mutes the water layer, or more generally sets to zeros the top of the model.","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 12))\nfor (i, (pr, name)) in enumerate(mcases)\n subplot(3,1,i)\n plot_simage((pr*rtm)'; new_fig=false, name=name)\nend\ntight_layout()","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Data-Preconditionners","page":"Seismic preconditionners","title":"Data Preconditionners","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Dt = judiTimeDerivative(d_obs, .5)\nIt = judiTimeIntegration(d_obs, .5)\nDmr = judiDataMute(q, d_obs; mode=:reflection, taperwidth=2)\nDmt = judiDataMute(q, d_obs; mode=:turning)\nDf = judiFilter(d_obs, .1, 5.0)\ndcases = [(Dt, \"Fractional (.5) time derivative\"),\n (It, \"Fractional (.5) time integration\"),\n (Dmr, \"Turning waves muting\"),\n (Dmt, \"Reflection muting\"),\n (Df, \"bandpass filter [.1, 5]Hz\")];","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 12))\nfor (i, (pr, name)) in enumerate(dcases)\n subplot(3,2,i)\n dloc = (pr*d_obs)[1]\n plot_sdata(dloc; new_fig=false, name=name)\nend\ntight_layout()","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/07_preconditionners/#Putting-it-together","page":"Seismic preconditionners","title":"Putting it together","text":"","category":"section"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Of course, in practice numerous preconditionners would be needed for the best result. Since our implementation relies on linear algebra abstractions, those preconditionners can be used with each other and in combination with propagators as well. We show a few examples below","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"# Low frequency gradient with turing waves only for FWI\nrtmlow = Il*Tm*J'* Dmt * Df * (d0 - d_obs);","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"Operator `forward` ran in 0.25 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.32 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.26 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.29 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.27 s\nOperator `gradient` ran in 0.16 s\nOperator `forward` ran in 0.33 s\nOperator `gradient` ran in 0.15 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.16 s\nOperator `forward` ran in 0.37 s\nOperator `gradient` ran in 0.21 s\nOperator `forward` ran in 0.27 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.34 s\nOperator `gradient` ran in 0.14 s\nOperator `forward` ran in 0.24 s\nOperator `gradient` ran in 0.15 s\n\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mJOLI linear operator, returning julia Array\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ JUDI ~/.julia/dev/JUDI/src/TimeModeling/Types/ModelStructure.jl:217\u001b[39m","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"figure(figsize=(12, 4))\nplot_simage(reshape(rtmlow, model.n)', model.d; new_fig=false, name=\"Low frequency FWI gradient\", cmap=seiscm(:bwr))","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.n, use size(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m\n\u001b[33m\u001b[1m┌ \u001b[22m\u001b[39m\u001b[33m\u001b[1mWarning: \u001b[22m\u001b[39mDeprecated model.d, use spacing(model)\n\u001b[33m\u001b[1m│ \u001b[22m\u001b[39m caller = ip:0x0\n\u001b[33m\u001b[1m└ \u001b[22m\u001b[39m\u001b[90m@ Core :-1\u001b[39m","category":"page"},{"location":"tutorials/07_preconditionners/","page":"Seismic preconditionners","title":"Seismic preconditionners","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#FWI-Example","page":"FWI Example","title":"FWI Example","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We will peform FWI using the following steps:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Prepare models\nSetup Constraints with SetIntersectionProjection\nBuild a small local compute cluster (2 workers)\nTake care of some HPC details related to thread affinity\nCreate source and receivers geometries\nBuild F, the JUDI modeling operator\nUse F to create data for both models\nVisualize data\nAssess if data is cycle skipped at the farthest offsets\nBuild the objective function\nPerform the FWI using minConf_PQN from JUDI\nVisualize velocity models and objective function\nVisualize data match\nRemove workers","category":"page"},{"location":"tutorials/03_constrained_fwi/#Note-on-runtime","page":"FWI Example","title":"Note on runtime","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Warning: this notebook takes more than 1 hour to run for 16 shots with two workers on an Intel 8168.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"lscpu CPU information: Intel(R) Xeon(R) Platinum 8168 CPU @ 2.70GHz","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# Add packages needed\n# PyPlot is already installed in the environment\n# using Pkg\n# Pkg.add([\"Distributed\", \"SlimOptim\", \"JUDI\", \"SetIntersectionProjection\"])","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"using Distributed, JUDI, SlimOptim, LinearAlgebra, PyPlot, SetIntersectionProjection, Printf","category":"page"},{"location":"tutorials/03_constrained_fwi/#Prepare-models","page":"FWI Example","title":"Prepare models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"n = (251, 251) # nx, nz\nd = (15., 15.) # hx, hz\no = (0., 0.); # ox, oz","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# Squared slowness\nm = 1.5f0^(-2) * ones(Float32, n)\nm[101:150, 101:150] .= 1.7f0^(-2)\nm0 = 1/1.5f0^2 * ones(Float32, n);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"model = Model(n,d,o,m)\nmodel0 = Model(n,d,o,m0);","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize","page":"FWI Example","title":"Visualize","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,9))\nvmin,vmax = extrema(m)\ndmin,dmax = -.1,.1\n\nsubplot(3,1,1); imshow(m,aspect=\"auto\",cmap=\"jet\"); \ncolorbar(); clim(vmin,vmax); title(\"True squared slowness (m)\")\n\nsubplot(3,1,2); imshow(m0,aspect=\"auto\",cmap=\"jet\");\ncolorbar(); clim(vmin,vmax); title(\"Initial squared slowness (m0)\");\n\nsubplot(3,1,3); imshow(m.-m0,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(); clim(dmin,dmax); title(\"Difference (m-m0)\");\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Setup-Constraints-with-[SetIntersectionProjection](https://github.com/slimgroup/SetIntersectionProjection.jl)","page":"FWI Example","title":"Setup Constraints with SetIntersectionProjection","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"options=PARSDMM_options()\noptions.FL=Float32\noptions=default_PARSDMM_options(options,options.FL)\nconstraint = Vector{SetIntersectionProjection.set_definitions}()\nconstraint2 = Vector{SetIntersectionProjection.set_definitions}()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"set_definitions[]","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We setup two constaints:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Bounds that limit maximum and minimum velocity\nTV, that limits variation and force a piece-wise constant structure","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#bounds:\nm_min = 0 .* m .+ minimum(m).*.5\nm_max = 0 .* m .+ maximum(m)\nset_type = \"bounds\"\nTD_OP = \"identity\"\napp_mode = (\"matrix\",\"\")\ncustom_TD_OP = ([],false)\npush!(constraint, set_definitions(set_type,TD_OP,vec(m_min),vec(m_max),app_mode,custom_TD_OP));\npush!(constraint2, set_definitions(set_type,TD_OP,vec(m_min),vec(m_max),app_mode,custom_TD_OP));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#TV\n(TV,dummy1,dummy2,dummy3) = get_TD_operator(model0,\"TV\",options.FL)\nm_min = 0.0\nm_max = norm(TV*vec(m),1) * .5\nset_type = \"l1\"\nTD_OP = \"TV\"\napp_mode = (\"matrix\",\"\")\ncustom_TD_OP = ([],false)\npush!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#set up constraints with bounds only, precompute some things and define projector\n(P_sub2,TD_OP2,set_Prop2) = setup_constraints(constraint2, model0,options.FL)\n(TD_OP2,AtA2,l2,y2) = PARSDMM_precompute_distribute(TD_OP2,set_Prop2,model0,options)\noptions2 = deepcopy(options)\noptions2.rho_ini = ones(length(TD_OP2))*10.0\n\nproj_intersection2 = x-> PARSDMM(x, AtA2, TD_OP2, set_Prop2, P_sub2, model0, options2) \n\n# Projection function\nfunction prj2(input)\n input = Float32.(input)\n (x,dummy1,dummy2,dymmy3) = proj_intersection2(vec(input))\n return x\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"prj2 (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"#set up constraints with bounds and TV\n(P_sub,TD_OP,set_Prop) = setup_constraints(constraint, model0,options.FL)\n(TD_OP,AtA,l,y) = PARSDMM_precompute_distribute(TD_OP,set_Prop,model0,options)\noptions.rho_ini = ones(length(TD_OP))*10.0\n\nproj_intersection = x-> PARSDMM(x, AtA, TD_OP, set_Prop, P_sub, model0, options)\n\n# Projection function\nfunction prj(input)\n input = Float32.(input)\n (x,dummy1,dummy2,dymmy3) = proj_intersection(vec(input))\n return x\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"prj (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-a-small-local-compute-cluster","page":"FWI Example","title":"Build a small local compute cluster","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/#Setup-OMP-environment-variables-for-the-cluster","page":"FWI Example","title":"Setup OMP environment variables for the cluster","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"In the distributed compute case the workers that we add would be on different hardware, and we might add tens of workers in 2D and hundreds in 3D. Here we run on a single machine with only 2 workers, and so we need to be careful with details related to high performance computing. If we did not specify thread affinity, the two workers would compete for the same physical cores and the modeling would be incredibly slow.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We spin up the small 2-worker cluster by calling addprocs(2), and because we set the environment variable ENV[\"OMP_DISPLAY_ENV\"] = \"true\" we will see the OMP environment printed out on each worker. In that output (below) we can verify that half of the total threads (44/2 = 22) are assigned to each socket on this 2 socket system. You can obtain more details about the hardware with the shell command lscpu.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We set four environment variables related to OpenMP:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"OMP_DISPLAY_ENV prints out the OpenMP environment on each worker\nOMP_PROC_BIND specifies that threads should be bound to physical cores\nOMP_NUM_THREADS specifies the number of threads per workers is 1/2 the number of physical cores\nGOMP_CPU_AFFINITY specifies which physical cores the threads run on for each worker","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"If you run the shell command top during execution, you will see 3 julia processes: the main process and two workers. The two workers should generally have about 50% of the system, and load average should tend towards the physical number of cores.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"nthread = Sys.CPU_THREADS\nnw = 2\n\nENV[\"OMP_DISPLAY_ENV\"] = \"true\"\nENV[\"OMP_PROC_BIND\"] = \"close\"\nENV[\"OMP_NUM_THREADS\"] = \"$(div(nthread, nw))\" \naddprocs(nw)\n@show workers()\nfor k in 1:nworkers()\n place1 = (k - 1) * div(nthread,nworkers())\n place2 = (k + 0) * div(nthread,nworkers()) - 1\n @show place1, place2, div(nthread, nw)\n @spawnat workers()[k] ENV[\"GOMP_CPU_AFFINITY\"] = \"$(place1)-$(place2)\";\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"workers() = [2, 3]\n(place1, place2, div(nthread, nw)) = (0, 1, 2)\n(place1, place2, div(nthread, nw)) = (2, 3, 2)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"@everywhere using Distributed, JUDI, LinearAlgebra","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 3:\t\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 3:\t _OPENMP = '201511'\n From worker 3:\t OMP_DYNAMIC = 'FALSE'\n From worker 3:\t OMP_NESTED = 'FALSE'\n From worker 3:\t OMP_NUM_THREADS = '2'\n From worker 3:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 3:\t OMP_PROC_BIND = 'CLOSE'\n From worker 3:\t OMP_PLACES = ''\n From worker 3:\t OMP_STACKSIZE = '2097152'\n From worker 3:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 3:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 3:\t OMP_MAX_ACTIVE_LEVELS = '1'\n From worker 3:\t OMP_CANCELLATION = 'FALSE'\n From worker 3:\t OMP_DEFAULT_DEVICE = '0'\n From worker 3:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 3:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 3:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 3:\t OMP_ALLOCATOR = 'omp_default_mem_alloc'\n From worker 3:\t OMP_TARGET_OFFLOAD = 'DEFAULT'\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT END\n From worker 2:\t\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 2:\t _OPENMP = '201511'\n From worker 2:\t OMP_DYNAMIC = 'FALSE'\n From worker 2:\t OMP_NESTED = 'FALSE'\n From worker 2:\t OMP_NUM_THREADS = '2'\n From worker 2:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 2:\t OMP_PROC_BIND = 'CLOSE'\n From worker 2:\t OMP_PLACES = ''\n From worker 2:\t OMP_STACKSIZE = '2097152'\n From worker 2:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 2:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 2:\t OMP_MAX_ACTIVE_LEVELS = '1'\n From worker 2:\t OMP_CANCELLATION = 'FALSE'\n From worker 2:\t OMP_DEFAULT_DEVICE = '0'\n From worker 2:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 2:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 2:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 2:\t OMP_ALLOCATOR = 'omp_default_mem_alloc'\n From worker 2:\t OMP_TARGET_OFFLOAD = 'DEFAULT'\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT END","category":"page"},{"location":"tutorials/03_constrained_fwi/#Create-source-and-receivers-geometries","page":"FWI Example","title":"Create source and receivers geometries","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We use 8 shot locations evenly distributed across the left of the model.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"tn = 4000 # Recording time in ms\ndt = 2f0 # Shot record sampling rate in ms\nf0 = 0.005 # Peak frquency in kHz","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"0.005","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"nsrc = 8\nxsrc = convertToCell(d[1].*ones(Float32, nsrc))\nysrc = convertToCell(range(0f0, stop = 0f0, length = nsrc))\nzsrc = convertToCell(range(0f0, (n[2] - 1)*d[2], length=nsrc))\nsrc_geom = Geometry(xsrc, ysrc, zsrc; dt=dt, t=tn);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"nrec = 251\nxrec = (n[1] - 2)*d[1] .* ones(Float32, nrec)\nyrec = 0f0\nzrec = range(0f0, (n[2] - 1)*d[2], length=nrec)\nrec_geom = Geometry(xrec, yrec, zrec; dt=dt, t=tn, nsrc=nsrc);","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-geometry","page":"FWI Example","title":"Visualize geometry","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure()\nvmin,vmax = extrema(m)\ndmin,dmax = -.1,.1\n\nimshow(m,aspect=\"auto\",cmap=\"jet\", extent=[0, 3750, 3750, 0]); \ncolorbar(); clim(vmin,vmax); title(\"True squared slowness (m)\")\nscatter(xsrc, zsrc, c=\"g\", label=\"Sources\")\nscatter(xrec[1:4:end], zrec[1:4:end], c=\"c\", label=\"Receiver\")\nlegend()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"PyObject ","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-F,-the-JUDI-modeling-operator","page":"FWI Example","title":"Build F, the JUDI modeling operator","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# True model operator\nF = judiModeling(model, src_geom, rec_geom)\n\n# Intial model operator\nF0 = judiModeling(model0, src_geom, rec_geom);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# Source function\nfsrc = judiVector(src_geom, ricker_wavelet(tn, dt, f0));","category":"page"},{"location":"tutorials/03_constrained_fwi/#Use-F-to-create-the-data-in-both-models","page":"FWI Example","title":"Use F to create the data in both models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"t1 = @elapsed begin\n dobs = F*fsrc;\nend\n@info @sprintf(\"Time in true model; %.2f seconds\\n\", t1);\n\nt2 = @elapsed begin\n d0 = F0*fsrc;\nend\n@info @sprintf(\"Time in init model; %.2f seconds\\n\", t2);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 3:\t\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 3:\t _OPENMP = '201511'\n From worker 3:\t OMP_DYNAMIC = 'FALSE'\n From worker 3:\t OMP_NESTED = 'FALSE'\n From worker 3:\t OMP_NUM_THREADS = '2'\n From worker 3:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 3:\t OMP_PROC_BIND = 'CLOSE'\n From worker 3:\t OMP_PLACES = ''\n From worker 3:\t OMP_STACKSIZE = '2097152'\n From worker 3:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 3:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 3:\t OMP_MAX_ACTIVE_LEVELS = '2147483647'\n From worker 3:\t OMP_CANCELLATION = 'FALSE'\n From worker 3:\t OMP_DEFAULT_DEVICE = '0'\n From worker 3:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 3:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 3:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 3:\tOPENMP DISPLAY ENVIRONMENT END\n From worker 2:\t\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT BEGIN\n From worker 2:\t _OPENMP = '201511'\n From worker 2:\t OMP_DYNAMIC = 'FALSE'\n From worker 2:\t OMP_NESTED = 'FALSE'\n From worker 2:\t OMP_NUM_THREADS = '2'\n From worker 2:\t OMP_SCHEDULE = 'DYNAMIC'\n From worker 2:\t OMP_PROC_BIND = 'CLOSE'\n From worker 2:\t OMP_PLACES = ''\n From worker 2:\t OMP_STACKSIZE = '2097152'\n From worker 2:\t OMP_WAIT_POLICY = 'PASSIVE'\n From worker 2:\t OMP_THREAD_LIMIT = '4294967295'\n From worker 2:\t OMP_MAX_ACTIVE_LEVELS = '2147483647'\n From worker 2:\t OMP_CANCELLATION = 'FALSE'\n From worker 2:\t OMP_DEFAULT_DEVICE = '0'\n From worker 2:\t OMP_MAX_TASK_PRIORITY = '0'\n From worker 2:\t OMP_DISPLAY_AFFINITY = 'FALSE'\n From worker 2:\t OMP_AFFINITY_FORMAT = 'level %L thread %i affinity %A'\n From worker 2:\tOPENMP DISPLAY ENVIRONMENT END\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n\n\n┌ Info: Time in true model; 27.31 seconds\n└ @ Main In[20]:4\n\n\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.29 s\n\n\n┌ Info: Time in init model; 10.21 seconds\n└ @ Main In[20]:9","category":"page"},{"location":"tutorials/03_constrained_fwi/#Compute-the-residual-data","page":"FWI Example","title":"Compute the residual data","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"r = d0 - dobs;","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-data","page":"FWI Example","title":"Visualize data","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"shots = [1,4,8]","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"3-element Vector{Int64}:\n 1\n 4\n 8","category":"page"},{"location":"tutorials/03_constrained_fwi/#Plot-shot-gathers-for-true-model,-initial-model,-and-residual","page":"FWI Example","title":"Plot shot gathers for true model, initial model, and residual","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"The table below describes the data images below. We flip the direction of the residual and modeled data in order to help display the match with the true data.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"
      Initial Residual Data
      (flipped)
      True Data Initial Data
      (flipped)
      ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Note that the data modeled in the initial model lacks a lot of reflectivity that is evident in the data modeled in the true model. We expect to recover this missing reflectivity with the FWI.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"scale = 10.0 / sqrt(norm(dobs)^2 / length(dobs.data))\n@show scale\n\nnzero = 5\npad = ones(Float32,2001,nzero)\n\nfigure(figsize=(8,9)); clf()\nfor (iplot,ishot) in enumerate(shots)\n cat2 = hcat(reverse(r.data[ishot],dims=2), pad, dobs.data[ishot], pad, reverse(d0.data[ishot],dims=2))\n subplot(3,1,iplot);\n imshow(cat2,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\" Initial Residual sz=$(zsrc[ishot]) | True sz=$(zsrc[ishot]) | Initial sz=$(zsrc[ishot]) (flipped)\");\nend\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"scale = 0.018069575143305386","category":"page"},{"location":"tutorials/03_constrained_fwi/#Assess-if-data-is-cycle-skipped-at-the-farthest-offsets","page":"FWI Example","title":"Assess if data is cycle skipped at the farthest offsets","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Next we plot the far offset traces for these three shots in order to assess if the data is cycle skipped. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"You can ovbserve in the plots below that the refraction waveforms (first arrivals) in the initial model are not cycle skipped with respect to the true model, so we can proceed. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"A very significant part of the residual wavefield is actually reflections in this example.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"scale = 10.0 / sqrt(norm(dobs)^2 / length(dobs.data))\nt = [0.0:dt:tn;]\n\nfigure(figsize=(8,9)); clf()\nfor (iplot,ishot) in enumerate(shots)\n subplot(3,1,iplot);\n plot(t,dobs.data[ishot][:,end],label=\"True Model $(ishot) at z=$(zsrc[ishot])\");\n plot(t,d0.data[ishot][:,end],label=\"Initial Model $(ishot) at z=$(zsrc[ishot])\");\n xlim([4.5,t[end]])\n legend()\nend\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-the-objective-functions","page":"FWI Example","title":"Build the objective functions","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/#Build-src/rec-positions-mask","page":"FWI Example","title":"Build src/rec positions mask","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We use this mask to remove the imprint in gradients of proximity to source locations. The mask is set to 0 wherever a source or receiver is close, and is set to 1 otherwise. Without this mask most of the gradient updates would be concentrated close to sources where the model is correct. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"wb_mask = ones(Float32,size(m))\nwb_mask[1:5, :] .= 0;\nwb_mask[end-5:end, :] .= 0;\n\nfigure(figsize=(8,3))\nimshow(wb_mask', aspect=\"auto\",cmap=\"gray_r\",clim=[0,+2]);\ncolorbar();\ntitle(\"Water Bottom Mask\");\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Build-the-objective-function","page":"FWI Example","title":"Build the objective function","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"This method is called by the solver whenever the gradient is required. Steps in computing the gradient are as follows:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Apply the adjoint of the Jacobian to the current residual J' * [F*v - d] \nApply simple scaling based on the size of the first gradient, and save to apply to future gradients","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# build Jacoian\nJ = judiJacobian(F0, fsrc)\n\nfunction objective(F0, G, m, dobs, wb_mask)\n F0.model.m .= m\n t = @elapsed begin\n d0 = F0*fsrc\n if isnothing(G)\n return .5f0*norm(d0 .- dobs)^2\n end\n G .= J' * (d0 .- dobs)\n end\n G .*= vec(wb_mask)\n ϕ = .5f0*norm(d0 .- dobs)^2\n if gscale == 0.0\n # compute scalar from first gradient, apply to future gradients\n global gscale = .25f0 ./ maximum(G) \n @show gscale\n end\n G .*= gscale\n return ϕ\nend\n\n# struct to save the first gradient scalar\ngscale = 0f0\nf(x) = objective(F0, nothing, x, dobs, wb_mask)\ng!(G, x) = objective(F0, G, x, dobs, wb_mask)\nfg!(G, x) = objective(F0, G, x, dobs, wb_mask)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"fg! (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Compute-gradient","page":"FWI Example","title":"Compute gradient","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"grad1 = 0f0 .* vec(m0)\ntgrad1 = @elapsed begin\n g!(grad1, vec(m0))\n gscale = 0\nend\n@show tgrad1;","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.26 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.59 s\n From worker 2:\tOperator `gradient` ran in 0.46 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.63 s\n From worker 2:\tOperator `gradient` ran in 0.49 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `gradient` ran in 0.54 s\n From worker 2:\tOperator `gradient` ran in 0.34 s\ngscale = 3.15937f-5\ntgrad1 = 33.235375243","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"dm = m0 .- m\ngrad1 = reshape(grad1, n)\nmg2 = reshape(m0 .- grad1, n)\nfigure(figsize=(8,9))\n\nsubplot(5,1,1)\nimshow(grad1' ./ maximum(abs,grad1),aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(-1,1);\ntitle(\"Initial Gradient without Illumination Compensation\");\n\nsubplot(5,1,2)\nimshow(dm ./ maximum(abs,dm),aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(-1,1);\ntitle(\"Squared slowness Difference: (m0 - m)\");\n\nsubplot(5,1,3)\nimshow(mg2',aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax)\ntitle(\"Updated squared slowness: (m0 - grad1)\");\n\nsubplot(5,1,4)\nimshow(reshape(prj(mg2), n)',aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax)\ntitle(\"Updated projected (bounds + TV) squared slowness: prj(m0 - grad1)\");\n\nsubplot(5,1,5)\nimshow(reshape(prj2(mg2), n)',aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax)\ntitle(\"Updated projected (bounds) squared slowness: prj(m0 - grad1)\");\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"relative evolution to small, exiting PARSDMM (iteration 37)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"input to PARSDMM is feasible, returning","category":"page"},{"location":"tutorials/03_constrained_fwi/#Perform-the-FWI-using-minConf_PQN","page":"FWI Example","title":"Perform the FWI using minConf_PQN","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We will do 10 functions evaluation cost of projected quasi-Newton with two setup:","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Bounds constraints only\nBounds + tv constrains","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"# FWI with PQN\nniter = 10\ngscale = 0f0\noptions_pqn = pqn_options(progTol=0, store_trace=true, verbose=3, maxIter=niter)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"SlimOptim.PQN_params(3, 1.0f-5, 0, 10, 0.0001f0, 10, false, true, true, 1.0f-6, 1.0f-7, 100, false, 20, 1, 1)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"sol = pqn(f, g!, fg!, vec(m0), prj, options_pqn);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Running PQN...\nNumber of L-BFGS Corrections to store: 10\nSpectral initialization of SPG: 1\nMaximum number of SPG iterations: 100\nSPG optimality tolerance: 1.00e-06\nSPG progress tolerance: 1.00e-07\nPQN optimality tolerance: 1.00e-05\nPQN progress tolerance: 0.00e+00\nQuadratic initialization of line search: 0\nMaximum number of iterations: 10\nLine search: BackTracking{Float32, Int64}\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.56 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `gradient` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.57 s\n From worker 3:\tOperator `gradient` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.38 s\ngscale = 3.159375f-5\n Iteration FunEvals GradEvals Projections Step Length Function Val Opt Cond\nrelative evolution to small, exiting PARSDMM (iteration 37)\n 0 0 0 0 0.00000e+00 8.04513e+05 6.74951e-02\nrelative evolution to small, exiting PARSDMM (iteration 37)\nrelative evolution to small, exiting PARSDMM (iteration 37)\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.28 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.54 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.23 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.33 s\n From worker 3:\tOperator `gradient` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.55 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.52 s\n From worker 3:\tOperator `gradient` ran in 0.32 s\nrelative evolution to small, exiting PARSDMM (iteration 38)\n 1 3 2 6 1.00000e-01 5.95413e+05 5.50441e-02\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 29)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.25 s\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.22 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.69 s\n From worker 3:\tOperator `gradient` ran in 0.71 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.73 s\n From worker 3:\tOperator `gradient` ran in 0.72 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `forward` ran in 0.54 s\n From worker 2:\tOperator `gradient` ran in 0.72 s\n From worker 3:\tOperator `gradient` ran in 0.72 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.66 s\n From worker 3:\tOperator `gradient` ran in 0.63 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n 2 5 3 36 1.00000e-01 4.97818e+05 1.70875e-01\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 30)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.62 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.66 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `gradient` ran in 0.66 s\n From worker 2:\tOperator `gradient` ran in 0.68 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.71 s\n From worker 2:\tOperator `gradient` ran in 0.68 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n 3 7 4 64 1.00000e-01 3.82668e+05 1.56781e-01\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 29)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.24 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.33 s\n From worker 2:\tOperator `gradient` ran in 0.52 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `gradient` ran in 0.53 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `gradient` ran in 0.54 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `gradient` ran in 0.49 s\n From worker 2:\tOperator `gradient` ran in 0.32 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n 4 8 5 92 1.00000e+00 3.50573e+05 6.73204e-02\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 38)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.24 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.30 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.67 s\n From worker 2:\tOperator `gradient` ran in 0.85 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.67 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `gradient` ran in 0.65 s\n From worker 2:\tOperator `gradient` ran in 0.66 s\nrelative evolution to small, exiting PARSDMM (iteration 26)\n 5 9 6 116 1.00000e+00 1.17713e+05 2.24156e-02\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.22 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `gradient` ran in 0.70 s\n From worker 2:\tOperator `gradient` ran in 0.72 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.74 s\n From worker 2:\tOperator `gradient` ran in 0.74 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\n From worker 2:\tOperator `gradient` ran in 0.55 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `gradient` ran in 0.32 s\nrelative evolution to small, exiting PARSDMM (iteration 26)\n 6 10 7 130 1.00000e+00 8.91832e+04 2.10390e-02\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 28)\nrelative evolution to small, exiting PARSDMM (iteration 28)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.30 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `gradient` ran in 0.59 s\n From worker 3:\tOperator `gradient` ran in 0.49 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.40 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 7 11 8 146 1.00000e+00 3.31746e+04 2.72466e-02\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 27)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.26 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.45 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.75 s\n From worker 2:\tOperator `gradient` ran in 0.77 s\n From worker 3:\tOperator `forward` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.62 s\n From worker 2:\tOperator `gradient` ran in 0.61 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.45 s\n From worker 3:\tOperator `gradient` ran in 0.76 s\n From worker 2:\tOperator `gradient` ran in 0.77 s\n From worker 3:\tOperator `forward` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.82 s\n From worker 2:\tOperator `gradient` ran in 0.80 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 8 12 9 164 1.00000e+00 1.77415e+04 2.05675e-02\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 27)\nrelative evolution to small, exiting PARSDMM (iteration 20)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.25 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.53 s\n From worker 3:\tOperator `gradient` ran in 0.96 s\n From worker 2:\tOperator `gradient` ran in 0.97 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `forward` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.78 s\n From worker 2:\tOperator `gradient` ran in 0.75 s\n From worker 3:\tOperator `forward` ran in 0.53 s\n From worker 2:\tOperator `forward` ran in 0.57 s\n From worker 3:\tOperator `gradient` ran in 0.77 s\n From worker 2:\tOperator `gradient` ran in 0.78 s\n From worker 3:\tOperator `forward` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.70 s\n From worker 2:\tOperator `gradient` ran in 0.74 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 9 13 10 184 1.00000e+00 1.05269e+04 1.38740e-02\nrelative evolution to small, exiting PARSDMM (iteration 25)\nrelative evolution to small, exiting PARSDMM (iteration 41)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 14)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\nrelative evolution to small, exiting PARSDMM (iteration 15)\n\n\n┌ Info: Line search failed\n└ @ SlimOptim /Users/mathiaslouboutin/.julia/dev/SlimOptim/src/linesearches.jl:47\n\n\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.27 s\nrelative evolution to small, exiting PARSDMM (iteration 15)\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.81 s\n From worker 2:\tOperator `gradient` ran in 0.81 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.58 s\n From worker 2:\tOperator `forward` ran in 0.54 s\n From worker 3:\tOperator `forward` ran in 0.52 s\n From worker 2:\tOperator `gradient` ran in 0.67 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\nrelative evolution to small, exiting PARSDMM (iteration 14)\n 10 14 11 216 1.00000e+00 5.58767e+03 9.81355e-03","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"sol2 = pqn(f, g!, fg!, vec(m0), prj2, options_pqn);","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"Running PQN...\nNumber of L-BFGS Corrections to store: 10\nSpectral initialization of SPG: 1\nMaximum number of SPG iterations: 100\nSPG optimality tolerance: 1.00e-06\nSPG progress tolerance: 1.00e-07\nPQN optimality tolerance: 1.00e-05\nPQN progress tolerance: 0.00e+00\nQuadratic initialization of line search: 0\nMaximum number of iterations: 10\nLine search: BackTracking{Float32, Int64}\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.31 s\n From worker 2:\tOperator `forward` ran in 0.31 s\n From worker 3:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `gradient` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.29 s\n Iteration FunEvals GradEvals Projections Step Length Function Val Opt Cond\ninput to PARSDMM is feasible, returning\n 0 0 0 0 0.00000e+00 8.04514e+05 2.50000e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.52 s\n From worker 3:\tOperator `forward` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.50 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.29 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.22 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.22 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `gradient` ran in 0.73 s\n From worker 3:\tOperator `gradient` ran in 0.77 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `gradient` ran in 0.59 s\n From worker 3:\tOperator `gradient` ran in 0.48 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.56 s\n From worker 3:\tOperator `gradient` ran in 0.33 s\ninput to PARSDMM is feasible, returning\n 1 3 2 6 1.00000e-01 4.12124e+05 1.59019e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.64 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.22 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.23 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.24 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `gradient` ran in 0.75 s\n From worker 2:\tOperator `gradient` ran in 0.75 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `gradient` ran in 0.61 s\n From worker 3:\tOperator `gradient` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.49 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.31 s\ninput to PARSDMM is feasible, returning\n 2 5 3 42 1.00000e-01 2.75734e+05 1.35675e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 3:\tOperator `forward` ran in 0.26 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `gradient` ran in 0.77 s\n From worker 2:\tOperator `gradient` ran in 0.79 s\n From worker 3:\tOperator `forward` ran in 0.67 s\n From worker 2:\tOperator `forward` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.99 s\n From worker 2:\tOperator `gradient` ran in 0.99 s\n From worker 3:\tOperator `forward` ran in 0.54 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.73 s\n From worker 2:\tOperator `gradient` ran in 0.93 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.67 s\n From worker 2:\tOperator `gradient` ran in 0.42 s\ninput to PARSDMM is feasible, returning\n 3 6 4 70 1.00000e+00 1.12580e+05 1.14484e-01\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `forward` ran in 0.53 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.52 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.84 s\n From worker 3:\tOperator `gradient` ran in 0.84 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `gradient` ran in 0.64 s\n From worker 3:\tOperator `gradient` ran in 0.69 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `gradient` ran in 0.78 s\n From worker 3:\tOperator `gradient` ran in 0.79 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.47 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.41 s\ninput to PARSDMM is feasible, returning\n 4 7 5 94 1.00000e+00 3.94970e+04 4.63609e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.32 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.32 s\n From worker 3:\tOperator `forward` ran in 0.56 s\n From worker 2:\tOperator `gradient` ran in 0.43 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `gradient` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `gradient` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `gradient` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\ninput to PARSDMM is feasible, returning\n 5 8 6 118 1.00000e+00 2.78628e+04 2.88242e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.29 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.25 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `gradient` ran in 0.62 s\n From worker 3:\tOperator `gradient` ran in 0.58 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `gradient` ran in 0.59 s\n From worker 3:\tOperator `gradient` ran in 0.50 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.56 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.35 s\ninput to PARSDMM is feasible, returning\n 6 9 7 150 1.00000e+00 1.45960e+04 2.03700e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.35 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.50 s\n From worker 2:\tOperator `gradient` ran in 0.64 s\n From worker 3:\tOperator `gradient` ran in 0.56 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.49 s\n From worker 2:\tOperator `gradient` ran in 0.64 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.60 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `gradient` ran in 0.54 s\n From worker 3:\tOperator `gradient` ran in 0.34 s\ninput to PARSDMM is feasible, returning\n 7 10 8 190 1.00000e+00 7.63842e+03 1.36674e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.23 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.26 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.33 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.33 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 2:\tOperator `forward` ran in 0.26 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.80 s\n From worker 3:\tOperator `gradient` ran in 0.80 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `gradient` ran in 0.53 s\n From worker 3:\tOperator `gradient` ran in 0.53 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `gradient` ran in 0.48 s\n From worker 3:\tOperator `gradient` ran in 0.53 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.42 s\n From worker 2:\tOperator `gradient` ran in 0.58 s\n From worker 3:\tOperator `gradient` ran in 0.38 s\ninput to PARSDMM is feasible, returning\n 8 11 9 238 1.00000e+00 3.18718e+03 1.09845e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.35 s\n From worker 2:\tOperator `forward` ran in 0.37 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.40 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.34 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.36 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.43 s\n From worker 3:\tOperator `gradient` ran in 0.61 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `gradient` ran in 0.65 s\n From worker 2:\tOperator `gradient` ran in 0.52 s\n From worker 3:\tOperator `forward` ran in 0.43 s\n From worker 2:\tOperator `forward` ran in 0.44 s\n From worker 3:\tOperator `gradient` ran in 0.52 s\n From worker 2:\tOperator `gradient` ran in 0.51 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.42 s\n From worker 3:\tOperator `gradient` ran in 0.51 s\n From worker 2:\tOperator `gradient` ran in 0.34 s\ninput to PARSDMM is feasible, returning\n 9 12 10 292 1.00000e+00 2.62946e+03 1.24063e-02\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.59 s\n From worker 3:\tOperator `forward` ran in 0.36 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.24 s\ninput to PARSDMM is feasible, returning\n From worker 3:\tOperator `forward` ran in 0.39 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.40 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `gradient` ran in 0.74 s\n From worker 2:\tOperator `gradient` ran in 0.76 s\n From worker 3:\tOperator `forward` ran in 0.46 s\n From worker 2:\tOperator `forward` ran in 0.46 s\n From worker 3:\tOperator `gradient` ran in 0.78 s\n From worker 2:\tOperator `gradient` ran in 0.82 s\n From worker 3:\tOperator `forward` ran in 0.52 s\n From worker 2:\tOperator `forward` ran in 0.50 s\n From worker 3:\tOperator `gradient` ran in 0.78 s\n From worker 2:\tOperator `gradient` ran in 0.78 s\n From worker 3:\tOperator `forward` ran in 0.44 s\n From worker 2:\tOperator `forward` ran in 0.47 s\n From worker 3:\tOperator `gradient` ran in 0.73 s\n From worker 2:\tOperator `gradient` ran in 0.71 s\ninput to PARSDMM is feasible, returning\n 10 13 11 338 1.00000e+00 1.73191e+03 5.47692e-03","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"mf = reshape(prj(sol.x), n) # optimal solution\nϕ = sol.ϕ_trace # cost vs iteration\nm1 = sol.x_trace # model vs iteration\ncollect(m1[i] = reshape(m1[i], n) for i=1:length(ϕ));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"relative evolution to small, exiting PARSDMM (iteration 15)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"mf2 = reshape(prj(sol2.x), n) # optimal solution\nϕ2 = sol2.ϕ_trace # cost vs iteration\nm2 = sol2.x_trace # model vs iteration\ncollect(m2[i] = reshape(m2[i], n) for i=1:length(ϕ2));","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"relative evolution to small, exiting PARSDMM (iteration 15)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-velocity-models-and-objective-function","page":"FWI Example","title":"Visualize velocity models and objective function","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,9)); clf()\n\nsubplot(4,1,1);imshow(m0',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"Initial Velocity\");\n\nsubplot(4,1,2);imshow(mf2',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"FWI Velocity\");\n\nsubplot(4,1,3);imshow(mf',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"FWI Velocity with TV\");\n\nsubplot(4,1,4);imshow(m',aspect=\"auto\",cmap=\"jet\");\ncolorbar(orientation=\"vertical\");clim(vmin,vmax);title(\"True Velocity\")\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Display-the-velocity-difference-models","page":"FWI Example","title":"Display the velocity difference models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"rms_v2 = @sprintf(\"%.1f m/s\", sqrt(norm(m .- m0)^2 / length(m)))\nrms_vf = @sprintf(\"%.1f m/s\", sqrt(norm(m .- mf)^2 / length(m)))\nrms_vf2 = @sprintf(\"%.1f m/s\", sqrt(norm(m .- mf2)^2 / length(m)))\n\nfigure(figsize=(8,6)); clf()\n\nsubplot(3,1,1);imshow(m .- m0,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(dmin,dmax);\ntitle(\"Vtrue - Vinit difference, rms=$(rms_v2)\");\n\nsubplot(3,1,2);imshow(m .- mf,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(dmin,dmax);\ntitle(\"Vtrue - Vfwi difference, rms=$(rms_vf)\");\n\nsubplot(3,1,3);imshow(m .- mf2,aspect=\"auto\",cmap=\"seismic\");\ncolorbar(orientation=\"vertical\");clim(dmin,dmax);\ntitle(\"Vtrue - Vfwi_TV difference, rms=$(rms_vf2)\");\n\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Display-the-cost-function","page":"FWI Example","title":"Display the cost function","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,4)); clf()\niters = [0:1:niter;]\nplot(ϕ[2:end] ./ ϕ[2], marker=\"o\", label=\"FWI_TV\")\nplot(ϕ2[2:end] ./ ϕ2[2], marker=\"o\", label=\"FWI\")\nylim([0,1.05])\nxlabel(\"Nonlinear Iteration\")\nylabel(\"Normalized cost ||f(v) - d||\")\ntitle(@sprintf(\"FWI Objective Function reduced %.1f percent and %.1f percent with TV\",\n 100 * (ϕ[2] - ϕ[end]) / ϕ[2], 100 * (ϕ2[2] - ϕ2[end]) / ϕ2[2]));\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Display-data-misfit-vs-model-misfit","page":"FWI Example","title":"Display data misfit vs model misfit","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"figure(figsize=(8,4)); clf()\n\nc = [norm(m1[i] .- m, 2) for i in 1:length(m1)]\nc2 = [norm(m2[i] .- m, 2) for i in 1:length(m2)]\nloglog(c[2:end], ϕ[2:end], label=\"FWI_TV\", marker=\"s\", linewidth=1)\nloglog(c2[2:end], ϕ2[2:end], label=\"FWI\", marker=\"s\", linewidth=1)\nlegend()\nxlabel(\"Log Model residual\")\nylabel(\"Log Data residual\")\ntitle(\"Misfit Trajectory, LOOK AT THAT TV MODEL ERROR\");\ntight_layout()","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Visualize-data-match","page":"FWI Example","title":"Visualize data match","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/#Generate-data-in-the-FWI-velocity-model","page":"FWI Example","title":"Generate data in the FWI velocity model","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"tf = @elapsed begin\n F0.model.m .= vec(mf)\n df = F0*fsrc;\nend\n@show tf;\n\ntf2 = @elapsed begin\n F0.model.m .= vec(mf2)\n df2 = F0*fsrc;\nend\n@show tf2;","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":" From worker 2:\tOperator `forward` ran in 0.24 s\n From worker 3:\tOperator `forward` ran in 0.37 s\n From worker 2:\tOperator `forward` ran in 0.48 s\n From worker 3:\tOperator `forward` ran in 0.41 s\n From worker 2:\tOperator `forward` ran in 0.39 s\n From worker 3:\tOperator `forward` ran in 0.34 s\n From worker 2:\tOperator `forward` ran in 0.37 s\n From worker 3:\tOperator `forward` ran in 0.24 s\ntf = 10.834594565\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.41 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.45 s\n From worker 2:\tOperator `forward` ran in 0.38 s\n From worker 3:\tOperator `forward` ran in 0.38 s\n From worker 2:\tOperator `forward` ran in 0.25 s\ntf2 = 10.631615719","category":"page"},{"location":"tutorials/03_constrained_fwi/#Compute-residuals","page":"FWI Example","title":"Compute residuals","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"rf = df - dobs;\nrf2 = df2 - dobs;","category":"page"},{"location":"tutorials/03_constrained_fwi/#Plot-shot-gathers-for-true,-initial-model,-and-fwi-models","page":"FWI Example","title":"Plot shot gathers for true, initial model, and fwi models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"The table below describes the data images below. We will flip the direction of the residual and modeled data in order to help display the match with the true data. We include the initial data as shown above for easier comparison. ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"
      Initial Residual Data
      (flipped)
      True Data Initial Data
      (flipped)
      FWI Residual Data
      (flipped)
      True Data FWI Data
      (flipped)
      ","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"We first make a function to create the plots that we can re-use for the selected shots.","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"zsrc = trunc.([zsrc[i][1] for i=1:nsrc]; digits=6)\nfunction make_plot(index)\n figure(figsize=(8,6)); clf()\n cat2 = hcat(reverse(r.data[index],dims=2), pad, dobs.data[index], pad, reverse(d0.data[index],dims=2))\n catf = hcat(reverse(rf.data[index],dims=2), pad, dobs.data[index], pad, reverse(df.data[index],dims=2))\n catf2 = hcat(reverse(rf2.data[index],dims=2), pad, dobs.data[index], pad, reverse(df.data[index],dims=2))\n subplot(3,1,1);\n imshow(cat2,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\" Initial Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || Initial sz=$(zsrc[index]) (flipped)\");\n subplot(3,1,2);\n imshow(catf2,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\" FWI Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || FWI sz=$(zsrc[index]) (flipped)\");\n tight_layout()\n subplot(3,1,3);\n imshow(catf,cmap=\"gray\",aspect=\"auto\",clim=[-1,+1]);\n title(\"TV FWI Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || FWI sz=$(zsrc[index]) (flipped)\");\n tight_layout()\nend","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot (generic function with 1 method)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Data-for-the-1st-shot,-generated-in-the-initial-and-FWI-models","page":"FWI Example","title":"Data for the 1st shot, generated in the initial and FWI models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot(1)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Data-for-the-4th-shot,-generated-in-the-initial-and-FWI-models","page":"FWI Example","title":"Data for the 4th shot, generated in the initial and FWI models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot(4)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Data-for-the-8th-shot,-generated-in-the-initial-and-FWI-models","page":"FWI Example","title":"Data for the 8th shot, generated in the initial and FWI models","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"make_plot(8)","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"(Image: png)","category":"page"},{"location":"tutorials/03_constrained_fwi/#Remove-workers","page":"FWI Example","title":"Remove workers","text":"","category":"section"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"rmprocs(workers());","category":"page"},{"location":"tutorials/03_constrained_fwi/","page":"FWI Example","title":"FWI Example","text":"┌ Warning: rmprocs: process 1 not removed\n└ @ Distributed /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.6/Distributed/src/cluster.jl:1038","category":"page"},{"location":"data_structures/#Data-structures","page":"Data Structures","title":"Data structures","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Pages = [\"data_structures.md\"]","category":"page"},{"location":"data_structures/#Physical-Parameter","page":"Data Structures","title":"Physical Parameter","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Data structure for physical parameter array in JUDI. A PhysicalParameter inherits from julia AbstractVector","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"PhysicalParameter","category":"page"},{"location":"data_structures/#JUDI.PhysicalParameter","page":"Data Structures","title":"JUDI.PhysicalParameter","text":"PhysicalParameter\n n::NTuple{N, T}\n d::NTuple{N, Tf}\n o::NTuple{N, Tf}\n data::Union{Array, Number}\n\nPhysicalParameter structure for physical space parameter.\n\nn: number of gridpoints in (x,y,z) for 3D or (x,z) for 2D\n\nd: grid spacing in (x,y,z) or (x,z) (in meters)\n\no: origin of coordinate system in (x,y,z) or (x,z) (in meters)\n\ndata: the array of the parameter values of size n\n\nConstructor\n\nA PhysicalParameter can be constructed in various ways but always require the origin o and grid spacing d that cannot be infered from the array.\n\nPhysicalParameter(v::Array{T}, d, o) where `v` is an n-dimensional array and n=size(v)\n\nPhysicalParameter(n, d, o; T=Float32) Creates a zero PhysicalParameter\n\nPhysicalParameter(v::Array{T}, A::PhysicalParameter{T, N}) where {T<:Real, N} Creates a PhysicalParameter from the Array `v` with n, d, o from `A`\n\nPhysicalParameter(v::Array{T, N}, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) where `v` is a vector or nd-array that is reshaped into shape `n`\n\nPhysicalParameter(v::T, n::NTuple{N, T}, d::NTuple{N, T}, o::Tuple) Creates a constant (single number) PhyicalParameter\n\n\n\n\n\n","category":"type"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Unless specified otherwise with the return_array option in Options, the result of a migration/FWIgradient(judiJacobian, fwi_objective, lsrtm_objective) will be wrapped into a PhysicalParameter. THis allow better handling of different model parts and a better representation of the dimensional array.","category":"page"},{"location":"data_structures/#Model-structure","page":"Data Structures","title":"Model structure","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Data structure for velocity models in JUDI.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Model","category":"page"},{"location":"data_structures/#JUDI.Model","page":"Data Structures","title":"JUDI.Model","text":"Model(n, d, o, m; epsilon=nothing, delta=nothing, theta=nothing,\n phi=nothing, rho=nothing, qp=nothing, vs=nothing, nb=40)\n\nThe parameters n, d, o and m are mandatory, whith nb and other physical parameters being optional input arguments.\n\nwhere\n\nm: velocity model in slowness squared (s^2/km^2)\n\nepsilon: Epsilon thomsen parameter ( between -1 and 1)\n\ndelta: Delta thomsen parameter ( between -1 and 1 and delta < epsilon)\n\ntheta: Anisotopy dip in radian\n\nphi: Anisotropy asymuth in radian\n\nrho: density (g / m^3)\n\nqp: P-wave attenuation for visco-acoustic models\n\nvs: S-wave velocity for elastic models.\n\nnb: Number of ABC points\n\n\n\n\n\n","category":"function"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Accessible fields include all of the above parameters p.n, p.d, p.o, p.data. Additionaly, arithmetic operation are all impemented such as addition, multiplication, broadcasting and indexing. Linear algebra operation are implemented as well but will return a standard Julia vector if the matrix used is external to JUDI.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Access fields:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Accessible fields include all of the above parameters, which can be accessed as follows:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"# Access model\nmodel.m\n\n# Access number of grid points\nmodel.n","category":"page"},{"location":"data_structures/#Geometry-structure","page":"Data Structures","title":"Geometry structure","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"JUDI's geometry structure contains the information of either the source or the receiver geometry. Construct an (in-core) geometry object for either a source or receiver set up:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":" Geometry","category":"page"},{"location":"data_structures/#JUDI.Geometry","page":"Data Structures","title":"JUDI.Geometry","text":"GeometryIC\n xloc::Array{Array{T, 1},1}\n yloc::Array{Array{T, 1},1}\n zloc::Array{Array{T, 1},1}\n dt::Array{T,1}\n nt::Array{Integer,1}\n t::Array{T,1}\n\nGeometry structure for seismic sources or receivers. Each field is a cell array, where individual cell entries contain values or arrays with coordinates and sampling information for the corresponding shot position. The first three entries are in meters and the last three entries in milliseconds.\n\nGeometryOOC{T} <: Geometry{T} container::Array{SegyIO.SeisCon,1} dt::Array{T,1} nt::Array{Integer,1} t::Array{T,1} nrec::Array{Integer,1} key::String segydepthkey::String\n\nConstructors\n\nOnly pass dt and n and automatically set t:\n\nGeometry(xloc, yloc, zloc; dt=[], nt=[])\n\nPass single array as coordinates/parameters for all nsrc experiments:\n\nGeometry(xloc, yloc, zloc, dt=[], nt=[], nsrc=1)\n\nCreate geometry structure for either source or receivers from a SegyIO.SeisBlock object. segy_depth_key is the SegyIO keyword that contains the depth coordinate and key is set to either source for source geometry or receiver for receiver geometry:\n\nGeometry(SeisBlock; key=\"source\", segy_depth_key=\"\")\n\nCreate geometry structure for from a SegyIO.SeisCon object (seismic data container):\n\nGeometry(SeisCon; key=\"source\", segy_depth_key=\"\")\n\nExamples\n\n(1) Set up receiver geometry for 2D experiment with 4 source locations and 80 fixed receivers:\n\nxrec = range(100,stop=900,length=80)\nyrec = range(0,stop=0,length=80)\nzrec = range(50,stop=50,length=80)\ndt = 4f0\nt = 1000f0\n\nrec_geometry = Geometry(xrec, yrec, zrec; dt=dt, t=t, nsrc=4)\n\n(2) Set up corresponding source geometry (coordinates can be of type linspace or regular arrays):\n\nxsrc = [200,400,600,800]\nysrc = [0,0,0,0]\nzsrc = [50,50,50,50]\n\nsrc_geometry = Geometry(xsrc, ysrc, zsrc; dt=dt, t=t, nsrc=4)\n\n(3) Read source and receiver geometries from SEG-Y file:\n\nusing SegyIO\nseis_block = segy_read(\"test_file.segy\")\nrec_geometry = Geometry(seis_block; key=\"receiver\", segy_depth_key=\"RecGroupElevation\")\nsrc_geometry = Geometry(seis_block; key=\"source\", segy_depth_key=\"SourceDepth\")\n\nCheck the seis_block's header entries to findall out which keywords contain the depth coordinates. The source depth keyword is either SourceDepth or SourceSurfaceElevation. The receiver depth keyword is typically RecGroupElevation.\n\n(4) Read source and receiver geometries from out-of-core SEG-Y files (for large data sets). Returns an out-of-core geometry object GeometryOOC without the source/receiver coordinates, but a lookup table instead:\n\nusing SegyIO\nseis_container = segy_scan(\"/path/to/data/directory\",\"filenames\",[\"GroupX\",\"GroupY\",\"RecGroupElevation\",\"SourceDepth\",\"dt\"])\nrec_geometry = Geometry(seis_container; key=\"receiver\", segy_depth_key=\"RecGroupElevation\")\nsrc_geometry = Geometry(seis_container; key=\"source\", segy_depth_key=\"SourceDepth\")\n\n\n\n\n\n","category":"type"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"From the optional arguments, you have to pass (at least) two of dt, nt and t. The third value is automatically determined and set from the two other values. a Geometry can be constructed in a number of different ways for in-core and out-of-core cases. Check our examples and the source for additional details while the documentation is being extended.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Access fields:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Accessible fields include all of the above parameters, which can be accessed as follows:","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"# Access cell arrays of x coordinates:\ngeometry.xloc\n\n# Access x coordinates of the i-th source location\ngeometry.xloc[i]\n\n# Access j-th receiver location (in x) of the i-th source location\ngeometry.xloc[i][j]","category":"page"},{"location":"data_structures/#Geometry-utilities","page":"Data Structures","title":"Geometry utilities","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"A few utilities to manipulates geometries are provided as well.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"super_shot_geometry\nreciprocal_geom","category":"page"},{"location":"data_structures/#JUDI.super_shot_geometry","page":"Data Structures","title":"JUDI.super_shot_geometry","text":"super_shot_geometry(Geometry)\n\nMerge all the sub-geometries 1:get_nsrc(Geometry) into a single supershot geometry\n\n\n\n\n\n","category":"function"},{"location":"data_structures/#JUDI.reciprocal_geom","page":"Data Structures","title":"JUDI.reciprocal_geom","text":"reciprocal_geom(sourceGeom, recGeom)\n\nApplies reciprocity to the par of geometries sourceGeom and recGeom where each source becomes a receiver and each receiver becomes a source.\n\nThis method expects:\n\nBoth geometries to be In Core. If the geometries are OOC they will be converted to in core geometries\nThe metadata to be compatible. In details all the time sampling rates (dt) and recording times (t) must be the same\nThe source to be single point sources. This method will error if a simultaneous sources (multiple poisiton for a single source) are used.\n\n\n\n\n\n","category":"function"},{"location":"data_structures/#Options-structure","page":"Data Structures","title":"Options structure","text":"","category":"section"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"The options structure allows setting several modeling parameters.","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Options","category":"page"},{"location":"data_structures/#JUDI.Options","page":"Data Structures","title":"JUDI.Options","text":"JUDIOptions\n space_order::Integer\n free_surface::Bool\n limit_m::Bool\n buffer_size::AbstractFloat\n save_rate::AbstractFloat\n save_data_to_disk::Bool\n file_path::String\n file_name::String\n sum_padding::Bool\n optimal_checkpointing::Bool\n frequencies::Array\n IC::String\n subsampling_factor::Integer\n dft_subsampling_factor::Integer\n return_array::Bool\n dt_comp::Real\n f0::Real\n\nOptions structure for seismic modeling.\n\nspace_order: finite difference space order for wave equation (default is 8, needs to be multiple of 4)\n\nfree_surface: set to true to enable a free surface boundary condition.\n\nlimit_m: for 3D modeling, limit modeling domain to area with receivers (saves memory)\n\nbuffer_size: if limit_m=true, define buffer area on each side of modeling domain (in meters)\n\nsave_data_to_disk: if true, saves shot records as separate SEG-Y files\n\nfile_path: path to directory where data is saved\n\nfile_name: shot records will be saved as specified file name plus its source coordinates\n\nsum_padding: when removing the padding area of the gradient, sum into boundary rows/columns for true adjoints\n\noptimal_checkpointing: instead of saving the forward wavefield, recompute it using optimal checkpointing\n\nfrequencies: calculate the FWI/LS-RTM gradient in the frequency domain for a given set of frequencies\n\nsubsampling_factor: compute forward wavefield on a time axis that is reduced by a given factor (default is 1)\n\ndft_subsampling_factor: compute on-the-fly DFTs on a time axis that is reduced by a given factor (default is 1)\n\nIC: Imaging condition. Options are 'as, isic, fwi' with \"as\" for adjoint state, isic for the inverse scattering imaging condition and FWI for the complement of isic (i.e isic + fwi = as)\n\nisic: Inverse scattering imaging condition. Deprecated, use IC=\"isic\" instead.\n\nreturn_array: return data from nonlinear/linear modeling as a plain Julia array.\n\ndt_comp: overwrite automatically computed computational time step with this value.\n\nf0: define peak frequency.\n\nConstructor\n\nAll arguments are optional keyword arguments with the following default values:\n\nOptions(;space_order=8, free_surface=false,\n limit_m=false, buffer_size=1e3,\n save_data_to_disk=false, file_path=\"\",\n file_name=\"shot\", sum_padding=false,\n optimal_checkpointing=false,\n num_checkpoints=nothing, checkpoints_maxmem=nothing,\n frequencies=[], isic=false,\n subsampling_factor=1, dft_subsampling_factor=1, return_array=false,\n dt_comp=nothing, f0=0.015f0)\n\n\n\n\n\n","category":"function"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"notes","category":"page"},{"location":"data_structures/","page":"Data Structures","title":"Data Structures","text":"Option has been renamed JUDIOptions as of JUDI version 4.0 to avoid potential (and exisiting) conflicts wiht other packages exporting an Options structure.","category":"page"},{"location":"abstract_vectors/#Abstract-Vectors","page":"Abstract vectors","title":"Abstract Vectors","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"JUDI provides abstract vector types that encapsulate seismic related objects. In particula, JUDI defines thre main types for seismic data judiVector, full time-space wavefields judiWavefield and extended source weights judiWeights.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Pages = [\"abstract_vectors.md\"]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"At the core of JUDI's vector types is the abstract type judiMultiSourceVector that represent a dimensionalized Vector{Array} where each sub-array correspond to a single source. All JUDI vector types inhert from this abstract type that implements most of the arithmetic and julia core utilities. As an abstract types, judiMultiSourceVector should not be instantiated but new concrete types based on it should be created if one desire to create its own JUDI multi-source vector type.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"All sub-type of judiMultiSourceVector must implement the following methods to be compatible with JUDI. The following JUDI core types are examples of sub-types.","category":"page"},{"location":"abstract_vectors/#judiVector","page":"Abstract vectors","title":"judiVector","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"The class judiVector is the basic data structure for seismic shot records or seismic sources. From JUDI's perspective, both are treated the same and can be multiplied with modeling operators.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"In the most basic way, judiVectors are contstructed from a Geometry object (containing either source or receiver geometry) and a cell array of data:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"judiVector(geometry::Geometry, data::Array{T, N}) where {T, N}","category":"page"},{"location":"abstract_vectors/#JUDI.judiVector-Union{Tuple{N}, Tuple{T}, Tuple{Geometry, Array{T, N}}} where {T, N}","page":"Abstract vectors","title":"JUDI.judiVector","text":"judiVector\n geometry::Geometry\n data\n\nAbstract vector for seismic data. This vector-like structure contains the geometry and data for either\nreceiver data (shot records) or source data (wavelets).\n\nConstructors\n\nConstruct vector from Geometry structure and cell array of shot records or wavelets. The data keyword\ncan also be a single (non-cell) array, in which case the data is the same for all source positions:\n\njudiVector(geometry, data)\n\nConstruct vector for observed data from SegyIO.SeisBlock. segy_depth_key is the SegyIO keyword \nthat contains the receiver depth coordinate:\n\njudiVector(SegyIO.SeisBlock; segy_depth_key=\"RecGroupElevation\")\n\nConstruct vector for observed data from out-of-core data container of type SegyIO.SeisCon:\n\njudiVector(SegyIO.SeisCon; segy_depth_key=\"RecGroupElevation\")\n\nExamples\n\n(1) Construct data vector from Geometry structure and a cell array of shot records:\n\ndobs = judiVector(rec_geometry, shot_records)\n\n(2) Construct data vector for a seismic wavelet (can be either cell arrays of individual\nwavelets or a single wavelet as an array):\n\nq = judiVector(src_geometry, wavelet)\n\n(3) Construct data vector from SegyIO.SeisBlock object:\n\nusing SegyIO\nseis_block = segy_read(\"test_file.segy\")\ndobs = judiVector(seis_block; segy_depth_key=\"RecGroupElevation\")\n\n(4) Construct out-of-core data vector from SegyIO.SeisCon object (for large SEG-Y files):\n\nusing SegyIO\nseis_container = segy_scan(\"/path/to/data/directory\",\"filenames\",[\"GroupX\",\"GroupY\",\"RecGroupElevation\",\"SourceDepth\",\"dt\"])\ndobs = judiVector(seis_container; segy_depth_key=\"RecGroupElevation\")\n\n\n\n\n\n","category":"method"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields (in-core data containers):","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access i-th shot record\nx.data[i]\n\n# Extract judiVector for i-th shot\nx1 = x[i]\n\n# Access j-th receiver location of i-th shot\nx.geometry.xloc[i][j]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields (out-of-core data containers):","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access data container of i-th shot\nx.data[i]\n\n# Read data from i-th shot into memory\nx.data[i][1].data\n\n# Access out-of-core geometry\nx.geometry\n\n# Load OOC geometry into memory\nGeometry(x.geometry)","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"In-core judiVectors can be used like regular Julia arrays and support common operations such as:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"x = judiVector(geometry, data)\n\n# Size (as if all data was vectorized)\nsize(x)\n\n# Norms\nnorm(x)\n\n# Inner product\ndot(x, x)\n\n# Addition, subtraction (geometries must match)\ny = x + x\nz = x - y\n\n# Scaling\nα = 2f0\ny = x * α\n\n# Concatenate\ny = vcat(x, x)","category":"page"},{"location":"abstract_vectors/#judiWavefield","page":"Abstract vectors","title":"judiWavefield","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Abstract vector class for wavefields. ","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":" judiWavefield","category":"page"},{"location":"abstract_vectors/#JUDI.judiWavefield","page":"Abstract vectors","title":"JUDI.judiWavefield","text":"judiWavefield nsrc::Integer dt::AbstractFloat data\n\nAbstract vector for seismic wavefields.\n\nConstructors\n\nConstruct wavefield vector from an info structure, a cell array of wavefields and the computational \ntime step dt:\n\njudiWavefield(nsrc, dt, data)\n\n\n\n\n\n","category":"type"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access wavefield from i-th shot location\nu.data[i]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Supports some basic arithmetric operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Size \nsize(u)\n\n# Norms\nnorm(u)\n\n# Inner product \ndot(u, y)\n\n# Addition, subtraction\nv = u + u\nz = u - v\n\n# Absolute value\nabs(u)\n\n# Concatenation\nv = vcat(u, u)","category":"page"},{"location":"abstract_vectors/#judiRHS","page":"Abstract vectors","title":"judiRHS","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Abstract vector class for a right-hand-side (RHS). A RHS has the size of a full wavefield, but only contains the data of the source wavelet of shot records in memory, as well as the geometry information of where the data is injected during modeling.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"rhs = judiRHS(geometry, data)","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"A JUDI RHS can also be constructed by multplying a judiVector and the corresponding transpose of a judiProjection operator:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"rhs1 = Ps'*q\nrhs2 = Pr'*d_obs","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"where Ps and Pr are judiProjection operators for sources and receivers respectively and q and d_obs are judiVectors with the source and receiver data.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Accessible fields include:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Source/receiver data\nrhs.data\n\n# Source/receiver geometry\nrhs.geometry\n\n# Info structure\nrhs.info","category":"page"},{"location":"abstract_vectors/#judiWeights","page":"Abstract vectors","title":"judiWeights","text":"","category":"section"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Abstract vector class for extended source weights. The weights for each shot location have the dimensions of the model (namely model.n).","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Construction:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"judiWeights(weights::Array{T, N}; nsrc=1, vDT::DataType=Float32) where {T<:Real, N}","category":"page"},{"location":"abstract_vectors/#JUDI.judiWeights-Union{Tuple{Array{T, N}}, Tuple{N}, Tuple{T}} where {T<:Real, N}","page":"Abstract vectors","title":"JUDI.judiWeights","text":"judiWeights\n nsrc\n weights\n\nAbstract vector for weighting an extended source, which is injected at every grid point, as weighted by this vector. Constructors ============ Construct vector cell array of weights. The weights keyword\ncan also be a single (non-cell) array, in which case the weights are the same for all source positions: judiWeights(weights; nsrc=1)\n\n\n\n\n\n","category":"method"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Parameters:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"weights: Cell array with one cell per shot location. Each cell contains a 2D/3D Julia array with the weights for the spatially extended source. Alternatively: pass a single Julia array which will be used for all source locations.","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Access fields:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"# Access weights of i-th shot locatoin\nw.weights[i]","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Operations:","category":"page"},{"location":"abstract_vectors/","page":"Abstract vectors","title":"Abstract vectors","text":"Supports the same arithmetric operations as a judiVector.","category":"page"},{"location":"#The-Julia-Devito-Inversion-framework-(JUDI.jl)","page":"Home","title":"The Julia Devito Inversion framework (JUDI.jl)","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"JUDI is a framework for large-scale seismic modeling and inversion and designed to enable rapid translations of algorithms to fast and efficient code that scales to industry-size 3D problems. Wave equations in JUDI are solved with Devito, a Python domain-specific language for automated finite-difference (FD) computations. ","category":"page"},{"location":"#Docs-overview","page":"Home","title":"Docs overview","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"This documentation provides an overview over JUDI's basic data structures and abstract operators:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Installation: Install guidlines for JUDI and compilers.\nGetting Started: A few simple guided examples to get familiar with JUDI.\nData structures: Explains the Model, Geometry and Options data structures and how to set up acquisition geometries.\nAbstract Vectors: Documents JUDI's abstract vector classes judiVector, judiWavefield, judiRHS, judiWeights and judiExtendedSource.\nLinear Operators: Lists and explains JUDI's abstract linear operators judiModeling, judiJacobian, judiProjection and judiLRWF.\nInput/Output: Read SEG-Y data and set up judiVectors for shot records and sources. Read velocity models.\nHelper functions: API of functions that make your life easier.\nSeismic Inversion: Inversion utility functions to avoid recomputation and memry overhead.\nSeismic Preconditioners: Basic preconditioners for seismic imaging.\npysource package: API reference for the propagators implementation with Devito in python. The API is the backend of JUDI handled with PyCall.","category":"page"}] } diff --git a/dev/tutorials/01_intro/index.html b/dev/tutorials/01_intro/index.html index 1150e973d..632ede1d5 100644 --- a/dev/tutorials/01_intro/index.html +++ b/dev/tutorials/01_intro/index.html @@ -66,4 +66,4 @@ ylabel("Recording time (s)") title("xsrc=$(1f-3xsrc[2*i][1])km") end -tight_layout()

      png

      +tight_layout()

      png

      diff --git a/dev/tutorials/02_fwi_example_NLopt/index.html b/dev/tutorials/02_fwi_example_NLopt/index.html index c02071c3b..68c6545cd 100644 --- a/dev/tutorials/02_fwi_example_NLopt/index.html +++ b/dev/tutorials/02_fwi_example_NLopt/index.html @@ -93,4 +93,4 @@ (22470.578125, [0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136, 0.4444444477558136 … 0.05060886426348713, 0.05044609939336615, 0.050292761170062046, 0.05017357884927398, 0.05011064413011435, 0.05011993876926194, 0.05020965433880526, 0.05038047472521424, 0.05062740429803987, 0.05094217839179781], :MAXEVAL_REACHED)

      We plot the final velocity model after 15 function evaluations:

      imshow(sqrt.(1f0./reshape(minx, model0.n))', cmap="GnBu", extent=(0,10,3,0), vmin=1.5, vmax=5.4); title("FWI with L-BFGS")
       xlabel("Lateral position [km]");
      -ylabel("Depth [km]");

      png

      +ylabel("Depth [km]");

      png

      diff --git a/dev/tutorials/03_constrained_fwi/index.html b/dev/tutorials/03_constrained_fwi/index.html index 05d7313d5..b38c42639 100644 --- a/dev/tutorials/03_constrained_fwi/index.html +++ b/dev/tutorials/03_constrained_fwi/index.html @@ -1825,4 +1825,4 @@ title("TV FWI Residual sz=$(zsrc[index]) || True sz=$(zsrc[index]) || FWI sz=$(zsrc[index]) (flipped)"); tight_layout() end
      make_plot (generic function with 1 method)

      Data for the 1st shot, generated in the initial and FWI models

      make_plot(1)

      png

      Data for the 4th shot, generated in the initial and FWI models

      make_plot(4)

      png

      Data for the 8th shot, generated in the initial and FWI models

      make_plot(8)

      png

      Remove workers

      rmprocs(workers());
      ┌ Warning: rmprocs: process 1 not removed
      -└ @ Distributed /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.6/Distributed/src/cluster.jl:1038
      +└ @ Distributed /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.6/Distributed/src/cluster.jl:1038 diff --git a/dev/tutorials/04_judi_leading_edge_tutorial/index.html b/dev/tutorials/04_judi_leading_edge_tutorial/index.html index bf859f685..ec0dbb83d 100644 --- a/dev/tutorials/04_judi_leading_edge_tutorial/index.html +++ b/dev/tutorials/04_judi_leading_edge_tutorial/index.html @@ -101,4 +101,4 @@ xlabel("Iteration number", size=12) ylabel("Misfit", size=12) xlim(1, 10) -show()

      Figure 3: Normalized function values for the FWI inversion example with stochastic gradient descent and the Gauss-Newton method.

      An alternative to (Gauss–)Newton methods are quasi-Newton methods, which build up an approximation of the Hessian from previous gradients only and require no additional PDE solves or matrix inversions. Implementing an efficient and correct version of this method, such as the L-BFGS algorithm, exceeds a few lines of code and we therefore leave this exercise to the reader. Instead of implementing more complicated algorithms by hand, it is also possible to interface third-party Julia optimization libraries and an example for this is given in the notebook fwiexampleNLopt.ipynb.

      Even though all examples shown here are two-dimensional, in order to make them reproducible on a laptop or desktop PC, JUDI can be used for 3D modeling and inversion without having to change the code, since the number of dimensions are automatically inferred from the velocity model and data dimensions.

      Conclusions

      In this final part of our FWI tutorial series, we demonstrated how to set up basic optimization algorithms for waveform inversion using JUDI. The methods shown here are all gradient based and differ in the way how update directions for the velocity model are computed. Our numerical examples can serve for the reader as a basis for developing more advanced FWI workflows, which usually include additional data preprocessing, frequency continuation techniques or further model constraints.

      Acknowledgments

      This research was carried out as part of the SINBAD II project with the support of the member organizations of the SINBAD Consortium. This work was financially supported in part by EPSRC grant EP/L000407/1 and the Imperial College London Intel Parallel Computing Centre.

      References

      Aminzadeh, F., Brac, J., and Kunz, T., 1997. 3D Salt and Overthrust models. SEG/EAGE Modeling Series, No. 1: Distribution CD of Salt and Overthrust models, SEG Book Series Tulsa, Oklahoma.

      Bezanson, J., Karpinski, S., Shah, V. B., and Edelman, A., 2012, Julia: A fast dynamic language for technical computing: CoRR. Retrieved from http://arxiv.org/abs/1209.5145

      Bunks, C., Saleck, F. M., Zaleski, S., and Chavent, G., 1995, Multiscale seismic waveform inversion: GEOPHYSICS, 60, 1457–1473. doi:10.1190/1.1443880

      Johnson, S., 2017, Calling python functions from the julia language: GitHub repository. https://github.com/JuliaPy/PyCall.jl; GitHub.

      Lange, M., Kukreja, N., Louboutin, M., Luporini, F., Zacarias, F. V., Pandolfo, V., … Gorman, G., 2016, Devito: Towards a generic finite difference DSL using symbolic python: 6th workshop on python for high-performance and scientific computing. doi:10.1109/PyHPC.2016.9

      Leeuwen, T. van, Aravkin, A. Y., Calandra, H., and Herrmann, F. J., 2013, In which domain should we measure the misfit for robust full waveform inversion? EAGE annual conference proceedings. doi:10.3997/2214-4609.20130839

      Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2017, Full-waveform inversion - part 1: Forward modeling: Retrieved from https://www.slim.eos.ubc.ca/Publications/Private/Submitted/2017/louboutin2017fwi/louboutin2017fwi.html

      Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2018, Full-waveform inversion - part 2: Adjoint modeling:

      Nocedal, J., and Wright, S., 2009, Numerical optimization: (2nd ed.). Springer.

      Paige, C. C., and Saunders, M. A., 1982, LSQR: An algorithm for sparse linear equations and sparse least squares: ACM Trans. Math. Softw., 8, 43–71. doi:10.1145/355984.355989

      Peters, B., and Herrmann, F. J., 2017, Constraints versus penalties for edge-preserving full-waveform inversion: The Leading Edge, 36, 94–100. doi:10.1190/tle36010094.1

      Warner, M., and Guasch, L., 2014, Adaptive waveform inversion: Theory: In SEG technical program expanded abstracts 2014 (pp. 1089–1093). doi:10.1190/segam2014-0371.1

      +show()

      Figure 3: Normalized function values for the FWI inversion example with stochastic gradient descent and the Gauss-Newton method.

      An alternative to (Gauss–)Newton methods are quasi-Newton methods, which build up an approximation of the Hessian from previous gradients only and require no additional PDE solves or matrix inversions. Implementing an efficient and correct version of this method, such as the L-BFGS algorithm, exceeds a few lines of code and we therefore leave this exercise to the reader. Instead of implementing more complicated algorithms by hand, it is also possible to interface third-party Julia optimization libraries and an example for this is given in the notebook fwiexampleNLopt.ipynb.

      Even though all examples shown here are two-dimensional, in order to make them reproducible on a laptop or desktop PC, JUDI can be used for 3D modeling and inversion without having to change the code, since the number of dimensions are automatically inferred from the velocity model and data dimensions.

      Conclusions

      In this final part of our FWI tutorial series, we demonstrated how to set up basic optimization algorithms for waveform inversion using JUDI. The methods shown here are all gradient based and differ in the way how update directions for the velocity model are computed. Our numerical examples can serve for the reader as a basis for developing more advanced FWI workflows, which usually include additional data preprocessing, frequency continuation techniques or further model constraints.

      Acknowledgments

      This research was carried out as part of the SINBAD II project with the support of the member organizations of the SINBAD Consortium. This work was financially supported in part by EPSRC grant EP/L000407/1 and the Imperial College London Intel Parallel Computing Centre.

      References

      Aminzadeh, F., Brac, J., and Kunz, T., 1997. 3D Salt and Overthrust models. SEG/EAGE Modeling Series, No. 1: Distribution CD of Salt and Overthrust models, SEG Book Series Tulsa, Oklahoma.

      Bezanson, J., Karpinski, S., Shah, V. B., and Edelman, A., 2012, Julia: A fast dynamic language for technical computing: CoRR. Retrieved from http://arxiv.org/abs/1209.5145

      Bunks, C., Saleck, F. M., Zaleski, S., and Chavent, G., 1995, Multiscale seismic waveform inversion: GEOPHYSICS, 60, 1457–1473. doi:10.1190/1.1443880

      Johnson, S., 2017, Calling python functions from the julia language: GitHub repository. https://github.com/JuliaPy/PyCall.jl; GitHub.

      Lange, M., Kukreja, N., Louboutin, M., Luporini, F., Zacarias, F. V., Pandolfo, V., … Gorman, G., 2016, Devito: Towards a generic finite difference DSL using symbolic python: 6th workshop on python for high-performance and scientific computing. doi:10.1109/PyHPC.2016.9

      Leeuwen, T. van, Aravkin, A. Y., Calandra, H., and Herrmann, F. J., 2013, In which domain should we measure the misfit for robust full waveform inversion? EAGE annual conference proceedings. doi:10.3997/2214-4609.20130839

      Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2017, Full-waveform inversion - part 1: Forward modeling: Retrieved from https://www.slim.eos.ubc.ca/Publications/Private/Submitted/2017/louboutin2017fwi/louboutin2017fwi.html

      Louboutin, M., Witte, P. A., Lange, M., Kukreja, N., Luporini, F., Gorman, G., and Herrmann, F. J., 2018, Full-waveform inversion - part 2: Adjoint modeling:

      Nocedal, J., and Wright, S., 2009, Numerical optimization: (2nd ed.). Springer.

      Paige, C. C., and Saunders, M. A., 1982, LSQR: An algorithm for sparse linear equations and sparse least squares: ACM Trans. Math. Softw., 8, 43–71. doi:10.1145/355984.355989

      Peters, B., and Herrmann, F. J., 2017, Constraints versus penalties for edge-preserving full-waveform inversion: The Leading Edge, 36, 94–100. doi:10.1190/tle36010094.1

      Warner, M., and Guasch, L., 2014, Adaptive waveform inversion: Theory: In SEG technical program expanded abstracts 2014 (pp. 1089–1093). doi:10.1190/segam2014-0371.1

      diff --git a/dev/tutorials/05_custom_misfit/index.html b/dev/tutorials/05_custom_misfit/index.html index 0343e796f..068c7f836 100644 --- a/dev/tutorials/05_custom_misfit/index.html +++ b/dev/tutorials/05_custom_misfit/index.html @@ -281,4 +281,4 @@ result{Float32}(Float32[0.44444445 0.44444445 … 0.053637963 0.052456174; 0.44444445 0.44444445 … 0.053673286 0.052389987; … ; 0.44444445 0.44444445 … 0.05293595 0.05135409; 0.44444445 0.44444445 … 0.052959688 0.051626943], Float32[0.0 0.0 … 0.0014253161 0.0031370732; 0.0 0.0 … 0.0007196072 0.0023543844; … ; 0.0 0.0 … 0.014447195 0.01871568; 0.0 0.0 … 0.013791027 0.017101452], 0.04899293f0, Float32[0.98902273, 0.5962111, 0.5856857, 0.38198355, 0.22556786, 0.17507246, 0.15682286, 0.14212339, 0.11700058, 0.111277334 … 0.079943426, 0.073783785, 0.073261, 0.07614578, 0.05530872, 0.059201777, 0.057410687, 0.04899293, 0.050576232, 0.04932886], Matrix{Float32}[[0.44444445 0.44444445 … 0.05585606 0.05512348; 0.44444445 0.44444445 … 0.05584115 0.055107832; … ; 0.44444445 0.44444445 … 0.055858355 0.05516676; 0.44444445 0.44444445 … 0.05585274 0.05516145]], 63, 40, 40)
      suptitle("FWI result")
      -plot_velocity(reshape(solh_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name="Ideal data, Envelope")

      png

      In this tutorial, we have seen how misfit functions can impact the inversion result and how JUDI provides multiple misfit functions and a flexible interface for custom user inputs. This interface will allow for better application to real worl dataset since, as show in our example suite, JUDI already provide an interface for handling large SegY datasets and interfaces tricially with optimization frameworks

      +plot_velocity(reshape(solh_ideal.x, model0.n)'.^(-.5), model0.d; new_fig=false, name="Ideal data, Envelope")

      png

      In this tutorial, we have seen how misfit functions can impact the inversion result and how JUDI provides multiple misfit functions and a flexible interface for custom user inputs. This interface will allow for better application to real worl dataset since, as show in our example suite, JUDI already provide an interface for handling large SegY datasets and interfaces tricially with optimization frameworks

      diff --git a/dev/tutorials/06_automatic_differentiation/index.html b/dev/tutorials/06_automatic_differentiation/index.html index 3d2fa4512..a3ec60752 100644 --- a/dev/tutorials/06_automatic_differentiation/index.html +++ b/dev/tutorials/06_automatic_differentiation/index.html @@ -155,4 +155,4 @@ -Grads(...) +Grads(...) diff --git a/dev/tutorials/07_preconditionners/index.html b/dev/tutorials/07_preconditionners/index.html index 44f1b5a19..5505d0416 100644 --- a/dev/tutorials/07_preconditionners/index.html +++ b/dev/tutorials/07_preconditionners/index.html @@ -151,4 +151,4 @@ └ @ Core :-1 ┌ Warning: Deprecated model.d, use spacing(model) │  caller = ip:0x0 -└ @ Core :-1

      png

      +└ @ Core :-1

      png

      diff --git a/dev/tutorials/imaging_conditions/index.html b/dev/tutorials/imaging_conditions/index.html index b9daa1d70..3ad76262c 100644 --- a/dev/tutorials/imaging_conditions/index.html +++ b/dev/tutorials/imaging_conditions/index.html @@ -57,4 +57,4 @@ imshow(ni(g_as'), cmap="seismic", aspect="auto", vmin=-1, vmax=1) title("Adjoint State") tight_layout() -display(fig)

      +display(fig)

      diff --git a/dev/tutorials/quickstart/index.html b/dev/tutorials/quickstart/index.html index 15628d227..35871df56 100644 --- a/dev/tutorials/quickstart/index.html +++ b/dev/tutorials/quickstart/index.html @@ -118,4 +118,4 @@ xlabel("Receiver position (m)") ylabel("Time (s)") title("TWRI gradient w.r.t y") -display(fig)

      +display(fig)