def test_irregular_write(self): """ Compute a simple stencil S w/o offloading it to YASK because of the presence of indirect write accesses (e.g. A[B[i]] = ...); YASK grid functions are however used in the generated code to access the data at the right location. This test checks that the numerical output is correct after this transformation. Initially, the input array (a YASK grid, under the hood), at t=0 is (2D view): 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 Then, the Operator "flips" its content, and at timestep t=1 we get (2D view): 3 2 1 0 3 2 1 0 3 2 1 0 3 2 1 0 """ p = PointData(name='points', nt=1, npoint=4) u = TimeData(name='yu4D', shape=(4, 4, 4), dimensions=(x, y, z), space_order=0) for i in range(4): for j in range(4): for k in range(4): u.data[0, i, j, k] = k ind = lambda i: p.indexed[0, i] eqs = [Eq(p.indexed[0, 0], 3.), Eq(p.indexed[0, 1], 2.), Eq(p.indexed[0, 2], 1.), Eq(p.indexed[0, 3], 0.), Eq(u.indexed[t + 1, ind(x), ind(y), ind(z)], u.indexed[t, x, y, z])] op = Operator(eqs, subs={t.spacing: 1}) op(yu4D=u, t=1) assert 'run_solution' not in str(op) assert all(np.all(u.data[1, :, :, i] == 3 - i) for i in range(4))
def execute_devito(ui, spacing=0.01, a=0.5, timesteps=500): """Execute diffusion stencil using the devito Operator API.""" nx, ny = ui.shape dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults u = TimeData(name='u', shape=(nx, ny), time_order=1, space_order=2) u.data[0, :] = ui[:] # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2)) stencil = solve(eqn, u.forward)[0] op = Operator(stencils=Eq(u.forward, stencil), subs={ h: spacing, s: dt }, nt=timesteps, shape=(nx, ny), spc_border=1, time_order=1) # Execute the generated Devito stencil operator tstart = time.time() op.apply() runtime = time.time() - tstart log("Devito: Diffusion with dx=%0.4f, dy=%0.4f, executed %d timesteps in %f seconds" % (spacing, spacing, timesteps, runtime)) return u.data[1, :], runtime
def test_multiple_loop_nests(self): """ Compute a simple stencil S, preceded by an "initialization loop" I and followed by a "random loop" R. * S is the trivial equation ``u[t+1,x,y,z] = u[t,x,y,z] + 1``; * I initializes ``u`` to 0; * R adds 2 to another field ``v`` along the ``z`` dimension but only over the planes ``[x=0, y=2]`` and ``[x=0, y=5]``. Out of these three loop nests, only S should be "offloaded" to YASK; indeed, I is outside the time loop, while R does not loop over space dimensions. This test checks that S is the only loop nest "offloaded" to YASK, and that the numerical output is correct. """ u = TimeData(name='yu4D', shape=(12, 12, 12), dimensions=(x, y, z), space_order=0) v = TimeData(name='yv4D', shape=(12, 12, 12), dimensions=(x, y, z), space_order=0) v.data[:] = 0. eqs = [Eq(u.indexed[0, x, y, z], 0), Eq(u.indexed[1, x, y, z], 0), Eq(u.forward, u + 1.), Eq(v.indexed[t + 1, 0, 2, z], v.indexed[t + 1, 0, 2, z] + 2.), Eq(v.indexed[t + 1, 0, 5, z], v.indexed[t + 1, 0, 5, z] + 2.)] op = Operator(eqs, subs={t.spacing: 1}) op(yu4D=u, yv4D=v, t=1) assert 'run_solution' in str(op) assert len(retrieve_iteration_tree(op)) == 3 assert np.all(u.data[0] == 0.) assert np.all(u.data[1] == 1.) assert np.all(v.data[0] == 0.) assert np.all(v.data[1, 0, 2] == 2.) assert np.all(v.data[1, 0, 5] == 2.)
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 Forward(self, save=False, dse='advanced', dle='advanced', q=False, free_surface=False): 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 rec = Receiver(name='rec', 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, save=False, dtype=self.model.dtype) if q: qfull = TimeData(name="qfull", shape=self.model.shape_domain, time_dim=nt, time_order=2, space_order=self.s_order, save=True, dtype=self.model.dtype) else: qfull = 0 # Execute operator and return wavefield and receiver data fw = ForwardOperator(self.model, u, src, rec, self.data, qfull, time_order=self.t_order, spc_order=self.s_order, save=save, dse=dse, dle=dle, tsave=self.tsave, free_surface=free_surface) return fw
def test_second_derivatives_space(derivative, dimension, order): """Test second derivative expressions against native sympy""" u = TimeData(name='u', shape=(20, 20, 20), time_order=2, space_order=order) expr = getattr(u, derivative) # Establish native sympy derivative expression width = int(order / 2) indices = [(dimension + i * h) for i in range(-width, width + 1)] s_expr = as_finite_diff(u.diff(dimension, dimension), indices) assert (simplify(expr - s_expr) == 0) # Symbolic equality assert (expr == s_expr) # Exact equailty
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 ForwardOperator(model, source, receiver, time_order=2, space_order=4, save=False, **kwargs): """ Constructor method for the forward modelling 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 :param save : Saving flag, True saves all time steps, False only the three """ m, damp = model.m, model.damp # Create symbols for forward wavefield, source and receivers u = TimeData(name='u', shape=model.shape_domain, time_dim=source.nt, time_order=time_order, space_order=space_order, save=save, dtype=model.dtype) 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) if time_order == 2: biharmonic = 0 dt = model.critical_dt else: biharmonic = u.laplace2(1/m) dt = 1.73 * model.critical_dt # Derive both stencils from symbolic equation: # Create the stencil by hand instead of calling numpy solve for speed purposes # Simple linear solve of a u(t+dt) + b u(t) + c u(t-dt) = L for u(t+dt) stencil = 1 / (2 * m + s * damp) * ( 4 * m * u + (s * damp - 2 * m) * u.backward + 2 * s**2 * (u.laplace + s**2 / 12 * biharmonic)) eqn = [Eq(u.forward, stencil)] # Construct expression to inject source values # Note that src and field terms have differing time indices: # src[time, ...] - always accesses the "unrolled" time index # u[ti + 1, ...] - accesses the forward stencil value ti = u.indices[0] src_term = src.inject(field=u, u_t=ti + 1, offset=model.nbpml, expr=src * dt**2 / m, p_t=time) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=u, u_t=ti, offset=model.nbpml) return Operator(eqn + src_term + rec_term, subs={s: dt, h: model.get_spacing()}, time_axis=Forward, name='Forward', **kwargs)
def born(self, dmin, src=None, rec=None, u=None, U=None, m=None, **kwargs): """ Linearized Born modelling function that creates the necessary data objects for running an adjoint modelling operator. :param src: Symbol with time series data for the injected source term :param rec: Symbol to store interpolated receiver data :param u: (Optional) Symbol to store the computed wavefield :param U: (Optional) Symbol to store the computed wavefield :param m: (Optional) Symbol for the time-constant square slowness """ # Source term is read-only, so re-use the default if src is None: src = self.source # Create a new receiver object to store the result if rec is None: rec = rec or Receiver(name='rec', ntime=self.receiver.nt, coordinates=self.receiver.coordinates.data) # Create the forward wavefields u and U if not provided if u is None: u = TimeData(name='u', shape=self.model.shape_domain, save=False, time_order=self.time_order, space_order=self.space_order, dtype=self.model.dtype) if U is None: U = TimeData(name='U', 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 = self.model.m # Execute operator and return wavefield and receiver data summary = self.op_born.apply(dm=dmin, u=u, U=U, src=src, rec=rec, m=m, **kwargs) return rec, u, U, summary
def test_dimension_size_override(self, nt=100): """Test explicit overrides for the leading time dimension""" i, j, k = dimify('i j k') a = TimeData(name='a', dimensions=(i, j, k)) one = symbol(name='one', dimensions=(i, j, k), value=1.) op = Operator(Eq(a.forward, a + one)) # Test dimension override via the buffered dimenions a.data[0] = 0. op(a=a, t=6) assert(np.allclose(a.data[1], 5.)) # Test dimension override via the parent dimenions a.data[0] = 0. op(a=a, time=5) assert(np.allclose(a.data[0], 4.))
def test_fixed_halo_w_ofs(self, space_order): """ Compute an N-point stencil sum, where N is the number of points sorrounding an inner (i.e., non-border) grid point. For example (in 2D view): 1 1 1 ... 1 1 1 4 4 ... 4 1 1 4 4 ... 4 1 1 4 4 ... 4 1 1 1 1 ... 1 1 """ v = TimeData(name='yv4D', shape=(16, 16, 16), dimensions=(x, y, z), space_order=space_order) v.data.with_halo[:] = 1. op = Operator(Eq(v.forward, v.laplace + 6*v), subs={t.spacing: 1, x.spacing: 1, y.spacing: 1, z.spacing: 1}) op(yv4D=v, t=1) assert 'run_solution' in str(op) # Chech that the domain size has actually been written to assert np.all(v.data[1] == 6.) # Check that the halo planes are untouched assert all(np.all(v.data.with_halo[1, i, :, :] == 1) for i in range(space_order)) assert all(np.all(v.data.with_halo[1, :, i, :] == 1) for i in range(space_order)) assert all(np.all(v.data.with_halo[1, :, :, i] == 1) for i in range(space_order))
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 run_simulation(save=False, dx=0.01, dy=0.01, a=0.5, timesteps=100): nx, ny = int(1 / dx), int(1 / dy) dx2, dy2 = dx**2, dy**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) u = TimeData(name='u', shape=(nx, ny), time_dim=timesteps, initializer=initializer, time_order=1, space_order=2, save=save) a, h, s = symbols('a h s') eqn = Eq(u.dt, a * (u.dx2 + u.dy2)) stencil = solve(eqn, u.forward)[0] op = Operator(Eq(u.forward, stencil), subs={ a: 0.5, h: dx, s: dt }, time_axis=Forward) op.apply(time=timesteps) if save: return u.data[timesteps - 1, :] else: return u.data[(timesteps + 1) % 2, :]
def test_override_composite_data(self): original_coords = (1., 1.) new_coords = (2., 2.) p_dim = Dimension('p_src') ndim = len(original_coords) u = TimeData(name='u', time_order=2, space_order=2, shape=(10, 10)) src1 = PointData(name='src1', dimensions=[time, p_dim], npoint=1, nt=10, ndim=ndim, coordinates=original_coords) src2 = PointData(name='src1', dimensions=[time, p_dim], npoint=1, nt=10, ndim=ndim, coordinates=new_coords) op = Operator(src1.inject(u, src1)) # Move the source from the location where the setup put it so we can test # whether the override picks up the original coordinates or the changed ones # Operator.arguments() returns a tuple of (data, dimension_sizes) args = op.arguments(src1=src2)[0] arg_name = src1.name + "_coords" assert (np.array_equal(args[arg_name], np.asarray((new_coords, ))))
def c(shape=(11, 11)): """Forward time data object, buffered (save=False)""" return TimeData(name='c', shape=shape, time_order=1, save=False, time_axis=Forward)
def u(self, model): space_order = 4 time_order = 2 return TimeData(name='u', shape=model.shape_domain, dimensions=(x, y, z), space_order=space_order, time_order=time_order)
def test_mixed_space_order(self): """ Make sure that no matter whether data objects have different space order, as long as they have same domain, the Operator will be executed correctly. """ u = TimeData(name='yu4D', shape=(8, 8, 8), dimensions=(x, y, z), space_order=0) v = TimeData(name='yv4D', shape=(8, 8, 8), dimensions=(x, y, z), space_order=1) u.data.with_halo[:] = 1. v.data.with_halo[:] = 2. op = Operator(Eq(v.forward, u + v), subs={t.spacing: 1}) op(yu4D=u, yv4D=v, t=1) assert 'run_solution' in str(op) # Chech that the domain size has actually been written to assert np.all(v.data[1] == 3.) # Check that the halo planes are untouched assert np.all(v.data.with_halo[1, 0, :, :] == 2) assert np.all(v.data.with_halo[1, :, 0, :] == 2) assert np.all(v.data.with_halo[1, :, :, 0] == 2)
def AdjointOperator(model, source, receiver, time_order=2, space_order=4, **kwargs): """ Constructor method for the adjoint modelling 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 v = TimeData(name='v', shape=model.shape_domain, save=False, time_order=time_order, space_order=space_order, dtype=model.dtype) srca = PointSource(name='srca', ntime=source.nt, ndim=source.ndim, npoint=source.npoint) rec = Receiver(name='rec', ntime=receiver.nt, ndim=receiver.ndim, npoint=receiver.npoint) if time_order == 2: biharmonic = 0 dt = model.critical_dt else: biharmonic = v.laplace2(1/m) dt = 1.73 * model.critical_dt # Derive both stencils from symbolic equation stencil = 1 / (2 * m + s * damp) * ( 4 * m * v + (s * damp - 2 * m) * v.forward + 2 * s**2 * (v.laplace + s**2 / 12 * biharmonic)) eqn = Eq(v.backward, stencil) # Construct expression to inject receiver values ti = v.indices[0] receivers = rec.inject(field=v, u_t=ti - 1, offset=model.nbpml, expr=rec * dt**2 / m, p_t=time) # Create interpolation expression for the adjoint-source source_a = srca.interpolate(expr=v, u_t=ti, offset=model.nbpml) return Operator([eqn] + receivers + source_a, subs={s: dt, h: model.get_spacing()}, time_axis=Backward, name='Adjoint', **kwargs)
def test_code(self): shape = (11, 11) a = TimeData(name='a', shape=shape, time_order=1, time_dim=6, save=True) eqn = Eq(a.forward, a + 1.) b = TimeData(name='a', shape=shape, time_order=1, time_dim=6, save=True) eqn2 = Eq(b.forward, b + 1.) op = Operator(eqn) op() op2 = Operator(eqn2, external=True) arg, _ = op.arguments(a=b) op2.cfunction(*list(arg.values())) assert (np.allclose(a.data[:], b.data[:]))
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 forward(self, src=None, rec=None, u=None, m=None, save=False, **kwargs): """ Forward modelling function that creates the necessary data objects for running a forward modelling operator. :param src: Symbol with time series data for the injected source term :param rec: Symbol to store interpolated receiver data :param u: (Optional) Symbol to store the computed wavefield :param m: (Optional) Symbol for the time-constant square slowness :param save: Option to store the entire (unrolled) wavefield :returns: Receiver, wavefield and performance summary """ # Source term is read-only, so re-use the default if src is None: src = self.source # Create a new receiver object to store the result if rec is None: rec = Receiver(name='rec', ntime=self.receiver.nt, coordinates=self.receiver.coordinates.data) # Create the forward wavefield if not provided if u is None: u = TimeData(name='u', shape=self.model.shape_domain, save=save, time_dim=self.source.nt if save else None, 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 # Execute operator and return wavefield and receiver data if save: summary = self.op_fwd_save.apply(src=src, rec=rec, u=u, m=m, **kwargs) else: summary = self.op_fwd.apply(src=src, rec=rec, u=u, m=m, **kwargs) return rec, u, summary
def _new_operator3(shape, time_order, **kwargs): spacing = 0.1 a = 0.5 c = 0.5 dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults u = TimeData(name='u', shape=shape, time_order=1, space_order=2) u.data[0, :] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2) - c * (u.dxl + u.dyl)) stencil = solve(eqn, u.forward, rational=False)[0] op = Operator(Eq(u.forward, stencil), subs={x.spacing: spacing, y.spacing: spacing, t.spacing: dt}, **kwargs) # Execute the generated Devito stencil operator op.apply(u=u, t=10) return u.data[1, :], op
def test_increasing_multi_steps(self): """ Apply the trivial equation ``u[t+1,x,y,z] = u[t,x,y,z] + 1`` for 11 timesteps and check that all grid domain values are equal to 11 within ``u[1]`` and equal to 10 within ``u[0]``. """ u = TimeData(name='yu4D', shape=(8, 8, 8), dimensions=(x, y, z), space_order=0) u.data.with_halo[:] = 0. op = Operator(Eq(u.forward, u + 1.), subs={t.spacing: 1}) op(yu4D=u, t=12) assert 'run_solution' in str(op) assert np.all(u.data[0] == 10.) assert np.all(u.data[1] == 11.)
def Adjoint(self, cache_blocking=None, auto_tuning=False, profile=False, save=False, dse='advanced', dle='advanced', compiler=None, free_surface=False): """Adjoint modelling of a shot record. """ 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 srca = PointSource(name='srca', ntime=self.source.nt, coordinates=self.source.receiver_coords) # Create receiver symbol rec = Receiver(name='rec', ntime=self.data.nt, coordinates=self.data.receiver_coords) # Create the forward wavefield v = TimeData(name="v", shape=self.model.shape_domain, time_dim=nt, time_order=2, space_order=self.s_order, save=save, dtype=self.model.dtype) # Execute operator and return wavefield and receiver data adj = AdjointOperator(self.model, v, srca, rec, 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 adj
def test_explicit_run(self): time_dim = 6 a = TimeData(name='a', shape=(11, 11), time_order=1, time_dim=time_dim, save=True) eqn = Eq(a.forward, a + 1.) op = Operator(eqn) assert isinstance(op, OperatorForeign) args = OrderedDict(op.arguments()) assert args['a'] is None # Emulate data feeding from outside array = np.ndarray(shape=a.shape, dtype=np.float32) array.fill(0.0) args['a'] = array op.cfunction(*list(args.values())) assert all(np.allclose(args['a'][i], i) for i in range(time_dim))
def test_increasing_halo_wo_ofs(self, space_order, nosimd): """ Apply the trivial equation ``u[t+1,x,y,z] = u[t,x,y,z] + 1`` and check that increasing space orders lead to proportionately larger halo regions, which are *not* written by the Operator. For example, with ``space_order = 0``, produce (in 2D view): 1 1 1 ... 1 1 1 1 1 ... 1 1 1 1 1 ... 1 1 1 1 1 ... 1 1 1 1 1 ... 1 1 With ``space_order = 1``, produce: 0 0 0 0 0 0 0 0 0 0 1 1 1 ... 1 1 0 0 1 1 1 ... 1 1 0 0 1 1 1 ... 1 1 0 0 1 1 1 ... 1 1 0 0 1 1 1 ... 1 1 0 0 0 0 0 0 0 0 0 0 And so on and so forth. """ # SIMD on/off yask_configuration['develop-mode'] = nosimd u = TimeData(name='yu4D', shape=(16, 16, 16), dimensions=(x, y, z), space_order=space_order) u.data.with_halo[:] = 0. op = Operator(Eq(u.forward, u + 1.), subs={t.spacing: 1}) op(yu4D=u, t=1) assert 'run_solution' in str(op) # Chech that the domain size has actually been written to assert np.all(u.data[1] == 1.) # Check that the halo planes are still 0 assert all( np.all(u.data.with_halo[1, i, :, :] == 0) for i in range(space_order)) assert all( np.all(u.data.with_halo[1, :, i, :] == 0) for i in range(space_order)) assert all( np.all(u.data.with_halo[1, :, :, i] == 0) for i in range(space_order))
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 _new_operator2(shape, time_order, **kwargs): infield = TimeData(name='infield', shape=shape, time_order=time_order, dtype=np.int32) infield.data[:] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) outfield = TimeData(name='outfield', shape=shape, time_order=time_order, dtype=np.int32) stencil = Eq(outfield.forward.indexify(), outfield.indexify() + infield.indexify()*3.0) # Run the operator op = Operator(stencil, **kwargs) op(infield=infield, outfield=outfield, t=10) return outfield, op
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 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