diff --git a/.gitignore b/.gitignore
index 44d98c6ff5fa70ab2d5bf53752c30b40b69f6272..5615f036ef0f3a51db7c156afe69b8511e015d4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,7 +29,7 @@ examples/*/*.h5
 examples/*/*.png
 examples/*/*.mp4
 examples/*/*.txt
-examples/*/*.dot
+examples/*/dependency_graph_*.csv
 examples/*/restart/*
 examples/*/used_parameters.yml
 examples/*/unused_parameters.yml
diff --git a/examples/main.c b/examples/main.c
index aca3cf8b1f85c19d7522259f34094271ec9998c9..b571eebcbd48af899ec9b0a56b753a3cbd333986 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -477,8 +477,7 @@ int main(int argc, char *argv[]) {
   }
 
   /* Check that we can write the structure finding catalogues by testing if the
-   * output
-   * directory exists and is searchable and writable. */
+   * output directory exists and is searchable and writable. */
   if (with_structure_finding) {
     char stfbasename[PARSER_MAX_LINE_SIZE];
     parser_get_param_string(params, "StructureFinding:basename", stfbasename);
diff --git a/src/engine.c b/src/engine.c
index 2734fe6a1d572f19c6763931c314535170cc8b8c..5edbed475a822a50503e3766ac702cdf0db7b201 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -2798,7 +2798,7 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
     gravity_exact_force_compute(e->s, e);
 #endif
 
-  if (e->nodeID == 0) scheduler_write_dependencies(&e->sched, e->verbose);
+  scheduler_write_dependencies(&e->sched, e->verbose);
   if (e->nodeID == 0) scheduler_write_task_level(&e->sched);
 
   /* Run the 0th time-step */
@@ -4671,8 +4671,7 @@ void engine_config(int restart, struct engine *e, struct swift_params *params,
   /* Expected average for tasks per cell. If set to zero we use a heuristic
    * guess based on the numbers of cells and how many tasks per cell we expect.
    * On restart this number cannot be estimated (no cells yet), so we recover
-   * from the end of the dumped run. Can be changed on restart.
-   */
+   * from the end of the dumped run. Can be changed on restart. */
   e->tasks_per_cell =
       parser_get_opt_param_int(params, "Scheduler:tasks_per_cell", 0);
   int maxtasks = 0;
diff --git a/src/scheduler.c b/src/scheduler.c
index 70705e3ee48fc3a038bd3ecc835b77017588f24f..bf976e39f33afa2a92ec3a68202ec17ff70c2dc9 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -114,31 +115,217 @@ void scheduler_addunlock(struct scheduler *s, struct task *ta,
 }
 
 /**
- * @brief generate the dependency name for the tasks
+ * @brief compute the number of similar dependencies
  *
- * @param ta_type The #task type.
- * @param ta_subtype The #task type.
- * @param ta_name (return) The formatted string
+ * @param s The #scheduler
+ * @param ta The #task
+ * @param tb The dependent #task
+ *
+ * @return Number of dependencies
  */
-void scheduler_task_dependency_name(int ta_type, int ta_subtype,
-                                    char *ta_name) {
-
-  /* Check input */
-  if ((ta_type < 0) || (ta_type >= task_type_count))
-    error("Unknown task type %i", ta_type);
-
-  if ((ta_subtype < 0) || (ta_subtype >= task_subtype_count))
-    error("Unknown task subtype %i with type %s", ta_subtype,
-          taskID_names[ta_type]);
-
-  /* construct line */
-  if (ta_subtype == task_subtype_none)
-    sprintf(ta_name, "%s", taskID_names[ta_type]);
-  else
-    sprintf(ta_name, "\"%s %s\"", taskID_names[ta_type],
-            subtaskID_names[ta_subtype]);
+int scheduler_get_number_relation(const struct scheduler *s,
+                                  const struct task *ta,
+                                  const struct task *tb) {
+
+  int count = 0;
+
+  /* loop over all tasks */
+  for (int i = 0; i < s->nr_tasks; i++) {
+    const struct task *ta_tmp = &s->tasks[i];
+
+    /* and their dependencies */
+    for (int j = 0; j < ta->nr_unlock_tasks; j++) {
+      const struct task *tb_tmp = ta->unlock_tasks[j];
+
+      if (ta->type == ta_tmp->type && ta->subtype == ta_tmp->subtype &&
+          tb->type == tb_tmp->type && tb->subtype == tb_tmp->subtype) {
+        count += 1;
+      }
+    }
+  }
+  return count;
+}
+
+/* Conservative number of dependencies per task type */
+#define MAX_NUMBER_DEP 128
+
+/**
+ * @brief Informations about all the task dependencies of
+ *   a single task.
+ */
+struct task_dependency {
+  /* Main task */
+  /* ID of the task */
+  int type_in;
+
+  /* ID of the subtask */
+  int subtype_in;
+
+  /* Is the task implicit */
+  int implicit_in;
+
+  /* Dependent task */
+  /* ID of the dependent task */
+  int type_out[MAX_NUMBER_DEP];
+
+  /* ID of the dependent subtask */
+  int subtype_out[MAX_NUMBER_DEP];
+
+  /* Is the dependent task implicit */
+  int implicit_out[MAX_NUMBER_DEP];
+
+  /* Statistics */
+  /* number of link between the two task type */
+  int number_link[MAX_NUMBER_DEP];
+
+  /* number of ranks having this relation */
+  int number_rank[MAX_NUMBER_DEP];
+};
+
+#ifdef WITH_MPI
+/**
+ * @brief Define the #task_dependency for MPI
+ *
+ * @param tstype The #MPI_Datatype to initialize
+ */
+void task_dependency_define(MPI_Datatype *tstype) {
+
+  /* Define the variables */
+  const int count = 8;
+  int blocklens[count];
+  MPI_Datatype types[count];
+  MPI_Aint disps[count];
+
+  /* all the type are int */
+  for (int i = 0; i < count; i++) {
+    types[i] = MPI_INT;
+  }
+
+  /* Task in */
+  disps[0] = offsetof(struct task_dependency, type_in);
+  blocklens[0] = 1;
+  disps[1] = offsetof(struct task_dependency, subtype_in);
+  blocklens[1] = 1;
+  disps[2] = offsetof(struct task_dependency, implicit_in);
+  blocklens[2] = 1;
+
+  /* Task out */
+  disps[3] = offsetof(struct task_dependency, type_out);
+  blocklens[3] = MAX_NUMBER_DEP;
+  disps[4] = offsetof(struct task_dependency, subtype_out);
+  blocklens[4] = MAX_NUMBER_DEP;
+  disps[5] = offsetof(struct task_dependency, implicit_out);
+  blocklens[5] = MAX_NUMBER_DEP;
+
+  /* statistics */
+  disps[6] = offsetof(struct task_dependency, number_link);
+  blocklens[6] = MAX_NUMBER_DEP;
+  disps[7] = offsetof(struct task_dependency, number_rank);
+  blocklens[7] = MAX_NUMBER_DEP;
+
+  /* define it for MPI */
+  MPI_Type_create_struct(count, blocklens, disps, types, tstype);
+  MPI_Type_commit(tstype);
+}
+
+/**
+ * @brief Sum operator of #task_dependency for MPI
+ *
+ * @param in_p The #task_dependency to add
+ * @param out_p The #task_dependency where in_p is added
+ * @param len The length of the arrays
+ * @param type The MPI datatype
+ */
+void task_dependency_sum(void *in_p, void *out_p, int *len,
+                         MPI_Datatype *type) {
+
+  /* change pointer type */
+  struct task_dependency *in = (struct task_dependency *)in_p;
+  struct task_dependency *out = (struct task_dependency *)out_p;
+
+  /* Loop over all the current objects */
+  for (int i = 0; i < *len; i++) {
+
+    /* loop over all the object set in invals */
+    for (int j = 0; j < MAX_NUMBER_DEP; j++) {
+
+      /* Have we reached the end of the links? */
+      if (in[i].number_link[j] == -1) {
+        break;
+      }
+
+      /* get a few variables */
+      int tb_type = in[i].type_out[j];
+      int tb_subtype = in[i].subtype_out[j];
+
+#ifdef SWIFT_DEBUG_CHECKS
+      /* Check tasks */
+      if (tb_type >= task_type_count) {
+        error("Unknown task type %i", tb_type);
+      }
+
+      if (tb_subtype >= task_subtype_count) {
+        error("Unknown subtask type %i", tb_subtype);
+      }
+#endif
+
+      /* find the corresponding id */
+      int k = 0;
+      while (k < MAX_NUMBER_DEP) {
+        /* have we reached the end of the links? */
+        if (out[i].number_link[k] == -1) {
+          /* reset the counter in order to be safe */
+          out[i].number_link[k] = 0;
+          out[i].number_rank[k] = 0;
+
+          /* set the relation */
+          out[i].type_in = in[i].type_in;
+          out[i].subtype_in = in[i].subtype_in;
+          out[i].implicit_in = in[i].implicit_in;
+
+          out[i].type_out[k] = in[i].type_out[j];
+          out[i].subtype_out[k] = in[i].subtype_out[j];
+          out[i].implicit_out[k] = in[i].implicit_out[j];
+          break;
+        }
+
+        /* do we have the same relation? */
+        if (out[i].type_out[k] == tb_type &&
+            out[i].subtype_out[k] == tb_subtype) {
+          break;
+        }
+
+        k++;
+      }
+
+      /* Check if we are still in the memory */
+      if (k == MAX_NUMBER_DEP) {
+        error("Not enough memory, please increase MAX_NUMBER_DEP");
+      }
+
+#ifdef SWIFT_DEBUG_CHECKS
+      /* Check if correct relation */
+      if (out[i].type_in != in[i].type_in ||
+          out[i].subtype_in != in[i].subtype_in ||
+          out[i].implicit_in != in[i].implicit_in ||
+          out[i].type_out[k] != in[i].type_out[j] ||
+          out[i].subtype_out[k] != in[i].subtype_out[j] ||
+          out[i].implicit_out[k] != in[i].implicit_out[j]) {
+        error("Tasks do not correspond");
+      }
+#endif
+
+      /* sum the contributions */
+      out[i].number_link[k] += in[i].number_link[j];
+      out[i].number_rank[k] += in[i].number_rank[j];
+    }
+  }
+
+  return;
 }
 
+#endif  // WITH_MPI
+
 /**
  * @brief Write a dot file with the task dependencies.
  *
@@ -152,253 +339,178 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) {
 
   const ticks tic = getticks();
 
-  /* Conservative number of dependencies per task type */
-  const int max_nber_dep = 128;
-
   /* Number of possible relations between tasks */
-  const int nber_relation =
-      2 * task_type_count * task_subtype_count * max_nber_dep;
+  const int nber_tasks = task_type_count * task_subtype_count;
 
-  /* To get the table of max_nber_dep for a task:
-   * ind = (ta * task_subtype_count + sa) * max_nber_dep * 2
+  /* To get the table for a task:
+   * ind = (ta * task_subtype_count + sa)
    * where ta is the value of task_type and sa is the value of
    * task_subtype  */
-  int *table = (int *)malloc(nber_relation * sizeof(int));
-  if (table == NULL)
-    error("Error allocating memory for task-dependency graph (table).");
-
-  int *count_rel = (int *)malloc(nber_relation * sizeof(int) / 2);
-  if (count_rel == NULL)
-    error("Error allocating memory for task-dependency graph (count_rel).");
-
-  /* Reset everything */
-  for (int i = 0; i < nber_relation; i++) table[i] = -1;
-  for (int i = 0; i < nber_relation / 2; i++) count_rel[i] = 0;
+  struct task_dependency *task_dep = (struct task_dependency *)malloc(
+      nber_tasks * sizeof(struct task_dependency));
 
-  /* Create file */
-  char filename[200] = "dependency_graph.dot";
-  FILE *f = fopen(filename, "w");
-  if (f == NULL) error("Error opening dependency graph file.");
+  if (task_dep == NULL)
+    error("Error allocating memory for task-dependency graph (table).");
 
-  /* Write header */
-  fprintf(f, "digraph task_dep {\n");
-  fprintf(f, "label=\"Task dependencies for SWIFT %s\";\n", git_revision());
-  fprintf(f, "\t compound=true;\n");
-  fprintf(f, "\t ratio=0.66;\n");
-  fprintf(f, "\t node[nodesep=0.15];\n");
+  /* Reset counter */
+  for (int i = 0; i < nber_tasks; i++) {
+    for (int j = 0; j < MAX_NUMBER_DEP; j++) {
+      /* Use number_link as indicator of the existance of a relation */
+      task_dep[i].number_link[j] = -1;
+    }
+  }
 
   /* loop over all tasks */
   for (int i = 0; i < s->nr_tasks; i++) {
     const struct task *ta = &s->tasks[i];
 
+    /* Current index */
+    const int ind = ta->type * task_subtype_count + ta->subtype;
+
+    struct task_dependency *cur = &task_dep[ind];
+
+    /* Set ta */
+    cur->type_in = ta->type;
+    cur->subtype_in = ta->subtype;
+    cur->implicit_in = ta->implicit;
+
     /* and their dependencies */
     for (int j = 0; j < ta->nr_unlock_tasks; j++) {
       const struct task *tb = ta->unlock_tasks[j];
 
-      /* check if dependency already written */
-      int written = 0;
-
-      /* Current index */
-      int ind = ta->type * task_subtype_count + ta->subtype;
-      ind *= 2 * max_nber_dep;
-
       int k = 0;
-      int *cur = &table[ind];
-      while (k < max_nber_dep) {
+      while (k < MAX_NUMBER_DEP) {
 
         /* not written yet */
-        if (cur[0] == -1) {
-          cur[0] = tb->type;
-          cur[1] = tb->subtype;
+        if (cur->number_link[k] == -1) {
+          /* set tb */
+          cur->type_out[k] = tb->type;
+          cur->subtype_out[k] = tb->subtype;
+          cur->implicit_out[k] = tb->implicit;
+
+          /* statistics */
+          const int count = scheduler_get_number_relation(s, ta, tb);
+          cur->number_link[k] = count;
+          cur->number_rank[k] = 1;
+
           break;
         }
 
         /* already written */
-        if (cur[0] == tb->type && cur[1] == tb->subtype) {
-          written = 1;
+        if (cur->type_out[k] == tb->type &&
+            cur->subtype_out[k] == tb->subtype) {
           break;
         }
 
         k += 1;
-        cur = &cur[2];
       }
 
-      /* max_nber_dep is too small */
-      if (k == max_nber_dep)
-        error("Not enough memory, please increase max_nber_dep");
+      /* MAX_NUMBER_DEP is too small */
+      if (k == MAX_NUMBER_DEP)
+        error("Not enough memory, please increase MAX_NUMBER_DEP");
+    }
+  }
 
-      /* Increase counter of relation */
-      count_rel[ind / 2 + k] += 1;
+#ifdef WITH_MPI
+  /* create MPI operator */
+  MPI_Datatype data_type;
+  task_dependency_define(&data_type);
 
-      /* Not written yet => write it */
-      if (!written) {
+  MPI_Op sum;
+  MPI_Op_create(task_dependency_sum, /* commute */ 1, &sum);
 
-        /* text to write */
-        char ta_name[200];
-        char tb_name[200];
+  /* create recv buffer */
+  struct task_dependency *recv = NULL;
 
-        /* construct line */
-        scheduler_task_dependency_name(ta->type, ta->subtype, ta_name);
-        scheduler_task_dependency_name(tb->type, tb->subtype, tb_name);
-
-        /* Change colour of implicit tasks */
-        if (ta->implicit)
-          fprintf(f, "\t %s [style = filled];\n\t %s [color = lightgrey];\n",
-                  ta_name, ta_name);
-        if (tb->implicit)
-          fprintf(f, "\t %s [style = filled];\n\t %s [color = lightgrey];\n",
-                  tb_name, tb_name);
-
-        /* Change shape of MPI communications */
-        if (ta->type == task_type_send || ta->type == task_type_recv)
-          fprintf(f, "\t \"%s %s\" [shape = diamond];\n",
-                  taskID_names[ta->type], subtaskID_names[ta->subtype]);
-        if (tb->type == task_type_send || tb->type == task_type_recv)
-          fprintf(f, "\t \"%s %s\" [shape = diamond];\n",
-                  taskID_names[tb->type], subtaskID_names[tb->subtype]);
+  if (s->nodeID == 0) {
+    recv = (struct task_dependency *)malloc(nber_tasks *
+                                            sizeof(struct task_dependency));
+
+    /* reset counter */
+    for (int i = 0; i < nber_tasks; i++) {
+      for (int j = 0; j < MAX_NUMBER_DEP; j++) {
+        /* Use number_link as indicator of the existance of a relation */
+        recv[i].number_link[j] = -1;
       }
     }
   }
 
-  int density_cluster[4] = {0};
-  int gradient_cluster[4] = {0};
-  int force_cluster[4] = {0};
-  int limiter_cluster[4] = {0};
-  int gravity_cluster[5] = {0};
-  int stars_density_cluster[4] = {0};
-
-  /* Check whether we need to construct a group of tasks */
-  for (int type = 0; type < task_type_count; ++type) {
-
-    for (int subtype = 0; subtype < task_subtype_count; ++subtype) {
-
-      const int ind = 2 * (type * task_subtype_count + subtype) * max_nber_dep;
-
-      /* Does this task/sub-task exist? */
-      if (table[ind] != -1) {
-
-        for (int k = 0; k < 4; ++k) {
-          if (type == task_type_self + k && subtype == task_subtype_density)
-            density_cluster[k] = 1;
-          if (type == task_type_self + k && subtype == task_subtype_gradient)
-            gradient_cluster[k] = 1;
-          if (type == task_type_self + k && subtype == task_subtype_force)
-            force_cluster[k] = 1;
-          if (type == task_type_self + k && subtype == task_subtype_limiter)
-            limiter_cluster[k] = 1;
-          if (type == task_type_self + k && subtype == task_subtype_grav)
-            gravity_cluster[k] = 1;
-          if (type == task_type_self + k &&
-              subtype == task_subtype_stars_density)
-            stars_density_cluster[k] = 1;
-        }
-        if (type == task_type_grav_mesh) gravity_cluster[2] = 1;
-        if (type == task_type_grav_long_range) gravity_cluster[3] = 1;
-        if (type == task_type_grav_mm) gravity_cluster[4] = 1;
-      }
-    }
+  /* Do the reduction */
+  int test =
+      MPI_Reduce(task_dep, recv, nber_tasks, data_type, sum, 0, MPI_COMM_WORLD);
+  if (test != MPI_SUCCESS) error("MPI reduce failed");
+
+  /* free some memory */
+  if (s->nodeID == 0) {
+    free(task_dep);
+    task_dep = recv;
   }
+#endif
 
-  /* Make a cluster for the density tasks */
-  fprintf(f, "\t subgraph cluster0{\n");
-  fprintf(f, "\t\t label=\"\";\n");
-  for (int k = 0; k < 4; ++k)
-    if (density_cluster[k])
-      fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
-              subtaskID_names[task_subtype_density]);
-  fprintf(f, "\t};\n");
-
-  /* Make a cluster for the force tasks */
-  fprintf(f, "\t subgraph cluster1{\n");
-  fprintf(f, "\t\t label=\"\";\n");
-  for (int k = 0; k < 4; ++k)
-    if (force_cluster[k])
-      fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
-              subtaskID_names[task_subtype_force]);
-  fprintf(f, "\t};\n");
-
-  /* Make a cluster for the gradient tasks */
-  fprintf(f, "\t subgraph cluster2{\n");
-  fprintf(f, "\t\t label=\"\";\n");
-  for (int k = 0; k < 4; ++k)
-    if (gradient_cluster[k])
-      fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
-              subtaskID_names[task_subtype_gradient]);
-  fprintf(f, "\t};\n");
-
-  /* Make a cluster for the limiter tasks */
-  fprintf(f, "\t subgraph cluster2{\n");
-  fprintf(f, "\t\t label=\"\";\n");
-  for (int k = 0; k < 4; ++k)
-    if (limiter_cluster[k])
-      fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
-              subtaskID_names[task_subtype_limiter]);
-  fprintf(f, "\t};\n");
-
-  /* Make a cluster for the gravity tasks */
-  fprintf(f, "\t subgraph cluster4{\n");
-  fprintf(f, "\t\t label=\"\";\n");
-  for (int k = 0; k < 2; ++k)
-    if (gravity_cluster[k])
-      fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
-              subtaskID_names[task_subtype_grav]);
-  if (gravity_cluster[2])
-    fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_mesh]);
-  if (gravity_cluster[3])
-    fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_long_range]);
-  if (gravity_cluster[4])
-    fprintf(f, "\t\t %s;\n", taskID_names[task_type_grav_mm]);
-  fprintf(f, "\t};\n");
-
-  /* Make a cluster for the density tasks */
-  fprintf(f, "\t subgraph cluster4{\n");
-  fprintf(f, "\t\t label=\"\";\n");
-  for (int k = 0; k < 4; ++k)
-    if (stars_density_cluster[k])
-      fprintf(f, "\t\t \"%s %s\";\n", taskID_names[task_type_self + k],
-              subtaskID_names[task_subtype_stars_density]);
-  fprintf(f, "\t};\n");
-
-  /* Write down the number of relation */
-  for (int ta_type = 0; ta_type < task_type_count; ta_type++) {
-
-    for (int ta_subtype = 0; ta_subtype < task_subtype_count; ta_subtype++) {
-
-      /* Get task indice */
-      const int ind =
-          (ta_type * task_subtype_count + ta_subtype) * max_nber_dep;
-
-      /* Loop over dependencies */
-      for (int k = 0; k < max_nber_dep; k++) {
-
-        if (count_rel[ind + k] == 0) continue;
-
-        /* Get task type */
-        const int i = 2 * (ind + k);
-        int tb_type = table[i];
-        int tb_subtype = table[i + 1];
-
-        /* Get names */
+  if (s->nodeID == 0) {
+    /* Create file */
+    char *filename = "dependency_graph.csv";
+    FILE *f = fopen(filename, "w");
+    if (f == NULL) error("Error opening dependency graph file.");
+
+    /* Write header */
+    fprintf(f, "# %s\n", git_revision());
+    fprintf(
+        f,
+        "task_in,task_out,implicit_in,implicit_out,mpi_in,mpi_out,cluster_in,"
+        "cluster_out,number_link,number_rank\n");
+
+    for (int i = 0; i < nber_tasks; i++) {
+      for (int j = 0; j < MAX_NUMBER_DEP; j++) {
+        /* Does this link exists */
+        if (task_dep[i].number_link[j] == -1) {
+          continue;
+        }
+
+        /* Define a few variables */
+        const int ta_type = task_dep[i].type_in;
+        const int ta_subtype = task_dep[i].subtype_in;
+        const int ta_implicit = task_dep[i].implicit_in;
+
+        const int tb_type = task_dep[i].type_out[j];
+        const int tb_subtype = task_dep[i].subtype_out[j];
+        const int tb_implicit = task_dep[i].implicit_out[j];
+
+        const int count = task_dep[i].number_link[j];
+        const int number_rank = task_dep[i].number_rank[j];
+
+        /* text to write */
         char ta_name[200];
         char tb_name[200];
 
-        scheduler_task_dependency_name(ta_type, ta_subtype, ta_name);
-        scheduler_task_dependency_name(tb_type, tb_subtype, tb_name);
+        /* construct line */
+        task_get_full_name(ta_type, ta_subtype, ta_name);
+        task_get_full_name(tb_type, tb_subtype, tb_name);
+
+        /* Check if MPI */
+        int ta_mpi = 0;
+        if (ta_type == task_type_send || ta_type == task_type_recv) ta_mpi = 1;
+
+        int tb_mpi = 0;
+        if (tb_type == task_type_send || tb_type == task_type_recv) tb_mpi = 1;
 
-        /* Write to the fle */
-        fprintf(f, "\t %s->%s[label=%i];\n", ta_name, tb_name,
-                count_rel[ind + k]);
+        /* Get group name */
+        char ta_cluster[20];
+        char tb_cluster[20];
+        task_get_group_name(ta_type, ta_subtype, ta_cluster);
+        task_get_group_name(tb_type, tb_subtype, tb_cluster);
+
+        fprintf(f, "%s,%s,%d,%d,%d,%d,%s,%s,%d,%d\n", ta_name, tb_name,
+                ta_implicit, tb_implicit, ta_mpi, tb_mpi, ta_cluster,
+                tb_cluster, count, number_rank);
       }
     }
+    /* Close the file */
+    fclose(f);
   }
 
-  /* Close the file */
-  fprintf(f, "}");
-  fclose(f);
-
   /* Be clean */
-  free(table);
-  free(count_rel);
+  free(task_dep);
 
   if (verbose)
     message("Printing task graph took %.3f %s.",
@@ -1960,9 +2072,6 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
       case task_type_timestep:
         cost = wscale * count_i + wscale * gcount_i;
         break;
-      case task_type_timestep_limiter:
-        cost = wscale * count_i;
-        break;
       case task_type_send:
 #if defined(WITH_MPI) && (defined(HAVE_PARMETIS) || defined(HAVE_METIS))
         partcost = 0;
@@ -2172,8 +2281,7 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
                           subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_xv ||
                    t->subtype == task_subtype_rho ||
-                   t->subtype == task_subtype_gradient ||
-                   t->subtype == task_subtype_limiter) {
+                   t->subtype == task_subtype_gradient) {
           err = MPI_Irecv(t->ci->hydro.parts, t->ci->hydro.count, part_mpi_type,
                           t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
                           &t->req);
@@ -2224,8 +2332,7 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
                              subtaskMPI_comms[t->subtype], &t->req);
         } else if (t->subtype == task_subtype_xv ||
                    t->subtype == task_subtype_rho ||
-                   t->subtype == task_subtype_gradient ||
-                   t->subtype == task_subtype_limiter) {
+                   t->subtype == task_subtype_gradient) {
           if ((t->ci->hydro.count * sizeof(struct part)) > s->mpi_message_limit)
             err = MPI_Isend(t->ci->hydro.parts, t->ci->hydro.count,
                             part_mpi_type, t->cj->nodeID, t->flags,
diff --git a/src/task.c b/src/task.c
index f27b5fd413c1c216afb7d777244b0cee88159e6e..4aed4db3abf183143f3836651b6a37bc6b8e2fdc 100644
--- a/src/task.c
+++ b/src/task.c
@@ -628,6 +628,71 @@ void task_print(const struct task *t) {
           t->nr_unlock_tasks, t->skip);
 }
 
+/**
+ * @brief Get the group name of a task.
+ *
+ * This is used to group tasks with similar actions in the task dependency
+ * graph.
+ *
+ * @param type The #task type.
+ * @param subtype The #subtask type.
+ * @param cluster (return) The group name (should be allocated)
+ */
+void task_get_group_name(int type, int subtype, char *cluster) {
+
+  if (type == task_type_grav_long_range || type == task_type_grav_mm ||
+      type == task_type_grav_mesh) {
+
+    strcpy(cluster, "Gravity");
+    return;
+  }
+
+  switch (subtype) {
+    case task_subtype_density:
+      strcpy(cluster, "Density");
+      break;
+    case task_subtype_gradient:
+      strcpy(cluster, "Gradient");
+      break;
+    case task_subtype_force:
+      strcpy(cluster, "Force");
+      break;
+    case task_subtype_grav:
+      strcpy(cluster, "Gravity");
+      break;
+    case task_subtype_stars_density:
+      strcpy(cluster, "Stars");
+      break;
+    default:
+      strcpy(cluster, "None");
+      break;
+  }
+}
+
+/**
+ * @brief Generate the full name of a #task.
+ *
+ * @param type The #task type.
+ * @param subtype The #task type.
+ * @param name (return) The formatted string
+ */
+void task_get_full_name(int type, int subtype, char *name) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check input */
+  if (type >= task_type_count) error("Unknown task type %i", type);
+
+  if (subtype >= task_subtype_count)
+    error("Unknown task subtype %i with type %s", subtype, taskID_names[type]);
+#endif
+
+  /* Full task name */
+  if (subtype == task_subtype_none)
+    sprintf(name, "%s", taskID_names[type]);
+  else
+    sprintf(name, "%s_%s", taskID_names[type], subtaskID_names[subtype]);
+}
+
 #ifdef WITH_MPI
 /**
  * @brief Create global communicators for each of the subtasks.
diff --git a/src/task.h b/src/task.h
index 83c71a73e23da8d25c99bf038045cde12aa64c89..100ac225bd5956e8d59d6a197c1257cb3e796ebb 100644
--- a/src/task.h
+++ b/src/task.h
@@ -205,6 +205,9 @@ float task_overlap(const struct task *ta, const struct task *tb);
 int task_lock(struct task *t);
 void task_do_rewait(struct task *t);
 void task_print(const struct task *t);
+void task_get_full_name(int type, int subtype, char *name);
+void task_get_group_name(int type, int subtype, char *cluster);
+
 #ifdef WITH_MPI
 void task_create_mpi_comms(void);
 #endif
diff --git a/tools/plot_task_dependencies.py b/tools/plot_task_dependencies.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd08f34487daee222786b7c4806200b670b31a7d
--- /dev/null
+++ b/tools/plot_task_dependencies.py
@@ -0,0 +1,359 @@
+#!/usr/bin/env python3
+"""
+This file generates a graphviz file that represents the SWIFT tasks
+ dependencies.
+
+Example: ./plot_task_dependencies.py dependency_graph_*.csv
+"""
+import sys
+from pandas import read_csv
+import numpy as np
+from subprocess import call
+
+
+def getGitVersion(f, git):
+    """
+    Read the git version from the file
+
+    Parameters
+    ----------
+
+    f: str
+        Filename
+
+    git: str
+        Git version of previous file
+
+    Returns
+    -------
+
+    new_git: str
+        Git version of current file
+    """
+    # read comment in csv file
+    with open(f, "r") as f:
+        line = f.readline()
+
+    # check if really a comment
+    if line[0] != "#":
+        return None
+
+    # remove trailing characters
+    new_git = line[2:].rstrip()
+
+    # check if previous and current are the same
+    if git is not None and git != new_git:
+        raise Exception("Files were not produced by the same version")
+
+    return new_git
+
+
+def appendSingleData(data0, datai):
+    """
+    Append two DataFrame together
+
+    Parameters
+    ----------
+
+    data0: DataFrame
+        One of the dataframe
+
+    datai: DataFrame
+        The second dataframe
+
+    Returns
+    -------
+
+    data0: DataFrame
+        The updated dataframe
+    """
+
+    # loop over all rows in datai
+    for i, row in datai.iterrows():
+        # get data
+        ta = datai["task_in"][i]
+        tb = datai["task_out"][i]
+        ind = np.logical_and(data0["task_in"] == ta,
+                             data0["task_out"] == tb)
+
+        # check number of ta->tb
+        N = np.sum(ind)
+        if N > 1:
+            raise Exception("Same dependency written multiple times %s->%s" %
+                            (ta, tb))
+        # if not present in data0
+        if N == 0:
+            data0.append(row)
+        else:
+            # otherwise just update the number of link
+            ind = ind[ind].index[0]
+            tmp = data0["number_link"][ind] + datai["number_link"][i]
+            data0.at[ind, "number_link"] = tmp
+
+    return data0
+
+
+def appendData(data):
+    """
+    Append all the dataframe together
+
+    Parameters
+    ----------
+
+    data: list
+        List containing all the dataframe to append together
+
+    Returns
+    -------
+
+    data: DataFrame
+        The complete dataframe
+    """
+    N = len(data)
+    if N == 1:
+        return data[0]
+
+    # add number link to data[0]
+    for i in range(N-1):
+        i += 1
+        data[0] = appendSingleData(data[0], data[i])
+
+    return data[0]
+
+
+def writeTask(f, name, implicit, mpi):
+    """
+    Write the special task (e.g. implicit and mpi)
+
+    Parameters
+    ----------
+
+    f: File
+        File where to write the data
+
+    name: str
+        Task name
+
+    implicit: int
+        Is the task implicit
+
+    mpi: int
+        Is the task MPI related
+    """
+    # do we need to do something?
+    if not implicit and not mpi:
+        return
+
+    # generate text
+    txt = "\t " + name + "["
+    if implicit:
+        txt += "style=filled, color=lightgrey"
+        if mpi:
+            txt += ","
+    if mpi:
+        txt += "shape=diamond"
+    txt += "];\n"
+
+    # write it
+    f.write(txt)
+
+
+def writeHeader(f, data, git):
+    """
+    Write the header and the special tasks
+
+    Parameters
+    ----------
+
+    f: File
+        File where to write the data
+
+    data: DataFrame
+        The dataframe to write
+
+    git: str
+        The git version
+    """
+    # write header
+    f.write("digraph task_dep {\n")
+    f.write("\t # Header\n")
+    f.write('\t label="Task dependencies for SWIFT %s";\n' % git)
+    f.write("\t compound=true;\n")
+    f.write("\t ratio=0.66;\n")
+    f.write("\t node[nodesep=0.15];\n")
+
+    f.write("\n")
+
+    # write the special task
+    f.write("\t # Special tasks\n")
+    N = len(data)
+    written = []
+    # do task in
+    for i in range(N):
+        ta = data["task_in"][i]
+        if ta in written:
+            continue
+
+        written.append(ta)
+        writeTask(f, ta, data["implicit_in"][i], data["mpi_in"][i])
+
+    # do task out
+    for i in range(N):
+        tb = data["task_out"][i]
+        if tb in written:
+            continue
+
+        written.append(tb)
+        writeTask(f, tb, data["implicit_out"][i], data["mpi_out"][i])
+
+    f.write("\n")
+
+
+def writeCluster(f, tasks, cluster):
+    """
+    Write a single cluster
+
+    Parameters
+    ----------
+
+    f: File
+        File where to write the data
+
+    tasks: list
+        List of all tasks in the cluster
+
+    cluster: str
+        Cluster name
+    """
+    f.write("\t subgraph cluster%s {\n" % cluster)
+    f.write('\t\t label="";\n')
+    for t in tasks:
+        f.write("\t\t %s;\n" % t)
+    f.write("\t };\n\n")
+
+
+def writeClusters(f, data):
+    """
+    Write all the clusters
+
+    Parameters
+    ----------
+
+    f: File
+        File where to write the data
+
+    data: DataFrame
+        The dataframe to write
+    """
+    f.write("\t # Clusters\n")
+    # get list of all the clusters
+    clusters = data[["cluster_in", "cluster_out"]]
+    clusters = np.unique(clusters)
+
+    cluster_in = data["cluster_in"]
+    cluster_out = data["cluster_out"]
+    # loop over all clusters
+    for cluster in clusters:
+        # is it a cluster?
+        if cluster == "None":
+            continue
+
+        # get all the task in current cluster
+        ta = data["task_in"][cluster_in == cluster]
+        tb = data["task_out"][cluster_out == cluster]
+
+        # make them unique
+        tasks = np.append(ta, tb)
+        tasks = np.unique(tasks)
+
+        # write current cluster
+        writeCluster(f, tasks, cluster)
+
+    f.write("\n")
+
+
+def writeDependencies(f, data):
+    """
+    Write all the dependencies between tasks
+
+    Parameters
+    ----------
+
+    f: File
+        File where to write the data
+
+    data: DataFrame
+        The dataframe to write
+
+    """
+    f.write("\t # Dependencies\n")
+    N = len(data)
+    written = []
+    max_rank = data["number_rank"].max()
+    for i in range(N):
+        # get data
+        ta = data["task_in"][i]
+        tb = data["task_out"][i]
+        number_link = data["number_link"][i]
+
+        # check if already done
+        name = "%s_%s" % (ta, tb)
+        if name in written:
+            raise Exception("Found two same task dependencies")
+
+        written.append(name)
+
+        # write relation
+        arrow = ""
+        if data["number_rank"][i] != max_rank:
+            arrow = ",style=dashed"
+        f.write("\t %s->%s[label=%i%s]\n" %
+                (ta, tb, number_link, arrow))
+
+
+def writeFooter(f):
+    """
+    Write the footer
+
+    Parameters
+    ----------
+
+    f: File
+        File where to write the data
+    """
+    f.write("}")
+
+
+if __name__ == "__main__":
+    # get input
+    filenames = sys.argv[1:]
+    if len(filenames) < 1:
+        raise Exception("You should provide at least a file name")
+
+    # output
+    dot_output = "dependency_graph.dot"
+    png_output = "dependency_graph.png"
+
+    # read files
+    data = []
+    git = None
+    for f in filenames:
+        tmp = read_csv(f, delimiter=",", comment="#")
+        git = getGitVersion(f, git)
+        data.append(tmp)
+
+    data = appendData(data)
+
+    # write output
+    with open(dot_output, "w") as f:
+        writeHeader(f, data, git)
+
+        writeClusters(f, data)
+
+        writeDependencies(f, data)
+
+        writeFooter(f)
+
+    call(["dot", "-Tpng", dot_output, "-o", png_output])
+
+    print("You will find the graph in %s" % png_output)
diff --git a/tools/plot_task_dependencies.sh b/tools/plot_task_dependencies.sh
deleted file mode 100755
index 77784d8a9cdd3720621c9ad35c4cfbdaf0167ff1..0000000000000000000000000000000000000000
--- a/tools/plot_task_dependencies.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-#  Creates a graphic from the task graph file dependency_graph.dot.
-#  Requires the graphviz command "dot".
-
-if [ ! -e dependency_graph.dot ]; then
-    echo "Missing task-graph output 'dependency_graph.dot'! Cannot generate figure."
-else 
-    dot -Tpng dependency_graph.dot -o task_graph.png
-    echo "Output written to task_graph.png"
-fi
-
-exit