Example #1
0
def errornorm(u, uh, norm_type="L2", degree_rise=3, mesh=None):
    """Compute the error :math:`e = u - u_h` in the specified norm.

    :arg u: a :class:`.Function` containing an "exact" solution
    :arg uh: a :class:`.Function` containing the approximate solution
    :arg norm_type: the type of norm to compute, see :func:`.norm` for
         details of supported norm types.
    :arg degree_rise: increase in polynomial degree to use as the
         approximation space for computing the error.
    :arg mesh: an optional mesh on which to compute the error norm
         (currently ignored).

    This function works by :func:`.project`\ing ``u`` and ``uh`` into
    a space of degree ``degree_rise`` higher than the degree of ``uh``
    and computing the error there.
    """
    urank = len(u.ufl_shape)
    uhrank = len(uh.ufl_shape)

    rank = urank
    if urank != uhrank:
        raise RuntimeError("Mismatching rank between u and uh")

    degree = uh.function_space().ufl_element().degree()
    if isinstance(degree, tuple):
        degree = max(degree) + degree_rise
    else:
        degree += degree_rise

    # The exact solution might be an expression, in which case this test is irrelevant.
    if isinstance(u, function.Function):
        degree_u = u.function_space().ufl_element().degree()
        if degree > degree_u:
            warning("Degree of exact solution less than approximation degree")

    mesh = uh.function_space().mesh()
    if rank == 0:
        V = functionspace.FunctionSpace(mesh, 'DG', degree)
    elif rank == 1:
        V = functionspace.VectorFunctionSpace(mesh,
                                              'DG',
                                              degree,
                                              dim=u.ufl_shape[0])
    else:
        raise RuntimeError(
            "Don't know how to compute error norm for tensor valued functions")

    u_ = projection.project(u, V)
    uh_ = projection.project(uh, V)

    uh_ -= u_

    return norm(uh_, norm_type=norm_type, mesh=mesh)
Example #2
0
def errornorm(u, uh, norm_type="L2", degree_rise=3, mesh=None):
    """Compute the error :math:`e = u - u_h` in the specified norm.

    :arg u: a :class:`.Function` containing an "exact" solution
    :arg uh: a :class:`.Function` containing the approximate solution
    :arg norm_type: the type of norm to compute, see :func:`.norm` for
         details of supported norm types.
    :arg degree_rise: increase in polynomial degree to use as the
         approximation space for computing the error.
    :arg mesh: an optional mesh on which to compute the error norm
         (currently ignored).

    This function works by :func:`.project`\ing ``u`` and ``uh`` into
    a space of degree ``degree_rise`` higher than the degree of ``uh``
    and computing the error there.
    """
    urank = len(u.ufl_shape)
    uhrank = len(uh.ufl_shape)

    rank = urank
    if urank != uhrank:
        raise RuntimeError("Mismatching rank between u and uh")

    degree = uh.function_space().ufl_element().degree()
    if isinstance(degree, tuple):
        degree = max(degree) + degree_rise
    else:
        degree += degree_rise

    # The exact solution might be an expression, in which case this test is irrelevant.
    if isinstance(u, function.Function):
        degree_u = u.function_space().ufl_element().degree()
        if degree > degree_u:
            warning("Degree of exact solution less than approximation degree")

    mesh = uh.function_space().mesh()
    if rank == 0:
        V = functionspace.FunctionSpace(mesh, 'DG', degree)
    elif rank == 1:
        V = functionspace.VectorFunctionSpace(mesh, 'DG', degree,
                                              dim=u.ufl_shape[0])
    else:
        raise RuntimeError("Don't know how to compute error norm for tensor valued functions")

    u_ = projection.project(u, V)
    uh_ = projection.project(uh, V)

    uh_ -= u_

    return norm(uh_, norm_type=norm_type, mesh=mesh)
Example #3
0
 def function_arg(self, g):
     '''Set the value of this boundary condition.'''
     if isinstance(g, function.Function) and g.function_space() != self._function_space:
         raise RuntimeError("%r is defined on incompatible FunctionSpace!" % g)
     if not isinstance(g, expression.Expression):
         try:
             # Bare constant?
             as_ufl(g)
         except UFLException:
             try:
                 # List of bare constants? Convert to UFL expression
                 g = as_ufl(as_tensor(g))
                 if g.ufl_shape != self._function_space.shape:
                     raise ValueError("%r doesn't match the shape of the function space." % (g,))
             except UFLException:
                 raise ValueError("%r is not a valid DirichletBC expression" % (g,))
     if isinstance(g, expression.Expression) or has_type(as_ufl(g), SpatialCoordinate):
         if isinstance(g, expression.Expression):
             self._expression_state = g._state
         try:
             g = function.Function(self._function_space).interpolate(g)
         # Not a point evaluation space, need to project onto V
         except NotImplementedError:
             g = projection.project(g, self._function_space)
     self._function_arg = g
     self._currently_zeroed = False
Example #4
0
 def function_arg(self, g):
     '''Set the value of this boundary condition.'''
     if isinstance(g, function.Function) and g.function_space() != self._function_space:
         raise RuntimeError("%r is defined on incompatible FunctionSpace!" % g)
     if not isinstance(g, expression.Expression):
         try:
             # Bare constant?
             as_ufl(g)
         except UFLException:
             try:
                 # List of bare constants? Convert to UFL expression
                 g = as_ufl(as_tensor(g))
                 if g.ufl_shape != self._function_space.shape:
                     raise ValueError("%r doesn't match the shape of the function space." % (g,))
             except UFLException:
                 raise ValueError("%r is not a valid DirichletBC expression" % (g,))
     if isinstance(g, expression.Expression) or has_type(as_ufl(g), SpatialCoordinate):
         if isinstance(g, expression.Expression):
             self._expression_state = g._state
         try:
             g = function.Function(self._function_space).interpolate(g)
         # Not a point evaluation space, need to project onto V
         except NotImplementedError:
             g = projection.project(g, self._function_space)
     self._function_arg = g
     self._currently_zeroed = False
Example #5
0
    def project(self, b, *args, **kwargs):
        """Project ``b`` onto ``self``. ``b`` must be a :class:`Function` or an
        :class:`.Expression`.

        This is equivalent to ``project(b, self)``.
        Any of the additional arguments to :func:`~firedrake.projection.project`
        may also be passed, and they will have their usual effect.
        """
        from firedrake import projection
        return projection.project(b, self, *args, **kwargs)
Example #6
0
    def project(self, b, *args, **kwargs):
        """Project ``b`` onto ``self``. ``b`` must be a :class:`Function` or an
        :class:`.Expression`.

        This is equivalent to ``project(b, self)``.
        Any of the additional arguments to :func:`~firedrake.projection.project`
        may also be passed, and they will have their usual effect.
        """
        from firedrake import projection
        return projection.project(b, self, *args, **kwargs)
Example #7
0
    def initialize(self, obj):
        if complex_mode:
            raise NotImplementedError(
                "HypreAMS preconditioner not yet implemented in complex mode")

        Citations().register("Kolev2009")
        A, P = obj.getOperators()
        prefix = obj.getOptionsPrefix()
        V = get_function_space(obj.getDM())
        mesh = V.mesh()

        family = str(V.ufl_element().family())
        degree = V.ufl_element().degree()
        if family != 'Nedelec 1st kind H(curl)' or degree != 1:
            raise ValueError(
                "Hypre AMS requires lowest order Nedelec elements! (not %s of degree %d)"
                % (family, degree))

        P1 = FunctionSpace(mesh, "Lagrange", 1)
        G = Interpolator(grad(TestFunction(P1)), V).callable().handle

        pc = PETSc.PC().create(comm=obj.comm)
        pc.incrementTabLevel(1, parent=obj)
        pc.setOptionsPrefix(prefix + "hypre_ams_")
        pc.setOperators(A, P)

        pc.setType('hypre')
        pc.setHYPREType('ams')
        pc.setHYPREDiscreteGradient(G)
        zero_beta = PETSc.Options(prefix).getBool(
            "pc_hypre_ams_zero_beta_poisson", default=False)
        if zero_beta:
            pc.setHYPRESetBetaPoissonMatrix(None)

        # Build constants basis for the Nedelec space
        cvecs = []
        for i in range(mesh.cell_dimension()):
            direction = [
                1.0 if i == j else 0.0 for j in range(mesh.cell_dimension())
            ]
            c = project(Constant(direction), V)
            with c.vector().dat.vec_ro as cvec:
                cvecs.append(cvec)
        pc.setHYPRESetEdgeConstantVectors(*cvecs)
        pc.setUp()

        self.pc = pc
Example #8
0
def SubDomainData(geometric_expr):
    """Creates a subdomain data object from a boolean-valued UFL expression.

    The result can be attached as the subdomain_data field of a
    :class:`ufl.Measure`. For example:

        x = mesh.coordinates
        sd = SubDomainData(x[0] < 0.5)
        assemble(f*dx(subdomain_data=sd))

    """
    import firedrake.functionspace as functionspace
    import firedrake.projection as projection

    # Find domain from expression
    m = geometric_expr.ufl_domain()

    # Find selected cells
    fs = functionspace.FunctionSpace(m, 'DG', 0)
    f = projection.project(ufl.conditional(geometric_expr, 1, 0), fs)

    # Create cell subset
    indices, = np.nonzero(f.dat.data_ro_with_halos > 0.5)
    return op2.Subset(m.cell_set, indices)
Example #9
0
    def __lshift__(self, data):
        """It allows file << function syntax for writing data out to disk.

        In the case of parallel, it would also accept (function, timestep)
        tuple as an argument. If only function is given, then the timestep
        will be automatically generated."""
        # If parallel, it needs to keep track of its timestep.
        if MPI.parallel:
            # if statements to keep the consistency of how to update the
            # timestep.
            if isinstance(data, tuple):
                if self._time_step == -1 or not self._generate_time:
                    function = data[0]
                    self._time_step = data[1]
                else:
                    raise TypeError("Expected function, got tuple.")
            else:
                if self._time_step != -1 and not self._generate_time:
                    raise TypeError("Expected tuple, got function.")
                function = data
                self._time_step += 1
                self._generate_time = True
        else:
            function = data

        def is_family1(e, family):
            import ufl.finiteelement.hdivcurl as hc
            if isinstance(e, (hc.HDiv, hc.HCurl)):
                return False
            if e.family() == 'OuterProductElement':
                if e.degree() == (1, 1):
                    if e._A.family() == family \
                       and e._B.family() == family:
                        return True
            elif e.family() == family and e.degree() == 1:
                return True
            return False

        def is_cgN(e):
            import ufl.finiteelement.hdivcurl as hc
            if isinstance(e, (hc.HDiv, hc.HCurl)):
                return False
            if e.family() == 'OuterProductElement':
                if e._A.family() in ('Lagrange', 'Q') \
                   and e._B.family() == 'Lagrange':
                    return True
            elif e.family() in ('Lagrange', 'Q'):
                return True
            return False

        mesh = function.function_space().mesh()
        e = function.function_space().ufl_element()

        if len(e.value_shape()) > 1:
            raise RuntimeError("Can't output tensor valued functions")

        ce = mesh.coordinates.function_space().ufl_element()

        coords_p1 = is_family1(ce, 'Lagrange') or is_family1(ce, 'Q')
        coords_p1dg = is_family1(ce, 'Discontinuous Lagrange') or is_family1(ce, 'DQ')
        coords_cgN = is_cgN(ce)
        function_p1 = is_family1(e, 'Lagrange') or is_family1(e, 'Q')
        function_p1dg = is_family1(e, 'Discontinuous Lagrange') or is_family1(e, 'DQ')
        function_cgN = is_cgN(e)

        project_coords = False
        project_function = False
        discontinuous = False
        # We either output in P1 or P1dg.
        if coords_cgN and function_cgN:
            family = 'CG'
            project_coords = not coords_p1
            project_function = not function_p1
        else:
            family = 'DG'
            project_coords = not coords_p1dg
            project_function = not function_p1dg
            discontinuous = True

        if project_function:
            if len(e.value_shape()) == 0:
                Vo = fs.FunctionSpace(mesh, family, 1)
            elif len(e.value_shape()) == 1:
                Vo = fs.VectorFunctionSpace(mesh, family, 1, dim=e.value_shape()[0])
            else:
                # Never reached
                Vo = None
            if not self._warnings[0]:
                warning(RED % "*** Projecting output function to %s1", family)
                self._warnings[0] = True
            output = projection.project(function, Vo, name=function.name())
        else:
            output = function
            Vo = output.function_space()
        if project_coords:
            Vc = fs.VectorFunctionSpace(mesh, family, 1, dim=mesh._coordinate_fs.dim)
            if not self._warnings[1]:
                warning(RED % "*** Projecting coordinates to %s1", family)
                self._warnings[1] = True
            coordinates = projection.project(mesh.coordinates, Vc, name=mesh.coordinates.name())
        else:
            coordinates = mesh.coordinates
            Vc = coordinates.function_space()

        num_points = Vo.node_count

        layers = mesh.layers - 1 if isinstance(e.cell(), OuterProductCell) else 1
        num_cells = mesh.num_cells() * layers

        if not isinstance(e.cell(), OuterProductCell) and e.cell().cellname() != "quadrilateral":
            connectivity = Vc.cell_node_map().values_with_halo.flatten()
        else:
            # Connectivity of bottom cell in extruded mesh
            base = Vc.cell_node_map().values_with_halo
            if _cells[mesh.ufl_cell()] == hl.VtkQuad:
                # Quad is
                #
                # 1--3
                # |  |
                # 0--2
                #
                # needs to be
                #
                # 3--2
                # |  |
                # 0--1
                base = base[:, [0, 2, 3, 1]]
                points_per_cell = 4
            elif _cells[mesh.ufl_cell()] == hl.VtkWedge:
                # Wedge is
                #
                #    5
                #   /|\
                #  / | \
                # 1----3
                # |  4 |
                # | /\ |
                # |/  \|
                # 0----2
                #
                # needs to be
                #
                #    5
                #   /|\
                #  / | \
                # 3----4
                # |  2 |
                # | /\ |
                # |/  \|
                # 0----1
                #
                base = base[:, [0, 2, 4, 1, 3, 5]]
                points_per_cell = 6
            elif _cells[mesh.ufl_cell()] == hl.VtkHexahedron:
                # Hexahedron is
                #
                #   5----7
                #  /|   /|
                # 4----6 |
                # | 1--|-3
                # |/   |/
                # 0----2
                #
                # needs to be
                #
                #   7----6
                #  /|   /|
                # 4----5 |
                # | 3--|-2
                # |/   |/
                # 0----1
                #
                base = base[:, [0, 2, 3, 1, 4, 6, 7, 5]]
                points_per_cell = 8
            # Repeat up the column
            connectivity_temp = np.repeat(base, layers, axis=0)

            if discontinuous:
                scale = points_per_cell
            else:
                scale = 1
            offsets = np.arange(layers) * scale

            # Add offsets going up the column
            connectivity_temp += np.tile(offsets.reshape(-1, 1), (mesh.num_cells(), 1))

            connectivity = connectivity_temp.flatten()

        if isinstance(output.function_space(), fs.VectorFunctionSpace):
            tmp = output.dat.data_ro_with_halos
            vdata = [None]*3
            if output.dat.dim[0] == 1:
                vdata[0] = tmp.flatten()
            else:
                for i in range(output.dat.dim[0]):
                    vdata[i] = tmp[:, i].flatten()
            for i in range(output.dat.dim[0], 3):
                vdata[i] = np.zeros_like(vdata[0])
            data = tuple(vdata)
            # only for checking large file size
            flat_data = {function.name(): tmp.flatten()}
        else:
            data = output.dat.data_ro_with_halos.flatten()
            flat_data = {function.name(): data}

        coordinates = self._fd_to_evtk_coord(coordinates.dat.data_ro_with_halos)

        cell_types = np.empty(num_cells, dtype="uint8")

        # Assume that all cells are of same shape.
        cell_types[:] = _cells[mesh.ufl_cell()].tid
        p_c = _points_per_cell[mesh.ufl_cell()]

        # This tells which are the last nodes of each cell.
        offsets = np.arange(start=p_c, stop=p_c * (num_cells + 1), step=p_c,
                            dtype='int32')
        large_file_flag = _requiresLargeVTKFileSize("VtkUnstructuredGrid",
                                                    numPoints=num_points,
                                                    numCells=num_cells,
                                                    pointData=flat_data,
                                                    cellData=None)
        new_name = self._filename

        # When vtu file makes part of a parallel process, aggregated by a
        # pvtu file, the output is : filename_timestep_rank.vtu
        if MPI.parallel:
            new_name += "_" + str(self._time_step) + "_" + str(MPI.comm.rank)

        self._writer = hl.VtkFile(
            new_name, hl.VtkUnstructuredGrid, large_file_flag)

        self._writer.openGrid()

        self._writer.openPiece(ncells=num_cells, npoints=num_points)

        # openElement allows the stuff in side of the tag <arg></arg>
        # to be editted.
        self._writer.openElement("Points")
        # addData adds the DataArray in the tag <arg1>
        self._writer.addData("Points", coordinates)

        self._writer.closeElement("Points")
        self._writer.openElement("Cells")
        self._writer.addData("connectivity", connectivity)
        self._writer.addData("offsets", offsets)
        self._writer.addData("types", cell_types)
        self._writer.closeElement("Cells")

        self._writer.openData("Point", scalars=function.name())
        self._writer.addData(function.name(), data)
        self._writer.closeData("Point")
        self._writer.closePiece()
        self._writer.closeGrid()

        # Create the AppendedData
        self._writer.appendData(coordinates)
        self._writer.appendData(connectivity)
        self._writer.appendData(offsets)
        self._writer.appendData(cell_types)
        self._writer.appendData(data)
        self._writer.save()
Example #10
0
    def __lshift__(self, data):
        """It allows file << function syntax for writing data out to disk.

        In the case of parallel, it would also accept (function, timestep)
        tuple as an argument. If only function is given, then the timestep
        will be automatically generated."""
        # If parallel, it needs to keep track of its timestep.
        if MPI.parallel:
            # if statements to keep the consistency of how to update the
            # timestep.
            if isinstance(data, tuple):
                if self._time_step == -1 or not self._generate_time:
                    function = data[0]
                    self._time_step = data[1]
                else:
                    raise TypeError("Expected function, got tuple.")
            else:
                if self._time_step != -1 and not self._generate_time:
                    raise TypeError("Expected tuple, got function.")
                function = data
                self._time_step += 1
                self._generate_time = True
        else:
            function = data

        def is_family1(e, family):
            import ufl.finiteelement.hdivcurl as hc
            if isinstance(e, (hc.HDivElement, hc.HCurlElement)):
                return False
            if e.family() == 'OuterProductElement':
                if e.degree() == (1, 1):
                    if e._A.family() == family \
                       and e._B.family() == family:
                        return True
            elif e.family() == family and e.degree() == 1:
                return True
            return False

        def is_cgN(e):
            import ufl.finiteelement.hdivcurl as hc
            if isinstance(e, (hc.HDivElement, hc.HCurlElement)):
                return False
            if e.family() == 'OuterProductElement':
                if e._A.family() in ('Lagrange', 'Q') \
                   and e._B.family() == 'Lagrange':
                    return True
            elif e.family() in ('Lagrange', 'Q'):
                return True
            return False

        mesh = function.function_space().mesh()
        e = function.function_space().ufl_element()

        if len(e.value_shape()) > 1:
            raise RuntimeError("Can't output tensor valued functions")

        ce = mesh.coordinates.function_space().ufl_element()

        coords_p1 = is_family1(ce, 'Lagrange') or is_family1(ce, 'Q')
        coords_p1dg = is_family1(ce, 'Discontinuous Lagrange') or is_family1(ce, 'DQ')
        coords_cgN = is_cgN(ce)
        function_p1 = is_family1(e, 'Lagrange') or is_family1(e, 'Q')
        function_p1dg = is_family1(e, 'Discontinuous Lagrange') or is_family1(e, 'DQ')
        function_cgN = is_cgN(e)

        project_coords = False
        project_function = False
        discontinuous = False
        # We either output in P1 or P1dg.
        if coords_cgN and function_cgN:
            family = 'CG'
            project_coords = not coords_p1
            project_function = not function_p1
        else:
            family = 'DG'
            project_coords = not coords_p1dg
            project_function = not function_p1dg
            discontinuous = True

        if project_function:
            if len(e.value_shape()) == 0:
                Vo = fs.FunctionSpace(mesh, family, 1)
            elif len(e.value_shape()) == 1:
                Vo = fs.VectorFunctionSpace(mesh, family, 1, dim=e.value_shape()[0])
            else:
                # Never reached
                Vo = None
            if not self._warnings[0]:
                warning(RED % "*** Projecting output function to %s1", family)
                self._warnings[0] = True
            output = projection.project(function, Vo, name=function.name())
        else:
            output = function
            Vo = output.function_space()
        if project_coords:
            Vc = fs.VectorFunctionSpace(mesh, family, 1, dim=mesh._coordinate_fs.dim)
            if not self._warnings[1]:
                warning(RED % "*** Projecting coordinates to %s1", family)
                self._warnings[1] = True
            coordinates = projection.project(mesh.coordinates, Vc, name=mesh.coordinates.name())
        else:
            coordinates = mesh.coordinates
            Vc = coordinates.function_space()

        num_points = Vo.node_count

        layers = mesh.layers - 1 if isinstance(e.cell(), OuterProductCell) else 1
        num_cells = mesh.num_cells() * layers

        if not isinstance(e.cell(), OuterProductCell) and e.cell().cellname() != "quadrilateral":
            connectivity = Vc.cell_node_map().values_with_halo.flatten()
        else:
            # Connectivity of bottom cell in extruded mesh
            base = Vc.cell_node_map().values_with_halo
            if _cells[mesh.ufl_cell()] == hl.VtkQuad:
                # Quad is
                #
                # 1--3
                # |  |
                # 0--2
                #
                # needs to be
                #
                # 3--2
                # |  |
                # 0--1
                base = base[:, [0, 2, 3, 1]]
                points_per_cell = 4
            elif _cells[mesh.ufl_cell()] == hl.VtkWedge:
                # Wedge is
                #
                #    5
                #   /|\
                #  / | \
                # 1----3
                # |  4 |
                # | /\ |
                # |/  \|
                # 0----2
                #
                # needs to be
                #
                #    5
                #   /|\
                #  / | \
                # 3----4
                # |  2 |
                # | /\ |
                # |/  \|
                # 0----1
                #
                base = base[:, [0, 2, 4, 1, 3, 5]]
                points_per_cell = 6
            elif _cells[mesh.ufl_cell()] == hl.VtkHexahedron:
                # Hexahedron is
                #
                #   5----7
                #  /|   /|
                # 4----6 |
                # | 1--|-3
                # |/   |/
                # 0----2
                #
                # needs to be
                #
                #   7----6
                #  /|   /|
                # 4----5 |
                # | 3--|-2
                # |/   |/
                # 0----1
                #
                base = base[:, [0, 2, 3, 1, 4, 6, 7, 5]]
                points_per_cell = 8
            # Repeat up the column
            connectivity_temp = np.repeat(base, layers, axis=0)

            if discontinuous:
                scale = points_per_cell
            else:
                scale = 1
            offsets = np.arange(layers) * scale

            # Add offsets going up the column
            connectivity_temp += np.tile(offsets.reshape(-1, 1), (mesh.num_cells(), 1))

            connectivity = connectivity_temp.flatten()

        if isinstance(output.function_space(), fs.VectorFunctionSpace):
            tmp = output.dat.data_ro_with_halos
            vdata = [None]*3
            if output.dat.dim[0] == 1:
                vdata[0] = tmp.flatten()
            else:
                for i in range(output.dat.dim[0]):
                    vdata[i] = tmp[:, i].flatten()
            for i in range(output.dat.dim[0], 3):
                vdata[i] = np.zeros_like(vdata[0])
            data = tuple(vdata)
            # only for checking large file size
            flat_data = {function.name(): tmp.flatten()}
        else:
            data = output.dat.data_ro_with_halos.flatten()
            flat_data = {function.name(): data}

        coordinates = self._fd_to_evtk_coord(coordinates.dat.data_ro_with_halos)

        cell_types = np.empty(num_cells, dtype="uint8")

        # Assume that all cells are of same shape.
        cell_types[:] = _cells[mesh.ufl_cell()].tid
        p_c = _points_per_cell[mesh.ufl_cell()]

        # This tells which are the last nodes of each cell.
        offsets = np.arange(start=p_c, stop=p_c * (num_cells + 1), step=p_c,
                            dtype='int32')
        large_file_flag = _requiresLargeVTKFileSize("VtkUnstructuredGrid",
                                                    numPoints=num_points,
                                                    numCells=num_cells,
                                                    pointData=flat_data,
                                                    cellData=None)
        new_name = self._filename

        # When vtu file makes part of a parallel process, aggregated by a
        # pvtu file, the output is : filename_timestep_rank.vtu
        if MPI.parallel:
            new_name += "_" + str(self._time_step) + "_" + str(MPI.comm.rank)

        self._writer = hl.VtkFile(
            new_name, hl.VtkUnstructuredGrid, large_file_flag)

        self._writer.openGrid()

        self._writer.openPiece(ncells=num_cells, npoints=num_points)

        # openElement allows the stuff in side of the tag <arg></arg>
        # to be editted.
        self._writer.openElement("Points")
        # addData adds the DataArray in the tag <arg1>
        self._writer.addData("Points", coordinates)

        self._writer.closeElement("Points")
        self._writer.openElement("Cells")
        self._writer.addData("connectivity", connectivity)
        self._writer.addData("offsets", offsets)
        self._writer.addData("types", cell_types)
        self._writer.closeElement("Cells")

        self._writer.openData("Point", scalars=function.name())
        self._writer.addData(function.name(), data)
        self._writer.closeData("Point")
        self._writer.closePiece()
        self._writer.closeGrid()

        # Create the AppendedData
        self._writer.appendData(coordinates)
        self._writer.appendData(connectivity)
        self._writer.appendData(offsets)
        self._writer.appendData(cell_types)
        self._writer.appendData(data)
        self._writer.save()