Пример #1
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
Пример #2
0
    def _linear_Oseen_no_DIM(self, v: ProxyFunction, w: GridFunction,
                             dt: Parameter) -> ngs.LinearForm:
        """
        Linear form when Oseen linearization is being used and the diffuse interface method is not being used.
        Handles both CG and DG.
        """

        L = dt * v * self.f * 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 * (
                    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)

        # 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
Пример #3
0
def run(t):
    w.data = a.mat * gf_psi.vec
    du.data = inv * w
    gf_psi.vec.data -= timestep * du

    print('t: ', t, ' Norm(psi): ', ngs.Norm(gf_psi.vec))
    l1.set_ydata(abs(gf_psi.vec.FV().NumPy()))
    return l1, l2,
Пример #4
0
                a.Apply(gfu.vec, xx)
                # print('damping:', damping)
                # print('curr:', ngs.Norm(curr))
                # print('xx:', ngs.Norm(xx))
                # print('w:', ngs.Norm(w))
                w2.data = mass.mat * curr
                mid.data = timestep/2 * (w + b) - w2 + b2
                # print('mid:', ngs.Norm(mid))

                a.AssembleLinearization(curr)
                mat.AsVector().data = timestep/2 * a.mat.AsVector() - mass.mat.AsVector()
                inv = mat.Inverse(V.FreeDofs())
                du.data = inv * mid
                # print('du:', ngs.Norm(du))
                curr.data -= damping * du
                # print('curr:', ngs.Norm(curr))

                a.Apply(curr, w)
                w2.data = mass.mat * curr
                new_mid.data = timestep/2 * (w + b) - w2 + b2
                # print('new_mid', ngs.Norm(new_mid))
                # if ngs.Norm(new_mid) < newton_safety_rho * ngs.Norm(mid):
                if ngs.Norm(new_mid) < ngs.Norm(mid):
                    break
                elif damping == newton_damping_sequence[-1]:
                    print("Damped Newton doesn't converge")
                    sys.exit()

        gfu.vec.data = curr.data
        ngs.Redraw()
Пример #5
0
m.Assemble()

## Initial condition
### Gaussian wave packet
delta_x = 2
x0 = -20
kx = 2
wave_packet = ngs.CoefficientFunction(
    exp(1j * (kx * x)) * exp(-((x - x0)**2) / 4 / delta_x**2))

### Heaviside function
# wave_packet = ngs.CoefficientFunction(IfPos(x, 1, 0))

gf_psi = ngs.GridFunction(fes)
gf_psi.Set(wave_packet)
gf_psi.vec.data = 1 / ngs.Norm(gf_psi.vec) * gf_psi.vec

freedofs = fes.FreeDofs()
for i in range(len(gf_psi.vec)):
    if not freedofs[i]:
        gf_psi.vec[i] = 0

## Crank-Nicolson time step
max_time = 100
timestep = 0.1
t = 0

mstar = m.mat.CreateMatrix()
mstar.AsVector().data = timestep / 2 * a.mat.AsVector() - m.mat.AsVector()
inv = mstar.Inverse(freedofs)
Пример #6
0
    def _bilinear_Oseen_no_DIM(self, u: ProxyFunction, p: ProxyFunction,
                               v: ProxyFunction, q: ProxyFunction,
                               dt: Parameter,
                               explicit_bilinear) -> ngs.BilinearForm:
        """
        Bilinear form when Oseen 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)

        w = self.W[0]

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

        if self.DG:
            avg_u = avg(u)
            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 * (
                    jump_v * (w * n * avg_u + 0.5 * ngs.Norm(w * n) * jump_u
                              )  # Convection
                    - 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 * (
                    v * (0.5 * w * n * u + 0.5 * ngs.Norm(w * n) * u
                         )  # 1/2 of uw^ (convection term)
                    - 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)

        # Stress needs a no-backflow component in the bilinear form.
        for marker in self.BC.get('stress', {}).get('stress', {}):
            if self.DG:
                a += dt * v * (ngs.IfPos(w * n, w * n, 0.0) *
                               u) * self._ds(marker)
            else:
                a += dt * v.Trace() * (ngs.IfPos(w * n, w * n, 0.0) *
                                       u.Trace()) * self._ds(marker)

        return a
Пример #7
0
    def _bilinear_Oseen_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 Oseen 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)

        w = self.W[0]

        a = dt * (
            self.kv *
            ngs.InnerProduct(ngs.Grad(u), ngs.Grad(v))  # Stress, Newtonian
            - ngs.InnerProduct(ngs.OuterProduct(u, w),
                               ngs.Grad(v))  # Convection term
            - 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:
            avg_u = avg(u)
            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 * (
                    jump_v * (w * n * avg_u + 0.5 * ngs.Norm(w * n) * jump_u
                              )  # Convection
                    - 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 * (
                    v * (0.5 * w * n * u + 0.5 * ngs.Norm(w * n) * u
                         )  # 1/2 of uw^ (convection term)
                    - 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 * (
                v * (0.5 * w * -self.DIM_solver.grad_phi_gfu * u +
                     0.5 * ngs.Norm(w * -self.DIM_solver.grad_phi_gfu) * u) +
                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 stress BC needs a no-backflow component in the bilinear form.
        for marker in self.BC.get('stress', {}).get('stress', {}):
            if self.DG:
                a += dt * v * (ngs.IfPos(w * n, w * n, 0.0) *
                               u) * self._ds(marker)
            else:
                a += dt * v.Trace() * (ngs.IfPos(w * n, w * n, 0.0) *
                                       u.Trace()) * self._ds(marker)

        # 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
Пример #8
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))
Пример #9
0
fes = ngs.H1(mesh, order=2, dirichlet=[1,2,3,4], complex=True)

u = fes.TrialFunction()
v = fes.TestFunction()

a = ngs.BilinearForm(fes)
a += ngs.SymbolicBFI(hbar / 2 / m_e * grad(u) * grad(v))
a.Assemble()

m = ngs.BilinearForm(fes)
m += ngs.SymbolicBFI(u * v)
m.Assemble()

gf_psi = ngs.GridFunction(fes)

freedofs = fes.FreeDofs()
for i in range(len(gf_psi.vec)):
    gf_psi.vec[i] = random() if freedofs[i] else 0

inv = a.mat.Inverse(freedofs)

w = gf_psi.vec.CreateVector()
for i in range(100):
    w.data = m.mat * gf_psi.vec
    gf_psi.vec.data = inv * w
    norm = 1/ngs.Norm(gf_psi.vec)
    gf_psi.vec.data = norm * gf_psi.vec

ngs.Draw(gf_psi)
Пример #10
0
m = ngs.BilinearForm(fes)
m += ngs.SymbolicBFI(1j * u * v)
m.Assemble()

## Initial condition
delta_x = 0.05
x0 = 0.2
y0 = 0.5
kx = 0
ky = 0
wave_packet = ngs.CoefficientFunction(
    exp(1j * (kx * x + ky * y)) * exp(-((x-x0)**2+(y-y0)**2)/4/delta_x**2))

gf_psi = ngs.GridFunction(fes)
gf_psi.Set(wave_packet)
gf_psi.vec.data = 1/ngs.Norm(gf_psi.vec) * gf_psi.vec

freedofs = fes.FreeDofs()
for i in range(len(gf_psi.vec)):
    if not freedofs[i]:
        gf_psi.vec[i] = 0

ngs.Draw(ngs.Norm(gf_psi), mesh, name='abs(psi)')

## Crank-Nicolson time step
max_time = 1
timestep = 0.0001
t = 0

mstar = m.mat.CreateMatrix()
mstar.AsVector().data = timestep / 2 * a.mat.AsVector() - m.mat.AsVector()