def test_subdimmiddle_notparallel(self): """ Tests application of an Operator consisting of a subdimension defined over different sub-regions, explicitly created through the use of SubDimensions. Different from ``test_subdimmiddle_parallel`` because an interior dimension cannot be evaluated in parallel. """ grid = Grid(shape=(20, 20)) x, y = grid.dimensions t = grid.stepping_dim thickness = 4 u = TimeFunction(name='u', save=None, grid=grid, space_order=0, time_order=1) xi = SubDimension.middle(name='xi', parent=x, thickness_left=thickness, thickness_right=thickness) yi = SubDimension.middle(name='yi', parent=y, thickness_left=thickness, thickness_right=thickness) # flow dependencies in x and y which should force serial execution # in reverse direction centre = Eq(u[t + 1, xi, yi], u[t, xi, yi] + u[t + 1, xi + 1, yi + 1]) u.data[0, 10, 10] = 1.0 op = Operator([centre]) iterations = FindNodes(Iteration).visit(op) assert all(i.is_Affine and i.is_Sequential for i in iterations if i.dim == xi) assert all(i.is_Affine and i.is_Parallel for i in iterations if i.dim == yi) op.apply(time_m=0, time_M=0) for i in range(4, 11): assert u.data[1, i, i] == 1.0 u.data[1, i, i] = 0.0 assert np.all(u.data[1, :] == 0)
def test_bcs(self): """ Tests application of an Operator consisting of multiple equations defined over different sub-regions, explicitly created through the use of :class:`SubDimension`s. """ grid = Grid(shape=(20, 20)) x, y = grid.dimensions t = grid.stepping_dim thickness = 4 u = TimeFunction(name='u', save=None, grid=grid, space_order=0, time_order=1) xleft = SubDimension.left(name='xleft', parent=x, thickness=thickness) xi = SubDimension.middle(name='xi', parent=x, thickness_left=thickness, thickness_right=thickness) xright = SubDimension.right(name='xright', parent=x, thickness=thickness) yi = SubDimension.middle(name='yi', parent=y, thickness_left=thickness, thickness_right=thickness) t_in_centre = Eq(u[t + 1, xi, yi], 1) leftbc = Eq(u[t + 1, xleft, yi], u[t + 1, xleft + 1, yi] + 1) rightbc = Eq(u[t + 1, xright, yi], u[t + 1, xright - 1, yi] + 1) op = Operator([t_in_centre, leftbc, rightbc]) op.apply(time_m=1, time_M=1) assert np.all(u.data[0, :, 0:thickness] == 0.) assert np.all(u.data[0, :, -thickness:] == 0.) assert all( np.all(u.data[0, i, thickness:-thickness] == (thickness + 1 - i)) for i in range(thickness)) assert all( np.all(u.data[0, -i, thickness:-thickness] == (thickness + 2 - i)) for i in range(1, thickness + 1)) assert np.all(u.data[0, thickness:-thickness, thickness:-thickness] == 1.)
def test_subdimmiddle_parallel(self): """ Tests application of an Operator consisting of a subdimension defined over different sub-regions, explicitly created through the use of :class:`SubDimension`s. """ grid = Grid(shape=(20, 20)) x, y = grid.dimensions t = grid.stepping_dim thickness = 4 u = TimeFunction(name='u', save=None, grid=grid, space_order=0, time_order=1) xi = SubDimension.middle(name='xi', parent=x, thickness_left=thickness, thickness_right=thickness) yi = SubDimension.middle(name='yi', parent=y, thickness_left=thickness, thickness_right=thickness) # a 5 point stencil that can be computed in parallel centre = Eq( u[t + 1, xi, yi], u[t, xi, yi] + u[t, xi - 1, yi] + u[t, xi + 1, yi] + u[t, xi, yi - 1] + u[t, xi, yi + 1]) u.data[0, 10, 10] = 1.0 op = Operator([centre]) iterations = FindNodes(Iteration).visit(op) assert all(i.is_Affine and i.is_Parallel for i in iterations if i.dim in [xi, yi]) op.apply(time_m=0, time_M=0) assert np.all(u.data[1, 9:12, 10] == 1.0) assert np.all(u.data[1, 10, 9:12] == 1.0) # Other than those, it should all be 0 u.data[1, 9:12, 10] = 0.0 u.data[1, 10, 9:12] = 0.0 assert np.all(u.data[1, :] == 0)
def test_subdimleft_parallel(self): """ Tests application of an Operator consisting of a subdimension defined over different sub-regions, explicitly created through the use of :class:`SubDimension`s. This tests that flow direction is not being automatically inferred from whether the subdimension is on the left or right boundary. """ grid = Grid(shape=(20, 20)) x, y = grid.dimensions t = grid.stepping_dim thickness = 4 u = TimeFunction(name='u', save=None, grid=grid, space_order=0, time_order=1) xl = SubDimension.left(name='xl', parent=x, thickness=thickness) yi = SubDimension.middle(name='yi', parent=y, thickness_left=thickness, thickness_right=thickness) # Can be done in parallel eq = Eq(u[t+1, xl, yi], u[t, xl, yi] + 1) op = Operator([eq]) iterations = FindNodes(Iteration).visit(op) assert all(i.is_Affine and i.is_Parallel for i in iterations if i.dim in [xl, yi]) op.apply(time_m=0, time_M=0) assert np.all(u.data[1, 0:thickness, 0:thickness] == 0) assert np.all(u.data[1, 0:thickness, -thickness:] == 0) assert np.all(u.data[1, 0:thickness, thickness:-thickness] == 1) assert np.all(u.data[1, thickness+1:, :] == 0)
def test_sub_dimension(): di = SubDimension.middle('di', Dimension(name='d'), 1, 1) pkl_di = pickle.dumps(di) new_di = pickle.loads(pkl_di) assert di.name == new_di.name assert di.dtype == new_di.dtype assert di.parent == new_di.parent assert di._thickness == new_di._thickness assert di._interval == new_di._interval
def test_everything(): nt = 50 grid = Grid(shape=(6, 6)) x, y = grid.dimensions time = grid.time_dim xi = SubDimension.middle(name='xi', parent=x, thickness_left=2, thickness_right=2) yi = SubDimension.middle(name='yi', parent=y, thickness_left=2, thickness_right=2) factor = Constant(name='factor', value=5, dtype=np.int32) t_sub = ConditionalDimension('t_sub', parent=time, factor=factor) save_shift = Constant(name='save_shift', dtype=np.int32) u = TimeFunction(name='u', grid=grid, time_order=0) u1 = TimeFunction(name='u', grid=grid, time_order=0) va = TimeFunction(name='va', grid=grid, time_order=0, save=(int(nt//factor.data)), time_dim=t_sub) vb = TimeFunction(name='vb', grid=grid, time_order=0, save=(int(nt//factor.data)), time_dim=t_sub) for i in range(va.save): va.data[i, :] = i vb.data[i, :] = i*2 - 1 vas = va.subs(t_sub, t_sub - save_shift) vasb = va.subs(t_sub, t_sub - 1 - save_shift) vasf = va.subs(t_sub, t_sub + 1 - save_shift) eqns = [Eq(u.forward, u + (vasb + vas + vasf)*2. + vb)] eqns = [e.xreplace({x: xi, y: yi}) for e in eqns] op0 = Operator(eqns, opt='noop') op1 = Operator(eqns, opt='buffering') # Check generated code assert len([i for i in FindSymbols().visit(op1) if i.is_Array]) == 2 op0.apply(time_m=15, time_M=35, save_shift=0) op1.apply(time_m=15, time_M=35, save_shift=0, u=u1) assert np.all(u.data == u1.data)
def test_sub_dimension(self): """ Test that SubDimensions with same name but different attributes do not alias to the same SubDimension. Conversely, if the name and the attributes are the same, they must alias to the same SubDimension. """ x = Dimension('x') xi0 = SubDimension.middle('xi', x, 1, 1) xi1 = SubDimension.middle('xi', x, 1, 1) assert xi0 is xi1 xl0 = SubDimension.left('xl', x, 2) xl1 = SubDimension.left('xl', x, 2) assert xl0 is xl1 xl2asxi = SubDimension.left('xi', x, 2) assert xl2asxi is not xl1 assert xl2asxi is not xi1 xr0 = SubDimension.right('xr', x, 1) xr1 = SubDimension.right('xr', x, 1) assert xr0 is xr1
def test_sub_dimension_cache(): """ Test that SubDimensions with same name but different attributes do not alias to the same SubDimension. Conversely, if the name and the attributes are the same, they must alias to the same SubDimension. """ x = Dimension('x') xi0 = SubDimension.middle('xi', x, 1, 1) xi1 = SubDimension.middle('xi', x, 1, 1) assert xi0 is xi1 xl0 = SubDimension.left('xl', x, 2) xl1 = SubDimension.left('xl', x, 2) assert xl0 is xl1 xl2asxi = SubDimension.left('xi', x, 2) assert xl2asxi is not xl1 assert xl2asxi is not xi1 xr0 = SubDimension.right('xr', x, 1) xr1 = SubDimension.right('xr', x, 1) assert xr0 is xr1
def test_save_w_subdims(self): nt = 10 grid = Grid(shape=(10, 10)) x, y = grid.dimensions time_dim = grid.time_dim xi = SubDimension.middle(name='xi', parent=x, thickness_left=3, thickness_right=3) yi = SubDimension.middle(name='yi', parent=y, thickness_left=3, thickness_right=3) factor = Constant(name='factor', value=2, dtype=np.int32) time_sub = ConditionalDimension(name="time_sub", parent=time_dim, factor=factor) u = TimeFunction(name='u', grid=grid) usave = TimeFunction(name='usave', grid=grid, time_order=0, save=int(nt // factor.data), time_dim=time_sub) eqns = [Eq(u.forward, u + 1), Eq(usave, u.forward)] eqns = [e.xreplace({x: xi, y: yi}) for e in eqns] op = Operator(eqns, opt=('buffering', 'tasking', 'orchestrate')) op.apply(time_M=nt - 1) for i in range(usave.save): assert np.all(usave.data[i, 3:-3, 3:-3] == 2 * i + 1) assert np.all(usave.data[i, :3, :] == 0) assert np.all(usave.data[i, -3:, :] == 0) assert np.all(usave.data[i, :, :3] == 0) assert np.all(usave.data[i, :, -3:] == 0)
def test_expandingbox_like(self): """ Make sure SubDimensions aren't an obstacle to expanding boxes. """ grid = Grid(shape=(8, 8)) x, y = grid.dimensions u = TimeFunction(name='u', grid=grid) xi = SubDimension.middle(name='xi', parent=x, thickness_left=2, thickness_right=2) yi = SubDimension.middle(name='yi', parent=y, thickness_left=2, thickness_right=2) eqn = Eq(u.forward, u + 1) eqn = eqn.subs({x: xi, y: yi}) op = Operator(eqn) op.apply(time=3, x_m=2, x_M=5, y_m=2, y_M=5, xi_ltkn=0, xi_rtkn=0, yi_ltkn=0, yi_rtkn=0) assert np.all(u.data[0, 2:-2, 2:-2] == 4.) assert np.all(u.data[1, 2:-2, 2:-2] == 3.) assert np.all(u.data[:, :2] == 0.) assert np.all(u.data[:, -2:] == 0.) assert np.all(u.data[:, :, :2] == 0.) assert np.all(u.data[:, :, -2:] == 0.)
def test_subdimensions(self): nt = 10 grid = Grid(shape=(10, 10, 10)) x, y, z = grid.dimensions xi = SubDimension.middle(name='xi', parent=x, thickness_left=2, thickness_right=2) yi = SubDimension.middle(name='yi', parent=y, thickness_left=2, thickness_right=2) zi = SubDimension.middle(name='zi', parent=z, thickness_left=2, thickness_right=2) u = TimeFunction(name='u', grid=grid, save=nt) u1 = TimeFunction(name='u', grid=grid, save=nt) eqn = Eq(u.forward, u + 1).xreplace({x: xi, y: yi, z: zi}) op0 = Operator(eqn, opt='noop') op1 = Operator(eqn, opt='buffering') # Check generated code assert len(retrieve_iteration_tree(op1)) == 2 assert len([i for i in FindSymbols().visit(op1) if i.is_Array]) == 1 op0.apply(time_M=nt-2) op1.apply(time_M=nt-2, u=u1) assert np.all(u.data == u1.data)
def test_symbolic_size(self): """Check the symbolic size of all possible SubDimensions is as expected.""" grid = Grid(shape=(4,)) x, = grid.dimensions thickness = 4 xleft = SubDimension.left(name='xleft', parent=x, thickness=thickness) assert xleft.symbolic_size == xleft.thickness.left[0] xi = SubDimension.middle(name='xi', parent=x, thickness_left=thickness, thickness_right=thickness) assert xi.symbolic_size == (x.symbolic_max - x.symbolic_min - xi.thickness.left[0] - xi.thickness.right[0] + 1) xright = SubDimension.right(name='xright', parent=x, thickness=thickness) assert xright.symbolic_size == xright.thickness.right[0]
def test_subdimleft_notparallel(self): """ Tests application of an Operator consisting of a subdimension defined over different sub-regions, explicitly created through the use of :class:`SubDimension`s. This tests that flow direction is not being automatically inferred from whether the subdimension is on the left or right boundary. """ grid = Grid(shape=(20, 20)) x, y = grid.dimensions t = grid.stepping_dim thickness = 4 u = TimeFunction(name='u', save=None, grid=grid, space_order=1, time_order=0) xl = SubDimension.left(name='xl', parent=x, thickness=thickness) yi = SubDimension.middle(name='yi', parent=y, thickness_left=thickness, thickness_right=thickness) # Flows inward (i.e. forward) rather than outward eq = Eq(u[t + 1, xl, yi], u[t + 1, xl - 1, yi] + 1) op = Operator([eq]) iterations = FindNodes(Iteration).visit(op) assert all(i.is_Affine and i.is_Sequential for i in iterations if i.dim == xl) assert all(i.is_Affine and i.is_Parallel for i in iterations if i.dim == yi) op.apply(time_m=1, time_M=1) assert all( np.all(u.data[0, :thickness, thickness + i] == [1, 2, 3, 4]) for i in range(12)) assert np.all(u.data[0, thickness:] == 0) assert np.all(u.data[0, :, thickness + 12:] == 0)
def test_index_mode_detection(self, indexed, expected): """ Test detection of IterationInstance access modes (AFFINE vs IRREGULAR). Proper detection of access mode is a prerequisite to any sort of data dependence analysis. """ grid = Grid(shape=(4, 4, 4)) x, y, z = grid.dimensions # noqa sx = SubDimension.middle('sx', x, 1, 1) # noqa u = Function(name='u', grid=grid) # noqa c = Constant(name='c') # noqa sc = Scalar(name='sc', is_const=True) # noqa s = Scalar(name='s') # noqa ii = IterationInstance(eval(indexed)) assert ii.index_mode == expected
def test_bcs_basic(self): """ Test MPI in presence of boundary condition loops. Here, no halo exchange is expected (as there is no stencil in the computed expression) but we check that: * the left BC loop is computed by the leftmost rank only * the right BC loop is computed by the rightmost rank only """ grid = Grid(shape=(20, )) x = grid.dimensions[0] t = grid.stepping_dim thickness = 4 u = TimeFunction(name='u', grid=grid, time_order=1) xleft = SubDimension.left(name='xleft', parent=x, thickness=thickness) xi = SubDimension.middle(name='xi', parent=x, thickness_left=thickness, thickness_right=thickness) xright = SubDimension.right(name='xright', parent=x, thickness=thickness) t_in_centre = Eq(u[t + 1, xi], 1) leftbc = Eq(u[t + 1, xleft], u[t + 1, xleft + 1] + 1) rightbc = Eq(u[t + 1, xright], u[t + 1, xright - 1] + 1) op = Operator([t_in_centre, leftbc, rightbc]) op.apply(time_m=1, time_M=1) glb_pos_map = u.grid.distributor.glb_pos_map if LEFT in glb_pos_map[x]: assert np.all(u.data_ro_domain[0, thickness:] == 1.) assert np.all( u.data_ro_domain[0, :thickness] == range(thickness + 1, 1, -1)) else: assert np.all(u.data_ro_domain[0, :-thickness] == 1.) assert np.all( u.data_ro_domain[0, -thickness:] == range(2, thickness + 2))
def test_subdim_middle(self): """ Tests that instantiating SubDimensions using the classmethod constructors works correctly. """ grid = Grid(shape=(4, 4, 4)) x, y, z = grid.dimensions t = grid.stepping_dim # noqa u = TimeFunction(name='u', grid=grid) # noqa xi = SubDimension.middle(name='xi', parent=x, thickness_left=1, thickness_right=1) eqs = [Eq(u.forward, u + 1)] eqs = [e.subs(x, xi) for e in eqs] op = Operator(eqs) u.data[:] = 1.0 op.apply(time_M=1) assert np.all(u.data[1, 0, :, :] == 1) assert np.all(u.data[1, -1, :, :] == 1) assert np.all(u.data[1, 1:3, :, :] == 2)
def test_nontrivial_operator(self): """ Test MPI in a non-trivial scenario: :: * 9 processes logically organised in a 3x3 cartesian grid (as opposed to most tests in this module, which only use 2 or 4 processed); * star-like stencil expression; * non-trivial Higdon-like BCs; * simultaneous presence of TimeFunction(grid), Function(grid), and Function(dimensions) """ size_x, size_y = 9, 9 tkn = 2 # Grid and Dimensions grid = Grid(shape=( size_x, size_y, )) x, y = grid.dimensions t = grid.stepping_dim # SubDimensions to implement BCs xl, yl = [SubDimension.left('%sl' % d.name, d, tkn) for d in [x, y]] xi, yi = [ SubDimension.middle('%si' % d.name, d, tkn, tkn) for d in [x, y] ] xr, yr = [SubDimension.right('%sr' % d.name, d, tkn) for d in [x, y]] # Functions u = TimeFunction(name='f', grid=grid) m = Function(name='m', grid=grid) c = Function(name='c', grid=grid, dimensions=(x, ), shape=(size_x, )) # Data initialization u.data_with_halo[:] = 0. m.data_with_halo[:] = 1. c.data_with_halo[:] = 0. # Equations c_init = Eq(c, 1.) eqn = Eq(u[t + 1, xi, yi], u[t, xi, yi] + m[xi, yi] + c[xi] + 1.) bc_left = Eq(u[t + 1, xl, yi], u[t + 1, xl + 1, yi] + 1.) bc_right = Eq(u[t + 1, xr, yi], u[t + 1, xr - 1, yi] + 1.) bc_top = Eq(u[t + 1, xi, yl], u[t + 1, xi, yl + 1] + 1.) bc_bottom = Eq(u[t + 1, xi, yr], u[t + 1, xi, yr - 1] + 1.) op = Operator([c_init, eqn, bc_left, bc_right, bc_top, bc_bottom]) op.apply(time=0) # Expected (global view): # 0 0 5 5 5 5 5 0 0 # 0 0 4 4 4 4 4 0 0 # 5 4 3 3 3 3 3 4 5 # 5 4 3 3 3 3 3 4 5 # 5 4 3 3 3 3 3 4 5 # 5 4 3 3 3 3 3 4 5 # 0 0 4 4 4 4 4 0 0 # 0 0 5 5 5 5 5 0 0 assert np.all(u.data_ro_domain[0] == 0) # The write occures at t=1 glb_pos_map = u.grid.distributor.glb_pos_map # Check cornes if LEFT in glb_pos_map[x] and LEFT in glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[0, 0, 5], [0, 0, 4], [5, 4, 3]]) elif LEFT in glb_pos_map[x] and RIGHT in glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[5, 0, 0], [4, 0, 0], [3, 4, 5]]) elif RIGHT in glb_pos_map[x] and LEFT in glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[5, 4, 3], [0, 0, 4], [0, 0, 5]]) elif RIGHT in glb_pos_map[x] and RIGHT in glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[3, 4, 5], [4, 0, 0], [5, 0, 0]]) # Check sides if not glb_pos_map[x] and LEFT in glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[5, 4, 3], [5, 4, 3], [5, 4, 3]]) elif not glb_pos_map[x] and RIGHT in glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[3, 4, 5], [3, 4, 5], [3, 4, 5]]) elif LEFT in glb_pos_map[x] and not glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[5, 5, 5], [4, 4, 4], [3, 3, 3]]) elif RIGHT in glb_pos_map[x] and not glb_pos_map[y]: assert np.all( u.data_ro_domain[1] == [[3, 3, 3], [4, 4, 4], [5, 5, 5]]) # Check center if not glb_pos_map[x] and not glb_pos_map[y]: assert np.all(u.data_ro_domain[1] == 3)