示例#1
0
    def step(self, u1, u0, t, dt,
             tol=1.0e-10,
             maxiter=1000,
             verbose=True,
             krylov='gmres',
             preconditioner='ilu'
             ):
        L, b = self.problem.get_system(t + dt)

        A = self.M + dt * L

        v = TestFunction(self.problem.V)
        rhs = assemble(u0 * v * self.problem.dx_multiplier * self.problem.dx)
        rhs.axpy(dt, b)

        # Apply boundary conditions.
        for bc in self.problem.get_bcs(t + dt):
            bc.apply(A, rhs)

        solver = KrylovSolver(krylov, preconditioner)
        solver.parameters['relative_tolerance'] = tol
        solver.parameters['absolute_tolerance'] = 0.0
        solver.parameters['maximum_iterations'] = maxiter
        solver.parameters['monitor_convergence'] = verbose
        P = self.problem.get_preconditioner(t + dt)
        if P:
            solver.set_operators(A, self.M + dt * P)
        else:
            solver.set_operator(A)

        solver.solve(u1.vector(), rhs)
        return
def stokes_solve(
    up_out,
    mu,
    u_bcs, p_bcs,
    f,
    dx=dx,
    verbose=True,
    tol=1.0e-10,
    maxiter=1000
    ):
    # Some initial sanity checks.
    assert mu > 0.0

    WP = up_out.function_space()

    # Translate the boundary conditions into the product space.
    new_bcs = []
    for k, bcs in enumerate([u_bcs, p_bcs]):
        for bc in bcs:
            space = bc.function_space()
            C = space.component()
            if len(C) == 0:
                new_bcs.append(DirichletBC(WP.sub(k),
                                           bc.value(),
                                           bc.domain_args[0]))
            elif len(C) == 1:
                new_bcs.append(DirichletBC(WP.sub(k).sub(int(C[0])),
                                           bc.value(),
                                           bc.domain_args[0]))
            else:
                raise RuntimeError('Illegal number of subspace components.')

    # TODO define p*=-1 and reverse sign in the end to get symmetric system?

    # Define variational problem
    (u, p) = TrialFunctions(WP)
    (v, q) = TestFunctions(WP)

    r = Expression('x[0]', degree=1, domain=WP.mesh())

    print("mu = %e" % mu)

    # build system
    a = mu * inner(r * grad(u), grad(v)) * 2 * pi * dx \
        - ((r * v[0]).dx(0) + (r * v[1]).dx(1)) * p * 2 * pi * dx \
        + ((r * u[0]).dx(0) + (r * u[1]).dx(1)) * q * 2 * pi * dx
      #- div(r*v)*p* 2*pi*dx \
      #+ q*div(r*u)* 2*pi*dx
    L = inner(f, v) * 2 * pi * r * dx

    A, b = assemble_system(a, L, new_bcs)

    mode = 'lu'

    if mode == 'lu':
        solve(A, up_out.vector(), b, 'lu')

    elif mode == 'gmres':
        # For preconditioners for the Stokes system, see
        #
        #     Fast iterative solvers for discrete Stokes equations;
        #     J. Peters, V. Reichelt, A. Reusken.
        #
        prec = mu * inner(r * grad(u), grad(v)) * 2 * pi * dx \
            - p * q * 2 * pi * r * dx
        P, btmp = assemble_system(prec, L, new_bcs)
        solver = KrylovSolver('tfqmr', 'amg')
        #solver = KrylovSolver('gmres', 'amg')
        solver.set_operators(A, P)

        solver.parameters['monitor_convergence'] = verbose
        solver.parameters['report'] = verbose
        solver.parameters['absolute_tolerance'] = 0.0
        solver.parameters['relative_tolerance'] = tol
        solver.parameters['maximum_iterations'] = maxiter

        # Solve
        solver.solve(up_out.vector(), b)
    elif mode == 'fieldsplit':
        raise NotImplementedError('Fieldsplit solver not yet implemented.')
        # For an assortment of preconditioners, see
        #
        #     Performance and analysis of saddle point preconditioners
        #     for the discrete steady-state Navier-Stokes equations;
        #     H.C. Elman, D.J. Silvester, A.J. Wathen;
        #     Numer. Math. (2002) 90: 665-688;
        #     <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>.
        #
        # Set up field split.
        W = SubSpace(WP, 0)
        P = SubSpace(WP, 1)
        u_dofs = W.dofmap().dofs()
        p_dofs = P.dofmap().dofs()
        prec = PETScPreconditioner()
        prec.set_fieldsplit([u_dofs, p_dofs], ['u', 'p'])

        PETScOptions.set('pc_type', 'fieldsplit')
        PETScOptions.set('pc_fieldsplit_type', 'additive')
        PETScOptions.set('fieldsplit_u_pc_type', 'lu')
        PETScOptions.set('fieldsplit_p_pc_type', 'jacobi')

        # Create Krylov solver with custom preconditioner.
        solver = PETScKrylovSolver('gmres', prec)
        solver.set_operator(A)

    return
def solve_maxwell(V, dx,
                  Mu, Sigma,  # dictionaries
                  omega,
                  f_list,  # list of dictionaries
                  convections,  # dictionary
                  bcs=None,
                  tol=1.0e-12,
                  compute_residuals=True,
                  verbose=False
                  ):
    '''Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical
    coordinates.

    :param V: function space for potentials
    :param dx: measure
    :param omega: current frequency
    :type omega: float
    :param f_list: list of right-hand sides
    :param convections: convection terms by subdomains
    :type convections: dictionary
    :param bcs: Dirichlet boundary conditions
    :param tol: solver tolerance
    :type tol: float
    :param verbose: solver verbosity
    :type verbose: boolean
    :rtype: list of functions
    '''
    # For the exact solution of the magnetic scalar potential, see
    # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>.
    # Here, the value of \phi along the rotational axis is specified as
    #
    #    phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2))
    #
    # where 'a' is the radius of the coil. This expression contradicts what is
    # specified by [Chaboudez97]_ who claim that phi=0 is the natural value
    # at the symmetry axis.
    #
    # For more analytic expressions, see
    #
    #     Simple Analytic Expressions for the Magnetic Field of a Circular
    #     Current Loop;
    #     James Simpson, John Lane, Christopher Immer, and Robert Youngquist;
    #     <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>.
    #

    # Check if boundary conditions on phi are explicitly provided.
    if not bcs:
        # Create Dirichlet boundary conditions.
        # In the cylindrically symmetric formulation, the magnetic vector
        # potential is given by
        #
        #    A = e^{i omega t} phi(r,z) e_{theta}.
        #
        # It is natural to demand phi=0 along the symmetry axis r=0 to avoid
        # discontinuities there.
        # Also, this makes sure that the system is well-defined (see comment
        # below).
        #
        def xzero(x, on_boundary):
            return on_boundary and abs(x[0]) < DOLFIN_EPS
        bcs = DirichletBC(V * V, (0.0, 0.0), xzero)
        #
        # Concerning the boundary conditions for the rest of the system:
        # At the other boundaries, it is not uncommon (?) to set so-called
        # impedance boundary conditions; see, e.g.,
        #
        #    Chaboudez et al.,
        #    Numerical Modeling in Induction Heating for Axisymmetric
        #    Geometries,
        #    IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997,
        #    <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>.
        #
        # or
        #
        #    <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>.
        #
        # TODO review those, references don't seem to be too accurate
        # Those translate into Robin-type boundary conditions (and are in fact
        # sometimes called that, cf.
        # https://en.wikipedia.org/wiki/Robin_boundary_condition).
        # The classical reference is
        #
        #     Impedance boundary conditions for imperfectly conducting
        #     surfaces,
        #     T.B.A. Senior,
        #     <http://link.springer.com/content/pdf/10.1007/BF02920074>.
        #
        #class OuterBoundary(SubDomain):
        #    def inside(self, x, on_boundary):
        #        return on_boundary and abs(x[0]) > DOLFIN_EPS
        #boundaries = FacetFunction('size_t', mesh)
        #boundaries.set_all(0)
        #outer_boundary = OuterBoundary()
        #outer_boundary.mark(boundaries, 1)
        #ds = Measure('ds')[boundaries]
        ##n = FacetNormal(mesh)
        ##a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \
        ##     - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1)
        ##L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \
        ##     - 1.0/Mu[i] * 1.0 * vi * ds(1)
        ## This is -n.grad(r u) = u:
        #a += 1.0/Mu[i] * ur * vr * ds(1) \
        #   + 1.0/Mu[i] * ui * vi * ds(1)

    # Create the system matrix, preconditioner, and the right-hand sides.
    # For preconditioners, there are two approaches. The first one, described
    # in
    #
    #     Algebraic Multigrid for Complex Symmetric Systems;
    #     D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer;
    #     <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730>
    #
    # doesn't work too well here.
    # The matrix P, created in _build_system(), provides a better alternative.
    # For more details, see documentation in _build_system().
    #
    A, P, b_list, M, W = _build_system(V, dx,
                                       Mu, Sigma,  # dictionaries
                                       omega,
                                       f_list,  # list of dicts
                                       convections,  # dict
                                       bcs
                                       )

    #from matplotlib import pyplot as pp
    #rows, cols, values = M.data()
    #from scipy.sparse import csr_matrix
    #M_matrix = csr_matrix((values, cols, rows))
    ##from matplotlib import pyplot as pp
    ###pp.spy(M_matrix, precision=1e-3, marker='.', markersize=5)
    ##pp.spy(M_matrix)
    ##pp.show()
    ## colormap
    #cmap = pp.cm.gray_r
    #M_dense = M_matrix.todense()
    #from matplotlib.colors import LogNorm
    #im = pp.imshow(abs(M_dense), cmap=cmap, interpolation='nearest', norm=LogNorm())
    ##im = pp.imshow(abs(M_dense), cmap=cmap, interpolation='nearest')
    ##im = pp.imshow(abs(A_r), cmap=cmap, interpolation='nearest')
    ##im = pp.imshow(abs(A_i), cmap=cmap, interpolation='nearest')
    #pp.colorbar()
    #pp.show()
    #exit()
    #print A
    #rows, cols, values = A.data()
    #from scipy.sparse import csr_matrix
    #A_matrix = csr_matrix((values, cols, rows))

    ###pp.spy(A_matrix, precision=1e-3, marker='.', markersize=5)
    ##pp.spy(A_matrix)
    ##pp.show()

    ## colormap
    #cmap = pp.cm.gray_r
    #A_dense = A_matrix.todense()
    ##A_r = A_dense[0::2][0::2]
    ##A_i = A_dense[1::2][0::2]
    #cmap.set_bad('r')
    ##im = pp.imshow(abs(A_dense), cmap=cmap, interpolation='nearest', norm=LogNorm())
    #im = pp.imshow(abs(A_dense), cmap=cmap, interpolation='nearest')
    ##im = pp.imshow(abs(A_r), cmap=cmap, interpolation='nearest')
    ##im = pp.imshow(abs(A_i), cmap=cmap, interpolation='nearest')
    #pp.colorbar()
    #pp.show()

    # prepare solver
    solver = KrylovSolver('gmres', 'amg')
    solver.set_operators(A, P)

    # The PDE for A has huge coefficients (order 10^8) all over. Hence, if
    # relative residual is set to 10^-6, the actual residual will still be of
    # the order 10^2. While this isn't too bad (after all the equations are
    # upscaled by a large factor), one can choose a very small relative
    # tolerance here to get a visually pleasing residual norm.
    solver.parameters['relative_tolerance'] = 1.0e-12
    solver.parameters['absolute_tolerance'] = 0.0
    solver.parameters['maximum_iterations'] = 100
    solver.parameters['report'] = verbose
    solver.parameters['monitor_convergence'] = verbose

    phi_list = []
    for k, b in enumerate(b_list):
        with Message('Computing coil ring %d/%d...' % (k + 1, len(b_list))):
            # Define goal functional for adaptivity.
            # Adaptivity not working for subdomains, cf.
            # https://bugs.launchpad.net/dolfin/+bug/872105.
            #(phi_r, phi_i) = split(phi)
            #M = (phi_r*phi_r + phi_i*phi_i) * dx(2)
            phi_list.append(Function(W))
            phi_list[-1].rename('phi%d' % k, 'phi%d' % k)
            solver.solve(phi_list[-1].vector(), b)

        ## Adaptive mesh refinement.
        #_adaptive_mesh_refinement(dx,
        #                          phi_list[-1],
        #                          Mu, Sigma, omega,
        #                          convections,
        #                          f_list[k]
        #                          )
        #exit()

        if compute_residuals:
            # Sanity check: Compute residuals.
            # This is quite the good test that we haven't messed up
            # real/imaginary in the above formulation.
            r_r, r_i = _build_residuals(V, dx, phi_list[-1],
                                        omega, Mu, Sigma,
                                        convections, voltages
                                        )

            def xzero(x, on_boundary):
                return on_boundary and abs(x[0]) < DOLFIN_EPS

            subdomain_indices = Mu.keys()

            # Solve an FEM problem to get the corresponding residual function
            # out.
            # This is exactly what we need here! :)
            u = TrialFunction(V)
            v = TestFunction(V)
            a = zero() * dx(0)
            for i in subdomain_indices:
                a += u * v * dx(i)

            # TODO don't hard code the boundary conditions like this
            R_r = Function(V)
            solve(a == r_r, R_r,
                  bcs=DirichletBC(V, 0.0, xzero)
                  )

            # TODO don't hard code the boundary conditions like this
            R_i = Function(V)
            solve(a == r_i, R_i,
                  bcs=DirichletBC(V, 0.0, xzero)
                  )

            nrm_r = norm(R_r)
            info('||r_r|| = %e' % nrm_r)
            nrm_i = norm(R_i)
            info('||r_i|| = %e' % nrm_i)
            res_norm = sqrt(nrm_r * nrm_r + nrm_i * nrm_i)
            info('||r|| = %e' % res_norm)

            plot(R_r, title='R_r')
            plot(R_i, title='R_i')
            interactive()
            #exit()
    return phi_list
示例#4
0
def solve(
    V,
    dx,
    Mu,
    Sigma,  # dictionaries
    omega,
    f_list,  # list of dictionaries
    convections,  # dictionary
    f_degree=None,
    bcs=None,
    tol=1.0e-12,
    verbose=False,
):
    """Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical
    coordinates

    .. math::
         \\div\\left(\\frac{1}{\\mu r} \\nabla(r\\phi)\\right)
         + \\left\\langle u, \\frac{1}{r} \\nabla(r\\phi)\\right\\rangle
         + i \\sigma \\omega \\phi
            = f

    with finite elements.

    :param V: function space for potentials

    :param dx: measure

    :param Mu: mu per subdomain
    :type Mu: dictionary

    :param Sigma: sigma per subdomain
    :type Sigma: dictionary

    :param omega: current frequency
    :type omega: float

    :param f_list: list of right-hand sides for each of which a solution will
                   be computed

    :param convections: convection, defined per subdomain
    :type convections: dictionary

    :param bcs: Dirichlet boundary conditions

    :param tol: relative solver tolerance
    :type tol: float

    :param verbose: solver verbosity
    :type verbose: boolean

    :rtype: list of functions
    """
    # For the exact solution of the magnetic scalar potential, see
    # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>.
    # Here, the value of \\phi along the rotational axis is specified as
    #
    #    phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2))
    #
    # where 'a' is the radius of the coil. This expression contradicts what is
    # specified by [Chaboudez97]_ who claim that phi=0 is the natural value
    # at the symmetry axis.
    #
    # For more analytic expressions, see
    #
    #     Simple Analytic Expressions for the Magnetic Field of a Circular
    #     Current Loop;
    #     James Simpson, John Lane, Christopher Immer, and Robert Youngquist;
    #     <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>.
    #

    # Check if boundary conditions on phi are explicitly provided.
    if not bcs:
        # Create Dirichlet boundary conditions.
        # In the cylindrically symmetric formulation, the magnetic vector
        # potential is given by
        #
        #    A = e^{i omega t} phi(r,z) e_{theta}.
        #
        # It is natural to demand phi=0 along the symmetry axis r=0 to avoid
        # discontinuities there.
        # Also, this makes sure that the system is well-defined (see comment
        # below).
        #
        def xzero(x, on_boundary):
            return on_boundary and abs(x[0]) < DOLFIN_EPS

        ee = V.ufl_element() * V.ufl_element()
        VV = FunctionSpace(V.mesh(), ee)
        bcs = DirichletBC(VV, (0.0, 0.0), xzero)
        #
        # Concerning the boundary conditions for the rest of the system:
        # At the other boundaries, it is not uncommon (?) to set so-called
        # impedance boundary conditions; see, e.g.,
        #
        # Chaboudez et al.,
        # Numerical Modeling in Induction Heating for Axisymmetric
        # Geometries,
        # IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997,
        # <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>.
        #
        # or
        #
        # <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>.
        #
        # TODO review those, references don't seem to be too accurate
        # Those translate into Robin-type boundary conditions (and are in fact
        # sometimes called that, cf.
        # https://en.wikipedia.org/wiki/Robin_boundary_condition).
        # The classical reference is
        #
        #   Impedance boundary conditions for imperfectly conducting surfaces,
        #   T.B.A. Senior,
        #   <http://link.springer.com/content/pdf/10.1007/BF02920074>.
        #
        # class OuterBoundary(SubDomain):
        #     def inside(self, x, on_boundary):
        #         return on_boundary and abs(x[0]) > DOLFIN_EPS
        # boundaries = MeshFunction(
        #     'size_t', mesh,
        #     mesh.topology().dim() - 1
        #     )
        # boundaries.set_all(0)
        # outer_boundary = OuterBoundary()
        # outer_boundary.mark(boundaries, 1)
        # ds = Measure('ds')[boundaries]
        # #n = FacetNormal(mesh)
        # #a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \
        # #     - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1)
        # #L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \
        # #     - 1.0/Mu[i] * 1.0 * vi * ds(1)
        # # This is -n.grad(r u) = u:
        # a += 1.0/Mu[i] * ur * vr * ds(1) \
        #    + 1.0/Mu[i] * ui * vi * ds(1)

    # Create the system matrix, preconditioner, and the right-hand sides.
    # For preconditioners, there are two approaches. The first one, described
    # in
    #
    #     Algebraic Multigrid for Complex Symmetric Systems;
    #     D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer;
    #     <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730>
    #
    # doesn't work too well here.
    # The matrix P, created in build_system(), provides a better alternative.
    # For more details, see documentation in build_system().
    #
    A, P, b_list, _, W = build_system(
        V, dx, Mu, Sigma, omega, f_list, f_degree, convections, bcs
    )

    # prepare solver
    # Don't use 'amg', since that defaults to `ml_amg` if available which
    # crashes
    # <https://bitbucket.org/fenics-project/docker/issues/61/petsc-vectorfunctionspace-amg-malloc>.
    solver = KrylovSolver("gmres", "hypre_amg")
    solver.set_operators(A, P)

    # The PDE for A has huge coefficients (order 10^8) all over. Hence, if
    # relative residual is set to 10^-6, the actual residual will still be of
    # the order 10^2. While this isn't too bad (after all the equations are
    # upscaled by a large factor), one can choose a very small relative
    # tolerance here to get a visually pleasing residual norm.
    solver.parameters["relative_tolerance"] = tol
    solver.parameters["absolute_tolerance"] = 0.0
    solver.parameters["maximum_iterations"] = 100
    solver.parameters["report"] = verbose
    solver.parameters["monitor_convergence"] = verbose

    phi_list = []
    for k, b in enumerate(b_list):
        phi_list.append(Function(W))
        phi_list[-1].rename("phi{}".format(k), "phi{}".format(k))
        solver.solve(phi_list[-1].vector(), b)

    return phi_list
    def ab2tr_step0(u0,
                    P,
                    f,  # right-hand side
                    rho,
                    mu,
                    dudt_bcs=[],
                    p_bcs=[],
                    eps=1.0e-4,  # relative error tolerance
                    verbose=True
                    ):
        # Make sure that the initial velocity is divergence-free.
        alpha = norm(u0, 'Hdiv0')
        if abs(alpha) > DOLFIN_EPS:
            warn('Initial velocity not divergence-free (||u||_div = %e).'
                 % alpha
                 )
        # Get the initial u0' and p0 by solving the linear equation system
        #
        #     [M   C] [u0']   [f0 - (K+N(u0)u0)]
        #     [C^T 0] [p0 ] = [ g0'            ],
        #
        # i.e.,
        #
        #     rho u0' + nabla(p0) = f0 + mu\Delta(u0) - rho u0.nabla(u0),
        #     div(u0')            = 0.
        #
        W = u0.function_space()
        WP = W*P

        # Translate the boundary conditions into product space. See
        # <http://fenicsproject.org/qa/703/boundary-conditions-in-product-space>.
        dudt_bcs_new = []
        for dudt_bc in dudt_bcs:
            dudt_bcs_new.append(DirichletBC(WP.sub(0),
                                            dudt_bc.value(),
                                            dudt_bc.user_sub_domain()))
        p_bcs_new = []
        for p_bc in p_bcs:
            p_bcs_new.append(DirichletBC(WP.sub(1),
                                         p_bc.value(),
                                         p_bc.user_sub_domain()))

        new_bcs = dudt_bcs_new + p_bcs_new

        (u, p) = TrialFunctions(WP)
        (v, q) = TestFunctions(WP)

        #a = rho * dot(u, v) * dx + dot(grad(p), v) * dx \
        a = rho * inner(u, v) * dx - p * div(v) * dx \
            - div(u) * q * dx
        L = _rhs_weak(u0, v, f, rho, mu)

        A, b = assemble_system(a, L, new_bcs)

        # Similar preconditioner as for the Stokes problem.
        # TODO implement something better!
        prec = rho * inner(u, v) * dx \
            - p*q*dx
        M, _ = assemble_system(prec, L, new_bcs)

        solver = KrylovSolver('gmres', 'amg')

        solver.parameters['monitor_convergence'] = verbose
        solver.parameters['report'] = verbose
        solver.parameters['absolute_tolerance'] = 0.0
        solver.parameters['relative_tolerance'] = 1.0e-6
        solver.parameters['maximum_iterations'] = 10000

        # Associate operator (A) and preconditioner matrix (M)
        solver.set_operators(A, M)
        #solver.set_operator(A)

        # Solve
        up = Function(WP)
        solver.solve(up.vector(), b)

        # Get sub-functions
        dudt0, p0 = up.split()

        # Choosing the first step size for the trapezoidal rule can be tricky.
        # Chapters 2.7.4a, 2.7.4e of the book
        #
        #     Incompressible flow and the finite element method,
        #     volume 1: advection-diffusion;
        #     P.M. Gresho, R.L. Sani,
        #
        # give some hints.
        #
        #     eps ... relative error tolerance
        #     tau ... estimate of the initial 'time constant'
        tau = None
        if tau:
            dt0 = tau * eps**(1.0/3.0)
        else:
            # Choose something 'reasonably small'.
            dt0 = 1.0e-3
        # Alternative:
        # Use a dissipative scheme like backward Euler or BDF2 for the first
        # couple of steps. This makes sure that noisy initial data is damped
        # out.
        return dudt0, p0, dt0
示例#6
0
def solve(
    V,
    dx,
    Mu,
    Sigma,  # dictionaries
    omega,
    f_list,  # list of dictionaries
    convections,  # dictionary
    f_degree=None,
    bcs=None,
    tol=1.0e-12,
    verbose=False,
):
    """Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical
    coordinates

    .. math::
         \\div\\left(\\frac{1}{\\mu r} \\nabla(r\\phi)\\right)
         + \\left\\langle u, \\frac{1}{r} \\nabla(r\\phi)\\right\\rangle
         + i \\sigma \\omega \\phi
            = f

    with finite elements.

    :param V: function space for potentials

    :param dx: measure

    :param Mu: mu per subdomain
    :type Mu: dictionary

    :param Sigma: sigma per subdomain
    :type Sigma: dictionary

    :param omega: current frequency
    :type omega: float

    :param f_list: list of right-hand sides for each of which a solution will
                   be computed

    :param convections: convection, defined per subdomain
    :type convections: dictionary

    :param bcs: Dirichlet boundary conditions

    :param tol: relative solver tolerance
    :type tol: float

    :param verbose: solver verbosity
    :type verbose: boolean

    :rtype: list of functions
    """
    # For the exact solution of the magnetic scalar potential, see
    # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>.
    # Here, the value of \\phi along the rotational axis is specified as
    #
    #    phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2))
    #
    # where 'a' is the radius of the coil. This expression contradicts what is
    # specified by [Chaboudez97]_ who claim that phi=0 is the natural value
    # at the symmetry axis.
    #
    # For more analytic expressions, see
    #
    #     Simple Analytic Expressions for the Magnetic Field of a Circular
    #     Current Loop;
    #     James Simpson, John Lane, Christopher Immer, and Robert Youngquist;
    #     <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>.
    #

    # Check if boundary conditions on phi are explicitly provided.
    if not bcs:
        # Create Dirichlet boundary conditions.
        # In the cylindrically symmetric formulation, the magnetic vector
        # potential is given by
        #
        #    A = e^{i omega t} phi(r,z) e_{theta}.
        #
        # It is natural to demand phi=0 along the symmetry axis r=0 to avoid
        # discontinuities there.
        # Also, this makes sure that the system is well-defined (see comment
        # below).
        #
        def xzero(x, on_boundary):
            return on_boundary and abs(x[0]) < DOLFIN_EPS

        ee = V.ufl_element() * V.ufl_element()
        VV = FunctionSpace(V.mesh(), ee)
        bcs = DirichletBC(VV, (0.0, 0.0), xzero)
        #
        # Concerning the boundary conditions for the rest of the system:
        # At the other boundaries, it is not uncommon (?) to set so-called
        # impedance boundary conditions; see, e.g.,
        #
        # Chaboudez et al.,
        # Numerical Modeling in Induction Heating for Axisymmetric
        # Geometries,
        # IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997,
        # <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>.
        #
        # or
        #
        # <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>.
        #
        # TODO review those, references don't seem to be too accurate
        # Those translate into Robin-type boundary conditions (and are in fact
        # sometimes called that, cf.
        # https://en.wikipedia.org/wiki/Robin_boundary_condition).
        # The classical reference is
        #
        #   Impedance boundary conditions for imperfectly conducting surfaces,
        #   T.B.A. Senior,
        #   <http://link.springer.com/content/pdf/10.1007/BF02920074>.
        #
        # class OuterBoundary(SubDomain):
        #     def inside(self, x, on_boundary):
        #         return on_boundary and abs(x[0]) > DOLFIN_EPS
        # boundaries = MeshFunction(
        #     'size_t', mesh,
        #     mesh.topology().dim() - 1
        #     )
        # boundaries.set_all(0)
        # outer_boundary = OuterBoundary()
        # outer_boundary.mark(boundaries, 1)
        # ds = Measure('ds')[boundaries]
        # #n = FacetNormal(mesh)
        # #a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \
        # #     - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1)
        # #L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \
        # #     - 1.0/Mu[i] * 1.0 * vi * ds(1)
        # # This is -n.grad(r u) = u:
        # a += 1.0/Mu[i] * ur * vr * ds(1) \
        #    + 1.0/Mu[i] * ui * vi * ds(1)

    # Create the system matrix, preconditioner, and the right-hand sides.
    # For preconditioners, there are two approaches. The first one, described
    # in
    #
    #     Algebraic Multigrid for Complex Symmetric Systems;
    #     D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer;
    #     <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730>
    #
    # doesn't work too well here.
    # The matrix P, created in build_system(), provides a better alternative.
    # For more details, see documentation in build_system().
    #
    A, P, b_list, _, W = build_system(V, dx, Mu, Sigma, omega, f_list,
                                      f_degree, convections, bcs)

    # prepare solver
    # Don't use 'amg', since that defaults to `ml_amg` if available which
    # crashes
    # <https://bitbucket.org/fenics-project/docker/issues/61/petsc-vectorfunctionspace-amg-malloc>.
    solver = KrylovSolver("gmres", "hypre_amg")
    solver.set_operators(A, P)

    # The PDE for A has huge coefficients (order 10^8) all over. Hence, if
    # relative residual is set to 10^-6, the actual residual will still be of
    # the order 10^2. While this isn't too bad (after all the equations are
    # upscaled by a large factor), one can choose a very small relative
    # tolerance here to get a visually pleasing residual norm.
    solver.parameters["relative_tolerance"] = tol
    solver.parameters["absolute_tolerance"] = 0.0
    solver.parameters["maximum_iterations"] = 100
    solver.parameters["report"] = verbose
    solver.parameters["monitor_convergence"] = verbose

    phi_list = []
    for k, b in enumerate(b_list):
        phi_list.append(Function(W))
        phi_list[-1].rename("phi{}".format(k), "phi{}".format(k))
        solver.solve(phi_list[-1].vector(), b)

    return phi_list
示例#7
0
def solve(WP, bcs, mu, f, verbose=True, tol=1.0e-13, max_iter=500):
    # Some initial sanity checks.
    assert mu > 0.0

    # Define variational problem
    (u, p) = TrialFunctions(WP)
    (v, q) = TestFunctions(WP)

    # Build system.
    # The sign of the div(u)-term is somewhat arbitrary since the right-hand
    # side is 0 here. We can either make the system symmetric or positive-
    # definite.
    # On a second note, we have
    #
    #    \int grad(p).v = - \int p * div(v) + \int_\Gamma p n.v.
    #
    # Since, we have either p=0 or n.v=0 on the boundary, we could as well
    # replace the term dot(grad(p), v) by -p*div(v).
    #
    a = mu * inner(grad(u), grad(v))*dx \
        - p * div(v) * dx \
        - q * div(u) * dx
    # a = mu * inner(grad(u), grad(v))*dx + dot(grad(p), v) * dx \
    #  - div(u) * q * dx
    L = inner(f, v) * dx
    A, b = assemble_system(a, L, bcs)

    # Use the preconditioner as recommended in
    # <http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html>,
    #
    #     prec = inner(grad(u), grad(v))*dx - p*q*dx
    #
    # although it doesn't seem to be too efficient.
    # The sign on the last term doesn't matter.
    prec = mu * inner(grad(u), grad(v))*dx \
        - p*q*dx
    M, _ = assemble_system(prec, L, bcs)
    # solver = KrylovSolver('tfqmr', 'hypre_amg')
    solver = KrylovSolver('gmres', 'hypre_amg')
    solver.set_operators(A, M)

    # For an assortment of preconditioners, see
    #
    #     Performance and analysis of saddle point preconditioners
    #     for the discrete steady-state Navier-Stokes equations;
    #     H.C. Elman, D.J. Silvester, A.J. Wathen;
    #     Numer. Math. (2002) 90: 665-688;
    #     <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>.
    #
    # Set up field split.
    # <https://fenicsproject.org/qa/12856/fieldsplit-petscpreconditioner_set_fieldsplit-arguments>
    # PETScOptions.set('ksp_view')
    # PETScOptions.set('ksp_monitor_true_residual')
    # PETScOptions.set('pc_type', 'fieldsplit')
    # PETScOptions.set('pc_fieldsplit_type', 'additive')
    # PETScOptions.set('pc_fieldsplit_detect_saddle_point')
    # PETScOptions.set('fieldsplit_0_ksp_type', 'preonly')
    # PETScOptions.set('fieldsplit_0_pc_type', 'lu')
    # PETScOptions.set('fieldsplit_1_ksp_type', 'preonly')
    # PETScOptions.set('fieldsplit_1_pc_type', 'jacobi')

    # solver = PETScKrylovSolver('gmres')
    # solver.set_operator(A)
    # solver.set_from_options()

    # http://scicomp.stackexchange.com/questions/7288/which-preconditioners-and-solver-in-petsc-for-indefinite-symmetric-systems-sho
    # PETScOptions.set('pc_type', 'fieldsplit')
    # #PETScOptions.set('pc_fieldsplit_type', 'schur')
    # #PETScOptions.set('pc_fieldsplit_schur_fact_type', 'upper')
    # PETScOptions.set('pc_fieldsplit_detect_saddle_point')
    # #PETScOptions.set('fieldsplit_u_pc_type', 'lsc')
    # #PETScOptions.set('fieldsplit_u_ksp_type', 'preonly')

    # PETScOptions.set('pc_type', 'fieldsplit')
    # PETScOptions.set('fieldsplit_u_pc_type', 'hypre')
    # PETScOptions.set('fieldsplit_u_ksp_type', 'preonly')
    # PETScOptions.set('fieldsplit_p_pc_type', 'jacobi')
    # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly')

    # # From PETSc/src/ksp/ksp/examples/tutorials/ex42-fsschur.opts:
    # PETScOptions.set('pc_type', 'fieldsplit')
    # PETScOptions.set('pc_fieldsplit_type', 'SCHUR')
    # PETScOptions.set('pc_fieldsplit_schur_fact_type', 'UPPER')
    # PETScOptions.set('fieldsplit_p_ksp_type', 'preonly')
    # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi')

    # From
    #
    # Composable Linear Solvers for Multiphysics;
    # J. Brown, M. Knepley, D.A. May, L.C. McInnes, B. Smith;
    # <http://www.computer.org/csdl/proceedings/ispdc/2012/4805/00/4805a055-abs.html>;
    # <http://www.mcs.anl.gov/uploads/cels/papers/P2017-0112.pdf>.
    #
    # PETScOptions.set('pc_type', 'fieldsplit')
    # PETScOptions.set('pc_fieldsplit_type', 'schur')
    # PETScOptions.set('pc_fieldsplit_schur_factorization_type', 'upper')
    # #
    # PETScOptions.set('fieldsplit_u_ksp_type', 'cg')
    # PETScOptions.set('fieldsplit_u_ksp_rtol', 1.0e-6)
    # PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi')
    # PETScOptions.set('fieldsplit_u_sub_pc_type', 'cholesky')
    # #
    # PETScOptions.set('fieldsplit_p_ksp_type', 'fgmres')
    # PETScOptions.set('fieldsplit_p_ksp_constant_null_space')
    # PETScOptions.set('fieldsplit_p_pc_type', 'lsc')
    # #
    # PETScOptions.set('fieldsplit_p_lsc_ksp_type', 'cg')
    # PETScOptions.set('fieldsplit_p_lsc_ksp_rtol', 1.0e-2)
    # PETScOptions.set('fieldsplit_p_lsc_ksp_constant_null_space')
    # #PETScOptions.set('fieldsplit_p_lsc_ksp_converged_reason')
    # PETScOptions.set('fieldsplit_p_lsc_pc_type', 'bjacobi')
    # PETScOptions.set('fieldsplit_p_lsc_sub_pc_type', 'icc')

    solver.parameters['monitor_convergence'] = verbose
    solver.parameters['report'] = verbose
    solver.parameters['absolute_tolerance'] = 0.0
    solver.parameters['relative_tolerance'] = tol
    solver.parameters['maximum_iterations'] = max_iter
    solver.parameters['error_on_nonconvergence'] = True

    # Solve
    up = Function(WP)
    solver.solve(up.vector(), b)

    # Get sub-functions
    u, p = up.split(True)

    return u, p