Source code for porepy.numerics.discretization

""" Module contains two classes:
    1) The abstract superclass for all discretizations
    2) A do-nothing discretization

"""
import abc
from typing import Dict, Union

import numpy as np
import scipy.sparse as sps

import porepy as pp


[docs] class Discretization(abc.ABC): """Interface for all discretizations. Specifies methods that must be implemented for a discretization class to be compatible with the assembler. """ def __init__(self, keyword: str) -> None: self.keyword = keyword def __repr__(self) -> str: s = f"Discretization of type {self.__class__.__name__}" if hasattr(self, "keyword"): s += f" with keyword {self.keyword}" return s
[docs] @abc.abstractmethod def ndof(self, g: pp.Grid) -> int: """ Return the number of degrees of freedom associated to the method. Parameters: g: grid, or a subclass. Return: dof: the number of degrees of freedom. """
[docs] @abc.abstractmethod def discretize(self, g: pp.Grid, data: Dict) -> None: """Construct discretization matrices. The discretization matrices should be added to data[pp.DISCRETIZATION_MATRICES][self.keyword] Parameters: g (pp.Grid): Grid to be discretized. data (dictionary): With discretization parameters. """ pass
[docs] def update_discretization(self, g: pp.Grid, data: Dict) -> None: """Partial update of discretization. Intended use is when the discretization should be updated, e.g. because of changes in parameters, grid geometry or grid topology, and it is not desirable to recompute the discretization on the entire grid. A typical case will be when the discretization operation is costly, and only a minor update is necessary. The updates can generally come as a combination of two forms: 1) The discretization on part of the grid should be recomputed. 2) The old discretization can be used (in parts of the grid), but the numbering of unknowns has changed, and the discretization should be reorder accordingly. By default, this method will simply forward the call to the standard discretize method. Discretization methods that wants a tailored approach should override the standard implementation. Information on the basis for the update should be stored in a field data['update_discretization'] this should be a dictionary with up to six keys. The following optional keys: modified_cells, modified_faces, modified_nodes define cells, faces and nodes that have been modified (either parameters, geometry or topology), and should be rediscretized. It is up to the discretization method to implement the change necessary by this modification. Note that depending on the computational stencil of the discretization method, a grid quantity may be rediscretized even if it is not marked as modified. The dictionary could further have keys: cell_index_map, face_index_map, node_index_map these should specify sparse matrices that maps old to new indices. If not provided, unit mappings should be assumed, that is, no changes to the grid topology are accounted for. It is up to the caller to specify which parts of the grid to recompute, and how to update the numbering of degrees of freedom. If the discretization method does not provide a tailored implementation for update, it is not necessary to provide this information. Parameters: g (pp.Grid): Grid to be rediscretized. data (dictionary): With discretization parameters. """ # Default behavior is to discretize everything self.discretize(g, data)
[docs] @abc.abstractmethod def assemble_matrix_rhs( self, g: pp.Grid, data: Dict ) -> Union[sps.spmatrix, np.ndarray]: """Assemble discretization matrix and rhs vector. Parameters: g (pp.Grid): Grid to be discretized. data (dictionary): With discretization parameters. Returns: sps.csc_matrix: Discretization matrix. np.ndarray: Right hand side term. """ pass
[docs] def assemble_matrix(self, g: pp.Grid, data: Dict) -> sps.spmatrix: """Assemble discretization matrix. The default implementation will assemble both the discretization matrix and the right hand side vector, and return only the former. This behavior is overridden by some discretization methods. Parameters: g (pp.Grid): Grid to be discretized. data (dictionary): With discretization parameters. Returns: sps.csc_matrix: Discretization matrix. """ A, _ = self.assemble_matrix_rhs(g, data) return A
[docs] def assemble_rhs(self, g: pp.Grid, data: Dict) -> np.ndarray: """Assemble right hand side term. The default implementation will assemble both the discretization matrix and the right hand side vector, and return only the latter. This behavior is overridden by some discretization methods. Parameters: g (pp.Grid): Grid to be discretized. data (dictionary): With discretization parameters. Returns: np.ndarray: Right hand side term. """ _, b = self.assemble_matrix_rhs(g, data) return b
[docs] class VoidDiscretization(Discretization): """Do-nothing discretization object. Used if a discretizaiton object is needed for technical reasons, but not really necessary. Attributes: keyword (str): Keyword used to identify parameters and discretization matrices for this object. ndof_cell (int): Number of degrees of freedom per cell in a grid. ndof_face (int): Number of degrees of freedom per face in a grid. ndof_node (int): Number of degrees of freedom per node in a grid. """ def __init__(self, keyword, ndof_cell=0, ndof_face=0, ndof_node=0): """Set the discretization, with the keyword used for storing various information associated with the discretization. Paramemeters: keyword (str): Identifier of all information used for this discretization. ndof_cell (int, optional): Number of degrees of freedom per cell in a grid. Defaults to 0. ndof_face (int, optional): Number of degrees of freedom per face in a grid. Defaults to 0. ndof_node (int, optional): Number of degrees of freedom per node in a grid. Defaults to 0. """ self.keyword = keyword self.ndof_cell = ndof_cell self.ndof_face = ndof_face self.ndof_node = ndof_node def _key(self): """Get the keyword of this object, on a format friendly to access relevant fields in the data dictionary Returns: String, on the form self.keyword + '_'. """ return self.keyword + "_"
[docs] def ndof(self, g): """Abstract method. Return the number of degrees of freedom associated to the method. Parameters g (grid): Computational grid Returns: int: the number of degrees of freedom. """ return ( g.num_cells * self.ndof_cell + g.num_faces * self.ndof_face + g.num_nodes * self.ndof_node )
[docs] def discretize(self, g, data): """Construct discretization matrices. Operation is void for this discretization. Parameters: g (pp.Grid): Grid to be discretized. data (dictionary): With discretization parameters. """ pass
[docs] def assemble_matrix_rhs(self, g, data): """Assemble discretization matrix and rhs vector, both empty. Parameters: g (pp.Grid): Grid to be discretized. data (dictionary): With discretization parameters. Returns: sps.csc_matrix: Of specified dimensions relative to the grid. Empty. np.array: Of specified dimensions relative to the grid. All zeros. """ ndof = self.ndof(g) return sps.csc_matrix((ndof, ndof)), np.zeros(ndof)