def symbol(name, dimensions, value=0., mode='function'): """Short-cut for symbol creation to test "function" and "indexed" API.""" assert(mode in ['function', 'indexed']) s = DenseData(name=name, dimensions=dimensions) s.data[:] = value return s.indexify() if mode == 'indexed' else s
def test_symbol_cache_aliasing_reverse(): """Test to assert that removing he original u[x, y] instance does not impede our alisaing cache or leaks memory. """ # Ensure a clean cache to start with clear_cache() # FIXME: Currently not working, presumably due to our # failure to cache new instances? # assert(len(_SymbolCache) == 0) # Create first instance of u and fill its data u = DenseData(name='u', shape=(3, 4)) u.data[:] = 6. u_ref = weakref.ref(u.data) # Create derivative and delete orignal u[x, y] dx = u.dx del u clear_cache() # We still have a references to u # FIXME: Unreliable cache sizes # assert len(_SymbolCache) == 1 # Ensure u[x + h, y] still holds valid data assert np.allclose(dx.args[0].args[1].data, 6.) del dx clear_cache() # FIXME: Unreliable cache sizes # assert len(_SymbolCache) == 0 # We still have a reference to u_h assert u_ref() is None
def test_data_movement_1D(self): u = DenseData(name='yu1D', shape=(16,), dimensions=(x,), space_order=0) u.data assert type(u._data_object) == YaskGrid u.data[1] = 1. assert u.data[0] == 0. assert u.data[1] == 1. assert all(i == 0 for i in u.data[2:])
def test_cache_after_indexification(): """Test to assert that the SymPy cache retrieves the right Devito data object after indexification. """ u0 = DenseData(name='u', shape=(4, 4, 4), space_order=0) u1 = DenseData(name='u', shape=(4, 4, 4), space_order=1) u2 = DenseData(name='u', shape=(4, 4, 4), space_order=2) for i in [u0, u1, u2]: assert i.indexify().base.function.space_order ==\ (i.indexify() + 1.).args[1].base.function.space_order
def test_constant_time_dense(self): """Test arithmetic between different data objects, namely ConstantData and DenseData.""" const = ConstantData(name='truc', value=2.) a = DenseData(name='a', shape=(20, 20)) a.data[:] = 2. eqn = Eq(a, a + 2.*const) op = Operator(eqn) op.apply(a=a, truc=const) assert(np.allclose(a.data, 6.)) # Applying a different constant still works op.apply(a=a, truc=ConstantData(name='truc2', value=3.)) assert(np.allclose(a.data, 12.))
def u(dims): u = DenseData(name='yu3D', shape=(16, 16, 16), dimensions=(x, y, z), space_order=0) u.data # Trigger initialization return u
def Born(self, save=False, cache_blocking=None, auto_tuning=False, dse='advanced', dle='advanced', compiler=None, free_surface=False): """Linearized modelling of one or multiple point source from an input model perturbation. """ nt, nrec = self.data.shape nsrc = self.source.shape[1] ndim = len(self.model.shape) h = self.model.get_spacing() dtype = self.model.dtype nbpml = self.model.nbpml # Create source symbol src = PointSource(name='src', ntime=self.source.nt, coordinates=self.source.receiver_coords) # Create receiver symbol Linrec = Receiver(name='Lrec', ntime=self.data.nt, coordinates=self.data.receiver_coords) # Create the forward wavefield u = TimeData(name="u", shape=self.model.shape_domain, time_dim=nt, time_order=2, space_order=self.s_order, dtype=self.model.dtype) du = TimeData(name="du", shape=self.model.shape_domain, time_dim=nt, time_order=2, space_order=self.s_order, dtype=self.model.dtype) dm = DenseData(name="dm", shape=self.model.shape_domain, dtype=self.model.dtype) # Execute operator and return wavefield and receiver data born = BornOperator(self.model, u, du, src, Linrec, dm, self.data, time_order=self.t_order, spc_order=self.s_order, cache_blocking=cache_blocking, dse=dse, dle=dle, free_surface=free_surface) return born
def test_inject_from_field(shape, coords, result, npoints=19): """Test point injection from a second field along a line through the middle of the grid. """ a = unit_box(shape=shape) spacing = a.data[tuple([1 for _ in shape])] a.data[:] = 0. b = DenseData(name='b', shape=a.data.shape) b.data[:] = 1. p = points(ranges=coords, npoints=npoints) expr = p.inject(field=a, expr=b) Operator(expr, subs={h: spacing})(a=a, b=b, t=1) indices = [slice(4, 6, 1) for _ in coords] indices[0] = slice(1, -1, 1) assert np.allclose(a.data[indices], result, rtol=1.e-5)
def test_fd_space(derivative, space_order): """ This test compare the discrete finite-difference scheme against polynomials For a given order p, the fiunite difference scheme should be exact for polynomials of order p :param derivative: name of the derivative to be tested :param space_order: space order of the finite difference stencil """ clear_cache() # dummy axis dimension nx = 100 xx = np.linspace(-1, 1, nx) dx = xx[1] - xx[0] # Symbolic data u = DenseData(name="u", shape=(nx, ), space_order=space_order, dtype=np.float32) du = DenseData(name="du", shape=(nx, ), space_order=space_order, dtype=np.float32) # Define polynomial with exact fd h, y = symbols('h y') coeffs = np.ones((space_order, ), dtype=np.float32) polynome = sum([coeffs[i] * y**i for i in range(0, space_order)]) polyvalues = np.array([polynome.subs(y, xi) for xi in xx], np.float32) # Fill original data with the polynomial values u.data[:] = polyvalues # True derivative of the polynome Dpolynome = diff(diff(polynome)) if derivative == 'dx2' else diff(polynome) Dpolyvalues = np.array([Dpolynome.subs(y, xi) for xi in xx], np.float32) # FD derivative, symbolic u_deriv = getattr(u, derivative) # Compute numerical FD stencil = Eq(du, u_deriv) op = Operator(stencil, subs={h: dx}) op.apply() # Check exactness of the numerical derivative except inside space_brd space_border = space_order error = abs(du.data[space_border:-space_border] - Dpolyvalues[space_border:-space_border]) assert np.isclose(np.mean(error), 0., atol=1e-3)
def _new_operator1(shape, **kwargs): infield = DenseData(name='infield', shape=shape, dtype=np.int32) infield.data[:] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) outfield = DenseData(name='outfield', shape=shape, dtype=np.int32) stencil = Eq(outfield.indexify(), outfield.indexify() + infield.indexify()*3.0) # Run the operator op = Operator(stencil, **kwargs) op(infield=infield, outfield=outfield) return outfield, op
def Gradient(self, cache_blocking=None, auto_tuning=False, dle='advanced', dse='advanced', compiler=None, free_surface=False): """FWI gradient from back-propagation of a shot record and input forward wavefield """ nt, nrec = self.data.shape ndim = len(self.model.shape) h = self.model.get_spacing() dtype = self.model.dtype nbpml = self.model.nbpml # Create receiver symbol rec = Receiver(name='rec', ntime=self.data.nt, coordinates=self.data.receiver_coords) # Gradient symbol grad = DenseData(name="grad", shape=self.model.shape_domain, dtype=self.model.dtype) # Create the forward wavefield v = TimeData(name="v", shape=self.model.shape_domain, time_dim=nt, time_order=2, space_order=self.s_order, dtype=self.model.dtype) u = TimeData(name="u", shape=self.model.shape_domain, time_dim=nt, time_order=2, space_order=self.s_order, dtype=self.model.dtype, save=True) # Execute operator and return wavefield and receiver data gradop = GradientOperator(self.model, v, grad, rec, u, self.data, time_order=self.t_order, spc_order=self.s_order, cache_blocking=cache_blocking, dse=dse, dle=dle, tsave=self.tsave, free_surface=free_surface) return gradop
def test_dimension_size_infer(self, nt=100): """Test that the dimension sizes are being inferred correctly""" i, j, k = dimify('i j k') shape = tuple([d.size for d in [i, j, k]]) a = DenseData(name='a', shape=shape).indexed b = TimeData(name='b', shape=shape, save=True, time_dim=nt).indexed eqn = Eq(b[time, x, y, z], a[x, y, z]) op = Operator(eqn) _, op_dim_sizes = op.arguments() assert(op_dim_sizes[time.name] == nt)
def test_clear_cache(nx=1000, ny=1000): clear_cache() cache_size = len(_SymbolCache) for i in range(10): assert (len(_SymbolCache) == cache_size) DenseData(name='u', shape=(nx, ny), dtype=np.float64, space_order=2) assert (len(_SymbolCache) == cache_size + 1) clear_cache()
def test_equations_mixed_densedata_timedata(self, shape, dimensions): """ Test that equations using a mixture of DenseData and TimeData objects are embedded within the same time loop. """ a = TimeData(name='a', shape=shape, time_order=2, dimensions=dimensions, space_order=2, time_dim=6, save=False) p_aux = Dimension(name='p_aux', size=10) b = DenseData(name='b', shape=shape + (10,), dimensions=dimensions + (p_aux,), space_order=2) b.data[:] = 1.0 b2 = DenseData(name='b2', shape=(10,) + shape, dimensions=(p_aux,) + dimensions, space_order=2) b2.data[:] = 1.0 eqns = [Eq(a.forward, a.laplace + 1.), Eq(b, time*b*a + b)] eqns2 = [Eq(a.forward, a.laplace + 1.), Eq(b2, time*b2*a + b2)] subs = {x.spacing: 2.5, y.spacing: 1.5, z.spacing: 2.0} op = Operator(eqns, subs=subs, dle='noop') trees = retrieve_iteration_tree(op) assert len(trees) == 2 assert all(trees[0][i] is trees[1][i] for i in range(3)) op2 = Operator(eqns2, subs=subs, dle='noop') trees = retrieve_iteration_tree(op2) assert len(trees) == 2 assert all(trees[0][i] is trees[1][i] for i in range(3)) # Verify both operators produce the same result op() op2() assert(np.allclose(b2.data[2, ...].reshape(-1) - b.data[..., 2].reshape(-1), 0.))
def test_symbol_cache_aliasing(): """Test to assert that our aiasing cache isn't defeated by sympys non-aliasing symbol cache. For further explanation consider the symbol u[x, y] and it's first derivative in x, which includes the symbols u[x, y] and u[x + h, y]. The two functions are aliased in devito's caching mechanism to allow multiple stencil indices pointing at the same data object u, but SymPy treats these two instances as separate functions and thus is allowed to delete one or the other when the cache is cleared. The test below asserts that u[x + h, y] is deleted, the data on u is still intact through our own caching mechanism.""" # Ensure a clean cache to start with clear_cache() # FIXME: Currently not working, presumably due to our # failure to cache new instances? # assert(len(_SymbolCache) == 0) # Create first instance of u and fill its data u = DenseData(name='u', shape=(3, 4)) u.data[:] = 6. u_ref = weakref.ref(u.data) # Create u[x + h, y] and delete it again dx = u.dx # Contains two u symbols: u[x, y] and u[x + h, y] del dx clear_cache() # FIXME: Unreliable cache sizes # assert len(_SymbolCache) == 1 # We still have a reference to u assert np.allclose(u.data, 6.) # u.data is alive and well # Remove the final instance and ensure u.data got deallocated del u clear_cache() assert u_ref() is None
def test_arithmetic_deep(expr, result): """Tests basic point-wise arithmetic on multi-dimensional data""" i = Dimension(name='i', size=3) j = Dimension(name='j', size=4) k = Dimension(name='k', size=5) l = Dimension(name='l', size=6) a = DenseData(name='a', dimensions=(i, j, k, l)) a.data[:] = 2. ai = a.indexify() b = DenseData(name='b', dimensions=(j, k)) b.data[:] = 3. bi = b.indexify() eqn = eval(expr) StencilKernel(eqn)(ai.base.function, bi.base.function) assert np.allclose(ai.base.function.data, result, rtol=1e-12)
def test_indexed_open_loops(self, expr, result): """Test point-wise arithmetic with stencil offsets and open loop boundaries in indexed expression format""" i, j, l = dimify('i j l') pushed = [d.size for d in [j, l]] j.size = None l.size = None a = DenseData(name='a', dimensions=(i, j, l), shape=(3, 5, 6)).indexed fa = a.function fa.data[0, :, :] = 2. eqn = eval(expr) Operator(eqn)(a=fa) assert np.allclose(fa.data[1, 1:-1, 1:-1], result[1:-1, 1:-1], rtol=1e-12) j.size, l.size = pushed
def GradientOperator(model, source, receiver, time_order=2, space_order=4, **kwargs): """ Constructor method for the gradient operator in an acoustic media :param model: :class:`Model` object containing the physical parameters :param source: :class:`PointData` object containing the source geometry :param receiver: :class:`PointData` object containing the acquisition geometry :param time_order: Time discretization order :param space_order: Space discretization order """ m, damp = model.m, model.damp # Gradient symbol and wavefield symbols grad = DenseData(name='grad', shape=model.shape_domain, dtype=model.dtype) u = TimeData(name='u', shape=model.shape_domain, save=True, time_dim=source.nt, time_order=time_order, space_order=space_order, dtype=model.dtype) v = TimeData(name='v', shape=model.shape_domain, save=False, time_order=time_order, space_order=space_order, dtype=model.dtype) rec = Receiver(name='rec', ntime=receiver.nt, ndim=receiver.ndim, npoint=receiver.npoint) if time_order == 2: biharmonic = 0 dt = model.critical_dt gradient_update = Eq(grad, grad - u.dt2 * v) else: biharmonic = v.laplace2(1/m) biharmonicu = - u.laplace2(1/(m**2)) dt = 1.73 * model.critical_dt gradient_update = Eq(grad, grad - (u.dt2 - s**2 / 12.0 * biharmonicu) * v) # Derive stencil from symbolic equation stencil = 1.0 / (2.0 * m + s * damp) * \ (4.0 * m * v + (s * damp - 2.0 * m) * v.forward + 2.0 * s ** 2 * (v.laplace + s**2 / 12.0 * biharmonic)) eqn = Eq(v.backward, stencil) # Add expression for receiver injection ti = v.indices[0] receivers = rec.inject(field=v, u_t=ti - 1, offset=model.nbpml, expr=rec * dt * dt / m, p_t=time) return Operator([eqn] + [gradient_update] + receivers, subs={s: dt, h: model.get_spacing()}, time_axis=Backward, name='Gradient', **kwargs)
def test_equations_emulate_bc(self, t0): """ Test that bc-like equations get inserted into the same loop nest as the "main" equations. """ shape = (3, 3, 3) a = DenseData(name='a', shape=shape).indexed b = TimeData(name='b', shape=shape, save=True, time_dim=6).indexed main = Eq(b[time + 1, x, y, z], b[time - 1, x, y, z] + a[x, y, z] + 3.*t0) bcs = [Eq(b[time, 0, y, z], 0.), Eq(b[time, x, 0, z], 0.), Eq(b[time, x, y, 0], 0.)] op = Operator([main] + bcs, dse='noop', dle='noop') trees = retrieve_iteration_tree(op) assert len(trees) == 4 assert all(id(trees[0][0]) == id(i[0]) for i in trees)
def gradient(self, rec, u, v=None, grad=None, m=None, **kwargs): """ Gradient modelling function for computing the adjoint of the Linearized Born modelling function, ie. the action of the Jacobian adjoint on an input data. :param recin: Receiver data as a numpy array :param u: Symbol for full wavefield `u` (created with save=True) :param v: (Optional) Symbol to store the computed wavefield :param grad: (Optional) Symbol to store the gradient field :returns: Gradient field and performance summary """ # Gradient symbol if grad is None: grad = DenseData(name='grad', shape=self.model.shape_domain, dtype=self.model.dtype) # Create the forward wavefield if v is None: v = TimeData(name='v', shape=self.model.shape_domain, save=False, time_order=self.time_order, space_order=self.space_order, dtype=self.model.dtype) # Pick m from model unless explicitly provided if m is None: m = m or self.model.m summary = self.op_grad.apply(rec=rec, grad=grad, v=v, u=u, m=m, **kwargs) return grad, summary
def __init__(self, origin, spacing, shape, vp, nbpml=20, dtype=np.float32, epsilon=None, delta=None, theta=None, phi=None): self.origin = origin self.spacing = spacing self.shape = shape self.nbpml = int(nbpml) self.dtype = dtype # Ensure same dimensions on all inpute parameters assert (len(origin) == len(spacing)) assert (len(origin) == len(shape)) # Create square slowness of the wave as symbol `m` if isinstance(vp, np.ndarray): self.m = DenseData(name="m", shape=self.shape_domain, dtype=self.dtype) else: self.m = ConstantData(name="m", value=1 / vp**2, dtype=self.dtype) # Set model velocity, which will also set `m` self.vp = vp # Create dampening field as symbol `damp` self.damp = DenseData(name="damp", shape=self.shape_domain, dtype=self.dtype) damp_boundary(self.damp.data, self.nbpml, spacing=self.get_spacing()) # Additional parameter fields for TTI operators self.scale = 1. if epsilon is not None: if isinstance(epsilon, np.ndarray): self.epsilon = DenseData(name="epsilon", shape=self.shape_domain, dtype=self.dtype) self.epsilon.data[:] = self.pad(1 + 2 * epsilon) # Maximum velocity is scale*max(vp) if epsilon > 0 if np.max(self.epsilon.data) > 0: self.scale = np.sqrt(np.max(self.epsilon.data)) else: self.epsilon = 1 + 2 * epsilon self.scale = epsilon else: self.epsilon = 1 if delta is not None: if isinstance(delta, np.ndarray): self.delta = DenseData(name="delta", shape=self.shape_domain, dtype=self.dtype) self.delta.data[:] = self.pad(np.sqrt(1 + 2 * delta)) else: self.delta = delta else: self.delta = 1 if theta is not None: if isinstance(theta, np.ndarray): self.theta = DenseData(name="theta", shape=self.shape_domain, dtype=self.dtype) self.theta.data[:] = self.pad(theta) else: self.theta = theta else: self.theta = 0 if phi is not None: if isinstance(phi, np.ndarray): self.phi = DenseData(name="phi", shape=self.shape_domain, dtype=self.dtype) self.phi.data[:] = self.pad(phi) else: self.phi = phi else: self.phi = 0
def ai(x, y, name='a', value=2.): a = DenseData(name=name, dimensions=(x, y)) a.data[:] = value return a.indexify()
def densedata(name, shape, dimensions): return DenseData(name=name, shape=shape, dimensions=dimensions)
def bi(x, y, name='b', value=3.): b = DenseData(name=name, dimensions=(x, y)) b.data[:] = value return b.indexify()
def BornOperator(model, source, receiver, time_order=2, space_order=4, **kwargs): """ Constructor method for the Linearized Born operator in an acoustic media :param model: :class:`Model` object containing the physical parameters :param source: :class:`PointData` object containing the source geometry :param receiver: :class:`PointData` object containing the acquisition geometry :param time_order: Time discretization order :param space_order: Space discretization order """ m, damp = model.m, model.damp # Create source and receiver symbols src = PointSource(name='src', ntime=source.nt, ndim=source.ndim, npoint=source.npoint) rec = Receiver(name='rec', ntime=receiver.nt, ndim=receiver.ndim, npoint=receiver.npoint) # Create wavefields and a dm field u = TimeData(name="u", shape=model.shape_domain, save=False, time_order=time_order, space_order=space_order, dtype=model.dtype) U = TimeData(name="U", shape=model.shape_domain, save=False, time_order=time_order, space_order=space_order, dtype=model.dtype) dm = DenseData(name="dm", shape=model.shape_domain, dtype=model.dtype) if time_order == 2: biharmonicu = 0 biharmonicU = 0 dt = model.critical_dt else: biharmonicu = u.laplace2(1/m) biharmonicU = U.laplace2(1/m) dt = 1.73 * model.critical_dt # Derive both stencils from symbolic equation # first_eqn = m * u.dt2 - u.laplace + damp * u.dt # second_eqn = m * U.dt2 - U.laplace - dm* u.dt2 + damp * U.dt stencil1 = 1.0 / (2.0 * m + s * damp) * \ (4.0 * m * u + (s * damp - 2.0 * m) * u.backward + 2.0 * s ** 2 * (u.laplace + s**2 / 12 * biharmonicu)) stencil2 = 1.0 / (2.0 * m + s * damp) * \ (4.0 * m * U + (s * damp - 2.0 * m) * U.backward + 2.0 * s ** 2 * (U.laplace + s**2 / 12 * biharmonicU - dm * u.dt2)) eqn1 = Eq(u.forward, stencil1) eqn2 = Eq(U.forward, stencil2) # Add source term expression for u ti = u.indices[0] source = src.inject(field=u, u_t=ti + 1, offset=model.nbpml, expr=src * dt * dt / m, p_t=time) # Create receiver interpolation expression from U receivers = rec.interpolate(expr=U, u_t=ti, offset=model.nbpml) return Operator([eqn1] + source + [eqn2] + receivers, subs={s: dt, h: model.get_spacing()}, time_axis=Forward, name='Born', **kwargs)
def run(dimensions=(50, 50, 50), tn=750.0, time_order=2, space_order=4, nbpml=40, dse='noop', dle='noop'): ndim = len(dimensions) origin = tuple([0.] * ndim) spacing = tuple([15.] * ndim) f0 = .010 t0 = 0.0 # True velocity true_vp = np.ones(dimensions) + .5 true_vp[..., int(dimensions[-1] / 2):] = 2. # Smooth velocity - we use this as our initial m initial_vp = smooth10(true_vp, dimensions) # Model perturbation model = Model(origin, spacing, true_vp.shape, true_vp, nbpml=nbpml) m0 = np.float32(model.pad(initial_vp**-2)) dm = np.float32(model.m.data - m0) dt = model.critical_dt if time_order == 4: dt *= 1.73 nt = int(1 + (tn - t0) / dt) # Source geometry time_series = np.zeros((nt, 1), dtype=np.float32) time_series[:, 0] = source(np.linspace(t0, tn, nt), f0) # Source location location = np.zeros((1, ndim), dtype=np.float32) location[0, :-1] = [ origin[i] + dimensions[i] * spacing[i] * .5 for i in range(ndim - 1) ] location[0, -1] = origin[-1] + 2 * spacing[-1] # Receivers locations receiver_coords = np.zeros((dimensions[0], ndim), dtype=np.float32) receiver_coords[:, 0] = np.linspace(0, origin[0] + (dimensions[0] - 1) * spacing[0], num=dimensions[0]) receiver_coords[:, 1:] = location[0, 1:] # Create source symbol src = PointSource(name="src", data=time_series, coordinates=location) # Receiver for true model rec_t = Receiver(name="rec", ntime=nt, coordinates=receiver_coords) # Receiver for smoothed model rec_s = Receiver(name="rec", ntime=nt, coordinates=receiver_coords) # Create the forward wavefield to use (only 3 timesteps) # Once checkpointing is in, this will be the only wavefield we need u_nosave = TimeData(name="u", shape=model.shape_domain, time_dim=nt, time_order=time_order, space_order=space_order, save=False, dtype=model.dtype) # Forward wavefield where all timesteps are stored # With checkpointing this should go away <---- u_save = TimeData(name="u", shape=model.shape_domain, time_dim=nt, time_order=time_order, space_order=space_order, save=True, dtype=model.dtype) # Forward Operators - one with save = True and one with save = False fw = ForwardOperator(model, src, rec_t, time_order=time_order, spc_order=space_order, save=True, dse=dse, dle=dle) fw_nosave = ForwardOperator(model, src, rec_t, time_order=time_order, spc_order=space_order, save=False, dse=dse, dle=dle) # Calculate receiver data for true velocity fw_nosave.apply(u=u_nosave, rec=rec_t, src=src) # Smooth velocity # This is the pass that needs checkpointing <---- fw.apply(u=u_save, rec=rec_s, m=m0, src=src) # Objective function value F0 = .5 * linalg.norm(rec_s.data - rec_t.data)**2 # Receiver for Gradient # Confusing nomenclature because this is actually the source for the adjoint # mode rec_g = Receiver(name="rec", coordinates=rec_s.coordinates.data, data=rec_s.data - rec_t.data) # Gradient symbol grad = DenseData(name="grad", shape=model.shape_domain, dtype=model.dtype) # Reusing u_nosave from above as the adjoint wavefield since it is a temp var anyway gradop = GradientOperator(model, src, rec_g, time_order=time_order, spc_order=space_order, dse=dse, dle=dle) # Clear the wavefield variable to reuse it # This time it represents the adjoint field u_nosave.data.fill(0) # Apply the gradient operator to calculate the gradient # This is the pass that requires the checkpointed data gradop.apply(u=u_save, v=u_nosave, m=m0, rec=rec_g, grad=grad) # The result is in grad gradient = grad.data # <J^T \delta d, dm> G = np.dot(gradient.reshape(-1), dm.reshape(-1)) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model mloc = m0 + H[i] * dm # Set field to zero (we're re-using it) u_nosave.data.fill(0) # Receiver data for the new model # Results will be in rec_s fw_nosave.apply(u=u_nosave, rec=rec_s, m=mloc, src=src) d = rec_s.data # First order error Phi(m0+dm) - Phi(m0) error1[i] = np.absolute(.5 * linalg.norm(d - rec_t.data)**2 - F0) # Second order term r Phi(m0+dm) - Phi(m0) - <J(m0)^T \delta d, dm> error2[i] = np.absolute(.5 * linalg.norm(d - rec_t.data)**2 - F0 - H[i] * G) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def a(shape=(11, 11)): x = np.linspace(0., 1., shape[0]) y = np.linspace(0., 1., shape[1]) a = DenseData(name='a', shape=shape) a.data[:] = np.meshgrid(x, y)[1] return a
def unit_box(name='a', shape=(11, 11)): """Create a field with value 0. to 1. in each dimension""" a = DenseData(name=name, shape=shape) dims = tuple([np.linspace(0., 1., d) for d in shape]) a.data[:] = np.meshgrid(*dims)[1] return a