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 disabled_test_mono_equals_multi(self): """ test that the mono_dimensional elliptic solver gives the same answer as the grid bucket elliptic """ g = pp.CartGrid([10, 10]) g.compute_geometry() gb = pp.meshing.cart_grid([], [10, 10]) param_g = pp.Parameters(g) def bc_val(g): left = g.face_centers[0] < 1e-6 right = g.face_centers[0] > 10 - 1e-6 bc_val = np.zeros(g.num_faces) bc_val[left] = -1 bc_val[right] = 1 return bc_val def bc_labels(g): bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] bound_face_centers = g.face_centers[:, bound_faces] left = bound_face_centers[0] < 1e-6 right = bound_face_centers[0] > 10 - 1e-6 labels = np.array(["neu"] * bound_faces.size) labels[np.logical_or(right, left)] = "dir" bc_labels = pp.BoundaryCondition(g, bound_faces, labels) return bc_labels param_g.set_bc_val("flow", bc_val(g)) param_g.set_bc("flow", bc_labels(g)) gb.add_node_props(["param"]) for sub_g, d in gb: d["param"] = pp.Parameters(sub_g) d["param"].set_bc_val("flow", bc_val(g)) d["param"].set_bc("flow", bc_labels(sub_g)) for e, d in gb.edges(): gl, _ = gb.nodes_of_edge(e) d_l = gb.node_props(gl) d["kn"] = 1.0 / np.mean(d_l["param"].get_aperture()) problem_mono = pp.EllipticModel(g, {"param": param_g}) problem_mult = pp.EllipticModel(gb) p_mono = problem_mono.solve() p_mult = problem_mult.solve() self.assertTrue(np.allclose(p_mono, p_mult))
def test_non_zero_bc_val(self): """ We mixed bc_val on domain boundary and fracture displacement in x-direction. """ frac = np.array([[1, 1, 1], [1, 2, 1], [2, 2, 1], [2, 1, 1]]).T physdims = np.array([3, 3, 2]) g = pp.meshing.cart_grid([frac], [3, 3, 2], physdims=physdims).grids_of_dimension(3)[0] data = {"param": pp.Parameters(g)} # Define boundary conditions bc_val = np.zeros((g.dim, g.num_faces)) frac_slip = np.zeros((g.dim, g.num_faces)) frac_bnd = g.tags["fracture_faces"] dom_bnd = g.tags["domain_boundary_faces"] frac_slip[0, frac_bnd] = np.ones(np.sum(frac_bnd)) bc_val[:, dom_bnd] = g.face_centers[:, dom_bnd] bound = pp.BoundaryCondition(g, g.get_all_boundary_faces(), "dir") data["param"].set_bc("mechanics", bound) data["param"].set_bc_val("mechanics", bc_val.ravel("F")) data["param"].set_slip_distance(frac_slip.ravel("F")) solver = pp.FracturedMpsa() A, b = solver.matrix_rhs(g, data) u = np.linalg.solve(A.A, b) u_f = solver.extract_frac_u(g, u) u_c = solver.extract_u(g, u) u_c = u_c.reshape((3, -1), order="F") # Test traction frac_faces = g.frac_pairs frac_left = frac_faces[0] frac_right = frac_faces[1] T = solver.traction(g, data, u) T = T.reshape((3, -1), order="F") T_left = T[:, frac_left] T_right = T[:, frac_right] self.assertTrue(np.allclose(T_left, T_right)) # we have u_lhs - u_rhs = 1 so u_lhs should be positive mid_ind = int(round(u_f.size / 2)) u_left = u_f[:mid_ind] u_right = u_f[mid_ind:] true_diff = np.atleast_2d(np.array([1, 0, 0])).T u_left = u_left.reshape((3, -1), order="F") u_right = u_right.reshape((3, -1), order="F") self.assertTrue(np.all(np.abs(u_left - u_right - true_diff) < 1e-10)) # should have a positive displacement for all cells self.assertTrue(np.all(u_c > 0))
def test_p1_1d(self): g = pp.structured.CartGrid(1, 1) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1) bn = g.get_boundary_nodes() bc = pp.BoundaryConditionNode(g, bn, bn.size * ["neu"]) solver = pp.P1(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() M_known = np.matrix([[1., -1.], [-1., 1.]]) self.assertTrue(np.allclose(M, M_known)) solver = pp.P1MassMatrix(physics="flow") M = solver.matrix(g, {"param": param}).todense() M_known = np.matrix([[2., 1.], [1., 2.]]) / 6. self.assertTrue(np.allclose(M, M_known))
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 test_rt0_1d_ani(self): g = pp.structured.CartGrid(3, 1) g.compute_geometry() kxx = 1. / (np.sin(g.cell_centers[0, :]) + 1) perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) solver = pp.RT0(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() M_known = np.matrix( [ [0.12954401, 0.06477201, 0., 0., 1., 0., 0.], [0.06477201, 0.29392463, 0.08219031, 0., -1., 1., 0.], [0., 0.08219031, 0.3577336, 0.09667649, 0., -1., 1.], [0., 0., 0.09667649, 0.19335298, 0., 0., -1.], [1., -1., 0., 0., 0., 0., 0.], [0., 1., -1., 0., 0., 0., 0.], [0., 0., 1., -1., 0., 0., 0.], ] ) self.assertTrue(np.allclose(M, M.T)) self.assertTrue(np.allclose(M, M_known))
def initial_condition(self) -> None: """ Initial value for the Darcy fluxes. TODO: Add to THM. """ for g, d in self.gb: d[pp.PARAMETERS] = pp.Parameters() d[pp.PARAMETERS].update_dictionaries( [self.mechanics_parameter_key, self.scalar_parameter_key,] ) self.update_all_apertures(to_iterate=False) self.update_all_apertures() super().initial_condition() for g, d in self.gb: d[pp.STATE]["cell_centers"] = g.cell_centers.copy() p0 = self.initial_scalar(g) state = { self.scalar_variable: p0, "u_exp_0": np.zeros(g.num_cells), "aperture_0": self.aperture(g) * self.length_scale, } iterate = { self.scalar_variable: p0, } # For initial flux pp.set_state(d, state) pp.set_iterate(d, iterate)
def test_periodic_bc_with_fractues(self): """ We set up the test case P |--------| | g2| | D | g3x g0| D | g1| | |--------| 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. The domain is a 2D domain with two fractures (g1, and g2) connected through the periodic bc and 0D intersection (g3) """ n = 8 xmax = 1 ymax = 1 gb = self.generate_grid_with_fractures(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: d["param"] = pp.Parameters(g) left = g.face_centers[0] < tol right = g.face_centers[0] > xmax - tol dir_bc = left + right d["param"].set_bc("flow", pp.BoundaryCondition(g, dir_bc, "dir")) bc_val = np.zeros(g.num_faces) bc_val[dir_bc], _, _ = analytic_p(g.face_centers[:, dir_bc]) d["param"].set_bc_val("flow", bc_val) aperture = 1e-6**(2 - g.dim) d["param"].set_aperture(aperture) pa, _, lpc = analytic_p(g.cell_centers) src = -lpc * g.cell_volumes * aperture d["param"].set_source("flow", src) for _, d in gb.edges(): d["kn"] = 1e10 self.solve(gb, analytic_p)
def _set_data(self): """Create a Parameter object and assign data based on the returned values from the functions (e.g., self.source(t)) """ if "param" not in self._data: self._data["param"] = pp.Parameters(self.grid()) self._data["param"].set_tensor(self.physics, self.diffusivity()) self._data["param"].set_bc(self.physics, self.bc()) self._data["param"].set_bc_val(self.physics, self.bc_val(0.0)) self._data["param"].set_source(self.physics, self.source(0.0)) if self.porosity() is not None: self._data["param"].set_porosity(self.porosity()) if self.aperture() is not None: self._data["param"].set_aperture(self.aperture()) if self.rock_specific_heat() is not None: self._data["param"].set_rock_specific_heat( self.rock_specific_heat()) if self.fluid_specific_heat() is not None: self._data["param"].set_fluid_specific_heat( self.fluid_specific_heat()) if self.rock_density() is not None: self._data["param"].set_rock_density(self.rock_density()) if self.fluid_density() is not None: self._data["param"].set_fluid_density(self.fluid_density())
def add_constant_darcy_flux(gb, upwind, flux, a): """ Adds the constant darcy_flux to all internal and mortar faces, the latter in the "mortar_solution" field. gb - grid bucket upwind- upwind discretization class flux - 3 x 1 flux at all faces [u, v, w] a - cross-sectional area of the fractures. """ for g, d in gb: aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim) d[pp.PARAMETERS]["transport"]["darcy_flux"] = upwind.darcy_flux( g, flux, aperture) for e, d in gb.edges(): g_h = gb.nodes_of_edge(e)[1] p_h = gb.node_props(g_h, pp.PARAMETERS) darcy_flux = p_h["transport"]["darcy_flux"] sign = np.zeros(g_h.num_faces) sign[g_h.get_all_boundary_faces()] = g_h.sign_of_faces( g_h.get_all_boundary_faces()) mg = d["mortar_grid"] sign = mg.master_to_mortar_avg() * sign # d["param"] = pp.Parameters(g_h) darcy_flux_e = sign * (d["mortar_grid"].master_to_mortar_avg() * darcy_flux) if pp.PARAMETERS not in d: d[pp.PARAMETERS] = pp.Parameters(mg, ["transport"], [{ "darcy_flux": darcy_flux_e }]) else: d[pp.PARAMETERS]["transport"]["darcy_flux"] = darcy_flux_e
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(3, 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(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) param.set_bc_val(solver, bc_val) M, rhs = solver.matrix_rhs(g, {"param": param}) up = sps.linalg.spsolve(M, rhs) p = solver.extract_p(g, up) err = np.sum(np.abs(p - p_ex(g.cell_centers))) self.assertTrue(np.isclose(err, 0))
def test_rt0_1d_iso(self): g = pp.structured.CartGrid(3, 1) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) solver = pp.RT0(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() M_known = np.matrix( [ [0.11111111, 0.05555556, 0., 0., 1., 0., 0.], [0.05555556, 0.22222222, 0.05555556, 0., -1., 1., 0.], [0., 0.05555556, 0.22222222, 0.05555556, 0., -1., 1.], [0., 0., 0.05555556, 0.11111111, 0., 0., -1.], [1., -1., 0., 0., 0., 0., 0.], [0., 1., -1., 0., 0., 0., 0.], [0., 0., 1., -1., 0., 0., 0.], ] ) self.assertTrue(np.allclose(M, M.T)) self.assertTrue(np.allclose(M, M_known))
def test_dual_rt0_1d_iso_line(self): g = pp.structured.CartGrid(3, 1) R = cg.rot(np.pi / 6., [0, 0, 1]) g.nodes = np.dot(R, g.nodes) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1) perm.rotate(R) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) solver = pp.RT0(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() # Matrix computed with an already validated code M_known = np.matrix( [ [0.11111111, 0.05555556, 0., 0., 1., 0., 0.], [0.05555556, 0.22222222, 0.05555556, 0., -1., 1., 0.], [0., 0.05555556, 0.22222222, 0.05555556, 0., -1., 1.], [0., 0., 0.05555556, 0.11111111, 0., 0., -1.], [1., -1., 0., 0., 0., 0., 0.], [0., 1., -1., 0., 0., 0., 0.], [0., 0., 1., -1., 0., 0., 0.], ] ) self.assertTrue(np.allclose(M, M.T)) self.assertTrue(np.allclose(M, M_known))
def test_p1_convergence_1d_exact(self): p_ex = lambda pt: 2 * pt[0, :] - 3 for i in np.arange(5): g = pp.structured.CartGrid(3 + i, 1) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1) bn = g.get_boundary_nodes() bc = pp.BoundaryConditionNode(g, bn, bn.size * ["dir"]) bc_val = np.zeros(g.num_nodes) bc_val[bn] = p_ex(g.nodes[:, bn]) solver = pp.P1(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) param.set_bc_val(solver, bc_val) M, rhs = solver.matrix_rhs(g, {"param": param}) p = sps.linalg.spsolve(M, rhs) err = np.sum(np.abs(p - p_ex(g.nodes))) self.assertTrue(np.isclose(err, 0))
def initialize_data(g, data: Dict, keyword: str, specified_parameters: Optional[Dict] = None) -> Dict: """Initialize a data dictionary for a single keyword. The initialization consists of adding a parameter dictionary and initializing a matrix dictionary in the proper fields of data. If there is a Parameters object in data, the new keyword is added using the update_dictionaries method. Args: g: The grid. Can be either standard grid, or mortar grid. data: Outer data dictionary, to which the parameters will be added. keyword: String identifying the parameters. specified_parameters: A dictionary with specified parameters, defaults to empty dictionary. Returns: data: The filled dictionary. """ if not specified_parameters: specified_parameters = {} add_discretization_matrix_keyword(data, keyword) if pp.PARAMETERS in data: data[pp.PARAMETERS].update_dictionaries([keyword], [specified_parameters]) else: data[pp.PARAMETERS] = pp.Parameters(g, [keyword], [specified_parameters]) return data
def test_rt0_2d_iso_simplex_surf(self): g = pp.simplex.StructuredTriangleGrid([1, 1], [1, 1]) R = cg.rot(-np.pi / 4., [1, 1, -1]) g.nodes = np.dot(R, g.nodes) g.compute_geometry() kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kxx, kzz=1) perm.rotate(R) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) solver = pp.RT0(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() # Matrix computed with an already validated code M_known = np.matrix( [ [0.33333333, 0., 0., -0.16666667, 0., -1., 0.], [0., 0.33333333, 0., 0., -0.16666667, 0., -1.], [0., 0., 0.33333333, 0., 0., -1., 1.], [-0.16666667, 0., 0., 0.33333333, 0., -1., 0.], [0., -0.16666667, 0., 0., 0.33333333, 0., -1.], [-1., 0., -1., -1., 0., 0., 0.], [0., -1., 1., 0., -1., 0., 0.], ] ) self.assertTrue(np.allclose(M, M.T)) # We test only the mass-Hdiv part self.assertTrue(np.allclose(M, M_known))
def test_p1_2d_ani_simplex_surf(self): g = pp.simplex.StructuredTriangleGrid([1, 1], [1, 1]) g.compute_geometry() kxx = np.square(g.cell_centers[1, :]) + 1 kyy = np.square(g.cell_centers[0, :]) + 1 kxy = -np.multiply(g.cell_centers[0, :], g.cell_centers[1, :]) perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kyy, kxy=kxy, kzz=1) R = cg.rot(np.pi / 3., [1, 1, 0]) perm.rotate(R) g.nodes = np.dot(R, g.nodes) g.compute_geometry() bn = g.get_boundary_nodes() bc = pp.BoundaryConditionNode(g, bn, bn.size * ["neu"]) solver = pp.P1(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() # Matrix computed with an already validated code M_known = np.matrix( [ [1.11111111, -0.66666667, -0.66666667, 0.22222222], [-0.66666667, 1.5, 0., -0.83333333], [-0.66666667, 0., 1.5, -0.83333333], [0.22222222, -0.83333333, -0.83333333, 1.44444444], ] ) self.assertTrue(np.allclose(M, M.T)) self.assertTrue(np.allclose(M, M_known))
def test_p1_1d_ani(self): g = pp.structured.CartGrid(3, 1) g.compute_geometry() kxx = np.sin(g.cell_centers[0, :]) + 1 perm = pp.SecondOrderTensor(3, kxx, kyy=1, kzz=1) bn = g.get_boundary_nodes() bc = pp.BoundaryConditionNode(g, bn, bn.size * ["neu"]) solver = pp.P1(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() M_known = np.matrix( [ [3.4976884, -3.4976884, 0., 0.], [-3.4976884, 7.93596501, -4.43827662, 0.], [0., -4.43827662, 9.65880718, -5.22053056], [0., 0., -5.22053056, 5.22053056], ] ) self.assertTrue(np.allclose(M, M.T)) self.assertTrue(np.allclose(M, M_known))
def test_rt0_2d_ani_simplex(self): g = pp.simplex.StructuredTriangleGrid([1, 1], [1, 1]) g.compute_geometry() al = np.square(g.cell_centers[1, :]) + np.square(g.cell_centers[0, :]) + 1 kxx = (np.square(g.cell_centers[0, :]) + 1) / al kyy = (np.square(g.cell_centers[1, :]) + 1) / al kxy = np.multiply(g.cell_centers[0, :], g.cell_centers[1, :]) / al perm = pp.SecondOrderTensor(3, kxx=kxx, kyy=kyy, kxy=kxy, kzz=1) bf = g.get_boundary_faces() bc = pp.BoundaryCondition(g, bf, bf.size * ["dir"]) solver = pp.RT0(physics="flow") param = pp.Parameters(g) param.set_tensor(solver, perm) param.set_bc(solver, bc) M = solver.matrix(g, {"param": param}).todense() # Matrix computed with an already validated code M_known = np.matrix( [ [0.39814815, 0., -0.0462963, -0.15740741, 0., -1., 0.], [0., 0.39814815, 0.0462963, 0., -0.15740741, 0., -1.], [-0.0462963, 0.0462963, 0.46296296, 0.00925926, -0.00925926, -1., 1.], [-0.15740741, 0., 0.00925926, 0.34259259, 0., -1., 0.], [0., -0.15740741, -0.00925926, 0., 0.34259259, 0., -1.], [-1., 0., -1., -1., 0., 0., 0.], [0., -1., 1., 0., -1., 0., 0.], ] ) self.assertTrue(np.allclose(M, M.T)) self.assertTrue(np.allclose(M, M_known))
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() gb.add_node_props(["param"]) for g, d in gb: kxx = np.ones(g.num_cells) perm = pp.SecondOrderTensor(gb.dim_max(), kxx) a = 0.01 / np.max(nx) a = np.power(a, gb.dim_max() - g.dim) param = pp.Parameters(g) param.set_tensor("flow", perm) param.set_aperture(a) 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] param.set_bc("flow", bound) param.set_bc_val("flow", bc_val) d["param"] = param for e, d in gb.edges(): gl, _ = gb.nodes_of_edge(e) d_l = gb.node_props(gl) d["kn"] = 1. / np.mean(d_l["param"].get_aperture()) return gb
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 add_data(gb, domain, kf, mesh_value): """ Define the permeability, apertures, boundary conditions and sources """ gb.add_node_props(['param']) tol = 1e-5 a = 1e-4 for g, d in gb: param = pp.Parameters(g) # Assign apertures a_dim = np.power(a, gb.dim_max() - g.dim) aperture = np.ones(g.num_cells) * a_dim param.set_aperture(aperture) # Permeability # Use fracture value in the fractures, i.e., the lower dimensional grids k_frac = np.power(kf, g.dim < gb.dim_max()) p = pp.SecondOrderTensor(3, np.ones(g.num_cells) * k_frac) param.set_tensor('flow', p) param.set_tensor('flow', p) # Source term param.set_source('flow', np.zeros(g.num_cells)) # 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) if g.dim == 2: # Account for the double inflow on the matrix-fracture overlap left_mid = np.array( np.absolute(g.face_centers[1, bound_faces[left]] - 0.5) < mesh_value) bc_val[bound_faces[left]] = - g.face_areas[bound_faces[left]] \ + left_mid * .5 * a else: bc_val[bound_faces[left]] = - \ g.face_areas[bound_faces[left]] * a bc_val[bound_faces[right]] = np.ones(np.sum(right)) param.set_bc('flow', pp.BoundaryCondition(g, bound_faces, labels)) param.set_bc_val('flow', bc_val) else: param.set_bc("flow", pp.BoundaryCondition(g, np.empty(0), np.empty(0))) d['param'] = 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: param = pp.Parameters(g) # Permeability kxx = np.ones(g.num_cells) * np.power(kf, g.dim < gb.dim_max()) if g.dim == 2: perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=kxx, kzz=1) else: perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=1, kzz=1) param.set_tensor("flow", perm) # Source term param.set_source("flow", np.zeros(g.num_cells)) # Assign apertures aperture = np.power(a, gb.dim_max() - g.dim) param.set_aperture(np.ones(g.num_cells) * aperture) # 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]] = -aperture * g.face_areas[bound_faces[left]] bc_val[bound_faces[right]] = 1 param.set_bc("flow", pp.BoundaryCondition(g, bound_faces, labels)) param.set_bc_val("flow", bc_val) else: param.set_bc("flow", pp.BoundaryCondition(g, np.empty(0), np.empty(0))) d["is_tangential"] = True d["param"] = param # Assign coupling permeability gb.add_edge_props("kn") for e, d in gb.edges(): g_l = gb.nodes_of_edge(e)[0] mg = d["mortar_grid"] check_P = mg.low_to_mortar_avg() gamma = check_P * gb.node_props(g_l, "param").get_aperture() d["kn"] = kf * np.ones(mg.num_cells) / gamma
def test_unit_slip(self): """ test unit slip of fractures """ frac = np.array([[1, 1, 1], [1, 2, 1], [2, 2, 1], [2, 1, 1]]).T physdims = np.array([3, 3, 2]) g = pp.meshing.cart_grid([frac], [3, 3, 2], physdims=physdims).grids_of_dimension(3)[0] data = {"param": pp.Parameters(g)} bound = pp.BoundaryConditionVectorial(g, g.get_all_boundary_faces(), "dir") data["param"].set_bc("mechanics", bound) frac_slip = np.zeros((g.dim, g.num_faces)) frac_bnd = g.tags["fracture_faces"] frac_slip[:, frac_bnd] = np.ones((g.dim, np.sum(frac_bnd))) data["param"].set_slip_distance(frac_slip.ravel("F")) solver = pp.FracturedMpsa() A, b = solver.matrix_rhs(g, data, inverter="python") u = np.linalg.solve(A.A, b) u_f = solver.extract_frac_u(g, u) u_c = solver.extract_u(g, u) u_c = u_c.reshape((3, -1), order="F") # obtain fracture faces and cells frac_faces = g.frac_pairs frac_left = frac_faces[0] frac_right = frac_faces[1] cell_left = np.ravel(np.argwhere(g.cell_faces[frac_left, :])[:, 1]) cell_right = np.ravel(np.argwhere(g.cell_faces[frac_right, :])[:, 1]) # Test traction T = solver.traction(g, data, u) T = T.reshape((3, -1), order="F") T_left = T[:, frac_left] T_right = T[:, frac_right] self.assertTrue(np.allclose(T_left, T_right)) # we have u_lhs - u_rhs = 1 so u_lhs should be positive self.assertTrue(np.all(u_c[:, cell_left] > 0)) self.assertTrue(np.all(u_c[:, cell_right] < 0)) mid_ind = int(round(u_f.size / 2)) u_left = u_f[:mid_ind] u_right = u_f[mid_ind:] self.assertTrue(np.all(np.abs(u_left - u_right - 1) < 1e-10)) # fracture displacement should be symetric since everything else is # symetric self.assertTrue(np.allclose(u_left, 0.5)) self.assertTrue(np.allclose(u_right, -0.5))
def set_params(self, gb, kn, kf): kw = "flow" for g, d in gb: parameter_dictionary = {} aperture = np.power(1e-2, gb.dim_max() - g.dim) parameter_dictionary["aperture"] = aperture * np.ones(g.num_cells) b_val = np.zeros(g.num_faces) if g.dim == 2: bound_faces = pp.face_on_side(g, ["ymin", "ymax"]) 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 perm = pp.SecondOrderTensor(kxx=np.ones(g.num_cells)) parameter_dictionary["second_order_tensor"] = perm else: perm = pp.SecondOrderTensor(kxx=kf * np.ones(g.num_cells)) parameter_dictionary["second_order_tensor"] = perm 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 _, d in gb.edges(): mg = d["mortar_grid"] flow_dictionary = { "normal_diffusivity": kn * np.ones(mg.num_cells) } d[pp.PARAMETERS] = pp.Parameters(keywords=["flow"], dictionaries=[flow_dictionary]) d[pp.DISCRETIZATION_MATRICES] = {"flow": {}}
def add_data_advection(gb, domain, tol): # Porosity phi_m = 1e-1 phi_f = 9 * 1e-1 # Density rho_w = 1e3 # kg m^{-3} rho_s = 2 * 1e3 # kg m^{-3} # heat capacity c_w = 4 * 1e3 # J kg^{-1} K^{-1} c_s = 8 * 1e2 # J kg^{-1} K^{-1} c_m = phi_m * rho_w * c_w + (1 - phi_m) * rho_s * c_s c_f = phi_f * rho_w * c_w + (1 - phi_f) * rho_s * c_s for g, d in gb: param = d["param"] rock = g.dim == gb.dim_max() source = np.zeros(g.num_cells) param.set_source("transport", source) param.set_porosity(1) param.set_discharge(d["discharge"]) bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] if bound_faces.size != 0: bound_face_centers = g.face_centers[:, bound_faces] bottom = bound_face_centers[1, :] < domain["ymin"] + tol left = bound_face_centers[0, :] < domain["xmin"] + tol right = bound_face_centers[0, :] > domain["xmax"] - tol boundary = np.logical_or(left, right) labels = np.array(["neu"] * bound_faces.size) labels[boundary] = ["dir"] bc_val = np.zeros(g.num_faces) bc_val[bound_faces[left]] = 1 param.set_bc("transport", pp.BoundaryCondition(g, bound_faces, labels)) param.set_bc_val("transport", bc_val) else: param.set_bc("transport", pp.BoundaryCondition(g, np.empty(0), np.empty(0))) d["param"] = param # Assign coupling discharge gb.add_edge_prop("param") for e, d in gb.edges_props(): g = gb.sorted_nodes_of_edge(e)[1] discharge = gb.node_prop(g, "param").get_discharge() d["param"] = pp.Parameters(g) d["param"].set_discharge(discharge)
def add_data(gb, domain, kf): """ Define the permeability, apertures, boundary conditions """ gb.add_node_props(['param']) tol = 1e-5 a = 1e-4 for g, d in gb: param = pp.Parameters(g) # Permeability kxx = np.ones(g.num_cells) * np.power(kf, g.dim < gb.dim_max()) if g.dim == 2: perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=kxx, kzz=1) else: perm = pp.SecondOrderTensor(g.dim, kxx=kxx, kyy=1, kzz=1) param.set_tensor("flow", perm) # Source term param.set_source("flow", np.zeros(g.num_cells)) # Assign apertures aperture = np.power(a, gb.dim_max() - g.dim) param.set_aperture(np.ones(g.num_cells) * aperture) # 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]] = -aperture \ * g.face_areas[bound_faces[left]] bc_val[bound_faces[right]] = 1 param.set_bc("flow", pp.BoundaryCondition(g, bound_faces, labels)) param.set_bc_val("flow", bc_val) else: param.set_bc("flow", pp.BoundaryCondition(g, np.empty(0), np.empty(0))) d['param'] = param # Assign coupling permeability gb.add_edge_props('kn') for e, d in gb.edges(): gn = gb.nodes_of_edge(e) aperture = np.power(a, gb.dim_max() - gn[0].dim) d['kn'] = np.ones(gn[0].num_cells) * kf / aperture
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 setup_3d(nx, simplex_grid=False): f1 = np.array([[0.2, 0.2, 0.8, 0.8], [0.2, 0.8, 0.8, 0.2], [0.5, 0.5, 0.5, 0.5]]) f2 = np.array([[0.2, 0.8, 0.8, 0.2], [0.5, 0.5, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]]) f3 = np.array([[0.5, 0.5, 0.5, 0.5], [0.2, 0.8, 0.8, 0.2], [0.2, 0.2, 0.8, 0.8]]) fracs = [f1, f2, f3] if not simplex_grid: gb = pp.meshing.cart_grid(fracs, nx, physdims=[1, 1, 1]) else: mesh_kwargs = {} mesh_size = 0.3 mesh_kwargs = { "mesh_size_frac": mesh_size, "mesh_size_bound": 2 * mesh_size, "mesh_size_min": mesh_size / 20, } domain = {"xmin": 0, "ymin": 0, "xmax": 1, "ymax": 1} gb = pp.meshing.simplex_grid(fracs, domain, **mesh_kwargs) gb.add_node_props(["param"]) for g, d in gb: a = 0.01 / np.max(nx) a = np.power(a, gb.dim_max() - g.dim) param = pp.Parameters(g) param.set_aperture(a) # BoundaryCondition left = g.face_centers[0] < 1e-6 top = g.face_centers[2] > 1 - 1e-6 dir_faces = np.argwhere(left) bc_cond = pp.BoundaryCondition(g, dir_faces, ["dir"] * dir_faces.size) bc_val = np.zeros(g.num_faces) bc_val[dir_faces] = 3 bc_val[top] = 2.4 param.set_bc("flow", bc_cond) param.set_bc_val("flow", bc_val) # Source and sink src = np.zeros(g.num_cells) src[0] = np.pi src[-1] = -np.pi param.set_source("flow", src) d["param"] = param gb.add_edge_props("kn") for e, d in gb.edges(): g = gb.nodes_of_edge(e)[0] mg = d["mortar_grid"] check_P = mg.slave_to_mortar_avg() d["kn"] = 1 / (check_P * gb.node_props(g, "param").get_aperture()) return gb
def add_data_darcy(gb, domain, tol): gb.add_node_props(["param", "if_tangent"]) apert = 1e-2 km = 7.5 * 1e-10 # 2.5*1e-11 kf = 5 * 1e-5 for g, d in gb: param = pp.Parameters(g) d["if_tangent"] = True if g.dim == gb.dim_max(): kxx = km else: kxx = kf perm = pp.SecondOrderTensor(g.dim, kxx * np.ones(g.num_cells)) param.set_tensor("flow", perm) param.set_source("flow", np.zeros(g.num_cells)) param.set_aperture(np.power(apert, gb.dim_max() - g.dim)) bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] if bound_faces.size != 0: bound_face_centers = g.face_centers[:, bound_faces] top = bound_face_centers[2, :] > domain["zmax"] - tol bottom = bound_face_centers[2, :] < domain["zmin"] + tol boundary = np.logical_or(top, bottom) labels = np.array(["neu"] * bound_faces.size) labels[boundary] = ["dir"] bc_val = np.zeros(g.num_faces) p = np.abs(domain["zmax"] - domain["zmin"]) * 1e3 * 9.81 bc_val[bound_faces[bottom]] = p param.set_bc("flow", pp.BoundaryCondition(g, bound_faces, labels)) param.set_bc_val("flow", bc_val) else: param.set_bc("flow", pp.BoundaryCondition(g, np.empty(0), np.empty(0))) d["param"] = param # Assign coupling permeability gb.add_edge_prop("kn") for e, d in gb.edges_props(): g = gb.sorted_nodes_of_edge(e)[0] d["kn"] = kf / gb.node_prop(g, "param").get_aperture()