def __init__(self, gb, data, physics='mechanics', **kwargs): self.physics = physics self._gb = gb if not isinstance(self._gb, Grid): raise ValueError('StaticModel only defined for Grid class') self._data = data self.lhs = [] self.rhs = [] self.x = [] file_name = kwargs.get('file_name', physics) folder_name = kwargs.get('folder_name', 'results') tic = time.time() logger.info('Create exporter') self.exporter = Exporter(self._gb, file_name, folder_name) logger.info('Elapsed time: ' + str(time.time() - tic)) self._stress_disc = self.stress_disc() self.displacement_name = 'displacement' self.frac_displacement_name = 'frac_displacement' self.is_factorized = False
def __init__(self, gb, physics='transport', time_step=1.0, end_time=1.0, **kwargs): self._gb = gb self.is_GridBucket = isinstance(self._gb, GridBucket) self.physics = physics self._data = kwargs.get('data', dict()) self._time_step = time_step self._end_time = end_time self._set_data() self._solver = self.solver() self._solver.parameters['store_results'] = False file_name = kwargs.get('file_name', str(physics)) folder_name = kwargs.get('folder_name', 'results') logger.info('Create exporter') tic = time.time() self.exporter = Exporter(self._gb, file_name, folder_name) logger.info('Done. Elapsed time: ' + str(time.time() - tic)) self.x_name = 'solution' self._time_disc = self.time_disc()
def main(id_problem, tol=1e-5, N_pts=1000, if_export=False): folder_export = 'example_2_2_tpfa/' + str(id_problem) + "/" file_export = 'tpfa' gb = example_2_2_create_grid.create(id_problem, tol=tol) # Assign parameters example_2_2_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flux = tpfa.TpfaDFN(gb.dim_max(), 'flow') A_flux, b_flux = solver_flux.matrix_rhs(gb) solver_source = source.IntegralDFN(gb.dim_max(), 'flow') A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flux + A_source, b_flux + b_source) solver_flux.split(gb, 'pressure', p) if if_export: save = Exporter(gb, file_export, folder_export) save.write_vtk(['pressure']) b_box = gb.bounding_box() y_range = np.linspace(b_box[0][1] + tol, b_box[1][1] - tol, N_pts) pts = np.stack((1.5 * np.ones(N_pts), y_range, 0.5 * np.ones(N_pts))) values = example_2_2_data.plot_over_line(gb, pts, 'pressure', tol) arc_length = y_range - b_box[0][1] np.savetxt(folder_export + "plot_over_line.txt", (arc_length, values)) # compute the flow rate fvutils.compute_discharges(gb, 'flow') diam, flow_rate = example_2_2_data.compute_flow_rate(gb, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate))
def __init__(self, gb, data, physics="mechanics", **kwargs): self.physics = physics self._gb = gb if not isinstance(self._gb, Grid): raise ValueError("StaticModel only defined for Grid class") self._data = data self.lhs = [] self.rhs = [] self.x = [] file_name = kwargs.get("file_name", physics) folder_name = kwargs.get("folder_name", "results") tic = time.time() logger.info("Create exporter") self.exporter = Exporter(self._gb, file_name, folder_name) logger.info("Elapsed time: " + str(time.time() - tic)) self._stress_disc = self.stress_disc() self.displacement_name = "displacement" self.frac_displacement_name = "frac_displacement" self.is_factorized = False
def main(id_problem, is_coarse=False, tol=1e-5, if_export=False): gb = example_1_create_grid.create(0.5 / float(id_problem), tol) if is_coarse: co.coarsen(gb, 'by_tpfa') folder_export = "example_1_vem_coarse/" else: folder_export = "example_1_vem/" file_name_error = folder_export + "vem_error.txt" if if_export: save = Exporter(gb, "vem", folder_export) example_1_data.assign_frac_id(gb) # Assign parameters example_1_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flow = vem_dual.DualVEMDFN(gb.dim_max(), 'flow') A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.DualSourceDFN(gb.dim_max(), 'flow') A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) gb.add_node_props(["discharge", 'pressure', "P0u", "err"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", 'pressure') solver_flow.project_u(gb, "discharge", "P0u") only_max_dim = lambda g: g.dim == gb.dim_max() diam = gb.diameter(only_max_dim) error_pressure = example_1_data.error_pressure(gb, "p") error_discharge = example_1_data.error_discharge(gb, "P0u") print("h=", diam, "- err(p)=", error_pressure, "- err(P0u)=", error_discharge) error_pressure = example_1_data.error_pressure(gb, 'pressure') with open(file_name_error, 'a') as f: info = str(gb.num_cells(only_max_dim)) + " " +\ str(gb.num_cells(only_max_dim)) + " " +\ str(error_pressure) + " " +\ str(error_discharge) + " " +\ str(gb.num_faces(only_max_dim)) + "\n" f.write(info) if if_export: save.write_vtk(['pressure', "err", "P0u"])
def main(id_problem, tol=1e-5, N_pts=1000, if_export=False): mesh_size = 0.025 # 0.01 0.05 folder_export = ("example_2_3_vem_coarse_" + str(mesh_size) + "/" + str(id_problem) + "/") file_export = "vem" gb = example_2_3_create_grid.create(id_problem, is_coarse=True, mesh_size=mesh_size, tol=tol) internal_flag = FaceTag.FRACTURE [g.remove_face_tag_if_tag(FaceTag.BOUNDARY, internal_flag) for g, _ in gb] # Assign parameters example_2_3_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flow = vem_dual.DualVEMDFN(gb.dim_max(), "flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.IntegralDFN(gb.dim_max(), "flow") A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) gb.add_node_props(["discharge", "p", "P0u"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", "p") solver_flow.project_u(gb, "discharge", "P0u") if if_export: save = Exporter(gb, file_export, folder_export) save.write_vtk(["p", "P0u"]) b_box = gb.bounding_box() y_range = np.linspace(b_box[0][1] + tol, b_box[1][1] - tol, N_pts) pts = np.stack((0.35 * np.ones(N_pts), y_range, np.zeros(N_pts))) values = example_2_3_data.plot_over_line(gb, pts, "p", tol) arc_length = y_range - b_box[0][1] np.savetxt(folder_export + "plot_over_line.txt", (arc_length, values)) # compute the flow rate diam, flow_rate = example_2_3_data.compute_flow_rate_vem(gb, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate)) # compute the number of cells num_cells = gb.num_cells(lambda g: g.dim == 2) with open(folder_export + "cells.txt", "w") as f: f.write(str(num_cells))
def main(grid_name, direction): file_export = 'solution' tol = 1e-4 folder_grids = '/home/elle/Dropbox/Work/tipetut/' gb = pickle.load(open(folder_grids + grid_name, 'rb')) co.coarsen(gb, 'by_volume') folder_export = './example_4_vem_coarse_' + grid_name + '_' + direction + '/' domain = { 'xmin': -800, 'xmax': 600, 'ymin': 100, 'ymax': 1500, 'zmin': -100, 'zmax': 1000 } internal_flag = FaceTag.FRACTURE [g.remove_face_tag_if_tag(FaceTag.BOUNDARY, internal_flag) for g, _ in gb] example_4_data.add_data(gb, domain, direction, tol) # Choose and define the solvers and coupler solver_flow = vem_dual.DualVEMDFN(gb.dim_max(), 'flow') A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.IntegralDFN(gb.dim_max(), 'flow') A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) gb.add_node_props(["discharge", "p", "P0u"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", "p") solver_flow.project_u(gb, "discharge", "P0u") save = Exporter(gb, file_export, folder_export) save.write_vtk(["p", "P0u"]) # compute the flow rate diam, flow_rate = example_4_data.compute_flow_rate_vem( gb, direction, domain, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate)) # compute the number of cells num_cells = gb.num_cells(lambda g: g.dim == 2) with open(folder_export + "cells.txt", "w") as f: f.write(str(num_cells))
def solve_tpfa(gb, folder): # Choose and define the solvers and coupler solver_flow = tpfa.TpfaMixedDim("flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = source.IntegralMixedDim("flow") A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "pressure", p) save = Exporter(gb, "sol", folder=folder) save.write_vtk(["pressure"])
def solve_p1(gb, folder, return_only_matrix=False): # Choose and define the solvers and coupler solver_flow = pp.P1MixedDim("flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) A = A_flow if return_only_matrix: return A, mortar_dof_size(A, gb, solver_flow) p = sps.linalg.spsolve(A, b_flow) solver_flow.split(gb, "pressure", p) save = Exporter(gb, "sol", folder=folder, simplicial=True) save.write_vtk("pressure", point_data=True)
def main(id_problem, tol=1e-5, N_pts=1000, if_export=False): mesh_size = 0.15 folder_export = 'example_2_1_vem_coarse_' + str(mesh_size) + '/' + str( id_problem) + "/" file_export = 'vem' gb = example_2_1_create_grid.create(id_problem, is_coarse=True, mesh_size=mesh_size, tol=tol) internal_flag = FaceTag.FRACTURE [g.remove_face_tag_if_tag(FaceTag.BOUNDARY, internal_flag) for g, _ in gb] # Assign parameters example_2_1_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flow = vem_dual.DualVEMDFN(gb.dim_max(), 'flow') A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.DualSourceDFN(gb.dim_max(), 'flow') A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) gb.add_node_props(["discharge", 'pressure', "P0u"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", 'pressure') solver_flow.project_u(gb, "discharge", "P0u") if if_export: save = Exporter(gb, file_export, folder_export) save.write_vtk(['pressure', "P0u"]) b_box = gb.bounding_box() z_range = np.linspace(b_box[0][2] + tol, b_box[1][2] - tol, N_pts) pts = np.stack((0.5 * np.ones(N_pts), 0.5 * np.ones(N_pts), z_range)) values = example_2_1_data.plot_over_line(gb, pts, 'pressure', tol) arc_length = z_range - b_box[0][2] np.savetxt(folder_export + "plot_over_line.txt", (arc_length, values)) # compute the flow rate diam, flow_rate = example_2_1_data.compute_flow_rate_vem(gb, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate))
def solve_rt0(gb, folder): # Choose and define the solvers and coupler solver_flow = rt0.RT0MixedDim("flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.IntegralMixedDim("flow") A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) solver_flow.extract_p(gb, "up", "pressure") save = Exporter(gb, "sol", folder=folder) save.write_vtk(["pressure"])
def solve_rt0(gb, folder, return_only_matrix=False): # Choose and define the solvers and coupler solver_flow = pp.RT0MixedDim("flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) A = A_flow if return_only_matrix: return A, mortar_dof_size(A, gb, solver_flow) up = sps.linalg.spsolve(A, b_flow) solver_flow.split(gb, "up", up) solver_flow.extract_p(gb, "up", "pressure") save = Exporter(gb, "sol", folder=folder) save.write_vtk(["pressure"])
def main(id_problem, tol=1e-5, N_pts=1000, if_export=False): mesh_size = 0.025 # 0.01 0.05 folder_export = 'example_2_3_tpfa_' + str(mesh_size) + '/' + str( id_problem) + "/" file_export = 'tpfa' gb = example_2_3_create_grid.create(id_problem, mesh_size=mesh_size, tol=tol) # Assign parameters example_2_3_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flux = tpfa.TpfaDFN(gb.dim_max(), 'flow') A_flux, b_flux = solver_flux.matrix_rhs(gb) solver_source = source.IntegralDFN(gb.dim_max(), 'flow') A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flux + A_source, b_flux + b_source) solver_flux.split(gb, "p", p) if if_export: save = Exporter(gb, file_export, folder_export) save.write_vtk(["p"]) b_box = gb.bounding_box() y_range = np.linspace(b_box[0][1] + tol, b_box[1][1] - tol, N_pts) pts = np.stack((0.35 * np.ones(N_pts), y_range, np.zeros(N_pts))) values = example_2_3_data.plot_over_line(gb, pts, 'p', tol) arc_length = y_range - b_box[0][1] np.savetxt(folder_export + "plot_over_line.txt", (arc_length, values)) # compute the flow rate fvutils.compute_discharges(gb, 'flow') diam, flow_rate = example_2_3_data.compute_flow_rate(gb, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate)) # compute the number of cells num_cells = gb.num_cells(lambda g: g.dim == 2) with open(folder_export + "cells.txt", "w") as f: f.write(str(num_cells))
def main(grid_name, direction): file_export = "solution" tol = 1e-4 folder_grids = "/home/elle/Dropbox/Work/tipetut/" gb = pickle.load(open(folder_grids + grid_name, "rb")) folder_export = "./example_4_tpfa_" + grid_name + "_" + direction + "/" domain = { "xmin": -800, "xmax": 600, "ymin": 100, "ymax": 1500, "zmin": -100, "zmax": 1000, } example_4_data.add_data(gb, domain, direction, tol) # Choose and define the solvers and coupler solver_flux = tpfa.TpfaDFN(gb.dim_max(), "flow") A_flux, b_flux = solver_flux.matrix_rhs(gb) solver_source = source.IntegralDFN(gb.dim_max(), "flow") A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flux + A_source, b_flux + b_source) solver_flux.split(gb, "p", p) save = Exporter(gb, file_export, folder_export) save.write_vtk(["p"]) # compute the flow rate fvutils.compute_discharges(gb, "flow") diam, flow_rate = example_4_data.compute_flow_rate(gb, direction, domain, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate)) # compute the number of cells num_cells = gb.num_cells(lambda g: g.dim == 2) with open(folder_export + "cells.txt", "w") as f: f.write(str(num_cells))
def main(grid_name, direction): file_export = 'solution' tol = 1e-4 folder_grids = '/home/elle/Dropbox/Work/tipetut/' gb = pickle.load(open(folder_grids + grid_name, 'rb')) folder_export = './example_4_mpfa_' + grid_name + '_' + direction + '/' domain = { 'xmin': -800, 'xmax': 600, 'ymin': 100, 'ymax': 1500, 'zmin': -100, 'zmax': 1000 } example_4_data.add_data(gb, domain, direction, tol) # Choose and define the solvers and coupler solver_flux = mpfa.MpfaDFN(gb.dim_max(), 'flow') A_flux, b_flux = solver_flux.matrix_rhs(gb) solver_source = source.IntegralDFN(gb.dim_max(), 'flow') A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flux + A_source, b_flux + b_source) solver_flux.split(gb, "p", p) save = Exporter(gb, file_export, folder_export) save.write_vtk(["p"]) # compute the flow rate fvutils.compute_discharges(gb, 'flow') diam, flow_rate = example_4_data.compute_flow_rate(gb, direction, domain, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate)) # compute the number of cells num_cells = gb.num_cells(lambda g: g.dim == 2) with open(folder_export + "cells.txt", "w") as f: f.write(str(num_cells))
def main(id_problem, is_coarse=False, tol=1e-5, N_pts=1000, if_export=False): folder_export = "example_2_2_vem/" + str(id_problem) + "/" file_export = "vem" gb = example_2_2_create_grid.create(id_problem, is_coarse=is_coarse, tol=tol) # Assign parameters example_2_2_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flow = vem_dual.DualVEMDFN(gb.dim_max(), "flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.DualSourceDFN(gb.dim_max(), "flow") A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) gb.add_node_props(["discharge", "pressure", "P0u"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", "pressure") solver_flow.project_u(gb, "discharge", "P0u") if if_export: save = Exporter(gb, file_export, folder_export) save.write_vtk(["pressure", "P0u"]) b_box = gb.bounding_box() y_range = np.linspace(b_box[0][1] + tol, b_box[1][1] - tol, N_pts) pts = np.stack((1.5 * np.ones(N_pts), y_range, 0.5 * np.ones(N_pts))) values = example_2_2_data.plot_over_line(gb, pts, "pressure", tol) arc_length = y_range - b_box[0][1] np.savetxt(folder_export + "plot_over_line.txt", (arc_length, values)) # compute the flow rate diam, flow_rate = example_2_2_data.compute_flow_rate_vem(gb, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate))
def main(kf, description, is_coarse=False, if_export=False): mesh_kwargs = {} mesh_kwargs['mesh_size'] = { 'mode': 'constant', 'value': 0.045, 'bound_value': 0.045 } domain = {'xmin': 0, 'xmax': 1, 'ymin': 0, 'ymax': 1} file_name = 'network_geiger.csv' write_network(file_name) gb = importer.dfm_2d_from_csv(file_name, mesh_kwargs, domain) gb.compute_geometry() if is_coarse: co.coarsen(gb, 'by_volume') gb.assign_node_ordering() internal_flag = FaceTag.FRACTURE [g.remove_face_tag_if_tag(FaceTag.BOUNDARY, internal_flag) for g, _ in gb] # Assign parameters add_data(gb, domain, kf) # Choose and define the solvers and coupler solver_flow = vem_dual.DualVEMMixedDim('flow') A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = vem_source.IntegralMixedDim('flow') A_source, b_source = solver_source.matrix_rhs(gb) up = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "up", up) gb.add_node_props(["discharge", 'pressure', "P0u"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", 'pressure') solver_flow.project_u(gb, "discharge", "P0u") if if_export: save = Exporter(gb, "vem", folder="vem_" + description) save.write_vtk(['pressure', "P0u"])
def __init__(self, gb, data=None, physics="flow", **kwargs): self.physics = physics self._gb = gb self.is_GridBucket = isinstance(self._gb, GridBucket) self._data = data self.lhs = [] self.rhs = [] self.x = [] file_name = kwargs.get("file_name", physics) folder_name = kwargs.get("folder_name", "results") mesh_kw = kwargs.get("mesh_kw", {}) tic = time.time() logger.info("Create exporter") self.exporter = Exporter(self._gb, file_name, folder_name, **mesh_kw) logger.info("Elapsed time: " + str(time.time() - tic)) self._flux_disc = self.flux_disc() self._source_disc = self.source_disc()
def __init__(self, gb, data=None, physics='flow', **kwargs): self.physics = physics self._gb = gb self.is_GridBucket = isinstance(self._gb, GridBucket) self._data = data self.lhs = [] self.rhs = [] self.x = [] file_name = kwargs.get('file_name', physics) folder_name = kwargs.get('folder_name', 'results') mesh_kw = kwargs.get('mesh_kw', {}) tic = time.time() logger.info('Create exporter') self.exporter = Exporter(self._gb, file_name, folder_name, **mesh_kw) logger.info('Elapsed time: ' + str(time.time() - tic)) self._flux_disc = self.flux_disc() self._source_disc = self.source_disc()
def main(kf, description, multi_point, if_export=False): # Define the geometry and produce the meshes mesh_kwargs = {} mesh_size = 0.045 mesh_kwargs['mesh_size'] = { 'mode': 'constant', 'value': mesh_size, 'bound_value': mesh_size } domain = {'xmin': 0, 'xmax': 1, 'ymin': 0, 'ymax': 1} file_name = 'network_geiger.csv' write_network(file_name) gb = importer.dfm_2d_from_csv(file_name, mesh_kwargs, domain) gb.compute_geometry() gb.assign_node_ordering() # Assign parameters add_data(gb, domain, kf, mesh_size) # Choose discretization and define the solver if multi_point: solver = mpfa.MpfaMixedDim('flow') else: solver = tpfa.TpfaMixedDim('flow') # Discretize A, b = solver.matrix_rhs(gb) # Solve the linear system p = sps.linalg.spsolve(A, b) # Store the solution gb.add_node_props(['pressure']) solver.split(gb, 'pressure', p) if if_export: save = Exporter(gb, "fv", folder="fv_" + description) save.write_vtk(['pressure'])
def test_upwind_example1(self, if_export=False): ####################### # Simple 2d upwind problem with implicit Euler scheme in time ####################### T = 1 Nx, Ny = 10, 1 g = structured.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() advect = upwind.Upwind("transport") param = Parameters(g) dis = advect.discharge(g, [1, 0, 0]) b_faces = g.get_all_boundary_faces() bc = BoundaryCondition(g, b_faces, ["dir"] * b_faces.size) bc_val = np.hstack(([1], np.zeros(g.num_faces - 1))) param.set_bc("transport", bc) param.set_bc_val("transport", bc_val) data = {"param": param, "discharge": dis} data["deltaT"] = advect.cfl(g, data) U, rhs = advect.matrix_rhs(g, data) M, _ = mass_matrix.MassMatrix().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 / data["deltaT"]) time = np.empty(Nt) folder = "example1" save = Exporter(g, "conc_IE", folder) 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] = data["deltaT"] * i if if_export: save.write_vtk({"conc": conc}, time_step=i) if if_export: save.write_pvd(time) 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 main(id_problem, tol=1e-5, if_export=False): folder_export = "example_1_tpfa/" file_name_error = folder_export + "tpfa_error.txt" gb = example_1_create_grid.create(0.5 / float(id_problem), tol) if if_export: save = Exporter(gb, "tpfa", folder_export) example_1_data.assign_frac_id(gb) # Assign parameters example_1_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flow = tpfa.TpfaDFN(gb.dim_max(), "flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = source.IntegralDFN(gb.dim_max(), "flow") A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flow + A_source, b_flow + b_source) solver_flow.split(gb, "pressure", p) def only_max_dim(g): return g.dim == gb.dim_max() diam = gb.diameter(only_max_dim) error_pressure = example_1_data.error_pressure(gb, "pressure") print("h=", diam, "- err(p)=", error_pressure) with open(file_name_error, "a") as f: info = (str(gb.num_cells(only_max_dim)) + " " + str(gb.num_cells(only_max_dim)) + " " + str(error_pressure) + "\n") f.write(info) if if_export: save.write_vtk(["pressure", "err"])
def main(id_problem, tol=1e-5, N_pts=1000, if_export=False): mesh_size = 0.425 folder_export = "example_2_1_mpfa_" + str(mesh_size) + "/" + str( id_problem) + "/" file_export = "mpfa" gb = example_2_1_create_grid.create(id_problem, mesh_size=mesh_size, tol=tol) # Assign parameters example_2_1_data.add_data(gb, tol) # Choose and define the solvers and coupler solver_flux = mpfa.MpfaDFN(gb.dim_max(), "flow") A_flux, b_flux = solver_flux.matrix_rhs(gb) solver_source = source.IntegralDFN(gb.dim_max(), "flow") A_source, b_source = solver_source.matrix_rhs(gb) p = sps.linalg.spsolve(A_flux + A_source, b_flux + b_source) solver_flux.split(gb, "pressure", p) if if_export: save = Exporter(gb, file_export, folder_export) save.write_vtk(["pressure"]) b_box = gb.bounding_box() z_range = np.linspace(b_box[0][2], b_box[1][2], N_pts) pts = np.stack((0.5 * np.ones(N_pts), 0.5 * np.ones(N_pts), z_range)) values = example_2_1_data.plot_over_line(gb, pts, "pressure", tol) arc_length = z_range - b_box[0][2] np.savetxt(folder_export + "plot_over_line.txt", (arc_length, values)) # compute the flow rate fvutils.compute_discharges(gb, "flow") diam, flow_rate = example_2_1_data.compute_flow_rate(gb, tol) np.savetxt(folder_export + "flow_rate.txt", (diam, flow_rate))
def __init__(self, gb, data, physics='slip', **kwargs): self.physics = physics if isinstance(gb, GridBucket): raise ValueError('FrictionSlip excpected a Grid, not a GridBucket') self._gb = gb self._data = data file_name = kwargs.get('file_name', physics) folder_name = kwargs.get('folder_name', 'results') tic = time.time() logger.info('Create exporter') self.exporter = Exporter(self._gb, file_name, folder_name) logger.info('Elapsed time: ' + str(time.time() - tic)) self.x = np.zeros((3, gb.num_faces)) self.d_n = np.zeros(gb.num_faces) self.is_slipping = np.zeros(gb.num_faces, dtype=np.bool) self.slip_name = 'slip_distance' self.aperture_name = 'aperture_change'
def __init__(self, gb, data, physics="slip", **kwargs): self.physics = physics if isinstance(gb, GridBucket): raise ValueError("FrictionSlip excpected a Grid, not a GridBucket") self._gb = gb self._data = data file_name = kwargs.get("file_name", physics) folder_name = kwargs.get("folder_name", "results") tic = time.time() logger.info("Create exporter") self.exporter = Exporter(self._gb, file_name, folder_name) logger.info("Elapsed time: " + str(time.time() - tic)) self.x = np.zeros((3, gb.num_faces)) self.d_n = np.zeros(gb.num_faces) self.is_slipping = np.zeros(gb.num_faces, dtype=np.bool) self.slip_name = "slip_distance" self.aperture_name = "aperture_change"
def test_upwind_example0(self, if_export=False): ####################### # Simple 2d upwind problem with explicit Euler scheme in time ####################### T = 1 Nx, Ny = 4, 1 g = structured.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() advect = upwind.Upwind("transport") param = Parameters(g) dis = advect.discharge(g, [1, 0, 0]) b_faces = g.get_all_boundary_faces() bc = BoundaryCondition(g, b_faces, ['dir'] * b_faces.size) bc_val = np.hstack(([1], np.zeros(g.num_faces - 1))) param.set_bc("transport", bc) param.set_bc_val("transport", bc_val) data = {'param': param, 'discharge': dis} data['deltaT'] = advect.cfl(g, data) U, rhs = advect.matrix_rhs(g, data) OF = advect.outflow(g, data) M, _ = mass_matrix.MassMatrix().matrix_rhs(g, data) conc = np.zeros(g.num_cells) M_minus_U = M - U invM, _ = mass_matrix.InvMassMatrix().matrix_rhs(g, data) # Loop over the time Nt = int(T / data['deltaT']) time = np.empty(Nt) folder = 'example0' production = np.zeros(Nt) save = Exporter(g, "conc_EE", folder) 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] = data['deltaT'] * i if if_export: save.write_vtk({"conc": conc}, time_step=i) if if_export: save.write_pvd(time) known = 1.09375 assert np.sum(production) == known
gb.add_node_props(["p", "P0u", "discharge"]) solver_flow.extract_u(gb, "up", "discharge") solver_flow.extract_p(gb, "up", "p") solver_flow.project_u(gb, "discharge", "P0u") # compute the flow rate total_flow_rate = 0 for g, d in gb: 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 flow_rate = d["discharge"][bound_faces[left]] total_flow_rate += np.sum(flow_rate) save = Exporter(gb, "darcy", export_folder, binary=False) save.write_vtk(["p", "P0u"]) ################################################################# physics = "transport" advection = upwind.UpwindMixedDim(physics) mass = mass_matrix.MassMatrixMixedDim(physics) invMass = mass_matrix.InvMassMatrixMixDim(physics) # Assign parameters add_data_advection(gb, domain, tol) gb.add_node_prop("deltaT", prop=deltaT) U, rhs_u = advection.matrix_rhs(gb)
class StaticModel(): ''' Class for solving an static elasticity problem flow problem: \nabla \sigma = 0, where nabla is the stress tensor. Parameters in Init: gb: (Grid) a grid object. data (dictionary): dictionary of data. Should contain a Parameter class with the keyword 'Param' physics: (string): defaults to 'mechanics' Functions: solve(): Calls reassemble and solves the linear system. Returns: the displacement d. Sets attributes: self.x step(): Same as solve, but without reassemble of the matrices reassemble(): Assembles the lhs matrix and rhs array. Returns: lhs, rhs. Sets attributes: self.lhs, self.rhs stress_disc(): Defines the discretization of the stress term. Returns stress discretization object (E.g., Mpsa) grid(): Returns: the Grid or GridBucket data(): Returns: Data dictionary traction(name='traction'): Calculate the traction over each face in the grid and assigne it to the data dictionary as keyword name. save(): calls split('d'). Then export the pressure to a vtk file to the folder kwargs['folder_name'] with file name kwargs['file_name'], default values are 'results' for the folder and physics for the file name. ''' def __init__(self, gb, data, physics='mechanics', **kwargs): self.physics = physics self._gb = gb if not isinstance(self._gb, Grid): raise ValueError('StaticModel only defined for Grid class') self._data = data self.lhs = [] self.rhs = [] self.x = [] file_name = kwargs.get('file_name', physics) folder_name = kwargs.get('folder_name', 'results') tic = time.time() logger.info('Create exporter') self.exporter = Exporter(self._gb, file_name, folder_name) logger.info('Elapsed time: ' + str(time.time() - tic)) self._stress_disc = self.stress_disc() self.displacement_name = 'displacement' self.frac_displacement_name = 'frac_displacement' def solve(self, max_direct=40000, callback=False, **kwargs): """ Reassemble and solve linear system. After the funtion has been called, the attributes lhs and rhs are updated according to the parameter states. Also, the attribute x gives the pressure given the current state. The function attempts to set up the best linear solver based on the system size. The setup and parameter choices here are still experimental. Parameters: max_direct (int): Maximum number of unknowns where a direct solver is applied. If a direct solver can be applied this is usually the most efficient option. However, if the system size is too large compared to available memory, a direct solver becomes extremely slow. callback (boolean, optional): If True iteration information will be output when an iterative solver is applied (system size larger than max_direct) Returns: np.array: Pressure state. """ # Discretize tic = time.time() logger.info('Discretize') self.lhs, self.rhs = self.reassemble(**kwargs) logger.info('Done. Elapsed time ' + str(time.time() - tic)) # Solve tic = time.time() ls = LSFactory() if self.rhs.size < max_direct: logger.info('Solve linear system using direct solver') self.x = ls.direct(self.lhs, self.rhs) else: logger.info('Solve linear system using GMRES') precond = self._setup_preconditioner() # precond = ls.ilu(self.lhs) slv = ls.gmres(self.lhs) self.x, info = slv(self.rhs, M=precond, callback=callback, maxiter=10000, restart=1500, tol=1e-8) if info == 0: logger.info('GMRES succeeded.') else: logger.error('GMRES failed with status ' + str(info)) logger.info('Done. Elapsed time ' + str(time.time() - tic)) return self.x def step(self, **kwargs): """ Calls self.solve(**kwargs) """ return self.solve(**kwargs) def reassemble(self, discretize=True): """ reassemble matrices. This must be called between every time step to update the rhs of the system. """ self.lhs, self.rhs = self._stress_disc.matrix_rhs( self.grid(), self.data(), discretize) return self.lhs, self.rhs def stress_disc(self): """ Define the stress discretization. Returns: FracturedMpsa (Solver object) """ return mpsa.FracturedMpsa(physics=self.physics) def grid(self): """ get the model grid Returns: gb (Grid object) """ return self._gb def data(self): """ get data Returns: data (Dictionary) """ return self._data def displacement(self, displacement_name='displacement'): """ Save the cell displacement to the data dictionary. The displacement will be saved as a (3, self.grid().num_cells) array Parameters: ----------- displacement_name: (string) Defaults to 'displacement'. Defines the keyword for the saved displacement in the data dictionary Returns: -------- d: (ndarray) the displacement as a (3, self.grid().num_cells) array """ self.displacement_name = displacement_name d = self._stress_disc.extract_u(self.grid(), self.x) d = d.reshape((3, -1), order='F') self._data[self.displacement_name] = d return d def frac_displacement(self, frac_displacement_name='frac_displacement'): """ Save the fracture displacement to the data dictionary. This is the displacement on the fracture facers. The displacement will be saved as a (3, self.grid().num_cells) array Parameters: ----------- frac_displacement_name: (string) Defaults to 'frac_displacement'. Defines the keyword for the saved displacement in the data dictionary Returns: -------- d: (ndarray) the displacement of size (3, #number of fracture faces) """ self.frac_displacement_name = frac_displacement_name d = self._stress_disc.extract_frac_u(self.grid(), self.x) d = d.reshape((3, -1), order='F') self._data[self.frac_displacement_name] = d return d def traction(self, traction_name='traction'): """ Save the traction on faces to the data dictionary. This is the area scaled traction on the fracture facers. The displacement will be saved as a (3, self.grid().num_cells) array Parameters: ----------- traction_name (string) Defaults to 'traction'. Defines the keyword for the saved traction in the data dictionary Returns: -------- d: (ndarray) the traction as a (3, self.grid().num_faces) array """ T = self._stress_disc.traction(self.grid(), self._data, self.x) T = T.reshape((self.grid().dim, -1), order='F') T_b = np.zeros(T.shape) sigma = self._data['param'].get_background_stress(self.physics) if np.any(sigma): normals = self.grid().face_normals for i in range(normals.shape[1]): T_b[:, i] = np.dot(normals[:, i], sigma) else: T_b = 0 self._data[traction_name] = T + T_b def save(self, variables=None, time_step=None): """ Save the result as vtk. Parameters: ---------- variables: (list) Optional, defaults to None. If None, only the grid will be exported. A list of strings where each element defines a keyword in the data dictionary to be saved. time_step: (float) optinal, defaults to None. The time step of the variable(s) that is saved """ if variables is None: self.exporter.write_vtk() else: variables = {k: self._data[k] for k in variables \ if k in self._data} self.exporter.write_vtk(variables, time_step=time_step) ### Helper functions for linear solve below def _setup_preconditioner(self): solvers, ind, not_ind = self._assign_solvers() def precond(r): x = np.zeros_like(r) for s, i, ni in zip(solvers, ind, not_ind): x[i] += s(r[i]) return x def precond_mult(r): x = np.zeros_like(r) A = self.lhs for s, i, ni in zip(solvers, ind, not_ind): r_i = r[i] - A[i, :][:, ni] * x[ni] x[i] += s(r_i) return x M = lambda r: precond(r) return spl.LinearOperator(self.lhs.shape, M) def _assign_solvers(self): mat, ind = self._obtain_submatrix() all_ind = np.arange(self.rhs.size) not_ind = [np.setdiff1d(all_ind, i) for i in ind] factory = LSFactory() num_mat = len(mat) solvers = np.empty(num_mat, dtype=np.object) for i, A in enumerate(mat): sz = A.shape[0] if sz < 5000: solvers[i] = factory.direct(A) else: # amg solver is pyamg is installed, if not ilu try: solvers[i] = factory.amg(A, as_precond=True) except ImportError: solvers[i] = factory.ilu(A) return solvers, ind, not_ind def _obtain_submatrix(self): return [self.lhs], [np.arange(self.grid().num_cells)]
class FrictionSlipModel: """ Class for solving a frictional slip problem: T_s <= mu * (T_n -p) Parameters in Init: gb: (Grid) a Grid Object. data: (dictionary) Should contain a Parameter class with the keyword 'Param' physics: (string): defaults to 'slip' Functions: solve(): Calls reassemble and solves the linear system. Returns: new slip if T_s > mu * (T_n - p) Sets attributes: self.x, self.is_slipping, self.d_n step(): Same as solve normal_shear_traction(): project the traction into the corresponding normal and shear components grid(): Returns: the Grid or GridBucket data(): Returns: Data dictionary fracture_dilation(slip_distance): Returns: the amount of dilation for given slip distance slip_distance(): saves the slip distance to the data dictionary and returns it aperture_change(): saves the aperture change to the data dictionary and returns it mu(faces): returns: the coefficient of friction gamma(): returns: the numerical step length parameter save(): calls split('pressure'). Then export the pressure to a vtk file to the folder kwargs['folder_name'] with file name kwargs['file_name'], default values are 'results' for the folder and physics for the file name. """ def __init__(self, gb, data, physics="slip", **kwargs): self.physics = physics if isinstance(gb, GridBucket): raise ValueError("FrictionSlip excpected a Grid, not a GridBucket") self._gb = gb self._data = data file_name = kwargs.get("file_name", physics) folder_name = kwargs.get("folder_name", "results") tic = time.time() logger.info("Create exporter") self.exporter = Exporter(self._gb, file_name, folder_name) logger.info("Elapsed time: " + str(time.time() - tic)) self.x = np.zeros((3, gb.num_faces)) self.d_n = np.zeros(gb.num_faces) self.is_slipping = np.zeros(gb.num_faces, dtype=np.bool) self.slip_name = "slip_distance" self.aperture_name = "aperture_change" def solve(self): """ Linearize and solve corresponding system First, the function calculate if the slip-criterion is satisfied for each face: T_s <= mu * (T_n - p). If this is violated, the fracture is slipping. It estimates the slip in direction of shear traction as: d += T_s - mu(T_n - p) * sqrt(face_area) / G. Stores this result in self.x which is a ndarray of dimension (3, number of faces). Also updates the ndarray self.is_slipping to True for any face that violated the slip-criterion. Requires the following keywords in the data dictionary: 'face_pressure': (ndarray) size equal number of faces in the grid. Only the pressure on the fracture faces are used, and should be equivalent to the pressure in the pressure in the corresponding lower dimensional cells. 'traction': (ndarray) size (3, number_of_faces). This should be the area scaled traction on each face. 'rock': a Rock Object with shear stiffness Rock.MU defined. Returns: new_slip (bool) returns True if the slip vector was violated for any faces """ assert self._gb.dim == 3, "only support for 3D (yet)" frac_faces = self._gb.frac_pairs fi = frac_faces[1] fi_left = frac_faces[0] T_n, T_s, n, t = self.normal_shear_traction(fi) assert np.all(T_s > -1e-10) assert np.all(T_n < 0), "Must have a normal force on the fracture" # we find the effective normal stress on the fracture face. # Here we need to multiply T_n with -1 as we want the absolute value, # and all the normal tractions should be negative. sigma_n = -T_n - self._data["face_pressure"][fi] # assert np.all(sigma_n > 0 ) # new slip are fracture faces slipping in this iteration new_slip = (T_s - self.mu(fi, self.is_slipping[fi]) * sigma_n > 1e-5 * self._data["rock"].MU) self.is_slipping[fi] = self.is_slipping[fi] | new_slip # calculate the shear stiffness shear_stiffness = np.sqrt( self._gb.face_areas[fi]) / (self._data["rock"].MU) # calculate aproximated slip distance excess_shear = np.abs(T_s) - self.mu(fi, self.is_slipping[fi]) * sigma_n slip_d = excess_shear * shear_stiffness * self.gamma() * new_slip # We also add the values to the left cells so that when we average the # face values to obtain a cell value, it will equal the face value slip_vec = -t * slip_d - n * self.fracture_dilation(slip_d, fi) self.d_n[fi] += self.fracture_dilation(slip_d, fi) self.d_n[fi_left] += self.fracture_dilation(slip_d, fi_left) assert np.all(self.d_n[fi] > -1e-6) self.x[:, fi] += slip_vec self.x[:, fi_left] -= slip_vec return new_slip def normal_shear_traction(self, faces=None): """ Project the traction vector into the normal and tangential components as seen from the fractures. Requires that the data dictionary has keyword: traction: (ndarray) size (3, number of faces). giving the area weighted traction on each face. Returns: -------- T_n: (ndarray) size (number of fracture_cells) the normal traction on each fracture. T_s: (ndarray) size (number of fracture_cells) the shear traction on each fracture. normals: (ndarray) size (3, number of fracture_cells) normal vector, i.e., the direction of normal traction tangents: (ndarray) size (3, number of fracture_cells) tangential vector, i.e., the direction of shear traction """ if faces is None: frac_faces = self._gb.frac_pairs fi = frac_faces[1] else: fi = faces assert self._gb.dim == 3 T = self._data["traction"].copy() T = T / self._gb.face_areas sgn = sign_of_faces(self._gb, fi) # sgn_test = g.cell_faces[fi, ci] T = sgn * T[:, fi] normals = sgn * self._gb.face_normals[:, fi] / self._gb.face_areas[fi] assert np.allclose(np.sqrt(np.sum(normals**2, axis=0)), 1) T_n = np.sum(T * normals, axis=0) tangents = T - T_n * normals T_s = np.sqrt(np.sum(tangents**2, axis=0)) tangents = tangents / np.sqrt(np.sum(tangents**2, axis=0)) assert np.allclose(np.sqrt(np.sum(tangents**2, axis=0)), 1) assert np.allclose(T, T_n * normals + T_s * tangents) # Sanity check: frac_faces = self._gb.frac_pairs trac = self._data["traction"].copy() fi_left = frac_faces[0] sgn_left = sign_of_faces(self._gb, fi_left) sgn_right = sign_of_faces(self._gb, fi) T_left = sgn_left * trac.reshape((3, -1), order="F")[:, fi_left] T_right = sgn_right * trac.reshape((3, -1), order="F")[:, fi] assert np.allclose(T_left, -T_right) # TESTING DONE return T_n, T_s, normals, tangents def fracture_dilation(self, distance, _): """ defines the fracture dilation as a function of slip distance Parameters: ---------- distance: (ndarray) the slip distances Returns: --------- dilation: (ndarray) the corresponding normal displacement of fractures. """ phi = 1 * np.pi / 180 return distance * np.tan(phi) def mu(self, faces, slip_faces=[]): """ Coefficient of friction. Parameters: ---------- faces: (ndarray) indexes of fracture faces slip_faces: (ndarray) optional, defaults to []. Indexes of faces that are slipping ( will be given a dynamic friciton). returns: mu: (ndarray) the coefficient of each fracture face. """ mu_d = 0.55 mu_ = 0.6 * np.ones(faces.size) mu_[slip_faces] = mu_d return mu_ def gamma(self): """ Numerical step length parameter. Defines of far a fracture violating the slip-condition should slip. """ return 2 def step(self): """ calls self.solve() """ return self.solve() def grid(self): """ returns model grid """ return self._gb def data(self): """ returns data """ return self._data def slip_distance(self, slip_name="slip_distance"): """ Save the slip distance to the data dictionary. The slip distance will be saved as a (3, self.grid().num_faces) array Parameters: ----------- slip_name: (string) Defaults to 'slip_distance'. Defines the keyword for the saved slip distance in the data dictionary Returns: -------- d: (ndarray) the slip distance as a (3, self.grid().num_faces) array """ self.slip_name = slip_name self._data[self.slip_name] = self.x return self.x def aperture_change(self, aperture_name="aperture_change"): """ Save the aperture change to the data dictionary. The aperture change will be saved as a (self.grid().num_faces) array Parameters: ----------- slip_name: (string) Defaults to 'aperture_name'. Defines the keyword for the saved aperture change in the data dictionary Returns: -------- d: (ndarray) the change in aperture as a (self.grid().num_faces) array """ self.aperture_name = aperture_name self._data[self.aperture_name] = self.d_n return self.d_n def save(self, variables=None, save_every=None): """ Save the result as vtk. Parameters: ---------- variables: (list) Optional, defaults to None. If None, only the grid will be exported. A list of strings where each element defines a keyword in the data dictionary to be saved. time_step: (float) optinal, defaults to None. The time step of the variable(s) that is saved """ if variables is None: self.exporter.write_vtk() else: variables = { k: self._data[k] for k in variables if k in self._data } self.exporter.write_vtk(variables)
def test_upwind_example2(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 folder = 'example2' def funp_ex(pt): return -np.sin(pt[0]) * np.sin(pt[1]) - pt[0] g = structured.CartGrid([Nx, Ny], [1, 1]) g.compute_geometry() param = Parameters(g) # Permeability perm = tensor.SecondOrderTensor(g.dim, kxx=np.ones(g.num_cells)) param.set_tensor("flow", perm) # Source term param.set_source("flow", np.zeros(g.num_cells)) # Boundaries b_faces = g.get_all_boundary_faces() bc = 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]) param.set_bc("flow", bc) param.set_bc_val("flow", bc_val) # Darcy solver data = {'param': param} solver = vem_dual.DualVEM("flow") D_flow, b_flow = solver.matrix_rhs(g, data) solver_source = vem_source.DualSource('flow') D_source, b_source = solver_source.matrix_rhs(g, data) up = sps.linalg.spsolve(D_flow + D_source, b_flow + b_source) p, u = solver.extract_p(g, up), solver.extract_u(g, up) P0u = solver.project_u(g, u, data) save = Exporter(g, "darcy", folder) if if_export: save.write_vtk({'pressure': p, "P0u": P0u}) # Discharge dis = u # Boundaries bc = BoundaryCondition(g, b_faces, ['dir'] * b_faces.size) bc_val = np.hstack(([1], np.zeros(g.num_faces - 1))) param.set_bc("transport", bc) param.set_bc_val("transport", bc_val) data = {'param': param, 'discharge': dis} # Advect solver advect = upwind.Upwind("transport") U, rhs = advect.matrix_rhs(g, data) data['deltaT'] = advect.cfl(g, data) M, _ = mass_matrix.MassMatrix().matrix_rhs(g, data) conc = np.zeros(g.num_cells) M_minus_U = M - U invM, _ = mass_matrix.InvMassMatrix().matrix_rhs(g, data) # Loop over the time Nt = int(T / data['deltaT']) time = np.empty(Nt) save.change_name("conc_darcy") for i in np.arange(Nt): # Update the solution conc = invM.dot((M_minus_U).dot(conc) + rhs) time[i] = data['deltaT'] * i if if_export: save.write_vtk({"conc": conc}, time_step=i) if if_export: save.write_pvd(time) 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)