def rhs_bound(self, g, data): """ Boundary component of the right hand side. TODO: Boundary effects of coupling terms. Parameters: g: grid, or subclass, with geometry fields computed. data: dictionary to store the data terms. Must have been through a call to discretize() to discretization of right hand side. state: np.ndarray, solution vector from previous time step. Returns: np.ndarray: Contribution to right hand side given the current state. """ d = data["param"].get_bc_val("mechanics") p = data["param"].get_bc_val("flow") div_flow = fvutils.scalar_divergence(g) div_mech = fvutils.vector_divergence(g) p_bound = -div_flow * data["bound_flux"] * p - data["bound_div_d"] * d s_bound = -div_mech * data["bound_stress"] * d return np.hstack((s_bound, p_bound))
def matrix_rhs(self, matrix, g_h, g_l, data_h, data_l, data_edge, discretize=True): """ Computes the coupling terms for the faces between cells in g_h and g_l using the two-point flux approximation. Parameters: g_h and g_l: grid structures of the higher and lower dimensional subdomains, respectively. data_h and data_l: the corresponding data dictionaries. Assumed to contain both permeability values ('perm') and apertures ('apertures') for each of the cells in the grids. Two hidden options (intended as "advanced" options that one should normally not care about): Half transmissibility calculation according to Ivar Aavatsmark, see folk.uib.no/fciia/elliptisk.pdf. Activated by adding the entry 'Aavatsmark_transmissibilities': True to the edge data. Aperture correction. The face centre is moved half an aperture away from the fracture for the matrix side transmissibility calculation. Activated by adding the entry 'aperture_correction': True to the edge data. Returns: cc: Discretization matrices for the coupling terms assembled in a csc.sparse matrix. """ if discretize: self.discretize(g_h, g_l, data_h, data_l, data_edge) # assemble discretization matrix: _, cc = self.create_block_matrix([g_h, g_l, data_edge["mortar_grid"]]) # Contribution from mortar variable to conservation in the higher domain # Acts as a boundary condition, treat with standard boundary discretization # cc[0, 2] = div_h * bound_flux_h * face_areas_h * hat_P.T div_h = fvutils.scalar_divergence(g_h) cc[0, 2] = div_h * data_edge["mortar_to_hat_bc"] # For the lower dimensional grid the mortar act as a source term. This # shows up in the equation as a jump operator: div u - ||lambda|| = 0 # where ||.|| is the jump cc[1, 2] = -data_edge["jump"] # * sps.diags(mg.cell_volumes) # Governing equation for the inter-dimensional flux law. # Equation on the form # \lambda = \kappa (p_h - p_l). The trace of the pressure from # the higher dimension is composed of the cell center pressure, # and a contribution from the boundary flux, represented by the mortar flux # Cell center contribution, mapped to the mortar grid cc[2, 0] = data_edge["hat_P_to_mortar"] # Contribution from the lower dimensional pressure cc[2, 1] = -data_edge["check_P_to_mortar"] # Contribution from mortar # Should we have hat_P_pressure here? # cc[2, 2] += hat_P * bound_pressure_face_h * face_areas_h * hat_P.T cc[2, 2] = data_edge["mortar_weight"] return matrix + cc
def matrix_rhs(self, matrix, g_l, g_r, data_l, data_r, data_edge, discretize=True): """ Computes the coupling terms for the faces between faces in g_l and faces in g_r using the two-point flux approximation. Parameters: g_l and g_r: grid structures of the two subdomains, respectively. data_l and data_r: the corresponding data dictionaries. Returns: cc: Discretization matrices for the coupling terms assembled in a csc.sparse matrix. """ if discretize: self.discretize(g_l, g_r, data_l, data_r, data_edge) # calculate divergence div_l = fvutils.scalar_divergence(g_l) div_r = fvutils.scalar_divergence(g_r) # create block matrix # store discretization matrix if g_l != g_r: _, cc = self.create_block_matrix([g_l, g_r, data_edge["mortar_grid"]]) # first the flux continuity cc[0, 2] = div_l * data_edge["mortar_to_hat_bc"] cc[1, 2] = div_r * data_edge["mortar_to_check_bc"] # then pressure continuity cc[2, 0] = data_edge["hat_P_to_mortar"] cc[2, 1] = -data_edge["check_P_to_mortar"] cc[2, 2] = data_edge["mortar_weight"] else: _, cc = self.create_block_matrix([g_l, data_edge["mortar_grid"]]) # if the edge connect a node to itself the contribution # from the left and right are both at the same node cc[0, 1] = ( div_l * data_edge["mortar_to_hat_bc"] + div_r * data_edge["mortar_to_check_bc"] ) cc[1, 0] = data_edge["hat_P_to_mortar"] - data_edge["check_P_to_mortar"] cc[1, 1] = data_edge["mortar_weight"] return matrix + cc
def matrix_rhs(self, g, data, discretize=True): """ Return the matrix and right-hand side for a discretization of a second order elliptic equation using a FV method with a multi-point flux approximation. The name of data in the input dictionary (data) are: k : second_order_tensor Permeability defined cell-wise. bc : boundary conditions (optional) bc_val : dictionary (optional) Values of the boundary conditions. The dictionary has at most the following keys: 'dir' and 'neu', for Dirichlet and Neumann boundary conditions, respectively. Parameters ---------- g : grid, or a subclass, with geometry fields computed. data: dictionary to store the data. For details on necessary keywords, see method discretize() discretize (boolean, optional): Whether to discetize prior to matrix assembly. If False, data should already contain discretization. Defaults to True. Return ------ matrix: sparse csr (g_num_cells, g_num_cells) Discretization matrix. rhs: array (g_num_cells) Right-hand side which contains the boundary conditions and the scalar source term. """ if discretize: self.discretize(g, data) div = fvutils.scalar_divergence(g) flux = data['flux'] M = div * flux bound_flux = data['bound_flux'] param = data['param'] bc_val = param.get_bc_val(self) return M, self.rhs(g, bound_flux, bc_val)
def matrix_rhs(self, g, data, discretize=True): """ Return the matrix and right-hand side for a discretization of a second order elliptic equation using a FV method with a multi-point flux approximation. The data should contain a parameter class under the field "param". The following parameters will be accessed: get_tensor : SecondOrderTensor. Permeability defined cell-wise. get_bc : boundary conditions get_bc_val : boundary values get_robin_weight : float. Weight for pressure in Robin condition Parameters ---------- g : grid, or a subclass, with geometry fields computed. data: dictionary to store the data. For details on necessary keywords, see method discretize() discretize (boolean, optional): Whether to discetize prior to matrix assembly. If False, data should already contain discretization. Defaults to True. Return ------ matrix: sparse csr (g_num_cells, g_num_cells) Discretization matrix. rhs: array (g_num_cells) Right-hand side which contains the boundary conditions and the scalar source term. """ if discretize: self.discretize(g, data) div = fvutils.scalar_divergence(g) flux = data["flux"] M = div * flux bound_flux = data["bound_flux"] param = data["param"] bc_val = param.get_bc_val(self) return M, self.rhs(g, bound_flux, bc_val)
def assemble_matrix(self, g, data): """ Assemble the poro-elastic system matrix. The discretization is presumed stored in the data dictionary. Parameters: g (grid): Grid for disrcetization data (dictionary): Data for discretization, as well as matrices with discretization of the sub-parts of the system. Returns: scipy.sparse.bmat: Block matrix with the combined MPSA/MPFA discretization. """ div_flow = fvutils.scalar_divergence(g) div_mech = fvutils.vector_divergence(g) param = data["param"] fluid_viscosity = param.fluid_viscosity biot_alpha = param.biot_alpha # Put together linear system A_flow = div_flow * data["flux"] / fluid_viscosity A_mech = div_mech * data["stress"] # Time step size dt = data["dt"] d_scaling = data.get("displacement_scaling", 1) # Matrix for left hand side A_biot = sps.bmat([ [A_mech, data["grad_p"] * biot_alpha], [ data["div_d"] * biot_alpha * d_scaling, data["compr_discr"] + dt * A_flow + data["stabilization"], ], ]).tocsr() return A_biot
def matrix_rhs(self, g, data, faces=None, discretize=True): """ Return the matrix and right-hand side for a discretization of a second order elliptic equation using a FV method with a two-point flux approximation. To set a source see the source.Integral discretization class Parameters ---------- g : grid, or a subclass, with geometry fields computed. data: dictionary to store the data. For details on necessary keywords, see method discretize() discretize (boolean, optional): Whether to discetize prior to matrix assembly. If False, data should already contain discretization. Defaults to True. Return ------ matrix: sparse csr (g_num_cells, g_num_cells) Discretization matrix. rhs: array (g_num_cells) Right-hand side which contains the boundary conditions and the scalar source term. """ div = fvutils.scalar_divergence(g) if discretize: self.discretize(g, data) flux = data['flux'] M = div * flux bound_flux = data['bound_flux'] param = data['param'] bc_val = param.get_bc_val(self) return M, self.rhs(g, bound_flux, bc_val)