Part 3: The Linear Problem and Validation¶
In this exercise you will solve the linear forward modeling equations and verify that the adjoint conditions hold for your code. The definition of the adjoint of a linear operator states that
where \(\left< \cdot, \cdot \right> _{\mathcal{D}}\) and \(\left< \cdot, \cdot \right> _{\mathcal{M}}\) are inner products in the data and model spaces, respectively. One test to see if your migration operator \(F^*\) is working correctly is to test if this relationship holds to high precision for any pair of \(d\) and \(\delta m\).
Linear Forward Operator¶
To implement the adjoint test, you will need to solve the linear modeling equations, which are derived in the notes,
where \(u_1\) is the Born scattered field and equivalently \(F\delta m = u_1\).
Problem 3.1
Write a function linear_sources(dm, u0s, config)
that takes an
arbitrary model perturbation dm
and a time sequence of incident
wavefields u0s
and generates the linear source wavefields (the
right-hand-sides of the linear modeling equations). Functions you have
previously written should be useful.
Problem 3.2
Use your leap_frog
function to solve for the linear forward wavefield
\(u_1\) due to a perturbation \(\delta m\). Use this code to write
a function linear_forward_operator(C0, dm, config)
which returns a
tuple containing the wavefields and the sampled data.
Adjoint Validation¶
When sampling is accounted for, the adjoint condition requires that,
hold to high precision.
Problem 3.3
Verify that the adjoint condition is satisfied by your implementation of
the linear forward modeling and migration operators. Be careful to take
into account the differences in the model and data inner product. Write a
function adjoint_condition(C0, config)
that implements this test. How
accurate is the relationship? It should be accurate to machine precision
for random values of the data \(d\) and the model perturbation
\(\delta m\). Be sure that you define \(\delta m(0) = \delta m(1)
= 0\), as it is nonphysical to have sources on an absorbing boundary.
Consider modifying your construct_matrices
function to accept a
config
key 'bc'
, which allows you to toggle between absorbing and
homogeneous Dirichlet boundaries. This result should still hold.
print "Absorbing BC"
adjoint_condition(C0, config)
print "Dirichlet BC"
config['bc'] = 'dirichlet'
adjoint_condition(C0, config)
Bonus Problems¶
Bonus Problem 3.4: It is compuationally intractible to compute the linear forward operator \(F\) directly. Why? If you wanted to explicitely compute this operator, how would you do it?