def test_linear_pressure_part_neumann_conditions(self): g = self.grid() bf = np.where(g.tags["domain_boundary_faces"].ravel())[0] bc_type = bf.size * ["neu"] # Set Dirichlet conditions. Note that indices are relative to bf, # that is, counting only boundary faces. bc_type[0] = "dir" bc_type[2] = "dir" # Not [3] bound = pp.BoundaryCondition(g, bf, bc_type) fd = pp.Tpfa("flow") bc_val = np.zeros(g.num_faces) # Set up unit flow in x-direction, thus pressure gradient the other way bc_val[[2, 5]] = 1 data = make_dictionary(g, bound, bc_val) p = self.pressure(fd, g, data) matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = ( matrix_dictionary["bound_pressure_cell"] * p + matrix_dictionary["bound_pressure_face"] * bc_val ) self.assertTrue(np.allclose(bound_p[bf], -g.face_centers[0, bf]))
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 __init__(self, gb, flow, model="flow"): self.model = model self.gb = gb self.data = None self.assembler = None # discretization operator name self.discr_name = self.model + "_flux" self.discr = pp.Tpfa(self.model) self.coupling_name = self.discr_name + "_coupling" self.coupling = pp.RobinCoupling(self.model, self.discr) self.source_name = self.model + "_source" self.source = pp.ScalarSource(self.model) # master variable name self.variable = self.model + "_variable" self.mortar = self.model + "_lambda" # post process variables self.pressure = "pressure" self.flux = "darcy_flux" # it has to be this one self.P0_flux = "P0_darcy_flux"
def test_linear_pressure_part_neumann_conditions_smaller_domain(self): # Smaller domain, check that the smaller pressure gradient is captured g = pp.CartGrid([2, 2], physdims=[1, 2]) g.compute_geometry() bf = np.where(g.tags["domain_boundary_faces"].ravel())[0] bc_type = bf.size * ["neu"] bc_type[0] = "dir" bc_type[2] = "dir" bound = pp.BoundaryCondition(g, bf, bc_type) fd = pp.Tpfa("flow") bc_val = np.zeros(g.num_faces) # Set up unit pressure gradient in x-direction bc_val[[2, 5]] = 1 data = make_dictionary(g, bound, bc_val) p = self.pressure(fd, g, data) matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = ( matrix_dictionary["bound_pressure_cell"] * p + matrix_dictionary["bound_pressure_face"] * bc_val ) self.assertTrue(np.allclose(bound_p[bf], -g.face_centers[0, bf]))
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 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 setup_discr_tpfa(gb, key="flow"): """ Setup the discretization Tpfa. """ discr = pp.Tpfa(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}} 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 set_params_disrcetize(g, ambient_dim, method, periodic=False): g.compute_geometry() keyword = "flow" if periodic: south = g.face_centers[1] < np.min(g.nodes[1]) + 1e-8 north = g.face_centers[1] > np.max(g.nodes[1]) - 1e-8 bc = pp.BoundaryCondition(g, north + south, "per") south_idx = np.argwhere(south).ravel() north_idx = np.argwhere(north).ravel() bc.set_periodic_map(np.vstack((south_idx, north_idx))) else: bc = pp.BoundaryCondition(g) k = pp.SecondOrderTensor(np.ones(g.num_cells)) params = { "bc": bc, "second_order_tensor": k, "mpfa_inverter": "python", "ambient_dimension": ambient_dim, } data = pp.initialize_data(g, {}, keyword, params) if method == "mpfa": discr = pp.Mpfa(keyword) elif method == "tpfa": discr = pp.Tpfa(keyword) discr.discretize(g, data) flux = data[pp.DISCRETIZATION_MATRICES][keyword][discr.flux_matrix_key] vector_source = data[pp.DISCRETIZATION_MATRICES][keyword][ discr.vector_source_matrix_key] div = pp.fvutils.scalar_divergence(g) return flux, vector_source, div
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 homo_tpfa(g): return { "scheme": pp.Tpfa("flow"), "dof": { "cells": 1 }, "label": "homo_tpfa" }
def test_uniform_flow_cart_2d_1d_simplex(self): # Unstructured simplex grid gb = setup_2d_1d(np.array([10, 10]), simplex_grid=True) key = "flow" tpfa = pp.Tpfa(key) assembler = test_utils.setup_flow_assembler(gb, tpfa, key) test_utils.solve_and_distribute_pressure(gb, assembler) self.assertTrue(check_pressures(gb))
def solve_tpfa(gb, folder, discr_3d=None): # Choose and define the solvers and coupler flow_discretization = pp.Tpfa("flow") source_discretization = pp.ScalarSource("flow") run_flow(gb, flow_discretization, source_discretization, folder, is_FV=True, discr_3d=discr_3d)
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 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 elliptic_disc(gb, keyword, use_mpfa=False): """ Discretize the elliptic operator on each graph node """ for g, d in gb: if use_mpfa: pp.Mpfa(keyword).discretize(g, d) else: pp.Tpfa(keyword).discretize(g, d) d[pp.DISCRETIZATION_MATRICES][keyword][ "div"] = pp.fvutils.scalar_divergence(g)
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
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_zero_pressure(self): g = self.grid() bf = np.where(g.tags["domain_boundary_faces"].ravel())[0] bc_type = bf.size * ["dir"] bound = pp.BoundaryCondition(g, bf, bc_type) fd = pp.Tpfa("flow") data = make_dictionary(g, bound) p = self.pressure(fd, g, data) self.assertTrue(np.allclose(p, np.zeros_like(p))) matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = matrix_dictionary["bound_pressure_cell"] * p + matrix_dictionary[ "bound_pressure_face" ] * np.zeros(g.num_faces) self.assertTrue(np.allclose(bound_p, np.zeros_like(bound_p)))
def diffusive_disc(self): "Discretization of term \nabla K \nabla T" class DiffusiveMixedDim(pp.TpfaMixedDim): def __init__(self, physics="flow", has_advective_term=False): pp.TpfaMixedDim.__init__(self, physics=physics) if has_advective_term: self.coupling_conditions = [None, self.coupling_conditions] self.solver = pp.numerics.mixed_dim.coupler.Coupler( self.discr, self.coupling_conditions) if self.is_GridBucket: diffusive_discr = DiffusiveMixedDim(self.physics, self.advective_term) else: diffusive_discr = pp.Tpfa(physics=self.physics) return diffusive_discr
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 test_linear_pressure_dirichlet_conditions(self): g = self.grid() bf = np.where(g.tags["domain_boundary_faces"].ravel())[0] bc_type = bf.size * ["dir"] bound = pp.BoundaryCondition(g, bf, bc_type) fd = pp.Tpfa("flow") bc_val = 1 * g.face_centers[0] + 2 * g.face_centers[1] data = make_dictionary(g, bound, bc_val) p = self.pressure(fd, g, data) matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = ( matrix_dictionary["bound_pressure_cell"] * p + matrix_dictionary["bound_pressure_face"] * bc_val ) self.assertTrue(np.allclose(bound_p[bf], bc_val[bf]))
def test_sign_trouble_two_neumann_sides(self): g = pp.CartGrid(np.array([2, 2]), physdims=[2, 2]) g.compute_geometry() bc_val = np.zeros(g.num_faces) bc_val[[0, 3]] = 1 bc_val[[2, 5]] = -1 t = pp.Tpfa("flow") data = make_dictionary(g, pp.BoundaryCondition(g), bc_val) t.discretize(g, data) t.assemble_matrix_rhs(g, data) # The problem is singular, and spsolve does not work well on all systems. # Instead, set a consistent solution, and check that the boundary # pressure is recovered. x = g.cell_centers[0] matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = ( matrix_dictionary["bound_pressure_cell"] * x + matrix_dictionary["bound_pressure_face"] * bc_val ) self.assertTrue(bound_p[0] == x[0] - 0.5) self.assertTrue(bound_p[2] == x[1] + 0.5)
def test_constant_pressure_simplex_grid(self): g = self.simplex_grid() bf = np.where(g.tags["domain_boundary_faces"].ravel())[0] bc_type = bf.size * ["dir"] bound = pp.BoundaryCondition(g, bf, bc_type) fd = pp.Tpfa("flow") bc_val = np.ones(g.num_faces) data = make_dictionary(g, bound, bc_val) p = self.pressure(fd, g, data) self.assertTrue(np.allclose(p, np.ones_like(p))) matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = ( matrix_dictionary["bound_pressure_cell"] * p + matrix_dictionary["bound_pressure_face"] * bc_val ) self.assertTrue(np.allclose(bound_p[bf], bc_val[bf]))
def test_linear_pressure_part_neumann_conditions_reverse_sign(self): g = self.grid() bf = np.where(g.tags["domain_boundary_faces"].ravel())[0] bc_type = bf.size * ["neu"] bc_type[0] = "dir" bc_type[2] = "dir" bound = pp.BoundaryCondition(g, bf, bc_type) fd = pp.Tpfa("flow") bc_val = np.zeros(g.num_faces) # Set up pressure gradient in x-direction, with value -1 bc_val[[2, 5]] = -1 data = make_dictionary(g, bound, bc_val) p = self.pressure(fd, g, data) matrix_dictionary = data[pp.DISCRETIZATION_MATRICES]["flow"] bound_p = ( matrix_dictionary["bound_pressure_cell"] * p + matrix_dictionary["bound_pressure_face"] * bc_val ) self.assertTrue(np.allclose(bound_p[bf], g.face_centers[0, bf]))
def solve(self, kf, description, multi_point): gb, domain = pp.grid_buckets_2d.benchmark_regular( {"mesh_size_frac": 0.045}) # Assign parameters setup.add_data(gb, domain, kf) key = "flow" if multi_point: method = pp.Mpfa(key) else: method = pp.Tpfa(key) coupler = pp.RobinCoupling(key, method) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {"pressure": {"cells": 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)
def advdiff(gb, param, model_flow): model = "transport" model_data_adv, model_data_diff = data.advdiff(gb, model, model_flow, param) # discretization operator names adv_id = "advection" diff_id = "diffusion" # variable names variable = "scalar" mortar_adv = "lambda_" + variable + "_" + adv_id mortar_diff = "lambda_" + variable + "_" + diff_id # discretization operatr discr_adv = pp.Upwind(model_data_adv) discr_diff = pp.Tpfa(model_data_diff) coupling_adv = pp.UpwindCoupling(model_data_adv) coupling_diff = pp.RobinCoupling(model_data_diff, discr_diff) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {variable: {"cells": 1}} d[pp.DISCRETIZATION] = { variable: { adv_id: discr_adv, diff_id: discr_diff } } for e, d in gb.edges(): g_slave, g_master = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = { mortar_adv: { "cells": 1 }, mortar_diff: { "cells": 1 } } d[pp.COUPLING_DISCRETIZATION] = { adv_id: { g_slave: (variable, adv_id), g_master: (variable, adv_id), e: (mortar_adv, coupling_adv) }, diff_id: { g_slave: (variable, diff_id), g_master: (variable, diff_id), e: (mortar_diff, coupling_diff) } } # setup the advection-diffusion problem assembler = pp.Assembler() logger.info( "Assemble the advective and diffusive terms of the transport problem") A, b, block_dof, full_dof = assembler.assemble_matrix_rhs(gb) logger.info("done") # mass term mass_id = "mass" discr_mass = pp.MassMatrix(model_data_adv) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {variable: {"cells": 1}} d[pp.DISCRETIZATION] = {variable: {mass_id: discr_mass}} gb.remove_edge_props(pp.COUPLING_DISCRETIZATION) for e, d in gb.edges(): g_slave, g_master = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = { mortar_adv: { "cells": 1 }, mortar_diff: { "cells": 1 } } logger.info("Assemble the mass term of the transport problem") M, _, _, _ = assembler.assemble_matrix_rhs(gb) logger.info("done") # Perform an LU factorization to speedup the solver #IE_solver = sps.linalg.factorized((M + A).tocsc()) # time loop logger.info("Prepare the exporting") save = pp.Exporter(gb, "solution", folder=param["folder"]) logger.info("done") variables = [variable, param["pressure"], param["P0_flux"]] x = np.ones(A.shape[0]) * param["initial_advdiff"] logger.info("Start the time loop with " + str(param["n_steps"]) + " steps") for i in np.arange(param["n_steps"]): #x = IE_solver(b + M.dot(x)) logger.info("Solve the linear system for time step " + str(i)) x = sps.linalg.spsolve(M + A, b + M.dot(x)) logger.info("done") logger.info("Variable post-process") assembler.distribute_variable(gb, x, block_dof, full_dof) logger.info("done") logger.info("Export variable") save.write_vtk(variables, time_step=i) logger.info("done") save.write_pvd(np.arange(param["n_steps"]) * param["time_step"])
def solve(self, gb, analytic_p): # Parameter-discretization keyword: kw = "flow" # Terms key_flux = "flux" key_src = "src" # Primary variables key_p = "pressure" # pressure name key_m = "mortar" # mortar name tpfa = pp.Tpfa(kw) src = pp.ScalarSource(kw) for g, d in gb: d[pp.DISCRETIZATION] = {key_p: {key_src: src, key_flux: tpfa}} d[pp.PRIMARY_VARIABLES] = {key_p: {"cells": 1}} for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = {key_m: {"cells": 1}} if g1.dim == g2.dim: mortar_disc = pp.FluxPressureContinuity(kw, tpfa) else: mortar_disc = pp.RobinCoupling(kw, tpfa) d[pp.COUPLING_DISCRETIZATION] = { key_flux: { g1: (key_p, key_flux), g2: (key_p, key_flux), e: (key_m, mortar_disc), } } assembler = pp.Assembler(gb) assembler.discretize() A, b = assembler.assemble_matrix_rhs() x = sps.linalg.spsolve(A, b) assembler.distribute_variable(x) # test pressure for g, d in gb: ap, _, _ = analytic_p(g.cell_centers) self.assertTrue(np.max(np.abs(d[pp.STATE][key_p] - ap)) < 5e-2) # test mortar solution for e, d_e in gb.edges(): mg = d_e["mortar_grid"] g1, g2 = gb.nodes_of_edge(e) if g1 == g2: left_to_m = mg.master_to_mortar_avg() right_to_m = mg.slave_to_mortar_avg() else: continue d1 = gb.node_props(g1) d2 = gb.node_props(g2) _, analytic_flux, _ = analytic_p(g1.face_centers) # the aperture is assumed constant left_flux = np.sum(analytic_flux * g1.face_normals[:2], 0) left_flux = left_to_m * ( d1[pp.DISCRETIZATION_MATRICES][kw]["bound_flux"] * left_flux ) # right flux is negative lambda right_flux = np.sum(analytic_flux * g2.face_normals[:2], 0) right_flux = -right_to_m * ( d2[pp.DISCRETIZATION_MATRICES][kw]["bound_flux"] * right_flux ) self.assertTrue(np.max(np.abs(d_e[pp.STATE][key_m] - left_flux)) < 5e-2) self.assertTrue(np.max(np.abs(d_e[pp.STATE][key_m] - right_flux)) < 5e-2)
def set_parameters( self, neu_val_top=None, dir_val_top=None, kn: float = 1e0, method="mpfa", aperture: float = 1e-1, gravity_angle: float = 0, ) -> None: """ Parameters: neu_val_top (float): Default None implies Dirichlet on top. If not None, the prescribed value will be applied as the Neumann bc value. dir_val_top (float): If not None, the prescribed value will be applied as the Dirichlet bc value. Note that neu_val_top takes precedent over dir_val_top. method: Discretization method. kn: Normal permeability of the fracture. Will be multiplied by aperture/2 to yield the normal diffusivity. aperture: Fracture aperture. gravity_angle: Angle by which to rotate the applied vector source field. """ # Set up flow field with uniform flow in y-direction kw = "flow" gb = self.gb for g, d in gb: a = np.power(aperture, gb.dim_max() - g.dim) perm = pp.SecondOrderTensor(kxx=a * np.ones(g.num_cells)) gravity = np.zeros((gb.dim_max(), g.num_cells)) # Angle of zero means force vector of [0, -1] gravity[1, :] = -np.cos(gravity_angle) gravity[0, :] = np.sin(gravity_angle) b_val = np.zeros(g.num_faces) if g.dim == self.gb.dim_max(): if neu_val_top is not None: dir_faces = np.atleast_1d(pp.face_on_side(g, ["ymin"])[0]) neu_faces = np.atleast_1d(pp.face_on_side(g, ["ymax"])[0]) b_val[neu_faces] = neu_val_top else: dir_faces = pp.face_on_side(g, ["ymin", "ymax"]) b_val[dir_faces[0]] = 0 if dir_val_top is not None: b_val[dir_faces[1]] = dir_val_top dir_faces = np.hstack((dir_faces[0], dir_faces[1])) labels = np.array(["dir"] * dir_faces.size) bc = pp.BoundaryCondition(g, dir_faces, labels) else: bc = pp.BoundaryCondition(g) parameter_dictionary = { "bc_values": b_val, "bc": bc, "ambient_dimension": gb.dim_max(), "mpfa_inverter": "python", "second_order_tensor": perm, "vector_source": gravity.ravel("F"), } pp.initialize_data(g, d, "flow", parameter_dictionary) for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) mg = d["mortar_grid"] a = aperture * np.ones(mg.num_cells) gravity = np.zeros((gb.dim_max(), mg.num_cells)) # Angle of zero means force vector of [0, -1] gravity[1, :] = -np.cos(gravity_angle) gravity[0, :] = np.sin(gravity_angle) gravity *= a / 2 parameter_dictionary = { "normal_diffusivity": 2 / a * kn, "ambient_dimension": gb.dim_max(), "vector_source": gravity.ravel("F"), } pp.initialize_data(mg, d, "flow", parameter_dictionary) # 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 == "tpfa": discr = pp.Tpfa(kw) else: raise ValueError("Unexpected discretization method ")
def test_md_flow(): # Three fractures, will create intersection lines and point frac_1 = np.array([[2, 4, 4, 2], [3, 3, 3, 3], [0, 0, 6, 6]]) frac_2 = np.array([[3, 3, 3, 3], [2, 4, 4, 2], [0, 0, 6, 6]]) frac_3 = np.array([[0, 6, 6, 0], [2, 2, 4, 4], [3, 3, 3, 3]]) gb = pp.meshing.cart_grid(fracs=[frac_1, frac_2, frac_3], nx=np.array([6, 6, 6])) gb.compute_geometry() pressure_variable = "pressure" flux_variable = "mortar_flux" keyword = "flow" discr = pp.Tpfa(keyword) source_discr = pp.ScalarSource(keyword) coupling_discr = pp.RobinCoupling(keyword, discr, discr) for g, d in gb: # Assign data if g.dim == gb.dim_max(): upper_left_ind = np.argmax(np.linalg.norm(g.face_centers, axis=0)) bc = pp.BoundaryCondition(g, np.array([0, upper_left_ind]), ["dir", "dir"]) bc_values = np.zeros(g.num_faces) bc_values[0] = 1 sources = np.random.rand(g.num_cells) * g.cell_volumes specified_parameters = { "bc": bc, "bc_values": bc_values, "source": sources } else: sources = np.random.rand(g.num_cells) * g.cell_volumes specified_parameters = {"source": sources} # Initialize data pp.initialize_default_data(g, d, keyword, specified_parameters) # Declare grid primary variable d[pp.PRIMARY_VARIABLES] = {pressure_variable: {"cells": 1}} # Assign discretization d[pp.DISCRETIZATION] = { pressure_variable: { "diff": discr, "source": source_discr } } # Initialize state d[pp.STATE] = { pressure_variable: np.zeros(g.num_cells), pp.ITERATE: { pressure_variable: np.zeros(g.num_cells) }, } for e, d in gb.edges(): mg = d["mortar_grid"] pp.initialize_data(mg, d, keyword, {"normal_diffusivity": 1}) d[pp.PRIMARY_VARIABLES] = {flux_variable: {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = {} d[pp.COUPLING_DISCRETIZATION]["coupling"] = { e[0]: (pressure_variable, "diff"), e[1]: (pressure_variable, "diff"), e: (flux_variable, coupling_discr), } d[pp.STATE] = { flux_variable: np.zeros(mg.num_cells), pp.ITERATE: { flux_variable: np.zeros(mg.num_cells) }, } dof_manager = pp.DofManager(gb) assembler = pp.Assembler(gb, dof_manager) assembler.discretize() # Reference discretization A_ref, b_ref = assembler.assemble_matrix_rhs() manager = pp.ad.EquationManager(gb, dof_manager) grid_list = [g for g, _ in gb] edge_list = [e for e, _ in gb.edges()] node_discr = pp.ad.MpfaAd(keyword, grid_list) edge_discr = pp.ad.RobinCouplingAd(keyword, edge_list) bc_val = pp.ad.BoundaryCondition(keyword, grid_list) source = pp.ad.ParameterArray(param_keyword=keyword, array_keyword='source', grids=grid_list) projections = pp.ad.MortarProjections(gb=gb) div = pp.ad.Divergence(grids=grid_list) p = manager.merge_variables([(g, pressure_variable) for g in grid_list]) lmbda = manager.merge_variables([(e, flux_variable) for e in edge_list]) flux = (node_discr.flux * p + node_discr.bound_flux * bc_val + node_discr.bound_flux * projections.mortar_to_primary_int * lmbda) flow_eq = div * flux - projections.mortar_to_secondary_int * lmbda - source interface_flux = edge_discr.mortar_scaling * ( projections.primary_to_mortar_avg * node_discr.bound_pressure_cell * p + projections.primary_to_mortar_avg * node_discr.bound_pressure_face * projections.mortar_to_primary_int * lmbda - projections.secondary_to_mortar_avg * p + edge_discr.mortar_discr * lmbda) flow_eq_ad = pp.ad.Expression(flow_eq, dof_manager, "flow on nodes") flow_eq_ad.discretize(gb) interface_eq_ad = pp.ad.Expression(interface_flux, dof_manager, "flow on interface") manager.equations += [flow_eq_ad, interface_eq_ad] state = np.zeros(gb.num_cells() + gb.num_mortar_cells()) A, b = manager.assemble_matrix_rhs(state=state) diff = A - A_ref if diff.data.size > 0: assert np.max(np.abs(diff.data)) < 1e-10 assert np.max(np.abs(b - b_ref)) < 1e-10
def test_tpfa_cart_2d(self): """ Apply TPFA on 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) bound_faces = np.array([0, 3, 12]) bound = pp.BoundaryCondition(g, bound_faces, ["dir"] * bound_faces.size) key = "flow" d = pp.initialize_default_data(g, {}, key, { "second_order_tensor": perm, "bc": bound }) discr = pp.Tpfa(key) 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 # Checks on interior cell mid = 4 self.assertTrue(a[mid, mid] == 4) self.assertTrue(a[mid - 1, mid] == -1) self.assertTrue(a[mid + 1, mid] == -1) self.assertTrue(a[mid - 3, mid] == -1) self.assertTrue(a[mid + 3, mid] == -1) self.assertTrue(np.all(b[mid, :] == 0)) # The first cell should have two Dirichlet bnds self.assertTrue(a[0, 0] == 6) self.assertTrue(a[0, 1] == -1) self.assertTrue(a[0, 3] == -1) self.assertTrue(b[0, 0] == 2) self.assertTrue(b[0, 12] == 2) # Cell 3 has one Dirichlet, one Neumann face self.assertTrue(a[2, 2] == 4) self.assertTrue(a[2, 1] == -1) self.assertTrue(a[2, 5] == -1) self.assertTrue(b[2, 3] == 2) self.assertTrue(b[2, 14] == -1) # Cell 2 has one Neumann face self.assertTrue(a[1, 1] == 3) self.assertTrue(a[1, 0] == -1) self.assertTrue(a[1, 2] == -1) self.assertTrue(a[1, 4] == -1) self.assertTrue(b[1, 13] == -1) return a