def main(kf, description, is_coarse=False, if_export=False): mesh_size = 0.045 gb, domain = make_grid_bucket(mesh_size) # Assign parameters add_data(gb, domain, kf) # Choose and define the solvers and coupler solver_flow = pp.DualVEMMixedDim("flow") A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = pp.DualSourceMixedDim("flow", coupling=[None]) 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 = pp.Exporter(gb, "vem", folder="vem_" + description) save.write_vtk(["pressure", "P0u"])
def main(): mesh_size = np.power(2., -7) gb = create_gb(mesh_size) param = get_param() scheme = Scheme(gb) scheme.set_data(param) # exporter folder = "./case2/" save = pp.Exporter(gb, "case2", folder_name=folder+"solution_ml") vars_to_save = scheme.vars_to_save() # post process save.write_vtk(vars_to_save, time_step=0) for i in np.arange(param["time"]["num_steps"]): time = (i+1)*param["time"]["step"] print("processing", i, "step at time", time) # do one step of the splitting scheme scheme.one_step_splitting_scheme(time) # post process save.write_vtk(vars_to_save, time_step=time) time = np.arange(param["time"]["num_steps"]+1)*param["time"]["step"] save.write_pvd(time)
def export(gb, folder): gb.add_node_props(["cell_volumes", "cell_centers"]) for g, d in gb: d["cell_volumes"] = g.cell_volumes d["cell_centers"] = g.cell_centers save = pp.Exporter(gb, "sol", folder=folder) props = ["pressure", "cell_volumes", "cell_centers"] # extra properties, problem specific if all(gb.has_nodes_prop(gb.get_grids(), "low_zones")): gb.add_node_props("bottom_domain") for g, d in gb: d["bottom_domain"] = 1 - d["low_zones"] props.append("bottom_domain") if all(gb.has_nodes_prop(gb.get_grids(), "color")): props.append("color") if all(gb.has_nodes_prop(gb.get_grids(), "aperture")): props.append("aperture") save.write_vtk(props)
def _export_vtu(self) -> None: self.exporter = pp.Exporter( self.gb, self.params["file_name"], folder_name=self.params["folder_name"] + "_vtu", ) for g, d in self.gb: if g.dim == self.Nd: pad_zeros = np.zeros((3 - g.dim, g.num_cells)) u = d[pp.STATE][self.displacement_variable].reshape( (self.Nd, -1), order="F") u_exp = np.vstack((u, pad_zeros)) d[pp.STATE]["u_exp"] = u_exp d[pp.STATE]["u_global"] = u_exp d[pp.STATE]["traction_exp"] = np.zeros( d[pp.STATE]["u_exp"].shape) elif g.dim == (self.Nd - 1): pad_zeros = np.zeros((2 - g.dim, g.num_cells)) g_h = self.gb.node_neighbors(g)[0] data_edge = self.gb.edge_props((g, g_h)) u_mortar_local = self.reconstruct_local_displacement_jump( data_edge, d["tangential_normal_projection"], from_iterate=False) u_exp = np.vstack((u_mortar_local, pad_zeros)) d[pp.STATE]["u_exp"] = u_exp traction = d[pp.STATE][self.contact_traction_variable].reshape( (self.Nd, -1), order="F") d[pp.STATE]["traction_exp"] = np.vstack((traction, pad_zeros)) export_fields = ["u_exp", "traction_exp"] self.exporter.write_vtu(export_fields)
def __init__(self, gb, folder, tol): self.model = "flow" self.gb = gb self.data = None self.assembler = None # discretization operator name self.discr_name = "flux" self.discr = pp.RT0(self.model) self.mass_name = "mass" self.mass = pp.MixedMassMatrix(self.model) self.coupling_name = self.discr_name + "_coupling" self.coupling = pp.RobinCoupling(self.model, self.discr) self.source_name = "source" self.source = pp.DualScalarSource(self.model) # master variable name self.variable = "flow_variable" self.mortar = "lambda_" + self.variable # post process variables self.pressure = "pressure" self.flux = "darcy_flux" # it has to be this one self.P0_flux = "P0_darcy_flux" # tolerance self.tol = tol # exporter self.save = pp.Exporter(self.gb, "solution", folder=folder)
def test_import_single_gb(self): folder = "../../geometry/mesh_test_porepy/meshconuna/" gb = import_gb(folder, 2, num_frac=1) for g, d in gb: d[pp.STATE] = {} pp.Exporter(gb, "grid").write_vtk()
def main(kf, description, multi_point, if_export=False): mesh_size = 0.045 gb, domain = make_grid_bucket(mesh_size) # Assign parameters add_data(gb, domain, kf, mesh_size) # Choose discretization and define the solver if multi_point: solver = pp.MpfaMixedDim("flow") else: solver = pp.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 = pp.Exporter(gb, "fv", folder="fv_" + description) save.write_vtk(["pressure"])
def main(): gb = import_gb("cut", 2) param = get_param() scheme = Scheme(gb) scheme.set_data(param) # exporter save = pp.Exporter(gb, "case5", folder_name="solution") vars_to_save = scheme.vars_to_save() # post process scheme.extract() save.write_vtk(vars_to_save, time_step=0) for i in np.arange(param["time"]["num_steps"]): print("processing", i, "step at time", i * param["time"]["step"]) # do one step of the splitting scheme scheme.one_step_splitting_scheme() # post process time = param["time"]["step"] * (i + 1) save.write_vtk(vars_to_save, time_step=time) time = np.arange(param["time"]["num_steps"] + 1) * param["time"]["step"] save.write_pvd(time)
def test_import_single_grid(self): folder = "../../geometry/mesh_test_porepy/mesh_senzafrattura/" fname = "_bulk" g = import_grid(folder, fname, 2) pp.Exporter(g, "grid").write_vtk() pp.plot_grid(g, info="c", alpha=0) print(g)
def export_mortar_grid(g, mg, data_edge, uc, Tc, key, key_m, *vargs, time_step=None, **kwargs): """ Export the displacement jumps and mortar variable on the mortar grid. """ # Get subcell topology and mappings to sub mortar grid s_t = pp.fvutils.SubcellTopology(g) sgn_bnd_hf, slv_2_mrt_nd, mstr_2_mrt_nd = utils.get_mappings(g, mg, s_t) # Get mortar mappings for faces also mg_f2c = data_edge['mortar_grid_f2c'] slavef2mortarc_nd = sps.kron(mg_f2c.slave_to_mortar_int(), sps.eye(g.dim)) hf2f = pp.fvutils.map_hf_2_f(g=g) # First get face valued vectors (from subface values) Tcc, ucc = utils.subface_to_face_mortar(g, mg, data_edge, Tc, uc) t_id = [0, 1] n_id = [2] M_inv, _ = utils.normal_tangential_rotations(g, mg) M = utils.inverse_3dmatrix(M_inv) tc1 = M[:, 0, :] tc2 = M[:, 1, :] nc = M[:, 2, :] # Then get the tangential and normal components T_hat = np.sum(M_inv * Tc, axis=1) Tc_tau = T_hat[t_id[0]] * tc1 + T_hat[t_id[1]] * tc2 Tc_n = T_hat[n_id] * nc Tcc_tau = slavef2mortarc_nd * hf2f * slv_2_mrt_nd.T * Tc_tau.ravel('F') Tcc_tau = Tcc_tau.reshape((g.dim, -1), order='F') Tcc_n = slavef2mortarc_nd * hf2f * slv_2_mrt_nd.T * Tc_n.ravel('F') Tcc_n = Tcc_n.reshape((g.dim, -1), order='F') # Sanity check assert np.allclose(Tcc_tau + Tcc_n, Tcc) # Export face values mg_plot = mg_f2c.side_grids['mortar_grid'] exporter = pp.Exporter(mg_plot, *vargs, **kwargs) exporter.write_vtk( { 'Ttau': Tcc_tau / mg_f2c.cell_volumes, 'Tn': Tcc_n / mg_f2c.cell_volumes, '[u]': ucc }, time_step=time_step)
def export(gb, x, name, solver_flow): solver_flow.split(gb, "up", x) gb.add_node_props("pressure") solver_flow.extract_p(gb, "up", "pressure") solver_flow.extract_u(gb, "up", "discharge") solver_flow.project_u(gb, "discharge", "P0u") save = pp.Exporter(gb, "rt0", folder=name) save.write_vtk(["pressure", "P0u"])
def init_viz(self, overwrite=False): """ Initialize visualization. Will only create a new object if none exists. Alternatively, the existing exporter can be overwritten using 'overwrite'. """ if (self.viz is None) or overwrite: # g3 = self.gb.grids_of_dimension(self.gb.dim_max())[0] self.viz = pp.Exporter(self.gb, name="test_biot", folder=self.viz_folder_name)
def export_nodal_values(g, mg, data_node, u, p, Tc, key, key_m, *vargs, **kwargs): """ Extrapolate the cell centered displacements to the nodes and export to vtk. """ u_n = utils.construct_nodal_values(g, mg, data_node, u, p, Tc, key, key_m) # Export if g.dim == 2: u_n_exp = np.vstack((u_n, np.zeros(u_n.shape[1]))) else: u_n_exp = u_n exporter = pp.Exporter(g, *vargs, **kwargs) exporter.write_vtk({"u": u_n_exp}, point_data=True)
def __init__( self, gb, physics="transport", time_step=1.0, end_time=1.0, callback=None, **kwargs ): self._gb = gb self.is_GridBucket = isinstance(self._gb, pp.GridBucket) self.physics = physics self._data = kwargs.get("data", dict()) self._time_step = time_step self._end_time = end_time self.callback = callback # A hack to make parabolic work with different number of mortars # I.e., if we have only advective or only diffusive, we have 1 # set of mortars, while if we have both advective and diffusive we have # two sets self.advective_term = True self.diffusive_term = True discs = self.space_disc() if not isinstance(discs, tuple): discs = [discs] self.advective_term = False self.diffusive_term = False for d in discs: if isinstance(d, pp.Upwind) or isinstance(d, pp.UpwindMixedDim): self.advective_term = True if isinstance(d, pp.Tpfa) or isinstance(d, pp.TpfaMixedDim): self.diffusive_term = True self._set_data() self._solver = self.solver() logger.info("Create exporter") tic = time.time() file_name = kwargs.get("file_name", "solution") folder_name = kwargs.get("folder_name", "results") self.exporter = pp.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 set_viz(self): """ Set exporter for visualization """ self.viz = pp.Exporter(self.gb, file_name=self.file_name, folder_name=self.viz_folder_name) # list of time steps to export with visualization. self.u_exp = 'u_exp' self.traction_exp = 'traction_exp' self.normal_frac_u = 'normal_frac_u' self.tangential_frac_u = 'tangential_frac_u' self.export_fields = [ self.u_exp, self.traction_exp, self.normal_frac_u, self.tangential_frac_u, ]
def export(gb, folder): props = ["cell_volumes", "cell_centers"] gb.add_node_props(props) for g, d in gb: d["cell_volumes"] = g.cell_volumes d["cell_centers"] = g.cell_centers # extra properties if all(gb.has_nodes_prop(gb.get_grids(), "pressure")): props.append("pressure") if all(gb.has_nodes_prop(gb.get_grids(), "P0u")): props.append("P0u") save = pp.Exporter(gb, "sol", folder=folder) save.write_vtk(props)
def prepare_simulation(self) -> None: """Is run prior to a time-stepping scheme. Use this to initialize discretizations, export for visualization, linear solvers etc. """ self.create_grid() self._Nd = self.gb.dim_max() self._set_parameters() self._assign_variables() self._assign_discretizations() self._initial_condition() self._discretize() self._initialize_linear_solver() g_max = self._nd_grid() self.viz = pp.Exporter(g_max, file_name="mechanics", folder_name=self.viz_folder_name)
def _export_flow(gb, partition, folder): for g, d in gb: d[pp.STATE]["is_low"] = d["is_low"] * np.ones(g.num_cells) d[pp.STATE]["frac_num"] = d["frac_num"] * np.ones(g.num_cells) # in the case of partition for g, d in gb: if g.dim == 2 and partition: g_old, subdiv = partition[g] d[pp.STATE]["pressure"] = d[pp.STATE]["pressure"][subdiv] d[pp.STATE]["is_low"] = d[pp.STATE]["is_low"][subdiv] d[pp.STATE]["frac_num"] = d[pp.STATE]["frac_num"][subdiv] gb.update_nodes({g: g_old}) break save = pp.Exporter(gb, "sol", folder=folder, binary=False) save.write_vtk(["pressure", "is_low", "frac_num"])
def main(kf, is_coarse=False): mesh_size = 0.045 gb, domain = make_grid_bucket(mesh_size) # Assign parameters add_data(gb, domain, kf) # Choose and define the solvers and coupler solver_flow = pp.DualVEMMixedDim("flow") A, b = solver_flow.matrix_rhs(gb) up = sps.linalg.spsolve(A, b) 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") save = pp.Exporter(gb, "vem", folder="vem_ref") save.write_vtk(["pressure", "P0u"])
def prepare_simulation(self): """ Is run prior to a time-stepping scheme. Use this to initialize discretizations, linear solvers etc. TODO: Should the arguments be solver_options and **kwargs (for everything else?) TODO: Examples needed """ self.create_grid() self.Nd = self.gb.dim_max() self.set_parameters() self.assign_variables() self.assign_discretizations() self.initial_condition() self.discretize() self.initialize_linear_solver() g_max = self._nd_grid() self.viz = pp.Exporter( g_max, file_name="mechanics", folder_name=self.viz_folder_name )
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 main(): # tolerance in the computation tol = 1e-10 # assign the flag for the low permeable fractures mesh_size = 0.5 * 1e-2 tol_network = mesh_size mesh_kwargs = { "mesh_size_frac": mesh_size, "mesh_size_min": mesh_size / 20 } # read and mark the original fracture network, the fractures id will be preserved file_name = "network.csv" domain = {"xmin": 0, "xmax": 1, "ymin": -1, "ymax": 1} network = pp.fracture_importer.network_2d_from_csv(file_name, domain=domain) # set the original id network.tags["original_id"] = np.arange(network.num_frac, dtype=np.int) # save the original network network_original = network.copy() # set the condition, meaning if for a branch we solve problem with a < (1) or with > (0) # for simplicity we just set all equal network.tags["condition"] = np.ones(network.num_frac, dtype=np.int) flux_threshold = 0.15 cond = lambda flux, op, tol=0: condition_interface(flux_threshold, flux, op, tol) file_name = "case2" folder_name = "./non_linear/" variable_to_export = [ Flow.pressure, Flow.P0_flux, "original_id", "condition" ] iteration = 0 max_iteration = 50 max_iteration_non_linear = 50 max_err_non_linear = 1e-4 okay = False while not okay: print("iteration", iteration) # create the grid bucket gb = network.mesh(mesh_kwargs, dfn=True, preserve_fracture_tags=["original_id", "condition"]) # create the discretization discr = Flow(gb) # the mesh is changed as well as the interface, do not use the solution at previous step # initialize the non-linear algorithm by setting zero the flux which is equivalent to get # the Darcy solution at the first iteration for g, d in gb: d.update({pp.STATE: {}}) d[pp.STATE].update({Flow.P0_flux: np.zeros((3, g.num_cells))}) d[pp.STATE].update( {Flow.P0_flux + "_old": np.zeros((3, g.num_cells))}) # non-linear problem solution with a fixed point strategy err_non_linear = max_err_non_linear + 1 iteration_non_linear = 0 while err_non_linear > max_err_non_linear and iteration_non_linear < max_iteration_non_linear: # solve the linearized problem discr.set_data(test_data()) A, b = discr.matrix_rhs() x = sps.linalg.spsolve(A, b) discr.extract(x) # compute the exit condition all_flux = np.empty((3, 0)) all_flux_old = np.empty((3, 0)) all_cell_volumes = np.empty(0) for g, d in gb: # collect the current flux flux = d[pp.STATE][Flow.P0_flux] all_flux = np.hstack((all_flux, flux)) # collect the old flux flux_old = d[pp.STATE][Flow.P0_flux + "_old"] all_flux_old = np.hstack((all_flux_old, flux_old)) # collect the cell volumes all_cell_volumes = np.hstack( (all_cell_volumes, g.cell_volumes)) # save the old flux d[pp.STATE][Flow.P0_flux + "_old"] = flux # compute the error and normalize the result err_non_linear = np.sum( all_cell_volumes * np.linalg.norm(all_flux - all_flux_old, axis=0)) norm_flux_old = np.sum(all_cell_volumes * np.linalg.norm(all_flux_old, axis=0)) err_non_linear = err_non_linear / norm_flux_old if norm_flux_old != 0 else err_non_linear print("iteration non-linear problem", iteration_non_linear, "error", err_non_linear) iteration_non_linear += 1 # exporter save = pp.Exporter(gb, "sol_" + file_name, folder_name=folder_name) save.write_vtu(variable_to_export, time_step=iteration) # save the network points to check if we have reached convergence old_network_pts = network.pts # construct the new network such that the interfaces are respected network = detect_interface(gb, network, network_original, discr, cond, tol) # export the current network with the associated tags network_file_name = make_file_name(file_name, iteration) network.to_file(network_file_name, data=network.tags, folder_name=folder_name, binary=False) # check if any point in the network has changed all_pts = np.hstack((old_network_pts, network.pts)) distances = pp.distances.pointset(all_pts) > tol_network # consider only the block between the old and new points distances = distances[:old_network_pts.shape[1], -network.pts.shape[1]:] # check if an old point has a point equal in the new set check = np.any(np.logical_not(distances), axis=0) if np.all(check) or iteration > max_iteration: okay = True iteration += 1 save.write_pvd(np.arange(iteration), np.arange(iteration)) write_network_pvd(file_name, folder_name, np.arange(iteration))
def viscous_flow(disc, data, time_step_param): """ Solve the coupled problem of fluid flow and temperature transport, where the viscosity is depending on the temperature. Darcy's law and mass conservation is solved for the fluid flow: u = -K/mu(T) grad p, div u = 0, where mu(T) is a given temperature depending viscosity. The temperature is advective and diffusive: \partial phi T /\partial t + div (cu) -div (D grad c) = 0. A darcy type coupling is assumed between grids of different dimensions: lambda = -kn/mu(T) * (p^lower - p^higher), and similar for the diffusivity: lambda_c = -D * (c^lower - c^higher). Parameters: disc (discretization.ViscousFlow): A viscous flow discretization class data (data.ViscousData): a viscous flow data class Returns: None The solution is exported to vtk. """ # Get information from data gb = data.gb flow_kw = data.flow_keyword tran_kw = data.transport_keyword # define shorthand notation for discretizations # Flow flux = disc.mat[flow_kw]["flux"] bound_flux = disc.mat[flow_kw]["bound_flux"] trace_p_cell = disc.mat[flow_kw]["trace_cell"] trace_p_face = disc.mat[flow_kw]["trace_face"] bc_val_p = disc.mat[flow_kw]["bc_values"] kn = disc.mat[flow_kw]["kn"] mu = data.viscosity # Transport diff = disc.mat[tran_kw]["flux"] bound_diff = disc.mat[tran_kw]["bound_flux"] trace_c_cell = disc.mat[tran_kw]["trace_cell"] trace_c_face = disc.mat[tran_kw]["trace_face"] bc_val_c = disc.mat[tran_kw]["bc_values"] Dn = disc.mat[tran_kw]["dn"] # Define projections between grids master2mortar, slave2mortar, mortar2master, mortar2slave = projection.mixed_dim_projections( gb) # And between cells and faces avg = projection.cells2faces_avg(gb) div = projection.faces2cells(gb) # Assemble geometric values mass_weight = disc.mat[tran_kw]["mass_weight"] cell_volumes = gb.cell_volumes() * mass_weight mortar_volumes = gb.cell_volumes_mortar() # mortar_area = mortar_volumes * (master2mortar * avg * specific_volume) # Define secondary variables: q_func = lambda p, c, lam: ( (flux * p + bound_flux * bc_val_p) * (mu(avg * c))**-1 + bound_flux * mortar2master * lam) trace_p = lambda p, lam: trace_p_cell * p + trace_p_face * (lam + bc_val_p) trace_c = lambda c, lam_c: trace_c_cell * c + trace_c_face * (lam_c + bc_val_c) # Define discrete equations # Flow, conservation mass_conservation = lambda p, lam, q: (div * q - mortar2slave * lam) # Flow, coupling law coupling_law = lambda p, lam, c: (lam / kn / mortar_volumes + ( slave2mortar * p - master2mortar * trace_p(p, mortar2master * lam) ) / mu((slave2mortar * c + master2mortar * avg * c) / 2)) # Transport # Define upwind and diffusive discretizations upwind = lambda c, lam, q: (div * disc.upwind(c, q) + disc.mortar_upwind( c, lam, div, avg, master2mortar, slave2mortar, mortar2master, mortar2slave)) diffusive = lambda c, lam_c: (div * (diff * c + bound_diff * ( mortar2master * lam_c + bc_val_c)) - mortar2slave * lam_c) # Diffusive copuling law coupling_law_c = lambda c, lam_c: (lam_c / Dn / mortar_volumes + slave2mortar * c - master2mortar * trace_c(c, mortar2master * lam_c)) # Tranpsort, conservation equation theta = 0.5 transport = lambda lam, lam0, c, c0, lam_c, lam_c0, q, q0: ( (c - c0) * (cell_volumes / dt) + theta * (upwind(c, lam, q) + diffusive(c, lam_c)) + (1 - theta) * (upwind(c0, lam0, q0) + (1 - theta) * diffusive(c0, lam_c0))) # Define ad variables print("Solve for initial condition") # We solve for inital pressure and mortar flux by fixing the temperature # to the initial value. c0 = np.zeros(gb.num_cells()) lam_c0 = np.zeros(gb.num_mortar_cells()) # Initial guess for the pressure and mortar flux p0_init = np.zeros(gb.num_cells()) lam0_init = np.zeros(gb.num_mortar_cells()) # Define Ad variables and set up equations p0, lam0 = ad.initAdArrays([p0_init, lam0_init]) eq_init = ad.concatenate( (mass_conservation(p0, lam0, q_func(p0, c0, lam0)), coupling_law(p0, lam0, c0))) # As the temperature is fixed, the system is linear, thus Newton's method converges # in one iteration sol_init = -sps.linalg.spsolve(eq_init.jac, eq_init.val) p0 = sol_init[:gb.num_cells()] lam0 = sol_init[gb.num_cells():] # Now that we have solved for initial condition, initalize full problem p, lam, c, lam_c = ad.initAdArrays([p0, lam0, c0, lam_c0]) sol = np.hstack((p.val, lam.val, c.val, lam_c.val)) q = q_func(p, c, lam) # define dofs indices p_ix = slice(gb.num_cells()) lam_ix = slice(gb.num_cells(), gb.num_cells() + gb.num_mortar_cells()) c_ix = slice( gb.num_cells() + gb.num_mortar_cells(), 2 * gb.num_cells() + gb.num_mortar_cells(), ) lam_c_ix = slice( 2 * gb.num_cells() + gb.num_mortar_cells(), 2 * gb.num_cells() + 2 * gb.num_mortar_cells(), ) # solve system dt = time_step_param["dt"] t = 0 k = 0 exporter = pp.Exporter(gb, time_step_param["file_name"], time_step_param["folder_name"]) # Export initial condition viz.split_variables(gb, [p.val, c.val], ["pressure", "concentration"]) exporter.write_vtk(["pressure", "concentration"], time_step=k) times = [0] # Store average concentration out_file_name = "res_avg_c/" + time_step_param["file_name"] + ".csv" os.makedirs(os.path.dirname(out_file_name), exist_ok=True) out_file = open(out_file_name, "w") out_file.write('time, average_c\n') viz.store_avg_concentration(gb, 0, "concentration", out_file) while t <= time_step_param["end_time"]: t += dt k += 1 print("Solving time step: ", k, " dt: ", dt, " Time: ", t) p0 = p.val lam0 = lam.val c0 = c.val lam_c0 = lam_c.val q0 = q.val err = np.inf newton_it = 0 sol0 = sol.copy() while err > 1e-9: newton_it += 1 q = q_func(p, c, lam) equation = ad.concatenate(( mass_conservation(p, lam, q), coupling_law(p, lam, c), transport(lam, lam0, c, c0, lam_c, lam_c0, q, q0), coupling_law_c(c, lam_c), )) err = np.max(np.abs(equation.val)) if err < 1e-9: break print("newton iteration number: ", newton_it - 1, ". Error: ", err) sol = sol - sps.linalg.spsolve(equation.jac, equation.val) p.val = sol[p_ix] lam.val = sol[lam_ix] c.val = sol[c_ix] lam_c.val = sol[lam_c_ix] if err != err or newton_it > 10 or err > 10e10: # Reset print("failed Netwon, reducing time step") t -= dt / 2 dt = dt / 2 p.val = p0 lam.val = lam0 c.val = c0 lam_c.val = lam_c0 sol = sol0 err = np.inf newton_it = 0 # print(err) print("Converged Newton in : ", newton_it - 1, " iterations. Error: ", err) if newton_it < 3 and dt < time_step_param["max_dt"]: dt = dt * 2 elif newton_it < 7 and dt < time_step_param["max_dt"]: dt *= 1.1 viz.split_variables(gb, [p.val, c.val], ["pressure", "concentration"]) exporter.write_vtk(["pressure", "concentration"], time_step=k) viz.store_avg_concentration(gb, t, "concentration", out_file) times.append(t) exporter.write_pvd(timestep=np.array(times)) out_file.close()
def set_viz(self): self.viz = pp.Exporter( self.gb, file_name=self.params.viz_file_name, folder_name=self.params.folder_name, )
def main(): # tolerance in the computation tol = 1e-10 # assign the flag for the low permeable fractures mesh_size = 0.5*1e-2 tol_network = mesh_size mesh_kwargs = {"mesh_size_frac": mesh_size, "mesh_size_min": mesh_size / 20} # read and mark the original fracture network, the fractures id will be preserved file_name = "network.csv" domain = {"xmin": 0, "xmax": 1, "ymin": -1, "ymax": 1} network = pp.fracture_importer.network_2d_from_csv(file_name, domain=domain) # set the original id network.tags["original_id"] = np.arange(network.num_frac, dtype=np.int) # save the original network network_original = network.copy() # set the condition, meaning if for a branch we solve problem with a < (1) or with > (0) # for simplicity we just set all equal network.tags["condition"] = np.ones(network.num_frac, dtype=np.int) flux_threshold = 0.15 cond = lambda flux, op, tol=0: condition_interface(flux_threshold, flux, op, tol) file_name = "case2" folder_name = "./linear/" variable_to_export = [Flow.pressure, Flow.P0_flux, "original_id", "condition"] iteration = 0 max_iteration = 1e3 okay = False while not okay: print("iteration", iteration) # create the grid bucket gb = network.mesh(mesh_kwargs, dfn=True, preserve_fracture_tags=["original_id", "condition"]) # create the discretization discr = Flow(gb) discr.set_data(test_data()) # problem solution A, b = discr.matrix_rhs() x = sps.linalg.spsolve(A, b) discr.extract(x) # exporter save = pp.Exporter(gb, "sol_" + file_name, folder_name=folder_name) save.write_vtu(variable_to_export, time_step=iteration) # save the network points to check if we have reached convergence old_network_pts = network.pts # construct the new network such that the interfaces are respected network = detect_interface(gb, network, network_original, discr, cond, tol) # export the current network with the associated tags network_file_name = make_file_name(file_name, iteration) network.to_file(network_file_name, data=network.tags, folder_name=folder_name, binary=False) # check if any point in the network has changed all_pts = np.hstack((old_network_pts, network.pts)) distances = pp.distances.pointset(all_pts) > tol_network # consider only the block between the old and new points distances = distances[:old_network_pts.shape[1], -network.pts.shape[1]:] # check if an old point has a point equal in the new set check = np.any(np.logical_not(distances), axis=0) if np.all(check) or iteration > max_iteration: okay = True iteration += 1 save.write_pvd(np.arange(iteration), np.arange(iteration)) write_network_pvd(file_name, folder_name, np.arange(iteration))
continue E0 = d['flow_data'].E0 d['param'].set_aperture(E0 + d[name]) # # Set up solvers # We are finally ready to define our solver objects and solve for flow and temperature. With all parameters defined, this is a relatively simple code: # In[8]: gb = create_grid() g3 = gb.grids_of_dimension(3)[0] data3 = gb.node_props(g3) # Create an exporter object, and dump the grid exporter = pp.Exporter(gb, 'low_pressure_stimulation', folder='results') exporter.write_vtk() # The resulting grid looks like this, after some manipulation in Paraview # In[9]: display(HTML("<img src='fig/mesh.png'>")) # ### Define solvers # The flow problem is dependent on time, and needs the time step as an argument. The mechanics and fracture deformation are both quasi-static, i.e., slip happens instantaneous when the Mohr-Colomb criterion is violated # # In[10]: # Define the time stepping
def run_biot(setup): """ Function for solving the time dependent Biot equations with a non-linear Coulomb contact condition on the fractures. There are some assumtions on the variable and discretization names given to the grid bucket: 'u': The displacement variable 'p': Fluid pressure variable 'lam': The mortar variable Furthermore, the parameter keyword from the elasticity is assumed the same as the parameter keyword from the contact condition. In addition to the standard parameters for Biot we also require the following under the mechanics keyword (returned from setup.set_parameters): 'friction_coeff' : The coefficient of friction 'c' : The numerical parameter in the non-linear complementary function. and for the parameters under the fluid flow keyword: 'time_step': time step of implicit Euler. Arguments: setup: A setup class with methods: set_parameters(g, data_node, mg, data_edge): assigns data to grid bucket. Returns the keyword for the linear elastic parameters and a keyword for the contact mechanics parameters. create_grid(): Create and return the grid bucket initial_condition(): Returns initial guess for 'u' and 'lam'. and attributes: out_name(): returns a string. The data from the simulation will be written to the file 'res_data/' + setup.out_name and the vtk files to 'res_plot/' + setup.out_name end_time: End time time of simulation. """ gb = setup.create_grid() # Extract the grids we use dim = gb.dim_max() g = gb.grids_of_dimension(dim)[0] data_node = gb.node_props(g) data_edge = gb.edge_props((g, g)) mg = data_edge['mortar_grid'] # set parameters key_m, key_f = setup.set_parameters(g, data_node, mg, data_edge) # Short hand for some parameters F = data_edge[pp.PARAMETERS][key_m]['friction_coeff'] c_num = data_edge[pp.PARAMETERS][key_m]['c'] dt = data_node[pp.PARAMETERS][key_f]["time_step"] # Define rotations M_inv, nc = utils.normal_tangential_rotations(g, mg) # Set up assembler and get initial condition assembler = pp.Assembler() u0 = data_node[pp.PARAMETERS][key_m]['state']['displacement'].reshape( (g.dim, -1), order='F') p0 = data_node[pp.PARAMETERS][key_f]['state'] Tc = data_edge[pp.PARAMETERS][key_m]['state'].reshape((g.dim, -1), order='F') # Reconstruct displacement jump on fractures uc = reconstruct_mortar_displacement(u0, Tc, g, mg, data_node, data_edge, key_m, key_f, pressure=p0) uc0 = uc.copy() # Define function for splitting the global solution vector def split_solution_vector(x, block_dof, full_dof): # full_dof contains the number of dofs per block. To get a global ordering, use global_dof = np.r_[0, np.cumsum(full_dof)] # split global variable block_u = block_dof[(g, "u")] block_p = block_dof[(g, "p")] block_lam = block_dof[((g, g), "lam_u")] # Get the global displacement and pressure dofs u_dof = np.arange(global_dof[block_u], global_dof[block_u + 1]) p_dof = np.arange(global_dof[block_p], global_dof[block_p + 1]) lam_dof = np.arange(global_dof[block_lam], global_dof[block_lam + 1]) # Plot pressure and displacements u = x[u_dof].reshape((g.dim, -1), order="F") p = x[p_dof] lam = x[lam_dof].reshape((g.dim, -1), order="F") return u, p, lam # prepare for time loop sol = None # inital guess for Newton solver. T_contact = [] u_contact = [] save_sliding = [] errors = [] exporter = pp.Exporter(g, setup.out_name, 'res_plot') t = 0.0 T = setup.end_time k = 0 times = [] newton_it = 0 while t < T: t += dt k += 1 print('Time step: ', k, '/', int(np.ceil(T / dt))) times.append(t) # Prepare for Newton counter_newton = 0 converged_newton = False max_newton = 12 newton_errors = [] while counter_newton <= max_newton and not converged_newton: print('Newton iteration number: ', counter_newton, '/', max_newton) counter_newton += 1 bf = F * np.clip(np.sum(nc * (-Tc + c_num * uc), axis=0), 0, np.inf) ducdt = (uc - uc0) / dt # Find the robin weight mortar_weight, robin_weight, rhs = contact_coulomb( Tc, ducdt, F, bf, c_num, c_num, M_inv) rhs = rhs.reshape((g.dim, -1), order='F') for i in range(mg.num_cells): robin_weight[i] = robin_weight[i] / dt rhs[:, i] = rhs[:, i] + robin_weight[i].dot(uc0[:, i]) data_edge[pp.PARAMETERS][key_m]['robin_weight'] = robin_weight data_edge[pp.PARAMETERS][key_m]['mortar_weight'] = mortar_weight data_edge[pp.PARAMETERS][key_m]['robin_rhs'] = rhs.ravel('F') # Re-discretize and solve A, b, block_dof, full_dof = assembler.assemble_matrix_rhs(gb) print('max A: ', np.max(np.abs(A))) print('max A sum: ', np.max(np.sum(np.abs(A), axis=1))) print('min A sum: ', np.min(np.sum(np.abs(A), axis=1))) if A.shape[0] > 10000: sol, msg, err = solvers.fixed_stress(gb, A, b, block_dof, full_dof, sol) if msg != 0: # did not converge. print('Iterative solver failed.') else: sol = sps.linalg.spsolve(A, b) # Split solution in the different variables u, p, Tc = split_solution_vector(sol, block_dof, full_dof) # Reconstruct displacement jump on mortar boundary uc = reconstruct_mortar_displacement(u, Tc, g, mg, data_node, data_edge, key_m, key_f, pressure=p) # Calculate the error if np.sum((u - u0)**2 * g.cell_volumes) / np.sum( u**2 * g.cell_volumes) < 1e-10: converged_newton = True print('error: ', np.sum((u - u0)**2) / np.sum(u**2)) newton_errors.append(np.sum((u - u0)**2) / np.sum(u**2)) # Prepare for nect newton iteration u0 = u newton_it += 1 errors.append(newton_errors) # Prepare for next time step uc0 = uc.copy() T_contact.append(Tc) u_contact.append(uc) mech_bc = data_node[pp.PARAMETERS][key_m]['bc_values'].copy() data_node[pp.PARAMETERS][key_m]['bc_values'] = setup.bc_values( g, t + dt, key_m) data_node[pp.PARAMETERS][key_f]['bc_values'] = setup.bc_values( g, t + dt, key_f) data_node[pp.PARAMETERS][key_m]["state"]["displacement"] = u.ravel( 'F').copy() data_node[pp.PARAMETERS][key_m]["state"]["bc_values"] = mech_bc data_node[pp.PARAMETERS][key_f]["state"] = p.copy() data_edge[pp.PARAMETERS][key_m]["state"] = Tc.ravel('F').copy() if g.dim == 2: u_exp = np.vstack((u, np.zeros(u.shape[1]))) elif g.dim == 3: u_exp = u.copy() m_exp_name = setup.out_name + "_mortar_grid" viz.export_mortar_grid(g, mg, data_edge, uc, Tc, key_m, key_m, m_exp_name, "res_plot", time_step=k) exporter.write_vtk({"u": u_exp, 'p': p}, time_step=k) exporter.write_pvd(np.array(times))
'mesh_size_bound': 40, 'mesh_size_min': 1e-1 } domain = {'xmin': 0, 'xmax': 700, 'ymin': 0, 'ymax': 600} gb = pp.importer.dfm_2d_from_csv("network.csv", mesh_kwargs, domain) gb.compute_geometry() pp.coarsening.coarsen(gb, 'by_volume') gb.assign_node_ordering() # Assign parameters add_data(gb, domain) # Choose and define the solvers and coupler solver_flow = pp.DualVEMMixedDim('flow') A_flow, b_flow = solver_flow.matrix_rhs(gb) solver_source = pp.DualSourceMixedDim('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") save = pp.Exporter(gb, "vem", folder="vem") save.write_vtk(['pressure', "P0u"])
equation_manager = pp.ad.EquationManager(gb, dof_manager) p = equation_manager.merge_variables([(g, pressure_var) for g in grid_list]) p_m = p.previous_iteration() p_n = p.previous_timestep() #%% We let the density to be a non-linear function of the pressure def rho(p): if isinstance(p, pp.ad.Ad_array): return rho_ref * pp.ad.exp(c * (p - p_ref)) else: return rho_ref * np.exp(c * (p - p_ref)) rho_ad = pp.ad.Function(rho, name="density") #%% Initialize exporter exporter = pp.Exporter(gb, "compressible_1p", "out") d[pp.STATE]["p_ex"] = p_ex(cc[0], cc[1], time * np.ones_like(cc[0])) exporter.write_vtu(["p_ex", pressure_var], time_step=0) #%% Declare AD operators and equations assign_parameters(time) div_ad = pp.ad.Divergence(grid_list) # discrete diveregence bound_ad = pp.ad.BoundaryCondition(param_key, grids=grid_list) # boundary vals dir_bound_ad = DirBC(bound_ad, grid_list) source_ad = pp.ad.ParameterArray(param_key, "source", grids=grid_list) mass_ad = pp.ad.MassMatrixAd(param_key, grid_list) mpfa_ad = pp.ad.MpfaAd(param_key, grid_list) flux_inactive = mpfa_ad.flux * p_m + mpfa_ad.bound_flux * bound_ad flux_active = mpfa_ad.flux * p + mpfa_ad.bound_flux * bound_ad
accumulation_ad = accum_active + accum_inactive # Continuity equation continuity_ad = accumulation_ad + dt * div_ad * flux_ad - dt * source_ad # We need to keep track of the pressure traces h_trace = mpfa_ad.bound_pressure_cell * h + mpfa_ad.bound_pressure_face * bound_ad psi_trace = h_trace - z_fc #%% Assemble the system of equations eqs = pp.ad.Expression(continuity_ad, dof_manager) # convert to expression equation_manager.equations.clear() equation_manager.equations.append(eqs) # feed eq to the equation manager #%% Initialize exporter exporter = pp.Exporter(gb, "new_mexico", "out") exporter.write_vtu([pressure_var], time_step=0) #%% Time loop total_iteration_counter = 0 # Time loop for n in range(1, num_time_steps + 1): time += dt recompute_solution = True sat_faces = [] control_faces = [] d[pp.STATE]["water_table"] = np.zeros(g.num_cells) print("Current time: ", np.round(time, decimals=1)) # Control Loop