[SCM] Gerris Flow Solver branch, upstream, updated. b3aa46814a06c9cb2912790b23916ffb44f1f203

Stephane Popinet s.popinet at niwa.co.nz
Fri May 15 02:52:06 UTC 2009


The following commit has been merged in the upstream branch:
commit 3b112634a6f857780087c9d7bd45c6643b5e8aa9
Author: Stephane Popinet <s.popinet at niwa.co.nz>
Date:   Tue Jun 28 09:55:10 2005 +1000

    Restructured GfsVariable implementation
    
    All the variables are now dynamically allocated. Temporary variables are
    allocated when needed. There is no limit (other than memory) on the maximum
    number of variables per cell.
    
    This also uncovered a serious bug: when using diffusive tracers, the solution of
    the diffusion equation would overwrite the stored values of the pressure
    gradients used to correct the advective terms for the velocity. With the new
    temporary variable allocation scheme messes like that should be avoided.
    
    darcs-hash:20050627235510-fbd8f-873cb9b443e8d2f93c7b0d3878f241f65e1d9a2d.gz

diff --git a/modules/Makefile.am b/modules/Makefile.am
index 92b58c8..004b5e9 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -3,20 +3,6 @@
 INCLUDES = -I$(top_srcdir)/src -I$(includedir) \
            -DG_LOG_DOMAIN=\"Gfs-modules\" $(GTS_CFLAGS)
 
-pkglib_LTLIBRARIES = \
-	libvorticity_spectrum2D.la
-
-AM_CFLAGS = -DFTT_2D=1
-AM_LDFLAGS = $(NO_UNDEFINED) -version-info 0:0:0 -export-dynamic
-
-EXTRA_DIST = \
-	vorticity_spectrum.mod
-
-BUILT_SOURCES = \
-	vorticity_spectrum.c
-
-libvorticity_spectrum2D_la_SOURCES = vorticity_spectrum.c
-
 if HAVE_MODULES
 %.c : %.mod
 	@echo "/* $@" > $@
diff --git a/modules/vorticity_spectrum.mod b/modules/vorticity_spectrum.mod
deleted file mode 100644
index 25cfacd..0000000
--- a/modules/vorticity_spectrum.mod
+++ /dev/null
@@ -1,220 +0,0 @@
-/* Gerris - The GNU Flow Solver                           (-*-C-*-)
- * Copyright (C) 2001 National Institute of Water and Atmospheric Research
- *
- * This program 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.
- *
- * This program 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  
- */
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "event.h"
-#include "domain.h"
-
-typedef struct _Spectrum Spectrum;
-
-struct _Spectrum {
-  gdouble ** r, ** i;
-  guint n;
-};
-
-static Spectrum * spectrum_new (guint n)
-{
-  Spectrum * s = g_malloc (sizeof (Spectrum));
-  guint i;
-
-  s->r = g_malloc (sizeof (gdouble *)*n);
-  s->i = g_malloc (sizeof (gdouble *)*n);
-  for (i = 0; i < n; i++) {
-    guint j;
-
-    s->r[i] = g_malloc (sizeof (gdouble)*n);
-    s->i[i] = g_malloc (sizeof (gdouble)*n);
-    for (j = 0; j < n; j++) {
-      gdouble k = sqrt ((i + 1)*(i + 1) + (j + 1)*(j + 1));
-
-      s->r[i][j] = 2.*(rand()/(gdouble) RAND_MAX - 0.5)
-	/(k*(1 + exp (4.*log (k/6.))));
-      s->i[i][j] = 2.*(rand()/(gdouble) RAND_MAX - 0.5)
-	/(k*(1 + exp (4.*log (k/6.))));
-    }
-  }
-  s->n = n;
-    
-  return  s;
-}
-
-static void spectrum_destroy (Spectrum * s)
-{
-  guint i;
-
-  g_return_if_fail (s != NULL);
-
-  for (i = 0; i < s->n; i++) {
-    g_free (s->r[i]);
-    g_free (s->i[i]);
-  }
-  g_free (s->r);
-  g_free (s->i);
-  g_free (s);
-}
-
-static gdouble spectrum_value (Spectrum * s,
-			       FttVector * pos)
-{
-  guint k1, k2;
-  gdouble val = 0.;
-
-  g_return_val_if_fail (s != NULL, 0.);
-  g_return_val_if_fail (pos != NULL, 0.);
-
-  for (k1 = 0; k1 < s->n; k1++)
-    for (k2 = 0; k2 < s->n; k2++)
-      val += s->r[k1][k2]*cos (2.*M_PI*((k1 + 1)*pos->x + (k2 + 1)*pos->y)) -
-	     s->i[k1][k2]*sin (2.*M_PI*((k1 + 1)*pos->x + (k2 + 1)*pos->y));
-  return val;
-}
-
-static void init_spectrum_streamfunction (FttCell * cell,
-					  Spectrum * s)
-{
-  FttVector pos;
-
-  ftt_cell_pos (cell, &pos);
-  GFS_STATE (cell)->div = spectrum_value (s, &pos);
-}
-
-static void init_velocity_from_streamfunction (FttCell * cell,
-					       GfsVariable * stream)
-{
-  gdouble size = ftt_cell_size (cell);
-
-  GFS_STATE (cell)->u = - gfs_center_gradient (cell, FTT_Y, stream->i)/size;
-  GFS_STATE (cell)->v =   gfs_center_gradient (cell, FTT_X, stream->i)/size;
-}
-
-/* GfsInitVorticitySpectrum: Header */
-
-GfsEventClass * gfs_init_vorticity_spectrum_class  (void);
-
-/* GfsInitVorticitySpectrum: Object */
-
-static gboolean gfs_init_vorticity_spectrum_event (GfsEvent * event, 
-						   GfsSimulation * sim)
-{
-  if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (gfs_init_vorticity_spectrum_class ())->parent_class)->event) (event, sim)) {
-    Spectrum * s = spectrum_new (128);
-
-    gfs_domain_cell_traverse (GFS_DOMAIN (sim), 
-			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-        (FttCellTraverseFunc) init_spectrum_streamfunction, s);
-    spectrum_destroy (s);
-    gfs_domain_cell_traverse (GFS_DOMAIN (sim), 
-			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-        (FttCellTraverseFunc) init_velocity_from_streamfunction, gfs_div);    
-    return TRUE;
-  }
-  return FALSE;
-}
-
-static void gfs_init_vorticity_spectrum_class_init (GfsEventClass * klass)
-{
-  GFS_EVENT_CLASS (klass)->event = gfs_init_vorticity_spectrum_event;
-}
-
-GfsEventClass * gfs_init_vorticity_spectrum_class (void)
-{
-  static GfsEventClass * klass = NULL;
-
-  if (klass == NULL) {
-    GtsObjectClassInfo gfs_init_vorticity_spectrum_info = {
-      "GfsInitVorticitySpectrum",
-      sizeof (GfsEvent),
-      sizeof (GfsEventClass),
-      (GtsObjectClassInitFunc) gfs_init_vorticity_spectrum_class_init,
-      (GtsObjectInitFunc) NULL,
-      (GtsArgSetFunc) NULL,
-      (GtsArgGetFunc) NULL
-    };
-    klass = gts_object_class_new (GTS_OBJECT_CLASS (gfs_generic_init_class ()),
-				  &gfs_init_vorticity_spectrum_info);
-  }
-
-  return klass;
-}
-
-/* GfsInitVorticityRandom: Header */
-
-GfsEventClass * gfs_init_vorticity_random_class  (void);
-
-/* GfsInitVorticityRandom: Object */
-
-static void init_random_streamfunction (FttCell * cell)
-{
-  GFS_STATE (cell)->div = rand()/(gdouble) RAND_MAX;
-}
-
-static gboolean gfs_init_vorticity_random_event (GfsEvent * event, 
-						 GfsSimulation * sim)
-{
-  if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (gfs_init_vorticity_random_class ())->parent_class)->event) (event, sim)) {
-    srand (GFS_DOMAIN (sim)->pid);
-    gfs_domain_cell_traverse (GFS_DOMAIN (sim), 
-			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-        (FttCellTraverseFunc) init_random_streamfunction, NULL);
-    gfs_domain_cell_traverse (GFS_DOMAIN (sim), 
-			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-        (FttCellTraverseFunc) init_velocity_from_streamfunction, gfs_div);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-static void gfs_init_vorticity_random_class_init (GfsEventClass * klass)
-{
-  GFS_EVENT_CLASS (klass)->event = gfs_init_vorticity_random_event;
-}
-
-GfsEventClass * gfs_init_vorticity_random_class (void)
-{
-  static GfsEventClass * klass = NULL;
-
-  if (klass == NULL) {
-    GtsObjectClassInfo gfs_init_vorticity_random_info = {
-      "GfsInitVorticityRandom",
-      sizeof (GfsEvent),
-      sizeof (GfsEventClass),
-      (GtsObjectClassInitFunc) gfs_init_vorticity_random_class_init,
-      (GtsObjectInitFunc) NULL,
-      (GtsArgSetFunc) NULL,
-      (GtsArgGetFunc) NULL
-    };
-    klass = gts_object_class_new (GTS_OBJECT_CLASS (gfs_generic_init_class ()),
-				  &gfs_init_vorticity_random_info);
-  }
-
-  return klass;
-}
-
-/* Initialize module */
-
-const gchar * g_module_check_init (void);
-
-const gchar * g_module_check_init (void)
-{
-  gfs_init_vorticity_spectrum_class ();
-  gfs_init_vorticity_random_class ();
-  return NULL;
-}
diff --git a/src/adaptive.c b/src/adaptive.c
index 76d3b62..5e63ce8 100644
--- a/src/adaptive.c
+++ b/src/adaptive.c
@@ -37,16 +37,18 @@
  */
 void gfs_cell_coarse_init (FttCell * cell, GfsDomain * domain)
 {
-  GfsVariable * v;
+  GSList * i;
 
   g_return_if_fail (cell != NULL);
   g_return_if_fail (!FTT_CELL_IS_LEAF (cell));
   g_return_if_fail (domain != NULL);
 
-  v = domain->variables;
-  while (v) {
+  i = domain->variables;
+  while (i) {
+    GfsVariable * v = i->data;
+  
     (* v->fine_coarse) (cell, v);
-    v = v->next;
+    i = i->next;
   }
 }
 
@@ -64,7 +66,7 @@ void gfs_cell_coarse_init (FttCell * cell, GfsDomain * domain)
 void gfs_cell_fine_init (FttCell * cell, GfsDomain * domain)
 {
   FttCell * parent;
-  GfsVariable * v;
+  GSList * i;
 
   g_return_if_fail (cell != NULL);
   g_return_if_fail (!FTT_CELL_IS_ROOT (cell));
@@ -75,10 +77,12 @@ void gfs_cell_fine_init (FttCell * cell, GfsDomain * domain)
   g_assert (GFS_CELL_IS_BOUNDARY (parent) || GFS_IS_FLUID (parent));
 
   gfs_cell_init (cell, domain);
-  v = domain->variables;
-  while (v) {
+  i = domain->variables;
+  while (i) {
+    GfsVariable * v = i->data;
+
     GFS_VARIABLE (cell, v->i) = GFS_VARIABLE (parent, v->i);
-    v = v->next;
+    i = i->next;
   }
 
   if (!GFS_CELL_IS_BOUNDARY (parent)) {
@@ -86,8 +90,10 @@ void gfs_cell_fine_init (FttCell * cell, GfsDomain * domain)
     FttComponent c;
     
     ftt_cell_relative_pos (cell, &p);
-    v = domain->variables;
-    while (v) {
+    i = domain->variables;
+    while (i) {
+      GfsVariable * v = i->data;
+
       for (c = 0; c < FTT_DIMENSION; c++)
 	GFS_VARIABLE (cell, v->i) += (&p.x)[c]*
 #if 1
@@ -95,13 +101,21 @@ void gfs_cell_fine_init (FttCell * cell, GfsDomain * domain)
 #else
           gfs_center_gradient (parent, c, v->i);
 #endif
-      v = v->next;
+      i = i->next;
     }
   }
 }
 
 /* GfsAdapt: Object */
 
+typedef struct {
+  GfsSimulation * sim;
+  guint nc;
+  GtsEHeap * hcoarse, * hfine;
+  gdouble clim;
+  GfsVariable * hcoarsev, * hfinev, * costv, * c;
+} AdaptParams;
+
 static void gfs_adapt_destroy (GtsObject * o)
 {
   gts_object_destroy (GTS_OBJECT (GFS_ADAPT (o)->minlevel));
@@ -337,6 +351,7 @@ static gboolean gfs_adapt_vorticity_event (GfsEvent * event,
       (event, sim)) {
     GfsAdaptVorticity * a = GFS_ADAPT_VORTICITY (event);
 
+    a->u = gfs_domain_velocity (GFS_DOMAIN (sim));
     a->maxa = gfs_domain_norm_velocity (GFS_DOMAIN (sim), FTT_TRAVERSE_LEAFS, -1).infty;
     return TRUE;
   }
@@ -352,7 +367,7 @@ static gdouble cost_vorticity (FttCell * cell, GfsAdaptVorticity * a)
 {
   if (a->maxa <= 0.)
     return 0.;
-  return fabs (gfs_vorticity (cell))*ftt_cell_size (cell)/a->maxa;
+  return fabs (gfs_vorticity (cell, a->u))*ftt_cell_size (cell)/a->maxa;
 }
 
 static void gfs_adapt_vorticity_init (GfsAdaptVorticity * object)
@@ -442,9 +457,7 @@ static void gfs_adapt_function_class_init (GtsObjectClass * klass)
 
 static gdouble function_cost (FttCell * cell, GfsAdaptFunction * a)
 {
-  FttVector p;
-  gfs_cell_cm (cell, &p);
-  return gfs_function_value (a->f, cell, &p, gfs_object_simulation (a)->time.t);
+  return gfs_function_value (a->f, cell);
 }
 
 static void gfs_adapt_function_init (GfsAdaptFunction * object)
@@ -682,9 +695,9 @@ GfsAdaptNotBox * gfs_adapt_not_box_new (GfsEventClass * klass,
   return a;
 }
 
-#define CELL_COST(cell) (GFS_STATE (cell)->div)
-#define CELL_HCOARSE(c) (GFS_DOUBLE_TO_POINTER (GFS_STATE (c)->dp))
-#define CELL_HFINE(c)   (GFS_DOUBLE_TO_POINTER (GFS_STATE (c)->g[0]))
+#define CELL_COST(cell) (GFS_VARIABLE (cell, p->costv->i))
+#define CELL_HCOARSE(c) (GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (c, p->hcoarsev->i)))
+#define CELL_HFINE(c)   (GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (c, p->hfinev->i)))
 
 static void refine_cell_corner (FttCell * cell, GfsDomain * domain)
 {
@@ -693,32 +706,38 @@ static void refine_cell_corner (FttCell * cell, GfsDomain * domain)
 			    domain);
 }
 
-static FttCell * remove_top_coarse (GtsEHeap * h, gdouble * cost)
+static FttCell * remove_top_coarse (GtsEHeap * h, gdouble * cost, GfsVariable * hcoarse)
 {
   FttCell * cell = gts_eheap_remove_top (h, cost);
-  if (cell) GFS_STATE (cell)->dp = 0.;
+
+  if (cell)
+    GFS_VARIABLE (cell, hcoarse->i) = 0.;
   while (cell && !FTT_CELL_IS_LEAF (cell)) {
     cell = gts_eheap_remove_top (h, cost);
-    if (cell) GFS_STATE (cell)->dp = 0.;
+    if (cell) 
+      GFS_VARIABLE (cell, hcoarse->i) = 0.;
   }
   return cell;
 }
 
-static FttCell * remove_top_fine (GtsEHeap * h, gdouble * cost)
+static FttCell * remove_top_fine (GtsEHeap * h, gdouble * cost, GfsVariable * hfine)
 {
   FttCell * cell = gts_eheap_remove_top (h, cost);
-  if (cell) GFS_STATE (cell)->g[0] = 0.;
+
+  if (cell)
+    GFS_VARIABLE (cell, hfine->i) = 0.;
   while (cell && ftt_cell_depth (cell) - ftt_cell_level (cell) != 1) {
     cell = gts_eheap_remove_top (h, cost);
-    if (cell) GFS_STATE (cell)->g[0] = 0.;
+    if (cell) 
+      GFS_VARIABLE (cell, hfine->i) = 0.;
   }
   return cell;
 }
 
-static void cell_coarse_init (FttCell * cell, GfsDomain * domain)
+static void cell_coarse_init (FttCell * cell, AdaptParams * p)
 {
   CELL_COST (cell) = 0.;
-  gfs_cell_coarse_init (cell, domain);
+  gfs_cell_coarse_init (cell, GFS_DOMAIN (p->sim));
 }
 
 static gdouble refine_cost (FttCell * cell, GfsSimulation * sim)
@@ -737,15 +756,13 @@ static gdouble refine_cost (FttCell * cell, GfsSimulation * sim)
   return cost;
 }
 
-static void compute_cost (FttCell * cell, gpointer * data)
+static void compute_cost (FttCell * cell, AdaptParams * p)
 {
-  guint * nc = data[1];
-
-  (*nc)++;
+  p->nc++;
   if (!GFS_IS_MIXED (cell)) {
-    gdouble cost = refine_cost (cell, data[0]);
+    gdouble cost = refine_cost (cell, p->sim);
 
-    GFS_STATE (cell)->dp = GFS_STATE (cell)->g[0] = 0.;
+    GFS_VARIABLE (cell, p->hcoarsev->i) = GFS_VARIABLE (cell, p->hfinev->i) = 0.;
     if (FTT_CELL_IS_LEAF (cell)) {
       CELL_COST (cell) = cost;
     }
@@ -773,23 +790,21 @@ static void compute_cost (FttCell * cell, gpointer * data)
   }
 }
 
-static void store_cost (FttCell * cell, GfsVariable * c)
+static void store_cost (FttCell * cell, AdaptParams * p)
 {
-  GFS_VARIABLE (cell, c->i) = GFS_IS_MIXED (cell) ? 0. : CELL_COST (cell);
+  GFS_VARIABLE (cell, p->c->i) = GFS_IS_MIXED (cell) ? 0. : CELL_COST (cell);
 }
 
 static guint minlevel (FttCell * cell, GfsSimulation * sim)
 {
   guint minlevel = 0;
   GSList * i = sim->adapts->items;
-  FttVector p;
 
-  ftt_cell_pos (cell, &p);
   while (i) {
     GfsAdapt * a = i->data;
     guint l;
     
-    if (a->active && (l = gfs_function_value (a->minlevel, NULL, &p, sim->time.t)) > minlevel)
+    if (a->active && (l = gfs_function_value (a->minlevel, cell)) > minlevel)
       minlevel = l;
     i = i->next;
   }
@@ -800,83 +815,68 @@ static guint maxlevel (FttCell * cell, GfsSimulation * sim)
 {
   GSList * i = sim->adapts->items;
   guint maxlevel = G_MAXINT;
-  FttVector p;
 
-  ftt_cell_pos (cell, &p);
   while (i) {
     GfsAdapt * a = i->data;
     guint l;
 
-    if (a->active && (l = gfs_function_value (a->maxlevel, NULL, &p, sim->time.t)) < maxlevel)
+    if (a->active && (l = gfs_function_value (a->maxlevel, cell)) < maxlevel)
       maxlevel = l;
     i = i->next;
   }
   return maxlevel;
 }
 
-static void fill_heaps (FttCell * cell, gpointer * data)
+static void fill_heaps (FttCell * cell, AdaptParams * p)
 {
   /* refinement of solid cells not implemented (yet) */
   if (!GFS_IS_MIXED (cell)) {
-    GtsEHeap * hcoarse = data[0], * hfine = data[1];
-    GfsSimulation * sim = data[2];
     guint level = ftt_cell_level (cell);
     FttCell * parent = ftt_cell_parent (cell);
 
-    if (level < maxlevel (cell, sim))
-      GFS_DOUBLE_TO_POINTER (GFS_STATE (cell)->dp) = 
-	gts_eheap_insert_with_key (hcoarse, cell, - CELL_COST (cell));
-    if (parent && GFS_STATE (parent)->g[0] == 0. && !GFS_IS_MIXED (parent) && 
-	level > minlevel (parent, sim))
-      GFS_DOUBLE_TO_POINTER (GFS_STATE (parent)->g[0]) = 
-	gts_eheap_insert_with_key (hfine, parent, CELL_COST (parent));
+    if (level < maxlevel (cell, p->sim))
+      GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (cell, p->hcoarsev->i)) = 
+	gts_eheap_insert_with_key (p->hcoarse, cell, - CELL_COST (cell));
+    if (parent && GFS_VARIABLE (parent, p->hfinev->i) == 0. && !GFS_IS_MIXED (parent) && 
+	level > minlevel (parent, p->sim))
+      GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (parent, p->hfinev->i)) = 
+	gts_eheap_insert_with_key (p->hfine, parent, CELL_COST (parent));
   }
 }
 
-#ifdef DEBUG
-static void cost_stats (FttCell * cell, GtsRange * r)
-{
-  if (!GFS_IS_MIXED (cell))
-    gts_range_add_value (r, CELL_COST (cell));
-}
-#endif /* DEBUG */
-
-static gboolean fine_cell_coarsenable (FttCell * cell, gpointer * data)
+static gboolean fine_cell_coarsenable (FttCell * cell, AdaptParams * p)
 {
   if (GFS_CELL_IS_BOUNDARY (cell))
     return TRUE;
   if (GFS_IS_MIXED (cell))
     return FALSE;
-  if (CELL_COST (cell) >= -(*((gdouble *)data[4])))
+  if (CELL_COST (cell) >= -p->clim)
     return FALSE;
-  if (ftt_cell_level (cell) < minlevel (cell, data[2]))
+  if (ftt_cell_level (cell) < minlevel (cell, p->sim))
     return FALSE;
   return TRUE;      
 }
 
-static void fine_cell_cleanup (FttCell * cell, gpointer * data)
+static void fine_cell_cleanup (FttCell * cell, AdaptParams * p)
 {
   if (!GFS_CELL_IS_BOUNDARY (cell)) {
-    guint * nc = data[3];
-    gpointer p;
-
-    (*nc)--;
-    if ((p = CELL_HCOARSE (cell)))
-      gts_eheap_remove (data[0], p);
-    if ((p = CELL_HFINE (cell)))
-      gts_eheap_remove (data[1], p);
+    gpointer o;
+
+    p->nc--;
+    if ((o = CELL_HCOARSE (cell)))
+      gts_eheap_remove (p->hcoarse, o);
+    if ((o = CELL_HFINE (cell)))
+      gts_eheap_remove (p->hfine, o);
   }
   gfs_cell_cleanup (cell);
 }
 
-static void cell_fine_init (FttCell * cell, gpointer * data)
+static void cell_fine_init (FttCell * cell, AdaptParams * p)
 {
-  gfs_cell_fine_init (cell, data[2]);
+  gfs_cell_fine_init (cell, GFS_DOMAIN (p->sim));
   CELL_COST (cell) = G_MAXDOUBLE;
-  if (!GFS_CELL_IS_BOUNDARY (ftt_cell_parent (cell))) {
-    guint * nc = data[3];
-    (*nc)++;
-  }
+  if (!GFS_CELL_IS_BOUNDARY (ftt_cell_parent (cell)))
+    p->nc++;
 }
 
 /**
@@ -925,103 +925,95 @@ void gfs_simulation_adapt (GfsSimulation * simulation,
     i = i->next;
   }
   if (active) {
-    guint depth, nc = 0;
+    guint depth;
     gint l;
-    GfsVariable * v;
-    GtsEHeap * hcoarse, * hfine;
-    gdouble ccoarse = 0., cfine = 0., clim;
+    GSList * i;
+    gdouble ccoarse = 0., cfine = 0.;
     FttCell * coarse, * fine;
-    gpointer data[5];
     gboolean changed = TRUE;
+    AdaptParams apar;
     
     depth = gfs_domain_depth (domain);
+
+    apar.sim = simulation;
+    apar.nc = 0;
+    apar.costv = gfs_temporary_variable (domain);
+    apar.hcoarsev = gfs_temporary_variable (domain);
+    apar.hfinev = gfs_temporary_variable (domain);
+    apar.hcoarse = gts_eheap_new (NULL, NULL);
+    apar.hfine = gts_eheap_new (NULL, NULL);
+    apar.c = c;
+
     gfs_domain_cell_traverse (domain, 
 			      FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-			      (FttCellTraverseFunc) cell_coarse_init,
-			      domain);
-    data[0] = simulation;
-    data[1] = &nc;
+			      (FttCellTraverseFunc) cell_coarse_init, &apar);
     for (l = depth; l >= 0; l--)
       gfs_domain_cell_traverse (domain, 
 				FTT_PRE_ORDER, FTT_TRAVERSE_LEVEL, l,
-				(FttCellTraverseFunc) compute_cost, data);
-    if (c)
+				(FttCellTraverseFunc) compute_cost, &apar);
+    if (apar.c)
       gfs_domain_cell_traverse (domain, 
 				FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-				(FttCellTraverseFunc) store_cost, c);
-#ifdef DEBUG
-    {
-      GtsRange r;
-      gts_range_init (&r);
-      gfs_domain_cell_traverse (domain, FTT_POST_ORDER, 
-				FTT_TRAVERSE_NON_LEAFS, -1,
-				(FttCellTraverseFunc) cost_stats, &r);
-      gts_range_update (&r);
-      fprintf (stderr, "cost_before: ");
-      gts_range_print (&r, stderr);
-      fprintf (stderr, "\n");
-    }
-#endif
-    hcoarse = gts_eheap_new (NULL, NULL);
-    hfine = gts_eheap_new (NULL, NULL);
-    gts_eheap_freeze (hcoarse);
-    gts_eheap_freeze (hfine);
-    data[0] = hcoarse;
-    data[1] = hfine;
-    data[2] = simulation;
-    data[3] = &nc;
-    data[4] = &clim;
+				(FttCellTraverseFunc) store_cost, &apar);
+    gts_eheap_freeze (apar.hcoarse);
+    gts_eheap_freeze (apar.hfine);
     gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttCellTraverseFunc) fill_heaps, data);
-    gts_eheap_thaw (hcoarse);
-    gts_eheap_thaw (hfine);
-    coarse = remove_top_coarse (hcoarse, &ccoarse);
-    fine = remove_top_fine (hfine, &cfine);
+			      (FttCellTraverseFunc) fill_heaps, &apar);
+    gts_eheap_thaw (apar.hcoarse);
+    gts_eheap_thaw (apar.hfine);
+    coarse = remove_top_coarse (apar.hcoarse, &ccoarse, apar.hcoarsev);
+    fine = remove_top_fine (apar.hfine, &cfine, apar.hfinev);
 #ifdef DEBUG
-    fprintf (stderr, "initial: %g %g %d\n", cfine, -ccoarse, nc);
+    fprintf (stderr, "initial: %g %g %d\n", cfine, -ccoarse, apar.nc);
 #endif /* DEBUG */
     while (changed) {
 #ifdef DEBUG
-      fprintf (stderr, "%g %g %d\n", cfine, -ccoarse, nc);
+      fprintf (stderr, "%g %g %d\n", cfine, -ccoarse, apar.nc);
 #endif /* DEBUG */
       changed = FALSE;
-      if (fine && ((cfine < -ccoarse && nc > maxcells) || 
-		   (cfine < cmax && nc >= mincells))) {
-	guint n = nc;
+      if (fine && ((cfine < -ccoarse && apar.nc > maxcells) || 
+		   (cfine < cmax && apar.nc >= mincells))) {
+	guint n = apar.nc;
 	
-	clim = MIN (ccoarse, -cmax);
+	apar.clim = MIN (ccoarse, -cmax);
 	ftt_cell_coarsen (fine,
-			  (FttCellCoarsenFunc) fine_cell_coarsenable, data,
-			  (FttCellCleanupFunc) fine_cell_cleanup, data);
+			  (FttCellCoarsenFunc) fine_cell_coarsenable, &apar,
+			  (FttCellCleanupFunc) fine_cell_cleanup, &apar);
 #ifdef DEBUG
-	fprintf (stderr, "coarsen: %d\n", nc);
+	fprintf (stderr, "coarsen: %d\n", apar.nc);
 #endif /* DEBUG */
-	fine = remove_top_fine (hfine, &cfine);
-	if (s) gts_range_add_value (&s->removed, n - nc);
+	fine = remove_top_fine (apar.hfine, &cfine, apar.hfinev);
+	if (s)
+	  gts_range_add_value (&s->removed, n - apar.nc);
 	changed = TRUE;
       }
-      if (coarse && ((-ccoarse > cfine && nc < mincells) ||
-		     (-ccoarse > cmax && nc <= maxcells))) {
-	guint level = ftt_cell_level (coarse), n = nc;
+      if (coarse && ((-ccoarse > cfine && apar.nc < mincells) ||
+		     (-ccoarse > cmax && apar.nc <= maxcells))) {
+	guint level = ftt_cell_level (coarse), n = apar.nc;
 	
-	ftt_cell_refine_corners (coarse, (FttCellInitFunc) cell_fine_init, data);
-	ftt_cell_refine_single (coarse, (FttCellInitFunc) cell_fine_init, data);
+	ftt_cell_refine_corners (coarse, (FttCellInitFunc) cell_fine_init, &apar);
+	ftt_cell_refine_single (coarse, (FttCellInitFunc) cell_fine_init, &apar);
 	if (level + 1 > depth)
 	  depth = level + 1;
 #ifdef DEBUG
-	fprintf (stderr, "refine: %d\n", nc);
+	fprintf (stderr, "refine: %d\n", apar.nc);
 #endif /* DEBUG */
-	coarse = remove_top_coarse (hcoarse, &ccoarse);
-	if (s) gts_range_add_value (&s->created, nc - n);
+	coarse = remove_top_coarse (apar.hcoarse, &ccoarse, apar.hcoarsev);
+	if (s) 
+	  gts_range_add_value (&s->created, apar.nc - n);
 	changed = TRUE;
       }
     }
     if (s) {
       gts_range_add_value (&s->cmax, -ccoarse);
-      gts_range_add_value (&s->ncells, nc);
+      gts_range_add_value (&s->ncells, apar.nc);
     }
-    gts_eheap_destroy (hcoarse);
-    gts_eheap_destroy (hfine);
+
+    gts_eheap_destroy (apar.hcoarse);
+    gts_eheap_destroy (apar.hfine);
+    gts_object_destroy (GTS_OBJECT (apar.costv));
+    gts_object_destroy (GTS_OBJECT (apar.hcoarsev));
+    gts_object_destroy (GTS_OBJECT (apar.hfinev));
     
     for (l = depth - 2; l >= 0; l--)
       gfs_domain_cell_traverse (domain, 
@@ -1030,30 +1022,11 @@ void gfs_simulation_adapt (GfsSimulation * simulation,
 				domain);
     gfs_domain_match (domain);
     gfs_set_merged (domain);
-    v = domain->variables;
-    while (v) {
-      gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v);
-      v = v->next;
+    i = domain->variables;
+    while (i) {
+      gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, i->data);
+      i = i->next;
     }
-    
-#ifdef DEBUG
-    {
-      GtsRange r;
-      
-      data[0] = simulation;
-      data[1] = &nc;
-      gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-				(FttCellTraverseFunc) compute_cost, data);
-      gts_range_init (&r);
-      gfs_domain_cell_traverse (domain, FTT_POST_ORDER, 
-				FTT_TRAVERSE_NON_LEAFS, -1,
-				(FttCellTraverseFunc) cost_stats, &r);
-      gts_range_update (&r);
-      fprintf (stderr, "cost_after: ");
-      gts_range_print (&r, stderr);
-      fprintf (stderr, "\n");
-    }
-#endif /* DEBUG */
   }
 
   gfs_domain_timer_stop (domain, "adapt");
diff --git a/src/adaptive.h b/src/adaptive.h
index c61d991..5a2b730 100644
--- a/src/adaptive.h
+++ b/src/adaptive.h
@@ -68,6 +68,7 @@ typedef struct _GfsAdaptVorticity         GfsAdaptVorticity;
 struct _GfsAdaptVorticity {
   /*< private >*/
   GfsAdapt parent;
+  GfsVariable ** u;
   gdouble maxa;
 
   /*< public >*/
diff --git a/src/advection.c b/src/advection.c
index 8309084..c6bcfa2 100644
--- a/src/advection.c
+++ b/src/advection.c
@@ -31,7 +31,7 @@ static gdouble transverse_term (FttCell * cell,
 {
   GfsStateVector * s = GFS_STATE (cell);
   gdouble vtan = par->use_centered_velocity ? 
-    GFS_VARIABLE (cell, GFS_VELOCITY_INDEX (c)) :
+    GFS_VARIABLE (cell, par->u[c]->i) :
     (s->f[2*c].un + s->f[2*c + 1].un)/2.;
   FttCellFace f;
   GfsGradient gf;
@@ -69,7 +69,7 @@ void gfs_cell_advected_face_values (FttCell * cell,
   size = ftt_cell_size (cell);
   for (c = 0; c < FTT_DIMENSION; c++) {
     gdouble unorm = par->use_centered_velocity ?
-      par->dt*GFS_VARIABLE (cell, GFS_VELOCITY_INDEX (c))/size :
+      par->dt*GFS_VARIABLE (cell, par->u[c]->i)/size :
       par->dt*(s->f[2*c].un + s->f[2*c + 1].un)/(2.*size);
     gdouble g = (* par->gradient) (cell, c, par->v->i);
     gdouble vl = GFS_VARIABLE (cell, par->v->i) + MIN ((1. - unorm)/2., 0.5)*g;
@@ -249,6 +249,7 @@ static gdouble interpolate_2D1 (const FttCell * cell,
  * gfs_face_upwinded_value:
  * @face: a #FttCellFace.
  * @upwinding: type of upwinding.
+ * @u: the cell-centered velocity.
  *
  * This function assumes that the face variable has been previously
  * defined using gfs_cell_advected_face_values().
@@ -256,7 +257,8 @@ static gdouble interpolate_2D1 (const FttCell * cell,
  * Returns: the upwinded value of the face variable.  
  */
 gdouble gfs_face_upwinded_value (const FttCellFace * face,
-				 GfsUpwinding upwinding)
+				 GfsUpwinding upwinding,
+				 GfsVariable ** u)
 {
   gdouble un = 0.;
 
@@ -267,11 +269,16 @@ gdouble gfs_face_upwinded_value (const FttCellFace * face,
 
   switch (upwinding) {
   case GFS_CENTERED_UPWINDING:
-    un = gfs_face_interpolated_value (face, GFS_VELOCITY_INDEX (face->d/2)); break;
+    g_return_val_if_fail (u != NULL, 0.);
+    un = gfs_face_interpolated_value (face, u[face->d/2]->i); 
+    break;
   case GFS_FACE_UPWINDING:
-    un = GFS_FACE_NORMAL_VELOCITY (face); break;
+    un = GFS_FACE_NORMAL_VELOCITY (face); 
+    break;
   case GFS_NO_UPWINDING:
     break;
+  default:
+    g_assert_not_reached ();
   }
   if (!FTT_FACE_DIRECT (face))
     un = - un;
@@ -351,7 +358,7 @@ void gfs_face_advection_flux (const FttCellFace * face,
   g_return_if_fail (par != NULL);
 
   flux = GFS_FACE_FRACTION (face)*GFS_FACE_NORMAL_VELOCITY (face)*par->dt*
-    gfs_face_upwinded_value (face, GFS_FACE_UPWINDING)/ftt_cell_size (face->cell);
+    gfs_face_upwinded_value (face, GFS_FACE_UPWINDING, NULL)/ftt_cell_size (face->cell);
   if (!FTT_FACE_DIRECT (face))
     flux = - flux;
   GFS_VARIABLE (face->cell, par->fv->i) -= flux;
@@ -392,7 +399,7 @@ void gfs_face_velocity_advection_flux (const FttCellFace * face,
   g_return_if_fail (face != NULL);
   g_return_if_fail (par != NULL);
 
-  c = GFS_VELOCITY_COMPONENT (par->v->i);
+  c = par->v->component;
   g_return_if_fail (c >= 0 && c < FTT_DIMENSION);
 
   flux = GFS_FACE_FRACTION (face)*GFS_FACE_NORMAL_VELOCITY (face)*par->dt
@@ -402,9 +409,9 @@ void gfs_face_velocity_advection_flux (const FttCellFace * face,
     flux *= GFS_FACE_NORMAL_VELOCITY (face);
   else /* tangential component */
 #else
-    flux *= gfs_face_upwinded_value (face, par->upwinding)
+    flux *= gfs_face_upwinded_value (face, par->upwinding, par->u)
       /* pressure correction */
-      - gfs_face_interpolated_value (face, GFS_GRADIENT_INDEX (c))*par->dt/2.;
+      - gfs_face_interpolated_value (face, par->g[c]->i)*par->dt/2.;
 #endif
   if (!FTT_FACE_DIRECT (face))
     flux = - flux;
@@ -447,7 +454,7 @@ void gfs_face_velocity_convective_flux (const FttCellFace * face,
   g_return_if_fail (par != NULL);
   g_return_if_fail (GFS_FACE_FRACTION (face) == 1.);
 
-  c = GFS_VELOCITY_COMPONENT (par->v->i);
+  c = par->v->component;
   g_return_if_fail (c >= 0 && c < FTT_DIMENSION);
 
 #if 0
@@ -458,9 +465,9 @@ void gfs_face_velocity_convective_flux (const FttCellFace * face,
       /* pressure correction */
       - gfs_face_interpolated_value (face, GFS_GRADIENT_INDEX (c))*par->dt/2.;
 #else
-  u = gfs_face_upwinded_value (face, par->upwinding)
+  u = gfs_face_upwinded_value (face, par->upwinding, par->u)
     /* pressure correction */
-    - gfs_face_interpolated_value (face, GFS_GRADIENT_INDEX (c))*par->dt/2.;
+    - gfs_face_interpolated_value (face, par->g[c]->i)*par->dt/2.;
 #endif
   u *= par->dt/(2.*ftt_cell_size (face->cell));
   if (!FTT_FACE_DIRECT (face))
@@ -489,7 +496,7 @@ void gfs_face_velocity_convective_flux (const FttCellFace * face,
 /**
  * gfs_face_advected_normal_velocity:
  * @face: a #FttCellFace.
- * @upwinding: the type of upwinding.
+ * @par: the #GfsAdvectionParams.
  *
  * Fills the normal component of the velocity at @face with the value
  * advected (to time t + dt/2) from the centered velocities.
@@ -499,16 +506,17 @@ void gfs_face_velocity_convective_flux (const FttCellFace * face,
  * gfs_cell_advected_face_values().  
  */
 void gfs_face_advected_normal_velocity (const FttCellFace * face,
-					GfsUpwinding * upwinding)
+					const GfsAdvectionParams * par)
 {
   gdouble u;
 
   g_return_if_fail (face != NULL);
+  g_return_if_fail (par != NULL);
 
   if (GFS_FACE_FRACTION (face) == 0.)
     return;
 
-  GFS_FACE_NORMAL_VELOCITY_LEFT (face) = u = gfs_face_upwinded_value (face, *upwinding);
+  GFS_FACE_NORMAL_VELOCITY_LEFT (face) = u = gfs_face_upwinded_value (face, par->upwinding, par->u);
 
   switch (ftt_face_type (face)) {
   case FTT_FINE_FINE:
@@ -527,21 +535,22 @@ void gfs_face_advected_normal_velocity (const FttCellFace * face,
 /**
  * gfs_face_interpolated_normal_velocity:
  * @face: a #FttCellFace.
+ * @v: the velocity.
  *
  * Fills the normal component of the velocity at @face with the value
  * interpolated from the centered velocities.
  */
-void gfs_face_interpolated_normal_velocity (const FttCellFace * face)
+void gfs_face_interpolated_normal_velocity (const FttCellFace * face, GfsVariable ** v)
 {
   gdouble u;
 
   g_return_if_fail (face != NULL);
+  g_return_if_fail (v != NULL);
 
   if (GFS_FACE_FRACTION (face) == 0.)
     return;
 
-  GFS_FACE_NORMAL_VELOCITY_LEFT (face) = u = 
-    gfs_face_interpolated_value (face, GFS_VELOCITY_INDEX (face->d/2));
+  GFS_FACE_NORMAL_VELOCITY_LEFT (face) = u = gfs_face_interpolated_value (face, v[face->d/2]->i);
 
   switch (ftt_face_type (face)) {
   case FTT_FINE_FINE:
@@ -832,6 +841,9 @@ void gfs_advection_params_init (GfsAdvectionParams * par)
 {
   g_return_if_fail (par != NULL);
 
+  par->fv = NULL;
+  par->u = NULL;
+  par->g = NULL;
   par->cfl = 0.8;
   par->dt = 1.;
   par->gradient = gfs_center_gradient;
diff --git a/src/advection.h b/src/advection.h
index c4e6079..6736cf4 100644
--- a/src/advection.h
+++ b/src/advection.h
@@ -45,7 +45,7 @@ void      (* GfsFaceAdvectionFluxFunc)       (const FttCellFace * face,
 
 struct _GfsAdvectionParams {
   gdouble cfl, dt;
-  GfsVariable * v, * fv;
+  GfsVariable * v, * fv, ** u, ** g;
   GfsCenterGradient gradient;
   gboolean use_centered_velocity;
   GfsUpwinding upwinding;
@@ -65,7 +65,8 @@ void         gfs_cell_advected_face_values    (FttCell * cell,
 void         gfs_cell_non_advected_face_values (FttCell * cell,
 						const GfsAdvectionParams * par);
 gdouble      gfs_face_upwinded_value          (const FttCellFace * face,
-					       GfsUpwinding upwinding);
+					       GfsUpwinding upwinding,
+					       GfsVariable ** u);
 void         gfs_face_advection_flux          (const FttCellFace * face,
 					       const GfsAdvectionParams * par);
 void         gfs_face_velocity_advection_flux (const FttCellFace * face,
@@ -73,8 +74,9 @@ void         gfs_face_velocity_advection_flux (const FttCellFace * face,
 void         gfs_face_velocity_convective_flux (const FttCellFace * face,
 						const GfsAdvectionParams * par);
 void         gfs_face_advected_normal_velocity     (const FttCellFace * face,
-						    GfsUpwinding * upwinding);
-void         gfs_face_interpolated_normal_velocity (const FttCellFace * face);
+						    const GfsAdvectionParams * par);
+void         gfs_face_interpolated_normal_velocity (const FttCellFace * face,
+						    GfsVariable ** v);
 void         gfs_face_reset_normal_velocity        (const FttCellFace * face);
 void         gfs_set_merged                        (GfsDomain * domain);
 typedef void (* GfsMergedTraverseFunc)             (GSList * merged,
diff --git a/src/boundary.c b/src/boundary.c
index b6b0ecb..608e3cd 100644
--- a/src/boundary.c
+++ b/src/boundary.c
@@ -37,8 +37,7 @@ static FttVector rpos[FTT_NEIGHBORS] = {
 
 static void symmetry (FttCellFace * f, GfsBc * b)
 {
-  if (b->v->i == GFS_VELOCITY_INDEX (f->d/2) ||
-      b->v->i == GFS_GRADIENT_INDEX (f->d/2))
+  if (b->v->component == f->d/2)
     GFS_VARIABLE (f->cell, b->v->i) = - GFS_VARIABLE (f->neighbor, b->v->i);
   else
     GFS_VARIABLE (f->cell, b->v->i) =   GFS_VARIABLE (f->neighbor, b->v->i);
@@ -46,8 +45,7 @@ static void symmetry (FttCellFace * f, GfsBc * b)
 
 static void face_symmetry (FttCellFace * f, GfsBc * b)
 {
-  if (b->v->i == GFS_VELOCITY_INDEX (f->d/2) ||
-      b->v->i == GFS_GRADIENT_INDEX (f->d/2))
+  if (b->v->component == f->d/2)
     GFS_STATE (f->cell)->f[f->d].v = 
       GFS_STATE (f->neighbor)->f[FTT_OPPOSITE_DIRECTION (f->d)].v = 0.;
   else
@@ -213,8 +211,7 @@ GfsBc * gfs_bc_value_new (GfsBcClass * k,
 static void dirichlet (FttCellFace * f, GfsBc * b)
 {
   GFS_VARIABLE (f->cell, b->v->i) = 
-    2.*gfs_function_face_value (GFS_BC_VALUE (b)->val, f, 
-		     GFS_SIMULATION (gfs_box_domain (b->b->box))->time.t)
+    2.*gfs_function_face_value (GFS_BC_VALUE (b)->val, f)
     - GFS_VARIABLE (f->neighbor, b->v->i);
 }
 
@@ -226,8 +223,7 @@ static void homogeneous_dirichlet (FttCellFace * f, GfsBc * b)
 static void face_dirichlet (FttCellFace * f, GfsBc * b)
 {
   GFS_STATE (f->cell)->f[f->d].v = 
-    gfs_function_face_value (GFS_BC_VALUE (b)->val, f,
-			     GFS_SIMULATION (gfs_box_domain (b->b->box))->time.t);
+    gfs_function_face_value (GFS_BC_VALUE (b)->val, f);
 }
 
 static void gfs_bc_dirichlet_init (GfsBc * object)
@@ -264,8 +260,7 @@ static void neumann (FttCellFace * f, GfsBc * b)
 {
   GFS_VARIABLE (f->cell, b->v->i) = 
     GFS_VARIABLE (f->neighbor, b->v->i) +
-    gfs_function_face_value (GFS_BC_VALUE (b)->val, f, 
-		  GFS_SIMULATION (gfs_box_domain (b->b->box))->time.t)
+    gfs_function_face_value (GFS_BC_VALUE (b)->val, f)
     *ftt_cell_size (f->cell);
 }
 
@@ -278,8 +273,7 @@ static void face_neumann (FttCellFace * f, GfsBc * b)
 {
   GFS_STATE (f->cell)->f[f->d].v = 
     GFS_VARIABLE (f->neighbor, b->v->i) +
-    gfs_function_face_value (GFS_BC_VALUE (b)->val, f, 
-	         GFS_SIMULATION (gfs_box_domain (b->b->box))->time.t)
+    gfs_function_face_value (GFS_BC_VALUE (b)->val, f)
     *ftt_cell_size (f->cell)/2.;
 }
 
@@ -743,8 +737,8 @@ static void inflow_constant_read (GtsObject ** o, GtsFile * fp)
 {
   GfsBoundary * b = GFS_BOUNDARY (*o);  
   FttComponent c;
-  GfsVariable * v;
   GfsFunction * un = GFS_BOUNDARY_INFLOW_CONSTANT (*o)->un;
+  GfsVariable ** v;
 
   if (GTS_OBJECT_CLASS (gfs_boundary_inflow_constant_class ())->parent_class->read)
     (* GTS_OBJECT_CLASS (gfs_boundary_inflow_constant_class ())->parent_class->read) 
@@ -754,14 +748,14 @@ static void inflow_constant_read (GtsObject ** o, GtsFile * fp)
 
   gfs_function_read (un, gfs_box_domain (b->box), fp);
 
-  v = gfs_variable_from_name (gfs_box_domain (b->box)->variables, "U");
-  for (c = 0; c < FTT_DIMENSION; c++, v = v->next)
+  v = gfs_domain_velocity (gfs_box_domain (b->box));
+  for (c = 0; c < FTT_DIMENSION; c++)
     if (c == b->d/2)
       gfs_boundary_add_bc (b, gfs_bc_value_new (gfs_bc_dirichlet_class (),
-						v, un, FALSE));
+						v[c], un, FALSE));
     else
       gfs_boundary_add_bc (b, gfs_bc_value_new (gfs_bc_dirichlet_class (),
-						v, NULL, FALSE));
+						v[c], NULL, FALSE));
 }
 
 static void gfs_boundary_inflow_constant_class_init (GtsObjectClass * klass)
@@ -809,9 +803,9 @@ static GtsColor outflow_color (GtsObject * o)
 
 static void outflow_read (GtsObject ** o, GtsFile * fp)
 {
-  GfsBoundary * b = GFS_BOUNDARY (*o);  
-  FttComponent c;
-  GfsVariable * v;
+  GfsBoundary * b = GFS_BOUNDARY (*o);
+  GfsDomain * domain;
+  GfsVariable ** v;
 
   if (GTS_OBJECT_CLASS (gfs_boundary_outflow_class ())->parent_class->read)
     (* GTS_OBJECT_CLASS (gfs_boundary_outflow_class ())->parent_class->read) 
@@ -819,14 +813,14 @@ static void outflow_read (GtsObject ** o, GtsFile * fp)
   if (fp->type == GTS_ERROR)
     return;
 
-  v = gfs_variable_from_name (gfs_box_domain (b->box)->variables, "U");
-  for (c = 0; c < b->d/2; c++, v = v->next) 
-    ;
-
-  gfs_boundary_add_bc (b, gfs_bc_value_new (gfs_bc_neumann_class (), v, NULL,
-					    FALSE));
+  domain = gfs_box_domain (b->box);
+  v = gfs_domain_velocity (domain);
+  gfs_boundary_add_bc (b, gfs_bc_value_new (gfs_bc_neumann_class (),
+					    v[b->d/2],
+					    NULL, FALSE));
   gfs_boundary_add_bc (b, gfs_bc_value_new (gfs_bc_dirichlet_class (),
-					    gfs_p, NULL, FALSE));
+					    gfs_variable_from_name (domain->variables, "P"),
+					    NULL, FALSE));
 }
 
 static void gfs_boundary_outflow_class_init (GfsBoundaryClass * klass)
diff --git a/src/domain.c b/src/domain.c
index 47dda35..d43e9c0 100644
--- a/src/domain.c
+++ b/src/domain.c
@@ -57,15 +57,14 @@ static void domain_write (GtsObject * o, FILE * fp)
   if (domain->lambda.z != 1.)
     fprintf (fp, "lz = %g ", domain->lambda.z);
   if (domain->max_depth_write > -2) {
-    GfsVariable * v = domain->variables_io;
-
-    if (v != NULL) {
-      fprintf (fp, "variables = %s", v->name);
-      v = v->next;
-      while (v) {
-	if (v->name)
-	  fprintf (fp, ",%s", v->name);
-	v = v->next;
+    GSList * i = domain->variables_io;
+
+    if (i != NULL) {
+      fprintf (fp, "variables = %s", GFS_VARIABLE1 (i->data)->name);
+      i = i->next;
+      while (i) {
+	fprintf (fp, ",%s", GFS_VARIABLE1 (i->data)->name);
+	i = i->next;
       }
       fputc (' ', fp);
     }
@@ -127,26 +126,14 @@ static void domain_read (GtsObject ** o, GtsFile * fp)
 
   if (variables != NULL) {
     gchar * variables1, * s;
-    gboolean empty = TRUE;
 
     variables1 = g_strdup (variables);
     s = strtok (variables1, ",");
     while (s) {
       gfs_domain_add_variable (domain, s);
-      empty = FALSE;
       s = strtok (NULL, ",");
     }
     g_free (variables1);
-
-    if (!empty) {
-      gchar * error;
-
-      if (domain->variables_io != domain->variables)
-	gfs_variable_list_destroy (domain->variables_io);
-      domain->variables_io = gfs_variables_from_list (domain->variables, 
-						      variables, &error);
-      g_assert (domain->variables_io);
-    }
     g_free (variables);
   }
 }
@@ -239,11 +226,20 @@ static void free_pair (gpointer key, gpointer value)
 static void domain_destroy (GtsObject * o)
 {
   GfsDomain * domain = GFS_DOMAIN (o);
+  GSList * i;
 
   g_timer_destroy (domain->timer);
-  gfs_variable_list_destroy (domain->variables);
-  if (domain->variables_io != domain->variables)
-    gfs_variable_list_destroy (domain->variables_io);
+
+  i = domain->variables;
+  while (i) {
+    GSList * next = i->next;
+    gts_object_destroy (i->data);
+    i = next;
+  }
+  g_assert (domain->variables == NULL);
+
+  g_array_free (domain->allocated, TRUE);
+
   g_hash_table_foreach (domain->timers, (GHFunc) free_pair, NULL);
   g_hash_table_destroy (domain->timers);
 
@@ -286,10 +282,11 @@ static void domain_init (GfsDomain * domain)
   domain->rootlevel = 0;
   domain->refpos.x = domain->refpos.y = domain->refpos.z = 0.;
   domain->lambda.x = domain->lambda.y = domain->lambda.z = 1.;
-  domain->variables = gfs_variable_list_copy (gfs_centered_variables, 
-					      GTS_OBJECT (domain));
-  domain->variables_size = sizeof (GfsStateVector);
-  domain->variables_io = domain->variables;
+
+  domain->allocated = g_array_new (FALSE, TRUE, sizeof (gboolean));
+  domain->variables = NULL;
+
+  domain->variables_io = NULL;
   domain->max_depth_write = -1;
 }
 
@@ -666,6 +663,17 @@ static void neumann_bc (FttCell * cell)
   GFS_STATE (cell)->solid->fv = 0.;
 }
 
+static gboolean is_velocity (GfsVariable * v, GfsDomain * domain)
+{
+  FttComponent c;
+  GfsVariable ** u = gfs_domain_velocity (domain);
+
+  for (c = 0; c < FTT_DIMENSION; c++)
+    if (v == u[c])
+      return TRUE;
+  return FALSE;
+}
+
 /**
  * gfs_domain_surface_bc:
  * @domain: a #GfsDomain.
@@ -683,14 +691,12 @@ void gfs_domain_surface_bc (GfsDomain * domain,
     gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL,
       (FttCellTraverseFunc) GFS_SURFACE_GENERIC_BC_CLASS (GTS_OBJECT (v->surface_bc)->klass)->bc, 
 			       v->surface_bc);
-  else {
-    if (GFS_VELOCITY_COMPONENT (v->i) < FTT_DIMENSION)
-      gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL,
-				 (FttCellTraverseFunc) dirichlet_bc, NULL);
-    else
-      gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL,
-				 (FttCellTraverseFunc) neumann_bc, NULL);
-  }
+  else if (is_velocity (v, domain))
+    gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL,
+			       (FttCellTraverseFunc) dirichlet_bc, NULL);
+  else
+    gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL,
+			       (FttCellTraverseFunc) neumann_bc, NULL);
 }
 
 static void box_traverse (GfsBox * box, gpointer * datum)
@@ -1379,11 +1385,13 @@ GfsNorm gfs_domain_norm_variable (GfsDomain * domain,
   return n;
 }
 
-static void add_norm_residual (const FttCell * cell, GfsNorm * n)
+static void add_norm_residual (const FttCell * cell, gpointer * data)
 {
   gdouble size = ftt_cell_size (cell);
-
-  gfs_norm_add (n, GFS_STATE (cell)->res/(size*size), 1.);
+  GfsVariable * res = data[0];
+  GfsNorm * n = data[1];
+  
+  gfs_norm_add (n, GFS_VARIABLE (cell, res->i)/(size*size), 1.);
 }
 
 /**
@@ -1392,6 +1400,7 @@ static void add_norm_residual (const FttCell * cell, GfsNorm * n)
  * @flags: which types of cells are to be visited.
  * @max_depth: maximum depth of the traversal.
  * @dt: the time step.
+ * @res: the residual.
  *
  * Traverses the domain defined by @domain using gfs_domain_cell_traverse()
  * and gathers norm statistics about the volume weighted relative residual
@@ -1404,15 +1413,20 @@ static void add_norm_residual (const FttCell * cell, GfsNorm * n)
 GfsNorm gfs_domain_norm_residual (GfsDomain * domain,
 				  FttTraverseFlags flags,
 				  gint max_depth,
-				  gdouble dt)
+				  gdouble dt,
+				  GfsVariable * res)
 {
   GfsNorm n;
+  gpointer data[2];
 
   g_return_val_if_fail (domain != NULL, n);
+  g_return_val_if_fail (res != NULL, n);
   
   gfs_norm_init (&n);
+  data[0] = res;
+  data[1] = &n;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, flags, max_depth, 
-			   (FttCellTraverseFunc) add_norm_residual, &n);
+			   (FttCellTraverseFunc) add_norm_residual, data);
 #ifdef HAVE_MPI
   domain_norm_reduce (domain, &n);
 #endif /* HAVE_MPI */
@@ -1425,17 +1439,33 @@ GfsNorm gfs_domain_norm_residual (GfsDomain * domain,
   return n;
 }
 
-static void add_norm_velocity (const FttCell * cell, GfsNorm * n)
+/**
+ * gfs_domain_velocity:
+ * @domain: a #GfsDomain.
+ *
+ * Returns: the components of the velocity vector for @domain.
+ */
+GfsVariable ** gfs_domain_velocity (GfsDomain * domain)
 {
   FttComponent c;
-  gdouble unorm = 0.;
+  static gchar name[][2] = {"U","V","W"};
+
+  g_return_val_if_fail (domain != NULL, NULL);
   
   for (c = 0; c < FTT_DIMENSION; c++) {
-    gdouble uc = GFS_VARIABLE (cell, GFS_VELOCITY_INDEX (c));
-
-    unorm += uc*uc;
+    GfsVariable * v = gfs_variable_from_name (domain->variables, name[c]);
+    g_return_val_if_fail (v != NULL, NULL);
+    domain->velocity[c] = v;
   }
-  gfs_norm_add (n, sqrt (unorm), gfs_cell_volume (cell));
+  return domain->velocity;
+}
+
+static void add_norm_velocity (FttCell * cell, gpointer * data)
+{
+  GfsVariable ** u = data[0];
+  GfsNorm * n = data[1];
+  
+  gfs_norm_add (n, gfs_vector_norm (cell, u), gfs_cell_volume (cell));
 }
 
 /**
@@ -1454,12 +1484,15 @@ GfsNorm gfs_domain_norm_velocity (GfsDomain * domain,
 				  gint max_depth)
 {
   GfsNorm n;
+  gpointer data[2];
 
   g_return_val_if_fail (domain != NULL, n);
   
   gfs_norm_init (&n);
+  data[0] = gfs_domain_velocity (domain);
+  data[1] = &n;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, flags, max_depth, 
-			   (FttCellTraverseFunc) add_norm_velocity, &n);
+			   (FttCellTraverseFunc) add_norm_velocity, data);
 #ifdef HAVE_MPI
   domain_norm_reduce (domain, &n);
 #endif /* HAVE_MPI */
@@ -1499,6 +1532,7 @@ static void box_split (GfsBox * box, gpointer * data)
   guint * bid = data[1];
   gboolean * one_box_per_pe = data[2];
   gint * pid = data[3];
+  GfsVariable * newboxp = data[4];
   guint refid = FTT_DIMENSION == 2 ? 2 : 6;
   FttCellChildren child;
   FttDirection d;
@@ -1525,7 +1559,7 @@ static void box_split (GfsBox * box, gpointer * data)
       else
 	newbox->id = (*bid)++;
 
-      GFS_DOUBLE_TO_POINTER (GFS_STATE (child.c[i])->div) = newbox;
+      GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (child.c[i], newboxp->i)) = newbox;
 
       if (FTT_CELL_IS_LEAF (child.c[i]))
 	ftt_cell_refine_single (child.c[i], (FttCellInitFunc) gfs_cell_init, domain);
@@ -1549,7 +1583,7 @@ static void box_split (GfsBox * box, gpointer * data)
       for (i = 0; i < FTT_CELLS/2; i++)
 	if (child.c[i]) {
 	  FttCell * neighbor = ftt_cell_neighbor (child.c[i], d);
-	  GfsBox * newbox = GFS_DOUBLE_TO_POINTER (GFS_STATE (child.c[i])->div);
+	  GfsBox * newbox = GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (child.c[i], newboxp->i));
 	  GfsBoundaryClass * klass = GFS_BOUNDARY_CLASS (GTS_OBJECT (boundary)->klass);
 	  GfsBoundary * newboundary = gfs_boundary_new (klass, newbox, d);
 	  gchar fname[] = "/tmp/XXXXXX";
@@ -1575,15 +1609,17 @@ static void box_split (GfsBox * box, gpointer * data)
     }
 }
 
-static void box_link (GfsBox * box, GfsDomain * domain)
+static void box_link (GfsBox * box, gpointer * data)
 {
+  GfsVariable * newboxp = data[4];
+  GfsDomain * domain = data[5];
   FttCellChildren child;
   guint i;
 
   ftt_cell_children (box->root, &child);
   for (i = 0; i < FTT_CELLS; i++)
     if (child.c[i]) {
-       GfsBox * newbox = GFS_DOUBLE_TO_POINTER (GFS_STATE (child.c[i])->div);
+       GfsBox * newbox = GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (child.c[i], newboxp->i));
        FttDirection d;
        
        g_assert (newbox);
@@ -1593,7 +1629,7 @@ static void box_link (GfsBox * box, GfsDomain * domain)
 	   FttCell * neighbor = ftt_cell_neighbor (child.c[i], d);
 
 	   if (neighbor) {
-	     GfsBox * newbox1 = GFS_DOUBLE_TO_POINTER (GFS_STATE (neighbor)->div);
+	     GfsBox * newbox1 = GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (neighbor, newboxp->i));
 	     FttDirection od = FTT_OPPOSITE_DIRECTION (d);
 	     GfsGEdge * edge;
 
@@ -1610,7 +1646,7 @@ static void box_link (GfsBox * box, GfsDomain * domain)
     }
 }
 
-static void box_destroy (GfsBox * box)
+static void box_destroy (GfsBox * box, GfsVariable * newboxp)
 {
   GfsBox * newbox[FTT_CELLS];
   FttCellChildren child;
@@ -1619,7 +1655,7 @@ static void box_destroy (GfsBox * box)
   ftt_cell_children (box->root, &child);
   for (i = 0; i < FTT_CELLS; i++)
     if (child.c[i])
-      newbox[i] = GFS_DOUBLE_TO_POINTER (GFS_STATE (child.c[i])->div);
+      newbox[i] = GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (child.c[i], newboxp->i));
     else
       newbox[i] = NULL;
 
@@ -1654,20 +1690,25 @@ void gfs_domain_split (GfsDomain * domain, gboolean one_box_per_pe)
   GSList * list = NULL;
   guint bid = 2;
   gint pid = 0;
-  gpointer data[4];
+  gpointer data[6];
+  GfsVariable * newboxp;
 
   g_return_if_fail (domain != NULL);
 
+  newboxp = gfs_temporary_variable (domain);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, 1,
-  			   (FttCellTraverseFunc) gfs_cell_reset, gfs_div);
+  			   (FttCellTraverseFunc) gfs_cell_reset, newboxp);
   data[0] = &list;
   data[1] = &bid;
   data[2] = &one_box_per_pe;
   data[3] = &pid;
+  data[4] = newboxp;
+  data[5] = domain;
   gts_container_foreach (GTS_CONTAINER (domain), (GtsFunc) box_split, data);
-  g_slist_foreach (list, (GFunc) box_link, domain);
-  g_slist_foreach (list, (GFunc) box_destroy, NULL);
+  g_slist_foreach (list, (GFunc) box_link, data);
+  g_slist_foreach (list, (GFunc) box_destroy, newboxp);
   g_slist_free (list);
+  gts_object_destroy (GTS_OBJECT (newboxp));
 
   gfs_domain_match (domain);
   domain->rootlevel++;
@@ -1733,7 +1774,7 @@ void gfs_domain_advect_point (GfsDomain * domain,
   FttCell * cell;
   FttVector p0, p1;
   FttComponent c;
-  GfsVariable * v1, * v;
+  GfsVariable ** u;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (p != NULL);
@@ -1744,15 +1785,14 @@ void gfs_domain_advect_point (GfsDomain * domain,
   cell = gfs_domain_locate (domain, p0, -1);
   if (cell == NULL)
     return;
-  v1 = v = gfs_variable_from_name (domain->variables, "U");
-  for (c = 0; c < FTT_DIMENSION; c++, v = v->next)
-    (&p1.x)[c] += dt*gfs_interpolate (cell, p0, v)/2.;
+  u = gfs_domain_velocity (domain);
+  for (c = 0; c < FTT_DIMENSION; c++)
+    (&p1.x)[c] += dt*gfs_interpolate (cell, p0, u[c])/2.;
   cell = gfs_domain_locate (domain, p1, -1);
   if (cell == NULL)
     return;
-  v = v1;
-  for (c = 0; c < FTT_DIMENSION; c++, v = v->next)
-    (&p->x)[c] += dt*gfs_interpolate (cell, p1, v);
+  for (c = 0; c < FTT_DIMENSION; c++)
+    (&p->x)[c] += dt*gfs_interpolate (cell, p1, u[c]);
 }
 
 static void count (FttCell * cell, guint * n)
@@ -1793,19 +1833,19 @@ guint gfs_domain_size (GfsDomain * domain,
 static void minimum_cfl (FttCell * cell, gpointer * data)
 {
   gdouble * cfl = data[0];
-  GfsVariable * v = data[1];
+  GfsVariable ** v = data[1];
   gdouble size = ftt_cell_size (cell);
   FttComponent c;
 
-  for (c = 0; c < FTT_DIMENSION; c++, v = v->next) {
-    if (GFS_VARIABLE (cell, v->i) != 0.) {
-      gdouble cflu = size/fabs (GFS_VARIABLE (cell, v->i));
+  for (c = 0; c < FTT_DIMENSION; c++) {
+    if (GFS_VARIABLE (cell, v[c]->i) != 0.) {
+      gdouble cflu = size/fabs (GFS_VARIABLE (cell, v[c]->i));
 
       if (cflu*cflu < *cfl)
 	*cfl = cflu*cflu;
     }
-    if (v->sources) {
-      gdouble g = gfs_variable_mac_source (v, cell);
+    if (v[c]->sources) {
+      gdouble g = gfs_variable_mac_source (v[c], cell);
 
       if (g != 0.) {
 	gdouble cflg = 2.*size/fabs (g);
@@ -1838,7 +1878,7 @@ gdouble gfs_domain_cfl (GfsDomain * domain,
   g_return_val_if_fail (domain != NULL, 0.);
 
   data[0] = &cfl;
-  data[1] = gfs_variable_from_name (domain->variables, "U");
+  data[1] = gfs_domain_velocity (domain);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, flags, max_depth, 
 			    (FttCellTraverseFunc) minimum_cfl, data);
 #ifdef HAVE_MPI
@@ -1865,7 +1905,23 @@ void gfs_cell_init (FttCell * cell, GfsDomain * domain)
   g_return_if_fail (cell->data == NULL);
   g_return_if_fail (domain != NULL);
 
-  cell->data = g_malloc0 (domain->variables_size);
+  cell->data = g_malloc0 (gfs_domain_variables_size (domain));
+}
+
+/**
+ * gfs_cell_reinit:
+ * @cell: a #FttCell.
+ * @domain: a #GfsDomain containing @cell.
+ *
+ * Re-allocates the memory for fluid state data associated to @cell.
+ */
+void gfs_cell_reinit (FttCell * cell, GfsDomain * domain)
+{
+  g_return_if_fail (cell != NULL);
+  g_return_if_fail (cell->data != NULL);
+  g_return_if_fail (domain != NULL);
+
+  cell->data = g_realloc (cell->data, gfs_domain_variables_size (domain));
 }
 
 /**
@@ -1896,7 +1952,7 @@ void gfs_cell_copy (const FttCell * from,
       tos = GFS_STATE (to);
     }
     solid = tos->solid;
-    memcpy (to->data, from->data, domain->variables_size);
+    memcpy (to->data, from->data, gfs_domain_variables_size (domain));
     if (froms->solid == NULL) {
       if (solid)
 	g_free (solid);
@@ -1914,14 +1970,14 @@ void gfs_cell_copy (const FttCell * from,
  * gfs_cell_write:
  * @cell: a #FttCell.
  * @fp: a file pointer.
- * @variables: the #GfsVariable to be written.
+ * @variables: the list of #GfsVariable to be written.
  *
  * Writes in @fp the fluid data associated with @cell and described by
  * @variables. This function is generally used in association with
  * ftt_cell_write().  
  */
 void gfs_cell_write (const FttCell * cell, FILE * fp,
-		     GfsVariable * variables)
+		     GSList * variables)
 {
   g_return_if_fail (cell != NULL);
   g_return_if_fail (fp != NULL);
@@ -1940,8 +1996,7 @@ void gfs_cell_write (const FttCell * cell, FILE * fp,
     fputs (" -1", fp);
   
   while (variables) {
-    if (variables->name)
-      fprintf (fp, " %g", GFS_VARIABLE (cell, variables->i));
+    fprintf (fp, " %g", GFS_VARIABLE (cell, GFS_VARIABLE1 (variables->data)->i));
     variables = variables->next;
   }
 }
@@ -1960,7 +2015,7 @@ void gfs_cell_read (FttCell * cell, GtsFile * fp, GfsDomain * domain)
 {
   gdouble s0;
   GfsStateVector * s;
-  GfsVariable * v;
+  GSList * i;
 
   g_return_if_fail (cell != NULL);
   g_return_if_fail (fp != NULL);
@@ -2005,15 +2060,17 @@ void gfs_cell_read (FttCell * cell, GtsFile * fp, GfsDomain * domain)
     }
   }
 
-  v = domain->variables_io;
-  while (v) {
+  i = domain->variables_io;
+  while (i) {
+    GfsVariable * v = i->data;
+
     if (fp->type != GTS_INT && fp->type != GTS_FLOAT) {
       gts_file_error (fp, "expecting a number (%s)", v->name);
       return;
     }
     GFS_VARIABLE (cell, v->i) = atof (fp->token->str);
     gts_file_next_token (fp);
-    v = v->next;
+    i = i->next;
   }
 }
 
@@ -2021,14 +2078,14 @@ void gfs_cell_read (FttCell * cell, GtsFile * fp, GfsDomain * domain)
  * gfs_cell_write_binary:
  * @cell: a #FttCell.
  * @fp: a file pointer.
- * @variables: the #GfsVariable to be written.
+ * @variables: the list of #GfsVariable to be written.
  *
  * Writes in @fp the fluid data associated with @cell and described by
  * @variables. This function is generally used in association with
  * ftt_cell_write_binary().
  */
 void gfs_cell_write_binary (const FttCell * cell, FILE * fp,
-			    GfsVariable * variables)
+			    GSList * variables)
 {
   g_return_if_fail (cell != NULL);
   g_return_if_fail (fp != NULL);
@@ -2046,10 +2103,8 @@ void gfs_cell_write_binary (const FttCell * cell, FILE * fp,
   }
   
   while (variables) {
-    if (variables->name) {
-      gdouble a = GFS_VARIABLE (cell, variables->i);
-      fwrite (&a, sizeof (gdouble), 1, fp);
-    }
+    gdouble a = GFS_VARIABLE (cell, GFS_VARIABLE1 (variables->data)->i);
+    fwrite (&a, sizeof (gdouble), 1, fp);
     variables = variables->next;
   }
 }
@@ -2068,7 +2123,7 @@ void gfs_cell_read_binary (FttCell * cell, GtsFile * fp, GfsDomain * domain)
 {
   gdouble s0;
   GfsStateVector * s;
-  GfsVariable * v;
+  GSList * i;
 
   g_return_if_fail (cell != NULL);
   g_return_if_fail (fp != NULL);
@@ -2099,8 +2154,9 @@ void gfs_cell_read_binary (FttCell * cell, GtsFile * fp, GfsDomain * domain)
     }
   }
 
-  v = domain->variables_io;
-  while (v) {
+  i = domain->variables_io;
+  while (i) {
+    GfsVariable * v = i->data;
     gdouble a;
 
     if (gts_file_read (fp, &a, sizeof (gdouble), 1) != 1) {
@@ -2108,76 +2164,60 @@ void gfs_cell_read_binary (FttCell * cell, GtsFile * fp, GfsDomain * domain)
       return;
     }
     GFS_VARIABLE (cell, v->i) = a;
-    v = v->next;
+    i = i->next;
   }
 }
 
+static void box_realloc (GfsBox * box, GfsDomain * domain)
+{
+  FttDirection d;
+
+  ftt_cell_traverse (box->root, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
+		     (FttCellTraverseFunc) gfs_cell_reinit, domain);
+  for (d = 0; d < FTT_CELLS; d++)
+    if (GFS_IS_BOUNDARY (box->neighbor[d]))
+      ftt_cell_traverse (GFS_BOUNDARY (box->neighbor[d])->root, 
+			 FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
+			 (FttCellTraverseFunc) gfs_cell_reinit, domain);
+}
+
 /**
- * gfs_domain_replace_variable:
+ * gfs_domain_alloc:
  * @domain: a #GfsDomain.
- * @v: the #GfsVariable to replace.
- * @with: the new #GfsVariable.
  *
- * Replaces existing variable @v with new variable @with.
+ * Returns: the index of a memory location newly allocated for each
+ * cell of @domain.
  */
-void gfs_domain_replace_variable (GfsDomain * domain,
-				  GfsVariable * v,
-				  GfsVariable * with)
+guint gfs_domain_alloc (GfsDomain * domain)
 {
-  GfsVariable * v1, * prev = NULL;
+  guint i = 0;
 
-  g_return_if_fail (domain != NULL);
-  g_return_if_fail (gts_container_size (GTS_CONTAINER (domain)) == 0);
-  g_return_if_fail (v != NULL);
-  g_return_if_fail (with != NULL);
+  g_return_val_if_fail (domain != NULL, -1);
 
-  v1 = domain->variables;
-  while (v1 && v1 != v) {
-    prev = v1;
-    v1 = v1->next;
+  while (i < domain->allocated->len && g_array_index (domain->allocated, gboolean, i))
+    i++;
+  if (i == domain->allocated->len) {
+    g_array_set_size (domain->allocated, domain->allocated->len + 1);
+    gts_container_foreach (GTS_CONTAINER (domain), (GtsFunc) box_realloc, domain);
   }
-  g_return_if_fail (v1 == v);
-  with->i = v->i;
-  v->i = -1;
-  with->p = GTS_OBJECT (domain);
-  v->p = NULL;
-  with->next = v->next;
-  v->next = NULL;
-  if (prev)
-    prev->next = with;
-  else
-    domain->variables = with;
+  g_array_index (domain->allocated, gboolean, i) = TRUE;
+  return i;
 }
 
 /**
- * gfs_domain_add_new_variable:
+ * gfs_domain_free:
  * @domain: a #GfsDomain.
- * @v: the #GfsVariable to add.
+ * @i: a memory location index previously allocated using gfs_domain_alloc().
  *
- * Adds a new variable @v to @domain.
+ * Frees the memory location of @domain defined by @i.
  */
-void gfs_domain_add_new_variable (GfsDomain * domain,
-				  GfsVariable * v)
+void gfs_domain_free (GfsDomain * domain, guint i)
 {
-  GfsVariable * v1, * last;
-
   g_return_if_fail (domain != NULL);
-  g_return_if_fail (gts_container_size (GTS_CONTAINER (domain)) == 0);
-  g_return_if_fail (v != NULL);
-  g_return_if_fail (v->name == NULL || 
-		    gfs_variable_from_name (domain->variables, v->name) == NULL);
+  g_return_if_fail (i < domain->allocated->len);
+  g_return_if_fail (g_array_index (domain->allocated, gboolean, i));
 
-  v1 = last = domain->variables;
-  while (v1) {
-    last = v1;
-    v1 = v1->next;
-  }
-  g_assert (last);
-
-  last->next = v;
-  v->i = last->i + 1;
-  v->p = GTS_OBJECT (domain);
-  domain->variables_size += sizeof (gdouble);
+  g_array_index (domain->allocated, gboolean, i) = FALSE;
 }
 
 /**
@@ -2190,30 +2230,28 @@ void gfs_domain_add_new_variable (GfsDomain * domain,
  * Returns: the new variable or %NULL if a variable with the same name
  * already exists.  
  */
-GfsVariable * gfs_domain_add_variable (GfsDomain * domain, 
+GfsVariable * gfs_domain_add_variable (GfsDomain * domain,
 				       const gchar * name)
 {
   GfsVariable * v;
 
   g_return_val_if_fail (domain != NULL, NULL);
-  g_return_val_if_fail (gts_container_size (GTS_CONTAINER (domain)) == 0, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
 
-  if (name && gfs_variable_from_name (domain->variables, name))
+  if ((v = gfs_variable_new (gfs_variable_class (), domain, name)) == NULL)
     return NULL;
-
-  v = GFS_VARIABLE1 (gts_object_new (GTS_OBJECT_CLASS (gfs_variable_class ())));
-  v->name = g_strdup (name);
-  gfs_domain_add_new_variable (domain, v);
-
+  domain->variables = g_slist_append (domain->variables, v);
   return v;
 }
 
-static void add_pressure_force (FttCell * cell, gdouble * f)
+static void add_pressure_force (FttCell * cell, gpointer * data)
 {
+  gdouble * f = data[0];
+  GfsVariable * p = data[1];
   FttVector ff;
   FttComponent c;
 
-  gfs_pressure_force (cell, &ff);
+  gfs_pressure_force (cell, p, &ff);
   for (c = 0; c < FTT_DIMENSION; c++)
     f[c] += (&ff.x)[c];
 }
@@ -2250,7 +2288,7 @@ static void add_viscous_force (FttCell * cell, gpointer * data)
   n.x = s->s[1] - s->s[0];
   n.y = s->s[3] - s->s[2];
 #if FTT_2D
-  switch (GFS_VELOCITY_COMPONENT (v->i)) {
+  switch (v->component) {
   case FTT_X:
     f->x -= D*(2.*g.x*n.x + g.y*n.y);
     f->y -= D*g.y*n.x;
@@ -2265,7 +2303,7 @@ static void add_viscous_force (FttCell * cell, gpointer * data)
 #else /* 3D */
   n.z = s->s[5] - s->s[4];
   D *= ftt_cell_size (cell);
-  switch (GFS_VELOCITY_COMPONENT (v->i)) {
+  switch (v->component) {
   case FTT_X:
     f->x -= D*(2.*g.x*n.x + g.y*n.y + g.z*n.z);
     f->y -= D*g.y*n.x;
@@ -2302,49 +2340,52 @@ void gfs_domain_solid_force (GfsDomain * domain,
 			     FttVector * vf)
 {
   FttComponent c;
-  GfsVariable * v;
+  GfsVariable ** v;
+  gpointer data[2];
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (pf != NULL);
   g_return_if_fail (vf != NULL);
 
   pf->x = pf->y = pf->z = 0.;
+  data[0] = pf;
+  data[1] = gfs_variable_from_name (domain->variables, "P");
   gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-			     (FttCellTraverseFunc) add_pressure_force, pf);
+			     (FttCellTraverseFunc) add_pressure_force, data);
 
   vf->x = vf->y = vf->z = 0.;
-  v = gfs_variable_from_name (domain->variables, "U");
-  for (c = 0; c < FTT_DIMENSION; c++, v = v->next) {
-    GfsSourceDiffusion * D = source_diffusion (v);
+  v = gfs_domain_velocity (domain);
+  for (c = 0; c < FTT_DIMENSION; c++) {
+    GfsSourceDiffusion * D = source_diffusion (v[c]);
 
     if (D) {
       gpointer data[3];
 
-      gfs_domain_surface_bc (domain, v);
+      gfs_domain_surface_bc (domain, v[c]);
       data[0] = vf;
-      data[1] = v;
+      data[1] = v[c];
       data[2] = D;
       gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-				 (FttCellTraverseFunc) add_viscous_force, 
-				 data);
+				 (FttCellTraverseFunc) add_viscous_force, data);
     }
   }
 }
 
-static void tag_cell_fraction (FttCell * cell, GfsVariable * c, guint tag, guint * size)
+static void tag_cell_fraction (FttCell * cell,
+			       GfsVariable * c, GfsVariable * v,
+			       guint tag, guint * size)
 {
   FttDirection d;
   FttCellNeighbors n;
 
   g_assert (FTT_CELL_IS_LEAF (cell));
-  GFS_STATE (cell)->div = tag;
+  GFS_VARIABLE (cell, v->i) = tag;
   (*size)++;
   ftt_cell_neighbors (cell, &n);
   for (d = 0; d < FTT_NEIGHBORS; d++)
-    if (n.c[d] && GFS_STATE (n.c[d])->div == 0. &&
-	GFS_VARIABLE (n.c[d], c->i) > 1e-4) {
+    if (n.c[d] && GFS_VARIABLE (n.c[d], v->i) == 0. && GFS_VARIABLE (n.c[d], c->i) > 1e-4) {
       if (FTT_CELL_IS_LEAF (n.c[d]))
-	tag_cell_fraction (n.c[d], c, tag, size);
+	tag_cell_fraction (n.c[d], c, v, tag, size);
       else {
 	FttCellChildren child;
 	FttDirection od = FTT_OPPOSITE_DIRECTION (d);
@@ -2355,21 +2396,23 @@ static void tag_cell_fraction (FttCell * cell, GfsVariable * c, guint tag, guint
 #endif	
 	ftt_cell_children_direction (n.c[d], od, &child);
 	for (i = 0; i < FTT_CELLS/2; i++)
-	  if (child.c[i] && GFS_STATE (child.c[i])->div == 0. &&
+	  if (child.c[i] && GFS_VARIABLE (child.c[i], v->i) == 0. &&
 	      GFS_VARIABLE (child.c[i], c->i) > 1e-4)
-	    tag_cell_fraction (child.c[i], c, tag, size);
+	    tag_cell_fraction (child.c[i], c, v, tag, size);
       }
     }
 }
 
 static void tag_new_fraction_region (FttCell * cell, gpointer * data)
 {
-  if (GFS_STATE (cell)->div == 0.) {
+  GfsVariable * v = data[3];
+
+  if (GFS_VARIABLE (cell, v->i) == 0.) {
     GfsVariable * c = data[0];
     GArray * sizes = data[1];
     guint size = 0;
     
-    tag_cell_fraction (cell, c, sizes->len + 1, &size);
+    tag_cell_fraction (cell, c, v, sizes->len + 1, &size);
     g_array_append_val (sizes, size);
   }
 }
@@ -2378,9 +2421,11 @@ static void reset_small_fraction (FttCell * cell, gpointer * data)
 {
   GfsVariable * c = data[0];
   GArray * sizes = data[1];
-  guint * min = data[2], i = GFS_STATE (cell)->div - 1.;
+  guint * min = data[2];
+  GfsVariable * v = data[3];
+  guint i = GFS_VARIABLE (cell, v->i) - 1.;
   
-  g_assert (GFS_STATE (cell)->div > 0.);
+  g_assert (GFS_VARIABLE (cell, v->i) > 0.);
   if (g_array_index (sizes, guint, i) < *min)
     GFS_VARIABLE (cell, c->i) = 0.;
 }
@@ -2404,18 +2449,21 @@ void gfs_domain_remove_droplets (GfsDomain * domain,
 				 GfsVariable * c,
 				 gint min)
 {
+  GfsVariable * v;
   GArray * sizes;
-  gpointer data[3];
+  gpointer data[4];
   guint minsize;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (c != NULL);
 
+  v = gfs_temporary_variable (domain);
   sizes = g_array_new (FALSE, FALSE, sizeof (guint));
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_div);
+			    (FttCellTraverseFunc) gfs_cell_reset, v);
   data[0] = c;
   data[1] = sizes;
+  data[3] = v;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) tag_new_fraction_region, data);
   g_assert (sizes->len > 0);
@@ -2434,24 +2482,25 @@ void gfs_domain_remove_droplets (GfsDomain * domain,
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) reset_small_fraction, data);
   g_array_free (sizes, TRUE);
+  gts_object_destroy (GTS_OBJECT (v));
 }
 
-static void tag_cell (FttCell * cell, guint tag, guint * size)
+static void tag_cell (FttCell * cell, GfsVariable * v, guint tag, guint * size)
 {
   FttDirection d;
   FttCellNeighbors n;
   GfsSolidVector * solid = GFS_STATE (cell)->solid;
 
   g_assert (FTT_CELL_IS_LEAF (cell));
-  GFS_STATE (cell)->div = tag;
+  GFS_VARIABLE (cell, v->i) = tag;
   (*size)++;
   ftt_cell_neighbors (cell, &n);
   for (d = 0; d < FTT_NEIGHBORS; d++)
-    if (n.c[d] && GFS_STATE (n.c[d])->div == 0. &&
+    if (n.c[d] && GFS_VARIABLE (n.c[d], v->i) == 0. &&
 	!GFS_CELL_IS_BOUNDARY (n.c[d]) &&
 	(!solid || solid->s[d] > 0.)) {
       if (FTT_CELL_IS_LEAF (n.c[d]))
-	tag_cell (n.c[d], tag, size);
+	tag_cell (n.c[d], v, tag, size);
       else {
 	FttCellChildren child;
 	FttDirection od = FTT_OPPOSITE_DIRECTION (d);
@@ -2459,19 +2508,22 @@ static void tag_cell (FttCell * cell, guint tag, guint * size)
 	
 	j = ftt_cell_children_direction (n.c[d], od, &child);
 	for (i = 0; i < j; i++)
-	  if (child.c[i] && GFS_STATE (child.c[i])->div == 0. &&
+	  if (child.c[i] && GFS_VARIABLE (child.c[i], v->i) == 0. &&
 	      (!GFS_IS_MIXED (child.c[i]) || GFS_STATE (child.c[i])->solid->s[od] > 0.))
-	    tag_cell (child.c[i], tag, size);
+	    tag_cell (child.c[i], v, tag, size);
       }
     }
 }
 
-static void tag_new_region (FttCell * cell, GArray * sizes)
+static void tag_new_region (FttCell * cell, gpointer * data)
 {
-  if (GFS_STATE (cell)->div == 0.) {
+  GfsVariable * v = data[0];
+
+  if (GFS_VARIABLE (cell, v->i) == 0.) {
+    GArray * sizes = data[1];
     guint size = 0;
 
-    tag_cell (cell, sizes->len + 1, &size);
+    tag_cell (cell, v, sizes->len + 1, &size);
     g_array_append_val (sizes, size);
   }
 }
@@ -2480,9 +2532,10 @@ static gboolean remove_small (FttCell * cell, gpointer * data)
 {
   if (FTT_CELL_IS_LEAF (cell)) {
     GArray * sizes = data[0];
-    guint * min = data[1], i = GFS_STATE (cell)->div - 1.;
+    GfsVariable * v = data[5];
+    guint * min = data[1], i = GFS_VARIABLE (cell, v->i) - 1.;
 
-    g_assert (GFS_STATE (cell)->div > 0.);
+    g_assert (GFS_VARIABLE (cell, v->i) > 0.);
     if (g_array_index (sizes, guint, i) < *min) {
       if (FTT_CELL_IS_ROOT (cell))
 	g_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "root cell belongs to a pond");
@@ -2545,14 +2598,18 @@ void gfs_domain_remove_ponds (GfsDomain * domain,
   gpointer dat[5];
   guint minsize;
   gboolean changed = FALSE;
+  GfsVariable * v;
 
   g_return_if_fail (domain != NULL);
 
+  v = gfs_temporary_variable (domain);
   sizes = g_array_new (FALSE, FALSE, sizeof (guint));
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_div);
+			    (FttCellTraverseFunc) gfs_cell_reset, v);
+  dat[0] = v;
+  dat[1] = sizes;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) tag_new_region, sizes);
+			    (FttCellTraverseFunc) tag_new_region, dat);
   g_assert (sizes->len > 0);
   if (min >= 0)
     minsize = min;
@@ -2570,15 +2627,17 @@ void gfs_domain_remove_ponds (GfsDomain * domain,
   dat[2] = cleanup;
   dat[3] = data;
   dat[4] = &changed;
+  dat[5] = v;
   gts_container_foreach (GTS_CONTAINER (domain), (GtsFunc) remove_small_box, dat);
   g_array_free (sizes, TRUE);
+  gts_object_destroy (GTS_OBJECT (v));
   if (changed)
     gfs_domain_match (domain);
 }
 
-static gboolean tag_speck (FttCell * cell)
+static gboolean tag_speck (FttCell * cell, GfsVariable * v)
 {
-  if (GFS_STATE (cell)->div == 0.) {
+  if (GFS_VARIABLE (cell, v->i) == 0.) {
     FttDirection d;
     FttCellNeighbors n;
     GfsSolidVector * solid = GFS_STATE (cell)->solid;
@@ -2588,15 +2647,15 @@ static gboolean tag_speck (FttCell * cell)
     for (d = 0; d < FTT_NEIGHBORS; d++)
       if (!n.c[d])
 	return FALSE;
-    GFS_STATE (cell)->div = 1.;
+    GFS_VARIABLE (cell, v->i) = 1.;
     for (d = 0; d < FTT_NEIGHBORS; d++)
-      if (GFS_STATE (n.c[d])->div == 0. && 
+      if (GFS_VARIABLE (n.c[d], v->i) == 0. && 
 	  !GFS_CELL_IS_BOUNDARY (n.c[d]) &&
 	  solid->s[d] > 0. && solid->s[d] < 1.) {
 	g_assert (GFS_IS_MIXED (n.c[d]));
 	if (FTT_CELL_IS_LEAF (n.c[d])) {
-	  if (!tag_speck (n.c[d])) {
-	    GFS_STATE (cell)->div = 0.;
+	  if (!tag_speck (n.c[d], v)) {
+	    GFS_VARIABLE (cell, v->i) = 0.;
 	    return FALSE;
 	  }
 	}
@@ -2610,10 +2669,10 @@ static gboolean tag_speck (FttCell * cell)
 #endif	
 	  ftt_cell_children_direction (n.c[d], od, &child);
 	  for (i = 0; i < FTT_CELLS/2; i++)
-	    if (!child.c[i] || (GFS_STATE (child.c[i])->div == 0 && 
+	    if (!child.c[i] || (GFS_VARIABLE (child.c[i], v->i) == 0 && 
 				GFS_IS_MIXED (child.c[i]) &&
-				!tag_speck (child.c[i]))) {
-	      GFS_STATE (cell)->div = 0.;
+				!tag_speck (child.c[i], v))) {
+	      GFS_VARIABLE (cell, v->i) = 0.;
 	      return FALSE;
 	    }
 	}
@@ -2622,9 +2681,12 @@ static gboolean tag_speck (FttCell * cell)
   return TRUE;
 }
 
-static void fill_speck (FttCell * cell, gboolean * changed)
+static void fill_speck (FttCell * cell, gpointer * data)
 {
-  if (GFS_STATE (cell)->div == 1.) {
+  GfsVariable * v = data[0];
+
+  if (GFS_VARIABLE (cell, v->i) == 1.) {
+    gboolean * changed = data[1];
     g_free (GFS_STATE (cell)->solid);
     GFS_STATE (cell)->solid = NULL;
     *changed = TRUE;
@@ -2643,15 +2705,21 @@ static void fill_speck (FttCell * cell, gboolean * changed)
 void gfs_domain_remove_specks (GfsDomain * domain)
 {
   gboolean changed = FALSE;
+  GfsVariable * v;
+  gpointer data[2];
 
   g_return_if_fail (domain != NULL);
 
+  v = gfs_temporary_variable (domain);
   gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL,
-			     (FttCellTraverseFunc) gfs_cell_reset, gfs_div);
+			     (FttCellTraverseFunc) gfs_cell_reset, v);
   gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-			     (FttCellTraverseFunc) tag_speck, NULL);
+			     (FttCellTraverseFunc) tag_speck, v);
+  data[0] = v;
+  data[1] = &changed;
   gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-			     (FttCellTraverseFunc) fill_speck, &changed);
+			     (FttCellTraverseFunc) fill_speck, data);
+  gts_object_destroy (GTS_OBJECT (v));
   if (changed)
     gfs_domain_cell_traverse (domain, FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
 			      (FttCellTraverseFunc) gfs_cell_init_solid_fractions_from_children, 
diff --git a/src/domain.h b/src/domain.h
index 652bdc8..ce1fa59 100644
--- a/src/domain.h
+++ b/src/domain.h
@@ -53,9 +53,13 @@ struct _GfsDomain {
   guint rootlevel;
   FttVector refpos;
   FttVector lambda;
-  GfsVariable * variables;
-  guint variables_size;
-  GfsVariable * variables_io;
+
+  GArray * allocated;
+  GSList * variables;
+
+  GfsVariable * velocity[FTT_DIMENSION];
+
+  GSList * variables_io;
   gboolean binary;
 
   gint max_depth_write;
@@ -75,6 +79,9 @@ struct _GfsDomainClass {
 						   gfs_domain_class())
 #define GFS_IS_DOMAIN(obj)         (gts_object_is_from_class (obj,\
 						   gfs_domain_class ()))
+
+#define gfs_domain_variables_size(d)   (sizeof (GfsStateVector) +\
+                                        sizeof (gdouble)*((d)->allocated->len - 1))
      
 GfsDomainClass * gfs_domain_class          (void);
 void         gfs_domain_cell_traverse         (GfsDomain * domain,
@@ -155,7 +162,9 @@ GfsNorm      gfs_domain_norm_variable         (GfsDomain * domain,
 GfsNorm      gfs_domain_norm_residual         (GfsDomain * domain,
 					       FttTraverseFlags flags,
 					       gint max_depth,
-					       gdouble dt);
+					       gdouble dt,
+					       GfsVariable * res);
+GfsVariable ** gfs_domain_velocity            (GfsDomain * domain);
 GfsNorm      gfs_domain_norm_velocity         (GfsDomain * domain,
 					       FttTraverseFlags flags,
 					       gint max_depth);
@@ -176,6 +185,8 @@ gdouble      gfs_domain_cfl                   (GfsDomain * domain,
 					       gint max_depth);
 void         gfs_cell_init                    (FttCell * cell,
 					       GfsDomain * domain);
+void         gfs_cell_reinit                  (FttCell * cell, 
+					       GfsDomain * domain);
 void         gfs_cell_copy                    (const FttCell * from, 
 					       FttCell * to,
 					       GfsDomain * domain);
@@ -184,18 +195,16 @@ void         gfs_cell_read                    (FttCell * cell,
 					       GfsDomain * domain);
 void         gfs_cell_write                   (const FttCell * cell, 
 					       FILE * fp,
-					       GfsVariable * variables);
+					       GSList * variables);
 void         gfs_cell_read_binary             (FttCell * cell, 
 					       GtsFile * fp,
 					       GfsDomain * domain);
 void         gfs_cell_write_binary            (const FttCell * cell, 
 					       FILE * fp,
-					       GfsVariable * variables);
-void         gfs_domain_replace_variable      (GfsDomain * domain,
-					       GfsVariable * v,
-					       GfsVariable * with);
-void         gfs_domain_add_new_variable      (GfsDomain * domain,
-					       GfsVariable * v);
+					       GSList * variables);
+guint        gfs_domain_alloc                 (GfsDomain * domain);
+void         gfs_domain_free                  (GfsDomain * domain, 
+					       guint i);
 GfsVariable * gfs_domain_add_variable         (GfsDomain * domain, 
 					       const gchar * name);
 void         gfs_domain_solid_force           (GfsDomain * domain, 
diff --git a/src/dx.c b/src/dx.c
index 9599bca..d5a0838 100644
--- a/src/dx.c
+++ b/src/dx.c
@@ -836,9 +836,10 @@ static void gfs_array_destroy (GfsArray * a, gboolean free_seg)
 static gint cell_index (FttCell * cell,
 			GfsArray * pos,
 			GPtrArray * data,
-			FttVector * lambda)
+			FttVector * lambda,
+			GfsVariable * index)
 {
-  if (GFS_STATE (cell)->dp <= 0.) {
+  if (GFS_VARIABLE (cell, index->i) <= 0.) {
     gfloat fp[3];
     FttVector p;
     guint i;
@@ -862,9 +863,9 @@ static gint cell_index (FttCell * cell,
     fp[1] = p.y/lambda->y;
     fp[2] = p.z/lambda->z;
     gfs_array_add (pos, fp);
-    GFS_STATE (cell)->dp = pos->i;
+    GFS_VARIABLE (cell, index->i) = pos->i;
   }
-  return GFS_STATE (cell)->dp - 1.;
+  return GFS_VARIABLE (cell, index->i) - 1.;
 }
 
 #if FTT_2D
@@ -875,15 +876,16 @@ static void add_cell (FttCell * cell, gpointer * par)
   GfsArray * con = par[1];
   GPtrArray * data = par[2];
   FttVector * lambda = par[3];
+  GfsVariable * iv = par[4];
   Edge e[12];
   guint i, len;
   gint index[3];
 
   len = cell_edges (cell, e);
-  index[0] = cell_index (cell, pos, data, lambda);
+  index[0] = cell_index (cell, pos, data, lambda, iv);
   for (i = 0; i < len; i++) {
-    index[1] = cell_index (e[i].c1, pos, data, lambda);
-    index[2] = cell_index (e[i].c2, pos, data, lambda);
+    index[1] = cell_index (e[i].c1, pos, data, lambda, iv);
+    index[2] = cell_index (e[i].c2, pos, data, lambda, iv);
     gfs_array_add (con, index);
   }
 }
@@ -896,16 +898,17 @@ static void add_cell (FttCell * cell, gpointer * par)
   GfsArray * con = par[1];
   GPtrArray * data = par[2];
   FttVector * lambda = par[3];
+  GfsVariable * iv = par[4];
   Face f[24];
   guint i, len;
   gint index[4];
 
   len = cell_faces (cell, f);
-  index[0] = cell_index (cell, pos, data, lambda);
+  index[0] = cell_index (cell, pos, data, lambda, iv);
   for (i = 0; i < len; i++) {
-    index[1] = cell_index (f[i].c1, pos, data, lambda);
-    index[2] = cell_index (f[i].c2, pos, data, lambda);
-    index[3] = cell_index (f[i].c3, pos, data, lambda);
+    index[1] = cell_index (f[i].c1, pos, data, lambda, iv);
+    index[2] = cell_index (f[i].c2, pos, data, lambda, iv);
+    index[3] = cell_index (f[i].c3, pos, data, lambda, iv);
     gfs_array_add (con, index);
   }
 }
@@ -952,14 +955,15 @@ Error m_ImportGfs3D (Object * in, Object * out)
   GtsFile * f = NULL;
   GfsSimulation * sim = NULL;
   GfsDomain * domain;
-  gpointer par[4];
+  gpointer par[5];
   GfsArray * pos = NULL, * con = NULL;
   GPtrArray * data = NULL;
   Group group = NULL;
-  GfsVariable * v;
+  GSList * j;
   guint i;
   gboolean pos_used = FALSE, con_used = FALSE;
   Field solid = NULL;
+  GfsVariable * iv;
 
   /* extract the file name from in[0] */
   if (!in[0]) {
@@ -998,10 +1002,10 @@ Error m_ImportGfs3D (Object * in, Object * out)
   domain = GFS_DOMAIN (sim);
     
   gfs_domain_match (domain);
-  v = domain->variables;
-  while (v) {
-    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v);
-    v = v->next;
+  j = domain->variables;
+  while (j) {
+    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, j->data);
+    j = j->next;
   }
 
   /* positions */
@@ -1014,31 +1018,32 @@ Error m_ImportGfs3D (Object * in, Object * out)
 
   /* data */
   data = g_ptr_array_new ();
-  v = domain->variables_io;
-  while (v) {
+  j = domain->variables;
+  while (j) {
+    GfsVariable * v = j->data;
     GfsVariable * vv[FTT_DIMENSION], * v1;
 
-    if (!strcmp (v->name, "U") && v->next &&
-	!strcmp (v->next->name, "V")
+    if (!strcmp (v->name, "U") && j->next &&
+	!strcmp (GFS_VARIABLE1 (j->next)->name, "V")
 #if (!FTT_2D)
-	&& v->next->next && !strcmp (v->next->next->name, "W")
+	&& j->next->next && !strcmp (GFS_VARIABLE1 (j->next->next)->name, "W")
 #endif /* not FTT_2D */
 	) {
-      vv[0] = v; v = v->next;
+      vv[0] = v; j = j->next; v = j->data;      
       vv[1] = v; 
 #if (!FTT_2D)
-      v = v->next;
+      j = j->next; v = j->data;
       vv[2] = v;
 #endif /* not FTT_2D */
       g_ptr_array_add (data, gfs_array_new (vv, "U", TYPE_FLOAT, 
 					    FTT_DIMENSION, TRUE));
     }
     else if (v->name[strlen (v->name) - 1] == 'x' &&
-	     (vv[0] = v) && (v1 = v->next) &&
+	     (vv[0] = v) && (j = j->next) && (v1 = j->data) &&
 	     v1->name[strlen (v1->name) - 1] == 'y' &&
 	     (vv[1] = v1) 
 #if (!FTT_2D)
-	     && (v1 = v1->next) &&
+	     && (j = j->next) && (v1 = j->data) &&
 	     v1->name[strlen (v1->name) - 1] == 'z' &&
 	     (vv[2] = v1)
 #endif /* not FTT_2D */
@@ -1051,19 +1056,22 @@ Error m_ImportGfs3D (Object * in, Object * out)
     }
     else 
       g_ptr_array_add (data, gfs_array_new (&v, v->name, TYPE_FLOAT, 1, TRUE));
-    v = v->next;
+    j = j->next;
   }
 
+  iv = gfs_temporary_variable (domain);
   gfs_domain_cell_traverse (domain,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_dp);
+			    (FttCellTraverseFunc) gfs_cell_reset, iv);
   par[0] = pos;
   par[1] = con;
   par[2] = data;
   par[3] = &domain->lambda;
+  par[4] = iv;
   gfs_domain_cell_traverse (domain,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) add_cell, par);
+  gts_object_destroy (GTS_OBJECT (iv));
 
   if (!(group = DXNewGroup ()))
     goto error;
diff --git a/src/event.c b/src/event.c
index 51f1439..02253d3 100644
--- a/src/event.c
+++ b/src/event.c
@@ -380,11 +380,15 @@ void gfs_event_init (GfsEvent * event,
  */
 void gfs_event_do (GfsEvent * event, GfsSimulation * sim)
 {
+  GfsEventClass * klass;
+
   g_return_if_fail (event != NULL);
   g_return_if_fail (sim != NULL);
 
-  g_assert (GFS_EVENT_CLASS (GTS_OBJECT (event)->klass)->event);
-  (* GFS_EVENT_CLASS (GTS_OBJECT (event)->klass)->event) (event, sim);
+  klass = GFS_EVENT_CLASS (GTS_OBJECT (event)->klass);
+  g_assert (klass->event);
+  if ((* klass->event) (event, sim) && klass->post_event)
+    (* klass->post_event) (event, sim);
 }
 
 /**
@@ -530,23 +534,16 @@ static void init_vf (FttCell * cell, gpointer * data)
 {
   GfsVariable * v = data[0];
   GfsFunction * f = data[1];
-  GfsSimulation * sim = data[2];
-  FttVector p;
 
-  if (v->centered)
-    ftt_cell_pos (cell, &p);
-  else
-    gfs_cell_cm (cell, &p);
-  GFS_VARIABLE (cell, v->i) = gfs_function_value (f, cell, &p, sim->time.t);
+  GFS_VARIABLE (cell, v->i) = gfs_function_value (f, cell);
 }
 
 static void init_f (GfsVariable * v, GfsFunction * f, GfsDomain * domain)
 {
-  gpointer data[3];
+  gpointer data[2];
   
   data[0] = v;
   data[1] = f;
-  data[2] = domain;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) init_vf, data);
 }
@@ -670,30 +667,35 @@ static void sum_volume (FttCell * cell, GtsRange * vol)
     gts_range_add_value (vol, size*size);
 }
 
-static void add_ddiv (FttCell * cell, gdouble * ddiv)
+static void add_ddiv (FttCell * cell, gpointer * data)
 {
+  gdouble * ddiv = data[0];
+  GfsVariable * div = data[1];
   gdouble size = ftt_cell_size (cell);
   
   if (GFS_IS_MIXED (cell))
-    GFS_STATE (cell)->div += size*size*GFS_STATE (cell)->solid->a*(*ddiv);
+    GFS_VARIABLE (cell, div->i) += size*size*GFS_STATE (cell)->solid->a*(*ddiv);
   else
-    GFS_STATE (cell)->div += size*size*(*ddiv);
+    GFS_VARIABLE (cell, div->i) += size*size*(*ddiv);
 }
 
-static void correct_div (GfsDomain * domain)
+static void correct_div (GfsDomain * domain, GfsVariable * v)
 {
   GtsRange div, vol;
   gdouble ddiv;
+  gpointer data[2];
 
-  div = gfs_domain_stats_variable (domain, gfs_div, FTT_TRAVERSE_LEAFS, -1);
+  div = gfs_domain_stats_variable (domain, v, FTT_TRAVERSE_LEAFS, -1);
   gts_range_init (&vol);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) sum_volume, &vol);
   gts_range_update (&vol);
   ddiv = - div.mean/vol.mean;
 
+  data[0] = &ddiv;
+  data[1] = v;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) add_ddiv, &ddiv);
+			    (FttCellTraverseFunc) add_ddiv, data);
 }
 
 static void stream_from_vorticity (GfsDomain * domain,
@@ -703,43 +705,44 @@ static void stream_from_vorticity (GfsDomain * domain,
 {
   GfsNorm norm;
   guint maxlevel, maxit = 100;
+  GfsVariable * res, * dia;
 
   g_return_if_fail (domain != NULL);
 
-  gfs_poisson_coefficients (domain, NULL, 1.);
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_gx);
-  correct_div (domain); /* enforce solvability condition */
+  dia = gfs_temporary_variable (domain);
+  gfs_poisson_coefficients (domain, dia, NULL, 1.);
+  correct_div (domain, vorticity); /* enforce solvability condition */
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) gfs_cell_reset, stream);
-  gfs_residual (domain, FTT_DIMENSION, FTT_TRAVERSE_LEAFS, -1, stream, vorticity, gfs_res);
-  norm = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, 1.);
+  res = gfs_temporary_variable (domain);
+  gfs_residual (domain, FTT_DIMENSION, FTT_TRAVERSE_LEAFS, -1, stream, vorticity, dia, res);
+  norm = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, 1., res);
   maxlevel = gfs_domain_depth (domain);
   while (norm.infty > tolerance && maxit) {
-    gfs_poisson_cycle (domain, FTT_DIMENSION, 0, maxlevel, 4, stream, vorticity);
-    norm = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, 1.);
+    gfs_poisson_cycle (domain, FTT_DIMENSION, 0, maxlevel, 4, stream, vorticity, dia, res);
+    norm = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, 1., res);
     maxit--;
   }
   if (maxit == 0)
     g_warning ("GfsInitVorticity: cannot solve streamfunction from vorticity\n"
 	       "  (residual: %g)", norm.infty);
+  gts_object_destroy (GTS_OBJECT (res));
+  gts_object_destroy (GTS_OBJECT (dia));
 }
 
-static void init_from_streamfunction (FttCell * cell, GfsVariable * stream)
+static void init_from_streamfunction (FttCell * cell, GfsInitVorticity * init)
 {
   gdouble size = ftt_cell_size (cell);
 
-  GFS_STATE (cell)->u = - gfs_center_gradient (cell, FTT_Y, stream->i)/size;
-  GFS_STATE (cell)->v = gfs_center_gradient (cell, FTT_X, stream->i)/size;
+  GFS_VARIABLE (cell, init->u[0]->i) = - gfs_center_gradient (cell, FTT_Y, init->stream->i)/size;
+  GFS_VARIABLE (cell, init->u[1]->i) = gfs_center_gradient (cell, FTT_X, init->stream->i)/size;
 }
 
 static void compute_vorticity (FttCell * cell, GfsInitVorticity * init)
 {
-  FttVector p;
   gdouble size = ftt_cell_size (cell);
 
-  gfs_cell_cm (cell, &p);
-  GFS_STATE (cell)->div = gfs_function_value (init->f, cell, &p, 0.)*size*size;  
+  GFS_VARIABLE (cell, init->vort->i) = gfs_function_value (init->f, cell)*size*size;  
 }
 
 static gboolean gfs_init_vorticity_event (GfsEvent * event, 
@@ -747,12 +750,20 @@ static gboolean gfs_init_vorticity_event (GfsEvent * event,
 {
   if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (gfs_init_vorticity_class ())->parent_class)->event) 
       (event, sim)) {
-    gfs_domain_cell_traverse (GFS_DOMAIN (sim), FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
+    GfsInitVorticity * init = GFS_INIT_VORTICITY (event);
+    GfsDomain * domain = GFS_DOMAIN (sim);
+
+    init->vort = gfs_temporary_variable (domain);
+    init->stream = gfs_temporary_variable (domain);
+    gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			      (FttCellTraverseFunc) compute_vorticity, event);
-    stream_from_vorticity (GFS_DOMAIN (sim), gfs_gy, gfs_div, 1e-9);
-    gfs_domain_cell_traverse (GFS_DOMAIN (sim), 
+    stream_from_vorticity (domain, init->stream, init->vort, 1e-9);
+    gts_object_destroy (GTS_OBJECT (init->vort));
+    init->u = gfs_domain_velocity (domain);
+    gfs_domain_cell_traverse (domain, 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttCellTraverseFunc) init_from_streamfunction, gfs_gy);
+			      (FttCellTraverseFunc) init_from_streamfunction, init);
+    gts_object_destroy (GTS_OBJECT (init->stream));
     return TRUE;
   }
   return FALSE;
diff --git a/src/event.h b/src/event.h
index 0ffd5a5..0caf12b 100644
--- a/src/event.h
+++ b/src/event.h
@@ -45,6 +45,7 @@ struct _GfsEventClass {
   GtsSListContaineeClass parent_class;
 
   gboolean (* event)      (GfsEvent * event, GfsSimulation * sim);
+  void     (* post_event) (GfsEvent * event, GfsSimulation * sim);
   void     (* event_half) (GfsEvent * event, GfsSimulation * sim);
 };
 
@@ -117,6 +118,7 @@ typedef struct _GfsInitVorticity         GfsInitVorticity;
 struct _GfsInitVorticity {
   /*< private >*/
   GfsGenericInit parent;
+  GfsVariable * vort, * stream, ** u;
 
   /*< public >*/
   GfsFunction * f;
diff --git a/src/fluid.c b/src/fluid.c
index 19865bf..d122c27 100644
--- a/src/fluid.c
+++ b/src/fluid.c
@@ -26,18 +26,6 @@
 #include "domain.h"
 #include "solid.h"
 
-/* Permanent variables */
-GfsVariable * gfs_div, * gfs_dp, * gfs_res;
-GfsVariable * gfs_gx, * gfs_gy;
-#if (!FTT_2D)
-GfsVariable * gfs_gz;
-#endif /* FTT_3D */
-GfsVariable * gfs_centered_variables;
-GfsVariable * gfs_p;
-
-/* Derived variables */
-GfsVariable * gfs_derived_first, * gfs_derived_last;
-
 typedef struct _Gradient Gradient;
 
 /* grad(p) = -a*p(cell) + b*p(neighbor) + c */
@@ -1556,16 +1544,19 @@ gdouble gfs_face_interpolated_value (const FttCellFace * face,
 /**
  * gfs_normal_divergence:
  * @cell: a #FttCell.
+ * @v: a #GfsVariable.
  *
- * Fills variable %GFS_DIV of @cell with the integral of the divergence
+ * Fills variable @v of @cell with the integral of the divergence
  * of the (MAC) velocity field in this cell.  
  */
-void gfs_normal_divergence (FttCell * cell)
+void gfs_normal_divergence (FttCell * cell,
+			    GfsVariable * v)
 {
   FttComponent c;
   gdouble div = 0.;
 
   g_return_if_fail (cell != NULL);
+  g_return_if_fail (v != NULL);
 
   if (GFS_IS_MIXED (cell)) {
     GfsSolidVector * solid = GFS_STATE (cell)->solid;
@@ -1584,22 +1575,25 @@ void gfs_normal_divergence (FttCell * cell)
       div += (GFS_STATE (cell)->f[d].un - 
 	      GFS_STATE (cell)->f[d + 1].un);
     }
-  GFS_STATE (cell)->div = div*ftt_cell_size (cell);
+  GFS_VARIABLE (cell, v->i) = div*ftt_cell_size (cell);
 }
 
 /**
  * gfs_normal_divergence_2D:
  * @cell: a #FttCell.
+ * @v: a #GfsVariable.
  *
- * Fills variable %GFS_DIV of @cell with the integral of the 2D
+ * Fills variable @v of @cell with the integral of the 2D
  * divergence of the (MAC) velocity field in this cell.
  */
-void gfs_normal_divergence_2D (FttCell * cell)
+void gfs_normal_divergence_2D (FttCell * cell,
+			       GfsVariable * v)
 {
   FttComponent c;
   gdouble div = 0.;
 
   g_return_if_fail (cell != NULL);
+  g_return_if_fail (v != NULL);
 
   if (GFS_IS_MIXED (cell)) {
     GfsSolidVector * solid = GFS_STATE (cell)->solid;
@@ -1618,35 +1612,40 @@ void gfs_normal_divergence_2D (FttCell * cell)
       div += (GFS_STATE (cell)->f[d].un - 
 	      GFS_STATE (cell)->f[d + 1].un);
     }
-  GFS_STATE (cell)->div = div*ftt_cell_size (cell);
+  GFS_VARIABLE (cell, v->i) = div*ftt_cell_size (cell);
 }
 
 /**
  * gfs_divergence:
  * @cell: a #FttCell.
+ * @v: the components of the vector.
  *
- * Returns: the divergence of the (centered) velocity field in @cell.
+ * Returns: the divergence of the (centered) vector field @v in @cell.
  */
-gdouble gfs_divergence (FttCell * cell)
+gdouble gfs_divergence (FttCell * cell,
+			GfsVariable ** v)
 {
   FttComponent c;
   gdouble div = 0.;
 
   g_return_val_if_fail (cell != NULL, 0.);
+  g_return_val_if_fail (v != NULL, 0.);
 
   for (c = 0; c < FTT_DIMENSION; c++)
-    div += gfs_center_gradient (cell, c, GFS_VELOCITY_INDEX (c));
+    div += gfs_center_gradient (cell, c, v[c]->i);
   return div/ftt_cell_size (cell);
 }
 
 /**
  * gfs_vorticity:
  * @cell: a #FttCell.
+ * @v: the components of the vector.
  *
  * Returns: the vorticity (norm of the vorticity vector in 3D) of the
- * velocity field in @cell.
+ * vector field @v in @cell.
  */
-gdouble gfs_vorticity (FttCell * cell)
+gdouble gfs_vorticity (FttCell * cell,
+		       GfsVariable ** v)
 {
   gdouble size;
 #if (!FTT_2D)
@@ -1654,71 +1653,68 @@ gdouble gfs_vorticity (FttCell * cell)
 #endif /* FTT_3D */
 
   g_return_val_if_fail (cell != NULL, 0.);
+  g_return_val_if_fail (v != NULL, 0.);
 
   size = ftt_cell_size (cell);
 #if FTT_2D
-  return (gfs_center_gradient (cell, FTT_X, GFS_V) -
-	  gfs_center_gradient (cell, FTT_Y, GFS_U))/size;
+  return (gfs_center_gradient (cell, FTT_X, v[1]->i) -
+	  gfs_center_gradient (cell, FTT_Y, v[0]->i))/size;
 #else  /* FTT_3D */
-  vort.x = (gfs_center_gradient (cell, FTT_Y, GFS_W) -
-	    gfs_center_gradient (cell, FTT_Z, GFS_V))/size;
-  vort.y = (gfs_center_gradient (cell, FTT_Z, GFS_U) -
-	    gfs_center_gradient (cell, FTT_X, GFS_W))/size;
-  vort.z = (gfs_center_gradient (cell, FTT_X, GFS_V) -
-	    gfs_center_gradient (cell, FTT_Y, GFS_U))/size;
+  vort.x = (gfs_center_gradient (cell, FTT_Y, v[2]->i) -
+	    gfs_center_gradient (cell, FTT_Z, v[1]->i))/size;
+  vort.y = (gfs_center_gradient (cell, FTT_Z, v[0]->i) -
+	    gfs_center_gradient (cell, FTT_X, v[2]->i))/size;
+  vort.z = (gfs_center_gradient (cell, FTT_X, v[1]->i) -
+	    gfs_center_gradient (cell, FTT_Y, v[0]->i))/size;
   return sqrt (vort.x*vort.x + vort.y*vort.y + vort.z*vort.z);
 #endif /* FTT_3D */
 }
 
 /**
- * gfs_velocity_norm:
+ * gfs_vector_norm2:
  * @cell: a #FttCell.
+ * @v: the components of the vector.
  *
- * Returns: the norm of the velocity field in @cell.
+ * Returns: the squared norm of the vector field @v in @cell.
  */
-gdouble gfs_velocity_norm (FttCell * cell)
+gdouble gfs_vector_norm2 (FttCell * cell,
+			  GfsVariable ** v)
 {
-  GfsStateVector * s;
+  FttComponent c;
+  gdouble n = 0.;
   
   g_return_val_if_fail (cell != NULL, 0.);
+  g_return_val_if_fail (v != NULL, 0.);
 
-  s = GFS_STATE (cell);
-#if FTT_2D
-  return sqrt (s->u*s->u + s->v*s->v);
-#else  /* FTT_3D */
-  return sqrt (s->u*s->u + s->v*s->v + s->w*s->w);
-#endif /* FTT_3D */
+  for (c = 0; c < FTT_DIMENSION; c++)
+    n += GFS_VARIABLE (cell, v[c]->i)*GFS_VARIABLE (cell, v[c]->i);
+  return n;
 }
 
 /**
- * gfs_velocity_norm2:
+ * gfs_vector_norm:
  * @cell: a #FttCell.
+ * @v: the components of the vector.
  *
- * Returns: the squared norm of the velocity field in @cell.
+ * Returns: the norm of the vector field @v in @cell.
  */
-gdouble gfs_velocity_norm2 (FttCell * cell)
+gdouble gfs_vector_norm (FttCell * cell,
+			 GfsVariable ** v)
 {
-  GfsStateVector * s;
-
-  g_return_val_if_fail (cell != NULL, 0.);
-
-  s = GFS_STATE (cell);
-#if FTT_2D
-  return s->u*s->u + s->v*s->v;
-#else  /* FTT_3D */
-  return s->u*s->u + s->v*s->v + s->w*s->w;
-#endif /* FTT_3D */
+  return sqrt (gfs_vector_norm2 (cell, v));
 }
 
 /**
- * gfs_velocity_lambda2:
+ * gfs_vector_lambda2:
  * @cell: a #FttCell.
+ * @v: the components of the vector.
  *
  * Returns: The value of the lambda2 eigenvalue used by Jeong and
  * Hussain as vortex criterion (JFM 285, 69-94, 1995), normalized by
  * the square of the size of the cell.
  */
-gdouble gfs_velocity_lambda2 (FttCell * cell)
+gdouble gfs_vector_lambda2 (FttCell * cell,
+			    GfsVariable ** v)
 {
   gdouble J[FTT_DIMENSION][FTT_DIMENSION];
   gdouble S2O2[FTT_DIMENSION][FTT_DIMENSION];
@@ -1726,10 +1722,11 @@ gdouble gfs_velocity_lambda2 (FttCell * cell)
   guint i, j, k;
 
   g_return_val_if_fail (cell != NULL, 0.);
+  g_return_val_if_fail (v != NULL, 0.);
 
   for (i = 0; i < FTT_DIMENSION; i++)
     for (j = 0; j < FTT_DIMENSION; j++)
-      J[i][j] = gfs_center_gradient (cell, j, GFS_VELOCITY_INDEX (i));
+      J[i][j] = gfs_center_gradient (cell, j, v[i]->i);
   for (i = 0; i < FTT_DIMENSION; i++)
     for (j = 0; j < FTT_DIMENSION; j++) {
       S2O2[i][j] = 0.;
@@ -1743,26 +1740,29 @@ gdouble gfs_velocity_lambda2 (FttCell * cell)
 /**
  * gfs_pressure_force:
  * @cell: a #FttCell.
+ * @p: a #GfsVariable.
  * @f: a #FttVector.
  *
- * Fills @f with the pressure component of the force exerted by the
+ * Fills @f with the pressure @p component of the force exerted by the
  * fluid on the fraction of embedded boundary contained in @cell.
  */
 void gfs_pressure_force (FttCell * cell,
+			 GfsVariable * p,
 			 FttVector * f)
 {
   GfsSolidVector * s;
 
   g_return_if_fail (cell != NULL);
+  g_return_if_fail (p != NULL);
   g_return_if_fail (f != NULL);
 
   if ((s = GFS_STATE (cell)->solid)) {
-    gdouble p = gfs_cell_dirichlet_value (cell, gfs_p, -1);
+    gdouble pv = gfs_cell_dirichlet_value (cell, p, -1);
     FttComponent c;
 
     gfs_solid_normal (cell, f);
     for (c = 0; c < FTT_DIMENSION; c++)
-      (&f->x)[c] *= p;
+      (&f->x)[c] *= pv;
   }
   else
     f->x = f->y = f->z = 0.;
@@ -2060,25 +2060,22 @@ gdouble gfs_center_curvature (FttCell * cell,
 /**
  * gfs_streamline_curvature:
  * @cell: a #FttCell.
+ * @v: the components of the vector.
  *
  * The curvature is normalized by the size of the cell.
  *
- * Returns: the value of the curvature of the streamline passing
+ * Returns: the value of the curvature of the streamline defined by @v passing
  * through the center of the cell.
  */
-gdouble gfs_streamline_curvature (FttCell * cell)
+gdouble gfs_streamline_curvature (FttCell * cell,
+				  GfsVariable ** v)
 {
-  GfsStateVector * s;
   gdouble u2;
 
   g_return_val_if_fail (cell != NULL, 0.);
+  g_return_val_if_fail (v != NULL, 0.);
 
-  s = GFS_STATE (cell);
-#if FTT_2D
-  u2 = s->u*s->u + s->v*s->v;
-#else  /* FTT_3D */
-  u2 = s->u*s->u + s->v*s->v + s->w*s->w;
-#endif /* FTT_3D */
+  u2 = gfs_vector_norm2 (cell, v);
 
   if (u2 == 0.)
     return 0.;
@@ -2091,58 +2088,13 @@ gdouble gfs_streamline_curvature (FttCell * cell)
       gdouble ugui = 0.;
 
       for (j = 0; j < FTT_DIMENSION; j++)
-	ugui += GFS_VARIABLE (cell, GFS_VELOCITY_INDEX (j))*
-	  gfs_center_gradient (cell, j, GFS_VELOCITY_INDEX (i));
+	ugui += GFS_VARIABLE (cell, v[j]->i)*gfs_center_gradient (cell, j, v[i]->i);
       ugu += ugui*ugui;
     }
     return sqrt (ugu)/u2;
   }
 }
 
-/**
- * gfs_cell_laplacian:
- * @cell: a #FttCell.
- * @v: a #GfsVariable.
- *
- * Returns: an evaluation of the Laplacian of @v at the center of
- * @cell normalized by h^2, where h is the cell size.
- */
-gdouble gfs_cell_laplacian (FttCell * cell, GfsVariable * v)
-{
-  FttCellFace f;
-  FttCellNeighbors n;
-  GfsGradient g = { 0., 0. };
-  FttComponent c;
-  gdouble v0;
-
-  g_return_val_if_fail (cell != NULL, 0.);
-  g_return_val_if_fail (v != NULL, 0.);
-
-  if (GFS_IS_MIXED (cell))
-    return 0.;
-
-  c = GFS_VELOCITY_COMPONENT (v->i);
-
-  v0 = GFS_VARIABLE (cell, v->i);
-  f.cell = cell;
-  ftt_cell_neighbors (cell, &n);
-  for (f.d = 0; f.d < FTT_NEIGHBORS; f.d++)
-    if (n.c[f.d]) {
-      GfsGradient e;
-
-      f.neighbor = n.c[f.d];
-      gfs_face_gradient (&f, &e, v->i, -1);
-      g.a += e.a;
-      g.b += e.b;
-    }
-    else if (f.d/2 == c) {
-      g.a += 1.;
-      g.b -= v0;
-    }
-
-  return g.b - g.a*v0;
-}
-
 static FttCell * cell_corner_neighbor (FttCell * cell,
 				       FttDirection * d,
 				       gint max_level,
diff --git a/src/fluid.h b/src/fluid.h
index c02f6f0..1ad41f0 100644
--- a/src/fluid.h
+++ b/src/fluid.h
@@ -47,36 +47,9 @@ struct _GfsStateVector {
   /* solid boundaries */
   GfsSolidVector * solid;
 
-  /* centered temporary variables */
-  gdouble div, dp, res;
-  gdouble g[FTT_DIMENSION];
-
-  /* centered primitive variables */
-  gdouble p;
-  gdouble u, v;
-#if (!FTT_2D)
-  gdouble w;
-#endif /* FTT_3D */
+  gdouble place_holder;
 };
 
-typedef enum {
-  /* centered temporary variables */
-  GFS_DIV = 0,
-  GFS_DP,
-  GFS_RES,
-  GFS_GX,
-  GFS_GY,
-#if (!FTT_2D)
-  GFS_GZ,
-#endif /* FTT_3D */
-  /* centered primitive variables */
-  GFS_P,
-  GFS_U, GFS_V,
-#if (!FTT_2D)
-  GFS_W,
-#endif /* FTT_3D */
-} GfsPermanentVariable;
-
 struct _GfsSolidVector {
   gdouble s[FTT_NEIGHBORS];
   gdouble a, v, fv;
@@ -91,24 +64,8 @@ typedef enum {
   GFS_FLAG_USER =            FTT_FLAG_USER + 3 /* user flags start here */
 } GfsFlags;
 
-/* Permanent variables: defined in fluid.c */
-GTS_C_VAR GfsVariable * gfs_div, * gfs_dp, * gfs_res;
-GTS_C_VAR GfsVariable * gfs_gx, * gfs_gy;
-#if (!FTT_2D)
-GTS_C_VAR GfsVariable * gfs_gz;
-#endif /* FTT_3D */
-GTS_C_VAR GfsVariable * gfs_centered_variables;
-GTS_C_VAR GfsVariable * gfs_p;
-
-/* Derived variables: defined in fluid.c */
-GTS_C_VAR GfsVariable * gfs_derived_first, * gfs_derived_last;
-
 #define GFS_STATE(cell)               ((GfsStateVector *) (cell)->data)
-#define GFS_VARIABLE(cell, index)     ((&GFS_STATE (cell)->div)[index])
-#define GFS_VELOCITY_INDEX(component) (GFS_U + (component))
-#define GFS_GRADIENT_INDEX(component) (GFS_GX + (component))
-#define GFS_VELOCITY_COMPONENT(index) ((index) - GFS_U)
-#define GFS_GRADIENT_COMPONENT(index) ((index) - GFS_GX)
+#define GFS_VARIABLE(cell, index)     ((&GFS_STATE (cell)->place_holder)[index])
 
 #define GFS_FACE_NORMAL_VELOCITY(fa)\
   (GFS_STATE ((fa)->cell)->f[(fa)->d].un)
@@ -187,14 +144,22 @@ void                  gfs_face_gradient_flux_centered(const FttCellFace * face,
 						      guint v,
 						      gint max_level);
 
-void                  gfs_normal_divergence          (FttCell * cell);
-void                  gfs_normal_divergence_2D       (FttCell * cell);
-gdouble               gfs_divergence                 (FttCell * cell);
-gdouble               gfs_vorticity                  (FttCell * cell);
-gdouble               gfs_velocity_norm              (FttCell * cell);
-gdouble               gfs_velocity_norm2             (FttCell * cell);
-gdouble               gfs_velocity_lambda2           (FttCell * cell);
+void                  gfs_normal_divergence          (FttCell * cell,
+						      GfsVariable * v);
+void                  gfs_normal_divergence_2D       (FttCell * cell,
+						      GfsVariable * v);
+gdouble               gfs_divergence                 (FttCell * cell,
+						      GfsVariable ** v);
+gdouble               gfs_vorticity                  (FttCell * cell,
+						      GfsVariable ** v);
+gdouble               gfs_vector_norm                (FttCell * cell,
+						      GfsVariable ** v);
+gdouble               gfs_vector_norm2               (FttCell * cell,
+						      GfsVariable ** v);
+gdouble               gfs_vector_lambda2             (FttCell * cell,
+						      GfsVariable ** v);
 void                  gfs_pressure_force             (FttCell * cell,
+						      GfsVariable * p,
 						      FttVector * f);
 GtsRange              gfs_stats_variable             (FttCell * root, 
 						      GfsVariable * v, 
@@ -247,9 +212,8 @@ void                  ftt_cell_refine_corners       (FttCell * cell,
 gdouble               gfs_center_curvature          (FttCell * cell,
 						     FttComponent c,
 						     guint v);
-gdouble               gfs_streamline_curvature      (FttCell * cell);
-gdouble               gfs_cell_laplacian            (FttCell * cell, 
-						     GfsVariable * v);
+gdouble               gfs_streamline_curvature      (FttCell * cell,
+						     GfsVariable ** v);
 
 typedef struct {
 #if FTT_2D
diff --git a/src/graphic.c b/src/graphic.c
index 0fc7aec..bd5a9f9 100644
--- a/src/graphic.c
+++ b/src/graphic.c
@@ -335,172 +335,6 @@ void gfs_write_gts (GfsDomain * domain,
   gts_matrix_destroy (transform);
 }
 
-static void write_vertex (GtsPoint * p, gpointer * data)
-{
-  guint * index = data[0];
-  FILE * fp = data[1];
-
-  fprintf (fp, "%g %g %g\n", p->x, p->y, p->z);
-  VERTEX_CELL_FACE (p)->index = (*index)++;
-}
-
-static void write_face (GtsTriangle * t, FILE * fp)
-{
-  GtsVertex * v1, * v2, * v3;
-
-  gts_triangle_vertices (t, &v1, &v2, &v3);
-  fprintf (fp, "3 %u %u %u\n",
-	   VERTEX_CELL_FACE (v1)->index, 
-	   VERTEX_CELL_FACE (v2)->index,
-	   VERTEX_CELL_FACE (v3)->index);
-}
-
-static gdouble variable_value (VertexCellFace * p, GfsVariable * v)
-{
-  if (p->cell)
-    return GFS_VARIABLE (p->cell, v->i);
-  else if (p->face.neighbor)
-    return gfs_face_interpolated_value (&p->face, v->i);
-  else
-    return GFS_VARIABLE (p->face.cell, v->i);
-}
-
-static void write_variable (VertexCellFace * p, gpointer * data)
-{
-  fprintf (data[1], "%g\n", variable_value (p, data[0]));
-}
-
-static void write_vector (VertexCellFace * p, gpointer * data)
-{
-  GfsVariable * v = data[0];
-
-#if FTT_2D
-  fprintf (data[1], "%g %g 0\n", 
-	   variable_value (p, v), 
-	   variable_value (p, v->next));
-#else  /* 3D */
-  fprintf (data[1], "%g %g %g\n",
-	   variable_value (p, v), 
-	   variable_value (p, v->next),
-	   variable_value (p, v->next->next));
-#endif /* 3D */
-}
-
-void gfs_write_vtk (GfsDomain * domain,
-		    FttTraverseFlags flags,
-		    gint level,
-		    FILE * fp)
-{
-  GtsSurface * s;
-  GtsVertex * v1, * v2, * v3;
-  GtsEdge * e1, * e2, * e3;
-  gpointer data[6] = { NULL, NULL, NULL, NULL, NULL };
-  guint nvertices, nfaces, index = 0, i;
-  GfsVariable * v;
-  GSList * long_segments = NULL;
-  FttDirection d;
-
-  g_return_if_fail (domain != NULL);
-  g_return_if_fail (fp != NULL);
-
-  v1 = gts_vertex_new (gts_vertex_class (), -100., -100., 0.);
-  v2 = gts_vertex_new (gts_vertex_class (), 100., -100., 0.);
-  v3 = gts_vertex_new (gts_vertex_class (), 0., 100., 0.);
-  e1 = gts_edge_new (gts_edge_class (), v1, v2);
-  e2 = gts_edge_new (gts_edge_class (), v2, v3);
-  e3 = gts_edge_new (gts_edge_class (), v3, v1);
-  s = gts_surface_new (gts_surface_class (), 
-		       gts_face_class (), 
-		       gts_edge_class (), 
-		       vertex_cell_face_class ());
-  gts_surface_add_face (s, gts_face_new (gts_face_class (), e1, e2, e3));
-
-  data[0] = s;
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, flags, level, 
-			    (FttCellTraverseFunc) triangulate, data);
-  data[5] = &d;
-  for (d = 0; d < FTT_NEIGHBORS; d++)
-    gfs_domain_cell_traverse_boundary (domain, d, FTT_PRE_ORDER, flags, level, 
-				       (FttCellTraverseFunc) triangulate_face,
-				       data);
-
-  gts_allow_floating_vertices = TRUE;
-  gts_object_destroy (GTS_OBJECT (v1));
-  gts_object_destroy (GTS_OBJECT (v2));
-  gts_object_destroy (GTS_OBJECT (v3));
-  gts_allow_floating_vertices = FALSE;
-
-  gts_surface_foreach_edge (s, (GtsFunc) add_long_segment, &long_segments);
-  gts_allow_floating_edges = TRUE;
-  g_slist_foreach (long_segments, (GFunc) gts_object_destroy, NULL);
-  gts_allow_floating_edges = FALSE;
-  g_slist_free (long_segments);
-
-  nvertices = gts_surface_vertex_number (s);
-  fprintf (fp, 
-	   "# vtk DataFile Version 2.0\n"
-	   "Generated by Gerris\n"
-           "ASCII\n"
-	   "DATASET UNSTRUCTURED_GRID\n"
-	   "POINTS %u float\n",
-	   nvertices);
-  data[0] = &index;
-  data[1] = fp;
-  gts_surface_foreach_vertex (s, (GtsFunc) write_vertex, data);
-  nfaces = gts_surface_face_number (s);
-  fprintf (fp, "CELLS %u %u\n", nfaces, 4*nfaces);
-  gts_surface_foreach_face (s, (GtsFunc) write_face, fp);
-  fprintf (fp, "CELL_TYPES %u\n", nfaces);
-  for (i = 0; i < nfaces; i++)
-    fputs ("5\n", fp);
-  fprintf (fp, "POINT_DATA %u\n", nvertices);
-  v = domain->variables;
-  while (v) {
-    guint d;
-
-    if (!strcmp (v->name, "U") && v->next &&
-	!strcmp (v->next->name, "V")
-#if (!FTT_2D)
-	&& v->next->next && !strcmp (v->next->next->name, "W")
-#endif /* not FTT_2D */
-	) {
-      fputs ("VECTORS U float\n", fp);
-      data[0] = v;
-      data[1] = fp;
-      gts_surface_foreach_vertex (s, (GtsFunc) write_vector, data);
-      for (d = 0; d < FTT_DIMENSION; d++) v = v->next;
-    }
-    else if (v->name[strlen (v->name) - 1] == 'x' && v->next &&
-	     v->next->name[strlen (v->name) - 1] == 'y'
-#if (!FTT_2D)
-	     && v->next->next &&
-	     v->next->next->name[strlen (v->name) - 1] == 'z'
-#endif /* not FTT_2D */
-	     ) {
-      gchar * name = g_strndup (v->name, strlen (v->name) - 1);
-
-      fprintf (fp, "VECTORS %s float\n", name);
-      g_free (name);
-      data[0] = v;
-      data[1] = fp;
-      gts_surface_foreach_vertex (s, (GtsFunc) write_vector, data);
-      for (d = 0; d < FTT_DIMENSION; d++) v = v->next;
-    }
-    else {
-      fprintf (fp, 
-	       "SCALARS %s float 1\n"
-	       "LOOKUP_TABLE default\n",
-	       v->name);
-      data[0] = v;
-      data[1] = fp;
-      gts_surface_foreach_vertex (s, (GtsFunc) write_variable, data);
-      v = v->next;
-    }
-  }
-	 
-  gts_object_destroy (GTS_OBJECT (s));
-}
-
 static void extent (FttCell * cell, gpointer * data)
 {
   FttVector * min = data[0];
@@ -1797,16 +1631,18 @@ static GSList * ribbon_profile (GtsPointClass * klass,
 }
 
 #if (!FTT_2D)
-static void vorticity_vector (FttCell * cell)
+static void vorticity_vector (FttCell * cell, gpointer * data)
 {
   gdouble size = ftt_cell_size (cell);
+  GfsVariable ** g = data[0];
+  GfsVariable ** v = data[1];
 
-  GFS_STATE (cell)->g[0] = (gfs_center_gradient (cell, FTT_Y, GFS_W) -
-			    gfs_center_gradient (cell, FTT_Z, GFS_V))/size;
-  GFS_STATE (cell)->g[1] = (gfs_center_gradient (cell, FTT_Z, GFS_U) -
-			    gfs_center_gradient (cell, FTT_X, GFS_W))/size;
-  GFS_STATE (cell)->g[2] = (gfs_center_gradient (cell, FTT_X, GFS_V) -
-			    gfs_center_gradient (cell, FTT_Y, GFS_U))/size;
+  GFS_VARIABLE (cell, g[0]->i) = (gfs_center_gradient (cell, FTT_Y, v[2]->i) -
+				  gfs_center_gradient (cell, FTT_Z, v[1]->i))/size;
+  GFS_VARIABLE (cell, g[1]->i) = (gfs_center_gradient (cell, FTT_Z, v[0]->i) -
+				  gfs_center_gradient (cell, FTT_X, v[2]->i))/size;
+  GFS_VARIABLE (cell, g[2]->i) = (gfs_center_gradient (cell, FTT_X, v[1]->i) -
+				  gfs_center_gradient (cell, FTT_Y, v[0]->i))/size;
 }
 #endif /* 3D */
 
@@ -1828,7 +1664,10 @@ static GSList * grow_curve (GfsDomain * domain,
   guint nstep = 0, nmax = 10000;
   GtsPointClass * path_class = gfs_vertex_class ();
   Colormap * colormap = NULL;
-  GfsVariable * U;
+  GfsVariable ** U = gfs_domain_velocity (domain);
+#if (!FTT_2D)
+  GfsVariable * vort[FTT_DIMENSION];
+#endif /* 3D */
 
   if (min < max)
     colormap = colormap_jet ();
@@ -1838,25 +1677,29 @@ static GSList * grow_curve (GfsDomain * domain,
     path_class = GTS_POINT_CLASS (gfs_twisted_vertex_class ());
     {
       FttComponent c;
-      GfsVariable * g;
+      gpointer data[2];
 
+      for (c = 0; c < FTT_DIMENSION; c++) {
+	vort[c] = gfs_temporary_variable (domain);
+	gfs_variable_set_vector (vort[c], c);
+      }
+      data[0] = vort;
+      data[1] = U;
       gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			     (FttCellTraverseFunc) vorticity_vector, NULL);
-      for (c = 0, g = gfs_gx; c < FTT_DIMENSION; c++, g = g->next)
+				(FttCellTraverseFunc) vorticity_vector, data);
+      for (c = 0; c < FTT_DIMENSION; c++)
 	gfs_domain_cell_traverse (domain,
 				  FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-				  (FttCellTraverseFunc) g->fine_coarse, g);
+				  (FttCellTraverseFunc) vort[c]->fine_coarse, vort[c]);
     }
   }
 #endif /* 3D */  
 
-  U = gfs_variable_from_name (domain->variables, "U");
   p1 = p2 = p;
   while ((cell = gfs_domain_locate (domain, p, -1)) != NULL && nmax--) {
     gdouble h = delta*ftt_cell_size (cell);
     FttVector u;
     FttComponent c;
-    GfsVariable * v;
     gdouble nu = 0.;
 
     cost += curve_cost (p1, p2, p);
@@ -1876,8 +1719,8 @@ static GSList * grow_curve (GfsDomain * domain,
       nstep = 0;
     }
 
-    for (c = 0, v = U; c < FTT_DIMENSION; c++, v = v->next) {
-      ((gdouble *) &u)[c] = direction*gfs_interpolate (cell, p, v);
+    for (c = 0; c < FTT_DIMENSION; c++) {
+      ((gdouble *) &u)[c] = direction*gfs_interpolate (cell, p, U[c]);
       nu += ((gdouble *) &u)[c]*((gdouble *) &u)[c];
     }
     if (nu > 0. && nstep++ < nmax) {
@@ -1893,8 +1736,8 @@ static GSList * grow_curve (GfsDomain * domain,
 	GtsVector dx;
 
 	dx[0] = p1.x - p.x; dx[1] = p1.y - p.y; dx[2] = p1.z - p.z;
-	for (c = 0, v = gfs_gx; c < FTT_DIMENSION; c++, v = v->next)
-	  rot[c] = gfs_interpolate (cell, p1, v);
+	for (c = 0; c < FTT_DIMENSION; c++)
+	  rot[c] = gfs_interpolate (cell, p1, vort[c]);
 	theta += gts_vector_scalar (rot, dx)/nu;
       }
 #endif /* 3D */
@@ -1917,6 +1760,13 @@ static GSList * grow_curve (GfsDomain * domain,
   if (colormap)
     colormap_destroy (colormap);
 
+#if (!FTT_2D)
+  if (twist) {
+    FttComponent c;
+    for (c = 0; c < FTT_DIMENSION; c++)
+      gts_object_destroy (GTS_OBJECT (vort[c]));
+  }
+#endif /* 3D */
   return direction > 0. ? g_slist_reverse (path) : path;
 }
 
diff --git a/src/graphic.h b/src/graphic.h
index 313f47c..63e186b 100644
--- a/src/graphic.h
+++ b/src/graphic.h
@@ -32,10 +32,6 @@ void               gfs_write_gts               (GfsDomain * domain,
 						gint level,
 						GtsBBox * box,
 						FILE * fp);
-void               gfs_write_vtk               (GfsDomain * domain,
-						FttTraverseFlags flags,
-						gint level,
-						FILE * fp);
 GtsSurface *       gfs_isosurface              (GfsDomain * domain, 
 						GfsVariable * v, 
 						gdouble val,
diff --git a/src/init.c b/src/init.c
index b6d399f..19965b6 100644
--- a/src/init.c
+++ b/src/init.c
@@ -78,42 +78,6 @@ static void gfs_log (const gchar * log_domain,
 	   log_domain, stype[type], pe, message); 
 }
 
-static void cell_vorticity (FttCell * cell, GfsVariable * v)
-{
-  GFS_VARIABLE (cell, v->i) = gfs_vorticity (cell);
-}
-
-static void cell_velocity_norm (FttCell * cell, GfsVariable * v)
-{
-  GFS_VARIABLE (cell, v->i) = gfs_velocity_norm (cell);
-}
-
-static void cell_velocity_norm2 (FttCell * cell, GfsVariable * v)
-{
-  GFS_VARIABLE (cell, v->i) = gfs_velocity_norm2 (cell);
-}
-
-static void cell_level (FttCell * cell, GfsVariable * v)
-{
-  GFS_VARIABLE (cell, v->i) = ftt_cell_level (cell);
-}
-
-static void cell_fraction (FttCell * cell, GfsVariable * v)
-{
-  GFS_VARIABLE (cell, v->i) = GFS_IS_MIXED (cell) ? GFS_STATE (cell)->solid->a : 1.;
-}
-
-static void cell_lambda2 (FttCell * cell, GfsVariable * v)
-{
-  gdouble size = ftt_cell_size (cell);
-  GFS_VARIABLE (cell, v->i) /= gfs_velocity_lambda2 (cell)/(size*size);
-}
-
-static void cell_curvature (FttCell * cell, GfsVariable * v)
-{
-  GFS_VARIABLE (cell, v->i) = gfs_streamline_curvature (cell)/ftt_cell_size (cell);
-}
-
 /**
  * gfs_init:
  * @argc: a pointer on the number of command line arguments passed to
@@ -126,8 +90,6 @@ static void cell_curvature (FttCell * cell, GfsVariable * v)
  */
 void gfs_init (int * argc, char *** argv)
 {
-  guint i = 0;
-  GfsVariable * v;
   static gboolean initialized = FALSE;
 
   if (initialized)
@@ -167,60 +129,6 @@ void gfs_init (int * argc, char *** argv)
 		     G_LOG_FLAG_RECURSION,
 		     (GLogFunc) gfs_log, NULL);
 
-  /* Initialize permanent variables */
-  gfs_div = v = gfs_variable_new (gfs_variable_class (), NULL, NULL, FALSE, i++);
-  v->permanent = NULL;
-  g_assert (v->i == GFS_DIV);
-  gfs_dp = v = v->next = gfs_variable_new (gfs_variable_class (), NULL, NULL, TRUE, i++);
-  v->permanent = NULL;
-  g_assert (v->i  == GFS_DP);
-  gfs_res = v = v->next = gfs_variable_new (gfs_variable_class (), NULL, NULL, FALSE, i++);
-  v->permanent = NULL;
-  g_assert (v->i == GFS_RES);
-  gfs_gx = v = v->next = gfs_variable_new (gfs_variable_class (), NULL, NULL, FALSE, i++);
-  v->permanent = NULL;
-  g_assert (v->i  == GFS_GX);
-  gfs_gy = v = v->next = gfs_variable_new (gfs_variable_class (), NULL, NULL, FALSE, i++);
-  v->permanent = NULL;
-  g_assert (v->i  == GFS_GY);
-#if (!FTT_2D)
-  gfs_gz = v = v->next = gfs_variable_new (gfs_variable_class (), NULL, NULL, FALSE, i++);
-  v->permanent = NULL;
-  g_assert (v->i  == GFS_GZ);
-#endif /* FTT_3D */
-  gfs_centered_variables = gfs_p = v = v->next = 
-    gfs_variable_new (gfs_variable_class (), NULL, "P", TRUE, i++);
-  g_assert (v->i   == GFS_P);
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "U", FALSE, i++);
-  g_assert (v->i == GFS_U);
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "V", FALSE, i++);
-  g_assert (v->i == GFS_V);
-#if (!FTT_2D)
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "W", FALSE, i++);
-  g_assert (v->i == GFS_W);
-#endif /* FTT_3D */
-
-  /* Initializes derived variables */
-  gfs_derived_first = v = 
-    gfs_variable_new (gfs_variable_class (), NULL, "Vorticity", FALSE, GFS_DIV);
-  v->derived = cell_vorticity;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "Divergence", FALSE, GFS_DIV);
-  v->derived = (GfsVariableDerivedFunc) gfs_divergence;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "Velocity", FALSE, GFS_DIV);
-  v->derived = cell_velocity_norm;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "Velocity2", FALSE, GFS_DIV);
-  v->derived = cell_velocity_norm2;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "Level", FALSE, GFS_DIV);
-  v->derived = cell_level;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "A", FALSE, GFS_DIV);
-  v->derived = cell_fraction;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "Lambda2", FALSE, GFS_DIV);
-  v->derived = cell_lambda2;
-  v = v->next = gfs_variable_new (gfs_variable_class (), NULL, "Curvature", FALSE, GFS_DIV);
-  v->derived = cell_curvature;
-  gfs_derived_last = v;
-  v->next = gfs_centered_variables;
-
   /* Instantiates classes before reading any domain or simulation file */
   gfs_simulation_class ();
     gfs_ocean_class ();
@@ -274,7 +182,7 @@ void gfs_init (int * argc, char *** argv)
       gfs_source_class ();
         gfs_source_control_class ();
       gfs_source_coriolis_class ();
-      gfs_source_hydrostatic_class ();
+      /* fixme: gfs_source_hydrostatic_class (); */
       gfs_source_diffusion_class ();
         gfs_source_diffusion_explicit_class ();
         gfs_source_viscosity_class ();
diff --git a/src/ocean.c b/src/ocean.c
index 65392c7..bab1ffd 100644
--- a/src/ocean.c
+++ b/src/ocean.c
@@ -27,6 +27,30 @@
 
 /* GfsOcean: Object */
 
+#if 1
+static fixme(){}
+
+GfsSimulationClass * gfs_ocean_class (void)
+{
+  static GfsSimulationClass * klass = NULL;
+
+  if (klass == NULL) {
+    GtsObjectClassInfo gfs_ocean_info = {
+      "GfsOcean",
+      sizeof (GfsOcean),
+      sizeof (GfsSimulationClass),
+      (GtsObjectClassInitFunc) NULL,
+      (GtsObjectInitFunc) NULL,
+      (GtsArgSetFunc) NULL,
+      (GtsArgGetFunc) NULL
+    };
+    klass = gts_object_class_new (GTS_OBJECT_CLASS (gfs_simulation_class ()), &gfs_ocean_info);
+  }
+
+  return klass;
+}
+#else
+
 static void ocean_destroy (GtsObject * object)
 {
   guint i;
@@ -323,7 +347,8 @@ static void gfs_free_surface_divergence (GfsDomain * domain, GfsVariable * div)
 			    (FttFaceTraverseFunc) gfs_face_reset_normal_velocity, NULL);
   gfs_domain_face_traverse (domain, FTT_XY,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, NULL);
+			    (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, 
+			    gfs_domain_velocity (domain));
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) gfs_normal_divergence_2D, NULL);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
@@ -441,7 +466,8 @@ static void ocean_run (GfsSimulation * sim)
 			      (FttFaceTraverseFunc) gfs_face_reset_normal_velocity, NULL);
     gfs_domain_face_traverse (domain, FTT_XY,
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, NULL);
+			      (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, 
+			      gfs_domain_velocity (domain));
     gfs_free_surface_pressure (domain, &sim->approx_projection_params, &sim->advection_params,
 			       ps, div, sim->physical_params.g);
     gfs_domain_timer_stop (domain, "free_surface_pressure");
@@ -703,3 +729,6 @@ GfsSourceGenericClass * gfs_source_hydrostatic_class (void)
 
   return klass;
 }
+
+
+#endif
diff --git a/src/output.c b/src/output.c
index 1d54cdc..d29714a 100644
--- a/src/output.c
+++ b/src/output.c
@@ -1134,14 +1134,13 @@ static gboolean gfs_output_location_event (GfsEvent * event,
     guint i;
 
     if (GFS_OUTPUT (event)->first_call) {
-      GfsVariable * v = domain->variables;
+      GSList * i = domain->variables;
       guint nv = 5;
 
       fputs ("# 1:T 2:X 3:Y 4:Z", fp);
-      while (v) {
-	if (v->name)
-	  fprintf (fp, " %d:%s", nv++, v->name);
-	v = v->next;
+      while (i) {
+	fprintf (fp, " %d:%s", nv++, GFS_VARIABLE1 (i->data)->name);
+	i = i->next;
       }
       fputc ('\n', fp);
     }
@@ -1150,13 +1149,12 @@ static gboolean gfs_output_location_event (GfsEvent * event,
       FttCell * cell = gfs_domain_locate (domain, p, -1);
       
       if (cell != NULL) {
-	GfsVariable * v = domain->variables;
+	GSList * i = domain->variables;
 	
 	fprintf (fp, "%g %g %g %g", sim->time.t, p.x, p.y, p.z);
-	while (v) {
-	  if (v->name)
-	    fprintf (fp, " %g", gfs_interpolate (cell, p, v));
-	  v = v->next;
+	while (i) {
+	  fprintf (fp, " %g", gfs_interpolate (cell, p, i->data));
+	  i = i->next;
 	}
 	fputc ('\n', fp);
       }
@@ -1207,7 +1205,7 @@ static void output_simulation_destroy (GtsObject * object)
 {
   GfsOutputSimulation * output = GFS_OUTPUT_SIMULATION (object);
 
-  gfs_variable_list_destroy (output->var);
+  g_slist_free (output->var);
 
   (* GTS_OBJECT_CLASS (gfs_output_simulation_class ())->parent_class->destroy) (object);
 }
@@ -1217,9 +1215,6 @@ static gboolean output_simulation_event (GfsEvent * event, GfsSimulation * sim)
   if ((* GFS_EVENT_CLASS (gfs_output_class())->event) (event, sim)) {
     GfsDomain * domain = GFS_DOMAIN (sim);
     GfsOutputSimulation * output = GFS_OUTPUT_SIMULATION (event);
-    GfsVariable * var = domain->variables_io;
-    gboolean binary = domain->binary;
-    gboolean surface = sim->output_surface;
 
     domain->variables_io = output->var;
     domain->binary =       output->binary;
@@ -1227,9 +1222,9 @@ static gboolean output_simulation_event (GfsEvent * event, GfsSimulation * sim)
     gfs_simulation_write (sim,
 			  output->max_depth,
 			  GFS_OUTPUT (event)->file->fp);
-    domain->variables_io = var;
-    domain->binary =       binary;
-    sim->output_surface =  surface;
+    domain->variables_io = NULL;
+    domain->binary =       TRUE;
+    sim->output_surface =  TRUE;
     fflush (GFS_OUTPUT (event)->file->fp);
     return TRUE;
   }
@@ -1239,20 +1234,19 @@ static gboolean output_simulation_event (GfsEvent * event, GfsSimulation * sim)
 static void output_simulation_write (GtsObject * o, FILE * fp)
 {
   GfsOutputSimulation * output = GFS_OUTPUT_SIMULATION (o);
-  GfsVariable * v = output->var;
+  GSList * i = output->var;
 
   (* GTS_OBJECT_CLASS (gfs_output_simulation_class ())->parent_class->write) (o, fp);
 
   fputs (" {", fp);
   if (output->max_depth != -1)
     fprintf (fp, " depth = %d", output->max_depth);
-  if (v != NULL) {
-    fprintf (fp, " variables = %s", v->name);
-    v = v->next;
-    while (v) {
-      if (v->name)
-	fprintf (fp, ",%s", v->name);
-      v = v->next;
+  if (i != NULL) {
+    fprintf (fp, " variables = %s", GFS_VARIABLE1 (i->data)->name);
+    i = i->next;
+    while (i) {
+      fprintf (fp, ",%s", GFS_VARIABLE1 (i->data)->name);
+      i = i->next;
     }
   }
   if (!output->binary)
@@ -1292,8 +1286,7 @@ static void output_simulation_read (GtsObject ** o, GtsFile * fp)
 
   if (variables != NULL) {
     gchar * error = NULL;
-    GfsVariable * vars = gfs_variables_from_list (domain->variables, 
-						  variables, &error);
+    GSList * vars = gfs_variables_from_list (domain->variables, variables, &error);
 
     if (vars == NULL) {
       gts_file_variable_error (fp, var, "variables",
@@ -1302,13 +1295,12 @@ static void output_simulation_read (GtsObject ** o, GtsFile * fp)
       return;
     }
     if (output->var)
-      gfs_variable_list_destroy (output->var);
+      g_slist_free (output->var);
     output->var = vars;
     g_free (variables);
   }
   else if (output->var == NULL)
-    output->var = gfs_variable_list_copy (domain->variables, 
-					  GTS_OBJECT (domain));
+    output->var = g_slist_copy (domain->variables);
 }
 
 static void gfs_output_simulation_class_init (GfsEventClass * klass)
@@ -1621,10 +1613,7 @@ static void gfs_output_scalar_write (GtsObject * o, FILE * fp)
 
 static void update_v (FttCell * cell, GfsOutputScalar * output)
 {
-  FttVector p;
-  gfs_cell_cm (cell, &p);
-  GFS_VARIABLE (cell, output->v->i) = gfs_function_value (output->f, cell, &p,
-							  gfs_object_simulation (output)->time.t);
+  GFS_VARIABLE (cell, output->v->i) = gfs_function_value (output->f, cell);
 }
 
 static gboolean gfs_output_scalar_event (GfsEvent * event,
@@ -1636,7 +1625,7 @@ static gboolean gfs_output_scalar_event (GfsEvent * event,
     GfsDomain * domain = GFS_DOMAIN (sim);
 
     if (!(output->v = gfs_function_get_variable (output->f))) {
-      output->v = gfs_div;
+      output->v = gfs_variable_new (gfs_variable_class (), domain, NULL);
       gfs_domain_cell_traverse (domain,
 				FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 				(FttCellTraverseFunc) update_v, output);
@@ -1658,9 +1647,21 @@ static gboolean gfs_output_scalar_event (GfsEvent * event,
   return FALSE;
 }
 
+static void gfs_output_scalar_post_event (GfsEvent * event,
+					  GfsSimulation * sim)
+{
+  GfsOutputScalar * output = GFS_OUTPUT_SCALAR (event);
+
+  if (!gfs_function_get_variable (output->f)) {
+    gts_object_destroy (GTS_OBJECT (output->v));
+    output->v = NULL;
+  }
+}
+
 static void gfs_output_scalar_class_init (GfsOutputClass * klass)
 {
   GFS_EVENT_CLASS (klass)->event = gfs_output_scalar_event;
+  GFS_EVENT_CLASS (klass)->post_event = gfs_output_scalar_post_event;
   GTS_OBJECT_CLASS (klass)->read = gfs_output_scalar_read;
   GTS_OBJECT_CLASS (klass)->write = gfs_output_scalar_write;
   GTS_OBJECT_CLASS (klass)->destroy = gfs_output_scalar_destroy;
@@ -1962,12 +1963,8 @@ static void update_histogram (FttCell * cell, GfsOutputScalar * h)
   if (i >= 0 && i < hi->n) {
     gdouble w = hi->dt;
 
-    if (hi->w) {
-      FttVector p;
-
-      gfs_cell_cm (cell, &p);
-      w *= gfs_function_value (hi->w, cell, &p, gfs_object_simulation (h)->time.t);
-    }
+    if (hi->w)
+      w *= gfs_function_value (hi->w, cell);
     else
       w *= gfs_cell_volume (cell);
 
@@ -2054,16 +2051,18 @@ GfsOutputClass * gfs_output_scalar_histogram_class (void)
 
 /* GfsOutputEnergy: Object */
 
-static void add_energy (FttCell * cell, gpointer * data)
+typedef struct {
+  GtsRange ps;
+  gdouble ke, pe;
+  GfsVariable ** u, * p;
+} EnergyParams;
+
+static void add_energy (FttCell * cell, EnergyParams * p)
 {
-  GfsStateVector * s = GFS_STATE (cell);
-  GtsRange * ps = data[2];
   gdouble vol = gfs_cell_volume (cell);
-  gdouble * ke = data[0];
-  gdouble * pe = data[1];
 
-  *ke += vol*(s->u*s->u + s->v*s->v);;
-  *pe += vol*(s->p - ps->mean)*(s->p - ps->mean);
+  p->ke += vol*gfs_vector_norm2 (cell, p->u);
+  p->pe += vol*(GFS_VARIABLE (cell, p->p->i) - p->ps.mean)*(GFS_VARIABLE (cell, p->p->i) - p->ps.mean);
 }
 
 static gboolean gfs_output_energy_event (GfsEvent * event,
@@ -2072,24 +2071,21 @@ static gboolean gfs_output_energy_event (GfsEvent * event,
   if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (gfs_output_energy_class ())->parent_class)->event) 
       (event, sim)) {
     GfsOutput * output = GFS_OUTPUT (event);
-    gpointer data[3];
-    gdouble ke = 0., pe = 0.;
-    GtsRange stats = gfs_domain_stats_variable (GFS_DOMAIN (sim), 
-						gfs_p,
-						FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL, 
-						-1);
+    EnergyParams p;
+
+    p.p = gfs_variable_from_name (GFS_DOMAIN (sim)->variables, "P");
+    p.u = gfs_domain_velocity (GFS_DOMAIN (sim));
+    p.ps = gfs_domain_stats_variable (GFS_DOMAIN (sim), p.p,
+				      FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL, -1);
+    p.pe = p.ke = 0.;
 
-    data[0] = &ke;
-    data[1] = &pe;
-    data[2] = &stats;
     gfs_domain_cell_traverse (GFS_DOMAIN (sim),
 			      FTT_PRE_ORDER, 
-			      FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL,
-			      -1,
-			      (FttCellTraverseFunc) add_energy, data);
+			      FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL, -1,
+			      (FttCellTraverseFunc) add_energy, &p);
     fprintf (output->file->fp,
 	     "Energy time: %g kinetic: %10.3e potential: %10.3e\n",
-	     sim->time.t, ke, pe/sim->physical_params.g);
+	     sim->time.t, p.ke, p.pe/sim->physical_params.g);
     return TRUE;
   }
   return FALSE;
@@ -2196,7 +2192,6 @@ static void output_error_norm_read (GtsObject ** o, GtsFile * fp)
       n->v = gfs_variable_from_name (domain->variables, fp->token->str);
       if (!n->v)
 	n->v = gfs_domain_add_variable (domain, fp->token->str);
-      g_assert (n->v);
       gts_file_next_token (fp);
     }
     else {
@@ -2222,7 +2217,7 @@ static void output_error_norm_write (GtsObject * o, FILE * fp)
   fputs (" { s = ", fp);
   gfs_function_write (n->s, fp);
   fprintf (fp, " unbiased = %d", n->unbiased);
-  if (n->v != gfs_div)
+  if (n->v)
     fprintf (fp, " v = %s }", n->v->name);
   else
     fputs (" }", fp);
@@ -2230,15 +2225,8 @@ static void output_error_norm_write (GtsObject * o, FILE * fp)
 
 static void compute_error (FttCell * cell, GfsOutputScalar * o)
 {
-  FttVector p;
-  GfsSimulation * sim = gfs_object_simulation (o);
-
-  if (o->v->centered) /* fixme: this does not work with new scalar functions */
-    ftt_cell_pos (cell, &p);
-  else
-    gfs_cell_cm (cell, &p);
   GFS_VARIABLE (cell, GFS_OUTPUT_ERROR_NORM (o)->v->i) = GFS_VARIABLE (cell, o->v->i) -
-    gfs_function_value (GFS_OUTPUT_ERROR_NORM (o)->s, cell, &p, sim->time.t); 
+    gfs_function_value (GFS_OUTPUT_ERROR_NORM (o)->s, cell);
 }
 
 static void remove_bias (FttCell * cell, gpointer * data)
@@ -2254,29 +2242,36 @@ static gboolean gfs_output_error_norm_event (GfsEvent * event,
   if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (gfs_output_error_norm_class ())->parent_class)->event)
       (event, sim)) {
     GfsOutputScalar * output = GFS_OUTPUT_SCALAR (event);
-    GfsVariable * v = GFS_OUTPUT_ERROR_NORM (event)->v;
+    GfsOutputErrorNorm * enorm = GFS_OUTPUT_ERROR_NORM (event);
+    GfsVariable * v = enorm->v;
     GfsNorm norm;
 
+    if (v == NULL)
+      enorm->v = gfs_variable_new (gfs_variable_class (), GFS_DOMAIN (sim), NULL);
     gfs_domain_cell_traverse (GFS_DOMAIN (sim), FTT_PRE_ORDER, 
 			      FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL,  
 			      output->maxlevel,
 			      (FttCellTraverseFunc) compute_error, output);
-    norm = gfs_domain_norm_variable (GFS_DOMAIN (sim), v,
+    norm = gfs_domain_norm_variable (GFS_DOMAIN (sim), enorm->v,
 				     FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL, 
 				     output->maxlevel);
     if (GFS_OUTPUT_ERROR_NORM (event)->unbiased) {
       gpointer data[2];
 
-      data[0] = v;
+      data[0] = enorm->v;
       data[1] = &norm;
       gfs_domain_cell_traverse (GFS_DOMAIN (sim), FTT_PRE_ORDER, 
 				FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL,  
 				output->maxlevel,
 				(FttCellTraverseFunc) remove_bias, data);
-      norm = gfs_domain_norm_variable (GFS_DOMAIN (sim), v,
+      norm = gfs_domain_norm_variable (GFS_DOMAIN (sim), enorm->v,
 				       FTT_TRAVERSE_LEAFS|FTT_TRAVERSE_LEVEL, 
 				       output->maxlevel);
     }
+    if (v == NULL) {
+      gts_object_destroy (GTS_OBJECT (enorm->v));
+      enorm->v = NULL;
+    }
     fprintf (GFS_OUTPUT (event)->file->fp,
 	     "%s time: %g first: % 10.3e second: % 10.3e infty: % 10.3e bias: %10.3e\n",
 	     output->name, sim->time.t,
@@ -2297,7 +2292,6 @@ static void gfs_output_error_norm_class_init (GfsOutputClass * klass)
 static void output_error_norm_init (GfsOutputErrorNorm * e)
 {
   e->s = gfs_function_new (gfs_function_class (), 0.);
-  e->v = gfs_div;
 }
 
 GfsOutputClass * gfs_output_error_norm_class (void)
@@ -2330,14 +2324,8 @@ static void compute_correlation (FttCell * cell, gpointer * data)
   gdouble * sum = data[2];
   gdouble * sumref = data[3];
   gdouble v, ref, w;
-  FttVector p;
-  GfsSimulation * sim = gfs_object_simulation (o);
 
-  if (o->v->centered) /* fixme: this does not work with new scalar functions */
-    ftt_cell_pos (cell, &p);
-  else
-    gfs_cell_cm (cell, &p);
-  ref = gfs_function_value (GFS_OUTPUT_ERROR_NORM (o)->s, NULL, &p, sim->time.t);
+  ref = gfs_function_value (GFS_OUTPUT_ERROR_NORM (o)->s, cell);
   v = GFS_VARIABLE (cell, o->v->i) - *bias;
   w = gfs_cell_volume (cell);
   *sumref += ref*ref*w;
diff --git a/src/output.h b/src/output.h
index f9b6f63..bc8deb7 100644
--- a/src/output.h
+++ b/src/output.h
@@ -131,7 +131,7 @@ struct _GfsOutputSimulation {
   GfsOutput parent;
 
   gint max_depth;
-  GfsVariable * var;
+  GSList * var;
   gboolean binary, surface;
 };
 
diff --git a/src/poisson.c b/src/poisson.c
index 30df8bb..f607e58 100644
--- a/src/poisson.c
+++ b/src/poisson.c
@@ -132,60 +132,59 @@ void gfs_face_gradient_flux_centered (const FttCellFace * face,
   }
 }
 
-static void relax (FttCell * cell, gpointer * data)
+typedef struct {
+  guint u, rhs, dia, res;
+  gint maxlevel;
+} RelaxParams;
+
+static void relax (FttCell * cell, RelaxParams * p)
 {
-  guint u = ((GfsVariable *) data[0])->i;
-  guint rhs = ((GfsVariable *) data[1])->i;
-  gint max_level = *((gint *) data[2]);
   GfsGradient g;
   FttCellNeighbors neighbor;
   FttCellFace f;
   GfsGradient ng;
 
-  g.a = GFS_STATE (cell)->g[0];
+  g.a = GFS_VARIABLE (cell, p->dia);
   g.b = 0.;
   f.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
   for (f.d = 0; f.d < FTT_NEIGHBORS; f.d++) {
     f.neighbor = neighbor.c[f.d];
     if (f.neighbor) {
-      FACE_GRADIENT (&f, &ng, u, max_level);
+      FACE_GRADIENT (&f, &ng, p->u, p->maxlevel);
       g.a += ng.a;
       g.b += ng.b;
     }
   }
   if (g.a > 0.)
-    GFS_VARIABLE (cell, u) = (g.b - GFS_VARIABLE (cell, rhs))/g.a;
+    GFS_VARIABLE (cell, p->u) = (g.b - GFS_VARIABLE (cell, p->rhs))/g.a;
   else
-    GFS_VARIABLE (cell, u) = 0.;
+    GFS_VARIABLE (cell, p->u) = 0.;
 }
 
-static void relax2D (FttCell * cell, gpointer * data)
+static void relax2D (FttCell * cell, RelaxParams * p)
 {
-  guint u = ((GfsVariable *) data[0])->i;
-  guint rhs = ((GfsVariable *) data[1])->i;
-  gint max_level = *((gint *) data[2]);
   GfsGradient g;
   FttCellNeighbors neighbor;
   FttCellFace f;
   GfsGradient ng;
 
-  g.a = GFS_STATE (cell)->g[0];
+  g.a = GFS_VARIABLE (cell, p->dia);
   g.b = 0.;
   f.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
   for (f.d = 0; f.d < FTT_NEIGHBORS_2D; f.d++) {
     f.neighbor = neighbor.c[f.d];
     if (f.neighbor) {
-      FACE_GRADIENT (&f, &ng, u, max_level);
+      FACE_GRADIENT (&f, &ng, p->u, p->maxlevel);
       g.a += ng.a;
       g.b += ng.b;
     }
   }
   if (g.a > 0.)
-    GFS_VARIABLE (cell, u) = (g.b - GFS_VARIABLE (cell, rhs))/g.a;
+    GFS_VARIABLE (cell, p->u) = (g.b - GFS_VARIABLE (cell, p->rhs))/g.a;
   else
-    GFS_VARIABLE (cell, u) = 0.;
+    GFS_VARIABLE (cell, p->u) = 0.;
 }
 
 /**
@@ -195,6 +194,7 @@ static void relax2D (FttCell * cell, gpointer * data)
  * @max_depth: the maximum depth of the domain to relax.
  * @u: the variable to use as left-hand side.
  * @rhs: the variable to use as right-hand side.
+ * @dia: the diagonal weight.
  *
  * Apply one pass of a Jacobi relaxation to all the leaf cells of
  * @domain with a level inferior or equal to @max_depth and to all the
@@ -206,75 +206,71 @@ void gfs_relax (GfsDomain * domain,
 		guint d,
 		gint max_depth,
 		GfsVariable * u,
-		GfsVariable * rhs)
+		GfsVariable * rhs,
+		GfsVariable * dia)
 {
-  gpointer data[3];
+  RelaxParams p;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (d > 1 && d <= 3);
   g_return_if_fail (u != NULL);
   g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
 
-  data[0] = u;
-  data[1] = rhs;
-  data[2] = &max_depth;
-
+  p.u = u->i;
+  p.rhs = rhs->i;
+  p.dia = dia->i;
+  p.maxlevel = max_depth;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, 
 			    FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS,
 			    max_depth,
-			    (FttCellTraverseFunc) (d == 2 ? relax2D : relax), data);
+			    (FttCellTraverseFunc) (d == 2 ? relax2D : relax), &p);
 }
 
-static void residual_set (FttCell * cell, gpointer * data)
+static void residual_set (FttCell * cell, RelaxParams * p)
 {
-  guint u = ((GfsVariable *) data[0])->i;
-  guint rhs = ((GfsVariable *) data[1])->i;
-  guint res = ((GfsVariable *) data[2])->i;
   GfsGradient g;
   FttCellNeighbors neighbor;
   FttCellFace f;
   GfsGradient ng;
 
-  g.a = GFS_STATE (cell)->g[0];
+  g.a = GFS_VARIABLE (cell, p->dia);
   g.b = 0.;
   f.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
   for (f.d = 0; f.d < FTT_NEIGHBORS; f.d++) {
     f.neighbor = neighbor.c[f.d];
     if (f.neighbor) {
-      FACE_GRADIENT (&f, &ng, u, -1);
+      FACE_GRADIENT (&f, &ng, p->u, -1);
       g.a += ng.a;
       g.b += ng.b;
     }
   }
-  GFS_VARIABLE (cell, res) = GFS_VARIABLE (cell, rhs) - 
-    (g.b - GFS_VARIABLE (cell, u)*g.a);
+  GFS_VARIABLE (cell, p->res) = GFS_VARIABLE (cell, p->rhs) - 
+    (g.b - GFS_VARIABLE (cell, p->u)*g.a);
 }
 
-static void residual_set2D (FttCell * cell, gpointer * data)
+static void residual_set2D (FttCell * cell, RelaxParams * p)
 {
-  guint u = ((GfsVariable *) data[0])->i;
-  guint rhs = ((GfsVariable *) data[1])->i;
-  guint res = ((GfsVariable *) data[2])->i;
   GfsGradient g;
   FttCellNeighbors neighbor;
   FttCellFace f;
   GfsGradient ng;
 
-  g.a = GFS_STATE (cell)->g[0];
+  g.a = GFS_VARIABLE (cell, p->dia);
   g.b = 0.;
   f.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
   for (f.d = 0; f.d < FTT_NEIGHBORS_2D; f.d++) {
     f.neighbor = neighbor.c[f.d];
     if (f.neighbor) {
-      FACE_GRADIENT (&f, &ng, u, -1);
+      FACE_GRADIENT (&f, &ng, p->u, -1);
       g.a += ng.a;
       g.b += ng.b;
     }
   }
-  GFS_VARIABLE (cell, res) = GFS_VARIABLE (cell, rhs) - 
-    (g.b - GFS_VARIABLE (cell, u)*g.a);
+  GFS_VARIABLE (cell, p->res) = GFS_VARIABLE (cell, p->rhs) - 
+    (g.b - GFS_VARIABLE (cell, p->u)*g.a);
 }
 
 /**
@@ -285,6 +281,7 @@ static void residual_set2D (FttCell * cell, gpointer * data)
  * @max_depth: maximum depth of the traversal.
  * @u: the variable to use as left-hand side.
  * @rhs: the variable to use as right-hand side.
+ * @dia: the diagonal weight.
  * @res: the variable to use to store the residual.
  *
  * For each cell of @domain, computes the sum of the residual over
@@ -296,22 +293,24 @@ void gfs_residual (GfsDomain * domain,
 		   guint d,
 		   FttTraverseFlags flags,
 		   gint max_depth,
-		   GfsVariable * u, GfsVariable * rhs, GfsVariable * res)
+		   GfsVariable * u, GfsVariable * rhs, GfsVariable * dia,
+		   GfsVariable * res)
 {
-  gpointer data[3];
+  RelaxParams p;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (d > 1 && d <= 3);
   g_return_if_fail (u != NULL);
   g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
   g_return_if_fail (res != NULL);
 
-  data[0] = u;
-  data[1] = rhs;
-  data[2] = res;
-
+  p.u = u->i;
+  p.rhs = rhs->i;
+  p.dia = dia->i;
+  p.res = res->i;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, flags, max_depth,
-			    (FttCellTraverseFunc) (d == 2 ? residual_set2D : residual_set), data);
+			    (FttCellTraverseFunc) (d == 2 ? residual_set2D : residual_set), &p);
 }
 
 static void reset_coeff (FttCell * cell)
@@ -323,6 +322,12 @@ static void reset_coeff (FttCell * cell)
     f[d].v = 0.;
 }
 
+static void reset_poisson_coeff (FttCell * cell, GfsVariable * dia)
+{
+  reset_coeff (cell);
+  GFS_VARIABLE (cell, dia->i) = 0.;
+}
+
 static void poisson_coeff (FttCellFace * face, gdouble * lambda2)
 {
   GfsStateVector * s = GFS_STATE (face->cell);
@@ -391,15 +396,23 @@ static void face_coeff_from_below (FttCell * cell)
   }
 }
 
+static void face_poisson_coeff_from_below (FttCell * cell, GfsVariable * dia)
+{
+  face_coeff_from_below (cell);
+  GFS_VARIABLE (cell, dia->i) = 0.;
+}
+
 /**
  * gfs_poisson_coefficients:
  * @domain: a #GfsDomain.
+ * @dia: the diagonal weight.
  * @c: the volume fraction.
  * @rho: the relative density.
  *
  * Initializes the face coefficients for the Poisson equation.
  */
 void gfs_poisson_coefficients (GfsDomain * domain,
+			       GfsVariable * dia,
 			       GfsVariable * c,
 			       gdouble rho)
 {
@@ -407,6 +420,7 @@ void gfs_poisson_coefficients (GfsDomain * domain,
   FttComponent i;
 
   g_return_if_fail (domain != NULL);
+  g_return_if_fail (dia != NULL);
 
   for (i = 0; i < FTT_DIMENSION; i++) {
     gdouble lambda = (&domain->lambda.x)[i];
@@ -415,7 +429,7 @@ void gfs_poisson_coefficients (GfsDomain * domain,
   }
   gfs_domain_cell_traverse (domain,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) reset_coeff, NULL);
+			    (FttCellTraverseFunc) reset_poisson_coeff, dia);
   if (c == NULL || rho == 1.)
     gfs_domain_face_traverse (domain, FTT_XYZ, 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
@@ -433,13 +447,14 @@ void gfs_poisson_coefficients (GfsDomain * domain,
   }
   gfs_domain_cell_traverse (domain,
 			    FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-			    (FttCellTraverseFunc) face_coeff_from_below, 
-			    NULL);
+			    (FttCellTraverseFunc) face_poisson_coeff_from_below, dia);
 }
 
-static void correct (FttCell * cell, GfsVariable * u) 
+static void correct (FttCell * cell, gpointer * data)
 {
-  GFS_VARIABLE (cell, u->i) += GFS_STATE (cell)->dp;
+  GfsVariable * u = data[0];
+  GfsVariable * dp = data[1];
+  GFS_VARIABLE (cell, u->i) += GFS_VARIABLE (cell, dp->i);
 }
 
 /**
@@ -451,17 +466,19 @@ static void correct (FttCell * cell, GfsVariable * u)
  * @nrelax: the number of relaxations to apply at each level.
  * @u: the variable to use as left-hand side.
  * @rhs: the variable to use as right-hand side.
+ * @dia: the diagonal weight.
+ * @res: the residual.
  *
  * Apply one multigrid iteration to the Poisson equation defined by @u
  * and @rhs.
  *
- * The initial value of %GFS_RES on the leaves of @root must be set to
+ * The initial value of @res on the leaves of @root must be set to
  * the residual of the Poisson equation (using gfs_residual()).
  *
  * The face coefficients must be set using gfs_poisson_coefficients().
  *
  * The values of @u on the leaf cells are updated as well as the values
- * of %GFS_RES (i.e. the cell tree is ready for another iteration).
+ * of @res (i.e. the cell tree is ready for another iteration).
  */
 void gfs_poisson_cycle (GfsDomain * domain,
 			guint d,
@@ -469,30 +486,37 @@ void gfs_poisson_cycle (GfsDomain * domain,
 			guint depth,
 			guint nrelax,
 			GfsVariable * u,
-			GfsVariable * rhs)
+			GfsVariable * rhs,
+			GfsVariable * dia,
+			GfsVariable * res)
 {
   guint n, l;
-
+  GfsVariable * dp;
+  gpointer data[2];
+  
   g_return_if_fail (domain != NULL);
   g_return_if_fail (d > 1 && d <= 3);
   g_return_if_fail (u != NULL);
   g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
+  g_return_if_fail (res != NULL);
+
+  dp = gfs_temporary_variable (domain);
 
   /* compute residual on non-leafs cells */
   gfs_domain_cell_traverse (domain, 
 			    FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_get_from_below_extensive, 
-			    gfs_res);
+			    (FttCellTraverseFunc) gfs_get_from_below_extensive, res);
 
   /* relax top level */
   gfs_domain_cell_traverse (domain, 
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEVEL, levelmin,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_dp);
+			    (FttCellTraverseFunc) gfs_cell_reset, dp);
   for (n = 0; n < 10*nrelax; n++) {
     gfs_domain_homogeneous_bc (domain,
 			       FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS,
-			       levelmin, gfs_dp, u);
-    gfs_relax (domain, d, levelmin, gfs_dp, gfs_res);
+			       levelmin, dp, u);
+    gfs_relax (domain, d, levelmin, dp, res, dia);
   }
 
   /* relax from top to bottom */
@@ -500,21 +524,24 @@ void gfs_poisson_cycle (GfsDomain * domain,
     /* get initial guess from coarser grid */ 
     gfs_domain_cell_traverse (domain, 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEVEL, l,
-			      (FttCellTraverseFunc) gfs_get_from_above, 
-			      gfs_dp);
+			      (FttCellTraverseFunc) gfs_get_from_above, dp);
     for (n = 0; n < nrelax; n++) {
       gfs_domain_homogeneous_bc (domain, 
 				 FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS,
-				 l, gfs_dp, u);
-      gfs_relax (domain, d, l, gfs_dp, gfs_res);
+				 l, dp, u);
+      gfs_relax (domain, d, l, dp, res, dia);
     }
   }
   /* correct on leaf cells */
+  data[0] = u;
+  data[1] = dp;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) correct, u);
+			    (FttCellTraverseFunc) correct, data);
   gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, u);
   /* compute new residual on leaf cells */
-  gfs_residual (domain, d, FTT_TRAVERSE_LEAFS, -1, u, rhs, gfs_res);
+  gfs_residual (domain, d, FTT_TRAVERSE_LEAFS, -1, u, rhs, dia, res);
+
+  gts_object_destroy (GTS_OBJECT (dp));
 }
 
 static void diffusion_coef (FttCellFace * face, gpointer * data)
@@ -551,7 +578,7 @@ static void diffusion_mixed_coef (FttCell * cell, gpointer * data)
 
     GFS_STATE (cell)->solid->v = *dt*gfs_source_diffusion_cell (d, cell);
   }
-  GFS_STATE (cell)->g[0] = 1.;
+  GFS_VARIABLE (cell, GFS_VARIABLE1 (data[3])->i) = 1.;
 }
 
 /**
@@ -559,19 +586,22 @@ static void diffusion_mixed_coef (FttCell * cell, gpointer * data)
  * @domain: a #GfsDomain.
  * @d: a #GfsSourceDiffusion.
  * @dt: the time-step.
+ * @dia: where to store the diagonal weight.
  *
  * Initializes the face coefficients for the diffusion equation.
  */
 void gfs_diffusion_coefficients (GfsDomain * domain,
 				 GfsSourceDiffusion * d,
-				 gdouble dt)
+				 gdouble dt,
+				 GfsVariable * dia)
 {
   gdouble lambda2[FTT_DIMENSION];
-  gpointer data[3];
+  gpointer data[4];
   FttComponent i;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (d != NULL);
+  g_return_if_fail (dia != NULL);
 
   for (i = 0; i < FTT_DIMENSION; i++) {
     gdouble lambda = (&domain->lambda.x)[i];
@@ -581,6 +611,7 @@ void gfs_diffusion_coefficients (GfsDomain * domain,
   data[0] = d;
   data[1] = lambda2;
   data[2] = &dt;
+  data[3] = dia;
   gfs_domain_cell_traverse (domain,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
 			    (FttCellTraverseFunc) diffusion_mixed_coef, data);
@@ -599,7 +630,7 @@ static void density (FttCell * cell, gpointer * data)
   gdouble c = GFS_VARIABLE (cell, v->i);
   gdouble * rho = data[1];
 
-  GFS_STATE (cell)->g[0] = 1. + *rho*(c > 1. ? 1. : c < 0. ? 0. : c);
+  GFS_VARIABLE (cell, GFS_VARIABLE1 (data[2])->i) = 1. + *rho*(c > 1. ? 1. : c < 0. ? 0. : c);
 }
 
 /**
@@ -609,6 +640,7 @@ static void density (FttCell * cell, gpointer * data)
  * @dt: the time-step.
  * @c: the volume fraction (at t+dt/2).
  * @rho: the relative density.
+ * @dia: where to store the diagonal weight.
  *
  * Initializes the face coefficients for the diffusion equation for
  * the velocity.
@@ -617,75 +649,92 @@ void gfs_viscosity_coefficients (GfsDomain * domain,
 				 GfsSourceDiffusion * d,
 				 gdouble dt,
 				 GfsVariable * c,
-				 gdouble rho)
+				 gdouble rho,
+				 GfsVariable * dia)
 {
   g_return_if_fail (domain != NULL);
   g_return_if_fail (d != NULL);
   g_return_if_fail (c != NULL);
+  g_return_if_fail (dia != NULL);
 
-  gfs_diffusion_coefficients (domain, d, dt);
+  gfs_diffusion_coefficients (domain, d, dt, dia);
   if (rho != 1.) {
-    gpointer data[2];
+    gpointer data[3];
 
     rho -= 1.;
     data[0] = c;
     data[1] = &rho;
+    data[2] = dia;
     gfs_domain_cell_traverse (domain,
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			      (FttCellTraverseFunc) density, data);
     gfs_domain_cell_traverse (domain,
 			      FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-			      (FttCellTraverseFunc) gfs_get_from_below_intensive, 
-			      gfs_gx);
+			      (FttCellTraverseFunc) gfs_get_from_below_intensive, dia);
   }
 }
 
-/**
- * gfs_diffusion_rhs:
- * @cell: a #FttCell.
- * @v: a #GfsVariable.
- *
- * Adds to the @div variable of @cell the right-hand side of the
- * diffusion equation for variable @v.
- *
- * The diffusion coefficients must have been already set using
- * gfs_diffusion_coefficients().
- */
-void gfs_diffusion_rhs (FttCell * cell, GfsVariable * v)
+static void diffusion_rhs (FttCell * cell, RelaxParams * p)
 {
   gdouble a, f, h, val;
   FttCellNeighbors neighbor;
   FttCellFace face;
   
-  g_return_if_fail (cell != NULL);
-  g_return_if_fail (v != NULL);
-
   if (GFS_IS_MIXED (cell)) {
-    a = GFS_STATE (cell)->solid->a*GFS_STATE (cell)->g[0];
+    a = GFS_STATE (cell)->solid->a*GFS_VARIABLE (cell, p->dia);
     if (((cell)->flags & GFS_FLAG_DIRICHLET) != 0)
-      f = gfs_cell_dirichlet_gradient_flux (cell, v->i, -1, GFS_STATE (cell)->solid->fv);
+      f = gfs_cell_dirichlet_gradient_flux (cell, p->u, -1, GFS_STATE (cell)->solid->fv);
     else
       f = GFS_STATE (cell)->solid->fv;
   }
   else {
-    a = GFS_STATE (cell)->g[0];
+    a = GFS_VARIABLE (cell, p->dia);
     f = 0.; /* Neumann condition by default */
   }
   h = ftt_cell_size (cell);
-  val = GFS_VARIABLE (cell, v->i);
+  val = GFS_VARIABLE (cell, p->u);
   face.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
   for (face.d = 0; face.d < FTT_NEIGHBORS; face.d++) {
     GfsGradient g;
 
     face.neighbor = neighbor.c[face.d];
-    gfs_face_gradient_flux (&face, &g, v->i, -1);
+    gfs_face_gradient_flux (&face, &g, p->u, -1);
     f += g.b - g.a*val;
   }
-  GFS_STATE (cell)->div += val + f/(2.*h*h*a);
+  GFS_VARIABLE (cell, p->rhs) += val + f/(2.*h*h*a);
+}
+
+/**
+ * gfs_diffusion_rhs:
+ * @domain: a #GfsDomain.
+ * @v: a #GfsVariable.
+ * @rhs: a #GfsVariable.
+ * @dia: the diagonal weight.
+ *
+ * Adds to the @rhs variable of @cell the right-hand side of the
+ * diffusion equation for variable @v.
+ *
+ * The diffusion coefficients must have been already set using
+ * gfs_diffusion_coefficients().
+ */
+void gfs_diffusion_rhs (GfsDomain * domain, GfsVariable * v, GfsVariable * rhs, GfsVariable * dia)
+{
+  RelaxParams p;
+
+  g_return_if_fail (domain != NULL);
+  g_return_if_fail (v != NULL);
+  g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
+
+  p.u = v->i;
+  p.rhs = rhs->i;
+  p.dia = dia->i;
+  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
+			    (FttCellTraverseFunc) diffusion_rhs, &p);
 }
 
-static void diffusion_relax (FttCell * cell, gint * maxlevel)
+static void diffusion_relax (FttCell * cell, RelaxParams * p)
 {
   gdouble a;
   GfsGradient g = { 0., 0. };
@@ -694,12 +743,12 @@ static void diffusion_relax (FttCell * cell, gint * maxlevel)
   FttCellFace face;
 
   if (GFS_IS_MIXED (cell)) {
-    a = GFS_STATE (cell)->solid->a*GFS_STATE (cell)->g[0];
+    a = GFS_STATE (cell)->solid->a*GFS_VARIABLE (cell, p->dia);
     if (((cell)->flags & GFS_FLAG_DIRICHLET) != 0)
-      g.b = gfs_cell_dirichlet_gradient_flux (cell, gfs_dp->i, *maxlevel, 0.);
+      g.b = gfs_cell_dirichlet_gradient_flux (cell, p->u, p->maxlevel, 0.);
   }
   else
-    a = GFS_STATE (cell)->g[0];
+    a = GFS_VARIABLE (cell, p->dia);
 
   face.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
@@ -707,31 +756,19 @@ static void diffusion_relax (FttCell * cell, gint * maxlevel)
     GfsGradient ng;
 
     face.neighbor = neighbor.c[face.d];
-    gfs_face_gradient_flux (&face, &ng, gfs_dp->i, *maxlevel);
+    gfs_face_gradient_flux (&face, &ng, p->u, p->maxlevel);
     g.a += ng.a;
     g.b += ng.b;
   }
   a *= 2.*h*h;
   g_assert (a > 0.);
   g.a = 1. + g.a/a;
-  g.b = GFS_STATE (cell)->res + g.b/a;
+  g.b = GFS_VARIABLE (cell, p->res) + g.b/a;
   g_assert (g.a > 0.);
-  GFS_STATE (cell)->dp = g.b/g.a;
+  GFS_VARIABLE (cell, p->u) = g.b/g.a;
 }
 
-/**
- * gfs_diffusion_residual:
- * @cell: a #FttCell.
- * @v: a #GfsVariable.
- *
- * Sets the @res variable of @cell to the residual of the diffusion
- * equation for @v.
- *
- * The diffusion coefficients must have been set using
- * gfs_diffusion_coefficients() and the right-hand side using
- * gfs_diffusion_rhs().
- */
-void gfs_diffusion_residual (FttCell * cell, GfsVariable * v)
+static void diffusion_residual (FttCell * cell, RelaxParams * p)
 {
   gdouble a;
   GfsGradient g = { 0., 0. };
@@ -739,20 +776,16 @@ void gfs_diffusion_residual (FttCell * cell, GfsVariable * v)
   FttCellNeighbors neighbor;
   FttCellFace face;
 
-  g_return_if_fail (cell != NULL);
-  g_return_if_fail (v != NULL);
-
   h = ftt_cell_size (cell);
   if (GFS_IS_MIXED (cell)) {
-    a = GFS_STATE (cell)->solid->a*GFS_STATE (cell)->g[0];
+    a = GFS_STATE (cell)->solid->a*GFS_VARIABLE (cell, p->dia);
     if (((cell)->flags & GFS_FLAG_DIRICHLET) != 0)
-      g.b = gfs_cell_dirichlet_gradient_flux (cell, v->i, -1, 
-					      GFS_STATE (cell)->solid->fv);
+      g.b = gfs_cell_dirichlet_gradient_flux (cell, p->u, -1, GFS_STATE (cell)->solid->fv);
     else
       g.b = GFS_STATE (cell)->solid->fv;
   }
   else
-    a = GFS_STATE (cell)->g[0];
+    a = GFS_VARIABLE (cell, p->dia);
 
   face.cell = cell;
   ftt_cell_neighbors (cell, &neighbor);
@@ -760,15 +793,48 @@ void gfs_diffusion_residual (FttCell * cell, GfsVariable * v)
     GfsGradient ng;
 
     face.neighbor = neighbor.c[face.d];
-    gfs_face_gradient_flux (&face, &ng, v->i, -1);
+    gfs_face_gradient_flux (&face, &ng, p->u, -1);
     g.a += ng.a;
     g.b += ng.b;
   }
   a *= 2.*h*h;
   g_assert (a > 0.);
   g.a = 1. + g.a/a;
-  g.b = GFS_STATE (cell)->div + g.b/a;
-  GFS_STATE (cell)->res = g.b - g.a*GFS_VARIABLE (cell, v->i);
+  g.b = GFS_VARIABLE (cell, p->rhs) + g.b/a;
+  GFS_VARIABLE (cell, p->res) = g.b - g.a*GFS_VARIABLE (cell, p->u);
+}
+
+/**
+ * gfs_diffusion_residual:
+ * @domain: a #GfsDomain.
+ *
+ * Sets the @res variable of each leaf cell of @domain to the residual
+ * of the diffusion equation for @v.
+ *
+ * The diffusion coefficients must have been set using
+ * gfs_diffusion_coefficients() and the right-hand side using
+ * gfs_diffusion_rhs().
+ */
+void gfs_diffusion_residual (GfsDomain * domain,
+			     GfsVariable * u,
+			     GfsVariable * rhs,
+			     GfsVariable * dia,
+			     GfsVariable * res)
+{
+  RelaxParams p;
+
+  g_return_if_fail (domain != NULL);
+  g_return_if_fail (u != NULL);
+  g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
+  g_return_if_fail (res != NULL);
+
+  p.u = u->i;
+  p.rhs = rhs->i;
+  p.dia = dia->i;
+  p.res = res->i;
+  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
+			    (FttCellTraverseFunc) diffusion_residual, &p);
 }
 
 /**
@@ -778,71 +844,89 @@ void gfs_diffusion_residual (FttCell * cell, GfsVariable * v)
  * @depth: the total depth of the domain.
  * @nrelax: the number of relaxations to apply at each level.
  * @u: the variable to use as left-hand side.
+ * @rhs: the right-hand side.
+ * @dia: the diagonal weight.
+ * @res: the residual.
  *
  * Apply one multigrid iteration to the diffusion equation for @u.
  *
- * The initial value of %GFS_RES on the leaves of @root must be set to
+ * The initial value of @res on the leaves of @root must be set to
  * the residual of the diffusion equation using gfs_diffusion_residual().
  *
  * The diffusion coefficients must be set using gfs_diffusion_coefficients().
  *
  * The values of @u on the leaf cells are updated as well as the values
- * of %GFS_RES (i.e. the cell tree is ready for another iteration).
+ * of @res (i.e. the cell tree is ready for another iteration).
  */
 void gfs_diffusion_cycle (GfsDomain * domain,
 			  guint levelmin,
 			  guint depth,
 			  guint nrelax,
-			  GfsVariable * u)
+			  GfsVariable * u,
+			  GfsVariable * rhs,
+			  GfsVariable * dia,
+			  GfsVariable * res)
 {
-  gint n, l;
+  guint n;
+  GfsVariable * dp;
+  RelaxParams p;
+  gpointer data[2];
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (u != NULL);
+  g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
+  g_return_if_fail (res != NULL);
+
+  dp = gfs_temporary_variable (domain);
 
   /* compute residual on non-leafs cells */
   gfs_domain_cell_traverse (domain, 
 			    FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_get_from_below_intensive, 
-			    gfs_res);
+			    (FttCellTraverseFunc) gfs_get_from_below_intensive, res);
 
   /* relax top level */
   gfs_domain_cell_traverse (domain, 
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEVEL, levelmin,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_dp);
-  l = levelmin;
+			    (FttCellTraverseFunc) gfs_cell_reset, dp);
+  p.maxlevel = levelmin;
+  p.u = dp->i;
+  p.res = res->i;
+  p.dia = dia->i;
   for (n = 0; n < 10*nrelax; n++) {
     gfs_domain_homogeneous_bc (domain, 
 			       FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS,
-			       levelmin, gfs_dp, u);
+			       levelmin, dp, u);
     gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, 
 			      FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS, 
 			      levelmin,
-			      (FttCellTraverseFunc) diffusion_relax, &l);
+			      (FttCellTraverseFunc) diffusion_relax, &p);
   }
 
   /* relax from top to bottom */
-  for (l = levelmin + 1; l <= depth; l++) {
+  for (p.maxlevel = levelmin + 1; p.maxlevel <= depth; p.maxlevel++) {
     /* get initial guess from coarser grid */ 
     gfs_domain_cell_traverse (domain, 
-			      FTT_PRE_ORDER, FTT_TRAVERSE_LEVEL, l,
-			      (FttCellTraverseFunc) gfs_get_from_above, 
-			      gfs_dp);
+			      FTT_PRE_ORDER, FTT_TRAVERSE_LEVEL, p.maxlevel,
+			      (FttCellTraverseFunc) gfs_get_from_above, dp);
     for (n = 0; n < nrelax; n++) {
       gfs_domain_homogeneous_bc (domain, 
 				 FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS,
-				 l, gfs_dp, u);
+				 p.maxlevel, dp, u);
       gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, 
-				FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS, l,
-				(FttCellTraverseFunc) diffusion_relax, &l);
+				FTT_TRAVERSE_LEVEL | FTT_TRAVERSE_LEAFS, p.maxlevel,
+				(FttCellTraverseFunc) diffusion_relax, &p);
     }
   }
   /* correct on leaf cells */
+  data[0] = u;
+  data[1] = dp;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) correct, u);
+			    (FttCellTraverseFunc) correct, data);
   gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, u);
   /* compute new residual on leaf cells */
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_diffusion_residual, u);
+  gfs_diffusion_residual (domain, u, rhs, dia, res);
+
+  gts_object_destroy (GTS_OBJECT (dp));
 }
 
diff --git a/src/poisson.h b/src/poisson.h
index 61e26c5..2ffb11a 100644
--- a/src/poisson.h
+++ b/src/poisson.h
@@ -33,15 +33,18 @@ void                  gfs_relax                      (GfsDomain * domain,
 						      guint d,
 						      gint max_depth,
 						      GfsVariable * u,
-						      GfsVariable * rhs);
+						      GfsVariable * rhs,
+						      GfsVariable * dia);
 void                  gfs_residual                   (GfsDomain * domain,
 						      guint d,
 						      FttTraverseFlags flags,
 						      gint max_depth,
 						      GfsVariable * u,
 						      GfsVariable * rhs,
+						      GfsVariable * dia,
 						      GfsVariable * res);
 void                  gfs_poisson_coefficients       (GfsDomain * domain,
+						      GfsVariable * dia,
 						      GfsVariable * c,
 						      gdouble rho);
 void                  gfs_poisson_cycle              (GfsDomain * domain,
@@ -50,25 +53,37 @@ void                  gfs_poisson_cycle              (GfsDomain * domain,
 						      guint depth,
 						      guint nrelax,
 						      GfsVariable * u,
-						      GfsVariable * rhs);
+						      GfsVariable * rhs,
+						      GfsVariable * dia,
+						      GfsVariable * res);
 
 void                  gfs_diffusion_coefficients     (GfsDomain * domain,
 						      GfsSourceDiffusion * d,
-						      gdouble dt);
+						      gdouble dt,
+						      GfsVariable * dia);
 void                  gfs_viscosity_coefficients     (GfsDomain * domain,
 						      GfsSourceDiffusion * d,
 						      gdouble dt,
 						      GfsVariable * c,
-						      gdouble rho);
-void                  gfs_diffusion_rhs              (FttCell * cell, 
-						      GfsVariable * v);
-void                  gfs_diffusion_residual         (FttCell * cell, 
-						      GfsVariable * v);
+						      gdouble rho,
+						      GfsVariable * dia);
+void                  gfs_diffusion_rhs              (GfsDomain * domain,
+						      GfsVariable * v,
+						      GfsVariable * rhs,
+						      GfsVariable * dia);
+void                  gfs_diffusion_residual         (GfsDomain * domain,
+						      GfsVariable * u,
+						      GfsVariable * rhs,
+						      GfsVariable * dia,
+						      GfsVariable * res);
 void                  gfs_diffusion_cycle            (GfsDomain * domain,
 						      guint levelmin,
 						      guint depth,
 						      guint nrelax,
-						      GfsVariable * u);
+						      GfsVariable * u,
+						      GfsVariable * rhs,
+						      GfsVariable * dia,
+						      GfsVariable * res);
 
 #ifdef __cplusplus
 }
diff --git a/src/refine.c b/src/refine.c
index 4eab428..9c0d940 100644
--- a/src/refine.c
+++ b/src/refine.c
@@ -26,10 +26,7 @@
 
 static gboolean refine_maxlevel (FttCell * cell, GfsFunction * maxlevel)
 {
-  FttVector p;
-
-  ftt_cell_pos (cell, &p);
-  return (ftt_cell_level (cell) < gfs_function_value (maxlevel, cell, &p, 0.));
+  return (ftt_cell_level (cell) < gfs_function_value (maxlevel, cell));
 }
 
 static void refine_box (GfsBox * box, GfsFunction * maxlevel)
@@ -143,10 +140,8 @@ static void refine_cut_cell (FttCell * cell, GtsSurface * s, gpointer * data)
 {
   GfsRefine * refine = data[0];
   GfsDomain * domain = data[1];
-  FttVector p;
 
-  ftt_cell_pos (cell, &p);
-  if (ftt_cell_level (cell) < gfs_function_value (refine->maxlevel, cell, &p, 0.))
+  if (ftt_cell_level (cell) < gfs_function_value (refine->maxlevel, cell))
     ftt_cell_refine_single (cell, (FttCellInitFunc) gfs_cell_fine_init, domain);
 }
 
@@ -318,49 +313,47 @@ static void refine_distance_destroy (GtsObject * object)
   GfsRefineDistance * d = GFS_REFINE_DISTANCE (object);
 
   if (d->stree)
-    gts_bb_tree_destroy (d->stree, TRUE);  
+    gts_bb_tree_destroy (d->stree, TRUE);
+  gfs_simulation_remove_derived_variable (gfs_object_simulation (object), "Distance");
 
   (* GTS_OBJECT_CLASS (gfs_refine_distance_class ())->parent_class->destroy) (object);
 }
 
-static void refine_distance_read (GtsObject ** o, GtsFile * fp)
-{
-  (* GTS_OBJECT_CLASS (gfs_refine_distance_class ())->parent_class->read) (o, fp);
-  if (fp->type == GTS_ERROR)
-    return;
-
-  GFS_REFINE_DISTANCE (*o)->stree = gts_bb_tree_surface (GFS_REFINE_SURFACE (*o)->surface);
-}
-
-static gboolean refine_distance_maxlevel (FttCell * cell, GfsRefine * refine)
+static gdouble cell_distance (FttCell * cell, 
+			      FttCellFace * face, 
+			      GfsSimulation * sim,
+			      GfsRefineDistance * refine)
 {
   FttVector pos;
   GtsPoint p;
-  gdouble d;
 
   ftt_cell_pos (cell, &pos);
   p.x = pos.x; p.y = pos.y; p.z = pos.z;
-  d = gts_bb_tree_point_distance (GFS_REFINE_DISTANCE (refine)->stree, &p,
-				  (GtsBBoxDistFunc) gts_point_triangle_distance, NULL);
-  return (ftt_cell_level (cell) < gfs_function_value (refine->maxlevel, cell, &pos, d));
+  return gts_bb_tree_point_distance (refine->stree, &p,
+				     (GtsBBoxDistFunc) gts_point_triangle_distance, NULL);
 }
 
-static void refine_distance (GfsBox * box, gpointer data)
+static void refine_distance_read (GtsObject ** o, GtsFile * fp)
 {
-  ftt_cell_refine (box->root, 
-		   (FttCellRefineFunc) refine_distance_maxlevel, data,
-		   (FttCellInitFunc) gfs_cell_fine_init, gfs_box_domain (box));
-}
+  GfsDerivedVariable v = { "Distance", cell_distance };
 
-static void refine_distance_refine (GfsRefine * refine, GfsSimulation * sim)
-{
-  gts_container_foreach (GTS_CONTAINER (sim), (GtsFunc) refine_distance, refine);
+  v.data = *o;
+  if (!gfs_simulation_add_derived_variable (gfs_object_simulation (*o), v)) {
+    gts_file_error (fp, "derived variable `Distance' already defined");
+    return;
+  }
+
+  (* GTS_OBJECT_CLASS (gfs_refine_distance_class ())->parent_class->read) (o, fp);
+  if (fp->type == GTS_ERROR)
+    return;
+
+  GFS_REFINE_DISTANCE (*o)->stree = gts_bb_tree_surface (GFS_REFINE_SURFACE (*o)->surface);
 }
 
 static void gfs_refine_distance_class_init (GfsRefineClass * klass)
 {
-  klass->refine = refine_distance_refine;
-  
+  klass->refine = gfs_refine_refine;
+
   GTS_OBJECT_CLASS (klass)->destroy = refine_distance_destroy;
   GTS_OBJECT_CLASS (klass)->read = refine_distance_read;
 }
@@ -388,77 +381,72 @@ GfsRefineClass * gfs_refine_distance_class (void)
 
 /* GfsRefineHeight: Object */
 
-static void refine_height_read (GtsObject ** o, GtsFile * fp)
+static void refine_height_destroy (GtsObject * object)
 {
-  (* GTS_OBJECT_CLASS (gfs_refine_distance_class ())->parent_class->read) (o, fp);
-  if (fp->type == GTS_ERROR)
-    return;
-  
-  if (gts_surface_volume (GFS_REFINE_SURFACE (*o)->surface) < 0.)
-    gts_surface_foreach_face (GFS_REFINE_SURFACE (*o)->surface, 
-			      (GtsFunc) gts_triangle_revert, NULL);
+  gfs_simulation_remove_derived_variable (gfs_object_simulation (object), "Height");
+
+  (* GTS_OBJECT_CLASS (gfs_refine_height_class ())->parent_class->destroy) (object);
 }
 
-static gboolean height_maxlevel (GtsPoint * p, guint level, GfsRefine * refine, GtsFace ** guess)
+static gdouble point_height (GtsPoint * p, GtsSurface * surface, GtsFace ** guess)
 {
-  GtsFace * f = gts_point_locate (p, GFS_REFINE_SURFACE (refine)->surface, *guess);
+  GtsFace * f = gts_point_locate (p, surface, *guess);
 
   if (f != NULL) {
-    FttVector pos;
-
     *guess = f;
     gts_triangle_interpolate_height (GTS_TRIANGLE (f), p);
-    pos.x = p->x; pos.y = p->y; pos.z = p->z;
-    return (level < gfs_function_value (refine->maxlevel, NULL, &pos, p->z));
+    return p->z;
   }
-  return FALSE;
+  return 0.;
 }
 
-static gboolean refine_height_maxlevel (FttCell * cell, gpointer * data)
+static gdouble cell_height (FttCell * cell, 
+			    FttCellFace * face, 
+			    GfsSimulation * sim,
+			    GfsRefineSurface * refine)
 {
-  GfsRefine * refine = data[0];
-  GtsFace ** guess = data[1];
-  guint level = ftt_cell_level (cell);
+  GtsFace * guess = NULL;
   gdouble h = ftt_cell_size (cell)/2.;
   FttVector pos;
   GtsPoint p;
+  static guint dp[4][2] = {{ -1, -1}, {1, -1}, {1, 1}, {-1, 1}}, i;
+  gdouble min = G_MAXDOUBLE;
 
   ftt_cell_pos (cell, &pos);
-  p.x = pos.x - h; p.y = pos.y - h;
-  if (height_maxlevel (&p, level, refine, guess))
-    return TRUE;
-  p.x = pos.x + h; p.y = pos.y - h;
-  if (height_maxlevel (&p, level, refine, guess))
-    return TRUE;
-  p.x = pos.x + h; p.y = pos.y + h;
-  if (height_maxlevel (&p, level, refine, guess))
-    return TRUE;
-  p.x = pos.x - h; p.y = pos.y + h;
-  if (height_maxlevel (&p, level, refine, guess))
-    return TRUE;
-  return FALSE;
-}
-
-static void refine_height (GfsBox * box, gpointer data)
-{
-  gpointer datum[2];
-  GtsFace * guess = NULL;
-
-  datum[0] = data;
-  datum[1] = &guess;
-  ftt_cell_refine (box->root, 
-		   (FttCellRefineFunc) refine_height_maxlevel, datum,
-		   (FttCellInitFunc) gfs_cell_fine_init, gfs_box_domain (box));
+  for (i = 0; i < 4; i++) {
+    gdouble v;
+    p.x = pos.x + h*dp[i][0]; p.y = pos.y + h*dp[i][1];
+    v = point_height (&p, refine->surface, &guess);
+    if (v < min)
+      min = v;
+  }
+  return min;
 }
 
-static void refine_height_refine (GfsRefine * refine, GfsSimulation * sim)
+static void refine_height_read (GtsObject ** o, GtsFile * fp)
 {
-  gts_container_foreach (GTS_CONTAINER (sim), (GtsFunc) refine_height, refine);
+  GfsDerivedVariable v = { "Height", cell_height };
+
+  v.data = *o;
+  if (!gfs_simulation_add_derived_variable (gfs_object_simulation (*o), v)) {
+    gts_file_error (fp, "derived variable `Height' already defined");
+    return;
+  }
+
+  (* GTS_OBJECT_CLASS (gfs_refine_distance_class ())->parent_class->read) (o, fp);
+  if (fp->type == GTS_ERROR)
+    return;
+  
+  if (gts_surface_volume (GFS_REFINE_SURFACE (*o)->surface) < 0.)
+    gts_surface_foreach_face (GFS_REFINE_SURFACE (*o)->surface, 
+			      (GtsFunc) gts_triangle_revert, NULL);
 }
 
 static void gfs_refine_height_class_init (GfsRefineClass * klass)
 {
-  klass->refine = refine_height_refine;
+  klass->refine = gfs_refine_refine;
+
+  GTS_OBJECT_CLASS (klass)->destroy = refine_height_destroy;
   GTS_OBJECT_CLASS (klass)->read = refine_height_read;
 }
 
diff --git a/src/simulation.c b/src/simulation.c
index e5590c7..a94346c 100644
--- a/src/simulation.c
+++ b/src/simulation.c
@@ -59,6 +59,8 @@ static void simulation_destroy (GtsObject * object)
     i = i->next;
   }
   g_slist_free (sim->modules);
+  g_slist_foreach (sim->derived_variables, (GFunc) g_free, NULL);
+  g_slist_free (sim->derived_variables);
   g_slist_free (sim->variables);
 
   (* GTS_OBJECT_CLASS (gfs_simulation_class ())->parent_class->destroy) 
@@ -101,14 +103,15 @@ static void simulation_write (GtsObject * object, FILE * fp)
     i = i->next;
   }
 
-  v = GFS_DOMAIN (sim)->variables;
-  while (v) {
+  i = GFS_DOMAIN (sim)->variables;
+  while (i) {
+    v = i->data;
     if (v->surface_bc) {
       fputs ("  ", fp);
       (* GTS_OBJECT (v->surface_bc)->klass->write) (GTS_OBJECT (v->surface_bc), fp);
       fputc ('\n', fp);
     }
-    v = v->next;
+    i = i->next;
   }
 
   if (GFS_DOMAIN (sim)->max_depth_write < -1) {
@@ -295,9 +298,7 @@ static void simulation_read (GtsObject ** object, GtsFile * fp)
 {
   GfsSimulation * sim = GFS_SIMULATION (*object);
   
-  if (GTS_OBJECT_CLASS (gfs_simulation_class ())->parent_class->read)
-    (* GTS_OBJECT_CLASS (gfs_simulation_class ())->parent_class->read)
-      (object, fp);
+  (* GTS_OBJECT_CLASS (gfs_simulation_class ())->parent_class->read) (object, fp);
   if (fp->type == GTS_ERROR)
     return;
 
@@ -467,19 +468,6 @@ static void simulation_read (GtsObject ** object, GtsFile * fp)
       else if (GFS_IS_ADAPT (object))
 	gts_container_add (GTS_CONTAINER (sim->adapts),
 			   GTS_CONTAINEE (object));
-      else if (GFS_IS_VARIABLE (object)) {
-	GfsVariable * v = GFS_VARIABLE1 (object);
-	GfsVariable * old = gfs_variable_from_name (GFS_DOMAIN (sim)->variables, v->name);
-
-	if (old == NULL)
-	  gfs_domain_add_new_variable (GFS_DOMAIN (sim), v);
-	else {
-	  gfs_domain_replace_variable (GFS_DOMAIN (sim), old, v);
-	  gts_object_destroy (GTS_OBJECT (old));
-	  sim->variables = g_slist_remove (sim->variables, old);
-	}
-	sim->variables = g_slist_append (sim->variables, v);
-      }
       else if (GFS_IS_EVENT (object))
 	gts_container_add (GTS_CONTAINER (sim->events), 
 			   GTS_CONTAINEE (object));
@@ -513,41 +501,44 @@ static void simulation_read (GtsObject ** object, GtsFile * fp)
 
 static void simulation_run (GfsSimulation * sim)
 {
-  GfsVariable * v, * c, * ch;
+  GfsVariable * p, * res = NULL;
   GfsDomain * domain;
+  GSList * i;
 
   domain = GFS_DOMAIN (sim);
 
+  p = gfs_variable_from_name (domain->variables, "P");
+  g_assert (p);
+
   gfs_simulation_refine (sim);
 
   gts_container_foreach (GTS_CONTAINER (sim->events), (GtsFunc) gfs_event_init, sim);
   gts_container_foreach (GTS_CONTAINER (sim->adapts), (GtsFunc) gfs_event_init, sim);
 
   gfs_set_merged (domain);
-  v = domain->variables;
-  while (v) {
-    gfs_event_init (GFS_EVENT (v), sim);
-    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v);
-    v = v->next;
+  i = domain->variables;
+  while (i) {
+    gfs_event_init (GFS_EVENT (i->data), sim);
+    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, i->data);
+    if (GFS_IS_VARIABLE_RESIDUAL (i->data))
+      res = i->data;
+    i = i->next;
   }
-  c = gfs_variable_from_name (domain->variables, "C");
-  g_assert (c == NULL);
-  ch = gfs_variable_from_name (domain->variables, "CH");
-  g_assert (ch == NULL);
 
-  sim->advection_params.c = c;
   gfs_approximate_projection (domain,
       			      &sim->approx_projection_params,
-      			      &sim->advection_params);
+      			      &sim->advection_params,
+			      p, res);
 
   while (sim->time.t < sim->time.end &&
 	 sim->time.i < sim->time.iend) {
+    GfsVariable * g[FTT_DIMENSION];
     gdouble tstart;
 
-    v = domain->variables;
-    while (v) {
-      gfs_event_do (GFS_EVENT (v), sim);
-      v = v->next;
+    i = domain->variables;
+    while (i) {
+      gfs_event_do (GFS_EVENT (i->data), sim);
+      i = i->next;
     }
     gfs_domain_cell_traverse (domain,
 			      FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
@@ -558,17 +549,17 @@ static void simulation_run (GfsSimulation * sim)
 
     gfs_simulation_set_timestep (sim);
 
-    sim->advection_params.c = c;
-
     gfs_predicted_face_velocities (domain, FTT_DIMENSION, &sim->advection_params);
-    gfs_mac_projection (domain, 
+    
+    gfs_mac_projection (domain,
     			&sim->projection_params, 
-    			&sim->advection_params);
+    			&sim->advection_params,
+			p, g);
 
-    v = domain->variables;
-    while (v) {
-      if (GFS_IS_VARIABLE_TRACER (v)) {
-	GfsVariableTracer * t = GFS_VARIABLE_TRACER (v);
+    i = domain->variables;
+    while (i) {
+      if (GFS_IS_VARIABLE_TRACER (i->data)) {
+	GfsVariableTracer * t = i->data;
 
 	t->advection.dt = sim->advection_params.dt;
 	switch (t->advection.scheme) {
@@ -577,26 +568,27 @@ static void simulation_run (GfsSimulation * sim)
 	  break;
 	case GFS_VOF:
 	  gfs_tracer_vof_advection (domain, &t->advection, NULL);
-	  gfs_domain_variable_centered_sources (domain, v, v, t->advection.dt);
+	  gfs_domain_variable_centered_sources (domain, i->data, i->data, t->advection.dt);
 	  break;
 	case GFS_NONE:
 	  break;
 	}
       }
-      v = v->next;
+      i = i->next;
     }
 
     gts_container_foreach (GTS_CONTAINER (sim->events), (GtsFunc) gfs_event_half_do, sim);
 
-    sim->advection_params.c = ch;
     gfs_centered_velocity_advection_diffusion (domain,
 					       FTT_DIMENSION,
 					       &sim->advection_params,
-					       &sim->diffusion_params);
+					       &sim->diffusion_params,
+					       g);
+
     gfs_simulation_adapt (sim, NULL);
     gfs_approximate_projection (domain,
    				&sim->approx_projection_params, 
-    				&sim->advection_params);
+    				&sim->advection_params, p, res);
 
     sim->time.t = sim->tnext;
     sim->time.i++;
@@ -620,14 +612,131 @@ static void gfs_simulation_class_init (GfsSimulationClass * klass)
   klass->run = simulation_run;
 }
 
+/* Derived variables */
+
+static gdouble cell_x (FttCell * cell, FttCellFace * face)
+{
+  FttVector p;
+
+  if (face)
+    ftt_face_pos (face, &p);
+  else
+    gfs_cell_cm (cell, &p);
+  return p.x;
+}
+
+static gdouble cell_y (FttCell * cell, FttCellFace * face)
+{
+  FttVector p;
+
+  if (face)
+    ftt_face_pos (face, &p);
+  else
+    gfs_cell_cm (cell, &p);
+  return p.y;
+}
+
+static gdouble cell_z (FttCell * cell, FttCellFace * face)
+{
+  FttVector p;
+
+  if (face)
+    ftt_face_pos (face, &p);
+  else
+    gfs_cell_cm (cell, &p);
+  return p.z;
+}
+
+static gdouble cell_t (FttCell * cell, FttCellFace * face, GfsSimulation * sim)
+{
+  return sim->time.t;
+}
+
+static gdouble cell_vorticity (FttCell * cell, FttCellFace * face, GfsDomain * domain)
+{
+  return gfs_vorticity (cell, gfs_domain_velocity (domain));
+}
+
+static gdouble cell_divergence (FttCell * cell, FttCellFace * face, GfsDomain * domain)
+{
+  return gfs_divergence (cell, gfs_domain_velocity (domain));
+}
+
+static gdouble cell_velocity_norm (FttCell * cell, FttCellFace * face, GfsDomain * domain)
+{
+  return gfs_vector_norm (cell, gfs_domain_velocity (domain));
+}
+
+static gdouble cell_velocity_norm2 (FttCell * cell, FttCellFace * face, GfsDomain * domain)
+{
+  return gfs_vector_norm2 (cell, gfs_domain_velocity (domain));
+}
+
+static gdouble cell_level (FttCell * cell)
+{
+  return ftt_cell_level (cell);
+}
+
+static gdouble cell_fraction (FttCell * cell)
+{
+  return GFS_IS_MIXED (cell) ? GFS_STATE (cell)->solid->a : 1.;
+}
+
+static gdouble cell_solid_area (FttCell * cell)
+{
+  FttVector n;
+  gfs_solid_normal (cell, &n);
+  return ftt_vector_norm (&n);
+}
+
+static gdouble cell_velocity_lambda2 (FttCell * cell, FttCellFace * face, GfsDomain * domain)
+{
+  return gfs_vector_lambda2 (cell, gfs_domain_velocity (domain));
+}
+
+static gdouble cell_streamline_curvature (FttCell * cell, FttCellFace * face, GfsDomain * domain)
+{
+  return gfs_streamline_curvature (cell, gfs_domain_velocity (domain));
+}
+
 static void gfs_simulation_init (GfsSimulation * object)
 {
+  GfsDomain * domain = GFS_DOMAIN (object);
+  static GfsDerivedVariable derived_variable[] = {
+    { "x",          cell_x },
+    { "y",          cell_y },
+    { "z",          cell_z },
+    { "t",          cell_t },
+    { "Vorticity",  cell_vorticity },
+    { "Divergence", cell_divergence },
+    { "Velocity",   cell_velocity_norm },
+    { "Velocity2",  cell_velocity_norm2 },
+    { "Level",      cell_level },
+    { "A",          cell_fraction },
+    { "S",          cell_solid_area },
+    { "Lambda2",    cell_velocity_lambda2 },
+    { "Curvature",  cell_streamline_curvature },
+    { NULL, NULL}
+  };
+  GfsDerivedVariable * v = derived_variable;
+
+  gfs_domain_add_variable (domain, "P");
+  gfs_variable_set_vector (gfs_domain_add_variable (domain, "U"), FTT_X);
+  gfs_variable_set_vector (gfs_domain_add_variable (domain, "V"), FTT_Y);
+#if (!FTT_2D)
+  gfs_variable_set_vector (gfs_domain_add_variable (domain, "W"), FTT_Z);
+#endif /* FTT_3D */
+
+  while (v->name) {
+    gfs_simulation_add_derived_variable (object, *v);
+    v++;
+  }
+
   gfs_time_init (&object->time);
   gfs_physical_params_init (&object->physical_params);
 
   gfs_advection_params_init (&object->advection_params);
   object->advection_params.flux = gfs_face_velocity_advection_flux;
-  object->advection_params.fv = gfs_res;
   gfs_multilevel_params_init (&object->diffusion_params);
   object->diffusion_params.tolerance = 1e-6;
 
@@ -758,19 +867,22 @@ void gfs_simulation_refine (GfsSimulation * sim)
   if (sim->surface) {
     gfs_domain_timer_start (domain, "solid_fractions");
     gfs_domain_init_solid_fractions (domain, sim->surface, TRUE,
-				     (FttCellCleanupFunc) gfs_cell_cleanup, NULL);
+				     (FttCellCleanupFunc) gfs_cell_cleanup, NULL, 
+				     NULL);
     gfs_domain_match (domain);
     gfs_domain_timer_stop (domain, "solid_fractions");
   }
   gts_container_foreach (GTS_CONTAINER (sim), (GtsFunc) check_solid_fractions, &nf);
   if (nf > 0) {
-    GfsVariable * v = domain->variables;
+    GSList * i = domain->variables;
     gboolean diffusion = FALSE;
     
-    while (v && !diffusion) {
+    while (i && !diffusion) {
+      GfsVariable * v = i->data;
+
       if (v->sources)
 	gts_container_foreach (v->sources, (GtsFunc) is_diffusion, &diffusion);
-      v = v->next;
+      i = i->next;
     }
     if (diffusion)
       g_warning ("the solid surface cuts %d boundary cells,\n"
@@ -834,12 +946,14 @@ void gfs_simulation_write (GfsSimulation * sim,
 static gdouble min_cfl (GfsSimulation * sim)
 {
   gdouble cfl = sim->advection_params.cfl;
-  GfsVariable * v = GFS_DOMAIN (sim)->variables;
+  GSList * i = GFS_DOMAIN (sim)->variables;
   
-  while (v) {
+  while (i) {
+    GfsVariable * v = i->data;
+
     if (GFS_IS_VARIABLE_TRACER (v) && GFS_VARIABLE_TRACER (v)->advection.cfl < cfl)
       cfl = GFS_VARIABLE_TRACER (v)->advection.cfl;
-    v = v->next;
+    i = i->next;
   }
 
   return cfl;
@@ -1053,12 +1167,72 @@ void gfs_simulation_run (GfsSimulation * sim)
   g_timer_stop (GFS_DOMAIN (sim)->timer);
 }
 
+/**
+ * gfs_simulation_add_derived_variable:
+ * @sim: a #GfsSimulation.
+ * @v: the #GfsDerivedVariable.
+ *
+ * Adds @v to @sim.
+ *
+ * Returns: %TRUE if the variable was successfully added to @sim or
+ * %FALSE if a derived variable with the same name already exists.
+ */
+gboolean gfs_simulation_add_derived_variable (GfsSimulation * sim, GfsDerivedVariable v)
+{
+  GSList * i;
+  
+  g_return_val_if_fail (sim != NULL, FALSE);
+
+  i = sim->derived_variables;
+  while (i) {
+    GfsDerivedVariable * u = i->data;
+    if (!strcmp (u->name, v.name))
+      return FALSE;
+    i = i->next;
+  }
+  sim->derived_variables = g_slist_prepend (sim->derived_variables, 
+					    g_memdup (&v, sizeof (GfsDerivedVariable)));
+  return TRUE;
+}
+
+/**
+ * gfs_simulation_remove_derived_variable:
+ * @sim: a #GfsSimulation.
+ * @name: the name of a #GfsDerivedVariable.
+ *
+ * Removes derived variable @name from @sim.
+ *
+ * Returns: %TRUE if the variable was successfully removed from @sim or
+ * %FALSE if a derived variable with the this name does not exist.
+ */
+gboolean gfs_simulation_remove_derived_variable (GfsSimulation * sim, const gchar * name)
+{
+  GSList * i;
+  
+  g_return_val_if_fail (sim != NULL, FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  i = sim->derived_variables;
+  while (i) {
+    GfsDerivedVariable * u = i->data;
+
+    if (!strcmp (u->name, name)) {
+      g_free (u);
+      sim->derived_variables = g_slist_remove_link (sim->derived_variables, i);
+      g_slist_free (i);
+      return TRUE;
+    }
+    i = i->next;
+  }
+  return FALSE;
+}
+
 /* GfsAdvection: Object */
 
 static void advection_run (GfsSimulation * sim)
 {
-  GfsVariable * v;
   GfsDomain * domain;
+  GSList * i;
 
   domain = GFS_DOMAIN (sim);
 
@@ -1068,21 +1242,21 @@ static void advection_run (GfsSimulation * sim)
   gts_container_foreach (GTS_CONTAINER (sim->adapts), (GtsFunc) gfs_event_init, sim);
 
   gfs_set_merged (domain);
-  v = domain->variables;
-  while (v) {
-    gfs_event_init (GFS_EVENT (v), sim);
-    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v);
-    v = v->next;
+  i = domain->variables;
+  while (i) {
+    gfs_event_init (GFS_EVENT (i->data), sim);
+    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, i->data);
+    i = i->next;
   }
 
   while (sim->time.t < sim->time.end &&
 	 sim->time.i < sim->time.iend) {
     gdouble tstart;
 
-    v = domain->variables;
-    while (v) {
-      gfs_event_do (GFS_EVENT (v), sim);
-      v = v->next;
+    i = domain->variables;
+    while (i) {
+      gfs_event_do (GFS_EVENT (i->data), sim);
+      i = i->next;
     }
     gfs_domain_cell_traverse (domain,
 			      FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
@@ -1095,16 +1269,16 @@ static void advection_run (GfsSimulation * sim)
 
     gfs_domain_face_traverse (domain, FTT_XYZ,
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttFaceTraverseFunc) gfs_face_reset_normal_velocity,
-			      NULL);
+			      (FttFaceTraverseFunc) gfs_face_reset_normal_velocity, NULL);
     gfs_domain_face_traverse (domain, FTT_XYZ,
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, NULL);
+			      (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity,
+			      gfs_domain_velocity (domain));
 
-    v = domain->variables;
-    while (v) {
-      if (GFS_IS_VARIABLE_TRACER (v)) {
-	GfsVariableTracer * t = GFS_VARIABLE_TRACER (v);
+    i = domain->variables;
+    while (i) {
+      if (GFS_IS_VARIABLE_TRACER (i->data)) {
+	GfsVariableTracer * t = GFS_VARIABLE_TRACER (i->data);
 
 	t->advection.dt = sim->advection_params.dt;
 	switch (t->advection.scheme) {
@@ -1113,13 +1287,13 @@ static void advection_run (GfsSimulation * sim)
 	  break;
 	case GFS_VOF:
 	  gfs_tracer_vof_advection (domain, &t->advection, NULL);
-	  gfs_domain_variable_centered_sources (domain, v, v, t->advection.dt);
+	  gfs_domain_variable_centered_sources (domain, i->data, i->data, t->advection.dt);
 	  break;
 	case GFS_NONE:
 	  break;
 	}
       }
-      v = v->next;
+      i = i->next;
     }
 
     gts_container_foreach (GTS_CONTAINER (sim->events), (GtsFunc) gfs_event_half_do, sim);
diff --git a/src/simulation.h b/src/simulation.h
index 90c613e..6e51d8e 100644
--- a/src/simulation.h
+++ b/src/simulation.h
@@ -55,6 +55,8 @@ struct _GfsAdaptStats {
 struct _GfsSimulation {
   GfsDomain parent;
 
+  GSList * derived_variables;
+
   GfsTime time;
   GfsPhysicalParams physical_params;
 
@@ -118,6 +120,10 @@ void                 gfs_physical_params_read    (GfsPhysicalParams * p,
 						  GtsFile * fp);
 void                 gfs_simulation_run          (GfsSimulation * sim);
 #define              gfs_object_simulation(o)     GFS_SIMULATION(GTS_OBJECT (o)->reserved)
+gboolean     gfs_simulation_add_derived_variable (GfsSimulation * sim, 
+						  GfsDerivedVariable v);
+gboolean     gfs_simulation_remove_derived_variable (GfsSimulation * sim, 
+						     const gchar * name);
 
 /* GfsAdvection: Header */
 
diff --git a/src/solid.c b/src/solid.c
index fb3ba7b..181adcb 100644
--- a/src/solid.c
+++ b/src/solid.c
@@ -540,11 +540,12 @@ void gfs_cell_init_solid_fractions_from_children (FttCell * cell)
   }
 }
 
-static void push_leaf (GtsFifo * fifo, FttCell * cell, FttDirection d, gdouble a)
+static void push_leaf (GtsFifo * fifo, FttCell * cell, FttDirection d, gdouble a,
+		       GfsVariable * status)
 {
   if (FTT_CELL_IS_LEAF (cell)) {
-    if (!GFS_IS_MIXED (cell) && GFS_STATE (cell)->div == 0.) {
-      GFS_STATE (cell)->div = a;
+    if (!GFS_IS_MIXED (cell) && GFS_VARIABLE (cell, status->i) == 0.) {
+      GFS_VARIABLE (cell, status->i) = a;
       gts_fifo_push (fifo, cell);
     }
   }
@@ -554,15 +555,15 @@ static void push_leaf (GtsFifo * fifo, FttCell * cell, FttDirection d, gdouble a
     
     n = ftt_cell_children_direction (cell, FTT_OPPOSITE_DIRECTION (d), &child);
     for (i = 0; i < n; i++)
-      if (child.c[i] && !GFS_IS_MIXED (child.c[i]) && GFS_STATE (child.c[i])->div == 0.) {
+      if (child.c[i] && !GFS_IS_MIXED (child.c[i]) && GFS_VARIABLE (child.c[i], status->i) == 0.) {
 	g_assert (FTT_CELL_IS_LEAF (child.c[i]));
-	GFS_STATE (child.c[i])->div = a;
+	GFS_VARIABLE (child.c[i], status->i) = a;
 	gts_fifo_push (fifo, child.c[i]);
       }
   }  
 }
 
-static void paint_leaf (GtsFifo * fifo, gdouble a)
+static void paint_leaf (GtsFifo * fifo, gdouble a, GfsVariable * status)
 {
   FttCell * cell;
 
@@ -573,11 +574,11 @@ static void paint_leaf (GtsFifo * fifo, gdouble a)
     ftt_cell_neighbors (cell, &n);
     for (i = 0; i < FTT_NEIGHBORS; i++)
       if (n.c[i] && !GFS_CELL_IS_BOUNDARY (n.c[i]))
-	push_leaf (fifo, n.c[i], i, a);
+	push_leaf (fifo, n.c[i], i, a, status);
   }
 }
 
-static void paint_mixed_leaf (FttCell * cell)
+static void paint_mixed_leaf (FttCell * cell, GfsVariable * status)
 {
   if (GFS_IS_MIXED (cell)) {
     GfsSolidVector * solid = GFS_STATE (cell)->solid;
@@ -589,8 +590,8 @@ static void paint_mixed_leaf (FttCell * cell)
     for (i = 0; i < FTT_NEIGHBORS; i++)
       if ((n = ftt_cell_neighbor (cell, i)) && !GFS_CELL_IS_BOUNDARY (n)) {
 	if (solid->s[i] == 0. || solid->s[i] == 1.) {
-	  push_leaf (fifo, n, i, solid->s[i] + 1.);
-	  paint_leaf (fifo, solid->s[i] + 1.);
+	  push_leaf (fifo, n, i, solid->s[i] + 1., status);
+	  paint_leaf (fifo, solid->s[i] + 1., status);
 	}
 	else if (!FTT_CELL_IS_LEAF (n)) {
 	  FttCellChildren child;
@@ -614,6 +615,7 @@ typedef struct {
   gboolean destroy_solid;
   FttCellCleanupFunc cleanup;
   gpointer data;
+  GfsVariable * status;
 } InitSolidParams;
 
 static void solid_fractions_from_children (FttCell * cell, InitSolidParams * p)
@@ -628,21 +630,21 @@ static void solid_fractions_from_children (FttCell * cell, InitSolidParams * p)
 	solid_fractions_from_children (child.c[i], p);
     if (FTT_CELL_IS_LEAF (cell))
       /* all the children have been destroyed i.e. the cell is solid */
-      GFS_STATE (cell)->div = 1.;
+      GFS_VARIABLE (cell, p->status->i) = 1.;
     else {
       gfs_cell_init_solid_fractions_from_children (cell);
       if (p->destroy_solid)
-	GFS_STATE (cell)->div = 0.;
+	GFS_VARIABLE (cell, p->status->i) = 0.;
       else if (!GFS_IS_MIXED (cell)) {
 	ftt_cell_children (cell, &child);
-	GFS_STATE (cell)->div = 1.;
+	GFS_VARIABLE (cell, p->status->i) = 1.;
 	for (i = 0; i < FTT_CELLS; i++)
-	  if (child.c[i] && GFS_STATE (child.c[i])->div == 2.)
-	    GFS_STATE (cell)->div = 2.;
+	  if (child.c[i] && GFS_VARIABLE (child.c[i], p->status->i) == 2.)
+	    GFS_VARIABLE (cell, p->status->i) = 2.;
       }
     }
   }
-  if (p->destroy_solid && GFS_STATE (cell)->div == 1.) {
+  if (p->destroy_solid && GFS_VARIABLE (cell, p->status->i) == 1.) {
     if (FTT_CELL_IS_ROOT (cell))
       g_log (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR,
 	     "root cell is entirely outside of the fluid domain\n"
@@ -664,6 +666,7 @@ static void foreach_box (GfsBox * box, InitSolidParams * p)
  * @destroy_solid: controls what to do with solid cells.
  * @cleanup: a #FttCellCleanupFunc or %NULL.
  * @data: user data to pass to @cleanup.
+ * @status: a temporary variable or %NULL.
  *
  * Initializes the solid fractions of all the cells of @domain.
  *
@@ -674,23 +677,27 @@ void gfs_domain_init_solid_fractions (GfsDomain * domain,
 				      GtsSurface * s,
 				      gboolean destroy_solid,
 				      FttCellCleanupFunc cleanup,
-				      gpointer data)
+				      gpointer data,
+				      GfsVariable * status)
 {
   InitSolidParams p;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (s != NULL);
 
+  p.destroy_solid = destroy_solid;
+  p.cleanup = cleanup;
+  p.data = data;
+  p.status = status ? status : gfs_temporary_variable (domain);
   gfs_domain_traverse_cut (domain, s, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
 			   (FttCellTraverseCutFunc) set_solid_fractions_from_surface, NULL);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_cell_reset, gfs_div);
+			    (FttCellTraverseFunc) gfs_cell_reset, p.status);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) paint_mixed_leaf, NULL);
-  p.destroy_solid = destroy_solid;
-  p.cleanup = cleanup;
-  p.data = data;
+			    (FttCellTraverseFunc) paint_mixed_leaf, p.status);
   gts_container_foreach (GTS_CONTAINER (domain), (GtsFunc) foreach_box, &p);
+  if (status == NULL)
+    gts_object_destroy (GTS_OBJECT (p.status));
 }
 
 static gboolean check_area_fractions (const FttCell * root)
@@ -842,6 +849,7 @@ static void restore_solid (FttCell * cell, gpointer * data)
 {
   GfsVariable * c = data[0];
   gboolean * not_cut = data[1];
+  GfsVariable * status = data[2];
   GfsSolidVector * solid = GFS_STATE (cell)->solid;
 
   GFS_STATE (cell)->solid = GFS_DOUBLE_TO_POINTER (GFS_VARIABLE (cell, c->i));
@@ -850,13 +858,13 @@ static void restore_solid (FttCell * cell, gpointer * data)
     g_free (solid);
     *not_cut = FALSE;
   }
-  else if (GFS_STATE (cell)->div == 0.) {
+  else if (GFS_VARIABLE (cell, status->i) == 0.) {
     g_assert (*not_cut);
     GFS_VARIABLE (cell, c->i) = 0.;
   }
   else {
-    g_assert (GFS_STATE (cell)->div == 1. || GFS_STATE (cell)->div == 2.);
-    GFS_VARIABLE (cell, c->i) = GFS_STATE (cell)->div - 1.;
+    g_assert (GFS_VARIABLE (cell, status->i) == 1. || GFS_VARIABLE (cell, status->i) == 2.);
+    GFS_VARIABLE (cell, c->i) = GFS_VARIABLE (cell, status->i) - 1.;
   }
 }
 
@@ -874,20 +882,26 @@ void gfs_domain_init_fraction (GfsDomain * domain,
 			       GfsVariable * c)
 {
   gboolean not_cut = TRUE;
-  gpointer data[2];
+  gpointer data[3];
+  GfsVariable * status;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (s != NULL);
   g_return_if_fail (c != NULL);
 
+  status = gfs_temporary_variable (domain);
+
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
 			    (FttCellTraverseFunc) save_solid, c);
-  gfs_domain_init_solid_fractions (domain, s, FALSE, NULL, NULL);
+  gfs_domain_init_solid_fractions (domain, s, FALSE, NULL, NULL, status);
   data[0] = c;
   data[1] = &not_cut;
+  data[2] = status;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
 			    (FttCellTraverseFunc) restore_solid, data);
   gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, c);
+
+  gts_object_destroy (GTS_OBJECT (status));
 }
 
 /**
diff --git a/src/solid.h b/src/solid.h
index e00a028..596772f 100644
--- a/src/solid.h
+++ b/src/solid.h
@@ -33,7 +33,8 @@ void         gfs_domain_init_solid_fractions             (GfsDomain * domain,
 							  GtsSurface * s,
 							  gboolean destroy_solid,
 							  FttCellCleanupFunc cleanup,
-							  gpointer data);
+							  gpointer data,
+							  GfsVariable * status);
 void         gfs_cell_init_solid_fractions_from_children (FttCell * cell);
 gboolean     gfs_cell_check_solid_fractions              (FttCell * root);
 void         gfs_domain_init_fraction                    (GfsDomain * domain,
diff --git a/src/source.c b/src/source.c
index 2892389..c58bd60 100644
--- a/src/source.c
+++ b/src/source.c
@@ -214,12 +214,15 @@ static void source_vector_read (GtsObject ** o, GtsFile * fp)
       gts_file_error (fp, "expecting a string (GfsVariable)");
       return;
     }
-    source->v[c] = gfs_variable_from_name (domain->variables, 
-					   fp->token->str);
+    source->v[c] = gfs_variable_from_name (domain->variables, fp->token->str);
     if (source->v[c] == NULL) {
       gts_file_error (fp, "unknown variable `%s'", fp->token->str);
       return;
     }
+    if (source->v[c]->component != c) {
+      gts_file_error (fp, "variable `%s' is not component %d", fp->token->str, c);
+      return;
+    }
     if (source->v[c]->sources == NULL)
       source->v[c]->sources = 
 	gts_container_new (GTS_CONTAINER_CLASS (gts_slist_container_class ()));
@@ -290,14 +293,7 @@ static gdouble source_value (GfsSourceGeneric * s,
 			     FttCell * cell, 
 			     GfsVariable * v)
 {
-  FttVector p;
-
-  if (v->centered)
-    ftt_cell_pos (cell, &p);
-  else
-    gfs_cell_cm (cell, &p);
-  return gfs_function_value (GFS_SOURCE (s)->intensity, cell, &p,
-			     gfs_object_simulation (s)->time.t);
+  return gfs_function_value (GFS_SOURCE (s)->intensity, cell);
 }
 
 static void source_class_init (GfsSourceGenericClass * klass)
@@ -362,8 +358,8 @@ static gboolean source_control_event (GfsEvent * event, GfsSimulation * sim)
     GfsSourceControl * s = GFS_SOURCE_CONTROL (event);
     GtsRange r = gfs_domain_stats_variable (GFS_DOMAIN (sim), GFS_SOURCE_GENERIC (event)->v,
 					    FTT_TRAVERSE_LEAFS, -1);
-    s->s = (gfs_function_value (GFS_SOURCE (s)->intensity, NULL, NULL, sim->time.t) - r.mean)/
-      gfs_function_value (s->delay, NULL, NULL, sim->time.t);
+    s->s = (gfs_function_value (GFS_SOURCE (s)->intensity, NULL) - r.mean)/
+      gfs_function_value (s->delay, NULL);
     return TRUE;
   }
   return FALSE;
@@ -681,7 +677,7 @@ static gdouble source_diffusion_value (GfsSourceGeneric * s,
   if (GFS_IS_MIXED (cell)) /* this improves results for channel test */
     return 0.;
 
-  c = GFS_VELOCITY_COMPONENT (v->i);
+  c = v->component;
 
   v0 = GFS_VARIABLE (cell, v->i);
   f.cell = cell;
@@ -830,31 +826,56 @@ GfsSourceGenericClass * gfs_source_diffusion_explicit_class (void)
 
 static void source_viscosity_read (GtsObject ** o, GtsFile * fp)
 {
-  GfsVariable * v;
   FttComponent c;
+  GfsDomain * domain;
+  GfsSourceViscosity * s;
 
-  if (GTS_OBJECT_CLASS (gfs_source_viscosity_class ())->parent_class->read)
-    (* GTS_OBJECT_CLASS (gfs_source_viscosity_class ())->parent_class->read)
-      (o, fp);
+  (* GTS_OBJECT_CLASS (gfs_source_viscosity_class ())->parent_class->read) (o, fp);
   if (fp->type == GTS_ERROR)
     return;
-  
-  v = GFS_SOURCE_GENERIC (*o)->v->next;
-  for (c = 1; c < FTT_DIMENSION; c++, v = v->next)
-    if (!v) {
-      gts_file_error (fp, "not enough velocity components");
+
+  s = GFS_SOURCE_VISCOSITY (*o);
+  s->v[0] = GFS_SOURCE_GENERIC (*o)->v;
+  if (s->v[0]->component != 0) {
+    gts_file_error (fp, "`%s' is not vector component 0", s->v[0]->name);
+    return;
+  }
+  domain = GFS_DOMAIN (gfs_object_simulation (*o));
+  for (c = 1; c < FTT_DIMENSION; c++) {
+    GfsVariable * v;
+
+    if (fp->type != GTS_STRING) {
+      gts_file_error (fp, "expecting a string (vector component %d)", c);
       return;
     }
-    else {
-      if (v->sources == NULL)
-	v->sources = 
-	  gts_container_new (GTS_CONTAINER_CLASS (gts_slist_container_class ()));
-      else if (previous_diffusion_source (v, NULL)) {
-	gts_file_error (fp, "only one diffusion source can be specified for a given variable");
-	return;
-      }
-      gts_container_add (v->sources, GTS_CONTAINEE (*o));
+    if (!(v = gfs_variable_from_name (domain->variables, fp->token->str))) {
+      gts_file_error (fp, "unknown variable `%s'", fp->token->str);
+      return;
     }
+    if (v->component != c) {
+      gts_file_error (fp, "`%s' is not vector component %d", fp->token->str, c);
+      return;
+    }
+    if (v->sources == NULL)
+      v->sources = gts_container_new (GTS_CONTAINER_CLASS (gts_slist_container_class ()));
+    else if (previous_diffusion_source (v, NULL)) {
+      gts_file_error (fp, "only one diffusion source can be specified for a given variable");
+      return;
+    }
+    gts_container_add (v->sources, GTS_CONTAINEE (*o));
+    gts_file_next_token (fp);
+    s->v[c] = v;
+  }
+}
+
+static void source_viscosity_write (GtsObject * o, FILE * fp)
+{
+  FttComponent c;
+
+  (* GTS_OBJECT_CLASS (gfs_source_viscosity_class ())->parent_class->write) (o, fp);
+  
+  for (c = 1; c < FTT_DIMENSION; c++)
+    fprintf (fp, " %s", GFS_SOURCE_VISCOSITY (o)->v[c]->name);
 }
 
 static gdouble source_viscosity_non_diffusion_value (GfsSourceGeneric * s,
@@ -866,15 +887,16 @@ static gdouble source_viscosity_non_diffusion_value (GfsSourceGeneric * s,
   if (mu == NULL)
     return 0.;
   else {
-    FttComponent c = GFS_VELOCITY_COMPONENT (v->i), i;
+    GfsVariable ** u = GFS_SOURCE_VISCOSITY (s)->v;
+    FttComponent c = v->component, j;
     gdouble rho = 1.
       /* fixme: + GFS_STATE (cell)->c*(gfs_object_simulation (s)->physical_params.rho - 1.)*/;
     gdouble h = ftt_cell_size (cell);
     gdouble a = 0.;
 
-    for (i = 0; i < FTT_DIMENSION; i++)
-      a += (gfs_center_gradient (cell, c, GFS_VELOCITY_INDEX (i))*
-	    gfs_center_gradient (cell, i, mu->i));
+    for (j = 0; j < FTT_DIMENSION; j++)
+      a += (gfs_center_gradient (cell, c, u[j]->i)*
+	    gfs_center_gradient (cell, j, mu->i));
     return a/(rho*h*h);
   }
 }
@@ -893,6 +915,7 @@ static gdouble source_viscosity_value (GfsSourceGeneric * s,
 static void source_viscosity_class_init (GfsSourceGenericClass * klass)
 {
   GTS_OBJECT_CLASS (klass)->read = source_viscosity_read;
+  GTS_OBJECT_CLASS (klass)->write = source_viscosity_write;
   klass->mac_value = source_viscosity_value;
   klass->centered_value = source_viscosity_non_diffusion_value;
 }
@@ -904,7 +927,7 @@ GfsSourceGenericClass * gfs_source_viscosity_class (void)
   if (klass == NULL) {
     GtsObjectClassInfo source_viscosity_info = {
       "GfsSourceViscosity",
-      sizeof (GfsSourceDiffusion),
+      sizeof (GfsSourceViscosity),
       sizeof (GfsSourceGenericClass),
       (GtsObjectClassInitFunc) source_viscosity_class_init,
       (GtsObjectInitFunc) NULL,
@@ -931,39 +954,22 @@ static void source_coriolis_destroy (GtsObject * o)
 static void gfs_source_coriolis_read (GtsObject ** o, GtsFile * fp)
 {
   FttComponent c;
-  GfsVariable * v;
   GfsDomain * domain = GFS_DOMAIN (gfs_object_simulation (*o));
 
-  if (GTS_OBJECT_CLASS (gfs_source_coriolis_class ())->parent_class->read)
-    (* GTS_OBJECT_CLASS (gfs_source_coriolis_class ())->parent_class->read) (o, fp);
+  (* GTS_OBJECT_CLASS (gfs_source_coriolis_class ())->parent_class->read) (o, fp);
   if (fp->type == GTS_ERROR)
     return;
 
   GFS_SOURCE_CORIOLIS (*o)->omegaz = gfs_function_new (gfs_function_class (), 0.);
   gfs_function_read (GFS_SOURCE_CORIOLIS (*o)->omegaz, gfs_object_simulation (*o), fp);
 
-  v = GFS_SOURCE_GENERIC (*o)->v->next;
-  for (c = 1; c < 2; c++, v = v->next) {
-    if (!v) {
-      gts_file_error (fp, "not enough velocity components");
-      return;
-    }
-    else {
-      if (v->sources == NULL)
-	v->sources = gts_container_new (GTS_CONTAINER_CLASS (gts_slist_container_class ()));
-      gts_container_add (v->sources, GTS_CONTAINEE (*o));
-    }
-  }
-  for (c = 0; c <  2; c++) {
+  for (c = 0; c <  2; c++)
     GFS_SOURCE_CORIOLIS (*o)->u[c] = gfs_domain_add_variable (domain, NULL);
-    g_assert (GFS_SOURCE_CORIOLIS (*o)->u[c]);
-  }
 }
 
 static void gfs_source_coriolis_write (GtsObject * o, FILE * fp)
 {
-  if (GTS_OBJECT_CLASS (gfs_source_coriolis_class ())->parent_class->write)
-    (* GTS_OBJECT_CLASS (gfs_source_coriolis_class ())->parent_class->write) (o, fp);
+  (* GTS_OBJECT_CLASS (gfs_source_coriolis_class ())->parent_class->write) (o, fp);
   gfs_function_write (GFS_SOURCE_CORIOLIS (o)->omegaz, fp);
 }
 
@@ -971,15 +977,13 @@ static gdouble gfs_source_coriolis_mac_value (GfsSourceGeneric * s,
 					      FttCell * cell,
 					      GfsVariable * v)
 {
-  FttVector p;
+  GfsSourceVector * sv = GFS_SOURCE_VECTOR (s);
   gdouble f;
 
-  gfs_cell_cm (cell, &p);
-  f = gfs_function_value (GFS_SOURCE_CORIOLIS (s)->omegaz, NULL, &p, 
-			  gfs_object_simulation (s)->time.t);
-  switch (GFS_VELOCITY_COMPONENT (v->i)) {
-  case FTT_X: return   f*GFS_STATE (cell)->v;
-  case FTT_Y: return - f*GFS_STATE (cell)->u;
+  f = gfs_function_value (GFS_SOURCE_CORIOLIS (s)->omegaz, cell);
+  switch (v->component) {
+  case FTT_X: return   f*GFS_VARIABLE (cell, sv->v[1]->i);
+  case FTT_Y: return - f*GFS_VARIABLE (cell, sv->v[0]->i);
   default: g_assert_not_reached ();
   }
   return 0.;
@@ -987,14 +991,15 @@ static gdouble gfs_source_coriolis_mac_value (GfsSourceGeneric * s,
 
 static void save_coriolis (FttCell * cell, GfsSourceCoriolis * s)
 {
+  GfsSourceVector * sv = GFS_SOURCE_VECTOR (s);
   FttComponent c;
-  FttVector p;
   gdouble f;
 
-  gfs_cell_cm (cell, &p);
-  f = gfs_function_value (s->omegaz, NULL, &p, gfs_object_simulation (s)->time.t)/2.;
+  f = gfs_function_value (s->omegaz, cell)/2.;
   for (c = 0; c < 2; c++)
-    GFS_VARIABLE (cell, s->u[c]->i) = c == FTT_X ? f*GFS_STATE (cell)->v : -f*GFS_STATE (cell)->u;
+    GFS_VARIABLE (cell, s->u[c]->i) = c == FTT_X ?
+      f*GFS_VARIABLE (cell, sv->v[1]->i) :
+      - f*GFS_VARIABLE (cell, sv->v[0]->i);
 }
 
 static gboolean gfs_source_coriolis_event (GfsEvent * event, GfsSimulation * sim)
@@ -1011,10 +1016,7 @@ static gdouble gfs_source_coriolis_centered_value (GfsSourceGeneric * s,
 						   FttCell * cell,
 						   GfsVariable * v)
 {
-  FttComponent c = GFS_VELOCITY_COMPONENT (v->i);
-  GfsSourceCoriolis * b = GFS_SOURCE_CORIOLIS (s);
-
-  return GFS_VARIABLE (cell, b->u[c]->i);
+  return GFS_VARIABLE (cell, GFS_SOURCE_CORIOLIS (s)->u[v->component]->i);
 }
 
 static void gfs_source_coriolis_class_init (GfsSourceGenericClass * klass)
@@ -1042,7 +1044,7 @@ GfsSourceGenericClass * gfs_source_coriolis_class (void)
       (GtsArgSetFunc) NULL,
       (GtsArgGetFunc) NULL
     };
-    klass = gts_object_class_new (GTS_OBJECT_CLASS (gfs_source_generic_class ()),
+    klass = gts_object_class_new (GTS_OBJECT_CLASS (gfs_source_vector_class ()),
 				  &gfs_source_coriolis_info);
   }
 
@@ -1051,16 +1053,15 @@ GfsSourceGenericClass * gfs_source_coriolis_class (void)
 
 static void implicit_coriolis (FttCell * cell, GfsSourceCoriolis * s)
 {
-  FttVector p;
+  GfsSourceVector * sv = GFS_SOURCE_VECTOR (s);
   gdouble c, u, v;
   GfsSimulation * sim = gfs_object_simulation (s);
 
-  gfs_cell_cm (cell, &p);
-  c = sim->advection_params.dt*gfs_function_value (s->omegaz, NULL, &p, sim->time.t)/2.;
-  u = GFS_STATE (cell)->u;
-  v = GFS_STATE (cell)->v;
-  GFS_STATE (cell)->u = (u + c*v)/(1. + c*c);
-  GFS_STATE (cell)->v = (v - c*u)/(1. + c*c);
+  c = sim->advection_params.dt*gfs_function_value (s->omegaz, cell)/2.;
+  u = GFS_VARIABLE (cell, sv->v[0]->i);
+  v = GFS_VARIABLE (cell, sv->v[1]->i);
+  GFS_VARIABLE (cell, sv->v[0]->i) = (u + c*v)/(1. + c*c);
+  GFS_VARIABLE (cell, sv->v[1]->i) = (v - c*u)/(1. + c*c);
 }
 
 /**
@@ -1078,11 +1079,13 @@ gboolean gfs_source_coriolis_implicit (GfsSimulation * sim,
 				       GfsVariable * p)
 {
   GfsVariable * v;
+  GfsDomain * domain;
 
   g_return_val_if_fail (sim != NULL, FALSE);
   g_return_val_if_fail (p != NULL, FALSE);
 
-  v = gfs_variable_from_name (GFS_DOMAIN (sim)->variables, "U");
+  domain = GFS_DOMAIN (sim);
+  v = gfs_variable_from_name (domain->variables, "U");
   g_assert (v);
   if (v->sources) {
     GSList * i = GTS_SLIST_CONTAINER (v->sources)->items;
@@ -1113,10 +1116,23 @@ gboolean gfs_source_coriolis_implicit (GfsSimulation * sim,
     }
 
     if (s != NULL) {
-      gfs_poisson_coefficients (GFS_DOMAIN (sim), apar->c, apar->rho);
-      gfs_correct_normal_velocities (GFS_DOMAIN (sim), 2, p, apar->dt);
-      gfs_correct_centered_velocities (GFS_DOMAIN (sim), 2, apar->dt);
-      gfs_domain_cell_traverse (GFS_DOMAIN (sim), FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
+      GfsVariable * dia, * g[2];
+      FttComponent c;
+
+      dia = gfs_temporary_variable (domain);
+      gfs_poisson_coefficients (domain, dia, apar->c, apar->rho);
+      gts_object_destroy (GTS_OBJECT (dia));
+
+      for (c = 0; c < 2; c++)
+	g[c] = gfs_temporary_variable (domain);
+
+      gfs_correct_normal_velocities (domain, 2, p, g, apar->dt);
+      gfs_correct_centered_velocities (domain, 2, g, apar->dt);
+      
+      for (c = 0; c < 2; c++)
+	gts_object_destroy (GTS_OBJECT (g[c]));
+
+      gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 				(FttCellTraverseFunc) implicit_coriolis, s);
       return TRUE;
     }
diff --git a/src/source.h b/src/source.h
index 97c64fd..a0cd39f 100644
--- a/src/source.h
+++ b/src/source.h
@@ -233,6 +233,17 @@ GfsSourceGenericClass * gfs_source_diffusion_explicit_class  (void);
 
 /* GfsSourceViscosity: Header */
 
+typedef struct _GfsSourceViscosity         GfsSourceViscosity;
+
+struct _GfsSourceViscosity {
+  /*< private >*/
+  GfsSourceGeneric parent;
+  GfsVariable * v[FTT_DIMENSION];
+};
+
+#define GFS_SOURCE_VISCOSITY(obj)            GTS_OBJECT_CAST (obj,\
+					         GfsSourceViscosity,\
+					         gfs_source_viscosity_class ())
 #define GFS_IS_SOURCE_VISCOSITY(obj) (gts_object_is_from_class (obj,\
 				       gfs_source_viscosity_class ()))
 
@@ -244,7 +255,7 @@ typedef struct _GfsSourceCoriolis         GfsSourceCoriolis;
 
 struct _GfsSourceCoriolis {
   /*< private >*/
-  GfsSourceGeneric parent;
+  GfsSourceVector parent;
   GfsVariable * u[2];
 
   /*< public >*/
diff --git a/src/tension.c b/src/tension.c
index 5f98576..76c25a2 100644
--- a/src/tension.c
+++ b/src/tension.c
@@ -77,9 +77,9 @@ static void foreach_cell_normal (FttCell * cell, GfsSourceTension * s)
   for (c = 0; c < FTT_DIMENSION; c++)
     nn += (&n.x)[c]*(&n.x)[c];
   nn = sqrt (nn + 1e-50);
-  GFS_STATE (cell)->g[0] = sigh*n.x*n.x/nn;
-  GFS_STATE (cell)->g[1] = sigh*n.y*n.y/nn;
-  GFS_STATE (cell)->div  = sigh*n.x*n.y/nn;
+  GFS_VARIABLE (cell, s->g[0]->i) = sigh*n.x*n.x/nn;
+  GFS_VARIABLE (cell, s->g[1]->i) = sigh*n.y*n.y/nn;
+  GFS_VARIABLE (cell, s->g[2]->i) = sigh*n.x*n.y/nn;
 }
 
 static void foreach_cell_tension (FttCell * cell, GfsSourceTension * s)
@@ -87,9 +87,9 @@ static void foreach_cell_tension (FttCell * cell, GfsSourceTension * s)
   gdouble h = ftt_cell_size (cell);
   FttVector nx, ny, nxy;
 
-  gfs_youngs_normal (cell, gfs_gx, &nx);
-  gfs_youngs_normal (cell, gfs_gy, &ny);
-  gfs_youngs_normal (cell, gfs_div, &nxy);
+  gfs_youngs_normal (cell, s->g[0], &nx);
+  gfs_youngs_normal (cell, s->g[1], &ny);
+  gfs_youngs_normal (cell, s->g[2], &nxy);
 
   GFS_VARIABLE (cell, s->t[0]->i) = (ny.x - nxy.y)/h;
   GFS_VARIABLE (cell, s->t[1]->i) = (nx.y - nxy.x)/h;
@@ -98,10 +98,16 @@ static void foreach_cell_tension (FttCell * cell, GfsSourceTension * s)
 static void gfs_source_tension_event (GfsEvent * event, 
 				      GfsSimulation * sim)
 {
+  GfsSourceTension * s = GFS_SOURCE_TENSION (event);
+  guint i;
+
 #if (!FTT_2D)
   g_assert_not_implemented ();
 #endif
 
+  for (i = 0; i < 3; i++)
+    s->g[i] = gfs_temporary_variable (GFS_DOMAIN (sim));
+
   gfs_domain_cell_traverse (GFS_DOMAIN (sim),
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) foreach_cell_normal, event);
@@ -109,6 +115,8 @@ static void gfs_source_tension_event (GfsEvent * event,
   gfs_domain_cell_traverse (GFS_DOMAIN (sim), 
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) foreach_cell_tension, event);
+  for (i = 0; i < 3; i++)
+    gts_object_destroy (GTS_OBJECT (s->g[i]));
 }
 
 static gdouble gfs_source_tension_value (GfsSourceGeneric * s, 
diff --git a/src/tension.h b/src/tension.h
index 3fecd30..d9896f0 100644
--- a/src/tension.h
+++ b/src/tension.h
@@ -33,7 +33,8 @@ typedef struct _GfsSourceTension         GfsSourceTension;
 struct _GfsSourceTension {
   /*< private >*/
   GfsSourceVector parent;
-
+  GfsVariable * g[3];
+  
   /*< public >*/
   GfsVariable * c, * t[FTT_DIMENSION];
   gdouble sigma;
diff --git a/src/timestep.c b/src/timestep.c
index b977b84..60c6c1a 100644
--- a/src/timestep.c
+++ b/src/timestep.c
@@ -95,12 +95,12 @@ void gfs_multilevel_params_read (GfsMultilevelParams * par, GtsFile * fp)
     gts_file_variable_error (fp, var, "nrelax", "nrelax must be non zero");
 }
 
-static void reset_gradients (FttCell * cell)
+static void reset_gradients (FttCell * cell, GfsVariable ** g)
 {
   FttComponent c;
 
   for (c = 0; c < FTT_DIMENSION; c++)
-    GFS_STATE (cell)->g[c] = 0.;
+    GFS_VARIABLE (cell, g[c]->i) = 0.;
 }
 
 static void correct_normal_velocity (FttCellFace * face,
@@ -111,7 +111,8 @@ static void correct_normal_velocity (FttCellFace * face,
   FttFaceType type;
   GfsStateVector * s;
   GfsVariable * p = data[0];
-  gdouble * dt = data[1];
+  GfsVariable ** gv = data[1];
+  gdouble * dt = data[2];
   FttComponent c;
 
   if (GFS_FACE_FRACTION (face) == 0.)
@@ -123,7 +124,7 @@ static void correct_normal_velocity (FttCellFace * face,
 
   //  gfs_face_gradient_flux_centered (face, &g, GFS_P, -1);
   gfs_face_weighted_gradient (face, &g, p->i, -1);
-  dp = (g.b - g.a*s->p)/ftt_cell_size (face->cell);
+  dp = (g.b - g.a*GFS_VARIABLE (face->cell, p->i))/ftt_cell_size (face->cell);
   if (!FTT_FACE_DIRECT (face))
     dp = - dp;
 
@@ -131,26 +132,17 @@ static void correct_normal_velocity (FttCellFace * face,
     dp /= s->solid->s[face->d];
 
   GFS_FACE_NORMAL_VELOCITY_LEFT (face) -= dp*(*dt);
-  s->g[c] += dp;
-
-  switch (type) {
-  case FTT_FINE_FINE:
-    GFS_FACE_NORMAL_VELOCITY_RIGHT (face) -= dp*(*dt);
-    GFS_STATE (face->neighbor)->g[c] += dp;
-    break;
-  case FTT_FINE_COARSE: {
+  GFS_VARIABLE (face->cell, gv[c]->i) += dp;
+
+  if (type == FTT_FINE_COARSE)
     /* fixme: does this work (FTT_CELLS/2?) for 2D3? */
     dp *= GFS_FACE_FRACTION_LEFT (face)/(GFS_FACE_FRACTION_RIGHT (face)*FTT_CELLS/2);
-    GFS_FACE_NORMAL_VELOCITY_RIGHT (face) -= dp*(*dt);
-    GFS_STATE (face->neighbor)->g[c] += dp;
-    break;
-  }
-  default:
-    g_assert_not_reached ();
-  }
+
+  GFS_FACE_NORMAL_VELOCITY_RIGHT (face) -= dp*(*dt);
+  GFS_VARIABLE (face->neighbor, gv[c]->i) += dp;
 }
 
-static void scale_gradients (FttCell * cell)
+static void scale_gradients (FttCell * cell, GfsVariable ** g)
 {
   FttComponent c;
   FttCellNeighbors n;
@@ -159,8 +151,8 @@ static void scale_gradients (FttCell * cell)
   for (c = 0; c < FTT_DIMENSION; c++) {
     FttCell * c1 = n.c[2*c], * c2 = n.c[2*c + 1];
 
-    if (c1 && c2 && !GFS_CELL_IS_BOUNDARY (c1) && !GFS_CELL_IS_BOUNDARY (c2))
-      GFS_STATE (cell)->g[c] /= 2.;
+    if (c1 && c2)
+      GFS_VARIABLE (cell, g[c]->i) /= 2.;
   }
 }
 
@@ -169,43 +161,46 @@ static void scale_gradients (FttCell * cell)
  * @domain: a #GfsDomain.
  * @dimension: the number of dimensions (2 or 3).
  * @p: the pressure field.
+ * @g: where to store the pressure gradient.
  * @dt: the timestep.
  *
  * Corrects the normal velocity field of @domain using @p and and @dt.
  *
- * Also fills the g[] field with the centered gradient of @p.
+ * Also fills @g[] with the centered gradient of @p.
  */
 void gfs_correct_normal_velocities (GfsDomain * domain,
 				    guint dimension,
 				    GfsVariable * p,
+				    GfsVariable ** g,
 				    gdouble dt)
 {
-  gpointer data[2];
+  gpointer data[3];
+  FttComponent c;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (p != NULL);
+  g_return_if_fail (g != NULL);
   
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) reset_gradients, NULL);
+			    (FttCellTraverseFunc) reset_gradients, g);
   data[0] = p;
-  data[1] = &dt;
+  data[1] = g;
+  data[2] = &dt;
   gfs_domain_face_traverse (domain, dimension == 2 ? FTT_XY : FTT_XYZ,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttFaceTraverseFunc) correct_normal_velocity, data);
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) scale_gradients, NULL);
-  gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, gfs_gx);
-  gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, gfs_gy);
-#if (!FTT_2D)
-  if (dimension == 3)
-    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, gfs_gz);
-#endif /* 2D3 or 3D */
+			    (FttCellTraverseFunc) scale_gradients, g);
+  for (c = 0; c < dimension; c++)
+    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, g[c]);
 }
 
-static void scale_divergence (FttCell * cell, gdouble * a)
+static void scale_divergence (FttCell * cell, gpointer * data)
 {
-  GFS_STATE (cell)->div /= *a;
-  GFS_STATE (cell)->g[0] = 0.;
+  GfsVariable * div = data[0];
+  gdouble * dt = data[1];
+
+  GFS_VARIABLE (cell, div->i) /= *dt;
 }
 
 /**
@@ -213,14 +208,18 @@ static void scale_divergence (FttCell * cell, gdouble * a)
  * @domain: a #GfsDomain.
  * @par: the projection control parameters.
  * @apar: the advection parameters.
+ * @p: the pressure.
+ * @g: where to store the pressure gradient.
  *
  * Corrects the face-centered velocity field (MAC field) on the leaf
  * level of @domain using an exact (MAC) projection. The resulting
  * face-centered velocity field is (almost) exactly divergence
  * free. The (potential) pressure field is also obtained as a
  * by-product as well as its gradient at the center of the leaf cells
- * of the domain (the gradient is stored in the %GFS_G variables and is
- * obtained by simple averaging from the face values to the center).
+ * of the domain. The gradient is stored in newly-allocated @g[]
+ * variables and is obtained by simple averaging from the face values
+ * to the center. The newly-allocated @g[] variables should be freed
+ * when not needed anymore.
  *
  * The @residual field of the @par projection parameters is set to the
  * norm of the residual after the projection. The @niter field of the
@@ -230,30 +229,42 @@ static void scale_divergence (FttCell * cell, gdouble * a)
  */
 void gfs_mac_projection (GfsDomain * domain,
 			 GfsMultilevelParams * par,
-			 GfsAdvectionParams * apar)
+			 GfsAdvectionParams * apar,
+			 GfsVariable * p,
+			 GfsVariable ** g)
 {
   guint minlevel, maxlevel;
   gdouble dt;
+  gpointer data[2];
+  GfsVariable * div, * dia, * res;
+  FttComponent c;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (par != NULL);
   g_return_if_fail (apar != NULL);
+  g_return_if_fail (p != NULL);
+  g_return_if_fail (g != NULL);
 
   gfs_domain_timer_start (domain, "mac_projection");
+
+  div = gfs_temporary_variable (domain);
+  dia = gfs_temporary_variable (domain);
+  res = gfs_temporary_variable (domain);
   
   apar->v = gfs_variable_from_name (domain->variables, "U");
   dt = apar->dt;
   apar->dt /= 2.;
 
   /* Initialize face coefficients */
-  gfs_poisson_coefficients (domain, apar->c, apar->rho);
+  gfs_poisson_coefficients (domain, dia, apar->c, apar->rho);
 
   /* compute MAC divergence */
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_normal_divergence, 
-			    NULL);
+			    (FttCellTraverseFunc) gfs_normal_divergence, div);
+  data[0] = div;
+  data[1] = &apar->dt;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-  			    (FttCellTraverseFunc) scale_divergence, &apar->dt);
+  			    (FttCellTraverseFunc) scale_divergence, data);
 
 #if 0
   {
@@ -262,7 +273,7 @@ void gfs_mac_projection (GfsDomain * domain,
 
     gfs_write_mac_velocity (domain, 0.9, FTT_TRAVERSE_LEAFS, -1, NULL, fp);
     fclose (fp);
-    norm = gfs_domain_norm_variable (domain, gfs_div, FTT_TRAVERSE_LEAFS, -1);
+    norm = gfs_domain_norm_variable (domain, div, FTT_TRAVERSE_LEAFS, -1);
     fprintf (stderr, "mac div before: %g %g %g\n",
 	     norm.first, norm.second, norm.infty);
   }
@@ -273,64 +284,58 @@ void gfs_mac_projection (GfsDomain * domain,
   if (par->minlevel > minlevel)
     minlevel = par->minlevel;
   maxlevel = gfs_domain_depth (domain);
-  gfs_residual (domain, par->dimension, FTT_TRAVERSE_LEAFS, -1, gfs_p, gfs_div, gfs_res);
+  gfs_residual (domain, par->dimension, FTT_TRAVERSE_LEAFS, -1, p, div, dia, res);
   par->residual_before = par->residual = 
-    gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt);
+    gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt, res);
   par->niter = 0;
   while (par->residual.infty > par->tolerance && 
 	 par->niter < par->nitermax) {
-    gfs_poisson_cycle (domain, par->dimension, minlevel, maxlevel, par->nrelax, gfs_p, gfs_div);
-    par->residual = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1,
-					      apar->dt);
+    gfs_poisson_cycle (domain, par->dimension, minlevel, maxlevel, par->nrelax, p, div, dia, res);
+    par->residual = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt, res);
     par->niter++;
   }
-  
-  gfs_correct_normal_velocities (domain, FTT_DIMENSION, gfs_p, apar->dt);
+
+  gts_object_destroy (GTS_OBJECT (div));
+  gts_object_destroy (GTS_OBJECT (dia));
+  gts_object_destroy (GTS_OBJECT (res));
+
+  for (c = 0; c < FTT_DIMENSION; c++) {
+    g[c] = gfs_variable_new (gfs_variable_class (), domain, NULL);
+    gfs_variable_set_vector (g[c], c);
+  }
+  gfs_correct_normal_velocities (domain, FTT_DIMENSION, p, g, apar->dt);
 
 #if 0
   {
     FILE * fp = fopen ("/tmp/macafter", "wt");
-    GfsNorm norm;
 
     gfs_write_mac_velocity (domain, 0.9, FTT_TRAVERSE_LEAFS, -1, NULL, fp);
     fclose (fp);
-    gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttCellTraverseFunc) gfs_normal_divergence, 
-			      NULL);
-    norm = gfs_domain_norm_variable (domain, gfs_div, FTT_TRAVERSE_LEAFS, -1);
-    fprintf (stderr, "mac div after: %g %g %g\n",
-	     norm.first, norm.second, norm.infty);
   }
 #endif
-
+  
   apar->dt = dt;
 
   gfs_domain_timer_stop (domain, "mac_projection");
 }
 
-static void correct_2D (FttCell * cell, gdouble * dt)
+static void correct (FttCell * cell, gpointer * data)
 {
-  GfsStateVector * s = GFS_STATE (cell);
-
-  s->u -= s->g[0]*(*dt);
-  s->v -= s->g[1]*(*dt);
-}
-
-#if (!FTT_2D)
-static void correct_3D (FttCell * cell, gdouble * dt)
-{
-  GfsStateVector * s = GFS_STATE (cell);
+  FttComponent c;
+  GfsVariable ** v = data[0];
+  GfsVariable ** g = data[1];
+  gdouble * dt = data[2];
+  guint * dimension = data[3];
 
-  s->u -= s->g[0]*(*dt);
-  s->v -= s->g[1]*(*dt);
-  s->w -= s->g[2]*(*dt);
+  for (c = 0; c < *dimension; c++)
+    GFS_VARIABLE (cell, v[c]->i) -= GFS_VARIABLE (cell, g[c]->i)*(*dt);
 }
-#endif /* 2D3 or 3D */
 
 /**
  * gfs_correct_centered_velocities:
  * @domain: a #GfsDomain.
  * @dimension: the number of dimensions (2 or 3).
+ * @g: the pressure gradient.
  * @dt: the timestep.
  *
  * Corrects the velocity field of @domain using the pressure gradient
@@ -338,24 +343,24 @@ static void correct_3D (FttCell * cell, gdouble * dt)
  */
 void gfs_correct_centered_velocities (GfsDomain * domain,
 				      guint dimension,
+				      GfsVariable ** g,
 				      gdouble dt)
 {
-  GfsVariable * v;
+  GfsVariable ** v;
   FttComponent c;
+  gpointer data[4];
 
   g_return_if_fail (domain != NULL);
+  g_return_if_fail (g != NULL);
 
+  data[0] = v = gfs_domain_velocity (domain);
+  data[1] = g;
+  data[2] = &dt;
+  data[3] = &dimension;
   gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc)
-#if FTT_2D
-			    correct_2D,
-#else  /* 2D3 or 3D */
-			    (dimension == 2 ? correct_2D : correct_3D),
-#endif /* 2D3 or 3D */
-			    &dt);
-  v = gfs_variable_from_name (domain->variables, "U");
-  for (c = 0; c < dimension; c++, v = v->next)
-    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v);
+			    (FttCellTraverseFunc) correct, data);
+  for (c = 0; c < dimension; c++)
+    gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v[c]);
 }
 
 /**
@@ -363,6 +368,8 @@ void gfs_correct_centered_velocities (GfsDomain * domain,
  * @domain: a #GfsDomain.
  * @par: the projection control parameters.
  * @apar: the advection parameters.
+ * @p: the pressure.
+ * @res: the residual or %NULL.
  *
  * Corrects the centered velocity field on the leaf level of @domain
  * using an approximate projection. The resulting centered velocity
@@ -383,27 +390,45 @@ void gfs_correct_centered_velocities (GfsDomain * domain,
  */
 void gfs_approximate_projection (GfsDomain * domain,
 				 GfsMultilevelParams * par,
-				 GfsAdvectionParams * apar)
+				 GfsAdvectionParams * apar,
+				 GfsVariable * p,
+				 GfsVariable * res)
 {
   guint minlevel, maxlevel;
+  gpointer data[2];
+  GfsVariable * dia, * div, * g[FTT_DIMENSION], * res1;
+  FttComponent c;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (par != NULL);
   g_return_if_fail (apar != NULL);
+  g_return_if_fail (p != NULL);
 
   gfs_domain_timer_start (domain, "approximate_projection");
+  
+  div = gfs_temporary_variable (domain);
+  dia = gfs_temporary_variable (domain);
+  res1 = res ? res : gfs_temporary_variable (domain);
 
   /* Initialize face coefficients */
-  gfs_poisson_coefficients (domain, apar->c, apar->rho);
+  gfs_poisson_coefficients (domain, dia, apar->c, apar->rho);
 
   /* compute MAC velocities from centered velocities */
   gfs_domain_face_traverse (domain, FTT_XYZ,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-      (FttFaceTraverseFunc) gfs_face_reset_normal_velocity,
-			    NULL);
+			    (FttFaceTraverseFunc) gfs_face_reset_normal_velocity, NULL);
   gfs_domain_face_traverse (domain, FTT_XYZ,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-      (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, NULL);
+			    (FttFaceTraverseFunc) gfs_face_interpolated_normal_velocity, 
+			    gfs_domain_velocity (domain));
+
+  /* compute MAC divergence */
+  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
+			    (FttCellTraverseFunc) gfs_normal_divergence, div);
+  data[0] = div;
+  data[1] = &apar->dt;
+  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
+  			    (FttCellTraverseFunc) scale_divergence, data);
 
 #if 0
   {
@@ -412,26 +437,20 @@ void gfs_approximate_projection (GfsDomain * domain,
 
     gfs_write_mac_velocity (domain, 0.9, FTT_TRAVERSE_LEAFS, -1, NULL, fp);
     fclose (fp);
-    norm = gfs_domain_norm_variable (domain, gfs_div, FTT_TRAVERSE_LEAFS, -1);
+    norm = gfs_domain_norm_variable (domain, div, FTT_TRAVERSE_LEAFS, -1);
     fprintf (stderr, "mac div before: %g %g %g\n",
 	     norm.first, norm.second, norm.infty);
   }
 #endif
-
-  /* compute MAC divergence */
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_normal_divergence, NULL);
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_ALL, -1,
-  			    (FttCellTraverseFunc) scale_divergence, &apar->dt);
   
   /* solve for pressure */
   minlevel = domain->rootlevel;
   if (par->minlevel > minlevel)
     minlevel = par->minlevel;
   maxlevel = gfs_domain_depth (domain);
-  gfs_residual (domain, par->dimension, FTT_TRAVERSE_LEAFS, -1, gfs_p, gfs_div, gfs_res);
+  gfs_residual (domain, par->dimension, FTT_TRAVERSE_LEAFS, -1, p, div, dia, res1);
   par->residual_before = par->residual = 
-    gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt);
+    gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt, res1);
   par->niter = 0;
   while (par->residual.infty > par->tolerance && 
 	 par->niter < par->nitermax) {
@@ -443,13 +462,26 @@ void gfs_approximate_projection (GfsDomain * domain,
 	     par->residual.second, 
 	     par->residual.infty);
 #endif
-    gfs_poisson_cycle (domain, par->dimension, minlevel, maxlevel, par->nrelax, gfs_p, gfs_div);
-    par->residual = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt);
+    gfs_poisson_cycle (domain, par->dimension, minlevel, maxlevel, par->nrelax, p, div, dia, res1);
+    par->residual = gfs_domain_norm_residual (domain, FTT_TRAVERSE_LEAFS, -1, apar->dt, res1);
     par->niter++;
   }
 
-  gfs_correct_normal_velocities (domain, FTT_DIMENSION, gfs_p, apar->dt);
-  gfs_correct_centered_velocities (domain, FTT_DIMENSION, apar->dt);
+  gts_object_destroy (GTS_OBJECT (div));
+  gts_object_destroy (GTS_OBJECT (dia));
+  if (!res)
+    gts_object_destroy (GTS_OBJECT (res1));
+
+  for (c = 0; c < FTT_DIMENSION; c++) {
+    g[c] = gfs_temporary_variable (domain);
+    gfs_variable_set_vector (g[c], c);
+  }
+
+  gfs_correct_normal_velocities (domain, FTT_DIMENSION, p, g, apar->dt);
+  gfs_correct_centered_velocities (domain, FTT_DIMENSION, g, apar->dt);
+
+  for (c = 0; c < FTT_DIMENSION; c++)
+    gts_object_destroy (GTS_OBJECT (g[c]));
 
   gfs_domain_timer_stop (domain, "approximate_projection");
 }
@@ -470,7 +502,6 @@ void gfs_predicted_face_velocities (GfsDomain * domain,
 {
   FttComponent c;
   FttCellTraverseFunc face_values;
-  GfsUpwinding upwinding;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (par != NULL);
@@ -479,27 +510,26 @@ void gfs_predicted_face_velocities (GfsDomain * domain,
 
   gfs_domain_face_traverse (domain, d == 2 ? FTT_XY : FTT_XYZ,
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-      (FttFaceTraverseFunc) gfs_face_reset_normal_velocity, 
-			    NULL);
+			    (FttFaceTraverseFunc) gfs_face_reset_normal_velocity, NULL);
+  par->u = gfs_domain_velocity (domain);
   par->use_centered_velocity = TRUE;
   if (par->scheme == GFS_NONE) {
     face_values = (FttCellTraverseFunc) gfs_cell_non_advected_face_values;
-    upwinding = GFS_NO_UPWINDING;
+    par->upwinding = GFS_NO_UPWINDING;
   }
   else {
     face_values = (FttCellTraverseFunc) gfs_cell_advected_face_values;
-    upwinding = GFS_CENTERED_UPWINDING;
+    par->upwinding = GFS_CENTERED_UPWINDING;
   }
-  par->v = gfs_variable_from_name (domain->variables, "U");
-  for (c = 0; c < d; c++, par->v = par->v->next) {
+  for (c = 0; c < d; c++) {
+    par->v = par->u[c];
     gfs_domain_cell_traverse (domain, 
     			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
     			      face_values, par);
     gfs_domain_face_bc (domain, c, par->v);
     gfs_domain_face_traverse (domain, c,
     			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttFaceTraverseFunc) gfs_face_advected_normal_velocity, 
-			      &upwinding);
+			      (FttFaceTraverseFunc) gfs_face_advected_normal_velocity, par);
   }
 
   gfs_domain_timer_stop (domain, "predicted_face_velocities");
@@ -510,40 +540,49 @@ void gfs_predicted_face_velocities (GfsDomain * domain,
  * @domain: a #GfsDomain.
  * @par: the multilevel parameters.
  * @v: a #GfsVariable.
+ * @rhs: the right-hand side.
+ * @dia: the diagonal weight.
  *
  * Solves a diffusion equation for variable @v using a Crank-Nicholson
  * scheme with multilevel relaxations.
  *
  * Diffusion coefficients must have been set using
  * gfs_diffusion_coefficients() and a right-hand side defined using
- * calls to gfs_diffusion_rhs().
+ * gfs_diffusion_rhs().
  */
 void gfs_diffusion (GfsDomain * domain,
 		    GfsMultilevelParams * par,
-		    GfsVariable * v)
+		    GfsVariable * v,
+		    GfsVariable * rhs, 
+		    GfsVariable * dia)
 {
   guint minlevel, maxlevel;
+  GfsVariable * res;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (par != NULL);
   g_return_if_fail (v != NULL);
+  g_return_if_fail (rhs != NULL);
+  g_return_if_fail (dia != NULL);
+
+  res = gfs_temporary_variable (domain);
 
   minlevel = domain->rootlevel;
   if (par->minlevel > minlevel)
     minlevel = par->minlevel;
   maxlevel = gfs_domain_depth (domain);
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_diffusion_residual, v);
+  gfs_diffusion_residual (domain, v, rhs, dia, res);
   par->residual_before = par->residual = 
-    gfs_domain_norm_variable (domain, gfs_res, FTT_TRAVERSE_LEAFS, -1);
+    gfs_domain_norm_variable (domain, res, FTT_TRAVERSE_LEAFS, -1);
   par->niter = 0;
   while (par->residual.infty > par->tolerance && 
 	 par->niter < par->nitermax) {
-    gfs_diffusion_cycle (domain, minlevel, maxlevel, par->nrelax, v);
-    par->residual = gfs_domain_norm_variable (domain, gfs_res, 
-					      FTT_TRAVERSE_LEAFS, -1);
+    gfs_diffusion_cycle (domain, minlevel, maxlevel, par->nrelax, v, rhs, dia, res);
+    par->residual = gfs_domain_norm_variable (domain, res, FTT_TRAVERSE_LEAFS, -1);
     par->niter++;
   }
+
+  gts_object_destroy (GTS_OBJECT (res));
 }
 
 static GfsSourceDiffusion * source_diffusion (GfsVariable * v)
@@ -564,11 +603,16 @@ static GfsSourceDiffusion * source_diffusion (GfsVariable * v)
 
 static void variable_sources (GfsDomain * domain,
 			      GfsAdvectionParams * par,
-			      GfsVariable * sv)
+			      GfsVariable * sv,
+			      GfsVariable ** g)
 {
   if (par->scheme == GFS_GODUNOV) {
     GfsVariable * v = par->v;
 
+    par->u = gfs_domain_velocity (domain);
+    par->g = g;
+    par->fv = gfs_temporary_variable (domain);
+    par->upwinding = GFS_FACE_UPWINDING;
     gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			      (FttCellTraverseFunc) gfs_cell_reset, par->fv);
     gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
@@ -580,6 +624,9 @@ static void variable_sources (GfsDomain * domain,
     par->v = sv;
     gfs_domain_traverse_merged (domain, (GfsMergedTraverseFunc) gfs_advection_update, par);
     par->v = v;
+    par->u = par->g = NULL;
+    gts_object_destroy (GTS_OBJECT (par->fv));
+    par->fv = NULL;
   }
   /* fixme: time should be set to t + dt/2 here for evaluation of
      source terms in the call below */
@@ -590,20 +637,26 @@ static void variable_diffusion (GfsDomain * domain,
 				GfsSourceDiffusion * d,
 				GfsAdvectionParams * par,
 				GfsMultilevelParams * dpar,
+				GfsVariable * rhs,
 				GfsVariable * c,
 				gdouble rho)
 {
+  GfsVariable * dia;
+
+  dia = gfs_temporary_variable (domain);
+
   if (c != NULL)
-    gfs_viscosity_coefficients (domain, d, par->dt, c, rho);
+    gfs_viscosity_coefficients (domain, d, par->dt, c, rho, dia);
   else
-    gfs_diffusion_coefficients (domain, d, par->dt);
+    gfs_diffusion_coefficients (domain, d, par->dt, dia);
   gfs_domain_surface_bc (domain, par->v);
-  gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			    (FttCellTraverseFunc) gfs_diffusion_rhs, par->v);
+  gfs_diffusion_rhs (domain, par->v, rhs, dia);
   /* fixme: time shoud be set to t + dt here in case boundary values are
      time-dependent in the call below */
   gfs_domain_surface_bc (domain, par->v);
-  gfs_diffusion (domain, dpar, par->v);
+  gfs_diffusion (domain, dpar, par->v, rhs, dia);
+
+  gts_object_destroy (GTS_OBJECT (dia));
 }
 
 /**
@@ -612,6 +665,7 @@ static void variable_diffusion (GfsDomain * domain,
  * @dimension: the number of dimensions (2 or 3).
  * @apar: the advection parameters.
  * @dpar: the multilevel solver parameters for the diffusion equation.
+ * @g: the pressure gradient.
  *
  * Advects the (centered) velocity field using the current
  * face-centered (MAC) velocity field and @par->flux to compute the
@@ -623,33 +677,47 @@ static void variable_diffusion (GfsDomain * domain,
  *
  * "Small" cut cells are treated using a cell-merging approach to
  * avoid any restrictive CFL stability condition.  
+ *
+ * The @g[] variables are freed by this function.
  */
 void gfs_centered_velocity_advection_diffusion (GfsDomain * domain,
 						guint dimension,
 						GfsAdvectionParams * apar,
-						GfsMultilevelParams * dpar)
+						GfsMultilevelParams * dpar,
+						GfsVariable ** g)
 {
   FttComponent c;
+  GfsVariable ** v;
 
   g_return_if_fail (domain != NULL);
   g_return_if_fail (apar != NULL);
   g_return_if_fail (dpar != NULL);
+  g_return_if_fail (g != NULL);
 
   gfs_domain_timer_start (domain, "centered_velocity_advection_diffusion");
 
   apar->use_centered_velocity = FALSE;
-  apar->v = gfs_variable_from_name (domain->variables, "U");
-  for (c = 0; c < dimension; c++, apar->v = apar->v->next) {
-    GfsSourceDiffusion * d = source_diffusion (apar->v);
+  v = gfs_domain_velocity (domain);
+  for (c = 0; c < dimension; c++) {
+    GfsSourceDiffusion * d = source_diffusion (v[c]);
 
+    apar->v = v[c];
     if (d) {
+      GfsVariable * rhs;
+
+      rhs = gfs_temporary_variable (domain);
       gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-				(FttCellTraverseFunc) gfs_cell_reset, gfs_div);
-      variable_sources (domain, apar, gfs_div);
-      variable_diffusion (domain, d, apar, dpar, apar->c, apar->rho);
+				(FttCellTraverseFunc) gfs_cell_reset, rhs);
+      variable_sources (domain, apar, rhs, g);
+      gts_object_destroy (GTS_OBJECT (g[c]));
+      g[c] = NULL;
+      variable_diffusion (domain, d, apar, dpar, rhs, apar->c, apar->rho);
+      gts_object_destroy (GTS_OBJECT (rhs));
     }
     else {
-      variable_sources (domain, apar, apar->v);
+      variable_sources (domain, apar, apar->v, g);
+      gts_object_destroy (GTS_OBJECT (g[c]));
+      g[c] = NULL;
       gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, apar->v);
     }
   }
@@ -709,13 +777,17 @@ void gfs_tracer_advection_diffusion (GfsDomain * domain,
   }
 
   if ((d = source_diffusion (par->v))) {
+    GfsVariable * rhs;
+
+    rhs = gfs_temporary_variable (domain);
     gfs_domain_cell_traverse (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttCellTraverseFunc) gfs_cell_reset, gfs_div);
-    variable_sources (domain, par, gfs_div);
-    variable_diffusion (domain, d, par, dpar, NULL, 0.);
+			      (FttCellTraverseFunc) gfs_cell_reset, rhs);
+    variable_sources (domain, par, rhs, NULL);
+    variable_diffusion (domain, d, par, dpar, rhs, NULL, 0.);
+    gts_object_destroy (GTS_OBJECT (rhs));
   }
   else {
-    variable_sources (domain, par, par->v);
+    variable_sources (domain, par, par->v, NULL);
     gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, par->v);
   }
 
@@ -866,10 +938,9 @@ static void gfs_surface_bc_write (GtsObject * o, FILE * fp)
 static void gfs_surface_bc_bc (FttCell * cell, GfsSurfaceGenericBc * b)
 {
   GfsSurfaceBc * bc = GFS_SURFACE_BC (b);
-  gdouble t = gfs_object_simulation (bc)->time.t;
-  gdouble val = gfs_function_value (bc->val, cell, &GFS_STATE (cell)->solid->ca, t);
+  gdouble val = gfs_function_value (bc->val, cell);
 
-  if (gfs_function_value (bc->type, cell, &GFS_STATE (cell)->solid->ca, t) > 0.) {
+  if (gfs_function_value (bc->type, cell) > 0.) {
     cell->flags |= GFS_FLAG_DIRICHLET;
     GFS_STATE (cell)->solid->fv = val;
   }
diff --git a/src/timestep.h b/src/timestep.h
index 2d84561..ad36e78 100644
--- a/src/timestep.h
+++ b/src/timestep.h
@@ -50,27 +50,36 @@ void          gfs_multilevel_params_read      (GfsMultilevelParams * par,
 void          gfs_correct_normal_velocities   (GfsDomain * domain,
 					       guint dimension,
 					       GfsVariable * p,
+					       GfsVariable ** g,
 					       gdouble dt);
 void          gfs_mac_projection              (GfsDomain * domain,
 					       GfsMultilevelParams * par,
-					       GfsAdvectionParams * apar);
+					       GfsAdvectionParams * apar,
+					       GfsVariable * p,
+					       GfsVariable ** g);
 void          gfs_correct_centered_velocities (GfsDomain * domain,
 					       guint dimension,
+					       GfsVariable ** g,
 					       gdouble dt);
 void          gfs_approximate_projection      (GfsDomain * domain,
 					       GfsMultilevelParams * par,
-					       GfsAdvectionParams * apar);
+					       GfsAdvectionParams * apar,
+					       GfsVariable * p,
+					       GfsVariable * res);
 void          gfs_predicted_face_velocities   (GfsDomain * domain,
 					       guint d,
 					       GfsAdvectionParams * par);
 
 void          gfs_diffusion                   (GfsDomain * domain,
 					       GfsMultilevelParams * par,
-					       GfsVariable * v);
+					       GfsVariable * v,
+					       GfsVariable * rhs, 
+					       GfsVariable * dia);
 void          gfs_centered_velocity_advection_diffusion (GfsDomain * domain,
-					       guint dimension,
-					       GfsAdvectionParams * par,
-					       GfsMultilevelParams * dpar);
+							 guint dimension,
+							 GfsAdvectionParams * par,
+							 GfsMultilevelParams * dpar,
+							 GfsVariable ** g);
 void          gfs_tracer_advection_diffusion  (GfsDomain * domain,
 					       GfsAdvectionParams * par,
 					       GfsMultilevelParams * dpar,
diff --git a/src/utils.c b/src/utils.c
index 0b5e9c4..b182bfc 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -43,50 +43,22 @@ gboolean gfs_char_in_string (char c, const char * s)
   return FALSE;
 }
 
-/* Derived variables */
-
-typedef gdouble (* GfsFunctionFunc) (const FttCell * cell, 
-				     gdouble x, gdouble y, gdouble z, 
-				     gdouble t);
-
-static gdouble cell_level (FttCell * cell)
-{
-  return ftt_cell_level (cell);
-}
-
-static gdouble cell_fraction (FttCell * cell)
-{
-  return GFS_IS_MIXED (cell) ? GFS_STATE (cell)->solid->a : 1.;
-}
-
-static gdouble cell_solid_area (FttCell * cell)
-{
-  FttVector n;
-  gfs_solid_normal (cell, &n);
-  return ftt_vector_norm (&n);
-}
-
-GfsDerivedVariable gfs_derived_variable[] = {
-  { "Vorticity",  gfs_vorticity },
-  { "Divergence", gfs_divergence },
-  { "Velocity",   gfs_velocity_norm },
-  { "Velocity2",  gfs_velocity_norm2 },
-  { "Level",      cell_level },
-  { "A",          cell_fraction },
-  { "S",          cell_solid_area },
-  { "Lambda2",    gfs_velocity_lambda2 },
-  { "Curvature",  gfs_streamline_curvature },
-  { NULL, NULL}
-};
-
-static GfsFunctionFunc lookup_derived_variable (const gchar * name)
+typedef gdouble (* GfsFunctionFunc) (const FttCell * cell,
+				     const FttCellFace * face,
+				     GfsSimulation * sim);
+typedef gdouble (* GfsFunctionDerivedFunc) (const FttCell * cell,
+					    const FttCellFace * face,
+					    GfsSimulation * sim,
+					    gpointer data);
+
+static GfsDerivedVariable * lookup_derived_variable (const gchar * name,
+						     GSList * i)
 {
-  GfsDerivedVariable * v = gfs_derived_variable;
-
-  while (v->name) {
+  while (i) {
+    GfsDerivedVariable * v = i->data;
     if (!strcmp (v->name, name))
-      return (GfsFunctionFunc) v->func;
-    v++;
+      return v;
+    i = i->next;
   }
   return NULL;
 }
@@ -101,6 +73,7 @@ struct _GfsFunction {
   gchar * sname;
   GtsSurface * s;
   GfsVariable * v;
+  GfsDerivedVariable * dv;
   gdouble val;
 };
 
@@ -267,6 +240,7 @@ static void function_read (GtsObject ** o, GtsFile * fp)
 {
   GfsFunction * f = GFS_FUNCTION (*o);
   GtsTokenType type;
+  GfsSimulation * sim;
   GfsDomain * domain;
 
   if (GTS_OBJECT_CLASS (gfs_function_class ())->parent_class->read)
@@ -274,7 +248,8 @@ static void function_read (GtsObject ** o, GtsFile * fp)
   if (fp->type == GTS_ERROR)
     return;
 
-  domain = GFS_DOMAIN (gfs_object_simulation (*o));
+  sim = gfs_object_simulation (*o);
+  domain = GFS_DOMAIN (sim);
   type = fp->type;
   switch (type) {
     /* constant value */
@@ -292,7 +267,7 @@ static void function_read (GtsObject ** o, GtsFile * fp)
     }
     else if ((f->v = gfs_variable_from_name (domain->variables, fp->token->str)))
       break;
-    else if ((f->f = lookup_derived_variable (fp->token->str))) {
+    else if ((f->dv = lookup_derived_variable (fp->token->str, sim->derived_variables))) {
       f->expr = g_string_new (fp->token->str);
       break;
     }
@@ -309,10 +284,7 @@ static void function_read (GtsObject ** o, GtsFile * fp)
       gchar finname[] = "/tmp/gfsXXXXXX";
       gint find, status;
       FILE * fin;
-      GfsVariable * v;
-      GfsDerivedVariable * dv;
-      GSList * lv = NULL, * ldv = NULL;
-      guint n = 0;
+      GSList * lv = NULL, * ldv = NULL, * i;
 
       isexpr = expr_or_func (fp, f);
       if (fp->type == GTS_ERROR)
@@ -327,21 +299,26 @@ static void function_read (GtsObject ** o, GtsFile * fp)
 	     "#include <stdio.h>\n"
 	     "#include <math.h>\n"
 	     "#include <gfs.h>\n"
+	     "typedef double (* Func) (const FttCell * cell,\n"
+	     "                         const FttCellFace * face,\n"
+	     "                         GfsSimulation * sim,\n"
+	     "                         gpointer data);\n"
 	     "static double Dirichlet = 1.;\n"
 	     "static double Neumann = 0.;\n"
-	     "double f (FttCell * cell, double x, double y, double z, double t) {\n",
+	     "double f (FttCell * cell, FttCellFace * face, GfsSimulation * sim) {\n",
 	     fin);
-      v = domain->variables;
-      while (v) {
-	if (v->name && find_identifier (f->expr->str, v->name))
-	  lv = g_slist_prepend (lv, v);
-	v = v->next;
+      i = domain->variables;
+      while (i) {
+	if (find_identifier (f->expr->str, GFS_VARIABLE1 (i->data)->name))
+	  lv = g_slist_prepend (lv, i->data);
+	i = i->next;
       }
-      dv = gfs_derived_variable;
-      while (dv->name) {
-	if (find_identifier (f->expr->str, dv->name))
-	  ldv = g_slist_prepend (ldv, GUINT_TO_POINTER (n));
-	dv++; n++;
+      i = sim->derived_variables;
+      while (i) {
+	GfsDerivedVariable * v = i->data;
+	if (find_identifier (f->expr->str, v->name))
+	  ldv = g_slist_prepend (ldv, v);
+	i = i->next;
       }
       if (lv || ldv) {
 	GSList * i = lv;
@@ -353,8 +330,8 @@ static void function_read (GtsObject ** o, GtsFile * fp)
 	}
 	i = ldv;
 	while (i) {
-	  guint n = GPOINTER_TO_UINT (i->data);
-	  fprintf (fin, "  double %s;\n", gfs_derived_variable[n].name);
+	  GfsDerivedVariable * v = i->data;
+	  fprintf (fin, "  double %s;\n", v->name);
 	  i = i->next;
 	}
 	fputs ("  if (cell) {\n", fin);
@@ -367,9 +344,12 @@ static void function_read (GtsObject ** o, GtsFile * fp)
 	g_slist_free (lv);
 	i = ldv;
 	while (i) {
-	  guint n = GPOINTER_TO_UINT (i->data);
-	  fprintf (fin, "    %s = (* gfs_derived_variable[%d].func) (cell);\n", 
-		   gfs_derived_variable[n].name, n);
+	  GfsDerivedVariable * v = i->data;
+	  fprintf (fin, "    %s = (* (Func) %p) (cell, face, sim, ", v->name, v->func);
+	  if (v->data)
+	    fprintf (fin, "%p);\n", v->data);
+	  else
+	    fprintf (fin, "NULL);\n");
 	  i = i->next;
 	}
 	g_slist_free (ldv);
@@ -385,7 +365,7 @@ static void function_read (GtsObject ** o, GtsFile * fp)
       close (find);
 
       status = compile (fp, f, finname);
-      remove (finname);
+      //      remove (finname);
       switch (status) {
       case SIGQUIT: exit (0);
       case SIGABRT: return;
@@ -529,25 +509,28 @@ gchar * gfs_function_description (GfsFunction * f)
  * gfs_function_value:
  * @f: a #GfsFunction.
  * @cell: a #FttCell or %NULL.
- * @p: a #FttVector.
- * @t: the time.
  *
- * Returns: the value of function @f at location @p of @cell.
+ * Returns: the value of function @f in @cell.
  */
-gdouble gfs_function_value (GfsFunction * f, FttCell * cell, FttVector * p, gdouble t)
+gdouble gfs_function_value (GfsFunction * f, FttCell * cell)
 {
   g_return_val_if_fail (f != NULL, 0.);
+  g_return_val_if_fail (cell != NULL, 0.);
 
-  if (f->s)
-    return interpolated_value (f, p);
-  else if (f->f) {
-    if (p != NULL)
-      return (* f->f) (cell, p->x, p->y, p->z, t);
-    else
-      return (* f->f) (cell, 0., 0., 0., t);
+  if (f->s) {
+    FttVector p;
+
+    gfs_cell_cm (cell, &p);
+    return interpolated_value (f, &p);
   }
   else if (f->v)
     return GFS_VARIABLE (cell, f->v->i);
+  else if (f->dv)
+    return (* (GfsFunctionDerivedFunc) f->dv->func) (cell, NULL, 
+						     gfs_object_simulation (f), 
+						     f->dv->data);
+  else if (f->f)
+    return (* f->f) (cell, NULL, gfs_object_simulation (f));
   else
     return f->val;
 }
@@ -556,33 +539,28 @@ gdouble gfs_function_value (GfsFunction * f, FttCell * cell, FttVector * p, gdou
  * gfs_function_face_value:
  * @f: a #GfsFunction.
  * @fa: a #FttCellFace.
- * @t: the time.
  *
  * Returns: the value of function @f at the center of face @fa.
  */
-gdouble gfs_function_face_value (GfsFunction * f, FttCellFace * fa,
-				 gdouble t)
+gdouble gfs_function_face_value (GfsFunction * f, FttCellFace * fa)
 {
   g_return_val_if_fail (f != NULL, 0.);
+  g_return_val_if_fail (fa != NULL, 0.);
 
   if (f->s) {
     FttVector p;
 
-    g_return_val_if_fail (fa != NULL, 0.);
-    
     ftt_face_pos (fa, &p);
     return interpolated_value (f, &p);
   }
-  else if (f->f) {
-    FttVector p;
-
-    g_return_val_if_fail (fa != NULL, 0.);
-    
-    ftt_face_pos (fa, &p);
-    return (* f->f) (fa->cell, p.x, p.y, p.z, t);
-  }
   else if (f->v)
     return gfs_face_interpolated_value (fa, f->v->i);
+  else if (f->dv)
+    return (* (GfsFunctionDerivedFunc) f->dv->func) (fa->cell, fa,
+						     gfs_object_simulation (f), 
+						     f->dv->data);
+  else if (f->f)
+    return (* f->f) (fa->cell, fa, gfs_object_simulation (f));
   else
     return f->val;
 }
@@ -597,7 +575,7 @@ gdouble gfs_function_face_value (GfsFunction * f, FttCellFace * fa,
 void gfs_function_set_constant_value (GfsFunction * f, gdouble val)
 {
   g_return_if_fail (f != NULL);
-  g_return_if_fail (!f->f && !f->s && !f->v);
+  g_return_if_fail (!f->f && !f->s && !f->v && !f->dv);
 
   f->val = val;
 }
@@ -613,7 +591,7 @@ gdouble gfs_function_get_constant_value (GfsFunction * f)
 {
   g_return_val_if_fail (f != NULL, G_MAXDOUBLE);
 
-  if (f->f || f->s || f->v)
+  if (f->f || f->s || f->v || f->dv)
     return G_MAXDOUBLE;
   else
     return f->val;
diff --git a/src/utils.h b/src/utils.h
index 887732f..24b2c08 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -35,11 +35,10 @@ gboolean gfs_char_in_string (char c, const char * s);
 
 typedef struct {
   gchar * name;
-  gdouble (* func) (FttCell *);
+  gpointer func;
+  gpointer data;
 } GfsDerivedVariable;
 
-GTS_C_VAR GfsDerivedVariable gfs_derived_variable[];
-
 /* GfsFunction: Header */
 
 typedef struct _GfsFunction         GfsFunction;
@@ -64,12 +63,9 @@ GfsFunction *      gfs_function_new         (GfsFunctionClass * klass,
 					     gdouble val);
 gchar *            gfs_function_description (GfsFunction * f);
 gdouble            gfs_function_face_value  (GfsFunction * f,
-					     FttCellFace * fa,
-					     gdouble t);
+					     FttCellFace * fa);
 gdouble            gfs_function_value       (GfsFunction * f,
-					     FttCell * cell,
-					     FttVector * p,
-					     gdouble t);
+					     FttCell * cell);
 void               gfs_function_set_constant_value (GfsFunction * f, 
 						    gdouble val);
 gdouble            gfs_function_get_constant_value (GfsFunction * f);
diff --git a/src/variable.c b/src/variable.c
index f0e62ce..7aa15dc 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -21,26 +21,38 @@
 
 /* GfsVariable: Object */
 
+static void variable_init_domain (GfsVariable * v, GfsDomain * domain)
+{
+   v->i = gfs_domain_alloc (domain);
+   v->centered = TRUE;
+   v->domain = domain;
+}
+
 static void gfs_variable_read (GtsObject ** o, GtsFile * fp)
 {
-  GfsEvent * e;
+  GfsDomain * domain;
+  GfsVariable * v, * old;
 
   if (GTS_OBJECT_CLASS (gfs_variable_class ())->parent_class->read)
     (* GTS_OBJECT_CLASS (gfs_variable_class ())->parent_class->read) (o, fp);
   if (fp->type == GTS_ERROR)
     return;
 
-  e = GFS_EVENT (*o);
-  if (e->end < G_MAXDOUBLE || e->iend < G_MAXINT || e->end_event) {
-    gts_file_error (fp, "a GfsVariable event cannot end");
-    return;
-  }
   if (fp->type != GTS_STRING) {
     gts_file_error (fp, "expecting a string (name)");
     return;
   }
-  GFS_VARIABLE1 (*o)->name = g_strdup (fp->token->str);
+  v = GFS_VARIABLE1 (*o);
+  v->name = g_strdup (fp->token->str);
   gts_file_next_token (fp);
+
+  domain = (*o)->reserved;
+  if ((old = gfs_variable_from_name (domain->variables, v->name))) {
+    domain->variables = g_slist_remove (domain->variables, old);
+    gts_object_destroy (GTS_OBJECT (old));
+  }
+  variable_init_domain (v, domain);
+  domain->variables = g_slist_append (domain->variables, v);
 }
 
 static void gfs_variable_write (GtsObject * o, FILE * fp)
@@ -59,33 +71,25 @@ static void gfs_variable_destroy (GtsObject * object)
     gts_object_destroy (GTS_OBJECT (v->sources));
   if (v->surface_bc)
     gts_object_destroy (GTS_OBJECT (v->surface_bc));
+  if (v->domain) {
+    gfs_domain_free (v->domain, v->i);
+    v->domain->variables = g_slist_remove (v->domain->variables, v);
+  }
 
   (* GTS_OBJECT_CLASS (gfs_variable_class ())->parent_class->destroy) (object);
 }
 
-static void gfs_variable_clone (GtsObject * clone, GtsObject * object)
-{
-  GfsVariable * c = GFS_VARIABLE1 (clone);
-  GfsVariable * v = GFS_VARIABLE1 (object);
-
-  (* GTS_OBJECT_CLASS (gfs_variable_class ())->parent_class->clone) (clone, object);
-  if (v->name)
-    c->name = g_strdup (v->name);
-  c->sources = NULL;
-  c->surface_bc = NULL;
-}
-
 static void gfs_variable_class_init (GfsVariableClass * klass)
 {
   GTS_OBJECT_CLASS (klass)->read = gfs_variable_read;
   GTS_OBJECT_CLASS (klass)->write = gfs_variable_write;
   GTS_OBJECT_CLASS (klass)->destroy = gfs_variable_destroy;
-  GTS_OBJECT_CLASS (klass)->clone = gfs_variable_clone;
 }
 
 static void gfs_variable_init (GfsVariable * v)
 {
   GFS_EVENT (v)->istep = 1;
+  v->component = FTT_DIMENSION;
   v->fine_coarse = (GfsVariableFineCoarseFunc) gfs_get_from_below_intensive;
 }
 
@@ -112,126 +116,79 @@ GfsVariableClass * gfs_variable_class (void)
 /**
  * gfs_variable_new:
  * @klass: a #GfsVariableClass.
- * @parent: the parent or %NULL.
- * @name: the name of the variable.
- * @centered: is the variable cell-centered?
- * @i: the variable index.
+ * @domain: a #GfsDomain.
+ * @name: the name of the variable or %NULL.
  *
- * Returns: a newly allocated #GfsVariable,
+ * Returns: a newly allocated #GfsVariable or %NULL if a variable
+ * named @name already exists in @domain.
  */
 GfsVariable * gfs_variable_new (GfsVariableClass * klass,
-				GtsObject * parent,
-				const gchar * name,
-				gboolean centered,
-				guint i)
+				GfsDomain * domain,
+				const gchar * name)
 {
   GfsVariable * v;
 
+  g_return_val_if_fail (klass != NULL, NULL);
+  g_return_val_if_fail (domain != NULL, NULL);
+
+  if (name && gfs_variable_from_name (domain->variables, name))
+    return NULL;
+
   v = GFS_VARIABLE1 (gts_object_new (GTS_OBJECT_CLASS (klass)));
   if (name)
     v->name = g_strdup (name);
-  v->i = i;
-  v->centered = centered;
-  v->p = parent;
-  v->permanent = v;
+  variable_init_domain (v, domain);
 
   return v;
 }
 
 /**
- * gfs_variable_list_copy:
- * @v: a #GfsVariable.
- * @parent: the parent of the new list or %NULL.
- *
- * Returns: a new variable list copy of @v.
- */
-GfsVariable * gfs_variable_list_copy (GfsVariable * v,
-				      GtsObject * parent)
-{
-  GfsVariable * start = NULL, * prev = NULL;
-
-  while (v) {
-    GfsVariable * n = GFS_VARIABLE1 (gts_object_clone (GTS_OBJECT (v)));
-
-    n->p = parent;
-    if (prev == NULL)
-      start = n;
-    else
-      prev->next = n;
-    prev = n;
-    v = v->next;
-  }
-  return start;
-}
-
-/**
- * gfs_variable_list_destroy:
- * @v: a #GfsVariable.
- *
- * Free all the memory allocated for the list starting at @v.
- */
-void gfs_variable_list_destroy (GfsVariable * v)
-{
-  while (v) {
-    GfsVariable * next = v->next;
-
-    gts_object_destroy (GTS_OBJECT (v));
-    v = next;
-  }
-}
-
-/**
  * gfs_variable_from_name:
- * @variables: the list of available #GfsVariable.
+ * @i: the list of available #GfsVariable.
  * @name: the name of the variable to find.
  *
  * Returns: the #GfsVariable @name or %NULL if this variable name does
  * not exist.  
  */
-GfsVariable * gfs_variable_from_name (GfsVariable * variables,
+GfsVariable * gfs_variable_from_name (GSList * i,
 				      const gchar * name)
 {
   g_return_val_if_fail (name != NULL, NULL);
 
-  while (variables && (!variables->name || strcmp (name, variables->name)))
-    variables = variables->next;
-  return variables;
+  while (i && strcmp (name, GFS_VARIABLE1 (i->data)->name))
+    i = i->next;
+  return i ? GFS_VARIABLE1 (i->data) : NULL;
 }
 
 /**
  * gfs_variables_from_list:
- * @variables: the list of available #GfsVariable.
+ * @i: the list of available #GfsVariable.
  * @list: a malloc'ed string containing comma separated variable names.
  * @error: where to return the variable name in case of error.
  *
  * Returns: a list of variables or %NULL in case of error, in which
  * case *@error points to the name of the unknown variable.  
  */
-GfsVariable * gfs_variables_from_list (GfsVariable * variables,
-				       gchar * list,
-				       gchar ** error)
+GSList * gfs_variables_from_list (GSList * i,
+				  gchar * list,
+				  gchar ** error)
 {
   gchar * s;
-  GfsVariable * var = NULL, * prev = NULL;
+  GSList * var = NULL;
 
-  g_return_val_if_fail (list != NULL, NULL);
+  g_return_val_if_fail (i != NULL, NULL);
   g_return_val_if_fail (error != NULL, NULL);
 
   s = strtok (list, ",");
   while (s) {
-    GfsVariable * v = gfs_variable_from_name (variables, s), * n;
+    GfsVariable * v = gfs_variable_from_name (i, s);
 
     if (v == NULL) {
       *error = s;
-      gfs_variable_list_destroy (var);
+      g_slist_free (var);
       return NULL;
     }
-    n = gfs_variable_new (gfs_variable_class (), v->p, v->name, FALSE, v->i);
-    if (prev)
-      prev->next = n;
-    else
-      var = n;
-    prev = n;
+    var = g_slist_append (var, v);
     s = strtok (NULL, ",");
   }
   return var;
@@ -273,7 +230,7 @@ static void variable_tracer_init (GfsVariableTracer * v)
   v->advection.gradient = gfs_center_van_leer_gradient;
   v->advection.flux = gfs_face_advection_flux;
   v->advection.v = GFS_VARIABLE1 (v);
-  v->advection.fv = gfs_res;
+  v->advection.fv = NULL;
 
   gfs_multilevel_params_init (&v->diffusion);
   v->diffusion.tolerance = 1e-6;
@@ -305,8 +262,8 @@ GfsVariableClass * gfs_variable_tracer_class (void)
 static void scale_residual (FttCell * cell, GfsVariable * res)
 {
   gdouble size = ftt_cell_size (cell);
-  gdouble dt = GFS_SIMULATION (gfs_variable_parent (res))->advection_params.dt;
-  GFS_VARIABLE (cell, res->i) = dt*GFS_STATE (cell)->res/(size*size);
+  gdouble dt = GFS_SIMULATION (res->domain)->advection_params.dt;
+  GFS_VARIABLE (cell, res->i) *= dt/(size*size);
 }
 
 static gboolean variable_residual_event (GfsEvent * event, GfsSimulation * sim)
diff --git a/src/variable.h b/src/variable.h
index 2432ebf..026a3e7 100644
--- a/src/variable.h
+++ b/src/variable.h
@@ -40,14 +40,14 @@ struct _GfsVariable {
 
   /*< public >*/
   guint i;
+  FttComponent component;
   gchar * name;
   gboolean centered;
   GfsVariableDerivedFunc derived;
   GfsVariableFineCoarseFunc fine_coarse;
   GtsContainer * sources;
   GfsSurfaceGenericBc * surface_bc;
-  GfsVariable * next, * permanent;
-  GtsObject * p;
+  GfsDomain * domain;
 };
 
 typedef struct _GfsVariableClass    GfsVariableClass;
@@ -67,23 +67,19 @@ struct _GfsVariableClass {
 						 gfs_variable_class())
 #define GFS_IS_VARIABLE(obj)         (gts_object_is_from_class (obj,\
 						 gfs_variable_class ()))
-#define gfs_variable_parent(v)         ((v)->p)
-#define gfs_variable_set_parent(v, pa) ((v)->p = GTS_OBJECT (pa))
 
 GfsVariableClass *    gfs_variable_class            (void);
 GfsVariable *         gfs_variable_new              (GfsVariableClass * klass,
-						     GtsObject * parent,
-						     const gchar * name,
-						     gboolean centered,
-						     guint i);
-GfsVariable *         gfs_variable_list_copy        (GfsVariable * v,
-						     GtsObject * parent);
-void                  gfs_variable_list_destroy     (GfsVariable * v);
-GfsVariable *         gfs_variable_from_name        (GfsVariable * variables,
+						     GfsDomain * domain,
 						     const gchar * name);
-GfsVariable *         gfs_variables_from_list       (GfsVariable * variables,
+#define               gfs_temporary_variable(d)     (gfs_variable_new (gfs_variable_class (),\
+                                                                      (d), NULL))
+GfsVariable *         gfs_variable_from_name        (GSList * i,
+						     const gchar * name);
+GSList *              gfs_variables_from_list       (GSList * i,
 						     gchar * list,
 						     gchar ** error);
+#define gfs_variable_set_vector(v, c)  ((v)->component = (c))
 
 /* GfsVariableTracer: header */
 
diff --git a/test/poisson/Makefile.am b/test/poisson/Makefile.am
index 39a30c5..9ee5304 100644
--- a/test/poisson/Makefile.am
+++ b/test/poisson/Makefile.am
@@ -8,7 +8,6 @@ LDADD = $(GFS2D_LIBS)
 AM_CFLAGS = -DFTT_2D=1
 
 noinst_PROGRAMS = \
-	poisson \
 	simple \
 	shapes
 
diff --git a/tools/gfs2oogl.c b/tools/gfs2oogl.c
index db70647..72e5d67 100644
--- a/tools/gfs2oogl.c
+++ b/tools/gfs2oogl.c
@@ -93,19 +93,21 @@ static gboolean stop (gdouble cost, guint nedge)
   return FALSE;
 }
 
-static void draw_vector (FttCell * cell, gdouble * scale)
+static void draw_vector (FttCell * cell, gpointer * data)
 {
+  gdouble * scale = data[0];
+  GfsVariable ** u = data[1];
   FILE * fp = stdout;
   FttVector pos, f;
 
   gfs_cell_cm (cell, &pos);
   
-  f.x = GFS_STATE (cell)->u*(*scale);
-  f.y = GFS_STATE (cell)->v*(*scale);
+  f.x = GFS_VARIABLE (cell, u[0]->i)*(*scale);
+  f.y = GFS_VARIABLE (cell, u[1]->i)*(*scale);
 #if FTT_2D
   f.z = 0.;
 #else
-  f.z = GFS_STATE (cell)->w*(*scale);
+  f.z = GFS_VARIABLE (cell, u[2]->i)*(*scale);
 #endif
   fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n",
 	   pos.x + f.x - (f.x - f.y/2.)/5.,
@@ -131,9 +133,8 @@ static void compute_mixed_vorticity (FttCell * cell, gpointer * data)
   FttVector g;
 
   g_assert (((cell)->flags & GFS_FLAG_DIRICHLET) != 0);
-  gfs_cell_dirichlet_gradient (cell, u->i, -1, 
-			       GFS_STATE (cell)->solid->fv, &g);
-  if (GFS_VELOCITY_COMPONENT (u->i) == FTT_X)
+  gfs_cell_dirichlet_gradient (cell, u->i, -1, GFS_STATE (cell)->solid->fv, &g);
+  if (u->component == FTT_X)
     GFS_VARIABLE (cell, v->i) -= g.y;
   else
     GFS_VARIABLE (cell, v->i) += g.x;
@@ -148,12 +149,12 @@ static void output_mixed_vorticity (FttCell * cell, GfsVariable * v)
 	  GFS_VARIABLE (cell, v->i)/size);
 }
 
-static void output_mixed_pressure (FttCell * cell, FILE * fp)
+static void output_mixed_pressure (FttCell * cell, GfsVariable * p)
 {
   GfsSolidVector * s = GFS_STATE (cell)->solid;
 
-  fprintf (fp, "%g %g %g %g\n", s->ca.x, s->ca.y, s->ca.z, 
-	   gfs_cell_dirichlet_value (cell, gfs_p, -1));
+  printf ("%g %g %g %g\n", s->ca.x, s->ca.y, s->ca.z, 
+	  gfs_cell_dirichlet_value (cell, p, -1));
 }
 
 /* SVertex: Header */
@@ -371,11 +372,11 @@ static gboolean advect (GfsDomain * domain,
   guint n = 10;
   gdouble h = ds/n;
   gboolean ad = TRUE;
-  GfsVariable * U = gfs_variable_from_name (domain->variables, "U"), * v;
+  GfsVariable ** U = gfs_domain_velocity (domain);
 
   while (n-- > 0 && ad) {
-    for (c = 0, v = U; c < 2/*FTT_DIMENSION*/; c++, v = v->next) {
-      ((gdouble *) &u)[c] = direction*gfs_interpolate (cell, *p, v);
+    for (c = 0; c < 2/*FTT_DIMENSION*/; c++) {
+      ((gdouble *) &u)[c] = direction*gfs_interpolate (cell, *p, U[c]);
       nu += ((gdouble *) &u)[c]*((gdouble *) &u)[c];
     }
     if (nu > 0.) {
@@ -386,8 +387,8 @@ static gboolean advect (GfsDomain * domain,
       cell = gfs_domain_locate (domain, ph, -1);
       if (cell != NULL) {
 	nu = 0.;
-	for (c = 0, v = U; c < 2/*FTT_DIMENSION*/; c++, v = v->next) {
-	  ((gdouble *) &u)[c] = direction*gfs_interpolate (cell, ph, v);
+	for (c = 0; c < 2/*FTT_DIMENSION*/; c++) {
+	  ((gdouble *) &u)[c] = direction*gfs_interpolate (cell, ph, U[c]);
 	  nu += ((gdouble *) &u)[c]*((gdouble *) &u)[c];
 	}
 	if (nu > 0.) {
@@ -664,10 +665,26 @@ static void write_stream (GSList * i, FILE * fp)
   }
 }
 
+static void update_var (FttCell * cell, gpointer * data)
+{
+  GfsVariable * v = data[0];
+  GfsFunction * f = data[1];
+
+  GFS_VARIABLE (cell, v->i) = gfs_function_value (f, cell);
+}
+
+static void velocity_norm (FttCell * cell, gpointer * data)
+{
+  GfsVariable * v = data[0];
+  GfsVariable ** u = data[1];
+  GFS_VARIABLE (cell, v->i) = gfs_vector_norm (cell, u);
+}
+
 int main (int argc, char * argv[])
 {
   int c = 0;
-  GfsVariable * v, * var = NULL;
+  GSList * i;
+  GfsVariable * var = NULL;
   GtsFile * fp;
   GtsSurface * surface = NULL;
   gboolean draw_surface = FALSE;
@@ -953,39 +970,50 @@ int main (int argc, char * argv[])
       return 1;
     }
 
-    if (color &&
-	!(var = gfs_variable_from_name (gfs_derived_first, color)) &&
-	!(var = gfs_variable_from_name (GFS_DOMAIN (simulation)->variables, color))) {
+    domain = GFS_DOMAIN (simulation);
+
+    if (color) {
+      GtsFile * fp = gts_file_new_from_string (color);
+      GfsFunction * f = gfs_function_new (gfs_function_class (), 0.);
+ 
+      gfs_function_read (f, domain, fp);
+      if (fp->type == GTS_ERROR) {
 	fprintf (stderr, 
-		 "gfs2oogl: unknown variable `%s'\n"
-		 "Try `gfs2oogl --help' for more information.\n",
-		 color);
-	return 1; /* failure */
+		 "gfs2oogl: incorrect `color' argument\n"
+		 "%d: %s\n",
+		 fp->pos, fp->error);
+	return 1;
+      }
+      gts_file_destroy (fp);
+      g_free (color);
+      
+      if (!(var = gfs_function_get_variable (f))) {
+	gpointer data[2];
+
+	data[0] = var = gfs_temporary_variable (domain);
+	data[1] = f;
+	gfs_domain_cell_traverse (domain,
+				  FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
+				  (FttCellTraverseFunc) update_var, data);
+      }
+      gts_object_destroy (GTS_OBJECT (f));
     }
 
     if (verbose)
       fprintf (stderr, "gfs2oogl: processing t = %10e\n", simulation->time.t);
 
-    domain = GFS_DOMAIN (simulation);
-
     if (!reinit)
       gfs_domain_match (domain);
     else
       gfs_simulation_refine (simulation);
 
-    v = domain->variables;
-    while (v) {
-      gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, v);
-      v = v->next;
+    i = domain->variables;
+    while (i) {
+      gfs_domain_bc (domain, FTT_TRAVERSE_LEAFS, -1, i->data);
+      i = i->next;
     }
 
     if (var != NULL) {
-      if (var->derived) {
-	gfs_variable_set_parent (var, GTS_OBJECT (domain));
-	gfs_domain_cell_traverse (domain, 
-				  FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-				  (FttCellTraverseFunc) var->derived, var);
-      }
       gfs_domain_cell_traverse (domain,
 				FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
 				(FttCellTraverseFunc) var->fine_coarse, var);
@@ -1057,26 +1085,26 @@ int main (int argc, char * argv[])
 	    printf ("%g %g %g %g\n", p.x, p.y, p.z, gfs_interpolate (cell, p, var));
 	}
       else {
-	GfsVariable * v;
+	GSList * j;
 	guint i = 4;
 
 	printf ("# 1:X 2:Y 3:Z ");
-	v = domain->variables;
-	while (v) {
-	  if (v->name)
-	    printf ("%d:%s ", i++, v->name);
-	  v = v->next;
+	j = domain->variables;
+	while (j) {
+	  GfsVariable * v = j->data;
+	  printf ("%d:%s ", i++, v->name);
+	  j = j->next;
 	}
 	printf ("\n");
 	while (fscanf (profile, "%lf %lf %lf", &p.x, &p.y, &p.z) == 3) {
 	  FttCell * cell = gfs_domain_locate (domain, p, -1);
 	  if (cell) {
 	    printf ("%g %g %g ", p.x, p.y, p.z);
-	    v = domain->variables;
-	    while (v) {
-	      if (v->name)
-		printf ("%g ", gfs_interpolate (cell, p, v));
-	      v = v->next;
+	    j = domain->variables;
+	    while (j) {
+	      GfsVariable * v = j->data;
+	      printf ("%g ", gfs_interpolate (cell, p, v));
+	      j = j->next;
 	    }
 	    printf ("\n");
 	  }
@@ -1086,13 +1114,16 @@ int main (int argc, char * argv[])
     else if (vector > 0.) {
       GtsRange stats;
       gdouble scale = 1.;
+      GfsVariable * norm = gfs_temporary_variable (domain);
+      gpointer data[2];
 
+      data[0] = norm;
+      data[1] = gfs_domain_velocity (domain);
       gfs_domain_cell_traverse (domain,
 				FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-				(FttCellTraverseFunc) gfs_velocity_norm, 
-				gfs_div);
-      stats = gfs_domain_stats_variable (domain, gfs_div, 
-					 FTT_TRAVERSE_LEAFS, -1);
+				(FttCellTraverseFunc) velocity_norm, data);
+      stats = gfs_domain_stats_variable (domain, norm, FTT_TRAVERSE_LEAFS, -1);
+      gts_object_destroy (GTS_OBJECT (norm));
       if (verbose)
 	fprintf (stderr, 
 		 "min: %g avg: %g| %g max: %g n: %7d\n",
@@ -1100,6 +1131,7 @@ int main (int argc, char * argv[])
       if (stats.max > 0.)
 	scale = vector*ftt_level_size (gfs_domain_depth (domain))/stats.max;
       printf ("(geometry \"vector-%g\" = LIST {\n", simulation->time.t);
+      data[0] = &scale;
 #if FTT_2D
       if (box == NULL)
 	box = gts_bbox_new (gts_bbox_class (), NULL,
@@ -1108,12 +1140,12 @@ int main (int argc, char * argv[])
 #else /* 3D */
       if (box == NULL)
 	gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-				  (FttCellTraverseFunc) draw_vector, &scale);
+				  (FttCellTraverseFunc) draw_vector, data);
       else
 #endif /* 3D */
       gfs_domain_cell_traverse_box (domain, box,
 				    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-				    (FttCellTraverseFunc) draw_vector, &scale);
+				    (FttCellTraverseFunc) draw_vector, data);
       printf ("})\n");
     }
     else if (draw_surface) {
@@ -1143,29 +1175,26 @@ int main (int argc, char * argv[])
     }
     else if (mixed && !strcmp (var->name, "Vorticity")) {
       FttComponent c;
-      GfsVariable * v;
+      GfsVariable ** u, * vort = gfs_temporary_variable (domain);
       gpointer data[2];
 
-      v = gfs_variable_from_name (domain->variables, "U");
+      u = gfs_domain_velocity (domain);
       gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-				 (FttCellTraverseFunc) gfs_cell_reset, 
-				 gfs_div);
-      v = gfs_variable_from_name (domain->variables, "U");
-      data[0] = gfs_div;
-      for (c = 0; c < FTT_DIMENSION; c++, v = v->next) {
-	gfs_domain_surface_bc (domain, v);
-	data[1] = v;
+				 (FttCellTraverseFunc) gfs_cell_reset, vort);
+      data[0] = vort;
+      for (c = 0; c < FTT_DIMENSION; c++) {
+	gfs_domain_surface_bc (domain, u[c]);
+	data[1] = u[c];
 	gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-	     (FttCellTraverseFunc) compute_mixed_vorticity, data);
+				   (FttCellTraverseFunc) compute_mixed_vorticity, data);
       }
       gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-				 (FttCellTraverseFunc) output_mixed_vorticity,
-				 gfs_div);
+				 (FttCellTraverseFunc) output_mixed_vorticity, vort);
+      gts_object_destroy (GTS_OBJECT (vort));
     }
     else if (mixed && !strcmp (var->name, "P"))
       gfs_domain_traverse_mixed (domain, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS,
-				 (FttCellTraverseFunc) output_mixed_pressure,
-				 stdout);
+				 (FttCellTraverseFunc) output_mixed_pressure, var);
     else if (even_stream > 0.) {
       GList * s, * i;
       ClosestGrid * grid;
diff --git a/tools/gfscompare.c b/tools/gfscompare.c
index a3cb768..32b52c0 100644
--- a/tools/gfscompare.c
+++ b/tools/gfscompare.c
@@ -109,7 +109,9 @@ static GtsSurface * surface_from_domain (GfsDomain * domain)
 static void difference_triangulated (GfsVertex * v, gpointer * data)
 {
   GtsSurface * s = data[0];
-  GfsVariable * var = data[1];
+  GfsVariable * var1 = data[1];
+  GfsVariable * var2 = data[2];
+  GfsVariable * e = data[3];
   GtsFace * f = gts_point_locate (GTS_POINT (v), s, NULL);
 
   if (f != NULL && gts_triangle_quality (GTS_TRIANGLE (f)) > 0.8) {
@@ -128,11 +130,11 @@ static void difference_triangulated (GfsVertex * v, gpointer * data)
     g_assert (det != 0.);
     a = (x*y2 - y*x2)/det;
     b = (y*x1 - x*y1)/det;
-    fv1 = GFS_VARIABLE (GFS_VERTEX (v1)->cell, var->i);
-    fv2 = GFS_VARIABLE (GFS_VERTEX (v2)->cell, var->i);
-    fv3 = GFS_VARIABLE (GFS_VERTEX (v3)->cell, var->i);
-    GTS_POINT (v)->z = GFS_STATE (v->cell)->dp = 
-      GFS_VARIABLE (v->cell, var->i) -
+    fv1 = GFS_VARIABLE (GFS_VERTEX (v1)->cell, var2->i);
+    fv2 = GFS_VARIABLE (GFS_VERTEX (v2)->cell, var2->i);
+    fv3 = GFS_VARIABLE (GFS_VERTEX (v3)->cell, var2->i);
+    GTS_POINT (v)->z = GFS_VARIABLE (v->cell, e->i) = 
+      GFS_VARIABLE (v->cell, var1->i) -
       (fv1 + a*(fv2 - fv1) + b*(fv3 - fv1));
   }
 }
@@ -148,7 +150,7 @@ static gboolean is_mixed (FttCell * cell, guint level)
   return FALSE;
 }
 
-static void inject (FttCell * cell)
+static void inject (FttCell * cell, GfsVariable * e)
 {
   if (!FTT_CELL_IS_LEAF (cell)) {
     FttCellChildren child;
@@ -157,15 +159,17 @@ static void inject (FttCell * cell)
     ftt_cell_children (cell, &child);
     for (i = 0; i < FTT_CELLS; i++)
       if (child.c[i]) {
-	GFS_STATE (child.c[i])->dp = GFS_STATE (cell)->dp;
-	inject (child.c[i]);
+	GFS_VARIABLE (child.c[i], e->i) = GFS_VARIABLE (cell, e->i);
+	inject (child.c[i], e);
       }
   }
 }
 
 static gboolean difference_tree (FttCell * cell,
 				 GfsDomain * ref,
-				 GfsVariable * v,
+				 GfsVariable * v1,
+				 GfsVariable * v2,
+				 GfsVariable * e,
 				 gdouble period)
 {
   guint level = ftt_cell_level (cell);
@@ -192,22 +196,22 @@ static gboolean difference_tree (FttCell * cell,
 
     ftt_cell_children (cell, &child);
     for (i = 0; i < FTT_CELLS; i++)
-      if (child.c[i] && difference_tree (child.c[i], ref, v, period))
+      if (child.c[i] && difference_tree (child.c[i], ref, v1, v2, e, period))
 	added = TRUE;
   }
   if (!added) {
-    GFS_STATE (cell)->dp = (GFS_VARIABLE (cell, v->i) -
-			    GFS_VARIABLE (locate, v->i));
-    inject (cell);
+    GFS_VARIABLE (cell, e->i) = (GFS_VARIABLE (cell, v1->i) -
+				 GFS_VARIABLE (locate, v2->i));
+    inject (cell, e);
   }
   return TRUE;
 }
 
 static void difference_box (GfsBox * box, gpointer * data)
 {
-  gdouble * period = data[2];
+  gdouble * period = data[4];
 
-  difference_tree (box->root, data[0], data[1], *period);
+  difference_tree (box->root, data[0], data[1], data[2], data[3], *period);
 }
 
 static void difference_constant (FttCell * cell, gpointer * data)
@@ -217,6 +221,7 @@ static void difference_constant (FttCell * cell, gpointer * data)
   gboolean * centered = data[3];
   gboolean * weighted = data[4];
   gdouble * weight = data[5];
+  GfsVariable * e = data[6];
   gdouble a = GFS_IS_MIXED (cell) ? GFS_STATE (cell)->solid->a : 1.;
 
   if ((full == -2 || 
@@ -225,7 +230,7 @@ static void difference_constant (FttCell * cell, gpointer * data)
       (!(*centered) || a >= 0.5)) {
     gdouble w = *weighted ? ftt_cell_volume (cell)*a : 1.;
 
-    *sum += w*GFS_STATE (cell)->dp;
+    *sum += w*GFS_VARIABLE (cell, e->i);
     *weight += w;
   }
 }
@@ -239,6 +244,7 @@ static void difference (FttCell * cell, gpointer * data)
   gboolean * weighted = data[4];
   gdouble * constant = data[5];
   gboolean * mixed = data[6];
+  GfsVariable * e = data[7];
   gdouble a = GFS_IS_MIXED (cell) ? GFS_STATE (cell)->solid->a : 1.;
 
   if ((!(*mixed) || a < 1.) &&
@@ -246,38 +252,41 @@ static void difference (FttCell * cell, gpointer * data)
        (full == -1 && !GFS_IS_MIXED (cell)) ||
        (full >= 0 && !is_mixed (cell, full))) &&
       (!(*centered) || a >= 0.5)) {
-    gfs_norm_add (norm, GFS_STATE (cell)->dp - *constant,
+    gfs_norm_add (norm, GFS_VARIABLE (cell, e->i) - *constant,
 		  *weighted ? ftt_cell_volume (cell)*a : 1.);
     if (*histogram)
-      printf ("%g %g\n", GFS_STATE (cell)->dp, a);
+      printf ("%g %g\n", GFS_VARIABLE (cell, e->i), a);
   }
   else
-    GFS_STATE (cell)->dp = 0.;
+    GFS_VARIABLE (cell, e->i) = 0.;
 }
 
 static void compute_gradient (FttCell * cell, gpointer * data) 
 {
   GfsVariable * v = data[0];
   FttComponent * c = data[1];
+  GfsVariable * g = data[2];
 
-  GFS_STATE (cell)->g[0] = 
+  GFS_VARIABLE (cell, g->i) = 
     gfs_center_gradient (cell, *c, v->i)/ftt_cell_size (cell);
 }
 
-static void compute_log (FttCell * cell) 
+static void compute_log (FttCell * cell, GfsVariable * e) 
 {
-  GFS_STATE (cell)->dp = log10 (fabs (GFS_STATE (cell)->dp) + 1e-10);
+  GFS_VARIABLE (cell, e->i) = log10 (fabs (GFS_VARIABLE (cell, e->i)) + 1e-10);
 }
 
-static void compute_absolute (FttCell * cell)
+static void compute_absolute (FttCell * cell, GfsVariable * e)
 {
-  GFS_STATE (cell)->dp =  fabs (GFS_STATE (cell)->dp);
+  GFS_VARIABLE (cell, e->i) =  fabs (GFS_VARIABLE (cell, e->i));
 }
 
 static void difference_centered (FttCell * cell, gpointer * data)
 {
   GfsDomain * ref = data[0];
-  GfsVariable * v = data[1];
+  GfsVariable * v1 = data[1];
+  GfsVariable * v2 = data[2];
+  GfsVariable * e = data[3];
   FttVector p;
   FttCell * locate;
 
@@ -286,8 +295,8 @@ static void difference_centered (FttCell * cell, gpointer * data)
   if (locate == NULL || ftt_cell_level (locate) < ftt_cell_level (cell)) {
     fprintf (stderr, "gfscompare: the files are not comparable\n");
     exit (1);
-  }  
-  GFS_STATE (cell)->dp = GFS_VARIABLE (cell, v->i) - gfs_interpolate (locate, p, v);
+  }
+  GFS_VARIABLE (cell, e->i) = GFS_VARIABLE (cell, v1->i) - gfs_interpolate (locate, p, v2);
 }
 
 int main (int argc, char * argv[])
@@ -295,7 +304,8 @@ int main (int argc, char * argv[])
   GtsFile * fp;
   FILE * f;
   int c = 0;
-  GfsVariable * var;
+  gchar * name;
+  GfsVariable * var1, * var2, * e;
   GfsSimulation * s1, * s2;
   
   gboolean verbose = FALSE;
@@ -310,7 +320,7 @@ int main (int argc, char * argv[])
   FttComponent gradient = FTT_DIMENSION;
 
   GfsNorm norm;
-  gpointer data[7];
+  gpointer data[8];
 
   gboolean refined_error = FALSE;
   gboolean histogram = FALSE;
@@ -495,15 +505,7 @@ int main (int argc, char * argv[])
 	     "Try `gfscompare --help' for more information.\n");
     return 1; /* failure */
   }
-  var = gfs_variable_from_name (gfs_p, argv[optind]);
-  if (var == NULL) {
-    fprintf (stderr, 
-	     "gfscompare: unknown variable `%s'\n"
-	     "Try `gfscompare --help' for more information.\n",
-	     argv[optind]);
-    return 1; /* failure */
-  }
-  optind++;
+  name = argv[optind++];
 
   f = fopen (fname1, "rt");
   if (f == NULL) {
@@ -537,13 +539,31 @@ int main (int argc, char * argv[])
   gts_file_destroy (fp);
   fclose (f);
 
+  var1 = gfs_variable_from_name (GFS_DOMAIN (s1)->variables, name);
+  if (var1 == NULL) {
+    fprintf (stderr, 
+	     "gfscompare: unknown variable `%s' for `%s'\n"
+	     "Try `gfscompare --help' for more information.\n",
+	     name, fname1);
+    return 1; /* failure */
+  }
+
+  var2 = gfs_variable_from_name (GFS_DOMAIN (s2)->variables, name);
+  if (var2 == NULL) {
+    fprintf (stderr, 
+	     "gfscompare: unknown variable `%s' for `%s'\n"
+	     "Try `gfscompare --help' for more information.\n",
+	     name, fname2);
+    return 1; /* failure */
+  }
+
   if (verbose) {
     GtsRange s;
 
     norm = gfs_domain_norm_variable (GFS_DOMAIN (s1),
-				     var, FTT_TRAVERSE_LEAFS, -1);
+				     var1, FTT_TRAVERSE_LEAFS, -1);
     s = gfs_domain_stats_variable (GFS_DOMAIN (s1),
-				   var, FTT_TRAVERSE_LEAFS, -1);
+				   var1, FTT_TRAVERSE_LEAFS, -1);
     fprintf (stderr, 
 	     "%s:\n"
 	     "  first: %g second: %g infty: %g w: %g\n"
@@ -552,9 +572,9 @@ int main (int argc, char * argv[])
 	     norm.first, norm.second, norm.infty, norm.w,
 	     s.min, s.mean, s.stddev, s.max);
     norm = gfs_domain_norm_variable (GFS_DOMAIN (s2),
-				    var, FTT_TRAVERSE_LEAFS, -1);
+				    var2, FTT_TRAVERSE_LEAFS, -1);
     s = gfs_domain_stats_variable (GFS_DOMAIN (s2),
-				   var, FTT_TRAVERSE_LEAFS, -1);
+				   var2, FTT_TRAVERSE_LEAFS, -1);
     fprintf (stderr, 
 	     "%s:\n"
 	     "  first: %g second: %g infty: %g w: %g\n"
@@ -565,22 +585,29 @@ int main (int argc, char * argv[])
   }
 
   if (gradient < FTT_DIMENSION) {
-    gpointer data[2];
+    gpointer data[3];
+    GfsVariable * g1 = gfs_temporary_variable (GFS_DOMAIN (s1));
+    GfsVariable * g2 = gfs_temporary_variable (GFS_DOMAIN (s2));
 
-    data[0] = var;
+    data[0] = var1;
     data[1] = &gradient;
-
+    data[2] = g1;
     gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			      (FttCellTraverseFunc) compute_gradient, data);
+    data[0] = var2;
+    data[2] = g2;
     gfs_domain_cell_traverse (GFS_DOMAIN (s2), 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			      (FttCellTraverseFunc) compute_gradient, data);
-    var = gfs_gx;
+    var1 = g1;
+    var2 = g2;
   }
 
   data[0] = s2;
-  data[1] = var;
+  data[1] = var1;
+  data[2] = var2;
+  data[3] = e = gfs_temporary_variable (GFS_DOMAIN (s1));
   if (centered)
     gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
@@ -588,7 +615,7 @@ int main (int argc, char * argv[])
 #if FTT_2D
   else if (triangulate) {
     GtsSurface * ss1, * ss2;
-    gpointer data[2];
+    gpointer data[3];
 
     gfs_simulation_refine (s1);
     gfs_simulation_refine (s2);
@@ -597,7 +624,9 @@ int main (int argc, char * argv[])
     ss1 = surface_from_domain (GFS_DOMAIN (s1));
     ss2 = surface_from_domain (GFS_DOMAIN (s2));
     data[0] = ss2;
-    data[1] = var;
+    data[1] = var1;
+    data[2] = var2;
+    data[3] = e;
     gts_surface_foreach_vertex (ss1, (GtsFunc) difference_triangulated, data);
   }
 #endif /* FTT_2D */
@@ -605,24 +634,20 @@ int main (int argc, char * argv[])
     if (!extensive) {
       gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 				FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-          (FttCellTraverseFunc) gfs_get_from_below_intensive,
-				var);
+				(FttCellTraverseFunc) gfs_get_from_below_intensive, var1);
       gfs_domain_cell_traverse (GFS_DOMAIN (s2), 
 				FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-          (FttCellTraverseFunc) gfs_get_from_below_intensive,
-				var);
+				(FttCellTraverseFunc) gfs_get_from_below_intensive, var2);
     }
     else {
       gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 				FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-          (FttCellTraverseFunc) gfs_get_from_below_extensive,
-				var);
+				(FttCellTraverseFunc) gfs_get_from_below_extensive, var1);
       gfs_domain_cell_traverse (GFS_DOMAIN (s2), 
 				FTT_POST_ORDER, FTT_TRAVERSE_NON_LEAFS, -1,
-          (FttCellTraverseFunc) gfs_get_from_below_extensive,
-				var);
+				(FttCellTraverseFunc) gfs_get_from_below_extensive, var2);
     }
-    data[2] = &period;
+    data[4] = &period;
     gts_container_foreach (GTS_CONTAINER (s1), (GtsFunc) difference_box, data);
   }
 
@@ -635,10 +660,10 @@ int main (int argc, char * argv[])
 
     data[1] = &sum;
     data[5] = &weight;
+    data[6] = e;
     gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 			      FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			      (FttCellTraverseFunc) difference_constant, 
-			      data);
+			      (FttCellTraverseFunc) difference_constant, data);
     constant = weight > 0. ? sum/weight : 0.;
   }
   
@@ -646,6 +671,7 @@ int main (int argc, char * argv[])
   data[1] = &norm;
   data[5] = &constant;
   data[6] = &mixed;
+  data[7] = e;
   gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 			    FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
 			    (FttCellTraverseFunc) difference, data);
@@ -656,7 +682,7 @@ int main (int argc, char * argv[])
 	     norm.first, norm.second, norm.infty, norm.w);
     if (refined_error) {
       norm = gfs_domain_norm_variable (GFS_DOMAIN (s1),
-				       gfs_dp, FTT_TRAVERSE_LEVEL,
+				       e, FTT_TRAVERSE_LEVEL,
 				       gfs_domain_depth (GFS_DOMAIN (s1)));
       fprintf (stderr, 
 	  "refined err first: %10.3e second: %10.3e infty: %10.3e w: %g\n",
@@ -672,16 +698,15 @@ int main (int argc, char * argv[])
     if (take_log)
       gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 			       FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			       (FttCellTraverseFunc) compute_log, NULL);
+			       (FttCellTraverseFunc) compute_log, e);
     else if (absolute)
       gfs_domain_cell_traverse (GFS_DOMAIN (s1), 
 			       FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
-			       (FttCellTraverseFunc) compute_absolute, NULL);
+			       (FttCellTraverseFunc) compute_absolute, e);
     if (squares) {
-      GtsRange stats = gfs_domain_stats_variable (GFS_DOMAIN (s1), gfs_dp,
-						  FTT_TRAVERSE_LEAFS, -1);
+      GtsRange stats = gfs_domain_stats_variable (GFS_DOMAIN (s1), e, FTT_TRAVERSE_LEAFS, -1);
 
-      gfs_write_squares (GFS_DOMAIN (s1), gfs_dp, 
+      gfs_write_squares (GFS_DOMAIN (s1), e, 
 			 min < G_MAXDOUBLE ? min : stats.min, 
 			 max > - G_MAXDOUBLE ? max : stats.max,
 			 FTT_TRAVERSE_LEAFS, -1, 
@@ -689,13 +714,12 @@ int main (int argc, char * argv[])
     }
 #if FTT_2D
     else if (gnuplot)
-      gfs_write_gnuplot (GFS_DOMAIN (s1), gfs_dp,
+      gfs_write_gnuplot (GFS_DOMAIN (s1), e,
 			 FTT_TRAVERSE_LEAFS, -1, 
 			 NULL, stdout);
 #endif /* FTT_2D */
     else
-	gfs_write_gts (GFS_DOMAIN (s1), 
-		     gfs_dp, FTT_TRAVERSE_LEAFS, -1, NULL, stdout);
+	gfs_write_gts (GFS_DOMAIN (s1), e, FTT_TRAVERSE_LEAFS, -1, NULL, stdout);
   }
 
   return 0;

-- 
Gerris Flow Solver



More information about the debian-science-commits mailing list