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 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))
flow = Flow("fix_p0.txt", "FixMassFlux") flow.load() #%% Plot bifurcation curves fig_gbifur = plt.figure(figsize=(6 * 1., 3.6 * 1.)) ax = fig_gbifur.gca() ax.plot(flow.Q[0:], flow.height[0:], 'b-o', markersize=4, label='fixed $p_0$') ax.grid(linestyle="--") ax.legend() plt.xlabel(r'$Q$') plt.ylabel('Wave height') plt.tight_layout() #%% Plot profiles n_point = flow.n_q - 1 flow.extract(flow.n_h - 1) flow.compute_derivative() fig_profile1 = plt.figure(figsize=(6 * 1., 3.6 * 1.)) ax = fig_profile1.gca() ax.plot(flow.q2, flow.y2[:, 0:-1:40], 'b-', linewidth=1.2) ax.plot(flow.q2, flow.y2[:, 0], '-m', linewidth=1.2) ax.grid(linestyle="--") plt.xlabel('$x$') plt.ylabel('$y$') plt.tight_layout() #%% Plot velocity flow.compute_velocity(flow.n_h - 1) fig_u = plt.figure(figsize=(6 * 1., 3.6 * 1.)) ax = fig_u.gca() ax.plot(flow.p, flow.c_u[0, :].T, 'b-')
class Scheme(object): # ------------------------------------------------------------------------------# def __init__(self, gb): self.gb = gb # -- flow -- # self.discr_flow = Flow(gb) shape = self.discr_flow.shape() self.flux_pressure = np.zeros(shape) # -- temperature -- # self.discr_temperature = Heat(gb) shape = self.discr_temperature.shape() self.temperature = np.zeros(shape) self.temperature_old = np.zeros(shape) # -- solute and precipitate -- # self.discr_solute_advection_diffusion = Transport(gb) self.discr_solute_precipitate_reaction = Reaction(gb) shape = self.discr_solute_advection_diffusion.shape() self.solute = np.zeros(shape) self.precipitate = np.zeros(shape) self.solute_old = np.zeros(shape) self.precipitate_old = np.zeros(shape) # -- porosity -- # self.discr_porosity = Porosity(gb) shape = self.discr_porosity.shape() self.porosity = np.zeros(shape) self.porosity_old = np.zeros(shape) self.porosity_star = np.zeros(shape) # -- aperture -- # self.discr_aperture = Aperture(gb) shape = self.discr_aperture.shape() self.aperture = np.zeros(shape) self.aperture_old = np.zeros(shape) self.aperture_star = np.zeros(shape) # -- composite variables -- # self.porosity_aperture_times_solute = np.zeros(shape) self.porosity_aperture_times_precipitate = np.zeros(shape) # ------------------------------------------------------------------------------# def compute_flow(self): A, b = self.discr_flow.matrix_rhs() return sps.linalg.spsolve(A, b) # ------------------------------------------------------------------------------# def compute_temperature(self, porosity_star, aperture_star): # compute the matrices A, M, b = self.discr_temperature.matrix_rhs() # compute the effective thermal capacity, keeping in mind that the fracture # contains only water rc_w = self.discr_temperature.data["rc_w"] rc_s = self.discr_temperature.data["rc_s"] c_star = rc_w * (porosity_star + aperture_star) + rc_s * (1 - porosity_star) c_old = rc_w * (self.porosity_old + self.aperture_old) + rc_s * (1 - self.porosity_old) # the mass term which considers the contribution from the effective thermal capacity M_star = M * sps.diags(c_star, 0) M_old = M * sps.diags(c_old, 0) # compute the new temperature return sps.linalg.spsolve(M_star + A, M_old * self.temperature_old + b) # ------------------------------------------------------------------------------# def compute_solute_precipitate_advection_diffusion(self, porosity_star, aperture_star): # compute the matrices A, M, b = self.discr_solute_advection_diffusion.matrix_rhs() # the mass term which considers both the porosity and aperture contribution M_star = M * sps.diags(porosity_star + aperture_star, 0) M_old = M * sps.diags(self.porosity_old + self.aperture_old, 0) # compute the new solute return sps.linalg.spsolve(M_star + A, M_old * self.solute_old + b) # ------------------------------------------------------------------------------# def compute_solute_precipitate_rection(self, solute_half, precipitate_half): # the dof associated to the porous media and fractures, are the first dof = self.gb.num_cells() # temporary solution vectors solute = np.zeros(self.discr_solute_advection_diffusion.shape()) precipitate = np.zeros(self.discr_solute_advection_diffusion.shape()) # compute the new solute and precipitate solute[:dof], precipitate[:dof] = self.discr_solute_precipitate_reaction.step( solute_half[:dof], precipitate_half[:dof], self.temperature[:dof]) return solute, precipitate # ------------------------------------------------------------------------------# def set_old_variables(self): self.temperature_old = self.temperature.copy() self.solute_old = self.solute.copy() self.precipitate_old = self.precipitate.copy() self.porosity_old = self.porosity.copy() self.aperture_old = self.aperture.copy() # ------------------------------------------------------------------------------# def set_data(self, param): # set the initial condition assembler = self.discr_solute_advection_diffusion.assembler dof = np.cumsum(np.append(0, np.asarray(assembler.full_dof))) for (g, _), bi in assembler.block_dof.items(): #g = pair[0] if isinstance(g, pp.Grid): dof_loc = slice(dof[bi], dof[bi+1]) data = param["temperature"]["initial"] self.temperature[dof_loc] = data(g, param, param["tol"]) data = param["solute_advection_diffusion"]["initial_solute"] self.solute[dof_loc] = data(g, param, param["tol"]) data = param["solute_advection_diffusion"]["initial_precipitate"] self.precipitate[dof_loc] = data(g, param, param["tol"]) data = param["porosity"]["initial"] self.porosity[dof_loc] = data(g, param, param["tol"]) data = param["aperture"]["initial"] self.aperture[dof_loc] = data(g, param, param["tol"]) # set the old variables self.set_old_variables() # save the initial porosity and aperture self.discr_porosity.extract(self.porosity, "porosity_initial") self.discr_aperture.extract(self.aperture, "aperture_initial") # extract the initialized variables, useful for setting the data self.extract() # set now the data for each scheme self.discr_flow.set_data(param["flow"], param["time"]) self.discr_temperature.set_data(param["temperature"], param["time"]) self.discr_solute_advection_diffusion.set_data(param["solute_advection_diffusion"], param["time"]) self.discr_solute_precipitate_reaction.set_data(param["solute_precipitate_reaction"], param["time"]) self.discr_porosity.set_data(param["porosity"]) self.discr_aperture.set_data(param["aperture"]) # ------------------------------------------------------------------------------# def extract(self): self.discr_flow.extract(self.flux_pressure) self.discr_temperature.extract(self.temperature, "temperature") self.discr_solute_advection_diffusion.extract(self.solute, "solute") self.discr_solute_advection_diffusion.extract(self.precipitate, "precipitate") self.discr_porosity.extract(self.porosity, "porosity") self.discr_porosity.extract(self.porosity_old, "porosity_old") self.discr_aperture.extract(self.aperture, "aperture") self.discr_aperture.extract(self.aperture_old, "aperture_old") self.discr_solute_advection_diffusion.extract(self.porosity_aperture_times_solute, "porosity_aperture_times_solute") self.discr_solute_advection_diffusion.extract(self.porosity_aperture_times_precipitate, "porosity_aperture_times_precipitate") # ------------------------------------------------------------------------------# def vars_to_save(self): name = ["solute", "precipitate", "porosity", "aperture", "temperature"] name += ["porosity_aperture_times_solute", "porosity_aperture_times_precipitate"] return name + [self.discr_flow.pressure, self.discr_flow.P0_flux] # ------------------------------------------------------------------------------# def one_step_splitting_scheme(self): # the dof associated to the porous media and fractures, are the first dof = slice(0, self.gb.num_cells()) # POINT 1) extrapolate the precipitate to get a better estimate of porosity precipitate_star = 2*self.precipitate - self.precipitate_old # POINT 2) compute the porosity and aperture star porosity_star = self.discr_porosity.step(self.porosity_old, precipitate_star, self.precipitate_old) self.discr_porosity.extract(porosity_star, "porosity_star") aperture_star = self.discr_aperture.step(self.aperture_old, precipitate_star, self.precipitate_old) self.discr_aperture.extract(aperture_star, "aperture_star") # -- DO THE FLOW PART -- # # POINT 3) update the data from the previous time step self.discr_flow.update_data() # POINT 4) solve the flow part self.flux_pressure = self.compute_flow() self.discr_flow.extract(self.flux_pressure) # -- DO THE HEAT PART -- # # POINT 5) set the flux and update the data from the previous time step self.discr_temperature.set_flux(self.discr_flow.flux, self.discr_flow.mortar) self.discr_temperature.update_data() # POINT 5) solve the temperature part self.temperature = self.compute_temperature(porosity_star, aperture_star) # -- DO THE TRANSPORT PART -- # # set the flux and update the data from the previous time step self.discr_solute_advection_diffusion.set_flux(self.discr_flow.flux, self.discr_flow.mortar) self.discr_solute_advection_diffusion.update_data() # POINT 6) solve the advection and diffusion part to get the intermediate solute solution solute_half = self.compute_solute_precipitate_advection_diffusion(porosity_star, aperture_star) # POINT 7) Since in the advection-diffusion step we have accounted for porosity changes using # phi_star, the new solute concentration accounts for the change in pore volume, thus, the # precipitate needs to be updated accordingly factor = np.zeros(self.porosity_old.size) factor[dof] = (self.porosity_old[dof] + self.aperture_old[dof]) / (porosity_star[dof] + aperture_star[dof]) precipitate_half = self.precipitate_old * factor # POINT 8) solve the reaction part solute_star_star, precipitate_star_star = self.compute_solute_precipitate_rection(solute_half, precipitate_half) # -- DO THE POROSITY PART -- # # POINT 9) solve the porosity and aperture part with the true concentration of precipitate self.porosity = self.discr_porosity.step(self.porosity, precipitate_star_star, self.precipitate_old) self.aperture = self.discr_aperture.step(self.aperture, precipitate_star_star, self.precipitate_old) # POINT 10) finally, we correct the concentrations to account for the difference between the extrapolated # and "true" new porosity to ensure mass conservation factor = np.zeros(self.porosity_old.size) factor[dof] = (porosity_star[dof] + aperture_star[dof]) / (self.porosity[dof] + self.aperture[dof]) self.solute = solute_star_star * factor self.precipitate = precipitate_star_star * factor # set the old variables self.set_old_variables() # compute composite variables factor = self.porosity + self.aperture self.porosity_aperture_times_solute = factor * self.solute self.porosity_aperture_times_precipitate = factor * self.precipitate # extract all the variables, useful for exporting self.extract()
class Scheme(object): # ------------------------------------------------------------------------------# def __init__(self, gb): self.gb = gb # -- flow -- # self.discr_flow = Flow(gb) shape = self.discr_flow.shape() self.flux_pressure = np.zeros(shape) # -- temperature -- # self.discr_temperature = Heat(gb) # -- solute and precipitate -- # self.discr_solute_advection_diffusion = Transport(gb) self.discr_solute_precipitate_reaction = Reaction(gb) # -- porosity -- # self.discr_porosity = Porosity(gb) # -- fracture aperture -- # self.discr_fracture_aperture = FractureAperture( gb, "fracture_aperture") # -- layer porosity and aperture -- # self.discr_layer_porosity = Porosity(gb, "layer_porosity") self.discr_layer_aperture = LayerAperture(gb, "layer_aperture") # the actual time of the simulation self.time = 0 # ------------------------------------------------------------------------------# def compute_precipitate_temperature_star(self): for g, d in self.gb: d[pp.STATE]["precipitate_star"] = 2 * d[ pp.STATE]["precipitate"] - d[pp.STATE]["precipitate_old"] d[pp.STATE]["temperature_star"] = 2 * d[ pp.STATE]["temperature"] - d[pp.STATE]["temperature_old"] # ------------------------------------------------------------------------------# def compute_porosity_aperture_star(self): dim_max = self.gb.dim_max() # only the rock matrix has the variable porosity for g in self.gb.grids_of_dimension(dim_max): d = self.gb.node_props(g) d[pp.STATE]["porosity_star"] = self.discr_porosity.step( d[pp.STATE]["porosity_old"], d[pp.STATE]["precipitate_star"], d[pp.STATE]["precipitate_old"]) # only the fracture and the layer have the variable aperture for g in self.gb.grids_of_dimension(dim_max - 1): d = self.gb.node_props(g) if "fracture" in g.name: d[pp.STATE][ "fracture_aperture_star"] = self.discr_fracture_aperture.step( d[pp.STATE]["fracture_aperture_old"], d[pp.STATE]["precipitate_star"], d[pp.STATE]["precipitate_old"]) if "layer" in g.name: d[pp.STATE]["layer_aperture_star"] = d[ pp.STATE]["layer_aperture_old"] d[pp.STATE][ "layer_porosity_star"] = self.discr_layer_porosity.step( d[pp.STATE]["layer_porosity_old"], d[pp.STATE]["precipitate_star"], d[pp.STATE]["precipitate_old"]) # ------------------------------------------------------------------------------# def compute_flow(self): A, b = self.discr_flow.matrix_rhs() x = sps.linalg.spsolve(A, b) if not np.all(np.isfinite(x)): raise ValueError self.discr_flow.extract(x) # ------------------------------------------------------------------------------# def compute_temperature(self): A, b = self.discr_temperature.matrix_rhs() x = sps.linalg.spsolve(A, b) if not np.all(np.isfinite(x)): raise ValueError self.discr_temperature.extract(x, "temperature") # ------------------------------------------------------------------------------# def compute_solute_precipitate_advection_diffusion(self): A, b = self.discr_solute_advection_diffusion.matrix_rhs() x = sps.linalg.spsolve(A, b) if not np.all(np.isfinite(x)): raise ValueError self.discr_solute_advection_diffusion.extract(x, "solute_half") # ------------------------------------------------------------------------------# def correct_precipitate_with_star(self): for g, d in self.gb: vol_star = d[pp.STATE]["porosity_star"] + d[pp.STATE]["fracture_aperture_star"] +\ d[pp.STATE]["layer_porosity_star"] vol_old = d[pp.STATE]["porosity_old"] + d[pp.STATE]["fracture_aperture_old"] + \ d[pp.STATE]["layer_porosity_old"] d[pp.STATE]["precipitate_half"] = d[ pp.STATE]["precipitate_old"] * (vol_old / vol_star) # ------------------------------------------------------------------------------# def compute_solute_precipitate_rection(self): for g, d in self.gb: d[pp.STATE]["solute_star_star"], d[pp.STATE]["precipitate_star_star"] = \ self.discr_solute_precipitate_reaction.step(d[pp.STATE]["solute_half"], d[pp.STATE]["precipitate_half"], d[pp.STATE]["temperature"]) # ------------------------------------------------------------------------------# def compute_porosity_aperture(self): dim_max = self.gb.dim_max() # only the rock matrix has the variable porosity for g in self.gb.grids_of_dimension(dim_max): d = self.gb.node_props(g) d[pp.STATE]["porosity"] = self.discr_porosity.step( d[pp.STATE]["porosity"], d[pp.STATE]["precipitate_star_star"], d[pp.STATE]["precipitate_old"]) # only the fracture and the layer have the variable aperture for g in self.gb.grids_of_dimension(dim_max - 1): d = self.gb.node_props(g) if "fracture" in g.name: d[pp.STATE][ "fracture_aperture"] = self.discr_fracture_aperture.step( d[pp.STATE]["fracture_aperture"], d[pp.STATE]["precipitate_star_star"], d[pp.STATE]["precipitate_old"]) if "layer" in g.name: flux_fracture = np.zeros(g.num_cells) solute_fracture = np.zeros(g.num_cells) porosity = np.zeros(g.num_cells) for e, d_e in self.gb.edges_of_node(g): g_m = d_e["mortar_grid"] g_l, g_h = self.gb.nodes_of_edge(e) if g_h.dim < self.gb.dim_max(): # save the flux from the fracture flux_fracture = -g_m.master_to_mortar_avg().T * d_e[ pp.STATE][self.discr_flow.mortar] / g.cell_volumes d_fracture = self.gb.node_props(g_l if "fracture" in g_l.name else g_h) solute_fracture = g_m.slave_to_mortar_avg( ) * d_fracture[pp.STATE]["solute"] else: cell_cell = g_m.mortar_to_master_int( ).T * g_h.cell_faces # the cells are already ordered according to the layer ordering of the cells given by the mortar # map interface_cells = cell_cell.indices d_h = self.gb.node_props(g_h) porosity = d_h[pp.STATE]["porosity"][interface_cells] temperature = d_h[ pp.STATE]["temperature"][interface_cells] lmbda = self.discr_solute_precipitate_reaction.data[ "lambda"](temperature) d[pp.STATE]["layer_aperture"] = self.discr_layer_aperture.step( flux_fracture, porosity, lmbda, self.time, solute_fracture) d[pp.STATE]["layer_porosity"] = self.discr_layer_porosity.step( d[pp.STATE]["layer_porosity"], d[pp.STATE]["precipitate_star_star"], d[pp.STATE]["precipitate_old"]) # ------------------------------------------------------------------------------# def correct_precipitate_solute(self): for g, d in self.gb: vol_star = d[pp.STATE]["porosity_star"] + d[pp.STATE]["fracture_aperture_star"] +\ d[pp.STATE]["layer_porosity_star"] vol = d[pp.STATE]["porosity"] + d[pp.STATE]["fracture_aperture"] + \ d[pp.STATE]["layer_porosity"] d[pp.STATE]["solute"] = d[pp.STATE]["solute_star_star"] * ( vol_star / vol) d[pp.STATE]["precipitate"] = d[ pp.STATE]["precipitate_star_star"] * (vol_star / vol) # ------------------------------------------------------------------------------# def set_old_variables(self): for g, d in self.gb: d[pp.STATE]["temperature_old"] = d[pp.STATE]["temperature"].copy() d[pp.STATE]["solute_old"] = d[pp.STATE]["solute"].copy() d[pp.STATE]["precipitate_old"] = d[pp.STATE]["precipitate"].copy() d[pp.STATE]["porosity_old"] = d[pp.STATE]["porosity"].copy() d[pp.STATE]["fracture_aperture_old"] = d[ pp.STATE]["fracture_aperture"].copy() d[pp.STATE]["layer_aperture_old"] = d[ pp.STATE]["layer_aperture"].copy() d[pp.STATE]["layer_porosity_old"] = d[ pp.STATE]["layer_porosity"].copy() # ------------------------------------------------------------------------------# def set_data(self, param): for g, d in self.gb: data = param["temperature"]["initial"] d[pp.STATE]["temperature"] = data(g, param, param["tol"]) d[pp.STATE]["temperature_star"] = d[pp.STATE]["temperature"].copy() data = param["solute_advection_diffusion"]["initial_solute"] d[pp.STATE]["solute"] = data(g, param, param["tol"]) data = param["solute_advection_diffusion"]["initial_precipitate"] d[pp.STATE]["precipitate"] = data(g, param, param["tol"]) d[pp.STATE]["precipitate_star"] = d[pp.STATE]["precipitate"].copy() data = param["porosity"]["initial"] d[pp.STATE]["porosity_initial"] = data(g, param, param["tol"]) d[pp.STATE]["porosity_star"] = d[ pp.STATE]["porosity_initial"].copy() d[pp.STATE]["porosity"] = d[pp.STATE]["porosity_initial"].copy() data = param["fracture_aperture"]["initial"] d[pp.STATE]["fracture_aperture_initial"] = data( g, param, param["tol"]) d[pp.STATE]["fracture_aperture_star"] = d[ pp.STATE]["fracture_aperture_initial"].copy() d[pp.STATE]["fracture_aperture"] = d[ pp.STATE]["fracture_aperture_initial"].copy() data = param["layer_aperture"]["initial"] d[pp.STATE]["layer_aperture_initial"] = data( g, param, param["tol"]) d[pp.STATE]["layer_aperture_star"] = d[ pp.STATE]["layer_aperture_initial"].copy() d[pp.STATE]["layer_aperture"] = d[ pp.STATE]["layer_aperture_initial"].copy() data = param["layer_porosity"]["initial"] d[pp.STATE]["layer_porosity_initial"] = data( g, param, param["tol"]) d[pp.STATE]["layer_porosity_star"] = d[ pp.STATE]["layer_porosity_initial"].copy() d[pp.STATE]["layer_porosity"] = d[ pp.STATE]["layer_porosity_initial"].copy() vol = d[pp.STATE]["porosity"] + d[pp.STATE]["fracture_aperture"] +\ d[pp.STATE]["layer_aperture"] * d[pp.STATE]["layer_porosity"] d[pp.STATE]["porosity_aperture_times_solute"] = vol * d[ pp.STATE]["solute"] d[pp.STATE]["porosity_aperture_times_precipitate"] = vol * d[ pp.STATE]["precipitate"] d[pp.STATE]["pressure"] = np.zeros(g.num_cells) d[pp.STATE]["P0_darcy_flux"] = np.zeros((3, g.num_cells)) for e, d in self.gb.edges(): d[pp.STATE][self.discr_flow.mortar] = np.zeros( d["mortar_grid"].num_cells) # set the old variables self.set_old_variables() # extract the initialized variables, useful for setting the data ###self.extract() # set now the data for each scheme self.discr_flow.set_data(param["flow"], param["time"]) self.discr_temperature.set_data(param["temperature"], param["time"]) self.discr_solute_advection_diffusion.set_data( param["solute_advection_diffusion"], param["time"]) self.discr_solute_precipitate_reaction.set_data( param["solute_precipitate_reaction"], param["time"]) self.discr_porosity.set_data(param["porosity"]) self.discr_fracture_aperture.set_data(param["fracture_aperture"]) self.discr_layer_aperture.set_data(param["layer_aperture"], param["time"]) self.discr_layer_porosity.set_data(param["layer_porosity"]) # ------------------------------------------------------------------------------# def compute_composite_variables(self): for g, d in self.gb: vol = d[pp.STATE]["porosity"] + d[pp.STATE]["fracture_aperture"] +\ d[pp.STATE]["layer_aperture"] * d[pp.STATE]["layer_porosity"] d[pp.STATE]["porosity_aperture_times_solute"] = vol * d[ pp.STATE]["solute"] d[pp.STATE]["porosity_aperture_times_precipitate"] = vol * d[ pp.STATE]["precipitate"] # ------------------------------------------------------------------------------# def vars_to_save(self): name = [ "solute", "precipitate", "porosity", "fracture_aperture", "layer_aperture", "layer_porosity", "temperature" ] name += [ "porosity_aperture_times_solute", "porosity_aperture_times_precipitate", "fracture", "layer" ] return name + [self.discr_flow.pressure, self.discr_flow.P0_flux] # ------------------------------------------------------------------------------# def one_step_splitting_scheme(self, time): # save the time of the current step self.time = time # POINT 1) extrapolate the precipitate to get a better estimate of porosity self.compute_precipitate_temperature_star() # POINT 2) compute the porosity and aperture star for both the fracture and layer self.compute_porosity_aperture_star() # -- DO THE FLOW PART -- # # POINT 3) update the data from the previous time step self.discr_flow.update_data() # POINT 4) solve the flow part self.compute_flow() # -- DO THE HEAT PART -- # # set the flux and update the data from the previous time step self.discr_temperature.set_flux(self.discr_flow.flux, self.discr_flow.mortar) self.discr_temperature.update_data() # POINT 5) solve the temperature part self.compute_temperature() # -- DO THE TRANSPORT PART -- # # set the flux and update the data from the previous time step self.discr_solute_advection_diffusion.set_flux(self.discr_flow.flux, self.discr_flow.mortar) self.discr_solute_advection_diffusion.update_data() # POINT 6) solve the advection and diffusion part to get the intermediate solute solution self.compute_solute_precipitate_advection_diffusion() # POINT 7) Since in the advection-diffusion step we have accounted for porosity changes using # phi_star and aperture_star, the new solute concentration accounts for the change in pore volume, thus, the # precipitate needs to be updated accordingly self.correct_precipitate_with_star() # POINT 8) solve the reaction part self.compute_solute_precipitate_rection() # -- DO THE POROSITY PART -- # # POINT 9) solve the porosity and aperture (fracture and layer) part with the true concentration of precipitate self.compute_porosity_aperture() # POINT 10) finally, we correct the concentrations to account for the difference between the extrapolated # and "true" new porosity to ensure mass conservation self.correct_precipitate_solute() # set the old variables self.set_old_variables() # compute composite variables self.compute_composite_variables()
def main(name, gb, coarse=False): tol = 1e-6 case = "case1" if coarse: partition = pp.coarsening.create_aggregations(gb, weight=0.5) partition = pp.coarsening.reorder_partition(partition) pp.coarsening.generate_coarse_grid(gb, partition) # the flow problem param = { "tol": tol, "k": 1, "aperture": 1e-4, } set_flag(gb) save_vars = ["pressure", "P0_darcy_flux"] folder = "solution_" + name if not os.path.exists(folder): os.makedirs(folder) # ---- flow ---- # flow = Flow(gb, folder) flow.set_data(param, bc_flag, source) # create the matrix for the Darcy problem A, b = flow.matrix_rhs() # solve the problem x = sps.linalg.spsolve(A, b) flow.extract(x) # export the matrix mmwrite(folder + "/matrix.mtx", A) # export extra data with open(folder + "/data.txt", "w") as f: f.write("num_faces " + str(gb.num_faces()) + "\n") f.write("num_cells " + str(gb.num_cells()) + "\n") f.write("diam " + str(gb.diameter()) + "\n") # save the file with the stabilization # export the stabilization terms only for the matrix for g, d in gb: if g.dim == 2: norm_A, norm_S, ratio = np.loadtxt(folder + "/stabilization.csv", delimiter=",").T d[pp.STATE]["norm_A"] = norm_A[:g.num_cells] d[pp.STATE]["norm_S"] = norm_S[:g.num_cells] d[pp.STATE]["ratio"] = ratio[:g.num_cells] else: d[pp.STATE]["norm_A"] = np.zeros(g.num_cells) d[pp.STATE]["norm_S"] = np.zeros(g.num_cells) d[pp.STATE]["ratio"] = np.zeros(g.num_cells) d[pp.STATE]["cell_volumes"] = g.cell_volumes save_vars += ["norm_A", "norm_S", "ratio", "cell_volumes"] # output the solution save = pp.Exporter(gb, case, folder=folder, binary=False) save.write_vtk(save_vars) if coarse: gb = decoarsify(gb, partition, save_vars) gb = simplexify(gb, save_vars) # output the solution save = pp.Exporter(gb, case, folder=folder + "_simplexify", binary=False) save.write_vtk(save_vars)