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
Пример #2
0
 def set_boundaries(self):
     self.boundaries = fe.MeshFunction("size_t", self.mesh,
                                       self.mesh.topology().dim() - 1)
     self.boundaries.set_all(0)
     self.ds = fe.Measure("ds")(subdomain_data=self.boundaries)
     self.I = fe.Identity(self.mesh.topology().dim())
     self.normal = fe.FacetNormal(self.mesh)
Пример #3
0
    def compute_shape_derivative(self):
        """Computes the part of the shape derivative that comes from the regularization

		Returns
		-------
		ufl.form.Form
			The weak form of the shape derivative coming from the regularization

		"""

        V = self.form_handler.test_vector_field
        if self.has_regularization:

            n = fenics.FacetNormal(self.form_handler.mesh)
            I = fenics.Identity(self.form_handler.mesh.geometric_dimension())

            self.shape_form = Constant(self.mu_surface) * (
                self.current_surface - Constant(self.target_surface)) * t_div(
                    V, n) * self.ds

            if not self.measure_hole:
                self.shape_form += Constant(self.mu_volume) * (
                    self.current_volume -
                    Constant(self.target_volume)) * div(V) * self.dx
                self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_x - Constant(self.target_barycenter_list[0]))\
                         *(self.current_barycenter_x/self.current_volume*div(V) + 1/self.current_volume*(V[0] + self.spatial_coordinate[0]*div(V)))*self.dx \
                       + Constant(self.mu_barycenter)*(self.current_barycenter_y - Constant(self.target_barycenter_list[1]))\
                         *(self.current_barycenter_y/self.current_volume*div(V) + 1/self.current_volume*(V[1] + self.spatial_coordinate[1]*div(V)))*self.dx

                if self.form_handler.mesh.geometric_dimension() == 3:
                    self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_z - Constant(self.target_barycenter_list[2]))\
                           *(self.current_barycenter_z/self.current_volume*div(V) + 1/self.current_volume*(V[2] + self.spatial_coordinate[2]*div(V)))*self.dx

            else:
                self.shape_form -= Constant(self.mu_volume) * (
                    self.current_volume -
                    Constant(self.target_volume)) * div(V) * self.dx
                self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_x - Constant(self.target_barycenter_list[0]))\
                         *(self.current_barycenter_x/self.current_volume*div(V) - 1/self.current_volume*(V[0] + self.spatial_coordinate[0]*div(V)))*self.dx \
                       + Constant(self.mu_barycenter)*(self.current_barycenter_y - Constant(self.target_barycenter_list[1]))\
                         *(self.current_barycenter_y/self.current_volume*div(V) - 1/self.current_volume*(V[1] + self.spatial_coordinate[1]*div(V)))*self.dx

                if self.form_handler.mesh.geometric_dimension() == 3:
                    self.shape_form += Constant(self.mu_barycenter)*(self.current_barycenter_z - Constant(self.target_barycenter_list[2]))\
                             *(self.current_barycenter_z/self.current_volume*div(V) - 1/self.current_volume*(V[2] + self.spatial_coordinate[2]*div(V)))*self.dx

            return self.shape_form

        else:
            dim = self.form_handler.mesh.geometric_dimension()
            return inner(fenics.Constant([0] * dim), V) * self.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
Пример #5
0
    def bottom_wall_shear_integrand(self):

        nhat = fenics.FacetNormal(self.mesh)

        p, u, T, C = fenics.split(self.solution)

        bottom_wall_id = 2

        mesh_function = fenics.MeshFunction("size_t", self.mesh,
                                            self.mesh.topology().dim() - 1)

        self.bottom_wall.mark(mesh_function, bottom_wall_id)

        dot, grad = fenics.dot, fenics.grad

        ds = fenics.ds(domain=self.mesh,
                       subdomain_data=mesh_function,
                       subdomain_id=bottom_wall_id)

        return dot(grad(u[0]), nhat) * ds
Пример #6
0
    def _build_function_space(self):
        class Exterior(fa.SubDomain):
            def inside(self, x, on_boundary):
                return on_boundary

        self.exterior = Exterior()
        self.V = fa.FunctionSpace(self.mesh, 'P', 1)
        self.sub_domains = fa.MeshFunction("size_t", self.mesh,
                                           self.mesh.topology().dim() - 1)
        self.sub_domains.set_all(0)
        self.normal = fa.FacetNormal(self.mesh)
        self.ds = fa.Measure("ds")(subdomain_data=self.sub_domains)

        # self.source = fa.Expression(("cos(pi*x[0])*cos(pi*x[1])"),  degree=3)
        self.source = fa.Expression(("x[0]*x[0] + x[1]*x[1]"), degree=3)
        self.source = fa.interpolate(self.source, self.V)
        # self.source = fa.Constant(1.)

        self.bcs = []
        boundary_fn = fa.Constant(0.)
        boundary_bc = fa.DirichletBC(self.V, boundary_fn, self.exterior)
        self.bcs = self.bcs + [boundary_bc]
Пример #7
0
import fenics as fn
fn.parameters["form_compiler"]["representation"] = "uflacs"
fn.parameters["form_compiler"]["cpp_optimize"] = True

# Create mesh and define function space
size = width, height = 1.0, 0.75

mesh = fn.RectangleMesh(fn.Point(-width / 2, 0.0), fn.Point(width / 2, height),
                        52, 39)  # 52 * 0.75 = 39, elements are square
nn = fn.FacetNormal(mesh)

# Defintion of function spaces
Vh = fn.VectorElement("CG", mesh.ufl_cell(), 2)
Zh = fn.FiniteElement("CG", mesh.ufl_cell(), 1)
Qh = fn.FiniteElement("CG", mesh.ufl_cell(), 2)

# spaces for displacement and total pressure should be compatible
# whereas the space for fluid pressure can be "anything". In particular the one for total pressure

Hh = fn.FunctionSpace(mesh, fn.MixedElement([Vh, Zh, Qh]))

(u, phi, p) = fn.TrialFunctions(Hh)
(v, psi, q) = fn.TestFunctions(Hh)

fileU = fn.File(mesh.mpi_comm(), "Output/2_5_1_Footing_wall_removed/u.pvd")
filePHI = fn.File(mesh.mpi_comm(), "Output/2_5_1_Footing_wall_removed/phi.pvd")
fileP = fn.File(mesh.mpi_comm(), "Output/2_5_1_Footing_wall_removed/p.pvd")

# ******** Model constants ********** #
E = 3.0e4
nu = 0.4995
Пример #8
0
# Define boundaries
inflow  = 'near(x[0], 0)'
outflow = 'near(x[0], 1)'
walls   = 'near(x[1], 0) || near (x[1], 1)'

# Define boundary bonditions
bcu_noslip  = fs.DirichletBC(V, fs.Constant((0,0)), walls)
bcp_inflow  = fs.DirichletBC(Q, fs.Constant(8), inflow)
bcp_outflow = fs.DirichletBC(Q, fs.Constant(0), outflow)
bcu = [bcu_noslip]
bcp = [bcp_inflow, bcp_outflow]

# Constants used to define variational problems
U   = 0.5*(u_n + u)
n   = fs.FacetNormal(mesh)
f   = fs.Constant((0, 0))
k   = fs.Constant(dt)
mu  = fs.Constant(mu)
rho = fs.Constant(rho)

# Define strain-rate tensor
def epsilon(u):
    return fs.sym(fs.nabla_grad(u))

# Define stress tensor
def sigma(u, p):
    return 2*mu*epsilon(u) - p*fs.Identity(len(u))

# Define variational problem for step 1 (Tentative velocity step)
F1 = rho*fs.dot((u - u_n) / k, v)*fs.dx + \
Пример #9
0
    def solve(self, **kwargs):
        """
        Solves the variational form of the electrostatics as defined in the
        End of Master thesis from Ximo Gallud Cidoncha:
            A comprehensive numerical procedure for solving the Taylor-Melcher
            leaky dielectric model with charge evaporation.
        Parameters
        ----------
        **kwargs : dict
            Accepted kwargs are:
                - electrostatics_solver_settings: The user may define its own solver parameters. They must be defined
                as follows:
                solver_parameters = {"snes_solver": {"linear_solver": "mumps",
                                      "maximum_iterations": 50,
                                      "report": True,
                                      "error_on_nonconvergence": True,
                                      'line_search': 'bt',
                                      'relative_tolerance': 1e-4}}
                where:
                    - snes_solver is the type of solver to be used. In this
                    case, it is compulsory to use snes, since it's the solver
                    accepted by multiphenics. However, one may try other
                    options if only FEniCS is used. These are: krylov_solver
                    and lu_solver.
                    - linear_solver is the type of linear solver to be used.
                    - maximum_iterations is the maximum number of iterations
                    the solver will try to solve the problem. In case no
                    convergence is achieved, the variable
                    error_on_nonconvergence will raise an error in case this
                    is True. If the user preferes not to raise an error when
                    no convergence, the script will continue with the last
                    results obtained in the iteration process.
                    - line_search is the type of line search technique to be
                    used for solving the problem. It is stronly recommended to
                    use the backtracking (bt) method, since it has been proven
                    to be the most robust one, specially in cases where sqrt
                    are defined, where NaNs may appear due to a bad initial
                    guess or a bad step in the iteration process.
                    - relative_tolerance will tell the solver the parameter to
                    consider convergence on the solution.
                All this options, as well as all the other options available
                can be consulted by calling the method
                Poisson.check_solver_options().
                - initial_potential: Dolfin/FEniCS function which will be used as an initial guess on the iterative
                    process. This must be introduced along with kwarg initial_surface_charge_density. Optional.
                - initial_surface_charge_density: Dolfin/FEniCS function which will be used as an initial guess on the
                    iterative process. This must be introduced along with kwarg initial_potential. Optional.
        Raises
        ------
        TypeError
            This error will raise when the convection charge has not one of the
            following types:
                - Dolfin Function.
                - FEniCS UserExpression.
                - FEniCS Constant.
                - Integer or float number, which will be converted to a FEniCS
                Constant.
        Returns
        -------
        phi : dolfin.function.function.Function
            Dolfin function containing the potential solution.
        surface_charge_density : dolfin.function.function.Function
            Dolfin function conataining the surface charge density solution.
        """

        # --------------------------------------------------------------------
        # EXTRACT THE INPUTS #
        # --------------------------------------------------------------------

        # Check if the type of j_conv is the proper one.
        if not isinstance(self.j_conv, (int, float)) \
            and not Poisson.isDolfinFunction(self.j_conv) \
            and not Poisson.isfenicsexpression(self.j_conv) \
                and not Poisson.isfenicsconstant(self.j_conv):
            conv_type = type(self.j_conv)
            raise TypeError(
                f'Convection charge must be an integer, float, Dolfin function, FEniCS UserExpression or FEniCS constant, not {conv_type}.'
            )
        else:
            if isinstance(self.j_conv, (int, float)):
                self.j_conv = fn.Constant(float(self.j_conv))

        # Extract the solver parameters.
        solver_parameters = kwargs.get('electrostatics_solver_settings')

        # --------------------------------------------------------------------
        # FUNCTION SPACES #
        # --------------------------------------------------------------------
        # Extract the restrictions to create the function spaces.
        """ This variable will be used by multiphenics when creating function spaces. It will create function spaces
            on the introduced restrictions. 
        """
        restrictions_block = [
            self.restrictions_dict['domain_rtc'],
            self.restrictions_dict['interface_rtc']
        ]

        # Base Function Space.
        V = fn.FunctionSpace(self.mesh, 'Lagrange', 2)

        # Block Function Space.
        """ Block Function Spaces are similar to FEniCS function spaces. However, since we are creating function spaces
        based on the block of restrictions, we need to create a 'block of function spaces' for each of the restrictions.
        That block of functions is the list [V, V] from the line of code below this comment. They are assigned in the
        same order in which the block of restrictions has been created, that is:
            - V -> domain_rtc
            - V -> interface_rtc
        """
        W = mp.BlockFunctionSpace([V, V], restrict=restrictions_block)

        # Check the dimensions of the created block function spaces.
        for ix, _ in enumerate(restrictions_block):
            assert W.extract_block_sub_space(
                (ix, )).dim() > 0., f'Subdomain {ix} has dimension 0.'

        # --------------------------------------------------------------------
        # TRIAL/TEST FUNCTIONS #
        # --------------------------------------------------------------------
        # Trial Functions.
        dphisigma = mp.BlockTrialFunction(W)

        # Test functions.
        vl = mp.BlockTestFunction(W)
        (v, l) = mp.block_split(vl)

        phisigma = mp.BlockFunction(W)
        (phi, sigma) = mp.block_split(phisigma)

        # --------------------------------------------------------------------
        # MEASURES #
        # --------------------------------------------------------------------
        self.get_measures()
        self.dS = self.dS(
            self.boundaries_ids['Interface'])  # Restrict to the interface.

        # Check proper marking of the interface.
        assert fn.assemble(
            1 * self.dS(domain=self.mesh)
        ) > 0., "The length of the interface is zero, wrong marking. Check the files in Paraview."

        # --------------------------------------------------------------------
        # DEFINE THE F TERM #
        # --------------------------------------------------------------------
        n = fn.FacetNormal(self.mesh)
        t = fn.as_vector((n[1], -n[0]))

        # Define auxiliary terms.
        r = fn.SpatialCoordinate(self.mesh)[0]
        K = 1 + self.Lambda * (self.T_h - 1)

        E_v_n_aux = fn.dot(-fn.grad(phi("-")), n("-"))

        def expFun():
            sqrterm = E_v_n_aux
            expterm = (self.Phi /
                       self.T_h) * (1 - pow(self.B, 0.25) * fn.sqrt(sqrterm))
            return fn.exp(expterm)

        def sigma_fun():
            num = K * E_v_n_aux + self.eps_r * self.j_conv
            den = K + (self.T_h / self.Chi) * expFun()
            return r * num / den

        # Define the relative permittivity.

        class relative_perm(fn.UserExpression):
            def __init__(self, markers, subdomain_ids, relative, **kwargs):
                super().__init__(**kwargs)
                self.markers = markers
                self.subdomain_ids = subdomain_ids
                self.relative = relative

            def eval_cell(self, values, x, cell):
                if self.markers[cell.index] == self.subdomain_ids['Vacuum']:
                    values[0] = 1.
                else:
                    values[0] = self.relative

        rel_perm = relative_perm(self.subdomains,
                                 self.subdomains_ids,
                                 relative=self.eps_r,
                                 degree=0)

        # Define the variational form.
        # vacuum_int = r*fn.inner(fn.grad(phi), fn.grad(v))*self.dx(self.subdomains_ids['Vacuum'])
        # liquid_int = self.eps_r*r*fn.inner(fn.grad(phi), fn.grad(v))*self.dx(self.subdomains_ids['Liquid'])

        F = [
            r * rel_perm * fn.inner(fn.grad(phi), fn.grad(v)) * self.dx -
            r * sigma("-") * v("-") * self.dS,
            r * sigma_fun() * l("-") * self.dS -
            r * sigma("-") * l("-") * self.dS
        ]

        J = mp.block_derivative(F, phisigma, dphisigma)

        # --------------------------------------------------------------------
        # BOUNDARY CONDITIONS #
        # --------------------------------------------------------------------
        bcs_block = []
        for i in self.boundary_conditions:
            if 'Dirichlet' in self.boundary_conditions[i]:
                bc_val = self.boundary_conditions[i]['Dirichlet'][0]
                bc = mp.DirichletBC(W.sub(0), bc_val, self.boundaries,
                                    self.boundaries_ids[i])
                # Check the created boundary condition.
                assert len(bc.get_boundary_values()
                           ) > 0., f'Wrongly defined boundary {i}'
                bcs_block.append(bc)

        bcs_block = mp.BlockDirichletBC([bcs_block])

        # --------------------------------------------------------------------
        # SOLVE #
        # --------------------------------------------------------------------
        # Define and assign the initial guesses.
        if kwargs.get('initial_potential') is None:
            """
            Check if the user is introducing a potential from a previous
            iteration.
            """
            phiv, phil, sigma_init = self.solve_initial_problem()
            # phi_init = self.solve_initial_problem_v2()
            phi.assign(phiv)
            sigma.assign(sigma_init)
        else:
            phi.assign(kwargs.get('initial_potential'))
            sigma.assign(kwargs.get('initial_surface_charge_density'))

        # Apply the initial guesses to the main function.
        phisigma.apply('from subfunctions')

        # Solve the problem with the solver options (either default or user).
        problem = mp.BlockNonlinearProblem(F, phisigma, bcs_block, J)
        solver = mp.BlockPETScSNESSolver(problem)
        solver_type = [i for i in solver_parameters.keys()][0]
        solver.parameters.update(solver_parameters[solver_type])
        solver.solve()

        # Extract the solutions.
        (phi, _) = phisigma.block_split()
        self.phi = phi
        # --------------------------------------------------------------------

        # Compute the electric field at vacuum and correct the surface charge density.
        self.E_v = self.get_electric_field('Vacuum')
        self.E_v_n = self.get_normal_field(n("-"), self.E_v)
        self.E_t = self.get_tangential_component(t("+"), self.E_v)
        C = self.Phi / self.T_h * (1 - self.B**0.25 * fn.sqrt(self.E_v_n))
        self.sigma = (K * self.E_v_n) / (K + self.T_h / self.Chi * fn.exp(-C))
Пример #10
0
    def solve(self):
        """
        Solve the Stokes_sim problem based on the mathematical procedure presented by Ximo in this thesis.
        Returns:

        """

        # --------------------------------------------------------------------
        # DEFINE THE INPUTS #
        # --------------------------------------------------------------------
        self.get_mesh()
        self.get_boundaries()
        self.get_subdomains()
        self.get_restrictions()

        # Create a block of restrictions.
        """ This variable will be used by multiphenics when creating function spaces. It will create function spaces
        on the introduced restrictions. 
        """
        block_restrictions = [
            self.restrictions_dict['liquid_rtc'],
            self.restrictions_dict['liquid_rtc'],
            self.restrictions_dict['interface_rtc']
        ]

        # --------------------------------------------------------------------

        # --------------------------------------------------------------------
        # FUNCTION SPACES #
        # --------------------------------------------------------------------
        V = fn.VectorFunctionSpace(self.mesh, "CG", 2)
        Q = fn.FunctionSpace(self.mesh, "CG", 1)
        L = fn.FunctionSpace(self.mesh, "DGT", 0)  # DGT 0.

        # Create a block function space.
        """ Block Function Spaces are similar to FEniCS function spaces. However, since we are creating function spaces
        based on the block of restrictions, we need to create a 'block of function spaces' for each of the restrictions.
        That block of functions is the list [V, Q, L] from the line of code below this comment. They are assigned in the
        same order in which the block of restrictions has been created, that is:
            - V -> liquid_rtc
            - Q -> liquid_rtc
            - L -> interface_rtc
        """
        W = mp.BlockFunctionSpace([V, Q, L], restrict=block_restrictions)
        # --------------------------------------------------------------------

        # --------------------------------------------------------------------
        # TRIAL/TEST FUNCTIONS #
        # --------------------------------------------------------------------
        """ Trial and test functions are created the multiphenics commands for creating these functions. However, the
        difference wrt the FEniCS functions for this purpose, a trial/test function will be created for each of the
        restrictions (for each function space of the BlockFunctionSpace).
        """
        test = mp.BlockTestFunction(W)
        (v, q, l) = mp.block_split(test)

        trial = mp.BlockTrialFunction(W)
        (u, p, theta) = mp.block_split(trial)

        # Use a value of previous velocity to make the system linear, as explained by Ximo.
        u_prev = fn.Function(V)
        u_prev.assign(fn.Constant((0.1, 0.1)))

        # --------------------------------------------------------------------

        # --------------------------------------------------------------------
        # MEASURES #
        # --------------------------------------------------------------------
        self.get_measures()
        self.dS = self.dS(
            self.boundaries_ids['Interface'])  # Restrict to the interface.

        # Check proper marking of the interface.
        assert fn.assemble(
            1 * self.dS(domain=self.mesh)
        ) > 0., "The length of the interface is zero, wrong marking."

        # --------------------------------------------------------------------
        # DEFINE THE VARIATIONAL PROBLEM #
        # --------------------------------------------------------------------
        r = fn.SpatialCoordinate(self.mesh)[0]
        n = fn.FacetNormal(self.mesh)
        tan_vector = fn.as_vector((n[1], -n[0]))
        e_r = fn.Constant((1., 0.))  # Define unit radial vector
        e_z = fn.Constant((0., 1.))  # Define unit axial vector
        aux_term = (self.eps_r * self.Ca *
                    np.sqrt(self.B)) / (1 + self.Lambda * (self.T_h - 1))

        # Define the term a.
        a = r * aux_term * fn.inner((fn.grad(u) + fn.grad(u).T),
                                    (fn.grad(v) + fn.grad(v).T)) * self.dx(
                                        self.subdomains_ids['Liquid'])
        a += 2 / r * aux_term * fn.dot(u, e_r) * fn.dot(v, e_r) * self.dx(
            self.subdomains_ids['Liquid'])

        # Define the term d.
        del_operation = fn.dot(fn.grad(u), u_prev)
        d = r * self.eps_r**2 * self.We * fn.dot(del_operation, v) * self.dx(
            self.subdomains_ids['Liquid'])

        # Define the term l1.
        def evaporated_charge():
            return (self.sigma * self.T_h) / (self.eps_r * self.Chi) * fn.exp(
                -self.Phi / self.T_h *
                (1 - self.B**0.25 * fn.sqrt(self.E_v_n)))

        l1 = -r * evaporated_charge() * l("+") * self.dS

        # Define the term l2.
        l2 = r * self.sigma * fn.dot(self.E_v, tan_vector("-")) * fn.dot(
            v("+"), tan_vector("-")) * self.dS

        # Define the term b.
        def b(vector, scalar):
            radial_term = r * fn.dot(vector, e_r)
            axial_term = r * fn.dot(vector, e_z)
            return -(radial_term.dx(0) + axial_term.dx(1)) * scalar * self.dx(
                self.subdomains_ids['Liquid'])

        # Define the term c.
        c1 = -r * fn.dot(v("+"), n("-")) * theta("+") * self.dS
        c2 = -r * fn.dot(u("+"), n("-")) * l("+") * self.dS

        # Define the tensors to be solved.
        # The following order is used.
        #       u            p           theta       #
        aa = [
            [a + d, b(v, p), c1],  # Test function v
            [b(u, q), 0, 0],  # Test function q
            [c2, 0, 0]
        ]  # Test function l

        bb = [l2, fn.Constant(0.) * q("+") * self.dS, l1]

        # --------------------------------------------------------------------
        # DEFINE THE BOUNDARY CONDITIONS #
        # --------------------------------------------------------------------
        """ When creating Dirichlet boundary conditions with the multiphenics code, a function space from the Block
        must be selected, depending on which subdomain/boundary should it be applied. To do so, the .sub method is used.
        The input is an integer, which depends on the function space in which you want the BC to be applied. For this
        case, inputs of 0, 1 and 2 are accepted, because we have 3 restrictions. The assignments of these ids to the 
        function space is the one done in the block of restrictions.
        """
        bcs_u = []
        bcs_p = []
        for i in self.boundary_conditions:
            if 'Dirichlet' in self.boundary_conditions[i]:
                bc_val = self.boundary_conditions[i]['Dirichlet'][1]
                if self.boundary_conditions[i]['Dirichlet'][0] == 'v':
                    bc = mp.DirichletBC(W.sub(0), bc_val, self.boundaries,
                                        self.boundaries_ids[i])

                    # Check the created boundary condition.
                    assert len(bc.get_boundary_values()
                               ) > 0., f'Wrongly defined boundary {i}'
                    bcs_u.append(bc)
                elif self.boundary_conditions[i]['Dirichlet'][0] == 'p':
                    bc = mp.DirichletBC(W.sub(1), bc_val, self.boundaries,
                                        self.boundaries_ids[i])
                    # Check the created boundary condition.
                    assert len(bc.get_boundary_values()
                               ) > 0., f'Wrongly defined boundary {i}'
                    bcs_p.append(bc)

        bcs_block = mp.BlockDirichletBC([bcs_u, bcs_p])

        # --------------------------------------------------------------------

        # --------------------------------------------------------------------
        # SOLVE #
        # --------------------------------------------------------------------
        # Assemble the system.
        AA = mp.block_assemble(aa)
        BB = mp.block_assemble(bb)

        # Apply the boundary conditions.
        bcs_block.apply(AA)
        bcs_block.apply(BB)

        # Solve.
        uptheta = mp.BlockFunction(W)
        mp.block_solve(AA, uptheta.block_vector(), BB)
        (u, p, theta) = uptheta.block_split()

        self.u = u
        self.p_star = p
        self.theta = theta

        # Compute normal and tangential velocity components.
        u_n = fn.dot(u, n)
        self.u_n = Stokes.block_project(
            u_n,
            self.mesh,
            self.restrictions_dict['interface_rtc'],
            self.boundaries,
            self.boundaries_ids['Interface'],
            space_type='scalar',
            boundary_type='internal',
            sign='-')

        u_t = fn.dot(u, tan_vector)
        self.u_t = Stokes.block_project(
            u_t,
            self.mesh,
            self.restrictions_dict['interface_rtc'],
            self.boundaries,
            self.boundaries_ids['Interface'],
            space_type='scalar',
            boundary_type='internal',
            sign='+')

        # Compute the convection charge transport.
        special = (fn.Identity(self.mesh.topology().dim()) -
                   fn.outer(n, n)) * fn.grad(self.sigma)
        self.j_conv = self.Kc * self.B**(
            3 / 2) * (fn.dot(self.sigma * n, fn.dot(fn.grad(self.u), n)) -
                      fn.dot(self.u, special))
        self.j_conv = Stokes.block_project(
            self.j_conv,
            self.mesh,
            self.restrictions_dict['interface_rtc'],
            self.boundaries,
            self.boundaries_ids['Interface'],
            space_type='scalar',
            boundary_type='internal',
            sign='-')
Пример #11
0
	def __init__(self, form_handler):
		"""Initializes the regularization

		Parameters
		----------
		form_handler : cashocs._forms.ShapeFormHandler
			the corresponding shape form handler object
		"""

		self.form_handler = form_handler
		self.config = self.form_handler.config

		self.dx = fenics.Measure('dx', self.form_handler.mesh)
		self.ds = fenics.Measure('ds', self.form_handler.mesh)

		self.spatial_coordinate = fenics.SpatialCoordinate(self.form_handler.mesh)
		
		self.use_relative_scaling = self.config.getboolean('Regularization', 'use_relative_scaling', fallback=False)

		self.measure_hole = self.config.getboolean('Regularization', 'measure_hole', fallback=False)
		if self.measure_hole:
			self.x_start = self.config.getfloat('Regularization', 'x_start', fallback=0.0)
			self.x_end = self.config.getfloat('Regularization', 'x_end', fallback=1.0)
			if not self.x_end >= self.x_start:
				raise ConfigError('Regularization', 'x_end', 'x_end must not be smaller than x_start.')
			self.delta_x = self.x_end - self.x_start

			self.y_start = self.config.getfloat('Regularization', 'y_start', fallback=0.0)
			self.y_end = self.config.getfloat('Regularization', 'y_end', fallback=1.0)
			if not self.y_end >= self.y_start:
				raise ConfigError('Regularization', 'y_end', 'y_end must not be smaller than y_start.')
			self.delta_y = self.y_end - self.y_start

			self.z_start = self.config.getfloat('Regularization', 'z_start', fallback=0.0)
			self.z_end = self.config.getfloat('Regularization', 'z_end', fallback=1.0)
			if not self.z_end >= self.z_start:
				raise ConfigError('Regularization', 'z_end', 'z_end must not be smaller than z_start.')
			self.delta_z = self.z_end - self.z_start
			if self.form_handler.mesh.geometric_dimension() == 2:
				self.delta_z = 1.0

		self.mu_volume = self.config.getfloat('Regularization', 'factor_volume', fallback=0.0)
		self.target_volume = self.config.getfloat('Regularization', 'target_volume', fallback=0.0)
		if self.config.getboolean('Regularization', 'use_initial_volume', fallback=False):
			if not self.measure_hole:
				self.target_volume = fenics.assemble(Constant(1)*self.dx)
			else:
				self.target_volume = self.delta_x*self.delta_y*self.delta_z - fenics.assemble(Constant(1.0)*self.dx)

		self.mu_surface = self.config.getfloat('Regularization', 'factor_surface', fallback=0.0)
		self.target_surface = self.config.getfloat('Regularization', 'target_surface', fallback=0.0)
		if self.config.getboolean('Regularization', 'use_initial_surface', fallback=False):
			self.target_surface = fenics.assemble(Constant(1)*self.ds)
		
		self.mu_curvature = self.config.getfloat('Regularization', 'factor_curvature', fallback=0.0)
		self.kappa_curvature = fenics.Function(self.form_handler.deformation_space)
		if self.mu_curvature > 0.0:
			n = fenics.FacetNormal(self.form_handler.mesh)
			x = fenics.SpatialCoordinate(self.form_handler.mesh)
			self.a_curvature = inner(fenics.TrialFunction(self.form_handler.deformation_space), fenics.TestFunction(self.form_handler.deformation_space))*self.ds
			self.L_curvature = inner(t_grad(x, n), t_grad(fenics.TestFunction(self.form_handler.deformation_space), n))*self.ds
		
		self.mu_barycenter = self.config.getfloat('Regularization', 'factor_barycenter', fallback=0.0)
		self.target_barycenter_list = json.loads(self.config.get('Regularization', 'target_barycenter', fallback='[0,0,0]'))
		
		if not type(self.target_barycenter_list) == list:
			raise ConfigError('Regularization', 'target_barycenter', 'This has to be a list.')
		
		if self.form_handler.mesh.geometric_dimension() == 2 and len(self.target_barycenter_list) == 2:
			self.target_barycenter_list.append(0.0)
		
		if self.config.getboolean('Regularization', 'use_initial_barycenter', fallback=False):
			self.target_barycenter_list = [0.0, 0.0, 0.0]
			if not self.measure_hole:
				volume = fenics.assemble(Constant(1)*self.dx)
				self.target_barycenter_list[0] = fenics.assemble(self.spatial_coordinate[0]*self.dx) / volume
				self.target_barycenter_list[1] = fenics.assemble(self.spatial_coordinate[1]*self.dx) / volume
				if self.form_handler.mesh.geometric_dimension() == 3:
					self.target_barycenter_list[2] = fenics.assemble(self.spatial_coordinate[2]*self.dx) / volume
				else:
					self.target_barycenter_list[2] = 0.0

			else:
				volume = self.delta_x*self.delta_y*self.delta_z - fenics.assemble(Constant(1)*self.dx)
				self.target_barycenter_list[0] = (0.5*(pow(self.x_end, 2) - pow(self.x_start, 2))*self.delta_y*self.delta_z - fenics.assemble(self.spatial_coordinate[0]*self.dx)) / volume
				self.target_barycenter_list[1] = (0.5*(pow(self.y_end, 2) - pow(self.y_start, 2))*self.delta_x*self.delta_z - fenics.assemble(self.spatial_coordinate[1]*self.dx)) / volume
				if self.form_handler.mesh.geometric_dimension() == 3:
					self.target_barycenter_list[2] = (0.5*(pow(self.z_end, 2) - pow(self.z_start, 2))*self.delta_x*self.delta_y - fenics.assemble(self.spatial_coordinate[2]*self.dx)) / volume
				else:
					self.target_barycenter_list[2] = 0.0
		
				
		if not (self.mu_volume >= 0.0 and self.mu_surface >= 0.0 and self.mu_curvature >= 0 and self.mu_barycenter >= 0.0):
			raise ConfigError('Regularization', 'mu_volume, mu_surface, or mu_barycenter', 'All regularization constants have to be nonnegative.')

		if self.mu_volume > 0.0 or self.mu_surface > 0.0 or self.mu_curvature > 0.0 or self.mu_barycenter > 0.0:
			self.has_regularization = True
		else:
			self.has_regularization = False
		
		self._scale_weights()

		self.current_volume = fenics.Expression('val', degree=0, val=1.0)
		self.current_surface = fenics.Expression('val', degree=0, val=1.0)
		self.current_barycenter_x = fenics.Expression('val', degree=0, val=0.0)
		self.current_barycenter_y = fenics.Expression('val', degree=0, val=0.0)
		self.current_barycenter_z = fenics.Expression('val', degree=0, val=0.0)
Пример #12
0
    def _build_function_space(self):
        width = self.width

        class Exterior(fa.SubDomain):

            def inside(self, x, on_boundary):
                return on_boundary and (
                    fa.near(x[1], 10) or
                    fa.near(x[0], 1) or
                    fa.near(x[0], 0) or
                    fa.near(x[1], 0))

        class Left(fa.SubDomain):

            def inside(self, x, on_boundary):
                return on_boundary and fa.near(x[0], 0)

        class Right(fa.SubDomain):

            def inside(self, x, on_boundary):
                return on_boundary and fa.near(x[0], width)

        class Bottom(fa.SubDomain):

            def inside(self, x, on_boundary):
                return on_boundary and fa.near(x[1], 0)

        class Top(fa.SubDomain):

            def inside(self, x, on_boundary):
                return on_boundary and fa.near(x[1], 10)

        self.exteriors_dic = {
            'left': Left(), 'right': Right(), 'bottom': Bottom(), 'top': Top()}
        self.exterior = Exterior()

        self.V = fa.VectorFunctionSpace(self.mesh, 'P', 1)
        self.W = fa.FunctionSpace(self.mesh, 'DG', 0)

        self.sub_domains = fa.MeshFunction(
            "size_t", self.mesh, self.mesh.topology().dim() - 1)
        self.sub_domains.set_all(0)

        self.boundaries_id_dic = {'left': 1, 'right': 2, 'bottom': 3, 'top': 4}
        self.left = Left()
        self.left.mark(self.sub_domains, 1)
        self.right = Right()
        self.right.mark(self.sub_domains, 2)
        self.bottom = Bottom()
        self.bottom.mark(self.sub_domains, 3)
        self.top = Top()
        self.top.mark(self.sub_domains, 4)

        self.normal = fa.FacetNormal(self.mesh)
        self.ds = fa.Measure("ds")(subdomain_data=self.sub_domains)
        self.bcs = [self.bottom]

        boundaries = [self.bottom, self.left, self.right]
        boundary_fn = [fa.Constant((0., 0.)),
                       fa.Expression(("0", ".1*x[1]"), degree=1),
                       fa.Expression(("0", ".1*x[1]"), degree=1)]
        self.bcs = []
        for i in range(len(boundaries)):
            boundary_bc = fa.DirichletBC(self.V, boundary_fn[i], boundaries[i])
            self.bcs = self.bcs + [boundary_bc]
    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 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 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