Example #1
0
    def __init__(self, *args, **kwargs):
        "Create Dirichlet boundary condition"

        # Copy constructor
        if len(args) == 1:
            if not isinstance(args[0], cpp.DirichletBC):
                cpp.dolfin_error("bcs.py",
                                 "create DirichletBC",
                                 "Expecting a DirichleBC as only argument"\
                                 " for copy constructor")

            # Initialize base class
            cpp.DirichletBC.__init__(self, args[0])
            return

        # Special case for value specified as float, tuple or similar
        if len(args) >= 2 and not isinstance(args[1], cpp.GenericFunction):
            if isinstance(args[1], ufl.classes.Expr):
                expr = project(args[1], args[0])
            else:
                expr = Constant(args[1]) # let Constant handle all problems
            args = args[:1] + (expr,) + args[2:]

        # Special case for sub domain specified as a function
        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            sub_domain = AutoSubDomain(args[2])
            args = args[:2] + (sub_domain,) + args[3:]

        # Special case for sub domain specified as a string
        if len(args) >= 3 and isinstance(args[2], str):
            sub_domain = compile_subdomains(args[2])
            args = args[:2] + (sub_domain,) + args[3:]

        # Store Expression to avoid scoping issue with SWIG directors
        if isinstance(args[1], cpp.Expression):
            self.function_arg = args[1]

        # Store SubDomain to avoid scoping issue with SWIG directors
        self.domain_args = args[2:]

        # FIXME: Handling of multiple default arguments does not
        # really work between Python and C++ when C++ requires them to
        # be ordered and cannot accept the latter without the
        # former...

        # Add keyword arguments (in correct order...)
        allowed_kwargs = ["method", "check_midpoint"]
        for key in allowed_kwargs:
            if key in kwargs:
                args = tuple(list(args) + [kwargs[key]])

        # Check for other keyword arguments
        for key in kwargs:
            if not key in allowed_kwargs:
                cpp.dolfin_error("bcs.py",
                                 "create boundary condition",
                                 "Unknown keyword argument \"%s\"" % key)

        # Initialize base class
        cpp.DirichletBC.__init__(self, *args)
Example #2
0
    def __init__(self, *args, **kwargs):
        "Create Dirichlet boundary condition"

        # Copy constructor
        if len(args) == 1:
            if not isinstance(args[0], cpp.DirichletBC):
                cpp.dolfin_error("bcs.py",
                                 "create DirichletBC",
                                 "Expecting a DirichleBC as only argument"\
                                 " for copy constructor")

            # Initialize base class
            cpp.DirichletBC.__init__(self, args[0])
            self.domain_args = args[0].domain_args
            return

        # Special case for value specified as float, tuple or similar
        if len(args) >= 2 and not isinstance(args[1], cpp.GenericFunction):
            if isinstance(args[1], ufl.classes.Expr):
                expr = project(args[1], args[0])
            else:
                expr = Constant(args[1])  # let Constant handle all problems
            args = args[:1] + (expr, ) + args[2:]

        # Special case for sub domain specified as a function
        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            sub_domain = AutoSubDomain(args[2])
            args = args[:2] + (sub_domain, ) + args[3:]

        # Special case for sub domain specified as a string
        if len(args) >= 3 and isinstance(args[2], str):
            sub_domain = CompiledSubDomain(args[2])
            args = args[:2] + (sub_domain, ) + args[3:]

        # Store Expression to avoid scoping issue with SWIG directors
        if isinstance(args[1], cpp.Expression):
            self.function_arg = args[1]

        # Store SubDomain to avoid scoping issue with SWIG directors
        self.domain_args = args[2:]

        # FIXME: Handling of multiple default arguments does not
        # really work between Python and C++ when C++ requires them to
        # be ordered and cannot accept the latter without the
        # former...

        # Add keyword arguments (in correct order...)
        allowed_kwargs = ["method", "check_midpoint"]
        for key in allowed_kwargs:
            if key in kwargs:
                args = tuple(list(args) + [kwargs[key]])

        # Check for other keyword arguments
        for key in kwargs:
            if not key in allowed_kwargs:
                cpp.dolfin_error("bcs.py", "create boundary condition",
                                 "Unknown keyword argument \"%s\"" % key)

        # Initialize base class
        cpp.DirichletBC.__init__(self, *args)
def compute_projection_error(mesh_resolution=20, p_order=1):
    # Define domain and mesh
    a, b = 0, 1
    mesh = IntervalMesh(mesh_resolution, a, b)

    # Define finite element function space
    V = FunctionSpace(mesh, "CG", p_order)

    # Extract vertices of the mesh
    x = V.tabulate_dof_coordinates()
    # Note: Not the same as x = mesh.coordinates() (apparently has been re-ordered)

    # Express the analytical function
    u = Expression("1 + 4 * x[0] * x[0] - 5 * x[0] * x[0] * x[0]", degree=5)

    # Project u onto V and extract the values in the mesh nodes
    Pu = project(u, V)
    Pua = Pu.vector().array()

    # Create a function in the finite element function space V
    Eu = Function(V)
    Eua = Eu.vector().array()

    # Evaluate function in the mesh nodes
    for i in range(len(x)):
        Eua[i] = 1 + 4 * x[i]**2 - 5 * x[i]**3

    # Compute sum of projection error in the nodes
    e = Eua - Pua
    error = 0
    for i in range(len(e)):
        error += abs(e[i])
    return error
def compute_projection_error(mesh_resolution=20, p_order=1):
    # Define domain and mesh
    a, b = 0, 1
    mesh = IntervalMesh(mesh_resolution, a, b)

    # Define finite element function space
    V = FunctionSpace(mesh, "CG", p_order)

    # Extract vertices of the mesh
    x = V.dofmap().tabulate_all_coordinates(mesh)
    indices = np.argsort(x)

    # Express the analytical function
    u = Expression("1 + 4 * x[0] * x[0] - 5 * x[0] * x[0] * x[0]", degree=5)

    # Project u onto V and extract the values in the mesh nodes
    Pu = project(u, V)
    Pua = Pu.vector().array()

    # Create a function in the finite element function space V
    Eu = Function(V)
    Eua = Eu.vector().array()

    # Evaluate function in the mesh nodes
    for j in indices:
        Eua[j] = 1 + 4 * x[j] * x[j] - 5 * x[j] * x[j] * x[j]

    # Compute sum of projection error in the nodes
    e = Eua - Pua
    error = 0
    for i in range(len(e)):
        error += abs(e[i])
    return error
def compare_errors(p_order=1):
    # Express the analytical function
    u = Expression("1 + sin(10*x[0])", degree=5)

    mesh_resolutions = [10, 50, 100, 200]

    projection_errors = []
    interpolation_errors = []
    for mesh_resolution in mesh_resolutions:
        # Define mesh
        mesh = UnitIntervalMesh(mesh_resolution)

        # Define finite element function space
        V = FunctionSpace(mesh, "CG", p_order)

        # Compute projection
        up = project(u, V)

        # Compute interpolation
        ui = interpolate(u, V)

        # Compute errors
        projection_errors.append(compute_error(u, up))
        interpolation_errors.append(compute_error(u, ui))

    print(projection_errors)
    print(interpolation_errors)
Example #6
0
    def computeError4level(self, meanApprox4lvl, lastSpace, meanExact = None):
        if meanExact == None:
            solMesh = lastSpace.mesh()
            solFamily = lastSpace.ufl_element().family()
            solDegree = lastSpace.ufl_element().degree()
            refSpace = FunctionSpace(refine(refine(solMesh)), solFamily, solDegree)
            meanExact = self.solveMean(refSpace)

        refSpace = meanExact.function_space()
        error4lvl = list(
                np.sqrt(assemble(
                    (project(meanApprox, refSpace) - interpolate(meanExact, refSpace)) ** 2 * dx
                )) for meanApprox in meanApprox4lvl)

        return error4lvl
Example #7
0
File: bcs.py Project: alogg/dolfin
    def __init__(self, *args, **kwargs):
        "Create Dirichlet boundary condition"

        # Copy constructor
        if len(args) == 1:
            if not isinstance(args[0], cpp.DirichletBC):
                cpp.dolfin_error("bcs.py",
                                 "create DirichletBC",
                                 "Expecting a DirichleBC as only argument"\
                                 " for copy constructor")

            # Initialize base class
            cpp.DirichletBC.__init__(self, args[0])
            return

        # Special case for value specified as float, tuple or similar
        if len(args) >= 2 and not isinstance(args[1], cpp.GenericFunction):
            if isinstance(args[1], ufl.classes.Expr):
                expr = project(args[1], args[0])
            else:
                expr = Constant(args[1]) # let Constant handle all problems
            args = args[:1] + (expr,) + args[2:]

        # Special case for sub domain specified as a function
        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            sub_domain = AutoSubDomain(args[2])
            args = args[:2] + (sub_domain,) + args[3:]

        # Special case for sub domain specified as a string
        if len(args) >= 3 and isinstance(args[2], str):
            sub_domain = compile_subdomains(args[2])
            args = args[:2] + (sub_domain,) + args[3:]

        # Store Expression to avoid scoping issue with SWIG directors
        if isinstance(args[1], cpp.Expression):
            self.function_arg = args[1]

        # Store SubDomain to avoid scoping issue with SWIG directors
        self.domain_args = args[2:]

        # Add method argument if it's given
        if "method" in kwargs:
            args = tuple(list(args) + [kwargs["method"]])

        # Initialize base class
        cpp.DirichletBC.__init__(self, *args)
Example #8
0
def plot(object, *args, **kwargs):
    """
    Plot given object.

    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC> or a :py:class:`FiniteElement
            <ufl.FiniteElement>`.

    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use

        .. code-block:: python

            mesh = UnitSquare(4,4)
            plot(mesh)

        Use the ``title`` argument to specify title of the plot

        .. code-block:: python

            plot(mesh, tite="Finite element mesh")

        It is also possible to plot an element

        .. code-block:: python

            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)

        Vector valued functions can be visualized with an alternative mode

        .. code-block:: python

            plot(u, mode = "glyphs")

        A more advanced example

        .. code-block:: python

            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0                    # scale the warping/glyphs
                 title = "Fancy plot"           # Set your own title
                 )

    """

    # Plot element
    if isinstance(object, ufl.FiniteElementBase):
        if os.environ.get("DOLFIN_NOPLOT", "0") != "0":
            return
        import ffc
        return ffc.plot(object, *args, **kwargs)

    # Get mesh from explicit mesh kwarg, only positional arg, or via object
    mesh = kwargs.pop('mesh', None)
    if isinstance(object, cpp.Mesh):
        if mesh is not None and mesh.id() != object.id():
            cpp.dolfin_error(
                "plotting.py", "plot mesh",
                "Got different mesh in plot object and keyword argument")
        mesh = object
    if mesh is None:
        if isinstance(object, cpp.Function):
            mesh = object.function_space().mesh()
        elif hasattr(object, "mesh"):
            mesh = object.mesh()

    # Expressions do not carry their own mesh
    if isinstance(object, cpp.Expression) and mesh is None:
        cpp.dolfin_error("plotting.py", "plot expression",
                         "Expecting a mesh as keyword argument")

    # Try to project if object is not a standard plottable type
    if not isinstance(object, _plottable_types):
        from dolfin.fem.projection import project
        try:
            cpp.info("Object cannot be plotted directly, projecting to"\
                     " piecewise linears.")
            object = project(object, mesh=mesh)
        except Exception as e:
            msg = "Don't know how to plot given object:\n  %s\n" \
                  "and projection failed:\n  %s" % (str(object), str(e))
            cpp.dolfin_error("plotting.py", "plot object", msg)

    # Select backend
    backend = cpp.parameters["plotting_backend"]
    if backend == "vtk":
        return _plot_cpp(object, mesh, kwargs)
    elif backend == "matplotlib":
        return _plot_matplotlib(object, mesh, kwargs)
Example #9
0
###

pvdFileU = File(outputFolder+"/u.pvd", "compressed")
pvdFileV = File(outputFolder+"/v.pvd", "compressed")
pvdFileStress = File(outputFolder+"/stress.pvd", "compressed")

## Save sub domains to VTK files
#bndFile = File(outputFolder+"/subdomains.pvd")
#bndFile << neumannSubdomains

###
### Solver
###

# Class representing the initial conditions
u0 = project(physics.u0, V)
v0 = project(physics.v0, V)

# Initial condition
tk = physics.t0
u = u0
v = v0

# Source
physics.f.t = tk
timef = False
fk = assemble(inner(physics.f, w)*dx)
if isinstance(physics.f, Expression):
	if 't' in physics.f.user_parameters: timef = True

# Neumann boundary condition
Example #10
0
def solve_navier_stokes_equation(interior_circle=True, num_mesh_refinements=0):
    """
    Solve the Navier-Stokes equation on a hard-coded mesh with hard-coded initial and boundary conditions
    """
    mesh, om, im, nm, ymax, sub_domains = setup_geometry(
        interior_circle, num_mesh_refinements)
    dsi = Measure("ds", domain=mesh, subdomain_data=sub_domains)

    # Setup FEM function spaces
    # Function space for the velocity
    V = VectorFunctionSpace(mesh, "CG", 1)
    # Function space for the pressure
    Q = FunctionSpace(mesh, "CG", 1)
    # Mixed function space for velocity and pressure
    W = V * Q

    # Setup FEM functions
    v, q = TestFunctions(W)
    w = Function(W)
    (u, p) = (as_vector((w[0], w[1])), w[2])
    u0 = Function(V)

    # Inlet velocity
    uin = Expression(("4*(x[1]*(YMAX-x[1]))/(YMAX*YMAX)", "0."),
                     YMAX=ymax,
                     degree=1)

    # Viscosity and stabilization parameters
    nu = 1e-6
    h = CellSize(mesh)
    d = 0.2 * h**(3.0 / 2.0)

    # Time parameters
    time_step = 0.1
    t_start, t_end = 0.0, 10.0

    # Penalty parameter
    gamma = 10 / h

    # Time stepping
    t = t_start
    step = 0
    while t < t_end:
        # Time discretization (Crank–Nicolson method)
        um = 0.5 * u + 0.5 * u0

        # Navier-Stokes equations in weak residual form (stabilized FEM)
        # Basic residual
        r = (inner((u - u0) / time_step + grad(p) + grad(um) * um, v) +
             nu * inner(grad(um), grad(v)) + div(um) * q) * dx
        # Weak boundary conditions
        r += gamma * (om * p * q + im * inner(u - uin, v) +
                      nm * inner(u, v)) * ds
        # Stabilization
        r += d * (inner(grad(p) + grad(um) * um,
                        grad(q) + grad(um) * v) + inner(div(um), div(v))) * dx

        # Solve the Navier-Stokes equation (one time step)
        solve(r == 0, w)

        if step % 5 == 0:
            # Plot norm of velocity at current time step
            nov = project(sqrt(inner(u, u)), Q)
            fig = plt.figure()
            plot(nov, fig=fig)
            plt.show()

            # Compute drag force on circle
            n = FacetNormal(mesh)
            drag_force_measure = p * n[0] * dsi(1)  # Drag (only pressure)
            drag_force = assemble(drag_force_measure)
            print("Drag force = " + str(drag_force))

        # Shift to next time step
        t += time_step
        step += 1
        u0 = project(u, V)
Example #11
0
def plot(object, *args, **kwargs):
    """
    Plot given object.

    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC>, a :py:class:`FiniteElement
            <ufl.FiniteElement>`.

    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use

        .. code-block:: python

            mesh = UnitSquare(4, 4)
            plot(mesh)

        Use the ``title`` argument to specify title of the plot

        .. code-block:: python

            plot(mesh, tite="Finite element mesh")

        It is also possible to plot an element

        .. code-block:: python

            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)

        Vector valued functions can be visualized with an alternative mode

        .. code-block:: python

            plot(u, mode = "glyphs")

        A more advanced example

        .. code-block:: python

            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0,                   # scale the warping/glyphs
                 title = "Fancy plot",          # set your own title
                 )

    """

    # Return if plotting is disables
    if os.environ.get("DOLFIN_NOPLOT", "0") != "0":
        return

    # Return if Matplotlib is not available
    if not _has_matplotlib():
        cpp.log.info("Matplotlib is required to plot from Python.")
        return

    # Plot element
    if isinstance(object, ufl.FiniteElementBase):
        import ffc
        return ffc.plot(object, *args, **kwargs)

    # For dolfin.function.Function, extract cpp_object
    if hasattr(object, "cpp_object"):
        object = object.cpp_object()

    # Get mesh from explicit mesh kwarg, only positional arg, or via
    # object
    mesh = kwargs.pop('mesh', None)
    if isinstance(object, cpp.mesh.Mesh):
        if mesh is not None and mesh.id() != object.id():
            raise RuntimeError(
                "Got different mesh in plot object and keyword argument")
        mesh = object
    if mesh is None:
        if isinstance(object, cpp.function.Function):
            mesh = object.function_space().mesh()
        elif hasattr(object, "mesh"):
            mesh = object.mesh()

    # Expressions do not carry their own mesh
    if isinstance(object, cpp.function.Expression) and mesh is None:
        raise RuntimeError("Expecting a mesh as keyword argument")

    backend = kwargs.pop("backend", "matplotlib")
    if backend not in ("matplotlib", "x3dom"):
        raise RuntimeError("Plotting backend %s not recognised" % backend)

    # Try to project if object is not a standard plottable type
    if not isinstance(object, _all_plottable_types):
        from dolfin.fem.projection import project
        try:
            cpp.log.info("Object cannot be plotted directly, projecting to "
                         "piecewise linears.")
            object = project(object, mesh=mesh)
            mesh = object.function_space().mesh()
            object = object._cpp_object
        except Exception as e:
            msg = "Don't know how to plot given object:\n  %s\n" \
                  "and projection failed:\n  %s" % (str(object), str(e))
            raise RuntimeError(msg)

    # Plot
    if backend == "matplotlib":
        return _plot_matplotlib(object, mesh, kwargs)
    elif backend == "x3dom":
        return _plot_x3dom(object, kwargs)
    else:
        assert False, "This code should not be reached."
def solve_heat_equation(k, time_stepping_method):
    """
    Solve the heat equation on a hard-coded mesh with a hard-coded initial and boundary conditions

    :param k: Thermal conductivity
    :param time_stepping_method: Time stepping method. Can be one of ["forward_euler", "backward_euler", "trapezoidal"]
    """
    mesh, boundary = setup_geometry()

    # Exact solution (Gauss curve)
    ue = Expression("exp(-(x[0]*x[0]+x[1]*x[1])/(4*a*t))/(4*pi*a*t)",
                    a=k,
                    t=1e-7,
                    domain=mesh,
                    degree=1)

    # Polynomial degree
    r = 1

    # Setup FEM function space
    V = FunctionSpace(mesh, "CG", r)

    # Create boundary condition
    bc = DirichletBC(V, ue, boundary)

    # Setup FEM functions
    v = TestFunction(V)
    u = Function(V)

    # Time parameters
    time_step = 0.001
    t_start, t_end = 0.0, 20.0

    # Time stepping
    t = t_start
    if time_stepping_method == "forward_euler":
        theta = 0.0
    if time_stepping_method == "backward_euler":
        theta = 1.0
    if time_stepping_method == "trapezoidal":
        theta = 0.5
    u0 = ue
    step = 0
    while t < t_end:
        # Intermediate value for u (depending on the chosen time stepping method)
        um = (1.0 - theta) * u0 + theta * u

        # Weak form of the heat equation
        a = (u - u0) / time_step * v * dx + k * inner(grad(um), grad(v)) * dx

        # Solve the heat equation (one time step)
        solve(a == 0, u, bc)

        # Advance time in exact solution
        t += time_step
        ue.t = t

        if step % 100 == 0:
            # Compute error in L2 norm
            error_L2 = errornorm(ue, u, 'L2')
            # or equivalently
            # sqrt(assemble((ue - u) * (ue - u) * dx))
            # Compute norm of exact solution
            nue = norm(ue)
            # Print relative error
            print("Relative error = {}".format(error_L2 / nue))

        # Shift to next time step
        u0 = project(u, V)
        step += 1
Example #13
0
def plot(object, *args, **kwargs):
    """
    Plot given object.

    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC> or a :py:class:`FiniteElement
            <ufl.FiniteElement>`.

    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use

        .. code-block:: python

            mesh = UnitSquare(4,4)
            plot(mesh)

        Use the ``title`` argument to specify title of the plot

        .. code-block:: python

            plot(mesh, tite="Finite element mesh")

        It is also possible to plot an element

        .. code-block:: python

            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)

        Vector valued functions can be visualized with an alternative mode

        .. code-block:: python

            plot(u, mode = "glyphs")

        A more advanced example

        .. code-block:: python

            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0                    # scale the warping/glyphs
                 title = "Fancy plot"           # Set your own title
                 )

    """

    mesh = kwargs.get('mesh')

    p = cpp.Parameters()
    for key in kwargs:
        # If there is a "mesh" kwarg it should not be added to the parameters
        if key != "mesh":
            try:
                p.add(key, kwargs[key])
            except TypeError:
                cpp.warning("Incompatible type for keyword argument \"%s\". Ignoring." % key)

    # Plot element
    if isinstance(object, ufl.FiniteElementBase):
        if os.environ.get("DOLFIN_NOPLOT", "0") != "0": return
        import ffc
        return ffc.plot(object, *args, **kwargs)

    if mesh is None and len(args) == 1 and isinstance(args[0], cpp.Mesh):
        mesh = args[0]

    # Plot expression
    if isinstance(object, cpp.Expression):
        if mesh is None:
            raise TypeError, "expected a mesh when plotting an expression."
        return cpp.plot(object, mesh, p)

    # Try to project if object is not a standard plottable type
    if not isinstance(object, (cpp.Function, cpp.Expression, cpp.Mesh,
        cpp.DirichletBC, cpp.MeshFunction, cpp.MeshFunctionBool,
        cpp.MeshFunctionInt, cpp.MeshFunctionDouble,
        cpp.MeshFunctionSizet, cpp.DirichletBC, cpp.CSGGeometry)):

        from dolfin.fem.projection import project
        try:
            cpp.info("Object cannot be plotted directly, projecting to"\
                    " piecewise linears.")
            object = project(object, mesh=mesh)
        except Exception as e:
            msg = ("Don't know how to plot given object:\n  %s\n"\
                    "and projection failed:\n  %s") % (str(object), str(e))
            #raise RuntimeError(msg)
            raise

    plot_object = cpp.plot(object, p)
    plot_object.write_ps = _VTKPlotter_write_ps

    # Avoid premature deletion of plotted objects if they go out of scope
    # before the plot window is closed. The plotter itself is safe, since it's
    # created in the plot() C++ function, not directly from Python. But the
    # Python plotter proxy may disappear, so we can't store the references
    # there.
    global _objects_referenced_from_plot_windows
    _objects_referenced_from_plot_windows[plot_object.key()] = (object, mesh, p)

    return plot_object
    for i in range(nPsi)
]

###
### Solver
###

# Timestep
dt = (timeInterval.tf - physics.t0) / timeInterval.nt

# Dirichlet boundary condition
dirichletBC = {}
if 'dirichletBC' in dir(physics):
    for i in physics.dirichletBC:
        physics.dirichletBC[i].t = physics.t0
        ug = project(physics.dirichletBC[i], V)
        dirichletBC[i] = DirichletBC(V, ug, globalBndSubdomains, i)
        dirichletBC[i].apply(M)
        dirichletBC[i].apply(R)

# Preparing solvers
solverM = LUSolver(M)
solverMdtR = LUSolver(M + newmark.beta * dt * dt * R)
solverM.parameters['reuse_factorization'] = True
solverMdtR.parameters['reuse_factorization'] = True

# Psi loop
for iPsi in range(nPsi):

    if verbose: print("Psi: ", iPsi)
Example #15
0
    def __init__(self, *args, **kwargs):
        if len(args) == 1:
            if not isinstance(args[0], cpp.fem.MultiMeshDirichletBC):
                raise RuntimeError(
                    "Expecting a MultiMeshDirichletBC as only argument for copy constructor"
                )
            cpp.fem.MultiMeshDirichletBC.__init__(self, args[0])
            return
        if not isinstance(args[0],
                          (MultiMeshFunctionSpace, MultiMeshSubSpace)):
            raise RuntimeError(
                "Expecting MultiMeshFunctionSpace or MultiMeshSubSpace as first argument"
            )

        if len(args) >= 2:
            # Check if we have an UFL-expression or a concrete type
            if not hasattr(args[1], "_cpp_object"):
                if isinstance(args[1], ufl.classes.Expr):
                    expr = project(args[1], args[0])  # Should be interpolation
                else:
                    expr = Constant(args[1])
                args = args[:1] + (expr, 1) + args[2:]
        if isinstance(args[1], (float, int)):
            u = cpp.function.Constant(float(args[1]))
        elif isinstance(args[1], ufl.Coefficient):
            u = args[1]._cpp_object
        elif isinstance(args[1], cpp.function.GenericFunction):
            u = args[1]
        else:
            raise RuntimeError(
                "Second argument must be convertiable to a GenericFucntion")

        if isinstance(args[0], MultiMeshSubSpace):
            args = args[:1] + (u, ) + args[2:]

        else:
            args = args[:1] + (u, ) + args[2:]
            args = (args[0]._cpp_object, ) + args[1:]

        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            raise NotImplementedError(
                "User-specified subdomains not implemented")
        if isinstance(args[2], cpp.mesh.SubDomain):
            self.sub_domain = args[2]
            args = args[:2] + (self.sub_domain, ) + args[3:]
        elif isinstance(args[2], str):
            raise NotImplementedError(
                "User-specified subdomains not implemented")
        elif isinstance(args[2], cpp.mesh.MeshFunctionSizet):
            pass
        else:
            raise RuntimeError("Invalid argument")

        # Add kwargs
        if isinstance(args[-1], str):
            method = args[-1]
        else:
            method = kwargs.pop("method", "topological")
            args += (method, )
        check_midpoint = kwargs.pop("check_midpoint", None)
        if check_midpoint is not None:
            args += (check_midpoint, )

        if (len(kwargs) > 0):
            raise RuntimeError("Invalid keyword arguments", kwargs)

        super().__init__(*args)
Example #16
0
def solve_wave_equation(a, symmetric=True):
    """
    Solve the wave equation on a hard-coded mesh with a hard-coded initial and boundary conditions

    :param a: Wave propagation factor
    :param symmetric: Whether or not the problem is symmetric
    """
    mesh, boundary = setup_geometry()

    # Exact solution
    if symmetric:
        ue = Expression(
            "(1-pow(a*t-x[0],2))*exp(-pow(a*t-x[0],2)) + (1-pow(a*t+x[0],2))*exp(-pow(a*t+x[0],2))",
            a=a,
            t=0,
            domain=mesh,
            degree=2)
        ve = Expression(
            "2*a*(a*t-x[0])*(pow(a*t-x[0],2)-2)*exp(-pow(a*t-x[0],2))"
            "+ 2*a*(a*t+x[0])*(pow(a*t+x[0],2)-2)*exp(-pow(a*t+x[0],2))",
            a=a,
            t=0,
            domain=mesh,
            degree=2)
    else:
        ue = Expression("(1-pow(a*t+x[0],2))*exp(-pow(a*t+x[0],2))",
                        a=a,
                        t=0,
                        domain=mesh,
                        degree=2)
        ve = Expression(
            "2*a*(a*t+x[0])*(pow(a*t+x[0],2)-2)*exp(-pow(a*t+x[0],2))",
            a=a,
            t=0,
            domain=mesh,
            degree=2)

    # Polynomial degree
    r = 1

    # Setup FEM function spaces
    Q = FunctionSpace(mesh, "CG", r)
    W = VectorFunctionSpace(mesh, "CG", r, dim=2)

    # Create boundary conditions
    bcu = DirichletBC(W.sub(0), ue, boundary)
    bcv = DirichletBC(W.sub(1), ve, boundary)
    bcs = [bcu, bcv]

    # Setup FEM functions
    p, q = TestFunctions(W)
    w = Function(W)
    u, v = w[0], w[1]

    # Time parameters
    time_step = 0.05
    t_start, t_end = 0.0, 5.0

    # Time stepping
    t = t_start
    u0 = ue
    v0 = ve
    step = 0
    while t < t_end:
        # Weak form of the wave equation
        um = 0.5 * (u + u0)
        vm = 0.5 * (v + v0)
        a1 = (u - u0) / time_step * p * dx - vm * p * dx
        a2 = (v - v0) / time_step * q * dx + a**2 * inner(grad(um),
                                                          grad(q)) * dx

        # Solve the wave equation (one time step)
        solve(a1 + a2 == 0, w, bcs)

        # Advance time in exact solution
        t += time_step
        ue.t = t
        ve.t = t

        if step % 10 == 0:
            # Plot solution at current time step
            fig = plt.figure()
            plot(u, fig=fig)
            plt.show()

            # Compute max error at vertices
            vertex_values_ue = ue.compute_vertex_values(mesh)
            vertex_values_w = w.compute_vertex_values(mesh)
            vertex_values_u = np.split(vertex_values_w, 2)[0]
            error_max = np.max(np.abs(vertex_values_ue - vertex_values_u))
            # Print error
            print(error_max)

        # Shift to next time step
        u0 = project(u, Q)
        v0 = project(v, Q)
        step += 1
Example #17
0
def plot(object, *args, **kwargs):
    """
    Plot given object.

    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC> or a :py:class:`FiniteElement
            <ufl.FiniteElement>`.

    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use

        .. code-block:: python

            mesh = UnitSquare(4,4)
            plot(mesh)

        Use the ``title`` argument to specify title of the plot

        .. code-block:: python

            plot(mesh, tite="Finite element mesh")

        It is also possible to plot an element

        .. code-block:: python

            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)

        Vector valued functions can be visualized with an alternative mode

        .. code-block:: python

            plot(u, mode = "glyphs")

        A more advanced example

        .. code-block:: python

            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0                    # scale the warping/glyphs
                 title = "Fancy plot"           # Set your own title
                 )

    """

    mesh = kwargs.get('mesh')

    p = cpp.Parameters()
    for key in kwargs:
        # If there is a "mesh" kwarg it should not be added to the parameters
        if key != "mesh":
            try:
                p.add(key, kwargs[key])
            except TypeError:
                cpp.warning(
                    "Incompatible type for keyword argument \"%s\". Ignoring."
                    % key)

    # Plot element
    if isinstance(object, ufl.FiniteElementBase):
        if os.environ.get("DOLFIN_NOPLOT", "0") != "0": return
        import ffc
        return ffc.plot(object, *args, **kwargs)

    if mesh is None and len(args) == 1 and isinstance(args[0], cpp.Mesh):
        mesh = args[0]

    # Plot expression
    if isinstance(object, cpp.Expression):
        if mesh is None:
            raise TypeError, "expected a mesh when plotting an expression."
        return cpp.plot(object, mesh, p)

    # Try to project if object is not a standard plottable type
    if not isinstance(
            object, (cpp.Function, cpp.Expression, cpp.Mesh, cpp.DirichletBC,
                     cpp.MeshFunction, cpp.MeshFunctionBool,
                     cpp.MeshFunctionInt, cpp.MeshFunctionDouble,
                     cpp.MeshFunctionSizet, cpp.DirichletBC, cpp.CSGGeometry)):

        from dolfin.fem.projection import project
        try:
            cpp.info("Object cannot be plotted directly, projecting to"\
                    " piecewise linears.")
            object = project(object, mesh=mesh)
        except Exception as e:
            msg = ("Don't know how to plot given object:\n  %s\n"\
                    "and projection failed:\n  %s") % (str(object), str(e))
            #raise RuntimeError(msg)
            raise

    plot_object = cpp.plot(object, p)
    plot_object.write_ps = _VTKPlotter_write_ps

    # Avoid premature deletion of plotted objects if they go out of scope
    # before the plot window is closed. The plotter itself is safe, since it's
    # created in the plot() C++ function, not directly from Python. But the
    # Python plotter proxy may disappear, so we can't store the references
    # there.
    global _objects_referenced_from_plot_windows
    _objects_referenced_from_plot_windows[plot_object.key()] = (object, mesh,
                                                                p)

    return plot_object
Example #18
0
    def __init__(self, *args, **kwargs):

        # FIXME: the logic in this function is really messy and
        # unclear

        # Copy constructor
        if len(args) == 1:
            if not isinstance(args[0], cpp.fem.DirichletBC):
                raise RuntimeError("Expecting a DirichleBC as only argument for copy constructor")

            # Initialize base class
            cpp.fem.DirichletBC.__init__(self, args[0])
            return

        # Get FunctionSpace
        if not isinstance(args[0], FunctionSpace):
            raise RuntimeError("First argument must be of type FunctionSpace")

        # FIXME: correct the below comment
        # Case: boundary value specified as float, tuple or similar
        # if len(args) >= 2 and not isinstance(args[1], (cpp.function.GenericFunction):
        if len(args) >= 2:
            # Check if we have a UFL expression or a concrete type
            if not hasattr(args[1], "_cpp_object"):
                if isinstance(args[1], ufl.classes.Expr):
                    expr = project(args[1], args[0])  # FIXME: This should really be interpolaton (project is expensive)
                else:
                    expr = Constant(args[1])
                args = args[:1] + (expr,) + args[2:]

        # Get boundary condition field (the condition that is applied)
        if isinstance(args[1], float) or isinstance(args[1], int):
            u = cpp.function.Constant(float(args[1]))
        elif isinstance(args[1], ufl.Coefficient):
            u = args[1].cpp_object()
        elif isinstance(args[1], cpp.function.GenericFunction):
            u = args[1]
        else:
            raise RuntimeError("Second argument must be convertiable to a GenericFunction: ",
                               args[1], type(args[1]))
        args = args[:1] + (u,) + args[2:]

        args = (args[0]._cpp_object,) + args[1:]

        # Case: Special sub domain 'inside' function provided as a
        # function
        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            # Note: using self below to avoid a problem where the user
            # function attached to AutoSubDomain get prematurely
            # destroyed. Maybe a pybind11 bug? Was the same with SWIG...
            self.sub_domain = AutoSubDomain(args[2])
            args = args[:2] + (self.sub_domain,) + args[3:]

        # FIXME: for clarity, can the user provided function case be
        # handled here too?
        # Create SubDomain object
        if isinstance(args[2], cpp.mesh.SubDomain):
            self.sub_domain = args[2]
            args = args[:2] + (self.sub_domain,) + args[3:]
        elif isinstance(args[2], str):
            self.sub_domain = CompiledSubDomain(args[2], mpi_comm=args[0].mesh().mpi_comm())
            args = args[:2] + (self.sub_domain,) + args[3:]
        elif isinstance(args[2], cpp.mesh.MeshFunctionSizet):
            self.domain_args = args[2:]
        else:
            raise RuntimeError("Invalid argument")

        # Add kwargs
        if isinstance(args[-1], str):
            method = args[-1]
        else:
            method = kwargs.pop("method", "topological")
            args += (method,)
        check_midpoint = kwargs.pop("check_midpoint", None)
        if check_midpoint is not None:
            args += (check_midpoint,)

        if (len(kwargs) > 0):
            raise RuntimeError("Invalid keyword arguments", kwargs)

        super().__init__(*args)
Example #19
0
def plot(object, *args, **kwargs):
    """
    Plot given object.

    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC> or a :py:class:`FiniteElement
            <ufl.FiniteElement>`.

    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use

        .. code-block:: python

            mesh = UnitSquare(4,4)
            plot(mesh)

        Use the ``title`` argument to specify title of the plot

        .. code-block:: python

            plot(mesh, tite="Finite element mesh")

        It is also possible to plot an element

        .. code-block:: python

            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)

        Vector valued functions can be visualized with an alternative mode

        .. code-block:: python

            plot(u, mode = "glyphs")

        A more advanced example

        .. code-block:: python

            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0                    # scale the warping/glyphs
                 title = "Fancy plot"           # Set your own title
                 )

    """

    # Plot element
    if isinstance(object, ufl.FiniteElementBase):
        if os.environ.get("DOLFIN_NOPLOT", "0") != "0":
            return
        import ffc
        return ffc.plot(object, *args, **kwargs)

    # Get mesh from explicit mesh kwarg, only positional arg, or via object
    mesh = kwargs.pop('mesh', None)
    if isinstance(object, cpp.Mesh):
        if mesh is not None and mesh.id() != object.id():
            cpp.dolfin_error("plotting.py",
                             "plot mesh",
                             "Got different mesh in plot object and keyword argument")
        mesh = object
    if mesh is None:
        if isinstance(object, cpp.Function):
            mesh = object.function_space().mesh()
        elif hasattr(object, "mesh"):
            mesh = object.mesh()

    # Expressions do not carry their own mesh
    if isinstance(object, cpp.Expression) and mesh is None:
        cpp.dolfin_error("plotting.py",
                         "plot expression",
                         "Expecting a mesh as keyword argument")

    # Try to project if object is not a standard plottable type
    if not isinstance(object, _plottable_types):
        from dolfin.fem.projection import project
        try:
            cpp.info("Object cannot be plotted directly, projecting to"\
                     " piecewise linears.")
            object = project(object, mesh=mesh)
        except Exception as e:
            msg = "Don't know how to plot given object:\n  %s\n" \
                  "and projection failed:\n  %s" % (str(object), str(e))
            cpp.dolfin_error("plotting.py", "plot object", msg)

    # Select backend
    backend = cpp.parameters["plotting_backend"]
    if backend == "vtk":
        return _plot_cpp(object, mesh, kwargs)
    elif backend == "matplotlib":
        return _plot_matplotlib(object, mesh, kwargs)
Example #20
0
def plot(object, *args, **kwargs):
    """
    Plot given object.

    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC>, a :py:class:`FiniteElement
            <ufl.FiniteElement>`.

    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use

        .. code-block:: python

            mesh = UnitSquare(4, 4)
            plot(mesh)

        Use the ``title`` argument to specify title of the plot

        .. code-block:: python

            plot(mesh, tite="Finite element mesh")

        It is also possible to plot an element

        .. code-block:: python

            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)

        Vector valued functions can be visualized with an alternative mode

        .. code-block:: python

            plot(u, mode = "glyphs")

        A more advanced example

        .. code-block:: python

            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0,                   # scale the warping/glyphs
                 title = "Fancy plot",          # set your own title
                 )

    """

    # Return if plotting is disables
    if os.environ.get("DOLFIN_NOPLOT", "0") != "0":
        return

    # Return if Matplotlib is not available
    if not _has_matplotlib():
        cpp.log.info("Matplotlib is required to plot from Python.")
        return

    # Plot element
    if isinstance(object, ufl.FiniteElementBase):
        import ffc
        return ffc.plot(object, *args, **kwargs)

    # For dolfin.function.Function, extract cpp_object
    if hasattr(object, "cpp_object"):
        object = object.cpp_object()

    # Get mesh from explicit mesh kwarg, only positional arg, or via
    # object
    mesh = kwargs.pop('mesh', None)
    if isinstance(object, cpp.mesh.Mesh):
        if mesh is not None and mesh.id() != object.id():
            raise RuntimeError("Got different mesh in plot object and keyword argument")
        mesh = object
    if mesh is None:
        if isinstance(object, cpp.function.Function):
            mesh = object.function_space().mesh()
        elif hasattr(object, "mesh"):
            mesh = object.mesh()

    # Expressions do not carry their own mesh
    if isinstance(object, cpp.function.Expression) and mesh is None:
        raise RuntimeError("Expecting a mesh as keyword argument")

    backend = kwargs.pop("backend", "matplotlib")
    if backend not in ("matplotlib", "x3dom"):
        raise RuntimeError("Plotting backend %s not recognised" % backend)

    # Try to project if object is not a standard plottable type
    if not isinstance(object, _all_plottable_types):
        from dolfin.fem.projection import project
        try:
            cpp.log.info("Object cannot be plotted directly, projecting to "
                         "piecewise linears.")
            object = project(object, mesh=mesh)
            mesh = object.function_space().mesh()
            object = object._cpp_object
        except Exception as e:
            msg = "Don't know how to plot given object:\n  %s\n" \
                  "and projection failed:\n  %s" % (str(object), str(e))
            raise RuntimeError(msg)

    # Plot
    if backend == "matplotlib":
        return _plot_matplotlib(object, mesh, kwargs)
    elif backend == "x3dom":
        return _plot_x3dom(object, kwargs)
    else:
        assert False, "This code should not be reached."
Example #21
0
    def __init__(self, *args, **kwargs):

        # FIXME: the logic in this function is really messy and
        # unclear

        # Copy constructor
        if len(args) == 1:
            if not isinstance(args[0], cpp.fem.DirichletBC):
                raise RuntimeError(
                    "Expecting a DirichleBC as only argument for copy constructor"
                )

            # Initialize base class
            cpp.fem.DirichletBC.__init__(self, args[0])
            return

        # Get FunctionSpace
        if not isinstance(args[0], FunctionSpace):
            raise RuntimeError("First argument must be of type FunctionSpace")

        # FIXME: correct the below comment
        # Case: boundary value specified as float, tuple or similar
        # if len(args) >= 2 and not isinstance(args[1], (cpp.function.GenericFunction):
        if len(args) >= 2:
            # Check if we have a UFL expression or a concrete type
            if not hasattr(args[1], "_cpp_object"):
                if isinstance(args[1], ufl.classes.Expr):
                    expr = project(
                        args[1], args[0]
                    )  # FIXME: This should really be interpolaton (project is expensive)
                else:
                    expr = Constant(args[1])
                args = args[:1] + (expr, ) + args[2:]

        # Get boundary condition field (the condition that is applied)
        if isinstance(args[1], float) or isinstance(args[1], int):
            u = cpp.function.Constant(float(args[1]))
        elif isinstance(args[1], ufl.Coefficient):
            u = args[1].cpp_object()
        elif isinstance(args[1], cpp.function.GenericFunction):
            u = args[1]
        else:
            raise RuntimeError(
                "Second argument must be convertiable to a GenericFunction: ",
                args[1], type(args[1]))
        args = args[:1] + (u, ) + args[2:]

        args = (args[0]._cpp_object, ) + args[1:]

        # Case: Special sub domain 'inside' function provided as a
        # function
        if len(args) >= 3 and isinstance(args[2], types.FunctionType):
            # Note: using self below to avoid a problem where the user
            # function attached to AutoSubDomain get prematurely
            # destroyed. Maybe a pybind11 bug? Was the same with SWIG...
            self.sub_domain = AutoSubDomain(args[2])
            args = args[:2] + (self.sub_domain, ) + args[3:]

        # FIXME: for clarity, can the user provided function case be
        # handled here too?
        # Create SubDomain object
        if isinstance(args[2], cpp.mesh.SubDomain):
            self.sub_domain = args[2]
            args = args[:2] + (self.sub_domain, ) + args[3:]
        elif isinstance(args[2], str):
            self.sub_domain = CompiledSubDomain(
                args[2], mpi_comm=args[0].mesh().mpi_comm())
            args = args[:2] + (self.sub_domain, ) + args[3:]
        elif isinstance(args[2], cpp.mesh.MeshFunctionSizet):
            self.domain_args = args[2:]
        else:
            raise RuntimeError("Invalid argument")

        # Add kwargs
        if isinstance(args[-1], str):
            method = args[-1]
        else:
            method = kwargs.pop("method", "topological")
            args += (method, )
        check_midpoint = kwargs.pop("check_midpoint", None)
        if check_midpoint is not None:
            args += (check_midpoint, )

        if (len(kwargs) > 0):
            raise RuntimeError("Invalid keyword arguments", kwargs)

        super().__init__(*args)
Example #22
0
def solve_heat_equation(k):
    """
    Solve the heat equation on a hard-coded mesh with a hard-coded initial and boundary conditions
    
    :param k: Thermal conductivity
    """
    mesh, boundary = setup_geometry()

    # Exact solution (Gauss curve)
    ue = Expression("exp(-(x[0]*x[0]+x[1]*x[1])/(4*a*t))/(4*pi*a*t)",
                    a=k,
                    t=1e-7,
                    domain=mesh,
                    degree=2)

    # Polynomial degree
    r = 1

    # Setup FEM function space
    V = FunctionSpace(mesh, "CG", r)

    # Create boundary condition
    bc = DirichletBC(V, ue, boundary)

    # Setup FEM functions
    v = TestFunction(V)
    u = Function(V)

    # Time parameters
    time_step = 0.5
    t_start, t_end = 0.0, 20.0

    # Time stepping
    t = t_start
    u0 = ue
    step = 0
    while t < t_end:
        # Weak form of the heat equation
        a = (u - u0) / time_step * v * dx + k * inner(grad(u), grad(v)) * dx

        # Solve the heat equation (one time step)
        solve(a == 0, u, bc)

        # Advance time in exact solution
        t += time_step
        ue.t = t

        if step % 5 == 0:
            # Plot solution at current time step
            fig = plt.figure()
            plot(u, fig=fig)
            plt.show()

            # Compute error in L2 norm
            error_L2 = errornorm(ue, u, 'L2')
            # or equivalently
            # sqrt(assemble((ue - u) * (ue - u) * dx))
            # Print error
            print(error_L2)

        # Shift to next time step
        u0 = project(u, V)
        step += 1