def _setup_model_and_acquisition(self, shape, spacing, nbpml, tn): nrec = shape[0] model = demo_model('layers-isotropic', shape=shape, spacing=spacing, nbpml=nbpml) self.model = model t0 = 0.0 self.nt = int(1 + (tn-t0) / self.dt) # Number of timesteps time = np.linspace(t0, tn, self.nt) # Discretized time axis # Define source geometry (center of domain, just below surface) src = RickerSource(name='src', grid=model.grid, f0=0.01, time=time) src.coordinates.data[0, :] = np.array(model.domain_size) * .5 src.coordinates.data[0, -1] = model.origin[-1] + 2 * spacing[-1] self.src = src # Define receiver geometry (spread across x, just below surface) # We need two receiver fields - one for the true (verification) run rec_t = Receiver(name='rec_t', grid=model.grid, ntime=self.nt, npoint=nrec) rec_t.coordinates.data[:, 0] = np.linspace(0., model.domain_size[0], num=nrec) rec_t.coordinates.data[:, 1:] = src.coordinates.data[0, 1:] self.rec_t = rec_t # and the other for the smoothed run self.rec = Receiver(name='rec', grid=model.grid, ntime=self.nt, npoint=nrec, coordinates=rec_t.coordinates.data) # Receiver for Gradient self.rec_g = Receiver(name="rec_g", coordinates=self.rec.coordinates.data, grid=model.grid, dt=self.dt, ntime=self.nt) # Gradient symbol self.grad = Function(name="grad", grid=model.grid)
def AdjointOperator(model, source, receiver, space_order=4, kernel='OT2', **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 = TimeFunction(name='v', grid=model.grid, save=None, time_order=2, space_order=space_order) srca = PointSource(name='srca', grid=model.grid, time_range=source.time_range, npoint=source.npoint) rec = Receiver(name='rec', grid=model.grid, time_range=receiver.time_range, npoint=receiver.npoint) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, m, s, damp, kernel, forward=False) # Construct expression to inject receiver values receivers = rec.inject(field=v.backward, expr=rec * s**2 / m, offset=model.nbpml) # Create interpolation expression for the adjoint-source source_a = srca.interpolate(expr=v, offset=model.nbpml) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + source_a, subs=model.spacing_map, name='Adjoint', **kwargs)
def fwi_gradient(vp_in): # Create symbols to hold the gradient and residual grad = Function(name="grad", grid=model.grid) residual = Receiver(name='rec', grid=model0.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) objective = 0. myshots_left=nshots*rank//size myshots_right=nshots*(rank+1)//size for i in range(myshots_left,myshots_right): # Update source location geometry.src_positions[0, :] = source_locations[i, :] # true data from file rec= np.load(path+'project2_data_shot_'+str(i)+'.npy',allow_pickle=True) true_d = Receiver(name='rec', grid=model.grid, npoint=nreceivers, time_range=geometry.time_axis,coordinates=geometry.rec_positions,data=rec[:,label_left[edge_num]:label_right[edge_num]]) #true_d, _, _ = solver.forward(vp=model.vp) #print(true_d.data[:,3]) #print(true_d.data.shape) # Compute smooth data and full forward wavefield u0 smooth_d, u0, _ = solver.forward(vp=vp_in, save=True) #print(smooth_d.data.shape) # Compute gradient from data residual and update objective function residual.data[:] = smooth_d.data[:] - true_d.data[:] objective += .5*np.linalg.norm(residual.data.flatten())**2 solver.gradient(rec=residual, u=u0, vp=vp_in, grad=grad) objective = comm.reduce(objective, root=0,op=MPI.SUM) grad = comm.reduce(grad, root=0,op=MPI.SUM) if (rank == 0): return objective, -grad.data
def fwi_gradient(vp_in): # Create symbols to hold the gradient grad = Function(name="grad", grid=model.grid) objective = 0. for i in range(nshots): # Create placeholders for the data residual and data residual = Receiver(name='residual', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) d_obs = Receiver(name='d_obs', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) d_syn = Receiver(name='d_syn', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) # Update source location solver.geometry.src_positions[0, :] = source_locations[i, :] # Generate synthetic data from true model solver.forward(vp=model.vp, rec=d_obs) # Compute smooth data and full forward wavefield u0 _, u0, _ = solver.forward(vp=vp_in, save=True, rec=d_syn) # Compute gradient from data residual and update objective function residual = compute_residual(residual, d_obs, d_syn) objective += .5 * norm(residual)**2 solver.gradient(rec=residual, u=u0, vp=vp_in, grad=grad) return objective, grad
def GradientOperator(model, source, receiver, space_order=4, save=True, kernel='OT2', **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 = Function(name='grad', grid=model.grid) u = TimeFunction(name='u', grid=model.grid, save=source.nt if save else None, time_order=2, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, save=None, time_order=2, space_order=space_order) rec = Receiver(name='rec', grid=model.grid, time_range=receiver.time_range, npoint=receiver.npoint) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, m, s, damp, kernel, forward=False) if kernel == 'OT2': gradient_update = Inc(grad, - u.dt2 * v) elif kernel == 'OT4': gradient_update = Inc(grad, - (u.dt2 + s**2 / 12.0 * u.laplace2(m**(-2))) * v) # Add expression for receiver injection receivers = rec.inject(field=v.backward, expr=rec * s**2 / m) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + [gradient_update], subs=model.spacing_map, name='Gradient', **kwargs)
def ForwardOperator(model, geometry, space_order=4, save=False, kernel='OT2', **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 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 = TimeFunction(name='u', grid=model.grid, save=geometry.nt if save else None, time_order=2, space_order=space_order) src = PointSource(name='src', grid=geometry.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=geometry.grid, time_range=geometry.time_axis, npoint=geometry.nrec) s = model.grid.stepping_dim.spacing eqn = iso_stencil(u, m, s, damp, kernel) # Construct expression to inject source values src_term = src.inject(field=u.forward, expr=src * s**2 / m) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=u) # Substitute spacing terms to reduce flops return Operator(eqn + src_term + rec_term, subs=model.spacing_map, name='Forward', **kwargs)
def AdjointOperator(model, geometry, space_order=4, kernel='OT2', **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 = TimeFunction(name='v', grid=model.grid, save=None, time_order=2, space_order=space_order) srca = PointSource(name='srca', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, m, s, damp, kernel, forward=False) # Construct expression to inject receiver values receivers = rec.inject(field=v.backward, expr=rec * s**2 / m) # Create interpolation expression for the adjoint-source source_a = srca.interpolate(expr=v) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + source_a, subs=model.spacing_map, name='Adjoint', **kwargs)
def GradientOperator(model, geometry, space_order=4, save=True, kernel='OT2', **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 = Function(name='grad', grid=model.grid) u = TimeFunction(name='u', grid=model.grid, save=geometry.nt if save else None, time_order=2, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, save=None, time_order=2, space_order=space_order) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, m, s, damp, kernel, forward=False) if kernel == 'OT2': gradient_update = Inc(grad, - u.dt2 * v) elif kernel == 'OT4': gradient_update = Inc(grad, - (u.dt2 + s**2 / 12.0 * u.laplace2(m**(-2))) * v) # Add expression for receiver injection receivers = rec.inject(field=v.backward, expr=rec * s**2 / m) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + [gradient_update], subs=model.spacing_map, name='Gradient', **kwargs)
def src_rec(p, model, geometry, **kwargs): """ Forward case: Source injection and receiver interpolation Adjoint case: Receiver injection and source interpolation """ dt = model.grid.time_dim.spacing m = model.m # Source symbol with input wavelet src = PointSource(name="src", grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) forward = kwargs.get('forward', True) time_order = p.time_order if forward: # The source injection term if(time_order == 1): src_term = src.inject(field=p.forward, expr=src * dt) else: src_term = src.inject(field=p.forward, expr=src * dt**2 / m) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=p) else: # Construct expression to inject receiver values if(time_order == 1): rec_term = rec.inject(field=p.backward, expr=rec * dt) else: rec_term = rec.inject(field=p.backward, expr=rec * dt**2 / m) # Create interpolation expression for the adjoint-source src_term = src.interpolate(expr=p) return src_term + rec_term
def fwi_gradient(vp_in): # Create symbols to hold the gradient and residual grad = Function(name="grad", grid=model.grid) residual = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) objective = 0. # Creat forward wavefield to reuse to avoid memory overload u0 = TimeFunction(name='u', grid=model.grid, time_order=2, space_order=4, save=geometry.nt) for i in range(nshots): # Important: We force previous wavefields to be destroyed, # so that we may reuse the memory. clear_cache() # Update source location geometry.src_positions[0, :] = source_locations[i, :] # Generate synthetic data from true model true_d, _, _ = solver.forward(vp=model.vp) # Compute smooth data and full forward wavefield u0 u0.data.fill(0.) smooth_d, _, _ = solver.forward(vp=vp_in, save=True, u=u0) # Compute gradient from data residual and update objective function residual.data[:] = smooth_d.data[:] - true_d.data[:] objective += .5*np.linalg.norm(residual.data.flatten())**2 solver.gradient(rec=residual, u=u0, vp=vp_in, grad=grad) return objective, -grad.data
def fwi_gradient(m_in): # Important: We force previous wavefields to be destroyed, # so that we may reuse the memory. clear_cache() # Create symbols to hold the gradient and residual grad = Function(name="grad", grid=model.grid) residual = Receiver(name='rec', grid=model.grid, ntime=nt, coordinates=rec.coordinates.data) objective = 0. for i in range(nshots): # Update source location src.coordinates.data[0, :] = source_locations[i, :] # Generate synthetic data from true model true_d, _, _ = solver.forward(src=src, m=model.m) # Compute smooth data and full forward wavefield u0 smooth_d, u0, _ = solver.forward(src=src, m=m_in, save=True) # Compute gradient from data residual and update objective function residual.data[:] = smooth_d.data[:] - true_d.data[:] objective += .5 * np.linalg.norm(residual.data.reshape(-1))**2 solver.gradient(rec=residual, u=u0, m=m_in, grad=grad) return objective, grad.data
def src_rec(vx, vy, vz, txx, tyy, tzz, model, geometry): """ Source injection and receiver interpolation """ s = model.grid.time_dim.spacing # Source symbol with input wavelet src = PointSource(name='src', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec1 = Receiver(name='rec1', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) rec2 = Receiver(name='rec2', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # The source injection term src_xx = src.inject(field=txx.forward, expr=src * s) src_zz = src.inject(field=tzz.forward, expr=src * s) src_expr = src_xx + src_zz if model.grid.dim == 3: src_yy = src.inject(field=tyy.forward, expr=src * s) src_expr += src_yy # Create interpolation expression for receivers rec_term1 = rec1.interpolate(expr=tzz) if model.grid.dim == 2: rec_expr = vx.dx + vz.dy else: rec_expr = vx.dx + vy.dy + vz.dz rec_term2 = rec2.interpolate(expr=rec_expr) return src_expr + rec_term1 + rec_term2
def ForwardOperator(model, source, receiver, space_order=4, save=False, kernel='OT2', **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 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 = TimeFunction(name='u', grid=model.grid, save=source.nt if save else None, time_order=2, space_order=space_order) src = PointSource(name='src', grid=model.grid, time_range=source.time_range, npoint=source.npoint) rec = Receiver(name='rec', grid=model.grid, time_range=receiver.time_range, npoint=receiver.npoint) s = model.grid.stepping_dim.spacing eqn = iso_stencil(u, m, s, damp, kernel) # Construct expression to inject source values src_term = src.inject(field=u.forward, expr=src * s**2 / m, offset=model.nbpml) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=u, offset=model.nbpml) # Substitute spacing terms to reduce flops return Operator(eqn + src_term + rec_term, subs=model.spacing_map, name='Forward', **kwargs)
def fwi_gradient_i(x, param): # Need to clear the workers cache. clear_cache() # Get the current model and the shot data for this worker. model0 = get_initial_model() model0.m.data[:] = x.astype(np.float32).reshape(model0.m.data.shape) src, rec, nt, solver = get_data(param) # Create symbols to hold the gradient and the misfit between # the 'measured' and simulated data. grad = Function(name="grad", grid=model0.grid) residual = Receiver(name='rec', grid=model0.grid, ntime=nt, coordinates=rec.coordinates.data) # Compute simulated data and full forward wavefield u0 d, u0, _ = solver.forward(src=src, m=model0.m, save=True) # Compute the data misfit (residual) and objective function residual.data[:] = d.data[:] - rec.data[:] f = .5*np.linalg.norm(residual.data.flatten())**2 # Compute gradient using the adjoint-state method. Note, this # backpropagates the data misfit through the model. solver.gradient(rec=residual, u=u0, m=model0.m, grad=grad) # return the objective functional and gradient. return f, np.array(grad.data)
def GradientOperator(model, source, receiver, time_order=2, space_order=4, save=True, **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 = Function(name='grad', grid=model.grid) u = TimeFunction(name='u', grid=model.grid, save=save, time_dim=source.nt if save else None, time_order=2, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, save=False, time_order=2, space_order=space_order) rec = Receiver(name='rec', grid=model.grid, ntime=receiver.nt, npoint=receiver.npoint) # Get computational time-step value dt = model.critical_dt * (1.73 if time_order == 4 else 1.0) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, time_order, m, s, damp, forward=False) if time_order == 2: gradient_update = Eq(grad, grad - u.dt2 * v) else: gradient_update = Eq( grad, grad - (u.dt2 + s**2 / 12.0 * u.laplace2(m**(-2))) * v) # Add expression for receiver injection receivers = rec.inject(field=v.backward, expr=rec * dt**2 / m, offset=model.nbpml) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + [gradient_update], subs=model.spacing_map, time_axis=Backward, name='Gradient', **kwargs)
def ForwardOperator(model, source, receiver, space_order=4, save=False, kernel='centered', **kwargs): """ Constructor method for the forward modelling operator in an acoustic media :param model: :class:`Model` object containing the physical parameters :param src: None ot IShot() (not currently supported properly) :param data: IShot() object containing the acquisition geometry and field data :param: time_order: Time discretization order :param: spc_order: Space discretization order """ dt = model.grid.time_dim.spacing m = model.m time_order = 1 if kernel == 'staggered' else 2 # Create symbols for forward wavefield, source and receivers u = TimeFunction(name='u', grid=model.grid, save=source.nt if save else None, time_order=time_order, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, save=source.nt if save else None, time_order=time_order, space_order=space_order) src = PointSource(name='src', grid=model.grid, time_range=source.time_range, npoint=source.npoint) rec = Receiver(name='rec', grid=model.grid, time_range=receiver.time_range, npoint=receiver.npoint) # FD kernels of the PDE FD_kernel = kernels[(kernel, len(model.shape))] stencils = FD_kernel(model, u, v, space_order) # Source and receivers stencils += src.inject(field=u.forward, expr=src * dt**2 / m, offset=model.nbpml) stencils += src.inject(field=v.forward, expr=src * dt**2 / m, offset=model.nbpml) stencils += rec.interpolate(expr=u + v, offset=model.nbpml) # Substitute spacing terms to reduce flops return Operator(stencils, subs=model.spacing_map, name='ForwardTTI', **kwargs)
def test_acoustic(mkey, shape, kernel, space_order, nbpml): t0 = 0.0 # Start time tn = 500. # Final time nrec = 130 # Number of receivers # Create model from preset model = demo_model(spacing=[15. for _ in shape], dtype=np.float64, space_order=space_order, shape=shape, nbpml=nbpml, **(presets[mkey])) # Derive timestepping from model spacing dt = model.critical_dt * (1.73 if kernel == 'OT4' else 1.0) time_range = TimeAxis(start=t0, stop=tn, step=dt) # Define source geometry (center of domain, just below surface) src = RickerSource(name='src', grid=model.grid, f0=0.01, time_range=time_range) src.coordinates.data[0, :] = np.array(model.domain_size) * .5 src.coordinates.data[0, -1] = 30. # Define receiver geometry (same as source, but spread across x) rec = Receiver(name='rec', grid=model.grid, time_range=time_range, npoint=nrec) rec.coordinates.data[:, 0] = np.linspace(0., model.domain_size[0], num=nrec) rec.coordinates.data[:, 1:] = src.coordinates.data[0, 1:] # Create solver object to provide relevant operators solver = AcousticWaveSolver(model, source=src, receiver=rec, kernel=kernel, space_order=space_order) # Create adjoint receiver symbol srca = Receiver(name='srca', grid=model.grid, time_range=solver.source.time_range, coordinates=solver.source.coordinates.data) # Run forward and adjoint operators rec, _, _ = solver.forward(save=False) solver.adjoint(rec=rec, srca=srca) # Adjoint test: Verify <Ax,y> matches <x, A^Ty> closely term1 = np.dot(srca.data.reshape(-1), solver.source.data) term2 = linalg.norm(rec.data)**2 info('<Ax,y>: %f, <x, A^Ty>: %f, difference: %12.12f, ratio: %f' % (term1, term2, (term1 - term2) / term1, term1 / term2)) assert np.isclose((term1 - term2) / term1, 0., rtol=1.e-10)
def GradientOperator(model, geometry, space_order=4, save=True, kernel='OT2', **kwargs): """ Construct a gradient operator in an acoustic media. Parameters ---------- model : Model Object containing the physical parameters. geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. space_order : int, optional Space discretization order. save : int or Buffer, optional Option to store the entire (unrolled) wavefield. kernel : str, optional Type of discretization, centered or shifted. """ m, damp = model.m, model.damp # Gradient symbol and wavefield symbols grad = Function(name='grad', grid=model.grid) u = TimeFunction(name='u', grid=model.grid, save=geometry.nt if save else None, time_order=2, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, save=None, time_order=2, space_order=space_order) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, m, s, damp, kernel, forward=False) if kernel == 'OT2': gradient_update = Inc(grad, -u.dt2 * v) elif kernel == 'OT4': gradient_update = Inc( grad, -(u.dt2 + s**2 / 12.0 * u.biharmonic(m**(-2))) * v) # Add expression for receiver injection receivers = rec.inject(field=v.backward, expr=rec * s**2 / m) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + [gradient_update], subs=model.spacing_map, name='Gradient', **kwargs)
def test_position(shape): t0 = 0.0 # Start time tn = 500. # Final time nrec = 130 # Number of receivers # Create model from preset model = demo_model('constant-isotropic', spacing=[15. for _ in shape], shape=shape, nbpml=10) # Derive timestepping from model spacing dt = model.critical_dt nt = int(1 + (tn - t0) / dt) # Number of timesteps time_values = np.linspace(t0, tn, nt) # Discretized time axis # Define source geometry (center of domain, just below surface) src = RickerSource(name='src', grid=model.grid, f0=0.01, time=time_values) src.coordinates.data[0, :] = np.array(model.domain_size) * .5 src.coordinates.data[0, -1] = 30. # Define receiver geometry (same as source, but spread across x) rec = Receiver(name='nrec', grid=model.grid, ntime=nt, npoint=nrec) rec.coordinates.data[:, 0] = np.linspace(0., model.domain_size[0], num=nrec) rec.coordinates.data[:, 1:] = src.coordinates.data[0, 1:] # Create solver object to provide relevant operators solver = AcousticWaveSolver(model, source=src, receiver=rec, time_order=2, space_order=4) rec, u, _ = solver.forward(save=False) # Define source geometry (center of domain, just below surface) with 100. origin src = RickerSource(name='src', grid=model.grid, f0=0.01, time=time_values) src.coordinates.data[0, :] = np.array(model.domain_size) * .5 + 100. src.coordinates.data[0, -1] = 130. # Define receiver geometry (same as source, but spread across x) rec2 = Receiver(name='rec2', grid=model.grid, ntime=nt, npoint=nrec) rec2.coordinates.data[:, 0] = np.linspace(100., 100. + model.domain_size[0], num=nrec) rec2.coordinates.data[:, 1:] = src.coordinates.data[0, 1:] rec1, u1, _ = solver.forward(save=False, src=src, rec=rec2, o_x=100., o_y=100., o_z=100.) assert (np.allclose(rec.data, rec1.data, atol=1e-5))
def BornOperator(model, source, receiver, space_order=4, kernel='OT2', **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', grid=model.grid, time_range=source.time_range, npoint=source.npoint) rec = Receiver(name='rec', grid=model.grid, time_range=receiver.time_range, npoint=receiver.npoint) # Create wavefields and a dm field u = TimeFunction(name="u", grid=model.grid, save=None, time_order=2, space_order=space_order) U = TimeFunction(name="U", grid=model.grid, save=None, time_order=2, space_order=space_order) dm = Function(name="dm", grid=model.grid, space_order=0) s = model.grid.stepping_dim.spacing eqn1 = iso_stencil(u, m, s, damp, kernel) eqn2 = iso_stencil(U, m, s, damp, kernel, q=-dm * u.dt2) # Add source term expression for u source = src.inject(field=u.forward, expr=src * s**2 / m, offset=model.nbpml) # Create receiver interpolation expression from U receivers = rec.interpolate(expr=U, offset=model.nbpml) # Substitute spacing terms to reduce flops return Operator(eqn1 + source + eqn2 + receivers, subs=model.spacing_map, name='Born', **kwargs)
def reference_shot(model, time_range, f0): """ Produce a reference shot gather with a level, conventional free-surface implementation. """ src = RickerSource(name='src', grid=model.grid, f0=f0, npoint=1, time_range=time_range) # First, position source centrally in all dimensions, then set depth src.coordinates.data[0, :] = np.array(model.domain_size) * .5 # Remember that 0, 0, 0 is top left corner # Depth is 100m from free-surface boundary src.coordinates.data[0, -1] = 600. # Create symbol for 101 receivers rec = Receiver(name='rec', grid=model.grid, npoint=101, time_range=time_range) # Prescribe even spacing for receivers along the x-axis rec.coordinates.data[:, 0] = np.linspace(0, model.domain_size[0], num=101) rec.coordinates.data[:, 1] = 500. # Centered on y axis rec.coordinates.data[:, 2] = 650. # Depth is 150m from free surface # Define the wavefield with the size of the model and the time dimension u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=4) # We can now write the PDE pde = model.m * u.dt2 - u.laplace + model.damp * u.dt stencil = Eq(u.forward, solve(pde, u.forward)) # Finally we define the source injection and receiver read function src_term = src.inject(field=u.forward, expr=src * model.critical_dt**2 / model.m) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=u.forward) x, y, z = model.grid.dimensions time = u.grid.stepping_dim # Throw a free surface in here free_surface_0 = Eq(u[time + 1, x, y, 60], 0) free_surface_1 = Eq(u[time + 1, x, y, 59], -u[time + 1, x, y, 61]) free_surface_2 = Eq(u[time + 1, x, y, 58], -u[time + 1, x, y, 62]) free_surface = [free_surface_0, free_surface_1, free_surface_2] op = Operator([stencil] + src_term + rec_term + free_surface) op(time=time_range.num - 1, dt=model.critical_dt) return rec.data
def ForwardOperator(model, geometry, space_order=4, save=False, kernel='OT2', **kwargs): """ Construct a forward modelling operator in an acoustic medium. Parameters ---------- model : Model Object containing the physical parameters. geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. space_order : int, optional Space discretization order. save : int or Buffer, optional Saving flag, True saves all time steps. False saves three timesteps. Defaults to False. kernel : str, optional Type of discretization, 'OT2' or 'OT4'. """ m = model.m # Create symbols for forward wavefield, source and receivers u = TimeFunction(name='u', grid=model.grid, save=geometry.nt if save else None, time_order=2, space_order=space_order) src = PointSource(name='src', grid=geometry.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=geometry.grid, time_range=geometry.time_axis, npoint=geometry.nrec) s = model.grid.stepping_dim.spacing eqn = iso_stencil(u, model, kernel) # Construct expression to inject source values src_term = src.inject(field=u.forward, expr=src * s**2 / m) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=u) # Substitute spacing terms to reduce flops return Operator(eqn + src_term + rec_term, subs=model.spacing_map, name='Forward', **kwargs)
def forward(self, src=None, rec1=None, rec2=None, lam=None, mu=None, b=None, v=None, tau=None, save=None, **kwargs): """ Forward modelling function that creates the necessary data objects for running a forward modelling operator. Parameters ---------- src : SparseTimeFunction or array_like, optional Time series data for the injected source term. rec1 : SparseTimeFunction or array_like, optional The interpolated receiver data of the pressure (tzz). rec2 : SparseTimeFunction or array_like, optional The interpolated receiver data of the particle velocities. v : VectorTimeFunction, optional The computed particle velocity. tau : TensorTimeFunction, optional The computed symmetric stress tensor. lam : Function, optional The time-constant first Lame parameter `rho * (vp**2 - 2 * vs **2)`. mu : Function, optional The Shear modulus `(rho * vs*2)`. b : Function, optional The time-constant inverse density (b=1 for water). save : int or Buffer, optional Option to store the entire (unrolled) wavefield. Returns ------- Rec1(tzz), Rec2(div(v)), particle velocities v, stress tensor tau and performance summary. """ # Source term is read-only, so re-use the default src = src or self.geometry.src # Create a new receiver object to store the result rec1 = rec1 or Receiver(name='rec1', grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.rec_positions) rec2 = rec2 or Receiver(name='rec2', grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.rec_positions) # Create all the fields vx, vz, tau_xx, tau_zz, tau_xz save_t = src.nt if save else None v = VectorTimeFunction(name='v', grid=self.model.grid, save=save_t, space_order=self.space_order, time_order=1) tau = TensorTimeFunction(name='tau', grid=self.model.grid, save=save_t, space_order=self.space_order, time_order=1) kwargs.update({k.name: k for k in v}) kwargs.update({k.name: k for k in tau}) # Pick Lame parameters from model unless explicitly provided kwargs.update(self.model.physical_params(lam=lam, mu=mu, b=b)) # Execute operator and return wavefield and receiver data summary = self.op_fwd(save).apply(src=src, rec1=rec1, rec2=rec2, dt=kwargs.pop('dt', self.dt), **kwargs) return rec1, rec2, v, tau, summary
def iso_acoustic(self, opt): shape = (101, 101) extent = (1000, 1000) origin = (0., 0.) v = np.empty(shape, dtype=np.float32) v[:, :51] = 1.5 v[:, 51:] = 2.5 grid = Grid(shape=shape, extent=extent, origin=origin) t0 = 0. tn = 1000. dt = 1.6 time_range = TimeAxis(start=t0, stop=tn, step=dt) f0 = 0.010 src = RickerSource(name='src', grid=grid, f0=f0, npoint=1, time_range=time_range) domain_size = np.array(extent) src.coordinates.data[0, :] = domain_size * .5 src.coordinates.data[0, -1] = 20. rec = Receiver(name='rec', grid=grid, npoint=101, time_range=time_range) rec.coordinates.data[:, 0] = np.linspace(0, domain_size[0], num=101) rec.coordinates.data[:, 1] = 20. u = TimeFunction(name="u", grid=grid, time_order=2, space_order=2) m = Function(name='m', grid=grid) m.data[:] = 1. / (v * v) pde = m * u.dt2 - u.laplace stencil = Eq(u.forward, solve(pde, u.forward)) src_term = src.inject(field=u.forward, expr=src * dt**2 / m) rec_term = rec.interpolate(expr=u.forward) op = Operator([stencil] + src_term + rec_term, opt=opt, language='openmp') # Make sure we've indeed generated OpenMP offloading code assert 'omp target' in str(op) op(time=time_range.num - 1, dt=dt) assert np.isclose(norm(rec), 490.55, atol=1e-2, rtol=0)
def AdjointOperator(model, geometry, space_order=4, **kwargs): """ Construct an adjoint modelling operator in an tti media. Parameters ---------- model : Model Object containing the physical parameters. geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. space_order : int, optional Space discretization order. """ dt = model.grid.time_dim.spacing m = model.m time_order = 2 # Create symbols for forward wavefield, source and receivers p = TimeFunction(name='p', grid=model.grid, save=None, time_order=time_order, space_order=space_order) r = TimeFunction(name='r', grid=model.grid, save=None, time_order=time_order, space_order=space_order) srca = PointSource(name='srca', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # FD kernels of the PDE FD_kernel = kernels[('centered', len(model.shape))] stencils = FD_kernel(model, p, r, space_order, forward=False) # Construct expression to inject receiver values stencils += rec.inject(field=p.backward, expr=rec * dt**2 / m) stencils += rec.inject(field=r.backward, expr=rec * dt**2 / m) # Create interpolation expression for the adjoint-source stencils += srca.interpolate(expr=p + r) # Substitute spacing terms to reduce flops return Operator(stencils, subs=model.spacing_map, name='AdjointTTI', **kwargs)
def ForwardOperator(model, geometry, space_order=4, save=False, kernel='centered', **kwargs): """ Construct an forward modelling operator in an acoustic media. Parameters ---------- model : Model Object containing the physical parameters. geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. data : ndarray IShot() object containing the acquisition geometry and field data. time_order : int Time discretization order. space_order : int Space discretization order. """ dt = model.grid.time_dim.spacing m = model.m time_order = 1 if kernel == 'staggered' else 2 if kernel == 'staggered': dims = model.space_dimensions stagg_u = (-dims[-1]) stagg_v = (-dims[0], -dims[1]) if model.grid.dim == 3 else (-dims[0]) else: stagg_u = stagg_v = None # Create symbols for forward wavefield, source and receivers u = TimeFunction(name='u', grid=model.grid, staggered=stagg_u, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, staggered=stagg_v, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) src = PointSource(name='src', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # FD kernels of the PDE FD_kernel = kernels[(kernel, len(model.shape))] stencils = FD_kernel(model, u, v, space_order) # Source and receivers stencils += src.inject(field=u.forward, expr=src * dt**2 / m) stencils += src.inject(field=v.forward, expr=src * dt**2 / m) stencils += rec.interpolate(expr=u + v) # Substitute spacing terms to reduce flops return Operator(stencils, subs=model.spacing_map, name='ForwardTTI', **kwargs)
def ForwardOperator(model, geometry, space_order=4, save=False, kernel='centered', **kwargs): """ Construct an forward modelling operator in an tti media. Parameters ---------- model : Model Object containing the physical parameters. geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. space_order : int, optional Space discretization order. save : int or Buffer, optional Saving flag, True saves all time steps. False saves three timesteps. Defaults to False. kernel : str, optional Type of discretization, centered or shifted """ dt = model.grid.time_dim.spacing m = model.m time_order = 1 if kernel == 'staggered' else 2 if kernel == 'staggered': stagg_u = stagg_v = NODE else: stagg_u = stagg_v = None # Create symbols for forward wavefield, source and receivers u = TimeFunction(name='u', grid=model.grid, staggered=stagg_u, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, staggered=stagg_v, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) src = PointSource(name='src', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # FD kernels of the PDE FD_kernel = kernels[(kernel, len(model.shape))] stencils = FD_kernel(model, u, v, space_order) # Source and receivers expr = src * dt / m if kernel == 'staggered' else src * dt**2 / m stencils += src.inject(field=u.forward, expr=expr) stencils += src.inject(field=v.forward, expr=expr) stencils += rec.interpolate(expr=u + v) # Substitute spacing terms to reduce flops return Operator(stencils, subs=model.spacing_map, name='ForwardTTI', **kwargs)
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 test_receiver(): grid = Grid(shape=(3,)) time_range = TimeAxis(start=0., stop=1000., step=0.1) nreceivers = 3 rec = Receiver(name='rec', grid=grid, time_range=time_range, npoint=nreceivers, coordinates=[(0.,), (1.,), (2.,)]) rec.data[:] = 1. pkl_rec = pickle.dumps(rec) new_rec = pickle.loads(pkl_rec) assert np.all(new_rec.data == 1) assert np.all(new_rec.coordinates.data == [[0.], [1.], [2.]])
def JacobianAdjOperator(model, geometry, space_order=4, save=True, **kwargs): """ Construct a linearized JacobianAdjoint modeling Operator in a TTI media. Parameters ---------- model : Model Object containing the physical parameters. geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. space_order : int, optional Space discretization order. save : int or Buffer, optional Option to store the entire (unrolled) wavefield. """ dt = model.grid.stepping_dim.spacing m = model.m time_order = 2 # Gradient symbol and wavefield symbols u0 = TimeFunction(name='u0', grid=model.grid, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) v0 = TimeFunction(name='v0', grid=model.grid, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) du = TimeFunction(name="du", grid=model.grid, save=None, time_order=time_order, space_order=space_order) dv = TimeFunction(name="dv", grid=model.grid, save=None, time_order=time_order, space_order=space_order) dm = Function(name="dm", grid=model.grid) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # FD kernels of the PDE FD_kernel = kernels[('centered', len(model.shape))] eqn = FD_kernel(model, du, dv, space_order, forward=False) dm_update = Inc(dm, - (u0 * du.dt2 + v0 * dv.dt2)) # Add expression for receiver injection rec_term = rec.inject(field=du.backward, expr=rec * dt**2 / m) rec_term += rec.inject(field=dv.backward, expr=rec * dt**2 / m) # Substitute spacing terms to reduce flops return Operator(eqn + rec_term + [dm_update], subs=model.spacing_map, name='GradientTTI', **kwargs)
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 __init__(self, model, src, rec_geom, time_start, time_end, space_order=16, **kwargs): self.data = Receiver(name='rec', grid=model.grid, npoint=rec_geom.shape[0], time_range=src.time_range, coordinates=rec_geom) self.source = Receiver(name='src', grid=model.grid, npoint=src.npoint, time_range=src.time_range, coordinates=src.coordinates.data) self.source.data[:] = src.data[:] self.space_order = space_order self.kwargs = kwargs self.model = model self.time_start = time_start self.time_end = time_end self.space_order = space_order self.solver = AcousticWaveSolver(model, source=self.source, receiver=self.data, space_order=space_order, **kwargs) self.grid = copy.copy(self.model.grid) self.grid.shape = (self.model.grid.shape[0] * 3, self.model.grid.shape[1]) u = Function(name="u", grid=self.grid, space_order=space_order) domain = DevitoSet(u) im = DevitoSet(u) super(F, self).__init__(domain=domain, range=im) self.FT = FT(self.model, self.source, self.data.coordinates.data, self.data.data, self.time_start, self.time_end, space_order=self.space_order, **self.kwargs)
def test_adjoint_F(self, mkey, shape, kernel, space_order): """ Adjoint test for the forward modeling operator. The forward modeling operator F generates a shot record (measurements) from a source while the adjoint of F generates measurments at the source location from data. This test uses the conventional dot test: < Fx, y> = <x, F^T y> """ tn = 500. # Final time # Create solver from preset solver = acoustic_setup(shape=shape, spacing=[15. for _ in shape], kernel=kernel, nbl=10, tn=tn, space_order=space_order, **(presets[mkey]), dtype=np.float64) # Create adjoint receiver symbol srca = Receiver(name='srca', grid=solver.model.grid, time_range=solver.geometry.time_axis, coordinates=solver.geometry.src_positions) # Run forward and adjoint operators rec, _, _ = solver.forward(save=False) solver.adjoint(rec=rec, srca=srca) # Adjoint test: Verify <Ax,y> matches <x, A^Ty> closely term1 = np.dot(srca.data.reshape(-1), solver.geometry.src.data) term2 = norm(rec) ** 2 info('<Ax,y>: %f, <x, A^Ty>: %f, difference: %4.4e, ratio: %f' % (term1, term2, (term1 - term2)/term1, term1 / term2)) assert np.isclose((term1 - term2)/term1, 0., atol=1.e-12)
def tti_setup(shape=(50, 50, 50), spacing=(20.0, 20.0, 20.0), tn=250.0, time_order=2, space_order=4, nbpml=10, **kwargs): nrec = 101 # Two layer model for true velocity model = demo_model('layers-tti', shape=shape, spacing=spacing, nbpml=nbpml) # Derive timestepping from model spacing dt = model.critical_dt t0 = 0.0 nt = int(1 + (tn - t0) / dt) time = np.linspace(t0, tn, nt) # Define source geometry (center of domain, just below surface) src = RickerSource(name='src', grid=model.grid, f0=0.015, time=time) src.coordinates.data[0, :] = np.array(model.domain_size) * .5 src.coordinates.data[0, -1] = model.origin[-1] + 2 * spacing[-1] # Define receiver geometry (spread across x, lust below surface) rec = Receiver(name='nrec', grid=model.grid, ntime=nt, npoint=nrec) rec.coordinates.data[:, 0] = np.linspace(0., model.domain_size[0], num=nrec) rec.coordinates.data[:, 1:] = src.coordinates.data[0, 1:] return AnisotropicWaveSolver(model, source=src, receiver=rec, time_order=time_order, space_order=space_order, **kwargs)
def ForwardOperator(model, geometry, space_order=4, save=False, kernel='centered', **kwargs): """ Constructor method for the forward modelling operator in an acoustic media :param model: :class:`Model` object containing the physical parameters :param src: None ot IShot() (not currently supported properly) :param data: IShot() object containing the acquisition geometry and field data :param: time_order: Time discretization order :param: spc_order: Space discretization order """ dt = model.grid.time_dim.spacing m = model.m time_order = 1 if kernel == 'staggered' else 2 if kernel == 'staggered': dims = model.space_dimensions stagg_u = (-dims[-1]) stagg_v = (-dims[0], -dims[1]) if model.grid.dim == 3 else (-dims[0]) else: stagg_u = stagg_v = None # Create symbols for forward wavefield, source and receivers u = TimeFunction(name='u', grid=model.grid, staggered=stagg_u, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, staggered=stagg_v, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) src = PointSource(name='src', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # FD kernels of the PDE FD_kernel = kernels[(kernel, len(model.shape))] stencils = FD_kernel(model, u, v, space_order) # Source and receivers stencils += src.inject(field=u.forward, expr=src * dt**2 / m) stencils += src.inject(field=v.forward, expr=src * dt**2 / m) stencils += rec.interpolate(expr=u + v) # Substitute spacing terms to reduce flops return Operator(stencils, subs=model.spacing_map, name='ForwardTTI', **kwargs)
def BornOperator(model, geometry, space_order=4, kernel='OT2', **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 = Receiver(name='src', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # Create wavefields and a dm field u = TimeFunction(name="u", grid=model.grid, save=None, time_order=2, space_order=space_order) U = TimeFunction(name="U", grid=model.grid, save=None, time_order=2, space_order=space_order) dm = Function(name="dm", grid=model.grid, space_order=0) s = model.grid.stepping_dim.spacing eqn1 = iso_stencil(u, m, s, damp, kernel) eqn2 = iso_stencil(U, m, s, damp, kernel, q=-dm*u.dt2) # Add source term expression for u source = src.inject(field=u.forward, expr=src * s**2 / m) # Create receiver interpolation expression from U receivers = rec.interpolate(expr=U) # Substitute spacing terms to reduce flops return Operator(eqn1 + source + eqn2 + receivers, subs=model.spacing_map, name='Born', **kwargs)