Exemple #1
0
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
Exemple #2
0
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]
Exemple #3
0
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
Exemple #5
0
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]
Exemple #6
0
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]
Exemple #7
0
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]
Exemple #8
0
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))
Exemple #9
0
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]
Exemple #10
0
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
Exemple #12
0
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
Exemple #13
0
 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
Exemple #14
0
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]
Exemple #15
0
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]
Exemple #16
0
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]
Exemple #17
0
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]
Exemple #18
0
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
Exemple #20
0
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]