def test_node_based_ind_3d_bound(self): # Nodes of cell 1 n = np.array([1, 2, 5, 6, 17, 18, 21, 22]) known_cells = np.array([0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 13, 14]) known_faces = np.array([1, 2, 37, 40, 73, 82]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_3d, nodes=n) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_3d, nodes=n) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_face_based_ind_2d_bound(self): f = np.array([2]) known_cells = np.array([0, 1, 2, 3, 5, 6, 7, 8, 11, 12]) known_faces = np.array([2, 8, 31, 32, 36, 37]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, faces=f) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_face_based_ind_2d_bound(self): # Face between cell 1 and 2 f = np.array([2]) known_cells = np.array( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]) known_faces = np.array([2, 8, 31, 32, 36, 37]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, faces=f) self.assertTrue(np.alltrue(known_cells == cell_ind)) self.assertTrue(np.alltrue(known_faces == face_ind))
def test_cell_based_ind_2d(self): c = np.array([12]) known_cells = np.setdiff1d(np.arange(25), np.array([0, 4, 20, 24])) known_faces = np.array([8, 9, 14, 15, 20, 21, 41, 42, 43, 46, 47, 48]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, cells=c) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_node_based_ind_2d_bound(self): # Nodes of cell 1 n = np.array([1, 2, 7, 8]) known_cells = np.array([0, 1, 2, 5, 6, 7]) known_faces = np.array([1, 2, 31, 36]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, nodes=n) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_node_based_ind_2d(self): # Nodes of cell 12 (middle one) - from counting n = np.array([14, 15, 20, 21]) known_cells = np.array([6, 7, 8, 11, 12, 13, 16, 17, 18]) known_faces = np.array([14, 15, 42, 47]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, nodes=n) assert np.allclose(known_cells, cell_ind) assert np.allclose(known_faces, face_ind)
def test_cell_based_ind_bound_3d(self): c = np.array([1]) known_cells = np.arange(27) fx = np.array([1, 2, 5, 6, 13, 14, 17, 18]) fy = 36 + np.array([0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17]) fz = 72 + np.array([0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 13, 14]) known_faces = np.hstack((fx, fy, fz)) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_3d, cells=c) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_node_based_ind_3d(self): # Nodes of cell 13 (middle one) - from counting n = np.array([21, 22, 25, 26, 37, 38, 41, 42]) known_cells = np.arange(27) known_faces = np.array([17, 18, 52, 55, 85, 94]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_3d, nodes=n) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_face_based_ind_2d(self): # Use face between cells 11 and 12 f = np.array([14]) known_cells = np.arange(self.g_2d.num_cells) known_faces = np.array([8, 14, 20, 41, 42, 46, 47]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, faces=f) self.assertTrue(np.alltrue(known_cells == cell_ind)) self.assertTrue(np.alltrue(known_faces == face_ind))
def test_face_based_ind_2d(self): # Use face between cells 11 and 12 f = np.array([14]) known_cells = np.array( [1, 2, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, 21, 22]) known_faces = np.array([8, 14, 20, 41, 42, 46, 47]) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_2d, faces=f) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def test_cell_based_ind_3d(self): # Use cell 13 (middle one) c = np.array([13]) known_cells = np.arange(27) fx = np.hstack((np.array([1, 2, 5, 6, 9, 10]), np.array([1, 2, 5, 6, 9, 10]) + 12, np.array([1, 2, 5, 6, 9, 10]) + 24)) fy = 36 + np.hstack( (np.array([3, 4, 5, 6, 7, 8]), np.array([3, 4, 5, 6, 7, 8]) + 12, np.array([3, 4, 5, 6, 7, 8]) + 24)) fz = 72 + np.hstack((np.arange(9) + 9, np.arange(9) + 18)) known_faces = np.hstack((fx, fy, fz)) cell_ind, face_ind = fvutils.cell_ind_for_partial_update(self.g_3d, cells=c) assert np.alltrue(known_cells == cell_ind) assert np.alltrue(known_faces == face_ind)
def mpfa_partial(g, k, bnd, eta=0, inverter='numba', cells=None, faces=None, nodes=None, apertures=None): """ Run an MPFA discretization on subgrid, and return discretization in terms of global variable numbers. Scenarios where the method will be used include updates of permeability, and the introduction of an internal boundary (e.g. fracture growth). The subgrid can be specified in terms of cells, faces and nodes to be updated. For details on the implementation, see fv_utils.cell_ind_for_partial_update() Parameters: g (porepy.grids.grid.Grid): grid to be discretized k (porepy.params.tensor.SecondOrderTensor) permeability tensor bnd (porepy.params.bc.BoundarCondition) class for boundary conditions faces (np.ndarray) faces to be considered. Intended for partial discretization, may change in the future eta Location of pressure continuity point. Should be 1/3 for simplex grids, 0 otherwise. On boundary faces with Dirichlet conditions, eta=0 will be enforced. inverter (string) Block inverter to be used, either numba (default), cython or python. See fvutils.invert_diagonal_blocks for details. cells (np.array, int, optional): Index of cells on which to base the subgrid computation. Defaults to None. faces (np.array, int, optional): Index of faces on which to base the subgrid computation. Defaults to None. nodes (np.array, int, optional): Index of nodes on which to base the subgrid computation. Defaults to None. apertures (np.ndarray, float, optional) apertures of the cells for scaling of the face normals. Defaults to None. Note that if all of {cells, faces, nodes} are None, empty matrices will be returned. Returns: sps.csr_matrix (g.num_faces x g.num_cells): Flux discretization, computed on a subgrid. sps.csr_matrix (g,num_faces x g.num_faces): Boundary flux discretization, computed on a subgrid np.array (int): Global of the faces where the flux discretization is computed. """ if cells is not None: warnings.warn('Cells keyword for partial mpfa has not been tested') if faces is not None: warnings.warn('Faces keyword for partial mpfa has not been tested') # Find computational stencil, based on specified cells, faces and nodes. ind, active_faces = fvutils.cell_ind_for_partial_update(g, cells=cells, faces=faces, nodes=nodes) # Extract subgrid, together with mappings between local and global # cells sub_g, l2g_faces, _ = partition.extract_subgrid(g, ind) l2g_cells = sub_g.parent_cell_ind # Local parameter fields # Copy permeability field, and restrict to local cells loc_k = k.copy() loc_k.perm = loc_k.perm[::, ::, l2g_cells] glob_bound_face = g.get_all_boundary_faces() # Boundary conditions are slightly more complex. Find local faces # that are on the global boundary. loc_bound_ind = np.argwhere(np.in1d(l2g_faces, glob_bound_face)).ravel('F') loc_cond = np.array(loc_bound_ind.size * ['neu']) # Then pick boundary condition on those faces. if loc_bound_ind.size > 0: # We could have avoided to explicitly define Neumann conditions, # since these are default. # For primal-like discretizations like the MPFA, internal boundaries # are handled by assigning Neumann conditions. is_dir = np.logical_and(bnd.is_dir, np.logical_not(bnd.is_internal)) is_neu = np.logical_or(bnd.is_neu, bnd.is_internal) is_dir = is_dir[l2g_faces[loc_bound_ind]] is_neu = is_neu[l2g_faces[loc_bound_ind]] loc_cond[is_dir] = 'dir' loc_bnd = bc.BoundaryCondition(sub_g, faces=loc_bound_ind, cond=loc_cond) # Discretization of sub-problem flux_loc, bound_flux_loc = _mpfa_local(sub_g, loc_k, loc_bnd, eta=eta, inverter=inverter, apertures=apertures) # Map to global indices face_map, cell_map = fvutils.map_subgrid_to_grid(g, l2g_faces, l2g_cells, is_vector=False) flux_glob = face_map * flux_loc * cell_map bound_flux_glob = face_map * bound_flux_loc * face_map.transpose() # By design of mpfa, and the subgrids, the discretization will update faces # outside the active faces. Kill these. outside = np.setdiff1d(np.arange(g.num_faces), active_faces, assume_unique=True) flux_glob[outside, :] = 0 bound_flux_glob[outside, :] = 0 return flux_glob, bound_flux_glob, active_faces