This example demonstrates how to use jInv
for multiphysics inversions. Here, we jointly invert 3D travel time and DC resitivity data. Our example geophysical imaging technique.
Our goal is to solve the inverse problem
\begin{equation*} \min_{m} \sum_{j=1}^{n^{\rm Eik}} \| P^{\rm Eik}_j u^{\rm Eik}_j(m) - d^{\rm Eik}_{j}\|^2 + \sum_{j=1}^{n^{\rm DC}} \| P^{\rm DC}_j u^{\rm DC}_j(m) - d^{\rm DC}_{j}\|^2 + \alpha \| L m\|^2 \quad \text{ subject to} \quad m_L \leq m \leq x_H \end{equation*}where
This experiment combines the data inverted in the notebooks exDCResistivity.ipynb
and exEikonal.ipynb
and is also described in [1]. You may compare the results of this joint inversion to the results in the other two examples. The fast marching method used to solve the Eikonal equation is presented in [2]. The test data here is generated using jInv and based on the 3D SEG/EAGE model of a salt reservoir described in [3]. Thus, please see and reference the following papers when using this example:
[1] Ruthotto L, Treister E, Haber E: jInv - a flexible Julia package for PDE parameter estimation, arXiv:1606.0739 [cs.MS] 2016
[2] Treister E, Haber E: A fast marching algorithm for the factored eikonal equation, Journal of Computational Physics, 324(1), p. 210–225, 2016
[3] F. Aminzadeh, B. Jean, and T. Kunz. 3-D salt and overthrust models. Society of Exploration Geophysicists, 1997.
The forward problems in our Eikonal package are solved using a fast marching method. This by itself is difficult to parallelize, however, as common in practice we have multiple measurements that need to be simulated. Our package automatically uses this potential for parallelization and uses all available workers.
So, before you start this experiment we recommend you add workers. Here, we just use local workers, but you may also use MPI or SSH to connect to remote workers. The parallelism in jInv
is designed to be communication efficient.
In [1]:
addprocs(8)
Out[1]:
In [14]:
using MAT
using PyPlot
using EikonalInv
using DivSigGrad
using jInv.InverseSolve
using jInv.Mesh
using jInv.ForwardShare
using jInv.Utils
using jInvVis
using jInv.LinearSolvers
In [3]:
matfile = matread("exEikonal.mat")
ReceiversEik = matfile["Receivers"] # sparse matrix with discrete Eikonal receivers
SourcesEik = matfile["Sources"] # sparse matrix with discrete Eikonal sources
dobsEik = matfile["dobs"] # observations simulated with true model
dobs0Eik = matfile["dobs0"] # observations simulated for reference model
slowref = matfile["slowref"] # reference slowness, serves as starting guess
n = matfile["n"] # size of mesh used for the model
domain = matfile["domain"] # domain size
mref = matfile["mref"]; # reference model
WdEik = matfile["Wd"]; # weighting matrix for misfit;
matfile = matread("exDCResistivity.mat")
ReceiversDC = matfile["Receivers"] # sparse matrix with discrete DC Receivers
SourcesDC = matfile["Sources"] # sparse matrix with discrete DC sources
dobsDC = matfile["dobs"] # observations simulated with true model
dobs0DC = matfile["dobs0"] # observations simulated for reference model
nfwd = matfile["nfwd"] # size of mesh used to solve PDEs
sigref = matfile["sigref"]; # reference guess for conductivity;
In this block we set up three different meshes used in the inversion.
In our Eikonal forward solver, the travel time and slowness are discretized on a nodal grid. This is in contrast to other jInv modules. To make this seamless, we use a shifted grid.
To accelerate the DC Resistivity simulations we use mesh decoupling and solve the forward problem on a coarser mesh.
In [4]:
h = (domain[2:2:end]-domain[1:2:end])./n
domainNodal = copy(domain); domainNodal[1:2:end] += h./2; domainNodal[2:2:end] -= h./2
Meik = getRegularMesh(domainNodal,n-1)
Minv = getRegularMesh(domain,n);
Mdc = getRegularMesh(domain,nfwd);
Mesh2Mesh = getInterpolationMatrix(Minv,Mdc)';
Sometimes we want to include some voxels from the inversion (for example, close to the boundary / sources / receivers). This can be done by the matrix Iact
by deleting some columns from a sparse identity matrix. If we do that, the size of the model gets smaller and so we need to update the reference model as well.
In [5]:
Iact = sparse(I,prod(n),prod(n))
mref = Iact'*mref[:]
mback = mref - Iact*(Iact'*mref);
slowback = slowref - Iact*(Iact'*slowref);
sigback = vec(sigref - Iact*(Iact'*sigref));
Next, we set up the misfit param for our joint inversion. We should remember three things:
SourcesSubInd
In [6]:
pForEik,contDiv,SourcesSubInd = getEikonalInvParam(Meik,SourcesEik,ReceiversEik,false,nworkers()-1)
# split up observed data and misfit weightings
WD = Array(Array{Float64},length(pForEik));
DOBS = Array(Array{Float64},length(pForEik));
for k=1:length(pForEik)
Ik = SourcesSubInd[k];
WD[k] = WdEik[:,Ik];
DOBS[k] = dobsEik[:,Ik];
end
modfunEik = velocityToSlowSquared;
pMisEik = getMisfitParam(pForEik,WD,DOBS,SSDFun,Iact,slowback,ones(length(pForEik)),modfunEik)
Out[6]:
Here, the forward problem mesh is relatively coarse. We will therefore use Julia's direct solver (MUMPS can be used as well, or iterative for larger problems). Since we have access to the factorized PDE, it will be benefitial to put all sources into a single forward problem. We'll use the last worker.
In [7]:
Ainv = getJuliaSolver()
# Ainv = getMUMPSsolver()
pForDC = getDivSigGradParam(Mdc, SourcesDC, ReceiversDC, Ainv=Ainv)
gloc = GlobalToLocal(Iact'*Mesh2Mesh,Mesh2Mesh'*sigback);
WdDC = 1./(mean(abs(vec(dobsDC)))/2+abs(dobsDC));
@everywhere begin
function velToConductMod(v,mid,a,b)
d = (b-a)./2.0;
dinv = 10;
tt = dinv.*(mid - v);
t = (d.*(tanh(tt)+1) + a);
dt = -(dinv*d)*(sech(tt)).^2;
dt = (2.0-v./mid).*dt + (-1./mid).*t;
t = t.*(2.0-v./mid);
return vec(t),spdiagm(vec(dt))
end
modfunDC(m) = velToConductMod(m,3.0,0.1,1.0);
end
pMisDC = initRemoteChannel(getMisfitParam,workers()[end],pForDC,WdDC,dobsDC,SSDFun,modfunDC,gloc)
Out[7]:
In [8]:
pMis = [pMisEik; pMisDC] # combines both problem types;
In [9]:
# configure regularization
alpha = 1e-15;
regparams = [1.0,1.0,1.0,1e-6];
regfun(m,mref,Minv) = wdiffusionReg(m,mref,Minv,Iact=Iact,C = regparams)
# configuer optimization
HesPrec = getSSORRegularizationPreconditioner(1.0,1e-15,200);
cgit = 8;
pcgTol = 1e-1;
maxIter = 10;
minUpdate = 1e-2;
boundsLow = 1.45*ones(size(Iact,2));
boundsHigh = 4.5 *ones(size(Iact,2));
maxStep = 0.1*maximum(boundsHigh);
pInv = getInverseParam(Minv,identityMod,regfun,alpha,vec(mref),
boundsLow,boundsHigh,maxStep=maxStep,pcgMaxIter=cgit,pcgTol=pcgTol,
minUpdate=minUpdate, maxIter = maxIter,HesPrec=HesPrec);
Some inversions might take a long time, so it's useful to store or visualize intermediate results. This way, we can cancel the inversion if, e.g., the regularization parameter is too large/small. The optimization algorithms in jInv provide the current model, the current data and some other info we can use. In this case we just plot the true sigma and the estimated sigma for comparison
Remark : interactive plotting does not work in IJulia notebooks. However, if you copy the code to a .jl
file you will be able to see the plots being updated through the iteration.
In [10]:
plotting = true
function plotIntermediates(mc,Dc,iter,pInv,pMis)
figure(13)
clf()
subplot(1,2,1)
viewOrthoSlices2D(mref,Minv)
title("starting guess for slowness")
colorbar()
subplot(1,2,2)
viewOrthoSlices2D(mc,Minv)
title("slowness at iter=$(iter)")
colorbar()
end
Out[10]:
In [11]:
mc,Dc,flag,His = projGNCG(copy(mref[:]),pInv,pMis,dumpResults = plotIntermediates);
In [12]:
subplot(1,3,1)
semilogy(His.F,"-o")
xlabel("PGNCG iterations")
title("misfit")
subplot(1,3,2)
semilogy(His.Rc,"-o")
xlabel("PGNCG iterations")
title("regularizer");
subplot(1,3,3)
semilogy(His.dJ,"-o")
xlabel("PGNCG iterations")
title("norm of proj grad");
In [13]:
# fetch Eikonal data
DcEik = zeros(size(dobsEik,1),size(dobsEik,2),length(His.Dc))
for iter=1:length(His.Dc)
for k=1:length(His.Dc[iter])-1
Ik = SourcesSubInd[k];
DcEik[:,Ik,iter] = fetch(His.Dc[iter][k])
end
end
DcDC = zeros(size(dobsDC,1),size(dobsDC,2),length(His.Dc))
for iter=1:length(His.Dc)
DcDC[:,:,iter] = fetch(His.Dc[iter][end]);
end
His.Dc = [];
matwrite("exJointEikonalDCRes.mat",Dict("mc"=>mc,"His"=>His,"DcEik"=>DcEik,"DcDC"=>DcDC))
In [ ]: