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 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_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 __init__(self, keyword, grids): if isinstance(grids, list): self._grids = grids else: self._grids = [grids] self._discretization = pp.Upwind(keyword) self._name = "Upwind" self.keyword = keyword self.upwind: _MergedOperator self.rhs: _MergedOperator self.outflow_neumann: _MergedOperator _wrap_discretization(self, self._discretization, grids)
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 permeability_upscaling(network, data, mesh_args, directions, do_viz=True, tracer_transport=False): """ Compute bulk permeabilities for a 2d domain with a fracture network. The function sets up a flow field in the specified directions, and calculates an upscaled permeability of corresponding to the calculated flow configuration. Parameters: network (pp.FractureNetwork2d): Network, with domain. data (dictionary): Data to be specified. mesh_args (dictionray): Parameters for meshing. See FractureNetwork2d.mesh() for details. directions (np.array): Directions to upscale permeabilities. Indicated by 0 (x-direction) and or 1 (y-direction). Returns: np.array, dim directions.size: Upscaled permeability in each of the directions. sim_info: Various information on the time spent on this simulation. """ directions = np.asarray(directions) upscaled_perm = np.zeros(directions.size) sim_info = {} for di, direct in enumerate(directions): tic = time.time() filename = str(uuid.uuid4()) gb = network.mesh(tol=network.tol, mesh_args=mesh_args, file_name=filename) toc = time.time() if di == 0: sim_info["num_cells_2d"] = gb.grids_of_dimension(2)[0].num_cells sim_info["time_for_meshing"] = toc - tic gb = _setup_simulation_flow(gb, data, direct) pressure_kw = "flow" mpfa = pp.Mpfa(pressure_kw) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {"pressure": {"cells": 1}} d[pp.DISCRETIZATION] = {"pressure": {"diffusive": mpfa}} coupler = pp.RobinCoupling(pressure_kw, mpfa) for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = {"mortar_darcy_flux": {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = { "lambda": { g1: ("pressure", "diffusive"), g2: ("pressure", "diffusive"), e: ("mortar_darcy_flux", coupler), } } assembler = pp.Assembler(gb) assembler.discretize() # Discretize tic = time.time() A, b = assembler.assemble_matrix_rhs() if di == 0: toc = time.time() sim_info["Time_for_assembly"] = toc - tic tic = time.time() p = spla.spsolve(A, b) if di == 0: toc = time.time() sim_info["Time_for_pressure_solve"] = toc - tic assembler.distribute_variable(p) # Post processing to recover flux tot_pressure = 0 tot_area = 0 tot_inlet_flux = 0 for g, d in gb: # Inlet faces of this grid inlet = d.get("inlet_faces", None) # If no inlet, no need to do anything if inlet is None or inlet.size == 0: continue # Compute flux field in the grid # Internal flux flux = (d[pp.DISCRETIZATION_MATRICES][pressure_kw]["flux"] * d[pp.STATE]["pressure"]) # Contribution from the boundary bound_flux_discr = d[ pp.DISCRETIZATION_MATRICES][pressure_kw]["bound_flux"] flux += bound_flux_discr * d["parameters"][pressure_kw]["bc_values"] # Add contribution from all neighboring lower-dimensional interfaces for e, d_e in gb.edges_of_node(g): mg = d_e["mortar_grid"] if mg.dim == g.dim - 1: flux += (bound_flux_discr * mg.mortar_to_master_int() * d_e[pp.STATE]["mortar_darcy_flux"]) # Add contribution to total inlet flux tot_inlet_flux += np.abs(flux[inlet]).sum() # Next, find the pressure at the inlet faces # The pressure is calculated as the values in the neighboring cells, # plus an offset from the incell-variations pressure_cell = (d[pp.DISCRETIZATION_MATRICES][pressure_kw] ["bound_pressure_cell"] * d[pp.STATE]["pressure"]) pressure_flux = (d[pp.DISCRETIZATION_MATRICES][pressure_kw] ["bound_pressure_face"] * d["parameters"][pressure_kw]["bc_values"]) inlet_pressure = pressure_cell[inlet] + pressure_flux[inlet] # Scale the pressure at the face with the face length. # Also include an aperture scaling here: For the highest dimensional grid, this # will be as scaling with 1. aperture = d[pp.PARAMETERS][pressure_kw]["aperture"][0] tot_pressure += np.sum(inlet_pressure * g.face_areas[inlet] * aperture) # Add to the toal outlet area tot_area += g.face_areas[inlet].sum() * aperture # Also compute the cross sectional area of the domain if g.dim == gb.dim_max(): # Extension of the domain in the active direction dx = g.nodes[direct].max() - g.nodes[direct].min() # Mean pressure at the inlet, which will also be mean pressure difference # over the domain mean_pressure = tot_pressure / tot_area # The upscaled permeability upscaled_perm[di] = (tot_inlet_flux / tot_area) * dx / mean_pressure # End of post processing for permeability upscaling if tracer_transport: _setup_simulation_tracer(gb, data, direct) tracer_variable = "temperature" temperature_kw = "transport" mortar_variable = "lambda_adv" # Identifier of the two terms of the equation adv = "advection" advection_term = "advection" mass_term = "mass" advection_coupling_term = "advection_coupling" adv_discr = pp.Upwind(temperature_kw) adv_coupling = pp.UpwindCoupling(temperature_kw) mass_discretization = pp.MassMatrix(temperature_kw) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {tracer_variable: {"cells": 1}} d[pp.DISCRETIZATION] = { tracer_variable: { advection_term: adv_discr, mass_term: mass_discretization, } } for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) d[pp.PRIMARY_VARIABLES] = {mortar_variable: {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = { advection_coupling_term: { g1: (tracer_variable, adv), g2: (tracer_variable, adv), e: (mortar_variable, adv_coupling), } } pp.fvutils.compute_darcy_flux(gb, keyword_store=temperature_kw, lam_name="mortar_darcy_flux") A, b, block_dof, full_dof = assembler.assemble_matrix_rhs( gb, active_variables=[tracer_variable, mortar_variable], add_matrices=False, ) advection_coupling_term += ("_" + mortar_variable + "_" + tracer_variable + "_" + tracer_variable) mass_term += "_" + tracer_variable advection_term += "_" + tracer_variable lhs = A[mass_term] + data["time_step"] * ( A[advection_term] + A[advection_coupling_term]) rhs_source_adv = data["time_step"] * (b[advection_term] + b[advection_coupling_term]) IEsolver = spla.factorized(lhs) save_every = 10 n_steps = int(np.round(data["t_max"] / data["time_step"])) # Initial condition tracer = np.zeros(rhs_source_adv.size) assembler.distribute_variable( gb, tracer, block_dof, full_dof, variable_names=[tracer_variable, mortar_variable], ) # Exporter exporter = pp.Exporter(gb, name=tracer_variable, folder="viz_tmp") export_fields = [tracer_variable] for i in range(n_steps): if np.isclose(i % save_every, 0): # Export existing solution (final export is taken care of below) assembler.distribute_variable( gb, tracer, block_dof, full_dof, variable_names=[tracer_variable, mortar_variable], ) exporter.write_vtk(export_fields, time_step=int(i // save_every)) tracer = IEsolver(A[mass_term] * tracer + rhs_source_adv) exporter.write_vtk(export_fields, time_step=(n_steps // save_every)) time_steps = np.arange(0, data["t_max"] + data["time_step"], save_every * data["time_step"]) exporter.write_pvd(time_steps) return upscaled_perm, sim_info
def transport(gb, data, solver_name, folder, callback=None, save_every=1): grid_variable = "tracer" mortar_variable = "mortar_tracer" kw = "transport" # Identifier of the discretization operator on each grid advection_term = "advection" source_term = "source" mass_term = "mass" # Identifier of the discretization operator between grids advection_coupling_term = "advection_coupling" # Discretization objects node_discretization = pp.Upwind(kw) source_discretization = pp.ScalarSource(kw) mass_discretization = pp.MassMatrix(kw) edge_discretization = pp.UpwindCoupling(kw) # Loop over the nodes in the GridBucket, define primary variables and discretization schemes for g, d in gb: # Assign primary variables on this grid. It has one degree of freedom per cell. d[pp.PRIMARY_VARIABLES] = {grid_variable: {"cells": 1, "faces": 0}} # Assign discretization operator for the variable. d[pp.DISCRETIZATION] = { grid_variable: { advection_term: node_discretization, source_term: source_discretization, mass_term: mass_discretization, } } # Loop over the edges in the GridBucket, define primary variables and discretizations for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) # The mortar variable has one degree of freedom per cell in the mortar grid d[pp.PRIMARY_VARIABLES] = {mortar_variable: {"cells": 1}} d[pp.COUPLING_DISCRETIZATION] = { advection_coupling_term: { g1: (grid_variable, advection_term), g2: (grid_variable, advection_term), e: (mortar_variable, edge_discretization), } } d[pp.DISCRETIZATION_MATRICES] = {kw: {}} assembler = pp.Assembler() # Assemble the linear system, using the information stored in the GridBucket. By # not adding the matrices, we can arrange them at will to obtain the efficient # solver defined below, which LU factorizes the system only once for all time steps. A, b, block_dof, full_dof = assembler.assemble_matrix_rhs( gb, active_variables=[grid_variable, mortar_variable], add_matrices=False) advection_coupling_term += ("_" + mortar_variable + "_" + grid_variable + "_" + grid_variable) mass_term += "_" + grid_variable advection_term += "_" + grid_variable source_term += "_" + grid_variable lhs = A[mass_term] + data["time_step"] * (A[advection_term] + A[advection_coupling_term]) rhs_source_adv = b[source_term] + data["time_step"] * ( b[advection_term] + b[advection_coupling_term]) IEsolver = sps.linalg.factorized(lhs) n_steps = int(np.round(data["t_max"] / data["time_step"])) # Initial condition tracer = np.zeros(rhs_source_adv.size) assembler.distribute_variable( gb, tracer, block_dof, full_dof, variable_names=[grid_variable, mortar_variable]) # Exporter exporter = pp.Exporter(gb, name="tracer", folder=folder) export_fields = ["tracer"] # Keep track of the outflow for each time step outflow = np.empty(0) # Time loop for i in range(n_steps): # Export existing solution (final export is taken care of below) assembler.distribute_variable( gb, tracer, block_dof, full_dof, variable_names=[grid_variable, mortar_variable], ) if np.isclose(i % save_every, 0): exporter.write_vtk(export_fields, time_step=int(i // save_every)) tracer = IEsolver(A[mass_term] * tracer + rhs_source_adv) outflow = compute_flow_rate(gb, grid_variable, outflow) if callback is not None: callback(gb) exporter.write_vtk(export_fields, time_step=(n_steps // save_every)) time_steps = np.arange(0, data["t_max"] + data["time_step"], save_every * data["time_step"]) exporter.write_pvd(time_steps) return tracer, outflow, A, b, block_dof, full_dof
def test_upwind_coupling_3d_2d_1d_0d(self): f1 = np.array([[0, 1, 1, 0], [0, 0, 1, 1], [0.5, 0.5, 0.5, 0.5]]) f2 = np.array([[0.5, 0.5, 0.5, 0.5], [0, 1, 1, 0], [0, 0, 1, 1]]) f3 = np.array([[0, 1, 1, 0], [0.5, 0.5, 0.5, 0.5], [0, 0, 1, 1]]) gb = pp.meshing.cart_grid([f1, f2, f3], [2, 2, 2], **{"physdims": [1, 1, 1]}) gb.compute_geometry() gb.assign_node_ordering() cell_centers1 = np.array([[0.25, 0.75, 0.25, 0.75], [0.25, 0.25, 0.75, 0.75], [0.5, 0.5, 0.5, 0.5]]) cell_centers2 = np.array([[0.5, 0.5, 0.5, 0.5], [0.25, 0.25, 0.75, 0.75], [0.75, 0.25, 0.75, 0.25]]) cell_centers3 = np.array([[0.25, 0.75, 0.25, 0.75], [0.5, 0.5, 0.5, 0.5], [0.25, 0.25, 0.75, 0.75]]) cell_centers4 = np.array([[0.5], [0.25], [0.5]]) cell_centers5 = np.array([[0.5], [0.75], [0.5]]) cell_centers6 = np.array([[0.75], [0.5], [0.5]]) cell_centers7 = np.array([[0.25], [0.5], [0.5]]) cell_centers8 = np.array([[0.5], [0.5], [0.25]]) cell_centers9 = np.array([[0.5], [0.5], [0.75]]) for g, d in gb: if np.allclose(g.cell_centers[:, 0], cell_centers1[:, 0]): d["node_number"] = 1 elif np.allclose(g.cell_centers[:, 0], cell_centers2[:, 0]): d["node_number"] = 2 elif np.allclose(g.cell_centers[:, 0], cell_centers3[:, 0]): d["node_number"] = 3 elif np.allclose(g.cell_centers[:, 0], cell_centers4[:, 0]): d["node_number"] = 4 elif np.allclose(g.cell_centers[:, 0], cell_centers5[:, 0]): d["node_number"] = 5 elif np.allclose(g.cell_centers[:, 0], cell_centers6[:, 0]): d["node_number"] = 6 elif np.allclose(g.cell_centers[:, 0], cell_centers7[:, 0]): d["node_number"] = 7 elif np.allclose(g.cell_centers[:, 0], cell_centers8[:, 0]): d["node_number"] = 8 elif np.allclose(g.cell_centers[:, 0], cell_centers9[:, 0]): d["node_number"] = 9 else: pass for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) n1 = gb.node_props(g1, "node_number") n2 = gb.node_props(g2, "node_number") if n1 == 1 and n2 == 0: d["edge_number"] = 0 elif n1 == 2 and n2 == 0: d["edge_number"] = 1 elif n1 == 3 and n2 == 0: d["edge_number"] = 2 elif n1 == 4 and n2 == 1: d["edge_number"] = 3 elif n1 == 5 and n2 == 1: d["edge_number"] = 4 elif n1 == 6 and n2 == 1: d["edge_number"] = 5 elif n1 == 7 and n2 == 1: d["edge_number"] = 6 elif n1 == 4 and n2 == 2: d["edge_number"] = 7 elif n1 == 5 and n2 == 2: d["edge_number"] = 8 elif n1 == 8 and n2 == 2: d["edge_number"] = 9 elif n1 == 9 and n2 == 2: d["edge_number"] = 10 elif n1 == 6 and n2 == 3: d["edge_number"] = 11 elif n1 == 7 and n2 == 3: d["edge_number"] = 12 elif n1 == 8 and n2 == 3: d["edge_number"] = 13 elif n1 == 9 and n2 == 3: d["edge_number"] = 14 elif n1 == 10 and n2 == 4: d["edge_number"] = 15 elif n1 == 10 and n2 == 5: d["edge_number"] = 16 elif n1 == 10 and n2 == 6: d["edge_number"] = 17 elif n1 == 10 and n2 == 7: d["edge_number"] = 18 elif n1 == 10 and n2 == 8: d["edge_number"] = 19 elif n1 == 10 and n2 == 9: d["edge_number"] = 20 else: raise ValueError # define discretization key = "transport" upwind = pp.Upwind(key) upwind_coupling = pp.UpwindCoupling(key) variable = "T" assign_discretization(gb, upwind, upwind_coupling, variable) # assign parameters tol = 1e-3 gb.add_node_props(["param"]) a = 1e-2 for g, d in gb: aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim) specified_parameters = {"aperture": aperture} 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, :] < tol right = bound_face_centers[0, :] > 1 - tol labels = np.array(["neu"] * bound_faces.size) labels[np.logical_or(left, right)] = ["dir"] bc_val = np.zeros(g.num_faces) bc_dir = bound_faces[np.logical_or(left, right)] bc_val[bc_dir] = 1 bound = pp.BoundaryCondition(g, bound_faces, labels) specified_parameters.update({"bc": bound, "bc_values": bc_val}) pp.initialize_default_data(g, d, "transport", specified_parameters) add_constant_darcy_flux(gb, upwind, [1, 0, 0], a) assembler = pp.Assembler(gb) assembler.discretize() U_tmp, rhs = assembler.assemble_matrix_rhs() grids = np.empty(gb.num_graph_nodes() + gb.num_graph_edges(), dtype=np.object) variables = np.empty_like(grids) for g, d in gb: grids[d["node_number"]] = g variables[d["node_number"]] = variable for e, d in gb.edges(): grids[d["edge_number"] + gb.num_graph_nodes()] = e variables[d["edge_number"] + gb.num_graph_nodes()] = "lambda_u" U, rhs = permute_matrix_vector(U_tmp, rhs, assembler.block_dof, assembler.full_dof, grids, variables) theta = np.linalg.solve(U.A, rhs) # deltaT = solver.cfl(gb) ( U_known, rhs_known, ) = (_helper_test_upwind_coupling. matrix_rhs_for_test_upwind_coupling_3d_2d_1d_0d()) theta_known = np.array([ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1, 1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, -0.25, -0.25, -0.25, -0.25, 0.25, 0.25, 0.25, 0.25, 0, 0, 0, 0, 0, 0, 0, 0, -5.0e-03, 5.0e-03, -5.0e-03, 5.0e-03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5e-03, 5e-03, -5e-03, 5e-03, 0, 0, -1e-04, 1e-04, 0, 0, ]) rtol = 1e-15 atol = rtol self.assertTrue(np.allclose(U.todense(), U_known, rtol, atol)) self.assertTrue(np.allclose(rhs, rhs_known, rtol, atol)) self.assertTrue(np.allclose(theta, theta_known, rtol, atol))
def test_upwind_coupling_2d_1d_left_right(self): gb, _ = pp.grid_buckets_2d.single_horizontal([1, 2], simplex=False) tol = 1e-3 # define discretization key = "transport" upwind = pp.Upwind(key) upwind_coupling = pp.UpwindCoupling(key) variable = "T" assign_discretization(gb, upwind, upwind_coupling, variable) # assign parameters gb.add_node_props(["param"]) a = 1e-2 for g, d in gb: aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim) specified_parameters = {"aperture": aperture} 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, :] < tol right = bound_face_centers[0, :] > 1 - tol labels = np.array(["neu"] * bound_faces.size) labels[np.logical_or(left, right)] = ["dir"] bc_val = np.zeros(g.num_faces) bc_dir = bound_faces[np.logical_or(left, right)] bc_val[bc_dir] = 1 bound = pp.BoundaryCondition(g, bound_faces, labels) specified_parameters.update({"bc": bound, "bc_values": bc_val}) pp.initialize_default_data(g, d, "transport", specified_parameters) add_constant_darcy_flux(gb, upwind, [1, 0, 0], a) assembler = pp.Assembler(gb) assembler.discretize() U_tmp, rhs = assembler.assemble_matrix_rhs() grids = np.empty(gb.num_graph_nodes() + gb.num_graph_edges(), dtype=np.object) variables = np.empty_like(grids) for g, d in gb: grids[d["node_number"]] = g variables[d["node_number"]] = variable for e, d in gb.edges(): grids[d["edge_number"] + gb.num_graph_nodes()] = e variables[d["edge_number"] + gb.num_graph_nodes()] = "lambda_u" U, rhs = permute_matrix_vector(U_tmp, rhs, assembler.block_dof, assembler.full_dof, grids, variables) theta = np.linalg.solve(U.A, rhs) # deltaT = solver.cfl(gb) U_known = np.array([ [0.5, 0.0, 0.0, 0.0, 1.0], [0.0, 0.5, 0.0, 1.0, 0.0], [0.0, 0.0, 0.01, -1.0, -1.0], [0.0, 0.0, 0.0, -1.0, 0.0], [0.0, 0.0, 0.0, 0.0, -1.0], ]) rhs_known = np.array([0.5, 0.5, 1e-2, 0, 0]) theta_known = np.array([1, 1, 1, 0, 0]) rtol = 1e-15 atol = rtol self.assertTrue(np.allclose(U.todense(), U_known, rtol, atol)) self.assertTrue(np.allclose(rhs, rhs_known, rtol, atol)) self.assertTrue(np.allclose(theta, theta_known, rtol, atol))
def test_upwind_coupling_2d_1d_left_right_cross(self): gb, _ = pp.grid_buckets_2d.two_intersecting([2, 2], simplex=False) # Enforce node orderning because of Python 3.5 and 2.7. # Don't do it in general. cell_centers_1 = np.array([ [7.50000000e-01, 2.5000000e-01], [0.50000000e00, 0.50000000e00], [-5.55111512e-17, 5.55111512e-17], ]) cell_centers_2 = np.array([ [0.50000000e00, 0.50000000e00], [7.50000000e-01, 2.5000000e-01], [-5.55111512e-17, 5.55111512e-17], ]) for g, d in gb: if g.dim == 2: d["node_number"] = 0 elif g.dim == 1: if np.allclose(g.cell_centers, cell_centers_1): d["node_number"] = 1 elif np.allclose(g.cell_centers, cell_centers_2): d["node_number"] = 2 else: raise ValueError("Grid not found") elif g.dim == 0: d["node_number"] = 3 else: raise ValueError for e, d in gb.edges(): g1, g2 = gb.nodes_of_edge(e) n1 = gb.node_props(g1, "node_number") n2 = gb.node_props(g2, "node_number") if n1 == 1 and n2 == 0: d["edge_number"] = 0 elif n1 == 2 and n2 == 0: d["edge_number"] = 1 elif n1 == 3 and n2 == 1: d["edge_number"] = 2 elif n1 == 3 and n2 == 2: d["edge_number"] = 3 else: raise ValueError # define discretization key = "transport" upwind = pp.Upwind(key) upwind_coupling = pp.UpwindCoupling(key) variable = "T" assign_discretization(gb, upwind, upwind_coupling, variable) # define parameters tol = 1e-3 gb.add_node_props(["param"]) a = 1e-2 for g, d in gb: aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim) specified_parameters = {"aperture": aperture} 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, :] < tol right = bound_face_centers[0, :] > 1 - tol labels = np.array(["neu"] * bound_faces.size) labels[np.logical_or(left, right)] = ["dir"] bc_val = np.zeros(g.num_faces) bc_dir = bound_faces[np.logical_or(left, right)] bc_val[bc_dir] = 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}) pp.initialize_default_data(g, d, "transport", specified_parameters) add_constant_darcy_flux(gb, upwind, [1, 0, 0], a) assembler = pp.Assembler(gb) assembler.discretize() U_tmp, rhs = assembler.assemble_matrix_rhs() grids = np.empty(gb.num_graph_nodes() + gb.num_graph_edges(), dtype=np.object) variables = np.empty_like(grids) for g, d in gb: grids[d["node_number"]] = g variables[d["node_number"]] = variable for e, d in gb.edges(): grids[d["edge_number"] + gb.num_graph_nodes()] = e variables[d["edge_number"] + gb.num_graph_nodes()] = "lambda_u" U, rhs = permute_matrix_vector(U_tmp, rhs, assembler.block_dof, assembler.full_dof, grids, variables) theta = np.linalg.solve(U.A, rhs) # deltaT = solver.cfl(gb) U_known = 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, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.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, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.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, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.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, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.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, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 1.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, -1.0, -1.0, -1.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, 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, 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, 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, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5, 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, ], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.5, 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, 0.0, 0.5, 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.5, 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, 0.0, 0.0, 0.01, 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, 0.0, 0.0, 0.0, 0.0, -0.01, 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, 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, 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, ], ]) theta_known = np.array([ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, -0.5, -0.5, 0.5, 0.5, 0.01, -0.01, 0, 0, ]) rhs_known = np.array([ 0.5, 0.0, 0.5, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]) rtol = 1e-15 atol = rtol self.assertTrue(np.allclose(U.todense(), U_known, rtol, atol)) self.assertTrue(np.allclose(rhs, rhs_known, rtol, atol)) self.assertTrue(np.allclose(theta, theta_known, rtol, atol))
def test_upwind_2d_full_beta_bc_dir(self): f = np.array([[2, 2], [0, 2]]) gb = pp.meshing.cart_grid([f], [4, 2]) gb.assign_node_ordering() gb.compute_geometry() # define discretization key = "transport" variable = "T" upwind = pp.Upwind(key) upwind_coupling = pp.UpwindCoupling(key) assign_discretization(gb, upwind, upwind_coupling, variable) a = 1e-1 for g, d in gb: aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim) specified_parameters = {"aperture": aperture} bound_faces = g.tags["domain_boundary_faces"].nonzero()[0] labels = np.array(["dir"] * bound_faces.size) bc_val = np.zeros(g.num_faces) bc_val[bound_faces] = 3 bound = pp.BoundaryCondition(g, bound_faces, labels) specified_parameters.update({"bc": bound, "bc_values": bc_val}) pp.initialize_default_data(g, d, "transport", specified_parameters) add_constant_darcy_flux(gb, upwind, [1, 1, 0], a) assembler = pp.Assembler(gb) assembler.discretize() U_tmp, rhs = assembler.assemble_matrix_rhs() grids = np.empty(gb.num_graph_nodes() + gb.num_graph_edges(), dtype=np.object) variables = np.empty_like(grids) for g, d in gb: grids[d["node_number"]] = g variables[d["node_number"]] = variable for e, d in gb.edges(): grids[d["edge_number"] + gb.num_graph_nodes()] = e variables[d["edge_number"] + gb.num_graph_nodes()] = "lambda_u" M, rhs = permute_matrix_vector(U_tmp, rhs, assembler.block_dof, assembler.full_dof, grids, variables) theta = np.linalg.solve(M.A, rhs) M_known = np.array([ [ 2.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, 1.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, 2.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, -1.0, 2.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, 2.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, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ], [ 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 2.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.1, -1.0, 0.0, -1.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, -1.0, 0.0, -1.0, ], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.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, -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, -1.0, 0.0 ], [ 0.0, 1.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 ], ]) rhs_known = np.array([ 6.0, 3.0, 3.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 0.0 ]) theta_known = np.array([ 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, -3.0, -3.0, 3.0, 3.0 ]) 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(theta, theta_known, rtol, atol))
def test_upwind_2d_beta_positive(self): f = np.array([[2, 2], [0, 2]]) gb = pp.meshing.cart_grid([f], [4, 2]) gb.assign_node_ordering() gb.compute_geometry() # define discretization key = "transport" upwind = pp.Upwind(key) upwind_coupling = pp.UpwindCoupling(key) variable = "T" assign_discretization(gb, upwind, upwind_coupling, variable) gb.add_node_props(["param"]) a = 1e-2 for g, d in gb: aperture = np.ones(g.num_cells) * np.power(a, gb.dim_max() - g.dim) bf = g.tags["domain_boundary_faces"].nonzero()[0] bc = pp.BoundaryCondition(g, bf, bf.size * ["neu"]) specified_parameters = {"aperture": aperture, "bc": bc} pp.initialize_default_data(g, d, "transport", specified_parameters) add_constant_darcy_flux(gb, upwind, [2, 0, 0], a) assembler = pp.Assembler(gb) assembler.discretize() U_tmp, rhs = assembler.assemble_matrix_rhs() grids = np.empty(gb.num_graph_nodes() + gb.num_graph_edges(), dtype=np.object) variables = np.empty_like(grids) for g, d in gb: grids[d["node_number"]] = g variables[d["node_number"]] = variable for e, d in gb.edges(): grids[d["edge_number"] + gb.num_graph_nodes()] = e variables[d["edge_number"] + gb.num_graph_nodes()] = "lambda_u" M, rhs = permute_matrix_vector(U_tmp, rhs, assembler.block_dof, assembler.full_dof, grids, variables) # add generic mass matrix to solve system I_diag = np.zeros(M.shape[0]) I_diag[:gb.num_cells()] = 1 I = np.diag(I_diag) theta = np.linalg.solve(M + I, I_diag + rhs) M_known = np.array([ [ 2.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 ], [ -2.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, 2.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, -2.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, 2.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, -2.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, 2.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.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, -1.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, -1.0, ], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 0.0, -1.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, -2.0, 0.0, -1.0, 0.0, 0.0, ], [ 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0 ], [ 0.0, 2.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 ], ]) rhs_known = np.zeros(14) theta_known = np.array([ 0.33333333, 0.55555556, 0.80246914, 2.60493827, 0.33333333, 0.55555556, 0.80246914, 2.60493827, 0.7037037, 0.7037037, -1.40740741, -1.40740741, 1.11111111, 1.11111111, ]) rtol = 1e-8 atol = rtol self.assertTrue(np.allclose(M.A, M_known, rtol, atol)) self.assertTrue(np.allclose(rhs, rhs_known, rtol, atol)) self.assertTrue(np.allclose(theta, theta_known, rtol, atol))
def test_upwind_example_3(self, if_export=False): ####################### # Simple 2d upwind problem with explicit Euler scheme in time coupled with # a Darcy problem ####################### T = 2 Nx, Ny = 10, 10 def funp_ex(pt): return -np.sin(pt[0]) * np.sin(pt[1]) - pt[0] g = pp.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() # Permeability perm = pp.SecondOrderTensor(kxx=np.ones(g.num_cells)) # Boundaries b_faces = g.get_all_boundary_faces() bc = pp.BoundaryCondition(g, b_faces, ["dir"] * b_faces.size) bc_val = np.zeros(g.num_faces) bc_val[b_faces] = funp_ex(g.face_centers[:, b_faces]) specified_parameters = { "bc": bc, "bc_values": bc_val, "second_order_tensor": perm, } data = pp.initialize_default_data(g, {}, "flow", specified_parameters) solver = pp.MVEM("flow") solver.discretize(g, data) D_flow, b_flow = solver.assemble_matrix_rhs(g, data) solver_source = pp.DualScalarSource("flow") solver_source.discretize(g, data) D_source, b_source = solver_source.assemble_matrix_rhs(g, data) up = sps.linalg.spsolve(D_flow + D_source, b_flow + b_source) _, u = solver.extract_pressure(g, up, data), solver.extract_flux(g, up, data) # Darcy_Flux dis = u # Boundaries bc = pp.BoundaryCondition(g, b_faces, ["dir"] * b_faces.size) bc_val = np.hstack(([1], np.zeros(g.num_faces - 1))) specified_parameters = {"bc": bc, "bc_values": bc_val, "darcy_flux": dis} data = pp.initialize_default_data(g, {}, "transport", specified_parameters) # Advect solver advect = pp.Upwind("transport") advect.discretize(g, data) U, rhs = advect.assemble_matrix_rhs(g, data) time_step = advect.cfl(g, data) rhs = time_step * rhs U = time_step * U data[pp.PARAMETERS]["transport"]["time_step"] = time_step mass = pp.MassMatrix("transport") mass.discretize(g, data) M, _ = mass.assemble_matrix_rhs(g, data) conc = np.zeros(g.num_cells) M_minus_U = M - U inv_mass = pp.InvMassMatrix("transport") inv_mass.discretize(g, data) invM, _ = inv_mass.assemble_matrix_rhs(g, data) # Loop over the time Nt = int(T / time_step) time = np.empty(Nt) for i in np.arange(Nt): # Update the solution conc = invM.dot((M_minus_U).dot(conc) + rhs) time[i] = time_step * i known = np.array( [ 9.63168200e-01, 8.64054875e-01, 7.25390695e-01, 5.72228235e-01, 4.25640080e-01, 2.99387331e-01, 1.99574336e-01, 1.26276876e-01, 7.59011550e-02, 4.33431230e-02, 3.30416807e-02, 1.13058617e-01, 2.05372538e-01, 2.78382057e-01, 3.14035373e-01, 3.09920132e-01, 2.75024694e-01, 2.23163145e-01, 1.67386939e-01, 1.16897527e-01, 1.06111312e-03, 1.11951850e-02, 3.87907727e-02, 8.38516119e-02, 1.36617802e-01, 1.82773271e-01, 2.10446545e-01, 2.14651936e-01, 1.97681518e-01, 1.66549151e-01, 3.20751341e-05, 9.85780113e-04, 6.07062715e-03, 1.99393042e-02, 4.53237556e-02, 8.00799828e-02, 1.17199623e-01, 1.47761481e-01, 1.64729339e-01, 1.65390555e-01, 9.18585872e-07, 8.08267622e-05, 8.47227168e-04, 4.08879583e-03, 1.26336029e-02, 2.88705048e-02, 5.27841497e-02, 8.10459333e-02, 1.07956484e-01, 1.27665318e-01, 2.51295298e-08, 6.29844122e-06, 1.09361990e-04, 7.56743783e-04, 3.11384414e-03, 9.04446601e-03, 2.03443897e-02, 3.75208816e-02, 5.89595194e-02, 8.11457277e-02, 6.63498510e-10, 4.73075468e-07, 1.33728945e-05, 1.30243418e-04, 7.01905707e-04, 2.55272292e-03, 6.96686157e-03, 1.52290448e-02, 2.78607282e-02, 4.40402650e-02, 1.71197497e-11, 3.47118057e-08, 1.57974045e-06, 2.13489614e-05, 1.48634295e-04, 6.68104990e-04, 2.18444135e-03, 5.58646819e-03, 1.17334966e-02, 2.09744728e-02, 4.37822313e-13, 2.52373622e-09, 1.83589660e-07, 3.40553325e-06, 3.02948532e-05, 1.66504215e-04, 6.45119867e-04, 1.90731440e-03, 4.53436628e-03, 8.99977737e-03, 1.12627412e-14, 1.84486857e-10, 2.13562387e-08, 5.39492977e-07, 6.08223906e-06, 4.05535296e-05, 1.84731221e-04, 6.25871542e-04, 1.66459389e-03, 3.59980231e-03, ] ) assert np.allclose(conc, known)
def advdiff(gb, discr, param, bc_flag): model = "transport" model_data_adv, model_data_diff, model_data_src = data_advdiff( gb, model, param, bc_flag ) # discretization operator names adv_id = "advection" diff_id = "diffusion" src_id = "source" # variable names variable = "scalar" mortar_adv = "lambda_" + variable + "_" + adv_id mortar_diff = "lambda_" + variable + "_" + diff_id # save variable name for the post-process param["scalar"] = variable discr_adv = pp.Upwind(model_data_adv) discr_adv_interface = pp.CellDofFaceDofMap(model_data_adv) discr_diff = pp.Tpfa(model_data_diff) discr_diff_interface = pp.CellDofFaceDofMap(model_data_diff) coupling_adv = pp.UpwindCoupling(model_data_adv) coupling_diff = pp.FluxPressureContinuity( model_data_diff, discr_diff, discr_diff_interface ) # mass term mass_id = "mass" discr_mass = pp.MassMatrix(model_data_adv) discr_mass_interface = pp.CellDofFaceDofMap(model_data_adv) discr_src = pp.ScalarSource(model_data_src) for g, d in gb: d[pp.PRIMARY_VARIABLES] = {variable: {"cells": 1}} if g.dim == gb.dim_max(): d[pp.DISCRETIZATION] = { variable: {adv_id: discr_adv, diff_id: discr_diff, mass_id: discr_mass, src_id: discr_src} } else: d[pp.DISCRETIZATION] = { variable: {adv_id: discr_adv_interface, diff_id: discr_diff_interface, mass_id: discr_mass_interface} } 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(gb, active_variables=[variable, mortar_diff, mortar_adv]) logger.info("Assemble the advective and diffusive terms of the transport problem") block_A, block_b = assembler.assemble_matrix_rhs(add_matrices=False) logger.info("done") # unpack the matrices just computed diff_name = diff_id + "_" + variable adv_name = adv_id + "_" + variable mass_name = mass_id + "_" + variable source_name = src_id + "_" + variable diff_coupling_name = diff_id + "_" + mortar_diff + "_" + variable + "_" + variable adv_coupling_name = adv_id + "_" + mortar_adv + "_" + variable + "_" + variable # need a sign for the convention of the conservation equation M = block_A[mass_name] A = block_A[diff_name] + block_A[diff_coupling_name] + \ block_A[adv_name] + block_A[adv_coupling_name] b = block_b[diff_name] + block_b[diff_coupling_name] + \ block_b[adv_name] + block_b[adv_coupling_name] + \ block_b[source_name] M_t = M.copy() / param["time_step"] * param.get("mass_weight", 1) M_r = M.copy() * param.get("reaction", 0) # Perform an LU factorization to speedup the solver IE_solver = sps.linalg.factorized((M_t + A + M_r).tocsc()) # time loop logger.info("Prepare the exporting") save = pp.Exporter(gb, "solution", folder=param["folder"]) logger.info("done") variables = [variable, param["pressure"], "frac_num", "cell_volumes"] if discr["scheme"] is pp.MVEM or discr["scheme"] is pp.RT0: variables.append(param["P0_flux"]) # assign the initial condition x = np.zeros(A.shape[0]) assembler.distribute_variable(x) for g, d in gb: if g.dim == gb.dim_max(): d[pp.STATE][variable] = param.get("init_trans", 0) * np.ones(g.num_cells) x = assembler.merge_variable(variable) outflow = np.zeros(param["n_steps"]) logger.info("Start the time loop with " + str(param["n_steps"]) + " steps") for i in np.arange(param["n_steps"]): logger.info("Solve the linear system for time step " + str(i)) x = IE_solver(b + M_t.dot(x)) logger.info("done") logger.info("Variable post-process") assembler.distribute_variable(x) logger.info("done") logger.info("Export variable") save.write_vtk(variables, time_step=i) logger.info("done") logger.info("Compute the production") outflow[i] = compute_outflow(gb, param) logger.info("done") time = np.arange(param["n_steps"]) * param["time_step"] save.write_pvd(time) logger.info("Save outflow on file") file_out = param["folder"] + "/outflow.csv" data = np.vstack((time, outflow)).T np.savetxt(file_out, data, delimiter=",") logger.info("done") logger.info("Save dof on file") file_out = param["folder"] + "/dof_transport.csv" np.savetxt(file_out, get_dof(assembler), delimiter=",", fmt="%d") logger.info("done")