Esempio n. 1
0
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))
Esempio n. 2
0
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))
Esempio n. 3
0
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-')
Esempio n. 4
0
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()
Esempio n. 6
0
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)