Beispiel #1
0
def simulate_seismic_topo (topo, circles_list, not_circles, vmax=5, vmin=1, f0 = 0.02500, dx=10, dy=10, t0=0, tn=700, pmlthickness=40, sigma_x=2, sigma_y=2, n_frames = 50):
    if circles_list == []:
        circles_list = [[int(topo.shape[0]/2),int(topo.shape[1]/2)]]
    circles = np.array(circles_list)

    topo = topo.astype(np.float32)
    topoRescale = scale_linear(topo, vmax, vmin)
    veltopo=smooth_topo( topoRescale, sigma_x, sigma_y )
    if not_circles != []:
        veltopo[not_circles] = vmax * 1.8

    # Define the model
    model = Model(vp=veltopo,        # A velocity model.
                  origin=(0, 0),     # Top left corner.
                  shape=veltopo.shape,    # Number of grid points.
                  spacing=(dx, dy),  # Grid spacing in m.
                  nbpml=pmlthickness)          # boundary layer.

    dt = model.critical_dt  # Time step from model grid spacing
    nt = int(1 + (tn-t0) / dt)  # Discrete time axis length
    time = np.linspace(t0, tn, nt)  # Discrete modelling time

    u = TimeFunction(name="u", grid=model.grid,
                 time_order=2, space_order=2,
                 save=True, time_dim=nt)
    pde = model.m * u.dt2 - u.laplace + model.damp * u.dt
    stencil = Eq(u.forward, solve(pde, u.forward)[0])


    src_coords = np.multiply(circles[0],[dx,dy])
    src = RickerSource(name='src0', grid=model.grid, f0=f0, time=time, coordinates=src_coords)
    src_term = src.inject(field=u.forward, expr=src * dt**2 / model.m, offset=model.nbpml)

    if circles.shape[0]>1:
        for idx, row in enumerate(circles[1:,:]):
            namesrc = 'src' + str(idx+1)
            src_coords = np.multiply(row,[dx,dy])
            src_temp = RickerSource(name=namesrc, grid=model.grid, f0=f0, time=time, coordinates=src_coords)
            src_term_temp = src_temp.inject(field=u.forward, expr=src * dt**2 / model.m, offset=model.nbpml)
            src_term += src_term_temp

    op_fwd = Operator( [stencil] + src_term )
    op_fwd(time=nt, dt=model.critical_dt)

    wf_data = u.data[:,pmlthickness:-pmlthickness,pmlthickness:-pmlthickness]
    wf_data_normalize = wf_data/np.amax(wf_data)

    framerate=np.int(np.ceil(wf_data.shape[0]/n_frames))
    return wf_data_normalize[0::framerate,:,:]
def reference_shot(model, time_range, f0):
    """
    Produce a reference shot gather with a level, conventional free-surface
    implementation.
    """
    src = RickerSource(name='src',
                       grid=model.grid,
                       f0=f0,
                       npoint=1,
                       time_range=time_range)

    # First, position source centrally in all dimensions, then set depth
    src.coordinates.data[0, :] = np.array(model.domain_size) * .5
    # Remember that 0, 0, 0 is top left corner
    # Depth is 100m from free-surface boundary
    src.coordinates.data[0, -1] = 600.

    # Create symbol for 101 receivers
    rec = Receiver(name='rec',
                   grid=model.grid,
                   npoint=101,
                   time_range=time_range)

    # Prescribe even spacing for receivers along the x-axis
    rec.coordinates.data[:, 0] = np.linspace(0, model.domain_size[0], num=101)
    rec.coordinates.data[:, 1] = 500.  # Centered on y axis
    rec.coordinates.data[:, 2] = 650.  # Depth is 150m from free surface

    # Define the wavefield with the size of the model and the time dimension
    u = TimeFunction(name="u", grid=model.grid, time_order=2, space_order=4)

    # We can now write the PDE
    pde = model.m * u.dt2 - u.laplace + model.damp * u.dt

    stencil = Eq(u.forward, solve(pde, u.forward))

    # Finally we define the source injection and receiver read function
    src_term = src.inject(field=u.forward,
                          expr=src * model.critical_dt**2 / model.m)

    # Create interpolation expression for receivers
    rec_term = rec.interpolate(expr=u.forward)

    x, y, z = model.grid.dimensions
    time = u.grid.stepping_dim
    # Throw a free surface in here
    free_surface_0 = Eq(u[time + 1, x, y, 60], 0)
    free_surface_1 = Eq(u[time + 1, x, y, 59], -u[time + 1, x, y, 61])
    free_surface_2 = Eq(u[time + 1, x, y, 58], -u[time + 1, x, y, 62])
    free_surface = [free_surface_0, free_surface_1, free_surface_2]

    op = Operator([stencil] + src_term + rec_term + free_surface)

    op(time=time_range.num - 1, dt=model.critical_dt)

    return rec.data
Beispiel #3
0
    def iso_acoustic(self, opt):
        shape = (101, 101)
        extent = (1000, 1000)
        origin = (0., 0.)

        v = np.empty(shape, dtype=np.float32)
        v[:, :51] = 1.5
        v[:, 51:] = 2.5

        grid = Grid(shape=shape, extent=extent, origin=origin)

        t0 = 0.
        tn = 1000.
        dt = 1.6
        time_range = TimeAxis(start=t0, stop=tn, step=dt)

        f0 = 0.010
        src = RickerSource(name='src',
                           grid=grid,
                           f0=f0,
                           npoint=1,
                           time_range=time_range)

        domain_size = np.array(extent)

        src.coordinates.data[0, :] = domain_size * .5
        src.coordinates.data[0, -1] = 20.

        rec = Receiver(name='rec',
                       grid=grid,
                       npoint=101,
                       time_range=time_range)
        rec.coordinates.data[:, 0] = np.linspace(0, domain_size[0], num=101)
        rec.coordinates.data[:, 1] = 20.

        u = TimeFunction(name="u", grid=grid, time_order=2, space_order=2)
        m = Function(name='m', grid=grid)
        m.data[:] = 1. / (v * v)

        pde = m * u.dt2 - u.laplace
        stencil = Eq(u.forward, solve(pde, u.forward))

        src_term = src.inject(field=u.forward, expr=src * dt**2 / m)
        rec_term = rec.interpolate(expr=u.forward)

        op = Operator([stencil] + src_term + rec_term,
                      opt=opt,
                      language='openmp')

        # Make sure we've indeed generated OpenMP offloading code
        assert 'omp target' in str(op)

        op(time=time_range.num - 1, dt=dt)

        assert np.isclose(norm(rec), 490.55, atol=1e-2, rtol=0)
Beispiel #4
0
#==============================================================================
# Construção Receivers Homogeneo
#==============================================================================
rec0 = Receiver(name='rec0', grid=model0.grid,npoint=nrec,time_range=time_range0)
rec0.coordinates.data[:,0] = nxrecpos
rec0.coordinates.data[:,1] = nzrecpos 
#==============================================================================

#==============================================================================
# Construção dos Campos Modelo Homogêneo
#==============================================================================
u0         = TimeFunction(name="u0",grid=model0.grid,time_order=tou,space_order=sou)    
pde0       = model0.m * u0.dt2 - u0.laplace + model0.damp * u0.dt
stencil0   = Eq(u0.forward, solve(pde0, u0.forward))
src_term0  = src0.inject(field=u0.forward, expr=src0* dt0**2 / model0.m)
rec_term0  = rec0.interpolate(expr=u0)
#==============================================================================

#==============================================================================
# Construção do Operador do Modelo Homogeneo
#==============================================================================
op0 = Operator([stencil0] + src_term0 + rec_term0, subs=model0.spacing_map)
#==============================================================================

#==============================================================================
# For para o Número de Fontes
#==============================================================================
for k in range(0,number_xfontpos):    
#==============================================================================
    print("Homogen Source:", k)
# space-varying field (m, Function)
from devito import TimeFunction, Buffer

# Define the wavefield with the size of the model and the time dimension
u = TimeFunction(name="u",
                 grid=model.grid,
                 time_order=2,
                 space_order=2,
                 save=Buffer(time_range.num + 1))

# We can now write the PDE
pde = model.m * u.dt2 - u.laplace + model.damp * u.dt

# The PDE representation is as on paper

from devito import Eq, solve

stencil = Eq(u.forward, solve(pde, u.forward))

# Finally we define the source injection and receiver read function to generate the corresponding code
src_term = src.inject(field=u.forward, expr=src * dt**2 / model.m)

# Create interpolation expression for receivers
rec_term = rec.interpolate(expr=u.forward)

from devito import Operator

op = Operator([stencil] + src_term + rec_term, subs=model.spacing_map)

op(time=time_range.num - 1, dt=model.critical_dt)
Beispiel #6
0
def subsampled_gradient(factor=1, tn=2000.):
    t0 = 0.  # Simulation starts a t=0

    shape = (100, 100)
    origin = (0., 0.)

    spacing = (15., 15.)

    space_order = 4

    vp = np.empty(shape, dtype=np.float64)
    vp[:, :51] = 1.5
    vp[:, 51:] = 2.5

    model = Model(vp=vp, origin=origin, shape=shape, spacing=spacing,
                  space_order=space_order, nbl=10)

    dt = model.critical_dt  # Time step from model grid spacing
    time_range = TimeAxis(start=t0, stop=tn, step=dt)
    nt = time_range.num  # number of time steps

    f0 = 0.010  # Source peak frequency is 10Hz (0.010 kHz)
    src = RickerSource(
        name='src',
        grid=model.grid,
        f0=f0,
        time_range=time_range)

    src.coordinates.data[0, :] = np.array(model.domain_size) * .5
    src.coordinates.data[0, -1] = 20.  # Depth is 20m

    rec = Receiver(
        name='rec',
        grid=model.grid,
        npoint=101,
        time_range=time_range)  # new
    rec.coordinates.data[:, 0] = np.linspace(0, model.domain_size[0], num=101)
    rec.coordinates.data[:, 1] = 20.  # Depth is 20m

    save_elements = (nt + factor - 1) // factor

    print(save_elements)

    time_subsampled = ConditionalDimension(
        't_sub', parent=model.grid.time_dim, factor=factor)
    usave = TimeFunction(name='usave', grid=model.grid, time_order=2,
                         space_order=space_order, save=save_elements,
                         time_dim=time_subsampled)

    u = TimeFunction(name="u", grid=model.grid, time_order=2,
                     space_order=space_order)
    pde = model.m * u.dt2 - u.laplace + model.damp * u.dt
    stencil = Eq(u.forward, solve(pde, u.forward))
    src_term = src.inject(
        field=u.forward,
        expr=src * dt**2 / model.m,
        offset=model.nbl)
    rec_term = rec.interpolate(expr=u, offset=model.nbl)

    fwd_op = Operator([stencil] + src_term + [Eq(usave, u)] + rec_term,
                      subs=model.spacing_map)  # operator with snapshots
    v = TimeFunction(name='v', grid=model.grid, save=None,
                     time_order=2, space_order=space_order)
    grad = Function(name='grad', grid=model.grid)

    rev_pde = model.m * v.dt2 - v.laplace + model.damp * v.dt.T
    rev_stencil = Eq(v.backward, solve(rev_pde, v.backward))
    gradient_update = Inc(grad, - usave.dt2 * v)

    s = model.grid.stepping_dim.spacing

    receivers = rec.inject(field=v.backward, expr=rec*s**2/model.m)
    rev_op = Operator([rev_stencil] + receivers + [gradient_update],
                      subs=model.spacing_map)

    fwd_op(time=nt - 2, dt=model.critical_dt)

    rev_op(dt=model.critical_dt, time=nt-16)

    return grad.data
# First, position source centrally in all dimensions, then set depth

stx = 0.1
ste = 0.9
stepx = (ste - stx) / int(np.sqrt(src.npoint))

src.coordinates.data[:, :2] = np.array(
    np.meshgrid(np.arange(stx, ste, stepx), np.arange(
        stx, ste, stepx))).T.reshape(-1, 2) * np.array(model.domain_size[:1])

src.coordinates.data[:, -1] = 20  # Depth is 20m

# f : perform source injection on an empty grid
f = TimeFunction(name="f", grid=model.grid, space_order=so, time_order=2)
src_f = src.inject(field=f.forward, expr=src * dt**2 / model.m)
# op_f = Operator([src_f], opt=('advanced', {'openmp': True}))
op_f = Operator([src_f])
op_f.apply(time=time_range.num - 1)
normf = norm(f)
print("==========")
print(normf)
print("===========")

# uref : reference solution
# uref = TimeFunction(name="uref", grid=model.grid, space_order=so, time_order=2)
# src_term_ref = src.inject(field=uref.forward, expr=src * dt**2 / model.m)
# pde_ref = model.m * uref.dt2 - uref.laplace + model.damp * uref.dt
# stencil_ref = Eq(uref.forward, solve(pde_ref, uref.forward))

#Get the nonzero indices
Beispiel #8
0
def fwi_gradient(vp_in):
    # AUTO
    grad = Function(name="grad", grid=model.grid)
    residual = Receiver(name='rec',
                        grid=model.grid,
                        time_range=geometry.time_axis,
                        coordinates=geometry.rec_positions)
    objective = 0.
    u0 = TimeFunction(name='u',
                      grid=model.grid,
                      time_order=2,
                      space_order=4,
                      save=geometry.nt)

    # MANUAL
    grad_manual = Function(name="grad", grid=model.grid)
    residual_man = Receiver(name='rec',
                            grid=model.grid,
                            time_range=time_axis,
                            coordinates=rec_coordinates)
    objective_manual = 0.
    u0_man = TimeFunction(name='u',
                          grid=model.grid,
                          time_order=2,
                          space_order=4,
                          save=nt)
    for i in range(9):
        # AUTO
        clear_cache()
        geometry.src_positions[0, :] = source_locations[i, :]
        true_d, _, _ = solver.forward(vp=model.vp)
        u0.data.fill(0.)
        smooth_d, _, _ = solver.forward(vp=vp_in, save=True, u=u0)
        residual.data[:] = smooth_d.data[:] - true_d.data[:]
        objective += .5 * np.linalg.norm(residual.data.flatten())**2
        solver.gradient(rec=residual, u=u0, vp=vp_in, grad=grad)

        # MANUAL
        # source
        src_true = RickerSource(name='src',
                                grid=model.grid,
                                time_range=time_axis,
                                coordinates=source_locations[i, :],
                                npoint=1,
                                f0=f0)
        src_term = src_true.inject(
            field=u.forward,
            expr=src_true * model.grid.stepping_dim.spacing**2 / model.m)

        # receiver
        rec_true = Receiver(name='rec',
                            grid=model.grid,
                            time_range=time_axis,
                            coordinates=rec_coordinates,
                            npoint=nreceivers)
        rec_term = rec_true.interpolate(expr=u)

        # operator
        op_fwd = Operator(eqn + src_term + rec_term,
                          subs=model.spacing_map,
                          name='Forward')
        op_fwd.apply(src=src_true,
                     rec=rec_true,
                     u=u0_man,
                     vp=model.vp,
                     dt=model.critical_dt)

        u0_man.data.fill(0.)
        rec_smooth = Receiver(name='rec',
                              grid=model.grid,
                              time_range=time_axis,
                              coordinates=rec_coordinates,
                              npoint=nreceivers)
        op_fwd.apply(src=src_true,
                     rec=rec_smooth,
                     u=u0_man,
                     vp=vp_in,
                     dt=model.critical_dt)

        # back-receiver
        rec_back = Receiver(name='rec',
                            grid=model.grid,
                            time_range=time_axis,
                            coordinates=rec_coordinates,
                            npoint=nreceivers)
        rec_back_term = rec_back.inject(
            field=v.backward,
            expr=rec_back * model.grid.stepping_dim.spacing**2 / model.m)

        # gradient
        gradient_update = Inc(grad_manual, -u.dt2 * v)
        op_grad = Operator(eqn_back + rec_back_term + [gradient_update],
                           subs=model.spacing_map,
                           name='Gradient')
        residual_man.data[:] = rec_smooth.data[:] - rec_true.data[:]
        objective_manual += .5 * np.linalg.norm(residual_man.data.flatten())**2
        op_grad.apply(rec=residual_man,
                      u=u0_man,
                      vp=vp_in,
                      dt=model.critical_dt,
                      grad=grad_manual)

        # sanity-check -> expect for 0!
        # plot_shotrecord(true_d.data[:] - rec_true.data[:], model, t0, tn)
        # plot_shotrecord(smooth_d.data[:] - rec_smooth.data[:], model, t0, tn)
    return objective, -grad.data, objective_manual, -grad_manual.data
Beispiel #9
0
from examples.seismic import RickerSource

# Define the time parameters of our simulation
t0 = 0.0  # Start time
tn = 400.  # Final time
dt = 4.2  # Timestep size in ms
nt = int(1 + (tn - t0) / dt)  # Number of timesteps
time_values = np.linspace(t0, tn, nt)  # Discretized time axis

grid = Grid(shape=(120, 120), extent=(1800., 1800.))

# Define source geometry (slightly above the center)
src = RickerSource(name='src', grid=grid, f0=0.01, time=time_values)
src.coordinates.data[0, :] = [900., 550.]

u = TimeFunction(name='u', grid=grid, space_order=2, time_order=2)
m = Function(name='m', grid=grid)
m.data[:] = 1. / 1.5**2

eqn = Eq(m * u.dt2 - u.laplace)
stencil = solve(eqn, u.forward)[0]
update = Eq(u.forward, stencil)

source = src.inject(field=u.forward, expr=src * dt**2 / m)
op = Operator([update] + source)

# Run for warm-up time; make sure it's % 3
op(t=51, dt=dt)

np.save('wavefield', u.data)
Beispiel #10
0
vnz = nz + 20

# Set symbolics for the wavefield object `u`, setting save on all time steps
# (which can occupy a lot of memory), to later collect snapshots (naive method):

u = TimeFunction(name="u",
                 grid=model.grid,
                 time_order=2,
                 space_order=2,
                 save=time_range.num)

# Set symbolics of the operator, source and receivers:
pde = model.m * u.dt2 - u.laplace + model.damp * u.dt
stencil = Eq(u.forward, solve(pde, u.forward))
src_term = src.inject(field=u.forward,
                      expr=src * dt**2 / model.m,
                      offset=model.nbpml)
rec_term = rec.interpolate(expr=u, offset=model.nbpml)
op = Operator([stencil] + src_term + rec_term, subs=model.spacing_map)

# Run the operator for `(nt-2)` time steps:
op(time=nt - 2, dt=model.critical_dt)

#########################################################################################################

#NBVAL_IGNORE_OUTPUT
from devito import ConditionalDimension

nsnaps = 100  # desired number of equally spaced snaps
factor = round(nt / nsnaps)  # subsequent calculated factor
Beispiel #11
0
u0_man = TimeFunction(name='u',
                      grid=model.grid,
                      time_order=2,
                      space_order=4,
                      save=nt)
receivers = []

for i in range(9):
    src_true = RickerSource(name='src',
                            grid=model.grid,
                            time_range=time_axis,
                            coordinates=source_locations[i, :],
                            npoint=1,
                            f0=f0)
    src_term = src_true.inject(field=u.forward,
                               expr=src_true *
                               model.grid.stepping_dim.spacing**2 / model.m)

    # receiver
    receiver = Receiver(name='rec',
                        grid=model.grid,
                        time_range=time_axis,
                        coordinates=rec_coordinates,
                        npoint=nreceivers)
    rec_term = receiver.interpolate(expr=u)
    receivers.append(receiver)

    # operator
    op_fwd = Operator(eqn + src_term + rec_term,
                      subs=model.spacing_map,
                      name='Forward')
def tilted_shot(model, time_range, f0, tilt, qc=False, toggle_normals=False):
    """
    Produce a shot for the same setup, but tilted with immersed free surface
    """
    src = RickerSource(name='src',
                       grid=model.grid,
                       f0=f0,
                       npoint=1,
                       time_range=time_range)

    # First, position source, then set depth
    src.coordinates.data[0, 0] = 500. - 100. * np.sin(np.radians(tilt))
    src.coordinates.data[0, 1] = 500.
    # Remember that 0, 0, 0 is top left corner
    # Depth is 100m from free-surface boundary
    src.coordinates.data[0, 2] = 500. + 100. * np.cos(np.radians(tilt))

    # Create symbol for 101 receivers
    rec = Receiver(name='rec',
                   grid=model.grid,
                   npoint=101,
                   time_range=time_range)

    # Prescribe even spacing for receivers along the x-axis
    rec_center_x = 500. - 150. * np.sin(np.radians(tilt))
    rec_center_z = 500. + 150. * np.cos(np.radians(tilt))

    rec_top_x = rec_center_x - 500. * np.cos(np.radians(tilt))
    rec_bottom_x = rec_center_x + 500. * np.cos(np.radians(tilt))

    rec_top_z = rec_center_z - 500. * np.sin(np.radians(tilt))
    rec_bottom_z = rec_center_z + 500. * np.sin(np.radians(tilt))

    rec.coordinates.data[:, 0] = np.linspace(rec_top_x, rec_bottom_x, num=101)
    rec.coordinates.data[:, 1] = 500.  # Centered on y axis
    rec.coordinates.data[:, 2] = np.linspace(rec_top_z, rec_bottom_z, num=101)

    # Define the wavefield with the size of the model and the time dimension
    u = TimeFunction(name="u",
                     grid=model.grid,
                     time_order=2,
                     space_order=4,
                     coefficients='symbolic')

    infile = 'tests/trial_surfaces/angled_surface_' + str(tilt) + '.ply'

    # Zero even derivatives on the boundary
    spec = {2 * i: 0 for i in range(u.space_order)}
    bcs_u = BoundaryConditions(spec, u.space_order)

    functions = pd.DataFrame({
        'function': [u],
        'bcs': [bcs_u]
    },
                             columns=['function', 'bcs'])

    # Create the immersed boundary surface
    surface = ImmersedBoundary('topography',
                               infile,
                               functions,
                               interior_point=tuple(src.coordinates.data[0]),
                               qc=qc,
                               toggle_normals=toggle_normals)
    # Configure derivative needed
    derivs = pd.DataFrame(
        {
            'function': [u],
            'derivative': [2],
            'eval_offset': [(0., 0., 0.)]
        },
        columns=['function', 'derivative', 'eval_offset'])
    coeffs = surface.subs(derivs)

    # We can now write the PDE
    pde = model.m * u.dt2 - u.laplace + model.damp * u.dt

    stencil = Eq(u.forward, solve(pde, u.forward), coefficients=coeffs)

    # Finally we define the source injection and receiver read function
    src_term = src.inject(field=u.forward,
                          expr=src * model.critical_dt**2 / model.m)

    # Create interpolation expression for receivers
    rec_term = rec.interpolate(expr=u.forward)

    op = Operator([stencil] + src_term + rec_term)
    op(time=time_range.num - 1, dt=model.critical_dt)

    return rec.data
Beispiel #13
0
src.coordinates.data[:, -1] = 13  # Depth is 20m

#src.coordinates.data[0, :] = np.array(model.domain_size) * .5
#src.coordinates.data[0, -1] = 20  # Depth is 20m
#src.coordinates.data[1, :] = np.array(model.domain_size) * .5
#src.coordinates.data[1, -1] = 20  # Depth is 20m
#src.coordinates.data[2, :] = np.array(model.domain_size) * .5
#src.coordinates.data[2, -1] = 20  # Depth is 20m

#src.show()
#pause(1)

# f : perform source injection on an empty grid
f = TimeFunction(name="f", grid=model.grid, space_order=so, time_order=2)
src_f = src.inject(field=f.forward, expr=src)
# op_f = Operator([src_f], opt=('advanced', {'openmp': True}))
op_f = Operator([src_f])
op_f.apply(time=time_range.num-1)
normf = norm(f)
print("==========")
print(normf)
print("===========")

# uref : reference solution
uref = TimeFunction(name="uref", grid=model.grid, space_order=so, time_order=2)
src_term_ref = src.inject(field=uref.forward, expr=src)
pde_ref = model.m * uref.dt2 - uref.laplace + model.damp * uref.dt
stencil_ref = Eq(uref.forward, solve(pde_ref, uref.forward))

#Get the nonzero indices
Beispiel #14
0
t0 = time.time()
#######################################################################################
# Source
# Source is at the middle of the domain at 10m depth
tstart, tn = 0., 16000.
time_range = TimeAxis(start=tstart, stop=tn, step=dt_cfl)

f0 = 0.010
r = np.pi * f0 * (time_range.time_values - 1. / f0)
wavelet = (1 - 2. * r**2) * np.exp(-r**2)

src_coords = np.array([17500., 20000., 10.])
src = RickerSource(name='src', grid=grid, f0=0.010, time_range=time_range)
src.coordinates.data[0, :] = src_coords

src_P = src.inject(field=tau.forward[0, 0], expr=s * src)
src_P += src.inject(field=tau.forward[1, 1], expr=s * src)
src_P += src.inject(field=tau.forward[2, 2], expr=s * src)
from scipy.signal import butter, sosfilt


def butter_lowpass(cutoff, nyq_freq, order=5):
    normal_cutoff = float(cutoff) / nyq_freq
    sos = butter(order,
                 normal_cutoff,
                 analog=False,
                 btype='lowpass',
                 output='sos')
    return sos