def match_grids_1d(new_1d, old_1d, tol): """ Obtain mappings between the cells of non-matching 1d grids. The function constructs an refined 1d grid that consists of all nodes of at least one of the input grids. It is asumed that the two grids are aligned, with common start and endpoints. Implementation note: It should be possible to avoid old_1d, by extracting points from a 2D grid that lie along the line defined by g_1d. However, if g_2d is split along a fracture, the nodes will be duplicated. We should then return two grids, probably based on the coordinates of the cell centers. sounds cool. Parameters: new_1d (grid): First grid to be matched old_1d (grid): Second grid to be matched. Returns: np.array: Ratio of cell volume in the common grid and the original grid. np.array: Mapping between cell numbers in common and first input grid. np.array: Mapping between cell numbers in common and second input grid. """ # Create a grid that contains all nodes of both the old and new grids. combined, _, new_ind, old_ind, _, _ = non_conforming.merge_1d_grids( new_1d, old_1d, tol=tol ) combined.compute_geometry() weights = combined.cell_volumes switch_new = new_ind[0] > new_ind[-1] if switch_new: new_ind = new_ind[::-1] switch_old = old_ind[0] > old_ind[-1] if switch_old: old_ind = old_ind[::-1] diff_new = np.diff(new_ind) diff_old = np.diff(old_ind) new_in_full = rldecode(np.arange(diff_new.size), diff_new) old_in_full = rldecode(np.arange(diff_old.size), diff_old) if switch_new: new_in_full = new_in_full.max() - new_in_full if switch_old: old_in_full = old_in_full.max() - old_in_full old_1d.compute_geometry() weights /= old_1d.cell_volumes[old_in_full] return weights, new_in_full, old_in_full
def _tensor_vector_prod(g, k, subcell_topology, apertures=None): """ Compute product of normal vectors and tensors on a sub-cell level. This is essentially defining Darcy's law for each sub-face in terms of sub-cell gradients. Thus, we also implicitly define the global ordering of sub-cell gradient variables (via the interpretation of the columns in nk). NOTE: In the local numbering below, in particular in the variables i and j, it is tacitly assumed that g.dim == g.nodes.shape[0] == g.face_normals.shape[0] etc. See implementation note in main method. Parameters: g (core.grids.grid): Discretization grid k (core.constit.second_order_tensor): The permeability tensor subcell_topology (fvutils.SubcellTopology): Wrapper class containing subcell numbering. Returns: nk: sub-face wise product of normal vector and permeability tensor. cell_node_blocks pairings of node and cell indices, which together define a sub-cell. sub_cell_ind: index of all subcells """ # Stack cell and nodes, and remove duplicate rows. Since subcell_mapping # defines cno and nno (and others) working cell-wise, this will # correspond to a unique rows (Matlab-style) from what I understand. # This also means that the pairs in cell_node_blocks uniquely defines # subcells, and can be used to index gradients etc. cell_node_blocks, blocksz = matrix_compression.rlencode( np.vstack((subcell_topology.cno, subcell_topology.nno))) nd = g.dim # Duplicates in [cno, nno] corresponds to different faces meeting at the # same node. There should be exactly nd of these. This test will fail # for pyramids in 3D assert np.all(blocksz == nd) # Define row and column indices to be used for normal_vectors * perm. # Rows are based on sub-face numbers. # Columns have nd elements for each sub-cell (to store a gradient) and # is adjusted according to block sizes _, j = np.meshgrid(subcell_topology.subhfno, np.arange(nd)) sum_blocksz = np.cumsum(blocksz) j += matrix_compression.rldecode(sum_blocksz - blocksz[0], blocksz) # Distribute faces equally on the sub-faces num_nodes = np.diff(g.face_nodes.indptr) normals = g.face_normals[:, subcell_topology.fno] / num_nodes[ subcell_topology.fno] if apertures is not None: normals = normals * apertures[subcell_topology.cno] # Represent normals and permeability on matrix form ind_ptr = np.hstack((np.arange(0, j.size, nd), j.size)) normals_mat = sps.csr_matrix((normals.ravel('F'), j.ravel('F'), ind_ptr)) k_mat = sps.csr_matrix( (k.perm[::, ::, cell_node_blocks[0]].ravel('F'), j.ravel('F'), ind_ptr)) nk = normals_mat * k_mat # Unique sub-cell indexes are pulled from column indices, we only need # every nd column (since nd faces of the cell meet at each vertex) sub_cell_ind = j[::, 0::nd] return nk, cell_node_blocks, sub_cell_ind