def test_unique_rows_1(self): a = np.array([[1, 2], [2, 1], [2, 4], [2, 1], [2, 4]]) ua_expected = np.array([[1, 2], [2, 1], [2, 4]]) ia_expected = np.array([0, 1, 2]) ic_expected = np.array([0, 1, 2, 1, 2]) ua, ia, ic = setmembership.unique_rows(a) assert np.sum(np.abs(ua) - np.abs(ua_expected)) == 0 assert np.all(ia - ia_expected == 0) assert np.all(ic - ic_expected == 0)
def create_partition(A, seeds=None, **kwargs): """ Create the partition based on an input matrix using the algebraic multigrid method coarse/fine-splittings based on direct couplings. The standard values for cdepth and epsilon are taken from the following reference. For more information see: U. Trottenberg, C. W. Oosterlee, and A. Schuller. Multigrid. Academic press, 2000. Parameters ---------- A: sparse matrix used for the agglomeration cdepth: the greather is the more intense the aggregation will be, e.g. less cells if it is used combined with generate_coarse_grid epsilon: weight for the off-diagonal entries to define the "strong negatively cupling" seeds: (optional) to define a-priori coarse cells Returns ------- out: agglomeration indices How to use ---------- part = create_partition(tpfa_matrix(g)) g = generate_coarse_grid(g, part) """ cdepth = int(kwargs.get("cdepth", 2)) epsilon = kwargs.get("epsilon", 0.25) if A.size == 0: return np.zeros(1) Nc = A.shape[0] # For each node, which other nodes are strongly connected to it ST = sps.lil_matrix((Nc, Nc), dtype=np.bool) # In the first instance, all cells are strongly connected to each other At = A.T for i in np.arange(Nc): loc = slice(At.indptr[i], At.indptr[i + 1]) ci, vals = At.indices[loc], At.data[loc] neg = vals < 0. nvals = vals[neg] nci = ci[neg] minId = np.argmin(nvals) ind = -nvals >= epsilon * np.abs(nvals[minId]) ST[nci[ind], i] = True # Temporary field, will store connections of depth 1 for _ in np.arange(2, cdepth + 1): STold = ST.copy() for j in np.arange(Nc): rowj = np.array(STold.rows[j]) if rowj.size == 0: continue row = np.hstack([STold.rows[r] for r in rowj]) ST[j, np.concatenate((rowj, row))] = True del STold ST.setdiag(False) lmbda = np.array([len(s) for s in ST.rows]) # Define coarse nodes candidate = np.ones(Nc, dtype=np.bool) is_fine = np.zeros(Nc, dtype=np.bool) is_coarse = np.zeros(Nc, dtype=np.bool) # cells that are not important for any other cells are on the fine scale. for row_id, row in enumerate(ST.rows): if not row: is_fine[row_id] = True candidate[row_id] = False ST = ST.tocsr() it = 0 while np.any(candidate): i = np.argmax(lmbda) is_coarse[i] = True j = ST.indices[ST.indptr[i]:ST.indptr[i + 1]] jf = j[candidate[j]] is_fine[jf] = True candidate[np.r_[i, jf]] = False loop = ST.indices[mcolon.mcolon(ST.indptr[jf], ST.indptr[jf + 1])] for row in np.unique(loop): s = ST.indices[ST.indptr[row]:ST.indptr[row + 1]] lmbda[row] = s[candidate[s]].size + 2 * s[is_fine[s]].size lmbda[np.logical_not(candidate)] = -1 it = it + 1 # Something went wrong during aggregation assert it <= Nc del lmbda, ST if seeds is not None: is_coarse[seeds] = True is_fine[seeds] = False # If two neighbors are coarse, eliminate one of them without touching the # seeds c2c = np.abs(A) > 0 c2c_rows, _, _ = sps.find(c2c) pairs = np.empty((0, 2), dtype=np.int) for idx, it in enumerate(np.where(is_coarse)[0]): loc = slice(c2c.indptr[it], c2c.indptr[it + 1]) ind = np.setdiff1d(c2c_rows[loc], it) cind = ind[is_coarse[ind]] new_pair = np.stack((np.repeat(it, cind.size), cind), axis=-1) pairs = np.append(pairs, new_pair, axis=0) # Remove one of the neighbors cells if pairs.size: pairs = setmembership.unique_rows(np.sort(pairs, axis=1))[0] for ij in pairs: A_val = np.array(A[ij, ij]).ravel() ids = ij[np.argsort(A_val)] ids = np.setdiff1d(ids, seeds, assume_unique=True) if ids.size: is_coarse[ids[0]] = False is_fine[ids[0]] = True coarse = np.where(is_coarse)[0] # Primal grid NC = coarse.size primal = sps.lil_matrix((NC, Nc), dtype=np.bool) primal[np.arange(NC), coarse[np.arange(NC)]] = True connection = sps.lil_matrix((Nc, Nc), dtype=np.double) for it in np.arange(Nc): n = np.setdiff1d(c2c_rows[c2c.indptr[it]:c2c.indptr[it + 1]], it) loc = slice(A.indptr[it], A.indptr[it + 1]) A_idx, A_row = A.indices[loc], A.data[loc] mask = A_idx != it connection[it, n] = np.abs(A_row[mask] / A_row[np.logical_not(mask)]) connection = connection.tocsr() candidates_rep = np.ediff1d(connection.indptr) candidates_idx = np.repeat(is_coarse, candidates_rep) candidates = np.stack( ( connection.indices[candidates_idx], np.repeat(np.arange(NC), candidates_rep[is_coarse]), ), axis=-1, ) connection_idx = mcolon.mcolon(connection.indptr[coarse], connection.indptr[coarse + 1]) vals = sps.csr_matrix( accumarray.accum(candidates, connection.data[connection_idx], size=[Nc, NC])) del candidates_rep, candidates_idx, connection_idx it = NC not_found = np.logical_not(is_coarse) # Process the strongest connection globally while np.any(not_found): np.argmax(vals.data) vals.argmax(axis=0) mcind = np.atleast_1d(np.squeeze(np.asarray(vals.argmax(axis=0)))) mcval = -np.inf * np.ones(mcind.size) for c, r in enumerate(mcind): loc = slice(vals.indptr[r], vals.indptr[r + 1]) vals_idx, vals_data = vals.indices[loc], vals.data[loc] mask = vals_idx == c if vals_idx.size == 0 or not np.any(mask): continue mcval[c] = vals_data[mask] mi = np.argmax(mcval) nadd = mcind[mi] primal[mi, nadd] = True it = it + 1 if it > Nc + 5: break not_found[nadd] = False vals.data[vals.indptr[nadd]:vals.indptr[nadd + 1]] = 0 loc = slice(connection.indptr[nadd], connection.indptr[nadd + 1]) nc = connection.indices[loc] af = not_found[nc] nc = nc[af] nv = mcval[mi] * connection[nadd, :] nv = nv.data[af] if len(nc) > 0: vals += sps.csr_matrix((nv, (nc, np.repeat(mi, len(nc)))), shape=(Nc, NC)) coarse, fine = primal.tocsr().nonzero() return coarse[np.argsort(fine)]
def __init__(self, p, tri=None, name=None): """ Create triangular grid from point cloud. If no triangulation is provided, Delaunay will be applied. Examples: >>> p = np.random.rand(2, 10) >>> tri = scipy.spatial.Delaunay(p.transpose()).simplices >>> g = TriangleGrid(p, tri.transpose()) Parameters ---------- p (np.ndarray, 2 x num_nodes): Point coordinates tri (np.ndarray, 3 x num_cells): Cell-node connections. If not provided, a Delaunay triangulation will be applied name (str, optional): Name of grid type. Defaults to None, in which case 'TriangleGrid' will be assigned. """ self.dim = 2 if tri is None: tri = scipy.spatial.Delaunay(p.transpose()) tri = tri.simplices tri = tri.transpose() if name is None: name = "TriangleGrid" num_nodes = p.shape[1] # Add a zero z-coordinate if p.shape[0] == 2: nodes = np.vstack((p, np.zeros(num_nodes))) else: nodes = p assert num_nodes > 2 # Check of transposes of point array # Face node relations face_nodes = np.hstack( (tri[[0, 1]], tri[[1, 2]], tri[[2, 0]])).transpose() face_nodes.sort(axis=1) face_nodes, _, cell_faces = setmembership.unique_rows(face_nodes) num_faces = face_nodes.shape[0] num_cells = tri.shape[1] num_nodes_per_face = 2 face_nodes = face_nodes.ravel("C") indptr = np.hstack(( np.arange(0, num_nodes_per_face * num_faces, num_nodes_per_face), num_nodes_per_face * num_faces, )) data = np.ones(face_nodes.shape, dtype=bool) face_nodes = sps.csc_matrix((data, face_nodes, indptr), shape=(num_nodes, num_faces)) # Cell face relation num_faces_per_cell = 3 cell_faces = cell_faces.reshape(num_faces_per_cell, num_cells).ravel("F") indptr = np.hstack(( np.arange(0, num_faces_per_cell * num_cells, num_faces_per_cell), num_faces_per_cell * num_cells, )) data = -np.ones(cell_faces.shape) _, sgns = np.unique(cell_faces, return_index=True) data[sgns] = 1 cell_faces = sps.csc_matrix((data, cell_faces, indptr), shape=(num_faces, num_cells)) super(TriangleGrid, self).__init__(2, nodes, face_nodes, cell_faces, name)