Ejemplo n.º 1
0
def corner_submesh(mesh):
    """create a submesh extending from the origin to the midpoint"""
    #
    # Because SubMesh only works sequentially, build the corner
    # submesh in the rank 0 process, then distribute it.
    #
    submesh = None              # needs to be initialized in all ranks
    # if MPI.COMM_WORLD.rank == 0:
    if True:
        logPERIODIC('constructing submesh')
        cf = MeshFunction("size_t", mesh, mesh.geometric_dimension())
        logPERIODIC('CornerDomain')
        corner = CornerDomain(mesh)
        logPERIODIC('cf.set_all(0)')
        cf.set_all(0)
        logPERIODIC('corner.mark(cf, 1)')
        corner.mark(cf, 1)
        logPERIODIC('calling SubMesh')
        logPERIODIC('mesh.mpi_comm().size', mesh.mpi_comm().size)
        submesh = SubMesh(mesh, cf, 1)
        logPERIODIC('submesh', submesh)
        logPERIODIC('submesh.mpi_comm().size', submesh.mpi_comm().size)
    logPERIODIC('distributing submesh')
    dsubmesh = distribute_mesh(submesh)
    return(dsubmesh)
Ejemplo n.º 2
0
    def __init__(self, mesh, N=0):

        # Define mesh parameters
        self.mesh = mesh
        self.cell_size = CellDiameter(mesh)
        self.normal_vector = FacetNormal(mesh)

        # Define measures
        inner_boundary = Inner_boundary()
        sub_domains = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
        sub_domains.set_all(0)
        inner_boundary.mark(sub_domains, 1)
        self.dx = Measure("dx", domain=mesh)
        self.ds = Measure("ds", domain=mesh, subdomain_data=sub_domains)

        # Define function spaces
        finite_element = FiniteElement("Lagrange", mesh.ufl_cell(), 1)
        self.function_space = FunctionSpace(mesh,
                                            finite_element * finite_element)
        self.function_space_split = [
            self.function_space.sub(0).collapse(),
            self.function_space.sub(1).collapse(),
        ]
Ejemplo n.º 3
0
a, L = lhs(F), rhs(F)

# Time-stepping
u_np1 = Function(V)
u_np1.rename("Temperature", "")
t = 0

# reference solution at t=0
u_ref = interpolate(u_D, V)
u_ref.rename("reference", " ")

# mark mesh w.r.t ranks
mesh_rank = MeshFunction("size_t", mesh, mesh.topology().dim())
if problem is ProblemType.NEUMANN:
    mesh_rank.set_all(MPI.rank(MPI.comm_world) + 4)
else:
    mesh_rank.set_all(MPI.rank(MPI.comm_world) + 0)
mesh_rank.rename("myRank", "")

# Generating output files
temperature_out = File("out/%s.pvd" % precice.get_participant_name())
ref_out = File("out/ref%s.pvd" % precice.get_participant_name())
error_out = File("out/error%s.pvd" % precice.get_participant_name())
ranks = File("out/ranks%s.pvd" % precice.get_participant_name())

# output solution and reference solution at t=0, n=0
n = 0
print('output u^%d and u_ref^%d' % (n, n))
temperature_out << u_n
ref_out << u_ref
class HyperelasticBeam:
    """Class implementing the solution to a hyperelastic problem on a deformable beam.
    """
    def __init__(
            self, eval_times, n=10, lx=1., ly=.1, lz=.1,
            f=(0.0, 0.0, -50.), time=1., timestep=.1,
            tol=1e-5, max_iter=30, rel_tol=1e-10, param_remapper=None):
        """Parameters
        ----------
        eval_times: numpy.ndarray
            Times at which evaluation (interpolation) of the solution is required.
        n: int, default 10
            Dimension of the grid along the smallest side of the beam
            (the grid size along all other dimensions will be scaled proportionally.
        lx: float, default 1.
            Length of the beam along the x axis.
        ly: float, default .1
            Length of the beam along the y axis.
        lz: float, default .1
            Length of the beam along the z axis.
        f: tuple or numpy.ndarray, default (0.0, 0.0, -50.)
            Force per unit volume acting on the beam.
        time: float, default 1.
            Final time of the simulation.
        timestep: float, default 1.
            Time discretization step to solve the problem.
        tol: float, default 1e-5
            Tolerance parameter to ensure the last time step is included in the solution.
        max_iter: int, default 30
            Maximum iterations for the SNES solver.
        rel_tol: int,
            Relative tolerance for the convergence of the SNES solver.
        param_remapper: object, default None
            Either None (no remapping of the parameters), or a function remapping
            the parameter of the problem (Young's modulus) to values suitable for the
            definition of the solution.
        """
        # solver parameters
        self.solver = CountIt(solve)
        self.solver_parameters = {
            'nonlinear_solver': 'snes',
            'snes_solver': {
                'linear_solver': 'lu',
                'line_search': 'basic',
                'maximum_iterations': max_iter,
                'relative_tolerance': rel_tol,
                'report': False,
                'error_on_nonconvergence': False}}
        self.param_remapper = param_remapper

        # mesh creation
        self.n = n
        self.lx = lx
        self.ly = ly
        self.lz = lz

        min_len = min(lx, ly, lz)
        mesh_dims = (int(n * lx / min_len), int(n * ly / min_len), int(n * lz / min_len))
        self.mesh = BoxMesh(Point(0, 0, 0), Point(lx, ly, lz), *mesh_dims)
        self.V = VectorFunctionSpace(self.mesh, 'Lagrange', 1)

        # boundary conditions
        self.left = CompiledSubDomain('near(x[0], side) && on_boundary', side=0.0)
        self.right = CompiledSubDomain('near(x[0], side) && on_boundary', side=lx)
        self.top = CompiledSubDomain('near(x[2], side) && on_boundary', side=lz)

        self.boundaries = MeshFunction('size_t', self.mesh, self.mesh.topology().dim() - 1, 0)
        self.boundaries.set_all(0)
        self.left.mark(self.boundaries, 1)
        self.right.mark(self.boundaries, 2)
        self.top.mark(self.boundaries, 3)

        self.bcs1 = DirichletBC(self.V, Constant([0.0, 0.0, 0.0]), self.boundaries, 1)
        self.bcs2 = DirichletBC(self.V, Constant([0.0, 0.0, 0.0]), self.boundaries, 2)
        self.bcs = [self.bcs1, self.bcs2]

        # surface force
        self.f = Constant(f)
        self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundaries)

        # evaluation times
        self.eval_times = eval_times
        self.dt = timestep
        self.T = time + tol
        self.times = np.arange(self.dt, self.T, self.dt)
        self.time = Expression('t', t=self.dt, degree=0)

    def _solve(self, z, x=None):
        # problem variables
        du = TrialFunction(self.V)                          # incremental displacement
        v = TestFunction(self.V)                            # test function
        u = Function(self.V)                                # displacement from previous iteration

        # kinematics
        ii = Identity(3)                                    # identity tensor dimension 3
        f = ii + grad(u)                                    # deformation gradient
        c = f.T * f                                         # right Cauchy-Green tensor

        # invariants of deformation tensors
        ic = tr(c)
        j = det(f)

        # elasticity parameters
        if type(z) in [list, np.ndarray]:
            param = self.param_remapper(z[0]) if self.param_remapper is not None else z[0]
        else:
            param = self.param_remapper(z) if self.param_remapper is not None else z

        e_var = variable(Constant(param))                   # Young's modulus
        nu = Constant(.3)                                   # Shear modulus (Lamè's second parameter)
        mu, lmbda = e_var / (2 * (1 + nu)), e_var * nu / ((1 + nu) * (1 - 2 * nu))

        # strain energy density, total potential energy
        psi = (mu / 2) * (ic - 3) - mu * ln(j) + (lmbda / 2) * (ln(j)) ** 2
        pi = psi * dx - self.time * dot(self.f, u) * self.ds(3)

        ff = derivative(pi, u, v)                           # compute first variation of pi
        jj = derivative(ff, u, du)                          # compute jacobian of f

        # solving
        if x is not None:
            numeric_evals = np.zeros(shape=(x.shape[1], len(self.times)))
            evals = np.zeros(shape=(x.shape[1], len(self.eval_times)))
        else:
            numeric_evals = None
            evals = None
        for it, t in enumerate(self.times):
            self.time.t = t
            self.solver(ff == 0, u, self.bcs, J=jj, bcs=self.bcs, solver_parameters=self.solver_parameters)
            if x is not None:
                numeric_evals[:, it] = np.log(np.array([-u(x_)[2] for x_ in x.T]).T)

        # time-interpolation
        if x is not None:
            for i in range(evals.shape[0]):
                evals[i, :] = np.interp(self.eval_times, self.times, numeric_evals[i, :])
        return (evals, u) if x is not None else u

    def __call__(self, z, x, reshape=True, retall=False):
        """Solves the equation for the provided parameter `z` and returns the evaluation
        for each one of the nodes in `x` and each evaluation time instant (passed at creation).

        Parameters
        ----------
        z : float or numpy.ndarray
            Value of the parameter (Young's modulus); if `z` is a vector, it has to have
            only one cell (the rest will not be considered).
        x : numpy.ndarray
            Matrix containing the coordinates of the points at which the solution has to be evaluated
            (one column per each point).
        reshape : bool, default True
            If `reshape` is set to True, then the results of the evaluations at different time points will
            be concatenated into a single vector, otherwise they are returned as a matrix whose columns
            correspond to values at different evaluation timesteps.
        retall : bool, default False
            If set to True, then also the solution at the last computed time step is returned.

        Returns
        -------
        numpy.ndarray or (numpy.ndarray, object)
            The vector or matrix of evaluation and additionally the FEniCS solution object
            (if `retall` was set to true).
        """
        evals, u = self._solve(z, x)
        evals = evals.flatten() if reshape else evals
        return (evals, u) if retall else evals

    def plot(self, z):
        """Solves the problem and then plots the beam with a colormap corresponding to the displacement.

        Parameters
        ----------
        z : float or numpy.ndarray
            Value of the parameter (Young's modulus); if `z` is a vector, it has to have
            only one cell (the rest will not be considered).
        """
        u = self._solve(z)
        vplot(u)
Ejemplo n.º 5
0
E = Constant(E)

if dim == 2:
    mesh = UnitSquareMesh(*N)  # generation of mesh
    inclusion = Inclusion_2d()
    point0 = "near(x[0], 0) && near(x[1], 0)"
elif dim == 3:
    mesh = UnitCubeMesh(*N)  # generation of mesh
    inclusion = Inclusion_3d()
    point0 = "near(x[0], 0) && near(x[1], 0) && near(x[2], 0)"

V = FunctionSpace(mesh, "CG", order, constrained_domain=PeriodicBoundary(dim))

# setting the elements that lies in inclusion and in matrix phase
domains = MeshFunction("size_t", mesh, dim)
domains.set_all(0)
inclusion.mark(domains, 1)
dx = Measure('dx', subdomain_data=domains)


def bilf(up, vp):  # bilinear form
    return inner(mat * up, vp) * dx(0) + inner(inc * up, vp) * dx(1)


bc0 = DirichletBC(V, Constant(0.), point0, method='pointwise')

# SOLVER
u = TrialFunction(V)
v = TestFunction(V)
uE = Function(V)
class DuctState:
    def __init__(self):

        self.mesh = None
        self.vid = None
        # parameters
        self.water_in_flux = 0.1

        self.boundary_fnc = lambda x: near(x[0], 1) and 0.3 < x[1]  < 0.6

    def load_mesh(self, fn):
        self.mesh = Mesh(fn)

    def load_video_mesh(self, nx = 30, ny = 30):
        self.mesh = UnitSquareMesh(nx, ny)

    def load_video(self, fn):
        self.video_filename = fn



    def compute_potential_flow(self):

        class Hole(SubDomain):
            def inside(s, x, on_boundary):
                return on_boundary and self.boundary_fnc(x)

        #class ActiveArea(SubDomain):
        #    def inside(self, x, on_boundary):
        #        return between(x[0], (0., 0.1)) and between(x[1], (0, 0.2))

        # Initialize sub-domain instances
        hole = Hole()
        # self.activate = ActiveArea()

        # Initialize mesh function for interior domains
        self.domains = MeshFunction("size_t", self.mesh, 2)
        self.domains.set_all(0)
        # self.activate.mark(self.domains, 1)

        # Initialize mesh function for boundary domains
        self.boundaries = MeshFunction("size_t", self.mesh, 1)
        self.boundaries.set_all(0)
        hole.mark(self.boundaries, 1)

        # Define new measures associated with the interior domains and
        # exterior boundaries

        self.dx = Measure('dx', domain=self.mesh, subdomain_data=self.domains)
        self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundaries)

        dx, ds = self.dx, self.ds

        # Define function space and basis functions
        V = FunctionSpace(self.mesh, "P", 1)
        self.V_vel = V
        phi = TrialFunction(V)  # potential
        v = TestFunction(V)

        # Define Dirichlet boundary conditions at top and bottom boundaries
        bcs = [fe.DirichletBC(V, 0.0, self.boundaries, 1)]

        # Define input data
        a0 = fe.Constant(1.0)
        a1 = fe.Constant(0.01)
        f_water = fe.Constant(self.water_in_flux)

        # Define the reaction equation

        # Define variational form
        F_pot = inner(a0 * grad(phi), grad(v)) * dx - f_water * v * dx

        # Separate left and right hand sides of equation
        a, L = fe.lhs(F_pot), fe.rhs(F_pot)

        # Solve problem
        phi = fe.Function(V)
        fe.solve(a == L, phi, bcs)

        # Evaluate integral of normal gradient over top boundary
        n = fe.FacetNormal(self.mesh)
        result_flux = fe.assemble(dot(grad(phi), n) * ds(1))

        # test conservation of water
        expected_flux = -self.water_in_flux * fe.assemble(1 * dx)

        print("relative error of conservation of water %f" % ((result_flux - expected_flux) / expected_flux))

        self.phi = phi
        self.flow = -grad(phi)

        # Save result
        output = fe.File("/tmp/potential.pvd")
        output << self.phi




    def F_diff_conv(self, u, v, n, flow, D, s, source):

        dx, ds = self.dx, self.ds
        return D * inner(grad(u), grad(v)) * dx \
               + s * inner(flow, grad(u)) * v * dx \
               - source * v * dx

    def compute_steady_state(self):

        names = {'Cl', 'Na', 'K'}

        P1 = FiniteElement('P', fe.triangle, 1)
        element = MixedElement([P1, P1, P1])
        V = FunctionSpace(self.mesh, element)
        self.V_conc = V

        (u_cl, u_na, u_k) = TrialFunction(V)
        (v_cl, v_na, v_k) = TestFunction(V)

        assert (self.flow is not None)

        n = fe.FacetNormal(self.mesh)

        # F = ( self.F_diff_conv(u_cl, v_cl, n, grad(self.phi), 1. ,1., 0.)
        #    + self.F_diff_conv(u_na, v_na, n, grad(self.phi), 1. ,1., 0.)
        #    + self.F_diff_conv(u_k , v_k , n, grad(self.phi), 1. ,1., 0.) )

        dx, ds = self.dx, self.ds
        flow = self.flow
        F = inner(grad(u_cl), grad(v_cl)) * dx \
            + inner(flow, grad(u_cl)) * v_cl * dx \
            + inner(grad(u_na), grad(v_na)) * dx \
            + inner(flow, grad(u_na)) * v_na * dx \
            + inner(grad(u_k), grad(v_k)) * dx \
            + inner(flow, grad(u_k)) * v_k * dx

        a, L = fe.lhs(F), fe.rhs(F)
        a_mat = fe.assemble(a)
        L_vec = fe.assemble(L)
        # solve
        u = Function(V)
        fe.solve(a_mat, u.vector(), L_vec)

        u_cl, u_na, u_k = u.split()

        output1 = fe.File('/tmp/steady_state_cl.pvd')
        output1 << u_cl
        output2 = fe.File('/tmp/steady_state_na.pvd')
        output2 << u_na
        output3 = fe.File('/tmp/steady_state_k.pvd')
        output3 << u_k

        self.u_cl = u_cl
        self.u_na = u_na
        self.u_k = u_k

    def compute_conv_diff_reac(self, initial_condition=None):

        names = {'Cl', 'Na', 'K'}

        dt = 0.1
        t = 0.
        t_end = 1.

        P1 = FiniteElement('P', fe.triangle, 3)
        element = MixedElement([P1, P1, P1])
        V = FunctionSpace(self.mesh, element)
        self.V_conc = V

        u_init = Function(V)
        (u_cl, u_na, u_k) = TrialFunction(V)
        (v_cl, v_na, v_k) = TestFunction(V)

        if initial_condition is None:
            initial_condition = Expression(("exp(-((x[0]-0.1)*(x[0]-0.1)+x[1]*x[1])/0.01)",
                                            "exp(-((x[0]-0.12)*(x[0]-0.12)+x[1]*x[1])/0.01)",
                                            "0."), element=element)

        u_init = fe.interpolate(initial_condition, V)
        u_init_cl = u_init[0]
        u_init_na = u_init[1]
        u_init_k = u_init[2]

        assert (self.flow is not None)

        n = fe.FacetNormal(self.mesh)

        dx, ds = self.dx, self.ds
        flow = 10 * self.flow
        f_in = fe.Constant(0.00)
        D = fe.Constant(0.01)
        k1 = fe.Constant(0.1)
        k_1 = fe.Constant(0.001)
        F = (
                (u_cl - u_init_cl) * v_cl * dx
                + dt * D * inner(grad(u_cl), grad(v_cl)) * dx
                + dt * inner(flow, grad(u_cl)) * v_cl * dx
                + (u_na - u_init_na) * v_na * dx
                + dt * D * inner(grad(u_na), grad(v_na)) * dx
                + dt * inner(flow, grad(u_na)) * v_na * dx
                + (u_k - u_init_k) * v_k * dx
                + dt * D * inner(grad(u_k), grad(v_k)) * dx
                + dt * inner(flow, grad(u_k)) * v_k * dx
                + f_in * v_cl * dx
                + f_in * v_na * dx
                + f_in * v_k * dx
                + dt * k1 * u_init_cl * u_init_na * v_cl * dx
                + dt * k1 * u_init_cl * u_init_na * v_na * dx
                - dt * k1 * u_init_cl * u_init_na * v_k * dx
                - dt * k_1 * u_init_k * v_cl * dx
                - dt * k_1 * u_init_k * v_na * dx
                + dt * k_1 * u_init_k * v_k * dx
        )

        self.F = F

        a, L = fe.lhs(F), fe.rhs(F)
        a_mat = fe.assemble(a)
        L_vec = fe.assemble(L)

        output1 = fe.File('/tmp/cl_dyn.pvd')
        output2 = fe.File('/tmp/na_dyn.pvd')
        output3 = fe.File('/tmp/k_dyn.pvd')
        output4 = fe.File('/tmp/all_dyn.pvd')
        # solve

        self.sol = []

        while t < t_end:
            t = t + dt
            print(t)

            u = Function(V)

            a_mat = fe.assemble(a)
            L_vec = fe.assemble(L)
            fe.solve(a_mat, u.vector(), L_vec)
            # NonlinearVariationalProblem(F,u)
            u_init.assign(u)

            u_cl, u_na, u_k = u.split()

            # u_init_cl.assign(u_cl)
            # u_init_na.assign(u_na)
            # u_init_k.assign(u_k)

            u_cl.rename("cl", "cl")
            u_na.rename("na", "na")
            u_k.rename("k", "k")
            output1 << u_cl, t
            output2 << u_na, t
            output3 << u_k, t
            self.sol.append((u_cl, u_na, u_k))



        self.u_cl = u_cl
        self.u_na = u_na
        self.u_k = u_k


    def compute_conv_diff_reac_video(self, initial_condition=None, video_ref=None, video_size=None):

        names = {'Cl', 'K'}


        P1 = FiniteElement('P', fe.triangle, 1)
        element = fe.MixedElement([P1, P1])
        V_single = FunctionSpace(self.mesh, P1)
        V = FunctionSpace(self.mesh, element)
        self.V_conc = V

        # load video
        video = VideoData(element=P1)
        video.load_video(self.video_filename)

        if( video_ref is not None and video_size is not None):
            video.set_reference_frame(video_ref, video_size)


        print(video)

        dt = 0.05
        t = 0.
        t_end = 20.

        u_init = Function(V)
        (u_cl, u_k) = TrialFunction(V)
        (v_cl, v_k) = TestFunction(V)

        #u_na = video


        if initial_condition is None:
            #initial_condition = Expression(("f*exp(-0.5*((x[0]-a)*(x[0]-a)+(x[1]-b)*(x[1]-b))/var)/(sqrt(2*pi)*var)",
            #                                "0."), a = 80, b= 55, var=10, f=10, pi=fe.pi, element=element)

            initial_condition = Expression(("f",
                                            "0."), a=80, b=55, var=0.1, f=0.1, pi=fe.pi, element=element)

        u_init = fe.interpolate(initial_condition, V)
        u_init_cl = u_init[0]

        u_init_k = u_init[1]
        u_init_na : VideoData= video

        assert (self.flow is not None)

        n = fe.FacetNormal(self.mesh)

        dx, ds = self.dx, self.ds
        flow = 5. * self.flow
        f_in = fe.Constant(0.00)
        f_in_cl = fe.Constant(-0.05)
        D = fe.Constant(0.1)

        C_na = fe.Constant(0.1)
        k1 = fe.Constant(0.2)
        k_1 = fe.Constant(0.00001)
        # explicit
        F = (
                (u_cl - u_init_cl) * v_cl * dx
                + dt * D * inner(grad(u_cl), grad(v_cl)) * dx
                + dt * inner(flow, grad(u_cl)) * v_cl * dx
                #+ (u_na - u_init_na) * v_na * dx
                #+ dt * D * inner(grad(u_na), grad(v_na)) * dx
                #+ dt * inner(flow, grad(u_na)) * v_na * dx
                + (u_k - u_init_k) * v_k * dx
                + dt * D * inner(grad(u_k), grad(v_k)) * dx
                + dt * inner(flow, grad(u_k)) * v_k * dx
                + f_in_cl * v_cl * dx
                #+ f_in * v_na * dx
                + f_in * v_k * dx
                + dt * k1 * u_init_cl * C_na * u_init_na * v_cl * dx
                #+ dt * k1 * u_init_cl * u_init_na * v_na * dx
                - dt * k1 * u_init_cl * C_na * u_init_na * v_k * dx
                - dt * k_1 * u_init_k * v_cl * dx
                #- dt * k_1 * u_init_k * v_na * dx
                + dt * k_1 * u_init_k * v_k * dx
        )
        # implicit
        F = (
                (u_cl - u_init_cl) * v_cl * dx
                + dt * D * inner(grad(u_cl), grad(v_cl)) * dx
                + dt * inner(flow, grad(u_cl)) * v_cl * dx
                # + (u_na - u_init_na) * v_na * dx
                # + dt * D * inner(grad(u_na), grad(v_na)) * dx
                # + dt * inner(flow, grad(u_na)) * v_na * dx
                + (u_k - u_init_k) * v_k * dx
                + dt * D * inner(grad(u_k), grad(v_k)) * dx
                + dt * inner(flow, grad(u_k)) * v_k * dx
                + f_in_cl * v_cl * dx
                # + f_in * v_na * dx
                + f_in * v_k * dx
                + dt * k1 * u_cl * C_na * u_init_na * v_cl * dx
                # + dt * k1 * u_init_cl * u_init_na * v_na * dx
                - dt * k1 * u_cl * C_na * u_init_na * v_k * dx
                - dt * k_1 * u_k * v_cl * dx
                # - dt * k_1 * u_init_k * v_na * dx
                + dt * k_1 * u_k * v_k * dx
        )

        self.F = F

        a, L = fe.lhs(F), fe.rhs(F)
        a_mat = fe.assemble(a)
        L_vec = fe.assemble(L)

        output1 = fe.File('/tmp/cl_dyn.pvd')
        output2 = fe.File('/tmp/na_dyn.pvd')
        output3 = fe.File('/tmp/k_dyn.pvd')
        output4 = fe.File('/tmp/all_dyn.pvd')
        # solve

        self.sol = []

        u_na = Function(V_single)
        u_na = fe.interpolate(u_init_na,V_single)

        na_inflow = 0

        t_plot = 0.5
        t_last_plot = 0

        while t < t_end:
            t = t + dt
            t_last_plot += dt
            print(t)

            u = Function(V)

            u_init_na.set_time(5*t)

            a_mat = fe.assemble(a)
            L_vec = fe.assemble(L)
            fe.solve(a_mat, u.vector(), L_vec)
            # NonlinearVariationalProblem(F,u)
            u_init.assign(u)

            u_cl, u_k = u.split()

            # u_init_cl.assign(u_cl)
            # u_init_na.assign(u_na)
            # u_init_k.assign(u_k)

            u_na = fe.interpolate(u_init_na, V_single)

            u_cl.rename("cl", "cl")
            u_na.rename("na", "na")
            u_k.rename("k", "k")
            output1 << u_cl, t
            output2 << u_na, t
            output3 << u_k, t
            self.sol.append((u_cl, u_na, u_k))

            print( fe.assemble(u_cl*self.dx))

            if t_last_plot > t_plot:
                t_last_plot = 0
                plt.figure(figsize=(8,16))
                plt.subplot(211)
                fe.plot(u_cl)
                plt.subplot(212)
                fe.plot(u_k)
                plt.show()



        self.u_cl = u_cl
        #self.u_na = u_na
        self.u_k = u_k


    def check_conservation_law(self, sol):
        dx = self.dx
        ds = self.ds
        total = []
        out = []
        for s in sol:
            cl = s[0]
            na = s[1]
            k = s[2]

            # integrate interior concentration
            mass_cl = fe.assemble(cl * dx)
            mass_na = fe.assemble(na * dx)
            mass_k = fe.assemble(k * dx)

            # integrate outflux
            n = fe.FacetNormal(self.mesh)
            flow = self.flow
            outflux_cl = fe.assemble(cl * inner(flow, n) * ds)
            outflux_na = fe.assemble(na * inner(flow, n) * ds)
            outflux_k = fe.assemble(k * inner(flow, n) * ds)

            total.append(mass_cl + mass_na + 2 * mass_k)
            out.append(outflux_cl + outflux_na + 2 * outflux_k)

        self.total_mass = total
        self.outflux = out