diff --git a/lbmpy/phasefield_allen_cahn/__init__.py b/lbmpy/phasefield_allen_cahn/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lbmpy/phasefield_allen_cahn/analytical.py b/lbmpy/phasefield_allen_cahn/analytical.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fb5b9e244e53c1d7209f2573be87683b48eebc1
--- /dev/null
+++ b/lbmpy/phasefield_allen_cahn/analytical.py
@@ -0,0 +1,12 @@
+
+def analytic_rising_speed(gravitational_acceleration, bubble_diameter, viscosity_gas):
+    r"""
+    Calculated the analytical rising speed of a bubble. This is the expected end rising speed.
+    Args:
+        gravitational_acceleration: the gravitational acceleration acting in the simulation scenario. Usually it gets
+                                    calculated based on dimensionless parameters which describe the scenario
+        bubble_diameter: the diameter of the bubble at the beginning of the simulation
+        viscosity_gas: the viscosity of the fluid inside the bubble
+    """
+    result = -(gravitational_acceleration * bubble_diameter * bubble_diameter) / (12.0 * viscosity_gas)
+    return result
diff --git a/lbmpy/phasefield_allen_cahn/force_model.py b/lbmpy/phasefield_allen_cahn/force_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..548a32538bf353c058011bc7a0a9654067636de0
--- /dev/null
+++ b/lbmpy/phasefield_allen_cahn/force_model.py
@@ -0,0 +1,34 @@
+import sympy as sp
+import numpy as np
+
+from lbmpy.forcemodels import Simple
+
+
+class MultiphaseForceModel:
+    r"""
+    A force model based on PhysRevE.96.053301. This model realises the modified equilibrium distributions meaning the
+    force gets shifted by minus one half multiplied with the collision operator
+    """
+    def __init__(self, force, rho=1):
+        self._force = force
+        self._rho = rho
+
+    def __call__(self, lb_method):
+
+        simple = Simple(self._force)
+        force = [f / self._rho for f in simple(lb_method)]
+
+        moment_matrix = lb_method.moment_matrix
+        relaxation_rates = sp.Matrix(np.diag(lb_method.relaxation_rates))
+        mrt_collision_op = moment_matrix.inv() * relaxation_rates * moment_matrix
+
+        return -0.5 * mrt_collision_op * sp.Matrix(force) + sp.Matrix(force)
+
+    def macroscopic_velocity_shift(self, density):
+        return default_velocity_shift(self._rho, self._force)
+
+# --------------------------------  Helper functions  ------------------------------------------------------------------
+
+
+def default_velocity_shift(density, force):
+    return [f_i / (2 * density) for f_i in force]
diff --git a/lbmpy/phasefield_allen_cahn/kernel_equations.py b/lbmpy/phasefield_allen_cahn/kernel_equations.py
new file mode 100644
index 0000000000000000000000000000000000000000..17549923402a495e876a8705772821a4cc37303c
--- /dev/null
+++ b/lbmpy/phasefield_allen_cahn/kernel_equations.py
@@ -0,0 +1,336 @@
+from pystencils.fd.derivation import FiniteDifferenceStencilDerivation
+from lbmpy.maxwellian_equilibrium import get_weights
+from pystencils import Assignment, AssignmentCollection
+from pystencils.simp import sympy_cse_on_assignment_list
+
+import sympy as sp
+import numpy as np
+
+
+def chemical_potential_symbolic(phi_field, stencil, beta, kappa):
+    r"""
+    Get a symbolic expression for the chemical potential according to equation (5) in PhysRevE.96.053301.
+    Args:
+        phi_field: the phase-field on which the chemical potential is applied
+        stencil: stencil of the lattice Boltzmann step
+        beta: coefficient related to surface tension and interface thickness
+        kappa: coefficient related to surface tension and interface thickness
+    """
+    dimensions = len(stencil[0])
+
+    deriv = FiniteDifferenceStencilDerivation((0, 0), stencil)
+    # assume the stencil is symmetric
+    deriv.assume_symmetric(0)
+    deriv.assume_symmetric(1)
+    if dimensions == 3:
+        deriv.assume_symmetric(2)
+
+    # set weights for missing degrees of freedom in the calculation
+    if len(stencil) == 9:
+        deriv.set_weight((1, 1), sp.Rational(1, 6))
+        deriv.set_weight((0, -1), sp.Rational(2, 3))
+        deriv.set_weight((0, 0), sp.Rational(-10, 3))
+    if len(stencil) == 27:
+        deriv.set_weight((1, 1, 1), sp.Rational(1, 48))
+
+    # assume the stencil is isotropic
+    res = deriv.get_stencil(isotropic=True)
+
+    # get the chemical potential
+    mu = 4.0 * beta * phi_field.center * (phi_field.center - 1.0) * (phi_field.center - 0.5) - \
+        kappa * res.apply(phi_field.center)
+    return mu
+
+
+def isotropic_gradient_symbolic(phi_field, stencil):
+    r"""
+    Get a symbolic expression for the isotropic gradient of the phase-field
+    Args:
+        phi_field: the phase-field on which the isotropic gradient is applied
+        stencil: stencil of the lattice Boltzmann step
+    """
+    dimensions = len(stencil[0])
+    deriv = FiniteDifferenceStencilDerivation((0, ), stencil)
+
+    deriv.assume_symmetric(0, anti_symmetric=True)
+    deriv.assume_symmetric(1, anti_symmetric=False)
+    if dimensions == 3:
+        deriv.assume_symmetric(2, anti_symmetric=False)
+
+    if len(stencil) == 19:
+        deriv.set_weight((0, 0, 0), sp.Integer(0))
+        deriv.set_weight((1, 0, 0), sp.Rational(1, 6))
+        deriv.set_weight((1, 1, 0), sp.Rational(1, 12))
+    elif len(stencil) == 27:
+        deriv.set_weight((0, 0, 0), sp.Integer(0))
+        deriv.set_weight((1, 1, 1), sp.Rational(1, 3360))
+
+    res = deriv.get_stencil(isotropic=True)
+    if dimensions == 2:
+        grad = [res.apply(phi_field.center), res.rotate_weights_and_apply(phi_field.center, (0, 1)), 0]
+    else:
+        grad = [res.apply(phi_field.center),
+                res.rotate_weights_and_apply(phi_field.center, (1, 0)),
+                res.rotate_weights_and_apply(phi_field.center, (2, 1))]
+
+    return grad
+
+
+def normalized_isotropic_gradient_symbolic(phi_field, stencil):
+    r"""
+    Get a symbolic expression for the normalized isotropic gradient of the phase-field
+    Args:
+        phi_field: the phase-field on which the normalized isotropic gradient is applied
+        stencil: stencil of the lattice Boltzmann step
+    """
+    isotropic_gradient = isotropic_gradient_symbolic(phi_field, stencil)
+    tmp = (sum(map(lambda x: x * x, isotropic_gradient_symbolic(phi_field, stencil))) + 1.e-12) ** 0.5
+
+    result = [x / tmp for x in isotropic_gradient]
+    return result
+
+
+def pressure_force(phi_field, stencil, density_heavy, density_light):
+    r"""
+    Get a symbolic expression for the pressure force
+    Args:
+        phi_field: phase-field
+        stencil: stencil of the lattice Boltzmann step
+        density_heavy: density of the heavier fluid
+        density_light: density of the lighter fluid
+    """
+    isotropic_gradient = isotropic_gradient_symbolic(phi_field, stencil)
+    result = list(map(lambda x: -sp.symbols("rho") * ((density_heavy - density_light) / 3) * x, isotropic_gradient))
+    return result
+
+
+def viscous_force(lb_velocity_field, phi_field, mrt_method, tau, density_heavy, density_light):
+    r"""
+    Get a symbolic expression for the viscous force
+    Args:
+        lb_velocity_field: hydrodynamic distribution function
+        phi_field: phase-field
+        mrt_method: mrt lattice boltzmann method used for hydrodynamics
+        tau: relaxation time of the hydrodynamic lattice boltzmann step
+        density_heavy: density of the heavier fluid
+        density_light: density of the lighter fluid
+    """
+    stencil = mrt_method.stencil
+    dimensions = len(stencil[0])
+
+    isotropic_gradient = isotropic_gradient_symbolic(phi_field, stencil)
+
+    weights = get_weights(stencil, c_s_sq=sp.Rational(1, 3))
+
+    op = sp.symbols("rho")
+
+    gamma = mrt_method.get_equilibrium_terms()
+    gamma = gamma.subs({sp.symbols("rho"): 1})
+
+    moment_matrix = mrt_method.moment_matrix
+    relaxation_rates = sp.Matrix(np.diag(mrt_method.relaxation_rates))
+    mrt_collision_op = moment_matrix.inv() * relaxation_rates * moment_matrix
+
+    # get the non equilibrium
+    non_equilibrium = [lb_velocity_field.center(i) - (weights[i] * op + gamma[i] - weights[i])
+                       for i, _ in enumerate(stencil)]
+    non_equilibrium = np.dot(mrt_collision_op, non_equilibrium)
+
+    stress_tensor = [0] * 6
+    # Calculate Stress Tensor MRT
+    for i, d in enumerate(stencil):
+        stress_tensor[0] += non_equilibrium[i] * (d[0] * d[0])
+        stress_tensor[1] += non_equilibrium[i] * (d[1] * d[1])
+
+        if dimensions == 3:
+            stress_tensor[2] += non_equilibrium[i] * (d[2] * d[2])
+            stress_tensor[3] += non_equilibrium[i] * (d[1] * d[2])
+            stress_tensor[4] += non_equilibrium[i] * (d[0] * d[2])
+
+        stress_tensor[5] += non_equilibrium[i] * (d[0] * d[1])
+
+    density_difference = density_heavy - density_light
+
+    # Calculate Viscous Force MRT
+    fmx = (0.5 - tau) * (stress_tensor[0] * isotropic_gradient[0]
+                         + stress_tensor[5] * isotropic_gradient[1]
+                         + stress_tensor[4] * isotropic_gradient[2]) * density_difference
+
+    fmy = (0.5 - tau) * (stress_tensor[5] * isotropic_gradient[0]
+                         + stress_tensor[1] * isotropic_gradient[1]
+                         + stress_tensor[3] * isotropic_gradient[2]) * density_difference
+
+    fmz = (0.5 - tau) * (stress_tensor[4] * isotropic_gradient[0]
+                         + stress_tensor[3] * isotropic_gradient[1]
+                         + stress_tensor[2] * isotropic_gradient[2]) * density_difference
+
+    return [fmx, fmy, fmz]
+
+
+def surface_tension_force(phi_field, stencil, beta, kappa):
+    r"""
+    Get a symbolic expression for the surface tension force
+    Args:
+        phi_field: the phase-field on which the chemical potential is applied
+        stencil: stencil of the lattice Boltzmann step
+        beta: coefficient related to surface tension and interface thickness
+        kappa: coefficient related to surface tension and interface thickness
+    """
+    chemical_potential = chemical_potential_symbolic(phi_field, stencil, beta, kappa)
+    isotropic_gradient = isotropic_gradient_symbolic(phi_field, stencil)
+    return [chemical_potential * x for x in isotropic_gradient]
+
+
+def hydrodynamic_force(lb_velocity_field, phi_field, mrt_method, tau,
+                       density_heavy, density_light, kappa, beta, body_force):
+    r"""
+    Get a symbolic expression for the hydrodynamic force
+    Args:
+        lb_velocity_field: hydrodynamic distribution function
+        phi_field: phase-field
+        mrt_method: mrt lattice boltzmann method used for hydrodynamics
+        tau: relaxation time of the hydrodynamic lattice boltzmann step
+        density_heavy: density of the heavier fluid
+        density_light: density of the lighter fluid
+        beta: coefficient related to surface tension and interface thickness
+        kappa: coefficient related to surface tension and interface thickness
+        body_force: force acting on the fluids. Usually the gravity
+    """
+    stencil = mrt_method.stencil
+    dimensions = len(stencil[0])
+    fp = pressure_force(phi_field, stencil, density_heavy, density_light)
+    fm = viscous_force(lb_velocity_field, phi_field, mrt_method, tau, density_heavy, density_light)
+    fs = surface_tension_force(phi_field, stencil, beta, kappa)
+
+    result = []
+    for i in range(dimensions):
+        result.append(fs[i] + fp[i] + fm[i] + body_force[i])
+
+    return result
+
+
+def interface_tracking_force(phi_field, stencil, interface_thickness):
+    r"""
+    Get a symbolic expression for the hydrodynamic force
+    Args:
+        phi_field: phase-field
+        stencil: stencil of the phase-field distribution lattice Boltzmann step
+        interface_thickness: interface thickness
+    """
+    dimensions = len(stencil[0])
+    normal_fd = normalized_isotropic_gradient_symbolic(phi_field, stencil)
+    result = []
+    for i in range(dimensions):
+        result.append(((1.0 - 4.0 * (phi_field.center - 0.5) ** 2.0) / interface_thickness) * normal_fd[i])
+
+    return result
+
+
+def get_assignment_list_stream_hydro(lb_vel_field, lb_vel_field_tmp, mrt_method, force_g, velocity, rho):
+    r"""
+    Returns an assignment list of the streaming step for the hydrodynamic lattice Boltzmann step. In the assignment list
+    also the update for the velocity is integrated
+    Args:
+        lb_vel_field: source field of velocity distribution function
+        lb_vel_field_tmp: destination field of the velocity distribution function
+        mrt_method: lattice Boltzmann method of the hydrodynamic lattice Boltzmann step
+        force_g: hydrodynamic force
+        velocity: velocity field
+        rho: interpolated density of the two fluids
+    """
+
+    stencil = mrt_method.stencil
+    dimensions = len(stencil[0])
+
+    velocity_symbol_list = [lb_vel_field.center(i) for i, _ in enumerate(stencil)]
+    velocity_tmp_symbol_list = [lb_vel_field_tmp.center(i) for i, _ in enumerate(stencil)]
+
+    g_subs_dic = dict(zip(velocity_symbol_list, velocity_tmp_symbol_list))
+    u_symp = sp.symbols("u_:{}".format(dimensions))
+
+    a = [0] * dimensions
+    for i, direction in enumerate(stencil):
+        for j in range(dimensions):
+            a[j] += velocity_tmp_symbol_list[i] * direction[j]
+
+    pull_g = list()
+    inv_dir = 0
+    for i, direction in enumerate(stencil):
+        inv_direction = tuple(-e for e in direction)
+        pull_g.append(Assignment(lb_vel_field_tmp(i), lb_vel_field[inv_direction](i)))
+        inv_dir += lb_vel_field[inv_direction](i)
+
+    for i in range(dimensions):
+        pull_g.append(Assignment(velocity.center_vector[i], a[i] + 0.5 * force_g[i].subs(g_subs_dic) / rho))
+
+    subexpression = [Assignment(sp.symbols("rho"), inv_dir)]
+
+    for i in range(dimensions):
+        subexpression.append(Assignment(u_symp[i], velocity.center_vector[i]))
+
+    ac_g = AssignmentCollection(main_assignments=pull_g, subexpressions=subexpression)
+
+    ac_g.main_assignments = sympy_cse_on_assignment_list(ac_g.main_assignments)
+
+    return ac_g
+
+
+def initializer_kernel_phase_field_lb(phi_field_distributions, phi_field, velocity, mrt_method, interface_thickness):
+    r"""
+    Returns an assignment list for initializing the phase-field distribution functions
+    Args:
+        phi_field_distributions: source field of phase-field distribution function
+        phi_field: phase-field
+        velocity: velocity field
+        mrt_method: lattice Boltzmann method of the phase-field lattice Boltzmann step
+        interface_thickness: interface thickness
+    """
+    stencil = mrt_method.stencil
+    dimensions = len(stencil[0])
+    weights = get_weights(stencil, c_s_sq=sp.Rational(1, 3))
+    u_symp = sp.symbols("u_:{}".format(dimensions))
+
+    normal_fd = normalized_isotropic_gradient_symbolic(phi_field, stencil)
+
+    gamma = mrt_method.get_equilibrium_terms()
+    gamma = gamma.subs({sp.symbols("rho"): 1})
+    gamma_init = gamma.subs({x: y for x, y in zip(u_symp, velocity.center_vector)})
+    # create the kernels for the initialization of the g and h field
+    h_updates = list()
+
+    def scalar_product(a, b):
+        return sum(a_i * b_i for a_i, b_i in zip(a, b))
+
+    f = []
+    for i, d in enumerate(stencil):
+        f.append(weights[i] * ((1.0 - 4.0 * (phi_field.center - 0.5) ** 2.0) / interface_thickness)
+                 * scalar_product(d, normal_fd[0:dimensions]))
+
+    for i, _ in enumerate(stencil):
+        h_updates.append(Assignment(phi_field_distributions.center(i), phi_field.center * gamma_init[i] - 0.5 * f[i]))
+
+    return h_updates
+
+
+def initializer_kernel_hydro_lb(velocity_distributions, velocity, mrt_method):
+    r"""
+    Returns an assignment list for initializing the velocity distribution functions
+    Args:
+        velocity_distributions: source field of velocity distribution function
+        velocity: velocity field
+        mrt_method: lattice Boltzmann method of the hydrodynamic lattice Boltzmann step
+    """
+    stencil = mrt_method.stencil
+    dimensions = len(stencil[0])
+    weights = get_weights(stencil, c_s_sq=sp.Rational(1, 3))
+    u_symp = sp.symbols("u_:{}".format(dimensions))
+
+    gamma = mrt_method.get_equilibrium_terms()
+    gamma = gamma.subs({sp.symbols("rho"): 1})
+    gamma_init = gamma.subs({x: y for x, y in zip(u_symp, velocity.center_vector)})
+
+    g_updates = list()
+    for i, _ in enumerate(stencil):
+        g_updates.append(Assignment(velocity_distributions.center(i), gamma_init[i] - weights[i]))
+
+    return g_updates
diff --git a/lbmpy/phasefield_allen_cahn/parameter_calculation.py b/lbmpy/phasefield_allen_cahn/parameter_calculation.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e3454020799b1c38e8c32f1c475b82dbabe3fd0
--- /dev/null
+++ b/lbmpy/phasefield_allen_cahn/parameter_calculation.py
@@ -0,0 +1,56 @@
+import math
+
+
+def calculate_parameters_rti(reference_length=256,
+                             reference_time=16000,
+                             density_heavy=1.0,
+                             capillary_number=0.26,
+                             reynolds_number=3000,
+                             atwood_number=0.5,
+                             peclet_number=1000,
+                             density_ratio=3,
+                             viscosity_ratio=1):
+    r"""
+    Calculate the simulation parameters for the Rayleigh-Taylor instability.
+    The calculation is done according to the description in part B of PhysRevE.96.053301.
+
+    Args:
+        reference_length: reference length of the RTI
+        reference_time: chosen reference time
+        density_heavy: density of the heavier fluid
+        capillary_number: capillary number of the simulation
+        reynolds_number: reynolds number of the simulation
+        atwood_number: atwood number of the simulation
+        peclet_number: peclet number of the simulation
+        density_ratio: density ration of the heavier and the lighter fluid
+        viscosity_ratio: viscosity ratio of the heavier and the lighter fluid
+    """
+    # calculate the gravitational acceleration and the reference velocity
+    gravitational_acceleration = reference_length / (reference_time ** 2 * atwood_number)
+    reference_velocity = math.sqrt(gravitational_acceleration * reference_length)
+
+    dynamic_viscosity_heavy = (density_heavy * reference_velocity * reference_length) / reynolds_number
+    dynamic_viscosity_light = dynamic_viscosity_heavy / viscosity_ratio
+
+    density_light = density_heavy / density_ratio
+
+    kinematic_viscosity_heavy = dynamic_viscosity_heavy / density_heavy
+    kinematic_viscosity_light = dynamic_viscosity_light / density_light
+
+    relaxation_time_heavy = 3.0 * kinematic_viscosity_heavy
+    relaxation_time_light = 3.0 * kinematic_viscosity_light
+
+    surface_tension = (dynamic_viscosity_heavy * reference_velocity) / capillary_number
+    mobility = (reference_velocity * reference_length) / peclet_number
+
+    parameters = {
+        "density_light": density_light,
+        "dynamic_viscosity_heavy": dynamic_viscosity_heavy,
+        "dynamic_viscosity_light": dynamic_viscosity_light,
+        "relaxation_time_heavy": relaxation_time_heavy,
+        "relaxation_time_light": relaxation_time_light,
+        "gravitational_acceleration": -gravitational_acceleration,
+        "mobility": mobility,
+        "surface_tension": surface_tension
+    }
+    return parameters
diff --git a/lbmpy_tests/phasefield_allen_cahn/test_phase_field_allen_cahn.ipynb b/lbmpy_tests/phasefield_allen_cahn/test_phase_field_allen_cahn.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..5b9312181b5d84af027faaab77760e973c12d69f
--- /dev/null
+++ b/lbmpy_tests/phasefield_allen_cahn/test_phase_field_allen_cahn.ipynb
@@ -0,0 +1,417 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import math\n",
+    "\n",
+    "from lbmpy.session import *\n",
+    "from pystencils.session import *\n",
+    "\n",
+    "from lbmpy.moments import MOMENT_SYMBOLS\n",
+    "from collections import OrderedDict\n",
+    "\n",
+    "from lbmpy.methods.creationfunctions import create_with_discrete_maxwellian_eq_moments\n",
+    "\n",
+    "from lbmpy.phasefield_allen_cahn.parameter_calculation import calculate_parameters_rti\n",
+    "from lbmpy.phasefield_allen_cahn.kernel_equations import *\n",
+    "from lbmpy.phasefield_allen_cahn.force_model import MultiphaseForceModel"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "try:\n",
+    "    import pycuda\n",
+    "except ImportError:\n",
+    "    pycuda = None\n",
+    "    gpu = False\n",
+    "    target = 'cpu'\n",
+    "    print('No pycuda installed')\n",
+    "\n",
+    "if pycuda:\n",
+    "    gpu = True\n",
+    "    target = 'gpu'"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "stencil_phase = get_stencil(\"D2Q9\")\n",
+    "stencil_velo = get_stencil(\"D2Q9\")\n",
+    "assert(len(stencil_phase[0]) == len(stencil_velo[0]))\n",
+    "\n",
+    "dimensions = len(stencil_phase[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# domain \n",
+    "L0 = 256\n",
+    "Nx = L0\n",
+    "Ny = 4 * L0\n",
+    "X0 = (Nx/2) + 1\n",
+    "Y0 = (Ny/2) + 1\n",
+    "\n",
+    "# time step\n",
+    "tf = 10001\n",
+    "reference_time = 16000\n",
+    "\n",
+    "# force acting on the bubble\n",
+    "body_force = [0, 0, 0]\n",
+    "rho_H = 1.0\n",
+    "\n",
+    "\n",
+    "parameters = calculate_parameters_rti(reference_length=L0,\n",
+    "                                      reference_time=reference_time,\n",
+    "                                      density_heavy=rho_H,\n",
+    "                                      capillary_number=0.26,\n",
+    "                                      reynolds_number=3000,\n",
+    "                                      atwood_number=0.5,\n",
+    "                                      peclet_number=1000,\n",
+    "                                      density_ratio=3,\n",
+    "                                      viscosity_ratio=1)\n",
+    "\n",
+    "rho_L = parameters.get(\"density_light\")\n",
+    "\n",
+    "mu_H = parameters.get(\"dynamic_viscosity_heavy\")\n",
+    "mu_L = parameters.get(\"dynamic_viscosity_light\")\n",
+    "\n",
+    "tau_H = parameters.get(\"relaxation_time_heavy\")\n",
+    "tau_L = parameters.get(\"relaxation_time_light\")\n",
+    "\n",
+    "sigma = parameters.get(\"surface_tension\")\n",
+    "M = parameters.get(\"mobility\")\n",
+    "gravitational_acceleration = parameters.get(\"gravitational_acceleration\")\n",
+    "\n",
+    "\n",
+    "# interface thickness\n",
+    "W = 5\n",
+    "# coeffcient related to surface tension\n",
+    "beta = 12.0 * (sigma/W)\n",
+    "# coeffcient related to surface tension\n",
+    "kappa = 1.5 * sigma*W\n",
+    "# relaxation rate allen cahn (h)\n",
+    "w_c = 1.0/(0.5 + (3.0 * M))\n",
+    "\n",
+    "\n",
+    "# fields \n",
+    "domain_size = (Nx, Ny)\n",
+    "dh = ps.create_data_handling((domain_size), periodicity = (True, False), parallel=False, default_target=target)\n",
+    "\n",
+    "g = dh.add_array(\"g\", values_per_cell=len(stencil_velo))\n",
+    "dh.fill(\"g\", 0.0, ghost_layers=True)\n",
+    "h = dh.add_array(\"h\",values_per_cell=len(stencil_phase))\n",
+    "dh.fill(\"h\", 0.0, ghost_layers=True)\n",
+    "\n",
+    "g_tmp = dh.add_array(\"g_tmp\", values_per_cell=len(stencil_velo))\n",
+    "dh.fill(\"g_tmp\", 0.0, ghost_layers=True)\n",
+    "h_tmp = dh.add_array(\"h_tmp\",values_per_cell=len(stencil_phase))\n",
+    "dh.fill(\"h_tmp\", 0.0, ghost_layers=True)\n",
+    "\n",
+    "u = dh.add_array(\"u\", values_per_cell=dh.dim)\n",
+    "dh.fill(\"u\", 0.0, ghost_layers=True)\n",
+    "\n",
+    "C = dh.add_array(\"C\")\n",
+    "dh.fill(\"C\", 0.0, ghost_layers=True)\n",
+    "\n",
+    "force = dh.add_array(\"force\",values_per_cell=dh.dim)\n",
+    "dh.fill(\"force\", 0.0, ghost_layers=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "tau = 0.5 + tau_L + (C.center) * (tau_H - tau_L)\n",
+    "s8 = 1/(tau)\n",
+    "\n",
+    "rho = rho_L + (C.center) * (rho_H - rho_L)\n",
+    "\n",
+    "body_force[1] = gravitational_acceleration * rho"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "method_phase = create_lb_method(stencil=stencil_phase, method='srt', relaxation_rate=w_c, compressible = True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "if len(stencil_velo) == 9:\n",
+    "    # for D2Q9 a self defined method is used\n",
+    "    x, y, z = MOMENT_SYMBOLS\n",
+    "    moment_table = [sp.Integer(1),\n",
+    "                    -4 + 3*(x**2 + y**2),\n",
+    "                    4 - sp.Rational(21, 2)*(x**2 + y**2) + sp.Rational(9, 2)*(x**2 + y**2)**2,\n",
+    "                    x,\n",
+    "                    (-5 + 3*(x**2 + y**2))*x,\n",
+    "                    y,\n",
+    "                    (-5 + 3*(x**2 + y**2))*y ,\n",
+    "                    x**2 - y**2,\n",
+    "                    x*y]\n",
+    "\n",
+    "    relax_table = [1, 1, 1, 1, 1, 1, 1, s8, s8]\n",
+    "    rr_dict = OrderedDict(zip(moment_table, relax_table))\n",
+    "elif len(stencil_velo) == 19:\n",
+    "    mrt = create_lb_method(method=\"mrt\", stencil=stencil_velo, relaxation_rates=[1, 1, 1, 1, s8, 1, 1])\n",
+    "    rr_dict = OrderedDict(zip(mrt.moments, mrt.relaxation_rates))\n",
+    "else:\n",
+    "    mrt = create_lb_method(method=\"mrt\", stencil=stencil_velo, relaxation_rates=[1, 1, s8, 1, 1, 1, 1])\n",
+    "    rr_dict = OrderedDict(zip(mrt.moments, mrt.relaxation_rates))\n",
+    "\n",
+    "method_velo = create_with_discrete_maxwellian_eq_moments(stencil_velo, rr_dict, compressible=False)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# initialize the domain\n",
+    "def Initialize_distributions():\n",
+    "    \n",
+    "    for block in dh.iterate(ghost_layers=True, inner_ghost_layers=False):\n",
+    "        x, y = block.midpoint_arrays\n",
+    "        y -= 2 * L0\n",
+    "        tmp = 0.1 * Nx * np.cos((2 * math.pi * x) / Nx)\n",
+    "        init_values = 0.5 + 0.5 * np.tanh((y - tmp) / (W / 2))\n",
+    "        block[\"C\"][:, :] = init_values\n",
+    "        \n",
+    "    if gpu:\n",
+    "        dh.all_to_gpu()            \n",
+    "    \n",
+    "    dh.run_kernel(h_init)\n",
+    "    dh.run_kernel(g_init)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "h_updates = initializer_kernel_phase_field_lb(h, C, u, method_phase, W)\n",
+    "g_updates = initializer_kernel_hydro_lb(g, u, method_velo)\n",
+    "\n",
+    "h_init = ps.create_kernel(h_updates, target=dh.default_target, cpu_openmp=True).compile()\n",
+    "g_init = ps.create_kernel(g_updates, target=dh.default_target, cpu_openmp=True).compile()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "force_h = [f / 3 for f in interface_tracking_force(C, stencil_phase, W)]\n",
+    "force_model_h = MultiphaseForceModel(force=force_h)\n",
+    "\n",
+    "force_g = hydrodynamic_force(g, C, method_velo, tau, rho_H, rho_L, kappa, beta, body_force)\n",
+    "force_model_g = MultiphaseForceModel(force=force_g, rho=rho)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "# create the allen cahl lattice boltzmann step for the h field as well as the hydrodynamic\n",
+    "# lattice boltzmann step for the g field\n",
+    "h_tmp_symbol_list = [h_tmp.center(i) for i, _ in enumerate(stencil_phase)]\n",
+    "sum_h = np.sum(h_tmp_symbol_list[:])\n",
+    "\n",
+    "method_phase = create_lb_method(stencil=stencil_phase,\n",
+    "                                method='srt',\n",
+    "                                relaxation_rate=w_c,\n",
+    "                                compressible = True,\n",
+    "                                force_model=force_model_h)\n",
+    "\n",
+    "allen_cahn_lb = create_lb_update_rule(lb_method=method_phase,\n",
+    "                                      velocity_input = u, \n",
+    "                                      compressible = True,\n",
+    "                                      optimization = {\"symbolic_field\": h,\n",
+    "                                                      \"symbolic_temporary_field\": h_tmp},\n",
+    "                                      kernel_type = 'stream_pull_collide')\n",
+    "\n",
+    "allen_cahn_lb.set_main_assignments_from_dict({**allen_cahn_lb.main_assignments_dict, **{C.center: sum_h}})\n",
+    "\n",
+    "ast_allen_cahn_lb = ps.create_kernel(allen_cahn_lb, target=dh.default_target, cpu_openmp=True)\n",
+    "kernel_allen_cahn_lb = ast_allen_cahn_lb.compile()\n",
+    "#------------------------------------------------------------------------------------------------------------------------\n",
+    "\n",
+    "\n",
+    "## collision of g\n",
+    "#force_model = forcemodels.Guo(force_g(0, MRT_collision_op))\n",
+    "method_velo = create_with_discrete_maxwellian_eq_moments(stencil_velo, rr_dict, force_model=force_model_g)\n",
+    "\n",
+    "hydro_lb = create_lb_update_rule(lb_method=method_velo,\n",
+    "                                 velocity_input = u,\n",
+    "                                 compressible = False,\n",
+    "                                 optimization = {\"symbolic_field\": g,\n",
+    "                                                 \"symbolic_temporary_field\": g_tmp},\n",
+    "                                 kernel_type = 'collide_only')\n",
+    "\n",
+    "ast_hydro_lb = ps.create_kernel(hydro_lb, target=dh.default_target, cpu_openmp=True)\n",
+    "kernel_hydro_lb = ast_hydro_lb.compile()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# periodic Boundarys for g, h and C\n",
+    "periodic_BC_g = dh.synchronization_function(g.name, target=dh.default_target, optimization = {\"openmp\": True})\n",
+    "periodic_BC_h = dh.synchronization_function(h.name, target=dh.default_target, optimization = {\"openmp\": True})\n",
+    "periodic_BC_C = dh.synchronization_function(C.name, target=dh.default_target, optimization = {\"openmp\": True})\n",
+    "\n",
+    "# No slip boundary for the phasefield lbm\n",
+    "bh_allen_cahn = LatticeBoltzmannBoundaryHandling(allen_cahn_lb.method, dh, 'h',\n",
+    "                                                 target=dh.default_target, name='boundary_handling_h')\n",
+    "\n",
+    "# No slip boundary for the velocityfield lbm\n",
+    "bh_hydro = LatticeBoltzmannBoundaryHandling(hydro_lb.method, dh, 'g' ,\n",
+    "                                            target=dh.default_target, name='boundary_handling_g')\n",
+    "\n",
+    "wall = NoSlip()\n",
+    "if dimensions == 2:\n",
+    "    bh_allen_cahn.set_boundary(wall, make_slice[:, 0])\n",
+    "    bh_allen_cahn.set_boundary(wall, make_slice[:, -1])\n",
+    "\n",
+    "    bh_hydro.set_boundary(wall, make_slice[:, 0])\n",
+    "    bh_hydro.set_boundary(wall, make_slice[:, -1])\n",
+    "else:\n",
+    "    bh_allen_cahn.set_boundary(wall, make_slice[:, 0, :])\n",
+    "    bh_allen_cahn.set_boundary(wall, make_slice[:, -1, :])\n",
+    "\n",
+    "    bh_hydro.set_boundary(wall, make_slice[:, 0, :])\n",
+    "    bh_hydro.set_boundary(wall, make_slice[:, -1, :])\n",
+    "\n",
+    "\n",
+    "bh_allen_cahn.prepare()\n",
+    "bh_hydro.prepare()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ac_g = get_assignment_list_stream_hydro(g, g_tmp, method_velo, force_g, u, rho)\n",
+    "ast_g = ps.create_kernel(ac_g, target=dh.default_target, cpu_openmp=True)\n",
+    "stream_g = ast_g.compile()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# definition of the timestep for the immiscible fluids model\n",
+    "def Improved_PhaseField_h_g():\n",
+    "    bh_allen_cahn()\n",
+    "    periodic_BC_h()\n",
+    "    \n",
+    "    dh.run_kernel(kernel_allen_cahn_lb)\n",
+    "    periodic_BC_C()\n",
+    "    dh.run_kernel(kernel_hydro_lb)\n",
+    "\n",
+    "    bh_hydro()\n",
+    "    periodic_BC_g()\n",
+    "    \n",
+    "    dh.run_kernel(stream_g)\n",
+    "    dh.swap(\"h\", \"h_tmp\")\n",
+    "    dh.swap(\"g\", \"g_tmp\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {
+    "scrolled": false
+   },
+   "outputs": [],
+   "source": [
+    "Initialize_distributions()\n",
+    "\n",
+    "pos_liquid_front = np.zeros((2, tf))\n",
+    "pos_bubble_front = np.zeros((2, tf))\n",
+    "for i in range(0, tf):\n",
+    "    # write out the phasefield\n",
+    "    if gpu:\n",
+    "        dh.to_cpu(\"C\")\n",
+    "\n",
+    "    pos_liquid_front[0, i] = i / reference_time\n",
+    "    pos_liquid_front[1, i] = (np.argmax(dh.cpu_arrays[\"C\"][L0//2, :] > 0.5) - Ny//2)/L0\n",
+    "\n",
+    "    pos_bubble_front[0, i] = i / reference_time\n",
+    "    pos_bubble_front[1, i] = (np.argmax(dh.cpu_arrays[\"C\"][0, :] > 0.5) - Ny//2)/L0\n",
+    "    \n",
+    "    # do one timestep\n",
+    "    Improved_PhaseField_h_g()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "assert(pos_liquid_front[1, -1] == -0.1953125)\n",
+    "assert(pos_bubble_front[1, -1] == 0.1875)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}