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 ImagingOperator(geometry, image, space_order, save=True): stagg_u = stagg_v = None u = TimeFunction(name='u', grid=geometry.model.grid, staggered=stagg_u, save=geometry.nt if save else None, time_order=2, space_order=space_order) v = TimeFunction(name='v', grid=geometry.model.grid, staggered=stagg_v, save=geometry.nt if save else None, time_order=2, space_order=space_order) uu = TimeFunction(name='uu', grid=geometry.model.grid, staggered=stagg_u, save=None, time_order=2, space_order=space_order) vv = TimeFunction(name='vv', grid=geometry.model.grid, staggered=stagg_v, save=None, time_order=2, space_order=space_order) dt = geometry.dt residual = PointSource(name='residual', grid=geometry.model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) stencils = kernel_centered_2d(geometry.model, uu, vv, space_order, forward=False) stencils += residual.inject(field=uu.backward, expr=residual * dt**2 / geometry.model.m) stencils += residual.inject(field=vv.backward, expr=residual * dt**2 / geometry.model.m) # Correlate u and v for the current time step and add it to the image image_update = Eq(image, image - (u.dt2*uu + v.dt2*vv)) return Operator(stencils + [image_update], subs=geometry.model.spacing_map)
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 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 src_rec(v, tau, 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=tau[0, 0].forward, expr=src * s) src_zz = src.inject(field=tau[-1, -1].forward, expr=src * s) src_expr = src_xx + src_zz if model.grid.dim == 3: src_yy = src.inject(field=tau[1, 1].forward, expr=src * s) src_expr += src_yy # Create interpolation expression for receivers rec_term1 = rec1.interpolate(expr=tau[-1, -1]) rec_term2 = rec2.interpolate(expr=div(v)) return src_expr + rec_term1 + rec_term2
def ImagingOperator(model, image): # Define the wavefield with the size of the model and the time dimension v = TimeFunction(name='v', grid=model.grid, time_order=2, space_order=4) u = TimeFunction(name='u', grid=model.grid, time_order=2, space_order=4, save=geometry.nt) # Define the wave equation, but with a negated damping term eqn = model.m * v.dt2 - v.laplace - model.damp * v.dt # Use `solve` to rearrange the equation into a stencil expression stencil = Eq(v.backward, solve(eqn, v.backward)) # Define residual injection at the location of the forward receivers dt = model.critical_dt residual = PointSource(name='residual', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) res_term = residual.inject(field=v, expr=residual * dt**2 / model.m) # Correlate u and v for the current time step and add it to the image image_update = Eq(image, image - u * v) return Operator([stencil] + res_term + [image_update], subs=model.spacing_map)
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 td_born_adjoint_op(model, geometry, time_order, space_order): nt = geometry.nt # Define the wavefields with the size of the model and the time dimension u = TimeFunction( name='u', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) # Define the wave equation pde = model.m * u.dt2 - u.laplace + model.damp * u.dt.T # Use `solve` to rearrange the equation into a stencil expression stencil = Eq(u.backward, solve(pde, u.backward), subdomain=model.grid.subdomains['physdomain']) # Inject at receivers born_data_rec = PointSource( name='born_data_rec', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions ) dt = Constant(name='dt') rec_term = born_data_rec.inject(field=u.backward, expr=born_data_rec * (dt ** 2) / model.m) return Operator([stencil] + rec_term, subs=model.spacing_map)
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 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 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 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 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 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, 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, 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 adjoint(self, rec, srca=None, v=None, m=None, **kwargs): """ Adjoint modelling function that creates the necessary data objects for running an adjoint modelling operator. :param rec: Symbol with stored receiver data. Please note that these act as the source term in the adjoint run. :param srca: Symbol to store the resulting data for the interpolated at the original source location. :param v: (Optional) Symbol to store the computed wavefield :param m: (Optional) Symbol for the time-constant square slowness :returns: Adjoint source, wavefield and performance summary """ # Create a new adjoint source and receiver symbol srca = srca or PointSource(name='srca', grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.src_positions) # Create the adjoint wavefield if not provided v = v or TimeFunction(name='v', grid=self.model.grid, time_order=2, space_order=self.space_order) # Pick m from model unless explicitly provided m = m or self.model.m # Execute operator and return wavefield and receiver data summary = self.op_adj().apply(srca=srca, rec=rec, v=v, m=m, dt=kwargs.pop('dt', self.dt), **kwargs) return srca, v, summary
def setup(dimensions=(50, 50, 50), spacing=(20.0, 20.0, 20.0), tn=250.0, time_order=2, space_order=4, nbpml=10, dse='advanced'): origin = (0., 0., 0.) # True velocity true_vp = np.ones(dimensions) + 1.0 true_vp[:, :, int(dimensions[0] / 3):int(2 * dimensions[0] / 3)] = 3.0 true_vp[:, :, int(2 * dimensions[0] / 3):int(dimensions[0])] = 4.0 model = Model(origin, spacing, dimensions, true_vp, nbpml=nbpml, epsilon=.4 * np.ones(dimensions), delta=-.1 * np.ones(dimensions), theta=-np.pi / 7 * np.ones(dimensions), phi=np.pi / 5 * np.ones(dimensions)) # Define seismic data. f0 = .010 dt = model.critical_dt t0 = 0.0 nt = int(1 + (tn - t0) / dt) # Set up the source as Ricker wavelet for f0 # Source geometry time_series = np.zeros((nt, 1)) time_series[:, 0] = source(np.linspace(t0, tn, nt), f0) location = np.zeros((1, 3)) location[0, 0] = origin[0] + dimensions[0] * spacing[0] * 0.5 location[0, 1] = origin[1] + dimensions[1] * spacing[1] * 0.5 location[0, 2] = origin[1] + 2 * spacing[2] src = PointSource(name='src', data=time_series, coordinates=location) # Receiver geometry receiver_coords = np.zeros((101, 3)) receiver_coords[:, 0] = np.linspace(0, origin[0] + dimensions[0] * spacing[0], num=101) receiver_coords[:, 1] = origin[1] + dimensions[1] * spacing[1] * 0.5 receiver_coords[:, 2] = location[0, 1] rec = Receiver(name='rec', ntime=nt, coordinates=receiver_coords) return AnisotropicWaveSolver(model, source=src, time_order=time_order, space_order=space_order, receiver=rec, dse=dse)
def adjoint(self, rec, srca=None, p=None, r=None, vp=None, epsilon=None, delta=None, theta=None, phi=None, save=None, **kwargs): """ Adjoint modelling function that creates the necessary data objects for running an adjoint modelling operator. Parameters ---------- geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. p : TimeFunction, optional The computed wavefield first component. r : TimeFunction, optional The computed wavefield second component. vp : Function or float, optional The time-constant velocity. epsilon : Function or float, optional The time-constant first Thomsen parameter. delta : Function or float, optional The time-constant second Thomsen parameter. theta : Function or float, optional The time-constant Dip angle (radians). phi : Function or float, optional The time-constant Azimuth angle (radians). Returns ------- Adjoint source, wavefield and performance summary. """ time_order = 2 stagg_p = stagg_r = None # Source term is read-only, so re-use the default srca = srca or PointSource(name='srca', grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.src_positions) # Create the wavefield if not provided if p is None: p = TimeFunction(name='p', grid=self.model.grid, staggered=stagg_p, time_order=time_order, space_order=self.space_order) # Create the wavefield if not provided if r is None: r = TimeFunction(name='r', grid=self.model.grid, staggered=stagg_r, time_order=time_order, space_order=self.space_order) # Pick vp and Thomsen parameters from model unless explicitly provided kwargs.update(self.model.physical_params( vp=vp, epsilon=epsilon, delta=delta, theta=theta, phi=phi) ) if self.model.dim < 3: kwargs.pop('phi', None) # Execute operator and return wavefield and receiver data summary = self.op_adj().apply(srca=srca, rec=rec, p=p, r=r, dt=kwargs.pop('dt', self.dt), **kwargs) return srca, p, r, summary
def AdjointOperator(model, geometry, space_order=4, kernel='OT2', **kwargs): """ Construct an adjoint 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. space_order : int, optional Space discretization order. kernel : str, optional Type of discretization, centered or shifted. """ 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 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 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 td_born_forward_op(model, geometry, time_order, space_order): nt = geometry.nt # Define the wavefields with the size of the model and the time dimension u0 = TimeFunction( name='u0', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) u = TimeFunction( name='u', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) dm = TimeFunction( name='dm', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) # Define the wave equation pde = model.m * u.dt2 - u.laplace + model.damp * u.dt - dm * u0 # Use `solve` to rearrange the equation into a stencil expression stencil = Eq(u.forward, solve(pde, u.forward), subdomain=model.grid.subdomains['physdomain']) # Sample at receivers born_data_rec = PointSource( name='born_data_rec', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions ) rec_term = born_data_rec.interpolate(expr=u) return Operator([stencil] + rec_term, subs=model.spacing_map)
def src_rec(vx, vy, vz, qx, qy, qz, txx, tyy, tzz, p, model, geometry): """ Source injection and receiver interpolation """ dt = 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) M = model.M # The source injection term #src_xx = src.inject(field=txx.forward, expr=src * (1.0 - model.phi) * dt, offset=model.nbpml) #src_zz = src.inject(field=tzz.forward, expr=src * (1.0 - model.phi) * dt, offset=model.nbpml) #src_xx = src.inject(field=txx.forward, expr=src * dt) #src_zz = src.inject(field=tzz.forward, expr=src * dt) src_pp = src.inject(field=p.forward, expr=src * M) #src_expr = src_xx + src_zz + src_pp src_expr = src_pp if model.grid.dim == 3: src_yy = src.inject(field=tyy.forward, expr=src * (1.0 - model.phi) * dt, offset=model.nbpml) src_expr += src_yy # Create interpolation expression for receivers rec_term1 = rec1.interpolate(expr=p, offset=model.nbpml) 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, offset=model.nbpml) return src_expr + rec_term1 + rec_term2
def adjoint(self, rec, src=None, b=None, v=None, damp=None, vp=None, save=None, **kwargs): """ Adjoint modeling function that creates the necessary data objects for running a adjoint modeling operator. Required parameters: rec. Parameters ---------- rec : SparseTimeFunction The interpolated receiver data to be injected. src : SparseTimeFunction Time series data for the adjoint source term. b : Function or float The time-constant buoyancy. v : Function or float The time-constant velocity. damp : Function or float The time-constant dissipation only attenuation w/Q field. ua : Function or float Stores the computed adjoint wavefield. save : int or Buffer, optional Whether (int nt) or not (None) to save the wavefield time history. Returns ---------- Adjoint source time series data, wavefield TimeFunction ua, and performance summary """ # Create a new adjoint source and receiver symbol srca = src or PointSource(name='srca', grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.src_positions) # Create the adjoint wavefield if not provided v = v or TimeFunction(name='v', grid=self.model.grid, time_order=2, space_order=self.space_order) # Pick input physical parameters kwargs.update(self.model.physical_params(vp=vp, damp=damp, b=b)) kwargs.update({'dt': kwargs.pop('dt', self.dt)}) # Execute operator and return wavefield and receiver data summary = self.op_adj(save).apply(src=srca, rec=rec, v=v, **kwargs) return srca, v, summary
def test_acoustic(dimensions, time_order, space_order): solver = setup(dimensions=dimensions, time_order=time_order, space_order=space_order, nbpml=10+space_order/2) srca = PointSource(name='srca', ntime=solver.source.nt, coordinates=solver.source.coordinates.data) # Run forward and adjoint operators rec, _, _ = solver.forward(save=False) solver.adjoint(rec=rec, srca=srca) # Actual adjoint test term1 = np.dot(srca.data.reshape(-1), solver.source.data) term2 = linalg.norm(rec.data) ** 2 print(term1, term2, ("%12.12f") % (term1 - term2), term1 / term2) assert np.isclose(term1 / term2, 1.0, atol=0.001)
def src_freq_filt(src, cutoff_freq, filt_type='lowpass', filt_order=2): """ Lowpass frequency filter function for the source. Parameters ---------- src : Source Object with the source information. cutoff_freq : float or 2-tuple Cutoff frequency or frequencies. For lowpass and highpass filters, cutoff_freq is a float. For bandpass and bandstop filters, cutoff_freq is a 2-tuple. filt_type : str, optional The type of the filter (‘lowpass’, ‘highpass’, ‘bandpass’ or ‘bandstop’). Default is 'lowpass'. filt_order : int, optional The order of the filter. Default is 2. Returns ------- Filtered source. """ src_filtered = PointSource(name='src', grid=src.grid, npoint=1, time_range=src.time_range) sos = signal.butter(filt_order, cutoff_freq, btype=filt_type, fs=1 / (src.time_range.step * 0.001), output='sos') src_filtered.data[:, 0] = signal.sosfiltfilt(sos, src.data[:, 0]) return src_filtered
def setup(dimensions=(50, 50, 50), spacing=(15.0, 15.0, 15.0), tn=500., time_order=2, space_order=4, nbpml=10, **kwargs): ndim = len(dimensions) origin = tuple([0.]*ndim) spacing = spacing[:ndim] # Velocity model, two layers true_vp = np.ones(dimensions) + .5 true_vp[..., int(dimensions[-1] / 3):dimensions[-1]] = 2.5 # 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:] # Define seismic data model = Model(origin, spacing, dimensions, true_vp, nbpml=int(nbpml)) f0 = .010 dt = model.critical_dt if time_order == 4: dt *= 1.73 t0 = 0.0 tn = tn nt = int(1+(tn-t0)/dt) # Set up the source as Ricker wavelet for f0 def source(t, f0): r = (np.pi * f0 * (t - 1./f0)) return (1-2.*r**2)*np.exp(-r**2) # Source geometry time_series = np.zeros((nt, 1), dtype=np.float32) time_series[:, 0] = source(np.linspace(t0, tn, nt), f0) # Define source and receivers and create acoustic wave solver src = PointSource(name='src', data=time_series, coordinates=location) rec = Receiver(name='rec', ntime=nt, coordinates=receiver_coords) return AcousticWaveSolver(model, source=src, receiver=rec, time_order=time_order, space_order=space_order, **kwargs)
def adjoint(self, rec, srca=None, v=None, vp=None, **kwargs): """ Adjoint modelling function that creates the necessary data objects for running an adjoint modelling operator. Parameters ---------- rec : SparseTimeFunction or array-like The receiver data. Please note that these act as the source term in the adjoint run. srca : SparseTimeFunction or array-like The resulting data for the interpolated at the original source location. v: TimeFunction, optional The computed wavefield. vp : Function or float, optional The time-constant velocity. Returns ------- Adjoint source, wavefield and performance summary. """ # Create a new adjoint source and receiver symbol srca = srca or PointSource(name='srca', grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.src_positions) # Create the adjoint wavefield if not provided v = v or TimeFunction(name='v', grid=self.model.grid, time_order=2, space_order=self.space_order) # Pick vp from model unless explicitly provided vp = vp or self.model.vp # Execute operator and return wavefield and receiver data summary = self.op_adj().apply(srca=srca, rec=rec, v=v, vp=vp, dt=kwargs.pop('dt', self.dt), **kwargs) return srca, v, summary
def element(self, inp=None): """Return an element from ``inp`` or from scratch. This method should be overridden by subclasses. """ if isinstance(self.input, Function): # from IPython import embed; embed() new = Function(name=self.name + '_out', grid=self.input.grid, space_order=self.input.space_order) elif isinstance(self.input, PointSource): new = PointSource(name=self.name + '_new', grid=self.input.grid, coordinates=self.input.coordinates.data[:], time_range=self.input.time_range, npoint=self.input.npoint) if inp is not None: new.data[:] = inp return new
def run_acoustic_forward(dse=None): dimensions = (50, 50, 50) origin = (0., 0., 0.) spacing = (10., 10., 10.) nbpml = 10 # True velocity true_vp = np.ones(dimensions) + 2.0 true_vp[:, :, int(dimensions[0] / 2):int(dimensions[0])] = 4.5 model = Model(origin, spacing, dimensions, true_vp, nbpml=nbpml) # Define seismic data. f0 = .010 dt = model.critical_dt t0 = 0.0 tn = 250.0 nt = int(1 + (tn - t0) / dt) t = np.linspace(t0, tn, nt) r = (np.pi * f0 * (t - 1. / f0)) # Source geometry time_series = np.zeros((nt, 1)) time_series[:, 0] = (1 - 2. * r**2) * np.exp(-r**2) location = np.zeros((1, 3)) location[0, 0] = origin[0] + dimensions[0] * spacing[0] * 0.5 location[0, 1] = origin[1] + dimensions[1] * spacing[1] * 0.5 location[0, 2] = origin[1] + 2 * spacing[2] # Receiver geometry receiver_coords = np.zeros((101, 3)) receiver_coords[:, 0] = np.linspace(0, origin[0] + dimensions[0] * spacing[0], num=101) receiver_coords[:, 1] = origin[1] + dimensions[1] * spacing[1] * 0.5 receiver_coords[:, 2] = location[0, 1] src = PointSource(name='src', data=time_series, coordinates=location) rec = Receiver(name='rec', ntime=nt, coordinates=receiver_coords) acoustic = AcousticWaveSolver(model, source=src, receiver=rec) rec, u, _ = acoustic.forward(save=False, dse=dse, dle='basic') return u, rec
def adjoint(self, rec, srca=None, v=None, m=None, **kwargs): """ Adjoint modelling function that creates the necessary data objects for running an adjoint modelling operator. :param rec: Symbol with stored receiver data. Please note that these act as the source term in the adjoint run. :param srca: Symbol to store the resulting data for the interpolated at the original source location. :param v: (Optional) Symbol to store the computed wavefield :param m: (Optional) Symbol for the time-constant square slowness :returns: Adjoint source, wavefield and performance summary """ # Create a new adjoint source and receiver symbol if srca is None: srca = PointSource(name='srca', ntime=self.source.nt, coordinates=self.source.coordinates.data) # Create the adjoint wavefield if not provided 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 = self.model.m # Execute operator and return wavefield and receiver data summary = self.op_adj.apply(srca=srca, rec=rec, v=v, m=m, **kwargs) return srca, v, summary