示例#1
0
    def _linear_IMEX_no_DIM(self, v: ProxyFunction, gfu_u: GridFunction,
                            dt: Parameter) -> ngs.LinearForm:
        """
        Linear form when IMEX linearization is being used and the diffuse interface method is not being used.
        Handles both CG and DG.
        """

        L = dt * (v * self.f -
                  ngs.InnerProduct(ngs.Grad(gfu_u) * gfu_u, v)) * ngs.dx

        # Define the special DG functions.
        n, h, alpha = get_special_functions(self.mesh, self.nu)

        # Dirichlet BC for u
        if self.DG:
            for marker in self.BC.get('dirichlet', {}).get('u', {}):
                g = self.BC['dirichlet']['u'][marker]
                L += dt * (
                    -self.kv * ngs.InnerProduct(ngs.Grad(
                        v), ngs.OuterProduct(g, n))  # 1/2 of penalty for u=g
                    + self.kv * alpha * g * v  # 1/2 of penalty for u=g
                    # from ∇u^ on 𝚪_D
                ) * self._ds(marker)

        # Stress BC
        for marker in self.BC.get('stress', {}).get('stress', {}):
            h = self.BC['stress']['stress'][marker]
            if self.DG:
                L += dt * v * h * self._ds(marker)
            else:
                L += dt * v.Trace() * h * self._ds(marker)

        return L
示例#2
0
    def _linear_DIM(self, v: ProxyFunction, dt: Parameter) -> ngs.LinearForm:
        """ Linear form when the diffuse interface method is being used. Handles both CG and DG. """

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        # Source term
        L = dt * self.f * v * self.DIM_solver.phi_gfu * ngs.dx

        if self.DG:
            for marker in self.BC.get('dirichlet', {}).get('u', {}):
                # Penalty terms for conformal Dirichlet BCs
                g = self.BC['dirichlet']['u'][marker]
                L += dt * self.dc * (
                    alpha * g *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                    - g * n * ngs.Grad(v)  # 1/2 of penalty for u=g on 𝚪_D
                ) * self._ds(marker)

        # Penalty term for DIM Dirichlet BCs. This is the Nitsche method.
        for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}):
            # Penalty terms for DIM Dirichlet BCs
            g = self.DIM_BC['dirichlet']['u'][marker]
            L += dt * self.dc * (
                ngs.Grad(v) * self.DIM_solver.grad_phi_gfu * g +
                alpha * g * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # DIM Neumann BCs for u.
        for marker in self.DIM_BC.get('neumann', {}).get('u', {}):
            h = self.DIM_BC['neumann']['u'][marker]
            L += dt * self.dc * (
                -h * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # DIM Robin BCs for u.
        for marker in self.DIM_BC.get('robin', {}).get('u', {}):
            r, q = self.DIM_BC['robin']['u'][marker]
            L += dt * self.dc * (
                r * q * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # Conformal Neumann BCs for u
        for marker in self.BC.get('neumann', {}).get('u', {}):
            h = self.BC['neumann']['u'][marker]
            L += dt * self.dc * h * v * self._ds(marker)

        # Conformal Robin BCs for u
        for marker in self.BC.get('robin', {}).get('u', {}):
            r, q = self.BC['robin']['u'][marker]
            L += -dt * self.dc * r * q * v * self._ds(marker)

        return L
示例#3
0
    def _linear_Oseen_DIM(self, v: ProxyFunction, w: GridFunction,
                          dt: Parameter) -> ngs.LinearForm:
        """
        Linear form when the diffuse interface method is being used with Oseen linearization. Handles both CG and DG.
        """

        L = dt * v * self.f * self.DIM_solver.phi_gfu * ngs.dx

        # Define the special DG functions.
        n, h, alpha = get_special_functions(self.mesh, self.nu)

        if self.DG:
            # Conformal Dirichlet BCs for u.
            for marker in self.BC.get('dirichlet', {}).get('u', {}):
                g = self.BC['dirichlet']['u'][marker]
                L += dt * (
                    v * (-0.5 * w * n * g + 0.5 * ngs.Norm(w * n) * g
                         )  # 1/2 of uw^ (convection)
                    - self.kv * ngs.InnerProduct(ngs.Grad(
                        v), ngs.OuterProduct(g, n))  # 1/2 of penalty for u=g
                    + self.kv * alpha * g *
                    v  # 1/2 of penalty for u=g from ∇u^ on 𝚪_D
                ) * self._ds(marker)

        # Penalty term for DIM Dirichlet BCs. This is the Nitsche method.
        for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}):
            # Penalty terms for DIM Dirichlet BCs
            g = self.DIM_BC['dirichlet']['u'][marker]
            L += dt * (
                v * (0.5 * w * self.DIM_solver.grad_phi_gfu * g +
                     0.5 * ngs.Norm(w * -self.DIM_solver.grad_phi_gfu) * g) +
                self.kv * ngs.InnerProduct(
                    ngs.Grad(v),
                    ngs.OuterProduct(g, self.DIM_solver.grad_phi_gfu)) +
                self.kv * alpha * g * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # Conformal stress BC
        for marker in self.BC.get('stress', {}).get('stress', {}):
            h = self.BC['stress']['stress'][marker]
            if self.DG:
                L += dt * v * h * self._ds(marker)
            else:
                L += dt * v.Trace() * h * self._ds(marker)

        # TODO: Add non-Dirichlet DIM BCs.

        return L
示例#4
0
    def _linear_no_DIM(self, v: ProxyFunction,
                       dt: Parameter) -> ngs.LinearForm:
        """ Linear form when the diffuse interface method is not being used. Handles both CG and DG. """

        # Source term
        L = dt * self.f * v * ngs.dx

        # Dirichlet BCs for u, they only get added if using DG
        if self.DG:
            # Define the special DG functions.
            n, _, alpha = get_special_functions(self.mesh, self.nu)

            for marker in self.BC.get('dirichlet', {}).get('u', {}):
                g = self.BC['dirichlet']['u'][marker]
                L += dt * self.dc * (
                    alpha * g *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                    - g * n * ngs.Grad(v)  # 1/2 of penalty for u=g on 𝚪_D
                ) * self._ds(marker)

        # Neumann BCs for u
        for marker in self.BC.get('neumann', {}).get('u', {}):
            h = self.BC['neumann']['u'][marker]
            L += dt * self.dc * h * v * self._ds(marker)

        # Robin BCs for u
        for marker in self.BC.get('robin', {}).get('u', {}):
            r, q = self.BC['robin']['u'][marker]
            L += -dt * self.dc * r * q * v * self._ds(marker)

        return L
示例#5
0
    def _Linear_no_DIM(self, v: ProxyFunction,
                       dt: Parameter) -> ngs.LinearForm:
        """ Linear form when the diffuse interface method is not being used. Handles both CG and DG. """

        # Define the base linear form
        L = dt * v * self.f * ngs.dx

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        if self.DG:
            # Dirichlet BCs for u
            for marker in self.BC.get('dirichlet', {}).get('u', {}):
                g = self.BC['dirichlet']['u'][marker]
                L += dt * self.kv * (
                    alpha * g * v  # 1/2 of penalty for u=g from ∇u^ on 𝚪_D
                    - ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct(
                        g, n))  # 1/2 of penalty for u=g
                ) * self._ds(marker)

        # Stress BCs
        for marker in self.BC.get('stress', {}).get('stress', {}):
            h = self.BC['stress']['stress'][marker]

            if self.DG:
                L += dt * v * h * self._ds(marker)
            else:
                L += dt * v.Trace() * h * self._ds(marker)

        return L
示例#6
0
    def _bilinear_no_DIM(self, u: ProxyFunction, p: ProxyFunction,
                         v: ProxyFunction, q: ProxyFunction, dt: Parameter,
                         explicit_bilinear: bool) -> ngs.BilinearForm:
        """ Bilinear form when the diffuse interface method is not being used. Handles both CG and DG. """

        a = dt * (
            self.kv *
            ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v))  # Stress, Newtonian
            - ngs.div(u) * q  # Conservation of mass
            - ngs.div(v) * p  # Pressure
            - 1e-10 * p * q  # Stabilization term
        ) * ngs.dx

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        # Bulk of Bilinear form
        if self.DG:
            jump_u = jump(u)
            avg_grad_u = grad_avg(u)

            jump_v = jump(v)
            avg_grad_v = grad_avg(v)

            if not explicit_bilinear:
                # Penalty for discontinuities
                a += -dt * self.kv * (
                    ngs.InnerProduct(avg_grad_u, ngs.OuterProduct(jump_v,
                                                                  n))  # Stress
                    + ngs.InnerProduct(avg_grad_v, ngs.OuterProduct(jump_u,
                                                                    n))  # U
                    - alpha * ngs.InnerProduct(
                        jump_u, jump_v)  # Term for u+=u- on 𝚪_I from ∇u^
                ) * ngs.dx(skeleton=True)

            # Penalty for dirichlet BCs
            if self.dirichlet_names.get('u', None) is not None:
                a += -dt * self.kv * (
                    ngs.InnerProduct(ngs.Grad(u), ngs.OuterProduct(
                        v, n))  # ∇u^ = ∇u
                    + ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct(
                        u, n))  # 1/2 of penalty for u=g on 𝚪_D
                    - alpha * u *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                ) * self._ds(self.dirichlet_names['u'])

        return a
示例#7
0
    def _bilinear_no_DIM(self, u: ProxyFunction, v: ProxyFunction,
                         dt: Parameter,
                         explicit_bilinear: bool) -> ngs.BilinearForm:
        """ Bilinear form when the diffuse interface method is not being used. Handles both CG and DG. """

        # Laplacian term
        a = dt * self.dc * ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v)) * ngs.dx

        # Bulk of Bilinear form
        if self.DG:
            # Define the special DG functions.
            n, _, alpha = get_special_functions(self.mesh, self.nu)

            jump_u = jump(u)
            avg_grad_u = grad_avg(u)

            jump_v = jump(v)
            avg_grad_v = grad_avg(v)

            if not explicit_bilinear:
                # Penalty for discontinuities
                a += dt * self.dc * (
                    -jump_u * n * avg_grad_v  # U
                    - avg_grad_u * n *
                    jump_v  # 1/2 term for u+=u- on 𝚪_I from ∇u^
                    + alpha * jump_u *
                    jump_v  # 1/2 term for u+=u- on 𝚪_I from ∇u^
                ) * ngs.dx(skeleton=True)

            if self.dirichlet_names.get('u', None) is not None:
                # Penalty terms for Dirichlet BCs
                a += dt * self.dc * (
                    -u * n * ngs.Grad(v)  # 1/2 of penalty for u=g on 𝚪_D
                    - ngs.Grad(u) * n * v  # ∇u^ = ∇u
                    + alpha * u *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                ) * self._ds(self.dirichlet_names['u'])

        # Robin BCs for u
        for marker in self.BC.get('robin', {}).get('u', {}):
            r, q = self.BC['robin']['u'][marker]
            a += -dt * self.dc * r * u * v * self._ds(marker)

        return a
示例#8
0
    def _linear_DIM(self, v: ProxyFunction, dt: Parameter) -> ngs.LinearForm:
        """ Linear form when the diffuse interface method is being used. Handles both CG and DG. """

        # Define the base linear form
        L = dt * v * self.f * self.DIM_solver.phi_gfu * ngs.dx

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        if self.DG:
            # Conformal Dirichlet BCs for u
            for marker in self.BC.get('dirichlet', {}).get('u', {}):
                g = self.BC['dirichlet']['u'][marker]
                L += dt * self.kv * (
                    alpha * g * v  # 1/2 of penalty for u=g from ∇u^ on 𝚪_D
                    - ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct(
                        g, n))  # 1/2 of penalty for u=g
                ) * self._ds(marker)

        # Penalty term for DIM Dirichlet BCs. This is the Nitsche method.
        for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}):
            # Penalty terms for DIM Dirichlet BCs
            g = self.DIM_BC['dirichlet']['u'][marker]
            L += dt * self.kv * (
                alpha * g * v * self.DIM_solver.mag_grad_phi_gfu +
                ngs.InnerProduct(
                    ngs.Grad(v),
                    ngs.OuterProduct(g, self.DIM_solver.grad_phi_gfu))
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # Stress BCs
        for marker in self.BC.get('stress', {}).get('stress', {}):
            h = self.BC['stress']['stress'][marker]

            if self.DG:
                L += dt * v * h * self._ds(marker)
            else:
                L += dt * v.Trace() * h * self._ds(marker)

        # TODO: Add non-Dirichlet DIM BCs.

        return L
示例#9
0
def setup_rot_elast(mesh,
                    mu=1,
                    lam=0,
                    f_vol=None,
                    multidim=True,
                    reorder=False,
                    diri=".*",
                    order=1,
                    fes_opts=dict(),
                    blf_opts=dict(),
                    lf_opts=dict()):
    dim = mesh.dim
    mysum = lambda x: sum(x[1:], x[0])
    if dim == 2:
        to_skew = lambda x: ngs.CoefficientFunction(
            (0, -x[0], x[0], 0), dims=(2, 2))
    else:
        # to_skew = lambda x : ngs.CoefficientFunction( (  0   , x[2],  -x[1], \
        # -x[2],    0 , x[0], \
        # x[1], -x[0],   0), dims = (3,3) )
        to_skew = lambda x : ngs.CoefficientFunction( (  0   , -x[2],  x[1], \
                                                         x[2],    0 , -x[0], \
                                                         -x[1], x[0],   0), dims = (3,3) )
    if multidim:
        mdim = dim + ((dim - 1) * dim) // 2
        V = ngs.H1(mesh, order=order, dirichlet=diri, **fes_opts, dim=mdim)
        if reorder:
            V = ngs.comp.Reorder(V)
        trial, test = V.TnT()
        u = ngs.CoefficientFunction(tuple(trial[x] for x in range(dim)))
        gradu = ngs.CoefficientFunction(tuple(
            ngs.Grad(trial)[i, j] for i in range(dim) for j in range(dim)),
                                        dims=(dim, dim))
        divu = mysum([ngs.Grad(trial)[i, i] for i in range(dim)])
        w = to_skew([trial[x] for x in range(dim, mdim)])
        ut = ngs.CoefficientFunction(tuple(test[x] for x in range(dim)))
        gradut = ngs.CoefficientFunction(tuple(
            ngs.Grad(test)[i, j] for i in range(dim) for j in range(dim)),
                                         dims=(dim, dim))
        divut = mysum([ngs.Grad(test)[i, i] for i in range(dim)])
        wt = to_skew([test[x] for x in range(dim, mdim)])
    else:
        Vu = ngs.VectorH1(mesh, order=order, dirichlet=diri, **fes_opts)
        if reorder == "sep":
            Vu = ngs.comp.Reorder(Vu)
        if dim == 3:
            Vw = Vu
        else:
            Vw = ngs.H1(mesh, order=order, dirichlet=diri, **fes_opts)
            if reorder == "sep":
                Vw = ngs.comp.Reorder(Vw)
        V = ngs.FESpace([Vu, Vw])
        # print("free pre RO: ", V.FreeDofs())
        if reorder is True:
            V = ngs.comp.Reorder(V)
        # print("free post RO: ", V.FreeDofs())
        (u, w), (ut, wt) = V.TnT()
        gradu = ngs.Grad(u)
        divu = mysum([ngs.Grad(u)[i, i] for i in range(dim)])
        w = to_skew(w)
        gradut = ngs.Grad(ut)
        divut = mysum([ngs.Grad(ut)[i, i] for i in range(dim)])
        wt = to_skew(wt)

    a = ngs.BilinearForm(V, **blf_opts)
    a += (mu * ngs.InnerProduct(gradu - w, gradut - wt)) * ngs.dx
    #a += ngs.InnerProduct(w,wt) * ngs.dx

    #trial, test = V.TnT()
    #a += 0.1 * ngs.InnerProduct(trial,test) * ngs.dx

    if lam != 0:
        a += lam * divu * divut * ngs.dx

    lf = ngs.LinearForm(V)
    lf += f_vol * ut * ngs.dx

    return V, a, lf
示例#10
0
    def _bilinear_IMEX_no_DIM(self, u: ProxyFunction, p: ProxyFunction,
                              v: ProxyFunction, q: ProxyFunction,
                              dt: Parameter,
                              explicit_bilinear) -> ngs.BilinearForm:
        """
        Bilinear form when IMEX linearization is being used and the diffuse interface method is not being used.
        Handles both CG and DG.
        """

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        p_I = construct_p_mat(p, self.mesh.dim)

        a = dt * (
            self.kv *
            ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v))  # Stress, Newtonian
            - ngs.div(u) * q  # Conservation of mass
            - ngs.div(v) * p  # Pressure
            - 1e-10 * p * q  # Stabilization term
        ) * ngs.dx

        if self.DG:
            jump_u = jump(u)
            avg_grad_u = grad_avg(u)

            jump_v = jump(v)
            avg_grad_v = grad_avg(v)

            if not explicit_bilinear:
                # Penalty for discontinuities
                a += dt * (
                    -self.kv * ngs.InnerProduct(
                        avg_grad_u, ngs.OuterProduct(jump_v, n))  # Stress
                    - self.kv * ngs.InnerProduct(
                        avg_grad_v, ngs.OuterProduct(jump_u, n))  # U
                    + self.kv * alpha * ngs.InnerProduct(
                        jump_u, jump_v)  # Penalty term for u+=u- on 𝚪_I
                    # from ∇u^
                ) * ngs.dx(skeleton=True)

            # Penalty for dirichlet BCs
            if self.dirichlet_names.get('u', None) is not None:
                a += dt * (
                    -self.kv * ngs.InnerProduct(
                        ngs.Grad(u), ngs.OuterProduct(v, n))  # ∇u^ = ∇u
                    - self.kv *
                    ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct(
                        u, n))  # 1/2 of penalty for u=g on
                    + self.kv * alpha * u * v  # 1/2 of penalty term for u=g
                    # on 𝚪_D from ∇u^
                ) * self._ds(self.dirichlet_names['u'])

        # Parallel Flow BC
        for marker in self.BC.get('parallel', {}).get('parallel', {}):
            if self.DG:
                a += dt * v * (u -
                               n * ngs.InnerProduct(u, n)) * self._ds(marker)
            else:
                a += dt * v.Trace() * (
                    u.Trace() -
                    n * ngs.InnerProduct(u.Trace(), n)) * self._ds(marker)

        return a
示例#11
0
    def _bilinear_IMEX_DIM(self, u: ProxyFunction, p: ProxyFunction,
                           v: ProxyFunction, q: ProxyFunction, dt: Parameter,
                           explicit_bilinear) -> ngs.BilinearForm:
        """
        Bilinear form when the diffuse interface method is being used with IMEX linearization. Handles both CG and DG.
        """

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        p_I = construct_p_mat(p, self.mesh.dim)

        a = dt * (
            self.kv *
            ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v))  # Stress, Newtonian
            - ngs.div(u) * q  # Conservation of mass
            - ngs.div(v) * p  # Pressure
            - 1e-10 * p * q  # Stabilization term
        ) * self.DIM_solver.phi_gfu * ngs.dx

        # Force u and grad(p) to zero where phi is zero.
        a += dt * (
            alpha * u *
            v  # Removing the alpha penalty following discussion with James.
            - p * (ngs.div(v))) * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx

        if self.DG:
            jump_u = jump(u)
            avg_grad_u = grad_avg(u)

            jump_v = jump(v)
            avg_grad_v = grad_avg(v)

            if not explicit_bilinear:
                # Penalty for discontinuities
                a += dt * (
                    -self.kv * ngs.InnerProduct(
                        avg_grad_u, ngs.OuterProduct(jump_v, n))  # Stress
                    - self.kv * ngs.InnerProduct(
                        avg_grad_v, ngs.OuterProduct(jump_u, n))  # U
                    + self.kv * alpha * ngs.InnerProduct(
                        jump_u, jump_v)  # Penalty term for u+=u- on 𝚪_I
                    # from ∇u^
                ) * self.DIM_solver.phi_gfu * ngs.dx(skeleton=True)

            if self.dirichlet_names.get('u', None) is not None:
                # Penalty terms for conformal Dirichlet BCs
                a += dt * (
                    -self.kv * ngs.InnerProduct(
                        ngs.Grad(u), ngs.OuterProduct(v, n))  # ∇u^ = ∇u
                    - self.kv *
                    ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct(
                        u, n))  # 1/2 of penalty for u=g on
                    + self.kv * alpha * u *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                ) * self._ds(self.dirichlet_names['u'])

        # Penalty term for DIM Dirichlet BCs. This is the Nitsche method.
        for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}):
            a += dt * (self.kv * ngs.InnerProduct(
                ngs.Grad(u), ngs.OuterProduct(v, self.DIM_solver.grad_phi_gfu))
                       + self.kv * ngs.InnerProduct(
                           ngs.Grad(v),
                           ngs.OuterProduct(u, self.DIM_solver.grad_phi_gfu)) +
                       self.kv * alpha * u * v *
                       self.DIM_solver.mag_grad_phi_gfu
                       ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # Conformal parallel flow BC
        for marker in self.BC.get('parallel', {}).get('parallel', {}):
            if self.DG:
                a += dt * v * (u -
                               n * ngs.InnerProduct(u, n)) * self._ds(marker)
            else:
                a += dt * v.Trace() * (
                    u.Trace() -
                    n * ngs.InnerProduct(u.Trace(), n)) * self._ds(marker)

        # TODO: Add non-Dirichlet DIM BCs.

        return a
示例#12
0
    def _bilinear_DIM(self, u: ProxyFunction, v: ProxyFunction, dt: Parameter,
                      explicit_bilinear: bool) -> ngs.BilinearForm:
        """ Bilinear form when the diffuse interface method is being used. Handles both CG and DG. """

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        # Laplacian term
        a = dt * self.dc * ngs.InnerProduct(
            ngs.Grad(u), ngs.Grad(v)) * self.DIM_solver.phi_gfu * ngs.dx

        # Force u to zero where phi is zero.
        # Applying a penalty can help convergence, but the penalty seems to interfere with Neumann BCs.
        if self.DIM_BC.get('neumann', {}).get('u', {}):
            a += dt * u * v * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx
        else:
            a += dt * alpha * u * v * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx

        # Bulk of Bilinear form
        if self.DG:
            jump_u = jump(u)
            avg_grad_u = grad_avg(u)

            jump_v = jump(v)
            avg_grad_v = grad_avg(v)

            if not explicit_bilinear:
                # Penalty for discontinuities
                a += dt * self.dc * (
                    -jump_u * n * avg_grad_v  # U
                    - avg_grad_u * jump_v *
                    n  # 1/2 term for u+=u- on 𝚪_I from ∇u^
                    + alpha * jump_u *
                    jump_v  # 1/2 term for u+=u- on 𝚪_I from ∇u^
                ) * self.DIM_solver.phi_gfu * ngs.dx(skeleton=True)

            if self.dirichlet_names.get('u', None) is not None:
                # Penalty terms for conformal Dirichlet BCs
                a += dt * self.dc * (
                    -u * n * ngs.Grad(v)  # 1/2 of penalty for u=g on 𝚪_D
                    - ngs.Grad(u) * n * v  # ∇u^ = ∇u
                    + alpha * u *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                ) * self._ds(self.dirichlet_names['u'])

        # Penalty term for DIM Dirichlet BCs. This is the Nitsche method.
        for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}):
            a += dt * self.dc * (
                ngs.Grad(u) * self.DIM_solver.grad_phi_gfu * v +
                ngs.Grad(v) * self.DIM_solver.grad_phi_gfu * u +
                alpha * u * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # DIM Robin BCs for u.
        for marker in self.DIM_BC.get('robin', {}).get('u', {}):
            r, q = self.DIM_BC['robin']['u'][marker]
            a += dt * self.dc * (
                r * u * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # Conformal Robin BCs for u
        for marker in self.BC.get('robin', {}).get('u', {}):
            r, q = self.BC['robin']['u'][marker]
            a += -dt * self.dc * r * u * v * self._ds(marker)

        return a
示例#13
0
    def get_DIM_gridfunctions(self, mesh: ngs.Mesh, interp_ord: int):
        """
        Function to get all of the phase fields and masks as gridfunctions. This is either done by loading them from
        files or by constructing the numpy arrays and then converting those into gridfunctions.

        Args:
            mesh: The mesh for the gridfunctions.
            interp_ord: The interpolant order for the finite element space for the gridfunctions.
        """

        if self.load_method == 'file':
            # The phase field and masks have already been created and saved as gridfunctions.
            fes = ngs.H1(mesh, order=interp_ord)

            phi_filename = self.config.get_item(
                ['PHASE FIELDS', 'phi_filename'], str)
            self.phi_gfu = create_and_load_gridfunction_from_file(
                phi_filename, fes)

            if self.multiple_bcs:
                # There are BC masks that must be loaded.
                for marker, filename in self.vertices.items():
                    mask_gfu = create_and_load_gridfunction_from_file(
                        filename, fes)
                    self.mask_gfu_dict[marker] = mask_gfu
            else:
                # One single mask that is just a grid function of ones.
                mask_gfu = ngs.GridFunction(fes)
                mask_gfu.Set(1.0)
                self.mask_gfu_dict['all'] = mask_gfu

            # Construct Grad(phi) and |Grad(phi)|.
            self.grad_phi_gfu = ngs.Grad(self.phi_gfu)
            self.mag_grad_phi_gfu = ngs.Norm(ngs.Grad(self.phi_gfu))

        else:
            # The phase field and masks must be generated as numpy arrays and then converted into gridfunctions.
            # The phase field array has already been generated because it is needed to generate the mesh.
            # Construct the phase field, Grad(phi), and |Grad(phi)|.
            if self.N == self.N_mesh:
                # phi was generated on the simulation mesh, so just load phi into a gridfunction and compute Grad(phi)
                # and |Grad(phi)|.
                self.phi_gfu = numpy_to_NGSolve(self.mesh, interp_ord,
                                                self.phi_arr, self.scale,
                                                self.offset, self.dim)
                self.grad_phi_gfu = ngs.Grad(self.phi_gfu)
                self.mag_grad_phi_gfu = ngs.Norm(ngs.Grad(self.phi_gfu))
            else:
                # phi was generated on a refined mesh, so load it into a refined mesh gridfunction, compute Grad(phi)
                # and |Grad(phi)|, then project all three into gridfunctions defined on the simulation mesh.
                phi_gfu_tmp = numpy_to_NGSolve(self.mesh_refined, interp_ord,
                                               self.phi_arr, self.scale,
                                               self.offset, self.dim)
                grad_phi_gfu_tmp = ngs.Grad(phi_gfu_tmp)
                mag_grad_phi_gfu_tmp = ngs.Norm(ngs.Grad(phi_gfu_tmp))

                # Now project onto the coarse simulation mesh.
                fes = ngs.H1(mesh, order=interp_ord)
                vec_fes = ngs.VectorH1(mesh, order=interp_ord)
                self.phi_gfu = ngs.GridFunction(fes)
                self.grad_phi_gfu = ngs.GridFunction(vec_fes)
                self.mag_grad_phi_gfu = ngs.GridFunction(fes)

                self.phi_gfu.Set(phi_gfu_tmp)
                self.grad_phi_gfu.Set(grad_phi_gfu_tmp)
                self.mag_grad_phi_gfu.Set(mag_grad_phi_gfu_tmp)

            if self.multiple_bcs:
                # There are multiple BC masks that must be generated and loaded.
                for marker, mask_arr in self.mask_arr_dict.items():
                    mask_gfu = numpy_to_NGSolve(mesh, interp_ord, mask_arr,
                                                self.scale, self.offset,
                                                self.dim)
                    self.mask_gfu_dict[marker] = mask_gfu
            else:
                # One single mask that is just a grid function of ones.
                mask_arr = np.ones(tuple([int(n + 1) for n in self.N]))
                mask_gfu = numpy_to_NGSolve(mesh, interp_ord, mask_arr,
                                            self.scale, self.offset, self.dim)
                self.mask_gfu_dict['all'] = mask_gfu

            # Save the gridfunctions if desired.
            save_to_file = self.config.get_item(
                ['PHASE FIELDS', 'save_to_file'], bool)
            if save_to_file:
                self.phi_gfu.Save(self.DIM_dir + '/phi.sol')
                self.ngmesh.Save(self.DIM_dir + '/mesh.vol')

                for marker, gfu in self.mask_gfu_dict.items():
                    # Save the BC masks inside the DIM bc_dir.
                    gfu.Save(self.DIM_dir +
                             '/bc_dir/{}_mask.sol'.format(marker))
示例#14
0
    def _bilinear_DIM(self, u: ProxyFunction, p: ProxyFunction,
                      v: ProxyFunction, q: ProxyFunction, dt: Parameter,
                      explicit_bilinear: bool) -> ngs.BilinearForm:
        """ Bilinear form when the diffuse interface method is being used. Handles both CG and DG. """

        # Define the special DG functions.
        n, _, alpha = get_special_functions(self.mesh, self.nu)

        a = dt * (
            self.kv *
            ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v))  # Stress, Newtonian
            - ngs.div(u) * q  # Conservation of mass
            - ngs.div(v) * p  # Pressure
            #- 1e-10 * p * q   # Stabilization term.
        ) * self.DIM_solver.phi_gfu * ngs.dx

        # Force u and grad(p) to zero where phi is zero.
        a += dt * (alpha * u * v -
                   p * ngs.div(v)) * (1.0 - self.DIM_solver.phi_gfu) * ngs.dx

        # Bulk of Bilinear form
        if self.DG:
            jump_u = jump(u)
            avg_grad_u = grad_avg(u)

            jump_v = jump(v)
            avg_grad_v = grad_avg(v)

            if not explicit_bilinear:
                # Penalty for discontinuities
                a += -dt * self.kv * (
                    ngs.InnerProduct(avg_grad_u, ngs.OuterProduct(jump_v,
                                                                  n))  # Stress
                    + ngs.InnerProduct(avg_grad_v, ngs.OuterProduct(jump_u,
                                                                    n))  # U
                    - alpha * ngs.InnerProduct(
                        jump_u, jump_v)  # Term for u+=u- on 𝚪_I from ∇u^
                ) * self.DIM_solver.phi_gfu * ngs.dx(skeleton=True)

            if self.dirichlet_names.get('u', None) is not None:
                # Penalty terms for conformal Dirichlet BCs
                a += -dt * self.kv * (
                    ngs.InnerProduct(ngs.Grad(u), ngs.OuterProduct(
                        v, n))  # ∇u^ = ∇u
                    + ngs.InnerProduct(ngs.Grad(v), ngs.OuterProduct(
                        u, n))  # 1/2 of penalty for u=g on 𝚪_D
                    - alpha * u *
                    v  # 1/2 of penalty term for u=g on 𝚪_D from ∇u^
                ) * self._ds(self.dirichlet_names['u'])

        # Penalty term for DIM Dirichlet BCs. This is the Nitsche method.
        for marker in self.DIM_BC.get('dirichlet', {}).get('u', {}):
            a += dt * self.kv * (
                ngs.InnerProduct(
                    ngs.Grad(u),
                    ngs.OuterProduct(v, self.DIM_solver.grad_phi_gfu)) +
                ngs.InnerProduct(
                    ngs.Grad(v),
                    ngs.OuterProduct(u, self.DIM_solver.grad_phi_gfu)) +
                alpha * u * v * self.DIM_solver.mag_grad_phi_gfu
            ) * self.DIM_solver.mask_gfu_dict[marker] * ngs.dx

        # TODO: Add non-Dirichlet DIM BCs.

        return a