def solve(self, gb, method=None): key = "flow" if method is None: discretization = pp.Tpfa(key) elif method == "mpfa": discretization = pp.Mpfa(key) elif method == "mvem": discretization = pp.MVEM(key) assembler = test_utils.setup_flow_assembler(gb, discretization, key) assembler.discretize() A_flow, b_flow = assembler.assemble_matrix_rhs() p = sps.linalg.spsolve(A_flow, b_flow) assembler.distribute_variable(p) if method == "mvem": g2d = gb.grids_of_dimension(2)[0] p_n = np.zeros(gb.num_cells()) for g, d in gb: if g.dim == 2: p_n[:g2d.num_cells] = d[pp.STATE]["pressure"][g.num_faces:] else: p_n[g2d.num_cells:] = d[pp.STATE]["pressure"][g.num_faces:] d[pp.STATE]["pressure"] = d[pp.STATE]["pressure"][g.num_faces:] p = p_n return p
def solve(self): # consturct the matrices self.assembler.discretize() # in the case the fractures are scaled modify the problem if self.data.get("length_ratio", None) is not None: for g, d in self.gb: if g.dim == 1: ratio = self.data["length_ratio"][g.frac_num] d[pp.DISCRETIZATION_MATRICES][self.model]["flux"] *= ratio d[pp.DISCRETIZATION_MATRICES][self.model]["bound_flux"] *= ratio d[pp.DISCRETIZATION_MATRICES][self.model]["bound_pressure_face"] *= ratio d[pp.DISCRETIZATION_MATRICES][self.model]["bound_pressure_cell"] *= ratio A, b = self.assembler.assemble_matrix_rhs() # solve the problem p = sps.linalg.spsolve(A, b) # distribute the variables self.assembler.distribute_variable(p) # split the variables for g, d in self.gb: d[pp.STATE][self.pressure] = d[pp.STATE][self.variable] d[pp.STATE][self.P0_flux] = np.zeros(g.num_cells) d[pp.PARAMETERS][self.model][self.flux] = np.zeros(g.num_faces) # reconstruct the Darcy flux pp.fvutils.compute_darcy_flux(self.gb, keyword=self.model, d_name=self.flux, p_name=self.pressure, lam_name=self.mortar) # split the darcy flux on each grid-bucket grid for _, d in self.gb: d[pp.STATE][self.flux] = d[pp.PARAMETERS][self.model][self.flux] for e, d in self.gb.edges(): _, g_master = self.gb.nodes_of_edge(e) if g_master.dim == 1: ratio = self.data["length_ratio"][g_master.frac_num] d[pp.PARAMETERS][self.model][self.flux] *= ratio d[pp.STATE][self.mortar] = d[pp.PARAMETERS][self.model][self.flux] # export the P0 flux reconstruction pp.project_flux(self.gb, pp.MVEM(self.model), self.flux, self.P0_flux, self.mortar) # compute the module of the flow velocity and the azimuth for g, d in self.gb: P0_flux = d[pp.STATE][self.P0_flux] norm = np.sqrt(np.einsum("ij,ij->j", P0_flux, P0_flux)) d[pp.STATE][self.norm_flux] = norm north = self.data["north"] / np.linalg.norm(self.data["north"]) P0_flux_dir = np.divide(P0_flux, norm) azimuth = np.arctan2(P0_flux_dir[1, :], P0_flux_dir[0, :]) - \ np.arctan2(north[1], north[0]); d[pp.STATE][self.azimuth] = azimuth
def setup_discr_mvem(gb, key="flow"): """ Setup the discretization MVEM. """ discr = pp.MVEM(key) p_trace = pp.CellDofFaceDofMap(key) interface = pp.FluxPressureContinuity(key, discr, p_trace) for g, d in gb: if g.dim == gb.dim_max(): d[pp.PRIMARY_VARIABLES] = {key: {"cells": 1, "faces": 1}} d[pp.DISCRETIZATION] = {key: {"flux": discr}} else: d[pp.PRIMARY_VARIABLES] = {key: {"cells": 1}} d[pp.DISCRETIZATION] = {key: {"flux": p_trace}} for e, d in gb.edges(): g_slave, g_master = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = {key: {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = { "flux": { g_slave: (key, "flux"), g_master: (key, "flux"), e: (key, interface), } } return pp.Assembler(gb), (discr, p_trace)
def main(self, N): Nx = Ny = N # g = structured.CartGrid([Nx, Ny], [2, 2]) g = pp.StructuredTriangleGrid([Nx, Ny], [1, 1]) g.compute_geometry() # co.coarsen(g, 'by_volume') # Assign parameters data = self.add_data(g) # Choose and define the solvers solver_flow = pp.MVEM("flow") solver_flow.discretize(g, data) A_flow, b_flow = solver_flow.assemble_matrix_rhs(g, data) solver_source = pp.DualScalarSource("flow") solver_source.discretize(g, data) A_source, b_source = solver_source.assemble_matrix_rhs(g, data) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) u = solver_flow.extract_flux(g, up, data) p = solver_flow.extract_pressure(g, up, data) # P0u = solver_flow.project_flux(g, u, data, keyword="flow") diam = np.amax(g.cell_diameters()) return diam, self.error_p(g, p)
def run_vem(self, gb): key = "flow" method = pp.MVEM(key) self._solve(gb, method, key) for g, d in gb: d[pp.STATE]["darcy_flux"] = d[pp.STATE]["pressure"][:g.num_faces] d[pp.STATE]["pressure"] = d[pp.STATE]["pressure"][g.num_faces:]
def homo_mvem(g): return { "scheme": pp.MVEM("flow"), "dof": { "cells": 1, "faces": 1 }, "label": "homo_mvem" }
def run_vem(self, gb): solver_flow = pp.MVEM("flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow, b_flow) solver_flow.split(gb, "up", up) solver_flow.extract_p(gb, "up", "pressure") self.verify_cv(gb)
def solve_vem(gb, folder, discr_3d=None): # Choose and define the solvers and coupler flow_discretization = pp.MVEM("flow") source_discretization = pp.DualScalarSource("flow") run_flow(gb, flow_discretization, source_discretization, folder, is_FV=False, discr_3d=discr_3d)
def run_vem(self, gb): key = "flow" method = pp.MVEM(key) assembler = test_utils.setup_flow_assembler(gb, method, key) assembler.discretize() A_flow, b_flow = assembler.assemble_matrix_rhs() p = sps.linalg.spsolve(A_flow, b_flow) assembler.distribute_variable(p) for g, d in gb: d[pp.STATE]["pressure"] = d[pp.STATE]["pressure"][g.num_faces:]
def set_param_flow(self, gb, no_flow=False, kn=1e3, method="mpfa"): # Set up flow field with uniform flow in y-direction kw = "flow" for g, d in gb: parameter_dictionary = {} perm = pp.SecondOrderTensor(kxx=np.ones(g.num_cells)) parameter_dictionary["second_order_tensor"] = perm b_val = np.zeros(g.num_faces) if g.dim == 2: bound_faces = pp.face_on_side(g, ["ymin", "ymax"]) if no_flow: b_val[bound_faces[0]] = 1 b_val[bound_faces[1]] = 1 bound_faces = np.hstack((bound_faces[0], bound_faces[1])) labels = np.array(["dir"] * bound_faces.size) parameter_dictionary["bc"] = pp.BoundaryCondition( g, bound_faces, labels) y_max_faces = pp.face_on_side(g, "ymax")[0] b_val[y_max_faces] = 1 else: parameter_dictionary["bc"] = pp.BoundaryCondition(g) parameter_dictionary["bc_values"] = b_val parameter_dictionary["mpfa_inverter"] = "python" d[pp.PARAMETERS] = pp.Parameters(g, [kw], [parameter_dictionary]) d[pp.DISCRETIZATION_MATRICES] = {"flow": {}} gb.add_edge_props("kn") for e, d in gb.edges(): mg = d["mortar_grid"] flow_dictionary = { "normal_diffusivity": 2 * kn * np.ones(mg.num_cells) } d[pp.PARAMETERS] = pp.Parameters(keywords=["flow"], dictionaries=[flow_dictionary]) d[pp.DISCRETIZATION_MATRICES] = {"flow": {}} discretization_key = kw + "_" + pp.DISCRETIZATION for g, d in gb: # Choose discretization and define the solver if method == "mpfa": discr = pp.Mpfa(kw) elif method == "mvem": discr = pp.MVEM(kw) else: discr = pp.Tpfa(kw) d[discretization_key] = discr for _, d in gb.edges(): d[discretization_key] = pp.RobinCoupling(kw, discr)
def _matrix(self, g, perm, bc, vect): solver = pp.MVEM(keyword="flow") data = pp.initialize_default_data( g, {}, "flow", {"second_order_tensor": perm, "bc": bc, "vector_source": vect}, ) solver.discretize(g, data) return solver.assemble_matrix_rhs(g, data)[1]
def solve(self, method): key = "flow" gb = self.gb if method == "tpfa": discretization = pp.Tpfa(key) elif method == "mpfa": discretization = pp.Mpfa(key) elif method == "mvem": discretization = pp.MVEM(key) assembler = test_utils.setup_flow_assembler(gb, discretization, key) assembler.discretize() A_flow, b_flow = assembler.assemble_matrix_rhs() p = sps.linalg.spsolve(A_flow, b_flow) assembler.distribute_variable(p) return p
def test_convergence_mvem_2d_iso_simplex_exact(self): p_ex = lambda pt: 2 * pt[0, :] - 3 * pt[1, :] - 9 u_ex = np.array([-1, 4, 0]) for i in np.arange(5): g = pp.StructuredTriangleGrid([3 + i] * 2, [1, 1]) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(kxx=kxx, kyy=kxx, kzz=1) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) bc_val = np.zeros(g.num_faces) bc_val[bf] = p_ex(g.face_centers[:, bf]) vect = np.vstack( (g.cell_volumes, g.cell_volumes, np.zeros(g.num_cells)) ).ravel(order="F") solver = pp.MVEM(keyword="flow") specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, "vector_source": vect, } data = pp.initialize_default_data(g, {}, "flow", specified_parameters) solver.discretize(g, data) M, rhs = solver.assemble_matrix_rhs(g, data) up = sps.linalg.spsolve(M, rhs) p = solver.extract_pressure(g, up, data) err = np.sum(np.abs(p - p_ex(g.cell_centers))) self.assertTrue(np.isclose(err, 0)) _ = data[pp.DISCRETIZATION_MATRICES]["flow"][solver.vector_proj_key] u = solver.extract_flux(g, up, data) P0u = solver.project_flux(g, u, data) err = np.sum( np.abs(P0u - np.tile(u_ex, g.num_cells).reshape((3, -1), order="F")) ) self.assertTrue(np.isclose(err, 0))
def hete2(g): if g.dim == 2: scheme = { "scheme": pp.MVEM("flow"), "dof": { "cells": 1, "faces": 1 }, "label": "hete2" } else: scheme = { "scheme": pp.Tpfa("flow"), "dof": { "cells": 1 }, "label": "hete2" } return scheme
def extract(self, x): self.assembler.distribute_variable(x) for g, d in self.gb: d[pp.STATE][self.pressure] = d[pp.STATE][self.variable] d[pp.STATE]["cell_volumes"] = g.cell_volumes pp.fvutils.compute_darcy_flux(self.gb, keyword=self.model, d_name=self.flux, p_name=self.pressure, lam_name=self.mortar) for _, d in self.gb: d[pp.STATE][self.flux] = d[pp.PARAMETERS][self.model][self.flux] # export the P0 flux reconstruction pp.project_flux(self.gb, pp.MVEM(self.model), self.flux, self.P0_flux, self.mortar)
def solve(self, kf, description, is_coarse=False): gb, domain = pp.grid_buckets_2d.benchmark_regular( {"mesh_size_frac": 0.045}, is_coarse) # Assign parameters setup.add_data(gb, domain, kf) key = "flow" method = pp.MVEM(key) coupler = pp.RobinCoupling(key, method) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {"pressure": {"cells": 1, "faces": 1}} d[pp.DISCRETIZATION] = {"pressure": {"diffusive": method}} for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = {"mortar_solution": {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = { "lambda": { g1: ("pressure", "diffusive"), g2: ("pressure", "diffusive"), e: ("mortar_solution", coupler), } } d[pp.DISCRETIZATION_MATRICES] = {"flow": {}} assembler = pp.Assembler(gb) # Discretize assembler.discretize() A, b = assembler.assemble_matrix_rhs() p = sps.linalg.spsolve(A, b) assembler.distribute_variable(p) for g, d in gb: d[pp.STATE]["darcy_flux"] = d[pp.STATE]["pressure"][:g.num_faces] d[pp.STATE]["pressure"] = d[pp.STATE]["pressure"][g.num_faces:]
def flow(gb, param): model = "flow" model_data = data.flow(gb, model, param) # discretization operator name flux_id = "flux" # master variable name variable = "flux_pressure" mortar = "lambda_" + variable # post process variables pressure = "pressure" flux = "darcy_flux" # it has to be this one P0_flux = "P0_flux" # save variable name for the advection-diffusion problem param["pressure"] = pressure param["flux"] = flux param["P0_flux"] = P0_flux param["mortar_flux"] = mortar # discretization operators discr = pp.MVEM(model_data) coupling = pp.RobinCoupling(model_data, discr) # define the dof and discretization for the grids for g, d in gb: d[pp.PRIMARY_VARIABLES] = {variable: {"cells": 1, "faces": 1}} d[pp.DISCRETIZATION] = {variable: {flux_id: discr}} # define the interface terms to couple the grids for e, d in gb.edges(): g_slave, g_master = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = {mortar: {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = { variable: { g_slave: (variable, flux_id), g_master: (variable, flux_id), e: (mortar, coupling) } } # solution of the darcy problem assembler = pp.Assembler() logger.info("Assemble the flow problem") A, b, block_dof, full_dof = assembler.assemble_matrix_rhs(gb) logger.info("done") logger.info("Solve the linear system") up = sps.linalg.spsolve(A, b) logger.info("done") logger.info("Variable post-process") assembler.distribute_variable(gb, up, block_dof, full_dof) for g, d in gb: d[pressure] = discr.extract_pressure(g, d[variable]) d[flux] = discr.extract_flux(g, d[variable]) pp.project_flux(gb, discr, flux, P0_flux, mortar) logger.info("done") return model_data
def test_upwind_example_3(self, if_export=False): ####################### # Simple 2d upwind problem with explicit Euler scheme in time coupled with # a Darcy problem ####################### T = 2 Nx, Ny = 10, 10 def funp_ex(pt): return -np.sin(pt[0]) * np.sin(pt[1]) - pt[0] g = pp.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() # Permeability perm = pp.SecondOrderTensor(kxx=np.ones(g.num_cells)) # Boundaries b_faces = g.get_all_boundary_faces() bc = pp.BoundaryCondition(g, b_faces, ["dir"] * b_faces.size) bc_val = np.zeros(g.num_faces) bc_val[b_faces] = funp_ex(g.face_centers[:, b_faces]) specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, } data = pp.initialize_default_data(g, {}, "flow", specified_parameters) solver = pp.MVEM("flow") solver.discretize(g, data) D_flow, b_flow = solver.assemble_matrix_rhs(g, data) solver_source = pp.DualScalarSource("flow") solver_source.discretize(g, data) D_source, b_source = solver_source.assemble_matrix_rhs(g, data) up = sps.linalg.spsolve(D_flow + D_source, b_flow + b_source) _, u = solver.extract_pressure(g, up, data), solver.extract_flux(g, up, data) # Darcy_Flux dis = u # Boundaries bc = pp.BoundaryCondition(g, b_faces, ["dir"] * b_faces.size) bc_val = np.hstack(([1], np.zeros(g.num_faces - 1))) specified_parameters = {"bc": bc, "bc_values": bc_val, "darcy_flux": dis} data = pp.initialize_default_data(g, {}, "transport", specified_parameters) # Advect solver advect = pp.Upwind("transport") advect.discretize(g, data) U, rhs = advect.assemble_matrix_rhs(g, data) time_step = advect.cfl(g, data) rhs = time_step * rhs U = time_step * U data[pp.PARAMETERS]["transport"]["time_step"] = time_step mass = pp.MassMatrix("transport") mass.discretize(g, data) M, _ = mass.assemble_matrix_rhs(g, data) conc = np.zeros(g.num_cells) M_minus_U = M - U inv_mass = pp.InvMassMatrix("transport") inv_mass.discretize(g, data) invM, _ = inv_mass.assemble_matrix_rhs(g, data) # Loop over the time Nt = int(T / time_step) time = np.empty(Nt) for i in np.arange(Nt): # Update the solution conc = invM.dot((M_minus_U).dot(conc) + rhs) time[i] = time_step * i known = np.array( [ 9.63168200e-01, 8.64054875e-01, 7.25390695e-01, 5.72228235e-01, 4.25640080e-01, 2.99387331e-01, 1.99574336e-01, 1.26276876e-01, 7.59011550e-02, 4.33431230e-02, 3.30416807e-02, 1.13058617e-01, 2.05372538e-01, 2.78382057e-01, 3.14035373e-01, 3.09920132e-01, 2.75024694e-01, 2.23163145e-01, 1.67386939e-01, 1.16897527e-01, 1.06111312e-03, 1.11951850e-02, 3.87907727e-02, 8.38516119e-02, 1.36617802e-01, 1.82773271e-01, 2.10446545e-01, 2.14651936e-01, 1.97681518e-01, 1.66549151e-01, 3.20751341e-05, 9.85780113e-04, 6.07062715e-03, 1.99393042e-02, 4.53237556e-02, 8.00799828e-02, 1.17199623e-01, 1.47761481e-01, 1.64729339e-01, 1.65390555e-01, 9.18585872e-07, 8.08267622e-05, 8.47227168e-04, 4.08879583e-03, 1.26336029e-02, 2.88705048e-02, 5.27841497e-02, 8.10459333e-02, 1.07956484e-01, 1.27665318e-01, 2.51295298e-08, 6.29844122e-06, 1.09361990e-04, 7.56743783e-04, 3.11384414e-03, 9.04446601e-03, 2.03443897e-02, 3.75208816e-02, 5.89595194e-02, 8.11457277e-02, 6.63498510e-10, 4.73075468e-07, 1.33728945e-05, 1.30243418e-04, 7.01905707e-04, 2.55272292e-03, 6.96686157e-03, 1.52290448e-02, 2.78607282e-02, 4.40402650e-02, 1.71197497e-11, 3.47118057e-08, 1.57974045e-06, 2.13489614e-05, 1.48634295e-04, 6.68104990e-04, 2.18444135e-03, 5.58646819e-03, 1.17334966e-02, 2.09744728e-02, 4.37822313e-13, 2.52373622e-09, 1.83589660e-07, 3.40553325e-06, 3.02948532e-05, 1.66504215e-04, 6.45119867e-04, 1.90731440e-03, 4.53436628e-03, 8.99977737e-03, 1.12627412e-14, 1.84486857e-10, 2.13562387e-08, 5.39492977e-07, 6.08223906e-06, 4.05535296e-05, 1.84731221e-04, 6.25871542e-04, 1.66459389e-03, 3.59980231e-03, ] ) assert np.allclose(conc, known)
def solve_two_phase_flow_upwind(g): time = [] sat_err = [] pres_err = [] maxflux = [] xc = g.cell_centers # Permeability tensor k_xx = np.zeros(g.num_cells) k_xy = np.zeros(g.num_cells) k_yy = np.zeros(g.num_cells) k_xx[:] = perm_xx_f(xc[0], xc[1]) k_yy[:] = perm_yy_f(xc[0], xc[1]) k_xy[:] = perm_xy_f(xc[0], xc[1]) perm = pp.SecondOrderTensor(k_xx, kyy=k_yy, kxy=k_xy) # Set type of boundary conditions xf = g.face_centers p_bound = np.zeros(g.num_faces) bnd_faces = g.get_all_boundary_faces() neu_faces = bnd_faces[:-1].copy() dir_faces = np.array([g.num_faces-1]) bot_faces = np.ravel(np.argwhere(g.face_centers[1] < 1e-10)) top_faces = np.ravel(np.argwhere(g.face_centers[1] > domain[1] - 1e-10)) left_faces = np.ravel(np.argwhere(g.face_centers[0] < 1e-10)) right_faces = np.ravel(np.argwhere(g.face_centers[0] > domain[0] - 1e-10)) bound_cond = pp.BoundaryCondition(g, dir_faces, ['dir'] * dir_faces.size) # set value of boundary condition p_bound[neu_faces] = 0 p_bound[dir_faces] = p_f(xf[0, dir_faces], xf[1, dir_faces]) # set rhs rhs1 = np.zeros(g.num_cells) rhs2 = np.zeros(g.num_cells) specified_parameters = {"second_order_tensor": perm, "source": rhs1, "bc": bound_cond, "bc_values": p_bound} data = pp.initialize_default_data(g, {}, "flow", specified_parameters) # Set initial conditions s = np.zeros(g.num_cells) s = s_f(xc[0], xc[1]) s_0 = s_f(xc[0], xc[1]) #pp.plot_grid(g, s_0, figsize=(15, 12), alpha = 0.9, color_map=(0,1)) V_start = np.sum(s*g.cell_volumes) fluxtot = np.zeros(g.num_faces) p_ex = p_f(xc[0], xc[1]) s_ex = s_eq_f(xc[0], xc[1]) time_steps_vec = [] #save = pp.Exporter(g, 'twophase_gcmpfa8_time', folder='plots') #save.write_vtk({"s": s}, 0) # Gravity term # < K > * rho_alpha * g gforce = np.zeros((2, g.num_cells)) gforce[0,:] = gx * np.ones(g.num_cells) gforce[1,:] = gy * np.ones(g.num_cells) gforce = gforce.ravel('F') g1 = rho1 * gforce g2 = rho2 * gforce flux_g1 = standard_discr(g, perm, g1) flux_g2 = standard_discr(g, perm, g2) # mpfa discretization flux, bound_flux, _, _ = pp.Mpfa("flow")._local_discr( g, perm, bound_cond, inverter="python" ) div = pp.fvutils.scalar_divergence(g) # define iteration parameters t = .0 k = 0 i = 0 time_steps_vec.append(t) time_step = 5 courant_max = 0.05 while t < T: s_old = s.copy() # Increment time k += 1 t += time_step # discretize gravity term kr1, kr2, f2, f1 = get_phase_mobility(g, s, fluxtot, flux_g1, flux_g2) fluxtot_g = kr1 * flux_g1 + kr2 * flux_g2 p_bound[neu_faces] = fluxtot[neu_faces] - fluxtot_g[neu_faces] p_bound[bot_faces] *= -1 p_bound[left_faces] *= -1 a = div * (kr1 + kr2) * flux b = (rhs1 + rhs2) - div * bound_flux * p_bound - div * fluxtot_g p = sps.linalg.spsolve(a, b) fluxtot = (kr1 + kr2) * flux * p + bound_flux * p_bound + fluxtot_g assert np.all(abs(fluxtot[neu_faces]) < thresh) assert np.all(abs(fluxtot[dir_faces]) < 1.0e-16) if pert == 0: assert np.all(fluxtot < thresh) threshold_indices = abs(fluxtot) < thresh fluxtot[threshold_indices] = 0 maxfluxtot = np.amax(np.absolute(fluxtot)) maxflux.append(maxfluxtot) # update s q2 = f2 * (fluxtot + kr1 * (flux_g2 - flux_g1)) q1 = f1 * (fluxtot + kr2 * (flux_g1 - flux_g2)) q2[bnd_faces]=0 q1[bnd_faces]=0 assert np.allclose(q1+q2, fluxtot) # check courant number q2max = np.zeros(g.num_cells) q1max = np.zeros(g.num_cells) for j in range (0, g.num_cells): faces_of_cell = g.cell_faces.indices[g.cell_faces.indptr[j] : g.cell_faces.indptr[j+1]] for i in range (0, faces_of_cell.size): q2max[j] += np.absolute(q2[faces_of_cell[i]]) / g.cell_volumes[j] q1max[j] += np.absolute(q1[faces_of_cell[i]]) / g.cell_volumes[j] courant = 0.5 * max(np.amax(q2max), np.amax(q1max)) * time_step div_q2 = div * q2 threshold_indices = abs(div_q2) < thresh div_q2[threshold_indices] = 0 s = s + time_step / phi / g.cell_volumes * (rhs2 - div_q2) if not np.all(s >= 0.0): inds=np.argwhere(s<0.0) print(k, inds, s[inds]) assert False if not np.all(s <= 1.0): inds=np.argwhere(s>1.0) #print(k, inds, s[inds]) assert np.all(s[inds] < 1. + 1.0e-12) s[inds] = 1. #assert False mass_error = (np.sum(s*g.cell_volumes) - np.sum(s*g.cell_volumes)) / np.sum(s*g.cell_volumes) if abs(mass_error) > tol_mc: print('error in mass conservation') print(t, mass_error) break s_diff = s - s_ex s_err=np.sqrt(np.sum(g.cell_volumes * s_diff**2))/np.sqrt(np.sum(g.cell_volumes * s_ex**2)) p_diff = p - p_ex p_err=np.sqrt(np.sum(g.cell_volumes * p_diff**2))/np.sqrt(np.sum(g.cell_volumes * p_ex**2)) sat_err.append(s_err) pres_err.append(p_err) time.append(t) #time_steps.append(t) #save.write_vtk({"s": s}, time_step=k) if k % 100 == 0: print(t, mass_error, maxfluxtot, s_err, p_err) F = courant_max / courant if F > 1.: adjust_time_step = min(min(F, 1. + 0.1 * F), 1.2) time_step *= adjust_time_step print(time_step) #time_steps_vec.append(t) #i += 1 """ if k % 500 == 0 and t < 1.0e8: Q_n = fluxtot * g.face_normals solver_flow = pp.MVEM("flow") P0u = solver_flow.project_flux(g, fluxtot, data) #pp.plot_grid(g, p, P0u * 5e7, figsize=(15, 12)) #pp.plot_grid(g, s_0, figsize=(15, 12), alpha = 0.9, color_map=(0,1)) pp.plot_grid(g, s, vector_value=Q_n, figsize=(15, 12), vector_scale=1e7, alpha = 0.9, color_map=(0,1)) #save.write_vtk({"s": s}, time_step=i) Q_n = fluxtot * g.face_normals pp.plot_grid(g, vector_value=Q_n, figsize=(15, 12), vector_scale=5e7) Q_1 = q1 * g.face_normals pp.plot_grid(g, vector_value=Q_1, figsize=(15, 12), vector_scale=5e7) Q_2 = q2 * g.face_normals pp.plot_grid(g, vector_value=Q_2, figsize=(15, 12), vector_scale=5e7) """ #save.write_pvd(np.array(time_steps_vec)) p_diff = p - p_ex s_diff = s - s_ex p_err=np.sqrt(np.sum(g.cell_volumes * p_diff**2))/np.sqrt(np.sum(g.cell_volumes * p_ex**2)) s_err=np.sqrt(np.sum(g.cell_volumes * s_diff**2))/np.sqrt(np.sum(g.cell_volumes * s_ex**2)) mass_error = (np.sum(s*g.cell_volumes) - np.sum(s*g.cell_volumes)) / np.sum(s*g.cell_volumes) print('error in mass conservation', mass_error) print('error in saturation ', s_err) print('error in pressure ', p_err) pp.plot_grid(g, s, figsize=(15, 12)) pp.plot_grid(g, p, figsize=(15, 12)) Q_n = fluxtot * g.face_normals solver_flow = pp.MVEM("flow") P0u = solver_flow.project_flux(g, fluxtot, data) #pp.plot_grid(g, p, P0u * 5e7, figsize=(15, 12)) #pp.plot_grid(g, s_0, figsize=(15, 12), alpha = 0.9, color_map=(0,1)) #pp.plot_grid(g, s, vector_value=Q_n, figsize=(15, 12), vector_scale=1e7, alpha = 0.9, color_map=(0,1)) #save.write_vtk({"s": s}, time_step=i) #save.write_vtk({"p": p, 's_0' : s_0, 's_f' : s}) return time, sat_err, pres_err, maxflux
def matrix_rhs(self, g, data): """ Return the matrix and righ-hand side for a discretization of a second order elliptic equation using hybrid dual virtual element method. The name of data in the input dictionary (data) are: perm : tensor.SecondOrderTensor Permeability defined cell-wise. If not given a identity permeability is assumed and a warning arised. source : array (self.g.num_cells) Scalar source term defined cell-wise. If not given a zero source term is assumed and a warning arised. 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. Return ------ matrix: sparse csr (g.num_faces+g_num_cells, g.num_faces+g_num_cells) Saddle point matrix obtained from the discretization. rhs: array (g.num_faces+g_num_cells) Right-hand side which contains the boundary conditions and the scalar source term. Examples -------- b_faces_neu = ... # id of the Neumann faces b_faces_dir = ... # id of the Dirichlet faces bnd = bc.BoundaryCondition(g, np.hstack((b_faces_dir, b_faces_neu)), ['dir']*b_faces_dir.size + ['neu']*b_faces_neu.size) bnd_val = {'dir': fun_dir(g.face_centers[:, b_faces_dir]), 'neu': fun_neu(f.face_centers[:, b_faces_neu])} data = {'perm': perm, 'source': f, 'bc': bnd, 'bc_val': bnd_val} H, rhs = hybrid.matrix_rhs(g, data) l = sps.linalg.spsolve(H, rhs) u, p = hybrid.compute_up(g, l, data) P0u = dual.project_u(g, perm, u) """ # pylint: disable=invalid-name # If a 0-d grid is given then we return an identity matrix if g.dim == 0: return sps.identity(self.ndof(g), format="csr"), np.zeros(1) parameter_dictionary = data[pp.PARAMETERS][self.keyword] k = parameter_dictionary["second_order_tensor"] f = parameter_dictionary["source"] bc = parameter_dictionary["bc"] bc_val = parameter_dictionary["bc_values"] a = parameter_dictionary["aperture"] faces, _, sgn = sps.find(g.cell_faces) # Map the domain to a reference geometry (i.e. equivalent to compute # surface coordinates in 1d and 2d) c_centers, f_normals, f_centers, _, _, _ = pp.map_geometry.map_grid(g) # Weight for the stabilization term diams = g.cell_diameters() weight = np.power(diams, 2 - g.dim) # Allocate the data to store matrix entries, that's the most efficient # way to create a sparse matrix. size = np.sum( np.square(g.cell_faces.indptr[1:] - g.cell_faces.indptr[:-1])) row = np.empty(size, dtype=np.int) col = np.empty(size, dtype=np.int) data = np.empty(size) rhs = np.zeros(g.num_faces) idx = 0 # Use a dummy keyword to trick the constructor of dualVEM. massHdiv = pp.MVEM("dummy").massHdiv # define the function to compute the inverse of the permeability matrix if g.dim == 1: inv_matrix = DualElliptic._inv_matrix_1d elif g.dim == 2: inv_matrix = DualElliptic._inv_matrix_2d elif g.dim == 3: inv_matrix = DualElliptic._inv_matrix_3d for c in np.arange(g.num_cells): # For the current cell retrieve its faces loc = slice(g.cell_faces.indptr[c], g.cell_faces.indptr[c + 1]) faces_loc = faces[loc] ndof = faces_loc.size # Retrieve permeability and normals assumed outward to the cell. sgn_loc = sgn[loc].reshape((-1, 1)) normals = np.multiply(np.tile(sgn_loc.T, (g.dim, 1)), f_normals[:, faces_loc]) # Compute the H_div-mass local matrix A = massHdiv( k.values[0:g.dim, 0:g.dim, c], inv_matrix(k.values[0:g.dim, 0:g.dim, c]), c_centers[:, c], a[c] * g.cell_volumes[c], f_centers[:, faces_loc], a[c] * normals, np.ones(ndof), diams[c], weight[c], )[0] # Compute the Div local matrix B = -np.ones((ndof, 1)) # Compute the hybrid local matrix C = np.eye(ndof, ndof) # Perform the static condensation to compute the hybrid local matrix invA = np.linalg.inv(A) S = 1 / np.dot(B.T, np.dot(invA, B)) L = np.dot(np.dot(invA, np.dot(B, np.dot(S, B.T))), invA) L = np.dot(np.dot(C.T, L - invA), C) # Compute the local hybrid right using the static condensation rhs[faces_loc] += np.dot(C.T, np.dot(invA, np.dot(B, np.dot(S, f[c]))))[:, 0] # Save values for hybrid matrix indices = np.tile(faces_loc, (faces_loc.size, 1)) loc_idx = slice(idx, idx + indices.size) row[loc_idx] = indices.T.ravel() col[loc_idx] = indices.ravel() data[loc_idx] = L.ravel() idx += indices.size # construct the global matrices H = sps.coo_matrix((data, (row, col))).tocsr() # Apply the boundary conditions if bc is not None: if np.any(bc.is_dir): norm = sps.linalg.norm(H, np.inf) is_dir = np.where(bc.is_dir)[0] H[is_dir, :] *= 0 H[is_dir, is_dir] = norm rhs[is_dir] = norm * bc_val[is_dir] if np.any(bc.is_neu): faces, _, sgn = sps.find(g.cell_faces) sgn = sgn[np.unique(faces, return_index=True)[1]] is_neu = np.where(bc.is_neu)[0] rhs[is_neu] += sgn[is_neu] * bc_val[is_neu] * g.face_areas[ is_neu] return H, rhs
def test_convergence_mvem_2d_iso_simplex(self): a = 8 * np.pi ** 2 rhs_ex = lambda pt: np.multiply( np.sin(2 * np.pi * pt[0, :]), np.sin(2 * np.pi * pt[1, :]) ) p_ex = lambda pt: rhs_ex(pt) / a u_ex_0 = ( lambda pt: np.multiply( -np.cos(2 * np.pi * pt[0, :]), np.sin(2 * np.pi * pt[1, :]) ) * 2 * np.pi / a + 1 ) u_ex_1 = ( lambda pt: np.multiply( -np.sin(2 * np.pi * pt[0, :]), np.cos(2 * np.pi * pt[1, :]) ) * 2 * np.pi / a ) p_errs_known = np.array( [ 0.007347293666843033, 0.004057878042430692, 0.002576479539795832, 0.0017817307824819935, 0.0013057660031758425, ] ) u_errs_known = np.array( [ 0.024425617686195774, 0.016806807988931565, 0.012859109258624922, 0.010445238111710832, 0.00881184436169123, ] ) for i, p_err_known, u_err_known in zip( np.arange(5), p_errs_known, u_errs_known ): g = pp.StructuredTriangleGrid([3 + i] * 2, [1, 1]) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(kxx=kxx, kyy=kxx, kzz=1) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) bc_val = np.zeros(g.num_faces) bc_val[bf] = p_ex(g.face_centers[:, bf]) # Minus sign to move to rhs source = np.multiply(g.cell_volumes, rhs_ex(g.cell_centers)) vect = np.vstack( (g.cell_volumes, np.zeros(g.num_cells), np.zeros(g.num_cells)) ).ravel(order="F") solver = pp.MVEM(keyword="flow") solver_rhs = pp.DualScalarSource(keyword="flow") specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, "source": source, "vector_source": vect, } data = pp.initialize_default_data(g, {}, "flow", specified_parameters) solver.discretize(g, data) solver_rhs.discretize(g, data) M, rhs_bc = solver.assemble_matrix_rhs(g, data) _, rhs = solver_rhs.assemble_matrix_rhs(g, data) up = sps.linalg.spsolve(M, rhs_bc + rhs) p = solver.extract_pressure(g, up, data) err = np.sqrt( np.sum( np.multiply(g.cell_volumes, np.power(p - p_ex(g.cell_centers), 2)) ) ) self.assertTrue(np.isclose(err, p_err_known)) _ = data[pp.DISCRETIZATION_MATRICES]["flow"][solver.vector_proj_key] u = solver.extract_flux(g, up, data) P0u = solver.project_flux(g, u, data) uu_ex_0 = u_ex_0(g.cell_centers) uu_ex_1 = u_ex_1(g.cell_centers) uu_ex_2 = np.zeros(g.num_cells) uu_ex = np.vstack((uu_ex_0, uu_ex_1, uu_ex_2)) err = np.sqrt( np.sum( np.multiply( g.cell_volumes, np.sum(np.power(P0u - uu_ex, 2), axis=0) ) ) ) self.assertTrue(np.isclose(err, u_err_known))
def test_convergence_mvem_2d_ani_simplex(self): rhs_ex = lambda pt: 14 p_ex = ( lambda pt: 2 * np.power(pt[0, :], 2) - 6 * np.power(pt[1, :], 2) + np.multiply(pt[0, :], pt[1, :]) ) u_ex_0 = lambda pt: -9 * pt[0, :] + 10 * pt[1, :] + 4 u_ex_1 = lambda pt: -6 * pt[0, :] + 23 * pt[1, :] + 5 p_errs_known = np.array( [ 0.2411784823808065, 0.13572349427526526, 0.08688469978140642, 0.060345813825004285, 0.044340156291519606, ] ) u_errs_known = np.array( [ 1.7264059760345327, 1.3416423116340397, 1.0925566034251672, 0.9198698104736416, 0.7936243780450764, ] ) for i, p_err_known, u_err_known in zip( np.arange(5), p_errs_known, u_errs_known ): g = pp.StructuredTriangleGrid([3 + i] * 2, [1, 1]) g.compute_geometry() kxx = 2 * np.ones(g.num_cells) kxy = np.ones(g.num_cells) perm = pp.SecondOrderTensor(kxx=kxx, kyy=kxx, kxy=kxy, kzz=1) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) bc_val = np.zeros(g.num_faces) bc_val[bf] = p_ex(g.face_centers[:, bf]) # Minus sign to move to rhs source = np.multiply(g.cell_volumes, rhs_ex(g.cell_centers)) vect = np.vstack( (g.cell_volumes, 2 * g.cell_volumes, np.zeros(g.num_cells)) ).ravel(order="F") solver = pp.MVEM(keyword="flow") solver_rhs = pp.DualScalarSource(keyword="flow") specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, "source": source, "vector_source": vect, } data = pp.initialize_default_data(g, {}, "flow", specified_parameters) solver.discretize(g, data) solver_rhs.discretize(g, data) M, rhs_bc = solver.assemble_matrix_rhs(g, data) _, rhs = solver_rhs.assemble_matrix_rhs(g, data) up = sps.linalg.spsolve(M, rhs_bc + rhs) p = solver.extract_pressure(g, up, data) err = np.sqrt( np.sum( np.multiply(g.cell_volumes, np.power(p - p_ex(g.cell_centers), 2)) ) ) self.assertTrue(np.isclose(err, p_err_known)) P = data[pp.DISCRETIZATION_MATRICES]["flow"][solver.vector_proj_key] u = solver.extract_flux(g, up, data) P0u = solver.project_flux(g, u, data) uu_ex_0 = u_ex_0(g.cell_centers) uu_ex_1 = u_ex_1(g.cell_centers) uu_ex_2 = np.zeros(g.num_cells) uu_ex = np.vstack((uu_ex_0, uu_ex_1, uu_ex_2)) err = np.sqrt( np.sum( np.multiply( g.cell_volumes, np.sum(np.power(P0u - uu_ex, 2), axis=0) ) ) ) self.assertTrue(np.isclose(err, u_err_known))