def solver(I, w, dt, T): """ Solve u'=v, v' = - w**2*u for t in (0,T], u(0)=I and v(0)=0, by an Euler-Cromer method. """ dt = float(dt) Nt = int(round(T/dt)) t = Dimension('t', spacing=Constant('h_t')) v = TimeFunction(name='v', dimensions=(t,), shape=(Nt+1,), space_order=2) u = TimeFunction(name='u', dimensions=(t,), shape=(Nt+1,), space_order=2) v.data[:] = 0 u.data[:] = I eq_v = Eq(v.dt, -(w**2)*u) eq_u = Eq(u.dt, v.forward) stencil_v = solve(eq_v, v.forward) stencil_u = solve(eq_u, u.forward) update_v = Eq(v.forward, stencil_v) update_u = Eq(u.forward, stencil_u) op = Operator([update_v, update_u]) op.apply(h_t=dt, t_M=Nt-1) return u.data, v.data, np.linspace(0, Nt*dt, Nt+1)
def solver_v1(I, w, dt, T): """ Solve u'=v, v' = - w**2*u for t in (0,T], u(0)=I and v(0)=0, by a central finite difference method with time step dt. """ dt = float(dt) Nt = int(round(T/dt)) t = Dimension('t', spacing=Constant('h_t')) u = TimeFunction(name='u', dimensions=(t,), shape=(Nt+1,), space_order=2) v = TimeFunction(name='v', dimensions=(t,), shape=(Nt+1,), space_order=2) u.data[:] = I v.data[:] = 0 - 0.5*dt*w**2*u.data[:] eq_u = Eq(u.dt, v) eq_v = Eq(v.dt, -(w**2)*u.forward) stencil_u = solve(eq_u, u.forward) stencil_v = solve(eq_v, v.forward) update_u = Eq(u.forward, stencil_u) update_v = Eq(v.forward, stencil_v) op = Operator([update_u, update_v]) op.apply(h_t=dt, t_M=Nt-1) t_mesh = np.linspace(0, Nt*dt, Nt+1) # mesh for u t_v_mesh = (t_mesh + dt/2)[:-1] # mesh for v return u.data, t_mesh, v.data, t_v_mesh
def test_solve(self, operate_on_empty_cache): """ Test to ensure clear_cache wipes out *all of* sympy caches. ``sympy.solve``, in particular, relies on a series of private caches that must be purged too (calling sympy's clear_cache() API function isn't enough). """ grid = Grid(shape=(4, )) u = TimeFunction(name='u', grid=grid, time_order=1, space_order=2) eqn = Eq(u.dt, u.dx2) solve(eqn, u.forward) del u del eqn del grid # `u` points to the various Dimensions, the Dimensions point to the various # spacing symbols, hence, we need three sweeps to clear up the cache assert len(_SymbolCache) == 12 clear_cache() assert len(_SymbolCache) == 8 clear_cache() assert len(_SymbolCache) == 2 clear_cache() assert len(_SymbolCache) == 0
def test_solve(self, operate_on_empty_cache): """ Test to ensure clear_cache wipes out *all of* sympy caches. ``sympy.solve``, in particular, relies on a series of private caches that must be purged too (calling sympy's clear_cache() API function isn't enough). """ grid = Grid(shape=(4,)) u = TimeFunction(name='u', grid=grid, time_order=1, space_order=2) eqn = Eq(u.dt, u.dx2) solve(eqn, u.forward) del u del eqn del grid # We only deleted `u`, however we also cache shifted version created by the # finite difference (u.dt, u.dx2). In this case we have three extra references # to u(t + dt), u(x - h_x) and u(x + h_x) that have to be cleared. # Then `u` points to the various Dimensions, the Dimensions point to the various # spacing symbols, hence, we need four sweeps to clear up the cache. assert len(_SymbolCache) == 16 clear_cache() assert len(_SymbolCache) == 9 clear_cache() assert len(_SymbolCache) == 3 clear_cache() assert len(_SymbolCache) == 1 clear_cache() assert len(_SymbolCache) == 0
def solver_FECS(I, U0, v, L, dt, C, T, user_action=None): Nt = int(round(T / float(dt))) t = np.linspace(0, Nt * dt, Nt + 1) # Mesh points in time dx = v * dt / C Nx = int(round(L / dx)) x = np.linspace(0, L, Nx + 1) # Mesh points in space # Make sure dx and dt are compatible with x and t dx = float(x[1] - x[0]) dt = float(t[1] - t[0]) C = v * dt / dx grid = Grid(shape=(Nx + 1, ), extent=(L, )) t_s = grid.time_dim u = TimeFunction(name='u', grid=grid, space_order=2, save=Nt + 1) pde = u.dtr + v * u.dxc stencil = solve(pde, u.forward) eq = Eq(u.forward, stencil) # Set initial condition u(x,0) = I(x) u.data[1, :] = [I(xi) for xi in x] # Insert boundary condition bc = [Eq(u[t_s + 1, 0], U0)] op = Operator([eq] + bc) op.apply(dt=dt, x_m=1, x_M=Nx - 1) if user_action is not None: for n in range(0, Nt + 1): user_action(u.data[n], x, t, n)
def iso_stencil(field, m, s, damp, kernel, **kwargs): """ Stencil for the acoustic isotropic wave-equation: u.dt2 - H + damp*u.dt = 0 :param field: Symbolic TimeFunction object, solution to be computed :param m: square slowness :param s: symbol for the time-step :param damp: ABC dampening field (Function) :param kwargs: forwad/backward wave equation (sign of u.dt will change accordingly as well as the updated time-step (u.forwad or u.backward) :return: Stencil for the wave-equation """ # Creat a temporary symbol for H to avoid expensive sympy solve H = Symbol('H') # Define time sep to be updated next = field.forward if kwargs.get('forward', True) else field.backward # Define PDE eq = m * field.dt2 - H - kwargs.get('q', 0) # Add dampening field according to the propagation direction eq += damp * field.dt if kwargs.get('forward', True) else -damp * field.dt # Solve the symbolic equation for the field to be updated eq_time = solve(eq, next) # Get the spacial FD lap = laplacian(field, m, s, kernel) # return the Stencil with H replaced by its symbolic expression return [Eq(next, eq_time.subs({H: lap}))]
def iso_stencil(field, m, s, damp, kernel, **kwargs): """ Stencil for the acoustic isotropic wave-equation: u.dt2 - H + damp*u.dt = 0. Parameters ---------- field : TimeFunction The computed solution. m : Function or float Square slowness. s : float or Scalar The time dimension spacing. damp : Function The damping field for absorbing boundary condition. forward : bool The propagation direction. Defaults to True. q : TimeFunction, Function or float Full-space/time source of the wave-equation. """ # Creat a temporary symbol for H to avoid expensive sympy solve H = Symbol('H') # Define time sep to be updated next = field.forward if kwargs.get('forward', True) else field.backward # Define PDE eq = m * field.dt2 - H - kwargs.get('q', 0) # Add dampening field according to the propagation direction eq += damp * field.dt if kwargs.get('forward', True) else damp * field.dt.T # Solve the symbolic equation for the field to be updated eq_time = solve(eq, next) # Get the spacial FD lap = laplacian(field, m, s, kernel) # return the Stencil with H replaced by its symbolic expression return [Eq(next, eq_time.subs({H: lap}))]
def test_issue_1725(): class ToyPMLLeft(SubDomain): name = 'toypmlleft' def define(self, dimensions): x, y = dimensions return {x: x, y: ('left', 2)} class ToyPMLRight(SubDomain): name = 'toypmlright' def define(self, dimensions): x, y = dimensions return {x: x, y: ('right', 2)} subdomains = [ToyPMLLeft(), ToyPMLRight()] grid = Grid(shape=(20, 20), subdomains=subdomains) u = TimeFunction(name='u', grid=grid, time_order=2, space_order=2) eqns = [ Eq(u.forward, solve(u.dt2 - u.laplace, u.forward), subdomain=sd) for sd in subdomains ] op = Operator(eqns, opt='fission') # Note the `x` loop is fissioned, so now both loop nests can be collapsed # for maximum parallelism assert_structure(op, ['t,x,i1y', 't,x,i2y'], 't,x,i1y,x,i2y')
def execute_devito(ui, spacing=0.01, a=0.5, timesteps=500, space_order=2, dse="advanced"): """Execute diffusion stencil using the devito Operator API.""" nx, ny = ui.shape dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults grid = Grid(shape=(nx, ny)) u = TimeFunction(name='u', grid=grid, time_order=1, space_order=space_order) u.data[0, :] = ui[:] # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil), dse=dse) # Execute the generated Devito stencil operator summary = op.apply(u=u, t=timesteps, dt=dt) return summary.gflopss, summary.oi, summary.timings
def __init__(self, shape, order): grid = Grid(shape=shape) c = 10.0 u = TimeFunction(name='u', grid=grid, spc_order=order) eq = Eq(u.dt + c * u.dxl + c * u.dyl) stencil = solve(eq, u.forward) self.op = Operator([Eq(u.forward, stencil)])
def iso_stencil(field, m, s, damp, kernel, **kwargs): """ Stencil for the acoustic isotropic wave-equation: u.dt2 - H + damp*u.dt = 0 :param field: Symbolic TimeFunction object, solution to be computed :param m: square slowness :param s: symbol for the time-step :param damp: ABC dampening field (Function) :param kwargs: forwad/backward wave equation (sign of u.dt will change accordingly as well as the updated time-step (u.forwad or u.backward) :return: Stencil for the wave-equation """ # Creat a temporary symbol for H to avoid expensive sympy solve H = Symbol('H') # Define time sep to be updated next = field.forward if kwargs.get('forward', True) else field.backward # Define PDE eq = m * field.dt2 - H - kwargs.get('q', 0) # Add dampening field according to the propagation direction eq += damp * field.dt if kwargs.get('forward', True) else -damp * field.dt # Solve the symbolic equation for the field to be updated eq_time = solve(eq, next) # Get the spacial FD lap = laplacian(field, m, s, kernel) # return the Stencil with H replaced by its symbolic expression return [Eq(next, eq_time.subs({H: lap}))]
def test_const_change(self): """ Test that Constand.data can be set as required. """ n = 5 t = Constant(name='t', dtype=np.int32) grid = Grid(shape=(2, 2)) x, y = grid.dimensions f = TimeFunction(name='f', grid=grid, save=n+1) f.data[:] = 0 eq = Eq(f.dt-1) stencil = Eq(f.forward, solve(eq, f.forward)) op = Operator([stencil]) op.apply(time_m=0, time_M=n-1, dt=1) check = Function(name='check', grid=grid) eq_test = Eq(check, f[t, x, y]) op_test = Operator([eq_test]) for j in range(0, n+1): t.data = j # Ensure constant is being updated correctly op_test.apply(t=t) assert(np.amax(check.data[:], axis=None) == j) assert(np.amin(check.data[:], axis=None) == j)
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 ImagingOperator(model, image): # Define the wavefield with the size of the model and the time dimension v = TimeFunction(name='v', grid=model.grid, time_order=2, space_order=4) u = TimeFunction(name='u', grid=model.grid, time_order=2, space_order=4, save=geometry.nt) # Define the wave equation, but with a negated damping term eqn = model.m * v.dt2 - v.laplace - model.damp * v.dt # Use `solve` to rearrange the equation into a stencil expression stencil = Eq(v.backward, solve(eqn, v.backward)) # Define residual injection at the location of the forward receivers dt = model.critical_dt residual = PointSource(name='residual', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) res_term = residual.inject(field=v, expr=residual * dt**2 / model.m) # Correlate u and v for the current time step and add it to the image image_update = Eq(image, image - u * v) return Operator([stencil] + res_term + [image_update], subs=model.spacing_map)
def iso_stencil(field, m, s, damp, kernel, **kwargs): """ Stencil for the acoustic isotropic wave-equation: u.dt2 - H + damp*u.dt = 0. Parameters ---------- field : TimeFunction The computed solution. m : Function or float Square slowness. s : float or Scalar The time dimension spacing. damp : Function The damping field for absorbing boundary condition. forward : bool The propagation direction. Defaults to True. q : TimeFunction, Function or float Full-space/time source of the wave-equation. """ # Forward or backward forward = kwargs.get('forward', True) # Define time step to be updated unext = field.forward if forward else field.backward udt = field.dt if forward else field.dt.T # Get the spacial FD lap = laplacian(field, m, s, kernel) # Get source q = kwargs.get('q', 0) # Define PDE and update rule eq_time = solve(m * field.dt2 - lap - q + damp * udt, unext) # Time update stencil return [Eq(unext, eq_time)]
def test_const_change(self): """ Test that Constand.data can be set as required. """ n = 5 t = Constant(name='t', dtype=np.int32) grid = Grid(shape=(2, 2)) x, y = grid.dimensions f = TimeFunction(name='f', grid=grid, save=n+1) f.data[:] = 0 eq = Eq(f.dt-1) stencil = Eq(f.forward, solve(eq, f.forward)) op = Operator([stencil]) op.apply(time_m=0, time_M=n-1, dt=1) check = Function(name='check', grid=grid) eq_test = Eq(check, f[t, x, y]) op_test = Operator([eq_test]) for j in range(0, n+1): t.data = j # Ensure constant is being updated correctly op_test.apply(t=t) assert(np.amax(check.data[:], axis=None) == j) assert(np.amin(check.data[:], axis=None) == j)
def _new_operator3(shape, blockshape0=None, blockshape1=None, dle=None): blockshape0 = as_tuple(blockshape0) blockshape1 = as_tuple(blockshape1) grid = Grid(shape=shape, dtype=np.float64) spacing = 0.1 a = 0.5 c = 0.5 dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults u = TimeFunction(name='u', grid=grid, time_order=1, space_order=(2, 2, 2)) u.data[0, :] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2) - c * (u.dxl + u.dyl)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil), dle=dle) blocksizes0 = get_blocksizes(op, dle, grid, blockshape0, 0) blocksizes1 = get_blocksizes(op, dle, grid, blockshape1, 1) op.apply(u=u, t=10, dt=dt, **blocksizes0, **blocksizes1) return u.data[1, :], op
def td_born_adjoint_op(model, geometry, time_order, space_order): nt = geometry.nt # Define the wavefields with the size of the model and the time dimension u = TimeFunction( name='u', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) # Define the wave equation pde = model.m * u.dt2 - u.laplace + model.damp * u.dt.T # Use `solve` to rearrange the equation into a stencil expression stencil = Eq(u.backward, solve(pde, u.backward), subdomain=model.grid.subdomains['physdomain']) # Inject at receivers born_data_rec = PointSource( name='born_data_rec', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions ) dt = Constant(name='dt') rec_term = born_data_rec.inject(field=u.backward, expr=born_data_rec * (dt ** 2) / model.m) return Operator([stencil] + rec_term, subs=model.spacing_map)
def test_function_coefficients(self): """Test that custom function coefficients return the expected result""" so = 2 grid = Grid(shape=(4, 4)) f0 = TimeFunction(name='f0', grid=grid, space_order=so, coefficients='symbolic') f1 = TimeFunction(name='f1', grid=grid, space_order=so) x, y = grid.dimensions s = Dimension(name='s') ncoeffs = so + 1 wshape = list(grid.shape) wshape.append(ncoeffs) wshape = as_tuple(wshape) wdims = list(grid.dimensions) wdims.append(s) wdims = as_tuple(wdims) w = Function(name='w', dimensions=wdims, shape=wshape) w.data[:, :, 0] = 0.0 w.data[:, :, 1] = -1.0 / grid.spacing[0] w.data[:, :, 2] = 1.0 / grid.spacing[0] f_x_coeffs = Coefficient(1, f0, x, w) subs = Substitutions(f_x_coeffs) eq0 = Eq(f0.dt + f0.dx, 1, coefficients=subs) eq1 = Eq(f1.dt + f1.dx, 1) stencil0 = solve(eq0.evaluate, f0.forward) stencil1 = solve(eq1.evaluate, f1.forward) op0 = Operator(Eq(f0.forward, stencil0)) op1 = Operator(Eq(f1.forward, stencil1)) op0(time_m=0, time_M=5, dt=1.0) op1(time_m=0, time_M=5, dt=1.0) assert np.all( np.isclose(f0.data[:] - f1.data[:], 0.0, atol=1e-5, rtol=0))
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
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)
def diffusion_stencil(): """Create stencil and substitutions for the diffusion equation""" p = sympy.Function('p') x, y, t, h, s = sympy.symbols('x y t h s') dx2 = p(x, y, t).diff(x, x).as_finite_difference([x - h, x, x + h]) dy2 = p(x, y, t).diff(y, y).as_finite_difference([y - h, y, y + h]) dt = p(x, y, t).diff(t).as_finite_difference([t, t + s]) eqn = Eq(dt, a * (dx2 + dy2)) stencil = solve(eqn, p(x, y, t + s)) return stencil, (p(x, y, t), p(x + h, y, t), p(x - h, y, t), p(x, y + h, t), p(x, y - h, t), s, h)
def second_order_stencil(model, u, v, H0, Hz, forward=True): """ Creates the stencil corresponding to the second order TTI wave equation m * u.dt2 = (epsilon * H0 + delta * Hz) - damp * u.dt m * v.dt2 = (delta * H0 + Hz) - damp * v.dt """ m, damp = model.m, model.damp unext = u.forward if forward else u.backward vnext = v.forward if forward else v.backward udt = u.dt if forward else u.dt.T vdt = v.dt if forward else v.dt.T # Stencils stencilp = solve(m * u.dt2 - H0 + damp * udt, unext) stencilr = solve(m * v.dt2 - Hz + damp * vdt, vnext) first_stencil = Eq(unext, stencilp) second_stencil = Eq(vnext, stencilr) stencils = [first_stencil, second_stencil] return stencils
def run_simulation(save=False, dx=0.01, dy=0.01, a=0.5, timesteps=100): nx, ny = int(1 / dx), int(1 / dy) dx2, dy2 = dx**2, dy**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) grid = Grid(shape=(nx, ny)) u = TimeFunction(name='u', grid=grid, save=timesteps if save else None, initializer=initializer, time_order=1, space_order=2) eqn = Eq(u.dt, a * (u.dx2 + u.dy2)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil)) op.apply(time=timesteps-2, dt=dt) return u.data[timesteps - 1]
def run_simulation(save=False, dx=0.01, dy=0.01, a=0.5, timesteps=100): nx, ny = int(1 / dx), int(1 / dy) dx2, dy2 = dx**2, dy**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) grid = Grid(shape=(nx, ny)) u = TimeFunction(name='u', grid=grid, save=timesteps if save else None, initializer=initializer, time_order=1, space_order=2) eqn = Eq(u.dt, a * (u.dx2 + u.dy2)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil)) op.apply(time=timesteps-2, dt=dt) return u.data[timesteps - 1]
def test_solve(self): """ By remaining unevaluated until after Operator's collect_derivatives, the Derivatives after a solve() should be collected. """ grid = Grid(shape=(10, 10)) u = TimeFunction(name="u", grid=grid, space_order=4, time_order=2) pde = u.dt2 - (u.dx.dx + u.dy.dy) - u.dx.dy eq = Eq(u.forward, solve(pde, u.forward)) leq = collect_derivatives.func([eq])[0] assert len(eq.rhs.find(Derivative)) == 5 assert len(leq.rhs.find(Derivative)) == 4 assert len( leq.rhs.args[2].find(Derivative)) == 3 # Check factorization
def test_subdomainset_mpi(self): n_domains = 5 class Inner(SubDomainSet): name = 'inner' bounds_xm = np.zeros((n_domains, ), dtype=np.int32) bounds_xM = np.zeros((n_domains, ), dtype=np.int32) bounds_ym = np.zeros((n_domains, ), dtype=np.int32) bounds_yM = np.zeros((n_domains, ), dtype=np.int32) for j in range(0, n_domains): bounds_xm[j] = j bounds_xM[j] = j bounds_ym[j] = j bounds_yM[j] = 2 * n_domains - 1 - j bounds = (bounds_xm, bounds_xM, bounds_ym, bounds_yM) inner_sd = Inner(N=n_domains, bounds=bounds) grid = Grid(extent=(10, 10), shape=(10, 10), subdomains=(inner_sd, )) assert (grid.subdomains['inner'].shape == ((10, 1), (8, 1), (6, 1), (4, 1), (2, 1))) f = TimeFunction(name='f', grid=grid, dtype=np.int32) f.data[:] = 0 stencil = Eq(f.forward, solve(Eq(f.dt, 1), f.forward), subdomain=grid.subdomains['inner']) op = Operator(stencil) op(time_m=0, time_M=9, dt=1) result = f.data[0] fex = Function(name='fex', grid=grid) expected = np.zeros((10, 10), dtype=np.int32) for j in range(0, n_domains): expected[j, j:10 - j] = 10 fex.data[:] = np.transpose(expected) assert ((np.array(result) == np.array(fex.data[:])).all())
def test_iterate_NDomains(self): """ Test that a set of subdomains are iterated upon correctly. """ n_domains = 10 class Inner(SubDomainSet): name = 'inner' bounds_xm = np.zeros((n_domains, ), dtype=np.int32) bounds_xM = np.zeros((n_domains, ), dtype=np.int32) bounds_ym = np.zeros((n_domains, ), dtype=np.int32) bounds_yM = np.zeros((n_domains, ), dtype=np.int32) for j in range(0, n_domains): bounds_xm[j] = j bounds_xM[j] = n_domains - 1 - j bounds_ym[j] = floor(j / 2) bounds_yM[j] = floor(j / 2) bounds = (bounds_xm, bounds_xM, bounds_ym, bounds_yM) inner_sd = Inner(N=n_domains, bounds=bounds) grid = Grid(extent=(10, 10), shape=(10, 10), subdomains=(inner_sd, )) f = TimeFunction(name='f', grid=grid, dtype=np.int32) f.data[:] = 0 stencil = Eq(f.forward, solve(Eq(f.dt, 1), f.forward), subdomain=grid.subdomains['inner']) op = Operator(stencil) op(time_m=0, time_M=9, dt=1) result = f.data[0] expected = np.zeros((10, 10), dtype=np.int32) for j in range(0, n_domains): expected[j, bounds_ym[j]:n_domains - bounds_yM[j]] = 10 assert ((np.array(result) == expected).all())
def td_born_forward_op(model, geometry, time_order, space_order): nt = geometry.nt # Define the wavefields with the size of the model and the time dimension u0 = TimeFunction( name='u0', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) u = TimeFunction( name='u', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) dm = TimeFunction( name='dm', grid=model.grid, time_order=time_order, space_order=space_order, save=nt ) # Define the wave equation pde = model.m * u.dt2 - u.laplace + model.damp * u.dt - dm * u0 # Use `solve` to rearrange the equation into a stencil expression stencil = Eq(u.forward, solve(pde, u.forward), subdomain=model.grid.subdomains['physdomain']) # Sample at receivers born_data_rec = PointSource( name='born_data_rec', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions ) rec_term = born_data_rec.interpolate(expr=u) return Operator([stencil] + rec_term, subs=model.spacing_map)
def solver(I, w, dt, T): """ Solve u'' + w**2*u = 0 for t in (0,T], u(0)=I and u'(0)=0, by a central finite difference method with time step dt. """ dt = float(dt) Nt = int(round(T / dt)) t = Dimension('t', spacing=Constant('h_t')) u = TimeFunction(name='u', dimensions=(t, ), shape=(Nt + 1, ), space_order=2) u.data[:] = I eqn = u.dt2 + (w**2) * u stencil = Eq(u.forward, solve(eqn, u.forward)) op = Operator(stencil) op.apply(h_t=dt, t_M=Nt - 1) return u.data, np.linspace(0, Nt * dt, Nt + 1)
def _new_operator3(shape, **kwargs): grid = Grid(shape=shape) spacing = 0.1 a = 0.5 c = 0.5 dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults u = TimeFunction(name='u', grid=grid, time_order=1, space_order=(2, 2, 2)) u.data[0, :] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2) - c * (u.dxl + u.dyl)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil), **kwargs) # Execute the generated Devito stencil operator op.apply(u=u, t=10, dt=dt) return u.data[1, :], op
def _new_operator3(shape, blockshape=None, dle=None): blockshape = as_tuple(blockshape) grid = Grid(shape=shape) spacing = 0.1 a = 0.5 c = 0.5 dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults u = TimeFunction(name='u', grid=grid, time_order=1, space_order=(2, 2, 2)) u.data[0, :] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2) - c * (u.dxl + u.dyl)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil), dle=dle) blocksizes = get_blocksizes(op, dle, grid, blockshape) op.apply(u=u, t=10, dt=dt, **blocksizes) return u.data[1, :], op
def execute_devito(ui, spacing=0.01, a=0.5, timesteps=500): """Execute diffusion stencil using the devito Operator API.""" nx, ny = ui.shape dx2, dy2 = spacing**2, spacing**2 dt = dx2 * dy2 / (2 * a * (dx2 + dy2)) # Allocate the grid and set initial condition # Note: This should be made simpler through the use of defaults grid = Grid(shape=(nx, ny)) u = TimeFunction(name='u', grid=grid, time_order=1, space_order=2) u.data[0, :] = ui[:] # Derive the stencil according to devito conventions eqn = Eq(u.dt, a * (u.dx2 + u.dy2)) stencil = solve(eqn, u.forward) op = Operator(Eq(u.forward, stencil)) # Execute the generated Devito stencil operator tstart = time.time() op.apply(u=u, t=timesteps, dt=dt) runtime = time.time() - tstart log("Devito: Diffusion with dx=%0.4f, dy=%0.4f, executed %d timesteps in %f seconds" % (spacing, spacing, timesteps, runtime)) return u.data[1, :], runtime
def iso_stencil(field, model, kernel, **kwargs): """ Stencil for the acoustic isotropic wave-equation: u.dt2 - H + damp*u.dt = 0. Parameters ---------- field : TimeFunction The computed solution. model : Model Physical model. kernel : str, optional Type of discretization, 'OT2' or 'OT4'. q : TimeFunction, Function or float Full-space/time source of the wave-equation. forward : bool, optional Whether to propagate forward (True) or backward (False) in time. """ # Forward or backward forward = kwargs.get('forward', True) # Define time step to be updated unext = field.forward if forward else field.backward udt = field.dt if forward else field.dt.T # Get the spacial FD lap = laplacian(field, model, kernel) # Get source q = kwargs.get('q', 0) # Define PDE and update rule eq_time = solve(model.m * field.dt2 - lap - q + model.damp * udt, unext) # Time-stepping stencil. eqns = [Eq(unext, eq_time, subdomain=model.grid.subdomains['physdomain'])] # Add free surface if model.fs: eqns.append(freesurface(model, Eq(unext, eq_time))) return eqns