Exemplo n.º 1
0
class StreamFunction(Field):
    def before_first_compute(self, get):
        u = get("Velocity")
        assert len(u) == 2, "Can only compute stream function for 2D problems"
        V = u.function_space()
        spaces = SpacePool(V.mesh())
        degree = V.ufl_element().degree()
        V = spaces.get_space(degree, 0)

        psi = TrialFunction(V)
        self.q = TestFunction(V)
        a = dot(grad(psi), grad(self.q)) * dx()

        self.bc = DirichletBC(V, Constant(0), DomainBoundary())
        self.A = assemble(a)
        self.L = Vector()
        self.bc.apply(self.A)
        self.solver = KrylovSolver(self.A, "cg")
        self.psi = Function(V)

    def compute(self, get):
        u = get("Velocity")
        assemble(dot(u[1].dx(0) - u[0].dx(1), self.q) * dx(), tensor=self.L)
        self.bc.apply(self.L)

        self.solver.solve(self.psi.vector(), self.L)
        #solve(self.A, self.psi.vector(), self.L)

        return self.psi
Exemplo n.º 2
0
def les_setup(u_, mesh, KineticEnergySGS, assemble_matrix, CG1Function,
              nut_krylov_solver, bcs, **NS_namespace):
    """
    Set up for solving the Kinetic Energy SGS-model.
    """
    DG = FunctionSpace(mesh, "DG", 0)
    CG1 = FunctionSpace(mesh, "CG", 1)
    dim = mesh.geometry().dim()
    delta = Function(DG)
    delta.vector().zero()
    delta.vector().axpy(1.0, assemble(TestFunction(DG) * dx))
    delta.vector().set_local(delta.vector().array()**(1. / dim))
    delta.vector().apply('insert')

    Ck = KineticEnergySGS["Ck"]
    ksgs = interpolate(Constant(1E-7), CG1)
    bc_ksgs = DirichletBC(CG1, 0, "on_boundary")
    A_mass = assemble_matrix(TrialFunction(CG1) * TestFunction(CG1) * dx)
    nut_form = Ck * delta * sqrt(ksgs)
    bcs_nut = derived_bcs(CG1, bcs['u0'], u_)
    nut_ = CG1Function(nut_form,
                       mesh,
                       method=nut_krylov_solver,
                       bcs=bcs_nut,
                       bounded=True,
                       name="nut")
    At = Matrix()
    bt = Vector(nut_.vector())
    ksgs_sol = KrylovSolver("bicgstab", "additive_schwarz")
    #ksgs_sol.parameters["preconditioner"]["structure"] = "same_nonzero_pattern"
    ksgs_sol.parameters["error_on_nonconvergence"] = False
    ksgs_sol.parameters["monitor_convergence"] = False
    ksgs_sol.parameters["report"] = False
    del NS_namespace
    return locals()
Exemplo n.º 3
0
        def _change_degree(self, degree, *args, **kwargs):
            gc.collect()

            with self.global_preprocessing_time:
                logger.debug('Creating function space...')
                self._V = FunctionSpace(self._mesh, "CG", degree)
                logger.debug('Done.  Creating integration subdomains...')
                self.create_integration_subdomains()
                logger.debug('Done.  Creating test function...')
                self._v = TestFunction(self._V)
                logger.debug('Done.  Creating potential function...')
                self._potential_function = Function(self._V)
                logger.debug('Done.  Creating trial function...')
                self._potential_trial = TrialFunction(self._V)
                logger.debug('Done.  Creating LHS part of equation...')
                self._a = self._lhs()
                logger.debug('Done.  Assembling linear equation matrix...')
                self._terms_with_unknown = assemble(self._a)
                logger.debug('Done.  Defining boundary condition...')
                self._dirichlet_bc = self._boundary_condition(*args, **kwargs)
                logger.debug('Done.  Applying boundary condition to the matrix...')
                self._dirichlet_bc.apply(self._terms_with_unknown)

                logger.debug('Done.  Creating solver...')
                self._solver = KrylovSolver("cg", "ilu")
                self._solver.parameters["maximum_iterations"] = self.MAX_ITER
                self._solver.parameters["absolute_tolerance"] = 1E-8
                logger.debug('Done.  Solver created.')

            self._degree = degree
Exemplo n.º 4
0
class StreamFunction(Field):
    def before_first_compute(self, get):
        u = get("Velocity")
        assert len(u) == 2, "Can only compute stream function for 2D problems"
        V = u.function_space()
        spaces = SpacePool(V.mesh())
        degree = V.ufl_element().degree()
        V = spaces.get_space(degree, 0)

        psi = TrialFunction(V)
        self.q = TestFunction(V)
        a = dot(grad(psi), grad(self.q))*dx()

        self.bc = DirichletBC(V, Constant(0), DomainBoundary())
        self.A = assemble(a)
        self.L = Vector()
        self.bc.apply(self.A)
        self.solver = KrylovSolver(self.A, "cg")
        self.psi = Function(V)


    def compute(self, get):
        u = get("Velocity")
        assemble(dot(u[1].dx(0)-u[0].dx(1), self.q)*dx(), tensor=self.L)
        self.bc.apply(self.L)

        self.solver.solve(self.psi.vector(), self.L)
        #solve(self.A, self.psi.vector(), self.L)

        return self.psi
Exemplo n.º 5
0
def les_setup(u_, mesh, KineticEnergySGS, assemble_matrix, CG1Function, nut_krylov_solver, bcs, **NS_namespace):
    """
    Set up for solving the Kinetic Energy SGS-model.
    """
    DG = FunctionSpace(mesh, "DG", 0)
    CG1 = FunctionSpace(mesh, "CG", 1)
    dim = mesh.geometry().dim()
    delta = Function(DG)
    delta.vector().zero()
    delta.vector().axpy(1.0, assemble(TestFunction(DG)*dx))
    delta.vector().set_local(delta.vector().array()**(1./dim))
    delta.vector().apply('insert')
    
    Ck = KineticEnergySGS["Ck"]
    ksgs = interpolate(Constant(1E-7), CG1)
    bc_ksgs = DirichletBC(CG1, 0, "on_boundary")
    A_mass = assemble_matrix(TrialFunction(CG1)*TestFunction(CG1)*dx)
    nut_form = Ck * delta * sqrt(ksgs)
    bcs_nut = derived_bcs(CG1, bcs['u0'], u_)
    nut_ = CG1Function(nut_form, mesh, method=nut_krylov_solver, bcs=bcs_nut, bounded=True, name="nut")
    At = Matrix()
    bt = Vector(nut_.vector())
    ksgs_sol = KrylovSolver("bicgstab", "additive_schwarz")
    ksgs_sol.parameters["preconditioner"]["structure"] = "same_nonzero_pattern"
    ksgs_sol.parameters["error_on_nonconvergence"] = False
    ksgs_sol.parameters["monitor_convergence"] = False
    ksgs_sol.parameters["report"] = False
    del NS_namespace
    return locals()
Exemplo n.º 6
0
def get_poisson_steps(pts, cells, tol):
    # Still can't initialize a mesh from points, cells
    filename = "mesh.xdmf"
    if cells.shape[1] == 3:
        meshio.write_points_cells(filename, pts, {"triangle": cells})
    else:
        assert cells.shape[1] == 4
        meshio.write_points_cells(filename, pts, {"tetra": cells})

    mesh = Mesh()
    with XDMFFile(filename) as f:
        f.read(mesh)
    os.remove(filename)
    os.remove("mesh.h5")

    # build Laplace matrix with Dirichlet boundary using dolfin
    V = FunctionSpace(mesh, "Lagrange", 1)

    u = TrialFunction(V)
    v = TestFunction(V)
    a = inner(grad(u), grad(v)) * dx
    u0 = Constant(0.0)
    bc = DirichletBC(V, u0, "on_boundary")
    f = Constant(1.0)
    L = f * v * dx

    A = assemble(a)
    b = assemble(L)
    bc.apply(A, b)

    # solve(A, x, b, "cg")
    solver = KrylovSolver("cg", "none")
    solver.parameters["absolute_tolerance"] = 0.0
    solver.parameters["relative_tolerance"] = tol

    x = Function(V)
    x_vec = x.vector()
    num_steps = solver.solve(A, x_vec, b)

    # # convert to scipy matrix
    # A = as_backend_type(A).mat()
    # ai, aj, av = A.getValuesCSR()
    # A = scipy.sparse.csr_matrix(
    #     (av, aj, ai), shape=(A.getLocalSize()[0], A.getSize()[1])
    # )

    # # ev = eigvals(A.todense())
    # ev_max = scipy.sparse.linalg.eigs(A, k=1, which="LM")[0][0]
    # assert numpy.abs(ev_max.imag) < 1.0e-15
    # ev_max = ev_max.real
    # ev_min = scipy.sparse.linalg.eigs(A, k=1, which="SM")[0][0]
    # assert numpy.abs(ev_min.imag) < 1.0e-15
    # ev_min = ev_min.real
    # cond = ev_max / ev_min

    # solve poisson system, count num steps
    # b = numpy.ones(A.shape[0])
    # out = pykry.gmres(A, b)
    # num_steps = len(out.resnorms)
    return num_steps
Exemplo n.º 7
0
 def __init__(self):
     mesh = UnitSquareMesh(200, 200)
     self.V = FunctionSpace(mesh, 'Lagrange', 2)
     test, trial = TestFunction(self.V), TrialFunction(self.V)
     M = assemble(inner(test, trial) * dx)
     #self.M = assemble(inner(test, trial)*dx)
     self.solverM = KrylovSolver("cg", "amg")
     self.solverM.set_operator(M)
Exemplo n.º 8
0
 def __missing__(self, key):
     assert len(key) == 4
     form, bcs, solver_type, preconditioner_type = key
     sol = KrylovSolver(solver_type, preconditioner_type)
     sol.parameters["preconditioner"]["structure"] = "same"
     sol.parameters["error_on_nonconvergence"] = False
     sol.parameters["monitor_convergence"] = False
     sol.parameters["report"] = False
     self[key] = sol
     return self[key]
Exemplo n.º 9
0
    def step(self, u1, u0, t, dt,
             tol=1.0e-10,
             maxiter=100,
             verbose=True
             ):
        v = TestFunction(self.problem.V)

        L, b = self.problem.get_system(t)
        Lu0 = Function(self.problem.V)
        L.mult(u0.vector(), Lu0.vector())
        rhs = assemble(u0 * v * self.problem.dx_multiplier * self.problem.dx)
        rhs.axpy(dt, -Lu0.vector() + b)

        A = self.M

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

        # Both Jacobi-and AMG-preconditioners are order-optimal for the mass
        # matrix, Jacobi is a little more lightweight.
        solver = KrylovSolver('cg', 'jacobi')
        solver.parameters['relative_tolerance'] = tol
        solver.parameters['absolute_tolerance'] = 0.0
        solver.parameters['maximum_iterations'] = maxiter
        solver.parameters['monitor_convergence'] = verbose
        solver.set_operator(A)

        solver.solve(u1.vector(), rhs)
        return
Exemplo n.º 10
0
    def step(self, u1, u0,
             t, dt,
             tol=1.0e-10,
             verbose=True,
             maxiter=1000,
             krylov='gmres',
             preconditioner='ilu'
             ):
        v = TestFunction(self.problem.V)

        L0, b0 = self.problem.get_system(t)
        L1, b1 = self.problem.get_system(t + dt)

        Lu0 = Function(self.problem.V)
        L0.mult(u0.vector(), Lu0.vector())

        rhs = assemble(u0 * v * self.problem.dx_multiplier * self.problem.dx)
        rhs.axpy(-dt * 0.5, Lu0.vector())
        rhs.axpy(+dt * 0.5, b0 + b1)

        A = self.M + 0.5 * dt * L1

        # 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
        solver.set_operator(A)

        solver.solve(u1.vector(), rhs)
        return
Exemplo n.º 11
0
    def __init__(self, fem, grounded_plate_at):
        self.GROUNDED_PLATE_AT = grounded_plate_at
        self.fem = fem
        self.CONDUCTIVITY = list(fem.CONDUCTIVITY)
        self.BOUNDARY_CONDUCTIVITY = list(fem.BOUNDARY_CONDUCTIVITY)

        self.solver = KrylovSolver("cg", "ilu")
        self.solver.parameters["maximum_iterations"] = 1000
        self.solver.parameters["absolute_tolerance"] = 1E-8

        self.V = fem._fm.function_space
        self.v = fem._fm.test_function()
        self.u = fem._fm.trial_function()
        self.dx = fem._dx
        self.ds = fem._ds
Exemplo n.º 12
0
 def __init__(self, form, Space, bcs=[], 
              name="x", 
              matvec=[None, None], 
              method="default", 
              solver_type="cg", 
              preconditioner_type="default"):
     
     Function.__init__(self, Space, name=name)
     self.form = form
     self.method = method
     self.bcs = bcs
     self.matvec = matvec
     self.trial = trial = TrialFunction(Space)
     self.test = test = TestFunction(Space)
     Mass = inner(trial, test)*dx()
     self.bf = inner(form, test)*dx()
     self.rhs = Vector(self.vector())
     
     if method.lower() == "default":
         self.A = A_cache[(Mass, tuple(bcs))]
         self.sol = KrylovSolver(solver_type, preconditioner_type)
         self.sol.parameters["preconditioner"]["structure"] = "same"
         self.sol.parameters["error_on_nonconvergence"] = False
         self.sol.parameters["monitor_convergence"] = False
         self.sol.parameters["report"] = False
             
     elif method.lower() == "lumping":
         assert Space.ufl_element().degree() < 2
         self.A = A_cache[(Mass, tuple(bcs))]
         ones = Function(Space)
         ones.vector()[:] = 1.
         self.ML = self.A * ones.vector()
         self.ML.set_local(1. / self.ML.array())
Exemplo n.º 13
0
    def solve_alpha_M_beta_F(self, alpha, beta, b, t):
        """Solve  :code:`alpha * M * u + beta * F(u, t) = b`  with Dirichlet
        conditions.
        """
        matrix = alpha * self.M + beta * self.A

        # See above for float conversion
        right_hand_side = -float(beta) * self.b.copy()
        if b:
            right_hand_side += b

        for bc in self.dirichlet_bcs:
            bc.apply(matrix, right_hand_side)

        # TODO proper preconditioner for convection
        if self.convection:
            # Use HYPRE-Euclid instead of ILU for parallel computation.
            # However, this PC sometimes fails.
            # solver = KrylovSolver('gmres', 'hypre_euclid')
            # Fallback:
            solver = LUSolver()
        else:
            solver = KrylovSolver("gmres", "hypre_amg")
            solver.parameters["relative_tolerance"] = 1.0e-13
            solver.parameters["absolute_tolerance"] = 0.0
            solver.parameters["maximum_iterations"] = 100
            solver.parameters["monitor_convergence"] = True

        solver.set_operator(matrix)

        u = Function(self.Q)
        solver.solve(u.vector(), right_hand_side)
        return u
Exemplo n.º 14
0
        def solve_alpha_M_beta_F(self, alpha, beta, b, t):
            # Solve  alpha * M * u + beta * F(u, t) = b  for u.
            A = alpha * self.M + beta * self.A

            rhs = b - beta * self.b
            self.bcs.apply(A, rhs)

            solver = KrylovSolver('gmres', 'ilu')
            solver.parameters['relative_tolerance'] = 1.0e-13
            solver.parameters['absolute_tolerance'] = 0.0
            solver.parameters['maximum_iterations'] = 100
            solver.parameters['monitor_convergence'] = True
            solver.set_operator(A)

            u = Function(self.V)
            solver.solve(u.vector(), rhs)
            return u
Exemplo n.º 15
0
 def _set_degree(self, degree):
     self._fm.degree = degree
     with self.global_preprocessing_time:
         logger.debug('Creating integration subdomains...')
         self.create_integration_subdomains()
         logger.debug('Done.  Creating test function...')
         self._v = self._fm.test_function()
         logger.debug('Done.  Creating potential function...')
         self._potential_function = self._fm.function()
         logger.debug('Done.  Creating trial function...')
         self._potential_trial = self._fm.trial_function()
         logger.debug('Done.  Creating LHS part of equation...')
         self._a = self._lhs()
         logger.debug('Done.  Creating base potential formula...')
         self._base_potential_expression = self._potential_expression()
         self._base_potential_gradient_normal_expression = self._potential_gradient_normal(
         )
         logger.debug('Done.  Creating solver...')
         self._solver = KrylovSolver("cg", "ilu")
         self._solver.parameters["maximum_iterations"] = self.MAX_ITER
         self._solver.parameters["absolute_tolerance"] = 1E-8
         logger.debug('Done.  Solver created.')
Exemplo n.º 16
0
def main_slice_fem(mesh, subdomains, boundaries, src_pos, snk_pos):
    sigma_ROI = Constant(params.sigma_roi)
    sigma_SLICE = Constant(params.sigma_slice)
    sigma_SALINE = Constant(params.sigma_saline)
    sigma_AIR = Constant(0.)

    V = FunctionSpace(mesh, "CG", 2)
    v = TestFunction(V)
    u = TrialFunction(V)

    phi = Function(V)
    dx = Measure("dx")(subdomain_data=subdomains)
    ds = Measure("ds")(subdomain_data=boundaries)
    a = inner(sigma_ROI * grad(u), grad(v))*dx(params.roivol) + \
        inner(sigma_SLICE * grad(u), grad(v))*dx(params.slicevol) + \
        inner(sigma_SALINE * grad(u), grad(v))*dx(params.salinevol)
    L = Constant(0) * v * dx
    A = assemble(a)
    b = assemble(L)

    x_pos, y_pos, z_pos = src_pos
    point = Point(x_pos, y_pos, z_pos)
    delta = PointSource(V, point, 1.)
    delta.apply(b)

    x_pos, y_pos, z_pos = snk_pos
    point1 = Point(x_pos, y_pos, z_pos)
    delta1 = PointSource(V, point1, -1.)
    delta1.apply(b)

    solver = KrylovSolver("cg", "ilu")
    solver.parameters["maximum_iterations"] = 1000
    solver.parameters["absolute_tolerance"] = 1E-8
    solver.parameters["monitor_convergence"] = True

    info(solver.parameters, True)
    #    set_log_level(PROGRESS) does not work in fenics 2018.1.0
    solver.solve(A, phi.vector(), b)

    ele_pos_list = params.ele_coords
    vals = extract_pots(phi, ele_pos_list)
    # np.save(os.path.join('results', save_as), vals)
    return vals
Exemplo n.º 17
0
        def solve_alpha_M_beta_F(self, alpha, beta, b, t):
            # Solve  alpha * M * u + beta * F(u, t) = b  for u.
            A = alpha * self.M + beta * self.A

            f.t = t
            v = TestFunction(self.V)
            b2 = assemble(f * v * dx)

            rhs = b.vector() - beta * b2
            self.bcs.apply(A, rhs)

            solver = \
                KrylovSolver('gmres', 'ilu') if alpha < 0.0 or beta > 0.0 \
                else KrylovSolver('cg', 'amg')
            solver.parameters['relative_tolerance'] = 1.0e-13
            solver.parameters['absolute_tolerance'] = 0.0
            solver.parameters['maximum_iterations'] = 100
            solver.parameters['monitor_convergence'] = False
            solver.set_operator(A)

            u = Function(self.V)
            solver.solve(u.vector(), rhs)
            return u
Exemplo n.º 18
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
Exemplo n.º 19
0
    def before_first_compute(self, get):
        u = get(self.valuename)

        if isinstance(u, Function):

            if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"):
                rank = len(u.ufl_shape)
            else:
                rank = u.rank()

            if rank == 0:
                self.f = Function(u.function_space())
            elif rank >= 1:
                # Assume all subpaces are equal
                V = u.function_space().extract_sub_space([0]).collapse()
                mesh = V.mesh()
                el = V.ufl_element()
                self.f = Function(V)

                # Find out if we can operate directly on vectors, or if we have to use a projection
                # We can operate on vectors if all sub-dofmaps are ordered the same way
                # For simplicity, this is only tested for CG- or DG0-spaces
                # (this might always be true for these spaces, but better to be safe than sorry )
                self.use_project = True
                if el.family() == "Lagrange" or (el.family()
                                                 == "Discontinuous Lagrange"
                                                 and el.degree() == 0):
                    #dm = u.function_space().dofmap()
                    dm0 = V.dofmap()
                    self.use_project = False
                    for i in xrange(u.function_space().num_sub_spaces()):
                        Vi = u.function_space().extract_sub_space(
                            [i]).collapse()
                        dmi = Vi.dofmap()
                        try:
                            # For 1.6.0+ and newer
                            diff = Vi.tabulate_dof_coordinates(
                            ) - V.tabulate_dof_coordinates()
                        except:
                            # For 1.6.0 and older
                            diff = dmi.tabulate_all_coordinates(
                                mesh) - dm0.tabulate_all_coordinates(mesh)
                        if len(diff) > 0:
                            max_diff = max(abs(diff))
                        else:
                            max_diff = 0.0
                        max_diff = MPI.max(mpi_comm_world(), max_diff)
                        if max_diff > 1e-12:
                            self.use_project = True
                            break
                        self.assigner = FunctionAssigner(
                            [V] * u.function_space().num_sub_spaces(),
                            u.function_space())
                        self.subfuncs = [
                            Function(V)
                            for _ in range(u.function_space().num_sub_spaces())
                        ]

                # IF we have to use a projection, build projection matrix only once
                if self.use_project:
                    self.v = TestFunction(V)
                    M = assemble(inner(self.v, TrialFunction(V)) * dx)
                    self.projection = KrylovSolver("cg", "default")
                    self.projection.set_operator(M)
        elif isinstance(u, Iterable) and all(
                isinstance(_u, Number) for _u in u):
            pass
        elif isinstance(u, Number):
            pass
        else:
            # Don't know how to handle object
            cbc_warning(
                "Don't know how to calculate magnitude of object of type %s." %
                type(u))
Exemplo n.º 20
0
class Magnitude(MetaField):
    """ Compute the magnitude of a Function-evaluated Field.

    Supports function spaces where all subspaces are equal.
    """
    def before_first_compute(self, get):
        u = get(self.valuename)

        if isinstance(u, Function):

            if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"):
                rank = len(u.ufl_shape)
            else:
                rank = u.rank()

            if rank == 0:
                self.f = Function(u.function_space())
            elif rank >= 1:
                # Assume all subpaces are equal
                V = u.function_space().extract_sub_space([0]).collapse()
                mesh = V.mesh()
                el = V.ufl_element()
                self.f = Function(V)

                # Find out if we can operate directly on vectors, or if we have to use a projection
                # We can operate on vectors if all sub-dofmaps are ordered the same way
                # For simplicity, this is only tested for CG- or DG0-spaces
                # (this might always be true for these spaces, but better to be safe than sorry )
                self.use_project = True
                if el.family() == "Lagrange" or (el.family()
                                                 == "Discontinuous Lagrange"
                                                 and el.degree() == 0):
                    #dm = u.function_space().dofmap()
                    dm0 = V.dofmap()
                    self.use_project = False
                    for i in xrange(u.function_space().num_sub_spaces()):
                        Vi = u.function_space().extract_sub_space(
                            [i]).collapse()
                        dmi = Vi.dofmap()
                        try:
                            # For 1.6.0+ and newer
                            diff = Vi.tabulate_dof_coordinates(
                            ) - V.tabulate_dof_coordinates()
                        except:
                            # For 1.6.0 and older
                            diff = dmi.tabulate_all_coordinates(
                                mesh) - dm0.tabulate_all_coordinates(mesh)
                        if len(diff) > 0:
                            max_diff = max(abs(diff))
                        else:
                            max_diff = 0.0
                        max_diff = MPI.max(mpi_comm_world(), max_diff)
                        if max_diff > 1e-12:
                            self.use_project = True
                            break
                        self.assigner = FunctionAssigner(
                            [V] * u.function_space().num_sub_spaces(),
                            u.function_space())
                        self.subfuncs = [
                            Function(V)
                            for _ in range(u.function_space().num_sub_spaces())
                        ]

                # IF we have to use a projection, build projection matrix only once
                if self.use_project:
                    self.v = TestFunction(V)
                    M = assemble(inner(self.v, TrialFunction(V)) * dx)
                    self.projection = KrylovSolver("cg", "default")
                    self.projection.set_operator(M)
        elif isinstance(u, Iterable) and all(
                isinstance(_u, Number) for _u in u):
            pass
        elif isinstance(u, Number):
            pass
        else:
            # Don't know how to handle object
            cbc_warning(
                "Don't know how to calculate magnitude of object of type %s." %
                type(u))

    def compute(self, get):
        u = get(self.valuename)

        if isinstance(u, Function):
            if not hasattr(self, "use_project"):
                self.before_first_compute(get)

            if LooseVersion(dolfin_version()) > LooseVersion("1.6.0"):
                rank = len(u.ufl_shape)
            else:
                rank = u.rank()

            if rank == 0:
                self.f.vector().zero()
                self.f.vector().axpy(1.0, u.vector())
                self.f.vector().abs()
                return self.f
            elif rank >= 1:
                if self.use_project:
                    b = assemble(sqrt(inner(u, u)) * self.v * dx(None))
                    self.projection.solve(self.f.vector(), b)
                else:
                    self.assigner.assign(self.subfuncs, u)
                    self.f.vector().zero()
                    for i in xrange(u.function_space().num_sub_spaces()):
                        vec = self.subfuncs[i].vector()
                        vec.apply('')
                        self.f.vector().axpy(1.0, vec * vec)

                    try:
                        sqrt_in_place(self.f.vector())
                    except:
                        r = self.f.vector().local_range()
                        self.f.vector()[r[0]:r[1]] = np.sqrt(
                            self.f.vector()[r[0]:r[1]])
                    self.f.vector().apply('')

                return self.f
        elif isinstance(u, Iterable) and all(
                isinstance(_u, Number) for _u in u):
            return np.sqrt(sum(_u**2 for _u in u))
        elif isinstance(u, Number):
            return abs(u)
        else:
            # Don't know how to handle object
            cbc_warning(
                "Don't know how to calculate magnitude of object of type %s. Returning object."
                % type(u))
            return u
Exemplo n.º 21
0
            def __call__(self, degree=3, y=0., standard_deviation=1.):
                V = FunctionSpace(self._mesh, "CG", degree)
                v = TestFunction(V)
                potential_trial = TrialFunction(V)
                potential = Function(V)
                dx = Measure("dx")(subdomain_data=self._subdomains)
                # ds = Measure("ds")(subdomain_data=self._boundaries)
                csd = Expression(f'''
                                  x[1] >= {self.H} ?
                                  0 :
                                  a * exp({-0.5 / standard_deviation ** 2}
                                          * ((x[0])*(x[0])
                                             + (x[1] - {y})*(x[1] - {y})
                                             + (x[2])*(x[2])
                                             ))
                                  ''',
                                 degree=degree,
                                 a=1.0)

                self.a = csd.a = 1.0 / assemble(
                    csd * Measure("dx", self._mesh))
                # print(assemble(csd * Measure("dx", self._mesh)))
                L = csd * v * dx

                known_terms = assemble(L)
                # a = (inner(grad(potential_trial), grad(v))
                #      * (Constant(self.SALINE_CONDUCTIVITY) * dx(self.SALINE_VOL)
                #         + Constant(self.SLICE_CONDUCTIVITY) * (dx(self.SLICE_VOL)
                #                                                + dx(self.ROI_VOL))))
                a = sum(
                    Constant(conductivity) *
                    inner(grad(potential_trial), grad(v)) * dx(domain)
                    for domain, conductivity in [
                        (self.SALINE_VOL, self.SALINE_CONDUCTIVITY),
                        (self.SLICE_VOL, self.SLICE_CONDUCTIVITY),
                        (self.ROI_VOL, self.SLICE_CONDUCTIVITY),
                    ])
                terms_with_unknown = assemble(a)
                dirchlet_bc = DirichletBC(
                    V,
                    Constant(2.0 * 0.25 /
                             (self.RADIUS * np.pi * self.SALINE_CONDUCTIVITY)),
                    # 2.0 becaue of dielectric base duplicating
                    # the current source
                    # slice conductivity and thickness considered
                    # negligible
                    self._boundaries,
                    self.MODEL_DOME)
                dirchlet_bc.apply(terms_with_unknown, known_terms)
                solver = KrylovSolver("cg", "ilu")
                solver.parameters["maximum_iterations"] = MAX_ITER
                solver.parameters["absolute_tolerance"] = 1E-8
                # solver.parameters["monitor_convergence"] = True
                start = datetime.datetime.now()
                try:
                    self.iterations = solver.solve(terms_with_unknown,
                                                   potential.vector(),
                                                   known_terms)
                    return potential

                except RuntimeError as e:
                    self.iterations = MAX_ITER
                    logger.warning("Solver failed: {}".format(repr(e)))
                    return None

                finally:
                    self.time = datetime.datetime.now() - start
Exemplo n.º 22
0
    class _SubtractionPointSourcePotentialFEM(object):
        def __init__(self, config):
            self._fm = FunctionManagerINI(config)
            self._setup_mesh(self._fm.getpath('fem', 'mesh')[:-5])
            self._load_config(self._fm.getpath('fem', 'config'))
            self.global_preprocessing_time = fc.Stopwatch()
            self.local_preprocessing_time = fc.Stopwatch()
            self.solving_time = fc.Stopwatch()

            self._set_degree(self.degree)

        def _load_mesh_data(self, path, name, dim):
            with XDMFFile(path) as fh:
                mvc = MeshValueCollection("size_t", self._fm.mesh, dim)
                fh.read(mvc, name)
                return cpp.mesh.MeshFunctionSizet(self._fm.mesh, mvc)

        def _setup_mesh(self, mesh):
            self._boundaries = self._load_mesh_data(mesh + '_boundaries.xdmf',
                                                    "boundaries", 2)
            self._subdomains = self._load_mesh_data(mesh + '_subdomains.xdmf',
                                                    "subdomains", 3)
            # self._facet_normal = FacetNormal(self._fm.mesh)

        def _load_config(self, config):
            self.config = configparser.ConfigParser()
            self.config.read(config)

        @property
        def degree(self):
            return self._fm.degree

        @degree.setter
        def degree(self, degree):
            if degree != self.degree:
                self._set_degree(degree)

        def _set_degree(self, degree):
            self._fm.degree = degree
            with self.global_preprocessing_time:
                logger.debug('Creating integration subdomains...')
                self.create_integration_subdomains()
                logger.debug('Done.  Creating test function...')
                self._v = self._fm.test_function()
                logger.debug('Done.  Creating potential function...')
                self._potential_function = self._fm.function()
                logger.debug('Done.  Creating trial function...')
                self._potential_trial = self._fm.trial_function()
                logger.debug('Done.  Creating LHS part of equation...')
                self._a = self._lhs()
                logger.debug('Done.  Creating base potential formula...')
                self._base_potential_expression = self._potential_expression()
                self._base_potential_gradient_normal_expression = self._potential_gradient_normal(
                )
                logger.debug('Done.  Creating solver...')
                self._solver = KrylovSolver("cg", "ilu")
                self._solver.parameters["maximum_iterations"] = self.MAX_ITER
                self._solver.parameters["absolute_tolerance"] = 1E-8
                logger.debug('Done.  Solver created.')

        def create_integration_subdomains(self):
            self._dx = Measure("dx")(subdomain_data=self._subdomains)
            self._ds = Measure("ds")(subdomain_data=self._boundaries)

        def _lhs(self):
            return sum(
                inner(
                    Constant(c) * grad(self._potential_trial), grad(self._v)) *
                self._dx(x) for x, c in self.CONDUCTIVITY)

        @property
        def CONDUCTIVITY(self):
            for section in self.config.sections():
                if self._is_conductive_volume(section):
                    yield (self.config.getint(section, 'volume'),
                           self.config.getfloat(section, 'conductivity'))

        def _is_conductive_volume(self, section):
            return (self.config.has_option(section, 'volume')
                    and self.config.has_option(section, 'conductivity'))

        @property
        def BOUNDARY_CONDUCTIVITY(self):
            for section in self.config.sections():
                if self._is_conductive_boundary(section):
                    yield (self.config.getint(section, 'surface'),
                           self.config.getfloat(section, 'conductivity'))

        def _is_conductive_boundary(self, section):
            return (self.config.has_option(section, 'surface')
                    and self.config.has_option(section, 'conductivity'))

        def _solve(self):
            self.iterations = self._solver.solve(
                self._terms_with_unknown, self._potential_function.vector(),
                self._known_terms)

        def solve(self, x, y, z):
            with self.local_preprocessing_time:
                logger.debug('Creating RHS...')
                L = self._rhs(x, y, z)
                logger.debug('Done.  Assembling linear equation vector...')
                self._known_terms = assemble(L)
                logger.debug('Done.  Assembling linear equation matrix...')
                self._terms_with_unknown = assemble(self._a)
                logger.debug('Done.')
                self._modify_linear_equation(x, y, z)

            try:
                logger.debug('Solving linear equation...')
                with self.solving_time:
                    self._solve()

                logger.debug('Done.')
                return self._potential_function

            except RuntimeError as e:
                self.iterations = self.MAX_ITER
                logger.warning("Solver failed: {}".format(repr(e)))
                return None

        def _rhs(self, x, y, z):
            base_conductivity = self.base_conductivity(x, y, z)
            self._setup_expression(self._base_potential_expression,
                                   base_conductivity, x, y, z)
            self._setup_expression(
                self._base_potential_gradient_normal_expression,
                base_conductivity, x, y, z)
            return (
                -sum((inner(
                    (Constant(c - base_conductivity) *
                     grad(self._base_potential_expression)), grad(self._v)) *
                      self._dx(x)
                      for x, c in self.CONDUCTIVITY if c != base_conductivity))
                # # Eq. 18 at Piastra et al 2018
                - sum(
                    Constant(c)
                    # * inner(self._facet_normal,
                    #         grad(self._base_potential_expression))
                    * self._base_potential_gradient_normal_expression *
                    self._v * self._ds(s)
                    for s, c in self.BOUNDARY_CONDUCTIVITY))

        def _setup_expression(self, expression, base_conductivity, x, y, z):
            expression.conductivity = base_conductivity
            expression.src_x = x
            expression.src_y = y
            expression.src_z = z
Exemplo n.º 23
0
class AcousticWave():
    """
    Solution of forward and adjoint equations for acoustic inverse problem
    """

    def __init__(self, functionspaces_V):
        """
        Input:
            functionspaces_V = dict containing functionspaces for state/adj
        ('V') and med param ('Vl' for lambda and 'Vr' for rho)
        """
        self.readV(functionspaces_V)
        self.verbose = False    # print info
        self.lump = False   # Lump the mass matrix
        self.lumpD = False   # Lump the ABC matrix
        self.timestepper = None # 'backward', 'centered'
        self.exact = None   # exact solution at final time
        self.u0init = None  # provides u(t=t0)
        self.utinit = None  # provides u_t(t=t0)
        self.u1init = None  # provides u1 = u(t=t0+/-Dt)
        self.bc = None
        self.abc = False
        self.ftime = lambda t: 0.0  # ftime(tt) = src term @ t=tt (in np.array())
        self.set_fwd()  # default is forward problem


    def copy(self):
        """(hard) copy constructor"""
        newobj = self.__class__({'V':self.V, 'Vl':self.Vl, 'Vr':self.Vr})
        newobj.lump = self.lump
        newobj.timestepper = self.timestepper
        newobj.exact = self.exact
        newobj.utinit = self.utinit
        newobj.u1init = self.u1init
        newobj.bc = self.bc
        if self.abc == True:
            newobj.set_abc(self.V.mesh(), self.class_bc_abc, self.lumpD)
        newobj.ftime = self.ftime
        newobj.update({'lambda':self.lam, 'rho':self.rho, \
        't0':self.t0, 'tf':self.tf, 'Dt':self.Dt, \
        'u0init':self.u0init, 'utinit':self.utinit, 'u1init':self.u1init})
        return newobj


    def readV(self, functionspaces_V):
        # Solutions:
        self.V = functionspaces_V['V']
        self.test = TestFunction(self.V)
        self.trial = TrialFunction(self.V)
        self.u0 = Function(self.V)    # u(t-Dt)
        self.u1 = Function(self.V)     # u(t)
        self.u2 = Function(self.V)    # u(t+Dt)
        self.rhs = Function(self.V)
        self.sol = Function(self.V)
        # Parameters:
        self.Vl = functionspaces_V['Vl']
        self.lam = Function(self.Vl)
        self.Vr = functionspaces_V['Vr']
        self.rho = Function(self.Vr)
        if functionspaces_V.has_key('Vm'):
            self.Vm = functionspaces_V['Vm']
            self.mu = Function(self.Vm)
            self.elastic = True
            assert(False)
        else:   
            self.elastic = False
            self.weak_k = inner(self.lam*nabla_grad(self.trial), \
            nabla_grad(self.test))*dx
            self.weak_m = inner(self.rho*self.trial,self.test)*dx


    def set_abc(self, mesh, class_bc_abc, lumpD=False):
        self.abc = True # False means zero-Neumann all-around
        if lumpD:    self.lumpD = True
        abc_boundaryparts = FacetFunction("size_t", mesh)
        class_bc_abc.mark(abc_boundaryparts, 1)
        self.ds = Measure("ds")[abc_boundaryparts]
        self.weak_d = inner(sqrt(self.lam*self.rho)*self.trial, self.test)*self.ds(1)
        self.class_bc_abc = class_bc_abc    # to make copies


    def set_fwd(self):  
        self.fwdadj = 1.0
        self.ftime = None

    def set_adj(self):  
        self.fwdadj = -1.0
        self.ftime = None

    def get_tt(self, nn):
        if self.fwdadj > 0.0:   return self.times[nn]
        else:   return self.times[-nn-1]


    def update(self, parameters_m):
        assert not self.timestepper == None, "You need to set a time stepping method"
        # Time options:
        if parameters_m.has_key('t0'):   self.t0 = parameters_m['t0'] 
        if parameters_m.has_key('tf'):   self.tf = parameters_m['tf'] 
        if parameters_m.has_key('Dt'):   self.Dt = parameters_m['Dt'] 
        if parameters_m.has_key('t0') or parameters_m.has_key('tf') or parameters_m.has_key('Dt'):
            self.Nt = int(round((self.tf-self.t0)/self.Dt))
            self.Tf = self.t0 + self.Dt*self.Nt
            self.times = np.linspace(self.t0, self.Tf, self.Nt+1)
            assert isequal(self.times[1]-self.times[0], self.Dt, 1e-16), "Dt modified"
            self.Dt = self.times[1] - self.times[0]
            assert isequal(self.Tf, self.tf, 1e-2), "Final time differs by more than 1%"
            if not isequal(self.Tf, self.tf, 1e-12):
                print 'Final time modified from {} to {} ({}%)'.\
                format(self.tf, self.Tf, abs(self.Tf-self.tf)/self.tf)
        # Initial conditions:
        if parameters_m.has_key('u0init'):   self.u0init = parameters_m['u0init']
        if parameters_m.has_key('utinit'):   self.utinit = parameters_m['utinit']
        if parameters_m.has_key('u1init'):   self.u1init = parameters_m['u1init']
        if parameters_m.has_key('um1init'):   self.um1init = parameters_m['um1init']
        # Medium parameters:
        setfct(self.lam, parameters_m['lambda'])
        if self.verbose: print 'lambda updated '
        if self.elastic == True:    
            setfct(self.mu, parameters_m['mu'])
            if self.verbose: print 'mu updated'
        if self.verbose: print 'assemble K',
        self.K = assemble(self.weak_k)
        if self.verbose: print ' -- K assembled'
        if parameters_m.has_key('rho'):
            setfct(self.rho, parameters_m['rho'])
            # Mass matrix:
            if self.verbose: print 'rho updated\nassemble M',
            Mfull = assemble(self.weak_m)
            if self.lump:
                self.solverM = LumpedMatrixSolverS(self.V)
                self.solverM.set_operator(Mfull, self.bc)
                self.M = self.solverM
            else:
                if mpisize == 1:
                    self.solverM = LUSolver()
                    self.solverM.parameters['reuse_factorization'] = True
                    self.solverM.parameters['symmetric'] = True
                else:
                    self.solverM = KrylovSolver('cg', 'amg')
                    self.solverM.parameters['report'] = False
                self.M = Mfull
                if not self.bc == None: self.bc.apply(Mfull)
                self.solverM.set_operator(Mfull)
            if self.verbose: print ' -- M assembled'
        # Matrix D for abs BC
        if self.abc == True:    
            if self.verbose:    print 'assemble D',
            Mfull = assemble(self.weak_m)
            Dfull = assemble(self.weak_d)
            if self.lumpD:
                self.D = LumpedMatrixSolverS(self.V)
                self.D.set_operator(Dfull, None, False)
                if self.lump:
                    self.solverMplD = LumpedMatrixSolverS(self.V)
                    self.solverMplD.set_operators(Mfull, Dfull, .5*self.Dt, self.bc)
                    self.MminD = LumpedMatrixSolverS(self.V)
                    self.MminD.set_operators(Mfull, Dfull, -.5*self.Dt, self.bc)
            else:
                self.D = Dfull
            if self.verbose:    print ' -- D assembled'
        else:   self.D = 0.0


    #@profile
    def solve(self):
        """ General solver method """
        if self.timestepper == 'backward':
            def iterate(tt):  self.iteration_backward(tt)
        elif self.timestepper == 'centered':
            def iterate(tt):  self.iteration_centered(tt)
        else:
            print "Time stepper not implemented"
            sys.exit(1)

        if self.verbose:    print 'Compute solution'
        solout = [] # Store computed solution
        # u0:
        tt = self.get_tt(0)
        if self.verbose:    print 'Compute solution -- time {}'.format(tt)
        setfct(self.u0, self.u0init)
        solout.append([self.u0.vector().array(), tt])
        # Compute u1:
        if not self.u1init == None: self.u1 = self.u1init
        else:
            assert(not self.utinit == None)
            setfct(self.rhs, self.ftime(tt))
            self.rhs.vector().axpy(-self.fwdadj, self.D*self.utinit.vector())
            self.rhs.vector().axpy(-1.0, self.K*self.u0.vector())
            if not self.bc == None: self.bc.apply(self.rhs.vector())
            self.solverM.solve(self.sol.vector(), self.rhs.vector())
            setfct(self.u1, self.u0)
            self.u1.vector().axpy(self.fwdadj*self.Dt, self.utinit.vector())
            self.u1.vector().axpy(0.5*self.Dt**2, self.sol.vector())
        tt = self.get_tt(1)
        if self.verbose:    print 'Compute solution -- time {}'.format(tt)
        solout.append([self.u1.vector().array(), tt])
        # Iteration
        for nn in xrange(2, self.Nt+1):
            iterate(tt)
            # Advance to next time step
            setfct(self.u0, self.u1)
            setfct(self.u1, self.u2)
            tt = self.get_tt(nn)
            if self.verbose:    
                print 'Compute solution -- time {}, rhs {}'.\
                format(tt, np.max(np.abs(self.ftime(tt))))
            solout.append([self.u1.vector().array(),tt])
        if self.fwdadj > 0.0:   
            assert isequal(tt, self.Tf, 1e-16), \
            'tt={}, Tf={}, reldiff={}'.format(tt, self.Tf, abs(tt-self.Tf)/self.Tf)
        else:
            assert isequal(tt, self.t0, 1e-16), \
            'tt={}, t0={}, reldiff={}'.format(tt, self.t0, abs(tt-self.t0))
        return solout, self.computeerror()

    def iteration_centered(self, tt):
        setfct(self.rhs, (self.Dt**2)*self.ftime(tt))
        self.rhs.vector().axpy(-1.0, self.MminD*self.u0.vector())
        self.rhs.vector().axpy(2.0, self.M*self.u1.vector())
        self.rhs.vector().axpy(-self.Dt**2, self.K*self.u1.vector())
        if not self.bc == None: self.bc.apply(self.rhs.vector())
        self.solverMplD.solve(self.u2.vector(), self.rhs.vector())

    def iteration_backward(self, tt):
        setfct(self.rhs, self.Dt*self.ftime(tt))
        self.rhs.vector().axpy(-self.Dt, self.K*self.u1.vector())
        self.rhs.vector().axpy(-1.0, self.D*(self.u1.vector()-self.u0.vector()))
        if not self.bc == None: self.bc.apply(self.rhs.vector())
        self.solverM.solve(self.sol.vector(), self.rhs.vector())
        setfct(self.u2, 2.0*self.u1.vector())
        self.u2.vector().axpy(-1.0, self.u0.vector())
        self.u2.vector().axpy(self.Dt, self.sol.vector())


    def computeerror(self): 
        return self.computerelativeerror()

    def computerelativeerror(self):
        if not self.exact == None:
            #MM = assemble(inner(self.trial, self.test)*dx)
            MM = self.M
            norm_ex = np.sqrt(\
            (MM*self.exact.vector()).inner(self.exact.vector()))
            diff = self.exact.vector() - self.u1.vector()
            if norm_ex > 1e-16: return np.sqrt((MM*diff).inner(diff))/norm_ex
            else:   return np.sqrt((MM*diff).inner(diff))
        else:   return []

    def computeabserror(self):
        if not self.exact == None:
            MM = self.M
            diff = self.exact.vector() - self.u1.vector()
            return np.sqrt((MM*diff).inner(diff))
        else:   return []
Exemplo n.º 24
0
                  source_z=7.85,
                  source_y=0,
                  sigma_2=0.1,
                  A=(2 * np.pi * 0.1) ** -1.5,
                  degree=DEGREE)
#B_inv = assemble(csd * Measure('dx', mesh))

# csd_at = interpolate(csd, V)
# csd_at(0, 0, 7.2) < 0

def boundary(x, on_boundary):
    return x[1] <= NECK_AT

bc = DirichletBC(V, Constant(0.), boundary)

solver = KrylovSolver("cg", "ilu")
solver.parameters["maximum_iterations"] = 1100
solver.parameters["absolute_tolerance"] = 1E-8
#solver.parameters["monitor_convergence"] = True

SOURCES = []
DBG = {}

TMP_FILENAME = f'proof_of_concept_fem_dirchlet_newman_CTX_deg_{DEGREE}_.npz'
try:
    fh = np.load(TMP_FILENAME)

except FileNotFoundError:
    print('no previous results found')
    previously_solved = set()
Exemplo n.º 25
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
Exemplo n.º 26
0
class SphericalModelFEM(object):
    def __init__(self, fem, grounded_plate_at):
        self.GROUNDED_PLATE_AT = grounded_plate_at
        self.fem = fem
        self.CONDUCTIVITY = list(fem.CONDUCTIVITY)
        self.BOUNDARY_CONDUCTIVITY = list(fem.BOUNDARY_CONDUCTIVITY)

        self.solver = KrylovSolver("cg", "ilu")
        self.solver.parameters["maximum_iterations"] = 1000
        self.solver.parameters["absolute_tolerance"] = 1E-8

        self.V = fem._fm.function_space
        self.v = fem._fm.test_function()
        self.u = fem._fm.trial_function()
        self.dx = fem._dx
        self.ds = fem._ds

    def function(self):
        return self.fem._fm.function()

    def reciprocal_correction_potential(self, x, y, z):
        """
        .. deprecated::
                   Moved to `SphereOnGroundedPlatePointSourcePotentialFEM` class
                   (rewritten) and available as its `.solve()` method.
                   Preserved as the original code not obfuscated by the
                   `_SubtractionPointSourcePotentialFEM` class protocol.

        Parameters
        ----------
        x
        y
        z

        Returns
        -------

        """
        dx_src = f'(x[0] - src_x)'
        dy_src = f'(x[1] - src_y)'
        dz_src = f'(x[2] - src_z)'

        r_src2 = f'({dx_src} * {dx_src} + {dy_src} * {dy_src} + {dz_src} * {dz_src})'
        r_src = f'sqrt({r_src2})'
        r_sphere2 = '(x[0] * x[0] + x[1] * x[1] + x[2] * x[2])'

        dot_src = f'({dx_src} * x[0] + {dy_src} * x[1] + {dz_src} * x[2]) / sqrt({r_src2} * {r_sphere2})'
        potential_exp = Expression(f'''
                                    {0.25 / np.pi} / conductivity
                                    * (1.0 / {r_src})
                                    ''',
                                   degree=self.fem.degree,
                                   domain=self.fem._fm._mesh,
                                   conductivity=0.33,
                                   src_x=0.0,
                                   src_y=0.0,
                                   src_z=0.0)
        minus_potential_exp = Expression(f'''
                                        {-0.25 / np.pi} / conductivity
                                        * (1.0 / {r_src})
                                        ''',
                                         degree=self.fem.degree,
                                         domain=self.fem._fm._mesh,
                                         conductivity=0.33,
                                         src_x=0.0,
                                         src_y=0.0,
                                         src_z=0.0)
        potential_grad_dot = Expression(f'''
                                        x[2] >= {self.GROUNDED_PLATE_AT} ?
                                        -1 * {-0.25 / np.pi} / conductivity
                                        * ({dot_src} / {r_src2})
                                        : 0
                                        ''',
                                        degree=self.fem.degree,
                                        domain=self.fem._fm._mesh,
                                        conductivity=0.33,
                                        src_x=0.0,
                                        src_y=0.0,
                                        src_z=0.0)

        conductivity = fem.base_conductivity(x, y, z)

        for expr in [potential_exp, potential_grad_dot, minus_potential_exp]:
            expr.conductivity = conductivity
            expr.src_x = x
            expr.src_y = y
            expr.src_z = z

        print(' solving')

        a = sum(
            inner(Constant(c) * grad(self.u), grad(self.v)) * self.dx(i)
            for i, c in self.CONDUCTIVITY)
        L = (-sum(
            inner((Constant(c - conductivity) *
                   grad(potential_exp)), grad(self.v)) * self.dx(i)
            for i, c in self.CONDUCTIVITY if c != conductivity) + sum(
                Constant(c) * potential_grad_dot * self.v * self.ds(i)
                for i, c in self.BOUNDARY_CONDUCTIVITY))

        return self.solve(L, a, minus_potential_exp)

    def source_potential(self, csd=None, src=None):
        if csd is None:
            csd = self.callable_as_function(src.csd)

        a = sum(
            Constant(c) * inner(grad(self.u), grad(self.v)) * self.dx(i)
            for i, c in self.CONDUCTIVITY)
        L = csd * self.v * self.dx

        return self.solve(L, a, Constant(0))

    def source_correction(self, src):
        potential_f = self.callable_as_function(src.potential)

        conductivity = self.fem.base_conductivity(x_ele, y_ele, z_ele)

        print(' solving')

        a = sum(
            inner(Constant(c) * grad(self.u), grad(self.v)) * self.dx(i)
            for i, c in self.CONDUCTIVITY)
        L = (-sum(
            inner((Constant(c - conductivity) *
                   grad(potential_f)), grad(self.v)) * self.dx(i)
            for i, c in self.CONDUCTIVITY if c != conductivity) - sum(
                Constant(c) * inner(grad(potential_f),
                                    dolfin.FacetNormal(self.fem._fm.mesh)) *
                self.v * self.ds(i) for i, c in self.BOUNDARY_CONDUCTIVITY))
        neg_potential_f = NegativePotential(src.potential, self.fem._fm.mesh)
        return self.solve(L, a, neg_potential_f)

    def callable_as_function(self, f):
        n = self.V.dim()
        d = self.fem._fm.mesh.geometry().dim()
        dof_coordinates = self.V.tabulate_dof_coordinates()
        dof_coordinates.resize((n, d))
        dof_x = dof_coordinates[:, 0]
        dof_y = dof_coordinates[:, 1]
        dof_z = dof_coordinates[:, 2]
        g = fem._fm.function()
        g.vector()[:] = f(dof_x, dof_y, dof_z)
        return g

    def solve(self, L, a, plate_potential):
        A = assemble(a)
        b = assemble(L)
        # print(' assembled')

        dirichlet_bc = DirichletBC(self.V, plate_potential,
                                   (lambda x, on_boundary: on_boundary and x[2]
                                    < self.GROUNDED_PLATE_AT))

        dirichlet_bc.apply(A, b)
        # print(' modified')
        f = self.function()
        self.solver.solve(A, f.vector(), b)
        # print(' solved')
        return f
Exemplo n.º 27
0
 def update(self, parameters_m):
     assert not self.timestepper == None, "You need to set a time stepping method"
     # Time options:
     if parameters_m.has_key('t0'):   self.t0 = parameters_m['t0'] 
     if parameters_m.has_key('tf'):   self.tf = parameters_m['tf'] 
     if parameters_m.has_key('Dt'):   self.Dt = parameters_m['Dt'] 
     if parameters_m.has_key('t0') or parameters_m.has_key('tf') or parameters_m.has_key('Dt'):
         self.Nt = int(round((self.tf-self.t0)/self.Dt))
         self.Tf = self.t0 + self.Dt*self.Nt
         self.times = np.linspace(self.t0, self.Tf, self.Nt+1)
         assert isequal(self.times[1]-self.times[0], self.Dt, 1e-16), "Dt modified"
         self.Dt = self.times[1] - self.times[0]
         assert isequal(self.Tf, self.tf, 1e-2), "Final time differs by more than 1%"
         if not isequal(self.Tf, self.tf, 1e-12):
             print 'Final time modified from {} to {} ({}%)'.\
             format(self.tf, self.Tf, abs(self.Tf-self.tf)/self.tf)
     # Initial conditions:
     if parameters_m.has_key('u0init'):   self.u0init = parameters_m['u0init']
     if parameters_m.has_key('utinit'):   self.utinit = parameters_m['utinit']
     if parameters_m.has_key('u1init'):   self.u1init = parameters_m['u1init']
     if parameters_m.has_key('um1init'):   self.um1init = parameters_m['um1init']
     # Medium parameters:
     setfct(self.lam, parameters_m['lambda'])
     if self.verbose: print 'lambda updated '
     if self.elastic == True:    
         setfct(self.mu, parameters_m['mu'])
         if self.verbose: print 'mu updated'
     if self.verbose: print 'assemble K',
     self.K = assemble(self.weak_k)
     if self.verbose: print ' -- K assembled'
     if parameters_m.has_key('rho'):
         setfct(self.rho, parameters_m['rho'])
         # Mass matrix:
         if self.verbose: print 'rho updated\nassemble M',
         Mfull = assemble(self.weak_m)
         if self.lump:
             self.solverM = LumpedMatrixSolverS(self.V)
             self.solverM.set_operator(Mfull, self.bc)
             self.M = self.solverM
         else:
             if mpisize == 1:
                 self.solverM = LUSolver()
                 self.solverM.parameters['reuse_factorization'] = True
                 self.solverM.parameters['symmetric'] = True
             else:
                 self.solverM = KrylovSolver('cg', 'amg')
                 self.solverM.parameters['report'] = False
             self.M = Mfull
             if not self.bc == None: self.bc.apply(Mfull)
             self.solverM.set_operator(Mfull)
         if self.verbose: print ' -- M assembled'
     # Matrix D for abs BC
     if self.abc == True:    
         if self.verbose:    print 'assemble D',
         Mfull = assemble(self.weak_m)
         Dfull = assemble(self.weak_d)
         if self.lumpD:
             self.D = LumpedMatrixSolverS(self.V)
             self.D.set_operator(Dfull, None, False)
             if self.lump:
                 self.solverMplD = LumpedMatrixSolverS(self.V)
                 self.solverMplD.set_operators(Mfull, Dfull, .5*self.Dt, self.bc)
                 self.MminD = LumpedMatrixSolverS(self.V)
                 self.MminD.set_operators(Mfull, Dfull, -.5*self.Dt, self.bc)
         else:
             self.D = Dfull
         if self.verbose:    print ' -- D assembled'
     else:   self.D = 0.0
Exemplo n.º 28
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
Exemplo n.º 29
0
def forward(mu_expression,
            lmbda_expression,
            rho,
            Lx=10,
            Ly=10,
            t_end=1,
            omega_p=5,
            amplitude=5000,
            center=0,
            target=False):
    Lpml = Lx / 10
    #c_p = cp(mu.vector(), lmbda.vector(), rho)
    max_velocity = 200  #c_p.max()

    stable_hx = stable_dx(max_velocity, omega_p)
    nx = int(Lx / stable_hx) + 1
    #nx = max(nx, 60)
    ny = int(Ly * nx / Lx) + 1
    mesh = mesh_generator(Lx, Ly, Lpml, nx, ny)
    used_hx = Lx / nx
    dt = stable_dt(used_hx, max_velocity)
    cfl_ct = cfl_constant(max_velocity, dt, used_hx)
    print(used_hx, stable_hx)
    print(cfl_ct)
    #time.sleep(10)
    PE = FunctionSpace(mesh, "DG", 0)
    mu = interpolate(mu_expression, PE)
    lmbda = interpolate(lmbda_expression, PE)

    m = 2
    R = 10e-8
    t = 0.0
    gamma = 0.50
    beta = 0.25

    ff = MeshFunction("size_t", mesh, mesh.geometry().dim() - 1)
    Dirichlet(Lx, Ly, Lpml).mark(ff, 1)

    # Create function spaces
    VE = VectorElement("CG", mesh.ufl_cell(), 1, dim=2)
    TE = TensorElement("DG", mesh.ufl_cell(), 0, shape=(2, 2), symmetry=True)

    W = FunctionSpace(mesh, MixedElement([VE, TE]))
    F = FunctionSpace(mesh, "CG", 2)
    V = W.sub(0).collapse()
    M = W.sub(1).collapse()

    alpha_0 = Alpha_0(m, stable_hx, R, Lpml)
    alpha_1 = Alpha_1(alpha_0, Lx, Lpml, degree=2)
    alpha_2 = Alpha_2(alpha_0, Ly, Lpml, degree=2)

    beta_0 = Beta_0(m, max_velocity, R, Lpml)
    beta_1 = Beta_1(beta_0, Lx, Lpml, degree=2)
    beta_2 = Beta_2(beta_0, Ly, Lpml, degree=2)

    alpha_1 = interpolate(alpha_1, F)
    alpha_2 = interpolate(alpha_2, F)
    beta_1 = interpolate(beta_1, F)
    beta_2 = interpolate(beta_2, F)

    a_ = alpha_1 * alpha_2
    b_ = alpha_1 * beta_2 + alpha_2 * beta_1
    c_ = beta_1 * beta_2

    Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]])
    Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]])

    a_ = alpha_1 * alpha_2
    b_ = alpha_1 * beta_2 + alpha_2 * beta_1
    c_ = beta_1 * beta_2

    Lambda_e = as_tensor([[alpha_2, 0], [0, alpha_1]])
    Lambda_p = as_tensor([[beta_2, 0], [0, beta_1]])

    # Set up boundary condition
    bc = DirichletBC(W.sub(0), Constant(("0.0", "0.0")), ff, 1)

    # Create measure for the source term
    dx = Measure("dx", domain=mesh)
    ds = Measure("ds", domain=mesh, subdomain_data=ff)

    # Set up initial values
    u0 = Function(V)
    u0.set_allow_extrapolation(True)
    v0 = Function(V)
    a0 = Function(V)
    U0 = Function(M)
    V0 = Function(M)
    A0 = Function(M)

    # Test and trial functions
    (u, S) = TrialFunctions(W)
    (w, T) = TestFunctions(W)

    g = ModifiedRickerPulse(0, omega_p, amplitude, center)

    F = rho * inner(a_ * N_ddot(u, u0, a0, v0, dt, beta) \
        + b_ * N_dot(u, u0, v0, a0, dt, beta, gamma) + c_ * u, w) * dx \
        + inner(N_dot(S, U0, V0, A0, dt, beta, gamma).T * Lambda_e + S.T * Lambda_p, grad(w)) * dx \
        - inner(g, w) * ds \
        + inner(compliance(a_ * N_ddot(S, U0, A0, V0, dt, beta) + b_ * N_dot(S, U0, V0, A0, dt, beta, gamma) + c_ * S, u, mu, lmbda), T) * dx \
        - 0.5 * inner(grad(u) * Lambda_p + Lambda_p * grad(u).T + grad(N_dot(u, u0, v0, a0, dt, beta, gamma)) * Lambda_e \
        + Lambda_e * grad(N_dot(u, u0, v0, a0, dt, beta, gamma)).T, T) * dx \

    a, L = lhs(F), rhs(F)

    # Assemble rhs (once)
    A = assemble(a)

    # Create GMRES Krylov solver
    solver = KrylovSolver(A, "gmres")

    # Create solution function
    S = Function(W)

    if target:
        xdmffile_u = XDMFFile("inversion_temporal_file/target/u.xdmf")
        pvd = File("inversion_temporal_file/target/u.pvd")
        xdmffile_u.write(u0, t)
        timeseries_u = TimeSeries(
            "inversion_temporal_file/target/u_timeseries")
    else:
        xdmffile_u = XDMFFile("inversion_temporal_file/obs/u.xdmf")
        xdmffile_u.write(u0, t)
        timeseries_u = TimeSeries("inversion_temporal_file/obs/u_timeseries")

    rec_counter = 0

    while t < t_end - 0.5 * dt:
        t += float(dt)

        if rec_counter % 10 == 0:
            print(
                '\n\rtime: {:.3f} (Progress: {:.2f}%)'.format(
                    t, 100 * t / t_end), )

        g.t = t

        # Assemble rhs and apply boundary condition
        b = assemble(L)
        bc.apply(A, b)

        # Compute solution
        solver.solve(S.vector(), b)
        (u, U) = S.split(True)

        # Update previous time step
        update(u, u0, v0, a0, beta, gamma, dt)
        update(U, U0, V0, A0, beta, gamma, dt)

        xdmffile_u.write(u, t)
        pvd << (u, t)
        timeseries_u.store(u.vector(), t)

        energy = inner(u, u) * dx
        E = assemble(energy)
        print("E = ", E)
        print(u.vector().max())
Exemplo n.º 30
0
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
Exemplo n.º 31
0
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
Exemplo n.º 32
0
class OasisFunction(Function):
    """Function with more or less efficient projection methods 
    of associated linear form.
    
    The matvec option is provided for letting the right hand side 
    be computed through a fast matrix vector product. Both the matrix 
    and the Coefficient of the required vector must be provided.
    
      method = "default"
        Solve projection with regular linear algebra using solver_type
        and preconditioner_type
        
      method = "lumping"
        Solve through lumping of mass matrix 
      
    """
    def __init__(self, form, Space, bcs=[], 
                 name="x", 
                 matvec=[None, None], 
                 method="default", 
                 solver_type="cg", 
                 preconditioner_type="default"):
        
        Function.__init__(self, Space, name=name)
        self.form = form
        self.method = method
        self.bcs = bcs
        self.matvec = matvec
        self.trial = trial = TrialFunction(Space)
        self.test = test = TestFunction(Space)
        Mass = inner(trial, test)*dx()
        self.bf = inner(form, test)*dx()
        self.rhs = Vector(self.vector())
        
        if method.lower() == "default":
            self.A = A_cache[(Mass, tuple(bcs))]
            self.sol = KrylovSolver(solver_type, preconditioner_type)
            self.sol.parameters["preconditioner"]["structure"] = "same"
            self.sol.parameters["error_on_nonconvergence"] = False
            self.sol.parameters["monitor_convergence"] = False
            self.sol.parameters["report"] = False
                
        elif method.lower() == "lumping":
            assert Space.ufl_element().degree() < 2
            self.A = A_cache[(Mass, tuple(bcs))]
            ones = Function(Space)
            ones.vector()[:] = 1.
            self.ML = self.A * ones.vector()
            self.ML.set_local(1. / self.ML.array())
                        
    def assemble_rhs(self):
        """
        Assemble right hand side (form*test*dx) in projection 
        """
        if not self.matvec[0] is None:
            mat, func = self.matvec
            self.rhs.zero()
            self.rhs.axpy(1.0, mat*func.vector())
        
        else:
            assemble(self.bf, tensor=self.rhs)
                
    def __call__(self, assemb_rhs=True):
        """
        Compute the projection
        """
        timer = Timer("Projecting {}".format(self.name()))
        
        if assemb_rhs: 
            self.assemble_rhs()
        
        for bc in self.bcs:
            bc.apply(self.rhs)
            
        if self.method.lower() == "default":
            self.sol.solve(self.A, self.vector(), self.rhs)
            
        else:
            self.vector()[:] = self.rhs * self.ML
Exemplo n.º 33
0
    F = rho * inner(a_ * N_ddot(u, u0, a0, v0, dt, beta) \
        + b_ * N_dot(u, u0, v0, a0, dt, beta, gamma) + c_ * u, w) * dx \
        + inner(N_dot(S, U0, V0, A0, dt, beta, gamma).T * Lambda_e + S.T * Lambda_p, grad(w)) * dx \
        - inner(g, w) * ds \
        + inner(compliance(a_ * N_ddot(S, U0, A0, V0, dt, beta) + b_ * N_dot(S, U0, V0, A0, dt, beta, gamma) + c_ * S, u, mu, lmbda), T) * dx \
        - 0.5 * inner(grad(u) * Lambda_p + Lambda_p * grad(u).T + grad(N_dot(u, u0, v0, a0, dt, beta, gamma)) * Lambda_e \
        + Lambda_e * grad(N_dot(u, u0, v0, a0, dt, beta, gamma)).T, T) * dx \

    a, L = lhs(F), rhs(F)

    # Assemble rhs (once)
    A = assemble(a)

    # Create GMRES Krylov solver
    solver = KrylovSolver(A, "gmres")

    # Create solution function
    S = Function(W)

    experiment_count_file = open("experiment_counter", 'rb')
    experiment_count = pickle.load(experiment_count_file)
    experiment_count_file.close()

    paraview_file_name = "experiment_{}".format(experiment_count)
    info_file_name = "{}_experiments_info/info_n{}.txt".format(
        type_of_medium, experiment_count)

    experiment_count += 1
    experiment_count_file = open("experiment_counter", 'wb')
Exemplo n.º 34
0
    class _FEM_Base(object):
        MAX_ITER = 10000

        def __init__(self, mesh_path=None):
            self.global_preprocessing_time = Stopwatch()
            self.local_preprocessing_time = Stopwatch()
            self.solving_time = Stopwatch()

            if mesh_path is not None:
                self.PATH = mesh_path

            logger.debug('Loading mesh...')

            # with XDMFFile(self.PATH + '.xdmf') as fh:
            #     self._mesh = Mesh()
            #     fh.read(self._mesh)
            #
            # with XDMFFile(self.PATH + '_boundaries.xdmf') as fh:
            #     mvc = MeshValueCollection("size_t", self._mesh, 2)
            #     fh.read(mvc, "boundaries")
            #     self._boundaries = cpp.mesh.MeshFunctionSizet(self._mesh, mvc)
            #
            # with XDMFFile(self.PATH + '_subdomains.xdmf') as fh:
            #     mvc = MeshValueCollection("size_t", self._mesh, 3)
            #     fh.read(mvc, "subdomains")
            #     self._subdomains = cpp.mesh.MeshFunctionSizet(self._mesh, mvc)

            self._mesh = Mesh(self.PATH + '.xml')
            self._subdomains = MeshFunction("size_t", self._mesh,
                                            self.PATH + '_physical_region.xml')
            self._boundaries = MeshFunction("size_t", self._mesh,
                                            self.PATH + '_facet_region.xml')

            logger.debug('Done.')
            self._degree = None

        def _change_degree(self, degree, *args, **kwargs):
            gc.collect()

            with self.global_preprocessing_time:
                logger.debug('Creating function space...')
                self._V = FunctionSpace(self._mesh, "CG", degree)
                logger.debug('Done.  Creating integration subdomains...')
                self.create_integration_subdomains()
                logger.debug('Done.  Creating test function...')
                self._v = TestFunction(self._V)
                logger.debug('Done.  Creating potential function...')
                self._potential_function = Function(self._V)
                logger.debug('Done.  Creating trial function...')
                self._potential_trial = TrialFunction(self._V)
                logger.debug('Done.  Creating LHS part of equation...')
                self._a = self._lhs()
                logger.debug('Done.  Assembling linear equation matrix...')
                self._terms_with_unknown = assemble(self._a)
                logger.debug('Done.  Defining boundary condition...')
                self._dirichlet_bc = self._boundary_condition(*args, **kwargs)
                logger.debug('Done.  Applying boundary condition to the matrix...')
                self._dirichlet_bc.apply(self._terms_with_unknown)

                logger.debug('Done.  Creating solver...')
                self._solver = KrylovSolver("cg", "ilu")
                self._solver.parameters["maximum_iterations"] = self.MAX_ITER
                self._solver.parameters["absolute_tolerance"] = 1E-8
                logger.debug('Done.  Solver created.')

            self._degree = degree

        def create_integration_subdomains(self):
            self._dx = Measure("dx")(subdomain_data=self._subdomains)

        def __call__(self, degree, *args, **kwargs):
            if degree != self._degree:
                self._change_degree(degree, *args, **kwargs)

            gc.collect()
            with self.local_preprocessing_time:
                L = self._rhs(degree, *args, **kwargs)
                logger.debug('Done.  Assembling linear equation vector...')
                known_terms = assemble(L)
                logger.debug('Done.  Applying boundary condition to the vector...')
                self._dirichlet_bc.apply(known_terms)
                logger.debug('Done.')

            try:
                logger.debug('Solving linear equation...')
                gc.collect()
                with self.solving_time:
                    self._solve(known_terms)

                logger.debug('Done.')
                return self._potential_function

            except RuntimeError as e:
                self.iterations = self.MAX_ITER
                logger.warning("Solver failed: {}".format(repr(e)))
                return None

        def _rhs(self, degree, *args, **kwargs):
            logger.debug('Creating CSD expression...')
            csd = self._make_csd(degree, *args, **kwargs)
            logger.debug('Done.  Normalizing...')
            self.a = csd.a = self._csd_normalization_factor(csd)
            logger.debug('Done.  Creating RHS part of equation...')
            return csd * self._v * self._dx

        def _solve(self, known_terms):
            self.iterations = self._solver.solve(
                                              self._terms_with_unknown,
                                              self._potential_function.vector(),
                                              known_terms)
Exemplo n.º 35
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
    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