def test_issue_1838(): """ MFE for issue #1838. """ space_order = 4 grid = Grid(shape=(4, 4, 4)) f = Function(name='f', grid=grid, space_order=space_order) b = Function(name='b', grid=grid, space_order=space_order) p0 = TimeFunction(name='p0', grid=grid, space_order=space_order) p1 = TimeFunction(name='p0', grid=grid, space_order=space_order) f.data[:] = 2.1 b.data[:] = 1.3 p0.data[:, 2, 2, 2] = .3 p1.data[:, 2, 2, 2] = .3 eq = Eq(p0.forward, (sin(b) * p0.dx).dx + (sin(b) * p0.dx).dy + (sin(b) * p0.dx).dz + p0) op0 = Operator(eq) op1 = Operator(eq, opt=('advanced', {'linearize': True})) op0.apply(time_M=3, dt=1.) op1.apply(time_M=3, dt=1., p0=p1) # Check generated code assert "r4L0(x, y, z) r4[(x)*y_stride2 + (y)*z_stride1 + (z)]" in str(op1) assert np.allclose(p0.data, p1.data, rtol=1e-6)
def test_fd_adjoint(self, so, ndim, derivative, adjoint_name): grid = Grid(shape=tuple([51] * ndim), extent=tuple([25] * ndim)) x = grid.dimensions[0] f = Function(name='f', grid=grid, space_order=so) f_deriv = Function(name='f_deriv', grid=grid, space_order=so) g = Function(name='g', grid=grid, space_order=so) g_deriv = Function(name='g_deriv', grid=grid, space_order=so) # Fill f and g with smooth cos/sin Operator( [Eq(g, x * cos(2 * np.pi * x / 5)), Eq(f, sin(2 * np.pi * x / 8))]).apply() # Check symbolic expression are expected ones for the adjoint .T deriv = getattr(f, derivative) coeff = 1 if derivative == 'dx2' else -1 expected = coeff * getattr(f, derivative).evaluate.subs( {x.spacing: -x.spacing}) assert simplify(deriv.T.evaluate) == simplify(bypass_uneval(expected)) # Compute numerical derivatives and verify dot test # i.e <f.dx, g> = <f, g.dx.T> eq_f = Eq(f_deriv, deriv) eq_g = Eq(g_deriv, getattr(g, derivative).T) op = Operator([eq_f, eq_g]) op() a = np.dot(f_deriv.data.reshape(-1), g.data.reshape(-1)) b = np.dot(g_deriv.data.reshape(-1), f.data.reshape(-1)) assert np.isclose(1 - a / b, 0, atol=1e-5)
def test_find_symbols_with_duplicates(): grid = Grid(shape=(4, 4)) x, y = grid.dimensions f = TimeFunction(name='f', grid=grid) g = Function(name='g', grid=grid) eq = Eq(f.forward, sin(g) * f + g) op = Operator(eq) exprs = FindNodes(Expression).visit(op) assert len(exprs) == 2 # Two syntactically identical indexeds r0[x, y], but they're different # objects because they are constructed in two different places during # the CIRE pass r0a = exprs[0].output r0b = exprs[1].expr.rhs.args[0].args[0] assert r0a.function is r0b.function assert r0a.name == r0b.name == "r0" assert hash(r0a) == hash(r0b) assert r0a is not r0b # So we expect FindSymbols to catch five Indexeds in total symbols = FindSymbols('indexeds').visit(op) assert len(symbols) == 5
def test_unsubstituted_indexeds(): """ This issue emerged in the context of PR #1828, after the introduction of Uxreplace to substitute Indexeds with FIndexeds. Basically what happened was that `FindSymbols('indexeds')` was missing syntactically identical objects that however look the same. For example, as in this test, we end up with two `r0[x, y, z]`, but the former's `x` and `y` are SpaceDimensions, while the latter's are BlockDimensions. This means that the two objects, while looking identical, are different, and in partical they hash differently, hence we need two entries in a mapper to perform an Uxreplace. But FindSymbols made us detect only one entry... """ grid = Grid(shape=(8, 8, 8)) f = Function(name='f', grid=grid) p = TimeFunction(name='p', grid=grid) p1 = TimeFunction(name='p', grid=grid) f.data[:] = 0.12 p.data[:] = 1. p1.data[:] = 1. eq = Eq(p.forward, sin(f) * p * f) op0 = Operator(eq) op1 = Operator(eq, opt=('advanced', {'linearize': True})) # NOTE: Eventually we compare the numerical output, but truly the most # import check is implicit to op1.apply, and it's the fact that op1 # actually jit-compiles successfully, meaning that all substitutions # were performed correctly op0.apply(time_M=2) op1.apply(time_M=2, p=p1) assert np.allclose(p.data, p1.data, rtol=1e-7)
def initialize_damp(damp, padsizes, spacing, abc_type="damp", fs=False): """ Initialize damping field with an absorbing boundary layer. Parameters ---------- damp : Function The damping field for absorbing boundary condition. nbl : int Number of points in the damping layer. spacing : Grid spacing coefficient. mask : bool, optional whether the dampening is a mask or layer. mask => 1 inside the domain and decreases in the layer not mask => 0 inside the domain and increase in the layer """ eqs = [Eq(damp, 1.0 if abc_type == "mask" else 0.0)] for (nbl, nbr), d in zip(padsizes, damp.dimensions): if not fs or d is not damp.dimensions[-1]: dampcoeff = 1.5 * np.log(1.0 / 0.001) / (nbl) # left dim_l = SubDimension.left(name='abc_%s_l' % d.name, parent=d, thickness=nbl) pos = Abs((nbl - (dim_l - d.symbolic_min) + 1) / float(nbl)) val = dampcoeff * (pos - sin(2 * np.pi * pos) / (2 * np.pi)) val = -val if abc_type == "mask" else val eqs += [Inc(damp.subs({d: dim_l}), val / d.spacing)] # right dampcoeff = 1.5 * np.log(1.0 / 0.001) / (nbr) dim_r = SubDimension.right(name='abc_%s_r' % d.name, parent=d, thickness=nbr) pos = Abs((nbr - (d.symbolic_max - dim_r) + 1) / float(nbr)) val = dampcoeff * (pos - sin(2 * np.pi * pos) / (2 * np.pi)) val = -val if abc_type == "mask" else val eqs += [Inc(damp.subs({d: dim_r}), val / d.spacing)] Operator(eqs, name='initdamp')()
def trig_func(model): """ Trigonometric function of the tilt and azymuth angles. """ try: theta = model.theta except AttributeError: theta = 0 costheta = cos(theta) sintheta = sin(theta) if model.dim == 3: try: phi = model.phi except AttributeError: phi = 0 cosphi = cos(phi) sinphi = sin(phi) return costheta, sintheta, cosphi, sinphi return costheta, sintheta