def cell_node_matrix(self): """ Get cell-node relations in a Nc x 3 matrix Perhaps move this method to a superclass when tet-grids are implemented """ # Absolute value needed since cellFaces can be negative cn = self.face_nodes * np.abs(self.cell_faces) * sps.eye(self.num_cells) row, col = cn.nonzero() scol = np.argsort(col) # Consistency check assert np.all(accumarray.accum(col, np.ones(col.size)) == (self.dim + 1)) return row[scol].reshape(self.num_cells, 3)
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)]