def test_periodic_bc(self): """ We set up the test case P |--------| | | D | g2 | D | | |--------| P where D are dirichlet boundaries and P the periodic boundaries. We construct periodic solution p = sin(pi/xmax * (x - x0)) * cos(2*pi/ymax * (y - y0)) which gives a source term on the rhs. """ n = 8 xmax = 1 ymax = 1 gb = self.generate_2d_grid(n, xmax, ymax) tol = 1e-6 def analytic_p(x): x = x.copy() shiftx = xmax / 4 shifty = ymax / 3 x[0] = x[0] - shiftx x[1] = x[1] - shifty p = np.sin(np.pi / xmax * x[0]) * np.cos(2 * np.pi / ymax * x[1]) px = ( (np.pi / xmax) * np.cos(np.pi / xmax * x[0]) * np.cos(2 * np.pi / ymax * x[1]) ) py = ( 2 * np.pi / ymax * np.sin(np.pi / xmax * x[0]) * np.sin(2 * np.pi / ymax * x[1]) ) pxx = -(np.pi / xmax) ** 2 * p pyy = -(2 * np.pi / ymax) ** 2 * p return p, np.vstack([px, py]), pxx + pyy for g, d in gb: left = g.face_centers[0] < tol right = g.face_centers[0] > xmax - tol dir_bc = left + right bound = pp.BoundaryCondition(g, dir_bc, "dir") bc_val = np.zeros(g.num_faces) bc_val[dir_bc], _, _ = analytic_p(g.face_centers[:, dir_bc]) pa, _, lpc = analytic_p(g.cell_centers) src = -lpc * g.cell_volumes specified_parameters = {"bc": bound, "bc_values": bc_val, "source": src} pp.initialize_default_data(g, d, "flow", specified_parameters) for _, d in gb.edges(): pp.params.data.add_discretization_matrix_keyword(d, "flow") self.solve(gb, analytic_p)
def setup_cart_2d(nx): frac1 = np.array([[0.2, 0.8], [0.5, 0.5]]) frac2 = np.array([[0.5, 0.5], [0.8, 0.2]]) fracs = [frac1, frac2] gb = pp.meshing.cart_grid(fracs, nx, physdims=[1, 1]) gb.compute_geometry() gb.assign_node_ordering() kw = "flow" aperture = 0.01 / np.max(nx) for g, d in gb: a = np.power(aperture, gb.dim_max() - g.dim) * np.ones(g.num_cells) kxx = np.ones(g.num_cells) * a perm = pp.SecondOrderTensor(kxx) specified_parameters = {"second_order_tensor": perm} if g.dim == 2: bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] bound = pp.BoundaryCondition(g, bound_faces.ravel("F"), ["dir"] * bound_faces.size) bc_val = np.zeros(g.num_faces) bc_val[bound_faces] = g.face_centers[1, bound_faces] specified_parameters.update({"bc": bound, "bc_values": bc_val}) # Initialize data and matrix dictionaries in d pp.initialize_default_data(g, d, kw, specified_parameters) for e, d in gb.edges(): # Compute normal permeability gl, _ = gb.nodes_of_edge(e) kn = 1.0 / aperture data = {"normal_diffusivity": kn} # Add parameters d[pp.PARAMETERS] = pp.Parameters(keywords=[kw], dictionaries=[data]) # Initialize matrix dictionary d[pp.DISCRETIZATION_MATRICES] = {kw: {}} return gb
def setup_biot(self): g = pp.CartGrid([5, 5]) g.compute_geometry() stiffness = pp.FourthOrderTensor(np.ones(g.num_cells), np.ones(g.num_cells)) bnd = pp.BoundaryConditionVectorial(g) specified_data = { "fourth_order_tensor": stiffness, "bc": bnd, "inverter": "python", "biot_alpha": 1, } keyword_mech = "mechanics" keyword_flow = "flow" data = pp.initialize_default_data(g, {}, keyword_mech, specified_parameters=specified_data) data = pp.initialize_default_data(g, data, keyword_flow) discr = pp.Biot() discr.discretize(g, data) div_u = data[pp.DISCRETIZATION_MATRICES][keyword_flow][ discr.div_u_matrix_key] bound_div_u = data[pp.DISCRETIZATION_MATRICES][keyword_flow][ discr.bound_div_u_matrix_key] stab = data[pp.DISCRETIZATION_MATRICES][keyword_flow][ discr.stabilization_matrix_key] grad_p = data[pp.DISCRETIZATION_MATRICES][keyword_mech][ discr.grad_p_matrix_key] bound_pressure = data[pp.DISCRETIZATION_MATRICES][keyword_mech][ discr.bound_pressure_matrix_key] return g, stiffness, bnd, div_u, bound_div_u, grad_p, stab, bound_pressure
def test_two_cart_grids(self): """ We set up the test case -----|--------- | | | | g1 | g2 | | | | -----|--------- with a linear pressure increase from left to right """ n = 2 xmax = 3 ymax = 1 split = 2 gb = self.generate_grids(n, xmax, ymax, split) tol = 1e-6 for g, d in gb: left = g.face_centers[0] < tol right = g.face_centers[0] > xmax - tol dir_bc = left + right bound = pp.BoundaryCondition(g, dir_bc, "dir") bc_val = np.zeros(g.num_faces) bc_val[left] = xmax bc_val[right] = 0 specified_parameters = {"bc": bound, "bc_values": bc_val} pp.initialize_default_data(g, d, "flow", specified_parameters) for e, d in gb.edges(): mg = d["mortar_grid"] d[pp.PARAMETERS] = pp.Parameters(mg, ["flow"], [{}]) pp.params.data.add_discretization_matrix_keyword(d, "flow") # assign discretization data_key = "flow" tpfa = pp.Tpfa(data_key) coupler = pp.FluxPressureContinuity(data_key, tpfa) assembler = test_utils.setup_flow_assembler(gb, tpfa, data_key, coupler=coupler) test_utils.solve_and_distribute_pressure(gb, assembler) # test pressure for g, d in gb: self.assertTrue( np.allclose(d[pp.STATE]["pressure"], xmax - g.cell_centers[0])) # test mortar solution for e, d_e in gb.edges(): mg = d_e["mortar_grid"] g2, g1 = gb.nodes_of_edge(e) master_to_m = mg.master_to_mortar_avg() slave_to_m = mg.slave_to_mortar_avg() master_area = master_to_m * g1.face_areas slave_area = slave_to_m * g2.face_areas self.assertTrue( np.allclose(d_e[pp.STATE]["mortar_flux"] / master_area, 1)) self.assertTrue( np.allclose(d_e[pp.STATE]["mortar_flux"] / slave_area, 1))
def set_parameters_cell_basis(self, gb: pp.GridBucket, data: Dict): """ Assign parameters for the micro gb. Very simple for now, this must be improved. Args: gb (TYPE): the micro gb. Returns: None. """ # First initialize data for g, d in gb: d["Aavatsmark_transmissibilities"] = True domain_boundary = np.logical_and( g.tags["domain_boundary_faces"], np.logical_not(g.tags["fracture_faces"]), ) boundary_faces = np.where(domain_boundary)[0] if domain_boundary.size > 0: bc_type = boundary_faces.size * ["dir"] else: bc_type = np.empty(0) bc = pp.BoundaryCondition(g, boundary_faces, bc_type) if hasattr(g, "face_on_macro_bound"): micro_ind = g.face_on_macro_bound macro_ind = g.macro_face_ind bc.is_neu[micro_ind] = data["bc_macro"]["bc"].is_neu[macro_ind] bc.is_dir[micro_ind] = data["bc_macro"]["bc"].is_dir[macro_ind] param = {"bc": bc} perm = data["g_data"](g)["second_order_tensor"] param["second_order_tensor"] = perm param["specific_volume"] = data["g_data"](g)["specific_volume"] # Use python inverter for mpfa for small problems, where it does not pay off # to fire up numba. The set threshold value is somewhat randomly picked. if g.num_cells < 100: param["mpfa_inverter"] = "python" pp.initialize_default_data(g, d, self.keyword, param) for e, d in gb.edges(): mg = d["mortar_grid"] g1, g2 = gb.nodes_of_edge(e) param = {} if not hasattr(g1, "is_auxiliary") or not g1.is_auxiliary: check_P = mg.secondary_to_mortar_avg() param.update(data["e_data"](mg, g1, g2, check_P)) pp.initialize_data(mg, d, self.keyword, param)
def add_data(gb, domain, kf): """ Define the permeability, apertures, boundary conditions """ gb.add_node_props(["param", "is_tangential"]) tol = 1e-5 a = 1e-4 for g, d in gb: # Assign aperture a_dim = np.power(a, gb.dim_max() - g.dim) aperture = np.ones(g.num_cells) * a_dim # Effective permeability, scaled with aperture. kxx = np.ones(g.num_cells) * np.power(kf, g.dim < gb.dim_max()) * aperture if g.dim == 2: perm = pp.SecondOrderTensor(kxx=kxx, kyy=kxx, kzz=1) else: perm = pp.SecondOrderTensor(kxx=kxx, kyy=1, kzz=1) specified_parameters = {"second_order_tensor": perm} # Boundaries bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] if bound_faces.size != 0: bound_face_centers = g.face_centers[:, bound_faces] left = bound_face_centers[0, :] < domain["xmin"] + tol right = bound_face_centers[0, :] > domain["xmax"] - tol labels = np.array(["neu"] * bound_faces.size) labels[right] = "dir" bc_val = np.zeros(g.num_faces) bc_val[ bound_faces[left]] = -a_dim * g.face_areas[bound_faces[left]] bc_val[bound_faces[right]] = 1 bound = pp.BoundaryCondition(g, bound_faces, labels) specified_parameters.update({"bc": bound, "bc_values": bc_val}) else: bound = pp.BoundaryCondition(g, np.empty(0), np.empty(0)) specified_parameters.update({"bc": bound}) d["is_tangential"] = True pp.initialize_default_data(g, d, "flow", specified_parameters) # Assign coupling permeability for _, d in gb.edges(): mg = d["mortar_grid"] kn = 2 * kf * np.ones(mg.num_cells) / a d[pp.PARAMETERS] = pp.Parameters(mg, ["flow"], [{ "normal_diffusivity": kn }]) d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}
def test_mpsa(self): self.setup() g, g_larger = self.g, self.g_larger specified_data = { "inverter": "python", } keyword = "mechanics" data_small = pp.initialize_default_data( g, {}, keyword, specified_parameters=specified_data) discr = pp.Mpsa(keyword) # Discretization on a small problem discr.discretize(g, data_small) # Perturb one node g_larger.nodes[0, 2] += 0.2 # Faces that have their geometry changed update_faces = np.array([2, 21, 22]) # Perturb the permeability in some cells on the larger grid mu, lmbda = np.ones(g_larger.num_cells), np.ones(g_larger.num_cells) high_coeff_cells = np.array([7, 12]) stiff_larger = pp.FourthOrderTensor(mu, lmbda) specified_data_larger = {"fourth_order_tensor": stiff_larger} # Do a full discretization on the larger grid data_full = pp.initialize_default_data( g_larger, {}, keyword, specified_parameters=specified_data_larger) discr.discretize(g_larger, data_full) # Cells that will be marked as updated, either due to changed parameters or # the newly defined topology update_cells = np.union1d(self.new_cells, high_coeff_cells) updates = { "modified_cells": update_cells, # "modified_faces": update_faces, "map_cells": self.cell_map, "map_faces": self.face_map, } # Data dictionary for the two-step discretization data_partial = pp.initialize_default_data( g_larger, {}, keyword, specified_parameters=specified_data_larger) data_partial["update_discretization"] = updates self._update_and_compare(data_small, data_partial, data_full, g_larger, [keyword], discr)
def test_inv_mass_matrix_1d(self): """ Test the inverse of mass matrix in 1d for a simple geometry """ g = pp.CartGrid(3, 1) g.compute_geometry() # Mass weight is scaled by aperture 0.01 specified_parameters = {"mass_weight": 0.5 * 1e-2} data = pp.initialize_default_data(g, {}, "flow", specified_parameters) discr = pp.MixedInvMassMatrix() discr.discretize(g, data) lhs, rhs = discr.assemble_matrix_rhs(g, data) lhs_known = 600 * np.array( [ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], ] ) self.assertTrue(np.allclose(lhs.toarray(), lhs_known)) self.assertTrue(np.allclose(rhs, 0))
def test_convergence_rt0_2d_iso_simplex_exact(self): p_ex = lambda pt: 2 * pt[0, :] - 3 * pt[1, :] - 9 for i in np.arange(5): g = pp.simplex.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]) solver = pp.RT0(keyword="flow") specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, } 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))
def test_upwind_2d_cart_darcy_flux_positive(self): g = pp.CartGrid([3, 2], [1, 1]) g.compute_geometry() solver = pp.Upwind() dis = solver.darcy_flux(g, [2, 0, 0]) bf = g.tags["domain_boundary_faces"].nonzero()[0] bc = pp.BoundaryCondition(g, bf, bf.size * ["neu"]) specified_parameters = {"bc": bc, "darcy_flux": dis} data = pp.initialize_default_data(g, {}, "transport", specified_parameters) solver.discretize(g, data) M = solver.assemble_matrix_rhs(g, data)[0].todense() deltaT = solver.cfl(g, data) M_known = np.array( [ [1, 0, 0, 0, 0, 0], [-1, 1, 0, 0, 0, 0], [0, -1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, -1, 1, 0], [0, 0, 0, 0, -1, 0], ] ) deltaT_known = 1 / 12 rtol = 1e-15 atol = rtol self.assertTrue(np.allclose(M, M_known, rtol, atol)) self.assertTrue(np.allclose(deltaT, deltaT_known, rtol, atol))
def test_upwind_1d_darcy_flux_negative_bc_neu(self): g = pp.CartGrid(3, 1) g.compute_geometry() solver = pp.Upwind() dis = solver.darcy_flux(g, [-2, 0, 0]) bf = g.tags["domain_boundary_faces"].nonzero()[0] bc = pp.BoundaryCondition(g, bf, bf.size * ["neu"]) bc_val = np.array([2, 0, 0, -2]).ravel("F") specified_parameters = {"bc": bc, "bc_values": bc_val, "darcy_flux": dis} data = pp.initialize_default_data(g, {}, "transport", specified_parameters) solver.discretize(g, data) M, rhs = solver.assemble_matrix_rhs(g, data) deltaT = solver.cfl(g, data) M_known = np.array([[0, -2, 0], [0, 2, -2], [0, 0, 2]]) rhs_known = np.array([-2, 0, 2]) deltaT_known = 1 / 12 rtol = 1e-15 atol = rtol self.assertTrue(np.allclose(M.todense(), M_known, rtol, atol)) self.assertTrue(np.allclose(rhs, rhs_known, rtol, atol)) self.assertTrue(np.allclose(deltaT, deltaT_known, rtol, atol))
def test_upwind_2d_simplex_surf_darcy_flux_negative(self): g = pp.StructuredTriangleGrid([2, 1], [1, 1]) R = pp.map_geometry.rotation_matrix(-np.pi / 5.0, [1, 1, -1]) g.nodes = np.dot(R, g.nodes) g.compute_geometry() solver = pp.Upwind() dis = solver.darcy_flux(g, np.dot(R, [-1, 0, 0])) bf = g.tags["domain_boundary_faces"].nonzero()[0] bc = pp.BoundaryCondition(g, bf, bf.size * ["neu"]) specified_parameters = {"bc": bc, "darcy_flux": dis} data = pp.initialize_default_data(g, {}, "transport", specified_parameters) solver.discretize(g, data) M = solver.assemble_matrix_rhs(g, data)[0].todense() deltaT = solver.cfl(g, data) M_known = np.array([[1, 0, 0, -1], [-1, 0, 0, 0], [0, 0, 1, 0], [0, 0, -1, 1]]) deltaT_known = 1 / 6 rtol = 1e-15 atol = rtol self.assertTrue(np.allclose(M, M_known, rtol, atol)) self.assertTrue(np.allclose(deltaT, deltaT_known, rtol, atol))
def add_data(self, g): """ Define the permeability, apertures, boundary conditions """ # Permeability kxx = np.array([self.permeability(*pt) for pt in g.cell_centers.T]) perm = pp.SecondOrderTensor(kxx) # Source term source = g.cell_volumes * np.array( [self.rhs(*pt) for pt in g.cell_centers.T]) # Boundaries bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] bound_face_centers = g.face_centers[:, bound_faces] labels = np.array(["dir"] * bound_faces.size) bc_val = np.zeros(g.num_faces) bc_val[bound_faces] = np.array( [self.solution(*pt) for pt in bound_face_centers.T]) bound = pp.BoundaryCondition(g, bound_faces, labels) specified_parameters = { "second_order_tensor": perm, "source": source, "bc": bound, "bc_values": bc_val, } return pp.initialize_default_data(g, {}, "flow", specified_parameters)
def setup_3d_grid(): g = pp.CartGrid([2, 2, 2], physdims=[1, 1, 1]) g.compute_geometry() src = np.zeros(g.num_cells) src[4] = 1 data = pp.initialize_default_data(g, {}, "flow", {"source": src}) return g, data
def setup(self): g = pp.CartGrid([5, 5]) g.compute_geometry() stiffness = pp.FourthOrderTensor(np.ones(g.num_cells), np.ones(g.num_cells)) bnd = pp.BoundaryConditionVectorial(g) specified_data = { "fourth_order_tensor": stiffness, "bc": bnd, "inverter": "python", } keyword = "mechanics" data = pp.initialize_default_data(g, {}, keyword, specified_parameters=specified_data) discr = pp.Mpsa(keyword) discr.discretize(g, data) stress = data[pp.DISCRETIZATION_MATRICES][keyword][ discr.stress_matrix_key] bound_stress = data[pp.DISCRETIZATION_MATRICES][keyword][ discr.bound_stress_matrix_key] return g, stiffness, bnd, stress, bound_stress
def test_symmetry_periodic_pressure_field_2d(method): """ Test that we obtain a symmetric solution accross the periodic boundary. The test consider the unit square with periodic boundary conditions on the top and bottom boundary. A source is added to the bottom row of cells and we test that the solution is periodic. Setup, with x denoting the source: -------- | | p = 0 | | p = 0 | x | ------- """ # Structured Cartesian grid g, kxx = setup_cart_2d(np.array([5, 5]), [1, 1]) bot_faces = np.argwhere(g.face_centers[1] < 1e-5).ravel() top_faces = np.argwhere(g.face_centers[1] > 1 - 1e-5).ravel() left_faces = np.argwhere(g.face_centers[0] < 1e-5).ravel() right_faces = np.argwhere(g.face_centers[0] > 1 - 1e-5).ravel() dir_faces = np.hstack((left_faces, right_faces)) g.set_periodic_map(np.vstack((bot_faces, top_faces))) bound = pp.BoundaryCondition(g, dir_faces, "dir") # Solve key = "flow" d = pp.initialize_default_data( g, {}, key, { "second_order_tensor": pp.SecondOrderTensor(kxx), "bc": bound }) if method == "tpfa": discr = pp.Tpfa(key) elif method == "mpfa": discr = pp.Mpfa(key) else: assert False discr.discretize(g, d) matrix_dictionary = d[pp.DISCRETIZATION_MATRICES][key] flux, bound_flux = matrix_dictionary["flux"], matrix_dictionary[ "bound_flux"] div = g.cell_faces.T a = div * flux pr_bound = np.zeros(g.num_faces) src = np.zeros(g.num_cells) src[2] = 1 rhs = -div * bound_flux * pr_bound + src pr = np.linalg.solve(a.todense(), rhs) p_diff = pr[5:15] - np.hstack((pr[-5:], pr[-10:-5])) assert np.max(np.abs(p_diff)) < 1e-10
def _tpfa_matrix(g, perm=None): """ Compute a two-point flux approximation matrix useful related to a call of create_partition. Parameters ---------- g: the grid perm: (optional) permeability, the it is not given unitary tensor is assumed Returns ------- out: sparse matrix Two-point flux approximation matrix """ if isinstance(g, grid_bucket.GridBucket): g = g.get_grids(lambda g_: g_.dim == g.dim_max())[0] if perm is None: perm = pp.SecondOrderTensor(np.ones(g.num_cells)) solver = pp.Tpfa("flow") specified_parameters = { "second_order_tensor": perm, "bc": pp.BoundaryCondition(g, np.empty(0), ""), } data = pp.initialize_default_data(g, {}, "flow", specified_parameters) solver.discretize(g, data) return solver.assemble_matrix(g, data)
def test_upwind_example_2(self, if_export=False): ####################### # Simple 2d upwind problem with implicit Euler scheme in time ####################### T = 1 Nx, Ny = 10, 1 g = pp.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() advect = pp.Upwind("transport") dis = advect.darcy_flux(g, [1, 0, 0]) b_faces = g.get_all_boundary_faces() 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) time_step = advect.cfl(g, data) data[pp.PARAMETERS]["transport"]["time_step"] = time_step advect.discretize(g, data) U, rhs = advect.assemble_matrix_rhs(g, data) rhs = time_step * rhs U = time_step * U mass = pp.MassMatrix("transport") mass.discretize(g, data) M, _ = mass.assemble_matrix_rhs(g, data) conc = np.zeros(g.num_cells) # Perform an LU factorization to speedup the solver IE_solver = sps.linalg.factorized((M + U).tocsc()) # Loop over the time Nt = int(T / time_step) time = np.empty(Nt) for i in np.arange(Nt): # Update the solution # Backward and forward substitution to solve the system conc = IE_solver(M.dot(conc) + rhs) time[i] = time_step * i known = np.array([ 0.99969927, 0.99769441, 0.99067741, 0.97352474, 0.94064879, 0.88804726, 0.81498958, 0.72453722, 0.62277832, 0.51725056, ]) assert np.allclose(conc, known)
def test_initialize_default_data(self): """ Test default flow data initialization with default values. initialize_data returns a data dictionary d with default "keyword" Parameters stored in d["parameters"]. """ data = pp.initialize_default_data(self.g, {}, parameter_type="flow") self.check_default_flow_dictionary(data[pp.PARAMETERS]["flow"])
def _matrix(self, g, perm, bc): solver = pp.RT0(keyword="flow") data = pp.initialize_default_data(g, {}, "flow", { "second_order_tensor": perm, "bc": bc }) solver.discretize(g, data) return solver.assemble_matrix(g, data).todense()
def test_matrix_rhs(self): g_list = setup_grids.setup_2d() kw = "mechanics" for g in g_list: solver = pp.numerics.fv.mpsa.Mpsa(kw) data = pp.initialize_default_data(g, {}, kw) A, b = solver.assemble_matrix_rhs(g, data) self.assertTrue( np.all(A.shape == (g.dim * g.num_cells, g.dim * g.num_cells))) self.assertTrue(b.size == g.dim * g.num_cells)
def setup_2d_1d(nx, simplex_grid=False): if not simplex_grid: frac1 = np.array([[0.2, 0.8], [0.5, 0.5]]) frac2 = np.array([[0.5, 0.5], [0.8, 0.2]]) fracs = [frac1, frac2] gb = pp.meshing.cart_grid(fracs, nx, physdims=[1, 1]) else: p = np.array([[0.2, 0.8, 0.5, 0.5], [0.5, 0.5, 0.8, 0.2]]) e = np.array([[0, 2], [1, 3]]) domain = {"xmin": 0, "ymin": 0, "xmax": 1, "ymax": 1} network = pp.FractureNetwork2d(p, e, domain) mesh_size = 0.08 mesh_kwargs = {"mesh_size_frac": mesh_size, "mesh_size_min": mesh_size / 20} gb = network.mesh(mesh_kwargs) gb.compute_geometry() gb.assign_node_ordering() aperture = 0.01 / np.max(nx) for g, d in gb: a = np.power(aperture, gb.dim_max() - g.dim) * np.ones(g.num_cells) kxx = np.ones(g.num_cells) * a perm = pp.SecondOrderTensor(kxx) specified_parameters = {"second_order_tensor": perm} if g.dim == 2: bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] bound = pp.BoundaryCondition( g, bound_faces.ravel("F"), ["dir"] * bound_faces.size ) bc_val = np.zeros(g.num_faces) bc_val[bound_faces] = g.face_centers[1, bound_faces] specified_parameters.update({"bc": bound, "bc_values": bc_val}) pp.initialize_default_data(g, d, "flow", specified_parameters) for e, d in gb.edges(): gl, _ = gb.nodes_of_edge(e) mg = d["mortar_grid"] kn = 1.0 / aperture d[pp.PARAMETERS] = pp.Parameters(mg, ["flow"], [{"normal_diffusivity": kn}]) d[pp.DISCRETIZATION_MATRICES] = {"flow": {}} return gb
def test_upwind_example_1(self, if_export=False): ####################### # Simple 2d upwind problem with explicit Euler scheme in time ####################### T = 1 Nx, Ny = 4, 1 g = pp.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() advect = pp.Upwind("transport") dis = advect.darcy_flux(g, [1, 0, 0]) b_faces = g.get_all_boundary_faces() 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) time_step = advect.cfl(g, data) data[pp.PARAMETERS]["transport"]["time_step"] = time_step advect.discretize(g, data) U, rhs = advect.assemble_matrix_rhs(g, data) rhs = time_step * rhs U = time_step * U OF = advect.outflow(g, data) 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) production = np.zeros(Nt) for i in np.arange(Nt): # Update the solution production[i] = np.sum(OF.dot(conc)) conc = invM.dot((M_minus_U).dot(conc) + rhs) time[i] = time_step * i known = 1.09375 assert np.sum(production) == known
def test_fv_cart_2d_periodic(method): """ Apply TPFA and MPFA on a periodic Cartesian grid, should obtain Laplacian stencil.""" # Set up 3 X 3 Cartesian grid nx = np.array([3, 3]) g = pp.CartGrid(nx) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(kxx) left_faces = [0, 4, 8, 12, 13, 14] right_faces = [3, 7, 11, 21, 22, 23] periodic_face_map = np.vstack((left_faces, right_faces)) g.set_periodic_map(periodic_face_map) bound = pp.BoundaryCondition(g) key = "flow" d = pp.initialize_default_data(g, {}, key, { "second_order_tensor": perm, "bc": bound }) if method == "tpfa": discr = pp.Tpfa(key) elif method == "mpfa": discr = pp.Mpfa(key) else: assert False discr.discretize(g, d) matrix_dictionary = d[pp.DISCRETIZATION_MATRICES][key] trm, bound_flux = matrix_dictionary["flux"], matrix_dictionary[ "bound_flux"] div = g.cell_faces.T a = div * trm b = -(div * bound_flux).A # Create laplace matrix A_lap = np.array([ [4.0, -1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0], [-1.0, 4.0, -1.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0], [-1.0, -1.0, 4.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0], [-1.0, 0.0, 0.0, 4.0, -1.0, -1.0, -1.0, 0.0, 0.0], [0.0, -1.0, 0.0, -1.0, 4.0, -1.0, 0.0, -1.0, 0.0], [0.0, 0.0, -1.0, -1.0, -1.0, 4.0, 0.0, 0.0, -1.0], [-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 4.0, -1.0, -1.0], [0.0, -1.0, 0.0, 0.0, -1.0, 0.0, -1.0, 4.0, -1.0], [0.0, 0.0, -1.0, 0.0, 0.0, -1.0, -1.0, -1.0, 4.0], ]) assert np.allclose(a.A, A_lap) assert np.allclose(b, 0) return a
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 test_convergence_rt0_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 errs_known = np.array([ 0.00128247705764, 0.000770088925769, 0.00050939369071, 0.000360006145403, 0.000267209318912, ]) for i, err_known in zip(np.arange(5), errs_known): g = pp.simplex.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)) solver = pp.RT0(keyword="flow") solver_rhs = pp.DualScalarSource(keyword="flow") specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, "source": source, } 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, err_known))
def setup_2d_1d(nx, simplex_grid=False): if not simplex_grid: mesh_args = nx else: mesh_size = 0.08 mesh_args = { "mesh_size_frac": mesh_size, "mesh_size_min": mesh_size / 20 } end_x = [0.2, 0.8] end_y = [0.8, 0.2] gb, _ = pp.grid_buckets_2d.two_intersecting(mesh_args, end_x, end_y, simplex_grid) aperture = 0.01 / np.max(nx) for g, d in gb: a = np.power(aperture, gb.dim_max() - g.dim) * np.ones(g.num_cells) kxx = np.ones(g.num_cells) * a perm = pp.SecondOrderTensor(kxx) specified_parameters = {"second_order_tensor": perm} if g.dim == 2: bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] bound = pp.BoundaryCondition(g, bound_faces.ravel("F"), ["dir"] * bound_faces.size) bc_val = np.zeros(g.num_faces) bc_val[bound_faces] = g.face_centers[1, bound_faces] specified_parameters.update({"bc": bound, "bc_values": bc_val}) pp.initialize_default_data(g, d, "flow", specified_parameters) for e, d in gb.edges(): gl, _ = gb.nodes_of_edge(e) mg = d["mortar_grid"] kn = 1.0 / aperture d[pp.PARAMETERS] = pp.Parameters(mg, ["flow"], [{ "normal_diffusivity": kn }]) d[pp.DISCRETIZATION_MATRICES] = {"flow": {}} return gb
def test_convergence_rt0_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, :])) errs_known = np.array([ 0.014848639601, 0.00928479234915, 0.00625096095775, 0.00446722560521, 0.00334170283883, ]) for i, err_known in zip(np.arange(5), errs_known): g = pp.simplex.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)) solver = pp.RT0(keyword="flow") solver_rhs = pp.DualScalarSource(keyword="flow") specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, "source": source, } 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, err_known))
def test_bound_cell_node_keyword(self): # Compute update for a single cell on the boundary g, perm, bnd, flux, bound_flux, vector_source = self.setup() # cell = 10 nodes_of_cell = np.array([12, 13, 18, 19]) faces_of_cell = np.array([12, 13, 40, 45]) specified_data = { "second_order_tensor": perm, "bc": bnd, "inverter": "python", "specified_nodes": nodes_of_cell, } keyword = "flow" data = pp.initialize_default_data(g, {}, keyword, specified_parameters=specified_data) discr = pp.Mpfa(keyword) discr.discretize(g, data) partial_flux = data[pp.DISCRETIZATION_MATRICES][keyword][ discr.flux_matrix_key] partial_bound = data[pp.DISCRETIZATION_MATRICES][keyword][ discr.bound_flux_matrix_key] partial_vector_source = data[pp.DISCRETIZATION_MATRICES][keyword][ discr.vector_source_matrix_key] active_faces = data[pp.PARAMETERS][keyword]["active_faces"] self.assertTrue(faces_of_cell.size == active_faces.size) self.assertTrue( np.all(np.sort(faces_of_cell) == np.sort(active_faces))) diff_flux = (flux - partial_flux).todense() diff_bound = (bound_flux - partial_bound).todense() diff_vc = (vector_source - partial_vector_source).todense() self.assertTrue(np.max(np.abs(diff_flux[faces_of_cell])) == 0) self.assertTrue(np.max(np.abs(diff_bound[faces_of_cell])) == 0) self.assertTrue(np.max(np.abs(diff_vc[faces_of_cell])) == 0) # Only the faces of the central cell should be zero pp.fvutils.zero_out_sparse_rows(partial_flux, faces_of_cell) pp.fvutils.zero_out_sparse_rows(partial_bound, faces_of_cell) pp.fvutils.zero_out_sparse_rows(partial_vector_source, faces_of_cell) self.assertTrue(np.max(np.abs(partial_flux.data)) == 0) self.assertTrue(np.max(np.abs(partial_bound.data)) == 0) self.assertTrue(np.max(np.abs(partial_vector_source.data)) == 0)
def test_periodic_pressure_field_2d(method): """ Test that TPFA approximate an analytical periodic solution by imposing periodic boundary conditions to the bottom and top faces of the unit square. """ # Structured Cartesian grid g, kxx = setup_cart_2d(np.array([10, 10]), [1, 1]) bot_faces = np.argwhere(g.face_centers[1] < 1e-5).ravel() top_faces = np.argwhere(g.face_centers[1] > 1 - 1e-5).ravel() left_faces = np.argwhere(g.face_centers[0] < 1e-5).ravel() right_faces = np.argwhere(g.face_centers[0] > 1 - 1e-5).ravel() dir_faces = np.hstack((left_faces, right_faces)) g.set_periodic_map(np.vstack((bot_faces, top_faces))) bound = pp.BoundaryCondition(g, dir_faces, "dir") # Solve key = "flow" d = pp.initialize_default_data( g, {}, key, { "second_order_tensor": pp.SecondOrderTensor(kxx), "bc": bound }) if method == "tpfa": discr = pp.Tpfa(key) elif method == "mpfa": discr = pp.Mpfa(key) else: assert False discr.discretize(g, d) matrix_dictionary = d[pp.DISCRETIZATION_MATRICES][key] flux, bound_flux = matrix_dictionary["flux"], matrix_dictionary[ "bound_flux"] div = g.cell_faces.T a = div * flux pr_bound, pr_cell, src = setup_periodic_pressure_field(g, kxx) rhs = -div * bound_flux * pr_bound + src * g.cell_volumes pr = np.linalg.solve(a.todense(), rhs) p_diff = pr - pr_cell assert np.max(np.abs(p_diff)) < 0.06