def tag_faces(grids): # Assume only one grid of highest dimension assert len(grids[0]) == 1, 'Must be exactly'\ '1 grid of dim: ' + str(len(grids)) g_h = grids[0][0] bnd_faces = g_h.get_boundary_faces() g_h.add_face_tag(bnd_faces, FaceTag.DOMAIN_BOUNDARY) bnd_nodes, _, _ = sps.find(g_h.face_nodes[:, bnd_faces]) bnd_nodes = np.unique(bnd_nodes) for g_dim in grids[1:-1]: for g in g_dim: # We find the global nodes of all boundary faces bnd_faces_l = g.get_boundary_faces() indptr = g.face_nodes.indptr fn_loc = mcolon.mcolon(indptr[bnd_faces_l], indptr[bnd_faces_l + 1] - 1) nodes_loc = g.face_nodes.indices[fn_loc] # Convert to global numbering nodes_glb = g.global_point_ind[nodes_loc] # We then tag each node as a tip node if it is not a global # boundary node is_tip = np.in1d(nodes_glb, bnd_nodes, invert=True) # We reshape the nodes such that each column equals the nodes of # one face. If a face only contains global boundary nodes, the # local face is also a boundary face. Otherwise, we add a TIP tag. nodes_per_face = find_nodes_per_face(g) is_tip = np.any(is_tip.reshape((nodes_per_face, bnd_faces_l.size), order='F'), axis=0) g.add_face_tag(bnd_faces_l[is_tip], FaceTag.TIP) g.add_face_tag(bnd_faces_l[is_tip == False], FaceTag.DOMAIN_BOUNDARY)
def sort_sub_list(indices, indptr): ix = np.zeros(indices.size, dtype=int) for i in range(indptr.size - 1): sub_ind = mcolon(indptr[i], indptr[i + 1] - 1) loc_ix = np.argsort(indices[sub_ind]) ix[sub_ind] = loc_ix + indptr[i] indices = indices[ix] iv = np.zeros(indices.size, dtype=int) iv[ix] = np.arange(indices.size) return indices, iv
def duplicate_faces(gh, face_cells): """ Duplicate all faces that are connected to a lower-dim cell Parameters ---------- gh - Higher-dim grid face_cells - A list of connection matrices. Each matrix gives the mapping from the cells of a lower-dim grid to the faces of the higher diim grid. """ # We find the indices of the higher-dim faces to be duplicated. # Each of these faces are duplicated, and the duplication is # attached to the same nodes. We do not attach the faces to # any cells as this connection will have to be undone later # anyway. _, frac_id, _ = sps.find(face_cells) frac_id = np.unique(frac_id) node_start = gh.face_nodes.indptr[frac_id] node_end = gh.face_nodes.indptr[frac_id + 1] nodes = gh.face_nodes.indices[mcolon(node_start, node_end - 1)] added_node_pos = np.cumsum(node_end - node_start) + \ gh.face_nodes.indptr[-1] assert(added_node_pos.size == frac_id.size) assert(added_node_pos[-1] - gh.face_nodes.indptr[-1] == nodes.size) gh.face_nodes.indices = np.hstack((gh.face_nodes.indices, nodes)) gh.face_nodes.indptr = np.hstack((gh.face_nodes.indptr, added_node_pos)) gh.face_nodes.data = np.hstack((gh.face_nodes.data, np.ones(nodes.size, dtype=bool))) gh.face_nodes._shape = ( gh.num_nodes, gh.face_nodes.shape[1] + frac_id.size) assert(gh.face_nodes.indices.size == gh.face_nodes.indptr[-1]) node_start = gh.face_nodes.indptr[frac_id] node_end = gh.face_nodes.indptr[frac_id + 1] #frac_nodes = gh.face_nodes[:, frac_id] #gh.face_nodes = sps.hstack((gh.face_nodes, frac_nodes)) # We also copy the attributes of the original faces. gh.num_faces += frac_id.size gh.face_normals = np.hstack( (gh.face_normals, gh.face_normals[:, frac_id])) gh.face_areas = np.append(gh.face_areas, gh.face_areas[frac_id]) gh.face_centers = np.hstack( (gh.face_centers, gh.face_centers[:, frac_id])) # Not sure if this still does the correct thing. Might have to # send in a logical array instead of frac_id. gh.add_face_tag(frac_id, FaceTag.FRACTURE | FaceTag.BOUNDARY) gh.face_tags = np.append(gh.face_tags, gh.face_tags[frac_id]) return frac_id
def get_boundary_nodes(self): """ Get nodes on the boundary Returns: np.ndarray (1d), index of nodes on the boundary """ b_faces = self.get_boundary_faces() first = self.face_nodes.indptr[b_faces] second = self.face_nodes.indptr[b_faces + 1] - 1 return np.unique(self.face_nodes.indices[mcolon.mcolon(first, second)])
def block_diag_index(m, n=None): """ Get row and column indices for block diagonal matrix This is intended as the equivalent of the corresponding method in MRST. Examples: >>> m = np.array([2, 3]) >>> n = np.array([1, 2]) >>> i, j = block_diag_index(m, n) >>> i, j (array([0, 1, 2, 3, 4, 2, 3, 4]), array([0, 0, 1, 1, 1, 2, 2, 2])) >>> a = np.array([1, 3]) >>> i, j = block_diag_index(a) >>> i, j (array([0, 1, 2, 3, 1, 2, 3, 1, 2, 3]), array([0, 1, 1, 1, 2, 2, 2, 3, 3, 3])) Parameters: m - ndarray, dimension 1 n - ndarray, dimension 1, defaults to m """ if n is None: n = m start = np.hstack((np.zeros(1, dtype='int'), m)) pos = np.cumsum(start) p1 = pos[0:-1] p2 = pos[1:]-1 p1_full = matrix_compression.rldecode(p1, n) p2_full = matrix_compression.rldecode(p2, n) i = mcolon.mcolon(p1_full, p2_full) sumn = np.arange(np.sum(n)) m_n_full = matrix_compression.rldecode(m, n) j = matrix_compression.rldecode(sumn, m_n_full) return i, j
def grid_sequence_fixed_lines(basedim, num_levels, grid_type, pert=0, ref_rate=2, domain=None, subdom_func=None): dim = basedim.shape[0] if domain is None: domain = np.ones(dim) for iter1 in range(num_levels): nx = basedim * ref_rate**iter1 g = make_grid(grid_type, nx, domain, dim) if pert > 0: g.compute_geometry() old_nodes = g.nodes.copy() dx = np.max(domain / nx) g = perturb(g, pert, dx) if subdom_func is not None: # Characteristic function for all cell centers xc = g.cell_centers chi = subdom_func(xc[0], xc[1]) # chi_face = np.abs(g.cell_faces * chi) bnd_face = np.argwhere(chi_face > 0).squeeze(1) node_ptr = g.face_nodes.indptr node_ind = g.face_nodes.indices bnd_nodes = node_ind[mcolon.mcolon(node_ptr[bnd_face], node_ptr[bnd_face + 1] - 1)] g.nodes[:, bnd_nodes] = old_nodes[:, bnd_nodes] g.compute_geometry() yield g
def test_mcolon_one_missing(): a = np.array([1, 2]) b = np.array([2, 1]) c = mcolon.mcolon(a, b) assert np.all((c - np.array([1, 2])) == 0)
def test_mcolon_simple(): a = np.array([1, 2]) b = np.array([2, 3]) c = mcolon.mcolon(a, b) assert np.all((c - np.array([1, 2, 2, 3])) == 0)
def create_partition(A, cdepth=2, epsilon=0.25, seeds=None): """ 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) """ 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): ci, _, vals = sps.find(At[:, i]) 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 STold = ST.copy() for _ in np.arange(2, cdepth + 1): for j in np.arange(Nc): rowj = np.array(STold.rows[j]) row = np.hstack([STold.rows[r] for r in rowj]) ST[j, np.concatenate((rowj, row))] = True STold = ST.copy() 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] - 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 c2c = np.abs(A) > 0 c2c_rows, _, _ = sps.find(c2c) pairs = np.empty((2, 0), 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)) pairs = np.append(pairs, new_pair, axis=1) if pairs.size: pairs = unique.unique_np113(np.sort(pairs, axis=0), axis=1) for ij in pairs.T: mi = np.argmin(A[ij, ij]) is_coarse[ij[mi]] = False is_fine[ij[mi]] = True coarse = np.where(is_coarse)[0] # Primal grid NC = coarse.size primal = sps.lil_matrix((NC, Nc), dtype=np.bool) for i in np.arange(NC): primal[i, coarse[i]] = 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) connection[it, n] = np.abs(A[it, n] / At[it, it]) 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] - 1) vals = accumarray.accum(candidates, connection.data[connection_idx], size=[Nc, NC]) del candidates_rep, candidates_idx, connection_idx mcind = np.argmax(vals, axis=0) mcval = [vals[r, c] for c, r in enumerate(mcind)] it = NC not_found = np.logical_not(is_coarse) # Process the strongest connection globally while np.any(not_found): mi = np.argmax(mcval) nadd = mcind[mi] primal[mi, nadd] = True not_found[nadd] = False vals[nadd, :] *= 0 nc = connection.indices[connection.indptr[nadd]:connection.indptr[nadd + 1]] 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)).todense() mcind = np.argmax(vals, axis=0) mcval = [vals[r, c] for c, r in enumerate(mcind)] it = it + 1 if it > Nc + 5: break coarse, fine = primal.tocsr().nonzero() return coarse[np.argsort(fine)]