diff --git a/phasefield/analytical.py b/phasefield/analytical.py
index a2e0f5f7a34ed22899c5377daaa517133d441701..5cae60b6ea8b261eacc9fc6fb6b7ee8a2d4a6b55 100644
--- a/phasefield/analytical.py
+++ b/phasefield/analytical.py
@@ -177,3 +177,17 @@ def createForceUpdateEquations(numPhases, forceField, phiField, muField, dx=1):
         forceSweepEqs.append(sp.Eq(forceField(d), rhs))
     return forceSweepEqs
 
+
+def cahnHilliardFdKernel(phaseIdx, phi, mu, velocity, mobility, dx, dt, target='cpu'):
+    from pystencils.finitedifferences import transient, advection, diffusion, Discretization2ndOrder
+    cahnHilliard = transient(phi, phaseIdx) + advection(phi, velocity, phaseIdx) - diffusion(mu, mobility, phaseIdx)
+    discretizedEq = Discretization2ndOrder(dx, dt)(cahnHilliard)
+    updateRule = [sp.Eq(phi.newFieldWithDifferentName('dst')(phaseIdx),
+                        discretizedEq)]
+
+    if target == 'cpu':
+        from pystencils.cpu import createKernel, makePythonFunction
+        return makePythonFunction(createKernel(updateRule))
+    elif target == 'gpu':
+        from pystencils.gpucuda import createCUDAKernel, makePythonFunction
+        return makePythonFunction(createCUDAKernel(updateRule))
diff --git a/phasefield/plot.py b/phasefield/plot.py
index eb58f368fb9413ebd7366aa257cf7f8c9c1c0c8b..652943f9bb3435745a01e7a33951c3e625ee61fa 100644
--- a/phasefield/plot.py
+++ b/phasefield/plot.py
@@ -1,7 +1,7 @@
 from lbmpy.plot2d import *
 
 
-def drawAngles(ax, groupedPoints):
+def _drawAngles(ax, groupedPoints):
     from matplotlib.lines import Line2D
     from matplotlib.text import Annotation
     from lbmpy.phasefield.postprocessing import getAngle
@@ -32,25 +32,33 @@ def drawAngles(ax, groupedPoints):
     ax.add_line(lines)
 
     angle = getAngle(*groupedPoints)
-    text = Annotation('%.1f°' % (angle,), (textPos[1], textPos[0]), axes=ax, fontsize=15, ha=ha, va=va)
-    ax.add_artist(text)
+    annotation = Annotation('%.1f°' % (angle,), (textPos[1], textPos[0]), axes=ax, fontsize=15, ha=ha, va=va)
+    ax.add_artist(annotation)
 
 
-def plotAngles(phi0, phi1, phi2, branchingDistance=0.5, onlyFirst=True):
+def phaseIndices(phi, **kwargs):
+    singlePhaseArrays = [phi[..., i] for i in range(phi.shape[-1])]
+    lastPhase = 1 - sum(singlePhaseArrays)
+    singlePhaseArrays.append(lastPhase)
+    idxArray = np.array(singlePhaseArrays).argmax(axis=0)
+    return scalarField(idxArray, **kwargs)
+
+
+def angles(phi0, phi1, phi2, branchingDistance=0.5, branchingPointFilter=3, onlyFirst=True):
     from lbmpy.phasefield.postprocessing import getTriplePointInfos
 
     levels = [0.5, ]
     scalarFieldContour(phi0, levels=levels)
     scalarFieldContour(phi1, levels=levels)
-    scalarFieldContour(phi2, levels=levels);
+    scalarFieldContour(phi2, levels=levels)
 
     if onlyFirst:
-        angleInfo = getTriplePointInfos(phi0, phi1, phi2, branchingDistance)
+        angleInfo = getTriplePointInfos(phi0, phi1, phi2, branchingDistance, branchingPointFilter)
     else:
-        angleInfo = getTriplePointInfos(phi0, phi1, phi2, branchingDistance) +\
-                    getTriplePointInfos(phi1, phi0, phi2, branchingDistance) +\
-                    getTriplePointInfos(phi2, phi1, phi0, branchingDistance)
+        angleInfo = getTriplePointInfos(phi0, phi1, phi2, branchingDistance, branchingPointFilter) +\
+                    getTriplePointInfos(phi1, phi0, phi2, branchingDistance, branchingPointFilter) +\
+                    getTriplePointInfos(phi2, phi1, phi0, branchingDistance, branchingPointFilter)
 
     for points in angleInfo:
-        drawAngles(gca(), points)
+        _drawAngles(gca(), points)
 
diff --git a/phasefield/postprocessing.py b/phasefield/postprocessing.py
index 63debd11196be656fb7f677348c1df676c3d8a5b..a6c63bea0f652bf8867435166a75fd6e5d1b6f72 100644
--- a/phasefield/postprocessing.py
+++ b/phasefield/postprocessing.py
@@ -33,19 +33,19 @@ def findJumpIndices(array, threshold=0, minLength=3):
         array = array[jump + minLength:]
 
 
-def findBranchingPoint(pathVertices1, pathVertices2, maxDistance=0.5):
+def findBranchingPoint(pathVertices1, pathVertices2, maxDistance=0.5, branchingPointFilter=3):
     tree = scipy.spatial.KDTree(pathVertices1)
     distances, indices = tree.query(pathVertices2, k=1, distance_upper_bound=maxDistance)
     distances[distances == np.inf] = -1
-    jumpIndices = findJumpIndices(distances, 0, 3)
+    jumpIndices = findJumpIndices(distances, 0, branchingPointFilter)
     return pathVertices2[jumpIndices]
 
 
-def findAllBranchingPoints(phaseField1, phaseField2, maxDistance=0.1):
+def findAllBranchingPoints(phaseField1, phaseField2, maxDistance=0.1, branchingPointFilter=3):
     result = []
     isoLines = [getIsolines(p, level=0.5, refinementFactor=4) for p in (phaseField1, phaseField2)]
     for path1, path2 in itertools.product(*isoLines):
-        bbs = findBranchingPoint(path1, path2, maxDistance)
+        bbs = findBranchingPoint(path1, path2, maxDistance, branchingPointFilter)
         result += list(bbs)
     return np.array(result)
 
@@ -114,7 +114,7 @@ def getAngle(pMid, p1, p2):
     return result
 
 
-def getTriplePointInfos(phi0, phi1, phi2, branchingDistance=0.5):
+def getTriplePointInfos(phi0, phi1, phi2, branchingDistance=0.5, branchingPointFilter=3):
     """
 
     :param branchingDistance: where the 1/2 contour lines  move apart farther than this value, the
@@ -126,9 +126,11 @@ def getTriplePointInfos(phi0, phi1, phi2, branchingDistance=0.5):
     # the angle at the triple points is measured with contour lines of level 1/2 at "branching points"
     # i.e. at points where the lines move away from each other
 
-    bb1 = findAllBranchingPoints(phi0, phi1, branchingDistance)
-    bb2 = findAllBranchingPoints(phi0, phi2, branchingDistance)
+    bb1 = findAllBranchingPoints(phi0, phi1, branchingDistance, branchingPointFilter)
+    bb2 = findAllBranchingPoints(phi0, phi2, branchingDistance, branchingPointFilter)
     ip = findAllIntersectionPoints(phi0, phi1)
     return groupPoints(ip, np.vstack([bb1, bb2]))
 
 
+def getAnglesOfPhase(phi0, phi1, phi2, branchingDistance=0.5, branchingPointFilter=3):
+    return [getAngle(*r) for r in getTriplePointInfos(phi0, phi1, phi2, branchingDistance, branchingPointFilter)]
diff --git a/phasefield/scenario.py b/phasefield/scenario.py
index 2d55802d735fd5af40891cc2bf42dad5467e2a9e..97eed47f2cd3591e06abffefb5511a049ea656a9 100644
--- a/phasefield/scenario.py
+++ b/phasefield/scenario.py
@@ -85,11 +85,21 @@ class PhasefieldScenario(object):
         self.lbSweepHydro = createLatticeBoltzmannFunction(force=[forceField(i) for i in range(D)],
                                                            output={'velocity': velField},
                                                            optimizationParams=optimizationParams, **kwargs)
-        self.lbSweepsCH = [createCahnHilliardLbFunction(stencil, mobilityRelaxationRates[i],
-                                                        velField, muField(i), phiField(i), optimizationParams, gamma)
-                           for i in range(numPhases-1)]
 
-        self.lbSweeps = [self.lbSweepHydro] + self.lbSweepsCH
+        useFdForCahnHilliard = True
+        if useFdForCahnHilliard:
+            dt = 0.01
+            mobility = 1
+            from lbmpy.phasefield.analytical import cahnHilliardFdKernel
+            self.sweepsCH = [cahnHilliardFdKernel(i, phiField, muField, velField, mobility,
+                                                  dx, dt, optimizationParams['target'])
+                             for i in range(numPhases-1)]
+        else:
+            self.sweepsCH = [createCahnHilliardLbFunction(stencil, mobilityRelaxationRates[i],
+                                                          velField, muField(i), phiField(i), optimizationParams, gamma)
+                             for i in range(numPhases-1)]
+
+        self.lbSweeps = [self.lbSweepHydro] + self.sweepsCH
 
         self._pdfPeriodicityHandler = PeriodicityHandling(self._pdfArrays[0][0].shape, (True, True, True),
                                                           optimizationParams['target'])