diff --git a/ci/docker_images/ubuntu/bionic/Dockerfile b/ci/docker_images/ubuntu/bionic/Dockerfile
index 369eb2fbaaf78dc021a89228e25b87facc4094db..d8a3985a950bdb115008af999cecff3341ec218c 100644
--- a/ci/docker_images/ubuntu/bionic/Dockerfile
+++ b/ci/docker_images/ubuntu/bionic/Dockerfile
@@ -103,7 +103,7 @@ RUN  wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
  && echo 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main'     >> /etc/apt/sources.list \
  && echo 'deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main' >> /etc/apt/sources.list \
  && apt-get update                                                                                 \
- && apt-get install --assume-yes llvm-3.8 clang-3.8 libllvm3.8 libclang-3.8-dev
+ && apt-get install --assume-yes llvm-3.9 clang-3.9 libllvm3.9 libclang-3.9-dev
 
 # Intel OpenCl
 RUN cd /tmp                                                                                 \
@@ -189,7 +189,7 @@ RUN cd /tmp                                       \
  && mkdir pycairo                                 \
  && find /usr/ -name 'pycairo.h' -exec cp {} ./pycairo/pycairo.h \; \
  && CPPFLAGS=-I. ./configure                      \
- && CPPFLAGS=-I. make                             \
+ && CPPFLAGS=-I. make -j16                        \
  && make install                                  \
  && cd -                                          \
  && rm -Rf /tmp/graph-tool-2.26
diff --git a/hysop/operator/base/custom_symbolic_operator.py b/hysop/operator/base/custom_symbolic_operator.py
index e287b978c70b3e3c984d132717fe503077bd4b62..dc7cdc3edbffead545e937a1eaa3bad774a3ddb6 100644
--- a/hysop/operator/base/custom_symbolic_operator.py
+++ b/hysop/operator/base/custom_symbolic_operator.py
@@ -313,7 +313,7 @@ class SymbolicExpressionInfo(object):
         self.field_requirements = field_requirements
         self.array_requirements = array_requirements
         self.has_direction = (self.direction is not None)
-        self.direction     = first_not_None(self.direction, 0)
+        self.direction = first_not_None(self.direction, 0)
 
     def discretize_expressions(self, input_dfields, output_dfields):
         check_instance(input_dfields,  dict, keys=Field, values=CartesianDiscreteFieldView)
@@ -605,7 +605,8 @@ class SymbolicExpressionParser(object):
         elif isinstance(expr, (AppliedSymbolicField, SymbolicScalarParameter)):
             cls.read(variables, info, expr)
         elif isinstance(expr, sm.Derivative):
-            info.determine_direction(*expr.args[1:])
+            dvars = tuple(v[0] for v in expr.args[1:] for _ in xrange(v[1]))
+            info.determine_direction(*dvars)
             cls.parse_subexpr(variables, info, expr.args[0])
         elif isinstance(expr, sm.Expr):
             for e in expr.args:
@@ -702,8 +703,10 @@ class SymbolicExpressionParser(object):
                     msg='\nError during temporary scalars expansion pass.'
                     msg+='\nSymbolic expressions were:'
                     msg+='\n   '+'\n   '.join(str(e) for e in info.exprs)
-                    msg+='\nExpression {}/ {} use temporary scalars that have not been defined yet:{}'
-                    msg=msg.format(i, expr, '\n  *'+'\n  *'.join(tmp.name for tmp in unknown_tmp))
+                    msg+='\nExpression {}/ {} use temporary scalars that have not been '
+                    msg+='defined yet:{}'
+                    msg=msg.format(i, expr, '\n  *'+'\n  *'.join(tmp.name 
+                        for tmp in unknown_tmp))
                     raise ValueError(msg)
                 try:
                     rhs = rhs.xreplace(tmp_map)
@@ -867,9 +870,10 @@ class SymbolicExpressionParser(object):
             if (direction is not None):
                 xd = space_symbols[direction]
                 if (unique_dvars - set([xd])):
-                    msg='Expression already contained a derivative with respect to {} (direction {}, '
-                    msg+='{}-axis).'
-                    msg+='\nFound a new derivative direction which is not compatible with the current one.'
+                    msg='Expression already contained a derivative with respect to {} '
+                    msg+='(direction {}, {}-axis).'
+                    msg+='\nFound a new derivative direction which is not compatible '
+                    msg+='with the current one.'
                     msg+='\nCannot differentiate with respect to {}.'
                     msg=msg.format(xd, direction, DirectionLabels[direction],
                             ', '.join(str(x) for x in (unique_dvars - set([xd]))))
@@ -929,14 +933,10 @@ class SymbolicExpressionParser(object):
         
         dexprs=()
         discretization_info = ExprDiscretizationInfo()
-        print 'DISCRETIZE: ', info.axes
         for expr in info.exprs:
-            print 'EXPR:',expr
             dexpr, di = cls.discretize_one(info, expr)
-            print 'DEXPR:',dexpr
             dexprs += (dexpr,)
             discretization_info.update(di)
-        print
         info.dexprs = dexprs
         info.discretization_info = discretization_info
 
@@ -1085,14 +1085,6 @@ class SymbolicExpressionParser(object):
         assert (symbols is not None)
 
         i = symbols.index(expr)
-        print '== SYMBOLS =='
-        print 'AXES=',axes
-        print 'SYMBOLS',symbols
-        print 'EXPR=',expr
-        print 'IDX=',i
-        print 'AXES[IDX]',axes[i]
-        print 'NEW SYMBOL',symbols[axes[i]]
-        print '============'
         return symbols[axes[i]]
 
 class CustomSymbolicOperatorBase(DirectionalOperatorBase):
diff --git a/hysop/operator/tests/test_analytic.py b/hysop/operator/tests/test_analytic.py
index aff8929592948a258d574360dcc0a43ac5fd82c6..57b61f4f9c4226466e2e4f0bcbc192021b397ab6 100644
--- a/hysop/operator/tests/test_analytic.py
+++ b/hysop/operator/tests/test_analytic.py
@@ -149,6 +149,7 @@ class TestAnalyticField(object):
                                     **base_kwds)
                 print
             elif impl is Implementation.OPENCL:
+                assert base_kwds['field'] is F
                 msg='   *Opencl: '
                 print msg
                 for cl_env in iter_clenv():
@@ -255,7 +256,7 @@ class TestAnalyticField(object):
     def test_2d_float32(self):
         self._test(dim=2, dtype=npw.float32)
     def test_3d_float32(self):
-        self._test(dim=3, dtype=npw.float64)
+        self._test(dim=3, dtype=npw.float32)
     def test_1d_float64(self):
         self._test(dim=1, dtype=npw.float64)
     def test_2d_float64(self):
diff --git a/hysop/symbolic/directional.py b/hysop/symbolic/directional.py
index 7ce51e8bbe0afa0b355d35cae40db5d56a1a68cc..5869f932f10ea62a37a1ca4289311e8d4d93100a 100644
--- a/hysop/symbolic/directional.py
+++ b/hysop/symbolic/directional.py
@@ -39,7 +39,6 @@ def split(F, fixed_residue, force_residue, coords):
     if isinstance(F, Assignment):
         msg='Expression cannot be of type Assignment: {}'.format(F)
         raise TypeError(msg)
-    print 'SPLIT {} with FIXED_RESIDUE={} and FORCE_RESIDUE={}'.format(F, fixed_residue, force_residue)
     res = {}
     coords = first_not_None(coords, space_symbols)
     for i, xi in enumerate(coords):
diff --git a/hysop/symbolic/field.py b/hysop/symbolic/field.py
index c627d0875ae50d561ff6ae3e25b79e58b9d5d138..d20c8e8e241197d67cbf1fed4e2ae0b268725b2f 100644
--- a/hysop/symbolic/field.py
+++ b/hysop/symbolic/field.py
@@ -72,6 +72,27 @@ class SymbolicField(FieldBase, UndefinedFunction):
         check_instance(field, Field)
         return super(SymbolicField, cls).__new__(cls, bases=bases, 
                 field=field, fn=fn, **kwds)
+    
+    def __hash__(self):
+        "Fix sympy v1.2 hashes"
+        h = super(SymbolicField, self).__hash__()
+        for hc in (self.field, self.index):
+            h ^= hash(h)
+        return h
+
+    def __eq__(self, other):
+        "Fix sympy v1.2 eq"
+        eq = super(SymbolicField, self).__eq__(other)
+        if (eq is not True):
+            return eq
+        for (lhc,rhc) in zip((self.field, self.index), (other.field, other.index)):
+            eq &= (lhc == rhc)
+        return eq
+
+    def __ne__(self, other):
+        "Fix sympy v1.2 neq"
+        return not (self==other)
+
 
 class AppliedSymbolicField(AppliedSymbolicFunction):
     """Applied scalar fields, hold a reference to a continuous field."""
@@ -93,6 +114,7 @@ class AppliedSymbolicField(AppliedSymbolicFunction):
     def index(self):
         """Get component index of the target field."""
         return type(self).index
+
     @property 
     def indexed_field(self):
         """Get a unique identifier for an indexed field component."""