def preprocess(vertices=V, edges=E): """ Initial step to create PyOP2 Sets and Maps with correct dimensions to create indirection for the graph problem: 1) poses is a Set of all poses (parameter blocks) 2) constraints is a Set of all measurements (edges between pose blocks) 3) constraints_to_poses maps a constraint to 2 poses 4) constraints_to_constraints maps a constraint to the right number of data based on dimension of constraints_to_poses 5) poses_to_poses maps poses to data based on dimension of poses """ global NUM_POSES, NUM_CONSTRAINTS, POSES_DIM, CONSTRAINT_DIM, POSES_PER_CONSTRAINT if (not vertices is None) and (not edges is None): NUM_CONSTRAINTS = len(edges) NUM_POSES = len(vertices) poses = op2.Set(NUM_POSES, 'poses') constraints = op2.Set(NUM_CONSTRAINTS, 'constraints') if _VERBOSE: print '2D BA: %d poses and %d constraints between them' \ % (NUM_POSES, NUM_CONSTRAINTS) print '2D BA: pose dimension %d and constraint dimensions %d' \ % (POSES_DIM, CONSTRAINT_DIM) edgemappings = edges[['from_v', 'to_v']].values.reshape(POSES_PER_CONSTRAINT * NUM_CONSTRAINTS) if _DEBUG: print 'ba edges:', edgemappings, edgemappings.dtype, edgemappings.shape constraints_to_poses = op2.Map(constraints, poses, POSES_PER_CONSTRAINT, edgemappings, 'constraints_to_poses') constraints_to_constraints = op2.Map(constraints, constraints, CONSTRAINT_DIM, balib.identity_map(NUM_CONSTRAINTS, CONSTRAINT_DIM), 'constraints_to_constraints') poses_to_poses = op2.Map(poses, poses, POSES_DIM, balib.identity_map(NUM_POSES, POSES_DIM), 'poses_to_poses') # this is needed to update hessian initial condition values initial = op2.Set(1, 'initial') initial_to_poses = op2.Map(initial, poses, 1, [0], 'initial_to_poses') # initial_to_constraints = op2.Set( initial) return (poses, constraints, initial, constraints_to_poses, constraints_to_constraints, poses_to_poses, initial_to_poses)
def test_sparsity_has_diagonal_space(self): # A sparsity should have space for diagonal entries if rmap==cmap s = op2.Set(1) d = op2.Set(4) m = op2.Map(s, d, 2, [1, 3]) d2 = op2.Set(4) m2 = op2.Map(s, d2, 3, [1, 2, 3]) sparsity = op2.Sparsity((d, d), (m, m)) sparsity2 = op2.Sparsity((d, d2), (m, m2)) assert all(sparsity.nnz == [1, 2, 1, 2]) assert all(sparsity2.nnz == [0, 3, 0, 3])
def test_uninitialized_map(self, iterset, indset, x): """Accessing a par_loop argument via an uninitialized Map should raise an exception.""" kernel_wo = "void kernel_wo(unsigned int* x) { *x = 42; }\n" with pytest.raises(MapValueError): op2.par_loop(op2.Kernel(kernel_wo, "kernel_wo"), iterset, x(op2.WRITE, op2.Map(iterset, indset, 1)))
def test_sum_nodes_to_edges(self): """Creates a 1D grid with edge values numbered consecutively. Iterates over edges, summing the node values.""" nedges = nnodes - 1 nodes = op2.Set(nnodes, "nodes") edges = op2.Set(nedges, "edges") node_vals = op2.Dat(nodes, numpy.arange(nnodes, dtype=numpy.uint32), numpy.uint32, "node_vals") edge_vals = op2.Dat(edges, numpy.zeros(nedges, dtype=numpy.uint32), numpy.uint32, "edge_vals") e_map = numpy.array([(i, i + 1) for i in range(nedges)], dtype=numpy.uint32) edge2node = op2.Map(edges, nodes, 2, e_map, "edge2node") kernel_sum = FunDecl( "void", "kernel_sum", [ Decl("int*", c_sym("nodes"), qualifiers=["unsigned"]), Decl("int*", c_sym("edge"), qualifiers=["unsigned"]) ], c_for("i", 2, Incr(c_sym("*edge"), Symbol("nodes", ("i", ))))) op2.par_loop(op2.Kernel(kernel_sum, "kernel_sum"), edges, node_vals(op2.READ, edge2node[op2.i[0]]), edge_vals(op2.INC)) expected = numpy.arange(1, nedges * 2 + 1, 2) assert all(expected == edge_vals.data)
def coords_map(elements, node_set1): lsize = nums[2] * map_dofs_coords ind_coords = compute_ind_extr(nums, map_dofs_coords, nelems, layers, mesh2d, dofs_coords, A, wedges, elems2nodes, lsize) return op2.Map(elements, node_set1, map_dofs_coords, ind_coords, "elem_dofs", off1)
def coarse_to_fine_node_map(coarse, fine): if len(coarse) > 1: assert len(fine) == len(coarse) return op2.MixedMap( coarse_to_fine_node_map(c, f) for c, f in zip(coarse, fine)) mesh = coarse.mesh() assert hasattr(mesh, "_shared_data_cache") if not (coarse.ufl_element() == fine.ufl_element()): raise ValueError("Can't transfer between different spaces") ch, level = get_level(mesh) fh, fine_level = get_level(fine.mesh()) if ch is not fh: raise ValueError("Can't map between different hierarchies") refinements_per_level = ch.refinements_per_level if refinements_per_level * level + 1 != refinements_per_level * fine_level: raise ValueError("Can't map between level %s and level %s" % (level, fine_level)) c2f, vperm = ch._cells_vperm[int(level * refinements_per_level)] key = entity_dofs_key(coarse.finat_element.entity_dofs()) + (level, ) cache = mesh._shared_data_cache["hierarchy_cell_node_map"] try: return cache[key] except KeyError: from .impl import create_cell_node_map map_vals, offset = create_cell_node_map(coarse, fine, c2f, vperm) return cache.setdefault( key, op2.Map(mesh.cell_set, fine.node_set, map_vals.shape[1], map_vals, offset=offset))
def cell_node_map(self, level): """A :class:`pyop2.Map` from cells on a coarse mesh to the corresponding degrees of freedom on a the fine mesh below it. :arg level: the coarse level the map should be from. """ if not 0 <= level < len(self) - 1: raise RuntimeError( "Requested coarse level %d outside permissible range [0, %d)" % (level, len(self) - 1)) try: return self._map_cache[level] except KeyError: pass Vc = self._hierarchy[level] Vf = self._hierarchy[level + 1] c2f, vperm = self._mesh_hierarchy._cells_vperm[level] map_vals, offset = impl.create_cell_node_map(Vc, Vf, c2f, vperm) map = op2.Map(self._cell_sets[level], Vf.node_set, map_vals.shape[1], map_vals, offset=offset) self._map_cache[level] = map return map
def composed_map(map1, map2): """ Manually build a :class:`PyOP2.Map` from the iterset of map1 to the toset of map2. :arg map1: The map with the desired iterset :arg map2: The map with the desired toset :returns: The composed map Requires that `map1.toset == map2.iterset`. Only currently implemented for `map1.arity == 1` """ if map2 is None: # Real function space case return None if map1.toset != map2.iterset: raise ValueError( "Cannot compose a map where the intermediate sets do not match!") if map1.arity != 1: raise NotImplementedError( "Can only currently build composed maps where map1.arity == 1") iterset = map1.iterset toset = map2.toset arity = map2.arity values = map2.values[map1.values].reshape(iterset.size, arity) assert values.shape == (iterset.size, arity) return op2.Map(iterset, toset, arity, values)
def test_complementary_subsets(self, backend, iterset): """Test par_loop on two complementary subsets""" even = np.array([i for i in range(nelems) if not i % 2], dtype=np.int) odd = np.array([i for i in range(nelems) if i % 2], dtype=np.int) sseven = op2.Subset(iterset, even) ssodd = op2.Subset(iterset, odd) indset = op2.Set(nelems, "indset") map = op2.Map(iterset, indset, 1, [i for i in range(nelems)]) dat1 = op2.Dat(iterset ** 1, data=None, dtype=np.uint32) dat2 = op2.Dat(indset ** 1, data=None, dtype=np.uint32) k = op2.Kernel("""\ void inc(unsigned int* v1, unsigned int* v2) { *v1 += 1; *v2 += 1; } """, "inc") op2.par_loop(k, sseven, dat1(op2.RW), dat2(op2.INC, map[0])) op2.par_loop(k, ssodd, dat1(op2.RW), dat2(op2.INC, map[0])) assert np.sum(dat1.data) == nelems assert np.sum(dat2.data) == nelems
def test_sum_nodes_to_edges(self): """Creates a 1D grid with edge values numbered consecutively. Iterates over edges, summing the node values.""" nedges = nnodes - 1 nodes = op2.Set(nnodes, "nodes") edges = op2.Set(nedges, "edges") node_vals = op2.Dat(nodes, numpy.array(range(nnodes), dtype=numpy.uint32), numpy.uint32, "node_vals") edge_vals = op2.Dat(edges, numpy.array([0] * nedges, dtype=numpy.uint32), numpy.uint32, "edge_vals") e_map = numpy.array([(i, i + 1) for i in range(nedges)], dtype=numpy.uint32) edge2node = op2.Map(edges, nodes, 2, e_map, "edge2node") kernel_sum = """ static void sum(unsigned int* edge, unsigned int *nodes) { *edge = nodes[0] + nodes[1]; } """ op2.par_loop(op2.Kernel(kernel_sum, "sum"), edges, edge_vals(op2.WRITE), node_vals(op2.READ, edge2node)) expected = numpy.asarray(range(1, nedges * 2 + 1, 2)) assert all(expected == edge_vals.data)
def time_sparsity(n): nodes = op2.Set(n) cells = op2.Set(n - 1) m = op2.Map(cells, nodes, 2, np.concatenate((np.arange(n - 1), np.arange(1, n)))) t = clock() s = op2.Sparsity((m, m), 1) return clock() - t
def test_mat_always_has_diagonal_space(self): # A sparsity should always have space for diagonal entries s = op2.Set(1) d = op2.Set(4) m = op2.Map(s, d, 1, [2]) d2 = op2.Set(3) m2 = op2.Map(s, d2, 1, [1]) sparsity = op2.Sparsity((d, d2), (m, m2)) from petsc4py import PETSc # petsc4py default error handler swallows SETERRQ, so just # install the abort handler to notice an error. PETSc.Sys.pushErrorHandler("abort") mat = op2.Mat(sparsity) PETSc.Sys.popErrorHandler() assert np.allclose(mat.handle.getDiagonal().array, 0.0)
def test_matrix(self, backend): """Test a indirect par_loop with a matrix argument""" iterset = op2.Set(2) idset = op2.Set(2) ss01 = op2.Subset(iterset, [0, 1]) ss10 = op2.Subset(iterset, [1, 0]) indset = op2.Set(4) dat = op2.Dat(idset ** 1, data=[0, 1], dtype=np.float) map = op2.Map(iterset, indset, 4, [0, 1, 2, 3, 0, 1, 2, 3]) idmap = op2.Map(iterset, idset, 1, [0, 1]) sparsity = op2.Sparsity((indset, indset), (map, map)) mat = op2.Mat(sparsity, np.float64) mat01 = op2.Mat(sparsity, np.float64) mat10 = op2.Mat(sparsity, np.float64) assembly = c_for("i", 4, c_for("j", 4, Incr(Symbol("mat", ("i", "j")), FlatBlock("(*dat)*16+i*4+j")))) kernel_code = FunDecl("void", "unique_id", [Decl("double*", c_sym("dat")), Decl("double", Symbol("mat", (4, 4)))], Block([assembly], open_scope=False)) k = op2.Kernel(kernel_code, "unique_id") mat.zero() mat01.zero() mat10.zero() op2.par_loop(k, iterset, dat(op2.READ, idmap[0]), mat(op2.INC, (map[op2.i[0]], map[op2.i[1]]))) mat.assemble() op2.par_loop(k, ss01, dat(op2.READ, idmap[0]), mat01(op2.INC, (map[op2.i[0]], map[op2.i[1]]))) mat01.assemble() op2.par_loop(k, ss10, dat(op2.READ, idmap[0]), mat10(op2.INC, (map[op2.i[0]], map[op2.i[1]]))) mat10.assemble() assert (mat01.values == mat.values).all() assert (mat10.values == mat.values).all()
def test_build_sparsity(self): """Building a sparsity from a pair of maps should give the expected rowptr and colidx.""" elements = op2.Set(4) nodes = op2.Set(5) elem_node = op2.Map(elements, nodes, 3, [0, 4, 3, 0, 1, 4, 1, 2, 4, 2, 3, 4]) sparsity = op2.Sparsity((nodes, nodes), (elem_node, elem_node)) assert all(sparsity._rowptr == [0, 4, 8, 12, 16, 21]) assert all(sparsity._colidx == [0, 1, 3, 4, 0, 1, 2, 4, 1, 2, 3, 4, 0, 2, 3, 4, 0, 1, 2, 3, 4])
def coarse_cell_to_fine_node_map(Vc, Vf): if len(Vf) > 1: assert len(Vf) == len(Vc) return op2.MixedMap( coarse_cell_to_fine_node_map(f, c) for f, c in zip(Vf, Vc)) mesh = Vc.mesh() assert hasattr(mesh, "_shared_data_cache") hierarchyf, levelf = get_level(Vf.ufl_domain()) hierarchyc, levelc = get_level(Vc.ufl_domain()) if hierarchyc != hierarchyf: raise ValueError("Can't map across hierarchies") hierarchy = hierarchyf increment = Fraction(1, hierarchyf.refinements_per_level) if levelc + increment != levelf: raise ValueError("Can't map between level %s and level %s" % (levelc, levelf)) key = (entity_dofs_key(Vf.finat_element.entity_dofs()) + (levelc, levelf)) cache = mesh._shared_data_cache["hierarchy_coarse_cell_to_fine_node_map"] try: return cache[key] except KeyError: assert Vc.extruded == Vf.extruded if Vc.mesh().variable_layers or Vf.mesh().variable_layers: raise NotImplementedError( "Not implemented for variable layers, sorry") if Vc.extruded and Vc.mesh().layers != Vf.mesh().layers: raise ValueError( "Coarse and fine meshes must have same number of layers") coarse_to_fine = hierarchy.coarse_to_fine_cells[levelc] _, ncell = coarse_to_fine.shape iterset = Vc.mesh().cell_set arity = Vf.finat_element.space_dimension() * ncell coarse_to_fine_nodes = numpy.full((iterset.total_size, arity), -1, dtype=IntType) values = Vf.cell_node_map().values[coarse_to_fine, :].reshape( iterset.size, arity) coarse_to_fine_nodes[:Vc.mesh().cell_set.size, :] = values offset = Vf.offset if offset is not None: offset = numpy.tile(offset, ncell) return cache.setdefault( key, op2.Map(iterset, Vf.node_set, arity=arity, values=coarse_to_fine_nodes, offset=offset))
def matrix_funptr(form): from firedrake.tsfc_interface import compile_form test, trial = map(operator.methodcaller("function_space"), form.arguments()) if test != trial: raise NotImplementedError("Only for matching test and trial spaces") kernel, = compile_form(form, "subspace_form", split=False) kinfo = kernel.kinfo if kinfo.subdomain_id != "otherwise": raise NotImplementedError("Only for full domain integrals") if kinfo.integral_type != "cell": raise NotImplementedError("Only for cell integrals") # OK, now we've validated the kernel, let's build the callback args = [] toset = op2.Set(1, comm=test.comm) dofset = op2.DataSet(toset, 1) arity = sum(m.arity * s.cdim for m, s in zip(test.cell_node_map(), test.dof_dset)) iterset = test.cell_node_map().iterset cell_node_map = op2.Map(iterset, toset, arity, values=numpy.zeros(iterset.total_size * arity, dtype=IntType)) mat = DenseMat(dofset) arg = mat(op2.INC, (cell_node_map[op2.i[0]], cell_node_map[op2.i[1]])) arg.position = 0 args.append(arg) mesh = form.ufl_domains()[kinfo.domain_number] arg = mesh.coordinates.dat(op2.READ, mesh.coordinates.cell_node_map()[op2.i[0]]) arg.position = 1 args.append(arg) for n in kinfo.coefficient_map: c = form.coefficients()[n] for (i, c_) in enumerate(c.split()): map_ = c_.cell_node_map() if map_ is not None: map_ = map_[op2.i[0]] arg = c_.dat(op2.READ, map_) arg.position = len(args) args.append(arg) iterset = op2.Subset(mesh.cell_set, [0]) mod = JITModule(kinfo.kernel, iterset, *args) return mod._fun, kinfo
def test_indirect_loop(self, backend, iterset): """Test a indirect ParLoop on a subset""" indices = np.array([i for i in range(nelems) if not i % 2], dtype=np.int) ss = op2.Subset(iterset, indices) indset = op2.Set(2, "indset") map = op2.Map(iterset, indset, 1, [(1 if i % 2 else 0) for i in range(nelems)]) d = op2.Dat(indset ** 1, data=None, dtype=np.uint32) k = op2.Kernel("void inc(unsigned int* v) { *v += 1;}", "inc") op2.par_loop(k, ss, d(op2.INC, map[0])) assert d.data[0] == nelems / 2
def test_indirect_loop_empty(self, backend, iterset): """Test a indirect ParLoop on an empty""" ss = op2.Subset(iterset, []) indset = op2.Set(2, "indset") map = op2.Map(iterset, indset, 1, [(1 if i % 2 else 0) for i in range(nelems)]) d = op2.Dat(indset ** 1, data=None, dtype=np.uint32) k = op2.Kernel("void inc(unsigned int* v) { *v += 1;}", "inc") d.data[:] = 0 op2.par_loop(k, ss, d(op2.INC, map[0])) assert (d.data == 0).all()
def test_plan_per_iterset_partition(self, backend): set = op2.Set([2, 4, 4, 4], "set") indset = op2.Set(4, "indset") dat = op2.Dat(set**1, [0, 1, 2, 3], dtype=numpy.int32) inddat = op2.Dat(indset**1, [0, 0, 0, 0], dtype=numpy.int32) map = op2.Map(set, indset, 1, [0, 1, 2, 3]) self.cache.clear() assert len(self.cache) == 0 op2.par_loop( op2.Kernel("void assign(int* src, int* dst) { *dst = *src; }", "assign"), set, dat(op2.READ), inddat(op2.WRITE, map[0])) assert (dat.data == inddat.data).all() assert len(self.cache) == 2
def test_indirect_loop_with_direct_dat(self, backend, iterset): """Test a indirect ParLoop on a subset""" indices = np.array([i for i in range(nelems) if not i % 2], dtype=np.int) ss = op2.Subset(iterset, indices) indset = op2.Set(2, "indset") map = op2.Map(iterset, indset, 1, [(1 if i % 2 else 0) for i in range(nelems)]) values = [2976579765] * nelems values[::2] = [i/2 for i in range(nelems)][::2] dat1 = op2.Dat(iterset ** 1, data=values, dtype=np.uint32) dat2 = op2.Dat(indset ** 1, data=None, dtype=np.uint32) k = op2.Kernel("void inc(unsigned* s, unsigned int* d) { *d += *s;}", "inc") op2.par_loop(k, ss, dat1(op2.READ), dat2(op2.INC, map[0])) assert dat2.data[0] == sum(values[::2])
def read_triangle(f, layers=None): """Read the triangle file with prefix f into OP2 data strctures. Presently only .node and .ele files are read, attributes are ignored, and there may be bugs. The dat structures are returned as: (nodes, coords, elements, elem_node) These items have type: (Set, Dat, Set, Map) The Layers argument allows the reading of data for extruded meshes. It is to be used when dealing with extruded meshes. """ # Read nodes with open(f + '.node') as h: num_nodes = int(h.readline().split(' ')[0]) node_values = np.zeros((num_nodes, 2), dtype=np.float64) for line in h: if line[0] == '#': continue node, x, y = line.split()[:3] node_values[int(node) - 1, :] = [float(x), float(y)] nodes = op2.Set(num_nodes, "nodes") coords = op2.Dat(nodes ** 2, node_values, name="coords") # Read elements with open(f + '.ele') as h: num_tri, nodes_per_tri, num_attrs = [int(col) for col in h.readline().split()] map_values = np.zeros((num_tri, nodes_per_tri), dtype=np.int32) for line in h: if line[0] == '#': continue vals = [int(v) - 1 for v in line.split()] map_values[vals[0], :] = vals[1:nodes_per_tri + 1] if layers is not None: elements = op2.ExtrudedSet(op2.Set(num_tri, "elements"), layers=layers) else: elements = op2.Set(num_tri, "elements") elem_node = op2.Map(elements, nodes, nodes_per_tri, map_values, "elem_node") return nodes, coords, elements, elem_node
def coarse_node_to_fine_node_map(Vc, Vf): if len(Vf) > 1: assert len(Vf) == len(Vc) return op2.MixedMap( coarse_node_to_fine_node_map(f, c) for f, c in zip(Vf, Vc)) mesh = Vc.mesh() assert hasattr(mesh, "_shared_data_cache") hierarchyf, levelf = get_level(Vf.ufl_domain()) hierarchyc, levelc = get_level(Vc.ufl_domain()) if hierarchyc != hierarchyf: raise ValueError("Can't map across hierarchies") hierarchy = hierarchyf increment = Fraction(1, hierarchyf.refinements_per_level) if levelc + increment != levelf: raise ValueError("Can't map between level %s and level %s" % (levelc, levelf)) key = (entity_dofs_key(Vc.finat_element.entity_dofs()) + entity_dofs_key(Vf.finat_element.entity_dofs()) + (levelc, levelf)) cache = mesh._shared_data_cache["hierarchy_coarse_node_to_fine_node_map"] try: return cache[key] except KeyError: assert Vc.extruded == Vf.extruded if Vc.mesh().variable_layers or Vf.mesh().variable_layers: raise NotImplementedError( "Not implemented for variable layers, sorry") if Vc.extruded and not ((Vf.mesh().layers - 1) / (Vc.mesh().layers - 1)).is_integer(): raise ValueError( "Coarse and fine meshes must have an integer ratio of layers") coarse_to_fine = hierarchy.coarse_to_fine_cells[levelc] coarse_to_fine_nodes = impl.coarse_to_fine_nodes( Vc, Vf, coarse_to_fine) return cache.setdefault( key, op2.Map(Vc.node_set, Vf.node_set, coarse_to_fine_nodes.shape[1], values=coarse_to_fine_nodes))
def fine_node_to_coarse_node_map(Vf, Vc): if len(Vf) > 1: assert len(Vf) == len(Vc) return op2.MixedMap( fine_node_to_coarse_node_map(f, c) for f, c in zip(Vf, Vc)) mesh = Vf.mesh() assert hasattr(mesh, "_shared_data_cache") hierarchyf, levelf = get_level(Vf.ufl_domain()) hierarchyc, levelc = get_level(Vc.ufl_domain()) if hierarchyc != hierarchyf: raise ValueError("Can't map across hierarchies") hierarchy = hierarchyf if levelc + 1 != levelf: raise ValueError("Can't map between level %s and level %s" % (levelc, levelf)) key = (entity_dofs_key(Vc.finat_element.entity_dofs()) + entity_dofs_key(Vf.finat_element.entity_dofs()) + (levelc, levelf)) cache = mesh._shared_data_cache["hierarchy_fine_node_to_coarse_node_map"] try: return cache[key] except KeyError: assert Vc.extruded == Vf.extruded if Vc.mesh().variable_layers or Vf.mesh().variable_layers: raise NotImplementedError( "Not implemented for variable layers, sorry") if Vc.extruded and Vc.mesh().layers != Vf.mesh().layers: raise ValueError( "Coarse and fine meshes must have same number of layers") fine_to_coarse = hierarchy.fine_to_coarse_cells[levelc + 1] fine_to_coarse_nodes = impl.fine_to_coarse_nodes( Vf, Vc, fine_to_coarse) return cache.setdefault( key, op2.Map(Vf.node_set, Vc.node_set, fine_to_coarse_nodes.shape[1], values=fine_to_coarse_nodes))
def test_2d_map(self): """Sum nodal values incident to a common edge.""" nedges = nelems - 1 nodes = op2.Set(nelems, "nodes") edges = op2.Set(nedges, "edges") node_vals = op2.Dat(nodes, np.arange(nelems, dtype=np.uint32), np.uint32, "node_vals") edge_vals = op2.Dat(edges, np.zeros(nedges, dtype=np.uint32), np.uint32, "edge_vals") e_map = np.array([(i, i + 1) for i in range(nedges)], dtype=np.uint32) edge2node = op2.Map(edges, nodes, 2, e_map, "edge2node") kernel_sum = """ static void sum(unsigned int *edge, unsigned int *nodes) { *edge = nodes[0] + nodes[1]; }""" op2.par_loop(op2.Kernel(kernel_sum, "sum"), edges, edge_vals(op2.WRITE), node_vals(op2.READ, edge2node)) expected = np.arange(1, nedges * 2 + 1, 2) assert all(expected == edge_vals.data)
def get_map(self, V, entity_set, map_arity, name, offset): """Return a :class:`pyop2.Map` from some topological entity to degrees of freedom. :arg V: The :class:`FunctionSpace` to create the map for. :arg entity_set: The :class:`pyop2.Set` of entities to map from. :arg map_arity: The arity of the resulting map. :arg name: A name for the resulting map. :arg offset: Map offset (for extruded).""" # V is only really used for error checking and "name". assert len(V) == 1, "get_map should not be called on MixedFunctionSpace" entity_node_list = self.entity_node_lists[entity_set] val = self.map_cache[entity_set] if val is None: val = op2.Map(entity_set, self.node_set, map_arity, entity_node_list, ("%s_"+name) % (V.name), offset=offset) self.map_cache[entity_set] = val return val
def test_minimal_zero_mat(self): """Assemble a matrix that is all zeros.""" code = c_for("i", 1, c_for("j", 1, Assign(Symbol("local_mat", ("i", "j")), c_sym("0.0")))) zero_mat_code = FunDecl("void", "zero_mat", [Decl("double", Symbol("local_mat", (1, 1)))], Block([code], open_scope=False)) nelems = 128 set = op2.Set(nelems) map = op2.Map(set, set, 1, np.array(list(range(nelems)), np.uint32)) sparsity = op2.Sparsity((set, set), (map, map)) mat = op2.Mat(sparsity, np.float64) kernel = op2.Kernel(zero_mat_code, "zero_mat") op2.par_loop(kernel, set, mat(op2.WRITE, (map[op2.i[0]], map[op2.i[1]]))) mat.assemble() expected_matrix = np.zeros((nelems, nelems), dtype=np.float64) eps = 1.e-12 assert_allclose(mat.values, expected_matrix, eps)
def testKernel(data=None, op2set=None, op2map=None): """ for testing purposes, it just prints out the data Dat array (assuming) it has dimension 3 in its Map (op2map). The kernel iterates over the Set op2set, if no arguments are passed in, this creates dummy values and prints them out """ if not op2set: op2set = op2.Set(5, 'fromset') if not op2map: op2toset = op2.Set(4, 'toset') npmapping = np.array([0, 1, 1, 2, 2, 3, 3, 1, 3, 2], np.uint32) print '-' * 80 print 'mapping: ', npmapping, npmapping.dtype, npmapping.shape op2map = op2.Map(op2set, op2toset, 2, npmapping, 'testmap') if not data: numpydata = np.array([[0, 1, 1], [1, 2, 2], [2, 3, 3], [3, 4, 4]], dtype=np.float64) print '-' * 80 print 'data:' print numpydata data = op2.Dat(op2toset ** 3, numpydata, np.float64, 'testdata') test = op2.Kernel(""" void test_kernel(double *x[3]) { std::cout << " " << x[0][0] << " " << x[0][1] << " " << x[0][2]; std::cout << " : " << x[1][0] << " " << x[1][1] << " " << x[1][2] << std::endl; } """, 'test_kernel') print '-' * 80 print 'PyOP2 output:' op2.par_loop(test, op2set, data(op2map, op2.READ)) print '-' * 80
def exterior_facet_boundary_node_map(self, V, method): """Return the :class:`pyop2.Map` from exterior facets to nodes on the boundary. :arg V: The function space. :arg method: The method for determining boundary nodes. See :class:`~.DirichletBC` for details. """ try: return self.map_caches["boundary_node"][method] except KeyError: pass el = V.finat_element dim = self.mesh.facet_dimension() if method == "topological": boundary_dofs = el.entity_closure_dofs()[dim] elif method == "geometric": # This function is only called on extruded meshes when # asking for the nodes that live on the "vertical" # exterior facets. boundary_dofs = entity_support_dofs(el, dim) nodes_per_facet = \ len(boundary_dofs[0]) # HACK ALERT # The facet set does not have a halo associated with it, since # we only construct halos for DoF sets. Fortunately, this # loop is direct and we already have all the correct # information available locally. So We fake a set of the # correct size and carry out a direct loop facet_set = op2.Set(self.mesh.exterior_facets.set.total_size, comm=self.mesh.comm) fs_dat = op2.Dat( facet_set**el.space_dimension(), data=V.exterior_facet_node_map().values_with_halo.view()) facet_dat = op2.Dat(facet_set**nodes_per_facet, dtype=IntType) # Ensure these come out in sorted order. local_facet_nodes = numpy.array( [boundary_dofs[e] for e in sorted(boundary_dofs.keys())]) # Helper function to turn the inner index of an array into c # array literals. c_array = lambda xs: "{" + ", ".join(map(str, xs)) + "}" # AST for: l_nodes[facet[0]][n] rank_ast = ast.Symbol("l_nodes", rank=(ast.Symbol("facet", rank=(0, )), "n")) body = ast.Block([ ast.Decl("int", ast.Symbol("l_nodes", (len(el.cell.topology[dim]), nodes_per_facet)), init=ast.ArrayInit( c_array(map(c_array, local_facet_nodes))), qualifiers=["const"]), ast.For( ast.Decl("int", "n", 0), ast.Less("n", nodes_per_facet), ast.Incr("n", 1), ast.Assign(ast.Symbol("facet_nodes", ("n", )), ast.Symbol("cell_nodes", (rank_ast, )))) ]) kernel = op2.Kernel( ast.FunDecl("void", "create_bc_node_map", [ ast.Decl("%s*" % as_cstr(fs_dat.dtype), "cell_nodes"), ast.Decl("%s*" % as_cstr(facet_dat.dtype), "facet_nodes"), ast.Decl("unsigned int*", "facet") ], body), "create_bc_node_map") local_facet_dat = op2.Dat( facet_set**self.mesh.exterior_facets._rank, self.mesh.exterior_facets.local_facet_dat.data_ro_with_halos, dtype=numpy.uintc) op2.par_loop(kernel, facet_set, fs_dat(op2.READ), facet_dat(op2.WRITE), local_facet_dat(op2.READ)) if self.extruded: offset = self.offset[boundary_dofs[0]] else: offset = None val = op2.Map(facet_set, self.node_set, nodes_per_facet, facet_dat.data_ro_with_halos, name="exterior_facet_boundary_node", offset=offset) self.map_caches["boundary_node"][method] = val return val
def get_map(self, V, entity_set, map_arity, bcs, name, offset, parent, kind=None): """Return a :class:`pyop2.Map` from some topological entity to degrees of freedom. :arg V: The :class:`FunctionSpace` to create the map for. :arg entity_set: The :class:`pyop2.Set` of entities to map from. :arg map_arity: The arity of the resulting map. :arg bcs: An iterable of :class:`~.DirichletBC` objects (may be ``None``. :arg name: A name for the resulting map. :arg offset: Map offset (for extruded). :arg parent: The parent map (used when bcs are provided).""" # V is only really used for error checking and "name". assert len( V) == 1, "get_map should not be called on MixedFunctionSpace" entity_node_list = self.entity_node_lists[entity_set] cache = self.map_caches[entity_set] if bcs is not None: # Separate explicit bcs (we just place negative entries in # the appropriate map values) from implicit ones (extruded # top and bottom) that require PyOP2 code gen. explicit_bcs = [ bc for bc in bcs if bc.sub_domain not in ['top', 'bottom'] ] implicit_bcs = [(bc.sub_domain, bc.method) for bc in bcs if bc.sub_domain in ['top', 'bottom']] if len(explicit_bcs) == 0: # Implicit bcs are not part of the cache key for the # map (they only change the generated PyOP2 code), # hence rewrite bcs here. bcs = () if len(implicit_bcs) == 0: implicit_bcs = None else: # Empty tuple if no bcs found. This is so that matrix # assembly, which uses a set to keep track of the bcs # applied to matrix hits the cache when that set is # empty. tuple(set([])) == tuple(). bcs = () implicit_bcs = None for bc in bcs: fs = bc.function_space() # Unwind proxies for ComponentFunctionSpace, but not # IndexedFunctionSpace. while fs.component is not None and fs.parent is not None: fs = fs.parent if fs.topological != V: raise RuntimeError( "DirichletBC defined on a different FunctionSpace!") # Ensure bcs is a tuple in a canonical order for the hash key. lbcs = tuple(sorted(bcs, key=lambda bc: bc.__hash__())) cache = self.map_caches[entity_set] try: # Cache hit val = cache[lbcs] # In the implicit bc case, we decorate the cached map with # the list of implicit boundary conditions so PyOP2 knows # what to do. if implicit_bcs: val = op2.DecoratedMap(val, implicit_bcs=implicit_bcs) return val except KeyError: # Cache miss. # Any top and bottom bcs (for the extruded case) are handled elsewhere. nodes = [ bc.nodes for bc in lbcs if bc.sub_domain not in ['top', 'bottom'] ] decorate = any(bc.function_space().component is not None for bc in lbcs) if nodes: bcids = reduce(numpy.union1d, nodes) negids = numpy.copy(bcids) for bc in lbcs: if bc.sub_domain in ["top", "bottom"]: continue nbits = IntType.itemsize * 8 - 2 if decorate and bc.function_space().component is None: # Some of the other entries will be marked # with high bits, so we need to set all the # high bits for these bcs idx = numpy.searchsorted(bcids, bc.nodes) if bc.function_space().dim > 3: raise ValueError( "Can't have component BCs with more than three components (have %d)", bc.function_space().dim) for cmp in range(bc.function_space().dim): negids[idx] |= (1 << (nbits - cmp)) # FunctionSpace with component is IndexedVFS if bc.function_space().component is not None: # For indexed VFS bcs, we encode the component # in the high bits of the map value. # That value is then negated to indicate to # the generated code to discard the values # # So here we do: # # node = -(node + 2**(nbits-cmpt) + 1) # # And in the generated code we can then # extract the information to discard the # correct entries. # bcids is sorted, so use searchsorted to find indices idx = numpy.searchsorted(bcids, bc.nodes) # Set appropriate bit negids[idx] |= ( 1 << (nbits - bc.function_space().component)) node_list_bc = numpy.arange(self.node_set.total_size, dtype=IntType) # Fix up for extruded, doesn't commute with indexedvfs # for now if self.extruded: node_list_bc[bcids] = -10000000 else: node_list_bc[bcids] = -(negids + 1) new_entity_node_list = node_list_bc.take(entity_node_list) else: new_entity_node_list = entity_node_list if kind == "interior_facet" and self.bt_masks is not None: bt_masks = {} off = map_arity // 2 for method, (bottom, top) in iteritems(self.bt_masks): b = [] t = [] for i in bottom: b.append(i) b.append(i + off) for i in top: t.append(i) t.append(i + off) bt_masks[method] = (b, t) else: bt_masks = self.bt_masks val = op2.Map(entity_set, self.node_set, map_arity, new_entity_node_list, ("%s_" + name) % (V.name), offset=offset, parent=parent, bt_masks=bt_masks) if decorate: val = op2.DecoratedMap(val, vector_index=True) cache[lbcs] = val if implicit_bcs: return op2.DecoratedMap(val, implicit_bcs=implicit_bcs) return val
def elem_node(elements, nodes): return op2.Map(elements, nodes, 3, elem_node_map, "elem_node")