Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/src/t8_vtk/t8_vtk_write_ASCII.cxx
903 views
/*
  This file is part of t8code.
  t8code is a C library to manage a collection (a forest) of multiple
  connected adaptive space-trees of general element classes in parallel.

  Copyright (C) 2024 the developers

  t8code is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  t8code is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with t8code; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "t8_vtk/t8_vtk_write_ASCII.hxx"
#include "t8_vtk/t8_vtk_writer_helper.hxx"
#include <t8_vtk.h>
#include <t8_forest/t8_forest_ghost.h>
#include <t8_types/t8_vec.hxx>
#include "t8_forest/t8_forest_types.h"
#include <t8_cmesh/t8_cmesh_internal/t8_cmesh_trees.h>
#include <t8_cmesh/t8_cmesh_internal/t8_cmesh_types.h>
#include <t8_schemes/t8_scheme.hxx>

/* TODO: Currently we only use ASCII mode and no data compression.
 *       We also do not use sc_io to buffer our output stream. */

/* There are different cell data to write, e.g. connectivity, type, vertices, ...
 * The structure is always the same:
 * Iterate over the trees,
 *      iterate over the elements of that tree
 *          execute an element dependent part to write in the file.
 * In order to simplify writing this code, we put all the parts that are
 * repetitive in the function
 *  t8_forest_vtk_write_cell_data.
 * This function accepts a callback function, which is then executed for
 * each element. The callback function is defined below.
 */
/* TODO: As soon as we have element iterators we should restructure this concept
 * appropriately. */
typedef enum { T8_VTK_KERNEL_INIT, T8_VTK_KERNEL_EXECUTE, T8_VTK_KERNEL_CLEANUP } T8_VTK_KERNEL_MODUS;

/** Callback function prototype for writing cell data.
 * The function is executed for each element.
 * The callback can run in three different modi:
 *  INIT    - Called once, to (possibly) initialize the data pointer
 *  EXECUTE - Called for each element, the actual writing happens here.
 *  CLEANUP - Called once after all elements. Used to cleanup any memory
 *            allocated during INIT.
 * \param [in] forest The forest.
 * \param [in] ltree_id   A local treeid.
 * \param [in] tree   The local tree of the forest with id \a ltree_id.
 * \param [in] element_index An index of an element inside \a tree.
 * \param [in] element  A pointer to the current element.
 * \param [in] tree_class   The eclass of the current tree.
 * \param [in] is_ghost Non-zero if the current element is a ghost element.
 *                      In this cas \a tree is NULL.
 *                      All ghost element will be traversed after all elements are
 * \param [in,out] vtufile The open file stream to which we write the forest.
 * \param [in,out] columns An integer counting the number of written columns.
 *                         The callback should increase this value by the number
 *                         of values written to the file.
 * \param [in,out] data    A pointer that the callback can modify at will.
 *                         Between modi INIT and CLEANUP, \a data will not be
 *                         modified outside of this callback.
 * \param [in]     modus   The modus in which the callback is called. See above.
 * \return                 True if successful, false if not (i.e. file i/o error).
 */
typedef int (*t8_forest_vtk_cell_data_kernel) (t8_forest_t forest, const t8_locidx_t ltree_id, const t8_tree_t tree,
                                               const t8_locidx_t element_index, const t8_element_t *element,
                                               const t8_eclass_t tree_class, const int is_ghost, FILE *vtufile,
                                               int *columns, void **data, T8_VTK_KERNEL_MODUS modus);

static t8_locidx_t
t8_forest_num_points (t8_forest_t forest, const int count_ghosts)
{
  const t8_scheme *scheme = t8_forest_get_scheme (forest);
  t8_locidx_t num_points = 0;

  for (t8_locidx_t itree = 0; itree < (t8_locidx_t) forest->trees->elem_count; itree++) {
    const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree);
    /* Get the tree that stores the elements */
    t8_tree_t tree = (t8_tree_t) t8_sc_array_index_locidx (forest->trees, itree);
    /* Get the scheme of the current tree */
    const size_t num_elements = t8_element_array_get_count (&tree->leaf_elements);
    for (t8_locidx_t ielem = 0; ielem < (t8_locidx_t) num_elements; ielem++) {
      const t8_element_t *elem = t8_element_array_index_locidx (&tree->leaf_elements, ielem);
      num_points += scheme->element_get_num_corners (tree_class, elem);
    }
  }
  if (count_ghosts) {
    T8_ASSERT (forest->ghosts != NULL);
    /* We also count the points of the ghost cells */
    const t8_locidx_t num_ghosts = t8_forest_ghost_num_trees (forest);
    for (t8_locidx_t itree = 0; itree < num_ghosts; itree++) {
      /* Get the element class of the ghost */
      const t8_eclass_t ghost_class = t8_forest_ghost_get_tree_class (forest, itree);
      const t8_element_array_t *ghost_elem = t8_forest_ghost_get_tree_leaf_elements (forest, itree);
      const size_t num_elements = t8_forest_ghost_tree_num_leaf_elements (forest, itree);
      for (t8_locidx_t ielem = 0; ielem < (t8_locidx_t) num_elements; ielem++) {
        const t8_element_t *elem = t8_element_array_index_locidx (ghost_elem, ielem);
        num_points += scheme->element_get_num_corners (ghost_class, elem);
      }
    }
  }
  return num_points;
}

static int
t8_forest_vtk_cells_vertices_kernel (t8_forest_t forest, const t8_locidx_t ltree_id,
                                     [[maybe_unused]] const t8_tree_t tree,
                                     [[maybe_unused]] const t8_locidx_t element_index, const t8_element_t *element,
                                     const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost, FILE *vtufile,
                                     int *columns, [[maybe_unused]] void **data, T8_VTK_KERNEL_MODUS modus)
{
  double element_coordinates[3];
  int num_el_vertices, ivertex;
  int freturn;
  t8_element_shape_t element_shape;

  if (modus != T8_VTK_KERNEL_EXECUTE) {
    /* Nothing to do if we are in Init or clean up mode */
    return 1;
  }

  /* TODO: be careful with pyramid class here.
   *       does this work too over tree->class or do we need something else?
   */
  const t8_scheme *scheme = t8_forest_get_scheme (forest);
  element_shape = scheme->element_get_shape (tree_class, element);
  num_el_vertices = t8_eclass_num_vertices[element_shape];
  for (ivertex = 0; ivertex < num_el_vertices; ivertex++) {
    const double *ref_coords = t8_forest_vtk_point_to_element_ref_coords[element_shape][ivertex];
    t8_forest_element_from_ref_coords (forest, ltree_id, element, ref_coords, 1, element_coordinates);
    freturn = fprintf (vtufile, "         ");
    if (freturn <= 0) {
      return 0;
    }
#ifdef T8_VTK_DOUBLES
    freturn = fprintf (vtufile, " %24.16e %24.16e %24.16e\n", element_coordinates[0], element_coordinates[1],
                       element_coordinates[2]);
#else
    freturn = fprintf (vtufile, " %16.8e %16.8e %16.8e\n", element_coordinates[0], element_coordinates[1],
                       element_coordinates[2]);
#endif
    if (freturn <= 0) {
      return 0;
    }
    /* We switch of the column control of the surrounding function
     * by keeping the columns value constant. */
    *columns = 1;
  }
  return 1;
}

static int
t8_forest_vtk_cells_connectivity_kernel (t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltree_id,
                                         [[maybe_unused]] const t8_tree_t tree,
                                         [[maybe_unused]] const t8_locidx_t element_index, const t8_element_t *element,
                                         const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost,
                                         FILE *vtufile, int *columns, void **data, T8_VTK_KERNEL_MODUS modus)
{
  int ivertex, num_vertices;
  int freturn;
  t8_locidx_t *count_vertices;
  t8_element_shape_t element_shape;

  if (modus == T8_VTK_KERNEL_INIT) {
    /* We use data to count the number of written vertices */
    *data = T8_ALLOC_ZERO (t8_locidx_t, 1);
    return 1;
  }
  else if (modus == T8_VTK_KERNEL_CLEANUP) {
    T8_FREE (*data);
    return 1;
  }
  T8_ASSERT (modus == T8_VTK_KERNEL_EXECUTE);

  count_vertices = (t8_locidx_t *) *data;
  const t8_scheme *scheme = t8_forest_get_scheme (forest);
  element_shape = scheme->element_get_shape (tree_class, element);
  num_vertices = t8_eclass_num_vertices[element_shape];
  for (ivertex = 0; ivertex < num_vertices; ++ivertex, (*count_vertices)++) {
    freturn = fprintf (vtufile, " %ld", (long) *count_vertices);
    if (freturn <= 0) {
      return 0;
    }
  }
  *columns += t8_eclass_num_vertices[element_shape];
  return 1;
}

static int
t8_forest_vtk_cells_offset_kernel (t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltree_id,
                                   [[maybe_unused]] const t8_tree_t tree,
                                   [[maybe_unused]] const t8_locidx_t element_index, const t8_element_t *element,
                                   const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost, FILE *vtufile,
                                   int *columns, void **data, T8_VTK_KERNEL_MODUS modus)
{
  long long *offset;
  int freturn;
  int num_vertices;

  if (modus == T8_VTK_KERNEL_INIT) {
    *data = T8_ALLOC_ZERO (long long, 1);
    return true;
  }
  else if (modus == T8_VTK_KERNEL_CLEANUP) {
    T8_FREE (*data);
    return true;
  }
  T8_ASSERT (modus == T8_VTK_KERNEL_EXECUTE);

  offset = (long long *) *data;

  const t8_scheme *scheme = t8_forest_get_scheme (forest);
  num_vertices = t8_eclass_num_vertices[scheme->element_get_shape (tree_class, element)];
  *offset += num_vertices;
  freturn = fprintf (vtufile, " %lld", *offset);
  if (freturn <= 0) {
    return false;
  }
  *columns += 1;

  return 1;
}

static int
t8_forest_vtk_cells_type_kernel (t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltree_id,
                                 [[maybe_unused]] const t8_tree_t tree,
                                 [[maybe_unused]] const t8_locidx_t element_index, const t8_element_t *element,
                                 const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost, FILE *vtufile,
                                 int *columns, [[maybe_unused]] void **data, T8_VTK_KERNEL_MODUS modus)
{
  int freturn;
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    /* print the vtk type of the element */
    const t8_scheme *scheme = t8_forest_get_scheme (forest);
    freturn = fprintf (vtufile, " %d", t8_eclass_vtk_type[scheme->element_get_shape (tree_class, element)]);
    if (freturn <= 0) {
      return 0;
    }
    *columns += 1;
  }
  return 1;
}

static int
t8_forest_vtk_cells_level_kernel (t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltree_id,
                                  [[maybe_unused]] const t8_tree_t tree,
                                  [[maybe_unused]] const t8_locidx_t element_index, const t8_element_t *element,
                                  const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost, FILE *vtufile,
                                  int *columns, [[maybe_unused]] void **data, T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    const t8_scheme *scheme = t8_forest_get_scheme (forest);
    fprintf (vtufile, "%i ", scheme->element_get_level (tree_class, element));
    *columns += 1;
  }
  return 1;
}

static int
t8_forest_vtk_cells_rank_kernel (t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltree_id,
                                 [[maybe_unused]] const t8_tree_t tree,
                                 [[maybe_unused]] const t8_locidx_t element_index,
                                 [[maybe_unused]] const t8_element_t *element,
                                 [[maybe_unused]] const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost,
                                 FILE *vtufile, int *columns, [[maybe_unused]] void **data, T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    fprintf (vtufile, "%i ", forest->mpirank);
    *columns += 1;
  }
  return 1;
}

static int
t8_forest_vtk_cells_treeid_kernel (t8_forest_t forest, const t8_locidx_t ltree_id,
                                   [[maybe_unused]] const t8_tree_t tree,
                                   [[maybe_unused]] const t8_locidx_t element_index,
                                   [[maybe_unused]] const t8_element_t *element,
                                   [[maybe_unused]] const t8_eclass_t tree_class, const int is_ghost, FILE *vtufile,
                                   int *columns, [[maybe_unused]] void **data, T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    long long tree_id;
    if (is_ghost) {
      /* For ghost elements we write -1 as the tree is */
      tree_id = -1;
    }
    else {
      /* Otherwise the global tree id */
      tree_id = (long long) ltree_id + forest->first_local_tree;
    }
    fprintf (vtufile, "%lli ", tree_id);
    *columns += 1;
  }
  return 1;
}

static int
t8_forest_vtk_cells_elementid_kernel (t8_forest_t forest, [[maybe_unused]] const t8_locidx_t ltree_id,
                                      const t8_tree_t tree, [[maybe_unused]] const t8_locidx_t element_index,
                                      [[maybe_unused]] const t8_element_t *element,
                                      [[maybe_unused]] const t8_eclass_t tree_class, const int is_ghost, FILE *vtufile,
                                      int *columns, [[maybe_unused]] void **data, T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    if (!is_ghost) {
      fprintf (vtufile, "%lli ",
               element_index + tree->elements_offset + (long long) t8_forest_get_first_local_leaf_element_id (forest));
    }
    else {
      fprintf (vtufile, "%lli ", (long long) -1);
    }
    *columns += 1;
  }
  return 1;
}

/** Given a tree id and an element in the tree compute the
 * data index, that is the index 0 <= I < num_local_elements + num_local_ghosts
 * corresponding to the element.
 * \param [in] forest A committed forest
 * \param [in] ltree_or_ghost_id The Id of a local tree or ghost. 0 <= \a ltree_or_ghost_id < num_local_trees + num_ghost_trees
 * \param [in] element_in_tree_index An index of an element of the tree. 0 <= \a element_in_tree_index < num_elements_of_tree(\a ltree_or_ghost_id)
 * \return The index I, 0 <= \a I < num_local_elements + num_local_ghosts corresponding to the
 * data entry of the element as used in array for i.e. \ref t8_forest_ghost_exchange_data or
 * \ref t8_forest_partition_data.
*/
static t8_locidx_t
t8_forest_compute_data_index (const t8_forest_t forest,
                              const t8_locidx_t ltree_or_ghost_id,  // 0<= ID < num_local_trees + num_ghosts
                              const t8_locidx_t element_in_tree_index)
{
  const bool is_local = t8_forest_tree_is_local (forest, ltree_or_ghost_id);

  if (is_local) {
    const t8_locidx_t element_offset = t8_forest_get_tree_element_offset (forest, ltree_or_ghost_id);
    return element_offset + element_in_tree_index;
  }
  else {
    // Compute the tree id of the ghost tree
    const t8_locidx_t ghost_tree_id = ltree_or_ghost_id - t8_forest_get_num_local_trees (forest);
    // Compute the offset among ghost elements
    const t8_locidx_t ghost_tree_offset = t8_forest_ghost_get_tree_element_offset (forest, ghost_tree_id);
    // Add the local element count
    const t8_locidx_t ghost_element_offset = ghost_tree_offset + t8_forest_get_local_num_leaf_elements (forest);
    return ghost_element_offset + element_in_tree_index;
  }
}

static int
t8_forest_vtk_cells_scalar_kernel (t8_forest_t forest, const t8_locidx_t ltree_id,
                                   [[maybe_unused]] const t8_tree_t tree, const t8_locidx_t element_index,
                                   [[maybe_unused]] const t8_element_t *element,
                                   [[maybe_unused]] const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost,
                                   FILE *vtufile, int *columns, void **data, T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    /* For local elements access the data array, for ghosts, write 0 */
    const t8_locidx_t data_index = t8_forest_compute_data_index (forest, ltree_id, element_index);
    const double element_value = ((double *) *data)[data_index];
    fprintf (vtufile, "%g ", element_value);
    *columns += 1;
  }
  return 1;
}

static int
t8_forest_vtk_cells_vector_kernel (t8_forest_t forest, const t8_locidx_t ltree_id,
                                   [[maybe_unused]] const t8_tree_t tree, const t8_locidx_t element_index,
                                   [[maybe_unused]] const t8_element_t *element,
                                   [[maybe_unused]] const t8_eclass_t tree_class, [[maybe_unused]] const int is_ghost,
                                   FILE *vtufile, int *columns, void **data, T8_VTK_KERNEL_MODUS modus)
{

  if (modus == T8_VTK_KERNEL_EXECUTE) {
    const int dim = 3;
    T8_ASSERT (forest->dimension <= 3);
    /* For local elements access the data array, for ghosts, write 0 */
    const t8_locidx_t data_index = t8_forest_compute_data_index (forest, ltree_id, element_index);
    const double *element_values = ((double *) *data) + data_index * dim;
    for (int idim = 0; idim < dim; idim++) {
      fprintf (vtufile, "%g ", element_values[idim]);
    }
    *columns += dim;
  }
  return 1;
}

/* The point data version of the scalar kernel */
static int
t8_forest_vtk_vertices_scalar_kernel (t8_forest_t forest, const t8_locidx_t ltree_id,
                                      [[maybe_unused]] const t8_tree_t tree, const t8_locidx_t element_index,
                                      const t8_element_t *element, const t8_eclass_t tree_class,
                                      [[maybe_unused]] const int is_ghost, FILE *vtufile, int *columns, void **data,
                                      T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    const t8_scheme *scheme = t8_forest_get_scheme (forest);
    const int num_vertex = scheme->element_get_num_corners (tree_class, element);

    for (int ivertex = 0; ivertex < num_vertex; ivertex++) {
      const t8_locidx_t scalar_index = t8_forest_compute_data_index (forest, ltree_id, element_index);
      const double element_value = ((double *) *data)[scalar_index];
      fprintf (vtufile, "%g ", element_value);
      *columns += 1;
    }
  }
  return 1;
}

/* The point data version of the vector kernel */
static int
t8_forest_vtk_vertices_vector_kernel (t8_forest_t forest, const t8_locidx_t ltree_id,
                                      [[maybe_unused]] const t8_tree_t tree, const t8_locidx_t element_index,
                                      const t8_element_t *element, const t8_eclass_t tree_class,
                                      [[maybe_unused]] const int is_ghost, FILE *vtufile, int *columns, void **data,
                                      T8_VTK_KERNEL_MODUS modus)
{
  if (modus == T8_VTK_KERNEL_EXECUTE) {
    const t8_scheme *scheme = t8_forest_get_scheme (forest);
    const int num_vertex = scheme->element_get_num_corners (tree_class, element);
    for (int ivertex = 0; ivertex < num_vertex; ivertex++) {
      constexpr int dim = 3;
      T8_ASSERT (forest->dimension <= 3);
      /* Get a pointer to the start of the element's vector data */
      const t8_locidx_t data_index = t8_forest_compute_data_index (forest, ltree_id, element_index);
      const double *element_values = ((double *) *data) + (data_index) *dim;
      for (int idim = 0; idim < dim; idim++) {
        fprintf (vtufile, "%g ", element_values[idim]);
      }
      *columns += dim;
    }
  }
  return 1;
}

/* Iterate over all cells and write cell data to the file using
 * the cell_data_kernel as callback */
static int
t8_forest_vtk_write_cell_data (t8_forest_t forest, FILE *vtufile, const char *dataname, const char *datatype,
                               const char *component_string, const int max_columns,
                               t8_forest_vtk_cell_data_kernel kernel, const int write_ghosts, void *udata)
{
  int freturn;
  int countcols;
  t8_tree_t tree;
  t8_locidx_t itree, ighost;
  t8_locidx_t element_index, elems_in_tree;
  t8_locidx_t num_local_trees, num_ghost_trees;
  t8_element_t *element;
  void *data = NULL;

  /* Write the connectivity information.
   * Thus for each tree we write the indices of its corner vertices. */
  freturn = fprintf (vtufile,
                     "        <DataArray type=\"%s\" "
                     "Name=\"%s\" %s format=\"ascii\">\n         ",
                     datatype, dataname, component_string);
  if (freturn <= 0) {
    return 0;
  }

  /* if udata != NULL, use it as the data pointer, in this case, the kernel
   * should not modify it */
  if (udata != NULL) {
    data = udata;
  }

  /* Call the kernel in initialization modus to possibly initialize the
   * data pointer */
  kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_COUNT, 0, NULL, NULL, &data, T8_VTK_KERNEL_INIT);
  /* We iterate over the trees and count each trees vertices,
   * we add this to the already counted vertices and write it to the file */
  /* TODO: replace with an element iterator */
  num_local_trees = t8_forest_get_num_local_trees (forest);
  for (itree = 0, countcols = 0; itree < num_local_trees; itree++) {
    /* Get the tree that stores the elements */
    tree = t8_forest_get_tree (forest, itree);
    /* Get the eclass scheme of the tree */
    const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree);
    elems_in_tree = (t8_locidx_t) t8_element_array_get_count (&tree->leaf_elements);
    for (element_index = 0; element_index < elems_in_tree; element_index++) {
      /* Get a pointer to the element */
      element = t8_forest_get_leaf_element (forest, tree->elements_offset + element_index, NULL);
      T8_ASSERT (element != NULL);
      /* Execute the given callback on each element */
      if (!kernel (forest, itree, tree, element_index, element, tree_class, 0, vtufile, &countcols, &data,
                   T8_VTK_KERNEL_EXECUTE)) {
        /* call the kernel in clean-up modus */
        kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_COUNT, 0, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
        return 0;
      }
      /* After max_columns we break the line */
      if (!(countcols % max_columns)) {
        freturn = fprintf (vtufile, "\n         ");
        if (freturn <= 0) {
          /* call the kernel in clean-up modus */
          kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_COUNT, 0, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
          return 0;
        }
      }
    } /* element loop ends here */
    if (freturn <= 0) {
      /* call the kernel in clean-up modus */
      kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_INVALID, 0, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
      return 0;
    }
  } /* tree loop ends here */

  if (write_ghosts) {
    t8_locidx_t num_ghosts_in_tree;
    /* Iterate over the ghost elements */
    /* TODO: replace with an element iterator */
    num_ghost_trees = t8_forest_ghost_num_trees (forest);
    for (ighost = 0; ighost < num_ghost_trees; ighost++) {
      /* Get the eclass of the ghost tree */
      const t8_eclass_t ghost_eclass = t8_forest_ghost_get_tree_class (forest, ighost);
      /* The number of ghosts in this tree */
      num_ghosts_in_tree = t8_forest_ghost_tree_num_leaf_elements (forest, ighost);
      for (element_index = 0; element_index < num_ghosts_in_tree; element_index++) {
        /* Get a pointer to the element */
        element = t8_forest_ghost_get_leaf_element (forest, ighost, element_index);
        /* Execute the given callback on each element */
        if (!kernel (forest, ighost + num_local_trees, NULL, element_index, element, ghost_eclass, 1, vtufile,
                     &countcols, &data, T8_VTK_KERNEL_EXECUTE)) {
          /* call the kernel in clean-up modus */
          kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_INVALID, 1, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
          return 0;
        }
        /* After max_columns we break the line */
        if (!(countcols % max_columns)) {
          freturn = fprintf (vtufile, "\n         ");
          if (freturn <= 0) {
            /* call the kernel in clean-up modus */
            kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_INVALID, 1, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
            return 0;
          }
        }
      } /* element loop ends here */
      if (freturn <= 0) {
        /* call the kernel in clean-up modus */
        kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_INVALID, 1, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
        return 0;
      }
    } /* ghost loop ends here */
  }   /* write_ghosts ends here */
  /* call the kernel in clean-up modus */
  kernel (NULL, 0, NULL, 0, NULL, T8_ECLASS_INVALID, 0, NULL, NULL, &data, T8_VTK_KERNEL_CLEANUP);
  freturn = fprintf (vtufile, "\n        </DataArray>\n");
  if (freturn <= 0) {
    return 0;
  }

  return 1;
}

/* Write the cell data to an open file stream.
 * Returns true on success and zero otherwise.
 * After completion the file will remain open, whether writing
 * cells was successful or not. */
static int
t8_forest_vtk_write_cells (t8_forest_t forest, FILE *vtufile, const int write_treeid, const int write_mpirank,
                           const int write_level, const int write_element_id, const int write_ghosts,
                           const int num_data, t8_vtk_data_field_t *data)
{
  int freturn;
  int idata;

  T8_ASSERT (t8_forest_is_committed (forest));
  T8_ASSERT (vtufile != NULL);

  freturn = fprintf (vtufile, "      <Cells>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_cell_failure;
  }

  /* Write the connectivity information.
   * Thus for each tree we write the indices of its corner vertices. */
  freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "connectivity", T8_VTK_LOCIDX, "", 8,
                                           t8_forest_vtk_cells_connectivity_kernel, write_ghosts, NULL);
  if (!freturn) {
    goto t8_forest_vtk_cell_failure;
  }
  /* Done with writing the connectivity */

  /* Write the offsets, that is for each tree the index of the first entry
   * in the connectivity output that
   * does not refer to a vertex of the tree anymore.
   * For example if the trees are a square and a triangle, the offsets would
   * be 4 and 7, since indices 0,1,2,3 refer to the vertices of the square
   * and indices 4,5,6 to the indices of the triangle. */
  freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "offsets", T8_VTK_LOCIDX, "", 8,
                                           t8_forest_vtk_cells_offset_kernel, write_ghosts, NULL);
  if (!freturn) {
    goto t8_forest_vtk_cell_failure;
  }
  /* Done with writing the offsets */

  /* Write the element types. The type specifies the element class, thus
   * square/triangle/tet etc. */

  freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "types", "Int32", "", 8, t8_forest_vtk_cells_type_kernel,
                                           write_ghosts, NULL);

  if (!freturn) {
    goto t8_forest_vtk_cell_failure;
  }
  /* Done with writing the types */
  freturn = fprintf (vtufile, "      </Cells>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_cell_failure;
  }
  /* clang-format off */
  freturn = fprintf (vtufile, "      <CellData Scalars =\"%s%s\">\n", "treeid,mpirank,level",
                     (write_element_id ? "id" : ""));
  /* clang-format on */
  if (freturn <= 0) {
    goto t8_forest_vtk_cell_failure;
  }

  if (write_treeid) {
    /* Write the tree ids. */

    freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "treeid", T8_VTK_GLOIDX, "", 8,
                                             t8_forest_vtk_cells_treeid_kernel, write_ghosts, NULL);
    if (!freturn) {
      goto t8_forest_vtk_cell_failure;
    }
    /* Done with writing the tree ids */
  }
  if (write_mpirank) {
    /* Write the mpiranks. */

    freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "mpirank", "Int32", "", 8,
                                             t8_forest_vtk_cells_rank_kernel, write_ghosts, NULL);
    if (!freturn) {
      goto t8_forest_vtk_cell_failure;
    }
    /* Done with writing the mpiranks */
  }
  if (write_level) {
    /* Write the element refinement levels. */

    freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "level", "Int32", "", 8, t8_forest_vtk_cells_level_kernel,
                                             write_ghosts, NULL);
    if (!freturn) {
      goto t8_forest_vtk_cell_failure;
    }
    /* Done with writing the levels */
  }

  if (write_element_id) {
    /* Write the element ids. */
    const char *datatype;

    /* Use 32 bit ints if the global element count fits, 64 bit otherwise. */
    datatype = forest->global_num_leaf_elements > T8_LOCIDX_MAX ? T8_VTK_GLOIDX : T8_VTK_LOCIDX;
    freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "element_id", datatype, "", 8,
                                             t8_forest_vtk_cells_elementid_kernel, write_ghosts, NULL);
    if (!freturn) {
      goto t8_forest_vtk_cell_failure;
    }

    /* Done with writing the element ids */
  }
  /* Write the user defined data fields per element */
  for (idata = 0; idata < num_data; idata++) {
    if (data[idata].type == T8_VTK_SCALAR) {
      freturn = t8_forest_vtk_write_cell_data (forest, vtufile, data[idata].description, T8_VTK_FLOAT_NAME, "", 8,
                                               t8_forest_vtk_cells_scalar_kernel, write_ghosts, data[idata].data);
    }
    else {
      char component_string[BUFSIZ];
      T8_ASSERT (data[idata].type == T8_VTK_VECTOR);
      snprintf (component_string, BUFSIZ, "NumberOfComponents=\"3\"");
      freturn = t8_forest_vtk_write_cell_data (forest, vtufile, data[idata].description, T8_VTK_FLOAT_NAME,
                                               component_string, 8 * forest->dimension,
                                               t8_forest_vtk_cells_vector_kernel, write_ghosts, data[idata].data);
    }
    if (!freturn) {
      goto t8_forest_vtk_cell_failure;
    }
  }

  freturn = fprintf (vtufile, "      </CellData>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_cell_failure;
  }

  /* Function completed successfully */
  return 1;
t8_forest_vtk_cell_failure:
  /* Something went wrong */
  t8_errorf ("Error when writing cell data to forest vtk file.\n");
  return 0;
}

/* Write the cell data to an open file stream.
 * Returns true on success and zero otherwise.
 * After completion the file will remain open, whether writing
 * cells was successful or not. */
static int
t8_forest_vtk_write_points (t8_forest_t forest, FILE *vtufile, const int write_ghosts, const int num_data,
                            t8_vtk_data_field_t *data)
{
  int freturn;
  int sreturn;
  int idata;
  char description[BUFSIZ];

  T8_ASSERT (t8_forest_is_committed (forest));
  T8_ASSERT (vtufile != NULL);

  /* Write the vertex coordinates */

  freturn = fprintf (vtufile, "      <Points>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_cell_failure;
  }
  freturn = t8_forest_vtk_write_cell_data (forest, vtufile, "Position", T8_VTK_FLOAT_NAME, "NumberOfComponents=\"3\"",
                                           8, t8_forest_vtk_cells_vertices_kernel, write_ghosts, NULL);
  if (!freturn) {
    goto t8_forest_vtk_cell_failure;
  }
  freturn = fprintf (vtufile, "      </Points>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_cell_failure;
  }
  /* Done writing vertex coordinates */

  /* Write the user defined data fields per element */
  if (num_data > 0) {
    freturn = fprintf (vtufile, "      <PointData>\n");
    for (idata = 0; idata < num_data; idata++) {
      if (data[idata].type == T8_VTK_SCALAR) {
        /* Write the description string. */
        sreturn = snprintf (description, BUFSIZ, "%s_%s", data[idata].description, "points");

        if (sreturn >= BUFSIZ) {
          /* The output was truncated */
          t8_debugf ("Warning: Truncated vtk point data description to '%s'\n", description);
        }
        freturn = t8_forest_vtk_write_cell_data (forest, vtufile, description, T8_VTK_FLOAT_NAME, "", 8,
                                                 t8_forest_vtk_vertices_scalar_kernel, write_ghosts, data[idata].data);
      }
      else {
        char component_string[BUFSIZ];
        T8_ASSERT (data[idata].type == T8_VTK_VECTOR);
        snprintf (component_string, BUFSIZ, "NumberOfComponents=\"3\"");
        /* Write the description string. */
        sreturn = snprintf (description, BUFSIZ, "%s_%s", data[idata].description, "points");

        if (sreturn >= BUFSIZ) {
          /* The output was truncated */
          /* Note: gcc >= 7.1 prints a warning if we
           * do not check the return value of snprintf. */
          t8_debugf ("Warning: Truncated vtk point data description to '%s'\n", description);
        }

        freturn = t8_forest_vtk_write_cell_data (forest, vtufile, description, T8_VTK_FLOAT_NAME, component_string,
                                                 8 * forest->dimension, t8_forest_vtk_vertices_vector_kernel,
                                                 write_ghosts, data[idata].data);
      }
      if (!freturn) {
        goto t8_forest_vtk_cell_failure;
      }
    }
    freturn = fprintf (vtufile, "      </PointData>\n");
  }
  /* Function completed successfully */
  return 1;
t8_forest_vtk_cell_failure:
  /* Something went wrong */
  t8_errorf ("Error when writing cell data to forest vtk file.\n");
  return 0;
}

int
t8_forest_vtk_write_ASCII (t8_forest_t forest, const char *fileprefix, const int write_treeid, const int write_mpirank,
                           const int write_level, const int write_element_id, int write_ghosts, const int num_data,
                           t8_vtk_data_field_t *data)
{
  FILE *vtufile = NULL;
  t8_locidx_t num_elements, num_points;
  char vtufilename[BUFSIZ];
  int freturn;

  T8_ASSERT (forest != NULL);
  T8_ASSERT (t8_forest_is_committed (forest));
  T8_ASSERT (fileprefix != NULL);
  if (forest->ghosts == NULL || forest->ghosts->num_ghosts_elements == 0) {
    /* Never write ghost elements if there aren't any */
    write_ghosts = 0;
  }
  T8_ASSERT (forest->ghosts != NULL || !write_ghosts);

  /* process 0 creates the .pvtu file */
  if (forest->mpirank == 0) {
    if (t8_write_pvtu (fileprefix, forest->mpisize, write_treeid, write_mpirank, write_level, write_element_id,
                       num_data, data)) {
      t8_errorf ("Error when writing file %s.pvtu\n", fileprefix);
      goto t8_forest_vtk_failure;
    }
  }

  /* The local number of elements */
  num_elements = t8_forest_get_local_num_leaf_elements (forest);
  if (write_ghosts) {
    num_elements += t8_forest_get_num_ghosts (forest);
  }
  /* The local number of points, counted with multiplicity */
  num_points = t8_forest_num_points (forest, write_ghosts);

  /* The filename for this processes file */
  freturn = snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", fileprefix, forest->mpirank);
  if (freturn >= BUFSIZ) {
    t8_errorf ("Error when writing vtu file. Filename too long.\n");
    goto t8_forest_vtk_failure;
  }

  /* Open the vtufile to write to */
  vtufile = fopen (vtufilename, "w");
  if (vtufile == NULL) {
    t8_errorf ("Error when opening file %s\n", vtufilename);
    goto t8_forest_vtk_failure;
  }
  /* Write the header information in the .vtu file.
   * xml type, Unstructured grid and number of points and elements. */
  freturn = fprintf (vtufile, "<?xml version=\"1.0\"?>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_failure;
  }
  freturn = fprintf (vtufile, "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"");
  if (freturn <= 0) {
    goto t8_forest_vtk_failure;
  }
#ifdef SC_IS_BIGENDIAN
  freturn = fprintf (vtufile, " byte_order=\"BigEndian\">\n");
#else
  freturn = fprintf (vtufile, " byte_order=\"LittleEndian\">\n");
#endif
  if (freturn <= 0) {
    goto t8_forest_vtk_failure;
  }
  freturn = fprintf (vtufile, "  <UnstructuredGrid>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_failure;
  }
  freturn = fprintf (vtufile, "    <Piece NumberOfPoints=\"%lld\" NumberOfCells=\"%lld\">\n", (long long) num_points,
                     (long long) num_elements);
  if (freturn <= 0) {
    goto t8_forest_vtk_failure;
  }
  /* write the point data */
  if (!t8_forest_vtk_write_points (forest, vtufile, write_ghosts, num_data, data)) {
    /* writings points was not successful */
    goto t8_forest_vtk_failure;
  }
  /* write the cell data */
  if (!t8_forest_vtk_write_cells (forest, vtufile, write_treeid, write_mpirank, write_level, write_element_id,
                                  write_ghosts, num_data, data)) {
    /* Writing cells was not successful */
    goto t8_forest_vtk_failure;
  }

  freturn = fprintf (vtufile, "    </Piece>\n"
                              "  </UnstructuredGrid>\n"
                              "</VTKFile>\n");
  if (freturn <= 0) {
    goto t8_forest_vtk_failure;
  }

  freturn = fclose (vtufile);
  /* We set it not NULL, even if fclose was not successful, since then any
   * following call to fclose would result in undefined behaviour. */
  vtufile = NULL;
  if (freturn != 0) {
    /* Closing failed, this usually means that the final write operation could
     * not be completed. */
    t8_global_errorf ("Error when closing file %s\n", vtufilename);
    goto t8_forest_vtk_failure;
  }
  /* Writing was successful */
  return 1;
t8_forest_vtk_failure:
  if (vtufile != NULL) {
    fclose (vtufile);
  }
  t8_errorf ("Error when writing vtk file.\n");
  return 0;
}

/* Return the local number of vertices in a cmesh.
 * \param [in] cmesh       The cmesh to be considered.
 * \param [in] count_ghosts If true, we also count the vertices of the ghost trees.
 * \return                 The number of vertices associated to \a cmesh.
 * \a cmesh must be committed before calling this function.
 */
static t8_gloidx_t
t8_cmesh_get_num_vertices (const t8_cmesh_t cmesh, const int count_ghosts)
{
  int iclass;
  t8_eclass_t ghost_class;
  t8_gloidx_t num_vertices = 0;
  t8_locidx_t ighost;
  T8_ASSERT (cmesh != NULL);
  T8_ASSERT (cmesh->committed);

  for (iclass = T8_ECLASS_ZERO; iclass < T8_ECLASS_COUNT; iclass++) {
    num_vertices += t8_eclass_num_vertices[iclass] * cmesh->num_local_trees_per_eclass[iclass];
  }
  if (count_ghosts) {
    /* Also count the vertices of the ghost trees */
    for (ighost = 0; ighost < t8_cmesh_get_num_ghosts (cmesh); ighost++) {
      ghost_class = t8_cmesh_get_ghost_class (cmesh, ighost);
      num_vertices += t8_eclass_num_vertices[ghost_class];
    }
  }

  return num_vertices;
}

static int
t8_cmesh_vtk_write_file_ext (const t8_cmesh_t cmesh, const char *fileprefix, const int write_ghosts)
{
  T8_ASSERT (cmesh != NULL);
  T8_ASSERT (t8_cmesh_is_committed (cmesh));
  T8_ASSERT (fileprefix != NULL);

  /* Constants used as return values in order
   * to have more readable code. */
  constexpr int write_successful = 1;
  constexpr int write_failure = 0;

  if (cmesh->mpirank == 0) {
    /* Write the pvtu header file. */
    int num_ranks_that_write = cmesh->set_partition ? cmesh->mpisize : 1;
    if (t8_write_pvtu (fileprefix, num_ranks_that_write, 1, 1, 0, 0, 0, NULL)) {
      SC_ABORTF ("Error when writing file %s.pvtu\n", fileprefix);
    }
  }
  /* If the cmesh is replicated only rank 0 prints it,
   * otherwise each process prints its part of the cmesh.*/
  if (cmesh->mpirank == 0 || cmesh->set_partition) {
    char vtufilename[BUFSIZ];
    FILE *vtufile;
    t8_locidx_t num_vertices, ivertex;
    t8_locidx_t num_trees;
    t8_ctree_t tree;
    double x, y, z;
    double *vertices, *vertex;
    int k, sk;
    long long offset, count_vertices;
    t8_locidx_t ighost, num_ghosts = 0, num_loc_trees;
#if T8_ENABLE_DEBUG
    t8_cghost_t ghost;
#endif
    t8_eclass_t eclass;

    num_vertices = t8_cmesh_get_num_vertices (cmesh, write_ghosts);
    num_trees = t8_cmesh_get_num_local_trees (cmesh);
    if (write_ghosts) {
      num_trees += t8_cmesh_get_num_ghosts (cmesh);
    }

    snprintf (vtufilename, BUFSIZ, "%s_%04d.vtu", fileprefix, cmesh->mpirank);
    vtufile = fopen (vtufilename, "wb");
    if (vtufile == NULL) {
      t8_global_errorf ("Could not open file %s for output.\n", vtufilename);
      return write_failure;
    }
    fprintf (vtufile, "<?xml version=\"1.0\"?>\n");
    fprintf (vtufile, "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"");
#ifdef SC_IS_BIGENDIAN
    fprintf (vtufile, " byte_order=\"BigEndian\">\n");
#else
    fprintf (vtufile, " byte_order=\"LittleEndian\">\n");
#endif
    fprintf (vtufile, "  <UnstructuredGrid>\n");
    fprintf (vtufile, "    <Piece NumberOfPoints=\"%lld\" NumberOfCells=\"%lld\">\n", (long long) num_vertices,
             (long long) num_trees);
    fprintf (vtufile, "      <Points>\n");

    /* write point position data */
    fprintf (vtufile,
             "        <DataArray type=\"%s\" Name=\"Position\""
             " NumberOfComponents=\"3\" format=\"%s\">\n",
             T8_VTK_FLOAT_NAME, T8_VTK_FORMAT_STRING);

    for (tree = t8_cmesh_get_first_tree (cmesh); tree != NULL; tree = t8_cmesh_get_next_tree (cmesh, tree)) {
      /*  TODO: Use new geometry here. Need cmesh_get_reference coords function. */
      vertices = t8_cmesh_get_tree_vertices (cmesh, tree->treeid);
      if (vertices == nullptr) {
        t8_errorf ("Error in writing file %s. Could not read vertex coordinates for cmesh tree %i.\n", vtufilename,
                   tree->treeid);
        fclose (vtufile);
        return write_failure;
      }
      for (ivertex = 0; ivertex < t8_eclass_num_vertices[tree->eclass]; ivertex++) {
        vertex = vertices + 3 * t8_eclass_t8_to_vtk_corner_number[tree->eclass][ivertex];
        x = vertex[0];
        y = vertex[1];
        z = vertex[2];
#ifdef T8_VTK_DOUBLES
        fprintf (vtufile, "     %24.16e %24.16e %24.16e\n", x, y, z);
#else
        fprintf (vtufile, "          %16.8e %16.8e %16.8e\n", x, y, z);
#endif
      }
    } /* end tree loop */
    if (write_ghosts) {

      /* Write the vertices of the ghost trees */
      num_ghosts = t8_cmesh_get_num_ghosts (cmesh);
      num_loc_trees = t8_cmesh_get_num_local_trees (cmesh);
      for (ighost = 0; ighost < num_ghosts; ighost++) {
        /* Get the eclass of this ghost */
        eclass = t8_cmesh_get_ghost_class (cmesh, ighost);
        /* Get a pointer to this ghosts vertices */
        vertices = (double *) t8_cmesh_get_attribute (cmesh, t8_get_package_id (), 0, ighost + num_loc_trees);
        if (vertices == nullptr) {
          t8_errorf ("Error in writing file %s. Could not read vertex coordinates for cmesh tree %i.\n", vtufilename,
                     tree->treeid);
          fclose (vtufile);
          return write_failure;
        }

        T8_ASSERT (vertices != NULL);
        /* TODO: This code is duplicated above */
        for (ivertex = 0; ivertex < t8_eclass_num_vertices[eclass]; ivertex++) {
          vertex = vertices + 3 * t8_eclass_vtk_to_t8_corner_number[eclass][ivertex];
          x = vertex[0];
          y = vertex[1];
          z = vertex[2];
#ifdef T8_VTK_DOUBLES
          fprintf (vtufile, "     %24.16e %24.16e %24.16e\n", x, y, z);
#else
          fprintf (vtufile, "          %16.8e %16.8e %16.8e\n", x, y, z);
#endif
        }
      } /* end ghost loop */
    }
    fprintf (vtufile, "        </DataArray>\n");
    fprintf (vtufile, "      </Points>\n");
    fprintf (vtufile, "      <Cells>\n");

    /* write connectivity data */
    fprintf (vtufile,
             "        <DataArray type=\"%s\" Name=\"connectivity\""
             " format=\"%s\">\n",
             T8_VTK_LOCIDX, T8_VTK_FORMAT_STRING);
    for (tree = t8_cmesh_get_first_tree (cmesh), count_vertices = 0; tree != NULL;
         tree = t8_cmesh_get_next_tree (cmesh, tree)) {
      fprintf (vtufile, "         ");
      for (k = 0; k < t8_eclass_num_vertices[tree->eclass]; ++k, count_vertices++) {
        fprintf (vtufile, " %lld", count_vertices);
      }
      fprintf (vtufile, "\n");
    }
    if (write_ghosts) {
      /* Write the ghost connectivity */
      for (ighost = 0; ighost < num_ghosts; ighost++) {
        eclass = t8_cmesh_get_ghost_class (cmesh, ighost);
        fprintf (vtufile, "         ");
        for (k = 0; k < t8_eclass_num_vertices[eclass]; ++k, count_vertices++) {
          fprintf (vtufile, " %lld", count_vertices);
        }
        fprintf (vtufile, "\n");
      }
    }
    fprintf (vtufile, "        </DataArray>\n");

    /* write offset data */
    fprintf (vtufile,
             "        <DataArray type=\"%s\" Name=\"offsets\""
             " format=\"%s\">\n",
             T8_VTK_LOCIDX, T8_VTK_FORMAT_STRING);
    fprintf (vtufile, "         ");
    for (tree = t8_cmesh_get_first_tree (cmesh), sk = 1, offset = 0; tree != NULL;
         tree = t8_cmesh_get_next_tree (cmesh, tree), ++sk) {
      offset += t8_eclass_num_vertices[tree->eclass];
      fprintf (vtufile, " %lld", offset);
      if (!(sk % 8))
        fprintf (vtufile, "\n         ");
    }
    if (write_ghosts) {
      /* ghost offset data */
      for (ighost = 0; ighost < num_ghosts; ighost++, ++sk) {
        eclass = t8_cmesh_get_ghost_class (cmesh, ighost);
        offset += t8_eclass_num_vertices[eclass];
        fprintf (vtufile, " %lld", offset);
        if (!(sk % 8))
          fprintf (vtufile, "\n         ");
      }
    }
    fprintf (vtufile, "\n");
    fprintf (vtufile, "        </DataArray>\n");
    /* write type data */
    fprintf (vtufile,
             "        <DataArray type=\"UInt8\" Name=\"types\""
             " format=\"%s\">\n",
             T8_VTK_FORMAT_STRING);
    fprintf (vtufile, "         ");
    for (tree = t8_cmesh_get_first_tree (cmesh), sk = 1; tree != NULL;
         tree = t8_cmesh_get_next_tree (cmesh, tree), ++sk) {
      fprintf (vtufile, " %d", t8_eclass_vtk_type[tree->eclass]);
      if (!(sk % 20) && tree->treeid != (cmesh->num_local_trees - 1))
        fprintf (vtufile, "\n         ");
    }
    if (write_ghosts) {
      /* ghost offset types */
      for (ighost = 0; ighost < num_ghosts; ighost++, ++sk) {
        eclass = t8_cmesh_get_ghost_class (cmesh, ighost);
        fprintf (vtufile, " %d", t8_eclass_vtk_type[eclass]);
        if (!(sk % 20) && ighost != (num_ghosts - 1))
          fprintf (vtufile, "\n         ");
      }
    }
    fprintf (vtufile, "\n");
    fprintf (vtufile, "        </DataArray>\n");
    fprintf (vtufile, "      </Cells>\n");
    /* write treeif data */
    fprintf (vtufile, "      <CellData Scalars=\"treeid,mpirank\">\n");
    fprintf (vtufile,
             "        <DataArray type=\"%s\" Name=\"treeid\""
             " format=\"%s\">\n",
             T8_VTK_GLOIDX, T8_VTK_FORMAT_STRING);
    fprintf (vtufile, "         ");
    for (tree = t8_cmesh_get_first_tree (cmesh), sk = 1, offset = 0; tree != NULL;
         tree = t8_cmesh_get_next_tree (cmesh, tree), ++sk) {
      /* Since tree_id is actually 64 Bit but we store it as 32, we have to check
       * that we do not get into conversion errors */
      /* TODO: We switched to 32 Bit because Paraview could not handle 64 well enough.
       */
      T8_ASSERT (tree->treeid + cmesh->first_tree == (t8_gloidx_t) ((long) tree->treeid + cmesh->first_tree));
      fprintf (vtufile, " %" T8_GLOIDX_FORMAT, tree->treeid + cmesh->first_tree);
      if (!(sk % 8))
        fprintf (vtufile, "\n         ");
    }
    if (write_ghosts) {
      /* ghost offset types */
      for (ighost = 0; ighost < num_ghosts; ighost++, ++sk) {
#if T8_ENABLE_DEBUG
        ghost = t8_cmesh_trees_get_ghost (cmesh->trees, ighost);
        /* Check for conversion errors */
        T8_ASSERT (ghost->treeid == (t8_gloidx_t) ((long) ghost->treeid));
#endif
        /* Write -1 as tree_id so that we can distinguish ghosts from normal trees
         * in the vtk file */
        fprintf (vtufile, " %ld", (long) -1);
        if (!(sk % 8))
          fprintf (vtufile, "\n         ");
      }
    }
    fprintf (vtufile, "\n");
    fprintf (vtufile, "        </DataArray>\n");
    /* write mpirank data */
    fprintf (vtufile,
             "        <DataArray type=\"%s\" Name=\"mpirank\""
             " format=\"%s\">\n",
             "Int32", T8_VTK_FORMAT_STRING);
    fprintf (vtufile, "         ");
    for (tree = t8_cmesh_get_first_tree (cmesh), sk = 1, offset = 0; tree != NULL;
         tree = t8_cmesh_get_next_tree (cmesh, tree), ++sk) {
      fprintf (vtufile, " %i", cmesh->mpirank);
      if (!(sk % 8))
        fprintf (vtufile, "\n         ");
    }
    if (write_ghosts) {
      /* write our rank for each ghost */
      for (ighost = 0; ighost < num_ghosts; ighost++, ++sk) {
        fprintf (vtufile, " %i", cmesh->mpirank);
        if (!(sk % 8))
          fprintf (vtufile, "\n         ");
      }
    }
    fprintf (vtufile, "\n");
    fprintf (vtufile, "        </DataArray>\n");
    fprintf (vtufile, "      </CellData>\n");
    /* write type data */
    fprintf (vtufile, "    </Piece>\n");
    fprintf (vtufile, "  </UnstructuredGrid>\n");
    fprintf (vtufile, "</VTKFile>\n");
    fclose (vtufile);
  }
  return write_successful;
}

int
t8_cmesh_vtk_write_ASCII (const t8_cmesh_t cmesh, const char *fileprefix)
{
  return t8_cmesh_vtk_write_file_ext (cmesh, fileprefix, 1);
}