Ejemplo n.º 1
0
    def run(self):
        sim = self.simulation
        sim.hooks.simulation_started()
        sim.hooks.new_timestep(timestep_number=1, t=1.0, dt=1.0)

        # Assemble system
        A = dolfin.assemble(self.form_lhs)
        b = dolfin.assemble(self.form_rhs)

        # Create Krylov solver
        solver = linear_solver_from_input(sim, 'solver/phi', 'cg')
        solver.set_operator(A)

        # The function where the solution is stored
        phi = self.simulation.data['phi']

        # Remove null space if present (pure Neumann BCs)
        if self.has_null_space:
            null_vec = dolfin.Vector(phi.vector())
            phi.function_space().dofmap().set(null_vec, 1.0)
            null_vec *= 1.0 / null_vec.norm("l2")

            null_space = dolfin.VectorSpaceBasis([null_vec])
            dolfin.as_backend_type(A).set_nullspace(null_space)
            null_space.orthogonalize(b)

        # Solve the linear system using the default solver (direct LU solver)
        solver.solve(phi.vector(), b)

        sim.hooks.end_timestep()
        sim.hooks.simulation_ended(success=True)
Ejemplo n.º 2
0
 def nullspace(self) -> df.VectorSpaceBasis:
     if self._nullspace_basis is None:
         null_vector = df.Vector(self.vur.vector())
         self.VUR.sub(1).dofmap().set(null_vector, 1.0)
         null_vector *= 1.0 / null_vector.norm("l2")
         self._nullspace_basis = df.VectorSpaceBasis([null_vector])
     return self._nullspace_basis
Ejemplo n.º 3
0
    def pressure_correction(self):
        """
        Solve the pressure correction equation

        We handle the case where only Neumann conditions are given
        for the pressure by taking out the nullspace, a constant shift
        of the pressure, by providing the nullspace to the solver
        """
        p = self.simulation.data['p']
        p_hat = self.simulation.data['p_hat']

        # Temporarily store the old pressure
        p_hat.vector().zero()
        p_hat.vector().axpy(-1, p.vector())

        # Assemble the A matrix only the first inner iteration
        if self.inner_iteration == 1:
            self.Ap = dolfin.as_backend_type(self.eq_pressure.assemble_lhs())
        A = self.Ap
        b = dolfin.as_backend_type(self.eq_pressure.assemble_rhs())

        # Inform PETSc about the null space
        if self.remove_null_space:
            if self.pressure_null_space is None:
                # Create vector that spans the null space
                null_vec = dolfin.Vector(p.vector())
                null_vec[:] = 1
                null_vec *= 1 / null_vec.norm("l2")

                # Create null space basis object
                self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec])

            # Make sure the null space is set on the matrix
            if self.inner_iteration == 1:
                A.set_nullspace(self.pressure_null_space)

            # Orthogonalize b with respect to the null space
            self.pressure_null_space.orthogonalize(b)

        # Solve for the new pressure correction
        self.niters_p = self.pressure_solver.inner_solve(
            A,
            p.vector(),
            b,
            in_iter=self.inner_iteration,
            co_iter=self.co_inner_iter)

        # Removing the null space of the matrix system is not strictly the same as removing
        # the null space of the equation, so we correct for this here
        if self.remove_null_space:
            dx2 = dolfin.dx(domain=p.function_space().mesh())
            vol = dolfin.assemble(dolfin.Constant(1) * dx2)
            pavg = dolfin.assemble(p * dx2) / vol
            p.vector()[:] -= pavg

        # Calculate p_hat = p_new - p_old
        p_hat.vector().axpy(1, p.vector())

        return p_hat.vector().norm('l2')
Ejemplo n.º 4
0
    def solve_coupled(self):
        """
        Solve the coupled equations
        """
        # Assemble the equation system
        A = self.eqs.assemble_lhs()
        b = self.eqs.assemble_rhs()

        if self.fix_pressure_dof:
            A.ident(self.pressure_row_to_fix)
        elif self.remove_null_space:
            if self.pressure_null_space is None:
                # Create null space vector in Vp Space
                null_func = dolfin.Function(self.simulation.data['Vp'])
                null_vec = null_func.vector()
                null_vec[:] = 1
                null_vec *= 1 / null_vec.norm("l2")

                # Convert null space vector to coupled space
                null_func2 = dolfin.Function(self.simulation.data['Vcoupled'])
                ndim = self.simulation.ndim
                fa = dolfin.FunctionAssigner(self.subspaces[ndim],
                                             self.simulation.data['Vp'])
                fa.assign(null_func2.sub(ndim), null_func)

                # Create the null space basis
                self.pressure_null_space = dolfin.VectorSpaceBasis(
                    [null_func2.vector()])

            # Make sure the null space is set on the matrix
            dolfin.as_backend_type(A).set_nullspace(self.pressure_null_space)

            # Orthogonalize b with respect to the null space
            self.pressure_null_space.orthogonalize(b)

        # Solve the equation system
        self.simulation.hooks.matrix_ready('Coupled', A, b)
        self.coupled_solver.solve(A, self.coupled_func.vector(), b)

        # Assign into the regular (split) functions from the coupled function
        funcs = [self.simulation.data[name] for name in self.subspace_names]
        self.assigner.assign(funcs, self.coupled_func)

        # If we remove the null space of the matrix system this will not be the exact same as
        # removing the proper null space of the equation, so we fix this here
        if self.fix_pressure_dof:
            p = self.simulation.data['p']
            dx2 = dolfin.dx(domain=p.function_space().mesh())
            vol = dolfin.assemble(dolfin.Constant(1) * dx2)
            # Perform correction multiple times due to round-of error. The first correction
            # can be i.e 1e14 while the next correction is around unity
            pavg = 1e10
            while abs(pavg) > 1000:
                pavg = dolfin.assemble(p * dx2) / vol
                p.vector()[:] -= pavg
Ejemplo n.º 5
0
    def __init__(self,
                 V,
                 bcs=None,
                 objects=None,
                 circuit=None,
                 remove_null_space=False,
                 eps0=1,
                 linalg_solver='gmres',
                 linalg_precond='hypre_amg'):

        if bcs == None:
            bcs = []

        if objects == None:
            objects = []

        if not isinstance(bcs, list):
            bcs = [bcs]

        if not isinstance(objects, list):
            objects = [objects]

        self.V = V
        self.bcs = bcs
        self.objects = objects
        self.circuit = circuit
        self.remove_null_space = remove_null_space
        """
        One could perhaps identify the cases in which different solvers and preconditioners
        should be used, and by default choose the best suited for the problem.
        """
        self.solver = df.PETScKrylovSolver(linalg_solver, linalg_precond)
        self.solver.parameters['absolute_tolerance'] = 1e-14
        self.solver.parameters['relative_tolerance'] = 1e-12
        self.solver.parameters['maximum_iterations'] = 1000
        self.solver.parameters['nonzero_initial_guess'] = True

        phi = df.TrialFunction(V)
        phi_ = df.TestFunction(V)

        self.a = df.Constant(eps0) * df.inner(df.grad(phi),
                                              df.grad(phi_)) * df.dx
        A = df.assemble(self.a)

        for bc in bcs:
            bc.apply(A)

        for o in objects:
            o.apply(A)

        if circuit != None:
            A, = circuit.apply(A)

        if remove_null_space:
            phi = df.Function(V)
            null_vec = df.Vector(phi.vector())
            V.dofmap().set(null_vec, 1.0)
            null_vec *= 1.0 / null_vec.norm("l2")

            self.null_space = df.VectorSpaceBasis([null_vec])
            df.as_backend_type(A).set_nullspace(self.null_space)

        self.A = A
        self.phi_ = phi_
Ejemplo n.º 6
0
    def pressure_correction(self, reassemble, last_piso_iter):
        """
        PIMPLE pressure correction
        """
        sim = self.simulation
        u_star = sim.data['uvw_star']
        p_star = sim.data['p']
        minus_p_hat = self.simulation.data['p_hat']

        # Assemble only once per time step
        if reassemble:
            self.E = dolfin.as_backend_type(self.matrices.assemble_E())

            # Compute LHS
            self.AtinvB = matmul(self.A_tilde_inv, self.B, self.AtinvB)
            self.CAtinvB = matmul(self.C, self.AtinvB, self.CAtinvB)

            # Needed for RHS
            self.AtinvA = matmul(self.A_tilde_inv, self.A, self.AtinvA)
            self.CAtinvA = matmul(self.C, self.AtinvA, self.CAtinvA)

        # Compute the residual divergence
        U = u_star.vector()
        div = self.C * U - self.E
        div_err = div.norm('l2')

        # The equation system
        lhs = self.CAtinvB
        rhs = div - self.CAtinvA * U + self.C * (self.A_tilde_inv * self.D)

        if DEBUG_RHS:
            # Quantify RHS contributions
            c0 = (self.C * U).norm('l2')
            c1 = (self.E).norm('l2')
            c2 = (self.CAtinvA * U).norm('l2')
            c3 = (self.C * (self.A_tilde_inv * self.D)).norm('l2')
            cT = max([c0, c1, c2, c3])
            sim.log.info(
                '                              Pressure RHS contributions:'
                '  %5.2f  %5.2f  %.2e    %5.2f  %5.2f    %.2e' %
                (c0 / cT, c1 / cT, div_err / cT, c2 / cT, c3 / cT, cT))

        # Inform PETSc about the pressure null space
        if self.remove_null_space:
            if self.pressure_null_space is None:
                # Create vector that spans the null space
                null_vec = dolfin.Vector(p_star.vector())
                null_vec[:] = 1
                null_vec *= 1 / null_vec.norm("l2")

                # Create null space basis object
                self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec])

            # Make sure the null space is set on the matrix
            if self.inner_iteration == 1:
                lhs.set_nullspace(self.pressure_null_space)

            # Orthogonalize b with respect to the null space
            self.pressure_null_space.orthogonalize(rhs)

        # Solve for the new pressure correction
        minus_p_hat.assign(p_star)
        self.niters_p = self.pressure_solver.inner_solve(
            lhs,
            p_star.vector(),
            rhs,
            in_iter=self.inner_iteration,
            co_iter=self.co_inner_iter)

        # Compute change from last iteration
        minus_p_hat.vector().axpy(-1.0, p_star.vector())
        minus_p_hat.vector().apply('insert')

        # Removing the null space of the matrix system is not strictly the same as removing
        # the null space of the equation, so we correct for this here
        if self.remove_null_space:
            dx2 = dolfin.dx(domain=p_star.function_space().mesh())
            vol = dolfin.assemble(dolfin.Constant(1) * dx2)
            pavg = dolfin.assemble(p_star * dx2) / vol
            p_star.vector()[:] -= pavg

        # Explicit relaxation
        if self.last_inner_iter and last_piso_iter:
            alpha = sim.input.get_value('solver/relaxation_p_last_iter',
                                        ALPHA_P_LAST, 'float')
        else:
            alpha = sim.input.get_value('solver/relaxation_p', ALPHA_P,
                                        'float')
        if alpha != 1:
            p_star.vector().axpy(1 - alpha, minus_p_hat.vector())
            p_star.vector().apply('insert')

        return minus_p_hat.vector().norm('l2'), div_err
Ejemplo n.º 7
0
    def pressure_correction(self, piso_rhs=False):
        """
        Solve the Navier-Stokes equations on SIMPLE form
        (Semi-Implicit Method for Pressure-Linked Equations)
        """
        sim = self.simulation
        p_hat = sim.data['p_hat']
        if self.solver_type == SOLVER_SIMPLE:
            alpha = sim.input.get_value('solver/relaxation_p', ALPHA_P,
                                        'float')
        else:
            alpha = 1.0

        # Compute the LHS = C⋅Ãinv⋅B
        if self.inner_iteration == 1:
            C, Ainv, B = self.C, self.A_tilde_inv, self.B
            self.mat_AinvB = matmul(Ainv, B, self.mat_AinvB)
            self.mat_CAinvB = matmul(C, self.mat_AinvB, self.mat_CAinvB)
            self.LHS_pressure = dolfin.as_backend_type(self.mat_CAinvB.copy())
        LHS = self.LHS_pressure

        # Compute the RHS
        if not piso_rhs:
            # Standard SIMPLE pressure correction
            # Compute the divergence of u* and the rest of the right hand side
            uvw_star = sim.data['uvw_star']
            RHS = self.matrices.assemble_E_star(uvw_star)
            self.niters_p = 0
        else:
            # PISO pressure correction (the second pressure correction)
            # Compute the RHS = - C⋅Ãinv⋅(Ãinv - A)⋅û
            C, Ainv, A = self.C, self.A_tilde_inv, self.A
            if self.inner_iteration == 1:
                self.mat_AinvA = matmul(Ainv, A, self.mat_AinvA)
                self.mat_CAinvA = matmul(C, self.mat_AinvA, self.mat_CAinvA)
            RHS = self.mat_CAinvA * self.minus_uvw_hat - C * self.minus_uvw_hat

        # Inform PETSc about the null space
        if self.remove_null_space:
            if self.pressure_null_space is None:
                # Create vector that spans the null space
                null_vec = dolfin.Vector(p_hat.vector())
                null_vec[:] = 1
                null_vec *= 1 / null_vec.norm("l2")

                # Create null space basis object
                self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec])

            # Make sure the null space is set on the matrix
            if self.inner_iteration == 1:
                LHS.set_nullspace(self.pressure_null_space)

            # Orthogonalize b with respect to the null space
            self.pressure_null_space.orthogonalize(RHS)

        # Solve for the new pressure correction
        self.niters_p += self.pressure_solver.inner_solve(
            LHS,
            p_hat.vector(),
            RHS,
            in_iter=self.inner_iteration,
            co_iter=self.co_inner_iter,
        )

        # Removing the null space of the matrix system is not strictly the same as removing
        # the null space of the equation, so we correct for this here
        if self.remove_null_space:
            dx2 = dolfin.dx(domain=p_hat.function_space().mesh())
            vol = dolfin.assemble(dolfin.Constant(1) * dx2)
            pavg = dolfin.assemble(p_hat * dx2) / vol
            p_hat.vector()[:] -= pavg

        # Calculate p = p* + α p^
        sim.data['p'].vector().axpy(alpha, p_hat.vector())
        sim.data['p'].vector().apply('insert')

        return p_hat.vector().norm('l2')
chi_ = df.Function(S)
psi = df.TestFunction(S)

ds = df.Measure("ds", domain=mesh, subdomain_data=subd)

F_chi = (n[0] * psi * ds(1) + df.inner(df.grad(chi), df.grad(psi)) * df.dx +
         Pe * psi * df.dot(U_, df.grad(chi)) * df.dx + Pe *
         (U_[0] - df.Constant(1.)) * psi * df.dx)

a_chi, L_chi = df.lhs(F_chi), df.rhs(F_chi)

solver_chi = df.PETScKrylovSolver("gmres")
nullvec = df.Vector(chi_.vector())
S.dofmap().set(nullvec, 1.0)
nullvec *= 1.0 / nullvec.norm("l2")
nullspace = df.VectorSpaceBasis([nullvec])

problem_chi2 = df.LinearVariationalProblem(a_chi, L_chi, chi_, bcs=[])
solver_chi2 = df.LinearVariationalSolver(problem_chi2)

solver_chi2.parameters["krylov_solver"]["absolute_tolerance"] = 1e-15

logPe_arr = np.linspace(args.logPe_min, args.logPe_max, args.logPe_N)

if rank == 0:
    data = np.zeros((len(logPe_arr), 3))

for iPe, logPe in enumerate(logPe_arr):
    Pe_loc = 10**logPe
    Pe.assign(Pe_loc)
Ejemplo n.º 9
0
    def pressure_correction(self):
        """
        Solve the Navier-Stokes equations on SIMPLE form
        (Semi-Implicit Method for Pressure-Linked Equations)
        """
        sim = self.simulation
        u_star = sim.data['uvw_star']
        p_star = sim.data['p']
        p_hat = self.simulation.data['p_hat']

        # Assemble only once per time step
        if self.inner_iteration == 1:
            self.C = assemble_into(self.eqC, self.C)
            self.Minv = dolfin.as_backend_type(self.compute_M_inverse())
            if self.eqE is not None:
                self.E = assemble_into(self.eqE, self.E)

            # Compute LHS
            self.MinvB = matmul(self.Minv, self.B, self.MinvB)
            self.CMinvB = matmul(self.C, self.MinvB, self.CMinvB)

        # The equation system
        lhs = self.CMinvB
        rhs = self.CMinvB * p_star.vector()
        rhs.axpy(1, self.C * u_star.vector())
        if self.eqE is not None:
            rhs.axpy(-1, self.E)
        rhs.apply('insert')

        # Inform PETSc about the pressure null space
        if self.remove_null_space:
            if self.pressure_null_space is None:
                # Create vector that spans the null space
                null_vec = dolfin.Vector(p_star.vector())
                null_vec[:] = 1
                null_vec *= 1 / null_vec.norm("l2")

                # Create null space basis object
                self.pressure_null_space = dolfin.VectorSpaceBasis([null_vec])

            # Make sure the null space is set on the matrix
            if self.inner_iteration == 1:
                lhs.set_nullspace(self.pressure_null_space)

            # Orthogonalize b with respect to the null space
            self.pressure_null_space.orthogonalize(rhs)

        # Temporarily store the old pressure
        p_hat.vector().zero()
        p_hat.vector().axpy(-1, p_star.vector())

        # Solve for the new pressure correction
        self.niters_p = self.pressure_solver.inner_solve(
            lhs,
            p_star.vector(),
            rhs,
            in_iter=self.inner_iteration,
            co_iter=self.co_inner_iter)

        # Removing the null space of the matrix system is not strictly the same as removing
        # the null space of the equation, so we correct for this here
        if self.remove_null_space:
            dx2 = dolfin.dx(domain=p_star.function_space().mesh())
            vol = dolfin.assemble(dolfin.Constant(1) * dx2)
            pavg = dolfin.assemble(p_star * dx2) / vol
            p_star.vector()[:] -= pavg

        # Calculate p_hat = p_new - p_old
        p_hat.vector().axpy(1, p_star.vector())

        return p_hat.vector().norm('l2')