def isic_freq(u, v, model, **kwargs): """ Inverse scattering imaging condition Parameters ---------- u: TimeFunction or Tuple Forward wavefield (tuple of fields for TTI or dft) v: TimeFunction or Tuple Adjoint wavefield (tuple of fields for TTI) model: Model Model structure """ freq = kwargs.get('freq') # Subsampled dft time axis time = model.grid.time_dim dt = time.spacing tsave, factor = sub_time(time, kwargs.get('factor')) expr = 0 for uu, vv in zip(u, v): ufr, ufi = uu # Frequencies nfreq = freq.shape[0] f = Function(name='f', dimensions=(ufr.dimensions[0],), shape=(nfreq,)) f.data[:] = freq[:] omega_t = 2*np.pi*f*tsave*factor*dt # Gradient weighting is (2*np.pi*f)**2/nt w = (2*np.pi*f)**2/time.symbolic_max expr += (w * (ufr * cos(omega_t) - ufi * sin(omega_t)) * vv * model.m - factor / time.symbolic_max * (grad(ufr * cos(omega_t) - ufi * sin(omega_t)).T * grad(vv))) return expr
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 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 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 isic_time(u, v, model, **kwargs): """ Inverse scattering imaging condition Parameters ---------- u: TimeFunction or Tuple Forward wavefield (tuple of fields for TTI or dft) v: TimeFunction or Tuple Adjoint wavefield (tuple of fields for TTI) model: Model Model structure """ w = - u.dimensions[0].spacing * model.irho return w * (u * v.dt2 * model.m + grad(u).T * grad(v))
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 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_shifted_grad(self, shift, ndim): grid = Grid(tuple([11] * ndim)) f = Function(name="f", grid=grid, space_order=4) g = grad(f, shift=shift).evaluate for d, gi in zip(grid.dimensions, g): x0 = None if shift is None else d + shift * d.spacing assert gi == getattr(f, 'd%s' % d.name)(x0=x0).evaluate
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_grad(self, shift, ndim): grid = Grid(tuple([11] * ndim)) f = Function(name="f", grid=grid, space_order=4) g = grad(f, shift=shift).evaluate for i, (d, gi) in enumerate(zip(grid.dimensions, g)): x0 = (None if shift is None else d + shift[i] * d.spacing if type(shift) is tuple else d + shift * d.spacing) assert gi == getattr(f, 'd%s' % d.name)(x0=x0).evaluate
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 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 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_grad_of_vector(shift, ndim): grid = Grid(tuple([11] * ndim)) f = VectorFunction(name="f", grid=grid, space_order=4) gf = grad(f, shift=shift).evaluate ref = [] for i in range(len(grid.dimensions)): for j, d in 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], 'd%s' % d.name)(x0=x0) ref.append(ge.evaluate) for i, d in enumerate(gf): assert d == ref[i]
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 _axial_setup(self): """ Update the axial distance function from the signed distance function. """ # Recurring value for tidiness m_size = int(self._order/2) # Create a padded version of the signed distance function pad_sdf = Function(name='pad_sdf', grid=self._pad, space_order=self._order) pad_sdf.data[:] = np.pad(self._sdf.data, (m_size,), 'edge') # Set default values for axial distance self._axial[0].data[:] = -self._order*self._pad.spacing[0] self._axial[1].data[:] = -self._order*self._pad.spacing[1] self._axial[2].data[:] = -self._order*self._pad.spacing[2] # Equations to decompose distance into axial distances x, y, z = self._pad.dimensions h_x, h_y, h_z = self._pad.spacing pos = sp.Matrix([x*h_x, y*h_y, z*h_z]) sdf_grad = grad(pad_sdf).evaluate # Gradient of the sdf # Plane eq: a*x + b*y + c*z = d a = sdf_grad[0] b = sdf_grad[1] c = sdf_grad[2] d = sdf_grad.dot(pos - pad_sdf*sdf_grad) # Only need to calculate adjacent to boundary close_sdf = Le(sp.Abs(pad_sdf), h_x) # Conditional mask for calculation mask = ConditionalDimension(name='mask', parent=z, condition=close_sdf) eq_x = Eq(self._axial[0], (d - b*pos[1] - c*pos[2])/a - pos[0], implicit_dims=mask) eq_y = Eq(self._axial[1], (d - a*pos[0] - c*pos[2])/b - pos[1], implicit_dims=mask) eq_z = Eq(self._axial[2], (d - a*pos[0] - b*pos[1])/c - pos[2], implicit_dims=mask) op_axial = Operator([eq_x, eq_y, eq_z], name='Axial') op_axial.apply() # Deal with silly values x x_nan_mask = np.isnan(self._axial[0].data) self._axial[0].data[x_nan_mask] = -self._order*h_x x_big_mask = np.abs(self._axial[0].data) > h_x self._axial[0].data[x_big_mask] = -self._order*h_x # Deal with silly values y y_nan_mask = np.isnan(self._axial[1].data) self._axial[1].data[y_nan_mask] = -self._order*h_y y_big_mask = np.abs(self._axial[1].data) > h_y self._axial[1].data[y_big_mask] = -self._order*h_y # Deal with silly values z z_nan_mask = np.isnan(self._axial[2].data) self._axial[2].data[z_nan_mask] = -self._order*h_z z_big_mask = np.abs(self._axial[2].data) > h_z self._axial[2].data[z_big_mask] = -self._order*h_z
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]