def ren(model, geometry, v, p, **kwargs): """ Stencil created from Ren et al. (2014) viscoacoustic wave equation. https://academic.oup.com/gji/article/197/2/948/616510 Parameters ---------- v : VectorTimeFunction Particle velocity. p : TimeFunction Pressure field. """ s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Angular frequency w = 2. * np.pi * f0 # Density rho = 1. / b # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_u = p - s * vp * vp * rho * div(v.forward) + \ s * ((vp * vp * rho) / (w * qp)) * div(b * grad(p)) u_p = Eq(p.forward, damp * pde_u) return [u_v, u_p]
def sls_2nd_order(model, geometry, p, **kwargs): """ Implementation of the 2nd order viscoacoustic wave-equation from Bai (2014). https://library.seg.org/doi/10.1190/geo2013-0030.1 Parameters ---------- p : TimeFunction Pressure field. """ forward = kwargs.get('forward', True) space_order = p.space_order s = model.grid.stepping_dim.spacing b = model.b vp = model.vp damp = model.damp qp = model.qp f0 = geometry._f0 # The stress relaxation parameter t_s = (sp.sqrt(1.+1./qp**2)-1./qp)/f0 # The strain relaxation parameter t_ep = 1./(f0**2*t_s) # The relaxation time tt = (t_ep/t_s)-1. # Density rho = 1. / b r = TimeFunction(name="r", grid=model.grid, time_order=2, space_order=space_order, staggered=NODE) if forward: pde_r = r + s * (tt / t_s) * rho * div(b * grad(p, shift=.5), shift=-.5) - \ s * (1. / t_s) * r u_r = Eq(r.forward, damp * pde_r) pde_p = 2. * p - damp * p.backward + s * s * vp * vp * (1. + tt) * rho * \ div(b * grad(p, shift=.5), shift=-.5) - s * s * vp * vp * r.forward u_p = Eq(p.forward, damp * pde_p) return [u_r, u_p] else: pde_r = r + s * (tt / t_s) * p - s * (1. / t_s) * r u_r = Eq(r.backward, damp * pde_r) pde_p = 2. * p - damp * p.forward + s * s * vp * vp * \ div(b * grad((1. + tt) * rho * p, shift=.5), shift=-.5) - s * s * vp * vp * \ div(b * grad(rho * r.backward, shift=.5), shift=-.5) u_p = Eq(p.backward, damp * pde_p) return [u_r, u_p]
def blanch_symes(model, geometry, v, p, **kwargs): """ Stencil created from from Blanch and Symes (1995) / Dutta and Schuster (2014) viscoacoustic wave equation. https://library.seg.org/doi/pdf/10.1190/1.1822695 https://library.seg.org/doi/pdf/10.1190/geo2013-0414.1 Parameters ---------- v : VectorTimeFunction Particle velocity. p : TimeFunction Pressure field. """ save = kwargs.get('save') s = model.grid.stepping_dim.spacing b = model.b vp = model.vp damp = model.damp qp = model.qp f0 = geometry._f0 # The stress relaxation parameter t_s = (sp.sqrt(1. + 1. / qp**2) - 1. / qp) / f0 # The strain relaxation parameter t_ep = 1. / (f0**2 * t_s) # The relaxation time tt = (t_ep / t_s) - 1. # Density rho = 1. / b # Bulk modulus bm = rho * (vp * vp) # Memory variable. r = TimeFunction(name="r", grid=model.grid, staggered=NODE, save=geometry.nt if save else None, time_order=1) # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_r = r - s * (1. / t_s) * r - s * (1. / t_s) * tt * bm * div(v.forward) u_r = Eq(r.forward, damp * pde_r) pde_p = p - s * bm * (tt + 1.) * div(v.forward) - s * r.forward u_p = Eq(p.forward, damp * pde_p) return [u_v, u_r, u_p]
def ren_1st_order(model, geometry, p, **kwargs): """ Implementation of the 1st order viscoacoustic wave-equation from Ren et al. (2014). https://academic.oup.com/gji/article/197/2/948/616510 Parameters ---------- p : TimeFunction Pressure field. """ forward = kwargs.get('forward', True) s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Particle velocity v = kwargs.pop('v') # Angular frequency w0 = 2. * np.pi * f0 # Density rho = 1. / b eta = vp**2 / (w0 * qp) # Bulk modulus bm = rho * vp**2 if forward: # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_p = p - s * bm * div(v.forward) + \ s * eta * rho * div(b * grad(p, shift=.5), shift=-.5) u_p = Eq(p.forward, damp * pde_p) return [u_v, u_p] else: pde_v = v + s * grad(bm * p) u_v = Eq(v.backward, pde_v * damp) pde_p = p + s * div(b * grad(rho * eta * p, shift=.5), shift=-.5) + \ s * div(b * v.backward) u_p = Eq(p.backward, pde_p * damp) return [u_v, u_p]
def deng_1st_order(model, geometry, p, **kwargs): """ Implementation of the 1st order viscoacoustic wave-equation from Deng and McMechan (2007). https://library.seg.org/doi/pdf/10.1190/1.2714334 Parameters ---------- p : TimeFunction Pressure field. """ forward = kwargs.get('forward', True) s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Particle velocity v = kwargs.pop('v') # Angular frequency w0 = 2. * np.pi * f0 # Density rho = 1. / b # Bulk modulus bm = rho * vp**2 if forward: # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_p = p - s * bm * div(v.forward) - s * (w0 / qp) * p u_p = Eq(p.forward, damp * pde_p) return [u_v, u_p] else: pde_v = v + s * grad(bm * p) u_v = Eq(v.backward, pde_v * damp) pde_p = p + s * div(b * v.backward) - s * (w0 / qp) * p u_p = Eq(p.backward, pde_p * damp) return [u_v, u_p]
def ren_2nd_order(model, geometry, p, **kwargs): """ Implementation of the 2nd order viscoacoustic wave-equation from Ren et al. (2014). https://library.seg.org/doi/pdf/10.1190/1.2714334 Parameters ---------- p : TimeFunction Pressure field. """ forward = kwargs.get('forward', True) s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Angular frequency w0 = 2. * np.pi * f0 # Density rho = 1. / b eta = (vp * vp) / (w0 * qp) # Bulk modulus bm = rho * (vp * vp) if forward: pde_p = 2. * p - damp * p.backward + s * s * bm * \ div(b * grad(p, shift=.5), shift=-.5) + s * s * eta * rho * \ div(b * grad(p - p.backward, shift=.5) / s, shift=-.5) u_p = Eq(p.forward, damp * pde_p) return [u_p] else: pde_p = 2. * p - damp * p.forward + s * s * \ div(b * grad(bm * p, shift=.5), shift=-.5) - s * s * \ div(b * grad(((p.forward - p) / s) * rho * eta, shift=.5), shift=-.5) u_p = Eq(p.backward, damp * pde_p) return [u_p]
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 deng_mcmechan(model, geometry, v, p, **kwargs): """ Stencil created from Deng and McMechan (2007) viscoacoustic wave equation. https://library.seg.org/doi/pdf/10.1190/1.2714334 Parameters ---------- v : VectorTimeFunction Particle velocity. p : TimeFunction Pressure field. """ s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Angular frequency w = 2. * np.pi * f0 # Density rho = 1. / b # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_p = p - s * vp * vp * rho * div(v.forward) - s * (w / qp) * p u_p = Eq(p.forward, damp * pde_p) return [u_v, u_p]
def dm(self): """ Create a simple model perturbation from the velocity as `dm = div(vp)`. """ dm = Function(name="dm", grid=self.grid, space_order=self.space_order) Operator(Eq(dm, div(self.vp)), subs=self.spacing_map)() return dm
def test_solve(so): """ Test that our solve produces the correct output and faster than sympy's default behavior for an affine equation (i.e. PDE time steppers). """ grid = Grid((10, 10, 10)) u = TimeFunction(name="u", grid=grid, time_order=2, space_order=so) v = Function(name="v", grid=grid, space_order=so) eq = u.dt2 - div(v * grad(u)) # Standard sympy solve t0 = time.time() sol1 = sympy.solve(eq.evaluate, u.forward, rational=False, simplify=False)[0] t1 = time.time() - t0 # Devito custom solve for linear equation in the target ax + b (most PDE tie steppers) t0 = time.time() sol2 = solve(eq.evaluate, u.forward) t12 = time.time() - t0 diff = sympy.simplify(sol1 - sol2) # Difference can end up with super small coeffs with different evaluation # so zero out everything very small assert diff.xreplace({k: 0 if abs(k) < 1e-10 else k for k in diff.atoms(sympy.Float)}) == 0 # Make sure faster (actually much more than 10 for very complex cases) assert t12 < t1/10
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 dm(self): """ Create a simple model perturbation (in squared slowness) from the velocity as `dm = div(m)`. """ dm = Function(name="dm", grid=self.grid, space_order=self.space_order) Operator(Eq(dm, div(self.m)))() return dm
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 test_shifted_div(self, shift, ndim): grid = Grid(tuple([11] * ndim)) f = Function(name="f", grid=grid, space_order=4) df = div(f, shift=shift).evaluate ref = 0 for d in grid.dimensions: x0 = None if shift is None else d + shift * d.spacing ref += getattr(f, 'd%s' % d.name)(x0=x0) assert df == ref.evaluate
def test_shifted_div_of_vectorfunction(self, shift, ndim): grid = Grid(tuple([11] * ndim)) f = Function(name="f", grid=grid, space_order=4) df = div(grad(f), shift=shift).evaluate ref = 0 for i, d in enumerate(grid.dimensions): x0 = None if shift is None else d + shift * d.spacing ref += getattr(grad(f)[i], 'd%s' % d.name)(x0=x0) assert df == ref.evaluate
def deng_2nd_order(model, geometry, p, **kwargs): """ Implementation of the 2nd order viscoacoustic wave-equation from Deng and McMechan (2007). https://library.seg.org/doi/pdf/10.1190/1.2714334 Parameters ---------- p : TimeFunction Pressure field. """ forward = kwargs.get('forward', True) s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Angular frequency w0 = 2. * np.pi * f0 # Density rho = 1. / b bm = rho * (vp * vp) if forward: pde_p = 2. * p - damp*p.backward + s * s * bm * \ div(b * grad(p, shift=.5), shift=-.5) - s * s * w0/qp * (p - p.backward)/s u_p = Eq(p.forward, damp * pde_p) return [u_p] else: pde_p = 2. * p - damp * p.forward + s * s * w0 / qp * (p.forward - p) / s + \ s * s * div(b * grad(bm * p, shift=.5), shift=-.5) u_p = Eq(p.backward, damp * pde_p) return [u_p]
def test_shifted_div_of_vector(shift, ndim): grid = Grid(tuple([11] * ndim)) v = VectorFunction(name="f", grid=grid, space_order=4) df = div(v, shift=shift).evaluate ref = 0 for i, d in enumerate(grid.dimensions): x0 = (None if shift is None else d + shift[i] * d.spacing if type(shift) is tuple else d + shift * d.spacing) ref += getattr(v[i], 'd%s' % d.name)(x0=x0) assert df == ref.evaluate
def ren_1st_order(model, geometry, p, **kwargs): """ Implementation of the 1st order viscoacoustic wave-equation from Ren et al. (2014). https://academic.oup.com/gji/article/197/2/948/616510 Parameters ---------- p : TimeFunction Pressure field. """ s = model.grid.stepping_dim.spacing f0 = geometry._f0 vp = model.vp b = model.b qp = model.qp damp = model.damp # Particle velocity v = kwargs.pop('v') # Angular frequency w0 = 2. * np.pi * f0 # Density rho = 1. / b # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_p = p - s * vp * vp * rho * div(v.forward) + \ s * ((vp * vp * rho) / (w0 * qp)) * div(b * grad(p, shift=.5), shift=-.5) u_p = Eq(p.forward, damp * pde_p) return [u_v, u_p]
def test_shifted_div_of_tensor(shift, ndim): grid = Grid(tuple([11] * ndim)) f = TensorFunction(name="f", grid=grid, space_order=4) df = div(f, shift=shift).evaluate ref = [] for i, a in enumerate(grid.dimensions): elems = [] for j, d in reversed(list(enumerate(grid.dimensions))): x0 = (None if shift is None else d + shift[i][j] * d.spacing if type(shift) is tuple else d + shift * d.spacing) ge = getattr(f[i, j], 'd%s' % d.name)(x0=x0) elems.append(ge.evaluate) ref.append(sum(elems)) for i, d in enumerate(df): assert d == ref[i]
def sls_1st_order(model, geometry, p, r=None, **kwargs): """ Implementation of the 1st order viscoacoustic wave-equation from Blanch and Symes (1995) / Dutta and Schuster (2014). https://library.seg.org/doi/pdf/10.1190/1.1822695 https://library.seg.org/doi/pdf/10.1190/geo2013-0414.1 Parameters ---------- p : TimeFunction Pressure field. r : TimeFunction Memory variable. """ forward = kwargs.get('forward', True) space_order = p.space_order save = kwargs.get('save', False) save_t = geometry.nt if save else None s = model.grid.stepping_dim.spacing b = model.b vp = model.vp damp = model.damp qp = model.qp f0 = geometry._f0 q = kwargs.get('q', 0) # Particle Velocity v = kwargs.pop('v') # The stress relaxation parameter t_s = (sp.sqrt(1.+1./qp**2)-1./qp)/f0 # The strain relaxation parameter t_ep = 1./(f0**2*t_s) # The relaxation time tt = (t_ep/t_s)-1. # Density rho = 1. / b # Bulk modulus bm = rho * vp**2 # Attenuation Memory variable. r = r or TimeFunction(name="r", grid=model.grid, time_order=1, space_order=space_order, save=save_t, staggered=NODE) if forward: # Define PDE pde_v = v - s * b * grad(p) u_v = Eq(v.forward, damp * pde_v) pde_r = r - s * (1. / t_s) * r - s * (1. / t_s) * tt * rho * div(v.forward) u_r = Eq(r.forward, damp * pde_r) pde_p = p - s * bm * (tt + 1.) * div(v.forward) - s * vp**2 * r.forward + \ s * vp**2 * q u_p = Eq(p.forward, damp * pde_p) return [u_v, u_r, u_p] else: # Define PDE pde_r = r - s * (1. / t_s) * r - s * p u_r = Eq(r.backward, damp * pde_r) pde_v = v + s * grad(rho * (1. + tt) * p) + s * \ grad((1. / t_s) * rho * tt * r.backward) u_v = Eq(v.backward, damp * pde_v) pde_p = p + s * vp**2 * div(b * v.backward) u_p = Eq(p.backward, damp * pde_p) return [u_r, u_v, u_p]
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)