Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix wrong dt #11

Merged
merged 1 commit into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TimeProbeSeismic"
uuid = "cd417b76-fc6b-4228-be91-94f5ea77264b"
authors = ["Mathias Louboutin", "Felix J. Herrmann"]
version = "1.1.2"
version = "1.1.3"

[deps]
JUDI = "f3b833dc-6b2e-5b9c-b940-873ed6319979"
Expand All @@ -11,6 +11,6 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[compat]
JUDI = "^3.3.3"
JUDI = "3.4.1"
Reexport = "1"
julia = "1.6"
35 changes: 16 additions & 19 deletions scripts/compare_vectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
# Author: mlouboutin3@gatech.edu
# Date: February 2021
#
using TimeProbeSeismic, PyPlot, LinearAlgebra, HDF5, Images
using TimeProbeSeismic, PyPlot, LinearAlgebra, HDF5, Images, SlimPlotting

# set_devito_config("log-level", "PERF")

# Load starting model
~isfile(datadir("models/", "overthrust_model.h5")) && run(`curl -L ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_model_2D.h5 --create-dirs -o $(datadir("models", "overthrust_model.h5"))`)
n, d, o, m0, m = read(h5open(datadir("models", "overthrust_model.h5"), "r"), "n", "d", "o", "m0", "m")
~isfile(datadir("models/", "overthrust_model.h5")) && run(`curl -L ftp://slim.gatech.edu/data/SoftwareRelease/WaveformInversion.jl/2DFWI/overthrust_model_2D.h5 --create-dirs -o $(datadir("models/", "overthrust_model.h5"))`)
n, d, o, m0, m = read(h5open(datadir("models/", "overthrust_model.h5"), "r"), "n", "d", "o", "m0", "m")

n = Tuple(n)
o = Tuple(o)
Expand All @@ -33,7 +35,7 @@ dtD = 4f0 # receiver sampling interval [ms]
recGeometry = Geometry(xrec, yrec, zrec; dt=dtD, t=timeD, nsrc=nsrc)

# Set up source geometry (cell array with source locations for each shot)
xsrc = div(n[1], 2) * d[1]
xsrc = div(n[1], 4) * d[1]
ysrc = 0f0
zsrc = 12.5f0

Expand All @@ -45,25 +47,19 @@ f0 = 0.010f0 # kHz
wavelet = ricker_wavelet(timeD, dtD, f0)
q = judiVector(srcGeometry, wavelet)

# Set up info structure for linear operators
ntComp = get_computational_nt(srcGeometry, recGeometry, model)
info = Info(prod(n), nsrc, ntComp)

###################################################################################################
# Write shots as segy files to disk
opt = Options(space_order=16)

# Setup operators
F = judiModeling(info, model, srcGeometry, recGeometry; options=opt)
F0 = judiModeling(info, model0, srcGeometry, recGeometry; options=opt)
F = judiModeling(model, srcGeometry, recGeometry; options=opt)
F0 = judiModeling(model0, srcGeometry, recGeometry; options=opt)
J = judiJacobian(F0, q)

# data
# Nonlinear modeling
dobs = F*q
d0 = F0*q
residual = d0 - dobs
δd = J*dm

# gradient
g = J'*residual
Expand All @@ -80,25 +76,25 @@ function fourierJ(F0, q, ps)
return J
end

ge = Array{Any}(undef, 3, 8)
ge = Array{Any}(undef, 4, 8)

for (p, mode) in enumerate([:Fourier, :QR, :Rademacher])
for (p, mode) in enumerate([:Fourier, :QR, :Rademacher, :hutchpp])
for ps=1:8
Jp = mode == :Fourier ? fourierJ(F0, q, 2^ps) : judiJacobian(F0, q, 2^ps, dobs; mode=mode)
ge[p, ps] = Jp'*residual
end
end

for (p, mode) in enumerate([:Fourier, :QR, :Rademacher])
for (p, mode) in enumerate([:Fourier, :QR, :Rademacher, :hutchpp])
clip = maximum(g)/10

figure(figsize=(15, 10))
subplot(331)
imshow(g', cmap="seismic", vmin=-clip, vmax=clip, aspect="auto")
plot_simage(g'; cmap="seismic", new_fig=false)
title("Reference")
for ps=1:8
subplot(3,3,ps+1)
imshow(ge[p, ps]', cmap="seismic", vmin=-clip, vmax=clip, aspect="auto")
plot_simage(ge[p, ps]'; cmap="seismic", new_fig=false)
title("$(mode) r=$(2^ps)")
end
tight_layout()
Expand All @@ -108,13 +104,14 @@ for (p, mode) in enumerate([:Fourier, :QR, :Rademacher])

figure(figsize=(15, 10))
subplot(331)
imshow(g', cmap="seismic", vmin=-clip, vmax=clip, aspect="auto")
plot_simage(g'; cmap="seismic", new_fig=false)
title("Reference")
for ps=1:8
subplot(3,3,ps+1)
imshow(g' - ge[p, ps]', cmap="seismic", vmin=-clip, vmax=clip, aspect="auto")
plot_simage(g' - ge[p, ps]'; cmap="seismic", new_fig=false)
title("$(mode) Difference r=$(2^ps)")
end
tight_layout()
savefig(plotsdir("geo-pros-paper/", "Error-$(mode).pdf"), bbox_inches=:tight, dpi=150)
end

18 changes: 11 additions & 7 deletions scripts/overthrust_probing_vectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,27 @@ title(L"$d_{obs} d_{obs}^\top$")

timea = 0:4f0:3000f0

fig, axs = subplots(5, 3, sharex=:col, sharey=:row, gridspec_kw=Dict(:hspace=> 0, :wspace=> 0))
fig, axs = subplots(5, 4, sharex=:col, sharey=:row, gridspec_kw=Dict(:hspace=> 0, :wspace=> 0))
for (i, ps)=enumerate([4, 16, 32, 64, 256])
@show i
# QR
Q = qr_data(dobs.data[1], ps)
Q = qr_data(dobs.data[1], ps; mode=:QR)
axs[i, 1].imshow(Q*Q',vmin=-.05, vmax=.05, cmap="seismic", aspect="auto")
i == 1 && axs[i, 1].set_title(L"$\mathbf{Q}\mathbf{Q}^\top$")
axs[i, 1].set_ylabel("r=$(ps)", rotation=0, labelpad=20)
# hutchpp
Q = qr_data(dobs.data[1], ps; mode=:hutchpp)
axs[i, 2].imshow(Q*Q',vmin=-.05, vmax=.05, cmap="seismic", aspect="auto")
i == 1 && axs[i, 2].set_title(L"$\mathbf{Q}\mathbf{Q}^\top$")
# gaussian
E = rand([-1f0, 1f0], length(timea), ps)/sqrt(ps)
axs[i, 2].imshow(E*E',vmin=-.25, vmax=.25, cmap="seismic", aspect="auto")
i == 1 && axs[i, 2].set_title(L"$\mathbf{Z}\mathbf{Z}^\top$")
Q = qr_data(dobs.data[1], ps; mode=:Gaussian)
axs[i, 3].imshow(Q*Q',vmin=-.25, vmax=.25, cmap="seismic", aspect="auto")
i == 1 && axs[i, 3].set_title(L"$\mathbf{Z}\mathbf{Z}^\top$")
# DFT
freq = select_frequencies(q_dist; fmin=0.002, fmax=0.04, nf=ps)
F = exp.(-2*im*pi*timea*freq')/sqrt(ps)
axs[i, 3].imshow(real.(F*F'),vmin=-.5, vmax=.5, cmap="seismic", aspect="auto")
i == 1 && axs[i, 3].set_title(L"$\mathbf{F}\mathbf{F}^\top$")
axs[i, 4].imshow(real.(F*F'),vmin=-.5, vmax=.5, cmap="seismic", aspect="auto")
i == 1 && axs[i, 4].set_title(L"$\mathbf{F}\mathbf{F}^\top$")
end
plt.setp(plt.gcf().get_axes(), xticks=[], yticks=[])

Expand Down
1 change: 1 addition & 0 deletions src/TimeProbeSeismic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function __init__()
copy!(si, pyimport("sensitivity"))
copy!(ut, pyimport("utils"))
copy!(np, pyimport("numpy"))
set!(dv."configuration", "autopadding", false)
end

# Propagators
Expand Down
7 changes: 4 additions & 3 deletions src/interface.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

function forward(model::AbstractModel, q::judiVector, dobs::judiVector; options=Options(), ps=16, modelPy=nothing, mode=:QR)
dt_Comp = get_dt(model)
# Python model
isnothing(modelPy) && (modelPy = devito_model(model, options))
dt_Comp = convert(Float32, modelPy."critical_dt")
# Interpolate input data to computational grid
q_data = time_resample(make_input(q), q.geometry, dt_Comp)
d_data = time_resample(make_input(dobs), dobs.geometry, dt_Comp)
Expand All @@ -21,9 +21,9 @@ function forward(model::AbstractModel, q::judiVector, dobs::judiVector; options=
end

function born(model::AbstractModel, q::judiVector, dobs::judiVector, dm; options=Options(), ps=16, modelPy=nothing)
dt_Comp = get_dt(model)
# Python model
isnothing(modelPy) && (modelPy = devito_model(model, options; dm=dm))
dt_Comp = convert(Float32, modelPy."critical_dt")

# Interpolate input data to computational grid
q_data = time_resample(make_input(q), q.geometry, dt_Comp)
Expand All @@ -46,9 +46,10 @@ end

function backprop(model::AbstractModel, residual::judiVector, Q::Array{Float32}, eu::PyObject;
options=Options(), ps=16, modelPy=nothin, offsets=0f0, pe=nothing)
dt_Comp = get_dt(model)
# Python mode
isnothing(modelPy) && (modelPy = devito_model(model, options))
dt_Comp = convert(Float32, modelPy."critical_dt")

# Interpolate input data to computational grid
d_data = time_resample(make_input(residual), residual.geometry, dt_Comp)

Expand Down
25 changes: 14 additions & 11 deletions src/judi.jl
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
time_resample(q::judiVector, dt) = begin @assert q.nsrc == 1 ;time_resample(make_input(q), q.geometry.dt[1], dt) end

#########################
struct judiJacobianP{D, O, FT} <: judiAbstractJacobian{D, O, FT}
m::AbstractSize
n::AbstractSize
F::FT
q::judiMultiSourceVector
r::Integer
mode::Symbol
probing_mode::Symbol
dobs::judiVector
offsets::Union{D, Vector{D}}
end

_accepted_modes = (:QR, :Rademacher, :Gaussian, :hutchpp)

# Jacobian with probing
function judiJacobian(F::judiComposedPropagator{D, O}, q::judiMultiSourceVector, r::Integer, dobs::judiMultiSourceVector;
options=nothing, mode=:QR, offsets=0f0) where {D, O}
mode ∈ [:QR, :Rademacher, :Gaussian] || throw(ArgumentError("Probing vector mode unrecognized, must be `:QR` or `:Rademacher`"))
mode ∈ _accepted_modes || throw(ArgumentError("Probing vector mode unrecognized, must be in $(_accepted_modes)"))
update!(F.F.options, options)
return judiJacobianP{D, :born, typeof(F)}(F.m, space(F.model.n), F, q, r, mode, dobs, asvec(offsets))
end

asvec(x::T) where T<:Number = x
asvec(x) = collect(x)

adjoint(J::judiJacobianP{D, O, FT}) where {D, O, FT} = judiJacobianP{D, adjoint(O), FT}(J.n, J.m, J.F, J.q, J.r, J.mode, J.dobs, J.offsets)
getindex(J::judiJacobianP{D, O, FT}, i) where {D, O, FT} = judiJacobianP{D, O, FT}(J.m[i], J.n[i], J.F[i], J.q[i], J.r, J.mode, J.dobs[i], J.offsets)
adjoint(J::judiJacobianP{D, O, FT}) where {D, O, FT} = judiJacobianP{D, adjoint(O), FT}(J.n, J.m, J.F, J.q, J.r, J.probing_mode, J.dobs, J.offsets)
getindex(J::judiJacobianP{D, O, FT}, i) where {D, O, FT} = judiJacobianP{D, O, FT}(J.m[i], J.n[i], J.F[i], J.q[i], J.r, J.probing_mode, J.dobs[i], J.offsets)

process_input_data(::judiJacobianP{D, :born, FT}, q::dmType{D}) where {D<:Number, FT} = q

Expand Down Expand Up @@ -56,7 +56,7 @@ function propagate(J::judiJacobianP{D, :adjoint_born, FT}, residual::AbstractArr

model_loc = get_model(J.q.geometry, residual.geometry, J.model, J.options)
modelPy = devito_model(model_loc, J.options)
_, Q, eu = forward(model_loc, J.q, J.dobs; ps=J.r, options=J.options, modelPy=modelPy, mode=J.mode)
_, Q, eu = forward(model_loc, J.q, J.dobs; ps=J.r, options=J.options, modelPy=modelPy, mode=J.probing_mode)
ge = backprop(model_loc, residual, Q, eu; options=J.options, modelPy=modelPy, offsets=J.offsets)
J.options.limit_m && (ge = extend_gradient(J.model, model_loc, ge))

Expand All @@ -72,12 +72,15 @@ function propagate(J::judiJacobianP{D, :adjoint_born, FT}, residual::AbstractArr
end

fwi_objective(model::MTypes, q::Dtypes, dobs::Dtypes, r::Integer; options=Options(), offsets=0f0) =
fwi_objective(model, q, dobs; options=options, r=r, offsets=offsets)
fwi_objective(model, q, dobs; options=options, r=r, offsets=offsets, func=multi_src_fg_r)

lsrtm_objective(model::MTypes, q::Dtypes, dobs::Dtypes, dm::dmType, r::Integer; options=Options(), nlind=false, offsets=0f0) =
lsrtm_objective(model, q, dobs, dm; options=options, nlind=nlind, r=r, offsets=offsets)
lsrtm_objective(model, q, dobs, dm; options=options, nlind=nlind, r=r, offsets=offsets, func=multi_src_fg_r)

function multi_src_fg(model::AbstractModel, q::judiVector, dobs::judiVector, dm, options::JUDIOptions, nlind::Bool, lin::Bool, r::Integer, offsets)

function multi_src_fg_r(model::AbstractModel, q::judiVector, dobs::judiVector, dm, options::JUDIOptions;
nlind::Bool=false, lin::Bool=false, misfit::Function=mse, illum::Bool=false, r::Integer=32,
data_precon=nothing, model_precon=LinearAlgebra.I)
q.geometry = Geometry(q.geometry)
dobs.geometry = Geometry(dobs.geometry)

Expand All @@ -92,4 +95,4 @@ function multi_src_fg(model::AbstractModel, q::judiVector, dobs::judiVector, dm,
ge = backprop(model_loc, residual, Q, eu; options=options, modelPy=modelPy, offsets=offsets, pe=eu.indices[1])
options.limit_m && (ge = extend_gradient(model, model_loc, ge))
return Ref{Float32}(.5f0*norm(residual)^2), PhysicalParameter(ge, model.d, model.o)
end
end
31 changes: 21 additions & 10 deletions src/propagators.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
p_dims = Dict()

# Basic caching of probing dim
function pdim(ne::Integer)
if ne in keys(p_dims)
p_e = p_dims[ne]
else
p_e = dv.CustomDimension(name="p_e", 0, ne-1, ne)
p_dims[ne] = p_e
end
return p_e
end

# Forward propagation
function forward(model::PyObject, src_coords::Array{Float32}, rcv_coords::Array{Float32},
wavelet::Array{Float32}, e::Array{Float32}, space_order::Integer=8, isic::Bool=false)
Expand Down Expand Up @@ -95,18 +108,18 @@ end

function time_probe(e::Array{Float32, 2}, wf::PyObject; fw=true, isic=false, pe=nothing)
ne = size(e, 2)
p_e = dv.DefaultDimension(name="p_e", default_value=ne)
p_e = pdim(ne)
# sub_time
t_sub = wf.grid.time_dim
s = fw ? t_sub.spacing : 1
# Probing vector
nt = size(e, 1)
q = dv.TimeFunction(name="Q", grid=wf.grid, dimensions=(t_sub, p_e),
shape=(nt, ne), time_order=0, initializer=e)
shape=(nt, ne), time_order=0, initializer=e, space_order=0)
# Probed output
if isnothing(pe)
pe = dv.Function(name="$(wf.name)e", grid=wf.grid, dimensions=(wf.grid.dimensions..., p_e),
shape=(wf.grid.shape..., ne), space_order=wf.space_order)
pe = dv.Function(name="$(wf.name)e", grid=wf.grid, dimensions=(p_e, wf.grid.dimensions...),
shape=(ne, wf.grid.shape...), space_order=wf.space_order)
end
probing = [dv.Eq(pe, pe + s*q*ic(wf, fw, isic))]
return pe, probing
Expand All @@ -115,18 +128,18 @@ end

function time_probe(e::Array{Float32, 2}, wf::Tuple{PyCall.PyObject, PyCall.PyObject}; fw=true, isic=false, pe=nothing)
ne = size(e, 2)
p_e = dv.DefaultDimension(name="p_e", default_value=ne)
p_e = pdim(ne)
# sub_time
t_sub = wf[1].grid.time_dim
s = fw ? t_sub.spacing : 1
# Probing vector
nt = size(e, 1)
q = dv.TimeFunction(name="Q", grid=wf[1].grid, dimensions=(t_sub, p_e),
shape=(nt, ne), time_order=0, initializer=e)
shape=(nt, ne), time_order=0, initializer=e, space_order=0)
# Probed output
if isnothing(pe)
pe = dv.Function(name="$(wf[1].name)e", grid=wf[1].grid, dimensions=(wf[1].grid.dimensions..., p_e),
shape=(wf[1].grid.shape..., ne), space_order=wf[1].space_order)
pe = dv.Function(name="$(wf[1].name)e", grid=wf[1].grid, dimensions=(p_e, wf[1].grid.dimensions...),
shape=(ne, wf[1].grid.shape...), space_order=wf[1].space_order)
end
probing = [dv.Eq(pe, pe + s*q*ic(wf, fw, isic))]
return pe, probing
Expand All @@ -146,7 +159,6 @@ end
function combine(eu::PyObject, ev::PyObject, offsets::Number, isic::Bool=false)
@assert offsets == 0
g = dv.Function(name="ge", grid=eu.grid, space_order=0)
ev = ev._subs(ev.indices[end], eu.indices[end])
eq = isic ? (eu * ev.laplace + si.inner_grad(eu, ev)) : eu * ev
op = dv.Operator(dv.Inc(g, -eq), subs=eu.grid.spacing_map)
op()
Expand All @@ -165,7 +177,6 @@ function combine(eu::PyObject, ev::PyObject, offsets::Vector{Int64}, isic::Bool=
# IC with space shifts
g = dv.Function(name="ge", grid=eu.grid, shape=(2*nh+1, eu.grid.shape...),
dimensions=(offs, eu.grid.dimensions...), space_order=0)
ev = ev._subs(ev.indices[end], eu.indices[end])
x = eu.grid.dimensions[1]

ev = ev._subs(x, x+oh)
Expand Down
30 changes: 24 additions & 6 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,31 @@ typedict(x::T) where {T} = Dict(fn=>getfield(x, fn) for fn ∈ fieldnames(T))

function qr_data(d::Array{Float32,2}, ps::Integer; seed=nothing, ts=0, mode=:QR)
!isnothing(seed) && Random.seed!(seed)
mode == :Gaussian && (return randn(Float32, size(d, 1), ps) ./ Float32(sqrt(ps)))
S = rand([-1f0, 1f0], size(d, 1), ps)
mode == :Rademacher && (return S ./ Float32(sqrt(ps)))
ts > 0 && (d = circshift(d, (-ts, 0)))
AS = d * (d' * S)
Q, _ = qr(AS)
return Matrix(Q)
if mode == :Gaussian
Q = randn(Float32, size(d, 1), ps) ./ Float32(sqrt(ps))
elseif mode == :Rademacher
return S ./ Float32(sqrt(ps))
elseif mode == :QR
ts > 0 && (d = circshift(d, (-ts, 0)))
AS = d * (d' * S)
Q, _ = qr(AS)
return Matrix(Q)
elseif mode == :hutchpp
ts > 0 && (d = circshift(d, (-ts, 0)))
# Split S
m = 3 * ps / 2
mid = div(ps, 2)
S1, S2 = S[:, 1:mid], S[:, mid+1:end]
AS = d * (d' * S2)
Q, _ = qr(AS)
Q = Matrix(Q)
G = sqrt(3 / m) * (S1 - Q * (Q'* S1))
Qhutch = hcat(Q, G)
return convert(Matrix{Float32}, Qhutch)
else
throw(ArgumentError("Probing vector mode unrecognized: $(mode), must be `:QR`, `:Rademacher`, `:Gaussian` or `:hutchpp`"))
end
end

simil(x, y) = dot(x, y)/(norm(x)*norm(y))
Expand Down