def BornOperator(model, geometry, space_order=4, kernel='sls', time_order=2, **kwargs): """ Construct an Linearized Born 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. """ # Create wavefields and a dm field p = TimeFunction(name='p', grid=model.grid, time_order=time_order, space_order=space_order, staggered=NODE) P = TimeFunction(name='P', grid=model.grid, time_order=time_order, space_order=space_order, staggered=NODE) rp = TimeFunction(name="rp", grid=model.grid, time_order=time_order, space_order=space_order, staggered=NODE) rP = TimeFunction(name="rP", grid=model.grid, time_order=time_order, space_order=space_order, staggered=NODE) dm = Function(name='dm', grid=model.grid, space_order=0) if time_order == 1: v = VectorTimeFunction(name="v", grid=model.grid, time_order=time_order, space_order=space_order) kwargs.update({'v': v}) # Equations kernels eq_kernel = kernels[kernel] eqn1 = eq_kernel(model, geometry, p, r=rp, **kwargs) s = model.grid.stepping_dim.spacing if time_order == 1: dv = VectorTimeFunction(name="dv", grid=model.grid, time_order=time_order, space_order=space_order) kwargs.update({'v': dv}) q = -dm * (p.forward - p) / s else: q = -dm * (p.forward - 2 * p + p.backward) / (s**2) eqn2 = eq_kernel(model, geometry, P, r=rP, q=q, **kwargs) # Add source term expression for p src_term, _ = src_rec(p, model, geometry) # Create receiver interpolation expression from P _, rec_term = src_rec(P, model, geometry) # Substitute spacing terms to reduce flops return Operator(eqn1 + src_term + rec_term + eqn2, subs=model.spacing_map, name='Born', **kwargs)
def ForwardOperator(model, geometry, space_order=4, kernel='sls', time_order=2, save=False, **kwargs): """ Construct method for the forward modelling operator in a viscoacoustic 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. kernel : string, optional selects a viscoacoustic equation from the options below: sls (Standard Linear Solid) : 1st order - Blanch and Symes (1995) / Dutta and Schuster (2014) viscoacoustic equation 2nd order - Bai et al. (2014) viscoacoustic equation ren - Ren et al. (2014) viscoacoustic equation deng_mcmechan - Deng and McMechan (2007) viscoacoustic equation Defaults to sls 2nd order. save : int or Buffer Saving flag, True saves all time steps, False saves three buffered indices (last three time steps). Defaults to False. """ # Create symbols for forward wavefield, particle velocity, source and receivers if time_order == 1: v = VectorTimeFunction(name="v", grid=model.grid, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) kwargs.update({'v': v}) p = TimeFunction(name="p", grid=model.grid, staggered=NODE, save=geometry.nt if save else None, time_order=time_order, space_order=space_order) # Equations kernels eq_kernel = kernels[kernel] eqn = eq_kernel(model, geometry, p, save=save, **kwargs) srcrec = src_rec(p, model, geometry) # Substitute spacing terms to reduce flops return Operator(eqn + srcrec, subs=model.spacing_map, name='Forward', **kwargs)
def test_staggered_div(): """ Test that div works properly on expressions. From @speglish issue #1248 """ grid = Grid(shape=(5, 5)) v = VectorTimeFunction(name="v", grid=grid, time_order=1, space_order=4) p1 = TimeFunction(name="p1", grid=grid, time_order=1, space_order=4, staggered=NODE) p2 = TimeFunction(name="p2", grid=grid, time_order=1, space_order=4, staggered=NODE) # Test that 1.*v and 1*v are doing the same v[0].data[:] = 1. v[1].data[:] = 1. eq1 = Eq(p1, div(1*v)) eq2 = Eq(p2, div(1.*v)) op1 = Operator([eq1]) op2 = Operator([eq2]) op1.apply(time_M=0) op2.apply(time_M=0) assert np.allclose(p1.data[:], p2.data[:], atol=0, rtol=1e-5) # Test div on expression v[0].data[:] = 5. v[1].data[:] = 5. A = Function(name="A", grid=grid, space_order=4, staggred=NODE, parameter=True) A._data_with_outhalo[:] = .5 av = VectorTimeFunction(name="av", grid=grid, time_order=1, space_order=4) # Operator with A (precomputed A*v) eq1 = Eq(av, A*v) eq2 = Eq(p1, div(av)) op = Operator([eq1, eq2]) op.apply(time_M=0) # Operator with div(A*v) directly eq3 = Eq(p2, div(A*v)) op2 = Operator([eq3]) op2.apply(time_M=0) assert np.allclose(p1.data[:], p2.data[:], atol=0, rtol=1e-5)
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 GradientOperator(model, geometry, space_order=4, kernel='sls', time_order=2, save=True, **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 : selects a visco-acoustic equation from the options below: sls (Standard Linear Solid) : 1st order - Blanch and Symes (1995) / Dutta and Schuster (2014) viscoacoustic equation 2nd order - Bai et al. (2014) viscoacoustic equation ren - Ren et al. (2014) viscoacoustic equation deng_mcmechan - Deng and McMechan (2007) viscoacoustic equation Defaults to sls 2nd order. """ # Gradient symbol and wavefield symbols save_t = geometry.nt if save else None grad = Function(name='grad', grid=model.grid) p = TimeFunction(name='p', grid=model.grid, time_order=time_order, space_order=space_order, save=save_t, staggered=NODE) pa = TimeFunction(name='pa', grid=model.grid, time_order=time_order, space_order=space_order, staggered=NODE) if time_order == 1: va = VectorTimeFunction(name="va", grid=model.grid, time_order=time_order, space_order=space_order) kwargs.update({'v': va}) # Equations kernels eq_kernel = kernels[kernel] eqn = eq_kernel(model, geometry, pa, forward=False, save=False, **kwargs) if time_order == 1: gradient_update = Eq(grad, grad - p.dt * pa) else: gradient_update = Eq(grad, grad + p.dt * pa.dt) # Add expression for receiver injection _, recterm = src_rec(pa, model, geometry, forward=False) # Substitute spacing terms to reduce flops return Operator(eqn + recterm + [gradient_update], subs=model.spacing_map, name='Gradient', **kwargs)
def AdjointOperator(model, geometry, space_order=4, kernel='sls', time_order=2, **kwargs): """ Construct an adjoint modelling operator in a viscoacoustic 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. kernel : selects a visco-acoustic equation from the options below: sls (Standard Linear Solid) : 1st order - Blanch and Symes (1995) / Dutta and Schuster (2014) viscoacoustic equation 2nd order - Bai et al. (2014) viscoacoustic equation ren - Ren et al. (2014) viscoacoustic equation deng_mcmechan - Deng and McMechan (2007) viscoacoustic equation Defaults to sls 2nd order. """ if time_order == 1: va = VectorTimeFunction(name="va", grid=model.grid, save=None, time_order=time_order, space_order=space_order) kwargs.update({'v': va}) pa = TimeFunction(name="pa", grid=model.grid, save=None, time_order=time_order, space_order=space_order, staggered=NODE) # Equations kernels eq_kernel = kernels[kernel] eqn = eq_kernel(model, geometry, pa, forward=False, **kwargs) srcrec = src_rec(pa, model, geometry, forward=False) # Substitute spacing terms to reduce flops return Operator(eqn + srcrec, subs=model.spacing_map, name='Adjoint', **kwargs)
def ForwardOperator(model, geometry, space_order=4, save=False, **kwargs): """ Construct method for the forward modelling operator in an elastic 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 Saving flag, True saves all time steps, False saves three buffered indices (last three time steps). Defaults to False. """ v = VectorTimeFunction(name='v', grid=model.grid, save=geometry.nt if save else None, space_order=space_order, time_order=1) tau = TensorTimeFunction(name='tau', grid=model.grid, save=geometry.nt if save else None, space_order=space_order, time_order=1) lam, mu, b = model.lam, model.mu, model.b dt = model.critical_dt u_v = Eq(v.forward, model.damp * v + model.damp * dt * b * div(tau)) u_t = Eq( tau.forward, model.damp * tau + model.damp * dt * lam * diag(div(v.forward)) + model.damp * dt * mu * (grad(v.forward) + grad(v.forward).T)) srcrec = src_rec(v, tau, model, geometry) op = Operator([u_v] + [u_t] + srcrec, subs=model.spacing_map, name="ForwardElastic", **kwargs) # Substitute spacing terms to reduce flops return op
def forward(self, src=None, rec=None, v=None,tau=None, vp=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. rec : SparseTimeFunction or array_like, optional The interpolated receiver data. v : VectorTimeFunction, optional The computed particle velocity. tau : TensorTimeFunction, optional The computed symmetric stress tensor. vp : Function or float, optional The time-constant velocity. save : int or Buffer, optional Option to store the entire (unrolled) wavefield. Returns ------- Receiver, wavefield 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 rec = rec or self.geometry.rec # Create Create Forward wavefield v = v or VectorTimeFunction(name='v', grid=self.model.grid, save=self.geometry.nt, space_order=self.space_order, time_order=1) tau = tau or TensorTimeFunction(name='tau', grid=self.model.grid, save=self.geometry.nt, space_order=self.space_order, time_order=1) # Pick vp from model unless explicitly provided vp = vp or self.model.vp # Execute operator and return wavefield and receiver data summary = self.op_fwd(save).apply(src=src, rec=rec, v=v, tau=tau, vp=vp, dt=kwargs.pop('dt', self.dt), **kwargs) return rec, v, tau, summary
def forward(self, src=None, rec=None, v=None, r=None, p=None, model=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. rec : SparseTimeFunction or array_like, optional The interpolated receiver data. v : VectorTimeFunction, optional The computed particle velocity. r : TimeFunction, optional The computed attenuation memory variable. p : TimeFunction, optional Stores the computed wavefield. model : Model, optional Object containing the physical parameters. qp : Function, optional The P-wave quality factor. b : Function, optional The time-constant inverse density. vp : Function or float, optional The time-constant velocity. save : bool, optional Whether or not to save the entire (unrolled) wavefield. Returns ------- Receiver, wavefield 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 rec = rec or self.geometry.rec # Create all the fields v, p, r save_t = src.nt if save else None if self.time_order == 1: v = v or VectorTimeFunction(name="v", grid=self.model.grid, save=save_t, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in v}) # Create the forward wavefield if not provided p = p or TimeFunction(name="p", grid=self.model.grid, save=save_t, time_order=self.time_order, space_order=self.space_order, staggered=NODE) # Memory variable: r = r or TimeFunction(name="r", grid=self.model.grid, save=save_t, time_order=self.time_order, space_order=self.space_order, staggered=NODE) model = model or self.model # Pick vp and physical parameters from model unless explicitly provided kwargs.update(model.physical_params(**kwargs)) if self.kernel == 'sls': # Execute operator and return wavefield and receiver data # With Memory variable summary = self.op_fwd(save).apply(src=src, rec=rec, r=r, p=p, dt=kwargs.pop('dt', self.dt), **kwargs) else: # Execute operator and return wavefield and receiver data # Without Memory variable summary = self.op_fwd(save).apply(src=src, rec=rec, p=p, dt=kwargs.pop('dt', self.dt), **kwargs) return rec, p, v, summary
def adjoint(self, rec, srca=None, va=None, pa=None, r=None, model=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. va : VectorTimeFunction, optional The computed particle velocity. pa : TimeFunction, optional Stores the computed wavefield. r : TimeFunction, optional The computed attenuation memory variable. model : Model, optional Object containing the physical parameters. vp : Function or float, optional The time-constant velocity. qp : Function, optional The P-wave quality factor. b : Function, optional The time-constant inverse density. 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) if self.time_order == 1: va = va or VectorTimeFunction(name="va", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in va}) kwargs['time_m'] = 0 pa = pa or TimeFunction(name="pa", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) # Memory variable: r = r or TimeFunction(name="r", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) model = model or self.model # Pick vp and physical parameters from model unless explicitly provided kwargs.update(model.physical_params(**kwargs)) # Execute operator and return wavefield and receiver data if self.kernel == 'sls': # Execute operator and return wavefield and receiver data # With Memory variable summary = self.op_adj().apply(src=srca, rec=rec, pa=pa, r=r, dt=kwargs.pop('dt', self.dt), **kwargs) else: summary = self.op_adj().apply(src=srca, rec=rec, pa=pa, dt=kwargs.pop('dt', self.dt), **kwargs) return srca, pa, va, summary
def jacobian_adjoint(self, rec, p, pa=None, grad=None, r=None, va=None, model=None, checkpointing=False, **kwargs): """ Gradient modelling function for computing the adjoint of the Linearized Born modelling function, ie. the action of the Jacobian adjoint on an input data. Parameters ---------- rec : SparseTimeFunction Receiver data. p : TimeFunction Full wavefield `p` (created with save=True). pa : TimeFunction, optional Stores the computed wavefield. grad : Function, optional Stores the gradient field. r : TimeFunction, optional The computed attenuation memory variable. va : VectorTimeFunction, optional The computed particle velocity. model : Model, optional Object containing the physical parameters. vp : Function or float, optional The time-constant velocity. qp : Function, optional The P-wave quality factor. b : Function, optional The time-constant inverse density. Returns ------- Gradient field and performance summary. """ dt = kwargs.pop('dt', self.dt) # Gradient symbol grad = grad or Function(name='grad', grid=self.model.grid) # Create the forward wavefield pa = pa or TimeFunction(name='pa', grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) model = model or self.model # Pick vp and physical parameters from model unless explicitly provided kwargs.update(model.physical_params(**kwargs)) if checkpointing: if self.time_order == 1: v = VectorTimeFunction(name="v", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in v}) p = TimeFunction(name='p', grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) r = TimeFunction(name="r", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) l = [p, r] + v.values() if self.time_order == 1 else [p, r] cp = DevitoCheckpoint(l) n_checkpoints = None wrap_fw = CheckpointOperator(self.op_fwd(save=False), src=self.geometry.src, p=p, r=r, dt=dt, **kwargs) ra = TimeFunction(name="ra", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) if self.time_order == 1: for i in {k.name: k for k in v}.keys(): kwargs.pop(i) va = VectorTimeFunction(name="va", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in va}) kwargs['time_m'] = 0 wrap_rev = CheckpointOperator(self.op_grad(save=False), p=p, pa=pa, r=ra, rec=rec, dt=dt, grad=grad, **kwargs) # Run forward wrp = Revolver( cp, wrap_fw, wrap_rev, n_checkpoints, rec.data.shape[0] - (1 if self.time_order == 1 else 2)) wrp.apply_forward() summary = wrp.apply_reverse() else: if self.time_order == 1: va = va or VectorTimeFunction(name="va", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in va}) kwargs['time_m'] = 0 # Memory variable: r = r or TimeFunction(name="r", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) summary = self.op_grad().apply(rec=rec, grad=grad, pa=pa, p=p, r=r, dt=dt, **kwargs) return grad, summary
def jacobian(self, dmin, src=None, rec=None, p=None, P=None, rp=None, rP=None, v=None, dv=None, model=None, **kwargs): """ Linearized Born modelling function that creates the necessary data objects for running an adjoint modelling operator. Parameters ---------- src : SparseTimeFunction or array_like, optional Time series data for the injected source term. rec : SparseTimeFunction or array_like, optional The interpolated receiver data. p : TimeFunction, optional The forward wavefield. P : TimeFunction, optional The linearized wavefield. rp : TimeFunction, optional The computed attenuation memory variable. rP : TimeFunction, optional The computed attenuation memory variable. v : VectorTimeFunction, optional The computed particle velocity. dv : VectorTimeFunction, optional The computed particle velocity. model : Model, optional Object containing the physical parameters. vp : Function or float, optional The time-constant velocity. qp : Function, optional The P-wave quality factor. b : Function, optional The time-constant inverse density. """ # 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 rec = rec or self.geometry.rec # Create the forward wavefields u and U if not provided p = p or TimeFunction(name='p', grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) P = P or TimeFunction(name='P', grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) # Memory variable: rp = rp or TimeFunction(name='rp', grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) # Memory variable: rP = rP or TimeFunction(name='rP', grid=self.model.grid, time_order=self.time_order, space_order=self.space_order, staggered=NODE) if self.time_order == 1: v = v or VectorTimeFunction(name="v", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in v}) dv = dv or VectorTimeFunction(name="dv", grid=self.model.grid, time_order=self.time_order, space_order=self.space_order) kwargs.update({k.name: k for k in dv}) model = model or self.model # Pick vp and physical parameters from model unless explicitly provided kwargs.update(model.physical_params(**kwargs)) # Execute operator and return wavefield and receiver data summary = self.op_born().apply(dm=dmin, p=p, P=P, src=src, rec=rec, rp=rp, rP=rP, dt=kwargs.pop('dt', self.dt), **kwargs) return rec, p, P, summary
def forward(self, src=None, rec1=None, rec2=None, v=None, tau=None, r=None, model=None, save=None, **kwargs): """ Forward modelling function that creates the necessary data objects for running a forward modelling operator. Parameters ---------- geometry : AcquisitionGeometry Geometry object that contains the source (src : SparseTimeFunction) and receivers (rec1(txx) : SparseTimeFunction, rec2(tzz) : SparseTimeFunction) and their position. v : VectorTimeFunction, optional The computed particle velocity. tau : TensorTimeFunction, optional The computed stress. r : TensorTimeFunction, optional The computed memory variable. model : Model, optional Object containing the physical parameters. lam : Function, optional The time-constant first Lame parameter rho * (vp**2 - 2 * vs **2). mu : Function, optional The Shear modulus (rho * vs*2). qp : Function, optional The P-wave quality factor (dimensionless). qs : Function, optional The S-wave quality factor (dimensionless). b : Function, optional The time-constant inverse density (1/rho=1 for water). save : bool, optional Whether or not to save the entire (unrolled) wavefield. Returns ------- Rec1 (txx), Rec2 (tzz), particle velocities vx and vz, stress txx, tzz and txz, memory variables rxx, rzz, rxz 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 self.geometry.new_rec(name='rec1') rec2 = rec2 or self.geometry.new_rec(name='rec2') # Create all the fields v, tau, r save_t = src.nt if save else None v = v or VectorTimeFunction(name="v", grid=self.model.grid, save=save_t, time_order=1, space_order=self.space_order) # Stress: tau = tau or TensorTimeFunction(name='t', grid=self.model.grid, save=save_t, space_order=self.space_order, time_order=1) # Memory variable: r = r or TensorTimeFunction(name='r', 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}) kwargs.update({k.name: k for k in r}) model = model or self.model # Pick physical parameters from model unless explicitly provided kwargs.update(model.physical_params(**kwargs)) # 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 forward(self, src=None, rec=None, v=None, r=None, p=None, qp=None, b=None, vp=None, save=None, **kwargs): """ Forward modelling function that creates the necessary data objects for running a forward modelling operator. Parameters ---------- geometry : AcquisitionGeometry Geometry object that contains the source (SparseTimeFunction) and receivers (SparseTimeFunction) and their position. v : VectorTimeFunction, optional The computed particle velocity. r : TimeFunction, optional The computed memory variable. p : TimeFunction, optional Stores the computed wavefield. qp : Function, optional The P-wave quality factor. b : Function, optional The time-constant inverse density. vp : Function or float, optional The time-constant velocity. save : int or Buffer, optional Option to store the entire (unrolled) wavefield. Returns ------- Receiver, wavefield 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 rec = rec or Receiver(name="rec", grid=self.model.grid, time_range=self.geometry.time_axis, coordinates=self.geometry.rec_positions) # Create all the fields v, p, r save_t = src.nt if save else None v = v or VectorTimeFunction(name="v", grid=self.model.grid, save=save_t, time_order=1, space_order=self.space_order) # Create the forward wavefield if not provided p = p or TimeFunction(name="p", grid=self.model.grid, save=save_t, time_order=1, space_order=self.space_order, staggered=NODE) # Memory variable: r = r or TimeFunction(name="r", grid=self.model.grid, save=save_t, time_order=1, space_order=self.space_order, staggered=NODE) kwargs.update({k.name: k for k in v}) # Pick physical parameters from model unless explicitly provided b = b or self.model.b qp = qp or self.model.qp # Pick vp from model unless explicitly provided vp = vp or self.model.vp if self.kernel == 'blanch_symes': # Execute operator and return wavefield and receiver data # With Memory variable summary = self.op_fwd(save).apply(src=src, rec=rec, qp=qp, r=r, p=p, b=b, vp=vp, dt=kwargs.pop('dt', self.dt), **kwargs) else: # Execute operator and return wavefield and receiver data # Without Memory variable summary = self.op_fwd(save).apply(src=src, rec=rec, qp=qp, p=p, b=b, vp=vp, dt=kwargs.pop('dt', self.dt), **kwargs) return rec, p, summary
def ForwardOperator(model, geometry, space_order=4, save=False, **kwargs): """ Construct method for the forward modelling operator in an elastic 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 Saving flag, True saves all time steps, False saves three buffered indices (last three time steps). Defaults to False. """ l, qp, mu, qs, ro, damp = \ model.lam, model.qp, model.mu, model.qs, model.irho, model.damp s = model.grid.stepping_dim.spacing f0 = geometry._f0 t_s = (sp.sqrt(1. + 1. / qp**2) - 1. / qp) / f0 t_ep = 1. / (f0**2 * t_s) t_es = (1. + f0 * qs * t_s) / (f0 * qs - f0**2 * t_s) # Create symbols for forward wavefield, source and receivers # Velocity: v = VectorTimeFunction(name="v", grid=model.grid, save=geometry.nt if save else None, time_order=1, space_order=space_order) # Stress: tau = TensorTimeFunction(name='t', grid=model.grid, save=geometry.nt if save else None, space_order=space_order, time_order=1) # Memory variable: r = TensorTimeFunction(name='r', grid=model.grid, save=geometry.nt if save else None, space_order=space_order, time_order=1) # Particle velocity u_v = Eq(v.forward, damp * (v + s * ro * div(tau))) symm_grad = grad(v.forward) + grad(v.forward).T # Stress equations: u_t = Eq( tau.forward, damp * (r.forward + tau + s * (l * t_ep / t_s * diag(div(v.forward)) + mu * t_es / t_s * symm_grad)) ) # Memory variable equations: u_r = Eq( r.forward, damp * (r - s / t_s * (r + mu * (t_es / t_s - 1) * symm_grad + l * (t_ep / t_s - 1) * diag(div(v.forward))))) src_rec_expr = src_rec(v, tau, model, geometry) # Substitute spacing terms to reduce flops return Operator([u_v, u_r, u_t] + src_rec_expr, subs=model.spacing_map, name='Forward', **kwargs)
def ForwardOperator(model, geometry, space_order=4, time_order = 1, save=False, **kwargs): """ Construct method for the forward modelling operator in an elastic 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 Saving flag, True saves all time steps, False saves three buffered indices (last three time steps). Defaults to False. """ v = VectorTimeFunction(name='v', grid=model.grid, space_order=space_order, time_order=time_order) tau = TensorTimeFunction(name='tau', grid=model.grid, space_order=space_order, time_order=time_order) ## Symbolic physical parameters used from the model irho = model.irho c11 = model.c11 c33 = model.c33 c44 = model.c44 c66 = model.c66 c13 = model.c13 # Model critical timestep computed from CFL condition for VTI media ts = model.grid.stepping_dim.spacing damp = model.damp.data # Particle Velocity for each direction u_vx = Eq(v[0].forward, damp*v[0] + damp*ts*irho*(tau[0,0].dx + tau[1,0].dy + tau[2,0].dz) ) u_vy = Eq(v[1].forward, damp*v[1] + damp*ts*irho*(tau[0,1].dx + tau[1,1].dy + tau[1,2].dz) ) u_vz = Eq(v[2].forward, damp*v[2] + damp*ts*irho*(tau[0,2].dx + tau[2,1].dy + tau[2,2].dz) ) # Stress for each direction in VTI Media: u_txx = Eq(tau[0,0].forward, damp*tau[0,0] + damp*ts*(c11*v[0].forward.dx + c11*v[1].forward.dy - 2*c66*v[1].forward.dy + c13*v[2].forward.dz) ) u_tyy = Eq(tau[1,1].forward, damp*tau[1,1] + damp*ts*(c11*v[0].forward.dx - 2*c66*v[0].forward.dx + c11*v[1].forward.dy + c13*v[2].forward.dz) ) u_tzz = Eq(tau[2,2].forward, damp*tau[2,2] + damp*ts*(c13*v[0].forward.dx + c13*v[1].forward.dy + c33*v[2].forward.dz) ) u_txz = Eq(tau[0,2].forward, damp*tau[0,2] + damp*ts*(c44*v[2].forward.dx + c44*v[0].forward.dz) ) u_tyz = Eq(tau[1,2].forward, damp*tau[1,2] + damp*ts*(c44*v[2].forward.dy + c44*v[1].forward.dz) ) u_txy = Eq(tau[0,1].forward, damp*tau[0,1] + damp*ts*(c66*v[1].forward.dx + c66*v[0].forward.dy) ) stencil = [u_vx, u_vy, u_vz, u_txx, u_tyy, u_tzz, u_txz, u_tyz, u_txy] #srcrec = src_rec(v, tau, model, geometry) src = PointSource(name='src', grid=model.grid, time_range=geometry.time_axis, npoint=geometry.nsrc) rec = Receiver(name='rec', grid=geometry.grid, time_range=geometry.time_axis, npoint=geometry.nrec) # The source injection term src_xx = src.inject(field=tau[0, 0].forward, expr=src * ts) src_zz = src.inject(field=tau[-1, -1].forward, expr=src * ts) src_term = src_xx + src_zz if model.grid.dim == 3: src_yy = src.inject(field=tau[1, 1].forward, expr=src * ts) src_term += src_yy # Create interpolation expression for receivers rec_term = rec.interpolate(expr=v) srcrec = src_term + rec_term pde = stencil + srcrec # Substitute spacing terms to reduce flops op = Operator(pde, subs=model.spacing_map, name="ForwardElasticVTI", **kwargs) return op
def forward(self, src=None, rec1=None, rec2=None, lam=None, qp=None, mu=None, qs=None, irho=None, v=None, tau=None, r=None, save=None, **kwargs): """ Forward modelling function that creates the necessary data objects for running a forward modelling operator. Parameters ---------- geometry : AcquisitionGeometry Geometry object that contains the source (src : SparseTimeFunction) and receivers (rec1(txx) : SparseTimeFunction, rec2(tzz) : SparseTimeFunction) and their position. v : VectorTimeFunction, optional The computed particle velocity. tau : TensorTimeFunction, optional The computed stress. r : TensorTimeFunction, optional The computed memory variable. lambda : Function, optional The time-constant first Lame parameter (rho * vp**2 - rho * vs **2). qp : Function, optional The P-wave quality factor (dimensionless). mu : Function, optional The Shear modulus (rho * vs*2). qs : Function, optional The S-wave quality factor (dimensionless). irho : Function, optional The time-constant inverse density (1/rho=1 for water). save : int or Buffer, optional Option to store the entire (unrolled) wavefield. Returns ------- Rec1 (txx), Rec2 (tzz), particle velocities vx and vz, stress txx, tzz and txz, memory variables rxx, rzz, rxz 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 v, tau, r save_t = src.nt if save else None v = VectorTimeFunction(name="v", grid=self.model.grid, save=save_t, time_order=1, space_order=self.space_order) # Stress: tau = TensorTimeFunction(name='t', grid=self.model.grid, save=save_t, space_order=self.space_order, time_order=1) # Memory variable: r = TensorTimeFunction(name='r', 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}) kwargs.update({k.name: k for k in r}) # Pick physical parameters from model unless explicitly provided lam = lam or self.model.lam qp = qp or self.model.qp mu = mu or self.model.mu qs = qs or self.model.qs irho = irho or self.model.irho # Execute operator and return wavefield and receiver data summary = self.op_fwd(save).apply(src=src, rec1=rec1, mu=mu, qp=qp, lam=lam, qs=qs, irho=irho, rec2=rec2, dt=kwargs.pop('dt', self.dt), **kwargs) return rec1, rec2, v, tau, summary
def ForwardOperator(model, geometry, space_order=4, kernel='blanch_symes', save=False, **kwargs): """ Construct method for the forward modelling operator in a viscoacoustic 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 : selects a visco-acoustic equation from the options below: blanch_symes - Blanch and Symes (1995) / Dutta and Schuster (2014) viscoacoustic equation ren - Ren et al. (2014) viscoacoustic equation deng_mcmechan - Deng and McMechan (2007) viscoacoustic equation Defaults to blanch_symes. save : int or Buffer Saving flag, True saves all time steps, False saves three buffered indices (last three time steps). Defaults to False. """ s = model.grid.stepping_dim.spacing # Create symbols for forward wavefield, particle velocity, source and receivers # Velocity: v = VectorTimeFunction(name="v", grid=model.grid, save=geometry.nt if save else None, time_order=1, space_order=space_order) p = TimeFunction(name="p", grid=model.grid, staggered=NODE, save=geometry.nt if save else None, time_order=1, 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) # Equations kernels eq_kernel = kernels[kernel] eqn = eq_kernel(model, geometry, v, p, save=save) # The source injection term src_term = src.inject(field=p.forward, expr=src * s) # Create interpolation expression for receivers rec_term = rec.interpolate(expr=p) # Substitute spacing terms to reduce flops return Operator(eqn + src_term + rec_term, subs=model.spacing_map, name='Forward', **kwargs)